FSD ์•„ํ‚คํ…์ฒ˜์™€ ํ•จ๊ป˜ ์ƒ๊ฐํ•ด๋ณด๊ธฐ

FSD ์™€ ๋ฐฐ๋Ÿด ํŒŒ์ผ

 

Vue3์—์„œ React.js๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋ฉด์„œ Feature-Sliced Design(FSD) ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„์ž…ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ณผ์ •์—์„œ ๋ฐฐ๋Ÿด ํŒŒ์ผ(barrel files)์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์ด ์ƒ๊ฒผ๊ณ , ์ตœ๊ทผ ๋ช‡ ๋…„ ์‚ฌ์ด์— ์ด์— ๋Œ€ํ•œ ๊ด€์ ์ด ๋งŽ์ด ๋ฐ”๋€Œ์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๋ฐฐ๋Ÿด ํŒŒ์ผ์ด๋ž€?

๋ฐฐ๋Ÿด ํŒŒ์ผ์€ ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์„ ํ•˜๋‚˜์˜ ํŒŒ์ผ(์ฃผ๋กœ index.js ๋˜๋Š” index.ts)์—์„œ ๋‹ค์‹œ ๋‚ด๋ณด๋‚ด๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ ์ฝ”๋“œ ์ •๋ฆฌ์™€ import ๋ฌธ์„ ๋‹จ์ˆœํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

 

๋ฐฐ๋Ÿด ํŒŒ์ผ ์ „ ํ›„

 

๋ฐฐ๋Ÿด ํŒŒ์ผ ์—†์ด:

// ๊ฐ ํŒŒ์ผ์—์„œ ๊ฐœ๋ณ„์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ 
import { Button } from "../components/Button"; 
import { TextField } from "../components/TextField"; 
import { Checkbox } from "../components/Checkbox";

 

๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ์‹œ:

// ํ•˜๋‚˜์˜ ๊ฒฝ๋กœ์—์„œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ 
import { Button, TextField, Checkbox } from "../components";

 

๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์‹ค์ œ ๊ตฌํ˜„

์ผ๋ฐ˜์ ์ธ ๋ฐฐ๋Ÿด ํŒŒ์ผ (components/index.ts):

// ์™€์ผ๋“œ์นด๋“œ ๋‚ด๋ณด๋‚ด๊ธฐ (๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ) 
export * from './Button'; 
export * from './TextField'; 
export * from './Checkbox'; 

// ๋˜๋Š” ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ (๊ถŒ์žฅ) 
export { Button } from './Button'; 
export { TextField } from './TextField'; 
export { Checkbox } from './Checkbox';

 

๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์„ฑ๋Šฅ ๋ฌธ์ œ

Marvin์˜ JavaScript ์ƒํƒœ๊ณ„ ์†๋„ ๊ฐœ์„ : ๋ฐฐ๋Ÿด ํŒŒ์ผ ๋ฌธ์ œ ๊ธ€์—์„œ ์ง€์ ํ•œ ๋ฐ”์™€ ๊ฐ™์ด, ๋ฐฐ๋Ÿด ํŒŒ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๋ชจ๋“ˆ ๊ทธ๋ž˜ํ”„ ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๋ฐฐ๋Ÿด ํŒŒ์ผ์ด ์ฆ๊ฐ€ํ• ์ˆ˜๋ก ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๊ฐ€ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
  2. ๋ถˆํ•„์š”ํ•œ ๋ชจ๋“ˆ ๋กœ๋”ฉ: ํŠนํžˆ ์™€์ผ๋“œ์นด๋“œ ๋‚ด๋ณด๋‚ด๊ธฐ(export * from)๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์‹ค์ œ๋กœ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ชจ๋“ˆ๊นŒ์ง€ ๋กœ๋“œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ชจ๋“ˆ ๊ทธ๋ž˜ํ”„ ๋น„๊ต

๋ฐฐ๋Ÿด ํŒŒ์ผ ์—†์ด:

App.js
โ”œโ”€โ”€ Button.js
โ”œโ”€โ”€ TextField.js
โ””โ”€โ”€ Checkbox.js

 

๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ์‹œ:

App.js
โ””โ”€โ”€ components/index.js
    โ”œโ”€โ”€ Button.js
    โ”œโ”€โ”€ TextField.js
    โ””โ”€โ”€ Checkbox.js

 

์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์˜ˆ์‹œ

๋ชจ๋“ˆ ์ˆ˜๋ฐฐ๋Ÿด ํŒŒ์ผ ์—†์Œ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ์ฐจ์ด

๋ชจ๋“ˆ ์ˆ˜ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์—†์Œ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ์ฐจ์ด
10๊ฐœ 150ms 180ms +20%
50๊ฐœ 450ms 630ms +40%
100๊ฐœ 820ms 1250ms +52%

 

์ฐธ๊ณ : ์ด ์ˆ˜์น˜๋Š” ์˜ˆ์‹œ์šฉ์ด๋ฉฐ, ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋‹ค์–‘ํ•œ ์š”์ธ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

FSD ์•„ํ‚คํ…์ฒ˜์—์„œ์˜ ๋ฐฐ๋Ÿด ํŒŒ์ผ

Feature-Sliced Design(FSD)์€ ๋ฐฐ๋Ÿด ํŒŒ์ผ๊ณผ ์œ ์‚ฌํ•œ ํŒจํ„ด์„ ์˜๋„์ ์œผ๋กœ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ˆœํžˆ ์ฝ”๋“œ ์ž‘์„ฑ์˜ ํŽธ์˜์„ฑ์„ ์œ„ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์•„ํ‚คํ…์ฒ˜์˜ ๊ตฌ์กฐ์™€ ๋ชฉ์ ์„ ๋ฐ˜์˜ํ•œ ๊ฒฐ์ •์ž…๋‹ˆ๋‹ค.

FSD๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ธฐ๋Šฅ ๋‹จ์œ„๋กœ ๋ถ„ํ• ํ•˜๊ณ  ๊ณ„์ธตํ™”๋œ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ์—์„œ ๊ฐ ๋ ˆ์ด์–ด(layer)์™€ ์Šฌ๋ผ์ด์Šค(slice)๋Š” ๋ช…ํ™•ํ•œ ์ฑ…์ž„๊ณผ ์—ญํ• ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ๋ฐฐ๋Ÿด ํŒŒ์ผ๊ณผ ์œ ์‚ฌํ•œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, FSD๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ ์„ ์–ป์Šต๋‹ˆ๋‹ค:

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

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

 

FSD์—์„œ์˜ ๋ฐฐ๋Ÿด ํŒŒ์ผ ํ™œ์šฉ - ์‹ค์ œ ๊ฒฝํ—˜๊ณผ ํŒ

FSD ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์— ๋”ฐ๋ฅด๋ฉด, ๋ฐฐ๋Ÿด ํŒŒ์ผ(Public API)์˜ ์ฃผ์š” ์ด์  ์ค‘ ํ•˜๋‚˜๋Š” ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ์ž…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด ์ด๋„ˆ์„œํด ์žฌ์ง์ž ๊ณผ์ •์—์„œ ๋งŒ๋‚œ ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž๋ถ„๊ป˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ธ์‚ฌ์ดํŠธ๋ฅผ ๊ณต์œ ํ•ด์ฃผ์…จ์–ด์š”:

"FSD๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋А๋‚€ ๋ฐ”๋กœ๋Š”, Public API๋ฅผ ํ™œ์šฉํ•˜๋Š” ์ฃผ๋œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๊ฐ€ ์œ ์ง€๋ณด์ˆ˜์— ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, entities/user์˜ ์Šคํ‚ค๋งˆ๊ฐ€ ์ผ๋ถ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ features/loginUser, features/registerUser ๋“ฑ์—์„œ import ๊ตฌ๋ฌธ์„ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง‘๋‹ˆ๋‹ค."


์ด๋Š” FSD์˜ ํ•ต์‹ฌ ์›์น™ ์ค‘ ํ•˜๋‚˜์ธ ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด์„ฑ ๊ด€๋ฆฌ์™€ ์ผ์น˜ํ•˜๋Š” ๊ด€์ ์ž…๋‹ˆ๋‹ค.

 

๋ฐฐ๋Ÿด ํŒŒ์ผ ์ƒ์„ฑ ์ž๋™ํ™”

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ ๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ ๋ฐฐ๋Ÿด ํŒŒ์ผ(Public API)์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์‹ค์šฉ์ ์ธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์œผ๋กœ, ๋ฐฐ๋Ÿด ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด:

pnpm api features/loginUser


์ด๋Ÿฌํ•œ CLI ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด features/loginUser ๋‚ด๋ถ€์˜ ๋ชจ๋“ˆ์„ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•˜๊ณ , ๋ช…์‹œ์ ์œผ๋กœ ๋‚ด๋ณด๋‚ด๋Š” ๋ฐฐ๋Ÿด ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ _๋กœ ์‹œ์ž‘ํ•˜๋Š” ํŒŒ์ผ์ด๋‚˜ ํด๋”๋Š” ๋‚ด๋ถ€์šฉ์œผ๋กœ ๊ฐ„์ฃผํ•˜์—ฌ ์ž๋™์œผ๋กœ ์ œ์™ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ์˜ ์ค‘์š”์„ฑ

์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ์™€์ผ๋“œ์นด๋“œ ๋‚ด๋ณด๋‚ด๊ธฐ(export * from './module')๋Š” ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ ์ด์Šˆ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ๋„ FSD์˜ ์ด์ ์„ ์ถฉ๋ถ„ํžˆ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// Good: ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ
export { LoginForm } from './LoginForm';
export { useAuth } from './useAuth';
export type { User } from './types';

์ด๋Ÿฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋ชจ๋“ˆ์˜ ๊ณต๊ฐœ API๋ฅผ ๋ช…ํ™•ํžˆ ์ •์˜ํ•˜๊ณ , ๋ถˆํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ๋ฐฉ์ง€ํ•˜๋ฉฐ, ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ์ข€ ๋” ์ƒ์„ธํ•œ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ์š”.

์Šคํฌ๋ฆฝํŠธ์— ๋Œ€ํ•œ ํด๋” ๊ตฌ์กฐ ์˜ˆ์‹œ:

project-root/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ features/
โ”‚   โ”‚   โ””โ”€โ”€ loginUser/
โ”‚   โ”‚       โ”œโ”€โ”€ ui/
โ”‚   โ”‚       โ”œโ”€โ”€ model/
โ”‚   โ”‚       โ””โ”€โ”€ index.ts (์ž๋™ ์ƒ์„ฑ๋  ํŒŒ์ผ)
โ”‚   โ””โ”€โ”€ ... (๋‹ค๋ฅธ FSD ํด๋”๋“ค)
โ”œโ”€โ”€ scripts/
โ”‚   โ””โ”€โ”€ generate-barrel.ts
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json

 

scripts/generate-barrel.ts ํŒŒ์ผ์˜ ๋‚ด์šฉ:

import fs from 'fs';
import path from 'path';

function generateBarrel(directory: string) {
  const files = fs.readdirSync(directory);
  const exports: string[] = [];

  files.forEach(file => {
    if (file.startsWith('_') || file === 'index.ts') return;

    const filePath = path.join(directory, file);
    const stats = fs.statSync(filePath);

    if (stats.isDirectory()) {
      exports.push(`export * from './${file}';`);
    } else if (file.endsWith('.ts') || file.endsWith('.tsx')) {
      const name = path.parse(file).name;
      exports.push(`export { ${name} } from './${name}';`);
    }
  });

  const content = exports.join('\n') + '\n';
  fs.writeFileSync(path.join(directory, 'index.ts'), content);
}

const targetDir = process.argv[2];
if (!targetDir) {
  console.error('Please specify a target directory');
  process.exit(1);
}

const fullPath = path.resolve(process.cwd(), targetDir);
generateBarrel(fullPath);
console.log(`Barrel file generated for ${fullPath}`);

 

package.json์— ์Šคํฌ๋ฆฝํŠธ ์ถ”๊ฐ€:

{
  "scripts": {
    "api": "ts-node scripts/generate-barrel.ts"
  }
}

์ด์ œ pnpm api features/loginUser ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, src/features/loginUser/index.ts ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค! ์•„๋ž˜๋ถ€ํ„ฐ๋Š” ์ข€ ๋” ์ผ๋ฐ˜์ ์ธ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ์š”.

 

FSD ๊ธฐ๋ณธ ํด๋” ๊ตฌ์กฐ:

src/
โ”œโ”€โ”€ app/        # ๊ธ€๋กœ๋ฒŒ ์„ค์ •, ์Šคํƒ€์ผ, ํ”„๋กœ๋ฐ”์ด๋”
โ”œโ”€โ”€ processes/  # ๋น„์ฆˆ๋‹ˆ์Šค ํ”„๋กœ์„ธ์Šค
โ”œโ”€โ”€ pages/      # ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ
โ”œโ”€โ”€ widgets/    # ๋ณตํ•ฉ UI ๋ธ”๋ก
โ”œโ”€โ”€ features/   # ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜
โ”œโ”€โ”€ entities/   # ๋น„์ฆˆ๋‹ˆ์Šค ์—”ํ‹ฐํ‹ฐ
โ””โ”€โ”€ shared/     # ๊ณต์œ  ์œ ํ‹ธ๋ฆฌํ‹ฐ, UI ํ‚คํŠธ

 

FSD์—์„œ์˜ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ์˜ˆ์‹œ

features/auth/index.ts (์ ์ ˆํ•œ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ):

// ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ - ๊ณต๊ฐœ API๋ฅผ ๋ช…ํ™•ํžˆ ์ •์˜
export { LoginForm } from './ui/LoginForm';
export { useAuth } from './model/useAuth';
export type { AuthUser } from './model/types';

 

๊ท ํ˜• ์žกํžŒ ์ ‘๊ทผ๋ฒ•: ์‹ค์ œ ๊ตฌํ˜„ ์˜ˆ์‹œ

1. ๊ตฌ์กฐํ™”๋œ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ:

features/
โ”œโ”€โ”€ auth/
โ”‚   โ”œโ”€โ”€ ui/
โ”‚   โ”‚   โ”œโ”€โ”€ LoginForm.tsx
โ”‚   โ”‚   โ””โ”€โ”€ RegisterForm.tsx
โ”‚   โ”œโ”€โ”€ model/
โ”‚   โ”‚   โ”œโ”€โ”€ useAuth.ts
โ”‚   โ”‚   โ””โ”€โ”€ types.ts
โ”‚   โ””โ”€โ”€ index.ts  # ์•„ํ‚คํ…์ฒ˜ ๊ฒฝ๊ณ„์—๋งŒ ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ
โ””โ”€โ”€ userProfile/
    โ”œโ”€โ”€ ui/
    โ”œโ”€โ”€ model/
    โ””โ”€โ”€ index.ts

 

2. ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ:

// features/auth/index.ts
// ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ๋กœ ๊ณต๊ฐœ API ์ •์˜
export { LoginForm } from './ui/LoginForm';
export { RegisterForm } from './ui/RegisterForm';
export { useAuth } from './model/useAuth';
export type { User, AuthCredentials } from './model/types';

 

3. FSD ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™์— ๋”ฐ๋ฅธ ๊ฐ€์ ธ์˜ค๊ธฐ:

// ์ž˜๋ชป๋œ ๋ฐฉ์‹: ๋ ˆ์ด์–ด ์šฐํšŒ
import { Button } from '@/shared/ui/Button';

// ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ์‹: ์•„ํ‚คํ…์ฒ˜ ๊ฒฝ๊ณ„ ์กด์ค‘
import { Button } from '@/shared/ui';

 

