Published on

๐Ÿคฟ ์ˆจ ๊พน ์ฐธ๊ณ  ๋”ฅ๋‹ค์ด๋ธŒ 2 : ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ (Finite State Machine)

Authors
  • avatar
    Name
    Woojin Son
    Twitter

๐Ÿคฟ ์ˆจ ๊พน ์ฐธ๊ณ  ๋”ฅ๋‹ค์ด๋ธŒ 2 : ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ (Finite State Machine)

๐ŸคŸ Intro

๋‹ค์‹œ ๋Œ์•„์™”์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒํ•˜์ฐจ ํ•˜๊ณ  ์žˆ๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์žก๋ถ€ ์†์šฐ์ง„์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ (Finite State Machine) ๋ผ๋Š” ๊ฐœ๋…์„ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

Computer Science, ํ”ํžˆ ๋งํ•˜๋Š” ์ปดํ“จํ„ฐ๊ณตํ•™์„ ์ „๊ณต ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” ์˜คํ† ๋งˆํƒ€ ํ˜น์€ ์ปดํŒŒ์ผ๋Ÿฌ ์ด๋ก  ์‹œ๊ฐ„์— ๋“ค์–ด๋ณด์…จ์„์ง€๋„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ „๊ณต์‹œ๊ฐ„์— ์ฐธ ์žฌ๋ฐŒ๊ฒŒ ๋“ค์—ˆ์ง€๋งŒ ๋ง‰์ƒ ๋‹น์‹œ์˜ ์ง€์‹์œผ๋กœ ๋ชจ๋“  ๊ฑธ ์ดํ•ดํ•˜๊ธด ์–ด๋ ค์› ๋˜ ๊ธฐ์–ต์ด ๋‚ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ ์— ๋Œ€ํ•œ ์ปดํ“จํ„ฐ๊ณตํ•™ ์ด๋ก  ์ด์•ผ๊ธฐ์™€ ์‹ค๋ฌด ์‚ฌ๋ก€์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ•ด ๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž ์™ธ์—๋„ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์žฌ๋ฐŒ๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ตœ๋Œ€ํ•œ ์ฝ”๋“œ๋Š” ๋ฐฐ์ œํ•˜๊ณ  ์ž‘์„ฑ ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. (๋ง ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ์ง€์ผœ์ง€์ง„ ์•Š์ง€๋งŒ...๋…ธ๋ ฅ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๐ŸคŸ)

๊ทธ๋Ÿผ ํ•œ๋ฒˆ ์ˆจ ๊พน ์ฐธ๊ณ  ๋”ฅ ๋‹ค์ด๋ธŒ! ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


๐Ÿง ์ƒํƒœ (State) ์— ๋Œ€ํ•œ ์ดํ•ด

์šฐ์„  ์ƒํƒœ ๋ผ๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ํ•œ๋ฒˆ ์งš๊ณ  ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ๋ผ๋Š” ๋‹จ์–ด์˜ ์‚ฌ์ „์  ์ •์˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ƒํƒœ, ๏งบๆ…‹ ์‚ฌ๋ฌผยทํ˜„์ƒ์ด ์ฒ˜ํ•ด ์žˆ๋Š” ํ˜•ํŽธ์ด๋‚˜ ๋ชจ์–‘.

์†Œํ”„ํŠธ์›จ์–ด์˜ ์„ธ๊ณ„์—์„œ ์‚ฌ๋ฌผ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑด ์ž‘์€ ๋ฐ์ดํ„ฐ ๋‹จ์œ„ ํ•˜๋‚˜ ๋ถ€ํ„ฐ ํ”ํžˆ ๊ฐ์ฒด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ์˜๋ฏธ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๋ฉ์–ด๋ฆฌ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํžˆ ์‹ ํ˜ธ๋“ฑ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ƒ๊ฐ ํ•ด ๋ด๋„ ์‹ ํ˜ธ๊ฐ€ 3 ๊ฐœ์˜ ์ƒํƒœ๋กœ ๋‚˜๋‰˜์ฃ . ๋นจ๊ฐ„๋“ฑ, ๋…ธ๋ž€๋“ฑ, ์ดˆ๋ก๋“ฑ ์ด๋ ‡๊ฒŒ์š”.

๐Ÿง : ์•„๋‹ˆ ์šฐ๋ฆฌ๋Š” ์‹ ํ˜ธ๋“ฑ์— ์ƒํƒœ๊ฐ€ 4๊ฐœ๊ฐ€ ์žˆ๋Š”๋ฐ์š”?
๐Ÿ˜Š : ...๐Ÿ‘Š๐Ÿ‘Š

์ƒํƒœ๋Š” ๊ณ„์†ํ•ด์„œ ๋ณ€ํ•˜๋Š” ํŠน์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ทธ๊ฑด ์ƒํƒœ๋ผ๊ณ  ๋ถ€๋ฅด์ง€ ์•Š์•„์š”. ์ƒ์ˆ˜ (Constant) ๋ผ๊ณ  ๋ถ€๋ฅด์ฃ . ์‹ ํ˜ธ๋“ฑ์ด ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๋ผ๋ฉด ์‹ ํ˜ธ๋“ฑ์œผ๋กœ์จ ์˜๋ฏธ๊ฐ€ ์—†์ฃ .


๐Ÿง  ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ (Finite State Machine) ์— ๋Œ€ํ•œ ์ดํ•ด

์ƒํƒœ๋Š” ๊ณ ์ • ๋˜์–ด์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณ€ํ•˜๋Š” ํŠน์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ, ์ด ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜๋Š” ์กฐ๊ฑด์„ ํ”ํžˆ ๋งํ•ด์„œ ์ด๋ฒคํŠธ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿฅณ : ๋ญ์š” ์ด๋ฒคํŠธ? ์ง‘ ์•ž์— ์‹์ž์žฌ ๋งˆํŠธ์—์„œ ํƒ€์ž„ ์„ธ์ผ์„ ํ•œ๋‹ค๊ตฌ์š”?
๐Ÿ˜Š : ๐Ÿ‘Š... ๊ทธ๋ž˜์š” ๊ทธ๊ฒƒ๋„ ๋งž๊ธด ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์กฐ๊ฑด์— ์˜ํ•ด ์ œ ์ง€๊ฐ‘์ด ์—ด๋ฆฌ๋‹ˆ๊นŒ์š”.

์‹ ํ˜ธ๋“ฑ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜๋Š” ์กฐ๊ฑด์œผ๋กœ๋Š” ์‹œ๊ฐ„์ด ์žˆ๊ฒ ์ฃ . ์ „์ฒด ์‚ฌ์ดํด์„ ๋งŒ์•ฝ 5๋ถ„์œผ๋กœ ์žก๋Š”๋‹ค๋ฉด, ๊ทธ ์ค‘ 3๋ถ„ 55์ดˆ๋Š” ๋นจ๊ฐ„๋ถˆ, 5์ดˆ๊ฐ„ ๋…ธ๋ž€๋ถˆ, 1๋ถ„๊ฐ„ ์ดˆ๋ก๋ถˆ์„ ํ• ๋‹นํ•˜๋Š” ์‹์œผ๋กœ ์กฐ๊ฑด์„ ๊ฑธ๊ฑฐ์—์š”. 5๋ถ„์ด ๋๋‚˜๋ฉด ๋‹ค์‹œ ์‚ฌ์ดํด์„ ๋Œ๋ฆด๊ฒ๋‹ˆ๋‹ค.

์ƒํƒœ๊ฐ€ ์ด๋ฒคํŠธ์— ๋”ฐ๋ผ ๋ณ€ํ•˜๋Š” ๊ฒƒ์„ ์ƒํƒœ์˜ ์ „์ด (Transition) ๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ค ์ƒํƒœ๋กœ ๋ฐ”๋€Œ์–ด์•ผ ํ•˜๋Š” ์ง€์— ๋Œ€ํ•œ ์ˆ˜ํ•™์  ๋ชจ๋ธ์„ '์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„ (Finite State Machine)' ๋˜๋Š” '์œ ํ•œ ์˜คํ† ๋งˆํ†ค (Finite Automaton)' ์ด ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜ํ•™์ด๋ผ๋Š” ๋ง์ด ๋‚˜์™€์„œ ํ ์นซ ํ•˜์‹  ๋ถ„๋“ค์€ ๊ฑฑ์ • ๋งˆ์„ธ์š”. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ ์ˆซ์ž๋Š” ์ตœ๋Œ€ํ•œ ๋ฐฐ์ œ ํ• ํ…Œ๋‹ˆ๊นŒ์š”.

์˜คํ† ๋งˆํƒ€ ๋ผ๋Š” ๋‹จ์–ด๋Š” '์ •ํ•ด์ง„ ๊ทœ์น™์— ๋”ฐ๋ผ ์ž…๋ ฅ์„ ์ฝ์œผ๋ฉด์„œ ๋‚ด๋ถ€์˜ ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๊ณ„์˜ ์ˆ˜ํ•™์  ๋ชจ๋ธ' ์ด๋ผ๋Š” ์˜๋ฏธ๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. '์ž๋™'์„ ์˜๋ฏธํ•˜๋Š” ๊ทธ๋ฆฌ์Šค์–ด 'ฮฑแฝฯ„ฯŒฮผฮฑฯ„ฮฑ'์—์„œ ์œ ๋ž˜ํ–ˆ์ฃ .


๐Ÿงฉ ํ•ต์‹ฌ ์š”์†Œ

FSM์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค:

  • State (์ƒํƒœ) : ์‹œ์Šคํ…œ์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ์œ ํ•œํ•œ ์ƒํƒœ๋“ค
  • Event/Input (์ž…๋ ฅ) : ์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๋Š” ์ž๊ทน
  • Transition (์ „์ด) : ์ž…๋ ฅ์— ๋”ฐ๋ผ ์ƒํƒœ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ •์˜
  • Initial State (์ดˆ๊ธฐ ์ƒํƒœ)
  • Final/Accepting State (์ข…๋ฃŒ ์ƒํƒœ) (์˜ต์…˜)
์˜ˆ: ์ปคํ”ผ ์ฃผ๋ฌธ FSM
[์ฃผ๋ฌธ๋Œ€๊ธฐ] --์ฃผ๋ฌธโ†’ [์ œ์กฐ์ค‘] --์™„๋ฃŒโ†’ [ํ”ฝ์—…๋Œ€๊ธฐ] --์ˆ˜๋ นโ†’ [์™„๋ฃŒ]

๐ŸŽฏ ์™œ ์œ ํ•œํ•ด์•ผ ํ• ๊นŒ์š”??

์ƒํƒœ๊ฐ€ ๋ฌดํ•œํ•˜๋‹ค๋ฉด ์‹œ์Šคํ…œ์€ ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค. ์„ค๊ณ„์™€ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์›Œ์ง€๊ณ , ๋™์ž‘์„ ์ฆ๋ช…ํ•˜๊ธฐ๋„ ์–ด๋ ต์ฃ . ์‹ ํ˜ธ๋“ฑ์˜ ์ƒํƒœ๊ฐ€ ๋ฌดํ•œ์ด๋ผ๊ณ  ์ƒ๊ฐ ํ•ด ๋ณผ๊นŒ์š”?

๐Ÿš• : ์ €๊ธฐ์š” ์•„์ €์”จ! ๋นจ๋ฆฌ๋นจ๋ฆฌ ์•ˆ๊ฐ€์š”?
๐Ÿš— : ์•„๋‹ˆ #^#%& ์•ผ ์‹ ํ˜ธ๋“ฑ์ด ์ด์ƒํ•˜๋‹ค๊ณ !

์œ ํ•œ ์ƒํƒœ๋ž€ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์™„์ „ํžˆ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ ์ง‘ํ•ฉ์ด๋ผ๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

