요즘 react, next.js 에서 대두되는 서버 컴포넌트에 대한 개념을 정리하기 전에 왜 이런 컴포넌트들이 생기게 되었는지 알 필요가 있다.
먼저 이전에 page 라우터 버전의 next.js의 문제에서 서버 컴포넌트가 생긴 이유를 알 수 있다.
기존의 page router 방식에서는, 상호작용이 필요없는 컴포넌트까지 파일을 불러오고, 하이드레이션이 필요없는 컴포넌트까지 렌더링을 수행하기 때문에 불필요한 JS번들링 파일의 크기가 증가하고 비용이 더 발생한다. 이러한 이유로 인해 next.js서버에서 사전렌더링을 할 때 단 한번만 렌더링이 일어나도록 하는 컴포넌트를 서버 컴포넌트
로 정한 것이다.
서버 컴포넌트
는 서버측에서 사전 렌더링을 진행할 때, 딱 한번만 실행된다.
클라이언트
는 사전 렌더링, 하이드레이션 두번 렌더링이 일어난다.
추천하는 컴포넌트 방식은 서버 컴포넌트로, 클라이언트 컴포넌트는 꼭 필요한 경우에만 사용하도록 next.js에서 권장하고 있다.
그렇다면, 어떤 컴포넌트가 클라이언트 컴포넌트가 되어야하냐? 물론, 상호작용이 있는 경우는 클라이언트 컴포넌트로 작성한다
ex) 검색창 컴포넌트, 매번 바뀌는 추천 상품 리스트 컴포넌트 등
왜냐하면 Next.js 서버에서만 렌더링이 실행되는 컴포넌트이기 때문에 웹 브라우저 환경이 아니기 때문이다. 또한 브라우저의 기능을 사용하는 라이브러리도 사용할 수 없다.
클라이언트 컴포넌트는 이전 page router 버전의 컴포넌트와 동일한 방식으로 렌더링이 2번 진행된다. Next.js 서버측 렌더링과 하이드레이션을 위한 브라우저에서의 렌더링이 일어나게 된다.
즉, 클라이언트와 서버단 둘 다 렌더링이 일어나게 된다.
클라이언트 컴포넌트의 코드는 서버와 브라우저에서 모두 실행 됨. 그러나 서버컴포넌트는 서버에서만 한번 실행되기때문 브라우저에서 서버컴포넌트를 import할 수 없기 때문이다. 클라이언트 컴포넌트에서 JS번들 파일을 가져올 때, 서버 컴포넌트의 코드는 제외된 상태이기 때문이다.
그러나, 나도 모르게 서버 컴포넌트인줄 모르고 import할 수도 있는데, next.js는 이러한 경우에는 서버 컴포넌트를 클라이언트 컴포넌트로 변경시키게된다.
하지만, 예기치 못한 상황으로 클라이언트 컴포넌트에서 서버 컴포넌트를 사용해야할 경우일 때는 import 하지말고,
"use client";
export default function ClientComponent({ children }: {children: ReactNode}) {
return <div>{children}</div>;
}
이렇게 children
으로 넘겨주고,
사용될 페이지에서
export default function Home() {
return (
<div>Home
<ClientComponent>
<ServerComponent />
</ClientComponent>
</div>
)
}
이런식으로 전달해주는 방식이 좋다.
이렇게하면 Next.js 에서는 서버컴포넌트를 클라이언트 컴포넌트로 변환하지 않고, 브라우저에서 렌더링없이 그 결과물만 children 이라는 props로 전달받게 된다.
서버 컴포넌트에서 클라이언트 컴포넌트에게 직렬화 되지 않는 props
는 전달 불가하다.
여기서 직렬화란? serialization 객체, 배열, 클래스 등의 복잡한 구조의 데이터를 네트워크상으로 전송하기 위해 아주 단순한형태(문자열, 바이트)로 변환하는 것이다
const person = {
name: "Kim",
age: 10,
},
{"name":"Kim","age":10}
함수는 참고로 직렬화가 불가능하다. 함수는 값을 가지고 있는것이 아닌 코드블럭들을 가지고 있는 형태이며, 클로저, 렉시컬스코프의 환경에 의존하고 있어서 단순한 문자열이나 바이트로 직렬화하기 어렵기 때문이다.
유저에서 페이지 요청이 왔다면, 서버에서 사전렌더링의 과정을 시작하게 된다. 여기서 디테일하게 과정을 설명하면,
먼저 서버컴포넌트들만 따로 먼저 렌더링을 실행한다.
그 후, 클라이언트 컴포넌트가 렌더링을 실행한다. 여기서 서버컴포넌트를 html로 바로 변환하는 것이아닌, 서버 컴포넌트는 RSC Payload
로 JSON과 비슷한 문자열이 생성된다.
여기서
RSC Payload
란 React Server Component의 순수한 데이터 결과물(문자열), 직렬화 한 결과이다.
이 안에는 서버 컴포넌트의 렌더링 결과, 연결된 클라이언트 컴포넌트의 위치, 클라이언트 컴포넌트에게 전달하는 props값이 포함된다.
그래서 이때 RSC payload 에서 함수형 형태의 값을 props
로 전달하고 있다면? 직렬화가 불가능하기 때문에 사용하지 못하는 것이다.