레이아웃은 하위 라우트를 감싸고 살아남아
layout.tsx는 자신이 놓인 구간과 그 아래의 모든 라우트를 감싸. 같은 레이아웃 아래에서 형제 페이지로 이동해도 레이아웃 컴포넌트는 언마운트되지 않아. 내비게이션, 사이드바, 레이아웃이 가진 로컬 상태를 페이지마다 다시 만들지 않아도 되는 이유야.
루트 레이아웃은 반드시 하나 필요해
모든 App Router 앱에는 app/layout.tsx가 있어야 하고, 여기서 <html>과 <body>를 직접 반환해야 해. 문서 언어, 전역 글꼴, body 클래스처럼 앱 전체가 상속할 설정도 이 자리에 둬. 이 두 태그를 다른 페이지에서 다시 만들면 문서 구조가 깨져.
레이아웃이 받는 값에는 경계가 있어
레이아웃은 하위 페이지나 레이아웃이 들어오는 children을 받아. 동적 구간 안에 있다면 Next.js 15부터 params도 Promise로 오므로 기다려서 읽어야 해. 반면 searchParams는 받지 않아. 레이아웃이 질의 문자열에 따라 달라져야 한다면, 그 데이터를 페이지 안의 서버 컴포넌트에서 읽도록 책임을 옮기는 편이 맞아.
유지가 곧 계약이야
레이아웃에 놓은 상태는 이동 뒤에도 남는다는 전제 아래 설계해야 해. 페이지가 바뀔 때마다 초기화돼야 하는 폼이나 애니메이션을 무심코 넣으면 오래된 상태가 따라다닐 수 있어. 그런 경우에만 다음 레슨의 template.tsx를 검토해.
레이아웃에 넣을 항목마다 두 질문을 해 봐. 하위 모든 페이지가 정말 공유하는가, 형제 페이지를 오갈 때 상태가 남아야 하는가. 두 답이 모두 맞으면 레이아웃 자리야. 검색 입력처럼 페이지마다 초기화돼야 한다면 더 아래로 내리거나 템플릿을 써. 공통으로 보인다고 전부 루트 레이아웃에 올리면 앱 전체가 하나의 거대한 수명을 가져. 특정 구역에서만 필요한 provider와 데이터가 모든 페이지 비용이 될 수 있어. 가장 넓은 공통 조상이 아니라 필요한 범위의 가장 가까운 공통 조상에 배치해. 형제 페이지를 오가며 레이아웃의 입력 상태와 페이지의 입력 상태를 각각 바꿔 봐. 무엇이 남고 무엇이 초기화되는지 예상표와 비교하고, query string 변화가 레이아웃을 다시 그리지 않는다는 사실도 확인해. 수명 차이가 요구와 다르면 컴포넌트 위치부터 고쳐.