TechFeedTechFeed
Frontend

cookies(), headers(), await | 넥스트가 동기라고 경고하면?

cookies()를 await 하라는 경고는 취향이 아니라 넥스트 15부터 요청 API가 Promise를 돌려 동기 호출이 깨지는 줄입니다. headers·draftMode도 같고, 서버 컴포넌트와 라우트 핸들러를 async로 바꿉니다. Next.js 15, 쿠키, 한국 1인 개발자 기준. 2026년 9월 Next.js cookies·sync dynamic APIs 문서.

by

cookies()를 await 하라는 경고는 취향이 아니라, 넥스트 15부터 요청 API가 Promise를 돌려서 동기처럼 쓰면 값이 비거나 빌드가 멈추는 줄입니다. headers()draftMode()도 같은 칸입니다.


예전에 cookies().get('sid')로 되던 코드가 타입 에러나 런타임 경고를 내기 시작합니다. 서버 컴포넌트, 라우트 핸들러, 서버 액션 어디서든 납니다.


호출부 앞에 await를 붙이고, 그 함수를 async로 바꾸면 대부분 끝납니다. 공식 마이그레이션 안내가 같은 처방을 적습니다. 근거는 Next.js cookies()sync dynamic APIs 메시지에 있습니다.


cookies()를 await 하라는 경고가 뜨는 이유

요청 API가 동기가 아니어서입니다. 넥스트 15는 cookies, headers, draftMode를 비동기로 바꿨고, 동기 호출은 호환 계층을 거칩니다. 그 계층이 경고를 띄우거나, 설정에 따라 에러로 올립니다.


이유는 렌더를 먼저 보내고 쿠키가 필요한 부분만 나중에 채우기 위해서입니다. 동기 호출은 그 경계를 숨겨서 정적 페이지와 동적 페이지가 섞여 보였습니다. 지금은 Promise를 명시해 어떤 트리가 요청에 묶이는지 드러냅니다.


하이드레이션 미스매치와 다른 줄입니다. 여기 경고는 서버에서 요청 저장소를 읽는 방식 문제고, 화면 첫 그림이 다른 문제는 하이드레이션 글입니다.


경고 문구의 함수 이름 | cookies인지 headers인지 로그에 적혀 있습니다. 한 파일에서 둘 다 쓰면 각각 await가 필요합니다. 하나만 고치고 넘어가면 다음 빌드에 같은 줄이 남습니다.


Next.js 15에서 cookies와 headers가 Promise를 돌려 동기 호출이 경고를 내는 개념 이미지
요청 API는 이제 Promise라 await가 필요하다

넥스트 15에서 동기 호출이 깨지는 지점

서버 컴포넌트 본문, 라우트 핸들러, 서버 액션 세 곳이 많습니다. 클라이언트 컴포넌트에서 cookies()를 부르면 아예 다른 에러가 납니다. 그 경우는 이 글 대상이 아닙니다.


위치예전지금
서버 페이지cookies().get('sid')const c = await cookies(); c.get('sid')
라우트 핸들러headers().get('x-url')const h = await headers(); h.get('x-url')
draftModedraftMode().isEnabled(await draftMode()).isEnabled
searchParams props동기 객체await searchParams (버전별)

페이지 컴포넌트를 async function으로 바꾸면 await를 쓸 수 있습니다. 이미 async인데도 경고가 남으면, 유틸 함수 안에서 동기로 한 번 더 호출하는 경우가 많습니다. 그 유틸도 async로 올리고 호출부에 await를 달아야 합니다.


서버 컴포넌트와 라우트 핸들러에서 고치기

호출을 한 번 저장한 뒤 get 합니다. 매 줄마다 await cookies()를 반복할 필요는 없습니다. 같은 요청 안에서 한 번 await하면 됩니다.


await 후 get
import { cookies, headers } from 'next/headers' export default async function Page() { const jar = await cookies() const sid = jar.get('sid')?.value return <p>{sid ? '로그인됨' : '손님'}</p> } export async function GET() { const h = await headers() const origin = h.get('origin') || '' return Response.json({ origin }) }

쿠키를 쓰는 응답이면 CORS 헤더와 자격이 겹칩니다. 브라우저가 읽는 API라면 CORS 글과 같이 보세요. SameSite 속성이 막히면 값은 읽히는데 로그인이 풀립니다. 그 칸은 쿠키 SameSite 글입니다.


서버 컴포넌트에서 cookies를 await한 뒤 get으로 값을 읽는 코드 흐름 이미지
한 번 await해서 저장한 다음 get 한다

