๐Ÿ’พ Backend ๊ธฐ์ˆ  ๋ฌธ์„œ


์šด์˜์ฒด์ œ ์บก์Šคํ†ค ์ธํ„ฐ๋ทฐ ๊ณผ์ œ ๊ด€๋ จ ์ •๋ฆฌ ๋ฌธ์„œ

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

์šด์˜์ฒด์ œ ๋ฌธ์„œ


๊ธฐ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ


์ฑ„ํŒ… ๊ตฌํ˜„

์ฑ„ํŒ… ์„œ๋ฒ„๋Š” ํฌ๊ฒŒ Polling, Long Polling, Steaming, Websocket ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ž์„  3๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ์ผ๋ฐ˜์ ์ธ RESTFUL API๋ฅผ ์ด์šฉํ•œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋น„๊ต์  ๊ตฌํ˜„์ด ์‰ฝ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋“ค์˜ ํŠน์„ฑ์ƒ ํด๋ผ์ด์–ธํŠธ โ†’ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์„œ๋ฒ„ โ†’ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋˜ํ•œ, 100% ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋‚˜์˜จ ๋ฐฉ๋ฒ•์ด ๋ฐ”๋กœ 4๋ฒˆ WebSocket ๋ฐฉ์‹์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ฑ„ํŒ…์„ websocket์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด, ๊ต‰์žฅํžˆ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์žก์•„๋จน๊ณ  ๊ตฌํ˜„์ด ๊นŒ๋‹ค๋กญ๋‹ค.

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Long Polling์„ ์ด์šฉํ•˜์—ฌ ์ฑ„ํŒ…์„ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ฑ„ํŒ…์€ 100% ์‹ค์‹œ๊ฐ„์„ฑ์„ ๋ณด์žฅํ•  ํ•„์š” ์—†์„ ๋ฟ๋”๋Ÿฌ, ์ฑ„ํŒ…์ด ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๊ณ , ํ”„๋กœ์ ํŠธ ๋งˆ๊ฐ๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ•ด์•ผ๋˜๊ธฐ ๋•Œ๋ฌธ์— Long Polling์„ ์ด์šฉํ•˜์—ฌ ์ฑ„ํŒ…์„ ๊ฐœ๋ฐœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

์ฑ„ํŒ… ์‹œ์Šคํ…œ ์„ค๊ณ„ ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๊ด€ํ•œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฑ„ํŒ… ๊ตฌํ˜„์— ๋Œ€ํ•œ ๊ณ ์ฐฐ ํ•„์ž์˜ ๋ธ”๋กœ๊ทธ์— ์„œ์ˆ ํ•ด๋†“์•˜๋‹ค.


ํฌ๋กค๋ง

ํ˜„์žฌ ํฌ๋กค๋ง์€ ํ•™์‹๊ณผ ๊ณต์ง€์‚ฌํ•ญ ๋ถ„์•ผ์—์„œ ์‹ค์‹œ๋˜๊ณ  ์žˆ๋‹ค. ํฌ๋กค๋ง ํ•  ๋•Œ๋„ ๋ช‡๊ฐ€์ง€ ๊ณ ๋ คํ•  ์‚ฌํ•ญ์ด ์กด์žฌํ•œ๋‹ค.

์ฒซ๋ฒˆ์งธ, ์ •์  ํฌ๋กค๋ง๊ณผ ๋™์  ํฌ๋กค๋ง ์–ด๋–ค ๊ฒƒ์ด ํ•„์š”ํ•œ๊ฐ€ ๊ณ ๋ คํ•ด๋ด์•ผ ํ•œ๋‹ค. ๊ณต์ง€์‚ฌํ•ญ๊ณผ ํ•™์‹์€ ๊ตณ์ด ๋กœ๊ทธ์ธ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตณ์ด ๋™์  ํฌ๋กค๋ง์ด ํ•„์š”ํ•˜์ง€ ์•Š์•˜๋‹ค. ๋˜ํ•œ, ๋™์  ํฌ๋กค๋ง์€ Selenium ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๊ณ , Chrome Driver๋ฅผ ์„ค์น˜ํ•˜๋Š” ๋“ฑ ์ถ”๊ฐ€์ ์ธ ์„ค์ •์ด ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค. ๋”ฐ๋ผ์„œ, ์‚ฌ์šฉ์ด ๊ฐ„๋‹จํ•œ Jsoup์„ ์ด์šฉํ•ด์„œ ์ •์  ํฌ๋กค๋ง์„ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

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

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

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


๋ฐœ์Œ ์—ฐ์Šต

๋ฐœ์Œ ์—ฐ์Šต ๊ตฌํ˜„์„ ์œ„ํ•ด์„œ Azure์—์„œ ์ œ๊ณตํ•˜๋Š” Speech Service๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

๊ทธ๋Ÿฐ๋ฐ, ๋ฐœ์Œ ์—ฐ์Šต์ด ๋ถ„๋ช…ํžˆ Local์—์„œ๋Š” ์ž˜๋์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , Docker๋กœ Containerํ•œ ํ›„, ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„์— ์˜ฌ๋ฆฌ๋‹ˆ

Could not initialize class com.microsoft.cognitiveservices.speech.SpeechConfig

์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค.

์ด์œ ๋Š” Spring Docker๋ฅผ ์œ„ํ•ด, JDK ๊ฒฝ๋Ÿ‰ํ™” ์ด๋ฏธ์ง€์ธ alpine์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, SDK ์„ค์น˜ ์„ค๋ช…์„œ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด Debian ๋˜๋Š” Ubuntu ํ™˜๊ฒฝ์—์„œ๋งŒ ์ž‘๋™ํ•œ๋‹ค๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ alpine์ด ์•„๋‹Œ ubuntu ๊ธฐ๋ฐ˜์˜ JDK Image๋ฅผ ์‚ฌ์šฉํ•ด์„œ Build๋ฅผ ํ•˜์˜€๋‹ค.

ํ•˜์ง€๋งŒ, ๋˜๋‹ค๋ฅธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ๋ชจ๋“  ์ ์ˆ˜๊ฐ€ 0์ ์ด ๋‚˜์˜ค๋Š” ์˜ค๋ฅ˜์˜€๋‹ค. ์ด์œ ๋Š” ์—ญ์‹œ ์„ค๋ช…์„œ์— ๋ช…์‹œ๋˜์–ด ์žˆ์—ˆ๋‹ค.

์„ค๋ช…์„œ๋ฅผ ์ž˜ ๋ณด๋ฉด, Ubuntu 22.04๋Š” ๊ธฐ๋ณธ๊ฐ’์ด OpenSSL 3.0์ธ๋ฐ, ์ด๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , Docker Build๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•œ eclipse-temurin:17-jdk๋Š” Ubuntu 22.04์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ธฐ์กด 3.0๋ฒ„์ „์˜ Open SSL์„ ์ง€์šฐ๊ณ , 1.1 ๋ฒ„์ „์„ ์„ค์น˜ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ Speech SDK Github Issue์— ์˜ฌ๋ผ์™€์žˆ๋Š” OpenSSL ๋‹ค์šด๊ทธ๋ ˆ์ด๋“œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ƒˆ๋กœ Dockerfile์„ ์ž‘์„ฑํ•˜์—ฌ ๊ณ ์ณค๋‹ค.

์ž์„ธํ•œ Dockerfile์€ ํ•„์ž ๋ธ”๋กœ๊ทธ์˜ Azure ๋ฐœ์Œํ‰๊ฐ€ ํ•ด๊ฒฐ๊ณผ์ • ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ๋‹ค.


API Gateway

JWT ํ™•์ธ ๋กœ์ง๊ณผ API Rate Limiter๋Š” ์ฑ„ํŒ…์„œ๋ฒ„, ์ฑ—๋ด‡์„œ๋ฒ„, ๋ฉ”์ธ ์„œ๋ฒ„ ๋ชจ๋‘ ํ•„์š”ํ•œ ๋กœ์ง์ด๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ฅผ ๊ฐ๊ฐ ์„œ๋ฒ„๋งˆ๋‹ค ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๊ณ  ์ฝ”๋“œ ์ค‘๋ณต์ด ๋งŽ์•„์ง„๋‹ค. ๋”ฐ๋ผ์„œ, API Gateway๋ฅผ ๊ตฌ์ถ•ํ•˜์—ฌ ๋ชจ๋“  ์š”์ฒญ์ด ์ด๋ฅผ ํ†ต๊ณผํ•˜๋„๋ก ๊ตฌ์„ฑํ•˜๋ฉด ๋งค์šฐ ํšจ์œจ์ ์ด๋‹ค.

