Decorator Pattern

Decorator Pattern 은 객체에 새로운 책임을 동적으로 추가할 수 있도록 설계된 구조 패턴이다. Component 인터페이스를 구현한 ConcreteComponent 를 래핑하는 Decorator 는 Component 인터페이스를 동일하게 구현하며, 내부에 참조할 Component 를 갖는다. 이를 통해 런타임에 서로 다른 Decorator 들을 조합하여 기능을 확장하거나 수정할 수 있다. 상속을 최소화하고, Open–Closed 원칙을 준수하는 유연한 확장 구조가 특징이다. 입출력 스트림 (I/O), UI 스타일링, 로깅, 인증, 트랜잭션 등 다양한 실무에 적용되며, 단방향 책임 분리 및 동적 기능 조합이 핵심 장점이다.

배경

데코레이터 패턴은 다음과 같은 문제를 해결하기 위해 개발되었다:

전통적인 상속의 문제점:

해결 배경:

목적 및 필요성

주요 목적

  1. 동적 기능 추가: 런타임에 객체의 기능을 동적으로 확장
  2. 유연한 조합: 여러 기능을 자유롭게 조합
  3. 코드 재사용성: 기존 코드 수정 없이 기능 확장
  4. 단일 책임 원칙: 각 데코레이터가 하나의 책임만 담당

필요성

핵심 개념

Decorator Pattern 은 기존 객체의 구조를 변경하지 않고 기능을 동적으로 추가하거나 확장하는 구조 (Structural) 디자인 패턴이다. 조합 (Composition) 을 기반으로 하여 상속의 한계를 보완한다.

기본 개념

심화 개념

실무 구현을 위한 필수 개념들

  1. 컴포넌트 인터페이스 (Component Interface)

    • 기본 기능을 정의하는 공통 인터페이스
    • 모든 구체적 컴포넌트와 데코레이터가 구현해야 함
    • 클라이언트가 일관된 방식으로 객체를 사용할 수 있게 함
  2. 구체적 컴포넌트 (Concrete Component)

    • 기본 기능을 구현하는 원본 객체
    • 데코레이터로 감쌀 수 있는 핵심 객체
  3. 기본 데코레이터 (Base Decorator)

    • 컴포넌트 인터페이스를 구현하면서 다른 컴포넌트를 래핑
    • 기본적으로는 래핑된 객체에 작업을 위임
  4. 구체적 데코레이터 (Concrete Decorator)

    • 실제 추가 기능을 구현하는 클래스
    • 기본 데코레이터를 상속받아 특정 기능을 추가

의도 (Intent)

객체에 동적으로 새로운 책임을 추가할 수 있게 하며, 기능 확장에 있어 서브클래싱보다 융통성 있는 방법을 제공한다.

다른 이름 (Also Known As)

동기 (Motivation / Forces)

적용 가능성 (Applicability)

패턴이 해결하고자 하는 설계 문제

문제 유형문제 상황주요 원인영향 및 한계점
클래스 폭발 문제기능 조합마다 새로운 클래스를 생성해야 함예: 텍스트에 볼드/이탤릭/밑줄 조합 시 2³ = 8 클래스 필요기능을 조합하는 방식이 상속 기반의 정적 조합에 의존클래스 수가 지수적으로 증가, 설계 복잡도 및 유지보수 부담 증가
상속의 한계상속 구조에서는 동적 기능 조합이 불가능, 런타임 변경 불가정적 상속 모델의 제약다중 상속의 복잡성유연성 부족, 기능 확장 시 코드 변경 불가, OCP(Open-Closed) 위배
기능별 관심사 분리하나의 클래스가 여러 기능 (책임) 을 동시에 가짐예: 렌더링 + 로깅 + 검증 로직이 한 클래스에 포함됨SRP(Single Responsibility Principle) 위반기능 단위 테스트 어려움, 코드 중복 증가, 재사용성과 유지보수성 저하

문제를 해결하기 위한 설계 구조와 관계

설계 구조
graph LR
    subgraph "Traditional Inheritance Problem"
        A[Text] --> B[BoldText]
        A --> C[ItalicText] 
        A --> D[UnderlineText]
        A --> E[BoldItalicText]
        A --> F[BoldUnderlineText]
        A --> G[ItalicUnderlineText]
        A --> H[BoldItalicUnderlineText]
    end
    
    subgraph "Decorator Pattern Solution"
        I[Text] --> J[TextDecorator]
        J --> K[BoldDecorator]
        J --> L[ItalicDecorator]
        J --> M[UnderlineDecorator]
        
        N[text] --> O[BoldDecorator]
        O --> P[ItalicDecorator]
        P --> Q[UnderlineDecorator]
    end
해결 방식
  1. 컴포지션 활용: 상속 대신 객체 래핑으로 기능 조합
  2. 동적 구성: 런타임에 필요한 기능만 선택적 적용
  3. 재귀적 구조: 데코레이터가 다른 데코레이터를 래핑 가능

주요 기능 및 역할

핵심 기능

  1. 투명한 래핑: 클라이언트는 원본과 데코레이터를 구분하지 않음
  2. 연쇄적 래핑: 여러 데코레이터를 중첩하여 사용 가능
  3. 선택적 기능: 필요한 기능만 조합하여 사용
  4. 런타임 구성: 실행 시점에 객체 구성 결정

주요 역할

특징

핵심 원칙

  1. 개방 - 폐쇄 원칙 (Open-Closed Principle): 확장에는 열려있고 수정에는 닫힘
  2. 단일 책임 원칙 (Single Responsibility Principle): 각 데코레이터가 하나의 책임만 담당
  3. 컴포지션 우선 원칙: 상속보다 컴포지션 활용
  4. 인터페이스 분리 원칙: 클라이언트가 사용하지 않는 인터페이스에 의존하지 않음

주요 원리 및 작동 원리

  1. Client 는 Component 인터페이스로 객체를 사용
  2. Decorator 는 Component 를 감싸고, 기능 호출을 내부 Component 에 위임
  3. ConcreteDecorator 는 위임 전후로 추가 기능을 수행
  4. 여러 Decorator 를 중첩해 다양한 기능 조합 가능

작동 원리

sequenceDiagram
    participant Client
    participant ConcreteDecoratorB
    participant ConcreteDecoratorA
    participant ConcreteComponent
    
    Client->>ConcreteDecoratorB: operation()
    ConcreteDecoratorB->>ConcreteDecoratorB: addedBehavior()
    ConcreteDecoratorB->>ConcreteDecoratorA: operation()
    ConcreteDecoratorA->>ConcreteDecoratorA: addedBehavior()
    ConcreteDecoratorA->>ConcreteComponent: operation()
    ConcreteComponent->>ConcreteDecoratorA: result
    ConcreteDecoratorA->>ConcreteDecoratorB: enhanced result
    ConcreteDecoratorB->>Client: final result

데코레이터 패턴의 작동 원리는 연쇄적 위임 (Chain of Delegation) 이다. 각 데코레이터는 자신의 기능을 수행한 후 다음 컴포넌트에게 작업을 위임하거나, 반대로 위임받은 결과를 가공하여 반환한다.

패턴 적용의 결과와 트레이드오프

긍정적 결과
트레이드오프

구조 및 아키텍처

Decorator 패턴은 기본 객체를 데코레이터 객체로 감싸는 구조를 통해 기능을 확장한다. 각 데코레이터는 동일한 인터페이스를 구현하여, 클라이언트는 데코레이터와 기본 객체를 동일하게 취급할 수 있다. 이러한 구조는 기능의 조합과 확장을 유연하게 만들어 준다.

전체 구조

classDiagram
    class Component {
        <<interface>>
        +operation()
    }
    
    class ConcreteComponent {
        +operation()
    }
    
    class BaseDecorator {
        -Component wrappee
        +BaseDecorator(Component c)
        +operation()
    }
    
    class ConcreteDecoratorA {
        +operation()
        +extraBehavior()
    }
    
    class ConcreteDecoratorB {
        -String addedState
        +operation()
        +anotherBehavior()
    }
    
    Component <|-- ConcreteComponent
    Component <|-- BaseDecorator
    BaseDecorator <|-- ConcreteDecoratorA
    BaseDecorator <|-- ConcreteDecoratorB
    BaseDecorator o-- Component : wraps

구성요소

