네트워크 스터디 2주차 - 쿠키, 세션, 토큰, 캐시

Jiwon An·2021년 9월 23일
2

CS/네트워크

목록 보기
4/10

1. 쿠키와 세션을 왜 사용할까?

  • 서버-클라이언트 모델을 따르는 HTTP 프로토콜의 특징이자 약점을 보완하기 위해 사용한다.
  • HTTP 프로토콜 환경에서 서버는 클라이언트가 누구인지 확인해야한다. 그 이유는 HTTP 프로토콜이 Connectionless, stateless한 특성이 있기 때문이다.

서버-클라이언트는 통신을 할 때마다 클라이언트를 인증해야만 한다.
그 이유는 아래의 2가지 특성 때문이다.

  • Connectionless(비연결지향) : 클라이언트가 서버에게 요청을 한 후 그에 맞는 응답을 받으면 그 연결을 끊는 특징이다.
    HTTP는 먼저 클라이언트가 요청을 서버에 보내면, 서버는 클라이언트에게 요청에 맞는 응답을 보내고 접속을 끊는 특성이 있다.
  • Stateless(상태정보유지 X) : 연결을 끊는 순간 통신이 끝나며 상태 정보는 유지하지 않는 특징
    비연결지향의 특성 때문에 계속해서 통신 연결을 유지하지 않아 리소스 낭비가 줄어드는 장점(특히 불특정 다수를 대상으로 할 때)이 있는 동시에 통신할 때마다 새로 커넥션을 열기 때문에 클라이언트는 인증을 계속해야하는 단점이 생긴다.

여기서 쿠키와 세션이 보완하는 역할을 하게 된다.

2. 쿠키(Cookie)

  • 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일로, 브라우저에서 관리된다.
  • 서버가 사용자의 웹 브라우저에 저장하는 데이터로, Key:Value 형태로 저장되는 데이터 파일이다.
  • 사용자 인증이 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지된다는 특징이 있다.
  • 쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참조한다.
  • 요청 헤더에 Set-Cookie 속성을 사용해 정보를 담으면 클라이언트에 쿠키를 만들 수 있다.
  • 쿠키는 사용자가 따로 요청하지 않아도 브라우저가 요청시에 요청 헤더를 넣어서 자동으로 서버에 전송한다.
    브라우저가 서버와 연결 되었을 때 브라우저에서 자동적으로 쿠키를 생성하고, 응답할 때 쿠키를 담아서 보낸다.
  • 특정 호스트에서 생성된 쿠키는 이후 모든 요청마다 서버로 전송된다.
  • 클라이언트에 300개까지 쿠키저장 가능, 하나의 도메인당 20개의 값만 가질 수 있음, 하나의 쿠키값은 4KB까지 저장한다.

2.1 쿠키의 구성 요소

  • 이름 : 각각의 쿠키를 구별하는 데 사용되는 이름
  • 값 : 쿠키의 이름과 관련된 값
  • 유효시간 : 쿠키의 유지시간
  • 도메인 : 쿠키를 전송할 도메인
  • 경로 : 쿠키를 전송할 요청 경로

2.2 쿠키 동작 순서

1) 클라이언트가 페이지를 요청한다.
2) 서버에서 쿠키를 생성해서 정보를 담아 클라이언트에게 응답한다.

  • HTTP 헤더에 쿠키를 포함시켜 응답한다.

3) 이후 클라이언트가 쿠키를 로컬에 가지고 있다가 다시 서버에 요청할 때 준다.

  • ex) 동일 사이트 방문시에 쿠키가 있는 경우

2.3 쿠키 사용 예

  • 로그인 시 “아이디와 비밀번호를 저장하시겠습니까?
  • 쇼핑몰 장바구니
  • 자동로그인
  • 팝업에서 “오늘 더 이상 이 창을 보지 않음" 체크

2.4 단점

  • 보안에 취약하다.
    • 요청 시 쿠키의 값을 그대로 보낸다.
    • 유출 및 조작 당할 위험이 존재한다.
  • 쿠키에는 용량 제한이 있어 많은 정보를 담을 수 없다.
  • 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유가 불가능하다.
  • 쿠키의 사이즈가 커질수록 네트워크에 부하가 심해진다.

