다른 프로그래밍 언어를 사용하다가 자바스크립트를 사용하게 되면 많은 부분이 어색하겠지만, 나의 경우에는 'undefined'라는 키워드가 이상하게 다가왔다. 하지만 깊이 공부하지 않고 의미 그대로 '정의되지 않았다'라는 뜻으로 인지하고 넘어갔다.
하지만 큰 의문은 그 후에도 찾아 왔다. 바로 'null' 때문이다. 겉보기에는 서로 다르지만 속은 참 비슷해 보이는 이 두 단어에 때문에 이번에 한 번 짚고 넘어가려한다.
const a = ;
console.log(a) // 예상 "null"
console.log(b) // 예상 "undefined"
사전적 의미로는 마치 이래야 할 것 처럼 보인다. 하지만 그렇지 않다. 위와 비슷하면서도 조금은 다른 결과가 나온다.
자바스크립트에서 null이 실질적으로 뜻하는 것을 알아보기 전에 다른 프로그래밍 언어의 null을 살펴보면 이해하기 쉽다. 자바에서의 null은 값이 할당 되기전에 임의로 들어가는 값이라고 할 수 있다. null은 아직 값이 정의 되지 않은 속성이나 객체에 대신 그 값으로 들어가고 후에 다른 원시타입의 값으로 변경된다. 이와 같은 과정은 컴파일에서 처리된다.
마찬가지로 자바스크립트에서의 null은 자바에서 null의 역할을 약간 축소했다. null은 값이 존재하지 않을 때, null이라는 값을 부여해서 나타나는 상값이고, undefined는 변수는 선언 되 었지만 아직 값이 할당 되지 않았을 때, 할당되는 값이다.
const a = null;
let b;
console.log(a) // null;
console.log(b) // undefined;
null == undefined; // true
null == false // false
undefined == false // false
자바스크립트 내 부에서 이 두값은 같다고 표시된다. 완전히 같은 값은 아니지만 비교 연산자 "==" 는 null과 undefined 모두같은 의미로 인식한다. 이 두 값은 if 문 안에서 false로 인지된다. 하지만 둘 모두 false와는 같지 않다고 나온다. 많이 혼란스럽다.
null === undefined; // false
하지만 비교 연산자 '===' 는 해당 변수의 type 서로 같은지도 확인하므로 false가 나온다.
자바를 하고 오셨다면 null이 자바에서는 객체가 아니란 것을 알고 있을 것이다. 하지만 놀랍게도 자바스크립트에서는 객체로 취급 된다. 그 이유는 자바스크립트의 모든 원시타입들이 객체로 체이닝 되어 만들어져 있고, 이 때 null은 object를 타입을 가진다. 그런데 가장 기저부분의 object 타입을 가본으로 타입이 아니라 정의 한다면 null은 자바처럼 하나의 키워드로 동작한다.
undefined는 'undefined' 타입이다.
typeof null //object
typeof undefined //undefined
이러한 특성 때문에 이 두 개의 값을 다루기가 쉽지가 않다.
다른 언어에너는 null 예외가 기본적으로 구현 되어 있지만 자바스크립트는 구현되어 있지 않다. 하지만 ES6+는 optional 연산자를 제공해주어서 undefined 를 예외처리할 수 있다. 그런데 이 방법은 예외의 경우를 상정하지 않기 때문에 코드상에 예기치 못한 undefined값이 전달 될 수 있다.
그리고 null과 undefined는 그 차제의 의미가 다르기 때문에 서로 같은 값으로 예외처리할 때 계획했던 것과 프로그램이 다르게 동작할 가능성이 있다. 그래서 예외를 처리하는 두 가지 방법을 소개하여 어떻게 자바스크립트에서 효과적으로 null과 undefined를 처리할 수 있을지 살펴 보자
try{
const a = async request();
if(a === null || a === undefined) throw new Error("예상 못한 값");
console.log("값이 들어옴");
}catch(error){
console.log(error.message);
}finally{
console.log("완료");
}
다양한 프로그래밍 언어에서 제공하는 try catch 와 매우 비슷하다. try 구문을 실행하고 구문 실행중 에러가 발생하면 이 에러를 catch가 잡아서 예외를 처리할 수 있다. null과 undefined의 예외 처리도 이 구문을 이용하여 간단히 처리가 가능하다.
어떤 사람은 null과 undefined를 같은 선상에 놓고 예외를 처리할 수 도 있지만, 어떤 프로그램의 경우에는 서로 다른 예외처리를 요구하는 경우가 있을 수 있다. 이 때 조건문 안에서 "==="연산자를 활용하여 해결할 수 있다. 그럼에도 자바스크립트에서는 위 두 가지 타입 말고도 예외를 처리 할 때 조금 애매한 값들이 있다. 첫번 째는 "NaN" 두번째는 "-0"이다.
NaN === NaN // false
Object.is(NaN, NaN) //true
+0 === -0 // true
Object.is(+0,-0) // falsed
위 코드상에서 "===" 보다 "Object.is()"가 값을 중요하게 본다는 것을 알 수가 있다. 그래서 숫자의 값이나, 문자열을 비교할 때에는 "==="를 사용하는 것이 좋지만, undefined와 null, NaN등의 예외처리를 할 때에는 좀 더 명확한 Object.is() 를 사용하는게 좋아보인다.
try{
const a = async request();
if(Object.is(a, null)) throw new Error("null");
if(Object.is(a, undefined)) throw new Error("undefined");
if(Object.is(a, NaN)) throw new Error("NaN");
console.log("값이 들어옴");
}catch(error){
console.log(error.message);
}finally{
console.log("완료");
}
위의 try...catch에 Object.is를 적용한 코드이다.
NaN은 원시타입이나 null이 아닌 Number의 Property이다.
이 패턴은 값의 유무에 따라 프로그램의 동작을 제어하는 패턴이다. 우선 Maybe를 통해 프로그램의 동작을 예상하고, 만약 값이 존재한다면 Just 의 프로그램이 실행 되고, 존재하지 않는다면 'Nothing'를 실행시킨다. 이 패턴은 함수형 프로그래밍으로 만들기 쉽다.
const exist = (value) => !Object.is(value,undefined);
const nothing = () => console.log("색 고르는 중"); // Nothing
const maybe = (fn) => (value)=> exist(value) ? fn(value) : nothing();
const draw = (color) => console.log(color,"으로 그렸다."); // Just
const maybeDraw = mabe(draw)
console.log(maybeDraw("red")) // red으로 그렸다.
console.log(maybeDraw()) // 색 고르는 중
이 글을 쓰면서 계속 고민했던 것은 null을 변수에 담아 사용해도 괜찮을까 였다. 프로그램이 잘 동작하기 위해서는 로직도 중요하지만 중간중간 들어오는 외부의 값에 영향을 받지 않아야 한다. 사용자의 입력, 데이터베이스에 프로그램은 알지 못하는 값 혹은 'bad request'등 다양한 곳에서 이상한 값이 온다. 이에 더해서 사용자마저 null을 이용하는 것은 좋은 선택이 아닌 것 같다.
하지만 undefined의 경우에는 값이 할당 되지 않은 모든 변수들이 가지는 값이므로 initial value를 생성할 때에 충분히 사용하도 좋은 것 같다. 자바스크립트에서도 optional property, arguments를 제공해주기 때문에 예외를 생각하며 잘 사용한다면 프로그래밍적으로 한 단계 더 성장 할 수도 있다.
한 가지 더 붙이자면 프로그래밍 외적으로는 사용자의 입력값이나 데이터베이스에 저장 된 data는 개발자가 오류를 가정하기 힘들기 때문에 어렵게 예외처리해야하지만, 프로그래밍 내적으로는 "타입 스크립트"를 사용한다면 다양한 오류들을 쉽게 놓치지 않을 수 있을 것이다.
Handling null and undefined in JavaScript
How to Check for
null
in JavaScriptJavaScript — Null vs. Undefined