Java Spring에서 Service 계층 개발 중 잘못 된 표현으로 개발한 경험을 공유하려고한다.
Spring Data JPA는 JpaRepository 상속받아서 Interface로 CRUD를 매우 간단하게 구현할 수 있다.
그 중 기본 제공 메소드인 findById()
는 DB 속에서 인자로 넘긴 PK에 해당하는 객체를 반환하고 반환 타입은 Optional
로 반환한다.
그렇기 때문에 만약 DB에서 PK에 해당하는 객체를 찾지 못할 경우 Optional 객체를 활용하여 orElseThrow()
메소드로 예외처리를 쉽게 할 수 있다.
그리고 나는 이러한 Optional
기능을 활용하면서 여러 포스트 엔티티를 불러오고 싶어서 Repository를 아래와 같이 작성했다.
// Repository
public interface PostRepository extends JpaRepository<PostEntity, Integer> {
// 작성자 UID가 작성한 모든 포스트 불러오기
Optional<List<PostEntity>> findByAuthorId(Integer author_id);
}
// Service
List<PostEntity> result = this.postRepo.findByAuthorId(authorId)
// 찾지 못할 경우 "포스트 없음" 예외 처리
.orElseThrow(()-> new CustomException(POST_NOT_FOUND));
이러면 List<PostEntity>
로 여러 포스트를 불러오면서 Optional 기능이 적용되어 orElseThrow()
를 이용한 예외처리를 쉽게 할 수 있을 줄 알았다.
그 결과 Post 불러오기 기능은 잘 동작하였으나 잘못된 값을 넘겨주었을 때 예외처리는 잘 작동하지 않았다.
예외처리가 동작하면 POST_NOT_FOUND
출력해야하는데 빈 List를 반환했다.
이유가 무엇일지 조사하던 중 Stackoverflow에서 답을 찾았다.
이 분도 나와 같이 리스트를 Optional로 사용하려했던 것 같다. 근데 잘 안됐나보다.
답변으로 위 방식은 말이 안된다고 아래와 같이 써야한다고 한다.
생각해보니 Optional은 null이 발생했을 경우 NPE가 발생하는 것이 아니라 다양한 메소드로 처리할 수 있게 하는 객체이다.
그런데 Optional<List<T>>
형식으로 쓰게 되면 Optional 제네릭 안에 있는 List<T>
는 리스트가 비어도 빈 리스트를 반환하지 null이 절대로 될 수 없다.
고로 Optional<List<T>>
는 절대로 null이 발생할 수 없으니 예외 처리를 할 수 없었던 것이다.
그래서 나도 List를 Optional로 예외처리하는 것은 포기하고 일반 List 객체에서 빈 리스트가 발생할 경우 If 문으로 예외처리하는 것으로 문제를 해결했다.
// Repository
public interface PostRepository extends JpaRepository<PostEntity, Integer> {
// 작성자 UID가 작성한 모든 포스트 불러오기
List<PostEntity> findByAuthorId(Integer author_id);
}
// service
List<PostEntity> posts = this.postRepo.findByAuthorId(authorId);
if (posts == null || posts.isEmpty())
// 찾지 못할 경우 "포스트 없음" 예외 처리
throw new CustomException(POST_NOT_FOUND);
같은 문제에 직면했는데 덕분에 많은 참고가 됐습니다.감사합니다!