Next.js는 서버 컴포넌트(Server Component)와 클라이언트 컴포넌트(Client Component)를 모두 지원합니다. 이를 통해 서버에서 데이터를 안전하게 가져오고, 클라이언트에서 상호작용이 필요한 UI를 구현할 수 있습니다.
서버 컴포넌트와 클라이언트 컴포넌트란?
- 서버 컴포넌트: 기본적으로 모든 컴포넌트는 서버에서 렌더링됩니다. 서버 컴포넌트는 데이터베이스 쿼리, 비동기 데이터 패칭 등 서버 전용 작업에 적합합니다.
- 클라이언트 컴포넌트: 상호작용(이벤트 핸들링, 상태 관리 등)이 필요한 경우, 파일 상단에
'use client'
지시어를 추가하여 클라이언트 컴포넌트로 지정할 수 있습니다.
// app/ui/button.tsx
'use client'
export default function Button() {
// 클라이언트 전용 로직 (예: useState, useEffect 등)
}
서버와 클라이언트 컴포넌트의 구분
- 서버 컴포넌트는 기본값입니다. 별도의 지시어가 없으면 서버에서 렌더링됩니다.
- 클라이언트 컴포넌트는 파일 최상단에
'use client'
를 선언해야 하며, 해당 파일과 그 하위 import 컴포넌트 모두 클라이언트 번들에 포함됩니다.
JS 번들 크기 최적화
상호작용이 필요한 컴포넌트에만 'use client'
를 선언하여, 클라이언트 JS 번들 크기를 최소화할 수 있습니다. 예를 들어, 레이아웃 전체가 아닌 검색 바 등 일부만 클라이언트 컴포넌트로 분리하세요.
// app/ui/search.tsx
'use client'
export default function Search() {
// ...
}
// app/layout.tsx
import Search from './search'
import Logo from './logo'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<nav>
<Logo />
<Search />
</nav>
<main>{children}</main>
)
}
서버에서 클라이언트로 데이터 전달
서버 컴포넌트에서 클라이언트 컴포넌트로 props를 통해 데이터를 전달할 수 있습니다.
// app/[id]/page.tsx
import LikeButton from '@/app/ui/like-button'
import { getPost } from '@/lib/data'
export default async function Page({ params }: { params: { id: string } }) {
const post = await getPost(params.id)
return <LikeButton likes={post.likes} />
}
// app/ui/like-button.tsx
'use client'
export default function LikeButton({ likes }: { likes: number }) {
// ...
}
참고: 클라이언트 컴포넌트로 전달하는 props는 React가 직렬화할 수 있어야 합니다.
서버와 클라이언트 컴포넌트의 중첩 (Interleaving)
서버 컴포넌트를 클라이언트 컴포넌트의 children 등 prop으로 전달할 수 있습니다. 예를 들어, 서버에서 데이터를 패칭하는 <Cart>
를 클라이언트 상태로 토글하는 <Modal>
의 children으로 넘길 수 있습니다.
// app/ui/modal.tsx
'use client'
export default function Modal({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
// app/page.tsx
import Modal from './ui/modal'
import Cart from './ui/cart'
export default function Page() {
return (
<Modal>
<Cart />
</Modal>
)
}
Context Provider 사용하기
React Context는 전역 상태(예: 테마 등) 공유에 자주 사용됩니다. Context Provider는 클라이언트 컴포넌트로 만들어야 하며, 서버 컴포넌트에서 import하여 감싸면 하위 클라이언트 컴포넌트에서 사용할 수 있습니다.
// app/theme-provider.tsx
'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>
}
// app/layout.tsx
import ThemeProvider from './theme-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}
참고: Provider는 트리에서 가능한 한 깊게(최소 범위로) 감싸는 것이 Next.js의 최적화에 유리합니다.
서드파티 컴포넌트 사용하기
클라이언트 전용 기능을 사용하는 서드파티 컴포넌트는 별도의 클라이언트 컴포넌트로 감싸서 사용해야 합니다.
// app/gallery.tsx
'use client'
import { useState } from 'react'
import { Carousel } from 'acme-carousel'
export default function Gallery() {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(true)}>View pictures</button>
{isOpen && <Carousel />}
</div>
)
}
직접 서버 컴포넌트에서 사용할 경우 에러가 발생할 수 있으니, 아래처럼 별도 파일로 감싸서 import하세요.
// app/carousel.tsx
'use client'
import { Carousel } from 'acme-carousel'
export default Carousel
// app/page.tsx
import Carousel from './carousel'
export default function Page() {
return (
<div>
<p>View pictures</p>
<Carousel />
</div>
)
}
라이브러리 제작자 참고:
클라이언트 전용 기능이 필요한 엔트리 포인트에는 반드시 'use client' 지시어를 추가하세요.
환경 오염(환경 변수 노출) 방지
서버/클라이언트 컴포넌트 간 모듈 공유가 가능하므로, 서버 전용 코드를 클라이언트에서 실수로 import할 수 있습니다. 예를 들어, 아래와 같이 서버 전용 API_KEY를 사용하는 함수가 있다면:
// lib/data.ts
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
})
return res.json()
}
Next.js는 NEXT_PUBLIC_
접두사가 없는 환경 변수는 클라이언트 번들에 포함하지 않고 빈 문자열로 대체합니다. 그래도 실수로 클라이언트에서 import하지 않도록, server-only 패키지를 활용하세요.
npm install server-only
// lib/data.js
import 'server-only'
export async function getData() {
// ...
}
이제 해당 모듈을 클라이언트 컴포넌트에서 import하면 빌드 타임에 에러가 발생합니다.
참고: 클라이언트 전용 로직(예: window 객체 접근 등)은 client-only 패키지로 명시할 수 있습니다.
Next Steps
이 페이지에서 언급된 API에 대해 더 알아보세요.
출처: https://nextjs.org/docs/app/getting-started/server-and-client-components