Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java NullPointerException(NPE) 발생 원인과 해결 방법 총정리


NullPointerException은 애플리케이션 코드, 참조 중인 외부 API 또는 미들웨어가 다음과 같은 상황에 직면했을 때 JVM(자바 가상 머신)이 던지는 런타임 예외입니다.

  • null 상태인 객체의 인스턴스 메서드를 호출하려고 시도하는 경우
  • null 객체의 특정 필드에 접근하거나 값을 수정하려고 시도하는 경우
  • null 상태인 배열 객체의 길이(length)를 구하려고 시도하는 경우

NullPointerException 해결 절차

  1. 스택 트레이스 분석: java.lang.NullPointerException의 스택 트레이스를 꼼꼼히 살펴보고 예외가 어디에서 발생했는지 확인합니다(애플리케이션 코드, 서드파티 API, 미들웨어 소프트웨어). 스택 트레이스에는 문제가 발생한 클래스명과 라인 번호가 포함되어 있으므로 해당 지점을 정확히 추출해야 합니다.
  2. 발생 위치에 따른 대응: 문제가 애플리케이션 코드 내부에 있다면 코드 워크스루(code walk-through)를 진행해야 합니다. 반면 서드파티 API나 미들웨어 쪽에서 발생했다면, 호출하는 쪽의 코드를 먼저 검토하여 간접적인 원인이 되지 않았는지 확인합니다. 예를 들어 null 값을 서드파티 API 메서드에 그대로 전달하는 경우가 대표적입니다.
  3. null이 된 객체 식별 및 코드 수정: 애플리케이션 코드에서 문제가 발견되었다면 어떤 객체 인스턴스가 null이 되어 오류를 일으키는지 파악합니다. 이후 적절한 null 체크(null check) 검증 로직과 로깅을 추가하도록 코드를 수정하면, null 값이 어디에서 유래했는지 추적하는 것도 훨씬 수월해집니다.

예제 코드

아래 예제는 객체 참조를 null로 만든 뒤 해당 객체의 메서드를 호출하여 NullPointerException이 발생하는 과정을 보여줍니다.

public class NPEDemo {
    private String field1 = null;
    private String field2 = null;

    public String getField1() {
        return field1;
    }

    private void setField1(String field1) {
        this.field1 = field1;
    }

    public String getField2() {
        return field2;
    }

    private void setField2(String field2) {
        this.field2 = field2;
    }

    public static void main(String[] args) {
        try {
            NPEDemo npe = new NPEDemo();
            npe.setField1("field1 value");
            npe = null; // 참조를 null로 변경
            npe.setField2("field2 Value"); // null 객체의 메서드 호출 → NPE 발생
        } catch (Throwable e) {
            System.out.println("Java Error is: " + e);
            e.printStackTrace();
        }
    }
}

실행 결과

Java Error is: java.lang.NullPointerException
java.lang.NullPointerException
at NPEDemo.main(NPEDemo.java:24)

출력 결과에서 볼 수 있듯이, npe = null;로 참조를 초기화한 상태에서 setField2()를 호출하는 순간 예외가 발생하며, 스택 트레이스는 문제가 발생한 정확한 라인(NPEDemo.java의 24번째 줄)을 알려줍니다.

추가 팁: NullPointerException 사전에 예방하기

예외 발생 후 디버깅하는 것보다 처음부터 예방하는 것이 효율적입니다. 다음 방법들을 활용해 보세요.

  • Optional 활용: Java 8 이상에서는 Optional<T>를 사용해 값이 없을 수 있음을 명시적으로 표현하면 NPE 위험을 크게 줄일 수 있습니다.
  • Objects.requireNonNull(): 필수 매개변수를 메서드 초입부에서 검증하여 잘못된 null 전달을 조기에 차단합니다.
  • 안전한 문자열 비교: str.equals("value") 대신 "value".equals(str)처럼 리터럴을 앞에 두면 str이 null이어도 예외가 발생하지 않습니다.
  • 정적 분석 도구 활용: IDE의 경고 기능이나 SpotBugs, NullAway 같은 도구를 빌드 과정에 통합하면 잠재적인 NPE를 컴파일 단계에서 미리 발견할 수 있습니다.