새 폴더가 아니라 새 실행 모델이야
App Router는 React Server Components, Suspense, 스트리밍을 한 덩어리로 설계했어. 그래서 화면을 먼저 완성한 뒤 보내는 대신, 서버와 브라우저가 각자 잘하는 일을 나누고 준비된 부분부터 전달할 수 있어.
- 서버 컴포넌트가 기본값이야. 브라우저에 보낼 필요 없는 라이브러리와 데이터 처리를 서버에 남겨.
- 중첩 레이아웃이 유지돼. 자식 페이지가 바뀌어도 공통 셸과 그 상태는 살아 있어.
- 느린 부분만 기다려. Suspense 경계마다 독립적으로 준비하고 스트리밍해.
- 여러 슬롯을 함께 그려. 각 슬롯이 자기 로딩과 오류 경계를 가질 수 있어.
- 변경 작업을 서버 함수로 표현해. 내부 CRUD마다 공개 API 주소를 새로 만들 필요가 없어.
클라이언트 JavaScript는 예산이야
상태와 이벤트가 필요한 작은 컴포넌트만 클라이언트로 보내고, 나머지는 서버에 두는 게 기본 자세야. “전부 브라우저에서 돌린 뒤 조금 줄이자”가 아니라 “필요한 부분만 브라우저에 보내자”로 출발점이 뒤집혀.
App Router를 쓰면서 옛 습관을 유지하면
모든 데이터를 페이지 꼭대기에서 읽고, 모든 컴포넌트에 'use client'를 붙이고, 변경마다 API 라우트를 만들면 새 구조의 비용만 내고 이점은 못 받아. 폴더를 옮기는 것보다 책임을 다시 배치하는 일이 먼저야.
화면 트리를 그린 뒤 상태·이벤트·브라우저 API가 필요한 노드에만 표시해 봐. 그 표시가 없는 조상과 형제는 서버에 남길 수 있어. 느린 데이터는 하나의 페이지 loading으로 덮기보다 사용자가 먼저 이해할 수 있는 덩어리마다 Suspense 경계를 두고, 공통 셸은 레이아웃에서 유지해.
서버 컴포넌트가 많다고 무조건 빠른 건 아니야. 서버에서 거대한 데이터를 읽고 직렬화해 클라이언트에 넘기거나, 작은 요청을 순차로 기다리면 다른 병목이 생겨. 핵심은 실행 위치를 숭배하는 게 아니라 전송량·왕복·상호작용의 주인을 가장 싼 곳에 배치하는 거야. 브라우저의 JavaScript를 끄거나 느리게 한 뒤 공통 셸이 언제 보이고 어떤 잎이 상호작용을 잃는지 관찰해. bundle analyzer에서는 use client 경계 하나를 위아래로 옮겨 전송량이 얼마나 달라지는지 재면 경계 위치가 단순 취향이 아니라는 걸 확인할 수 있어.