Adapter Pattern

호환되지 않는 인터페이스를 가진 객체들이 협력할 수 있도록 하는 구조적 디자인 패턴
한국의 220V 전기 제품을 미국에서 사용하기 위해 변환 어댑터를 사용하듯이, 소프트웨어에서도 호환되지 않는 인터페이스들을 함께 작동하도록 만들어주는 것이 어댑터 패턴의 핵심

특징

사용사례

장점

단점

주의사항 및 고려사항

예시

Python

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
from abc import ABC, abstractmethod
from typing import Dict

# Target Interface
class PaymentProcessor(ABC):
    @abstractmethod
    def process_payment(self, amount: float) -> bool:
        pass

    @abstractmethod
    def refund_payment(self, amount: float) -> bool:
        pass

# Existing payment system (Adaptee)
class StripePaymentSystem:
    def __init__(self, api_key: str):
        self.api_key = api_key

    def create_charge(self, amount: float, currency: str = "USD") -> Dict:
        # Simulate Stripe API call
        print(f"Stripe: Charging ${amount} using API key {self.api_key}")
        return {"success": True, "transaction_id": "str_123", "amount": amount}

    def create_refund(self, transaction_id: str) -> Dict:
        # Simulate Stripe API call
        print(f"Stripe: Refunding transaction {transaction_id}")
        return {"success": True, "refund_id": "ref_123"}

# Another existing payment system (Adaptee)
class PayPalAPI:
    def __init__(self, client_id: str):
        self.client_id = client_id

    def submit_payment(self, amount: float) -> Dict:
        # Simulate PayPal API call
        print(f"PayPal: Processing payment of ${amount} with client ID {self.client_id}")
        return {"status": "SUCCESS", "payment_id": "PAY123"}

    def reverse_payment(self, payment_id: str) -> Dict:
        # Simulate PayPal API call
        print(f"PayPal: Reversing payment {payment_id}")
        return {"status": "SUCCESS"}

# Adapter for Stripe
class StripeAdapter(PaymentProcessor):
    def __init__(self, stripe_processor: StripePaymentSystem):
        self.stripe = stripe_processor
        self.transaction_records = {}

    def process_payment(self, amount: float) -> bool:
        result = self.stripe.create_charge(amount)
        if result["success"]:
            self.transaction_records[amount] = result["transaction_id"]
            return True
        return False

    def refund_payment(self, amount: float) -> bool:
        transaction_id = self.transaction_records.get(amount)
        if not transaction_id:
            return False
        
        result = self.stripe.create_refund(transaction_id)
        return result["success"]

# Adapter for PayPal
class PayPalAdapter(PaymentProcessor):
    def __init__(self, paypal_processor: PayPalAPI):
        self.paypal = paypal_processor
        self.payment_records = {}

    def process_payment(self, amount: float) -> bool:
        result = self.paypal.submit_payment(amount)
        if result["status"] == "SUCCESS":
            self.payment_records[amount] = result["payment_id"]
            return True
        return False

    def refund_payment(self, amount: float) -> bool:
        payment_id = self.payment_records.get(amount)
        if not payment_id:
            return False
        
        result = self.paypal.reverse_payment(payment_id)
        return result["status"] == "SUCCESS"

# Client code
def process_order(processor: PaymentProcessor, amount: float):
    """Process an order using any payment processor"""
    if processor.process_payment(amount):
        print(f"Successfully processed payment of ${amount}")
        return True
    print(f"Failed to process payment of ${amount}")
    return False

# Usage example
if __name__ == "__main__":
    # Create payment processors
    stripe_processor = StripePaymentSystem(api_key="sk_test_123")
    paypal_processor = PayPalAPI(client_id="client_123")

    # Create adapters
    stripe_adapter = StripeAdapter(stripe_processor)
    paypal_adapter = PayPalAdapter(paypal_processor)

    # Process payments using different processors
    process_order(stripe_adapter, 100.00)
    process_order(paypal_adapter, 50.00)

    # Test refunds
    stripe_adapter.refund_payment(100.00)
    paypal_adapter.refund_payment(50.00)

Javascript

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Target interface (what the client expects)
class DataAnalyzer {
    analyze(data) {
        throw new Error('analyze method must be implemented');
    }

    getReport() {
        throw new Error('getReport method must be implemented');
    }
}

// Existing CSV data processor (Adaptee)
class CSVProcessor {
    constructor() {
        this.data = null;
    }

    loadCSV(csvData) {
        // Simulate processing CSV data
        console.log('Processing CSV data…');
        this.data = csvData.split('\n').map(row => row.split(','));
        return true;
    }

    generateStats() {
        if (!this.data) return null;
        
        // Simulate generating statistics
        return {
            rowCount: this.data.length,
            columnCount: this.data[0].length,
            format: 'CSV'
        };
    }
}

// Existing JSON data processor (Adaptee)
class JSONProcessor {
    constructor() {
        this.jsonData = null;
    }

    parseJSON(jsonString) {
        // Simulate parsing JSON data
        console.log('Parsing JSON data…');
        this.jsonData = JSON.parse(jsonString);
        return {
            success: true,
            timestamp: new Date()
        };
    }

    calculateMetrics() {
        if (!this.jsonData) return null;

        // Simulate calculating metrics
        return {
            size: JSON.stringify(this.jsonData).length,
            type: 'JSON',
            keys: Object.keys(this.jsonData)
        };
    }
}

// Adapter for CSV Processor
class CSVAdapter extends DataAnalyzer {
    constructor(csvProcessor) {
        super();
        this.processor = csvProcessor;
        this.analysis = null;
    }

    analyze(data) {
        const success = this.processor.loadCSV(data);
        if (success) {
            this.analysis = this.processor.generateStats();
            return true;
        }
        return false;
    }

    getReport() {
        if (!this.analysis) return null;

        return {
            type: 'CSV Analysis',
            entries: this.analysis.rowCount,
            fields: this.analysis.columnCount,
            format: this.analysis.format,
            timestamp: new Date()
        };
    }
}

// Adapter for JSON Processor
class JSONAdapter extends DataAnalyzer {
    constructor(jsonProcessor) {
        super();
        this.processor = jsonProcessor;
        this.analysis = null;
    }

    analyze(data) {
        const result = this.processor.parseJSON(data);
        if (result.success) {
            this.analysis = this.processor.calculateMetrics();
            return true;
        }
        return false;
    }

    getReport() {
        if (!this.analysis) return null;

        return {
            type: 'JSON Analysis',
            dataSize: this.analysis.size,
            format: this.analysis.type,
            availableFields: this.analysis.keys,
            timestamp: new Date()
        };
    }
}

// Client code
class DataAnalysisService {
    constructor(analyzer) {
        this.analyzer = analyzer;
    }

