📌 김영한 님의 "모든 개발자를 위한 HTTP 웹 기본 지식" 강의 듣고 정리

 

📍캐시 기본 동작

  • 캐시가 없을 때 
    ex) 이미지를 요청할 경우 서버는 이미지에 대한 헤더(0.1M)와 바디의 이미지(1.0M)를 합쳐 1.1M의 응답을 보낸다. 
          캐시가 없다면 요청할 때마다 1.1M의 응답을 보냄
    • 데이터가 변경이 없어도 계속 네트워크를 통해 데이터를 다운받아야 한다.
    • 인터넷 네트워크는 매우 느리고 비싸다 + 브라우저 로딩 속도가 느리다 = 느린 사용자 경험
  • 캐시를 적용하면
    ex) 서버에서 캐시 적용을 하게 되면 요청시 서버가 응답할 때 캐시가 유효한 시간을 지정하여 해당 요청에 대한 
          데이터를 보낸다. 그리고 웹브라우저는 그 응답 결과를 다운받아 브라우저 캐시에 저장한다 (유효시간만큼)
          두번째 요청시 웹 브라우저는 캐시부터 뒤져서 유효시간이 만료되지 않았다면 존재하고 있을 데이터를 가져온다.
          다시 요청시 유효시간 만료되면 다시 다운받아 기존에 캐시에 저장된 데이터를 초기화하고 가져온 것으로 덮는다.
    • 비싼 네트워크 사용량을 줄일 수 있다.
    • 캐시 가능 시간동안 네트워크를 사용하지 않아도 된다. + 브라우저 로딩 속도가 매우 빠르다 = 빠른 사용자 경험
    • 만료되면 다시 조회하고 다시 다운로드하여 캐시를 갱신한다. 

그런데 시간만 만료됬을 뿐 데이터가 변한건 아닌데 또 다운받아야 한다?!
이걸 해결할 수 있는 매커니즘이 바로 검증 헤더이다. 


📍검증 헤더와 조건부 요청1

캐시 만료 후에도 서버에서 데이터를 변경하지 않았다면 만료됬더라도 그 캐시를 재사용 할 수 있다.

단, 웹브라우저 캐시의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법이 필요한데, 그게 검증 헤더.

 

  1. 클라이언트의 데이터 요청
  2. 검증 헤더를 통해 서버가 Last-Modified 값을 캐시 만료시간과 함께 전송
    ex) Last-Modified: 2020년 11월 10일 10:00:00 (검증 헤더)
  3. 응답 결과를 웹브라우저가 캐시에 저장. (만료시간과 최종 수정일 정보 또한 같이)
  4. 클라이언트의 데이터 요청시 먼저 캐시를 조회하고 만료시간 초과한 경우 캐시의 최종 수정일을 요청에 넣어 보낸다.
    ex) if-modified-since: 2020년 11월 10일 10:00:00 (조건부 헤더, 만약 이후로 변경되었다면?)
  5. 서버가 이를 서버내의 최종 수정일과 비교해 봤을 때 동일하면 바디 없이 헤더 정보만 있는 304 Not Modified 응답을 보낸다. (헤더 크기 만큼의 용량은 전달)
    ex) cache-control: max-age=60
          Last-Modified: 2020년 11월 10일 10:00:00
  6. 이 응답을 받으면 캐시 컨트롤 값을 갱신시켜 캐시를 다시 세팅. 그리고 이 캐시를 불러와 사용한다. 

 

  • 캐시 유효시간 초과 + 서버 데이터 갱신 없음 시
  • 304 Moidified + 헤더 메타 정보만 응답(바디X)
  • 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신
  • 클라이언트는 캐시에 저장되어 있는 데이터 재활용
  • 결과적으로 네트워크 다운로드는 발생하나 용량이 적은 헤더만 다운받으므로 매우 실용적인 해결책이다. 

📍검증 헤더와 조건부 요청2

  • 검증 헤더
    • 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터
    • Last-Modified, ETag
  • 조건부 요청 헤더
    • 검증 헤더로 조건에 따른 분기
    • If-Modified-Since: Last-Modified 사용
    • If-None-Match: ETag 사용
    • 조건이 만족하면 200 OK
    • 조건이 만족하지 않으면 304 Not Modified
  • If-Modified-Since: 이후에 데이터가 수정되었다면?
    • 데이터 미변경시 (실패)
      - 304 Not Modified, 헤더 데이터만 전송 (헤더 용량만큼 전송)
    • 데이터 변경시 (성공)
      - 200 OK, 모든 데이터 전송 (헤더 + 바디 용량 전송)
  • Last-Modified, If-Modified-Since 단점
    • 1초 미만(0.x초) 단위로 캐시 조정이 불가능 (최소 단위가 초라서)
    • 날짜 기반의 로직 사용
    • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우도 전체 다운로드 
    • 서버에서 최종 수정일이 아니라 별도의 캐시 로직을 관리하고 싶은 경우 사용 불가
      (ex. 스페이스나 주석처럼 크게 영향이 없는 변경은 무시하고 캐시 유지 원하는 경우)

      이런 경우 이걸 해결 할 수 있는 것이 ETag!
  • ETag, If-None-Match 
    • ETag(Entity Tag)
    • 캐시용 데이터에 임의의 고유한 버전 이름을 달아둠 (ex. ETag: "v1.0" , ETag: "a2jiodwjekjl3")
    • 데이터가 변경되면 이 이름을 바꾸어서 변경 (Hash를 다시 생성, 만약 컨텐츠가 동일하면 동일한 Hash값 나옴) 
    • 요청시 ETag를 보내어 같으면 유지, 다르면 다시 다운로드 
    • 사용 과정은 Last-Modified, If-Modified-Since과 동일하며 실패시(미변경) 304 Not Modified, 성공시(변경) 200 OK

    • 캐시 제어 로직을 서버에서 완전히 관리 (클라이언트는 캐시 매커니즘을 모른다)
    • 클라이언트는 단순히 이 값을 서버에 제공

