How to Diagnose a Null Exception Message in Java

1. 개요

Java에서의 예외는 버그를 찾아 수정하는 데 도움이 될 수 있습니다. 하지만 예외 메시지가 null인 경우에는 도대체 무엇이 잘못되었는지 단서를 찾아 헤매게 됩니다.

이 간단한 튜토리얼에서는 이 문제를 살펴보겠습니다.

2. 문제 소개

먼저 예제를 통해 문제를 이해해 보겠습니다:

class Team {
    private String name;
    //... getters and setters
}

class Player {
    private static Logger LOG = LoggerFactory.getLogger(Player.class);
    private String name;
    private Team team;

    public Player(String name) {
        this.name = name;
    }

    void output() {
        try {
            if (team != null) {
                LOG.info("Player:{}, Team:{}", name.toUpperCase(), team.getName().toUpperCase());
            } else {
                throw new IllegalArgumentException("Team is null");
            }
        } catch (Exception e) {
            LOG.error("Error occurred:" + e.getMessage());
        }
    }
    //... getters and setters
}

위의 코드에서, 우리는 PlayerTeam 인스턴스를 참조하도록 하고 있습니다. output() 메서드에서는 현재 Playerteamnull일 경우 유의미한 메시지를 가진 IllegalArgumentException을 던집니다. 더불어 output() 본문 전체가 try-catch 블록에 감싸져 있습니다.

다음으로, output()을 호출하기 위해 Player 인스턴스를 생성해 보겠습니다:

Player kai = new Player("Kai");
kai.setTeam(new Team());

kai.output();

예제에서 보듯, 우리는 kai를 생성한 후 kai.output()을 호출했습니다.

JDK 14 이전 버전으로 이 코드를 실행하면, 다음과 같은 출력이 나타납니다:

10:03:54.016 [main] ERROR com.baeldung...Player -- Error occurred: null

null 예외 메시지는 혼란스럽고 문제가 무엇인지 파악하는 데 도움이 되지 않습니다.

다음으로, 왜 이러한 일이 발생하는지 그리고 혼란스러운 null 메시지를 피하는 방법에 대해 이해해 보겠습니다.

3. null 예외 메시지가 의미하는 것 이해하기

보통 Java에서 null 예외 메시지를 보게 되면, 일반적으로 메시지 없이 예외가 던져졌음을 의미합니다. 예를 들어, JDK 14 이전 버전에서 애플리케이션이 실행될 때 NullPointerException()이 던져질 수 있습니다.

이 코드를 주의 깊게 살펴보면, Player 인스턴스(kai)는 널이 아닌 Team 참조를 가지고 있습니다. 그러나 우리는 Team.name 속성을 설정하지 않았습니다. 즉, team.getName()null입니다. 따라서 team.getName().toUpperCase()는 예상치 못한 NullPointerException을 발생시킵니다.

JDK 14 이전에는 NullPointerException에 메시지가 없었으므로 출력에서는 null 메시지가 나타납니다.

JEP 358가 JDK 14에 구현되었습니다. 따라서 JDK 14부터는 NullPointerException이 예외 발생 원인을 나타내는 자세한 메시지를 제공합니다. 예를 들어, JDK 17로 동일한 코드를 실행하면 출력은 다음과 같습니다:

10:23:53.016 [main] ERROR com.baeldung...Player -- Oops! Error occurred. Cannot invoke "String.toUpperCase()" because the return value of "com.baeldung...Team.getName()" is null

이번에는 메시지가 NullPointerException의 원인을 가리켰기 때문에, 우리는 문제의 본질에 접근하고 버그를 수정할 수 있었습니다.

하지만 가끔은 오래된 JDK와 작업해야 할 때도 있습니다. 따라서 다음으로 혼란스러운 null 예외 메시지를 피하는 방법을 알아보겠습니다.

4. 메시지 대신 스택 트레이스 사용하기

스택 트레이스는 예외가 발생할 때 근본 원인을 찾기 위한 출발점입니다. 그래서 예외를 출력할 때 스택 트레이스를 포함해야 합니다.

스택 트레이스를 얻는 일반적인 방법은 Exception.printStackTrace() 메서드를 호출하는 것입니다:

void outputWithStackTrace() {
    try {
        // ... 같은 코드
    } catch (Exception e) {
        e.printStackTrace();
    }
}

우리는 catch 블록에서 LOG.error() 호출을 e.printStackTrace()로 교체하기만 했습니다. 이제 같은 Player kai를 만들고 kai.outputWithStackTrace()를 호출하면 다음과 같은 출력이 나옵니다:

java.lang.NullPointerException
    at com.baeldung...Player.outputWithStackTrace(ExceptionGetMessageNullUnitTest.java:48)
...

메시지가 없더라도 스택 트레이스는 우리가 NullPointerException을 만났고 예외가 발생한 코드의 정확한 줄을 알려줍니다. 이는 체크 포인트를 제공합니다.

때때로 우리는 애플리케이션 오류 메시지를 관리하기 위해 여전히 로거를 사용할 수 있습니다. 예를 들어, 우리는 예제에서 SLF4J를 사용하고 있습니다. 따라서 SLF4J를 사용하여 예외를 기록할 수 있습니다:

void outputWithStackTraceLog() {
    try {
        // ... 같은 코드
    } catch (Exception e) {
        LOG.error("Error occurred.", e);
    }
}

이제 kai.outputWithStackTraceLog()를 호출하면 다음과 같은 오류 로그가 생성됩니다:

10:36:31.961 [main] ERROR com.baeldung...Player -- Error occurred.
java.lang.NullPointerException
    at com.baeldung...Player.outputWithStackTraceLog(ExceptionGetMessageNullUnitTest.java:60)
...

출력에서 보듯이 이 오류 로그는 “Team.name이 null이다” 문제를 찾고 수정하는 데 도움이 됩니다.

5. 결론

이번 기사에서는 null 예외 메시지가 발생하는 이유와 예외의 스택 트레이스를 얻는 방법에 대해 논의했습니다. 이는 문제의 근본 원인을 찾는 데 도움이 됩니다.

소스 코드는 GitHub에서 확인할 수 있습니다.

원본 출처

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다