Content-Type
Content-Type은 api 연동시에 보내는 자원을 명시하기 위해 보통 사용합니다.
Content-Type을 깊게 이해하기 위해서는 HTTP의 request의 구조를 이해하는 것이 좋습니다.
HTTP의 Request는 다음과 같이 4개의 파트로 나눌 수 있습니다.
위 그림에서 Message Body에 들어가는 타입을 HTTP Header에 명시해줄 수 있는데 이때 명시해줄 수 있도록 해주는 필드가 바로 Content Type입니다.
예를 들어 api 요청 시 request에 실어 보내는 데이터(body)의 type정보를 표현합니다.
그중에 Text타입으로는 text/css, text/javascript, text/html, text/plain 등이 있습니다.
또 file을 실어보내기 위해서는 multipart/form-data가 있고 Application 타입으로는 application/json, application/x-www-urlencoded가 있습니다.
Content-Type의 형식으로는 위 말고도 여러 가지가 있지만 가장 많이 사용되고 헷갈리는 것 위주로 정리를 해보겠습니다.
application/json과 application/x-www-form-urlencoded
요즈음의 대부분의 request에 대한 Content-Type은 application/json 타입인 것이 많습니다.
application/json은 RestFul API를 사용하게 되며 request를 날릴 때 대부분 json을 많이 사용하게 됨에 따라 자연스럽게 사용이 많이 늘게 되었습니다.
application/x-www-form-urlencoded는 html의 form의 기본 Content-Type으로 요즘은 자주 사용하지 않지만 여전히 사용하는 경우가 종종 존재합니다.
차이점은 application/json은 **{key: value}**의 형태로 전송되지만 application/x-www-form-urlencoded는 key=value&key=value의 형태로 전달된다는 점입니다.
RestAPI의 경우 보통 JSON타입으로 요청하고, 요청을 받는다. 그래서 당연히 application/json 타입으로 Content-Type 사용한다고 생각을 하는 경우가 많은데, 자료를 찾다보니 그렇지 않다는걸 알게되었다.
html form 태그를 사용하여 post 방식으로 요청하거나, jQuery의 ajax 등의 요청을 할때 default Content-Type은 'application/json'이 아니라 'application/x-www-form-urlencoded'다.
아래와 같은 코드로 client에서 server로 요청을 보낸다고 해보자.
Content-Type에 따라서 client에서 server로 보내는 데이터의 형식이 달라진다.
const data = {
"name": "kim",
"age": 29
}
axios({
method: 'post',
url: '<https://localhost:8080>',
headers: {
'Content-Type': ....
},
data: data
}).then((res) => {
// handle success
}).catch((err) => {
// handle error
}).then(() => {
// always
})
- application/x-www-form-urlencoded :
x-www-form-urlencoded의 경우, 아래와 같이 key1=value1&key2=value2 의 형식으로 요청이 가고 있다.
POST / HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
name=kim&age=29
- application/json :
json의 경우, 아래와 같이 json 형태로 요청이 가는걸 볼수 있다.
POST / HTTP/1.1
Host: localhost
Content-Type: application/json
{
"name":"kim",
"age":"29"
}
Spring에서 POST 요청 데이터 받기
보통 Spring으로 개발한 RestAPI에서 Post요청을 받을때는 data가 json 형식으로 body에 들어있다고 가정하고 개발을 하게 된다.
따라서 body에서 json 객체를 꺼내어 알맞은 dto로 받기 위해 아래와 같은 코드를 짜게 된다.
@PostMapping(value = "/add")
public String postHanlder(@RequestBody Person person) {
log.info("person :: {}", person);
return person.toString();
}
이때, client에서 header에 content-type으로 'application/json'을 추가해서 보내지 않으면 위에서 말했듯이 415 error가 발생한다.
클라이언트에서 해결하기 위해서는 'content-type'을 'application/json'으로 하여 보내야 한다.
그럼 백엔드에서 해결하기 위해서는 어떻게 해야할까? 실무는 실전이다.. 항상 클라이언트에서 친절하게 해결해주진 않기 때문에 백엔드에서 해결하는 방법을 알고는 있어야 한다.
백엔드에서 처리하는 방법
@RequestBody나 @ModelAttribute, @RequestParam 등의 어노테이션을 사용하게 되면 해당 어노테이션과 매칭되는 '메시지 컨버터'가 AnnotationMethodHandlerAdapter에 의해 등록되게 된다.
1. @RequestBody Person person
model 객체 앞에 @RequestBody 어노테이션을 붙이면, AnnotationMethodHandlerAdapter에 의해 MappingJacksonHttpMessageConverter가 등록되게 된다. MappingJackson2HttpMessageConverter는 HttpMessageConverter의 구현체로 JSON 형식의 데이터가 들어오면 (당연히 content-type은 application/json) 해당 json 데이터를 jackson 라이브러리를 사용하여 model 객체로 변환해주게 된다.
- Content-Type : application/json 이고 json 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(@RequestBody Person person) {
log.info("person :: {}", person);
return person.toString();
}
2. @RequestBody MulityValueMap<String, String> map
MultiValueMap 과 함께 @RequestBody 어노테이션을 붙이면, AnnotationMethodHandlerAdapter에 의해 FormHttpMessageConverter가 등록되게 된다. FormHttpMessageConverter는 미디어 타입이 application/x-www-form-urlencodede로 정의된 폼 데이터를 주고 받을 때 사용하게 된다.
- Content-Type : application/x-www-form-urlencoded 이고 key1=value1 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(@RequestBody MultiValueMap<String, String> data) {
log.info("data :: {}", data);
return data.toString();
}
물론 이론상으로는 2번 방법이 가능하지만, FormHttpMessageConverter를 더 효율적으로 사용하려면 아래 3번 방법이 더 낫다.
3. @ModelAttribute Person person
model 객체와 함께 @ModelAttribute를 사용하면 2번과 같이 FormHttpMessageConverter가 등록되어 key=value를 model로 converting 하게 된다. 앞에 아무런 어노테이션도 안붙이고 Person person 으로 선언하면 @ModelAttribute가 암묵적으로 사용된다.
- Content-Type : application/x-www-form-urlencoded 이고 key1=value1 타입의 데이터
@PostMapping(value = "/add")
public String postHanlder(Person person) {
log.info("data :: {}", data);
return data.toString();
}
'IT Study > IT 상식' 카테고리의 다른 글
Maven과 Gradle 차이 (2) | 2024.09.07 |
---|---|
Javascript Base64로 인코딩, 변환 (0) | 2021.11.19 |
네이티브앱 vs 모바일웹앱 vs 하이브리드앱 (0) | 2021.11.10 |
인증 관련 - PIN 비밀번호 차이 / CI DI 차이점 (0) | 2021.11.04 |
크롬 vs 파이어폭스 / 크롬 파이어폭스 차이점, 웹킷 게코 차이점 비교(webkit & geko) (0) | 2021.11.03 |