๐Ÿค  React Server Components๋ฅผ ์ดํ•ดํ•ด๋ณด์ž! ๐Ÿค 

9rganizedChaosยท2023๋…„ 10์›” 11์ผ
3
post-thumbnail

๋–จ๋ฆฌ๋Š” ๋งˆ์Œ์„ ์•ˆ๊ณ , ์ƒˆ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ NextJS using App router๋กœ ์‹œ์ž‘ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋ช‡ ๊ฐœ์›” ์ „์ฏค ์ „ ํšŒ์‚ฌ์—์„œ app router๊ฐ€ ๋ฒ ํƒ€ ๋ฒ„์ „์œผ๋กœ ๋ฆด๋ฆฌ์ฆˆ๋˜์—ˆ์„ ๋•Œ, ํ•œ๋ฒˆ ๋ฆฌํŒฉํ† ๋ง์„ ์‹œ๋„ํ–ˆ๋‹ค๊ฐ€, ์‹คํŒจํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๊ฐ€ ์ด๋ฏธ ์ข€ ์ปค์ง€๊ธฐ๋„ ํ–ˆ๊ณ , ๋ฌด์—‡๋ณด๋‹ค "use client", "use server"ํ•˜๋ฉฐ, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ๊ฐ™์€ ๊ฐœ๋…๋“ค์ด ๋“ฑ์žฅํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋Š”๋ฐ, ์–ด๋–ค ๊ฐœ๋…์ธ์ง€ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ๋˜ ํ„ฐ๋ผ, ์šฐ์„  Server Component์˜ ๊ฐœ๋…๋ถ€ํ„ฐ ์ •ํ™•ํžˆ ์ •๋ฆฌํ•ด๋ณด๊ธฐ๋กœ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

ํ•œ๋‹ฌ ์ฏค ์ „์— ํŠธ์œ„ํ„ฐ์—์„œ ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ์™€ ๊ด€๋ จํ•ด ์ข‹์€ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๊ฐ€ ๊ณต์œ ๋œ ๊ฒƒ์„ ํ™•์ธํ–ˆ๊ณ , ์ด ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋ฅผ ๋ฒˆ์—ญํ•ด๋ณด๋ฉด์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ์ดํ•ดํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋ฅผ ๋จผ์ € ์†Œ๊ฐœ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ๐Ÿ‘‰๐Ÿผ Making Sense of React Server Components

์•„๋ž˜์—์„œ ์ด์–ด์ง€๋Š” ๋‚ด์šฉ์€ Josh W Comeau๋‹˜์˜ Making Sense of React Server Components๋ฅผ ๋ฒˆ์—ญํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ๋Š” ์ง€๋‚œ 10๋…„ ๊ฐ„, ๋Š˜ ๋” ๋‚˜์€ ์†”๋ฃจ์…˜์„ ์ฐพ์•„์™”์Šต๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ ๋ฆฌ์•กํŠธ๋Š” ์—ฌ๋Ÿฌ ์ฐจ๋ก€์˜ ๋ณ€ํ™”๋ฅผ ๊พ€ํ–ˆ๊ณ , ์ง„ํ™”ํ•ด์™”์Šต๋‹ˆ๋‹ค. ๋ช‡ ๋‹ฌ ์ „ ReactํŒ€์€ React Server Components๋ฅผ ๊ณต๊ฐœํ–ˆ๊ณ , ์ด๊ฒƒ์€ ์ตœ๊ทผ ๋ฆฌ์•กํŠธ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ์ „ํ™˜ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด React Server Components์— ๋Œ€ํ•ด ์งˆ๋ฌธํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์˜ค๋Š˜ ๊ทธ ์งˆ๋ฌธ๋“ค์— ๋‹ตํ•˜๋ฉด์„œ, ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๊ถ๊ธˆ์ฆ์„ ํ•ด์†Œํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์ž…๋‹ˆ๋‹ค.

SSR์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ

