- 페이징 기능에 사용되는 컴포넌트
: 백엔드 - Controller, Service, Repository, Mapper.xml, VO, Pager / 프론트엔드 - JSP, JS, CSS
1) 페이징 기능 구현을 위해, VO 객체 외에도 페이징을 구현할 변수가 따로 필요하다. 이는 클래스 Pager를 생성하여 만든다. Pager 클래스에 담길 변수(필드)는 다음과 같다.
- 전체 글의 갯수 totalCount
- 전체 페이지의 수 totalPage
- 페이지당 글의 수 perPage
- 전체 블럭의 수 totalBlock
- 페이지당 블럭의 수 perBlock
- 현재 블럭 번호 curBlock
- 게시판 테이블의 컬럼 kind
- 검색어 search
- 페이지 시작번호 startNum
- 페이지 끝번호 lastNum
- 마지막 블럭 조사 lastCheck
- 첫 시작 행 startRow
이 변수들을 토대로 getter, setter도 생성해준다. lombok을 설치했다면, 애노테이션으로 해결한다.
2) Controller에서 글 목록 조회 시 페이징 처리도 함께 구현되도록 설정한다.
@GetMapping("list")
public ModelAndView getList(Pager pager, ModelAndView mv, NoticeVO noticeVO) throws Exception{
List<NoticeVO> ar = noticeService.getList(pager);
mv.addObject("noticeList", ar);
mv.addObject("pager", pager);
mv.setViewName("board/list");
return mv;
}
웹 브라우저에서 url로 list를 호출하고, 여기에 paging 처리가 더해져 url 뒤에 페이징 관련 파라미터 값이 붙게 된다. 그리고 이 값이 Controller의 getList 메서드를 통해 전달되는데, 매개변수로 url+파라미터 값을 받는다.
다만, 처음 list를 호출했을 경우에는 페이지 번호를 선택하여 호출한 것이 아닌, 전체 목록을 표시하는 첫 페이지인 것이므로 페이징 처리 관련한 파라미터 값에 기본으로 포함되는 kind(컬럼), search(검색), pageNum(페이지번호)는 모두 디폴트값으로 처리된다. 디폴트값 설정 코드는 Pager 클래스에 구현한다.
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getSearch() {
if(this.search==null) {
this.search="";
}
return this.search;
}
public void setSearch(String search) {
this.search = search;
}
public Integer getPageNum() {
if(this.pageNum==null || this.pageNum<1) {
this.pageNum=1;
}
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
Controller에서는 먼저 Service의 getList 메서드를 호출하며 웹 브라우저로부터 받아온 페이징 관련 파라미터 값을 pager에 담아 함께 전달한다. 이후 Service로부터 리턴된 값은 List 타입의 ar 객체에 담아 ModelAndView의 객체로 이름을 붙여 추가해주고, 웹 브라우저에서 받았던 pager 값도 똑같이 추가해준 후, view 네임을 설정하여 리턴해준다.
3) Service에서는 호출받은 메서드를 마찬가지로 실행한다.
public List<NoticeVO> getList(Pager pager) throws Exception{
pager.makeRow();
Long totalCount = noticeRepository.getTotalCount(pager);
pager.makeNum(totalCount);
return noticeRepository.getList(pager);
};
makeRow()와 makeNum() 메서드는 Pager 클래스에 구현하는데, 코드는 아래와 같다.
public void makeRow() {
this.startRow = (this.getPageNum()-1)*this.getPerPage();
}
public void makeNum(Long totalCount) {
//전체페이지 갯수 구하기
Long totalPage = totalCount/this.getPerPage();
if(totalCount%this.getPerPage() !=0) {
totalPage++;
}
//총 페이징 갯수 구하기
Long perBlock=5L;
Long totalBlock = totalPage/perBlock;
if(totalPage%perBlock != 0) {
totalBlock++;
}
//pageNum으로 현재 블럭 번호 구하기
Long curBlock = this.getPageNum()/perBlock;
if(this.getPageNum()%perBlock != 0) {
curBlock++;
}
//curBlock으로 시작번호, 끝번호 구하기
startNum = (curBlock-1)*perBlock+1;
lastNum = curBlock*perBlock;
if(curBlock==totalBlock) {
lastCheck=true;
lastNum=totalPage;
}
}
4) Repository에서도 Service에서 호출받은 메서드를 실행한다. 실제 실행은 Mapper.xml에서 이루어진다.
public List<NoticeVO> getList(Pager pager) throws Exception;
5) Mapper.xml에는 다음과 같이 작성한다.
<select id="getList" parameterType="Pager" resultType="NoticeVO">
select * from destudynotice
where
<choose>
<when test="kind == 'writer'">writer</when>
<when test="kind == 'contents'">contents</when>
<otherwise>title</otherwise>
</choose>
like concat('%', #{search}, '%')
order by num desc
limit #{startRow}, #{perPage}
</select>
<select id="getTotalCount" parameterType="Pager" resultType="Long">
select count(num) from notice
where
<choose>
<when test="kind == 'writer'">writer</when>
<when test="kind == 'contents'">contents</when>
<otherwise>title</otherwise>
</choose>
like concat('%', #{search}, '%')
</select>
list 호출 시, 파라미터 값으로 전달되는 값이 첫 페이지여서 kind와 search가 없는 경우는 디폴트값으로 title를 설정한다. 따라서 search 값은 빈 문자열로 전달된다. kind와 search 값이 존재하는 경우에는 like를 이용하여 search에 입력된 값이 포함되는 데이터를 가져올 수 있도록 '%' 문자열을 넣어 sql 구문을 작성한다.
그리고 다시 4) -> 3) -> 2) -> 1)의 순서로 처리된다.
6) JSP에는 검색 기능과 페이징 기능을 모두 작성해주는데, 페이징만 하려면 a 링크 태그를 이용하여 작성하면 된다. 두 가지를 같이 작성하는 이유는 페이징과 검색 기능이 공통되는 코드를 공유하기 때문에 하나의 코드로 두 개의 기능을 만들 수 있기 때문이다.
페이징만 구현하는 코드는 아래와 같다.
<section id="paging">
<!-- 이전 버튼 -->
<a href="./list?kind=${pager.kind}$search=${pager.search}&pageNum=${pager.startNum-1}">
<button class="p" type="button">이전</button>
</a>
<!-- 현재 페이지 -->
<c:forEach begin="${pager.startNum}" end="${pager.lastNum}" var="i">
<a href="./list?kind=${pager.kind}&search=${pager.search}&pageNum=${i}">${i}</a>
</c:forEach>
<!-- 다음 버튼 -->
<c:if test="${!pager.lastCheck}">
<a href="./list?kind=${pager.kind}$search=${pager.search}&pageNum=${pager.lastNum+1}">
<button class="p" type="button">다음</button>
</a>
</c:if>
</section>
페이징 + 검색 기능을 함께 구현하려면 아래와 같이 작성한다. 이 경우는 하나의 코드로 두 기능을 실행되도록 만들기 위해 JS 코드도 만들어준다.
<!-- 검색 -->
<form action="./list" id="frm">
<input type="hidden" name="pageNum" value="1" id="pageNum">
<select name="kind" id="kind">
<option class="s" value="title">제목</option>
<option class="s" value="contents">내용</option>
<option class="s" value="writer">작성자</option>
</select>
<input type="text" name="search" id="search" value="${pager.search}">
<button type="submit" id="btn">검색</button>
</form>
<!-- 페이징 -->
<section id="paging">
<button class="p" data-list-pn="${pager.startNum-1}" type="button">이전</button>
<c:forEach begin="${pager.startNum}" end="${pager.lastNum}" var="i">
<span class="p" data-list-pn="${i}" >${i}</span>
</c:forEach>
<c:if test="${!pager.lastCheck}">
<button class="p" data-list-pn="${pager.lastNum+1}" type="button">다음</button>
</c:if>
</section>
<!-- 추가로 setKind 함수 호출 시 매개변수 넣어주기 -->
<script type="text/javascript">
setKind("${pager.kind}");
</script>
JS 코드는 아래와 같다. 페이징에서 버튼을 클릭하면, 검색 기능의 코드에 있는 pageNum에 값을 넣어 입력 폼을 서버로 보내준다. 즉, 페이징 호출 -> 검색 기능의 입력 폼에 넣어 서버로 보내는 식으로 처리되는 것이다.
function setKind(kind) {
$(".s").each(function() {
if($(this).val()==kind){
$(this).prop("selected", true);
}
})
};
$(".p").click(function() {
const n= $(this).attr("data-list-pn");
$("#pageNum").val(n);
$('#frm').submit();
});
'TIL' 카테고리의 다른 글
<스프링 부트 Spring boot> 인터셉터(Interceptor)로 관리자, 일반회원 구분 기능 구현 (0) | 2021.12.01 |
---|---|
<스프링 부트 Spring boot> 게시판 구현 - 답글 기능 (0) | 2021.11.30 |
<스프링 부트 Spring boot> 게시판 구현 - 글 목록 조회 (0) | 2021.11.28 |
<자바 Java> 추상클래스 (0) | 2021.11.27 |
<스프링 부트 Spring boot> Ajax 아이디 중복확인 기능 구현 (4) | 2021.11.26 |