3. 세션 (Session)

  • 세션은 쿠키를 기반으로 하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 저장하고 관리한다.
    • 세션 스토리지에 세션 정보를 저장한다.
    • 쿠키를 통해 클라이언트 로그인 상태를 유지시킬 수 있지만, 가장 큰 단점은 쿠키가 유출 및 조작 당할 위험이 존재한다. 개인정보를 HTTP로 주고 받는 것은 위험하다.
  • 서버에서는 클라이언트를 구분하기 위해 유일한 값인 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지한다.
    • 클라이언트가 request를 보내면, 해당 서버의 엔진이 클라이언트에게 세션 ID를 부여하여 클라이언트를 구별한다.
    • 클라이언트는 이 세션 ID를 쿠키를 통해 기억한다.
    • 이후 클라이언트가 어떤 요청을 보낼 때마다 헤더의 쿠키에 세션 ID를 담아서 전송한다.
  • 서버는 클라이언트가 보낸 요청의 쿠키에 담긴 세션 ID와 세션 스토리지에 담긴 세션 ID를 대조해 인증 상태를 판단한다.
    (즉, 세션과 쿠키는 완전히 분리된 개념이 아니며 세션은 쿠키를 기반으로 함)
  • 각 클라이언트마다 유니크한 세션 객체가 주어지고, 이 세션 객체에 데이터를 담아 관리할 수도 있다.
    (세션 객체가 자물쇠로 잠긴 상자라면 세션 ID가 열쇠인 셈이다.)
  • 세션을 사용하지 않고 쿠키만으로 어떤 데이터를 주고받는다면, 클라이언트는 이미 모든 데이터를 알고 있다는 것이다.
  • 접속 시간에 제한을 두어 일정 시간 응답이 없다면 정보가 유지되지 않게 설정이 가능하다.
  • 사용자에 대한 정보를 서버에 두기 때문에 쿠키보다 보안에 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 된다.
  • 즉, 동접자 수가 많은 웹사이트인 경우 서버에 과부하를 주게 되므로 성능 저하의 요인이 된다.
  • 일정 시간동안 같은 브라우저가 보내는 일련의 요청을 하나의 상태로 보고 그 상태를 유지한다. 즉, 브라우저를 종료할 때까지 유지되는 상태이다.

ex)

HTTP/1.1 200
Set-Cookie: JSESSIONID=FDB5E30BF20045E8A9AAFC788383680C;

3.1 세션/쿠키 기반 인증 방식 (서버 기반 인증)

서버의 세션과 사용자의 쿠키를 기반으로 하는 인증 방식이다.

1. 클라이언트가 로그인을 위해 해당 정보를 서버에 전달한다.
2. 서버는 로그인 성공시 사용자를 식별할 수 있는 고유한 세션 ID(JSESSIONID)를 생성하고 이를 클라이언트에 전달한다.
3. 클라이언트는 전달받은 세션 ID(JSESSIONID)를 쿠키에 저장하고 이후 서버에 요청시 쿠키를 실어서 전달한다.

  • 요청의 헤더에 있는 쿠키를 확인하여 세션 ID를 통해 클라이언트를 구별한다.
  1. 서버는 쿠키를 받아서 세션 ID(JSESSIONID)가 유효한지 확인한 이후, 그것을 키로 활용하여 클라이언트를 구분해서 클라이언트의 요청을 처리한다.
    • 세션 ID가 없을 경우, 생성 후 클라이언트에게 전달한다.

3.2 사용 예

로그인 같이 보안상 중요한 작업을 수행할 때 사용

3.3 장단점

  • 쿠키를 포함한 요청이 외부에 노출되더라도 세션 ID 자체는 유의미한 개인정보를 담고 있지 않다.
    • 그러나 해커가 이를 중간에 탈취해 클라이언트인척 위장할 수 있다는 한계가 존재한다.
  • 각 사용자마다 고유한 세션ID가 발급되기 때문에, 요청이 들어올 때마다 회원정보를 확인할 필요가 없다.
  • 서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 심해진다.

3.4 쿠키와 세션 차이

3.5 왜 같이 써야 할까?

세션이 보안성이 좋다고 무분별하게 만들면 서버의 자원에 한계가 있기 때문에 메모리 문제가 발생한다. 각각의 장단점을 활용하여 같이 사용할 때 최고의 성능을 낼 수 있다.