2015๋…„์— ๋ฆฌ์•กํŠธ๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ๋Œ€๋ถ€๋ถ„์˜ ๋ฆฌ์•กํŠธ ์„ค์ •์€ CSR์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์œ ์ €๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ html ํŒŒ์ผ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <script src="/static/js/bundle.js"></script>
  </body>
</html>

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

SSR์€ ์ด ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์œ ์ €๊ฐ€ ์›น์‚ฌ์ดํŠธ์— ๋ฐฉ๋ฌธํ–ˆ์„ ๋•Œ, ๋นˆ html ํŒŒ์ผ์„ ๋‚ด๋ ค๋ณด๋‚ด๋Š” ๋Œ€์‹ , ์„œ๋ฒ„๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ Œ๋”๋งํ•˜์—ฌ ์‹ค์ œ html์„ ์ƒ์„ฑํ•˜๊ณ , ์œ ์ €๋Š” ํ‹€์ด ๊ฐ–์ถฐ์ง„ html ๋ฌธ์„œ๋ฅผ ๋ฐ›๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ํ•ด๋‹น html ํŒŒ์ผ์€ ์—ฌ์ „ํžˆ script ํƒœ๊ทธ๋ฅผ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ CSR๊ณผ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ํ˜•ํƒœ๋กœ React๊ฐ€ ๋™์ž‘ํ•œ๋‹ค๋Š” ์ ์„ ์œ ๋…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. React๋Š” ๋” ์ด์ƒ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ชจ๋“  DOM ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” ์ด์ œ ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ html์„ ์ฑ„ํƒํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์„ ๋ฐ”๋กœ hydration์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Dan Abramov๋Š” hydration์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Hydration is like watering the โ€œdryโ€ HTML with the โ€œwaterโ€ of interactivity and event handlers.
Hydration์€ ๋ง๋ผ์žˆ๋Š” html์„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์™€ ์œ ์ €์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์œผ๋กœ ์ ์‹œ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

An umbrella term
๋ณดํ†ต SSR์„ ๋– ์˜ฌ๋ฆฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ๋ฆ„์„ ์ƒ์ƒํ•ฉ๋‹ˆ๋‹ค.
ใ„ฑ) ์‚ฌ์šฉ์ž๊ฐ€ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธ
ใ„ด) NodeJS ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ๋ฐ›์€ ์ฆ‰์‹œ React ์•ฑ์„ ๋ Œ๋”๋งํ•ด html ์ƒ์„ฑ
ใ„ท) ๊ฐ“ ๊ตฌ์šด html์„ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก
์‚ฌ์‹ค ์ด ๋ฐฉ๋ฒ•์€ SSR์„ ๊ตฌํ˜„ํ•˜๋Š” ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์€ ์•ฑ์„ ๋นŒ๋“œํ•  ๋•Œ, html์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ์•กํŠธ ์•ฑ์€ jsx๋ฅผ ๋ฐ”๋‹๋ผ js๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋ชจ๋“  ๋ชจ๋“ˆ์„ ๋ฒˆ๋“ค๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ html์„ ๋ฏธ๋ฆฌ ๋ Œ๋”ํ•œ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”? ์ด๊ฒƒ์„ ๋ฐ”๋กœ SSG๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. SSR์˜ ํ•˜์œ„ ๋ณ€ํ˜•์ธ ๊ฒƒ์ด์ฃ . SSR์€ ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ๋ Œ๋”๋ง ์ „๋žต์„ ํฌํ•จํ•˜๋Š” ๋Œ€ํ‘œ์šฉ์–ด์ž…๋‹ˆ๋‹ค.

Bouncing back and forth

