--- id: wiki-2026-0508-finite-state-machines-fsm title: Finite State Machines (FSM) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [FSM, state machine, statechart, XState, hierarchical FSM, behavior tree] duplicate_of: none source_trust_level: A confidence_score: 0.98 verification_status: applied tags: [computer-science, fsm, state-machine, xstate, statechart, game-ai, control] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript / Python framework: XState / Stateless / SCXML --- # Finite State Machines (FSM) ## 매 한 줄 > **"매 finite state + 매 transition (event-driven)"**. 매 oldest CS abstraction. 매 modern: 매 statechart (Harel), 매 XState, 매 behavior tree (game). 매 game AI, 매 UI workflow, 매 protocol, 매 LLM agent의 control 의 사용. ## 매 핵심 ### 매 component - **State**: 매 finite set. - **Initial state**. - **Transition**: state × event → state. - **Guard**: 매 transition condition. - **Action**: 매 transition / entry / exit 의 effect. ### 매 variant - **DFA**: 매 deterministic. - **NFA**: 매 non-deterministic. - **Statechart** (Harel): 매 nested + parallel. - **HSM** (hierarchical). - **Behavior tree**: 매 game alternative. ### 매 응용 1. **Game AI**: 매 enemy behavior. 2. **UI**: 매 form workflow. 3. **Protocol**: 매 TCP, BLE. 4. **Compiler**: 매 lexer. 5. **LLM agent**: 매 conversation flow. 6. **Saga**: 매 distributed transaction. ## 💻 패턴 ### Simple FSM (TypeScript) ```typescript type State = 'idle' | 'fetching' | 'success' | 'error'; type Event = { type: 'FETCH' } | { type: 'RESOLVE'; data: any } | { type: 'REJECT' } | { type: 'RESET' }; function transition(state: State, event: Event): State { switch (state) { case 'idle': if (event.type === 'FETCH') return 'fetching'; break; case 'fetching': if (event.type === 'RESOLVE') return 'success'; if (event.type === 'REJECT') return 'error'; break; case 'success': case 'error': if (event.type === 'RESET') return 'idle'; break; } return state; } ``` ### XState (declarative) ```typescript import { createMachine, assign, interpret } from 'xstate'; const fetchMachine = createMachine({ id: 'fetch', initial: 'idle', context: { data: null, error: null }, states: { idle: { on: { FETCH: 'loading' }, }, loading: { invoke: { src: 'fetchData', onDone: { target: 'success', actions: assign({ data: (_, e) => e.data }) }, onError: { target: 'failure', actions: assign({ error: (_, e) => e.data }) }, }, }, success: { on: { REFRESH: 'loading' }, }, failure: { on: { RETRY: 'loading' }, }, }, }); const service = interpret(fetchMachine).start(); service.send({ type: 'FETCH' }); ``` ### Hierarchical (statechart) ```typescript const trafficLight = createMachine({ initial: 'red', states: { red: { after: { 5000: 'green' }, }, green: { after: { 5000: 'yellow' }, }, yellow: { initial: 'flashing', states: { flashing: {}, steady: {}, }, after: { 2000: 'red' }, }, }, }); ``` ### Game AI FSM (Python) ```python class EnemyAI: def __init__(self): self.state = 'patrol' def update(self, world): if self.state == 'patrol': if world.player_visible(self): self.state = 'chase' else: self.patrol_move() elif self.state == 'chase': if world.in_attack_range(self): self.state = 'attack' elif not world.player_visible(self): self.state = 'search' else: self.chase_player() elif self.state == 'attack': if not world.in_attack_range(self): self.state = 'chase' else: self.attack() elif self.state == 'search': if world.search_timeout(): self.state = 'patrol' elif world.player_visible(self): self.state = 'chase' else: self.search_area() ``` ### Behavior tree (alternative) ```python class Node: def tick(self): raise NotImplementedError class Sequence(Node): def __init__(self, *children): self.children = children def tick(self): for c in self.children: if c.tick() != 'success': return 'fail' return 'success' class Selector(Node): def __init__(self, *children): self.children = children def tick(self): for c in self.children: if c.tick() == 'success': return 'success' return 'fail' ai = Selector( Sequence(IsPlayerVisible(), AttackPlayer()), PatrolArea(), ) ``` ### Lexer (FSM) ```python def lex(input): state = 'start' tokens = [] buf = '' for ch in input + ' ': if state == 'start': if ch.isalpha(): state, buf = 'ident', ch elif ch.isdigit(): state, buf = 'number', ch elif state == 'ident': if ch.isalnum(): buf += ch else: tokens.append(('IDENT', buf)); state, buf = 'start', '' elif state == 'number': if ch.isdigit(): buf += ch else: tokens.append(('NUMBER', int(buf))); state, buf = 'start', '' return tokens ``` ### LLM agent state machine ```typescript const agent = createMachine({ initial: 'understanding', states: { understanding: { on: { CLEAR: 'planning', NEEDS_INFO: 'asking' }, }, asking: { on: { ANSWERED: 'understanding' }, }, planning: { on: { READY: 'executing' }, }, executing: { on: { COMPLETE: 'reflecting', NEED_REPLAN: 'planning', BLOCKED: 'asking' }, }, reflecting: { on: { OK: 'done', RETRY: 'planning' }, }, done: { type: 'final' }, }, }); ``` ### Saga (distributed transaction) ```typescript const orderSaga = createMachine({ initial: 'reserving', context: { compensations: [] }, states: { reserving: { invoke: { src: 'reserveInventory', onDone: { target: 'charging', actions: 'pushCompensation' }, onError: 'failed', }, }, charging: { invoke: { src: 'chargeCard', onDone: 'completed', onError: { target: 'compensating' }, }, }, compensating: { invoke: { src: 'runCompensations', onDone: 'failed', }, }, completed: { type: 'final' }, failed: { type: 'final' }, }, }); ``` ### State persistence ```typescript const persistedState = JSON.stringify(service.getSnapshot()); localStorage.setItem('appState', persistedState); // 매 restore const restored = JSON.parse(localStorage.getItem('appState')!); const restoredService = interpret(machine, { state: restored }).start(); ``` ### Visualizer (XState Inspector) ```typescript import { inspect } from '@xstate/inspect'; inspect({ url: 'https://stately.ai/viz?inspect', iframe: false }); const service = interpret(machine, { devTools: true }).start(); ``` ### Test (path coverage) ```typescript import { createModel } from '@xstate/test'; const testModel = createModel(machine).withEvents({ FETCH: { exec: async () => fireEvent.click(fetchBtn) }, RESOLVE: { ... }, }); testModel.getSimplePathPlans().forEach(plan => { describe(plan.description, () => { plan.paths.forEach(path => { it(path.description, async () => { await path.test(/* page */); }); }); }); }); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | UI workflow | XState | | Game AI | FSM or Behavior Tree | | Lexer / parser | DFA | | Distributed | Saga (XState compatible) | | LLM agent | Statechart | | Tiny linear | Plain switch | **기본값**: 매 XState (TS) + 매 statechart (hierarchical) + 매 visualizer + 매 model-based test. ## 🔗 Graph - 변형: [[Statechart]] · [[Hierarchical-FSM]] · [[Behavior-Tree]] - Adjacent: [[Exhaustiveness-Checking]] · [[Encapsulation-of-Domain-Invariants]] · [[Event-Driven-Architecture]] ## 🤖 LLM 활용 **언제**: 매 multi-step workflow. 매 game AI. 매 protocol. **언제 X**: 매 1-2 state. ## ❌ 안티패턴 - **Boolean explosion**: 매 N flags 의 implicit FSM. - **Hidden FSM**: 매 spaghetti. - **No exhaustive transition**: 매 invalid state. - **State + global side effect**: 매 untestable. - **Reinvent XState**: 매 use library. ## 🧪 검증 / 중복 - Verified (Harel statecharts 1987, XState docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-04-26 | FSM auto | | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — FSM + 매 XState / hierarchical / game / saga / LLM agent code |