본문 바로가기
프로그래밍/소프트웨어 공학 (완)

7장 - 디자인 패턴 종합 정리 및 치트시트 (52)

by 서가_ 2025. 6. 18.
반응형

디자인 패턴 완전 정복: 종합 정리와 실무 치트시트

지금까지 디자인 패턴의 세계를 함께 탐험해왔습니다. 이번 포스팅에서는 모든 패턴을 한눈에 정리하고, 실무에서 바로 사용할 수 있는 치트시트를 제공하겠습니다.

언제든 돌아와서 참고할 수 있는 디자인 패턴 완전 가이드로 만들어보겠습니다!

 

🎯 패턴 선택 플로우차트

프로젝트에서 패턴이 필요한 순간, 이 플로우를 따라해보세요:

문제 상황 발생
    ↓
❓ 무엇이 자주 변하는가?
    ↓
┌─────────────────────┬─────────────────────┬─────────────────────┐
│    알고리즘/기능     │        상태         │     객체 생성       │
│        ↓           │        ↓           │        ↓           │
│  Strategy 패턴      │   State 패턴        │  Factory Method     │
└─────────────────────┴─────────────────────┴─────────────────────┘

❓ 어떤 문제를 해결하려는가?
    ↓
┌─────────────────────┬─────────────────────┬─────────────────────┐
│   기능 동적 조합     │   인터페이스 호환    │   인스턴스 제어     │
│        ↓           │        ↓           │        ↓           │
│  Decorator 패턴     │   Adapter 패턴      │   Singleton 패턴    │
└─────────────────────┴─────────────────────┴─────────────────────┘

📋 패턴별 완전 치트시트

🎭 행위 패턴 (Behavioral Patterns)

Strategy 패턴

한 줄 요약: 알고리즘을 교체 가능하게 만들기

항목 내용
언제 사용 같은 작업을 다양한 방법으로 수행해야 할 때
핵심 구조 Context + Strategy 인터페이스 + 구체적 전략들
실무 예시 결제 시스템, 정렬 알고리즘, 파일 압축 방식
장점 런타임 알고리즘 교체, 확장성, 테스트 용이성
단점 클래스 수 증가, 클라이언트가 전략을 알아야 함
주의사항 단순한 조건분기에는 과함
// 핵심 패턴
public interface Strategy {
    void execute();
}

public class Context {
    private Strategy strategy;
    public void setStrategy(Strategy strategy) { this.strategy = strategy; }
    public void doWork() { strategy.execute(); }
}

State 패턴

한 줄 요약: 상태에 따른 행동 변화를 우아하게 관리

항목 내용
언제 사용 객체의 상태에 따라 행동이 달라질 때
핵심 구조 Context + State 인터페이스 + 구체적 상태들
실무 예시 주문 상태 관리, 게임 캐릭터 상태, TCP 연결
장점 상태별 행동 캡슐화, 상태 전환 로직 분리
단점 클래스 수 증가, 상태가 적으면 과함
주의사항 상태 전환이 복잡할 때만 사용
// 핵심 패턴
public interface State {
    void handle(Context context);
}

public class Context {
    private State state;
    public void setState(State state) { this.state = state; }
    public void request() { state.handle(this); }
}

🏗️ 구조 패턴 (Structural Patterns)

Decorator 패턴

한 줄 요약: 기능을 동적으로 조합하는 마법

항목 내용
언제 사용 객체에 동적으로 기능을 추가해야 할 때
핵심 구조 Component + ConcreteComponent + Decorator + ConcreteDecorator
실무 예시 커피 옵션, 스트림 처리, HTTP 미들웨어
장점 무한 조합 가능, 런타임 기능 추가
단점 체인이 길어지면 복잡, 디버깅 어려움
주의사항 데코레이터 순서가 중요할 수 있음
// 핵심 패턴
public abstract class Component {
    public abstract void operation();
}

public abstract class Decorator extends Component {
    protected Component component;
    public Decorator(Component component) { this.component = component; }
    public void operation() { component.operation(); }
}

Adapter 패턴

한 줄 요약: 호환되지 않는 인터페이스를 연결하는 다리

항목 내용
언제 사용 기존 클래스를 수정하지 않고 호환성을 확보할 때
핵심 구조 Target 인터페이스 + Adapter + Adaptee
실무 예시 외부 라이브러리 통합, 레거시 시스템 연동
장점 기존 코드 수정 없음, 인터페이스 통일
단점 코드 복잡성 증가, 성능 오버헤드
주의사항 어댑터에 비즈니스 로직 넣지 말 것
// 핵심 패턴
public interface Target {
    void request();
}

