Spring ajax 게시판 목록 - Spring ajax gesipan moglog

 안녕하세요 코북입니다.

며칠 전에 학원에서 스프링 게시판 만들기 수업이 마무리됐습니다. 수업이 끝나서 간단한 테스트를 봤는데, 게시판 리스트를 두 가지 방식으로 조회하는 문제였습니다. 먼저 가장 기본적인 방법으로 클라이언트가 .do 요청 시 JSTL과 EL을 사용해 데이터를 가져오는 방식, 그리고 ajax를 사욯해 json형식의 리스트를 가져오는 방식을 구현했습니다. 

 작업진행 순서는 다음과 같습니다.

작업 진행 순서

  1. 테이블 설계 -> VO생성
  2. 컨트롤러 작성 + Mapper 인터페이스, SQL Mapper XML 작성
  3. View, JSP 구현

1. 테이블 설계 ( VO 생성 ) 

Spring ajax 게시판 목록 - Spring ajax gesipan moglog

먼저 SQL 명령어를 통해 테이블을 생성했습니다.

-- 테이블 생성
create table tbl_book(
	num int not null auto_increment,
	title varchar(50) not null,
	author varchar(30) not null,
	company varchar(50) not null,
	isbn varchar(30) not null,
	count int not null default 0,
	primary key(num)
);

-- 생성 확인
select * from tbl_book 

-- 데이터 삽입
insert into tbl_book(title, author, company, isbn)
values ('제 4차 산업혁명','코북','아주커','111-11-1111-111-1');

...

insert into tbl_book(title, author, company, isbn)
values ('줄게 네 갤럭시','코북','Samsung','555-55-5555-555-5');

테이블 생성 후 그 구조에 맞춰 VO를 설계했습니다.

MySQL 데이터 베이스를 활용했습니다.

package kr.book.mapper;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookVO {
	
	private int num;
	private String title;
	private String author;
	private String company;
	private String isbn;
	private int count;
	
}

Lombok API를 사용하면 생성자와 getter, setter를 스프링에서 자동으로 생성해줍니다. 물론 스프링이 완전히 혼자 생성하지는 못하고, 자동생성을 위해 Annotation(어노테이션)을 배치시켜 정보를 제공해줘야 합니다. 각각의 어노테이션의 기능입니다.

@Data : getter, setter 생성

@AllArgsConstructor : 모든 프로퍼티(property)를 변수로 갖는 생성자

@NoArgsConstructor : 기본 생성자

Spring ajax 게시판 목록 - Spring ajax gesipan moglog

잘 생성됐는지 Outline에서 확인할 수 있습니다.

2. 컨트롤러 작성 + Mapper 인터페이스, SQL Mapper XML 작성

 설계한 테이블에 저장된 데이터들을 프로그램에서 어떻게 가져올 것인지 컨트롤러(POJO)와 Mapper를 통해 구현합니다. 가시성을 위해 import는 코드에서 제외했습니다.

1. 컨트롤러

package kr.book.bshop;

@Controller
public class BookController {

	@Autowired
	private BookMapper mapper;
	
	@RequestMapping("/bookListAjax.do")
	public @ResponseBody List<BookVO> bookListAjax(){
		List<BookVO> list = mapper.bookListAjax();
		return list;
	}
	
	@RequestMapping("/bookList.do")
	public String bookList(Model model) {
		
		List<BookVO> list = mapper.bookList();
		model.addAttribute("list", list);
		
		return "bookList";
	}
}

2. Mapper Interface

package kr.book.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BookMapper {
	
	public List<BookVO> bookListAjax();
	public List<BookVO> bookList();
	
}

3. SQL Mapper XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.book.mapper.BookMapper">
    
    <select id='bookListAjax' resultType='kr.book.mapper.BookVO'>
        select * from tbl_book order by num desc
    </select>
    
    <select id='bookList' resultType='kr.book.mapper.BookVO'>
        select * from tbl_book order by num
    </select>
    
</mapper>

 코드보다는 클라이언트 요청에서부터 어떻게 작업이 처리되는지 흐름이 중요합니다.

 요청 처리 과정은 클라이언트가 request(요청)한 내용을 Front Cotroller에서 먼저 확인합니다. Front Cotroller는 확인한 내용을 Controller에게 요청하고, Controller는 Handler Mapping을 통해 클라이언트의 요청( ~~ .do)을 해석해 메소드를 실행합니다. 이때, Controller가 @Mapper를 통해 mapper interface의 위치를 찾아 scan하면, Spring Container라는 메모리에 mapper interface가 올라가면서, 결과적으로 Controller가 interface의 메소드들을 사용할 수 있게 됩니다. 

 mapper interface는 MyBatis를 통해 xml과 연결됩니다. 이때, xml은 namespace를 통해 interface를 찾아갈 수 있습니다. 이렇게 interface와 xml이 연결되면 SQL이 실행됩니다.

 위 과정을 걸친 Controller는 메모리(Model class)에 객체바인드를 통해 setAttribute를 하고, jsp가 forwarding을 통해 getAttribute를 할 수 있습니다.

 Contoller의 메소드 return값(String type)이 Front Controller에 전달되면 ViewResolver가 이를 해석하여 다시 Front Controller에 값을 보내줍니다. Front Controller는 해석한 값을 forwarding하여 클라이언트의 요청에 응답합니다. 클라이언트는 jsp가 getAttribute한 값을 볼 수 있게 됩니다.