4. ์‹ค์ œ React ์ปดํฌ๋„ŒํŠธ ์˜ˆ์‹œ:

// features/auth/ui/LoginForm.tsx
import { useState } from 'react';
import { Button, TextField } from '@/shared/ui';
import { useAuth } from '../model/useAuth';
import type { AuthCredentials } from '../model/types';

export const LoginForm = () => {
  const [credentials, setCredentials] = useState({
    email: '',
    password: ''
  });
  const { login, isLoading } = useAuth();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    login(credentials);
  };

  return (
    
      <TextField
        label="Email"
        value={credentials.email}
        onChange={(e) => setCredentials(prev => ({
          ...prev,
          email: e.target.value
        }))}
      />
      <TextField
        type="password"
        label="Password"
        value={credentials.password}
        onChange={(e) => setCredentials(prev => ({
          ...prev,
          password: e.target.value
        }))}
      />
      
        {isLoading ? 'Logging in...' : 'Login'}
      
    
  );
};

 

ํ˜„๋Œ€์  ๋„๊ตฌ์˜ ์˜ํ–ฅ

์ตœ์‹  ๋นŒ๋“œ ๋„๊ตฌ๋“ค์€ ๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ผ๋ถ€ ์™„ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

 

๋ฒˆ๋“ค๋Ÿฌ๋ณ„ ์ตœ์ ํ™” ์„ค์ •:

Vite ์„ค์ • ์˜ˆ์‹œ:

// vite.config.js
export default {
  build: {
    target: 'esnext',
    minify: 'esbuild',
    rollupOptions: {
      output: {
        manualChunks(id) {
          // ์ตœ์ ํ™”๋œ ์ฒญํฌ ์„ค์ •
        }
      }
    }
  }
}

 

Webpack ์„ค์ • ์˜ˆ์‹œ:

// webpack.config.js
module.exports = {
  optimization: {
    usedExports: true, // ํŠธ๋ฆฌ ์‰์ดํ‚น ํ™œ์„ฑํ™”
    moduleIds: 'deterministic',
    splitChunks: {
      chunks: 'all',
    }
  }
}

 

์„ฑ๋Šฅ ์ธก์ • - ์‹ค์‹œ๊ฐ„ ํ”„๋กœ์ ํŠธ ๋ชจ๋‹ˆํ„ฐ๋ง

// build-analyzer.ts
import { performance } from 'perf_hooks';

export function measureBuildTime(taskName: string, task: () => void) {
  const startTime = performance.now();
  task();
  const endTime = performance.now();
  console.log(`Task "${taskName}" took ${endTime - startTime}ms`);
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
measureBuildTime('Module compilation', () => {
  // ๋นŒ๋“œ ๋กœ์ง
});

 

๊ฒฐ๋ก  - ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๋ณ„ ๊ถŒ์žฅ ์ ‘๊ทผ๋ฒ•

 

์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ (10,000์ค„ ์ดํ•˜)

src/
โ”œโ”€โ”€ components/
โ”‚   โ””โ”€โ”€ index.ts  # ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โ”œโ”€โ”€ hooks/
โ”‚   โ””โ”€โ”€ index.ts  # ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
โ””โ”€โ”€ utils/
    โ””โ”€โ”€ index.ts  # ๋ฐฐ๋Ÿด ํŒŒ์ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

 

์ค‘๊ฐ„ ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ (10,000~50,000์ค„)

src/
โ”œโ”€โ”€ features/
โ”‚   โ”œโ”€โ”€ feature1/
โ”‚   โ”‚   โ””โ”€โ”€ index.ts  # ์•„ํ‚คํ…์ฒ˜ ๊ฒฝ๊ณ„์—๋งŒ ๋ฐฐ๋Ÿด ํŒŒ์ผ
โ”‚   โ””โ”€โ”€ feature2/
โ”‚       โ””โ”€โ”€ index.ts
โ””โ”€โ”€ shared/
    โ”œโ”€โ”€ ui/
    โ”‚   โ””โ”€โ”€ index.ts  # ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ ์‚ฌ์šฉ
    โ””โ”€โ”€ lib/
        โ””โ”€โ”€ index.ts  # ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ ์‚ฌ์šฉ

 

๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ (50,000์ค„ ์ด์ƒ)

src/
โ”œโ”€โ”€ app/
โ”œโ”€โ”€ processes/
โ”œโ”€โ”€ pages/
โ”‚   โ””โ”€โ”€ index.ts  # ์—„๊ฒฉํ•˜๊ฒŒ ์ œํ•œ๋œ ๋ฐฐ๋Ÿด ํŒŒ์ผ
โ”œโ”€โ”€ widgets/
โ”‚   โ””โ”€โ”€ index.ts  # ์—„๊ฒฉํ•˜๊ฒŒ ์ œํ•œ๋œ ๋ฐฐ๋Ÿด ํŒŒ์ผ
โ”œโ”€โ”€ features/
โ”‚   โ””โ”€โ”€ [๊ฐ ๊ธฐ๋Šฅ๋ณ„]/
โ”‚       โ””โ”€โ”€ index.ts  # ๊ณต๊ฐœ API๋งŒ ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ
โ”œโ”€โ”€ entities/
โ”‚   โ””โ”€โ”€ [๊ฐ ์—”ํ‹ฐํ‹ฐ๋ณ„]/
โ”‚       โ””โ”€โ”€ index.ts  # ๊ณต๊ฐœ API๋งŒ ๋ช…์‹œ์  ๋‚ด๋ณด๋‚ด๊ธฐ
โ””โ”€โ”€ shared/
    โ””โ”€โ”€ [๊ฐ ๋ชจ๋“ˆ๋ณ„]/
        โ””โ”€โ”€ index.ts  # ์„ฑ๋Šฅ ์ธก์ • ํ›„ ๊ฒฐ์ •
 

 

๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์‚ฌ์šฉ์€ ๊ฒฐ๊ตญ ๊ฐœ๋ฐœ ๊ฒฝํ—˜๊ณผ ์„ฑ๋Šฅ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ์ฐพ๋Š” ๋ฌธ์ œ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

FSD ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋„์ž…ํ•œ๋‹ค๋ฉด, ๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์žฅ๋‹จ์ ์„ ์ธ์‹ํ•˜๊ณ  ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ์™€ ํŒ€์˜ ํ•„์š”์— ๋งž๊ฒŒ ์ ์ ˆํžˆ ์กฐ์ ˆํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•  ๊ฒƒ ๊ฐ™์•„์š”.

๋ช…ํ™•ํ•œ ์•„ํ‚คํ…์ฒ˜ ๊ฒฝ๊ณ„, ๋ช…์‹œ์ ์ธ ๋‚ด๋ณด๋‚ด๊ธฐ, ๊ทธ๋ฆฌ๊ณ  ํ˜„๋Œ€์ ์ธ ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•œ๋‹ค๋ฉด, ๋ฐฐ๋Ÿด ํŒŒ์ผ์˜ ์ด์ ์„ ์ทจํ•˜๋ฉด์„œ๋„ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์™œ ์ธํ”„๋ผ์™€ ๋ฐ๋ธŒ์˜ต์Šค๋ฅผ ๊ณต๋ถ€ํ•˜๊ฒŒ ๋˜์—ˆ๋‚˜์š”? ๐Ÿค”

์•ˆ๋…•ํ•˜์„ธ์š”, ์˜ค๋Š˜์€ ์ธํ”„๋ผ์™€ ๋ฐ๋ธŒ์˜ต์Šค์— ๊ด€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ์ ์–ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. (์—ฐํœด๋ผ ์˜ค๋žœ๋งŒ์— ์ ์–ด๋ด…๋‹ˆ๋‹ค! ์‚ผ์ผ์ ˆ ๋งŒ์„ธ!)

ํ˜„์žฌ ์ €๋Š” ํšŒ์‚ฌ์—์„œ ๋„์ปค์™€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ์—์„œ ์ž‘์—… ์ค‘์ด์—์š”. ํŒ€์›๋“ค๊ณผ ์›ํ™œํ•œ ์†Œํ†ต์„ ์œ„ํ•ด ์Šคํ„ฐ๋””์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ•™์Šต์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ๋‹จ์ˆœํžˆ ๊ฐœ๋…๋งŒ ๊ณต๋ถ€ํ•˜๋Š” ๊ฒƒ์€ ์•„์‰ฌ์› ์–ด์š”.

๊ทธ๋ž˜์„œ ์‹ค์ œ๋กœ ์ธํ”„๋ผ๋ฅผ ์šด์˜ํ•˜๋Š” ๋ฐ๋ธŒ์˜ต์Šค ํŒ€๊ณผ์˜ ํ˜‘์—…์ด๋ผ๋Š” ๊ด€์ ์—์„œ ์ •๋ฆฌํ•ด๋ณด๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ ์ œ๊ฐ€ ๊ฐœ์ธ์ ์œผ๋กœ ์šด์˜ ์ค‘์ธ ์ƒ์šฉ ํ”„๋กœ์ ํŠธ์—์„œ๋„ ์ˆ˜์˜์‚ฌ๋ถ„๋“ค์˜ ์šด์˜๋น„์šฉ ์ ˆ๊ฐ์„ ์œ„ํ•ด ์ธํ”„๋ผ ํ•™์Šต์ด ํฐ ๋„์›€์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์–ด์š”. ์ด๋Ÿฐ ์‹ค์งˆ์ ์ธ ํ•„์š”์„ฑ์ด ๋” ๊นŠ์€ ํ•™์Šต์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

์ด ๊ธ€์€ ํ˜„์žฌ ์ œ๊ฒŒ ํ•„์š”ํ•œ ๋‚ด์šฉ๊ณผ ์ œ ์ฃผ๊ด€์ด ๋“ค์–ด๊ฐ„ ์ •๋ฆฌ๋ณธ์ด๋‹ˆ, ์ฝ์œผ์‹œ๋Š” ๋ถ„๋“ค๋„ ๊ผญ ๊ต์ฐจ๊ฒ€์ฆ์ด๋‚˜ ์ถ”๊ฐ€์ ์ธ ํ•™์Šต์„ ์ง„ํ–‰ํ•˜์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ ค์š”!


์ธํ”„๋ผ ์ง€์‹์˜ ์‹ค๋ฌด์  ๊ฐ€์น˜ ๐ŸŒ‰

  • ์ธํ”„๋ผ๋ฅผ ์ดํ•ดํ•˜๋ฉด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ๋ฐฐํฌ ๊ณผ์ •์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ์— ๋Šฅ์ˆ™ํ•ด์ง€๋ฉด ๋ฆฌ์†Œ์Šค ํ™•์žฅ๊ณผ ๋น„์šฉ ์ตœ์ ํ™”๋ฅผ ์†์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ๋„์ปค & ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋‹ค๋ฃจ๋ฉด ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ์ผ๊ด€๋œ ๊ฐœ๋ฐœ๊ณผ ์‹ ์†ํ•œ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ ธ ์ž‘์—… ํšจ์œจ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ผ์š”!

๋ฐ๋ธŒ์˜ต์Šค ์ฒ ํ•™ ๐Ÿ’ญ

  • ๋ฐ๋ธŒ์˜ต์Šค ์ฒ ํ•™์—์„œ๋Š” ๊ฐœ๋ฐœ์ด ์šด์˜๊นŒ์ง€ ํ•˜๋Š” ๊ฒƒ์„ ์›์น™์œผ๋กœ ํ•ด์š”.
  • ๊ฐœ๋ฐœ์ž๊ฐ€ ์šด์˜ํ™˜๊ฒฝ๊นŒ์ง€ ๊ด€์—ฌํ•˜๋Š” ๊ฒƒ์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ๋ฐ๋ธŒ์˜ต์Šค ์ฒ ํ•™์ด์—์š”.
  • ์ด๋•Œ ๋งŽ์ด ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ ์…‹ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋„์ปค์™€ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ˆ์š”.

๋„์ปค ์—ญ๋Ÿ‰์€ ์–ด๋А์ •๋„๊ฐ€ ํ•„์š”ํ• ๊นŒ์š”? ๐Ÿณ

  • ๊ฐœ๋ฐœ์ž๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ Docker ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒจํ‚ค์ง•, ๋ฐฐํฌ, ์‹คํ–‰ํ•˜๋Š” ์ „์ฒด ๊ณผ์ •์ธ ๋„์ปค๋ผ์ด์ง•์„ ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด์š”.
  • ์ปจํ…Œ์ด๋„ˆ ์ƒํ™ฉ์—์„œ์˜ ์ตœ์ ํ™”์™€ ์ผ๋ฐ˜์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ ์ƒํ™ฉ์—์„œ์˜ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.
    • ์˜ˆ: ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€๋Š” ๋ถˆ๋ณ€์„ฑ(immutability)์„ ๊ฐ€์ง€๋ฏ€๋กœ ๋กœ๊ทธ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ์ง€ ๊ณ ๋ฏผํ•ด์•ผ ํ•ด์š”.
    • ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ตœ์†Œํ™”: ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ๊ณผ ์ข…์†์„ฑ์„ ์ œ๊ฑฐํ•˜์—ฌ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ์•ผ ํ•ด์š”.
    • ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€๋กœ ๋งํฌ๋ฅผ ๊ฑธ์–ด์ค„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ(๋ณผ๋ฅจ)์„ ํ†ตํ•ด ์˜์†์  ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด์š”.
  • ๋„์ปค๋กœ ๊ฐ”์„ ๋•Œ ์–ด๋–ค ํŠน์„ฑ์ด ์žˆ๊ณ  ์–ด๋–ค ์‹์œผ๋กœ ์ตœ์ ํ™”ํ•ด์•ผ ํ• ์ง€ ์ด๋Ÿฐ ๊ฒƒ๋“ค์„ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ด์š”.
    • ์ด๋ฏธ์ง€ ๋ ˆ์ด์–ด ์ตœ์ ํ™” (์บ์‹ฑ ํ™œ์šฉ)
    • ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ ์‚ฌ์šฉ: ํ•˜๋‚˜์˜ Dockerfile์—์„œ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ ์ตœ์ข… ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, ๋นŒ๋“œ ๋„๊ตฌ๋‚˜ ์†Œ์Šค ์ฝ”๋“œ ์—†์ด ์‹คํ–‰ ํŒŒ์ผ๋งŒ ํฌํ•จ๋œ ์ž‘์€ ํฌ๊ธฐ์˜ ์ตœ์ข… ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์š”.
    • ๋น„๋ฃจํŠธ ์‚ฌ์šฉ์ž๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ํ•˜๊ธฐ
    • ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•œ ์„ค์ • ๊ด€๋ฆฌ

์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๋ฐฉ๋ฒ•์€? ๐Ÿ—๏ธ

  • ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹จ์ˆœํžˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•ด์„œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ๋ชจ๋†€๋ฆฌ์‹(Monolithic) ์ƒํƒœ๋กœ ๋ฐฐํฌํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์–ด์š”.
  • ๋„์ปค๋‚˜ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋ฅผ ๋‹ค๋ฃจ๋‹ค ๋ณด๋ฉด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ƒํƒœ๋กœ ์„ค๊ณ„ํ•˜๊ณ  ์ชผ๊ฐœ์•ผ ํ•  ๋•Œ๊ฐ€ ๋งŽ์•„์š”.
  • ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ ํŠน์„ฑ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์•„์š”.
    • ์„œ๋น„์Šค ๋ถ„๋ฆฌ์™€ ๋…๋ฆฝ์  ์Šค์ผ€์ผ๋ง
    • ์žฅ์•  ๊ฒฉ๋ฆฌ(Fault Isolation)
    • ๊ธฐ๋Šฅ๋ณ„ ๋ฐฐํฌ ๋ฐ ๋กค๋ฐฑ ์šฉ์ด์„ฑ
  • ์˜ˆ์‹œ)
    • ๋ชจ๋†€๋ฆฌ์‹ ์•„ํ‚คํ…์ฒ˜๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•˜๋‚˜์˜ ํฐ ์ฝ”๋“œ๋ฒ ์ด์Šค๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด ๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ๊ฐ€ ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ„๋‹จํ•ด์š”.
    • ๊ทธ๋Ÿฌ๋‚˜ ์•„๋ž˜์™€๊ฐ™์€ ์ด์œ ๋กœ ๋„์ปค๋‚˜ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์™€ ๊ฐ™์€ ์ปจํ…Œ์ด๋„ˆ ๊ธฐ์ˆ ์„ ๋‹ค๋ฃจ๋‹ค ๋ณด๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ์„ค๊ณ„ํ•˜๊ณ  ๋ถ„ํ• ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„์ ธ์š”.
      • ๋…๋ฆฝ์ ์ธ ๋ฐฐํฌ: ๊ฐ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์–ด, ์ „์ฒด ์‹œ์Šคํ…œ์„ ์ค‘๋‹จํ•˜์ง€ ์•Š๊ณ ๋„ ํŠน์ • ๊ธฐ๋Šฅ์„ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์–ด์š” (API ์„œ๋ฒ„, ํ”„๋ก ํŠธ์—”๋“œ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ๊ฐ์„ ๋…๋ฆฝ์ ์ธ ์ปจํ…Œ์ด๋„ˆ๋กœ ์šด์˜).
      • ๋ฆฌ์†Œ์Šค ํšจ์œจ์„ฑ: ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ ํŠน์„ฑ์ƒ ๊ฐ ์„œ๋น„์Šค์— ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ํ• ๋‹นํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ๋ฉ”๋ชจ๋ฆฌ์™€ CPU ์‚ฌ์šฉ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”.
      • ๊ธฐ์ˆ  ๋‹ค์–‘์„ฑ: ๊ฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋Š” ๋…๋ฆฝ์ ์ด๋ฏ€๋กœ, ์„œ๋น„์Šค๋ณ„๋กœ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๊ธฐ์ˆ  ์Šคํƒ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋„ ๋ฐฐํฌ์— ๊ด€์‹ฌ์„ ๊ฐ€์ ธ์•ผ ํ•ด์š”! ๐ŸŒ

