한 줄 요약: 좋은 API URI 설계는 리소스(명사)를 식별하는 데 집중하고, 행위(조회/등록/수정/삭제)는 HTTP 메서드로 표현한다.

비유로 이해하는 URI와 HTTP 메서드

도서관을 생각해보자.

  • URI = 책의 위치 정보 (예: “3층 > 컴퓨터공학 > 자바 > 312번 책”)
  • HTTP 메서드 = 도서관에서 하는 행위 (책을 빌린다, 반납한다, 새 책을 등록한다)

책의 위치(URI)에 행위(“빌리기”, “반납하기”)를 섞으면 위치 체계가 복잡해진다. 위치는 위치답게, 행위는 행위답게 분리하는 것이 좋은 설계다.


API URI 설계 원칙

나쁜 URI 설계 예시

GET  /read-member-list       ← 행위(read)가 URI에 포함
POST /create-member          ← 행위(create)가 URI에 포함
PUT  /update-member          ← 행위(update)가 URI에 포함
DELETE /delete-member        ← 행위(delete)가 URI에 포함

URI에 행위 동사가 들어가면 문제가 생긴다. “수정하면서 조회도 해야 하면?” 같은 상황에서 URI가 폭발적으로 늘어난다.

좋은 URI 설계 — 리소스 중심

GET    /members              ← 회원 목록 조회
GET    /members/{id}         ← 특정 회원 조회
POST   /members              ← 회원 등록
PUT    /members/{id}         ← 회원 전체 수정
PATCH  /members/{id}         ← 회원 일부 수정
DELETE /members/{id}         ← 회원 삭제

핵심 원칙:

  • URI는 리소스(명사) 만 식별한다
  • 행위(동사) 는 HTTP 메서드로 표현한다
  • 리소스는 복수형 명사로 표현한다 (/members, /orders)

URI 설계 흐름

sequenceDiagram
    participant C as "클라이언트"
    participant S as "서버"

    C->>S: "1. GET /members — 회원 목록 조회"
    S-->>C: "2. 200 OK + 회원 목록 JSON"

    C->>S: "3. POST /members — 새 회원 등록"
    Note over C: "Body: {name: 홍길동, age: 30}"
    S-->>C: "4. 201 Created + Location: /members/100"

    C->>S: "5. GET /members/100 — 특정 회원 조회"
    S-->>C: "6. 200 OK + 회원 정보 JSON"

    C->>S: "7. PATCH /members/100 — 회원 일부 수정"
    Note over C: "Body: {age: 31}"
    S-->>C: "8. 200 OK"

    C->>S: "9. DELETE /members/100 — 회원 삭제"
    S-->>C: "10. 200 OK 또는 204 No Content"

HTTP 메서드 상세

GET — 리소스 조회

GET /members/100 HTTP/1.1
Host: api.example.com
Accept: application/json
  • 서버에서 리소스를 조회할 때 사용한다
  • 데이터를 전달할 때는 쿼리 파라미터(Query String) 를 사용한다
  • 메시지 바디에 데이터를 담는 것은 지원하지 않는 서버가 많아 권장하지 않는다
# 쿼리 파라미터로 검색 조건 전달
curl "https://api.example.com/members?age=30&city=서울"

POST — 데이터 처리 (주로 등록)

POST /members HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "홍길동",
  "age": 30
}
  • 메시지 바디를 통해 서버에 데이터를 전달한다
  • 주로 새 리소스 등록에 사용하지만, 범용적으로 “요청 데이터 처리”에 쓸 수 있다
  • 다른 메서드로 처리하기 애매한 경우 POST를 사용한다

POST의 다양한 활용:

POST /orders                  ← 주문 생성
POST /members/100/follow      ← 팔로우 (프로세스 처리)
POST /payment/validate        ← 결제 검증 (복잡한 쿼리 → POST)

PUT — 리소스 전체 교체

PUT /members/100 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "홍길동",
  "age": 31
}
  • 클라이언트가 리소스의 전체 내용을 알고 URI를 직접 지정한다 (POST와의 차이점)
  • 해당 URI에 리소스가 없으면 생성, 있으면 전체 교체한다
  • 일부 필드만 보내면 나머지 필드가 삭제된다
기존: { "name": "홍길동", "age": 30, "city": "서울" }
PUT:  { "name": "홍길동", "age": 31 }
결과: { "name": "홍길동", "age": 31 }  ← city 필드 삭제됨!

