Next.js App Router에서 컴포넌트는 기본적으로 모두 서버 컴포넌트로 동작합니다. 클라이언트 컴포넌트를 사용하려면 컴포넌트 상단에 use client
라는 특별한 예약어를 선언해야 합니다. use client
를 선언하면 해당 컴포넌트부터 아래 트리까지 클라이언트 경계(Client Boundary)가 형성되어, 그 아래 모든 컴포넌트는 클라이언트 컴포넌트로 동작합니다.
클라이언트 컴포넌트에서 import되는 모듈과 컴포넌트는 모두 클라이언트에서 동작합니다. 서버 컴포넌트 아래에는 클라이언트 컴포넌트가 포함될 수 있으며, 클라이언트 컴포넌트 아래에도 클라이언트 컴포넌트가 포함될 수 있습니다.
그러나 클라이언트 컴포넌트 아래로 서버 컴포넌트가 들어갈 수는 없습니다.
다음은 서버 컴포넌트와 클라이언트 컴포넌트의 예시입니다
'use client';
export const ClientComponent = () => {
return (
<div>
<h1>Client Component</h1>
</div>
);
};
import React from 'react';
export const ServerComponent = async () => {
const data = await getData();
return <div>{JSON.stringify(data)}</div>;
};
async function getData() {
const res = await fetch('<https://jsonplaceholder.typicode.com/todos/1>');
const json = await res.json();
if (!res.ok) {
throw new Error('Failed to fetch data');
}
return json;
}
export default ServerComponent;
동작하지 않는 패턴
"use client";
import ServerComponent from './ServerComponent';
export const ClientComponent = () => {
return (
<div>
<h1>Client Component</h1>
<ServerComponent />
</div>
);
};
const App = () => {
return (
<ClientComponent />
)
}
그렇다면 서버 컴포넌트를 클라이언트 컴포넌트 아래에서 사용하려면 어떻게 해야 할까요?
현재 유일한 방법은 클라이언트 컴포넌트에게 서버 컴포넌트를 props로 넘기는 것입니다.
동작하는 패턴
'use client';
export const ClientComponent = ({ children }: { children: React.ReactNode }) => {
return (
<div>
<h1>Client Component</h1>
{children}
</div>
);
};
const App = () => {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}
위에서 use client
를 선언하면 해당 컴포넌트로부터 아래 트리에 Client Boundary가 생성된다고 했습니다. Client Boundary에서 중요한 것은 컴포넌트의 부모-자식 관계가 아니라 어디서 import되었느냐입니다. 동작하는 패턴의 예제에서 ServerComponent는 서버 컴포넌트로써 동작하는 App에서 import되었습니다.
ClientComponent는 단순히 props로 받은 children이 어디서 렌더링될지 알 뿐, 어떤 컴포넌트가 올지는 모릅니다. 이러한 패턴을 통해 Layout에 use client로 선언된 Provider 등으로 감싸져 있어도 서버 컴포넌트를 사용할 수 있습니다.
참조
https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns
https://www.youtube.com/watch?si=6SwhGCoYwEgKkuwS&v=vjBQks_tjOA&feature=youtu.be