구분구성요소기능역할특징
필수Component(컴포넌트 인터페이스)기본 작업을 정의하는 공통 인터페이스클라이언트와 구현체 간의 계약 정의모든 구체적 컴포넌트와 데코레이터가 구현함
ConcreteComponent(구체적 컴포넌트)기본 동작을 구현데코레이터로 래핑될 수 있는 원본 객체추가 기능 없이 순수한 기본 기능만 제공
BaseDecorator(기본 데코레이터)Component 인터페이스 구현 + Component 객체 래핑모든 구체적 데코레이터의 기본 구조 제공래핑된 객체에게 기본 작업을 위임
ConcreteDecorator(구체적 데코레이터)실제 추가 기능 구현특정 기능을 기존 객체에 추가기존 기능 확장 또는 새로운 상태/행동 추가 가능
선택DecoratorFactory(데코레이터 팩토리)데코레이터 생성을 관리복잡한 데코레이터 조합을 캡슐화선택적 사용, 객체 생성 복잡성 감소
ConfigurableDecorator(설정 가능한 데코레이터)런타임에 데코레이터의 동작을 설정 가능유연한 기능 조정 제공매개변수 기반 동작 변경 가능, 전략 패턴과 병행 가능

참여자들 간의 상호작용 방식

호출 흐름
sequenceDiagram
    participant C as Client
    participant D1 as DecoratorA
    participant D2 as DecoratorB
    participant CC as ConcreteComponent
    
    C->>D1: operation()
    Note over D1: Pre-processing
    D1->>D2: operation()
    Note over D2: Pre-processing
    D2->>CC: operation()
    CC->>D2: result
    Note over D2: Post-processing
    D2->>D1: enhanced result
    Note over D1: Post-processing
    D1->>C: final result

구현 기법

카테고리구현 방식핵심 구성 요소주요 목적 / 특징예시
1. 객체지향 기반클래식 구조 기반인터페이스, 기본 클래스, 추상 데코레이터, 구체 데코레이터타입 안정성, 명확한 구조, 확장 가능한 계층화 구조Java I/O Stream, Python Class 기반 데코레이터
컴포지션 + 위임 구조내부에 Component 참조, operation() 위임상속 대신 합성을 통한 유연한 기능 조합ConcreteDecoratorA(Component)
중첩 조합 구성여러 데코레이터 연속 조합다단계 기능 확장 및 체이닝 구현DecoratorB(DecoratorA(Component))
2. 함수형/고차 함수함수형 데코레이터고차 함수, 클로저, 데코레이터 함수간결한 문법, 동적 적용, 실행 전후 기능 삽입Python @decorator, JavaScript wrapper 함수
3. 메타데이터 기반어노테이션/리플렉션 기반어노테이션 + 리플렉션, AOP 컨테이너선언적 구성, 관심사 분리, 자동 적용Java Annotation, Spring AOP, NestJS Decorator
4. 프록시 기반프록시 + 인터셉터 구성프록시 객체, 인터셉터, 핸들러 (핵심 기능과 추가 기능 분리)호출 흐름 캡처 및 투명한 기능 확장Spring Proxy,.NET Castle Proxy
5. DI 기반 구조의존성 주입 + 자동 조합IoC 컨테이너, 데코레이터 체인 자동 등록생명주기 관리, 동적 구성 및 테스트 유연성Scrutor(.NET), Spring Bean PostProcessor
6. 실행 시점 제어런타임 적용 / 지연 평가데코레이터 인스턴스를 실행 중 동적으로 구성동적 구성 가능, 구성 유연성 극대화전략 변경 가능한 런타임 래퍼 구성

클래식 OOP 기반 데코레이터 구현 기법

코드 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 기본 컴포넌트
class Coffee:
    def cost(self):
        return 0

class BasicCoffee(Coffee):
    def cost(self):
        return 2

# 데코레이터 기본 구조
class CoffeeDecorator(Coffee):
    def __init__(self, coffee):
        self._coffee = coffee
    def cost(self):
        return self._coffee.cost()

# 구체 데코레이터
class MilkDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 1

class SugarDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 0.5

# 사용 예시
coffee = SugarDecorator(MilkDecorator(BasicCoffee()))
print(coffee.cost())  # 출력: 3.5

함수형 데코레이터 (Functional Decorator)

코드 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"실행 시간: {time.time() - start:f}초")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(1)
    return "완료"

# 사용 예시
print(slow_function())  # 출력: 완료 (실행 시간: 약 1초)

어노테이션 기반 데코레이터

구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 공통 관심사를 정의한 Aspect
@Aspect
@Component
public class LoggingAspect {
    @Before("@annotation(LogExecution)")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("메서드 실행 전: " + joinPoint.getSignature().getName());
    }
}

// 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
}

// 사용
@Service
public class UserService {
    @LogExecution
    public void registerUser() {
        System.out.println("사용자 등록 중…");
    }
}

프록시 기반 데코레이터 (Proxy-Based Decorator)

구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Service:
    def process(self):
        print("실제 서비스 처리")

class LoggingProxy:
    def __init__(self, target):
        self._target = target

    def process(self):
        print("[로그] 호출 전")
        self._target.process()
        print("[로그] 호출 후")

# 사용
service = LoggingProxy(Service())
service.process()
1
2
3
[로그] 호출 전
실제 서비스 처리
[로그] 호출 후

의존성 주입 기반 데코레이터 (Dependency Injection Decorator)

구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Component
@Primary
public class CachingUserService implements UserService {
    private final UserService delegate;

    public CachingUserService(@Qualifier("userServiceImpl") UserService delegate) {
        this.delegate = delegate;
    }

    @Override
    public User findUser(String id) {
        // 캐시 검사
        return delegate.findUser(id); // 원본 호출
    }
}

런타임 플러그인 기반 데코레이터 (Dynamic Plugin Decorator)

구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Processor {
  run(data) {
    return data;
  }
}

function logDecorator(processor) {
  return {
    run: (data) => {
      console.log("입력:", data);
      const result = processor.run(data);
      console.log("출력:", result);
      return result;
    }
  };
}

// 동적 조합
let base = new Processor();
base = logDecorator(base);

base.run("Hello");
// 출력:
// 입력: Hello
// 출력: Hello

Decorator vs. Composite vs. Proxy

항목Decorator (데코레이터)Composite (컴포지트)Proxy (프록시)
목적객체의 기능을 동적으로 확장객체의 계층 구조 표현 및 일관된 방식으로 처리객체 접근 제어 또는 지연 초기화
구조적 핵심객체를 래핑 (wrapping) 하여 새로운 기능 추가트리 구조 구성 (Leaf/Composite)실제 객체에 대한 인터페이스를 동일하게 유지
사용 조건런타임 중 기능 확장이 필요할 때계층적 구조를 동일한 방식으로 처리할 때접근 제어, 로깅, 캐싱, 원격 호출 등이 필요할 때
동적 기능 변경가능 (조합 순서에 따라 변화)불가 (계층 구조 고정적)제한적 (기능 위임 중심)
클래스 수 증가많아짐 (데코레이터마다 클래스 필요)중간 수준 (Leaf/Composite 구분)적음 (프록시와 실제 객체 1:1)
대표 사용 사례스트림 처리 (Java I/O), 인증, 캐싱폴더 - 파일 트리 구조, UI 컴포넌트Virtual Proxy, Remote Proxy, Protection Proxy
조합/중첩중첩 가능 (계속 감싸기)자식들을 포함하는 구조중첩보단 단일 위임 중심
대표 원칙 기반개방 - 폐쇄 원칙 (OCP), 책임 연쇄전체 - 부분 계층 구조대리 제어 (Surrogate Control)

공통된 주제: 도형(Shape) 그리기 시스템

공통 설정: Shape 인터페이스

1
2
3
4
5
6
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass
패턴목적구성 구조기능 추가 방식공통 인터페이스내부 객체 참조 여부
Composite전체 - 부분 관계 표현 및 계층 구조 처리객체 트리 구성 (ShapeGroup 이 하위 Shape 포함)재귀적으로 구조화됨 (기능 조합 아님)✅ (자식 목록 참조)
Decorator객체에 기능을 동적으로 추가래퍼 객체 (Decorator) 가 원본 객체 감싸기기능을 계층적으로 누적 (연쇄 호출)✅ (하나의 객체 참조)
Proxy접근 제어, 로깅, 보안, 지연 로딩 등프록시 객체가 진짜 객체 대신 요청 처리실제 객체의 기능 호출 전/후 가로채기✅ (진짜 객체 참조)
Composite Pattern

목적: 개별 객체와 복합 객체 (트리 구조) 를 동일하게 처리 (부분 - 전체 계층 구조)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Circle(Shape):
    def draw(self):
        print("Draw Circle")

