๋ณธ ๊ธ€์€ https://github.com/kdevkr/spring-demo-ajax ์—์„œ ์ œ๊ณตํ–ˆ๋˜ ์ •๋ณด์ž…๋‹ˆ๋‹ค.

๋“ค์–ด๊ฐ€๋ฉฐ

์›น ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋น„๋™๊ธฐ ํ†ต์‹  ๊ธฐ์ˆ ์ธ AJAX๋ฅผ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์—ฐ๊ณ„ํ•˜์—ฌ ํ™œ์šฉํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

XHR(XMLHttpRequest)๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ด์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” JQuery์—์„œ ์ง€์›ํ•˜๋Š” AJAX(Asynchronos Javascript And XML)๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๋‚˜์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค.

JQuery ์ด์™ธ์—๋„ ajax๋ฅผ ์ง€์›ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Prototypejs๋„ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์œ ํ‹ธ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ €์™€ ๊ฐ™์€ ์ดˆ๋ณด์ž ๋ฐ ์‹ ์ž… ๊ฐœ๋ฐœ์ž๋“ค์€ JQuery์— ์ต์ˆ™ํ•˜๋ฏ€๋กœ JQuery๊ฐ€ ์ง€์›ํ•˜๋Š” Ajax ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Vue.js์—์„œ๋Š” axios๋ฅผ ์„ ํ˜ธํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

JQuery.ajax

