Back

React Server Components ์‹ฌ์ธต ๋ถ„์„: useEffect, ์ด์ œ ๋ณด๋‚ด์ค์‹œ๋‹ค

Next.js 13, 14๊ฐ€ ๋‚˜์˜ค๋ฉด์„œ React ์ƒํƒœ๊ณ„๊ฐ€ ์‹œ๋Œ์‹œ๋Œํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์ƒˆ๋กœ์šด ํ›…์ด ๋‚˜์™”๋‹ค๊ฑฐ๋‚˜ ์†๋„๊ฐ€ ๋นจ๋ผ์กŒ๋‹ค๋Š” ์ˆ˜์ค€์ด ์•„๋‹ˆ์ฃ . ์šฐ๋ฆฌ๋Š” ์ง€๊ธˆ React Server Components(RSC) ๋ผ๋Š” ๊ฑฐ๋Œ€ํ•œ ํŒŒ๋„๋ฅผ ๋งˆ์ฃผํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์†”์งํžˆ ๋งํ•ด์„œ, ๊ฝค ํ˜ผ๋ž€์Šค๋Ÿฝ์ง€ ์•Š๋‚˜์š”? "์ง๋ ฌํ™”(serialization) ์˜ค๋ฅ˜" ๊ฐ™์€ ๋‚ฏ์„  ์—๋Ÿฌ๋“ค์ด ํŠ€์–ด๋‚˜์˜ค๊ณ , ๊ทธ๋™์•ˆ ์ž˜ ์จ์™”๋˜ useEffect๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์€ ์ด์ œ ์•ˆํ‹ฐ ํŒจํ„ด ์ทจ๊ธ‰์„ ๋ฐ›๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋‹ˆ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋‹ˆ ๊ฒฝ๊ณ„๋ฅผ ๋‚˜๋ˆ„๋Š” ๊ฒƒ๋„ ๋จธ๋ฆฌ ์•„ํ”„๊ณ ์š”.

ํ•ต์‹ฌ์€ ์ด๊ฒ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ˆ˜๋…„๊ฐ„ "๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋‹น์—ฐํžˆ ์“ฐ๋Š” ๊ฒƒ"์ด๋ผ ์—ฌ๊ฒผ๋˜ useEffect๊ฐ€ ์ด์ œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ์˜ ์ฃผ๋„๊ถŒ์„ ๋บ๊ฒผ๋‹ค๋Š” ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ useEffect๋Š” ์ด์ œ ์“ธ๋ชจ์—†๋Š” ๊ฑธ๊นŒ์š”? ๊ทธ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ "๋ฐ์ดํ„ฐ ํŽ˜์นญ" ์šฉ๋„๋กœ๋Š” ์ด์ œ ๋†“์•„์ค„ ๋•Œ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๊ฒ‰ํ•ฅ๊ธฐ ์‹์œผ๋กœ "RSC ์“ฐ๋Š” ๋ฒ•"๋งŒ ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋„๋Œ€์ฒด ๋‚ด๋ถ€๊ฐ€ ์–ด๋–ป๊ฒŒ ๋Œ์•„๊ฐ€๋Š”์ง€, ์™œ RSC๊ฐ€ ๊ธฐ์กด SPA์˜ ์•„ํ‚คํ…์ฒ˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” "์น˜ํŠธํ‚ค"์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ๋ฐ”๋€ ๋ฉ˜ํƒˆ ๋ชจ๋ธ์ด ์‹ค์ œ ๋Œ€๊ทœ๋ชจ ์•ฑ ๊ฐœ๋ฐœ์— ์–ด๋–ค ์ด์ ์„ ์ฃผ๋Š”์ง€ ์•„์ฃผ ๊นŠ๊ฒŒ ํŒŒ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์šฐ๋ฆฌ๊ฐ€ ๊ฒช๋˜ ๊ณ ํ†ต: ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์›Œํ„ฐํด (Waterfall)

RSC๊ฐ€ ์™œ ํ•„์š”ํ•œ์ง€ ๋‚ฉ๋“ํ•˜๋ ค๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๋™์•ˆ React๋กœ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๊ฒช์—ˆ๋˜ ๊ณ ์งˆ์ ์ธ ๋ฌธ์ œ๋ฅผ ์ง์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ React ์•ฑ(SPA)์„ ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ JS ๊พธ๋Ÿฌ๋ฏธ๋ฅผ ์ž”๋œฉ ๋‚ด๋ ค๋ฐ›์Šต๋‹ˆ๋‹ค. React๊ฐ€ ์‹คํ–‰๋˜๊ณ , ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ์ œ์•ผ useEffect๊ฐ€ "์•„์ฐจ, ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์™€์•ผ์ง€" ํ•˜๊ณ  API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

