Files
2nd/10_Wiki/Topics/Architecture/State-Strategy Pattern (상태-전략 패턴).md
T

8.6 KiB

State/Strategy Pattern (상태/전략 패턴)

📌 Brief Summary

State/Strategy Pattern(상태/전략 패턴)은 객체의 생명 주기 동안 분류 부호(Type Code)가 변경되거나, 클래스가 이미 다른 이유로 하위 클래싱되어 상속을 사용할 수 없을 때 분류 부호를 대체하기 위해 사용하는 리팩토링 기법이자 객체지향 패턴이다 [1, 2]. 이 패턴은 객체의 상태 변화나 알고리즘 변형을 별도의 클래스 계층으로 분리하여 위임(Delegation)함으로써 구현된다 [3, 4]. 궁극적으로 이 패턴은 복잡한 조건문(Switch 또는 If-then-else)을 다형성(Polymorphism)으로 교체하기 위한 구조적 기반(Scaffolding) 역할을 한다 [1, 5].

📖 Core Content

  • 적용 동기와 컨텍스트 분류 부호(Type Code)가 클래스의 동작에 영향을 미칠 때, 즉 switch 문이나 if-then-else 문과 같은 조건부 로직이 존재할 때 주로 적용된다 [6, 7]. 만약 객체가 생성된 이후에도 분류 부호의 값이 변하거나, 이미 다른 이유로 인해 해당 클래스가 상속(Subclassing)을 사용 중이라면 단순한 '분류 부호를 하위 클래스로 바꾸기(Replace Type Code with Subclasses)' 기법을 사용할 수 없으므로 상태(State)나 전략(Strategy) 패턴을 도입해야 한다 [1, 5, 8].
  • 상태(State)와 전략(Strategy)의 구분 구조적으로 두 패턴은 매우 유사하여 적용되는 리팩토링 절차도 동일하다 [9]. 이 둘의 선택은 개발자가 시스템 구조를 어떻게 사고하느냐에 달려 있다 [3]. 단일 알고리즘을 단순화하기 위해 다형성을 활용한다면 '전략(Strategy)' 패턴이라는 용어가 더 적합하며, 상태별 데이터를 이동시키고 객체가 상태를 변경하는 것으로 간주한다면 '상태(State)' 패턴을 사용한다 [9].
  • 리팩토링 실행 절차 (Mechanics)
    1. 먼저 분류 부호를 자체 캡슐화(Self-encapsulate)한다 [4].
    2. 분류 부호의 목적을 나타내는 새로운 상태/전략 클래스와 각 분류 부호에 해당하는 하위 클래스들을 생성한다 [4].
    3. 원본 클래스에 새로운 상태 객체를 위한 필드를 만들고, 원본 클래스의 분류 부호 조회 및 설정 메서드가 이 새로운 상태 객체에 위임하도록 수정한다 [4].
    4. 이 과정을 통해 다형성을 도입할 준비를 마치게 되며, 이후 '조건식을 다형성으로 바꾸기(Replace Conditional with Polymorphism)'를 통해 조건문 로직을 각 상태/전략 하위 클래스로 분산시킨다 [2, 10, 11].

⚖️ Trade-offs & Caveats

  • 구조적 복잡성 증가: 상태/전략 패턴을 도입하면 간접 참조(Indirection)의 수준이 하나 더 추가되며, 상태나 전략을 나타내는 별도의 클래스 계층이 생성되어 전체 클래스 수가 늘어난다 [8, 12]. 조건부 로직이 단일 메서드에만 영향을 미치고 향후 변경될 가능성이 거의 없는 경우에는 이러한 다형성 도입이 과도한 설계(Overkill)가 될 수 있다 [13].
  • 생성 시점의 조건문 잔존: 이 리팩토링을 완료하더라도, 객체를 생성하거나 상태를 변경하여 할당하는 시점(설정 메서드나 팩토리 메서드 내부)에는 적절한 하위 클래스를 생성하기 위한 switch 문이 하나 남게 된다 [14, 15]. 이는 상태가 변경될 때만 실행되는 유일한 조건문으로 고립된다 [15].
  • 단순 대안의 우선 고려: 분류 부호가 객체의 생명 주기 동안 변하지 않고 다른 상속 구조가 없다면, 보다 구조가 단순한 '분류 부호를 하위 클래스로 바꾸기(Replace Type Code with Subclasses)'를 우선적으로 적용하는 것이 권장된다 [5].

🔗 Knowledge Connections

