프로젝트 중 게시판에서 DB 에 저장되어 있는 "날씨", "수정일" 기준으로 입력받은 파라미터 값과 비교하여 조회하고자한다.
이때, null 값이 들어와도 되도록 한다.
@PostMapping("/todoList")
public ResponseEntity<Page<TodoResponse>> getTodos(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestBody(required = false) TodoListRequest request,
@RequestParam(value = "searchStart", required = false, defaultValue = "19990101") @DateTimeFormat(pattern="yyyyMMdd") LocalDate searchStart,
@RequestParam(value = "searchEnd", required = false, defaultValue = "99991231") @DateTimeFormat(pattern="yyyyMMdd") LocalDate searchEnd
) {
return ResponseEntity.ok(todoService.getTodos(page, size, request, searchStart, searchEnd));
}
조회하고자 하는 기간을 입력 받을 때, default 값을 설정하여 입력 받지 않아도 기간이 설정되도록 구현했다.
들어온 파라미터 값들로 서비스 로직은 아래와 같이 진행된다.
public Page<TodoResponse> getTodos(int page, int size, TodoListRequest request, LocalDate searchStart, LocalDate searchEnd) {
Pageable pageable = PageRequest.of(page - 1, size);
String weather = request.getWeather();
LocalDateTime searchStartDate = searchStart.atStartOfDay();
LocalDateTime searchEndDate = searchEnd.atStartOfDay();
Page<Todo> todos;
if (weather.equals("null")) {
todos = todoRepository.findAllTodo(pageable, searchStartDate, searchEndDate);
} else {
todos = todoRepository.findAllTodo(pageable, weather, searchStartDate, searchEndDate);
}
return todos.map(todo -> new TodoResponse(
todo.getId(),
todo.getTitle(),
todo.getContents(),
todo.getWeather(),
new UserResponse(todo.getUser().getId(), todo.getUser().getEmail(), todo.getUser().getNickname()),
todo.getCreatedAt(),
todo.getModifiedAt()
));
}
프론트단에서 들어온 값이 존재하지 않을 땐, null 값으로 받는단 가정을 하고 이렇게 코드를 작성했지만 추후에 유지보수까지 생각한다면 꽤나 복잡해진 코드다. 그리고 파라미터 값들의 존재 유무에 따라 쿼리문을 추가로 생성해야 한다.
@Query("SELECT t FROM Todo t WHERE t.createdAt BETWEEN :searchStart AND :searchEnd")
Page<Todo> findAllTodo(Pageable pageable, LocalDateTime searchStart, LocalDateTime searchEnd);
@Query("SELECT t FROM Todo t WHERE t.weather = :weather AND t.createdAt BETWEEN :searchStart AND :searchEnd")
Page<Todo> findAllTodo(Pageable pageable, String weather, LocalDateTime searchStart, LocalDateTime searchEnd);
그래서 이렇게 쿼리문을 사용하게 되었다.
@GetMapping("/todoList")
public ResponseEntity<Page<TodoResponse>> getTodos(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String weather,
@RequestParam(required = false) LocalDate searchStartAt,
@RequestParam(required = false) LocalDate searchEndAt
) {
return ResponseEntity.ok(todoService.getTodos(page, size, weather, searchStartAt, searchEndAt));
}
일관되게 모든 정보를 Param 으로 받으며, 받고자 하는 정보에 default 값을 설정하지 않았다.
public Page<TodoResponse> getTodos(int page, int size, String weather, LocalDate searchStartAt, LocalDate searchEndAt) {
Pageable pageable = PageRequest.of(page - 1, size);
LocalDateTime searchStartDate = searchStartAt.atTime(LocalTime.MIN);
LocalDateTime searchEndDate = searchEndAt.atTime(LocalTime.MAX);
Page<Todo> todos = todoRepository.searchTodos(pageable, weather, searchStartDate, searchEndDate);
return todos.map(todo -> new TodoResponse(
todo.getId(),
todo.getTitle(),
todo.getContents(),
todo.getWeather(),
new UserResponse(todo.getUser().getId(), todo.getUser().getEmail(), todo.getUser().getNickname()),
todo.getCreatedAt(),
todo.getModifiedAt()
));
}
들어온 일정 값은 LocalDate 로 받기 때문에 이를 LocaldateTime 으로 형변환 하는 과정을 진행한다. 이 때, 시작일은 LocalTime.MIN 으로 하루의 시작으로 초기화 했고, 종료일은 LocalTime.MAX 로 하루의 끝으로 초기화하여 기간 내 모든 값이 나오도록 했다.
@Query("""
SELECT t FROM Todo t
LEFT JOIN FETCH t.user u
WHERE (:weather IS NULL OR t.weather = :weather)
AND (:searchStartDate IS NULL OR t.modifiedAt >= :searchStartDate)
AND (:searchEndDate IS NULL OR t.modifiedAt <= :searchEndDate)
ORDER BY t.modifiedAt DESC
"""
)
Page<Todo> searchTodos(Pageable pageable, String weather, LocalDateTime searchStartDate, LocalDateTime searchEndDate);
<여기서 처음 알게된 부분>
IS NULL로 설정하여 값이 들어오지 않으면 조건을 무시하고 값이 들어온다면 해당 값으로 조건을 검색하는 로직이다.
이렇게 JPQL 을 사용하니 보다 편리하게 쿼리문을 작성할 수 있었다.
다만, 쿼리문 중 오류가 발생하면 런타임시 오류를 확인해야 한다는 단점이 존재했다.
QueryDSL 로 바꿔보자..!