class Rectangle(Shape):
    def draw(self):
        print("Draw Rectangle")

class ShapeGroup(Shape):  # Composite
    def __init__(self):
        self.children = []

    def add(self, shape: Shape):
        self.children.append(shape)

    def draw(self):
        print("Draw Group Start")
        for child in self.children:
            child.draw()
        print("Draw Group End")

# 사용 예
circle = Circle()
rect = Rectangle()
group = ShapeGroup()
group.add(circle)
group.add(rect)
group.draw()

특징:

Decorator Pattern

목적: 기존 객체에 동적으로 새로운 기능을 추가

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class BaseCircle(Shape):
    def draw(self):
        print("Draw Circle")

class ShapeDecorator(Shape):  # Decorator (추상)
    def __init__(self, shape: Shape):
        self._shape = shape

    def draw(self):
        self._shape.draw()

class BorderDecorator(ShapeDecorator):
    def draw(self):
        self._shape.draw()
        print("Add Border")

class ShadowDecorator(ShapeDecorator):
    def draw(self):
        self._shape.draw()
        print("Add Shadow")

# 사용 예
circle = BaseCircle()
decorated = ShadowDecorator(BorderDecorator(circle))
decorated.draw()

특징:

Proxy Pattern

목적: 접근 제어 또는 부가 기능을 대신 수행하는 객체

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class RealCircle(Shape):
    def draw(self):
        print("Draw Circle")

class CircleProxy(Shape):
    def __init__(self):
        self._real_circle = None

    def draw(self):
        if self._real_circle is None:
            print("Initializing RealCircle…")
            self._real_circle = RealCircle()
        print("Access granted to RealCircle")
        self._real_circle.draw()

# 사용 예
proxy = CircleProxy()
proxy.draw()  # 실제 객체는 내부에서 생성됨

특징:

장점

카테고리항목설명
확장성 (유연한 구조)동적 기능 확장컴파일이 아닌 런타임 시점에 기능을 동적으로 추가/변경/조합할 수 있어 높은 유연성을 제공함
무제한 기능 조합 가능다양한 데코레이터를 조합해 복잡한 기능 구성 가능. 기능 요구사항에 따라 손쉽게 확장 가능
계층 구조 최소화상속 기반 설계보다 컴포지션 기반으로 계층 구조를 단순화할 수 있음
설계 원칙 준수개방 - 폐쇄 원칙 (OCP) 준수기존 코드를 변경하지 않고 새로운 기능을 확장할 수 있어 유지보수성과 확장성 모두 확보 가능
단일 책임 원칙 (SRP) 준수각 데코레이터가 하나의 기능만을 담당하도록 설계하여 모듈화와 응집도를 높임
재사용성코드 재사용성기능 단위 데코레이터를 다양한 컴포넌트에 조합하여 활용할 수 있어 코드 중복 최소화 및 유지보수 효율화
모듈화된 기능 제공공통 기능을 별도 클래스로 분리하여 범용적으로 재사용 가능
일관성 및 유지보수인터페이스 일관성모든 데코레이터는 동일한 인터페이스를 구현하여 클라이언트 입장에서 사용 방식이 일관되고 예측 가능함
테스트 용이성각 데코레이터가 독립적으로 구현되므로 단위 테스트와 조합 테스트 모두 수행 가능
성능 및 구조적 효율성메모리 효율성복잡한 상속 트리 대신 객체 합성을 사용하여 메모리 사용을 절감하고 더 가벼운 구조 제공
독립적 배포/교체 가능데코레이터 간 결합도가 낮아 특정 기능만 분리하거나 교체하기 쉬움

단점과 문제점 그리고 해결방안

단점

카테고리단점 항목설명해결 방안
구조적 복잡성객체 계층 복잡도여러 데코레이터가 중첩되면 객체 구조가 복잡해지고 추적이 어려움빌더/팩토리 패턴 활용으로 객체 생성 책임 분리, 구조 시각화 도구 도입
디버깅/식별성원본 객체 식별 어려움데코레이터가 래핑된 객체는 타입 식별이 어려움instanceof, Visitor 패턴, 명시적 메타데이터 포함
호출 흐름 추적 어려움중첩 호출로 인해 디버깅 시 흐름 분석이 어려움상세 로깅, 체인 추적 도구 활용
유지보수 비용클래스 수 증가기능 단위로 데코레이터가 분리되며 클래스 수 증가기능 분류 기준 정의, 공통 데코레이터 통합
성능 이슈성능 오버헤드각 데코레이터마다 메서드 호출이 누적되어 성능 저하 발생프록시 패턴 연계, 캐싱/지연 평가 적용
설계 민감도순서 의존성데코레이터 적용 순서에 따라 결과가 달라짐적용 순서 규칙 정의 및 문서화, 체인 명시적 구성

문제점

카테고리문제 항목원인영향탐지/진단 방법예방 및 해결 방안
성능/안정성무의미한 중첩기능적 필요 없는 데코레이터 조합호출 오버헤드, 성능 저하프로파일링 도구, 체인 분석 도구불필요 조합 제거, 표준 조합 가이드 정의
스택 오버플로우과도한 중첩으로 재귀 호출 스택 초과애플리케이션 크래시스택 깊이 모니터링, 재귀 트레이스 분석반복적 처리 방식 변경, 트램폴린 기법, 최대 깊이 제한
메모리 누수순환 참조로 인해 GC 되지 않는 객체 발생메모리 사용량 증가, 성능 저하메모리 프로파일러, 참조 체인 분석WeakReference 활용, 명시적 해제 메서드 도입
설정/구성 충돌설정 충돌여러 데코레이터가 동일 속성에 대해 상이한 설정을 적용예외, 예측 불가한 동작, 버그테스트 (Unit, Integration), 설정 충돌 검사설정 우선순위 규칙 정의, 설정 병합 전략 구현, 네임스페이스 분리
설계 유연성인터페이스 종속성모든 데코레이터가 동일 인터페이스를 구현해야 하므로 구조적 제한 발생추상화 유연성 감소설계 리뷰, 인터페이스 정적 분석인터페이스 설계 명확화, 어댑터 패턴 병행 적용

도전 과제

카테고리주제설명주요 원인대표 해결책
성능 최적화과도한 호출 체인 오버헤드데코레이터 중첩으로 인해 메서드 호출 비용 증가다중 래핑, 반복 호출, 체인 길이 증가- 호출 경로 캐싱
- 지연 로딩/프록시 적용
- 성능 중요 영역에서는 최소화
설계 복잡성중첩 구조 추적의 어려움데코레이터 체인이 깊어질수록 구조와 동작 흐름 파악이 어려워짐재귀적 호출, 구조적 분리 부족- 구조 시각화 도구 활용
- 역할 분리 명확화
- 명명 규칙 적용
설계 복잡성데코레이터 순서 의존성데코레이터 적용 순서에 따라 결과가 달라지는 문제의존 순서 불명확, 책임 충돌- 순서 규칙 정의 및 문서화
- 순서 독립형 설계
- 테스트 케이스로 명시적 검증
유지보수데코레이터 책임 중복유사한 기능이 여러 데코레이터에 중복 정의되어 혼란 발생SRP 위반, 모듈 경계 모호화- 공통 데코레이터 추상화
- 역할 단일화
- 데코레이터 분류 체계 수립
디버깅/테스트디버깅/테스트 어려움중첩된 데코레이터 구조로 인해 테스트 및 디버깅이 어려움내부 동작 추적 어려움, 예외 처리 불명확- 로깅 데코레이터 도입
- 독립적 단위 테스트
- 호출 체인 시각화 및 추적
문서화/이해도구조 가시성 부족수십~수백 개 데코레이터가 조합된 경우 시스템 전반의 구조 파악이 어려움자동화된 문서 부재, 도구 부족- 자동 문서화 도구 사용
- 계층 구조로 분류
- 시각화 및 예시 코드 병행
대규모 시스템데코레이터 스케일 관리수많은 데코레이터 조합 시 유지보수성과 일관성 확보 필요무분별한 추가/중복, 카테고리화 미비- 계층적 구조 설계
- 공통 추상 계층 도입
- 공통 인터페이스 및 정책 적용
분산 시스템서비스 간 데코레이터 적용 문제마이크로서비스 환경에서 요청이 서비스 경계를 넘으며 데코레이터 적용 필요네트워크 지연, 트레이싱 부족, 분산 상태 관리 어려움- 서비스 메시 (Ambassador, Sidecar) 활용
- OpenTelemetry
- 회로 차단기 패턴 적용

