개발
State 패턴: if-else 없이 굴러가는 ‘상태 머신’의 힘
스택큐힙리스트
2025. 7. 23. 08:45
반응형
왜 필요한가?
업무 로직이 복잡해질수록 if (status == CREATED) … else if (status == SHIPPED)… 같은 분기문이 폭발한다. 새 상태가 추가될 때마다 코드가 누더기가 되는 건 시간문제다. State 패턴은 객체의 상태를 클래스로 쪼개고, 상태별 동작(행위)을 그 클래스 안에 숨겨 버린다. 그러면 Context(주 객체)는 “지금 내가 어떤 상태인가?”만 알고 있으면 된다. 조건문 대신 상태 객체 교체만으로 행동이 바뀌니, 가독성과 유지보수성이 크게 올라간다.
핵심 개념 한 줄 요약
“상태를 클래스로 캡슐화해, 객체의 행동을 런타임에 교체한다.”
Context ↔ State 인터페이스 ↔ ConcreteState 들이 만드는 삼각 구도가 전부다.
Kotlin 예제 — 주문 상태 흐름
// 1) 상태 인터페이스
interface OrderState {
fun next(order: Order)
fun cancel(order: Order)
}
// 2) 구체 상태
object Created : OrderState {
override fun next(order: Order) { order.state = Shipped }
override fun cancel(order: Order) { order.state = Cancelled }
}
object Shipped : OrderState {
override fun next(order: Order) { order.state = Delivered }
override fun cancel(order: Order) = println("이미 배송 중이라 취소 불가")
}
object Delivered : OrderState {
override fun next(order: Order) = println("이미 배송 완료")
override fun cancel(order: Order) = println("반품 절차 필요")
}
object Cancelled : OrderState {
override fun next(order: Order) = println("취소된 주문")
override fun cancel(order: Order) = println("이미 취소됨")
}
// 3) 컨텍스트
class Order(var state: OrderState = Created) {
fun next() = state.next(this)
fun cancel() = state.cancel(this)
}
// 사용 예
val order = Order()
order.next() // Created → Shipped
order.cancel() // 배송 중이라 취소 불가
객체를 갈아끼우는 것만으로 상태 전이가 끝난다. State 클래스들이 단일 책임을 지키기 때문에 새 단계(예: Returned)를 추가해도 기존 코드는 손대지 않는다.
언제 쓸까요?
- 주문·결제·배송처럼 상태 전이가 명확한 도메인
- 게임 캐릭터(대기→공격→피격) 같은 상태머신 구현
- 네트워크 커넥션(연결 시도→연결됨→종료) 관리
- 리팩터링: 거대한 switch 문을 객체지향적으로 분리할 때
실전 팁
- enum + State 혼합
상태 개수를 enum으로 관리하면서, enum에 전략 메서드를 붙여 가볍게 구현할 수도 있다. - Spring StateMachine
복잡한 전이 조건과 Guard 로직이 많다면, OSS 라이브러리로 도표 기반 정의를 추천한다. - 디버깅
전이 로그를 남기면 QA 단계에서 “어디서 막혔는지” 한눈에 보인다.
반응형