์ œ์ด์ฟผ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ด์™ธ์—๋„ ์ƒ๋žต๋œ ๋‹ค์–‘ํ•œ ํ”„๋กœํผํ‹ฐ๋“ค์ด ์กด์žฌํ•˜๋ฏ€๋กœ ๋” ์ฐพ์•„๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ํ˜•ํƒœ๋Š” ์•„๋งˆ๋„ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋กœํผํ‹ฐ๋งŒ ๋ชจ์•„๋†“์€ ๋ถ€๋ถ„์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$.ajax({
  type	: "GET", //์š”์ฒญ ๋ฉ”์†Œ๋“œ ํƒ€์ž…
  url		: "url", //์š”์ฒญ ๊ฒฝ๋กœ
  async : true, //๋น„๋™๊ธฐ ์—ฌ๋ถ€
  data  : {key : value}, //์š”์ฒญ ์‹œ ํฌํ•จ๋˜์–ด์งˆ ๋ฐ์ดํ„ฐ
  processData : true, //๋ฐ์ดํ„ฐ๋ฅผ ์ปจํ…ํŠธ ํƒ€์ž…์— ๋งž๊ฒŒ ๋ณ€ํ™˜ ์—ฌ๋ถ€
  cache : true, //์บ์‹œ ์—ฌ๋ถ€
  contentType : "application/json", //์š”์ฒญ ์ปจํ…ํŠธ ํƒ€์ž… "application/x-www-form-urlencoded; charset=UTF-8"
  dataType	: "json", //์‘๋‹ต ๋ฐ์ดํ„ฐ ํ˜•์‹ ๋ช…์‹œํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์ถ”์ธก
  beforeSend  : function(){
    // XHR Header๋ฅผ ํฌํ•จํ•ด์„œ HTTP Request๋ฅผ ํ•˜๊ธฐ์ „์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
  },
  success	: function(data, status, xhr){
    // ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ต ๋ฐ›์•˜์„ ๊ฒฝ์šฐ์—๋Š” success ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    // ์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ๋Š” ์‘๋‹ต ๋ฐ”๋””, ์‘๋‹ต ์ฝ”๋“œ ๊ทธ๋ฆฌ๊ณ  XHR ํ—ค๋”๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  },
  error	: function(xhr, status, error){
  	// ์‘๋‹ต์„ ๋ฐ›์ง€ ๋ชปํ•˜์˜€๋‹ค๊ฑฐ๋‚˜ ์ •์ƒ์ ์ธ ์‘๋‹ต์ด์ง€๋งŒ ๋ฐ์ดํ„ฐ ํ˜•์‹์„ ํ™•์ธํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— error ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  	// ์˜ˆ๋ฅผ ๋“ค์–ด, dataType์„ ์ง€์ •ํ•ด์„œ ์‘๋‹ต ๋ฐ›์„ ๋ฐ์ดํ„ฐ ํ˜•์‹์„ ์ง€์ •ํ•˜์˜€์ง€๋งŒ, ์„œ๋ฒ„์—์„œ๋Š” ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜๋ฉด  error ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  },
  complete : function(xhr, status){
    // success์™€ error ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋œ ํ›„์— ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
    // try - catch - finally์˜ finally ๊ตฌ๋ฌธ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
  }
});

์—ฌ๊ธฐ์„œ ์ž ๊น! GET๊ณผ POST์˜ ์ฐจ์ด๋Š” ๋ฌด์—‡์ธ์ง€ ์•„์‹œ๋‚˜์š”?

๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋””์— ์œ„์น˜ํ•˜๋Š”๊ฐ€์— ์žˆ์Šต๋‹ˆ๋‹ค. POST ์š”์ฒญ์‹œ์— URL์— ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์š”์ฒญ ๋ฐ”๋””์— ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— GET๊ณผ POST์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ URL์— ์ถ”๊ฐ€ํ•ด์•ผํ• ์ง€ ์š”์ฒญ ๋ฐ”๋””์— ์ถ”๊ฐ€ํ•ด์•ผํ• ์ง€๋ฅผ ์•Œ๊ณ  ์žˆ์–ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, processData๋ผ๋Š” ์†์„ฑ์€ GET ์š”์ฒญ์ธ๋ฐ data์— ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์ง€์ •๋  ๊ฒฝ์šฐ์— ์š”์ฒญํ•˜๊ธฐ์ „์— ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹์œผ๋กœ URL์— ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด์™ธ์—๋„ JQuery์—์„œ๋Š” ๋‹ค์–‘ํ•œ ajax ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์ ์šฉํ•ด์„œ HTTP ํ†ต์‹ ์‹œ์— CSRF ํ† ํฐ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด XHR Header์— CSRF ํ† ํฐ์„ ์ถ”๊ฐ€ํ•ด์„œ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ํƒœ๊ทธ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋ฉ”ํƒ€ ํƒœ๊ทธ์— ํ† ํฐ ์ •๋ณด๋ฅผ ์ ์šฉํ–ˆ๋‹ค๋Š” ๊ฐ€์ •์ž…๋‹ˆ๋‹ค.
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$.ajaxSetup({
   beforeSend: function(xhr) {
    xhr.setRequestHeader(header, token);
   }
});

JQuery.ajax์— ๋Œ€ํ•ด์„œ๋Š” ์ด ์ •๋„๊นŒ์ง€๋งŒ ์•Œ์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

Spring Controller

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ajax ํ†ต์‹ ์„ ์œ„ํ•ด์„œ ์Šคํ”„๋ง @MVC๋กœ ๋‹ค์–‘ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ํ™•์ธํ•˜๋ฉด์„œ ๊ตฌ์กฐ๋ฅผ ์ตํ˜€๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ทฐ ๋ฆฌ์กธ๋ฒ„๋ฅผ ํ†ตํ•ด์„œ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ HTTP ์š”์ฒญ์˜ ๊ฒฝ์šฐ์—๋Š” JstlView๋กœ์จ ์‘๋‹ต์„ ํ•˜๊ฒŒ ๋˜์ง€๋งŒ, XHR ์š”์ฒญ์— ์˜ํ•ด์„œ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ผ๋Š” ๊ฒƒ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Message Converter List
- StringHttpMessageConverter
- FormHttpMessageConverter
- ByteArrayMessageConverter
- MarshallingHttpMessageConverter
- MappingJacksonHttpMessageConverter
- MappingJackson2HttpMessageConverter
- SourceHttpMessageConverter
- BufferedImagedHttpMessageConverter

์Šคํ”„๋ง์˜์€ ๋งŽ์€ ์—ญํ• ์„ ํ•ด์ฃผ๋Š”๋ฐ, ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋””ํดํŠธ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•ด์ค๋‹ˆ๋‹ค. ๋‹ค๋งŒ, MappingJacksonHttpMessageConverter๋Š” jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ• ๋•Œ๋งŒ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ AJAX๋ฅผ ์ด์šฉํ•  ๋•Œ ๋ฐ์ดํ„ฐ ํ˜•์‹์„ JSON์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, List, Map๋“ฑ๊ณผ ๊ฐ™์€ ์˜ค๋ธŒ์ ํŠธ๋“ค์„ JSON ํ˜•ํƒœ๋กœ ์‘๋‹ตํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ModelAndView๋ฅผ ์ด์šฉํ•˜๊ฑฐ๋‚˜ ๋ฉ”์‹œ์ง€์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋ณธ ๋ฌธ์„œ์—์„œ๋Š” ModelAndView๋กœ์จ ์‘๋‹ตํ•˜๋Š” ๋ฐฉ์‹์€ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ํ™•์ธํ•˜๋„๋ก ํ•ฉ์‹œ๋‹ค

๊ทธ๋Ÿฌ๋‚˜ ์Šคํ”„๋ง 3 ์ด์ƒ ๋ถ€ํ„ฐ๋Š” jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•  ๊ฒฝ์šฐ์— ์ž๋™์ ์œผ๋กœ MappingJacksonHttpMessageConverter๋ฅผ ์ ์šฉํ•ด์ค๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ž ๊น!
์Šคํ”„๋ง 3.1.2 ๋ถ€ํ„ฐ๋Š” jackson 2.0์„ ์ง€์›ํ•˜๋„๋ก ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. jackson 2.0์€ MappingJackson2HttpMessageConverter๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.
์ž์„ธํ•œ ์‚ฌํ•ญ์€ ์Šคํ”„๋ง ๋ฒ„์ „๋ณ„ jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „ ํ•ญ๋ชฉ์—์„œ ํ™•์ธํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ถ€๋ถ„๋„ ๊ด€๋ฆฌํ•ด์ฃผ๋ฏ€๋กœ ์ถ”๊ฐ€์ ์œผ๋กœ jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ pom.xml์— ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ฒ„์ „๋ณ„ ์‚ฌ์šฉ๋˜๋Š” Jackson Library

์Šคํ”„๋ง 3.0 ์ด์ƒ๋ถ€ํ„ฐ๋Š” jackson ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋”ฐ๋ผ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•ด์ค๋‹ˆ๋‹ค.

Spring 3.0.x

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>${org.codehaus.jackson}</version>
</dependency>

Spring 3.1.2

MappingJackson2HttpMessageConverter๋กœ jackson 2.0์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>${org.codehaus.jackson}</version>
</dependency>

<!-- Jackson 2.0 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

Spring 4.0.0

GsonHttpMessageConverter ์ง€์›
MappingJacksonHttpMessageConverter ๋ฏธ์ง€์›

<!-- Jackson 2.0 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${com.fasterxml.jackson}</version>
</dependency>

<!-- gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.3.1</version>
</dependency>

https://github.com/spring-projects/spring-framework/wiki/Migrating-to-Spring-Framework-4.x#jackson-1819
์œ„ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ์Šคํ”„๋ง 4.1 ๋ถ€ํ„ฐ๋Š” org.codehaus.jackson(1.8 or 1.9)์˜ ์ง€์›์„ ์ค‘๋‹จํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • ๋ฒ„์ „๋ณ„ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ ์ง€์›ํ‘œ
Spring Versionorg.codehaus.jackson(1.8 or 1.9)com.fasterxml.jackson(2.0)gson
3.0.xMappingJacksonHttpMessageConverter
3.1.2MappingJacksonHttpMessageConverterMappingJackson2HttpMessageConverter
4.+MappingJackson2HttpMessageConverterGsonHttpMessageConverter

Annotations

๊ทธ๋Ÿผ ์ด์ œ ์Šคํ”„๋ง ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜๋“ค์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@RequestMapping

@RequestMapping์—๋Š” ์š”์ฒญ๊ณผ ์‘๋‹ต๊ณผ ๊ด€๋ จํ•œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. produces์™€ consumes๋Š” ํ™•์‹คํžˆ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • method=RequestMethod.GET
    method๋Š” ์–ด๋– ํ•œ ์š”์ฒญ ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ๊ฐ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
  • produces=MediaType.APPLICATION_JSON_VALUE
    produces๋Š” ์–ด๋– ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•  ๊ฒƒ์ธ๊ฐ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
  • consumes=MediaType.APPLICATION_JSON_VALUE
    consumes๋Š” ์–ด๋– ํ•œ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ธ๊ฐ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

produces์™€ consumes ํ”„๋กœํผํ‹ฐ๋Š” Spring 3.1์—์„œ ๋ถ€ํ„ฐ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

@ModelAttribute, @ReqeustParam

์ด ๋‘๊ฐœ์˜ ์–ด๋…ธํ…Œ์ด์…˜์€ GET๊ณผ DELETE ์š”์ฒญ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ํ™•์ธํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. @RequestParam์€ request.getParameter()๋กœ์จ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ˜๋ฉด์— @ModelAttribute๋Š” ์ž๋ฐ” ํด๋ž˜์Šค์˜ Getter, Setter์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉ์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ ๊ฐ์ฒด ๋‹จ์œ„๋กœ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด @ModelAttribute๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ง์ ‘ ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ๋ณธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋™์ž‘์‹œ์ผœ ajaxList์™€ ajaxListModel์˜ ์ฐจ์ด๋ฅผ ํ™•์ธํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@RequestBody

์ด ์–ด๋…ธํ…Œ์ด์…˜์€ POST์™€ PUT ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๊ฐ€ HTTP ์š”์ฒญ ๋ฐ”๋””์— ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ์— ์ด๋ฅผ ํ™•์ธํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•ด์ค๋‹ˆ๋‹ค. ์ด ์–ด๋…ธํ…Œ์ด์…˜์˜ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ GET ์š”์ฒญ๊ณผ ๊ฐ™์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์ œ๊ณต๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์ง์ ‘ ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ๋ณธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋™์ž‘์‹œ์ผœ ajaxMap์™€ ajaxMapGet์˜ ์ฐจ์ด๋ฅผ ํ™•์ธํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ajaxMapGet์˜ ์š”์ฒญ์ด ์™œ ์‹คํŒจํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์„œ๋ฒ„์ธก ๋กœ๊ทธ๋ฅผ ์‚ดํŽด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

@ResponseBody

์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์‘๋‹ต๋˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•˜์—ฌ ๋“ฑ๋ก๋œ ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜์‹œ์ผœ ์‘๋‹ตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋ทฐ์— ๋ชจ๋ธ๋กœ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์‹œ์ผœ ์‘๋‹ตํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ HTTP ๋ณธ๋ฌธ์œผ๋กœ ์‘๋‹ตํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ajaxList์™€ ajaxListNobody๋ฅผ ํ†ตํ•ด์„œ @ResponseBody๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ๋ž‘ ์—†์„ ๊ฒฝ์šฐ๋ฅผ ๋น„๊ตํ•ด๋ณด์„ธ์š”. ์™œ @ResponseBody๊ฐ€ ์—†์„ ๋•Œ ViewResolver๋ฅผ ์ฐพ๋Š” ๊ฒƒ ๊ฐ™๋‚˜์š”?

@RestController

์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์Šคํ”„๋ง 4 ๋ถ€ํ„ฐ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฉ”์†Œ๋“œ๋“ค์— @ResponseBody ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ข€ ๋” ํŽธ์˜์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Test Case

๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ ํ˜„์žฌ ์ง„ํ–‰ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1. GET, @ResponseBody์™€ @ModelAttribute, @RequestParm์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

2. GET, 1๋ฒˆ๊ณผ ๋™์ผํ•˜๋‚˜ @ModelAttribute๋ฅผ ํ†ตํ•ด์„œ ๊ฐ์ฒด ๋‹จ์œ„๋กœ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

3. GET, 1๋ฒˆ๊ณผ ๋™์ผํ•˜๋‚˜ @ResponseBody๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

4. PUT, @ResponseBody์™€ @RequestBody๋ฅผ ์ง€์ •ํ–ˆ์„ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

5. GET, 4๋ฒˆ๊ณผ ๋™์ผํ•˜๋‚˜ GET ์š”์ฒญ์— @RequestBody๋ฅผ ์ง€์ •ํ–ˆ์„ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

6. POST, ResponseEntity๋ฅผ ํ†ตํ•ด์„œ HttpStatus๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๋Š” ์ผ€์ด์Šค

7. POST, 6๋ฒˆ๊ณผ ๋™์ผํ•˜๋‚˜ @ResponseBody๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

8. POST, 7๋ฒˆ๊ณผ ๋™์ผํ•˜๋‚˜ URL์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•จ๊ป˜ ์š”์ฒญ์‹œ์— @RequestParam ์ง€์›์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค

6๋ฒˆ๊ณผ 7๋ฒˆ์€ ์˜์•„ํ•ดํ•˜์‹ค ์ˆ˜ ์žˆ์œผ์‹ค ๊ฒ๋‹ˆ๋‹ค. ์ด์™€ ๊ด€๋ จ๋œ ์ •๋ณด๋Š” ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด ResponseEntity๋Š” ์‘๋‹ต ํ—ค๋”์™€ ๋ฐ”๋””๋ฅผ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด๋ฅผ ์‘๋‹ตํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์ดˆ๋ณด์ž๋“ค์ด ์ž˜๋ชป ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์•Œ์•„๋ด…์‹œ๋‹ค.

Okky์—์„œ Ajax ๊ด€๋ จ ์งˆ๋ฌธ์ด ์˜ฌ๋ผ์˜จ๋‹ค๋ฉด ์ง€์†์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์ €๋Š” ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•œ ๊ฒƒ ๊ฐ™์€๋ฐ ์—๋Ÿฌ๋กœ ์‘๋‹ต๋ฐ›์Šต๋‹ˆ๋‹ค.

  • dataType์„ ์ง€์ •ํ•œ ๋’ค ๊ทธ ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ตํ•ด๋„ ์—๋Ÿฌ ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์Šคํ”„๋ง ์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋Š” ๋ฌธ์ž์—ด์ด๋‚˜ null์„ ์‘๋‹ตํ•˜๋Š”๋ฐ ajax์—์„œ๋Š” json์œผ๋กœ ์ง€์ •ํ•  ๊ฒฝ์šฐ์—๋Š” ์„œ๋ฒ„์—์„œ๋Š” ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ต๋˜์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ json์œผ๋กœ ํŒŒ์‹ฑํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ Ajax ๊ด€๋ จ ์งˆ๋ฌธ์€ ์—ฌ๊ธฐ์— ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ error๊ฐ€ ๋‚œ๋‹ค๊ณ  ํ•ด์„œ ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ต๋ฐ›์ง€ ๋ชปํ–ˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜๊ณ  ๊ณ„์…จ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” html๋ฌธ์„œ๋ฅผ ์‘๋‹ตํ•˜๋Š”๋ฐ ajax ๋ฐ์ดํ„ฐ ํ˜•์‹์ด json์ด๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

2. ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

  • ์š”์ฒญ ๋ฉ”์†Œ๋“œ ํƒ€์ž…์ด GET์ผ ๋•Œ processData๋ฅผ false๋กœ ์ง€์ •ํ•  ๊ฒฝ์šฐ์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ url์— ์ง์ ‘ ํฌํ•จ์‹œ์ผœ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ, POST๊ฐ™์€ ์š”์ฒญ์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์š”์ฒญ ๋ฐ”๋””์— ํฌํ•จ๋˜์–ด์ ธ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

POST์˜ ๊ฒฝ์šฐ์—๋„ url์— ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํฌํ•จ๋œ๋‹ค๋ฉด @RequestParam ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. GET ์š”์ฒญ์„ ํ†ตํ•ด ๋ณด๋‚ด๋Š”๋ฐ ์ผ์ • ๋ฐ์ดํ„ฐ ํฌ๊ธฐ์ด์ƒ ๋ณด๋‚ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ €๋ณ„๋กœ URL์˜ ์ง€์› ํฌ๊ธฐ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— POST๋กœ ์š”์ฒญ ๋ฐ”๋””์— ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จ์‹œ์ผœ ๋ณด๋‚ด๋Š” ์ด์œ ๋„ ๋ฐ”๋กœ ์ด ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์„œ๋ฒ„ ์ธก์—์„œ๋„ ์š”์ฒญ ๋ฐ”๋””์— ๋Œ€ํ•œ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•  ์ˆ˜ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ์‹ ์ž… ๋ฉด์ ‘์‹œ์— GET๊ณผ POST์˜ ์ฐจ์ด๋ฅผ ๋ฌป๋Š” ์ด์œ ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋ณด์•ˆ ๋•Œ๋ฌธ์— ๊ตฌ๋ถ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

4. 406 - Not Acceptable ์‘๋‹ตํ•ด์š” ใ… ใ…  [2017-02-14]

  • ํ˜น์‹œ @ResponseBody๋กœ List๋‚˜ Map์„ ์‘๋‹ตํ•˜๊ณ  ๊ณ„์‹ ๊ฐ€์š”? ์‘๋‹ตํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” annotation-driven ์„ค์ •์ด ๋˜์–ด์žˆ์–ด์•ผ ํ•˜๊ณ  ์š”์ฒญํ•˜๋Š” ์‘๋‹ต ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ฒ„์ „์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ ์Šคํ”„๋ง 3 ๋ถ€ํ„ฐ๋Š” annotation-driven ์„ค์ •์„ ํ•˜๊ณ  jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅด ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด ๋””ํดํŠธ ์ „๋žต์œผ๋กœ ๋ฉ”์‹œ์ง€์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.

5. MessageConverter๋ฅผ ์ด์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•„์š” [2017-04-20]

  • ๋ฉ”์‹œ์ง€ ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์ด์šฉํ•˜์ง€ ์•Š๊ณ ๋„ JSON์œผ๋กœ ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ ContentNegotiatingViewResolver๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํ™•์žฅ์ž๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
<beans:bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
	    <!-- ViewResolver ์šฐ์„ ์ˆœ์œ„ ์„ค์ • -->
	    <beans:property name="order" value="1" />
	    <beans:property name="mediaTypes">
	        <!-- ๋งตํ•‘๋  ํ™•์žฅ์ž ์ •์˜ -->
	        <beans:map>
	            <beans:entry key="json" value="application/json" />
	        </beans:map>
	    </beans:property>
	    <beans:property name="defaultViews">
	        <beans:list>
	            <!-- JSON ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ๋ทฐ -->
	            <beans:bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
	        </beans:list>
	    </beans:property>
	    <beans:property name="ignoreAcceptHeader" value="true" />
	</beans:bean>
@RequestMapping("/api/test.json")
public void api(final ModelMap map){
  Message message = new Message("Message Bean");
  map.put("response", message);
}

ModelMap์— ์ถ”๊ฐ€ํ•˜์—ฌ JSON ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฐธ์กฐ : http://ismydream.tistory.com/139