분류에 따른 종류 및 유형

분류 기준세부 유형설명적용 예시
적용 시점정적 데코레이터컴파일 타임에 구조가 고정되는 방식Java Annotation, 템플릿 기반 구현
동적 데코레이터런타임에 객체에 동적으로 기능을 추가Proxy 기반 데코레이터, 리플렉션 활용
기능 추가 시점전처리형핵심 기능 실행 전에 동작 수행로깅, 인증, 입력 유효성 검사
후처리형핵심 기능 실행 후에 동작 수행캐싱, 결과 압축, 응답 암호화
래핑형핵심 기능 전체를 감싸고 새로운 흐름으로 구성트랜잭션 관리, 예외 래핑
기능 범위행위 데코레이터객체의 행동 (메서드 등) 을 확장 또는 변경로깅, 리트라이, 에러 처리
상태 데코레이터객체의 속성/상태 값을 확장캐시 카운터, 플래그 추가
상태 보유 여부상태 없는 데코레이터내부 상태를 저장하지 않음단순 로그 기록, 조건 분기 기능
상태 있는 데코레이터상태 저장을 통해 실행 결과에 영향을 줌요청 횟수 카운팅, 동적 구성 관리
구현 방식상속 기반기능 확장을 위해 클래스 상속 구조 사용전통 GoF 데코레이터 패턴
컴포지션 기반객체 합성을 통해 기능을 위임하고 래핑현대적인 데코레이터 구현 (e.g. Java I/O)
함수형함수를 데코레이터로 활용Python @decorator 함수
적용 범위메서드 레벨특정 메서드에만 데코레이터를 적용Python @decorator, Java Method Annotation
클래스 레벨클래스 전체에 영향을 주는 방식Spring AOP, Java Proxy 클래스

실무 사용 예시

도메인적용 목적기술 스택 / 기본 구성요소데코레이터 활용 방식 예시
웹 프레임워크HTTP 요청/응답 파이프라인 구성Express.js, FastAPI Middleware, Spring Filter인증/인가, 로깅, 예외 처리, 성능 측정 등을 미들웨어 형태로 계층화
보안 시스템인증, 인가, 권한 검증Spring Security, OAuth, JWTAuthenticationDecorator, AuthorizationDecorator 등으로 보안 로직 조합
로깅/모니터링동적 로깅, 필터링, 다중 출력SLF4J, Logback, Log4jTimeStampedLogger, LevelFilterLogger, FileLoggerDecorator 등으로 계층화
데이터 처리포맷 변환, 필터, 정제, 캐싱 적용Pandas, JSON Parser, Stream ProcessorSanitizerDecorator, DataFormatDecorator, CachingDecorator 등으로 데이터 처리 강화
UI 개발기능 및 스타일 확장React HOC, Vue 플러그인withTooltip(), withBorder(), withStyle() 등 고차 컴포넌트 또는 함수형 데코레이터 사용
스트림 I/O성능 개선, 인코딩, 압축, 암호화Java InputStream, Python file-like objectBufferedInputStream, GZIPInputStream, EncryptingDecorator 등으로 기능 중첩 구현
게임 개발캐릭터 능력 확장, 무기 강화Unity, Unreal, Custom Class-Based SystemsDamageBoostDecorator, MagicEffectDecorator, SpeedEnhancerDecorator 등으로 동적 능력 추가
GUI 컴포넌트UI 위젯 확장Java Swing, Windows API, Flutter 등ScrollBarDecorator, BorderDecorator, ShadowDecorator 등으로 레이아웃 구성 요소 확장
메시징 시스템알림 채널 다중화, 메시지 필터링 적용Apache Kafka, RabbitMQ, Custom Notifier PatternSlackDecorator, SMSDecorator, EmailLoggerDecorator 등으로 다양한 채널과 기능 조합
데이터베이스연결 풀 관리, 트랜잭션 확장JDBC, Hibernate, SQLAlchemy, Connection PoolingPooledConnectionDecorator, TransactionalDecorator, LoggingConnectionDecorator 등 적용 가능

활용 사례

사례 1: 스트리밍 서비스의 비디오 처리 시스템

대규모 비디오 스트리밍 플랫폼에서 사용자별 맞춤 비디오 처리가 필요한 상황

시스템 구성:

1
2
3
4
5
VideoProcessor (기본 컴포넌트)
├── SubtitleDecorator (자막 추가)
├── QualityEnhancer (화질 향상)
├── CompressionDecorator (압축 처리)
└── AnalyticsDecorator (시청 분석)

시스템 구성 다이어그램:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Client Request] 
[AnalyticsDecorator] ← 시청 데이터 수집
[CompressionDecorator] ← 대역폭에 따른 압축
[QualityEnhancer] ← AI 기반 화질 향상
[SubtitleDecorator] ← 언어별 자막 추가
[VideoProcessor] ← 기본 비디오 처리
[Processed Video Stream]

Workflow:

  1. 요청 수신: 클라이언트가 비디오 스트리밍 요청
  2. 분석 시작: AnalyticsDecorator 가 시청 패턴 분석 시작
  3. 압축 적용: 사용자 네트워크 환경에 따른 압축 레벨 결정
  4. 화질 향상: AI 알고리즘을 통한 실시간 화질 개선
  5. 자막 처리: 사용자 언어 설정에 따른 자막 오버레이
  6. 기본 처리: 핵심 비디오 디코딩 및 스트리밍 준비
  7. 결과 전송: 최종 처리된 비디오 스트림 전송

데코레이터별 역할:

사례 2: 커피 주문 시스템

시스템 구성:

Workflow:

  1. SimpleCoffee 객체 생성
  2. WhippedCreamDecorator 로 감쌈
  3. SyrupDecorator 로 추가 감쌈
  4. cost()/description() 호출 시 각 Decorator 가 기능 추가
1
[Client] → [SyrupDecorator] → [WhippedCreamDecorator] → [SimpleCoffee]

사례 3: 웹 요청 필터 체인 구성 (Express.js)

사용 목적: 인증 → 로깅 → 데이터 파싱 순으로 요청 전처리

시스템 구성:

1
2
3
4
Client -> MiddlewareChain -> RouteHandler -> Response
         └─ AuthMiddleware
         └─ LoggerMiddleware
         └─ BodyParser
1
2
3
app.use(authMiddleware)
app.use(loggerMiddleware)
app.use(bodyParser.json())

사례 4: Spring Framework 의 AOP (Aspect-Oriented Programming) 구현

Spring Framework 에서는 데코레이터 패턴을 활용하여 AOP 를 구현한다.
이는 횡단 관심사 (Cross-cutting Concerns) 를 분리하여 코드의 모듈성을 높이는 사례.

시스템 구성:

graph TB
    subgraph "Spring AOP Architecture"
        Client[Client Code]
        Proxy[Spring Proxy]
        Advisor[Advisor]
        Pointcut[Pointcut]
        Advice[Advice]
        Target[Target Object]
        
        Client --> Proxy
        Proxy --> Advisor
        Advisor --> Pointcut
        Advisor --> Advice
        Proxy --> Target
    end
    
    subgraph "Decorator Chain"
        LoggingDecorator[Logging Decorator]
        SecurityDecorator[Security Decorator]
        TransactionDecorator[Transaction Decorator]
        BusinessLogic[Business Logic]
        
        LoggingDecorator --> SecurityDecorator
        SecurityDecorator --> TransactionDecorator
        TransactionDecorator --> BusinessLogic
    end

Workflow:

  1. 클라이언트 요청: 클라이언트가 비즈니스 메서드 호출
  2. 프록시 인터셉션: Spring 프록시가 호출을 가로챔
  3. 어드바이스 체인 실행: 로깅 → 보안 → 트랜잭션 순서로 데코레이터 실행
  4. 비즈니스 로직 수행: 최종적으로 실제 비즈니스 로직 실행
  5. 후처리: 역순으로 데코레이터들의 후처리 작업 수행

데코레이터 패턴의 역할:

데코레이터 유무에 따른 차이점

구현 예시

Python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Component 인터페이스
class Coffee:
    def cost(self):
        pass

# ConcreteComponent
class SimpleCoffee(Coffee):
    def cost(self):
        return 5

# Decorator
class CoffeeDecorator(Coffee):
    def __init__(self, coffee):
        self._coffee = coffee

    def cost(self):
        return self._coffee.cost()

