티스토리 뷰


1. @ResponseBody

  이 Annotation 이 쓰여 있는 메소드에서 리턴 되는 값은 View 를 통해서 출력 되지 않고 HTTP Response Body 에 직접 쓰인다.

이 때, 쓰여지기 전에 리턴 되는 데이터 타입에 따라 MessageConverter 에서 변환이 이뤄진 후 쓰여지게 됩니다.


 MessageCover 의 종류

 - StringHttpMessageConverter

 - FromHttpMessageConverter

 - ByteArrayMessageConverter

 - MarshallingHttpMessageConverter

 - SourceHttpMessageConverter

 - BufferedImagedHttpMessageConverter

 - MappingJacksonHttpMessageConverter


 Jackson's ObjectMapper 를 사용하여 Request, Response 를 JSON 으로 변환할 때 사용 되는 MessageConverter 이다.


Jackson Mapper 와 Core 를 받기 위해서 jar 파일을 검색하였다.



Data Mapper for Jackson 라이브러리만 추가해도 Maven 이 Core 를 하위 라이브러리로 가지고 있으므로, Mapper 만 추가해도 자동으로 Spring 에서 다운 받는다.

안 받아 질 때는 직접 받으면 된다.



본격적으로, @ResponseBody 를 이용한 JSON 데이터 전송을 이용해보자.


댓글을 관리하는 Controller 소스 파일이다.

메소드에 @ResponseBody Annotation 이 추가 되어있다.


comment-Controller.java


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

package com.ktds.comment.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.servlet.ModelAndView;

import com.ktds.comment.service.CommService;

import com.ktds.comment.vo.CommentVO;

@Controller

public class Comm_Controller {

    @Autowired

    CommService cs;

    ModelAndView mav;

    @RequestMapping("showComment.ktds")

    @ResponseBody

    public List<CommentVO> showComment(HttpServletRequest req, @RequestParam int article_num) {

        return cs.getAllComment(article_num);

    }

    @RequestMapping("writeComment.ktds")

    @ResponseBody

    public List<CommentVO> writeComment(HttpServletRequest req) {

        int article_num = Integer.parseInt((req.getParameter("article_num")));

        String comment = req.getParameter("comment");

        HttpSession ss = req.getSession();

        String id = (String) ss.getAttribute("id");

        cs.insertComm(article_num, comment, id);

        List<CommentVO> commList = cs.getAllComment(article_num);

        return commList;

    }

    @RequestMapping("delComment.ktds")

    @ResponseBody

    public List<CommentVO> delComment(HttpServletRequest req,

            @RequestParam int article_num, @RequestParam int comment_num) {

        HttpSession ss = req.getSession();

        String id = (String) ss.getAttribute("id");

        cs.delComm(comment_num, id);

        List<CommentVO> commList = cs.getAllComment(article_num);

        return commList;

    }

}




comment-servlet.xml


1

2

3

4

5

6

7

8

9

10

11

12

13

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"

    xmlns:mvc="http://www.springframework.org/schema/mvc"

    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="com.ktds.comment"></context:component-scan>

    <mvc:annotation-driven/>

</beans>


bean scan을 위한 component-scan 과 annotation 을 읽기 위한 annotation-driven 을 추가한다.


