Server/Client Component 경계에서 지켜야 할 것들
2025년 1월 30일
경계 설정의 핵심은 "이 코드가 어디서 실행되는가"를 이해하는 것이다.
Server Component에서 쓸 수 없는 것들
서버 컴포넌트는 서버에서 단 한 번 실행되고 끝난다. 상태를 가지지 않으며, 렌더링 후 DOM이 마운트되는 개념이 없다.
React 훅
useState, useReducer, useEffect, useLayoutEffect는 쓸 수 없다. 상태와 생명주기 개념 자체가 없기 때문이다.
브라우저 전용 API
window, document, localStorage, navigator는 서버에 존재하지 않는다. 접근하면 에러가 발생한다.
이벤트 핸들러
onClick, onChange, onSubmit 등 사용자 액션을 감지하는 핸들러를 붙일 수 없다. 서버에서 렌더링이 끝난 시점엔 사용자가 없다.
상태를 사용하는 Custom Hook
내부적으로 useState 등을 쓰는 커스텀 훅도 호출할 수 없다. 훅 자체가 클라이언트 실행 환경을 전제로 하기 때문이다.
Client Component에서 주의해야 할 것들
클라이언트 컴포넌트는 코드 전체가 브라우저로 내려간다. 에러가 나는 경우와 권장하지 않는 경우 두 가지가 있다.
민감한 정보 (보안)
데이터베이스 비밀번호, API Secret Key 등 백엔드 환경 변수가 들어가면 안 된다. 소스 코드가 브라우저에 그대로 노출된다.
직접적인 서버 리소스 접근
DB 쿼리나 Node.js의 fs 모듈 같은 서버 전용 모듈은 브라우저에서 실행될 수 없어 에러가 발생한다.
무거운 라이브러리
에러는 아니지만, 마크다운 파서나 복잡한 포맷팅 라이브러리를 클라이언트 컴포넌트에 넣으면 번들 크기가 커져 초기 로딩이 느려진다. 서버 컴포넌트에서 처리하고 결과값만 넘기는 게 낫다.
Props 전달 시 제약 사항
서버 컴포넌트에서 클라이언트 컴포넌트로 Props를 넘길 때 가장 많이 실수하는 부분이다.
서버에서 클라이언트로 넘어가는 데이터는 네트워크를 거치며 JSON으로 직렬화(Serialization)된다. 직렬화가 안 되는 값은 넘길 수 없다.
함수 전달 불가
// 불가능
function Page() {
const handleClick = () => console.log("clicked");
return <Button onClick={handleClick} />; // 에러
}
이벤트 핸들러나 콜백 함수는 직렬화가 안 된다. 단, Server Actions는 예외다.
// Server Actions는 가능
async function handleSubmit() {
"use server";
// 서버에서 실행되는 액션
}
return <Form action={handleSubmit} />;
클래스 인스턴스 전달 불가
Date 객체나 커스텀 클래스 인스턴스도 직렬화가 안 된다.
// 불가능
const date = new Date();
return <Component date={date} />; // 에러
// 가능 — 문자열로 변환
return <Component date={date.toISOString()} />;
순수 데이터(문자열, 숫자, 일반 객체, 배열)만 넘길 수 있다. 클래스 인스턴스는 반드시 변환해서 넘겨야 한다.