๋ฆฌ์•กํŠธ์˜ Data-fetching์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ด…์‹œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ํ†ต์‹ ํ•˜๋Š” ๋‘๊ฐœ์˜ ๋ณ„๋„์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์˜ ๋ฆฌ์•กํŠธ ์•ฑ๊ณผ, ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์˜ RestAPI๊ฐ€ ๊ทธ๊ฒƒ์ž…๋‹ˆ๋‹ค. Data-fetching ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฐฑ์—”๋“œ๋กœ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ๋ฐฑ์—”๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์œ„ ๊ทธ๋ž˜ํ”„๋Š” CSR ์ „๋žต์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ์˜ ํ๋ฆ„์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋กœ๋”ฉ์ƒํƒœ์™€ Shell(ํ—ค๋”, ํ‘ธํ„ฐ, ์ผ๋ฐ˜์ ์ธ ๋ ˆ์ด์•„์›ƒ)๋งŒ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ Skeleton UI๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๊ณ , ๋ฆฌ์•กํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋  ๋•Œ๊นŒ์ง€ ์œ ์ €๋Š” ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ๋ด…๋‹ˆ๋‹ค.

์ด๋ฒˆ์—๋Š” SSR ์ „๋žต์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ๋ฅผ ๋„์‹ํ™”ํ•œ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค. ์ด์ œ ์‚ฌ์šฉ์ž๋Š” ๋น„์–ด ์žˆ์ง€ ์•Š์€ html ํŒŒ์ผ์„ ๋‚ด๋ ค๋ฐ›์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ถ๊ทน์ ์œผ๋กœ ํฐ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. (SSR์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.) ์œ ์ €๋Š” ๋กœ๋”ฉํ™”๋ฉด์„ ๋ณด๊ธฐ ์œ„ํ•ด ์•ฑ์„ ๋ฐฉ๋ฌธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋‚ด์šฉ์„ ๋ณด๋Ÿฌ ๋ฐฉ๋ฌธํ•˜๋Š” ๊ฒƒ์ด๋‹ˆ๊นŒ์š”. ๊ทธ๋ ‡๋‹ค๋ฉด ์ฐจ์ด๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด First Paint, Page Interactive, Content Paint ๋“ฑ์˜ ์›น ์„ฑ๋Šฅ ์ง€ํ‘œ์™€ ํ•จ๊ป˜ ์‚ดํŽด๋ด…์‹œ๋‹ค.

  • First Paint: ์ฒซ ๋ Œ๋”๋ง ๋‹จ๊ณ„๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋นˆ ํ™”๋ฉด์„ ๋ณด์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ๋ ˆ์ด์•„์›ƒ์ด ์™„์„ฑ๋˜์—ˆ์ง€๋งŒ, ๋‚ด์šฉ์€ ๋ˆ„๋ฝ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • Page Interactive: ๋ฆฌ์•กํŠธ๊ฐ€ ๋‹ค์šด๋กœ๋“œ ๋˜๊ณ , ์•ฑ์ด ๋ Œ๋”๋ง ๋ฐ hydreated ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€๋Šฅํ•œ ์š”์†Œ๊ฐ€ ์‘๋‹ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Content Paint: ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€์‹ฌ์„ ๊ฐ–๋Š” ๋‚ด์šฉ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ UI์— ๋ Œ๋”๋ง ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๋ฅผ ์™”๋‹ค๊ฐ”๋‹ค ํ•˜๋Š” ๋Œ€์‹ , ์ดˆ๊ธฐ ์š”์ฒญ์˜ ์ผ๋ถ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์™„์ „ํžˆ ์ฑ„์›Œ์ง„ UI๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ง์ ‘ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. React์—์„œ ์œ„ ๊ทธ๋ž˜ํ”„์™€ ๊ฐ™์€ ํ๋ฆ„์„ ๊ตฌํ˜„ํ•˜๊ธฐ์—๋Š” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ๋“ฑ์žฅํ•œ ๊ฒƒ์ด NextJS์™€ GatsbyJS๊ฐ™์€ ๋ฉ”ํƒ€ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

import db from 'imaginary-db';
// This code only runs on the server:
export async function getServerSideProps() {
  const link = db.connect('localhost', 'root', 'passw0rd');
  const data = await db.query(link, 'SELECT * FROM products');
  return {
    props: { data },
  };
}
// This code runs on the server + on the client
export default function Homepage({ data }) {
  return (
    <>
      <h1>Trending Products</h1>
      {data.map((item) => (
        <article key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.description}</p>
        </article>
      ))}
    </>
  );
}