public class Adapter implements Target {
    private Adaptee adaptee;
    public void request() { adaptee.specificRequest(); }
}

🏭 생성 패턴 (Creational Patterns)

Factory Method 패턴

한 줄 요약: 객체 생성을 서브클래스에 위임

항목 내용
언제 사용 생성할 객체의 타입을 런타임에 결정할 때
핵심 구조 Creator + ConcreteCreator + Product + ConcreteProduct
실무 예시 데이터베이스 연결, 파서 생성, 드라이버 팩토리
장점 확장성, 느슨한 결합, 생성 로직 캡슐화
단점 클래스 수 증가, 복잡성 증가
주의사항 단순한 객체 생성에는 과함
// 핵심 패턴
public abstract class Creator {
    public abstract Product factoryMethod();

    public void someOperation() {
        Product product = factoryMethod();
        // 비즈니스 로직...
    }
}

Singleton 패턴

한 줄 요약: 인스턴스를 하나로 제한하는 제어자

항목 내용
언제 사용 시스템에서 하나의 인스턴스만 필요할 때
핵심 구조 private 생성자 + static 인스턴스 + getInstance()
실무 예시 설정 관리자, 로거, 캐시 매니저, 연결 풀
장점 메모리 절약, 전역 접근, 지연 초기화
단점 전역 상태, 테스트 어려움, 확장성 제한
주의사항 스레드 안전성 반드시 고려
// 권장 구현 (Enum 방식)
public enum SingletonService {
    INSTANCE;

    public void doSomething() {
        // 비즈니스 로직
    }
}

🎮 실무 상황별 패턴 매칭 가이드

웹 개발 상황

상황 추천 패턴 이유
다양한 인증 방식 (OAuth, JWT, 세션) Strategy 인증 알고리즘이 자주 변경됨
주문/결제 상태 관리 State 복잡한 상태 전환 로직
HTTP 요청 처리 (로깅, 인증, 캐싱) Decorator 기능을 조합해서 적용
외부 API 통합 Adapter 각기 다른 API 인터페이스
환경별 설정 관리 Singleton 전역 설정 접근 필요

게임 개발 상황

상황 추천 패턴 이유
캐릭터 스킬 시스템 Strategy 스킬이 자주 추가/변경됨
캐릭터 상태 (평상시, 전투, 사망) State 상태에 따라 행동이 다름
아이템 강화 시스템 Decorator 다양한 옵션 조합
플랫폼별 입력 처리 Adapter 플랫폼마다 다른 API
게임 매니저 Singleton 전역 게임 상태 관리

모바일 앱 개발

상황 추천 패턴 이유
이미지 필터링 Strategy 다양한 필터 적용
앱 라이프사이클 관리 State 앱 상태에 따른 처리
푸시 알림 스타일링 Decorator 다양한 알림 옵션
소셜 로그인 통합 Adapter 각기 다른 SDK
앱 설정 관리 Singleton 전역 설정 접근

⚡ 성능 고려사항

패턴별 성능 특성

패턴 메모리 사용량 실행 속도 주의사항
Strategy 보통 빠름 전략 객체 재사용 고려
State 보통 빠름 상태 객체 재사용 권장
Decorator 높음 느림 체인 길이 제한 필요
Adapter 낮음 보통 불필요한 변환 최소화
Factory Method 보통 보통 객체 풀링 고려
Singleton 낮음 빠름 동기화 오버헤드 주의

최적화 팁

// ✅ Strategy 객체 재사용
public class PaymentContext {
    private static final Map<String, PaymentStrategy> strategies = new HashMap<>();
    static {
        strategies.put("CARD", new CardPaymentStrategy());
        strategies.put("BANK", new BankTransferStrategy());
    }

    public PaymentStrategy getStrategy(String type) {
        return strategies.get(type); // 매번 new 하지 말고 재사용
    }
}

// ✅ Decorator 체인 제한
public class RequestProcessor {
    private static final int MAX_DECORATORS = 5;

    public void addDecorator(RequestDecorator decorator) {
        if (decoratorCount >= MAX_DECORATORS) {
            throw new IllegalStateException("Too many decorators");
        }
        // 추가 로직...
    }
}

🚨 안티패턴과 함정들

1. Golden Hammer (만능 망치)

// ❌ 모든 문제를 Singleton으로 해결하려는 시도
public class DatabaseSingleton { /* ... */ }
public class LoggerSingleton { /* ... */ }
public class ConfigSingleton { /* ... */ }
public class CacheSingleton { /* ... */ }
// 전역 상태가 너무 많아서 테스트와 유지보수가 악몽

