본문 바로가기
Dev/Java

Java Modern Syntax : Optional

by DevGyu0511 2026. 1. 10.
반응형

개요

안드로이드 앱이 예기치 않게 종료되는 가장 흔한 원인은 바로 'NPE(NullPointerException)'이다. 특히 서버로부터 데이터를 받아오거나 사용자의 입력을 처리할 때, 값이 존재하지 않는 상황을 적절히 제어하지 못하면 앱은 순식간에 크래시가 발생한다. 오늘은 Java 8부터 도입되어 안드로이드 실무에서도 널리 쓰이는 'Optional'을 통해 널 체크의 지옥에서 벗어나는 방법을 알아본다.


안드로이드 개발과 NPE(NullPointerException)의 악연

안드로이드에서는 다음과 같은 상황에서 널 값이 자주 발생한다.

  • 'Intent'를 통해 전달받은 'Extra' 데이터가 없을 때
  • 'SharedPreferences''DB'에서 조회한 값이 존재하지 않을 때
  • 서버 'API' 응답의 특정 필드가 비어 있을 때 (JSON 파싱 오류 등)

기존에는 'if (data != null)'과 같은 중첩된 조건문으로 이를 방어했으나, 코드가 지저분해지고 실수로 누락할 위험이 컸다. 'Optional'은 이러한 복잡한 조건문 없이도 우아하게 널 값을 처리할 수 있게 도와준다.


Optional 객체의 원형

'Optional''값이 존재할 수도 있고 아닐 수도 있는' 객체를 감싸는 래퍼 클래스이다. 'ofNullable()' 등을 통해 생성된 'Optional' 객체 내부에서 데이터 가공을 담당하는 주요 함수 원형들은 다음과 같다.

public final class Optional<T> {
    // 1. 객체 생성 관련 원형
    public static<T> Optional<T> empty() { ... } // 비어있는 Optional 객체 생성
    public static <T> Optional<T> of(T value) { ... } // null이 아닌 명확한 값을 가진 객체 생성 (null일 경우 NPE 발생)
    public static <T> Optional<T> ofNullable(T value) { ... } // null일 수도 있는 값을 감싸는 객체 생성 (권장)

    // 2. 데이터 가공 및 필터링 (핵심 도구)
    public Optional<T> filter(Predicate<? super T> predicate) { ... } // 조건에 맞으면 유지, 아니면 빈 객체 반환
    public <U> Optional<U> map(Function<? super T, ? extends U> mapper) { ... } // 값을 변환하여 다시 Optional에 담음

    // 3. 값의 존재 여부에 따른 동작 정의
    public boolean isPresent() { ... } // 내부 값이 존재하면 true 반환
    public void ifPresent(Consumer<? super T> action) { ... } // 값이 존재할 때만 실행 (매개변수 이름은 자유롭게 지정 가능)

    // 4. 값 추출 및 대체 관련 원형
    public T get() { ... } // 값이 없으면 예외 발생 (주의)
    public T orElse(T other) { ... } // 값이 없으면 준비된 기본값(other) 반환
}

간단한 사용 예로, 문자열의 길이를 출력할 때 'ifPresent'를 사용하면 다음과 같이 안전하게 처리할 수 있다. 괄호 안의 'str'은 객체 안의 값을 지칭하는 임시 변수명으로, 사용자 편의에 따라 변경 가능하다.

Optional<String> optionalStr = Optional.ofNullable(getTextFromServer());

// str은 optionalStr이 가진 실제 값을 의미하며, s나 value 등으로 자유롭게 명명할 수 있다.
optionalStr.ifPresent(str -> System.out.println("문자열 길이: " + str.length()));

실무 활용 예제

실무에서는 주로 데이터 바인딩이나 'API' 응답 데이터를 가공할 때 'Optional'의 진가가 발휘된다.

1. Intent 데이터 및 기본값 처리

'Intent'로부터 데이터를 가져올 때 'filter'를 사용하여 빈 문자열까지 한 번에 걸러내는 예제이다.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // filter: 추출한 이름이 공백이 아닐 때만 유지, 공백이면 null처럼 취급하여 Guest 반환
    String userName = Optional.ofNullable(getIntent().getStringExtra("user_name"))
                               .filter(name -> !name.trim().isEmpty())
                               .orElse("Guest");

    textViewWelcome.setText(userName + "님 환영합니다!");
}

2. API 응답 데이터 가공 (map 활용)

'map'을 사용하여 객체 체이닝을 안전하게 수행하는 과정이다. 람다식 매개변수의 역할을 명확히 확인해 본다.

public void updateCityUI(User user) {
    // map: 객체 안의 User를 Address로, 다시 Address를 City 문자열로 변환
    String cityName = Optional.ofNullable(user)
                       .map(u -> u.getAddress()) // u: Optional 객체 안의 'User' 객체
                       .map(a -> a.getCity())    // a: 추출된 'Address' 객체
                       .orElse("Unknown City");

    binding.tvCity.setText(cityName);
}
  • 'u': 'Optional' 객체에 담긴 'User' 객체를 의미하며, 이를 통해 주소 정보에 접근한다.
  • 'a': 앞선 단계에서 성공적으로 추출된 'Address' 객체를 의미한다.
  • 각 단계에서 만약 주소('Address')가 null이라면 다음 단계로 넘어가지 않고 즉시 'orElse'에 정의된 기본값을 반환하여 안전을 보장한다.

3. 리스트 데이터 안전하게 처리

서버 리스트 데이터가 'null'일 때 발생할 수 있는 반복문 오류를 방지한다.

public void displayItems(List<Item> serverItems) {
    // 리스트가 null이면 빈 리스트를 반환하여 안전하게 순회
    List<Item> safeList = Optional.ofNullable(serverItems)
                                  .orElse(Collections.emptyList());

    for (Item item : safeList) {
        adapter.addItem(item);
    }
}

마치며

안드로이드 프로젝트에서 'Optional'을 사용하는 것은 단순히 코드를 줄이는 것이 아니라, 'NPE(NullPointerException)'로 인한 앱 종료를 설계 단계에서 원천 봉쇄하는 것이다. null check 하는 수고를 덜고 다른 구현에 집중할 수 있게 Optional을 잘 활용하길 바란다.

반응형

'Dev > Java' 카테고리의 다른 글

Java Modern Syntax: Text Blocks  (0) 2026.02.06
Java Modern Syntax: Sealed Classes  (0) 2026.01.11
Java Modern Syntax : Lambda in switch  (0) 2026.01.08
Java Modern Syntax : Record  (0) 2026.01.07
Java Modern Syntax: Stream API  (0) 2025.12.27