Tech Blog
Tech Blog
DocsHTTP GET/DELETE 요청에 Body를 넣으면 안 되는 이유
GitHub
Web40분

HTTP GET/DELETE 요청에 Body를 넣으면 안 되는 이유

RFC 스펙, 클라이언트/서버 호환성 문제, 그리고 올바른 대안

2025년 1월 26일
httprest-apibest-practices

API 스펙을 전달받았는데, GET 요청에 필터 조건을 request body로 보내달라는 내용이었습니다.

GET /api/products
Content-Type: application/json

{
  "category": "electronics",
  "priceRange": { "min": 10000, "max": 50000 },
  "tags": ["sale", "new"]
}

"Query string이 복잡해지니까 body로 받는 게 깔끔하지 않나요?"라는 이유였습니다. 하지만 GET 요청에 body를 담는 건 표준적인 방식이 아닙니다. Axios는 GET body를 지원하지 않고, 브라우저의 Fetch API도 body를 무시합니다.

이 문서는 왜 그런지 조사한 내용을 정리한 것입니다. GET/DELETE 요청에 body를 넣는 것이 왜 문제인지, RFC 스펙은 무엇을 말하는지, 그리고 어떤 대안이 있는지 다룹니다.

RFC 스펙이 말하는 것

RFC 7231 (HTTP/1.1, 2014)

GET 요청:

"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."

DELETE 요청:

"A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request."

RFC 9110 (HTTP Semantics, 2022 - 최신)

최신 HTTP 스펙인 RFC 9110은 더 강한 경고를 추가했습니다:

"A client SHOULD NOT generate content in a GET request unless it is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported."

핵심 포인트

주의

"no defined semantics"의 의미 - 금지된 것은 아님 (NOT forbidden) - 하지만 의미가 정의되지 않음 (undefined behavior) - 서버가 무시해도 됨 - 서버가 거부해도 됨 - 어떤 일이 일어날지 보장 없음

참고로 유일하게 body가 명시적으로 금지된 메서드는 TRACE입니다:

"A client MUST NOT send a message body in a TRACE request."

실제로 발생하는 문제들

JavaScript
// ❌ 많은 환경에서 body가 전송되지 않음
fetch('/api/users', {
  method: 'GET',
  body: JSON.stringify({ filter: 'active' })  // 무시될 수 있음
});

클라이언트별 동작

Fetch API (브라우저)

JavaScript
// GET + body
fetch("/api/data", {
  method: "GET",
  body: JSON.stringify({ query: "test" }),
});
// ⚠️ Chrome, Firefox, Safari 모두 body 무시됨 (전송되지 않음)
 
// DELETE + body
fetch("/api/items/1", {
  method: "DELETE",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ reason: "duplicate" }),
});
// ✅ 대부분 브라우저에서 전송됨 (하지만 서버에서 문제 가능)

Axios

JavaScript
// ❌ GET + body - 지원하지 않음
axios.get("/api/data", {
  data: { query: "test" }, // 무시됨
});
 
// ⚠️ DELETE + body - 특수 문법 필요
axios.delete("/api/items/1", { reason: "duplicate" }); // ❌ 작동 안함
 
// 올바른 방식 (작동할 수 있음)
axios.delete("/api/items/1", {
  data: { reason: "duplicate" }, // data 속성 사용
});
 
// 또는 axios() 직접 호출
axios({
  method: "delete",
  url: "/api/items/1",
  data: { reason: "duplicate" },
});

정보

Axios 공식 문서:

"data is the data to be sent as the request body. Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH'"

GET은 명시적으로 제외됩니다.

클라이언트 호환성 요약

클라이언트GET + bodyDELETE + body
Fetch (Chrome)❌ 무시됨⚠️ 전송됨
Fetch (Firefox)❌ 무시됨⚠️ 전송됨
Fetch (Safari)❌ 무시됨⚠️ 전송됨
Axios❌ 지원 안함⚠️ data 속성 필요
XMLHttpRequest⚠️ 브라우저마다 다름⚠️ 전송됨
cURL⚠️ 전송됨⚠️ 전송됨
Postman⚠️ 전송됨⚠️ 전송됨

서버/프레임워크별 동작

Node.js (Express)

JavaScript
const express = require("express");
const app = express();
 
app.use(express.json()); // body-parser
 
app.get("/api/data", (req, res) => {
  console.log(req.body); // {} 또는 undefined
  // GET 요청의 body는 파싱되지 않을 수 있음
});
 
