REST API 설계 시 가장 중요한 항목은 다음의 2가지로 요약할 수 있습니다.
자원 원형은 REST API 내의 자원들이 어떤 역할을 수행하는지, 어떤 종류의 자원인지를 분류하는 방법입니다. Leonard Richardson의 모델에서는 주로 네 가지 원형을 구분합니다:
/users/{userId}
/users
/users/{userId}/suspend
/users/{userId}/subscription
자원 원형 | HTTP 메소드 | 설명 | 예시 |
---|---|---|---|
Document | GET | 자원 조회 | /users/{userId} - 사용자 정보 조회 |
PUT | 자원 전체 업데이트 | /users/{userId} - 사용자 정보 전체 수정 | |
PATCH | 자원 부분 업데이트 | /users/{userId} - 사용자 정보 일부 수정 | |
DELETE | 자원 삭제 | /users/{userId} - 사용자 정보 삭제 | |
Collection | GET | 자원 컬렉션 조회 | /users - 모든 사용자 조회 |
POST | 자원 생성 및 컬렉션에 추가 | /users - 새 사용자 추가 | |
PUT | 404 Method Not Allow | - | |
DELETE | 컬렉션의 모든 자원 삭제 | /users - 모든 사용자 정보 삭제 | |
Store | GET | 저장소에서 자원 검색 | /files - 모든 파일 조회 |
POST | 저장소에 자원 추가 | /users/{userId}/subscription - 구독 | |
PUT | 404 Method Not Allow | - | |
DELETE | 저장소에서 자원 삭제 | /users/{userId}/subscription - 구독취소 | |
Controller | POST | 특정 작업 수행 | /users/{userId}/activate - 계정 활성화 |
GET | 상태 정보 또는 작업 결과 조회 | /system/status - 시스템 상태 조회 | |
PUT | 404 Method Not Allow | - | |
DELETE | 404 Method Not Allow | - |
자원 관계는 자원들 간의 상호 연결성을 나타내며, 어떻게 자원이 서로 관련되어 있는지를 설명합니다. 주로 다음과 같은 관계가 있습니다:
부모-자식 관계: 한 자원이 다른 자원을 소유합니다. 부모가 없으면 자식도 없습니다.
/users/{userId}
연관 관계: 두 자원이 서로 연결되어 있습니다.
/users/{userId}/order
명시된 연관 관계: 자원 간의 특별한 관계를 가지고 있거나, 애매할때는 어떤 관계인지도 명시합니다.
/users/{userId}/recent/order
깊은 관계를 탐색할 수 있는 URI를 제공하고 싶을 수 있지만 그런 URI은 관리하기가 어렵고 유연성이 떨어집니다.
Avoid
/users/{userId}/friend/10/post
- 친구의 게시글 탐색
Good
/users/{userId}/friend
- 친구 목록을 가져온 후
/users/{userId}0/post
- 친구 게시글 탐색
링크 포함 (Link Inclusion): 클라이언트에게 자원 간의 관계를 나타내는 링크를 직접 포함합니다. 예를 들어, JSON 형식의 응답에 다른 자원에 대한 링크를 추가할 수 있습니다.
GET /users/{userId}
{
"id": 1,
"name": "Hong Gil Dong",
"post": "http://api.example.com/users/{userId}/post" //절대경로
}
임베딩 (Embedding): 하위 자원을 현재 자원에 포함하여 응답합니다. 이는 클라이언트가 추가 요청을 하지 않고도 관련 자원의 정보를 즉시 얻을 수 있게 해줍니다.
GET /users/{userId}
{
"id": 1,
"name": "Hong Gil Dong",
"post": [
{
"id": "1",
"title": "This is title",
"content": "This is content"
},
{
"id": "2",
"title": "This is title",
"content": "This is content"
}
]
}
하이퍼미디어 컨트롤 (HATEOAS): 클라이언트가 현재 자원에서 다음으로 이동할 수 있는 가능한 작업을 제시합니다. 이를 통해 클라이언트는 상태 전이(state transition)를 수행하고 자원 간의 관계를 탐색할 수 있습니다.
GET /users/{userId}
{
"id": 1,
"name": "Hong Gil Dong",
"links": [
{
"rel": "self",
"href": "http://api.example.com/users/{userId}"
},
{
"rel": "post",
"href": "http://api.example.com/users/{userId}/post"
}
]
}
Avoid
http://api.example.com/post/1/POST-COMMENT
Good
http://api.example.com/post/1/post-comment
http://api.example.com/post/1
Avoid
http://api.example.com/post/1/
Good
http://api.example.com/post/1
Avoid
http://api.example.com/post/1/post_comment
Good
http://api.example.com/post/1/post-comment
Avoid
http://api.example.com/post/picture.jpg
Good
http://api.example.com/post/picture
request
GET /post/picture HTTP/1.1
Accept: image/jpg
response
HTTP/1.1 200 OK
Content-Location: /post/picture?format=jpg
Content-Type: image/jpeg
참조: 콘텐츠 협상
Avoid
POST http://api.example.com/delete-post/1
Good
DELETE http://api.example.com/post/1
HTTP Method로 표현 할 수 없는 기능은 Control Resource로 제작하고 동사로 짓는다.
Avoid
http://api.test.com/posts/duplicating
Good
http://api.test.com/posts/duplicate
data를 응답시 Content-Type의 MIME을 application/json만 제공한다. 다른 형식(xml)은 필요한게 아니라면 굳이 지원할 필요가 없습니다.
POST로 자원 생성 후 생성된 자원를 위치를 표기한다
자원에 접근할때 GET, PUT, DELETE는 이미 자원의 위치를 알고 요청한다. 하지만 POST는 자원를 생성한뒤 새로 생성된 자원의 위치를 요청자가 모르기 때문에 알려 줘야한다.
이때 생성된 자원 접근 위치를 Location header에 표기한다.
request
POST /image
response
HTTP/1.1 201 OK
Content-Type: image/png
Location: /image/photo - 생성 위치
특정 Content-Type의 자원의 위치를 가르키는 헤더이다.
Content-Type: image/png
Location: /image/photo
Content-Location: /image/photo?format=png
비정상적인 방법(DoS, Brute-force attack)으로 API 서버를 이용하려는 경우 429 Too Many Requests 오류 응답과 함께 일정 시간 뒤 요청할 것을 나타낸다.
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
token 방식의 인증을 사용할때 사용한다.
GET /admin
Authorization: Bearer YOUR_ACCESS_TOKEN
HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다.
1xx(정보) : 정보 제공 응답
2xx(성공) : 요청 성공 응답
3xx(리다이렉션) : 리다이렉트 응답
4xx(클라이언트 오류) : 클라이언트 에러 응답
5xx(서버 오류) : 서버 에러 응답
HTTP Method | 200 OK | 201 Created | 204 No Content | 400 Bad Request |
---|---|---|---|---|
GET | 리소스 성공적 반환 | - | - | 잘못된 요청 |
POST | 리소스 생성 성공 후 데이터를 얻고 싶을때 | 리소스 생성 성공 | - | 잘못된 요청 |
PUT | 리소스 업데이트됨 | 업데이트로 인해 새 리소스 생성됨 | 변경 없이 성공 | 잘못된 요청 |
DELETE | - | - | 삭제 후 콘텐츠 없음 | 잘못된 요청 |
HTTP/1.1 200 OK
{
"result" : false
"status" : 400
}
status는 200으로 성공인데 body 내용엔 실패에 관한 내용을 리턴하고 있다.
모든 응답을 200으로 처리하고 body 내용으로 성공|실패를 판단하는 구조에서 사용된다. 잘못된 설계다.
HTTP/1.1 400 Bad Request
{
"msg" : "check your parameter"
}
http 상태 코드를 응답 객체에 중복으로 표시할 필요 없다.
HTTP/1.1 404 Not Found
{
"code" : 404,
"error_code": -765
"more_info" : "https://api.test.com/errors/-765"
}
HTTP/1.1 404 Not Found
{
"error_code" : -765,
"more_info" : "https://api.test.com/errors/-765"
}
클라이언트 요청이 미리 정의된 파라미터 요구사항을 위반한 경우
파라미터의 위치(path, query, body), 사용자 입력 값, 에러 이유를 알린다
case 1
{
"message" : "'name'(body) must be Number, input 'name': test123"
}
case 2
{
"errors": [
{
"location": "body",
"param": "name",
"value": "test123",
"msg": "must be Number"
}
]
}
한 번에 모든 결과를 응답하지 않고 적당한 크기로 데이터 셋을 나눠서 응답한다.
GET /users?offset=1&limit=10
정렬 매개 변수에는 정렬이 수행되는 속성의 이름이 쉼표로 구분되어 있어야 한다.
order 또는 sort라는 key를 사용한다.
GET /users?order=-email,name
필터링은 일부 속성과 예상 값을 지정하여 쿼리된 자원 수를 제한하는 것으로 구성된다.
동시에 여러 속성에 대한 컬렉션을 필터링하고 하나의 필터링된 속성에 여러 값을 허용할 수 있다.
http://api.example.com?type=ssd or hdd
결과의 일부분만 선택해서 응답받을 수 있다.
GET /users?fields=name,level
HTTP/1.1 200 OK
{
"name" : "david",
"level": 10
}
정말 좋은 글 감사합니다!