PATCH — 리소스 부분 수정

PATCH /members/100 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "age": 31
}
  • 리소스의 일부 필드만 수정한다
  • 보내지 않은 필드는 그대로 유지된다
기존: { "name": "홍길동", "age": 30, "city": "서울" }
PATCH: { "age": 31 }
결과: { "name": "홍길동", "age": 31, "city": "서울" }  ← city 유지

DELETE — 리소스 삭제

DELETE /members/100 HTTP/1.1
Host: api.example.com
  • 리소스를 삭제한다
  • 성공 시 200 OK 또는 204 No Content를 반환한다

HTTP 메서드 속성 비교

안전성 (Safe)

호출해도 리소스를 변경하지 않는 메서드를 안전하다고 한다.

메서드 안전 여부 이유
GET O 리소스 조회만 함
HEAD O 헤더만 조회
POST X 리소스 생성/변경
PUT X 리소스 교체
PATCH X 리소스 수정
DELETE X 리소스 삭제

멱등성 (Idempotent)

동일한 요청을 여러 번 보내도 결과가 같은 메서드를 멱등하다고 한다.

메서드 멱등 여부 이유
GET O 같은 데이터를 100번 조회해도 결과 동일
PUT O 같은 내용으로 100번 교체해도 최종 결과 동일
DELETE O 이미 삭제된 리소스를 다시 삭제해도 결과 동일
POST X 두 번 호출하면 같은 주문이 2개 생성될 수 있음
PATCH 조건부 { age: 31 } 방식은 멱등, { age: +1 } 방식은 비멱등

멱등성의 실무 활용:

  • 서버가 타임아웃으로 응답을 못 줬을 때, 클라이언트가 재요청해도 되는지 판단하는 근거
  • GET, PUT, DELETE는 재요청 가능. POST는 중복 처리 주의

캐시 가능성 (Cacheable)

메서드 캐시 가능 여부
GET O (실무에서 주로 사용)
HEAD O
POST 이론상 가능, 실무에서는 거의 미사용
PUT, PATCH, DELETE X

클라이언트에서 서버로 데이터 전송

방식 1: 쿼리 파라미터

# 정렬, 필터, 검색어 전달에 주로 사용
GET /members?age=30&city=서울&sort=name

방식 2: 메시지 바디 (application/json)

curl -X POST https://api.example.com/members \
  -H "Content-Type: application/json" \
  -d '{"name":"홍길동","age":30}'

방식 3: HTML Form 데이터

POST /members HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=30

방식 4: 파일 업로드 (multipart)

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----boundary

----boundary
Content-Disposition: form-data; name="name"

홍길동
----boundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

(바이너리 데이터)

HTTP API 설계 패턴

컬렉션(Collection) 기반 — POST 등록

서버가 리소스 URI를 생성하고 관리한다.

GET    /members          회원 목록 조회
POST   /members          회원 등록 → 서버가 /members/100 URI 생성
GET    /members/{id}     특정 회원 조회
PATCH  /members/{id}     회원 수정
DELETE /members/{id}     회원 삭제

스토어(Store) 기반 — PUT 등록

클라이언트가 리소스 URI를 알고 직접 지정한다.

GET    /files            파일 목록
GET    /files/{filename} 파일 조회
PUT    /files/{filename} 파일 업로드 (URI 클라이언트가 지정)
DELETE /files/{filename} 파일 삭제

컨트롤 URI — 동사 사용 (불가피한 경우)

HTTP 메서드로 표현하기 어려운 행위는 동사 URI를 허용한다.

POST /members/{id}/follow     팔로우 처리
POST /orders/{id}/cancel      주문 취소
POST /members/{id}/change-password  비밀번호 변경

핵심 포인트 정리

메서드 용도 멱등 안전
GET 리소스 조회 O O
POST 데이터 처리, 등록 X X
PUT 리소스 전체 교체 O X
PATCH 리소스 부분 수정 조건부 X
DELETE 리소스 삭제 O X
  • URI는 명사(리소스)로, 행위는 HTTP 메서드(동사)로 표현한다
  • PUT은 전체 교체이므로 일부 필드만 보내면 나머지가 삭제된다. 부분 수정은 PATCH를 사용한다
  • 멱등성은 네트워크 오류 시 재요청 가능 여부를 판단하는 중요한 기준이다
  • POST는 다른 메서드로 처리하기 애매할 때 사용하는 만능 메서드이기도 하다

카테고리:

업데이트:

댓글