4. 토큰

  • 인증을 위해 사용되는 암호화된 문자열이다.
  • 사용자가 인증에 성공하면 서버는 토큰을 생성해서 클라이언트로 보낸다.
  • 토큰도 세션과 마찬가지로 사용자가 보내는 요청에 포함이 된다.
  • 세션 인증에서는 서버가 세션 ID를 저장하고 클라이언트가 쿠키에 실어 보낸 세션 ID와 대조해서 확인하는 반면, 토큰을 사용하면 요청을 받은 서버는 토큰이 유효한 지를 확인만 한다. HTTP 통신의 Stateless 한 성격과 더 적합한 인증 방식이다.
  • 세션 인증에 비해 서버 운영의 효율이 더 좋다.

4.1 JWT 기반 인증

  • JWT(Json Web Token)이란 인증에 필요한 정보들을 암호화시킨 토큰을 의미한다.
  • JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별한다.

4.2 JWT 구조

  • header : JWT인 토큰의 유형이나 HMAC SHA256 또는 RSA와 같이 사용되는 해시 알고리즘 중 무엇으로 사용했는 지 등의 정보가 담긴다.
  • payload : 토큰에 담을 정보, 클라이언트에 대한 정보나 메타 데이터 같은 내용이 들어있다.
  • signature : 헤더에서 지정한 알고리즘과 secret 키, 서명으로 payload와 header를 담는다.
  • JWT의 경우 자체 내에 정보를 가지고 있기 때문에 클라이언트가 해독해 정보를 볼 수 있다. 따라서 보안상 민감한 정보는 payload에 담아서는 안된다.
    단, 토큰 정보를 수정하기 위해서는 secret 키를 알고 있어야 한다.

ex)

  • alg과 typ는 각각 정보를 암호화할 해싱 알고리즘 및 토큰 타입을 지정한다.

Payload

  • 토큰에 담을 정보를 가지고 있다. 주로 클라이언트의 고유 ID값 및 유효 기간 등이 포함되는 영역이다.
  • key-value 형식으로 이루어진 한 쌍의 정보를 Claim이라고 칭한다.

Signature

  • 인코딩된 Header와 Payload를 더한 뒤 secret 키로 해싱하여 생성한다.
  • Header와 Payload는 단순히 인코딩된 값이기 때문에 제3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 따라서 Signature는 토큰의 위변조 여부를 확인하는 데 사용된다.

4.3 JWT(Json Web Token) 인증 방식 (토큰 기반 인증)

  • JWT는 토큰 기반 인증 방식으로 클라이언트의 세션 상태를 저장하는 게 아니라 필요한 정보를 토큰에 담아서 클라이언트가 가지고 있다가 서버 요청시 담아서 사용한다.
  • 서버는 요청이 오면 토큰을 검증하고 해당 요청을 처리한다.

요약
1. 클라이언트가 로그인을 위해 해당 정보를 서버에 전달한다.
2. 서버는 로그인 성공시 사용자 식별에 대한 정보를 Body에 담아 토큰을 발급하고, 이를 클라이언트에 전달한다.
3. 클라이언트는 전달받은 토큰을 쿠키나 로컬/세션 스토리지에 저장하고 이후 서버에 요청시 해당 토큰을 담아서 전달한다.
4. 서버는 토큰을 검증하고 해당 요청을 처리한다.

자세히

  1. 클라이언트 로그인 요청이 들어오면, 서버는 검증 후 클라이언트의 고유한 ID 등의 정보를 payload에 담는다.
  2. JWT의 유효기간을 설정하고 secret 키를 이용해 access token(JWT)을 발급한다.
  3. 클라이언트는 access token(JWT)을 받아 저장한 후 인증이 필요할 때마다 토큰을 요청 헤더 Authorization에 포함시켜 함께 전달한다.
  4. 서버에서는 해당 토큰의 signature를 secret 키로 복호화한 후, 위변조 여부 및 유효기간을 확인한다.
  5. 검증이 완료되고 유효한 토큰이라면, payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져온다.

서버 기반 인증 방식과 토큰 기반 인증 방식에는 공통적으로 치명적인 취약점이 존재하는데 세션 ID나 토큰이 탈취될 경우 인증 절차가 바로 뚫리는 상황이 발생하게 된다.
토큰 기반 인증 방식에서는 이를 막기 위해 토큰의 수명이 짧은 access token과 access token을 다시 발급 받기 위한 수명이 긴 refresh token을 발급하여 관리한다. refresh token은 상대적으로 긴 유효기간을 가지며 access token이 만료됐을 때 새로 발급해주는 키가 된다. 보통 현업에서 refresh token의 발급기간은 2주 정도라고 한다.

4.4 Refresh Token

