[네트워크] REST API URI 네이밍 컨벤션과 관행
목차
배경
네트워크와 HTTP에 대한 강의를 듣는 중에 유익한 글을 공유받아 읽어보았는데 내용이 만족스러웠고 평소 고민했던 내용들과 관련되어 있어 많은 도움이 되었다. 한글로 정리해서 공유해 두면 읽기 편할 것 같아 블로그에 정리해 두었다.
(https://restfulapi.net/resource-naming/ 를 읽고 해석하여 정리한 내용입니다. 의역 및 오역이 있을 수 있습니다.)
1. 리소스(Resource)란 무엇인가? (What is a Resource?)
리소스(Resource)란 REST에서 다루는 주요한 데이터들을 일컫는다.
일관적이면서 강력한 REST Resource 네이밍 전략을 갖는 것은 장기적인 관점에서 좋은 설계 방안이 될 것이다.
REST에서 정보의 추상화의 핵심은 자원(Resource)이다. 문서, 이미지, 일시적인 서비스(e.g. 오늘 서울 날씨), 다른 자원(Resource)의 컬렉션(Collection), 실존하는 개체(e.g. 사람) 등 모든 정보들은 리소스(Resource)가 될 수 있다.
즉, 누군가의 href(hypertext reference)의 대상이 될 수 있는 모든 개념들이 곧 자원(Resource)이 된다.
자원(Resource)은 특정 시점의 특정 상황에 부합하는 개체(Entity)가 아니라 일련의 특정 개체들(Entities)에 매핑되는 개념이다.
The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a non-virtual object (e.g., a person), and so on.
In other words, any concept that might be the target of an author’s hypertext reference must fit within the definition of a resource.
A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.
— Roy Fielding’s dissertation
1-1. 싱글톤 자원과 컬렉션 자원(SIngleton and Collection Resources)
자원(Resource)은 싱글톤이거나 컬렉션이다. 예를 들어 (은행 도메인에서) "customers"는 컬렉션 자원이고 "customer"는 싱글톤 자원이다. (도메인(domain) : 소프트웨어적으로 해결할 수 있는 모든 문제)
"customers"라는 컬렉션 자원은 URI "/customers"로 식별할 수 있고 "customer"라는 싱글톤 자원은 URI "/customers/{customerId}"로 식별할 수 있다.
1-2. 컬렉션과 하위 컬렉션 자원(Collection and Sub-collection Resources)
어떠한 자원(Resource)은 하위 컬렉션(Sub-Collection)을 포함할 수 있다.
예를 들어 (은행 도메인에서) customer의 하위 컬렉션인 "accounts" 자원은 /customers/{customerId}/accounts로 식별할 수 있다. 그리고 이와 비슷하게 하위 컬렉션 accounts의 하위 자원 "account"는 /customers/{customerId}/accounts/{accountId}로 식별할 수 있을 것이다.
1-3. URI
REST API는 자원의 위치를 식별하기 위해 URI(Uniform Resource Identifiers)를 사용한다. 따라서 만일 REST API를 개발한다면 그 API를 사용하게 될 클라이언트가 REST API 형식으로 리소스를 주고받을 수 있는 URI를 설계해야 한다. 리소스(Resource)의 이름이 명확하다면 API 또한 직관적이고 사용하기 쉬울 것이다. 만약 그렇지 않다면 이해하고 사용하기 어려운 API가 될 것이다.
2. Best Practices
2-1. 명사(nouns)로 리소스를 표현한다.
명사는 동사에는 없는 속성을 가질 수 있고 리소스의 특성도 명사와 유사하다. 따라서 RESTful URI는 동사(verb) 대신에 명사(noun)로 자원(Resources)을 참조해야 한다.만일 어떤 시스템에서 사용되는 장비를 나타내는 자원이 있다면 명사로 나타낸 그 자원의 URI는 다음과 같을 것이다.
/device-management/manged-devices/{device-id}
어떤 시스템의 사용자를 나타내는 자원이 있다면 그 자원의 URI는 다음과 같을 것이다.
/user-manaement/users/{id}
리소스는 3가지 카테고리(document, collection, store)로 나뉠 수 있다. URI를 설계할 때 식별할 대상이 되는 리소스가 이 3가지 카테고리 중 어떤 카테고리에 속하는지 고민하고 네이밍 컨벤션을 일관적으로 활용한다면 좋은 URI 설계에 도움이 될 것이다.
2-1-1. 도큐먼트(document)
REST에서는 여러 개의 (객체 인스턴스나 데이터베이스 레코드와 같은) 단일 자원(Resource)들이 모여 하나의 컬렉션(Collection)을 이루는데, document resource는 이처럼 컬렉션을 이루는 단일(Singular) 개념이다. documet는 일반적으로 field와 value를 통해 다른 리소스와의 연관관계를 표현한다.
document의 예시는 다음과 같다.
http://api.example.com/device-management/managed-devices/{device-id}
http://api.example.com/user-management/users/{id}
http://api.example.com/user-management/users/admin
2-1-2. 컬렉션(collection)
컬렉션(collection)이란 서버가 관리하는 자원의 디렉토리이다.
클라이언트는 보통 컬렉션에 새로운 자원을 추가해 달라고 서버에 요청(제안)하지만, 새로운 자원을 생성할지 안 할지는 서버가 판단한다. 컬렉션 리소스는 URI에 어떤 자원을 포함할지와 이를 어떻게 표현할지를 결정한다.
컬렉션 자원을 나타낼 때에는 복수를 사용하며 그 예시는 다음과 같다.
/device-management/managed-devices/{device-id}
/user-management/users/{id}
/user-management/users/admin
2-1-3. 스토어(store)
스토어(store)란 클라이언트가 관리하는 리소스 저장소(Repository)이다.
클라이언트는 스토어(Store)에 리소스를 저장하고 꺼내어 사용하며, 제거 시기 등을 결정할 수 있다. (여기서 스토어(store)는 실제로 데이터가 저장될 장소를 나타낸다) 각 저장된 리소스들은 URI를 갖고 있는데, 이 URI는 리소스를 처음 저장소에 저장할 때 클라이언트가 선택한 것이다.
스토어 자원을 나타낼 때에도 복수를 사용하며 그 예시는 다음과 같다.
/song-management/users/{id}/playlists
2-2. 핵심은 일관성(Consistency)이다.
일관적인 리소스 네이밍 컨벤션과 URI 포맷을 사용하는 것은 모호함은 줄이고 가독성을 높이며 유지보수를 용이하게 한다.
URI를 설계할 때 따르는 일반적인 네이밍 컨벤션은 다음과 같다.
2-2-1. 슬래시(/)를 사용하여 계층 구조를 나타낸다.
리소스 간 계층 구조를 나타내기 위해 슬래시(/)를 활용한다. 그 예시는 다음과 같다.
/device-management
/device-management/managed-devices
/device-management/managed-devices/{id}
/device-management/managed-devices/{id}/scripts
/device-management/managed-devices/{id}/scripts/{id}
2-2-2. URI 가장 마지막에는 슬래시(/)를 사용하지 않는다.
계층 구조를 나타내는 URI 경로에서 / 뒤에 아무 자원이 오지 않으면 오히려 혼동이 올 수 있다. 따라서 URI 경로의 마지막 글자 뒤에는 / 를 붙이지 않는다.
/device-management/managed-devices/ (X)
/device-management/managed-devices (O)
2-2-3. URI의 가독성 향상을 위해 하이픈(-)을 활용한다.
다른 사람이 URI을 보고 쉽게 이해할 수 있도록 가독성을 향상하기 위해 긴 글자 사이에는 하이픈(-)을 활용한다.
http://api.example.com/devicemanagement/manageddevices/ (X)
http://api.example.com/device-management/managed-devices (O)
2-2-4. 언더스코어(_)를 사용하지 않는다.
종종 가독성 향상을 위해 하이픈(-) 대신 언더스코어(_)를 사용하기도 한다. 하지만 URI를 설계할 때는 언더스코어(_)를 권장하지는 않는데, 웹 브라우저의 주소창 등에서 언더스코어(_)는 가려져 못 볼 수 있기 때문이다.
이러한 혼란을 줄이기 위해 URI를 설계할 때 가독성을 향상하기 위해 하나의 긴 단어를 분리할 때는 언더스코어(_) 대신 하이픈(-)을 사용한다.
http://api.example.com/device_management/managed_devices (X)
http://api.example.com/device-management/managed-devices (O)
2-2-5. 소문자를 사용한다.
일관되게 대문자만을 혹은 소문자만을 사용한다.
예를 들어 아래 예시에서 1번과 2번은 동일한 자원을 나타내지만 3번은 My-Folder에 의해 다른 자원을 나타내게 된다.
http://api.example.org/my-folder/my-doc //1
HTTP://API.EXAMPLE.ORG/my-folder/my-doc //2
http://api.example.org/My-Folder/my-doc //3
따라서 이러한 혼란을 줄이기 위해 대문자만을 혹은 소문자만을 사용하는데, 소문자를 사용하기 편리한 상황에서는 소문자만을 사용한다. (일반적으로 소문자만을 사용한다)
2-3. 파일 확장자를 사용하지 않는다.
파일 확장자(File extenstion)를 URI 경로에 나타내는 것은 어떠한 이점이 없다. 오히려 URI 경로를 간결하게 표현하기 위해 파일 확장자는 나타내지 않는 게 더 좋다. (URI 경로에 파일 확장자를 남겨둘 이유가 전혀 없다.)
/device-management/managed-devices.xml (X)
/device-management/managed-devices (O)
만일 전달할 리소스의 데이터 형식을 나타내야 한다면, API에 파일 확장자를 사용하는 게 아니라 HTTP의 Content-Type 표현 헤더를 활용한다. xml 데이터를 전송하는 경우 그 예시는 다음과 같다.
Content-Type: application/xml
Content-Type: text/xml
2-4. CRUD를 URI에 (절대) 나타내지 않는다.
URI는 오직 자원(Resource)을 유니크하게 식별(identify) 하기 위해서 사용하며 그에 대한 행위를 구분하지 않는다.
URI에는 자원(명사)만 나타내고 자원을 어떻게 사용하는지(행위, 동사)는 나타내지 않는다. 자원을 어떻게 사용할지는 URI에 나타내는 것이 아니라 HTTP 메서드를 활용하여 나타낸다.
HTTP GET /members // 회원 목록
HTTP POST /members // 회원 등록
HTTP GET /members/{id} // 특정 회원정보 조회
HTTP PUT /members/{id} // 특정 회원정보 수정
HTTP DELETE /members/{id} // 회원 탈퇴
2-5. URI 컬렉션을 필터링하기 위해 Query parameter를 사용한다.
Query parameter를 사용하여 URI 컬렉션을 필터링하거나 정렬한다. 종종 컬렉션 자원을 정렬(sort)하거나, 필터링(filter), 특정 자원에 대해서만 작업(limit)이 필요한데 이때 특정 작업만을 수행하는 새로운 API를 개발하는 것이 아니라 기존 URI에 Query parameter(Query String) 활용한다.
/device-management/managed-devices
/device-management/managed-devices?region=USA
/device-management/managed-devices?region=USA&brand=XYZ
/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date
3. URI에 동사를 사용하지 않는다.
REST URI에 오로지 자원을 명사로만 나타내며, 동사를 사용하지 않는다.
동사는 보통 자원에 대한 행위(또는 작업)를 나타내기 위해 사용하게 되는데, 이러한 행위는 URI에 명시하는 것이 아닌 HTTP Method (GET, POST, PUT, DELETE etc)를 활용하는 게 더 적절하다.
물론 비즈니스에서 이러한 HTTP 메서드로 모든 행위를 나타내는 데에는 한계가 있다. 예를 들어 비즈니스상 URI 자체에 어떤 작업이 포함되어(e.g. 특정 URI로 요청을 하면 '배송 시작 ' 과 같은 특정 작업을 시작하는 경우) URI를 식별자로써 자원 자체를 나타내는 데에 한계가 있는 경우에는 불가피하게 URI 경로에 동사를 두는 방식으로 설계해야 한다.
결론적으로 URI는 동사를 사용하지 않고 명사만을 나타내며 가능한 HTTP 메서드로 해결하고 불가피한 경우에만 컨트룰 URI(경로에 동사를 두는 설계 방식)를 활용한다.
내용 출처 및 원문 : https://restfulapi.net/resource-naming/