6 minute read

์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ, ๋’ค๋Šฆ์€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•๊ธฐ (with. TestContainer)

๋” ๋น ๋ฅด๊ฒŒ ๋‹ฌ๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž ๊น ๋ฉˆ์ถ˜๋‹ค..

์•ฝ๊ฐ„ ๋ชจ์ˆœ์ด ๋˜๋Š” ๋ฌธ์žฅ์ด๊ธฐ๋„ ํ•œ๋ฐ์š”,

์ด๋ฒˆ ํฌ์ŠคํŒ…์˜ ์ฃผ์ œ๊ฐ€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ๋‹ค์‹œ ์ œ๋ชฉ์„ ์ฝ์–ด๋ณธ๋‹ค๋ฉด

์–ด๋Š์ •๋„๋Š” ๋‹ค๋“ค ๊ณต๊ฐ์ด ๊ฐˆ ๋งŒํ•œ ์ œ๋ชฉ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. (๐Ÿ˜… ํฌํ )

์ž‘๋…„ 10์›”์ธ๊ฐ€์š”?

๊ทธ๋•Œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•œ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์ธ ๋Œ€ํ”ผ๋กœ ๋Š” 2 ๊ฐœ์›” ์•ˆ์— ๊ธฐํš/๊ฐœ๋ฐœ/๋””์ž์ธ์ด ์™„๋ฃŒ๋˜์–ด

๋ฐ๋ชจ ์–ดํ”Œ์„ ์‹œ์—ฐํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ–ˆ์–ด์š”.

์ค‘๊ฐ„์— ๊ธฐํš์ด ๋ณ€๊ฒฝ๋˜๊ธฐ๋„ ํ•˜๊ณ 

์‹œ์—ฐ ์ง์ „๊นŒ์ง€๋„ ์ƒˆ๋กœ์šด ์š”๊ตฌ์‚ฌํ•ญ์ด ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ๊ธฐ์กด ๊ธฐ๋Šฅ ์ŠคํŽ™์ด ์ˆ˜์ •๋˜๋Š” ์ผ์ด ๋งค์šฐ ๋งŽ์•˜๊ธฐ ๋•Œ๋ฌธ์—

์‚ฌ์‹ค์ƒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ํ• ์• ํ•  ์‹œ๊ฐ„๊ณผ ์—ฌ๋ ฅ์ด ์—†์—ˆ๋˜ ๊ฒƒ ๊ฐ™์•„์š”โ€ฆ

ํ•ด์ปคํ†ค ๊ฐ™์€ ๋Š๋‚Œ์œผ๋กœ ํ•˜๋ฃจ ํ•˜๋ฃจ ๊ฐœ๋ฐœํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์™„๋ฒฝํ•œ DDD (๋ฐ๋“œ๋ผ์ธ ์ฃผ๋„ ๊ฐœ๋ฐœ) ์ด์˜€์ฃ  ใ…‹ใ…‹ใ…‹ใ…‹ใ…‹ใ…‹ใ…‹


์ž‘๋…„ ํ•˜๋ฐ˜๊ธฐ๋ฅผ ๋ถˆํƒœ์› ๋˜ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ๋Œ€ํ”ผ๋กœ๋Š”,

์˜ฌ 2์›”๋ถ€ํ„ฐ ๋‹ค์‹œ ๋‹ฌ๋ ค๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์™€ ๐ŸŽ‰ ~

์ž‘๋…„ ํ•˜๋ฐ˜๊ธฐ 2๊ฐœ์›” ๊ฐ„,

์Œ“์—ฌ๋ฒ„๋ฆฐ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ๋ฆฌ์ŠคํŠธ์—…ํ•˜๊ณ  ํ•˜๋‚˜ํ•˜๋‚˜ ํ•ด๊ฒฐํ•ด๋‚˜๊ฐ€๋Š” ์ค‘์ธ๋ฐ์š”

์ด๋ฒˆ์— ์ด์•ผ๊ธฐํ•  ๋‚ด์šฉ์€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์ž…๋‹ˆ๋‹ค. (๋‘๋‘ฅ)


ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ..?

๊ทธ๊ฑฐ ๊ทธ๋ƒฅ ํ•˜๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹ˆ์•ผ..?

๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋Œ€ํ”ผ๋กœ ๋ฐฑ์—”๋“œ์—์„œ๋Š” ์‚ฌ์‹ค ๋งŽ์€ ๊ณ ๋‡Œ์™€ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ์š”..