access token의 경우 수명을 짧게 하여 지속적으로 반복되는 요청에 대해 빠르고 가볍게 처리할 수 있는 반면, refresh token의 경우 access token의 경우 access token이 만료된 경우 로그인 대신 refresh token을 이용해 다시 access token을 발급 받는다.
단, MSA를 도입할 경우 서버 기반 인증과 마찬가지로 Redis와 같은 별도 Refresh Token을 관리하기 위한 서버가 필요하다.

  1. 클라이언트가 로그인을 하면 회원 DB에서 값을 비교한다.

  2. 로그인이 완료되면 access token, refresh token을 발급한다.

  3. 이 때 서버는 회원 DB에 refresh token을 저장, 클라이언트는 안전한 저장소에 저장 후 access token만을 헤더에 실어 요청을 보낸다.

  4. 서버는 access token을 검증해 이에 맞는 데이터를 보낸다.

    • payload를 통해 유효기간을 미리 알 수 있으므로 프론트 단에서 API 요청 전에 토큰이 만료됐다면 바로 재발급 요청을 할 수도 있다.)
  5. access token이 만료됐을 때는 서버는 ‘권한없음'을 신호로 보낸다.

    • 이 때, 클라이언트는 바로 refresh token도 함께 서버로 보낸다.
    • 서버는 access token의 조작여부를 판단 후, refresh token을 비교한다. token이 동일하고 유효기간이 지나지 않았다면 새로운 access token을 발급하고 API 요청에 의해 클라이언트에게 다시 전달한다.

4.5 장단점

장점

  • Header와 Payload를 가지고 Signature를 생성하므로 데이터 위변조를 막을 수 있다.
  • 인증 정보에 대한 별도의 저장소가 필요없다.
  • JWT는 토큰에 대한 기본 정보와 전달할 정보 및 토큰이 검증됐음을 증명하는 서명 등 필요한 모든 정보를 자체적으로 지니고 있다.
  • 클라이언트 인증 정보를 저장하는 세션과 다르게, 서버는 무상태가 된다.
  • 확장성이 우수하다.
  • 토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능하다.
  • OAuth의 경우 Facebook, Google 등 소셜 계정을 이용하여 다른 웹서비스에서도 로그인을 할 수 있다.
  • 모바일 어플리케이션 환경에서도 잘 동작한다.

단점

  • 쿠키/세션과 다르게 JWT는 토큰의 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해진다.
  • Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
  • 토큰을 탈취당하면 대처하기 어렵다.
    • 토큰은 한 번 발급되면 유효기간이 만료될 때까지 계속 사용이 가능하기 때문이다.
  • 특정 사용자의 접속을 강제로 만료하기 어렵지만, 쿠키/세션 기반 인증은 서버 쪽에서 쉽게 세션을 삭제할 수 있다.

4.6 그럼 세션과 토큰 인증 중 어떤 게 더 나은 선택일까?

  • 웹 사이트의 규모, 핸들링할 요청의 종류에 따라 선택할 수 있다.
  • 유저의 로그인 세션 정도만 인증이 필요한 작은 웹사이트에서는 세션 인증이 보통 좋은 선택이 될 수 있다.
  • 하지만 규모가 큰 웹 사이트라면 JWT가 훨씬 나은 선택이 될 수 있다.

4.7 쿠키/세션은 캐시와 다르다

  • 캐시는 이미지나 css, js 파일 등을 브라우저나 서버 앞 단에 저장해놓고 사용하는 것이다.
  • 한번 캐시에 저장되면 브라우저를 참고하기 때문에 서버에서 변경되어도 사용자는 변경되지 않게 보일 수 있는데 이런 부분을 캐시를 지워주거나 서버에서 클라이언트로 응답을 보낼 때 헤더에 캐시 만료시간을 명시하는 방법등을 이용할 수 있다.
  • 보통 쿠키와 세션의 차이를 물어볼 때 저장위치와 보안에 대해서는 잘 말하는데 사실 중요한 것은 라이프사이클을 얘기하는 것이다.

5. 캐시