    processData(data) {
        if (this.analyzer.analyze(data)) {
            const report = this.analyzer.getReport();
            console.log('Analysis Report:', report);
            return report;
        }
        console.log('Analysis failed');
        return null;
    }
}

// Usage example
const csvData = 'name,age,city\nJohn,New York\nJane,Boston';
const jsonData = JSON.stringify({
    users: [
        { name: 'John', age: 30, city: 'New York' },
        { name: 'Jane', age: 25, city: 'Boston' }
    ]
});

// Create processors and adapters
const csvProcessor = new CSVProcessor();
const jsonProcessor = new JSONProcessor();
const csvAdapter = new CSVAdapter(csvProcessor);
const jsonAdapter = new JSONAdapter(jsonProcessor);

// Analyze both types of data using the same interface
const analysisService = new DataAnalysisService(csvAdapter);
analysisService.processData(csvData);

const jsonAnalysisService = new DataAnalysisService(jsonAdapter);
jsonAnalysisService.processData(jsonData);

용어 정리

용어설명

참고 및 출처


1. 주제의 분류가 적절한지에 대해 조사

Adapter Pattern(어댑터 패턴)은 “Computer Science and Engineering > Software Design and Architecture > Software Design Patterns > GoF > Structural Design Patterns” 분류에 정확하게 해당합니다. GoF(Gang of Four)에서 정의한 23가지 디자인 패턴 중 구조(Structural) 패턴에 속하며, 호환되지 않는 인터페이스를 연결하는 데 중점을 둡니다[1][2][6].


2. 200자 요약

어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 동작할 수 있도록 중간에 어댑터(Adapter)를 두어 인터페이스를 변환해주는 구조 패턴입니다. 기존 코드 수정 없이 새로운 시스템과 통합하거나 외부 라이브러리, 레거시 시스템과 연동할 때 활용됩니다[1][2][3][6].


3. 250자 개요

Adapter Pattern은 기존의 인터페이스(Adaptee)를 클라이언트가 기대하는 인터페이스(Target)로 변환하는 구조 패턴입니다. 어댑터(Adapter)는 Target 인터페이스를 구현하고 Adaptee 객체를 감싸(합성)거나 상속하여, 클라이언트가 요구하는 방식으로 기능을 제공합니다. 이를 통해 기존 코드 수정 없이 다양한 외부 시스템, 레거시 코드, 라이브러리와의 통합이 가능하며, 코드의 재사용성과 유지보수성을 크게 높일 수 있습니다[1][2][6][19].


핵심 개념


주요 내용 정리

패턴 이름과 분류

항목내용
패턴 이름Adapter Pattern(어댑터 패턴)
분류GoF 구조(Structural) 패턴

의도 (Intent)

호환되지 않는 인터페이스를 변환하여, 기존 클래스(Adaptee)를 클라이언트가 기대하는 인터페이스(Target)로 사용할 수 있게 한다[1][2][6].


다른 이름 (Also Known As)


동기 (Motivation / Forces)


적용 가능성 (Applicability)


구조 및 아키텍처

구조 다이어그램

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
+-----------+        +---------------+
|  Client   |------->|    Target     |
+-----------+        +---------------+
                        ^
                        |
                +----------------+
                |   Adapter      |
                +----------------+
                        |
                +----------------+
                |   Adaptee      |
                +----------------+

구성 요소 및 역할

구성 요소기능 및 역할
Target클라이언트가 기대하는 인터페이스 정의
Adaptee기존(호환되지 않는) 인터페이스를 가진 클래스
AdapterTarget 인터페이스 구현, Adaptee를 감싸(합성)거나 상속하여 변환
ClientTarget 인터페이스를 통해 Adapter 사용

필수/선택 구성요소

구분구성 요소기능 및 특징
필수Target클라이언트가 기대하는 인터페이스
필수AdapterTarget 구현, Adaptee를 감싸거나 상속
필수Adaptee기존(호환 불가) 인터페이스를 가진 클래스
필수ClientTarget 인터페이스를 통해 Adapter 사용

주요 원리 및 작동 원리

  1. Client는 Target 인터페이스를 통해 Adapter를 사용
  2. Adapter는 Target 인터페이스를 구현하고, 내부에서 Adaptee의 메서드를 호출
  3. Adapter가 Adaptee의 인터페이스를 Target에 맞게 변환하여 클라이언트에 제공

작동 원리 다이어그램

1
[Client] → [Target(인터페이스)] ← [Adapter] → [Adaptee]

구현 기법

예시 코드 (Java)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Target 인터페이스
public interface Target {
    void request();
}

// Adaptee 클래스
public class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee specific request");
    }
}

// Adapter 클래스 (객체 어댑터)
public class Adapter implements Target {
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    public void request() {
        adaptee.specificRequest();
    }
}

장점과 단점

구분항목설명
✅ 장점호환성 제공기존 코드 수정 없이 다양한 인터페이스 통합 가능[1][3][6][19]
코드 재사용성기존 클래스/라이브러리 재사용, 유지보수성 향상
느슨한 결합SRP, OCP 준수, 결합도 낮춤[13]
외부 시스템 연동레거시, 써드파티, 외부 API 연동 용이
⚠ 단점코드 복잡성 증가Adapter 클래스 추가로 복잡성 증가[3][4][8][17]
성능 저하중간 계층 추가로 오버헤드 발생 가능
관리 비용Adapter가 많아지면 관리 어려움
상속 기반 어댑터다중상속 미지원 언어에서는 한계 존재

도전 과제 및 해결책


분류에 따른 종류 및 유형

분류 기준종류/유형설명
구현 방식클래스 어댑터상속 기반, 다중상속 지원 언어에서 사용[1][4][10]
객체 어댑터합성(Composition) 기반, 대부분의 언어에서 사용
구조 적용 범위단일 어댑터하나의 Adaptee 변환
다중 어댑터여러 Adaptee 변환

실무 적용 예시

분야적용 예시설명
외부 API 연동결제, 인증, 메시징 API 어댑터서로 다른 API 인터페이스 통합
데이터 변환XML ↔ JSON 변환 어댑터데이터 포맷 변환, 표준화
레거시 시스템레거시 DB/서비스 래퍼기존 시스템과 신규 시스템 연동
UI/그래픽다양한 위젯/컨트롤 어댑터호환되지 않는 위젯 통합

활용 사례 (시나리오 기반)

상황 가정: 외부 메일 발송 솔루션 교체

1
[Client] → [Target 인터페이스] ← [Adapter] → [MailSenderB(Adaptee)]

실무에서 효과적으로 적용하기 위한 고려사항 및 주의점