์ฐจ๊ทผ์ฐจ๊ทผ ์†Œ๊ฐœํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.. ๐Ÿ˜ญ


์ œ๋ชฉ์„ ์ž ๊น ๋‹ค์‹œ ๋ณด๋ฉดโ€ฆ

๋” ๋น ๋ฅด๊ฒŒ ๋‹ฌ๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž ๊น ๋ฉˆ์ถ”๊ธฐ.

์ด ๋ฌธ์žฅ์€,

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์™œ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์ง€ ๊ทธ ์ด์œ ๋ฅผ ํ•จ์ถ•์ ์œผ๋กœ ์•„์ฃผ ์ž˜ ๋‹ด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ๊ธฐํš์ด ๊ณ ๋„ํ™”๋˜๋ฉด์„œ

๊ธฐ์กด ๊ธฐ๋Šฅ์„ ํ™•์žฅ์‹œํ‚ค๊ฑฐ๋‚˜

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ผ์ด ๋ถ„๋ช… ๋งŽ์•„์งˆ ๊ฒƒ ๊ฐ™์€๋ฐ์š”,

image

(์ž๋™ํ™” ๋œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์—†๋‹ค๊ณ  ๊ฐ€์ •)

A ์˜์—ญ์„ ์—ด์‹ฌํžˆ ์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธํ•ด์„œ ๋ณด์žฅ๋œ ๊ธฐ๋Šฅ์ด ๋˜์—ˆ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ณ ๋„ํ™” ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ฉฐ Aโ€™ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜์˜€์–ด์š”.

๋ฌผ๋ก  A์™€ Aโ€™ ์ด ๊ฒน์น˜์ง€ ์•Š๋Š” ๋ถ€๋ถ„์€ ์ƒˆ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด์•ผ๊ฒ ์ง€๋งŒ,

A ์™€ Aโ€™ ์ด ๊ฒน์น˜๋Š” ๋ถ€๋ถ„๋„ ๋˜ ๋‹ค์‹œ ์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด์•ผ๊ฒ ์ฃ .

์ดํ›„์— B ๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด,

์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ธ B ๋ฟ๋งŒ์ด ์•„๋‹ˆ๋ผ A ์™€ B ๊ฐ€ ๊ฒน์น˜๋Š” ๊ตฌ๊ฐ„๋„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•ด์š”.

๊ธฐ์กด์— ํ…Œ์ŠคํŠธ๋ฅผ ํ–ˆ๋˜ ์˜์—ญ์„ ๊ณ„์† ๋ฐ˜๋ณต์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ๋Š” ๋น„ํšจ์œจ์ ์ธ ์ƒํ™ฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ๋Š” ๊ฒฌ๊ณ ํ•œ ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์—†์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ์„œ๋น„์Šค ํ”ผ์ณ๋ฅผ ๊ฐœ๋ฐœํ•˜๋ ค๋ฉด, ํ•ด๋‹น ๊ฐœ๋ฐœ ๊ฑด์ด ํ˜„์žฌ ๋™์ž‘ ์ค‘์ธ ์–ด๋–ค ๋ถ€๋ถ„์— ์˜ํ–ฅ์ด ๊ฐˆ ์ง€

์ˆ˜๋™์œผ๋กœ ํ™•์ธํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์‚ฐ์„ฑ์ด ํ˜„์ €ํžˆ ๋–จ์–ด์ง€๊ฒ ์ฃ .

๋งŒ์•ฝ..

์ž˜ ์ž‘์„ฑ๋œ, ์ž๋™ํ™” ๋œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์žˆ์—ˆ๋”๋ผ๋ฉด

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜, ๊ธฐ์กด ๊ธฐ๋Šฅ์„ ์•„์˜ˆ ๊ฐˆ์•„์—Ž๋”๋ผ๋„ ์ „ํ˜€ ๋‘๋ ค์›€ ์—†์ด ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ .

๋” ๋นจ๋ฆฌ ๋‹ฌ๋ฆฌ๊ธฐ ์œ„ํ•ด, ์ž ๊น ๋ฉˆ์ถฐ์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋Š” ์ด๊ฑฐ์—์š”.

์šฐ๋ฆฌ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์ธ ๋Œ€ํ”ผ๋กœ ๋Š”,

์ž‘๋…„ 2๊ฐœ์›” ๊ฐ„ ๋ฐ๋ชจ๋ฐ์ด ์ถœ์‹œ๋ผ๋Š” ๋‹จ๊ฑฐ๋ฆฌ ๊ฑฐ๋ฆฌ ์งˆ์ฃผ๋Š” ํ•ด๋ƒˆ์ง€๋งŒ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์ด ์ „ํ˜€ ๊ตฌ์ถ•๋˜์–ด์žˆ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—

