Copilot이 그려준 next.js 쿠키
웹에서 사용하는 데이터 중에 DB에 저장하기엔 부적합하면서 전체적인 페이지에 영향을 미치는 데이터가 몇 가지 있다. 대표적으로 유저 테마나 토큰이 있는데 일반 React SPA에선 이를 Context와 Provider로 클라이언트에 저장해서 사용했었다. Next.js에서는 런타임 환경이 서버와 클라이언트로 분리되어 있으므로 Context만으론 불충분하다. 때문에 데이터를 서버에 공유하는 역할을 쿠키나 헤더가 수행하는데 개인적으로 생소하게 느껴져서 정리해보려한다.
먼저 Next.js의 서버와 클라이언트의 동작 방식을 이해해본다.
Next.js 프로젝트가 일반적으로 배포되는 Vercel 서비스를 기준으로 설명한다.
Next.js의 서버는 기본적으로 Serverless Function으로 작동한다. 보통 서버를 생각하면 24시간 돌아가면서 요청이 들어올 때 응답을 주는 방식을 떠올린다. Serverless Function은 평소엔 작동하지 않다가 요청이 들어오면 켜져 응답을 주고 일정 시간 뒤에 다시 꺼진다.
서버리스의 장점은 24시간 켜있을 필요가 없기에 비용이 절감된다는 점이다. 단점으로는 요청이 들어왔을 때 서버를 켜는 시간이 필요해서 응답시간이 늘어나는 점과 가동되는 기능에 용량 제한이 있다는 점, 응답간 상태를 저장할 수 없다는 점이 있다.
단점이 많아보이지만 백엔드처럼 무거운 계산을 하지않고 HTTP를 주로 생성하는 프론트엔드 서버로서는 SSR을 구성할 때 효율적인 방식으로 보인다.
서버리스의 런타임 환경은 기본적으론 Node.js
다. 추가 설정을 통해 가볍고 빠른 V8 엔진 환경의 함수를 만들 수도 있는데 이는 Edge Function이라고 한다. 서버를 켜는 시간이 매우 적은 대신 용량 제한이 작고 Node.js의 API를 일부만 사용할 수 있다.
클라이언트 환경은 SPA과 유사하다. 다만 페이지 이동 시에 자바스크립트를 실행해서 다음 페이지를 처음부터 렌더링하는 SPA와 다르게 일부 정적인 요소는 서버에 요청해서 받아온다. 때문에 Next.js에서 Link를 클릭하면 아래 이미지처럼 RSC(React Server Component)나 page 및 layout 파일 요청이 네트워크 탭에 나타난다.
응답에 데이터가 없는 경우도 있는데 캐시 동작과 관련된 걸로 예상한다.
위에서 언급한 것처럼 서버는 상태를 저장하지 못하기 때문에 데이터 공유는 클라이언트를 중심으로 이루어진다. 따라서 데이터를 브라우저에 저장하면서 서버로의 요청에 끼워보내기 용이한 방법이 필요하다. Next.js는 이 역할로 쿠키를 선택했다.
쿠키는 웹 서버의 요청으로 브라우저에 정보를 저장하는 기능이다.
서버는 HTTP 응답에 Set-Cookie
항목을 추가해서 브라우저에 쿠키 저장 지시를 내릴 수 있다. 저장하고 싶은 데이터를 key=value;
형태로 전해주면 된다.
저장된 쿠키는 서버로 요청을 보낼 때마다 자동으로 헤더에 담겨서 보내진다.
🔺 서버로 보내는 요청 헤더에 Cookie가 자동 삽입된 모습
Set-Cookie
에는 데이터 말고도 옵션을 지정해줄 수 있다. 옵션은 쿠키 별로 다르게 지정해줄 수 있다.
Domain
, Path
: 쿠키의 범위를 지정한다. 지정된 도메인이나 path로의 서버 요청에만 쿠키를 담아서 보낸다.Expires
, Max-Age
: 쿠키의 만료 시간을 날짜나 기간으로 지정한다. Secure
: HTTPS 요청에만 쿠키를 담을 수 있게 한다.HttpOnly
: 클라이언트 측 자바스크립트로 쿠키에 접근할 수 없게 한다. XSS의 방어수단이다. SameSite
: Domain
과 비슷하게 쿠키의 범위를 지정한다. 대신 약간의 유연성을 추가할 수 있다.
SameSite
옵션
Strict
: 프로토콜과 도메인이 같아야만 쿠키 허용Lax
(기본값): 사이트 간 subresource(이미지, 스크립트, 스타일시트 등) 요청엔 보내지지 않고, 링크 클릭과 같은 top-level navigation에서는 허용. 타 사이트로부터 링크로 이동하더라도 쿠키를 통해 정보를 받을 수 있으므로 유저가 더 편하게 사이트를 이용할 수 있다.
None
: 모든 요청에 허용
옵션을 통해 최소한의 보안을 챙길 수 있다. 단, 값을 숨기지는 못하니 민감한 데이터라면 암호화를 하거나 애초에 서버에서만 사용하도록 설계해야 한다.
Next.js app router는 쿠키 사용이 쉽도록 cookies
API를 제공한다.
유의할 점으로 쿠키를 설정하거나 삭제하는 메서드는 서버 컴포넌트에서 직접적으로 사용할 수는 없다. Route Handler나 Server Action, Middleware에서만 사용이 가능하다.
데이터 전달의 측면에선 커스텀 헤더를 HTTP 요청에 추가해서 보내는 것도 당연히 가능하다. 다만 커스텀 헤더는 일회성 정보를 전달할 때 사용하는 게 일반적인 것 같다.
데이터의 저장, 관리, 보안 기능이 추상화된 커스텀 헤더가 쿠키인 느낌이다.
웹 개발에 입문하고 소셜로그인을 구현할 때 레퍼런스들을 보면서 쿠키는 보안과 성능 이슈가 있는 구 시대적 기능이고 storage API를 쓰는 게 낫다 라는 정보를 봤던 것 같다. 때문에 깊게 공부안하고 있었는데, Next.js에서 쓸 일이 생기니 동작 원리를 몰라 혼란이 있었다. 서버에 쿠키가 왜 있다는 건가 싶었는데 이제 이해가 간다.