본문 바로가기

2주차

🤫 NaN을 조심하세요...

이 사진으로 4개의 차이 한 방에 이해 가능

 

안녕하세요 웹 YB 김다현입니다.

 

개발을 하다 보면 NaN이라는 친구를 마주칠 때가 있는데요. 보통 숫자를 다룰 때 등장하지만 진짜 숫자처럼 다루면 곤혹을,,, 치룰 수도 있는 함정 같은 친구랍니다. 코드가 의도치 않게 NaN을 반환하거나, NaN을 제대로 처리하지 않으면 예상치 못한 오류가 발생할 수 있어요. 이번 기회에 NaN의 개념을 확실히 이해하고 미리 실수를 방지해볼까요?  

 

🔎 그래서 NaN이 뭔데?

자바스크립트로 개발을 하다 보면 숫자가 있어야 할 자리에 뜬금없이 NaN이라는 값이 튀어나올 때가 있는데요. NaN은 "Not-a-Number"의 약자로, 말 그대로 숫자가 아니라는 의미랍니다. 자바스크립트는 주로 숫자를 다루는 계산이 이상하게 꼬였을 때 NaN을 반환해요.

 

예를 들어, 숫자가 아닌 값을 숫자로 나누거나 제곱근을 구하는 등 말이 안 되는 연산을 할 때 NaN이 등장합니다.

console.log(0 / 0);         // NaN
console.log(Math.sqrt(-1));  // NaN
console.log("hello" - 3);    // NaN

 

위 코드를 보면 0 / 0 이나 음수의 제곱근을 구하려고 할 때 NaN이 반환되는 걸 볼 수 있어요. 이런 계산은 논리적으로 성립하지 않기 때문에 자바스크립트가 "이건 숫자가 아니야" 라는 의미로 NaN을 돌려줘요.

 

🪄 NaN의 독특한 성질

NaN에는 다른 숫자와 구별되는 독특한 점이 하나 있는데요. 바로 NaN은 자기 자신과도 같지 않다는 거예요. 이게 무슨 뜻이지? 싶으실 것 같아 코드로 설명해볼게요. 

console.log(NaN === NaN); // false

 

다른 값들은 대부분 자기 자신과 같지만 NaN은 유일하게 NaN === NaN에서 false를 반환한답니다. 자바스크립트에서 NaN은 어떤 값과도 같지 않다고 판단해요. 심지어 자기 자신과도,,, ^^ 그래서 NaN이 코드에 있을 때 이를 감지하는 게 살짝 까다로울 수 있어요.

 

왜 그럴까요? 그건 바로 자바스크립트는 NaN을 "이건 확실히 숫자가 아니야" 라고 인식하고, 어떤 숫자와도 비교하지 않으려고 하기 때문이에요.

 

😵‍💫 그럼 어떻게 확인해?

다행히 자바스크립트에는 NaN을 감지하기 위한 함수들이 있어요. 근데 NaN을 감지할 때 사용하는 함수도 종류가 여러 가지라서 헷갈리기 쉬워요. (가지가지...) 그럼 하나씩 설명해볼게요!

 

isNaN() 함수 

이 함수는 값이 NaN인지 확인하는 기본적인 방법이에요. 하지만, isNaN()은 쓰면서도 조심해야 하는 함수예요. 왜냐하면, 숫자가 아닌 값에 대해서도 NaN으로 판별할 수 있기 때문에...!

console.log(isNaN("Hello"));     // true
console.log(isNaN(123));         // false
console.log(isNaN(NaN));         // true

 

"Hello" 같은 문자열이 NaN이 아님에도 불구하고 true가 반환되는 걸 볼 수 있어요. isNaN()은 숫자가 아니면 무조건 NaN으로 간주하는 경향이 있어서 정확하게 NaN만 감지하는 데는 적합하다고 볼 수 없어요. (ㅠㅠ)

 

Number.isNaN() 함수

이 문제를 해결하기 위해 나온 함수가 바로 Number.isNaN()이에요. Number.isNaN()은 정확하게 NaN인 경우에만 true를 반환해요. 즉, 숫자가 아니라고 해서 무조건 NaN으로 간주하지 않아요. 둘을 비교해볼게요.

console.log(isNaN("Hello"));           // true
console.log(Number.isNaN("Hello"));     // false

