URL은 동사가 아니라 명사다
행위는 HTTP 메서드로, 대상은 명사형 URI로 표현하는 REST 리소스 모델링 원칙과 컬렉션/단건 경로 규칙을 정리한다.
API 엔드포인트를 여러 개 새로 늘리던 주였다. 이런 작업에서 가장 먼저 흔들리는 건 URL 명명 규칙이다. getUser, deleteOrder, searchProduct 같은 이름을 그대로 경로에 박으면 엔드포인트가 늘수록 일관성이 무너진다. REST의 핵심은 단순하다. URL은 동사가 아니라 명사다.
왜 명사인가 — 행위와 대상의 분리
HTTP는 이미 동사를 가지고 있다. GET/POST/PUT/PATCH/DELETE가 그것이다. 그래서 URL이 다시 동사를 표현하면 의미가 중복된다. REST는 “무엇을(리소스)”과 “어떻게(메서드)”를 분리한다. URL은 조작 대상인 리소스를 식별하고, 그 리소스에 가하는 행위는 메서드가 담당한다.
GET /users— 사용자 컬렉션 조회GET /users/42— 단건 조회POST /users— 컬렉션에 새 사용자 생성PUT /users/42— 전체 교체,PATCH /users/42— 부분 수정DELETE /users/42— 삭제
이 분리가 주는 실질적 이득은 메서드의 의미론적 속성을 그대로 쓸 수 있다는 점이다. GET은 안전(safe)하고 멱등(idempotent)하다. PUT/DELETE는 안전하진 않지만 멱등하다. POST는 둘 다 아니다. 멱등성은 단순한 이론이 아니라 운영 도구다. 네트워크 타임아웃으로 클라이언트가 재시도할 때, PUT은 몇 번을 보내도 결과가 같으므로 안전하게 재시도할 수 있다. POST는 그렇지 않아 중복 생성을 부른다. URL을 명사로 두고 메서드를 동사로 쓰면 이 속성들이 자동으로 따라온다.
경로 규칙 — 컬렉션과 단건, 그리고 계층
리소스는 보통 두 형태로 노출된다. 컬렉션(/orders)과 그 안의 단건(/orders/1001)이다. 복수형 명사로 컬렉션을 표현하고, 식별자를 붙여 단건을 가리키는 게 관례다. 관계는 경로 중첩으로 표현한다.
1
2
3
GET /users/42/orders # 42번 사용자의 주문 목록
POST /users/42/orders # 42번 사용자에게 주문 생성
GET /orders/1001/items # 1001번 주문의 항목들
행위를 굳이 동사로 표현해야 하는 경우(상태 전이, 검색 등)는 두 가지 길이 있다. 검색·필터는 쿼리스트링으로 푼다.
1
GET /products?category=book&minPrice=10000&sort=-createdAt&page=2
상태 전이처럼 CRUD에 안 맞는 행위는, 그 행위 자체를 하위 리소스로 모델링하면 깔끔하다. “주문을 취소한다”는 POST /orders/1001/cancel보다 취소라는 행위를 자원으로 보고 POST /orders/1001/cancellation처럼 명사화하거나, 상태 필드를 PATCH하는 식으로 표현한다.
운영 함정
함정 1 — 컬렉션 응답에 페이징 메타를 빼먹는 것. GET /orders가 전체를 다 내려주면 데이터가 쌓일수록 응답이 폭발한다. 컬렉션 조회는 처음부터 페이징을 전제로 설계하고, page/size와 총건수를 응답에 포함한다.
함정 2 — 단건 조회의 식별자 누수. GET /users?id=42처럼 단건을 쿼리스트링으로 조회하면 캐싱·로깅·권한 체크 기준이 흐트러진다. 단건은 경로 변수(/users/42)로, 필터는 쿼리스트링으로 분리하는 원칙을 지킨다.
핵심 요약
- URL은 리소스(명사), 메서드는 행위(동사). 이 분리가 멱등성·안전성 같은 HTTP 의미론을 공짜로 준다.
- 컬렉션은 복수 명사(
/orders), 단건은 식별자(/orders/1001), 관계는 중첩으로. - 면접 한 줄 Q&A: “PUT과 PATCH의 차이는?” → PUT은 리소스 전체 교체(멱등), PATCH는 부분 수정. PUT을 같은 본문으로 여러 번 보내도 결과가 동일하다.