Back-End/Spring

๋Œ€๊ทœ๋ชจ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ

Kr1 2026. 5. 14. 01:43

 

 

๐Ÿ—จ๏ธ ์˜ค๋ฆฌ์—”ํ…Œ์ด์…˜

์˜คํ”„๋‹

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์ด๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”?

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

๊ทธ์ค‘ ํ๋ฅผ ํ†ตํ•œ ๋Œ€๊ทœ๋ชจ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์— ์ง‘์ค‘ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

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

๊ฐœ๋ฐœํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

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

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ

๐Ÿ“Œ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ด๋– ํ•œ ๊ธฐ์ค€์œผ๋กœ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•ด์•ผ ํ• ๊นŒ์š”?

๋™์‹œ ์ ‘์†์ž์™€ ์ดˆ๋‹น ์š”์ฒญ๋Ÿ‰(TPS)

์‚ฌ์šฉ์ž์ˆ˜

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

TPS (TPS, Transactions Per Second)

  • TPS๋ž€?
    • TPS (Transactions Per Second)๋Š” ์ดˆ๋‹น ์ฒ˜๋ฆฌ๋˜๋Š” ํŠธ๋žœ์žญ์…˜์˜ ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ง€ํ‘œ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์„ ํ‰๊ฐ€ํ•˜๋Š” ์ค‘์š”ํ•œ ์ง€ํ‘œ ์ค‘ ํ•˜๋‚˜๋กœ, ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. TPS๋Š” ์‹œ์Šคํ…œ์ด ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์š”์ฒญ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์‹œ์Šคํ…œ์˜ ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ์„ ๊ฐ€๋Š ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค.
  • ์‹œ์Šคํ…œ์ด ์ดˆ๋‹น ์š”์ฒญ๋Ÿ‰(TPS, Transactions Per Second)์„ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ผ๊ฐ„ ์ ‘์†๋Ÿ‰์ด ์•„๋‹Œ, ํŠน์ • ์‹œ๊ฐ„๋Œ€์— ์ดˆ๋‹น ์ ‘์†์ž ์š”์ฒญ๋Ÿ‰์ด ๊ฐ€์žฅ ๋งŽ์€ ์‹œ๊ฐ„์„ ํŒŒ์•…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ •๋ณด๋Š” ์‹œ์Šคํ…œ์˜ ์šฉ๋Ÿ‰ ๊ณ„ํš์„ ์„ธ์šฐ๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ธฐ์กด ์‹œ์Šคํ…œ์ด ์˜ค์ „ 9์‹œ 30๋ถ„์— ์ดˆ๋‹น 200๊ฑด์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ์Šคํ…œ์˜ ์šฉ๋Ÿ‰์„ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ๋Š” ์‹œ์Šคํ…œ์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ตœ๋Œ€ ๋ถ€ํ•˜๋ฅผ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ 200๊ฑด์„ ๊ธฐ์ค€์œผ๋กœ 1.5๋ฐฐ์ธ 300๊ฑด์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ์Šคํ…œ์ด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ์—๋„ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๋Š” ์—ฌ์œ ๋ฅผ ์ œ๊ณตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ด๋ฒคํŠธ๋กœ ์ธํ•ด ์„ค๊ณ„ ์˜ˆ์ƒ ์ด์ƒ์˜ ์š”์ฒญ์ด ๋ชฐ๋ฆฐ๋‹ค๋ฉด ์‹œ์Šคํ…œ์ด ์ค‘๋‹จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์„ ๋Œ€๋น„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      1. ์ฒซ์งธ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.
      2. ๋‘˜์งธ, ์˜ค๋ฅ˜ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋Œ€๊ธฐ์—ด์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๋„ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
      3. ์…‹์งธ, ์ž๋™ ์Šค์ผ€์ผ๋ง์„ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์ž์›์„ ๋™์ ์œผ๋กœ ํ• ๋‹นํ•˜์—ฌ ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ค๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์š”์ฒญ ์ข…๋ฅ˜์— ๋”ฐ๋ฅธ ๊ฐœ๋ฐœ

  • ๐Ÿ“Œ ์‹œ์Šคํ…œ์ด ์ฝ๊ธฐ ์ „์šฉ์ธ์ง€, ์“ฐ๊ธฐ ๋ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ ๊ฒƒ์ธ์ง€๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ•˜์—ฌ ์‘๋‹ตํ•œ๋‹ค๋ฉด ๋ณด๋‹ค ๋งŽ์€ ์‚ฌ์šฉ์ž๋ฅผ ์ˆ˜์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ ์ œ๊ณต ๋ฐ ์ €์žฅ์—์„œ ๊ฐ€์žฅ ๋งŽ์€ ์‹œ๊ฐ„์„ ์†Œ๋ชจํ•˜๋Š” ๋ถ€๋ถ„์€ ๋Œ€๋ถ€๋ถ„ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ์“ฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • ์š”์ฒญ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ด ๋ถ€๋ถ„์˜ ํ—ˆ๋“ค์„ ์ตœ์†Œํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. 

์ฝ๊ธฐ ์š”์ฒญ ์ตœ์ ํ™”

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

๐Ÿ”ฅ Redis ์บ์‹œ ๋„์ž… ์‹œ ๋ฐ˜๋“œ์‹œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์‹ค๋ฌด ํฌ์ธํŠธ

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

๐Ÿšจ 1. ๋ฌด์‹ฌ์ฝ” ์“ด ์บ์‹œ๊ฐ€ ์„œ๋ฒ„๋ฅผ ์ฃฝ์ธ๋‹ค: ์šฉ๋Ÿ‰ ์ดˆ๊ณผ(OOM) ์œ„ํ—˜

  • Redis์˜ ์ตœ๋Œ€ ์šฉ๋Ÿ‰(์˜ˆ: 50GB)์ด ๋‹ค ์ฐจ๋ฒ„๋ฆฌ๋ฉด ์‹œ์Šคํ…œ ์ „์ฒด๊ฐ€ ๋‹ค์šด๋˜๋Š” ๋Œ€์ฐธ์‚ฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
    • ์›์ธ: Spring์—์„œ ์ œ๊ณตํ•˜๋Š” ์บ์‹œ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ฌด์‹ฌ์ฝ” ์‚ฌ์šฉํ•˜๋ฉด, ๊ฐ์ฒด ์ „์ฒด๊ฐ€ ์ง๋ ฌํ™”(๋ฌธ์ž์—ด ํ˜•ํƒœ)๋˜์–ด ์ €์žฅ๋˜๋ฉด์„œ ๋ถˆํ•„์š”ํ•œ ์ •๋ณด๊นŒ์ง€ ๋ง‰๋Œ€ํ•œ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. (์˜ˆ: 10KB์งœ๋ฆฌ ํŽ˜์ด์ง• ๋ฐ์ดํ„ฐ๋„ ์‚ฌ์šฉ์ž์™€ ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด ์ˆœ์‹๊ฐ„์— 50GB๋ฅผ ์ดˆ๊ณผํ•จ)
    • ํ•ด๊ฒฐ์ฑ…: ๊ฐ์ฒด๋ฅผ ํ†ต์งธ๋กœ ๋„ฃ์ง€ ๋ง๊ณ , ๊ผญ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์„ ๋ณ„์ ์œผ๋กœ ์ถ”์ถœํ•˜์—ฌ ๊ฐ€๋ณ๊ฒŒ ์บ์‹ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Š 2. ๊ณจ๋“ ํƒ€์ž„์„ ํ™•๋ณดํ•˜๋ผ: ์ด˜์ด˜ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์•Œ๋žŒ ์„ค์ •

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

๐Ÿ›ก๏ธ 3. ๋ ˆ๋””์Šค๊ฐ€ ์ฃฝ์–ด๋„ ์‹œ์Šคํ…œ์€ ์‚ด๋ ค๋ผ: ๋‹ค์ค‘ ๋ฐฉ์–ด์„  ๊ตฌ์ถ•

  • Redis์— ์žฅ์• ๊ฐ€ ์ƒ๊ฒผ๋‹ค๊ณ  ํ•ด์„œ ์ „์ฒด ์„œ๋น„์Šค๊ฐ€ ๋ฉˆ์ถ”๊ฑฐ๋‚˜ DB๊นŒ์ง€ ๋ป—๊ฒŒ ๋‘์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.
    • ์ปค๋„ฅ์…˜ ํƒ€์ž„์•„์›ƒ ์„ค์ • (ํ•„์ˆ˜): ํƒ€์ž„์•„์›ƒ์„ 3~4์ดˆ๋กœ ์งง๊ฒŒ ์„ค์ •ํ•˜์„ธ์š”. ์ด ์„ค์ •์ด ์—†๋‹ค๋ฉด Redis ์žฅ์•  ์‹œ ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์ด ๋ฉ”์ธ DB๋กœ ์ง์ ‘ ์Ÿ์•„์ ธ ๋‚ด๋ ค DB๋งˆ์ € ์—ฐ์‡„์ ์œผ๋กœ ๋‹ค์šด๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์œ ์—ฐํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง: ๋ ˆ๋””์Šค ์žฅ์•  ์‹œ์—๋Š” ๋นˆ ๋ฐ์ดํ„ฐ ๋˜๋Š” ๋…ธ๋ฐ์ดํ„ฐ ์‘๋‹ต์„ ์ฃผ์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์—๋Ÿฌ๋กœ ์ธ์‹๋˜์ง€ ์•Š๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ”„๋ก ํŠธ์—”๋“œ ๋”๋ฏธ ๋ฐ์ดํ„ฐ: ํ”„๋ก ํŠธ์—”๋“œ ๋‹จ์—์„œ ๋ฏธ๋ฆฌ ์ค€๋น„๋œ ๋”๋ฏธ(Dummy) ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก 2์ฐจ ๋ฐฉ์–ด์„ ์„ ๊ตฌ์ถ•ํ•˜๋ฉด, ์‚ฌ์šฉ์ž๋Š” ์žฅ์• ๋ฅผ ๋ˆˆ์น˜์ฑ„์ง€ ๋ชปํ•˜๊ณ  ์šด์˜์ž๋Š” ๊ธด์žฅ๊ฐ ์—†์ด ๋ณต๊ตฌ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿง  4. ์ค‘๋ณต ์ €์žฅ์€ ์ฃ„์•…์ด๋‹ค: ๋ฐ์ดํ„ฐ ํ†ตํ•ฉ ์ฒ˜๋ฆฌ์™€ ํ•„ํ„ฐ๋ง

  • ์‚ฌ์šฉ์ž A, B, C๊ฐ€ ๊ฐ์ž ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ, ์‚ฌ์šฉ์ž๋ณ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋”ฐ๋กœ ์บ์‹ฑํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ์Œ“์—ฌ ๊ธˆ๋ฐฉ ํฌํ™” ์ƒํƒœ(์ฒœ ๊ฐœ, ๋งŒ ๊ฐœ ์ด์ƒ)๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
    • ๋ฌธ์ œ์ : ์‚ฌ์šฉ์ž๋ณ„ ๋งž์ถค ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ๊ฐ ์ €์žฅํ•˜๋ฉด ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณ„์† ์ค‘๋ณต ์ ์žฌ๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‚ญ๋น„ํ•ฉ๋‹ˆ๋‹ค.
    • ํ•ด๊ฒฐ์ฑ… (์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ ํ•„ํ„ฐ๋ง):
      1. Redis์—๋Š” ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๊ณตํ†ต์œผ๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” '๊ฐ€๋ฒผ์šด ์ „์ฒด ๋ฐ์ดํ„ฐ ๋ชฉ๋ก(Common Data Set)' ๋”ฑ ํ•˜๋‚˜๋งŒ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
      2. ์‹ค์ œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ "์ด ๋ฐ์ดํ„ฐ๊ฐ€ A์—๊ฒŒ ํ•„์š”ํ•œ๊ฐ€?"๋ฅผ ํŒ๋‹จํ•˜์—ฌ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋ง(๊ฐ€๊ณต)ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • ํšจ๊ณผ: ์ €์žฅ ๋น„์šฉ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๊ทน์ ์œผ๋กœ ์ค„์ด๋ฉด์„œ๋„, ์œ ์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉ์ž ๋งž์ถคํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๐Ÿ’ก ์š”์•ฝํ•˜์ž๋ฉด: ๋Œ€์šฉ๋Ÿ‰ ํ™˜๊ฒฝ์—์„œ์˜ Redis๋Š” "๊ผญ ํ•„์š”ํ•œ ๊ณตํ†ต ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€๋ณ๊ฒŒ ์ €์žฅํ•˜๊ณ (์ตœ์ ํ™”), ์ฒ ์ €ํžˆ ๊ฐ์‹œํ•˜๋ฉฐ(๋ชจ๋‹ˆํ„ฐ๋ง), ๋งŒ์•ฝ์˜ ์‚ฌํƒœ์— ๋Œ€๋น„ํ•œ ํ”Œ๋žœ B(ํƒ€์ž„์•„์›ƒ ๋ฐ ๋”๋ฏธ ์‘๋‹ต)๋ฅผ ์„ธ์›Œ๋‘๋Š” ๊ฒƒ"์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

 

๐Ÿ—จ๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ๋ฑ์‹ฑ

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

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒค๋”ฉ

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒค๋”ฉ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์—ฌ๋Ÿฌ ์ƒค๋“œ(๋ถ„์‚ฐ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์กฐ๊ฐ)๋กœ ๋ถ„ํ• ํ•˜์—ฌ ๊ฐ๊ฐ์˜ ์ƒค๋“œ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‹จ์ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฝ๊ธฐ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ์ƒค๋“œ์— ๋ถ„์‚ฐ ์ €์žฅํ•˜์—ฌ ์ฝ๊ธฐ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด ํ…Œ์ด๋ธ” ํŒŒํ‹ฐ์…”๋‹์„ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ์˜ ์ƒค๋”ฉ์œผ๋กœ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ˆ˜์ค€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
-- ์ฃผ๋ฌธ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY, -- ์ฃผ๋ฌธ ID (์ž๋™ ์ฆ๊ฐ€ ๊ธฐ๋ณธ ํ‚ค)
  customer_id INT,             -- ์ฃผ๋ฌธํ•œ ๊ณ ๊ฐ ID
  order_date DATE,             -- ์ฃผ๋ฌธ ๋‚ ์งœ
  amount DECIMAL               -- ์ฃผ๋ฌธ ๊ธˆ์•ก
) PARTITION BY RANGE (order_date);
-- order_date(์ฃผ๋ฌธ ๋‚ ์งœ)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฒ”์œ„(RANGE) ํŒŒํ‹ฐ์…”๋‹ ์ˆ˜ํ–‰
-- ์ฆ‰ ๋‚ ์งœ๋ณ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ˆ„์–ด ์ €์žฅํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ


-- 2023๋…„ 1์›” ๋ฐ์ดํ„ฐ๋งŒ ์ €์žฅํ•˜๋Š” ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ”
CREATE TABLE orders_2023_01
PARTITION OF orders
FOR VALUES FROM ('2023-01-01') TO ('2023-02-01');
-- 2023-01-01 ์ด์ƒ ~ 2023-02-01 ๋ฏธ๋งŒ ๋ฐ์ดํ„ฐ ์ €์žฅ


-- 2023๋…„ 2์›” ๋ฐ์ดํ„ฐ๋งŒ ์ €์žฅํ•˜๋Š” ํŒŒํ‹ฐ์…˜ ํ…Œ์ด๋ธ”
CREATE TABLE orders_2023_02
PARTITION OF orders
FOR VALUES FROM ('2023-02-01') TO ('2023-03-01');
-- 2023-02-01 ์ด์ƒ ~ 2023-03-01 ๋ฏธ๋งŒ ๋ฐ์ดํ„ฐ ์ €์žฅ

 

์ฝ๊ธฐ ์ „์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

  • ์ฝ๊ธฐ ์ „์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ฃผ๋กœ ์ฝ๊ธฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋™๊ธฐํ™”ํ•˜์—ฌ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ์ฝ๊ธฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ฃผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ฝ๊ธฐ ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ  ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ฝ๊ธฐ ์ „์šฉ DB ๋ฐ Redis ์‚ฌ์šฉ ์‹œ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ์ „๋žต
      1. Redis์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐ(ํผ์ง€)ํ•œ ๋’ค write DB์—์„œ๋Š” ์ด๋ฏธ ๋ฐ์ดํ„ฐ๊ฐ€ 'abc'๋กœ ๋ณ€๊ฒฝ๋์ง€๋งŒ, read DB์—๋Š” ๋ฐ˜์˜์ด ์•ˆ ๋˜์–ด redis์— ๋‹ค์‹œ ๊ตฌ ๋ฐ์ดํ„ฐ('a')๊ฐ€ ์ ์žฌ๋˜๋Š” ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      2. ์ด ํ˜„์ƒ์€ ๋ฐ์ดํ„ฐ๊ฐ€ Redis์—์„œ ์ง€์›Œ์ง€๊ณ , read DB๋Š” ์•„์ง ์—…๋ฐ์ดํŠธ ์ „ ์ƒํƒœ๋ผ์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์˜ˆ์ „ ๋ฐ์ดํ„ฐ('a')๋ฅผ ๋‹ค์‹œ ์ฝ๋Š” ๊ฒฝ์šฐ์— ์ด‰๋ฐœ๋ฉ๋‹ˆ๋‹ค.
      3. ๋Œ€๊ทœ๋ชจ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ๋™์‹œ์— ์ฒ˜๋ฆฌ๋  ๋•Œ ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ๋”์šฑ ๋นˆ๋ฒˆํžˆ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      4. ๊ทผ๋ณธ ๋Œ€์ฑ…์€ ํผ์ง€(furge) ๋Œ€์‹  ๋ฐ์ดํ„ฐ ์ž์ฒด๋ฅผ Redis์—์„œ ์ตœ์‹  ๊ฐ’์œผ๋กœ ์ง์ ‘ ๊ฐฑ์‹ ํ•˜๊ฑฐ๋‚˜, ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ ์‹œ Redis๋ฅผ ํผ์ง€ํ•˜์—ฌ ๊ฐ•์ œ๋กœ DB์—์„œ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์บ์‹œ ๋ฐ์ดํ„ฐ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋Š” Redis ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•œ ๋’ค, ์ดํ›„ ์š”์ฒญ ์‹œ DB์—์„œ ๋‹ค์‹œ ์กฐํšŒํ•˜์—ฌ Redis์— ์žฌ์ €์žฅํ•˜๋„๋ก ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ์ตœ์ ํ™”

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

์“ฐ๊ธฐ ์š”์ฒญ ์ตœ์ ํ™”

  • ์“ฐ๊ธฐ์—์„œ ๊ฐ€์žฅ ๋งŽ์€ ์‹œ๊ฐ„์„ ์†Œ์š”ํ•˜๋Š” ๋ถ€๋ถ„์€ DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

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

๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

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

๋ถ„์‚ฐ DB

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

 

๐Ÿ—จ๏ธ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€

๐Ÿ“Œ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜, ์ด๋ฒคํŠธ ์†Œ์‹ฑ, CQRS(Command Query Responsibility Segregation) ๋“ฑ์˜ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜

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

์ฃผ์š” ๊ฐœ๋…

  • ํŠธ๋žœ์žญ์…˜(Transaction):
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ƒํƒœ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์˜ ๋‹จ์œ„๋กœ, ACID(์›์ž์„ฑ, ์ผ๊ด€์„ฑ, ๊ณ ๋ฆฝ์„ฑ, ์ง€์†์„ฑ) ์†์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
      1. ์›์ž์„ฑ (Atomicity): ํŠธ๋žœ์žญ์…˜์€ ์ „๋ถ€ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ์ „๋ถ€ ์‹คํŒจํ•˜์—ฌ, ๋ถ€๋ถ„์ ์ธ ์ž‘์—… ์ˆ˜ํ–‰์ด ์—†๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
      2. ์ผ๊ด€์„ฑ (Consistency): ํŠธ๋žœ์žญ์…˜์ด ์™„๋ฃŒ๋œ ํ›„์—๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋ชจ๋“  ๋ฌด๊ฒฐ์„ฑ ์ œ์•ฝ ์กฐ๊ฑด์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
      3. ๊ฒฉ๋ฆฌ์„ฑ (Isolation): ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ํŠธ๋žœ์žญ์…˜์ด ์„œ๋กœ ๊ฐ„์„ญํ•˜์ง€ ์•Š๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
      4. ์ง€์†์„ฑ (Durability): ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋œ ํ›„์˜ ๊ฒฐ๊ณผ๋Š” ์‹œ์Šคํ…œ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์˜๊ตฌ์ ์œผ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
  • ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜(Distributed Transaction):
    • ์—ฌ๋Ÿฌ ๋ถ„์‚ฐ๋œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๊ฑธ์ณ ํŠธ๋žœ์žญ์…˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๋Ÿฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋™์‹œ์— ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ด์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • 2PC (Two-Phase Commit):
    • ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœํ† ์ฝœ๋กœ, ์ค€๋น„(Prepare) ๋‹จ๊ณ„์™€ ์ปค๋ฐ‹(Commit) ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„์–ด ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
      1. ์ค€๋น„ ๋‹จ๊ณ„(Prepare Phase): ๊ฐ ์ฐธ์—ฌ ๋…ธ๋“œ๋Š” ํŠธ๋žœ์žญ์…˜ ์ค€๋น„ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ , ์ค€๋น„ ์™„๋ฃŒ๋ฅผ ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
      2. ์ปค๋ฐ‹ ๋‹จ๊ณ„(Commit Phase): ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ๋Š” ๋ชจ๋“  ์ฐธ์—ฌ ๋…ธ๋“œ๊ฐ€ ์ค€๋น„๋˜์—ˆ์Œ์„ ํ™•์ธํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ๋…ธ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ๊ฐ€ ํŒจํ„ด(Saga Pattern):
    • ํŠธ๋žœ์žญ์…˜์„ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์ปค๋ฐ‹๋ฉ๋‹ˆ๋‹ค. ์‹คํŒจ ์‹œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜์„ ์‹คํ–‰ํ•˜์—ฌ ์ƒํƒœ๋ฅผ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
      1. ์ฃผ๋ฌธ ์ƒ์„ฑ ๋‹จ๊ณ„: ์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
      2. ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋‹จ๊ณ„: ๊ฒฐ์ œ ์„œ๋น„์Šค๊ฐ€ ์ฃผ๋ฌธ ๊ฒฐ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
      3. ์žฌ๊ณ  ๊ฐ์†Œ ๋‹จ๊ณ„: ์žฌ๊ณ  ์„œ๋น„์Šค๊ฐ€ ์ฃผ๋ฌธ๋œ ์ƒํ’ˆ์˜ ์žฌ๊ณ ๋ฅผ ๊ฐ์†Œ์‹œํ‚ต๋‹ˆ๋‹ค.
      4. ๊ฐ ๋‹จ๊ณ„๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๊ณ , ์‹คํŒจํ•˜๋ฉด ์ด์ „ ๋‹จ๊ณ„์—์„œ ์ˆ˜ํ–‰๋œ ์ž‘์—…์„ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ ์†Œ์‹ฑ(Event Sourcing):
    • ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ด๋ฒคํŠธ๋กœ ๊ธฐ๋กํ•˜๊ณ , ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ƒํ•˜์—ฌ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      1. ๋ฐ์ดํ„ฐ์˜ ์ตœ์ข… ์ƒํƒœ๋งŒ ์ €์žฅํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, “๋ฌด์Šจ ์ผ์ด ๋ฐœ์ƒํ–ˆ๋Š”์ง€”๋ฅผ ์ด๋ฒคํŠธ ๋กœ๊ทธ์ฒ˜๋Ÿผ ์ˆœ์„œ๋Œ€๋กœ ์ €์žฅํ•ด๋‘๊ณ ,
        ๊ทธ ๊ธฐ๋ก๋“ค์„ ๋‹ค์‹œ ์ฝ์–ด ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹
      2. ์ฆ‰, ์ƒํƒœ๊ฐ’ ์ž์ฒด๋ณด๋‹ค “๋ณ€๊ฒฝ ๊ณผ์ •”์„ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹
      3. โ‘  5,000์› ์ž…๊ธˆ โ‘ก 10,000์› ์ž…๊ธˆ โ‘ข 5,000์› ์ถœ๊ธˆ โž” "์ด ๊ธฐ๋ก๋“ค(์ด๋ฒคํŠธ)์„ ์ˆœ์„œ๋Œ€๋กœ ์ญ‰ ๊ณ„์‚ฐ(์žฌ์ƒ)ํ•ด๋ณด๋‹ˆ ์ง€๊ธˆ 10,000์›์ด๋„ค!"
      4. ๋‚˜์ค‘์— ๋ถ„์‚ฐ๋œ ์‹œ์Šคํ…œ ๊ฐ„์— ๋ฐ์ดํ„ฐ๊ฐ€ ๊ผฌ์ด๋”๋ผ๋„, ๋ฐœ์ƒํ•œ ์ผ(์ด๋ฒคํŠธ)๋“ค์˜ ๊ธฐ๋ก์ด ๊ณ ์Šค๋ž€ํžˆ ๋‚จ์•„์žˆ์œผ๋‹ˆ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ญ‰ ๊ณ„์‚ฐํ•ด์„œ ์ •ํ™•ํ•œ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋งž์ถœ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒŒ ํ•ต์‹ฌ

๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์˜ ์žฅ์ 

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

๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์˜ ๋‹จ์ 

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

๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์˜ ์˜ˆ์‹œ

  • ์ฃผ๋ฌธ ์ƒ์„ฑ๊ณผ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ:
    • ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ์—์„œ ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•˜๊ณ , ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์ฃผ๋ฌธ ์„œ๋น„์Šค์™€ ๊ฒฐ์ œ ์„œ๋น„์Šค๊ฐ€ ๊ฐ๊ฐ ๋…๋ฆฝ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์„ ํ†ตํ•ด ๋‘ ์„œ๋น„์Šค๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ์ฃผ๋ฌธ๊ณผ ๊ฒฐ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ฃผ๋ฌธ ๋ฐ ๊ฒฐ์ œ ์ •๋ณด๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
  • 2PC๋ฅผ ์‚ฌ์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ:
    • ์ค€๋น„ ๋‹จ๊ณ„: ์ฃผ๋ฌธ ์„œ๋น„์Šค์™€ ๊ฒฐ์ œ ์„œ๋น„์Šค๊ฐ€ ํŠธ๋žœ์žญ์…˜์„ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ์„œ๋น„์Šค๊ฐ€ ๋ชจ๋‘ ์ค€๋น„ ์™„๋ฃŒ ์ƒํƒœ๋ฅผ ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
    • ์ปค๋ฐ‹ ๋‹จ๊ณ„: ๋งˆ์Šคํ„ฐ ๋…ธ๋“œ๋Š” ๋‘ ์„œ๋น„์Šค๊ฐ€ ๋ชจ๋‘ ์ค€๋น„๋˜์—ˆ์Œ์„ ํ™•์ธํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•˜๋‚˜์˜ ์„œ๋น„์Šค๋ผ๋„ ์ค€๋น„๋˜์ง€ ์•Š์•˜์œผ๋ฉด, ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
2PC๋Š” ์ค‘์•™ ์กฐ์œจ์ž๊ฐ€ ๋ชจ๋“  ์„œ๋น„์Šค์˜ ์ปค๋ฐ‹ ์—ฌ๋ถ€๋ฅผ ํ†ต์ œํ•˜๋Š” ๋ฐฉ์‹์ด๊ณ , Saga ํŒจํ„ด์€ ๊ฐ ์ž‘์—…์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋‹ค๊ฐ€ ์‹คํŒจ ์‹œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ด์ „ ์ž‘์—…์„ ๋˜๋Œ๋ฆฌ๋Š” ๋ฐฉ์‹์ด๋‹ค.

 

๐Ÿ—จ๏ธ 2PC๋ฅผ ์‚ฌ์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ

์ด๋ฒคํŠธ ์†Œ์‹ฑ

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

์ฃผ์š” ๊ฐœ๋…

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

์ด๋ฒคํŠธ ์†Œ์‹ฑ์˜ ์žฅ์ 

  • ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ด๋ ฅ ์ถ”์ :
    • ๋ชจ๋“  ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ด๋ฒคํŠธ๋กœ ๊ธฐ๋กํ•˜๋ฏ€๋กœ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ์‚ฌ์™€ ๋””๋ฒ„๊น…์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณต๊ตฌ ๋ฐ ์žฌ์ƒ:
    • ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ƒํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ ์†์‹ค์ด๋‚˜ ์‹œ์Šคํ…œ ์žฅ์•  ์‹œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • CQRS์™€์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ†ตํ•ฉ:
    • ์ด๋ฒคํŠธ ์†Œ์‹ฑ์€ CQRS(Command Query Responsibility Segregation)์™€ ์ž˜ ์–ด์šธ๋ฆฝ๋‹ˆ๋‹ค. ๋ช…๋ น๊ณผ ์กฐํšŒ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์„ฑ๋Šฅ๊ณผ ํ™•์žฅ์„ฑ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒคํŠธ ์†Œ์‹ฑ์˜ ๋‹จ์ 

  • ๋ณต์žก์„ฑ ์ฆ๊ฐ€:
    • ์‹œ์Šคํ…œ ์„ค๊ณ„์™€ ๊ตฌํ˜„์˜ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ๋ชจ๋ธ๋ง๊ณผ ์ด๋ฒคํŠธ ์Šคํ† ์–ด ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฝ๊ธฐ ์„ฑ๋Šฅ:
    • ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ƒํ•˜์—ฌ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ฝ๊ธฐ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์ ์…˜๊ณผ CQRS๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

CQRS

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

์ฃผ์š” ๊ฐœ๋…

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

CQRS์˜ ์žฅ์ 

  • ์„ฑ๋Šฅ ํ–ฅ์ƒ:
    • ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ ์ž‘์—…์— ์ตœ์ ํ™”๋œ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์™€ ์ธํ”„๋ผ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์กฐํšŒ ์„ฑ๋Šฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ฝ๊ธฐ ์ „์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์บ์‹œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ™•์žฅ์„ฑ:
    • ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ฝ๊ธฐ ์š”์ฒญ์ด ๋งŽ์€ ๊ฒฝ์šฐ ์กฐํšŒ ๋ชจ๋ธ์„ ์ˆ˜ํ‰์œผ๋กœ ํ™•์žฅํ•˜์—ฌ ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ์ง€๋ณด์ˆ˜์„ฑ:
    • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ช…๋ น ๋ชจ๋ธ์— ์ง‘์ค‘๋˜๋ฏ€๋กœ, ๋ณต์žกํ•œ ์ƒํƒœ ๋ณ€๊ฒฝ ๋กœ์ง์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ฝ๊ธฐ ๋ชจ๋ธ์€ ๋‹จ์ˆœํ™”๋˜์–ด ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ:
    • CQRS๋Š” ์ด๋ฒคํŠธ ์†Œ์‹ฑ๊ณผ ์ž˜ ์–ด์šธ๋ฆฝ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ์†Œ์‹ฑ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์ด๋ฒคํŠธ๋กœ ๊ธฐ๋กํ•˜๊ณ , ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ƒํ•˜์—ฌ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CQRS์˜ ๋‹จ์ 

  • ๋ณต์žก์„ฑ ์ฆ๊ฐ€:
    • ์‹œ์Šคํ…œ ์„ค๊ณ„์™€ ๊ตฌํ˜„์˜ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ช…๋ น ๋ชจ๋ธ๊ณผ ์กฐํšŒ ๋ชจ๋ธ์„ ๊ฐ๊ฐ ์„ค๊ณ„ํ•˜๊ณ  ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”:
    • ๋ช…๋ น ๋ชจ๋ธ๊ณผ ์กฐํšŒ ๋ชจ๋ธ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ถ”๊ฐ€์ ์ธ ๊ตฌํ˜„๊ณผ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น…

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

๋ชจ๋‹ˆํ„ฐ๋ง

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

์ฃผ์š”์‚ฌํ•ญ

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

 

๋กœ๊น…

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฃผ์š” ์ด๋ฒคํŠธ๋ฅผ ๋กœ๊น…ํ•˜์—ฌ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์›์ธ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ๋กœ๊ทธ๋Š” Elasticsearch, Logstash, Kibana(ELK ์Šคํƒ) ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ง‘, ์ €์žฅ, ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ณ , ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋กœ๊น…์€ ์‹œ์Šคํ…œ์˜ ๋ชจ๋“  ์ค‘์š”ํ•œ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋กํ•˜๋ฏ€๋กœ, ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ •ํ™•ํ•œ ์›์ธ์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค

์ฃผ์š”์‚ฌํ•ญ

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

 

ํ…Œ์ŠคํŠธ์™€ ๋ฐฐํฌ

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

ํ…Œ์ŠคํŠธ

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Test):
    • ์‹œ์Šคํ…œ์˜ ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ๊ฐ ๋ถ€๋ถ„์ด ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • JUnit, TestNG์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™ํ™”๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ๊ฐœ๋ฐœ ์ดˆ๊ธฐ ๋‹จ๊ณ„์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฐํ•จ์„ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(Integration Test):
    • ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
    • Spring Boot์—์„œ๋Š” @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์‹œ์Šคํ…œ์˜ ๋‹ค์–‘ํ•œ ๋ถ€๋ถ„๋“ค์ด ํ•จ๊ป˜ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜์—ฌ, ์ธํ„ฐํŽ˜์ด์Šค ๊ฐ„์˜ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ(Load Test):
    • ์‹œ์Šคํ…œ์ด ๋†’์€ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ๋„ ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
    • Apache JMeter์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๋ถ€ํ•˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์„ค์ •ํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ ํ•œ๊ณ„๋ฅผ ํŒŒ์•…ํ•˜๊ณ , ๋ณ‘๋ชฉ ์ง€์ ์„ ์ฐพ์•„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํšŒ๊ท€ ํ…Œ์ŠคํŠธ(Regression Test):
    • ์ƒˆ๋กœ์šด ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ๊ธฐ์กด ๊ธฐ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž๋™ํ™”ํ•˜์—ฌ ์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•จ์œผ๋กœ์จ, ์ฝ”๋“œ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ ๊ฒฐํ•จ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋Š” ์ง€์†์ ์ธ ์ฝ”๋“œ ๋ณ€๊ฒฝ์—๋„ ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ˆ˜์šฉ ํ…Œ์ŠคํŠธ(UAT, User Acceptance Test):
    • ์‹ค์ œ ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ์—์„œ ์‹œ์Šคํ…œ์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ, ์‚ฌ์šฉ์ž๊ฐ€ ์š”๊ตฌํ•˜๋Š” ๊ธฐ๋Šฅ์ด ๋ชจ๋‘ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•˜์—ฌ ์‹œ์Šคํ…œ์„ ์ตœ์ข… ์กฐ์ •ํ•˜๊ณ , ๋ฐฐํฌ ์ค€๋น„๋ฅผ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž ์ˆ˜์šฉ ํ…Œ์ŠคํŠธ๋Š” ์‹œ์Šคํ…œ์ด ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๊ธฐ๋Œ€๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

๋ฐฐํฌ

  • ์ง€์†์ ์ธ ํ†ตํ•ฉ(CI, Continuous Integration):
    • ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ€๊ฒฝํ•œ ์ฝ”๋“œ๋ฅผ ์ž์ฃผ, ์ž๋™์œผ๋กœ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜์—ฌ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ์ ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
    • Jenkins, GitLab CI, Travis CI์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CI ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • CI๋Š” ์ฝ”๋“œ ํ†ตํ•ฉ์„ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜์—ฌ ๊ฐœ๋ฐœ ์ฃผ๊ธฐ๋ฅผ ๋‹จ์ถ•์‹œํ‚ค๊ณ , ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  • ์ง€์†์ ์ธ ๋ฐฐํฌ(CD, Continuous Deployment):
    • CI ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ๊ฒ€์ฆ๋œ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.
    • Argo CD์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • CD๋Š” ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ์ ์šฉํ•˜์—ฌ, ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์‹ ์†ํ•˜๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Canary ๋ฐฐํฌ:
    • ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ์ „์ฒด ์‹œ์Šคํ…œ์— ๋ฐฐํฌํ•˜๊ธฐ ์ „์—, ์ผ๋ถ€ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ๋ฐฐํฌํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ ๋น ๋ฅด๊ฒŒ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • Canary ๋ฐฐํฌ๋Š” ๋ฆฌ์Šคํฌ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋‹จ๊ณ„์ ์œผ๋กœ ๋„์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.
  • ๋ธ”๋ฃจ-๊ทธ๋ฆฐ ๋ฐฐํฌ(Blue-Green Deployment):
    • ๋‘ ๊ฐœ์˜ ํ™˜๊ฒฝ(๋ธ”๋ฃจ์™€ ๊ทธ๋ฆฐ)์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•˜๋‚˜๋Š” ํ˜„์žฌ ์šด์˜ ์ค‘์ธ ํ™˜๊ฒฝ์ด๊ณ , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๋ฐฐํฌํ•˜๋Š” ํ™˜๊ฒฝ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๊ทธ๋ฆฐ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•œ ํ›„, ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์„ ๊ทธ๋ฆฐ ํ™˜๊ฒฝ์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ธ”๋ฃจ ํ™˜๊ฒฝ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋กค๋ฐฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ธ”๋ฃจ-๊ทธ๋ฆฐ ๋ฐฐํฌ๋Š” ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ฉฐ, ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์‹ ์†ํ•œ ๋ณต๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ๋กค๋ง ๋ฐฐํฌ(Rolling Deployment):
    • ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ์ ์ง„์ ์œผ๋กœ ๋ฐฐํฌํ•˜์—ฌ, ๊ฐ ์„œ๋ฒ„๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    • ์‹œ์Šคํ…œ ๊ฐ€๋™ ์‹œ๊ฐ„์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ ์ง„์ ์œผ๋กœ ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๋„์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋กค๋ง ๋ฐฐํฌ๋Š” ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

๐Ÿ—จ๏ธ RabbitMQ

RabbitMQ๋ž€

  • RabbitMQ๋Š” ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๋Š” ๋ฐ์ดํ„ฐ(๋ฉ”์‹œ์ง€)๋ฅผ ์†ก์‹ ์ž(ํ”„๋กœ๋“€์„œ)๋กœ๋ถ€ํ„ฐ ์ˆ˜์‹ ์ž(์ปจ์Šˆ๋จธ)์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ์ค‘๊ฐ„ ๋งค๊ฐœ์ฒด ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • RabbitMQ๋Š” ์ด๋Ÿฌํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ํ(queue)์— ์ €์žฅํ•˜๊ณ , ํ•„์š”ํ•  ๋•Œ ์ ์ ˆํ•œ ์ˆ˜์‹ ์ž์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

RabbitMQ์˜ ์—ญํ• 

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

์žฅ๋‹จ์ 

์žฅ์ 

  • ์‹ ๋ขฐ์„ฑ(Reliability)
    • ๋ฉ”์‹œ์ง€ ์ง€์†์„ฑ: RabbitMQ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋””์Šคํฌ์— ์ €์žฅํ•˜์—ฌ ์‹œ์Šคํ…œ ์žฅ์•  ๋ฐœ์ƒ ์‹œ์—๋„ ๋ฉ”์‹œ์ง€๊ฐ€ ์†์‹ค๋˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • ํ™•์ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜: ๋ฉ”์‹œ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์†Œ๋น„์ž์—๊ฒŒ ์ „๋‹ฌ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ACK(acknowledgment) ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์—ฐ์„ฑ(Flexibility)
    • ๋‹ค์–‘ํ•œ ๋ฉ”์‹œ์ง€ ํŒจํ„ด: RabbitMQ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ํŒจํ„ด(๋‹จ์ผ ์†Œ๋น„์ž, ๋‹ค์ค‘ ์†Œ๋น„์ž, ๋ผ์šด๋“œ ๋กœ๋นˆ, ํŒฌ์•„์›ƒ, ์ฃผ์ œ ๊ธฐ๋ฐ˜ ๋“ฑ)์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
    • ํ”„๋กœํ† ์ฝœ ์ง€์›: ๊ธฐ๋ณธ์ ์œผ๋กœ AMQP(Advanced Message Queuing Protocol)๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, STOMP, MQTT ๋“ฑ ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ํ™•์žฅ์„ฑ(Scalability)
    • ํด๋Ÿฌ์Šคํ„ฐ๋ง: RabbitMQ๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ๋…ธ๋“œ๋กœ ๊ตฌ์„ฑ๋œ ํ™˜๊ฒฝ์—์„œ ๋†’์€ ๊ฐ€์šฉ์„ฑ๊ณผ ๋ถ€ํ•˜ ๋ถ„์‚ฐ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ๋ถ„์‚ฐ ์•„ํ‚คํ…์ฒ˜: ํŽ˜๋”๋ ˆ์ด์…˜(Federation) ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ RabbitMQ ์ธ์Šคํ„ด์Šค ๊ฐ„์— ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ๋ถ„์‚ฐ๋œ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง(Manageability and Monitoring)
    • ๊ด€๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค: ์›น ๊ธฐ๋ฐ˜ ๊ด€๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ํ, ์ต์Šค์ฒด์ธ์ง€, ๋ฐ”์ธ๋”ฉ ๋“ฑ์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ: ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: ๊ด€๋ฆฌ ํ”Œ๋Ÿฌ๊ทธ์ธ, ๋ชจ๋‹ˆํ„ฐ๋ง ํ”Œ๋Ÿฌ๊ทธ์ธ ๋“ฑ).
  • ์„ฑ๋Šฅ(Performance)
    • ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰: ์ ์ ˆํžˆ ๊ตฌ์„ฑ๋œ RabbitMQ๋Š” ๋†’์€ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์ œ๊ณตํ•˜์—ฌ ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋„ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ 

  • ์„ค์ • ๋ฐ ์šด์˜ ๋ณต์žก์„ฑ(Setup and Operational Complexity)
    • ๋ณต์žกํ•œ ์„ค์ •: RabbitMQ์˜ ์ดˆ๊ธฐ ์„ค์ •์ด ๋‹ค์†Œ ๋ณต์žกํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฐ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ๋”์šฑ ๋งŽ์€ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ์šด์˜ ๊ด€๋ฆฌ: ๋Œ€๊ทœ๋ชจ ํ™˜๊ฒฝ์—์„œ RabbitMQ๋ฅผ ์šด์˜ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ ์ถ”๊ฐ€์ ์ธ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ ๋ฌธ์ œ(Performance Issues)
    • ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค ์˜ค๋ฒ„ํ—ค๋“œ: RabbitMQ๋Š” ๋ชจ๋“  ๋ฉ”์‹œ์ง€๋ฅผ ์ค‘์•™ ๋ธŒ๋กœ์ปค๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋†’์€ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ๋Š” ๋ธŒ๋กœ์ปค์˜ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋Œ€๊ทœ๋ชจ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ: ๋งค์šฐ ๋Œ€๊ทœ๋ชจ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ์ ์ ˆํ•œ ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฐ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ์šด์˜ ๋น„์šฉ(Operational Costs)
    • ๋ฆฌ์†Œ์Šค ์†Œ๋น„: RabbitMQ๋Š” ๋ฉ”๋ชจ๋ฆฌ์™€ CPU ์ž์›์„ ๋งŽ์ด ์†Œ๋น„ํ•  ์ˆ˜ ์žˆ์–ด, ์ถฉ๋ถ„ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•ด์•ผ ์›ํ™œํ•˜๊ฒŒ ์šด์˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์œ ์ง€๋ณด์ˆ˜: ์ง€์†์ ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ์ด๋ฅผ ์œ„ํ•ด ์ถ”๊ฐ€์ ์ธ ์ธ๋ ฅ๊ณผ ๋น„์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ œํ•œ๋œ ๋ฉ”์‹œ์ง€ ํฌ๊ธฐ(Limited Message Size)
    • ๋ฉ”์‹œ์ง€ ํฌ๊ธฐ ์ œํ•œ: RabbitMQ๋Š” ๋งค์šฐ ํฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ์— ์ œํ•œ์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ „์†ก์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋Ÿฌ๋‹ ์ปค๋ธŒ(Learning Curve)
    • ํ•™์Šต ํ•„์š”์„ฑ: RabbitMQ์˜ ๊ฐœ๋…๊ณผ ์„ค์ •์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

RabbitMQ์˜ ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ

  • ๋ฉ”์‹œ์ง€(Message)
    • ๋ฉ”์‹œ์ง€๋Š” RabbitMQ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๋Š” ๋ฐ์ดํ„ฐ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์ •๋ณด๋‚˜ ์ฃผ๋ฌธ ๋‚ด์—ญ์ด ๋ฉ”์‹œ์ง€๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœ๋“€์„œ(Producer)
    • ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  RabbitMQ์— ๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์ •๋ณด๋ฅผ RabbitMQ์— ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ ํ”„๋กœ๋“€์„œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
  • ํ(Queue)
    • ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€๋Š” ํ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ์†Œ๋น„์ž์—๊ฒŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ํ๋Š” FIFO(First In, First Out) ๋ฐฉ์‹์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ์ปจ์Šˆ๋จธ(Consumer)
    • ํ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ์™€ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ฉ”์ผ ๋ฐœ์†ก ์„œ๋น„์Šค๊ฐ€ ํ์—์„œ ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ํ™˜์˜ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ ์ปจ์Šˆ๋จธ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
  • ์ต์Šค์ฒด์ธ์ง€(Exchange)
    • ๋ฉ”์‹œ์ง€๋ฅผ ์ ์ ˆํ•œ ํ๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋“€์„œ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ง์ ‘ ํ์— ๋ณด๋‚ด์ง€ ์•Š๊ณ , ์ต์Šค์ฒด์ธ์ง€์— ๋ณด๋‚ด๋ฉฐ, ์ต์Šค์ฒด์ธ์ง€๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ ์ ˆํ•œ ํ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

RabbitMQ์™€ AMQP

  • RabbitMQ๋Š” AMQP(Advanced Message Queuing Protocol)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • AMQP๋Š” ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๋ฅผ ์œ„ํ•œ ํ”„๋กœํ† ์ฝœ๋กœ, ๋ฉ”์‹œ์ง€์˜ ์ƒ์„ฑ, ์ „์†ก, ํ์ž‰, ๋ผ์šฐํŒ… ๋“ฑ์„ ํ‘œ์ค€ํ™”ํ•˜์—ฌ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๊ฐ€ ์ƒํ˜ธ ์šด์šฉ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ํ”„๋กœํ† ์ฝœ(Protocol)์€ ์ปดํ“จํ„ฐ ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๊ธฐ ์œ„ํ•œ ์ผ๋ จ์˜ ๊ทœ์น™๊ณผ ์ ˆ์ฐจ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด, ์ปดํ“จํ„ฐ๋‚˜ ์žฅ์น˜๋“ค์ด ์„œ๋กœ ํ†ต์‹ ํ•  ๋•Œ ์–ด๋–ป๊ฒŒ ์†Œํ†ตํ•ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์ •ํ•ด ๋†“์€ ์•ฝ์†์ž…๋‹ˆ๋‹ค.

AMQP์˜ ์ฃผ์š” ๊ฐœ๋…

  • ๋ฉ”์‹œ์ง€(Message): ์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.
  • ํ(Queue): ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ์ „๋‹ฌํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.
  • ์ต์Šค์ฒด์ธ์ง€(Exchange): ๋ฉ”์‹œ์ง€๋ฅผ ํ๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ”์ธ๋”ฉ(Binding): ์ต์Šค์ฒด์ธ์ง€์™€ ํ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์„ค์ •์ž…๋‹ˆ๋‹ค. ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๊ฐ€ ์–ด๋А ํ๋กœ ์ „๋‹ฌ๋ ์ง€ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    • ํ์˜ ์ด๋ฆ„๊ณผ ๋ฐ”์ธ๋”ฉ์˜ ์ด๋ฆ„์€ ์ผ์น˜์‹œํ‚จ๋‹ค!

์ต์Šค์ฒด์ธ์ง€ ์œ ํ˜•

  • ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตํ™˜๊ธฐ์—์„œ ํ๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ์ต์Šค์ฒด์ธ์ง€๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ผ์šฐํŒ…ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฃผ๋กœ ๋ฉ”์‹œ์ง€์˜ ๋ผ์šฐํŒ… ํ‚ค์™€ ๋ฐ”์ธ๋”ฉ ํ‚ค ๋˜๋Š” ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

1) Direct Exchange

  • ๋ผ์šฐํŒ… ํ‚ค๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ํ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ผ์šฐํŒ… ํ‚ค๊ฐ€ error์ธ ๋ฉ”์‹œ์ง€๋Š” error๋ผ๋Š” ๋ฐ”์ธ๋”ฉ ํ‚ค๋ฅผ ๊ฐ€์ง„ ํ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.
→ ํ˜„์—…์—์„œ๋Š” ๊ฑฐ์˜ ๋‹ค ์ด๊ฑธ๋กœ ์ž‘์—…. ์ด๊ฒƒ๋งŒ ์ž˜ ์•Œ์•„๋‘์ž!

 

2) Topic Exchange

  • ๋ผ์šฐํŒ… ํ‚ค์˜ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค. ํŒจํ„ด์—๋Š” ์™€์ผ๋“œ์นด๋“œ * (๋‹จ์–ด ํ•˜๋‚˜)์™€ # (0๊ฐœ ์ด์ƒ์˜ ๋‹จ์–ด)๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ผ์šฐํŒ… ํ‚ค๊ฐ€ quick.orange.rabbit์ธ ๋ฉ”์‹œ์ง€๋Š” ๋ฐ”์ธ๋”ฉ ํ‚ค๊ฐ€ *.orange.*์ธ ํ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

3) Fanout Exchange

  • ๋ผ์šฐํŒ… ํ‚ค๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๊ตํ™˜๊ธฐ์— ๋ฐ”์ธ๋”ฉ๋œ ๋ชจ๋“  ํ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ๋ฐ”์ธ๋”ฉ๋œ ํ๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

4) Headers Exchange

  • ๋ผ์šฐํŒ… ํ‚ค ๋Œ€์‹  ๋ฉ”์‹œ์ง€์˜ ํ—ค๋”๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.
  • ํ—ค๋” ๊ฐ’๊ณผ ๋ฐ”์ธ๋”ฉ๋œ ํ—ค๋” ๊ฐ’์ด ์ผ์น˜ํ•˜๋Š” ํ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

์‹ค์Šต

 

 

RabbitMQ

  • ๋„์ปค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ RabbitMQ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
docker run -d --name rabbitmq -p5672:5672 -p 15672:15672 --restart=unless-stopped rabbitmq:management
  • localhost:15672์— ์ ‘์†ํ•˜๋ฉด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค. guest/guest๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ ‘์†ํ•˜๋ฉด ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Order Application

  • start.spring.io ์— ์ ‘์†ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด build.gradle์˜ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
# ํ˜„์žฌ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„
# Eureka ๊ฐ™์€ ์„œ๋น„์Šค ๋“ฑ๋ก ์‹œ ์‚ฌ์šฉ๋˜๊ธฐ๋„ ํ•จ
spring.application.name=order

# RabbitMQ Exchange ์ด๋ฆ„
# Exchange๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋А Queue๋กœ ๋ณด๋‚ผ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์ค‘๊ฐ„ ๋ผ์šฐํ„ฐ ์—ญํ• 
message.exchange=market

# ์ƒํ’ˆ ์„œ๋น„์Šค์šฉ Queue ์ด๋ฆ„
# Product ๊ด€๋ จ ๋ฉ”์‹œ์ง€๊ฐ€ ์ €์žฅ๋  Queue
message.queue.product=market.product

# ๊ฒฐ์ œ ์„œ๋น„์Šค์šฉ Queue ์ด๋ฆ„
# Payment ๊ด€๋ จ ๋ฉ”์‹œ์ง€๊ฐ€ ์ €์žฅ๋  Queue
message.queue.payment=market.payment

