14. 지식 공유 - ajax
포스트
취소

14. 지식 공유 - ajax

    index

  1. 서론
  2. 기획 및 명세
  3. 패키지 트리
  4. 프로젝트 환경
  5. 메시지와 국제화
  6. 예외 다루기
  7. 검증
  8. 계정 관련
  9. 권한 인터셉터
  10. 도서 관련
  11. 대여 관련
  12. 오프라인 관련
  13. about ajax
  14. 지식 공유 - ajax
  15. DTO, Form, VO, Entity
  16. 후기




 ajax 로 hello world 는 할 줄 알아야지

처음 ajax 를 통해 서버와 통신하며 겪은 시행착오에 대한 내용이다.
팀원들에게도 알려주려고 여러 경우의 수를 추가하여 작성했다.



@RequestBody

아무 것도 모를 때


일단 편하게, 생각나는대로 해보자.

json 객체를 선언하고,
key 에는 쌍따옴표를 생략해도 된다고 했고,
post 방식. 자주 사용하던 것부터 해봐야지.
json 객체를 그대로 보내고,
@RequestBody 로 받으면 되겠지?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : data
});
</script>

@PostMapping("uri")
public void test(@RequestBody String code){
	System.out.println(code) 
	출력 ==> "code=text" // 원하는 결과가 아님
}


기본 컨텐츠 타입인 “application/x-www-form-urlencoded”
JSON 객체를 “code=text” 로 변형시켰고,
payload 를 그대로 읽는 @RequestBody 는 String 타입의 변수에
payload 의 값을 그대로 전달했다.



Content-type 이 문제인가?


요즘 데이터는 json 으로 주고 받는게 대세라고 했는데,
컨텐츠 타입을 지정해주지 않아서 이런 결과가 나왔겠지?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : data,
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public void test(@RequestBody String code){
	System.out.println(code) 
	출력 ==> "code=text" // 원하는 결과가 아님
}

1
2
	RequestResponseBodyMethodProcessor 
	- Read "application/json;charset=UTF-8" to ["code=text"]


로그엔 이렇게 나타난다.
여기에 대해선 나도 의문이 있다.
json 타입인걸 명시했는데 왜 기본 타입과 동일한 결과 나타나는 것일까?

많은 생각이 드는데,
JSON 객체를 전송한 거지, JSON 형태의 문자열을 보낸 게 아니다.
“application/json” 은 payload 의 정보를 제공한 건 맞지만,
payload 의 변형을 불러일으킨 건 아니다.

컨텐츠 타입과 상관없이 JSON 객체는 HTTP 프로토콜로 데이터가 옮겨질 때,
이미 “code=text” 형태로 지정된 것 같다.

이 구조에서 컨텐츠 타입은 서버에서
어떤 메시지 컨버터를 사용하여 요청 데이터를 해석할 지에 대한 힌트인 것 같다.



객체가 아닌 문자를 보내야되나?


stringify 라는 메소드를 본 것 같은데,,,,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : JSON.stringify(data),
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public void test(@RequestBody String code){
	System.out.println(code) 
	출력 ==> {"code":"abc"} // 원하는 결과가 절대 아님
}


여기까진 전부 String 타입에 @RequestBody 를 그대로 넣고 있다.
“data : “ 에 JSON 객체를 보내냐, JSON 형태의 문자열을 보내냐에 따라
등호와 콜론이 스위치 되고 있다.



문자열을 보냈으니 컨텐츠 타입을 제거?


JSON 형태의 문자열을 보낸 거라 JSON 객체가 아니라서 그럴까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : JSON.springify(data)
});
</script>

@PostMapping("uri")
public void test(@RequestBody String code){
	System.out.println(code) 
	출력 ==> "%7B%22code%22%3A%22text%22%7D="
	// 원하는 결과가 절대 아님
}

이 결과는 뭔가 여태까지의 실험을 다 부정당하는 느낌이다.
이건 진짜 URL 인코딩이다.
내 수준이 아직 바닥임을 알 수 있다.
어디서 이 인코딩이 적용된 건지,
위의 내용들은 어쩄든 모두 json 형태로 해석된 건지.
당장 알 수가 없다.


계속 문자가 순수하게 출력되네? 그러면 text?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : "text",
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public void test(@RequestBody String code){
	System.out.println(code)
	출력 ==> text // 원하는 결과
}


이건 사실 생각보다 특수한 상황이다.
@RequestBody 와 String 타입 매개변수가 하나만 존재하는.
그냥 문자열을 그대로 받을 수 있다.



쓸모없어보이는데? 이걸 어디쓰지?


테스트는 간단하게 하자는 나쁜 습관 때문에,
문자열만을 주고 받는 심플한 상황을 연출했지만
데이터를 주고 받을 때 json 형식의 문자열을 주고받는 데 익숙해져야 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Setter @Getter
	static class Data {
		String code;
	}

(컨트롤러 클래스 내부에 정적 멤버 클래스를 선언했다)

<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : "text",
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public Data testData(@RequestBody Data data) 


HttpMessageNotReadableException: JSON parse error:

예외가 터진다.
String 일 때는 key 여부 상관 없이 들어갔지만,
객체의 필드에 바인딩 시키기 위해선 JSON 형식을 넘겨야 된다.



객체에는 객체를 넘겨야 정상이지


