FEInterview Prep

rendering · high priority

Next.js App Router

파일 컨벤션 = React 트리. RSC · Streaming · Cache Components 까지 한 시스템으로 묶인 라우팅

intermediate 난이도6시간토스카카오네이버배민당근
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

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

대형 백화점의 매장 배치도

`app/` 의 폴더 구조는 *백화점 평면도* 와 같습니다. `layout` 은 *층 전체에 깔린 안내 데스크* — 매장(자식 페이지) 을 옮겨다녀도 안내 데스크는 그대로 서 있죠. `loading` 은 *공사 가림막*, `error` 는 *비상구 안내*. `(group)` 은 *사람에겐 안 보이지만 직원이 쓰는 구역 분류표*. `@slot` 은 *대형 디스플레이 두 개를 동시에 켜둔* 모양. PPR 은 *건물 외관은 미리 지어두고, 가격표만 그날 아침에 바꿔다는* 운영 — 손님이 빨리 들어오면서도 가격은 항상 최신.

핵심 개념

page.tsx

해당 세그먼트의 *고유 UI*. 이 파일이 있어야 URL 로 접근 가능. 기본 RSC.

layout.tsx

하위 세그먼트를 감싸는 *영속 UI*. navigation 시 *리렌더 / 언마운트되지 않음* — 상태가 유지된다.

loading.tsx

자동으로 <Suspense fallback> 경계를 만든다. 같은 세그먼트의 page.tsx 가 서버에서 데이터 로딩 중일 때 즉시 표시된다.

error.tsx

자동으로 ErrorBoundary 를 만든다. *항상 Client Component* — reset() 으로 재시도 가능.

not-found.tsx

notFound() 호출 시 렌더되는 UI. 세그먼트별로 둘 수 있다.

template.tsx

layout 과 비슷하지만 navigation 마다 *새 인스턴스로 마운트*. 페이지 전환 애니메이션·useEffect 재실행이 필요할 때.

route.ts

HTTP 메서드 핸들러 (GET/POST/...) 를 export 하는 *API 엔드포인트*. webhook · 외부 통합용.

layout 은 *상태 유지*, template 은 *매번 새로*

layout.tsx 는 자식 세그먼트 navigation 시 리렌더되지 않습니다. 사이드바 스크롤 위치, 폼 입력값이 그대로 남죠. *반대로* 페이지마다 fade-in 애니메이션을 다시 돌리거나 useEffect 를 다시 실행하고 싶으면 template.tsx 를 씁니다.

실무 적용

어떤 상황에서 사용하는가

월간 100만 PV 의 이커머스 상품 상세 페이지. LCP 2.5s 미만 + 재고는 항상 최신 + 마케터가 상품 설명 변경 시 즉시 반영을 모두 만족해야 함.

어떻게 적용하는가

(1) `app/product/[id]/page.tsx` 를 RSC 로 두고 상품 메타(이름·설명·이미지) 는 `"use cache"` + `cacheLife("days")` + `cacheTag(\`product-${id}\`)` 로 캐시. (2) 재고 표시는 `<Suspense>` 로 감싼 별도 RSC 에서 `fetch(..., { cache: "no-store" })`. PPR 이 자동으로 정적 셸 + 동적 구멍으로 쪼갬. (3) 어드민에서 상품 수정 시 Server Action 에서 `revalidateTag(\`product-${id}\`)` 호출 → 다음 요청부터 새 캐시. (4) 인터랙션이 필요한 *장바구니 버튼* 만 `"use client"` 잎 컴포넌트로 분리해 클라이언트 번들 최소화. (5) `loading.tsx` 를 *세그먼트별로* 두어 첫 렌더에서 깜빡임 최소화.

흔한 실수와 안티패턴

  • 루트 가까이 `"use client"` 를 두어 페이지 전체가 클라이언트 번들이 됨.
  • Server Action 에서 입력 검증·인가를 빼먹어 공개 엔드포인트로 노출.
  • `fetch` 를 16 에서도 자동 캐시된다고 가정 — 16 부터 기본 `no-store` 라 의도와 다른 SSR 부담.
  • `async` 컴포넌트에서 `await params` 를 빠뜨려 빌드 에러.
  • `loading.tsx` 위치를 너무 위에 두어 *세그먼트 전체* 가 깜빡임.
  • Parallel Routes 의 default.tsx 를 안 만들어 새로고침 시 404.

흔한 오해

오해

"layout 은 navigation 시 다시 렌더된다."

교정

App Router 의 `layout.tsx` 는 자식 세그먼트 navigation 시 *언마운트되지 않고 상태가 유지* 된다. 매번 새로 마운트하고 싶다면 `template.tsx`.

왜 중요

"layout 안 사이드바의 스크롤 위치가 유지된다" 는 흔한 관찰이 이 컨벤션의 결과다.

오해

"`"use server"` 함수는 안전한 내부 함수다."

교정

"use server" 는 사실상 *공개 RPC 엔드포인트*. 누구나 임의 인자로 호출 가능 — 입력 검증과 인증·인가를 *반드시* 함수 시작부에 둔다.

왜 중요

Server Action 은 클라이언트가 직접 호출하므로 보안 표면적이 일반 API 와 같다. Zod + 세션 체크가 표준.

오해

"PPR 은 SSG 와 SSR 사이 어딘가의 *느린 옵션* 이다."

교정

PPR 은 *정적 셸로 LCP 를 즉시* + *동적 구멍은 streaming* 으로 둘 다 챙기는 모델. 잘 쓰면 SSG 보다 빠를 수 있다.

왜 중요

브라우저는 첫 바이트로 의미 있는 HTML 을 받고, 동적 부분만 추가 청크로 흘러들어온다 — 둘이 직렬이 아니라 병렬.

면접 질문

중급토스카카오네이버

답변 방향 힌트

"부분 레이아웃", "워터폴", "번들 크기" 가 키워드.

반드시 언급할 키워드

  • 부분 레이아웃 / 영속 layout — `_app.tsx` 단일 루트 한계 해소
  • RSC 로 클라이언트 번들에서 빠지는 컴포넌트 → LCP / TTI 개선
  • 컴포넌트 단위 데이터 페칭 + Suspense → 워터폴 해소, streaming
  • Server Actions / 캐시 태그 등 *mutation 모델* 일원화
  • `loading.tsx`/`error.tsx` 컨벤션으로 Suspense/ErrorBoundary 자동화

예상 꼬리 질문

  • Pages Router 의 `getServerSideProps` 는 App Router 에서 어떻게 매핑하시겠습니까?
  • 마이그레이션을 점진적으로 한다면 어떤 순서로 옮기시겠습니까?

자기 점검

`layout.tsx` 와 `template.tsx` 의 *런타임 차이* 한 줄로?

기대 키워드

언마운트상태 유지매번 새로 마운트

자주 하는 오해

"layout 도 매번 다시 렌더된다" 는 잘못. layout 은 자식 navigation 에서도 *유지* 된다.

`error.tsx` 가 항상 Client Component 여야 하는 이유는?

기대 키워드

error boundarycomponentDidCatch`reset`

자주 하는 오해

"서버에서 에러를 잡아주는 파일" 이 아니라 *클라이언트의 ErrorBoundary 를 정의* 하는 파일이다.

Server Action 을 안전하게 호출 가능한 *공개 엔드포인트* 처럼 설계하기 위해 함수 시작부에 두어야 하는 두 가지는?

기대 키워드

입력 검증Zod인증/인가session

자주 하는 오해

"내가 작성한 form 만 호출하니 안전하다" — 누구나 임의 payload 로 호출 가능.

학습 자료