NextJS์˜ Page router๋ฅผ ํ™œ์šฉํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์œ ์ €๊ฐ€ ์•ฑ์— ๋ฐฉ๋ฌธํ•ด ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด, getServerSideProps ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” props ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , props๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋˜์–ด, ๋จผ์ € ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋˜๊ณ  ๊ทธ๋Ÿฐ๋‹ค์Œ ํด๋ผ์ด์–ธํŠธ์—์„œ hydrate๋ฉ๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ์ ์€ getServerSideProps๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค์‹œ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์ด ํ•จ์ˆ˜๋Š” JS ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ๋ฉ‹์ง„ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ์—ฌ๊ธฐ์—” ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด ์ „๋žต์€ ํŠธ๋ฆฌ ๊ฐ€์žฅ ์œ„ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ๊ฐ ๋ฉ”ํƒ€ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์ €๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐฉ์‹์„ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. NextJS GatsbyJS Remix ๋ชจ๋‘ ๋‹ค๋ฅธ ๋ฐฉ์‹์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋“  React ์ปดํฌ๋„ŒํŠธ๋Š” ํ•ญ์ƒ ํด๋ผ์ด์–ธํŠธ์—์„œ hydrate๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†์„ ๋•Œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€ ์ž…๋‹ˆ๋‹ค.

์œ„ ๋‹จ์ ๋“ค์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ๋ฐ”๋กœ React Server Component์ž…๋‹ˆ๋‹ค!

๋ฆฌ์•กํŠธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ์†Œ๊ฐœ

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

import db from 'imaginary-db';
async function Homepage() {
  const link = db.connect('localhost', 'root', 'passw0rd');
  const data = await db.query(link, 'SELECT * FROM products');
  return (
    <>
      <h1>Trending Products</h1>
      {data.map((item) => (
        <article key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.description}</p>
        </article>
      ))}
    </>
  );
}
export default Homepage;

๋ฆฌ์•กํŠธ๋ฅผ ์˜ค๋žซ๋™์•ˆ ์‚ฌ์šฉํ•ด์˜จ ์‚ฌ๋žŒ์œผ๋กœ์„œ ์œ„ ์ฝ”๋“œ๋Š” ์—„์ฒญ๋‚œ ๋“ฏ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ๋Š” ์ง€๊ธˆ๊ป ๋น„๋™๊ธฐ์ผ ์ˆ˜ ์—†์—ˆ๊ณ , ๋ Œ๋”๋ง ์•ˆ์— ์ง์ ‘ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜๋„ ์—†์—ˆ์œผ๋‹ˆ๊นŒ์š”. ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๊ฐ€ ์ดํ•ดํ•ด์•ผ ํ•˜๋Š” ์ค‘์š”ํ•œ ๊ฒƒ์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” UI๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์—์„œ ํ•œ ๋ฒˆ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ์ด ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก๋˜๊ณ  ๊ทธ ์ƒํƒœ๋กœ ๋ถˆ๋ณ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์‹ค ๋ฆฌ์•กํŠธ์˜ API ์ค‘ ์ƒ๋‹น ๋ถ€๋ถ„์ด ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์œผ๋กœ state๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. state๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฐ’์ด์ง€๋งŒ, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์‹œ ๋ Œ๋”๋ง๋  ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ์š”. ์ด๋Š” ์‚ฌ์‹ค ๋ฆฌ์•กํŠธ์˜ ๋ฃฐ์— ๋Œ€ํ•ด ์ข€ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ธฐ์กด ๋ฆฌ์•กํŠธ์—์„œ ๋ชจ๋“  ๋ Œ๋”๋ง์—์„œ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋„๋ก useEffect๋ฅผ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋œ๋‹ค๋ฉด ์œ„์™€ ๊ฐ™์€ ๊ฑฑ์ •์„ ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋†€๋ž๋„๋ก ๊ฐ„๋‹จํ•˜์ง€๋งŒ React Server Component ํŒจ๋Ÿฌ๋‹ค์ž„์€ ํ›จ์”ฌ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ์ผ๋ฐ˜์ ์ธ ์ปดํฌ๋„ŒํŠธ (์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์—์„œ ์ด๋ฅผ Client Components๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์‹ค ํด๋ผ์ธ์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๋ชจ๋‘์—์„œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์— ์†์ง€ ๋ง™์‹œ๋‹ค.)