ํ•˜์ง€๋งŒ, API Gateway๋ฅผ SpringMVC๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๋‹ค. SpringMVC์˜ WAS๋Š” Tomcat์ธ๋ฐ, Tomcat์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ThreadPool์—์„œ Thread๋ฅผ ํ•˜๋‚˜ ๋ฐฐ์ •ํ•ด์ค€๋‹ค. ๋˜ํ•œ, ๊ฐ ์š”์ฒญ์„ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋Š” ๋ธ”๋กœํ‚น ๋œ๋‹ค. (๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ์•„๋‹˜. ํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋”๋ผ๋„ ์–ด์ฐŒ๋๋˜ ๊ฐ„์— ThreadPool์„ ์†Œ๋ชจํ•˜๋‹ˆ๊น, ThreadPool ๊ณ ๊ฐˆ ๋ฌธ์ œ๋Š” ์—ฌ์ „) ๊ทธ๋ž˜์„œ ๋™์‹œ์— ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œํ’€์ด ๊ณ ๊ฐˆ๋˜์–ด, ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, API Gateway๋กœ ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค. ๊ทธ๋ž˜์„œ ํƒํ•œ ๊ฒƒ์ด Spring Cloud Gateway์ด๋‹ค.

Spring Cloud Gateway๋Š” Netty ๊ธฐ๋ฐ˜์ด๋‹ค. Netty๋Š” ๋…ผ๋ธ”๋กœํ‚น ๋น„๋™๊ธฐ Event-driven ๊ตฌ์กฐ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋น ๋ฅด๊ฒŒ ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•ด์ค˜์•ผํ•˜๋Š” API Gateway ๊ตฌ์กฐ์—์„œ Netty๊ฐ€ ์ ํ•ฉํ•˜๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ ์€ ์–‘์˜ ์Šค๋ ˆ๋“œ์™€ ์ตœ์†Œํ•œ์˜ ์ž์›์œผ๋กœ ํšจ์œจ์ ์œผ๋กœ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋‹ค๋งŒ, Spring Cloud Gateway๋ง๊ณ ๋„, AWS์—์„œ ์ œ๊ณตํ•˜๋Š” API Gateway๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์„ ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. (ํ•˜์ง€๋งŒ, ์ด๋Š” ์ฝ”๋“œ ๋ ˆ๋ฒจ ๊ด€๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ์ธํ”„๋ผ ๋ ˆ๋ฒจ์—์„œ์˜ ๊ด€๋ฆฌ๋ผ์„œ ์‚ด์ง ์ฐจ์ด๊ฐ€ ์žˆ์Œ)

๋กœ๊น…

๋กœ๊น…์€ ์šฐ์„  ํ•  ์ˆ˜ ์žˆ๋Š” ๋งŒํผ ์ตœ๋Œ€ํ•œ ๋งŽ์ด, ๊ทธ ๋‹ค์Œ์— ํ•„์š” ์—†๋Š” ๋กœ๊ทธ๋Š” ์ง€์šฐ๊ฑฐ๋‚˜ ๋ ˆ๋ฒจ์„ ๋‚ฎ์ถ”๋Š” ์‹์œผ๋กœ ์šด์˜ํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค. ๊ทธ๋ž˜์„œ, ๋กœ๊น…์€ ๋‘๊ฐ€์ง€ ๋ถ€๋ถ„์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์˜€๋‹ค.

์ฒซ๋ฒˆ์งธ, API Gateway๋‹จ์—์„œ ๋ชจ๋“  Request๋ฅผ Loggingํ•œ๋‹ค. ์ด๊ณณ์—์„œ ์š”์ฒญํ•œ url, method, request body, Authorization Header๋ฅผ Logging ํ•˜์˜€๋‹ค. ์ด๋กœ์จ, ํ•œ ๊ณณ์—์„œ ๊ณตํ†ต์ ์œผ๋กœ Logging์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

๋‘๋ฒˆ์งธ, Spring์—์„œ AOP๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ณ„ ์‹คํ–‰ ์‹œ๊ฐ„ ๊ธฐ๋ก์„ ์ง„ํ–‰ํ•˜์˜€๋‹ค.

์œ„์™€ ๊ฐ™์ด Spring ์„œ๋ฒ„์—์„œ ๊ตฌ์„ฑ์„ ํ•˜์—ฌ, ๋ฉ”์„œ๋“œ๋ณ„ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์‰ฝ๊ฒŒ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.


API ์‘๋‹ต ๋ฐ ๋ฌธ์„œ

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

image

์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋Š” ์œ„์™€ ๊ฐ™์ด ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์ฃผ๋Š” success, ๊ทธ์— ๋Œ€ํ•œ message, ๊ทธ๋ฆฌ๊ณ  ์‘๋‹ต์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ’์ธ response๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. response์•ˆ์— ์ด์ œ ํ”„๋ก ํŠธ ์ธก์—์„œ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‹ด๊ฒจ์žˆ๋‹ค.

image

์‹คํŒจํ–ˆ์„ ๋•Œ๋Š” success๊ฐ€ false, ์‹คํŒจ์— ๋Œ€ํ•œ message, ๊ทธ๋ฆฌ๊ณ  ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ์•Œ๋ ค์ฃผ๋Š” code๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. ํ•ด๋‹น ์˜ค๋ฅ˜ ์ฝ”๋“œ๋Š” ์„œ๋ฒ„ ์ธก์—์„œ Enum์œผ๋กœ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ…Œ์ŠคํŠธ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ์œ„ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋‹ค.

image

API ๋ฌธ์„œ๋Š” Swagger๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์†Œํ”„ํŠธ์›จ์–ด๊ณตํ•™์—์„œ Agile Process์— ๋Œ€ํ•ด์„œ ๊ณผ๋„ํ•œ ๋ฌธ์„œํ™”๋ฅผ ํ•˜์ง€ ๋ง๋ผ๊ณ  ์–ธ๊ธ‰ํ•˜์˜€๋‹ค. ํŠนํžˆ, API ๋ฌธ์„œํ™” ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๊ณ„์†ํ•ด์„œ ์‘๋‹ต ํ˜•์‹์ด๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ์ด ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋งค๋ฒˆ API ๋ฌธ์„œ๋ฅผ ์ตœ์‹ ํ™” ํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋ ต๊ณ , ์„œ๋ฒ„์™€ ๋ฌธ์„œ๊ฐ€ ์„œ๋กœ ์•ˆ๋งž๋Š” ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์˜คํžˆ๋ ค ๋ฌธ์„œ๋ฅผ ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ Cost์™€ Time์„ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

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


๋ฐฐํฌ

AWS EC2 ์ธ์Šคํ„ด์Šค์— ๋ฐฐํฌํ•œ๋‹ค๋˜์ง€, ์ปดํ“จํ„ฐ์™€ ๋…ธํŠธ๋ถ์„ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ๊ฐœ๋ฐœํ•˜๋‹ค๋˜์ง€, ์ด๋Ÿฌ๋ฉด ๊ฐ๊ฐ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ด ํ‹€๋ ค ๋งค๋ฒˆ ์„ค์ •์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ Docker๊ฐ€ ์žˆ๋‹ค๋ฉด? ๊ท€์ฐฎ๊ฒŒ ๊ทธ๋Ÿด ํ•„์š” ์—†์ด ์‰ฝ๊ฒŒ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์‹คํ–‰ํ•˜๋Š” OS ํ™˜๊ฒฝ์— ์ƒ๊ด€์—†์ด, ์–ธ์ œ๋‚˜ ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋„๋ก Docker Containerํ™”๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค. Ruby On Rails๋‚˜ AI์„œ๋ฒ„์˜ Docker Containerํ™”๋Š” ๋‹ค๋ฅธ ๊ฒƒ๊ณผ ๋ณ„๋ฐ˜ ๋‹ค๋ฅผ๊ฒŒ ์—†๋Š”๋ฐ, Spring Containerํ™”๋ฅผ ์ข€ ํŠน์ดํ•˜๊ฒŒ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์ผ๋ฐ˜์ ์ธ Docker ๋ฐฐํฌ ๋ฐฉ์‹์œผ๋กœ Spring ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•œ๋‹ค๋ฉด, jar ํŒŒ์ผ์ด ๋ฌด๊ฑฐ์›Œ์งˆ์ˆ˜๋ก docker image๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์ด ๋น„ํšจ์œจ์ ์œผ๋กœ ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, Docker์˜ ์žฅ์ ์ด ๋ ˆ์ด์–ด๋งˆ๋‹ค ์บ์‰ฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๋น ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ํ•ด๋‹น ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ํ•œ์ค„๋งŒ ๋ฐ”๊ฟ”๋„ ์บ์‰ฌ๊ฐ€ ๊นจ์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ ์—ฐ์‚ฐ์„ ํ•ด์•ผ๋˜์„œ, Docker๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์ ์ด ์—†์–ด์ง„๋‹ค. ์™œ๋ƒํ•˜๋ฉด, Spring์˜ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Single layer์— ๋ฐฐ์น˜๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ฒฐ๊ตญ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ์˜ ์‹œ์ž‘ ์‹œ๊ฐ„์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค.

