기능구현

  • 회원가입(주소API연동, 이메일 인증, ajax를 사용하여 아이디 중복검사)
  • 관리자 페이지(인터셉터 적용, 상품관리, 회원관리)
  • 업로드(이미지 등록, 수정, 삭제)
  • 검색
  • 페이징
  • 구매(장바구니, 포인트사용)
  • 댓글(등록,수정,삭제)
  • 중간에 막혔거나 에러 해결 못하겠으면 ybnr92@gmail.com 으로 문의


목표

  • 페이지네이션 구현
  • 사용자가 클릭을 통해 이동할 수 있도록 해주는 페이지네이션 구현을 목표로 한다.


순서

  • PageDTO 클래스 작성
  • 전체 데이터 개수
  • Controller 작업
  • View 처리


PageDTO 클래스 작성

  • com.store.model패키지에 PageDTO 클래스를 생성 후 아래의 변수, 생성자를 작성한다. PageDTO 클래스의 데이터들은 페이지 이동 인터페이스를 출력시키는데 필요로 한 데이터들의 모임이다.


package com.store.model;

public class PageDTO {
	
	/* 페이지 시작 번호 */
	private int pageStart;
	
	/* 페이지 끝 번호 */
	private int pageEnd;
	
	/* 이전, 다음 버튼 존재 유무 */
	private boolean next, prev;
	
	/* 행 전체 개수 */
	private int total;
	
	/* 현재 페이지 번호(pageNum), 행 표시 수(amount), 검색 키워드(keyword), 검색 종류(type) */
	private Criteria cri;
	
	/* 생성자(클래스 호출 시 각 변수 값 초기화) */
	public PageDTO(Criteria cri, int total) {
		
		/* cri, total 초기화 */
		this.cri = cri;
		this.total = total;
		
		/* 페이지 끝 번호 */
		this.pageEnd = (int)(Math.ceil(cri.getPageNum()/10.0))*10;
		
		/* 페이지 시작 번호 */
		this.pageStart = this.pageEnd - 9;
		
		/* 전체 마지막 페이지 번호 */
		int realEnd = (int)(Math.ceil(total*1.0/cri.getAmount()));
		
		/* 페이지 끝 번호 유효성 체크 */
		if (realEnd < pageEnd) {
			
			this.pageEnd = realEnd;
		}
		
		/* 이전 버튼 값 초기화 */
		this.prev = this.pageStart > 1;
		
		/* 다음 버튼 값 초기화 */
		this.next = this.pageEnd < realEnd;
	}

}


  • 선언된 변수들에 대한 getter()/setter(), toString() 메서드를 추가한다.


package com.store.model;

public class PageDTO {
	
	/* 페이지 시작 번호 */
	private int pageStart;
	
	/* 페이지 끝 번호 */
	private int pageEnd;
	
	/* 이전, 다음 버튼 존재 유무 */
	private boolean next, prev;
	
	/* 행 전체 개수 */
	private int total;
	
	/* 현재 페이지 번호(pageNum), 행 표시 수(amount), 검색 키워드(keyword), 검색 종류(type) */
	private Criteria cri;
	
	/* 생성자(클래스 호출 시 각 변수 값 초기화) */
	public PageDTO(Criteria cri, int total) {
		
		/* cri, total 초기화 */
		this.cri = cri;
		this.total = total;
		
		/* 페이지 끝 번호 */
		this.pageEnd = (int)(Math.ceil(cri.getPageNum()/10.0))*10;
		
		/* 페이지 시작 번호 */
		this.pageStart = this.pageEnd - 9;
		
		/* 전체 마지막 페이지 번호 */
		int realEnd = (int)(Math.ceil(total*1.0/cri.getAmount()));
		
		/* 페이지 끝 번호 유효성 체크 */
		if (realEnd < pageEnd) {
			
			this.pageEnd = realEnd;
		}
		
		/* 이전 버튼 값 초기화 */
		this.prev = this.pageStart > 1;
		
		/* 다음 버튼 값 초기화 */
		this.next = this.pageEnd < realEnd;
	}

