스택큐힙리스트

Adapter 패턴으로 레거시 API 호환하기 본문

개발

Adapter 패턴으로 레거시 API 호환하기

스택큐힙리스트 2025. 7. 17. 23:23
반응형

낡은 인터페이스 때문에 새 코드가 발목 잡힐 때, Adapter 패턴은 “변환 잭”처럼 두 세계를 연결해 줍니다. 아이폰 충전기에 C-타입 젠더를 꽂아 갤럭시를 충전하듯, 호환되지 않는 객체를 한 번에 맞춰 주는 것이죠.


1. Adapter 패턴 한눈에 보기

  • 목적 : 기존 코드를 수정하지 않고도 서로 다른 인터페이스를 호환.
  • 역할
    • Target : 클라이언트가 원하는 인터페이스
    • Adaptee : 그대로 두고 싶은 레거시 객체
    • Adapter : 둘을 이어 주는 중간 변환기
  • 대표 사례 : JDBC 드라이버가 각 DB를 단일 인터페이스로 묶는 방식

2. 언제 쓰면 좋은가?

  • 레거시 API 교체가 어렵다 : 기존 라이브러리 메서드 시그니처가 바뀌기 힘든 경우
  • 서드파티 SDK 통합 : 외부 모듈이 내 코드 스타일과 맞지 않을 때
  • 다중 플랫폼 대응 : 모바일·웹·데스크톱에서 동일 데이터 모델을 쓰고 싶을 때

3. React 예시 – DTO를 갈아입히는 함수형 Adapter

// userAdapter.ts
export interface UserDTO {
  id: string;
  fullName: string;
  avatar: string;
}
export const userAdapter = (raw: any): UserDTO => ({
  id: raw.user_id,
  fullName: `${raw.first_name} ${raw.last_name}`,
  avatar: raw.profile_img ?? '/fallback.png',
});
// React Query와 함께
const { data: user } = useQuery(['user', id], fetchUser, {
  select: userAdapter,   // 컴포넌트는 깨끗한 DTO만 본다
});

React Query의 select 옵션에 연결하면 화면 쪽 로직이 더 이상 지저분한 데이터 변환을 몰라도 됩니다.


4. Spring Boot 예시 – 외부 결제 모듈 래핑

@Service
@RequiredArgsConstructor
public class TossPaymentAdapter implements PaymentPort {

    private final TossSdk toss;

    @Override
    public PaymentResult pay(Order order) {
        TossResponse res = toss.requestPayment(order.toDto());
        return new PaymentResult(res.id(), res.approvedAt());
    }
}

PaymentPort라는 Target 인터페이스만 의존하도록 만들어 두면, 내일 결제 수단을 PayPal로 바꿔도 주문 도메인 코드는 그대로입니다.


5. 구현 꿀팁

  • 구성(Composition) 우선 : 상속 기반 클래스 Adapter 대신 객체 합성 객체 Adapter가 테스트와 확장에 유리.
  • 예외 변환 : Adaptee가 던지는 예외를 도메인 공통 예외로 감싸서 위 계층으로 올린다.
  • 단일 책임 : 데이터 포맷 변환만 맡기고, 비즈니스 로직은 Service/Use-Case에 둔다.
  • 캡슐화된 테스트 : Target 인터페이스 기반의 테스트팩으로 교체 비용을 제로에 가깝게.

6. 장점 vs 주의점

  • 👍 레거시 무수정 재사용 : 안정적인 코드 자산 유지
  • 👍 OCP(개방·폐쇄) 준수 : 새 요구가 와도 기존 모듈 건드리지 않음 Velog
  • ⚠️ Adapter 남용 자제 : 무분별한 래핑은 추적 난이도만 높임. 패키지 구조와 네이밍 규칙으로 통일감을 확보하세요.

한 줄 요약

Adapter는 “호환” 그 이상의 가치, 변화에 강한 아키텍처를 만드는 첫 단추입니다.

반응형
Comments