[관계 유형 A: 리팩토링 핵심 기법 (Refactoring Core Techniques)]

  • Replace Conditional with Polymorphism (조건식을 다형성으로 바꾸기)
    • 연결 이유: State/Strategy 패턴은 궁극적으로 복잡한 조건문(switch 등)을 다형성으로 교체하기 위한 기반 구조(Scaffolding) 역할을 수행한다 [1, 5].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 상태나 전략 패턴이 적용된 이후, 어떻게 각 하위 클래스로 조건부 로직이 분산되어 복잡성이 제거되는지 이해할 수 있다 [10, 16].
  • Replace Type Code with Subclasses (분류 부호를 하위 클래스로 바꾸기)
    • 연결 이유: State/Strategy 패턴과 목적은 동일하지만(조건부 로직 처리를 위한 타입 코드 교체), 제약 조건(객체 수명 중 타입 변경 여부 등)에 따라 선택이 달라지는 대안적 기법이다 [1, 5].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 두 기법 간의 트레이드오프와 언제 더 단순한 상속 구조를 선택해야 하는지 판별하는 기준을 학습할 수 있다 [5].

[관계 유형 B: 객체지향 설계 원리 (Object-Oriented Design Principles)]

  • Polymorphism (다형성)
    • 연결 이유: State/Strategy 패턴이 의존하는 가장 핵심적인 객체지향 원리로, 타입에 따라 다른 동작을 하도록 코드를 구성하는 근간이다 [7, 17].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 조건문을 줄이고 시스템의 결합도를 낮추어 확장을 용이하게 만드는 객체지향의 근본적인 메커니즘을 파악할 수 있다 [17].

Deeper Research Questions

  • 단일 알고리즘의 변형을 처리하는 Strategy 패턴과 객체의 상태 전이를 관리하는 State 패턴을 실제 리팩토링 과정에서 구체적으로 어떻게 구분하여 적용하는가?
  • 생성 후 타입 코드가 빈번하게 변경되는 객체에 State/Strategy 패턴을 적용할 때, 동적으로 생성되는 상태 객체 간의 데이터 공유와 메모리 관리 문제는 어떻게 최적화할 수 있는가?
  • Replace Type Code with Subclasses를 적용할 수 없는 기존 상속 계층의 복잡성이 존재하는 레거시 시스템에서, State/Strategy 패턴의 도입은 아키텍처적 유연성에 어떠한 영향을 미치는가?
  • 비즈니스 로직 상 조건문이 거의 변경되지 않거나 그 수가 매우 적은 시스템에서도 State/Strategy 패턴을 도입하는 것이 경제적 관점(유지보수 비용 대비 오버헤드)에서 타당한가?
  • 거대 언어 모델(LLM)을 활용한 AI 기반 리팩토링 도구들은 코드 스멜을 분석하여 State/Strategy 패턴 적용을 자동으로 제안할 때, 도메인의 비즈니스 맥락을 어느 수준까지 이해하고 반영할 수 있는가?

Practical Application Contexts

  • Implementation: 클래스의 동작이 동적으로 변하는 분류 부호(Type Code)를 가질 때, 새로운 상태 클래스 계층을 생성하여 원본 객체가 상태 객체에 로직을 위임(Delegation)하도록 구현하는 데 직접적으로 사용된다.
  • System Design: 다형성을 활용하여 조건부 로직을 각 상태/전략 클래스로 캡슐화함으로써, 새로운 타입이 추가될 때 기존 코드의 수정 없이 하위 클래스만 추가하도록 시스템을 설계할 수 있다.
  • Operation / Maintenance: 상태나 전략별 로직이 각 클래스로 철저히 분리되므로, 특정 상태와 관련된 버그 수정 및 유지보수 시 타 코드에 미치는 변경 영향 범위를 국소화할 수 있다.
  • Learning Path: 리팩토링의 핵심 목표 중 하나인 '조건부 로직 단순화(Simplifying Conditional Expressions)' 영역을 마스터하기 위한 필수 선행 디자인 패턴으로 학습된다.
  • My Project Relevance: 런타임에 동적으로 역할이나 상태가 바뀌어야 하는 도메인 객체(예: 사용자 권한 상태, 상품의 라이프사이클)를 설계하거나 레거시 시스템의 거대한 분기문(Switch)을 객체지향적으로 해체해야 할 때 핵심 가이드라인으로 활용된다.

Adjacent Topics

  • Introduce Null Object (널 객체 도입하기)
    • 확장 방향: 다형성을 활용하여 조건문을 제거하는 또 다른 리팩토링 패턴으로, null 값을 검사하는 로직을 다형성 구조로 분리하는 방법을 통해 객체지향적 설계 역량을 확장할 수 있다.
  • Extract Class (클래스 추출하기)
    • 확장 방향: 비대해진 클래스의 책임을 분리하는 범용적인 기법으로, State/Strategy 패턴 도입 시 위임을 위해 새로운 클래스 계층을 추출해 내는 원리와 연계하여 이해를 넓힐 수 있다.

Last updated: 2026-05-03