Server Component vs Client Component — 차이와 기준
App Router 의 가장 중요한 개념. 두 모델을 정확히 갈라본다.
App Router 를 익히는 데 있어 가장 큰 산. 옛 React 가 100% 브라우저에서 돌던 것과 달리, 이제 컴포넌트가 서버 또는 클라이언트 중 한 곳에서 돈다. 같은 React 문법이지만 실행 위치가 다르고, 그래서 할 수 있는 일도 다르다.
이번 편에서 둘의 정확한 차이를 갈라본다. 기본은 서버 컴포넌트, 인터랙션이 필요할 때만 'use client'. 이 한 줄이 결국 이 챕터의 결론이지만, 그 결론에 도달하는 추론을 정리해 둬야 헷갈리지 않는다.
1. 기본 모델 — App Router 는 서버 컴포넌트 우선
Pages Router 시절 React 컴포넌트는 전부 브라우저에서 돌았다. 서버에선 첫 HTML 만 그리고 (SSR), 그 뒤 모든 인터랙션·상태·effect 는 브라우저 React 가 담당.
App Router 는 이걸 뒤집었다. 기본이 서버에서 실행, 클라이언트로 가는 건 명시적으로 신청해야 한다. 그 신청서가 파일 첫 줄의 'use client'.
위 두 파일의 차이는 단 한 줄 — 'use client' 의 유무. 그런데 동작이 완전히 달라진다.
2. 무엇이 다른가 — 일곱 가지 비교
| 항목 | 서버 컴포넌트 | 클라이언트 컴포넌트 |
|---|---|---|
| 실행 위치 | 서버 (Node 또는 엣지) | 서버에서 미리 + 브라우저에서 hydrate |
| JS 번들 포함 | X (HTML 만 전송) | O (브라우저로 보내짐) |
| DB·파일·서버 시크릿 | 직접 접근 가능 | 불가 (보안 사고) |
| 상태 / useState | 불가 | 가능 |
| useEffect / 이벤트 | 불가 | 가능 |
| 브라우저 API | 불가 | 가능 (window·document) |
| async/await | 컴포넌트 함수 자체가 async | useEffect 안에서만 |
요약 — 서버 컴포넌트는 데이터를 가져와 HTML 을 그리는 도구, 클라이언트 컴포넌트는 사용자 상호작용을 처리하는 도구. 역할이 명확히 갈린다.
3. 'use client' 의 정확한 의미
흔한 오해 — "'use client' 가 있으면 그 컴포넌트는 서버에서 안 돈다". 틀렸다.
정확한 의미는 — "여기서부터 아래는 클라이언트 컴포넌트 트리다. 브라우저로 JS 를 보내라". 그래도 서버에선 한 번 실행되어 초기 HTML 을 생성한다. 그 뒤 브라우저가 받은 JS 로 다시 한 번 실행해 hydrate.
'use client' 는 파일에 1번 적으면, 그 파일과 그 안에서 import 하는 모든 자식 컴포넌트가 클라이언트 트리에 속한다. 자식엔 또 적을 필요 없다. 마치 "여기부터 클라이언트 구역" 표지판 같은 것.
4. 섞어 쓰는 패턴 — 서버가 클라이언트를 children 으로 받기
실전에선 둘을 자유롭게 섞는다. 단 규칙 하나 — 서버가 클라이언트를 import 할 수 있지만, 클라이언트가 서버 컴포넌트를 import 할 수는 없다.
그래서 자주 쓰는 패턴은 children 으로 주입.
이 패턴이면 카로셀 인터랙션은 클라이언트에서, 상품 카드 자체의 데이터 fetch 는 서버에서. JS 번들도 슬라이더 부분만 클라이언트로 가니까 가볍다.
5. 어느 쪽을 골라야 하나 — 결정 트리
새 컴포넌트 만들 때 매번 고민하지 말고 이 순서로 결정.
- 이 컴포넌트에 useState·useEffect·onClick 같은 인터랙션 있나? → Yes 면
'use client'. - 브라우저 전용 API(window·localStorage·document) 가 필요한가? → Yes 면
'use client'. - 외부 클라이언트 라이브러리(framer-motion·zustand) 를 쓰나? → 보통 Yes 면
'use client'. - 그 외 모두 — 서버 컴포넌트 (기본). DB·외부 API 호출, HTML 만 그리는 정적 UI 등.
의심스러우면 서버 컴포넌트로 시작한다. 인터랙션 필요해지면 그 시점에 'use client' 한 줄 추가. 섣불리 클라이언트로 가지 말 것 — JS 번들이 커지고 LCP 가 나빠진다.
'use client' 박는 것. App Router 에선 이게 안티패턴. 가능한 깊은 leaf 만 클라이언트로 두고, 위쪽 트리는 서버로 유지. 검색하면 "'use client' 를 leaf 까지 미루라" 는 조언이 정답.
요약 — 6편 좌표
여기까지 정리. App Router 의 기본은 서버 컴포넌트, 인터랙션 필요 시 'use client'. 둘의 차이는 실행 위치·JS 번들·할 수 있는 일. 'use client' 는 "여기서부터 클라이언트 구역" 표지판이지 "서버에서 안 돌린다" 가 아니다. 섞을 땐 서버→클라이언트 children 주입 패턴. 의심되면 서버, 인터랙션 생기면 leaf 부터 클라이언트. 다음 편에선 Metadata API 로 SEO 를 세팅한다.
다음 편 예고 — Metadata API 로 SEO 설정
title·description·OG 이미지·sitemap 까지 한 API 로. 7편.