단순 조회는 무조건 GET이어야 할까?
API를 설계할 때, 단순 조회 기능이라면 무조건 GET을 사용하는 것이 좋을까?
조건이 늘어나고, URL이 복잡해지고, 필터가 많아지더라도?
아래 메서드의 API는 날짜와 테마를 통해서 예약이 가능한 시간을 응답하는 GET 요청이다. 그리고 다음과 같은 피드백을 받았다.
모든 것을 http get 메서드 uri 를 통해 표현할 필요는 없습니다. 우리는 예약 가능한 시간을 얻어오는 것이고, 이에 필요한 리소스는 http post 의 body 를 통해 표현하는 것이 확장하는데 있어서도 효율적일거에요 :)
모든 조회를 GET으로 표현하지 않아도 괜찮구나!
당연히 조회 요청은 GET메서드만 사용해야 한다는 생각을 하고 있었다.
Microsoft의 Azure Search REST API 에서는
복잡한 검색 조건을 처리할 때 POST 요청을 사용하도록 권장하고 있다.
그럼 반대로 말해서 GET과 POST 요청중에 아무거나 사용해도 상관이 없다는 얘기인데,
왜 둘을 구별하고 있을까?
의도 표현
조회와 생성 등, 모든 요청을 POST로 보내게 되면 의도 파악이 어려워진다.
그래서 HTTP 메서드를 통해서 의도를 표현해야 한다.
그럼 왜 의도를 표현해야 할까?
API는 사용자와 개발자간의 인터페이스
API는 사용자와 개발자간의 의사소통 통로다. 여기서 사용자란 이용자, 다른 개발자, 클라이언트 개발자 모두를 뜻한다.
웹 서비스는 API를 통해서 어떤 기능이 있는지 알 수 있다. 이렇게 API가 중요하기 때문에 문서화 까지 한다.
API를 통해서 기능의 의도를 파악하고 어떤 기능을 하는지 예측할 수 있다.
GET /users/123 // 사용자 정보 조회 (의미 명확)
POST /users/123 // ??? 뭐하는 요청이지? 조회인가? 수정인가?
HTTP 메서드로 의도를 표현해주지 않으면 API가 무슨 의미인지 한번에 알 수 없다. 부가적인 설명이 필요하다.
REST의 기본 원칙은 리소스를 URL로 표현하고, 그 리소스에 어떤 행위를 할지 메서드로 구분한다.
의도와 현실 사이
그렇다면 기존의 GET 요청을 POST 요청으로 변경해보았다.
GET /reservations/dates{date}/themes/{themeId}/times //변경 전
POST /reservations/available-times //변경 후
POST로 변경 했을 때 몇가지 장점을 얻을 수 있었다.
- 바디에 JSON 등을 담아 더 복잡한 조건 전달 가능 (예: 여러 날짜, 필터 조건
- URL 노출을 줄일 수 있음
- 데이터 양이 많아져도 문제없음 (바디에 담기 때문에)
- URL보다 훨씬 큰 데이터를 body에 담을 수 있다.
간결하고 좋아보인다!
하지만 API를 봤을 때 의미가 헷갈린다.
예약 가능한 시간을 POST 하라?
의미가 이상하다.
"예약 가능한 시간을 생성하라" vs "예약 가능한 시간을 조회하라"
나는 후자가 더 자연스럽게 느껴진다.
하지만 기존의 GET 요청도 단점이 있다.
- date나 themeId가 길거나 복잡하면 URL 길이 제한에 걸릴 수 있음
- 필요한 정보가 추가되면 URL이 복잡해짐
- 민감한 정보가 포함되면 URL 노출 위험
🤔 그럼 GET 요청에 body를 담아서 요청하면 되지 않나?
HTTP 표준(RFC 7231)에 따르면
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
GET 요청 메시지에 포함된 페이로드(즉, 본문)는 정의된 의미가 없습니다.
GET 요청에 본문을 포함하면 일부 구현에서는 요청을 거부할 수 있습니다.
GET 요청은 일반적으로 '데이터 조회'를 위한 메서드로 정의된다. 일반적으로 본문이 필요하지 않기 때문에
GET 요청에 본문(body)를 넣는 것은 기술적으로는 가능하지만 권장되지 않는 방법이다. 라고 설명한다.
GET, POST 비교 테이블
GET | POST | |
기본 목적 | 데이터 조회 (Read) | 리소스 생성, 조건 기반 요청 전송 |
요청 본문 (Body) | ❌ 비 권장 (RFC 상 무의미) | ✅ 자유롭게 사용 가능 (JSON 등 포함) |
URL 사용 | ❗ 파라미터 포함 (쿼리 스트링) | ✔️ URL은 깔끔하게 유지, 파라미터는 Body에 포함 |
URL 길이 제한 | ❗ 존재 (보통 2KB ~ 8KB) | ✅ 사실상 제한 없음 (서버 설정에 따라 수 MB~GB) |
멱등성 | ✅ 동일 요청은 동일 응답 | ❌ 요청마다 결과가 달라질 수 있음 |
사용 용도 | 모두 조회, 단건 조회 | 필터 조건이 많은 검색, 저장, 업로드 |
두 요청 모두 동일한 응답을 하고 있다.
여기서 고민을 할 수 있다.
어느쪽을 선택하는것이 좋을까?
정답은 없지만 나의 생각은
우선, HTTP 메서드의 의도에 맞는 방식으로 출발하는 것이 좋다고 생각한다.
예시와 같이 간단한 요청은 굳이 POST를 사용하지 않아도 된다고 생각한다. 장점들이 있지만, 그것보단 의도의 파악이 더 중요하다고 생각하기 때문이다.
만약 민감한 정보가 URL상으로 노출이 된다거나, 복잡한 조건이 추가 되어야 한다면 그땐 POST로 변경해도 괜찮을 것 같다.
검색 조건이 POST로 요청을 보냈을 때 조회의 의도를 담아야 한다면 '/available-times/search' 처럼 POST지만 검색의 의미를 표현하는 키워드를 추가해줘도 좋을 것 같은데,
아직 이 부분은 고민을 해봐야 될 것 같다.
좋은 API가 무엇인지, 어떻게 하면 좋은 API를 설계할 수 있을지 고민해보자!
참고