우당탕탕

Java Optional 제대로 쓰는 법과 흔한 안티패턴 사례 비교 본문

언어/Java

Java Optional 제대로 쓰는 법과 흔한 안티패턴 사례 비교

모찌모찝 2026. 8. 7. 10:36

제가 Java Optional을 처음 접했을 때, 굉장히 편리하다고 생각했는데 막상 프로젝트에 적용하면서 왜 이렇게 복잡하고 헷갈리는지 한참 삽질했어요. 그래서 이번에 제대로 Optional을 쓰면서 겪은 문제와 그때 참고했던 다른 사례들을 비교해봤습니다.

이 글에서는 Java Optional을 어떤 상황에 쓰는 게 맞는지, 흔히 하는 안티패턴은 무엇인지, 그리고 타사례와 비교해 보며 어떻게 하면 깔끔하고 안전하게 코드를 작성할 수 있을지 차근차근 알려드릴게요.

개발 환경 / 버전 정보

프로젝트는 Java 17 기반이고 Spring Boot 3.1을 사용하고 있습니다. Optional은 Java 8부터 정식 지원됐지만, 최신 버전에서 쓰는 팁 위주로 설명드릴게요.

Java Optional 제대로 쓰는 법과 안티패턴 관련 이미지

Java Optional 제대로 쓰는 법과 안티패턴 관련 정보

Optional 제대로 써야 하는 이유와 기본 개념

사실 이 부분이 Optional에서 가장 헷갈릴 수 있는데요, Optional의 핵심은 NullPointerException을 줄이고 명시적으로 값의 존재 여부를 표현하는 것입니다. 근데 저도 처음엔 이걸 "그냥 null 대신 감싸면 되는구나" 정도로만 이해해서, 어디에 어떻게 써야 할지 감이 안 왔어요.

Optional은 반환 타입으로 주로 씁니다. 메서드가 값을 반환할 때 '값이 없으면 null' 대신 Optional을 씌워주면, 호출하는 쪽에서 안전하게 처리할 수 있거든요. 하지만 필드 타입이나 파라미터 타입에 쓰는 건 권장하지 않는 방법이에요. 그렇지 않으면 오히려 코드가 더 복잡해질 수 있거든요.

제가 사용한 방법 vs 다른 사람이 쓴 안티패턴 비교

제가 처음 Optional을 적용했을 때, 아래와 같은 코드를 짰는데요. 문제는 Optional을 메서드 파라미터에 쓰고, 내부에서 또 불필요하게 null 체크까지 겹쳤다는 점이었어요.

// 저의 초기 코드 예시: Optional을 파라미터로 받음
public void processData(Optional optName) {
    if (optName != null && optName.isPresent()) {
        System.out.println("Name: " + optName.get());
    } else {
        System.out.println("이름이 없어요.");
    }
}

이 코드는 Optional 파라미터가 null일 수도 있다고 가정하고 체크하는데, 사실 Optional은 내부에서 null이 아닌 값의 유무를 감싸는 것이기 때문에 Optional 그 자체가 null이면 안 됩니다. 즉, Optional을 파라미터로 받는 건 안티패턴이에요.

반면에, 다른 개발자들이 많이 쓰는 안티패턴을 살펴보면 이런 경우가 많더라고요.

  • Optional을 필드로 선언해놓고, 외부에서 null 체크를 전혀 안 함
  • Optional 내부 값을 꺼낼 때 무조건 get()만 호출, 존재 유무 체크 안 함
  • Optional 생성 시 Optional.of(null)처럼 null이 들어갈 수 있는 방식으로 생성

이런 경우는 자칫하면 NPE를 초래하거나 Optional의 장점을 무색하게 만들죠.

Java Optional 제대로 쓰는 법과 안티패턴 직접 정리한 자료

Java Optional 제대로 쓰는 법과 안티패턴 관련 정보

이렇게 하면 됩니다: 제가 정리한 올바른 Optional 활용법

먼저 Optional은 반환 타입으로만 쓰는 게 제일 깔끔해요. 그리고 null 대신 Optional.empty()를 꼭 사용해야 합니다.

public Optional findNameById(Long id) {
    String name = database.queryName(id); // null일 수도 있음
    return Optional.ofNullable(name); // null 허용 시 ofNullable 사용
}

// 호출 측
Optional optName = findNameById(10L);
optName.ifPresent(name -> System.out.println("Name: " + name));

