본문 바로가기
Java

[JAVA] Optional 클래스로 NullPointerException 방지하기

by teamnova 2025. 4. 10.
728x90

안녕하세요.

오늘은 Optional 클래스를 활용해서 NullPointerException을 방지하는 방법을 알아보겠습니다.

 

1. NullPointerException이란?

NullPointerException은 널값을 참조하려고 할 때 발생하는 런타임 에러입니다. 예를 들어, 아래 코드는 널값을 참조하려고 하기 때문에 에러가 발생합니다.

String value = null;
System.out.println(value.length()); // NullPointerException 발생

 

이 문제를 해결하기 위해 우리는 흔히 if문을 사용하여 널 체크를 합니다.

if (value != null) {
    System.out.println(value.length());
} else {
    System.out.println("값이 없습니다.");
}

 

이 방식도 유용하지만, 코드가 복잡해지고 가독성이 떨어질 수 있습니다. 이를 해결하기 위해 Optional 클래스가 등장했습니다.

 

2. Optional 클래스란?

Optional은 널값을 안전하게 처리하기 위해 자바 8에서 도입된 클래스입니다. Optional은 값이 있을 수도 있고 없을 수도 있는 상황을 명시적으로 표현하며, 널값을 직접 다루지 않아도 되도록 설계되었습니다.

Optional의 주요 특징

  • 값이 존재하면 값을 반환하고, 존재하지 않으면 대체 동작을 수행.
  • 널 체크를 명시적으로 처리하여 코드 가독성을 향상.
  • NullPointerException 발생 가능성을 줄임.

3. Optional 생성 방법

1) Optional.of()

  • 값이 반드시 널이 아니어야 합니다.
  • 널값이 전달되면 NullPointerException이 발생합니다.
Optional<String> optional = Optional.of("Hello");
System.out.println(optional.get()); // 출력: Hello

 

2) Optional.ofNullable()

  • 값이 널일 수도 있고, 아닐 수도 있을 때 사용합니다.
  • 널값이 전달되면 빈 Optional 객체를 생성합니다.
Optional<String> optional = Optional.ofNullable(null);
System.out.println(optional.isPresent()); // 출력: false

 

3) Optional.empty()

  •  Optional 객체를 생성합니다.
Optional<String> optional = Optional.empty();
System.out.println(optional.isPresent()); // 출력: false

 

 

4. Optional 사용법

1) isPresent()와 ifPresent()

  • isPresent(): 값이 존재하면 true, 없으면 false를 반환.
  • ifPresent(): 값이 존재할 경우 특정 동작을 수행.
Optional<String> optional = Optional.ofNullable("Hello");

// 값이 존재하는지 확인
if (optional.isPresent()) {
    System.out.println("값: " + optional.get()); // 출력: 값: Hello
}

// 값이 존재할 때만 실행
optional.ifPresent(value -> System.out.println("값: " + value)); // 출력: 값: Hello

 

2) orElse()와 orElseGet()

  • orElse(): 값이 없을 경우 기본값을 반환.
  • orElseGet(): 값이 없을 경우 람다를 통해 기본값을 생성.
// 값이 존재하지 않을 경우 기본값 사용
Optional<String> optional = Optional.ofNullable(null);

String result1 = optional.orElse("기본값");
System.out.println(result1); // 출력: 기본값

String result2 = optional.orElseGet(() -> "기본값 생성");
System.out.println(result2); // 출력: 기본값 생성

 

3) orElseThrow()

  • 값이 없을 경우 예외를 던집니다.
Optional<String> optional = Optional.ofNullable(null);

try {
    String result = optional.orElseThrow(() -> new IllegalArgumentException("값이 없습니다."));
    System.out.println(result);
} catch (Exception e) {
    System.out.println(e.getMessage()); // 출력: 값이 없습니다.
}

 

4) map()

  • 값을 가공하거나 변환할 때 사용합니다.
  • 값이 존재하면 변환된 값을 포함하는 새로운 Optional을 반환합니다.
Optional<String> optional = Optional.ofNullable("Hello");

// 값을 대문자로 변환
Optional<String> upperCase = optional.map(String::toUpperCase);
System.out.println(upperCase.orElse("값이 없습니다.")); // 출력: HELLO

 

5) flatMap()

  • 중첩된 Optional을 풀어주는 데 사용합니다.
Optional<Optional<String>> nestedOptional = Optional.of(Optional.of("Hello"));

// flatMap으로 중첩된 Optional 해제
Optional<String> flatOptional = nestedOptional.flatMap(value -> value);
System.out.println(flatOptional.orElse("값이 없습니다.")); // 출력: Hello

 

6) filter()

  • 조건에 따라 값을 필터링합니다.
Optional<String> optional = Optional.ofNullable("Hello");

// 문자열 길이가 5인 경우만 반환
Optional<String> filtered = optional.filter(value -> value.length() == 5);
System.out.println(filtered.orElse("조건에 맞는 값이 없습니다.")); // 출력: Hello

 

5. 예제

1) 사용자 정보 조회

사용자 정보를 조회할 때, 널값을 안전하게 처리하는 예제입니다.

import java.util.Optional;

public class UserService {
    public static void main(String[] args) {
        Optional<String> username = findUsernameById(1);

        // 값이 존재하면 출력, 없으면 기본값 사용
        String result = username.orElse("기본 사용자");
        System.out.println("사용자 이름: " + result); // 출력: 사용자 이름: John
    }

    // 사용자 이름을 ID로 조회
    public static Optional<String> findUsernameById(int id) {
        if (id == 1) {
            return Optional.of("John");
        }
        return Optional.empty();
    }
}

 

2) API 응답 처리

API 응답 데이터를 처리할 때, 널값을 효과적으로 처리하는 예제입니다.

import java.util.Optional;

public class ApiResponseExample {
    public static void main(String[] args) {
        Optional<String> apiResponse = getApiResponse();

        // 응답 데이터를 대문자로 변환
        String result = apiResponse
                .map(String::toUpperCase)
                .orElse("응답 없음");

        System.out.println("API 응답: " + result); // 출력: API 응답: SUCCESS
    }

    // API 응답 시
    public static Optional<String> getApiResponse() {
        return Optional.ofNullable("success"); // 실제 응답
    }
}

 

6. Optional 사용 시 주의사항

  1. Optional은 필드에 사용하지 말 것:
    • Optional은 메서드 반환값으로 사용하는 것이 적합합니다. 필드로 사용하면 불필요한 오버헤드가 발생할 수 있습니다.
  2. 널값 대신 Optional 반환:
    • 메서드에서 널값을 반환하지 말고 항상 Optional.empty()를 반환하세요.
  3. Optional 남용 금지:
    • 간단한 널 체크가 가능한 경우 Optional을 굳이 사용할 필요는 없습니다.