๐Ÿ“š ์ปดํ“จํ„ฐ๊ณตํ•™ ์ด๋ก  ์ด์•ผ๊ธฐ : ์˜คํ† ๋งˆํƒ€ ์ด๋ก ๊ณผ ํŠœ๋ง ๋จธ์‹ 

๐Ÿ“ ๊ฐœ์š”

์—ฌ๋‹ด์ด์ง€๋งŒ ์ปด๊ณต์ด ์ฝ”๋”ฉ ๋ฐฐ์šฐ๋Š” ๊ณณ์ด๋ผ ์˜คํ•ดํ•˜์‹œ๋Š” ๋ถ„๋“ค์ด ๊ฐ€๋” ์žˆ์œผ์‹ญ๋‹ˆ๋‹ค...

๋ฌผ๋ก  ๊ณผ ํŠน์„ฑ์ƒ ์ฝ”๋”ฉ์„ ๋งŽ์ด ํ•˜๊ธด ํ•˜์ฃ . ๊ฐœ์ธ์ ์œผ๋ก  ์ฝ”๋”ฉ์„ ๋Œ€ํ•™์—์„œ ์ง์ ‘์ ์œผ๋กœ ๋ฐฐ์šด ๊ฑด 1ํ•™๋…„๋•Œ๊ฐ€ ๋งˆ์ง€๋ง‰์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
2ํ•™๋…„ ๋•Œ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ˆ˜์—…์„ ๋‹ค์‹œ ๋˜์งš์–ด๋ณด๋ฉด Java ๋ผ๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์–ธ์–ด์˜ ์ฒ ํ•™์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ๋” ํ•˜์…จ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์˜› ๊ธฐ์–ต์€ ๋’ค๋Šฆ๊ฒŒ ๋– ์˜ฌ๋ ค๋ณด๋ฉด ๊ทธ๋•Œ ์™œ ๊ต์ˆ˜๋‹˜์ด ๊ทธ๋ ‡๊ฒŒ ์„ค๋ช…ํ–ˆ๋Š” ์ง€ ์ดํ•ด๊ฐ€ ๊ฐ€๊ณค ํ•˜์ฃ ...

์ œ์ž„์Šค ๊ณ ์Šฌ๋ง ๋น ๋Œ์ด์—ˆ๋˜ ๊ต์ˆ˜๋‹˜๊ป˜์„œ ๋ง์”€ํ•˜์…จ์ฃ .
๐Ÿ‘จโ€๐Ÿฆฒ : ์–ธ์  ๊ฐ€ ์ด ์•„์ €์”จ์ฒ˜๋Ÿผ ๋˜๊ณ ์‹ถ์€ ๋‚ ์ด ์˜ฌ๊ฒ๋‹ˆ๋‹ค ํ—ˆํ—ˆ...

์ปดํ“จํ„ฐ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ฒŒ์ž„ ํ•  ๋•Œ๋„ ์“ฐ์ง€๋งŒ ๋ณธ์งˆ์ ์ธ ๋ชฉ์ ์€ '๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ ์œ„ํ•œ ๋„๊ตฌ' ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋Ÿฐ ์งˆ๋ฌธ์„ ๋˜์งˆ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์š”.

"์ปดํ“จํ„ฐ๋Š” ๋„๋Œ€์ฒด ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ’€ ์ˆ˜ ์žˆ์„๊นŒ?"
"๋˜ ์–ด๋–ค ๋ฌธ์ œ๋Š” ์ปดํ“จํ„ฐ๋กœ๋Š” ์ ˆ๋Œ€ ๋ชป ํ‘ธ๋Š” ๊ฑธ๊นŒ?"

์ด๋Ÿฐ ์งˆ๋ฌธ์— ๋‹ตํ•˜๋ ค๋Š” ๋ถ„์•ผ๊ฐ€ '์˜คํ† ๋งˆํƒ€ ์ด๋ก (Automata Theory)' ์ด๊ณ  ์ด๋ฅผ ํฌํ•จํ•œ ๋” ํฐ ํ‹€์ด '๊ณ„์‚ฐ ์ด๋ก  (Theory of Computation)' ์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์šฐ๋ฆฌ๊ฐ€ ์ปดํ“จํ„ฐ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์—, โ€œ์ด๊ฒŒ ์ปดํ“จํ„ฐ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ์ธ์ง€๋ถ€ํ„ฐ ๋”ฐ์ ธ๋ณด์žโ€ ๋ผ๋Š” ์งˆ๋ฌธ์„ ๋˜์ง€๋Š” ์•„์ฃผ ๊ธฐ์ดˆ์ ์ด๊ณ ๋„ ์ค‘์š”ํ•œ ๋ถ„์•ผ์ธ ๊ฑฐ์ฃ .


๐Ÿ—๏ธ ์˜คํ† ๋งˆํƒ€ ์ด๋ก  (Automata Theory) ์†Œ๊ฐœ

์ข€ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณผ๊ฒŒ์š”. ์˜คํ† ๋งˆํƒ€ ์ด๋ก ์€ ๊ณ„์‚ฐ ๋Šฅ๋ ฅ์ด ์žˆ๋Š” ๊ธฐ๊ณ„์™€ ๊ทธ ๊ธฐ๊ณ„๋ฅผ ์ด์šฉํ•ด์„œ ํ’€ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์—ฐ๊ตฌํ•˜๋Š” ๋ถ„์•ผ์—์š”. ๋„ˆ๋ฌด ๊ธธ๊ฒŒ ์ด์•ผ๊ธฐํ•˜๋ฉด ์žฌ๋ฏธ์—†์œผ๋‹ˆ๊นŒ ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜์ž๋ฉด, โ€˜๊ธฐ๊ณ„๊ฐ€ ์–ด๋–ค ๋ฌธ์ œ๊นŒ์ง€ ํ’€ ์ˆ˜ ์žˆ์„๊นŒ?โ€™๋ฅผ ๋”ฐ์ง€๋Š” ํ•™๋ฌธ์ด์—์š”.

์˜คํ† ๋งˆํƒ€๋Š” ์ด์‚ฐ ์‹œ๊ฐ„ ๋™์•ˆ ์ฃผ์–ด์ง„ ์ž…๋ ฅ์— ์˜์กดํ•ด ์ž‘๋™ํ•˜๋Š” ์ˆ˜ํ•™์ ์ธ ๊ธฐ๊ณ„๋ผ๊ณ  ์œ„ํ‚ค๋ฐฑ๊ณผ์— ์„ค๋ช… ๋˜์–ด์žˆ์–ด์š”. ์—ฌ๊ธฐ์„œ ์ด์‚ฐ ์ด๋ผ๋Š” ๊ฐœ๋…์ด ์ค‘์š”ํ•œ๋ฐ, ์ด์‚ฐ ์€ ์—ฐ์†์ ์ด์ง€ ์•Š๊ณ  ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค๋Š” ์˜๋ฏธ์—์š”.

์šฐ๋ฆฌ ์ฃผ์ œ๊ฐ€ ๋”ฅ๋‹ค์ด๋ธŒ๋‹ˆ ์ข€ ๋” ๊นŠ๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ๋„˜์–ด๊ฐ€์ž๋ฉด, ์•„๋‚ ๋กœ๊ทธ์™€ ๋””์ง€ํ„ธ์˜ ๊ด€๊ณ„๋ฅผ ์ƒ๊ฐ ํ•ด ๋ณด๋ฉด ์ดํ•ดํ•˜๊ธฐ ์ข€ ๋” ์ˆ˜์›” ํ•ฉ๋‹ˆ๋‹ค.

์•„๋‚ ๋กœ๊ทธ๋Š” ์—ฐ์†์ ์ธ ๊ฐ’์„ ๊ฐ€์ง€์ฃ . ๋‹น์žฅ ์ฑ…์ƒ ์œ„์— ์žˆ๋Š” ์•„๋‚ ๋กœ๊ทธ ์‹œ๊ณ„์˜ ์ดˆ์นจ์€ ๋งค๋„๋Ÿฝ๊ฒŒ ์›€์ง์ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ค‘๊ฐ„ ๊ฐ’์€ ๋ฌดํ•œํžˆ ์กด์žฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ๋ž˜์„œ 3์ดˆ์™€ 4์ดˆ ์‚ฌ์ด ์–ด๋”˜๊ฐ€ ๋ผ๋Š” ์• ๋งคํ•œ ์ˆœ๊ฐ„๋„ ์กด์žฌํ•  ์ˆ˜ ์žˆ์ฃ .

ํ•˜์ง€๋งŒ ๋””์ง€ํ„ธ์˜ ์„ธ๊ณ„๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. 3์ดˆ, 4์ดˆ ๊ฐ๊ฐ์˜ ๋‹จ๊ณ„์˜ ๊ฐ’๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— ๊ฐ’์ด ๋ฌดํ•œํžˆ ์กด์žฌํ•˜๋ ค๋ฉด ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, 3.333333333์ดˆ ๋“ฑ ์‹ค์ˆ˜์˜ ๋ฒ”์œ„์—์„œ ๋Š์–ด์งˆ ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

์ปดํ“จํ„ฐ์—์„œ ๋ณด๋Š” ๋ชจ๋“  ๊ฐ’๋“ค ํ•˜๋‚˜ํ•˜๋‚˜๋Š” ์ด์‚ฐ์ ์ธ ์ •๋ณด์ž…๋‹ˆ๋‹ค. ํ‚ค๋ณด๋“œ ์ž…๋ ฅ๋ถ€ํ„ฐ ์‹ค์ˆ˜ ๊ฐ’ ํ•˜๋‚˜ ๊นŒ์ง€๋„ ๋ง์ด์ฃ . ์ปดํ“จํ„ฐ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹์ด ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ์— ์‹ค์ˆ˜ ๊ฐ’ ํ•˜๋‚˜๋ฅผ ์ €์žฅํ•œ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด ๋ณด๋ฉด ๊ฒฐ๊ตญ ์–ด๋А ์ •๋„ ์‹œ์ ์—์„œ ๋Š์–ด์งˆ ์ˆ˜ ๋ฐ–์— ์—†์–ด์š”. ์™œ๋ƒ๋ฉด ์ปดํ“จํ„ฐ์˜ ํ•˜๋“œ์›จ์–ด์˜ ์šฉ๋Ÿ‰์€ ํ•œ๊ณ„๊ฐ€ ์žˆ์œผ๋‹ˆ๊นŒ์š”.

์˜คํ† ๋งˆํƒ€๋Š” ๋‹จ๊ณ„๋ณ„๋กœ ํ•˜๋‚˜์˜ ์ž…๋ ฅ์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํ•œ ์ž…๋ ฅ์„ ๋ฐ›๊ณ  ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๊ณ , ๋‹ค์‹œ ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๋“ฑ ๋Š์–ด์ง„ ์‹œ๊ฐ„ ์†์—์„œ ์›€์ง์ด๋Š” ๊ธฐ๊ณ„์ž…๋‹ˆ๋‹ค. ๋ญ”๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋Š” ์†Œํ”„ํŠธ์›จ์–ด์™€ ๋А๋‚Œ์ด ๋น„์Šทํ•˜์ฃ ?


๐Ÿงฎ ๊ณ„์‚ฐ ์ด๋ก  (Theory of Computation)

๊ณ„์‚ฐ ์ด๋ก ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ์ค„๊ธฐ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

