개발 노트

Next.js app router 기본 본문

React

Next.js app router 기본

한츄 2024. 1. 4. 19:05

Server Component

 

위 그림에서 Navbar나 Sidebar, Main컴포넌트는 interactive한 요소가 없으므로 Server Component가 적합합니다.

내부에 Search나 button같은 경우에는 상호작용 요소가 있으므로 Client Component가 적합합니다.

왜 Server Component를 써야하는가

Server Component 사용 시 초기 로드 속도가 향상되고 클라이언트의 js번들 사이즈는 감소합니다. 기존의 client 측의 런타임은 캐시할 수 있고 크기를 예측 가능하며 어플리케이션이 커져도 증가하지 않습니다. 

 

App router에서는 기본적으로 모든 컴포넌트가 Server Component가 Default로 사용됩니다. Client Component로 사용하고 싶다면 상단에 'use Client'를 사용하면 됩니다.

 

use client 를 선언함과 동시에 해당 파일안에서 의존하고 있는 다른 모듈과 child components는 client bundle에 포함됩니다. 기본적으로 server component가 default이기 때문에 module 그래프의 모든 컴포넌트는 use client 선언문이 없다면 Server component에 해당합니다.

 

Server Component VS Client Component

  • Server component
    • 데이터 fetching
    • 백엔드 자원에(직접적으로) 접근
    • 민감한 정보를 서버에서 유지(JWT, API Key 등등)
    • large dependencies를 서버에서 유지/클라이언트 JS 번들 사이즈 감소
  • Client component
    • interactivity, event listener(onClick 등)가 필요할때
    • state 및 라이프사이클이 필요할 때
    • browser-only API 사용
    • custom hook depend on state or browser-only API 사용

 

사용패턴

1. Client 컴포넌트는 리프노드에 두기

Client Component 는 가능한 한 리프노드에 두는 것이 바람직 합니다. 이는 Client Component가 되고 나면 하위 항목들은 Client Component가 되어버리기 때문에 Client Component를 사용을 최소화하는 방향으로 사용하면 좋습니다.

 

2. Client와 Server Component 함께쓰기

Server와 Client Component는 동일한 컴포넌트 트리상에서 결합될 수 있다. 리액트는 다음과 같이 동작합니다.

  • Server에서 클라이언트에 결과를 전달하기 전에 모든 Server Component를 렌더링합니다.
    • Client Component 안에 중첩되어 있는 Server Component 합니다.
    • Client Component를만나면 스킵합니다.
  • Client에서 React는 Client Component와 Server Components 렌더링된 결과를 렌더링 한다. 즉, Server와 Client 상의 작업을 합합니다.
    • Client Component 안에 중첩되어 있는 Server 컴포넌트는 렌더링된 내용이 Client Component 내부에 위치하게 됩니다.

3. Server Component를 Client Component 안에 중첩시키기

Server Component는  Client Component 내부에서 import 할 수 없습니다.

따라서 다음과같이 사용할 수 없습니다.

'use client';
 
// This pattern will **not** work!
// You cannot import a Server Component into a Client Component.
import ExampleServerComponent from './example-server-component';
 
export default function ExampleClientComponent({
  children,
}: {
  children: React.ReactNode;
}) {
  const [count, setCount] = useState(0);
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
 
      <ExampleServerComponent />
    </>
  );
}

따라서 권장되는 패턴은 Server Component를 Client Component의 props로 전달하는 방법입니다. 이렇게 하면 Server Component는 서버에서 렌더링 되고 Client Component가 Client에서 렌더링 되었을 때 props로 전달된 Server Component가 Server Component의 렌더링된 결과로 표시됩니다.

보통은 children props를 사용하라고 한다. 물론 다른 props를 써도 문제는 없습니다.

 

4. Server 에서 Client Component로 props전달하기

Server에서 Client로 Component로 전달하는 props은 serilization을 해야 합니다. Date, 함수, 등의 값으로는 직접적으로 전달할 수 없습니다. 따라서 JSON.stringify로 직렬화 해야합니다.

 

5. Server-only코드의 경우에는 Client Component 바깥에서 유지하기

자바스크립트 모듈은 Server랑 Client Component 모두에서 공유될 수 있는데 Server에서 실행하기만 의도했던 코드를 Client에서 실행할 수 도 있다. 이는 보안적으로 민감한 정보가 있을 때 문제가 될 수 있습니다.

예를 들어 API_KEY를 환경변수로 사용한다고 했을 때 NEXT_PUBLIC prefix가 없는 환경변수를 사용하는 코드가 있다면 Server에서만 접근할 수 있는 private한 변수로 동작할 수 있습니다.  Client에서 이 코드를 사용하게 되면 이런 민감한 정보가 노출될 수 있고 Client에서 제대로 동작하지도 않을 것입니다.

그래서 이런 상황을 방지하기 위해 server-only 라는 패키지를 사용할 수 있습니다. 이건 검사용 패키지로 이 패키지를 import한 코드를 Client 측에서 사용하면 빌드타임 오류가 발생합니다. 

import 'server-only';
 
export async function getData() {
  const res = await fetch('https://external-service.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  });
 
  return res.json();
}

6. Data Fetch

data를 Client Component에서 fetch할 수 있지만 특별한 이유가 없다면 더 나은 성능과 사용자 경험을 위해 Server에서 하는걸 권장한다고 합니다.

7. Third-party

Server Component가 새롭게 등장한 것으로 기존에 사용하고 있던 third-party 패키지들 중에 Client에서만 사용할 수 있는 feature를 사용한다면 use client 문을 상단에 추가해야 합니다. 하지만 지금 많은 npm 패키지에 적용안되어 있어서 선언이 안되어 있고 Client Component 내에서 사용하면 정상적으로 Client Component로 동작하겠지만 Server Component 내에서는 정상적으로 동작하지 않을 수 가 있습니다.

그래서 이런 경우에는 third-party Component를 Client Component 로 만들어서 사용하면 됩니다.

'use client';
 
import { AcmeCarousel } from 'acme-carousel';
 
export default AcmeCarousel;

 

참조: https://velog.io/@asdf99245/Next.js-app-router-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EC%A0%95%EB%A6%AC