๊ทธ๋ž˜์„œ Multistage Build์™€ Jar ํŒŒ์ผ์˜ ํŠน์„ฑ ๋‘๊ฐ€์ง€๋ฅผ ๊ณ ๋ คํ•ด์„œ Dockerfile์„ ์ž‘์„ฑํ–ˆ๋‹ค. ์šฐ์„ , ์ผ๋ฐ˜์ ์ธ ๋ฐฉ์‹์œผ๋กœ Docker Image๋ฅผ ์ƒ์„ฑ์‹œ, ์šฐ๋ฆฌ๋Š” jar ํŒŒ์ผ๋งŒ ํ•„์š”ํ•œ๋ฐ ์“ธ๋ฐ์—†๋Š” ์†Œ์Šค ์ฝ”๋“œ๊นŒ์ง€ ๋‹ค ํฌํ•จํ•˜๊ฒŒ ๋˜์–ด์„œ ์šฉ๋Ÿ‰์ด ์ง€๋‚˜์น˜๊ฒŒ ์ปค์ง€๊ณ , ์—…๋กœ๋“œํ•˜๋Š”๋ฐ๋„ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฒŒ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, MultiStage Build๋ฅผ ์ด์šฉํ•˜์—ฌ Buildํ•  ๋•Œ๋งŒ ํ•„์ˆ˜์ ์ธ ์ฝ”๋“œ๋กœ jar ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ์ตœ์ข… Docker image์—๋Š” ์ด๊ฒƒ๋“ค์ด ํฌํ•จ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

๋˜ํ•œ, Spring ๊ณต์‹ Docs๋ฅผ ๋“ค์–ด๊ฐ€๋ณด๋ฉด Spring์˜ Jar ํŒŒ์ผ์€ ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด 4๊ฐ€์ง€ Layer๋กœ ๋‚˜๋‰œ๋‹ค๊ณ  ํ•œ๋‹ค. ํ•ด๋‹น ๋ฌธ์„œ์—์„œ, application layer๊ฐ€ ๊ฐ€์žฅ ์ž์ฃผ ๋ฐ”๋€Œ๊ณ , ๊ทธ๋‹ค์Œ์€ snatphot, ๊ทธ๋‹ค์Œ์€ spring-boot-loader, dependencies ์ˆœ์œผ๋กœ ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. ์ฆ‰, Dependencies๊ฐ€ ๊ฐ€์žฅ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด Dockerfile์„ ์ž‘์„ฑํ•  ๋•Œ, Docker File์€ ์ œ์ผ ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์ฐจ๋ก€๋กœ ์ž‘์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์˜ ์—ญ์ˆœ์œผ๋กœ Docker File์„ ์ž‘์„ฑํ•˜์˜€๋‹ค.

CI/CD๋กœ ๋Œ€ํ‘œ์ ์ธ ๊ฒƒ์€ Jenkins์™€ Git Actions๊ฐ€ ์žˆ๋Š”๋ฐ, ์šฐ๋ฆฌ๋Š” Git Actions์„ ํƒํ–ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” Github์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค ๋ณด๋‹ˆ๊น, ์ฝ”๋“œ ์ €์žฅ์†Œ๋ž‘ ๋ฐ”๋กœ๋ฐ”๋กœ ์—ฐ๊ฒฐ์ด ๋˜๊ณ , PR์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด GitHub Actions๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์ฝ”๋“œ ๋ณ€๊ฒฝ ๋ถ€๋ถ„์— ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€ ๊ฐ์ข… ๊ฒ€์‚ฌ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์œ„์™€ ๊ฐ™์€ ํ”Œ๋กœ์šฐ๋กœ Github์—์„œ ์ž๋™์œผ๋กœ ์„œ๋ฒ„๋“ค์ด ๋ฐฐํฌ๋  ์ˆ˜ ์žˆ๋„๋ก Git Actions ์„ค์ •์„ ํ•ด๋†จ๋‹ค.

setup-env์—์„œ git actions secret์œผ๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ .env๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฑธ ์šฐ๋ฆฌ ec2์— scp ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•ด์„œ ์ „์†กํ•œ๋‹ค. ๋˜ํ•œ, ์‹ค์งˆ์ ์ธ ์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ๋‹ด๋‹นํ•œ docker-compose.yml๋„ scp ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด์„œ ์ „์†กํ•œ๋‹ค.

๊ทธ ๋‹ค์Œ, ์šฐ๋ฆฌ์˜ spring, ruby, spring gateway, nginx ๋“ฑ์„ docker image๋กœ buildํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. Git Actions๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ์šด RunTime ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์—, docker์˜ ์ด์ ์ธ docker cache๋ฅผ ์ ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๊ทธ๋ž˜์„œ Build ํ•  ๋•Œ ๊ต‰์žฅํžˆ ์‹œ๊ฐ„์ด ์˜ค๋ž˜๊ฑธ๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ, cache๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ docker์—์„œ ๊ณต์‹์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” docker buildX๋ฅผ ์ด์šฉํ–ˆ๋‹ค. BuildX๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Git Actions ๋‚ด๋ถ€์ ์ธ Cache ์ €์žฅ์†Œ๋ฅผ ํ™œ์šฉํ•˜์—ฌ Docker Cache๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ, BuildX๋ฅผ ์ ์šฉํ•˜๊ธฐ ์ „์—๋Š” Spring Container ๋นŒ๋“œ ์‹œ๊ฐ„์ด 13 ~ 15๋ถ„ ์ •๋„ ๊ฑธ๋ ธ๋‹ค. ํ•˜์ง€๋งŒ, ์บ์‹œ๋ฅผ ์ ์šฉํ•œ ์ดํ›„๋กœ๋Š” ์œ„ ์‚ฌ์ง„์—์„œ ๋ณด์ด๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ 5๋ถ„์œผ๋กœ Build ์‹œ๊ฐ„์ด 10๋ถ„ ๊ฐ€๊นŒ์ด ์ค„์–ด๋“ค์—ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์šฐ๋ฆฌ ec2์— ssh๋กœ ์ ‘์†ํ•ด์„œ ์ด๋ฏธ์ง€๋ฅผ ๋‚ด๋ ค๋ฐ›๊ณ  docker-compose๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋ฐฐํฌ๋ฅผ ์™„๋ฃŒํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.


์„ฑ๋Šฅ์  ๊ณ ๋ ค ์‚ฌํ•ญ


์บ์‹ฑ

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

์บ์‹ฑ ์ „๋žต์€ Look Aside์™€ Write Around ์ „๋žต์˜ ์กฐํ•ฉ์„ ํƒํ•˜์˜€๋‹ค. Read ์ „๋žต์€ Look Aside ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ์šฐ์„ ์ ์œผ๋กœ Redis์—์„œ ์ฝ์–ด์˜ค๊ณ , cache miss๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ, RDS์—์„œ ์ฝ์–ด์˜ค๊ณ  ๋‹ค์‹œ redis์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. Write ์ „๋žต์€ Write Around ์ „๋žต์„ ํ™œ์šฉํ–ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ๋•Œ, Redis์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๋ฐ˜๋“œ์‹œ RDB์— ์ €์žฅํ•˜๊ณ  ๊ธฐ์กด ์บ์‹œ๋Š” ๋ฌดํšจํ™”ํ•˜๋Š” ์ „๋žต์ด๋‹ค.

์‹ค์ œ ์บ์‹ฑ์„ ์ ์šฉํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด๋ณด์•˜๋‹ค.

์บ์‹ฑ ์ „์—๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ 414ms๊ฐ€ ๊ฑธ๋ ธ๋‹ค.

ํ•˜์ง€๋งŒ ์บ์‹ฑ ํ›„์—๋Š” 12ms๋กœ ๋‹จ์ถ•๋˜์—ˆ๋‹ค. ์‹คํ–‰์‹œ๊ฐ„์ด 1/40 ๋‹จ์ถ•๋œ ๊ฒƒ์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉด, class ์ •๋ณด์— ๋Œ€ํ•œ field๊ฐ€ json์— ์ถ”๊ฐ€๋˜๋Š” ํ˜„์ƒ์ด ์žˆ์–ด์„œ ์ถ”๊ฐ€ ๋ณด๋ฅ˜๋ฅผ ํ•˜์˜€๋‹ค.

๋˜ํ•œ, ์ด๋ฏธ์ง€๋ฅผ ๋ฐ›์•„์˜ค๋Š” S3๋„ ์บ์‹ฑ์„ ๋ฌด์กฐ๊ฑด ํ•ด์•ผํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด S3 ๋น„์šฉ์ด ๊ต‰์žฅํžˆ ๋งŽ์•„์งˆ ๊ฒƒ์ด๊ณ  ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ AWS CloudFront๋ฅผ S3 ์•ž๋‹จ์— ๋‘์–ด ์บ์‹ฑ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.


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

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