๋ถ„์•ผ๋‹ค๋ฃจ๋Š” ์งˆ๋ฌธ์˜ˆ์‹œ
์˜คํ† ๋งˆํƒ€ ์ด๋ก ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ’€ ์ˆ˜ ์žˆ๋Š”๊ฐ€?์ •๊ทœ ํ‘œํ˜„์‹, ์ƒํƒœ ๊ธฐ๊ณ„
๊ณ„์‚ฐ ๊ฐ€๋Šฅ์„ฑ ์ด๋ก ์–ด๋–ค ๋ฌธ์ œ๋Š” ์ ˆ๋Œ€๋กœ ๋ชป ํ‘ธ๋Š”๊ฐ€?์ •์ง€ ๋ฌธ์ œ (Halting Problem)
๋ณต์žก๋„ ์ด๋ก ์–ผ๋งˆ๋‚˜ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ํ’€ ์ˆ˜ ์žˆ๋Š”๊ฐ€?NP ๋ฌธ์ œ, ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ฑ๋Šฅ ๋ถ„์„

๊ณ„์‚ฐ ์ด๋ก ์€ ๋‹จ์ˆœํžˆ '๋ฌธ์ œ๋ฅผ ํ‘ผ๋‹ค' ์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ํ’€ ์ˆ˜ ์žˆ๋Š”์ง€, ์–ผ๋งˆ๋‚˜ ๋น ๋ฅด๊ฒŒ ํ’€ ์ˆ˜ ์žˆ๋Š” ์ง€, ์•„์˜ˆ ๋ชป ํ‘ธ๋Š” ๊ฑด ์—†๋Š”์ง€ ๊นŒ์ง€ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.


๐Ÿ–จ๏ธ ํŠœ๋ง ๋จธ์‹  (Turing Machine)

์ปด๊ณต ์ „๊ณต์ž๋ผ๋ฉด ํ•œ ๋ฒˆ์ฏค์€ ๋“ค์—ˆ์„ ์ด๋ฆ„์ด์ฃ . ์ปดํ“จํ„ฐ์˜ ์•„๋ฒ„์ง€๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์•จ๋Ÿฐ ํŠœ๋ง(Alan Turing)์ด 1930๋…„๋Œ€์— ๊ณ ์•ˆํ•œ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ง€๊ธˆ ์“ฐ๋Š” ์ปดํ“จํ„ฐ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ, ํŠœ๋ง ๋จธ์‹ ์€ ํ˜„์‹ค์— ์กด์žฌํ•˜๋Š” ๊ธฐ๊ณ„๋Š” ์•„๋‹ˆ์—์š”. ํ•˜์ง€๋งŒ ์ด๋ก ์ ์œผ๋กœ๋Š” ๋ชจ๋“  ์ปดํ“จํ„ฐ๊ฐ€ ํŠœ๋ง ๋จธ์‹ ์„ ํ‰๋‚ด ๋‚ด๊ณ  ์žˆ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠœ๋ง ๋จธ์‹ ์€ ๊ต‰์žฅํžˆ ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ ์ด ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ๋งŒ์œผ๋กœ๋„ ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋žจ์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠœ๋ง ๋จธ์‹ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์„ฑ์š”์†Œ๋กœ ๋˜์–ด ์žˆ์–ด์š”:

  • ๋ฌดํ•œํžˆ ๊ธด ํ…Œ์ดํ”„: ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด์—์š”. ๋ฉ”๋ชจ๋ฆฌ ๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ—ค๋“œ(Head): ํ…Œ์ดํ”„ ์œ„๋ฅผ ์™”๋‹ค๊ฐ”๋‹ค ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์”๋‹ˆ๋‹ค.
  • ์ƒํƒœ(State): ํ˜„์žฌ ํŠœ๋ง ๋จธ์‹ ์ด ์–ด๋–ค โ€˜๋ชจ๋“œโ€™์— ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์˜คํ† ๋งˆํƒ€์—์„œ ๋ฐฐ์šด ์ƒํƒœ์™€ ๊ฐ™์€ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค.
  • ์ „์ด ํ•จ์ˆ˜: ํ˜„์žฌ ์ƒํƒœ์™€ ํ…Œ์ดํ”„์˜ ๋ฌธ์ž๋ฅผ ๋ณด๊ณ , ๋ฌด์—‡์„ ์“ธ์ง€, ์–ด๋””๋กœ ์›€์ง์ผ์ง€, ์–ด๋–ค ์ƒํƒœ๋กœ ์ „์ดํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ทœ์น™์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์•„์ฃผ ๋‹จ์ˆœํ•œ ํŠœ๋ง ๋จธ์‹  ํ•˜๋‚˜๋ฅผ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: โ€˜0โ€™์ด ์—ฐ์†์œผ๋กœ ์ฃผ์–ด์ง„ ํ…Œ์ดํ”„์—์„œ, ์ฒ˜์Œ์œผ๋กœ ๋‚˜์˜ค๋Š” โ€˜1โ€™์„ ๋งŒ๋‚˜๋ฉด ์ž‘์—…์„ ๋ฉˆ์ถ”๋Š” ๊ธฐ๊ณ„. ์ด๊ฑด ์•„์ฃผ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ๊ทธ ๋™์ž‘์„ ์ƒํƒœ์™€ ์ž…๋ ฅ, ์ถœ๋ ฅ, ์ด๋™์œผ๋กœ ์ชผ๊ฐœ๋ณด๋ฉด ํŠœ๋ง ๋จธ์‹ ์˜ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.

์ด๋Ÿฐ ๋ฐฉ์‹์€ ์‚ฌ์‹ค ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ(while loop)์ด๋‚˜ ์กฐ๊ฑด๋ฌธ(if-else)๊ณผ ๊ต‰์žฅํžˆ ๋‹ฎ์•„ ์žˆ์–ด์š”. ์ปดํŒŒ์ผ๋Ÿฌ ์ž…์žฅ์—์„œ ๋ณด๋ฉด, ํŠœ๋ง ๋จธ์‹ ์€ ์•„์ฃผ ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ์ง€๋งŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ๋…ผ๋ฆฌ ํ๋ฆ„์„ ๊ฑฐ์˜ ๋ชจ๋‘ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด์ฃ .


โ“ ๊ทธ๋Ÿผ ์ด๊ฑธ ์™œ ๋งŒ๋“ค์—ˆ์„๊นŒ?

ํŠœ๋ง์€ โ€˜๋ฌด์Šจ ์ผ์ด๋“  ์ž๋™์œผ๋กœ ๊ณ„์‚ฐํ•˜๋Š” ๊ธฐ๊ณ„โ€™๋ฅผ ์ƒ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๊ธฐ๊ณ„๊ฐ€ ํ’€ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์˜ ํ•œ๊ณ„๊ฐ€ ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ(computable problem) ์˜ ์ •์˜๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ฃ .

๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ๊ธฐ์ค€์ด ์ƒ๊น๋‹ˆ๋‹ค:

โ€œํŠœ๋ง ๋จธ์‹ ์œผ๋กœ ํ’€ ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ด ๋ฌธ์ œ๋Š” ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•˜๋‹ค.โ€ ๋ฐ˜๋Œ€๋กœ, โ€œํŠœ๋ง ๋จธ์‹ ์œผ๋กœ๋„ ๋ชป ํ‘ธ๋Š” ๋ฌธ์ œ๋Š” ๊ณ„์‚ฐ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ๋‹ค.โ€

์ด ๊ธฐ์ค€์€ ์ง€๊ธˆ๋„ ๊ณ„์‚ฐ ์ด๋ก ์˜ ํ•ต์‹ฌ ์›์น™์ด์—์š”. ์ •์ง€ ๋ฌธ์ œ(Halting Problem) ๊ฐ™์€ ๊ณ ์ „์ ์ธ ์ด์Šˆ๋„, ๊ฒฐ๊ตญ ํŠœ๋ง ๋จธ์‹ ์œผ๋กœ ํ’€ ์ˆ˜ ์žˆ๋Š”๊ฐ€/์—†๋Š”๊ฐ€๋ฅผ ๋”ฐ์ง€๋ฉฐ ์ •์˜๋ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด โ€˜์ด ํ”„๋กœ๊ทธ๋žจ์ด ๋ฌดํ•œ ๋ฃจํ”„์— ๋น ์งˆ์ง€ ์•„๋‹์ง€โ€™๋ฅผ ์ž๋™์œผ๋กœ ํŒ๋‹จํ•˜๋Š” ๋ฌธ์ œ๋Š” ํŠœ๋ง ๋จธ์‹ ์œผ๋กœ๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด ์ •์ง€ ๋ฌธ์ œ(Halting Problem)์ฃ .


โš–๏ธ ์œ ํ•œ์ƒํƒœ๊ธฐ๊ณ„ vs ํŠœ๋ง ๋จธ์‹ 

์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„๊ฐ€ โ€˜๊ธฐ์–ต ์—†์ด ํŒ๋‹จํ•˜๋Š” ๊ธฐ๊ณ„โ€™๋ผ๋ฉด, ํŠœ๋ง ๋จธ์‹ ์€ โ€˜๊ธฐ์–ต์„ ํ†ตํ•ด ๋ณต์žกํ•œ ํ๋ฆ„์„ ์ดํ•ดํ•˜๋Š” ๊ธฐ๊ณ„โ€™์ž…๋‹ˆ๋‹ค. ๋งˆ์น˜ ๊ณ„์‚ฐ๊ธฐ์™€ ์—‘์…€ ์ •๋„์˜ ์ฐจ์ด๋ž„๊นŒ์š”?

ํ•ต์‹ฌ์€ ๊ธฐ์–ต ๋Šฅ๋ ฅ์ด์—์š”. ํ•œ๋ฒˆ ์ •๋ฆฌ ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„(FSM)ํŠœ๋ง ๋จธ์‹ 
๊ธฐ์–ต ๊ณต๊ฐ„์—†์Œ ๋˜๋Š” ๋งค์šฐ ์ œํ•œ์  (ํ˜„์žฌ ์ƒํƒœ๋งŒ ๊ธฐ์–ต)๋ฌดํ•œ ํ…Œ์ดํ”„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๊ณ„์‚ฐ ๋Šฅ๋ ฅ์ •๊ทœ ์–ธ์–ด๋งŒ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ์ด๋ก ์ ์œผ๋กœ ๋ชจ๋“  ๊ณ„์‚ฐ ๊ฐ€๋Šฅ
ํ˜„์‹ค ์ ์šฉ์ •๊ทœ์‹, ์–ดํœ˜ ๋ถ„์„๊ธฐ ๋“ฑ์ปดํ“จํ„ฐ ์ž์ฒด์˜ ์ˆ˜ํ•™์  ๋ชจ๋ธ
๋ณต์žก๋„๋‹จ์ˆœ๋ณต์žกํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•จ

๊ฒฐ๊ตญ ํŠœ๋ง ๋จธ์‹ ์€ ์šฐ๋ฆฌ๊ฐ€ ์˜ค๋Š˜๋‚  ์‚ฌ์šฉํ•˜๋Š” ์ปดํ“จํ„ฐ์˜ ์ˆ˜ํ•™์  ๋ชจ๋ธ์ด์ž, "์ปดํ“จํ„ฐ๊ฐ€ ์–ด๋””๊นŒ์ง€ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€" ๋ฅผ ๊ทœ์ •ํ•ด์ฃผ๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฐœ๋…์ด์—์š”.


โš™๏ธ ์ปดํ“จํ„ฐ๊ณตํ•™ ์ด๋ก  ์ด์•ผ๊ธฐ : ์ปดํŒŒ์ผ๋Ÿฌ

๐Ÿงฌ ์ปดํŒŒ์ผ๋Ÿฌ ์ด๋ก  (Compiler Theory)

์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ๊ณ ์ˆ˜์ค€ ์–ธ์–ด(Java, Kotlin, C ๋“ฑ)๋ฅผ ์ปดํ“จํ„ฐ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ €์ˆ˜์ค€ ์–ธ์–ด(๊ธฐ๊ณ„์–ด ๋“ฑ)๋กœ ๋ฒˆ์—ญํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋‚ด๋ถ€์—” ์šฐ๋ฆฌ๊ฐ€ ์•ž์„œ ์ด์•ผ๊ธฐํ•œ ์˜คํ† ๋งˆํƒ€ ์ด๋ก ์ด ์•„์ฃผ ๊นŠ์ด ๋…น์•„ ์žˆ์ฃ .

