Welcome! Everything is fine.

queryForObject() vs query() 본문

TIL

queryForObject() vs query()

개발곰발 2025. 2. 3. 23:36
728x90

예외 처리를 구현하는 도중, 원하는 곳에서 예외가 발생하지 않고 엉뚱한 곳에서 발생해서 그 원인을 찾아보았다.

 

일정을 수정하는 부분 중 findScheduleByIdWithPassword()에서 내가 설정한 ScheduleNotFoundException이 아니라 EmptyResultDataAccessException이 났다.

@Transactional
@Override
public ScheduleResponseDto updateSchedule(Long scheduleId, ScheduleRequestDto dto) {
    Schedule current = scheduleRepository.findScheduleByIdWithPassword(scheduleId);

    if (current == null) {
        throw new ScheduleNotFoundException("수정할 일정이 존재하지 않습니다.");
    }

	// 중략..

}

 

해당 메서드가 구현되어있는 Repository로 가서 살펴보니, 다음과 같이 queryForObject()를 사용하고 있는 것이 문제였다. queryForObject()는 결과가 없으면 null을 반환하는 게 아니라 예외를 던지기 때문에, Service에서 null 체크를 할 수 없던 것이었다!

@Override
public Schedule findScheduleByIdWithPassword(Long scheduleId) {
    return jdbcTemplate.queryForObject(
            "SELECT * FROM schedules WHERE scheduleId = ?",
            scheduleWithPasswordRowMapper(),
            scheduleId
    );
}

 

따라서 queryForObject() 대신 query()를 사용하여 예외를 발생시키지 않고, 결과가 없으면 null을 반환하도록 수정했다. 그리고 Service에서 null 체크 후 사용자 정의 예외인 ScheduleNotFoundException을 던지는 것에 성공했다.

@Override
public Schedule findScheduleByIdWithPassword(Long scheduleId) {
    String query = "SELECT * FROM schedules WHERE scheduleId = ? AND is_deleted = FALSE";
    List<Schedule> schedules = jdbcTemplate.query(query, scheduleWithPasswordRowMapper(), scheduleId);
    return schedules.isEmpty() ? null : schedules.get(0);
}

 

Postman에서도 내가 원하는 대 상태코드와 메세지가 출력되는 것을 볼 수 있다.

 

✔️ 정리

  • queryForObject(): 단일 객체를 반환하는 메서드
    • 결과가 반드시 1개일 거라고 가정할 때 사용해야 하는 메서드
    • 조회된 결과가 0개면 EmptyResultDataAccessException 발생
    • 조회된 결과가 2개 이상이면 IncorrectResultSizeDataAccessException 발생
  • query(): 여러 개의 결과를 처리하는 메서드
    • 조회된 결과가 1개 이상이면 리스트로 반환
    • queryForObject()와 다르게 결과가 없을 때 예외를 던지지 않음

즉, "조회된 결과가 없을 수도 있다"면 query()를 쓰고, "반드시 1개가 나와야 한다"면 queryForObject()를 쓰자!