Cloud Design 1st - 100-hours-a-week/KTB4-1st-wiki GitHub Wiki

1. 1์ฐจ ๋ฐฐํฌ ์ƒํ™ฉ

๋ฌธ์ œ ์ •์˜: ์Šคํƒ€ํŠธ์—… ์„œ๋น„์Šค๋ฅผ ์ €๋ ดํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค.

1) ํšŒ์‚ฌ ์ƒํ™ฉ ๋ฐ ์ œ์•ฝ ์กฐ๊ฑด

  • ํšŒ์‚ฌ ๊ฐœ๋ฐœ ์ธ๋ ฅ 6๋ช…
  • 4๋ช…์€ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ๋‹ด๋‹น
  • 2๋ช…์€ ์ธํ”„๋ผ ๋‹ด๋‹น
  • ์Šคํƒ€ํŠธ์—…์ด๋ฏ€๋กœ ๋น„์šฉ๊ณผ ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•˜๋ฉฐ, ๋น„์šฉ์ด ์ ˆ๋Œ€์ ์œผ๋กœ ๋ถ€์กฑํ•จ

๋ชฉํ‘œ

  • ์ž‘์€ ๋น„์šฉ๊ณผ ์šด์˜ ๋ณต์žก๋„๋กœ ์„œ๋น„์Šค๋ฅผ ๋น ๋ฅด๊ฒŒ ์ถœ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๊ตฌ์ถ•

๊ธฐ์ค€

  1. ์ ์€ ๋น„์šฉ
  2. ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌ(์ ์€ ์šด์˜ ๋ณต์žก๋„)

์žฅ์•  ํ—ˆ์šฉ ์ˆ˜์ค€

1์ฐจ ๋ฐฐํฌ์—์„œ๋Š” ์žฅ์• ๋กœ ์ธํ•ด ์„œ๋น„์Šค ์ œ๊ณต์ด ์ž ์‹œ ์ค‘๋‹จ๋˜์–ด๋„ ๊ดœ์ฐฎ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ์ดˆ๊ธฐ์— ๋ถ€ํŠธ์บ ํ”„ ๋‚ด๋ถ€์—์„œ๋งŒ ํ์‡„์ ์œผ๋กœ ๋ฐฐํฌ๋˜๋ฏ€๋กœ ์„œ๋น„์Šค๊ฐ€ ์ค‘๋‹จ๋˜๋”๋ผ๋„ ํฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

2) ์ดˆ๊ธฐ ์„œ๋น„์Šค ๊ทœ๋ชจ ์ •์˜

ํ•ญ๋ชฉ v1
ํƒ€๊ฒŸ ์œ ์ € ์นดํ…Œ๋ถ€ ๊ต์œก์ƒ (20๋Œ€ ์ค‘๋ฐ˜-30๋Œ€ ์ดˆ๋ฐ˜)
SOM(๋ชฉํ‘œ ์‹œ์žฅ) 120๋ช…
MAU 30๋ช…
DAU 30๋ช…
์„ธ์…˜ ์š”์ฒญ์ˆ˜ 21.23
์„ธ์…˜ ์ˆ˜ 30 (DAU์™€ ๋™์ผ)
ํ•˜๋ฃจ ์ด ์š”์ฒญ ์ˆ˜ 636.9
ํŠธ๋ž˜ํ”ฝ ์ง‘์ค‘ ์‹œ๊ฐ„ 140๋ถ„ = 8,400์ดˆ
ํ‰๊ท  RPS 0.076
ํŠธ๋ž˜ํ”ฝ/์ž์› ์‚ฌ์šฉ๋Ÿ‰ ํฐ ๊ธฐ๋Šฅ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ, ๋ฌผํ’ˆ ๋“ฑ๋ก ๊ฒ€์ฆ ์‹œ AI API ํ˜ธ์ถœ

ํŠธ๋ž˜ํ”ฝ ์ง‘์ค‘ ์‹œ๊ฐ„: ํ‰์ผ ์‰ฌ๋Š” ์‹œ๊ฐ„(9:00โ€“18:00 ์ค‘ ๋งค์‹œ 50๋ถ„โ€“์ •๊ฐ, 9ํšŒร—10๋ถ„=90๋ถ„) + ์ ์‹ฌ์‹œ๊ฐ„(12:00โ€“13:00, 60๋ถ„) = 140๋ถ„ = 8,400์ดˆ

v1์€ ์‚ฌ์šฉ์ž ์ˆ˜๊ฐ€ ์ ๊ณ  ์‚ฌ์šฉ ์‹œ๊ฐ„๋Œ€๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์–ด ํ”ผํฌ RPS๋ณด๋‹ค ํ‰๊ท  RPS๊ฐ€ ๋” ์œ ์˜๋ฏธํ•˜๋‹ค. ํ‰๊ท  RPS = ํ•˜๋ฃจ ์ด ์š”์ฒญ ์ˆ˜ รท ํŠธ๋ž˜ํ”ฝ ์ง‘์ค‘ ์‹œ๊ฐ„(์ดˆ)

1์ฐจ ํ•ต์‹ฌ User Flow

์‹ ๊ทœ ์œ ์ € Flow (DAU์˜ 70%)

๋ฒˆํ˜ธ User Flow ์š”์ฒญ ์š”์ฒญ ์ˆ˜
1 ๋””์Šค์ฝ”๋“œ ์ง„์ž… โ†’ ์นด์นด์˜ค ๋กœ๊ทธ์ธ โ†’ ์ทจํ–ฅ ์„ค์ • โ†’ ๊ทธ๋ฃน ํƒ์ƒ‰ โ†’ ๊ทธ๋ฃน ๊ฐ€์ž… ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ 1, ์ธ์ฆ ์ฝœ๋ฐฑ 1, ๊ฑฐ๋ž˜ ์ทจํ–ฅ ์„ค์ • 1, ๊ทธ๋ฃน ํƒ์ƒ‰ 1, ๊ทธ๋ฃน ์ฐธ๊ฐ€ 1 5
2 ๋ฌผ๊ฑด ๋“ฑ๋ก ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ 1, ๋“ฑ๋ก ์ €์žฅ 1 2
3 ๊ทธ๋ฃน ๋‚ด ๋ฌผ๊ฑด ํƒ์ƒ‰ โ†’ ์ƒ์„ธ ํ™•์ธ ํ™ˆ ๋ชฉ๋ก ์กฐํšŒ 1, ๋ฌผ๊ฑด ์ƒ์„ธ ์กฐํšŒ 5 6
4 ๊ตํ™˜ ์š”์ฒญ โ†’ ์ฑ„ํŒ… ์กฐ์œจ โ†’ ์ง๊ฑฐ๋ž˜ ๋‚ด ๋ฌผ๊ฑด ์„ ํƒ ์กฐํšŒ 1, ๊ตํ™˜์š”์ฒญ ์ƒ์„ฑ 1, ์ฑ„ํŒ…๋ฐฉ ์ƒ์„ฑ 1, ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ 1, ๋ฉ”์‹œ์ง€ ์™•๋ณต 7 11
5 ๊ตํ™˜ ์™„๋ฃŒ ์ฒ˜๋ฆฌ ์™„๋ฃŒ ์ฒ˜๋ฆฌ 1, ์ƒ์„ธ ์žฌ์กฐํšŒ 1 2
ํ•ฉ๊ณ„ 26

์žฌ๋ฐฉ๋ฌธ ์œ ์ € Flow (DAU์˜ 30%)

๋ฒˆํ˜ธ User Flow ์š”์ฒญ ์ˆ˜ ํ™•๋ฅ  ๊ฐ€์ค‘ ์š”์ฒญ ์ˆ˜
1 ์นด์นด์˜ค ๋กœ๊ทธ์ธ โ†’ ๋ชฉ๋ก ํƒ์ƒ‰ 3 100% 3.0
2 ๋ฌผ๊ฑด ๋“ฑ๋ก 2 10% 0.2
3 ๋ฌผ๊ฑด ์ƒ์„ธ ํ™•์ธ 6 50% 3.0
4 ๊ตํ™˜ ์š”์ฒญ โ†’ ์ฑ„ํŒ… ์กฐ์œจ โ†’ ์ง๊ฑฐ๋ž˜ 11 30% 3.3
5 ๊ตํ™˜ ์™„๋ฃŒ ์ฒ˜๋ฆฌ 2 30% 0.6
ํ•ฉ๊ณ„ 10.1

์„ธ์…˜ ํ‰๊ท  ์š”์ฒญ์ˆ˜