๋” ๋น ๋ฅด๊ฒŒ ์˜ค๋ž˜ ๋‹ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ์—ฌ๋ ฅ์ด ๋‚จ์•„์žˆ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ง€๊ธˆ์€ ์ž ๊น ๋ฉˆ์ถ”๊ณ , ๋” ๋ฉ€๋ฆฌ ๊ฐ€๊ธฐ ์œ„ํ•œ ์ •๋น„๋ฅผ ํ•ด์•ผํ•  ๋•Œ๋ผ๊ณ  ๊ฒฐ์ •ํ–ˆ์ฃ .


์„œ๋ก ์ด ๊ธธ์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋„ค์š”.

์š”์•ฝํ•˜์ž๋ฉด, ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ๋Œ€ํ”ผ๋กœ ์˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„ ์ค‘ ํ•˜๋‚˜์ธ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์„ ์ง€๊ธˆ ํ•ด๊ฒฐํ•ด์•ผ๋งŒ ํ•œ๋‹ค.

์ด๊ฑด๋ฐ์š”..!

๋Œ€ํ”ผ๋กœ ๋ฐฑ์—”๋“œ์—์„œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ–ˆ๋˜ ํ—ˆ๋“ค์€ ๋ฌด์—‡์ด ์žˆ์—ˆ์„๊นŒ์š”?

image

๋Œ€ํ”ผ๋กœ์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ํ˜„์žฌ ์œ„์น˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ€๊นŒ์šด ๋Œ€ํ”ผ์†Œ๋“ค์„ ์กฐํšŒํ•˜๋Š” API ์ž…๋‹ˆ๋‹ค.

๊ฐ๊ฐ์˜ ์œ„์น˜๋Š” ์œ„๊ฒฝ๋„ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์กฐํšŒํ•˜๋ฉฐ,

๋Œ€ํ”ผ์†Œ์™€ ์‚ฌ์šฉ์ž ๊ฐ„ ๊ฑฐ๋ฆฌ ๋˜ํ•œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์‚ฐ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด ๋Œ€ํ”ผ๋กœ๋Š” ๊ณต๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.

๊ณต๊ฐ„ ๋ฐ์ดํ„ฐ ๊ด€๋ จ ํ’๋ถ€ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋Š” mysql ์„ ๋ฉ”์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—”์ง„์œผ๋กœ ์„ ํƒํ•œ ์ด์œ ๋„ ์ด๋•Œ๋ฌธ์ด์—ˆ๋Š”๋ฐ์š”..!

( ์ถ”ํ›„์— ๊ณต๊ฐ„ ์ธ๋ฑ์Šค ๊ด€๋ จํ•œ ์ฟผ๋ฆฌ ํŠœ๋‹ ํฌ์ŠคํŒ…์ด ์˜ˆ์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค ~ )

๋ณดํ†ต ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ •๋ง ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋Š” h2 DB ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜์ฃ .

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

H2 ๊ธฐ๋ฐ˜์˜ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์œผ๋กœ๋Š” ์ œ์•ฝ์ด ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์›ํ™œํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด,

ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์—์„œ mysql ์— ์˜์กด์ ์ธ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์—ˆ๋Š”๋ฐ์š”

๊ทธ๋Ÿฌ๊ธฐ์—๋Š” mysql ์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ์œ„์น˜ ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ์ด ๊ฐ•๋ ฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

์•„๋‹ˆ๋ฉด ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋‘ mocking ํ•ด๋„ ๋  ํ…๋ฐ์š”,

์™ธ๋ถ€ ์‹œ์Šคํ…œ์ด ์•„๋‹Œ ๋Œ€ํ”ผ๋กœ ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ธ๋ฐ ์ด๋ฅผ mocking ํ•˜๋Š” ๊ฒŒ ๊ณผ์—ฐ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์ผ๊นŒ์š”?

๋‹น์—ฐํžˆ.. ์•„๋‹ˆ๊ฒ ์ฃ .

๊ทธ๋Ÿฌ๋ฉด ๋‚จ์€ ๋ฐฉ๋ฒ•์€ ๋‹จ ํ•˜๋‚˜.

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ h2 ๊ฐ€ ์•„๋‹Œ mysql ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š”๊ฑฐ์ฃ ! (๋‹น์—ฐ)

