미끄러운 경사
Source: Dev.to
위에 제공된 내용 외에 번역할 텍스트가 없습니다. 번역이 필요한 전체 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.
소개
최근 LinkedIn 게시물에서는 쿼리 매개변수를 사용해 리소스의 대체 표현을 요청하는 방법에 대해 논의했습니다. 필요한 필드만 투영(projection)하는 아이디어이며, 예를 들어:
GET /users/1
Response
HTTP/1.1 200 OK
Content-Type: application/json
{"username": "Name", "displayName": "Display Name"}
클라이언트가 displayName만 필요하다면 요청을 다음과 같이 작성할 수 있습니다:
GET /users/1?fields=displayName
Response
HTTP/1.1 200 OK
Content-Type: application/json
{"displayName": "Display Name"}
겉보기에는 합리적으로 보이지만, 쿼리 컴포넌트의 적절한 사용에 대해 여러 의문이 제기됩니다.
RFC 3986 개요
RFC 3986은 URI의 일반 구문을 정의합니다:
scheme ":" hier-part [ "?" query ] [ "#" fragment ]
- Scheme – 프로토콜 식별자(예:
http,ftp). 대소문자 구분 없음. - Authority – 호스트, 선택적 포트 및 선택적 사용자 정보(예:
//www.example.com:80). - Path – 리소스를 식별하는 계층적 세그먼트 시퀀스.
- Query – 비계층적 데이터, 일반적으로
key=value쌍이며?로 시작. - Fragment – 보조 리소스 식별자(예: 페이지 앵커),
#로 시작; 사용자 에이전트가 처리하고 서버는 처리하지 않음.
이 사양은 쿼리 문자열을 불투명한 키‑값 쌍 집합으로 취급한다. fields, sort, filter와 같은 매개변수에 대한 의미를 규정하지 않는다.
Source: …
쿼리 매개변수를 미니‑DSL로 사용하기
쿼리 매개변수를 명령어(예: fields, sort, filter)로 활용하면 제한된 HTTP 동사 집합 위에 도메인‑특화 언어(DSL)가 효과적으로 만들어집니다. 예시:
GET /users/1?fields=displayName
GET /users/?sort=
GET /users/?filter=
GET /users?id=1&fields=displayName
여기서 id는 검색 매개변수이고, fields는 투영 명령어입니다. 시간이 지나면서 이러한 관례는 사실상의 표준이 될 수 있으며, API 설계자는 추가 문서를 유지하고 이 키워드들을 예약어로 취급해야 하는 부담을 안게 됩니다.
왜 이것이 문제인가
- URI의 불투명성 – RFC 3986은
?뒤의 모든 것을 불투명하게 취급합니다; 이를 DSL로 해석하면 숨겨진 계약이 생깁니다. - 동사와 명령어의 결합 – 제한된 HTTP 메서드 집합에 추가 의미가 과부하됩니다.
- 유지 보수 부담 – 새로운 명령어(예:
fields,sort)가 추가될 때마다 문서화와 클라이언트 인지가 필요합니다.
대안으로서의 콘텐츠 협상
Projection은 근본적으로 표현에 관한 문제입니다. HTTP는 이미 클라이언트가 특정 표현을 요청할 수 있는 메커니즘을 제공합니다: 콘텐츠 협상.
GET /users/1
Accept: application/json; fields=displayName
Accept 헤더는 다음과 같이 미디어 타입 매개변수를 포함할 수 있습니다:
GET /users/1
Accept: application/json; q=0.9; charset=UTF-8
이러한 매개변수는 개방형이며, 서버가 필요에 따라 해석할 수 있습니다.
RFC 6906 – Profile 매개변수
RFC 6906은 표현 변형을 요청하는 보다 구조화된 방법을 도입합니다:
GET /users/1
Accept: application/json; profile="http://www.example.com/profiles/user-summary"
또는, 사용자 정의 미디어 타입을 정의할 수도 있습니다:
GET /users/1
Accept: application/vnd.user.displayname+json
실용적인 고려사항
-
링크 – 리소스를 생성할 때, 원하는 프로젝션을 이미 포함한 URL을 전달하는 것이 더 간단합니다:
Location: /users/1?fields=displayName추가 헤더를 사용해 프로젝션 지시를 전달하면 응답이 복잡해집니다.
-
캐싱 – 캐시는 기본적으로 사용자 정의 쿼리‑파라미터 의미를 인식하지 못합니다. 적절한 캐싱을 위해서는 명시적인
Vary:헤더가 필요하며, 구현에 따라 동작이 달라져 캐시 미스나 오래된 데이터가 발생할 수 있습니다.
결론
쿼리 파라미터 단축키가 널리 사용된다고 해서 그것이 자동으로 아키텍처상의 모범 사례가 되는 것은 아닙니다. 실용적이긴 하지만, 이러한 단축키는 숨겨진 계약을 도입하고 REST가 제공하려는 명확성과 상호 운용성을 약화시킬 수 있습니다. 기억하세요:
“아키텍처는 잘못된 결정이 아니라, 잊혀진 이유 때문에 무너지게 된다.”
이러한 단축키를 관례(convention)로 명시적으로 문서화하고, 표준이 아니라는 점을 명확히 하면 의도를 보존하고, 확립된 HTTP 의미론에서 벗어나는 미끄러운 경사를 방지할 수 있습니다.