๊ตฌ๋ถ„ ๋น„์œจ ์š”์ฒญ ์ˆ˜
์‹ ๊ทœ 70% 26
์žฌ๋ฐฉ๋ฌธ 30% 10.1
๊ฐ€์ค‘ ํ‰๊ท  100% 21.23

ํ‰๊ท  RPS ๊ณ„์‚ฐ

ํ•˜๋ฃจ ์ด ์š”์ฒญ ์ˆ˜ = ์„ธ์…˜ ์ˆ˜ ร— ์„ธ์…˜ ํ‰๊ท  ์š”์ฒญ ์ˆ˜ = 30 ร— 21.23 = 636.9
ํ‰๊ท  RPS       = 636.9 รท 8,400 โ‰ˆ 0.076

Peak RPS ๋Œ€์‹  ํ‰๊ท  RPS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  1. ์‚ฌ์šฉ์ž ๊ทœ๋ชจ(DAU 30๋ช…): ์†Œ๊ทœ๋ชจ ์ดˆ๊ธฐ ์„œ๋น„์Šค์—์„œ 30๋ช… ์ „์›์ด ํŠน์ • ์ˆœ๊ฐ„์— ์ผ์ œํžˆ ์„ธ์…˜ ์š”์ฒญ์„ ์Ÿ์•„๋ถ“๋Š” ๊ทน๋‹จ์ ์ธ ํ”ผํฌ ์ƒํ™ฉ์€ ๋น„ํ˜„์‹ค์ 
  2. ๋ช…ํ™•ํ•œ ํ™œ๋™ ์‹œ๊ฐ„๋Œ€: 24์‹œ๊ฐ„ ์ƒ์‹œ ์ ‘์† ์„œ๋น„์Šค๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ, ์‹ค์ œ ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ์ฃผ์š” ํ™œ๋™ ์‹œ๊ฐ„๋Œ€(์‰ฌ๋Š” ์‹œ๊ฐ„ + ์ ์‹ฌ์‹œ๊ฐ„ = 140๋ถ„ / 8,400์ดˆ)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ฐ์ •ํ•ด์•ผ ํ˜„์‹ค์ ์ธ ์ดˆ๋‹น ๋ถ€ํ•˜๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Œ
  3. ๊ฒฐ๋ก : v1 ์ธํ”„๋ผ ์‚ฌ์ด์ง•์˜ ํ˜„์‹ค์ ์ธ ๊ธฐ์ค€์€ ๋น„ํ˜„์‹ค์ ์ธ ์ˆœ๊ฐ„ ํ”ผํฌ๊ฐ€ ์•„๋‹Œ, ์ง‘์ค‘ ์‹œ๊ฐ„๋Œ€ ํ‰๊ท  RPS

2. ๋ฐฐํฌ ๋ฐฉ์‹

1์ฐจ ๋ฐฐํฌ์˜ ๋ชฉํ‘œ: ์ ์€ ๋น„์šฉ๊ณผ ๋น ๋ฅธ ๋ฐฐํฌ

1) ์˜จํ”„๋ ˆ๋ฏธ์Šค vs ํด๋ผ์šฐ๋“œ

์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด 1์ฐจ ๋ฐฐํฌํ•œ๋‹ค.

  • ๋ฐฐํฌ๋Š” ํฌ๊ฒŒ ์˜จ ํ”„๋ ˆ๋ฏธ์Šค์™€ ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๋กœ ๋‚˜๋‰œ๋‹ค.
  1. ์˜จ ํ”„๋ ˆ๋ฏธ์Šค๋Š” ์ดˆ๊ธฐ ํˆฌ์ž ๋น„์šฉ์ด ๋†’์œผ๋ฏ€๋กœ([N100 ๋ฏธ๋‹ˆ PC 22๋งŒ์›]((https://prod.danawa.com/info/?pcode=35770394&keyword=n100), ๊ณต์œ ๊ธฐ 4.5๋งŒ์› , UPS 18๋งŒ์›๋ฅผ ํ•ฉ์ณ 44.5๋งŒ์›) ํ˜„์žฌ ์ƒํ™ฉ์— ์ ํ•ฉํ•˜์ง€ ์•Š์•˜๋‹ค.
  2. ํด๋ผ์šฐ๋“œ๋Š” ์˜จ ํ”„๋ ˆ๋ฏธ์Šค ๋Œ€๋น„ ์ ์€ ๋น„์šฉ(AWS EC2 t3.medium ๊ธฐ์ค€ ํ•œ ๋‹ฌ ์•ฝ 38๋‹ฌ๋Ÿฌ)์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ดˆ๊ธฐ ๋น„์šฉ์„ ์ค„์—ฌ์•ผ ํ•˜๋Š” ์ง€๊ธˆ ์ƒํ™ฉ์— ์ž˜ ๋งž์•˜๋‹ค.
  • ๋”ฐ๋ผ์„œ 1์ฐจ ๋ฐฐํฌ๋Š” ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

2) CSP

๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ, CSP ๊ฐ„ ๋น„์šฉ ์ฐจ์ด๊ฐ€ ํฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด ํŒ€์›์˜ ๊ฒฝํ—˜์ด ์žˆ๋Š” AWS๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐฐํฌํ•œ๋‹ค.

  1. ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” DAU๊ฐ€ 30๋ช…์ด๋ฏ€๋กœ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ์„ ์‚ฌ์šฉํ•  ๋•Œ์˜ CSP ๊ฐ„์˜ ๋น„์šฉ ์ฐจ์ด๊ฐ€ ์ ์—ˆ๋‹ค.(ํ•˜๋‹จ ๋น„์šฉ ํ‘œ ์ฐธ์กฐ) ๋”ฐ๋ผ์„œ '์ ์€ ๋น„์šฉ'์ด๋ผ๋Š” ๊ธฐ์ค€์—์„œ๋Š” CSP๋ฅผ ๊ณ ๋ฅผ ์ˆ˜ ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
  2. '๋น ๋ฅธ ๋ฐฐํฌ' ๊ธฐ์ค€์—์„œ๋Š” ์ ์œ ์œจ์ด ๋†’์•„ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋งŽ๊ณ  ํŒ€์› ๋ชจ๋‘์˜ ์‚ฌ์šฉ ๊ฒฝํ—˜์ด ์žˆ๋Š” AWS๊ฐ€ ์šฐ์ˆ˜ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ CSP๋กœ AWS๋ฅผ ์„ ํƒํ–ˆ๋‹ค.

๋Œ€ํ‘œ ํด๋ผ์šฐ๋“œ 3์‚ฌ t3.medium ๊ธ‰ ์ธ์Šคํ„ด์Šค ๋น„๊ต

  • ๋Œ€์ƒ: ๋Œ€ํ‘œ CSP 3์‚ฌ(AWS, Azure, GCP)
  • ๊ธฐ์ค€ ์กฐ๊ฑด: Ubuntu, ์˜จ๋””๋งจ๋“œ(Pay-As-You-Go), ํ•œ ๋‹ฌ 730์‹œ๊ฐ„ ๊ฐ€๋™, ์ˆœ์ˆ˜ ์ปดํ“จํŒ… ์ธ์Šคํ„ด์Šค ๋น„์šฉ๋งŒ ๊ณ ๋ ค
ํด๋ผ์šฐ๋“œ (CSP) ์ธ์Šคํ„ด์Šค ์œ ํ˜• CPU / RAM ์‚ฌ์–‘ ์‹œ๊ฐ„๋‹น ์š”๊ธˆ (USD) ํ•œ ๋‹ฌ ์š”๊ธˆ (USD) ํŠน์ง• ๋ฐ CPU ๋ฐฉ์‹
AWS t3.medium 2 vCPU / 4 GiB $0.052 $37.96 โ€ข ๋ฒ„์Šคํ„ฐ๋ธ”(Burstable) ์ธ์Šคํ„ด์Šค
โ€ข ๊ธฐ๋ณธ ์„ฑ๋Šฅ ๊ธฐ์ค€ ์ดˆ๊ณผ ์‹œ ํฌ๋ ˆ๋”ง ์†Œ๋ชจ
Azure B2ls v2 2 vCPU / 4 GiB $0.052 $37.96 โ€ข ํฌ๋ ˆ๋”ง ๊ธฐ๋ฐ˜ ๋ฒ„์ŠคํŠธ ์ง€์›
GCP e2-medium 2 vCPU / 4 GB $0.04298286 $31.38 โ€ข E2 ๊ณต์œ  ์ฝ”์–ด ๋ฐฉ์‹
โ€ข ๊ธฐ๋ณธ ๋ถ€ํ•˜๊ฐ€ ์ ์€ ์›น/API ์„œ๋ฒ„์— ์ตœ์ ํ™”
โ€ข ์›”์ •์•ก์„ ๊ฐ€์ž…ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ์‚ฌ์šฉ ๊ธฐ๊ฐ„์— ๋Œ€ํ•ด ํ• ์ธ ์ ์šฉ

๋ฒ„์Šคํ„ฐ๋ธ” ํด๋ž˜์Šค์˜ ํฌ๋ ˆ๋”ง ๊ฐœ๋…

  • t3.medium, Standard_B2s๋Š” ํฌ๋ ˆ๋”ง ๊ธฐ๋ฐ˜์ด๋ผ ํฌ๋ ˆ๋”ง์ด ์—†์œผ๋ฉด baseline ์„ฑ๋Šฅ์œผ๋กœ ๋Œ์•„๊ฐ€๊ฑฐ๋‚˜ ํฌ๋ ˆ๋”ง์„ ์‚ฌ์•ผ ํ•œ๋‹ค.(aws t3 unlimited์˜ ๊ฒฝ์šฐ)
  • e2-medium์€ ๊ณต์œ  ์ฝ”์–ด ๋ฐฉ์‹์ด๋ผ ๋ฌผ๋ฆฌ ์„œ๋ฒ„์— ์—ฌ์œ ๊ฐ€ ์žˆ๋Š” ํ•œ, ๊ณ„์†ํ•ด์„œ ๋ฒ„์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ํŠน์ • ์‹œ๊ฐ„๋Œ€์— ์‚ฌ์šฉ๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ ํฌ๋ ˆ๋”ง์€ ์ค‘์š”ํ•œ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

3) ์ธ์Šคํ„ด์Šค ๊ฐœ์ˆ˜

์ดˆ๊ธฐ ๋ฐฐํฌ์—์„œ๋Š” ๋‹จ์ผ EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๊ฒฝ์ œ์ ์ด๊ณ  ๋‹จ์ˆœํ•˜๋‹ค.

  • ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ์ ์€ ์ˆ˜์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉ(ํ‰๊ท  RPS 0.076)ํ•˜๋ฏ€๋กœ ์ ์€ ๋น„์šฉ์œผ๋กœ ๋ฐฐํฌํ•ด์•ผ ํ•˜๊ณ , ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค์— ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

3. ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ๋ฐฐํฌ

1) ์ปจํ…Œ์ด๋„ˆํ™”

