Controller => Action => Service => DAO => Mybatis => DB
=Service를 건너뛰고 바로 DAO를 호출하는 일이 없도록 하자.
Controller부터 Action까지는 Spring이 대신 해주지만, 그 이후에 로직은 지금이나 Spring을 쓸때나 같다.
*Service
client로부터 요청이 들어오면 controller가 요청에 따라 Action을 호출하고 Action내에서 dao를 호출했다.
=> 단순한 처리였기때문에 가능했다. 하지만 복잡한 기능을 처리해야 하는 경우 서비스 로직이 필요하다.
예를들어서 CRUD로 가져온 데이터를 재 가공 하거나, DAO를 여러번 호출해야 하거나 할때. <DAO는 DB를 단순 호출한다.>
사례를 보자> 인터넷쇼핑에서 주문기능이 있을때. 내부적으로는 어떤 처리를 해야할까? 단순히 DB에 쿼리하나만 날려서 될 일이 아니다.
재고를 줄이고.. 결재를 하고.. 여러가지 동작이 필요하다.
=> 이런것을 서비스라고 할 수 있지!
//예제를 살펴보자.
//기존의 listAction는 Board테이블 안의 모든 글을 List.jsp(뷰)에게 줬었고
//List.jsp 는 아무생각없이 받은 글들을 출력했었다.
=> 예제에 페이징 기능을 추가해보자.!
//listAction.jsp Service로직을 BoardService라는 클래스로 전부 옮겼다. 간결해졌다.
package kosta.action;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import kosta.bean.Board;
import kosta.bean.BoardDao2;
import kosta.bean.BoardService;
import kosta.bean.ListModel;
import kosta.bean.Search;
public class listAction implements Action {
@Override
public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
BoardService service = BoardService.getInstance();
//데이터 뿐만 아니라 페이징 정보까지 받기 위해 ListModel객체로 받는다.
ListModel list = service.listBoardService(request);
request.setAttribute("listModel", list);
ActionForward forward = new ActionForward();
forward.setRedirect(false);
forward.setPath("List.jsp");
return forward;
}
}
2)Service 로직
package kosta.bean;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/*
* DAO메서드 하나만 호출해서 해결되지 않는 비즈니스 로직을 위해 서비스 클래스를 만들었다.
* 페이징 처리
* 1) 페이지당 글 갯수 정하기
* 2) 총 글 개수 구하기
* 3) 총 페이지 수 구하기
* 4) 현재 페이지에 뿌려줄 글 가져오기.
* 5) 시작페이지번호, 마지막 페이지번호 구하기.
*/
public class BoardService {
private static BoardDao2 dao;
private static BoardService service = new BoardService();
private static final int MAX_BOARD_COUNT = 2; //한페이지에 들어갈 최대 글 갯수!
private static final int MAX_PAGE_COUNT = 5; //한페이지에 표시될 페이지목록의 최대 숫자.
public static BoardService getInstance() {
dao = BoardDao2.getInstance();
return service;
}
public Search setSearchToSession(HttpServletRequest request) {
Search search = new Search();
HttpSession session = request.getSession();
String sk = "";
String[] area = null;
//검색조건의 체크박스에 체크가 되어있을 경우 새로운 검색어를 가져온다.
if((area = request.getParameterValues("area")) != null) {
sk = request.getParameter("searchKey");
search.setSearchKey('%' + sk + '%');
search.setArea(area);
session.setAttribute("search", search);
}else if(area == null && (search = (Search)session.getAttribute("search")) != null) {
}
return search;
}
//사용자가 요청하는 페이지에 따라서 표시해야할 페이지 예) 2를 클릭하면 1,2,3,4,5를 표시한다.
public ListModel listBoardService(HttpServletRequest request) throws Exception{
//사용자가 클릭한 페이지번호 가져오기.
String pageNum = request.getParameter("pageNum");
//default = 1page.
if(pageNum == null) {
pageNum = "1";
}
int userRequestPage = Integer.parseInt(pageNum);
Search search = setSearchToSession(request);
// 2)총 글개수
int totalCount = dao.countBoard(search); // Search글개수에 따른 totalCount 구하기.
// 3)총 페이지수
int totalPageCount = totalCount/MAX_BOARD_COUNT;
// 잔여 페이지 처리
if(totalCount % MAX_BOARD_COUNT > 0) {
totalPageCount++;
}
//시작페이지, 마지막페이지 구하기
//시작페이지 공식 = 사용자요청페이지 - (사용자요청페이지 - 1) % (몇페이지씩 보여줄지)
int startPage = userRequestPage - (userRequestPage - 1) % MAX_PAGE_COUNT;
int endPage = startPage + (MAX_PAGE_COUNT - 1); //5page씩 약속을 했으니깐!
//글이 적어서 endPage가 MAX_PAGE_COUNT 보다 적으면
if(endPage > totalPageCount) {
endPage = totalPageCount;
}
//startRow = 화면에 출력해야할 row 중에서 시작.
//startRow 구하기: (사용자가 요청한 페이지 - 1) * 페이지당 글갯수
int startRow = (userRequestPage - 1) * MAX_BOARD_COUNT;
//startRow에서 해당하는 Board객체를 받아온다.
List<Board> list = dao.listBoard(search, startRow);
//글과 함께 list객체(글정보가 들어있음!) 그리고 사용자가 요청한 페이지.. 총페이지.. 화면에 표시할 페이지를 객체로 만들어서 return 한다!
ListModel listModel = new ListModel(list, userRequestPage, totalPageCount, startPage, endPage);//객체로 던지기 위해 생성
return listModel;
}
}
길어지긴 했지만 크게 어려운 내용은 없다.
함께 살펴보자.
//싱글톤패턴으로 dao객체를 싱글톤으로 생성 후, service를 return 한다.
//상수들은 주석과 같다.
private static BoardDao2 dao;
private static BoardService service = new BoardService();
private static final int MAX_BOARD_COUNT = 2; //한페이지에 들어갈 최대 글 갯수!
private static final int MAX_PAGE_COUNT = 5; //한페이지에 표시될 페이지목록의 최대 숫자.
public static BoardService getInstance() {
dao = BoardDao2.getInstance();
return service;
}
listBoardService
//사용자가 클릭한 페이지번호에 따라 최대 2개의 글을 list에 담고,
//뷰에서 페이징 처리를 해주기 위해서 총 페이지 갯수(totalPageCount)/ 사용자가 클릭한 페이지의 화면에 표시될 첫페이지 (startPage)/
사용자가 클릭한 페이지의 화면에 표시될 마지막 페이지 숫자(endPage)를 객체로 return 한다.
=> 글 갯수에 따라 | 1 | 2 | 3 | 4 | 5 | 이렇게 표시한다. 만약 사용자가 4를 클릭했으면, startPage는 1이고 endPage는 5가 되어야 겠지.
//시작페이지, 마지막페이지 구하기
//시작페이지 공식 = 사용자요청페이지 - (사용자요청페이지 - 1) % (몇페이지씩 보여줄지)
int startPage = userRequestPage - (userRequestPage - 1) % MAX_PAGE_COUNT;
int endPage = startPage + (MAX_PAGE_COUNT - 1); //5page씩 약속을 했으니깐!
//글이 적어서 endPage가 MAX_PAGE_COUNT 보다 적으면
if(endPage > totalPageCount) {
endPage = totalPageCount;
}
//startRow = 화면에 출력해야할 row 중에서 시작.
//startRow 구하기: (사용자가 요청한 페이지 - 1) * 페이지당 글갯수
int startRow = (userRequestPage - 1) * MAX_BOARD_COUNT;
//사용자가 클릭한 페이지번호 가져오기. 글 클릭시 뷰에서 파라미터로 넘겨준다.
//최초 화면이 load됐을경우 1로 설정해준다 (1page 보여주려고)
String pageNum = request.getParameter("pageNum");
//default = 1page.
if(pageNum == null) {
pageNum = "1";
}
int userRequestPage = Integer.parseInt(pageNum);
//구하기 위해 아래와 같은 메서드를 실행하는데 우선 기본 로직이 아니니까 뒤에서 설명하자.
public Search setSearchToSession(HttpServletRequest request) {
Search search = new Search();
HttpSession session = request.getSession();
String sk = "";
String[] area = null;
//검색조건의 체크박스에 체크가 되어있을 경우 새로운 검색어를 가져온다.
if((area = request.getParameterValues("area")) != null) {
sk = request.getParameter("searchKey");
search.setSearchKey('%' + sk + '%');
search.setArea(area);
session.setAttribute("search", search);
}else if(area == null && (search = (Search)session.getAttribute("search")) != null) {
}
return search;
}
//총 글개수를 구한다. 여기서 첫번째 Mapper메서드를 호출한다.
//Board.xml에 id countBoard를 select태그로 선언하고, BoardMapper/BoardDao에서 메서드로 맵핑해준다.
//총 페이지 개수는, 모든 글을 페이징한다고 했을때의 총 페이지 개수이다. 화면에 표시될 최대 글개수를 의미한다.
// 그 후 총 글개수가 최대페이지수로 나누어서 떨어지지 않을경우(잔여페이지)를 처리해준다.
Search search = setSearchToSession(request); // 뒤에서 설명할 메서드
// 2)총 글개수
int totalCount = dao.countBoard(search); // Search글개수에 따른 totalCount 구하기.
// 3)총 페이지수
int totalPageCount = totalCount/MAX_BOARD_COUNT;
// 잔여 페이지 처리
if(totalCount % MAX_BOARD_COUNT > 0) {
totalPageCount++;
}
//1 | 2 | 3 | 4 | 5 이런 페이지리스트가 있을때,
//사용자가 임의로 클릭한 페이지에서 시작페이지 넘버와, 마지막 페이지 넘버를 구하는 과정.
//일정한 공식이 있다.
//후에 두번째 MapperMethod를 호출한다.(listBoard)
하지만 listBoard메서드는 이전과 다르다. search(이후 설명)와 startRow를 인자로 받는다.
//시작페이지, 마지막페이지 구하기
//시작페이지 공식 = 사용자요청페이지 - (사용자요청페이지 - 1) % (몇페이지씩 보여줄지)
int startPage = userRequestPage - (userRequestPage - 1) % MAX_PAGE_COUNT;
int endPage = startPage + (MAX_PAGE_COUNT - 1); //5page씩 약속을 했으니깐!
//글이 적어서 endPage가 MAX_PAGE_COUNT 보다 적으면
if(endPage > totalPageCount) {
endPage = totalPageCount;
}
//startRow = 화면에 출력해야할 row 중에서 시작.
//startRow 구하기: (사용자가 요청한 페이지 - 1) * 페이지당 글갯수
int startRow = (userRequestPage - 1) * MAX_BOARD_COUNT;
//startRow에서 해당하는 Board객체를 받아온다.
List<Board> list = dao.listBoard(search, startRow);
//글과 함께 list객체(글정보가 들어있음!) 그리고 사용자가 요청한 페이지.. 총페이지.. 화면에 표시할 페이지를 객체로 만들어서 return 한다!
ListModel listModel = new ListModel(list, userRequestPage, totalPageCount, startPage, endPage);//객체로 던지기 위해 생성
return listModel;
}
//변경후 listBoard
특이사항은 Rowbounds()객체를 파라미터로 전달한다는점이다.
RowBounds함수는 DB 조회 결과중에서 startRow번째부터, MAX_BOARD_COUNT개만큼을 list로 반환해준다는것이다.
(즉, 결과가 몇개라도 내가 원하는 번호의 행에서 2개만 가져 올 수 있다.)
public List<Board> listBoard(Search search, int startRow){
SqlSession sqlSession = getSqlSessionFactory().openSession();
List<Board> list = null;
try {
//DB조회 결과중에서 startRow번부터 2개만 똑 떼서 준다.
list = sqlSession.getMapper(BoardMapper.class).listBoard(new RowBounds(startRow, BoardService.MAX_BOARD_COUNT), search);
//RowBounds = Mybatis에던지는게 아니라, 시작과 마지막값을 지정할 수 있음!
//결과가 10개라도 RowBounds에 해당하는 갯수의 list객체를 만들어준다!
//XML은 실행할 필요가 없다!
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
//여기까지 마치면 페이징에 대한 서버측 구현은 마무리된다.
//리스트를 표시해줄 List.jsp
<%@page import="kosta.bean.BoardDao2"%>
<%@page import="kosta.bean.Board"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>글목록 보기</h3>
<a href="insertForm.do"> 글쓰기 </a>
<table width="500" border="1" cellpadding="0" cellspacing="0">
<tr>
<td>글번호</td>
<td>제목</td>
<td>작성자</td>
<td>작성일</td>
<td>조회수</td>
</tr>
<c:forEach var="board" items='${listModel.list}'>
<tr>
<td>${board.seq}</td>
<td><a href="detail.do?seq=${board.seq}">${board.title}</a></td>
<td>${board.writer}</td>
<td>
<fmt:parseDate var="dateString" value='${board.regdate}'
pattern="yyyy-MM-dd"/>
<fmt:formatDate value="${dateString}" pattern="yyyy-MM-dd"/>
</td>
<td>${board.hitcount}</td>
<td>
<c:if test="${board.fname != null }">
<c:set var="head" value="${fn:substring(board.fname,
0, fn:length(board.fname)-4) }"></c:set>
<c:set var="pattern" value="${fn:substring(board.fname,
fn:length(head) +1, fn:length(board.fname)) }"></c:set>
<c:choose>
<c:when test="${pattern == 'jpg' || pattern == 'gif' }">
<img src="upload/${head }_small.${pattern}">
</c:when>
<c:otherwise>
<c:out value="NO IMAGE"></c:out>
</c:otherwise>
</c:choose>
</c:if>
</td>
</tr>
</c:forEach>
</table>
<!-- 페이지처리 영역 -->
<!-- 이전 startPage-->
<c:if test='${listModel.startPage > 5}'>
<a href="List.do?pageNum=${listModel.startPage - 5}"> [이전] </a>
</c:if>
<!-- 페이지 목록 -->
<c:forEach var = "pageNo" begin='${listModel.startPage}'
end='${listModel.endPage }'>
<!-- 현재페이지에 b태그 적용 -->
<c:if test = '${listModel.requestPage == pageNo }'><b></c:if>
<a href="List.do?pageNum=${pageNo}">[${pageNo}]</a>
<c:if test = '${listModel.requestPage == pageNo }'></b></c:if>
</c:forEach>
<!-- 이후 startPage-->
<c:if test='${listModel.endPage < listModel.totalPageCount}'>
<a href="List.do?pageNum=${listModel.startPage + 5}"> [이후] </a>
</c:if>
<!-- 동적쿼리문 작성 예제 -->
<form action="List.do" method="post" accept-charset="euc-kr" onsubmit="document.charset='euc=kr';">
<input type="checkbox" name="area" value="title"> 제목
<input type="checkbox" name="area" value="writer"> 작성자
<input type="text" name="searchKey" size="10">
<input type="submit" value="검색">
</form>
</body>
</html>
'Today I learned' 카테고리의 다른 글
12월4일 (0) | 2019.01.10 |
---|---|
1월10일 (0) | 2019.01.10 |
1월3일 (0) | 2019.01.03 |
2019년 1월2일 (0) | 2019.01.02 |
12월28일 (0) | 2019.01.02 |
댓글