본문 바로가기
Programming/Java, Spring

간단한 코드 한 줄로 NPE (Null Pointer Exception) 방지하는 방법

by Renechoi 2024. 2. 18.

요약

자바 코드에서 자주 발생하는 NPE를 간단한 습관 변경으로 예방하는 방법을 소개합니다.

NPE 방지해야 하는 이유 ?

개발을 하다 보면, NullPointerException (이하 NPE)을 만나는 일은 빈번한데요. NPE는 자바에서 가장 흔하게 발생하는 런타임 예외 중 하나로, 개발자가 정의한 커스텀 예외가 아니라는 점에서 예상치 못한 예외로 분류됩니다. 어떤 변수가 null 상태인데, 마치 유효한 객체인 것처럼 접근하려 할 때 NPE가 발생합니다. 개발 중이라면 그나마 괜찮겠지만 배포한 서버에서 불연듯 NPE가 뜨면 난감해집니다.  🥵

한 가지 예시

NPE를 방지하기 위한 가장 간단하고 효과적인 방법 중 하나는 객체 참조나 메서드 사용시에 주의를 기울이는 것입니다. 예를 들어 equals 메서드를 사용할 때 다음과 같이 "안전한 비교(safe compare)"를 할 수 있습니다. 같은 기능을 하는 코드 한 줄인데, 극과 극의 차이가 납니다.

예시: 기존 코드
public boolean isInUse(){
    return this.status.equals(TicketStatus.IN_USE);
}

 

위 코드에서 this.statusnull일 경우, NullPointerException이 발생합니다.

 

개선된 코드
public boolean isInUse(){    
    return TicketStatus.IN_USE.equals(this.status);
}

 

개선된 코드에서는 TicketStatus.IN_USE.equals(this.status) 형태로 작성되었습니다. 여기서 TicketStatus.IN_USE는 enum 타입으로서 null이 아님이 보장되죠. 따라서 this.statusnull이어도 equals() 메소드는 안전하게 false를 반환하며, NPE를 방지합니다.

 

다른 예시로 적용해본다면 ?

안전한 비교(safe compare) 방식을 다른 상황에도 적용해봅시다. 예를 들어, 사용자의 이메일을 확인하는 상황을 생각해 볼까요? 사용자가 로그인을 시도할 때 이메일 필드가 null인지 아닌지를 확인해야 합니다.

 

예시: 기존 코드
public boolean isValidUser(String registeredEmail){
    return this.email.equals(registeredEmail);
}

 

이 코드에서 this.emailnull일 경우, 다시 NPE가 발생합니다.

 

개선된 코드
public boolean isValidUser(String registeredEmail){
    return registeredEmail.equals(this.email);
}

 

개선된 코드에서는 registeredEmail.equals(this.email) 형태로 작성됩니다. 단, 이 경우에는 registeredEmailnull이 아닌 것으로 보장이 되어야 하겠고, 그러하다면 this.emailnull이라 할지라도 안전하게 처리됩니다.

 

이처럼, 비교 대상 중 하나가 null이 아니라는 것이 확실하다면, 그 대상을 앞에 두고 equals() 메소드를 사용하는 것이 NPE를 방지하는 간단하면서도 효과적인 방법입니다. 이런 작은 습관이 큰 버그를 예방하는 데 큰 도움이 됩니다.

 

다른 방법들

NPE를 피하는 또 다른 방법은 'null 검사'를 통해서입니다. 간단한 null 검사를 통해 객체가 null이 아닐 때만 메서드를 호출하거나, 참조에 접근하도록 하는 것이죠. 이 방법은 특히 외부 라이브러리나 API로부터 데이터를 받을 때 유용합니다. 어떤 값을 기대했는데 null이 반환되는 경우가 종종 있을 텐데요.

 

예를 들면...

 

예시: 기존 코드
public String getUsername(User user){
    return user.getName();
}

 

위 코드에서 user 객체가 null이면, getName() 메서드 호출 시 NPE가 발생합니다.

 

개선된 코드
public String getUsername(User user){
    return user != null ? user.getName() : "Unknown";
}

 

개선된 코드는 usernull인지 먼저 확인합니다. 만약 null이라면 'Unknown'을 반환합니다. 이렇게 하면 user 객체가 null일 때 NullPointerException을 방지할 수 있습니다.

 

물론, 모든 null 검사를 직접 작성하는 것은 번거로울 수 있습니다. 그래서 Java 8 부터는 Optional 클래스를 사용합니다.

 

Optional을 사용하면 null 관련 문제를 보다 간결하고 안전하게 처리할 수 있습니다. (코틀린에서는 엘비스 연산자를 사용해서 더 쉽지만 이 글에서는 생략!)

 

Optional을 사용한 코드
public String getUsername(User user){
    return Optional.ofNullable(user)
                   .map(User::getName)
                   .orElse("Unknown");
}

 

이 코드에서 Optional.ofNullable(user)user가 null이 아니면 Optional 객체를 반환하고, null이면 빈 Optional 객체를 반환합니다. map(User::getName)user가 null이 아니라면 getName()을 호출하고, orElse("Unknown")Optional 객체가 비어 있으면 "Unknown"을 반환합니다. 이런 방식으로 Optional을 활용하면 코드가 더 깔끔하고, null을 안전하게 처리할 수 있습니다.

 

개인적으로 Optional을 많이 사용하는 편입니다. Optional을 잘 사용하면 코드 가독성이 많이 좋아지고 좀 더 안정성 높은 코딩을 할 수 있는 것 같아요. 🚀

반응형