본문으로 건너뛰기
개발 머꼬
개발 노트Java
hohyeon.dev16

Optional을 필드에 저장했다가 생긴 문제

  • #API Design
  • #Engineering Note
  • #Java
  • #Optional

문제 발생

null 체크를 줄이려고 클래스 필드를 Optional<String>으로 선언했는데, 직렬화(JSON 변환)나 JPA 엔티티 매핑에서 예상치 못한 오류가 발생했습니다.

public class User {
    private Optional<String> nickname; // 권장되지 않는 패턴
}

원인 분석

Optional은 JDK 설계자가 명시적으로 "메서드의 반환 타입"으로 쓰도록 설계했습니다. 필드나 생성자/메서드 매개변수로 쓰면 여러 문제가 생깁니다.

  1. OptionalSerializable을 구현하지 않습니다 — 필드에 쓰면 그 클래스 전체의 직렬화가 깨지거나 별도 처리가 필요합니다.
  2. JPA 같은 ORM은 Optional 필드를 기본적으로 매핑하지 못합니다.
  3. Optional 자체도 null일 수 있습니다 — 필드로 쓰면 optionalField == nulloptionalField.isEmpty()라는 두 가지 "없음" 상태가 생겨 오히려 null 체크가 하나 더 늘어납니다.
  4. Optional은 매번 새 객체를 감싸는 wrapper라 필드로 계속 유지하면 불필요한 메모리 오버헤드가 쌓입니다.

해결 방안

  1. 필드는 그냥 nullable한 타입으로 두고(@Nullable 애너테이션으로 의도를 표시), 메서드가 값을 반환할 때만 Optional로 감쌉니다.
public class User {
    private String nickname; // 필드는 그냥 String, null 가능

    public Optional<String> getNickname() {
        return Optional.ofNullable(nickname); // 반환할 때만 Optional
    }
}
  1. 생성자나 setter의 매개변수로도 Optional을 쓰지 않습니다 — 호출하는 쪽에서 Optional.empty()를 넘기는 것보다 오버로드된 메서드나 그냥 null을 받는 게 더 단순합니다.
  2. Optional을 받았으면 .get()으로 바로 꺼내지 말고 .map(), .orElse(), .orElseThrow() 같은 API로 값이 없는 경우를 명시적으로 처리합니다. .get()은 값이 없으면 NoSuchElementException을 던지므로 null 체크 안 하던 실수를 예외 처리 안 하던 실수로 옮긴 것뿐입니다.
  3. 컬렉션 필드는 Optional<List<T>>가 아니라 빈 리스트(Collections.emptyList())로 표현하는 것이 관례입니다 — 컬렉션 자체가 "없음"을 표현할 수 있습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.