// ์ต์ˆ™ํ•˜์‹œ์ฃ ? ์šฐ๋ฆฌ๊ฐ€ ๋งจ๋‚  ์งœ๋˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); // 1. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜๊ณ  ๋‚˜์„œ์•ผ ํŒจ์นญ ์‹œ์ž‘ useEffect(() => { fetch(`/api/users/${userId}`).then(data => { setUser(data); setLoading(false); }); }, [userId]); if (loading) return <Spinner />; return ( <div> <h1>{user.name}</h1> {/* 2. ์ด๊ฒŒ ๋ Œ๋”๋ง ๋˜์–ด์•ผ ๋น„๋กœ์†Œ ์ž์‹์˜ ํŒจ์นญ์ด ์‹œ์ž‘๋จ */} <UserPosts userId={userId} /> </div> ); }

์—ฌ๊ธฐ์„œ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๋Š” <UserPosts />์ž…๋‹ˆ๋‹ค. ๋ถ€๋ชจ์ธ UserProfile์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค ๋กœ๋”ฉ๋˜๊ณ  ๋ Œ๋”๋ง์ด ๋๋‚˜์•ผ, ๋น„๋กœ์†Œ ์ž์‹์ธ UserPosts๊ฐ€ ๋งˆ์šดํŠธ๋˜๊ณ  ์ž์‹ ์˜ useEffect๋ฅผ ๋Œ๋ฆฝ๋‹ˆ๋‹ค.

  1. JS ๋กœ๋“œ
  2. UserProfile ๋ Œ๋”๋ง โ†’ API ํ˜ธ์ถœ
  3. (๊ธฐ๋‹ค๋ฆผ...) โ†’ ๋ฐ์ดํ„ฐ ๋„์ฐฉ โ†’ ๋ฆฌ๋ Œ๋”๋ง
  4. UserPosts ๋งˆ์šดํŠธ โ†’ API ํ˜ธ์ถœ
  5. (๋˜ ๊ธฐ๋‹ค๋ฆผ...) โ†’ ๋ฐ์ดํ„ฐ ๋„์ฐฉ โ†’ ๋ฆฌ๋ Œ๋”๋ง

์ด๊ฒŒ ๋ฐ”๋กœ ๋„คํŠธ์›Œํฌ ์›Œํ„ฐํด(Network Waterfall) ์ž…๋‹ˆ๋‹ค. ํญํฌ์ˆ˜์ฒ˜๋Ÿผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋–จ์–ด์ง„๋‹ค๋Š” ๊ฑฐ์ฃ . ์‚ฌ์šฉ์ž๋Š” ๊ณ„์† ๋Œ์•„๊ฐ€๋Š” ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ๋ณด๋ฉฐ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. (Loading Spinner Hell ๐Ÿ˜ˆ)

React Query๋‚˜ SWR์ด ์ด ๋ฌธ์ œ๋ฅผ ๋งŽ์ด ์™„ํ™”ํ•ด์ฃผ๊ธด ํ–ˆ์ง€๋งŒ, ๊ทผ๋ณธ์ ์ธ ํ•œ๊ณ„๋Š” ์—ฌ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค. "๋ฐ์ดํ„ฐ ๋กœ์ง์€ ์ปดํฌ๋„ŒํŠธ์— ์žˆ๋Š”๋ฐ, ์‹คํ–‰์€ ๋ฌด์กฐ๊ฑด ํด๋ผ์ด์–ธํŠธ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ•ด์•ผ ํ•œ๋‹ค" ๋Š” ์ œ์•ฝ ๋•Œ๋ฌธ์ด์ฃ .

2. React Server Components: ์„œ๋ฒ„๋ฅผ ์ปดํฌ๋„ŒํŠธํ™” ํ•˜๋‹ค

RSC์˜ ์•„์ด๋””์–ด๋Š” ๋‹จ์ˆœํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค. "๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ณณ(์„œ๋ฒ„)์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฏธ๋ฆฌ ๋ Œ๋”๋งํ•˜์ž."

RSC๋Š” ๋ฐฑ์—”๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. API๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค ํ•„์š” ์—†์ด ๋ฐ”๋กœ DB๋ฅผ ์ฐ”๋Ÿฌ๋„ ๋ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋กœ ์ฝ”๋“œ๋ฅผ ๋ณด๋‚ผ ํ•„์š”๋„ ์—†์ฃ .

