[Paging] Servlet 페이징처리 -> 오프셋 기반

SeoHa·2025년 5월 24일

SPRING

목록 보기
1/7

[페이징 구현을 위해 알아야하는 값]

totalPosts(모든 글 개수), currentPage(현재 페이지 번호), postsPerPage(한 페이지당 표시할 글 개수), displayPageNum(한 번에 표시할 페이지 개수), totalPages(총 페이징 수), totalNum(총 개수)

[페이징을 구현하기 위해 구해야 할 값]

prev(이전), next(다음), startPage, endPage


1. prev(이전)

prev는 currentPage로 구한 startPage가 1인 경우 false고, 나머지 경우는 모두 true.
prev = (startPage == 1) ? false : true

2. next(다음)

next는 currentPage로 구한 endPage가 totalPages(모든 페이지 개수)인 경우 false고, 나머지 경우는 true
next = (endPage == totalPages) ? false : true

3. endPage(마지막 페이지 번호)

endPage는 currentPage, displayPageNum, totalPages 세 가지 값에 의존( endPage는 현재 페이지의 마지막 번호)
endPage = (( (currentPage - 1) / displayPageNum ) + 1 ) * displayPageNum

  • 만약 totalPages가 이 endPage보다 작은 경우([EX 2, 4, 5]가 여기에 해당)
    이럴 때만 위의 식으로 구한 endPage 대신 totalPages
int endPage = (((currentPage-1)/displayPageNum)+1) * displayPageNum;

if(totalPages < endPage) 
    endPage = totalPages;

4. startPage (시작 페이지 번호)

startPage도 endPage와 비슷한 원리
startPage = ((currentPage-1)/displayPageNum) displayPageNum + 1*

5. totalpages(총 page 개수)

total page 개수는 total post 개수와 postsPerPage의 값에 의존
totalPages = ( (totalPosts - 1) / postsPerPage ) + 1 (이때, /는 '몫' 연산자임)

6.currentPage(현재 페이지 번호)

page를 파라미터로 받아와서 진행 -> 해당 파라미터가 null이 아니면 받아온 값으로 현재 페이지 번호 설정 (디폴트 값은 1)

String pageParam = request.getParameter("page");
  
int currentPage = 1;
            if(pageParam!=null){
                try{
                    currentPage = Integer.parseInt(pageParam);
                }catch (NumberFormatException e){
                    currentPage = 1;
                }
            }

7. postsPerPage(한 페이지당 표시할 글 개수)

한 페이지당 몇개씩 보이게 할건지 설정

8. displayPageNum(한 번에 표시할 페이지 개수)

한번에 보이게 할 페이지 수 설정

9. 총 개수

값의 총 개수 -> DAO에서 해당 디비값의 전체 개수를 가져와서 사용

-controller
int total = dao.getRoomcountAll();

-DAO
 //페이징 처리
 //SQL 실행해서 offset부터 limit만큼 방 목록을 가져옴
 public List<Room> getRoomsByPage(int offset, int limit) {
     List<Room> rooms = new ArrayList<>();
     String sql = "SELECT * FROM rooms ORDER BY id ASC LIMIT ?, ?";

     try (
             Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)
        ) {
            pstmt.setInt(1, offset);
            pstmt.setInt(2, limit);

         try (ResultSet rs = pstmt.executeQuery()) {
             while (rs.next()) {
                 Room room = new Room();
                 room.setId(rs.getInt("id"));
                 room.setName(rs.getString("name"));
                 room.setStatus(rs.getString("status"));
                 rooms.add(room);
             }
         }
     } catch (Exception e) {
         System.err.println("페이징처리 실패: " + e.getMessage());
         e.printStackTrace();
     }

     return rooms;
 }

추가

