์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์‡ผํ•‘๋ชฐ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ๋Œ€์‘: ์˜คํ† ์Šค์ผ€์ผ๋ง ๋ฐ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿš€ ์‡ผํ•‘๋ชฐ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ๋Œ€์‘: ์˜คํ† ์Šค์ผ€์ผ๋ง ๋ฐ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

ํญ์ฃผํ•˜๋Š” ์ฃผ๋ฌธ์„ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ํ˜„๋ช…ํ•œ ์ธํ”„๋ผ ์ „๋žต ๐Ÿ’ช

๐ŸŽฏ ์™œ ๊ฐ‘์ž๊ธฐ ์„œ๋ฒ„๊ฐ€ ๋‹ค์šด๋ ๊นŒ?

์นœ๊ตฌ์•ผ, ์ƒ์ƒํ•ด๋ด. ๋„ˆ๊ฐ€ ์—ด์‹ฌํžˆ ๋งŒ๋“  ์‡ผํ•‘๋ชฐ์ด ๋“œ๋””์–ด ๋Œ€๋ฐ•์ด ๋‚ฌ์–ด! ๐ŸŽ‰
์ธํ”Œ๋ฃจ์–ธ์„œ๊ฐ€ SNS์— ์˜ฌ๋ ค์คฌ๊ณ , ์ฃผ๋ฌธ์ด ํญ์ฃผํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์ง€.
๊ทผ๋ฐ ๊ฐ‘์ž๊ธฐ... "502 Bad Gateway" ์—๋Ÿฌ๊ฐ€ ๋œฌ๋‹ค๋ฉด? ๐Ÿ˜ฑ

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

๐Ÿ’ก ์‹ค์ œ ์‚ฌ๋ก€:
ํ•œ ์ค‘์†Œ ์‡ผํ•‘๋ชฐ์ด TV ํ™ˆ์‡ผํ•‘์— ๋‚˜๊ฐ„ ์ ์ด ์žˆ์–ด.
ํ‰์†Œ ๋™์‹œ์ ‘์†์ž 100๋ช… ์ˆ˜์ค€์ด์—ˆ๋Š”๋ฐ, ๋ฐฉ์†ก ํ›„ 5๋ถ„ ๋งŒ์— 5,000๋ช…์ด ๋ชฐ๋ ธ์ง€.
๊ฒฐ๊ณผ? ์„œ๋ฒ„ ๋‹ค์šด์œผ๋กœ 30๋ถ„๊ฐ„ ์ฃผ๋ฌธ ๋ถˆ๊ฐ€ ์ƒํƒœ๊ฐ€ ๋๊ณ , ์•ฝ 2์–ต ์›์˜ ๋งค์ถœ ์†์‹ค์ด ๋ฐœ์ƒํ–ˆ์–ด.
์ด๊ฒŒ ๋ฐ”๋กœ ์˜คํ† ์Šค์ผ€์ผ๋ง๊ณผ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์ด ํ•„์š”ํ•œ ์ด์œ ์•ผ.

์ด ๊ธ€์—์„œ๋Š” ์‡ผํ•‘๋ชฐ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ ์•Œ์•„์•ผ ํ•  ํŠธ๋ž˜ํ”ฝ ๋Œ€์‘ ์ „๋žต์„ ์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์ค„๊ฒŒ.
์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๊ฐœ๋ฐœ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•  ๋•Œ๋„ ์ด๋Ÿฐ ์ง€์‹์€ ์ •๋ง ์ค‘์š”ํ•˜๊ฑฐ๋“ ! ๐Ÿ’ผ

ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ์‹œ๋‚˜๋ฆฌ์˜ค ํ‰์ƒ์‹œ 100๋ช… ์ ‘์† ์ด๋ฒคํŠธ ์‹œ์ž‘ 5,000๋ช… ๊ธ‰์ฆ! ์ž๋™ ๋Œ€์‘ ์„œ๋ฒ„ ์ฆ์„ค ์™„๋ฃŒ ๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ 1๋Œ€ ๐Ÿ”ฅ ๊ณผ๋ถ€ํ•˜ ๋ฐœ์ƒ ๐Ÿ–ฅ๏ธ ๐Ÿ–ฅ๏ธ ๐Ÿ–ฅ๏ธ ์ž๋™ ํ™•์žฅ๋œ ์„œ๋ฒ„๋“ค ์˜คํ† ์Šค์ผ€์ผ๋ง + ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ = ์•ˆ์ •์„ฑ

๐Ÿ” ์˜คํ† ์Šค์ผ€์ผ๋ง์ด ๋ญ๊ธธ๋ž˜?

์˜คํ† ์Šค์ผ€์ผ๋ง(Auto Scaling)์€ ๋ง ๊ทธ๋Œ€๋กœ ์ž๋™์œผ๋กœ ๊ทœ๋ชจ๋ฅผ ์กฐ์ ˆํ•˜๋Š” ๊ฑฐ์•ผ.
ํŠธ๋ž˜ํ”ฝ์ด ๋Š˜์–ด๋‚˜๋ฉด ์„œ๋ฒ„๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ , ์ค„์–ด๋“ค๋ฉด ๋‹ค์‹œ ์ค„์ด๋Š” ๋˜‘๋˜‘ํ•œ ์‹œ์Šคํ…œ์ด์ง€. ๐Ÿง 

๐Ÿ“Œ ์˜คํ† ์Šค์ผ€์ผ๋ง์˜ ํ•ต์‹ฌ ๊ฐœ๋…

1 ์ˆ˜ํ‰ ํ™•์žฅ (Scale Out)
์„œ๋ฒ„ ๋Œ€์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋Š” ๋ฐฉ์‹์ด์•ผ. 1๋Œ€ โ†’ 3๋Œ€ โ†’ 10๋Œ€๋กœ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๊ฑฐ์ง€.
์‡ผํ•‘๋ชฐ์—์„œ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์ด์•ผ! ๐Ÿ’ช
2 ์ˆ˜์ง ํ™•์žฅ (Scale Up)
์„œ๋ฒ„ ํ•œ ๋Œ€์˜ ์„ฑ๋Šฅ์„ ๋†’์ด๋Š” ๋ฐฉ์‹. CPU 2์ฝ”์–ด โ†’ 8์ฝ”์–ด๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ์‹์ด์•ผ.
ํ•˜์ง€๋งŒ ํ•œ๊ณ„๊ฐ€ ์žˆ๊ณ , ์žฌ๋ถ€ํŒ…์ด ํ•„์š”ํ•ด์„œ ์‡ผํ•‘๋ชฐ์—” ๋น„์ถ”์ฒœ! ๐Ÿ˜…
3 ์ž๋™ ์ถ•์†Œ (Scale In)
ํŠธ๋ž˜ํ”ฝ์ด ์ค„๋ฉด ์„œ๋ฒ„๋ฅผ ์ž๋™์œผ๋กœ ์ค„์—ฌ์„œ ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜๋Š” ๊ฑฐ์•ผ.
์ƒˆ๋ฒฝ ์‹œ๊ฐ„๋Œ€์— ์„œ๋ฒ„ 10๋Œ€ โ†’ 2๋Œ€๋กœ ์ค„์ด๋ฉด ํด๋ผ์šฐ๋“œ ๋น„์šฉ์ด ํ™• ์ค„์–ด๋“ค์ง€! ๐Ÿ’ฐ
๐ŸŽ“ ์šฉ์–ด ์ •๋ฆฌ:
โ€ข ์ธ์Šคํ„ด์Šค(Instance): ํด๋ผ์šฐ๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ๊ฐ€์ƒ ์„œ๋ฒ„ ํ•œ ๋Œ€
โ€ข ์˜คํ† ์Šค์ผ€์ผ๋ง ๊ทธ๋ฃน(ASG): ์ž๋™์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” ์ธ์Šคํ„ด์Šค๋“ค์˜ ์ง‘ํ•ฉ
โ€ข ์Šค์ผ€์ผ๋ง ์ •์ฑ…: ์–ธ์ œ ์„œ๋ฒ„๋ฅผ ๋Š˜๋ฆฌ๊ณ  ์ค„์ผ์ง€ ์ •ํ•˜๋Š” ๊ทœ์น™
โ€ข ํ—ฌ์Šค์ฒดํฌ: ์„œ๋ฒ„๊ฐ€ ์ •์ƒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒ€์‚ฌ

โš™๏ธ ์˜คํ† ์Šค์ผ€์ผ๋ง ์ž‘๋™ ์›๋ฆฌ

์ž, ์ด์ œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ? ๐Ÿค”

1๋‹จ๊ณ„: ๋ชจ๋‹ˆํ„ฐ๋ง ๐Ÿ“Š
์‹œ์Šคํ…œ์ด ๊ณ„์†ํ•ด์„œ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ์ฒดํฌํ•ด.
CPU ์‚ฌ์šฉ๋ฅ , ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ, ์‘๋‹ต ์‹œ๊ฐ„ ๋“ฑ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์‹œํ•˜์ง€.

2๋‹จ๊ณ„: ์ž„๊ณ„๊ฐ’ ๋„๋‹ฌ ๐Ÿšจ
์˜ˆ๋ฅผ ๋“ค์–ด "CPU ์‚ฌ์šฉ๋ฅ ์ด 70% ์ด์ƒ์ด 5๋ถ„๊ฐ„ ์ง€์†๋˜๋ฉด"์ด๋ผ๋Š” ์กฐ๊ฑด์„ ์„ค์ •ํ–ˆ๋‹ค๊ณ  ํ•ด๋ด.
์‹ค์ œ๋กœ CPU๊ฐ€ 75%๋กœ ์˜ฌ๋ผ๊ฐ€๊ณ  5๋ถ„์ด ์ง€๋‚˜๋ฉด? ์•Œ๋žŒ์ด ์šธ๋ ค!

3๋‹จ๊ณ„: ์Šค์ผ€์ผ๋ง ์‹คํ–‰ ๐Ÿš€
์ž๋™์œผ๋กœ ์ƒˆ ์„œ๋ฒ„๊ฐ€ ์ƒ์„ฑ๋ผ.
AWS๋ผ๋ฉด EC2 ์ธ์Šคํ„ด์Šค๊ฐ€, Azure๋ผ๋ฉด VM์ด ์ž๋™์œผ๋กœ ๋„์›Œ์ง€๋Š” ๊ฑฐ์ง€.
๋ณดํ†ต 1~3๋ถ„ ์ •๋„๋ฉด ์ƒˆ ์„œ๋ฒ„๊ฐ€ ์ค€๋น„๋ผ.

4๋‹จ๊ณ„: ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์—ฐ๊ฒฐ ๐Ÿ”—
์ƒˆ๋กœ ์ƒ๊ธด ์„œ๋ฒ„๊ฐ€ ์ž๋™์œผ๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์— ๋“ฑ๋ก๋ผ.
๊ทธ๋Ÿฌ๋ฉด ํŠธ๋ž˜ํ”ฝ์ด ์ƒˆ ์„œ๋ฒ„๋กœ๋„ ๋ถ„์‚ฐ๋˜๊ธฐ ์‹œ์ž‘ํ•ด!

5๋‹จ๊ณ„: ์•ˆ์ •ํ™” ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง โœ…
์‹œ์Šคํ…œ์ด ์•ˆ์ •๋˜๋ฉด ๋‹ค์‹œ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ชจ๋“œ๋กœ ๋Œ์•„๊ฐ€.
ํŠธ๋ž˜ํ”ฝ์ด ์ค„๋ฉด ๋ฐ˜๋Œ€๋กœ ์„œ๋ฒ„๋ฅผ ์ค„์ด๋Š” ๊ณผ์ •์ด ์ง„ํ–‰๋˜์ง€.

๐Ÿ’ก Pro Tip:
์‡ผํ•‘๋ชฐ์—์„œ๋Š” ์˜ˆ์ธก ์Šค์ผ€์ผ๋ง๋„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!
"๋งค์ฃผ ๊ธˆ์š”์ผ ์ €๋… 8์‹œ์— ํŠธ๋ž˜ํ”ฝ์ด ๊ธ‰์ฆํ•œ๋‹ค"๋Š” ํŒจํ„ด์„ ํ•™์Šตํ•ด์„œ,
๋ฏธ๋ฆฌ ์„œ๋ฒ„๋ฅผ ๋Š˜๋ ค๋†“๋Š” ๊ฑฐ์•ผ. ๋” ๋น ๋ฅธ ๋Œ€์‘์ด ๊ฐ€๋Šฅํ•˜์ง€! ๐ŸŽฏ

๐Ÿ“ˆ ์Šค์ผ€์ผ๋ง ์ •์ฑ… ์ข…๋ฅ˜

์ •์ฑ… ์œ ํ˜• ์„ค๋ช… ์‡ผํ•‘๋ชฐ ์ ํ•ฉ๋„
๋Œ€์ƒ ์ถ”์  ํŠน์ • ์ง€ํ‘œ๋ฅผ ๋ชฉํ‘œ๊ฐ’์œผ๋กœ ์œ ์ง€
(์˜ˆ: CPU 50% ์œ ์ง€)
โญโญโญโญโญ
๊ฐ€์žฅ ์ถ”์ฒœ!
๋‹จ๊ณ„ ์กฐ์ • ์ž„๊ณ„๊ฐ’์— ๋”ฐ๋ผ ๋‹จ๊ณ„์  ํ™•์žฅ
(60%โ†’1๋Œ€, 80%โ†’2๋Œ€)
โญโญโญโญ
์„ธ๋ฐ€ํ•œ ์ œ์–ด ๊ฐ€๋Šฅ
๋‹จ์ˆœ ์กฐ์ • ์กฐ๊ฑด ์ถฉ์กฑ ์‹œ ๊ณ ์ • ์ˆ˜๋Ÿ‰ ์ฆ๊ฐ โญโญ
๋„ˆ๋ฌด ๋‹จ์ˆœํ•จ
์˜ˆ์•ฝ ์กฐ์ • ํŠน์ • ์‹œ๊ฐ„์— ๋ฏธ๋ฆฌ ํ™•์žฅ โญโญโญโญ
์ด๋ฒคํŠธ ๋Œ€๋น„ ์ข‹์Œ
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ:
์˜คํ† ์Šค์ผ€์ผ๋ง์ด ๋„ˆ๋ฌด ๋ฏผ๊ฐํ•˜๋ฉด "ํ”Œ๋ž˜ํ•‘(Flapping)" ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ด.
์„œ๋ฒ„๊ฐ€ ๊ณ„์† ์ƒ๊ฒผ๋‹ค ์—†์–ด์กŒ๋‹ค ๋ฐ˜๋ณตํ•˜๋Š” ๊ฑฐ์ง€. ๐Ÿ˜ต
์ฟจ๋‹ค์šด ์‹œ๊ฐ„(Cooldown Period)์„ ์„ค์ •ํ•ด์„œ ์ตœ์†Œ 5๋ถ„ ์ •๋„๋Š” ๋Œ€๊ธฐํ•˜๋„๋ก ํ•ด์•ผ ํ•ด!

โš–๏ธ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ: ํŠธ๋ž˜ํ”ฝ์˜ ๊ตํ†ต์ •๋ฆฌ

์˜คํ† ์Šค์ผ€์ผ๋ง์œผ๋กœ ์„œ๋ฒ„๋ฅผ 10๋Œ€ ๋Š˜๋ ธ๋‹ค๊ณ  ํ•ด๋ณด์ž.
๊ทธ๋Ÿฐ๋ฐ ์‚ฌ์šฉ์ž๋“ค์ด ์–ด๋–ป๊ฒŒ ์ด 10๋Œ€ ์„œ๋ฒ„์— ๊ณจ๊ณ ๋ฃจ ์ ‘์†ํ• ๊นŒ? ๐Ÿคทโ€โ™‚๏ธ
๋ฐ”๋กœ ์—ฌ๊ธฐ์„œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ(Load Balancer)๊ฐ€ ํ•„์š”ํ•ด!

๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋Š” ์ผ์ข…์˜ ๊ตํ†ต๊ฒฝ์ฐฐ์ด์•ผ. ๐Ÿšฆ
๋“ค์–ด์˜ค๋Š” ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๊ณ  "๋„ˆ๋Š” 1๋ฒˆ ์„œ๋ฒ„๋กœ, ๋„ˆ๋Š” 2๋ฒˆ ์„œ๋ฒ„๋กœ" ํ•˜๋ฉด์„œ ๋ถ„์‚ฐ์‹œ์ผœ์ฃผ์ง€.

๐ŸŽฏ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์˜ ํ•ต์‹ฌ ์—ญํ• 

1. ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ ๐ŸŒŠ
ํ•œ ์„œ๋ฒ„์— ๋ถ€ํ•˜๊ฐ€ ๋ชฐ๋ฆฌ์ง€ ์•Š๋„๋ก ์—ฌ๋Ÿฌ ์„œ๋ฒ„๋กœ ๊ณ ๋ฅด๊ฒŒ ๋ถ„๋ฐฐํ•ด.
10,000๋ช…์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†ํ•˜๋ฉด ์„œ๋ฒ„ 10๋Œ€์— ๊ฐ๊ฐ 1,000๋ช…์”ฉ ๋ฐฐ๋ถ„ํ•˜๋Š” ์‹์ด์ง€.