항목설명권장사항
코드 복잡성 관리Adapter 클래스 증가 시 복잡성 증가공통 추상화, 문서화, 코드 생성 도구 활용
성능 최적화중간 계층 오버헤드 발생 가능Adapter 계층 최소화, 직접 통합 검토
테스트 용이성Adapter 단위 테스트 필요Mock, Stub 활용, 단위 테스트 강화
인터페이스 일관성다양한 Adaptee 관리 필요표준 Target 인터페이스 정의

최적화하기 위한 고려사항 및 주의점

항목설명권장사항
오버헤드 최소화Adapter 계층 호출 비용 발생불필요 Adapter 최소화, 직접 통합 검토
객체 생성 비용Adapter/Adaptee 객체 생성 비용 고려객체 풀, 싱글턴 등 활용
메모리 관리Adapter/Adaptee 객체 누수 방지GC 활용, 객체 수명 관리
테스트 자동화다양한 조합 테스트 필요자동화 테스트, Mock 활용

2025년 기준 최신 동향

주제항목설명
외부 APIAPI 어댑터 자동 생성외부 API 연동 자동화 도구 확산
데이터 표준화데이터 포맷 변환JSON↔XML, 다양한 포맷 변환 어댑터 활용 증가
마이크로서비스서비스 간 인터페이스 변환서비스 간 통신 표준화, 어댑터 활용 증가
테스트Mock 어댑터 활용테스트 자동화, Mock 어댑터 활용 증가

주제와 관련하여 주목할 내용

주제항목설명
Wrapper래퍼 패턴Adapter는 Wrapper 패턴의 대표적 사례
SRP/OCP단일 책임, 개방/폐쇄 원칙어댑터로 책임 분리, 코드 확장성 강화
비교 패턴데코레이터/프록시구조 유사, 목적/용도 차이
레거시 통합외부 시스템 연동레거시/외부 시스템 통합에 어댑터 활용

앞으로의 전망

주제항목설명
자동화어댑터 생성 도구코드 자동 생성, 어댑터 자동화 도구 확산
마이크로서비스서비스 표준화서비스 간 인터페이스 표준화, 어댑터 활용 증가
테스트Mock 어댑터테스트 자동화, Mock 어댑터 활용 증가
데이터 변환포맷/스키마 변환다양한 데이터 포맷/스키마 변환 어댑터 확산

하위 주제별 추가 학습 필요 내용

카테고리주제간략 설명
패턴 구조클래스/객체 어댑터상속/합성 기반 어댑터 구조 비교
테스트Mock 어댑터테스트용 어댑터 설계/활용법
비교 패턴데코레이터/프록시구조/목적 차이 학습
자동화어댑터 생성 도구코드 자동 생성 도구 활용법

추가 학습/알아야 할 내용

카테고리주제간략 설명
소프트웨어 아키텍처레거시/외부 시스템 통합어댑터 패턴 활용 사례
성능오버헤드 최소화Adapter 계층 최적화 전략
프레임워크어댑터 활용Spring, Express 등 프레임워크 내 어댑터 활용법
실무 도구어댑터 자동 생성코드 생성/관리 도구 활용법

용어 정리

용어설명
Target클라이언트가 기대하는 인터페이스
Adaptee기존(호환되지 않는) 인터페이스를 가진 클래스
AdapterAdaptee를 Target 인터페이스로 변환하는 클래스
ClientTarget 인터페이스를 통해 Adapter를 사용하는 객체
Wrapper(래퍼)기존 객체를 감싸서 인터페이스를 변환하는 구조 패턴

참고 및 출처

Citations: [1] https://4z7l.github.io/2021/01/29/design_pattern_adapter.html [2] https://en.wikipedia.org/wiki/Adapter_pattern [3] https://ittrue.tistory.com/551 [4] https://conpulake.tistory.com/254 [5] https://sjh9708.tistory.com/126 [6] https://gsbang.tistory.com/entry/Design-Pattern-Adapter-Pattern%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4 [7] https://mypark.tistory.com/entry/Design-Pattern-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4Command-Pattern%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90 [8] https://lsoovmee-rhino.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0%ED%8C%A8%ED%84%B41-Adapter-Pattern-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4 [9] https://mini-min-dev.tistory.com/286 [10] https://bj25.tistory.com/23 [11] https://dev-youngjun.tistory.com/235 [12] https://todamfather.tistory.com/91 [13] https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%96%B4%EB%8C%91%ED%84%B0Adaptor-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90 [14] https://siyoon210.tistory.com/166 [15] https://mudata0101.tistory.com/92 [16] https://aisparkup.com/posts/2148 [17] https://ssow93.tistory.com/48 [18] https://velog.io/@gyomni/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-01.-%EC%96%B4%EB%8C%91%ED%84%B0Adapter-%ED%8C%A8%ED%84%B4 [19] https://daehyun-bigbread.tistory.com/89 [20] https://velog.io/@luda412/Adapter-Pattern [21] https://velog.io/@qwe916/%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4Adapter-Pattern [22] https://howisitgo1ng.tistory.com/entry/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-14-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4-%ED%8D%BC%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4Adapter-Pattern-Facade-Pattern-java [23] https://gymdev.tistory.com/24 [24] http://jidum.com/jidums/view.do?jidumId=991 [25] https://inblog.ai/uni/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4adapter-pattern-26440 [26] https://ones1kk.tistory.com/entry/GoF-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4Adapter-Pattern [27] https://rldd.tistory.com/404 [28] https://yozm.wishket.com/magazine/detail/2077/ [29] https://f-lab.kr/insight/adapter-vs-proxy-pattern-20240704 [30] https://jh-bk.tistory.com/50 [31] https://m.hanbit.co.kr/channel/view.html?cmscode=CMS8616098823 [32] https://kyeong-hoon.tistory.com/432 [33] https://dicws.tistory.com/162 [34] https://jusungpark.tistory.com/22 [35] https://refactoring.guru/ko/design-patterns/adapter [36] https://mslim8803.tistory.com/87 [37] https://readystory.tistory.com/125 [38] https://engineering.linecorp.com/ko/blog/port-and-adapter-architecture/ [39] https://velog.io/@kyeun95/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4 [40] https://velog.io/@haero_kim/%EC%9A%B0%EB%A6%AC%EB%8A%94-%EC%9D%B4%EB%AF%B8-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%95%8C%EA%B3%A0-%EC%9E%88%EB%8B%A4 [41] https://velog.io/@parksegun/%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8C%A8%ED%84%B4Adapter-Pattern-95lq8fyv [42] https://jobc.tistory.com/21 [43] https://velog.io/@hjern/CS-Study-13%EC%A3%BC%EC%B0%A8 [44] https://www.ibm.com/docs/ko/was-zos/9.0.5?topic=zos-optimized-local-adapters-performance-considerations [45] https://www.e-biogen.com/technote/Tech_Note_ebiogen_202503.pdf [46] https://iitp.kr/resources/file/201217/1.%EC%B4%9D%EA%B4%84%EB%B3%B4%EA%B3%A0%EC%84%9C.pdf [47] https://www.pwc.com/kr/ko/insights/samil-insight/samilpwc_ces2025-preview.pdf [48] https://gngsn.tistory.com/131 [49] https://belatrix.globant.com/us-en/blog/tech-trends/adapter-design-pattern/ [50] https://refactoring.guru/design-patterns/adapter [51] https://faun.pub/mastering-design-patterns-07-a-comprehensive-guide-to-the-adapter-pattern-ec86b23773fb [52] https://ios-development.tistory.com/1235 [53] https://www.jingyiaudio.com/ko/blog/2025-trends-in-rca-connectors/