# RabbitMQ ์„œ๋ฒ„ ์ฃผ์†Œ
# localhost = ํ˜„์žฌ ๋‚ด ์ปดํ“จํ„ฐ
spring.rabbitmq.host=localhost

# RabbitMQ ๊ธฐ๋ณธ ์—ฐ๊ฒฐ ํฌํŠธ
# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด RabbitMQ์™€ ํ†ต์‹ ํ•  ๋•Œ ์‚ฌ์šฉ
spring.rabbitmq.port=5672

# RabbitMQ ๋กœ๊ทธ์ธ ๊ณ„์ •
spring.rabbitmq.username=guest
OrderApplicationQueueConfig.java
// RabbitMQ์˜ Queue, Exchange, Binding ๋“ฑ์„
// ์Šคํ”„๋ง Bean์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ์„ค์ • ํด๋ž˜์Šค

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// ์Šคํ”„๋ง ์„ค์ • ํด๋ž˜์Šค๋ผ๋Š” ์˜๋ฏธ
// ๋‚ด๋ถ€ @Bean ๋ฉ”์„œ๋“œ๋“ค์„ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•จ
public class OrderApplicationQueueConfig {

    // application.properties ๊ฐ’ ์ฃผ์ž…
    // ${...} ๋ฌธ๋ฒ•์œผ๋กœ properties ๊ฐ’ ๊ฐ€์ ธ์˜ด
    @Value("${message.exchange}")
    private String exchange;

    @Value("${message.queue.product}")
    private String queueProduct;

    @Value("${message.queue.payment}")
    private String queuePayment;

    @Bean
    // RabbitMQ Exchange ์ƒ์„ฑ
    // Exchange๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ ์ ˆํ•œ Queue๋กœ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• 
    public TopicExchange exchange() {
        return new TopicExchange(exchange);
    }

    @Bean
    // ์ƒํ’ˆ Queue ์ƒ์„ฑ
    // Queue๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์‹ค์ œ ์ €์žฅ๋˜๋Š” ๊ณต๊ฐ„
    public Queue queueProduct() {
        return new Queue(queueProduct);
    }

    @Bean
    // ๊ฒฐ์ œ Queue ์ƒ์„ฑ
    public Queue queuePayment() {
        return new Queue(queuePayment);
    }

    @Bean
    // Product Queue์™€ Exchange ์—ฐ๊ฒฐ
    // with(queueProduct) ๋ถ€๋ถ„์€ Routing Key
    // ์ฆ‰ ์–ด๋–ค ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋–ค Queue๋กœ ๋ณด๋‚ผ์ง€ ๊ธฐ์ค€๊ฐ’
    public Binding bindingProduct() {
        return BindingBuilder
                .bind(queueProduct()) // Queue ์„ ํƒ
                .to(exchange())       // Exchange ์—ฐ๊ฒฐ
                .with(queueProduct);  // Routing Key ์„ค์ •
    }

    @Bean
    // Payment Queue์™€ Exchange ์—ฐ๊ฒฐ
    public Binding bindingPayment() {
        return BindingBuilder
                .bind(queuePayment())
                .to(exchange())
                .with(queuePayment);
    }
}
OrderController.java
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
// REST API Controller
// ๋ฐ˜ํ™˜๊ฐ’์ด JSON/String ํ˜•ํƒœ๋กœ HTTP ์‘๋‹ต๋จ
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ž๋™ ์ƒ์„ฑ
// ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์‰ฝ๊ฒŒ ํ•ด์ฃผ๋Š” Lombok ๊ธฐ๋Šฅ
public class OrderController {

    // OrderService ์˜์กด์„ฑ ์ฃผ์ž…
    // final = ์ƒ์„ฑ์ž ์ฃผ์ž… ๋Œ€์ƒ
    private final OrderService orderService;

    @GetMapping("/order/{id}")
    // GET ์š”์ฒญ ๋งคํ•‘
    // ex) /order/1
    public String order(@PathVariable String id) {

        // URL์˜ {id} ๊ฐ’์„ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์Œ
        // ex) /order/1 -> id = "1"

        // ์ฃผ๋ฌธ ์ƒ์„ฑ ๋กœ์ง ์‹คํ–‰
        orderService.createOrder(id);

        return "Order complete";
    }
}
OrderService.java
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
// ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น ํด๋ž˜์Šค
@RequiredArgsConstructor
public class OrderService {

    // ์ƒํ’ˆ Queue ์ด๋ฆ„ ์ฃผ์ž…
    @Value("${message.queue.product}")
    private String productQueue;

    // ๊ฒฐ์ œ Queue ์ด๋ฆ„ ์ฃผ์ž…
    @Value("${message.queue.payment}")
    private String paymentQueue;

    // RabbitMQ ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    // RabbitTemplate = RabbitMQ์™€ ํ†ต์‹ ํ•˜๋Š” ํ•ต์‹ฌ ํด๋ž˜์Šค
    private final RabbitTemplate rabbitTemplate;

    public void createOrder(String orderId) {

        // ์ƒํ’ˆ Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
        // convertAndSend():
        // ๊ฐ์ฒด๋ฅผ ๋ฉ”์‹œ์ง€๋กœ ๋ณ€ํ™˜(convert) ํ›„ ์ „์†ก(send)
        //
        // ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ:
        // ์–ด๋–ค Queue(Routing Key)๋กœ ๋ณด๋‚ผ์ง€
        //
        // ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ:
        // ์‹ค์ œ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ
        rabbitTemplate.convertAndSend(productQueue, orderId);

        // ๊ฒฐ์ œ Queue๋กœ ๋™์ผํ•œ ์ฃผ๋ฌธ ID ์ „์†ก
        // ์ฆ‰ ํ•˜๋‚˜์˜ ์ฃผ๋ฌธ ์š”์ฒญ์„
        // Product Service์™€ Payment Service๊ฐ€ ๊ฐ๊ฐ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
        rabbitTemplate.convertAndSend(paymentQueue, orderId);
    }
}

 

์ฃผ๋ฌธ ์š”์ฒญ ๋ฐœ์ƒ
↓
OrderService
↓
RabbitMQ๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
↓
Product Queue ์ €์žฅ
Payment Queue ์ €์žฅ
↓
๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ

 

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋Ÿฐํ•˜์—ฌ /order/1 ๋กœ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ๊ทธ ํ›„ http://localhost:15672๋กœ ์ ‘์†ํ•˜์—ฌ RabbitMQ์˜ Exchange์™€ Queue๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ Queue ์—์„œ๋Š” ํ˜„์žฌ ๋ฐœํ–‰๋œ ๋ฉ”์‹œ์ง€๊ฐ€ Total์— ์Œ“์—ฌ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Queue and Stream ํŽ˜์ด์ง€์—์„œ ํ ์ด๋ฆ„์„ ํด๋ฆญํ•˜์—ฌ ์ƒ์„ธํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ ํ›„ ์Šคํฌ๋กคํ•˜์—ฌ Get Messages ์„น์…˜์œผ๋กœ ๊ฐ€์„œ Get Message๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ˜„์žฌ ํ์— ์Œ“์—ฌ ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์กฐํšŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Payment Application

  • start.spring.io ์— ์ ‘์†ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด build.gradle์˜ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
payment - application.properties
# ํ˜„์žฌ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„
# ๋กœ๊ทธ๋‚˜ ์„œ๋น„์Šค ์‹๋ณ„์šฉ์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
spring.application.name=payment

# Payment Service๊ฐ€ ๊ตฌ๋…ํ•  Queue ์ด๋ฆ„
# Order Service์—์„œ market.payment๋กœ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€๋ฅผ ์—ฌ๊ธฐ์„œ ๋ฐ›์Œ
message.queue.payment=market.payment

# RabbitMQ ์„œ๋ฒ„ ์ฃผ์†Œ
# localhost = ๋‚ด ์ปดํ“จํ„ฐ์—์„œ ์‹คํ–‰ ์ค‘์ธ RabbitMQ
spring.rabbitmq.host=localhost

# RabbitMQ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—ฐ๊ฒฐ ํฌํŠธ
spring.rabbitmq.port=5672

# RabbitMQ ๋กœ๊ทธ์ธ ๊ณ„์ •
spring.rabbitmq.username=guest

# RabbitMQ ๋กœ๊ทธ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ
spring.rabbitmq.password=guest
PaymentEndpoint.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
// log.info(), log.error() ๊ฐ™์€ ๋กœ๊ทธ ๊ฐ์ฒด๋ฅผ ์ž๋™ ์ƒ์„ฑํ•ด์ฃผ๋Š” Lombok ์–ด๋…ธํ…Œ์ด์…˜
@Component
// ์Šคํ”„๋ง Bean์œผ๋กœ ๋“ฑ๋ก
// Controller๋Š” HTTP ์š”์ฒญ์„ ๋ฐ›๋Š” ์—ญํ• ์ด๊ณ ,
// ์—ฌ๊ธฐ์„œ๋Š” RabbitMQ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š” ๊ฐ์ฒด๋ผ Component๋กœ ๋“ฑ๋ก
public class PaymentEndpoint {

    @Value("${spring.application.name}")
    // application.properties์˜ spring.application.name ๊ฐ’์„ ์ฃผ์ž…
    // ์—ฌ๊ธฐ์„œ๋Š” "payment" ๊ฐ’์ด ๋“ค์–ด๊ฐ
    private String appName;

    @RabbitListener(queues = "${message.queue.payment}")
    // RabbitMQ Queue๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ
    // market.payment Queue์— ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋จ
    public void receiveMessage(String orderId) {

        // Queue์—์„œ ๊บผ๋‚ธ ๋ฉ”์‹œ์ง€๊ฐ€ orderId ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜ด
        // Order Service์—์„œ "1"์„ ๋ณด๋ƒˆ๋‹ค๋ฉด orderId = "1"
        log.info("receive orderId:{}, appName : {}", orderId, appName);

        // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์—ฌ๊ธฐ์„œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•จ
        // ex) ๊ฒฐ์ œ ์Šน์ธ, ๊ฒฐ์ œ ์ƒํƒœ ๋ณ€๊ฒฝ, ๊ฒฐ์ œ ์‹คํŒจ ์ฒ˜๋ฆฌ ๋“ฑ
    }
}
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด receiveMessage์˜ ๋กœ๊ทธ๊ฐ€ ์ฐํžˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Order ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐœํ–‰ํ•œ ๋ฉ”์‹œ์ง€๊ฐ€ Payment Consumer๊ฐ€ ์‹คํ–‰๋˜์ž๋งˆ์ž ์†Œ๋ชจ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

Payment ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
↓
@RabbitListener๊ฐ€ market.payment Queue ๊ฐ์‹œ ์‹œ์ž‘
↓
Queue ์•ˆ์— ๋ฉ”์‹œ์ง€ ์žˆ์Œ
↓
์ž๋™์œผ๋กœ ๋ฉ”์‹œ์ง€ ๊บผ๋ƒ„
↓
receiveMessage(String orderId) ์‹คํ–‰
↓
log.info(...) ์ฐํž˜
↓
์ •์ƒ ์ฒ˜๋ฆฌ๋˜๋ฉด ๋ฉ”์‹œ์ง€ ack
↓
Queue์—์„œ ๋ฉ”์‹œ์ง€ ์‚ฌ๋ผ์ง

 

Product Application

  • start.spring.io ์— ์ ‘์†ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด build.gradle์˜ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
product - application.properties
# ํ˜„์žฌ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„
# ๋กœ๊ทธ๋‚˜ ์„œ๋น„์Šค ์‹๋ณ„์šฉ์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
spring.application.name=product

# Product Service๊ฐ€ ๊ตฌ๋…ํ•  Queue ์ด๋ฆ„
# Order Service์—์„œ market.product๋กœ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€๋ฅผ ์—ฌ๊ธฐ์„œ ๋ฐ›์Œ
message.queue.product=market.product

# RabbitMQ ์„œ๋ฒ„ ์ฃผ์†Œ
spring.rabbitmq.host=localhost

# RabbitMQ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—ฐ๊ฒฐ ํฌํŠธ
spring.rabbitmq.port=5672

# RabbitMQ ๋กœ๊ทธ์ธ ๊ณ„์ •
spring.rabbitmq.username=guest

# RabbitMQ ๋กœ๊ทธ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ
spring.rabbitmq.password=guest
ProductEndpoint.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Slf4j
// ๋กœ๊ทธ ์ถœ๋ ฅ์„ ์œ„ํ•œ Lombok ์–ด๋…ธํ…Œ์ด์…˜
@Component
// ์Šคํ”„๋ง์ด ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด๋กœ ๋“ฑ๋ก
// RabbitMQ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋˜์–ด์•ผ ํ•จ
public class ProductEndpoint {

    @Value("${spring.application.name}")
    // application.properties์˜ spring.application.name ๊ฐ’์„ ๊ฐ€์ ธ์˜ด
    // ์—ฌ๊ธฐ์„œ๋Š” "product" ๊ฐ’์ด ๋“ค์–ด๊ฐ
    private String appName;

    @RabbitListener(queues = "${message.queue.product}")
    // market.product Queue๋ฅผ ๊ฐ์‹œํ•จ
    // ํ•ด๋‹น Queue์— ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด receiveMessage()๊ฐ€ ์ž๋™ ํ˜ธ์ถœ๋จ
    public void receiveMessage(String orderId) {

        // RabbitMQ์—์„œ ๋ฐ›์€ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ
        // Order Service์—์„œ ๋ณด๋‚ธ ์ฃผ๋ฌธ ID๋ฅผ ํ™•์ธํ•˜๋Š” ์šฉ๋„
        log.info("receive orderId:{}, appName : {}", orderId, appName);

        // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์—ฌ๊ธฐ์„œ ์ƒํ’ˆ/์žฌ๊ณ  ๊ด€๋ จ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•จ
        // ex) ์žฌ๊ณ  ํ™•์ธ, ์žฌ๊ณ  ์ฐจ๊ฐ, ์ƒํ’ˆ ์ƒํƒœ ๋ณ€๊ฒฝ ๋“ฑ
    }
}

 

Order Service
↓ ๋ฉ”์‹œ์ง€ ์ „์†ก
RabbitMQ Queue
↓
Payment Service๋Š” market.payment๋ฅผ ๋“ฃ๊ณ  ์žˆ์Œ
Product Service๋Š” market.product๋ฅผ ๋“ฃ๊ณ  ์žˆ์Œ
↓
๊ฐ์ž ์ž๊ธฐ Queue์— ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ž๋™ ์‹คํ–‰

 

  • ์ปจ์Šˆ๋จธ๊ฐ€ ๋ผ์šด๋“œ๋กœ๋นˆ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ Intellij์—์„œ ๊ตฌ์„ฑํŽธ์ง‘์— ๋“ค์–ด๊ฐ€ ๋‘ ๊ฐœ์˜ Product๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. application.name ์˜ต์…˜์„ ํ†ตํ•ด 2๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

  • order์—์„œ ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค Product Application์ด ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉด์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹  ๋ฐ›๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

RabbitMQ ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์ •๋ฆฌ

  • ์ด๋ฒˆ ์˜ˆ์ œ๋Š” Order, Product, Payment ์ด 3๊ฐœ์˜ Spring Boot ํ”„๋กœ์ ํŠธ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
  • ๊ฐ ํ”„๋กœ์ ํŠธ๋Š” ์ง์ ‘ HTTP๋กœ ์„œ๋กœ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, RabbitMQ Queue๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”๋‹ค.
ํ”„๋กœ์ ํŠธ ์—ญํ•  ์ฃผ์š” ์ฝ”๋“œ Queue
Order ์ฃผ๋ฌธ ์š”์ฒญ์„ ๋ฐ›๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” Producer OrderController, OrderService, OrderApplicationQueueConfig market.product, market.payment๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
Product ์ƒํ’ˆ/์žฌ๊ณ  ๊ด€๋ จ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋Š” Consumer ProductEndpoint market.product ๊ตฌ๋…
Payment ๊ฒฐ์ œ ๊ด€๋ จ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋Š” Consumer PaymentEndpoint market.payment ๊ตฌ๋…

 

๊ฐ ํ”„๋กœ์ ํŠธ์˜ ์ฐจ์ด

๊ตฌ๋ถ„ Order ํ”„๋กœ์ ํŠธ Product ํ”„๋กœ์ ํŠธ Payment ํ”„๋กœ์ ํŠธ
์—ญํ•  ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๋Š” ์ชฝ ๋ฉ”์‹œ์ง€ ๋ฐ›๋Š” ์ชฝ ๋ฉ”์‹œ์ง€ ๋ฐ›๋Š” ์ชฝ
RabbitMQ ๊ธฐ์ค€ Producer Consumer Consumer
์ฃผ์š” ์–ด๋…ธํ…Œ์ด์…˜ @RestController, RabbitTemplate @RabbitListener @RabbitListener
ํ•˜๋Š” ์ผ /order/{id} ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด Queue์— ์ฃผ๋ฌธ ID ์ „์†ก market.product Queue์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ์ƒํ’ˆ ์ฒ˜๋ฆฌ market.payment Queue์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
์‹คํ–‰ ๊ฒฐ๊ณผ Queue์— ๋ฉ”์‹œ์ง€๊ฐ€ ์Œ“์ž„ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋ฉด Queue์—์„œ ์‚ฌ๋ผ์ง ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋ฉด Queue์—์„œ ์‚ฌ๋ผ์ง

 

์ „์ฒด ํ๋ฆ„

  1. ์‚ฌ์šฉ์ž๊ฐ€ Order ํ”„๋กœ์ ํŠธ์— /order/1 ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
  2. Order Service๋Š” RabbitMQ์— ์ฃผ๋ฌธ ID 1์„ ๋ฉ”์‹œ์ง€๋กœ ๋ฐœํ–‰ํ•œ๋‹ค.
  3. ๋ฉ”์‹œ์ง€๋Š” ๊ฐ๊ฐ market.product, market.payment Queue์— ๋“ค์–ด๊ฐ„๋‹ค.
  4. Product ํ”„๋กœ์ ํŠธ๋Š” market.product Queue๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ๊ฐ„๋‹ค.
  5. Payment ํ”„๋กœ์ ํŠธ๋Š” market.payment Queue๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ๊ฐ„๋‹ค.
  6. Consumer๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ •์ƒ ์ฒ˜๋ฆฌํ•˜๋ฉด Queue์—์„œ ๋ฉ”์‹œ์ง€๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค.

ํ•ต์‹ฌ ์ •๋ฆฌ

  • RabbitMQ ๊ตฌ์กฐ์—์„œ๋Š” Order ์„œ๋น„์Šค๊ฐ€ Product, Payment ์„œ๋น„์Šค๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋Œ€์‹  RabbitMQ์— ๋ฉ”์‹œ์ง€๋งŒ ๋„ฃ๊ณ , Product์™€ Payment ์„œ๋น„์Šค๊ฐ€ ๊ฐ์ž ์ž์‹ ์˜ Queue๋ฅผ ๊ตฌ๋…ํ•˜์—ฌ ํ•„์š”ํ•œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ์ฆ‰, ์„œ๋น„์Šค ๊ฐ„ ์ง์ ‘ ์˜์กด์„ฑ์„ ์ค„์ด๊ณ  ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ—จ๏ธ Kafka

Kafka๋ž€?

  • Kafka๋Š” ๋ถ„์‚ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ์œผ๋กœ, ์ฃผ๋กœ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํ”ผ๋“œ์˜ ๋น… ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ๋ถ„์‚ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ ํ’€์–ด์„œ ๋งํ•˜๋ฉด:
      1. ๋ถ„์‚ฐ = ์—ฌ๋Ÿฌ ์„œ๋ฒ„๊ฐ€ ๋‚˜๋ˆ  ์ฒ˜๋ฆฌ
      2. ์ŠคํŠธ๋ฆฌ๋ฐ = ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณ„์† ํ๋ฆ„
      3. ํ”Œ๋žซํผ = ๊ทธ๊ฑธ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ
    • Kafka๋Š” ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—ฌ๋Ÿฌ ์„œ๋ฒ„์— ์ „๋‹ฌํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ ์ €์žฅ/์ „์†ก ์‹œ์Šคํ…œ์ด๋‹ค.
  • Kafka๋Š” ๋ฉ”์‹œ์ง€ ํ์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์ €์žฅํ•˜๊ณ  ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ถ„์„ํ•˜๊ฑฐ๋‚˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.
    • RabbitMQ๊ฐ€ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ์ž์ฒด์— ์ดˆ์ ์„ ๋‘”๋‹ค๋ฉด, Kafka๋Š” ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์ €์žฅํ•˜๊ณ  ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋” ๊ฐ•์ ์„ ๊ฐ€์ง„๋‹ค.

Kafka์˜ ์—ญํ• 

  • ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ: ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ํ†ตํ•ฉ: ๋‹ค์–‘ํ•œ ์†Œ์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‚ด๊ฒฐํ•จ์„ฑ: ๋ฐ์ดํ„ฐ ์†์‹ค ์—†์ด ์•ˆ์ •์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

์žฅ๋‹จ์ 

์žฅ์ 

  • ์‹ ๋ขฐ์„ฑ(Reliability)
    • ๋ฐ์ดํ„ฐ ๋ณต์ œ: ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ธŒ๋กœ์ปค์— ๋ณต์ œํ•˜์—ฌ ์ €์žฅํ•˜๋ฏ€๋กœ, ๋‹จ์ผ ๋ธŒ๋กœ์ปค ์žฅ์•  ์‹œ์—๋„ ๋ฐ์ดํ„ฐ ์†์‹ค์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ™•์ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜: ๋ฐ์ดํ„ฐ๊ฐ€ ์†Œ๋น„์ž์—๊ฒŒ ์„ฑ๊ณต์ ์œผ๋กœ ์ „๋‹ฌ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์—ฐ์„ฑ(Flexibility)
    • ๋‹ค์–‘ํ•œ ์†Œ๋น„์ž ํŒจํ„ด: ์—ฌ๋Ÿฌ ์†Œ๋น„์ž๊ฐ€ ๋™์‹œ์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ”„๋กœํ† ์ฝœ ์ง€์›: ๊ธฐ๋ณธ์ ์œผ๋กœ Kafka์˜ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๋‹ค์–‘ํ•œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ™•์žฅ์„ฑ(Scalability)
    • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ: ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ๋…ธ๋“œ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ˆ˜ํ‰ ํ™•์žฅ: ๋ธŒ๋กœ์ปค์™€ ํŒŒํ‹ฐ์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ(Performance)
    • ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰: ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ €์ง€์—ฐ: ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ์ง€์—ฐ์„ ์ตœ์†Œํ™”ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง(Manageability and Monitoring)
    • ๊ด€๋ฆฌ ๋„๊ตฌ: ๋‹ค์–‘ํ•œ ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ: ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ์ 

  • ์„ค์ • ๋ฐ ์šด์˜ ๋ณต์žก์„ฑ(Setup and Operational Complexity)
    • ๋ณต์žกํ•œ ์„ค์ •: ์ดˆ๊ธฐ ์„ค์ •์ด ๋‹ค์†Œ ๋ณต์žกํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฐ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ๋Š” ๋” ๋งŽ์€ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    • ์šด์˜ ๊ด€๋ฆฌ: ๋Œ€๊ทœ๋ชจ ํ™˜๊ฒฝ์—์„œ ์šด์˜ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์ถ”๊ฐ€์ ์ธ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ ๋ฌธ์ œ(Performance Issues)
    • ๋ธŒ๋กœ์ปค ์˜ค๋ฒ„ํ—ค๋“œ: ๋†’์€ ํŠธ๋ž˜ํ”ฝ ์ƒํ™ฉ์—์„œ๋Š” ๋ธŒ๋กœ์ปค์˜ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋Œ€๊ทœ๋ชจ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ: ๋งค์šฐ ๋Œ€๊ทœ๋ชจ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ์ ์ ˆํ•œ ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฐ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ์šด์˜ ๋น„์šฉ(Operational Costs)
    • ๋ฆฌ์†Œ์Šค ์†Œ๋น„: ๋ฉ”๋ชจ๋ฆฌ์™€ CPU ์ž์›์„ ๋งŽ์ด ์†Œ๋น„ํ•  ์ˆ˜ ์žˆ์–ด, ์ถฉ๋ถ„ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•ด์•ผ ์›ํ™œํ•˜๊ฒŒ ์šด์˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์œ ์ง€๋ณด์ˆ˜: ์ง€์†์ ์ธ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ์ด๋ฅผ ์œ„ํ•ด ์ถ”๊ฐ€์ ์ธ ์ธ๋ ฅ๊ณผ ๋น„์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋Ÿฌ๋‹ ์ปค๋ธŒ(Learning Curve)
    • ํ•™์Šต ํ•„์š”์„ฑ: ๊ฐœ๋…๊ณผ ์„ค์ •์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‹ค์†Œ ๋‚œ์ด๋„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Kafka์˜ ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ

