본문 바로가기
Design Pattern

6. 어댑터 패턴(Adapter pattern)

by 킹차니 2021. 12. 17.

 

어댑터 패턴

: 클라이언트가 사용하는 인터페이스가 정해져 있는데, 본인이 사용하는 인터페이스는 다르다면 중간에 adapter 패턴을 적용하여 사용한다. 즉 기존 코드를 클라이언트가 사용하는 인터페이스의 구현체로 바꿔주는 패턴이다.

-> 클라이언트가 사용하는 인터페이스를 따르지 않는 기존 코드를 재사용할 수 있게 해준다.

ex) 일상생활에서 보면 110V코드를 220V콘센트에 꽂는다던가 또는 그 반대로 할 때 그 사이에 adapter를 사용한다.

https://medium.com/@pramodayajayalath/adapter-design-pattern-3307ada690db

 

 


 

코드로 살펴보기

이제 코드로 살펴보자. 

시나리오는 다음과 같다. Client인 LoginHandler는 UserDetailsService와 UserDetails를 사용해서 로그인을 할 수 있도록 되어있다. 즉 UserDetailsService와 UserDetails는 target이고, 그것을 Client인 LoginHandler가 사용하는 것이다.

하지만 이때 Account와 AccountService라는 새로운 도메인이 추가된다. 이 계좌관련 도메인들은 사용자의 계좌 정보와 계좌를 만들고, 계좌를 사용자 이름으로 찾아주는 등의 로직을 담고 있는 전혀 다른 도메인이다. Client인 LoginHandler는 Account와 AccountService로도 login처리를 하고 싶은 것이다(Account에도 사용자의 정보가 담기므로 그 정보로 login처리를 하고 싶음). 이러한 상황에서 어떻게 adapter를 적용하여 login을 할 수 있을까??

 

 

 

먼저 Client인 LoginHandler는 아래와 같다.

LoginHandler

LoginHandler를 보면 userDetailsService를 사용하여 로그인을 하도록 되어 있는 것을 볼 수 있다.

 

 

target인 UserDetails와 UserDetailsService를 보자.

UserDetails

 

UserDetailsService

둘 다 인터페이스로 구현되어 있는데, LoginHandler가 로그인을 하기 위해서는 위와 같은 인터페이스를 구현해야만 한다.

 

 

이제 adaptee인 Account와 AccountService를 보자.

Account

 

AccountService

 

 

이제 LoginHandler가 Account, AccountService로도 로그인을 할 수 있도록 어댑터를 만들어보자.

Adapter인 AccountUserDetails, AccountUserDetailsService를 보자

이들은 각각 target인 UserDetails와 UserDetailsService를 구현해야 한다.

AccountUserDetails

(AccountUserDetails는 UserDetails 타입으로 Account를 감싸고 있다. - Wrapper클래스)

 

AccountUserDetailsService

loadUser를 보면 accountService의 findAccountByUsername을 사용하는 것을 볼 수 있다. 이처럼 adapter는 타입을 맞추고 대신 함수를 보내주는 역할만 하는 것이다.

 

 

이제 이를 테스트해보자.

수행결과:

 

 

즉 정리하면 클라이언트(LoginHandler)는 Target인 UserDetailService, UserDatails 만을 알고 있다. 하여 이들은 추상 인터페이스인 UserDetailService와 UserDetails를 사용하고 있지만, 실제로 사용하고 있는 것은 Adaptee인 AccountService, Account를 사용하는 것이다.

이를 위해서는 중간에서 Adaptee를 연결해주는 Adapter가 필요한데, 이것이 바로 AccountDetails와 AccountDetailService이다. 

이들 Adapter는 Target를 implements하여 Target타입을 가지게 되어 클라이언트인 LoginHandler는 UserDetailService, UserDetails를 사용하는 것과 똑같이 Adaptee를 사용할 수 있게 되는 것이다.


 

장점과 단점

 

장점:

1. 기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다. (OCP - Open Closed Principal 개방 폐쇄 원틱)

2. 기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다. (SRP - Single Responsibility Principal 단일 책임 원칙)

 

단점:

새 클래스가 생겨 복잡도가 증가할 수 있게 된다.(adapter 클래스의 추가) 하지만 경우에 따라 기존 코드가 해당 인터페이스를 구현하도록 수정하는 것은 좋은 선택일 수 있다.

 

 

 

 


실제로 어디에서 사용될까?

 

1. 자바의 Arrays와 Collections에서 adapter 패턴을 볼 수 있다.

 

 

 

2. 이미 우리가 코드로 살펴보기에서 알아본 것처럼 스프링 Security에 adapter패턴을 볼 수 있다.

또한 스프링MVC의 HandlerAdapter에 적용되어 있다.

 

 

 

 

출처: 인프런 백기선님 '코딩으로 학습하는 GoF 강의'
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4