📍캐시와 조건부 요청 헤더 정리

  • 캐시 제어 헤더
    • Cache-Control: 캐시 제어 (지금은 이걸로 모두 가능)
      • 캐시 지시어(directives)
      • Cache-Control: max-age
        - 캐시 유효 시간, 초단위
      • Cache-Control: no-cache
        - 데이터는 캐시해도 되지만 캐시 사용하려고 할때 항상 Origin 서버에 검증하고 사용
      • Cache-Control: no-store
        - 데이터에 민감한 정보가 있으면 저장하면 안된다는 표시 (메모리에서 사용하고 최대한 빨리 삭제)
    • Pragma: 캐시 제어(하위 호환) : HTTP 1.0 
    • Expires: 캐시 유효 기간(하위 호환)
      • 캐시 만료일 지정
      • 더 유연한 Cache-Control: max-age 권장
      • Cache-Control: max-age 랑 같이 사용시 Expires는 무시된다.
  • 검증 헤더 (Validator)
    • ETag: "v1.0" , ETag: "a2jiodwjekjl3"
    • Last-Modified: Thu, 04 Jun 2020 07:19:24 GMT
  • 조건부 요청 헤더
    • If-Match, If-None-Match: ETag 값 사용
    • If-Modified-Since, If-Unmodified-Since: Last-Modified 값 사용

📍프록시 캐시

  • 원(Origin) 서버
    원 서버에 직접 접근하게 되면 거리가 멀수록 속도가 느리다
    이를 해결하기 위해 프록시 캐시 서버를 도입

  • 프록시(Proxy) 캐시 서버 
    보통 CDN(Content Delivery Network)이름으로 서비스로 제공
    원서버가 아닌 프록시 서버(클라이언트와 가까운 프록시 캐시 서버)에 거쳐 가게 하여, 프록시 서버에 캐시가 있으면 거기서 캐시를 조회해 빠른 응답을 받게 한다. (없으면 원서버 다녀와야함)
    - 프록시 서버의 캐시를 Public 캐시라 하고 클라이언트의 캐시는 Private 캐시라고 한다. 

  • Cache-Control: 기타 
    • Cache-Control: public
      - 응답이 public 캐시에 저장되어도 됨
    • Catche-Control: private
      - 응답이 해당 사용자만을 위한 것임, private 캐시에 저장해야 함(기본값)
    • Cache-Control: s-maxage
      - 프록시 캐시에만 적용되는 max-age
    • Age: 60 (HTTP 헤더)
      - 오리진 서버에 응답 후 프록시 캐시 내에 머문 시간(초)

📍캐시 무효화

  • 확실한 캐시 무효화 응답이 존재 (캐시를 적용안해도 웹 브라우저가 임의로 캐시해버리고 하기 때문)
  • Cache-Control
    • Cache-Control: no-cache, no-store, must-revalidate (이걸 모두 넣어주어야 무효화)
      • Cache-Control: no-cache
        - 데이터는 캐시해도 되지만 캐시 사용하려고 할때 항상 Origin 서버에 검증하고 사용
      • Cache-Control: no-store
        - 데이터에 민감한 정보가 있으면 저장하면 안된다는 표시 (메모리에서 사용하고 최대한 빨리 삭제)
      • Cahce-Control: must-revalidate
        - 캐시 만료후 최초 조회시 원 서버에 검증해야함
        - 원 서버 접근 실패시 반드시 오류가 발생해야함 (504 Gateway Timeout)
        - must-revalidate는 캐시 유효 시간이라면 캐시를 사용함
        - ❓no-cache가 있는데 이거 왜 사용
        no-cache의 경우 프록시 서버와 원 서버 사이의 일시적 장애가 생긴 경우 프록시 서버의 오래된 데이터라도 보여주는 설정도 할 수 있다. must-revalidate는 서버 사이 장애시 무조건 504 오류 응답
    • Pragma: no-cache (HTTP 1.0 하위 호환, HTTP 1.0 요청 경우를 무효화 위해)

📌 김영한 님의 "모든 개발자를 위한 HTTP 웹 기본 지식" 강의 듣고 정리

 

📍HTTP 헤더 개요

  • HTTP 헤더 구조
    더보기
     

    - 요청 메시지

    start-line 시작 라인 GET /search?q=hello&hl=ko HTTP/1.1
    header 헤더 Host: www.google.com
    empty line 공백 라인 (CRLF)  
    message body  

    시작 라인 :

    method SP(공백) request-target SP(공백) HTTP-version CRLF(엔터)
    GET   /search?q=hello&hl=ko   HTTP/1.1  

    HTTP 헤더 :

    field-name: OWS(띄어쓰기 허용) field-value OWS(띄어쓰기 허용)
    Host:   www.google.com  

    - 응답 메시지

    start-line 시작 라인 HTTP/1.1 200 OK
    header 헤더 Content-Type: text/html;charset=UTF-8
    Content-Length: 3423
    empty line 공백 라인 (CRLF)  
    message body <html>
        <body>...</body>
    </html>

    시작 라인 :

    HTTP-version SP(공백) status-code SP(공백) reason-phrase CRLF
    HTTP/1.1   200   OK  

    HTTP 헤더 :

    field-name: OWS(띄어쓰기 허용) field-value OWS(띄어쓰기 허용)
    Content-Type:
    Content-Length:
      text/html;charset=UTF-8
    3423
     
  • HTTP 헤더의 용도
    • HTTP 전송에 필요한 모든 부가정보
      ex) 메시지 바디의 내용, 메시지 바디의 크기, 압축, 인증, 요청 클라이언트, 서버 정보, 캐시 관리 정보 등
    • 표준 헤더가 너무 많음
    • 필요시 임의 헤더 추가 가능   
  • HTTP 헤더의 분류 : 
    • 1999년 RFC2616 등장 (폐기됨)   RFC2616 과거 : https://www.rfc-editor.org/rfc/rfc2616

      ✅ 과거에는 헤더를 크게 4가지로 분류


      - General 헤더 : 메세지 전체에 적용되는 정보     ex) Connection: close
      - Request 헤더 : 요청 정보                                   ex) User-Agent: Mozilla/5.0 (Macintosh; ..)
      - Response 헤더 : 응답 정보                                ex) Server: Apache
      - Entity 헤더: 엔티티 바디 정보                             ex) Content-Type: text/html, Content-Length:3423


       message Body


      - 메시지 본문 (message body)은 엔티티 본문(entity body)을 전달하는 데 사용
      - 엔티티 본문은 요청이나 응답에서 전달할 실제 데이터에 해당한다. 엔티티 본문을 메시지 본문에 담아 전송.
      - 엔티티 헤더는 엔티티 본문의 데이터를 해석할 수 있는 정보 제공
         - 데이터 유형(html, json), 데이터 길이, 압축 정보 등등

    • 2014년 RFC7230-7235 등장
      - 스펙이 쪼개지면서 다수 개정
      - 엔티티(Entity) 대신 표현(Representation) 요소가 도입 (완전 대응되는 건 X)
      - Representation (표현) = representation Metadata (표현 메타데이터(헤더)) + Representation Data (표현 데이터)
      - 이 REpresentation가 REST API의 RE에 해당

      message Body


      - 메시지 본문(message body)을 통해 표현 데이터 전달
      - 메시지 본문 = 페이로드(payload)
      - 요청이나 응답에서 전달할 실제 데이터를 명확하게 표현으로 정의
      - 표현 헤더는 표현 데이터를 해석할 수 있는 정보 제공
         - 데이터 유형(html, json), 데이터 길이, 압축 정보 등등
      - 참고: 표현 헤더는 표현 메타데이터와 페이로드 메시지를 구분해야 하지만, 여기 강의에선 생략

 