// ✅ 의존성 주입으로 해결
public class OrderService {
    private final DatabaseService dbService;
    private final Logger logger;

    public OrderService(DatabaseService dbService, Logger logger) {
        this.dbService = dbService;
        this.logger = logger;
    }
}

2. Premature Optimization (성급한 최적화)

// ❌ 당장 필요하지 않은 Strategy 패턴
public interface CalculationStrategy {
    int calculate(int a, int b);
}

public class AdditionStrategy implements CalculationStrategy {
    public int calculate(int a, int b) { return a + b; } // 너무 단순함
}

// ✅ 간단한 경우는 직접 구현
public class Calculator {
    public int add(int a, int b) { return a + b; }
    public int subtract(int a, int b) { return a - b; }
}

3. God Object (전지전능한 객체)

// ❌ Factory에 모든 생성 책임 몰아주기
public class UniversalFactory {
    public Object create(String type) {
        // 수백 개의 if-else...
        if (type.equals("user")) return new User();
        if (type.equals("order")) return new Order();
        if (type.equals("product")) return new Product();
        // 끝없이 계속...
    }
}

// ✅ 도메인별로 Factory 분리
public class UserFactory { /* 사용자 관련만 */ }
public class OrderFactory { /* 주문 관련만 */ }
public class ProductFactory { /* 상품 관련만 */ }

🔧 팀 개발을 위한 실무 가이드

코드 리뷰 체크리스트

✅ Strategy 패턴 리뷰 포인트

  • 알고리즘이 정말 자주 변경되는가?
  • 전략 인터페이스가 명확한가?
  • 전략 객체가 재사용되는가?
  • 클라이언트가 전략을 쉽게 교체할 수 있는가?

✅ Singleton 패턴 리뷰 포인트

  • 정말로 하나의 인스턴스만 필요한가?
  • 스레드 안전성이 고려되었는가?
  • 테스트 격리가 가능한가?
  • 의존성 주입으로 대체 가능한가?

문서화 템플릿

/**
 * Payment Strategy Pattern Implementation
 * 
 * 목적: 다양한 결제 방식을 런타임에 교체 가능하게 함
 * 
 * 사용 시나리오:
 * - 신용카드, 계좌이체, 간편결제 등 결제 방식 확장
 * - 국가별로 다른 결제 방식 지원
 * 
 * 주의사항:
 * - PaymentStrategy 구현체는 반드시 상태를 가지지 말 것
 * - 전략 객체는 ApplicationContext에서 재사용
 * 
 * @see PaymentContext
 * @see PaymentStrategy
 * @author TeamName
 * @since 2025-06-10
 */
public interface PaymentStrategy {
    PaymentResult process(PaymentRequest request);
}

🎓 학습 로드맵

초급 개발자 (0-2년)

  1. Strategy, State 패턴 마스터하기
  2. 간단한 프로젝트에 적용해보기
  3. 패턴 없이 구현 vs 패턴 적용 비교

중급 개발자 (2-5년)

  1. Decorator, Adapter 패턴 추가 학습
  2. 패턴 조합 사용법 익히기
  3. 레거시 코드 리팩토링에 적용

고급 개발자 (5년+)

  1. 전체 GoF 패턴 완전 이해
  2. 팀 아키텍처 설계에 활용
  3. 도메인별 커스텀 패턴 개발

🚀 마무리: 패턴을 넘어서

디자인 패턴은 출발점이지 목적지가 아닙니다.

진정한 목표는:

  • 변화에 유연한 코드
  • 이해하기 쉬운 구조
  • 팀원과 소통하기 쉬운 설계
  • 테스트하기 좋은 아키텍처

앞으로의 학습 방향:

  • 함수형 프로그래밍: 일부 패턴을 더 간결하게 해결
  • 도메인 주도 설계: 비즈니스 로직에 집중하는 설계
  • 마이크로서비스 패턴: 분산 시스템에서의 패턴들
  • 리액티브 프로그래밍: 비동기 처리를 위한 새로운 패턴들

기억하세요:

"패턴을 아는 것보다 언제 사용하지 말아야 하는지 아는 것이 더 중요합니다."

디자인 패턴은 여러분의 코드를 한 단계 더 성숙하게 만들어주는 도구입니다. 하지만 도구에 매몰되지 말고, 항상 "이 코드를 읽을 사람"을 생각하며 개발하세요.

반응형