5.1 웹서버 기준 캐싱 처리

  1. 웹에 요청을 호출하면, 요청은 웹 브라우저로부터 하드 디스크의 파일 시스템에 있는 정적 리소스를 제공하는 웹 서버로 전달한다.

  2. 이때 성능을 고려하여 캐싱 시스템을 잘 활용하고 있는 시스템 환경이라고 한다면
    웹 서버로 첫 요청시, 하드 디스크는 캐시를 확인하고 “캐시 미스(Cache miss)”를 발생시킨다. 그리고 추후 다시 요청받을 수 있다고 하면 캐시를 하드디스크에 저장한다.

  3. 이후 요청부터는 캐시를 사용할 수 있는 경우 “캐시 히트(Cache hit)”를 발생시키고, 캐시 미스가 발생하기 전까지 버퍼에서 캐시가 제공된다.

    • Database 캐싱과 같은 경우도 시스템 성능에 있어 매우 중요하다.
      데이터베이스 쿼리는 데이터베이스 서버에서 수행되기 때문에 사용자가 급격히 증가함에 따라 속도가 매우 느려지고 부하가 몰릴 수 있다. 이런 쿼리들이 반복되면 그 결과값을 데이터베이스에 캐싱하여 응답시간 향상 및 서버 부하 감소가 가능하다.
      특히 다수의 머신이 동일한 데이터베이스에 동일한 쿼리를 사용할 수록 그 효과는 커지게 된다.

5.2 응답 캐싱

  • 웹 서버는 응답을 캐싱하도록 구성하여 유사한 요청이 애플리케이션 호스트로 전달되지 않도록 할 수 있다.
  • 이와 비슷한 방식으로, 애플리케이션 호스트는 비용이 높은 데이터베이스 쿼리나 자주 요청되는 파일들에 대한 응답을 캐시할 수도 있다.
  • 웹 서버의 응답은 메모리에 캐싱하고, 애플리케이션 캐시는 로컬 인메모리에 저장하거나 캐시 서버 위에서 실행되는 레디스와 같은 인메모리 데이터베이스에 저장할 수 있다.

5.3 함수 메모이제이션 (Memoization)

  • 메모이제이션 : 컴퓨터 프로그램이 동일한 계산을 반복해야할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술, 캐싱의 한 형태
  • 이는 함수의 입력 파라미터에 해당하는 키와 결과값에 해당하는 값을 매칭시켜둔 조회 테이블을 통해 구현할 수 있다.
  • 프로그램의 성능을 높이는데 사용되는 일반적인 기술이지만 드물게 요청되는 함수나 빠른 응답 속도를 가진 함수에 대해서는 적합하지 않을 수도 있다.

5.4 웹 캐시의 종류

웹 캐시의 종류는 어디에 적용하느냐에 따라 나뉠 수 있고, 가장 중요한 것은 브라우저 캐시이다.

5.4.1 브라우저 캐시 특징

  • 브라우저 또는 HTTP 요청을 하는 클라이언트 애플리케이션에 의해 내부 디스크에 캐시한다.
  • 캐시된 리소스를 공유하지 않는 한 개인에 한정된 캐시이다.
  • 브라우저의 뒤로 버튼 또는 이미 방문한 페이지를 재방문하는 경우 효과가 극대화된다.

5.4.2 프록시 캐시

프록시 서버

컴퓨터 네트워크에서 프록시 서버는 컴퓨터 시스템, 하드웨어 기기 또는 애플리케이션 형태로 나타난다.
다른 서버의 리소스를 찾는 클라이언트의 요청에 대한 중개 역할을 하며 그 반대의 경우도 마찬가지이다.
다양한 형태의 프록시 서버가 존재한다. 사용자의 로컬 컴퓨터, 네트워크 라우터 또는 클라이언트와 대상 호스트 사이의 다양한 중개 서버에 상주할 수 있다. 모든 프록시 서버는 캐싱이 가능하다.

프록시 캐시 특징

  • 브라우저 캐시와 동일한 원리로 동작하며 클라이언트나 서버가 아닌 네트워크 상에서 동작한다.
  • 큰회사나 IPS의 방화벽에 설치되며 대기시간 & 트래픽 감소, 접근 정책 & 제한 우회, 사용률 기록 등을 수행한다.
  • 한정된 수의 클라이언트를 위하여 무한대의 웹서버의 컨텐츠를 캐시한다.

5.4.3 게이트웨이 캐시(REVERSE OR SURROGATE PROXY)

  • 서버 앞 단에 설치되어 요청에 대한 캐시 및 효율적인 분배를 통해 가용성, 신뢰성, 성능 등을 향상시킨다.
  • 암호화 / SSL 가속기(acceleration), 로드 밸런싱, 서버/캐시 정적 컨텐츠, 압축 등을 수행한다.
  • 무한대의 클라이언트들에게 한정된 수(또는 하나)의 웹서버 컨텐츠를 제공한다.