Perplexity로부터의 답변: pplx.ai/share

다음은 **Adapter Pattern (어댑터 패턴)**에 대한 실무 중심 정리입니다.


1. 주제의 분류 적절성


2. 요약 설명 (200자 내외)

Adapter Pattern은 서로 호환되지 않는 인터페이스를 가진 클래스들을 함께 사용할 수 있도록 중간에서 변환 역할을 수행하는 구조 패턴이다. 기존 코드를 수정하지 않고 외부 시스템이나 레거시 코드와 통합하는 데 유용하다.


3. 개요 설명 (250자 내외)

어댑터 패턴(Adapter Pattern)은 인터페이스가 서로 다른 클래스들이 함께 작동할 수 있도록 변환기를 제공하는 구조 패턴이다. 기존 클래스를 수정하지 않고 새 시스템에 통합할 수 있어 레거시 시스템, 서드파티 라이브러리와 연동할 때 유리하다. 어댑터는 클라이언트가 기대하는 인터페이스를 제공하고, 실제 동작은 adaptee에게 위임하는 방식으로 작동한다. 이 패턴은 객체지향 설계의 개방-폐쇄 원칙(Open/Closed Principle)을 잘 구현한 예시이다.


4. 핵심 개념

항목설명
정의서로 다른 인터페이스 간 호환성을 제공하는 구조적 디자인 패턴
목적기존 코드를 변경하지 않고 새로운 인터페이스에 적응시키기
대상 문제인터페이스가 호환되지 않는 클래스 간 협업 필요 시
구조어댑터(Adapter), 클라이언트(Client), 대상(Target), 실제 객체(Adaptee)
방식클래스 기반(Adapter 상속) 또는 객체 기반(Adapter 위임) 방식 존재
적용 예Logger 시스템, Payment Gateway 연동, 드라이버 시스템 등

이어서 주요 작동 원리 다이어그램, 구조 및 아키텍처, 구현 기법 등으로 정리해드리겠습니다. 계속 진행할까요? 이어서 **Adapter Pattern (어댑터 패턴)**의 구조, 구현 기법, 장단점 등 실무 중심 심화 내용을 정리합니다.


5. 주요 원리 및 작동 원리

📌 작동 다이어그램

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
      Client
        |
        v
+------------------+       adapts to       +------------------+
|     Adapter      | -------------------> |     Adaptee       |
+------------------+                      +------------------+
| - adaptee        |                      | specificRequest() |
| + request()      |                      +------------------+
+------------------+
        ^
        |
+------------------+
|     Target       | ← 클라이언트가 기대하는 인터페이스
+------------------+
| + request()      |
+------------------+

6. 구조 및 아키텍처

📦 구성 요소 및 역할

구성 요소기능설명
Target (인터페이스)클라이언트가 기대하는 인터페이스 정의request() 등 클라이언트 메서드 명세
Adaptee (적응 대상)기존 구현이 되어 있는 클래스인터페이스가 다름. specificRequest()
Adapter (어댑터)Target을 구현하고 Adaptee를 참조요청을 변환해서 Adaptee에게 위임
ClientTarget 인터페이스만 사용어댑터를 통해 간접적으로 Adaptee 호출

✅ 필수 구성 요소

✅ 선택 구성 요소


7. 구현 기법

① 객체 어댑터 방식 (구성 기반)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Target
interface MediaPlayer {
    void play(String fileName);
}

// Adaptee
class VLCPlayer {
    void playVLC(String fileName) {
        System.out.println("Playing VLC file: " + fileName);
    }
}

// Adapter
class MediaAdapter implements MediaPlayer {
    private VLCPlayer vlcPlayer = new VLCPlayer();

    public void play(String fileName) {
        vlcPlayer.playVLC(fileName);
    }
}

② 클래스 어댑터 방식 (상속 기반 - Java는 다중 상속 미지원)

1
2
3
4
5
class MediaAdapter extends VLCPlayer implements MediaPlayer {
    public void play(String fileName) {
        playVLC(fileName); // 상속된 메서드 호출
    }
}

8. 장점과 단점

구분항목설명
✅ 장점재사용성 증가기존 코드 수정 없이 시스템 확장 가능
레거시 통합호환되지 않는 외부 시스템 연동 용이
설계 원칙 준수개방-폐쇄 원칙(OCP) 구현 가능
⚠ 단점클래스 수 증가각 대상 클래스마다 어댑터 작성 필요
복잡성 증가어댑터 계층 추가로 설계 구조 복잡
성능 오버헤드호출 위임 구조로 미세한 성능 손실 발생 가능

9. 도전 과제 및 해결 방안

도전 과제설명해결 방안
인터페이스 호환 어려움너무 상이한 인터페이스는 어댑팅 어려움추상화 수준 재조정 또는 중간 변환기 추가
남용불필요한 어댑터 도입 시 오히려 복잡도 증가객체 간 관계 명확히 설계하고 필요한 경우에만 적용
유지보수어댑터 변경 시 다수 의존 객체 영향 가능공통 인터페이스 사용 + DI(의존성 주입) 구조 설계

10. 분류에 따른 종류 및 유형

분류 기준유형설명
구조 기반객체 어댑터어댑터가 Adaptee를 포함 (구성)
클래스 어댑터어댑터가 Adaptee를 상속 (상속)
방향성단방향 어댑터Target → Adaptee 방향으로만 동작
양방향 어댑터양쪽 인터페이스를 상호 변환 가능
활용도인터페이스 어댑터인터페이스 전체 구현 대신 기본 구현 제공 (Java 8 이후 default methods 대체됨)

이후 이어서 실무 적용 예시, 활용 사례, 최적화/고려사항 등도 정리하겠습니다.

이어서 **Adapter Pattern (어댑터 패턴)**의 실무 적용 예시, 활용 사례, 최적화 고려사항 등을 포함해 마무리 정리합니다.


11. 실무 적용 예시

