게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌5 - HTTP - 6년 전 등록 / 3년 전 수정

알아둬야 할 HTTP 쿠키 & 캐시 헤더

안녕하세요. 이번 시간에는 쿠키 & 캐시 전용 헤더만 따로 알아보겠습니다. 웹 자원을 효율적으로 쓰기 위해서는 캐싱이 중요합니다. 똑같은 데이터를 계속해서 내려 받을 필요는 없죠. 쿠키는 클라이언트(프론트)와 서버 간에 데이터를 주고받는 가장 간단한 방법 중 하나입니다. 이런 것들에 대한 설정을 헤더를 통해 할 수 있습니다. 쿠키나 캐시에 대한 정보는 개발자 도구(크롬 기준)의 Application 탭에서 쉽게 확인할 수 있습니다.

undefined

캐시

여기서 말하는 캐시는 개인 캐시를 뜻합니다. CDN같은 공유 캐시가 아니라요. 여러분의 브라우저에 응답으로 온 HTML이나 JSON같은 데이터가 저장되어 나중에 서버에 요청을 보내지 않고도 브라우저에 저장된 응답을 사용할 수 있습니다.

보통 캐싱은 GET 요청에만 합니다. GET이 REST적의미로 가져오다이기 때문에, 가져온 데이터를 저장해두고 두고두고 쓰는 것이죠. 다른 요청 메서드를 캐싱하는 것을 잘 보지 못했습니다. 복잡한 경우는 제외하고요. 일반적으로 200(가져오기 성공), 301(다른 주소로 이동 후 가져옴), 404(가져올 게 없음) 상태 코드로 온 응답을 캐싱할 수 있습니다.

Cache-Control

많은 옵션들이 있지만, 자주 쓰이는 옵션만 알아봅니다.

먼저 아무것도 캐싱하지 않으려면

Cache-Control: no-store

를 하면됩니다. 또는 no-cache, no-store, must-revalidate로 no 시리즈를 다 붙여줍니다.

Cache-Control: no-cache

는 가장 많이 헷갈려하는 헤더 설정인데요. no-cache이지만 cache하지 말라는 뜻이 아닙니다!!! 모든 캐시를 쓰기 전에 서버에 이 캐시 진짜 써도 되냐고 물어보라는 뜻입니다.

Cache-Control: must-revalidate

must-revalidate는 만료된 캐시만 서버에 확인을 받도록 하는 겁니다. no-cache랑 must-revalidate는 이름이 잘못 지어진 감이 있습니다.

Cache-Control: public 또는 private

public이면 공유 캐시(또는 중개 서버)에 저장해도 된다는 뜻이고 private이면 브라우저같은 특정 사용자 환경에만 저장하라는 뜻입니다.

Cache-Control: public, max-age=3600

max-age로 캐시 유효시간을 줄 수 있습니다. 초 단위이므로 위 예제에서는 1시간입니다. 1시간이 지나면 이 응답 캐시는 만료된 것으로 여겨집니다.

참고로 위의 옵션들은 혼합해서 써도 됩니다. no-store, no-cache, must-revalidate처럼 콤마로만 구분하면 되고요. 

Cache-Control을 응답 헤더라고 생각하실 수도 있는데, 요청 헤더로도 사용할 수 있습니다. 프론트 - 중개 서버 - 진짜 서버와 같은 구조인 경우에 중개 서버에 있는 캐시를 가져오지 않도록 하려면 요청 시부터 Cache-Control을 헤더로 넣어주곤 합니다.

Age

Age 헤더는 캐시 응답 때 나타나는데, max-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려줍니다. 위 예제에서 max-age= 3600을 설정한 경우, 1분이 지나면

Age: 60

이 캐시 응답 헤더에 포함됩니다.

Expires

Cache-Control과 별개로 응답에 Expires라는 헤더를 줄 수도 있습니다.

Expires: Thu, 26 Jul 2018 07:28:00 GMT

응답 컨텐츠가 언제 만료되는지를 나타내며, Cache-Control의 max-age가 있는 경우 이 헤더는 무시됩니다.

ETag

HTTP 컨텐츠가 바뀌었는지를 검사할 수 있는 태그입니다. 같은 주소의 자원이더라도 컨텐츠가 달라졌다면 ETag가 다릅니다. 예를 하나 들어봅시다.

