감 잃지말고 개발하기

[JSP][MVC][MySQL] Select 박스 옵션에 따른 페이징 처리 구현하기 #3. 로직 구현(마지막) 본문

JSP/MVC

[JSP][MVC][MySQL] Select 박스 옵션에 따른 페이징 처리 구현하기 #3. 로직 구현(마지막)

persii 2023. 5. 27. 06:13

도서 목록 페이지에서 <Select> 박스 옵션에 따른 페이징 처리 로직을 기록하고자 한다.

 

저번 포스팅에서는 기본적인 페이징 처리 로직을 구현해 보았다.

기본적인 페이징 처리 이해를 바탕으로 이번 포스팅에서 <select> 박스 옵션에 따른 페이징 처리 로직을 정리해 봄으로써 구현을 완성해 보도록 하겠다.

 

서버 딴 로직은 이전 포스팅에서 정리한 코드에서 살짝씩 수정 및 보충할 것이니

자세한 설명은 이전 포스팅을 참고해 주길 바란다.  


♠ 해당 구현의 로직 흐름이 궁금하면 아래 포스팅을 참고하세요 ♠

 

2023.05.27 - [JSP/MVC] - [JSP][MVC][MySQL] Select 박스 옵션에 따른 페이징 처리 구현하기 #1. 기본 설정

 

[JSP][MVC][MySQL] Select 박스 옵션에 따른 페이징 처리 구현하기 #1. 기본 설정

도서 목록 페이지에서 박스 옵션에 따른 페이징 처리 로직을 총 2개의 포스팅에 걸쳐 기록하고자 한다. 이번 포스팅에서는 구현 흐름과 필요한 테이블 및 클래스를 정리해 보도록 하겠다. 목표

persimmon-ary-stepbystep.tistory.com


 

목표

♠ <select> 박스 옵션에 따른 페이징 처리를 구현할 수 있다.

 

 

로직 코드 및 실행화면

1. 도서 목록 페이지

1-1.  도서 목록 페이지

도서 목록 페이지

 

1-2.  <select> 옵션 HTML

두 개의 <select> 박스의 클래스명은 "sort-select"로 동일하다.

두 개의 <select> 박스 중 어느 옵션을 선택하던지 간에 두 개의 옵션 값이 모두 URL의 파라미터로 설정되어 서버에 요청되게 할 것이다.

 

이전 포스팅에서 설명했듯이,

전체 / 국내 도서 / 외국 도서는 book 테이블의 "b_main_catgy" 칼럼, 즉 대분류 값을 의미한다.

전체를 선택하면 0이, 국내 도서를 선택하면 1이, 외국 도서를 선택하면 2가 URL 파라미터로 설정된다.

 

<div class="sorting">
    <select class="sort-select">
        <option value="s0">전체</option>
        <option value="s1">국내 도서</option>
        <option value="s2">외국 도서</option>
    </select>
</div>
<div class="sorting mr-auto">
    <select class="sort-select">
        <option value="l9">9개씩 보기</option>
        <option value="l15">15개씩 보기</option>
        <option value="l21">21개씩 보기</option>
    </select>
</div>

 

1-3.  <select> 옵션 클릭 JS 코드

$('.sort-select').on('change', function() { });

어느 <select> 박스를 선택하던지 간에 'change' 이벤트를 호출해 두 개의 옵션 값을 모두 URL의 파라미터로 설정하도록 할 것이다.

 

☞ $('.sort-select').not(this);

클래스명이 "sort-select"인 요소 중, 현재 사용자가 선택한 <select> 박스가 아닌 다른 <select> 박스를 찾는다.

 

<script type="text/javascript">
    $(document).ready(function() {

        /* select박스 옵션 선택 */
        $('.sort-select').on('change', function() {
          
          var selectedOption = $(this).val(); // 선택된 값 가져오기
          var otherSelect = $('.sort-select').not(this); // 현재 선택 상자와 다른 선택 상자 찾기

          // 선택된 값 출력하기
          console.log('현재 선택 상자: ' + selectedOption);
          console.log('다른 선택 상자: ' + otherSelect.val());

        });

    });