๋‹น์žฅ ๋„์ปค๊นŒ์ง„ ์•„๋‹ˆ๋”๋ผ๋„, React.js๋ฅผ ๋‹ค๋ฃจ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค ์ค‘์— ๋ฐฐํฌ๋ฅผ ์™„์ „ํžˆ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋‚˜ ์ธํ”„๋ผํŒ€์— ๋„˜๊ธฐ๊ณ  ๊ด€์‹ฌ์„ ๋‘์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ ์ด๋Ÿฌ๋ฉด ๋งŽ์ด ์•„์‰ฝ์ž–์•„์š”?!

์•„๋ž˜์— ์ œ ๊ธฐ์ค€์„ ์ ์–ด๋†จ์–ด์š”.

  • FE๋„ ๋ฐฐํฌ๋ฅผ ํด๋ผ์šฐ๋“œ์ชฝ์— ํ•˜๋‹ค๋ณด๋‹ˆ ํด๋ผ์šฐ๋“œ ๊ธฐ์ˆ ๋“ค์„ ๋งŽ์ด ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ด์š”.
  • ์ด๋ฅผํ…Œ๋ฉด FE์ชฝ์—์„œ ๋‹ค๋ฃจ๋‹ค๋ณด๋ฉด Node.js๋กœ ๊ฐœ๋ฐœํ•˜๊ณ  ์„œ๋น™์„ ํ•˜๊ฒŒ ๋ผ์š”.
  • ์‹ค์ œ๋กœ ํด๋ผ์šฐ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด 'CFS3'๋ผ๊ณ  ๋งŽ์ด ์ด์•ผ๊ธฐ๋ฅผ ํ•˜๋Š”๋ฐ, ์ด๋Š” CloudFront ๋”ํ•˜๊ธฐ S3๋กœ ์Šคํƒœํ‹ฑํ•˜๊ฒŒ ํŒŒ์ผ์„ ๋นŒ๋“œํ•ด์„œ ์„œ๋น™ํ•œ๋‹ค๋Š” ๋œป์ด์—์š”.
  • ์ด๋Ÿฐ ๊ฒƒ๋“ค์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ๋ธŒ์˜ต์ŠคํŒ€๊ณผ ์ด์•ผ๊ธฐ๋ฅผ ํ•ด์•ผ ํ•˜๊ณ , ์ ์–ด๋„ ๋‚ด๊ฐ€ ๊ฐœ๋ฐœํ•˜๋Š” ์‹œ์Šคํ…œ์ด ์–ด๋–ค ํ˜•์ƒ์œผ๋กœ ๋ฐฐํฌ๋˜์–ด ์„œ๋น™๋  ๊ฒƒ์ธ์ง€ ๊ทธ ๊ตฌ์กฐ๋ฅผ ์•Œ์•„์•ผ ์ธํ”„๋ผํŒ€์ด๋‚˜ ๋ฐ๋ธŒ์˜ต์ŠคํŒ€, ํ˜น์€ ํด๋ผ์šฐ๋“œ ์—”์ง€๋‹ˆ์–ด์™€์˜ ๋Œ€ํ™”๊ฐ€ ํŽธํ•˜์‹ค ๊ฑฐ์˜ˆ์š”.

๊ทธ๋Ÿฌ๋‹ˆ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค๋„ ๋ฐฐํฌ ๊ณผ์ •๊ณผ ์ธํ”„๋ผ์— ๋Œ€ํ•ด ์•Œ๊ณ  ๊ณ„์…”์•ผ ์ข‹์Šต๋‹ˆ๋‹ค!!

๋ฐ๋ธŒ์˜ต์Šค ์—”์ง€๋‹ˆ์–ด์™€์˜ ํ˜‘์—…์€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์งˆ๊นŒ์š”? ๐Ÿค

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

๊ทธ๋Ÿผ ํ˜‘์—…์„ ์œ„ํ•ด ์•Œ์•„์•ผ ํ•  ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์ด๋ก ์€ ๋ฌด์—‡์ด ์žˆ์„๊นŒ์š”? โ˜ธ๏ธ

  • ์šด์˜ ์ธก๋ฉด์—์„œ ํ•„์š”ํ•œ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์ด๋ก ์ด ์žˆ๊ณ  ๊ฐœ๋ฐœ์ด๋‚˜ ๋ฐฐํฌ ์ธก๋ฉด์—์„œ ํ•„์š”ํ•œ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์ด๋ก ์ด ์žˆ์–ด์š”.
  • ๊ฐœ๋ฐœ์ž๋Š” ๊ฐœ๋ฐœ์ด๋‚˜ ๋ฐฐํฌ ์ธก๋ฉด์—์„œ ํ•„์š”ํ•œ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ์ด๋ก ์„ ๋งŽ์ด ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์ข‹์•„์š”.

๊ฐœ๋ฐœ์ž๊ฐ€ ์•Œ์•„์•ผ ํ•  ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฆฌ์†Œ์Šค ๐Ÿ“ฆ

  • ๊ฐœ๋ฐœ์ด๋‚˜ ๋ฐฐํฌ ์ธก๋ฉด์—์„œ ๋‹ค๋ฃจ๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฆฌ์†Œ์Šค๋“ค์€:
    • ํŒŒ๋“œ(Pod): ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฐํฌ ๋‹จ์œ„์˜ˆ์š” (์›๋ž˜ ํ‘œ๊ธฐ๋Š” Pod์ด๋ฉฐ ํ•œ๊ธ€๋กœ๋Š” 'ํŒŒ๋“œ'๋กœ ํ‘œ๊ธฐํ•ด์š”).
    • ์„œ๋น„์Šค(Service): ํŒŒ๋“œ ์ง‘ํ•ฉ์— ๋Œ€ํ•œ ๋‹จ์ผ ์ง„์ž…์ ์„ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒํ™” ๊ณ„์ธต์ด์—์š”.
      • ์„œ๋น„์Šค๋Š” ์—ฌ๋Ÿฌ ํŒŒ๋“œ์— ๊ฑธ์ณ ํŠธ๋ž˜ํ”ฝ์„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•ด์š”.
      • ClusterIP, NodePort, LoadBalancer, ExternalName ๋“ฑ ๋‹ค์–‘ํ•œ ์œ ํ˜•์ด ์žˆ์–ด์š”.
      • ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์™€ ๋‚ด๋ถ€ DNS๋ฅผ ํ†ตํ•ด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์š”.
      • ํŒŒ๋“œ์˜ ์ƒ์„ฑ, ์‚ญ์ œ, ์žฌ์‹œ์ž‘์— ๊ด€๊ณ„์—†์ด ์•ˆ์ •์ ์ธ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•ด์š”.
      • ๋ ˆ์ด๋ธ” ์…€๋ ‰ํ„ฐ๋ฅผ ํ†ตํ•ด ์–ด๋–ค ํŒŒ๋“œ๊ฐ€ ์„œ๋น„์Šค์— ํฌํ•จ๋ ์ง€ ๊ฒฐ์ •ํ•ด์š”.
    • ์ธ๊ทธ๋ ˆ์Šค(Ingress): ํด๋Ÿฌ์Šคํ„ฐ ์™ธ๋ถ€์—์„œ ๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ์˜ HTTP/HTTPS ๋ผ์šฐํŒ… ๊ทœ์น™ ๊ด€๋ฆฌ์˜ˆ์š”.
    • ์ธ๊ทธ๋ ˆ์Šค ์ปจํŠธ๋กค๋Ÿฌ(Ingress Controller): ์ธ๊ทธ๋ ˆ์Šค ๊ทœ์น™์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”.
  • ์ปจํŠธ๋กค ํ”Œ๋ ˆ์ธ ์˜์—ญ์€ ์•„์ง ๊นŠ๊ฒŒ ์•Œ ํ•„์š” ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์–ด์š”. ๊ธฐ๋ณธ์ ์ธ ๊ฒƒ๋งŒ ์•Œ๋ฉด ๋ฉ๋‹ˆ๋‹ค!
    • ๊ธฐ๋ณธ์ ์ธ๊ฒƒ์˜ ์˜ˆ์‹œ:
      • kube-apiserver๊ฐ€ ๋ฌด์Šจ ์ผ์„ ํ•˜๋Š”์ง€, kube-dns(CoreDNS)๊ฐ€ ๋ฌด์Šจ ์ผ์„ ํ•˜๋Š”์ง€ ์ •๋„์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋งŒ ์•Œ๋ฉด ๋ผ์š”.

๋ฐ๋ธŒ์˜ต์Šค/์ธํ”„๋ผ ์ „๋ฌธ๊ฐ€๊ฐ€ ์•Œ์•„์•ผ ํ•  ๊ฒƒ ๐Ÿ”

  • ์ธํ”„๋ผ๋ฅผ ์ „๋ฌธ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ฑฐ๋‚˜ ๋ฐ๋ธŒ์˜ต์Šค๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด ์ปจํŠธ๋กค ํ”Œ๋ ˆ์ธ ์˜์—ญ์—์„œ ๋‹ค๋ฃจ๋Š” 4๋Œ€ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ์ƒ์„ธํžˆ ์•Œ์•„์•ผ ํ•ด์š”.
    • kube-apiserver: API ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ์˜ˆ์š”.
    • etcd: ํด๋Ÿฌ์Šคํ„ฐ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ํ‚ค-๊ฐ’ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ˆ์š”.
    • kube-scheduler: ํŒŒ๋“œ๋ฅผ ๋…ธ๋“œ์— ํ• ๋‹นํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”.
    • kube-controller-manager: ๋‹ค์–‘ํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”.
  • IRSA(IAM Roles for Service Accounts) ๊ฐ™์€ ์ธ์ฆ ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ๋„ ์ž˜ ์•Œ์•„์•ผ ํ•ด์š”.
    • ์„œ๋น„์Šค ๊ณ„์ •์— ๋Œ€ํ•œ IAM ์—ญํ• ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋ณด์•ˆ ์ธ์ฆ ์ •๋ณด ์ฒด์ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด์—์š”.
  • GKE(Google Kubernetes Engine), AWS์˜ EKS(Elastic Kubernetes Service) ๋“ฑ ๊ฐ ํด๋ผ์šฐ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ๋“ค์˜ ํŠน์„ฑ์— ๋Œ€ํ•ด์„œ๋„ ์ž˜ ์•Œ์•„์•ผ ํ•ด์š”.

๋ฆฌ์†Œ์Šค ์„ค๊ณ„์™€ ๊ตฌ์„ฑ ๊ฒฐ์ • ๐Ÿ“

ํ˜„์žฌ ํ™˜๊ฒฝ

  • ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ OKD(OpenShift Kubernetes Distribution)๋ผ๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฐฐํฌํŒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์š”.
  • OKD๋Š” Red Hat์˜ ์˜คํ”ˆ์†Œ์Šค ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ”Œ๋žซํผ์œผ๋กœ, ๊ธฐ๋ณธ์ ์ธ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๊ธฐ๋Šฅ ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

OKD ๋‚ด๋ถ€ ๋ฆฌ์†Œ์Šค ์„ค๊ณ„

  • OKD ํ™˜๊ฒฝ์—์„œ ๋ฆฌ์†Œ์Šค ๊ตฌ์„ฑ์„ ์–ด๋–ป๊ฒŒ ํ• ์ง€ ์„ค๊ณ„ํ•˜์—ฌ ๋ฐ๋ธŒ์˜ต์Šค ํŒ€์— ๊ฐ€์ด๋“œ๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:
    • Routes vs Ingress: OKD๋Š” Route๋ผ๋Š” ๊ณ ์œ  ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์ด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HAProxy ๊ธฐ๋ฐ˜ ์ธ๊ทธ๋ ˆ์Šค ์ปจํŠธ๋กค๋Ÿฌ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•ด์š”.
    • ํ…œํ”Œ๋ฆฟ๊ณผ ์˜คํผ๋ ˆ์ดํ„ฐ: OKD์—์„œ ์ œ๊ณตํ•˜๋Š” ํ…œํ”Œ๋ฆฟ๊ณผ ์˜คํผ๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ๋ฅผ ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”.
    • ํ”„๋กœ์ ํŠธ์™€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค: OKD์˜ ํ”„๋กœ์ ํŠธ๋Š” ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋„ค์ž„์ŠคํŽ˜์ด์Šค์˜ ํ™•์žฅ ๊ฐœ๋…์œผ๋กœ, ์ถ”๊ฐ€์ ์ธ ๋ณด์•ˆ ๋ฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์š”.
    • DeploymentConfig: OKD์—์„œ๋Š” Deployment ๋Œ€์‹  DeploymentConfig๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋” ์„ธ๋ฐ€ํ•œ ๋ฐฐํฌ ์ „๋žต์„ ์ œ๊ณตํ•ด์š”.

