Next.js 15랑 React 19 올렸다가 새벽에 서버리스 비용 폭탄 맞을 뻔한 썰 (캐싱·쿠키 에러 3가지)

운영 중인 프로덕트 코드베이스를 Next.js 15와 React 19로 넘기면서 직접 머리 싸매고 겪었던 3가지 에러 로그다. 공식 문서만 훑고 넘어가면 프로덕션에서 무조건 터지는 포인트들만 정리했다.

1. fetch 캐시 기본값 변경에 따른 무한 동적 요청
Next.js 14까지는 fetch() 호출 시 강력한 데이터 캐싱(force-cache)이 기본이었으나, Next.js 15부터는 기본값이 no-store로 변경되었다.
공통 설정 조회 API가 매 요청마다 동적 평가를 유발해 서버리스 함수 실행 시간이 급증했다.
해결: 정적 공유 데이터는 { cache: 'force-cache', next: { revalidate: 3600 } }로 명시적 캐시 전략을 적용해야 한다.

2. useActionState 전환 시 폼 상태 리셋과 하이드레이션 불일치
React 19에서 useFormState가 deprecated되고 useActionState로 대체되었다.
initialState 타입과 서버 액션 반환 타입이 엄격히 일치하지 않으면 클라이언트 트리가 불필요하게 재평가되어 폼 에러 메시지가 즉시 리셋된다.
해결: [state, formAction, isPending] 튜플 반환값을 정확히 바인딩하고 초기 상태 객체 불변성을 유지해야 한다.

3. Server Actions 내부 cookies() Promise 미처리 버그
Next.js 15의 가장 큰 변화는 cookies(), headers()가 동기 객체에서 비동기 Promise로 바뀐 점이다.
기존처럼 cookies().get('token')을 동기 호출하면 런타임에서 TypeError: cookies(...).get is not a function이 발생한다.
해결: 반드시 const cookieStore = await cookies(); 로 await 처리 후 가져와야 한다.

요약:
- fetch 캐시: Next 15는 no-store가 기본이므로 캐시 필요 시 명시 지정.
- React 19 폼: useActionState 쓰고 초기 상태 불변성 유지.
- 쿠키/헤더: await cookies(), await headers() 비동기 필수 처리.