REST API
REST란?
개념
Representational State Transfer
분산된 시스템에서 구성 요소들이 일정한 방식으로 통신하도록 설계하기 위한 Architectural Style
특정 Protocol이나 Framework, Library를 의미하는것이 아니라 시스템의 구성 요소가 어떤 역할을 가지고 어떤 제약 아래에서 통신할지를 정의하는 설계 방식
Resource를 중심으로 통신
Resource = 시스템에서 식별할 수 있는 대상
ex) 쇼핑몰의 Resource => 상품,사용자, 주문, 리뷰
REST에서는 Client-Sever가 Resource 자체를 직접 주고받는 것이 아니라,
Resource를 표현한 Representation을 주고받음
ex)
{
"id": 10,
"name": "Keyboard",
"price": 50000
}
=> 상품 → Resource , JSON데이터 → Resource의 Representation
주요 원칙
REST는 단순히 Resource를 URI로 표현하는 방식이 아니라,
몇 가지 Architecture Constraint를 조합하여 만들어진 Architectural Style
| 원칙 | 의미 |
| Client-Server | Client와 Server의 역할 분리 = Client는 Server내부에서 데이터를 어떻게 저장하고 처리하는지 알 필요가 없고, Server도 Client화면이 어떻게 구현되어 있는지 알 필요가 없음. => 이렇게 역할을 분리하면, Client와 Server를 각각 독립적으로 개발하고 변경하기 쉬워짐. |
| Stateless | 각각의 요청을 독립적으로 처리 = Server는 Client의 이전 요청 상태에 의존하지 않고 각 Request를 독립적으로 처리할 수 있어야 함. => 각 Request에는 Server가 해당 요청을 처리하는 데 필요한 정보가 포함되어 있어야 함 |
| Cache | Response를 다시 사용해도 되는지 판단할 수 있도록 Cache 가능 여부를 명확하게 표현하는 원칙 = 동일한 요청에 대해 이전 Response를 재사용할 수 있다면 Client나 중간 계층에서 Cache된 데이터를 사용하여 불필요한 Server 요청과 처리 비용을 줄일 수 있음. 단, 데이터가 변경되었는데 오래된 Response가 계속 사용될 수 있으므로 Cache를 얼마나 유지할지, 언제 다시 Server의 데이터를 확인할지 등의 정책이 함께 필요. |
| Uniform Interface | 일관된 방식으로 Resource와 상호작용 => Client-Server의 구현이 서로 강하게 결합되는 것을 줄일 수 있음. |
| Layered System | 여러 계층을 둘 수 있는 구조 (Client-Server 사이에 여러 계층이 존재할 수 있음) 각 계층은 요청을 처리하는 과정에 어떤 중간 계층이 존재하는지 모두 알 필요가 없음. , 구성 요소가 자신이 직접 상호작용하는 계층 너무의 구조를 알 필요가 없도록 제한 |
| Code on Demand | 필요하면 Server가 실행 가능한 Code를 Client에 전달 (Optional) 단, 다른 REST 제약 조건과 달리 선택적인(Optional) 제약 조건 |
REST API와 RESTful API
REST API
주로 Resource를 중심으로 API를 구성하고, 각 Request를 독립적으로 처리하며, 일관된 Interface를 통해 Client와 Server가 통신하도록 설계
RESTful
REST의 원칙을 따르는 형태
Resource와 URI
요청의미 = URI( Resource 중심 ) + HTTP Method
URI
→ 어떤 Resource인가?
ex) 쇼핑몰의 Resource : 상품, 사용자, 주문, 리뷰
=> /products, /users, /orders, /reviews
HTTP Method
→ 해당 Resource에 어떤 작업을 요청하는가?
| Method | 의미 | 일반적인 REST API 사용 예 |
| GET | Resource의 Representation 요청 | 조회 |
| POST | 대상 Resource에 데이터 처리를 요청 | 생성 등 |
| PUT | 대상 Resource의 상태를 생성하거나 전체 교체 | 전체 수정 |
| PATCH | Resource의 일부 변경 | 일부 수정 |
| DELETE | 대상 Resource 삭제 요청 | 삭제 |
Resource의 집합과 개별 Resource
/resources
↓
Resource Collection = 같은 종류의 Resource 집합
/resources/{id}
↓
Individual Resource = Collection의 특정 Resource
ex) /users => 사용자 Resource의 Collection
ex) /users/15 => 식별자가 15인 사용자 Resource
Resource 간의 관계
/resources/{id}/related-resources
Resource는 다른 Resource와 관계를 가질 수도 있음.
=> 이러한 관계를 URI의 계층 구조로 표현할 수 있음.
ex) /posts/10/comments => 10번 게시글에 속한 댓글 Resource
RESTful API의 장단점
장점
일관된 API 구조
API의 구조를 예측하기 쉬워짐.
Endpoint마다 별도의 동작 이름을 학습하는 것보다 API 전체를 하나의 공통된 방식으로 이해할 수 있음.
Client와 Server를 분리할 수 있음.
REST의 Client-Server 원칙에서는 Client와 Server가 정해진 Interface를 기준으로 통신함.
각각은 내부에서 어떻게 처리하는지 알 필요가 없음.
=> 각각의 내부 구현을 변경하기 쉬워짐.
Stateless 구조를 활용하기 쉬움
각 Request가 독립적으로 처리되기 때문에 Server가 이전 요청의 Context에 의존하는 정도를 줄일 수 있음.
HTTP의 기능을 활용할 수 있음
HTTP기반 REST API에서는 이미 정의되어 있는 HTTP의 기능을 그대로 활용할 수 있음.
ex) GET/POST/PUT/PATCH/DELETE
그리고 요청 처리 결과 역시 HTTP가 제공하는 의미를 사용할 수 있음.
ex) 200 OK, 201 Created, 404 NOT Found
=> 별도의 요청 방식과 상태 표현 방식을 처음부터 새로 정의할 필요가 줄어듦
단점과 한계
모든 기능을 CRUD 형태로 표현하기 어려움
실제 서비스에는 단순 CRUD로 표현하기 애매한 기능을 많음
=> 억지로 CRUD에 맞추면 오히려 API의 의미가 불명확해질 수 있음.
실제 API에서는 필요에 따라 Action을 별도로 표현하는 방식 등을 사용할 수도 있음
ex) 주문 취소, 결제 승인, 비밀번호 재설정, 이메일 인증, 파일 변환
필요한 데이터와 Resource 단위가 맞지 않을 수 있음
Resource를 중심으로 데이터를 제공하기 때문에 Client가 원하는 데이터 단위와 API의 Resource 단위가 항상 일치하지는 않음.
ex) 화면에는 사용자 이름+최신 게시글 5개+알림 개수가 한꺼번에 필요하지만 각각 별도의 Resource라면 여러 API를 호출해야 할 수 있음.
ex) API가 반환하는 Resource에 Client가 사용하지 않는 데이터까지 포함되면 필요한 것보다 많은 데이터를 받게 될 수 있음
Resource 설계 자체가 어려울 수 있음.
단순한 데이터는 Resource로 표현하기 쉽지만,
하지만 복잡한 비지니스 기능에서는 무엇을 하나의 Resource로 볼 것인지 결정하기 어려움
ex) 주문 결제, 주문 취소, 환불 요청, 배송 상태 변경
=> 각각 Resource로 볼 것인지, 주문 Resource의 상태 변경으로 볼 것인지, 별도의 Action으로 표현할 것인지 결정해야 함.
RESTful API 설계 시 알아둘 점
| 유의점 | 설명 |
| URI는 Resource 중심으로 표현 | URI에는 가능하면 수행할 동작보다 대상이 되는 Resrouce를 표현 |
| Resource 이름은 일관된 규칙 사용 | 하나의 Naming Convention을 정해 일관되게 사용하는 편이 좋다 |
| Resource 관계를 지나치게 깊게 표현하지 않기 | 관계를 계속 중첩하면 URI가 복잡해질 수 있음 ex) /users/{userId}/posts/{postId}/comments/{commentId}/replies => 관계를 이해하는 데 필요한 수준까지만 계층화하는 것이 좋다. 계층이 지나치게 깊어진다면 해당 Resource를 독립적으로 접근할 수 있도록 설계하는 방법도 고려 |
| 조회 조건은 Query Paramater 활용 | Resource 자체가 아니라 조회 조건이 달라지는 경우에는 Query Parameter 활용 => Path = 어떤 Resource인가?, Query Parameter = Resource를 어떤 조건으로 조회할 것인가? ex) 게시글 Resource에서 공개된 게시글만 조회 : GET /posts?status=published ex) 페이지를 나누어 조회 : GET /posts?page=2&limit=20 |
| HTTP Method의 의미에 맞게 사용 | URI를 Resource 중심으로 만들었더라도 Method를 일관되지 않게 사용하면 API의 의미를 예상하기 어려움 ex) 삭제 작업을 POST /users/{id}/delete 처럼 처리하기보다 일반적인 Resource 삭제라면 "DELETE /users/{id}" |
| 모든 기능을 CRUD에 억지로 맞추지 않기 | 실제 서비스에는 단순한 생성·조회·수정·삭제로 표현하기 어려운 기능도 존재 ex) 주문 취소, 결제 승인, 이메일 인증, 비밀번호 재설정 이런 기능을 억지로 맞추면, 오히려 API의 의미가 불명확해질 수 있다. Resrouce 중심 구조를 기본으로 하되, Domain의 동작을 가장 명확하게 표현할 수 있는 방법을 선택하는 것이 중요 ex) POST /orders/{id}/cancel |
| API 전체에서 일관성을 유지 | 개별 Endpoint하나가 RESTful해 보이는 것보다 중요한 것은 서비스 전체가 같은 규칙으로 설계되어 있는지. => Resource가 달라져도 Endpoint의 역할을 쉽계 예상할 수 있음. ex) GET /users/{id} GET /getPost?id={id} POST /createOrder DELETE /comments/{id} => 여러 규칙이 섞이면 API를 사용할 때마다 별도의 규칙을 파악해야 함 GET /users/{id} GET /posts/{id} POST /orders DELETE /comments/{id} => 일관된 구조 지향 |