1. 개요
웹 애플리케이션에서 많은 데이터를 한 번에 불러오면 속도 저하와 과부하가 발생할 수 있습니다.
이를 해결하기 위해 페이징(Pagination) 기법을 사용하여 일정 개수의 데이터만 조회하고, 필요할 때 추가로 불러오도록 합니다.
2. 부트스트랩의 그리드 시스템
- 부트스트랩의 그리드 시스템은 화면을 12개의 컬럼으로 분할하여 반응형 웹 페이지를 쉽게 구성할 수 있도록 돕습니다.
- col-sm-8 → 작은 화면에서는 8개의 컬럼을 차지하는 레이아웃
- col-md-6 → 중간 화면에서는 6개의 컬럼을 차지하는 레이아웃
<div class="container">
<div class="row">
<div class="col-sm-8">왼쪽 콘텐츠</div>
<div class="col-sm-4">오른쪽 콘텐츠</div>
</div>
</div>
3. 플렉스박스(Flexbox)와 중앙 정렬
- 플렉스박스(Flexbox)는 요소를 쉽게 정렬하고 배치할 수 있는 CSS 레이아웃 모델입니다.
- d-flex와 justify-content-center를 사용하면 자식 요소를 수평 중앙에 정렬할 수 있습니다.
<div class="d-flex justify-content-center">
<button class="btn btn-primary">중앙 버튼</button>
</div>
4. 페이징(Pagination) 개념
페이징이란?
- 페이징은 대량의 데이터를 작은 단위로 나누어 여러 페이지에 걸쳐 표시하는 기법입니다.
- 한 번에 모든 데이터를 불러오는 것이 아니라, 사용자가 요청한 페이지에 해당하는 데이터만 가져오는 방식입니다.
페이징 처리 방식
페이지 번호 | OFFSET | LIMIT (한 페이지당 개수) |
1 | 0 | 5 |
2 | 5 | 5 |
3 | 10 | 5 |
📌 페이징의 핵심 개념
- LIMIT → 한 페이지에서 가져올 데이터 개수
- OFFSET → 몇 번째 데이터부터 가져올 것인지 지정 ((현재 페이지 - 1) * LIMIT)
5. 페이징 처리 구현 (Spring Boot + MyBatis)
1) HistoryRepository 인터페이스에서 페이징 처리
@Mapper
public interface HistoryRepository {
List<HistoryAccountDTO> findByAccountIdAndTypeOfHistory(
@Param("type") String type,
@Param("accountId") Integer accountId,
@Param("limit") int limit,
@Param("offset") int offset
);
int countHistoryAccountIdAndType(
@Param("type") String type,
@Param("accountId") Integer accountId
);
}
✅ @Param을 사용하여 LIMIT과 OFFSET을 SQL에 전달합니다.
2) MyBatis XML에서 LIMIT & OFFSET 적용
<select id="findByAccountIdAndTypeOfHistory" resultType="com.example.dto.HistoryAccountDTO">
SELECT h.id, h.amount, h.created_at,
COALESCE(wa.number, 'ATM') AS sender,
COALESCE(da.number, 'ATM') AS receiver
FROM history_tb AS h
LEFT JOIN account_tb AS da ON h.d_account_id = da.id
LEFT JOIN account_tb AS wa ON h.w_account_id = wa.id
WHERE h.d_account_id = #{accountId} OR h.w_account_id = #{accountId}
LIMIT #{limit} OFFSET #{offset}
</select>
✅ LIMIT과 OFFSET을 적용하여 필요한 만큼의 데이터만 가져오도록 설정했습니다.
3) 서비스 계층에서 페이징 처리
@Service
public class AccountService {
@Autowired
private HistoryRepository historyRepository;
// 전체 데이터 개수 조회
public int countHistoryByAccountAndType(String type, Integer accountId) {
return historyRepository.countHistoryAccountIdAndType(type, accountId);
}
// 페이징 처리된 거래 내역 조회
public List<HistoryAccountDTO> readHistoryByAccountId(String type, Integer accountId, int page, int size) {
int offset = (page - 1) * size; // (현재 페이지 - 1) * 페이지당 데이터 개수
return historyRepository.findByAccountIdAndTypeOfHistory(type, accountId, size, offset);
}
}
✅ offset = (page - 1) * size로 계산하여 페이지에 맞는 데이터만 가져옵니다.
4) 컨트롤러에서 페이징 처리
@GetMapping("/detail/{accountId}")
public String detailPage(@PathVariable Integer accountId,
@RequestParam(name = "type", required = false) String type,
@RequestParam(name = "page", defaultValue = "1") int page,
@RequestParam(name = "size", defaultValue = "5") int size,
Model model) {
int totalRecords = accountService.countHistoryByAccountAndType(type, accountId);
int totalPages = (int) Math.ceil((double) totalRecords / size);
List<HistoryAccountDTO> historyList = accountService.readHistoryByAccountId(type, accountId, page, size);
model.addAttribute("historyList", historyList);
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", totalPages);
model.addAttribute("type", type);
return "/account/detail";
}
✅ 현재 페이지 데이터 조회 → JSP로 전달
✅ totalPages를 계산하여 JSP에서 페이징 버튼을 만들 준비
5) JSP에서 페이징 버튼 만들기 (Bootstrap)
<ul class="pagination">
<c:if test="${currentPage > 1}">
<li class="page-item">
<a class="page-link" href="?page=${currentPage - 1}&type=${type}">이전</a>
</li>
</c:if>
<c:forEach begin="1" end="${totalPages}" var="i">
<li class="page-item ${i == currentPage ? 'active' : ''}">
<a class="page-link" href="?page=${i}&type=${type}">${i}</a>
</li>
</c:forEach>
<c:if test="${currentPage < totalPages}">
<li class="page-item">
<a class="page-link" href="?page=${currentPage + 1}&type=${type}">다음</a>
</li>
</c:if>
</ul>
✅ 현재 페이지가 1보다 크면 "이전" 버튼을 활성화
✅ 전체 페이지 수(totalPages) 만큼 페이지 번호를 동적으로 생성
✅ 현재 페이지가 totalPages보다 작으면 "다음" 버튼을 활성화
- 페이징이 필요한 이유와 핵심 개념 (LIMIT, OFFSET)을 정리했습니다.
- MyBatis를 활용한 페이징 적용 SQL과 서비스, 컨트롤러 구현을 확인했습니다.
- JSP에서 Bootstrap을 활용한 페이징 UI 구현까지 마무리했습니다.
단계 | 설명 | 코드 예제 |
1. 전체 데이터 개수 조회 | 전체 데이터 개수를 가져와서 총 페이지 수 계산 | countHistoryAccountIdAndType(type, accountId) |
2. 총 페이지 수 계산 | totalPages = ceil(전체 데이터 개수 / 페이지 크기) | int totalPages = (int) Math.ceil((double) totalRecords / size); |
3. 현재 페이지 데이터 조회 | LIMIT과 OFFSET을 적용하여 SQL 실행 | findByAccountIdAndTypeOfHistory(type, accountId, limit, offset) |
4. JSP로 데이터 전달 | 모델에 데이터 추가하여 페이지로 전달 | model.addAttribute("historyList", historyList); |
5. JSP에서 페이징 버튼 생성 | 이전, 다음 버튼과 페이지 번호 동적 생성 | <c:forEach>를 사용한 페이지 번호 반복 출력 |