넥스트 15부터 페이지 params와 searchParams는 프로미스입니다. 서버는 await, 클라이언트는 use로 푼 뒤 슬러그와 카카오 code를 읽습니다. 레이아웃에는 쿼리가 없고, cacheComponents에선 동기 접근이 에러입니다. 프론트엔드, 앱 라우터, 1인 개발자 기준. 2026년 9월 Next.js 공식 문서.
넥스트 15부터 페이지와 레이아웃에 들어오는 params는 문자열이 담긴 객체가 아니라, 그 객체를 나중에 주는 프로미스입니다. params.slug를 동기로 꺼내면 개발 모드가 경고를 남기고, cacheComponents가 켜진 빌드에서는 동기 접근 자체가 에러가 됩니다.
제가 글 상세 페이지를 15로 올린 날, params.slug가 예전이랑 같은 줄인 줄 알고 바로 꺼냈습니다. 화면은 한동안 됐고, 터미널만 노랬습니다. 공식 페이지 문서가 버전 14까지는 동기 속성이었고, 15부터는 프로미스라고 적습니다.
훅을 서버 파일에 넣은 칸, 브라우저 전용 값을 맨 위에서 읽은 칸, 객체를 화면 자식으로 넣은 칸과 다릅니다. 여기는 동적 세그먼트와 쿼리 문자열을 읽는 자리만 봅니다. 한국 1인 쇼핑몰의 상품 주소, 블로그 글 주소가 거의 여기입니다. 카카오 콜백이랑 글 상세를 같은 날 올리면 경고가 두 파일에서 따로 납니다. 글 상세는 슬러그, 콜백은 쿼리입니다.
먼저 기억할 것 | 화면이 나와도 터미널 경고를 덮지 마세요. 15는 이주를 위해 동기 읽기를 잠시 받아 주고, 이후 모드에서는 같은 줄이 빌드를 죽입니다.
서버 페이지는 기다린 뒤에 슬러그를 꺼낸다
서버 컴포넌트 페이지는 함수를 비동기로 두고 await params부터 합니다. 글 본문을 가져오는 조회도 그 다음에 두면 슬러그 문자열이 확정된 뒤에만 쿼리가 나갑니다.
파일
들어오는 값
푸는 방법
서버 page
params, searchParams
await 후 필드
클라이언트 page
params, searchParams
use 후 필드
layout
params만
await 또는 use
generateMetadata
params, searchParams
await 후 필드
route 핸들러
두 번째 인자의 params
await segmentData.params
페이지가 아닌 클라이언트 자식
페이지 props 없음
useSearchParams 훅
레이아웃에는 searchParams가 없습니다. 쿼리가 바뀔 때마다 레이아웃을 다시 그리지 않기 때문입니다. 필터나 페이지 번호는 페이지에서 기다리거나, 클라이언트 자식에서 훅으로 읽습니다. 레이아웃과 페이지 문서가 이 분기를 적습니다.
서버 페이지에서 슬러그 기다리기
// 깨지는 줄. 15부터 params는 프로미스다
export default async function Page({ params }) {
const post = await getPost(params.slug);
return <Article post={post} />;
}
// 고친 줄. 문자열을 받은 뒤에 조회한다
export default async function Page({ params }) {
const { slug } = await params;
const post = await getPost(slug);
return <Article post={post} />;
}
서버 페이지는 await params 다음에 조회를 보낸다
클라이언트 페이지는 use로만 푼다
클라이언트 컴포넌트는 await를 함수 몸통에 둘 수 없습니다. 페이지 파일 맨 위에 클라이언트 지시자가 있으면 use(params)로 풉니다. 리액트 use는 프로미스를 읽는 훅이 아니라, 렌더 중에 프로미스를 풀어 주는 함수입니다.
useState를 쓰려고 지시자를 붙인 자리와 섞지 마세요. 지시자가 필요한 이유는 훅이고, 슬러그를 못 읽는 이유는 프로미스입니다. 두 줄이 한 파일에 같이 있을 수는 있어도, 고치는 칸은 다릅니다.
페이지가 아닌 버튼, 필터 칩 같은 자식은 페이지 props를 받지 않습니다. 그 파일에서 쿼리를 읽으려면 useSearchParams를 씁니다. 페이지 파일의 searchParams 프로미스와 훅은 같은 값이어도 입구가 다릅니다.
인가 코드는 경로가 아니라 물음표 뒤입니다. 카카오 인가 코드 요청이 돌려 주는 주소는 redirect_uri?code=... 형태입니다. params.code를 찾으면 비어 있고, searchParams를 기다리면 문자열이 나옵니다.
토스 결제 성공 주소의 주문번호, 다음 로그인 콜백의 상태 값도 같은 자리입니다. 배열로 두 번 붙은 키는 문자열이 아니라 배열일 수 있습니다. 공식 페이지 문서 표가 ?a=1&a=2를 배열로 적습니다. 한 개만 받을 거면 첫 칸을 고르거나, 카카오처럼 키가 하나인 줄만 믿으세요.
페이지만 고치고 레이아웃과 메타를 남겨 두면, 본문은 되고 탭 제목만 경고가 납니다. generateMetadata도 await params가 필요합니다. 라우트 핸들러는 두 번째 인자의 params를 기다립니다.
아이콘, 오픈그래프 이미지 파일에 동적 세그먼트가 있으면 그 파일의 params도 프로미스입니다. 15 업그레이드 문서가 레이아웃, 페이지, 라우트, 아이콘까지 한 목록으로 적습니다. 글 상세만 고치고 메타 파일을 건너뛰면 배포 로그에 같은 경고가 남습니다. 탭 제목에 상품 이름을 넣으려면 슬러그를 기다린 다음 조회하고, 그 결과를 제목 칸에 넣습니다. 기다림을 빼면 탭 글자가 비거나 경고가 남습니다.
쿠키와 요청 헤더 함수도 15부터 기다려야 합니다. 그건 로그인 세션 칸이고, 이 글의 슬러그 칸과 형제는 맞지만 입구가 다릅니다. 슬러그 경고를 쿠키 대기로 덮지 마세요.
동기로 읽으면 언제 경고고 언제 에러인가
15 기본값은 동기 읽기를 잠시 허용하고 개발 모드에 경고를 남깁니다. cacheComponents가 켜지면 같은 줄이 프리렌더 에러가 됩니다. 공식 메시지 페이지가 이 분기를 적습니다.
상황
증상
먼저 할 일
넥스트 15, 기본
화면은 되고 개발 경고
지금 await 또는 use로 바꾸기
cacheComponents 켜짐
동기 접근 에러
페이지를 비동기로 바꾸고 기다리기
재수출한 페이지 컴포넌트
코드모드가 못 찾음
실제 페이지 함수를 직접 고치기
정적 껍데기를 남기고 싶을 때
페이지 전체가 동적
프로미스를 자식에 통째로 넘기기
코드모드가 페이지 파일을 재수출만 하는 구조를 놓치면, 바깥 함수가 예전처럼 params.slug를 읽습니다. 공식 동기 접근 메시지 페이지가 이 함정을 따로 적습니다. 업그레이드 문서의 코드모드를 돌린 뒤에도 경고가 남으면, 재수출된 함수부터 여세요.
페이지에서 굳이 기다리지 않고 프로미스를 자식에게 통째로 넘긴 뒤, 조회하는 서버 자식에서만 풀 수도 있습니다. 동적 세그먼트 문서가 이 패턴을 정적 껍데기와 함께 보여 줍니다. 글 제목은 바로 그리고 본문만 기다릴 때 씁니다. 1인 개발자가 버셀 프리뷰에서 글 목록은 바로 뜨고 상세만 늦게 뜨는 화면을 만들 때 이 줄을 씁니다.