Java 8 Optional 실전 사용기
배경
회사 프로젝트가 Java 7에서 8로 마이그레이션되면서 Optional을 사용할 수 있게 되었다. 기존 코드베이스에서 NPE가 자주 발생하는 부분들을 리팩토링하면서 학습한 내용을 정리한다.
적용 사례
Before
public User findUser(Long id) {
User user = userRepository.findById(id);
if (user == null) {
throw new UserNotFoundException();
}
return user;
}
After
public Optional<User> findUser(Long id) {
return Optional.ofNullable(userRepository.findById(id));
}
// 사용처
User user = findUser(id)
.orElseThrow(() -> new UserNotFoundException());
주의할 점
1. Optional을 필드로 사용하지 않기
Optional은 직렬화를 지원하지 않는다. 엔티티나 DTO의 필드로 사용하면 안 된다.
2. orElse vs orElseGet
// orElse는 항상 실행됨
user.orElse(createDefaultUser()); // 비효율
// orElseGet은 null일 때만 실행
user.orElseGet(() -> createDefaultUser()); // 권장
3. isPresent().get() 안티패턴
// Bad
if (optional.isPresent()) {
return optional.get();
}
// Good
return optional.orElse(defaultValue);
체이닝의 편리함
기존에 null 체크로 중첩된 코드를 깔끔하게 개선할 수 있었다.
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
결론
Optional을 도입하면서 NPE가 30% 정도 감소했다. 다만 팀원들이 익숙해지는 데 시간이 필요했고, 기존 코드와 혼재되어 일관성을 유지하는 게 과제다. 점진적으로 핵심 도메인부터 적용해나갈 계획이다.