서 론
State Machine.
스테이트 머신은 흔히들 여러 이름으로 불린다.
State Machine (상태 기계), Finite State Machine (유한 상태 기계), Finite Automaton (유한 오토마톤), Finite Automata (유한 오토마타), 등등...
보통 스테이트 머신은 Finite, 유한 개의 스테이트로 구성된 Finite State Machine과,
Infinite, 무한 개의 스테이트로 구성되는 Infinite State Machine으로 나뉘지만,
Real World에서는 주로 Finite State Machine으로 프로덕트가 구성되고,
그렇기에 일반적으로 State Machine이라 함은 Finite State Machine을 통칭한다. (약칭 FSM)
스테이트 머신은 아주 여러 곳에서 쓰이지만,
주로 컴파일러, 임베디드, 게임 캐릭터(몬스터) AI에 자주 사용된다.
그래서 스테이트 머신에 관한 많은 코드들은 주로 C/C++로 예제가 구성되어 있다.
단, 이것을 타 language로 구성하지 말라는 법은 없다.
State Machine의 정의
유한 상태 기계는 컴퓨터 프로그램과 전자 논리 회로를 설계하는 데에 쓰이는 수학적 모델이며, 유한한 개수의 상태를 가질 수 있는 오토마타, 즉 추상 기계라고 할 수 있다.
이러한 기계는 한 번에 오로지 한 개의 상태만을 가지게 되며, 현재 상태(Current State)란 임의의 주어진 시간의 상태를 칭한다. 이러한 기계는 어떠한 사건(Event)에 의해 한 상태에서 다른 상태로 변화할 수 있으며, 이를 천이(Transition)라 한다.
특정한 유한 오토마톤은 현재 상태로부터 가능한 천이 상태와, 이러한 천이를 유발하는 조건들의 집합으로서 정의된다.
그러한데,
이런 것을 왜 쓸까?
얼핏 보기에는, 절차지향적인 프로그램에서 if-else 문을 타고 이벤트문을 처리하거나, switch-case 문을 타고 이벤트문을 처리해도 전혀 다를 게 없어 보인다.
구현하는 데에 시간만 더 걸리는 것을 대체 왜 쓸까?
(대체로 스테이트 머신은 라이브러리를 그대로 갖다 쓰기가 어렵다. 어차피 구조만 가져오고 내부 구현은 새로 쫙 다시 해야하는 경우가 부지기수이기 때문이다.)
그 이유는 '안정성' 때문이다.
수학적 모델은 그 정의 자체만으로도 안정성이 있다.
통제 가능한 변인을 제어하여 원하는 아웃풋을 내기 때문이다.
if-else문이나 switch-case문에서는 설계 자체에 결함이 없더라도,
구현 중에 변수 오염이라던가, 잘못된 플로우를 타게 할 경우의 수가 존재한다.
특히 여러 명이 협업할 때에,
멀티스레딩/멀티프로세싱 환경일 때에는 변수 오염의 문제가 더욱 심각해진다.
State Machine은 그러한 결함이 일어날 경우의 수가 현저히 낮아진다.
스테이트 머신의 예시
예를 들어,
Init State - Idle State - Processing State - Error State - Recovery State 순으로 5단계로 나뉘는 스테이트 머신을 설계하고 구현한다고 가정하자.
A) Init State에서 디바이스의 각종 이닛을 처리하고,
① 이닛 프로세스가 모두 종료되면
B) Idle State로 향하고 대기한다.
② 센서나 외부 입력이 감지되면
C) Processing State로 향하고 입력값에 대한 처리를 진행한다.
③ 입력값에 대한 처리가 끝났다면 이를 디스플레이에 표시하거나 서버에 전송하고
D) 다시 Idle State로 향한 후 대기한다.
④ 만약 Idle State나 Processing State에서 에러가 발생했다면, 에러 코드를 가지고서
E) Error State로 향하고 에러 코드를 분석한다.
⑤ 만약 에러 코드가 복구 가능한 에러라면,
F) Recovery State로 향해서 복구를 진행한다.
⑥ 복구가 끝이 났다면
G) Idle State로 다시 되돌아가서 대기한다.
위 A ~ G 까지의 플로우에서,
원 내에 있는 숫자로 표시된 볼드체의 문구는 모두 `이벤트(Event)` 이고,
알파벳으로 표시된 문구는 모두 `상태(State)` 이자 상태 내부에서 처리하는 방식이다.
위 스테이트 머신은 스테이트 머신 중 무어 모델에 속하며,
오직 진입 동작만을 사용하여 천이(transition)하는 모델을 말한다.
위 플로우를 보면 확실히 스테이트 머신만의 장점이 보인다.
1) 반드시 설계한대로만 플로우가 흐른다.
- 위 예시에서는 절대로 Init State에서 Processing State로 바로 갈 수가 없다. 고로 안정성이 높아진다.
- 또한 크리티컬 에러가 발생하여 디바이스나 서버가 죽을 시에는 개발자가 대충 어느 지점에서 크리티컬 에러가 발생했는지 로그를 보지 않고도 짐작이 가능하여 디버깅이 빨라진다.
2) 정해진 이벤트를 통해 상태가 천이된다.
- 설계자는 모든 플로우를 주관하여 설계할 수 있고, 구현자는 설계 문서만 보더라도 설계와 똑같이 구현이 가능해진다.
- 설계 자체도 단순해지며, 문서화에 걸리는 시간이 줄어든다.
- 멀티스레딩/멀티프로세싱 환경이나 기타 외란(disturbance)에도 오염이 되지 않으며 (미리 정해진 이벤트 규약이기에) 안정된 실행 환경을 보장한다.
3) 전체적인 흐름이 유기적이며 명확하다.
- 나중에 들어온 후임자가 보더라도 코드는 명확하고 한 눈에 들어온다.
- 설령 코드 구현체 자체가 복잡하더라도 설계 문서 한 장만 보더라도 곧바로 이해가 가능해진다.
이후 내용은 다음 편에서……
'IT > 이론' 카테고리의 다른 글
[프로세스 vs 스레드 - 2] 프로세스 & 멀티프로세싱 (6) | 2022.01.01 |
---|---|
[프로세스 vs 스레드 - 1] 프로세스와 스레드 (0) | 2021.12.28 |
[Git] 오픈소스 컨트리뷰트 중 Pull Request를 올리기 위해 깃 커밋 로그 깔끔하게 하기 (1) | 2020.08.13 |
[Git] Forked Repository가 꼬였을 때... (5 commits ahead of...) (0) | 2020.07.30 |
[이론] C의 qsort와 C++의 sort 중 누가 더 빠를까? (3) | 2020.07.19 |