Next.js 14버전을 공부하던중 서버컴포넌트와 클라이언트 컴포넌트를 같이 사용할 방법을 찾아보다 알게된 점을 블로그에 요약해보려한다.
일반적으로 Next.js에서는(13버전이상 앱디렉토리) default는 서버컴포넌트이며
상단에 "use client"를 넣을 경우 그 컴포넌트는 클라이언트 컴포넌트가 되고 react의 hook들과 이벤트, 브라우저객체 등을 사용할 수 있게 된다.
또한, 클라이언트 컴포넌트 하위의 컴포넌트들은 클라이언트가 되는데..
프로젝트를 진행하며 상위 컴포넌트의 스테이트등의 처리로 인해 굳이 클라이언트 컴포넌트가 되지 않아도 되는 자식 컴포넌트들이 존재하는 경우가 생기는 걸 보며 어떻게 하면 이걸 해결 할 수 있을까 공부를하다 children을 이용하면 되는 것을 알게 됬다.
예시코드들을 봐보자.
// TestContainer.tsx (컴포넌트들의 부모컴포넌트)
import TestClientComp from "@/components/rsctest/TestClientComp";
import TestServerCompInClientComp from "@/components/rsctest/TestServerCompInClientComp";
import axios from "axios";
const fetchData = async () => {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos/1"
);
return response.data;
};
const TestContainer = async () => {
const data = await fetchData();
return (
<div>
<h1>nextjs에서 서버컴포넌트와 클라이언트컴포넌트를 공부해보자!</h1>
<div>서버컴포넌트 컨테이너</div>
<TestClientComp parentData={data}>
<TestServerCompInClientComp data={data} />
</TestClientComp>
</div>
);
};
export default TestContainer;
컨테이너컴포넌트에선 서버에서 api통신을 한 후 받아온 데이터를 클라이언트 컴포넌트와 클라이언트 컴포넌트를 부모로 둔 서버컴포넌트에게 프롭스로 내려준형태이다.
// TestClientComp.tsx (클라이언트 컴포넌트)
"use client";
import axios from "axios";
import { useEffect, useState } from "react";
const TestClientComp = ({ parentData, children }) => {
const [count, setCount] = useState(0);
const [clientData, setClientData] = useState(null);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/todos/2")
.then((response) => {
setClientData(response.data);
console.log("클라이언트컴포넌트");
});
}, []);
// if (!clientData) {
// // 옵셔널체이닝을 사용하지 않을경우 리턴을 칠드런으로만했을때 서버에서 처음 렌더링시 자식컴포넌트를 제외한 칠드런컴포넌트만 html에받아옴
// return <>{children}</>;
// }
return (
<div>
<div>
Count: <span>{count}</span>
</div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<p>
클라이언트 컴포넌트 <span>{clientData?.title}</span>
</p>
<p>
클라이언트 컴포넌트 부모에서받아온 데이터
<span>{parentData.title}</span>
</p>
{children}
</div>
);
};
export default TestClientComp;
자식 클라이언트 컴포넌트이다. Children을 프롭스로받아 렌더링을 진행하고 클라이언트단에서 데이터 페칭을 하고있다.
// TestServerCompInClientComp (클라이언트 컴포넌트를 부모로 둔 서버컴포넌트)
import { useState } from "react";
const TestServerCompInClientComp = ({ data }) => {
console.log("클라이언트컴포넌트를 부모로둔 서버 컴포넌트", data);
// const [test, setTest] = useState(0); // 서버 컴포넌트이므로 주석을풀면 에러발생
return (
<div>
<p>서버컴포넌트에게 프롭스로 받은 손자 서버컴포넌트{data.title}</p>
</div>
);
};
export default TestServerCompInClientComp;
손자컴포넌트는 프롭스로 데이터를받아 뿌려주기만 하는형태이다.
트리구조는 아래 사진과 같은 구조이다
실제 렌더링을 할때 터미널에 손자컴포넌트와 컨테이너컴포넌트의 콘솔이 터미널에 찍히고 클라이언트 컴포넌트의 콘솔은찍히지않는다.
클라이언트 컴포넌트의 콘솔은 브라우저 콘솔에 찍힌다.
서버에서 렌더링되어 받아온 html은 이런형태이다. 클라이언트에서 페칭한 데이터를 보여주는영역은 값이 비어있다.
밑줄친영역의 클라이언트 컴포넌트에서 페칭해서 보여주는 데이터는 빈 span태그로 오는게 보이고 나머지 서버컴포넌트에서 받은데이터를 보여주는 span태그들은 값이 바인딩되어 오는걸 볼 수 있다.
서버컴포넌트와 클라이언트 컴포넌트를 잘 조합해서 사용해야한다는 말의 의미를 조금은 더 깨달은거 같은 기분이드는 공부였고 꾸준히 가자!
*제가 공부한것에서 틀린점이 있다면 언제나 지적은 환영입니다!:)