1๋Œ€์˜ EC2 ์•ˆ์— ์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ๋™ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•˜๋Š” ํ™˜๊ฒฝ ์ถฉ๋Œ๊ณผ ์žฅ์•  ์ „ํŒŒ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ปจํ…Œ์ด๋„ˆ ๊ฐ€์ƒํ™” ๊ธฐ์ˆ ๋กœ Docker๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๋‹จ์ผ EC2 ์ธ์Šคํ„ด์Šค ์œ„์— ๋ฐฑ์—”๋“œ(Java/Spring), AI(Python/FastAPI), ํ”„๋ก ํŠธ์—”๋“œ(React), ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(PostgreSQL), ์บ์‹œ(Redis) ๋“ฑ ๋‹ค์–‘ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋™์‹œ์— ๋ฐฐํฌํ•˜๊ณ  ์šด์˜ํ•ด์•ผ ํ•œ๋‹ค.
  • ์—ฌ๋Ÿฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ง์ ‘ ์„ค์น˜ํ•˜์—ฌ ๋ฐฐํฌํ•˜๋Š” ๊ฒฝ์šฐ ํ™˜๊ฒฝ ์ถฉ๋Œ์ด๋‚˜ ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์žฅ์• ๊ฐ€ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ปจํ…Œ์ด๋„ˆ ๊ธฐ์ˆ  Docker๋ฅผ ๋„์ž…ํ•œ๋‹ค.

2) ์ปดํฌ๋„ŒํŠธ๋ณ„ ๋ฐฐํฌ ๋ฐฉ์‹

  • ํ”„๋ก ํŠธ์—”๋“œ
FE ๋ฐฐํฌ ๋ฐฉ์‹

EC2 Nginx

  • ํŠน์ง•
    • EC2 ๋””์Šคํฌ์— ์ €์žฅ๋œ ๋นŒ๋“œ ํŒŒ์ผ์„ Nginx ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฝ์–ด์„œ HTTP ์‘๋‹ต
    • ๋น„์šฉ: EC2 ์ธ์Šคํ„ด์Šค ๋น„์šฉ(์œ ๋ฃŒ ์ž์› ์†Œ๋ชจ)
    • ์บ์‹ฑ ๋ฐ ์†๋„: EC2 ๋ฆฌ์ „์— ์˜์กด
    • SSL/TLS ์ธ์ฆ์„œ: ์ธ์ฆ์„œ๋ฅผ Nginx์— ์ง์ ‘ ์„ธํŒ…ํ•˜๊ณ  ์ฃผ๊ธฐ์  ๊ฐฑ์‹  ํ•„์š”
    • CI/CD: EC2๋กœ ๋นŒ๋“œ ํŒŒ์ผ ์ „์†ก
    • SSR: Node.js ์ปจํ…Œ์ด๋„ˆ ์ถ”๊ฐ€
  • ๋‹จ์ 
    • ๋ฐฐํฌ ์‹œ EC2์— ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ์ž‘์—… ํ•„์š”
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด FE ์ •์  ํŒŒ์ผ ์ „์†ก์ด EC2 ์ธ์Šคํ„ด์Šค์˜ ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์„ ์‚ฌ์šฉํ•ด์„œ BE API ์‘๋‹ต ์†๋„์— ์•…์˜ํ–ฅ
    • ์ธ์Šคํ„ด์Šค์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด FE ํ™”๋ฉด๋„ ๋…ธ์ถœ๋˜์ง€ ์•Š์•„ ์ ๊ฒ€ ์ค‘ ์•ˆ๋‚ด ๋“ฑ ๋ถˆ๊ฐ€

AWS S3 + CloudFront

  • ํŠน์ง•
    • S3 ๋ฒ„ํ‚ท์— ๋นŒ๋“œ ํŒŒ์ผ ์—…๋กœ๋“œ ํ›„ CloudFront ์—ฃ์ง€ ์บ์‹ฑ
    • ๋น„์šฉ: ๊ฑฐ์˜ ๋ฌด๋ฃŒ(ํ”„๋ฆฌํ‹ฐ์–ด 1TB/์›”)
    • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ ๋กœ์ผ€์ด์…˜ ์บ์‹ฑ, ๋น ๋ฆ„
    • SSL/TLS ์ธ์ฆ์„œ: AWS ACM์œผ๋กœ ๊ด€๋ฆฌ(์ž๋™ ๊ฐฑ์‹ )
    • CI/CD: S3๋กœ sync ํ›„ ์บ์‹œ ๋ฌดํšจํ™”
    • SSR: Lambda ์ถ”๊ฐ€
  • ๋‹จ์ 
    • ๋ฐฐํฌ ์‹œ S3์— ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ์ž‘์—… ํ•„์š”
    • S3 ๋ฒ„ํ‚ท ์ •์ฑ… ๊ด€๋ฆฌ

Vercel

  • ํŠน์ง•
    • Git Push ์‹œ ์ž๋™ ๋นŒ๋“œ ๋ฐ ๊ธ€๋กœ๋ฒŒ Edge Network ๋ฐฐํฌ
    • ๋น„์šฉ: Hobby(๊ฐœ์ธ/ํ•™์Šต์šฉ) ํ‹ฐ์–ด 100GB/์›” ๋ฌด๋ฃŒ, ์ƒ์—…์  ์ด์šฉ ์‹œ ์œ ๋ฃŒ
    • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ ์—ฃ์ง€ ๋„คํŠธ์›Œํฌ ๊ธฐ๋ณธ ํƒ‘์žฌ, ๋น ๋ฆ„
    • SSL/TLS ์ธ์ฆ์„œ: ๋„๋ฉ”์ธ ์—ฐ๊ฒฐ ์‹œ ์ž๋™ ๋ฐœ๊ธ‰ ๋ฐ ๊ฐฑ์‹ 
    • CI/CD: GitHub ๋ ˆํฌ ์—ฐ๋™ํ•˜๋ฉด ๋
    • SSR: ์ง€์›
  • ๋‹จ์ 
    • ์ƒ์—…์  ํ”„๋กœ์ ํŠธ๋‚˜ ํŒ€ ํ”„๋กœ์ ํŠธ(GitHub Organization)๋Š” ๋น„์šฉ ๋ฐœ์ƒ
    • ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB ์ดˆ๊ณผ ์‹œ ์„œ๋น„์Šค ์ผ์‹œ ์ •์ง€