์ด๋ฒˆ ์„ธ์…˜์—์„œ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๋™์ž‘ ๋‹จ๊ณ„๋ฅผ ์•Œ์•„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ ๋‹จ๊ณ„๋ณ„๋กœ ์ƒํƒœ์˜ ํ๋ฆ„์„ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ (State Diagram) ๋˜ํ•œ ๊ทธ๋ ค๋ณผ๊ฒŒ์š”.

์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ๋‹จ๊ณ„๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ์–ดํœ˜ ๋ถ„์„ (Lexical Analysis) ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ํ† ํฐ(Token)์ด๋ผ๋Š” ์ตœ์†Œ ์˜๋ฏธ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ•๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„(FSM) ๊ฐ€ ํ™œ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ํ•˜๋‚˜ ๋“ค์–ด๋ณผ๊ฒŒ์š”. int, ์ •์ˆ˜ ์ž๋ฃŒํ˜• ํ‚ค์›Œ๋“œ๋ฅผ ์ธ์‹ํ•˜๋Š” ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ํ•œ๋ฒˆ ๊ทธ๋ ค๋ณผ๊นŒ์š”?

์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์—์„œ i โ†’ n โ†’ t ์ˆœ์œผ๋กœ ์ฝํžˆ๋ฉด int๋ผ๋Š” ํ‚ค์›Œ๋“œ ํ† ํฐ์œผ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์™ธ ์ž…๋ ฅ์ด ์˜ค๋ฉด ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ์ „์ดํ•˜๊ฑฐ๋‚˜ ๊ฑฐ๋ถ€ ์ƒํƒœ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ๋กœ ์ˆซ์ž๋ฅผ ์ธ์‹ํ•˜๋Š” ์˜ˆ์‹œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 12345 ์ฒ˜๋Ÿผ ์—ฌ๋Ÿฌ ์ž๋ฆฌ ์ •์ˆ˜๋ฅผ ์ธ์‹ํ•˜๋Š” ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ ค ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ•˜๋‚˜ ์ด์ƒ์˜ ์ˆซ์ž๋ฅผ ์ฝ์œผ๋ฉด DIGIT ์ƒํƒœ์— ๋จธ๋ฌด๋ฅด๋ฉฐ ์ˆซ์ž๋ฅผ ๊ณ„์† ๋ˆ„์ ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ๊ณต๋ฐฑ์ด๋‚˜ ๋‹ค๋ฅธ ๊ธฐํ˜ธ๊ฐ€ ์˜ค๋ฉด ์ƒํƒœ ์ข…๋ฃŒ๋˜๊ณ  ํ† ํฐ์ด ๋ฐ˜ํ™˜๋˜์ฃ .

๋‹ค์Œ์œผ๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ (Syntax Analysis) ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ๋ฌธ๋ฒ• ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ํŒŒ์‹ฑ ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ๋งฅ ์ž์œ  ๋ฌธ๋ฒ•(Context-Free Grammar) ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์Šคํƒ ๊ธฐ๋ฐ˜ ์˜คํ† ๋งˆํƒ€(PDA) ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์Šคํƒ์ด๋ผ๋Š” ์ด๋ฆ„์— ๋งž๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์Œ“์•„๋‘์ฃ .

์Šคํƒ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์„ค๋ช… ํ•ด ์ฃผ์ž๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์Œ“์•„๋‘๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ (ํ”ํžˆ ๋งํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ) ์ž…๋‹ˆ๋‹ค.

๋จผ์ € ๋“ค์–ด์˜จ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งˆ์ง€๋ง‰์— ๋‚˜๊ฐ€๋Š” First In Last Out (FILO) ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€์ฃ . ์šฐ๋ฆฌ๊ฐ€ ์ด๋ถˆ์„ ๊ฐœ์–ด์„œ ์Œ“์•„๋‘๋ฉด ๋งจ ๋ฐ‘์— ์žˆ๋Š” ๊ฑธ ๋นผ์ง„ ์•Š๋“ฏ์ด ์Šคํƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ์Œ“๋Š” ๋ช…๋ น์„ ์ผ๋ฐ˜์ ์œผ๋กœ push ๋นผ๋Š” ๋ช…๋ น์„ pop ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”? ์ค‘์ฒฉ ๊ด„ํ˜ธ ((())) ๋ฅผ ์ธ์‹ํ•˜๋Š” ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ ค๋ณผ๊ฒŒ์š”.

stateDiagram-v2
    [*] --> S0 : ์ž…๋ ฅ ์‹œ์ž‘
    S0 --> S0 : ( / push
    S0 --> S0 : ) / pop
    S0 --> ACCEPT : ์Šคํƒ์ด ๋น„์—ˆ์„ ๋•Œ ์ž…๋ ฅ ์ข…๋ฃŒ

์—ฌ๋Š” ๊ด„ํ˜ธ (๋ฅผ ์ฝ์œผ๋ฉด ์Šคํƒ์— push ํ•ฉ๋‹ˆ๋‹ค. ๋‹ซ๋Š” ๊ด„ํ˜ธ )๋ฅผ ์ฝ์œผ๋ฉด ์Šคํƒ์—์„œ pop ํ•˜์ฃ .

์Šคํƒ์ด ์ •ํ™•ํžˆ ๋น„์–ด์•ผ ACCEPT ์ƒํƒœ๋กœ ์ง„์ž… ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ตœ์ข…์ ์œผ๋กœ ์ค‘์ฒฉ ๊ด„ํ˜ธ๋ฅผ ์–ป๊ฒŒ ๋˜์ฃ .

์•„๋งˆ ์ฝ”๋”ฉ ๊ณต๋ถ€ํ•˜์‹œ๋Š” ๋ถ„๋“ค์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฌธ์ œ ํ’€ ๋•Œ ์ด๋Ÿฐ ๋ฌธ์ œ ๋งŽ์ด ๊ฒช์–ด๋ณด์…จ์„๊ฒ๋‹ˆ๋‹ค. ์Šคํƒ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ง€ ๋ฌป๋Š” ๋ฌธ์ œ์ฃ .

๋งˆ์ง€๋ง‰์œผ๋กœ ์˜๋ฏธ ๋ถ„์„, ์ค‘๊ฐ„ ์ฝ”๋“œ ์ƒ์„ฑ, ์ตœ์ ํ™” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ ์  ์ถ”์ƒํ™”๋œ ํ‘œํ˜„์„ ๋” ํšจ์œจ์ ์ธ ์ฝ”๋“œ๋กœ ๋ฐ”๊พธ๊ณ  ์ตœ์ข… ๋ชฉ์  ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜์ฃ .

์˜ˆ๋ฅผ ๋“ค์–ด int x = 2 + 3; ๋ผ๋Š” ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ถ„์„ํ•˜๋Š” ๊ณผ์ •์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช… ํ•ด ๋ณผ๊ฒŒ์š”.

๋จผ์ € ์–ดํœ˜ ๋ถ„์„ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ž…๋ ฅ์ด int, x, =, 2, +, 3, ; ๊ฐ๊ฐ์œผ๋กœ ๊ตฌ๋ถ„๋  ์ˆ˜ ์žˆ์ฃ . ๊ทธ ๊ฒฐ๊ณผ ์•„๋ž˜์™€ ๊ฐ™์ด ํ† ํฐ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

[Keyword:int], [Identifier:x], [Operator:=], [Literal:2], [Operator:+], [Literal:3]

๋‹ค์Œ์œผ๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. AST ๋Š” Abstract Syntax Tree ํ˜น์€ ๋‹จ์ˆœํžˆ Syntax Tree ๋ผ๊ณ  ๋ถˆ๋ ค์š”. ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ์†Œ์Šค ์ฝ”๋“œ์˜ ์ถ”์ƒ ๊ตฌ๋ฌธ ๊ตฌ์กฐ์˜ ํŠธ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํŠธ๋ฆฌ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”...๋ผ๊ณ  ํ•˜์‹œ๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด ์„ค๋ช… ๋“œ๋ฆฌ์ž๋ฉด ์ผ์ข…์˜ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์š”์†Œ๋“ค์ด ์—ฐ๊ฒฐ ๋˜์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์˜๋ฏธํ•ด์š”.

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

ํ‚ค์›Œ๋“œ๋“ค์— ๋Œ€ํ•œ AST ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

Assignment
 โ”œโ”€โ”€ Identifier(x)
 โ””โ”€โ”€ Expression
     โ”œโ”€โ”€ Literal(2)
     โ””โ”€โ”€ +
     โ””โ”€โ”€ Literal(3)

๋‹ค์Œ์œผ๋กœ ์˜๋ฏธ๋ถ„์„ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๊ฐ๊ฐ ํ† ํฐ์œผ๋กœ ๋‚˜๋ˆˆ ํ›„ ๋…ธ๋“œ๋“ค์ด ์—ฐ๊ฒฐ ๋œ ํŠธ๋ฆฌ๋กœ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ์˜๋ฏธ๋ฅผ ๋ถ„์„ ํ•ด ๋ณด์•„์•ผ ๊ฒ ์ฃ ?

  • x ๊ฐ€ int ๋กœ ์„ ์–ธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • 2 + 3 ์˜ ๊ฒฐ๊ณผ๋„ int (์ •์ˆ˜) ์ž…๋‹ˆ๋‹ค. ํƒ€์ž…์ด ์ผ์น˜ํ•˜๋„ค์š”.
  • ์‹ฌ๋ณผ ํ…Œ์ด๋ธ”์— x ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์„ ์–ธ ๋œ ๋ณ€์ˆ˜๋“ค์„ ์ €์žฅํ•˜๋Š” ๊ณณ์ด์—์š”.

๋‹ค์Œ์œผ๋กœ ์ค‘๊ฐ„ ์ฝ”๋“œ ์ƒ์„ฑ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋‹ค์–‘ํ•œ ํ•˜๋“œ์›จ์–ด๋ฅผ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”ํžˆ ๋งํ•˜๋Š” ์ปดํ“จํ„ฐ CPU ์‚ฌ์–‘ ์ค‘์— x86, ARM ๊ฐ™์€ ์šฉ์–ด๊ฐ€ ๋‚˜์˜ค์ž–์•„์š”? ์ด๋Ÿฐ CPU ๋“ค ๊ฐ๊ฐ์€ ๊ฐ์ž์˜ ๊ธฐ๊ณ„์–ด๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์น˜ ํ•œ๊ตญ์ธ, ์ค‘๊ตญ์ธ์ด ์–ธ์–ด๊ฐ€ ๋‹ค๋ฅธ ๊ฒƒ ์ฒ˜๋Ÿผ ๋ง์ด์ฃ . ์ปดํ“จํ„ฐ์—๊ฒŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ๋ถ„์„ํ•ด์„œ ๊ฐ์ž CPU ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๊ณ„์–ด๋กœ ๋ฐ”๊พธ๊ธฐ ์ „์—๋Š” ์ค‘๊ฐ„ ๋‹จ๊ณ„์˜ ํ‘œํ˜„์œผ๋กœ ๋ณ€ํ™˜ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ”ํžˆ ์“ฐ์ด๋Š” ๊ฒŒ 3-address-code ์ธ๋ฐ, 2๊ฐœ์˜ ์ž…๋ ฅ์šฉ, 1๊ฐœ์˜ ์ถœ๋ ฅ์šฉ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•œ๋‹ค๊ณ  ์ด๋ ‡๊ฒŒ ์ด๋ฆ„์ด ๋ถ™์—ˆ์–ด์š”.

t1 = 2 + 3
x = t1

