FSM(Finite State Machine) ํŒจํ„ด with Go

[์›๋ณธ ๋งํฌ]

FSM(Finite State Machine:์œ ํ•œ ์ƒํƒœ ๋จธ์‹ )์€ ๋‹ค๊ฐ์ ์œผ๋กœ ๋ณ€ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ ๊ด€๋ฆฌ์— ์ดˆ์ ์„ ๋‘” ๊ตฌ์„ฑ ํŒจํ„ด์ด๋‹ค.

์ด๊ฒŒ ์–ด๋–จ ๋•Œ ํ•„์š”ํ• ๊นŒ?

๋ฐฐ์†ก์ƒํƒœ๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณด๊ฒ ๋‹ค.

๋ฐฐ์†ก์€ ์–ผํ• ๋ณด๋ฉด 1์ฐจ์›์ ์œผ๋กœ๋งŒ ์ง„ํ–‰๋˜๋Š” ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๊ทธ๋ ‡์ง€๋„ ์•Š๋‹ค.
"๊ตฌ๋งค ์™„๋ฃŒ"์—์„œ "๊ตฌ๋งค ์ทจ์†Œ"๋กœ ๊ฐˆ ์ˆ˜๋„ ์žˆ๊ณ ,
"๊ตฌ๋งค ์™„๋ฃŒ"์—์„œ "๋ฐฐ์†ก ์ค‘"์œผ๋กœ ๊ฐˆ ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ,
"๊ตฌ๋งค ์™„๋ฃŒ"์—์„œ "์ƒํ’ˆ ์ค€๋น„์ค‘"์œผ๋กœ ๊ฐˆ ์ˆ˜๋„ ์žˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  "์ƒํ’ˆ ์ค€๋น„์ค‘"์—์„œ "๊ตฌ๋งค ์ทจ์†Œ"๋กœ ๊ฐˆ ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์ด๋Ÿฐ ๋กœ์ง์€ ์ƒํƒœ๊ฐ€ ๋งŽ์•„์ง€๊ณ  ์ „์ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง์— ๋”ฐ๋ผ ๊ด€๋ฆฌํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ค์›Œ์ง€๊ณ , ์ŠคํŒŒ๊ฒŒํ‹ฐ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๊ธฐ๊ฐ€ ์‰ฝ๋‹ค.

FSM์€ ์ƒํƒœ ์ „์ด์— ๋Œ€ํ•œ ๋ณต์žกํ•œ if-else ๋–ก์น  ๋กœ์ง์„ ์•ฝ๊ฐ„ ์ถ”์ƒํ™”ํ•˜๊ณ , ์ด๋ฒคํŠธ ๋งคํ•‘์„ ํ†ตํ•ด ์ „์ด ๋กœ์ง์„ ์กฐ๊ธˆ ๋” ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ตฌํ˜„ ํŒจํ„ด์ด๋‹ค.




Go์—์„œ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

์ƒ๋‹น์ˆ˜์— ์–ธ์–ด๋“ค์—๋Š” FSM์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ•œ๋‹ค.
์—ฌ๊ธฐ์„œ๋Š” looplab์ด๋ผ๋Š” ๋ฐ์„œ ๋งŒ๋“ ๊ฑธ ์จ์„œ FSM์„ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ๋‹ค.

"open"๊ณผ "closed"์˜ 2๊ฐœ์˜ ์ƒํƒœ๊ฐ€ ์žˆ๊ณ , ์„œ๋กœ์„œ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋ผ๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.
๊ทธ๋Ÿผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒํƒœ๋จธ์‹ ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

package main

import (
	"context"
	"fmt"

	"github.com/looplab/fsm"
)

func main() {
	fsm := fsm.NewFSM(
		"closed", // ์ดˆ๊ธฐ ์ƒํƒœ
		fsm.Events{ // ์ด๋ฒคํŠธ ์ •์˜
			{Name: "open", Src: []string{"closed"}, Dst: "open"},  // ํ˜„์žฌ ์ƒํƒœ๊ฐ€ closed ์ผ๋•Œ open ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด open ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
			{Name: "close", Src: []string{"open"}, Dst: "closed"}, // ํ˜„์žฌ ์ƒํƒœ๊ฐ€ open ์ผ๋•Œ close ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด closed ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
		},
		fsm.Callbacks{},
	)

	fmt.Println(fsm.Current())

	// open ์ด๋ฒคํŠธ๋ฅผ ๋‚ ๋ฆผ
	err := fsm.Event(context.Background(), "open")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(fsm.Current())

	// close ์ด๋ฒคํŠธ๋ฅผ ๋‚ ๋ฆผ
	err = fsm.Event(context.Background(), "close")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(fsm.Current())
}

