11 KiB
11 KiB
Design Patterns (디자인 패턴)
📌 Brief 단기 Summary
디자인 패턴은 소프트웨어 개발 과정에서 자주 발생하는 설계 문제들에 대한 재사용 가능한 객체지향적 해결책이자, 리팩토링이 도달하고자 하는 '목표 지점(Target)'입니다. 리팩토링과 디자인 패턴은 자연스러운 공생 관계를 맺고 있으며, 리팩토링은 현재의 구조적 결함(코드 스멜)을 가진 시스템을 유연하고 견고한 디자인 패턴 구조로 안전하게 이끌어가는 구체적인 방법론 역할을 합니다. 이를 통해 개발자는 조건부 로직, 중복 코드, 엉킨 의존성을 다형성과 객체 간의 명확한 역할 분담으로 대체할 수 있습니다.
📖 Core Content
소스 데이터를 기반으로 분석한 디자인 패턴과 리팩토링의 핵심 관계 및 적용 원리는 다음과 같습니다.
-
리팩토링의 목표로서의 디자인 패턴 (Targets for Refactoring)
- 'Gang of Four (GoF)'의 디자인 패턴은 소프트웨어가 지향해야 할 훌륭한 아키텍처의 모습을 제시합니다. 마틴 파울러(Martin Fowler)는 디자인 패턴이 리팩토링의 훌륭한 목표(Target)가 된다고 강조합니다.
- 리팩토링은 다른 어딘가(엉망인 코드)에서 출발하여 이러한 디자인 패턴 구조에 도달하기 위해 거치는 '안전하고 점진적인 경로(Ways to get there from somewhere else)'를 제공합니다.
-
리팩토링에 빈번하게 활용되는 핵심 디자인 패턴
- 상태/전략 패턴 (State/Strategy Pattern): 클래스의 행동에 영향을 미치는 타입 코드(Type Code)를 서브클래싱할 수 없거나 생명주기 동안 변경되어야 할 때 사용됩니다(
Replace Type Code with State/Strategy기법). 이를 통해 다형성을 활용하여 복잡한 조건문(switch, if-else)을 제거할 수 있습니다. - 템플릿 메서드 패턴 (Template Method Pattern): 두 서브클래스에서 순서는 같지만 세부 구현이 다른 메서드를 가졌을 때, 공통된 단계를 상위 클래스의 템플릿으로 올리고 차이점만 다형성으로 위임하여 중복을 제거합니다(
Form Template Method기법). - 팩토리 메서드 패턴 (Factory Method Pattern): 단순히 객체를 생성하는 것 이상의 작업이 필요하거나, 생성 요청을 받는 곳과 실제 생성되는 서브클래스를 분리하여 은닉하고 싶을 때 생성자(Constructor)를 대체합니다(
Replace Constructor with Factory Method기법). - 널 객체 패턴 (Null Object Pattern / Special Case): 코드 베이스 전반에 걸쳐 나타나는 반복적인
null확인 로직을 다형성으로 대체합니다. 기본 동작(아무것도 하지 않음 등)을 수행하는 널 객체를 반환하게 하여(Introduce Null Object기법) 클라이언트 코드를 단순화합니다.
- 상태/전략 패턴 (State/Strategy Pattern): 클래스의 행동에 영향을 미치는 타입 코드(Type Code)를 서브클래싱할 수 없거나 생명주기 동안 변경되어야 할 때 사용됩니다(
-
디자인 패턴의 범주 (Categories of Patterns)
- 목적에 따라 객체의 생성 메커니즘을 다루는 생성형 패턴(Factory Method, Abstract Factory, Singleton 등), 객체들의 구성을 다루는 구조형 패턴(Adapter, Composite, Decorator 등), 객체 간의 통신과 책임 분배를 다루는 행동형 패턴(State, Strategy, Template Method, Command, Observer 등)으로 분류됩니다.
⚖️ Trade-offs & Caveats
디자인 패턴의 도입 및 이를 향한 리팩토링이 항상 긍정적인 결과만을 보장하는 것은 아니며 다음과 같은 부작용과 제약 사항을 수반합니다.
- 간접 참조(Indirection)의 증가와 복잡성: 디자인 패턴을 도입하면 대개 객체나 메서드를 작게 쪼개고 위임(Delegation)하게 되므로 시스템 내의 간접 참조가 늘어납니다. 이는 코드의 유연성을 높이지만, 코드를 읽는 흐름이 여러 객체로 분산되어 직관적인 이해를 방해할 수 있습니다.
- 추측성 일반화 (Speculative Generality): '언젠가 필요할 것'이라는 추측만으로 당장 필요하지 않은 유연성을 확보하기 위해 복잡한 패턴을 도입하면, 오버엔지니어링(Over-engineering)이 발생합니다. 이는 불필요한 위임과 추상 클래스를 양산하여 유지보수 비용을 증가시킵니다. 가장 단순한 형태(Simple Solution)에서 출발해, 필요할 때 패턴으로 리팩토링하는 것이 권장됩니다.
- 잘못된 추상화 (Wrong Abstraction): 잘못된 추상화로 디자인 패턴을 도입하면 오히려 코드 중복보다 유지보수성이 떨어집니다. 패턴에 억지로 코드를 끼워 맞추기 위해 새로운 플래그(Flag)나 조건문이 남발될 수 있으며, 이는 설계가 잘못된 방향으로 가고 있다는 강력한 경고입니다.
🔗 Knowledge Connections
Related Concepts
[아키텍처/설계 목표 (Architecture/Design Goals)]
- Refactoring (리팩토링)
- 연결 이유: 리팩토링은 소프트웨어의 외부 동작을 변경하지 않고 디자인 패턴이라는 목표 구조로 내부를 개선해 나가는 구체적인 실행 체계(수단)이기 때문입니다.
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 완성된 디자인 패턴을 처음부터 완벽하게 설계하는 대신, 지속적이고 작은 변환 단계를 통해 패턴을 도입하는 점진적 설계 진화 과정을 이해할 수 있습니다.
- Polymorphism (다형성)
- 연결 이유: State, Strategy, Null Object 등 주요 패턴들이 장황한 조건부 로직(Switch/If-else)을 제거하기 위해 활용하는 핵심적인 객체지향 원리이기 때문입니다.
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 분기 처리 로직이 어떻게 객체의 타입에 따른 동적 바인딩으로 깔끔하게 대체되는지 그 원리를 명확히 파악할 수 있습니다.
[구현/활용 패턴 (Implementation/Utilization Patterns)]
- State/Strategy Pattern (상태/전략 패턴)
- 연결 이유: 타입 코드(Type Code)를 상속으로 풀 수 없는 경우, 객체의 상태나 알고리즘을 유연하게 교체할 수 있도록 돕는 대표적인 행동형 패턴이기 때문입니다.
- 이 개념을 통해 더 깊게 이해할 수 있는 부분:
Replace Type Code with State/Strategy리팩토링이 어떻게 변경에 닫혀 있고 확장에 열려 있는(Open-Closed) 구조를 만드는지 구체적으로 이해할 수 있습니다.
- Template Method Pattern (템플릿 메서드 패턴)
- 연결 이유: 여러 서브클래스에 흩어진 유사한 로직들의 순서를 통일하고 중복을 제거할 때 사용되는 패턴이기 때문입니다.
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 상속 계층 내에서
Form Template Method기법을 통해 공통점(알고리즘 뼈대)과 차이점(구체적 단계 구현)을 어떻게 우아하게 분리하는지 이해할 수 있습니다.
- Null Object Pattern (널 객체 패턴)
- 연결 이유: 코드 곳곳에 산재한 예외 처리용
null검사를 다형성을 지닌 하나의 특수 케이스(Special Case) 객체로 응집시키기 때문입니다. - 이 개념을 통해 더 깊게 이해할 수 있는 부분: 예외적인 상황조차 일반적인 객체 간 협력망(Message Passing) 속으로 흡수하여 클라이언트 코드를 단순화하는 방법을 이해할 수 있습니다.
- 연결 이유: 코드 곳곳에 산재한 예외 처리용
Deeper Research Questions
- 디자인 패턴을 시스템에 적용하는 과정에서 발생하는 간접 참조(Indirection)의 증가로 인한 인지적 복잡도 상승을 어떻게 정량적으로 측정하고 통제할 수 있는가?
- '추측성 일반화(Speculative Generality)' 코드 스멜을 피하면서, 단순한 로직을 언제 디자인 패턴(예: State/Strategy)으로 리팩토링할 것인지 결정하는 실용적인 판단 기준(예: Rule of Three)은 무엇인가?
- 대규모 레거시 시스템에서 테스트 코드 없이 디자인 패턴을 도입하기 위해, 시스템 구조를 훼손하지 않으면서 '접점(Seam)'을 형성하여 의존성을 끊어내는 전략적 접근법은 무엇인가?
- 객체지향 남용(OO Abusers) 스멜을 제거하기 위해 적용되는 다양한 디자인 패턴들이 오히려 기능 욕심(Feature Envy)이나 미들맨(Middle Man)과 같은 새로운 스멜을 유발하지 않도록 경계하는 아키텍처 가이드라인은 무엇인가?
- 디자인 패턴 도입을 위한 리팩토링이 전체 시스템의 결함 발생률(Defect Rate)이나 변경 영향 범위(Locality of Change)에 미치는 장기적인 효과는 경험적 연구(Empirical Study)를 통해 어떻게 입증되었는가?
Practical Application Contexts
- Implementation: 비즈니스 요구사항 변경에 따라 코드가 복잡해지고 조건문(Switch문 등)이 남발될 때, 코드를 분해하여(Extract Method) 궁극적으로 State나 Strategy 등의 디자인 패턴 구조로 재편하는 리팩토링 작업을 수행합니다.
- System Design: 소프트웨어의 초기 아키텍처를 잡거나 진화시킬 때, 객체의 생성(Factory), 구조 결합(Decorator), 행동 제어(Observer) 등 발생 가능한 변경 요소를 예측하여 GoF 디자인 패턴을 설계의 이정표로 활용합니다.
- Operation / Maintenance: 유지보수 과정에서 이해하기 어려운 레거시 코드의 복잡성을 낮추기 위해, 산재된 로직을 패턴화하여 개발자의 인지 부하를 줄이고 후속 수정 시 발생할 수 있는 부수 효과(Side effects)를 예방합니다.
- Learning Path: 리팩토링 카탈로그에 명시된 개별 단위의 리팩토링 기법(예: 함수 추출, 필드 이동)을 익히고, 이러한 마이크로 단위의 변화가 축적되어 어떻게 거대한 디자인 패턴 아키텍처를 형성하는지 훈련합니다.
- My Project Relevance: 현재 참여 중인 프로젝트에서 기능 확장 시 반복해서 고쳐야 하는 산탄총 수술(Shotgun Surgery)이나 중복 코드가 발견되면, 이를 식별하고 디자인 패턴을 목표로 점진적인 리팩토링을 추진하여 기술 부채(Technical Debt)를 청산합니다.
Adjacent Topics
- Code Smells (코드 스멜)
- 확장 방향: 어떤 징후가 나타났을 때 구조적 개선(디자인 패턴 적용)이 필요한지 진단하는 지표로서, 코드의 부패 상태를 식별하는 다양한 카테고리(비대화, 객체지향 남용 등)를 학습합니다.
- Test-Driven Development (TDD)
- 확장 방향: 디자인 패턴을 도입하는 리팩토링 과정에서 외부 동작이 보존됨을 보장하는 핵심 안전망인 'Red-Green-Refactor' 워크플로우를 함께 조사하여 안정적인 구조 변환 절차를 이해합니다.
Last updated: 2026-05-03