v7
★ MILESTONE · BOARD

페이징 게시판

대용량 대비

학습 목표

  • 페이징의 필요성을 직접 체감
  • LIMIT 시작행, 개수 로 SQL 페이징 (MySQL)
  • 스키마는 그대로 — SQL 만 바뀐다
  • 페이지 번호 UI 구현

⚠️ 글이 100 개가 되면

v6 의 한계

v6 는 SELECT * FROM myboard ORDER BY num DESC 로 모든 글을 한 번에 가져옴.

  • 글 100 개 → 화면이 아래로 끝없이
  • 글 1000 개 → DB 와 네트워크 부담
  • 글 10000 개 → 응답 시간 폭발

실험 — 100 개 INSERT 후 list 진입. 직접 부하 체감.

🛠️ 페이징 — 한 페이지에 N 개씩

LIMIT 시작행, 개수 (MySQL)

SQL 의 LIMIT startRow, pageSize 로 일부분만 가져옴. 스키마 변경 없음 — 쿼리만 바뀌는 단계.

페이지 1 (글 1~10) : LIMIT 0, 10 페이지 2 (글 11~20) : LIMIT 10, 10 페이지 3 (글 21~30) : LIMIT 20, 10 ... 페이지 N : LIMIT (N-1)*10, 10

① Mapper — 페이지 단위 조회


<select id="selectListPaging" parameterType="map"
        resultType="com.smhrd.domain.Board">
    SELECT num, title, writer, content, created_at
    FROM myboard
    ORDER BY num DESC
    LIMIT #{startRow}, #{pageSize}
</select>

<select id="count" resultType="int">
    SELECT COUNT(*) FROM myboard
</select>

v6 의 selectList 는 그대로 두고 selectListPaging 을 신설. 「예전 화면도 살아있고 새 화면이 추가된」 형태.

② Paging 보조 클래스 (도메인 아님)


package com.smhrd.domain;

// 화면 페이징 계산 전용 — DB 와 1:1 매핑되는 도메인이 아님
public class Paging {
    private int pageNo;       // 현재 페이지 (1부터)
    private int pageSize;     // 한 페이지 행 수 (예: 10)
    private int totalCount;   // 전체 글 수

    public Paging(int pageNo, int pageSize, int totalCount) {
        this.pageNo = pageNo < 1 ? 1 : pageNo;
        this.pageSize = pageSize;
        this.totalCount = totalCount;
    }
    public int getStartRow() { return (pageNo - 1) * pageSize; }
    public int getTotalPages() {
        return (int) Math.ceil((double) totalCount / pageSize);
    }
    public int getPageNo()     { return pageNo; }
    public int getPageSize()   { return pageSize; }
    public int getTotalCount() { return totalCount; }
}

③ Service — 페이징 처리


@Service
public class BoardService {
    @Autowired private BoardMapper mapper;

    private static final int PAGE_SIZE = 10;

    public List<Board> selectListPaging(Paging paging) {
        Map<String, Integer> param = new HashMap<>();
        param.put("startRow", paging.getStartRow());
        param.put("pageSize", paging.getPageSize());
        return mapper.selectListPaging(param);
    }

    public Paging buildPaging(int pageNo) {
        int total = mapper.count();
        return new Paging(pageNo, PAGE_SIZE, total);
    }
}

④ Controller


@GetMapping("/board/list")
public String list(@RequestParam(defaultValue = "1") int page,
                    Model model) {
    Paging paging = service.buildPaging(page);
    List<Board> boards = service.selectListPaging(paging);

    model.addAttribute("boards", boards);
    model.addAttribute("paging", paging);
    return "board/list";
}

⑤ JSP — 목록과 이전/다음


<table>
    <c:forEach var="b" items="${boards}">
        <tr><td>${b.num}</td><td>${b.title}</td><td>${b.writer}</td></tr>
    </c:forEach>
</table>

<div class="pagination">
    <c:if test="${paging.pageNo > 1}">
        <a href="?page=${paging.pageNo - 1}">이전</a>
    </c:if>

    <c:if test="${paging.pageNo < paging.totalPages}">
        <a href="?page=${paging.pageNo + 1}">다음</a>
    </c:if>
</div>

⑤ JSP — 페이지 번호 출력


<c:forEach begin="1" end="${paging.totalPages}" var="p">
    <c:choose>
        <c:when test="${p == paging.pageNo}">
            <strong>${p}</strong>
        </c:when>
        <c:otherwise>
            <a href="?page=${p}">${p}</a>
        </c:otherwise>
    </c:choose>
</c:forEach>

현재 페이지면 강조, 그 외에는 링크. 한 줄 안에 모든 페이지 숫자가 나열됩니다.

실험 — 부하 직접 보기


-- 더미 데이터 100 개 (MySQL)
INSERT INTO myboard(title, writer, content)
SELECT CONCAT('테스트 글 ', seq), 'tester', '본문'
FROM (
    SELECT @row := @row + 1 AS seq
    FROM (SELECT 0 UNION SELECT 1 UNION SELECT 2) t1,
         (SELECT 0 UNION SELECT 1 UNION SELECT 2) t2,
         (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) t3,
         (SELECT @row := 0) r
) numbers
LIMIT 100;

👉 100 개 INSERT 후 v6 list 와 v7 list 비교 — UI 차이 직접 확인.

흔한 실수

실수증상
OFFSET 계산 오류페이지 점프 시 잘못된 데이터
ORDER BY 누락매 요청마다 다른 순서 (DB 별 다름)
page=0 처리 안 함OFFSET 음수 → 에러
총 페이지 수 잘못마지막 페이지 빈 데이터

🔄 Before / After

v6 끝

전체 글 한 번에. 글 많으면 화면 망가짐.

v7 끝

한 페이지 10 개씩. 페이지 번호로 이동. 글 1 만 개도 부드럽게.

정리

오늘 들고 가는 것

  • SQL: LIMIT #{startRow}, #{pageSize} (MySQL)
  • 총 개수는 별도 COUNT(*) 쿼리
  • Paging 보조 클래스 — 도메인이 아닌 계산용
  • 스키마는 그대로 — myboard 변경 없음
  • JSP c:forEach begin..end 로 페이지 번호 UI