Java 8 Optional 제대로 사용하기

문제 상황

레거시 프로젝트를 Java 8로 마이그레이션하면서 Optional을 적극 도입했다. 하지만 코드 리뷰 중 다음과 같은 패턴을 자주 발견했다.

Optional<User> user = userRepository.findById(id);
if (user.isPresent()) {
    return user.get().getName();
}
return null;

이건 Optional을 쓰는 의미가 없다. null 체크를 isPresent()로 바꾼 것뿐이다.

올바른 사용법

1. map과 orElse 활용

String name = userRepository.findById(id)
    .map(User::getName)
    .orElse("Unknown");

2. orElseThrow로 명확한 예외 처리

User user = userRepository.findById(id)
    .orElseThrow(() -> new UserNotFoundException(id));

3. ifPresent로 side-effect 처리

userRepository.findById(id)
    .ifPresent(user -> emailService.sendWelcome(user.getEmail()));

안티패턴

Optional을 필드로 사용하지 말 것

// 나쁜 예
public class User {
    private Optional<String> middleName;
}

Optional은 직렬화를 지원하지 않고, 메모리 오버헤드가 있다. 필드는 nullable로 두고 getter에서만 Optional로 감싸 반환하는 게 낫다.

Optional.of()와 Optional.ofNullable() 구분

// null이 절대 아닐 때
Optional.of(value)  // null이면 NPE 발생

// null 가능성이 있을 때
Optional.ofNullable(value)

결론

Optional은 반환 타입으로 사용할 때 가장 효과적이다. null을 반환하는 대신 Optional.empty()를 반환하면 호출자가 값의 부재를 명시적으로 처리하게 된다. 팀 컨벤션에 이 내용을 추가했다.