디자인 패턴 — 자주 만나는 풀이의 이름들
디자인 패턴 — 자주 만나는 풀이의 이름들
같은 문제를 풀다 보면 비슷한 형태가 반복됩니다. 1990 년대 객체지향이 보편화되면서 그런 반복을 이름 붙여 정리한 책이 나왔고, 이후 패턴은 개발 문화의 공통어가 됐습니다. 이 글은 패턴의 출자 · 23 GoF 패턴의 분류 · 자주 만나는 7 가지의 의미 · 함수형 등가물 · 안티패턴 · 패턴이 도구이지 목적이 아니라는 시각.
1. 디자인 패턴에 대한 이야기
대표적 텍스트는 1994 년 출간된 Design Patterns: Elements of Reusable Object-Oriented Software. 저자가 Erich Gamma · Richard Helm · Ralph Johnson · John Vlissides 네 명이라 "Gang of Four (GoF)" 라 불림. 이 책이 23 가지 패턴을 분류해 "디자인 패턴" 이라는 용어를 컴퓨터 과학에 정착.
GoF 보다 앞선 영감은 건축가 Christopher Alexander 의 A Pattern Language (1977). 건축의 반복되는 좋은 형태를 이름 붙인 책으로, GoF 가 같은 발상을 소프트웨어로.
이후 Martin Fowler 의 Patterns of Enterprise Application Architecture (2002), 도메인 주도 설계의 전술 패턴, 동시성 패턴, 함수형 패턴 등이 부풀어 오늘에.
2. GoF 23 패턴의 분류
| 분류 | 패턴 |
|---|---|
| 생성 (Creational) | Singleton · Factory Method · Abstract Factory · Builder · Prototype |
| 구조 (Structural) | Adapter · Bridge · Composite · Decorator · Facade · Flyweight · Proxy |
| 행동 (Behavioral) | Chain of Responsibility · Command · Iterator · Mediator · Memento · Observer · State · Strategy · Template Method · Visitor · Interpreter |
23 개를 다 외우는 일은 드뭅니다. 자주 등장하는 7 개 정도만 익숙해도 충분히 멀리.
3. Singleton — 인스턴스가 하나뿐
전역적으로 유일한 객체. 설정 객체 · 로거 · DB 풀에서 자주:
// 자주 보는 형태
const config = { apiUrl: "...", timeout: 5000 };
export default config;
// 클래스 형태
class Logger {
static instance;
static getInstance() {
if (!Logger.instance) Logger.instance = new Logger();
return Logger.instance;
}
}
남용 시 테스트 어려움 (전역 상태) · 멀티스레드 동기화 문제. "전역 변수의 객체지향 분장" 이라는 비판이 자주.
4. Factory — 생성을 함수에 위임
직접 new 를 쓰지 않고 팩토리 함수에 어떤 종류를 만들지 위임:
function createUser(role) {
if (role === "admin") return new AdminUser();
if (role === "guest") return new GuestUser();
return new RegularUser();
}
호출자는 어떤 클래스가 만들어지는지 알 필요가 없음.
5. Observer — 변화를 알림
한 객체가 바뀌면 구독자들에게 통지:
class EventBus {
listeners = new Map();
on(event, fn) {
if (!this.listeners.has(event)) this.listeners.set(event, []);
this.listeners.get(event).push(fn);
}
emit(event, data) {
this.listeners.get(event)?.forEach(fn => fn(data));
}
}
브라우저의 addEventListener · Node 의 EventEmitter · RxJS 의 Observable 모두 이 패턴.
6. Strategy · Adapter · Decorator · Iterator
Strategy — 같은 인터페이스의 여러 구현을 런타임에 골라 씀:
const sortStrategies = {
asc: (a, b) => a - b,
desc: (a, b) => b - a,
byName: (a, b) => a.name.localeCompare(b.name),
};
arr.sort(sortStrategies[mode]);
Adapter — 기존 코드와 새 코드의 모양이 안 맞을 때 사이에 끼워 모양을 맞춤:
function fetchAllPromise() {
return new Promise((resolve, reject) => {
legacyLib.fetchAll((err, data) => err ? reject(err) : resolve(data));
});
}
Decorator — 객체 자체를 안 바꾸고 동작을 추가:
function withLogging(fn) {
return (...args) => {
console.log("call", fn.name, args);
const r = fn(...args);
console.log("ret", r);
return r;
};
}
const loggedAdd = withLogging((a, b) => a + b);
Python 의 @decorator · TS 의 @experimental_decorator · Java / Spring 의 어노테이션이 같은 발상.
Iterator — 내부 표현을 숨기고 순회 인터페이스만 노출:
const it = arr[Symbol.iterator]();
it.next(); // { value: 0, done: false }
for (const x of arr) { /* ... */ }
for...of · Python 의 for x in xs · Java 의 Iterable 모두 이 패턴.
7. 함수형 등가물
OOP 패턴 중 일부는 함수형 언어에서 함수 · 클로저로 자연스럽게 표현. "패턴이 사라진" 게 아니라 언어의 1 급 기능으로 흡수된 것:
| OOP 패턴 | 함수형 등가 |
|---|---|
| Strategy | 1 급 함수를 인자로 |
| Command | 클로저 |
| Observer | Reactive stream · pub-sub |
| Iterator | lazy sequence · generator |
| Decorator | 고차 함수 |
| Template Method | 함수 합성 |
| Singleton | 모듈 (1 번만 평가) |
Peter Norvig 의 1996 년 발표 Design Patterns in Dynamic Languages 가 23 패턴 중 16 개가 동적 언어에서 더 단순해진다는 점을 지적해 자주 인용.
8. GoF 외 자주 만나는 패턴군
- 엔터프라이즈 패턴 (Fowler 2002) — Repository · Unit of Work · DAO · MVC · MVP · MVVM.
- 동시성 패턴 — Producer-Consumer · Read-Write Lock · Actor · Future / Promise.
- 분산 시스템 패턴 — Circuit Breaker · Bulkhead · Retry · Saga · Event Sourcing · CQRS.
- DDD 전술 패턴 — Entity · Value Object · Aggregate · Domain Service.
- 함수형 패턴 — Functor · Monad · Applicative · Lens.
9. 패턴은 외우는 도구가 아니라 떠오르는 이름
코드를 짜다가 비슷한 모양이 반복되어 이름이 필요해질 때 떠올리는 것이 자연스러움:
1) 코드를 짠다 (패턴 의식 없이)
2) 비슷한 모양이 두 번째 보인다 → 그 모양에 익숙한 이름이 있는지 떠올린다
3) 이름이 있다면 패턴 적용. 같이 일하는 사람과 의사소통이 빨라짐
4) 이름이 없다면 굳이 만들지 않는다. 단순한 코드가 우선
안티 흐름 — "이번 프로젝트에 디자인 패턴 5 개 적용해 보자" → 보통 코드가 더 복잡해짐.
10. 자주 걸리는 자리
패턴 강박 (Pattern fetish) — 단순한 함수면 될 자리에 Factory · Strategy 를 끼워 코드가 두 배. 작은 코드에는 패턴이 짐.
God Object — 한 클래스가 너무 많은 일. 안티패턴.
Anemic Domain Model — 도메인 객체가 데이터 그릇만 되고 행동은 모두 서비스에. DDD 관점의 안티패턴.
Spaghetti Code — 흐름이 얽혀 추적 불가. 패턴 부재.
Singleton 남용 — 전역 상태로 테스트가 어려워짐. 의존성 주입 (DI) 으로 대체 가능한지 검토.
이름은 같지만 의도가 다른 — 같은 "Adapter" 이라도 GoF 정의와 자기 코드의 의도가 다를 수 있음. 단어가 같다고 같은 일은 아님.
패턴이 모든 문제의 답이라는 신화 — 패턴은 어휘일 뿐. 좋은 설계는 패턴을 적게 쓴다는 견해도 흔함.
하고픈 말
디자인 패턴은 외우는 도구가 아니라 의사소통의 어휘. 같은 모양이 두 번째 보일 때 이름을 붙이면 토론이 빨라집니다. 동적 언어 (JS · Python) 에서는 23 패턴 중 절반 이상이 1 급 함수 · 모듈로 흡수되어 더 단순. 좋은 설계가 패턴을 많이 쓴다는 신화는 그 반대일 수 있음 — KISS 가 먼저.
Next
- oop-vs-functional
- (programming 끝)
Design Patterns: Elements of Reusable Object-Oriented Software (1994) · A Pattern Language (Christopher Alexander 1977) · Patterns of Enterprise Application Architecture (Martin Fowler 2002) · Design Patterns in Dynamic Languages (Peter Norvig 1996) · Refactoring (Martin Fowler) · Refactoring.Guru · Game Programming Patterns (Robert Nystrom) · Domain-Driven Design (Eric Evans 2003) · Anti-Patterns (Wikipedia) 을 참고합니다.