개발
코틀린 Strategy 패턴 한방에 이해하기 – 알고리즘을 ‘핫스왑’하는 가장 간단한 방법
스택큐힙리스트
2025. 7. 21. 22:34
반응형
1. 왜 Strategy 패턴인가?
“결제 수수료 정책이 또 바뀌었대요.”
매번 if … else … 문을 고쳐 배포했다면 이제는 Strategy 패턴으로 갈아타 보세요.
행위를 인터페이스(전략)로 분리해 두면 런타임에 원하는 구현으로 교체할 수 있어 코드 가독성·테스트 용이성이 모두 상승합니다. 이를테면 신용카드 → 계좌이체 → 간편결제 로직을 바꿀 때 클래스만 새로 만들어 DI(Container)가 끼워 넣어 주면 끝!
2. 핵심 구조 (말로 툭 설명)
- Strategy: 알고리즘 규약을 담은 인터페이스
- ConcreteStrategy: 실제 알고리즘 클래스들
- Context: Strategy를 받아 행위를 위임하고, 필요하면 전략을 교체
3. Kotlin 예제 – 결제 수수료 계산기
// 1) 전략 인터페이스
fun interface FeePolicy {
fun fee(amount: Int): Int
}
// 2) 구체 전략
class CardFeePolicy : FeePolicy {
override fun fee(amount: Int) = (amount * 0.03).toInt() // 3% 카드 수수료
}
class BankTransferFeePolicy : FeePolicy {
override fun fee(amount: Int) = (amount * 0.01).toInt() // 1% 계좌이체 수수료
}
// 3) 컨텍스트
class PaymentService(private var feePolicy: FeePolicy) {
fun changePolicy(newPolicy: FeePolicy) {
this.feePolicy = newPolicy // 런타임 전략 교체
}
fun pay(amount: Int): Int {
val fee = feePolicy.fee(amount)
println("결제 금액: $amount, 수수료: $fee")
return amount + fee
}
}
// --- 사용 예시 ---
fun main() {
val payment = PaymentService(CardFeePolicy())
payment.pay(10_000) // 카드 기준
payment.changePolicy(BankTransferFeePolicy()) // 전략 교체
payment.pay(10_000) // 계좌이체 기준
}
코틀린의 함수형 인터페이스(fun interface) 덕분에 람다로도 전략을 가볍게 전달할 수 있습니다 – changePolicy { amt -> (amt * 0.05).toInt() } 처럼요.
4. 장·단점 체크리스트
- 👍 장점
- OCP 준수: 새 정책 추가 시 기존 클래스 수정 없음
- 테스트 수월: Mockito stub로 전략만 갈아끼워 시나리오 검증
- 조합 가능: 데코레이터·팩토리와 함께 쓰면 선택 로직이 더 우아
- ⚠️ 단점
- 전략 클래스가 많아지면 파일 수 폭증
- 단순 분기라면 when 문이 오히려 직관적일 수도
5. 실전 팁
- 스프링 DI: @Component + @Qualifier로 전략을 자동 주입하면 컨텍스트가 훨씬 깔끔합니다.
- 함수 타입 전략: 작은 로직이면 클래스를 생략하고 (Int) -> Int 람다로 전달해 boilerplate를 줄이세요.
- 테스트 우선: Context의 changePolicy()와 pay() 호출을 먼저 테스트로 고정해 두면 전략 교체에도 마음이 편합니다.
반응형