Github Pages

  • ํŠน์ง•
    • GitHub ๋ ˆํฌ์˜ ํŠน์ • ๋ธŒ๋žœ์น˜/์ •์  ํŒŒ์ผ ํ˜ธ์ŠคํŒ…
    • ๋น„์šฉ: 100GB/์›” ๋ฌด๋ฃŒ
    • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ CDN์ด ํƒ‘์žฌ๋˜์–ด ์žˆ์œผ๋‚˜ ์„ค์ • ์ œ์•ฝ์ด ์žˆ์–ด ๋ณดํ†ต ์ˆ˜์ค€
    • SSL/TLS ์ธ์ฆ์„œ: ์ž๋™ ๋ฐœ๊ธ‰ ๋ฐ ๊ฐฑ์‹ 
    • CI/CD: GitHub Actions ํ…œํ”Œ๋ฆฟ ์ œ๊ณต
    • SSR: ๋ฏธ์ง€์›
  • ๋‹จ์ 
    • SPA ๋ผ์šฐํŒ… ์ฃผ์†Œ๋กœ ์ง์ ‘ ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ 404 ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„ํ—˜์œผ๋กœ ์šฐํšŒ ์„ค์ • ํ•„์š”
    • ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB ์ดˆ๊ณผ ์‹œ ์„œ๋น„์Šค ์ผ์‹œ ์ •์ง€
    • ์ €์žฅ์†Œ๊ฐ€ public์ด์–ด์•ผ ํ•˜๋ฉฐ private ์ €์žฅ์†Œ ๋ฐฐํฌ๋Š” ์œ ๋ฃŒ ํ”Œ๋žœ ํ•„์š”
  1. ์ง€๊ธˆ 1์ฐจ ๋ฐฐํฌ์—์„œ๋Š” ์œ ์ €๊ฐ€ 30~100๋ช…์ด๋ผ ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB์— ๋ฏธ์น˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ์— ํ† ํฐ ํ•œ๋„๋Š” ๊ณ ๋ คํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  2. ์ €์žฅ์†Œ๋Š” public์ด์ง€๋งŒ, ํŒ€ ํ”„๋กœ์ ํŠธ(organization)์ด๊ธฐ ๋•Œ๋ฌธ์— vercel์€ ์ถ”๊ฐ€ ๋น„์šฉ(ํŒ€์› ํ•œ ๋ช…๋‹น 20$/์›”)์ด ๋ฐœ์ƒํ•˜๊ธฐ์— ํƒˆ๋ฝ์ด๋‹ค.
  3. GitHub Pages๋Š” 404 ์šฐํšŒ ์„ค์ •์ด ํ•„์š”ํ•œ ์ ์—์„œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š”๋ฐ, ํŽธ์˜์„ฑ๋„ ์žˆ์ง€๋งŒ ๋ณต์žก๋„๊ฐ€ ๋” ํด ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ•ด์„œ ํƒˆ๋ฝ์ด๋‹ค.
  4. CloudFront๋Š” ์ตœ๋Œ€ ์žฅ์ ์ด ๊ธ€๋กœ๋ฒŒ ์บ์‹ฑ์ธ๋ฐ, [๋ฐ”๊พธ๊นŒ]๋Š” ๊ตญ๋‚ด ๊ฑฐ์ฃผ์ž๋งŒ ์‚ฌ์šฉํ•  ์„œ๋น„์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š” ์—†๋‹ค.
  5. ๋”ฐ๋ผ์„œ CloudFront์™€ Nginx๋Š” ํ˜„์žฌ ๊ทœ๋ชจ์—์„œ ํฌ๊ฒŒ ์ฐจ์ด๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
  6. ๋‹ค๋งŒ, S3 ๋ฒ„ํ‚ท ์ •์ฑ…์„ FE ๋ฐฐํฌ๋งŒ์„ ์œ„ํ•ด ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” Docker Compose๋กœ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ๋“ค๊ณผ ๋ฌถ์–ด์„œ ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ๋” ํŽธ๋ฆฌํ•˜๊ณ  ํ•œ๋ˆˆ์— ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šธ ๊ฒƒ์ด๋‹ค.
  7. ๊ฒฐ๋ก ์ ์œผ๋กœ Nginx ์„ ์ •
  • ๋ฐฑ์—”๋“œ & AI ์„œ๋ฒ„
    • Java์™€ Python์˜ ์„œ๋กœ ๋‹ค๋ฅธ ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๋ฉฐ, ์™ธ๋ถ€ LLM์„ ํ˜ธ์ถœํ•˜๋Š” AI ์„œ๋ฒ„์˜ ๋ณ‘๋ชฉ์ด ๋ฐฑ์—”๋“œ๋กœ ์ „ํŒŒ๋˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
    • [๊ฒฐ๋ก ] ๋”ฐ๋ผ์„œ ๋ฐฑ์—”๋“œ์™€ AI ์„œ๋ฒ„๋Š” ๊ฐ๊ฐ ๋…๋ฆฝ๋œ Docker ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ฐฐํฌํ•œ๋‹ค.
  • DB(MySQL, MongoDB, Redis)
    • ์ปจํ…Œ์ด๋„ˆ๋Š” ์ข…๋ฃŒ ์‹œ ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์†Œ๋ฉธ๋œ๋‹ค. ๊ด€๋ฆฌํ˜• ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€ ๋น„์šฉ์ด ๋ฐœ์ƒ๋œ๋‹ค.(RDS ์›” $21)
    • [๊ฒฐ๋ก ] ๋น„์šฉ ์ ˆ๊ฐ์„ ์œ„ํ•ด DB์™€ Redis๋ฅผ ๊ฐ๊ฐ ์ปจํ…Œ์ด๋„ˆ๋กœ ์ง์ ‘ ๋„์šฐ๋˜, ์ธ์Šคํ„ด์Šค์˜ ๋””์Šคํฌ(EBS)์™€ ๋ณผ๋ฅจ์„ ์—ฐ๊ฒฐํ•ด ๋ฐ์ดํ„ฐ ์˜์†์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

3) ์ปจํ…Œ์ด๋„ˆ ์ž์› ํ• ๋‹น

  • DAU๊ฐ€ 30์ด๊ธฐ์— ์ผ๋ฐ˜์ ์ธ ์šด์˜ ํ™˜๊ฒฝ ๊ธฐ์ค€์ด ์•„๋‹Œ, ๊ธฐ๋ณธ ๊ตฌ๋™์ด ๊ฐ€๋Šฅํ•œ ์ˆ˜์ค€์œผ๋กœ ๋น„์šฉ ์ตœ์ ํ™”
  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ จ ์„ค์ •๊ฐ’(JVM ํž™, ์ตœ๋Œ€ Connection ๋“ฑ) ํŠœ๋‹ ํ•„์š”

์ปจํ…Œ์ด๋„ˆ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ƒ์„ธ

  • Nginx: 50MB

    • ์›Œ์ปค ํ”„๋กœ์„ธ์Šค 1๊ฐœ ๋ฉ”๋ชจ๋ฆฌ 10MB ๋‚ด์™ธ ์˜ˆ์ƒ
  • Redis: 128MB

    • ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ ํฌ๊ธฐ 10MB ๋‚ด์™ธ ์˜ˆ์ƒ
    • BE์˜ Redis ํ™œ์šฉ ๋ฒ”์œ„๊ฐ€ ๋ฏธ์ •์ธ ๊ด€๊ณ„๋กœ ์—ฌ์œ  ์žˆ๊ฒŒ ์‚ฐ์ •
  • MySQL: 256MB

    • BufferPool ์ถ•์†Œ, ์ตœ๋Œ€ Connection ์ œํ•œ ๋“ฑ ์„ค์ •์œผ๋กœ 220MB ์„ ์—์„œ ๋ถ€ํŒ… ์˜ˆ์ƒ
  • MongoDB: 512MB

    • WiredTiger ์—”์ง„์˜ ๋‚ด๋ถ€ ์บ์‹œ ํ•˜ํ•œ์„ ์ด 216MB
    • ์บ์‹œ ์™ธ์— ์—”์ง„ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ์Šคํƒ, ์—ฐ๊ฒฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์‹ค์ œ ์•ฝ 500MB๊นŒ์ง€ ์ ์œ 
    • ๊ฐ•์ œ๋กœ ์ถ•์†Œํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ฆฌ๋ˆ…์Šค Swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •์„ ๊ณ ๋ คํ•˜๋Š” ๋“ฑ OOM Kill์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ธฐ์— ์•ˆ์ •์ ์œผ๋กœ ์ตœ์†Œ 512MB ํ™•๋ณด
  • FastAPI: 512MB

    • ์™ธ๋ถ€ API ํ˜ธ์ถœ ์ „์šฉ ์„œ๋ฒ„์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋ถ„ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ
  • SpringBoot: 1GB

    • JVM ํž™, metaspace, TreadPool ๋“ฑ ์ถ•์†Œ ์‹œ ์ถฉ๋ถ„ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ

4) EC2 ์ธ์Šคํ„ด์Šค ์‚ฌ์–‘

๊ฐ ์ปจํ…Œ์ด๋„ˆ์˜ ์ตœ์†Œ ๋ฉ”๋ชจ๋ฆฌ์˜ ํ•ฉ์€ ์•ฝ 2.5GB์ด๊ณ , OS ์ปค๋„๊ณผ Docker ๋ฐ๋ชฌ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๊ณ ๋ คํ•˜๋ฉด 3GB๊ฐ€ ์กฐ๊ธˆ ์•ˆ ๋  ๊ฑฐ ๊ฐ™๋‹ค.

SpringBoot, FastAPI ์ปจํ…Œ์ด๋„ˆ์˜ ์‚ฌ์šฉ๋Ÿ‰์„ ๋„‰๋„‰ํ•˜๊ฒŒ ์‚ฐ์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Swap ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด 2GiB RAM์œผ๋กœ๋„ ๊ตฌ๋™์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ด๋‹ค.

  • t4g.small, t3a.small, t3.small

Swap ์„ค์ • ์—†์ด ๋„‰๋„‰ํžˆ ์šด์˜ํ•˜๋ ค๋ฉด 4GiB RAM์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • t4g.medium, t3a.medium, t3.medium

๊ฐ ์‚ฌ์–‘์€ ์นฉ์…‹ ์•„ํ‚คํ…์ฒ˜์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.(ARM, AMD x86, Intel x86) ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ARM์—์„œ๋„ ์ž‘๋™ํ•˜๋ฏ€๋กœ ๊ตณ์ด x86 ํ”„๋กœ์„ธ์„œ๋ฅผ ๊ณ ์ˆ˜ํ•˜๋ฉฐ ๋น„์šฉ์„ ๋” ์ง€๋ถˆํ•  ์ด์œ ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์–‘์€ ๋™์ผํ•˜๋‚˜ ๋น„์šฉ์ด ๋” ์ €๋ ดํ•œ t4g.medium์„ ์‚ฌ์šฉํ•œ๋‹ค.

EC2 ๋น„์šฉํ‘œ(์„œ์šธ ๋ฆฌ์ „, ์›” ์š”๊ธˆ)

์œ ํ˜• small medium ํŠน์ง•
t4g 15.18$ 30.37$ ARM ํ”„๋กœ์„ธ์„œ๋กœ, x86 ์ „์šฉ C-extension ๋ชจ๋“ˆ ์‚ฌ์šฉ ๋ถˆ๊ฐ€
t3a 17.08$ 34.16$ x86 ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, t3๋ณด๋‹ค 10% ์ €๋ ด
t3 18.98$ 37.96$ Intel CPU์— ์ตœ์ ํ™”๋œ ์—ฐ์‚ฐ ๋ชจ๋“ˆ์ด ํ•„์š”ํ•œ ๊ฒŒ ์•„๋‹ˆ๋ฉด ๊ฐ€์„ฑ๋น„๊ฐ€ ์•ˆ ์ข‹์•„์„œ ๊ตณ์ด ์„ ํƒํ•  ์ด์œ  ์—†์Œ

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

  1. t4g.medium์ด swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •์ด๋‚˜ ์ปจํ…Œ์ด๋„ˆ ํŠœ๋‹์ด ํฌ๊ฒŒ ํ•„์š”ํ•˜์ง€ ์•Š์•„์„œ ์šฐ์„ ์ ์œผ๋กœ ์„ ํƒํ•œ๋‹ค.
  2. ํŒ€์›๋“ค์ด medium์—์„œ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ, ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ์„ ์œ„ํ•ด ํŠœ๋‹ํ•œ ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ๊ฐ€ small ์‚ฌ์–‘์—์„œ ๋ฌด๋ฆฌ ์—†์ด ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  3. CI/CD ์‹œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ•  ๊ฒฝ์šฐ ์ผ์‹œ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ 2๋ฐฐ๋กœ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”์‹œ ์ด ๋ถ€๋ถ„๋„ ํ™•์ธํ•œ๋‹ค.
  4. ๊ฒ€์ฆ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ์นœ ๊ฒฝ์šฐ t4g.medium์—์„œ t4g.small๋กœ ์ „ํ™˜ํ•œ๋‹ค.

5) ๋‹ค์ค‘ ์ปจํ…Œ์ด๋„ˆ ๊ด€๋ฆฌ

์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ์ผ๊ด„ ์ œ์–ดํ•˜๊ณ  ํ™˜๊ฒฝ ์„ค์ • ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Docker Compose๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๊ฐœ๋ณ„ `docker run`์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ํฌํŠธ/๋ณผ๋ฅจ/ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋“ฑ ๋ณต์žกํ•œ CLI ์˜ต์…˜ ๋ˆ„๋ฝ์œผ๋กœ ํœด๋จผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๊ณ , DB๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „ ๋ฐฑ์—”๋“œ๊ฐ€ ๋จผ์ € ์‹œ์ž‘๋˜๋Š” ๋“ฑ์˜ ์ปจํ…Œ์ด๋„ˆ ๊ฐ„ ์‹คํ–‰ ์ˆœ์„œ์™€ ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ์ผ์ผ์ด ์ˆ˜๋™ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.
  • Docker Compose๋ฅผ ํ†ตํ•ด ๋‹จ์ผ `yaml` ํŒŒ์ผ๋กœ ์ „์ฒด ์„œ๋น„์Šค์˜ ์„ค์ •๊ณผ ์˜์กด ๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•˜์—ฌ ๋ช…๋ น์–ด ํ•œ ์ค„๋กœ ์ผ๊ด„ ์‹คํ–‰ ๋ฐ ์žฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Docker Compose ๊ตฌ์„ฑ

EBS ๋งˆ์šดํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ

EBS ๋งˆ์šดํŠธ ๊ฒฝ๋กœ ์•„๋ž˜ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ

sudo mkdir -p /data/mysql /data/mongodb /data/nginx/conf.d /data/nginx/certbot /data/frontend

์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ UID์— ๋งž์ถ˜ ๊ถŒํ•œ ์„ค์ •