📍 표현

  • 표현 :
    클라이언트와 서버 간에  주고 받을 때는 서로가 이해할 수 있는 무언가로 변환해야한다. 그걸 위해 각자의 데이터를 html, xml, Json 등으로 표현하게 된다. 

  • 표현 헤더 (요청, 응답 둘 다 사용)
    • Content-Type: 표현 데이터의 형식
      - 미디어 타입, 문자 인코딩
      ex) text/html; charset=utf-8    ,    application/json    ,    image/png 등

    • Content-Encoding: 표현 데이터의 압축 방식
      - 데이터를 송신 측에서 압축 후 인코딩 헤더 추가
      - 데이터를 수신 측에서 인코딩 헤더 정보로 압축 해제
      ex) gzip    ,    deflate    ,    identity (No압축) 등

    • Content-Language: 표현 데이터의 자연 언어
      - 자연 언어? 인간이 일상적으로 사용하고 있는 언어
      ex) ko    ,    en    ,    en-US  등
    • Content-Length: 표현 데이터의 길이
      - 바이트 단위
      - Transfer-Encoding(전송 코딩)을 사용할 때는 Content-Length를 사용하면 안됨
      (전송 코딩 안에 이미 정보가 다 들어 있다.)

 


📍콘텐츠 협상

  •  협상 (콘텐츠 네고시에이션) :
    클라이언트가 선호하는 표현이 이것이라고 서버에게 요청하는 것.
    서버가 최대한 선호에 맞춰서 보내줄 수 있도록 한다. (못맞춰 줄 수도 있음)
    (협상헤더는 요청시에만 사용)
    • Accept : 클라이언트가 선호하는 미디어 타입 전달
    • Accept-Charset: 클라이언트가 선호하는 문자 인코딩
    • Accept-Encoding: 클라이언트가 선호하는 압축 인코딩 
    • Accept-Language: 클라이언트가 선호하는 자연언어
      - 협상 헤더 없는 경우
      클라이언트 > 서버 : 클라이언트가 한국어 브라우저를 사용해서 서버에 요청 전송
      서버 > 클라이언트 : 클라이언트의 선호 사항을 모름으로 그냥 서버의 기본으로 설정된 언어로 전송
      - 협상 헤더 있는 경우
      클라이언트 > 서버 : 클라이언트가 한국어 브라우저를 사용해서 서버에 요청 전송
      서버 > 클라이언트 : 협상 헤더 Accept-Language: 로 ko(한국어)를 보내면 선호 언어로 전송 (그 언어 지원 경우)

      ❓그럼 독일어와 영어를 지원하는 서버에 한국어로 요청했는데... 독일어 보단 영어를 받고 싶다 이럴 때는? 
      그럴 때를 위한게 우선 순위 이다

 

  • 협상과 우선순위 
    • Quality Values(q) 값 사용
      0~1, 클수록 높은 우선순위에 해당한다 (생략하면 1)
      ex) Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
      1. ko-KR;q=1 (위에서는 q가 생략됨,ko-KR은 한국사람이 쓰는 한국어) 
      2. ko;q=0.9 (ko는 한국공통어)
      3. en-US;q=0.8 (en-US는 US에서 쓰는 영어)
      4. en:q=0.7 (en는 영어공통어)
    • 구체적인 것이 가장 우선시 한다. 
      ex) Accept: text/*, text/plain, text/plain;format=flowed, */*
      1. 순위: text/plain;format=flowed
      2. 순위: text/plain
      3. 순위: text/*
      4. 순위: */*
    • 구체적인 것을 기준으로 미디어 타입을 맞춘다.
      ex) Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5

      Media Type Quality
      text/html;level=1 1
      text/html 0.7
      text/plain  0.3 (text/*에 맞춤)
      image/jpeg 0.5 (*/*에 맞춤)
      text/html;level=2 0.4
      (얘는 왜 0.4 인가 하면 위에 text/html;level=2 에 대해서는 구체적인 우선순위가 나와있지만 아래 level=3는 정확히 매칭되는 것이 없어 text/html 의 우선순위에 맞춘 것)
      text/html;level=3 0.7 (text/html 에 맞춤)

📍전송 방식

  • 단순 전송: 요청하면 응답을 주는 데 Content-Length만 지정해서 콘텐츠를 그냥 주고 받는다. (Content-Length는 message body 전송시 기본인듯하다 분할 전송은 제외)
  • 압축 전송: 서버가 콘텐츠를 압축하고 뭘로 압축했는지 정보를 Content-Encoding 헤더도 함께 보낸다. 
  • 분할 전송: Transfer-Encoding: chunked, chunk는 덩어리를 의미.
    데이터를 분할해서 보낼거다 라는 표시를 하며 표시된 바이트 (여기서 5, 5, 0 에 해당하는 숫자들)의 Hello 데이터를 먼저 보내고, World를 보내고 \r\n(Enter, 끝이라는 뜻)을 보낸다.
    주의) 분할 전송에서는 Content-Length를 보내면 안된다! Chunk안에 길이 정보 있다. 
  • 범위 전송: Range, Content-Range. 데이터의 범위를 지정해 전송 받을 수 있다.
    파일을 받다가 끊겼을 때 이미 받아진 걸 제외한 걸 받고자 할 때 사용가능

