pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.3</version>
</dependency>
pom.xml에 dependency추가
JSON 값을 RESPONSE 해주기 jackson 라이브러리 사용을 위한 설정.
Jackson은 JSON 데이터 구조를 처리해주는 라이브러리.
Spring 3.0 이후로부터, Jacskon과 관련된 API를 제공함으로써, Jackson라이브러리를 사용할 때, 자동화 처리가 가능하게 되었다.
덕분에, JSON데이터를 직접 만들던가, GSON or SimpleJSON방식과 같이 직접 키와 벨류를 세팅하는 방식에서 한 단계 더 발전한 방식.
Spring3.0 이후로 컨트롤러의 리턴 방식이 @RequestBody 형식이라면, Spring은 MessageConverter API를 통해, 컨트롤러가 리턴하는 객체를 후킹 할 수 있다.
Jackson은 JSON데이터를 출력하기 위한 MappingJacksonHttpMessageConverter를 제공.
만약 우리가 스프링 MessageConverter를 위의 MappingJacksonHttpMessageConverter으로 등록한다면,
컨트롤러가 리턴하는 객체를 다시 뜯어(자바 리플렉션 사용), Jackson의 ObjectMapper API로 JSON 객체를 만들고 난 후, 출력하여 JSON 데이터를 완성합니다.
더욱 편리해진 점은, Spring 3.1 이후로 만약 클래스패스에 Jackson 라이브러리가 존재한다면, ( 쉽게 말해 Jackson을 설치했느냐 안 했느냐 )
자동적으로 MessageConverter가 등록된다는 점입니다. 덕분에 우리는 아래와 같이 매우 편리하게 사용할 수 있다.
dispatcher-servlet.xml
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
Spring 과 json연동.
TestController
@RequestMapping(value = "/testList.do")
public String testListView() {
return "test/testList";
}
@RequestMapping(value = "/getTestList.do", produces = "application/json")
@ResponseBody
public ResponseEntity<HashMap<String, Object>> testListDo(Search search) throws Exception {
HashMap<String, Object> result = new HashMap<>();
// 전체 게시글 개수를 얻어와 listCnt에 저장
int listCnt = testServiceImpl.getBoardListCnt(search);
// 검색
search.pageInfo(listCnt);
// 페이징
result.put("pagination", search);
// 게시글 화면 출력
result.put("list", testServiceImpl.selectTest(search));
return ResponseEntity.ok(result);
}
이전의 modelandview방식의 view를 return 해주는 컨트롤러를 위처럼 비동기 방식의 컨트롤러로 코드를 수정한다.
// ajax url을 getTestList.do로 받기 (후킹??)
// produces = "application/json json형태로 response request요청에 따른 response의 content-type을 변경
// @ResponseBody는 서버에서 클라이언트로 응답데이터를 전송하기 위해 사용
Spring Framwork에서 제공하는 클래스중 HttpEntity라는 클래스.
이것은 HTTP 요청(Request) 또는 응답(Response)에 해당하는 HttpHeader와 HttpBody를 포함하는 클래스.
이 HttpEntity 라는 클래스를 상속받아 구현한 클래스가 RequestEntity, ResponseEntity ResponseEntity는 당연히 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스. 따라서 HttpStatus, HttpHeaders, HttpBody를 포함.
HashMap은 Map을 구현한다. Key와 value를 묶어 하나의 entry로 저장한다는 특징을 갖는다. 그리고 hashing을
사용하기 때문에 많은양의 데이터를 검색하는데 뛰어난 성능을 보인다. Map 인터페이스의 한 종류로 ( "Key", value)로 이뤄져 있다. key 값을 중복이 불가능하고 value는 중복이 가능. value에 null값도 사용 가능하다. 멀티스레드에서 동시에
HashMap을 건드려 Key - value값을 사용하면 문제가 될 수 있다. 멀티쓰레드에서는 HashTable을 쓴다
ResponseEntity의 static 메소드인 ok()는 HttpStatus코드의 OK(200)를 응답 데이터에 포함하도록 하고
ResponseEntity의 BodyBuilder를 return함
TestServiceImpl
@Override
public List<TestVO> selectTest(Search search) throws Exception {
search.setPage(search.getPage() * search.getListSize());
List<TestVO> result = testDAOService.selectTest(search);
search.setPage(search.getPage() / search.getListSize());
return result;
}
페이지 번호에 따라 10개씩 정상적으로 넘어가기 위한 코드
testList.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
<!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
crossorigin="anonymous">
<link href="/css/test/test.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
</head>
<body>
<div class="container">
<h1>게시판List</h1>
<div class="testlist">
<form id="boardForm" name="boardForm" method="post">
<table class="table table-hover">
<colgroup>
<col width="20%" />
<col width="50%" />
<col width="15%" />
<col width="15%" />
</colgroup>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>등록일자</th>
</tr>
</thead>
<tbody id="dataSection"></tbody>
</table>
</form>
</div>
<div>
<button id="btn_write" type="button">글작성</button>
</div>
<br>
<!-- pagination{s} -->
<div class="pagination1">
<ul id="paginationBox" class="pagination">
</ul>
</div>
<!-- pagination{e} -->
<!-- search{s} -->
<div class="form-group row justify-content-center">
<div class="w100" style="padding-right: 10px">
<select class="form-control form-control-sm" id="keywordType">
<option value="testTitle">제목</option>
<option value="testContent">본문</option>
<option value="testName">작성자</option>
</select>
</div>
<div class="w300" style="padding-right: 10px">
<input type="text" class="form-control form-control-sm"
name="keyword" id="keyword">
</div>
<div>
<button class="btn btn-sm btn-primary" name="btnSearch"
id="btnSearch">검색</button>
</div>
</div>
<!-- search{e} -->
<!-- 페이지 목록 갯수 -->
<div class="form-group row justify-content-center">
<p>게시판 목록 갯수</p>
<div class="w100" style="padding-right: 10px">
<select id="listSize" class="form-control form-control-sm">
<option value="10">10 개</option>
<option value="15">15 개</option>
<option value="20">20 개</option>
</select>
</div>
</div>
</div>
<input type="hidden" id="searchInput" value="" />
<input type="hidden" id="searchType" value="testTitle" />
</body>
<script type="text/javascript">
$(document).ready(function() {
getPage();
})
$("#listSize").change(function() {
getPage();
});
// 글조회
// 어떤 게시물을 클릭했는지 게시물의 번호(testId)를 넘겨 줘야 함 따라서 게시물 클릭 이벤트에서 게시물의 번호를 인자 값으로 받습니다.
// get 방식으로 데이터를 전송합니다. 따라서 ? 연산자를 사용해 testId를 주소 뒤에 붙여 줍니다
function showDetail(testId) {
var form = document.getElementById("boardForm");
var url = "<c:url value='/testDetail.do'/>";
url = url + "?testId=" + testId;
form.action = url;
form.submit();
}
//글 작성 버튼 클릭 시 testRegister로 이동
$("#btn_write").click(function javascript_onclikc() {
$(location).attr('href', 'testRegister.do');
});
$("#btnSearch").click(function() {
const search = $("#keyword").val();
const searchType = $("#keywordType").val();
$("#searchInput").val(search);
$("#searchType").val(searchType);
getPage();
});
function getPage(page) {
var listSize = $("#listSize").val();
var searchType = $("#searchType").val();
var search = $("#searchInput").val();
$
.ajax({
url : "getTestList.do", //서비스 주소
data : { //서비스 처리에 필요한 인자값
page : page,
searchType : searchType,
keyword : search,
listSize : listSize
},
success : function(res) {
const list = res['list'];
const pagination = res['pagination'];
var data = "";
var block = "";
console.log(pagination);
// 테이블의 row를 삽입하는 부분
for (var i = 0; i < list.length; i++) {
data += "<tr style='cursor:pointer' onclick='showDetail("
+ list[i]['testId'] + ")'>";
data += "<td>" + list[i]['testId'] + "</td>";
data += "<td>" + list[i]['testTitle'] + "</td>";
data += "<td>" + list[i]['testName'] + "</td>";
data += "<td>" + list[i]['testDate'] + "</td>";
data += "</tr>";
}
// 이전버튼 활성화 여부를 결정하는 부분
if (pagination['prev']) {
block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
+ (pagination['startPage'] - 1)
+ ")'> < </a></li>";
} else {
block += "<li class='page-item disabled'><a class='page-link'> < </a></li>";
}
// 번호를 표시하는 부분
for (var i = pagination['startPage']; i < pagination['endPage']; i++) {
if (page !== i) {
block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
+ i + ")'>" + (i + 1) + "</a></li>";
} else {
block += "<li class='page-item disabled'><a class='page-link'>"
+ (i + 1) + "</a></li>";
}
}
if (pagination['next']) {
block += "<li class='page-item'><a class='page-link' href='javascript:getPage("
+ (pagination['endPage'])
+ ")'> > </a></li>";
} else {
block += "<li class='page-item disabled'><a class='page-link'> > </a></li>";
}
$("#dataSection").html(data);
$("#paginationBox").html(block);
}
})
}
</script>
</html>
Console
2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor] Written [{pagination=egovframework.example.test.domain.Search@2e77becd, list=[TestVO [testId=264, testTitle=asdf, testContent=asdf, testDate=2020-10-19], TestVO [testId=263, testTitle=fd, testContent=fd, testDate=2020-10-19], TestVO [testId=262, testTitle=fdas, testContent=asdf, testDate=2020-10-19], TestVO [testId=261, testTitle=d d , testContent=df, testDate=2020-10-19], TestVO [testId=260, testTitle= , testContent=asdfasdf, testDate=2020-10-19], TestVO [testId=259, testTitle=d d, testContent=d, testDate=2020-10-19], TestVO [testId=258, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=257, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=256, testTitle=sdfg, testContent=sdfg, testDate=2020-10-19], TestVO [testId=255, testTitle=asdf, testContent=asdf, testDate=2020-10-19]]}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@3e01cad7]
2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'action': assuming HandlerAdapter completed request handling
2020-10-19 14:46:55,266 DEBUG [org.springframework.web.servlet.DispatcherServlet] Successfully completed request
콘솔창에 제이슨 형태로 Convert가 된 것을 볼 수 있다.
'Spring Framework > 게시판 연습' 카테고리의 다른 글
7. ajax게시판 만들기(작성, 수정, 삭제)(JSON형태로 AJAX비동기 통신) (0) | 2020.10.20 |
---|---|
5. 게시판 만들기(파일 업로드 다운로드) (1) | 2020.09.25 |
4. 게시판 만들기 (페이징, 검색, 페이지 목록 갯수 변경하기) (0) | 2020.09.25 |
3. 게시판 만들기 CRUD 게시물 작성 수정 삭제 (2) | 2020.09.24 |
2. 게시판 만들기 maria DB 연결 (0) | 2020.09.22 |