서버 컴포넌트 vs 클라이언트 컴포넌트
서버·클라이언트 컴포넌트 책임 분리, hydration 비용, 안티패턴과 실무 판단 기준
“어디서 실행되는가”가 아니라 “어디까지 책임지는가”
React Server Components(RSC)는 많은 혼란을 낳았다.
이유는 단순하다. 대부분이 이 개념을 물리적 서버/클라이언트 구분으로 이해하기 때문이다.
이 글의 핵심 전제는 다음이다.
서버 컴포넌트와 클라이언트 컴포넌트의 차이는
배포 위치가 아니라, 실행 책임과 비용 분리다.
1. 서버 컴포넌트의 본질
“서버에서 렌더링된다”는 말은 절반만 맞다
서버 컴포넌트의 본질은 다음 한 문장으로 요약된다.
브라우저로 전달되지 않아도 되는 React 컴포넌트
핵심 특성
- 브라우저 JS 번들에 포함되지 않음
- hydration 대상이 아님
- 서버에서 실행되고 결과만 HTML/스트림으로 전달
즉, 서버 컴포넌트는
- UI를 “보여주기”는 하지만
- UI를 “관리”하지는 않는다
👉 서버 컴포넌트는 렌더링 전용 컴포넌트다.
흔한 오해 정리
❌ “서버 컴포넌트는 백엔드 코드다”
→ 아니다. React 컴포넌트이며 UI 트리를 구성한다.
❌ “서버 컴포넌트는 항상 SSR이다”
→ 아니다. SSG, 스트리밍, 캐시된 결과 모두 가능하다.
❌ “서버 컴포넌트는 느리다”
→ 오히려 클라이언트 JS 제거로 체감 성능은 개선된다.
2. 클라이언트 컴포넌트가 필요한 조건
“할 수 있어서”가 아니라 “불가피해서” 사용한다
NextJS App Router에서 기본값은 서버 컴포넌트다.
클라이언트 컴포넌트는 예외 케이스다.
클라이언트 컴포넌트가 필요한 경우
다음 중 하나라도 해당하면 필요하다.
- 브라우저 전용 API 사용 (window, document 등)
- 사용자 입력 처리
- 이벤트 핸들러(onClick 등)
- 클라이언트 상태(useState, useReducer)
- useEffect 사용
이 조건은 모두 하나로 귀결된다.
렌더링 이후에도 UI가 스스로 변화해야 하는가?
YES → 클라이언트 컴포넌트
NO → 서버 컴포넌트
중요한 설계 원칙
클라이언트 컴포넌트는
UI 트리의 말단에 위치해야 한다
- 상위는 서버 컴포넌트
- 인터랙션이 필요한 최소 단위만 클라이언트
이 원칙을 어기면 비용이 폭증한다.
3. Hydration 비용과 렌더링 흐름
“보이는 것”과 “살아있는 것”의 차이
전통적인 React SPA 흐름
- 빈 HTML 수신
- JS 다운로드
- React 실행
- 전체 UI hydration
👉 모든 UI가 브라우저에서 살아난다
App Router + 서버 컴포넌트 흐름
- 서버에서 UI 트리 생성
- HTML + 스트리밍 전송
- 클라이언트 컴포넌트만 hydration
👉 살아야 할 것만 살아난다
Hydration 비용이란?
- JS 파싱
- 이벤트 바인딩
- 상태 초기화
이 비용은:
- 컴포넌트 수
- 트리 깊이
- 상태 복잡도
에 비례한다.
서버 컴포넌트는 이 비용을 근본적으로 제거한다.
4. 남용 사례와 안티패턴
안티패턴 1: 'use client'를 상단에 붙이고 시작
- “나중에 생각하자”는 접근
- 결과: 전체 트리 클라이언트화
- 서버 컴포넌트 이점 소멸
안티패턴 2: 서버 데이터를 클라이언트 상태로 재포장
- 서버 캐시 무력화
- 중복 fetch
- 불필요한 리렌더링
안티패턴 3: 서버 컴포넌트에서 클라이언트 로직 흉내
- 불필요한 분기
- 책임 혼재
- 가독성 저하
5. 실무 판단 기준 체크리스트
컴포넌트를 작성하기 전, 아래 질문을 순서대로 던져라.
- 이 컴포넌트는 렌더링 이후에도 변해야 하는가?
- 브라우저 API가 필요한가?
- 사용자 입력을 직접 다루는가?
- 상태가 없다면 서버에서 끝낼 수 있는가?
- 이 컴포넌트를 클라이언트로 만들면
상위 컴포넌트까지 전염되지 않는가?
5번에서 YES라면, 설계가 잘못됐다.
결론: 서버 컴포넌트는 “최적화 기법”이 아니다
서버 컴포넌트는 성능 트릭이 아니다.
- 실행 책임 분리
- 비용 위치 이동
- UI의 생명주기 단순화
이를 통해 NextJS는
React 애플리케이션을 ‘운영 가능한 구조’로 만든다.
서버 컴포넌트를 잘 쓴다는 것은
React를 덜 쓰는 것이 아니라,
React를 정확히 쓰는 것이다.