sudo chown -R 999:999 /data/mysql
sudo chown -R 999:999 /data/mongodb
sudo chmod 755 /data/frontend
docker-compose.yml
services:
  # ๋ฉ”์ธ DB
  mysql:
    image: mysql:8.4
    container_name: mysql
    restart: unless-stopped
    # dirty page ๋””์Šคํฌ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ์œ„ํ•œ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ํ™•๋ณด
    stop_grace_period: 1m
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      TZ: Asia/Seoul
    # 256MB ํ™˜๊ฒฝ ๊ตฌ๋™์„ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋‹ค์ด์–ดํŠธ ์˜ต์…˜ ์ „๋‹ฌ
    command:
      - --innodb-buffer-pool-size=128M
      - --performance-schema=OFF
      - --max-connections=30
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    volumes:
      - /data/mysql:/var/lib/mysql
    deploy:
      resources:
        limits:
          memory: 384M # 256MB ๋ชฉํ‘œ์ด๋‚˜ ๊ตฌ๋™ ์‹œ ๋ฒ„ํผ ๊ณ ๋ คํ•ด 384MB ํ•˜๋“œ ๋ฆฌ๋ฐ‹
    healthcheck:
      # ํฌํŠธ ์˜คํ”ˆ ์—ฌ๋ถ€๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ์ฟผ๋ฆฌ ์‘๋‹ต ์—ฌ๋ถ€ ๊ฒ€์ฆ
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    logging: &default-logging
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - backend-net

  # ์ฑ„ํŒ… DB
  mongodb:
    image: mongo:7.0
    container_name: mongodb
    restart: unless-stopped
    stop_grace_period: 30s
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      TZ: Asia/Seoul
    # WiredTiger ์บ์‹œ๋ฅผ 256MB๋กœ ๋ช…์‹œ์  ์ œํ•œํ•˜์—ฌ OOM Crash ๋ฐฉ์ง€
    command: ["mongod", "--wiredTigerCacheSizeGB", "0.25"]
    volumes:
      - /data/mongodb:/data/db
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "mongosh --eval 'db.adminCommand(\"ping\")' --quiet"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s
    logging: *default-logging
    networks:
      - backend-net

  # ์„ธ์…˜ ๊ด€๋ฆฌ ๋ฐ ์‘๋‹ต ์บ์‹ฑ
  redis:
    image: redis:7.2-alpine
    container_name: redis
    restart: unless-stopped
    command: >
      redis-server
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 96mb
      --maxmemory-policy allkeys-lru
    deploy:
      resources:
        limits:
          memory: 128M
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 5s
    logging: *default-logging
    networks:
      - backend-net

  # AI ์ค‘๊ณ„ ์„œ๋ฒ„
  fastapi:
    image: ${FASTAPI_IMAGE} # GitHub Actions์—์„œ ๋นŒ๋“œ/ํ‘ธ์‹œํ•œ ์ด๋ฏธ์ง€ ํƒœ๊ทธ
    container_name: fastapi
    restart: unless-stopped
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - CLAUDE_API_KEY=${CLAUDE_API_KEY}
      - TZ=Asia/Seoul
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging: *default-logging
    networks:
      - backend-net

  # ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง API ์„œ๋ฒ„
  springboot:
    image: ${SPRINGBOOT_IMAGE}
    container_name: springboot
    restart: unless-stopped
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useSSL=false&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=${MYSQL_USER}
      - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD}
      - SPRING_DATA_MONGODB_URI=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongodb:27017/${MONGO_DATABASE}?authSource=admin
      - SPRING_DATA_REDIS_HOST=redis
      - SPRING_DATA_REDIS_PORT=6379
      - SPRING_DATA_REDIS_PASSWORD=${REDIS_PASSWORD}
      - FASTAPI_BASE_URL=http://fastapi:8000
      - TZ=Asia/Seoul
      # JVM ํž™ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 512MB๋กœ ์ œํ•œํ•˜๊ณ  GC ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ์˜ต์…˜
      - JAVA_OPTS=-Xms256m -Xmx512m -XX:MaxMetaspaceSize=192m
    depends_on:
      mysql:
        condition: service_healthy # MySQL ํ•‘ ์‘๋‹ต ํ™•์ธ ํ›„ ๊ตฌ๋™ ์‹œ์ž‘
      mongodb:
        condition: service_healthy
      redis:
        condition: service_healthy
      fastapi:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 1024M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 40s # Spring Boot JVM ๋ถ€ํŒ… ์‹œ๊ฐ„์„ ๊ฐ์•ˆํ•œ ์œ ์˜ˆ์‹œ๊ฐ„
    logging: *default-logging
    networks:
      - backend-net
      - frontend-net

  # Reverse Proxy & Static Web Server
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /data/nginx/conf.d:/etc/nginx/conf.d:ro
      - /data/nginx/certbot:/etc/letsencrypt:ro
      - /data/frontend:/usr/share/nginx/html:ro # React ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ๋งˆ์šดํŠธ
    depends_on:
      springboot:
        condition: service_healthy # ๋ฐฑ์—”๋“œ ๊ธฐ๋™ ์™„๋ฃŒ ํ›„ ๋ผ์šฐํŒ… ์˜คํ”ˆ
    deploy:
      resources:
        limits:
          memory: 64M
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://localhost/ || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
    logging: *default-logging
    networks:
      - frontend-net

# ๋„คํŠธ์›Œํฌ ๋ถ„๋ฆฌ: DB/์บ์‹œ๋Š” ์™ธ๋ถ€์— ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์™„์ „ ๊ฒฉ๋ฆฌ
networks:
  backend-net:
    driver: bridge
  frontend-net:
    driver: bridge
Image

4. AWS ํ™˜๊ฒฝ ๋ฐฐํฌ ๋ฐ ์šด์˜

์„œ๋น„์Šค ์š”์•ฝ
- ์Šคํƒ€ํŠธ์—…์ด ์ตœ์ดˆ๋กœ ๋ฐฐํฌํ•˜๋Š” ์„œ๋น„์Šค, DAU 30, ํ‰๊ท  RPS 0.07, ๋ถ€ํŠธ์บ ํ”„ ๋‚ด๋ถ€ ์ธ์›์„ ํƒ€๊ฒŸ์œผ๋กœ ๋ฐฐํฌ ์‹œ์ž‘
- AWS EC2 ํ•œ ๋Œ€์— ๊ฐ ์„œ๋น„์Šค ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ปจํ…Œ์ด๋„ˆํ™”ํ•˜์—ฌ Docker Compose๋กœ ๋ฐฐํฌ

1) ๋„คํŠธ์›Œํฌ ๋ฐ ๋ณด์•ˆ

(1) VPC์™€ ์„œ๋ธŒ๋„ท

์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐฐํฌํ•˜๋ฏ€๋กœ AWS์˜ ๊ธฐ๋ณธ VPC์™€ ์„œ๋ธŒ๋„ท์„ ์‚ฌ์šฉํ•œ๋‹ค.

(2) ๋ณด์•ˆ ๊ทธ๋ฃน ์ธ๋ฐ”์šด๋“œ ์ตœ์†Œํ™”

๊ธฐ๋ณธ VPC์˜ ํผ๋ธ”๋ฆญ ์„œ๋ธŒ๋„ท์„ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ธํ„ฐ๋„ท์— ์ง์ ‘ ๋…ธ์ถœ๋˜๋ฏ€๋กœ, ์ ‘๊ทผ ๋ฒ”์œ„๋ฅผ ์ค„์ด๊ณ  ๋น„์ธ๊ฐ€ ์ ‘๊ทผ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋น„์Šค ์šด์˜ ๋ฐ ๊ด€๋ฆฌ์— ํ•„์š”ํ•œ ์ตœ์†Œ ํฌํŠธ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.

Inbound
- 80 TCP (HTTP) 0.0.0.0/0(์ „์—ญ): ์‚ฌ์šฉ์ž์˜ HTTP ์š”์ฒญ, HTTPS๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•œ๋‹ค.
- 443 TCP (HTTPS) 0.0.0.0/0(์ „์—ญ): ์‚ฌ์šฉ์ž์˜ HTTPS ์š”์ฒญ, ํŠธ๋ž˜ํ”ฝ์„ Nginx ์ปจํ…Œ์ด๋„ˆ๋กœ ์ „๋‹ฌํ•ด ์š”์ฒญ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌํ•œ๋‹ค.
- 22 TCP (SSH) ๊ฐœ๋ฐœIP: ํšŒ์‚ฌ ์ธํ”„๋ผ ํŒ€์˜ IP์—์„œ๋งŒ ์ ‘๊ทผํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค.

(3) ์ธํ”„๋ผ ์ ‘๊ทผ ๊ถŒํ•œ ํ†ต์ œ

์šฐ๋ฆฌ ํŒ€์˜ ์ธํ”„๋ผ ๋‹ด๋‹น์€ tobby์™€ janna 2์ธ์ด๋‹ค.

aws root ๊ณ„์ •์ด ํƒˆ์ทจ๋˜๋Š” ๊ฒฝ์šฐ, ๋น„์‹ผ EC2 ๋“ฑ์„ ์ƒ์„ฑ/์‚ฌ์šฉํ•˜์—ฌ ์ธํ”„๋ผ ๋น„์šฉ์ด ๊ณผ๋„ํ•˜๊ฒŒ ์ง€์ถœ๋  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ root ๊ณ„์ •์ด ํƒˆ์ทจ๋˜์ง€ ์•Š๋„๋ก ํ•˜๊ณ , root ๊ณ„์ •์— ์ธํ”„๋ผ ๋‹ด๋‹น์ž๊ฐ€ ์ ‘๊ทผํ•˜์ง€ ์•Š๋„๋ก ์ธํ”„๋ผ ๋‹ด๋‹น์ž ๊ฐ์ž์˜ ๊ฐœ๋ณ„ IAM User๋ฅผ ์ƒ์„ฑํ•˜๊ณ  'Infra-Admin' IAM ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด ์ œํ•œ๋œ ๊ถŒํ•œ๋งŒ์„ ๋ถ€์—ฌํ•ด ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.

AWS Account
โ”‚
โ”œโ”€ Root
โ”‚   โ””โ”€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ž‘์—…์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
โ”‚
โ”œโ”€ IAM Group: Infra-Admin
โ”‚   โ”œโ”€ tobby
โ”‚   โ””โ”€ janna
โ”‚
โ””โ”€ IAM Users
    โ”œโ”€ tobby
    โ””โ”€ janna

