react · high priority
React Server Components — 서버에서 렌더링되고 끝나는 컴포넌트
RSC · Flight 직렬화 · "use client" 경계 · Server Actions · Streaming SSR 와의 차이
학습 개요
탄생 배경
쉬운 설명
복잡한 개념을 실생활 비유로 설명합니다.
“극장과 무대 세트”
*Server Component* 는 무대 *세트와 배경* 입니다 — 공연 전에 무대 뒤에서 다 만들어 두고, 관객(브라우저)에게는 *완성된 그림* 만 보여줍니다. *Client Component* 는 무대 위에서 *실시간으로 움직이는 배우* 입니다 — 관객의 박수에 반응(이벤트) 하고 대사를 바꿉니다(상태). `"use client"` 는 "이 부분은 배우가 필요해" 라고 표시하는 *마커* 입니다. *Flight 페이로드* 는 무대 도면이고, *하이드레이션* 은 배우들이 자기 위치를 잡고 첫 대사를 준비하는 리허설입니다.
핵심 개념
CSR (Client-Side Rendering)
서버는 빈 HTML + JS 번들만 보내고, 브라우저에서 React 가 컴포넌트를 처음부터 렌더링. 모든 컴포넌트 코드가 번들에 포함된다.
SSR (Server-Side Rendering)
서버에서 컴포넌트를 한 번 실행해 *HTML 문자열* 을 만든 뒤 클라이언트로 보낸다. 클라이언트는 같은 트리를 다시 만들고 *하이드레이션* 으로 이벤트 핸들러를 붙인다. 컴포넌트 코드는 여전히 번들에 포함.
RSC (React Server Components)
서버에서 컴포넌트를 실행해 *직렬화된 React 엘리먼트 트리* (Flight 페이로드) 를 보낸다. Server Component 의 코드는 *클라이언트 번들에서 제외*. 이벤트 핸들러·useState 가 필요한 부분만 "use client" 로 표시해 그 경계 안에서만 하이드레이션.
Streaming SSR
서버에서 컴포넌트 트리를 한 번에 끝내지 않고 *준비되는 부분부터 청크로 흘려 보내는* SSR 변형. RSC 와 직교 — RSC 는 *어디서 코드가 실행되는가*, Streaming 은 *언제 보낼 것인가*.
| 모델 | 서버 → 클라이언트로 가는 것 | 컴포넌트 코드 위치 | 하이드레이션 |
|---|---|---|---|
| CSR | 빈 HTML + JS 번들 | 클라이언트 번들 | 전체 트리 |
| SSR | HTML 문자열 + JS 번들 | 클라이언트 번들 (서버에서도 실행) | 전체 트리 |
| RSC | 직렬화된 React 트리(Flight) + 클라이언트 청크 JS | 서버에만 (Server) / 번들 (Client) | "use client" 경계 안만 |
| Streaming SSR | HTML 청크들 + JS 번들 | 클라이언트 번들 | 전체 트리 (점진적) |
RSC 는 SSR 의 대체가 아니라 *조합 가능한 다른 축*
Next.js App Router 의 RSC 페이지는 *기본적으로 Streaming SSR + RSC* 가 함께 동작합니다 — RSC 가 서버에서 트리를 만들고, 그 결과가 HTML 로 한 번 더 직렬화되어 *Streaming* 으로 흘러갑니다. "RSC 가 SSR 을 대체했다" 는 흔한 오해로, 실제로는 *두 축의 곱* 으로 이해해야 합니다.
실무 적용
어떤 상황에서 사용하는가
뉴스 피드 페이지 — 상단 헤더 + 좌측 카테고리 + 메인 피드(서버 데이터) + 댓글 입력 폼 + 좋아요 버튼.
어떻게 적용하는가
(1) 페이지 자체는 RSC 로. (2) 헤더·카테고리·피드 카드의 정적 부분은 모두 RSC — 서버에서 DB 직접 조회. (3) 좋아요 버튼은 작은 Client Component (`"use client"` + `useState` + Server Action). (4) 댓글 폼은 Client Component 지만 *내부 댓글 목록* 은 RSC 를 children 으로 받아 경계를 얇게. (5) Server Action 으로 좋아요·댓글 작성 처리, `revalidatePath` 로 같은 응답에 새 RSC 트리. (6) 부모 RSC 에서 `Promise.all` 로 헤더 데이터·피드를 *병렬 페치* . (7) Suspense 경계를 메인 피드 위에 두어 카테고리·헤더는 즉시 렌더, 피드만 스트리밍.
흔한 실수와 안티패턴
- 좋아요 버튼을 위해 *피드 카드 전체* 를 클라이언트 컴포넌트로 만들어 카드 마크업까지 번들에 포함되는 실수.
- Server Action 내부에서 인증/권한 체크를 빼서 누구나 다른 사람의 좋아요를 조작할 수 있는 사고.
- `useEffect` 안에서 다시 `fetch` 를 호출해 RSC 의 의미를 무력화.
- `Date.now()`/`Math.random()` 을 RSC 에서 결과로 그대로 보내, 새로고침마다 hydration mismatch 또는 캐시 미스.
- 컴포넌트 단위로 await 을 *순차* 로 걸어 waterfall 발생.
흔한 오해
"RSC 가 SSR 을 대체한다."
교정RSC 와 SSR 은 직교 축. App Router 에서는 *RSC + Streaming SSR* 가 함께 동작.
왜 중요RSC 는 *코드 실행 위치* 와 *번들 포함 여부* 를 결정하고, SSR 은 *초기 HTML 을 만들지 여부* 를 결정한다.
""use client" 를 붙이면 더 빨라진다."
교정오히려 클라이언트 번들에 코드가 포함되어 *느려진다*. 필요한 곳에만 최소 범위로 사용해야 한다.
왜 중요"use client" 는 인터랙션 가능 영역을 표시하는 *비용 표시* 다. 가능한 한 좁고 깊지 않게.
"Server Component 도 결국 매 요청 다시 그려지니 SSR 이랑 같다."
교정RSC 는 *클라이언트 번들에서 코드가 빠진다* 는 점에서 SSR 과 다르다. SSR 은 같은 코드가 클라이언트에서도 다시 실행되며 hydrate 된다.
왜 중요JS 비용 측면에서 결과가 아주 다르다 — RSC 영역은 hydrate 되지 않고 인터랙션 비용도 0 이다.
"Server Action 은 안전하다 — 클라이언트에 코드가 없으니까."
교정함수 *참조 ID* 만 직렬화돼 누구든 호출 가능. 액션 내부에서 인증·권한·입력 검증을 반드시 한다.
왜 중요Server Action 은 사실상 *공개된 RPC 엔드포인트* 다. 코드가 안 보인다고 보호되는 건 아니다.
면접 질문
답변 방향 힌트
"무엇을 보내는가 + 코드는 어디에 있는가 + 무엇을 hydrate 하는가" 세 축.
반드시 언급할 키워드
- SSR: 서버에서 HTML 생성 + 같은 컴포넌트 코드가 클라이언트 번들에도
- RSC: 서버에서 *직렬화 React 트리(Flight)* 생성, Server Component 코드는 *클라이언트 번들에서 제외*
- 하이드레이션 범위: SSR 은 전체 트리, RSC 는 `"use client"` 경계 안만
- RSC 는 SSR 의 대체가 아니라 *직교 축* — 둘이 함께 동작
- 결과: 클라이언트 JS 감소 + 서버 자원 직접 접근
예상 꼬리 질문
- Flight 페이로드는 정확히 어떤 형식이고, 거기에 클라이언트 컴포넌트는 어떻게 표현되나요?
- RSC 영역과 Streaming SSR 을 동시에 켜면 첫 바이트(TTFB) 와 LCP 에 어떤 영향이 있나요?
자기 점검
RSC Flight 페이로드의 정체를 한 문장으로 답해보세요.
기대 키워드
자주 하는 오해
"RSC 페이로드는 HTML 이다" — HTML 이 아니라 직렬화된 React 트리입니다. HTML 은 SSR 단계에서 별도로 만들어집니다.
Server Component 안에서 `useState` 가 동작하지 않는 *근본적 이유* 는?
기대 키워드
자주 하는 오해
"Next.js 가 막아둬서" 가 아닙니다 — RSC 는 서버에서 한 번 실행되고 클라이언트에 hydrate 되지 않으므로, state 가 살아 있을 곳이 *근본적으로 없습니다*.
"use client" 가 붙은 파일에 *무거운 라이브러리* 를 import 하면 어떤 영향이 있는가?
기대 키워드
자주 하는 오해
"클라이언트 컴포넌트 안에서만 쓰니 RSC 영역엔 영향이 없다" — `"use client"` 의 import 그래프 전체가 클라이언트 번들에 들어가므로 *전염* 됩니다.
Server Action 이 보안 측면에서 *공개 엔드포인트처럼 다뤄져야* 하는 이유는?
기대 키워드
자주 하는 오해
"클라이언트에 코드가 안 보이니 호출도 못 한다" — 함수 ID 가 직렬화되어 가므로 누구든 호출 가능합니다.
학습 자료
- Server Components — React 공식 레퍼런스Server Component 의 정의, 제약, Client 와의 경계에 대한 공식 설명.Docreact.dev
- Next.js — Server and Client ComponentsApp Router 의 RSC 도입, "use client" 경계, 데이터 페칭 패턴.Docnextjs.org
- Making Sense of React Server ComponentsRSC 가 SSR 과 어떻게 다른지, 왜 직교 축인지 가장 명료하게 설명한 글.BlogJosh W. Comeau
- React Server Components: Do They Really Improve Performance?RSC 의 실제 성능 영향과 함정에 대한 측정 기반 분석.BlogDeveloper Way
- Server Actions and MutationsServer Actions 의 작성·호출·revalidate 와 보안 고려사항.Docnextjs.org