app.delete("/api/items/:id", (req, res) => {
  console.log(req.body); // 대부분 작동하지만 보장 없음
});

Spring Boot (Java)

JAVA
// GET + body - 기본적으로 지원하지 않음
@GetMapping("/api/data")
public Response getData(@RequestBody FilterDto filter) {
    // ⚠️ 작동할 수 있지만 권장하지 않음
}
 
// DELETE + body
@DeleteMapping("/api/items/{id}")
public Response deleteItem(
    @PathVariable Long id,
    @RequestBody DeleteOptionsDto options  // ⚠️ 권장하지 않음
) {
    // ...
}

서버 호환성 요약

환경GET body 파싱DELETE body 파싱
Express.js⚠️ 기본 미지원✅ 대부분 작동
Spring Boot⚠️ 설정 필요✅ 대부분 작동
Django❌ 기본 미지원⚠️ DRF 설정 필요
ASP.NET Core⚠️ 설정 필요✅ 대부분 작동
nginx (프록시)⚠️ 제거될 수 있음⚠️ 제거될 수 있음
AWS API Gateway❌ 거부⚠️ 설정에 따라 다름

올바른 대안

GET 요청에 필터/검색 조건 전달

JavaScript
// ❌ Body 사용 (안티패턴)
fetch('/api/users', {
  method: 'GET',
  body: JSON.stringify({
    filter: 'active',
    sort: 'name',
    page: 1
  })
});

DELETE 요청에 추가 정보 전달

JavaScript
// ✅ Query Parameters
fetch('/api/items/123?reason=duplicate&force=true', {
  method: 'DELETE'
});

대량 삭제 (Bulk Delete)

JavaScript
// ❌ DELETE + body로 ID 목록 전달 (안티패턴)
fetch("/api/items", {
  method: "DELETE",
  body: JSON.stringify({ ids: [1, 2, 3, 4, 5] }),
});
 
// ✅ 방법 1: Query Parameters (소량)
fetch("/api/items?ids=1,2,3,4,5", { method: "DELETE" });
 
// ✅ 방법 2: POST 사용 (대량)
fetch("/api/items/bulk-delete", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ ids: [1, 2, 3, 4, 5] }),
});

대안 비교

방법장점단점적합한 경우
Query Params캐시 가능, 호환성 최고URL 길이 제한간단한 필터, 소량 ID
Custom Headers깔끔한 URL비표준, 디버깅 어려움메타데이터 전달
POST 변경Body 사용 가능, 복잡한 데이터REST 의미론 훼손복잡한 작업, 대량 처리
별도 리소스REST 원칙 준수, 확장성복잡한 API 설계비동기 작업, 감사 로그

정리

GET/DELETE + Body = 지뢰밭

  • RFC 스펙: "no defined semantics" = 무슨 일이 일어날지 모름 - 브라우저: GET body는 대부분 무시됨 - Axios: GET body 지원 안함, DELETE는 특수 문법 필요 - 프록시/CDN: body를 제거하거나 캐시 문제 발생 가능 - 서버: 파싱 안 할 수도, 거부할 수도 있음 - API 도구: 경고/에러 발생 (Swagger, OpenAPI)

권장 방법

상황권장 방법
GET + 필터/검색Query Parameters
DELETE + 단순 옵션Query Parameters 또는 Headers
DELETE + 복잡한 데이터POST로 변경
대량 삭제POST /bulk-delete 엔드포인트

참고 자료

  • RFC 9110 - HTTP Semantics (2022)
  • RFC 7231 - HTTP/1.1 Semantics and Content
  • MDN - DELETE Method
  • Axios DELETE Issue #897
목차
  • RFC 스펙이 말하는 것
    • RFC 7231 (HTTP/1.1, 2014)
    • RFC 9110 (HTTP Semantics, 2022 - 최신)
    • 핵심 포인트
  • 실제로 발생하는 문제들
  • 클라이언트별 동작
    • Fetch API (브라우저)
    • Axios
    • 클라이언트 호환성 요약
  • 서버/프레임워크별 동작
    • Node.js (Express)
    • Spring Boot (Java)
    • 서버 호환성 요약
  • 올바른 대안
    • GET 요청에 필터/검색 조건 전달
    • DELETE 요청에 추가 정보 전달
    • 대량 삭제 (Bulk Delete)
    • 대안 비교
  • 정리
    • 권장 방법
  • 참고 자료