GET www.zerocho.com의 응답 본문이 안녕 제로초이고 ETag 헤더 값이 12345라 칩시다. 만약 서버 컨텐츠(응답 본문)가 동일하다면 매번 GET www.zerocho.com을 할 때마다 ETag는 12345입니다. 그런데 안녕 제로초에서 안녕 이태그로 컨텐츠가 바뀌었다면 ETag 헤더 값도 34567로 바뀝니다. 그러면 서버가 클라이언트의 응답 내용이 달라졌구나를 깨닫게 되어 캐시를 지우고 새로 컨텐츠를 내려받을 수 있게 됩니다.

Etag: W/"3bf2-wdj3VvN8/CvXVgafkI30/TyczHk"

If-None-Match

서버보고 ETag가 달라졌는 지 검사해서 ETag가 다를 경우에만 컨텐츠를 새로 내려주라는 뜻입니다.

If-None-Match: W/"3bf2-wdj3VvN8/CvXVgafkI30/TyczHk"

만약 ETag가 같다면 서버는 304 Not Modified를 응답해서 캐시를 그대로 사용하게 합니다.

쿠키

쿠키는 브라우저에 저장되는 작은 데이터 조각으로, 임시 데이터 보관 또는 웹페이지 개인화 등에 사용됩니다. 쿠키를 주기적으로 지우지 않으면 브라우저에 엄청나게 많은 쿠키들이 쌓여 있는 것을 보게 되실텐데 이것들이 여러분을 추적하고 있는 것입니다.

Set-Cookie

서버에서 클라이언트(브라우저)한테 이런 이런 쿠키를 저장하라고 명령하는 응답 헤더입니다.

Set-Cookie: 키=값; 옵션들

Set-Cookie: hello=zero면 hello라는 키에 값을 zero로 해서 보낼 수 있는거죠. 옵션들도 몇 개 알려드리겠습니다.

  • Expires: 쿠키 만료 날짜를 알려줄 수 있습니다.
  • Max-Age: 쿠키 수명을 알려줄 수 있습니다. Expires는 무시됩니다.
  • Secure: https에서만 쿠키가 전송됩니다.
  • HttpOnly: 자바스크립트에서 쿠키에 접근할 수 없습니다. XSS 요청을 막으려면 활성화해두는 것이 좋습니다.
  • Domain: 도메인을 적어주면 도메인이 일치하는 요청에서만 쿠키가 전송됩니다. 가끔 도메인이 다른 쿠키들이 있는데, 이런 쿠키들은 써드 파티 쿠키로 여러분을 추적하고 있는 쿠키입니다. 구글이나 페이스북같은 곳이 써드 파티 쿠키를 적극적으로 사용합니다.
  • Path: 패스를 적어주면 이 패스와 일치하는 요청 요청에서만 쿠키가 전송됩니다.

예를 들면 다음과 같이 가능합니다.

Set-Cookie: zerocho=babo; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

쿠키는 XSS 공격과 CSRF 공격 등에 취약하기 때문에 HttpOnly 옵션을 켜두고, 쿠키를 사용하는 요청은 서버 단에서 검증하는 로직을 꼭 마련해두는 것이 좋습니다.

Cookie

반대로 클라이언트가 서버한테 쿠키를 보내줄 때는 이 요청 헤더에 담아 보냅니다.

Cookie: 키=값; 키=값;

서버는 이 쿠키 헤더를 파싱해서 사용하게 됩니다. 아까도 말했듯 CSRF 공격같은 것을 막기 위해서 반드시 서버는 쿠키가 제대로 된 상황에서 온 것인지 확인하는 로직을 갖춰야 합니다.

이렇게 캐시와 쿠키에 관련된 헤더를 알아봤습니다. 보통은 서버가 알아서 세팅하는 캐시와 쿠키를 사용하지만, 여러분이 수정해야할 때도 있습니다. 그 때 참조하시면 될 것 같습니다.

다음 시간에는 X로 시작하는 헤더들에 대해 알려드리겠습니다!

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

9개의 댓글이 있습니다.
3년 전
친절한 설명 감사합니다 mdn보고 이해 안됐는데 덕분에 이해가 됐어요!
3년 전
(본문의 내용 중)


... 그러면 `서버가 클라이언트가` 이 응답 내용이 달라졌구나를 깨닫게 되어 캐시를 지우고 새로 컨텐츠를 내려받 ...