Producer๊ฐ€ Topic์— ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๋ฉด, ๋ฉ”์‹œ์ง€๋Š” ์—ฌ๋Ÿฌ Partition์œผ๋กœ ๋‚˜๋‰˜์–ด Broker๋“ค์— ๋ถ„์‚ฐ ์ €์žฅ๋˜๊ณ , Consumer Group์˜ Consumer๋“ค์ด ๊ฐ Partition์„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

  • ๋ฉ”์‹œ์ง€(Message)
    • ๋ฉ”์‹œ์ง€๋Š” Kafka๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๋Š” ๋ฐ์ดํ„ฐ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋‚˜ ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฉ”์‹œ์ง€๋Š” ํ‚ค(key), ๊ฐ’(value), ํƒ€์ž„์Šคํƒฌํ”„(timestamp), ๊ทธ๋ฆฌ๊ณ  ๋ช‡ ๊ฐ€์ง€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ํ”„๋กœ๋“€์„œ(Producer)
    • ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Kafka์— ๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ Kafka์— ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ ํ”„๋กœ๋“€์„œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
    • ํ”„๋กœ๋“€์„œ๋Š” ํŠน์ • ํ† ํ”ฝ(topic)์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ํ† ํ”ฝ(Topic)
    • ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€๋Š” ํ† ํ”ฝ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ์†Œ๋น„์ž์—๊ฒŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.
    • ํ† ํ”ฝ์€ ์—ฌ๋Ÿฌ ํŒŒํ‹ฐ์…˜(partition)์œผ๋กœ ๋‚˜๋ˆ„์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŒŒํ‹ฐ์…˜์€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…˜์„ ํ†ตํ•ด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ: “user-activity”๋ผ๋Š” ํ† ํ”ฝ์— ์‚ฌ์šฉ์ž์˜ ํ™œ๋™ ๋กœ๊ทธ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŒŒํ‹ฐ์…˜(Partition)
    • ํŒŒํ‹ฐ์…˜์€ ํ† ํ”ฝ์„ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋‚˜๋ˆˆ ๋‹จ์œ„๋กœ, ๊ฐ ํŒŒํ‹ฐ์…˜์€ ๋…๋ฆฝ์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฐ ํŒŒํ‹ฐ์…˜์€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ €์žฅํ•˜๋ฉฐ, ํŒŒํ‹ฐ์…˜ ๋‚ด์˜ ๋ฉ”์‹œ์ง€๋Š” ๊ณ ์œ ํ•œ ์˜คํ”„์…‹(offset)์œผ๋กœ ์‹๋ณ„๋ฉ๋‹ˆ๋‹ค.
    • ํŒŒํ‹ฐ์…˜์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์˜ ์—ฌ๋Ÿฌ ๋ธŒ๋กœ์ปค์— ๋ถ„์‚ฐ์‹œ์ผœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ‚ค(Key)
    • ํ‚ค๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํŠน์ • ํŒŒํ‹ฐ์…˜์— ํ• ๋‹นํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค.
    • ๋™์ผํ•œ ํ‚ค๋ฅผ ๊ฐ€์ง„ ๋ฉ”์‹œ์ง€๋Š” ํ•ญ์ƒ ๋™์ผํ•œ ํŒŒํ‹ฐ์…˜์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ์‚ฌ์šฉ์ž ID๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ๋™์ผํ•œ ํŒŒํ‹ฐ์…˜์— ์ €์žฅ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ปจ์Šˆ๋จธ(Consumer)
    • ํ† ํ”ฝ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ์™€ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ปจ์Šˆ๋จธ๋Š” ํŠน์ • ์ปจ์Šˆ๋จธ ๊ทธ๋ฃน(consumer group)์— ์†ํ•˜๋ฉฐ, ๊ฐ™์€ ๊ทธ๋ฃน์— ์†ํ•œ ์ปจ์Šˆ๋จธ๋“ค์€ ํ† ํ”ฝ์˜ ํŒŒํ‹ฐ์…˜์„ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปจ์Šˆ๋จธ๋Š” ์Šคํ‹ฐํ‚ค ํŒŒํ‹ฐ์…”๋‹(Sticky Partitioning)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํŠน์ • ์ปจ์Šˆ๋จธ๊ฐ€ ํŠน์ • ํŒŒํ‹ฐ์…˜์— ๋ถ™์–ด์„œ ๊ณ„์†ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ์ด๋Š” ๋ฐ์ดํ„ฐ ์ง€์—ญ์„ฑ์„ ๋†’์—ฌ ์บ์‹œ ํžˆํŠธ์œจ์„ ์ฆ๊ฐ€์‹œํ‚ค๊ณ  ์ „๋ฐ˜์ ์ธ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
Kafka๋Š” Topic์„ ์—ฌ๋Ÿฌ Partition์œผ๋กœ ๋‚˜๋ˆ„๊ณ , Consumer Group์˜ Consumer๋“ค์ด ๊ฐ Partition์„ ๋‚˜๋ˆ  ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•œ๋‹ค.
Topic: order

Partition 0
[๋ฉ”์‹œ์ง€1][๋ฉ”์‹œ์ง€2]

Partition 1
[๋ฉ”์‹œ์ง€3][๋ฉ”์‹œ์ง€4]

Partition 2
[๋ฉ”์‹œ์ง€5][๋ฉ”์‹œ์ง€6]

↓

Consumer Group A

Consumer1 → Partition0 ์ฒ˜๋ฆฌ
Consumer2 → Partition1 ์ฒ˜๋ฆฌ
Consumer3 → Partition2 ์ฒ˜๋ฆฌ
  • ๋ธŒ๋กœ์ปค(Broker)
    • Kafka ํด๋Ÿฌ์Šคํ„ฐ์˜ ๊ฐ ์„œ๋ฒ„๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ์ „์†กํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ํ•˜๋‚˜์˜ Kafka ํด๋Ÿฌ์Šคํ„ฐ๋Š” ์—ฌ๋Ÿฌ ๋ธŒ๋กœ์ปค๋กœ ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ ๋ธŒ๋กœ์ปค๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ† ํ”ฝ ํŒŒํ‹ฐ์…˜์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
Broker๋Š” Kafka ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋ฒ„์ด๋ฉฐ, Kafka Cluster๋Š” ์—ฌ๋Ÿฌ Broker ์„œ๋ฒ„๋“ค์„ ๋ฌถ์–ด ํ•˜๋‚˜์˜ Kafka ์‹œ์Šคํ…œ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.
Kafka Cluster
 โ”œโ”€ Broker1
 โ”‚    โ””โ”€ Partition0
 โ”œโ”€ Broker2
 โ”‚    โ””โ”€ Partition1
 โ””โ”€ Broker3
      โ””โ”€ Partition2
  • ์ฃผํ‚คํผ(Zookeeper)
    • Kafka ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์กฐ์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ถ„์‚ฐ ์ฝ”๋””๋„ค์ด์…˜ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
    • ์ฃผํ‚คํผ๋Š” ๋ธŒ๋กœ์ปค์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ , ๋ธŒ๋กœ์ปค ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
Zookeeper๋Š” Kafka Broker๋“ค์˜ ์ƒํƒœ์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ Broker๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ์กฐ์œจํ•˜๋Š” ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์ด๋‹ค.

 

Kafka์™€ RabbitMQ์˜ ์ฐจ์ด์ 

1) ์„ค๊ณ„ ์ฒ ํ•™

  • RabbitMQ: ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๋กœ, ๋ฉ”์‹œ์ง€์˜ ์•ˆ์ •์  ์ „๋‹ฌ๊ณผ ํ์ž‰์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.
  • Kafka: ๋ถ„์‚ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ์œผ๋กœ, ๋Œ€๊ทœ๋ชจ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์˜ ์ €์žฅ๊ณผ ๋ถ„์„์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.

2) ๋ฉ”์‹œ์ง€ ๋ชจ๋ธ

  • RabbitMQ: ํ(queue)๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€๋Š” ํ์— ์ €์žฅ๋˜๊ณ , ํ์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ปจ์Šˆ๋จธ์—๊ฒŒ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.
  • Kafka: ํ† ํ”ฝ(topic)์„ ์ค‘์‹ฌ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€๋Š” ํ† ํ”ฝ์˜ ํŒŒํ‹ฐ์…˜์— ์ €์žฅ๋˜๊ณ , ์ปจ์Šˆ๋จธ๋Š” ์ด ํŒŒํ‹ฐ์…˜์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค.

3) ๋ฉ”์‹œ์ง€ ์ง€์†์„ฑ

  • RabbitMQ: ๋ฉ”์‹œ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ ๋””์Šคํฌ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹จ๊ธฐ ์ €์žฅ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.
  • Kafka: ๋ฉ”์‹œ์ง€๋ฅผ ๋””์Šคํฌ์— ์ €์žฅํ•˜๋ฉฐ, ์žฅ๊ธฐ ์ €์žฅ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๋กœ๊ทธ๋Š” ์„ค์ •๋œ ๊ธฐ๊ฐ„ ๋™์•ˆ ๋ณด์กด๋ฉ๋‹ˆ๋‹ค.

4) ์‚ฌ์šฉ

  • RabbitMQ: ์ž‘์—… ํ, ์š”์ฒญ/์‘๋‹ต ํŒจํ„ด, ๋น„๋™๊ธฐ ์ž‘์—… ์ฒ˜๋ฆฌ ๋“ฑ ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ํ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
  • Kafka: ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ, ๋กœ๊ทธ ์ˆ˜์ง‘ ๋ฐ ๋ถ„์„, ์ด๋ฒคํŠธ ์†Œ์‹ฑ ๋“ฑ ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
ํ•ญ๋ชฉ RabbitMQ Apache Kafka
ํ•ต์‹ฌ ๋ชฉ์  ๋ฉ”์‹œ์ง€๋ฅผ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ์ „๋‹ฌ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ค๋ž˜ ์ €์žฅํ•˜๊ณ  ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ
๋ฐ์ดํ„ฐ ๊ตฌ์กฐ Queue ๊ธฐ๋ฐ˜ Topic + Partition ๊ธฐ๋ฐ˜
๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ๋ฐฉ์‹ Consumer๊ฐ€ ๊ฐ€์ ธ๊ฐ€๋ฉด Queue์—์„œ ์ œ๊ฑฐ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ์ฒ˜๋Ÿผ ์ €์žฅํ•˜๊ณ  Consumer๊ฐ€ ์ฝ์–ด๊ฐ
๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐฉ์‹ ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ์— ๊ฐ•ํ•จ, ํ•„์š” ์‹œ ๋””์Šคํฌ ์ €์žฅ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋””์Šคํฌ์— ๊ณ„์† ์ €์žฅ
๋ฉ”์‹œ์ง€ ๋ณด๊ด€ ๋ณดํ†ต ์ฒ˜๋ฆฌ ํ›„ ๋น ๋ฅด๊ฒŒ ์†Œ๋น„๋จ ์„ค์ • ๊ธฐ๊ฐ„ ๋™์•ˆ ๊ณ„์† ๋ณด๊ด€ ๊ฐ€๋Šฅ
์žฌ์กฐํšŒ ์ด๋ฏธ ์†Œ๋น„ํ•œ ๋ฉ”์‹œ์ง€๋Š” ๋‹ค์‹œ ์ฝ๊ธฐ ์–ด๋ ค์›€ Offset ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณผ๊ฑฐ ๋ฉ”์‹œ์ง€ ์žฌ์กฐํšŒ ๊ฐ€๋Šฅ
์ฒ˜๋ฆฌ ํŠน์ง• ์ž‘์—… ์ฒ˜๋ฆฌ(Job Queue), ๋น„๋™๊ธฐ ์š”์ฒญ ์ฒ˜๋ฆฌ์— ์ ํ•ฉ ๋Œ€๊ทœ๋ชจ ๋กœ๊ทธ/์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์— ์ ํ•ฉ
๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ Queue ๋‹จ์œ„ ์†Œ๋น„ ์ค‘์‹ฌ Partition ๊ธฐ๋ฐ˜ ๋Œ€๊ทœ๋ชจ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
๋Œ€ํ‘œ ์‚ฌ์šฉ ์˜ˆ์‹œ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ, ์ด๋ฉ”์ผ ๋ฐœ์†ก, ์•Œ๋ฆผ ํ ๋กœ๊ทธ ์ˆ˜์ง‘, ์‹ค์‹œ๊ฐ„ ๋ถ„์„, ์ด๋ฒคํŠธ ์†Œ์‹ฑ
ํ•ต์‹ฌ ๋А๋‚Œ “๋ฉ”์‹œ์ง€ ์ „๋‹ฌ ์‹œ์Šคํ…œ” “์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ €์žฅ/์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ์Šคํ…œ”

 

  • RabbitMQ๋Š” “๋ฉ”์‹œ์ง€๋ฅผ ์ž˜ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ”์— ๋” ์ดˆ์ ์ด ๋งž์ถฐ์ ธ ์žˆ๋‹ค.
  • Kafka๋Š” “๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๊ทธ์ฒ˜๋Ÿผ ์ €์žฅํ•˜๊ณ  ๊ณ„์† ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ”์— ๋” ์ดˆ์ ์ด ๋งž์ถฐ์ ธ ์žˆ๋‹ค.
  • RabbitMQ๋Š” ์ž‘์—… ์ฒ˜๋ฆฌ(Job Queue) ๋А๋‚Œ์— ๊ฐ€๊น๊ณ , Kafka๋Š” ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ ๋А๋‚Œ์— ๊ฐ€๊น๋‹ค.
  • Kafka๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋””์Šคํฌ์— ์žฅ๊ธฐ๊ฐ„ ์ €์žฅํ•˜๋ฉฐ, Consumer๊ฐ€ ๋‚˜์ค‘์— ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.
  • Kafka๋Š” Topic์„ Partition์œผ๋กœ ๋‚˜๋ˆ„์–ด ์—ฌ๋Ÿฌ Consumer๊ฐ€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์‰ฝ๋‹ค.

 

 

+) ๋Œ€๊ทœ๋ชจ ์˜ˆ์•ฝ ์‹œ์Šคํ…œ์˜ ์ˆœ์„œ ๋ณด์žฅ๊ณผ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ

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

 

๐Ÿ—จ๏ธ Kafka ์‹ค์Šต

์ด ์‹ค์Šต์€ Kafka์— ์ €์žฅ๋œ ๋ฉ”์‹œ์ง€๋ฅผ Consumer ํ”„๋กœ์ ํŠธ๊ฐ€ ์ฝ์–ด์˜ค๋Š” ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•˜๋Š” ์˜ˆ์ œ๋‹ค.
Producer๊ฐ€ Kafka Topic์— ๋ฉ”์‹œ์ง€ ์ €์žฅ
↓
Kafka๋Š” Topic/Partition์— ๋ฉ”์‹œ์ง€ ๋ณด๊ด€
↓
Consumer๊ฐ€ @KafkaListener๋กœ Topic์„ ๊ตฌ๋…
↓
๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋จ

Kafka ์„ค์น˜

  • ๋„์ปค ์ปดํฌ์ฆˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ kafka ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. docker-compose.yml ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
version: '3.8'
services:
  zookeeper:
    image: wurstmeister/zookeeper:3.4.6
    platform: linux/amd64
    ports:
      - "2181:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: wurstmeister/kafka:latest
    platform: linux/amd64
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:29092,OUTSIDE://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
      KAFKA_LISTENERS: INSIDE://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  kafka-ui:
    image: provectuslabs/kafka-ui:latest
    platform: linux/amd64
    ports:
      - "8080:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092
      KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181
      KAFKA_CLUSTERS_0_READONLY: "false"

 

+) ์„ค๋ช…

๋”๋ณด๊ธฐ
# Docker Compose ๋ฌธ๋ฒ• ๋ฒ„์ „
version: '3.8'

# ์‹คํ–‰ํ•  ์ปจํ…Œ์ด๋„ˆ ๋ชฉ๋ก
services:

  # Kafka ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋ฒ„
  # Broker ์ƒํƒœ/๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ์—ญํ• 
  zookeeper:

    # ์‚ฌ์šฉํ•  Docker ์ด๋ฏธ์ง€
    image: wurstmeister/zookeeper:3.4.6

    # ์‹คํ–‰ ํ”Œ๋žซํผ
    # ๋งฅ M์นฉ ๋“ฑ์—์„œ amd64 ๊ฐ•์ œ ์‹คํ–‰ ์‹œ ์‚ฌ์šฉ
    platform: linux/amd64

    # ํฌํŠธ ์—ฐ๊ฒฐ
    # ๋‚ด ์ปดํ“จํ„ฐ 2181 ↔ ์ปจํ…Œ์ด๋„ˆ 2181
    ports:
      - "2181:2181"

    # ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ์„ค์ •๊ฐ’
    environment:

      # Zookeeper ์ ‘์† ํฌํŠธ
      ZOOKEEPER_CLIENT_PORT: 2181

      # Zookeeper ๋‚ด๋ถ€ ์‹œ๊ฐ„ ์„ค์ •
      ZOOKEEPER_TICK_TIME: 2000


  # ์‹ค์ œ Kafka ๋ฉ”์‹œ์ง€ ์„œ๋ฒ„(Broker)
  kafka:

    # Kafka Docker ์ด๋ฏธ์ง€
    image: wurstmeister/kafka:latest

    # ์‹คํ–‰ ํ”Œ๋žซํผ
    platform: linux/amd64

    # Kafka ์ ‘์† ํฌํŠธ
    # Spring Boot์—์„œ localhost:9092 ๋กœ ์ ‘์† ๊ฐ€๋Šฅ
    ports:
      - "9092:9092"

    # Kafka ๋‚ด๋ถ€ ์„ค์ •๊ฐ’
    environment:

      # Kafka ์ ‘์† ์ฃผ์†Œ ์„ค์ •
      # ์™ธ๋ถ€(localhost:9092)์™€ ๋‚ด๋ถ€(kafka:29092) ์—ฐ๊ฒฐ์šฉ
      KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:29092,OUTSIDE://localhost:9092

      # Listener ํ”„๋กœํ† ์ฝœ ์„ค์ •
      # PLAINTEXT = ์•”ํ˜ธํ™” ์—†์ด ํ†ต์‹ 
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT

      # Kafka๊ฐ€ ์‹ค์ œ ์—ด์–ด๋‘˜ ํฌํŠธ
      KAFKA_LISTENERS: INSIDE://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092

      # Broker๋ผ๋ฆฌ ํ†ต์‹  ์‹œ ์‚ฌ์šฉํ•  Listener
      KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE

      # Kafka๊ฐ€ ์—ฐ๊ฒฐํ•  Zookeeper ์ฃผ์†Œ
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

    # Docker ๋‚ด๋ถ€ ์ •๋ณด ์ ‘๊ทผ์šฉ ์„ค์ •
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock


  # Kafka ์ƒํƒœ๋ฅผ ์›น์—์„œ ํ™•์ธํ•˜๋Š” UI ์„œ๋ฒ„
  kafka-ui:

    # Kafka UI Docker ์ด๋ฏธ์ง€
    image: provectuslabs/kafka-ui:latest

    # ์‹คํ–‰ ํ”Œ๋žซํผ
    platform: linux/amd64

    # ์›น ์ ‘์† ํฌํŠธ
    # localhost:8080 ์ ‘์† ๊ฐ€๋Šฅ
    ports:
      - "8080:8080"

    # Kafka UI ์„ค์ •๊ฐ’
    environment:

      # UI์— ํ‘œ์‹œํ•  Kafka ํด๋Ÿฌ์Šคํ„ฐ ์ด๋ฆ„
      KAFKA_CLUSTERS_0_NAME: local

      # Kafka Broker ์—ฐ๊ฒฐ ์ฃผ์†Œ
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092

      # ์—ฐ๊ฒฐํ•  Zookeeper ์ฃผ์†Œ
      KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181

      # false = UI์—์„œ ์ˆ˜์ • ๊ฐ€๋Šฅ
      KAFKA_CLUSTERS_0_READONLY: "false"

 

์„œ๋ฒ„(์ปจํ…Œ์ด๋„ˆ) ์—ญํ•  ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ ‘์† ํฌํŠธ
Zookeeper Kafka ๊ด€๋ฆฌ ์„œ๋ฒ„ Broker ์ƒํƒœ ๋ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ 2181
Kafka ์‹ค์ œ ๋ฉ”์‹œ์ง€ ์„œ๋ฒ„(Broker) Topic/Partition์— ๋ฉ”์‹œ์ง€ ์ €์žฅ ๋ฐ ์ „๋‹ฌ 9092
Kafka UI Kafka ์›น ๊ด€๋ฆฌ ํŽ˜์ด์ง€ Topic, Partition, ๋ฉ”์‹œ์ง€ ์ƒํƒœ ํ™•์ธ 8080
Zookeeper, Kafka, Kafka UI๋Š” ์„œ๋กœ ์—ญํ• ์ด ๋‹ค๋ฅธ ๋ณ„๊ฐœ์˜ ์„œ๋ฒ„(ํ”„๋กœ์„ธ์Šค)์ด๋ฉฐ, ์‹ค์Šต์—์„œ๋Š” ๊ฐ๊ฐ Docker ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ถ„๋ฆฌ ์‹คํ–‰ํ•˜์˜€๋‹ค. Docker Compose๋Š” ์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ๋กœ ์ž๋™ ์—ฐ๊ฒฐํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„๋งŒ์œผ๋กœ ์„œ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

  • docker-compose.yml ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ๋กœ์—์„œ ๋„์ปค ์ปดํฌ์ฆˆ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฒ„์ „ ๊ด€๋ จ์œผ๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ zookeeper์˜ image๋ฅผ wurstmeister/zookeeper:latest๋กœ ๋ณ€๊ฒฝ
docker compose up -d
  • localhost:8080 ์— ์ ‘์†ํ•˜๋ฉด kafka UI ์— ์ ‘์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Producer Application

  • start.spring.io ์— ์ ‘์†ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด build.gradle์˜ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.kafka:spring-kafka'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.kafka:spring-kafka-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
# ํ˜„์žฌ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„
# Kafka Producer ํ”„๋กœ์ ํŠธ ์‹๋ณ„์šฉ
spring.application.name=producer

# Spring Boot ์‹คํ–‰ ํฌํŠธ
# localhost:8090 ์œผ๋กœ ์ ‘์† ๊ฐ€๋Šฅ
server.port=8090

# Kafka Broker ์ ‘์† ์ฃผ์†Œ
# localhost:9092 ์— ์‹คํ–‰ ์ค‘์ธ Kafka ์„œ๋ฒ„ ์—ฐ๊ฒฐ
spring.kafka.bootstrap-servers=localhost:9092

# ๋ฉ”์‹œ์ง€ Key ์ง๋ ฌํ™” ํด๋ž˜์Šค
# Kafka๋Š” ๋ฐ์ดํ„ฐ๋ฅผ byte ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—
# String -> byte ๋ณ€ํ™˜ ๊ณผ์ •(์ง๋ ฌํ™”)์ด ํ•„์š”ํ•จ
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer

# ๋ฉ”์‹œ์ง€ Value ์ง๋ ฌํ™” ํด๋ž˜์Šค
# ์‹ค์ œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ(String)์„ byte๋กœ ๋ณ€ํ™˜
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
ProducerApplicationKafkaConfig.java
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;

import java.util.HashMap;
import java.util.Map;

@Configuration
// Kafka Producer ์„ค์ • ํด๋ž˜์Šค
// KafkaTemplate Bean ๋“ฑ์„ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก
public class ProducerApplicationKafkaConfig {

    @Bean
    // Kafka Producer ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” Factory Bean
    public ProducerFactory<String, String> producerFactory() {

        // Kafka ์„ค์ •๊ฐ’ ์ €์žฅ Map
        Map<String, Object> configProps = new HashMap<>();

        // Kafka Broker ์ฃผ์†Œ ์„ค์ •
        // localhost:9092 Kafka ์„œ๋ฒ„ ์—ฐ๊ฒฐ
        configProps.put(
                ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
                "localhost:9092"
        );

        // ๋ฉ”์‹œ์ง€ Key ์ง๋ ฌํ™” ๋ฐฉ์‹
        // String ๋ฐ์ดํ„ฐ๋ฅผ byte ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
        configProps.put(
                ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                StringSerializer.class
        );

        // ๋ฉ”์‹œ์ง€ Value ์ง๋ ฌํ™” ๋ฐฉ์‹
        // ์‹ค์ œ ๋ฉ”์‹œ์ง€ ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”
        configProps.put(
                ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                StringSerializer.class
        );

        // ProducerFactory ์ƒ์„ฑ ๋ฐ˜ํ™˜
        return new DefaultKafkaProducerFactory<>(configProps);
    }

    @Bean
    // Kafka ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    // ์‹ค์ œ๋กœ Kafka์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ํ•ต์‹ฌ ํด๋ž˜์Šค
    public KafkaTemplate<String, String> kafkaTemplate() {

        // ProducerFactory ๊ธฐ๋ฐ˜ KafkaTemplate ์ƒ์„ฑ
        return new KafkaTemplate<>(producerFactory());
    }
}
ProducerController.java
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
// REST API Controller
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ž๋™ ์ƒ์„ฑ
public class ProducerController {

    // ProducerService ์˜์กด์„ฑ ์ฃผ์ž…
    private final ProducerService producerService;

    @GetMapping("/send")
    // GET ์š”์ฒญ API
    // ex)
    // /send?topic=test&key=user1&message=hello
    public String sendMessage(

            // URL ํŒŒ๋ผ๋ฏธํ„ฐ topic ๊ฐ’ ๋ฐ›๊ธฐ
            // ex) topic=test
            @RequestParam("topic") String topic,

            // ๋ฉ”์‹œ์ง€ Key ๊ฐ’ ๋ฐ›๊ธฐ
            // ๊ฐ™์€ Key๋Š” ๊ฐ™์€ Partition์œผ๋กœ ์ €์žฅ๋จ
            @RequestParam("key") String key,

            // ์‹ค์ œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ
            @RequestParam("message") String message
    ) {

        // Kafka ๋ฉ”์‹œ์ง€ ์ „์†ก ์„œ๋น„์Šค ํ˜ธ์ถœ
        producerService.sendMessage(topic, key, message);

        return "Message sent to Kafka topic";
    }
}
ProducerService.java
import lombok.RequiredArgsConstructor;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
// ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ํด๋ž˜์Šค
@RequiredArgsConstructor
public class ProducerService {

    // Kafka ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    private final KafkaTemplate<String, String> kafkaTemplate;

    public void sendMessage(
            String topic,
            String key,
            String message
    ) {

        // ๋ฉ”์‹œ์ง€ 10๊ฐœ ๋ฐ˜๋ณต ์ „์†ก
        for (int i = 0; i < 10; i++) {

            // Kafka ๋ฉ”์‹œ์ง€ ์ „์†ก
            //
            // ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ:
            // Topic ์ด๋ฆ„
            //
            // ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ:
            // ๋ฉ”์‹œ์ง€ Key
            // ๊ฐ™์€ Key๋ฉด ๊ฐ™์€ Partition ์ €์žฅ ๊ฐ€๋Šฅ
            //
            // ์„ธ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ:
            // ์‹ค์ œ ๋ฉ”์‹œ์ง€ ๋ฐ์ดํ„ฐ
            kafkaTemplate.send(
                    topic,
                    key,
                    message + " " + i
            );
        }
    }
}

 