5.5 웹 캐시

웹 캐시란 클라이언트가 요청하는 리소스(html, image, js, css 등)에 대해 최초 요청 시에 파일을 내려받아 특정 위치에 복사본을 저장하고, 이후 동일한 URL의 리소스 요청은 다시 내려 받지 않고 내부에 저장한 파일(캐시)을 사용해 더 빠르게 서비스하기 위한 것이다.

서버를 통하지 않아(또는 서버를 통해 내려 받는 양이 적어지기 때문에) 응답 시간이 감소하고 네트워크 트래픽이 감소되기 때문에 서버와 클라이언트 모두에게 좋다.

하지만 잘못 사용한다면 고객들에게 실시간으로 정보를 올바르게 주지 못하고, 오히려 고객 CS가 발생하는 등 해당 업무 프로세스와 시스템 환경을 잘 분석하여 적용하는 것이 중요하다.

5.6 HTTP 헤더를 통한 캐싱

  • 모든 브라우저는 HTML, javascript 파일 및 이미지와 같은 웹 문서의 임시 저장을 위해 HTTP 캐시(웹 캐시)의 구현을 제공하고 있다.
  • HTTP 헤더 지시문을 제공하여 서버 응답이 브라우저에 응답을 캐싱할 수 있는 시기와 지속 시간을 지시할 때 사용된다.
  • 리소스가 로컬 캐시로부터 빠르게 로드되기 때문에 점점 하드웨어 성능이 급격하게 증가하고 있는 최근에 잘만 활용한다면 엄청 강력한 속도를 제공한다.
  • 캐시 설정을 통해 브라우저에 응답으로 온 HTML, JSON 등의 이미 저장된 데이터를 통해 서버에 요청을 보내지 않고도 사용할 수 있다.
  • 요청이 네트워크를 통해 전송되지 않기 때문에 왕복 시간(RTT, Roung Trip Time)이 발생하지 않는다.
  • 애플리케이션 서버 및 파이프라인의 다른 구성 요소에 대한 부하가 줄어든다.
  • 일반적으로 캐싱은 GET 요청에서 처리한다.
    일반적으로 200(가져오기 성공), 206(부분 컨텐츠 응답), 301(다른 주소로 이동 후 가져옴), 404(가져올 게 없음) 상태 코드로 온 응답들을 캐싱할 수 있다.
  • 가장 흔하게 볼 수 있는 캐싱 페이지가 오류 페이지이다.
    어떤 오류가 발생하면 그 사이트 특색에 맞게 오류 페이지를 보여주도록 해둔 사이트도 많다.

5.7 HTTP 헤더를 통한 캐싱 문법

헤더 예시

헤더 종류

파일이 이전과 비교하여 변경되었는가를 체크하는 validation과 캐시의 만료 여부를 체크하는 freshness로 구성된다. 요청과 응답에 따라 서로 사용될 수 있는 값이 다르며 HTTP 1.0에서 HTTP 1.1로 넘어오면서 약간의 변화가 생겼다. HTTP 1.1에선 하위 호환되므로 1.0의 헤더를 사용하여도 정상 동작하지만 중복으로 선언된다면 1.1에 정의된 것이 우선순위를 가지게 된다.

ex) Last-Modified와 Etag가 동시에 있다면 Etag가 우선순위를 가진다. Expires와 Cache-Control도 마찬가지다.

HTTP 1.1의 Cache-Control은 하나의 값이 아니라 다양한 지시자를 , 를 이용하여 값을 전달할 수 있다. 그로 인해 여러가지 컨트롤을 가능하게 만들어 준다. ex)Cache-Control:max-age=2600, must-revalidate

1. Cache-Control

  • 일반적으로 캐싱은 GET 메서드에 대한 응답을 캐싱하는 것으로 제한한다.
  • Cache-Control HTTP 헤더를 통해 캐싱 정책을 정의할 수 있다.
  • Cache-Control 지시문은 응답을 캐시할 수 있는 사용자, 해당 조건 및 기간을 제어한다.
  • Cache-Control 헤더는 HTTP/1.1 사용자 일부로 정의되었고, 응답 캐싱 정책을 정의하는 데 사용된 이전 헤더(ex. Expires)를 대체한다.
  • 모든 최신 브라우저는 Cache-Control을 지원한다.
  • Cache-Control는 응답 헤더 뿐만 아니라, 요청 헤더로도 사용할 수 있다.
    프론트 - 중개서버 - 서버와 같은 구조인 경우 중개 서버에 있는 캐시를 가져오지 않도록 하려면 요청 시 Cache-Control을 헤더로 넣어주어 해결하기도 한다.