์•„๋ž˜๋Š” 'Infra-Admin' IAM ๊ทธ๋ฃน ์„ค์ •์— ํ•„์š”ํ•œ ๋‚ด์šฉ์ด๋‹ค.

  • MFA๊ฐ€ ์—†์œผ๋ฉด ๋ชจ๋“  ์š”์ฒญ์„ ๊ฑฐ๋ถ€(MFA ๋“ฑ๋ก ์ œ์™ธ)
  • ๊ถŒํ•œ ์ƒ์Šน(IAM ์ˆ˜์ •) ๋ถˆ๊ฐ€
  • ํ—ˆ์šฉ๋œ ์„œ๋น„์Šค์—๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ณ , ํ—ˆ์šฉ๋œ ๋ฆฌ์†Œ์Šค๋งŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ
    • ec2(t3), S3, CloudFront, CloudWatch, Billing(ํ˜„์žฌ ๋น„์šฉ ์ฝ๊ธฐ๋งŒ ํ—ˆ์šฉ)
'Infra-Admin' IAM ๊ทธ๋ฃน์— ์ ์šฉํ•  JSON ์ „๋ฌธ (ํด๋ฆญํ•˜์—ฌ ํŽผ์น˜๊ธฐ)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListActions",
      "Effect": "Allow",
      "Action": [
        "iam:ListUsers",
        "iam:ListVirtualMFADevices"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowUserToCreateVirtualMFADevice",
      "Effect": "Allow",
      "Action": "iam:CreateVirtualMFADevice",
      "Resource": "arn:aws:iam::*:mfa/${aws:username}"
    },
    {
      "Sid": "AllowUserToManageTheirOwnMFA",
      "Effect": "Allow",
      "Action": [
        "iam:EnableMFADevice",
        "iam:GetMFADevice",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice"
      ],
      "Resource": "arn:aws:iam::*:user/${aws:username}"
    },
    {
      "Sid": "AllowUserToDeactivateTheirOwnMFAOnlyWhenUsingMFA",
      "Effect": "Allow",
      "Action": "iam:DeactivateMFADevice",
      "Resource": "arn:aws:iam::*:user/${aws:username}",
      "Condition": {
        "Bool": { "aws:MultiFactorAuthPresent": "true" }
      }
    },
    {
      "Sid": "BlockMostAccessUnlessSignedInWithMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:ListMFADevices",
        "iam:ListUsers",
        "iam:ListVirtualMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
		"iam:GetUser"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" }
      }
    },
    {
      "Sid": "DenyIAMPrivilegeEscalation",
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "iam:CreateRole",
        "iam:CreateAccessKey",
        "iam:CreateLoginProfile",
        "iam:UpdateLoginProfile",
        "iam:UpdateAssumeRolePolicy",
        "iam:AttachUserPolicy",
        "iam:AttachGroupPolicy",
        "iam:AttachRolePolicy",
        "iam:PutUserPolicy",
        "iam:PutGroupPolicy",
        "iam:PutRolePolicy",
        "iam:CreatePolicy",
        "iam:CreatePolicyVersion",
        "iam:SetDefaultPolicyVersion",
        "iam:AddUserToGroup",
        "iam:PassRole"
      ],
      "Resource": "*"
    },

    {
      "Sid": "DenyExpensiveEC2Instances",
      "Effect": "Deny",
      "Action": [
		"ec2:RunInstances",
		"ec2:ModifyInstanceAttribute"
	  ],
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotLike": { "ec2:InstanceType": "t3.*" }
      }
    },
    {
      "Sid": "AllowEC2",
      "Effect": "Allow",
      "Action": "ec2:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowS3",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowCloudFront",
      "Effect": "Allow",
      "Action": "cloudfront:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowCloudWatch",
      "Effect": "Allow",
      "Action": "cloudwatch:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowBillingReadOnly",
      "Effect": "Allow",
      "Action": [
        "ce:Get*",
        "ce:Describe*",
        "billing:Get*",
        "billing:List*",
        "budgets:ViewBudget",
        "budgets:DescribeBudget"
      ],
      "Resource": "*"
    }
  ]
}

2) ์›น ์ ‘์†(HTTPS)๊ณผ ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ…

(1) ์ ‘์† ์ฃผ์†Œ ๊ณ ์ •

์‚ฌ์šฉ์ž๊ฐ€ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋ฅผ ๊ธฐ์–ตํ•˜๊ธฐ ์‰ฌ์šด ๊ณ ์ •๋œ ์ฃผ์†Œ๋กœ ๋“ค์–ด์™€์•ผ ํ•˜๋ฏ€๋กœ, ๋„๋ฉ”์ธ์„ ๊ตฌ๋งคํ•˜์—ฌ ๊ณ ์ • IP(Elastic IP)์— ์—ฐ๊ฒฐํ•œ๋‹ค.

  • ์‚ฌ์šฉ์ž๋Š” IP์ฃผ์†Œ๊ฐ€ ์•„๋‹Œ ๊ธฐ์–ตํ•˜๊ธฐ ์‰ฌ์šด ๋„๋ฉ”์ธ ์ฃผ์†Œ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ๋„๋ฉ”์ธ ์ฃผ์†Œ๋ฅผ EC2 IP ์ฃผ์†Œ์— ์—ฐ๊ฒฐํ•  ๋•Œ, EC2 ์žฌ์‹œ์ž‘ ์‹œ IP ๋ณ€๊ฒฝ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ณ ์ • IP(Elastic IP)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

(2) HTTPS

์ถ”๊ฐ€ ์ธํ”„๋ผ ๋น„์šฉ์„ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด, ALB๋‚˜ CloudFront ๋Œ€์‹  EC2 ๋‚ด๋ถ€์˜ Nginx์™€ Let's Encrypt(Certbot)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTTPS๋ฅผ ์ ์šฉํ•œ๋‹ค.

  • ALB + ACM: ACM ๋ฌด๋ฃŒ ์ธ์ฆ์„œ ์ง€์› ๋ฐ ๊ด€๋ฆฌ๊ฐ€ ํŽธํ•˜์ง€๋งŒ, ALB ๊ธฐ๋ณธ ์š”๊ธˆ(์›” ์•ฝ $16~$22)์ด ๋ฐœ์ƒํ•˜์—ฌ ๋น„์šฉ ์ธก๋ฉด์—์„œ ๋ถˆ๋ฆฌํ•˜๊ณ , ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ํ™˜๊ฒฝ์—์„œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์ด ๋ถˆํ•„์š”ํ•˜์—ฌ ์ œ์™ธํ•จ
  • CloudFront + ACM: 1TB ์ „์†ก/10,000,000๊ฑด ๋ฌด๋ฃŒ, SSL ์ธ์ฆ์„œ ๊ฐฑ์‹ ์ด ์ž๋™์œผ๋กœ ๋˜์ง€๋งŒ ์ด๋ฏธ ํ”„๋ก ํŠธ ๋ฐฐํฌ์™€ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€ ๋ผ์šฐํŒ…์„ ์œ„ํ•ด ์กด์žฌํ•˜๋Š” Nginx๊ฐ€ ์žˆ๋Š” ์ƒํƒœ์—์„œ ์ธ์ฆ์„œ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด Route53, CloudFront, ACM ์„ค์ •์„ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ•จ
  • Nginx + Let's Encrypt: Let's Encrypt์™€ Certbot์„ ์—ฐ๋™ํ•˜์—ฌ SSL ๋ฐœ๊ธ‰๊ณผ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•จ. ์ธ์ฆ์„œ 90์ผ ๋งŒ๋ฃŒ ๊ด€๋ฆฌ๋Š” Certbot ์ž๋™ ๊ฐฑ์‹  ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ์ž๋™ํ™”๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅํ•˜๊ณ , ์ด๋ฏธ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ๋‚ด ๋ผ์šฐํŒ…์„ ์œ„ํ•ด Nginx ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์ถ”๊ฐ€ ๋น„์šฉ ์—†์ด ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ Nginx๋ฅผ ์„ ํƒํ•จ

(3) ํŠธ๋ž˜ํ”ฝ ํ๋ฆ„