๊ฐœ๋… ์˜๋ฏธ
Producer Kafka์— ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๋Š” ์—ญํ• 
KafkaTemplate Kafka ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
Topic ๋ฉ”์‹œ์ง€ ์ €์žฅ ์žฅ์†Œ
Key ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋А Partition์— ๋„ฃ์„์ง€ ๊ฒฐ์ •
Serializer String → byte ๋ณ€ํ™˜
bootstrap-servers Kafka Broker ์ ‘์† ์ฃผ์†Œ

 

์‚ฌ์šฉ์ž ์š”์ฒญ
↓
Controller
↓
ProducerService
↓
KafkaTemplate.send()
↓
Kafka Topic ์ €์žฅ
↓
Partition ๋ถ„๋ฐฐ

 

Consumer Application

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด build.gradle์˜ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.kafka:spring-kafka'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.kafka:spring-kafka-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=consumer
server.port=8091

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
ConsumerApplicationKafkaConfig.java
  • ์ด ํด๋ž˜์Šค๋Š” Kafka Consumer๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„์ง€ ์„ค์ •ํ•˜๋Š” ํด๋ž˜์Šค๋‹ค.
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;

import java.util.HashMap;
import java.util.Map;

// ์ด ํด๋ž˜์Šค๋Š” Kafka ์ปจ์Šˆ๋จธ ์„ค์ •์„ ์œ„ํ•œ Spring ์„ค์ • ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
@EnableKafka // Kafka ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค.
@Configuration // Spring ์„ค์ • ํด๋ž˜์Šค๋กœ ์„ ์–ธํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ž…๋‹ˆ๋‹ค.
public class ConsumerApplicationKafkaConfig {

    // Kafka ์ปจ์Šˆ๋จธ ํŒฉํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋นˆ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    // ConsumerFactory๋Š” Kafka ์ปจ์Šˆ๋จธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    // ๊ฐ ์ปจ์Šˆ๋จธ๋Š” ์ด ํŒฉํ† ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ ์„ค์ •์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        // ์ปจ์Šˆ๋จธ ํŒฉํ† ๋ฆฌ ์„ค์ •์„ ์œ„ํ•œ ๋งต์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        Map<String, Object> configProps = new HashMap<>();
        // Kafka ๋ธŒ๋กœ์ปค์˜ ์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        // ๋ฉ”์‹œ์ง€ ํ‚ค์˜ ๋””์‹œ๋ฆฌ์–ผ๋ผ์ด์ € ํด๋ž˜์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        // ๋ฉ”์‹œ์ง€ ๊ฐ’์˜ ๋””์‹œ๋ฆฌ์–ผ๋ผ์ด์ € ํด๋ž˜์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        // ์„ค์ •๋œ ํ”„๋กœํผํ‹ฐ๋กœ DefaultKafkaConsumerFactory๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return new DefaultKafkaConsumerFactory<>(configProps);
    }

    // Kafka ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ ํŒฉํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋นˆ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    // ConcurrentKafkaListenerContainerFactory๋Š” Kafka ๋ฉ”์‹œ์ง€๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜์‹ ํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    // ์ด ํŒฉํ† ๋ฆฌ๋Š” @KafkaListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋“ค์„ ์‹คํ–‰ํ•  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
        // ConcurrentKafkaListenerContainerFactory๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        // ์ปจ์Šˆ๋จธ ํŒฉํ† ๋ฆฌ๋ฅผ ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ ํŒฉํ† ๋ฆฌ์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        factory.setConsumerFactory(consumerFactory());
        // ์„ค์ •๋œ ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ ํŒฉํ† ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return factory;
    }
}

 

+) ConsumerApplicationKafkaConfig

๋”๋ณด๊ธฐ
ConsumerApplicationKafkaConfig.java

 

์ด ํด๋ž˜์Šค๋Š” Kafka Consumer๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„์ง€ ์„ค์ •ํ•˜๋Š” ํด๋ž˜์Šค๋‹ค.

 
@EnableKafka
 

@KafkaListener๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ™œ์„ฑํ™”ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.
์ด๊ฒŒ ์žˆ์–ด์•ผ Kafka ๋ฉ”์‹œ์ง€ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค.

 
@Configuration
 

์ด ํด๋ž˜์Šค๊ฐ€ ์Šคํ”„๋ง ์„ค์ • ํด๋ž˜์Šค๋ผ๋Š” ๋œป์ด๋‹ค.
์•ˆ์— ์žˆ๋Š” @Bean ๋ฉ”์„œ๋“œ๋“ค์ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ๋‹ค.

 
public ConsumerFactory<String, String> consumerFactory()
 

Consumer ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ณต์žฅ์ด๋‹ค.
<String, String>์€ ๋ฉ”์‹œ์ง€์˜ key์™€ value๋ฅผ ๋‘˜ ๋‹ค String์œผ๋กœ ์ฝ๊ฒ ๋‹ค๋Š” ๋œป์ด๋‹ค.

 

Kafka Consumer๋Š” ๊ทธ๋ƒฅ ์ž๋™์œผ๋กœ ์ƒ๊ธฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, “์–ด๋–ค Kafka ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ• ์ง€”, “๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ฝ์„์ง€” ๊ฐ™์€ ์„ค์ •์ด ๋จผ์ € ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋ž˜์„œ ๋จผ์ € Consumer๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์„ค์ • ๊ณต์žฅ์ธ ConsumerFactory๋ฅผ ๋งŒ๋“ ๋‹ค.

 

 
Map<String, Object> configProps = new HashMap<>();
 

Kafka Consumer ์„ค์ •๊ฐ’์„ ๋‹ด๋Š” Map์ด๋‹ค.
Kafka๊ฐ€ ์ •ํ•ด๋‘” ์„ค์ • key์— ๋งž์ถฐ ๊ฐ’์„ ๋„ฃ๋Š”๋‹ค.

 
configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
 

์ด ๋ฉ”์„œ๋“œ๋Š” Kafka Consumer๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ • ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ์—ญํ• ์ด๋‹ค. 

์—ฌ๊ธฐ์„œ: Kafka Broker ์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•œ๋‹ค.
์‹ค์ œ๋กœ๋Š” "bootstrap.servers" = "localhost:9092"๋ฅผ ๋„ฃ๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค.

์ฆ‰, “Kafka ์„œ๋ฒ„๋Š” localhost:9092์— ์žˆ๋‹ค”๋Š” ์„ค์ •์ด๊ณ ,

 
configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
 

๋Š” Kafka์—์„œ ๊ฐ€์ ธ์˜จ byte ๋ฐ์ดํ„ฐ๋ฅผ String์œผ๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋ฉ”์‹œ์ง€ key๋ฅผ String์œผ๋กœ ์—ญ์ง๋ ฌํ™”ํ•œ๋‹ค.

 
configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
 

๋ฉ”์‹œ์ง€ value๋ฅผ String์œผ๋กœ ์—ญ์ง๋ ฌํ™”ํ•œ๋‹ค.

 
return new DefaultKafkaConsumerFactory<>(configProps);
 

์œ„์—์„œ ๋งŒ๋“  ์„ค์ •๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ConsumerFactory๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

์ฆ‰ ConsumerFactory๋Š”:

"Kafka Consumer๋ฅผ ์–ด๋–ค ์„ค์ •์œผ๋กœ ๋งŒ๋“ค์ง€ ์ •์˜ํ•˜๋Š” ๊ณต์žฅ"
 

์ด๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

+) kafkaListenerContainerFactory()

๋”๋ณด๊ธฐ
kafkaListenerContainerFactory()

 

Consumer๋งŒ ๋งŒ๋“ ๋‹ค๊ณ  ๋ฉ”์‹œ์ง€๊ฐ€ ์ž๋™์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ Kafka Topic์„ ๊ณ„์† ๊ฐ์‹œํ•˜๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค ๋ฉด @KafkaListener ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด์ค„ ์‹คํ–‰ ํ™˜๊ฒฝ์ด ํ•„์š”ํ•˜๋‹ค. ๊ทธ ์—ญํ• ์„ ํ•˜๋Š” ๊ฒŒ:

 
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory()
 

์ด๋‹ค.

์ด๊ฑด @KafkaListener๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ ์„ค์ •์ด๋‹ค.

 

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด:

@KafkaListener ๋ฉ”์„œ๋“œ๋“ค์ด Kafka ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์‹คํ–‰ ํ™˜๊ฒฝ
 

์ด๋‹ค.

 
factory.setConsumerFactory(consumerFactory());
 

์•„๊นŒ ๋งŒ๋“  ConsumerFactory๋ฅผ ์—ฐ๊ฒฐํ•œ๋‹ค.
์ฆ‰, "์•„๊นŒ ๋งŒ๋“  Consumer ์„ค์ •์„ ์‚ฌ์šฉํ•ด์„œ @KafkaListener๊ฐ€ ์‹ค์ œ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ๊ฒŒ ํ•ด๋ผ" ๋Š” ๋œป์ด๋‹ค.

ConsumerFactory๋Š” Consumer๋ฅผ ๋งŒ๋“œ๋Š” ์„ค์ •์ด๊ณ ,
ConcurrentKafkaListenerContainerFactory๋Š” ๊ทธ Consumer๋ฅผ ์ด์šฉํ•ด์„œ @KafkaListener๋ฅผ ์‹ค์ œ๋กœ ๋Œ๋ฆฌ๋Š” ์‹คํ–‰ ํ™˜๊ฒฝ์ž„.
๊ตฌ๋ถ„ ConsumerFactory<string, string> ConcurrentKafkaListenerContainerFactory<string, string>
ํ•œ ์ค„ ์ •์˜ Kafka Consumer๋ฅผ ๋งŒ๋“œ๋Š” ๊ณต์žฅ @KafkaListener๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณต์žฅ
์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด “์–ด๋–ค ์„ค์ •์œผ๋กœ Consumer๋ฅผ ๋งŒ๋“ค์ง€” ์ •ํ•จ “๊ทธ Consumer๋ฅผ ๊ฐ€์ง€๊ณ  ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ๋Œ๋ฆด์ง€” ์ •ํ•จ
๋‹ด๋‹น ์—ญํ•  Kafka ์ ‘์† ์ •๋ณด, ์—ญ์ง๋ ฌํ™” ๋ฐฉ์‹ ๋“ฑ Consumer ๊ธฐ๋ณธ ์„ค์ • @KafkaListener๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ž๋™์œผ๋กœ ๋ฐ›๋„๋ก ์‹คํ–‰ ํ™˜๊ฒฝ ๊ตฌ์„ฑ
๋“ค์–ด๊ฐ€๋Š” ์„ค์ • bootstrap.servers, key.deserializer, value.deserializer ConsumerFactory, ๋™์‹œ ์ฒ˜๋ฆฌ ๊ฐœ์ˆ˜, Ack ๋ฐฉ์‹ ๋“ฑ
์ฝ”๋“œ ์œ„์น˜ new DefaultKafkaConsumerFactory<>(configProps) new ConcurrentKafkaListenerContainerFactory<>()
์—ฐ๊ฒฐ ๊ด€๊ณ„ ๋จผ์ € ๋งŒ๋“ค์–ด์ง factory.setConsumerFactory(consumerFactory())๋กœ ConsumerFactory๋ฅผ ์‚ฌ์šฉํ•จ
๋น„์œ  “์ง์› ์ฑ„์šฉ ๊ธฐ์ค€์„œ” “์ง์›์„ ๋ฐฐ์น˜ํ•ด์„œ ์ผํ•˜๊ฒŒ ํ•˜๋Š” ์ž‘์—…์žฅ”
์‹ค์ œ ์˜๋ฏธ Kafka ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„ Consumer ๊ฐ์ฒด ์ƒ์„ฑ ์ค€๋น„ @KafkaListener ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋˜๋„๋ก Consumer๋ฅผ ๋ถ™์—ฌ์คŒ
์—†์œผ๋ฉด? Consumer ์ž์ฒด๋ฅผ ๋งŒ๋“ค ์„ค์ •์ด ์—†์Œ @KafkaListener๊ฐ€ ์–ด๋–ค Consumer๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ์ง€ ๋ชจ๋ฆ„
ํ•ต์‹ฌ ๊ธฐ์–ต Consumer ์„ค์ • ๋‹ด๋‹น Listener ์‹คํ–‰ ๋‹ด๋‹น
ConsumerService.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ConsumerService {

    // ์ด ๋ฉ”์„œ๋“œ๋Š” Kafka์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    // @KafkaListener ์–ด๋…ธํ…Œ์ด์…˜์€ ์ด ๋ฉ”์„œ๋“œ๋ฅผ Kafka ๋ฆฌ์Šค๋„ˆ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    @KafkaListener(groupId = "group_a", topics = "topic1")
    // Kafka ํ† ํ”ฝ "test-topic"์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
    // groupId๋Š” ์ปจ์Šˆ๋จธ ๊ทธ๋ฃน์„ ์ง€์ •ํ•˜์—ฌ ๋™์ผํ•œ ๊ทธ๋ฃน์— ์†ํ•œ ๋‹ค๋ฅธ ์ปจ์Šˆ๋จธ์™€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ถ„๋ฐฐ๋ฐ›์Šต๋‹ˆ๋‹ค.
    public void consumeFromGroupA(String message) {
        log.info("Group A consumed message from topic1: " + message);
    }

    // ๋™์ผํ•œ ํ† ํ”ฝ์„ ๋‹ค๋ฅธ ๊ทธ๋ฃน ID๋กœ ์†Œ๋น„ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    @KafkaListener(groupId = "group_b", topics = "topic1")
    public void consumeFromGroupB(String message) {
        log.info("Group B consumed message from topic1: " + message);
    }

    // ๋‹ค๋ฅธ ํ† ํ”ฝ์„ ๋‹ค๋ฅธ ๊ทธ๋ฃน ID๋กœ ์†Œ๋น„ํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    @KafkaListener(groupId = "group_c", topics = "topic2")
    public void consumeFromTopicC(String message) {
        log.info("Group C consumed message from topic2: " + message);
    }

    // ๋‹ค๋ฅธ ํ† ํ”ฝ์„ ๋‹ค๋ฅธ ๊ทธ๋ฃน ID๋กœ ์†Œ๋น„ํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
    @KafkaListener(groupId = "group_c", topics = "topic3")
    public void consumeFromTopicD(String message) {
        log.info("Group C consumed message from topic3: " + message);
    }

    @KafkaListener(groupId = "group_d", topics = "topic4")
    public void consumeFromPartition0(String message) {
        log.info("Group D consumed message from topic4: " + message);
    }
}
  1. ConsumerService๋Š” @KafkaListener๋ฅผ ํ†ตํ•ด Kafka Topic์„ ๊ตฌ๋…ํ•œ๋‹ค.
  2. ๋ฉ”์‹œ์ง€๊ฐ€ Topic์— ๋“ค์–ด์˜ค๋ฉด ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋œ๋‹ค.
  3. groupId๋Š” Consumer Group์„ ์˜๋ฏธํ•˜๋ฉฐ,
  4. ๊ฐ™์€ Topic์ด๋ผ๋„ groupId๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ๊ฐ ๋…๋ฆฝ์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

 

ํ™•์ธ

  • ๋‘ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๊ณ  Kafka UI๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. Topic ํƒญ๊ณผ Comsumers ํƒญ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

topic ์„ test-topic ์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์š”์ฒญํ•ด๋ด…๋‹ˆ๋‹ค.

  • kafka ui > Topics > Messages ์— ์ ‘์†ํ•˜๋ฉด ๋ฐฉ๊ธˆ ์š”์ฒญํ•œ ํ† ํ”ฝ์œผ๋กœ ๋ฐœํ–‰๋œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

topic ์„ topic1 ์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์š”์ฒญํ•ด๋ด…๋‹ˆ๋‹ค.

  • ์ปจ์Šˆ๋จธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด GroupA ์™€ GroupB๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
  • ๋”ฐ๋ผ์„œ ๊ฐ™์€ ํ† ํ”ฝ์„ ๊ฐ€์ง€๊ณ  ๊ทธ๋ฃน์ด ๋‹ค๋ฅด๋ฉด ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ ๊ทธ๋ฃน๋งˆ๋‹ค ์ˆ˜์‹ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

topic ์„ topic2 ์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์š”์ฒญํ•ด๋ด…๋‹ˆ๋‹ค.

  • ์ปจ์Šˆ๋จธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด GroupC์ด๊ณ  topic ์ด 2 ์ธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

topic ์„ topic3 ์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์š”์ฒญํ•ด๋ด…๋‹ˆ๋‹ค.

  • ์ปจ์Šˆ๋จธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด GroupC์ด๊ณ  topic ์ด 3 ์ธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

topic ์„ topic4 ์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์š”์ฒญํ•ด๋ด…๋‹ˆ๋‹ค.

  • ์ปจ์Šˆ๋จธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด GroupD์ด๊ณ  topic ์ด 4 ์ธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Kafka ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์ •๋ฆฌ

  • ์ด๋ฒˆ Kafka ์˜ˆ์ œ๋Š” Producer, Consumer ์ด 2๊ฐœ์˜ Spring Boot ํ”„๋กœ์ ํŠธ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
  • Producer ํ”„๋กœ์ ํŠธ๋Š” Kafka Topic์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ , Consumer ํ”„๋กœ์ ํŠธ๋Š” Topic์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ๋…ํ•ด์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.
ํ”„๋กœ์ ํŠธ ์—ญํ•  ์ฃผ์š” ์ฝ”๋“œ Kafka ๊ธฐ์ค€
Producer ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Kafka Topic์— ๋ฐœํ–‰ ProducerController, ProducerService, ProducerApplicationKafkaConfig Producer
Consumer Kafka Topic์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์–ด ์ฒ˜๋ฆฌ ConsumerService, ConsumerApplicationKafkaConfig Consumer

 

๊ฐ ํ”„๋กœ์ ํŠธ์˜ ์ฐจ์ด

๊ตฌ๋ถ„ Producer ํ”„๋กœ์ ํŠธ Consumer ํ”„๋กœ์ ํŠธ
์—ญํ•  ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๋Š” ์ชฝ ๋ฉ”์‹œ์ง€ ๋ฐ›๋Š” ์ชฝ
์‹คํ–‰ ํฌํŠธ 8090 8091
Kafka ๊ธฐ์ค€ Producer Consumer
์ฃผ์š” ๊ฐ์ฒด/์–ด๋…ธํ…Œ์ด์…˜ KafkaTemplate @KafkaListener
์„ค์ • ํด๋ž˜์Šค ProducerFactory, KafkaTemplate ์„ค์ • ConsumerFactory, KafkaListenerContainerFactory ์„ค์ •
ํ•˜๋Š” ์ผ /send ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด Kafka Topic์— ๋ฉ”์‹œ์ง€ ์ „์†ก ์ง€์ •ํ•œ Topic์„ ๊ตฌ๋…ํ•˜๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ž๋™ ์ฒ˜๋ฆฌ
๋ฉ”์‹œ์ง€ ์œ„์น˜ Topic/Partition์— ์ €์žฅ๋จ Topic/Partition์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์Œ
์‹คํ–‰ ๊ฒฐ๊ณผ Topic์— ๋ฉ”์‹œ์ง€๊ฐ€ ๊ธฐ๋ก๋จ Consumer Group ๊ธฐ์ค€์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•จ

 

Kafka ๋ฉ”์‹œ์ง€ ํ๋ฆ„

  1. ์‚ฌ์šฉ์ž๊ฐ€ Producer ํ”„๋กœ์ ํŠธ์— /send?topic=topic1&key=user1&message=hello ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.
  2. ProducerController๊ฐ€ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›๋Š”๋‹ค.
  3. ProducerService๊ฐ€ KafkaTemplate.send(topic, key, message)๋กœ Kafka์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ธ๋‹ค.
  4. Kafka๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ง€์ •ํ•œ Topic์— ์ €์žฅํ•œ๋‹ค.
  5. ๋ฉ”์‹œ์ง€๋Š” Key๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŠน์ • Partition์— ์ €์žฅ๋  ์ˆ˜ ์žˆ๋‹ค.
  6. Consumer ํ”„๋กœ์ ํŠธ์˜ @KafkaListener๊ฐ€ ํ•ด๋‹น Topic์„ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ๋Š”๋‹ค.
  7. Consumer Group ๊ธฐ์ค€์œผ๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌ๋œ๋‹ค.

ConsumerService ๊ธฐ์ค€ ์ •๋ฆฌ

๋ฉ”์„œ๋“œ Consumer Group Topic ์˜๋ฏธ 
consumeFromGroupA group_a topic1 topic1 ๋ฉ”์‹œ์ง€๋ฅผ group_a ์†Œ๋น„์ž๊ฐ€ ์ฝ์Œ
consumeFromGroupB group_b topic1 ๊ฐ™์€ topic1์ด์ง€๋งŒ ๋‹ค๋ฅธ ๊ทธ๋ฃน์ด๋ฏ€๋กœ ๋ณ„๋„๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์Œ
consumeFromTopicC group_c topic2 topic2 ๋ฉ”์‹œ์ง€๋ฅผ group_c๊ฐ€ ์ฝ์Œ
consumeFromTopicD group_c topic3 ๊ฐ™์€ group_c์ง€๋งŒ ๋‹ค๋ฅธ Topic์„ ์ฝ์Œ
consumeFromPartition0 group_d topic4 topic4 ๋ฉ”์‹œ์ง€๋ฅผ group_d๊ฐ€ ์ฝ์Œ

 

RabbitMQ ์˜ˆ์ œ์™€ ๋‹ค๋ฅธ ์ 

๊ตฌ๋ถ„ RabbitMQ  ์˜ˆ์ œ  Kafka ์˜ˆ์ œ
๋ฉ”์‹œ์ง€ ์ €์žฅ ์œ„์น˜ Queue Topic / Partition
๋ณด๋‚ด๋Š” ๊ฐ์ฒด RabbitTemplate KafkaTemplate
๋ฐ›๋Š” ์–ด๋…ธํ…Œ์ด์…˜ @RabbitListener @KafkaListener
๋ฉ”์‹œ์ง€ ์†Œ๋น„ ํ›„ Queue์—์„œ ์‚ฌ๋ผ์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ Topic์— ์„ค์ • ๊ธฐ๊ฐ„ ๋™์•ˆ ๋‚จ์•„ ์žˆ์Œ
์†Œ๋น„ ๊ธฐ์ค€ Queue ๊ตฌ๋… Topic + Consumer Group ๊ตฌ๋…
ํ•ต์‹ฌ ๋ชฉ์  ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ/์ž‘์—… ํ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ ์ €์žฅ/์ฒ˜๋ฆฌ
RabbitMQ๋Š” Queue์— ๋ฉ”์‹œ์ง€๋ฅผ ๋„ฃ๊ณ  Consumer๊ฐ€ ๊ฐ€์ ธ๊ฐ€๋ฉด ์‚ฌ๋ผ์ง€๋Š” ์ž‘์—… ํ ๋А๋‚Œ์ด๊ณ ,
Kafka๋Š” Topic์— ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•ด๋‘๊ณ  Consumer Group์ด ๊ฐ์ž ์ฝ์–ด๊ฐ€๋Š” ๋กœ๊ทธ ์ €์žฅ์†Œ ๋А๋‚Œ์ด๋‹ค.
๊ตฌ๋ถ„ RabbitMQ ์˜ˆ์ œ Kafka ์˜ˆ์ œ ์™œ ์ฐจ์ด๋‚˜๋Š”์ง€
ํ”„๋กœ์ ํŠธ ์ˆ˜ Order, Product, Payment 3๊ฐœ Producer, Consumer 2๊ฐœ RabbitMQ ์˜ˆ์ œ๋Š” ์ฃผ๋ฌธ/์ƒํ’ˆ/๊ฒฐ์ œ ์„œ๋น„์Šค ๋ถ„๋ฆฌ ์‹ค์Šต์ด๊ณ , Kafka ์˜ˆ์ œ๋Š” ์†ก์‹ /์ˆ˜์‹  ๊ตฌ์กฐ ํ™•์ธ ์‹ค์Šต์ด๋ผ ๋” ๋‹จ์ˆœํ•จ
๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๋Š” ์ชฝ OrderService ProducerService ๋‘˜ ๋‹ค ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ์„œ๋น„์Šค ๋กœ์ง ๋‹ด๋‹น
๋ฉ”์‹œ์ง€ ๋ฐ›๋Š” ์ชฝ ProductEndpoint, PaymentEndpoint ConsumerService RabbitMQ๋Š” Queue๋ณ„ Consumer๋ฅผ ์„œ๋น„์Šค์ฒ˜๋Ÿผ ๋‚˜๋ˆด๊ณ , Kafka๋Š” ์—ฌ๋Ÿฌ Topic/Group ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ•œ ํด๋ž˜์Šค์— ๋ชจ์•„๋‘ 
์ „์†ก ๊ฐ์ฒด RabbitTemplate KafkaTemplate RabbitMQ๋กœ ๋ณด๋‚ด๋ƒ Kafka๋กœ ๋ณด๋‚ด๋ƒ์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•˜๋Š” ํ…œํ”Œ๋ฆฟ์ด ๋‹ค๋ฆ„
๋ฐ›๋Š” ์–ด๋…ธํ…Œ์ด์…˜ @RabbitListener @KafkaListener ๊ฐ ๋ฉ”์‹œ์ง• ๊ธฐ์ˆ ์—์„œ Queue/Topic์„ ๊ตฌ๋…ํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฆ„
์ €์žฅ ๋‹จ์œ„ Queue Topic / Partition RabbitMQ๋Š” Queue ์ค‘์‹ฌ, Kafka๋Š” Topic๊ณผ Partition ์ค‘์‹ฌ
์†Œ๋น„ ๊ธฐ์ค€ Queue ์ด๋ฆ„ Topic + Consumer Group Kafka๋Š” ๊ฐ™์€ Topic๋„ Group์ด ๋‹ค๋ฅด๋ฉด ๊ฐ๊ฐ ๋”ฐ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ์Œ
์„ค์ • ์ฝ”๋“œ Queue, Exchange, Binding ์„ค์ • ProducerFactory, ConsumerFactory ์„ค์ • RabbitMQ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์–ด๋А Queue๋กœ ๋ณด๋‚ผ์ง€ ์—ฐ๊ฒฐ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค๊ณ , Kafka๋Š” Producer/Consumer ์ƒ์„ฑ ์„ค์ •์ด ์ค‘์š”ํ•จ
๋ฉ”์‹œ์ง€ ์†Œ๋น„ ํ›„ Queue์—์„œ ์ œ๊ฑฐ๋˜๋Š” ํ๋ฆ„ Topic์— ๋‚จ๊ณ  Offset์œผ๋กœ ์ฝ์€ ์œ„์น˜ ๊ด€๋ฆฌ RabbitMQ๋Š” ์ž‘์—… ํ ๋А๋‚Œ, Kafka๋Š” ๋กœ๊ทธ ์ €์žฅ์†Œ ๋А๋‚Œ
์ฝ”๋“œ ๊ตฌ์กฐ ๋А๋‚Œ ์‹ค์ œ MSA ์„œ๋น„์Šค ๋ถ„๋ฆฌ ๋А๋‚Œ Kafka ๊ฐœ๋… ์‹ค์Šต ๋А๋‚Œ RabbitMQ ์ฝ”๋“œ๋Š” ๋„๋ฉ”์ธ ์„œ๋น„์Šค์ฒ˜๋Ÿผ ๋‚˜๋‰˜์—ˆ๊ณ , Kafka ์ฝ”๋“œ๋Š” Topic/Group ๋™์ž‘ ํ™•์ธ์šฉ์— ๊ฐ€๊นŒ์›€

 