์ž ๊ทธ๋Ÿผ ๋ฌธ์ œ ์ •์˜๊ฐ€ ์กฐ๊ธˆ ๋ฐ”๋€Œ์—ˆ๋„ค์š”.

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ mysql ์„ ์–ด๋–ป๊ฒŒ ๊ตฌ์ถ•ํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

๊ฐ์ž ๋กœ์ปฌ์— mysql ์„ demon ์œผ๋กœ ์‹คํ–‰์‹œํ‚จ ๋’ค, ํ”„๋กœ๋•์…˜ db ๋ฅผ dump ํ•ด์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋ฉด ์–ด๋–จ๊นŒ์š”?

ํ โ€ฆ

๋„ˆ๋ฌด ๋กœ์ปฌ ํ™˜๊ฒฝ์— ์˜์กด์ ์ด์ฃ .

โ€œ์Ÿค ์ปด์—์„  ํ…Œ์ŠคํŠธ๊ฐ€ ๋‹ค ์„ฑ๊ณตํ•˜๋Š”๋ฐ ์™œ ์ „ ์•ˆ๋˜๋Š”๊ฑฐ์ฃ ?โ€ 

ํ”„๋กœ๋•์…˜ db ์™€ ์Šคํ‚ค๋งˆ๋ฅผ ํ•ญ์ƒ ๋™๊ธฐํ™”ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ด€๋ฆฌํฌ์ธํŠธ๋„ ํ•˜๋‚˜ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค..

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด ๋งค์šฐ ๊ฐ„ํŽธํ•˜๊ณ  ํŽธ๋ฆฌํ•ด์•ผ,

๋” ๋งŽ์€ ์‹œ๊ฐ„์„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ํ• ์• ํ•˜๋„๋ก ์œ ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์—

์ด ๋ฐฉ๋ฒ•์€ ๊ธฐ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์Œ..

๋‹ค๋ฅธ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ์š”?

๋กœ์ปฌ ํ™˜๊ฒฝ์—.. ์ข…์†์ ์ด์ง€ ์•Š์€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์ด๋ผ..

์•„ ์ด๊ฑฐ ์™„์ „ ์ปจํ…Œ์ด๋„ˆ ๊ฐœ๋… ์•„๋‹Œ๊ฐ€์š”? (๋‘๋‘ฅ)

ํ…Œ์ŠคํŠธ๋ฅผ run ํ•˜๋ฉด

ํ…Œ์ŠคํŠธ ์šฉ๋„์˜ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋„์›Œ์ง€๊ณ 

ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‚ด๋ ค์ฃผ๋ฉด ๋˜๊ฒ ๋„ค์š”!

๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ  ๋ฐ”๋กœ ๊ตฌ๊ธ€๋งํ•ด๋ณด์•˜๋Š”๋ฐ,

์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฐœ๋…์„ ๊ตฌํ˜„ํ•ด๋†“์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋”๋ผ๊ตฌ์š”.. ( ์—†๋Š”๊ฒŒ์—†๋„ค์š”.. )


ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ(TestContainers)

image

ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ด์šฉํ•˜๋ฉด, ์ž๋ฐ” ์ฝ”๋“œ๋งŒ์œผ๋กœ ํ…Œ์ŠคํŠธ ์šฉ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ์•„์ฃผ ์•„์ฃผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ ๋‹ค๋ฃจ๋Š” mysql ๋ฟ๋งŒ์ด ์•„๋‹ˆ๋ผ, mongoDB, Kafka ์™€ ๊ฐ™์€ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ๋“ค๋„ ๋ชจ๋‘ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์‹ค์ œ ์„œ๋น„์Šค ํ™˜๊ฒฝ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•˜์ง€๋งŒ ๊ฒฉ๋ฆฌ๋œ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์ฃ .

๋‹จ์ ์€ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ๋„์ปค๊ฐ€ ๋™์ž‘ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ๊ณผ,

ํ…Œ์ŠคํŠธ ์‹œ์ž‘ํ•  ๋•Œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋„์šฐ๋Š” ์ •๋„์˜ ์†Œ์š” ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚œ๋‹ค๋Š” ์ ์ด ์žˆ์–ด์š”.

ํ•˜์ง€๋งŒ,

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

๋™์ž‘ํ•˜๋Š” ๋ชจ์Šต์„ ๋จผ์ € ๋ณผ๊นŒ์š”?

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด,

image

๋ฏธ๋ฆฌ ์„ค์ •ํ•ด๋‘” ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

image

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์ฃ .

image