📍일반 정보

 

  • From: 유저 에이전트의 이메일 정보
    - 일반적으로 사용X, 검색 엔진 같은 곳에서 사용
    - 요청에서 사용

  • Referer: 이전 웹 페이지 주소
    - 현재 요청된 페이지의 이전 웹 페이지 주소
    - A > B로 이동하는 경우 B를 요청할 때 Referer: A 를 포함해서 요청한다.
    - Referer을 사용해 유입 경로 분석 가능하다.
    - 요청에서 사용
    - 😯❓황당하게도 Referer은 Referrer의 오타인데 이미 너무 많이 사용해서 그대로 사용하는 거라고

  • User-Agent: 유저 에이전트 애플리케이션 정보
    - ex) user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
    - 클라이언트의 애플리케이션 정보 (웹 브라우저 정보 등)
    - 통계 정보로 사용, 어떤 종류의 브라우저에서 장애가 발생하는 지 파악 가능
    - 요청에서 사용

  • Server: 요청을 처리하는 오리진 서버의 소프트웨어 정보
    (중간의 정보가 거치는 서버들이 아니라 정보를 처리하는 끝점에 있는 서버를 오리진 서버라고 한다.)
    - ex) Server: Apach/2.2.22 (Debian)
    - ex) server: nginx
    - 응답에서 사용

  • Date: 메시지가 생성된 날짜 
    - ex) Date: Tue, 15 Nov 1994 08:12:31 GMT
    - 응답에서 사용

📍특별한 정보

 

  • Host: 요청한 호스트 정보 (도메인)
    - 필수값
    - 요청에서 사용
    - 하나의 서버가 여러 도메인을 처리해야 할 때, 하나의 IP 주소에 여러 도메인이 적용되어 있을 때 
    (가상호스트를 통해 서버에 여러 애플리케이션 여러개가 구동 중일 경우 클라이언트에서 어느 도메인으로 데이터가 가야할지 모르기 때문에 이를 통해 구분) ? 포트랑 어떻게 다르지? TCP/IP는 IP로만 통신? 포트는?

  • Location: 페이지 리다이렉션
    - 웹 브라우저 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동(리다이렉트)
    - 201 (Created): Location 값은 요청에 의해 생성된 리소스 URI로 응답시 함께 전달
    - 3xx (Redirection): Location 값은 요청을 자동으로 리다이렉션하기 위한 대상 리소스를 가르킴

  • Allow: 허용 가능한 HTTP 메서드
    - 405 (Method Not Allowed) 에서 응답에 포함해야함
    - Allow: GET, HEAD, PUT
    - ex) 만약 경로가 존재하고 GET, HEAD, PUT만을 허용하는데 POST로 요청한 경우 위와 같이 응답

  • Retry-After: 유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간
    - 503 (Service Unavailavble): 서비스가 언제까지 불능인지 알려줄 수 있음
    - ex) Retry-Ater: Fri, 31 Dec 1999 23:59:59 GMT (날짜 표기)
    - ex) Retry-Ater: 120 (초단위 표기) 

📍인증

 

  • Authorization: 클라이언트 인증 정보를 서버에 전달
    - ex) Authorization: Basic xxxxxxxxxxxxxxx
    - 다양한 인증 메커니즘 마다 value가 달라짐

  • WWW-Authenticate: 리소스 접근시 필요한 인증 방법 정의
    - 접근시 인증이 안됬거나 문제시 401 Unauthorized 응답과 함께 사용,
      아래처럼 필요한 인증 정보의 예시를 함께 보내준다.
    - WWW-Authenticate: Newauth realm="apps", type=1, title="Login to \"apps\"",Basic realm="simple"

📍쿠키

 

  • Set-Cookie: 서버에서 클라이언트로 쿠키 전달 (응답)
  • Cookie: 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청시 서버로 전달

* 쿠키 등장

HTTP는 무상태 프로토콜이다. 요청과 응답을 받고 나면 끊어져 서로 상태를 유지하지 않기 때문에 로그인 한 상대라는 걸 알 수 있는 방법이 서버에겐 없다.

하지만 로그인 상태를 유지해야한다면 대안은?

  • 모든 요청에 사용자 정보를 포함해서 전송
    - 보안문제부터 데이터 전송량이 늘고, 개발 어려움 등의 다양한 문제 발생
    - 브라우저를 완전히 종료하고 다시 열면 그 땐 또 어떻게 해야하는 지 등의 문제 발생    

이걸 해결하기 위해 쿠키라는 개념이 도입됨

 

* 쿠키 저장과 전달 과정

로그인을 하게 되면 서버에서 Set-Cookie에 로그인 유저 정보를 말아서 보낸다. (왜 만다라는 표현을 할까?) 

웹브라우저는 이를 받아 웹브라우저 안의 쿠키 저장소에 그 정보를 저장해 놓는다. 

이제 웹브라우저는 해당 서버에 요청할 때 마다 쿠키 저장소를 뒤져서 쿠키 값을 가져와 헤더에 Cookie 값으로 전달한다. 

 

* 쿠키는 모든 요청에 쿠키 정보를 자동 포함한다.  

  •  하지만 정말 모든 요청에 보내면 이는 또 다른 문제가 발생한다. (따라서 따로 제약하는 방법도 있다)
  •  네트워크 트래픽이 추가로 유발되기 때문에 최소한의 정보(세션 ID, 인증토큰 등)만 사용해야 한다.
  •  보안에 민감한 데이터는 저장하면 안된다. 
    - 서버에 전송하지 않고 웹 브라우저 내에만 저장하고 싶다면 웹 스토리지 참고

 

* 쿠키의 예시

  ex) set-cookie: sessionId=abcde1234; expires=Sat, 26-Dec-2020 00:00:00 GMT; path=/; domain=.google.com; Secure

 

* 쿠키의 사용처

  • 사용자 로그인 세션 관리
  • 광고 정보 트래킹

