๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ๋ฐœ/NextJS

[๊ณต์‹๋ฌธ์„œ ๋ฒˆ์—ญ] Composition Pattern

by Jaeguk 2024. 3. 22.

์ปดํฌ์ง€ํŠธ ํŒจํ„ด?


์ปดํฌ์ง€ํŠธ ํŒจํ„ด์€ ๊ฐ์ฒด๋“ค์˜ ๊ด€๊ณ„๋ฅผ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ๋ถ€๋ถ„-์ „์ฒด ๊ณ„์ธต์„ ํ‘œํ˜„ํ•˜๋Š” ํŒจํ„ด์œผ๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ๋‹จ์ผ ๊ฐ์ฒด์™€ ๋ณตํ•ฉ ๊ฐ์ฒด ๋ชจ๋‘ ๋™์ผํ•˜๊ฒŒ ๋‹ค๋ฃจ๋„๋ก ํ•œ๋‹ค

  • ์›๋ž˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ์‚ฌ์šฉ๋˜๋˜ ํŒจํ„ด์ธ๋“ฏํ•˜๋‹ค
  • ์ด๊ฑธ ์–ด๋–ป๊ฒŒ NextJS์—์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ ์ง€, ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค

 

Server and Client Composition Patterns


์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์ปดํฌ์ง€ํŠธ ํŒจํ„ด

๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ๋•Œ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์–ด๋–ค ๋ถ€๋ถ„์ด ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ๋˜๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ Œ๋”๋˜์–ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ๊ณ ๋ คํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
์ด ํŽ˜์ด์ง€๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ถ”์ฒœ๋˜๋Š” ์ปดํฌ์ง€์…˜ ํŒจํ„ด์„ ๋‹ค๋ฃฌ๋‹ค.

 

When to use Server and Client Components?


์–ธ์ œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€?

๋‹ค์Œ์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์ •๋ฆฌ์ด๋‹ค.

 

Server Component Patterns


์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ํŒจํ„ด

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

์•„๋ž˜๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด๋“ค์ด๋‹ค.

 

Sharing data between components


์ปดํฌ๋„ŒํŠธ๊ฐ„์— ๋ฐ์ดํ„ฐ ๊ณต์œ ํ•˜๊ธฐ

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

React Context(์ด๊ฒƒ์€ ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค)๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ๋ฅผ Props๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ ๋Œ€์‹ ์— fetch ๋˜๋Š” ๋ฆฌ์•กํŠธ์˜ cache ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋•Œ, ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋ฐ˜๋ณต๋˜๋Š” ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
์ด๊ฒƒ์€ ๋ฆฌ์•กํŠธ๊ฐ€ fetch๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ์ €์žฅํ•˜๋„๋ก ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๊ทธ๋ฆฌ๊ณ  cacheํ•จ์ˆ˜๋Š” fetch๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ฆฌ์•กํŠธ์˜ memoization ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ•ด๋ผ.

 

Keeping Server-only Code out of the Client Environment


ํด๋ผ์ด์–ธํŠธ ํ™˜๊ฒฝ์—์„œ๋„ ์„œ๋ฒ„์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ

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

์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ์˜ data-fetching ํ•จ์ˆ˜๋ฅผ ๋ณด์ž:

export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  })

  return res.json()
}

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

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ API_KEY์•ž์— NEXT_PUBLIC์ด๋ผ๋Š” ์ ‘๋‘์‚ฌ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ๊ฒƒ์€ ์„œ๋ฒ„์—์„œ๋งŒ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
๋‹น์‹ ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋กœ ์ƒˆ์–ด๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด, NextJS๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

๊ทธ ๊ฒฐ๊ณผ, getData()๊ฐ€ ํด๋ผ์ด์–ธํŠธ์ธก์—์„œ import๋˜๊ณ  ์‹คํ–‰๋  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์›ํ•˜๋Š”๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.
๋ณ€์ˆ˜๋ฅผ public์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ ํ•จ์ˆ˜๊ฐ€ ํด๋ผ์–ธํŠธ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ํด๋ผ์ด์–ธํŠธ์ธก์— ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€๋Š” ์•Š์„ ๊ฒƒ์ด๋‹ค.