3. View, JSP 구현

 데이터를 가져오는 연결을 모두 마무리했으니 클라이언트에게 보여 줄 JSP를 구현하면 끝입니다.

Spring ajax 게시판 목록 - Spring ajax gesipan moglog
Spring ajax 게시판 목록 - Spring ajax gesipan moglog

리스트를 구현하고 도서목록 가져오기 버튼 클릭 시 ajax를 사용해 리스트를 다시 가져올 수 있도록 만들어야 합니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="cpath" value ="${pageContext.request.contextPath}"/>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
  <script type ="text/javascript">
  	
  	function loadJson(){
  		$.ajax({
  			url:"${cpath}/bookListAjax.do",
  			type:"get",
  			dataType:"json",
  			success: ajaxHtml,
  			error:function(){alert("error");}
  		});
  	}
  	
  	function ajaxHtml(data){
  		var html="<table class='table'>";
  		html+="<tr>";
  		html+="<td>번호</td>";
  		html+="<td>제목</td>";
  		html+="<td>작가</td>";
  		html+="<td>출판사</td>";
  		html+="<td>ISBN</td>";
  		html+="<td>보유도서수</td>";  		
  		html+="</tr>";
  		
  		$.each(data, (index, obj)=>{ 
  			html+="<tr>";
  	  		html+="<td>"+obj.num+"</td>";
  	  		html+="<td>"+obj.title+"</td>";
  	  		html+="<td>"+obj.author+"</td>";
  	  		html+="<td>"+obj.company+"</td>";
  	  		html+="<td>"+obj.isbn+"</td>";
  	  		html+="<td>"+obj.count+"</td>";
  	  		html+="</tr>";
  		})
  		html+="</table>";
  		
  		$("#ajaxBookList").html(html);
  	}
  
  </script>
<title>Insert title here</title>
</head>
<body>
<div class="container">
  <h2>Spring MVC BOOK (JSTL+EL)</h2>
  <div class="panel panel-default">
    <div class="panel-heading">BOOK LIST</div>
    <div class="panel-body">
    	<table class ="table table-hover table-bordered">
    	  <tr>
    	    <td>번호</td>
    	    <td>제목</td>
    	    <td>작가</td>
    	    <td>출판사</td>
    	    <td>ISBN</td>
    	    <td>보유도서수</td>
    	  </tr>

    	  <c:forEach var="vo" items="${list}">
    	  <tr>
    	    <td>${vo.num}</td>    	 
    	    <td>${vo.title}</td>
    	    <td>${vo.author}</td>
    	    <td>${vo.company}</td>
    	    <td>${vo.isbn}</td>
    	    <td>${vo.count}</td>
    	  </tr>    	  
    	  </c:forEach>
    	 
    	</table>
    	<button class = "btn btn-success btn-sm" onclick="loadJson()">도서목록 가져오기</button>
    	<div id="ajaxBookList">여기에 도서목록이 출력됩니다.</div>
    </div>
    <div class="panel-footer">빅데이터 (코북)</div>
  </div>
</div>
</body>
</html>

 버튼 클릭 시 loadJson() 함수가 실행되도록 설정해줍니다. 함수는 ajax를 사용해 bookListAjax.do를 요청받으면 json데이터를 응답해줍니다. callback함수를 통해 서버에 요청한 내용에 대한 응답을 처리합니다. callback함수는 jQuery의 배열을 관리하는 each함수를 사용해 구현했습니다.

JSTL core 라이브러리를 사용했고 접두사는 c로 했습니다. cpath라는 JSTL 변수를 선언했는데, value값은 EL문법을 사용해 context-root로 했습니다. 이제 ajax url에 cpath를 적어두면 나중에 context-root의 이름이 바뀌어도 바뀐 root경로를 스스로 찾아갈 수 있게 됩니다.

body태그에 구현된 리스트는 JSTL과 EL문법을 사용해 구현했습니다. 기존의 JSP와 다르게 JSTL을 사용해 스크립트릿 요소를 제거하여 구성했고, 표현식은 대신 EL문법을 사용했습니다. 

구현 화면

구현 화면입니다.

Spring ajax 게시판 목록 - Spring ajax gesipan moglog
Spring ajax 게시판 목록 - Spring ajax gesipan moglog

배운 점

 코드의 난이도는 쉬웠지만 실습을 하면서 Spring이 어떤 흐름 속에서 클라이언트의 request를 처리하고 response하는지 공부할 수 있었습니다.