๐จ๏ธ ์ค๋ฆฌ์ํ ์ด์
์คํ๋
๋๊ท๋ชจ ์์คํ ์ด๋ ๋ฌด์์ผ๊น์?
- ์ธํฐ๋ท ํ๊ฒฝ์์๋ ์๋ฐฑ๋ง ๋ช ์ ์ฌ์ฉ์๊ฐ ๋์์ ์ ์ํ๊ณ ์ํธ์์ฉํ ์ ์๋ ์์คํ ์ ๊ตฌ์ถํด์ผ ํ๋ ์ํฉ์ด ์์ฃผ ๋ฐ์ํฉ๋๋ค.
- ์ด๋ฌํ ์์คํ ์ ๋จ์ํ ๋ง์ ์ฌ์ฉ์๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ๋ฟ๋ง ์๋๋ผ, ์์ ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ ์ ์งํ๋ฉด์ ๊ณ ์ฑ๋ฅ์ ์ ๊ณตํด์ผ ํฉ๋๋ค.
- ๋๊ท๋ชจ ์์คํ ์ ์ค๊ณํ๊ณ ๊ตฌ์ถํ๋ ๊ณผ์ ์์ ๊ณ ๋ คํด์ผ ํ ์ค์ํ ์์๋ค์ ์์๋ณผ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋๊ท๋ชจ ์คํธ๋ฆผ ์ฒ๋ฆฌ์ ๋ํ ์ดํด๋ฟ๋ง ์๋๋ผ, ๋๊ท๋ชจ ์์คํ ์ ์ ๋ฐ์ ์ธ ๊ตฌ์ฑ๊ณผ ์ด์์ ๋ํ ๊น์ ํต์ฐฐ์ ์ป๊ฒ ๋ ๊ฒ์ ๋๋ค.
๊ทธ์ค ํ๋ฅผ ํตํ ๋๊ท๋ชจ ์คํธ๋ฆผ ์ฒ๋ฆฌ์ ์ง์คํด ๋ณด๊ฒ ์ต๋๋ค.
- ํ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ท๋ชจ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค๋ ์ด์ผ๊ธฐ๋ฅผ ๋ง์ด ๋ค์ด๋ดค์ ๊ฒ์ ๋๋ค.
- ๋ชจ๋ ์ํฉ์์ ํ๋ฅผ ์ฌ์ฉํด์ผ ํ ๊น์? ํ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ์ํฉ๊ณผ ์ฌ์ฉํ์ง ์์๋ ๋๋ ์ํฉ์ด ์์ ๊ฒ์ ๋๋ค.
- ์ฐ๋ฆฌ๋ ์์ ์ ํตํด ์ด๋ ํ ์ํฉ์์ ํ๋ฅผ ์ฌ์ฉํ๊ณ ๋, ํ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ธ ๊ฒ์ ๋๋ค. ๋ํ ํ ๋ผ์ฐํ ์ ํตํด ์๋น์ค๋ฅผ ์ค๊ณํ๊ณ ๊ฐ๋ฐํด ๋ณผ ๊ฒ์ ๋๋ค.
๊ฐ๋ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ ํ ์คํธ๋ ํ์์ ๋๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ํ์๋ ๋ฐ๋์ ํ ์คํธ๋ฅผ ๊ฑฐ์ณ์ผ ํฉ๋๋ค.
- ๊ธฐ๋ฅ์ ๋ํ ํ ์คํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ ์์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ ํ์ธํ๋ ๊ฒ์ ๋น์ฐํ ๊ฒ์ ๋๋ค.
- ์ฐ๋ฆฌ๋ ์ฑ๋ฅ ํ ์คํธ๋ฅผ ํตํด ์ฐ๋ฆฌ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ธก์ ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ค์ ํ๊ฒฝ์์ ์ด๋ป๊ฒ ๋์ํ๋์ง ๋ฏธ๋ฆฌ ๊ฒ์ฆํ๊ณ , ์ ๋ขฐ์ฑ ์๋ ์์คํ ์ ๊ตฌ์ถํ ์ ์๊ฒ ๋ ๊ฒ์ ๋๋ค.
๋๊ท๋ชจ ์์คํ
๐ ๋ฉ์์ง ์์คํ ์ ๊ฐ๋ฐํ๋ ๊ฒ์ ์๋ก ๋ค์ด๋ณด๊ฒ ์ต๋๋ค. ์ด๋ ํ ๊ธฐ์ค์ผ๋ก ์์คํ ์ ์ค๊ณํด์ผ ํ ๊น์?
๋์ ์ ์์์ ์ด๋น ์์ฒญ๋(TPS)
์ฌ์ฉ์์
- ๋๊ท๋ชจ ์์คํ
์ ์ค๊ณํ ๋ ๊ฐ์ฅ ์ค์ํ ์์ ์ค ํ๋๋ ์ฌ์ฉ์ ์์
๋๋ค.
- ์ผ๋ง๋ ๋ง์ ์ฌ์ฉ์๊ฐ ์์คํ ์ ์ฌ์ฉํ ๊ฒ์ธ์ง ํ์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ธฐ์กด ์์คํ ์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ, ์์คํ ๋ชจ๋ํฐ๋ง์ ํตํด ํ๋ฃจ์ ๋ช ๋ช ์ ์ฌ์ฉ์๊ฐ ์ ์ํ๋์ง ์ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋จ์ํ ํ๋ฃจ ์ ์๋์ ํ์ ํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง ์์ต๋๋ค. ๋ ์ค์ํ ๊ฒ์ ๋์ ์ ์์์ ์์ฒญ ์์ ๋๋ค.
TPS (TPS, Transactions Per Second)
- TPS๋?
- TPS (Transactions Per Second)๋ ์ด๋น ์ฒ๋ฆฌ๋๋ ํธ๋์ญ์ ์ ์๋ฅผ ๋ํ๋ด๋ ์งํ์ ๋๋ค. ์ด๋ ์์คํ ์ ์ฑ๋ฅ์ ํ๊ฐํ๋ ์ค์ํ ์งํ ์ค ํ๋๋ก, ํนํ ๋๊ท๋ชจ ์์คํ ์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. TPS๋ ์์คํ ์ด ์ผ๋ง๋ ๋ง์ ์์ฒญ์ ๋์์ ์ฒ๋ฆฌํ ์ ์๋์ง๋ฅผ ๋ํ๋ด๋ฉฐ, ์์คํ ์ ์ฒ๋ฆฌ ๋ฅ๋ ฅ์ ๊ฐ๋ ํ ์ ์๊ฒ ํด ์ค๋๋ค.
- ์์คํ
์ด ์ด๋น ์์ฒญ๋(TPS, Transactions Per Second)์ ๊ฒฌ๋ ์ ์์ด์ผ ํฉ๋๋ค.
- ์ด๋ฅผ ์ํด์๋ ์ผ๊ฐ ์ ์๋์ด ์๋, ํน์ ์๊ฐ๋์ ์ด๋น ์ ์์ ์์ฒญ๋์ด ๊ฐ์ฅ ๋ง์ ์๊ฐ์ ํ์ ํด์ผ ํฉ๋๋ค. ์ด ์ ๋ณด๋ ์์คํ ์ ์ฉ๋ ๊ณํ์ ์ธ์ฐ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค.
- ์๋ฅผ ๋ค์ด, ๊ธฐ์กด ์์คํ ์ด ์ค์ 9์ 30๋ถ์ ์ด๋น 200๊ฑด์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. ์ด๋ฌํ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์คํ ์ ์ฉ๋์ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ์ ๋ชฉํ๋ ์์คํ ์ด ์์์น ๋ชปํ ์ต๋ ๋ถํ๋ฅผ ๊ฒฌ๋ ์ ์๋๋ก ํ๋ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ 200๊ฑด์ ๊ธฐ์ค์ผ๋ก 1.5๋ฐฐ์ธ 300๊ฑด์ ์ฒ๋ฆฌํ ์ ์๋๋ก ์์คํ ์ ์ค๊ณํ๋ ๊ฒ์ด ๋ฐ๋์งํฉ๋๋ค. ์ด๋ ์์คํ ์ด ์๊ธฐ์น ์์ ํธ๋ํฝ ๊ธ์ฆ์๋ ๊ฒฌ๋ ์ ์๋ ์ฌ์ ๋ฅผ ์ ๊ณตํ ๊ฒ์ ๋๋ค.
- ์์์น ๋ชปํ ์ด๋ฒคํธ๋ก ์ธํด ์ค๊ณ ์์ ์ด์์ ์์ฒญ์ด ๋ชฐ๋ฆฐ๋ค๋ฉด ์์คํ
์ด ์ค๋จ๋ ์ ์์ต๋๋ค.
- ์ด๋ฌํ ์ํฉ์ ๋๋นํ๊ธฐ ์ํด์๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ๋ค์ ํตํด ์์คํ
์ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ฒซ์งธ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ฅผ ๋๋ฆฌ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
- ๋์งธ, ์ค๋ฅ ์ํฉ์์ ์ฌ์ฉ์๊ฐ ๋๊ธฐํ ์ ์๋๋ก ๋๊ธฐ์ด์ ์ค์ ํ๋ ๊ฒ๋ ํ๋์ ๋ฐฉ๋ฒ์ ๋๋ค.
- ์ ์งธ, ์๋ ์ค์ผ์ผ๋ง์ ํตํด ์์คํ ์ ์์์ ๋์ ์ผ๋ก ํ ๋นํ์ฌ ๋ถํ๋ฅผ ๋ถ์ฐ์ํค๋ ๊ฒ๋ ์ค์ํฉ๋๋ค.
- ์ด๋ฌํ ์ํฉ์ ๋๋นํ๊ธฐ ์ํด์๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ๋ค์ ํตํด ์์คํ
์ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
์์ฒญ ์ข ๋ฅ์ ๋ฐ๋ฅธ ๊ฐ๋ฐ
- ๐ ์์คํ
์ด ์ฝ๊ธฐ ์ ์ฉ์ธ์ง, ์ฐ๊ธฐ ๋ฐ ์
๋ฐ์ดํธ๋ฅผ ์ํ ๊ฒ์ธ์ง๋ ์ค์ํฉ๋๋ค. ์ด๋ฅผ ํ์
ํ๊ณ ์ฒ๋ฆฌ ์๋๋ฅผ ๋น ๋ฅด๊ฒ ํ์ฌ ์๋ตํ๋ค๋ฉด ๋ณด๋ค ๋ง์ ์ฌ์ฉ์๋ฅผ ์์ฉํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ์ ๊ณต ๋ฐ ์ ์ฅ์์ ๊ฐ์ฅ ๋ง์ ์๊ฐ์ ์๋ชจํ๋ ๋ถ๋ถ์ ๋๋ถ๋ถ 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๊ฐ ๊ฐ์ ํ์ํ ๋ฐ์ดํฐ๊ฐ ์์ ๋, ์ฌ์ฉ์๋ณ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก ์บ์ฑํ๋ฉด ๋ฐ์ดํฐ๊ฐ ๊ธฐํ๊ธ์์ ์ผ๋ก ์์ฌ ๊ธ๋ฐฉ ํฌํ ์ํ(์ฒ ๊ฐ, ๋ง ๊ฐ ์ด์)๊ฐ ๋ฉ๋๋ค.
- ๋ฌธ์ ์ : ์ฌ์ฉ์๋ณ ๋ง์ถค ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ฐ ์ ์ฅํ๋ฉด ๋์ผํ ๋ฐ์ดํฐ๊ฐ ๊ณ์ ์ค๋ณต ์ ์ฌ๋์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ญ๋นํฉ๋๋ค.
- ํด๊ฒฐ์ฑ
(์ ํ๋ฆฌ์ผ์ด์
๋ ๋ฒจ ํํฐ๋ง):
- Redis์๋ ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ๊ณตํต์ผ๋ก ์ธ ์ ์๋ '๊ฐ๋ฒผ์ด ์ ์ฒด ๋ฐ์ดํฐ ๋ชฉ๋ก(Common Data Set)' ๋ฑ ํ๋๋ง ์ ์ฅํฉ๋๋ค.
- ์ค์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ํจ์ ๋ด๋ถ์์ "์ด ๋ฐ์ดํฐ๊ฐ 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 ์ฌ์ฉ ์ ๋ฐ์ดํฐ ๋ถ์ผ์น ๋ฌธ์ ์ ํด๊ฒฐ ์ ๋ต
- Redis์์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฑฐ(ํผ์ง)ํ ๋ค write DB์์๋ ์ด๋ฏธ ๋ฐ์ดํฐ๊ฐ 'abc'๋ก ๋ณ๊ฒฝ๋์ง๋ง, read DB์๋ ๋ฐ์์ด ์ ๋์ด redis์ ๋ค์ ๊ตฌ ๋ฐ์ดํฐ('a')๊ฐ ์ ์ฌ๋๋ ๋ฐ์ดํฐ ๋ถ์ผ์น ํ์์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์ด ํ์์ ๋ฐ์ดํฐ๊ฐ Redis์์ ์ง์์ง๊ณ , read DB๋ ์์ง ์ ๋ฐ์ดํธ ์ ์ํ๋ผ์ ์ฌ์ฉ์๊ฐ ์์ ๋ฐ์ดํฐ('a')๋ฅผ ๋ค์ ์ฝ๋ ๊ฒฝ์ฐ์ ์ด๋ฐ๋ฉ๋๋ค.
- ๋๊ท๋ชจ ์ฌ์ฉ์ ์์ฒญ์ด ๋์์ ์ฒ๋ฆฌ๋ ๋ ์ด๋ฐ ๋ฌธ์ ๊ฐ ๋์ฑ ๋น๋ฒํ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ๊ทผ๋ณธ ๋์ฑ ์ ํผ์ง(furge) ๋์ ๋ฐ์ดํฐ ์์ฒด๋ฅผ Redis์์ ์ต์ ๊ฐ์ผ๋ก ์ง์ ๊ฐฑ์ ํ๊ฑฐ๋, ๋ฌธ์ ๊ฐ ๋ฐ์ ์ Redis๋ฅผ ํผ์งํ์ฌ ๊ฐ์ ๋ก DB์์ ๋ค์ ๊ฐ์ ธ์ค๊ฒ ๋ง๋๋ ๋ฐฉ์์ด ์์ต๋๋ค. ๋ํ ์บ์ ๋ฐ์ดํฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ์๋ 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(์์์ฑ, ์ผ๊ด์ฑ, ๊ณ ๋ฆฝ์ฑ, ์ง์์ฑ) ์์ฑ์ ๋ณด์ฅํฉ๋๋ค.
- ์์์ฑ (Atomicity): ํธ๋์ญ์ ์ ์ ๋ถ ์ฑ๊ณตํ๊ฑฐ๋ ์ ๋ถ ์คํจํ์ฌ, ๋ถ๋ถ์ ์ธ ์์ ์ํ์ด ์๋ ๊ฒ์ ๋ณด์ฅํฉ๋๋ค.
- ์ผ๊ด์ฑ (Consistency): ํธ๋์ญ์ ์ด ์๋ฃ๋ ํ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ชจ๋ ๋ฌด๊ฒฐ์ฑ ์ ์ฝ ์กฐ๊ฑด์ ์ ์งํฉ๋๋ค.
- ๊ฒฉ๋ฆฌ์ฑ (Isolation): ๋์์ ์คํ๋๋ ํธ๋์ญ์ ์ด ์๋ก ๊ฐ์ญํ์ง ์๋๋ก ๋ณด์ฅํฉ๋๋ค.
- ์ง์์ฑ (Durability): ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ํ์ ๊ฒฐ๊ณผ๋ ์์คํ ์ฅ์ ๊ฐ ๋ฐ์ํด๋ ์๊ตฌ์ ์ผ๋ก ์ ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ๋ฅผ ๋ณํํ๋ ์์
์ ๋จ์๋ก, ACID(์์์ฑ, ์ผ๊ด์ฑ, ๊ณ ๋ฆฝ์ฑ, ์ง์์ฑ) ์์ฑ์ ๋ณด์ฅํฉ๋๋ค.
- ๋ถ์ฐ ํธ๋์ญ์
(Distributed Transaction):
- ์ฌ๋ฌ ๋ถ์ฐ๋ ๋ฐ์ดํฐ ์์ค์ ๊ฑธ์ณ ํธ๋์ญ์ ์ ์ํํ๋ ์์ ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ๋ฌ ๋ง์ดํฌ๋ก์๋น์ค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ๊ฐ ์ด์ ํด๋นํฉ๋๋ค.
- 2PC (Two-Phase Commit):
- ๋ถ์ฐ ํธ๋์ญ์
์ ๊ด๋ฆฌํ๋ ํ๋กํ ์ฝ๋ก, ์ค๋น(Prepare) ๋จ๊ณ์ ์ปค๋ฐ(Commit) ๋จ๊ณ๋ก ๋๋์ด ํธ๋์ญ์
์ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ค๋น ๋จ๊ณ(Prepare Phase): ๊ฐ ์ฐธ์ฌ ๋ ธ๋๋ ํธ๋์ญ์ ์ค๋น ์ํ๋ฅผ ํ์ธํ๊ณ , ์ค๋น ์๋ฃ๋ฅผ ๋ง์คํฐ ๋ ธ๋์ ์๋ฆฝ๋๋ค.
- ์ปค๋ฐ ๋จ๊ณ(Commit Phase): ๋ง์คํฐ ๋ ธ๋๋ ๋ชจ๋ ์ฐธ์ฌ ๋ ธ๋๊ฐ ์ค๋น๋์์์ ํ์ธํ๊ณ , ํธ๋์ญ์ ์ ์ปค๋ฐํ๋๋ก ์ง์ํฉ๋๋ค. ๋ง์ฝ ์ค๋น๊ฐ ์๋ฃ๋์ง ์์ ๋ ธ๋๊ฐ ์๋ค๋ฉด ํธ๋์ญ์ ์ ๋กค๋ฐฑํฉ๋๋ค.
- ๋ถ์ฐ ํธ๋์ญ์
์ ๊ด๋ฆฌํ๋ ํ๋กํ ์ฝ๋ก, ์ค๋น(Prepare) ๋จ๊ณ์ ์ปค๋ฐ(Commit) ๋จ๊ณ๋ก ๋๋์ด ํธ๋์ญ์
์ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ฌ๊ฐ ํจํด(Saga Pattern):
- ํธ๋์ญ์
์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋์ด ์ฒ๋ฆฌํ๊ณ , ๊ฐ ๋จ๊ณ๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ์ปค๋ฐ๋ฉ๋๋ค. ์คํจ ์ ๋ณด์ ํธ๋์ญ์
์ ์คํํ์ฌ ์ํ๋ฅผ ๋กค๋ฐฑํฉ๋๋ค.
- ์ฃผ๋ฌธ ์์ฑ ๋จ๊ณ: ์ฌ์ฉ์๊ฐ ์ฃผ๋ฌธ์ ์์ฑํฉ๋๋ค.
- ๊ฒฐ์ ์ฒ๋ฆฌ ๋จ๊ณ: ๊ฒฐ์ ์๋น์ค๊ฐ ์ฃผ๋ฌธ ๊ฒฐ์ ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ฌ๊ณ ๊ฐ์ ๋จ๊ณ: ์ฌ๊ณ ์๋น์ค๊ฐ ์ฃผ๋ฌธ๋ ์ํ์ ์ฌ๊ณ ๋ฅผ ๊ฐ์์ํต๋๋ค.
- ๊ฐ ๋จ๊ณ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๋ฉด ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ๊ณ , ์คํจํ๋ฉด ์ด์ ๋จ๊ณ์์ ์ํ๋ ์์ ์ ์ทจ์ํฉ๋๋ค.
- ํธ๋์ญ์
์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋์ด ์ฒ๋ฆฌํ๊ณ , ๊ฐ ๋จ๊ณ๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ์ปค๋ฐ๋ฉ๋๋ค. ์คํจ ์ ๋ณด์ ํธ๋์ญ์
์ ์คํํ์ฌ ์ํ๋ฅผ ๋กค๋ฐฑํฉ๋๋ค.
- ์ด๋ฒคํธ ์์ฑ(Event Sourcing):
- ์ํ ๋ณํ๋ฅผ ์ด๋ฒคํธ๋ก ๊ธฐ๋กํ๊ณ , ํด๋น ์ด๋ฒคํธ๋ฅผ ์ฌ์ํ์ฌ ํ์ฌ ์ํ๋ฅผ ์ ์งํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ถ์ฐ ํธ๋์ญ์
์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ์ ์ต์ข
์ํ๋ง ์ ์ฅํ๋ ๊ฒ ์๋๋ผ, “๋ฌด์จ ์ผ์ด ๋ฐ์ํ๋์ง”๋ฅผ ์ด๋ฒคํธ ๋ก๊ทธ์ฒ๋ผ ์์๋๋ก ์ ์ฅํด๋๊ณ ,
๊ทธ ๊ธฐ๋ก๋ค์ ๋ค์ ์ฝ์ด ํ์ฌ ์ํ๋ฅผ ๋ง๋๋ ๋ฐฉ์ - ์ฆ, ์ํ๊ฐ ์์ฒด๋ณด๋ค “๋ณ๊ฒฝ ๊ณผ์ ”์ ์ ์ฅํ๋ ๋ฐฉ์
- โ 5,000์ ์ ๊ธ โก 10,000์ ์ ๊ธ โข 5,000์ ์ถ๊ธ โ "์ด ๊ธฐ๋ก๋ค(์ด๋ฒคํธ)์ ์์๋๋ก ์ญ ๊ณ์ฐ(์ฌ์)ํด๋ณด๋ ์ง๊ธ 10,000์์ด๋ค!"
- ๋์ค์ ๋ถ์ฐ๋ ์์คํ ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ๊ผฌ์ด๋๋ผ๋, ๋ฐ์ํ ์ผ(์ด๋ฒคํธ)๋ค์ ๊ธฐ๋ก์ด ๊ณ ์ค๋ํ ๋จ์์์ผ๋ ์ฒ์๋ถํฐ ๋ค์ ์ญ ๊ณ์ฐํด์ ์ ํํ ํ์ฌ ์ํ๋ฅผ ๋ง์ถ ์ ์๋ค๋ ๊ฒ ํต์ฌ
- ๋ฐ์ดํฐ์ ์ต์ข
์ํ๋ง ์ ์ฅํ๋ ๊ฒ ์๋๋ผ, “๋ฌด์จ ์ผ์ด ๋ฐ์ํ๋์ง”๋ฅผ ์ด๋ฒคํธ ๋ก๊ทธ์ฒ๋ผ ์์๋๋ก ์ ์ฅํด๋๊ณ ,
- ์ํ ๋ณํ๋ฅผ ์ด๋ฒคํธ๋ก ๊ธฐ๋กํ๊ณ , ํด๋น ์ด๋ฒคํธ๋ฅผ ์ฌ์ํ์ฌ ํ์ฌ ์ํ๋ฅผ ์ ์งํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ถ์ฐ ํธ๋์ญ์
์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ต๋๋ค.
๋ถ์ฐ ํธ๋์ญ์ ์ ์ฅ์
- ๋ฐ์ดํฐ ์ผ๊ด์ฑ ๋ณด์ฅ:
- ๋ถ์ฐ๋ ์ฌ๋ฌ ๋ฐ์ดํฐ ์์ค์ ๊ฑธ์ณ ์ผ๊ด๋ ๋ฐ์ดํฐ ์ํ๋ฅผ ์ ์งํ ์ ์์ต๋๋ค. ๋ชจ๋ ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๊ฑฐ๋ ๋ชจ๋ ์คํจํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
- ํ์ฅ์ฑ:
- ๋ถ์ฐ ํธ๋์ญ์ ์ ํตํด ์ฌ๋ฌ ์์คํ ์ด ๋ ๋ฆฝ์ ์ผ๋ก ๋์ํ๋ฉด์๋, ํ์ํ ๊ฒฝ์ฐ ํ๋ ฅํ์ฌ ์ผ๊ด๋ ์ํ๋ฅผ ์ ์งํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ์์คํ ์ ํ์ฅ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ ๋ขฐ์ฑ:
- ํธ๋์ญ์ ์ 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, ์๋ต ์๊ฐ, ์๋ฌ์จ ๋ฑ)๋ฅผ ๋ชจ๋ํฐ๋งํ๊ณ , ์ด์ ์งํ๋ฅผ ๊ฐ์งํ๋ฉด ์๋ฆผ์ ๋ฐ์ ์ ์์ต๋๋ค
- ๋ชจ๋ํฐ๋ง์ ํตํด ์์คํ ์ ์ํ๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ ํ๊ณ , ๋ฌธ์ ๋ฐ์ ์ ๋น ๋ฅด๊ฒ ๋์ํ ์ ์์ต๋๋ค. ๋ํ, ๋ชจ๋ํฐ๋ง ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์คํ ์ ์ฑ๋ฅ์ ๋ถ์ํ๊ณ , ์ต์ ํํ ์ ์์ต๋๋ค.
์ฃผ์์ฌํญ
- ์ค์๊ฐ ์ํ ํ์
:
- ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ ์์คํ ์ ์ฃผ์ ์งํ(TPS, ์๋ต ์๊ฐ, ์๋ฌ์จ ๋ฑ)๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๊ฒ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์์คํ ์ ํ์ฌ ์ํ๋ฅผ ํ์ ํ๊ณ , ์ด์ ์งํ๋ฅผ ๋น ๋ฅด๊ฒ ๊ฐ์งํ ์ ์์ต๋๋ค
- ์๋ ์๋ฆผ:
- ํน์ ์๊ณ์น๋ฅผ ์ด๊ณผํ๋ ๊ฒฝ์ฐ ์๋์ผ๋ก ์๋ฆผ์ ๋ฐ์ ์ ์์ด, ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ๋์ํ ์ ์์ต๋๋ค.
- ์ฑ๋ฅ ๋ถ์:
- ๋ชจ๋ํฐ๋ง ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์คํ ์ ์ฑ๋ฅ์ ๋ถ์ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ์๊ฐ๋์ ํธ๋ํฝ์ด ๊ธ์ฆํ๋ ๊ฒฝ์ฐ ํด๋น ์๊ฐ๋์ ์์์ ์ถ๊ฐ๋ก ํ ๋นํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค.
- ๋ณ๋ชฉ ์ง์ ํ์
:
- ๋ชจ๋ํฐ๋ง์ ํตํด ์์คํ ์ ๋ณ๋ชฉ ์ง์ ์ ํ์ ํ๊ณ , ์ด๋ฅผ ์ต์ ํํ์ฌ ์ ์ฒด ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ต๋๋ค.
- ์ฌ์ ์๋ฐฉ:
- ์ค์๊ฐ ๋ชจ๋ํฐ๋ง์ ํตํด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ์ ์ ์๋ฐฉ ์กฐ์น๋ฅผ ์ทจํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋์คํฌ ์ฌ์ฉ๋์ด ๊ธ์ฆํ๋ ๊ฒฝ์ฐ ๋์คํฌ ์ฉ๋์ ๋ฏธ๋ฆฌ ํ์ฅํ ์ ์์ต๋๋ค.
- ์ ์ํ ๋์:
- ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ ๋ ๋น ๋ฅด๊ฒ ๊ฐ์งํ๊ณ ๋์ํ ์ ์์ด, ์์คํ ๋ค์ดํ์์ ์ต์ํํ ์ ์์ต๋๋ค.
์๋ฒฝํ ์์คํ ์ค๊ณ์ ์ต์ ํ๊ฐ ์ด์์ ์ด์ง๋ง, ๋ฐ๋๋ผ์ธ๊ณผ ๋ณํํ๋ ์๊ตฌ์ฌํญ ๋ฑ ํ์ค์ ์ ์ฝ์ผ๋ก ์ธํด ๋น ๋ฅธ ํ๋กํ ํ์ ๊ตฌํ๊ณผ ์ ์ง์ ๊ฐ์ ์ ๋ต์ด ํ์ํฉ๋๋ค.
๋น ๋ฅธ ํ๋กํ ํ์ ๊ฐ๋ฐ๊ณผ ํ์ ๊ธฐ๋ฅ ์ฐ์ ๊ตฌํ์ ํตํด ์ ์์ผํ๊ฒ ์์คํ ์ ์ ์ง์ ์ผ๋ก ๋ฐ์ ์ํค๋ ์ ๋ต์ด ์ค์ํฉ๋๋ค
๋ก๊น
- ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฃผ์ ์ด๋ฒคํธ๋ฅผ ๋ก๊น ํ์ฌ ๋ฌธ์ ๋ฐ์ ์ ์์ธ์ ์ถ์ ํ ์ ์์ต๋๋ค
- ๋ก๊ทธ๋ 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์์ ์ฌ๋ผ์ง |
์ ์ฒด ํ๋ฆ
- ์ฌ์ฉ์๊ฐ Order ํ๋ก์ ํธ์ /order/1 ์์ฒญ์ ๋ณด๋ธ๋ค.
- Order Service๋ RabbitMQ์ ์ฃผ๋ฌธ ID 1์ ๋ฉ์์ง๋ก ๋ฐํํ๋ค.
- ๋ฉ์์ง๋ ๊ฐ๊ฐ market.product, market.payment Queue์ ๋ค์ด๊ฐ๋ค.
- Product ํ๋ก์ ํธ๋ market.product Queue๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ค๊ฐ ๋ฉ์์ง๋ฅผ ๊ฐ์ ธ๊ฐ๋ค.
- Payment ํ๋ก์ ํธ๋ market.payment Queue๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ค๊ฐ ๋ฉ์์ง๋ฅผ ๊ฐ์ ธ๊ฐ๋ค.
- Consumer๊ฐ ๋ฉ์์ง๋ฅผ ์ ์ ์ฒ๋ฆฌํ๋ฉด Queue์์ ๋ฉ์์ง๊ฐ ์ ๊ฑฐ๋๋ค.
ํต์ฌ ์ ๋ฆฌ
- RabbitMQ ๊ตฌ์กฐ์์๋ Order ์๋น์ค๊ฐ Product, Payment ์๋น์ค๋ฅผ ์ง์ ํธ์ถํ์ง ์๋๋ค.
- ๋์ RabbitMQ์ ๋ฉ์์ง๋ง ๋ฃ๊ณ , Product์ Payment ์๋น์ค๊ฐ ๊ฐ์ ์์ ์ Queue๋ฅผ ๊ตฌ๋ ํ์ฌ ํ์ํ ์์ ์ ์ฒ๋ฆฌํ๋ค.
- ์ฆ, ์๋น์ค ๊ฐ ์ง์ ์์กด์ฑ์ ์ค์ด๊ณ ๋น๋๊ธฐ์ ์ผ๋ก ์์ ์ ๋ถ๋ฆฌํ ์ ์๋ค.
๐จ๏ธ Kafka
Kafka๋?
- Kafka๋ ๋ถ์ฐ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ์ผ๋ก, ์ฃผ๋ก ์ค์๊ฐ ๋ฐ์ดํฐ ํผ๋์ ๋น
๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- ๋ถ์ฐ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ ํ์ด์ ๋งํ๋ฉด:
- ๋ถ์ฐ = ์ฌ๋ฌ ์๋ฒ๊ฐ ๋๋ ์ฒ๋ฆฌ
- ์คํธ๋ฆฌ๋ฐ = ๋ฐ์ดํฐ๊ฐ ๊ณ์ ํ๋ฆ
- ํ๋ซํผ = ๊ทธ๊ฑธ ๊ด๋ฆฌํ๋ ์์คํ
- 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์ ๊ธฐ๋ณธ ๊ตฌ์ฑ ์์

