단어 사전 API를 만들어보자.
필요한 기능은 다음과 같다.
여기서 리소스는 "단어"이다.
URI API는 "단어"만 식별하도록 설계해야한다.
나쁜 URI 설계 예시
1. 단어 목록 조회: /read-words-list
2. 단어 조회: /read-word-by-id
3. 단어 등록: /create-word
4. 단어 수정: /edit-word
5. 단어 삭제: /delete-word
위의 URI는 리소스만 식별하는 것이 아니라, 리소스 + 행위까지 식별하고 있다.
좋은 URI 설계 예시
1. 단어 목록 조회: /words
2. 단어 조회: /words/{id}
3. 단어 등록: /words/{id}
4. 단어 수정: /words/{id}
5. 단어 삭제: /words/{id}
위의 URI는 리소스인 단어만 식별하고 있다.
조회, 등록, 수정, 삭제 등의 행위는 HTTP Method로 구분하면 된다.
HTTP의 주요 메소드
GET Method는 리소스를 조회할 때 사용한다.
정적 데이터 조회: 리소스 경로 사용
동적 데이터 조회: 쿼리 파라미터를 통해서 조회하고자 하는 리소스를 서버에 전달한다.
Request 메시지
GET /words/1020 HTTP/1.1 Host: localhost:8080
Response 메시지
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 35<br>
{ "word": "Hello", "meaning": "안녕하세요" }
POST Method는 요청한 리소스를 처리한다.
Request Body를 통해서 서버로 데이터를 전달한다.
전달받은 데이터는 주로 신규 리소스 등록, 프로세스 처리에 사용한다.
Request 메시지
POST /words HTTP/1.1 Content-Type: application/json
{ "word": "Hello", "meaning": "안녕하세요" }
Response 메시지
HTTP/1.1 201 Created Content-Type: application/json Content-Length: 35 Location: /words/1020
{ "word": "Hello", "meaning": "안녕하세요" }
여기서 주목해야할 점은, Request 메시지에서 /words 라는 URI를 썼다는 것이다.
새로운 단어를 등록하기 위해서 POST Method를 사용했지만, 어떤 Path에 넣을 것인지는 지정하지 않았다.
서버는 자동으로 /words/1020 이라는 새로운 식별자를 생성해서 Response 메시지로 Client에게 전달했다.
중요한 것은, 리소스를 어떻게 처리할 것인지 서버측에서 지정해주어야 한다는 것이다.
HTTP Method를 사용해서 API를 제작할 때 애매한 부분이 있다면 POST를 사용하자.
PUT Method는 요청한 리소스를 대체한다.
요청한 리소스가 없다면 새로 생성한다.
Request 메시지
PUT /words/1020 HTTP/1.1 Content-Type: application/json
{ "word": "Hello", "meaning": "안녕하세요" }
신규 리소스 생성됨
/words/1020 { "word": "Hello", "meaning": "안녕하세요" }
ex) 요청 메시지로 다음과 같이 보내면
{
"meaning": "안녕"
}
/words/1020에 있는 리소스에서 "word": "Hello"
는 없어지고 "meaning": "안녕"
만 바뀐다.
{
"meaning": "안녕"
}
개발자가 의도한 것은 이게 아닐 것이다.
리소스의 일부분만 변경하고 싶다면 어떻게 해야할까?
PATCH Method를 사용하면 리소스의 일부만을 변경할 수 있다.
현재 /words/1020에 존재하는 리소스:
{
"word": "Hello",
"meaning": "안녕하세요"
}
Request 메시지
PATCH /words/1020 HTTP/1.1 Content-Type: application/json
{ "meaning": "안녕" }
위의 Request 메시지에는 words 필드가 존재하지 않는다.
PATCH Method를 사용하면 words 필드는 바뀌지 않고, meaning 필드만 "안녕"으로 변경된다.
변경된 /words/1020 리소스:
{
"word": "Hello",
"meaning": "안녕"
}
HTML FORM을 사용해서 데이터를 전송할 수 있다.
HTML FORM을 통한 데이터 전송은 GET과 POST만 지원한다.
GET Method에 대해서 공부할 때, 정적 데이터는 리소스 경로를 사용해서 조회하고 동적 데이터는 쿼리 파라미터를 사용해서 조회한다고 학습했다.
HTML FORM을 사용하면 입력한 정보를 바탕으로 쿼리 파라미터를 작성해서 요청을 자동으로 생성해준다.
<form action = "/words" method="get">
<input type = "text" name = "word">
<input type = "text" name = "meaning">
<button type = "submit">전송</button>
</form>
word에 hello
meaning에 안녕하세요
입력한 후에 전송버튼 누름
생성된 HTTP request message
GET /words?word=hello&meaning=안녕하세요 HTTP/1.1
Host: localhost:8080
word=hello&meaning=안녕하세요 쿼리 파라미터를 자동으로 생성했다.
GET FORM은 입력한 정보를 쿼리 파라미터로 작성해줬다면, POST FORM은 입력한 정보를 Body에 넣어준다.
<form action = "/save" method="post">
<input type = "text" name = "word">
<input type = "text" name = "meaning">
<button type = "submit">전송</button>
</form>
word에 hello
meaning에 안녕하세요
입력한 후에 전송버튼 누름
생성된 HTTP request message
POST /save HTTP/1.1
Host: localHost:8080
Content-Type: application/x-www-form-urlencoded
word=hello&meaning=안녕하세요
Content-Type: application/x-www-form-urlencoded
을 사용한다.
바이너리 데이터를 전송하는 경우 Content-Type: multipart/form-data
을 사용한다.
HTTP Method 각각은 서로 다른 속성을 가지고 있다.
Method | safe 속성 | idempotent 속성 | cacheable 속성 |
---|---|---|---|
GET | O | O | O |
POST | X | X | O |
PUT | X | O | X |
PATCH | X | X | O |
DELETE | X | O | X |
Safe Method
== 리소스를 변경하지 않는 Method
ex) GET은 리소스를 조회만 할 뿐 변경하지 않는다.
POST, PUT, PATCH, DELETE는 모두 리소스를 변경하는 Method이다.
idempotent Method
== 요청 횟수에 상관없이 결과가 같은 Method
한 번 호출하든 n번 호출하든 결과가 같다.
ex) request1: POST로 단어 "Book" 전달
➜ 새로운 단어로 처리
request2: POST로 단어 "BOOK" 전달
➜ 이미 있으므로 다르게 처리됨
∴ POST는 idempotent Method가 아니다.
cacheable Method
== 응답 결과 리소스를 캐시해도 되는 Method
리소스의 타입을 명확하게 알기 위해서 다음과 같은 4가지 방식으로 리소스를 구분한다.
: 단일 개념(파일 하나, 객체 인스턴스)
ex) /words/1020
POST Method로도 리소스 등록을 할 수 있고, PUT Method로도 리소스 등록을 할 수 있다.
POST는 서버가 리소스의 URI를 관리하고,
PUT은 클라이언트가 리소스의 URI를 관리한다.
서버가 관리하는 리소스 디렉토리를 Collection이라 하고,
클라이언트가 관리하는 리소스 저장소를 Store라 한다.
명사만 가지고 API를 설계하기 어려울 때가 있다.
예를 들어, HTML FORM을 사용하여 단어 사전 API를 설계해보자.
HTML FORM은 GET과 POST만 지원한다.
- 단어 목록 조회: /words
- 단어 조회: /words/{id}
- 단어 등록: /words/{id}
- 단어 수정: /words/{id}
- 단어 삭제: /words/{id}
Json을 사용하면 조회, 등록, 수정, 삭제를 각각 GET, POST, PATCH, DELETE에 대응시키면 되겠지만, HTML FORM은 GET과 POST만 지원한다.
이러한 제약을 해결하기 위해 "동사" 경로를 사용한다.
- 단어 목록 조회: /words
- 단어 조회: /words/{id}
- 단어 등록: /words/new
- 단어 수정: /words/{id}/edit
- 단어 삭제: /words/{id}/delete
이러한 URI를 Control URI라고 부른다.