분야예시설명
로깅 시스템SLF4J ↔ Log4j 어댑터SLF4J 인터페이스에 맞추어 다양한 로깅 프레임워크 연동
결제 시스템PG AdapterPG사마다 다른 API를 공통 인터페이스로 변환
데이터 파이프라인Kafka ↔ AWS SNS/SQS 어댑터메시지 시스템 간 인터페이스 통합
프론트 ↔ 백엔드GraphQL ↔ REST 어댑터GraphQL 요청을 내부 REST API 호출로 변환
레거시 연동JDBC ↔ ORM (JPA) 어댑터JPA 내부에서 JDBC 자원을 추상화하여 사용

12. 활용 사례: 시나리오 기반

📌 시나리오: 레거시 지불 시스템 연동

배경
신규 e-commerce 플랫폼에서는 PaymentProcessor 인터페이스를 사용하지만, 레거시 시스템은 LegacyPaymentService.processOldPayment() 메서드만 제공

🔧 어댑터 구성

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Target
interface PaymentProcessor {
    void process(String amount);
}

// Adaptee
class LegacyPaymentService {
    void processOldPayment(double value) {
        System.out.println("Processed old payment: " + value);
    }
}

// Adapter
class PaymentAdapter implements PaymentProcessor {
    private LegacyPaymentService legacy = new LegacyPaymentService();

    public void process(String amount) {
        double value = Double.parseDouble(amount);
        legacy.processOldPayment(value);
    }
}

🔁 Workflow

  1. 사용자는 PaymentProcessor를 통해 결제 요청

  2. PaymentAdapter가 요청을 변환

  3. LegacyPaymentService가 처리


13. 실무에서 효과적으로 적용하기 위한 고려사항

고려사항설명권장 사항
인터페이스 명세 정의클라이언트 요구를 먼저 정의명확한 Target 인터페이스부터 설계
객체 어댑터 우선클래스 어댑터는 Java 다중 상속 제한객체 조합 방식 사용 권장
중복 방지다양한 시스템 연동 시 어댑터 계층 정리추상 어댑터 및 팩토리 활용
테스트 가능성어댑터만으로 테스트 불가 시 어려움Mock 객체 기반 단위 테스트 고려

14. 성능을 최적화하기 위한 고려사항

고려사항설명권장 사항
위임 비용호출 전환이 많은 경우 성능 저하 우려어댑터 내부 로직 최소화
캐싱 전략외부 시스템 호출 결과 캐싱 고려Spring Cache 또는 Guava Cache 적용
Lazy 초기화대형 객체 생성 지연 필요 시의존성 주입 또는 Provider 패턴 사용
로깅 분리어댑터 내부 로깅 분리단순 위임과 로직 분리 필요 (관심사 분리)

15. 최신 동향 (2025 기준)

주제항목설명
인터페이스 기반 Adapter 강화Spring, NestJS 등에서 추상화된 인터페이스 우선 설계 확대아키텍처 계층 분리 기반 강화
API Gateway + Adapter 통합GraphQL, REST 혼합 운영에서 Adapter 패턴 활용 증가API 조합을 위한 중간 계층 통합
테스트 자동화어댑터 레이어 단위 테스트 강화 추세MSA 내 추상 계층 테스트 중요도 증가
클라우드 연동어댑터를 통해 GCP, AWS, Azure API 동시 대응Provider ↔ Adapter 패턴 조합 증가

16. 하위 학습 주제 정리

카테고리주제설명
구현 기법객체 vs 클래스 어댑터객체 위임과 상속 기반 구현 차이
결합 구조Bridge vs Adapter구성 방식과 사용 목적 비교 학습
동작 원리위임, 변환 전략단일 책임 분리 원칙과 어댑터 내부 역할 구분
프레임워크 활용Spring에서의 Adapter 적용HandlerAdapter, ViewResolver 등 내부 구조 파악

17. 관련 학습 및 추가 주제

카테고리주제설명
디자인 패턴Decorator vs Adapter동작 변경 vs 인터페이스 변경 비교
아키텍처 패턴Anti-Corruption Layer (ACL)어댑터 확장 버전으로 외부 시스템 캡슐화
패키징 전략어댑터 계층 분리 설계application / adapter / domain 구조 권장
테스트 기술Mock 기반 테스트어댑터 단위 테스트에서의 mock 객체 활용

용어 정리

용어설명
Adaptee기존 구현 클래스. 인터페이스가 요구하는 메서드와 불일치
Target클라이언트가 기대하는 인터페이스
AdapterTarget 인터페이스를 구현하고 Adaptee를 호출하여 변환 수행
객체 어댑터어댑터가 Adaptee를 포함하여 위임 방식으로 호출
클래스 어댑터어댑터가 Adaptee를 상속하여 동작 구현
Anti-Corruption Layer외부 시스템과의 의존성을 최소화하기 위한 어댑터 아키텍처

참고 및 출처


원하신다면 다음도 정리해드릴 수 있습니다:

다음은 요청하신 ✅ Adapter vs Facade vs Decorator 비교✅ GraphQL → REST Adapter 실무 코드 예제입니다.


✅ Adapter vs Facade vs Decorator 비교표

구분Adapter (어댑터)Facade (퍼사드)Decorator (데코레이터)
📌 목적인터페이스 호환복잡한 서브시스템을 단순화객체 기능 확장
🧩 구조클라이언트 ↔ Adapter ↔ Adaptee클라이언트 ↔ Facade ↔ 복잡한 하위 컴포넌트객체 ↔ 여러 데코레이터 체인
🔄 동작요청 변환 후 기존 객체에 위임여러 객체를 묶어 하나의 API처럼 제공기존 객체에 기능을 동적으로 추가
🎯 사용 사례레거시 코드 연동, 호환성 해결서브시스템 단순화, 외부 API 감싸기기능 추가 (로깅, 캐싱 등)
🧱 변경 여부인터페이스 변경 없이 사용내부 구현 감추고 단순화기존 클래스를 수정하지 않음
예시SLF4J ↔ Log4j 어댑터Spring RestTemplate 내부 구현Java IO의 BufferedReader, GzipInputStream

📌 요약:


✅ GraphQL → REST Adapter 실무 코드 예제

📌 시나리오

기존 REST API(GET /users/{id})를 사용하는 시스템을 GraphQL API를 통해 통합 제공해야 함.


1. GraphQL 스키마 정의

1
2
3
4
5
6
7
8
9
type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String
  email: String
}

2. REST API 응답 예시

1
2
3
4
5
6
GET /api/v1/users/42
{
  "userId": 42,
  "username": "lee",
  "emailAddress": "lee@example.com"
}

3. Java (Spring Boot + GraphQL) 어댑터 구현