</script>

원래 위의 코드로 작성하면, 콘솔 창에 <select> 박스의 옵션 값이 정상적으로 출력되어야 한다.

위의 코드로 잘 출력되면 위의 코드를 사용하면 된다.

 

하지만 나의 경우, 가져온 부트스트랩 템플렛의 내부 JS 코드 때문인지 해당 코드가 잘 동작하지 않았다.

HTML 코드를 살펴보니 아래처럼 되어 있었다.

알고 보니, <select> 박스가 display:none이 되고 <ul> 태그가 대신 나타난 것이었다.

게다가 내가 선택한 옵션은 클래스 명에 "selected"가 추가되어 나타나고 있었다.

때문에 나는 class="list"의 class="option selected"으로 접근해서 값을 가져왔다.

 

수정한 <select> 옵션 클릭 JS 코드

 $('.list').children('li.option.selected').each(function() { ));

class명이 list인 요소의 자식 중, <li> 태그의 클래스명이 option selected인 요소를 가져와서 루프를 돈다.

 

  $(this).data('value');

위 이미지를 보면, 내가 가져오고자 하는 <li> 태그의 값이 속성 data-value에 들어있다.

data() 함수는 속성 data-value의 값을 가져오거나 설정할 때 사용할 수 있다.

 

 switch(selectedOption.slice(0,1)) 

사용자가 선택한 옵션 값의 첫 단어로 대분류 값인지, 출력 도서 개수 값인지를 구분한다.

대분류 값이면 파라미터 sort의 값으로, 출력 도서 개수 값이면 파라미터 limit의 값에 넣어서 서버에 전송한다.

 

<script type="text/javascript">
	$(document).ready(function() {

        /* select박스 옵션 선택 */
        $('.sort-select').on('change', function() {

            var sortValue = '';
            var limitValue = '';

            // 선택한 선택 상자와 다른 선택 상자의 선택된 값 가져오기
            $('.list').children('li.option.selected').each(function() {
                selectedValue = $(this).data('value');
                console.log('상자: ' + selectedValue);

                switch(selectedValue.slice(0,1)) {
                    case 's':
                    sortValue = selectedValue.slice(1);
                    break;

                    default:
                    limitValue = selectedValue.slice(1);
                    break;
       	 	}
            });

            // 선택된 값 출력하기
            console.log('sortValue: ' + sortValue);
            console.log('limitValue: ' + limitValue);

            // 서버로 전송
            location.href='/bookList.ok?sort='+sortValue+'&limit='+limitValue;
        });
 
	});
</script>

 

 

값이 잘 출력되는지 확인해 보자.

아래는 첫 번째 <select> 박스에서 "국내 도서"를 택했을 때의 콘솔 결과이다.

첫 번째 <select> 박스 옵션 선택

 

▼ 출력 확인

sortValue엔 국내 도서의 숫자 1이, limitValue엔 9가 출력되었음을 확인할 수 있다.

 

콘솔 결과

 

다음은 두 번째 <select> 박스에서 "15개씩 보기"를 택했을 때의 결과를 보자.

두 번째 <select> 박스 옵션 선택

 

▼ 출력 확인

sortValue엔 국내 도서의 숫자 1이, limitValue엔 15가 출력되었음을 확인할 수 있다.

콘솔 결과


 

2. 컨트롤러(BookFrontController.java)

이전 포스팅 참고

 

3. Action 클래스(BookListAction.java)

이전 포스팅의 Action 클래스를 아래처럼 수정한다.

 

☞ 요청 URL 파라미터 관련 설정

<select> 옵션이 들어간 URL은 http://localhost:.../bookList.ok?sort=0&limit=9 와 같이 2개의 파라미터를 가진다.

각각의 파라미터는 아래와 같은 정보를 지닌다.

 

  • 파라미터 sort : 가져올 도서 대분류(전체 / 국내도서 / 외국도서)
  • 파라미터 limit : 한 화면에 출력할 도서 개수