(์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” JS ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ Hydrate๋˜๊ฑฐ๋‚˜ ๋ฆฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

ํ˜ธํ™˜ ํ™˜๊ฒฝ

ํ˜„ ์‹œ์  React Server Components๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ NextJS 13.4+๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘์—์„œ๋„ App router๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ–ฅํ›„์— ๋” ๋งŽ์€ ๋ฆฌ์•กํŠธ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ Server Components๋ฅผ ํ†ตํ•ฉํ•˜๊ธฐ ์‹œ์ž‘ํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์ง€์ •ํ•˜๊ธฐ

React Server Components๋ผ๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„ ์†์—์„œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋”ฐ๋กœ ์ปดํฌ๋„ŒํŠธ์— ํ‘œ์‹œํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


'use client';
import React from 'react';
function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Current value: {count}
    </button>
  );
}
export default Counter;

๋งจ ์ฒซ ์ค„์— ์ ํžŒ "use client"๊ฐ€ ๊ทธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ JS ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์–ด ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ƒํ•˜๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ์šฐ๋ฆฌ๋Š” Strict Mode๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด "use strict"๋ผ๋Š” ์ง€์‹œ๋ฌธ์„ ํ™œ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด "use server"๋ฅผ ์ ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. "use server"๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์ธ "์„œ๋ฒ„ ์•ก์…˜(Server Action)"์— ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค. (ํ•ด๋‹น ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๋‚ด์šฉ)

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

๋ฐ”์šด๋”๋ฆฌ

React Server Components๊ฐ€ ์ต์ˆ™ํ•ด์ง€๋Š” ๊ณผ์ •์—์„œ ๊ฐ€์žฅ ๋จผ์ € ๋– ์˜ค๋ฅธ ์งˆ๋ฌธ ์ค‘ ํ•˜๋‚˜๋Š” "ํ”„๋กญ์Šค๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋‚˜์š”?" ์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค.

function HitCounter({ hits }) {
  return (
    <div>
      Number of hits: {hits}
    </div>
  );
}

์ฒซ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ๋ Œ๋”๋ง ๋  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋งˆํฌ์—…์„ ๊ฐ€์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

<div>
  Number of hits: 0
</div>

๊ทธ๋Ÿฌ๋‚˜ ๋งŒ์ผ hits์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด? HitCounter๋Š” ๋ฆฌ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•˜์ง€๋งŒ, ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ์ด๋ฏ€๋กœ ๋‹ค์‹œ ๋ Œ๋”๋ง๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค! ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •
ํ•ด๋ด…์‹œ๋‹ค.

์œ„ ๊ทธ๋ฆผ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ด๋ฉด ๋ง์ด๋ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ํ”„๋กญ์Šค๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ํ…Œ๋‹ˆ๊นŒ์š”. ๊ทธ๋Ÿฌ๋‚˜ Article ์ปดํฌ๋„ŒํŠธ๊ฐ€ hits ์ƒํƒœ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. state๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Article์ด ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ HitCounter์™€ Discussion๋„ ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ๋ฆฌ์•กํŠธํŒ€์€ ๊ทœ์น™์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. "use client" ์ง€์‹œ๋ฌธ์€ HitCounter์™€ Discussion์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜๋˜์–ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

"use client" ์ง€์‹œ๋ฌธ์„ Article ์ปดํฌ๋„ŒํŠธ์— ์ถ”๊ฐ€ํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ ๊ฒฝ๊ณ„๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ๊ณ„ ๋‚ด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์•”์‹œ์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. HitCounter์™€ ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ์— "use client" ์ง€์‹œ๋ฌธ์ด ์—†๋”๋ผ๋„ ํŠน์ • ์ƒํ™ฉ์—์„œ๋Š” ์—ฌ์ „ํžˆ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ˆ˜ํ™”๋˜๊ฑฐ๋‚˜ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๋ชจ๋“  ํŒŒ์ผ์— ์ผ์ผ์ด "use client"๋ฅผ ์ž‘์„ฑํ•ด์ค„ ํ•„์š”๋Š” ์—†๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

