반응형
디자인 패턴 완전 정복: 종합 정리와 실무 치트시트
지금까지 디자인 패턴의 세계를 함께 탐험해왔습니다. 이번 포스팅에서는 모든 패턴을 한눈에 정리하고, 실무에서 바로 사용할 수 있는 치트시트를 제공하겠습니다.
언제든 돌아와서 참고할 수 있는 디자인 패턴 완전 가이드로 만들어보겠습니다!
🎯 패턴 선택 플로우차트
프로젝트에서 패턴이 필요한 순간, 이 플로우를 따라해보세요:
문제 상황 발생
↓
❓ 무엇이 자주 변하는가?
↓
┌─────────────────────┬─────────────────────┬─────────────────────┐
│ 알고리즘/기능 │ 상태 │ 객체 생성 │
│ ↓ │ ↓ │ ↓ │
│ 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년)
- Strategy, State 패턴 마스터하기
- 간단한 프로젝트에 적용해보기
- 패턴 없이 구현 vs 패턴 적용 비교
중급 개발자 (2-5년)
- Decorator, Adapter 패턴 추가 학습
- 패턴 조합 사용법 익히기
- 레거시 코드 리팩토링에 적용
고급 개발자 (5년+)
- 전체 GoF 패턴 완전 이해
- 팀 아키텍처 설계에 활용
- 도메인별 커스텀 패턴 개발
🚀 마무리: 패턴을 넘어서
디자인 패턴은 출발점이지 목적지가 아닙니다.
진정한 목표는:
- 변화에 유연한 코드
- 이해하기 쉬운 구조
- 팀원과 소통하기 쉬운 설계
- 테스트하기 좋은 아키텍처
앞으로의 학습 방향:
- 함수형 프로그래밍: 일부 패턴을 더 간결하게 해결
- 도메인 주도 설계: 비즈니스 로직에 집중하는 설계
- 마이크로서비스 패턴: 분산 시스템에서의 패턴들
- 리액티브 프로그래밍: 비동기 처리를 위한 새로운 패턴들
기억하세요:
"패턴을 아는 것보다 언제 사용하지 말아야 하는지 아는 것이 더 중요합니다."
디자인 패턴은 여러분의 코드를 한 단계 더 성숙하게 만들어주는 도구입니다. 하지만 도구에 매몰되지 말고, 항상 "이 코드를 읽을 사람"을 생각하며 개발하세요.
반응형
'프로그래밍 > 소프트웨어 공학 (완)' 카테고리의 다른 글
8장 - 표준 코딩 규칙의 필요성과 장점 (54) (2) | 2025.06.18 |
---|---|
8장 - 프로그래밍 언어의 역사와 발전 (53) (3) | 2025.06.18 |
7장 - 디자인 패턴 실무 적용 가이드 (51) (0) | 2025.06.18 |
7장 - 생성 패턴: Factory Method와 Singleton 패턴 (50) (15) | 2025.06.17 |
7장 - 구조 패턴: Decorator와 Adapter 패턴 (49) (2) | 2025.06.17 |