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/
๋ณต๋ถํ๋ฉด

์ฌ๋ฌ ํฌ๋งท์ผ๋ก ๋ค์ด์ด๊ทธ๋จ์ ๋ณด๋ฉด์ ํ์
์ ํด๋ณผ ์ ์๋ค.