이에 따라 위 2개의 파라미터와 함께 페이지 번호를 저장할 변수를 초기화해준다.

 

  • int page : 파라미터 page 값을 저장(기본값 1)
  • int limit : 파라미터 limit 값을 저장(기본값 9)
  • int b_main_catgy : 파라미터 sort 값을 저장(기본값 0) 

 

☞ service.getListCount(b_main_catgy); 

기존의 getListCount() 메서드에 파라미터 b_main_catgy를 넘겨주어 대분류 값에 따른 도서 개수를 가져오도록 한다.

 

☞ service.getBookList(page, b_main_catgy, limit);

기존의 getBookList(int page, int limit) 메서드에 파라미터 b_main_catgy를 넘겨주어 대분류 값에 따른 도서를 가져오도록 한다.

 

☞ req.setAttribute("sort", b_main_catgy);

     req.setAttribute("limit", limit);

HttpServletRequest 객체에 대분류 값과 출력될 도서 개수를 속성으로 설정한다. 포워딩을 했을 때 bookList.jsp 페이지에서 사용할 수 있도록 하기 위함이다.

 

package action;

import java.util.ArrayList;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import svc.BookListService;
import vo.ActionForward;
import vo.Book;
import vo.PageInfo;

/** 책 상품 목록보기 요청을 처리하는 Action 클래스*/
public class BookListAction implements Action {

	@Override
	public ActionForward execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		
                /* 필요 변수 초기화 */
		int page = 1;		
		int limit = 9;		
		int b_main_catgy = 0;		

		/* URL 파라미터 설정 */		
		if(req.getParameter("page") != null) {
			page = Integer.parseInt(req.getParameter("page"));
		}

		if(req.getParameter("sort") != null) {
			b_main_catgy = Integer.parseInt(req.getParameter("sort"));
		} 
		
		if(req.getParameter("limit") != null) {
			limit = Integer.parseInt(req.getParameter("limit"));
		}
		
		System.out.println("b_main_catgy : "+b_main_catgy);
		System.out.println("limit : "+limit);
		
		/* DB 처리 
		 * 1. DB에 저장된 총 책 개수 GET
		 * 2. 지정한 페이지에 출력될 책 목록을 GET */
		BookListService service = new BookListService();
		int listCount = service.getListCount(b_main_catgy); 
		ArrayList<Book> bookList = service.getBookList(page, b_main_catgy, limit);
        
                /* 페이징 처리 
                 * 1. 총 페이지 수(나머지가 있는 경우에만 올림처리) 계산
                 * 2. 페이징 부분에 출력되는 페이지 번호 중 첫 번째 페이지 번호(1, 11, 21 등) 계산
                 * 3. 페이징 부분에 출력되는 페이지 번호 중 마지막 페이지 번호(10, 20, 30 등) 계산 */
		int maxPage = listCount / limit + (listCount % limit == 0 ? 0 : 1);
		int startPage = (((int)((double)page / 10 + 0.9)) - 1) * 10 + 1;
		int endPage = startPage + 10 - 1;
		
		// 계산된 endPage 값을 존재하는 페이지의 마지막 페이지 번호(maxPage)로 지정
		if(endPage > maxPage) endPage = maxPage;
		
		/* 페이징에 관한 정보를 저장할 PageInfo 객체 생성 */		
		PageInfo pageInfo = new PageInfo();
		pageInfo.setEndPage(endPage);
		pageInfo.setListCount(listCount);
		pageInfo.setMaxPage(maxPage);
		pageInfo.setPage(page);
		pageInfo.setStartPage(startPage);
		
		/* 포워딩할 때 가져갈 정보 저장 
		 * 1. 책 상품 목록 정보를 속성으로 공유 
		 * 2. pageInfo 객체를 request 영역에 속성 값으로 공유
		 * 3. 도서 분류 값을 속성으로 공유
		 * 4. limit 값을 속성으로 공유
		 * .ok -> .jsp : Forward
		 * */
		req.setAttribute("bookList", bookList);
		req.setAttribute("pageInfo", pageInfo);
		req.setAttribute("sort", b_main_catgy);
		req.setAttribute("limit", limit);
		
		ActionForward forward = new ActionForward();
		forward.setPath("/book/bookList.jsp");
		forward.setRedirect(false);
		return forward;
	}
}

 