๋Œ€์•ˆ

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

'use client';
import { DARK_COLORS, LIGHT_COLORS } from '@/constants.js';
import Header from './Header';
import MainContent from './MainContent';
function Homepage() {
  const [colorTheme, setColorTheme] = React.useState('light');
  const colorVariables = colorTheme === 'light'
    ? LIGHT_COLORS
    : DARK_COLORS;
  return (
    <body style={colorVariables}>
      <Header />
      <MainContent />
    </body>
  );
}

๋‹คํฌ๋ชจ๋“œ ์„ค์ • ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. ๋‹คํฌ๋ชจ๋“œ๋Š” body ํƒœ๊ทธ์— css ๋ณ€์ˆ˜๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ฑ ํŠธ๋ฆฌ์—์„œ ๋†’์€ ์œ„์น˜์— ์ง€์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Homepage๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์•ฑ์˜ ์ตœ์ƒ๋‹จ์ด๋ฏ€๋กœ ๋‹ค๋ฅธ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋„ ์•”์‹œ์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด theme ๊ด€๋ฆฌ ๋‚ด์šฉ์„ ์ž์ฒด ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ๋ณ„๋„์˜ ํŒŒ์ผ๋กœ ์ด๋™์‹œํ‚ค๊ฒ ์Šต๋‹ˆ๋‹ค.

// /components/ColorProvider.js
'use client';
import { DARK_COLORS, LIGHT_COLORS } from '@/constants.js';
function ColorProvider({ children }) {
  const [colorTheme, setColorTheme] = React.useState('light');
  const colorVariables = colorTheme === 'light'
    ? LIGHT_COLORS
    : DARK_COLORS;
  return (
    <body style={colorVariables}>
      {children}
    </body>
  );
}

์ด์ œ Homepage.js๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

// /components/Homepage.js
import Header from './Header';
import MainContent from './MainContent';
import ColorProvider from './ColorProvider';
function Homepage() {
  return (
    <ColorProvider>
      <Header />
      <MainContent />
    </ColorProvider>
  );
}

Homepage์—์„œ ์ƒํƒœ ๋˜๋Š” ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ ์ธก ๋ฆฌ์•กํŠธ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, Homepage์—์„œ "use client"๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ Header์™€ MainContent๋Š” ๋” ์ด์ƒ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์•”์‹œ์ ์œผ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ColorProvider๊ฐ€ ์—ฌ์ „ํžˆ Header์™€ MainContent์˜ ๋ถ€๋ชจ๊ฐ€ ์•„๋‹ˆ๋ƒ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ client boundries์—์„œ ๋ถ€๋ชจ/์ž์‹ ๊ด€๊ณ„๋Š” ์ค‘์š”์น˜ ์•Š์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํžˆ ๋งํ•˜๋ฉด "use client" ์ง€์‹œ๋ฌธ์€ ํŒŒ์ผ/๋ชจ๋“ˆ ์ˆ˜์ค€์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜จ ๋ชจ๋“  ๋ชจ๋“ˆ๋„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

๋‚ด๋ถ€๋™์ž‘์›๋ฆฌ ์‚ดํ”ผ๊ธฐ

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ถœ๋ ฅ์€ ์–ด๋–ป๊ฒŒ ๋ณด์ด๊ณ  ์‹ค์ œ๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ผ๊นŒ์š”? ๋งค์šฐ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋กœ ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค.

function Homepage() {
  return (
    <p>
      Hello world!
    </p>
  );
}

