얼마 전에 JSON을 사용해서 AJAX 통신 API를 만드는 과정에서 삽질을 한 적이 있다. 기본만 제대로 살폈으면 쉽게 넘어갈 수 있는 일이었는데 나의 얼렁뚱땅 대충대충 ‘필요하면 그때그때 찾아서 쓰면 되지 뭐.’ 라는 안일함이 하루의 시간을 날려먹는 상황을 낳고 말았다. 방법은 정말 간단했다.

헤맸던 소스: Before Source

  • ● TestForm.java

    @Data
    @NoArgsConstructor
    @ToString
    public class TestForm {
      private String id;
      private String name;
      private List<TestTag> testTags;
    
      @Data
      @NoArgsConstructor
      @ToString
      public class TestTag {
          private String id;
          private String tag;
      }
    }
    
  • ● TestController.java

    @Controller
    public class TestController {
      private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
      @RequestMapping(value="/test", method=RequestMethod.GET)
      public void test(@RequestBody TestForm form, ModelMap map) {
          logger.debug("TestForm : {}", form);
      }
    }
    
  • ● Form JSON

      var form = {
          id: "123",
          name: "123",
          testTags: [{id: "1111", tag: "2222"}]
      };
    
      $.ajax({
          url: "http://localhost:8080/test",
          method: "get",
          type: "json",
          data: form,
          success: function(data) {
              console.log(data);
          }
      });
    

    여러가지 시도를 해봤지만, form의 데이터를 TestController의 test에서 제대로 받아들이지 못하는 문제로 골머리를 썩었다(지금 생각해보면 나의 무식함에 부끄럽지만).

해결 코드: After Source

  • ● TestForm.java

    @Data
    @NoArgsConstructor
    @ToString
    public class TestForm {
      private String id;
      private String name;
      private List<TestTag> testTags;
    
      @Data
      @NoArgsConstructor
      @ToString
      public static class TestTag {
          private String id;
          private String tag;
      }
    }
    
  • ● TestController.java

    @Controller
    public class TestController {
      private static final Logger logger = LoggerFactory.getLogger(TestController.class);
    
      @RequestMapping(value="/test", method=RequestMethod.POST)
      public void test(@RequestBody TestForm form, ModelMap map) {
          logger.debug("TestForm : {}", form);
      }
    }
    
  • ● Form JSON

      var form = {
          id: "123",
          name: "123",
          testTags: [{id: "1111", tag: "2222"}]
      };
    
      $.ajax({
          url: "http://localhost:8080/test",
          method: "post",
          type: "json",
          contentType: "application/json",
          data: JSON.stringify(form),
          success: function(data) {
              console.log(data);
          }
      });
    

Before Source와 After Source의 차이를 눈치챘는가? ㅡ_-)?
RequestMethod가 GET에서 POST로 변경되었다. 이에 대한 설명을 해본다. 토비의 스프링에 @RequestBody, @ResponseBody 를 살펴보기 바란다.

@RequestBody, @ResponseBody