์‚ฌ์šฉ์ž (๋ธŒ๋ผ์šฐ์ €)
   โ”‚
   โ”œโ”€ [HTTP :80]  โ”€โ”€> Nginx โ”€โ”€> HTTPS(:443)๋กœ 301 ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
   โ”‚
   โ””โ”€ [HTTPS :443] โ”€โ”€> Nginx (SSL ์ข…๋ฃŒ ๋ฐ ๋ผ์šฐํŒ…)
                          โ”‚
                          โ”œโ”€ [/] ์ •์  ํŽ˜์ด์ง€  โ”€โ”€> React ๋นŒ๋“œ ํŒŒ์ผ ์ง์ ‘ ์„œ๋น™
                          โ”‚
                          โ””โ”€ [/api] ์š”์ฒญ โ”€โ”€> ๋ฐฑ์—”๋“œ ์ปจํ…Œ์ด๋„ˆ (Spring Boot)
                                                     โ”‚
                                                     โ”œโ”€ AI ๊ฒ€์ฆ โ”€โ”€> AI ์ปจํ…Œ์ด๋„ˆ (FastAPI)
                                                     โ”œโ”€ ๋ฐ์ดํ„ฐ   โ”€โ”€> DB ์ปจํ…Œ์ด๋„ˆ
                                                     โ””โ”€ ์บ์‹œ/์„ธ์…˜ โ”€โ”€> Redis ์ปจํ…Œ์ด๋„ˆ

3) EC2์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์–ด๋–ป๊ฒŒ ์•ˆ์ •์ ์œผ๋กœ ์šด์˜ํ•  ๊ฒƒ์ธ๊ฐ€?

(1) DB ๋ฐ์ดํ„ฐ ์˜์†์„ฑ ๋ณด์กด

๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด, RDS ๊ฐ™์€ ๊ด€๋ฆฌํ˜• DB๋ฅผ ์“ฐ์ง€ ์•Š๊ณ  ์ปจํ…Œ์ด๋„ˆ๋กœ ์ง์ ‘ ๋„์šฐ๋˜, ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ ์‹œ ๋ฐ์ดํ„ฐ ์†Œ๋ฉธ์„ ๋ง‰๊ธฐ ์œ„ํ•ด EC2์˜ ๊ธฐ๋ณธ EBS ๋ณผ๋ฅจ์„ ๋งˆ์šดํŠธํ•˜์—ฌ ์ถ”๊ฐ€ ๋น„์šฉ ์—†์ด ๋ฐ์ดํ„ฐ ์˜์†์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

  • PostgreSQL RDS ์›” ๋น„์šฉ(์„œ์šธ): ์ธ์Šคํ„ด์Šค(t4g.micro, $0.0250/์‹œ๊ฐ„ * 730์‹œ๊ฐ„ = $18.25) + ์Šคํ† ๋ฆฌ์ง€($0.131/GB * 20GB = 2.62) = $20.87 RDS ๋น„์šฉ ๋ฌธ์„œ

(2) ์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ์‚ฌ์ง„ ์ €์žฅ

์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ์‚ฌ์ „์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด, ์ €๋ ดํ•œ ์ˆ˜ ์žˆ๋Š” S3๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

EBS ๋น„์šฉ

  • EBS gp3 0.08$/GB

S3 ๋น„์šฉ

  • S3 ์Šคํƒ ๋‹ค๋“œ 0.023$~0.025$/GB

S3์— ๋น„ํ•ด EBS๊ฐ€ 3๋ฐฐ ์ด์ƒ ๋น„์šฉ์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์— S3๋กœ ๊ฒฐ์ •ํ•œ๋‹ค.

(3) ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ์ €์žฅ

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

  • Public ์ปจํ…Œ์ด๋„ˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ, ์†Œ์ˆ˜์˜ docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ/์—…๋กœ๋“œ ์กฐ๊ฑด์—์„œ๋Š” Docker hub, AWS ECR, GHCR ์„ธ ์ปจํ…Œ์ด๋„ˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋ชจ๋‘ ๋ฌด๋ฃŒ
  • CI/CD ๋‹จ๊ณ„์—์„œ Docker hub์™€ ECR์€ ๊ฐ๊ฐ ์‹œํฌ๋ฆฟ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, GHCR๋Š” github์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธ์ฆ ํ† ํฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ํŽธ๋ฆฌํ•˜๋‹ค.(${{ secrets.GITHUB_TOKEN }})

(4) ๋ฉ”ํŠธ๋ฆญ ๊ธฐ๋ฐ˜ ์žฅ์•  ์ธ์ง€

ํ”„๋กœ๋ฉ”ํ…Œ์šฐ์Šค๋‚˜ ๊ทธ๋ผํŒŒ๋‚˜ ๊ฐ™์€ ๋ณ„๋„ ๋ชจ๋‹ˆํ„ฐ๋ง ํˆด ๊ตฌ์ถ•์€ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„์™€ ์šด์˜ ๋ณต์žก๋„๊ฐ€ ์ปค์ง€๋ฏ€๋กœ, ์ถ”๊ฐ€ ๋น„์šฉ๊ณผ ์„ค์น˜ ์—†์ด EC2์˜ CPU/๋ฉ”๋ชจ๋ฆฌ/๋„คํŠธ์›Œํฌ ์ƒํƒœ๋ฅผ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๋Š” AWS CloudWatch ๊ธฐ๋ณธ ๋ฉ”ํŠธ๋ฆญ๊ณผ ์•Œ๋žŒ์„ ์‚ฌ์šฉํ•œ๋‹ค.

5. 1์ฐจ ๋ฐฐํฌ ๊ตฌ์กฐ์˜ ํ•œ๊ณ„

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

1) ๋‹จ์ผ ์žฅ์• ์ (SPOF)๊ณผ ๋ฐฐํฌ ์‹œ ์„œ๋น„์Šค ์ค‘๋‹จ

  • ์ „์ฒด ์„œ๋น„์Šค ์ค‘๋‹จ: EC2 ์ธ์Šคํ„ด์Šค 1๋Œ€์— ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์›น, API, DB, AI๊นŒ์ง€ ๋ชจ๋“  ์„œ๋น„์Šค๊ฐ€ ์ฆ‰์‹œ ์ค‘๋‹จ๋œ๋‹ค.
  • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ถˆ๊ฐ€: ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐฐํฌํ•˜๋ฏ€๋กœ ์ƒˆ ๋ฒ„์ „์„ ๋ฐฐํฌํ•  ๋•Œ ์ปจํ…Œ์ด๋„ˆ ์žฌ์‹œ์ž‘ ๋™์•ˆ ์„œ๋น„์Šค ์ค‘๋‹จ(๋‹ค์šดํƒ€์ž„)์ด ๋ฐœ์ƒํ•œ๋‹ค.

2) ๋ฐ์ดํ„ฐ ์•ˆ์ •์„ฑ ๋ฐ ๋ฐฑ์—… ํ•œ๊ณ„

  • ๊ณ ๊ฐ€์šฉ์„ฑ(HA) ๋ถ€์žฌ: ๋‹จ์ผ AZ ์ข…์†์œผ๋กœ ์ธํ•ด AZ ์žฅ์•  ์‹œ ์ž๋™ ๋ณต๊ตฌ๊ฐ€ ๋ถˆ๊ฐ€ํ•˜๊ณ  ๋‹ค์šดํƒ€์ž„์ด ๊ธธ์–ด์ง„๋‹ค.
  • ์‹œ์  ๋ณต์›(PITR) ๋ถ€์žฌ: ๊ด€๋ฆฌํ˜• RDS์™€ ๋‹ฌ๋ฆฌ ์งง์€ ์‹œ๊ฐ„ ๋‹จ์œ„(e.g. ์ดˆ/๋ถ„ ๋‹จ์œ„) ๋ณต์›์ด ์–ด๋ ค์›Œ ์žฅ์•  ์ง์ „ ๋ฐ์ดํ„ฐ ๋ณต๊ตฌ์— ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค.
  • ์“ฐ๊ธฐ ์ž‘์—… ์‹œ ์Šค๋ƒ…์ƒท ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ์œ„ํ—˜์„ฑ: ์Šค๋ƒ…์ƒท์€ ๋‹จ์ˆœํžˆ EBS์˜ ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋ผ ์“ฐ๊ธฐ ์ž‘์—… ์ค‘ ์Šค๋ƒ…์ƒท์„ ์ƒ์„ฑํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ์Œ

3) ์ˆ˜ํ‰์  ํ™•์žฅ(Scale-out) ๋ถˆ๊ฐ€

  • ํŠธ๋ž˜ํ”ฝ์ด ๋Š˜์–ด๋‚ฌ์„ ๋•Œ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐํ•ด ์ค„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ(ALB)๊ฐ€ ์—†๊ณ , DB(PostgreSQL)๊ฐ€ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋กœ ํ•จ๊ป˜ ๋–  ์žˆ์–ด ์„œ๋ฒ„ ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ Scale-out์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ ์ธ์Šคํ„ด์Šค ์ฒด๊ธ‰์„ ์˜ฌ๋ฆฌ๋Š” Scale-up๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