6. 스프링부트 MVC 게시판 (MyBatis Mapper 사용)
in Gitlog on springboot-mvc
스프링부트 MVC 구조로 사용자 화면은 템플릿엔진인 thymeleaf를 사용하고 데이터 계층은 Mybatis의 Mapper Interface으로 SQL Mapper XML파일을 사용하는 내용이다.
개발 환경
- OS: Window 10
- Dev Tool: Eclipse IDE 2019-06
- JDK: 1.8(_221)
- JAVA: 8
- Framework: Springboot v2.1.7
- Build Tool: Gradle v3
- RDBMS: MariaDB
- DBCP: hikariCP
- Persistence Framework: MyBatis
관련 포스팅
참고 할 내용
스프링 MVC 구조
- Mybatis의 Mapper Interface으로 SQL Mapper XML파일을 사용한 구조이다.
- 해당 포스팅에서 파일 추가 작업이 된 후 프로젝트 구조이다.
DTO(Data Transfer Object) 생성
웹 어플리케이션의 각 계층에 데이터를 교환하는 Model Object인 DTO
생성해줍니다.
BoardDto
public class BoardDto implements Serializable {
private static final long serialVersionUID = 1L;
public int boardIdx;
public String title;
public String content;
public int hitCnt;
public String regId;
public String regDate;
public int getBoardIdx() {
return boardIdx;
}
public void setBoardIdx(int boardIdx) {
this.boardIdx = boardIdx;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getHitCnt() {
return hitCnt;
}
public void setHitCnt(int hitCnt) {
this.hitCnt = hitCnt;
}
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getRegDate() {
return regDate;
}
public void setRegDate(String regDate) {
this.regDate = regDate;
}
@Override
public String toString() {
return "BoardDto [boardIdx=" + boardIdx + ", title=" + title + ", content=" + content + ", hitCnt=" + hitCnt
+ ", regId=" + regId + ", regDate=" + regDate + "]";
}
}
- 일반적으로 위와 같이 해당 클래스 내에서 이클립스 기능으로 getter()/setter()와 toString()를 개발자의 수고가 없이 필드명을 정의해주면 자동으로 만들어 줍니다.
- 우클릭 > Source > Generate Getters and Setters…
- 우클릭 > Source > Generate toString()…
- 필드명이 적기 때문에 그나마 코드가 짧다.(다음 포스팅에서 Lombok으로 변경)
Presentation Layer인 Controller 생성
클라이언트의 요청에 맞는 URL맵핑과 비즈니스 로직을 호출하고 Dispatcher역할을 하는 Presentation Layer(프레젠테이션 계층)
인 @Controller이 선언 된 컨트롤러 영역 클래스이다.
BoardController.java
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping("list")
public ModelAndView notice(HttpServletRequest request, BoardDto boardDto) throws Exception {
ModelAndView mv = new ModelAndView();
// 목록 갯수
int totalCnt = this.boardService.selectBoardListCnt(request, boardDto);
// 목록
List<BoardDto> list = null;
if(totalCnt > 0){
list = this.boardService.selectBoardList(request, boardDto);
}
mv.addObject("totalCnt", totalCnt);
mv.addObject("list", list);
mv.addObject("boardDto", boardDto);
return mv;
}
}
Bussiness Logic Layer인 Service 생성
실제 사용자의 요구에 맞는 서비스를 담당하고 데이터 계층을 호출하는 Bussiness Logic Layer(비즈니스 로직 계층)
이며 @Service 애너테이션이 선언 된 실제 비즈니스 로직이 코딩되어 있는 ServiceImpl(구현클래스)와 서비스 Service(인터페이스)이다.
서비스 영역에서는
BoardServiceImpl.java
@Service
public class BoardServiceImpl implements BoardService {
@Autowired
private BoardMapper boardMapper;
public int selectBoardListCnt(HttpServletRequest request, BoardDto boardDto) throws Exception {
return boardMapper.selectBoardListCnt();
}
public List<BoardDto> selectBoardList(HttpServletRequest request, BoardDto boardDto) throws Exception {
return boardMapper.selectBoardList();
}
}
BoardService.java
public interface BoardService {
public int selectBoardListCnt(HttpServletRequest request, BoardDto boardDto) throws Exception;
public List<BoardDto> selectBoardList(HttpServletRequest request, BoardDto boardDto) throws Exception;
}
Data Access Layer인 Mapper 생성
스프링 프로젝트에서는 Persistence Framework가 MyBatis일 경우 MyBatis 장점인 비즈니스 로직과 SQL구문을 나누는 방식인 @Repository를 사용한 DAO객체 만들거나 @Mapper을 이용해 Mapper 인터페이스를 만들어 Data Access Layer(데이터 접근 계층)
을 구현하고 SQL구문이 있는 SQL Mapper 파일의 namespace와 id를 불러오는 방식이다.
또한 SQL Mapper 작성 없이 Mapper 인터페이스 상에 @Select, @Insert, @Update, @Delete 등의 annotation을 통해 자바로만 객체지향 개발을 할 수 있다.
아래는 Mybatis 스프링 모듈이 @Mapper 애너테이션이 선언 된 클래스들을 스캔하여 Mapper 인터페이스로 인식하며, SQL Mapper인 XML 파일의 namespace(Mapper 인터페이스 전체 경로)와 id(Mapper 인터페이스 메서드)로 맵핑하여 SQL문을 호출하여 결과값을 가진다.
BoardMapper.java
@Mapper
public interface BoardMapper {
int selectBoardListCnt() throws Exception;
List<BoardDto> selectBoardList() throws Exception;
}
board_SQL.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="neo.apps.board.mapper.BoardMapper">
<select id="selectBoardListCnt" parameterType="BoardDto" resultType="int">
SELECT
COUNT(*) AS TOTCNT
FROM
T_BOARD
</select>
<select id="selectBoardList" parameterType="BoardDto" resultType="BoardDto">
SELECT
TITLE
, CONTENT
, HIT_CNT
, REG_ID
, REG_DATE
FROM
T_BOARD
</select>
</mapper>
Presentation Layer인 View 생성
클라이언트의 요청을 처리한 후에 Dispatcher가 @RequestMapping에 View Name으로 model를 사용자에게 보여주는 단계
이다.
스프링부트 프로젝트를 만들때 thymeleaf를 의존성이 추가된 것을 볼 수 있고 사용할려면 추가만 하면 된다. jsp와 문법만 상이하고 그냥 사용하면 된다.(배움은 필요하다.)
아래는 view 역할을 하는 list.html은 thymeleaf 문법의 일부분이다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<meta charset="UTF-8" />
<title>스프링부트 thymeleaf</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<style>
.devCss {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="row col-md-7 table-responsive">
<table class="table devCss" id="listTable">
<thead>
<tr>
<th>제목</th>
<th>내용</th>
<th>등록한 ID</th>
<th>등록한 날짜</th>
</tr>
</thead>
<tbody>
<tr th:if="${totalCnt} > 0" th:each="list : ${list}">
<td th:text="${list.title}">title</td>
<td th:text="${list.content}">content</td>
<td th:text="${list.regid}">regid</td>
<td th:text="${list.regdate}">regdate</td>
</tr>
<tr th:unless="${totalCnt} > '0'">
<td colspan="4">게시물 없음</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
thymeleaf 문법에 관련해서 정리가 잘 되어 다른 분에 블로그다. 가볍게 보시면 도움이 될 듯 하다.
- 참고: https://eblo.tistory.com/55
추후에 thymeleaf대신 handlebar라는 템플릿엔진으로 대체해 개발할 때, 템플릿엔진에 대해서 다시 알아보겠습니다.
[참고]
- https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html