document.cookie로 읽던 값이 SSR에서 undefined였던 이유
문제 발생
테마 설정을 쿠키에 저장하고 SSR에서도 반영하려 했습니다.
const theme = document.cookie.match(/theme=(\w+)/)?.[1]; // 서버에서 터짐ReferenceError: document is not definedif (import.meta.client)로 감싸자 오류는 사라졌지만, 이번에는 서버 렌더에 테마가 반영되지 않아 화면이 한 번 깜빡였습니다.
원인 분석
document는 서버에 없습니다. SSR에서 쿠키는 브라우저 저장소가 아니라 들어온 요청의 헤더에 있고, 응답에 쿠키를 실으려면 Set-Cookie 헤더를 써야 합니다. 두 환경의 접근 방식이 다르기 때문에, 조건 분기로 한쪽을 꺼버리면 SSR 결과에서 그 값이 통째로 빠집니다.
Nuxt는 이 차이를 감춘 컴포저블을 제공합니다. 문서는 useCookie를 쿠키를 읽고 쓰는 SSR 친화적 컴포저블로 소개하며, 반환된 ref가 값의 JSON 직렬화/역직렬화까지 처리한다고 설명합니다. 그리고 ref를 갱신하면 쿠키가 갱신됩니다(readonly가 아닌 경우).
주의사항도 함께 있습니다 — useCookie는 Nuxt 컨텍스트에서만 동작하고, 쿠키가 외부에서 바뀐 경우에는 refreshCookie로 값을 수동으로 갱신해야 합니다.
해결 방안
- 읽기와 쓰기를 하나의 ref로 다룹니다.
const theme = useCookie<"light" | "dark">("theme", {
maxAge: 60 * 60 * 24 * 365,
sameSite: "lax",
path: "/",
});
theme.value = "dark"; // 쿠키까지 갱신된다-
인증 토큰은 화면 상태와 구분합니다. 세션 토큰은 서버에서
httpOnly로 심고, 클라이언트가 읽는 쿠키는 테마·언어처럼 노출돼도 되는 값만 담습니다. -
옵션을 상황에 맞게 고릅니다. 문서가 나열하는
maxAge,expires,httpOnly,secure,sameSite,readonly,watch중 필요한 것만 씁니다. 다만httpOnly쿠키는 클라이언트에서 읽을 수 없으므로 화면 상태용으로는 맞지 않습니다. -
서버 라우트에서 쿠키를 바꿨다면 새로 읽습니다. API 호출로 값이 바뀐 뒤에는
refreshCookie("theme")을 호출해야 화면의 ref가 최신이 됩니다. -
깜빡임(FOUC)을 실제로 확인합니다. 서버 렌더 HTML에 그 값이 이미 들어 있는지 보는 게 유일한 검증입니다 — JS를 끄고 페이지를 열어보면 바로 드러납니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.