* 쿠키의 생명주기

  • Set-Cookie: expires= 만료일을 지정 (GMT 기준)
  • Set-Cookie: max-age= 초를 설정 (0이나 음수면 쿠키 삭제)
  • 만료시간에 따른 쿠키 종류
    • 세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지
    • 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지 

* 쿠키의 도메인

  • ex) domain=example.org
  • 명시: 명시한 문서 기준 도메인 + 서브 도메인도 포함하여 쿠키 전송 가능
    (O) example.org (O) dev.example.org
  • 생략: 현재 문서 기준 도메인만 적용되어 서브 도메인에는 쿠키가 전송되지 않는다.
    (O) example.org (X) dev.example.org

* 쿠키의 경로

  • ex) path=/home
  • 이 경로를 포함한 하위 경로 페이지만 쿠키 접근 가능
  • 일반적으로 path=/ 루트로 지정

* 쿠키의 보안

  • Secure
    • 쿠키는 http, https를 구분하지 않고 전송
    • Secure를 전송하면 https인 경우에만 전송
  • HttpOnly
    • XSS 공격 방지
    • 자바스크립트에서 접근 불가 (document.cookie)
    • HTTP전송에만 사용
  • SameSite
    • XSRF 공격 방지
    • 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송

📌 김영한 님의 "모든 개발자를 위한 HTTP 웹 기본 지식" 강의 듣고 정리

 

📍상태코드 란?

클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능

 

HTTP/1.1 200 OK
Content-Type: application/json
Content-length: 34

{
   "username" : "young",
   "age" : 20
}

 

  • 1xx (Informational) : 요청이 수신되어 처리중
  • 2xx (Successful) : 요청 정상 처리
  • 3xx (Redirection) : 요청을 완료하려면 추가 행동이 필요
  • 4xx (Client Error) : 클라이언트 오류, 잘못된 문법 등으로 서버가 요청을 수행할 수 없음
  • 5xx (Server Error) : 서버 오류, 서버가 정상 요청을 처리하지 못함

처음 보는 상태 코드가 있다?

클라이언트가 인식할 수 없는 상태코드를 서버가 반환하게 되면 클라이언트는 상위 상태코드로 해석해 처리하게 된다. 그래서 새로운 상태 코드가 추가 되더라도 클라이언트를 변경하지 않아도 된다. (ex. 299 ??? > 2xx (Successful))

 

 

📍1xx (Informational)

거의 사용하지 않으므로 생략

 

📍2xx - 성공

클라이언트의 요청을 성공적으로 처리

  • 200 OK : 요청 성공
  • 201 Created : 요청 성공해 새로운 리소스가 생성됨, 생성된 리소스는 Location 헤더 필드로 식별
  • 202 Accepted : 요청 접수되었으나 처리는 미완료. 배치 처리 등에 사용. ex) 접수 뒤 1시간 뒤 배치 프로세스가 처리
  • 204 No Content : 요청을 성공적으로 수행했으나 응답 페이로드 본문에 보낼 데이터 없음
    • 결과 내용이 없어도 204 메시지(2xx)만으로 성공을 인식
    • ex) 웹 문서 편집기의 save 버튼 : 버튼 결과로 아무 내용 없어도 된다. 버튼을 눌러도 같은 화면을 유지해야 한다. 드의 경우

📍3xx - 리다이렉션

요청을 완료하기 위해 유저 에이전트의 추가 조치 필요

웹브라우저는 3xx 응답 결과에 Location 헤더가 있으면 해당 위치로 자동 이동(리다이렉트)한다. 

  • 300 Multiple Choices : 사용안함
  • 301 Moved Permanently
  • 302 Found
  • 303 See Other
  • 304 Not Modified
  • 307 Temporary Redirect
  • 308 Permanent Redirect

 

  • 리다이렉션 흐름

 

  • 리다이렉션의 종류
    • 영구 리다이렉션 - 특정 리소스의 URI가 영구적으로 이동 (ex. /members > /users)
      • 원래 URL 사용하지 않으며 검색 엔진 등에서도 변경을 인지한다.
      • 301 Moved Permanently : 리다이렉트시 요청 메서드가 GET이 될 수도 있고, 본문이 제거될 수도 있다 (모호함)
      • 308 Permanent Redirect : 301과 기능이 같으나 처음의 요청 메서드와 본문이 유지 된다
    • 일시 리다이렉션 - 일시적인 잠깐 이동 (ex. 주문 완료 후 주문 내역화면으로 이동. PRG패턴)
      • 실무에서 많이 사용하는 방식
      • 리소스 URI가 일시적으로 변경하기 때문에 검색 엔진 등에서 URL을 변경하면 안된다. 
      • 302 Found : 리다이렉트시 요청 메서드가 GET으로 변할 수 있고, 본문이 제거될 수도 있다. 모호하다.
      • 303 See Other : 302와 기능은 같고 리다이렉트 요청 메서드가 GET으로 변경 (302가 기능 스펙에 대해 모호한 부분이 있다면 303은 확실하게 GET 메서드로 사용한다)
      • 307 Temporary Redirect : 302와 기능은 같으나 처음의 요청 메서드와 본문이 유지 된다 (요청 메서드 변경하면 안됨) 
      • 일시적 리다이렉션을 언제 쓸까?
        PRG패턴 (Post Redirect Get) : Post로 주문 후 새로고침할 경우 주문을 재요청하게 되여 중복 주문이 생길 수 가 있다. 이때 post로 주문하고 주문 결과 화면을 get으로 리다이렉트 하게되면 새로고침해도 결과 화면을 get요청하게 되니 중복을 피할 수가 있다. 
        PRG 사용전 새로고침하면 order가 재요청되어 5번 발생
         
        PRG 사용후 새로고침하면 했을 때는 주문 결과 화면을 GET요청하게 되어 중복 주문 방지
    • 특수 리다이렉션 - 결과 대신 캐시  
      • 304 Not Modified
        • 클라이언트에게 리소스가 수정되지 않았으니 로컬PC 캐시를 재사용하라며 캐시로 리다이렉트 시킴.
        • 304 응답은 응답에 메시지 바디를 포함하면 안된다(로컬 캐시를 사용해야 하므로).  
        • 조건부 GET, HEAD 요청시 사용

 

