소프트웨어 설계의 이정표 - 디자인 패턴의 역사와 23가지 핵심 분류
디자인 패턴은 어느 날 갑자기 천재적인 프로그래머가 발명해낸 마법 같은 공식이 아닙니다. 그것은 수많은 숙련된 개발자들이 반복되는 문제 앞에서 치열하게 고민하며 쌓아올린 경험의 산물입니다.

코드의 바다에서 길을 잃어본 적이 있는 개발자라면 한 번쯤 이런 생각을 해보셨을 겁니다. 왜 내가 마주하는 문제는 항상 어디선가 본 것만 같을까? 혹은 이 복잡한 구조를 나보다 앞서 길을 걷던 선배들은 어떻게 해결했을까? 하는 의문 말입니다. 디자인 패턴은 어느 날 갑자기 천재적인 프로그래머가 발명해낸 마법 같은 공식이 아닙니다. 그것은 수많은 숙련된 개발자들이 반복되는 문제 앞에서 치열하게 고민하며 쌓아올린 경험의 산물입니다. 흥미롭게도 이 여정의 시작은 컴퓨터 앞이 아닌 거친 공사 현장과 건축 도면 위였습니다.
건축의 지혜가 소프트웨어의 언어가 되기까지
1970년대 건축가 크리스토퍼 알렉산더는 건축물에서 반복되는 문제에는 항상 공통된 해결책이 있다는 사실을 포착했습니다. 그는 이를 패턴 언어라고 명명했습니다. 물론 건축과 소프트웨어는 서로 다른 영역처럼 보이지만 구조적인 안정성과 재사용성을 추구한다는 본질은 맞닿아 있습니다. 이 철학적 영감은 소프트웨어 공학자들에게 전해져 우리도 코드 설계의 반복되는 문제를 패턴화할 수 있지 않을까 하는 질문을 던지게 했습니다.
하지만 단순히 아이디어만으로는 부족했습니다. 90년대 들어 객체 지향 프로그래밍인 C++이 대중화되면서 시스템의 규모는 걷잡을 수 없이 커졌습니다. 클래스를 어떻게 나누고 연결해야 나중에 수정하기 편할까라는 실무적인 고민이 극에 달했을 때 비로소 검증된 설계 방식들이 공유되기 시작했습니다.
결국 1994년 에릭 감마를 비롯한 4명의 개발자가 당시 가장 뛰어난 설계 사례들을 수집해 Design Patterns: Elements of Reusable Object-Oriented Software를 출간하며 우리가 아는 23가지 패턴이 확립되었습니다. 이들을 일컬어 우리는 네 명의 갱 즉 **GoF(Gang of Four)**라 부릅니다.
23가지 패턴의 지도 그리기
디자인 패턴은 목적에 따라 크게 세 가지 카테고리로 나뉩니다. 각 패턴이 어떤 고민을 해결하려 하는지 그 핵심 역할을 살펴보면 전체적인 설계의 흐름이 보이기 시작할 것입니다.
1. 생성 패턴 (Creational Patterns)
생성 패턴은 객체가 어떻게 만들어지는가에 모든 정신을 집중합니다. 객체 생성 과정을 유연하게 만들거나 캡슐화하여 시스템의 결합도를 낮추는 역할을 합니다.
| 패턴 이름 | 핵심 설명 |
|---|---|
| 싱글톤 (Singleton) | 클래스의 인스턴스가 오직 하나만 존재하도록 보장합니다. |
| 팩토리 메서드 (Factory Method) | 객체 생성 처리를 서브 클래스로 미뤄 유연성을 높입니다. |
| 추상 팩토리 (Abstract Factory) | 연관된 객체들의 가족을 구체적인 클래스 지정 없이 생성합니다. |
| 빌더 (Builder) | 복잡한 객체를 단계별로 조립하여 생성합니다. |
| 프로토타입 (Prototype) | 기존 객체를 복사하여 새로운 객체를 만듭니다. |
2. 구조 패턴 (Structural Patterns)
구조 패턴은 클래스와 객체를 어떻게 조합하여 더 큰 구조를 만들지를 다룹니다. 서로 다른 인터페이스를 가진 객체들을 연결하거나 복잡한 시스템을 단순하게 보이게 만드는 마법을 부립니다.
| 패턴 이름 | 핵심 설명 |
|---|---|
| 어댑터 (Adapter) | 호환되지 않는 인터페이스를 가진 객체들이 협력할 수 있게 합니다. |
| 브릿지 (Bridge) | 구현부에서 추상층을 분리하여 각자 독립적으로 확장하게 합니다. |
| 컴포지트 (Composite) | 객체들을 트리 구조로 구성하여 개별과 복합 객체를 동일하게 다룹니다. |
| 데코레이터 (Decorator) | 객체에 동적으로 새로운 책임이나 기능을 추가합니다. |
| 퍼사드 (Facade) | 복잡한 서브시스템에 대한 단순화된 통합 인터페이스를 제공합니다. |
| 플라이웨이트 (Flyweight) | 많은 수의 소형 객체들을 공유하여 메모리 사용량을 줄입니다. |
| 프록시 (Proxy) | 다른 객체에 대한 접근을 제어하기 위한 대리자 역할을 수행합니다. |
3. 행동 패턴 (Behavioral Patterns)
행동 패턴은 객체 간의 통신과 책임 분배에 집중합니다. 복잡하게 얽힌 객체들이 어떻게 서로 메시지를 주고받으며 협력할지 그 알고리즘과 관계를 정의합니다.
| 패턴 이름 | 핵심 설명 |
|---|---|
| 전략 (Strategy) | 알고리즘을 캡슐화하여 실행 중에 교체 가능하게 만듭니다. |
| 옵저버 (Observer) | 한 객체의 상태 변화를 관찰자들에게 자동으로 통지합니다. |
| 상태 (State) | 객체 내부 상태에 따라 스스로 행동을 바꿀 수 있게 합니다. |
| 템플릿 메서드 (Template Method) | 알고리즘의 골격은 유지하되 구체적 단계는 자식 클래스에서 정의합니다. |
| 커맨드 (Command) | 요청을 객체로 캡슐화하여 매개변수화하거나 취소를 지원합니다. |
| 이터레이터 (Iterator) | 컬렉션의 내부 구조를 노출하지 않고 요소들에 순차적으로 접근합니다. |
| 중재자 (Mediator) | 객체들 간의 복잡한 상호작용을 하나의 객체로 집약시킵니다. |
| 메멘토 (Memento) | 객체의 내부 상태를 저장해 두었다가 나중에 복구합니다. |
| 책임 연쇄 (Chain of Responsibility) | 요청을 처리할 수 있는 객체를 만날 때까지 고리를 따라 전달합니다. |
| 방문자 (Visitor) | 데이터 구조와 처리를 분리하여 새로운 연산을 기존 구조 변경 없이 추가합니다. |
| 인터프리터 (Interpreter) | 특정 언어의 문법 표현과 해석기를 정의합니다. |
어디서부터 시작해야 할까요
23가지의 지도를 펼쳐보았지만 막상 실무에 적용하려고 하면 막막함이 앞설 수 있습니다. 모든 패턴을 한꺼번에 외우려 하기보다는 여러분이 지금 겪고 있는 설계의 통증이 무엇인지 먼저 진단해보는 것은 어떨까요?
인터페이스에 집중하라와 구성을 사용하라는 GoF의 철학은 시대를 불문하고 유효합니다. 하지만 과도한 패턴 적용은 오히려 코드를 이해하기 어렵게 만드는 독이 되기도 합니다. 빛이 있으면 그림자가 있듯 기술의 유연함 뒤에는 복잡성이라는 대가가 따르기 때문입니다.
여러분은 어떤 방향으로 더 깊이 들어가 보고 싶으신가요? 객체를 만드는 기본기부터 익히는 생성 패턴일까요 아니면 실무에서 가장 활용도가 높은 전략이나 옵저버 패턴 같은 인기 패턴들일까요? 혹은 비슷한 패턴들이 가진 미묘한 차이점에 대해 탐구해보고 싶으신가요?
본 글이 여러분의 코드 설계에 작은 이정표가 되었기를 바랍니다.