์ด๋ ‡๊ฒŒ ์˜๋„์น˜์•Š๊ฒŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” server-only ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด ํŒจํ‚ค์ง€๋Š” ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์ด ์šฐ์—ฐํžˆ ์ด๋Ÿฌํ•œ ๋ชจ๋“ˆ์„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ importํ•  ๊ฒฝ์šฐ์— ๋นŒ๋“œ ํƒ€์ž„์— ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฒƒ์ด๋‹ค.

server-onlyํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋จผ์ € ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค

npm install server-only

 

๊ทธ๋Ÿฐ ๋‹ค์Œ server-only ์ฝ”๋“œ๋กœ ์ง€์ •ํ•˜๊ณ  ์‹ถ์€ ๊ณณ์— ํŒจํ‚ค์ง€๋ฅผ importํ•˜๋ฉด ๋œ๋‹ค:

import 'server-only'

export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  })

  return res.json()
}

์ด์ œ getData()๋ฅผ importํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” "์ด ๋ชจ๋“ˆ์€ ์„œ๋ฒ„์—์„œ๋งŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค"๋ผ๋Š” ๋‚ด์šฉ์˜ ๋นŒ๋“œ ํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐ›์„ ๊ฒƒ์ด๋‹ค.


์œ ์‚ฌํ•œ ํŒจํ‚ค์ง€์ธ client-only๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“ˆ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, window๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ชจ๋“ˆ์ด ์žˆ๋‹ค. (window๊ฐ์ฒด๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋กœ ํด๋ผ์ด์–ธํŠธ์ธก ๊ฐ์ฒด์ด๋‹ค)

 

Using Third-party Packages and Providers


์„œ๋“œ ํŒŒํ‹ฐ ํŒจํ‚ค์ง€์™€ ํ”„๋กœ๋ฐ”์ด๋” ์‚ฌ์šฉํ•˜๊ธฐ

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ์•กํŠธ์˜ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๊ธฐ์กด ์ƒํƒœ๊ณ„์˜ ์„œ๋“œ ํŒŒํ‹ฐ ํŒจํ‚ค์ง€๋“ค๊ณผ ํ”„๋กœ๋ฐ”์ด๋”๋“ค์€ useState, useEffect, createContext์™€ ๊ฐ™์€ ํด๋ผ์ด์–ธํŠธ์ธก์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ ๋“ค์„ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ์ด์ œ ๋ง‰ "use client"๋ฅผ ๋ถ™์ด๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

์˜ค๋Š˜๋‚ , client-only๋ฅผ ์‚ฌ์šฉํ•˜๋Š” npmํŒจํ‚ค์ง€์˜ ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ๋“ค์€ "use client"๋ฅผ ์•„์ง ๋ถ™์ด์ง€ ์•Š์•˜๋‹ค.
๊ทธ๋Ÿฌํ•œ ์„œ๋“œ ํŒŒํ‹ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ "use client"๊ฐ€ ์„ ์–ธ๋œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์ž˜ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, <Carousel /> ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํฌํ•จ๋œ ๊ฐ€์ƒ์˜ acme-carouselํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ–ˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.
์ด ์ปดํฌ๋„ŒํŠธ๋Š” useState๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์•„์ง "use client"๋Š” ์„ ์–ธํ•˜์ง€ ์•Š์•˜๋‹ค.

๋งŒ์•ฝ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ <Carousel />๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์›ํ•˜๋Š”๋Œ€๋กœ ๋™์ž‘ํ•  ๊ฒƒ์ด๋‹ค:

'use client'

import { useState } from 'react'
import { Carousel } from 'acme-carousel'

export default function Gallery() {
  let [isOpen, setIsOpen] = useState(false)

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>View pictures</button>

      {/* Works, since Carousel is used within a Client Component */}
      {isOpen && <Carousel />}
    </div>
  )
}

๊ทธ๋Ÿฌ๋‚˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณง๋ฐ”๋กœ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค:

import { Carousel } from 'acme-carousel'

export default function Page() {
  return (
    <div>
      <p>View pictures</p>

      {/* Error: `useState` can not be used within Server Components */}
      <Carousel />
    </div>
  )
}

์ด๊ฒƒ์€ Next๊ฐ€ <Carousel />์ด ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ๋“ค์„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ, ๋ณธ์ธ๋งŒ์˜ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์„œ๋“œ ํŒŒํ‹ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์Œ€ ์ˆ˜ ์žˆ๋‹ค:

