목차8개 항목
출처와 참고
ByteByteGo의 System Design 101 가이드 「18 Key Design Patterns Every Developer Should Know」, 「Design Patterns Cheat Sheet」를 바탕으로 재구성한 노트다. GoF 밖의 패턴은 「SourceMaking: Design Patterns」로 보강했다.
디자인 패턴이란
디자인 패턴은 자주 마주치는 설계 문제에 대한, 이름 붙은 재사용 가능한 해법이다. 코드 조각이 아니라 클래스와 객체를 어떻게 배치하고 책임을 나눌지에 대한 청사진이다. 1994년 GoF(Gang of Four)의 책 『Design Patterns』가 23개 패턴을 생성·구조·행위로 분류해 정리했다.
패턴의 가장 큰 가치는 공통 어휘다. "여기는 옵저버로 하자"는 한마디가 긴 설명을 대신한다.
생성 패턴: 객체를 어떻게 만드나
| 패턴 | 비유 | 푸는 문제 |
|---|---|---|
| 팩토리 메서드(Factory Method) | 주문서 | 어떤 구체 클래스를 만들지 하위 클래스가 정한다. 호출하는 쪽은 인터페이스만 안다 |
| 추상 팩토리(Abstract Factory) | 가족 단위 생산자 | 서로 어울리는 객체 묶음(예: 같은 테마의 버튼·체크박스)을 일관되게 만든다 |
| 빌더(Builder) | 레고 조립 | 선택 항목이 많은 객체를 단계별로 조립한다. 만드는 과정과 결과 표현을 분리한다 |
| 프로토타입(Prototype) | 복제기 | 준비된 원본을 복제해 새 객체를 만든다. 생성 비용이 클 때 |
| 싱글톤(Singleton) | 단 하나 | 인스턴스를 하나만 두고 어디서나 같은 것을 쓴다. 전역 상태가 되어 테스트를 어렵게 만들기 쉽다. 스레드 안전한 구현은 싱글턴 패턴 |
구조 패턴: 객체를 어떻게 조합하나
| 패턴 | 비유 | 푸는 문제 |
|---|---|---|
| 어댑터(Adapter) | 여행용 플러그 | 인터페이스가 맞지 않는 클래스를 이어 붙인다 |
| 브리지(Bridge) | 연결기 | "무엇을 하나(추상)"와 "어떻게 하나(구현)"를 분리해 둘을 따로 확장한다 |
| 컴포지트(Composite) | 나무 | 단일 객체와 객체 묶음을 같은 방식으로 다룬다. 폴더·파일, UI 트리 |
| 데코레이터(Decorator) | 꾸미기 | 원래 객체를 감싸 기능을 덧붙인다. 상속 없이 기능을 조합한다 |
| 퍼사드(Facade) | 원스톱 창구 | 복잡한 하위 시스템 앞에 단순한 창구 하나를 둔다 |
| 플라이웨이트(Flyweight) | 공유 부품 | 같은 내부 상태를 가진 작은 객체를 공유해 메모리를 아낀다 |
| 프록시(Proxy) | 대역 배우 | 진짜 객체 앞에서 접근 제어, 지연 로딩, 캐싱, 로깅을 대신한다 |
행위 패턴: 객체가 어떻게 협력하나
| 패턴 | 비유 | 푸는 문제 |
|---|---|---|
| 책임 연쇄(Chain of Responsibility) | 릴레이 | 요청을 처리할 수 있는 객체가 나올 때까지 사슬을 따라 넘긴다. 미들웨어 체인 |
| 커맨드(Command) | 작업 봉투 | 요청을 객체로 감싸 큐에 넣고, 기록하고, 되돌린다(undo) |
| 이터레이터(Iterator) | 탐험가 | 컬렉션 내부 구조를 드러내지 않고 하나씩 꺼낸다 |
| 미디에이터(Mediator) | 관제탑 | 객체끼리 직접 얽히지 않고 중재자를 통해서만 대화하게 한다 |
| 메멘토(Memento) | 타임캡슐 | 캡슐화를 깨지 않고 객체 상태를 저장했다가 복원한다 |
| 옵저버(Observer) | 뉴스 방송 | 한 객체의 변화를 구독자들에게 자동으로 알린다. 이벤트 시스템의 뿌리 |
| 비지터(Visitor) | 출장 전문가 | 클래스를 고치지 않고 새 연산을 추가한다. 컴파일러의 AST 순회 |
| 전략(Strategy) | 갈아 끼우는 도구 | 알고리즘을 객체로 빼 실행 중에 바꾼다 |
| 템플릿 메서드(Template Method) | 빈칸 채우기 | 알고리즘 뼈대는 상위 클래스가, 세부 단계는 하위 클래스가 정한다 |
| 상태(State) | 모드 전환 | 상태별 행동을 객체로 나눠 거대한 조건문을 없앤다 |
GoF의 23개 중 나머지(인터프리터)는 작은 언어의 문법을 클래스로 표현하는 패턴이다.
GoF 밖에서 자주 쓰는 패턴
| 패턴 | 하는 일 | 쓰임 |
|---|---|---|
| 객체 풀(Object Pool) | 만들기 비싼 객체를 미리 만들어 두고 빌려 쓰고 돌려준다 | DB 커넥션 풀, 스레드 풀. 반납을 잊으면 풀이 마른다 |
| 널 객체(Null Object) | 「없음」을 아무 일도 하지 않는 대체 객체로 표현한다 | null 검사 분기를 없앤다. 기본 로거, 빈 할인 정책 |
| 비공개 클래스 데이터(Private Class Data) | 속성들을 별도 데이터 객체로 묶어 생성 뒤 바꿀 수 없게 숨긴다 | 불변 값 객체, 생성 후 수정되면 안 되는 설정 |
헷갈리는 짝
| 짝 | 차이 |
|---|---|
| 어댑터 vs 퍼사드 | 어댑터는 이미 있는 인터페이스를 다른 인터페이스로 바꾼다. 퍼사드는 여러 개를 감싸 새 단순한 인터페이스를 만든다 |
| 데코레이터 vs 프록시 | 구조는 같다(감싸기). 데코레이터는 기능을 더하고, 프록시는 접근을 통제한다 |
| 전략 vs 상태 | 전략은 바깥에서 알고리즘을 골라 끼운다. 상태는 객체가 스스로 다음 상태로 바뀐다 |
| 팩토리 메서드 vs 추상 팩토리 | 하나를 만드는 메서드 vs 관련된 여러 개를 만드는 객체 |
언제 쓰나
- 문제가 먼저, 패턴은 나중이다. 패턴을 적용할 곳을 찾아다니면 과설계가 된다. 같은 변경이 두세 번 반복될 때 패턴으로 정리한다.
- 언어 기능이 패턴을 대신하기도 한다. 일급 함수가 있으면 전략·커맨드는 함수 하나로 끝나고, 이터레이터는 언어의 반복 프로토콜이 맡는다.
- 다중 스레드 환경에 특화된 패턴(생산자-소비자, 스레드 풀 등)은 멀티스레딩 디자인 패턴에 있다. JavaScript로 구현한 생성 패턴은 JavaScript 디자인 패턴에 있다.
- 패턴은 UML 클래스 다이어그램으로 그려 설명하는 경우가 많다. 표기법은 소프트웨어 설계와 공학에 있다.
- 주차장·ATM·체스 같은 객체 지향 설계 면접 사례 어디에 어떤 패턴이 쓰이는지는 객체 지향 설계 면접에 모아 두었다.
정리
- 디자인 패턴은 반복되는 설계 문제에 대한 이름 붙은 해법이고, 무엇보다 팀의 공통 어휘다.
- 생성 패턴은 만드는 법, 구조 패턴은 조합하는 법, 행위 패턴은 협력하는 법을 다룬다.
- 구조가 비슷한 패턴은 의도로 구분한다.
- 패턴을 쓰려고 문제를 만들지 않는다. 쓰지 않아도 될 곳의 패턴은 복잡도만 더한다.
관련 노트: JavaScript 디자인 패턴: 생성 패턴, 소프트웨어 아키텍처 패턴, 프로그래밍 패러다임, 좋은 코드를 위한 열 가지 원칙, 프론트엔드와 SOLID 원칙, 객체 지향 설계 면접: 풀이 순서와 반복되는 설계 부품