최근 개발하고 있는 방식은 대부분이 프론트엔드와 백엔드를 분리하여 개발을 하고 있다. 프론트엔드의 AJAX요청은 대부분 JSON으로 되어 있고, 이에 맞춰 백엔드에서도 JSON 형태로 응답을 해주는 방식을 취하게 된다. 스프링에서는 이와 관련된 @MVC 관련 애노테이션과 설정을 통해 기능을 제공하고 있다.

  • ● @RequestBody

    이 애노테이션이 붙은 파라미터에는 HTTP 요청의 본문body 부분이 그대로 전달된다.
    AnnotationMethodHandlerAdapter에는 HttpMessageConverter 타입의 메시지 변환기message converter가 여러 개 등록되어 있다. @RequestBody가 붙은 파라미터가 있으면 HTTP 요청의 미디어 타입과 파라미터의 타입을 먼저 확인한다(servlet-context.xml 에서 <annotation-drvien> 태그 내에 선언하는 <message-converter> 에서 확인). 메시지 변환기 중에서 해당 미디어 타입과 파라미터 타입을 처리할 수 있다면, HTTP 요청의 본문 부분을 통째로 변환해서 지정된 메소드 파라미터로 전달해준다.

    내가 헤매던 부분이 바로 이부분이었다. ㅡ_-);;JSON 메시지 변환기에는 MappingJackson2HttpMessageConverter를 사용했다. @RequestBody 애노테이션은 요청에서 Body부분을 살펴 요청된 데이터를 추출하여 파라미터로 변환해주는데, ‘GET’ 메소드 요청의 경우에는 HTTP Body에 요청이 전달되는 것이 아니라, URL의 파라메터로 전달(ex: http://localhost:8080/test?id=123&name=123&testTag=…) 형식으로 전달되기 때문에 @RequestBody로 받으려고 해도 서로 다른 곳을 보며 데이터가 없다는 결과를 던질 수밖에 없다(이 부분도 로그에 대해서 상세하게 설정해서 살펴보면서 확인한 결과. 로그! 개발 중에 문제가 되는 요인들을 찾기 위해서 관심을 가지자).

  • ● @ResponseBody

    @ResponseBody는 @RequestBody와 비슷한 방식으로 동작한다. @ResponseBody가 메소드 레벨에서 부여되면 메소드가 리턴하는 오브젝트는 뷰를 통해 결과를 만들어내는 모델로 사용하는 대신, 메시지 컨버터를 통해 바로 HTTP 응답의 메시지 본문으로 변환된다.

    간단히 이야기 하자면, 요청한 형태에 맞춰서 메시지 변환기를 통해 결과값을 반환한다. ‘콩심은 데 콩나고 팥 심은데 팥난다.’ 랄까? ContentNegotiatingViewResolver 와는 동작방식이 좀 다르다. ContentNegotiatingViewResolver는 등록되어 있는 ViewResolver중에서 controller 메소드의 리턴값을 통해 등록된 ViewResolver 중에서 적합한 형태로 처리해서 반환하는 반면, @ResponseBody는 @RequestBody가 선택한 형식으로 결과값을 변환하여 반환한다고 보면 된다.

  • ● MessageConverter 메시지 변환기의 종류는 Spring API 문서를 참고하자.

정리

해당하는 애노테이션들이 어떻게 동작하는지 내가 했던 인터넷 설정들이 어떻게 반응하는지를 제대로 이해했다면, 별다른 삽질없이 조용히 넘어갈 수 있던 문제였는데, 쉬운 문제였다. 하아!!
요즘 들어서 부쩍 ‘기본을 탄탄히 갖춰야겠다.’라는 생각을 하게되는 일들이 많아지고 있다.

끊임없이 공부하고 공부하라!

  • ● 이와 관련된 내용들은 ‘[토비의 스프링]에서 [스프링 @MVC]’ 관련 내용을 상세하게 설명되어 있다.


  1. https://github.com/spring-projects/spring-mvc-showcase

    스프링 프로젝트에서 제공하는 Spring-MVC-Showcase를 살펴봐도 괜찮다.

  2. SoonHyun 2014.03.07 15:21 신고

    구글링 하다가 좋은글 감사합니다...

JavaScript sort 참고 : http://www.w3schools.com/jsref/jsref_sort.asp
  
var testArray = [
    {"id" : 1, "total" : 3}, {"id" : 2, "total" : 20}, {"id" : 3, "total" : 12}, {"id" : 4, "total" : 9}, {"id" : 3, "total" : 24}
];
function custonSort(a, b) {
  if(a.total == b.total){ return 0} return  a.total > b.total ? 1 : -1;
}
testArray.sort(custonSort);
console.log(testArray);
<< 정렬 결과 >>
[
Object
  1. id1
  2. total3
  3. __proto__Object
,
Object
  1. id4
  2. total9
  3. __proto__Object
,
Object
  1. id3
  2. total12
  3. __proto__Object
,
Object
  1. id2
  2. total20
  3. __proto__Object
,
Object
  1. id3
  2. total24
  3. __proto__Object
]

간단하게 예제를 만들어봤다.


난 전혀 다른 곳을 파고 있었던 것이다. 


  1. DAILY_CHANGLIST_SENDREPORT 2015.03.10 16:36 신고

    앙되요 ㄷㄷㄷ.

허니몬이 궁리하고 있는 오픈API를 이용한 웹애플리케이션 
'일명 미쓰리돌(http://www.ihoney.pe.kr/291)'
을 만들어 볼 준비를 하기 위해서 오픈API 관련한 자료 수집을 하는 과정에서 몇몇 Javascript를 활용하는 API들에서 JSON( JSON - WIKI )이라는 단어가 많이 노출되는 것에 신경이 쓰여서 나중에 JSON이라는 단어에 대해서 한번 알아보자하고 마음의 다짐만 하고 있었습니다.

  그런데 우연찮게 OKJSP(http://okjsp.pe.kr/, 허광남님 운영) RSS를 구독하는 중에 JSON이 보여서 한번 찾아봐야지 하면서 자료를 모으기 찾아보기 시작했습니다(P.S. 허광남님이 5월 10일 DTFE와 관련한 세미나 발표가 있으십니다. ^^ 사전등록 했는데 될런지 모르겠네요!!).

  현재까지의 대략적인 파악으로는 자바스크립트(Javascript)를 뛰어넘는 더 넓은 의미라는 것 밖에는 알 수가 없습니다(잠시 읽고 이정도만 깨우쳤어도 나름 성공아닐까요? ^^). 

  오늘 얼핏 감을 잡은 것으로는, 대부분의 OPENAPI 들이 HTML 에서 자바스크립트(Javascript)를 이용하여 OPENAPI 키와 데이터들을 받아서 가공하고 작동시킬 수 있다, 정도입니다. 2주간의 짧은 과정으로 HTML, CSS, Javascript를 배웠는데, 생각보다 Javascript는 심오함을 간직하고 있는 스크립트 언어입니다. 예전에는 원하는 기능을 구현하려고 소스를 단순히 붙여넣기만 했었는데 이제는 단순히 붙여넣기만 하는 것이 아니라, 소스를 읽고서 대략적으로나마 어떤 기능을 하는지 짐작을 할 수 있는 수준에 겨우 도달한 것 같기도 합니다.

제가 IT 관련한 길을 걷기로 한 이상, 끊임없는 배움의 의무(혹은 즐거움)를 짊어져야 하는 것에 대한 맛뵈기라고 할 수 있겠네요. ^^ 자~!! 저와 함께 JSON(요약하자면 자바스크립트 코딩스타일!?)의 세계로 풍덩 하고 빠져들어봅시다.

● Introducing JSON : http://www.json.org/
● '구글의 페이지 번역하기'로 번역된 페이지
● Introducing JSON 한국어 페이지 : http://www.json.org/json-ko.html아래 자료의 출처입니다.
● codeordie : JSON은 무엇인가? : http://http://www.codeordie.org/wiki/?WhatIsJson2

JSON (JavaScript Object Notation)은 경량의 DATA-교환 형식이다. 이 형식은 사람이 읽고 쓰기에 용이하며, 기계가 분석하고 생성함에도 용이하다. JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999의 일부에 토대를 두고 있다. JSON은 완벽하게 언어로 부터 독립적이지만 C-family 언어 - C, C++, C#, Java, JavaScript, Perl, Python 그외 다수 - 의 프로그래머들에게 친숙한 관습을 사용하는 텍스트 형식이다. 이러한 속성들이 JSON을 이상적인 DATA-교환 언어로 만들고 있다.

JSON은 두개의 구조를 기본으로 두고 있다:

  • name/value 형태의 쌍으로 collection 타입. 다양한 언어들에서, 이는 object, record, struct(구조체), dictionary, hash table, 키가 있는 list, 또는 연상배열로서 실현 되었다.
  • 값들의 순서화된 리스트. 대부분의 언어들에서, 이는 array, vector, list, 또는 sequence로서 실현 되었다.

이러한 것들은 보편적인 DATA 구조이다. 사실상 모든 현대의 프로그래밍 언어들은 어떠한 형태로든 이것들을 지원한다. 프로그래밍 언어들을 이용하여 호환성 있는 DATA 형식이 이러한 구조들을 근간에 두고 있는 것은 당연하다.

JSON 에서, 이러한 형식들을 가져간다:

object는 name/value 쌍들의 비순서화된 SET이다. object는 { (좌 중괄호)로 시작하고 } (우 중괄호)로 끝내어 표현한다. 각 name 뒤에 : (colon)을 붙이고 , (comma)로 name/value 쌍들 간을 구분한다.

array은 값들의 순서화된 collection 이다. array는 [ (left bracket)로 시작해서 ] (right bracket)로 끝내어 표현한다. , (comma)로 array의 값들을 구분한다.

value는 큰따옴표안에 string, number ,true ,false , null, object ,array이 올수 있다. 이러한 구조들을 포함한다.

string은 큰따옴표안에 둘러 싸인 zero 이상 Unicode 문자들의 조합이며, 쌍다옴표안에 감싸지며,backslash escape가 적용된다. 하나의 문자(character)도 하나의 문자열(character string)로서 표현된다. string은 C 또는 Java 문자열 처럼 매우 많이 비슷하다.

number는 8진수와 16진수 형식을 사용하지 않는것을 제외하면 C와 Java number 처럼 매우 많이 비슷하다.

토근들의 어떤 쌍 사이에 공백을 삽입할수 있다. 드물게 encode된 세부 항목을 제외하면, 이렇게 설명된 JSON의 형식은 완벽하게 그 언어를 설명한다.



자료출처 : http://www.codeordie.org/wiki/?WhatIsJson2

JSON

Table of Contents

JSON은 무엇인가?

  1. JSON은 무엇인가?
    • 경량의 데이타 교환 포맷이다.
      > XML과 비교한다.
    • 간단한 포맷
      > 사람들을 위해 읽고 쓰기가 쉽다.
      > 기계들을 위해 분석과 생성이 쉽다.
    • JSON은 텍스트 포맷이다.
      > 언어에 독립적으로 프로그래밍된다.
      > 프로그래머들에게 잘 알려진 C,C++,C#,Java,JavaScript,Perl,Pyton을 포함하는 C와 유사한 언어로 모여서 사용된다.
  2. XML을 넘어 왜 JSON인가?
    • on-the-wire(선을 통한) 데이타 포맷인 XML보다 가볍고 빠르다.
    • JSON 오브젝트는 XML 데이타가 타입이 없는데 비해 타입을 가진다.
      > JSON types : string, number, array, boolean
      > XML 데이타는 모두 String 이다.
    • JavaScript 코드를 위해 Native 코드포맷이다.
      > Data는 사용자의 JavaScript코드 안에서 JSON 객체에 접근이 쉽다.
      XML 데이타가 해석과 장황한 DOM API를 통해 변수에 접근하는 것을 필요로 하는데 비해 
      > 회수한 값들은 사용자의 자바스크립트 안의 객체속성에서 읽기가 쉽다.
  3. JSON은 어디에서 사용되는가?
    • 구성정보를 나타낸다.
    • 통신 프로토콜을 실행한다.

JSON Object

  1. JSON 구조
    • name/value 쌍으로 구성된다.
      > 여러가지의 언어들에서 object, record, struct, dictionary, hashtable, 키를 가지는 리스트, 배열집합 처럼 얻어진다.
    • 값들이 리스트는 순서가 있다.
      > 대부분의 언어들에서 array, vector, list, sequence 처럼 얻어진다.
    • JSON은 대부분의 지금의 언어를 통해 일반적인 데이타구조들이 지원된다.
  2. JSON Object 표기법
    • JSON Object는 name/value 쌍의 set은 순서가 없다.
    • JSON Object는 { 로 시작하고 } 로 끝난다.
    • 각각의 이름은 : 와 ,로 구분된 name/value 쌍의 형식을 따른다.
  3. JSON과 JavaScript
    • JSON은 JavaScript의 객체 문자 표기의 부분집합이다.
      > JSON은 JavaScript안에서 혼란스럽거나 야단스럽지 않게 사용될 수 있다.
  4. JSON Object 예제
      var myJSONObject = {"bindings": [     {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"},     {"ircEvent": "PRIVMSG", "method": "deleteURI", "regex": "^delete.*"},     {"ircEvent": "PRIVMSG", "method": "randomURI", "regex": "^random.*"}   ] }; 
    • 위의 예에서, JSON JavaScript 객체는 세개의 객체를 포함한 배열을 갖고있는, 각각은 "ircEvent","method","regex" 멤버들을 포함한 하나의 멤버 "bindings"를 포함하여 생성된다.
    • 멤버들은 점(.) 이나 그아래 지시자들로 회수 할수 있다.
        myJSONObject.bindings[0].method // "newURI" 
  5. JavaScript 코드안에서 Text를 Object로 변환하기
      var myObject = eval('(' + myJSONtext + ')'); 
    • eval() 함수를 사용하여, JSON text를 JSON 객체로 변환한다.
      > eval()은 JavaScript 컴파일러에서 실행한다.
      > JSON은 JavaScript의 서브셋으로 적합하다. 컴파일러는 text를 정확하게 변환하고, 객체 구조를 만든다.
  6. 보안 & JSON Parser
      // Include http://www.json.org/json.js var myObject = myJSONtext.parseJSON(); 
    • eval()은 컴파일 할 수 있고, 어떤 JavaScript 프로그램에서도 실행된다. 그래서 보안 이슈들(cross-site scripting)을 가질 수 있다.
      > 소스를 신뢰할 수 있을때, eval()을 사용해라.
    • 보안이 염려될 때 - 소스를 신뢰할 수 없을때 - JSON parser를 사용하는게 낫다.
      > JSON parser는 JSON text를 승인할 수 있다. 그래서 좀더 안전하다.
  7. Object를 Text로 변환하기
      var myJSONText = myObject.toJSONString(); 
    • 사용자는 JSON 객체를 JSON text로 변환할 수 있다.
    • JSON은 주기적 데이타 구조를 지원하지 않는다.

      > Do not give cyclical structures to the JSON stringifier
      > 주기적 구조들을 JSON stringfier로 줄수 없다

Java 안에서의 JSON

  1. 자바 개발자를 위한 JSON Tools
    • Parser
      > JSON text 파일들을 해석하고, 그들을 자바 모델로 변환한다.
    • Renderer
      > 자바를 text로 표현하게 한다.
    • Serializer
      > 알기쉬운 POJO clusters를 JSON 표현으로 나열한다.
    • Validator
      > JSON 스키마를 사용하여 JSON 파일의 내용을 유효한지 체크한다.
  2. JSONObject 자바 클래스
    • JSONObject의 name/value 쌍의 집합은 순서가 없다.
    • put 메소드는 객체로 name/value쌍을 add 한다.
    • text들은 JSON syntax rules을 정확히 따른 toString 메소드에 의해 만들어진다.
        myString = new JSONObject().put("JSON", "Hello, World!").toString(); // myString is {"JSON": "Hello, World"} 

클라이언트와 서버사이드 양쪽에서 JSON 데이타를 주고 받는 방법

  1. 서버사이드에서 JSON 데이타를 생성하고 보내는 방법
    • JSONObject 자바 객체를 생성한다.
    • put 메소드를 사용하여 name/value 쌍을 add한다.
    • toString 메소드를 사용하여 String 타입으로 변환한다.
      그리고 "text/xml" 또는 "text/plain" 처럼 content-type과 함께 클라이언트로 보낸다.
        myString = new JSONObject().put("JSON",toString(); // myString is {"JSON": "Hello, World"} 
  2. 클라이언트 사이드에서 JSON 데이타를 받는 방법
    • JSON 데이타는 String 처럼 반환된다.
    • JavaScript 코드안에서 JSON 객체를 만들수 있게 eval()을 호출한다.
      > var JSONdata = eval(req.response.Text);
    • 사용자는 한번 JSON 객체를 가질수 있고, 객체의 속성들에 접근하기 위해 . 을 사용할 수 있다.
        var name = JSONdata.name; var address = JSONdata.addresses[3]; var streetname = JSONdata.addresses[3].street; 
    • 클라이언트 사이드에서 JSON 데이타를 생성하고 보내는 방법
    • JSON 자바스크립트 객체를 생성한다.
    • XMLHttpRequest객체의 open 메소드 안에 "POST" HTTO 메소드를 사용한다.
    • XMLHttpRequest객체의 send 메소드안에서 JSON 자바스크립트 객체를 패스한다.
        var carAsJSON = JSON.stringify(car); var url = "JSONExample?timeStamp=" + new Date().getTime(); createXMLHttpRequest(); xmlHttp.open("POST", url, true); xmlHttp.onreadystatechange = handleStateChange; xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlHttp.send(carAsJSON); 
  3. 서버사이드에서 JSON 데이타를 받는 방법
    • String 타입처럼 JSON데이타를 읽는다.
    • string으로부터 JSONObject 자바객체를 생성한다.
        String json = readJSONStringFromRequestBody(request); //Use the JSON-Java binding library to create a JSON object in Java JSONObject jsonObject = null; try { jsonObject = new JSONObject(json); } catch(ParseException pe) { } 

리소스들

  1. JSON 리소스들
    • Introducing JSON
    > http://www.json.org/
    • JSON in JavaScript
    > http://www.json.org/js.html
    • JSON in Java
    > http://www.json.org/java/index.html

참고문헌

문서에 대하여

최초작성자 : 난다
최초작성일 : 2007년 4월 17일
버전 : 0.1



  1. Favicon of http://cain007.tistory.com BlogIcon 쿠커 2009.12.11 17:43 신고

    잘보고 가요~~
    매우 유용한 정보인것 같아여 실제로 서버와 클라이언트간은 어떻게 처리 할까 했는데
    여기서 답을 얻고 갑니다 ㅎㅎ

+ Recent posts