web.xml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

    <servlet>

        <servlet-name>commServlet</servlet-name>

        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>

            <param-name>contextConfigLocation</param-name>

            <param-value>/WEB-INF/spring/appServlet/comment-context.xml</param-value>

        </init-param>

        <load-on-startup>1</load-on-startup>

    </servlet>

    <servlet-mapping>

        <servlet-name>commServlet</servlet-name>

        <url-pattern>/comment/*</url-pattern>

    </servlet-mapping>



comment.js ( 스크립트 소스 코드 )


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

$(document).ready(function() {

    printResult = function(data){

        var result = JSON.stringify(data);

        alert(result);

    };

    

    $('#comm_show').click(function() {

        $.ajax({

            type : "POST",

            url : "/bbs2/comment/showComment.ktds",

            async : true,

            dataType : "json",

            data : {

                article_num : $('#article_num').val()

            },

            success : function(data) {

//                printResult(data);

                var html = "<table width='600' border='1'>";

                //껍질이 하나 더 생겨서 들어올 때는, data에 해당 리스트에 대한 이름을 직접 지정

//                $.each(data.commentList, function(entryIndex, entry) {

                $.each(data, function(entryIndex, entry) {

                    html += '<tr class="bbb">';

                    html += '<td class="bgcolor1">' + entry.id + '</td>';

                    html += '<td class="bgcolor2">' + entry.comment_content + '</td>';

                    html += '<td class="bgcolor2">' + entry.write_date + '</td>';

                    html += '<td id="abc" class="bgcolor2"><input type="button" onclick="rowDelete(this)" value='+ entry.comment_num+'></td>';

                    html += '</tr>';

                });

                html += '</table>';

                $("#show_comment").html(html);

            },

            error : function(xhr) {

                alert("error html = " + xhr.statusText);

            }

        });

    });

    $('.writer').click(function() {

        $.ajax({

            type : "POST",

            url : "/bbs2/comment/writeComment.ktds",

            async : true,

            dataType : "json",

            data : {

                article_num : $('#article_num').val(),

                comment : $('.textcomment').val()

            },

            success : function(data) {

                // json으로 넘어오므로 파싱이 필요없음 data=JSON.parse(data);

                var html = "<table width='600' border='1'>";

                $.each(data, function(entryIndex, entry) {

                    html += '<tr class="bbb">';

                    html += '<td class="bgcolor1">' + entry.id + '</td>';

                    html += '<td class="bgcolor2">' + entry.comment_content + '</td>';

                    html += '<td class="bgcolor2">' + entry.write_date + '</td>';

                    html += '<td class="bgcolor2"><input type="button" onclick="rowDelete(this)" value='+ entry.comment_num+'></td>';

                    html += '</tr>';

                });

                html += '</table>';

                $("#show_comment").html(html);

                

                $('.textcomment').val('');

            },

            error : function(xhr) {

                alert("error html = " + xhr.statusText);

            }

        });

    });

    

    var flag = false;

    $('#btnwrite').click(function() {

        

        if (flag == true) {

            $('#write_show').attr('style''display:none');

            flag = false;

        } else {

            $('#write_show').attr('style''display:inline');

            flag = true;

        }

        

    });

});

function rowDelete(obj){

     $.ajax({

            type : "POST",

            url : "/bbs2/comment/delComment.ktds",

            async : true,

            dataType : "json",

            data : {

                article_num : $('#article_num').val(),

                comment_num : $(obj).val()

            },

            success : function(data) {

                // json으로 넘어오므로 파싱이 필요없음 data=JSON.parse(data);

                var html = "<table width='600' border='1'>";

                $.each(data, function(entryIndex, entry) {

                    html += '<tr class="bbb">';

                    html += '<td class="bgcolor1">' + entry.id + '</td>';

                    html += '<td class="bgcolor2">' + entry.comment_content + '</td>';

                    html += '<td class="bgcolor2">' + entry.write_date + '</td>';

                    html += '<td class="bgcolor2"><input type="button" onclick="rowDelete(this)" value='+ entry.comment_num+'></td>';

                    html += '</tr>';

                });

                html += '</table>';

                $("#show_comment").html(html);

            },

            error : function(xhr) {

                alert("error html = " + xhr.statusText);

            }

        });

}


위 설정은 Http 요청의 바디 정보를 Controller 메소드의 인수로서 받아, HTTP 응답의 바다에 

설정할 정보를 직접 Return 하는 방식이다.

HTTP 요청/응답의 바디와 자바 객체를 서로 변환해줄 필요가 있다. 이 때, 이 변환을 해주는 것이 HttpMessageConverter 객체이다. 이 객체가 Http 메시지를 서로 변환해주는 객체이다.


보통 <context:annotation-config/>  로 annotation 을 읽을 수 있는 것을 사용하지만

HttpMessageConverter 를 위해  <mvc:annotation-driven/> 을 사용한다.

이는 HttpMessageConverter 를 기본적으로 자동 등록하기 때문이다


이 방법에 단점은 날짜가 제대로 출력 되지 않는 점이다.

클라이언트 측에서 날짜를 변경해도 보안 상의 문제가 없을 경우에는

script 를 이용해서 변환해주면 되고, 보안 상의 문제가 있을 경우에는 서버 단에서

포맷을 설정하여 전송하면 된다


먼저, Json 사용을 위한 라이브러리 두 개를 위에서 다운로드 하였으므로, 바로 진행한다.


commtentVO.java 中

1

2

3

4

    @JsonSerialize(using=Write_Formatting.class)

    public Timestamp getWrite_date() {

        return write_date;

    }


포맷을 설정하기 위한 VO(또는 DTO) 에 시간의 getter 에 @JsonSerialize Annotation을 위와 같이 설정한다.


해당 클래스의 대한 내용은 아래와 같다.

Write_Formatting.java


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import java.io.IOException;

import java.sql.Timestamp;

import java.text.SimpleDateFormat;

import org.codehaus.jackson.JsonGenerator;

import org.codehaus.jackson.JsonProcessingException;

import org.codehaus.jackson.map.JsonSerializer;

import org.codehaus.jackson.map.SerializerProvider;

public class Write_Formatting extends JsonSerializer<Timestamp> {

    @Override

    public void serialize(Timestamp comment_date, JsonGenerator gen,

            SerializerProvider arg2) throws IOException,

            JsonProcessingException {

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:SS");

        String formattedDate = formatter.format(comment_date);

        gen.writeString(formattedDate);

    }

}


이 부분만 구현하여 사용하면 정상적인 시간 값을 출력해 줄 수 있다.

포맷은  SimpleDateFormat 에서 알아서 원하는 표현 식으로 사용하면 된다.


TIP.

<context:annotation-config/>

@autowired, @resource를 이용할 때의 선언이다.

context:component-scan 이나 mvc:adnnotation-driven이 bean 정의 파일에 기술되어 있으면 생략할 수 있다.



2. MappingJacksonJSONView( 대소문자 오류 남 )

 - BeanNameViewResolver


Controlloer 의 소스의 일부분을 보면 아래와 같다


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

    @RequestMapping("writeComment.ktds")

    public ModelAndView writeComment(HttpServletRequest req) {

        int article_num = Integer.parseInt((req.getParameter("article_num")));

        String comment = req.getParameter("comment");

        HttpSession ss = req.getSession();

        String id = (String) ss.getAttribute("id");

        cs.insertComm(article_num, comment, id);

        mav = new ModelAndView();

        mav.addObject("commentList", cs.getAllComment(article_num));

        mav.setViewName("JSON");

        return mav;

    }


ModelAndView에 List를 담고 setViewName에 "JSON"이라 명하고 리턴한다.

여기서 중요한 점은 MappingJacksonJSONView 에서의 id를 "JSON"과 맞추는 것이다. 설정 파일을 보자


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"

    xmlns:mvc="http://www.springframework.org/schema/mvc"

    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:component-scan base-package="com.ktds.comment"></context:component-scan>

    <mvc:annotation-driven />

    <bean id="viewResolver"

        class="org.springframework.web.servlet.view.BeanNameViewResolver" />

    <bean id="JSON"

        class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">

        <property name="extractValueFromSingleKeyModel" value="true" />

    </bean>

</beans>


BeanNameViewReslover 와 MappingJacksonJsonView 를 등록해줘야 결과를 얻을 수 있다.


BeanNameViewResolver

 - Bean의 이름(ID)를 이용해서, View로 대신 사용하기 위한 Bean을 찾는 클래스

 - setViewName 의 이름을 bean 중에서 찾는다.


MappingJacksonJsonView

 - Model 의 저장된 Object를 JSON 형태로 변환하여 View 로 만들어주는 클래스


한 가지 중요한 TIP으로, extractValueFromSingleKeyModel 의 value를 true 하는 부분이다.

실제로 저 property가 존재 하지 않을 경우에는 ModelAndView에서 addObject시 이름을 지정하는 부분으로 JSON Data 를 한번 감싸서 전송한다. 이것에 대한 처리는 스크립트에서 처리 할 수 있지만, 기존의 사용하던 여러 Data가 전송될 때, 바로 사용하기 위해서 위 Property를 이용하면 된다.



다른 방법은 MappingJacksonJsonView 를 상속 받은 클래스를 직접 만들어서,

filter를 수정하는 방법이 있는데 이는, 인터넷 검색을 해보도록 한다.


2. PathVariable ( 나중에 더 공부 )

 Path 에 넘어 오는 값을 이용한 처리를 할 때 필요한 Annotation

 Id, 또는 그 url 자체에 기능에 따라 필요한 주소를 넘길 수가 있다.

댓글