์ด๋ ‡๊ฒŒ ์ชผ๊ฐœ๋‘๋ฉด 2 + 3 ์ด๋ผ๋Š” ํ‘œํ˜„์‹์ด ๋ณ„๋„์˜ ๋ณ€์ˆ˜ (t1) ์— ๋‹ด๊น๋‹ˆ๋‹ค. ์ดํ›„ ๋‹ค๋ฅธ ๊ณณ์— ์žฌ์‚ฌ์šฉ๋˜๊ฑฐ๋‚˜ ๊ณ„์‚ฐ์„ ๋ฏธ๋ฆฌ ํ•ด๋ฒ„๋ฆฌ๋Š” ์ตœ์ ํ™” (Constant Folding) ์„ ํ•  ์ˆ˜ ์žˆ์ฃ .

๋‹ค์Œ์œผ๋กœ ์ตœ์ ํ™” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋” ๋น ๋ฅด๊ณ , ์งง๊ณ , ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์•„๊นŒ ์–ธ๊ธ‰ํ•œ Constant Folding ์„ ํ•ด๋ณผ๊นŒ์š”?

t1 = 2 + 3 โ†’ t1 = 5
x = t1 โ†’ x = 5

์ด ์—ฐ์‚ฐ ์ž์ฒด๋ฅผ ์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ ํ•ด๋ฒ„๋ ค์„œ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋˜๋Š” ๋Ÿฐํƒ€์ž„์— ํ•˜์ง€ ์•Š๋„๋ก ๋ฏธ๋ฆฌ ์ƒ์ˆ˜ํ™” ํ•ด ๋‘ก๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ชฉ์ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ํ”Œ๋žซํผ (x86, ARM) ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ด ๋ชฉ์ ์ฝ”๋“œ๋Š” ์–ด์…ˆ๋ธ”๋ฆฌ๋ผ๊ณ  ํ•˜๋Š”๋ฐ CPU ์˜ ๊ธฐ๊ณ„์–ด๋ฅผ ์‚ฌ๋žŒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋„๋กœ ๋ฒˆ์—ญ ํ•ด ๋‘”๊ฑฐ๋ผ๊ณ ๋งŒ ์ดํ•ดํ•ด๋‘์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

MOV eax, 5
MOV [x], eax

๊ฐ’ 5๋ฅผ eax ๋ ˆ์ง€์Šคํ„ฐ์— ๋„ฃ๊ณ  eax ๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์ฃผ์†Œ x์— ์ €์žฅํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

์ œ๊ฐ€ ๊ธฐ๊ณ„์–ด ์ฝ”๋”ฉ์„ ํ•  ์ค„ ์•„๋Š” ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค....์—ด์‹ฌํžˆ ์ฐพ์•„๊ฐ€๋ฉฐ ์˜ˆ์‹œ๋ฅผ ๋งŒ๋“ค์—ˆ์ฃ .

์ด๋ ‡๊ฒŒ ๋ณด๋ฉด ์˜คํ† ๋งˆํƒ€ ์ด๋ก ์€ ๋‹จ์ˆœํžˆ ํ•™๋ถ€ ์‹œ์ ˆ์˜ ๊ณจ์น˜ ์•„ํ”ˆ ์ด๋ก ์ด ์•„๋‹ˆ๋ผ, ํ˜„๋Œ€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ๊ธฐ์ˆ ์— ์‘์šฉ๋˜๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ๋ผ๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐ŸŒฑ ์‹ค๋ฌด ์ด์•ผ๊ธฐ : Spring State Machine

์ง€๊ธˆ๊นŒ์ง€ ์œ ํ•œ ์ƒํƒœ ๊ธฐ๊ณ„(FSM)์˜ ๊ฐœ๋…๊ณผ ๊ทธ ์ด๋ก ์  ๋ฐฐ๊ฒฝ, ๊ทธ๋ฆฌ๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ์ฒ˜๋Ÿผ FSM์ด ํ™œ์•ฝํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์ œ๊นŒ์ง€ ์‚ดํŽด๋ดค์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ๋ฌป๋Š” ๋ถ„๋“ค๋„ ๊ณ„์‹ค ์ˆ˜ ์žˆ์–ด์š”.

"๊ทผ๋ฐ... ์ด๊ฑธ ์šฐ๋ฆฌ๊ฐ€ ์‹ค๋ฌด์—์„œ ์™œ ์จ์•ผ ํ•˜์ฃ ?"

์ข‹์€ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ์‹œ์Šคํ…œ์€ ์ƒ๊ฐ๋ณด๋‹ค ํ›จ์”ฌ '์ƒํƒœ' ์ค‘์‹ฌ์œผ๋กœ ํ˜๋Ÿฌ๊ฐ€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ์ œ๋ฅผ ๋ˆŒ๋ €์–ด์š”.
  • ๊ฒฐ์ œ ์ค‘์— ์ทจ์†Œํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์‹คํŒจํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์„ฑ๊ณตํ•  ์ˆ˜๋„ ์žˆ์ฃ .
  • ์„ฑ๊ณตํ•˜๋ฉด ๋ฐฐ์†ก์œผ๋กœ ๋„˜์–ด๊ฐ€๊ณ , ๋ฐฐ์†ก ์ค‘์—๋„ ์ƒํƒœ๋Š” '์ถœ๊ณ  ์™„๋ฃŒ', '๋ฐฐ์†ก ์ค‘', '๋ฐฐ์†ก ์™„๋ฃŒ' ๋“ฑ์œผ๋กœ ๊ณ„์† ๋ฐ”๋€Œ์–ด์š”.

์ฆ‰, ์‹œ์Šคํ…œ ๋‚ด๋ถ€์—์„  ํ˜„์žฌ ์ƒํƒœ(state) ์™€ ์ด๋ฒคํŠธ(event) ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ ๋™์ž‘์„ ๊ฒฐ์ •ํ•ด์•ผ ํ•˜๋Š” ํ๋ฆ„์ด ๋ฌด์ˆ˜ํžˆ ๋งŽ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฑธ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ ์ฝ”๋“œ๋กœ ํ’€์–ด๋ณด๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋ƒ๊ณ ์š”?

if (status == "PENDING") { ... } else if (status == "PAID") { ... } else if (...)
๐Ÿ˜จ : ์ ์  ๊ดด๋ฌผ์ด ๋˜์–ด๊ฐ‘๋‹ˆ๋‹คโ€ฆ
๐Ÿ : ์—ฌ๊ธฐ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์‹œํ‚ค์‹  ๋ถ„?
๋ณต์žกํ•˜๊ฒŒ ๊ผฌ์ธ ์ฝ”๋“œ๋ฅผ ํ”ํžˆ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ง€์ฃ .

์ด์ œ ์—ฌ๊ธฐ์„œ Spring State Machine์ด ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค.


โœ… Spring State Machine์ด๋ž€?

Spring State Machine์€ ๋ณต์žกํ•œ ์ƒํƒœ ์ „์ด ๋กœ์ง์„ ์„ ์–ธ์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
์ƒํƒœ(state), ์ด๋ฒคํŠธ(event), ์ „์ด(transition)๋ฅผ ์ฝ”๋“œ๋กœ ์ •์˜ํ•˜๊ณ , ์ƒํƒœ๊ฐ€ ์ „์ด๋  ๋•Œ ์–ด๋–ค ์•ก์…˜์„ ์‹คํ–‰ํ• ์ง€ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์ฃ .

์•ž์„œ ์˜คํ† ๋งˆํƒ€ ์ด๋ก ์—์„œ ๋ณธ ๊ตฌ์„ฑ์š”์†Œ์™€ ๋น„๊ตํ•ด๋ณด๋ฉด ๊ฑฐ์˜ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค.

์˜คํ† ๋งˆํƒ€ ๊ตฌ์„ฑ ์š”์†ŒSpring State Machine ๋Œ€์‘ ๊ฐœ๋…
์ƒํƒœ (State)์ƒํƒœ ์—ด๊ฑฐํ˜• (enum) ๋˜๋Š” ๋ฌธ์ž์—ด
์ž…๋ ฅ/์ด๋ฒคํŠธ (Event)์ „์ด ํŠธ๋ฆฌ๊ฑฐ์šฉ ์ด๋ฒคํŠธ
์ „์ด (Transition)source, target, event
์ „์ด ์กฐ๊ฑด/์•ก์…˜guard, action ๋ฉ”์„œ๋“œ

์ฆ‰, ํ•™๋ถ€ ์‹œ์ ˆ ์–ด๋ ต๊ฒŒ ๋ฐฐ์šด ์˜คํ† ๋งˆํƒ€ ์ด๋ก ์ด,
์‹ค์ œ ์„œ๋น„์Šค์˜ ๋ณต์žกํ•œ ์ƒํƒœ ํ๋ฆ„์„ ๋ช…ํ™•ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๋กœ ์žฌ๋“ฑ์žฅํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค.


๐Ÿ’ก ์–ธ์ œ ์“ฐ๋ฉด ์ข‹์„๊นŒ?

Spring State Machine์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ํŠนํžˆ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒํƒœ๊ฐ€ 3~4๊ฐœ ์ด์ƒ์ด๊ณ , ๊ฐ ์ƒํƒœ๋งˆ๋‹ค ์ด๋ฒคํŠธ ์กฐ๊ฑด์ด๋‚˜ ํ›„์ฒ˜๋ฆฌ ๋กœ์ง์ด ์กด์žฌํ•  ๋•Œ
  • ์ƒํƒœ ์ „์ด๊ฐ€ ๋ช…ํ™•ํ•œ ํ”„๋กœ์„ธ์Šค (๊ฒฐ์ œ, ์ฃผ๋ฌธ, ๋ฐฐ์†ก, ์Šน์ธ, ์ธ์ฆ ๋“ฑ)๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ
  • ํ๋ฆ„ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์šด if-else ์ง€์˜ฅ์—์„œ ๋ฒ—์–ด๋‚˜๊ณ  ์‹ถ์„ ๋•Œ
  • ์ƒํƒœ์— ๋”ฐ๋ผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ถ„๊ธฐ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋  ๋•Œ

๐Ÿงฉ Spring State Machine ์˜ ๊ตฌ์„ฑ์š”์†Œ

์ข€ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ Spring State Machine ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜คํ† ๋งˆํƒ€์˜ ๊ตฌ์„ฑ์š”์†Œ์™€ ๋Œ€์‘๋˜๋Š” ํ‚ค์›Œ๋“œ๋“ค์ด ์žˆ๋Š” ๊ฑด ์ดํ•ดํ–ˆ์ง€๋งŒ, ์šฐ๋ฆฌ๊ฐ€ ์ด ๊ธฐ์ˆ ์„ ์จ๋จน๊ธฐ ์œ„ํ•ด์„  ๊ฐ ๊ตฌ์„ฑ์š”์†Œ๋“ค์— ๋Œ€ํ•ด ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ›‘ ์ƒํƒœ (State)

Spring State Machine ์—์„œ ์ƒํƒœ (State) ๋ž€ ์‹œ์Šคํ…œ์ด ์ผ์ • ์‹œ์ ์— ์ฒ˜ํ•ด ์žˆ๋Š” ๋…ผ๋ฆฌ์  ๊ตฌ๊ฐ„์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ๋กœ enum ํด๋ž˜์Šค๋กœ ์„ ์–ธํ•˜์ฃ .

์—ฌ๊ธฐ์„œ enum ์ด๋ž€?

๊ฐ’์˜ ์ง‘ํ•ฉ์„ ์ด๋ฃจ๋Š” ์š”์†Œ๋ฅผ ๋œปํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ž…๋‹ˆ๋‹ค. ์—ด๊ฑฐํ˜•์ด๋ผ๊ณ ๋„ ๋ถ€๋ฅด์ฃ .
enum ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฉค๋ฒ„๋“ค ๊ฐ๊ฐ์€ ๊ฐ์ž์˜ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
์ƒ์ˆ˜๋“ค์„ ๋ชจ์•„ ๋‘” ๊ฒƒ์ด๋ผ ์ดํ•ดํ•ด๋„ ์ข‹์•„์š”.

