본문 바로가기

취업과 기본기 튼튼/빽 투더 기본기

[디자인 패턴 18편] 디자인 패턴 총 정리. 구조편

들어가기 앞서

들어가기 앞서, 먼저 디자인 패턴에 대해 어떻게 생각하고 받아들여야 할지 고민해본다.
디자인 패턴이라고 하는 개념들은 왜 있으며, 왜 배우고 정리하는걸까?

디자인 패턴에 대한 검색을 해보면, 각 패턴에 대한 무수한 예제들이 있다.
예제도 다양한데 각 구현부가 달라 무엇을 기억해야 하는지 헷갈릴 때가 종종 있곤 했다.
또, 이 패턴과 저 패턴이 비슷해 보일 때도 있으며, 실제로 두 패턴을 무엇으로 구분하나 싶기도 하다.

이러던 중, 이승현 님 블로그에 다음과 같은 글을 보았다.

패턴을 공부하거나 구현할 때 UML에 집중해서 공부하면 안 된다고 생각한다.
구조만을 외우고 구조로 구분을 한 사람은 공부한 것을 금방 까먹거나 헷갈려하기 쉽기 때문인데, 이유는 거의 비슷한 구조를 갖춘 패턴들은 정말 많기 때문이다.
더 헷갈린 것은 동일 패턴이 구조가 다른 경우도 부지기수이다. 의도가 같기 때문.
즉 "의도", "목적" 이 중요하다.

출처: https://hamait.tistory.com/868 [HAMA 블로그]

아직 내가 잘 아는 것은 아니지만, 전적으로 동감한다.
디자인 패턴은 어떤 '맥락'에서 자주 쓰이는 것들이며, 그 맥락에서 무엇을 하고 싶은 지에 대한 '의도'를 담고 있다. 이 의도를 담아 UML로 표현하고 구체적인 코드로 구현하는 것은 사실 언어마다 조금씩 다르다.
특히 검색되는 대부분의 UML 은 JAVA에 기반하여 설계되어있는데, 파이썬의 파이써닉한 설계와는 조금씩 안 맞는 경우가 있긴 하다. (파이썬을 주 언어로 쓰는 나로선, JAVA 식의 구현 코드를 그대로 따라가야 하는지 좀 의문이긴 했다.)

따라서, UML이나 구체적인 구현 코드는 참고만 하자.
더 중요한 것은 이 패턴을 사용한다는 것은 어떤 의도를 전달하려는 건지, 그러기 위해 클래스 구조를 어떻게 설계할 수 있는지에 대해서 아는 것이다. 디자인 패턴은 결국 다른 개발자와 커뮤니케이션하기 위해 배우는 것이다. 따라서 조금씩 달라질 수 있는 구체적인 구현을 알려하기보다는, 각 디자인 패턴이 무엇을 하려 하는지에 대해 좀 더 초점을 두고 공부하자.
(이승현 님은 이 의도의 느낌만 받아들여도 충분하다고 한다.)

저번 정리에 이어 이번에는 구조 편이다.
구조 패턴은 일반적인 클래스의 구조를 다룬다.

생성과 행동 패턴 역시 클래스 구조를 다루는 건 맞지만, 생성 패턴은 객체의 생성과정 부분에 좀 더 포커스 되어있고, 행동 패턴은 구체적인 상황과 액션에 좀 더 포커싱 되어있다.
구조 패턴은 이 둘 보다는 좀 더 범용적인 아키텍처 개념을 다룬달까?
그래서 단순히 클래스 설계에만 들어가는 개념이 아니라 일반적인 아키텍처의 개념을 다루고 있다는 생각이 든다.
(예를 들어 프록시 패턴의 '프록시'는 프록시 서버 등 시스템 아키텍처의 여기저기서 쓰이는 일반적인 개념이다. 일반적인 개념을 가지고와 적용하다 보니, 패턴 이름에 그 의도가 명확히 보인다.)

이전 포스팅과 마찬가지로, 구체적인 코드는 적지 않겠다.
다시 말하지만, UML 은 그저 이렇게 설계할 수 있다는 느낌으로 참고만 하자.


어댑터 (Adapter)

110V 콘센트에 220V 플러그를 꼽으려면 '110V -> 220V 플러그 어댑터'가 필요하다.
이 처럼 기존의 서로 다른 인터페이스를 맞추기 위해 중간에 두는 무언가를 '어댑터' 라고 한다.

