Today I Learned_220829
HTTP
HTTP 메서드
- HTTP API 설계
- GET, POST
- PUT, PATCH, DELETE
- HTTP 메서드의 속성
HTTP API 설계
API URI 설계
리소스 식별 -> 가장 중요함
행위 자체가 리소스가 될 수는 없다.
예시 : 회원 조회 기능 -> 리소스는 회원을 조회하는 기능이 아니라 회원 그 자체
리소스를 url에 매핑하자
행위에 기반한 URI 설계 -> 리소스 기반 URI 설계
- 회원 목록 조회 /read-member-list -> /members (계층 구조상 상위를 컬렉션으로 보고 복수 단어 사용 권장)
- 회원 조회 /read-member-by-id -> /members/{id}
- 회원 등록 /create-member -> /members/{id}
- 회원 수정 /update-member -> /members/{id}
- 회원 삭제 /delete-member -> /members/{id}
-> 조회, 등록, 수정, 삭제가 구분되지 않는다. 이는 메서드를 사용하여 구분하자.
리소스와 행위의 분리
URI는 리소스만 식별한다.
리소스(회원)와 해당 리소스를 대상으로 하는 행위(조회, 등록, 수정, 삭제)를 분리하자.
리소스는 명사, 행위는 동사
행위는 메서드를 통해 구분하자. 그러므로 URI의 리소스만 식별하자.
HTTP 메서드 종류
HTTP 메서드 : 클라이언트가 서버에 요청을 할 때 기대하는 행동
[주요 메서드]
- GET : 리소스 조회(요청)
- POST : 요청 데이터 처리, 주로 등록에 사용. 무조건 클라이언트에 데이터를 담아서 서버로 보내야 한다.
- PUT : 클라이언트에서 서버로 리소스를 보낸다. 리소스를 대체하며, 해당 리소스가 없다면 생성
- PATCH : 리소스 부분 변경(특정 필드를 몇 개 바꿀 때)
- DELETE : 리소스 삭제 -> 최근 스펙에는 리소스가 아니라 representation으로 바뀌었다.
[기타 메서드]
- HEAD : GET과 동일하나 body 제외하고 상태 줄과 헤더 부분만 반환
- OPTIONS : CORS에서 주로 사용한다. 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명
- CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정 (거의 사용하지 않음)
- TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트 수행 (거의 사용하지 않음)
GET
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
- 리소스 조회 메서드. 해당 path에 있는 자원을 달라는 말
- 서버에 전달하고 싶은 데이터는 query(쿼리 스트링, 쿼리 파라미터)를 통해서 전달
- body를 이용해 데이터를 전달할 수 있지만(최근 스펙), 지원하지 않는 곳이 많아서 권장하지 않는다 -> 필요한 경우 쿼리스트링 사용해서 데이터 보낸다.
- 요청을 받으면 데이터를 만들어서 JSON 등으로 응답 데이터를 만들어서 클라이언트로 보낸다
POST
POST /members HTTP/1.1
Content-Type: application/json
{
"username": "hello",
"age": 20
}
- 요청 데이터 처리해달라고 클라이언트가 서버에게 전달
- 메시지 바디를 통해 서버로 요청 데이터 전달
- 서버는 요청 데이터를 받아서 처리
-
신규 리소스 등록, 프로세스 처리에 사용
- POST의 정식 스펙 : 대상 리소스가 리소스의 고유한 의미 체계에 따라 요청에 포함된 표현을 처리하도록 요청
- POST가 사용되는 예시
- HTML의 form에서 회원가입하거나 주문 같은 것을 처리
- 게시판에 글 쓰기, 댓글 달기 등
- 서버가 아직 식별하지 않은 새 리소스 생성
- 기존 자원의 끝에 데이터 추가
- 핵심 : 리소스 URI에 POST 요청이 오면 리소스마다 요청 데이터를 어떻게 처리할지 따로 정해야 함 -> 정해진 것이 없다.
POST가 사용되는 곳 정리
- 서버가 아직 식별하지 않은 새 리소스를 생성하고 등록하는데 사용
- 요청 데이터를 처리하는데 사용
- 서버에서 프로세스를 처리해야 하는 경우(값 변경을 넘어서 프로세스의 상태가 변경되는 경우, 서버에서 큰 변화가 일어날 경우)
- POST의 결과로 새로운 리소스가 생성되지 않을 수도 있음 - 리소스로 URI가 정의되지 않을 수 있음(컨트롤 URI)
- 다른 메서드로 처리하기 애매한 경우
- 쿼리 파라미터 말고 다른 수단으로 조회 데이터를 넘겨야 하는 경우
사실 POST로 모든 것을 할 수 있긴 하다. 하지만 그래도 조회의 경우, GET을 사용하는 것이 유리하다. -> GET을 사용할 경우 캐싱이 이루어지지만, POST는 캐싱이 어려워짐
PUT
PUT /members/100 HTTP/1.1
Content-Type: application/json
{
"username": "hello",
"age": 20
}
- 리소스를 대체. 없으면 생성한다 (폴더에 파일을 생성/복사하는 것과 유사) -> 완전히 대체한다. 기존거를 완전히 덮어버린다.
- 완전히 대체 : 기존 데이터의 필드가 A와 B가 있는 상황. 새로운 리소스가 C 필드만 있다면 C 필드만 남는 형식으로 대체.
- 기존 리소스를 삭제하고 완전히 덮어버린다 -> 따라서 PUT은 리소스 수정에 적합하지 않다
- 클라이언트가 구체적인 리소스의 위치를 알고 URL을 지정한다
- POST에서는 POST /members HTTP/1.1 와 같이 지정 -> POST에서는 어느 경로에 생성이 될지 모름
- PUT의 경우 /members/100 여기에 직접 생성 -> 클라이언트가 리소스의 위치를 알아야 한다.
PATCH
PATCH /members/100 HTTP/1.1
Content-Type: application/json
{
"age": 50
}
- PUT은 리소스의 변경이 어려움 -> PATCH 사용
- 예전에는 PUT만 있다가 PATCH가 새로 나왔다.
- 부분적으로 리소스의 데이터를 변경하고 싶을 때 사용
- PATCH가 지원이 되지 않는 서버의 경우 POST를 사용해야 할 듯..
DELETE
DELETE /members/100 HTTP/1.1
Host: localhost:8080
- 리소스 제거
HTTP 메서드의 속성
- 안전(Sate Methods)
- 멱등(Idempotent Methods)
- 캐시 가능(Cacheable Methods)
GET은 요청에 Body를 넣을 수 있지만, 되는 곳이 있고 안 되는 곳이 있으므로 사용하지 않는 것이 좋다.
안전(Safe)
- 호출해도 리소스를 변경하지 않는다
- GET은 단순히 조회만 하므로 안전하다.
- POST, DELETE, PUT, PATCH는 호출했을 때 변경이 일어나므로 안전하지 않다.
- 안전은 해당 리소스가 변하는지 변하지 않는지만 고려 - 로그 쌓여서 나는 장애 같은거는 고려하지 않음
멱등(Idempotent Methods)
- f(f(x)) = f(x)
- 한 번 호출하든 두 번 호출하든 100번 호출하든 결과가 똑같다
- 멱등 메서드
- GET : 멱등. 몇 번 조회하든 결과가 같음.
- PUT : 멱등. 결과를 대체하기에(기존걸 날리고 완전히 덮어버리기 때문) 같은 요청을 여러번 해도 결과는 같다.
- DELETE : 멱등. 그냥 날려버리니까 같은 요청을 여러번 해도 삭제된 결과는 같다. 두 번 호출해도 삭제된 상태로 남아있기 때문에
- POST : 멱등 아님! 두 번 호출하면 두 번의 다른 액션이 취해지기 때문
- 멱등의 활용성
- 자동 복구 메커니즘 -> 서버가 정상 응답을 주지 못했을 때, 클라이언트가 같은 요청을 다시 해도 되는지 판단의 근거로
- 재요청 중간에 다른 곳에서 리소스를 변경해버려서 결과값이 변경될 경우?
- 멱등은 외부 요인으로 중간에 리소스가 변경되는 것 까지는 고려하지 않는다.
- 동일한 사용자가 같은 요청을 했을 때 멱등한지만 판단한다.
캐시 가능(Cacheable Methods)
- 응답 결과 리소스를 캐시해서 사용해도 되는가?
- GET, HEAD, POST, PATCH -> 캐시 가능
- 웹 브라우저 등에 리소스를 저장할 수 있는지?
- 실제로는 GET, HEAD 정도만 캐시로 사용
- 캐시를 하려면 똑같은 리소스랑 키가 맞아야 함 - POST와 PATCH는 본문 내용까지 캐시 키를 고려해야 하는데(맞아야 하는데) 구현이 쉽지 않다. -> 실무에서는 GET만 캐시로 사용하는 편