ํด๋ž˜์Šค๋ž€ ๋˜ ๋ฌด์—‡์ธ๊ฐ€? ๋ผ๊ณ  ์งˆ๋ฌธํ•˜์‹ค ๋ถ„๋„ ๊ณ„์‹ค ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
์ง€๊ธˆ์€ ์ฝ”๋“œ๋ณด๋‹จ ๊ฐœ๋…์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ํ•˜๊ณ  ์‹ถ์œผ๋‹ˆ ๊ฐ„๋‹จํžˆ๋งŒ ์„ค๋ช…ํ•˜์ž๋ฉด, ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ฝ”๋“œ ๋‹จ์œ„๋ผ๊ณ  ์ƒ๊ฐ ํ•ด ์ฃผ์„ธ์š”.

์ด ํด๋ž˜์Šค ๋ผ๋Š” ์„ค๊ณ„๋„๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ ๋œ ๊ฐ์ฒด๋“ค์ด ์ƒํ˜ธ์ž‘์šฉํ•˜๋ฉด์„œ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ด๋ค„์š”. ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก  ์ค‘ '๊ฐ์ฒด์ง€ํ–ฅ' ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ๋กœ์ง๋“ค์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋ฌผ๋ก  ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์•„๋‹™๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์—๊ฒ '์ ˆ์ฐจ์ง€ํ–ฅ' ๋„ ์žˆ์œผ๋‹ˆ๊นŒ์š”.

์ด ์ƒํƒœ ๋ฐ์ดํ„ฐ ์ค‘ ๋กœ์ง์„ ์‹คํ–‰ํ•  ๋•Œ์˜ ์ƒํƒœ๋ฅผ entry, ๋กœ์ง์ด ๋๋‚  ๋•Œ์˜ ์ƒํƒœ๋ฅผ exit ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

enum class PaymentState {
    WAITING, PROCESSING, APPROVED, FAILED, CANCELED
}

์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์—์„œ๋Š” ๊ฒฐ์ œ ๋Œ€๊ธฐ -> ๊ฒฐ์ œ ์ค‘ -> ๊ฒฐ์ œ ์™„๋ฃŒ ๋“ฑ์˜ ์ƒํƒœ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿšฆ์ด๋ฒคํŠธ (Event)

์ƒํƒœ์˜ ์ „์ด๋ฅผ ์œ ๋ฐœํ•˜๋Š” ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด ๋˜ํ•œ enum ์œผ๋กœ ์„ ์–ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ด๋ฒคํŠธ๋“ค์€ ์™ธ๋ถ€ ์ž…๋ ฅ ๋˜๋Š” ๋‚ด๋ถ€ ํ˜ธ์ถœ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ฃ .

enum class PaymentEvent {
    REQUEST, APPROVE, DECLINE, CANCEL
}

๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ์ œ ์š”์ฒญ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด REQUEST ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ ํ•˜๊ฒŒ ๋˜๊ฒ ์ฃ ?

๐Ÿ”€ ์ „์ด (Transition)

ํ•œ ์ƒํƒœ์—์„œ ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒฝ๋กœ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

source, target, event ๊ฐ๊ฐ์„ ์กฐํ•ฉํ•ด์„œ ๋งŒ๋“ค์ฃ . ์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”?

transitions
    .withExternal()
        .source(PaymentState.WAITING)
        .target(PaymentState.PROCESSING)
        .event(PaymentEvent.REQUEST)

WAITING ์ƒํƒœ์—์„œ REQUEST ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด PROCESSING ์œผ๋กœ ์ „์ดํ•œ๋‹ค๋Š” ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๊ฐ ์ƒํƒœ๋“ค์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ›ก๏ธ ๊ฐ€๋“œ (Guard)

๊ฐ€๋“œ (Guard) ๋Š” ํŠน์ • ์กฐ๊ฑด์ด ์ถฉ์กฑ ๋  ๋•Œ๋งŒ ์ „์ด๋ฅผ ํ—ˆ์šฉํ•˜๋Š” boolean (true / false) ์กฐ๊ฑด์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์กฐ๊ฑด์ด true ์ผ ๋•Œ๋งŒ ์ „์ด๋˜๋„๋ก ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ™‹โ€โ™‚๏ธ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ญ์—์š”?

๋ฉ”์†Œ๋“œ(method)๋Š” ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ช…๋ น๋ฌธ ์ง‘ํ•ฉ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
ํ”ํžˆ ํ•จ์ˆ˜ (function) ์ด๋ผ๊ณ ๋„ ํ•˜๋Š”๋ฐ ์ข€ ๋” ๋ถ€๋ถ„์ง‘ํ•ฉ ์ ์ธ ์˜๋ฏธ์—์š”.
๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์˜ ๋‹จ์œ„๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ฒ”์šฉ์ ์œผ๋กœ ์“ฐ์ด๋Š” ์ž‘์—… ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ผ ํŠน์ • ๊ฐ์ฒด๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์˜ ๋‹จ์œ„๋ฅผ ์˜๋ฏธํ•˜์ฃ .

.guard { context ->
    val paymentId = context.extendedState.variables["paymentId"] as Long
    paymentService.canProcess(paymentId)
}

๊ฒฐ์ œ๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ธ ์ง€ ํ™•์ธ ํ•œ ํ›„์— ๊ฒฐ์ œ ์š”์ฒญ ์ „์ด๋ฅผ ํ•˜๊ณ ์‹ถ์„ ๋•Œ ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฌ ์•ก์…˜ (Action)

์•ก์…˜ (Action) ์€ ์ „์ด ์‹œ์  ๋˜๋Š” ์ƒํƒœ ์ง„์ž… ์‹œ ์‹คํ–‰ํ•  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ž…๋‹ˆ๋‹ค.

.action { context ->
    val paymentId = context.extendedState.variables["paymentId"] as Long
    paymentService.processPayment(paymentId)
}

processPayment ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์˜ˆ์‹œ์—์š”. ํŠน์ • ์ƒํƒœ์— ์ง„์ž…ํ–ˆ์„ ๋•Œ (PROCESSING ์ด ๋˜๊ฒ ์ฃ ?) ์ด ๋กœ์ง์„ ํ˜ธ์ถœํ•˜๋ผ๊ณ  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•  ์ˆ˜ ์žˆ์ฃ .

๐Ÿ“ฆ ExtendedState

ExtendedState ๋Š” ์ƒํƒœ ์ „์ด ์‹œ์ ์— ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ „์—ญ ์ƒํƒœ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ๊ฒฐ์ œ ID (paymentId) ์™€ ๊ฐ™์€ ์ •๋ณด๋ฅผ FSM ์•ˆ์—์„œ ๊ณต์œ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜์ฃ .

stateMachine.extendedState.variables["paymentId"] = 123L

๐Ÿ’ณ ๊ฒฐ์ œ ์„œ๋น„์Šค ์˜ˆ์ œ๋กœ ๋ณด๋Š” Spring State Machine

๋งˆ์นจ ๊ฒฐ์ œ์— ๋Œ€ํ•œ ์˜ˆ์‹œ๊ฐ€ ์•ž์„œ ๋‚˜์˜ค๊ธฐ๋„ ํ–ˆ์œผ๋‹ˆ ๊ตฌ์ฒด์ ์œผ๋กœ ์‹ค๋ฌด์—์„œ ๋„์ž… ํ•œ ์˜ˆ์ œ๋ฅผ ์งš์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ์ œ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ด ๋กœ์ง์€ ์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • ๋Œ€๊ธฐ ์ค‘ โ†’ ์ฒ˜๋ฆฌ ์ค‘ โ†’ ์Šน์ธ๋จ
  • ํ˜น์€ ์ค‘๊ฐ„์— ์‹คํŒจ, ํ˜น์€ ์‚ฌ์šฉ์ž ์ทจ์†Œ
  • ๋˜ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์„  ํ™˜๋ถˆ, ์žฌ์Šน์ธ, ๋ณด๋ฅ˜ ์ƒํƒœ๋„ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

์ด์ฒ˜๋Ÿผ ์ƒํƒœ๊ฐ€ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„๋˜๊ณ , ์ „์ด๊ฐ€ ์ด๋ฒคํŠธ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ Spring State Machine์„ ์ ์šฉํ•˜๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ „์ฒด ํ๋ฆ„์„ ๋‹จ์ˆœํ™”ํ•ด ์‹œ๊ฐํ™”ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

[WAITING] --๊ฒฐ์ œ ์š”์ฒญโ†’ [PROCESSING]
    |                        |
    |                        +--์Šน์ธโ†’ [APPROVED]
    |                        +--์‹คํŒจโ†’ [FAILED]
    |                        +--์ทจ์†Œโ†’ [CANCELED]

โœ๏ธ ๋กœ์ง ์ž‘์„ฑ

Spring(Kotlin) ์˜ˆ์‹œ๋กœ ํ•œ๋ฒˆ ๋ณผ๊นŒ์š”? (์ฝ”๋“œ๋Š” ์ตœ์†Œํ•œ์œผ๋กœ ์ž‘์„ฑ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.)

enum class PaymentState {
    WAITING, PROCESSING, APPROVED, FAILED, CANCELED
}

enum class PaymentEvent {
    REQUEST, APPROVE, DECLINE, CANCEL
}

๊ฒฐ์ œ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ์ƒํƒœ์™€ ์ด๋ฒคํŠธ๋“ค์„ enum ํด๋ž˜์Šค๋กœ ์„ ์–ธ ํ•ด ๋‘ก๋‹ˆ๋‹ค.

@Configuration
@EnableStateMachine
class PaymentStateMachineConfig : StateMachineConfigurerAdapter<PaymentState, PaymentEvent>() {

    override fun configure(states: StateMachineStateConfigurer<PaymentState, PaymentEvent>) {
        states
            .withStates()
            .initial(PaymentState.WAITING)
            .end(PaymentState.APPROVED)
            .end(PaymentState.FAILED)
            .end(PaymentState.CANCELED)
            .states(EnumSet.allOf(PaymentState::class.java))
    }

    override fun configure(transitions: StateMachineTransitionConfigurer<PaymentState, PaymentEvent>) {
        transitions
            .withExternal().source(PaymentState.WAITING).target(PaymentState.PROCESSING).event(PaymentEvent.REQUEST)
            .and()
            .withExternal().source(PaymentState.PROCESSING).target(PaymentState.APPROVED).event(PaymentEvent.APPROVE)
            .and()
            .withExternal().source(PaymentState.PROCESSING).target(PaymentState.FAILED).event(PaymentEvent.DECLINE)
            .and()
            .withExternal().source(PaymentState.PROCESSING).target(PaymentState.CANCELED).event(PaymentEvent.CANCEL)
    }
}

๊ทธ๋ฆฌ๊ณ  ์ด ์ƒํƒœ๋“ค์˜ ๋ณ€ํ™”์— ๋Œ€ํ•œ ์ „์ด ๋ฐฉํ–ฅ์„ฑ์„ Configuration ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ์ œ ๋กœ์ง์˜ ์ดˆ๊ธฐ ์ƒํƒœ๋Š” WAITING, ๊ทธ๋ฆฌ๊ณ  APPROVED, FAILED, CANCELED ๋Š” ์ข…๋ฃŒ ์ƒํƒœ์ฃ . ์ด ์ƒํƒœ๋“ค์˜ ๊ด€๊ณ„๋ฅผ StateMachineStateConfigurer ์— ์ง€์ • ํ•ด ์ค๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ƒํƒœ ์ „์ด ํ๋ฆ„ ๊ด€๊ณ„๋ฅผ StateMachineTransitionConfigurer ์— ์ž‘์„ฑ ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ๊ฐ€ ์ถ”์ƒ์ ์ผ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ ค๋ณผ๊นŒ์š”?