์œ„ ์ปดํฌ๋„ŒํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ผ๊ณ  ํ‘œ์‹œํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์„œ๋ฒ„์—์„œ๋งŒ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ด ์•ฑ์„ ๋ฐฉ๋ฌธํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ html ๋ฌธ์„œ๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html>
  <body>
    <p>Hello world!</p>
    <script src="/static/js/bundle.js"></script>
    <script>
      self.__next['$Homepage-1'] = {
        type: 'p',
        props: null,
        children: "Hello world!",
      };
    </script>
  </body>
</html>

์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•œ ์žฌ๊ตฌ์กฐํ™”
์œ„ html์—์„œ ์‹ค์ œ ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์„œ ํŒŒ์ผ ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ์ตœ์ ํ™”๋กœ ๋ฌธ์ž์—ฌ๋กœํ•˜๋œ JSON ๋ฐฐ์—ด์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ head ํƒœ๊ทธ ๋“ฑ์˜ ๋น„ ํ•ต์‹ฌ์š”์†Œ๋Š” ๋ชจ๋‘ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์œ„ ๋‚ด์šฉ์„ ์‚ดํŽด๋ณด๋ฉด, JS ๋ฒˆ๋“ค์„ ๋กœ๋“œํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฒˆ๋“ค์—๋Š” ๋ฆฌ์•กํŠธ์™€ ๊ฐ™์€ ์ข…์†์„ฑ ๋ฟ ์•„๋‹ˆ๋ผ, ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋„ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Homepage ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ด๋ฏ€๋กœ ์ด ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๋Š” ์ด ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ ๋ฐ‘์—๋Š” ์ธ๋ผ์ธ JS๊ฐ€ ํฌํ•จ๋œ ๋‘ ๋ฒˆ์งธ ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

self.__next['$Homepage-1'] = {
  type: 'p',
  props: null,
  children: "Hello world!",
};

์œ„ ๋‚ด์šฉ์ด ํ•˜๋Š” ์—ญํ• ์€, ๋ฆฌ์•กํŠธ์—๊ฒŒ "์•Œ๊ฒ ์–ด, Homepage ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ์ง€? ์—ฌ๊ธฐ์— ์ด๊ฒŒ ๋ Œ๋”๋ง๋œ ๋‚ด์šฉ์ด์•ผ"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ์•กํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ Œ๋”๋งํ•  ๋•Œ ๊ฐ€์ƒDOM์„ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—๋Š” ์ด๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. JS ๋ฒˆ๋“ค์— ์ฝ”๋“œ๊ฐ€ ์—†์œผ๋‹ˆ๊นŒ์š”. ๊ทธ๋ž˜์„œ ์ด๋•Œ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฆฌ์•กํŠธ๊ฐ€ ๋กœ๋“œ๋  ๋•Œ, ๊ฐ€์ƒDOM ๋นŒ๋“œ์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์œ„์™€ ๊ฐ™์€ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ColorProvider๊ฐ€ ๋™์ž‘ํ•˜๋Š” ์›๋ฆฌ์ž…๋‹ˆ๋‹ค. Header์™€ MainContent์—์„œ ๋‚˜์˜จ ์ถœ๋ ฅ๋ฌผ์€ children ํ”„๋กญ์„ ํ†ตํ•ด ColorProvider ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ColorProvider๋Š” ์–ผ๋งˆ๋“  ๋ฆฌ๋ Œ๋” ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฐ์ดํ„ฐ(Homepage๋ผ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง€์นญํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.)๋Š” ์„œ๋ฒ„์— ๊ณ ์ •๋˜์–ด ์žˆ๊ณ  ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋งŒ์ผ ์–ด๋–ป๊ฒŒ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‹œ๋ฆฌ์–ผ๋ผ์ด์ฆˆ๋˜๊ณ  ๋„คํŠธ์›Œํฌ๋กœ ์ „์†ก๋˜๋Š”์ง€ ์‹ค์ œ ํ‘œํ˜„์„ ๋ณด๊ณ  ์‹ถ๋‹ค๋ฉด ๊ฐœ๋ฐœ์ž Alvar Lagerlรถf์˜ RSC Devtools๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”!

์ด์ 