mysql ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋„์› ๊ธฐ ๋•Œ๋ฌธ์—, mysql ์—์„œ ์ œ๊ณตํ•˜๋Š” ์œ„์น˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ธ Point ์™€ ์œ„์น˜ ํ•จ์ˆ˜์ธ ST_DISTANCE_SPHERE ์„ ์‚ฌ์šฉํ•œ ์„œ๋น„์Šค ๋กœ์ง์„ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋„ค์š”!

( ST_DISTANCE_SPHERE ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ๊ณต๊ฐ„ ์ธ๋ฑ์Šค R-tree ๋ฅผ ํƒ€์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ด์Šˆ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Š” ์ถ”ํ›„ ํฌ์ŠคํŒ…์—์„œ ๊ณต๊ฐ„ ์ฟผ๋ฆฌ ๊ฐœ์„  ์ฃผ์ œ๋กœ ๋‹ค์‹œ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด์—์š”!! ์‚ฌ์‹ค ์ „๋ฐ˜์ ์œผ๋กœ ๋น„ํšจ์œจ์ ์ธ ์ฟผ๋ฆฌ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด ๊ธฐ ๋•Œ๋ฌธ์—, ์ „์ฒด์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๊ฐˆ์•„์—Ž์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.. ๋Œ€๋žต 4~5์›” ์•ˆ์— ๋ชจ๋‘ ๊ต์ฒดํ•ด๋ฒ„๋ฆฌ๊ณ  ์‹ถ๋„ค์š”..ใ…Žใ…Ž)

ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์˜ ์˜์กด์„ฑ์„ ๋ฐ›์•„์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
testImplementation 'org.testcontainers:mysql'

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์ฃผ๋ฉด

@Container  
MySQLContainer mySQLContainer = new MySQLContainer("mysql:8");

mysql:8 ์ด๋ฏธ์ง€๋กœ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋„์›Œ์ฃผ๊ณ , ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ์ข…๋ฃŒ์‹œ์ผœ์ค๋‹ˆ๋‹ค.

๋‹ค๋งŒ, ๋งค๋ฒˆ ๋ชจ๋“  ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•ด์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋„์›Œ์ฃผ๊ณ  ๋‚ด๋ ค์ฃผ๋Š” ์ž‘์—…์„ ๋ฐ˜๋ณตํ•˜๋‹ค ๋ณด๋‹ˆ ์ „์ฒด์ ์ธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰์‹œ๊ฐ„์ด ๋งŽ์ด ์ง€์ฒด๋˜๊ฒ ์ฃ .

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ static ํ•„๋“œ๋กœ ์„ ์–ธํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค.

๊ฐ ์ปจํ…Œ์ด๋„ˆ๋งˆ๋‹ค ํ•„์š”ํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” @DynamicPropertySource ์„ ํ†ตํ•ด ๋™์ ์œผ๋กœ ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ์š”,

๋Œ€ํ”ผ๋กœ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”!

@Slf4j
@SpringBootTest(classes = ServiceIntegrationTestConfiguration.class)
@Import({CoreTestConfiguration.class})
public abstract class ServiceIntegrationTestBase {

    private static final String REDIS_DOCKER_IMAGE = "redis:5.0.3-alpine";
    private static final String MYSQL_DOCKER_IMAGE = "mysql:8.0";
    private static final String MYSQL_ROOT = "root";
    private static final String MYSQL_PASSWORD = "1234";

    private static final int REDIS_PORT = 6379;

    @Container
    protected static MySQLContainer mySQLContainer;

    @Container
    protected static GenericContainer redisContainer;

    @DynamicPropertySource
    static void configureProperties(final DynamicPropertyRegistry registry) {
        // mysql env init
        log.info("""
                    \n
                    โœ… mysql property ๋ฅผ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.
                """);
        registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl);
        registry.add("spring.datasource.username", () -> MYSQL_ROOT);
        registry.add("spring.datasource.password", () -> MYSQL_PASSWORD);