์ปค์Šคํ…€ ๋ฆฌ์†Œ์Šค ๋ฐ ํ™•์žฅ ๊ตฌ์„ฑ

  • ํŠน์ • ์š”๊ตฌ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ถ”๊ฐ€ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
    • ์ปค์Šคํ…€ ๋ฆฌ์†Œ์Šค(CustomResources) ์ •์˜๋ฅผ ํ†ตํ•ด OKD์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ๋ฆฌ์†Œ์Šค ํƒ€์ž…์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.
    • ์Šคํ…Œ์ดํŠธํ’€์…‹(StatefulSet) ๊ตฌ์„ฑ์œผ๋กœ ์ƒํƒœ ์œ ์ง€๊ฐ€ ํ•„์š”ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(์˜ˆ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค)์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
    • ์„œ๋น„์Šค ๋ฉ”์‹œ(Service Mesh): OKD์—์„œ๋Š” Red Hat Service Mesh(Istio ๊ธฐ๋ฐ˜)๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹ ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ธ๊ทธ๋ ˆ์Šค ๋ฐ ํŠธ๋ž˜ํ”ฝ ๊ด€๋ฆฌ ์˜ต์…˜

  • OKD ํ™˜๊ฒฝ์—์„œ ๋‹ค์–‘ํ•œ ์ธ๊ทธ๋ ˆ์Šค ์˜ต์…˜์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
    • ๊ธฐ๋ณธ HAProxy ์ธ๊ทธ๋ ˆ์Šค: OKD๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HAProxy ๊ธฐ๋ฐ˜ ์ธ๊ทธ๋ ˆ์Šค ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ œ๊ณตํ•ด์š”.
    • NGINX ์ธ๊ทธ๋ ˆ์Šค ์ปจํŠธ๋กค๋Ÿฌ: ๊ณ ๋ถ€ํ•˜ ํŠธ๋ž˜ํ”ฝ ํ™˜๊ฒฝ์—์„œ๋Š” NGINX ์ธ๊ทธ๋ ˆ์Šค ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋Œ€์•ˆ์œผ๋กœ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์–ด์š”.
      • ์žฅ์ : ๊ณ ์„ฑ๋Šฅ, ์„ธ๋ฐ€ํ•œ ์„ค์ • ๊ฐ€๋Šฅ, ๊ด‘๋ฒ”์œ„ํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ ์ง€์›
      • ๋‹จ์ : ์ถ”๊ฐ€ ๊ตฌ์„ฑ ๋ฐ ๊ด€๋ฆฌ ํ•„์š”
    • API ๊ฒŒ์ดํŠธ์›จ์ด: Kong, Ambassador ๋“ฑ์˜ API ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ OKD ์œ„์— ์„ค์น˜ํ•˜์—ฌ ๊ณ ๊ธ‰ ๋ผ์šฐํŒ… ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

OKD์˜ ๋Œ€์•ˆ์ฑ… ๊ฒ€ํ† 

  • ํ˜„์žฌ OKD๋ฅผ ์‚ฌ์šฉ ์ค‘์ด์ง€๋งŒ, ํ•„์š”์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฐฐํฌํŒ๋„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:
    • EKS(Amazon Elastic Kubernetes Service): AWS ํ™˜๊ฒฝ์—์„œ ๊ด€๋ฆฌํ˜•์œผ๋กœ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋ฅผ ์šด์˜ํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ
      • ์žฅ์ : ๊ด€๋ฆฌ ๋ถ€๋‹ด ๊ฐ์†Œ, AWS ์„œ๋น„์Šค์™€์˜ ํ†ตํ•ฉ ์šฉ์ด
      • ๋‹จ์ : OKD์˜ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ๋“ค(CI/CD ํ†ตํ•ฉ, ๊ฐœ๋ฐœ์ž ์นดํƒˆ๋กœ๊ทธ ๋“ฑ) ๋ถ€์žฌ
    • GKE(Google Kubernetes Engine): Google Cloud์—์„œ ๊ด€๋ฆฌํ˜• ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋ฅผ ์›ํ•  ๊ฒฝ์šฐ
    • AKS(Azure Kubernetes Service): Microsoft Azure์—์„œ ๊ด€๋ฆฌํ˜• ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋ฅผ ์›ํ•  ๊ฒฝ์šฐ
    • Rancher: ๋‹ค์ค‘ ํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์˜ ๋Œ€์•ˆ

๊ฐœ๋ฐœ์ž์™€ ์šด์˜ ํ˜‘์—…

  • ๊ฐœ๋ฐœ ํŒ€์—์„œ๋„ ์ธํ”„๋ผ๋ฅผ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:
    • OKD์˜ ์›น ์ฝ˜์†”์„ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋“ค๋„ ๋ฐฐํฌ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ๊ณผ์ •์— ์ฐธ์—ฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
    • ๊ฐœ๋ฐœ์ž์™€ ์šด์˜ํŒ€์ด ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” OKD์˜ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ์ด ๊ฐ€๋Šฅํ•ด์š”.
    • ๊ฐœ๋ฐœ์ž๋“ค์ด ์ง์ ‘ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌ์†Œ์Šค ์š”๊ตฌ์‚ฌํ•ญ(CPU, ๋ฉ”๋ชจ๋ฆฌ, ์Šคํ† ๋ฆฌ์ง€ ๋“ฑ)์„ ์ •์˜ํ•˜๋„๋ก ๊ฐ€์ด๋“œํ•ด์ค˜์•ผ ํ•ด์š”.

๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ ์ „๋žต โš™๏ธ

  • ํŠนํžˆ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ์ชผ๊ฐœ๊ณ  ๋‚˜๋ˆŒ ๊ฒƒ์ธ์ง€ ๊ฒฐ์ •์ด ํ•„์š”ํ•ด์š”.
  • ์–ด๋А ๋ถ€๋ถ„์„ ConfigMap์— ๋‘๊ณ  ์–ด๋А ๋ถ€๋ถ„์„ Secret์œผ๋กœ ๊ด€๋ฆฌํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ์„ค๊ณ„๋„ ๊ฐœ๋ฐœ ํŒ€์—์„œ ์ œ๊ณตํ•˜๋ฉด ์ข‹์•„์š”.
    • ConfigMap: ํ™˜๊ฒฝ๋ณ„ ์„ค์ •, ๋กœ๊ทธ ๋ ˆ๋ฒจ, ์•ฑ ๊ตฌ์„ฑ ๋“ฑ ๋ฏผ๊ฐํ•˜์ง€ ์•Š์€ ์„ค์ •์ด์—์š”.
    • Secret: API ํ‚ค, ์ธ์ฆ์„œ, ํŒจ์Šค์›Œ๋“œ ๋“ฑ ๋ฏผ๊ฐํ•œ ์ •๋ณด์˜ˆ์š”.

๋งˆ์น˜๋ฉฐ 

์ด์ƒ์œผ๋กœ ์ธํ”„๋ผ์™€ ๋ฐ๋ธŒ์˜ต์Šค์— ๊ด€ํ•œ ์ค‘์š” ๊ฐœ๋…๋“ค์„ ๊ฐ„๋žตํžˆ ๋ณด์•˜์Šต๋‹ˆ๋‹ค! ์ด ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ํ•™์Šต๊ณผ ์—…๋ฌด์— ๋„์›€์ด ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์–ด์š”.

๋‹ค์‹œ ํ•œ๋ฒˆ ๋ง์”€๋“œ๋ฆฌ์ง€๋งŒ, ์ด ๊ธ€์€ ์ œ ์ฃผ๊ด€์ ์ธ ๊ด€์ ๊ณผ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ต๋‹ˆ๋‹ค. ๊ผญ ๋‹ค๋ฅธ ์ž๋ฃŒ๋“ค๋„ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•˜์‹œ๊ณ , ์‹ค๋ฌด์— ์ ์šฉํ•˜์‹ค ๋•Œ๋Š” ๊ต์ฐจ๊ฒ€์ฆ์„ ํ•˜์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ ค์š”!

๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ์˜๊ฒฌ์ด ์žˆ์œผ์‹œ๋ฉด ์–ธ์ œ๋“ ์ง€ ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์„ธ์š”. ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ธฐํšŒ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. 

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์˜ค๋Š˜ HTTP ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๋‹ต๋ณ€์„ ๋“œ๋ฆฌ๋ ค๊ณ  ํ•˜๋‹ค๋ณด๋‹ˆ ์ œ๊ฐ€ GET, POST, DELETE, PUT, PATCH ๊ฐ™์€ ๊ธฐ๋ณธ์ ์ธ ๋ฉ”์„œ๋“œ ์™ธ์—๋Š” ์ž˜ ๋ชจ๋ฅธ๋‹ค๋Š”๊ฑธ ์•Œ๊ฒŒ๋์–ด์š”๐Ÿฅฒ ํŠนํžˆ HEAD๋‚˜ OPTIONS ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋“ค์€ ์กด์žฌ์˜ ์œ ๋ฌด๋Š” ์•Œ์•˜์œผ๋‚˜, ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉ๋˜๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ์ธ์ง€ํ•˜์ง€๋„ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

 

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

ํŠนํžˆ React์™€ Next.js๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด ์‹ค์ œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉ๋˜๋Š”์ง€ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์„ค๋ช…ํ•ด๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

HTTP ๋ฉ”์„œ๋“œ Next.js ๋Š” Pages Router๋ฐฉ์‹์ด ๋” ์ต์ˆ™ํ•˜์‹  ๋ถ„๋“ค์„ ์œ„ํ•ด API Routes handler ๋ฌธ๋ฒ•(pages/api/ ๋””๋ ‰ํ† ๋ฆฌ ์‚ฌ์šฉ)์ธ Next.js 12 ์ด์ „์˜ ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

App Router ๋ฐฉ์‹๋งŒ์„ ์•Œ๊ณ  ๊ณ„์‹  ๋ถ„๋“ค์€, export async function GET() ์ฒ˜๋Ÿผ HTTP ๋ฉ”์„œ๋“œ๋ฅผ ํ•จ์ˆ˜ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ƒˆ๋กœ์šด convention ๋ฐฉ์‹ ๋“ฑ์„ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ ์ฐจ์ด์ ์ด ์žˆ์ง€๋งŒ ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜๋ฉฐ ์ง„ํ–‰ํ•˜์‹ ๋‹ค๋ฉด ๋ฌธ์ œ์—†์ด ์ดํ•ดํ•˜์‹ค ์ˆ˜ ์žˆ์„ ๊ฑฐ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 

 

 

HTTP Method

HTTP ๋ฉ”์„œ๋“œ๋ž€?

HTTP ๋ฉ”์„œ๋“œ๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์˜ ํ†ต์‹ ์—์„œ ์–ด๋–ค ๋™์ž‘์„ ์ˆ˜ํ–‰ํ• ์ง€ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. REST API์—์„œ ๋งค์šฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•˜๋ฉฐ, ๊ฐ ๋ฉ”์„œ๋“œ๋Š” ํŠน์ •ํ•œ ๋ชฉ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์›น ๊ฐœ๋ฐœ์˜ ๊ธฐ์ดˆ๊ฐ€ ๋˜์ฃ .

 

์ฃผ์š” HTTP ๋ฉ”์„œ๋“œ ์‚ดํŽด๋ณด๊ธฐ

1. GET ๋ฉ”์„œ๋“œ

GET์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

 

React์—์„œ์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ:

// React ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ ์กฐํšŒ
const UserProfile = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch('/api/users/1');
        const data = await response.json();
        setUser(data);
      } catch (error) {
        console.error('Error fetching user:', error);
      }
    };

    fetchUser();
  }, []);

  return (
    <div>
      {user && <h1>{user.name}</h1>}
    </div>
  );
};

 

Next.js API Route์—์„œ์˜ ์ฒ˜๋ฆฌ:

// pages/api/users/[id].ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== 'GET') {
    return res.status(405).json({ message: 'Method not allowed' });
  }

  const { id } = req.query;
  // DB ์กฐํšŒ ๋กœ์ง
  const user = await prisma.user.findUnique({
    where: { id: Number(id) }
  });

  res.status(200).json(user);
}

 

2. POST ๋ฉ”์„œ๋“œ

POST๋Š” ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

React์—์„œ์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ:

// ์ƒˆ ์‚ฌ์šฉ์ž ๋“ฑ๋ก ํผ
const RegisterForm = () => {
  const handleSubmit = async (event) => {
    event.preventDefault();
    const formData = new FormData(event.target);

    try {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          name: formData.get('name'),
          email: formData.get('email'),
        }),
      });

      if (!response.ok) throw new Error('Registration failed');
      
      const data = await response.json();
      // ์„ฑ๊ณต ์ฒ˜๋ฆฌ
    } catch (error) {
      // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" type="text" required />
      <input name="email" type="email" required />
      <button type="submit">๋“ฑ๋ก</button>
    </form>
  );
};

 

3. PUT๊ณผ PATCH ๋ฉ”์„œ๋“œ

// pages/api/users/[id].ts
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { id } = req.query;

  if (req.method === 'PUT') {
    // ์ „์ฒด ๋ฐ์ดํ„ฐ ๊ต์ฒด
    const userData = req.body;
    await prisma.user.update({
      where: { id: Number(id) },
      data: userData,
    });
    return res.status(200).json({ message: 'User updated' });
  }

  if (req.method === 'PATCH') {
    // ๋ถ€๋ถ„ ๋ฐ์ดํ„ฐ ์ˆ˜์ •
    const updates = req.body;
    await prisma.user.update({
      where: { id: Number(id) },
      data: updates,
    });
    return res.status(200).json({ message: 'User partially updated' });
  }
}

 

4. DELETE ๋ฉ”์„œ๋“œ

React์—์„œ์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ:

const DeleteUserButton = ({ userId }) => {
  const handleDelete = async () => {
    try {
      const response = await fetch(`/api/users/${userId}`, {
        method: 'DELETE',
      });

      if (!response.ok) throw new Error('Delete failed');
      // ์„ฑ๊ณต ์ฒ˜๋ฆฌ
    } catch (error) {
      // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    }
  };

  return (
    <button onClick={handleDelete}>
      ์‚ฌ์šฉ์ž ์‚ญ์ œ
    </button>
  );
};

 

React์™€ Next.js์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„์™€ ํšจ๊ณผ์ ์œผ๋กœ ํ†ต์‹ ํ•˜๊ณ , RESTful API๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ž์ฃผ ์‚ฌ์šฉ๋˜์ง€ ์•Š์ง€๋งŒ ์•Œ์•„๋‘๋ฉด ์œ ์šฉํ•œ HTTP ๋ฉ”์„œ๋“œ๋“ค

1. HEAD ๋ฉ”์„œ๋“œ

HEAD ๋ฉ”์„œ๋“œ๋Š” GET๊ณผ ๋™์ผํ•œ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€๋งŒ, ์‘๋‹ต์œผ๋กœ ํ—ค๋”๋งŒ ๋ฐ›๊ณ  ๋ณธ๋ฌธ์€ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€:

  • ํฐ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ ์ „์— ํŒŒ์ผ ํฌ๊ธฐ ํ™•์ธ
  • ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์‚ฌ(Last-Modified ํ™•์ธ)
  • ๋ฆฌ์†Œ์Šค ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
// Next.js์—์„œ HEAD ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ ์˜ˆ์‹œ
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'HEAD') {
    // ํŒŒ์ผ ์ •๋ณด๋งŒ ํ™•์ธ
    const fileStats = await getFileStats('large-file.pdf');
    
    res.setHeader('Content-Length', fileStats.size);
    res.setHeader('Last-Modified', fileStats.mtime.toUTCString());
    return res.status(200).end();
  }
}

