클라이언트에서 서버로 요청할 때, Ajax를 많이 이용한다. 보통은 Json으로만 사용해서 Ajax에는 application/json을 명시하고 서버에서는 dto를 만들어 @RequestBody를 사용했다. 당연하게 사용했던 부분에 대해 "application/json 일 때는 왜 @ResquestBody로만 받아야 할까?", "application/x-www-form-urlencoded 일 경우는 @RequestBody로 받지 못할까" 등 궁금증이 생겨서 흐름을 정리해 보려고 한다.
[개념]
0. HTTP의 구조
application/x-www-form-urlencoded와 application/json의 흐름을 설명하기 위해서는 간략하게 HTTP 구조를 알아야 한다. HTTP는 Request Line(Start Line), Request Headers, Empty Line, Request Message Body로 나눠진다.
1. Ajax 기본 형태
$.ajax({
type : 'POST',
url : '/test.do',
async : true,
dataType : 'json',
contentType: 'application/json',
data : JSON.stringify({
"no" : 142,
"name" : "Kim",
"nick" : "K"
}),
success : function(result) {
// 결과 성공 콜백함수
console.log(result);
},
error : function(request, status, error) {
// 결과 에러 콜백함수
console.log(error)
},
complete: function (){
// 완료 후 콜백함수
}
})
현재 Ajax를 사용하고 있어서, Ajax 기반으로 작성하려고 한다. HTTP 통신을 위해 다룰 옵션은 contentType, data, dataType이다. (POST 요청으로 가정)
1) data
- contentType이 application/json일 때, 당연히 파라미터 값들도 JSON 형식이어야 한다.
- 위 예제처럼, JSON 객체의 stringify() 메서드를 사용하면 JavaScript 객체를 JSON 문자열로 변환이 되어 보내지는 것을 확인할 수 있다. ( 예시 - JSON.stringify(param))
2) dataType
- Ajax를 통해 리턴 받을 데이터의 타입을 성정하는 옵션이다.
- 설정하지 않으면, default로 MIME 타입을 참고하여 자동 파싱된다.
- 종류 : xml, html, script, json, text
3) contentType
- contentType은 HTTP Header의 일부로, 전송되는 데이터의 유형을 지정하는 데 사용된다. 클라이언트와 서버 간의 어떤 유형의 데이터가 전송되고 있는지 알려주고, 수신하는 쪽에서 그에 맞게 데이터를 올바르게 처리할 수 있다.
- Ajax에서는 contentType 옵션에 넣어준 값이 HTTP Header로 보내지게 된다. 설정을 하지 않으면, default로 " application/x-www-form-urlencoded"가 설정된다.
※ application/x-www-form-urlencoded과 application/json
자주 사용하는 Content Type인 application/x-www-form-urlencoded와 application/json에 대해 얘기해보려고 한다.
[application/x-www-form-urlencoded]
HTTP 통신에서 주로 사용되는 Content-Type 중 하나로, HTML의 form 데이터를 URL 인코딩하여 전송할 때 사용된다. 이 형식은 특히 Web form을 통해 데이터를 서버에 전송할 때 많이 사용된다.
URL 인코딩이 되면 아래와 같이 key와 value는 = 기호로 연결되고, 여러 쌍은 & 기호로 구분된다. 그리고 공백은 + 기호로 인코딩 된다.
//key=value&key=value의 형태
name=John&age=30&city=New+York
서버에서는 @RequestParam 어노테이션을 활용해서 데이터를 받을 수 있다.
@RequestMapping(value = "/test.do", method= RequestMethod.POST)
public String testIndex2(@RequestParam Map<String, Object> map) {
System.out.println("map2 = " + map);
return "testIndex2";
}
[application/json]
요즘은 JSON 형식의 데이터를 많이 사용하는 걸 볼 수 있다. {key:value, key:value } 형식으로 Message Body에 담겨 서버로 전달된다. JSON 형식은 객체(Object), 배열(Array), 문자열(String), 숫자(Number), 불리언(boolean), null 등 다양한 데이터 타입을 포함한다.
//1. 객체(Object)
{
"name" : "John",
"age" : 30,
"city" : "New York"
}
//2. 배열(Array)
[
"apple",
"banana",
"grape"
]
서버에서는 @RequestBody 어노테이션을 활용해 JSON Object, Array 데이터를 받을 수 있다.
//application/json 으로 받을 때
//JSON Object로 받을 때
@RequestMapping(value = "/test.do", method= RequestMethod.POST)
public String testIndex(@RequestBody Map<String, Object> map) {
System.out.println("map1 = " + map);
return "testIndex";
}
//JSON Array로 받을 때
@RequestMapping(value = "/test.do", method= RequestMethod.POST)
public String testIndex(@RequestBody List<Object> list) {
System.out.println("list1 = " + list);
return "testIndex";
}
[질문]
Q. @RequestBody를 이용해서 application/x-www-form-urlencoded 데이터를 받을 수 없을까?
MultiValueMap 클래스를 이용해 받는 방법이 있다. 하지만 원하는 형태로 받아지지 않아서 @RequestParam이나 @ModelAttribute를 사용하면 원하는 형식으로 받을 수 있다.
@PostMapping(value = "/test.do")
public String testIndex(@RequestBody MultiValueMap<String, Object> map) {
System.out.println("map = " + map);
return "testIndex";
}
//결과
// map = {no=[142], name=[Kim], nick=[K]}
@RequestBody에서 message convert를 이용해 인스턴스로 변환시켜 주는데, ContentType이 application/x-www-form-urlencoded인 요청을 Map클래스나 VO 타입의 인스턴스로 변환해 주는 Message converter가 없기 때문이다.
FormHttpMessageConverter가 application/x-www-form-urlencoded인 요청을 MultiValuedMap타입의 인스턴스로 변환해 주는 역할을 한다. (기본으로 내장)
[Tip - 1] Ajax에서 JSON으로 서버와 통신하기 위해서 확인해야 할 옵션 3가지
1) contentType : 'application/json'
2-1) JSON 객체일 경우
data : JSON.stringify({
"user_id" : 1.
"name" : "testUser"
});
2-1) JSON Array일 경우
data : JSON.stringify(["apple", "banana", "grape"]);
3) type : "POST"
[Tip - 2] 서버에서 Content Type 별로 받을 수 있는 코드
@RestController
public class TestController {
//Content Type별 메소드
//application/json 으로 받을 때
@RequestMapping(value = "/test.do", method= RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public String testIndex(@RequestBody Map<String, Object> map) {
System.out.println("map1 = " + map);
return "testIndex";
}
//application/x-www-form-urlencoded (form)으로 받을 때
@RequestMapping(value = "/test.do", method= RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String testIndex2(@RequestParam Map<String, Object> map) {
System.out.println("map2 = " + map);
return "testIndex2";
}
}
[참고]
Ajax와 @RequestBody, @ResponseBody
너무 단순한 주제지만 프로젝트를 분석하다보면 잘못 사용된 경우도 있고 나도 제대로 알지 못하고 사용했던 부분이 있어 정리해놓는다. 우선 아래와 같은 Controller 메소드가 있다고 가정해보자
findmypiece.tistory.com
[Spring] Post 요청과 Content-Type의 관계
실무에서 RestAPI를 만들면서 간혹 마주치는 이슈가 있었다. Client에서 POST 방식으로 요청을 보...
blog.naver.com
'개발 > 개발노트' 카테고리의 다른 글
[Java] 제네릭 메서드 활용 예제 (0) | 2024.06.04 |
---|---|
[DB 형상관리] SpringBoot에 Flyway 도입 (0) | 2024.04.01 |
[Network] 나는 지금 RESTful API를 만들고 있을까? (1) | 2024.02.01 |
[JPA] DTO와 Entity를 분리하는 이유 (1) | 2024.01.26 |
[Linux] AArch64와 x64의 차이점 (0) | 2024.01.11 |