자 그럼, 객체를 주고 받고, json 타입을 명시해준다면 되지 않을까?

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : data,
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public Data testData(@RequestBody Data data) 

HttpMessageNotReadableException: JSON parse error…..

예외가 터진다.
여기까지 봤다면 JSON 객체가 payload 에 실릴 때
어떤 형식으로 변환되는 것을 알 수 있다.
자바에서 배웠던 ObjectStream 이 생각난다.
헤더같은 게 덧붙어서 이런 결과가 일어나는 것 같다.


JSON 객체를 문자열로 변환하면?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : JSON.stringify(data),
	contentType : "application/json",
});
</script>

@PostMapping("uri")
public Data testMulti(@RequestBody Data data) {
  System.out.println(data.getCode()) 
  출력 ==> text // 완전 원하던 결과
}

정리

  1. RequestBody 는 순수한 텍스트를 읽어준다.
    하지만 다양한 메시지 컨버터들에 치여 예상치 못한 결과가 나타나기도 한다.
    텍스트 자체를 전달하고 싶다면, 객체를 보낼 게 아니라 값만 전달해야 된다.

  2. 객체를 전달하고 싶다면 클라이언트 측에서 stringify 메소드를 사용해야 된다.
    RequestBody 의 역할은 JSON 타입 자체의 해석이 아닌, JSON 형태의 문자열을 해석해주고 Jackson 라이브러리의 힘을 빌려 필드에 바인딩 해주는 것.

  3. json 형태의 문자열은 일반 문자열과 구분할 수 없다.
    클라이언트 측에서 메시지 바디에 들어갈 문자열이 json 타입임을 선언해줘야하는데,
    이 바디에 대한 정보를 알려주는 HTTP 프로토콜의 영역이 Header 이다.
    Header 의 내용 중 하나가 Content-type 인데
    Content-type 은 HTTP 바디가 어떤 MIME 타입임을 알려준다.
    그래서 Content-type 이 “application/json” 인 문자열
    @RequestBody 와 jackson-databind 가 해석 후 인자에 바인딩한다.
    (참고로 http message body 는 payload 라고도 불리는 걸로 알고 있다.)




@RequestParam

ajax는 ajax인데,, ajax는 json 통신 아닌가?

(@RequestParam 의 경우 생략 가능한 어노테이션이다.)

컨텐츠 타입을 명시하지 않았을 때

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ContentType 명시하지 않을  기본  : application/x-www-form-urlencoded

<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : data,
});
</script>

@PostMapping("uri")
public void test(Data data){
	System.out.println(data.getDate()) 
	출력 ==> text // 바로 성공
}

@RequestBody 에서 컨텐츠 타입을 명시하지 않고 JSON 객체를 보냈을 때를 생각해보면
“code=text” 라고 나왔다.
근데 이건 사실 @RequestParam 이 아닌 @ModelAttribute 다.


컨텐츠 타입을 json으로 지정했을 때


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	contentType: "application/json",
	data : data,
});
</script>

@PostMapping("uri")
public void test(Data data){
	System.out.println(data.getCode()) ==> null
}


컨텐츠 타입이 json 인데 @RequestBody 를 사용하지 않았다.
게다가 stringify 함수를 사용하지도 않았다.
바인딩 실패.



순수 text를 전달했을 때


아까처럼 String 타입에 urlEncoded 를 사용하여 text 자체만 보내보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$.ajax({
	type : "post",
	url : "${path}/uri",
	data : "text",
});
</script>

@PostMapping("uri")
public void requestParam(String code){
	System.out.println(code) 
	출력 ==> null // 어림도 없다.
}


드디어 @RequestParam 이 적용되는 부분이다.
하지만 ReqeustParam 은 Key=value 형태로 넘겨야 받을 수 있다.



다시 JSON 객체를 넘긴다면?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
let data = { code : "text"};

$.ajax({
	type : "post",
	url : "${path}/uri",
	data : data,
});
</script>

@PostMapping("uri")
public void requestParam(String code){
	System.out.println(code) 
	출력 ==> text
}


간단 요약

json 형태의 문자열을 보내고 싶다면
@RequestBody 를 이용하라

JSON 객체를 보내고 싶다면
@RequestBody 를 우선 생략하라


JQuery.ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$.ajax({
    url: '${path}/plz', 
    // HTTP Method GET, POST, PUT, PATCH, DELETE
    type: 'put', 
    // json 객체 let code = { "code" : "code" }
	// 보내고 싶은 데이터
    data: code, 
    // 요청 데이터의 컨텐츠 타입을 지정
    // Get 요청의 경우 메시지 바디가 아닌 쿼리 파라미터로 전송
    // @RequestBody 가 해석할 수 없음.
    contentType: "application/json", 
	// 응답 데이터의 컨텐츠 타입을 지정
	// Accept를 application/json 으로 변경
	// json 일 경우, 서버에서 @ResponseBody 를 사용
    dataType: 'json', 
    success: function(result){
        $('.bookName').val(result.content);
    }
	// 상태코드가 4xx, 5xx 일 때 실행됨
    error: function(error){
        console.log(error);
})

$.ajax() 는 반환값이 있다.
바로 jqXHR 이라는 객체인데,
$.ajax().done, .fail, .always 등의 메소드를 사용할수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.