주어가 사라졌습니다. 서버랑 클라이언트 둘다 주어가 되고 싶었나봐요 ㅠㅠ
3년 전
Age
Age 헤더는 캐시 응답 때 나타나는데, max-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려줍니다. 위 예제에서 max-age= 3600을 설정한 경우, 1분이 지나면

Age: 60
이 캐시 응답 헤더에 포함됩니다.


님꺼 사이트 지금 이페이지 브라우저 도구열고 보면

max-age= 3600 으로 응답헤더에 담겨오는 요청에 대해서 관찰해보는데

시간이 지나고 새로고침을 하여도 age 헤더값은 나타나지 않는데요?
3년 전
네트워크 탭에서 size가 disk cache인 것 중에 age가 들어 있는 게 있습니다.
3년 전
써드파티 쿠키는 처음알았네요.. 아직 전 부족한것 같네요. Http 완벽 가이드라는 책을 추천해주시는것 같은데 그책을 한번 읽어봐야겠네요
4년 전
제일 최근 익명님 댓글대로라면 제로초님은 모두 서버측 RES 헤더를 말하신거고 저 제일 처음 댓글러는 클라이언트측 REQ에 헤더를 말한건가요? 그렇다면 내용 중 If-None-Match 는 내용만 보면 클라이언트 REQ 헤더 같은데.. 구분이 잘 안되네요..
4년 전
혹시 이 페이지에 오셔서 댓글보고 헷갈리는 분이 계실까봐.. no-cache 정책에 대한 설명은 ZeroCho님 말씀이 맞습니다.
익명분은 1) cache-control 헤더를 서버가 붙일때와 클라이언트가 붙일때의 동작이 다른데 헷갈리고 있음 2) 캐시가 유효한지 확인하는 과정 자체가 한번 요청이 발생하니 그건 캐시가 아니라고 주장하는데 본인 생각으로 보임
5년 전
와우;; 캐싱에 대해 보고있다가 두 분의 열띤 토론보고 겁먹었습니다.. ㅠ 초보인 저는 직접해봐서 이해를 해봐야겠습니다.. 좋은 글 감사드립니다!
6년 전
헤더 전송시 쿠키가 잇으면 같이 전송되는데 formdata형식으로 이미지업로드 하는 axois post에선 헤더를 까봐도 쿠키가 안오는데 어떻게 해야되나요 ??
6년 전
글 잘 보았습니다! 궁굼한것이 있는데요~ 클라이언트 request 헤더에 `cache-control: no-cache`를 담아 서버에 전달할경우 서버에서 Etag나 cache-control(또는 expires)유효기간과 관계없이 캐싱되지 않은 응답(200 OK)을 내려주는것으로 알고있습니다. 게시글에서 말하시는 캐시를 써도 되냐?라고 물어본다는 것이 어떤것인지 궁굼합니다~
6년 전
no-cache는 무조건 캐싱되지 않은 응답을 내려주는 것이 아니라, 서버에 캐시 가져다 써도 되냐를 물어봐서, 가져다 써도 된다고 하면 캐시를 쓰고, 안 된다고 하면 새로 컨텐츠를 내려받는 옵션입니다.
6년 전
@ZeroCho request `cache-control: no-cache`에 대해서 어디서 참고하신 내용인지 알 수 있을까요? https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32(RFC2616 14.32)에 보시면 아시겠지만 no-cache directive가 포함된 경우 복사본(캐시된)을 가지고 있더라도 요청을 원서버(origin)으로 전달해야 한다. 라고 되어있고 https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Cache-Control에도 유사한 내용이있는데 캐싱을 끄기 위해서 cache-control: no-cache를 사용하는 예제가 있네요~
6년 전
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 "14.9.3 Modifications of the Basic Expiration Mechanism"보시면 HTTP1.0에서 cache-control을 지원하지 않기때문에 HTTP/1.0에서 구현하는 방법에 대해 설명하는데요~ HTTP/1.0에서는 expires 값을 응답시간보다 작게 설정(=캐시만료)하여 no-cache와 "동일하게 처리한다"고 되어있습니다~
6년 전
rfc2616은 20년 전 스펙입니다. 최신 스펙을 보세요... 그리고 HTTP1.0도 죽은지 오래입니다
6년 전
HTTP 1.0도 현재도 쓰고있고 위에서 말한 cache-control 관련된 스펙은 변경된 적없는것으로 알고 있는데 참고하신 자료가 있다면 공유 부탁드립니다~ 20년된 스펙이라고 말씀하셨는데 2616이 obsolate되고 여러개의 문서로 분할되어 각 기능들이 자세하게 서술된것이지 스펙 자체가 크게 변경된게 아닙니다..

