URI Design
URI(Uniform Resource Identifier) 디자인은 API 설계의 근본적인 요소로, 개발자 경험과 API의 사용성, 유지보수성에 직접적인 영향을 미친다. 잘 설계된 URI는 API의 직관성을 높이고, 학습 곡선을 완화하며, 리소스의 구조와 관계를 명확히 보여준다.
URI의 기본 개념과 구조
URI는 인터넷에서 특정 리소스를 고유하게 식별하는 문자열이다. API 설계에서 URI는 클라이언트가 서버의 리소스와 상호 작용하는 진입점 역할을 한다.
URI의 구성 요소
URI의 주요 구성 요소를 이해하는 것은 효과적인 디자인의 시작점이다:
- 스킴(Scheme): URI가 사용하는 프로토콜(https, http 등)
- 권한(Authority): 서비스의 도메인 이름 또는 IP 주소
- 포트(Port): 서비스가 수신 대기하는 네트워크 포트(종종 생략됨)
- 경로(Path): 리소스의 위치를 계층적으로 나타내는 문자열
- 쿼리(Query): 리소스에 대한 추가 매개변수(필터링, 정렬 등)
- 프래그먼트(Fragment): 리소스 내의 특정 부분을 가리키는 식별자(일반적으로 API에서 덜 사용됨)
URI vs. URL vs. URN
URI 개념을 정확히 이해하기 위해서는 관련 용어의 차이점을 아는 것이 중요하다:
- URI(Uniform Resource Identifier): 리소스를 식별하는 문자열의 일반적인 개념
- URL(Uniform Resource Locator): 리소스의 위치를 지정하는 URI의 하위 집합(예: https://api.example.com/users)
- URN(Uniform Resource Name): 리소스의 이름을 지정하는 URI의 하위 집합, 위치에 상관없이 고유한 이름으로 식별(예: urn:isbn:0451450523)
URI 디자인의 핵심 원칙
효과적인 URI 디자인은 몇 가지 핵심 원칙을 따른다. 이러한 원칙은 RESTful API 설계에서 특히 중요하지만, 다른 API 스타일에도 적용할 수 있다.
리소스 중심 디자인
URI는 동작이 아닌 리소스(명사)를 중심으로 설계해야 한다. 리소스는 API가 노출하는 개체나 개념이다.
좋은 예:
피해야 할 예:
리소스는 명사로 표현되며, HTTP 메서드(GET, POST, PUT, DELETE 등)가 리소스에 대한 작업을 나타낸다.
계층적 구조
URI 경로는 리소스 간의 관계를 계층적으로 표현해야 한다:
|
|
이 URI는 “42번 부서에 소속된 직원들"이라는 관계를 명확히 보여준다. 계층 구조는 데이터 모델의 관계를 반영하고, 이해하기 쉬운 API를 만드는 데 도움이 된다.
일관성과 예측 가능성
URI 패턴은 API 전체에서 일관되게 적용되어야 한다. 이는 개발자가 API를 더 쉽게 학습하고 사용할 수 있게 해준다.
일관된 복수형 사용:
일관된 매개변수 스타일:
간결성과 명확성
URI는 가능한 한 간결하면서도 명확해야 한다. 불필요한 세그먼트나 정보는 피해야 한다.
간결한 예:
|
|
과도하게 복잡한 예:
|
|
대소문자 처리
URI 경로는 일반적으로 소문자를 사용하는 것이 권장된다. 이는 URI가 대소문자를 구분하기 때문에 혼란을 줄이는 데 도움이 된다.
권장:
|
|
권장하지 않음:
|
|
특수 문자 처리
URI에는 제한된 문자 집합만 직접 사용할 수 있으며, 다른 문자는 인코딩되어야 한다. 가능한 한 URL 인코딩이 필요한 특수 문자 사용을 최소화하는 것이 좋다.
권장:
|
|
피해야 할 예:
|
|
리소스 명명 전략
리소스 이름을 선택하는 것은 URI 디자인의 핵심 측면이다. 명확하고 의미 있는 리소스 이름은 API를 직관적으로 만든다.
명사와 복수형
리소스 이름으로는 일반적으로 명사의 복수형을 사용한다. 이는 리소스가 대개 객체의 컬렉션을 나타내기 때문이다.
단, 단일 항목을 나타내는 개념이나 집합체의 경우 단수형을 사용할 수 있다:
구체적인 이름 사용
리소스 이름은 가능한 한 구체적이어야 한다. 애매한 이름이나 지나치게 일반적인 이름은 피해야 한다.
좋은 예:
피해야 할 예:
합성어와 구분자
여러 단어로 구성된 리소스 이름은 구분자를 사용해 명확하게 표현해야 한다. 일반적으로 하이픈(-
)이 선호된다.
권장:
피해야 할 예:
CRUD 작업과 동사
기본 CRUD(Create, Read, Update, Delete) 작업은 URI에 동사를 사용하지 않고 HTTP 메서드로 표현한다:
- Create: POST /resources
- Read: GET /resources 또는 GET /resources/{id}
- Update: PUT /resources/{id} 또는 PATCH /resources/{id}
- Delete: DELETE /resources/{id}
동작을 나타내는 리소스
일부 작업은 간단한 CRUD로 표현하기 어려울 수 있다. 이런 경우에는 다음과 같은 접근 방식을 고려할 수 있다:
하위 리소스로 모델링:
1
POST /users/42/profile-picture
별도의 리소스로 모델링:
1
POST /password-reset-requests
컨트롤러 패턴 사용 (동사 허용, 특별한 경우에만):
이 경우 동사는 콜론(
:
)으로 구분하여 특별한 작업임을 나타낸다.
경로 변수와 쿼리 매개변수
URI 디자인에서 경로 변수와 쿼리 매개변수를 언제, 어떻게 사용할지 결정하는 것은 중요한 고려사항이다.
경로 변수
경로 변수는 리소스 식별자 또는 필수적인 정보를 표현하는 데 사용된다:
경로 변수는 일반적으로 리소스를 식별하거나 하위 리소스 관계를 나타내는 데 사용된다.
쿼리 매개변수
쿼리 매개변수는 필터링, 정렬, 페이지네이션, 검색 등 선택적인 작업이나 매개변수를 표현하는 데 사용된다:
쿼리 매개변수의 주요 사용 사례:
필터링:
1
/products?category=electronics&brand=samsung
정렬:
1
/products?sort=price&direction=asc
페이지네이션:
1
/products?page=2&per_page=25
검색:
1
/products?q=smartphone
확장 제어:
1
/users/42?expand=orders,preferences
경로 변수 vs. 쿼리 매개변수 선택 가이드라인
언제 경로 변수를 사용하고 언제 쿼리 매개변수를 사용해야 할지 구분하는 가이드라인:
경로 변수 사용:
- 리소스 식별자(ID)
- 리소스 계층 구조의 필수 부분
- 리소스의 주요 속성
쿼리 매개변수 사용:
- 선택적 필터링
- 정렬 조건
- 페이지네이션 매개변수
- 검색 쿼리
- 포맷 지정(
?format=json
) - 필드 선택(
?fields=name,email,phone
)
버전 관리 전략
API 버전 관리는 하위 호환성을 유지하면서 API를 발전시키는 데 필수적이다. URI 디자인 관점에서 몇 가지 주요 버전 관리 접근 방식이 있다.
URI 경로 버전 관리
가장 일반적인 접근 방식으로, URI 경로에 버전을 포함한다:
장점:
- 명시적이고 이해하기 쉬움
- 클라이언트 구현이 간단함
- 다양한 버전을 동시에 지원하기 쉬움
단점:
- URI가 변경되어 캐싱에 영향을 줄 수 있음
- URI가 리소스 식별보다 API 버전에 초점을 맞추는 것처럼 보일 수 있음
쿼리 매개변수 버전 관리
버전 정보를 쿼리 매개변수로 전달한다:
장점:
- 기본 URI는 변경되지 않음
- 버전 지정이 선택적일 수 있음
단점:
- 기본 버전에 대한 규칙이 필요함
- 다른 쿼리 매개변수와 함께 사용 시 복잡해질 수 있음
헤더 기반 버전 관리
HTTP 헤더를 사용하여 버전을 지정한다:
또는 사용자 정의 헤더:
|
|
장점:
- URI가 깨끗하게 유지됨
- HTTP 콘텐츠 협상 메커니즘과 일치함
단점:
- 테스트하기 어려울 수 있음(URL만으로는 충분하지 않음)
- 문서화가 더 복잡할 수 있음
- 웹 브라우저에서 직접 사용하기 어려움
버전 관리를 위한 권장 사항
버전 관리 방식 선택은 API 요구사항과 대상 사용자에 따라 달라진다. 하지만 일반적인 권장 사항은 다음과 같다:
- 경로 기반 버전 관리: 가장 명시적이고 이해하기 쉬운 방식
- 메이저 버전만 URI에 포함: 마이너 변경사항은 하위 호환성을 유지하면서 구현
- 첫 버전부터 버전 번호 포함: 나중에 버전 관리를 추가하는 것보다 처음부터 시작하는 것이 좋음
- 최소 두 개의 활성 버전 지원: 클라이언트에게 마이그레이션 시간 제공
URI 패턴과 관례
API 설계에서 널리 사용되는 몇 가지 일반적인 URI 패턴과 관례를 살펴보면:
표준 컬렉션 패턴
컬렉션 리소스와 개별 리소스 항목에 대한 표준 패턴:
- 컬렉션:
/resources
- 특정 항목:
/resources/{id}
예:
하위 리소스 패턴
관련 리소스를 표현하기 위한 패턴:
이 패턴은 부모 리소스와 자식 리소스 간의 관계를 명확하게 표현한다.
필터, 정렬, 페이지네이션 관례
데이터 조작을 위한 일반적인 쿼리 매개변수 패턴:
필터링:
정렬:
페이지네이션:
확장 및 필드 선택 패턴
클라이언트가 반환되는 데이터를 제어할 수 있게 하는 패턴:
확장(관련 리소스 포함):
|
|
필드 선택(응답 크기 제한):
|
|
검색 및 복잡한 쿼리 패턴
검색 및 복잡한 작업을 위한 패턴:
간단한 검색:
|
|
고급 검색:
|
|
복잡한 필터링:
|
|
URI 길이 제한 및 복잡성 관리
URI는 실용적인 길이 제한 내에서 설계되어야 하며, 복잡성을 관리해야 한다.
URI 길이 제한
여러 브라우저, 서버, 도구는 URI 길이에 제한을 둔다:
- 대부분의 브라우저: 2000~8000자 사이
- 서버(Apache, NGINX 등): 구성에 따라 다양함
API URI 길이에 대한 실용적인 가이드라인:
- 경로 부분: 가능한 짧게 유지(일반적으로 255자 미만)
- 쿼리 문자열: 최대 2000자 이내로 유지
- 전체 URI: 가능한 8000자 미만으로 유지
복잡한 쿼리 처리
복잡한 쿼리나 필터링이 필요한 경우 다음 접근 방식을 고려할 수 있다:
구조화된 쿼리 문자열 사용:
1
/products?filter[category]=electronics&filter[price][min]=100&filter[price][max]=500
JSON 기반 쿼리 언어:
1
/products?filter={"category":"electronics","price":{"min":100,"max":500}}
이 경우 URL 인코딩 필요:
1
/products?filter=%7B%22category%22%3A%22electronics%22%2C%22price%22%3A%7B%22min%22%3A100%2C%22max%22%3A500%7D%7D
POST 기반 검색 엔드포인트 사용:
전용 검색 리소스 생성:
응답은 검색 결과에 대한 참조를 포함한다:
이후 결과 조회:
1
GET /searches/search-abc123/results
URI 중첩 깊이 관리
URI 경로의 중첩 깊이는 관리 가능한 수준으로 유지해야 한다. 일반적으로 2-3 수준의 중첩이 실용적이다.
관리 가능한 중첩:
|
|
과도한 중첩:
|
|
과도한 중첩을 피하기 위한 전략:
- 핵심 관계만 중첩: 가장 중요한 관계만 URI에 포함
- 평평한 구조 사용: 필요한 경우 쿼리 매개변수로 관계 표현
- HATEOAS 사용: 응답에 관계를 링크로 포함
API 종류별 URI 디자인 접근법
다양한 API 스타일에 따라 URI 디자인 접근법이 달라질 수 있다.
REST API의 URI 디자인
REST API는 리소스 중심 URI 설계를 강조한다:
- 리소스는 명사로 표현
- HTTP 메서드로 작업 표현
- 계층적 구조로 관계 표현
- 상태 없음(Stateless)
REST API URI 예:
GraphQL API의 URI 디자인
GraphQL API는 일반적으로 단일 엔드포인트를 사용하며, 쿼리 자체에 필요한 데이터 구조를 정의한다:
|
|
요청 본문:
GraphQL에서는 URI 디자인이 간단한 반면, 쿼리 언어가 복잡성을 처리한다.
RPC 스타일 API의 URI 디자인
RPC(Remote Procedure Call) 스타일 API는 동작 중심이며, 함수 호출과 유사하다:
또는 RESTful 접근 방식과 혼합:
gRPC 및 프로토콜 버퍼
gRPC는 일반적으로 HTTP/2 위에서 작동하며, URI는 서비스와 메서드를 식별한다:
|
|
예:
URI 디자인 문서화 및 스타일 가이드
조직 내에서 일관된 URI 디자인을 보장하기 위해 명확한 문서화와 스타일 가이드가 필요하다.
API 스타일 가이드 작성
API 스타일 가이드는 다음 내용을 포함해야 한다:
- 리소스 명명 규칙:
- 복수형 vs 단수형
- 대소문자 규칙(일반적으로 소문자)
- 합성어 처리(하이픈, 언더스코어 등)
- URI 구조 규칙:
- 계층 깊이 제한
- 경로 변수 형식
- 쿼리 매개변수 명명
- 표준 매개변수:
- 필터링 매개변수 형식
- 정렬 매개변수 형식
- 페이지네이션 매개변수
- 버전 관리 규칙:
- 버전 관리 방식(URI, 헤더 등)
- 버전 형식(v1, v2 등)
- 표준 패턴:
- 컬렉션과 항목 패턴
- 하위 리소스 표현
- 특수 작업 처리 방법
- 예외 처리:
- 특수 케이스 처리 방법
- 비표준 작업 허용 기준
- 검색 및 필터링 표준:
- 단순 검색 매개변수
- 고급 필터링 구문
URI 설계 문서화
각 API 엔드포인트에 대한 문서는 다음 정보를 포함해야 한다:
- URI 패턴: 전체 URI 패턴 및 예시
- 매개변수 설명: 모든 경로 변수와 쿼리 매개변수에 대한 세부 설명
- 지원되는 매개변수:
- 필수 vs 선택적 매개변수
- 기본값
- 허용 범위 또는 열거형 값
- 관계: 다른 리소스와의 관계 설명
- 예시: 다양한 사용 사례에 대한 URI 예시
예시:
|
|
OpenAPI/Swagger 사용
OpenAPI(Swagger) 규격을 사용하여 API URI를 문서화하는 것이 좋다. 이는 인터랙티브 문서와 클라이언트 생성을 가능하게 한다:
|
|
URI 설계의 실용적 측면
URI 설계에는 이론적 원칙 외에도 실용적인 측면이 있다.
URI 설계의 반복적 개선
API URI 설계는 일반적으로 반복적인 프로세스이다:
- 초기 설계: 기본 리소스와 관계 식별
- 리뷰 및 피드백: 팀 및 API 소비자로부터 피드백 수집
- 프로토타이핑: 설계로 간단한 프로토타입 구현
- 테스트: 다양한 사용 사례 테스트
- 개선: 문제점 해결 및 개선
- 문서화: 최종 설계 문서화
- 반복: 새로운 요구사항에 따라 계속 개선
이 과정에서 처음부터 완벽한 설계보다는 적응 가능하고 점진적으로 개선할 수 있는 설계가 중요하다.
URI 설계 검토
API URI 설계를 검토할 때 고려해야 할 핵심 질문:
- 일관성: URI가 API 전체에서 일관된 패턴을 따르는가?
- 명확성: URI가 리소스와 관계를 명확하게 표현하는가?
- 간결성: URI가 불필요하게 길거나 복잡하지 않은가?
- 인간 친화성: URI를 쉽게 읽고 기억할 수 있는가?
- 기술적 제약: URI가 장기적인 캐싱, 프록시 등의 기술적 고려사항을 만족하는가?
- 확장성: 새로운 기능과 리소스를 추가할 수 있는 유연성이 있는가?
- 호환성: 기존 시스템 및 클라이언트와 호환되는가?
URI 설계에서 흔한 실수와 함정
URI 설계에서 피해야 할 일반적인 실수:
- 동사 기반 URI:
/getUsers
,/createUser
등의 동사 사용 - 비일관적인 명명: 일부는 복수형, 일부는 단수형 사용
- 불필요한 접두사:
/api/v1/api-resources/
등의 중복적인 접두사 - 깊은 중첩: 지나치게 깊은 계층 구조 사용
- 기술 세부 사항 노출:
/users/{id}/fetch-from-database
등의 구현 세부 사항 노출 - 비일관적인 대소문자: 혼합된 대소문자 스타일 사용
- 구분자 불일치: 접두사를 위해 하이픈(
-
) 사용, 나머지에 언더스코어(_
) 사용 등
일반적인 URI 설계 트레이드오프
URI 설계에서 자주 발생하는 트레이드오프와 해결 방안:
- 단순성 vs 표현력:
- 트레이드오프: 단순한 URI는 이해하기 쉽지만 표현력이 제한될 수 있음
- 해결 방안: 기본 URI는 단순하게 유지하되, 필요한 경우 쿼리 매개변수로 표현력 확장
- 일관성 vs 특수 케이스:
- 트레이드오프: 엄격한 일관성을 유지하면 특별한 경우를 처리하기 어려울 수 있음
- 해결 방안: 일관된 기본 패턴을 정의하고, 문서화된 예외 허용
- REST 순수성 vs 실용적 설계:
- 트레이드오프: 순수 REST 원칙은 현실적 제약과 충돌할 수 있음
- 해결 방안: 핵심 REST 원칙을 따르되, 필요한 경우 실용적 접근 허용
- URI 길이 vs 표현력:
- 트레이드오프: 표현력을 높이면 URI가 길어질 수 있음
- 해결 방안: 복잡한 쿼리는 POST 요청이나 전용 검색 리소스로 해결
용어 정리
용어 | 설명 |
---|---|