2. ๊ณ ๊ฐ€์šฉ์„ฑ ๋ณด์žฅ ๐Ÿ›ก๏ธ
์„œ๋ฒ„ ํ•œ ๋Œ€๊ฐ€ ์ฃฝ์–ด๋„ ๋‹ค๋ฅธ ์„œ๋ฒ„๋กœ ์ž๋™ ์ „ํ™˜๋ผ.
์‚ฌ์šฉ์ž๋Š” ์„œ๋ฒ„ ์žฅ์• ๋ฅผ ์ „ํ˜€ ๋А๋ผ์ง€ ๋ชปํ•ด! ์ด๊ฒŒ ์ง„์งœ ์ค‘์š”ํ•œ ๊ฑฐ์•ผ.

3. ํ—ฌ์Šค์ฒดํฌ ์ˆ˜ํ–‰ ๐Ÿ’“
๊ฐ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ธ์ง€ ๊ณ„์† ํ™•์ธํ•ด.
์‘๋‹ต์ด ์—†๊ฑฐ๋‚˜ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š” ์„œ๋ฒ„๋Š” ์ž๋™์œผ๋กœ ์ œ์™ธ์‹œํ‚ค์ง€.

4. SSL/TLS ์ฒ˜๋ฆฌ ๐Ÿ”
HTTPS ์•”ํ˜ธํ™” ์ž‘์—…์„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ค˜.
๊ฐ ์„œ๋ฒ„์˜ ๋ถ€๋‹ด์„ ์ค„์—ฌ์ฃผ๋Š” ๊ฑฐ์•ผ!

๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์ž‘๋™ ๊ตฌ์กฐ ๐Ÿ‘ค ๐Ÿ‘ค ๐Ÿ‘ค ๐Ÿ‘ค ์‚ฌ์šฉ์ž๋“ค ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ ํ—ฌ์Šค์ฒดํฌ SSL ์ฒ˜๋ฆฌ โš–๏ธ ์„œ๋ฒ„ 1 ๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ 2 ๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ 3 ๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ 4 โŒ ์ œ์™ธ๋จ ์ •์ƒ ์„œ๋ฒ„๋กœ๋งŒ ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ

๐Ÿ”„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜

๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€์•ผ.
์‡ผํ•‘๋ชฐ ํŠน์„ฑ์— ๋งž๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด! ๐ŸŽฒ

1 ๋ผ์šด๋“œ ๋กœ๋นˆ (Round Robin) โญโญโญโญโญ
๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ด๊ณ  ๊ณตํ‰ํ•œ ๋ฐฉ์‹์ด์•ผ.
์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„ 1 โ†’ 2 โ†’ 3 โ†’ 1 โ†’ 2 โ†’ 3... ์ˆœ์„œ๋Œ€๋กœ ๋Œ์•„๊ฐ€๋ฉฐ ๋ฐฐ๋ถ„ํ•ด.
์‡ผํ•‘๋ชฐ ์ถ”์ฒœ๋„: ์ตœ๊ณ ! ๊ฐ„๋‹จํ•˜๋ฉด์„œ๋„ ํšจ๊ณผ์ ์ด์•ผ.
2 ์ตœ์†Œ ์—ฐ๊ฒฐ (Least Connections) โญโญโญโญ
ํ˜„์žฌ ์—ฐ๊ฒฐ ์ˆ˜๊ฐ€ ๊ฐ€์žฅ ์ ์€ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹.
์„œ๋ฒ„ 1์— 100๊ฐœ ์—ฐ๊ฒฐ, ์„œ๋ฒ„ 2์— 50๊ฐœ ์—ฐ๊ฒฐ์ด๋ฉด ์„œ๋ฒ„ 2๋กœ ๋ณด๋‚ด๋Š” ๊ฑฐ์ง€.
์‡ผํ•‘๋ชฐ ์ถ”์ฒœ๋„: ๋†’์Œ! ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ๋“ค์ญ‰๋‚ ์ญ‰ํ•  ๋•Œ ์ข‹์•„.
3 IP ํ•ด์‹œ (IP Hash) โญโญโญ
์‚ฌ์šฉ์ž IP ์ฃผ์†Œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ญ์ƒ ๊ฐ™์€ ์„œ๋ฒ„๋กœ ์—ฐ๊ฒฐํ•ด.
์„ธ์…˜ ์œ ์ง€๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์œ ์šฉํ•˜์ง€๋งŒ, ์š”์ฆ˜์€ ์„ธ์…˜ ์Šคํ† ์–ด๋ฅผ ๋”ฐ๋กœ ์“ฐ๋Š” ๊ฒŒ ๋‚˜์•„.
์‡ผํ•‘๋ชฐ ์ถ”์ฒœ๋„: ๋ณดํ†ต
4 ๊ฐ€์ค‘ ๋ผ์šด๋“œ ๋กœ๋นˆ (Weighted Round Robin) โญโญโญโญ
์„œ๋ฒ„ ์„ฑ๋Šฅ์— ๋”ฐ๋ผ ๊ฐ€์ค‘์น˜๋ฅผ ์ค˜.
๊ณ ์„ฑ๋Šฅ ์„œ๋ฒ„๋Š” 70%, ์ €์„ฑ๋Šฅ ์„œ๋ฒ„๋Š” 30% ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๋Š” ์‹์ด์•ผ.
์‡ผํ•‘๋ชฐ ์ถ”์ฒœ๋„: ๋†’์Œ! ์„œ๋ฒ„ ์ŠคํŽ™์ด ๋‹ค๋ฅผ ๋•Œ ํ•„์ˆ˜!
5 ์‘๋‹ต ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ (Response Time) โญโญโญโญโญ
๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ์‘๋‹ตํ•˜๋Š” ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹.
์‹ค์‹œ๊ฐ„์œผ๋กœ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๋‹ˆ๊นŒ ์ตœ์ ์˜ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ์–ด!
์‡ผํ•‘๋ชฐ ์ถ”์ฒœ๋„: ์ตœ๊ณ ! AWS ALB์˜ ๊ธฐ๋ณธ ๋™์ž‘ ๋ฐฉ์‹์ด์•ผ.
๐ŸŽฏ ์‡ผํ•‘๋ชฐ์— ์ตœ์ ์ธ ์กฐํ•ฉ:
๋ผ์šด๋“œ ๋กœ๋นˆ + ์ตœ์†Œ ์—ฐ๊ฒฐ์„ ํ˜ผํ•ฉํ•œ ๋ฐฉ์‹์„ ์ถ”์ฒœํ•ด!
๊ธฐ๋ณธ์€ ๋ผ์šด๋“œ ๋กœ๋นˆ์œผ๋กœ ๋Œ๋ฆฌ๋˜, ํŠน์ • ์„œ๋ฒ„์— ๋ถ€ํ•˜๊ฐ€ ๋ชฐ๋ฆฌ๋ฉด ์ž๋™์œผ๋กœ ์ตœ์†Œ ์—ฐ๊ฒฐ ๋ฐฉ์‹์œผ๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฑฐ์•ผ.
AWS ALB๋‚˜ NGINX Plus์—์„œ ์ด๋Ÿฐ ์Šค๋งˆํŠธํ•œ ๋ถ„์‚ฐ์ด ๊ฐ€๋Šฅํ•ด! ๐Ÿง 

๐Ÿ—๏ธ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์ข…๋ฅ˜

๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋„ ์—ฌ๋Ÿฌ ๋ ˆ๋ฒจ์ด ์žˆ์–ด. OSI 7๊ณ„์ธต ์ค‘ ์–ด๋А ๋ ˆ๋ฒจ์—์„œ ์ž‘๋™ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ๋‚˜๋‰˜์ง€. ๐Ÿ“š

L4 ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ (Transport Layer) ๐Ÿš›
IP ์ฃผ์†Œ์™€ ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ๋ณด๊ณ  ๋ถ„์‚ฐํ•ด.
๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ด์ง€๋งŒ, HTTP ํ—ค๋”๋‚˜ ์ฟ ํ‚ค ๊ฐ™์€ ๊ฑด ๋ชป ๋ด.
TCP/UDP ๋ ˆ๋ฒจ์—์„œ ์ž‘๋™ํ•˜์ง€.

์žฅ์ :
โ€ข ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ๋งค์šฐ ๋น ๋ฆ„ โšก
โ€ข ๋น„์šฉ์ด ์ƒ๋Œ€์ ์œผ๋กœ ์ €๋ ด
โ€ข ๋‹จ์ˆœํ•˜๊ณ  ์•ˆ์ •์ 

๋‹จ์ :
โ€ข URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๋ถˆ๊ฐ€
โ€ข ์ฟ ํ‚ค๋‚˜ ์„ธ์…˜ ์ธ์‹ ๋ถˆ๊ฐ€
โ€ข SSL ํ„ฐ๋ฏธ๋„ค์ด์…˜ ์ œํ•œ์ 

