널
널(NULL)이란 어떠한 값도 가지지 않고 있다는 뜻이다. 널값이라고도 한다. 널은 빈칸과 같은 개념으로서, 이용할 수 없는, 할당되지 않은, 적용 불가능하다는 의미이다. 널값을 포함한 어떠한 산술식의 결과 역시 널(NULL)이 된다.
개요
널(null, 간단히 NUL)은 사전적인 뜻으로 '값이 없음'을 나타내는 영단어이며 값이 없다는 것은 그 값이 0조차 아니라는 뜻이다. 독일어에선 숫자 0이란 뜻으로 쓰인다. 독일식으로는 눌, 영미식으로는 널이라고 발음한다. 구조적 질의 언어 (SQL)에서는 데이터베이스 내의 데이터 값이 존재하지 않다는 것을 지시하는데 사용되는 특별한 표시어이다. 데이터베이스 관계 모델의 창시자인 E. F. 콧(E. F. Codd)에 만들어진 개념으로, SQL NULL은 모든 true RDBMS가 빠진 정보와 적용할 수 없는 정보를 지원해야 한다는 요구 사항을 충족시키는 역할을 한다. 콧은 또한 데이터베이스 이론에서 널을 표현하기 위해 그리스어의 오메가의 소문자(ω)를 이용할 것을 도입했다. 널은 SQL에서 널 특수 표시를 구분하기 위해 사용되는 유보된 키워드이다.
널은 SQL 조인에서 사용하기 위해 필수적인 3치 자궁로 인해 논란의 중심이 되어 왔다. 컴퓨터 과학자인 론 밴 더 메이든 교수는 그러한 여러 문제를 다음과 같이 요약했다. SQL 표준에서 비일관성은 SQL에서 널의 취급 문제를 직관적인 논리 어휘 탓으로 돌리는 것은 가능하지 않다는 것을 의미하는 것이다. 이 문제를 해결하기 위해 여러 해법들이 제시되었지만, 대안의 복잡성은 폭넓은 이론 확산의 걸림돌이 되었다.
데이터베이스 비전문가들에게, 널이 뜻하는 바가 무엇인지 이해하는 좋은 방법은 정보적 측면을 기억하는 것이다. “값의 부족”은 0값과 동일한 의미가 아니며, 유사하게 ‘정답의 부족’이라는 말도 정답이 없다는 말과 같은 의미가 아니다. 예를 들어, 다음과 같은 질문을 예로 들어보자. “철수가 책을 몇 권 가지고 있지?” 답은 0(하나도 가지고 있지 않다는 것을 안다면) 또는 null(그가 가지고 있는지, 아닌지 또는 얼마나 가지고 있는지 모름)일 것이다. 데이터베이스 테이블에서, 이러한 답을 가진 컬럼은 null 값으로 시작할 것이다. 그리고 철수가 책을 가지고 있다는 것이 확인되기 전까지는 0으로 수정되지 않을 것이다. 유사하게, 질문이 “철수가 차를 가지고 있어?”라는 질문에도 “모르겠는데”라는 정답은 “아니!(No)”라는 것과 같은 의미가 아니다. 전자는 데이터베이스의 Null 목록을 생성하며, 후자는 데이터베이스 엔트리에서 No를 생성한다.
아스키(ASCII)코드에서는 0번(0x00)에 해당하는 제어 문자이다. 화면상에는 표시되지 않고 오직 바이트 상으로만 존재한다. 단, 윈도우 한정으로 일반적인 텍스트 편집기에서는 고정폭 글꼴을 쓰는 경우에 한해서 공백 비슷하게 표시되는 경우가 있다. 텍스트를 입력하는 필드 같은 데서 바이트 수가 정해져 있는 데, 그 바이트 수를 채우지 못하고 끝내버리면 오류가 발생할 수도 있다. 이런 경우에 바이트 수를 임의로 채우기 위해 나머지 자리에 널 문자를 넣는다. 예를 들면 MP3 파일에 사용되는 ID3 태그 같은 것들이 있다. 일부 프로그램은 널 문자가 아닌 공백 문자(0x20)를 대신 입력하는 경우도 있다. FAT 파일 시스템에서도 공백으로 채운다. 예를 들면, ABCDEFG.TXT라는 파일은 ABCDEFG .TXT와 같이 기록된다. 이외에도 ISO/IEC 646 (아스키), C0 제어 코드, 국제 문자 집합 (유니코드의 U+0000), 확장 이진화 십진법 교환 부호(EBCDIC)와 같은 수많은 문자 집합에 존재한다. 주로 쓰이는 거의 모든 프로그래밍 언어에서 사용할 수 있다.[1][2]
역사
E. F. 콧(E. F. Codd)은 1975년 그의 논문 'FDT Bulletin of ACM-SIGMOD'에서 널을 빠진 데이터를 나타내는 수단으로서 언급했다. SQL에서 채택된 널을 설명하기 위해 가장 보편적으로 인용되는 콧의 논문은 1979년 논문이었던 'ACM Transactions on Database Systems'으로 비록 이후의 논문에서 많은 제안들 대부분이 모호한 채로 남아있지만, 여기에서 그는 또는 관계 모델/태즈매니아를 소개했다. 1979년 논문 2.3장에는 수학적 연산에서의 널 보급의 세부적인 사항과 널 값을 비교할 때, 3가 논리를 이용하는 비교도 소개를 했다. 여기에는 또한 다른 연산자 셋과 Null을 취급하는 것도 소개를 했으며, 후자의 이슈는 오늘날에도 여전히 논란이 많은 문제이다. 데이터베이 이론계에서는, 1975년과 1979년 제안한 콧의 원래 이론들이 지금은 ‘콧의 테이블’로 언급되고 있다. 콧은 이후에 모든 관계형 데이터베이스 관리 시스템이 빠진 데이터를 지시하기 위해 널에 지원해야할 그의 요구사항을 1985년 '컴퓨터월드 매거진'에 기고한 2개의 글을 통해 보강했다. 1986년 SQL 표준은 근본적으로 IBM 시스템 R에서 수행 프로토타입 이후 콧의 제안을 채택했다. 비록 돈 챔벌린은 널 값을 SQL에서 가장 논쟁적인 특징으로 규정했지만, 그는 빠진 정보를 설명하는 가장 비용이 적게 드는 시스템 지원이라는 실용적인 측면을 들어 SQL에서 널의 설계를 옹호했다.[2]
특징
널 보급
- 수학적 연산
널은 데이터 값이 아니라, 미지의 값에 대한 표시일 뿐이기 때문에, 널에 수학적 연산을 사용하는 것은 미지의 값으로 나타난다. 아래의 예에서, 10을 널에 곱하면 결과값은 널이 된다.
10 * NULL -- 결과는 NULL
이것은 예기치 않은 결과를 야기한다. 예를 들어, 널을 0으로 나누려 한다면, 플랫폼은 ‘0으로 나눈’ 예상된 데이터 예외 값을 던지는 대신, 널 값을 반환한다. 비록 이러한 행위가 ISO SQL 표준으로 정의되어 있지는 않지만, 많은 DBMS 벤더들이 이러한 연산을 유사하게 다룬다. 예를 들어, 오라클, PostgreSQL, MySQL 서버, 그리고 마이크로소프트 SQL 서버 플랫폼은 모두 널 값을 아래와 같이 반환한다.
NULL / 0
- 문자열 연속
SQL에서 흔히 볼 수 있는 문자열 연속 연산 또한 연산자 중 하나가 널 값일 때 널 값으로 나타난다. 아래의 예제는 SQL || 문자열 연속 연산자에 널을 사용함으로써 널 값이 리턴되는 것을 보여준다.
'생선' || NULL || '김치' -- 결과는 NULL
모든 데이터베이스에서 이러한 것은 아니다. 오라클 RDBMS를 예로 들면, 널 과 빈 문자열은 동일한 것으로 취급되어, '생선' || NULL || '김치'는 ‘생선 김치’로 나타난다.[2]
프로그래밍 언어
널은 0차원의 난해한 프로그래밍 언어이다. 하나의 양의 정수를 소인수분해하여 명령을 만들어낸다. 아스키코드는 0인데 문자 '0'과는 다르다. 문자 '0'의 아스키코드값은 48 유니코드는 U+0000이 널 문자다. C 프로그래밍에서는 '존재하지 않는 메모리 주소'를 널로 나타낸다. 아래와 같이 특정 포인터 변수를 초기화할 때는 실제로 메모리를 할당하기 전에 널로 초기화해야 한다.
char *ptr = NULL; ptr = (char*)malloc(...);
포인터 변수와 관련된 연산에서 값이 0인 정수 상수식이 나오면 이를 곧 널로 해석한다. 사실 널은 0, 혹은 (void*)0과 같도록 정의되어야 한다. 따라서 위의 코드는 아래와 같이 써도 된다.
char *ptr = 0; ptr = (char*)malloc(...);
흔히 아스키나 유니코드 상에서 널의 문자 값이 0이라 메모리 주소로 사용할 때의 널도 0일 거라고 생각하고 코딩을 하는 경우가 있는데 C 표준은 코드상에 직접 나타나는 0에 널이란 의미를 부여하고 있지만, 내부적으로 실제로 널 값이 뭐가 되는지는 규정하지 못하므로 위험하다. 이는 프로그램이 컴파일되는 시스템이 결정하며, 따라서 NULL != 0x0인 경우가 있다. 이런 경우에는 위의 char *ptr = 0;이란 코드가 실제로 ptr에 주솟값 0x0을 대입하지 않는다. 그 코드 자체가 '시스템에 정의된 NULL 값을 대입한다'라는 의미지 0x0을 대입한다는 의미가 아니기 때문이다. 하지만 워낙 많은 시스템에서 널을 0으로 정의하고 있었고, 오래전부터 내려온 코드들에 대해 호환성을 가지기 위해 개발 환경이 맞춰지다 보니 이제는 0이 아닌 환경을 생각도 하기 힘들어졌다. 다만 코드의 구체성을 높이기 위해서 0과 널을 다르게 만드는 프로그래밍 언어도 있다. 자바, 자바스크립트, PHP에서는 널은 널이지 0으로 대체할 수 없다. 단, PHP의 경우 주의할 사항이 있다.
$test = NULL; print ( $test == 0 ) ? "true" : "false" ; // (1) print ( $test === 0 ) ? "true" : "false" ; // (2)
위와 같이 비교하면 (1)은 '='이 2개이며 느슨한 비교이기 때문에 true가 출력되고, (2)는 '='이 3개이며 변수의 자료형까지 다 비교하므로 false가 출력된다. PHP 이외에도 이와 유사한 형태로 비교 연산자를 분리한 언어에서는 이와 유사한 식으로 인식한다. 리스프(Lisp)에서는 데이터 단위가 리스트인지라, 복수인 nil을 사용하며, false의 의미로도 사용한다. 오브젝트-C(Objective-C)에서는 NULL, nil, NIL 모두 기술적으론 똑같이 (void *0) 이긴 하지만 혼용해서는 안 된다. NULL은 고전적인 C 널 포인터, NIL은 클래스 널 포인터, nil은 인스턴스 널 포인터로 서로 역할이 나뉘어져 있다.[1]
런타임 환경
널 프로그램에서 사용할 수 있는 환경은 초기에 비어있는 3개의 큐와 임의의 2개의 양의 정수인 변수 'x'와 'y'로 구성된다. 'x'는 프로그램에 설정되고 'y'는 1로 설정된다.
그런 다음, 'x'를 'x'의 가장 작은 소인수로 나누고 'y'를 곱한 다음 소수 요소에 해당하는 명령어가 실행된다.
명령어
2 | 다음 (마지막일 경우 첫 번째) 큐 선택. |
---|---|
3 | 이전 (첫 번째일 경우 마지막) 큐 선택. |
5 | 선택한 큐의 앞에 있는 바이트를 (큐가 비어있으면 NUL을) 출력한다. 이 때 NUL은 '\0' 이다(아스키 코드 0). |
7 | 한 바이트를 입력받고 선택한 큐의 앞의 값을 덮어쓰며, 선택한 큐가 비어 있다면 입력받은 값을 큐에 추가한다. 현재 EOF를 입력받았을 때의 동작에 대해서는 정의되어 있지 않다. |
11 | 선택한 큐의 앞에 있는 바이트를 (큐가 비어있다면 0을) y에 뺀다. 만약 y가 음수가 되면 y를 0으로 설정한다. |
13 | 선택한 큐의 앞에 있는 바이트를 (큐가 비어있다면 0을) y에 더한다. |
17 | 선택한 큐의 앞에 y를 256으로 나눈 값을 집어넣는다. |
19 | 선택한 큐의 앞에 있는 바이트를 (선택한 큐가 비어있을 경우에는 0을) 꺼내서 다음 (마지막일 경우 첫 번째) 큐의 뒤에 추가한다. |
23 | 선택한 큐의 앞에 있는 바이트를 (선택한 큐가 비어있을 경우에는 0을) 꺼내서 이전 (첫 번째일 경우 마지막) 큐의 뒤에 추가한다. |
29 | 선택한 큐의 앞에 있는 바이트를 제거한다. |
31 | 선택한 큐의 끝에 y를 256으로 나눈 값을 추가한다. |
37 | 만약 선택한 큐가 비어있거나 선택한 큐의 앞의 바이트 값이 0이면 x를 가장 작은 소인수로 나누고 y에 곱한다. |
41 | 두 변수(x, y)의 값을 서로 변경. |
43 | 프로그램 종료 |
이 명령어는 항상 14번째 소수마다 반복된다. 예를 들어, 47은 2와 같은 역할을 한다.[3]
각주
참고자료
- 나무위키 - https://namu.wiki/w/Null#s-2.1
- 위키백과 - https://ko.wikipedia.org/wiki/Null_(SQL)
- 위키백과 - https://ko.wikipedia.org/w/index.php?title=NULL_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%96%B8%EC%96%B4)