IT/nextjs 기초 내 공부

쉬운 NextJS 06 : 'use client' 지시문과 페이지 렌더링 심층 분석

higold 2024. 11. 12. 15:43
반응형

Next.js의 'use client' 지시문과 페이지 렌더링 심층 분석

목차

  1. 소개
  2. 페이지 컴포넌트에서의 'use client'
  3. 렌더링 프로세스 비교
  4. 빌드 결과물 분석
  5. 실제 구현 예제
  6. 결론

소개

Next.js에서 페이지 컴포넌트 최상단에 'use client' 지시문을 사용하면 어떤 일이 발생하는지 심층적으로 알아보겠습니다. 특히 빌드 결과물과 렌더링 프로세스의 차이점을 자세히 살펴보겠습니다.

페이지 컴포넌트에서의 'use client'

페이지 컴포넌트에서 'use client' 사용 여부에 따라 렌더링 방식이 크게 달라집니다.

'use client' 미사용 시

// app/example/page.tsx
export default function Page() {
  return (
    <div>
      <h1>Server Component Page</h1>
      <p>This is rendered on the server</p>
    </div>
  );
}

'use client' 사용 시

'use client';

// app/example/page.tsx
export default function Page() {
  return (
    <div>
      <h1>Client Component Page</h1>
      <p>This is rendered on the client</p>
    </div>
  );
}

렌더링 프로세스 비교

전체 페이지 로드 (Full Page Load) 시나리오

  1. 서버 컴포넌트 (use client 미사용)
  2. // 서버에서 생성되는 초기 HTML { "__next": { "initialTree": { "children": { "example": { "children": { "page": { "data": "서버에서 렌더링된 콘텐츠" } } } } } } }
  3. 클라이언트 컴포넌트 (use client 사용)
  4. // 클라이언트 페이지 루트 정보 포함 { "__next": { "clientPageRoot": "example/page" } }

빌드 결과물 분석

빌드 후 생성되는 파일 구조를 살펴보겠습니다:

.next/
├── server/
│   └── app/
│       └── example/
│           └── page.html  # 서버/클라이언트 렌더링 결과
└── static/
    └── chunks/
        └── app/
            └── example/
                └── page-[hash].js  # 클라이언트 사이드 JavaScript

주요 차이점

  1. 초기화 프로세스:
    • 서버 컴포넌트: SSG/SSR 초기화 데이터 포함
    • 클라이언트 컴포넌트: 클라이언트 페이지 루트 정보만 포함
  2. HTML 구조:
  3. <!-- 서버 컴포넌트 --> <div id="__next"> <div>서버에서 렌더링된 콘텐츠</div> <script> // 초기 상태 데이터 포함 window.__NEXT_DATA__ = { props: { pageProps: { /* 초기 데이터 */ } } } </script> </div> <!-- 클라이언트 컴포넌트 --> <div id="__next"> <div>클라이언트 측 렌더링을 위한 플레이스홀더</div> <script> // 클라이언트 페이지 루트 정보만 포함 window.__NEXT_DATA__ = { clientPageRoot: "example/page" } </script> </div>

실제 구현 예제

다음은 두 가지 방식의 차이를 보여주는 실제 예제입니다:

// 서버 컴포넌트 버전
// app/server-example/page.tsx
import { headers } from 'next/headers';

export default function ServerPage() {
  const headersList = headers();

  return (
    <div>
      <h1>Server-side rendered</h1>
      <pre>{JSON.stringify(headersList, null, 2)}</pre>
    </div>
  );
}

// 클라이언트 컴포넌트 버전
// app/client-example/page.tsx
'use client';

import { useState, useEffect } from 'react';

export default function ClientPage() {
  const [windowWidth, setWindowWidth] = useState(0);

  useEffect(() => {
    setWindowWidth(window.innerWidth);

    const handleResize = () => setWindowWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return (
    <div>
      <h1>Client-side rendered</h1>
      <p>Window width: {windowWidth}px</p>
    </div>
  );
}

결론

페이지 컴포넌트에서 'use client' 사용은 단순한 지시문 이상의 의미를 가집니다. 이는 전체 렌더링 전략과 초기화 프로세스에 영향을 미치며, 특히 SSG/SSR과 관련된 최적화에 중요한 영향을 줍니다.

참고 문헌

  1. Next.js 공식 문서 - Server Components
  2. Next.js 공식 문서 - Client Components
  3. React Server Components RFC
  4. Next.js GitHub Repository
  5. Vercel 엔지니어링 블로그
반응형