๐ โ๏ธ Next.js 1์ฅ: React ๋ฉํ ๋ชจ๋ธ์์ RSC๋ก์ ์งํ
๐ ๊ฐ์
React ๋ฉํ ๋ชจ๋ธ์์ RSC๋ก์ ์งํ โ Server Component๊ฐ ์, ์ด๋ป๊ฒ ๋ค๋ฅธ์ง ์ดํดํฉ๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ ์ Next.js์ด๊ณ , ์ RSC์ธ๊ฐ? ๐ข
- โ๏ธ Client Component vs Server Component ๐ข
- ๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ๊ฐ์ฅ ๋จ์ํ RSC ๋์ผ๋ก ํ์ธํ๊ธฐ
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 15๋ถ(์ ์ฒด) / ํต์ฌ ํํธ๋ง: 7๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
๊ธฐ์กด ๋ฆฌ์กํธ์ ํ๊ณ โ RSC ๋์
๋ฐฐ๊ฒฝ โ ์๋ฒ ๊ธฐ๋ฐ ๋ ๋๋ง ์๋ฆฌ โ ๋ ์ปดํฌ๋ํธ ๊ฐ ๊ฒฝ๊ณ ๊ตฌ๋ถ ( ์์๋ค ์ปค๋ฎค๋ํฐ ์์ )
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ๊ธฐ์กด React ์ฑ๊ณผ Next.js App Router ์ฑ์ ๊ทผ๋ณธ์ ์ธ ์ฐจ์ด๋ฅผ ์๋์ด์ ์ธ์ด๋ก ์ค๋ช ํ ์ ์๋ค
- RSC ( React Server Component ) ๊ฐ ์ ํ์ํ์ง ๋ช ํํ ์๊ณ , ์ํฉ์ ๋ง๊ฒ ์ ์ฉํ ์ ์๋ค.
- ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค ์ฌ์ด์ฆ๋ฅผ ๊ทน๋จ์ ์ผ๋ก ์ค์ด๋ ์ค๊ณ ๋ฐฉ์์ ์ค๋ฌด์ ๋์ ํ ์ ์๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ ( ์ ์ ): "๋ฆฌ๋ ๋! ๋ฐฉ๊ธ ์คํฐ๋ ์ฒซ ํ๋ฉด ์์ฑํ๋๋ฐ, ํ๋ฉด์ด ๋ฐ ๋๋ง๋ค 1์ด ์ ๋ ํ์๊ฒ ๋น์๋ค๊ฐ ์คํผ๋๊ฐ ๋์์. ์๋ ๋ฆฌ์กํธ๋ ๋ค ์ด๋ฐ ๊ฑด๊ฐ์?"
- ๐ฆ ์ํธ ( ๋ฆฌ๋ ): "์์ฒ ๋, ๊ทธ๊ฑด ์ ํ์ ์ธ ํด๋ผ์ด์ธํธ ํ์นญ ( Client Fetching ) ์ ํ์์ด์์. ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ๋ธ๋ผ์ฐ์ ์ ๋ค ๋ด๋ ค๊ฐ ๋๊น์ง ์ ์ ๋ ๊ตฌ๊ฒฝ๋ง ํด์ผ ํ๊ฑฐ๋ ์."
- ๐ ์์ ( PM ): "์์ฒ ์จ, ์ ์ ๋ค์ 1์ด๋ ๋ชป ๊ธฐ๋ค๋ ค์. ์ดํ๋ฅ ์ด ๋ฒ์จ ๊ฑฑ์ ๋๋๋ฐ์?"
- ๐จ ์์ ( UX ): "๋ง์์, ์์ฒ ์จ. ํ๋ฉด์ด ๋์ปน๊ฑฐ๋ฆฌ๋ ๋๋( Layout Shift )๋ ๋๋ฌด ์ฌํด์. ์ข ๋ ์ฐ์ํ๊ฒ ๋ณด์ฌ์ค ์ ์๋์?"
- ๐ฆ ์ํธ ( ๋ฆฌ๋ ): "์, ๋ค๋ค ์ง์ ํ์๊ณ ! Next.js ์ RSC ๋ฅผ ์ฐ๋ฉด ๋ธ๋ผ์ฐ์ ์ ๊ฐ๊ธฐ๋ ์ ์ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฝ๊ฝ ์ฑ์ด HTML ์ ๋ณด๋ด์ค ์ ์์ด์. ์์ฒ ๋, ์ด์ ๊ตฌ์๋์ ๋ก์ ํจํด์ ๋ฒ๋ฆด ๋๊ฐ ๋์ต๋๋ค."
๐ค ์ ์์์ผ ํ๋๊ฐ
์์(PM/BE) ์ ์ํธ(ํ๋ก ํธ ๋ฆฌ๋), ๊ทธ๋ฆฌ๊ณ ๊ฐ ์ ์ฌํ ์ฃผ๋์ด ์์ฒ ์ด๋ "๊ฐ๋ฐ์ ์คํฐ๋ ๋งค์นญ ์ปค๋ฎคํฐ๋" ๋ฅผ ๋ง๋ค๊ณ ์์ด.
์ด๋ ๋ ์์ฒ ์ด๊ฐ "์คํฐ๋ ์ฒซ ํ๋ฉด" ์ React ๋ก ์ด์ฌํ ๊ตฌํํ์ด. ๊ทธ๋ฐ๋ฐ ํ๋ฉด์ด ๋ฐ ๋๋ง๋ค ํ์ ํ๋ฉด์ด 1์ด ๋จ๊ณ ๋์ ์คํผ๋๊ฐ ๋๋๋ ๋ฐ์ดํฐ๊ฐ ๋์ค๋ค? ์์๋ "์ด๊ธฐ ๋ก๋ฉ์ด ๋๋ฌด ๋๋ ค์ ์ ์ ๋ค์ด ๋ค ์ดํํ๊ฒ ์ด์!" ๋ผ๊ณ ๋ถ๋ง์ ํํ์ง. ์์ฒ ์ด๋ useEffect ์์์ API ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ ์์๊ฑฐ๋ ( ์ ํ์ ์ธ ํด๋ผ์ด์ธํธ ํ์นญ ).
React ๋ก ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ์ปดํฌ๋ํธ ์์ useEffect ๋ฅผ ๊น๊ณ API ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ ๋น์ฐํ๊ฒ ๋๊ปด์ ธ. ํ์ง๋ง JS ๋ฒ๋ค ์ฌ์ด์ฆ๋ ๊ธฐํ๊ธ์์ ์ผ๋ก ๋์ด๋๊ณ , ์ฌ์ฉ์์ ์ค๋งํธํฐ์ด ๊ทธ ๊ฑฐ๋ํ JS ๋ฅผ ๋ค์ด๋ฐ๊ณ ํ์ฑํด์ ์คํํ ๋๊น์ง ํ๋ฉด์ ํ์๊ฒ ๋น์ด์์ด.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์ ๋ Next.js ์ ํต์ฌ ๋ฌด๊ธฐ, RSC ( React Server Component ) ์ ๋ฉํ ๋ชจ๋ธ๋ก ์ ํํ์ง ์์ผ๋ฉด, Next.js ์์์๋ ๋ฆฌ์กํธ ์์ ์ ๋ก์ ํจํด๋ง ๋ฐ๋ณตํ๊ฒ ๋ ๊ฑฐ์ผ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
์ฐ๋ฆฌ๊ฐ ์น์ฌ์ดํธ๋ฅผ ๋ณด๋ ๊ณผ์ ์ '์์ ๋ฐฐ๋ฌ'์ ๋น์ ํ๋ค๋ฉด, ์ง๊ธ๊น์ง์ ๋ฆฌ์กํธ๋ ์ด๋ค ๋ฐฉ์์ด์์๊น?
๐ฆ ์ด์ผ์ ๊ฐ๊ตฌ ์กฐ๋ฆฝ ๋น์
๊ธฐ๋ณธ ๋ฆฌ์กํธ(CSR)๋ ๊ฐ๊ตฌ ๋๋ฉด๊ณผ ์กฐ๋ฆฝ ๋๊ตฌ(JS ๋ฌถ์), ๊ทธ๋ฆฌ๊ณ ๊ฐ๊ณต๋์ง ์์ ๋๋ฌด ํ์ ๋ฅผ ์ฐ๋ฆฌ ์ง(๋ธ๋ผ์ฐ์ )์ ๋ค ๋ณด๋ด์ฃผ๋ ๊ฑฐ์ผ. ์ฐ๋ฆฌ๊ฐ ์ง์์ ์ง์ ๋ ํ๋ฆฌ๋ฉฐ ์กฐ๋ฆฝ(๋ ๋๋ง)ํด์ผ ๋น๋ก์ ๊ฐ๊ตฌ๊ฐ ๋ณด์ด๊ธฐ ์์ํ์ง.
๋ฐ๋ฉด, Next.js ์ ์๋ฒ ์ปดํฌ๋ํธ ( RSC ) ๋ ๊ณต์ฅ ( ์๋ฒ ) ์์ ์๋ฒฝํ๊ฒ ์์ฑ๋ ๊ฐ๊ตฌ ๋ฅผ ํต์งธ๋ก ๋ณด๋ด์ฃผ๋ ๋ฐฉ์์ด์ผ. ์ฐ๋ฆฌ๋ ๊ทธ๋ฅ ์ ์๋ฆฌ์ ๋๊ธฐ๋ง ํ๋ฉด ๋ผ!
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
"๊ธฐ์กด ๋ฆฌ์กํธ๋ ๋ ๊ณ ๋ธ๋ก์ด๋ ์ค๋ช ์๋ฅผ ์ง์ผ๋ก ๋ณด๋ด์ค์ ์ฐ๋ฆฌ๊ฐ ์ง์ ๋ง์ถฐ์ผ ํ์ด. ํ์ง๋ง ์๋ฒ ์ปดํฌ๋ํธ๋ ์ผ์ด์ด ๋ฏธ๋ฆฌ ๋ค ๋ง์ถฐ๋์ ๋ฉ์ง ์ฑ์ ํต์งธ๋ก ์ ๋ฌผํด์ฃผ๋ ๊ฑฐ์ผ. ์ฐ๋ฆฐ ๊ทธ๋ฅ ๋ฐ์์ ๋๊ธฐ๋ง ํ๋ฉด ๋ผ!"
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
CSR ์ ์ฌ๋ฃ ๋ฐฐ๋ฌ, RSC ๋ ์์ฑํ ๋ฐฐ๋ฌ์ด๋ค.
๐งฉ ์ Next.js์ด๊ณ , ์ RSC์ธ๊ฐ? ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋จ์ํ "๋น ๋ฅด๋ค" ๊ฐ ์๋๋ผ, ์ค๊ณ ๊ด์ ์์ RSC ๊ฐ ๊ฐ์ ธ๋ค์ค ํจ๋ฌ๋ค์ ๋ณํ๋ฅผ ์ค๋ช ํ ์ ์๋ค.
- ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ์ ์๋ฆฌ๋ฅผ ์์ ํ ํต์ ํ ์ ์๋ค.
์ด๊ฒ ๋ญ๊ฐ?
RSC(React Server Component) ๋ ์ค๋ก์ง ์๋ฒ ํ๊ฒฝ์์๋ง ๋ ๋๋ง๋๊ณ , ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ ) ๋ก๋ ์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๊ฐ ์ ์ก๋์ง ์๋ ์ปดํฌ๋ํธ์ผ.
๐ ์ฉ์ด:
RSCโ React Server Component . ๋ฆฌ์กํธ์ ํต์ฌ ๋ ๋๋ง ์์ง์ด ์๋ฒ๋ก ์ฎ๊ฒจ๊ฐ ํ์ ์ ์ธ ๊ธฐ์ .
์ ์ด๊ฒ ๋ฐ๋ก ์กด์ฌํ๋๊ฐ?
โ ์ด๊ฒ ์์๋ค๋ฉด? ( ๊ธฐ์กด React ์ ํ๊ณ )
์์ฒ ์ด๊ฐ ์คํฐ๋ ๋ชจ์ง๊ธ ๋ ์ง๋ฅผ ์ด์๊ฒ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด ๋ฌด๊ฑฐ์ด ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ( moment.js ๋ฑ ) ๋ฅผ ์ผ๋ค๊ณ ์น์. ๊ธฐ์กด ๋ฆฌ์กํธ์์๋ ์ฌ์ฉ์๊ฐ ๊ทธ ๋ฌด๊ฑฐ์ด ๋ชจ๋ ์๋ฐฑ KB ๋ฅผ ์ต์ง๋ก ๋๊ฒจ๋ฐ์์ผ ํ์ด. ํ๋ฉด ํ๋ ๊ทธ๋ฆฌ๋๋ฐ ๋ฐ์ดํฐ๋ณด๋ค ๋๊ตฌ๊ฐ ๋ ๋ฌด๊ฑฐ์ด ๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ํฐ ์ํฉ์ด์์ง.
โ
RSC ๊ฐ ์๊ธด ๋ค์๋?
๊ฐ์ฅ ๊ทน์ ์ธ ์ฐจ์ด์ ์ ๋ฐ์ดํฐ ํ์นญ๊ณผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฐ์ฐ ์ ์ฒด๊ฐ ๋ธ๋ผ์ฐ์ ์ ๋๋ฌํ๊ธฐ๋ ์ ์ ์๋ฒ์์ ์ข
๊ฒฐ ๋๋ค๋ ๊ฑฐ์ผ. ๊ฒฐ๊ณผ๋ฌผ์ธ HTML ๋ง ์ ๋ฌ๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ๊ธฐ๊ธฐ์ ๋ถ๋ด์ด ์ ๋ก(0) ์ ์๋ ดํด.
// app/studies/page.tsx
// ๐ฆ ์ํธ: "์์ฒ ๋, ์ด ์ฝ๋๋ ๋ธ๋ผ์ฐ์ ์์ ๋จ 1๋ฐ์ดํธ์ JS๋ ์ฐ์ง ์์์."
import db from '@/lib/db' // ๐ ์์: "์๋ฒ DB ์ปค๋ฅํฐ๋ฅผ ์ง์ ๋ถ๋ฅผ ์ ์์ผ๋ ํธํ๋ค์."
import { format } from 'date-fns' // ๐ฆ ์ํธ: "์๋ฌด๋ฆฌ ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์จ๋ ํด๋ผ์ด์ธํธ๋ ๋ชฐ๋ผ์."
export default async function StudiesPage() {
// ๐ฆ ์ํธ: "์ปดํฌ๋ํธ ์์ฒด๊ฐ async๋ผ๋, ์ ๋ง ์ฐ์ํ์ง ์๋์?"
const studies = await db.study.findMany()
return (
<main>
<h1>์คํฐ๋ ๋ชจ์ง๊ธ ๋ชฉ๋ก</h1>
<ul>
{studies.map(study => (
<li key={study.id}>
{study.title} - {format(study.createdAt, 'yyyy-MM-dd')}
</li>
))}
</ul>
</main>
)
}๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
RSC ๋ ์๋ฒ์์ ๋๋ ๋น๋ฐ ์์์ด์ผ. ๊ฒฐ๊ณผ๋ฌผ๋ง ๋ธ๋ผ์ฐ์ ์ ๋์ ธ์ฃผ๊ณ ํ์ ๋ ์์ด ์ฌ๋ผ์ ธ.
์ฌ๊ธฐ์ ์ฐ๋ฉด ์ ๋๋ ๊ฒ
useState,useEffect๋ถ๊ฐ (์๋ฒ์๋ ๋ธ๋ผ์ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ํ๊ฐ ์์)- ๋ธ๋ผ์ฐ์ ์ด๋ฒคํธ(
onClick) ํ ๋น ๋ถ๊ฐ window๋document๊ฐ์ฒด ์ ๊ทผ ๋ถ๊ฐ
๐ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ
์ด ์ ์ฝ ์ฌํญ๋ค์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ๋ค์์ ๋ฐฐ์ธ Client Component ์ผ.
โ๏ธ Client Component vs Server Component ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋ ์ปดํฌ๋ํธ์ ์ฐจ์ด๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ๊ณ , ์ธ์ ๋ฌด์์ ์ธ์ง ๊ฒฐ์ ํ ์ ์๋ค.
- Leaf Pattern ์ ํ์ฉํด ๋ฒ๋ค ์ฌ์ด์ฆ๋ฅผ ์ต์ํํ๋ ์ค๊ณ๋ฅผ ํ ์ ์๋ค.
์์ฒ ์ด๊ฐ RSC ์ ๋ง์ ์๋๋ ๋ชจ๋ ๊ฒ์ ์๋ฒ์์ ํ๋ ค๊ณ ํด. ํ์ง๋ง "์ข์์" ๋ฒํผ์ ๋ง๋ค๋ ค๊ณ <button onClick={...}> ์ ๋ฐ๋ ์๊ฐ ํฐ๋ฏธ๋์ ์๋ป๊ฑด ์๋ฌ๊ฐ ๋ฌ์ง. ์ํธ( FE ๋ฆฌ๋ ) ๊ฐ ๋ค๊ฐ์์ ์์ผ๋ฉฐ ๋งํ์ด.
"์์ฒ ์, ์ํธ์์ฉ์ ๊ฒฐ๊ตญ ํด๋ผ์ด์ธํธ์์ ์ผ์ด๋์ผ ํด. use client ๋ฅผ ์ธ ์๊ฐ์ด์ผ."
์ด๋ป๊ฒ ๋ค๋ฅธ๊ฐ?
| ๋น๊ต | Server Component(RSC) | Client Component(use client) |
|---|---|---|
| ๊ธฐ๋ณธ ์ํ | App Router ์ ๊ธฐ๋ณธ๊ฐ ( Default ) | ์ต์๋จ์ "use client" ๋ช
์ ํ์ |
| JS ๋ฒ๋ค ํฌ๊ธฐ | +0 KB ( ๊ฒฐ๊ณผ๋ฌผ HTML ๋ง ๋ด๋ ค๊ฐ ) | ์ฝ๋๊ฐ ๊ทธ๋๋ก ๋ฒ๋ค์ ํฌํจ๋จ |
| ๋ฐ์ดํฐ ํ์นญ | async/await ์ง์ ํธ์ถ | useEffect, ์ปค์คํ
ํ
๋ฑ |
| ์ ์ฝ ์กฐ๊ฑด | ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ถ๊ฐ | ๋ฏผ๊ฐํ ์ ๋ณด(API ํค) ๋ ธ์ถ ์ํ |
์ค๋ฌด์์๋ ์ด๋ป๊ฒ ๋ถ๋ฆฌํด์ผ ํ๋๊ฐ? ( Leaf Pattern ์ฒดํ )
"์ํธ์์ฉ์ด ํ์ํ ๋๋ญ์(Leaf) ๋์๋ฝ ๋ถ๋ถ๋ง ๋ฐ์ด๋ด๋ผ." ์ด๊ฒ์ด ์๋์ด๋ค์ ๋ฉํ ๋ชจ๋ธ์ด์ ๊ฐ์ฅ ์ฐ์ํ ํจํด์ด์ผ.
โ ์์งํ ์ฝ๋ ( Naive Approach ) - ์์ฒ ์ด์ ํญํ ๋๋ฆฌ๊ธฐ
์์ฒ ์ด๋ ์๋ฌ๊ฐ ๋์ ๊ท์ฐฎ์์ ํ์ด์ง ์ ์ฒด ์ต์๋จ์ "use client" ๋ฅผ ๋ฐ์๋ฒ๋ ธ์ด.
// app/studies/page.tsx
'use client' // ๐ฃ ์์ฒ : "์, ์๋ฌ๋๋๊น ์ผ๋จ 'use client' ๋ฐ๊ณ ๋ณผ๊ฒ์!"
// ๐ฆ ์ํธ: "์์ฒ ๋, ์ด๋ฌ๋ฉด ํ์์ ๋ชจ๋ ์ ์ UI๊น์ง JS ๋ฒ๋ค๋ก ๋ง๋ ค๋ค์ด๊ฐ๋๋ค!"
import StudyDesc from './StudyDesc'
import LikeButton from './LikeButton'
export default function StudiesPage() {
return (
<div>
<StudyDesc /> {/* ๐ฆ ์ํธ: "ํ
์คํธ ๋ฉ์ด๋ฆฌ์ธ๋ฐ ํด๋ผ์ด์ธํธ ๋ฒ๋ค์ ์กํ๊ฐ๋๋ค (์ฌ์)" */}
<LikeButton />
</div>
)
}โ
์ฐ์ํ ์ฝ๋ ( Pro Approach ) - ์ํธ์ ๋ฆฌํฉํ ๋ง
์ํธ(FE ๋ฆฌ๋) ๋ LikeButton.tsx ๋ง ๋ฐ๋ก ํ์ผ๋ก ๋นผ์, ๊ทธ ์์๋ง "use client" ๋ฅผ ์ ์ธํ๊ฒ ํ์ด.
// app/studies/LikeButton.tsx
'use client' // ๐ฆ ์ํธ: "๋ฑ ์ํธ์์ฉ์ด ํ์ํ ์ด ๋ถ๋ถ๋ง ํด๋ผ์ด์ธํธ๋ก ๋ณด๋
๋๋ค."
import { useState } from 'react'
export default function LikeButton() {
const [likes, setLikes] = useState(0)
return <button onClick={() => setLikes(l => l + 1)}>๐ {likes}</button>
}// app/studies/page.tsx
// ๐ฆ ์ํธ: "๋ถ๋ชจ๋ ๊ณ์ ์์ํ Server Component๋ก ๋จ๊ฒจ์ ์ต์ ํํ์ธ์."
import StudyDesc from './StudyDesc'
import LikeButton from './LikeButton' // Leaf ๋ถ๋ถ๋ง Client Component
export default function StudiesPage() {
return (
<div>
<StudyDesc />
<LikeButton />
</div>
)
}๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
๋๋ค๋ณด์ ๊ธฐ๋ฅ์ ํผํผํ RSC ๋ก ์ธ์๋๊ณ , ๋ถ์ด ์ผ์ ธ์ผ ํ๋ ์ ๊ตฌ(๋๋ญ์) ๋ถ๋ถ๋ง ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ๋ฌ์์ค๋ค.
๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ๊ฐ์ฅ ๋จ์ํ RSC ๋์ผ๋ก ํ์ธํ๊ธฐ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์๋ฒ ์ปดํฌ๋ํธ ๋ก๊ทธ๊ฐ ์ฐํ๋ ์์น๋ฅผ ํตํด ์คํ ํ๊ฒฝ์ ์ฐจ์ด๋ฅผ ์ค๊ฐ์ผ๋ก ๋๋ ์ ์๋ค.
์ค์ ๋ก ์๋ฒ ์ปดํฌ๋ํธ๊ฐ ๋ธ๋ผ์ฐ์ ์ฝ์์ ๋จ๊ธฐ์ง ์๊ณ , ์๋ฒ์์๋ง ๋๋ ๊ฑธ ํ์ธํด๋ณด์.
// app/sandbox/page.tsx
export default function SandboxPage() {
// ๐ฆ ์ํธ: "์ด ๋ก๊ทธ๋ ๋ธ๋ผ์ฐ์ F12 ์ฝ์์ ์ฐํ๊น์?"
console.log("๐ฅ ์ด ๋ก๊ทธ๋ ๊ณผ์ฐ ์ด๋์ ์ฐํ๊น์?");
return (
<div>
<h1>์์๋ค ์ปค๋ฎค๋ํฐ ์๋๋ฐ์ค</h1>
</div>
)
}๐ ์ฝ๋ ํ ์ค์ฉ ๋ฏ์ด๋ณด๊ธฐ:
| ๋ถ๋ถ | ์๋ฏธ |
|---|---|
console.log | ์ผ๋ฐ์ ์ธ ์ถ๋ ฅ ํจ์์ง๋ง, RSC ์์๋ ํฐ๋ฏธ๋(์๋ฒ) ์๋ง ์ฐํ. |
SandboxPage | "use client" ๊ฐ ์์ผ๋ฏ๋ก ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ์์๋ง ๋ ๋๋ง๋ผ. |
๐ ๋ฐ๋ผํด๋ณด๊ธฐ ๊ฒฐ๊ณผ ๋ถ์:
๋ธ๋ผ์ฐ์ ์๋ ์ ๋ ์ ์ฐํ๊ณ ํฐ๋ฏธ๋ ์ฐฝ(์๋ฒ๋ฅผ ๋๋ฆฌ๋ Node.js ํ๊ฒฝ) ์๋ง ๋ก๊ทธ๊ฐ ๋จ์. ์ด ๋จ์ํ ์ฌ์ค์ด ์ SSR / RSC ํ๊ฒฝ์์ window is not defined ๊ฐ ๋ฐ์ํ๋์ง๋ฅผ ์๋ฒฝํ ๋๋ณํด.
โ ์ค์ต ํ ์ฒดํฌ๋ฆฌ์คํธ
- ์๋ฒ ์ปดํฌ๋ํธ ๋ก๊ทธ๊ฐ ๋ธ๋ผ์ฐ์ ์ ์ฐํ์ง ์๋ ์ด์ ๋ฅผ ์ค๋ช ํ ์ ์๋ค
- ๋ ๋๋ง ํธ๋ฆฌ์ ํ๋จ(Leaf) ์ ์๋ ์ปดํฌ๋ํธ๋ง
use client๋ก ๋ฐ์ด๋ด๋ ํจํด์ ์ด์ ๋ฅผ ์๋ค.
๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
์๋ฌ ๋ฉ์์ง๊ฐ ๋จ๋ฉด Ctrl+F๋ก ๊ฒ์ํด๋ด.
โ Event handlers cannot be passed to Client Component props...
์์ธ: ์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ฆญ(onClick)์ ๋ฌ์์ ์๋ฌ๋จ.
ํด๊ฒฐ์ฑ
: ํด๋ฆญ์ด ํ์ํ ๊ทธ ์ปดํฌ๋ํธ ํ์ผ ๋จ์๋ง ์ชผ๊ฐ์ด ์ต์๋จ์ 'use client'๋ฅผ ์ ์ธํ๋ค.
โ window is not defined
์์ธ: ์๋ฒ ์ธก์์ ํ์ด์ง๋ฅผ ๋ ๋๋งํ๊ณ ์๋๋ฐ ๋ธ๋ผ์ฐ์ ๊ฐ์ฒด(window, localStorage)๋ฅผ ์ฐธ์กฐํจ.
ํด๊ฒฐ์ฑ
: ์ปดํฌ๋ํธ๋ฅผ ํด๋ผ์ด์ธํธ๋ก ๋ง๋ค๊ณ useEffect ๋ด๋ถ์์ ์ ๊ทผํ๊ฑฐ๋ ์กฐ๊ฑด์ ๊ฑธ์ด ์ฌ์ฉํ๋ค.
๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
์ค๋ ๋ฐฐ์ด ํต์ฌ์ ํ๋์ ์ ๋ฆฌํด๋ณผ๊น? ์ค๋ฌด์์ ๊ธธ์ ์์์ ๋ ์ด๊ฒ๋ง ๋ด๋ ๋ผ.
๐ ํต์ฌ ๋ชจ๋ธ ์ฐจ์ด
| HTML ๋ ๋๋ง | ๋น div ๋ฐ๊ณ ํฐ์์ ์์ค๋ก ์ฑ์ | ์๋ฒ์์ HTML ๊ฝ๊ฝ ์ฑ์์ ๋ด๋ ค์ค |
| JS ๋ฒ๋ค ํฌ๊ธฐ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐํ ์๋ก ๋น๋ํด์ง | ์๋ฒ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ 0KB ๋ฌ์ฑ |
| ๋ฐ์ดํฐ ํ์นญ | ํ๋ฉด ๋๋ฆฌ๊ณ ๋์์ผ useEffect ํธ์ถ | HTML ๊ทธ๋ฆด ๋ ์ด๋ฏธ ๋ฐ์ดํฐ๋ฅผ ๋ค๊ณ ์ด |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ | โ ์ข์ ์ |
|---|---|---|
| ์๋ฌ ํด๊ฒฐ | ๊ท์ฐฎ๋ค๊ณ ์ต์๋จ์ "use client" ๋ฐ๊ธฐ | ๋ง๋จ ์ปดํฌ๋ํธ(Leaf) ๋ง ํด๋ผ์ด์ธํธ๋ก ๋ถ๋ฆฌ |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ | ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ๋ฌด๊ฑฐ์ด ์ ํธ(lodash ๋ฑ) ์ฌ์ฉ | ์๋ฒ ์ปดํฌ๋ํธ์์ ์ฐ์ฐ ํ ๋ฐ์ดํฐ๋ง ๋๊ธฐ๊ธฐ |
| ๊ฐ์ฒด ์ ๊ทผ | ์๋ฒ ์ปดํฌ๋ํธ์์ window, localStorage ํธ์ถ | useEffect ์์์ ์ ๊ทผํ๊ฑฐ๋ ํด๋ผ์ด์ธํธ๋ก ์์ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ์์ฒ ์ด๊ฐ ํ์๊ฐ์
ํผ์ ๊ฐ๋ฐ ์ค์ด์ผ. ์ฝ๊ด ๋์ ๋ด์ฉ์ด ๋ด๊ธด ์์ฃผ ๊ธด ํ
์คํธ ์ปดํฌ๋ํธ <Terms> ์ ๋์ ์ฒดํฌ๋ฐ์ค ๋ฐ ์ ์ถ ๋ฒํผ์ ๊ฐ์ง <SignUpForm> ์ปดํฌ๋ํธ๊ฐ ์์ด. ์ฌ๊ธฐ์ ๋ฒ๋ค ์ฌ์ด์ฆ๋ฅผ ๊ฐ์ฅ ์ต์ ํํ๋๋ก ์ปดํฌ๋ํธ๋ฅผ ์ค๊ณํ๋ ค๋ฉด 'use client' ๋ ์ด๋์ ์ ์ธํด์ผ ํ ๊น?
โ
์ ๋ต: <SignUpForm> ์ปดํฌ๋ํธ์๋ง 'use client' ๋ฅผ ์ ์ธํ๊ณ , <Terms> ๋ Server Component ๋ก ์ ์งํ๋ค.
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
<Terms>๋ ๋จ์ํ ๋ณด์ฌ์ฃผ๋ ํ ์คํธ์ผ ๋ฟ์ด๋ฏ๋ก ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ํ์ ์์ด. ์ด๊ฑธ ์๋ฒ ์ปดํฌ๋ํธ๋ก ๋๋ฉด ์ฌ์ฉ์์๊ฒ ์ ์ก๋๋ ์์ค ๋ฒ๋ค์์ ์์ ํ ๋น ์ง๊ฒ ๋ผ. - ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, ํ์ด์ง ์ ์ฒด์
use client๋ฅผ ๋ถ์ด๋ฉด ์ ๊ธด ์ฝ๊ด ํ ์คํธ๊น์ง ์ ์ ์ ๋ฐ์ดํฐ ์๊ธ์ ๊ฐ์๋จน์ผ๋ฉฐ ๋ค์ด๋ก๋๋ผ์!" - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "์ธํฐ๋์ ์ด ์๋ ํ ์คํธ๋ ์๋ฒ๋ก!"
Q2. ์ํธ(FE ๋ฆฌ๋) ๊ฐ ์์ฒ ์ด์ PR ์ ๋ณด๋ฉฐ ์ฝ๋ฉํธ๋ฅผ ๋จ๊ฒผ๋ค. "์์ฒ ์, ์ด๊ฑฐ use client ์ ์ธํ ํ์ผ ์์ชฝ์์ ๋ฌด๊ฑฐ์ด lodash ๊ฐ์ ์ ํธ์ ์ฐ๊ณ ์๋ค? ์ด๋ฌ๋ฉด ์๋ฏธ๊ฐ ์์ด." ์ํธ์ ์ง์ ์ Next.js ๋ฒ๋ค ๊ด์ ์์ ์ด๋ค ์๋ฏธ์ธ๊ฐ?
โ
์ ๋ต: 'use client' ํ์ผ์์ import ํ ๋๊ตฌ๋ ๋ชจ๋ ํด๋ผ์ด์ธํธ ๋ฒ๋ค์ ํฌํจ๋์ด ์ ์ ๊ฐ ๋ค์ด๋ก๋ํด์ผ ํ๋ค๋ ์๋ฏธ์ด๋ค.
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
use client๊ฐ ์ ์ธ๋๋ ์๊ฐ, ๊ทธ ํ์๋ก ๋ธ๋ ค์ค๋ ๋ชจ๋ ์์กด์ฑ(import) ์ ๋ธ๋ผ์ฐ์ ์ฉ ๋ฒ๋ค๋ก ๋ฌถ์ฌ. ์๋ฌด๋ฆฌ ์๋ฒ ์ฐ์ฐ์ด ๋นจ๋ผ๋ ์ ์ ๊ฐ ๋ค์ด๋ก๋ํ๋ ๋ฐ ์๊ฐ์ด ๋ค ๊ฐ๋ฉด ์์ฉ์์ง. - ๋ฒ ์คํธ ํ๋ํฐ์ค: ๋ฌด๊ฑฐ์ด ์ฐ์ฐ์ ์๋ฒ ์ปดํฌ๋ํธ์์ ๋๋ด๊ณ , ํด๋ผ์ด์ธํธ์๋ ์ค์ง ์ง๋ ฌํ๋(Serialized) ์์ ๋ฐ์ดํฐ ๊ฐ๋ง Props ๋ก ๋๊ฒจ์ค.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋๊ตฌ๋ ์๋ฒ์์ ์ฐ๊ณ , ์ ์ ์๊ฒ ๊ฒฐ๊ณผ๋ฌผ๋ง ์ฃผ์."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋ ๋๋์ด RSC ๋ผ๋ ๋ ์์ ์ค์ฒด๋ฅผ ์ข ์ ๊ฒ ๊ฐ์. ์ฒ์์ ์๋ฒ์์ ๋ค ํด๋ฒ๋ฆฐ๋ค๊ธธ๋ "๋ด ์ผ์๋ฆฌ๊ฐ ์ํ๋ฐ๋ ๊ฑฐ ์๋?" ํ๊ณ ์ซ์๋๋ฐ(๋๋ด), ์๊ณ ๋ณด๋ ๋ด ์๋ฐ์คํฌ๋ฆฝํธ ๋ค์ด์ดํธ๋ฅผ ๋์์ฃผ๋ ๊ณ ๋ง์ด ์น๊ตฌ์์ด.
๐ก ์ค๋์ ๊ตํ: "์ํธ์์ฉ ์์ผ๋ฉด ๋ฌด์กฐ๊ฑด ์๋ฒ๋ก ๋ฐ์ด๋ผ. ์ ์ ์ ๋ฐฐํฐ๋ฆฌ๋ ์์คํ๋๊น!"
ํญ์ ๋ฌด๋๋ํด ๋ณด์ด๋ ์ํธ ๋ฆฌ๋ ๋ ์ด์ง๋ง, ๋ด๊ฐ ์ง ๋นํจ์จ์ ์ธ ์ฝ๋๋ฅผ ๋ณด๊ณ "์ ์ ์ ๋ฐฐํฐ๋ฆฌ๋ ๋น์ฉ์ด๋ค"๋ผ๊ณ ๋ผ ๋๋ฆฌ๋ ์กฐ์ธ์ ํด์ฃผ์ค ๋ ์ ๋ง ์๋์ด์ ํฌ์ค๊ฐ ๋๊ปด์ ธ. ๋๋ ์ธ์ ๊ฐ๋ ์ํธ ๋์ฒ๋ผ ์๋ฆฌ๋ฅผ ๊ฟฐ๋ซ๋ ๊ฐ๋ฐ์๊ฐ ๋๊ณ ์ถ๋ค. ์ค๋์ ๋จธ๋ฆฌ๋ฅผ ๋๋ฌด ๋ง์ด ์ผ๋๋ ๋ฐฐ๊ฐ ๊ณ ํ๋ค. ์ง์ ๊ฐ๋ ๊ธธ์ ๊ฐ ๊ตฌ์ด ๋ถ์ด๋นต์ด๋ ์ฌ ๋จน์ด์ผ์ง! ๐ฃ