Spring Data JPA로 개발을 진행하다보면 JpaRepository의 쿼리 메소드를 자주 사용하게 됩니다. 하지만 예를들어 findById() 쿼리 메소드를 사용한다면 Optional 을 반환하게 되는데 궁금증이 생겨 Optional 에 대해 살펴보았습니다.
JPA의 findBy 쿼리 메소드는 일반적으로 단일 결과를 조회하는 메소드입니다. 그렇기 때문에 결과가 없는 경우, 메소드가 null을 반환하면 NullPointerException이 발생할 수 있습니다. 이러한 예외를 방지하고 클라이언트 코드에서 명시적으로 처리하고자 Optional을 사용하는 것입니다.
아래에서 Optional에 대해 상세히 말씀드리겠습니다.
Optional 클래스는 Java 8부터 도입된 클래스로, 값이 존재할 수도 있고 없을 수도 있는 상황에서 null을 대체하는 데 사용됩니다.
Optional 클래스는 null을 대체하고 NullPointerException을 방지하는 등의 장점을 제공하지만, 모든 상황에서 강제적으로 사용해야 하는 것은 아닙니다. null이 예상되지 않는 상황에서는 null을 사용하는 것이 코드의 가독성을 높일 수 있습니다. Optional은 null 처리가 필요한 경우에 유용하게 사용될 수 있습니다.
null을 반환 값으로 사용하는 것이 위험하기 때문에 Optional이 생겼습니다. 그렇기 때문에 Optional 변수에 null을 할당하는 것은 Optional의 취지와 맞지 않습니다.
Optional<User> findById(Long id) {
if (result == 0) {
/*
**Optional 은 내부 값을 null 로 초기화한
**싱글톤 객체를 Optional.empty() 메소드를 통해 제공
*/
return Optional.empty();
}
}
Optional을 사용하면 그 안의 값은 get() 메소드를 통해 접근 할 수 있습니다. 하지만 빈 Optional 객체에 get() 메소드를 호출한다면 NoSuchElementException이 발생하기 때문에 빈 값에 대한 처리를 해주는 것이 중요하다.
먼저 isPresent() 메서드를 사용하여 Optional 객체에 값이 존재하는지 확인할 수 있습니다. 값이 존재하면 true를 반환하고, 값이 없으면 false를 반환합니다.
Optional<User> optionalUser = findById(1);
if (optionalUser.isPresent()) {
return optionalUser.get();
} else {
throw new NoSuchElementException();
}
하지만 orElseThrow()를 사용하다면 위의 코드보다 더욱 간단하고 가독성이 좋게 코드를 작성할 수 있습니다.
orElseThrow() 메소드는 Optional 객체에 값이 존재하지 않는 경우, 원하는 예외를 던지도록 할 수 있는 Optional의 메소드입니다. 이를 통해 값이 존재하지 않는 경우에 예외를 발생시킬 수 있습니다.
User user = findById(1)
.orElseThrow(() -> new MemberNotFoundException("user not found id : " + 1));
Optional은 반환 타입을 위해 설계된 타입이기에 클래스의 필드로 선언하거나, 메소드의 인자로 사용하고 컬렉션의 원소로 사용하는 것은 Optional의 도입 취지에 반하는 행위입니다.
아래는 잘못 사용된 Optional의 예시입니다.
// 필드값
public class User {
private Optional<String> name;
}
// 인자값
void addCount(Optional<User> user, int count) {
user.ifPresent(user -> user.addCount(count));
}
// 컬렉션의 요소
Map<String, Optional<String>> foods = new HashMap<>();
sports.put("10", Optional.of("pizza"));
sports.put("11", Optional.ofNullable(otherFoods));
String pizza = foods.get("10").orElse("pizza");
String unknown = foods.get("11").orElse("");
피드백 및 개선점은 댓글을 통해 알려주세요😊