Soft Delete는 데이터를 실제로 삭제하지 않고, 삭제된 것처럼 표시만 해두는 방식이다.
데이터베이스에서는 보통 is_deleted, deleted_at 같은 필드를 활용해 삭제 여부를 판단한다.삭제한 데이터를 복구하거나 삭제 이력을 추적할 수 있도록 할 때 사용한다.
장점
- 실수로 삭제한 데이터 복구 가능
- 삭제 이력 추적 가능
통계/로그 보존
즉 삭제 여부를 표시하는 플래그를 사용하는 것이기 때문에 실제 DB에서는 삭제되지는 않고, 다음과 같이 쿼리를 실행할 수 있다.
UPDATE reviews SET is_deleted = true WHERE id = 1;
HTTP 메서드는 일반적으로 해당 API의 작업이 무엇인지 명시하는 역할을 한다. 클라이언트 입장에서도 데이터를 삭제하는 것이고, 삭제 필드를 변경한 것이고 조회 시에도 표시되지 않도록 하는 작업이기 때문에, HTTP 메서드는DELETE를 사용한다.
DELETE /api/reviews/1
RESTful 구조에서 리소스의 상태를 제어하거나 동작을 트리거하는 API URI이다.
리소스를 직접 다루는 것이 아니라, 그 리소스에 대해 추가 동작을 요청하는 경우에 사용한다. 주로 PATCH,POST 메서드와 함께 사용되며, URI에는 동작을 명시하는 명사/동사 구조를 사용한다.
PATCH /api/missions/1/complete
해당 API는 id가 1인 미션을 성공 상태로 처리한다. 즉 리소스에 대해 상태만 변경하는 것이다.
POST /api/users/1/deactivate
일반적으로 유저의 상태 필드를 active, inactive 등으로 설정한다. 유저가 탈퇴할 경우 위와 같이 유저의 상태를 비활성화시키고, 30일이 지나면 유저 정보를 자동으로 삭제하거나 하는 방식으로 설정할 수 있다.
Rest(Representational State Transfer)는 웹 서비스를 디자인하는 아키텍처 접근 방식으로 제안된 것이다. REST는 특정 프로토콜에 의존하는 것은 아니지만, 대부분의 REST API 구현은 HTTP를 애플리케이션 프로토콜로 사용하고 있다.
api가 표시하려는 비즈니스 엔터티에 집중해야 한다. 주문,고객이라는 엔터티를 포함해서 주문을 하는 api를 설계하려고 한다면, 주문을 한다라는 행동이 아니라 주문에 집중해야 한다.
실제 데이터 항목을 기반으로 할 필요는 없으며, 해당 uri가 클라이언트에 포함되는 만큼, 클라이언트에서 내부 구현과 관련된 부분을 포함하지 않도록 해야 한다.
컬렉션/항목/컬렉션과 같은 구조로 하면 api를 직관적으로 구성할 수 있다. 접근해야할 엔터티가 늘어난다고 해서 컬렉션/항목/컬렉션보다 더 복잡한 구조를 가지지 않도록 하는 것이 좋다. 다수의 작은 리소스를 포함하는 api의 경우 해당 요청을 많이 보내야하고, 요청이 많을수록 부하가 커지기 때문이다. 단일 요청을 통해 관련 정보를 얻을 수 있도록 더 큰 리소스와 결합하는 구조로 하는 것이 좋을 수도 있다.
GET, POST, PUT, PATCH, DELETE 와 같은 HTTP 메서드의 측면에서 API의 작업을 정의해야 한다.
PUT,PATCH의 개념이 헷갈릴 수 있는데, PUT요청은 리소스를 생성하거나 업데이트하는 것이고 PATCH는 기존 리소스의 부분 업데이트를 실행한다. PUT 요청은 idempotent여야한다. 클라이언트에서 동일한 PUT 요청을 여러번 제출하더라도 결과가 항상 같아야 한다는 것이다.
상태 코드를 명시해야 한다.
200(성공), 201(생성), 204(내용 없음), 400(잘못된 요청), 404(없음), 409(충돌)
형식을 명시해야 한다.
Content-Type 및 Accept 헤더로 JSON, XML 등 형식 명시
각 응답에 다음 동작을 위한 링크를 제공해야 한다.
클라이언트는 URI를 기억하지 않고도 링크를 따라 리소스를 탐색이 가능해야 한다.
예를 들어 사용자가 작성한 리뷰 조회, 리뷰 단일 조회라는 두 api가 있다면 클라이언트가 따로 uri를 기억할 필요 없이 링크를 따라 조회가 가능하도록 응답을 구성해야 한다.
리뷰 조회
{
"userId": 3,
"reviews": [
{
"reviewId": 10,
"title": "좋은 상품이에요",
"rating": 5,
"links": [
{ "rel": "self", "href": "/reviews/10", "action": "GET" },
{ "rel": "delete", "href": "/reviews/10", "action": "DELETE" },
{ "rel": "edit", "href": "/reviews/10", "action": "PUT" }
]
}
}
리뷰 단건 조회
{
"reviewId": 10,
"userId": 3,
"title": "좋은 상품이에요",
"content": "정말 마음에 들어요!",
"rating": 5,
"links": [
{ "rel": "self", "href": "/reviews/10", "action": "GET" },
{ "rel": "edit", "href": "/reviews/10", "action": "PUT" },
{ "rel": "delete", "href": "/reviews/10", "action": "DELETE" },
{ "rel": "user", "href": "/users/3", "action": "GET" }
]
}
api는 리소스 구조나 기능이 변경될 수 있기 때문에, 버전 관리는 클라이언트의 호환성을 유지하는 데 중요하다. API 버전 관리는 여러 방식으로 구현할 수 있으며, 각각 장단점이 있다.
방식 | 설명 | 장점 | 단점 |
---|---|---|---|
URI 버전 관리/v1/orders | URI 경로에 버전 명시 | 명확하고 간편 | HATEOAS와 충돌 가능 |
쿼리 문자열/orders?version=2 | 쿼리 파라미터로 버전 전달 | URI 재사용 | 캐싱 문제 발생 가능 |
헤더 기반 버전 관리Custom-Header: version=2 | 사용자 정의 헤더로 버전 명시 | URI 깔끔 | 숨겨져 있어서 명시성 낮음 |
미디어 타입 버전 관리Accept: application/vnd.company.v2+json | MIME 타입에 버전 포함 | HATEOAS 친화적, 유연함 | 구현 복잡, 인지 난이도 있음 |
성능을 고려한다면 캐시 관점에서도 URI 기반이 유리하고, swagger에서도 주로 URI 버전 관리 방식이 사용된다.
데이터 조회 시, 쿼리 파라미터로 구현해서 필요한 데이터만 효율적으로 조회하는 방식이 권장된다.
/orders?minPrice=100&sort=price&limit=10&offset=20
기본값 설정 권장: limit=10, offset=0 등
필드 선택: /orders?fields=id,name,price
응답에도 총 리소스 수 등의 메타데이터를 포함하는 것이 권장된다.
대용량 리소스에 대한 응답을 설계할 때는, Accept-Ranges, HEAD, Range 메서드를 사용하여 부분 응답을 지원할 수 있다. 이미지나 PDF 등의 대용량 리소스 응답 시에 유용한 방법이다.
GET /images/1 HTTP/1.1
Range: bytes=0-1023
이렇게 요청을 보낸다면, 206 Partial Content
이렇게 응답 상태 코드를 보낼 수 있다.