HTTP API
작성일
HTTP API
API URI 설계
리소스
URI를 설계시 가장 중요한 것은 리소스 식별
이다. 리소스란 동작을 제외한 자원
그 자체를 리소스
라 한다.
리소스를 식별하는 방법은 리소스 자체에만 집중하여 리소스를 URI에 매핑한다.
예를 들어, 회원과 관련된 HTTP API를 만들어본다고 가정하면 회원이라는 개념 자체가 리소스이다.
그리고 회원 리소스를 식별하는 URI는 아래와 같다. 이렇게 URI를 작성하고 조회, 수정, 삭제를 구분할 때는 HTTP 메서드로 구분한다.
- 회원 목록 조회: /members
- 회원 조회: /members/{id}
- 회원 등록: /members
- 회원 수정: /members/{id}
- 회원 삭제: /members/{id}
리소스와 행위를 분리
가장 중요한 것은 리소스를 식별하는 것이다. 따라서, URI는 리소스만 식별한다. 즉, 리소스와 해당 리소스를 대상으로 하는 행위를 분리한 것이다. 회원으로 예를 들면 리소스는 회원이고 행위는 조회, 등록, 삭제, 변경이다. 앞에서도 말했듯이 행위는 HTTP 메서드로 구분한다. (GET, POST, PUT, PATCH, DELETE…)
참고
계층 구조상 상위를 컬렉션으로 보고 복수단어 사용 권장(member → members)
참고 Query String vs Path Variable
Query String: /members?id=123 Path Variable: /members/123 Query String과 Path Variable 둘 다 리소스를 식별하기 위해 사용한다. query는 주로 비계층 구조로 된 정보를 포함하고 path는 계층 구조로 된 정보를 포함한다. 관례적으로 리소스를 식별할 때는 path variable 처럼 사용하고 검색조건의 경우에 query string을 사용한다.
HTTP 메서드
HTTP 메서드 종류 중 주요 메서드는 GET, POST, PUT, PATCH, DELETE
가 있다.
GET
GET은 리소스를 조회
할 때 사용한다. 서버에 전달하고 싶은 데이터는 query(쿼리 파라미터, 쿼리 스트링)를 통해서 전달한다. 메시지 바디를 사용해서 데이터를 전달할 수 있지만, 지원하지 않는 곳이 많아서 권장하지 않는다.
POST
POST는 요청 데이터를 처리할 때 주로 사용한다. 메시지 바디를 통해 서버로 요청 데이터 전달하면 서버가 메시지 바디를 통해 들어온 데이터를 처리하는 모든 기능을 수행한다.
또한, 주로 전달된 데이터로 신규 리소스 등록, 프로세스 처리에 사용한다.
신규 리소스 등록
서버가 아직 식별하지 않은 새 리소스 생성한다.
프로세스 처리
단순히 데이터를 생성하거나 변경하는 것을 넘어서 프로세스를 처리해야 하는 경우이다. 예를 들어 주문에서 ‘결제완료 -> 배달시작 -> 배달완료’처럼 단순히 상태 값 변경을 넘어 프로세스의 상태가 변경되는 경우에도 POST를 사용한다.
다른 메서드로 처리하기 애매한 경우
애매하면 POST를 사용하면 된다. 예) JSON으로 조회 데이터를 넘겨야 하는데, GET 메서드를 사용하기 어려운 경우
참고 GET과 POST의 차이점?
클라이언트에서 서버로 요청을 할 때, GET은 보통 (메시지 바디에) 데이터를 전달하지 않는다. 하지만, POST는 클라이언트에서 서버로 데이터를 전달하고 서버에게 처리해달라고 요청한다.
참고
Created(201)로 보낼 땐, 응답 데이터에 Location(신규 리소스 식별자)를 포함해 클라이언트에 전달한다.
참고 POST vs PATCH
POST를 사용하는 경우 중에서 프로세스를 처리할 때도 POST를 사용한다고 했다. 이때, 값이 변경되는 것이니 PATCH를 써도 되지 않나 하는 의문이 들었다. 그러나, 이 경우는 단순히 데이터를 생성, 변경하는 것을 넘어 내부에서 매우 큰 프로세스가 실행된다. 이렇게 해당 리소스만 변경하는것이 아닌 내부 프로세스를 실행해야 할 때는 PATCH 보다 POST를 사용하는 것이 좋다.
PUT
PUT은 리소스를 대체한다. 해당 리소스가 있으면 완전히 대체(주의)하고 없으면 생성한다. 쉽게 이야기 하면 덮어쓰기 한다.
POST와 PUT의 큰 차이점은 PUT은 클라이언트가 리소스를 식별한다는 것이다. POST는 클라이언트가 리소스의 위치를 모른다.(POST /members) 하지만, PUT은 클라이언트가 리소스 위치를 알고 URI 지정한다.(PUT /member/100)
PATCH
PATCH는 리소스를 부분 변경한다. 만약, PATCH를 지원하지 않는다면 POST를 사용하면 된다. (물론 요즘은 대부분 지원함)
DELETE
DELETE는 리소스를 삭제한다.
참고
리소스가 최근에는 Representation(표현)으로 변경되었다.
HTTP 메서드 종류 중 기타 메서드는 아래와 같다.
- HEAD: GET과 동일하지만 메시지 부분을 제외하고 상태 줄과 헤더만 반환
- OPTIONS: 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
- CONNECT, TRACE도 있지만 거의 사용하지 않음
HTTP 메서드의 속성
안전(Safe Methods)
호출해도 리소스를 변경하지 않는다. GET의 경우가 이에 해당되고 POST, DELETE 등은 해당되지 않는다. (바뀌면 안전하지 않음)
참고
여기서 안전이란 단어의 관점은 리소스의 상태가 변경되냐 안되냐의 차이이다.
멱등(Idempotent Methods)
멱등이란 한 번 호출하든 두번 호출하든 100번 호출하든 결과가 똑같다는 뜻이다. 멱등 메서드에는 GET, PUT, DELETE가 해당된다. POST는 멱등이 아니다. (두 번 호출하면 같은 결제가 중복해서 발생할 수 있음)
멱등을 활용하는 경우는 자동 복구 메커니즘에서 사용한다. 예를 들어 서버가 TIMEOUT 등으로 정상 응답을 못주었을 때, 클라이언트가 같은 요청을 다시 해도 되는지에 대한 부분을 판단하는 근거가 된다.
캐시가능(Cacheable Methods)
중요한 내용이다. 응답 결과 리소스를 캐시해서 사용해도 되는 메서드는 GET, HEAD, POST, PATCH 이다. 실제로는 GET, HEAD 정도만 캐시로 사용한다. POST, PATCH를 캐시해서 사용하지 않는 이유는 본문 내용까지 캐시 키로 고려해야 하는데, 구현이 쉽지 않기 때문이다.
참고
캐시를 이해하는 중요한 키는 데이터의 변경 관점이다. 캐시는 원본 데이터가 변경되지 않고 유지되어야 하는데 POST, PUT, DELETE, PATCH로 데이터를 변경하게 되면 원본 데이터가 변경되기 때문에 캐시를 유지하기가 어렵다. 따라서, 데이터를 변경할 가능성이 있는 POST, PUT, DELETE, PATCH는 대부분의 구현에서 캐시를 유지하지 않는다.
클라이언트에서 서버로 데이터 전송
정적 데이터 조회
이미지, 정적 텍스트 문서를 전송한다. GET을 사용하여 조회하며 쿼리 파라미터를 사용하지 않는다. 정적 데이터는 일반적으로 쿼리 파라미터 없이 리소스 경로로 단순하게 조회할 수 있다.
동적 데이터 조회
쿼리 파라미터(?q=hello&hl=ko) 사용하여 데이터를 전달하고 조회한다. 주로 검색, 게시판 목록에서 정렬 필터(검색어)에서 사용한다.
HTML Form 데이터 전송
HTML Form 데이터 전송에는 POST 전송, GET 전송, multipart/form-data 3가지가 있다.
POST 전송 - 저장
주로 회원 가입, 상품 주문 등에서 사용한다. (Content-Type: application/x-www-form-urlencoded) form의 내용을 메시지 바디를 통해서 전송한다. (key=value 형식, 쿼리 파라미터 형식) 이때, 전송 데이터를 url encoding 처리한다. (예. abc김 -> abc%EA%B9%80)
참고 인코딩하는 이유?
웹 브라우저와 웹 서버는 서로 다른 인코딩 방식을 가지므로 적절한 인코딩 설정이 필요하다.
GET 전송 - 저장
GET 방식으로 form 데이터를 전송하면 URL이 쿼리 파라미터 형식으로 서버에 들어온다. 단, form 데이터를 사용할 때 GET은 조회에만 사용해야 한다. 리소스 변경(저장 등)이 발생하는 곳에서 사용하면 안된다.
multipart/form-data
파일 업로드 같은 바이너리 데이터 전송시 사용한다. 다른 종류의 여러 파일과 폼의 내용 함께 전송 가능하다. (Content-Type: multipart/form-data)
참고
HTML Form 전송은 GET, POST만 지원한다.
HTTP API 데이터 전송
HTTP API 데이터 전송은 서버 to 서버(백엔드 시스템 통신), 앱 클라이언트(아이폰, 안드로이드), 웹 클라이언트(ajax, React, Vue 같은 웹 클라이언트와 API 통신)에서 사용한다. Content-Type: application/json
을 주로 사용한다. (사실상 표준)
GET은 조회, 쿼리 파라미터로 데이터 전달하고 POST, PUT, PATCH는 메시지 바디를 통해 데이터를 전송한다.
HTTP API 설계 예시
HTTP API 설계에는 컬렉션
을 사용하는 POST 기반 등록, 스토어
를 사용하는 PUT 기반 등록, 컨트롤 URI
를 사용하는 HTML FORM 기반 등록 방법이 있다.
POST 기반 등록
- 회원 목록 /members → GET
- 회원 등록 /members → POST
- 회원 조회 /members/{id} → GET
- 회원 수정 /members/{id} → PATCH(default), PUT(게시글을 수정할 때), POST
- 회원 삭제 /members/{id} → DELETE
POST
로 신규 자원을 등록할 때의 특징은 클라이언트는 등록될 리소스의 URI를 모른다는 것이다. 서버가 데이터를 저장하고 새로 등록된 리소스 URI
를 생성해준다. 즉, 응답메시지에 식별자를 Location에 담아(Location: /members/100) 클라이언트에 전달한다.
이런 형식을 컬렉션(Collection)
이라 한다. 즉, 서버가 관리하는 리소스 디렉토리에 서버가 리소스의 URI를 생성하고 관리하는 것이다. 여기서 컬렉션은 /members이다.
PUT 기반 등록
- 파일 목록 /files → GET
- 파일 조회 /files/{filename} → GET
- 파일 등록 /files/{filename} → PUT(파일 업로드의 경우엔 PUT이 딱 맞다. 없으면 생성해야 하고 있으면 더덮어쓰기 해야하므로..)
- 파일 삭제 /files/{filename} → DELETE
- 파일 대량 등록 /files → POST
PUT
로 신규 자원을 등록할때의 특징은 클라이언트가 리소스 URI를 알고 있어야 한다. 즉, 클라이언트가 직접 리소스의 URI를 지정한다.
이런 형식을 스토어(Store)
라 한다. 즉, 클라이언트가 관리하는 리소스 저장소에 클라이언트가 리소스의 URI를 알고 관리하는 것이다. 여기서 스토어는 /files이다.
중요 POST, PUT 기반 신규 자원 등록의 차이점
API를 설계할때 크게 두가지로 분류할 수 있다. 첫번째가POST 기반
의 등록을 하는 것이고컬렉션
이라 하고 두번째는PUT 기반
의 등록을 하는 것이고스토어
라 한다. 이 둘의 차이점은 서버가 리소스 관리를 하는지(컬렉션), 클라이언트가 리소스 관리를 하는지(스토어)의 차이이다. 대부분 컬렉션을 사용하고 파일 업로드 같은 경우엔 스토어를 사용하기도 한다.
HTML FORM 사용
- 회원 목록 /members → GET
- 회원 등록 폼 /members/new → GET
- 회원 등록 /members/new, /members → POST
- 회원 조회 /members/{id} → GET
- 회원 수정 폼 /members/{id}/edit → GET
- 회원 수정 /members/{id}/edit, /members/{id} → POST
- 회원 삭제 /members/{id}/delete → POST
HTML FORM은 GET, POST만 지원한다. AJAX 같은 기술을 사용해서 해결 가능(회원 API 참고)하지만 여기서는 순수 HTML, HTML FORM 이야기를 하겠다.
HTML FORM은 GET, POST만 지원하므로 제약이 있다. 이를 해결하기 위해 컨트롤 URI
를 사용한다. 컨트롤 URI는 /new, /edit, /delete와 같이 동사
로 된 리소스 경로를 사용한다. HTTP 메서드로 해결하기 애매한 경우에 사용한다. (HTTP API 포함)
참고 컨트롤 URI
URI를 리소스만 가지고 설계하기 힘들 때 URI에 동사를 넣어 설계하는 것을 컨트롤 URI라 한다. 실무에서 정말 많이 사용된다. 하지만, 최대한 리소스란 개념을 가지고 설계하고 그 상황에서 안될 때 대체제로 컨트롤 URI를 사용한다고 이해하자.