// React์—์„œ HEAD ์š”์ฒญ ์˜ˆ์‹œ
const checkFileDetails = async () => {
  try {
    const response = await fetch('/api/files/large-file.pdf', {
      method: 'HEAD'
    });
    
    const fileSize = response.headers.get('Content-Length');
    const lastModified = response.headers.get('Last-Modified');
    
    console.log(`File size: ${fileSize} bytes`);
    console.log(`Last modified: ${lastModified}`);
  } catch (error) {
    console.error('Error checking file:', error);
  }
};

 

1. OPTIONS ๋ฉ”์„œ๋“œ์™€ CORS

OPTIONS๋Š” ์„œ๋ฒ„๊ฐ€ ์ง€์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ์™€ ๊ธฐ๋Šฅ์„ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. 

์ฃผ๋กœ CORS์˜ Preflight ์š”์ฒญ์—์„œ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€:

  • CORS ์‚ฌ์ „ ๊ฒ€์‚ฌ
  • ์„œ๋ฒ„ ์ง€์› ๊ธฐ๋Šฅ ํ™•์ธ
  • API ์—”๋“œํฌ์ธํŠธ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์กฐํšŒ

 

Next.js์—์„œ CORS ์„ค์ • ์˜ˆ์‹œ:

// pages/api/users/[id].ts
import Cors from 'cors';

// CORS ๋ฏธ๋“ค์›จ์–ด ์ดˆ๊ธฐํ™”
const cors = Cors({
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
  origin: ['https://allowed-origin.com'],
  credentials: true,
});

// ๋ฏธ๋“ค์›จ์–ด ์‹คํ–‰ ํ•จ์ˆ˜
const runMiddleware = (req: NextApiRequest, res: NextApiResponse, fn: Function) => {
  return new Promise((resolve, reject) => {
    fn(req, res, (result: any) => {
      if (result instanceof Error) {
        return reject(result);
      }
      return resolve(result);
    });
  });
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // CORS ๋ฏธ๋“ค์›จ์–ด ์‹คํ–‰
  await runMiddleware(req, res, cors);

  // OPTIONS ์š”์ฒญ ์ฒ˜๋ฆฌ
  if (req.method === 'OPTIONS') {
    return res.status(200).end();
  }

  // ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ...
}

 

๋ธŒ๋ผ์šฐ์ €์˜ Preflight ์š”์ฒญ ์˜ˆ์‹œ:

// React์—์„œ CORS ๊ด€๋ จ OPTIONS ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ
const fetchWithCustomHeaders = async () => {
  try {
    const response = await fetch('https://api.example.com/data', {
      method: 'GET',
      headers: {
        'Custom-Header': 'value',  // ์ปค์Šคํ…€ ํ—ค๋” ์ถ”๊ฐ€์‹œ ์ž๋™์œผ๋กœ preflight ์š”์ฒญ ๋ฐœ์ƒ
      },
      credentials: 'include',  // ์ธ์ฆ ์ •๋ณด ํฌํ•จ
    });
    
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
  }
};

 

CORS์™€ Preflight ์š”์ฒญ์˜ ์ดํ•ด

๋ธŒ๋ผ์šฐ์ €๋Š” ํŠน์ • ์กฐ๊ฑด์—์„œ ์‹ค์ œ ์š”์ฒญ ์ „์— OPTIONS ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ Preflight ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

 

Preflight ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ:

  1. ์ปค์Šคํ…€ ํ—ค๋”๋ฅผ ํฌํ•จํ•  ๋•Œ (์˜ˆ: Authorization)
  2. ๋‹จ์ˆœํ•˜์ง€ ์•Š์€ Content-Type์„ ์‚ฌ์šฉํ•  ๋•Œ
  3. ๋‹จ์ˆœํ•˜์ง€ ์•Š์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ (GET, HEAD, POST ์™ธ)
// Preflight ์š”์ฒญ ์˜ˆ์‹œ (๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ณด์ด๋Š” ํ˜•ํƒœ)
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type
Origin: https://your-app.com

// ์„œ๋ฒ„ ์‘๋‹ต ์˜ˆ์‹œ
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://your-app.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 86400

 

3. CONNECT ๋ฉ”์„œ๋“œ

CONNECT๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด SSL/TLS ํ„ฐ๋„์„ ๊ตฌ์„ฑํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€:

  • HTTPS ํ”„๋ก์‹œ ์—ฐ๊ฒฐ ์„ค์ •
  • ์›น์†Œ์ผ“ ์—ฐ๊ฒฐ ์—…๊ทธ๋ ˆ์ด๋“œ
  • ํ”„๋ก์‹œ ์„œ๋ฒ„๋ฅผ ํ†ตํ•œ ์•”ํ˜ธํ™”๋œ ํ†ต์‹ 
// Node.js์—์„œ CONNECT ์š”์ฒญ ์ฒ˜๋ฆฌ ์˜ˆ์‹œ
const net = require('net');
const server = require('http').createServer();

server.on('connect', (req, clientSocket, head) => {
  // ํ”„๋ก์‹œ ์—ฐ๊ฒฐ ์„ค์ •
  const { port, hostname } = new URL(`http://${req.url}`);
  const serverSocket = net.connect(port || 80, hostname, () => {
    clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    serverSocket.write(head);
    serverSocket.pipe(clientSocket);
    clientSocket.pipe(serverSocket);
  });
});

 

4. TRACE ๋ฉ”์„œ๋“œ

TRACE๋Š” ์š”์ฒญ ๋ฉ”์‹œ์ง€๊ฐ€ ํ”„๋ก์‹œ๋‚˜ ๋ฐฉํ™”๋ฒฝ์„ ํ†ต๊ณผํ•˜๋ฉด์„œ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ์ถ”์ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€:

  • ๋””๋ฒ„๊น…
  • ์š”์ฒญ ๋ฉ”์‹œ์ง€ ๋ณ€์กฐ ๊ฐ์ง€
  • ํ”„๋ก์‹œ ์ฒด์ธ ์ถ”์ 
// Next.js์—์„œ TRACE ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ ์˜ˆ์‹œ
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'TRACE') {
    // ์š”์ฒญ ํ—ค๋”์™€ ๊ฒฝ๋กœ ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
    const responseBody = {
      method: req.method,
      url: req.url,
      headers: req.headers,
      path: req.url
    };
    
    res.setHeader('Content-Type', 'message/http');
    return res.status(200).json(responseBody);
  }
}

// ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋ฒ„๋Š” TRACE ๋ฉ”์„œ๋“œ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค

 

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

// Next.js์—์„œ ๋ฉ”์„œ๋“œ ์ œํ•œ ๊ตฌํ˜„ ์˜ˆ์‹œ
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฉ”์„œ๋“œ ์ฐจ๋‹จ
  const allowedMethods = ['GET', 'POST', 'OPTIONS'];
  if (!allowedMethods.includes(req.method)) {
    return res.status(405).json({ 
      error: 'Method Not Allowed',
      allowedMethods 
    });
  }

  // TRACE ๋ฉ”์„œ๋“œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์ฐจ๋‹จ
  if (req.method === 'TRACE') {
    return res.status(403).json({ 
      error: 'TRACE method is disabled for security reasons' 
    });
  }
}
 

์ด๋Ÿฌํ•œ HTTP ๋ฉ”์„œ๋“œ๋“ค์€ ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ์—์„œ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ณด์•ˆ์„ ๊ณ ๋ คํ•ด ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํŠนํžˆ TRACE ๋ฉ”์„œ๋“œ๋Š” ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฉฑ๋“ฑ์„ฑ

HTTP ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰ ์„ค๋ช…์œผ๋กœ ์ค‘์š”ํ•œ ๊ฐœ๋…์ธ ๋ฉฑ๋“ฑ์„ฑ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฉฑ๋“ฑ์„ฑ(Idempotency)์ด๋ž€ ๋™์ผํ•œ ์š”์ฒญ์„ ํ•œ ๋ฒˆ ๋ณด๋‚ด๋Š” ๊ฒƒ๊ณผ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์„œ๋ฒ„์˜ ์ƒํƒœ์— ๋™์ผํ•œ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์ฃผ์š” HTTP ๋ฉ”์„œ๋“œ์˜ ๋ฉฑ๋“ฑ์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

๋ฉฑ๋“ฑ์„ฑ์„ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ:

  1. GET: ๋ฆฌ์†Œ์Šค๋ฅผ ์กฐํšŒ๋งŒ ํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ
  2. HEAD: GET๊ณผ ๋™์ผํ•˜๊ฒŒ ๋ฆฌ์†Œ์Šค ์ •๋ณด๋งŒ ์กฐํšŒํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ
  3. PUT: ๋ฆฌ์†Œ์Šค๋ฅผ ๋Œ€์ฒด(๋ฎ์–ด์“ฐ๊ธฐ)ํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ
  4. DELETE: ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ญ์ œํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ (์ด๋ฏธ ์‚ญ์ œ๋œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ค์‹œ ์‚ญ์ œํ•ด๋„ ์ƒํƒœ๋Š” ๋™์ผ)
  5. OPTIONS: ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ง€์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ
  6. TRACE: ์š”์ฒญ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑ

๋ฉฑ๋“ฑ์„ฑ์ด ์—†๋Š” ๋ฉ”์„œ๋“œ:

  1. POST: ์ƒˆ๋กœ์šด ๋ฆฌ์†Œ์Šค ์ƒ์„ฑ ๋˜๋Š” ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์š”์ฒญํ•˜๋ฏ€๋กœ ๋ฉฑ๋“ฑํ•˜์ง€ ์•Š์Œ
    • ์˜ˆ: ๊ฐ™์€ ์ฃผ๋ฌธ์„ ์—ฌ๋Ÿฌ ๋ฒˆ POSTํ•˜๋ฉด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฃผ๋ฌธ์ด ์ƒ์„ฑ๋จ
  2. PATCH: ๋ฆฌ์†Œ์Šค์˜ ๋ถ€๋ถ„ ์ˆ˜์ •์ด๋ฏ€๋กœ ๋ฉฑ๋“ฑํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
    • ์˜ˆ: ์ˆซ์ž๋ฅผ 1์”ฉ ์ฆ๊ฐ€์‹œํ‚ค๋Š” PATCH ์š”์ฒญ์€ ๋ฉฑ๋“ฑํ•˜์ง€ ์•Š์Œ

๋ฉฑ๋“ฑ์„ฑ์ด ์ค‘์š”ํ•œ ์ด์œ :

  1. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ์‹œ ์žฌ์‹œ๋„ ์ „๋žต ์ˆ˜๋ฆฝ ๊ฐ€๋Šฅ
  2. ์„œ๋ฒ„์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ ๋ณด์žฅ
  3. ์บ์‹ฑ ์ „๋žต ์ˆ˜๋ฆฝ์— ๋„์›€

API๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ๊ฐ ๋ฉ”์„œ๋“œ์˜ ๋ฉฑ๋“ฑ์„ฑ ํŠน์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ์ ์ ˆํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

 

 

GraphQL

๋ฒˆ์™ธ: REST API vs GraphQL 

HTTP ๋ฉ”์„œ๋“œ๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋นผ๋†“์„ ์ˆ˜ ์—†๋Š” ์ด์•ผ๊ธฐ๊ฐ€ ํ•˜๋‚˜ ์žˆ๋Š”๋ฐ์š”, ๋ฐ”๋กœ GraphQL์ž…๋‹ˆ๋‹ค. REST API๋งŒ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ ์ฒ˜์Œ GraphQL์„ ์ ‘ํ–ˆ์„ ๋•Œ ์ƒ๋‹นํžˆ ์‹ ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. "์•„๋‹ˆ, HTTP ๋ฉ”์„œ๋“œ๋„ ์—†์ด ์–ด๋–ป๊ฒŒ API๋ฅผ ๋งŒ๋“ ๋‹ค๋Š” ๊ฑฐ์ง€?" ํ•˜๋ฉด์„œ์š”. ๐Ÿ˜…

 

GraphQL์ด๋ž€?

GraphQL์€ Facebook์—์„œ ๊ฐœ๋ฐœํ•œ API๋ฅผ ์œ„ํ•œ ์ฟผ๋ฆฌ ์–ธ์–ด์ž…๋‹ˆ๋‹ค. REST API์™€๋Š” ๋‹ฌ๋ฆฌ, GraphQL์€ ๋‹จ์ผ ์—”๋“œํฌ์ธํŠธ์—์„œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ™•ํžˆ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

REST API vs GraphQL ๋น„๊ต

// REST API ๋ฐฉ์‹
// GET /api/users/1
// GET /api/users/1/posts
// GET /api/users/1/followers

// GraphQL ๋ฐฉ์‹
const query = `
  query {
    user(id: 1) {
      name
      email
      posts {
        title
        content
      }
      followers {
        name
      }
    }
  }
`;

 

Next.js์—์„œ GraphQL ์‚ฌ์šฉ ์˜ˆ์‹œ

// pages/api/graphql.ts
import { ApolloServer } from 'apollo-server-micro';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(req, res) {
  await apolloServer.start();
  await apolloServer.createHandler({
    path: '/api/graphql',
  })(req, res);
}

 

React์—์„œ GraphQL ์‚ฌ์šฉ ์˜ˆ์‹œ (Apollo Client)

import { useQuery } from '@apollo/client';

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      name
      email
      posts {
        title
      }
    }
  }