1
2
3
4
5
6
7
8
// GraphQL의 User 타입
public class UserDTO {
    private String id;
    private String name;
    private String email;

    // getters/setters
}
1
2
3
4
5
6
7
8
// REST 응답 모델
public class RestUserResponse {
    private int userId;
    private String username;
    private String emailAddress;

    // getters/setters
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 어댑터: REST API → GraphQL DTO 변환
@Component
public class UserAdapter {

    public UserDTO fromRest(RestUserResponse restUser) {
        UserDTO dto = new UserDTO();
        dto.setId(String.valueOf(restUser.getUserId()));
        dto.setName(restUser.getUsername());
        dto.setEmail(restUser.getEmailAddress());
        return dto;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// GraphQL Resolver
@Component
public class UserQueryResolver implements GraphQLQueryResolver {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserAdapter adapter;

    public UserDTO user(Long id) {
        String url = "http://internal-service/api/v1/users/" + id;
        RestUserResponse restUser = restTemplate.getForObject(url, RestUserResponse.class);
        return adapter.fromRest(restUser);
    }
}

🔁 구성도

1
2
3
4
5
6
7
Client (GraphQL)
GraphQL Resolver (user)
REST 호출 (GET /users/{id})
REST 응답 객체 → Adapter → GraphQL DTO

✅ 실무 고려사항

항목설명
타입 매핑REST 응답 필드와 GraphQL 필드 간 명확한 매핑 설계
오류 변환REST의 오류 응답을 GraphQL 오류 포맷으로 변환
공통 어댑터 추출다양한 REST 응답에 대해 일관된 DTO 변환 로직 필요
테스트 전략REST Stub을 활용한 Resolver 단위 테스트 권장

필요 시 아래도 제공 가능합니다:

이제 수집한 정보를 바탕으로 어댑터 패턴에 대한 포괄적인 분석을 시작하겠습니다.

이제 웹 검색 결과를 바탕으로 Adapter Pattern (어댑터 패턴)에 대한 종합적인 분석을 진행하겠습니다.

Adapter Pattern (어댑터 패턴) 종합 분석

1. 주제 분류 검증 및 개요

주제 분류 적절성

제시된 분류 **“Computer Science and Engineering” > “Software Design and Architecture” > “Software Design Patterns” > “GoF” > “Structural Design Patterns”**는 완전히 적절합니다. 어댑터 패턴은 GoF(Gang of Four)가 정의한 23개 디자인 패턴 중 구조 패턴(Structural Patterns) 7개 중 하나로, 클래스나 객체들을 조합해서 더 큰 구조로 만들어주는 패턴입니다.

주제 요약 (200자 내외)

어댑터 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 하는 구조적 디자인 패턴입니다. 한 클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환하여, 인터페이스 호환성 문제로 함께 사용할 수 없는 클래스들을 연결해서 사용할 수 있게 합니다.

전체 개요 (250자 내외)

어댑터 패턴은 기존 시스템과 새로운 라이브러리, 레거시 코드와 신규 시스템 간의 브릿지 역할을 수행합니다. 기존 코드를 수정하지 않고 써드파티 라이브러리나 새로운 인터페이스를 통합할 수 있어 코드 재사용성을 높입니다. 클래스 어댑터(상속)와 객체 어댑터(합성) 두 가지 구현 방식이 있으며, 2025년 현재 마이크로서비스와 클라우드 환경에서 API 통합의 핵심 패턴으로 활용되고 있습니다.

2. 구조 및 핵심 개념

핵심 개념

어댑터 패턴에서 반드시 알아야 하는 핵심 개념들:

  1. 인터페이스 변환 (Interface Translation): 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 것
  2. Wrapper 개념: 어댑터가 기존 객체를 감싸서 새로운 인터페이스를 제공하는 방식으로, Wrapper 패턴이라고도 불림
  3. 합성 vs 상속: 클래스 어댑터(상속)와 객체 어댑터(합성) 두 가지 구현 방식
  4. 중간 계층 (Intermediary Layer): 코드와 레거시 클래스, 타사 클래스 간의 변환기 역할을 하는 중간 레이어

배경 및 필요성

현재 사용하고 있는 라이브러리가 더 이상 요구에 부합하지 않아 재작성하거나 다른 라이브러리를 사용해야 할 때, 어댑터 패턴을 이용해 기존 코드를 가능한 적게 변경하면서 새로운 라이브러리로 교체할 수 있습니다. 기존에 있는 시스템에 새로운 써드파티 라이브러리가 추가되거나 레거시 인터페이스를 새로운 인터페이스로 교체하는 경우에 코드의 재사용성을 높일 수 있습니다.

목적 및 필요성

주요 기능 및 역할

  1. 인터페이스 변환: 서로 다른 인터페이스 간의 호환성 제공
  2. 의존성 격리: 클라이언트와 구현된 인터페이스를 분리하여, 향후 인터페이스 변경 시 클라이언트 수정 불필요
  3. 투명성 제공: 클라이언트는 어댑터의 존재를 인식하지 못함
  4. 코드 안정성: 기존 코드 변경 없이 새로운 기능 통합

3. 구조 및 아키텍처

구조 다이어그램

1
2
3
[Client] → [Target Interface] ← [Adapter] → [Adaptee]
                              (implements)

구성 요소

필수 구성요소

  1. Target (대상 인터페이스)

    • 기능: 클라이언트가 사용하려는 인터페이스를 정의
    • 역할: 클라이언트가 기대하는 메서드 시그니처 제공
    • 특징: 일반적으로 인터페이스나 추상 클래스로 구현
  2. Adapter (어댑터)

    • 기능: Target Interface를 구현하며, 클라이언트의 요청을 Adaptee가 이해할 수 있는 방법으로 전달
    • 역할: Target과 Adaptee 사이의 중재자
    • 특징: Target 인터페이스를 구현하고 Adaptee를 포함하거나 상속
  3. Adaptee (피어댑터)

    • 기능: 이미 개발이 완료되어 사용 중인 클래스
    • 역할: 실제 작업을 수행하는 기존 클래스
    • 특징: 수정이 어렵거나 불가능한 기존 라이브러리
  4. Client (클라이언트)

    • 기능: Target Interface를 통해 어댑터에 요청을 보내는 주체
    • 역할: 어댑터를 통해 Adaptee의 기능을 간접적으로 사용
    • 특징: 어댑터의 존재를 인식하지 못함

선택 구성요소

주요 원리 및 작동 원리

클라이언트에서는 Target Interface를 호출하는 것처럼 보이지만, 클라이언트의 요청을 전달받은 Adapter는 자신이 감싸고 있는 Adaptee에게 실질적인 처리를 위임합니다.

작동 순서:

  1. Client가 Target 인터페이스의 메서드 호출
  2. Adapter가 요청을 받아 Adaptee의 메서드로 변환
  3. Adaptee가 실제 작업 수행
  4. 결과를 다시 Target 인터페이스 형태로 반환

4. 구현 기법

클래스 어댑터 (상속 기반)

정의: adapter가 target, adaptee를 모두 상속한 형태

구성:

목적: 상속을 통한 직접적인 기능 확장

실제 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Target 인터페이스
interface Duck {
    void quack();
    void fly();
}

// Adaptee 클래스  
class Turkey {
    public void gobble() { System.out.println("Gobble gobble"); }
    public void fly() { System.out.println("Flying short distance"); }
}

// 클래스 어댑터 (다중 상속이 가능한 언어에서)
class TurkeyAdapter extends Turkey implements Duck {
    public void quack() { super.gobble(); }
    // fly()는 상속받아 그대로 사용
}

객체 어댑터 (합성 기반)

정의: target을 상속한 adapter가 클래스 변수로 adaptee를 가지고, 해당 프로퍼티를 통해서 메소드를 호출하는 형태

구성:

목적: 상속을 이용한 구현에 비해 보다 편리

실제 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 객체 어댑터
class TurkeyAdapter implements Duck {
    private Turkey turkey;
    
    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }
    
    public void quack() { turkey.gobble(); }
    public void fly() { turkey.fly(); }
}

5. 장점과 단점

구분항목설명
✅ 장점기존 코드 보존기존 코드를 변경하지 않아도 되므로 클래스 재활용성을 증가시킬 수 있음
인터페이스 분리클라이언트와 구현된 인터페이스를 분리시켜 향후 인터페이스 변경 시 클라이언트 수정 불필요
단일 책임 원칙인터페이스 변환 로직을 별도 클래스로 분리
개방/폐쇄 원칙새로운 어댑터 추가 시 기존 코드 수정 없음
⚠ 단점복잡도 증가구성요소를 위해 클래스를 증가시켜야 하므로 복잡도가 증가할 수 있음
유연성 제한클래스 어댑터의 경우 상속을 사용하므로 유연하지 못함
구현 비용객체 어댑터의 경우 대부분의 코드를 다시 작성해야 하므로 효율적이지 못함
디버깅 어려움중간 계층 추가로 인한 디버깅 복잡성 증가

6. 실무 적용 예시

분야사례설명구현 방식
Java 표준 라이브러리InputStreamReaderInputStream을 Reader로 변환하는 어댑터객체 어댑터
Java CollectionsCollections.enumeration()Iterator를 Enumeration으로 변환익명 내부 클래스
대규모 시스템Netflix API Gateway각 장치에 맞는 API를 제공하는 장치별 어댑터마이크로서비스 아키텍처
프론트엔드React 컴포넌트백엔드 API 응답을 컴포넌트 props에 맞게 변환함수형 어댑터
안드로이드RecyclerView Adapter데이터를 뷰에 바인딩하는 어댑터추상 클래스 상속
파일 I/OBufferedReaderInputStream을 문자열 기반으로 읽기 위한 어댑터다중 레벨 어댑터

7. 활용 사례: 전자상거래 플랫폼의 결제 시스템 통합

시나리오 배경

기존 전자상거래 플랫폼에서 새로운 결제 서비스 (Stripe, PayPal 등)를 통합해야 하는 상황에서 어댑터 패턴을 활용한 사례입니다.

시스템 구성

1
2
3
4
5
[E-commerce App] → [PaymentProcessor Interface] 
                    [StripeAdapter] → [Stripe SDK]
                    [PayPalAdapter] → [PayPal SDK]
                    [TossAdapter] → [Toss SDK]

시스템 구성요소

Workflow