ํ•ต์‹ฌ ์ •๋ฆฌ

  • Kafka์—์„œ๋Š” Producer๊ฐ€ Topic์— ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ , Consumer๋Š” Topic์„ ๊ตฌ๋…ํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ๋Š”๋‹ค.
  • ๊ฐ™์€ Topic์ด๋ผ๋„ Consumer Group์ด ๋‹ค๋ฅด๋ฉด ๊ฐ๊ฐ ๋…๋ฆฝ์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰, Kafka๋Š” ๋ฉ”์‹œ์ง€๋ฅผ “์ „๋‹ฌํ•˜๊ณ  ๋”์ด๋ผ๊ธฐ๋ณด๋‹ค, Topic์— ๊ธฐ๋กํ•ด๋‘๊ณ  ์—ฌ๋Ÿฌ Consumer Group์ด ํ•„์š”์— ๋”ฐ๋ผ ์ฝ์–ด๊ฐ€๋Š” ๊ตฌ์กฐ์ด๋‹ค.

 

๐Ÿ—จ๏ธ ์ถ”๊ฐ€ ์‹ค์Šต (SAGA Pattern)

 

OrderApplication

  • OrderApplication ์ชฝ Saga ์‹œ์ž‘ ๋ถ€๋ถ„. ํ•ต์‹ฌ์€ ์ฃผ๋ฌธ ์ƒ์„ฑ → ์ƒํ’ˆ/๊ฒฐ์ œ ํ๋กœ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰ → ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์œผ๋ฉด ์ฃผ๋ฌธ ์ทจ์†Œ๋กœ ๋ณด์ƒ ์ฒ˜๋ฆฌ ํ๋ฆ„์ž„.
    • Saga ํŒจํ„ด์€ ์ „์ฒด ์ž‘์—…์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ๋Š” ๋Œ€์‹ , ๊ฐ ์„œ๋น„์Šค๊ฐ€ ์ž๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ค‘๊ฐ„์— ์‹คํŒจํ•˜๋ฉด ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ด์ „ ์ž‘์—…์„ ๋˜๋Œ๋ฆฌ๋Š” ๋ฐฉ์‹์ด๋‹ค.
    • ์ด ์ฝ”๋“œ์—์„œ Order์˜ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜์€ cancelOrder()์ด๊ณ , ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š” ์ž…๊ตฌ๋Š” @RabbitListener(queues = "${message.queue.err.order}")์ด๋‹ค.
build.gradle > dependencies
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
# ํ˜„์žฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„
spring.application.name=order

# ์ •์ƒ ์ฒ˜๋ฆฌ์šฉ Exchange
# Order๊ฐ€ Product/Payment ์ชฝ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉ
message.exchange=market

# ์ƒํ’ˆ ์ฒ˜๋ฆฌ Queue
message.queue.product=market.product

# ๊ฒฐ์ œ ์ฒ˜๋ฆฌ Queue
message.queue.payment=market.payment

# ์—๋Ÿฌ ์ฒ˜๋ฆฌ์šฉ Exchange
# Product/Payment ์ฒ˜๋ฆฌ ์ค‘ ์‹คํŒจํ–ˆ์„ ๋•Œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉ
message.err.exchange=market.err

# Order๊ฐ€ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ Queue
# ์ด Queue๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๋ฉด Order๋Š” ์ฃผ๋ฌธ์„ ์ทจ์†Œ ์ฒ˜๋ฆฌํ•จ
message.queue.err.order=market.err.order

# Product๊ฐ€ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ Queue
# ๊ฒฐ์ œ ์‹คํŒจ ๋“ฑ์œผ๋กœ ์ƒํ’ˆ ์žฌ๊ณ  ๋ณต๊ตฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
message.queue.err.product=market.err.product

# RabbitMQ ์„œ๋ฒ„ ์ ‘์† ์ •๋ณด
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

 

DeliveryMessage.java
  • DeliveryMessage๋Š” ์„œ๋น„์Šค ์‚ฌ์ด์—์„œ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฉ”์‹œ์ง€ DTO๋‹ค.
    • Order, Product, Payment๊ฐ€ ๊ฐ™์€ ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
import lombok.*;

import java.util.UUID;

@Data
// getter, setter, toString, equals ๋“ฑ์„ ์ž๋™ ์ƒ์„ฑ
@Builder
// DeliveryMessage.builder() ํ˜•ํƒœ๋กœ ๊ฐ์ฒด ์ƒ์„ฑ ๊ฐ€๋Šฅ
@ToString
@AllArgsConstructor
// ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž ์ƒ์„ฑ
@NoArgsConstructor
// ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ์ƒ์„ฑ
// RabbitMQ๊ฐ€ JSON ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ
public class DeliveryMessage {

    // ์ฃผ๋ฌธ ID
    // ์—ฌ๋Ÿฌ ์„œ๋น„์Šค๊ฐ€ ๊ฐ™์€ ์ฃผ๋ฌธ์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ ํ•ต์‹ฌ ๊ฐ’
    private UUID orderId;

    // ๊ฒฐ์ œ ID
    // ๊ฒฐ์ œ ์„œ๋น„์Šค์—์„œ ๊ฒฐ์ œ ์„ฑ๊ณต ํ›„ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’
    private UUID paymentId;

    // ์ฃผ๋ฌธํ•œ ์‚ฌ์šฉ์ž ID
    private String userId;

    // ์ƒํ’ˆ ID
    private Integer productId;

    // ์ฃผ๋ฌธ ์ƒํ’ˆ ์ˆ˜๋Ÿ‰
    private Integer productQuantity;

    // ๊ฒฐ์ œ ๊ธˆ์•ก
    private Integer payAmount;

    // ์‹คํŒจ ์›์ธ
    // Saga ๋ณด์ƒ ์ฒ˜๋ฆฌ์—์„œ ์–ด๋–ค ์ด์œ ๋กœ ์‹คํŒจํ–ˆ๋Š”์ง€ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉ
    private String errorType;
}
Order.java
  • cancelOrder()๋Š” Saga์—์„œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ์—ญํ• ์„ ํ•œ๋‹ค.
    • ๊ฒฐ์ œ๋‚˜ ์ƒํ’ˆ ์ฒ˜๋ฆฌ ์‹คํŒจ ์‹œ ์ด๋ฏธ ์ƒ์„ฑ๋œ ์ฃผ๋ฌธ์„ ์ทจ์†Œ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.
import lombok.Builder;
import lombok.Data;
import lombok.ToString;

import java.util.UUID;

@Builder
// Order.builder()๋กœ ๊ฐ์ฒด ์ƒ์„ฑ ๊ฐ€๋Šฅ
@Data
// getter, setter ์ž๋™ ์ƒ์„ฑ
@ToString
public class Order {

    // ์ฃผ๋ฌธ ID
    private UUID orderId;

    // ์ฃผ๋ฌธํ•œ ์‚ฌ์šฉ์ž ID
    private String userId;

    // ์ฃผ๋ฌธ ์ƒํƒœ
    // ์˜ˆ: RECEIPT, CANCEL
    private String orderStatus;

    // ์ฃผ๋ฌธ ์‹คํŒจ ์›์ธ
    private String errorType;

    public void cancelOrder(String receiveErrorType) {

        // Saga ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜
        // ๋’ค ๋‹จ๊ณ„์—์„œ ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ ์ทจ์†Œ๋กœ ๋ณ€๊ฒฝ
        orderStatus = "CANCEL";

        // ์™œ ์ทจ์†Œ๋๋Š”์ง€ ์‹คํŒจ ์›์ธ ์ €์žฅ
        errorType = receiveErrorType;
    }
}
  • Order๋Š” ํ˜„์žฌ ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฃผ๋ฌธ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋‹ค. ์ฆ‰, “์ด ์ฃผ๋ฌธ์ด ์ง€๊ธˆ ์ ‘์ˆ˜ ์ƒํƒœ์ธ์ง€(RECEIPT), ์ทจ์†Œ ์ƒํƒœ์ธ์ง€(CANCEL)“ ๊ฐ™์€ ์‹ค์ œ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค. ๊ทธ๋ž˜์„œ orderStore.put(...)์ฒ˜๋Ÿผ ์ฃผ๋ฌธ ์ €์žฅ์†Œ(Map/DB)์— ์ €์žฅ๋˜๋ฉฐ, ์ฃผ๋ฌธ ์กฐํšŒ๋‚˜ ์ƒํƒœ ๋ณ€๊ฒฝ์— ์‚ฌ์šฉ๋œ๋‹ค.
  • ๋ฐ˜๋ฉด DeliveryMessage๋Š” ์„œ๋น„์Šค๋ผ๋ฆฌ RabbitMQ๋กœ ์ฃผ๊ณ ๋ฐ›๊ธฐ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ DTO๋‹ค. Product ์„œ๋น„์Šค๋‚˜ Payment ์„œ๋น„์Šค๊ฐ€ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ๋‹ด์•„์„œ ์ „๋‹ฌํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
OrderApplicationQueueConfig.java
  • ์ •์ƒ ํ๋ฆ„์šฉ Exchange/Queue์™€ ์—๋Ÿฌ ๋ณด์ƒ ํ๋ฆ„์šฉ Exchange/Queue๋ฅผ ๋”ฐ๋กœ ๋‘”๋‹ค.
    • ์ด๊ฒŒ Saga ํŒจํ„ด์—์„œ “์‹คํŒจ ์‹œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ๋ฉ”์‹œ์ง€”๋ฅผ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ๋‹ค.
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// RabbitMQ Queue, Exchange, Binding ์„ค์ • ํด๋ž˜์Šค
public class OrderApplicationQueueConfig {

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {

        // Java ๊ฐ์ฒด๋ฅผ RabbitMQ ๋ฉ”์‹œ์ง€๋กœ ๋ณด๋‚ผ ๋•Œ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” Converter
        // DeliveryMessage ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ํ•„์š”
        return new Jackson2JsonMessageConverter();
    }

    @Value("${message.exchange}")
    private String exchange;

    @Value("${message.queue.product}")
    private String queueProduct;

    @Value("${message.queue.payment}")
    private String queuePayment;

    @Value("${message.err.exchange}")
    private String exchangeErr;

    @Value("${message.queue.err.order}")
    private String queueErrOrder;

    @Value("${message.queue.err.product}")
    private String queueErrProduct;

    @Bean
    public TopicExchange exchange() {

        // ์ •์ƒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ Exchange
        // Product, Payment Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ผ์šฐํŒ…ํ•จ
        return new TopicExchange(exchange);
    }

    @Bean
    public Queue queueProduct() {

        // ์ƒํ’ˆ ์ฒ˜๋ฆฌ์šฉ Queue
        return new Queue(queueProduct);
    }

    @Bean
    public Queue queuePayment() {

        // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ์šฉ Queue
        return new Queue(queuePayment);
    }

    @Bean
    public Binding bindingProduct() {

        // ์ •์ƒ Exchange์™€ ์ƒํ’ˆ Queue๋ฅผ ์—ฐ๊ฒฐ
        // routing key๊ฐ€ queueProduct ๊ฐ’์ด๋ฉด market.product Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ
        return BindingBuilder
                .bind(queueProduct())
                .to(exchange())
                .with(queueProduct);
    }

    @Bean
    public Binding bindingPayment() {

        // ์ •์ƒ Exchange์™€ ๊ฒฐ์ œ Queue๋ฅผ ์—ฐ๊ฒฐ
        // routing key๊ฐ€ queuePayment ๊ฐ’์ด๋ฉด market.payment Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ
        return BindingBuilder
                .bind(queuePayment())
                .to(exchange())
                .with(queuePayment);
    }

    @Bean
    public TopicExchange exchangeErr() {

        // ์—๋Ÿฌ/๋ณด์ƒ ์ฒ˜๋ฆฌ์šฉ Exchange
        // ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ผ์šฐํŒ…ํ•จ
        return new TopicExchange(exchangeErr);
    }

    @Bean
    public Queue queueErrOrder() {

        // Order ๋ณด์ƒ ์ฒ˜๋ฆฌ Queue
        // ์ด Queue๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๋ฉด ์ฃผ๋ฌธ ์ทจ์†Œ ์ฒ˜๋ฆฌ
        return new Queue(queueErrOrder);
    }

    @Bean
    public Queue queueErrProduct() {

        // Product ๋ณด์ƒ ์ฒ˜๋ฆฌ Queue
        // ์žฌ๊ณ  ๋ณต๊ตฌ ๊ฐ™์€ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ
        return new Queue(queueErrProduct);
    }

    @Bean
    public Binding bindingErrOrder() {

        // ์—๋Ÿฌ Exchange์™€ Order ์—๋Ÿฌ Queue ์—ฐ๊ฒฐ
        return BindingBuilder
                .bind(queueErrOrder())
                .to(exchangeErr())
                .with(queueErrOrder);
    }

    @Bean
    public Binding bindingErrProduct() {

        // ์—๋Ÿฌ Exchange์™€ Product ์—๋Ÿฌ Queue ์—ฐ๊ฒฐ
        return BindingBuilder
                .bind(queueErrProduct())
                .to(exchangeErr())
                .with(queueErrProduct);
    }
}
OrderEndpoint.java
  • OrderEndpoint๋Š” HTTP ์š”์ฒญ๋„ ๋ฐ›๊ณ , RabbitMQ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋„ ๋ฐ›๋Š”๋‹ค.
    • @PostMapping("/order")๋Š” ์ฃผ๋ฌธ ์‹œ์ž‘์ ์ด๊ณ , @RabbitListener๋Š” ์‹คํŒจ ์‹œ ๋ณด์ƒ ์ฒ˜๋ฆฌ ์‹œ์ž‘์ ์ด๋‹ค.
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@Slf4j
// log.info() ์‚ฌ์šฉ ๊ฐ€๋Šฅ
@RestController
// HTTP ์š”์ฒญ์„ ๋ฐ›๋Š” Controller
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ž๋™ ์ƒ์„ฑ
public class OrderEndpoint {

    private final OrderService orderService;

    private final RabbitTemplate rabbitTemplate;

    @GetMapping("order/{orderId}")
    public ResponseEntity<Order> getOrder(@PathVariable UUID orderId) {

        // ์ฃผ๋ฌธ ID๋กœ ์ฃผ๋ฌธ ์กฐํšŒ
        Order order = orderService.getOrder(orderId);

        // ์กฐํšŒํ•œ ์ฃผ๋ฌธ์„ HTTP 200 ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜
        return ResponseEntity.ok(order);
    }

    @PostMapping("/order")
    public ResponseEntity<Order> order(@RequestBody OrderRequestDto orderRequestDto) {

        // ์ฃผ๋ฌธ ์ƒ์„ฑ ์š”์ฒญ
        // ๋‚ด๋ถ€์—์„œ ์ฃผ๋ฌธ์„ ๋งŒ๋“ค๊ณ  Product/Payment ์ชฝ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ
        Order order = orderService.createOrder(orderRequestDto);

        return ResponseEntity.ok(order);
    }

    @RabbitListener(queues = "${message.queue.err.order}")
    public void errOrder(DeliveryMessage message) {

        // Order ์—๋Ÿฌ Queue๋ฅผ ๊ฐ์‹œํ•˜๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๋ฉด ์ž๋™ ์‹คํ–‰
        // ์ฆ‰ ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด ์—ฌ๊ธฐ์„œ ๋ฐ›์Œ
        log.info("ERROR RECEIVE !!!");

        // Saga ๋ณด์ƒ ์ฒ˜๋ฆฌ
        // ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฃผ๋ฌธ ์ทจ์†Œ ์ฒ˜๋ฆฌ
        orderService.rollbackOrder(message);
    }

    @Data
    public static class OrderRequestDto {

        // ์ฃผ๋ฌธ ์š”์ฒญ ์‚ฌ์šฉ์ž ID
        private String userId;

        // ์ฃผ๋ฌธ ์ƒํ’ˆ ID
        private Integer productId;

        // ์ฃผ๋ฌธ ์ˆ˜๋Ÿ‰
        private Integer productQuantity;

        // ๊ฒฐ์ œ ๊ธˆ์•ก
        private Integer payAmount;

        public Order toOrder() {

            // ์š”์ฒญ DTO๋ฅผ Order ๋„๋ฉ”์ธ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
            return Order.builder()

                    // ์ฃผ๋ฌธ ์ƒ์„ฑ ์‹œ ์ƒˆ๋กœ์šด ์ฃผ๋ฌธ ID ๋ฐœ๊ธ‰
                    .orderId(UUID.randomUUID())

                    // ์š”์ฒญํ•œ ์‚ฌ์šฉ์ž ID ์ €์žฅ
                    .userId(userId)

                    // ์ตœ์ดˆ ์ฃผ๋ฌธ ์ƒํƒœ
                    // ์•„์ง ์ตœ์ข… ์™„๋ฃŒ๊ฐ€ ์•„๋‹ˆ๋ผ ์ ‘์ˆ˜ ์ƒํƒœ
                    .orderStatus("RECEIPT")
                    .build();
        }

        public DeliveryMessage toDeliveryMessage(UUID orderId) {

            // ๋‹ค๋ฅธ ์„œ๋น„์Šค๋กœ ๋ณด๋‚ผ ๋ฉ”์‹œ์ง€ ๊ฐ์ฒด ์ƒ์„ฑ
            return DeliveryMessage.builder()

                    // ์ƒ์„ฑ๋œ ์ฃผ๋ฌธ ID๋ฅผ ๋ฉ”์‹œ์ง€์— ํฌํ•จ
                    .orderId(orderId)

                    // ์ƒํ’ˆ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ์ •๋ณด
                    .productId(productId)
                    .productQuantity(productQuantity)

                    // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ์— ํ•„์š”ํ•œ ์ •๋ณด
                    .payAmount(payAmount)
                    .build();
        }
    }
}
OrderService
  • OrderService๋Š” ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•œ ๋’ค RabbitMQ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ , ์ดํ›„ ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ ์‹คํŒจ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๋ฉด rollbackOrder()๋กœ ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•˜๋Š” Saga ๋ณด์ƒ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Slf4j
// log.info() ์‚ฌ์šฉ ๊ฐ€๋Šฅ
@Service
// ์ฃผ๋ฌธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ํด๋ž˜์Šค
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ž๋™ ์ƒ์„ฑ
public class OrderService {

    @Value("${message.queue.product}")
    // application.properties์˜ Queue ์ด๋ฆ„ ์ฃผ์ž…
    // market.product ๊ฐ’์ด ๋“ค์–ด๊ฐ
    private String productQueue;

    // RabbitMQ ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    // Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ํ•ต์‹ฌ ๊ฐ์ฒด
    private final RabbitTemplate rabbitTemplate;

    // ๊ฐ€์งœ ๋ฉ”๋ชจ๋ฆฌ DB
    // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด JPA Repository/DB ์‚ฌ์šฉ
    private Map<UUID, Order> orderStore = new HashMap<>();

    public Order createOrder(OrderEndpoint.OrderRequestDto orderRequestDto) {

        // ์š”์ฒญ DTO๋ฅผ Order ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
        // ์ฃผ๋ฌธ ์ƒํƒœ๋Š” RECEIPT ์ƒํƒœ๋กœ ์ƒ์„ฑ๋จ
        Order order = orderRequestDto.toOrder();

        // ๋‹ค๋ฅธ ์„œ๋น„์Šค(Product/Payment)๋กœ ๋ณด๋‚ผ ๋ฉ”์‹œ์ง€ ์ƒ์„ฑ
        DeliveryMessage deliveryMessage =
                orderRequestDto.toDeliveryMessage(order.getOrderId());

        // ์ฃผ๋ฌธ ์ €์žฅ
        // ํ˜„์žฌ๋Š” ๋ฉ”๋ชจ๋ฆฌ Map์— ์ €์žฅ
        orderStore.put(order.getOrderId(), order);

        // ์ „์†กํ•  ๋ฉ”์‹œ์ง€ ๋กœ๊ทธ ์ถœ๋ ฅ
        log.info("send Message : {}",deliveryMessage.toString());

        // RabbitMQ Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
        //
        // productQueue:
        // ์–ด๋А Queue๋กœ ๋ณด๋‚ผ์ง€
        //
        // deliveryMessage:
        // ์‹ค์ œ ์ „์†ก ๋ฐ์ดํ„ฐ
        //
        // ์ฆ‰:
        // "์ƒํ’ˆ ์„œ๋น„์Šค์•ผ ์ด ์ฃผ๋ฌธ ์ฒ˜๋ฆฌํ•ด์ค˜"
        // ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ
        rabbitTemplate.convertAndSend(
                productQueue,
                deliveryMessage
        );

        // ์ƒ์„ฑ๋œ ์ฃผ๋ฌธ ๋ฐ˜ํ™˜
        return order;
    }

    public void rollbackOrder(DeliveryMessage message) {

        // ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์•ˆ์˜ orderId๋กœ ์ฃผ๋ฌธ ์กฐํšŒ
        Order order = orderStore.get(message.getOrderId());

        // Saga ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜
        // ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ CANCEL๋กœ ๋ณ€๊ฒฝ
        order.cancelOrder(message.getErrorType());

        // ์ทจ์†Œ ๊ฒฐ๊ณผ ๋กœ๊ทธ ์ถœ๋ ฅ
        log.info(order.toString());
    }

    public Order getOrder(UUID orderId) {

        // ์ฃผ๋ฌธ ์กฐํšŒ
        return orderStore.get(orderId);
    }
}
  • ์ „์ฒด ํ๋ฆ„
    1. ์‚ฌ์šฉ์ž๊ฐ€ /order ๋กœ ์ฃผ๋ฌธ ์š”์ฒญ
    2. OrderService๊ฐ€ ์ฃผ๋ฌธ ์ƒ์„ฑ
    3. DeliveryMessage๋ฅผ ๋งŒ๋“ค์–ด Product/Payment Queue๋กœ ์ „์†ก
    4. Product๋‚˜ Payment์—์„œ ์‹คํŒจ ๋ฐœ์ƒ
    5. ์‹คํŒจํ•œ ์„œ๋น„์Šค๊ฐ€ ์—๋Ÿฌ Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
    6. OrderEndpoint์˜ @RabbitListener๊ฐ€ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
    7. orderService.rollbackOrder(message) ์‹คํ–‰
    8. ์ฃผ๋ฌธ ์ƒํƒœ CANCEL ์ฒ˜๋ฆฌ 
Order ์ƒ์„ฑ
↓
Order ์ƒํƒœ ์ €์žฅ
↓
DeliveryMessage ์ƒ์„ฑ
↓
RabbitMQ๋กœ Product/Payment์— ์ „๋‹ฌ
↓
์‹คํŒจ ์‹œ DeliveryMessage๋กœ ์—๋Ÿฌ ์ „๋‹ฌ
↓
Order ์ƒํƒœ CANCEL ๋ณ€๊ฒฝ

 

ProductApplication