์ง€๊ธˆ๊นŒ์ง€ ์ž˜ ๋”ฐ๋ผ ์™€ ์ฃผ์…จ์Šต๋‹ˆ๋‹ค. State Machine ์„ ์„ค์ •ํ–ˆ์œผ๋‹ˆ ์‹ค์ œ ์„œ๋น„์Šค์— ํ•œ๋ฒˆ ๋…น์—ฌ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Spring MVC ๊ตฌ์กฐ ์ƒ์—์„œ ๊ฒฐ์ œ ์š”์ฒญ์— ๋Œ€ํ•œ ๋กœ์ง์€ ์ด๋ ‡๊ฒŒ ํ˜๋Ÿฌ๊ฐ‘๋‹ˆ๋‹ค.

Controller (๊ฒฐ์ œ ์š”์ฒญ) โ†’ Service (์ƒํƒœ ์ „์ด trigger)
                         โ†˜ StateMachine โ†’ action / entry ์‹คํ–‰
@Component
class PaymentActions {

    @Bean
    fun requestAction(): Action<PaymentState, PaymentEvent> {
        return Action { context ->
            val paymentId = context.extendedState.variables["paymentId"] as Long
            paymentService.processPayment(paymentId)
        }
    }
}

๋งŒ์•ฝ ๊ฒฐ์ œ ์š”์ฒญ์ด PROCESSING ์ƒํƒœ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ ์‹คํ–‰๋˜์–ด์•ผ ํ•  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๊ฒฐ์ œ ๋กœ์ง ํ˜ธ์ถœ์ด๋ผ๋ฉด ์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ Configuration ์ฝ”๋“œ์— ๋ถ€๊ฐ€ ๋กœ์ง์„ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

transitions
    .withExternal()
        .source(PaymentState.WAITING)
        .target(PaymentState.PROCESSING)
        .event(PaymentEvent.REQUEST)
        .action(paymentActions.requestAction())

Service ๋ ˆ์ด์–ด (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ ˆ์ด์–ด) ์—์„œ๋Š” ๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Service
class PaymentService(
    private val stateMachineFactory: StateMachineFactory<PaymentState, PaymentEvent>
) {
    /**
    * ๊ฒฐ์ œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ ์ œ์ผ ๋จผ์ € ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์†Œ๋“œ
    */
    fun handlePaymentRequest(paymentId: Long) {
        val stateMachine = stateMachineFactory.getStateMachine(paymentId.toString())
        stateMachine.start()
        stateMachine.extendedState.variables["paymentId"] = paymentId
        stateMachine.sendEvent(PaymentEvent.REQUEST)
    }

    /**
    * ๊ฒฐ์ œ ๋กœ์ง ์‹œ์ž‘
    */
    fun processPayment(paymentId: Long) {
        // do something
    }
}

๊ฒฐ์ œ ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ handlePaymentRequest ๋ฉ”์†Œ๋“œ๋ถ€ํ„ฐ ํ˜ธ์ถœํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด๋ฉด, ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ stateMachine ์„ ํ•˜๋‚˜ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

์ „์ฒด ํ๋ฆ„์„ ์ƒํƒœ ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์š”์•ฝํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

sequenceDiagram
    participant C as Controller
    participant S as PaymentService
    participant FSM as StateMachine
    participant PG as PG API

    C->>S: ๊ฒฐ์ œ ์š”์ฒญ(paymentId)
    S->>FSM: ์ƒํƒœ ๋จธ์‹  ์‹œ์ž‘ ๋ฐ ์ด๋ฒคํŠธ ์š”์ฒญ(REQUEST)
    FSM->>S: action(requestAction) ํ˜ธ์ถœ
    S->>PG: ๊ฒฐ์ œ ์Šน์ธ ์š”์ฒญ
    PG-->>S: ์Šน์ธ ์‘๋‹ต

๋ชจ๋“  ๊ฒฐ์ œ ๋กœ์ง์ด ๋๋‚˜๊ณ  APPROVED ์ƒํƒœ์— ์ง„์ž…ํ–ˆ์„ ๋•Œ ์•Œ๋žŒ์„ ๋ณด๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์•ก์…˜์„ ์ง€์ •ํ•ด์ค„ ์ˆ˜๋„ ์žˆ์ฃ .

/**
* APPROVED ์ƒํƒœ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ Slack, Telegram ๋“ฑ์œผ๋กœ ์•Œ๋žŒ ์ „๋‹ฌ
*/
@Bean
fun approvalEntryAction(): Action<PaymentState, PaymentEvent> {
    return Action { context ->
        val paymentId = context.extendedState.variables["paymentId"] as Long
        notificationService.sendApprovalMessage(paymentId)
    }
}

override fun configure(states: StateMachineStateConfigurer<PaymentState, PaymentEvent>) {
    states
        .withStates()
        .initial(PaymentState.WAITING)
        .state(PaymentState.APPROVED, paymentActions.approvalEntryAction(), null)
}

๐Ÿงช ๋กœ์ง ํ…Œ์ŠคํŠธ

์ง€๊ธˆ๊นŒ์ง€ ๋กœ์ง์„ ์ž˜ ์ž‘์„ฑํ–ˆ์ง€๋งŒ, ๊ฐœ๋ฐœ์ž ์„ ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ณ  QA ๋กœ ๋„˜๊ฒจ์•ผ๊ฒ ์ฃ ?

์ง€๊ธˆ๊นŒ์ง€ ์ž‘์„ฑํ•œ State Machine ์„ Kotest ๋กœ ํ…Œ์ŠคํŠธ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.statemachine.StateMachine
import org.springframework.statemachine.config.StateMachineFactory
import org.springframework.test.context.TestPropertySource

@SpringBootTest
@TestPropertySource(locations = ["classpath:application-test.properties"])
class PaymentStateMachineBehaviorSpecTest @Autowired constructor(
    private val stateMachineFactory: StateMachineFactory<PaymentState, PaymentEvent>
) : BehaviorSpec({

    given("๊ฒฐ์ œ ์ƒํƒœ ๋จธ์‹ ์ด ์ดˆ๊ธฐํ™”๋˜์—ˆ์„ ๋•Œ") {

        `when`("REQUEST ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋ฉด") {
            then("WAITING โ†’ PROCESSING ์ƒํƒœ๋กœ ์ „์ด๋œ๋‹ค") {
                val machine: StateMachine<PaymentState, PaymentEvent> =
                    stateMachineFactory.getStateMachine("PAYMENT_REQ_1")
                machine.start()

                machine.sendEvent(PaymentEvent.REQUEST)

                machine.state.id shouldBe PaymentState.PROCESSING
            }
        }

        `when`("REQUEST โ†’ APPROVE ์ด๋ฒคํŠธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ณด๋‚ด๋ฉด") {
            then("WAITING โ†’ PROCESSING โ†’ APPROVED ์ƒํƒœ๋กœ ์ „์ด๋œ๋‹ค") {
                val machine = stateMachineFactory.getStateMachine("PAYMENT_REQ_2")
                machine.start()

                machine.sendEvent(PaymentEvent.REQUEST)
                machine.sendEvent(PaymentEvent.APPROVE)

                machine.state.id shouldBe PaymentState.APPROVED
            }
        }

        `when`("REQUEST โ†’ DECLINE ์ด๋ฒคํŠธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ณด๋‚ด๋ฉด") {
            then("WAITING โ†’ PROCESSING โ†’ FAILED ์ƒํƒœ๋กœ ์ „์ด๋œ๋‹ค") {
                val machine = stateMachineFactory.getStateMachine("PAYMENT_REQ_3")
                machine.start()

                machine.sendEvent(PaymentEvent.REQUEST)
                machine.sendEvent(PaymentEvent.DECLINE)

                machine.state.id shouldBe PaymentState.FAILED
            }
        }
    }
})

ํ”ํžˆ ๋ณด๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ง€๋งŒ, State Machine ์œผ๋กœ ๋กœ์ง์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ๋งˆ์น˜ ๊ธฐ๊ณ„๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ช…์‹œ์ ์ด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์ ์ด ์žˆ์ฃ .

โš ๏ธ Spring State Machine ๋„์ž… ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์ ?

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

๋จผ์ € Finite State Machine ์„ ์“ฐ๋Š” ๊ฒŒ ๊ณผํ•œ ์„ค๊ณ„์ธ ์ง€ ๊ณ ๋ คํ•ด๋ณผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ๊ฐ€ ์ ๋‹ค๋ฉด, ์˜ˆ๋ฅผ ๋“ค์–ด 2 ~ 3๊ฐœ ์ •๋„๋ฉด ๊ตณ์ด State Machine ๊นŒ์ง€ ํ•„์š”๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์–ด์š”. ๋‹จ์ˆœํ•œ ์ฝ”๋“œ๋กœ ํ•ด๊ฒฐ ๋  ๋งŒํผ ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ๋ผ๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒŒ ๋งž์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ณต์žกํ•œ ์กฐ๊ฑด ๋ถ„๋ฆฌ์— ๋Œ€ํ•ด์„œ๋Š” Guard ๋กœ ์ž˜ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์Šน์ธ ์กฐ๊ฑด์— 1๋ถ„ ์ด๋‚ด ์žฌ์‹œ๋„ ํ–ˆ๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์ƒํƒœ๊ฐ€ ๋ธ”๋ž™ํšŒ์›์ธ ๊ฒฝ์šฐ์—” Guard ๋กœ ์ถ”์ถœํ•ด์•ผ ํ•˜์ฃ .

๋˜ํ•œ ์žฅ๊ธฐ ๊ฑฐ๋ž˜๋‚˜ ์Šน์ธ ํ”„๋กœ์„ธ์Šค ๋“ฑ ์ƒํƒœ๋ฅผ ๋ณด์กดํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—” Oracle, MySQL ๊ฐ™์€ RDB ํ˜น์€ Redis ๋“ฑ์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์ €์žฅ ๋ฐฉ์‹์— ๋”ฐ๋ผ ๋ณต๊ตฌ๋‚˜ ์žฌ์‹œ๋„ ์ „๋žต์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ „์ด๊ฐ€ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์˜ˆ์™ธ๊ฐ€ ์ผ์–ด๋‚˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๋ฅผ ๋ช…ํ™•ํžˆ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. StateMachineListener ๋“ฑ์„ ํ™œ์šฉํ•ด์•ผ ํ•˜์ฃ .

โฑ๏ธ ์‹ฌํ™” : ๊ฒฐ์ œ ์„ธ์…˜ ๊ด€๋ฆฌ ์˜ˆ์ œ

๊ทธ๋Ÿผ ๋งŒ์•ฝ์— ์ด๋Ÿฐ ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐ ํ•ด๋ณผ๊นŒ์š”?

๊ฑฐ๋ž˜ ์Šน์ธ์€ ์„œ๋ฒ„ ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ 3๋ถ„ ๊ฐ„ ์œ ํšจํ•ด์•ผ ํ•œ๋‹ค.

๋ฉ€๋ฆฌ ๊ฐˆ ๊ฒƒ๋„ ์—†์ด ์‡ผํ•‘๋ชฐ์—์„œ XX ํŽ˜์ด ๋“ฑ์œผ๋กœ ๊ฒฐ์ œ ํ•  ๋•Œ, ๋ช‡ ๋ถ„ ์•ˆ์— ๊ฒฐ์ œ๊ฐ€ ์ด๋ค„์ง€์ง€ ์•Š์œผ๋ฉด ๋‹ค์‹œ ์š”์ฒญ์„ ํ•ด์•ผ ํ•˜์ฃ .