์ด ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ Spring์—์„œ ThreadExecutor๋ฅผ ์ด์šฉํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ๊ธฐ๋ณธ Async Executor SimpleAsyncTaskExecutor์ธ๋ฐ, ์ด๋Š” ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„, ์„ฑ๋Šฅ ์ €ํ•˜, ์Šค์ผ€์ผ๋ง ๋ฌธ์ œ ๋“ฑ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด Thread Pool ๋ฐฉ์‹ Executor๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ์Šค๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ์„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํ–‰์‹œ๊ฐ„์ด ์งง์€ ๋งŽ์€ ์–‘์˜ Task๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ถˆ๋ฆฌํ•˜๋‹ค. ๋ฆฌ์†Œ์Šค ์ธก๋ฉด์—์„œ๋Š” ๊ฐ ๋น„๋™๊ธฐ ์ž‘์—…๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ๋™์‹œ์— ๋งŽ์€ ๋น„๋™๊ธฐ ์ž‘์—…์ด ์š”์ฒญ๋˜๋ฉด ๋งค๋ฒˆ ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ๋”ฐ๋ผ์„œ CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ๋ฆฌ์†Œ์Šค์˜ ์‚ฌ์šฉ๋Ÿ‰์ด ๊ณผ๋„ํ•˜๊ฒŒ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฉด์—์„œ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์†Œ๋ฉธ์‹œํ‚ค๋Š” ๋ฐ๋Š” ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋ฆฌ์†Œ์Šค๊ฐ€ ์†Œ์š”๋œ๋‹ค. ๊ฐ ์ž‘์—…๋งˆ๋‹ค ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ด๋Ÿฐ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๊ณ , ์ „์ฒด์ ์ธ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ผ์น  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, SimpleAsyncTaskExecutor๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜์— ๋Œ€ํ•œ ์ œํ•œ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋™์‹œ์— ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ •์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์—†๋Š” ์ˆ˜์ค€์œผ๋กœ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๊ณง OutOfMemoryError ๋“ฑ์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ, ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด Async์˜ Executor๋ฅผ ThreadPool๋ฐฉ์‹์˜ Executor๋กœ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋Š”, ์‚ฌ์šฉํ•  ์Šค๋ ˆ๋“œ ํ’€์— ์†ํ•œ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ˆ˜์ธ corepoolsize, corepoolsize๊ฐ€ ๊ฐ€๋“ ์ฐฌ ์ƒํƒœ์—์„œ ๋”์ด์ƒ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ• ๋•Œ, ๋Œ€๊ธฐํ•˜๋Š” ์žฅ์†Œ ํฌ๊ธฐ์ธ queuecapacity, ์Šค๋ ˆ๋“œ ํ’€์ด ํ™•์žฅ๋  ์ˆ˜ ์žˆ๋Š” ์Šค๋ ˆ๋“œ์˜ ์ƒํ•œ์„ , ์ฆ‰ ์Šค๋ ˆ๋“œ ์ˆ˜์˜ ์ƒํ•œ์„ ์ธ maxpoolsize๋ฅผ ์กฐ์ •ํ•˜์—ฌ ThreadPool๋ฐฉ์‹์˜ Async Executor๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค.


Race Condition ํ•ด๊ฒฐ

ํ”„๋กœ์ ํŠธ์—์„œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ถ”์ฒœ / ์ถ”์ฒœ ํ•ด์ œ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ถ”์ฒœ์ˆ˜, ์กฐํšŒ์ˆ˜์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ํ•˜๋‚˜์˜ ํ•„๋“œ์— ๋Œ€ํ•ด ์ˆ˜๋งŽ์€ ํŠธ๋žœ์ ์…˜ ์š”์ฒญ์ด ์˜ค๊ฐ€๋Š” ๊ธฐ๋Šฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒฝ์šฐ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ Race Condition, ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ Redis๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. Redis๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋กœ ์ž์›์— ๋Œ€ํ•œ Race Condition ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Cache Write ์ „๋žต์ค‘์—์„œ Write Back ์ „๋žต์„ ์‘์šฉํ•˜์˜€๋‹ค. Redis์— ์š”์ฒญ์„ ๋ณด๋‚ผ countTemplate๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ด์„œ Redis์— ๊ฐ์ฒด์™€ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ณ  ์ผ์ •์‹œ๊ฐ„๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ Cache์— ์ €์žฅ๋œ ๋‚ด์šฉ์„ DB์— ๋ฐ˜์˜์‹œํ‚ค๋„๋ก ํ•˜์˜€๋‹ค. ์ด๋ ‡๊ฒŒ Redis์— ์ €์žฅ๋œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ 30์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ DB์— ๋ฐ˜์˜๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

ํ•˜์ง€๋งŒ, Redis๋ฅผ ์ด์šฉํ•˜๋”๋ผ๋„ ๊ฒฐ๊ตญ์—๋Š” Redis์—์„œ ๊ฐ’์„ ์ฝ๊ณ  ๋น„๊ตํ•˜๊ณ  ์“ฐ๋Š” ์ด ์ผ๋ จ์˜ ํ–‰์œ„๋Š” Spring ์ฝ”๋“œ์—์„œ Lock์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๊ณ  ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— Atomicํ•˜๊ฒŒ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Redis Lock์„ ๊ฑธ์–ด์ฃผ์–ด Atomicํ•˜๊ฒŒ ์‹คํ–‰๋˜๋„๋ก ๋ณด์žฅํ•˜์˜€๋‹ค. ๋ฌผ๋ก , ์šฐ๋ฆฌ๋Š” Redis Lock์„ ์ด์šฉํ•˜์˜€์ง€๋งŒ, ์ด์™ธ์—๋„ Redis Transaction ์ด์šฉ, Redis ๋‚ด๋ถ€์—์„œ Lua Script๋ฅผ ์ด์šฉํ•˜์—ฌ Atomicํ•˜๊ฒŒ ์‹คํ–‰, DB Lock, DB์˜ Isolation Level ์กฐ์ • ๋“ฑ์˜ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค.

ํ˜„์žฌ ๋ฐฉ๋ฒ•๋„ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ๋‚ด๊ฐ€ ์ถ”์ฒœํ•œ ๋‹ต๋ณ€์˜ ์ถ”์ฒœ์ˆ˜๊ฐ€ ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์–ด ๋ณด์—ฌ์ง€์ง€๋Š” ์•Š๋Š”๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ฌ๋•Œ๋Š” ์บ์‹œ์—์„œ ์ฐพ๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ถ”ํ›„์—๋Š” ์บ์‹œ์—์„œ ๊ฐ’์„ ์ฐพ๋„๋ก ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ, ์šด์˜์ฒด์ œ ๊ณผ๋ชฉ์—์„œ ์บก์Šคํ†ค ์ธํ„ฐ๋ทฐ ๊ณผ์ œ ๋ฌธ์„œ์— Race Condition ์˜ˆ๋ฐฉ์— ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์—ˆ์—ˆ๋‹ค. ํ˜น์‹œ๋ผ๋„ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ์— ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ๋ฌธ์„œ์— 2๋ฒˆ์„ ์ฐธ์กฐํ•ด์ฃผ๊ธธ ๋ฐ”๋ž€๋‹ค.


N+1 ๋ฌธ์ œ

์—ฌ๋Ÿฌ ์ฐธ์กฐ๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ด๋“ค์„ Joinํ•˜์—ฌ ํ˜ธ์ถœํ•˜๋Š” ์ƒํ™ฉ์ด ๋งŽ์ด ์žˆ์—ˆ์œผ๋ฉฐ ์ด๋•Œ N+1๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Fetch Join์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Projection ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์˜ Inner Join๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ฒฝ์šฐ์˜ N+1์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋ชจ์ƒ‰ํ•˜์—ฌ ์ ์šฉํ•˜์˜€๋‹ค. ์ด๋กœ ์ธํ•ด ์„œ๋ฒ„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ์„ฑ๋Šฅํ–ฅ์ƒ์ด ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ ์‹œํ–‰ ์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ฐฉ์‹์€ Projection์„ ์‚ฌ์šฉํ•˜์—ฌ DTO๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ JPA ๊ฒ€์ƒ‰์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋Š”๋ฐ ์ด ๊ฒฝ์šฐ Fetch Join์ด๋‚˜ EntityGraph๋กœ๋Š” ํ•ด๊ฒฐ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. @Projection์„ ์‚ฌ์šฉํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ๋„ ์žˆ์œผ๋‚˜ ์ข€ ๋” ๊ณ ๋ฏผํ•ด๋ณธ ๊ฒฐ๊ณผ Join์˜ ๋ชฉ์ ์—์„œ N+1๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•  ํ•„๋“œ๊ฐ’์ด ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด InnerJoin์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ํ•ด๊ฒฐ๋˜๋Š” ๋ฒ”์ฃผ์˜€๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜๋‹ค.