이렇게 하면 호출하는 쪽에서 isPresent()ifPresent() 등을 이용해 명확히 처리할 수 있어요. 저는 orElseThrow()를 써서 값이 없을 때 바로 예외를 던지는 방식도 자주 씁니다.

Optional 필드로 쓰는 건 꼭 피하세요. 자바 공식 문서에서도 Optional은 필드나 파라미터 타입으로 쓰지 말라고 명시되어 있습니다.

제가 겪은 삽질 사례와 타 개발자들 경험 비교

한번은 Optional을 메서드 파라미터로 받고 내부에 orElse(null)를 써서 또 null 처리를 하는 복합적인 코드를 작성했는데요, 이렇게 하면 Optional의 장점이 사라지는 거였어요. 제가 작성한 예시는 다음과 같습니다.

public void handle(Optional optValue) {
    String value = optValue.orElse(null);
    if (value != null) {
        System.out.println(value);
    } else {
        System.out.println("값 없음");
    }
}

반면에 주변 다른 개발자들은 Optional은 오직 반환 값으로만 쓰고, 이렇게 명확한 API 설계를 권장하더라고요. 그래서 저는 지금은 이렇게 개선했습니다.

public void handle(String value) {
    if (value != null) {
        System.out.println(value);
    } else {
        System.out.println("값 없음");
    }
}

public Optional getValue() {
    return Optional.ofNullable(this.value);
}

결국 Optional은 사용법이 명확해야지, 사용 위치나 의도에 맞지 않으면 오히려 복잡해지고 헷갈려요.

Java Optional 제대로 쓰는 법과 안티패턴 참고 사진

Java Optional 제대로 쓰는 법과 안티패턴 관련 정보

자주 하는 실수와 이런 부분에서 많이 틀립니다

  • Optional 생성 시 null 직접 전달: Optional.of(null)은 바로 NPE, 대신 Optional.ofNullable()을 써야 합니다.
  • 필드 타입으로 Optional 선언: 자바 가이드라인상 비추천, 필요하면 그냥 null 처리하거나 별도 래퍼 클래스 사용.
  • Optional.get()을 바로 호출: 값이 없는 경우 NoSuchElementException 발생하니 꼭 isPresent()ifPresent()로 체크.

심화: 스트림과 Optional 결합해서 더 깔끔하게

그런데 여기서 많이들 헷갈려하는 게 Optional과 스트림을 같이 쓸 때인데요, 저도 처음엔 Optional을 스트림처럼 처리하려다 삽질 많았어요. 예를 들어, Optional 내부 값이 있으면 매핑하고, 없으면 빈 스트림처럼 처리하는 방법이 있습니다.

// Optional을 Stream으로 변환하는 깔끔한 방법
Optional optional = Optional.ofNullable("hello");

List result = optional.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(result); // [HELLO]

이 방식은 Optional을 스트림처럼 다룰 수 있어서, 여러 Optional값을 합쳐서 처리할 때 매우 유용하더라고요.

자주 물어보시는 것들

Q. Optional을 필드로 쓰면 왜 안 되나요?

A. Optional은 API 반환값으로 명확한 의도를 표현하는 것이라, 필드로 쓰면 불필요한 래핑이 계속 발생해 성능 저하와 코드 복잡도 상승을 초래합니다. 그리고 직렬화/역직렬화나 ORM 매핑에서 문제도 생겨요.

Q. Optional 내부 값 꺼낼 때 무조건 get()만 써도 되나요?

A. 절대 안 됩니다. 값이 없으면 NoSuchElementException이 떨어져서요. 반드시 isPresent()ifPresent()를 써서 값 존재 여부를 먼저 체크해야 해요.

Q. null과 Optional, 둘 중 어떤 걸 더 선호하나요?

A. 반환하는 API에서는 Optional을 선호합니다. null이 섞이면 NPE 위험이 커서요. 다만 내부 로직이나 필드에서는 null 체크로 충분한 경우가 많습니다.

정리하자면, Optional은 반환 타입에 명확한 의도를 넣는 용도로 깔끔하게 쓰고, 그 외에는 null 체크를 적절히 병행하는 게 가장 무난합니다. 저도 이런 구조로 바꾸고 나서 코드가 훨씬 읽기 쉽고 안전해졌습니다.

필드 타입으로 쓰는 사례나 파라미터로 쓰는 사례는 자칫 코드 복잡도와 버그를 키우는 안티패턴임을 꼭 기억하세요. 그리고 스트림과 결합하면 Optional을 더 쓸모 있게 활용할 수 있습니다.

Comments