Java Optional과 `orElseThrow`

Kim jisu·2025년 2월 12일
0

 Debugging Note

목록 보기
11/37

코드 실수와 올바른 예외 처리 방법

Java 8부터 도입된 Optional 클래스는 null 관련 오류를 예방하고, 값의 존재 여부를 보다 명확하게 표현할 수 있게 도와줍니다. Optional의 사용 예제와 함께 orElseThrow 메서드를 활용하여 예외 처리를 하는 방법, 그리고 흔히 발생하는 실수에 대해 다뤄보겠습니다.


1. 문제 상황: 두 코드의 차이점

아래 두 코드 예제를 비교해보겠습니다.

작동하는 코드

Product product = productRepository.findById(Math.toIntExact(productId))
                        .orElseThrow(() -> new NullPointerException("해당 상품이 존재하지 않습니다."));
  • 설명:
    • findByIdOptional<Product>를 반환합니다.
    • .orElseThrow(...) 메서드를 사용하여, 만약 Optional 내부에 값이 존재하면 이를 반환하고, 값이 없으면 지정된 예외(여기서는 NullPointerException)를 던집니다.
    • 이렇게 하면 product 변수에는 실제 Product 객체가 안전하게 할당됩니다.

작동하지 않는 코드

try {
    Product product1 = productRepository.findById(Math.toIntExact(folderId));
} catch (Exception e) {
    throw new NullPointerException("해당 상품 존재 않음");
}
  • 설명:
    • 이 코드에서도 findByIdOptional<Product>를 반환합니다.
    • 하지만 반환된 Optional을 직접 Product 타입의 변수에 할당하려고 시도하기 때문에 타입 불일치(컴파일 에러)가 발생합니다.
    • 또한, findById는 값이 없을 경우 예외를 던지지 않고 Optional.empty()를 반환하므로, try-catch 블록으로는 적절한 예외 처리를 할 수 없습니다.

요약:
첫 번째 코드는 Optional을 올바르게 언래핑(unwrapping)하여 실제 객체를 얻는 반면, 두 번째 코드는 Optional을 그대로 객체 변수에 할당하려고 하여 컴파일 단계에서 문제가 발생합니다.


2. orElseThrow 메서드란?

orElseThrowOptional 내부의 값을 안전하게 가져오거나, 값이 존재하지 않을 때 지정된 예외를 발생시키는 메서드입니다.

기본 사용법

Optional<Product> optionalProduct = productRepository.findById(productId);
Product product = optionalProduct.orElseThrow(() -> new NullPointerException("해당 상품이 존재하지 않습니다."));
  • 만약 optionalProduct에 값이 있다면, product 변수에 그 값이 할당됩니다.
  • 값이 없다면, 람다식 내에서 지정한 NullPointerException이 던져집니다.

메서드 시그니처

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
  • 제네릭 타입 T: Optional이 감싸고 있는 실제 값의 타입입니다.
  • 파라미터 Supplier<? extends X> exceptionSupplier: 값이 없을 때 생성할 예외를 제공하는 함수형 인터페이스입니다.
  • 동작 원리:
    • 값이 존재하면: 내부의 값을 그대로 반환합니다.
    • 값이 없으면: exceptionSupplier.get()을 호출해 예외를 생성하고 던집니다.

orElseThrow를 사용할까요?

  1. 명시적인 예외 처리:
    Optional.get()은 값이 없을 경우 NoSuchElementException을 던지지만, orElseThrow를 사용하면 보다 구체적인 예외를 던져서 문제의 원인을 명확하게 할 수 있습니다.

  2. 코드 가독성 향상:
    조건문 없이 한 줄로 값의 존재 여부와 예외 처리를 할 수 있어 코드가 간결해집니다.

  3. 안전성 보장:
    값을 사용하기 전에 반드시 존재 여부를 체크함으로써 런타임의 NullPointerException을 예방할 수 있습니다.

실제 예제

public User getUserById(Long userId) {
    return userRepository.findById(userId)
           .orElseThrow(() -> new UserNotFoundException("User not found with id: " + userId));
}
  • findById 메서드는 Optional<User>를 반환하고, orElseThrow를 사용하여 값이 없을 경우 UserNotFoundException을 발생시킵니다.

3. 결론

Java의 OptionalorElseThrow를 올바르게 활용하면 코드의 안정성과 가독성을 높일 수 있습니다.

  • 핵심 포인트:
    • Optional 언래핑: orElseThrow를 통해 Optional 내부 값을 안전하게 추출합니다.
    • 명시적 예외 처리: 값이 없을 때 보다 구체적인 예외를 발생시켜 문제를 쉽게 추적할 수 있습니다.
    • 컴파일 에러 방지: Optional 자체를 실제 객체에 할당하려는 시도를 방지합니다.
profile
Dreamer

0개의 댓글