본문 바로가기
Language/Java

[JAVA] Optional<T> Class를 사용하는 이유는? Null과의 전쟁을 끝내는 법

by Papa Martino V 2026. 1. 22.
728x90

Optional&lt;T&gt; Class
Optional<T> Class

 

자바 프로그래머라면 누구나 한 번쯤 NullPointerException(NPE)이라는 불청객 때문에 고생한 경험이 있을 것입니다. 런타임에 갑자기 튀어나와 프로그램의 흐름을 끊어버리는 이 예외를 방지하기 위해, 우리는 습관적으로 if (obj != null)과 같은 방어적인 코드를 남발하곤 했습니다. 이러한 불편함을 우아하게 해결하기 위해 Java 8부터 등장한 것이 바로 Optional<T> 클래스입니다. 본 포스팅에서는 단순히 NPE를 막는 수단을 넘어, 가독성 높은 API를 설계하고 코드의 의도를 명확히 전달하기 위해 왜 Optional을 사용해야 하는지 심도 있게 다루어 보겠습니다.


1. Optional<T>이란 무엇인가?

Optional<T>는 "존재할 수도 있고, 존재하지 않을 수도 있는 객체"를 감싸는 래퍼(Wrapper) 클래스입니다. 데이터가 비어있다면(null) 비어있는 상태를 나타내는 특수한 객체(Optional.empty())를 담고 있고, 데이터가 있다면 해당 값을 감싸고 있습니다. 가장 중요한 설계 목적은 "반환값이 없을 수 있음을 API 사용자에게 명시적으로 알리는 것"입니다. 이는 개발자로 하여금 반환된 결과가 null일 가능성을 인지하고 대응하게 강제함으로써, 런타임 에러를 사전에 차단하는 역할을 합니다.


2. 왜 Optional을 사용해야 하는가? (핵심 장점)

Optional을 사용함으로써 얻는 이득은 단순히 에러 방지에 그치지 않습니다. 소프트웨어 공학적 측면에서 다음과 같은 이점이 있습니다.

  • 명시적인 API 설계: 메서드가 반환하는 타입이 Optional<User>라면, 해당 메서드를 호출하는 사람은 "아, 사용자가 없을 수도 있겠구나"라고 즉시 파악할 수 있습니다.
  • 가독성 향상: 복잡한 중첩 if-null 체크 문법을 제거하고, 선언형 프로그래밍(함수형 인터페이스) 스타일로 간결하게 코드를 짤 수 있습니다.
  • 안전한 기본값 처리: orElseorElseGet을 활용하여 데이터가 없을 때의 동작을 체이닝 방식으로 우아하게 정의할 수 있습니다.

3. Null 체크 방식과 Optional 방식의 비교

기존의 전통적인 방식과 Optional을 활용한 현대적 방식의 차이를 표로 정리해 보았습니다.

구분 기존 Null 체크 방식 (Before) Optional 활용 방식 (After)
의도 파악 실행해보기 전까지 Null 가능성 확인 어려움 반환 타입을 통해 즉시 확인 가능
코드 스타일 명령형 (if-else 문법 중심) 선언형 (함수형 메서드 체이닝)
예외 발생 위험 NPE 발생 확률 높음 (체크 누락 시) API 구조상 NPE 발생 가능성 현격히 낮음
가독성 중첩된 if문으로 인한 코드 가독성 저하 직관적이고 간결한 로직 구현 가능

4. 실무에서의 Sample Example

사용자 아이디로 사용자를 찾아 이름을 반환하는 상황을 예로 들어보겠습니다.

전통적인 방식 (위험한 방식)


public String getUserName(Long id) {
    User user = userRepository.findById(id); // 결과가 null일 수 있음
    if (user != null) {
        return user.getName();
    }
    return "Unknown";
}

Optional을 활용한 현대적 방식 (안전한 방식)


public String getUserName(Long id) {
    return userRepository.findOptionalById(id) // Optional<User> 반환
            .map(User::getName)
            .orElse("Unknown");
}

위 코드에서 볼 수 있듯이 Optional을 사용하면 비즈니스 로직이 한 줄의 흐름으로 읽히게 되어 유지보수가 매우 용이해집니다.


5. 주의사항 (Anti-Pattern)

Optional이 좋다고 해서 모든 곳에 남발해서는 안 됩니다. 성능 저하나 직렬화 문제 등 부작용이 있을 수 있기 때문입니다.

  1. 필드(Field) 타입으로 사용 금지: Optional은 직렬화(Serializable)를 지원하지 않으므로 DTO나 엔티티의 멤버 변수로 사용하지 않는 것이 원칙입니다.
  2. 파라미터로 전달 금지: 메서드의 인자로 Optional을 넘기면, 호출하는 쪽에서 굳이 Optional 객체를 생성해야 하는 번거로움과 비효율이 발생합니다.
  3. 컬렉션 반환 시 사용 금지: List, Set 같은 컬렉션은 비어있음을 표현할 때 Collections.emptyList() 등을 사용하는 것이 훨씬 효율적입니다.

6. 결론

Java Optional<T>는 단순히 Null을 감싸는 도구가 아니라, 개발자 간의 커뮤니케이션 도구입니다. 반환 타입에 Optional을 적용함으로써 "이 데이터는 비어있을 수 있으니 주의해라"라는 강력한 메시지를 전달할 수 있습니다. 올바른 Optional 사용법을 익혀 더욱 견고하고(robust) 깔끔한 코드를 작성해 보시기 바랍니다.


내용 출처:

  • Oracle Java Documentation: java.util.Optional Class
  • Joshua Bloch, "Effective Java 3rd Edition", Item 55
  • Java 8 in Action: Lambdas, Streams, and functional-style programming
728x90