| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Tool Calling
- docker
- 어댑터패턴
- 인덱스
- dhgrp
- 데코레이터패턴
- mcp server
- 소프트웨어설계
- 객체지향
- LLM
- PHP객체지향
- docker network
- ai agent
- 디자인패턴
- Agent Loop
- MCP
- 객체복제
- OOP
- SQL기초
- Laravel
- 데이터베이스
- MySQL
- ai 에이전트
- 매직메서드
- linux 권한
- 라라벨
- SQL문법
- SQL
- PHP
- mcp client
- Today
- Total
개발블로그
[디자인패턴] 어댑터 패턴 본문
개념
필요성
프로그램을 만들다 보면 이미 존재하는 코드와 새로 사용해야 하는 코드의 형식이 맞지 않는 경우가 있음.
=> 기존 코드가 기대하는 방식과 새로 연결할 객체의 방식이 다를 때, 중간에서 맞춰줄 수 없을까?
ex)
결제 객체가 pay(amount)라는 메서드를 제공
payment.pay(10000)
그런데 새로 연동해야 하는 외부 결제 API는 다른 이름과 다른 형식의 메서드를 제공 할 수 있음
external_payment.request_payment(price=10000)
기능은 비슷함 (둘 다 결제를 처리)
BUT 사용하는 방식이 다르기 때문에 기존 코드에서는 바로 사용할 수 없음.
해결방법 => 외부 API를 기존 코드 형식에 맞게 감싸기
의미
어댑터 패턴은 서로 맞지 않는 인터페이스를 중간에서 변환해 연결하는 패턴
=> 기존 코드는 익숙한 방식 그대로 사용할 수 있음.
핵심 구조
| 구성 요소 | 역할 | 예시 |
| Client | 기존 인터페이스를 사용하는 코드 | 주문 서비스 |
| Target Interface | Client가 기대하는 인터페이스 | pay(amount) |
| Adaptee | 실제 연결해야 하는 기존 객체 또는 외부 API | 외부 결제 API |
| Adapter | Target Interface에 맞게 Adaptee를 감싸는 객체 | 결제 어댑터 |
=> 외부 API나 기존 코드 자체를 억지로 바꾸는 것이 아니라,
중간에 어댑터를 두어, 서로 다른 사용 방식을 맞춰주는 것.
예시
EX]
주문로직 : 결제 객체가 apy(amount) 메서드를 제공한다고 가정
class OrderService:
def __init__(self, payment):
self.payment = payment
def order(self, amount):
return self.payment.pay(amount)
이 구조에서 OrderService는 구체적인 결제 방식이 무엇인지 알 필요가 없음.
그저 pay(amount)를 호출할 수 있으면 됨
class CardPayment:
def pay(self, amount):
return "카드 결제"
그런데 새로 연동해야 하는 외부 결제 API는 다른 형식을 사용한다고 할 때
class ExternalPaymentAPI:
def request_payment(self, price):
return f"{price}원 외부 결제 요청"
기능은 결제지만, 메서드 이름이 pay()가 아니라 request_payment()다.
파라미터 이름도 amount가 아니라 price
=> 이 상태에서는 OrderService에 바로 넣을 수 없음
external_payment = ExternalPaymentAPI()
order_service = OrderService(external_payment)
order_service.order(10000)
OrderService는 내부에서 self.payment.pay(amount)를 호출한다.
하지만 ExternalPaymentAPI에는 pay()가 없기 때문에 오류가 발생
이 문제를 해결하기 위해 어댑터로 인터페이스 변환
class ExternalPaymentAdapter:
def __init__(self, external_payment):
self.external_payment = external_payment
def pay(self, amount):
return self.external_payment.request_payment(price=amount)
=> 외부 API를 내부 코드가 기대하는 pay(amount) 형태로 바꿔줌
이제 OrderService는 수정하지 않아도 됨
external_payment = ExternalPaymentAPI()
payment = ExternalPaymentAdapter(external_payment)
order_service = OrderService(payment)
print(order_service.order(10000))
실행흐름)
OrderService.order(10000)
→ payment.pay(10000)
→ ExternalPaymentAdapter.pay(10000)
→ ExternalPaymentAPI.request_payment(price=10000)
| 구분 | 역할 |
| OrderService | pay(amount)를 기대하는 기존 코드 |
| ExternalPaymentAPI | request_payment(price)를 제공하는 외부 API |
| ExternalPaymentAdapter | pay(amount) 호출을 request_payment(price) 호출로 변환 |
장점
장점
| 장점 | 설명 |
| 기존 코드 변경 최소화 | 기존 코드가 기대하는 인터페이스를 유지한 채 새로운 객체를 연결할 수 있다. |
| 외부 구현과 내부 코드 분리 | 외부 API의 메서드 이름이나 응답 구조를 내부 코드가 직접 알 필요가 줄어든다. |
| 호환성 확보 | 서로 다른 인터페이스를 가진 객체들을 같은 방식으로 사용할 수 있다. |
| 변경 영향 축소 | 외부 API 형식이 바뀌어도 어댑터만 수정하면 되는 구조를 만들 수 있다. |
| 일관된 사용 방식 제공 | 내부 코드에서는 여러 외부 객체를 동일한 인터페이스로 다룰 수 있다. |
효과적인 상황
| 상황 | 이유 |
| 외부 API를 연동해야 하는 경우 | 외부 API 형식을 내부 코드에 맞게 감쌀 수 있다. |
| 기존 코드를 크게 바꾸기 어려운 경우 | 기존 인터페이스를 유지하면서 새 구현을 연결할 수 있다. |
| 서로 다른 라이브러리를 같은 방식으로 사용하고 싶은 경우 | 어댑터를 통해 일관된 사용 방식을 제공할 수 있다. |
| 외부 변경 영향을 줄이고 싶은 경우 | 변경 지점을 어댑터로 제한할 수 있다. |
주의점
어댑터는 기존 코드와 외부 코드 사이의 형식 차이를 흡수한다.
하지만 두 코드의 개념이나 책임이 근본적으로 다르다면 단순히 메서드 이름만 맞춘다고 좋은 구조가 되지는 않음.
ex)
외부 API의 request_payment()가 실제로는 결제 요청뿐 아니라 포인트 적립, 알림 발송, 정산 요청까지 함께 처리한다고 할 때
class ExternalPaymentAPI:
def request_payment(self, price):
process_payment(price)
add_point(price)
send_notification(price)
create_settlement(price)
이 API를 단순히 pay(amount)로 감싸면 겉으로는 결제 인터페이스처럼 보임
class ExternalPaymentAdapter:
def pay(self, amount):
return self.external_payment.request_payment(price=amount)
하지만 내부적으로는 결제 외의 여러 작업까지 함께 실행된다.
이 경우 pay()라는 이름만 보고 사용하는 코드는 실제로 어떤 부작용이 발생하는지 알기 어려움.
| 주의점 | 설명 |
| 의미 차이를 숨기지 않기 | 메서드 이름만 맞추고 실제 동작 의미가 다르면 오해가 생길 수 있다. |
| 어댑터 비대화 | 변환 로직이 너무 많아지면 어댑터가 복잡해질 수 있다. |
| 오류 처리 방식 차이 | 외부 API의 예외, 실패 응답, 재시도 정책을 내부 기준에 맞게 정리해야 한다. |
| 성능 비용 | 변환 과정이 많아지면 불필요한 처리 비용이 생길 수 있다. |
| 책임 범위 확인 | 어댑터가 단순 변환을 넘어 비즈니스 로직까지 담당하지 않도록 주의해야 한다. |
'STUDY > CS' 카테고리의 다른 글
| [디자인패턴] 싱글톤 패턴 (0) | 2026.09.04 |
|---|---|
| [디자인패턴] 데코레이터 패턴 (0) | 2026.09.04 |
| [디자인패턴] 옵저버 패턴 (0) | 2026.09.04 |
| [디자인패턴] 팩토리 패턴 (1) | 2026.09.04 |
| [디자인 패턴] 전략패턴 (0) | 2026.09.04 |