	public int getPageStart() {
		return pageStart;
	}

	public void setPageStart(int pageStart) {
		this.pageStart = pageStart;
	}

	public int getPageEnd() {
		return pageEnd;
	}

	public void setPageEnd(int pageEnd) {
		this.pageEnd = pageEnd;
	}

	public boolean isNext() {
		return next;
	}

	public void setNext(boolean next) {
		this.next = next;
	}

	public boolean isPrev() {
		return prev;
	}

	public void setPrev(boolean prev) {
		this.prev = prev;
	}

	public int getTotal() {
		return total;
	}

	public void setTotal(int total) {
		this.total = total;
	}

	public Criteria getCri() {
		return cri;
	}

	public void setCri(Criteria cri) {
		this.cri = cri;
	}

	@Override
	public String toString() {
		return "PageDTO [pageStart=" + pageStart + ", pageEnd=" + pageEnd + ", next=" + next + ", prev=" + prev
				+ ", total=" + total + ", cri=" + cri + "]";
	}
	

}


전체 데이터 개수(total)

  • PageDTO 클래스를 정의하였는데 해당 클래스를 인스턴스화 시키기 위해서는 Criteria 클래스의 데이터와 전체 데이터 개수(total) 값이 필요하다. 왜냐하면 두 개의 데이터를 PageDTO 클래스 생성자 파라미터로 부여했기 때문이다.
  • Criteria 클래스는 이미 정의되어 있기도 하고 PageDTO 클래스를 사용할 url 매핑 메서드인 authorManageGET()은 뷰로부터 Criteria 클래스 데이터를 받아오기 때문에 전체 데이터 개수(total)에 대한 값만 구해주면 된다.


AuthorMapper.java 인터페이스

  • author 테이블의 총 행의 개수를 구하는 쿼리를 호출하는 메서드를 선언한다.
  • 총 개수 값을 반환받아야 하기 때문에 메서드 선언부의 리턴 타입은 int이다.
  • 그리고 조건문에 사용할 keyword 데이터를 전달받기 위해 파라미터로 Criteria 클래스를 부여하였다.


package com.store.mapper;

import java.util.List;

import com.store.model.AuthorVO;
import com.store.model.Criteria;

public interface AuthorMapper {
	
	/* 작가 등록 */
	public void authorEnroll(AuthorVO author);
	
	/* 작가 목록 */
	public List<AuthorVO> authorGetList(Criteria cri);
	
	/* 작가 총 수 */
	public int authorGetTotal(Criteria cri);
}


AuthorMapper.xml 인터페이스

  • Mapper 인터페이스에서 선언한 메서드가 실행할 쿼리를 작성한다.
  • 총 개수를 구하는 것이기 때문에 count(*)를 사용하였다. authorGetList() 쿼리에서 적용되는 조건문을 같이 작성해주어야 한다. 왜냐하면 검색조건에 따라 페이지 개수가 변해야 하는데 조건문을 적용해주지 않는다면 뷰에 출력되는 페이지 이동 인터페이스에 실제 존재해야 할 페이지 개수보다 더 많은 페이지 번호가 출력되기 때문이다.