'use client'

import { Carousel } from 'acme-carousel'

export default Carousel

์ด์ œ, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ <Carousel />์„ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค:

import Carousel from './carousel'

export default function Page() {
  return (
    <div>
      <p>View pictures</p>

      {/*  Works, since Carousel is a Client Component */}
      <Carousel />
    </div>
  )
}

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š” ์„œ๋“œ ํŒŒํ‹ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๊ฒŒ ๊ฐ์‹ธ์ง€ ์•Š์•„๋„ ๋  ๊ฒƒ์ด๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๋ฌธ์ œ๋Š” ํ”„๋กœ๋ฐ”์ด๋”์ด๋‹ค. ๊ทธ๋“ค์€ React ์ƒํƒœ์™€ ์ปจํ…์ŠคํŠธ์— ์˜์กดํ•˜๊ณ , ์ผ๋ฐ˜์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Root์—์„œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

Using Context Providers


Context Providers ์‚ฌ์šฉํ•˜๊ธฐ

์ปจํ…์ŠคํŠธ ํ”„๋กœ๋ฐ”์ด๋”๋“ค์€ ํ˜„์žฌ ํ…Œ๋งˆ์™€ ๊ฐ™์€ ์ „์—ญ์ ์ธ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Root ๊ทผ์ฒ˜์—์„œ ๋ Œ๋”๋œ๋‹ค.
React Context๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง€์›๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Root์—์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค:

import { createContext } from 'react'

//  createContext is not supported in Server Components
export const ThemeContext = createContext({})

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
      </body>
    </html>
  )
}

์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋ Œ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค:

'use client'

import { createContext } from 'react'

export const ThemeContext = createContext({})

export default function ThemeProvider({
  children,
}: {
  children: React.ReactNode
}) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}

๊ทธ๊ฒƒ์€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งˆํฌ๊ฐ€ ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์ด์ œ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ๋ Œ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค:

import ThemeProvider from './theme-provider'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

Root์—์„œ ๋ Œ๋”๋œ ํ”„๋กœ๋ฐ”์ด๋” ๋•๋ถ„์—, ์•ฑ ๋‚ด์˜ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ด Context๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

์•Œ๋ฉด ์ข‹์€ ๊ฒƒ

ํ”„๋กœ๋ฐ”์ด๋”๋Š” ํŠธ๋ฆฌ์—์„œ ์ตœ๋Œ€ํ•œ ๊นŠ์€ ๊ณณ์—์„œ ๋ Œ๋”๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
์™œ ThemeProvider๊ฐ€ ์ „์ฒด <html>์„ ๊ฐ์‹ธ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ {children}๋งŒ ๊ฐ์ŒŒ์„๊นŒ.
์ด๊ฒƒ์€ Next๊ฐ€ ์„œ๋ฒ„ ์ปค๋ชจ๋„ŒํŠธ์˜ ์ •์ ์ธ ๋ถ€๋ถ„์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์„ ์‰ฝ๊ฒŒ ํ•ด์ค€๋‹ค.

 

Advice for Library Authors


๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ œ์ž‘์ž์—๊ฒŒ ์ „ํ•˜๋Š” ์กฐ์–ธ

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

"use cleint"๋ฅผ ํŠธ๋ฆฌ์˜ ๋” ๊นŠ์€ ๊ณณ์—์„œ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ํŒจํ‚ค์ง€๋ฅผ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ๊ฐ€์ ธ์˜จ ๋ชจ๋“ˆ๋“ค์ด ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋“ˆ ๊ทธ๋ž˜ํ”„์˜ ์ผ๋ถ€๊ฐ€ ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Client Components


ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ

 

Moving Client Components Down the Tree


ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŠธ๋ฆฌ ์•„๋ž˜์ชฝ์œผ๋กœ ๋ณด๋‚ด๊ธฐ

ํด๋ผ์ด์–ธํŠธ์ธก์˜ JavaScript ๋ฒˆ๋“ค์˜ ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์˜ ํ•˜๋‹จ์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ •์ ์ธ ์š”์†Œ๋“ค(๋กœ๊ณ , ๋งํฌ ๋“ฑ)์„ ํฌํ•จํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ๊ณผ state๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒ€์ƒ‰๋ฐ”๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.