2. ‘no-cache’ vs ‘no-store’

  • ‘no-cache’는 캐시를 사용하지 말라는 뜻이 아니라 캐시를 쓰기 전에 서버에 이 캐시를 진짜 써도 되냐고 물어보라는 뜻이다.
  • ‘no-store’는 아무것도 캐싱하지 않으려면 사용한다.
    ex) Cache-Control : no-cache, no-store

3. ‘public’ 및 ‘private’

  • ‘public’ 이면 공유 캐시(또는 중개 서버)에 저장해도 된다는 뜻이다.
    ex) public, max-age=3600
    max-age로 캐시 유효시간을 줄 수 있다. 초 단위이므로 1시간이 지나면 이 응답 캐시는 만료된 것으로 여겨진다.
    대부분의 경우, 명시적 캐싱 정보(예: ‘max-age’)가 응답이 어떠한 경우든지 캐시가 가능하다고 나타내므로 ‘public’이 필요하지 않다.

  • ‘private’ 은 브라우저같은 특정 사용자 환경에만 저장하라는 뜻이다.
    일반적으로 단일 사용자를 대상으로 하므로 중간 캐시가 이 응답을 캐시하는 것은 허용되지 않는다.
    예를 들어, 비공개 사용자 정보가 포함된 HTML 페이지는 사용자의 브라우저가 캐시할 수 있지만, CDN은 이 페이지를 캐시할 수 없다.

4. must-revalidate

  • 만료된 캐시만 서버에 확인을 받도록 한다.
  • max-age=
    ex) Cache-Control: max-age=86400

5.1 max-age

  • 캐시 유효시간, 즉 리소스가 유효하다고 판단되는 최대 시간이다
  • 초단위로 설정하며, 해당 시간이 지나면 만료된 것으로 여겨진다.
    ex) Cache-Control: max-age=31536000

5.2 Age

  • 캐시 응답 때 나타나는데, max-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려준다.

6. Expires

  • Cache-Control과 동시에 동작하진 않지만, Expires라는 헤더를 지정하여 응답 컨텐츠 만료 시간을 나타낼 수 있다.
    ex) Expires: Wed, 23 Jan 2019 03:44:00 GMT
  • Cache-Control의 max-age가 있는 경우 무시된다.
  • 유효 수명 : Expires 헤더에서 Date 헤더의 값을 뺀 결과

7. ETag

  • ETag로 캐시된 응답에 대한 유효성 검사를 수행한다. (HTTP 컨텐츠가 바뀌었는지를 검사할 수 있는 태그)
  • 서버는 ETag HTTP 헤더를 사용하여 유효성 검사 토큰을 전달한다. 유효성 검사 토큰을 사용하여 리소스 업데이트 검사하며 리소스가 변경되지 않은 경우 데이터가 전송되지 않는다.
  • Expires 나 max-age 등을 사용하여 특정 시간동안만 캐시를 사용한다고 했을 때 특정 시간, 시점이 지나야 브라우저가 새 요청을 발송하고 전체 새 응답을 가져온다.
    그러나, 리소스가 변경되지 않은 경우엔 이미 캐시에 있는 동일한 정보를 다운로드할 이유가 없으므로 이렇게 시간을 지정하여 캐시를 갱신하는 작업은 비효율적이다.
  • ETag 헤더에 지정된 유효성 검사 토큰은 바로 이 문제를 해결하기 위해 고안되었다. 서버는 일반적으로 파일 콘텐츠의 해시나 기타 일부 지문인 임의 토큰을 생성하고 반환한다.
    클라이언트는 다음 요청 시 지문을 서버에 전송하기만 하면 된다. 지문이 여전히 동일한 경우 리소스가 변경되지 않고 이 다운로드를 건너뛸 수 있다.
    ex) 클라이언트에 최대 120초 동안 캐시하도록 지시하고, 응답이 만료된 후 리소스가 수정되었는지 확인하는 데 사용할 수 있는 유효성 검사 토큰(‘x456dff’)을 제공한다.

8. If-None-Match

  • 서버보고 ETag가 달라졌는 지 검사하고 ETag가 다를 경우에만 컨텐츠를 새로 받는다.
    ex) If-None-Match: “0-2b800-5c466dda”
  • 만약 ETag가 같다면 서버는 304 Not Modified를 응답하고 캐시를 그대로 사용하게 한다.