`;

function UserProfile({ userId }) {
  const { loading, error, data } = useQuery(GET_USER, {
    variables: { id: userId },
  });

  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return (
    <div>
      <h1>{data.user.name}</h1>
      <p>{data.user.email}</p>
      <h2>Posts</h2>
      <ul>
        {data.user.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

 

REST API vs GraphQL: ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ 

REST API

๐Ÿ‘ ์žฅ์ :

  • HTTP ์บ์‹ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ™œ์šฉ
  • ์ง๊ด€์ ์ธ ๋ฆฌ์†Œ์Šค ๊ตฌ์กฐ
  • ๋” ๋„๋ฆฌ ์‚ฌ์šฉ๋˜์–ด ํ•™์Šต ์ž๋ฃŒ๊ฐ€ ํ’๋ถ€
  • ๋‹จ์ˆœํ•œ API์—์„œ๋Š” ๊ตฌํ˜„์ด ๋” ๊ฐ„๋‹จ

๐Ÿ‘Ž ๋‹จ์ :

  • Over-fetching/Under-fetching ๋ฌธ์ œ
  • ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์š”์ฒญ ํ•„์š”
  • ์—”๋“œํฌ์ธํŠธ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Œ

 

GraphQL

๐Ÿ‘ ์žฅ์ :

  • ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์ •ํ™•ํžˆ ์š”์ฒญ ๊ฐ€๋Šฅ
  • ๋‹จ์ผ ์š”์ฒญ์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค ์กฐํšŒ ๊ฐ€๋Šฅ
  • ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ
  • ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ ์ง€์› (Subscriptions)

๐Ÿ‘Ž ๋‹จ์ :

  • ํ•™์Šต ๊ณก์„ ์ด ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ€ํŒŒ๋ฆ„
  • ์บ์‹ฑ ๊ตฌํ˜„์ด ๋” ๋ณต์žก
  • ๋‹จ์ˆœํ•œ API์—์„œ๋Š” ์˜ค๋ฒ„ ์—”์ง€๋‹ˆ์–ด๋ง์ด ๋  ์ˆ˜ ์žˆ์Œ
  • ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ๊ฐ€ ๋ณต์žก

 

์‹ค์ œ ์‚ฌ์šฉ ํŒ

// GraphQL๊ณผ REST API๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ ‘๊ทผ
// pages/api/hybrid/[...path].ts
export default async function handler(req, res) {
  // ํŒŒ์ผ ์—…๋กœ๋“œ๋Š” REST API๋กœ ์ฒ˜๋ฆฌ
  if (req.url.startsWith('/api/upload')) {
    return handleFileUpload(req, res);
  }

  // ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ๋Š” GraphQL๋กœ ์ฒ˜๋ฆฌ
  if (req.url.startsWith('/api/graphql')) {
    return handleGraphQLRequest(req, res);
  }

  // ๋‹จ์ˆœํ•œ CRUD๋Š” REST API๋กœ ์ฒ˜๋ฆฌ
  return handleRESTRequest(req, res);
}

 

์–ธ์ œ ๋ฌด์—‡์„ ์„ ํƒํ• ๊นŒ?

 

  • REST API๊ฐ€ ์ข‹์€ ๊ฒฝ์šฐ:
    • ๋‹จ์ˆœํ•œ CRUD ์ž‘์—…์ด ์ฃผ๋ฅผ ์ด๋ฃจ๋Š” ๊ฒฝ์šฐ
    • ์บ์‹ฑ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ
    • ํŒŒ์ผ ์—…๋กœ๋“œ/๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ
    • ํŒ€์ด REST์— ๋” ์ต์ˆ™ํ•œ ๊ฒฝ์šฐ
  • GraphQL์ด ์ข‹์€ ๊ฒฝ์šฐ:
    • ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ด€๊ณ„๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒฝ์šฐ
    • ๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ๋Ÿ‰ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
    • ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
    • API ๋ณ€๊ฒฝ์ด ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ

 

์‹ค์ œ๋กœ ๊ธฐ์—…๋“ค์—์„œ ๋‘ ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ ‘๊ทผ์„ ํƒํ•˜๊ณ  ์žˆ๋Š”๊ฑธ๋กœ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. GitHub์ด ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ์ธ๋ฐ์š”, ์š”๊ตฌ ์‚ฌํ•ญ์— ๊ฐ€์žฅ ์ž˜ ๋ถ€ํ•ฉํ•˜๊ณ  ๊ฐ€์žฅ ํŽธ์•ˆํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋ผ ๊ถŒ๊ณ ํ•˜๊ณ  ์žˆ๊ณ , ํŒŒ์ผ ์—…๋กœ๋“œ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ REST API๋กœ, ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ๋Š” GraphQL๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

React.js์™€ Next.js ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ์ค‘, ์ฒ˜์Œ ๊ฐœ๋ฐœ ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ๊ณ ๋ฏผํ–ˆ๋˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋Œ€ํ•ด ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ฆฌํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ณผ๊ฑฐ์—๋Š” ์ด๋“ค์„ ์™„์„ฑ๋œ ๋กœ๋ด‡๊ณผ ์กฐ๋ฆฝํ˜• ๋กœ๋ด‡์— ๋น„์œ ํ•˜๊ณค ํ–ˆ์ง€๋งŒ, ์ง€๊ธˆ์€ ๋‹ค๋ฅด๊ฒŒ ์ ‘๊ทผํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ์— ์•ž์„œ ๊ธ€ ์ œ๋ชฉ๊ณผ ๊ฐ™์ด React.js์— ๋Œ€ํ•ด "๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ๋ฌด์—‡์ธ์ง€" ํ˜ผ๋™ํ•˜๋Š” ๋ถ„๋“ค์ด ๊ณ„์‹ค๊นŒ์š”?

https://ko.legacy.reactjs.org/

 

๊ทธ๋ ‡๋‹ค๋ฉด ๊ณต์‹ ๋ฌธ์„œ๋ถ€ํ„ฐ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

๊ณต์‹ ๋ฌธ์„œ์˜ ๋ฉ”์ธ ํŽ˜์ด์ง€์—์„œ์กฐ์ฐจ "์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ" ๋ผ๋Š” ๋ฌธ๊ตฌ๋กœ ์šฐ๋ฆฌ๋ฅผ ํ™˜์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ˆ˜๋งŽ์€ ์‚ฌ๋žŒ๋“ค์€ ๋„๋Œ€์ฒด ๋ฌด์—‡ ๋•Œ๋ฌธ์— ํ˜ผ๋™ํ•˜๊ฒŒ ๋œ ๊ฑธ๊นŒ์š”?

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ์„ค๋ช…๋“œ๋ฆฐ ํ›„ "React.js๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ผ๊นŒ?"  ๋ชฉ์ฐจ์—์„œ ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ฆฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 


๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ๋ž€?

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๊ฒ€์ƒ‰ ์‹œ ์‰ฝ๊ฒŒ ์•„๋ž˜์˜ ๋‚ด์šฉ๋“ค์„ ํ™•์ธํ•ด ๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

"ํŠน์ •ํ•œ ๊ธฐ๋Šฅ์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฏธ๋ฆฌ ์ž‘์„ฑ๋œ ์ฝ”๋“œ์˜ ์ง‘ํ•ฉ"

"๊ฐœ๋ฐœ์ž๊ฐ€ ํ•„์š”์— ๋”ฐ๋ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ๋„๊ตฌ์ฒ˜๋Ÿผ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์„ ํƒ์ ์œผ๋กœ ์‚ฌ์šฉ"

 

์ •๋ฆฌํ•ด ๋ณด์ž๋ฉด, ๋ฌด์–ธ๊ฐ€๋ฅผ ๋งŒ๋“ค ๋•Œ ์œ ์šฉํ•œ ๋„๊ตฌ ๊ฐ™์€ ์นœ๊ตฌ๋„ค์š”.

๋งˆ์น˜ ๋ฏธ์šฉ์‹ค์—์„œ ์“ฐ๋Š” ๋„๊ตฌ๋‚˜ ๊ณต์‚ฌ ํ˜„์žฅ์—์„œ ์“ฐ๋Š” ๋„๊ตฌ์ฒ˜๋Ÿผ, ํŠน์ •ํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด ์ค๋‹ˆ๋‹ค.

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

ํ”„๋ ˆ์ž„์›Œํฌ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณด๋‹ค ๋” ํฐ ๋‹จ์œ„์˜ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ์˜ ์˜ˆ์‹œ๋ฅผ ๊ฐ€์ ธ์™€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฏธ์šฉ์‹ค์—์„œ ๋จธ๋ฆฌ๋ฅผ ๋ง๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค ์ •ํ•ด์ฃผ๊ณ , ๊ทธ ์•ˆ์—์„œ ์ •ํ•ด์ง„ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•ด ๋จธ๋ฆฌ๋ฅผ ๋ง๋ฆฌ๋Š”(ํ•„์š”ํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š”) ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ Next.js๋Š” React.js๋ฅผ ๋ฒ ์ด์Šค๋กœ ํ•ด ์›น์„ ๋งŒ๋“œ๋Š”๋ฐ ํ•„์š”ํ•œ ์ „์ฒด์ ์ธ ํ•ด๊ฒฐ์ฑ…์„ ์ œ๊ณตํ•ด ์ค๋‹ˆ๋‹ค!

React.js์™€ Next.js์˜ ๊ด€๊ณ„

  • React.js๋Š” UI๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์„ ํƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Next.js๋Š” ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ์ „์ฒด์ ์ธ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•˜๋ฉฐ, React๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ํ”„๋ ˆ์ž„์›Œํฌ์™€ React,js, Next.js์— ๋Œ€ํ•ด ๊ฐ„๋žตํžˆ ์„ค๋ช…๋“œ๋ ธ์Šต๋‹ˆ๋‹ค.

์ด์ œ ๋ณธ๋ก ์œผ๋กœ ๋“ค์–ด๊ฐ€ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

 

React.js๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ผ๊นŒ?

์ €๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ํ•™์Šต์—์„œ "ํ”„๋ ˆ์ž„์›Œํฌ ์—†๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ | ํ”„๋ž€์„ธ์Šค์ฝ” ์ŠคํŠธ๋ผ์ธจ๋กœ" ๊ต์žฌ์˜,

"ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ฝ”๋“œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค"๋ผ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ๊ฐ€์žฅ ์™€๋‹ฟ์•˜์Šต๋‹ˆ๋‹ค.

๊ต์žฌ๋ฅผ ํ•™์Šตํ•˜๋ฉฐ ํ•ด๋‹น ๋ฌธ๊ตฌ๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์ฐจ์ด์— ๋Œ€ํ•ด ์ •๋ง ์ž˜ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์–ด์š”.

 

๋ฐœ์ทŒํ•œ ๋ฌธ์žฅ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์‹œ ์ •๋ฆฌํ•˜์ž๋ฉด,

  • ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‹คํ–‰ ํ๋ฆ„์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋‚˜๋ˆ ๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”!
์—ฌ๊ธฐ์„œ ์ฝ”๋“œ๋ž€ ๊ตฌ์ฒด์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

๋ฐœ์ทŒํ•œ ๋ฌธ์žฅ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธ€์„ ์จ ๋‚ด๋ ค๊ฐˆ ์˜ˆ์ •์ด์—์š”.

์ฐธ๊ณ ํ•˜์—ฌ ์ฝ์œผ์‹ ๋‹ค๋ฉด ์ข€ ๋” ๋„์›€์ด ๋˜์‹ค ๊ฑฐ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค!

๋‹ค์‹œ ๊ฐ•์กฐํ•˜์ž๋ฉด React.js๋Š” ๊ณต์‹์ ์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค.

์ด๋Š” React๊ฐ€ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์ค‘์ ์„ ๋‘๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜ ์ €๋ฅผ ํฌํ•จํ•œ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์€ React๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
์ด์œ ๊ฐ€ ๋ญ˜๊นŒ์š”?

 

์ด๋Š” ๋ฆฌ์•กํŠธ ์ƒํƒœ๊ณ„์™€ ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๊ฐ•๋ ฅํ•œ ์˜ํ–ฅ๋ ฅ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
React๋Š” UI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ, ์ƒํƒœ๊ณ„์˜ ๋ฐœ์ „๊ณผ ํ•จ๊ป˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด React๋ฅผ ์ด์šฉํ•ด ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ์กฐํ™”ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

 

์ด์ œ ์ง€๊ธˆ๊นŒ์ง€์˜ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ ์‹ค์ œ ๊ฐœ๋ฐœ๊ณผ์ •์„ ์˜ˆ์‹œ๋กœ ๋“ค์–ด ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ฆฌํ•ด ๋ณด๋„๋ก ํ• ๊ฒŒ์š”!

 

์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ๋ ๊นŒ?

 

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ, ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‚ฌ์šฉ ๋ฐฉ์‹๊ณผ ๋ชฉ์ ์„ ๋ช…ํ™•ํžˆ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

React๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋ฉด ๋” ๋‚˜์€ ์„ ํƒ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ

์ปค๋ฎค๋‹ˆํ‹ฐ์™€ ์ƒํƒœ๊ณ„์˜ ์˜ํ–ฅ๋ ฅ

  • ๋ฆฌ์•กํŠธ๋Š” ์ดˆ๊ธฐ ์„ค์ •๊ณผ ๊ตฌ์กฐ๊ฐ€ ์ด๋ฏธ ์žกํ˜€์žˆ๋Š” ์ƒํƒœ์—์„œ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์€ ์ด๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, npm create-react-app์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ์ •ํ•ด์ง„ ๊ตฌ์กฐ ์•ˆ์—์„œ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๋‚ด์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ

  • ๋ฆฌ์•กํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ์— ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง, API ํ˜ธ์ถœ ๋“ฑ์„ ๋ชจ๋‘ ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์€ ๋ฆฌ์•กํŠธ๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜์—ฌ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ๋А๊ปด์ง€๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ฆ‰, React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ชจ๋“  ์‹คํ–‰ ํ๋ฆ„์„ ์ œ์–ดํ•˜๋ฉฐ, ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์žฅ์ :

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

๋‹จ์ :

  • ์˜์กด์„ฑ ์ฆ๊ฐ€: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์— ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด, ๋ฆฌ์•กํŠธ๋ฅผ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋Œ€์ฒดํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ์ด๋Š” ํ–ฅํ›„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ ํฐ ๋น„์šฉ์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

React๋ฅผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ UI ๋ Œ๋”๋ง์„ ๋ถ„๋ฆฌํ•˜๊ณ , ์ด ๋‘˜์„ ์—ฐ๊ฒฐํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‘๋Š” ๋ฐฉ๋ฒ•

 

์ƒํƒœ ๊ด€๋ฆฌ์™€ UI ์—…๋ฐ์ดํŠธ

  • ๋ฆฌ์•กํŠธ์—์„œ setState๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด UI๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค. ์ด ์ ์„ ํ™œ์šฉํ•˜์—ฌ, setState๋ฅผ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์š”์†Œ๋กœ ๊ฐ„์ฃผํ•˜๊ณ , UI ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ์‹œ์ ์—๋งŒ setState๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋Ÿฐ์‹์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋ฆฌ์•กํŠธ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” ์˜ค๋กœ์ง€ UI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์—ญํ• ๋งŒ ํ•˜๊ฒŒ ๋˜๋ฉฐ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ

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

UI ๋ Œ๋”๋ง์—๋งŒ ์ง‘์ค‘

  • ๋ฆฌ์•กํŠธ๋ฅผ ์ˆœ์ˆ˜ UI ๋ Œ๋”๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ฆฌ์•กํŠธ ์™ธ๋ถ€์˜ ์ˆœ์ˆ˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค ๋“ฑ์˜ ์ฝ”๋“œ๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” ์˜ค์ง ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ๋ Œ๋”๋งํ•˜๋Š” ์—ญํ• ๋งŒ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋Š” ํด๋ž˜์Šค ๋“ฑ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜์—ฌ, ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์™€ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ํ†ต์‹ 

  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ํ†ต์‹ (์—ฐ๊ฒฐ)์„ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•˜์—ฌ, ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๋ฆฌ์•กํŠธ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , ํ•„์š”ํ•œ ์‹œ์ ์— ๋ฆฌ์•กํŠธ์˜ setState๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ๊ตฌํ˜„ ์˜ˆ์‹œ

/**
* ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง - ์ˆœ์ˆ˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค
*/
class BusinessLogic {
  constructor() {
    this.data = null;
    this.listeners = [];
  }

  setData(newData) {
    this.data = newData;
    this.notifyListeners();
  }

  getData() {
    return this.data;
  }

  addListener(listener) {
    this.listeners.push(listener);
  }

  notifyListeners() {
    this.listeners.forEach(listener => listener(this.data));
  }
}

/**
* ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ - UI ๋ Œ๋”๋ง
*/
import React, { useState, useEffect } from 'react';

const TestComp = ({ businessLogic }) => {
  const [data, setData] = useState(businessLogic.getData());

  useEffect(() => {
    const handleDataChange = (newData) => {
      setData(newData);
    };

    businessLogic.addListener(handleDataChange);

    return () => {
      businessLogic.removeListener(handleDataChange);
    };
  }, [businessLogic]);

  return (
    <div>
      <h1>{data}</h1>
    </div>
  );
};

/**
* ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ
*/
const logic = new BusinessLogic();
logic.setData("์ปคํ”ผ ๋งˆ์‹œ๊ณ ์‹ถ๋‹ค..");

const App = () => (
  <TestComp businessLogic={logic} />
);

export default App;

 

์žฅ์ 

  • ๋งŒ์•ฝ ๋ฆฌ์•กํŠธ๋ฅผ ์‹œ์žฅ์—์„œ ๋”์ด์ƒ ์›ํ•˜์ง€ ์•Š๊ฒŒ ๋˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด UI ๋ Œ๋”๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์˜ˆ: ์Šค๋ฒจํŠธ)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ–ˆ๋‹ค๋ฉด ์ „ํ™˜์ด ๋งค์šฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ฆฌ์•กํŠธ์™€ ๋…๋ฆฝ์ ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‹จ์ 

  • ์‹ค์ œ ์‚ฌ๋ก€ ๋“œ๋ฌพ: ๋งŽ์€ ํšŒ์‚ฌ๋“ค์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์„ ์ฑ„ํƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ๋น„์šฉ๊ณผ ์‹œ๊ฐ„์ด ๋งŽ์ด ๋“ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • ๋†’์€ ๊ฐœ๋ฐœ ๋น„์šฉ: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ UI๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ชจ๋“  ๊ฒƒ์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ๊ฐœ๋ฐœ ๋น„์šฉ์ด ๋งŽ์ด ๋“ญ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ดˆ๊ธฐ ๋Ÿฐ์นญ์ด ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•„๋ž˜์—์„œ ์ข€ ๋” ์ƒ์„ธํžˆ ์ €์˜ ์ƒ๊ฐ์„ ํ’€์–ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

์œ„์™€ ์ค‘๋ณต๋˜๋Š” ๋‚ด์šฉ์ด ์žˆ์ง€๋งŒ ํ๋ฆ„์„ ์œ„ํ•ด ๊ทธ๋Œ€๋กœ ์ ์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

 

๊ธฐ์ˆ  ๋ถ€์ฑ„์™€ ๊ทธ ๊ด€๋ฆฌ

์ €๋Š” ๊ธฐ์ˆ  ๋ถ€์ฑ„์— ๋Œ€ํ•ด ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ๋‹จ๊ธฐ์ ์ธ ์ด์ต์„ ์œ„ํ•ด ์žฅ๊ธฐ์ ์ธ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์„ ํƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ๊ธฐ์ˆ  ๋ถ€์ฑ„์˜ ๊ฐœ๋…์€ "React.js์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ ๋ฐฉ์‹์˜ ๊ฒฐ์ •"์— ๋„์›€์„ ์ค„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ด์š”!

 

๊ทธ๋ž˜์„œ ์ง€๊ธˆ๋ถ€ํ„ฐ React๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ๋น„๊ตํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„

  • ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์œผ๋กœ ์ธํ•ด ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ „ํ™˜์ด ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด React ์ปดํฌ๋„ŒํŠธ์— ๊นŠ์ด ๊ฒฐํ•ฉ๋˜์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„

  • ์ดˆ๊ธฐ ๊ฐœ๋ฐœ ๋น„์šฉ์ด ๋†’์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ ๊ธฐ๋Šฅ์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ๋ถ€์ฑ„ ๊ด€๋ฆฌ์˜ ์ค‘์š”์„ฑ

๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ์˜ ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ์ผ์ •์— ๋งž๊ฒŒ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ์ž˜ ์กฐ์ •ํ•˜๋ฉด, ์žฅ๊ธฐ์ ์œผ๋กœ ๋” ๋‚˜์€ ์œ ์ง€๋ณด์ˆ˜์™€ ํ™•์žฅ์„ฑ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฌด์—‡๋ณด๋‹ค ์ €๋Š” ๊ธฐ์ˆ  ๋ถ€์ฑ„๊ฐ€ ์ƒ๊ฒจ๋„ ์ฒญ์‚ฐ๋งŒ ์ž˜ํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ์ž˜ ๋‚ด๋Š” ๊ฒƒ๋„ ์ €๋Š” ์ค‘์š”ํ•œ ๋Šฅ๋ ฅ์ด๋ผ๊ณ  ๋ณด๊ณ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž๊ฐ€ ์ด์œ ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ•˜๋Š” ์˜ˆ์ˆ ๊ฐ€๋Š” ์•„๋‹ˆ๋‹ˆ๊นŒ์š”.

๊ทธ๋Ÿฌ๋‹ˆ ๋ฌด์–ธ๊ฐ€๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋‹จ์ˆœํ•˜๊ฒŒ ์ •์˜๋œ ํŠน์ • ๋‹จ์–ด๋‚˜ ๋ฌธ์žฅ๋งŒ์„ ๋ฐ›์•„๋“ค์ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ƒ๋Œ€์ ์œผ๋กœ ์ƒ๊ฐํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋น ๋ฅธ ์„ฑ์žฅ์— ์œ ๋ฆฌํ•˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 


๊ฒฐ๋ก 

React๋Š” ๊ณต์‹์ ์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด์ง€๋งŒ, ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ํ”„๋ ˆ์ž„์›Œํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ์˜ ํŠน์„ฑ๊ณผ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ React๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉด์„œ, ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ ์ ˆํ•˜๊ฒŒ ํ™œ์šฉํ•˜๋ฉด ์„ฑ๊ณต์ ์ธ ๊ฐœ๋ฐœ์„ ์ด๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

 

์—ฌ๋Ÿฌ๋ถ„๋“ค๊ป˜์„œ ์ด ๊ธ€์„ ์ฝ๊ณ  ๋‚œ ํ›„์—๋„, ๊ณ„์† ํ•™์Šตํ•˜๋ฉฐ ๋‹ค์–‘ํ•œ ์˜๊ฒฌ์„ ์ ‘ํ•˜๊ฒŒ ๋˜์‹œ๊ฒ ์ฃ ?!

์ž์‹ ๋“ค๋งŒ์˜ ๋‹จ์–ด๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ , ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ์— ๋Œ€ํ•œ ๊ธธ์žก์ด ์—ญํ• ์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ผ๋ฉฐ ์ด๋งŒ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

'CS' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Chrome ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง - Critical Path ์ด์ „ ๊ณผ์ •์€?  (0) 2024.03.20

๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์ด์ „์˜ ํ•ต์‹ฌ ๋‹จ๊ณ„

๋ Œ๋”๋ง ๊ณผ์ •์„ ๋” ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜๋Š” ์—†์„๊นŒ๋ผ๋Š” ์ƒ๊ฐ์— ํฌ๋ฆฌํ‹ฐ์ปฌ ํŒจ์Šค(Critical Path)์— ๋Œ€ํ•ด ๋‹ค์‹œ ํ•™์Šตํ•˜๋˜ ์ค‘ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ™”๋ฉด์„ ๋ Œ๋”๋ง ํ•˜๊ธฐ ์ „์— ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€์— ๋Œ€ํ•ด ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.
์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ Chrome for Developers๋ฅผ ์ฝ์–ด๋ณด์•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ, ์ง€๊ธˆ๋ถ€ํ„ฐ ์„ค๋ช…ํ•˜๊ฒŒ ๋  ๋‹จ๊ณ„๋“ค์ด ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์ด์ „์— ์–ด๋–ป๊ฒŒ ์ง„ํ–‰๋˜๋Š”์ง€์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ๋“œ๋””์–ด ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค!

๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์— url์„ ์ž…๋ ฅ ํ›„ ์›น์‚ฌ์ดํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์ด๊ธฐ๊นŒ์ง€ ๊ณผ์ • ์ค‘์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์‚ฌ์ดํŠธ๊ฐ€ ๋ณด์ด๊ธฐ ์ง์ „๊นŒ์ง€์ธ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ๋˜๊ธฐ๊นŒ์ง€ 5๊ฐ€์ง€์˜ ๊ณผ์ •์œผ๋กœ ์ง„ํ–‰์ด ๋˜๋ฉฐ ์ด ๊ณผ์ •๋“ค์— ๋Œ€ํ•ด ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

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

1๋‹จ๊ณ„: ์ž…๋ ฅ ์ฒ˜๋ฆฌ (Handling Inputs)

์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ์†Œ ํ‘œ์‹œ์ค„์— text๋ฅผ ์ž…๋ ฅํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค ์•ˆ์— ์žˆ๋Š” UI ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ ์ด text ๊ฐ’์„ ํ†ตํ•ด์„œ '๊ฒ€์ƒ‰์–ด ๋˜๋Š” URL์ธ๊ฐ€์š”?'๋ผ๊ณ  ๋ฌผ์œผ๋ฉฐ ํŒ๋‹จ์„ ํ•ฉ๋‹ˆ๋‹ค.

Chrome์—์„œ๋Š” ์ฃผ์†Œ ํ‘œ์‹œ์ค„์ด ๊ฒ€์ƒ‰ ์ž…๋ ฅ๋ž€์ด๊ธฐ๋„ ํ•˜๋ฏ€๋กœ UI ์Šค๋ ˆ๋“œ๊ฐ€ ์ž…๋ ฅ๋œ text ํŒŒ์‹ฑ ํ•˜์—ฌ Search Query๋กœ ํŒ๋‹จ ํ›„ ๊ฒ€์ƒ‰์—”์ง„(Search Engine)์—๊ฒŒ ๋ณด๋‚ด์–ด ๊ฒ€์ƒ‰์ด ๋˜๊ฒŒ ๋งŒ๋“ค์ง€ URL์ด๋ผ๊ณ  ํŒ๋‹จํ•˜์—ฌ Network Thread์—๊ฒŒ ์ „๋‹ฌํ• ์ง€ ์ค€๋น„ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ์‚ฌ์šฉ์ž๊ฐ€ Enter ํ‚ค๋ฅผ ๋ˆ„๋ฅด๊ธฐ ์ „๊นŒ์ง€์˜ ๊ณผ์ •์ด๋ž€ ๊ฑธ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”!

2๋‹จ๊ณ„: ๋‚ด๋น„๊ฒŒ์ด์…˜ ์‹œ์ž‘ (Start Navigation)

1๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ๊ฐ’์„ ๋‹ค ์ž…๋ ฅํ•œ ๋‹ค์Œ Enter ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด 2๋‹จ๊ณ„๊ฐ€ ์‹œ์ž‘๋˜๋ฉฐ UI ์Šค๋ ˆ๋“œ๊ฐ€ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ(Network call initiates)์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
์ด๋•Œ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์ด๋ž€ Network thread์—๊ฒŒ URL์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ ์‚ฌ์ดํŠธ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๊ฐ€ ํ‘œ์‹œ๋˜๊ณ  ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๊ฐ€ DNS ์กฐํšŒ์™€ ๊ฐ™์€ ์ ์ ˆํ•œ ํ”„๋กœํ† ์ฝœ์„ ํ†ต๊ณผํ•˜๊ณ  ์š”์ฒญ์— TLS ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
์ด ์‹œ์ ์—์„œ ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋Š” 301๊ณผ ๊ฐ™์€ HTTP Status Code์ธ ์„œ๋ฒ„ Redirection ํ—ค๋”๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋Š” UI ์Šค๋ ˆ๋“œ์—๊ฒŒ Redirect ํ•ด์•ผ ํ•œ๋‹ค๋Š” ํ†ต์‹ ์„ ํ•˜๊ณ  UI ์Šค๋ ˆ๋“œ๋Š” ๋‹ค๋ฅธ URL ์š”์ฒญ ์ฆ‰, ๋‹ค๋ฅธ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
์ด๋•Œ 301 ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด 3๋‹จ๊ณ„ ๊ณผ์ •์ธ ์‘๋‹ต ์ฝ๊ธฐ(Read Response)๊ฐ€ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋งŒ ์–ด์งธ์„œ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ๊ณผ์ •์„ Network Thread๊ฐ€ ์ง์ ‘์ ์œผ๋กœ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ UI Tread๋ฅผ ๊ฑฐ์ณ ์ด๋ฃจ์–ด์งˆ๊นŒ์š”? ๊ทธ ์ด์œ ๋Š” ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ(Loading spinner)์™€ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค(Renderer process) ํƒ์ƒ‰์— ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ ์‚ฌ์ง„์˜ ๊ฐ€์žฅ ์™ผ์ชฝ ํƒญ ํ‚ค์—์„œ ํŒŒ๋ž—๊ฒŒ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š” ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๊ฐ€ ๋ณด์ด์‹ค ๊ฒ๋‹ˆ๋‹ค.
์ด๋ ‡๊ฒŒ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ํƒญ์˜ ์™ผ์ชฝ ๋ชจ์„œ๋ฆฌ์— ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด UI Tread์ž…๋‹ˆ๋‹ค.
๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์ด ์‹œ์ž‘๋๋‹ค๋Š” ๊ฒƒ์„ UI Tread๊ฐ€ ์•Œ ์ˆ˜ ์—†๋‹ค๋ฉด ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
Network Thread๊ฐ€ UI Tread์—๊ฒŒ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์ด ์‹œ์ž‘๋๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ํƒ์ƒ‰๊ณผ์ •์€ 4๋‹จ๊ณ„ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์ฐพ๊ธฐ์—์„œ ์ž์„ธํžˆ ์ ์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
UI ์Šค๋ ˆ๋“œ๊ฐ€ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์„ ํ•  ๋•Œ ์ด ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์€ ์„œ๋ฒ„์™€ ํ†ต์‹ ์„ ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ์— ์ˆ˜๋ฐฑ ๋ฐ€๋ฆฌ์ดˆ๋‚˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด ํ”„๋กœ์„ธ์Šค์˜ ์†๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•œ ์ตœ์ ํ™”๋ฅผ ํ•œ๋‹ค๋Š” ๊ฒƒ๋งŒ ๊ธฐ์–ตํ•ด ์ฃผ์„ธ์š”!

3๋‹จ๊ณ„: ์‘๋‹ต ์ฝ๊ธฐ (Read response)

Response body (ํŽ˜์ด๋กœ๋“œ)๊ฐ€ ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋กœ ๋“ค์–ด์˜ค๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋Š” Response Body์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ํ™•์ธ ํ›„ HTML ํŒŒ์ผ์ธ ๊ฒฝ์šฐ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ์ „๋‹ฌํ•  ์ค€๋น„๋ฅผ ํ•˜๊ณ  ZIP ํŒŒ์ผ ๋˜๋Š” ๊ธฐํƒ€ ํŒŒ์ผ ํ˜•์‹์ธ ๊ฒฝ์šฐ ๋‹ค์šด๋กœ๋“œ ์š”์ฒญ์ด๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๊ด€๋ฆฌ์ž์— ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (a ํƒœ๊ทธ์—์„œ์˜ ๋‹ค์šด๋กœ๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ํด๋ฆญํ•˜์—ฌ ๋‹ค์šด๋กœ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ์ˆ˜๋™์ ์ธ ๊ณผ์ •๊ณผ๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค.)

ํ™•์ธ์€ ์–ด๋–ค ์‹์œผ๋กœ ์ด๋ฃจ์–ด์งˆ๊นŒ์š”?
Response body๊ฐ€ ๋“ค์–ด์˜ค๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋Š” ํ•„์š”์— ๋”ฐ๋ผ ์ŠคํŠธ๋ฆผ์˜ ์ฒ˜์Œ ๋ช‡ ๋ฐ”์ดํŠธ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์ • ๋จผ์ € Response body์˜ Content-Type ํ—ค๋”์— ํ‘œ์‹œ๋˜๋Š” ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ํ™•์ธํ•˜์ง€๋งŒ, ๋ˆ„๋ฝ๋˜๊ฑฐ๋‚˜ ์ž˜๋ชป๋˜์—ˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์—์„œ MIME ํƒ€์ž… ์Šค๋‹ˆํ•‘ (MIME Type Sniffing)์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
Sniffing ๋‹จ์–ด ๊ทธ๋Œ€๋กœ ๋ƒ„์ƒˆ๋ฅผ ๋งก๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฃผ๋กœ ๋ฐ์ดํ„ฐ์˜ ์ฒ˜์Œ ๋ช‡ ๋ฐ”์ดํŠธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ํŠน์ • ํŒŒ์ผ ํ˜•์‹์˜ ํŠน์ง•์ ์ธ ํŒจํ„ด์ด๋‚˜ ์‹๋ณ„์ž๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ •ํ™•ํ•œ MIME ํƒ€์ž…์„ ํ™•์ธํ•˜๊ณ , ๊ทธ์— ๋งž๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.(MIME ํƒ€์ž… ์Šค๋‹ˆํ•‘์€ ์ฃผ๋กœ ๋ณด์•ˆ ๋ฐ ํ˜ธํ™˜์„ฑ ์ธก๋ฉด์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ˜•์‹์˜ ํŒŒ์ผ์ด๋‚˜ ์•…์„ฑ ์ฝ”๋“œ๋ฅผ ํƒ์ง€ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.)

์ด ๊ณผ์ •์—์„œ SafeBrowsing ๊ฒ€์‚ฌ ๊ณผ์ •๊ณผ CORB(Cross Origin Read Blocking)๊ณผ์ •๋„ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € SafeBrowsing์€ ๊ตฌ๊ธ€์—์„œ ์„ธ์ดํ”„ ๋ธŒ๋ผ์šฐ์ง•์ด๋ผ๊ณ  ๊ฒ€์ƒ‰ ์‹œ ์ œ๊ณต์„ ํ•ฉ๋‹ˆ๋‹ค! Domain๊ณผ Response Data๊ฐ€ ์•Œ๋ ค์ง„ ์•…์„ฑ ์‚ฌ์ดํŠธ(Malicious site)์™€ ์ผ์น˜ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋ฉด ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ ์•Œ๋ฆผ์ด ๊ฒฝ๊ณ  ํŽ˜์ด์ง€(Warning page)๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

CORB ๊ณผ์ •์€ Same-Origin Policy์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
Same-Origin Policy๋Š” ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์œผ๋กœ, ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ๋™์ผํ•œ ์ถœ์ฒ˜(Origin)์—์„œ๋งŒ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ์ด ํ—ˆ์šฉ๋˜๋„๋ก ํ•˜๋Š” ์ •์ฑ…์ž…๋‹ˆ๋‹ค.
์ฆ‰, Same-Origin ๋ฐ์ดํ„ฐ๋Š” ์•ˆ์ „ํ•˜๊ณ  ์‹ ๋ขฐํ• ๋งŒํ•˜๋‹ค๊ณ  ๋งํ•ด์ฃผ๋Š” ์ •์ฑ…์ž…๋‹ˆ๋‹ค.

CORB๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ํŽ˜์ด์ง€์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฝ๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ ํŠนํžˆ, ๋ธŒ๋ผ์šฐ์ €๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ๋•Œ, ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ํ•ด๋‹น ์Šคํฌ๋ฆฝํŠธ์˜ ์ถœ์ฒ˜์™€ ๋‹ค๋ฅผ ๊ฒฝ์šฐ CORB๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ ์ด์šฉํ•ด Renderer process์—๊ฒŒ cross-site data๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ์ ˆ๋Œ€์ ์ธ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.
CORS ์„ค์ •์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๋„๋ฉ”์ธ ๊ฐ„์— ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ํ—ˆ์šฉํ•จ์œผ๋กœ์จ, Same-Origin Policy์™€ CORB์— ์˜ํ•œ ๋ณด์•ˆ ์ •์ฑ…์„ ์œ ์—ฐํ•˜๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์˜ˆ์‹œ๋กœ Spring Security ๋“ฑ์˜ ๋ณด์•ˆ ์„ค์ •์—์„œ ํ—ˆ์šฉํ•  ๋„๋ฉ”์ธ์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฑธ ๋– ์˜ฌ๋ฆฌ์‹œ๋ฉด ์‰ฝ๊ฒ ๋„ค์š”!

์ด ๋ชจ๋“  3๋‹จ๊ณ„ ๊ณผ์ •์„ ํ†ต๊ณผํ•œ HTML ํŒŒ์ผ ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ์ „๋‹ฌํ•  ์ค€๋น„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.

4๋‹จ๊ณ„: ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์ฐพ๊ธฐ (Find Renderer process)

1๋‹จ๊ณ„ - 3๋‹จ๊ณ„ ๊นŒ์ง€์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ ์•ˆ์ •์„ฑ์— ๋Œ€ํ•œ ๊ฒ€์‚ฌ๊ฐ€ ์™„๋ฃŒ๋˜๊ณ  ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญํ•œ ์‚ฌ์ดํŠธ๋กœ ์ด๋™ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ™•์‹ ํ•œ๋‹ค๋ฉด 4๋‹จ๊ณ„๊ฐ€ ์‹œ์ž‘๋˜๋ฉฐ Network Thread ๊ฐ€ UI Thread์—๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜์—ˆ์Œ(Data is ready)์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

์ด๋•Œ UI ์Šค๋ ˆ๋“œ๊ฐ€ ์›นํŽ˜์ด์ง€ ๋ Œ๋”๋ง์„ ์‹คํ–‰ํ•  ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฐพ๋Š”๋‹ค๊ณ  ์˜คํ•ดํ•˜๊ธฐ ๋”ฑ ์ข‹์Šต๋‹ˆ๋‹ค!
๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋Š” ์˜๋ฏธ๊ฒ ์ฃ ?!
2๋‹จ๊ณ„์—์„œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ํƒ์ƒ‰๊ณผ์ •์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž๋Š” ๋ง์„ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๊ธฐ์–ต์ด ์•ˆ๋‚˜์‹ ๋‹ค๋ฉด ๋‹ค์‹œ ์˜ฌ๋ผ๊ฐ€ ํ™•์ธํ•ด ์ฃผ์„ธ์š”!
๋“œ๋””์–ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์‹œ๊ฐ„์„ ํ™œ์šฉํ•œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ํƒ์ƒ‰ ์ตœ์ ํ™” ๊ณผ์ •์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

2๋‹จ๊ณ„์—์„œ UI ์Šค๋ ˆ๋“œ๊ฐ€ ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๋กœ URL ์š”์ฒญ์„ ๋ณด๋ƒˆ๊ธฐ์— ์–ด๋–ค ์‚ฌ์ดํŠธ๋กœ ์ด๋™ํ•˜๋Š”์ง€ ์ด๋ฏธ ์•Œ๊ณ  ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. UI ์Šค๋ ˆ๋“œ๋Š” ๋„คํŠธ์›Œํฌ ์š”์ฒญ๊ณผ ๋™์‹œ์— ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์ „์— ์ฐพ๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
๋ณ‘๋ ฌ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ์— ๋งŽ์€ ์‹œ๊ฐ„์„ ์•„๋‚„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!
๊ทธ๋ ‡๋‹ค๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ง„ํ–‰๋œ ํ›„ ๋„คํŠธ์›Œํฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ–ˆ์„ ๋•Œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ด๋ฏธ ๋Œ€๊ธฐ ์ƒํƒœ์— ์žˆ๊ฒ ์ฃ ?!
์ด๋•Œ ํฌ๋กœ์Šค ์‚ฌ์ดํŠธ๋กœ Redirection ๋˜๋Š” ๊ฒฝ์šฐ ์ด ํ”„๋กœ์„ธ์Šค์—์„œ ์ฐพ๋Š” ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ฒฝ์šฐ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ณผ์ • ๋˜ํ•œ ์ •์ƒ์ ์œผ๋กœ ์ง„ํ–‰๋˜์—ˆ๋‹ค๋ฉด ๋“œ๋””์–ด!! ์‘๋‹ต๋ฐ›์€ ๋ฐ์ดํ„ฐ์ธ HTML ํŒŒ์ผ์„ ๋ฏธ๋ฆฌ ์ฐพ์•„๋†“์€ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค!
์ด์ œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋Š” ๋ Œ๋”๋ง ํ•  ์ค€๋น„๋ฅผ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด ์ง์ „์— ๋ฐœ์ƒํ•˜๋Š” ๊ฒŒ ์ปค๋ฐ‹๋‚ด๋น„๊ฒŒ์ด์…˜์ด๋ผ๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

5๋‹จ๊ณ„: ํƒ์ƒ‰ ์ปค๋ฐ‹ (Commit Navigation)

๋“œ๋””์–ด ๋งˆ์ง€๋ง‰ ๊ณผ์ •์ž…๋‹ˆ๋‹ค!
ํ•ด๋‹น ๊ณผ์ •์„ ํ†ตํ•ด์„œ ๋‚ด๋น„๊ฒŒ์ด์…˜์ด ์ปค๋ฐ‹๋˜๊ณ  ์ด ๊ณผ์ •์ด ๋๋‚˜๋ฉด ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ์˜ ๋ Œ๋”๋ง ๊ณผ์ •์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค ์•ˆ์˜ UI ์Šค๋ ˆ๋“œ๊ฐ€ HTML ํŒŒ์ผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค์—์„œ HTML ํŒŒ์ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
ํ”„๋กœ์„ธ์Šค์—์„œ ํ”„๋กœ์„ธ์Šค๋กœ์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด๊ธฐ์— IPC(Inter-Process Communication)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์ „๋‹ฌํ•˜๋ฏ€๋กœ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ HTML ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ ‡๊ฒŒ ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค์—์„œ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋กœ์˜ ์ปค๋ฐ‹์ด ์™„๋ฃŒ๋๋‹ค๋Š” ํ™•์ธ์„ ์ˆ˜์‹ ํ•˜๋ฉด ๋‚ด๋น„๊ฒŒ์ด์…˜์ด ๋๋‚˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์ด์ œ ๋ฌธ์„œ ๋กœ๋“œ ๋‹จ๊ณ„(Document Loading Phase)๊ฐ€ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.
๋ฌธ์„œ ๋กœ๋“œ ๋‹จ๊ณ„๊ฐ€ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹œ์ž‘๋˜๋Š” ๋ Œ๋”๋ง ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์ด ๋ Œ๋”๋ง ๊ณผ์ •์ด ์‹œ์ž‘๋˜๊ธฐ ์ „ ์ฃผ์†Œ ํ‘œ์‹œ์ค„์ด ์‚ฌ์šฉ์ž๊ฐ€ ์ฒ˜์Œ์— ์ž…๋ ฅํ–ˆ๋˜ ๊ทธ ์ฃผ์†Œ์ฐฝ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๊ณ  ์œ„ ์ด๋ฏธ์ง€์—์„œ URL ์ฃผ์†Œ ์™ผ์ชฝ ์ž๋ฌผ์‡  ๋ชจ์–‘์˜ ๋ณด์•ˆ ํ‘œ์‹œ๊ธฐ(Security indicator) ๋ฐ ์‚ฌ์ดํŠธ ์„ค์ • UI๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.
์ด๋•Œ ์ž๋ฌผ์‡  ํ‘œ์‹œ์™€ ์‚ฌ์ดํŠธ ์„ค์ • UI๋Š” ์ด๋™ ์ „ ์‚ฌ์ดํŠธ๊ฐ€ ์•„๋‹Œ ์ด๋™ํ•  ์‚ฌ์ดํŠธ์˜ ์ •๋ณด๋กœ ํ™•์ธ์ด ๋˜์–ด ํ•ด๋‹น ์‚ฌ์ดํŠธ์˜ ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋‚ด๋น„๊ฒŒ์ด์…˜์ด ์ปค๋ฐ‹๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์€ ์„ธ์…˜ ํžˆ์Šคํ† ๋ฆฌ(Sesstion History)๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋’ค๋กœ ๊ฐ€๊ธฐ-์•ž์œผ๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฐฉ๊ธˆ ์ด๋™ํ•œ ์‚ฌ์ดํŠธ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํƒญ์˜ ์„ธ์…˜ ๊ธฐ๋ก์ด ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ํƒญ ๋˜๋Š” ์ฐฝ์„ ๋‹ซ์„ ๋•Œ ํƒญ/์„ธ์…˜ ๋ณต์›์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์„ธ์…˜ ๊ธฐ๋ก์€ ํ•˜๋“œ ๋””์Šคํฌ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ์— ํ™”๋ฉด์ด ๊ทธ๋ ค์ง€๊ธฐ ์ „์ด๋ผ๋„ ์ปค๋ฐ‹ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๊ณผ์ •๊นŒ์ง€๋งŒ ์™„๋ฃŒ๊ฐ€ ๋˜์—ˆ๋‹ค๋ฉด ๋’ค๋กœ ๊ฐ€๊ธฐ๋ฅผ ํ•˜์˜€์„ ๋•Œ ๋ฐ”๋กœ ์ง์ „์˜ ๋ธŒ๋ผ์šฐ์ €๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค!

์ถ”๊ฐ€ ๋‹จ๊ณ„: ์ดˆ๊ธฐ ๋กœ๋“œ ์™„๋ฃŒ

5๋‹จ๊ณ„ ๊ณผ์ •๊นŒ์ง€ ์™„๋ฃŒ ํ›„ ์ตœ์ข…์ ์œผ๋กœ ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ HTML ํŒŒ์ผ์„ ๋ฐ›์œผ๋ฉฐ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด์„œ ๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง์˜ ๋ชจ๋“  ๊ณผ์ •์ด ๋๋‚˜๋ฉด ๋ Œ๋”๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋Š” ๋ชจ๋“  onload ์ด๋ฒคํŠธ๊ฐ€ ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ํ”„๋ ˆ์ž„์—์„œ ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ํ›„์— IPC๋ฅผ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ๋ชจ๋“  ๊ณผ์ •์ด ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
์ด ์‘๋‹ต์„ ๋ฐ›์€ ๋ธŒ๋ผ์šฐ์ € ํ”„๋กœ์„ธ์Šค์˜ UI ์Šค๋ ˆ๋“œ๋Š” ํƒญ์—์„œ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค!

์ด ์‹œ์  ์ดํ›„์—๋„ ํด๋ผ์ด์–ธํŠธ ์ธก JavaScript๊ฐ€ ์—ฌ์ „ํžˆ ์ถ”๊ฐ€ ๋ฆฌ์†Œ์Šค๋ฅผ ๋กœ๋“œํ•˜๊ณ  ์ƒˆ ๋ทฐ๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ์ „ํžˆ ๋™์ ์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์ ์ž…๋‹ˆ๋‹ค.

๋” ์•Œ์•„๋ณด๊ธฐ!

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ์†Œ ํ‘œ์‹œ์ค„์— ๋‹ค๋ฅธ URL์„ ๋‹ค์‹œ ์ž…๋ ฅํ•˜๋ฉด?! - beforeunload ํ•™์Šต
  • ์„œ๋น„์Šค ์›Œ์ปค
  • Chrome ์†๋„ ๋†’์ด๊ธฐ์— ๊ด€๋ จ๋œ ํƒ์ƒ‰ ๋ฏธ๋ฆฌ ๋กœ๋“œ
    ๋“ฑ์— ๋Œ€ํ•œ ์ถ”๊ฐ€์ ์ธ ํ•™์Šต์„ ์›ํ•˜์‹ ๋‹ค๋ฉด ์•„๋ž˜์˜ ์ฐธ๊ณ  ์ž๋ฃŒ๋ฅผ ์ฝ์–ด๋ณด์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค!!

์ฐธ๊ณ ์ž๋ฃŒ

Chrome for Developers - ํƒ์ƒ‰์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ผ

'CS' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

React.js๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ผ๊นŒ ํ”„๋ ˆ์ž„์›Œํฌ์ผ๊นŒ  (0) 2024.07.02

+ Recent posts