๋˜ํ•œ, batch size๋ฅผ ์กฐ์ ˆํ•ด์„œ N+1 ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ์„œ๋น„์Šค ์ดˆ๊ธฐ๋ผ์„œ batch size๋กœ ์˜ํ–ฅ์ด ๊ฐˆ ๋งŒํผ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ ์€ ๋ณด๋ฅ˜ํ•˜์˜€๋‹ค.


์•ˆ์ •์„ฑ ๊ณ ๋ ค ์‚ฌํ•ญ


์‚ฌ์šฉ์ž ํŠธ๋ž˜ํ”ฝ ์ œํ•œ

A๋ผ๋Š” ์‚ฌ๋žŒ์ด ์•…์˜์ ์œผ๋กœ ์šฐ๋ฆฌ AI ๋ชจ๋ธ์—๊ฒŒ 1๋ถ„์— 100๋งŒ๋ฒˆ์˜ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์šฐ๋ฆฌ AI Token์€ ๋ชจ๋‘ ์‚ฌ๋ผ์ง„๋‹ค. B๋ผ๋Š” ์‚ฌ๋žŒ์€ Spring ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์— 1์ดˆ์— 1000๋งŒ๋ฒˆ ์š”์ฒญ์„ ๋‚ ๋ฆฐ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์—ญ์‹œ ์„œ๋ฒ„๊ฐ€ ํ„ฐ์งˆ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด์—์„œ ๋ฏธ๋“ค์›จ์–ด ์—ญํ• ์„ ํ•˜์—ฌ ๋ถ„๋‹น API ์‚ฌ์šฉ๋Ÿ‰์„ ์ œํ•œํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ API Rate Limiter๋ฅผ ์ ์šฉํ–ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์ตœ๋Œ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” Token Bucket Capacity๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ณ„์†, ์ง€์†์ ์œผ๋กœ ์ผ์ •์‹œ๊ฐ„๋งˆ๋‹ค Token์ด ๋ฆฌํ•„์ด ๋œ๋‹ค. ์ด Token ๋ฆฌํ•„์€ Bucket Size๊ฐ€ ๊ฝ‰์ฐผ์„๋•Œ๋Š” ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ API ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ๋งˆ๋‹ค Bucket์—์„œ Token์„ ๊บผ๋‚ด๊ฐ„๋‹ค. ๋งŒ์•ฝ, ๊บผ๋‚ผ Token์ด ์—†๋‹ค๋ฉด, Status 429๋ฅผ ๋ณด๋‚ด์ค€๋‹ค. (Too Many Requests) ์ด์™ธ์—๋„ ๋ˆ„์ถœ ๋ฒ„ํ‚ท ์•Œ๊ณ ๋ฆฌ์ฆ˜, ๊ณ ์ • ์œˆ๋„ ์นด์šดํ„ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ด๋™ ์œˆ๋„ ๋กœ๊น… ์•Œ๊ณ ๋ฆฌ์ฆ˜, ์ด๋™ ์œˆ๋„ ์นด์šดํ„ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋“ฑ์ด ์กด์žฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ Token Bucket Algorithm์„ ๋งŽ์ด ์“ฐ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

Redis๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ DB์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ๋นจ๋ผ์„œ ์ ํ•ฉํ•˜๋‹ค. ๋˜ํ•œ, Redis์˜ ๋˜ ๋‹ค๋ฅธ ํŠน์ง•์€ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, ๋‹ค์ค‘ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์—๋„ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ดˆ๋‹น ์‚ฌ์šฉ์ž๊ฐ€ 6~7๋ฒˆ์˜ ์š”์ฒญ๊นŒ์ง€๋งŒ ๋ณด๋‚ด๊ณ , ์•„๋‹๊ฒฝ์šฐ 429 Error๋ฅผ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•ด์„œ ํŠธ๋ž˜ํ”ฝ ์ œํ•œ์„ ๊ฑธ์—ˆ๋‹ค.

์‹ค์ œ๋กœ ์œ„์— ์‚ฌ์ง„์ฒ˜๋Ÿผ Apache Jmeter๋กœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ๋Œ๋ ค๋ณด๋ฉด, 429 Error๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ํŒŒ์ผ ์šฉ๋Ÿ‰ ์ œํ•œ

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


๋ณด์•ˆ์  ๊ณ ๋ ค ์‚ฌํ•ญ


์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”

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

๊ทธ๋ž˜์„œ, โ€œ์™ธ๊ตญ๋ฏผโ€ ์„œ๋น„์Šค๋Š” Firebase๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ID์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. Firebase๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ž˜ ๋ณด๊ด€ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


JWT๋ฅผ ํ†ตํ•œ Authentication

Token์€ ์„œ๋ฒ„๊ฐ€ ๊ฐ๊ฐ์˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์‚ฌ์šฉ์ž์˜ ์œ ๋‹ˆํฌํ•œ ์ •๋ณด๋ฅผ ๋‹ด์€ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ์ด๋‹ค. ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ •์ƒ์ผ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋กœ๊ทธ์ธ์‹œ Main Business Server์—์„œ Access ๋ฐ Refresh Token์„ ๋ฐœ๊ธ‰ํ•ด์ฃผ๊ณ , ๋งค ์š”์ฒญ๋งˆ๋‹ค Header๋กœ AccessToken์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด API Gateway์„œ๋ฒ„์—์„œ ํ•ด๋‹น ํ† ํฐ์„ ํ™•์ธํ•ด๋ณด๊ณ , Invalidํ•œ JWT Token์ผ ๊ฒฝ์šฐ 403 ์—๋Ÿฌ๋ฅผ ๋ณด๋‚ด์ค€๋‹ค. ์ •์ƒ์ ์ธ Token์ผ ๊ฒฝ์šฐ, ํ•ด๋‹น ์„œ๋ฒ„๋กœ ๋ผ์šฐํŒ…์„ ํ•ด์ฃผ์–ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.


Token ํƒˆ์ทจ ์ƒํ™ฉ ์˜ˆ๋ฐฉ

JWTํ† ํฐ์€ ํƒˆ์ทจ์˜ ์œ„ํ—˜์„ฑ์ด ์žˆ๋‹ค. JWT๊ธฐ๋ฐ˜ ์ธ์ฆ ๋ฐฉ์‹์—์„œ๋Š” ์„ธ์…˜๊ณผ ๋‹ค๋ฅด๊ฒŒ Statelessํ•˜๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Token์ด ํƒˆ์ทจ๋‹นํ•ด๋„ ์šฐ๋ฆฌ ์„œ๋ฒ„๊ฐ€ ๊ทธ๊ฑธ ์•Œ์•„์ฐจ๋ฆฌ๊ณ  ํ•ด๋‹น Token์„ ์ค‘์ง€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ, accessToken์€ ์œ ํšจ๊ธฐ๊ฐ„์ด ์งง์€ ๋‹จ์œ„์˜ ํ† ํฐ์œผ๋กœ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์ฃผ๋กœ 15๋ถ„ ์ •๋„๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ ‡๊ฒŒ ์งง์€ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž๋Š” ๊ณ„์†ํ•ด์„œ ์žฌ๋กœ๊ทธ์ธ์„ ํ•ด์•ผ๋œ๋‹ค๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด Refresh Token์ด๋‹ค.

Refresh Token์€ AccessToken๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋งค์šฐ ๊ธด ์œ ํšจ๊ธฐ๊ฐ„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. Access Token๊ณผ Refresh Token์„ ์‚ฌ์šฉํ•  ๋•Œ, ํ”Œ๋กœ์šฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ˜๋Ÿฌ๊ฐ„๋‹ค.

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํšŒ์›๊ฐ€์ž… ํ˜น์€ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด ์„œ๋ฒ„๋Š” ์‘๋‹ต๊ฐ’์œผ๋กœ Access Token๊ณผ Refresh Token์„ ํ•จ๊ป˜ ์ œ๊ณตํ•œ๋‹ค.
  2. ํด๋ผ์ด์–ธํŠธ๋Š” API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ Access Token ๋‹ด์•„ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„์—์„œ Access Token์„ ํ†ตํ•ด ์œ ์ €๋ฅผ ์ธ์ฆํ•œ๋‹ค.
  3. ๋งŒ์•ฝ Access Token์˜ ์œ ํšจ ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด, ํด๋ผ์ด์–ธํŠธ๋Š” Refresh Token์„ ์„œ๋ฒ„์— ์ „๋‹ฌํ•˜์—ฌ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰๋ฐ›๋Š”๋‹ค.

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

๊ทธ๋Ÿฐ๋ฐ RefreshToken๋„ ํƒˆ์ทจ๋‹นํ•˜๋ฉด ์–ด๋–ป๊ฒŒํ•˜์ง€?๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งŽ์ด ์“ฐ๋Š” ๋ฐฉ๋ฒ•์ด Refresh Token Rotation(RTR) ๋ฐฉ๋ฒ•์ด๋‹ค.