์ „์ฒด ๋ ˆ์ด์•„์›ƒ์„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค๊ธฐ๋ณด๋‹ค๋Š”, ์ƒํ˜ธ์ž‘์šฉ์„ ํ•˜๋Š” ๋กœ์ง์„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ(์˜ˆ: <SearchBar />)์— ๋„ฃ์–ด์„œ ๋ ˆ์ด์•„์›ƒ์„ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๊ฒƒ์€ ๋ ˆ์ด์•„์›ƒ์—์„œ ์‚ฌ์šฉ๋˜๋Š” JavaScript๋ฅผ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
(๋ ˆ์ด์•„์›ƒ์€ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—)

// SearchBar is a Client Component
import SearchBar from './searchbar'
// Logo is a Server Component
import Logo from './logo'

// Layout is a Server Component by default
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Logo />
        <SearchBar />
      </nav>
      <main>{children}</main>
    </>
  )
}

 

Passing props from Server to Client Components (Serialization)


์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ Props ์ „๋‹ฌํ•˜๊ธฐ (Serialization)

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•œ ๋‹ค์Œ์— ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ๋„˜๊ฒจ์ฃผ๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๋‹ค.
์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋˜๋Š” Props๋“ค์€ ๋ฆฌ์•กํŠธ์— ์˜ํ•ด serializable ํ•ด์•ผ ํ•œ๋‹ค.

๋งŒ์•ฝ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜์กดํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ serializableํ•˜์ง€ ์•Š๋‹ค๋ฉด, ์„œ๋“œ ํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํด๋ผ์ด์–ธํŠธ์ธก์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ฑฐ๋‚˜ ๋˜๋Š” Route Handler๋ฅผ ํ†ตํ•ด์„œ ์„œ๋ฒ„์—์„œ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Interleaving Server and Client Components


์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋ฒˆ๊ฐˆ์•„ ์‚ฌ์šฉํ•˜๊ธฐ

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์™€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฒˆ๊ฐˆ์•„ ์‚ฌ์šฉํ•  ๋•Œ, UI๋ฅผ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋กœ ์‹œ๊ฐํ™”ํ•˜๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋  ๊ฒƒ์ด๋‹ค.
์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ธ Root ๋ ˆ์ด์•„์›ƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์˜ ํŠน์ • ์„œ๋ธŒํŠธ๋ฆฌ๋Š” "use client"๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ Œ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฌํ•œ ํด๋ผ์ด์–ธํŠธ ์„œ๋ธŒํŠธ๋ฆฌ ์•ˆ์—์„œ๋„ ์—ฌ์ „ํžˆ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ค‘์ฒฉํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„ ์•ก์…˜๋“ค์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๋ช‡๊ฐ€์ง€ ๋ช…์‹ฌํ•ด์•ผ ํ•  ๊ฒƒ๋“ค์ด ์žˆ๋‹ค.

  • ์š”์ฒญ-์‘๋‹ต ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ์—, ์ฝ”๋“œ๋Š” ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์˜ค๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ์™€ ์ž์›์— ์ ‘๊ทผํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค - ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ„์— ์™”๋‹ค๊ฐ”๋‹ค ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋ฉด, ๋‚ด๋ถ€์— ์ค‘์ฒฉ๋œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•ด์„œ ๋ชจ๋“  ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋จผ์ € ๋ Œ๋”๋œ๋‹ค. ๋ Œ๋”๋œ ๊ฒฐ๊ณผ(RSC Payload)๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์œ„์น˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋„ ํฌํ•จํ•œ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ํด๋ผ๋ฆฌ์–ธํŠธ ์ธก์—์„œ ๋ฆฌ์•กํŠธ๋Š” RSC Payload๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋ฆฌ๋กœ ํ•ฉ์นœ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋‹ค์Œ์œผ๋กœ ๋ Œ๋”๋˜๊ธฐ ๋•Œ๋ฌธ์—, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋“ˆ ๋‚ด์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†๋‹ค (์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋˜ ๋‹ค์‹œ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด๊ฒŒ ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—). ๋Œ€์‹  ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜์˜ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒจํ„ด๊ณผ ์ง€์›ํ•˜๋Š” ํŒจํ„ด ๋ถ€๋ถ„์„ ๋ณด๋ฉด ๋œ๋‹ค.

 