console.log(isNaN(NaN));                // true
console.log(Number.isNaN(NaN));         // true

 

"Hello"에 대해 Number.isNaN()은 false를 반환하는 걸 볼 수 있어요. 정확히 NaN인 경우에만 true를 반환하니까, NaN을 감지할 때는 Number.isNaN()을 사용하는 것이 훨씬 더 안전하겠죠? 😇

 

🥸 잠깐! 느슨한 비교와 엄격한 비교?

자바스크립트에서는 두 값을 비교할 때 사용할 수 있는 두 가지 연산자가 있는데요. 느슨한 동등 비교(==)와 엄격한 동등 비교(===)가 있어요. 이 두 연산자는 비교할 때 미~묘하게 다른 방식을 사용하는데, 이걸 알아두면 NaN뿐만 아니라 다른 값들을 비교할 때도 실수를 줄일 수 있답니다!

 

느슨한 비교(==)

==는 느슨한 비교라고 불리고, 서로 다른 타입의 값들도 비교할 수 있도록 타입을 자동으로 변환하려고 해요.

예를 들어, 문자열 "5"와 숫자 5를 느슨하게 비교하면 자바스크립트가 문자열을 숫자로 바꾼 후에! 비교해요.

console.log("5" == 5);      // true
console.log(false == 0);    // true

 

이렇게 타입 변환을 해가면서 비교하기 때문에 예상치 못한 결과가 나올 수 있어요. 그렇기 때문에 ==을 사용할 때는 주의하기로 약속,,,

 

엄격한 비교(===)

이 친구는 엄격한 비교라고 불리고, 타입이 다르면 무조건 false를 반환해요. 타입이 같을 때만 값을 비교하기 때문에 이 방식이 더 안전하다고 볼 수 있답니다.

console.log("5" === 5);     // false
console.log(false === 0);   // false

 

갑자기 이 두 친구를 언급한 이유에 대해 더 짚고 넘어가자면, 자바스크립트에서 NaN뿐만 아니라 다른 값들도 비교할 때 혼란스러울 수 있는 상황이 많기 때문이에요. NaN 말고도 타입 비교의 함정 ^^ 에 조심하기 위해 알아두면 좋을 것 같아 같이 설명해봤어요...ㅎㅎ

 

NaN인지 확인하고 싶을 때는 Number.isNaN()을 사용해야 하는 거 잊지 말기!

 

🐛 실제 코드에서 어떻게 버그가 발생할까?

실전에서 NaN이 어떻게 예기치 않은 문제를 일으킬 수 있는지 보기 위해 간단한 예제를 가져왔어요. 

계산 중간에 NaN이 발생하는 상황을 생각해볼게요. 예를 들어 유저로부터 여러 개의 숫자 입력을 받아 평균을 계산하는 코드가 있다고 가정해 봅시다!

let inputs = ["10", "20", "DahyunKim"];
let sum = 0;

inputs.forEach(input => {
    sum += Number(input);
});

let average = sum / inputs.length;
console.log(average);

 

이 코드에서는 DahyunKim을 숫자로 변환할 때 NaN이 발생하고, 결국 sum에 NaN이 더해지면서 전체 계산이 NaN으로 변질됩니다. 평균을 계산하려 했지만 NaN이 나오고 예상치 못한 결과가 발생하는 이슈가 생겨요. ㅠㅠ

 

이럴 때는 각 입력 값이 NaN인지 확인한 후에만 더하도록 하면 오류를 방지할 수 있어요.

inputs.forEach(input => {
    let num = Number(input);
    if (!Number.isNaN(num)) {
        sum += num;
    }
});

 

이렇게 하면 정확한 숫자 값만 더하게 되어 NaN이 포함된 오류를 방지할 수 있답니다!

 

🍯 세 줄 요약이자 NaN을 안전하게 다루기 위한 꿀팁!

  • NaN인지 확인할 때는 Number.isNaN()을 사용하기!
    isNaN()은 정확히 NaN이 아닌 값들도 true로 판단할 수 있으니 주의 필요

  • 비교 연산에서는 ==보다는 ===를 사용하기!
    불필요한 타입 변환을 줄여주기 때문에 오류가 줄어들 거예요...

  • NaN이 발생할 가능성이 있다면 미리 NaN을 감지하고 처리하는 습관 들이기 ^_^