빠른 설정
POST /api/orders HTTP/1.1
Content-Type: application/json
Accept: application/json
{
"sku": "book-001",
"quantity": 1
}전송 기준
Content-Type은 요청 본문의 형식을 말한다
HTTP 요청에서 Content-Type: application/json은 본문이 JSON text라는 계약이다. 서버는 이 header를 보고 JSON parser를 적용할지 판단한다. 본문은 JSON인데 text/plain이나 잘못된 media type으로 보내면 서버 프레임워크가 body parser를 실행하지 않거나, 원시 문자열로만 처리할 수 있다.
Content-Type: application/json파일 확장자나 URL이 .json인지보다 실제 HTTP header가 중요하다. 특히 API 클라이언트, webhook, form 제출을 섞어 쓰는 환경에서는 Content-Type 불일치가 흔한 원인이다.
Accept는 응답으로 받고 싶은 형식을 말한다
Accept: application/json은 클라이언트가 JSON 응답을 기대한다는 뜻이다. Content-Type은 보내는 본문, Accept는 받고 싶은 응답 형식이므로 서로 다른 방향의 header다. GET 요청처럼 body가 없는 요청에서도 Accept는 의미가 있지만, Content-Type은 보낼 본문이 없으면 보통 필요하지 않다.
application/json에는 별도 charset parameter를 붙이지 않는 편이 단순하다
RFC 8259의 media type 등록은 application/json의 required parameter와 optional parameter를 두지 않는다. 최신 JSON 교환에서는 UTF-8을 기본 기준으로 맞추는 편이 가장 안전하다. application/json; charset=utf-8을 받는 구현도 많지만, JSON 계약의 핵심은 media type과 실제 UTF-8 JSON text가 일치하는지다.
빈 응답과 JSON null은 다르다
HTTP 204처럼 본문이 없는 응답은 JSON 값이 아니다. 반면 null은 유효한 JSON 값이다. 클라이언트에서 모든 응답에 무조건 response.json()을 호출하면 빈 body에서 파싱 오류가 날 수 있다. status code와 Content-Length, Content-Type을 먼저 확인하고, JSON body가 있는 경우에만 parser를 호출하는 편이 안전하다.
체크포인트
| 상황 | 확인할 것 |
|---|---|
| JSON 요청 전송 | Content-Type: application/json |
| JSON 응답 기대 | Accept: application/json |
| 204 또는 빈 body | JSON parser 호출하지 않음 |
| HTML 에러 페이지 수신 | 응답 Content-Type 먼저 확인 |
| webhook 수신 실패 | header와 실제 body가 일치하는지 확인 |
공식 참고: RFC 8259: JSON, RFC 9110: HTTP Semantics
주의할 점
응답 status가 성공이어도 body가 JSON이라는 보장은 없습니다. 인증 실패, 프록시 오류,
서버 예외에서 HTML이 내려오면 JSON parser가 실패합니다. 먼저 Content-Type을 확인하고
그 다음 parser를 호출하는 흐름이 안전합니다.
const response = await fetch("/api/orders");
if (response.status === 204) {
return null;
}
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.includes("application/json")) {
throw new Error("Expected JSON response");
}
return response.json();참고 링크
2 sources