2026. 7. 18.
Next.js cookies().get이 Promise라면 어디에서 읽고 써야 할까?
Next.js cookies().get에서 Promise 타입 오류가 날 때 읽기는 await한 store에서 처리하고, 쓰기는 Server Function이나 Route Handler로 옮겨 검증하는 순서다.

const theme = cookies().get('theme')에서 Property 'get' does not exist on type 'Promise<ReadonlyRequestCookies>'가 뜨면 get의 타입을 억지로 바꿀 문제가 아니다. cookies()를 먼저 await해 cookie store를 얻어야 한다. 쿠키를 저장하려는 코드라면 한 단계가 더 필요하다. 렌더링 중인 Server Component가 아니라 Server Function 또는 Route Handler로 옮겨야 한다.
먼저 오류가 난 파일이 쿠키를 읽기만 하는지, 값을 쓰거나 지우는지 표시한다. 읽기라면 await가 첫 수정이다. 쓰기라면 await와 코드 위치를 함께 바꾼다. 이 두 판단을 섞지 않으면 타입 단언이나 임시 우회 없이 오류를 좁힐 수 있다.
get은 await한 store에서, set은 서버 응답 경계에서 호출한다
Next.js cookies 공식 문서는 cookies를 Promise를 반환하는 비동기 함수로 설명한다. Next.js 16 업그레이드 문서에 따르면 동기 Request API 접근은 제거됐다. 예전 예제의 cookies().get()을 그대로 옮기면 get은 Promise에 없는 속성이라는 타입 오류가 난다.
오류를 고칠 때는 먼저 아래 두 줄을 기준으로 나눈다.
| 하려는 일 | 첫 수정 | 실행 위치 |
|---|---|---|
| 쿠키 읽기 | const cookieStore = await cookies() | Server Component, Server Function, Route Handler |
| 쿠키 쓰기·삭제 | store를 await한 뒤 set·delete | Server Function 또는 Route Handler |
읽기 코드는 아래처럼 두 줄로 나눈다.
import { cookies } from 'next/headers'
export default async function ThemeLabel() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')?.value ?? 'system'
return <p>현재 테마: {theme}</p>
}
await를 쓰려면 이 Server Component도 async 함수여야 한다. 반대로 파일 상단에 'use client'가 있다면 바로 async 컴포넌트로 바꾸지 않는다. 쿠키 읽기를 서버 쪽으로 올리거나 값을 prop으로 내려야 한다. 서버와 클라이언트 경계가 함께 흔들린다면 Next.js params Promise 오류에서 Server와 Client를 나누는 순서처럼 먼저 파일의 실행 위치부터 표시한다.
동기 접근용 타입 단언은 임시 호환 수단일 뿐이다. 현재 코드의 기본 해법은 await cookies()로 store를 명시적으로 얻는 것이다.
쿠키 읽기와 쓰기는 같은 API라도 위치가 다르다
await를 붙여 타입 오류가 사라져도 Server Component 렌더링 중 set이나 delete를 호출하면 목적에 맞지 않는다. 브라우저에 쿠키를 저장하려면 서버가 응답의 Set-Cookie 헤더를 보내야 한다. 이미 렌더링과 스트리밍이 시작된 뒤에는 이 응답 헤더를 추가할 수 없다.
Server Component에서는 요청 쿠키를 읽는다
Server Component는 브라우저가 요청 헤더로 보낸 쿠키를 읽을 수 있다. 테마, 언어 같은 값을 기준으로 서버 화면을 만들 때는 앞의 await cookies() 패턴이면 된다.
Server Function에서는 사용자 행동 뒤 값을 쓴다
폼 제출이나 버튼 동작 뒤 쿠키를 바꾼다면 별도 서버 함수에 둔다. 예제 값은 인증 토큰이 아닌 화면 설정이다.
'use server'
import { cookies } from 'next/headers'
export async function saveTheme(formData: FormData) {
const nextTheme = String(formData.get('theme') ?? 'system')
const cookieStore = await cookies()
cookieStore.set('theme', nextTheme, {
httpOnly: true,
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
path: '/',
})
}
Server Function은 파일 상단이나 함수 안에 'use server' 지시문이 있어야 한다. 'use client'의 역할이 아직 헷갈린다면 Next.js 컴포넌트 경계를 use client로 확인하는 글에서 가져오기 방향과 이벤트 경계를 먼저 점검할 수 있다.
Route Handler에서는 응답과 함께 값을 쓴다
API 요청의 결과로 쿠키를 바꾼다면 app/api/.../route.ts가 맞다.
import { cookies } from 'next/headers'
export async function POST() {
const cookieStore = await cookies()
cookieStore.set('onboarding', 'done', {
httpOnly: true,
sameSite: 'lax',
path: '/',
})
return Response.json({ ok: true })
}
판단 기준은 짧다. 화면을 만드는 중에는 읽고, 사용자 요청에 대한 서버 응답을 만드는 경계에서 쓴다.
수정 결과는 세 곳에서 확인한다
코드 모양만 바뀌었다고 끝내지 않는다. 타입, 응답, 브라우저 저장 상태를 순서대로 확인한다.
1. typecheck에서 Promise 오류가 사라졌는지 본다
프로젝트에 정의된 타입 검사 명령을 실행한다.
npm run typecheck
별도 스크립트가 없다면 프로젝트의 빌드나 TypeScript 검사 명령을 사용한다. 여기서 같은 오류가 다른 호출부에 남는지 파일 경로와 첫 오류 줄을 확인한다.
2. Network에서 응답의 Set-Cookie를 확인한다
쿠키를 쓰는 폼이나 API 요청을 한 번 실행한다. 브라우저 개발자 도구의 Network에서 해당 요청을 열고 Response Headers를 확인한다. Set-Cookie가 없다면 Server Function이나 Route Handler가 실제로 호출됐는지부터 추적한다.
프런트엔드 코드에서 response.headers.get('set-cookie')가 null이라고 바로 실패로 판단하면 안 된다. MDN의 Set-Cookie 헤더 문서는 브라우저가 이 헤더를 프런트엔드 JavaScript에 노출하지 않는다고 설명한다.
3. Application에서 저장 범위를 확인한다
개발자 도구의 Application 또는 Storage에서 이름, Domain, Path, SameSite, Secure 값을 본다. 로컬 HTTP 환경에서 secure: true를 강제했거나 Path가 현재 주소와 다르면 코드가 실행돼도 기대한 요청에 쿠키가 붙지 않을 수 있다.
세 검사가 모두 맞으면 확인 가능한 결과는 다음과 같다.
- typecheck에
Promise<ReadonlyRequestCookies>의get오류가 없다. - 쿠키 변경 요청의 응답에 의도한
Set-Cookie가 있다. - 브라우저 저장소에서 이름과 범위가 의도와 일치한다.
호출부가 많다면 codemod 뒤 수동으로 경계를 확인한다
cookies, headers, draftMode 호출이 여러 파일에 퍼져 있다면 Next.js의 async Request API codemod를 먼저 검토할 수 있다.
npx @next/codemod@latest next-async-request-api .
Codemod가 모든 위치의 의도를 판단해 주는 것은 아니다. 변환 뒤 diff를 읽고, Client Component로 잘못 퍼진 호출과 렌더링 중 mutation이 남지 않았는지 확인한다. 타입 단언으로 오류만 숨긴 호출부는 수동 검토 대상으로 남긴다.
세션 쿠키와 인증 정책을 설계하는 일은 이 오류 수정의 범위를 넘는다. 실제 로그인 쿠키라면 값 암호화, 만료, HttpOnly, Secure, SameSite, 권한 검증을 별도 보안 설계로 다뤄야 한다. 지금 할 일은 오류가 난 호출 하나를 읽기 또는 쓰기로 분류하고, await와 서버 위치를 고친 뒤 세 검사를 끝내는 것이다.
참고 출처
- Functions: cookies (Next.js)
- Upgrading: Version 16 (Next.js)
- Guides: Data Security (Next.js)
- File-system conventions: route.js (Next.js)
- Upgrading: Codemods (Next.js)
- Set-Cookie header (MDN)
이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.
다음으로 읽을 기사
같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.
첫 번째 댓글을 남겨보세요
여러분의 생각이 다른 독자에게 도움이 됩니다.
댓글 0
이 글을 읽은 독자들의 생각을 나눠보세요.