FEInterview Prep

react · high priority

React Server Components — 서버에서 렌더링되고 끝나는 컴포넌트

RSC · Flight 직렬화 · "use client" 경계 · Server Actions · Streaming SSR 와의 차이

advanced 난이도5시간토스당근카카오네이버배민라인쿠팡
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

복잡한 개념을 실생활 비유로 설명합니다.

극장과 무대 세트

*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 번들클라이언트 번들전체 트리
SSRHTML 문자열 + JS 번들클라이언트 번들 (서버에서도 실행)전체 트리
RSC직렬화된 React 트리(Flight) + 클라이언트 청크 JS서버에만 (Server) / 번들 (Client)"use client" 경계 안만
Streaming SSRHTML 청크들 + 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 페이로드의 정체를 한 문장으로 답해보세요.

기대 키워드

직렬화React 엘리먼트 트리JSON클라이언트 컴포넌트 슬롯

자주 하는 오해

"RSC 페이로드는 HTML 이다" — HTML 이 아니라 직렬화된 React 트리입니다. HTML 은 SSR 단계에서 별도로 만들어집니다.

Server Component 안에서 `useState` 가 동작하지 않는 *근본적 이유* 는?

기대 키워드

hydration 없음서버 일회 실행state 보존 위치 없음클라이언트 번들 제외

자주 하는 오해

"Next.js 가 막아둬서" 가 아닙니다 — RSC 는 서버에서 한 번 실행되고 클라이언트에 hydrate 되지 않으므로, state 가 살아 있을 곳이 *근본적으로 없습니다*.

"use client" 가 붙은 파일에 *무거운 라이브러리* 를 import 하면 어떤 영향이 있는가?

기대 키워드

클라이언트 번들 포함import 그래프 전염번들 절감 이점 손실

자주 하는 오해

"클라이언트 컴포넌트 안에서만 쓰니 RSC 영역엔 영향이 없다" — `"use client"` 의 import 그래프 전체가 클라이언트 번들에 들어가므로 *전염* 됩니다.

Server Action 이 보안 측면에서 *공개 엔드포인트처럼 다뤄져야* 하는 이유는?

기대 키워드

함수 ID클라이언트 노출인증/권한 검증 필수

자주 하는 오해

"클라이언트에 코드가 안 보이니 호출도 못 한다" — 함수 ID 가 직렬화되어 가므로 누구든 호출 가능합니다.

학습 자료