5.7.1 캐시에 관한 헤더를 상황에 맞게 선택할 수 있도록 도와주는 알고리즘

5.8 HTTP 헤더를 통한 캐시의 동작 과정

이제 캐시가 일어나는 과정에서 위에서 설명한 HTTP 헤더들이 어떻게 사용되는 지 살펴보겠다

1. 첫 요청

  1. 브라우저는 서버에 index.html 파일을 요청한다.
  2. 서버는 index.html 파일을 찾아보고 존재하는 파일이라면 파일 내용을 브라우저에게 몇가지 헤더값과 함께 응답한다.
  3. 브라우저는 응답 받은 내용을 브라우저에 표시하고 응답 헤더의 내용에 따라 캐시 정책을 수행한다.
    (응답 헤더에 Last-Modifid, Etag, Expires, Cache-Control:max-age 항목이 존재한다면 복사본을 생성하고 값을 저장한다.)

2. 재요청

2.1 LAST-MODIFIED

  1. 브라우저는 최초 응답 시 받은 Last-Modified 값을 If-Modified-Since 라는 헤더에 포함시켜 페이지를 요청한다.
  2. 서버는 요청 파일의 수정 시간을 If-Modified-Since값과 비교하여 동일하다면 304 Not Modified로 응답하고 다르다면 200 OK와 함께 새로운 Last-Modified 값을 응답 헤더에 전송한다.
  3. 브라우저는 응답 코드가 304인 경우 캐시에서 페이지를 로드하고 200이라면 새로 다운받은 후 Last-Modified 값을 갱신한다.

2.2 ETAG(ENTITY TAG)

  1. 브라우저는 최초 응답 시 받은 Etag 값을 If-None-Match 라는 헤더에 포함시켜 페이지를 요청한다.
  2. 서버는 요청 파일의 Etag 값을 If-None-Match 값과 비교하여 동일하다면 304 Not Modified로 응답하고 다르다면 200 OK와 함께 새로운 Etag 값을 응답 헤더에 전송한다.
  3. 브라우저는 응답 코드가 304인 경우 캐시에서 페이지를 로드하고 200 이라면 새로 다운받은 후 Etag 값을 갱신한다.
  • Etag는 서버마다 생성하는 값이 다르며 파일마다 고유한 값을 가진다.
  • Last-Modified(1.0)와 Etag(1.1)는 validation을 체크한다. 이를 체크하기 위해 서버와 한번의 통신이 발생하게 되며 그로 인해 요청과 응답에서 헤더와 쿠키 등에 의한 데이터 전송(1KB)이 발생하게 된다.

2.3 Expires

  1. 브라우저는 최초 응답 시 받은 Expires 시간을 비교하여 기간 이내라면 서버를 거치지 않고 바로 캐시에서 페이지를 로드한다. 만약 기간이 만료되었다면 위에 설명한 validation 작업을 수행한다.

2.4 Cache-Control

  1. 브라우저는 최초 응답 시 받은 Cache-Control 중 max-age 값(초 단위)를 GMT와 비교하여 기간 이내라면 서버를 거치지 않고 캐시에서 페이지를 로드한다. 만약 기간이 만료되었다면 validation 작업을 수행한다.
  • Expires(1.0)와 Cache-Control:max-age(1.1)는 freshness를 체크한다. 기간 이내라면 서버와 통신하지 않고 캐시를 사용한다.
  • 시간은 HTTP date의 형태이며 로컬 타임이 아닌 GMT를 사용한다.
  • 서버가 Last Modified Time 또는 Last Access Time을 기준으로 하여 일정 시간 이후로 Expires 또는 max-age를 설정한다.

참고
https://k-elon.tistory.com/4
https://kihyeoksong.tistory.com/99
https://hahahoho5915.tistory.com/33
https://goddaehee.tistory.com/171
https://seoramyeon.tistory.com/48
https://velog.io/@dogy/%EC%84%9C%EB%B2%84-%EC%9D%B8%EC%A6%9D.-%EC%BF%A0%ED%82%A4Cookie-%EC%84%B8%EC%85%98Session-%ED%86%A0%ED%81%B0JWT
https://dunkey2615.tistory.com/119
https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/

profile
아무것도 모르는 백엔드 3년차 개발자입니다 :)

0개의 댓글