Mobile & App Stack/Java Software Architecture & Patterns

어댑터 패턴(Adapter Pattern) 완벽 정리: 레거시 코드 통합의 열쇠

임베디드 친구 2024. 12. 24. 09:55
728x90
반응형

해외여행을 갈 때 110V 전압을 사용하는 국가에서 한국의 220V 가전제품을 쓰려면 '돼지코'라고 불리는 변환 플러그가 필요합니다. 소프트웨어 세계에서도 이와 똑같은 역할을 하는 것이 있습니다. 바로 어댑터 패턴(Adapter Pattern)입니다.

오늘은 서로 다른 인터페이스를 가진 클래스들을 매끄럽게 연결해주는 어댑터 패턴에 대해 자세히 알아보겠습니다.

Generated by Gemini AI.


1. 어댑터 패턴이란?

어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 구체적인 클래스를 감싸는(Wrapper) 구조 패턴입니다.

왜 사용하는가?

  • 기존 코드 재사용: 이미 잘 작동하는 레거시 클래스가 있지만, 새로운 시스템의 인터페이스와 맞지 않을 때 코드를 수정하지 않고 재사용할 수 있습니다.
  • 클라이언트 코드 보호: 클라이언트는 인터페이스가 바뀌어도 어댑터만 수정하면 되므로 본래의 로직을 유지할 수 있습니다.
  • 결합도 감소: 타사 라이브러리나 외부 시스템의 API가 변경되어도 우리 시스템에 미치는 영향을 최소화합니다.

2. 어댑터 패턴의 구조

어댑터 패턴은 크게 네 가지 구성 요소로 나뉩니다.

  1. Target (대상): 클라이언트가 직접적으로 사용하려는 인터페이스입니다.
  2. Client (클라이언트): Target 인터페이스를 통해 시스템을 사용하는 주체입니다.
  3. Adaptee (어댑티): 아직 호환되지 않는 기존의 클래스나 서비스입니다.
  4. Adapter (어댑터): Target을 구현하며, 내부적으로 Adaptee를 호출하여 둘 사이를 연결합니다.

3. Java 구현 예제: 110V 전압 변환기

이해를 돕기 위해 110V 가전제품을 220V 환경에서 사용하는 상황을 코드로 구현해 보겠습니다.

Step 1. Target 인터페이스와 Adaptee 클래스

Java
 
// Target: 우리 시스템이 기대하는 220V 인터페이스
interface Electronic220V {
    void connect();
}

// Adaptee: 기존의 110V 전용 가전제품 (호환되지 않음)
class HairDryer110V {
    public void powerOn() {
        System.out.println("110V 헤어드라이어 작동 중...");
    }
}

Step 2. Adapter 구현

Java
 
// Adapter: 110V를 220V 인터페이스에 맞게 변환
class VoltageAdapter implements Electronic220V {
    private HairDryer110V hairDryer;

    public VoltageAdapter(HairDryer110V hairDryer) {
        this.hairDryer = hairDryer;
    }

    @Override
    public void connect() {
        System.out.println("어댑터: 220V 전압을 110V로 변환합니다.");
        hairDryer.powerOn(); // 실제 동작은 110V 기기가 수행
    }
}

Step 3. 클라이언트 코드 실행

Java
 
public class Client {
    public static void main(String[] args) {
        // 1. 기존 기기 준비
        HairDryer110V oldDryer = new HairDryer110V();
        
        // 2. 어댑터에 끼우기
        Electronic220V adapter = new VoltageAdapter(oldDryer);
        
        // 3. 220V 콘센트에 연결
        System.out.println("--- 클라이언트가 220V 인터페이스를 호출합니다 ---");
        adapter.connect();
    }
}

4. 어댑터 패턴의 장단점

👍 장점

  • 개방-폐쇄 원칙(OCP) 준수: 기존의 레거시 코드를 수정하지 않고도 새로운 인터페이스에 적응시킬 수 있습니다.
  • SRP(단일 책임 원칙): 데이터 변환 및 인터페이스 호환 로직을 별도의 클래스로 분리하여 관리할 수 있습니다.
  • 유연성: 클라이언트는 어댑터 뒤에 어떤 복잡한 로직이 있는지 알 필요 없이 인터페이스만 보고 코딩하면 됩니다.

👎 단점

  • 복잡성 증가: 새로운 인터페이스와 어댑터 클래스를 계속 추가해야 하므로 코드의 전체적인 복잡도가 높아질 수 있습니다.
  • 성능 오버헤드: 드문 경우지만, 어댑터를 거치는 단계가 추가되어 아주 미세한 성능 차이가 발생할 수 있습니다.

5. 실무에서의 활용 사례

  • Java Arrays.asList(): 배열을 List 인터페이스에 맞게 변환해주는 어댑터 역할을 합니다.
  • InputStreamReader: 바이트 스트림을 문자 스트림으로 변환해주는 어댑터입니다.
  • DB 드라이버: 각기 다른 데이터베이스(MySQL, Oracle 등)의 접속 방식을 JDBC라는 공통 인터페이스로 맞춰주는 것도 어댑터 패턴의 일종입니다.

결론

어댑터 패턴은 "이미 있는 것을 버리지 않고 새로운 환경에 맞춰 쓰는 지혜"가 담긴 패턴입니다. 레거시 시스템과의 통합이나 외부 라이브러리 사용 시 인터페이스 불일치로 고민하고 있다면 어댑터 패턴 도입을 적극 고려해 보세요!


도움이 되셨다면 공감과 구독 부탁드립니다!

반응형