Refresh Token Ratation(RTR)์ด๋ž€ Access Token์ด ๋งŒ๋ฃŒ๋˜๊ณ  Refresh Token์œผ๋กœ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐ›์•„์˜ฌ ๋•Œ, ์ƒˆ๋กœ์šด Refresh Token๋„ ๋ฐ›์•„์˜ค๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์ฆ‰. Refresh Token์ด ์‚ฌ์šฉ๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด Access Token๊ณผ Refresh Token์„ ๋ฐœ๊ธ‰ํ•˜์—ฌ ์ด์ „์— ๋ฐœ๊ธ‰๋œ Token๋“ค์€ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค.


AccessToken Ban

๋งŒ์•ฝ์— ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒ์„ ํ–ˆ๋‹ค๊ณ  ํ•ด๋ณด์ž. AccessToken์˜ ๊ธฐ๊ฐ„์ด ๋งŒ์•ฝ์— 30๋ถ„์ธ๋ฐ, 5๋ถ„๋งŒ์— ๋กœ๊ทธ์•„์›ƒ์„ ํ–ˆ๋Š”๋ฐ๋„, ๊ทธ Token๊ฐ€์ง€๊ณ  ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋กœ๊ทธ์•„์›ƒ์„ ํ•  ๊ฒฝ์šฐ, Redis์— ํ•ด๋‹น AccessToken์„ Ban์„ ํ•ด๋‘์–ด์„œ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ๋ชปํ•˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.


HMAC

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

๋”ฐ๋ผ์„œ, ํšŒ์›๊ฐ€์ž… ๋ฐ ๋กœ๊ทธ์ธ ์‹œ์—๋Š” HMAC์„ ํฌํ•จํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ์šฐ๋ฆฌ Flutter ์•ฑ๊ณผ ์„œ๋ฒ„ ๋ชจ๋‘ ๊ณตํ†ต๋œ Secret๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ์ด Secret์„ ๊ฐ€์ง€๊ณ  ์šฐ๋ฆฌ Request์˜ HMAC์„ ๊ฐ™์ด ๋ณด๋‚ด๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ์šฐ๋ฆฌ ์•ฑ์—์„œ ๋ณด๋‚ด์ง€ ์•Š์€ ์š”์ฒญ๋“ค์€ ๋ชจ๋‘ ๋ง‰์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.


ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ ์šฉ

JWT Secret, HMAC Secret, API Key ๋“ฑ๋“ฑ์„ ์šฐ๋ฆฌ Code์— Hard Codingํ•ด์„œ๋Š” ์ ˆ๋Œ€ ์•ˆ๋œ๋‹ค. ๋”ฐ๋ผ์„œ, env ํŒŒ์ผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.


ํ…Œ์ŠคํŠธ ๊ณ ๋ ค ์‚ฌํ•ญ


Test Container๋ฅผ ์ด์šฉํ•œ ๊ฐ€์ƒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•

๋˜ํ•œ, ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ ์„œ๋น„์Šคํ•˜๋Š” RDS๋ฅผ ๊ฐ€์ง€๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค๋ฉด, ๋งค์šฐ ์œ„ํ—˜ํ•  ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ง„ํ–‰์„ ์œ„ํ•ด์„œ ๋ถ„๋ช…ํžˆ DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ•ด๋ด์•ผ๋˜๋Š” ์ˆœ๊ฐ„์ด ์กด์žฌํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Test Container๋ฅผ ์ด์šฉํ•˜์—ฌ MySQL, Redis ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“  ๋’ค, ๋…๋ฆฝ๋œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.


Apache Jmeter๋ฅผ ์ด์šฉํ•œ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ

ํ”„๋กœ์ ํŠธ์—์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ ํ…Œ์ŠคํŠธ์™€ ๊ฐ™์ด ๋Œ€๊ทœ๋ชจ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ, ์‰ฝ๊ฒŒ ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ์„ ์ƒ์„ฑํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์™ธ๋ถ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ด์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ TPS๋ฅผ ์ธก์ •ํ•  ์ˆ˜๋„ ์žˆ์—ˆ๋‹ค.

์‹ค์ œ๋กœ ๊ณต์ง€์‚ฌํ•ญ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ›์•„์˜ค๋Š” API์— ๋Œ€ํ•˜์—ฌ RPS๋ฅผ ์ธก์ •ํ•ด๋ณด๋‹ˆ 2000 RPS ์ •๋„๊นŒ์ง€๋Š” ๋ฌด๋ฆฌ ์—†์ด ์ž˜ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ด ์ด์ƒ์œผ๋กœ๋Š” ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ง€ ์•Š์•˜์ง€๋งŒ, ๋” ๊ฐ€๋Šฅํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.


์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„

๊ธฐ์กด ์•„ํ‚คํ…์ฒ˜๋Š” ์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์œ„์˜ ์•„ํ‚คํ…์ฒ˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ์žˆ์—ˆ๋‹ค. ์šฐ์„ , ์šฐ๋ฆฌ ๋ฉ”์ธ Spring ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„๋Š” ๋‹ค๋ฅธ๊ฑฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋„ ๋ฐ”์˜๋‹ค. ๊ฒŒ๋‹ค๊ฐ€, ์ฑ„ํŒ…์„ Long Polling์œผ๋กœ ๊ฐœ๋ฐœํ•˜์˜€๋Š”๋ฐ, ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋™์‹œ ๋‹ค๋ฐœ์ ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค? ๊ทธ๋Ÿผ ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ์„ ๋งค์šฐ ๋–จ์–ด์งˆ ๊ฒƒ์ด๋ฉฐ, ์‘๋‹ต์‹œ๊ฐ„์€ ๊ธธ์–ด์งˆ ๊ฒƒ์ด๋‹ค. ๊ฒฐ๊ตญ Software Quality๊ฐ€ ๋–จ์–ด์งˆ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ฑ„ํŒ… ์„œ๋ฒ„๋ฅผ ๋ถ„๋ฆฌํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, api ์š”์ฒญ์„ ๋ณด๋‚ผ๋•Œ https๊ฐ€ ์•„๋‹Œ http๋กœ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ์˜ Integrity๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์š”์ฒญ์„ https๋กœ ์•”ํ˜ธํ™”ํ•ด์„œ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ฑ—๋ด‡, ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, ์ฑ„ํŒ… ์„œ๋ฒ„ ๋ชจ๋‘ JWT Token์„ ํ™•์ธํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค. ํ•˜์ง€๋งŒ, ์œ„์™€ ๊ฐ™์ด ๋””์ž์ธํ•˜๋ฉด, ๊ฐ๊ฐ ์„œ๋ฒ„์—์„œ JWT Token์„ ํ™•์ธํ•˜๋Š” ๋กœ์ง์„ ์งœ์•ผ๋˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ ์ด๋‹ค. ๋˜ํ•œ, API ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ์„ ๊ฑธ๊ธฐ ์œ„ํ•ด์„œ ๊ฐ๊ฐ API Rate Limiter๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, API Gateway๋ฅผ ๊ตฌ์ถ•ํ•ด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์€ ์•„ํ‚คํ…์ฒ˜๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

์šฐ์„ , ์ฑ„ํŒ… ์„œ๋ฒ„ ๋ถ„๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ์ฑ„ํŒ… ์„œ๋ฒ„๋Š” Ruby On Rails๋กœ ๊ฐœ๋ฐœํ•˜์˜€๋‹ค. ๊ทธ ์ด์œ ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€์ด๋‹ค. ์ฒซ๋ฒˆ์งธ, Ruby๋ผ๋Š” ์–ธ์–ด๋Š” ๋งค์šฐ ์‰ฝ๋‹ค. ํŒŒ์ด์ฌ์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•ด๋ณด์ง€ ์•Š์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ž…๋ฌธ์šฉ์œผ๋กœ ์ถ”์ฒœ์ด ๊ฐ€๋Šฅํ•  ์ •๋„๋กœ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ํ•„์ž๋Š” ์–ด๋ฆด ๋•Œ RPG VX ACE๋ผ๋Š” ๊ฒŒ์ž„ ๋งŒ๋“ค๊ธฐ ํˆด์—์„œ, ์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋กœ Ruby๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์–ด๋Š์ •๋„ ์กฐ๊ธˆ์€ ์•„๋Š” ์ƒํƒœ์˜€๋‹ค. ๋‘๋ฒˆ์งธ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ๋งค์šฐ ์‰ฝ๋‹ค. Spring์€ Test Code๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๊ต‰์žฅํžˆ ์—ฌ๋Ÿฌ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผํ•˜๋ฉฐ ๋ณต์žกํ•˜๋‹ค. ๋ฐ˜๋ฉด์— Ruby On Rails์€ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์„ฑ๋˜์žˆ๋‹ค. ์„ธ๋ฒˆ์งธ, Ruby On Rails๋Š” ๋น ๋ฅธ ๊ฐœ๋ฐœ์„ ์ง€ํ–ฅํ•œ๋‹ค. COC์›์น™์„ ์ค‘์š”์‹œํ•ด์„œ, ์„ค์ •์ด ๊ฑฐ์˜ ์—†๋‹ค. Spring์œผ๋กœ ์„œ๋ฒ„ ํ•˜๋‚˜ ๋งŒ๋“ค๋ ค๋ฉด ํ•˜๋‚˜๋ถ€ํ„ฐ ์—ด๊นŒ์ง€ ์„ค์ •ํ• ๊ฒŒ ๋งŽ์•„์„œ ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค. ํ”„๋กœ์ ํŠธ ๋งˆ๊ฐ ๊ธฐํ•œ์ด ์–ผ๋งˆ ์•ˆ๋‚จ์€ ์ง€๊ธˆ, Ruby On Rails ๋˜๋Š” Express๋งŒํผ ์ข‹์„๊ฒŒ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ํŠนํžˆ, ์–ธ์–ด์˜ ํŠน์„ฑ์ด ์• ์ž์ผ์— ์ดˆ์ ์ด ๋งž์ถฐ์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์‚ฐ์„ฑ์ด ๋†’๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์ฑ„ํŒ… ์„œ๋ฒ„ ๊ฐœ๋ฐœ์— Ruby On Rails๋ฅผ ์ฑ„ํƒํ–ˆ๋‹ค.