<?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="com.store.mapper.AuthorMapper">
  	
  	<!-- 작가 등록 -->
  	<insert id="authorEnroll">
  		insert into author(authorName, nationId, authorIntro) values(#{authorName}, #{nationId}, #{authorIntro});
  	</insert>
  	
  	<!-- 작가 목록 -->
  	<select id="authorGetList" resultType="com.store.model.AuthorVO">
  		select authorid, authorname, nationid, regdate, updatedate
  		from author
  		<if test="keyword != null">
  			where authorname like concat('%',#{keyword}, '%')
  		</if>
  		order by authorid desc
  		limit #{skip}, #{amount}
  	</select>
  	
  	<!-- 작가 수 -->
  	<select id="authorGetTotal" resultType="int">
  		select count(*) from author
  		
  		<if test="keyword != null">
  			where authorname like concat('%', #{keyword}, '%');
  		</if>
  	</select>
      
  </mapper>


AuthorMapper 메서드 테스트

  • AuthorMapperTests.java에서 작성한 Mapper 메서드가 정상적으로 동작하는지 junit 테스트를 한다.


	/* 작가 총 수 */
	@Test
	public void authorGetTotalTest() throws Exception{
		Criteria cri = new Criteria();
		cri.setKeyword("이작가야");
		
		int total = mapper.authorGetTotal(cri);
		
		System.out.println("total ......... : " + total);
	}


Service 작성

  • AuthorService.java에 앞서 작성한 Mapper 메서드를 호출한 메서드를 선언한다.


package com.store.service;

import java.util.List;

import com.store.model.AuthorVO;
import com.store.model.Criteria;

public interface AuthorService {
	
	/* 작가 등록 */
	public void authorEnroll(AuthorVO author) throws Exception;
	
	/* 작가 목록 */
	public List<AuthorVO> authorGetList(Criteria cri) throws Exception;
	
	/* 작가 총 수 */
	public int authorGetTotal(Criteria cri) throws Exception;

}


  • AuthorServiceImpl.java에 오버라이딩하여 인터페이스에서 메서드의 구현부를 작성한다.


package com.store.service;


import java.util.List;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

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

import com.store.mapper.AuthorMapper;
import com.store.model.AuthorVO;
import com.store.model.Criteria;

@Service
public class AuthorServiceImpl implements AuthorService{
	
	private static final Logger log = LoggerFactory.getLogger(AuthorServiceImpl.class);
	
	@Autowired
	AuthorMapper authorMapper;
	
	/* 작가 등록 */
	@Override
	public void authorEnroll(AuthorVO author) throws Exception {
		
		authorMapper.authorEnroll(author);
	}
	
	/* 작가 목록 */
	@Override
	public List<AuthorVO> authorGetList(Criteria cri) throws Exception {
		
		log.info("(service)authorGetList()......." + cri);
		
		return authorMapper.authorGetList(cri);
	}
	
	/* 작가 총 수 */
	@Override
	public int authorGetTotal(Criteria cri) throws Exception{
		
		log.info("(service)authorGetTotal ....... :" + cri);
		return authorMapper.authorGetTotal(cri);
	}

}


Controller 작업

  • total의 값을 얻기 위해 AuthorService 클래스의 authorGetTotal()을 호출한 후 해당 값과 Criteria 클래스 데이터를 인자값으로 부여하여 PageDTO를 인스턴스화 시킨다.


	/* 작가 관리 페이지 접속 */
	@RequestMapping(value = "authorManage", method = RequestMethod.GET)
	public void anthorManageGET(Criteria cri, Model model) throws Exception {
		logger.info(">>>>>>>>>> 작가 관리 페이지 접속");
		
		/* 작가 목록 출력 데이터 */
		List list = authorService.authorGetList(cri);
		
		model.addAttribute("list", list);
		
		/* 페이지 이동 인터페이스 데이터 */
		model.addAttribute("pageMaker", new PageDTO(cri, authorService.authorGetTotal(cri)));
	}


View 처리

  • 서버로부터 전달받은 pageMaker 데이터를 활용하여 페이지 이동 인터페이스를 구현한다.
  • authorManage.jsp에 class 속성 값이 author_table_wrap인 div 태그 바로 다음 순서에 아래의 코드를 추가한다.


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="../resources/css/admin/authorManage.css?after">
 
<script
  src="https://code.jquery.com/jquery-3.4.1.js"
  integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU="
  crossorigin="anonymous"></script>
</head>
</head>
<body>
 
<%@include file="../includes/admin/header.jsp" %>
                <div class="admin_content_wrap">
                    <div class="admin_content_subject"><span>작가 관리</span></div>
                    <div class="author_table_wrap">
                    	<table class="author_table">
                    		<thead>
                    			<tr>
                    				<td class="th_column_1">작가 번호</td>
                    				<td class="th_column_2">작가 이름</td>
                    				<td class="th_column_3">작가 국가</td>
                    				<td class="th_column_4">등록 날짜</td>
                    				<td class="th_column_5">수정 날짜</td>
                    			</tr>
                    		</thead>
                    		<c:forEach items="${list}" var="list">
                    		<tr>
                    			<td><c:out value="${list.authorId}"></c:out> </td>
                    			<td><c:out value="${list.authorName}"></c:out></td>
                    			<td><c:out value="${list.nationName}"></c:out> </td>
                    			<td><fmt:formatDate value="${list.regDate}" pattern="yyyy-MM-dd"/></td>
                    			<td><fmt:formatDate value="${list.updateDate}" pattern="yyyy-MM-dd"/></td>
                    		</tr>
                    		</c:forEach>
                    	</table>                			
                    </div>   
                     <!-- 페이지 이동 인터페이스 영역 -->
                    <div class="pageMaker_wrap" >
                    
	                    <ul class="pageMaker">
	                    
	                    	<!-- 이전 버튼 -->
	                    	<c:if test="${pageMaker.prev}">
	                    		<li class="pageMaker_btn prev">
	                    			<a href="${pageMaker.pageStart - 1}">이전</a>
	                    		</li>
	                    	</c:if>
	                    	
	                    	<!-- 페이지 번호 -->
	                    	<c:forEach begin="${pageMaker.pageStart}" end="${pageMaker.pageEnd}" var="num">
	                    		<li class="pageMaker_btn ${pageMaker.cri.pageNum == num ? "active":""}">
	                    			<a href="${num}">${num}</a>
	                    		</li>
	                    	</c:forEach>
	                    	
	                    	<!-- 다음 버튼 -->
	                    	<c:if test="${pageMaker.next}">
	                    		<li class="pageMaker_btn next">
	                    			<a href="${pageMaker.pageEnd + 1 }">다음</a>
	                    		</li>
	                    	</c:if>
	                    	
	                    </ul>
	                    
                    </div>      
                    <form id="moveForm" action="/admin/authorManage" method="get">
						<input type="hidden" name="pageNum" value="${pageMaker.cri.pageNum}">
						<input type="hidden" name="amount" value="${pageMaker.cri.amount}">
						<input type="hidden" name="keyword" value="${pageMaker.cri.keyword}">
					</form>     
                </div>
<%@include file="../includes/admin/footer.jsp" %>
<script>
$(document).ready(function() {
	
	//let result = "${enroll_result}";
	let result = '<c:out value="${enroll_result}"/>';
	
	checkResult(result);
	
	function checkResult(result) {
		
		if (result === '') {
			
			return;
		}
		alert("작가'${enroll_result}' 님을 등록하였습니다.");
		
	}
	
});
</script>
</body>
</html>
  • 페이지 이동 버튼이 동작하도록 script 태그 내에 아래의 js 코드를 추가한다.
let moveForm = $('#moveform');

/* 페이지 이동 버튼 */
$(".pageMaker_btn a").on("click", function(e) {
	
	e.preventDefault();
	
	moveForm.find("input[name='pageNum']").val($(this).attr("href"));
	
	moveForm.submit();
});



  • css코드를 authorManage.css에 추가한다.


/* 페이지 버튼 인터페이스 */
.pageMaker_wrap{
	text-align: center;
    margin-top: 30px;
    margin-bottom: 40px;
}
.pageMaker_wrap a{
	color : black;
}
.pageMaker{
    list-style: none;
    display: inline-block;
}	
.pageMaker_btn {
    float: left;
    width: 40px;
    height: 40px;
    line-height: 40px;
    margin-left: 20px;
}
.next, .prev {
    border: 1px solid #ccc;
    padding: 0 10px;
}
.next a, .prev a {
    color: #ccc;
}
.active{							/* 현재 페이지 버튼 */
	border : 2px solid black;
	font-weight:400;
}


  • 이래저래 바쁘다는 핑계로 업로드 기간이 늦었는데, 시리즈를 보며 공부하던 분들에겐 죄송하다는 말씀을 드리며, 시간나는대로 부지런히 올리겠습니다.

Leave a comment