        // redis env init
        log.info("""
                    \n
                    โœ… redis property ๋ฅผ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.
                """);
        registry.add("spring.redis.host", redisContainer::getHost);
        registry.add("spring.redis.port", () -> "" + redisContainer.getMappedPort(REDIS_PORT));
    }

    static {
        // mysql test container init
        mySQLContainer = (MySQLContainer) new MySQLContainer(MYSQL_DOCKER_IMAGE)
                .withUsername(MYSQL_ROOT)
                .withPassword(MYSQL_PASSWORD)
                .withDatabaseName("test")
                .withEnv("MYSQL_ROOT_PASSWORD", MYSQL_PASSWORD);

        log.info("""
                \n
                ๐Ÿณ MYSQL ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
                - base image: {}
                """, MYSQL_DOCKER_IMAGE);
        mySQLContainer.start();

        // redis test container init
        redisContainer = new GenericContainer<>(REDIS_DOCKER_IMAGE)
                .withExposedPorts(REDIS_PORT)
                .withReuse(true);
        log.info("""
                \n
                ๐Ÿณ Redis ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
                - base image: {}
                """, REDIS_DOCKER_IMAGE);
        redisContainer.start();
    }

}

์„œ๋น„์Šค ๋ ˆ์ด์–ด ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ๋ชจ๋‘ ์œ„ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋„๋ก ํ•ด์„œ,

Spring Context ๋ฅผ ์ค‘๋ณต์œผ๋กœ ๋„์›Œ์„œ ๋ฐœ์ƒํ•˜๋Š” ์‹œ๊ฐ„ ์ง€์—ฐ์„ ์ตœ์†Œํ™”ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ๋กœ ์ž‘์„ฑํ•ด๋‘” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์•„์š”!

public class ShelterServiceTest extends ServiceIntegrationTestBase {

    @Autowired
    private ShelterService shelterService;

    @Autowired
    private ShelterRepository shelterRepository;

    @DisplayName("1000 m ์ด๋‚ด์— ์œ„์น˜ํ•œ ์ฃผ๋ณ€ ๋Œ€ํ”ผ์†Œ ๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
    @Test
    void getNearestShelterTest() {
        // given
        shelterRepository.save(Shelter.of(ShelterType.CIVIL_DEFENCE, "1000m ์ด๋‚ด์˜ ๋Œ€ํ”ผ์†Œ ๋ฏผ๋ฐฉ์œ„๋Œ€ํ”ผ์†Œ", 35.504, 35.5));
        shelterRepository.save(Shelter.of(ShelterType.CIVIL_DEFENCE, "1000m ๋ณด๋‹ค ๋จผ ๋Œ€ํ”ผ์†Œ", 36.504, 36.5));

        // when
        NearbyShelterListResponse response = shelterService.getNearbyShelterList(NearbyShelterRequest.of(35.5, 35.5, "๋ฏผ๋ฐฉ์œ„"));

        // then
        Assertions.assertThat(response.getCount()).isEqualTo(1);
    }

}

์‚ฌ์‹ค ์œ„์— ์ž‘์„ฑ๋œ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋งŒ ๋ณด๊ณ ๋„, ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์—์„œ ๊ฐœ์„ ํ•ด์•ผ ํ•  ์ ์„ ํ•˜๋‚˜ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์–ด์š”.

๋ฌด์—‡์ผ๊นŒ์š”?

๋ฐ”๋กœ, 1000m ์ด๋‚ด๋ผ๋Š” ์กฐ๊ฑด์ธ๋ฐ์š”..!!

์œ„ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ง์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ Query ๋ ˆ๋ฒจ์—์„œ where ์ ˆ์— 1000 m ์ดํ•˜์ธ ๋ ˆ์ฝ”๋“œ๋งŒ ์กฐํšŒํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ์–ด์š”.

์ด๋ณด๋‹ค๋Š”, 1000 ์ด๋ผ๋Š” ๊ฐ’์„ ์™ธ๋ถ€์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋˜๋„๋ก ํ•˜๋ฉด

์กฐ๊ธˆ ๋” ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์›Œ์ง€๋Š” ์ฝ”๋“œ๊ฐ€ ๋จ๊ณผ ๋™์‹œ์— ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ๋„ ๋” ์œ ์—ฐํ•ด์ง€์ง€ ์•Š์„๊นŒ์š”?

์ด์™€ ๊ฐ™์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋ฉด,

๋ฏธ์ฒ˜ ๋ฐœ๊ฒฌํ•˜์ง€ ๋ชปํ–ˆ๋˜ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ์ƒ์˜ ๋ฌธ์ œ์ ์ด ๋ˆˆ์— ๋ณด์ด๊ฒŒ ๋œ๋‹ค๋Š” ์•„์ฃผ ์ข‹์€ ์žฅ์ ์ด ์žˆ์–ด์š”..!

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ–ˆ์œผ๋‹ˆ,

๋‹น๋ถ„๊ฐ„์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉฐ

ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์„ ์ œ๋Œ€๋กœ ์ง„๋‹จํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Leave a comment