자바 개념 정리 - Optional 클래스

진용완·2025년 5월 22일
0
publc interface UserRepository extends JapRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

    리포지토리에서 JPA를 이용하여 데이터베이스에 접근하는 코드를 작성할 때에, 메서드가 반환하는 데이터의 타입은 Optional<T>로 지정되어있다. Optional이란 어떤 클래스이며 이 경우에 Optional 타입의 데이터를 반환 받는 이유에 대해 정리하기 위하여 이 포스팅을 작성하였다.


Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

    자바에서 공식으로 제공하는 Optional 타입에 대한 설명은 이와 같다. Optional은 반환(return)하고자 하는 값이 존재하지 않는 경우에 그 사실을 명확하게 표기하여 에러를 일으키지 않기 위한 용도로 사용하는 타입이다.

    메서드에서 반환한 값을 참조하려고 했지만 그 값이 존재하지 않는(null) 경우에는 NullPointerException이라는 예외가 발생한다. NullPointerException은 존재하지 않는 값을 참조할 때 발생하는 예외이다. 이 예외가 발생하면 컴파일 에러 문구가 뜨면서 프로그램이 비정상적으로 종료되는 문제가 발생할 수 있다.

    이러한 문제를 예방하려는 목적으로 만들어진 타입이 바로 Optional이다. NullPointerException이 자주 발생하리라 예상되는 메서드의 반환값을 받는 변수의 타입을 Optional로 지정하면 NullPointerException을 좀 더 안정적으로 대응할 수 있도록 한다.

    Optional은 null이 올 수 있는 값을 감싸는(wrapper) 클래스이기 때문에 이러한 대응이 가능하다. Optional 타입으로 반환이 된 객체는 원래 객체의 타입 그대로 반환이 되는 것이 아니라 Optional 타입으로 감싸진 채로 반환이 된다. 반환되는 객체가 존재하지 않는 null일 경우에는 null인 채로 그대로 반환이 되는 것이 아니라 Optional에 감싸진 채로 반환이 된다. null인 채로 반환이 되었을 때 그 사실을 모르고 해당 값을 참조하게 되면 NullPointerException이 발생하는 것이다. 하지만 Optional로 감싸진 null은 Optional에 들어있는 메서드들을 통해 별도의 예외 처리를 할 수 있다.

    Optional을 통해 NullPointerException을 안정적으로 대응하는 예시는 다음과 같다.

@RequiredArgsConstructor
@Service
public class UserDetailService implements UserDetailsService{

    private final UserRepository userRepository;
    
    @Override
    public User loadUserByUsername(String email){
        return userRepository.findByEmail(email).orElseThrow(
        () -> new IlleagalArgumentException("해당 이메일로 가입한 회원이 존재하지 않습니다."));
    }
}

    이 포스팅의 맨 처음에 제시한 리포지토리 UserRepository에 등록된 JPA 메서드 findByEmail()로 데이터베이스에 접근하여 데이터를 찾으려고 시도했지만, 매개변수로 입력받은 이메일 주소로 가입된 회원이 데이터베이스에 존재하지 않을 수도 있다. 그런 경우에 findByEmail()은 null을 반환할 것이다. 하지만 findByEmail()의 반환값의 타입은 User가 아니라 Optional<User>이다. Optional은 정상적인 User 객체가 반환되지 않고 null이 반환되었을 때에 어떻게 처리할지에 대한 메서드를 추가로 작성하지 않으면 IDE에서 빨간 밑줄이 생긴다.

    이 때 작성한 메서드는 orElseThrow()이다. orElseThrow()는 Optional로 감싸진 반환값이 null일 경우에 NullPointerException 대신에 어떤 예외 처리를 할 지에 대해 설정하는 메서드이다. 위의 코드에서는 IlleagalArgumentException 예외 처리를 함과 동시에 "해당 이메일로 가입한 회원이 존재하지 않습니다."라는 메시지 로그를 띄우는 것으로 해결했다.

    orElseThrow()대신에 쓸 수 있는 메서드는 다음과 같다.

    ○ orElse() : 매개변수로 객체를 받는다. 반환값이 null일 경우, null 대신 매개변수에 입력된 객체를 반환한다. 이 메서드의 매개변수에 객체 대신 메서드를 입력할 경우, null값을 반환하는 것과 상관 없이 매개변수로 입력된 메서드가 일단 실행되므로 주의할 것.

    ○ orElseGet() : 매개변수로 메서드를 받는다. 반환값이 null일 경우, null을 반환하는 대신 매개변수에 입력된 메서드가 실행된다. 반환값이 null이 아닐 경우, 매개변수에 입력된 메서드는 아예 실행되지 않는다.


    NullPointerException은 자주 발생하는 예외이자 어디에서 발생했는지 발견하기 어려운 예외이다. Optional타입을 적절하게 사용하면 NullPointerException으로 발생할 피해를 미연에 방지할 수 있는 효과를 얻을 수 있다.


참고자료

https://mangkyu.tistory.com/70
https://mangkyu.tistory.com/203
https://www.elancer.co.kr/blog/detail/265

0개의 댓글