headers·draftMode도 같은 줄이다

세 API를 한 파일에서 쓰면 세 번 await 합니다. 미들웨어의 NextRequest 쿠키는 이 변경과 무관합니다. 미들웨어는 요청 객체의 req.cookies를 그대로 읽습니다.


라이브러리가 내부에서 동기 cookies()를 호출하면 내 코드는 고쳤는데도 경고가 남습니다. 그 패키지 버전을 넥스트 15 호환으로 올리거나, 해당 호출을 내 서버 코드로 옮겨야 합니다. 인증 헬퍼가 대표적이에요.


쿼리 문자열 훅과 혼동하지 마세요. useSearchParams는 클라이언트 훅이고 Suspense 경계가 필요합니다. 그 줄은 missing suspense 글입니다.


  • [ ] 경고에 적힌 함수 이름을 확인했다
  • [ ] 페이지·핸들러를 async로 바꿨다
  • [ ] await 결과를 변수에 담아 get 했다
  • [ ] 유틸 함수 안의 동기 호출도 올렸다
  • [ ] 미들웨어 req.cookies와 서버 cookies()를 섞지 않았다

클라이언트에서 cookies() 금지 | 'use client' 파일은 이 API를 못 부릅니다. 값은 서버에서 읽어 props로 넘기세요. 브라우저 쿠키가 필요하면 document.cookie가 아니라 서버 액션이나 라우트를 거치는 편이 안전합니다.


빌드는 되는데 타입이 불평할 때

타입은 Promise인데 런타임은 아직 호환 계층인 과도기가 있습니다. @typesnext 버전을 맞추면 경고와 타입이 같이 정리됩니다. 타입만 우회하려고 as any를 붙이면 다음 메이저에서 런타임이 깨집니다.


넥스트 설정에 동기 동적 API를 에러로 승격하는 플래그가 있으면, 개발에선 경고만 보이던 코드가 CI 빌드에서 실패합니다. 로컬에서 next build를 한 번 돌려 같은 파일을 찾으세요.


한국에서 넥스트 14 앱을 15로 올린 날, 인증 레이아웃의 cookies().get 한 줄에서 막히는 경우가 많습니다. 그 레이아웃만 async로 바꾸면 하위 페이지는 그대로 둘 수 있습니다.


Next.js 빌드가 동기 cookies 호출을 경고하고 async 서버 컴포넌트로 고치는 흐름 이미지
로컬 next build로 동기 호출 파일을 찾는다

참고 자료


내부 연계: useSearchParams Suspense, CORS, 쿠키 SameSite


인용한 동작은 2026년 9월 공개 문서 기준입니다.


자주 묻는 질문

동기 호출을 그대로 두면 어떻게 되나요?

지금은 호환 계층이 값을 줄 수 있습니다. 버전과 설정에 따라 경고에서 빌드 실패로 바뀝니다. await로 옮기는 편이 안전합니다.


클라이언트 컴포넌트에서 await cookies를 쓰면 되나요?

안 됩니다. 이 API는 서버 전용입니다. 서버에서 읽어 props로 넘기거나, 라우트 핸들러에서 응답하세요.


유틸에 cookies를 넣어 두었는데 경고가 남습니다.

유틸도 async로 바꾸고 호출부에서 await 해야 합니다. 페이지 한 곳만 고치면 유틸 안의 동기 호출이 그대로 남습니다.


미들웨어 쿠키도 await 하나요?

미들웨어는 NextRequest의 cookies를 씁니다. next/headers의 cookies()와 다른 객체입니다. 요청 객체에서 읽으면 됩니다.


searchParams도 Promise인가요?

넥스트 15 페이지 props는 searchParams와 params가 Promise일 수 있습니다. await로 받은 뒤 필드를 읽으세요. 클라이언트 useSearchParams와는 다른 칸입니다.


값이 undefined입니다. await를 빼먹은 건가요?

Promise를 객체처럼 get 하면 값이 없습니다. 콘솔에 Promise {}가 보이면 await가 빠진 겁니다. 그래도 없으면 쿠키 이름과 도메인·SameSite를 보세요.


이 경고는 요청 API가 Promise가 되었다는 뜻입니다. 함수를 async로 바꾸고 한 번 await한 뒤 get 하세요. 관련 글: useSearchParams, CORS, SameSite.


cookies()headers()await넥스트Next.js 15서버 컴포넌트draftMode프론트엔드쿠키개발자
EXPLORE / Frontend

이어서 읽어보기

전체 토픽 둘러보기