# ConcreteDecorator
class MilkDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 2

class SugarDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 1

# 사용 예시
coffee = SimpleCoffee()
print(f"Simple coffee: {coffee.cost()}")  # 5

coffee = MilkDecorator(coffee)
print(f"Coffee with milk: {coffee.cost()}")  # 7

coffee = SugarDecorator(coffee)
print(f"Coffee with milk and sugar: {coffee.cost()}")  # 8

# 주석: 위 코드는 커피에 우유, 설탕을 동적으로 추가하는 예시입니다.
# 데코레이터 패턴을 사용해 객체에 기능을 유연하게 추가할 수 있습니다.

Python: Spring AOP 방식의 데코레이터 패턴

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
from abc import ABC, abstractmethod
import time
import functools
from typing import Any, Callable

# 1. 컴포넌트 인터페이스 정의
class BusinessService(ABC):
    """비즈니스 서비스를 위한 공통 인터페이스"""
    
    @abstractmethod
    def process_data(self, data: str) -> str:
        """데이터 처리를 위한 핵심 메서드"""
        pass

# 2. 구체적 컴포넌트 구현
class UserService(BusinessService):
    """사용자 관련 비즈니스 로직을 처리하는 구체적 컴포넌트"""
    
    def process_data(self, data: str) -> str:
        """실제 사용자 데이터 처리 로직"""
        # 실제 비즈니스 로직 수행
        processed_data = f"User data processed: {data}"
        return processed_data

# 3. 기본 데코레이터 클래스
class ServiceDecorator(BusinessService):
    """모든 서비스 데코레이터의 기본 클래스"""
    
    def __init__(self, service: BusinessService):
        """래핑할 서비스 객체를 받아 초기화"""
        self._service = service
    
    def process_data(self, data: str) -> str:
        """기본적으로는 래핑된 서비스에게 작업을 위임"""
        return self._service.process_data(data)

# 4. 구체적 데코레이터들

class LoggingDecorator(ServiceDecorator):
    """로깅 기능을 추가하는 데코레이터"""
    
    def process_data(self, data: str) -> str:
        """메서드 실행 전후로 로깅 기능 추가"""
        print(f"[LOG] Starting to process data: {data}")
        
        # 래핑된 서비스의 메서드 호출
        result = self._service.process_data(data)
        
        print(f"[LOG] Finished processing. Result: {result}")
        return result

class SecurityDecorator(ServiceDecorator):
    """보안 검증 기능을 추가하는 데코레이터"""
    
    def __init__(self, service: BusinessService, required_role: str = "USER"):
        """보안 데코레이터 초기화 - 필요한 권한 설정"""
        super().__init__(service)
        self.required_role = required_role
        self.current_user_role = "ADMIN"  # 실제로는 인증 시스템에서 가져옴
    
    def process_data(self, data: str) -> str:
        """보안 검증 후 데이터 처리"""
        print(f"[SECURITY] Checking access for role: {self.required_role}")
        
        # 권한 검증 로직
        if not self._has_permission():
            raise PermissionError(f"Access denied. Required role: {self.required_role}")
        
        print(f"[SECURITY] Access granted for user with role: {self.current_user_role}")
        
        # 검증 통과 후 다음 단계로 진행
        return self._service.process_data(data)
    
    def _has_permission(self) -> bool:
        """권한 검증 로직 (실제로는 더 복잡한 로직)"""
        role_hierarchy = {"ADMIN": 3, "USER": 2, "GUEST": 1}
        return role_hierarchy.get(self.current_user_role, 0) >= role_hierarchy.get(self.required_role, 0)

class TransactionDecorator(ServiceDecorator):
    """트랜잭션 관리 기능을 추가하는 데코레이터"""
    
    def process_data(self, data: str) -> str:
        """트랜잭션 내에서 데이터 처리"""
        transaction_id = self._begin_transaction()
        
        try:
            print(f"[TRANSACTION] Started transaction: {transaction_id}")
            
            # 실제 비즈니스 로직 실행
            result = self._service.process_data(data)
            
            self._commit_transaction(transaction_id)
            print(f"[TRANSACTION] Committed transaction: {transaction_id}")
            
            return result
            
        except Exception as e:
            self._rollback_transaction(transaction_id)
            print(f"[TRANSACTION] Rolled back transaction: {transaction_id}")
            raise e
    
    def _begin_transaction(self) -> str:
        """트랜잭션 시작"""
        transaction_id = f"tx_{int(time.time() * 1000)}"
        # 실제로는 데이터베이스 트랜잭션 시작
        return transaction_id
    
    def _commit_transaction(self, transaction_id: str):
        """트랜잭션 커밋"""
        # 실제로는 데이터베이스 커밋 수행
        pass
    
    def _rollback_transaction(self, transaction_id: str):
        """트랜잭션 롤백"""
        # 실제로는 데이터베이스 롤백 수행
        pass

class PerformanceDecorator(ServiceDecorator):
    """성능 측정 기능을 추가하는 데코레이터"""
    
    def process_data(self, data: str) -> str:
        """실행 시간을 측정하여 성능 모니터링"""
        start_time = time.time()
        print(f"[PERFORMANCE] Starting execution measurement")
        
        # 래핑된 서비스 실행
        result = self._service.process_data(data)
        
        end_time = time.time()
        execution_time = (end_time - start_time) * 1000  # 밀리초 단위
        
        print(f"[PERFORMANCE] Execution completed in {execution_time:f}ms")
        
        # 성능 임계값 검사
        if execution_time > 1000:  # 1초 초과시 경고
            print(f"[PERFORMANCE] WARNING: Slow execution detected!")
        
        return result

# 5. 함수형 데코레이터 구현 (Python의 함수 데코레이터 활용)
def timing_decorator(func: Callable) -> Callable:
    """함수 실행 시간을 측정하는 함수형 데코레이터"""
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        
        print(f"[TIMING] {func.__name__} executed in {(end_time - start_time)*1000:f}ms")
        return result
    
    return wrapper

def logging_decorator(func: Callable) -> Callable:
    """함수 호출을 로깅하는 함수형 데코레이터"""
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[FUNC_LOG] Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"[FUNC_LOG] {func.__name__} returned: {result}")
        return result
    
    return wrapper

# 6. 데코레이터 팩토리 패턴
class ServiceDecoratorFactory:
    """데코레이터 조합을 쉽게 생성하기 위한 팩토리 클래스"""
    
    @staticmethod
    def create_full_stack_service(base_service: BusinessService) -> BusinessService:
        """모든 기능이 포함된 서비스 스택을 생성"""
        # 데코레이터를 순서대로 적용 (안쪽부터 바깥쪽으로)
        decorated_service = base_service
        decorated_service = TransactionDecorator(decorated_service)
        decorated_service = SecurityDecorator(decorated_service, "USER")
        decorated_service = LoggingDecorator(decorated_service)
        decorated_service = PerformanceDecorator(decorated_service)
        
        return decorated_service
    
    @staticmethod
    def create_basic_service(base_service: BusinessService) -> BusinessService:
        """기본 로깅과 성능 측정만 포함된 서비스"""
        decorated_service = base_service
        decorated_service = LoggingDecorator(decorated_service)
        decorated_service = PerformanceDecorator(decorated_service)
        
        return decorated_service

# 7. 사용 예시
def main():
    """데코레이터 패턴 사용 예시"""
    
    print("=== Decorator Pattern 구현 예시 ===\n")
    
    # 기본 서비스 생성
    user_service = UserService()
    
    print("1. 기본 서비스 실행:")
    print("-" * 40)
    result1 = user_service.process_data("John Doe")
    print(f"결과: {result1}\n")
    
    print("2. 로깅 데코레이터 적용:")
    print("-" * 40)
    logged_service = LoggingDecorator(user_service)
    result2 = logged_service.process_data("Jane Smith")
    print(f"결과: {result2}\n")
    
    print("3. 여러 데코레이터 조합 (수동):")
    print("-" * 40)
    # 중첩하여 여러 데코레이터 적용
    complex_service = PerformanceDecorator(
        LoggingDecorator(
            SecurityDecorator(
                TransactionDecorator(user_service), 
                "USER"
            )
        )
    )
    result3 = complex_service.process_data("Bob Johnson")
    print(f"결과: {result3}\n")
    
    print("4. 팩토리를 사용한 서비스 생성:")
    print("-" * 40)
    full_stack_service = ServiceDecoratorFactory.create_full_stack_service(user_service)
    result4 = full_stack_service.process_data("Alice Brown")
    print(f"결과: {result4}\n")
    
    print("5. 함수형 데코레이터 예시:")
    print("-" * 40)
    
    @timing_decorator
    @logging_decorator
    def simple_function(name: str) -> str:
        """간단한 함수에 데코레이터 적용"""
        time.sleep(0.1)  # 시뮬레이션을 위한 지연
        return f"Hello, {name}!"
    
    result5 = simple_function("Charlie")
    print(f"결과: {result5}")