- ๋ฉ์์ง(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);
}
}
- ConsumerService๋ @KafkaListener๋ฅผ ํตํด Kafka Topic์ ๊ตฌ๋ ํ๋ค.
- ๋ฉ์์ง๊ฐ Topic์ ๋ค์ด์ค๋ฉด ํด๋น ๋ฉ์๋๊ฐ ์๋ ์คํ๋๋ค.
- groupId๋ Consumer Group์ ์๋ฏธํ๋ฉฐ,
- ๊ฐ์ 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 ๋ฉ์์ง ํ๋ฆ
- ์ฌ์ฉ์๊ฐ Producer ํ๋ก์ ํธ์ /send?topic=topic1&key=user1&message=hello ์์ฒญ์ ๋ณด๋ธ๋ค.
- ProducerController๊ฐ ์์ฒญ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ๋๋ค.
- ProducerService๊ฐ KafkaTemplate.send(topic, key, message)๋ก Kafka์ ๋ฉ์์ง๋ฅผ ๋ณด๋ธ๋ค.
- Kafka๋ ๋ฉ์์ง๋ฅผ ์ง์ ํ Topic์ ์ ์ฅํ๋ค.
- ๋ฉ์์ง๋ Key๋ฅผ ๊ธฐ์ค์ผ๋ก ํน์ Partition์ ์ ์ฅ๋ ์ ์๋ค.
- Consumer ํ๋ก์ ํธ์ @KafkaListener๊ฐ ํด๋น Topic์ ๊ตฌ๋ ํ๊ณ ์๋ค๊ฐ ๋ฉ์์ง๋ฅผ ์ฝ๋๋ค.
- 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);
}
}
- ์ ์ฒด ํ๋ฆ
- ์ฌ์ฉ์๊ฐ /order ๋ก ์ฃผ๋ฌธ ์์ฒญ
- OrderService๊ฐ ์ฃผ๋ฌธ ์์ฑ
- DeliveryMessage๋ฅผ ๋ง๋ค์ด Product/Payment Queue๋ก ์ ์ก
- Product๋ Payment์์ ์คํจ ๋ฐ์
- ์คํจํ ์๋น์ค๊ฐ ์๋ฌ Queue๋ก ๋ฉ์์ง ์ ์ก
- OrderEndpoint์ @RabbitListener๊ฐ ์คํจ ๋ฉ์์ง ์์
- orderService.rollbackOrder(message) ์คํ
- ์ฃผ๋ฌธ ์ํ 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๋ฅผ ํตํด ์ญํ ์ ๋ถ์ฐํ ๊ตฌ์กฐ๊ฐ
ํ์ฅ์ฑ๊ณผ ์์ ์ฑ ์ธก๋ฉด์์ ํจ์ฌ ์ ๋ฆฌํ๋ค.
'Back-End > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ํ๋ก์ ํธ ๊ด๋ฆฌ ์ฌํ: ์ฑํฐ 2 (CI/CD) (0) | 2026.05.14 |
|---|---|
| ํ๋ก์ ํธ ๊ด๋ฆฌ ์ฌํ: ์ฑํฐ 1 (Docker) (0) | 2026.05.04 |
| MSA (Microservice Architecture) (0) | 2026.04.14 |
| Spring ์๋ จ: ์ฑํฐ 2 (0) | 2026.04.08 |
| Spring ์๋ จ: ์ฑํฐ 1 (0) | 2026.04.07 |