์‚ฌ์‹ค 2016๋…„ ํ›„๋กœ NextJS์—์„œ ์„œ๋ฒ„์ „์šฉ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€ ์ด์ „์—๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ์„œ๋ฒ„ ์ „์šฉ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๋‹ค๋Š” ๊ฒƒ์ด์ฃ . ์‚ฌ์‹ค ๊ฐ€์žฅ ๋ช…๋ฐฑํ•œ ์ด์ ์€ ์„ฑ๋Šฅ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” JS ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์šด๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” JS ์–‘๊ณผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ˆ˜๊ฐ€ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

์‚ฌ์‹ค NextJS ์•ฑ์€ ํŽ˜์ด์ง€์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ํƒ€์ด๋ฐ์— ๋Œ€ํ•œ ์„ฑ๋Šฅ๋ฉด์—์„œ ์ถฉ๋ถ„ํžˆ ๋น ๋ฆ…๋‹ˆ๋‹ค. ์˜๋ฏธ๋ก ์  html ์›์น™์„ ๋”ฐ๋ฅด๋ฉด ๋ฆฌ์•กํŠธ๋Š” ํ•˜์ด๋“œ๋ ˆ์ดํŠธ ๋˜๊ธฐ ์ „์— ์ด๋ฏธ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. link๋‚˜ form, accordions ๋“ฑ. ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ์ ํŠธ์—์„œ React๊ฐ€ hydrate๋˜๊ธฐ๊นŒ์ง€ ๋ช‡์ดˆ๊ฐ€ ๊ฑธ๋ ค๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ์ •๋ง ์ค‘์š”ํ•œ ์ ์€ ๊ธฐ๋Šฅ vs ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๊ฐ–๊ณ  ๋” ์ด์ƒ ํ•œ์ชฝ์„ ํฌ๊ธฐํ•˜๊ฑฐ๋‚˜ ํƒ€ํ˜‘ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ์—๋„ˆ๋Š” ๊ตฌ๋ฌธ ๊ฐ•์กฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

function exampleJavaScriptFunction(param) {
  return "Hello world!"
}

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

JS๋ฒˆ๋“ค์— ํฌํ•จํ•˜๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋งŽ์€ ๋น„์šฉ์„ ์ง€๋ถˆํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋“ค์ด ์„œ๋ฒ„์—์„œ ๋ฌด๋ฃŒ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฒˆ๋“ค์— ๋‹จ 1kb๋„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ฑ๋Šฅ๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๋งŒํผ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ Server Component๋Š” ๊ทธ ๋ฌธ๋ฒ•์ด ๋งค์šฐ ๊ฐ„๊ฒฐํ•˜๊ณ  ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ข…์†์„ฑ ๋ฐฐ์—ด, ํด๋กœ์ €, ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๋“ฑ์œผ๋กœ ๋” ์ด์ƒ ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ง€๊ธˆ์€ ์•„์ง ์ดˆ๊ธฐ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์„ ํ™œ์šฉํ•ด Bright์™€ ๊ฐ™์€ ์ƒˆ๋กœ์šด ์†”๋ฃจ์…˜์„ ์ง€์†์ ์œผ๋กœ ๋ฐœ๊ฒฌํ•ด๋‚˜๊ฐ€๊ธธ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.

๋˜ React Server Components๋Š” ๋งค์šฐ ํฅ๋ฏธ๋กœ์šด ๋ฐœ์ „์ž…๋‹ˆ๋‹ค๋งŒ ์‹ค์ œ๋กœ๋Š” ๋ชจ๋˜ ๋ฆฌ์•กํŠธ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. React Server Components๋ฅผ Suspense์™€ Streaming SSR ์•„ํ‚คํ…์ฒ˜์™€ ๊ฒฐํ•ฉํ•  ๋•Œ ์ •๋ง ํฅ๋ฏธ๋กœ์›Œ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

profile
๋ถ€์ •ํ™•ํ•œ ์ •๋ณด๋‚˜ ์ž˜๋ชป๋œ ์ •๋ณด๋Š” ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๋น ๋ฅด๊ฒŒ ์ˆ˜์ •ํ† ๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€