[프로그래밍] 널 (Null)
프로그래밍에서 널
프로그래밍에서 널(null)은 포인터가 유효하지 않은 객체를 참조하는 것 또는 객체 참조가 유효하지 않은 것을 의미한다. 값이 존재하지 않는다(값이 초기화되지 않거나 연산 결과가 없다)는 것을 표현하기 위해 널이라는 개념을 사용한다. 유효하지 않는 객체를 참조하는 것을 널 참조(null reference)라고 한다. 토니 호어(Tony Hoare)는 1965년 알골(ALGOL) 언어에서 널 참조를 발명했다. 함수형 언어인 하스켈, ML 등은 널 참조 개념을 사용하지 않는다.
유효하지 않은 객체를 참조하는 포인터를 널 포인터(null pointer)라고 한다. 널 포인터는 어떠한 객체도 참조하지 않는 포인터라고 할 수 있다. 널 포인터는 존재하지 않는 메모리 주소를 가리킨다(참조한다).
객체를 참조하는 포인터 변수가 유효한 객체를 참조하는 경우 해당 변수에는 객체가 할당된 메모리 주소가 저장되지만, 유효하지 않은 객체를 참조하는 경우 변수에는 널 값이 저장된다.
변수 초기화 시 특정 객체를 참조하지 않게 하기 위해 널 값을 할당할 수도 있다. 프로그래밍 언어에 따라 명시적으로 초기화되지 않은 객체 참조 변수에 널 값이 할당되는 경우가 존재한다(예: 자바, 파이썬 등).
유효하지 않은 객체의 메모리 주소에 접근하려는 경우 널 포인터는 런타임 에러를 발생시키거나 프로그램을 종료시키기도 하며 이를 널 포인터 역참조(dereference) 또는 널 역참조라고 한다. 포인터를 역참조한다는 것은 해당 포인터가 가리키는 메모리 주소에 접근한다는 것을 의미한다. 포인터가 가리키는 메모리 주소에 접근하기 위해서는 메모리 주소를 참조 해제해야 한다. 널 포인터는 존재하지 않는 메모리 주소를 가리키므로 널 포인터 역참조는 존재하지 않는 메모리를 해제하는 것이다. 포인터가 널 포인터가 아닌 경우 포인터 역참조는 정상적인 동작이다. 자바 언어는 널 포인터 역참조를 정의되지 않은 동작으로 간주하여 NullPointerException
예외를 발생시킨다.
널 참조를 확인하는 코드를 추가함으로써 널 포인터 역참조로 인한 NullPointerException
예외 발생을 막을 수 있다. 하지만 널 포인터 역참조로 인해 발생 가능한 오류를 방지하기 위한 방법은 다음과 같은 문제점이 있다.
- 값이 없다는 것을 표현하기 위해 널을 사용하더라도 널 포인터 역참조로 인한 오류가 발생하게 되므로 이를 방지하기 위한 작업이 코드 상에 추가로 필요하다.
- 단순히 널 참조를 확인하기 위한 조건 로직이 추가되어 비즈니스 로직과 널 참조 확인 로직이 함께 존재하게 됨으로써 코드 복잡도가 증가한다.
널을 사용하여 값이 없다는 것을 나타내는 개념의 문제점을 개선하기 위한 방법들이 존재한다.
- 널 가능한 타입(nullable type)을 사용하여 값이 존재하지 않는 경우 널 참조가 일어날 수 있음을 명시적으로 컴파일러에게 알림으로써 런타임시 예외 발생을 막을 수 있다. 이 경우 널 참조 확인을 위한 코드 작성은 필요하다.
- 안전 내비게이션 연산자(safe navigation operator)를 사용하여 널 참조 확인을 위한 중첩 조건문 작성 없이 널 참조에 대한 접근을 막을 수 있다. 널 참조 확인을 위한 코드 작성이 간단해진다.
- 값이 존재하지 않는다는 것을 나타낼 수 있는 선택 타입(option type)을 사용하여 널 참조 개념을 적용하지 않음으로써(널 참조에 접근하지 않도록 함으로써) 널 참조 확인을 위한 추가적인 로직 없이 예외 발생을 막을 수 있다. 널 참조 확인을 위한 코드가 필요 없어지지만 선택 타입을 위한 또다른 문법과 이에 따른 코드 작성이 필요해진다.
선택 타입
선택 타입을 사용하면 데이터가 없을 수도 있음을 명시적으로 나타낼 수 있다. 데이터가 반드시 있어야 되기 때문에 선택 타입을 사용하지 않았다면, 널 포인터 역참조로 인한 에러가 발생 가능하지만 널인지 확인하는 코드를 반드시 작성할 필요는 없으며 널 값이 설정되는 로직(알고리즘)의 문제인지 먼저 판단해야 한다.
데이터가 없을 수도 있는 경우 선택 타입을 사용하여 널 참조 예외가 발생하는 것 자체를 막을 수 있지만 데이터가 반드시 존재해야 하는 경우에는 선택 타입을 사용하여 코드 상에서 값을 초기화할지, 널 참조가 가능하도록 하여 적절한 값이 할당되지 않은 것을 런타임에 오류로 발생시킬지 판단이 필요하다.
예를 들어 도메인 객체를 모델링할 때 해당 객체에 값이 존재할 수도 있고 존재하지 않을 수도 있다면 선택 타입을 사용하여 널 참조로 인한 예외 발생 가능성을 줄일 수 있으며, 선택 타입을 인자로 받거나 반환하는 메서드를 정의하면 해당 메서드를 사용할 때 해당 객체의 값 존재 여부 및 가능성에 대해 명시적으로 알려주는 것이므로 더 좋은 API 설계가 가능해진다.
자바 언어는 8 버전부터 널 포인터 역참조 문제를 개선하기 위해 Optional
인터페이스를 통해 선택 타입을 도입하였다.
Comments