JAVA

NullPointerException을 우아하게?? 해결하는 방법, 자바 Optional 완벽 가이드

JihwanGo 2025. 9. 9. 14:05
반응형
"개발자를 괴롭히는 가장 흔한 에러"

 

자바 개발자라면 누구나 한 번쯤 java.lang.NullPointerException(널포인터 예외)이라는 빨간 에러 메시지를 마주하고 좌절해본 경험이 있을 겁니다. if (a != null) 코드를 수도 없이 반복하다가 결국 어딘가에서 널 체크를 빼먹어 프로덕션 서버가 멈추는 악몽을 겪기도 하죠.

자바 8부터 도입된 Optional<T>는 이 끔찍한 널포인터 예외를 우아하고 안전하게 다룰 수 있게 해주는 특별한 컨테이너 클래스입니다. 오늘은 Optional이 정확히 무엇이고, 어떻게 사용해야 하는지 파헤쳐 보겠습니다.

1. Optional, 도대체 넌 누구니?

Optional은 단일 객체를 담을 수 있는 '컨테이너'입니다. 마치 값(T)이 담겨있을 수도 있고, 비어 있을 수도 있는 상자와 같습니다. Optional의 핵심은 "값이 존재하지 않을 수도 있다"는 것을 명시적으로 알려주는 것입니다.

이전에는 메서드가 null을 반환할 수 있다는 사실을 문서(Javadoc)나 개발자의 습관에 의존해야 했지만, Optional을 사용하면 반환 타입 자체만으로 이 사실을 명확히 알 수 있습니다.

// `null`이 반환될 수 있다는 것을 코드로 명시함
public Optional<User> findById(long id) {
    // 로직...
}

 

2. Optional 핵심 메서드 완전 정복

생성 메서드

Optional을 만드는 방법은 크게 두 가지가 있습니다.

  • Optional.of(value): 절대 null이 아닐 때만 사용해야 합니다. 만약 value가 null이면 NullPointerException이 즉시 발생합니다.
  • Optional.ofNullable(value): value가 null일 수도 있고 아닐 수도 있을 때 사용합니다. null이면 빈 Optional을 반환합니다.
// of() - 널이 아니라고 확신할 때
String name = "gemini";
Optional<String> optionalName = Optional.of(name);

// ofNullable() - 널일 가능성이 있을 때
String address = null;
Optional<String> optionalAddress = Optional.ofNullable(address); // 빈 Optional 반환

값 확인 메서드

  • isPresent(): Optional이 값을 가지고 있으면 true, 비어 있으면 false를 반환합니다.
  • isEmpty() (Java 11+): isPresent()의 반대. 비어 있으면 true를 반환합니다.
if (optionalName.isPresent()) {
    // 값이 존재할 때의 로직
}

값 접근 메서드 (주의!)

  • get(): Optional이 가진 값을 반환합니다. 하지만 주의해야 합니다. 만약 Optional이 비어있으면 NoSuchElementException이 발생합니다. isPresent()로 먼저 확인하는 것이 안전합니다.
// 이 코드는 위험합니다.
// optionalAddress.get(); // NoSuchElementException 발생

3. Optional을 활용한 우아한 코드 작성법

orElse(), orElseGet()으로 안전하게 기본값 제공하기

값이 없을 때 기본값을 제공하는 가장 좋은 방법입니다.

  • orElse(defaultValue): 값이 없으면 defaultValue를 반환합니다.
  • orElseGet(supplier): defaultValue를 반환하는 람다식을 인자로 받습니다. 값이 있을 때는 람다식을 실행하지 않아 성능상 이점이 있습니다.
String result1 = optionalAddress.orElse("주소 없음"); // "주소 없음" 반환
String result2 = optionalAddress.orElseGet(() -> "주소 없음"); // 람다식은 실행되지 않음

orElseGet은 기본값을 계산하는 데 비용이 많이 들 때 유용합니다.

ifPresent()로 널 체크 없이 로직 실행하기

- ifPresent()는 Optional에 값이 있을 때만 특정 로직을 실행하는 메서드입니다. 널 체크를 대체하여 코드를 깔끔하게 만듭니다.

// 널 체크 없이, 값이 존재할 때만 문자열 길이를 출력
optionalName.ifPresent(name -> System.out.println(name.length()));

map(), flatMap()으로 함수형 프로그래밍 스타일 적용하기

Optional의 진가는 map()과 flatMap()에서 드러납니다. 중간에 널이 발생할 수 있는 여러 메서드를 깔끔하게 체이닝할 수 있습니다.

// User 객체에서 회사의 이름을 가져오는 상황
public Optional<String> getUserCompanyName(User user) {
    // null 체크 없이 메서드 체이닝
    return Optional.ofNullable(user)
            .map(User::getCompany) // Optional<Company>
            .map(Company::getName); // Optional<String>
}

4. Optional을 써야 하는 경우와 피해야 하는 경우 (현업 팁!)

Optional은 만능이 아닙니다. 잘못 사용하면 오히려 코드가 복잡해질 수 있습니다.

  • 써야 하는 경우:
    • 메서드의 반환 타입으로 사용: 메서드가 값을 반환하지 않을 가능성이 있을 때.
    • 복잡한 메서드 체이닝 과정에서 널 체크를 피하고 싶을 때.
  • 피해야 하는 경우:
    • 메서드의 파라미터로 사용: Optional을 파라미터로 받으면 코드가 복잡해집니다. 차라리 메서드 내부에서 널 체크를 하거나, Objects.requireNonNull()로 초기에 에러를 던지는 것이 좋습니다.
    • 인스턴스 필드(멤버 변수)로 사용: Optional 객체 자체가 직렬화(Serialization)를 지원하지 않거나 오버헤드가 발생할 수 있습니다.
마무리: Optional은 개발자의 안전벨트입니다.

Optional은 단순히 null을 피하는 도구를 넘어, 개발자에게 **'안전하고 명확한 코드 작성'**이라는 중요한 메시지를 던집니다. 이제 널포인터 예외를 두려워하지 마세요. Optional이라는 강력한 도구로 더 우아하고 견고한 자바 코드를 만들어 봐요~~~

반응형