Next.JS 중요 개념을 정리해보자! - ClientComponent 지정하기

woong·2024년 3월 20일
0
post-thumbnail

서버컴포넌트와 클라이언트컴포넌트의 개념을 알았다면 이제 Next.js에서의 클라이언트컴포넌트 지정 방법과 주의사항에 대해서 알아보자.

부족한 제가 잘못 정리한 내용이 있다면 알려주세요.


클라이언트컴포넌트를 지정해보자

Nex.js 13버전부터 기존 pages폴더가 app폴더로 변경되었다. 단순히 이름만 바뀐게 아니라 app폴더 안에 있는 컴포넌트는 따로 명시를 해주지 않는이상 서버컴포넌트라고 간주한다.

그러면 어떻게 클라이언트컴포넌트를 지정할 수 있을까? 바로 파일 최 상단에"use client" 라고 써주기만 하면 Next.js는 해당 컴포넌트를 클라이언트컴포넌트라고 인식하게 된다.

그렇다고 매번 클라이언트 컴포넌트에 최상단에 "use client"를 명시해줘야 할 필요는 없다. 클라이언트 컴포넌트의 return문에 들어가는 컴포넌트들은 "use client"를 쓰지 않아도 자동으로 클라이언트 컴포넌트로 인식한다.

다음의 두개 예시 코드를 살펴보자 위의 TopHeader컴포넌트는 서버컴포넌트이고 아래의 DarkModeButton컴포넌트는 클라이언트컴포넌트이다.

import DarkModeButton from "./darkModeButton";

const TopHeader = () => {
  return (
    <header className="h-14 z-10 sticky top-0 bg-slate-600">
      Header
      <DarkModeButton />
    </header>
  );
};

export default TopHeader;
"use client";

const DarkModeButton = () => {
  const setDarkMode = () => {
   //...다크모드 설정 로직
   //...
  };
  return (
    <div>
      <button onClick={setDarkMode} className="bg-white">
        다크모드
      </button>
    </div>
  );
};

export default DarkModeButton;

서버컴포넌트는 기본적으로 EventHandler를 사용할 수 없다. 따라서 onClick이벤트가 붙어있는 버튼을 포함한 컴포넌트인 DarkModeButton는 서버컴포넌트로 지정할 수 없다.

만약 DarkModeButton컴포넌트 최상단에 명시되어 있는 "use client"를 삭제하게 되면 Next.js는 클라이언트컴포넌트로 지정하라는 오류를 뿜어낸다.

그렇다면 조금더 들어가서 어떤 컴포넌트를 클라이언트컴포넌트로 지정하는게 좋을까?

일반적으로는 서버 컴포넌트로 만들 수 있다면 그렇게 만드는게 좋다고 한다. 기본적으로 서버 컴포넌트는 간단하고 이해하기 쉽다는 장점이 있다. 또한 이전 서버컴포넌트와 클라이언트컴포넌트의 개념에서 설명했듯이 클라이언트에서 실행되지 않기 때문에 JS번들에 코드가 포함되지 않는다는 성능상의 이점도 있다.

하지만 그렇다고 해서 단순히 클라이언트컴포넌트를 없애거나 최소한으로 줄이는 것을 목표로 잡으면 안된다. 클라이언트컴포는트는 원래 React의 표준이되는 컴포넌트였고 지금까지 모든 React앱의 모든 컴포넌트는 클라이언트컴포넌트였다.

사용자 인터렉션(상호작용)이 필요한 부분을 클라이언트 컴포넌트로 지정하고, 나머지는 서버컴포넌트로 놔두자.


위의 사진은 Next.js공식문서에 나와있는 클라이언트컴포넌트를 지정한 예시 이미지인다.

NavBar에서 Logo부분은 서버컴포넌트로 놔두고 Search와 같은 사용자 인터렉션이 필요한 부분은 클라이언트컴포넌트로 지정하고 있다.
SidBar역시 라우팅기능 외에 Web API나 Eventhandler등의 기능이 필요없기 때문에 서버컴포넌트로 놔두는게 좋을것이다.

하지만 Main안에 있는 Button은 onClick이벤트같은 Eventhandler등록이 필요하고 사용자 인터렉션이 가능해야하는 부분이기 때문에 클라이언트 컴포넌트로 지정했다.


클라이언트컴포넌트 안에 있는 서버컴포넌트?

리액트는 기본적으로 컴포넌트를 중첩할 수 있는 구조이다. 서버컴포넌트와 클라이언트컴포넌트가 나뉘어져 있다면 다음과 같은 상황을 생각해 볼 수 있다.

  1. 서버컴포넌트의 return문 안에 있는 클라이언트컴포넌트
  2. 클라이언트컴포넌트의 return문 안에 있는 서버컴포넌트
  3. 서버컴포넌트의 props.children으로 존재하는 클라이언트컴포넌트
  4. 클라이언트컴포넌트의 props.children으로 존재하는 서버컴포넌트

결론적으로 이중에서 2번째 경우(클라이언트컴포넌트의 return문 안에 있는 서버컴포넌트)를 제외하고는 전부 가능하다.

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

// Pages는 기본적으로 서버컴포넌트
export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  );
}
'use client'

import ServerComponent from "./ServerComponent";

// 클라이언트 컴포넌트의 return문 안에 있는 서버컴포넌트
export default function ClientComponent() {
  return (
    <div>
      <ServerComponent />
    </div>
  );
}

코드로 보자면 첫번째 코드와 같이 클라이언트 컴포넌트 안의 children으로 서버 컴포넌트를 넣을 수 있다.하지만 문제는 클라이언트 컴포넌트 내에서 서버 컴포넌트를 호출하는 경우다. 서버컴포넌트는 서버에서만 실행될 수 있으므로, 클라이언트 사이드에서 이를 렌더링하려고 하면 오류가 발생하게 된다.

왜냐하면 서버 컴포넌트가 클라이언트 사이드의 브라우저 환경에서 작동하지 않기 때문이다. 서버 컴포넌트는 서버에서 데이터를 미리 불러오거나, 서버의 파일 시스템에 접근하거나, 외부 API를 호출하는 등의 서버 전용 기능을 수행할 수 있다. 이런 기능들은 클라이언트 사이드에서는 작동하지 않으므로, 클라이언트 컴포넌트에서 서버 컴포넌트를 호출하면 문제가 발생하게 되는것이다.

여기서 드는 의문점이 있다. 클라이언트 컴포넌트의 return문으로 들어가는 컴포넌트들은 "use client"를 명시하지 않아도 자동으로 클라이언트 컴포넌트가 된다고 했다. 그러면 클라이언트 컴포넌트의 Children으로 들어간 서버 컴포넌트도 클라이언트 컴포넌트로 인식이 되는걸까?

결론적으로 그렇지 않다. children으로 들어간 서버컴포넌트는 그대로 서버컴포넌트로 인식된다. 클라이언트 컴포넌트의 return문 안에 있는 서버컴포넌트는 문제가 되지만, 클라이언트 컴포넌트의 children으로 들어간 서버컴포넌트는 그대로 서버컴포넌트로 유지됨을 기억하자.


참고

https://plasmic.app/blog/how-react-server-components-work

https://nextjs.org/docs/app/building-your-application/rendering

profile
안녕하세요! 👋

0개의 댓글