/customers/5/orders처럼 한 리소스 아래에 그와 관련된 리소스 컬렉션을 두는 URI(Uniform Resource Identifier) 설계를 중첩 리소스(nested resource)라고 부른다. 그런 경로를 가진 엔드포인트를 nested endpoint, 아래쪽 리소스를 sub-resource라고도 한다. 표준 용어는 아니고 REST(Representational State Transfer) API 설계에서 관례로 쓰는 이름이다. URI의 경로(path)는 보통 계층 형태로 리소스를 식별하고, 중첩 리소스는 그 계층으로 리소스 사이의 관계를 표현한다.
GET /customers 고객 컬렉션
GET /customers/5 고객 5 (개별 리소스)
GET /customers/5/orders 고객 5의 주문 컬렉션 (중첩 리소스)
GET /orders/99 주문 99 자체관계를 보여 줄 뿐, DB 구조가 아니다
- 경로는 "고객 5와 관련된 주문"이라는 관계를 말한다. 저장소에서 주문이 고객 테이블 밑에 있다는 뜻이 아니다
- 반대로 DB의 내부 구조를 그대로 URI로 드러내지 않는다. 테이블마다 컬렉션을 만들면 구현이 클라이언트에 새고 공격 면도 넓어진다
- 중첩 리소스가 독립 리소스이기도 하면 두 경로가 함께 있을 수 있다.
/customers/5/orders는 고객과의 관계로 주문 목록에 접근하고,/orders/99는 주문 하나를 직접 가리킨다. 관계는 반대 방향(/orders/99/customer)으로도 표현할 수 있다
깊이는 컬렉션/항목/컬렉션까지
/customers/1/orders/99/products처럼 관계를 여러 단계 따라가는 URI는 유지하기 어렵고, 나중에 리소스 사이의 관계가 바뀌면 대응하기 어렵다. 마이크로소프트의 API 설계 가이드는 컬렉션/항목/컬렉션보다 복잡한 URI를 요구하지 말라고 권한다. 주문 99를 알고 있다면 /orders/99/products로 바로 가면 된다.
피할 것: /customers/1/orders/99/products
나눠서: /customers/1/orders → 고객 1의 주문 목록
/orders/99/products → 주문 99의 상품 목록관계를 더 따라가야 하면 응답 본문에 관련 리소스의 링크를 넣는 방법(HATEOAS, Hypermedia as the Engine of Application State)도 있다. 한 번에 여러 단계의 관계를 가져와야 하는 화면이 많다면 GraphQL이 맞을 수 있다.
이름 짓기
구글 API 설계 가이드(AIP-122)는 리소스 이름을 publishers/123/books/les-miserables처럼 컬렉션 식별자와 리소스 ID가 번갈아 나오게 짓는다. 컬렉션 식별자는 복수형 명사를 쓴다. 스프링에서 이런 경로를 받는 방법은 스프링 REST 컨트롤러를 본다.
명세: RFC 3986 §3.3 — Path 출처: Microsoft Learn — Web API design best practices: Resource URI naming conventions · Google AIP-122 — Resource names