코드에서도 마찬가지다.
서로 다른 두 클래스(Client 와 Adaptee) 가 있고, 이 둘은 그대로 둔 채 이 둘의 인터페이스를 연결하고자 어댑터 클래스를 만들어 사용하는 구조를 어댑터 패턴이라고 한다.
이 패턴은 이미 정의된 3자의 인터페이스를 자신의 인터페이스의 모양으로 "인터페이스의 변경" 하고 싶을 때 사용한다.

구체적인 동작은 [디자인 패턴 6편] 구조 패턴, 어댑터(Adapter) 참고.


컴퍼지트 (Composite)

컴퓨터의 파일과 디렉토리를 생각해보자.
크게 보면 파일과 디렉토리 둘 다 파일 시스템의 한 요소다.
다른 점이 있다면, 파일은 파일 그 자체인 반면 디렉토리는 파일이나 디렉토리를 담을 수 있다.
그래서 파일 - 디렉토리 시스템은 구조적으로 디렉토리가 부모가 되고 하위 디렉토리 혹은 파일이 자식이 되는 트리구조다.

이처럼, 여러 개의 클래스가 크게 보면 같은 요소(Component) 에 속하지만, 여기에 속한 어떤 클래스(Composite)가 자기 자신 혹은 다른 클래스(Leaf)를 가질 수 있는 구조를 컴퍼지트 패턴이라고 한다.
이 패턴은 같은 개념의 클래스들 간에 소유 개념을 나타낸 트리 형태 묶고 싶을 때 사용한다.

구체적인 동작은 [디자인 패턴 7편] 구조 패턴, 컴퍼지트(Composite) 참고.


데코레이터 (Decorator)

크리스마스 시즌이 다가와 크리스마스 트리를 샀다고 해보자.
이 트리를 꾸밀 여러 장식(Decorator)들도 같이 샀다.
이제 이 크리스마스 트리에 반짝이는 전구를 붙이기도 하고, 트리 볼도 달아준다.
그런데 뭔가 어울리지 않는 거 같아, 트리볼은 다시 빼고 곰돌이 인형을 달아주었다. 한결 나은 거 같다.

이 처럼 각각의 기능을 담당하는 클래스(Decorator)들과 이 기능을 적용할 클래스(Component)를 분리한 뒤, 필요에 따라 동적으로 각 기능을 적용할 수 있는 구조를 데코레이터 패턴이라고 한다.
이 패턴은 주요 기능에 경우에 따라 부가적인 기능을 추가하거나 빼고 싶을 때 사용한다.

구체적인 동작은 [디자인 패턴 8편] 구조 패턴, 데코레이터(Decorator) 참고.


프록시 (Proxy)

돈을 대출받기 위해 은행에 가본다고 해보자.
일반적으로 은행에서는 기본적으로 대출 담당자가 대출 관련 업무를 처리해준다.
그런데 내가 찾아간 은행 A에서는 대출 담당자 이전에 대출 안내 담당자가 나를 먼저 맞이하여 준다.
이 담당자를 통해 간단한 서류 작성을 하거나(전처리), 내가 이전에 대출한 기록이 있는 경우 대출 담당자를 통하지 않고도 바로 대출(캐싱)을 해주고 있다.

이처럼, 구체적인 업무를 담당하고 있는 클래스에 접근하기 전에, 간단한 사전 작업 처리하는 클래스(Proxy)를 두는 구조를 프록시 패턴이라고 한다.
이 패턴은 주요 기능이 요청을 받아 수행하기 전에, 이 요청에 대한 부가적인 전처리들을 수행하는 로직을 세우고 싶을 때 사용한다.

구체적인 동작은 [디자인 패턴 9편] 구조 패턴, 프록시(Proxy) 참고.

어댑터와 프록시 패턴의 차이는 뭘까?

두 패턴 모두 기존에 존재하는 두 클래스 중간에 하나의 클래스(어댑터 혹은 프록시)를 두는 구조다.
따라서 전반적인 구조는 같아 보인다. 그럼 두 패턴의 차이는 뭘까?

한 마디로 말하면, 의도가 다르다.
어댑터는 하나의 인터페이스 맞추는 게 목적이고,
프록시는 다양한 방식으로 로직 컨트롤이 목적이다.

여기에 퍼사드 패턴에 대해 적지는 않았지만 (너무 간단한 내용이라...)
퍼사드 패턴 역시 비슷한 구조인데, 퍼사드는 복잡한 기능을 감추고 이를 하나의 심플한 인터페이스로 제공하는 것이 목적이다.


참고

[하마] 이승현 님 블로그인데, 평소에 정말 잘 참고하고 있다.
이 외에도 읽어볼거리가 꽤 많으므로, 공부하시는 분들은 종종 참고해보시길...