노트

데코레이터 패턴

Decorator

설계#pattern · 연결된 개념 9개

쉽게 말하면

데코레이터 패턴은 스마트폰에 케이스, 보호 필름, 그립톡을 하나씩 덧씌우는 것과 같아요. 폰 자체는 손대지 않고 겹겹이 감싸며 기능을 더하고, 조합도 그때그때 고를 수 있죠.

비유가 깨지는 곳 폰 케이스는 끼우는 순서가 별 상관없지만, 데코레이터는 감싸는 순서가 결과를 바꿔요. 로깅과 재시도의 순서를 바꾸면 재시도마다 로그가 찍히고, 겹이 많으면 디버깅도 어려워져요.

같은 인터페이스를 구현한 래퍼로 객체를 감싸, 원래 객체를 고치지 않고 앞뒤에 기능을 덧붙이는 패턴. 래퍼를 여러 겹 쌓아 기능을 조합할 수 있다.

interface Fetcher { get(url: string): Promise<Response> }
 
const withLogging = (inner: Fetcher): Fetcher => ({
  async get(url) {
    console.time(url)
    try { return await inner.get(url) } finally { console.timeEnd(url) }
  },
})
const withRetry = (inner: Fetcher, times = 3): Fetcher => ({
  async get(url) {
    for (let i = 1; ; i++) {
      try { return await inner.get(url) } catch (e) { if (i >= times) throw e }
    }
  },
})
 
const fetcher = withLogging(withRetry(baseFetcher))
  • 상속으로 "로깅되는 재시도 fetcher" 같은 조합을 만들면 조합 수만큼 클래스가 폭발한다. 데코레이터는 조합을 실행 시점에 고른다(상속보다 위임)
  • 감싸는 순서가 결과에 영향을 준다. 위 예에서 순서를 바꾸면 재시도마다 로그가 찍힌다
  • 여러 겹이면 디버깅할 때 호출 흐름을 따라가기 어렵다
  • 입출력 스트림에 버퍼링·압축·암호화를 겹치는 것이 고전적인 예이고, React의 고차 컴포넌트(Higher-Order Component, HOC)도 같은 모양이다

언어의 데코레이터 문법과 구분

파이썬의 @decorator(파이썬 데코레이터)나 TypeScript 데코레이터(TypeScript 데코레이터)는 함수·클래스를 감싸는 문법이다. 이 패턴을 구현하는 데 쓸 수 있지만 같은 것은 아니다. 패턴은 객체를 실행 중에 감싸는 설계이고, 문법은 정의 시점에 변환을 적용하는 도구다.

접근을 통제하려고 감싸면 프록시, 인터페이스를 바꾸려고 감싸면 어댑터다. 구조 패턴 전체는 구조 패턴.

출처: Decorator — Refactoring.Guru

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 디자인 패턴

    반복해서 나타나는 설계 문제에 대한 검증된 해결 구조와 그 이름. 복사해 쓰는 완성 코드가 아니라 객체들이 협력하는 방식을 설명하는 어휘다. 1994년 이른바 GoF(Gang of Four, 네 명의 저자)의 책 『Design Patterns: Elements of Reusable Object-Oriented Software』가 23개 패턴을 정리하며 널리 퍼졌다.

  • 커맨드 패턴

    요청이나 작업을 실행 가능한 객체로 포장하는 패턴. 작업을 값처럼 저장·전달·대기열에 넣을 수 있게 되고, 되돌리기 같은 부가 연산을 붙일 수 있다.

  • 상태 패턴

    객체의 행동이 현재 상태에 따라 달라질 때, 상태마다 별도 객체를 두고 현재 상태 객체에 행동을 맡기는 패턴. 상태별 조건문이 메서드마다 반복되는 문제를 푼다.

  • 퍼사드 패턴

    복잡한 하위 시스템 앞에 단순한 창구 하나를 세워, 호출자가 내부의 여러 객체와 호출 순서를 몰라도 되게 하는 패턴.

  • 전략 패턴

    같은 목적을 가진 여러 알고리즘을 각각 별도 객체(또는 함수)로 빼고, 실행할 때 그중 하나를 골라 끼우는 패턴. 호출하는 쪽은 공통 인터페이스만 안다.

보기 옵션