RSC ๋ฐฉ์‹์œผ๋กœ ๋ฐ”๊พธ๋ฉด ์ฝ”๋“œ๊ฐ€ ์ด๋ ‡๊ฒŒ ๋ฐ”๋€๋‹ˆ๋‹ค:

// app/users/[id]/page.tsx import db from '@/lib/db'; // 1. async ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. async function UserProfile({ params }) { // 2. DB ์ง์ ‘ ์กฐํšŒ! API fetch ๋Œ€์‹  await๋กœ ๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. const user = await db.user.findUnique({ where: { id: params.id } }); return ( <div> <h1>{user.name}</h1> <UserPosts userId={params.id} /> </div> ); } async function UserPosts({ userId }) { const posts = await db.post.findMany({ where: { authorId: userId } }); return ( <ul> {posts.map(post => <li key={post.id}>{post.title}</li>)} </ul> ); }

ํ™• ๋‹ฌ๋ผ์ง„ ์ ์ด ๋ณด์ด์‹œ๋‚˜์š”?

  1. useState, useEffect ์‚ญ์ œ: ์ƒํƒœ ๊ด€๋ฆฌ๋‚˜ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.
  2. async/await ์‚ฌ์šฉ: ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์ปดํฌ๋„ŒํŠธ ๋ ˆ๋ฒจ์—์„œ ๋ฐ”๋กœ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ UserProfile์„ ๊ทธ๋ฆด ๋•Œ DB๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋ฉด ๋ฐ”๋กœ UserPosts๋กœ ๋„˜์–ด๊ฐ€์„œ ๋˜ DB๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ชจ๋“  ๊ฒŒ ์„œ๋ฒ„ ๋‚ด๋ถ€(๋ฐ์ดํ„ฐ์„ผํ„ฐ) ์—์„œ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์ง€์—ฐ(Latency)์ด ๊ฑฐ์˜ ์—†์ฃ . ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๋ฅผ ์™”๋‹ค ๊ฐ”๋‹ค ํ•˜๋Š” ๋น„์šฉ์ด '0'์— ์ˆ˜๋ ดํ•ฉ๋‹ˆ๋‹ค.

3. ๋ธŒ๋ผ์šฐ์ €๋Š” HTML ๋Œ€์‹  "Flight"๋ฅผ ๋ฐ›๋Š”๋‹ค

"๊ทธ๋Ÿผ ๊ทธ๋ƒฅ SSR(Server Side Rendering)์ด๋ž‘ ๊ฐ™์€ ๊ฑฐ ์•„๋ƒ?"๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

RSC๋ฅผ ์š”์ฒญํ•˜๋ฉด ์„œ๋ฒ„๋Š” ์™„์„ฑ๋œ HTML ๋ฌธ์ž์—ด์„ ์ฃผ๋Š” ๊ฒŒ ์•„๋‹™๋‹ˆ๋‹ค. HTML์€ SSR์˜ ์—ญํ• ์ด๊ณ ์š”, RSC๋Š” "Flight" ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํŠน๋ณ„ํ•œ ํฌ๋งท์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

1:I["./src/components/ClientCounter.js",["234","345"],"ClientCounter"] 0:["$","div",null,{"children":[["$","h1",null,{"children":"Hello World"}],["$","$L1",null,{}]]}]

์ด๋Ÿฐ ์‹์˜ ๋ฐ์ดํ„ฐ ๋ฉ์–ด๋ฆฌ๊ฐ€ ๋‚ ๋ผ์˜ต๋‹ˆ๋‹ค. React Element ํŠธ๋ฆฌ๋ฅผ ํ…์ŠคํŠธ๋กœ ์ง๋ ฌํ™”(Serialize)ํ•œ ๊ฑฐ์ฃ .

  • ์ผ๋ฐ˜ HTML ํƒœ๊ทธ(div, h1)๋Š” ๊ทธ๋Œ€๋กœ ๋“ค์–ด์žˆ๊ณ ,
  • Client Component๊ฐ€ ํ•„์š”ํ•œ ์ž๋ฆฌ์—๋Š” "์—ฌ๊ธฐ์— ClientCounter.js ๊ฐ–๋‹ค ๋ถ™์—ฌ!"๋ผ๋Š” ์ฐธ์กฐ(Reference)๋งŒ ๋‚จ๊ฒจ๋‘ก๋‹ˆ๋‹ค.

