์ปดํฌ์งํธ ํจํด?
์ปดํฌ์งํธ ํจํด์ ๊ฐ์ฒด๋ค์ ๊ด๊ณ๋ฅผ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ๊ตฌ์ฑํ์ฌ ๋ถ๋ถ-์ ์ฒด ๊ณ์ธต์ ํํํ๋ ํจํด์ผ๋ก, ์ฌ์ฉ์๊ฐ ๋จ์ผ ๊ฐ์ฒด์ ๋ณตํฉ ๊ฐ์ฒด ๋ชจ๋ ๋์ผํ๊ฒ ๋ค๋ฃจ๋๋ก ํ๋ค
- ์๋๋ ๊ฐ์ฒด์งํฅ์์ ์ฌ์ฉ๋๋ ํจํด์ธ๋ฏํ๋ค
- ์ด๊ฑธ ์ด๋ป๊ฒ 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
'๊ฐ๋ฐ > NextJS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
RSC vs SSR (0) | 2024.05.04 |
---|---|
[๊ณต์๋ฌธ์ ๋ฒ์ญ] NextJS ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ (0) | 2024.03.21 |
[๊ณต์๋ฌธ์ ๋ฒ์ญ] NextJS ์๋ฒ ์ปดํฌ๋ํธ (1) | 2024.03.20 |