4. Service 클래스(BookListService.java)

기존 메서드에 파라미터 b_main_catgy를 받아 bookDAO 객체의 각 메서드에 파라미터로 다시 던져준다.

 

package svc;

import java.sql.Connection;
import java.util.ArrayList;

import dao.BookDAO;
import vo.Book;

import static db.JdbcUtil.*;

/** 책 상품 목록보기 요청을 처리하는 비즈니스 로직을 구현하는 Service 클래스*/
public class BookListService {

	public int getListCount(int b_main_catgy) {

		int listCount = 0;
		
		/* DB 처리 */
		Connection conn = getConnection();
		BookDAO boardDAO = BookDAO.getInstance();
		boardDAO.setConnection(conn);
		listCount = boardDAO.selectListCount(b_main_catgy);
		close(conn);

		return listCount;
	}
	
	public ArrayList<Book> getBookList(int page, int b_main_catgy, int limit) throws Exception{

		/* DB 처리 */
		BookDAO bookDAO = BookDAO.getInstance();
		Connection conn = getConnection();
		bookDAO.setConnection(conn);
		ArrayList<Book> bookList = bookDAO.selectBookList(page, b_main_catgy, limit);
		close(conn);

		return bookList;
	}

}

 

5. DAO 클래스(BookDAO.java)

이전 포스팅의 DAO 클래스 메서드를 아래와 같이 수정한다.

 

☞ if(b_main_catgy != 0) sql += " WHERE b_main_catgy = " + b_main_catgy; 

book 테이블의 b_main_catgy 칼럼에는 1(국내 도서)과 2(외국 도서)의 값만 저장된다.

하지만 파라미터 b_main_catgy의 값에는 0(전체)이 올 수 있기 때문에 그에 대한 처리를 해준다.

파라미터 b_main_catgy의 값이 0이 아닌 경우에만 쿼리문에 WHERE 절이 추가되도록 한다.

 

/** 전체 책 개수 구하는 메서드 */
public int selectListCount(int b_main_catgy) {

    int listCount = 0;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    String sql = "SELECT COUNT(*) FROM jspbookshop.book";
    if(b_main_catgy != 0) sql += " WHERE b_main_catgy = " + b_main_catgy; 

    try {
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();

        if(rs.next()) {
            listCount = rs.getInt(1);
        }
    } catch (Exception e) {
        System.out.println(" selectListCount ERROR : "+e);
    } finally {
        close(rs);
        close(pstmt);
    }

    return listCount;
}

/** 해당 페이지에 출력될 책 상품 목록을 반환하는 메서드 */
public ArrayList<Book> selectBookList(int page, int b_main_catgy, int limit) {

    PreparedStatement pstmt = null;
    ResultSet rs = null;
    ArrayList<Book> bookList = null;
    String book_sql = null;

    switch (b_main_catgy) {
    case 1:
    case 2:
        book_sql = "SELECT * "
                + "FROM jspbookshop.book "
                + "WHERE b_main_catgy = " + b_main_catgy + " "
                + "ORDER BY b_publish_date DESC "
                + "LIMIT ?, ? ";
        break;

    default:
        book_sql = "SELECT * "
                + "FROM jspbookshop.book "
                + "ORDER BY b_publish_date DESC "
                + "LIMIT ?, ? ";
        break;
    }

    System.out.println("sql: "+book_sql);
    int startrow = (page-1)*limit;

    try {
        pstmt = conn.prepareStatement(book_sql);
        pstmt.setInt(1, startrow);
        pstmt.setInt(2, limit);
        rs = pstmt.executeQuery();

        if (rs.next()) {
            bookList = new ArrayList<>();

            do {
                bookList.add(new Book(
                        rs.getInt("b_id"),
                        rs.getString("b_name"),
                        rs.getString("b_writer"),
                        rs.getString("b_translator"),
                        rs.getString("b_publisher"),
                        rs.getString("b_catgy"),
                        rs.getInt("b_price"),
                        rs.getString("b_image"),
                        rs.getInt("b_page"),
                        rs.getString("b_publish_date"),
                        rs.getString("b_content"),
                        rs.getInt("b_readcount")));
            } while (rs.next());
        }
    } catch (Exception e) {
        System.out.println(" B.DAO : selectBookList ERROR : "+e);
    } finally {
        close(rs);
        close(pstmt);
    }

    return bookList;
}

 