  1. 사용자가 결제 요청
  2. E-commerce App이 PaymentProcessor 인터페이스 호출
  3. 선택된 결제 서비스의 어댑터가 요청 받음
  4. 어댑터가 해당 결제 서비스 SDK의 API 호출
  5. 결과를 통합 형태로 변환하여 반환

어댑터 패턴의 역할

8. 실무에서 효과적으로 적용하기 위한 고려사항

고려사항설명권장사항
인터페이스 설계Target 인터페이스는 최대한 간단하고 명확하게 설계단일 책임 원칙을 준수하여 인터페이스 분리
어댑터 선택클래스 vs 객체 어댑터 선택 기준다중 상속이 불가능한 언어에서는 객체 어댑터 선호
성능 고려어댑터 계층으로 인한 성능 오버헤드빈번한 호출이 예상되는 경우 성능 테스트 필수
에러 처리어댑터에서의 예외 처리 전략Adaptee의 예외를 Target에 맞는 예외로 변환
테스트 전략어댑터와 Adaptee 각각의 테스트Mock 객체를 활용한 단위 테스트 작성
문서화어댑터의 변환 로직 문서화변환 규칙과 제약사항을 명확히 기술

9. 성능 최적화를 위한 고려사항

고려사항설명권장사항
객체 생성 비용어댑터 객체 생성 최적화싱글톤 패턴이나 팩토리 패턴과 결합 활용
메모리 사용량어댑터가 Adaptee를 참조할 때의 메모리 관리약한 참조(WeakReference) 활용 고려
캐싱 전략변환 결과의 캐싱 가능성 검토불변 객체의 경우 결과 캐싱으로 성능 향상
배치 처리대량 데이터 처리 시 배치 어댑터 구현스트림 API와 결합하여 메모리 효율성 확보
비동기 처리어댑터에서의 비동기 작업 처리CompletableFuture나 Reactive Streams 활용
프로파일링어댑터 계층의 성능 모니터링APM 도구를 통한 지속적 성능 모니터링

10. GoF 패턴 상세 분석

패턴 이름과 분류

의도 (Intent)

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환합니다. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.

동기 (Motivation / Forces)

적용 가능성 (Applicability)

구조 (Structure)

어댑터 패턴의 UML 구조:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    Client  ────────→  Target
                       <<interface>>
                       + request()
                           │ implements
                       Adapter
                       - adaptee: Adaptee
                       + request()
                           ↓ delegates to
                       Adaptee
                       + specificRequest()

참여자 (Participants)

  1. Target: 클라이언트가 사용하려는 인터페이스
  2. Client: Target 인터페이스를 사용하는 객체
  3. Adaptee: 이미 개발이 완료되어 사용 중이며 수정하기 곤란한 상황의 클래스
  4. Adapter: Target 인터페이스를 Adaptee에 맞춰 구현하는 클래스

협력 (Collaboration)

클라이언트에서는 Target Interface를 호출하는 것처럼 보이지만, 클라이언트의 요청을 전달받은 Adapter는 자신이 감싸고 있는 Adaptee에게 실질적인 처리를 위임합니다.

결과 (Consequences)

장점:

단점:

구현 (Implementation)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Target 인터페이스
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

// Adaptee 클래스들
public class Mp4Player {
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file: " + fileName);
    }
}

public class VlcPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file: " + fileName);
    }
}

