[Spring] 게시판 조회시 여러 기준 추가 로직

정석·2024년 10월 12일

Spring

목록 보기
21/21
post-thumbnail

프로젝트 중 게시판에서 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 로 바꿔보자..!

0개의 댓글