fsm.Event๋กœ ์ด๋ฒคํŠธ ๋ชฉ๋ก์„ ์ •์˜ํ•˜๋Š”๋ฐ, ๊ฐ ๋ฐฐ์—ด์˜ Name์€ ์ด๋ฒคํŠธ๋ช…์ด๊ณ , Src๋Š” ์‹œ์ž‘ ์ƒํƒœ, Dst๋Š” ๋„์ฐฉ ์ƒํƒœ๋‹ค.
"closed" ์ƒํƒœ์—์„œ "open"์ด๋ผ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด "open"์ด๋ผ๋Š” ์ƒํƒœ๋กœ ์ „์ด์‹œํ‚จ๋‹ค๋Š” ๋‚ด์šฉ์ด๋‹ค.

์ €๋Œ€๋กœ ์‹คํ–‰ํ•ด๋ณด๋ฉด

์‹œ๋Œ€ํ•œ๋Œ€๋กœ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค.




์ฝœ๋ฐฑ

๊ทธ๋ƒฅ ์ƒํƒœ๋งŒ ๋Œ๋ฆฌ๊ณ  ๋ง๊ฑฐ๋ผ๋ฉด ์ด๊ฑธ ์“ฐ๋Š” ์ด์œ ๊ฐ€ ์—†์„ ๊ฒƒ์ด๋‹ค.
Callback์„ ์ •์˜ํ•˜๋ฉด ๊ฐ ์ด๋ฒคํŠธ๊ฐ€ ํ˜ธ์ถœ๋ ๋•Œ๋งˆ๋‹ค ์‚ฌ์šฉํ•  ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋˜๋Š” ์ด๋ฒคํŠธ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ „์ด ์ƒํƒœ๋ฅผ ํ™•์ธํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์ด๋Ÿฐ ์‹์ด๋‹ค.
์ผ๋‹จ ๋กœ๊ทธ๋งŒ ์ฐ์–ด์ฃผ๋„๋ก ํ–ˆ๋‹ค.

package main

import (
	"context"
	"fmt"

	"github.com/looplab/fsm"
)

func main() {
	fsm := fsm.NewFSM(
		"closed", // ์ดˆ๊ธฐ ์ƒํƒœ
		fsm.Events{ // ์ด๋ฒคํŠธ ์ •์˜
			{Name: "open", Src: []string{"closed"}, Dst: "open"},  // ํ˜„์žฌ ์ƒํƒœ๊ฐ€ closed ์ผ๋•Œ open ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด open ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
			{Name: "close", Src: []string{"open"}, Dst: "closed"}, // ํ˜„์žฌ ์ƒํƒœ๊ฐ€ open ์ผ๋•Œ close ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด closed ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
		},
		fsm.Callbacks{
			"open": func(c context.Context, e *fsm.Event) {
				fmt.Println(e.Src + " -> " + e.Dst)
			},
			"close": func(c context.Context, e *fsm.Event) {
				fmt.Println(e.Src + " -> " + e.Dst)
			},
		},
	)

	// open ์ด๋ฒคํŠธ๋ฅผ ๋‚ ๋ฆผ
	err := fsm.Event(context.Background(), "open")
	if err != nil {
		fmt.Println(err)
	}

	// close ์ด๋ฒคํŠธ๋ฅผ ๋‚ ๋ฆผ
	err = fsm.Event(context.Background(), "close")
	if err != nil {
		fmt.Println(err)
	}
}

๊ทธ๋Ÿผ ์ž˜ ์ฐํž ๊ฒƒ์ด๋‹ค.




Graphbiz ๊ฐ€์‹œํ™”ํ•˜๊ธฐ

์ „์ด ์ƒํƒœ ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก ๋ถ„์„ํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์›Œ์ง„๋‹ค.
๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” graphbiz ๋“ฑ ๊ทธ๋ž˜ํ”„ ํ‘œํ˜„์— ๋Œ€ํ•œ ํ˜ธํ™˜์„ฑ์„ ์ œ๊ณตํ•ด์ค€๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด Visualize ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•œ ๋‹ค์Œ์—

์•„๋ž˜ ์‚ฌ์ดํŠธ์— ๋“ค์–ด๊ฐ€์„œ
https://dreampuf.github.io/GraphvizOnline/

๋ณต๋ถ™ํ•˜๋ฉด

์—ฌ๋Ÿฌ ํฌ๋งท์œผ๋กœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด๋ฉด์„œ ํŒŒ์•…์„ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.



์ฐธ์กฐ
https://pkg.go.dev/github.com/looplab/fsm@v1.0.1