📍4xx (Client Error)

클라이언트 오류

  • 클라이언트의 요청에 잘못된 문법등으로 서버가 요청을 수행할 수 없을 때
  • 오류의 원인이 클라이언트에 있음
  • ❗클라이언트가 이미 잘못된 요청이나 데이터를 보내고 있어 똑같은 재시도는 몇번을 해도 실패할 수 밖에 없다. (복구 불가능)

 

  • 400 Bad Request : 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음. 요청 검토하여 다시 보내야 함
  • 401 Unauthorized : 클라이언트가 해당 리소스에 대한 인증이 필요.
    • 인증(Authentication) 되지 않음
    • 401 오류 발생시 응답에 WWW-Authenticate 헤더와 함께 인증 방법 설명을 보낸다. 
    • 참고
      • 인증 (Authentication): 본인이 누구인지 확인
      • 인가 (Authorization): 권한부여 (인증이 있어야 인가가 있다)
  • 403 Forbidden : 서버 요청 이해했지만 승인 거부, 주로 인증 자격 증명은 있으나 접근 권한이 없는 경우
  • 404 Not Found : 요청 리소스가 서버에 없어 리소스 찾을 수 없음 또는 클라이언트가 권한이 부족한 리소스에 접근 시도 했을 때 리소스를 숨기고자 하면 사용

 

📍5xx (Server Error)

서버 오류

  • 오류의 원인이 서버에 있음
  • 서버에 문제가 있어 재시도하면 성공 할 수도 있다. (복구 등으로 상태가 변하여)

 

  • 500 Internal Server Error : 서버 내부 문제로 오류 발생, 서버에서 발생한 에러가 애매한 경우 500으로
  • 503 Service Unavailavle : 서비스 이용 불가. 일시적 과부화나 예정된 작업으로 요청을 처리할 수 없을 경우를 말한다. Retry-After 헤더 필드로 복구 시기를 알 수 있다.

 

들어온 데이터 값에 대한 예외를 500처리하면 안된다. 들어온 데이터의 문제고 로직상으로는 정상 작업된 것이기 때문. 따라서 클라이언트와 서버에서 발생한 오류를 명확하게 구분할 줄 알아야한다.

📌 김영한 님의 "모든 개발자를 위한 HTTP 웹 기본 지식" 강의 듣고 정리

 

📍클라이언트에서 서버로 데이터 전송

클라이언트에서 서버로 데이터 전달하는 방식은 크게 2가지 이다.

 

1. 전달방식 

  • 쿼리 파라미터를 통한 데이터 전송
    • GET
    • 주로 정렬 필터(검색어)
  • 메시지 바디를 통한 데이터 전송
    • POST, PUT, PATCH
    • 주로 회원가입, 상품주문, 리소스 등록, 리소스 변경 등

        

2. 상황

  • 정적 데이터 조회 : 이미지, 정적 텍스트 문서
    • 조회는 GET 사용
    • 정적 데이터는 일반적으로 쿼리 파라미터 사용없이 리소스 경로만으로 조회 가능
  • 동적 데이터 조회 : 주로 검색, 게시판 목록에서 정렬 필터(검색어)
    • 쿼리 파라미터를 사용, 이를 서버에서 key-value로 꺼내어 사용
    • 조회 조건을 줄여주는 필터, 조회 결과를 정렬하는 정렬 조건에 주로 사용
    • 조회는 GET
    • GET은 쿼리 파라미터를 사용해서 데이터를 전달
  • HTML Form을 통한 데이터 전송 : 회원가입, 상품주문, 데이터 변경
    • HTML의 Form 태그를 통해 데이터를 전송하는 방식으로 form태그의 submit을 하게 되면 웹 브라우저에서 form의 데이터를 읽어 HTTP 메시지를 생성해준다. (Type = application/x-www-form-urlencoded 사용)
    • (왜 url encoded 인가? url로 사용할 수 없는 문자를 사용할 수 있도록 인코딩해서 넘겨주기 때문)
<form action="/save" method="post">
	<input type="text" name="username"/>
    <input type="text" name="age"/>
    <button type="submit">전송</button>
</form>
POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

username=kim&age=20
  • form에서 method를 get으로 지정하게 되면 GET은 보통 메시지 바디를 사용하지 않기에 쿼리 파라미터로 바꿔서 HTTP 메시지를 만든다 (GET의 메시지 바디를 지원하지 않는 곳들이 존재한다. 그리고 GET은 조회용)
GET /members?username=kim&age=20 HTTP/1.1
Host: localhost:8080
  • multipart/form-data : 파일 업로드와 같은 바이너리 데이터 전송할 때 사용하는 Content-Type
<form action="/save" method="post" enctype="multipart/form-data">
    <input type="text" name="username"/>
    <input type="text" name="age"/>
    <input type="file" name="file1" />
    <button type="submit">전송</button>
</form>

        웹 브라우저가 생성한 HTTP 메시지 : boundary 기준으로 자름 (랜덤지정)

        multipart라는 이름은 이런식으로 다른 종류의 여러 파일과 form 내용을 함께 전송이 가능해 multipart이다.

POST /save HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=---XXX

------XXX
Content-Disposition: form-data; name="username"

kim
------XXX
Content-Disposition: form-data; name="age"

20
------XXX
Content-Disposition: form-data; name="file1"; filename="intro.png"
Content-Type: image/png

109238a9o0p3eqwokjasd09ou3oirjwoe9u34ouidf...   <= 이미지에 대한 바이트 정보
------XXX--

+ 참고: HTML Form은 GET과 POST만 지원

 

  • HTTP API를 통한 데이터 전송 : 서버 to 서버, 앱 클라이언트, 웹 클라이언트(AJAX방식)
    • HTML Form을 쓰지 않고 데이터를 전송하는 경우 대부분
    • POST, PUT, PATCH : 메시지 바디를 통해 데이터 전송
    • GET : 조회, 쿼리 파라미터로 데이터 전달
    • Content-Type: application/json을 주로 사용하는 데 사실상 표준처럼 사용

 

📍HTTP API 설계 예시

 

