[JSP] 커스텀 태그 예시 - 쪽 번호 매기기
- jsp
- controller
- web.xml
- /WEB-INF/tlds/custom-taglib.tld
- com.sample.PagingTag.java
- scripts/include/paging.jsp
필수 라이브러리
- jsp-api-x.x.jar
- servlet-api-x.x.jar
- el-api-x.x.jar
커스텀 태그 설정에 필요한 소스의 일부만 제공하니 참고만 할 것.
jsp 페이지에서 공통으로 사용될 페이징 처리를 커스텀 태그로 구현한 소스다. 처리 결과는 위 사진과 같다. 먼저 아래 설정 목록을 모두 세팅한 뒤 jsp와 controller에 각각 다음의 코드를 작성한다.
붙여넣기 전에…
아래의 소스대로라면 <<
는 first(최초의 페이지 인덱스)를, >>
는 last(마지막 페이지 인덱스)를 불러오게 되며 <
, >
는 각각 바로 이전 페이지, 바로 다음 페이지로의 이동을 의미한다. (딱 1페이지씩만 이동하며 indexPerPage로 나누어 떨어지는 페이지, 혹은 나누어 1만 남는 페이지가 current일 땐 페이지 인덱스가 증가하거나 감소할 것이다.)
만약 페이지 이동 인디케이터인 <
, >
를 바로 이전, 다음 페이지로의 이동이 아니라 indexPerPage(한 화면에 표시할 최대 페이지 인덱스 수)만큼의 이동이라면 당연히 계산식을 수정해야한다. (indexPerPage가 10이면 <
, >
의 의미가 이전 10개의 페이지, 다음 10개의 페이지이길 원할 때)
실제로 자료가 많으면 많을 수록 한 페이지 씩 이동하는것은 탐색기능으로써의 의미가 퇴색된다. 게다가 넘버링 된 숫자를 클릭하는 것과 중복되는 처리이기도 하고…
페이지 이동을 submission이 아닌 ajax로 처리해야 할 경우 다음 코드로 클릭 이벤트 핸들러를 바꿔준다:
$('.paging').find('a').click(function (event) {
event.preventDefault(); // location.href 작동방지
if ($(this).attr("href") != "#") {
$.ajax({
url: $(this).attr("href"),
dataType: 'html',
success: function (data) {
$('#resultArea').html(data);
}
});
}
});
URL을 변경해야 한다면 다음 두 방법 중 하나를 이용한다:
$('.paging a').click(function (event) {
event.preventDefault();
if ($(this).attr("href") != "#") {
var hrefURI = $(this).attr('href');
var params = hrefURI.substring(hrefURI.indexOf('?'));
$.ajax({
url: '/test/sampleurl.do' + params,
type: 'post',
dataType: 'html',
success: function (data) {
$('#resultArea').html(data);
}
});
}
});
$('.paging a').each(function () {
if ($(this).attr('href') != '#') {
var hrefURI = $(this).attr('href');
var params = hrefURI.substring(hrefURI.indexOf('?'));
$(this).click(function (event) {
event.preventDefault();
$.ajax({
url: '/sampleurl.do' + params,
type: 'post',
dataType: 'html',
success: function (data) {
$('#resultArea').html(data);
}
});
});
}
});
jsp
<%@ include file="/scripts/include/paging.jsp"%>
controller
public class SomeController {
@RequestMapping("/list")
public ModelAndView drawList(HttpServletRequest request,
HttpServletResponse response) {
String currentPage = request.getParameter("currentPage");
// ... 페이지 관련 수치 계산, 데이터 조회
request.setAttribute("dataList", [조회한 데이터]);
request.setAttribute("currentPage", [현재 페이지]);
request.setAttribute("totalRecordCount", [데이터의 총 개수]);
request.setAttribute("recordsPerPage", [페이지 당 표시할 데이터 수]);
//request.addAttribute("indexPerPage", 10);
// 최대 페이지 인덱스 수 (default: 10)
// pagingTag.java에서 10으로 설정되어 있다.
// ... 출력
}
}
currentPage
: 페이지 인덱스를 클릭했을 때 컨트롤러에 넘어가는 '요청 페이지 인덱스'의 파라미터명
나머지 소스는 아래와 같으며 의존관계는 대략 jsp -> web.xml -> tld -> java 정도다.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<jsp-config>
<taglib>
<taglib-uri>/tlds/custom-taglib</taglib-uri>
<taglib-location>/WEB-INF/tlds/custom-taglib.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
/WEB-INF/tlds/custom-taglib.tld
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eeweb-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.2</tlib-version>
<jsp-version>2.1</jsp-version>
<short-name>Custom Tag Library</short-name>
<uri>/tlds/custom-taglib</uri>
<tag>
<name>paging</name>
<tag-class>com.sample.PagingTag</tag-class>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>totalRecordCount</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>recordsPerPage</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentPage</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>indexPerPage</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
com.sample.PagingTag.java
package com.sample;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("serial")
public class PagingTag extends TagSupport {
private static final Logger logger = LoggerFactory.getLogger(PagingTag.class);
private String name = null;
private String href = null;
private int totalRecordCount = 0;
private int recordsPerPage = 0;
private int currentPage = 1;
private int startIndex = 0;
private int endIndex = 0;
private int lastIndex = 0;
private List<Page> pages = new ArrayList<Page>();
private Page firstPage = null;
private Page lastPage = null;
private Page previousPage = null;
private Page nextPage = null;
private int indexPerPage = 10;
public String getName() {
return name;
}
public int getTotalRecordCount() {
return totalRecordCount;
}
public int getRecordsPerPage() {
return recordsPerPage;
}
public int getIndexPerPage() {
return indexPerPage;
}
public int getCurrentPage() {
return currentPage;
}
public Page getFirstPage() {
return firstPage;
}
public Page getLastPage() {
return lastPage;
}
public Page getPreviousPage() {
return previousPage;
}
public Page getNextPage() {
return nextPage;
}
public List<Page> getPages() {
return pages;
}
public void setName(String name) {
this.name = name;
}
public void setTotalRecordCount(int totalRecordCount) {
this.totalRecordCount = totalRecordCount;
}
public void setRecordsPerPage(int recordsPerPage) {
this.recordsPerPage = recordsPerPage;
}
public void setIndexPerPage(int indexPerPage) {
this.indexPerPage = indexPerPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public void setFirstPage(Page firstPage) {
this.firstPage = firstPage;
}
public void setLastPage(Page lastPage) {
this.lastPage = lastPage;
}
@Override
public int doStartTag() throws JspException {
try {
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
findStartEndIndex();
this.href = getHref(request);
for (int i = startIndex; i <= endIndex; i++) {
pages.add(new Page(i, getURL(i)));
}
firstPage = new Page(startIndex, getURL(1));
lastPage = new Page(lastIndex, getURL(lastIndex));
if (currentPage - 1 > 0) {
previousPage = new Page(currentPage - 1, getURL(currentPage - 1));
} else {
previousPage = firstPage;
}
if (currentPage + 1 < endIndex || currentPage + 1 < lastIndex) {
nextPage = new Page(currentPage + 1, getURL(currentPage + 1));
} else {
nextPage = lastPage;
}
pageContext.setAttribute(this.getName(), this);
} catch (Exception e) {
logger.error(e.toString());
throw new JspException(e);
}
return EVAL_BODY_INCLUDE;
}
@Override
public int doEndTag() throws JspException {
pages.clear();
firstPage = null;
lastPage = null;
previousPage = null;
nextPage = null;
return SKIP_BODY;
}
private void findStartEndIndex() {
int interval = indexPerPage;
lastIndex = totalRecordCount / recordsPerPage;
if (totalRecordCount % recordsPerPage > 0) {
lastIndex += 1;
}
// logger.debug("interval : {}", interval);
// logger.debug("last index : {}", lastIndex);
startIndex = 1;
while (true) {
endIndex = startIndex + interval - 1;
if (currentPage >= startIndex && currentPage <= endIndex && endIndex <= lastIndex) {
break;
}
if (endIndex > lastIndex) {
endIndex = lastIndex;
break;
}
startIndex = endIndex + 1;
}
if (endIndex == 0) {
endIndex = 1;
}
}
private String getURL(int index) {
// return href + "&page=" + index;
String concatUrl = (href.indexOf("?") == -1) ? "?" : "&";
StringBuffer sb = new StringBuffer();
sb.append(href).append(concatUrl).append("currentPage=").append(index);
return sb.toString();
}
private String getHref(HttpServletRequest request) {
String href = (String) request.getAttribute("javax.servlet.forward.request_uri");
StringBuffer sb = new StringBuffer(256);
sb.append(href);
int i = 0;
@SuppressWarnings("unchecked")
Enumeration<String> e = request.getParameterNames();
while (e.hasMoreElements()) {
String param = (String) e.nextElement();
if (!param.equals("currentPage")) {
String[] values = request.getParameterValues(param);
for (String value : values) {
if (i++ == 0) {
sb.append("?");
} else {
sb.append("&");
}
sb.append(param).append("=").append(value);
}
}
}
// logger.debug("sb.toString(): {}", sb.toString());
return sb.toString();
}
public class Page {
private int index = 0;
private String href = null;
private Page(int index, String href) {
this.index = index;
this.href = href;
}
public int getIndex() {
return index;
}
public String getHref() {
return href;
}
}
}
scripts/include/paging.jsp
<%@ 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="custom" uri="/tlds/custom-taglib"%>
<c:set var="page" value="1" />
<c:if test="${!empty param.currentPage}"><c:set var="page" value="${param.currentPage}" /></c:if>
<c:set var="indexPerPage" value="10" />
<c:if test="${!empty param.indexPerPage}"><c:set var="indexPerPage" value="${param.indexPerPage}" /></c:if>
<div class="paging">
<custom:paging name="paging" recordsPerPage="${recordsPerPage}" totalRecordCount="${totalRecordCount}" currentPage="${page}" indexPerPage="${indexPerPage}">
<%-- First --%>
<c:if test="${!empty paging.firstPage.href}">
<span class="btn"><a href="${paging.firstPage.href}" class="first">처음</a></span>
</c:if>
<%-- Prev --%>
<c:if test="${!empty paging.previousPage.href}">
<span class="btn"><a href="${paging.previousPage.href}" class="prev">이전</a> </span>
</c:if>
<%-- Numbering --%>
<span class="num">
<c:forEach var="numbering" items="${paging.pages}" varStatus="status">
<c:choose>
<c:when test="${numbering.index eq paging.currentPage}">
<strong class="on">${numbering.index}</strong>
</c:when>
<c:otherwise>
<a href="${numbering.href}" title="${numbering.index} 페이지">${numbering.index}</a>
</c:otherwise>
</c:choose>
</c:forEach>
</span>
<%-- Next --%>
<c:if test="${!empty paging.nextPage.href}">
<span class="btn"><a href="${paging.nextPage.href}" class="next">다음</a></span>
</c:if>
<%-- Last --%>
<c:if test="${!empty paging.lastPage.href}">
<span class="btn"><a href="${paging.lastPage.href}" class="last">마지막</a></span>
</c:if>
</custom:paging>
</div>