개발/TIL

Java의 Optional<T>로 안전하게 null 데이터 처리하기

ebang 2025. 2. 20. 23:32

Java의 Optional<T>은 null을 직접 다루지 않고 안전하게 값(객체)을 다룰 수 있도록 감싸는 컨테이너이다.

 

기본적으로 null을 방지하는 방법은 javax validation, spring validation, lombok 등에서 제공하는 어노테이션을 활용하는 방법을 통해 null이 아니어야 하는 데이터를 사전 방어 하는 방식이 있다. 

 

이번에 알아보는 방법은, 그런 경우는 다 막더라도 null일 수 있는 데이터에 대해 어떻게 처리할 것인가, 이다. 

 

JAVA SE 8부터 등장한 Optional 은, null이거나 , null이 아닐 수 있는 데이터를 안전하게 감싸고 있는 클래스이며 다양한 api를 제공한다.


1. Optional 을 사용하지 않고 get() 을 사용하는 경우. (비추천)

- 값이 없을 때 NoSuchElementException 예외가 발생한다. 

Optional<String> optionalValue = Optional.of("Hello");
String value = optionalValue.get(); // "Hello"

 

Optional<String> emptyOptional = Optional.empty();
String value = emptyOptional.get(); //  NoSuchElementException 발생.

 


2. orElse(defaultValue) 사용 (기본값 제공)

Optional<String> optionalValue = Optional.empty();
String value = optionalValue.orElse("Default Value"); // "Default Value"

 


3. orElseGet(() -> defaultValue) 사용 (람다 표현식 활용)

Optional<String> optionalValue = Optional.empty();
String value = optionalValue.orElseGet(() -> "Dynamic Default Value"); // "Dynamic Default Value"

orElse()와 비슷하지만, 기본값을 동적으로 생성할 수 있다. 

Optional 이 비어있을 때만 람다 표현식이 실행된다.  (orElse 와 다른 점. )

 

 

String expensiveOperation() {
    System.out.println("Expensive operation executed!");
    return "Computed Value";
}

Optional<String> optionalValue = Optional.empty();
String value = optionalValue.orElse(expensiveOperation()); // 🚨 항상 실행됨!
String value2 = optionalValue.orElseGet(() -> expensiveOperation()); // ✅ Optional이 비어 있을 때만 실행

 

값이 반드시 존재해야하는 경우 사용하기 유용하다. 


4. orElseThrow(() -> new Exception()) 사용 (예외 던지기)

java
CopyEdit
Optional<String> optionalValue = Optional.empty();
String value = optionalValue.orElseThrow(() -> new IllegalArgumentException("값이 없습니다!"));

값이 없으면 Default value 를 제공하기 때문에 안전한 처리가 가능하다.  

 

 

 5. ifPresent(Consumer<T>) 사용 (값이 있을 때만 실행)

Optional<String> optionalValue = Optional.of("Hello");

optionalValue.ifPresent(value -> System.out.println("값: " + value)); // "값: Hello"

값이 있을 때만 실행해서, null 체크해서 사용할 때 유용하다. 

 


6. map() 사용 (값 변환)

Optional<String> optionalValue = Optional.of("hello");

Optional<String> upperCaseValue = optionalValue.map(String::toUpperCase);
System.out.println(upperCaseValue.orElse("값 없음")); // "HELLO"

값이 없으면 Optional.empty() 를 반환한다. 

연이어서 함수를 사용하려는 경우, empty면 실행하지 않기 때문에 null 여부에 따라 값을 꺼낼 때 사용하기도 유용하다. 

 

Optional<User> user = Optional.of(new User("Alice"));

Optional<String> userName = user.map(User::getName);
System.out.println(userName.orElse("Unknown")); // "Alice"

Optional<User> user = Optional.empty();
Optional<String> name = user.map(User::getName); //Optional.empty 반환

7. flatMap() 사용 (Optional 중첩 방지)

Optional<Optional<String>> nestedOptional = Optional.of(Optional.of("Hello"));
Optional<String> flattened = nestedOptional.flatMap(value -> value);
System.out.println(flattened.orElse("값 없음")); // "Hello"

Optional을 반환하는 메서드 사용 시 다시 꺼낼 때 유용하다. 

map 과 다른 점은, Optional의 중첩을 제거한다는 점이다. 

 

Optional<User> user = Optional.of(new User("Alice"));
Optional<String> email = user.flatMap(User::getEmail); // `Optional<String>` 반환

 

관련 공식문서 는 여기에 있다. 

 

Optional (Java Platform SE 8 )

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as orEl

docs.oracle.com

 

다음 글에서는 null을 사전 방지하는 법에 대해서도 알아보겠다. (@NotNull, @NonNull 등)