L7 ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ (Application Layer) ๐ŸŽฏ
HTTP ํ—ค๋”, URL, ์ฟ ํ‚ค ๋“ฑ์„ ๋ถ„์„ํ•ด์„œ ๋˜‘๋˜‘ํ•˜๊ฒŒ ๋ถ„์‚ฐํ•ด.
/api/*๋Š” API ์„œ๋ฒ„๋กœ, /images/*๋Š” ์ด๋ฏธ์ง€ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๋Š” ์‹์ด์ง€!

์žฅ์ :
โ€ข URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๊ฐ€๋Šฅ ๐ŸŽฏ
โ€ข ์ฟ ํ‚ค/์„ธ์…˜ ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ
โ€ข SSL ์˜คํ”„๋กœ๋”ฉ ์™„๋ฒฝ ์ง€์›
โ€ข ์ฝ˜ํ…์ธ  ๊ธฐ๋ฐ˜ ์Šค์œ„์นญ
โ€ข ํ—ฌ์Šค์ฒดํฌ๊ฐ€ ๋” ์ •๊ตํ•จ

๋‹จ์ :
โ€ข L4๋ณด๋‹ค ๋А๋ฆผ (ํ•˜์ง€๋งŒ ์ฒด๊ฐ ์•ˆ ๋จ)
โ€ข ๋น„์šฉ์ด ์กฐ๊ธˆ ๋” ๋น„์Œˆ
โ€ข ์„ค์ •์ด ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ

๐Ÿ’ก ์‡ผํ•‘๋ชฐ ์ถ”์ฒœ:
๋ฌด์กฐ๊ฑด L7 ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์จ!
AWS ALB, Azure Application Gateway, Google Cloud Load Balancing ๋ชจ๋‘ L7์ด์•ผ.
URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์œผ๋กœ /checkout์€ ๊ฒฐ์ œ ์„œ๋ฒ„๋กœ, /products๋Š” ์ƒํ’ˆ ์„œ๋ฒ„๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์–ด์„œ ํ›จ์”ฌ ํšจ์œจ์ ์ด์•ผ! ๐Ÿš€

๐Ÿ” ํ—ฌ์Šค์ฒดํฌ ์„ค์ •

๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ํ—ฌ์Šค์ฒดํฌ์•ผ.
์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ธ์ง€ ๊ณ„์† ํ™•์ธํ•ด์„œ ๋ฌธ์ œ ์žˆ๋Š” ์„œ๋ฒ„๋Š” ์ž๋™์œผ๋กœ ์ œ์™ธ์‹œํ‚ค์ง€. ๐Ÿ’“

ํ—ฌ์Šค์ฒดํฌ ๋ฐฉ์‹:

1. HTTP/HTTPS ์ฒดํฌ
ํŠน์ • URL์— ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด.
์˜ˆ: GET /health โ†’ 200 OK๋ฉด ์ •์ƒ, 500์ด๋ฉด ๋น„์ •์ƒ

2. TCP ์ฒดํฌ
ํฌํŠธ๊ฐ€ ์—ด๋ ค์žˆ๋Š”์ง€๋งŒ ํ™•์ธํ•ด. ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋œ ์ •ํ™•ํ•ด.

3. ์ปค์Šคํ…€ ์ฒดํฌ
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ, ์บ์‹œ ์ƒํƒœ ๋“ฑ์„ ์ข…ํ•ฉ์ ์œผ๋กœ ํ™•์ธํ•˜๋Š” ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋งŒ๋“ค์–ด.
์ด๊ฒŒ ์ œ์ผ ์ •ํ™•ํ•ด! ๐ŸŽฏ

๐Ÿฅ ํ—ฌ์Šค์ฒดํฌ ์—”๋“œํฌ์ธํŠธ ์˜ˆ์‹œ:
// Node.js + Express ์˜ˆ์‹œ
app.get('/health', async (req, res) => {
  try {
    // DB ์—ฐ๊ฒฐ ํ™•์ธ
    await db.ping();
    
    // Redis ์—ฐ๊ฒฐ ํ™•์ธ
    await redis.ping();
    
    // ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ  ํ™•์ธ
    const memUsage = process.memoryUsage();
    if (memUsage.heapUsed / memUsage.heapTotal > 0.9) {
      throw new Error('๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ');
    }
    
    res.status(200).json({ 
      status: 'healthy',
      timestamp: new Date()
    });
  } catch (error) {
    res.status(503).json({ 
      status: 'unhealthy',
      error: error.message 
    });
  }
});

ํ—ฌ์Šค์ฒดํฌ ์„ค์ • ํŒ:

โ€ข ๊ฐ„๊ฒฉ(Interval): 30์ดˆ๋งˆ๋‹ค ์ฒดํฌ (๋„ˆ๋ฌด ์งง์œผ๋ฉด ๋ถ€ํ•˜, ๋„ˆ๋ฌด ๊ธธ๋ฉด ์žฅ์•  ๊ฐ์ง€ ๋Šฆ์Œ)
โ€ข ํƒ€์ž„์•„์›ƒ: 5์ดˆ ์ด๋‚ด ์‘๋‹ต ์—†์œผ๋ฉด ์‹คํŒจ๋กœ ๊ฐ„์ฃผ
โ€ข ์ž„๊ณ„๊ฐ’: ์—ฐ์† 2ํšŒ ์‹คํŒจ ์‹œ ์ œ์™ธ, ์—ฐ์† 3ํšŒ ์„ฑ๊ณต ์‹œ ๋ณต๊ท€
โ€ข ๊ฒฝ๋กœ: /health ๋˜๋Š” /healthcheck ์‚ฌ์šฉ

โš ๏ธ ์ฃผ์˜:
ํ—ฌ์Šค์ฒดํฌ ์—”๋“œํฌ์ธํŠธ๋Š” ์ธ์ฆ ์—†์ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์•ผ ํ•ด!
๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•˜๋ฉด ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ์„œ๋ฒ„๋ฅผ ์ฃฝ์€ ๊ฑธ๋กœ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์–ด.
ํ•˜์ง€๋งŒ ๋ณด์•ˆ์„ ์œ„ํ•ด IP ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ๋Š” ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ๐Ÿ”’

๐Ÿ› ๏ธ ์‹ค์ „ ๊ตฌ์ถ•: AWS ํ™˜๊ฒฝ ์˜ˆ์‹œ

์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ฐฐ์› ์œผ๋‹ˆ, ์ด์ œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌ์ถ•ํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ? ๐Ÿ’ช
AWS๋ฅผ ์˜ˆ์‹œ๋กœ ๋“ค๊ฒ ์ง€๋งŒ, Azure๋‚˜ GCP๋„ ๊ฐœ๋…์€ ๋˜‘๊ฐ™์•„!

๐Ÿ—๏ธ 1๋‹จ๊ณ„: ๊ธฐ๋ณธ ์ธํ”„๋ผ ์ค€๋น„

1 VPC ๋ฐ ์„œ๋ธŒ๋„ท ๊ตฌ์„ฑ
๊ฐ€์šฉ ์˜์—ญ(AZ) 2๊ฐœ ์ด์ƒ์— ์„œ๋ธŒ๋„ท์„ ๋งŒ๋“ค์–ด.
์˜ˆ: ap-northeast-2a, ap-northeast-2c
์ด๋ ‡๊ฒŒ ํ•ด์•ผ ํ•œ ์ง€์—ญ์— ์žฅ์• ๊ฐ€ ๋‚˜๋„ ๋‹ค๋ฅธ ์ง€์—ญ์—์„œ ์„œ๋น„์Šค๊ฐ€ ๊ณ„์†๋ผ! ๐ŸŒ
2 ๋ณด์•ˆ ๊ทธ๋ฃน ์„ค์ •
โ€ข ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ: 80(HTTP), 443(HTTPS) ํฌํŠธ ์˜คํ”ˆ
โ€ข ์›น ์„œ๋ฒ„: ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •
โ€ข DB ์„œ๋ฒ„: ์›น ์„œ๋ฒ„์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
๋ณด์•ˆ์€ ๋ ˆ์ด์–ด๋ณ„๋กœ! ๐Ÿ”
3 AMI(์ด๋ฏธ์ง€) ์ค€๋น„
์™„๋ฒฝํ•˜๊ฒŒ ์„ค์ •๋œ ์„œ๋ฒ„๋ฅผ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด๋‘ฌ.
Node.js, Nginx, ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋“ฑ ๋ชจ๋“  ๊ฒŒ ์„ค์น˜๋œ ์ƒํƒœ๋กœ!
์ด ์ด๋ฏธ์ง€๋กœ ์ƒˆ ์„œ๋ฒ„๊ฐ€ ์ž๋™ ์ƒ์„ฑ๋˜๋‹ˆ๊นŒ ์ค‘์š”ํ•ด. ๐Ÿ“ฆ

โš™๏ธ 2๋‹จ๊ณ„: Application Load Balancer ์ƒ์„ฑ

ALB ๊ธฐ๋ณธ ์„ค์ •:

โ€ข ์ด๋ฆ„: my-shop-alb
โ€ข ์Šคํ‚ด: Internet-facing (์™ธ๋ถ€ ์ ‘๊ทผ ๊ฐ€๋Šฅ)
โ€ข IP ์ฃผ์†Œ ์œ ํ˜•: IPv4
โ€ข ๋ฆฌ์Šค๋„ˆ: HTTP(80), HTTPS(443)
โ€ข ๊ฐ€์šฉ ์˜์—ญ: ์ตœ์†Œ 2๊ฐœ ์„ ํƒ

ํƒ€๊ฒŸ ๊ทธ๋ฃน ์„ค์ •:

โ€ข ํƒ€๊ฒŸ ์œ ํ˜•: ์ธ์Šคํ„ด์Šค
โ€ข ํ”„๋กœํ† ์ฝœ: HTTP
โ€ข ํฌํŠธ: 3000 (Node.js ๊ธฐ๋ณธ ํฌํŠธ)
โ€ข ํ—ฌ์Šค์ฒดํฌ ๊ฒฝ๋กœ: /health
โ€ข ์ •์ƒ ์ž„๊ณ„๊ฐ’: 3ํšŒ ์—ฐ์† ์„ฑ๊ณต
โ€ข ๋น„์ •์ƒ ์ž„๊ณ„๊ฐ’: 2ํšŒ ์—ฐ์† ์‹คํŒจ

๐ŸŽฏ URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ์„ค์ •:
ALB์˜ ์ง„์งœ ํŒŒ์›Œ๋Š” ์—ฌ๊ธฐ์„œ ๋‚˜์™€!

๊ทœ์น™ 1: /api/* โ†’ API ์„œ๋ฒ„ ํƒ€๊ฒŸ ๊ทธ๋ฃน
๊ทœ์น™ 2: /admin/* โ†’ ๊ด€๋ฆฌ์ž ์„œ๋ฒ„ ํƒ€๊ฒŸ ๊ทธ๋ฃน
๊ทœ์น™ 3: *.jpg, *.png โ†’ ์ด๋ฏธ์ง€ ์„œ๋ฒ„ ํƒ€๊ฒŸ ๊ทธ๋ฃน
๊ธฐ๋ณธ: ๋‚˜๋จธ์ง€ ๋ชจ๋“  ์š”์ฒญ โ†’ ์›น ์„œ๋ฒ„ ํƒ€๊ฒŸ ๊ทธ๋ฃน

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„œ๋ฒ„ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽจ

๐Ÿš€ 3๋‹จ๊ณ„: Auto Scaling ๊ทธ๋ฃน ์ƒ์„ฑ

์‹œ์ž‘ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ:

{
  "LaunchTemplateName": "my-shop-template",
  "VersionDescription": "v1.0",
  "LaunchTemplateData": {
    "ImageId": "ami-0c76973fbe0ee100c",
    "InstanceType": "t3.medium",
    "KeyName": "my-key-pair",
    "SecurityGroupIds": ["sg-0123456789abcdef"],
    "UserData": "IyEvYmluL2Jhc2gKY2QgL2hvbWUvZWMyLXVzZXIvYXBwCm5wbSBzdGFydA==",
    "IamInstanceProfile": {
      "Name": "EC2-CloudWatch-Role"
    },
    "TagSpecifications": [{
      "ResourceType": "instance",
      "Tags": [
        {"Key": "Name", "Value": "shop-web-server"},
        {"Key": "Environment", "Value": "production"}
      ]
    }]
  }
}

Auto Scaling ๊ทธ๋ฃน ์„ค์ •:

โ€ข ์ตœ์†Œ ์šฉ๋Ÿ‰: 2๋Œ€ (ํ•ญ์ƒ ์ตœ์†Œ 2๋Œ€๋Š” ์œ ์ง€)
โ€ข ์›ํ•˜๋Š” ์šฉ๋Ÿ‰: 4๋Œ€ (ํ‰์ƒ์‹œ 4๋Œ€ ์šด์˜)
โ€ข ์ตœ๋Œ€ ์šฉ๋Ÿ‰: 20๋Œ€ (์ตœ๋Œ€ 20๋Œ€๊นŒ์ง€ ํ™•์žฅ)
โ€ข ๊ฐ€์šฉ ์˜์—ญ: 2๊ฐœ ์ด์ƒ (๊ณ ๊ฐ€์šฉ์„ฑ)
โ€ข ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ: ์œ„์—์„œ ๋งŒ๋“  ALB ์—ฐ๊ฒฐ

๐Ÿ’ก ๋น„์šฉ ์ตœ์ ํ™” ํŒ:
์ŠคํŒŸ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ž์–ด์„œ ์จ๋ด!
์˜จ๋””๋งจ๋“œ 70% + ์ŠคํŒŸ 30% ์กฐํ•ฉ์œผ๋กœ ํ•˜๋ฉด ๋น„์šฉ์„ 30~50% ์ ˆ๊ฐํ•  ์ˆ˜ ์žˆ์–ด.
์ŠคํŒŸ์€ ๊ฐ‘์ž๊ธฐ ํšŒ์ˆ˜๋  ์ˆ˜ ์žˆ์ง€๋งŒ, ASG๊ฐ€ ์ž๋™์œผ๋กœ ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ๋„์›Œ์ฃผ๋‹ˆ๊นŒ ๋ฌธ์ œ์—†์–ด! ๐Ÿ’ฐ

๐Ÿ“Š 4๋‹จ๊ณ„: ์Šค์ผ€์ผ๋ง ์ •์ฑ… ์„ค์ •

๋Œ€์ƒ ์ถ”์  ์Šค์ผ€์ผ๋ง ์ •์ฑ… (์ถ”์ฒœ!):

{
  "TargetTrackingScalingPolicyConfiguration": {
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ASGAverageCPUUtilization"
    },
    "TargetValue": 50.0,
    "ScaleInCooldown": 300,
    "ScaleOutCooldown": 60
  }
}

์ด ์„ค์ •์€ "CPU ์‚ฌ์šฉ๋ฅ ์„ 50%๋กœ ์œ ์ง€ํ•ด์ค˜"๋ผ๋Š” ์˜๋ฏธ์•ผ.
CPU๊ฐ€ 50% ๋„˜์œผ๋ฉด ์„œ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , 50% ๋ฐ‘์œผ๋กœ ๋–จ์–ด์ง€๋ฉด ์ค„์ด๋Š” ๊ฑฐ์ง€! ๐ŸŽฏ

๋‹จ๊ณ„ ์กฐ์ • ์ •์ฑ… (์„ธ๋ฐ€ํ•œ ์ œ์–ด):

{
  "StepScalingPolicyConfiguration": {
    "AdjustmentType": "PercentChangeInCapacity",
    "MetricAggregationType": "Average",
    "StepAdjustments": [
      {
        "MetricIntervalLowerBound": 0,
        "MetricIntervalUpperBound": 10,
        "ScalingAdjustment": 10
      },
      {
        "MetricIntervalLowerBound": 10,
        "MetricIntervalUpperBound": 20,
        "ScalingAdjustment": 20
      },
      {
        "MetricIntervalLowerBound": 20,
        "ScalingAdjustment": 30
      }
    ]
  }
}

์ด๊ฑด ๋” ๋˜‘๋˜‘ํ•ด:
โ€ข CPU 60~70%: ์šฉ๋Ÿ‰ 10% ์ฆ๊ฐ€
โ€ข CPU 70~80%: ์šฉ๋Ÿ‰ 20% ์ฆ๊ฐ€
โ€ข CPU 80% ์ด์ƒ: ์šฉ๋Ÿ‰ 30% ์ฆ๊ฐ€
์ƒํ™ฉ์— ๋งž๊ฒŒ ํƒ„๋ ฅ์ ์œผ๋กœ ๋Œ€์‘ํ•˜๋Š” ๊ฑฐ์•ผ! ๐Ÿง 

์˜ˆ์•ฝ ์กฐ์ • (์ด๋ฒคํŠธ ๋Œ€๋น„):

{
  "ScheduledActionName": "scale-up-for-event",
  "StartTime": "2024-11-11T19:00:00Z",
  "EndTime": "2024-11-12T02:00:00Z",
  "MinSize": 10,
  "MaxSize": 50,
  "DesiredCapacity": 15
}

11์›” 11์ผ(๊ด‘๊ตฐ์ œ) ์ €๋… 8์‹œ๋ถ€ํ„ฐ ์ƒˆ๋ฒฝ 2์‹œ๊นŒ์ง€ ๋ฏธ๋ฆฌ ์„œ๋ฒ„๋ฅผ ๋Š˜๋ ค๋†“๋Š” ๊ฑฐ์•ผ.
ํŠธ๋ž˜ํ”ฝ ํญ์ฆ์„ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ์„ ๋•Œ ์™„๋ฒฝํ•œ ์ „๋žต์ด์ง€! ๐ŸŽ‰

โš ๏ธ ์Šค์ผ€์ผ๋ง ์ •์ฑ… ์ฃผ์˜์‚ฌํ•ญ:
1. ์ฟจ๋‹ค์šด ์‹œ๊ฐ„์„ ๋„ˆ๋ฌด ์งง๊ฒŒ ์„ค์ •ํ•˜์ง€ ๋งˆ. ์ตœ์†Œ 5๋ถ„์€ ์ค˜์•ผ ํ•ด.
2. ์ตœ์†Œ ์šฉ๋Ÿ‰์€ ํ•ญ์ƒ 2๋Œ€ ์ด์ƒ์œผ๋กœ! 1๋Œ€๋ฉด ์žฅ์•  ์‹œ ์„œ๋น„์Šค ์ค‘๋‹จ๋ผ.
3. ์ตœ๋Œ€ ์šฉ๋Ÿ‰์€ ์˜ˆ์‚ฐ์„ ๊ณ ๋ คํ•ด์„œ ์„ค์ •ํ•ด. ๋ฌดํ•œ์ • ๋Š˜์–ด๋‚˜๋ฉด ํŒŒ์‚ฐํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ’ธ
4. CloudWatch ์•Œ๋žŒ์„ ๊ผญ ์„ค์ •ํ•ด์„œ ๋น„์ •์ƒ ํ™•์žฅ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•ด.

๐Ÿ” 5๋‹จ๊ณ„: SSL/TLS ์„ค์ •

์‡ผํ•‘๋ชฐ์€ ๊ฒฐ์ œ ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋‹ˆ๊นŒ HTTPS๋Š” ํ•„์ˆ˜์•ผ! ๐Ÿ”’

AWS Certificate Manager๋กœ ์ธ์ฆ์„œ ๋ฐœ๊ธ‰:

1. ACM ์ฝ˜์†”์—์„œ "์ธ์ฆ์„œ ์š”์ฒญ" ํด๋ฆญ
2. ๋„๋ฉ”์ธ ์ด๋ฆ„ ์ž…๋ ฅ (์˜ˆ: www.myshop.com, myshop.com)
3. DNS ๊ฒ€์ฆ ๋˜๋Š” ์ด๋ฉ”์ผ ๊ฒ€์ฆ ์„ ํƒ
4. Route 53 ์‚ฌ์šฉ ์‹œ ์ž๋™์œผ๋กœ DNS ๋ ˆ์ฝ”๋“œ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
5. ์ธ์ฆ์„œ ๋ฐœ๊ธ‰ ์™„๋ฃŒ! (๋ฌด๋ฃŒ์•ผ! ๐ŸŽ)

ALB์— ์ธ์ฆ์„œ ์—ฐ๊ฒฐ:

โ€ข ALB ๋ฆฌ์Šค๋„ˆ์—์„œ HTTPS(443) ์ถ”๊ฐ€
โ€ข ์œ„์—์„œ ๋ฐœ๊ธ‰๋ฐ›์€ ACM ์ธ์ฆ์„œ ์„ ํƒ
โ€ข HTTP(80)๋Š” HTTPS๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์„ค์ •
โ€ข ๋ณด์•ˆ ์ •์ฑ…: ELBSecurityPolicy-TLS-1-2-2017-01 (TLS 1.2 ์ด์ƒ)

๐ŸŽฏ ๋ณด์•ˆ ๊ฐ•ํ™” ํŒ:
โ€ข HSTS ํ—ค๋” ์ถ”๊ฐ€: ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ญ์ƒ HTTPS๋กœ๋งŒ ์ ‘์†ํ•˜๋„๋ก
โ€ข ๋ณด์•ˆ ํ—ค๋” ์„ค์ •: X-Frame-Options, X-Content-Type-Options ๋“ฑ
โ€ข WAF(Web Application Firewall) ์—ฐ๊ฒฐ: SQL ์ธ์ ์…˜, XSS ๊ณต๊ฒฉ ์ฐจ๋‹จ
โ€ข DDoS ๋ฐฉ์–ด: AWS Shield Standard๋Š” ์ž๋™ ์ ์šฉ๋จ!

๐ŸŽจ ์„ธ์…˜ ๊ด€๋ฆฌ ์ „๋žต

์˜คํ† ์Šค์ผ€์ผ๋ง ํ™˜๊ฒฝ์—์„œ ๊ฐ€์žฅ ๊นŒ๋‹ค๋กœ์šด ๊ฒŒ ๋ฐ”๋กœ ์„ธ์…˜ ๊ด€๋ฆฌ์•ผ. ๐Ÿค”
์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„ 1์—์„œ ๋กœ๊ทธ์ธํ–ˆ๋Š”๋ฐ, ๋‹ค์Œ ์š”์ฒญ์ด ์„œ๋ฒ„ 2๋กœ ๊ฐ€๋ฉด? ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์—†์–ด์ ธ!

โŒ ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ•: ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ์„ธ์…˜ ์ €์žฅ

// ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•ˆ ๋ผ!
const sessions = {}; // ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ

app.post('/login', (req, res) => {
  const sessionId = generateSessionId();
  sessions[sessionId] = { userId: req.body.userId };
  res.cookie('sessionId', sessionId);
});

์ด ๋ฐฉ์‹์€ ๋‹จ์ผ ์„œ๋ฒ„์—์„œ๋งŒ ์ž‘๋™ํ•ด.
์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๋Œ€๋ฉด ๊ฐ ์„œ๋ฒ„๋งˆ๋‹ค ๋‹ค๋ฅธ ์„ธ์…˜ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋˜์ง€. ๐Ÿ˜ต

โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ• 1: Redis ์„ธ์…˜ ์Šคํ† ์–ด

// Redis๋ฅผ ์ค‘์•™ ์„ธ์…˜ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉ
const redis = require('redis');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);

const redisClient = redis.createClient({
  host: 'my-redis-cluster.abc123.ng.0001.apn2.cache.amazonaws.com',
  port: 6379
});

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'my-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true, // HTTPS only
    httpOnly: true,
    maxAge: 1000 * 60 * 60 * 24 // 24์‹œ๊ฐ„
  }
}));

์ด์ œ ๋ชจ๋“  ์„œ๋ฒ„๊ฐ€ ๊ฐ™์€ Redis๋ฅผ ๋ฐ”๋ผ๋ณด๋‹ˆ๊นŒ ์„ธ์…˜์ด ๊ณต์œ ๋ผ! ๐ŸŽ‰
์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋А ์„œ๋ฒ„๋กœ ์—ฐ๊ฒฐ๋˜๋“  ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜์ง€.

๐Ÿ’ก Redis ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์„ฑ:
โ€ข ElastiCache for Redis ์‚ฌ์šฉ (AWS ๊ด€๋ฆฌํ˜• ์„œ๋น„์Šค)
โ€ข ํด๋Ÿฌ์Šคํ„ฐ ๋ชจ๋“œ ํ™œ์„ฑํ™”๋กœ ๊ณ ๊ฐ€์šฉ์„ฑ ํ™•๋ณด
โ€ข ๋‹ค์ค‘ AZ ๋ฐฐํฌ๋กœ ์žฅ์•  ๋Œ€์‘
โ€ข ์ž๋™ ๋ฐฑ์—… ์„ค์ •์œผ๋กœ ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ
Redis๋„ ์ฃฝ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ ์ด์ค‘ํ™”๋Š” ํ•„์ˆ˜์•ผ! ๐Ÿ›ก๏ธ

โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ• 2: JWT ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ

const jwt = require('jsonwebtoken');

// ๋กœ๊ทธ์ธ ์‹œ JWT ๋ฐœ๊ธ‰
app.post('/login', async (req, res) => {
  const user = await authenticateUser(req.body);
  
  const token = jwt.sign(
    { 
      userId: user.id,
      email: user.email,
      role: user.role
    },
    process.env.JWT_SECRET,
    { expiresIn: '24h' }
  );
  
  res.json({ token });
});

// ์ธ์ฆ ๋ฏธ๋“ค์›จ์–ด
const authenticateToken = (req, res, next) => {
  const token = req.headers['authorization']?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: '์ธ์ฆ ํ•„์š”' });
  }
  
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ error: '์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ' });
    }
    req.user = user;
    next();
  });
};

JWT๋Š” ์„œ๋ฒ„์— ์„ธ์…˜์„ ์ €์žฅํ•˜์ง€ ์•Š์•„!
ํ† ํฐ ์ž์ฒด์— ๋ชจ๋“  ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์–ด์„œ ์™„์ „ํžˆ ๋ฌด์ƒํƒœ(Stateless)์•ผ.
์˜คํ† ์Šค์ผ€์ผ๋ง ํ™˜๊ฒฝ์— ์™„๋ฒฝํ•˜๊ฒŒ ๋งž๋Š” ๋ฐฉ์‹์ด์ง€! ๐ŸŽฏ

๐Ÿค” Redis vs JWT, ๋ญ˜ ์จ์•ผ ํ• ๊นŒ?

Redis ์„ธ์…˜ ์Šคํ† ์–ด:
โœ… ์„œ๋ฒ„์—์„œ ์„ธ์…˜ ์ œ์–ด ๊ฐ€๋Šฅ (๊ฐ•์ œ ๋กœ๊ทธ์•„์›ƒ ๋“ฑ)
โœ… ๋ฏผ๊ฐํ•œ ์ •๋ณด ์„œ๋ฒ„์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€
โŒ Redis ์žฅ์•  ์‹œ ์˜ํ–ฅ ๋ฐ›์Œ
โŒ ๋„คํŠธ์›Œํฌ ์˜ค๋ฒ„ํ—ค๋“œ ์กด์žฌ

JWT ํ† ํฐ:
โœ… ์™„์ „ํžˆ ๋ฌด์ƒํƒœ, ํ™•์žฅ์„ฑ ์ตœ๊ณ 
โœ… ์™ธ๋ถ€ ์ €์žฅ์†Œ ๋ถˆํ•„์š”
โŒ ํ† ํฐ ๋ฌดํšจํ™”๊ฐ€ ์–ด๋ ค์›€
โŒ ํ† ํฐ ํฌ๊ธฐ๊ฐ€ ํผ

์ถ”์ฒœ: ๋‘ ๊ฐ€์ง€๋ฅผ ํ˜ผํ•ฉํ•ด์„œ ์จ!
JWT๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์“ฐ๋˜, ์ค‘์š”ํ•œ ์ž‘์—…(๊ฒฐ์ œ ๋“ฑ)์€ Redis์— ์ถ”๊ฐ€ ๊ฒ€์ฆ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฑฐ์•ผ. ๐ŸŽจ

๐Ÿ›’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

์žฅ๋ฐ”๊ตฌ๋‹ˆ๋„ ์„ธ์…˜์ฒ˜๋Ÿผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด.
์‚ฌ์šฉ์ž๊ฐ€ ์ƒํ’ˆ์„ ๋‹ด์•˜๋Š”๋ฐ ์„œ๋ฒ„ ๋ฐ”๋€Œ๋ฉด์„œ ์‚ฌ๋ผ์ง€๋ฉด ํฐ์ผ์ด์ž–์•„! ๐Ÿ˜ฑ

๋ฐฉ๋ฒ• 1: Redis์— ์ €์žฅ

// ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€
app.post('/cart/add', authenticateToken, async (req, res) => {
  const userId = req.user.userId;
  const { productId, quantity } = req.body;
  
  const cartKey = `cart:${userId}`;
  await redisClient.hset(cartKey, productId, quantity);
  await redisClient.expire(cartKey, 60 * 60 * 24 * 7); // 7์ผ ์œ ํšจ
  
  res.json({ success: true });
});

// ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์กฐํšŒ
app.get('/cart', authenticateToken, async (req, res) => {
  const userId = req.user.userId;
  const cartKey = `cart:${userId}`;
  
  const cart = await redisClient.hgetall(cartKey);
  res.json({ cart });
});

๋ฐฉ๋ฒ• 2: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ

// ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ…Œ์ด๋ธ” ์Šคํ‚ค๋งˆ
CREATE TABLE cart_items (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id BIGINT NOT NULL,
  product_id BIGINT NOT NULL,
  quantity INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  INDEX idx_user_id (user_id),
  UNIQUE KEY uk_user_product (user_id, product_id)
);

// ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€
app.post('/cart/add', authenticateToken, async (req, res) => {
  const userId = req.user.userId;
  const { productId, quantity } = req.body;
  
  await db.query(`
    INSERT INTO cart_items (user_id, product_id, quantity)
    VALUES (?, ?, ?)
    ON DUPLICATE KEY UPDATE quantity = quantity + ?
  `, [userId, productId, quantity, quantity]);
  
  res.json({ success: true });
});
๐Ÿ’ก ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ด€๋ฆฌ ์ „๋žต:
โ€ข ๋น„๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž: ๋ธŒ๋ผ์šฐ์ € localStorage์— ์ž„์‹œ ์ €์žฅ
โ€ข ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž: Redis(๋น ๋ฅธ ์ ‘๊ทผ) + DB(์˜๊ตฌ ์ €์žฅ) ์ด์ค‘ํ™”
โ€ข ๋™๊ธฐํ™”: ๋กœ๊ทธ์ธ ์‹œ localStorage ๋‚ด์šฉ์„ ์„œ๋ฒ„๋กœ ๋ณ‘ํ•ฉ
โ€ข ๋งŒ๋ฃŒ ์ •์ฑ…: 7์ผ ์ด์ƒ ๋ฐฉ์น˜๋œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋Š” ์ž๋™ ์‚ญ์ œ
์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๋„ ์ข‹๊ณ  ์„œ๋ฒ„ ๋ถ€๋‹ด๋„ ์ค„์–ด๋“ค์–ด! ๐ŸŽฏ

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์•Œ๋ฆผ ์„ค์ •

์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ–ˆ์œผ๋ฉด ์ด์ œ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ๊ฐ์‹œํ•ด์•ผ์ง€! ๐Ÿ‘€
๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์ฆ‰์‹œ ์•Œ์•„์ฑ„๊ณ  ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด.

๐Ÿ“ˆ ํ•ต์‹ฌ ๋ชจ๋‹ˆํ„ฐ๋ง ์ง€ํ‘œ

1 ์ธํ”„๋ผ ์ง€ํ‘œ
โ€ข CPU ์‚ฌ์šฉ๋ฅ : 70% ์ด์ƒ ์ง€์† ์‹œ ์•Œ๋ฆผ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ : 85% ์ด์ƒ ์‹œ ์•Œ๋ฆผ
โ€ข ๋””์Šคํฌ ์‚ฌ์šฉ๋ฅ : 80% ์ด์ƒ ์‹œ ์•Œ๋ฆผ
โ€ข ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ: ์ž„๊ณ„๊ฐ’ ์ดˆ๊ณผ ์‹œ ์•Œ๋ฆผ
2 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ง€ํ‘œ
โ€ข ์‘๋‹ต ์‹œ๊ฐ„: ํ‰๊ท  500ms ์ด์ƒ ์‹œ ์•Œ๋ฆผ
โ€ข ์—๋Ÿฌ์œจ: 1% ์ด์ƒ ์‹œ ์•Œ๋ฆผ
โ€ข ์ดˆ๋‹น ์š”์ฒญ ์ˆ˜(RPS): ๊ธ‰์ฆ/๊ธ‰๊ฐ ์‹œ ์•Œ๋ฆผ
โ€ข ๋™์‹œ ์ ‘์†์ž ์ˆ˜: ์ž„๊ณ„๊ฐ’ ๋„๋‹ฌ ์‹œ ์•Œ๋ฆผ
3 ๋น„์ฆˆ๋‹ˆ์Šค ์ง€ํ‘œ
โ€ข ์ฃผ๋ฌธ ์ „ํ™˜์œจ: ๊ธ‰๊ฒฉํ•œ ํ•˜๋ฝ ์‹œ ์•Œ๋ฆผ
โ€ข ๊ฒฐ์ œ ์„ฑ๊ณต๋ฅ : 95% ๋ฏธ๋งŒ ์‹œ ์•Œ๋ฆผ
โ€ข ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ดํƒˆ๋ฅ : ํ‰์†Œ๋ณด๋‹ค ๋†’์„ ์‹œ ์•Œ๋ฆผ
โ€ข ํ‰๊ท  ์ฃผ๋ฌธ ๊ธˆ์•ก: ์ด์ƒ ํŒจํ„ด ๊ฐ์ง€ ์‹œ ์•Œ๋ฆผ

๐Ÿ”” CloudWatch ์•Œ๋žŒ ์„ค์ •

// AWS SDK๋กœ ์•Œ๋žŒ ์ƒ์„ฑ
const cloudwatch = new AWS.CloudWatch();

// CPU ์‚ฌ์šฉ๋ฅ  ์•Œ๋žŒ
await cloudwatch.putMetricAlarm({
  AlarmName: 'high-cpu-usage',
  ComparisonOperator: 'GreaterThanThreshold',
  EvaluationPeriods: 2,
  MetricName: 'CPUUtilization',
  Namespace: 'AWS/EC2',
  Period: 300,
  Statistic: 'Average',
  Threshold: 70.0,
  ActionsEnabled: true,
  AlarmActions: [
    'arn:aws:sns:ap-northeast-2:123456789012:alert-topic'
  ],
  AlarmDescription: 'CPU ์‚ฌ์šฉ๋ฅ ์ด 70% ์ด์ƒ 10๋ถ„๊ฐ„ ์ง€์†',
  Dimensions: [
    {
      Name: 'AutoScalingGroupName',
      Value: 'my-shop-asg'
    }
  ]
}).promise();

// ์‘๋‹ต ์‹œ๊ฐ„ ์•Œ๋žŒ
await cloudwatch.putMetricAlarm({
  AlarmName: 'slow-response-time',
  ComparisonOperator: 'GreaterThanThreshold',
  EvaluationPeriods: 3,
  MetricName: 'TargetResponseTime',
  Namespace: 'AWS/ApplicationELB',
  Period: 60,
  Statistic: 'Average',
  Threshold: 0.5,
  ActionsEnabled: true,
  AlarmActions: [
    'arn:aws:sns:ap-northeast-2:123456789012:alert-topic'
  ],
  AlarmDescription: 'ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด 500ms ์ดˆ๊ณผ',
  Dimensions: [
    {
      Name: 'LoadBalancer',
      Value: 'app/my-shop-alb/1234567890abcdef'
    }
  ]
}).promise();

๐Ÿ“ฑ ์•Œ๋ฆผ ์ฑ„๋„ ์„ค์ •

SNS(Simple Notification Service) ํ™œ์šฉ:

// SNS ํ† ํ”ฝ ์ƒ์„ฑ
const sns = new AWS.SNS();

await sns.createTopic({
  Name: 'shop-critical-alerts'
}).promise();

// ์ด๋ฉ”์ผ ๊ตฌ๋… ์ถ”๊ฐ€
await sns.subscribe({
  Protocol: 'email',
  TopicArn: 'arn:aws:sns:ap-northeast-2:123456789012:shop-critical-alerts',
  Endpoint: 'admin@myshop.com'
}).promise();

// SMS ๊ตฌ๋… ์ถ”๊ฐ€ (๊ธด๊ธ‰ ์•Œ๋ฆผ์šฉ)
await sns.subscribe({
  Protocol: 'sms',
  TopicArn: 'arn:aws:sns:ap-northeast-2:123456789012:shop-critical-alerts',
  Endpoint: '+821012345678'
}).promise();

// Slack ์›นํ›… ์—ฐ๋™
await sns.subscribe({
  Protocol: 'https',
  TopicArn: 'arn:aws:sns:ap-northeast-2:123456789012:shop-critical-alerts',
  Endpoint: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXX'
}).promise();
๐ŸŽฏ ์•Œ๋ฆผ ๋ ˆ๋ฒจ ๊ตฌ๋ถ„:

Critical (๊ธด๊ธ‰):
โ€ข ์„œ๋น„์Šค ๋‹ค์šด
โ€ข ๊ฒฐ์ œ ์‹œ์Šคํ…œ ์žฅ์• 
โ€ข ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์‹คํŒจ
โ†’ SMS + ์ „ํ™” + Slack + ์ด๋ฉ”์ผ

Warning (๊ฒฝ๊ณ ):
โ€ข CPU 70% ์ด์ƒ
โ€ข ์‘๋‹ต ์‹œ๊ฐ„ ์ฆ๊ฐ€
โ€ข ์—๋Ÿฌ์œจ ์ƒ์Šน
โ†’ Slack + ์ด๋ฉ”์ผ

Info (์ •๋ณด):
โ€ข ์Šค์ผ€์ผ๋ง ๋ฐœ์ƒ
โ€ข ๋ฐฐํฌ ์™„๋ฃŒ
โ€ข ์ผ์ผ ๋ฆฌํฌํŠธ
โ†’ ์ด๋ฉ”์ผ๋งŒ

๐Ÿ“Š ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ

CloudWatch ๋Œ€์‹œ๋ณด๋“œ๋กœ ํ•œ๋ˆˆ์— ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ“บ

// ๋Œ€์‹œ๋ณด๋“œ ์ƒ์„ฑ
await cloudwatch.putDashboard({
  DashboardName: 'shop-overview',
  DashboardBody: JSON.stringify({
    widgets: [
      {
        type: 'metric',
        properties: {
          metrics: [
            ['AWS/ApplicationELB', 'RequestCount', { stat: 'Sum' }],
            ['.', 'TargetResponseTime', { stat: 'Average' }]
          ],
          period: 300,
          stat: 'Average',
          region: 'ap-northeast-2',
          title: 'ํŠธ๋ž˜ํ”ฝ ๋ฐ ์‘๋‹ต ์‹œ๊ฐ„'
        }
      },
      {
        type: 'metric',
        properties: {
          metrics: [
            ['AWS/EC2', 'CPUUtilization', { stat: 'Average' }],
            ['.', 'NetworkIn', { stat: 'Sum' }],
            ['.', 'NetworkOut', { stat: 'Sum' }]
          ],
          period: 300,
          stat: 'Average',
          region: 'ap-northeast-2',
          title: '์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋ฅ '
        }
      },
      {
        type: 'metric',
        properties: {
          metrics: [
            ['AWS/AutoScaling', 'GroupDesiredCapacity'],
            ['.', 'GroupInServiceInstances'],
            ['.', 'GroupMinSize'],
            ['.', 'GroupMaxSize']
          ],
          period: 300,
          stat: 'Average',
          region: 'ap-northeast-2',
          title: '์˜คํ† ์Šค์ผ€์ผ๋ง ์ƒํƒœ'
        }
      }
    ]
  })
}).promise();
๐Ÿ’ก ๋ชจ๋‹ˆํ„ฐ๋ง Best Practice:
1. ๊ณจ๋“  ์‹œ๊ทธ๋„ ์ง‘์ค‘: ์ง€์—ฐ์‹œ๊ฐ„, ํŠธ๋ž˜ํ”ฝ, ์—๋Ÿฌ, ํฌํ™”๋„
2. ๋ฒ ์ด์Šค๋ผ์ธ ์„ค์ •: ํ‰์ƒ์‹œ ํŒจํ„ด์„ ํŒŒ์•…ํ•ด์„œ ์ด์ƒ ์ง•ํ›„ ๋น ๋ฅด๊ฒŒ ๊ฐ์ง€
3. ์•Œ๋ฆผ ํ”ผ๋กœ๋„ ๋ฐฉ์ง€: ๋„ˆ๋ฌด ๋งŽ์€ ์•Œ๋ฆผ์€ ์˜คํžˆ๋ ค ์—ญํšจ๊ณผ
4. ์ž๋™ ๋ณต๊ตฌ ์„ค์ •: ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ๋Š” ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋˜๋„๋ก
5. ์ •๊ธฐ ๋ฆฌ๋ทฐ: ์ฃผ๊ฐ„/์›”๊ฐ„ ๋ฆฌํฌํŠธ๋กœ ํŠธ๋ Œ๋“œ ๋ถ„์„ ๐Ÿ”
์™„๋ฒฝํ•œ ์‡ผํ•‘๋ชฐ ์ธํ”„๋ผ ์•„ํ‚คํ…์ฒ˜ ์‚ฌ์šฉ์ž ๐Ÿ‘ฅ Application Load Balancer SSL ํ„ฐ๋ฏธ๋„ค์ด์…˜ ํ—ฌ์Šค์ฒดํฌ / URL ๋ผ์šฐํŒ… HTTPS ์›น ์„œ๋ฒ„ 1 ๐Ÿ–ฅ๏ธ Node.js t3.medium ์›น ์„œ๋ฒ„ 2 ๐Ÿ–ฅ๏ธ Node.js t3.medium ์›น ์„œ๋ฒ„ 3 ๐Ÿ–ฅ๏ธ Node.js t3.medium ์›น ์„œ๋ฒ„ N โž• ์ž๋™ ํ™•์žฅ ์ตœ๋Œ€ 20๋Œ€ Redis Cluster ๐Ÿ’พ ์„ธ์…˜ / ์บ์‹œ RDS (MySQL) ๐Ÿ—„๏ธ ์ฃผ๋ฌธ / ์ƒํ’ˆ DB S3 / CloudFront ๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ / ์ •์  ํŒŒ์ผ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์•Œ๋ฆผ ๐Ÿ“Š CloudWatch ๐Ÿ”” SNS ์•Œ๋ฆผ ๐Ÿ“ฑ Slack ์—ฐ๋™ ๐Ÿ“ˆ ๋Œ€์‹œ๋ณด๋“œ ๐Ÿšจ ์ž๋™ ๋ณต๊ตฌ Auto Scaling Group ์ตœ์†Œ: 2๋Œ€ ์›ํ•˜๋Š”: 4๋Œ€ ์ตœ๋Œ€: 20๋Œ€ ๋ณด์•ˆ ๐Ÿ” WAF / Shield ๐Ÿ›ก๏ธ Security Groups

๐Ÿ’ฐ ๋น„์šฉ ์ตœ์ ํ™” ์ „๋žต

์˜คํ† ์Šค์ผ€์ผ๋ง์€ ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์ฒญ๊ตฌ์„œ ํญํƒ„์„ ๋งž์„ ์ˆ˜ ์žˆ์–ด! ๐Ÿ’ธ
๋˜‘๋˜‘ํ•˜๊ฒŒ ๋น„์šฉ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

๐Ÿ“Š ๋น„์šฉ ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

ํ•ญ๋ชฉ ๊ณผ๊ธˆ ๋ฐฉ์‹ ์›” ์˜ˆ์ƒ ๋น„์šฉ
EC2 ์ธ์Šคํ„ด์Šค ์‹œ๊ฐ„๋‹น ๊ณผ๊ธˆ
(t3.medium ๊ธฐ์ค€)
$30~$200
(2~20๋Œ€ ์šด์˜ ์‹œ)
Application Load Balancer ์‹œ๊ฐ„๋‹น + ์ฒ˜๋ฆฌ๋Ÿ‰ $20~$50
๋ฐ์ดํ„ฐ ์ „์†ก GB๋‹น ๊ณผ๊ธˆ $10~$100
(ํŠธ๋ž˜ํ”ฝ์— ๋”ฐ๋ผ)
RDS (MySQL) ์‹œ๊ฐ„๋‹น + ์Šคํ† ๋ฆฌ์ง€ $50~$150
ElastiCache (Redis) ์‹œ๊ฐ„๋‹น ๊ณผ๊ธˆ $30~$80
S3 + CloudFront ์ €์žฅ๋Ÿ‰ + ์ „์†ก๋Ÿ‰ $10~$50
์ด ์˜ˆ์ƒ ๋น„์šฉ $150~$630/์›”

๐Ÿ’ก ๋น„์šฉ ์ ˆ๊ฐ ๊ฟ€ํŒ

1 ์˜ˆ์•ฝ ์ธ์Šคํ„ด์Šค (Reserved Instances) ํ™œ์šฉ
์ตœ์†Œ ์šฉ๋Ÿ‰ ์„œ๋ฒ„๋Š” 1๋…„ ๋˜๋Š” 3๋…„ ์•ฝ์ •์œผ๋กœ ๊ตฌ๋งคํ•ด.
์˜จ๋””๋งจ๋“œ ๋Œ€๋น„ ์ตœ๋Œ€ 72% ํ• ์ธ! ๐ŸŽ‰
์˜ˆ: ํ•ญ์ƒ 2๋Œ€๋Š” ์œ ์ง€ํ•œ๋‹ค๋ฉด, ์ด 2๋Œ€๋Š” ์˜ˆ์•ฝ ์ธ์Šคํ„ด์Šค๋กœ!
2 ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค (Spot Instances) ํ˜ผํ•ฉ
์˜จ๋””๋งจ๋“œ ๋Œ€๋น„ ์ตœ๋Œ€ 90% ์ €๋ ดํ•ด!
๊ฐ‘์ž๊ธฐ ํšŒ์ˆ˜๋  ์ˆ˜ ์žˆ์ง€๋งŒ, ASG๊ฐ€ ์ž๋™์œผ๋กœ ๋Œ€์ฒดํ•ด์ฃผ๋‹ˆ๊นŒ ๊ดœ์ฐฎ์•„.
์ถ”์ฒœ ๋น„์œจ: ์˜จ๋””๋งจ๋“œ 50% + ์ŠคํŒŸ 50%
3 ์Šค์ผ€์ค„ ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ๋ง
์ƒˆ๋ฒฝ ์‹œ๊ฐ„๋Œ€(2~6์‹œ)์—๋Š” ์„œ๋ฒ„๋ฅผ ์ตœ์†Œ๋กœ ์ค„์—ฌ.
ํŠธ๋ž˜ํ”ฝ์ด ๊ฑฐ์˜ ์—†๋Š” ์‹œ๊ฐ„์— ์„œ๋ฒ„ 10๋Œ€ ๋Œ๋ฆฌ๋Š” ๊ฑด ๋‚ญ๋น„์•ผ!
์ด๊ฒƒ๋งŒ์œผ๋กœ๋„ ์›” 30~40% ๋น„์šฉ ์ ˆ๊ฐ ๊ฐ€๋Šฅ! ๐Ÿ’ฐ
4 CloudFront๋กœ ํŠธ๋ž˜ํ”ฝ ์ ˆ๊ฐ
์ด๋ฏธ์ง€, CSS, JS ๊ฐ™์€ ์ •์  ํŒŒ์ผ์€ CDN์œผ๋กœ ์„œ๋น™ํ•ด.
์„œ๋ฒ„ ๋ถ€ํ•˜๋„ ์ค„๊ณ , ๋ฐ์ดํ„ฐ ์ „์†ก ๋น„์šฉ๋„ ์ ˆ์•ฝ๋ผ!
ํŠนํžˆ ํ•ด์™ธ ๊ณ ๊ฐ์ด ๋งŽ๋‹ค๋ฉด ํ•„์ˆ˜์•ผ. ๐ŸŒ
5 RDS ์ฝ๊ธฐ ์ „์šฉ ๋ณต์ œ๋ณธ ํ™œ์šฉ
์“ฐ๊ธฐ๋Š” ๋งˆ์Šคํ„ฐ DB๋กœ, ์ฝ๊ธฐ๋Š” ๋ณต์ œ๋ณธ์œผ๋กœ ๋ถ„์‚ฐํ•ด.
์‡ผํ•‘๋ชฐ์€ ์ฝ๊ธฐ๊ฐ€ 90% ์ด์ƒ์ด๋‹ˆ๊นŒ ํšจ๊ณผ์ ์ด์•ผ!
๋งˆ์Šคํ„ฐ๋Š” ์ž‘์€ ์ธ์Šคํ„ด์Šค, ๋ณต์ œ๋ณธ์€ ํฐ ์ธ์Šคํ„ด์Šค๋กœ ๊ตฌ์„ฑํ•˜๋ฉด ๋น„์šฉ ํšจ์œจ์ ! ๐Ÿ“š
๐ŸŽฏ ์‹ค์ œ ๋น„์šฉ ์ ˆ๊ฐ ์‚ฌ๋ก€:

Before: ์˜จ๋””๋งจ๋“œ t3.medium 10๋Œ€ 24์‹œ๊ฐ„ ์šด์˜
โ†’ ์›” $600

After: ์ตœ์ ํ™” ์ ์šฉ
โ€ข ์˜ˆ์•ฝ ์ธ์Šคํ„ด์Šค 2๋Œ€ (์ตœ์†Œ ์šฉ๋Ÿ‰): $60
โ€ข ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค ํ‰๊ท  4๋Œ€ (ํ”ผํฌ ์‹œ๊ฐ„): $40
โ€ข ์Šค์ผ€์ค„ ์Šค์ผ€์ผ๋ง์œผ๋กœ ์•ผ๊ฐ„ ์ถ•์†Œ
โ†’ ์›” $180 (70% ์ ˆ๊ฐ!) ๐ŸŽ‰

๊ฐ™์€ ์„ฑ๋Šฅ์— ๋น„์šฉ์€ 1/3๋กœ ์ค„์—ˆ์–ด!

๐Ÿ“‰ ๋น„์šฉ ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ •

// AWS Cost Explorer API๋กœ ๋น„์šฉ ์ถ”์ 
const costexplorer = new AWS.CostExplorer();

// ์ผ์ผ ๋น„์šฉ ์กฐํšŒ
const response = await costexplorer.getCostAndUsage({
  TimePeriod: {
    Start: '2024-01-01',
    End: '2024-01-31'
  },
  Granularity: 'DAILY',
  Metrics: ['UnblendedCost'],
  GroupBy: [
    {
      Type: 'SERVICE',
      Key: 'SERVICE'
    }
  ]
}).promise();

// ์˜ˆ์‚ฐ ์ดˆ๊ณผ ์•Œ๋ฆผ ์„ค์ •
const budgets = new AWS.Budgets();

await budgets.createBudget({
  AccountId: '123456789012',
  Budget: {
    BudgetName: 'monthly-shop-budget',
    BudgetLimit: {
      Amount: '500',
      Unit: 'USD'
    },
    TimeUnit: 'MONTHLY',
    BudgetType: 'COST'
  },
  NotificationsWithSubscribers: [
    {
      Notification: {
        NotificationType: 'ACTUAL',
        ComparisonOperator: 'GREATER_THAN',
        Threshold: 80,
        ThresholdType: 'PERCENTAGE'
      },
      Subscribers: [
        {
          SubscriptionType: 'EMAIL',
          Address: 'admin@myshop.com'
        }
      ]
    }
  ]
}).promise();
  • โš ๏ธ ๋น„์šฉ ํญํƒ„ ๋ฐฉ์ง€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
  • โœ… ์ตœ๋Œ€ ์ธ์Šคํ„ด์Šค ์ˆ˜ ์ œํ•œ ์„ค์ • (์˜ˆ: 20๋Œ€)
  • โœ… ์˜ˆ์‚ฐ ์•Œ๋ฆผ ์„ค์ • (80%, 100% ๋„๋‹ฌ ์‹œ)
  • โœ… ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค ์ •๊ธฐ ์ ๊ฒ€
  • โœ… ์Šค์ผ€์ผ ์ธ ์ •์ฑ… ๋ฐ˜๋“œ์‹œ ์„ค์ •
  • โœ… ๋กœ๊ทธ ๋ณด๊ด€ ๊ธฐ๊ฐ„ ์„ค์ • (๋ฌดํ•œ์ • ์Œ“์ด๋ฉด ๋น„์šฉ ์ฆ๊ฐ€)
  • โœ… ์Šค๋ƒ…์ƒท ์ž๋™ ์‚ญ์ œ ์ •์ฑ… ์„ค์ •
  • โœ… ๊ฐœ๋ฐœ/ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์€ ์—…๋ฌด ์‹œ๊ฐ„๋งŒ ์šด์˜

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•

์ธํ”„๋ผ๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌ์ถ•ํ–ˆ์–ด. ์ด์ œ ํ•œ ๋‹จ๊ณ„ ๋” ๋‚˜์•„๊ฐ€๋ณผ๊นŒ? ๐ŸŽฏ
์ง„์งœ ๊ณ ์ˆ˜๋“ค์ด ์“ฐ๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์„ ์•Œ๋ ค์ค„๊ฒŒ!

โšก 1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™”

์•„๋ฌด๋ฆฌ ์„œ๋ฒ„๋ฅผ ๋Š˜๋ ค๋„ DB๊ฐ€ ๋ณ‘๋ชฉ์ด๋ฉด ์†Œ์šฉ์—†์–ด!
์‡ผํ•‘๋ชฐ์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ๋“ค์„ ์ตœ์ ํ™”ํ•ด๋ณด์ž. ๐Ÿ”

์ƒํ’ˆ ๋ชฉ๋ก ์กฐํšŒ ์ตœ์ ํ™”:

-- โŒ ๋А๋ฆฐ ์ฟผ๋ฆฌ (์ธ๋ฑ์Šค ์—†์Œ)
SELECT * FROM products 
WHERE category_id = 10 
AND status = 'active'
ORDER BY created_at DESC
LIMIT 20;

-- โœ… ๋น ๋ฅธ ์ฟผ๋ฆฌ (๋ณตํ•ฉ ์ธ๋ฑ์Šค ํ™œ์šฉ)
CREATE INDEX idx_category_status_created 
ON products(category_id, status, created_at DESC);

SELECT 
  id, name, price, thumbnail_url, stock
FROM products 
WHERE category_id = 10 
AND status = 'active'
ORDER BY created_at DESC
LIMIT 20;

-- ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ SELECT! (SELECT * ๊ธˆ์ง€)

์ฃผ๋ฌธ ๋‚ด์—ญ ์กฐํšŒ ์ตœ์ ํ™”:

-- โŒ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ
// ์ฃผ๋ฌธ ๋ชฉ๋ก ์กฐํšŒ
const orders = await db.query('SELECT * FROM orders WHERE user_id = ?', [userId]);

// ๊ฐ ์ฃผ๋ฌธ๋งˆ๋‹ค ์ƒํ’ˆ ์ •๋ณด ์กฐํšŒ (N๋ฒˆ ์ฟผ๋ฆฌ ๋ฐœ์ƒ!)
for (const order of orders) {
  order.items = await db.query('SELECT * FROM order_items WHERE order_id = ?', [order.id]);
}

-- โœ… JOIN์œผ๋กœ ํ•œ ๋ฒˆ์— ์กฐํšŒ
SELECT 
  o.id, o.order_number, o.total_amount, o.status,
  oi.product_id, oi.product_name, oi.quantity, oi.price
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
WHERE o.user_id = ?
ORDER BY o.created_at DESC
LIMIT 10;
๐Ÿ’ก DB ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
โœ… ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ์ปฌ๋Ÿผ์— ์ธ๋ฑ์Šค ์ƒ์„ฑ
โœ… ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ˆœ์„œ ์ตœ์ ํ™” (์นด๋””๋„๋ฆฌํ‹ฐ ๋†’์€ ์ˆœ)
โœ… EXPLAIN์œผ๋กœ ์ฟผ๋ฆฌ ์‹คํ–‰ ๊ณ„ํš ๋ถ„์„
โœ… ์Šฌ๋กœ์šฐ ์ฟผ๋ฆฌ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง
โœ… ์ปค๋„ฅ์…˜ ํ’€ ํฌ๊ธฐ ์ ์ ˆํžˆ ์„ค์ •
โœ… ์ฝ๊ธฐ ์ „์šฉ ์ฟผ๋ฆฌ๋Š” ๋ณต์ œ๋ณธ์œผ๋กœ ๋ถ„์‚ฐ

๐Ÿ—„๏ธ 2. ์บ์‹ฑ ์ „๋žต

DB ์กฐํšŒ๋ฅผ ์ค„์ด๋Š” ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์€ ์บ์‹ฑ์ด์•ผ!
Redis๋ฅผ ํ™œ์šฉํ•œ ๋‹ค์ธต ์บ์‹ฑ ์ „๋žต์„ ์•Œ์•„๋ณด์ž. ๐Ÿ’พ

์ƒํ’ˆ ์ •๋ณด ์บ์‹ฑ:

// ์บ์‹œ ์šฐ์„  ์กฐํšŒ ํŒจํ„ด
async function getProduct(productId) {
  const cacheKey = `product:${productId}`;
  
  // 1. Redis ์บ์‹œ ํ™•์ธ
  let product = await redis.get(cacheKey);
  
  if (product) {
    console.log('Cache HIT! โšก');
    return JSON.parse(product);
  }
  
  console.log('Cache MISS, DB ์กฐํšŒ ์ค‘...');
  
  // 2. DB์—์„œ ์กฐํšŒ
  product = await db.query(
    'SELECT * FROM products WHERE id = ?',
    [productId]
  );
  
  // 3. Redis์— ์บ์‹ฑ (1์‹œ๊ฐ„ ์œ ํšจ)
  await redis.setex(
    cacheKey,
    3600,
    JSON.stringify(product)
  );
  
  return product;
}

์ธ๊ธฐ ์ƒํ’ˆ ๋ชฉ๋ก ์บ์‹ฑ:

// ์ž์ฃผ ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋Š” ๊ธด TTL
async function getPopularProducts() {
  const cacheKey = 'products:popular';
  
  let products = await redis.get(cacheKey);
  
  if (products) {
    return JSON.parse(products);
  }
  
  // ๋ณต์žกํ•œ ์ง‘๊ณ„ ์ฟผ๋ฆฌ
  products = await db.query(`
    SELECT 
      p.id, p.name, p.price, p.thumbnail_url,
      COUNT(oi.id) as order_count
    FROM products p
    JOIN order_items oi ON p.id = oi.product_id
    WHERE oi.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
    GROUP BY p.id
    ORDER BY order_count DESC
    LIMIT 20
  `);
  
  // 6์‹œ๊ฐ„ ์บ์‹ฑ (์ž์ฃผ ์•ˆ ๋ฐ”๋€œ)
  await redis.setex(
    cacheKey,
    21600,
    JSON.stringify(products)
  );
  
  return products;
}

์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต:

// ์ƒํ’ˆ ์ •๋ณด ์—…๋ฐ์ดํŠธ ์‹œ ์บ์‹œ ์‚ญ์ œ
async function updateProduct(productId, data) {
  // 1. DB ์—…๋ฐ์ดํŠธ
  await db.query(
    'UPDATE products SET ? WHERE id = ?',
    [data, productId]
  );
  
  // 2. ๊ด€๋ จ ์บ์‹œ ๋ชจ๋‘ ์‚ญ์ œ
  await redis.del(`product:${productId}`);
  await redis.del('products:popular');
  await redis.del(`products:category:${data.category_id}`);
  
  // 3. ์บ์‹œ ์›Œ๋ฐ (์„ ํƒ์ )
  await getProduct(productId); // ์ƒˆ ๋ฐ์ดํ„ฐ๋กœ ์บ์‹œ ์žฌ์ƒ์„ฑ
}
๐ŸŽฏ ์บ์‹ฑ ์ „๋žต ๊ฐ€์ด๋“œ:

์งง์€ TTL (1~5๋ถ„):
โ€ข ์žฌ๊ณ  ์ˆ˜๋Ÿ‰
โ€ข ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ฐœ์ˆ˜
โ€ข ์‹ค์‹œ๊ฐ„ ๋žญํ‚น

์ค‘๊ฐ„ TTL (1~6์‹œ๊ฐ„):
โ€ข ์ƒํ’ˆ ์ƒ์„ธ ์ •๋ณด
โ€ข ์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก
โ€ข ์ธ๊ธฐ ์ƒํ’ˆ

๊ธด TTL (12~24์‹œ๊ฐ„):
โ€ข ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€
โ€ข ์ •์ฑ… ํŽ˜์ด์ง€
โ€ข ๊ณต์ง€์‚ฌํ•ญ

์˜๊ตฌ ์บ์‹œ (์ˆ˜๋™ ์‚ญ์ œ):
โ€ข ์ƒํ’ˆ ์ด๋ฏธ์ง€ URL
โ€ข ์นดํ…Œ๊ณ ๋ฆฌ ๊ตฌ์กฐ
โ€ข ์„ค์ • ๊ฐ’

๐Ÿ”„ 3. API ์‘๋‹ต ์ตœ์ ํ™”

ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ตœ์ ํ™”:

// โŒ OFFSET ๋ฐฉ์‹ (ํŽ˜์ด์ง€๊ฐ€ ๋’ค๋กœ ๊ฐˆ์ˆ˜๋ก ๋А๋ ค์ง)
SELECT * FROM products 
ORDER BY id DESC 
LIMIT 20 OFFSET 10000; -- 10,000๊ฐœ๋ฅผ ์Šค์บ”ํ•˜๊ณ  ๋ฒ„๋ฆผ!

// โœ… ์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜ (ํ•ญ์ƒ ๋น ๋ฆ„)
SELECT * FROM products 
WHERE id < ? -- ๋งˆ์ง€๋ง‰ ์กฐํšŒ ID
ORDER BY id DESC 
LIMIT 20;
// API ๊ตฌํ˜„
app.get('/api/products', async (req, res) => {
  const { cursor, limit = 20 } = req.query;
  
  const query = cursor
    ? 'SELECT * FROM products WHERE id < ? ORDER BY id DESC LIMIT ?'
    : 'SELECT * FROM products ORDER BY id DESC LIMIT ?';
  
  const params = cursor ? [cursor, limit] : [limit];
  const products = await db.query(query, params);
  
  const nextCursor = products.length > 0 
    ? products[products.length - 1].id 
    : null;
  
  res.json({
    data: products,
    nextCursor,
    hasMore: products.length === limit
  });
});

์‘๋‹ต ์••์ถ•:

// Express์—์„œ gzip ์••์ถ• ํ™œ์„ฑํ™”
const compression = require('compression');

app.use(compression({
  level: 6, // ์••์ถ• ๋ ˆ๋ฒจ (1~9, 6์ด ์ ๋‹น)
  threshold: 1024, // 1KB ์ด์ƒ๋งŒ ์••์ถ•
  filter: (req, res) => {
    // ์ด๋ฏธ์ง€๋Š” ์ด๋ฏธ ์••์ถ•๋˜์–ด ์žˆ์œผ๋‹ˆ ์ œ์™ธ
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));

// ํ‰๊ท  70% ์šฉ๋Ÿ‰ ๊ฐ์†Œ! ๐ŸŽ‰

๋ถ€๋ถ„ ์‘๋‹ต (Partial Response):

// ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ํ•„๋“œ๋งŒ ์š”์ฒญ
app.get('/api/products/:id', async (req, res) => {
  const { fields } = req.query;
  
  let selectFields = '*';
  if (fields) {
    // fields=id,name,price
    selectFields = fields.split(',').join(', ');
  }
  
  const product = await db.query(
    `SELECT ${selectFields} FROM products WHERE id = ?`,
    [req.params.id]
  );
  
  res.json(product);
});

// ์‚ฌ์šฉ ์˜ˆ์‹œ:
// GET /api/products/123?fields=id,name,price
// โ†’ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ „์†ก! ๋„คํŠธ์›Œํฌ ๋น„์šฉ ์ ˆ๊ฐ!

๐Ÿ–ผ๏ธ 4. ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

์‡ผํ•‘๋ชฐ์—์„œ ์ด๋ฏธ์ง€๋Š” ํŠธ๋ž˜ํ”ฝ์˜ 70% ์ด์ƒ์„ ์ฐจ์ง€ํ•ด!
์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋Š” ํ•„์ˆ˜์•ผ. ๐Ÿ“ธ

CloudFront + Lambda@Edge๋กœ ์‹ค์‹œ๊ฐ„ ๋ฆฌ์‚ฌ์ด์ง•:

// Lambda@Edge ํ•จ์ˆ˜
exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  const uri = request.uri;
  
  // URL์—์„œ ํฌ๊ธฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”์ถœ
  // ์˜ˆ: /images/product.jpg?w=300&h=300
  const params = new URLSearchParams(request.querystring);
  const width = params.get('w');
  const height = params.get('h');
  
  if (width || height) {
    // S3์—์„œ ์›๋ณธ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
    const s3 = new AWS.S3();
    const originalImage = await s3.getObject({
      Bucket: 'my-shop-images',
      Key: uri.substring(1)
    }).promise();
    
    // Sharp๋กœ ๋ฆฌ์‚ฌ์ด์ง•
    const sharp = require('sharp');
    const resizedImage = await sharp(originalImage.Body)
      .resize(parseInt(width), parseInt(height), {
        fit: 'cover',
        position: 'center'
      })
      .webp({ quality: 80 }) // WebP ๋ณ€ํ™˜!
      .toBuffer();
    
    return {
      status: '200',
      headers: {
        'content-type': [{ value: 'image/webp' }],
        'cache-control': [{ value: 'max-age=31536000' }]
      },
      body: resizedImage.toString('base64'),
      bodyEncoding: 'base64'
    };
  }
  
  return request;
};

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐ˜์‘ํ˜• ์ด๋ฏธ์ง€:

<!-- ๋””๋ฐ”์ด์Šค ํฌ๊ธฐ์— ๋งž๋Š” ์ด๋ฏธ์ง€ ์ž๋™ ์„ ํƒ -->
<picture>
  <source 
    srcset="https://cdn.myshop.com/product.jpg?w=400&h=400" 
    media="(max-width: 768px)">
  <source 
    srcset="https://cdn.myshop.com/product.jpg?w=800&h=800" 
    media="(max-width: 1200px)">
  <img 
    src="https://cdn.myshop.com/product.jpg?w=1200&h=1200" 
    alt="์ƒํ’ˆ ์ด๋ฏธ์ง€"
    loading="lazy"> <!-- ์ง€์—ฐ ๋กœ๋”ฉ -->
</picture>
๐Ÿ’ก ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
โœ… WebP ํฌ๋งท ์‚ฌ์šฉ (JPEG ๋Œ€๋น„ 30% ์ž‘์Œ)
โœ… ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ๋ฆฌ์‚ฌ์ด์ง• (์›๋ณธ ๊ทธ๋Œ€๋กœ ์“ฐ์ง€ ๋ง๊ธฐ)
โœ… CDN ์บ์‹ฑ (CloudFront, Cloudflare ๋“ฑ)
โœ… Lazy Loading ์ ์šฉ
โœ… ์••์ถ• ํ’ˆ์งˆ 80% (๋ˆˆ์œผ๋กœ ์ฐจ์ด ์•ˆ ๋‚จ)
โœ… ์ธ๋„ค์ผ์€ ๋ณ„๋„ ์ƒ์„ฑ (๋ชฉ๋ก์šฉ ์ž‘์€ ์ด๋ฏธ์ง€)

๐ŸŽฏ ์‹ค์ „ ์‹œ๋‚˜๋ฆฌ์˜ค: ๋ธ”๋ž™ํ”„๋ผ์ด๋ฐ์ด ๋Œ€์‘

์ž, ์ด์ œ ๋ฐฐ์šด ๊ฑธ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ „ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ด๋ณด์ž! ๐ŸŽฎ
๋ธ”๋ž™ํ”„๋ผ์ด๋ฐ์ด ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ์ด๋ฒคํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ค€๋น„ํ•˜๊ณ  ๋Œ€์‘ํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊ฒŒ.

๐Ÿ“… D-7: ์‚ฌ์ „ ์ค€๋น„

1 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์‹คํ–‰
// Apache JMeter ๋˜๋Š” k6๋กœ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
  stages: [
    { duration: '5m', target: 100 },   // ์›Œ๋ฐ์—…
    { duration: '10m', target: 1000 }, // ์ •์ƒ ๋ถ€ํ•˜
    { duration: '5m', target: 5000 },  // ํ”ผํฌ ๋ถ€ํ•˜
    { duration: '10m', target: 5000 }, // ์œ ์ง€
    { duration: '5m', target: 0 },     // ์ฟจ๋‹ค์šด
  ],
};

export default function () {
  // ๋ฉ”์ธ ํŽ˜์ด์ง€
  let res = http.get('https://myshop.com');
  check(res, { 'status is 200': (r) => r.status === 200 });
  
  sleep(1);
  
  // ์ƒํ’ˆ ๋ชฉ๋ก
  res = http.get('https://myshop.com/api/products');
  check(res, { 
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500
  });
  
  sleep(2);
  
  // ์ƒํ’ˆ ์ƒ์„ธ
  res = http.get('https://myshop.com/api/products/123');
  check(res, { 'status is 200': (r) => r.status === 200 });
  
  sleep(1);
}
2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”
โ€ข ์Šฌ๋กœ์šฐ ์ฟผ๋ฆฌ ๋ชจ๋‘ ์ œ๊ฑฐ
โ€ข ์ธ๋ฑ์Šค ์žฌ๊ตฌ์„ฑ
โ€ข ํ†ต๊ณ„ ์ •๋ณด ์—…๋ฐ์ดํŠธ
โ€ข ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์•„์นด์ด๋น™
โ€ข ์ปค๋„ฅ์…˜ ํ’€ ํฌ๊ธฐ ์ฆ๊ฐ€
3 ์บ์‹œ ์›Œ๋ฐ
// ์ฃผ์š” ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ์บ์‹ฑ
async function warmupCache() {
  console.log('์บ์‹œ ์›Œ๋ฐ ์‹œ์ž‘...');
  
  // ์ธ๊ธฐ ์ƒํ’ˆ 100๊ฐœ
  const popularProducts = await db.query(`
    SELECT id FROM products 
    ORDER BY view_count DESC 
    LIMIT 100
  `);
  
  for (const product of popularProducts) {
    await getProduct(product.id); // ์บ์‹œ์— ์ €์žฅ
  }
  
  // ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ
  const categories = await db.query('SELECT id FROM categories');
  for (const category of categories) {
    await getCategoryProducts(category.id);
  }
  
  console.log('์บ์‹œ ์›Œ๋ฐ ์™„๋ฃŒ! โœ…');
}

// ์ด๋ฒคํŠธ ์‹œ์ž‘ 1์‹œ๊ฐ„ ์ „์— ์‹คํ–‰
schedule.scheduleJob('0 18 * * *', warmupCache);
4 ์Šค์ผ€์ผ๋ง ์ •์ฑ… ์กฐ์ •
// ์ด๋ฒคํŠธ ๊ธฐ๊ฐ„ ๋™์•ˆ ๊ณต๊ฒฉ์ ์ธ ์Šค์ผ€์ผ๋ง
{
  "TargetTrackingScalingPolicyConfiguration": {
    "TargetValue": 40.0,  // ํ‰์†Œ 50% โ†’ 40%๋กœ ๋‚ฎ์ถค
    "ScaleOutCooldown": 30,  // ํ‰์†Œ 60์ดˆ โ†’ 30์ดˆ๋กœ ๋‹จ์ถ•
    "ScaleInCooldown": 600   // ์ถ•์†Œ๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ
  }
}

๐Ÿ“… D-1: ์ตœ์ข… ์ ๊ฒ€

โœ… ์ตœ์ข… ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
โ–ก ์˜ˆ์•ฝ ์Šค์ผ€์ผ๋ง ์„ค์ • ํ™•์ธ (์ด๋ฒคํŠธ ์‹œ์ž‘ 1์‹œ๊ฐ„ ์ „๋ถ€ํ„ฐ ์„œ๋ฒ„ ์ฆ์„ค)
โ–ก ์ตœ๋Œ€ ์ธ์Šคํ„ด์Šค ์ˆ˜ ์ƒํ–ฅ (20๋Œ€ โ†’ 50๋Œ€)
โ–ก ์•Œ๋ฆผ ์ฑ„๋„ ํ…Œ์ŠคํŠธ (Slack, SMS ์ •์ƒ ์ž‘๋™ ํ™•์ธ)
โ–ก ๋ฐฑ์—… ์™„๋ฃŒ (DB, Redis ์Šค๋ƒ…์ƒท)
โ–ก ๋กค๋ฐฑ ๊ณ„ํš ์ˆ˜๋ฆฝ
โ–ก ์˜จ์ฝœ ๋‹ด๋‹น์ž ์ง€์ •
โ–ก ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์‹œ๋ณด๋“œ ์ค€๋น„
โ–ก CDN ์บ์‹œ ํ”„๋ฆฌ๋กœ๋”ฉ
โ–ก ๊ฒฐ์ œ ์‹œ์Šคํ…œ ์ด์ค‘ํ™” ํ™•์ธ
โ–ก ๊ณ ๊ฐ์„ผํ„ฐ ์ธ๋ ฅ ์ฆ์› ํ™•์ธ

๐Ÿ”ฅ D-Day: ์‹ค์‹œ๊ฐ„ ๋Œ€์‘

์ด๋ฒคํŠธ ์‹œ์ž‘ (19:00):

// ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์Šคํฌ๋ฆฝํŠธ
const monitor = setInterval(async () => {
  const metrics = await getMetrics();
  
  console.log(`
    โฐ ${new Date().toLocaleTimeString()}
    ๐Ÿ‘ฅ ๋™์‹œ ์ ‘์†์ž: ${metrics.activeUsers}
    ๐Ÿ–ฅ๏ธ  ํ™œ์„ฑ ์„œ๋ฒ„: ${metrics.activeInstances}
    ๐Ÿ“Š CPU ํ‰๊ท : ${metrics.avgCPU}%
    โšก ์‘๋‹ต ์‹œ๊ฐ„: ${metrics.avgResponseTime}ms
    ๐Ÿ’ฐ ์ฃผ๋ฌธ ์ˆ˜: ${metrics.orderCount}
    โŒ ์—๋Ÿฌ์œจ: ${metrics.errorRate}%
  `);
  
  // ์ด์ƒ ์ง•ํ›„ ๊ฐ์ง€
  if (metrics.errorRate > 1) {
    await sendAlert('๐Ÿšจ ์—๋Ÿฌ์œจ 1% ์ดˆ๊ณผ!', 'critical');
  }
  
  if (metrics.avgResponseTime > 1000) {
    await sendAlert('โš ๏ธ ์‘๋‹ต ์‹œ๊ฐ„ 1์ดˆ ์ดˆ๊ณผ', 'warning');
  }
  
  if (metrics.activeInstances >= 45) {
    await sendAlert('๐Ÿ“ˆ ์„œ๋ฒ„ 45๋Œ€ ๋„๋‹ฌ, ์ตœ๋Œ€์น˜ ์ž„๋ฐ•', 'warning');
  }
}, 30000); // 30์ดˆ๋งˆ๋‹ค ์ฒดํฌ

19:30 - ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ๊ฐ์ง€:

// ์ž๋™ ์Šค์ผ€์ผ๋ง ๋กœ๊ทธ
[19:30:15] ๐Ÿš€ CPU ์‚ฌ์šฉ๋ฅ  45% ๋„๋‹ฌ
[19:30:45] โž• ์„œ๋ฒ„ 2๋Œ€ ์ถ”๊ฐ€ ์‹œ์ž‘ (์ด 6๋Œ€ โ†’ 8๋Œ€)
[19:31:20] โœ… ์ƒˆ ์„œ๋ฒ„ 2๋Œ€ ํ—ฌ์Šค์ฒดํฌ ํ†ต๊ณผ
[19:31:25] ๐Ÿ”— ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์— ์—ฐ๊ฒฐ ์™„๋ฃŒ
[19:31:30] ๐Ÿ“Š ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ ์‹œ์ž‘

[19:35:20] ๐Ÿš€ CPU ์‚ฌ์šฉ๋ฅ  50% ๋„๋‹ฌ
[19:35:50] โž• ์„œ๋ฒ„ 3๋Œ€ ์ถ”๊ฐ€ ์‹œ์ž‘ (์ด 8๋Œ€ โ†’ 11๋Œ€)
[19:36:30] โœ… ์ƒˆ ์„œ๋ฒ„ 3๋Œ€ ์ค€๋น„ ์™„๋ฃŒ

[19:40:00] ๐Ÿ“ˆ ๋™์‹œ ์ ‘์†์ž 10,000๋ช… ๋ŒํŒŒ!
[19:40:05] ๐ŸŽ‰ ์‹œ์Šคํ…œ ์•ˆ์ •์ ์œผ๋กœ ์ž‘๋™ ์ค‘
[19:40:10] ๐Ÿ’ฐ ๋ถ„๋‹น ์ฃผ๋ฌธ ์ˆ˜: 150๊ฑด

20:00 - ํ”ผํฌ ํƒ€์ž„:

๐Ÿ“Š ์‹ค์‹œ๊ฐ„ ํ˜„ํ™ฉ:
โ€ข ๋™์‹œ ์ ‘์†์ž: 15,000๋ช…
โ€ข ํ™œ์„ฑ ์„œ๋ฒ„: 18๋Œ€
โ€ข ํ‰๊ท  CPU: 55%
โ€ข ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„: 320ms
โ€ข ์—๋Ÿฌ์œจ: 0.3%
โ€ข ๋ถ„๋‹น ์ฃผ๋ฌธ: 200๊ฑด
โ€ข ๋ˆ„์  ๋งค์ถœ: 3์–ต ์›

โœ… ๋ชจ๋“  ์ง€ํ‘œ ์ •์ƒ ๋ฒ”์œ„!

22:00 - ํŠธ๋ž˜ํ”ฝ ๊ฐ์†Œ ์‹œ์ž‘:

[22:00:00] ๐Ÿ“‰ ํŠธ๋ž˜ํ”ฝ ๊ฐ์†Œ ๊ฐ์ง€
[22:00:05] โธ๏ธ  ์Šค์ผ€์ผ ์ธ ๋Œ€๊ธฐ ์ค‘ (์ฟจ๋‹ค์šด 10๋ถ„)
[22:10:00] โž– ์„œ๋ฒ„ 2๋Œ€ ์ œ๊ฑฐ ์‹œ์ž‘ (18๋Œ€ โ†’ 16๋Œ€)
[22:10:30] ๐Ÿ”Œ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์—์„œ ์—ฐ๊ฒฐ ํ•ด์ œ
[22:11:00] ๐Ÿ’พ ์„œ๋ฒ„ ์ข…๋ฃŒ ์ „ ์„ธ์…˜ ๋“œ๋ ˆ์ธ ์™„๋ฃŒ
[22:11:30] โœ… ์„œ๋ฒ„ 2๋Œ€ ์•ˆ์ „ํ•˜๊ฒŒ ์ข…๋ฃŒ

[22:30:00] โž– ์„œ๋ฒ„ 4๋Œ€ ์ถ”๊ฐ€ ์ œ๊ฑฐ (16๋Œ€ โ†’ 12๋Œ€)
[23:00:00] โž– ์„œ๋ฒ„ 4๋Œ€ ์ถ”๊ฐ€ ์ œ๊ฑฐ (12๋Œ€ โ†’ 8๋Œ€)
[00:00:00] โž– ์„œ๋ฒ„ 4๋Œ€ ์ถ”๊ฐ€ ์ œ๊ฑฐ (8๋Œ€ โ†’ 4๋Œ€)
[02:00:00] ๐ŸŒ™ ์ตœ์†Œ ์šฉ๋Ÿ‰ 2๋Œ€๋กœ ๋ณต๊ท€

๐Ÿ“Š D+1: ์‚ฌํ›„ ๋ถ„์„

// ์ด๋ฒคํŠธ ๊ฒฐ๊ณผ ๋ฆฌํฌํŠธ
{
  "event": "Black Friday 2024",
  "duration": "7 hours",
  "summary": {
    "peakConcurrentUsers": 15234,
    "totalOrders": 45678,
    "totalRevenue": "12์–ต ์›",
    "averageResponseTime": "285ms",
    "errorRate": "0.25%",
    "uptime": "100%"
  },
  "infrastructure": {
    "maxInstances": 18,
    "minInstances": 2,
    "averageInstances": 9.5,
    "totalScaleOutEvents": 12,
    "totalScaleInEvents": 10,
    "estimatedCost": "$180"
  },
  "performance": {
    "cacheHitRate": "92%",
    "databaseQueryTime": "45ms",
    "cdnHitRate": "88%",
    "apiSuccessRate": "99.75%"
  },
  "issues": [
    {
      "time": "19:45",
      "issue": "Redis ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋ฅ  85% ๋„๋‹ฌ",
      "action": "๋ถˆํ•„์š”ํ•œ ์บ์‹œ ์‚ญ์ œ",
      "resolved": "19:47"
    },
    {
      "time": "20:15",
      "issue": "ํŠน์ • API ์‘๋‹ต ์‹œ๊ฐ„ 1.2์ดˆ",
      "action": "์ฟผ๋ฆฌ ์ตœ์ ํ™” ๋ฐ ์บ์‹œ ์ถ”๊ฐ€",
      "resolved": "20:20"
    }
  ],
  "improvements": [
    "Redis ํด๋Ÿฌ์Šคํ„ฐ ๋ฉ”๋ชจ๋ฆฌ ์ฆ์„ค ํ•„์š”",
    "ํŠน์ • ์ƒํ’ˆ ํŽ˜์ด์ง€ ์ถ”๊ฐ€ ์ตœ์ ํ™” ํ•„์š”",
    "๊ฒฐ์ œ API ํƒ€์ž„์•„์›ƒ ์„ค์ • ์กฐ์ • ํ•„์š”"
  ]
}
๐Ÿ’ก ์ด๋ฒคํŠธ ์„ฑ๊ณต ์š”์ธ:
1. ์‚ฌ์ „ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋กœ ๋ณ‘๋ชฉ ์ง€์  ๋ฏธ๋ฆฌ ํŒŒ์•…
2. ๊ณต๊ฒฉ์ ์ธ ์Šค์ผ€์ผ๋ง ์ •์ฑ…์œผ๋กœ ๋น ๋ฅธ ๋Œ€์‘
3. ์บ์‹œ ์›Œ๋ฐ์œผ๋กœ ์ดˆ๊ธฐ ๋ถ€ํ•˜ ๊ฐ์†Œ
4. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง์œผ๋กœ ์ฆ‰๊ฐ ๋Œ€์‘
5. ์˜ˆ์•ฝ ์Šค์ผ€์ผ๋ง์œผ๋กœ ์‚ฌ์ „ ์ค€๋น„
6. ๋กค๋ฐฑ ๊ณ„ํš ์ˆ˜๋ฆฝ์œผ๋กœ ์•ˆ์ „์žฅ์น˜ ๋งˆ๋ จ

๐Ÿ› ๏ธ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ๋ณ„ ๊ตฌํ˜„ ๊ฐ€์ด๋“œ

AWS๋งŒ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ์•ผ! ๋‹ค๋ฅธ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž. โ˜๏ธ

๐Ÿ”ท Azure ๊ตฌํ˜„

Azure Load Balancer + VM Scale Sets:

// Azure CLI๋กœ ๊ตฌ์„ฑ
# ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน ์ƒ์„ฑ
az group create --name myshop-rg --location koreacentral

# ๊ฐ€์ƒ ๋„คํŠธ์›Œํฌ ์ƒ์„ฑ
az network vnet create \
  --resource-group myshop-rg \
  --name myshop-vnet \
  --address-prefix 10.0.0.0/16 \
  --subnet-name myshop-subnet \
  --subnet-prefix 10.0.1.0/24

# Application Gateway (L7 ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ) ์ƒ์„ฑ
az network application-gateway create \
  --name myshop-appgw \
  --resource-group myshop-rg \
  --vnet-name myshop-vnet \
  --subnet myshop-subnet \
  --capacity 2 \
  --sku Standard_v2 \
  --http-settings-cookie-based-affinity Disabled \
  --frontend-port 80 \
  --http-settings-port 3000 \
  --http-settings-protocol Http

# VM Scale Set ์ƒ์„ฑ
az vmss create \
  --resource-group myshop-rg \
  --name myshop-vmss \
  --image UbuntuLTS \
  --upgrade-policy-mode automatic \
  --instance-count 2 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --load-balancer myshop-appgw

# ์˜คํ† ์Šค์ผ€์ผ๋ง ๊ทœ์น™ ์„ค์ •
az monitor autoscale create \
  --resource-group myshop-rg \
  --resource myshop-vmss \
  --resource-type Microsoft.Compute/virtualMachineScaleSets \
  --name autoscale-myshop \
  --min-count 2 \
  --max-count 20 \
  --count 4

# CPU ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ ์•„์›ƒ
az monitor autoscale rule create \
  --resource-group myshop-rg \
  --autoscale-name autoscale-myshop \
  --condition "Percentage CPU > 70 avg 5m" \
  --scale out 2

# CPU ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ ์ธ
az monitor autoscale rule create \
  --resource-group myshop-rg \
  --autoscale-name autoscale-myshop \
  --condition "Percentage CPU < 30 avg 5m" \
  --scale in 1

๐Ÿ”ถ Google Cloud Platform ๊ตฌํ˜„

Cloud Load Balancing + Managed Instance Groups:

# ์ธ์Šคํ„ด์Šค ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ
gcloud compute instance-templates create myshop-template \
  --machine-type=n1-standard-2 \
  --image-family=ubuntu-2004-lts \
  --image-project=ubuntu-os-cloud \
  --boot-disk-size=20GB \
  --tags=http-server \
  --metadata-from-file startup-script=startup.sh

# ๊ด€๋ฆฌํ˜• ์ธ์Šคํ„ด์Šค ๊ทธ๋ฃน ์ƒ์„ฑ
gcloud compute instance-groups managed create myshop-mig \
  --base-instance-name=myshop-vm \
  --template=myshop-template \
  --size=2 \
  --zone=asia-northeast3-a

# ์˜คํ† ์Šค์ผ€์ผ๋ง ์„ค์ •
gcloud compute instance-groups managed set-autoscaling myshop-mig \
  --zone=asia-northeast3-a \
  --min-num-replicas=2 \
  --max-num-replicas=20 \
  --target-cpu-utilization=0.6 \
  --cool-down-period=60

# HTTP ํ—ฌ์Šค์ฒดํฌ ์ƒ์„ฑ
gcloud compute health-checks create http myshop-health-check \
  --port=3000 \
  --request-path=/health \
  --check-interval=30s \
  --timeout=10s \
  --unhealthy-threshold=3 \
  --healthy-threshold=2

# ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค ์ƒ์„ฑ
gcloud compute backend-services create myshop-backend \
  --protocol=HTTP \
  --health-checks=myshop-health-check \
  --global

# ์ธ์Šคํ„ด์Šค ๊ทธ๋ฃน์„ ๋ฐฑ์—”๋“œ์— ์ถ”๊ฐ€
gcloud compute backend-services add-backend myshop-backend \
  --instance-group=myshop-mig \
  --instance-group-zone=asia-northeast3-a \
  --balancing-mode=UTILIZATION \
  --max-utilization=0.8 \
  --global

# URL ๋งต ์ƒ์„ฑ
gcloud compute url-maps create myshop-url-map \
  --default-service=myshop-backend

# HTTP ํ”„๋ก์‹œ ์ƒ์„ฑ
gcloud compute target-http-proxies create myshop-http-proxy \
  --url-map=myshop-url-map

# ์ „์—ญ ์ „๋‹ฌ ๊ทœ์น™ ์ƒ์„ฑ (๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ IP)
gcloud compute forwarding-rules create myshop-forwarding-rule \
  --global \
  --target-http-proxy=myshop-http-proxy \
  --ports=80

๐Ÿณ Kubernetes ๊ธฐ๋ฐ˜ ๊ตฌํ˜„

์š”์ฆ˜ ๋Œ€์„ธ๋Š” Kubernetes์•ผ! ํด๋ผ์šฐ๋“œ ์ข…์†์„ฑ ์—†์ด ์–ด๋””์„œ๋“  ์‹คํ–‰ ๊ฐ€๋Šฅํ•ด. ๐Ÿš€

# Deployment ์„ค์ • (deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myshop-deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myshop
  template:
    metadata:
      labels:
        app: myshop
    spec:
      containers:
      - name: myshop
        image: myshop:latest
        ports:
        - containerPort: 3000
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
---
# HorizontalPodAutoscaler ์„ค์ •
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myshop-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myshop-deployment
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
      - type: Pods
        value: 4
        periodSeconds: 30
      selectPolicy: Max
---
# Service ์„ค์ • (LoadBalancer)
apiVersion: v1
kind: Service
metadata:
  name: myshop-service
spec:
  type: LoadBalancer
  selector:
    app: myshop
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
---
# Ingress ์„ค์ • (L7 ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myshop-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - myshop.com
    secretName: myshop-tls
  rules:
  - host: myshop.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myshop-service
            port:
              number: 80
๐ŸŽฏ Kubernetes์˜ ์žฅ์ :
โœ… ํด๋ผ์šฐ๋“œ ์ค‘๋ฆฝ์ : AWS, Azure, GCP ์–ด๋””์„œ๋“  ๋™์ผํ•˜๊ฒŒ ์ž‘๋™
โœ… ์„ ์–ธ์  ์„ค์ •: YAML๋กœ ์ธํ”„๋ผ๋ฅผ ์ฝ”๋“œ๋กœ ๊ด€๋ฆฌ
โœ… ์ž๋™ ๋ณต๊ตฌ: Pod๊ฐ€ ์ฃฝ์œผ๋ฉด ์ž๋™์œผ๋กœ ์žฌ์‹œ์ž‘
โœ… ๋กค๋ง ์—…๋ฐ์ดํŠธ: ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ธฐ๋ณธ ์ง€์›
โœ… ์„œ๋น„์Šค ๋ฉ”์‹œ: Istio ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ํ™œ์šฉ ๊ฐ€๋Šฅ
โœ… ๋น„์šฉ ํšจ์œจ: ๋ฆฌ์†Œ์Šค ํ™œ์šฉ๋ฅ  ๊ทน๋Œ€ํ™”

๐Ÿ”ง ์˜จํ”„๋ ˆ๋ฏธ์Šค ํ™˜๊ฒฝ ๊ตฌํ˜„

ํด๋ผ์šฐ๋“œ๋ฅผ ๋ชป ์“ฐ๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด? ์˜จํ”„๋ ˆ๋ฏธ์Šค์—์„œ๋„ ๊ฐ€๋Šฅํ•ด! ๐Ÿ’ช

HAProxy + Keepalived ์กฐํ•ฉ:

# HAProxy ์„ค์ • (/etc/haproxy/haproxy.cfg)
global
    log /dev/log local0
    maxconn 4096
    user haproxy
    group haproxy
    daemon

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000

frontend http_front
    bind *:80
    stats uri /haproxy?stats
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    
    server web1 192.168.1.101:3000 check inter 2000 rise 2 fall 3
    server web2 192.168.1.102:3000 check inter 2000 rise 2 fall 3
    server web3 192.168.1.103:3000 check inter 2000 rise 2 fall 3
    server web4 192.168.1.104:3000 check inter 2000 rise 2 fall 3 backup

์ˆ˜๋™ ์Šค์ผ€์ผ๋ง ์Šคํฌ๋ฆฝํŠธ:

#!/bin/bash
# auto_scale.sh - ๊ฐ„๋‹จํ•œ ์˜คํ† ์Šค์ผ€์ผ๋ง ์Šคํฌ๋ฆฝํŠธ

THRESHOLD_HIGH=70
THRESHOLD_LOW=30
MAX_SERVERS=10
MIN_SERVERS=2

while true; do
  # ํ˜„์žฌ CPU ์‚ฌ์šฉ๋ฅ  ํ™•์ธ
  CPU_USAGE=$(ssh web1 "top -bn1 | grep 'Cpu(s)' | awk '{print \$2}' | cut -d'%' -f1")
  ACTIVE_SERVERS=$(grep "^    server web" /etc/haproxy/haproxy.cfg | grep -v backup | wc -l)
  
  echo "[$(date)] CPU: ${CPU_USAGE}%, ํ™œ์„ฑ ์„œ๋ฒ„: ${ACTIVE_SERVERS}๋Œ€"
  
  # ์Šค์ผ€์ผ ์•„์›ƒ ์กฐ๊ฑด
  if (( $(echo "$CPU_USAGE > $THRESHOLD_HIGH" | bc -l) )) && [ $ACTIVE_SERVERS -lt $MAX_SERVERS ]; then
    echo "๐Ÿš€ ์„œ๋ฒ„ ์ถ”๊ฐ€ ์ค‘..."
    NEXT_SERVER=$((ACTIVE_SERVERS + 1))
    
    # ์ƒˆ VM ์‹œ์ž‘
    virsh start web${NEXT_SERVER}
    sleep 30
    
    # HAProxy์— ์ถ”๊ฐ€
    sed -i "/backend http_back/a\    server web${NEXT_SERVER} 192.168.1.1${NEXT_SERVER}:3000 check" /etc/haproxy/haproxy.cfg
    systemctl reload haproxy
    
    echo "โœ… web${NEXT_SERVER} ์ถ”๊ฐ€ ์™„๋ฃŒ"
  fi
  
  # ์Šค์ผ€์ผ ์ธ ์กฐ๊ฑด
  if (( $(echo "$CPU_USAGE < $THRESHOLD_LOW" | bc -l) )) && [ $ACTIVE_SERVERS -gt $MIN_SERVERS ]; then
    echo "โž– ์„œ๋ฒ„ ์ œ๊ฑฐ ์ค‘..."
    
    # HAProxy์—์„œ ์ œ๊ฑฐ
    sed -i "/server web${ACTIVE_SERVERS}/d" /etc/haproxy/haproxy.cfg
    systemctl reload haproxy
    
    sleep 60  # ๋“œ๋ ˆ์ธ ์‹œ๊ฐ„
    
    # VM ์ข…๋ฃŒ
    virsh shutdown web${ACTIVE_SERVERS}
    
    echo "โœ… web${ACTIVE_SERVERS} ์ œ๊ฑฐ ์™„๋ฃŒ"
  fi
  
  sleep 60  # 1๋ถ„๋งˆ๋‹ค ์ฒดํฌ
done
โš ๏ธ ์˜จํ”„๋ ˆ๋ฏธ์Šค์˜ ํ•œ๊ณ„:
โ€ข ๋ฌผ๋ฆฌ ์„œ๋ฒ„ ์ˆ˜์— ์ œํ•œ์ด ์žˆ์–ด ์™„์ „ํ•œ ์˜คํ† ์Šค์ผ€์ผ๋ง ์–ด๋ ค์›€
โ€ข ํ•˜๋“œ์›จ์–ด ์žฅ์•  ์‹œ ์ˆ˜๋™ ๋Œ€์‘ ํ•„์š”
โ€ข ์ดˆ๊ธฐ ํˆฌ์ž ๋น„์šฉ์ด ๋†’์Œ
โ€ข ์œ ์ง€๋ณด์ˆ˜ ์ธ๋ ฅ ํ•„์š”

์ถ”์ฒœ: ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ํด๋ผ์šฐ๋“œ ์ „๋žต!
ํ‰์ƒ์‹œ๋Š” ์˜จํ”„๋ ˆ๋ฏธ์Šค, ํ”ผํฌ ์‹œ๊ฐ„์—๋งŒ ํด๋ผ์šฐ๋“œ ๋ฒ„์ŠคํŒ… ๐Ÿš€

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌ: ํ•ต์‹ฌ ์š”์•ฝ ๋ฐ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐Ÿ‘
์ด์ œ ์‡ผํ•‘๋ชฐ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ์— ์™„๋ฒฝํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์‹์„ ๊ฐ–์ท„์–ด.
๋งˆ์ง€๋ง‰์œผ๋กœ ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! ๐Ÿ“

๐ŸŽฏ ํ•ต์‹ฌ ๊ฐœ๋… ์š”์•ฝ

1๏ธโƒฃ ์˜คํ† ์Šค์ผ€์ผ๋ง (Auto Scaling)
โ€ข ํŠธ๋ž˜ํ”ฝ์— ๋”ฐ๋ผ ์„œ๋ฒ„๋ฅผ ์ž๋™์œผ๋กœ ๋Š˜๋ฆฌ๊ณ  ์ค„์ด๋Š” ๊ธฐ์ˆ 
โ€ข ์ˆ˜ํ‰ ํ™•์žฅ(Scale Out)์ด ์‡ผํ•‘๋ชฐ์— ์ตœ์ 
โ€ข ๋Œ€์ƒ ์ถ”์  ์ •์ฑ… ์ถ”์ฒœ (CPU 50% ์œ ์ง€)
โ€ข ์ฟจ๋‹ค์šด ์‹œ๊ฐ„ ์ตœ์†Œ 5๋ถ„ ์„ค์ •
โ€ข ์ตœ์†Œ/์ตœ๋Œ€ ์šฉ๋Ÿ‰ ๋ฐ˜๋“œ์‹œ ์„ค์ •

2๏ธโƒฃ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ (Load Balancing)
โ€ข ํŠธ๋ž˜ํ”ฝ์„ ์—ฌ๋Ÿฌ ์„œ๋ฒ„๋กœ ๋ถ„์‚ฐํ•˜๋Š” ๊ธฐ์ˆ 
โ€ข L7 ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํ•„์ˆ˜ (URL ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…)
โ€ข ๋ผ์šด๋“œ ๋กœ๋นˆ + ์ตœ์†Œ ์—ฐ๊ฒฐ ํ˜ผํ•ฉ ์ถ”์ฒœ
โ€ข ํ—ฌ์Šค์ฒดํฌ๋กœ ์žฅ์•  ์„œ๋ฒ„ ์ž๋™ ์ œ์™ธ
โ€ข SSL ํ„ฐ๋ฏธ๋„ค์ด์…˜์œผ๋กœ ์„œ๋ฒ„ ๋ถ€๋‹ด ๊ฐ์†Œ

3๏ธโƒฃ ์„ธ์…˜ ๊ด€๋ฆฌ
โ€ข Redis ์ค‘์•™ ์„ธ์…˜ ์Šคํ† ์–ด ๋˜๋Š” JWT ํ† ํฐ
โ€ข ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ ์„ธ์…˜์€ ์ ˆ๋Œ€ ๊ธˆ์ง€
โ€ข ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋„ Redis ๋˜๋Š” DB์— ์ €์žฅ
โ€ข ์„ธ์…˜ ๋งŒ๋ฃŒ ์ •์ฑ… ๋ช…ํ™•ํžˆ ์„ค์ •

4๏ธโƒฃ ์„ฑ๋Šฅ ์ตœ์ ํ™”
โ€ข ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™” (์ธ๋ฑ์Šค, N+1 ํ•ด๊ฒฐ)
โ€ข ๋‹ค์ธต ์บ์‹ฑ ์ „๋žต (Redis, CDN)
โ€ข ์ด๋ฏธ์ง€ ์ตœ์ ํ™” (WebP, ๋ฆฌ์‚ฌ์ด์ง•)
โ€ข API ์‘๋‹ต ์••์ถ• (gzip)
โ€ข ์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜

5๏ธโƒฃ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์•Œ๋ฆผ
โ€ข ๊ณจ๋“  ์‹œ๊ทธ๋„ ์ง‘์ค‘ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข ์•Œ๋ฆผ ๋ ˆ๋ฒจ ๊ตฌ๋ถ„ (Critical, Warning, Info)
โ€ข ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ•
โ€ข ์ž๋™ ๋ณต๊ตฌ ์„ค์ •
โ€ข ์ •๊ธฐ ๋ฆฌํฌํŠธ ๋ฐ ๊ฐœ์„ 

โœ… ๊ตฌ์ถ• ์ „ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

1 ์ธํ”„๋ผ ์„ค๊ณ„
โ–ก ๊ฐ€์šฉ ์˜์—ญ 2๊ฐœ ์ด์ƒ ์‚ฌ์šฉ
โ–ก VPC ๋ฐ ์„œ๋ธŒ๋„ท ๊ตฌ์„ฑ
โ–ก ๋ณด์•ˆ ๊ทธ๋ฃน ๋ ˆ์ด์–ด๋ณ„ ์„ค์ •
โ–ก ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ L7 ์„ ํƒ
โ–ก ์˜คํ† ์Šค์ผ€์ผ๋ง ๊ทธ๋ฃน ์ƒ์„ฑ
โ–ก RDS ๋‹ค์ค‘ AZ ๋ฐฐํฌ
โ–ก Redis ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์„ฑ
2 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ค€๋น„
โ–ก ๋ฌด์ƒํƒœ(Stateless) ์•„ํ‚คํ…์ฒ˜
โ–ก ํ—ฌ์Šค์ฒดํฌ ์—”๋“œํฌ์ธํŠธ ๊ตฌํ˜„
โ–ก ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์„ค์ • ๊ด€๋ฆฌ
โ–ก ๋กœ๊ทธ ์ค‘์•™ํ™” (CloudWatch, ELK)
โ–ก ์—๋Ÿฌ ์ถ”์  (Sentry ๋“ฑ)
โ–ก ๋ฐฐํฌ ์ž๋™ํ™” (CI/CD)
3 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”
โ–ก ์Šฌ๋กœ์šฐ ์ฟผ๋ฆฌ ์ œ๊ฑฐ
โ–ก ์ธ๋ฑ์Šค ์ตœ์ ํ™”
โ–ก ์ฝ๊ธฐ ์ „์šฉ ๋ณต์ œ๋ณธ ๊ตฌ์„ฑ
โ–ก ์ปค๋„ฅ์…˜ ํ’€ ์„ค์ •
โ–ก ์ฟผ๋ฆฌ ์บ์‹ฑ ํ™œ์„ฑํ™”
โ–ก ์ •๊ธฐ ๋ฐฑ์—… ์„ค์ •
4 ์บ์‹ฑ ์ „๋žต
โ–ก Redis ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์ถ•
โ–ก ์บ์‹œ ํ‚ค ๋„ค์ด๋ฐ ๊ทœ์น™
โ–ก TTL ์ •์ฑ… ์ˆ˜๋ฆฝ
โ–ก ์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต
โ–ก CDN ์„ค์ • (CloudFront ๋“ฑ)
โ–ก ๋ธŒ๋ผ์šฐ์ € ์บ์‹ฑ ํ—ค๋”
5 ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ •
โ–ก CloudWatch ๋Œ€์‹œ๋ณด๋“œ
โ–ก ์•Œ๋ฆผ ์ฑ„๋„ ์„ค์ • (Slack, SMS)
โ–ก ์˜ˆ์‚ฐ ์•Œ๋ฆผ ์„ค์ •
โ–ก ๋กœ๊ทธ ๋ถ„์„ ๋„๊ตฌ
โ–ก APM ๋„๊ตฌ (New Relic, Datadog)
โ–ก ์ •๊ธฐ ๋ฆฌํฌํŠธ ์ž๋™ํ™”
6 ๋ณด์•ˆ ์„ค์ •
โ–ก HTTPS ์ธ์ฆ์„œ (Let's Encrypt, ACM)
โ–ก WAF ๊ทœ์น™ ์„ค์ •
โ–ก DDoS ๋ฐฉ์–ด (Shield)
โ–ก ๋ณด์•ˆ ํ—ค๋” ์„ค์ •
โ–ก ์ •๊ธฐ ๋ณด์•ˆ ๊ฐ์‚ฌ
โ–ก ์นจํˆฌ ํ…Œ์ŠคํŠธ
7 ๋น„์šฉ ์ตœ์ ํ™”
โ–ก ์˜ˆ์•ฝ ์ธ์Šคํ„ด์Šค ๊ตฌ๋งค
โ–ก ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค ํ™œ์šฉ
โ–ก ์Šค์ผ€์ค„ ์Šค์ผ€์ผ๋ง
โ–ก ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
โ–ก ๋น„์šฉ ๋ชจ๋‹ˆํ„ฐ๋ง ์„ค์ •
โ–ก ์›”๊ฐ„ ๋น„์šฉ ๋ฆฌ๋ทฐ
8 ์žฌํ•ด ๋ณต๊ตฌ ๊ณ„ํš
โ–ก ์ •๊ธฐ ๋ฐฑ์—… ์ž๋™ํ™”
โ–ก ๋ณต๊ตฌ ์ ˆ์ฐจ ๋ฌธ์„œํ™”
โ–ก ๋กค๋ฐฑ ๊ณ„ํš ์ˆ˜๋ฆฝ
โ–ก ์žฌํ•ด ๋ณต๊ตฌ ํ›ˆ๋ จ
โ–ก ๋‹ค์ค‘ ๋ฆฌ์ „ ๊ณ ๋ ค
โ–ก ๋น„์ƒ ์—ฐ๋ฝ๋ง

๐Ÿš€ ๋‹ค์Œ ๋‹จ๊ณ„

์ด์ œ ๊ธฐ๋ณธ์€ ๋งˆ์Šคํ„ฐํ–ˆ์–ด! ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด: ๐Ÿ“š

1. ๊ณ ๊ธ‰ ์ฃผ์ œ:
โ€ข ์„œ๋น„์Šค ๋ฉ”์‹œ (Istio, Linkerd)
โ€ข ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜ (Lambda, Cloud Functions)
โ€ข ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํŒจํ„ด
โ€ข ์ด๋ฒคํŠธ ๋“œ๋ฆฌ๋ธ ์•„ํ‚คํ…์ฒ˜
โ€ข CQRS ๋ฐ Event Sourcing

2. ์‹ค์Šต ํ”„๋กœ์ ํŠธ:
โ€ข ๊ฐ„๋‹จํ•œ ์‡ผํ•‘๋ชฐ ํ”„๋กœํ† ํƒ€์ž… ๊ตฌ์ถ•
โ€ข ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์ž‘์„ฑ
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์‹œ๋ณด๋“œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
โ€ข ๋น„์šฉ ์ตœ์ ํ™” ์‹œ๋ฎฌ๋ ˆ์ด์…˜
โ€ข ์žฅ์•  ๋Œ€์‘ ํ›ˆ๋ จ

3. ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฐธ์—ฌ:
โ€ข AWS/Azure/GCP ์‚ฌ์šฉ์ž ๊ทธ๋ฃน
โ€ข DevOps Korea ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข Stack Overflow ์งˆ๋ฌธ/๋‹ต๋ณ€
โ€ข ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ ์šด์˜
โ€ข ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ ๊ธฐ์—ฌ

๐Ÿ’ผ ์žฌ๋Šฅ๋„ท ํ™œ์šฉํ•˜๊ธฐ:
์ด๋Ÿฐ ๊ณ ๊ธ‰ ์ง€์‹์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ์ปจ์„คํŒ…์ด๋‚˜ ๊ฐ•์˜ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•ด๋ณด๋Š” ๊ฑด ์–ด๋•Œ?
"์‡ผํ•‘๋ชฐ ์ธํ”„๋ผ ๊ตฌ์ถ• ์ปจ์„คํŒ…", "AWS ์˜คํ† ์Šค์ผ€์ผ๋ง ์„ค์ • ๋Œ€ํ–‰", "์„ฑ๋Šฅ ์ตœ์ ํ™” ์ง„๋‹จ" ๊ฐ™์€ ์žฌ๋Šฅ์œผ๋กœ ์ˆ˜์ต๋„ ์ฐฝ์ถœํ•˜๊ณ  ๊ฒฝํ—˜๋„ ์Œ“์„ ์ˆ˜ ์žˆ์–ด! ๐Ÿ’ฐ

๋˜๋Š” ๋ฐ˜๋Œ€๋กœ ๋” ์ „๋ฌธ์ ์ธ ๋„์›€์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„๋ณผ ์ˆ˜๋„ ์žˆ์ง€.
์‹ค์ „ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์œผ๋‹ˆ๊นŒ ๋„์›€๋ฐ›๊ธฐ ์ข‹์•„! ๐Ÿค

๐ŸŽ‰ ๋งˆ์ง€๋ง‰ ํ•œ๋งˆ๋””

์นœ๊ตฌ์•ผ, ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ์ˆ˜๊ณ ํ–ˆ์–ด! ๐ŸŽŠ
์‡ผํ•‘๋ชฐ ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ๋Œ€์‘์€ ๋‹จ์ˆœํžˆ ๊ธฐ์ˆ ๋งŒ์˜ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ์•ผ.
์‚ฌ์šฉ์ž ๊ฒฝํ—˜, ๋น„์šฉ ํšจ์œจ, ์•ˆ์ •์„ฑ์„ ๋ชจ๋‘ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ์ข…ํ•ฉ ์˜ˆ์ˆ ์ด์ง€.

์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ด๊ฒ ์ง€๋งŒ, ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด
์–ด๋А์ƒˆ ๋„ˆ๋„ ์ธํ”„๋ผ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋˜์–ด ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช

๊ธฐ์–ตํ•ด:
โ€ข ์ž‘๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๊ธฐ
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์ธก์ •์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ •
โ€ข ์‹คํŒจ๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋ง๊ณ  ๋น ๋ฅด๊ฒŒ ๋ฐฐ์šฐ๊ธฐ
โ€ข ์ปค๋ฎค๋‹ˆํ‹ฐ์™€ ์†Œํ†ตํ•˜๋ฉฐ ์ง€์‹ ๊ณต์œ ํ•˜๊ธฐ

์ด ๊ธ€์ด ๋„ˆ์˜ ์‡ผํ•‘๋ชฐ ๊ฐœ๋ฐœ ์—ฌ์ •์— ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ผ! ๐Ÿš€
๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ๋Œ“๊ธ€๋กœ ๋ฌผ์–ด๋ด.
ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์ž! ๐ŸŒŸ

Happy Scaling! ๐ŸŽฏ

๐Ÿ“š ์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต์œ ํ•ด์ฃผ์„ธ์š”!
๐Ÿ’ฌ ์งˆ๋ฌธ์ด๋‚˜ ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค!
๐ŸŒŸ ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ๊ณต์ ์ธ ์‡ผํ•‘๋ชฐ ์šด์˜์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค!

โœ๏ธ ์ž‘์„ฑ์ผ: 2024๋…„ | ๐Ÿ“ ์นดํ…Œ๊ณ ๋ฆฌ: ํ”„๋กœ๊ทธ๋žจ๊ฐœ๋ฐœ > ์‡ผํ•‘๋ชฐ๊ด€๋ จ ๊ฐœ๋ฐœ
๐Ÿ”— ์žฌ๋Šฅ๋„ท ์ง€์‹์ธ์˜ ์ˆฒ์—์„œ ๋” ๋งŽ์€ ๊ฐœ๋ฐœ ์ง€์‹์„ ๋งŒ๋‚˜๋ณด์„ธ์š”!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0