Server and Client Components[Next.js]

SnowCat·2023년 4월 19일
0

Next.js

목록 보기
9/16
post-thumbnail

Server Components

  • 기본적으로 app 디렉터리에 있는 컴포넌트들은 React Server Components(RSC)로 취급됨
  • 서버 구성 요소를 사용하게 되면 클라이언트가 받는 종속성 파일들아 줄어 성능 향상에 도움이 됨
  • 클라이언트에서는 요소를 캐시 가능하고, 크기를 예측할 수 있으며, 각 페이지의 파일 크기는 애플리케이션과 독립적이게 됨

Client Components

  • 클라이언트에서 상호작용이 필요하다면 Client Components를 사용할수도 있음
  • 이를 사용하면 Next.js에서 사전에 렌더링되고, 클라이언트가 이를 받아 사용하게 됨
  • 이전의 pages/ 디렉터리의 작동 방법과 유사함
  • 파일에 use client를 맨 위에 작성해줌으로써 Client Components 구현가능
'use client';

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

  • use client를 사용하는 경우 자식 구성요소, import한 모듈은 전부 클라이언트 번들의 일부로 간주됨

Server vs. Client Components

  • 데이터를 가져오거나, 백엔드 요소에 직접 접근하거나, 민감한 데이터를 저장하는경우, 클라이언트의 js코드, 의존성을 줄이고 싶다면 Server component를 사용해야 함
    데이터 fetch는 클라이언트에서 가능하지만 권장하지 않음
  • onClick등의 event와 상호작용, useState등의 생명주기 메서드, 브라우저 api 등을 사용하는 경우에는 client component를 사용해야 함
  • 만약 Client Components를 사용하는 경우 해당 요소는 성능 향상을 위해 컴포넌트 트리의 leaf로 보내는것이 권장됨

Client Components에서 Server Components 가져오기

  • 서버 구성 요소는 서버 전용 코드를 가질 수 있기 때문에 클라이언트에서 서버 구성 요소를 가져오는데 제한이 있음
  • 예를 들어 클라이언트 구성 요소에서 서버 구성 요소를 가져오는것은 지원되지 않음
'use client';

// 불가능
import ServerComponent from './ServerComponent';

export default function ClientComponent() {
  return (
    <>
      <ServerComponent />
    </>
  );
}
  • 클라이언트에서 컴포넌트를 가져오기 위해서는 서버 컴포넌트를 prop으로 사용해 클라이언트 컴포넌트에 전달해야 함
// 클라이언트 코드
'use client';

export default function ClientComponent({children}) {
  return (
    <>
      {children}
    </>
  );
}

// page.js
import ClientComponent from "./ClientComponent";
import ServerComponent from "./ServerComponent";

// Pages are Server Components by default
export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  );
}
  • 서버 컴포넌트에서 클라리언트 컴포넌트로 전달될 때 필요한 데이터는 직접적으로 클라이언트 컴포넌트에 전달되야 함

Server 전용 코드

export async function getData() {
  let res = await fetch("https://external-service.com/data", {
    headers: {
      authorization: process.env.API_KEY,
    },
  });

  return res.json();
}
  • next.js에서는 NEXT_PUBLIC가 없는 환경변수 값은 빈 문자열이 됨 => 클라이언트에서 getData 코드는 작동하지 않는 server-only 코드
  • 의도하지 않은 클라이언트 접근을 빌드타임에서 막기 위해서는 server-only 패키지 사용
// npm install server-only로 패키지 설치 필요
import "server-only";

export async function getData() {
  let resp = await fetch("https://external-service.com/data", {
    headers: {
      authorization: process.env.API_KEY,
    },
  });

  return resp.json();
}

Client 전용 코드

  • useState, useEffect, createContext등의 기능은 서버 컴포넌트에서 사용이 불가능 함
  • use client는 최근에 생긴 기능으로, 이를 사용하지 않은 패키지를 서버에서 사용시 오류가 발생할 수 있음
    일반적으로 패키지들을 클라이언트에서 쓰기 때문에 큰 문제는 없지만, root component에 래핑을 하는 컴포넌트들의 경우 조치를 취해주어야 함
  • 이 경우 use client를 선언한 새로운 컴포넌트 내에 래핑을 통해 문제를 해결할 수 있음
// 기존의 코드
import { ThemeProvider } from 'acme-theme';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {/* 🔴 Error: `createContext` can't be used in Server Components */}
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

// 문제 해결을 위해 래퍼 제작
'use client';

import { ThemeProvider } from 'acme-theme';
import { AuthProvider } from 'acme-auth';

export function Providers({ children }) {
  return (
    <ThemeProvider>
      <AuthProvider>
        {children}
      </AuthProvider>
    </ThemeProvider>
  );
}

// 이제 새로운 래퍼로 대체해 사용하면 됨
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

출처:
https://beta.nextjs.org/docs/rendering/server-and-client-components

profile
냐아아아아아아아아앙

0개의 댓글