๋‘๋ฒˆ์งธ, ์ž๋™์œผ๋กœ CI/CD๊ฐ€ ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, ์ฑ„ํŒ… ์„œ๋ฒ„, AI ์„œ๋ฒ„์— Git Actions๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋กœ์จ, ๊ตณ์ด ์šฐ๋ฆฌ EC2์— ์ ‘์†ํ•  ํ•„์š” ์—†์ด ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ ๋ฐฐํฌ๊ฐ€ ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

์„ธ๋ฒˆ์งธ, Route53์„ ์ด์šฉํ•ด ๋„๋ฉ”์ธ์„ ์ ์šฉํ–ˆ๋‹ค. ๊ฐ€๋น„์•„ ๋„๋ฉ”์ธ์—์„œ capstone30.shop์ด๋ผ๋Š” ๋„๋ฉ”์ธ์„ ๊ตฌ๋งคํ–ˆ๋‹ค. ์šฐ๋ฆฌ ํŒ€์€ API ๋ฌธ์„œ๋ฅผ Swagger๋ฅผ ์ด์šฉํ•ด์„œ ๊ณต์œ ํ•˜๋Š”๋ฐ, ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ ์šฐ๋ฆฌ ์„œ๋ฒ„ ๋„๋ฉ”์ธ์„ ์ณ์•ผํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๊ธฐ์กด AWS EC2 ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์€ ๊ธฐ์–ตํ•˜๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์„œ, ์™ธ์šฐ๊ธฐ ์‰ฌ์šด ๋„๋ฉ”์ธ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค. ๋˜ํ•œ, Https๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด DV SSL ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์•˜์–ด์•ผ ํ–ˆ๋Š”๋ฐ, AWS EC2 ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์œผ๋กœ๋Š” ์ธ์ฆ์„œ ๋ฐœ๊ธ‰์ด ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋”ฐ๋กœ ๋„๋ฉ”์ธ์„ ๊ตฌ๋งคํ•ด์„œ ์ ์šฉํ–ˆ๋‹ค.

๋„ค๋ฒˆ์งธ, Nginx์™€ Certbot์„ ์ด์šฉํ•˜์—ฌ Https๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ Letโ€™s Encrypt๋ผ๋Š” ๋ฌด๋ฃŒ๋กœ DV ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” CA๋กœ๋ถ€ํ„ฐ SSL์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ Https๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด๋กœ์จ, ๋ฐ์ดํ„ฐ์˜ Integrity๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ, Letโ€™s Encrypt ์ธ์ฆ์„œ๋Š” 90์ผ๋งˆ๋‹ค ์ƒˆ๋กœ ๋ฐœ๊ธ‰ ๋ฐ›์•„์•ผํ•˜๋Š”๋ฐ, Docker Compose์— ์ฃผ๊ธฐ์ ์œผ๋กœ ์ธ์ฆ์„œ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š” command๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ์ด ๊ณผ์ •์ด ์ž๋™์œผ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋˜ํ•œ, Spring Cloud Gateway ์•ž๋‹จ์—, Nginx๊ฐ€ ๋จผ์ € ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„ https์„ ๋‚ด๋ถ€์ ์œผ๋กœ http๋กœ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, ๋กœ์ปฌ ์„œ๋ฒ„ ๋‚ด์—์„œ๋Š” ์•”ํ˜ธํ™”ํ•˜๋Š”๋ฐ ๋ฆฌ์†Œ์Šค๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

๋‹ค์„ฏ๋ฒˆ์งธ, Spring ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„, Ruby ์ฑ„ํŒ… ์„œ๋ฒ„, AI ์„œ๋ฒ„ ์•ž๋‹จ์—์„œ JWT ํ† ํฐ Authentication์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๋Š” Gateway๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋กœ์จ, ๊ฐ๊ฐ ์„œ๋ฒ„์—์„œ ํ† ํฐ ํ™•์ธ ๋กœ์ง์„ ๋งŒ๋“ค ํ•„์š” ์—†์ด ๊ฒŒ์ดํŠธ์›จ์ด์—์„œ ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ, Gateway์—์„œ Redis๋ฅผ ์ด์šฉํ•˜์—ฌ Token Bucket Algorithm์„ ๊ตฌํ˜„ํ•˜์—ฌ API Rate Limiter๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋กœ์จ, ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ํ•œ๋ฒˆ์— ๋งŽ์€ ์š”์ฒญ์„ ์šฐ๋ฆฌ ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, Spring ๋ฉ”์ธ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋ฒ„์— Redis๋ฅผ ์ ์šฉํ•˜์˜€๋‹ค. ์ด๋Š” Redis๋ฅผ ์ด์šฉํ•˜์—ฌ Refresh Token์„ ์ €์žฅํ•˜๊ณ , ๋กœ๊ทธ์•„์›ƒ์‹œ Access Token์„ Banํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ, ๊ธ€์˜ ์ถ”์ฒœ์ˆ˜๋‚˜ ์กฐํšŒ์ˆ˜ ๋“ฑ์—์„œ Race Condition์„ ์˜ˆ๋ฐฉํ•˜๋Š” ์šฉ๋„๋กœ๋„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ์›๋ž˜ ๊ณง๋ฐ”๋กœ ์šฐ๋ฆฌ RDB์— ์ €์žฅํ•˜๋ฉด ๋™์‹œ์— ๋งŽ์€ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ๋“ค์–ด์™”์„ ๋•Œ Race Condition์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋งค์šฐ ๋†’์•˜๋‹ค. ํ•˜์ง€๋งŒ, Redis๋Š” ์‹ฑ๊ธ€์Šค๋ ˆ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์—, Redis์— ์ž„์‹œ๋กœ ์กฐํšŒ์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ณ , ์ฃผ๊ธฐ์ ์œผ๋กœ Scheduler๋ฅผ ์ด์šฉํ•ด ์ง„์งœ ์šฐ๋ฆฌ ์„œ๋น„์Šค RDB์— ๋ฐ˜์˜ํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, Redis๋ฅผ ์ด์šฉํ•ด Cache๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ๋งค๋ฒˆ ๋˜‘๊ฐ™์€ ์ •๋ณด๋ฅผ RDB์— ์ ‘๊ทผํ•˜์—ฌ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ์€ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ํฌ๋ฏ€๋กœ Redis์— Caching์„ ํ•จ์œผ๋กœ์จ Response Time์„ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.


ํ˜„์‹ค์  ์ œํ•œ ๋ฐ ๊ฐœ์„  ํ•„์š” ์‚ฌํ•ญ


Admin ํŽ˜์ด์ง€์˜ ๋ถ€์žฌ

ํ˜„์žฌ admin์ด ๊ธ€์„ ๊ด€๋ฆฌํ•˜๊ณ , ์œ ์ €๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ์‹ ๊ณ  ๋ฐ›์€ ๊ฒƒ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ, ์•ž์œผ๋กœ ์•„์ดํฐ ์ถœ์‹œ๋ฅผ ์œ„ํ•ด์„œ๋ผ๋„ admin ํŽ˜์ด์ง€๋Š” ํ•„์ˆ˜์ด๋‹ค. ํ˜„์žฌ๋Š” ์‹œ๊ฐ„ ๋ถ€์กฑ์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š์€ ์ƒํƒœ์ด๋‚˜, ์ถ”ํ›„ ์ œ์ž‘ํ•  ์˜ˆ์ •์ด๋‹ค.


