티스토리 뷰

ALL

Null, NPE 방어에 대해서

whoAmI_ 2023. 7. 24. 13:20

 

대부분의 언어는 값이 할당되지 않은 변수의 기본값으로 null을 할당한다. 자바에서는 참조변수들의 기본값이 NULL이며, 업무를 하다보면 널 포인터 예외(Null Pointer Exception)은 자주 마주치게 되는 Exception이다. 
처음에는 NPE라는 용어도 낯설었는데, NPE를 null pointer exception의 줄임말로 입에 붙어 사용하는 지금은 업무중 발생하는 에러 중 "이 변수에는 값이 할당돼 있지 않을 수가 없는데" 하는 상황을 마주하게 된다.

변수가 널 값을 갖지 않았는지 검사하는 아래와 같은 코드들을 수없이 발견할 수 있다.(현재 작업중인 프로젝트에서)

if(Object != null)

jdk 1.7버전으로 jdk1.8부터 지원하기 시작한 Optional도 사용하지 않고 1.7환경에서 작업하다보니 그런게 있다는 것조차 잊고 있었다. 

하지만 요즘 언어 추세가 null값 자체를 할당하지 않도록, 갖을 수 없도록 만들어가고 있다. Optional이란것을 통해 보완할 수 있다고 들었고, 요즘 배우기 시작한 Rust에서도 null값을 아예 방지한다.

우리는 이런 상황을 마주하게 된다면,
변수가 널값을 갖지 않았는지 검사하거나, 널 객체 패턴(null object pattern)이나 옵션타입(option type)등을 사용해서 널 포인터 예외를 방지할 수 있다.
실무에서 널 객체 패턴이 얼마나 사용되고 있는지 나의 매우 적은 실무 경험으로는 잘 모르겠다. 적어도 현재 업무에서는 아쉽게도 널 객체 패턴이란 찾아볼 수 없다는 점이다..


널 객체(오브젝트) 패턴 이란 무엇일까?

자바에서 변수가 선언되어있고 여기에 값이 할당되지 않은 상태에서 변수를 참조하면 우리가 잘 알고있는 NULL POINTER EXCEPTION이 발생된다. 의도하지 않았던 unchecked exception이 발생되기 때문에 시스템이 오류상황을 미리 알기 어렵게 된다. NULL OBJECT PATTERN은 이와같이 값이 NULL일 때 발생할 수 있는 문제를 최대한 방어하기 위한 패턴이다.

먼저 계정 정보를 반환하는 service 함수가 있다고 할 때, getById(String id) 메서드를 호출시 계정 정보가 없다면 null 을 반환 하는게 일반적인 함수 구현 방식인데 이때 null 을 반환 하는게 아니라 널 오브젝트를 반환하는게 이 패턴의 핵심 이다.
널 오브젝트는 내부에는 아무런 값이 들어있지 않은 상태이지만 getById() 메서드는 최소한 null을 반환하지는 않게 된다.
널 오브젝트를 이용하면 비즈니스 로직에서 null에 대한 신경을 쓰지 않고도 유효한 객체를 반환한다는 것을 보장해 줄 수 있다는 것을 의미한다. 이 때 반환된 널 오브젝트는 아무런 동작을 하지 않는다.

  • 널오브젝트 패턴은 반환값이 항상 유효한 객체를 반환해 줌을 보장해 줄 수 있다.
  • 널오브젝트는 대상 구현체와 같은 인터페이스를 구현 한다.

이렇게 널오브젝트 패턴을 적용해서 NPE를 방지할 수 있다.
하지만 자바8부터 지원하기 시작한 Optional을 이용하면 보다 쉽게 null 값에 대한 방어를 할 수 있다.

 

Optional 이란?

: 값이 존재 할 수도, 존재하지 않을 수도 있는 값을 포장한 객체

런타임에서 발생하는 NullPointException 방어를 위해 만들어둔 로직체크는 코드의 가독성과 유지 보수성이 떨어진다. 어떻게 null 을 다루면 좋을 지에 대한 해결책을 함수형 언어에서 찾았다. 함수형 언어는 존재하지 않을 수도 있는 값에 대한 별도의 타입을 가지고 있다. 개발자들은 여러가지 API 를 통해 간접적으로 값에 접근할 수 있다. 자바는 함수형 언어로부터 영감을 받아 자바 8에 처음 Optional 이 도입 되었다.

Optional은 Null 이 될 가능성을 가진 값을 객체로 감싸는 래퍼 클래스다. 즉, Optional 에 포장된 객체는 하나의 원소 혹은 Null 원소가 되는 것을 뜻한다. Null 을 직접 다루면 위험한 상황이 발생하거나 굉장히 까다롭다. 이를 Optional 객체에 포장함으로써 유연한 처리가 가능해 진다. Null 을 Optional 에 포장하게 되면 Null 을 값으로 보고 로직을 구현할 수 있다.

 


null 값에 대한 처리를 찾아보다 보니, Objects.isNull(), Objects.nonNull(), Objects.requireNonNull() 에 대한 메서드들을 마주하게 되었다. 

- Objects.isNull() - isNull() 메서드는 매개값이 null인 경우 true를 return한다.
- Objects.nonNull()  - nonNull() 메서드는 매개값이 not null일 경우 true를 return한다.
- Objects.requireNonNull() - requireNonNull() 메서드는 세가지로 오버라이딩을 할 수 있다. 

 

requireNonNull 로 널 체크를 하고, 사용하는 이유는 무엇일까?

requireNonNull은 자바7에 추가된 Object 클래스에서 제공하는 널 체크를 위한 메소드이다. 파라미터 로 입력된 값이 null이라면 NPE가 발생하고, 그렇지 않다면 입력값을 그대로 반환하는 메소드이다.

사용법

첫번째 메소드는 null 을 전달하면 메세지가 비어있는 NPE 예외를 던진다.

Objects.requireNonNull(null); 
// java.lang.NullPointerException

두번째 메소드는 null 을 전달하면, 두번째 파라미터로 전달한 문자열을 메세지로 갖는 NPE 예외를 던진다.

Objects.requireNonNull(null, "null은 전달될 수 없습니다!");
// java.lang.NullPointerException: null은 전달될 수 없습니다!

세번째 메소드는 null 을 전달하면, 두번째 파라미터로 전달한 Supplier 를 구현한 익명 함수의 반환값을 메세지로 갖는 NPE 예외를 던진다.

Objects.requireNonNull(null, () -> "null은 전달될 수 없습니다!");
// java.lang.NullPointerException: null은 전달될 수 없습니다!

 

requireNonNull 을 사용하는 이유
=> 빠른 null 감지, 명시성, 가독성


@NotNull

lombok에서 지원하는 @NotNull 어노테이션은 이름 그대로 Null만 허용하지 않는다. "" 이나 " " 은 허용한다.


이러한 방식을 사용하여 null값의 사용을 피해 장애에 대비하기 위한 방어적 프로그래밍을 하도록 해야한다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함