build.gradle > dependencies
dependencies {
	// Jackson ์˜์กด์„ฑ
	implementation 'com.fasterxml.jackson.core:jackson-databind'
	implementation 'com.fasterxml.jackson.core:jackson-core'
	implementation 'com.fasterxml.jackson.core:jackson-annotations'

	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=product

message.exchange=market
message.queue.product=market.product
message.queue.payment=market.payment

message.err.exchange=market.err
message.queue.err.order=market.err.order
message.queue.err.product=market.err.product


spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
DeliveryMessage.java
import lombok.*;

import java.util.UUID;

@Data
// getter, setter, toString, equals, hashCode ์ž๋™ ์ƒ์„ฑ
@Builder
// DeliveryMessage.builder() ํ˜•ํƒœ๋กœ ๊ฐ์ฒด ์ƒ์„ฑ ๊ฐ€๋Šฅ
@ToString
// ๊ฐ์ฒด ๋‚ด์šฉ์„ ๋กœ๊ทธ๋กœ ๋ณด๊ธฐ ์ข‹๊ฒŒ ์ถœ๋ ฅ
@AllArgsConstructor
// ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž ์ƒ์„ฑ
@NoArgsConstructor
// ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ์ƒ์„ฑ
// RabbitMQ๊ฐ€ JSON ๋ฉ”์‹œ์ง€๋ฅผ DeliveryMessage ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟ€ ๋•Œ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ
public class DeliveryMessage {

    // ์ฃผ๋ฌธ ID
    // Order, Product, Payment๊ฐ€ ๊ฐ™์€ ์ฃผ๋ฌธ์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ’
    private UUID orderId;

    // ๊ฒฐ์ œ ID
    // Payment์—์„œ ๊ฒฐ์ œ ์ƒ์„ฑ ์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’
    private UUID paymentId;

    // ์ฃผ๋ฌธํ•œ ์‚ฌ์šฉ์ž ID
    private String userId;

    // ์ƒํ’ˆ ID
    // Product ์„œ๋น„์Šค๊ฐ€ ์–ด๋–ค ์ƒํ’ˆ์„ ์ฐจ๊ฐํ• ์ง€ ํŒ๋‹จํ•  ๋•Œ ์‚ฌ์šฉ
    private Integer productId;

    // ์ฃผ๋ฌธ ์ˆ˜๋Ÿ‰
    // Product ์„œ๋น„์Šค๊ฐ€ ์žฌ๊ณ  ์ฐจ๊ฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ๋•Œ ์‚ฌ์šฉ
    private Integer productQuantity;

    // ๊ฒฐ์ œ ๊ธˆ์•ก
    // Payment ์„œ๋น„์Šค๊ฐ€ ๊ฒฐ์ œ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ๋•Œ ์‚ฌ์šฉ
    private Integer payAmount;

    // ์‹คํŒจ ์›์ธ
    // Saga ๋กค๋ฐฑ ๊ณผ์ •์—์„œ ์–ด๋–ค ์ด์œ ๋กœ ์‹คํŒจํ–ˆ๋Š”์ง€ ์ „๋‹ฌ
    private String errorType;
}
ProductApplicationQueueConfig.java
  • DeliveryMessage ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ RabbitMQ ๋ฉ”์‹œ์ง€๋กœ ์ฃผ๊ณ ๋ฐ›์œผ๋ ค๋ฉด JSON ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// ProductApplication์˜ RabbitMQ ์„ค์ • ํด๋ž˜์Šค
public class ProductApplicationQueueConfig {

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {

        // Java ๊ฐ์ฒด๋ฅผ JSON ๋ฉ”์‹œ์ง€๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜,
        // JSON ๋ฉ”์‹œ์ง€๋ฅผ Java ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” Converter
        //
        // DeliveryMessage ๊ฐ์ฒด๋ฅผ RabbitMQ๋กœ ์ฃผ๊ณ ๋ฐ›๊ธฐ ์œ„ํ•ด ํ•„์š”
        return new Jackson2JsonMessageConverter();
    }
}
ProductEndpoint.java
  • ProductEndpoint๋Š” ์ •์ƒ ๋ฉ”์‹œ์ง€์™€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋‘˜ ๋‹ค ๋ฐ›๋Š”๋‹ค.
    • ์ •์ƒ ๋ฉ”์‹œ์ง€๋Š” ์žฌ๊ณ  ์ฐจ๊ฐ ์ฒ˜๋ฆฌ, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋Š” ์žฌ๊ณ  ๋ณต๊ตฌ ๊ฐ™์€ ๋ณด์ƒ ์ฒ˜๋ฆฌ๋กœ ์ด์–ด์ง„๋‹ค.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Slf4j
// log.info(), log.error() ์‚ฌ์šฉ ๊ฐ€๋Šฅ
@Component
// RabbitMQ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š” Bean์œผ๋กœ ๋“ฑ๋ก
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ž๋™ ์ƒ์„ฑ
public class ProductEndpoint {

    // ์ƒํ’ˆ ์ฒ˜๋ฆฌ ๋กœ์ง ๋‹ด๋‹น Service
    private final ProductService productService;

    @RabbitListener(queues = "${message.queue.product}")
    // market.product Queue๋ฅผ ๊ตฌ๋…
    // Order ์„œ๋น„์Šค๊ฐ€ productQueue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋จ
    public void receiveMessage(DeliveryMessage deliveryMessage) {

        // ์ƒํ’ˆ ์žฌ๊ณ  ์ฐจ๊ฐ ๋กœ์ง ์‹คํ–‰
        // ์„ฑ๊ณตํ•˜๋ฉด Payment Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋„˜๊ธฐ๊ณ ,
        // ์‹คํŒจํ•˜๋ฉด rollbackProduct()๋กœ ๋ณด์ƒ ์ฒ˜๋ฆฌ ์‹œ์ž‘
        productService.reduceProductAmount(deliveryMessage);

        // Product ์„œ๋น„์Šค๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜๋‹ค๋Š” ๋กœ๊ทธ
        log.info("PRODUCT RECEIVE:{}", deliveryMessage.toString());
    }

    @RabbitListener(queues="${message.queue.err.product}")
    // market.err.product Queue๋ฅผ ๊ตฌ๋…
    // Payment ์„œ๋น„์Šค์—์„œ ๊ฒฐ์ œ ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Product์—๊ฒŒ ๋กค๋ฐฑ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ„
    public void receiveErrorMessage(DeliveryMessage deliveryMessage) {

        // ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜๋‹ค๋Š” ๋กœ๊ทธ
        log.info("ERROR RECEIVE !!!");

        // Product ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ์‹คํ–‰
        // ์˜ˆ: ์ด๋ฏธ ์ฐจ๊ฐํ•œ ์žฌ๊ณ ๋ฅผ ๋ณต๊ตฌํ•˜๋Š” ์—ญํ• 
        productService.rollbackProduct(deliveryMessage);
    }
}
ProductService.java
  • ProductService๋Š” ์ƒํ’ˆ ์ฒ˜๋ฆฌ ์„ฑ๊ณต ์‹œ Payment Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋„˜๊ธฐ๊ณ , ์ƒํ’ˆ ์ฒ˜๋ฆฌ ์‹คํŒจ ๋˜๋Š” ๊ฒฐ์ œ ์‹คํŒจ ๋กค๋ฐฑ ์‹œ Order Error Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ฃผ๋ฌธ ์ทจ์†Œ๋ฅผ ์œ ๋„ํ•œ๋‹ค.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Slf4j
// ๋กœ๊ทธ ์‚ฌ์šฉ
@Service
// ์ƒํ’ˆ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น ํด๋ž˜์Šค
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ฃผ์ž…
public class ProductService {

    // RabbitMQ ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    private final RabbitTemplate rabbitTemplate;

    @Value("${message.queue.payment}")
    // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ Queue ์ด๋ฆ„ ์ฃผ์ž…
    // Product ์ฒ˜๋ฆฌ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด Payment Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ„
    private String paymentQueue;

    @Value("${message.queue.err.order}")
    // Order ์—๋Ÿฌ Queue ์ด๋ฆ„ ์ฃผ์ž…
    // Product ์ฒ˜๋ฆฌ ์‹คํŒจ ๋˜๋Š” ๋กค๋ฐฑ ๋ฐœ์ƒ ์‹œ Order์—๊ฒŒ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ„
    private String orderErrorQueue;

    public void reduceProductAmount(DeliveryMessage deliveryMessage) {

        // ๋ฉ”์‹œ์ง€์—์„œ ์ƒํ’ˆ ID ๊บผ๋‚ด๊ธฐ
        Integer productId = deliveryMessage.getProductId();

        // ๋ฉ”์‹œ์ง€์—์„œ ์ฃผ๋ฌธ ์ˆ˜๋Ÿ‰ ๊บผ๋‚ด๊ธฐ
        Integer productQuantity = deliveryMessage.getProductQuantity();

        // ์‹ค์Šต์šฉ ์‹คํŒจ ์กฐ๊ฑด
        // productId๊ฐ€ 1์ด ์•„๋‹ˆ๊ฑฐ๋‚˜, ์ˆ˜๋Ÿ‰์ด 1๋ณด๋‹ค ํฌ๋ฉด ์‹คํŒจ ์ฒ˜๋ฆฌ
        //
        // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด:
        // ์ƒํ’ˆ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
        // ์žฌ๊ณ  ์ˆ˜๋Ÿ‰ ํ™•์ธ
        // ์žฌ๊ณ  ์ฐจ๊ฐ
        // ๊ฐ™์€ ๋กœ์ง์ด ๋“ค์–ด๊ฐ
        if (productId != 1 || productQuantity > 1) {

            // Product ์ฒ˜๋ฆฌ ์‹คํŒจ ์‹œ ๋ณด์ƒ ์ฒ˜๋ฆฌ
            // Order ์ชฝ์— ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ฃผ๋ฌธ ์ทจ์†Œ๋กœ ์ด์–ด์ง€๊ฒŒ ํ•จ
            this.rollbackProduct(deliveryMessage);
            return;
        }

        // Product ์ฒ˜๋ฆฌ ์„ฑ๊ณต
        // ๋‹ค์Œ ๋‹จ๊ณ„์ธ Payment Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ
        //
        // ์ฆ‰:
        // "์žฌ๊ณ  ์ฒ˜๋ฆฌ๋Š” ์„ฑ๊ณตํ–ˆ์œผ๋‹ˆ ์ด์ œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌํ•ด์ค˜"
        rabbitTemplate.convertAndSend(paymentQueue, deliveryMessage);
    }

    public void rollbackProduct(DeliveryMessage deliveryMessage){

        // Product ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ๋กœ๊ทธ
        log.info("PRODUCT ROLLBACK!!!");

        // errorType์ด ๋น„์–ด ์žˆ์œผ๋ฉด Product ์‹คํŒจ๋กœ ์„ค์ •
        //
        // Payment ์‹คํŒจ๋กœ ๋„˜์–ด์˜จ ๋ฉ”์‹œ์ง€๋Š” ์ด๋ฏธ errorType์ด ์žˆ์„ ์ˆ˜ ์žˆ์Œ
        // ๊ทธ ๊ฒฝ์šฐ ๊ธฐ์กด ์‹คํŒจ ์›์ธ์„ ์œ ์ง€ํ•ด์•ผ ํ•จ
        if(!StringUtils.hasText(deliveryMessage.getErrorType())){

            // ์‹คํŒจ ์›์ธ ์ €์žฅ
            deliveryMessage.setErrorType("PRODUCT ERROR");
        }

        // Order ์—๋Ÿฌ Queue๋กœ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ „์†ก
        //
        // Order ์„œ๋น„์Šค๋Š” ์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ณ 
        // ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ CANCEL๋กœ ๋ณ€๊ฒฝํ•จ
        rabbitTemplate.convertAndSend(orderErrorQueue, deliveryMessage);
    }
}

PaymentApplication

build.gradle > dependencies 
dependencies {
	// Jackson ์˜์กด์„ฑ
	implementation 'com.fasterxml.jackson.core:jackson-databind'
	implementation 'com.fasterxml.jackson.core:jackson-core'
	implementation 'com.fasterxml.jackson.core:jackson-annotations'

	implementation 'org.springframework.boot:spring-boot-starter-amqp'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.amqp:spring-rabbit-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.properties
spring.application.name=payment

message.exchange=market
message.queue.product=market.product
message.queue.payment=market.payment

message.err.exchange=market.err
message.queue.err.order=market.err.order
message.queue.err.product=market.err.product


spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
DeliveryMessage.java
import lombok.*;

import java.util.UUID;

@Data
// getter, setter ์ž๋™ ์ƒ์„ฑ
@Builder
// builder ํŒจํ„ด์œผ๋กœ ๊ฐ์ฒด ์ƒ์„ฑ ๊ฐ€๋Šฅ
@ToString
// ๋กœ๊ทธ ์ถœ๋ ฅ์šฉ ๋ฌธ์ž์—ด ์ž๋™ ์ƒ์„ฑ
@AllArgsConstructor
// ์ „์ฒด ํ•„๋“œ ์ƒ์„ฑ์ž
@NoArgsConstructor
// ๊ธฐ๋ณธ ์ƒ์„ฑ์ž
// JSON ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ
public class DeliveryMessage {

    // ์ฃผ๋ฌธ ID
    private UUID orderId;

    // ๊ฒฐ์ œ ID
    private UUID paymentId;

    // ์‚ฌ์šฉ์ž ID
    private String userId;

    // ์ƒํ’ˆ ID
    private Integer productId;

    // ์ƒํ’ˆ ์ˆ˜๋Ÿ‰
    private Integer productQuantity;

    // ๊ฒฐ์ œ ๊ธˆ์•ก
    private Integer payAmount;

    // ์‹คํŒจ ์›์ธ
    private String errorType;
}
Payment.java
  • Payment๋Š” ๊ฒฐ์ œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์ด๊ณ , DeliveryMessage๋Š” ์„œ๋น„์Šค ๊ฐ„ ์ „๋‹ฌ์šฉ ๋ฉ”์‹œ์ง€ ๊ฐ์ฒด๋‹ค.
import lombok.Builder;
import lombok.Data;

import java.util.UUID;

@Data
// getter, setter ์ž๋™ ์ƒ์„ฑ
@Builder
// Payment.builder()๋กœ ๊ฒฐ์ œ ๊ฐ์ฒด ์ƒ์„ฑ ๊ฐ€๋Šฅ
public class Payment {

    // ๊ฒฐ์ œ ID
    private UUID paymentId;

    // ๊ฒฐ์ œ ์‚ฌ์šฉ์ž ID
    private String userId;

    // ๊ฒฐ์ œ ๊ธˆ์•ก
    // ์—ฌ๊ธฐ์„œ๋Š” String์œผ๋กœ ๋˜์–ด ์žˆ์ง€๋งŒ,
    // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด Integer, Long, BigDecimal ๋“ฑ์„ ์“ฐ๋Š” ๊ฒŒ ๋” ์ž์—ฐ์Šค๋Ÿฌ์›€
    private String payAmount;

    // ๊ฒฐ์ œ ์ƒํƒœ
    // ์˜ˆ: SUCCESS, CANCEL
    private String payStatus;
}
PaymentApplicationQueueConfig.java
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// PaymentApplication์˜ RabbitMQ ์„ค์ • ํด๋ž˜์Šค
public class PaymentApplicationQueueConfig {

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {

        // Java ๊ฐ์ฒด์™€ JSON ๋ฉ”์‹œ์ง€๋ฅผ ์„œ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” Converter
        //
        // Payment ์„œ๋น„์Šค๋„ DeliveryMessage ๊ฐ์ฒด๋ฅผ RabbitMQ๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”
        return new Jackson2JsonMessageConverter();
    }
}
PaymentEndpoint.java
  • PaymentEndpoint๋Š” Payment Queue๋ฅผ ๊ฐ์‹œํ•˜๋‹ค๊ฐ€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๊ฒฐ์ œ ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Slf4j
// ๋กœ๊ทธ ์‚ฌ์šฉ
@Component
// RabbitMQ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋“ฑ๋ก
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ฃผ์ž…
public class PaymentEndpoint {

    // ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋กœ์ง ๋‹ด๋‹น Service
    private final PaymentService paymentService;

    @RabbitListener(queues = "${message.queue.payment}")
    // market.payment Queue๋ฅผ ๊ตฌ๋…
    // Product ์„œ๋น„์Šค๊ฐ€ ๊ฒฐ์ œ Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™ ์‹คํ–‰๋จ
    public void receiveMessage(DeliveryMessage deliveryMessage) {

        // Payment Queue์—์„œ ๋ฐ›์€ ๋ฉ”์‹œ์ง€ ๋กœ๊ทธ
        log.info("PAYMENT RECEIVE : {}", deliveryMessage.toString());

        // ๊ฒฐ์ œ ์ƒ์„ฑ/์ฒ˜๋ฆฌ ๋กœ์ง ์‹คํ–‰
        // ๊ฒฐ์ œ ๊ธˆ์•ก ์กฐ๊ฑด์— ๋”ฐ๋ผ ์„ฑ๊ณต ๋˜๋Š” ๋กค๋ฐฑ ์ฒ˜๋ฆฌ
        paymentService.createPayment(deliveryMessage);
    }
}
PaymentService.java
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Slf4j
// ๋กœ๊ทธ ์‚ฌ์šฉ
@Service
// ๊ฒฐ์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‹ด๋‹น ํด๋ž˜์Šค
@RequiredArgsConstructor
// final ํ•„๋“œ ์ƒ์„ฑ์ž ์ฃผ์ž…
public class PaymentService {

    // RabbitMQ ๋ฉ”์‹œ์ง€ ์ „์†ก ๊ฐ์ฒด
    private final RabbitTemplate rabbitTemplate;

    @Value("${message.queue.err.product}")
    // Product ์—๋Ÿฌ Queue ์ด๋ฆ„ ์ฃผ์ž…
    // ๊ฒฐ์ œ ์‹คํŒจ ์‹œ Product์—๊ฒŒ ๋กค๋ฐฑํ•˜๋ผ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ƒ„
    private String productErrorQueue;

    public void createPayment(DeliveryMessage deliveryMessage) {

        // ๊ฒฐ์ œ ๊ฐ์ฒด ์ƒ์„ฑ
        // ์‹ค์Šต์—์„œ๋Š” ์‹ค์ œ DB ์ €์žฅ์€ ํ•˜์ง€ ์•Š๊ณ  ๊ฐ์ฒด๋งŒ ์ƒ์„ฑ
        Payment payment = Payment.builder()

                // ์ƒˆ๋กœ์šด ๊ฒฐ์ œ ID ์ƒ์„ฑ
                .paymentId(UUID.randomUUID())

                // ๋ฉ”์‹œ์ง€์—์„œ ์‚ฌ์šฉ์ž ID ๊ฐ€์ ธ์˜ค๊ธฐ
                .userId(deliveryMessage.getUserId())

                // ์ผ๋‹จ ๊ฒฐ์ œ ์„ฑ๊ณต ์ƒํƒœ๋กœ ์ƒ์„ฑ
                .payStatus("SUCCESS")
                .build();

        // ๋ฉ”์‹œ์ง€์—์„œ ๊ฒฐ์ œ ๊ธˆ์•ก ๊บผ๋‚ด๊ธฐ
        Integer payAmount = deliveryMessage.getPayAmount();

        // ์‹ค์Šต์šฉ ๊ฒฐ์ œ ์‹คํŒจ ์กฐ๊ฑด
        // ๊ฒฐ์ œ ๊ธˆ์•ก์ด 10000 ์ด์ƒ์ด๋ฉด ์‹คํŒจ ์ฒ˜๋ฆฌ
        //
        // ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด:
        // ์นด๋“œ ์Šน์ธ ์‹คํŒจ
        // ์ž”์•ก ๋ถ€์กฑ
        // ๊ฒฐ์ œ ํ•œ๋„ ์ดˆ๊ณผ
        // ๊ฐ™์€ ์กฐ๊ฑด์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์Œ
        if (payAmount >= 10000) {

            // ๊ฒฐ์ œ ์‹คํŒจ ๋กœ๊ทธ
            log.error("Payment amount exceeds limit: {}", payAmount);

            // ๊ฒฐ์ œ ์ƒํƒœ ์ทจ์†Œ๋กœ ๋ณ€๊ฒฝ
            payment.setPayStatus("CANCEL");

            // ์‹คํŒจ ์›์ธ ์„ค์ •
            deliveryMessage.setErrorType("PAYMENT_LIMIT_EXCEEDED");

            // Payment ๋ณด์ƒ ์ฒ˜๋ฆฌ ์‹œ์ž‘
            // Product ์—๋Ÿฌ Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด์„œ
            // Product๊ฐ€ ์žฌ๊ณ  ๋กค๋ฐฑ์„ ํ•˜๊ฒŒ ๋งŒ๋“ฆ
            this.rollbackPayment(deliveryMessage);
        }
    }

    public void rollbackPayment(DeliveryMessage deliveryMessage) {

        // Payment ๋กค๋ฐฑ ๋กœ๊ทธ
        log.info("PAYMENT ROLLBACK !!!");

        // Product ์—๋Ÿฌ Queue๋กœ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ „์†ก
        //
        // Product๋Š” ์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ณ  rollbackProduct() ์‹คํ–‰
        // ์ดํ›„ Order ์—๋Ÿฌ Queue๋กœ ๋‹ค์‹œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ฃผ๋ฌธ ์ทจ์†Œ๊นŒ์ง€ ์ด์–ด์ง
        rabbitTemplate.convertAndSend(productErrorQueue, deliveryMessage);
    }
}

 

Saga ํŒจํ„ด ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์ •๋ฆฌ

  • ์ด๋ฒˆ ์˜ˆ์ œ๋Š” Order → Product → Payment ์ˆœ์„œ๋กœ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ณ , ์ค‘๊ฐ„์— ์‹คํŒจํ•˜๋ฉด ์ด์ „ ๋‹จ๊ณ„๋กœ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜(๋กค๋ฐฑ)์„ ์ˆ˜ํ–‰ํ•˜๋Š” Saga ํŒจํ„ด ๊ตฌ์กฐ์ด๋‹ค.
ํ”„๋กœ์ ํŠธ ์—ญํ•  ์ฃผ์š” ์ฝ”๋“œ ์ •์ƒ ํ๋ฆ„ ์‹คํŒจ ์‹œ ์—ญํ•  
OrderApplication ์ฃผ๋ฌธ ์ƒ์„ฑ ๋ฐ Saga ์‹œ์ž‘ OrderEndpoint, OrderService ์ฃผ๋ฌธ ์ƒ์„ฑ ํ›„ Product Queue๋กœ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰ Product ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์œผ๋ฉด ์ฃผ๋ฌธ ์ทจ์†Œ(CANCEL)
ProductApplication ์ƒํ’ˆ/์žฌ๊ณ  ์ฒ˜๋ฆฌ ProductEndpoint, ProductService ์žฌ๊ณ  ์ฒ˜๋ฆฌ ์„ฑ๊ณต ์‹œ Payment Queue๋กœ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰ ์‹คํŒจ ์‹œ Order Error Queue๋กœ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰
PaymentApplication ๊ฒฐ์ œ ์ฒ˜๋ฆฌ PaymentEndpoint, PaymentService ๊ฒฐ์ œ ์„ฑ๊ณต ์ฒ˜๋ฆฌ ์‹คํŒจ ์‹œ Product Error Queue๋กœ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰

 

Queue ํ๋ฆ„ ์ •๋ฆฌ

Queue ์˜๋ฏธ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋น„์Šค 
market.product ์ƒํ’ˆ ์ฒ˜๋ฆฌ ์š”์ฒญ Queue Order → Product
market.payment ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ์š”์ฒญ Queue Product → Payment
market.err.product Product ๋กค๋ฐฑ ์š”์ฒญ Queue Payment → Product
market.err.order Order ๋กค๋ฐฑ ์š”์ฒญ Queue Product → Order

 

์ „์ฒด Saga ํ๋ฆ„

Order ์ƒ์„ฑ
↓
Order → Product Queue ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰
↓
Product ์žฌ๊ณ  ์ฒ˜๋ฆฌ
↓
์„ฑ๊ณต ์‹œ Payment Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
↓
Payment ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
↓
๊ฒฐ์ œ ์‹คํŒจ ๋ฐœ์ƒ
↓
Payment → Product Error Queue ๋ฉ”์‹œ์ง€ ์ „์†ก
↓
Product ๋กค๋ฐฑ ์‹คํ–‰
↓
Product → Order Error Queue ๋ฉ”์‹œ์ง€ ์ „์†ก
↓
Order ๋กค๋ฐฑ ์‹คํ–‰
↓
์ฃผ๋ฌธ ์ƒํƒœ CANCEL

 

Saga ํŒจํ„ด ํ•ต์‹ฌ ์ •๋ฆฌ

๊ฐœ๋… ์˜๋ฏธ 
Saga Pattern ๊ฐ ์„œ๋น„์Šค๊ฐ€ ์ž๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹คํŒจ ์‹œ ์ด์ „ ๋‹จ๊ณ„๋กœ ๋ณด์ƒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ƒํƒœ๋ฅผ ๋˜๋Œ๋ฆฌ๋Š” ๋ฐฉ์‹
๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ์‹คํŒจ ์‹œ ์ด์ „ ์ž‘์—…์„ ์ทจ์†Œ/๋ณต๊ตฌํ•˜๋Š” ์ฒ˜๋ฆฌ
๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ ์„œ๋น„์Šค๋ผ๋ฆฌ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  RabbitMQ ๋ฉ”์‹œ์ง€๋กœ ํ†ต์‹ 
๋กค๋ฐฑ ํ๋ฆ„ Payment ์‹คํŒจ → Product ๋กค๋ฐฑ → Order ๋กค๋ฐฑ
ํ•ต์‹ฌ ํŠน์ง• ์ „์ฒด๋ฅผ ํ•˜๋‚˜์˜ DB ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์ง€ ์•Š์Œ
  • Saga ํŒจํ„ด์€ ํ•˜๋‚˜์˜ ํฐ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋Œ€์‹ , ๊ฐ ์„œ๋น„์Šค๊ฐ€ ์ž๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‹คํŒจ ์‹œ ์ด์ „ ๋‹จ๊ณ„๋กœ ๋กค๋ฐฑ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์ƒํƒœ๋ฅผ ๋ณด์ƒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
  • ์ด ์˜ˆ์ œ์—์„œ๋Š” Payment ์‹คํŒจ ์‹œ Product๊ฐ€ ๋กค๋ฐฑ๋˜๊ณ , ์ดํ›„ Order๊นŒ์ง€ ๋กค๋ฐฑ๋˜์–ด ์ตœ์ข…์ ์œผ๋กœ ์ฃผ๋ฌธ ์ƒํƒœ๊ฐ€ CANCEL ๋œ๋‹ค.

 

๐Ÿ—จ๏ธ ํ”„๋กœ๋•ํŠธ ์—๋Ÿฌ ํ™•์ธํ•ด๋ณด๊ธฐ

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

1. ๋ฉ”์‹œ์ง€ ํ๋ฅผ ํ†ตํ•œ ์ดˆ๊ธฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๊ฒ€์ฆ

  • ๋จผ์ € ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•œ ๋’ค ์žฌ์‹คํ–‰ํ•˜์ง€ ์•Š์•„ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ฅผ ํ™•์ธํ•˜๊ณ , ๋‹ค์‹œ ์‹คํ–‰ํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค.
  • ์ •์ƒ์ ์ธ ์ƒํ’ˆ ์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅํ•œ ๋’ค ๊ฒฐ์ œ ๊ธˆ์•ก์„ ํฌ๊ฒŒ ์„ค์ •ํ•˜๋ฉด, Payment ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฒฐ์ œ ํ•œ๋„ ์ดˆ๊ณผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋•Œ ์—๋Ÿฌ ์ •๋ณด๋Š” ํ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋˜๊ณ , ์ตœ์ข…์ ์œผ๋กœ Order Error Queue๊นŒ์ง€ ๋„์ฐฉํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ˜๋Œ€๋กœ ๊ฒฐ์ œ ๊ธˆ์•ก์€ ์ •์ƒ์œผ๋กœ ๋‘๊ณ  ์ƒํ’ˆ ์ˆ˜๋Ÿ‰์„ ์ž˜๋ชป ์ž…๋ ฅํ•˜๋ฉด, ์ด๋ฒˆ์—๋Š” Product ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ƒํ’ˆ ์ˆ˜๋Ÿ‰ ๊ด€๋ จ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋„ Product์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ํ๋ฅผ ํ†ตํ•ด Order ์ชฝ์œผ๋กœ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ์ฆ‰ ์ด ๋‹จ๊ณ„์˜ ํ•ต์‹ฌ์€ Payment ์—๋Ÿฌ์™€ Product ์—๋Ÿฌ๊ฐ€ ๊ฐ๊ฐ Queue๋ฅผ ํ†ตํ•ด ์ •์ƒ์ ์œผ๋กœ ์ „๋‹ฌ๋˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

2. ์˜ค๋” ์„œ๋น„์Šค์˜ ์—๋Ÿฌ ํ ๋ฆฌ์Šค๋„ˆ ๊ตฌํ˜„

  • Order ์„œ๋น„์Šค๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ƒˆ๋กœ์šด ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
@RabbitListener(queues = "${message.queue.err.order}")
  • ์ด ๋ฆฌ์Šค๋„ˆ๋Š” Order Error Queue๋ฅผ ๊ฐ์‹œํ•˜๋‹ค๊ฐ€, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ์ž๋™์œผ๋กœ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • ์ˆ˜์‹ ๋œ ๋ฉ”์‹œ์ง€๋Š” errOrder() ๊ฐ™์€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌ๋œ๋‹ค.
public void errOrder(DeliveryMessage message)
  • ์ด ๊ตฌ์กฐ๋ฅผ ํ†ตํ•ด Product๋‚˜ Payment์—์„œ ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„, ์ตœ์ข…์ ์œผ๋กœ Order ์„œ๋น„์Šค๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

3. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์ค€๋น„์™€ rollbackOrder() ์ƒ์„ฑ

  • Order ์„œ๋น„์Šค๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธด๋‹ค.
log.info("ERROR RECEIVE !!!");
  • ์ด ๋กœ๊ทธ๋Š” Order๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ •์ƒ์ ์œผ๋กœ ์ˆ˜์‹ ํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์šฉ๋„๋‹ค.
  • ์ดํ›„ ์‹ค์ œ ๋กค๋ฐฑ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด rollbackOrder() ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ ๋‹ค.
orderService.rollbackOrder(message);
  • ์ฆ‰ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
Order Error Queue์— ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋„์ฐฉ
↓
@RabbitListener๊ฐ€ ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
↓
ERROR RECEIVE ๋กœ๊ทธ ์ถœ๋ ฅ
↓
rollbackOrder(message) ์‹คํ–‰
 

4. order error ํ ๋ฆฌ์Šค๋‹๊ณผ rollback ์ฒ˜๋ฆฌ

  • rollbackOrder()๋Š” DeliveryMessage๋ฅผ ๋ฐ›์•„ ๋กค๋ฐฑ์„ ์‹œ์ž‘ํ•œ๋‹ค.
  • ์ด๋•Œ DeliveryMessage ์•ˆ์—๋Š” ๋กค๋ฐฑ ๋Œ€์ƒ์ด ๋˜๋Š” orderId๊ฐ€ ๋“ค์–ด ์žˆ๋‹ค.
message.getOrderId()
  • Order ์„œ๋น„์Šค๋Š” ์ด orderId๋ฅผ ์ด์šฉํ•ด ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด๋‘” ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ์ฐพ๋Š”๋‹ค.
Order order = orderStore.get(message.getOrderId());
  • ์—ฌ๊ธฐ์„œ orderStore๋Š” ์‹ค์Šต์šฉ ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ๋‹ค. ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด DB์—์„œ ์ฃผ๋ฌธ์„ ์กฐํšŒํ•˜๋Š” ๋กœ์ง์œผ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ต์‹ฌ์€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์•ˆ์˜ orderId๋ฅผ ๊ธฐ์ค€์œผ๋กœ, ์–ด๋–ค ์ฃผ๋ฌธ์„ ๋กค๋ฐฑํ•ด์•ผ ํ•˜๋Š”์ง€ ์ฐพ๋Š” ๊ฒƒ์ด๋‹ค.

5. ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜ ์ฃผ๋ฌธ ๋กค๋ฐฑ ๊ตฌํ˜„

  • ์กฐํšŒํ•œ ์ฃผ๋ฌธ์€ ์ทจ์†Œ ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
order.cancelOrder(message.getErrorType());
  • cancelOrder()๋Š” ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ CANCEL๋กœ ๋ฐ”๊พธ๊ณ , ์—๋Ÿฌ ์›์ธ๋„ ํ•จ๊ป˜ ์ €์žฅํ•œ๋‹ค.
public void cancelOrder(String receiveErrorType) {
    orderStatus = "CANCEL";
    errorType = receiveErrorType;
}
  • ์˜ˆ๋ฅผ ๋“ค์–ด Payment์—์„œ ๊ฒฐ์ œ ํ•œ๋„ ์ดˆ๊ณผ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋ฉด:
PAYMENT_LIMIT_EXCEEDED
  • ๊ฐ™์€ ์—๋Ÿฌ ํƒ€์ž…์ด Order์— ๊ธฐ๋ก๋œ๋‹ค.
  • ๋˜ํ•œ ์‹ค์Šต ์ค‘ Order ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ์ธ orderStore๊ฐ€ ์ดˆ๊ธฐํ™”๋  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๊ธฐ์กด Queue์— ๋‚จ์•„ ์žˆ๋˜ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ ค ํ•ด๋„, ๋ฉ”๋ชจ๋ฆฌ์— ์ฃผ๋ฌธ ์ •๋ณด๊ฐ€ ์—†์–ด์„œ ์ •์ƒ ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋ ต๋‹ค. ๊ทธ๋ž˜์„œ ํ…Œ์ŠคํŠธ ์ „์—๋Š” RabbitMQ Queue๋ฅผ purgeํ•˜์—ฌ ๊ธฐ์กด ๋ฉ”์‹œ์ง€๋ฅผ ์‚ญ์ œํ•œ ๋’ค ๋‹ค์‹œ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด ํ•„์š”ํ•˜๋‹ค.

6. ์ •์ƒ/์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ

  • Queue๋ฅผ ๋น„์šด ๋’ค Order ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•˜๊ณ  ์ •์ƒ ์ผ€์ด์Šค๋ฅผ ์ž…๋ ฅํ•˜๋ฉด, ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ •์ƒ ์ €์žฅ๋˜๊ณ  errorType์€ ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ๋กœ ์œ ์ง€๋œ๋‹ค.
  • ์ƒํ’ˆ ์ˆ˜๋Ÿ‰์„ ์ž˜๋ชป ์ž…๋ ฅํ•˜๋ฉด Product์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์ด ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ Order๊นŒ์ง€ ์ „๋‹ฌ๋˜์–ด ์ฃผ๋ฌธ ์ƒํƒœ๊ฐ€ CANCEL๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.
  • ์ •์ƒ ์ˆ˜๋Ÿ‰์œผ๋กœ ๋ณ€๊ฒฝํ•œ ๋’ค ๊ฒฐ์ œ ๊ธˆ์•ก์„ ํฌ๊ฒŒ ์ž…๋ ฅํ•˜๋ฉด Payment์—์„œ ๊ฒฐ์ œ ํ•œ๋„ ์ดˆ๊ณผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋„ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ Product๋ฅผ ๊ฑฐ์ณ Order๊นŒ์ง€ ์ „๋‹ฌ๋˜๊ณ , Order ๊ฐ์ฒด๊ฐ€ ์ˆ˜์ •๋˜์–ด ์ตœ์ข…์ ์œผ๋กœ ์ทจ์†Œ ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.
  • ์ •๋ฆฌํ•˜๋ฉด ํ…Œ์ŠคํŠธ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
์ •์ƒ ์š”์ฒญ
→ Order ์ •์ƒ ์ €์žฅ

์ƒํ’ˆ ์ˆ˜๋Ÿ‰ ์˜ค๋ฅ˜
→ Product Error
→ Order CANCEL

๊ฒฐ์ œ ๊ธˆ์•ก ์ดˆ๊ณผ
→ Payment Error
→ Product Rollback
→ Order CANCEL
 

7. ์ „์ฒด ๊ฐœ๋ฐœ ์ˆœ์„œ์™€ ๊ตฌํ˜„ ํ๋ฆ„

  • ์ด๋ฒˆ ์‹ค์Šต์€ ํ•œ ๋ฒˆ์— ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, Queue ๋ผ์šฐํŒ… ํ๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.
  • ๋จผ์ € Order์—์„œ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ๋ฐ›๋Š” ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ์ธ orderStore์— ์ €์žฅํ•˜์˜€๋‹ค. ์ดํ›„ RabbitMQ๋ฅผ ํ†ตํ•ด Product Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ณ , Product๊ฐ€ ์ด๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€๋‹ค.
  • Product๊ฐ€ ์ •์ƒ ์ฒ˜๋ฆฌ๋˜๋ฉด Payment Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ , Payment ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ด๋ฅผ ์ˆ˜์‹ ํ•ด ๊ฒฐ์ œ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค.
  • ์ดํ›„ Payment ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด market.err.product Queue๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์ด๋™ํ•˜๊ณ , Product๊ฐ€ ์ด ์—๋Ÿฌ๋ฅผ ๋ฐ›์•„ ๋กค๋ฐฑ ์ฒ˜๋ฆฌํ•œ ๋’ค ๋‹ค์‹œ market.err.order Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ Order ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ market.err.order Queue๋ฅผ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋‹ค๊ฐ€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด, orderStore์˜ ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ฃผ๋ฌธ ์ƒํƒœ๋ฅผ CANCEL๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

์ตœ์ข… ์ •๋ฆฌ

  • ์ด๋ฒˆ ์‹ค์Šต์˜ ํ•ต์‹ฌ์€ ์ •์ƒ ์ฒ˜๋ฆฌ ํ๋ฆ„๋ฟ ์•„๋‹ˆ๋ผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ๋ฆ„๋„ Queue๋ฅผ ํ†ตํ•ด ์„ค๊ณ„ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
Order
→ Product
→ Payment
  • ์ •์ƒ ํ๋ฆ„์ด ์ง„ํ–‰๋˜๋‹ค๊ฐ€ Payment๋‚˜ Product์—์„œ ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด:
Payment Error
→ Product Rollback
→ Order Rollback

๋˜๋Š”

Product Error
→ Order Rollback
  • ํ๋ฆ„์œผ๋กœ ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜์ด ์ˆ˜ํ–‰๋œ๋‹ค.
  • ํ˜„์žฌ ์‹ค์Šต์—์„œ๋Š” DB ์ €์žฅ ๊ธฐ๋Šฅ์€ ์•„์ง ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜๊ณ , orderStore๋ผ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์‹ค์ œ ์„œ๋น„์Šค์— ๊ฐ€๊น๊ฒŒ ๋งŒ๋“ค๋ ค๋ฉด DB ์—ฐ๊ฒฐ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฃผ๋ฌธ ์ƒํƒœ์™€ ์—๋Ÿฌ ์ •๋ณด๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋„๋ก ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ € Queue ๋ผ์šฐํŒ… ํ๋ฆ„์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์–ด๋–ค Queue๋กœ ๋ฉ”์‹œ์ง€๊ฐ€ ์ด๋™ํ•˜๊ณ , ์–ด๋–ค ์„œ๋น„์Šค๊ฐ€ ๊ทธ Queue๋ฅผ ๋ฆฌ์Šค๋‹ํ•˜๋Š”์ง€ ๋จผ์ € ํŒŒ์•…ํ•ด์•ผ Saga ํŒจํ„ด์˜ ์ „์ฒด ํ๋ฆ„์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๐Ÿ—จ๏ธ JMeter๋กœ ์„ฑ๋Šฅ ํ™•์ธํ•ด๋ณด๊ธฐ

๐Ÿ‘‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ…Œ์ŠคํŠธ๊ณ„ํš > ์ถ”๊ฐ€ > ์“ฐ๋ ˆ๋“œ๋“ค > ์“ฐ๋ ˆ๋“œ ๊ทธ๋ฃน์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ์“ฐ๋ ˆ๋“œ ์†์„ฑ์„ ์ž…๋ ฅํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์ด๋ฏธ์ง€๋Š” 100๋ช…์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ 10๋ฒˆ์”ฉ ์š”์ฒญํ•˜์—ฌ 1000๋ฒˆ์˜ ์š”์ฒญ์ด ์ด๋ฃจ์–ด์ง€๋Š” ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค.

  • ์“ฐ๋ ˆ๋“œ ๊ทธ๋ฃน > ์ถ”๊ฐ€ > ํ‘œ๋ณธ์ถ”์ถœ๊ธฐ > HTTP ์š”์ฒญ์„ ํด๋ฆฝํ•ฉ๋‹ˆ๋‹ค.

  • HTTP ์š”์ฒญ์˜ ํ•ญ๋ชฉ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

  • HTTP ์š”์ฒญ > ์ถ”๊ฐ€ > ์„ค์ • ์—˜๋ฆฌ๋จผํŠธ > HTTP ํ—ค๋” ๊ด€๋ฆฌ์ž๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ํ—ค๋” ๊ด€๋ฆฌ์ž์˜ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ์“ฐ๋ ˆ๋“œ ๊ทธ๋ฃน > ์ถ”๊ฐ€ > ๋ฆฌ์Šค๋„ˆ > ๊ฒฐ๊ณผ๋“ค์˜ ํŠธ๋ฆฌ ๋ณด๊ธฐ ๋ฐ ์š”์•ฝ ๋ณด๊ณ ์„œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒ๋‹จ์˜ ์žฌ์ƒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์žฌ์‹คํ–‰ํ•˜๊ธฐ ์ „์—๋Š” ํด๋ฆฌ์–ด ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์•ฝ ๋ณด๊ณ ์„œ์—์„œ๋Š” ์ฒ˜๋ฆฌ๋Ÿ‰์„ ํ†ตํ•ด TPS๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ๋“ค์˜ ํŠธ๋ฆฌ๋ณด๊ธฐ์—์„œ๋Š” ํ…Œ์ŠคํŠธ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์š”์•ฝ ๋ณด๊ณ ์„œ

JMeter ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์™€ ๋ฉ”์‹œ์ง€ ํ ๊ธฐ๋ฐ˜ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์ •๋ฆฌ

  • ์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” JMeter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ RabbitMQ ๊ธฐ๋ฐ˜ Saga ๊ตฌ์กฐ์˜ ์„ฑ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ , ๋ฉ”์‹œ์ง€ ํ๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ ๋ถ€ํ•˜ ๋ถ„์‚ฐ์˜ ํšจ๊ณผ๋ฅผ ํ™•์ธํ•˜์˜€๋‹ค.

1. ํ˜„์žฌ ์ฝ”๋“œ ๊ตฌ์กฐ์˜ ํ•œ๊ณ„์™€ DB ์—ฐ๋™ ํ•„์š”์„ฑ

  • ํ˜„์žฌ createOrder() ๋กœ์ง์€ ์‹ค์ œ DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๋‹จ์ˆœํžˆ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๋’ค RabbitMQ Queue๋กœ ๋ฉ”์‹œ์ง€๋งŒ ์ „๋‹ฌํ•œ๋‹ค.
  • ์ฆ‰ ํ๋ฆ„์€:
Order ์ƒ์„ฑ
↓
RabbitMQ Product Queue๋กœ ๋ฉ”์‹œ์ง€ ์ „์†ก
↓
์ฆ‰์‹œ ์‘๋‹ต ๋ฐ˜ํ™˜
  • ์ •๋„๋งŒ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ๋กœ๋Š” ๋ฌด๊ฑฐ์šด ์ž‘์—…์ด ๊ฑฐ์˜ ์—†๋‹ค.
rabbitTemplate.convertAndSend(productQueue, deliveryMessage);
  • ์ดํ›„ ๋ฐ”๋กœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ TPS(์ดˆ๋‹น ์ฒ˜๋ฆฌ๋Ÿ‰)๊ฐ€ ๋งค์šฐ ๋†’๊ฒŒ ์ธก์ •๋  ์ˆ˜๋ฐ–์— ์—†๋‹ค.
  • ํ•˜์ง€๋งŒ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ž‘์—…์ด ์ถ”๊ฐ€๋œ๋‹ค.
    • JPA DB ์ €์žฅ
    • ์žฌ๊ณ  ์ฐจ๊ฐ
    • ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
    • ์™ธ๋ถ€ API ํ˜ธ์ถœ
  • ํŠนํžˆ Order, Product, Payment ๊ฐ๊ฐ์ด DB ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•˜๊ณ  TPS๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฐ์†Œํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ฆ‰ ์ด๋ฒˆ ํ…Œ์ŠคํŠธ์˜ ํ•ต์‹ฌ์€: ํ˜„์žฌ ๋†’์€ TPS๋Š” "DB ์ž‘์—…์ด ๊ฑฐ์˜ ์—†๋Š” ๊ตฌ์กฐ"์ด๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๋Š” ์ ์ด๋‹ค.

2. ๋ฉ”์‹œ์ง€ ํ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ํšจ๊ณผ

  • ์ด๋ฒˆ ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ์€ ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.
Order
→ Product
→ Payment
  • ํ๋ฆ„์œผ๋กœ ์ด์–ด์ง€์ง€๋งŒ, ๊ฐ ์„œ๋น„์Šค๋Š” Queue๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค.
  • ์ฆ‰ Order๋Š” Product์™€ Payment์˜ ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ , ๋ฉ”์‹œ์ง€๋งŒ Queue์— ๋„ฃ์€ ๋’ค ์ž์‹ ์˜ ์ž‘์—…์„ ๋๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
Order๋Š” ๋ฉ”์‹œ์ง€๋ฅผ Queue์— ๋„ฃ๊ณ  ๋น ๋ฅด๊ฒŒ ์‘๋‹ต
↓
Product์™€ Payment๋Š” ๊ฐ์ž ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ ๋งŒํผ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„
  • ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.
  • ์ด ๋ฐฉ์‹์˜ ์žฅ์ :
    • ๋ถ€ํ•˜ ๋ถ„์‚ฐ
    • ์„œ๋น„์Šค ๊ฐ„ ๊ฒฐํ•ฉ๋„ ๊ฐ์†Œ
    • ํŠน์ • ์„œ๋น„์Šค ์žฅ์•  ์‹œ ์ „์ฒด ์‹œ์Šคํ…œ ์ค‘๋‹จ ๋ฐฉ์ง€
    • ๋†’์€ ํ™•์žฅ์„ฑ
  • ๋ฐ˜๋Œ€๋กœ Queue ์—†์ด Order๊ฐ€ ๋ชจ๋“  ์ฒ˜๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ด๋‹นํ•˜๋ฉด:
์ฃผ๋ฌธ ์ €์žฅ
→ ์žฌ๊ณ  ์ฒ˜๋ฆฌ
→ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
→ ์‘๋‹ต ๋ฐ˜ํ™˜
  • ๋ชจ๋“  ์ž‘์—…์ด Order์— ์ง‘์ค‘๋˜์–ด ์„ฑ๋Šฅ์ด ๊ธ‰๊ฒฉํžˆ ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰ Queue๋ฅผ ๋„์ž…ํ•˜๋ฉด ๋ฉ”์‹œ์ง€๊ฐ€ Queue์— ์Œ“์ด๊ณ , ๊ฐ ์„œ๋น„์Šค๊ฐ€ ์ž์‹ ์ด ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ ๋งŒํผ๋งŒ ๊ฐ€์ ธ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์ „์ฒด ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ์ด ๋†’์•„์ง„๋‹ค.

3. Order ์•ž๋‹จ Queue ๋„์ž…๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ 

  • ํ˜„์žฌ ๊ตฌ์กฐ์—์„œ๋Š” Order๊ฐ€ DB ์ €์žฅ ํ›„ ๋ฐ”๋กœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— TPS๋ฅผ ๋†’๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ DB ์ €์žฅ ์ž์ฒด๋„ ๋ถ€ํ•˜๊ฐ€ ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋” ๋‚˜์•„๊ฐ€๋ฉด:
์‚ฌ์šฉ์ž ์š”์ฒญ
↓
Order Front Queue ์ ์žฌ
↓
๋น„๋™๊ธฐ Order ์ฒ˜๋ฆฌ
  • ๊ตฌ์กฐ๋กœ ํ™•์žฅํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์ฆ‰ DB ์ €์žฅ ์ž์ฒด๋ฅผ Queue ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด๋‹ค.
  • ์ด ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋Š” ์ฃผ๋ฌธ ์ฆ‰์‹œ:
"์ฃผ๋ฌธ์ด ์š”์ฒญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋งˆ์ดํŽ˜์ด์ง€์—์„œ ์ฃผ๋ฌธ ๋‚ด์—ญ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
  • ๊ฐ™์€ ๋น ๋ฅธ ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์‹ค์ œ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ์™€ ๊ฒฐ์ œ/์žฌ๊ณ  ์ฒ˜๋ฆฌ๋Š” ๋’ค์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰๋œ๋‹ค.
  • ์ฆ‰ ํ•ต์‹ฌ์€ ์‚ฌ์šฉ์ž๋Š” ๋น ๋ฅธ ์‘๋‹ต์„ ๋ฐ›๊ณ , ์‹ค์ œ ๋ฌด๊ฑฐ์šด ์ฒ˜๋ฆฌ๋Š” ๋’ค์—์„œ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ๋‹ค.

4. DB ์—ฐ๋™ ์ดํ›„์˜ ์‹ค์ œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ

  • ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ๋ฌธ ์งํ›„ ์ฃผ๋ฌธ ์กฐํšŒ๋ฅผ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ ๊ตฌ์กฐ์—์„œ๋Š” ์•„์ง ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚˜์ง€ ์•Š์•˜์„ ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ:
"์ฃผ๋ฌธ์ด ์•„์ง ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
"์ž ์‹œ๋งŒ ๊ธฐ๋‹ค๋ ค์ฃผ์„ธ์š”."
  • ๊ฐ™์€ ์ƒํƒœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜ํ•œ Payment ์ƒํƒœ์— ๋”ฐ๋ผ Queue ๋ผ์šฐํŒ…์„ ํ†ตํ•ด Order ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ˆ)

๊ฒฐ์ œ ์„ฑ๊ณต
→ Order ์ƒํƒœ COMPLETE

๊ฒฐ์ œ ์‹คํŒจ
→ Order ์ƒํƒœ CANCEL
  • ๋‹ค๋งŒ Queue ๋ผ์šฐํŒ… ๊ตฌ์กฐ๋Š” ๋งค์šฐ ์œ ์—ฐํ•œ ๋Œ€์‹  ์•„๋ž˜์™€  ๊ฐ™์€ ๋‹จ์ ๋„ ์กด์žฌํ•œ๋‹ค.
    • ๋ผ์šฐํŒ… ๋ณต์žก๋„ ์ฆ๊ฐ€
    • Queue ๊ด€๋ฆฌ ์ฆ๊ฐ€
    • ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ ๋ผ์šฐํŒ… ์ˆ˜์ • ํ•„์š”

5. ์ด๋ฒˆ ์‹ค์Šต์˜ ํ•ต์‹ฌ ์ •๋ฆฌ

  • ์ด๋ฒˆ ์‹ค์Šต์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ–ˆ๋˜ ์ :
๋ฉ”์‹œ์ง€ ํ๋ฅผ ํ™œ์šฉํ•˜๋ฉด
๊ฐ ์„œ๋น„์Šค๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ
๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ
  • ํ˜„์žฌ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ๋ผ TPS๊ฐ€ ๋†’๊ฒŒ ๋‚˜์™”์ง€๋งŒ, ์‹ค์ œ DB/JPA ์—ฐ๋™ ์ดํ›„์—๋Š” ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ํ›จ์”ฌ ํฌ๊ฒŒ ๋“œ๋Ÿฌ๋‚  ์ˆ˜ ์žˆ๋‹ค.
Order๊ฐ€ ๋ชจ๋“  ์ž‘์—…์„ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ๋ณด๋‹ค,
Queue๋ฅผ ํ†ตํ•ด ์—ญํ• ์„ ๋ถ„์‚ฐํ•œ ๊ตฌ์กฐ๊ฐ€
ํ™•์žฅ์„ฑ๊ณผ ์•ˆ์ •์„ฑ ์ธก๋ฉด์—์„œ ํ›จ์”ฌ ์œ ๋ฆฌํ•˜๋‹ค.