1. Strategy 패턴이란?
Strategy 패턴은 행위를 캡슐화한 알고리즘을 런타임에 바꿀 수 있도록 하는 디자인 패턴입니다. 즉, 특정 행위(전략)를 사용하는 주체가 그 구현을 직접 알지 않아도 되고, 실행 시점에 전략을 선택하거나 변경할 수 있도록 하여 유연성과 확장성을 극대화합니다.
사용 목적
- 조건에 따라 다양한 행위를 적용할 수 있도록 분리
- 코드 변경 없이 알고리즘 교체 가능
- if/else 또는 switch 문을 객체 구조로 치환
2. 구조와 동작 원리
기본 구조
Context
├─ Strategy 인터페이스 타입을 필드로 보유
└─ setStrategy() 로 전략 주입 가능
Strategy (인터페이스)
├─ ConcreteStrategyA
└─ ConcreteStrategyB
예시 코드
public interface PaymentStrategy {
void pay(int amount);
}
public class CreditCardStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("신용카드로 결제: " + amount);
}
}
public class KakaoPayStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("카카오페이로 결제: " + amount);
}
}
public class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void pay(int amount) {
strategy.pay(amount);
}
}
3. 어떤 문제를 해결하기 위해 자주 쓰일까?
전략 적용의 일반적 배경
기존에는 하나의 클래스 내에서 다양한 조건(if-else 또는 switch)으로 로직을 분기하는 경우가 많았습니다. 예를 들어 결제 서비스 하나에 카드, 카카오페이, 포인트 등 여러 결제 방식이 혼재돼 있는 경우, 코드가 복잡해지고 유지보수가 어려워지며 테스트 또한 힘들어집니다.
Strategy 패턴을 적용하면 각 결제 수단별로 클래스를 분리하여 관리할 수 있습니다. 이렇게 하면 클래스 수는 증가하지만 각 전략 클래스가 단일 책임 원칙(SRP)을 만족하게 되어 가독성과 확장성이 훨씬 높아집니다.
런타임에 다른 알고리즘을 사용하는 이유
Strategy 패턴의 핵심은 행위를 캡슐화해서 실행 중에 전략을 동적으로 교체할 수 있다는 점입니다. 사용자 입력이나 환경 설정, 혹은 요청 컨텍스트에 따라 적절한 전략을 런타임에 선택해 적용하는 것이 가능합니다.
예를 들어 결제 시스템에서 사용자가 '카드', '카카오페이', '포인트' 중 어떤 결제 방식을 사용할지 모른다면, 실행 시점에 맞는 전략 객체를 주입해 결제를 수행할 수 있습니다.
주요 활용 케이스
- 조건 분기 제거: 전략 객체로 교체
- 런타임에 알고리즘이 바뀌는 경우 (결제 방식, 정렬 방식 등)
- OCP(Open-Closed Principle) 준수: 기존 코드를 수정하지 않고 전략 추가 가능
4. Spring에서 실전 활용 예시
1) Bean 주입으로 전략 교체
@Component
public class PaymentService {
private final Map<String, PaymentStrategy> strategyMap;
public PaymentService(List<PaymentStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(
s -> s.getClass().getSimpleName().replace("Strategy", "").toLowerCase(),
s -> s
));
}
public void pay(String type, int amount) {
PaymentStrategy strategy = strategyMap.get(type);
strategy.pay(amount);
}
}
2) @Qualifier로 특정 전략 명시 주입
- 복수 전략 중 하나만 사용할 경우 명시적으로 주입
3) enum 기반 전략 매핑
전략 선택 시 문자열 대신 enum 타입을 사용하는 방식은 타입 안정성과 명시성이 뛰어납니다.
public enum PaymentType {
CARD, KAKAO
}
@Component
public class PaymentService {
private final Map<PaymentType, PaymentStrategy> strategyMap;
public PaymentService(List<PaymentStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(
s -> PaymentType.valueOf(s.getClass().getSimpleName().replace("Strategy", "").toUpperCase()),
s -> s
));
}
public void pay(PaymentType type, int amount) {
PaymentStrategy strategy = strategyMap.get(type);
strategy.pay(amount);
}
}
5. 장단점
장점단점
런타임 전략 교체 가능 | 클래스 수 증가 가능 |
조건문 제거로 가독성 향상 | 전략 선택을 위한 관리 코드 필요 |
OCP 원칙 충족 | 전략 간 공통 인터페이스 설계가 중요함 |
6. 실무 적용 팁
- 전략이 많다면 Map 주입 방식 추천 (Spring에서는 자동 주입 가능)
- 전략 간 입력값이 다르면 공통 인터페이스 설계 또는 래퍼 객체 사용 고려
- enum + 함수형 인터페이스 조합도 최근 자주 사용됨
7. 마무리 요약
Strategy 패턴은 조건 분기 대신 전략 객체를 통해 행위를 유연하게 교체할 수 있게 해주는 구조입니다. 실행 중 전략을 교체할 수 있고, 새로운 전략 추가 시 기존 코드를 수정하지 않아도 되므로 유지보수와 확장성 면에서 매우 유리합니다. Spring의 DI와 결합하면 실전에서 더 효과적으로 활용할 수 있습니다.
'Back-End > Java & Spring' 카테고리의 다른 글
Specification 패턴 : 복잡한 비즈니스 규칙을 조합 및 재사용하는 전략 (0) | 2025.05.11 |
---|---|
Adapter 패턴 : 호환되지 않는 인터페이스를 연결하는 설계 전략 (0) | 2025.05.11 |
Template Method 패턴&공통 로직 및 기능 (0) | 2025.05.11 |
Proxy 패턴 개념&Spring AOP (0) | 2025.05.11 |
Factory 패턴 개념 (0) | 2025.05.11 |