Spring Statemachine 적용사례
by 구강원
State (상태)
State
“임의의 주어진 시간에의 갖게 되는 하나의 상태를 칭한다”
문을 만들어보아요!
오늘의 주인공 방문입니다.
방문 클래스 정의
방문 방문 클래스
Door
+ doOpen() :void
+ doClose() :void
+ doLock() :void
+ doUnlock() :void
+ isWayEmpty() :boolean
Door
- state :DoorState = Opend
+ doOpen() :void
+ doClose() :void
+ doLock() :void
+ doUnlock() :void
«enumeratio...
DoorState
Opend
Closed
Locked
방문 클래스 + 방문 상태
방문 상태 정의
방문 상태도
방문 클래스
방문 상태도
Locked
Opened Closed
Initial
doClose [doorWay->IsWayEmpty]
doLockdoUnlock
doOpen
create
Door
- state :DoorState = Opend
+ doOpen() :void
+ doClose() :void
+ doLock() :void
+ doUnlock() :void
«enumeratio...
DoorState
Opend
Closed
Locked
방문 상태표(State Chart)
방문 상태도
Locked
Opened Closed
Initial
doClose [doorWay->IsWayEmpty]
doLockdoUnlock
doOpen
create
State
Next State
S0
Locked
S1
Opened
S2
Closed
S3
Initial
S0Locked
doUnlock
S1Opened
doCl... do...
S2Closed
doLock doOpen
S3Initial
create
방문 상태표(state chart)
State Machine
Diagram
(상태도)
상태도 표기법
State Machine Diagram
• 사건(Event)에 따른 상태간 전이(Transition) 및 전이 조건(Guard) 그리고 상태 변경에
따른 행위(Action)를 정의한 다이어그램
• 제어 플로우가 아닌 상태 변경과 관련된 프로세스에 적합
• 라이프 사이클 전체를 취급 하거나 동시 동작을 기술이 필요 할때 사용
● entry : 상태 진입시 실행될 액션
● exit : 상태 진출시 실행될 액션
● do : 상태안에서 반복 실행될 처리, 상태 이동시 중단
State Machine 표기법
State Machine 표기법
Trigger
● 상태 전이가 발생(촉발) 하는 원인으로 보통 이벤트라 칭한다.
● signal, event, change, passage time 4가지 발생(촉발) 타입
Guard
● 상태 이동 성립 조건, 성립 조건이 True일경우만 상태 전이가 가능하다.
Effect
● 상태 이동에 따른 효과, 실행 액션
Source State Target State
trigger [guard] / effect
여러가지 State
Pseudo
State
Choice
상태를 추가 해보세요
무한 합니다….
말발굽 이중잠금장치 자동잠금장치
도어락 추가
Locked
+ entry / 도어락 잠김
Opened Closed
+ entry / 도어락 열림
Initial
비밀번호일치 5초후 자동 이벤트 발생
doClose [doorWay->IsWayEmpty]
doLock
doUnlock
doOpen
create
1
2
도어락추가
Action 위치
1 2 3
Action 위치에따른 실행
1 2 3
Spring
State Machine
적용사례
A사 O2O 프로젝트
O2O
이미지출처
훌륭한 DA 한분이 상태를 잘 정의
O2O주문 모델링 - 주문 상태
접수구분 - 배정상태 - 수락결과 - 출고유형
1 개월 설계 및 기술 검토
일반 주문 상태 약 19개
O2O주문 모델링 - 주문 상태
접수구분 - 배정상태 - 수락결과 - 출고유형
내가 생각 했던 사용 기대 용어
배정접수,
배정확정,
수락요청
수락, 거절,
출고지시,
출고완료
….
공식 용어가 아닌 코드 값으로 대화
10-10
10-20
20-10
30-10
30-20
20-99
....
배정접수,
배정확정,
수락요청
수락, 거절,
출고지시,
출고완료
….
사용 기대 용어 실제 사용 용어
X
O2O주문 모델링 - 주문 상태
접수구분 - 배정상태 - 수락결과 - 출고유형
혼란
이런느낌?
생존을 위한 몸부림
A군 담당 B군 담당(나) C군 담당
B군 담당(나) B군 담당(나)
상태 코드의 비밀
A군 담당 B군 담당(나) C군 담당
10
20 30 41 50 9030
31
40
상태전이 = 현재 상태 +1 or + 10
상태도를 그리기 전까지...
- 상태 전이는 다음과 같았다.
상태전이 = 현재 상태 +1 or + 10
- 다음 4가지 값의 조합으로 주문상태를 식별 한다.
접수구분 - 배정상태 - 수락결과 - 출고유형
- 각 상태 변경시 entry action, guard 등이 다양하게 존재
- 각 개인이 이해하는 수준이 틀리고 일치 하지 않음
A군 B군
C군
갱신! 갱신! 갱신! 상태도 최종 완성
- 교환에 대한 상태도 추가
- 반품에 대한 상태도 추가
- 취소에 대한 상태도 추가
Implementation(구현)
어떻게?
껌기계를 만들어 보아요
매진(껌없음)
대기(동전없음) 동전있음
껌배출
Choice
동전입력 [동전없음]
크랭크 돌림/껌내보냄
[남은껌있음]
[남은껌 없음]
껌 꺼냄
껌채움
껌뽑기 기계
코드가 직관적? 이고 빠르게 결과를 낼수 있다… 단 변화에 취약
1안 : 닥 개발
if if if if if... huk huk huk
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
} else if (state == SOLD_OUT) {
System.out.println("You can't insert a quarter, the machine is sold out");
} else if (state == SOLD) {
System.out.println("Please wait, we're already giving you a gumball");
}
}
public void turnCrank() {
if (state == SOLD) {
System.out.println("Turning twice doesn't get you another gumball!");
} else if (state == NO_QUARTER) {
System.out.println("You turned but there's no quarter");
} else if (state == SOLD_OUT) {
System.out.println("You turned, but there are no gumballs");
} else if (state == HAS_QUARTER) {
System.out.println("You turned...");
state = SOLD;
코드가 깔끔해지고 “If hell” 에서 벗어 날 수 있음
하지만 각 상태별로 의존성 제약이 생김
2안 : State Pattern 적용
public void insertQuarter() {
state.insertQuarter();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
깔금 깔금
class HasQuarterState implements State {
...
}
class HasQuarterState implements State {
...
}
상태로직을 담은 상태 클래스
State Pattern
Context
+ request() :void
«interface»
State
+ handle() :void
껌배출
껌매진
동전없음
동전있음
상태를 기반으로 하는 행동을 캡슐화 하고 현재 상태에 위임 한다.
• 객체 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있다.
• OCP(Open Close Principal) 따른다.
(상태를 무한 증가 시킬수 있음…)
• 바뀌는 부분인 상태를 캡슐화
• 전략패턴과 비슷
Gumball Class Diagram O2O 주문배정 Class Diagram
O2O State Pattern 적용시...
3안 Spring
State Machine
복잡한 상태를 부탁해
S2M Feature
Easy to use flat one level state machine for simple use cases.
Hierarchical state machine structure to ease complex state configuration.
State machine regions to provide even more complex state configurations.
Usage of triggers, transitions, guards and actions.
Type safe configuration adapter.
Builder pattern for easy instantiation for use outside of Spring Application context
Recipes for usual use cases
Distributed state machine based on a Zookeeper
State machine event listeners.
Spring IOC integration to associate beans with a state machine.
S2M Example
Locked
Opened Closed
Initial
doClose [doorWay->IsWayEmpty]
doLockdoUnlock
doOpen
create
상태머신 요소(상태,이벤트, 가드) 식별
static enum States {
Opend, Closed, Locked
}
static enum Events {
Open, Close, Lock, Unlock
}
@Bean
static Guard<States, Events> isEmptyDoorWay() {
return new Guard<States, Events>() {
@Override
public boolean evaluate(StateContext<States, Events> context) {
return new Random().nextBoolean();
}
};
}
➜ Enum Type 상태 정의
➜ Enum Type 이벤트 정의
➜ Guard 즉 상태 전이 가능 여부 평가
상태머신 Configuration
@Configuration
@EnableStateMachine
public static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
states
.withStates()
.initial(States.Closed)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
transitions
.withExternal()
.source(States.Opend).target(States.Closed).event(Events.Close)
.guard(isEmptyDoorWay())
.and()
.withExternal()
.source(States.Closed).target(States.Opend).event(Events.Open);
}
}
➜ 상태 등록 Config
➜ 상태 동작(이벤트, 가드) config
S2M에서 제공하는 상태
States
Hierarchical States
Distributed States
Regions
Transitions
Guards
Actions
Pseudo States
Initial State
Terminate State
History State
Choice State
Fork State
Join State
도입결과
1. 현격히 줄어즌 코드량(IF문 제거)
2. 주문 상태 전이가 명확 해지고 좀비 상태가 줄어듬
정의하지 않은 상태전이가 일어나는 현상
3. 컴포넌트간 역할이 명확해짐
모든 상태 변경 처리는 S2M이 처리… but 일부분은 적용하지 못하고 프로젝트 종료
프로젝를 겸한 2개월 안정화 기간동안 문제가 발생한 곳이 주로 S2M을 적용하지 못한 곳
마치며
1. 상태머신을 적용하기 위해선 상태도를 반드시 그립니다.
2. S2M GitHub의 Example을 꼭 활용하세요
3. S2M 사용시 1.0.2 이상 사용하세요 @Scope 적용 가능
(Spring 4.2이에에서 @Scope을 사용하기 위해 약간의 수정 필요)
4년전 이맘때쯤... Redmine 일감 상태도
Before After
반복반복반복…
감사합니다

Spring statemachine

  • 1.
  • 2.
  • 3.
    State “임의의 주어진 시간에의갖게 되는 하나의 상태를 칭한다”
  • 4.
  • 5.
  • 6.
    방문 클래스 정의 방문방문 클래스 Door + doOpen() :void + doClose() :void + doLock() :void + doUnlock() :void + isWayEmpty() :boolean
  • 7.
    Door - state :DoorState= Opend + doOpen() :void + doClose() :void + doLock() :void + doUnlock() :void «enumeratio... DoorState Opend Closed Locked 방문 클래스 + 방문 상태 방문 상태 정의
  • 8.
    방문 상태도 방문 클래스 방문상태도 Locked Opened Closed Initial doClose [doorWay->IsWayEmpty] doLockdoUnlock doOpen create Door - state :DoorState = Opend + doOpen() :void + doClose() :void + doLock() :void + doUnlock() :void «enumeratio... DoorState Opend Closed Locked
  • 9.
    방문 상태표(State Chart) 방문상태도 Locked Opened Closed Initial doClose [doorWay->IsWayEmpty] doLockdoUnlock doOpen create State Next State S0 Locked S1 Opened S2 Closed S3 Initial S0Locked doUnlock S1Opened doCl... do... S2Closed doLock doOpen S3Initial create 방문 상태표(state chart)
  • 10.
  • 11.
    State Machine Diagram •사건(Event)에 따른 상태간 전이(Transition) 및 전이 조건(Guard) 그리고 상태 변경에 따른 행위(Action)를 정의한 다이어그램 • 제어 플로우가 아닌 상태 변경과 관련된 프로세스에 적합 • 라이프 사이클 전체를 취급 하거나 동시 동작을 기술이 필요 할때 사용
  • 12.
    ● entry :상태 진입시 실행될 액션 ● exit : 상태 진출시 실행될 액션 ● do : 상태안에서 반복 실행될 처리, 상태 이동시 중단 State Machine 표기법
  • 13.
    State Machine 표기법 Trigger ●상태 전이가 발생(촉발) 하는 원인으로 보통 이벤트라 칭한다. ● signal, event, change, passage time 4가지 발생(촉발) 타입 Guard ● 상태 이동 성립 조건, 성립 조건이 True일경우만 상태 전이가 가능하다. Effect ● 상태 이동에 따른 효과, 실행 액션 Source State Target State trigger [guard] / effect
  • 14.
  • 15.
  • 16.
  • 17.
    도어락 추가 Locked + entry/ 도어락 잠김 Opened Closed + entry / 도어락 열림 Initial 비밀번호일치 5초후 자동 이벤트 발생 doClose [doorWay->IsWayEmpty] doLock doUnlock doOpen create 1 2 도어락추가
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
    훌륭한 DA 한분이상태를 잘 정의 O2O주문 모델링 - 주문 상태 접수구분 - 배정상태 - 수락결과 - 출고유형 1 개월 설계 및 기술 검토
  • 23.
    일반 주문 상태약 19개 O2O주문 모델링 - 주문 상태 접수구분 - 배정상태 - 수락결과 - 출고유형 내가 생각 했던 사용 기대 용어 배정접수, 배정확정, 수락요청 수락, 거절, 출고지시, 출고완료 ….
  • 24.
    공식 용어가 아닌코드 값으로 대화 10-10 10-20 20-10 30-10 30-20 20-99 .... 배정접수, 배정확정, 수락요청 수락, 거절, 출고지시, 출고완료 …. 사용 기대 용어 실제 사용 용어 X O2O주문 모델링 - 주문 상태 접수구분 - 배정상태 - 수락결과 - 출고유형
  • 25.
  • 26.
  • 27.
    생존을 위한 몸부림 A군담당 B군 담당(나) C군 담당 B군 담당(나) B군 담당(나)
  • 28.
    상태 코드의 비밀 A군담당 B군 담당(나) C군 담당 10 20 30 41 50 9030 31 40 상태전이 = 현재 상태 +1 or + 10
  • 29.
    상태도를 그리기 전까지... -상태 전이는 다음과 같았다. 상태전이 = 현재 상태 +1 or + 10 - 다음 4가지 값의 조합으로 주문상태를 식별 한다. 접수구분 - 배정상태 - 수락결과 - 출고유형 - 각 상태 변경시 entry action, guard 등이 다양하게 존재 - 각 개인이 이해하는 수준이 틀리고 일치 하지 않음 A군 B군 C군
  • 30.
    갱신! 갱신! 갱신!상태도 최종 완성 - 교환에 대한 상태도 추가 - 반품에 대한 상태도 추가 - 취소에 대한 상태도 추가
  • 31.
  • 32.
    껌기계를 만들어 보아요 매진(껌없음) 대기(동전없음)동전있음 껌배출 Choice 동전입력 [동전없음] 크랭크 돌림/껌내보냄 [남은껌있음] [남은껌 없음] 껌 꺼냄 껌채움 껌뽑기 기계
  • 33.
    코드가 직관적? 이고빠르게 결과를 낼수 있다… 단 변화에 취약 1안 : 닥 개발 if if if if if... huk huk huk public void insertQuarter() { if (state == HAS_QUARTER) { System.out.println("You can't insert another quarter"); } else if (state == NO_QUARTER) { state = HAS_QUARTER; System.out.println("You inserted a quarter"); } else if (state == SOLD_OUT) { System.out.println("You can't insert a quarter, the machine is sold out"); } else if (state == SOLD) { System.out.println("Please wait, we're already giving you a gumball"); } } public void turnCrank() { if (state == SOLD) { System.out.println("Turning twice doesn't get you another gumball!"); } else if (state == NO_QUARTER) { System.out.println("You turned but there's no quarter"); } else if (state == SOLD_OUT) { System.out.println("You turned, but there are no gumballs"); } else if (state == HAS_QUARTER) { System.out.println("You turned..."); state = SOLD;
  • 34.
    코드가 깔끔해지고 “Ifhell” 에서 벗어 날 수 있음 하지만 각 상태별로 의존성 제약이 생김 2안 : State Pattern 적용 public void insertQuarter() { state.insertQuarter(); } public void turnCrank() { state.turnCrank(); state.dispense(); } 깔금 깔금 class HasQuarterState implements State { ... } class HasQuarterState implements State { ... } 상태로직을 담은 상태 클래스
  • 35.
    State Pattern Context + request():void «interface» State + handle() :void 껌배출 껌매진 동전없음 동전있음 상태를 기반으로 하는 행동을 캡슐화 하고 현재 상태에 위임 한다. • 객체 내부 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있다. • OCP(Open Close Principal) 따른다. (상태를 무한 증가 시킬수 있음…) • 바뀌는 부분인 상태를 캡슐화 • 전략패턴과 비슷
  • 36.
    Gumball Class DiagramO2O 주문배정 Class Diagram O2O State Pattern 적용시...
  • 37.
  • 38.
    S2M Feature Easy touse flat one level state machine for simple use cases. Hierarchical state machine structure to ease complex state configuration. State machine regions to provide even more complex state configurations. Usage of triggers, transitions, guards and actions. Type safe configuration adapter. Builder pattern for easy instantiation for use outside of Spring Application context Recipes for usual use cases Distributed state machine based on a Zookeeper State machine event listeners. Spring IOC integration to associate beans with a state machine.
  • 39.
    S2M Example Locked Opened Closed Initial doClose[doorWay->IsWayEmpty] doLockdoUnlock doOpen create
  • 40.
    상태머신 요소(상태,이벤트, 가드)식별 static enum States { Opend, Closed, Locked } static enum Events { Open, Close, Lock, Unlock } @Bean static Guard<States, Events> isEmptyDoorWay() { return new Guard<States, Events>() { @Override public boolean evaluate(StateContext<States, Events> context) { return new Random().nextBoolean(); } }; } ➜ Enum Type 상태 정의 ➜ Enum Type 이벤트 정의 ➜ Guard 즉 상태 전이 가능 여부 평가
  • 41.
    상태머신 Configuration @Configuration @EnableStateMachine public staticclass Config1 extends EnumStateMachineConfigurerAdapter<States, Events> { @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.Closed) .states(EnumSet.allOf(States.class)); } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.Opend).target(States.Closed).event(Events.Close) .guard(isEmptyDoorWay()) .and() .withExternal() .source(States.Closed).target(States.Opend).event(Events.Open); } } ➜ 상태 등록 Config ➜ 상태 동작(이벤트, 가드) config
  • 42.
    S2M에서 제공하는 상태 States HierarchicalStates Distributed States Regions Transitions Guards Actions Pseudo States Initial State Terminate State History State Choice State Fork State Join State
  • 43.
    도입결과 1. 현격히 줄어즌코드량(IF문 제거) 2. 주문 상태 전이가 명확 해지고 좀비 상태가 줄어듬 정의하지 않은 상태전이가 일어나는 현상 3. 컴포넌트간 역할이 명확해짐 모든 상태 변경 처리는 S2M이 처리… but 일부분은 적용하지 못하고 프로젝트 종료 프로젝를 겸한 2개월 안정화 기간동안 문제가 발생한 곳이 주로 S2M을 적용하지 못한 곳
  • 44.
    마치며 1. 상태머신을 적용하기위해선 상태도를 반드시 그립니다. 2. S2M GitHub의 Example을 꼭 활용하세요 3. S2M 사용시 1.0.2 이상 사용하세요 @Scope 적용 가능 (Spring 4.2이에에서 @Scope을 사용하기 위해 약간의 수정 필요)
  • 45.
    4년전 이맘때쯤... Redmine일감 상태도 Before After 반복반복반복…
  • 46.