null과 NullReferenceException

오늘의 주제는 null입니다. C#이란 언어가 C, C++, Java의 계보를 이으며 발전하였기 때문에 이들 언어에 존재하는 null이라는 개념도 그대로 이어 받았습니다. C, C++ 시절에는 null을 참조한다는 게 말 그대로 죽음(crash)를 의미하였으나, 세월이 지나면서 저항력이 생겨 C#에 와서는 런타임에 처리할 수 있는 예외 중 하나인NullReferenceException로 증상이 완화되었습니다. 하지만 여전히 심각한 문제인 것만은 변함이 없습니다.

퀵소트를 만든 영국의 컴퓨터 과학자 Tony Hoare는 Null References: The Billion Dollar Mistake라는 강연을 통해 null reference라는 아이디어가 10억불 짜리 실수라고 이야기하기도 하였습니다. 강연 내용을 들어보면 Tony Hoare가 1965년 알골 W(Algol W) 언어 설계를 하고 있었는데 단순히 구현 편의 때문에 null이라는 개념을 언어 도입했고, 지금 돌이켜서 생각해 보면 심각한 실수였다고 인정하고 있습니다.

오늘날의 프로그래밍은 null 체크로 점철되어 있다고 해도 가정이 아닙니다. C#으로 작성된 게임의 경우도 레퍼런스 타입의 모든 인스턴스가 null이 될 수 있기 때문에 각 레퍼런스를 사용하기 전에 null을 체크하는 코드가 잔뜩 들어가 있는 경우를 빈번히 볼 수 있었습니다. 물론 callee 입장에서는 caller가 null을 넘기지 않는다고 보장해 주면 굳이 null 체크를 할 필요가 없지만, 복잡한 소프트웨어에서 이런 규칙을 일일이 지키기도 힘들기 때문에 모든 개발자들이 방어적으로 null 체크를 넣는 게 일반화되어 있습니다.

물론 null이라는 개념이 아무런 이유 없이 도입된 것은 아닙니다. 프로그래밍 로직상 값의 부재를 나타낼 수 있는 방법이 필요한 경우가 있기 때문입니다. 예를 들어, 게임 캐릭터 테이블에서 캐릭터 아이디로 캐릭터 정보를 찾을 때, 해당 아이디가 없다면 null을 리턴하는 게 일반적인 프로그래밍 방법입니다. 문제는 대부분의 프로그래밍 언어가 꼭 필요한 경우에만 null을 리턴할 수 있는 게 아니라 프로그램 내의 모든 타입이 null이 될 수 있게 프로그래밍 언어를 설계했다는 점입니다.

C#의 null 처리 방식은 굉장히 애매한 지점에 있습니다. 자바는 모든 class 타입이 레퍼런스 타입이고 null이 될 수 있는 반면에, C#의 타입은 크게 레퍼런스 타입(모든 class), value 타입(모든 struct와 기본 타입)으로 나뉘고 이중 레퍼런스 타입만 null이 될 수 있습니다. value 타입은 기본적으로 null이 될 수 없지만, C#의 2.0의nullable type을 이용하면 필요할 경우에는 null이 가능한 타입으로 변경할 수 있습니다. 일례로, int 타입에는 null이 없지만, int? 타입은 int의 모든 값에 추가로 null 값을 가집니다. 하지만 .NET에 존재하는 대부분의 타입은 레퍼런스 타입이고 언제든 null이 될 수 있기 때문에 자바보다 문제가 덜 심각하다고 이야기하기도 어렵습니다.

string 타입은 문제를 더 복잡하게 만듭니다. null이 보통 값의 부재를 나타내는 용도로 쓰이는데, string 타입은 이미 값의 부재를 뜻하는 값이 string 타입 내에 존재하기 때문입니다. "" 혹은 string.Empty가 그런 값입니다. 따라서 string을 인자로 받거나 리턴하는 모든 메소드는 null 뿐만 아니라 Empty인지 아닌지도 확인해야만 합니다. String.IsNullOrEmpty이란 메소드가 존재하는 이유도 null이나 Empty 둘 중 하나만 체크하고 나머지 하나를 빼먹는 실수를 방지하기 위해서입니다. 이 문제는 length가 0인 array나 collection 타입에도 공통으로 존재하는 문제입니다.

null 문제의 역사는 꽤나 깊은데, 이 문제에 대한 가장 체계적인 해결책은 프로그래밍 언어가 원천적으로 null 사용을 불허하고 프로그래머가 원하는 타입에 대해서만 명시적으로 타입 시스템을 통해 null 값을 추가로 받을 수 있도록 허용하는 방식입니다. 이런 타입을 Option 혹은 Maybe 타입이라고 부르는데, .NET 계열의 함수 언어인 F#의 경우에는 Option 타입을 제공합니다.

하지만 게임 클라이언트 개발에 주로 사용하는 C#은 이런 문제에 대해서 자유로울 수 없고, null 체크에 대해 여전히 신경쓸 수밖에 없습니다. C#에서도 Option 타입을 제공하는 여러 라이브러리가 나와 있지만, Option 타입을 쓰도록 강제할 방법이 없고 수많은 .NET 라이브러리가 여전히 null을 리턴하고 있기 때문에 문제를 원천적으로 해결하기는 어렵습니다. 최소한 우리가 작성하는 코드에 대해서는 어떤 타입이 언제 어떻게 null이 될 수 있는 규약을 정하고 따르는 정도가 현재로서는 최선이라고 할 수 있는 것 같습니다.

One thought on “null과 NullReferenceException

  1. Pingback: null과 NullReferenceException | 서광열의 C# 스쿨

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s