어댑터 패턴으로 타입 호환하기

javascript/typescript
블로그 이미지

이챙(leechaeng)

﹒2025. 3. 24.

어댑터 패턴

어댑터패턴(Adapter pattern)은 서로 호환되지 않는 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴이다.
어댑터 말 그대로 어댑터 역할을 해준다. 다른전압을 가진 나라를 가서 여행용 어댑터를 들고가는 것과 같다.

// 아이폰 충전기
class IphonCharger {
  chargeLightning(){
	console.log('라이트닝 케이블로 아이폰 충전중');
	return '아이폰 충전 완료'
  }
}
// 안드로이드 충전기
class AndroidCharger {
  chargeUSBC(){
    console.log('USBC 케이블로 안드로이드폰 충전중');
    return '안드로이드폰 충전 완료'
  }
}

class Adapter {
  constructor{
    this.androidPhone = new AndroidCharger();
  }
   // 아이폰 충전 인터페이스와 같은 메소드 사용
  chargeLightning(){
    console.log('라이트닝 -> USBC 변환')
    const result = this.androidPhone.chargeUSBC();
    return `어댑터 사용 + ${result}`
  }
}

function chargePhone(charger){
  const result = charger.chargeLightning()
  console.log(`충전완료: ${result}`)
}
// 기존 아이폰 충전기 사용
const iphoneCharger = new IphonCharger();
chargePhone(iphoneCharger);

// 어댑터를 사용해 안드로이드기 충전기로 아이폰 충전
const adapter = new Adapter();
chargePhone(androidCharger)

 
위의 예시는 안드로이드 충전기로 아이폰을 충전하게 해주는 예제이다. [클로드의 도움을 받음 ㅎㅎ!]
안드로이드 USBC 케이블을 라이트닝 포트로 변환해주는 어댑터를 구현하여 호환되지 않는 인터페이스를 연결하여 AndroidCharger의 코드 변화 없이 사용하도록 했다.

- chargePhone함수는 chargeLightning 메소드만 알고있으며
- Adapter내 라이트닝 인터페이스를 연결하여
- 내부에서는 안드로이드의 메소드를 호출했다.

어댑터는 중간에서 인터페이스를 변환하고 기존 코드를 수정하지 않고도 서로 다른 인터페이스를 가진 새로운 시스템을 만들 수 있다.

 

 

 

실제 타입스크립트에서 사용

내가 이 패턴을 알게된건 타입스크립트를 사용하며 생긴 호환성 이슈 때문이었다.
한 컴포넌트내에서 2개의 데이터가 props로 들어오는데 값은 같지만 필드는 달랐다.

const data1 = {
  commonId: string;
  commonTitle: string;
  ...
}

const data2 = {
  id: string
  title: string
  ...
}


예를 들어 위의 데이터 처럼 사용하고자 하는 값은 같은데 필드가 다른 경우였다. 하나의 필드로 만들어야 했다.
타입을 어떻게 해야 하나 고민을 했었는데 어댑터 패턴을 사용하면 쉽게 해결할수있었다.

 

interface CommonItems {
  id: string;
  title: string;
  content: string;
  category: string;
  thumbnailPath: null | string;
}

export const boardAdapter = (item: ContentResponse): CommonItems => ({
  id: item.id,
  title: item.title,
  content: item.content,
  category: item.category,
  thumbnailPath: item.thumbPath,
});

export const searchAdapter = (item: SearchGallery): CommonItems => ({
  id: item.boardContentId,
  title: item.title,
  content: item.content,
  category: item.category,
  thumbnailPath: item.thumbnailPath,
});

interface TabListItemProps {
  guide: CommonGuideItems;
  index: number;
}


const Guide = ({guide, index}: TabListItemProps) => {
	...
}

export default Guide;



// 어댑터 사용하는 컴포넌트
<GuideListITem guide={boardAdapter(guide)} key={guide.id} index={index} styles={styles} />


props오는 데이터들이 서로 타입이 다르므로 보낼때 어댑터로 CommonGuide 형태로 변환을 해줬다.
호환되지 않는 인터페이스들을 공통된 인터페이스로 변환하여 한 컴포넌트내에서 일관적으로 사용하게끔 했다. 

 

그런데 데이터들이 계속 늘어날경우엔 이 방법이 맞지 않아보인다. 사실상 타입 자체를 통일해서 내려주는게 제일 베스트이지만 모든 여건이 딱딱 들어 맞을 순 없으니,,이런 패턴들을 알고 있으면 자유롭게 사용할 수 있을거같다.

이챙(leechaeng)
이챙(leechaeng)

프론트엔드 개발도 하고 뛰기도 하고

'javascript/typescript' 카테고리의 관련 글