번역 자료 / / 2025. 5. 24. 10:55

[nextjs 번역] 서버와 클라이언트 컴포넌트 사용하기 (Server and Client Components)

원문: Getting Started: Server and Client Components | Next.js


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

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유