// Adapter 클래스
public class MediaAdapter implements MediaPlayer {
    private Mp4Player mp4Player;
    private VlcPlayer vlcPlayer;
    
    public MediaAdapter(String audioType) {
        if(audioType.equalsIgnoreCase("mp4")) {
            mp4Player = new Mp4Player();
        } else if(audioType.equalsIgnoreCase("vlc")) {
            vlcPlayer = new VlcPlayer();
        }
    }
    
    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("mp4")) {
            mp4Player.playMp4(fileName);
        } else if(audioType.equalsIgnoreCase("vlc")) {
            vlcPlayer.playVlc(fileName);
        }
    }
}

샘플 코드 (Sample Code)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// JavaScript 예제
class OldPrinter {
    printOld(text) {
        console.log(`Old format: ${text}`);
    }
}

class NewPrinter {
    print(text) {
        console.log(`New format: ${text}`);
    }
}

// Adapter
class PrinterAdapter {
    constructor(oldPrinter) {
        this.oldPrinter = oldPrinter;
    }
    
    print(text) {
        this.oldPrinter.printOld(text);
    }
}

// 사용법
const oldPrinter = new OldPrinter();
const adapter = new PrinterAdapter(oldPrinter);
adapter.print("Hello World"); // Old format: Hello World

알려진 사용 (Known Uses)

11. 2025년 기준 최신 동향

주제항목설명
마이크로서비스API Gateway 패턴마이크로서비스 기반 애플리케이션에서 단일 진입점 역할을 하는 API Gateway에서 어댑터 패턴 광범위 활용
클라우드 네이티브서비스 메시 통합Istio, Linkerd 등 서비스 메시에서 프로토콜 변환 어댑터 활용
AI/ML 통합AI 서비스 어댑터다양한 AI 서비스 (OpenAI, Google AI, AWS Bedrock) 통합을 위한 어댑터 패턴
컨테이너화쿠버네티스 어댑터클라우드 제공업체의 엣지 서비스를 활용하기 위한 어댑터 서비스
프론트엔드상태 관리 어댑터Redux, MobX, Zustand 등 상태 관리 라이브러리 간 어댑터
백엔드데이터베이스 어댑터멀티클라우드 데이터 로지스틱스 플랫폼에서 하이퍼스케일러 간 데이터 마이그레이션 어댑터

12. 주제와 관련하여 주목할 내용

주제항목설명
성능 최적화비동기 어댑터CompletableFuture, Reactive Streams를 활용한 비동기 어댑터 구현
보안 강화보안 어댑터OAuth, JWT 토큰 변환을 위한 보안 어댑터 패턴
관찰 가능성모니터링 어댑터OpenTelemetry, Micrometer를 활용한 메트릭 수집 어댑터
함수형 프로그래밍함수형 어댑터고차 함수와 커링을 활용한 함수형 어댑터 패턴
타입 안전성제네릭 어댑터TypeScript, Kotlin의 제네릭을 활용한 타입 안전 어댑터
테스트 용이성Mock 어댑터테스트를 위한 Mock 어댑터와 테스트 더블 패턴

13. 앞으로의 전망

주제항목설명
자동화코드 생성 어댑터AI 기반 코드 생성으로 어댑터 자동 생성 도구 발전
플랫폼 통합멀티 클라우드 어댑터마이크로서비스 기반 아키텍처에 대한 선호도 증가로 클라우드 API 시장 성장
실시간 처리스트리밍 어댑터Kafka, Pulsar 등 이벤트 스트리밍에서의 어댑터 패턴 확장
엣지 컴퓨팅엣지 어댑터엣지 인퍼런싱을 위한 클라우드 제공업체의 엣지 서비스 어댑터
표준화어댑터 프로토콜gRPC, GraphQL 기반 표준화된 어댑터 인터페이스 발전
성능 향상네이티브 어댑터GraalVM, 웹어셈블리를 활용한 고성능 어댑터 구현

14. 추가 학습 내용

하위 주제별 학습 내용

카테고리주제설명
고급 패턴체인 어댑터여러 어댑터를 체인으로 연결하는 복합 패턴
고급 패턴전략 어댑터전략 패턴과 결합한 동적 어댑터 선택
고급 패턴팩토리 어댑터팩토리 패턴과 결합한 어댑터 생성 관리
성능 최적화어댑터 풀링객체 풀을 활용한 어댑터 재사용 전략
성능 최적화지연 로딩 어댑터Lazy Loading을 적용한 어댑터 구현
테스팅어댑터 테스트Mock, Stub을 활용한 어댑터 단위 테스트

관련 분야 학습 내용

카테고리주제설명
아키텍처 패턴헥사고날 아키텍처포트와 어댑터 아키텍처로 불리는 헥사고날 아키텍처에서의 어댑터 활용
아키텍처 패턴클린 아키텍처의존성 역전을 위한 어댑터 계층 설계
마이크로서비스서비스 메시Envoy Proxy, Istio에서의 어댑터 구현
클라우드서버리스 어댑터AWS Lambda, Azure Functions에서의 어댑터 패턴
데이터 엔지니어링ETL 어댑터데이터 파이프라인에서의 어댑터 패턴 활용
보안인증 어댑터SAML, OAuth2, OpenID Connect 어댑터 구현

용어 정리

용어설명
Adaptee (피어댑터)어댑터에 의해 감싸지는 기존 클래스로, 실제 작업을 수행하지만 클라이언트가 원하는 인터페이스와 다른 인터페이스를 가진 클래스
Delegation (위임)객체 어댑터에서 사용하는 방식으로, 어댑터가 자신이 포함하고 있는 다른 객체에게 실제 작업을 맡기는 것
Interface Translation (인터페이스 변환)한 인터페이스의 메서드 호출을 다른 인터페이스의 메서드 호출로 변환하는 과정
Wrapper (래퍼)어댑터 패턴의 다른 이름으로, 기존 객체를 감싸서 새로운 인터페이스를 제공한다는 의미
Legacy System (레거시 시스템)오래된 기술이나 방법론으로 구축된 기존 시스템으로, 수정이 어렵거나 위험한 시스템
Third-party Library (써드파티 라이브러리)외부에서 개발된 라이브러리로, 소스 코드 수정이 불가능하거나 권장되지 않는 라이브러리
Object Composition (객체 합성)상속 대신 다른 객체를 포함하여 기능을 구현하는 방식
Single Responsibility Principle (단일 책임 원칙)하나의 클래스는 하나의 책임만 가져야 한다는 객체지향 설계 원칙

참고 및 출처