[Spring Boot] Query did not return a unique result: 3 results were returned 에러 해결하기

밤새·2024년 11월 21일
0

에러 모음

목록 보기
19/19

Spring Boot로 애플리케이션 개발 중 아래와 같은 에러를 만난 적이 있으신가요?

Query did not return a unique result: 3 results were returned

이 글에서는 이 오류의 원인과 해결 방법에 대해 알아보겠습니다!
특히, 발생한 문제를 LIMIT 1을 활용해 해결한 경험을 공유하겠습니다!


1. 오류 발생 원인

이 오류는 JPA Query가 고유한(Unique) 결과를 반환해야 하는데, 여러 개의 결과를 반환했을 때 발생합니다.

예를 들어, 아래와 같은 상황을 가정해 보겠습니다.

  • 특정 사용자(user)와 파트너(partner) 간의 가장 최근 메시지 1개를 가져오려고 했습니다.
  • 그러나 쿼리가 LIMIT 1 없이 실행되었고, 데이터베이스에는 동일 조건을 만족하는 여러 메시지가 존재했습니다.
  • 그 결과, JPA는 하나의 결과만 반환해야 한다는 규칙을 지키지 못해 예외를 던졌습니다.
@Query("SELECT m FROM ChatMessage m WHERE m.user = :user AND m.partner = :partner ORDER BY m.timestamp DESC")
ChatMessage findTopByUsersOrderByTimestampDesc(@Param("user") User user, @Param("partner") User partner);

위와 같은 JPQL 쿼리가 실행되었을 때, 반환된 결과가 3개 이상이라면 해당 오류가 발생합니다.


2. 에러 해결 방법

2.1 JPQL 쿼리에 LIMIT 1 적용

이 문제를 해결하기 위해 JPQL 또는 Query Method를 수정하여 데이터베이스에서 첫 번째 결과만 가져오도록 설정해야 합니다.

Spring Data JPA는 이를 위해 findTopBy 또는 findFirstBy 메서드 키워드를 제공합니다. 이를 활용하면 LIMIT 1이 자동으로 적용됩니다.

수정된 코드

@Query("SELECT m FROM ChatMessage m WHERE m.user = :user AND m.partner = :partner ORDER BY m.timestamp DESC")
List<ChatMessage> findTopByUsersOrderByTimestampDesc(@Param("user") User user, @Param("partner") User partner);

이후 결과를 가져올 때 다음과 같이 첫 번째 메시지만 반환하도록 처리합니다

List<ChatMessage> lastMessages = chatMessageRepository.findTopByUsersOrderByTimestampDesc(user, partner);
ChatMessage lastMessage = lastMessages.isEmpty() ? null : lastMessages.get(0);

여기서 findTopBy는 결과를 정렬 후, 가장 위에 있는 결과를 가져오는 방식입니다. JPA가 자동으로 LIMIT 1을 쿼리에 추가해줍니다.

2.2 Repository 메서드의 반환 타입 변경

만약 단일 결과만 반환하도록 강제하려면, 메서드의 반환 타입을 Optional<ChatMessage>로 변경하는 것도 하나의 방법입니다.

Optional<ChatMessage> findTopByUserAndPartnerOrderByTimestampDesc(User user, User partner);

위 방식은 다음과 같은 장점이 있습니다

  • Optional을 사용하면 결과가 없을 때 NullPointerException을 방지할 수 있습니다.
  • 단일 결과만 반환하므로 Query did not return a unique result 오류를 근본적으로 방지할 수 있습니다.

사용 예

Optional<ChatMessage> lastMessageOpt = chatMessageRepository.findTopByUserAndPartnerOrderByTimestampDesc(user, partner);
ChatMessage lastMessage = lastMessageOpt.orElse(null);

3. 에러를 예방하기 위한 팁

  1. 비즈니스 로직에서 단일 결과를 예상할 때 명확히 쿼리를 제한하세요.
    • findTopBy 또는 LIMIT 1을 사용하는 것을 권장합니다.
  2. 데이터베이스 상태 점검:
    • 같은 조건을 만족하는 데이터가 중복 저장되지 않도록 설계하세요.
    • 예를 들어, 사용자 간 메시지를 저장하는 경우 user, partner, timestamp를 기준으로 유니크 제약 조건(Unique Constraint)을 추가하는 것도 좋은 방법입니다.
  3. 반환 타입에 주의:
    • 단일 객체를 반환해야 한다면 Optional을 활용하고, 다중 결과를 예상한다면 리스트로 반환하도록 설정하세요.

4. 결론

Spring Boot에서 발생한 "Query did not return a unique result" 오류는 다중 결과를 반환하는 쿼리가 단일 결과로 반환되기를 기대할 때 발생하였습니다. 이 문제를 해결하기 위해 LIMIT 1을 적용하거나, findTopBy 메서드를 활용하여 데이터를 정확히 제한하는 방법을 사용해야 합니다.

여러분도 이와 같은 문제를 경험하셨다면, 위의 방법을 참고해보시기 바랍니다!! 궁금한 점은 댓글로 남겨주시면 답변 드리겠습니다. 😊

profile
프로젝트를 통해 배운 개념이나 겪은 문제점들을 정리하고, 회고록을 작성하며 성장해나가는 곳입니다 😊

0개의 댓글