위에서 HTTP/1.0을 언급한것은 no-cache가 어떻게 사용되는지를 말씀드리려고 하는것입니다. 말씀하신대로 서버에 캐시를 써도 되는지 질의를 하는 용도라면 위에서 설명한 스펙이 완전 변경 되었다고 봐야하는데 관련된 설명이 없는것 같습니다. 참고하신 문서 또는 RFC 문서 공유 부탁드립니다~
6년 전
https://tools.ietf.org/html/rfc7234#section-4.2.2 보면 RFC2616 13.9섹션 언급과 함께 캐싱을 하지못하게(preclude)하려면 `cache-control: no-cache`를 명시하라고 되어있습니다. 참고하세요~
6년 전
아뇨. 왜 자꾸 다른 섹션을 보시는 지를 모르겠네요. no-cache의 정의를 보시면 If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent caching even by caches that have been configured to return stale responses to client requests.
이게 끝입니다. 구글, MDN, MSDN에서도 모두 no-cache는 캐싱하지말라 이 뜻이 아니라고 밝히고 있고, 실제로 브라우저에서 뒤로가기 같은 거 해보시면 캐시된 거 사용합니다.
6년 전
without successful revalidation with the origin server. 이 부분을 보시면 아시겠지만, successful revalidation with the origin server이 있다면 cache를 사용해도 됩니다. cache를 쓰는 경우 no-cache에서도 304 Not Modified가 나옵니다.
6년 전
그리고 no-cache 설명 바로 밑에 Note: Most HTTP/1.0 caches will not recognize or obey this directive.가 되어있는데 왜 HTTP/1.0을 언급하시는지도 모르겠네요. Pragma도 deprecated된 헤더고요. 그리고 스펙 해석을 하실 때 should랑 must도 구분하셔야겠습니다. should는 반드시라는 뜻이 아닙니다.
6년 전
덕분에 스펙도 다시 읽어보고 공부하는 시간을 가졌습니다. 또 다른 질문이 있다면 언제든지 하셔도 됩니다~
6년 전
뭔가 예시를 들어 설명을 해드리려 해도 이해를 잘 못하시는것 같은데요, 그냥 아파치같은 웹서버를 직접 설정해보시거나 소스코드를 참고하시면 도움이 되실것 같습니다~ "no-cache가 어디에도 캐싱 하지 말라는 뜻이 `아니다`"라고 나와있지 않습니다.

영어 독해 능력이 얼마나 뛰어나신지는 모르겠지만 위에 적어주신 내용 해석해 보시면 캐싱을 방지하는(prevent)것에 대한 내용입니다. 그리고 should, must가 왜 나왔는지는 모르겠으나 둘 다 ~해야한다는 의미를 갖습니다.

번역기 돌려서보시는건지 어떤건지 모르겠지만 origin에 요청을 전달한다는 뜻은 캐싱을 하지 않는다는것과 같은 의미입니다. 캐싱이되면 origin에 도달하기 전에 304 status code가 떨어집니다.

본인 생각이 확고한것은 좋으나 정확한 자료와 근거를가지고 남을 헐뜯는 말투는 지양하셨으면 좋겠습니다. 좋은 개발자 되시길 바랍니다.
6년 전
제 생각이 아니라 질문자분을 제외한 모두가 그렇게 설명하고 있습니다(구글개발자문서, MSDN, MDN 보세요) no-cache는 캐시가 있어도 서버에 확인 요청을 보내서, 서버가 새로운 응답 써야한다고 하면 그걸 받아서 쓰고, 서버가 캐시 써도 된다고 응답하면 캐시 씁니다. 그 때 304 떨어지고요. should는 스펙 초반부에 권장된다라고 정확하게 써있습니다. 해야한다라는 의미랑 전혀 다르고요. 반대로 질문자분이 직접 서버 구성해서 no-cache 해서 해보세요. 어떤 결과가 나오는지.