- backend -> java
1. DAO에서 원하는 페이지 만큼 값 가져오는 메소드 생성 :
-> DB 쿼리에서 페이징 처리 - SQL에서 LIMIT, OFFSET을 이용하여 원하는 "페이지만큼"만 가져오기 (SQL 실행해서 offset부터 limit만큼 글 개수 가져옴)

    -DAO - 페이징 처리
    public List<Room> getRoomsByPage(int offset, int limit) {
        List<Room> rooms = new ArrayList<>();
        String sql = "SELECT * FROM rooms ORDER BY id ASC LIMIT ?, ?";

        try (
                Connection conn = getConnection();
                PreparedStatement pstmt = conn.prepareStatement(sql)
        ) {
            pstmt.setInt(1, offset);
            pstmt.setInt(2, limit);

            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    Room room = new Room();
                    room.setId(rs.getInt("id"));
                    room.setName(rs.getString("name"));
                    room.setStatus(rs.getString("status"));
                    rooms.add(room);
                }
            }
        } catch (Exception e) {
            System.err.println("페이징처리 실패: " + e.getMessage());
            e.printStackTrace();
        }

        return rooms;
    }
  1. controller에서 위에 9가지 값 구하고 + offset 계산
    -> offset 계산 : (한 페이지당 표시할 글 개수 구하고 현재페이지 -1) * (한 페이지당 표시할 글 개수)
  int postPage = 10; // 페이지당 방 수
  int offset = (currentPage - 1) * postPage; // (3-1)*10 = 20
  1. model에서 paging을 위한 변수들 getter / setter선언 후 controller에서 해당 변수 set 해주기 (총 개수와 한 페이지당 출력할 개수 빼고 나머지 다)
-paging 
@Getter
@Setter
public class Paging {
    private int currentpage; // 현재 페이지
    private int pageNum;     // 페이지 네비게이션 수
    private boolean start;   // 이전 버튼 필요 여부
    private boolean end;     // 다음 버튼 필요 여부
    private int endPage;     // 마지막 페이지 번호
    private int startPage;   // 시작 페이지 번호
    private int totalPages;  // 총 페이지 수
}

- controller
Paging paging = new Paging();
paging.setCurrentpage(currentPage);
paging.setPageNum(pageNum);
paging.setStart(prev);
paging.setEnd(next);
paging.setStartPage(startPage);
paging.setEndPage(endPage);
paging.setTotalPages(totalPages);
  1. controller에서 DAO에 요청해 딱 필요한 방만 가져오기
    -> dao객체 생성 후 dao에 있는 1번에서 구현한 메소드를 list형식으로 가져오기
	RoomDAO dao = new RoomDAO();
	List<Room> dbRooms = dao.getRoomsByPage(offset, postPage);
  1. controller에서 실제 출력할 목록 구성 -> 메서드로 빼도 됨(권장)
    List<Room> displayRooms = new ArrayList<>();
    for (Room dbRoom : dbRooms) {
        Room memoryRoom = findRoomById(dbRoom.getId());
        if (memoryRoom != null) {
            displayRooms.add(memoryRoom); // 메모리의 최신 상태 사용
        } else {
            dbRoom.setPlayers(new ArrayList<>());
            displayRooms.add(dbRoom);
            roomSet.add(dbRoom); // 메모리에 없는 방은 등록해줌
        }
    }
  1. request.setAttribute()해서 JSP로 넘기기 -> controller에서 실제 출력할 목록 list에 담은 값(화면에 보여줄 방 목록)
    request.setAttribute("paging", paging); -> 페이징 정보
    (paging 정보를 JSP에서 쓰려면 반드시 setAttribute("paging", paging)을 호출) =>paging은 아까 model에서 paging을 위한 변수들 getter / setter선언한 것
	request.setAttribute("roomList", displayRooms);

- frontend

--> jsp로 구현
<!-- 페이지 네비게이션 -->
<c:if test="${paging.totalPages > 1}">
    <div class="pagination">
        <!-- 이전 페이지 링크 -->
        <c:if test="${paging.currentpage > 1}">
            <a href="lobby?page=${paging.currentpage - 1}" class="page-link">« 이전</a>
        </c:if>

        <!-- 페이지 번호 목록 -->
        <c:forEach var="i" begin="${paging.startPage}" end="${paging.endPage}">
            <c:choose>
                <c:when test="${i == paging.currentpage}">
                    <span class="current">${i}</span>
                </c:when>
                <c:otherwise>
                    <a href="lobby?page=${i}">${i}</a>
                </c:otherwise>
            </c:choose>
        </c:forEach>

        <!-- 다음 페이지 링크 -->
        <c:if test="${paging.currentpage < paging.totalPages}">
            <a href="lobby?page=${paging.currentpage + 1}" class="page-link">다음 »</a>
        </c:if>
    </div>
</c:if>
--> javascript로 이벤트
// 페이지 링크 클릭 이벤트 바인딩
function attachPaginationListeners() {
  const pageLinks = document.querySelectorAll('.pagination-link');
  pageLinks.forEach(link => {
    link.addEventListener('click', function (event) {
      event.preventDefault();
      const page = parseInt(this.dataset.page);
      if (!isNaN(page)) {
        currentPage = page;
        refreshRoomList(currentPage);
      }
    });
  });
}

0개의 댓글