HTTP API를 크게 두가지로 분류하여 설계할 수 있는데 하나는 POST기반의 등록 방식 "컬렉션",  다른 하나는 PUT기반의 등록방식 "스토어"다. POST와 PUT을 이용해 새로운 정보를 저장할 때 서로 다른 특징이 있는데 이를 잘 정리해야한다.

 

* HTTP API - 컬렉션

    - POST 기반 등록으로 설명

회원 목록 조회 /members GET
회원 등록 /members POST
회원 단건 조회 /members/{id} GET
회원 수정 /members/{id} PATCH, PUT, POST
회원 삭제  /members/{id} DELETE

    - 클라이언트는 등록될 리소스 URI를 모른다 > 서버가 데이터 받아 새로 등록된 리소스 URI를 생성 (ex. /members/100)

    - 이러한 형식을 컬렉션(Collection) 이라고 한다. 

      서버가 관리하는 리소스 디렉토리를 말하며 서버가 리소스 URI를 생성하고 관리한다. 여기서는 /members가 컬렉션!

    - 일반적으로 이 방식을 많이 사용

 

    

* HTTP API - 스토어

    - PUT 기반 등록으로 설명

파일 목록 조회 /files GET
파일 등록 /files/{filename} PUT (업로드할 파일 이름을 알고 있기 때문, 동일한 이름 있으면 대체 없으면 생성)
파일 단건 조회 /files/{filename} GET
파일 삭제  /files/{filename} DELETE
파일 대량 등록 /files POST (등록에 PUT을 사용했으므로 POST는 필요에 따라 임의 지정)

    - 클라이언트는 등록될 리소스 URI를 알아야 한다. > 클라이언트가 직접 리소스 URI를 지정해 관리

    - 이러한 형식을 스토어(Store) 라고 한다. 

      클라이언트가 관리하는 리소스 저장소를 말하며 클라이언트가 리소스 URI를 알고 관리. 여기서는 /files가 스토어!

 

 

* HTML FORM 사용

    - HTML FORM은 GET, POST만 지원 (AJAX 같은 기술을 사용해 이런 제약 해결 가능)

    - 여기서는 순수 HTML + HTML FORM 이야기할 것으로 GET과 POST만 지원하므로 제약이 있다. 

회원 목록 조회 /members GET
회원 등록 폼 /members/new GET (등록 폼을 조회, submit 하면 아래 회원 등록 POST로)
회원 등록 /members
/members/new
POST (두가지 선택 가능, 컬렉션처럼 리소스 기준 or 등록 폼을 따르는)
회원 단건 조회 /members/{id} GET
회원 수정 폼 /members/{id}/edit GET (수정 폼 및 데이터를 조회, submit 하면 아래 회원 수정 POST로)
회원 수정 /members/{id}
/members/{id}/edit
POST
회원 삭제  /members/{id}/delete POST (/members/{id} 로 POST를 사용할 수 없으면 컨트롤 URI를 사용)

    - 컨트롤 URI

      GET, POST만 지원하므로 제약이 있어 이걸 해결하기 위해, "동사"로 된 리소스 경로를 사용

      POST의 /new, /edit, /delete 가 컨트롤 URI에 해당한다. 

      제약 해결 뿐만 아니라 HTTP 메서드로 해결하기 애매한 경우에도 사용한다(HTTP API 포함).
      최대한 리소스개념을 사용하되 그게 힘들 경우 대체제로 사용!

 

📍참고하면 좋은 URI 설계 개념

* 문서 (document)

  • 단일 개념 (파일 하나, 객체 인스턴스, 데이터베이스 row)
  • ex) /members/100, /files.star.jpg

* 컬렉션 (collection)

  • 서버가 관리하는 리소스 디렉토리
  • 서버가 리소스의 URI를 생성하고 관리
  • ex) /members

* 스토어 (store)

  • 클라이언트가 관리하는 리소스 저장소
  • 클라이언트가 리소스의 URI를 알고 관리
  • ex) /files

* 컨트롤러 (controller), 컨트롤 URI

  • 문서, 컬렉션, 스토어로 해결하기 어려운 추가 프로세스 실행
  • 동사를 직접 사용
  • ex) /members/{id}/delete
 

REST API - URL Naming Conventions

In REST, having a strong and consistent REST resource naming strategy – will prove one of the best design decisions in the long term.

restfulapi.net

 

 

📌 김영한 님의 "모든 개발자를 위한 HTTP 웹 기본 지식" 강의 듣고 정리

 

📍HTTP API를 만들어보자

 

*API URI설계 예시 (이게 좋을 URI 설계일까?)

회원 목록 조회 /read-member-list
회원 조회 /read-member-by-id
회원 등록 /create-member
회원 수정 /update-member
회원 삭제  /delete-member

No!

 

API URI에서 가장 중요한 건 리소스 식별이다.

 

리소스란 무엇일까?

회원을 조회하고 수정하는 것이 리소스가 아니라 "회원"이라는 개념자체가 리소스이다. 

그럼 이걸 어떻게 식별할 까?

행위을 배제하고 "회원"이라는 리소스만 식별하여 URI에 매핑하면 된다. 

 

리소스 식별과 URI 계층 구조 활용해 다시 설계해보자

(* 계층 구조상 상위를 컬렉션으로 보고 복수단어 사용을 권장하고 있다 (member -> members))

회원 목록 조회 /members
회원 조회 /members/{id}
회원 등록 /members
회원 수정 /members/{id}
회원 삭제  /members/{id}

???? 그럼 똑같은 것이 4개나 생기게 된다. 구분이 안되는 데 이때 이를 구분하게 하는게 바로 HTTP 메서드이다.

URI는 리소스만 식별하고 행위는 모두 HTTP 메서드로 구분하게 된다. (리소스는 명사, 행위는 동사)

 

 

📍HTTP 메서드 - GET, POST

HTTP 메서드라는 건 클라이언트가 서버에게 요청할 때 기대하는 행동이다.

주로 사용되는 메서드에 대해 미리 간략하게 보자면

  • GET : 리소스 조회
  • POST : 요청 데이터 처리, 주로 등록에 사용
  • PUT : 리소스를 대체, 해당 리소스 없으면 생성
  • PATCH : 리소스 부분 변경
  • DELETE : 리소스 삭제

기타 메서드들도 보면

  • HEAD : GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환
  • OPTIONS : 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명 (주로 CORS에서 사용한다)
  • CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정
  • TRACE :  대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행

이 있다.

 

* GET

  • 리소스 조회 (path에 있는 자원을 주세요!)
  • 검색 등으로 서버에 전달하고 싶은 데이터는 query(query parameter, query string)를 통해 전달한다.
  • 메시지 바디를 사용해서 데이터를 전달할 수도 있지만, 지원하지 않는 곳이 많아 권장하지 않는다
GET /members/100
Host: localhost:8080

서버는 이 메시지를 받으면 /members의 100번을 달라는 말로 해석하여 처리하여 아래 처럼 Response 메시지로 보내게 된다.

HTTP/1.1 200 OK
Content-Type: application/json
Content-length: 34

{
   "username" : "young",
   "age" : 20
}

 

* POST

  • 요청 데이터를 처리
  • 클라이언트는 메시지 바디를 통해 서버로 요청 데이터를 전달
  • 서버는 요청 데이터를 처리 (메시지 바디를 통해 들어온 데이터를 처리하는 모든기능 수행)
POST /members HTTP/1.1
Content-Type: application/json

{
    "username" : "young",
    "age" : 20
}

응답 (members 100번에 해당 자원이 신규 등록되었다)

HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 34
Location: /members/100

{
    "username" : "young",
    "age" : 20
}

 

요청 데이터를 어떻게 처리한다는 뜻일까?

POST 메서드는 요청 데이터를 어떻게 처리할지 리소스 URI에 리소스마다 따로 정한다. (정의하기 나름)

크게 묶어보자면

  • 서버가 아직 식별하지 않은 새 리소스 등록(조회)
  • 요청 데이터 처리 (데이터 생성, 수정 외에도 프로세스 처리 경우도 있음)
  • 다른 메서드로 처리 애매한 경우 (GET 메서드를 허용하지 않는 경우도 있음)

 

📍HTTP 메서드 - PUT, PATCH, DELETE

 

* PUT

  • 리소스 대체 (리소스가 있다면 완전히 대체, 없다면 생성)
  • 클라이언트가 리소스를 식별
    • Post는 그냥 /members 에서 끝났다면 Put 경우 /members/100 으로 구체적인 리소스 경로를 식별
PUT /members/100 HTTP/1.1
Content-Type: application/json

{
   "username" : "old",
   "age" : 50
}

서버에 해당 리소스 없으면 추가, 있으면 해당 값으로 대체한다.

/members/100 의 기존값 /members/100 의 변경값 /members/100 의 결과값
{
    "username" : "young",
    "age" : 20
}
{
   "username" : "old",
   "age" : 50
}
{
   "username" : "old",
   "age" : 50
}

이때 완전히 대체한다는 말을 주의해야 한다. 

기존 값 가운데 일부만 변경하고 싶어 PUT을 사용하면

/members/100 의 기존값 /members/100 의 변경값 /members/100 의 결과값
{
    "username" : "young",
    "age" : 20
}
{
   "age" : 50
}
{
   "age" : 50
}
  나이만 바꿀래! 😱 ??????

이런 결과가 나온다. 왜냐면 일부 변경이 아니라 완전 대체이기 때문. 기존 걸 지워버리고 바꿔주려는 값으로 대체해버린다.

위와 같은 의도로 사용하고 싶다면 PATCH를 이용하면 된다.

 

* PATCH

  • 리소스 부분 변경
PATCH /members/100 HTTP/1.1
Content-Type: application/json

{
   "age" : 50
}
/members/100 의 기존값 /members/100 의 변경값 /members/100 의 결과값
{
    "username" : "young",
    "age" : 20
}
{
   "age" : 50
}
{
   "username" : "young",
   "age" : 50
}
  나이만 바꿀래! 😊👍

+ PATCH가 지원이 안된다면 POST사용!

 

* DELETE

  • 리소스 제거
DELETE /members/100
Host: localhost:8080

 

📍HTTP 메서드의 속성

 

* 안전 (Safe Methods)

  • 호출해도 리소스를 변경하지 않는다. (안전은 해당 리소스만 고려함)

* 멱등 (Idenmpotent Methods)

  • 한번 호출하든 두번 호출하든 100번 호출하든 결과가 똑같다. 
  • 멱등 메서드 ("같은 데이터, 같은 파일에 대한 같은 요청"을 할때를 생각해볼것)
    • GET : 한 번 조회하든, 두번 조회하든 같은 결과를 조회
    • PUT : 결과를 대체한다. 따라서 같은 요청을 여러번 해도 최종 결과는 같다
    • DELETE : 결과를 삭제 여러번 삭제해도 결국 삭제되는 같은 결과가 나온다
    • POST ? POST는 멱등이 아니다. 같은 요청을 해도 때마다 프로세스를 진행할 수도 있어 중복이 될 수 있기 때문
더보기

+ 멱등성 (idempotent) 

멱등성의 멱(冪)은 거듭제곱이란 뜻이 있으며 등(等)은 같다는 뜻을 가지고 있다. 

거듭해도 같다는 뜻으로 수학에서는 연산을 여러번 적용하더라도 결과가 달라지지 않는 성질을 의미하며, 웹에서는 동일한 요청을 한 번 보내는 것과 여러번 연속으로 보내는 것이 같은 효과를 지니고, 서버의 상태에도 동일하게 남을 때, 해당 HTTP 메소드가 멱등성을 가졌다고 한다.

 

멱등이란 개념이 왜 필요할까?

자동 복구 메커니즘에 사용이 가능하다. 

- 서버가 정상 응답을 못주었을 때, 클라이언트가 같은 요청을 다시 해도 되는지 판단의 근거

 

어? 그럼 재요청 사이에 외부에서 그걸 바꿔버린다면 결과가 달라질텐데?

멱등은 외부요인까지 고려하지 않는 개념이다. 내가 동일한 요청을 했을때 만을 판단

 

* 캐시가능 (Cacheable Methods)

  • 응답 결과 리소스를 캐시해서 사용해도 되는가? 
  • GET, HEAD, POST, PATCH 캐시가능 (실제로는 GET, HEAD 정도만 캐시 사용, POST, PATCH는 구현이 쉽지 않기 때문)

 

+ Recent posts