์šฐ๋ฆฌ๊ฐ€ ๊ฑฐ๋ž˜ ์š”์ฒญ์„ ํ•˜๋Š” ์‹œ์ ์—์„œ State Machine ์ด ํŠน์ • ๊ฑฐ๋ž˜ ID ์— ๋Œ€ํ•ด ์ดˆ๊ธฐํ™”๋˜๊ณ , 3๋ถ„ ์•ˆ์— ๊ฑฐ๋ž˜๋ฅผ ์Šน์ธํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”?

์„œ๋ฒ„ ์ž…์žฅ์—์„  ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋”˜๊ฐ€์— ์ €์žฅ ํ•ด ๋‘์–ด์•ผ ๊ฒ ์ฃ . ์ผ๋ฐ˜์ ์œผ๋กœ RDB ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๊ฒฐ์ œ ์š”์ฒญ์ด ๋Œ€๊ทœ๋ชจ๋กœ ๋“ค์–ด์˜ค๊ฒŒ ๋œ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”? ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ณ‘๋ชฉ์ด ์ผ์–ด๋‚  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Redis ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ (RAM) ์— ์˜ฌ๋ ค๋‘๊ณ  ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ RDB ๋Š” ๋””์Šคํฌ (ํ”ํžˆ ๋งํ•˜๋Š” SSD, HDD) ์— ๊ธฐ๋ฐ˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ๊ธฐ/์“ฐ๊ธฐ ์„ฑ๋Šฅ์— ์ œํ•œ์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์ฃ .

๋Œ€๋Ÿ‰์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋Š” ๊ฒฝ์šฐ ํŠนํžˆ RDB ๋งŒ์œผ๋กœ๋Š” ๋™์‹œ์— ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์— ๋Œ€ํ•ด ์“ฐ๊ธฐ ์š”์ฒญ์ด ๋“ค์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ ๋ฝ (๋‹ค๋ฅธ ์“ฐ๊ธฐ ์š”์ฒญ์ด ๋ง‰ํžˆ๋Š” ๊ฑธ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.)์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Redis ๋Š” ํŠน์ • key ๊ฐ’์— ํ• ๋‹น ๋œ ๋ฐ์ดํ„ฐ๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋˜์ฃ . ๊ฒŒ๋‹ค๊ฐ€ TTL(๋งŒ๋ฃŒ์‹œ๊ฐ„) ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ๋ฃŒ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ์ฃ . RDB ๋Š” Scheduler ๋“ฑ์„ ํ†ตํ•ด ์ฃผ๊ธฐ์ ์œผ๋กœ ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๋“ค์„ ์ง€์›Œ์ค˜์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ ๊ฐ™์€ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ์™€ ๊ฐ™์ด ์ผ์‹œ์ ์ด๊ณ  ๋นˆ๋ฒˆํžˆ ๋ณ€๊ฒฝ๋˜๋Š” ์ƒํƒœ ๋ฐ์ดํ„ฐ๋Š” Redis ๊ฐ€ ์ข€ ๋” ์ ํ•ฉํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  RDB๋ผ๊ณ  ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ์•ž์„œ ๋ง์”€๋“œ๋ฆฐ ์ƒํ™ฉ์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ๋Œ€์‘ ๊ฐ€๋Šฅํ•œ ํŠธ๋ž˜ํ”ฝ์ด๋ผ๋ฉด ์šฐ์„  RDB๋กœ ๋กœ์ง์„ ๊ตฌํ˜„ ํ•ด ๋ณด๋Š”๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์ฃ .

ํ•ญ์ƒ ์€ํƒ„ํ™˜์€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”!

๋ง‰์ƒ Redis ๋ฅผ ๋„์ž…ํ•œ๋‹ค๊ณ  ํ•ด๋„ ๋ชจ๋“  ๊ฒŒ ํ•ด๊ฒฐ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
Redis ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ณต์ˆ˜๋„ ๋“ค์ฃ . ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฌ๋ ค๋‘”๋‹ค๋Š” ๊ฑด, ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฃฝ์œผ๋ฉด (ํ”ํžˆ ํ”„๋กœ๊ทธ๋žจ์ด ์ฃฝ์—ˆ๋‹ค๊ณ  ํ•˜์ฃ ) ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์‹ค๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ํ•˜๊ณค ํ•˜๋Š”๋ฐ...์ด๊ฒƒ๋„ ๊ฒฐ๊ตญ ์—”์ง€๋‹ˆ์–ด๋ง ๋น„์šฉ์ด์ฃ ...ํ•˜์ง€๋งŒ ํŠธ๋ž˜ํ”ฝ์ด ๋Š˜์–ด๋‚ฌ๋‹ค๋ฉด ๊ทธ ๋งŒํผ ๊ณ ๊ฐ๋“ค์„ ๋Œ€์‘ํ•ด์•ผ ํ•˜์ž–์•„์š”?
๋งˆ์น˜ ์‹๋‹น์ด ์ž˜ ๋˜๋ฉด ์ง์›์„ ๋ฝ‘๊ณ , ํ…Œ์ด๋ธ”์„ ๋Š˜๋ฆฌ๊ณ  ์žฅ๋น„๋ฅผ ๋„์ž…ํ•˜๋Š” ๋“ฑ...์„œ๋น„์Šค๋„ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์“ธ ๋„๊ตฌ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒƒ ๊ฐ™์•„์š”.

fun requestPayment(paymentId: String) {
    val machine = stateMachineFactory.getStateMachine(paymentId)
    machine.start()
    machine.sendEvent(PaymentEvent.REQUEST)
    persister.persist(machine, paymentId)

    // Redis์— ๊ฑฐ๋ž˜ ์ƒํƒœ ์ €์žฅ (TTL: 3๋ถ„)
    redisTemplate.opsForValue().set("payment:$paymentId", "PROCESSING", Duration.ofMinutes(3))
}

stateMachine ์ด ์ดˆ๊ธฐํ™” ๋˜๋Š” ์‹œ์ ์—์„œ Redis ์— paymentId ํ‚ค๊ฐ’์œผ๋กœ stateMachine ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. 3๋ถ„ TTL ์„ ์ง€์ • ํ•  ์ˆ˜ ์žˆ์ฃ .

fun approvePayment(paymentId: String) {
    val machine = stateMachineFactory.getStateMachine(paymentId)
    persister.restore(machine, paymentId)

    if (machine.state.id == PaymentState.PROCESSING) {
        machine.sendEvent(PaymentEvent.APPROVE)
        persister.persist(machine, paymentId)

        // Redis TTL ์ œ๊ฑฐ
        redisTemplate.delete("payment:$paymentId")
    }
}

์Šน์ธ์ด ๋งˆ๋ฌด๋ฆฌ๋˜๋ฉด Redis ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง€์›Œ์ค๋‹ˆ๋‹ค. ๋งŒ์•ฝ 3๋ถ„ ์•ˆ์— ์Šน์ธ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ Redis ์—์„œ ์•Œ์•„์„œ ์ง€์›Œ์ฃผ์ฃ .

๋งŒ์•ฝ 3๋ถ„์ด ์ง€๋‚˜์„œ ์œ ํšจ์‹œ๊ฐ„์ด ๋งŒ๋ฃŒ๊ฐ€ ๋˜๋ฉด Redis ์˜ Keyspace Notification ๊ธฐ๋Šฅ์„ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. TTL ๋งŒ๋ฃŒ ์‹œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์„œ State Machine ์„ ์ž๋™์œผ๋กœ EXPIRED ์ƒํƒœ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ฃ .

@Configuration
class RedisExpiredEventListener(
    private val stateMachineFactory: StateMachineFactory<PaymentState, PaymentEvent>
) {

    @Bean
    fun messageListenerAdapter(): MessageListenerAdapter {
        return MessageListenerAdapter(ExpiredKeyEventListener(stateMachineFactory))
    }

    @Bean
    fun redisMessageListenerContainer(
        connectionFactory: RedisConnectionFactory,
        listenerAdapter: MessageListenerAdapter
    ): RedisMessageListenerContainer {
        val container = RedisMessageListenerContainer()
        container.setConnectionFactory(connectionFactory)
        container.addMessageListener(listenerAdapter, PatternTopic("__keyevent@0__:expired"))
        return container
    }
}

์‚ฌ์ „ ์ž‘์—…์„ ํ•ด๋ณผ๊ฒŒ์š”. Redis ๊ฐ€ ๋ณด๋‚ด๋Š” ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” Config ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

class ExpiredKeyEventListener(
    private val stateMachineFactory: StateMachineFactory<PaymentState, PaymentEvent>
) : MessageListener {

    override fun onMessage(message: Message, pattern: ByteArray?) {
        val key = String(message.body)

        // ํ‚ค ๋„ค์ด๋ฐ ์˜ˆ์‹œ: "payment:PAYMENT_123"
        if (key.startsWith("payment:")) {
            val paymentId = key.removePrefix("payment:")
            val machine = stateMachineFactory.getStateMachine(paymentId)

            machine.start()
            machine.sendEvent(PaymentEvent.EXPIRED)
        }
    }
}

๊ฑฐ๋ž˜ ์„ธ์…˜ ์ •๋ณด๊ฐ€ TTL ์ด ์ง€๋‚˜์„œ ๋งŒ๋ฃŒ ์ฒ˜๋ฆฌ๊ฐ€ ๋˜์—ˆ๋‹ค๋ฉด, Redis ์—์„œ Spring ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์‹œ์ง€๊ฐ€ ์ „๋‹ฌ ๋˜์—ˆ์„ ๋•Œ ํ•ธ๋“ค๋Ÿฌ์—๊ฒŒ ๋งŒ๋ฃŒ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ํ• ๋‹น์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ์ฃ .

๐Ÿšช Outro

์ง€๊ธˆ๊นŒ์ง€ Finite State Machine ์— ๋Œ€ํ•œ ์ปดํ“จํ„ฐ๊ณตํ•™ ์ด๋ก  ์ง€์‹, ๊ทธ๋ฆฌ๊ณ  ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๋Š” Spring State Machine ์— ๋Œ€ํ•ด ๋”ฅ๋‹ค์ด๋ธŒ ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ฒ˜์Œ์—๋Š” ์‹ค๋ฌด ํˆด์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•˜๋‹ค๋ณด๋‹ˆ ๊ธฐ๋ฐ˜ ์ง€์‹์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ์ด ๋“ค์–ด์„œ ๊ธ€์„ ์“ฐ๊ธฐ ์‹œ์ž‘ํ–ˆ์–ด์š”. ์–ด์ฉŒ๋‹ค๊ฐ€ ์ปดํŒŒ์ผ๋Ÿฌ ์ด๋ก ๊นŒ์ง€ ๋‚˜์™”๋Š”๋ฐ, ํ”„๋ ˆ์ž„์›Œํฌ ํ•˜๋‚˜ํ•˜๋‚˜๊ฐ€ ์‚ฌ์‹ค์€ ์ˆจ์–ด์žˆ๋Š” ์ด๋ก  ์ง€์‹๋“ค๋กœ ์ผ๋งฅ์ƒํ†ต ํ•œ๋‹ค๋Š” ๋ง์„ ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

์ƒํƒœ๊ฐ€ ๋งŽ์•„์ง€๊ณ , ํ๋ฆ„์ด ๊ผฌ์ด๊ณ , ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ฉ์–ด๋ฆฌ์ฒ˜๋Ÿผ ๋ชฐ๋ฆฌ๋Š” ๋А๋‚Œ์ด ๋“ ๋‹ค๋ฉด ํ•œ ๋ฒˆ์ฏค FSM์œผ๋กœ ๊ทธ๋ ค๋ณด๊ณ , Spring State Machine์„ ๋„์ž…ํ•ด๋ณด๋Š” ๊ฑธ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”– Reference