if __name__ == "__main__":
    main()

JavaScript: 웹 미들웨어 체인

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Base Handler
function baseHandler(req, res) {
  res.send("요청 처리 완료");
}

// Decorator 생성
function loggerDecorator(handler) {
  return (req, res) => {
    console.log(`[LOG] ${req.url}`);
    handler(req, res);
  };
}

function authDecorator(handler) {
  return (req, res) => {
    if (req.headers.authorization !== "secret") {
      res.status(403).send("Forbidden");
      return;
    }
    handler(req, res);
  };
}

// 데코레이터 체인 적용
const finalHandler = loggerDecorator(authDecorator(baseHandler));

Python: 커피 주문 시스템템

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from abc import ABC, abstractmethod
from typing import List

# Component interface
class Coffee(ABC):
    """Base Coffee interface"""
    @abstractmethod
    def get_cost(self) -> float:
        pass

    @abstractmethod
    def get_ingredients(self) -> List[str]:
        pass

    @abstractmethod
    def get_description(self) -> str:
        pass

# Concrete component
class SimpleCoffee(Coffee):
    """Basic coffee implementation"""
    def get_cost(self) -> float:
        return 2.0

    def get_ingredients(self) -> List[str]:
        return ["Coffee"]

    def get_description(self) -> str:
        return "Simple coffee"

# Base decorator
class CoffeeDecorator(Coffee):
    """Base decorator class"""
    def __init__(self, coffee: Coffee):
        self._coffee = coffee

    def get_cost(self) -> float:
        return self._coffee.get_cost()

    def get_ingredients(self) -> List[str]:
        return self._coffee.get_ingredients()

    def get_description(self) -> str:
        return self._coffee.get_description()

# Concrete decorators
class MilkDecorator(CoffeeDecorator):
    """Adds milk to the coffee"""
    def get_cost(self) -> float:
        return super().get_cost() + 0.5

    def get_ingredients(self) -> List[str]:
        return super().get_ingredients() + ["Milk"]

    def get_description(self) -> str:
        return f"{super().get_description()}, with steamed milk"

class WhipDecorator(CoffeeDecorator):
    """Adds whipped cream to the coffee"""
    def get_cost(self) -> float:
        return super().get_cost() + 0.7

    def get_ingredients(self) -> List[str]:
        return super().get_ingredients() + ["Whipped Cream"]

    def get_description(self) -> str:
        return f"{super().get_description()}, topped with whipped cream"

class CaramelDecorator(CoffeeDecorator):
    """Adds caramel to the coffee"""
    def get_cost(self) -> float:
        return super().get_cost() + 0.6

    def get_ingredients(self) -> List[str]:
        return super().get_ingredients() + ["Caramel"]

    def get_description(self) -> str:
        return f"{super().get_description()}, drizzled with caramel"

class ExtraShotDecorator(CoffeeDecorator):
    """Adds an extra shot of espresso"""
    def get_cost(self) -> float:
        return super().get_cost() + 1.0

    def get_ingredients(self) -> List[str]:
        return super().get_ingredients() + ["Extra Espresso Shot"]

    def get_description(self) -> str:
        return f"{super().get_description()}, with an extra shot"

# Order management
class CoffeeOrder:
    """Manages coffee orders and provides order summary"""
    def __init__(self):
        self.coffee = None

    def create_order(self) -> None:
        """Creates a new coffee order starting with simple coffee"""
        self.coffee = SimpleCoffee()

    def add_milk(self) -> None:
        self.coffee = MilkDecorator(self.coffee)

    def add_whip(self) -> None:
        self.coffee = WhipDecorator(self.coffee)

    def add_caramel(self) -> None:
        self.coffee = CaramelDecorator(self.coffee)

    def add_extra_shot(self) -> None:
        self.coffee = ExtraShotDecorator(self.coffee)

    def get_order_summary(self) -> str:
        """Generates a summary of the current order"""
        return f"""
        Order Summary:
        Description: {self.coffee.get_description()}
        Ingredients: {', '.join(self.coffee.get_ingredients())}
        Total Cost: ${self.coffee.get_cost():f}
        """

# Usage example
if __name__ == "__main__":
    # Create a new order
    order = CoffeeOrder()
    order.create_order()

    # Customize the coffee with various additions
    order.add_milk()
    order.add_extra_shot()
    order.add_whip()
    order.add_caramel()

    # Print the order summary
    print(order.get_order_summary())

Javascript: Text Processing

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Component interface
class TextProcessor {
    constructor() {
        if (this.constructor === TextProcessor) {
            throw new Error("Abstract class cannot be instantiated");
        }
    }

    process(text) {
        throw new Error("Method 'process' must be implemented");
    }
}

// Concrete component
class SimpleTextProcessor extends TextProcessor {
    process(text) {
        return text;
    }
}

// Base decorator
class TextProcessorDecorator extends TextProcessor {
    constructor(processor) {
        super();
        this._processor = processor;
    }

    process(text) {
        return this._processor.process(text);
    }
}

// Concrete decorators
class CapitalizeDecorator extends TextProcessorDecorator {
    process(text) {
        const processedText = this._processor.process(text);
        return processedText.toUpperCase();
    }
}

class TrimDecorator extends TextProcessorDecorator {
    process(text) {
        const processedText = this._processor.process(text);
        return processedText.trim();
    }
}

class HTMLEscapeDecorator extends TextProcessorDecorator {
    process(text) {
        const processedText = this._processor.process(text);
        return processedText
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }
}

