웹 애플리케이션이나 데이터베이스에서 데이터를 조회할 때, 한 번에 모든 데이터를 불러오면 성능 문제가 발생할 수 있기 때문에 이를 해결하기 위해 페이지 단위로 데이터를 가져오는 방식을 사용한다. 페이지네이션을 이용해 등록된 일정을 페이지 번호와 크기 기준으로 모두 조회하는 기능을 구현했다.
페이지네이션의 방식에는 다음과 같이 두 가지가 있었다. 그 중 가장 많이 사용되는 방식인 오프셋 기반 페이지네이션을 사용했다.
Pageable
객체를 이용하면 더 편리하다고 했지만, 현재 프로젝트에서는 JDBC만 사용 중이기 때문에 직접 LIMIT
과 OFFSET
을 사용하는 방향으로 진행했다. 따라서 다음과 같이 직접 Paging
클래스를 만들어 페이지네이션을 처리했다.
@Getter
@AllArgsConstructor
public class Paging<T> {
private List<T> content;
private int totalPages;
private int totalElements;
private int size;
private int number;
}
기존 코드에서의 List<T>
대신 Paging<T>
을 사용해 페이지네이션 정보를 포함하여 반환받도록 했다. 그리고 기존 코드의 매개변수에 page
와 size
를 추가했다.
@GetMapping
public ResponseEntity<Paging<ScheduleResponseDto>> findSchedules(
@RequestParam(required = false) String writer,
@RequestParam(required = false) String updatedAt,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size
) {
Paging<ScheduleResponseDto> response;
if (writer == null && updatedAt == null) {
response = scheduleService.findAllSchedules(page, size);
} else {
response = scheduleService.findSchedules(writer, updatedAt, page, size);
}
return ResponseEntity.ok(response);
}
Service Layer에서는 Repository를 통해 page
와 size
에 맞는 Schedule
목록과 totalElements
(전체 데이터 개수)와 totalPages
(총 페이지 개수)를 구해 반환한다.
@Override
public Paging<ScheduleResponseDto> findAllSchedules(int page, int size) {
List<ScheduleResponseDto> schedules = scheduleRepository.findAllSchedules(page, size);
int totalElements = scheduleRepository.countSchedules();
int totalPages = (int) Math.ceil((double) totalElements / size);
return new Paging<>(schedules, totalPages, totalElements, size, page);
}
@Override
public Paging<ScheduleResponseDto> findSchedules(String writer, String updatedAt, int page, int size) {
List<ScheduleResponseDto> schedules = scheduleRepository.findSchedules(writer, updatedAt, page, size);
int totalElements = scheduleRepository.countSchedulesByWriter(writer, updatedAt);
int totalPages = (int) Math.ceil((double) totalElements / size);
return new Paging<>(schedules, totalPages, totalElements, size, page);
}
Repository Layer에서는 userId를 기준으로 schedules 테이블과 users 테이블을 조인해 각 사용자와 맞는 일정을 조회하고자 했다.
private String getScheduleWithUserQuery() {
return """
SELECT s.scheduleId, s.task, u.name AS writer, s.createdAt, s.updatedAt
FROM schedules s
JOIN users u ON s.userId = u.userId
""";
}
@Override
public int countSchedules() {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM schedules", Integer.class);
}
@Override
public int countSchedulesByWriter(String writer, String updatedAt) {
List<Object> params = new ArrayList<>();
StringBuilder query = new StringBuilder("""
SELECT COUNT(*)
FROM schedules s
JOIN users u ON s.userId = u.userId
""");
if (writer != null || updatedAt != null) {
query.append(" WHERE");
boolean addAnd = false;
if (writer != null) {
query.append(" u.name = ?");
params.add(writer);
addAnd = true;
}
if (updatedAt != null) {
if (addAnd) query.append(" AND");
query.append(" DATE(s.updatedAt) = ?");
params.add(updatedAt);
}
}
return jdbcTemplate.queryForObject(
query.toString(),
Integer.class,
params.toArray()
);
}
주말 일정 때문에 오늘 벼락치기로 과제를 거의 마무리 했다. 숙련 과제는 빨리 끝내고 나서 리팩토링 해보는 시간을 가지고 싶다.😵