Unsupported Pattern: Importing Server Components into Client Components


์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒจํ„ด: ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

๋‹ค์Œ์˜ ํŒจํ„ด์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒจํ„ด์ด๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†๋‹ค:

'use client'

// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'

export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>

      <ServerComponent />
    </>
  )
}

 

Supported Pattern: Passing Server Components to Client Components as Props


์ง€์›ํ•˜๋Š” ํŒจํ„ด: ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ์ „๋‹ฌํ•˜๊ธฐ

๋‹ค์Œ์˜ ํŒจํ„ด์€ ์ง€์›ํ•œ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์€ ๋ฆฌ์•กํŠธ์˜ children prop์„ ์‚ฌ์šฉํ•ด์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— "์Šฌ๋กฏ"์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์‹œ์—์„œ, <ClientComponent />๋Š” children prop์„ ํ—ˆ์šฉํ•œ๋‹ค:

'use client'

import { useState } from 'react'

export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      {children}
    </>
  )
}

<ClientComponent />๋Š” children์ด ๊ฒฐ๊ณผ์ ์œผ๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ฑ„์›Œ์งˆ ๊ฒƒ์ด๋ž€ ๊ฑธ ๋ชจ๋ฅธ๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— <ClientComponent />๋Š” ์–ด๋””์— children์„ ๋ Œ๋”ํ•  ๊ฒƒ์ธ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์ฑ…์ž„๋งŒ์„ ๊ฐ–๋Š”๋‹ค.

์ƒ์œ„ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ <ClientComponent />์™€ <ServerComponent /> ๋ชจ๋‘๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ, <ServerComponent />๋ฅผ <ClientComponent />์˜ ์ž์‹์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค:

// This pattern works:
// You can pass a Server Component as a child or prop of a
// Client Component.
import ClientComponent from './client-component'
import ServerComponent from './server-component'

// Pages in Next.js are Server Components by default
export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  )
}

์ด๋Ÿฌํ•œ ์ ‘๊ทผ์œผ๋กœ, <ClientComponent />์™€ <ServerCompmonent />๋Š” ๋ถ„๋ฆฌ๋˜์–ด ๋…๋ฆฝ์ ์œผ๋กœ ๋ Œ๋”๋  ์ˆ˜ ์žˆ๋‹ค.
์ด ๊ฒฝ์šฐ์—์„œ <ServerCompmonent />๋Š” <ClientComponent />๊ฐ€ ํด๋ผ์ด์–ธํŠธ์ธก์—์„œ ๋ Œ๋”๋˜๊ธฐ ์ „์— ์„œ๋ฒ„์—์„œ ๋ฏธ๋ฆฌ ๋ Œ๋”๋  ์ˆ˜ ์žˆ๋‹ค.

์•Œ๋ฉด ์ข‹์€ ๊ฒƒ
- "lifting content up" ํŒจํ„ด์€ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋  ๋•Œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋„ ๋ฆฌ๋ Œ๋”๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋œ๋‹ค.
- children prop๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. JSX๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค prop์„ ์‚ฌ์šฉํ•˜๋“  ๋ฌด๊ด€ํ•˜๋‹ค.

 

Reference


 

์ปดํฌ์ง€ํŠธ ํŒจํ„ด - ์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „

์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „. ์ปดํฌ์ง€ํŠธ ํŒจํ„ด(Composite pattern)์ด๋ž€ ๊ฐ์ฒด๋“ค์˜ ๊ด€๊ณ„๋ฅผ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ๋ถ€๋ถ„-์ „์ฒด ๊ณ„์ธต์„ ํ‘œํ˜„ํ•˜๋Š” ํŒจํ„ด์œผ๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ๋‹จ์ผ ๊ฐ์ฒด์™€ ๋ณตํ•ฉ ๊ฐ์ฒด ๋ชจ๋‘ ๋™์ผํ•˜

ko.wikipedia.org

 

Rendering: Composition Patterns | Next.js

Recommended patterns for using Server and Client Components.

nextjs.org

 

'use server' directive – React

The library for web and native user interfaces

react.dev

 

728x90