์ค‘์š”ํ•œ ๊ฑด Server Component์˜ ์ฝ”๋“œ๋Š” ๋ธŒ๋ผ์šฐ์ €๋กœ ์ „์†ก๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
๋งŒ์•ฝ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‚ ์งœ ํฌ๋งทํŒ…์„ ์œ„ํ•ด ๋ฌด๊ฑฐ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ผ๋‹ค๊ณ  ์นฉ์‹œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ฒฐ๊ณผ ๊ฐ’์ธ "2025๋…„ 12์›” 9์ผ"์ด๋ผ๋Š” ํ…์ŠคํŠธ๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์šฉ๋Ÿ‰์€ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Zero Bundle Size์˜ ๊ฟˆ์ด ์‹คํ˜„๋˜๋Š” ์ˆœ๊ฐ„์ด์ฃ .

4. 'use client'๋Š” ํด๋ผ์ด์–ธํŠธ ์ „์šฉ์ด ์•„๋‹ˆ๋‹ค

RSC๋Š” ์„œ๋ฒ„ ์ „์šฉ์ด๋ผ ์ธํ„ฐ๋ž™์…˜(ํด๋ฆญ, ์ž…๋ ฅ ๋“ฑ)์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐ๋ž™์…˜์ด ํ•„์š”ํ•˜๋ฉด "use client" ๋ฅผ ์„ ์–ธํ•ด์„œ Client Component๋กœ ์ „ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// src/components/LikeButton.tsx 'use client'; // "์ด๋ณด๊ฒŒ, ์—ฌ๊ธฐ๋ถ€ํ„ฐ๋Š” React๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋•๊ฒŒ ํ•ด์ฃผ๊ฒŒ" import { useState } from 'react'; export default function LikeButton() { const [likes, setLikes] = useState(0); return <button onClick={() => setLikes(likes + 1)}>์ข‹์•„์š” {likes}</button>; }

์˜คํ•ดํ•˜์‹œ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒŒ, "use client"๋ฅผ ๋ถ™์ธ๋‹ค๊ณ  ํ•ด์„œ "CSR(Client Side Rendering)"๋งŒ ํ•˜๋Š” ๊ฒŒ ์•„๋‹™๋‹ˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ๋“ค๋„ ์„œ๋ฒ„์—์„œ ๋ฏธ๋ฆฌ HTML๋กœ ๋ Œ๋”๋ง(SSR) ๋˜์–ด์„œ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋ฅผ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ง€, ๋ธŒ๋ผ์šฐ์ €์—์„œ JS๊ฐ€ ๋ถ™์–ด์„œ(Hydration) ์›€์ง์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์กฐํ•ฉ์˜ ๋ฌ˜๋ฏธ (Composition)

"๊ทธ๋Ÿผ ์ตœ์ƒ์œ„์— Context Provider ๊ฐ์‹ธ๋ ค๋ฉด "use client" ๋ถ™์—ฌ์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ๋Ÿผ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค๋„ ๋‹ค ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋˜๋Š” ๊ฑฐ ์•„๋ƒ?"

์•„๋‹™๋‹ˆ๋‹ค. Children ํŒจํ„ด์„ ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// app/page.tsx (Server Component) import ClientLayout from './ClientLayout'; // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ import ServerPostList from './ServerPostList'; // ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ export default function Page() { return ( // Server Component๋ฅผ Client Component์˜ ์ž์‹์œผ๋กœ ๋„˜๊น๋‹ˆ๋‹ค! <ClientLayout> <ServerPostList /> </ClientLayout> ); }
// app/ClientLayout.tsx (Client Component) 'use client'; export default function ClientLayout({ children }) { // children์œผ๋กœ ๋“ค์–ด์˜จ ServerPostList๋Š” ์ด๋ฏธ ์„œ๋ฒ„์—์„œ ๋‹ค ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๋ฌผ(์Šฌ๋กฏ)์ž…๋‹ˆ๋‹ค. // ClientLayout ์ž…์žฅ์—์„œ๋Š” ๊ทธ์ € "๋ญ”์ง€ ๋ชจ๋ฅผ ๋ฆฌ์•กํŠธ ๋…ธ๋“œ"์ผ ๋ฟ์ด์ฃ . return <div className="dark-theme">{children}</div>; }

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ServerPostList๋Š” ์—ฌ์ „ํžˆ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‚จ์•„์„œ DB์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ , ClientLayout์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•ฉ์„ฑ(Composition) ํŒจํ„ด์ด RSC ์•„ํ‚คํ…์ฒ˜์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