6. bookList.jsp

<select> 박스와 페이징 처리 부분을 아래와 같이 수정한다.

 

포워딩했을 때 request 내장 객체의 ${sort} 및 ${limit} 값에 따라 <option>이 selected 되도록 해주고,페이징 처리 <a> 태그의 속성 href 값에 파라미터 sort와 limit가 들어가도록 다시 설정해 준다.

 

<div class="filter-bar d-flex flex-wrap align-items-center">
    <div class="sorting">
        <select class="sort-select">
            <option value="s0">전체</option>
            <option value="s1" <c:if test='${sort eq 1 }'>selected="selected"</c:if>>국내 도서</option>
            <option value="s2" <c:if test='${sort eq 2 }'>selected="selected"</c:if>>외국 도서</option>
        </select>
    </div>
    <div class="sorting mr-auto">
        <select class="sort-select">
            <option value="l9">9개씩 보기</option>
            <option value="l15"<c:if test='${limit eq 15 }'>selected="selected"</c:if>>15개씩 보기</option>
            <option value="l21"<c:if test='${limit eq 21 }'>selected="selected"</c:if>>21개씩 보기</option>
        </select>
    </div>

    <c:if test="${pageInfo.listCount != null }">
        <div class="pagination">

            <!-- 이전 화살표 -->							
            <c:if test="${pageInfo.page > 10 }">
                <a href="/bookList.ok?page=${pageInfo.page-1 }&sort=${sort}&limit=${limit}" class="prev-arrow"><i class="fa fa-long-arrow-left pt-3" aria-hidden="true"></i></a>
            </c:if>

            <c:forEach var="a" end="${pageInfo.endPage }" step="1" begin="${pageInfo.startPage }">
                <c:if test="${a == pageInfo.page}">
                    <a>${a }</a>
                </c:if>
                <c:if test="${a != pageInfo.page}">
                    <a href="/bookList.ok?page=${a }&sort=${sort}&limit=${limit}">${a }</a>
                </c:if>
            </c:forEach>

            <!-- 다음 화살표 -->		
            <c:if test="${pageInfo.endPage < pageInfo.maxPage }">
                <a href="/bookList.ok?page=${pageInfo.endPage+1 }&sort=${sort}&limit=${limit}" class="next-arrow">
                    <i class="fa fa-long-arrow-right pt-3" aria-hidden="true"></i>
                </a>
            </c:if>
        </div>
    </c:if>

</div>

 

7. 브라우저 결과

각각의 경우에 따른 대분류 값과 표시될 도서 개수, 그리고 쿼리문을 서버 딴 콘솔창에서 확인해 보자.

경우 1. 도서 목록 페이지 최초 요청

요청 URL은 http://localhost:8090/bookList.ok이다.

최초 요청
서버 딴 콘솔 창

 

경우 2. 첫 번째 <select> 옵션 선택

요청 URL은 http://localhost:8090/bookList.ok?sort=2&limit=9이다.

외국 도서가 9개씩 출력되고 있는 걸 확인할 수 있다.

첫 번째 <select> 옵션

 

경우 3. 두 번째 <select> 옵션 선택

요청 URL은 http://localhost:8090/bookList.ok?sort=2&limit=15이다.

외국 도서가 15개씩 출력되고 있는 걸 확인할 수 있다.

두 번째 <select> 옵션

 

경우 4. 두 번째 페이지 클릭

요청 URL은 http://localhost:8090/bookList.ok?page=2&sort=2&limit=15이다.

두 번째 페이지에서 외국 도서가 15개씩 출력되고 있는 걸 확인할 수 있다.

(마지막 페이지라 14개만 출력되었다.)

 


 

 

이렇게 <select> 박스 옵션에 따른 페이징 처리를 구현한 로직이 모두 완성되었다.

 

 

페이징 처리 끝.