class MarkdownToHTMLDecorator extends TextProcessorDecorator {
    process(text) {
        const processedText = this._processor.process(text);
        return processedText
            .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
            .replace(/\*(.*?)\*/g, '<em>$1</em>')
            .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2">$1</a>')
            .replace(/^# (.*$)/gm, '<h1>$1</h1>')
            .replace(/^## (.*$)/gm, '<h2>$1</h2>');
    }
}

class ValidationDecorator extends TextProcessorDecorator {
    constructor(processor, maxLength = 1000) {
        super(processor);
        this.maxLength = maxLength;
    }

    process(text) {
        if (!text) {
            throw new Error("Text cannot be empty");
        }
        if (text.length > this.maxLength) {
            throw new Error(`Text length exceeds maximum limit of ${this.maxLength} characters`);
        }
        return this._processor.process(text);
    }
}

// Text processing manager
class TextProcessingManager {
    constructor() {
        this.processor = new SimpleTextProcessor();
        this.history = [];
    }

    addCapitalization() {
        this.processor = new CapitalizeDecorator(this.processor);
        return this;
    }

    addTrimming() {
        this.processor = new TrimDecorator(this.processor);
        return this;
    }

    addHTMLEscaping() {
        this.processor = new HTMLEscapeDecorator(this.processor);
        return this;
    }

    addMarkdownProcessing() {
        this.processor = new MarkdownToHTMLDecorator(this.processor);
        return this;
    }

    addValidation(maxLength) {
        this.processor = new ValidationDecorator(this.processor, maxLength);
        return this;
    }

    process(text) {
        const result = this.processor.process(text);
        this.history.push({
            input: text,
            output: result,
            timestamp: new Date()
        });
        return result;
    }

    getProcessingHistory() {
        return this.history;
    }
}

// Usage example
const manager = new TextProcessingManager();

// Configure text processing chain
manager
    .addValidation(2000)
    .addTrimming()
    .addMarkdownProcessing()
    .addHTMLEscaping();

// Process some text
try {
    const input = `
    # Welcome to Text Processing
    
    This is a **bold** and *italic* text example.
    [Click here](https://example.com) to learn more.
    `;

    const result = manager.process(input);
    console.log("Processed text:");
    console.log(result);

    console.log("\nProcessing history:");
    console.log(manager.getProcessingHistory());
} catch (error) {
    console.error("Error processing text:", error.message);
}

실무에서 효과적으로 적용하기 위한 고려사항

카테고리고려사항설명권장사항
설계인터페이스 설계 안정성미래 확장을 고려한 추상화 수준 결정변경 가능성이 낮은 핵심 인터페이스로 설계
책임 분리 (SRP)각 데코레이터는 하나의 책임만 가지도록 구성단일 책임 원칙 (SRP) 준수
데코레이터 명명 규칙기능이 명확하게 드러나도록 클래스 명 지정예: AuthLoggerDecorator, EncryptedMessageDecorator
중복 기능 방지여러 데코레이터에서 동일한 기능 중복 구현 방지기능 구성 시 중복 여부 검토, 공통 베이스 추상화
데코레이터 적용 순서순서에 따라 실행 결과가 달라질 수 있음적용 순서 정의, 설계 문서화, 테스트 기반 검증
구현예외 처리 전략체인 중간에서 발생하는 예외의 전파 및 복구 관리 필요각 데코레이터별 예외 처리 책임 명확화
의존성 최소화데코레이터 간 불필요한 결합을 방지하여 유연성 확보느슨한 결합, 인터페이스 기반 구성
메모리 생명주기 관리중첩 구조가 많아질 경우 GC 대상이 아닌 객체가 남을 수 있음가비지 컬렉션 최적화, 참조 해제 고려
성능 최적화체인 깊이 제한중첩이 깊어질수록 호출 오버헤드 발생 가능최대 4~5 단계 이내로 구성, 캐싱/프록시/지연 평가 등 활용
불필요한 데코레이터 최소화기능이 겹치거나 필요하지 않은 데코레이터를 제거하여 성능 유지런타임 조건 기반으로 적용 여부 판단
테스트 전략단위 테스트 가능성개별 데코레이터 기능 테스트 용이성 확보Mock, Stub 활용한 독립 테스트 구성
조합 테스트여러 데코레이터를 조합한 전체 동작 검증 필요통합 테스트 케이스 설계
유지보수/문서화조합 구조 문서화어떤 데코레이터가 어떤 순서로 어떤 기능을 제공하는지 명확히 설명 필요클래스 다이어그램, 시퀀스 다이어그램, 예시 코드 포함
사용 가이드 작성팀원 간의 적용 방식 일관성 유지적용 순서 매트릭스, 제한 사항, 사용 방법 명세
배포/운영의존성 관리데코레이터 간 결합으로 인한 배포/빌드 복잡성 발생 가능구성 설정 기반 적용, DI 컨테이너를 통한 주입 구조로 분리

테스트 전략

단위 테스트
1
2
3
4
5
6
7
8
9
def test_individual_decorator():
    # 각 데코레이터를 독립적으로 테스트
    base_service = MockService()
    logging_service = LoggingDecorator(base_service)
    
    result = logging_service.process("test")
    
    assert result is not None
    assert base_service.was_called()
통합 테스트
1
2
3
4
5
6
7
8
9
def test_decorator_chain():
    # 데코레이터 체인 전체 테스트
    service = create_decorated_service()
    
    result = service.process("integration_test")
    
    assert_logging_occurred()
    assert_security_checked()
    assert_transaction_managed()
성능 테스트
1
2
3
4
5
6
7
def test_performance_overhead():
    # 데코레이터로 인한 성능 영향 측정
    base_time = measure_execution_time(base_service)
    decorated_time = measure_execution_time(decorated_service)
    
    overhead = (decorated_time - base_time) / base_time
    assert overhead < 0.1  # 10% 이하 오버헤드

리팩토링 전략

상속에서 데코레이터로 리팩토링

Before (상속 기반):

1
2
3
4
5
6
class LoggingUserService(UserService):
    def process(self, data):
        log("Processing started")
        result = super().process(data)
        log("Processing completed")
        return result

After (데코레이터 기반):

1
2
3
4
5
6
7
8
9
class LoggingDecorator(ServiceDecorator):
    def process(self, data):
        log("Processing started")
        result = self._service.process(data)
        log("Processing completed")
        return result

# 사용
service = LoggingDecorator(UserService())
점진적 마이그레이션
  1. 1 단계: 기존 클래스를 래핑하는 데코레이터 생성
  2. 2 단계: 클라이언트 코드를 데코레이터 사용으로 변경
  3. 3 단계: 기존 상속 구조 제거

활용 시 흔한 실수

순서 의존성 간과

문제: 데코레이터 적용 순서에 따라 결과가 달라짐

1
2
3
4
5
6
7
# 잘못된 순서
service = SecurityDecorator(LoggingDecorator(base_service))
# 보안 검사 전에 로깅이 발생

# 올바른 순서  
service = LoggingDecorator(SecurityDecorator(base_service))
# 보안 검사 후에만 로깅 발생
과도한 래핑

문제: 불필요하게 많은 데코레이터 적용
해결책: 필요한 기능만 선별적으로 적용

타입 안전성 무시

문제: 런타임에만 오류 발견
해결책: 제네릭스나 타입 힌트 활용

메모리 누수

문제: 순환 참조로 인한 메모리 누수
해결책: 약한 참조 사용, 명시적 정리 메서드 제공

최적화하기 위한 고려사항 및 주의할 점

카테고리최적화 항목설명권장사항
설계/구조 최적화데코레이터 중첩 제한과도한 중첩은 호출 스택 증가 및 유지보수 복잡도 초래최대 중첩 깊이 제한, 경고 시스템 구축
데코레이터 조합 최적화동일 조합 반복 시 성능 저하 및 코드 중복 발생 가능자주 사용되는 조합을 위한 전용 데코레이터 클래스 정의
필요 최소한의 데코레이터 사용모든 기능을 데코레이터로 분리 시 복잡도/오버헤드 증가기능 간소화 및 단일 책임 원칙 준수
성능 개선 전략호출 오버헤드 최적화메서드 호출 체인 깊어질수록 실행 성능 저하인라인 최적화, 프록시 캐싱, 불필요한 체인 제거
Lazy Initialization필요 시점에만 데코레이터 활성화하여 리소스 절약Lazy 로딩 기법 적용 (@lazy, Supplier 패턴 등)
비동기 데코레이터 실행독립적인 데코레이터는 병렬로 실행 가능async, CompletableFuture, Promise 등 활용
캐싱 및 메모이제이션동일 입력에 대한 중복 계산 제거로 응답 시간 단축데코레이터 캐싱 전략 설계 (예: LRU, TTL 기반 캐시)
메모리 관리객체 풀링동일한 데코레이터 인스턴스를 재사용하여 메모리 소모 최소화객체 풀 관리 또는 싱글턴 적용 (Thread-safe 고려 필요)
GC 유도 / 수명 관리사용 후 데코레이터의 불필요 참조로 인해 메모리 누수 발생 가능약한 참조 (WeakReference), 수명 관리 및 명시적 제거
테스트 및 품질테스트 용이성 확보데코레이터 체인 내 개별 요소의 검증 필요Mock 객체 기반 단위 테스트, 체인 조합 자동 테스트
중복 기능 제거유사 기능의 데코레이터가 중복 정의될 수 있음공통 기능을 상위 데코레이터로 통합
개발 생산성반복 코드 자동화유사한 데코레이터 코드가 반복될 경우 개발 효율 저하어노테이션 프로세서, 코드 생성기 (CodeGen), 메타프로그래밍 활용
모니터링/분석성능 추적 및 프로파일링데코레이터 체인에서 병목 구간 식별 필요APM 도구 (NewRelic, Datadog 등), OpenTelemetry 연동
실행 시간 측정데코레이터별 처리 시간 또는 리소스 사용량 확인 필요데코레이터 단위 측정 로직 삽입 (로깅, 타이머 등)
디버깅 지원중첩 구조에서 오류 추적이 어려울 수 있음체인 구조 시각화, 로깅 레벨 세분화, 디버그 플래그 제공

주제와 관련하여 주목할 내용

카테고리주제항목설명
설계 패턴 핵심데코레이터 패턴유연성런타임에 기능 추가/제거 가능
확장성새로운 기능을 기존 객체 변경 없이 확장 가능
일관성기본 객체와 데코레이터가 동일한 인터페이스를 공유
스트림/UI 활용Java I/O, UI 컴포넌트 구조 등에서 대표적으로 활용됨
객체지향 원칙/구조SOLID 원칙OCP / SRP개방 - 폐쇄 원칙 및 단일 책임 원칙의 대표 적용 사례
상속 vs 합성합성/위임 구조 사용상속 대신 객체 포함과 위임을 통해 기능을 확장
책임 분리기능 계층화각 데코레이터는 하나의 책임만 가지도록 설계
관련 디자인 패턴 비교구조적 패턴 비교프록시 / 어댑터 / 컴포지트 / 전략 패턴구조는 유사하지만 목적과 사용 방식이 다른 유사 패턴들과의 비교 분석
AOP 와의 차이데코레이터 vs AOPAOP 는 런타임 자동 처리 / 데코레이터는 명시적 정적 조합
구현 전략컴포지션객체 포함 기반 구조위임을 통해 기능을 조합하고 확장
위임내부 객체에게 기능 위임데코레이터는 실제 기능을 포함된 객체에게 위임함
재귀적 컴포지션중첩된 구조다단계 데코레이터를 계층적으로 조합
투명성클라이언트 관점에서 원본/데코레이터 구분 없음API 일관성 유지
동적 프록시런타임 데코레이터 생성Java DynamicProxy, Python decorators, CGLib 등 활용
함수형 프로그래밍고차 함수함수형 데코레이터함수를 인자로 받아 확장된 함수를 반환 (JS, Python 등)
커링함수 분리와 데코레이터 결합함수를 부분 적용하여 데코레이터로 응용
메타프로그래밍/리플렉션어노테이션 / 리플렉션 기반선언적 데코레이터 구성Spring AOP, NestJS 데코레이터 등에서 활용되는 기반 기술
IoC 통합DI 컨테이너 내 데코레이터 자동 등록Autofac, Scrutor 등에서의 데코레이터 스캔 및 구성
성능 및 최적화캐싱메모이제이션 적용계산 결과를 캐싱하여 성능 향상
호출 오버헤드체인 수 증가에 따른 성능 저하 가능성경량 구조 설계, 체인 최소화
성능 모니터링관찰성 도구 연계OpenTelemetry 등으로 성능 측정 및 추적
동시성 및 안정성스레드 안전성Thread-safe 데코레이터 구현멀티스레드 환경에서의 동시 접근 보호
클라우드/아키텍처 적용서버리스 / 메시 구조데코레이터 기반 미들웨어 처리 구조API Gateway, Serverless 미들웨어, Filter Chain 등 구조화
서비스 메시 / 사이드카 패턴횡단 관심사 분리 처리로깅, 인증, 트래픽 제어 등을 보조 컴포넌트로 위임
테스트 전략단위 테스트 / 모킹데코레이터 객체의 개별/조합 테스트 방법Mock Decorator 설계 및 의존성 격리를 통한 테스트
테스트 자동화데코레이터 체인의 테스트 시나리오 구성시나리오 기반 테스트 및 성능 테스트 적용 가능
프레임워크 적용 예시Java I/O StreamInputStream 데코레이터 구조Java 에서의 데코레이터 대표적 사례 (BufferedInputStream 등)
Express / NestJS요청 흐름을 장식하는 데코레이터 구조Request/Response 전후 처리 미들웨어 구조
Spring AOPAspect 기반 선언적 데코레이터 적용횡단 관심사 로직을 어노테이션으로 분리 적용

반드시 학습해야할 내용들

카테고리주제세부 항목/설명
디자인 패턴 비교구조적 패턴 차이 이해Decorator vs Proxy / Adapter / Composite / Bridge 비교 분석
책임 연쇄 패턴과 비교Chain of Responsibility 패턴과의 흐름/의도/연결 방식 비교
객체지향 설계 원칙SOLID 원칙 적용SRP (단일 책임 원칙), OCP (개방 - 폐쇄 원칙) 의 실제 적용 배경 및 코드 구조 영향
상속 vs 합성컴포지션 기반 위임 구조가 상속 대비 가지는 유연성과 적용 시점
구현 기법 및 구조합성 / 위임데코레이터의 내부 위임 구조, 인터페이스 기반 위임 설계 이해
투명성 / 메서드 체이닝 / 지연 평가데코레이터의 구조적 특성과 최적화 기법 (Chaining, Lazy Evaluation 등)
재귀적 컴포지션데코레이터 중첩 적용 구조의 설계 방법 및 장단점 분석
동적 프록시런타임에 프록시 생성하여 데코레이터 적용하는 기술 (예: Java Dynamic Proxy)
프레임워크 활용 및 연계AOP 와의 관계AOP (관점 지향 프로그래밍) 와 Decorator 의 유사점/차이점 및 결합 구조
미들웨어/필터 체인웹 프레임워크에서 데코레이터 유사 구조인 필터 체인과의 관계
플러그인 시스템확장 가능한 시스템 설계에서 데코레이터 기반 모듈 주입 방식 이해
NestJS / Spring AOP 활용메타프로그래밍 기반 데코레이터 구현 및 횡단 관심사 적용 사례
프레임워크 내 활용Java I/O Stream, Express 미들웨어, Python 데코레이터 등 실무 예제 분석
함수형 프로그래밍 연계고차 함수 (HOF)함수형 언어에서 데코레이터와 유사한 고차 함수 구조 이해 (Python, JS)
함수 vs 클래스 데코레이터Python 등에서 함수와 클래스 데코레이터의 차이점 및 적용법
성능 및 최적화 전략호출 오버헤드 / 메모리 최적화데코레이터 체인 구성 시 성능 비용 최소화 전략, 메모이제이션 활용
Flyweight 패턴 결합상태 공유를 통해 데코레이터의 메모리 오버헤드를 줄이는 방법
테스트 및 실무 전략단위 테스트 / Mock Decorator데코레이터 적용 객체의 테스트 전략, 의존성 분리 (Mock) 기법
데코레이터 조합 테스트 전략여러 데코레이터를 조합한 객체 테스트 및 책임 분리 검증 방법
소프트웨어 아키텍처마이크로서비스 / 서버리스서비스 간의 횡단 관심사 처리 (로깅, 인증 등) 에서 데코레이터 기반 미들웨어 적용
계층 간 데코레이터 활용클린 아키텍처에서 인터페이스/어댑터 계층의 공통 로직 삽입 방식으로서의 활용
코드 생성 및 관리 도구데코레이터 자동 생성 도구 또는 관리 프레임워크 활용 (예: Swagger, Lombok 등)

용어 정리

카테고리용어설명
설계 패턴 요소Decorator Pattern객체에 동적으로 기능을 추가할 수 있게 해주는 구조적 디자인 패턴
Component기본 객체와 데코레이터가 공통으로 구현하는 인터페이스 또는 추상 클래스
ConcreteComponent실제 기능을 구현한 기본 객체 (기능의 대상)
DecoratorComponent 를 확장하고 내부에 Component 를 참조하는 추상 클래스
ConcreteDecoratorDecorator 를 상속받아 실제 기능을 확장하는 클래스
객체지향 개념Composition (합성)객체를 포함하여 기능을 확장하는 방식 (상속보다 유연함)
Delegation (위임)한 객체가 작업을 다른 객체에게 위임하여 처리하는 방식
Recursive Composition데코레이터가 중첩 구조로 구성되어 계층적으로 기능을 조합하는 방식
설계 원칙OCP (Open-Closed Principle)확장에는 열려 있고, 변경에는 닫혀 있어야 한다는 소프트웨어 설계 원칙
SRP (Single Responsibility Principle)하나의 클래스는 단 하나의 책임만을 가져야 한다는 원칙
구현 기법Transparency (투명성)클라이언트가 원본 객체와 데코레이터를 구분하지 않도록 설계하는 특성
Method Chaining (연쇄 호출)메서드를 연속적으로 호출할 수 있도록 반환 객체를 this/self 로 설정
Lazy Evaluation (지연 평가)필요한 시점에만 계산 또는 로직 실행하는 기법
Method Interception (메소드 인터셉션)메서드 호출 시점에 로직을 가로채서 부가 기능을 실행하는 기술
프레임워크/패러다임AOP (Aspect-Oriented Programming)횡단 관심사를 모듈화하여 핵심 로직과 분리하는 프로그래밍 패러다임
Cross-cutting Concerns (횡단 관심사)여러 모듈에 걸쳐 나타나는 공통 기능 (로깅, 인증, 트랜잭션 등)
Sidecar Pattern보조 기능을 애플리케이션과 분리된 별도 컨테이너로 처리하는 패턴
Proxy Pattern접근을 제어하는 구조적 패턴으로, 구조적으로 Decorator 와 유사
성능 및 부작용Call Overhead (호출 오버헤드)메서드 호출이 중첩됨에 따라 발생하는 성능 비용
Memoization (메모이제이션)계산 결과를 캐시하여 중복 연산을 피하는 기법, Decorator 내 캐싱 적용 시 활용 가능

참고 및 출처