5. ๊ฒฐ๋ก : useEffect๋Š” ์ด์ œ ๋ณด๋‚ด์ฃผ์ž

์šฐ๋ฆฌ๊ฐ€ ๊ทธ๋™์•ˆ useEffect๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๋˜ ๊ฑด, React ๋ Œ๋”๋ง ์‚ฌ์ดํด ์ค‘์—์„œ ๋ถ€์ˆ˜ ํšจ๊ณผ(Side Effect)๋ฅผ ์ฒ˜๋ฆฌํ•  ์œ ์ผํ•œ ํƒ€์ด๋ฐ์ด ๊ทธ๋•Œ์˜€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ์„ ํƒ์ด์—ˆ์ฃ .
ํ•˜์ง€๋งŒ ๋‹จ์ ์ด ๋„ˆ๋ฌด ์ปธ์Šต๋‹ˆ๋‹ค.

  1. ๋А๋ฆฝ๋‹ˆ๋‹ค. (๋ Œ๋”๋ง โ†’ ๋งˆ์šดํŠธ โ†’ ์ดํŽ™ํŠธ ์‹คํ–‰ โ†’ ํŒจ์นญ ์‹œ์ž‘)
  2. ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. (๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌ, ๋ ˆ์ด์Šค ์ปจ๋””์…˜ ์ฒ˜๋ฆฌ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€...)

์ด์ œ RSC์—์„œ๋Š” ๋ Œ๋”๋ง ์ค‘์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

const data = await getData(); // ๊ธฐ๋‹ค๋ ค! return <View data={data} />; // ๋‹ค ์™”๋„ค? ๊ทธ๋ ค!

์ฝ”๋“œ๊ฐ€ ๋™๊ธฐ(Synchronous) ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ง๊ด€์ ์œผ๋กœ ๋ณ€ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒŒ ๋ณด์žฅ๋˜๋‹ˆ ์˜ต์…”๋„ ์ฒด์ด๋‹(?.)์„ ๋‚จ๋ฐœํ•  ํ•„์š”๋„ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ์›น์†Œ์ผ“ ์—ฐ๊ฒฐ์ด๋‚˜ ์œˆ๋„์šฐ ๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ ๊ฐ™์€ ๊ฑด ์—ฌ์ „ํžˆ useEffect๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ "ํŽ˜์ด์ง€ ๋กœ๋”ฉํ•˜์ž๋งˆ์ž API ์ฐ”๋Ÿฌ์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ" ์šฉ๋„๋กœ๋Š” ์ด์ œ ์€ํ‡ดํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.

Next.js App Router๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ ๋ฉ˜ํƒˆ ๋ถ•๊ดด๋ฅผ ๊ฒช๊ณ  ๊ณ„์‹ ๊ฐ€์š”?
๊ด€์ ์„ ๋ฐ”๊ฟ”๋ณด์„ธ์š”. "ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„ ํ‰๋‚ด๋ฅผ ๋‚ด๋˜ ์ง"์„ ๋‚ด๋ ค๋†“๊ณ , "์ง„์งœ ์„œ๋ฒ„"์—๊ฒŒ ์ผ์„ ๋งก๊ธฐ๋Š” ๊ณผ์ •์ด๋ผ๊ณ ์š”.

๋‹ค์Œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์Šต๊ด€์ ์œผ๋กœ useEffect๋ฅผ ์น˜๊ธฐ ์ „์— ํ•œ๋ฒˆ ๋ฉˆ์นซํ•ด๋ณด์„ธ์š”.
"์ด๊ฑฐ... ๊ทธ๋ƒฅ ์„œ๋ฒ„์—์„œ await ํ•œ ์ค„์ด๋ฉด ๋๋‚˜๋Š” ๊ฑฐ ์•„๋‹ˆ์•ผ?"


Next.js App Router ๋„์ž… ํŒ: ํ•œ ๋ฒˆ์— ๋ชจ๋“  ๊ฑธ ๋ฐ”๊พธ๋ ค ํ•˜์ง€ ๋งˆ์„ธ์š”. ๋ฒ„ํŠผ, ์ž…๋ ฅ์ฐฝ ๊ฐ™์€ ์ž‘์€ 'Leaf Component'๋ถ€ํ„ฐ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค๊ณ , ํŽ˜์ด์ง€ ์ „์ฒด ๊ตฌ์กฐ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์œ ์ง€ํ•˜๋Š” '๋ฐ”๊นฅ์—์„œ ์•ˆ์œผ๋กœ' ์ „๋žต์ด ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.

ReactNext.jsServer ComponentsPerformanceWeb Development