์• ํ”Œ์Šคํ† ์–ด ์ถœ์‹œ

ํ˜„์žฌ ์™ธ๊ตญ๋ฏผ์€ ํ”Œ๋ ˆ์ด์Šคํ† ์–ด์—๋งŒ ์ถœ์‹œ๋œ ์ƒํƒœ์ด๋‹ค. ์• ํ”Œ์Šคํ† ์–ด์— ์ถœ์‹œํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฑ„ํŒ… ์•”ํ˜ธํ™” ์ž‘์—… ๋ฐ ์‹ ๊ณ ํ•˜๊ธฐ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผ๋œ๋‹ค. admin ํŽ˜์ด์ง€ ์ž‘์—…๊ณผ ํ•จ๊ป˜ ์ด ์ž‘์—…์„ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ด๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฒˆ์—ญ API๋„ DeepL์—์„œ ํŒŒํŒŒ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฒˆ์—ญ์˜ ํ’ˆ์งˆ๋„ ์˜ฌ๋ฆด ํ•„์š”๊ฐ€ ์žˆ๋‹ค.


๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ฐœ์„ 

ํ˜„์žฌ ๊ฒŒ์‹œ๋ฌผ ๋ฐ ๊ณต์ง€์‚ฌํ•ญ ๊ฒ€์ƒ‰์€ ์ œ๋ชฉ์œผ๋กœ๋งŒ ๊ฒ€์ƒ‰๋œ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋ณด๋‹ค ์ œ๋ชฉ + ๋‚ด์šฉ์œผ๋กœ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ SQL Like ์—ฐ์‚ฐ์ž๋กœ ๋‚ด์šฉ๊นŒ์ง€ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜๊ธด ํ•˜๋‚˜, Like ์—ฐ์‚ฐ์ž๋Š” ์„ ํ˜• ์—ฐ์‚ฐ์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ElasticSearch๋ฅผ ์ด์šฉํ•ด์„œ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ElasticSearch๋Š” ๋ถ„์‚ฐ ๊ฒ€์ƒ‰ ๋ฐ ๋ถ„์„ ์—”์ง„์œผ๋กœ, ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์—์„œ ๋น ๋ฅธ ์ „์ฒด ํ…์ŠคํŠธ ๊ฒ€์ƒ‰์„ ์ง€์›ํ•œ๋‹ค. ์ถ”ํ›„, ElasticSearch๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ RDB์— ์ €์žฅ๋œ ๊ธ€๋“ค์„ ์—ญ์ƒ‰์ธํ•˜์—ฌ ๊ฑฐ์˜ ์‹ค์‹œ๊ฐ„์— ๊ฐ€๊น๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๋„๋ก ๊ฐœ์„ ํ•  ๊ฒƒ์ด๋‹ค.


ํ…Œ์ŠคํŠธ ์ง„ํ–‰ ๊ฐ•ํ™”

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

image

๋˜ํ•œ, ํ˜„์žฌ๋Š” ์šฐ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ Code Coverage๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ๊ทธ๋ž˜์„œ, ์šฐ๋ฆฌ๊ฐ€ ๋ฏธ์ณ ์ƒ๊ฐํ•˜์ง€ ๋ชปํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋‚˜ Domain์— ๋Œ€ํ•˜์—ฌ ๋ˆ„๋ฝ๋  ๊ฐ€๋Šฅ์„ฑ๋„ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ถ”ํ›„ ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด Jacoco๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์šฐ๋ฆฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ Code Coverage๋ฅผ ์ธก์ •ํ•˜๊ณ , html๋กœ ๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํŒ€์›๋“ค์ด ์‰ฝ๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ถ”์ถœํ•  ๊ฒƒ์ด๋‹ค.


์ธ์Šคํ„ด์Šค ์„ฑ๋Šฅ์˜ ํ•œ๊ณ„

ํ˜„์žฌ ์šฐ๋ฆฌ์„œ๋ฒ„๋Š” Free Tier๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ๋Š” ํŠธ๋ž˜ํ”ฝ์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋Š” ์ƒํ™ฉ์ด๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ˆ˜์ง์  ํ™•์žฅ ๋˜๋Š” ์ˆ˜ํ‰์  ํ™•์žฅ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๋‹ค. ํ˜„์žฌ Free Tier ์„ฑ๋Šฅ์ด ์•„๋‹Œ ๋” ์ข‹์€ EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๊ฐœ์˜ EC2๋ฅผ ๋งŒ๋“ค์–ด Load Balancing์„ ์ ์šฉํ•˜์—ฌ Scale Out์ ์ธ ํ™•์žฅ์„ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ๋Š” ๋น„์šฉ์ ์ธ ๋ฌธ์ œ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด๋‹ค.


๋ฌด์ค‘๋‹จ ๋ฐฐํฌ

ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” Git Actions๋ฅผ ์ด์šฉํ•œ CI/CD๋ฅผ ์ ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ํ˜„์žฌ ๋ฐฉ์‹์€ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ๋ฐฐํฌ๋  ๋•Œ ๋ฐ˜๋“œ์‹œ 1~2๋ถ„์ •๋„ ์„œ๋ฒ„๊ฐ€ ์ค‘๋‹จ๋  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ์ด๋Š”, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค.



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


๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ

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


๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น… ๊ฐ•ํ™”

์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ์ „๋ฐ˜์ ์ธ ๋กœ๊น…์„ ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค. ํ˜„์žฌ ๋กœ๊น…์€ Nginx ๋‹จ์—์„œ ํŒŒ์ผ ์ €์žฅ์œผ๋กœ ํ•œ๋ฒˆ, API Gateway๋‹จ์—์„œ ์š”์ฒญ url, body, Authorization์œผ๋กœ ํ•œ๋ฒˆ, ๊ฐ MSA ์„œ๋ฒ„ ๋‹จ์—์„œ ํ•œ๋ฒˆ ์ด๋ฃจ์–ด์ง„๋‹ค. ํ•˜์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ์—์„œ์˜ ๋กœ๊ทธ๋Š” ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ์ง€ ์•Š์•„ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์ปจํ…Œ์ด๋„ˆ์— ์ ‘๊ทผํ•ด์„œ ํ•˜๋‚˜ํ•˜๋‚˜ ์ฐพ์•„๋ด์•ผํ•œ๋‹ค. ๋˜ํ•œ, MSA ๊ตฌ์กฐ๋‹ค ๋ณด๋‹ˆ๊น, ๊ฐ๊ฐ ์ปจํ…Œ์ด๋„ˆ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ ๋ด์•ผํ•œ๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฐ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํŒŒ์•…ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์„ ๋” ์จ์„œ ์„œ๋น„์Šค์— ๋งค์šฐ ์น˜๋ช…์ ์ด๋‹ค. ๋”ฐ๋ผ์„œ, Kafka + ELK Stack์„ ํ†ตํ•˜์—ฌ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ELK Stack์œผ๋กœ ์ „์†กํ•˜๊ณ , ์ด๋ฅผ ๋ถ„์„ํ•œ ํ›„, ๋Œ€์‹œ๋ณด๋“œ ํ˜•ํƒœ์˜ ์‹œ๊ฐ์ ์ธ ๋ฐ์ดํ„ฐ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ์‹œ์Šคํ…œ์ด ๊ตฌ์ถ•๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

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


์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜

ํ˜„์žฌ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” MSA ๊ตฌ์กฐ์— ๊ฐ€๊นŒ์šด ํ˜•ํƒœ์—ฌ์„œ ์ˆ˜๋งŽ์€ Container๋“ค์ด ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค. ๋Œ์•„๊ฐ€๋Š” Container๋งŒ ์„ธ๋ณด๋”๋ผ๋„, Redis, Spring, Spring Cloud Gateway, Ruby On Rails, Nginx, Certbot, FastAPI ๋ฒŒ์จ 7๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์— ์•ž์„œ ์–ธ๊ธ‰ํ•œ Kafka, Grafana, Prometheus, DB Replication, ELK Stack ๋“ฑ ๊นŒ์ง€ ์ ์šฉํ•˜๋ฉด ์ˆ˜๋งŽ์€ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋Œ์•„๊ฐˆ ๊ฒƒ์ด๋‹ค. ๊ฑฐ๊ธฐ์— ๋กœ๋“œ ๋ฐธ๋ฆฐ์„ฑ๊นŒ์ง€ ์ ์šฉํ•œ๋‹ค๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ด ์ปจํ…Œ์ด๋„ˆ๋“ค์„ ํ•œ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋ฌด๋ฆฌ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ, EKS, Docker Swarm, Kubernetes๊ฐ™์€ ์ž๋™์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์žฅ์•  ๋ณต๊ตฌ๋ฅผ ๋„์™€์ฃผ๋Š” ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ผ์ œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค.