ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • HTTP 메서드 속성을 알아보자
    Protocol/HTTP 2023. 12. 23. 00:35

    들어가기 전에

    개발자로서 정말 많이 사용하는 네트워크 통신규약 HTTP 이지만, 기술의 깊이는 정말 단순하지 않다. 지금까지 사용해왔고 앞으로도 사용할 HTTP 메서드를 알아보자.

     

     

    HTTP 메서드 속성

    서버 API를 설계할 때 HTTP 복구 메커니즘, 캐시 최적화를 고려했을 때 로직이 달라질 수 있다.

     

    HTTP 메서드의 3가지 속성인 안전(Safe), 멱등(Idempotent), 캐시 가능(Cacheable)이 있다.

     

     

     

    Safe methods

    HTTP 스펙상 GET, HEAD, OPTIONS, TRACE 메서드는 읽기 전용으로 만들어졌기 때문에 안전한 방법이다.

    여기서 말하는 안전성은 메서드를 호출해도 리소스가 변경되지 않는 성질을 말한다. GET 메서드는 단순히 데이터를 조회하는 기능을 수행하고 리소스를 변경 및 수정하지 않으니 안전하다고 할 수 있다.

     

    반면, POST, PUT, PATCH, DELETE 메서드는 호출할 경우 데이터에 변경이 발생하거나 서버에서 삭제되기 때문에 안전하지 않다.

     

    ✅ 여기서 잠깐!
    트래픽이 몰려 수많은 GET 요청으로 서버가 터진다면 '안전성' 이라는 특성에 부합하지 않는다! 라고 할 수 있지만 여기서 말하는 안정성은, 리소스를 수정/삭제 하지 않으므로 데이터 일관성 유지에 있어 안전하다는 뜻이다.

    즉, 메서드가 전체 시스템 장애로부터 안전하다는 의미를 가지지 않는다.

     

    Idempotent methods

    멱등이라는 단어의 뜻을 먼저 알아보자.

    멱등(冪等): 연산을 여러 번 적용하더라도 결과값이 달라지지 않는 일.

     

    HTTP 멱등성이란, 요청(Request)을 한 번 호출하든 여러 번 호출하든 그 결과가 같음을 의미. 동일한 요청을 한번 보내는 것과 여러번 연속으로 보내는 것이 같은 효과를 가지고, 서버의 상태도 동일할 때 해당 HTTP 메서드가 멱등성을 가진다고 할 수 있다.

     

    ✅ 여기서 잠깐!
    멱등의 개념을 안전(Safe)과 혼동하지 말자.
    - HTTP 메서드의 안전은 한 번을 호출하든 여러 번을 호출하든 리소스에 수정이 발생하지 않는 속성
    - HTTP 메서드의 멱등은 리소스에 수정이 발생해도 메서드를 여러 번 실행한 결과가 한 번 실행한 결과와 같다면 만족하는 속성

     

    호출을 실행한 결과가 의미하는 것이 응답 상태 코드가 아닌 서버의 상태라는 점도 유의한다.

     

    HTTP 스펙에 명시된 것에 의하면 GET, PUT, DELETE는 멱등성을 가지도록, POST와 PATCH는 멱등성을 가지지 않도록 구현한다.

     

     

    GET 멱등

    GET은 데이터를 한 번 조회하든 여러 번 조회하든 같은 결과가 조회되므로 안전과 멱등을 동시에 만족한다.

     

    올바른 GET API 예시

    - GET /post/1 요청
    - 서버에서 id값이 1인 게시글 조회
    - 해당 게시글 데이터 응답

     

    위 경우 같은 요청을 여러번 보내더라도 서버의 상태는 항상 같다.

     

    멱등적이지 않은 GET API 예시

    개발자가 조회수 기능을 추가해 다음과 같이 게시글 조회하면 동시에 조회수도 올리도록 구현했다면?

    - GET /post/1 요청
    - 서버에서 id값이 1인 게시글 조회
    - 해당 게시글의 조회수 데이터 1 증가
    - 해당 게시글 데이터 응답

     

    위 경우 GET 요청을 여러번 보낼 경우 서버 데이터 상태는 매번 바뀐다. 멱등성을 가지지 않는 것이며, 개발자는 HTTP 스펙에 부합하지 않게 API를 구현했다고 볼 수 있다.

     

    따라서 GET 멱등성에 맞게 API를 설계하기 위해서는 조회수 값을 증가시키는 요청을 PATCH 요청으로 따로 분리하는 것이 옳다.

     

    멱등하다고 결과가 항상 같지 않다

    GET이 멱등성을 만족하기에 여러 번 요청할 경우 항상 같은 결과물을 내놓는다고 했지만, 엄밀히 말하면 틀리다.

     

    A가 GET /members/100 통해 리소스를 조회
    B가 PUT /members/100 통해 리소스를 변경
    A가 GET /members/100 통해 리소스를 다시 조회

     

    위 상황은 A가 100번째 멤버에 대한 리소스 조회를 재요청 하기 직전, B가 해당 리소스를 변경한 경우이다.

     

    이때 A가 멱등성이 있는 GET 메서드를 사용함에도 불구하고, 중간에 B의 개입으로 응답받는 결과 값이 달라지게 된다.

     

    그럼 멱등성이 깨진것이냐? 아니다! 멱등성의 여부는 외부 요인으로 중간에 리소스가 변경되는 것은 고려하지 않고, 또한 서버의 상태 기준으로 판단하기 때문에 GET의 멱등성을 문제가 없다.

     

    서버의 상태를 바꾼건 PUT 메서드이지 GET 메서드는 아니기 때문이다.

     

     

    DELETE 멱등

    GET이 단순 조회라면, DELETE는 단순 삭제이다. DELETE를 처음 요청 하면, 서버에서 해당 리소스는 삭제 된다. 이후 DELETE를 여러번 요청하더라도 해당 리소스는 삭제된 상태 그대로이니 서버의 상태는 변하지 않아 DELETE는 멱등성을 가진다.

     

    멱등적이지 않는 DELETE API 예시

    해당 API 설계할 때에는 정확한 식별자를 통해 리소스를 지정해야 한다.

     

    게시글을 삭제할 때 정확한 게시글 id값이 아닌 last로 명시해 서버에서 처리되도록 구현한다면
    DELETE /posts/last

    이런 경우 해당 DELETE 요청을 여러번 보내게 되면 매번 마지막 게시글을 삭제하기 때문에 매번 서버의 상태가 변한다.
    따라서 해당 API는 HTTP 스펙상 옳지 않고 멱등성을 가지지 않는 POST를 쓰는게 스펙상 옳다.
    POST /posts/last

     

     

    POST 멱등

    POST는 서버로 데이터를 전송하여 새로운 자원을 생성하는 역할을 하고 요청을 여러 번 보내는 경우 매번 새로운 자원이 생겨난다. 이는 서버의 상태가 변경되는 것을 의미 멱등을 만족하지 않는 것이다.

     

    복귀 메커니즘에 따져야 하는 멱등성

    클라이언트가 서버에게 HTTP 메시지 전송 후 TIMEOUT과 같은 문제들로 인해 정상적인 응답을 전달받지 못했다.

     

    정상적인 응답을 못 받으면 다시 요청해! 하겠지만 HTTP 멱등성의 유무를 생각해보면 생각이 달라진다.

     

    GET, DELETE 같은 메서드들은 여러 번 호출해도 응답 결과는 변함이 없기에 통신 장애 시 똑같은 요청을 재전송하도록 설계해도 문제 없다. 하지만, POST 같은 멱등하지 않게 설계된 메서드들은 똑같은 요청을 다시 전송할 경우 문제 발생 가능성이 있다.

     

    만약 서버에서 정상 처리했는데 응답 과정에서 네트워크 문제로 정상적인 응답을 받지 않았다면, POST를 재요청 하게되면 서버 입장에선 동일한 처리를 한번 더 하게 된다.

     

    이것이 돈과 관련된 결제 서비스라면 결제가 중복으로 되는 큰 문제로 직결된다. 따라서 POST 요청 같은 경우 재요청이 아닌 다른 조치를 취할 수 있도록 설계하는 것이 적절하다

     

     

    PUT 멱등

    PUT 메서드는 대상 리소스를 덮어씌우거나, 대상 리소스 없다면 새로 추가한다. 그래서 대상 리소스 없다면 PUT, POST와 같은 동작을 한다.

     

    ✅ 여기서 잠깐!
    대상 리소스가 없을 때 동작이 조금 달란진다. POST는 매번 새로운 자원을 만드는 반면, PUT은 해당 자원이 이미 있다면 데이터만 덮어쓴다.

    따라서 요청을 한번하든 여러번하든 결국 서버의 상태는 같아지니, PUT은 멱등하다.

     

    멱등하다고 결과가 항상 같지 않다

    GET의 멱등 부분에서 확인한 것처럼 PUT도 예외 케이스가 있다.

    PUT 요청을 보냈는데, 새로운 데이터를 생성한 경우 201(Created), 기존 데이터 덮어쓴 경우 200(OK) 혹은 204(No Content)를 응답한다.

     

    200 OK 요청이 올바르게 수행됨 (GET, PUT)
    201 Created 서버가 새로운 리소스를 생성함 (POST, PUT)
    204 No Content 응답할 데이터가 없음 (HTTP Body 없음. DELETE, PUT)

     

    다른 상태 코드를 응답한다고 멱등하지 않다는 것이 아니다. 서버의 상태는 동일하므로 멱등성을 가진다고 할 수 있다

    다만, 동일한 요청을 여러번 보내도 상태 코드라는 결과 값이 다를 수 있다는 점이 중요하다.

     

     

    PATCH 멱등

    PUT이 리소스 전체 교체라면, PATCH는 리소스의 부분적인 수정이다.

    PATCH는 멱등성을 가지지 않는 메서드인데, PUT과 동일한 방식으로 할 경우 멱등성을 가지게 되는 특성이 있다.

     

    ✅ 여기서 잠깐!
    PATCH는 멱등하게 설계할 수도 있고 멱등하지 않도록 설계할 수 있다.

    PUT과 PATCH 메서드의 진짜 중요 차이점은 전체 교체, 일부 교체 행위의 차이가 아니다. PUT 메서드는 반드시 멱등성을 보장하지만 PATCH 메서드는 멱등성을 보장하지 않을 수 있다는 것이다.

     

    멱등적인 PATCH API 예시

    PATCH 메서드에 수정할 리소스의 일부분만 담아서 보내는 경우 멱등성이 보장된다.

     

    // 기존 리소스
    {
      id: 1,
      name: "신노스케",
      age: 5
    }
    
    // 전송된 요청
    PATCH /users/1
    {
      age: 7
    }
    
    // 변경 리소스
    {
      id: 1,
      name: "신노스케",
      age: 7
    }

     

     

    멱등적이지 않은 PATCH API 예시

    PATCH 특징으로 HTTP 스펙상 구현 방법에 제한이 없다는 것이다. 때문에 위 예시처럼 데이터를 '대체'하도록 구성할 필요는 없다.

     

    그래서 동일한 요청을 여러번 보내면, 요청마다 age가 1씩 증가하도록 PATCH API를 설계 했다면 요청이 발생할 때마다 age 값이 계속 달라지니 멱등성을 가지지 않게 된다.

     

    // 기존 리소스
    {
      id: 1,
      name: "신노스케",
      age: 5
    }
    
    // 전송된 요청
    PATCH /users/1
    {
      age: {
        type: increment,
        value: 1
      }
    }
    
    // 변경 리소스
    {
      id: 1,
      name: "신노스케",
      age: 6
    }

     

     

    Cacheable

    캐시 가능성은 응답 결과 리소스를 캐싱해서 효율적으로 사용할 수 있는 여부이다.

     

    캐시(Cache)가 운영체제나 서버에만 있는 것이 아닌, 브라우저 자체도 하나의 소프트웨어라 캐시 공간을 가지고 있는데, 클라이언트가 서버에 한 번 요청했던 데이터를 다시 전송할 필요가 없도록 브라우저가 임시적으로 데이터를 보관하는 장소이다.

     

    1. 처음 웹사이트에 접속할 때 서버로부터 리소스를 모두 받는다
    Browser <-> HTTP Request-Response Exchange <-> Web Site

    Web site resources
    - index.html
    - /images/img.png
    - /js/script.js
    - /css/style.css

    2. 웹사이트를 재방문 할 경우 캐시에서 리소스를 가져온다
    Browser <-> HTTP Request-Response Exchange <-> Web Site

    Web site resources
    - index.html

    Cache resources
    - img.png
    - script.js
    - style.css

     

    HTTP 스펙상 GET, HEAD, POST 메서드는 캐시가 가능하다.

     

    하지만 실제 사용은 GET, HEAD 정도만 캐시로 이용하고 POST는 지원되지 않는게 일반적이다.

     

    왜냐하면, 브라우저 캐시를 이용하면 원본 데이터가 변경되지 않고 유지되어야 하는데, POST, PUT, DELETE, PATCH는 데이터 변경이 되는 메서드라서 호출로 데이터가 변경되면 원본 데이터 또한 변경되기에 캐시 데이터 불일치 문제가 생긴다.

     

    따라서 GET,HEAD 메서드는 캐시를 이용해 브라우저에서 리소스를 임시로 보관하고, 나머지 메서드들은 구현의 복잡성과 유지의 어려움 때문에 캐시를 이용하지 않는다.

    'Protocol > HTTP' 카테고리의 다른 글

    Handshake failed due to invalid Upgrade header: null 알아보자  (0) 2023.12.12
Designed by Tistory.