NaN에 대해 알아보기
Javascript에서 숫자를 처리하는 방법
NaN에 대해 본격적으로 다루기 전에,
Javascript에서 숫자를 저장하는 방법에 대해 알아볼 필요가 있어요!
위의 이미지는 [ECMA]에서 정한 Number에 대한 정의입니다.
설명을 통해 다음과 같은 정보를 알 수 있어요.
- Javascript는 IEEE754라는 표준에 따라 Number value를 처리한다.
- `Not-a-Number(NaN)`, `양수 무한`, `음수 무한`도 Number value에 속한다.
IEEE754란?
전기전자공학자협회(IEEE)에서 정한 표준 중 하나입니다.
Javascript는 숫자를 다룰 때 IEEE754에서 정한 표준에 따라 처리하고 있어요.
더 알아보고 싶다면 이전 포스팅을 참고해주세요!
[양수0, 음수0 차이 쉽게 알아보기]
실제로 콘솔에 `typeof NaN`을 찍어보면 `'number'`를 출력합니다.
숫자 1과 동일한 타입을 가지고 있어요.
NaN의 특성
IEEE754표준에 따르면 NaN은 다음과 같은 특성을 갖고 있어요.
- NaN은 어떤 수와도 같지 않다.
- NaN은 자기 자신과도 같지 않다.
따라서 `NaN === NaN` 은 `false`라는
언뜻 보아서 이해할 수 없는 결과를 출력하게 됩니다. 😯
NaN 처리하는 방법
그렇다면 `NaN`을 `NaN`과 비교할 수 없다면
어떻게 `NaN`인지 확인할 수 있을까요?
isNaN()
isNaN의 로직
Javasciprt에서는 표준 내장 객체로 `isNaN`함수를 지원합니다.
isNaN(123) // false
isNaN('123') // false
isNaN('hello') // true
isNaN(true) // false
isNaN(null) // false
isNaN(undefined) // true
isNaN([]) // false
isNaN({}) // true
`isNaN`함수를 사용하여 `NaN`인지 아닌지 확인할 수 있어요.
그런데 조금 의문이 드는 것이 있어요.
- `'123'`은 문자열인데 `NaN`이 아니다.
- `true`는 `boolean`인데 `NaN`이 아니다.
- `null`은 `NaN`이 아닌데, `undefined`은 `NaN`이다.
- `[]`은 `NaN`이 아닌데, `{}`은 `NaN`이다.
이 궁금증을 해결하기 위해서 `isNaN`의 내부 동작을 살펴봅시다!
[ECMA]에서 설명하는 `isNaN`의 로직입니다.
1. 파라미터를 Number로 형변환한다.
2. 변환한 값이 `NaN`이면 `true`를 반환한다.
3. 그렇지 않으면 `false`를 반환한다.
위의 로직에서 중요한 것은 `isNaN`의 인수를 Number로 형변환을 한다는 것이에요.
예시로 한번 알아봅시다!
`isNaN('123')` 로직 따라가기
1. 파라미터 `'123'`을 `Number`로 변환한다.
2. `Number('123')`의 결과값은 `123`이다. 즉, `NaN`이 아니다.
3. 따라서 `false`를 반환한다.
`isNaN('hello')` 로직 따라가기
1. 파라미터 `'hello'`을 `Number`로 변환한다.
2. `Number('hello')`의 결과값은 `NaN`이다.
3. 따라서 `true`를 반환한다.
정리해보자면
`'asdf'` `undefined` `{}`
위의 세 가지는 Number로 변환했을 때 NaN을 반환하기 때문에 isNaN이 true가 되고,
`123` `'123'` `true` `null` `[]`
위의 네 가지는 Number로 변환했을 때 숫자를 반환하기 때문에 isNaN이 False가 됩니다.
Number()의 로직
그렇다면 이어서 궁금해지는 것은
`Number()`는 어떻게 동작하길래 어떤 것은 숫자를 반환하고 어떤 것은 `NaN`을 반환하는 것일까요?
ECMA에서 설명하는 Number로 형변환하는 과정입니다. (아까보다 조금 더 길죠..?)
대략적으로 살펴보자면 다음과 같습니다.
1. 인수가 Number 타입이라면 인수를 그대로 반환한다.
2. 인수가 Symbol 또는 Bigint 타입이라면 TypeError를 발생한다.
3. 인수가 `undefined`라면 `NaN`을 반환한다.
4. 인수가 `null` 또는 `flase`라면 `+0`을 반환한다.
5. 인수가 `true`라면 `1`을 반환한다.
6. 인수가 String 타입이라면 StringToNumber를 수행한 결과값을 반환한다.
7. 인수가 객체타입이라면 특정 조건에 따라 결과값을 반환한다.
(객체타입을 판단하는 조건은 아직 이해하지 못했어요 ㅜㅜ)
왜 `undefined`는 `NaN`을 반환하고 `null`은 `+0`을 반환하는지 궁금하신가요?
그건 ECMA에서 그렇게 하기로 결정한 것이기 때문에 이를 증명하는 방법은 없어요.
다만 그 이유를 추론해보자면,,
`undefined`는 값이 존재하지 않음을 의미하기 때문에 숫자로 표현될 수 없는 것이고,
`null`은 빈 값(이라는 값)을 의미하기 때문에 `0`과 유사하게 볼 수 있어서 숫자로 표현된 것이라고 생각해요!
복합한 isNaN()
NaN을 비교하고 싶었는데,
isNaN을 이해하기 위해선 Number의 복잡한 로직까지 알아야하네요..🥵
그래서 MDN에서도 결과값을 도출하기까지 너무 많은 if문을 거치는 isNaN()는
특별한 경우가 아니면 사용을 지양하고, 그 대신 Number.isNaN()을 사용하는 것을 제안하고 있습니다.
Number.isNaN()
`Number.isNaN()`은 표준 내장 함수인 `isNaN()`보다 간단하고 엄격하게 판단해요.
1. 인수가 Number타입이 아니라면 `false`를 반환한다.
2. 인수가 `NaN`이라면 `true`를 반환한다.
3. 그 외에는 `false`를 반환한다.
`Number.isNaN('123')` 로직 따라가기
1. 인수 `'123'`은 String 타입이기 때문에 `false`를 반환한다.
`Number.isNaN(123)`로직 따라가기
1. 인수 `123`은 Number 타입이다.
2. 인수 `123`은 `NaN`이 아니다.
3. 따라서 `false`를 반환한다.
`Number.isNaN(NaN)`로직 따라가기
1. 인수 `NaN`은 Number타입이다.
2. 인수 `NaN`은 `NaN`이기 때문에 true를 반환한다.
isNaN()과 Number.isNaN() 차이
`isNaN()`과의 가장 큰 차이점은 `Number.isNaN()`은 형변환을 하지 않는다는 것이에요.
그래서 `isNaN()`에서는 `true`를 반환하는 값도
`Number.isNaN()`에서는 `false`를 반환하는 경우도 있습니다.
value | isNaN(value) 결과 | Number.isNaN(value)결과 |
NaN | true | true |
undefined | true | false |
{} | true | false |
[] | false | false |
[1,2,3] | true | false |
[123] | false | false |
123 | false | false |
'123' | false | false |
'' | false | false |
'abc' | true | false |
true | false | false |
false | false | false |
마치며
평소에 `isNaN()`과 `Number.isNaN()`의 차이를 깊이 생각하지 않고 개발을 했었어요.
방금 찾아보니 이런 코드도 작성했더라구요. 🤣
isNaN(Number(value))
// 어짜피 isNaN이 형변환을 해주는데, Number로 굳이 형변환을 해주었다.
// isNaN의 동작을 이해하지 못하고 짠 코드이다.
반면 이런 코드는 적절한 사용법입니다.
// 두 인수가 number 타입인 것을 보장한다.
// 단, number 타입 중 연산이 되지 않는 NaN의 경우에만 error를 반환한다.
// 깔끔하고 안정적인 함수이다.
const add = (a: number, b: number) => {
if (Number.isNaN(a) || Number.isNaN(b)) return 'error'
return a + b
}
add(Number(x), Number(y))
역시 기본을 알아야 더 좋은 코드를 작성할 수 있고,
나아가서 어떤 것이 좋은 코드인지 분별할 수 있게 되는 것 같습니다!
처음엔 음수0과 양수0가 어떤 차이가 있는지 궁금해서 검색해보기 시작했는데,
질문이 꼬리에 꼬리를 물며 이 글까지 작성하게 되었네요.
저와 비슷한 궁금증을 갖고 계셨던 분들께 조금이나마 도움이 되었길 바라요. ☺️
추천글
'개발 > Javascript' 카테고리의 다른 글
[Javascript] 0.1 + 0.2가 0.3이 아닌 이유 쉽게 이해하기 (0) | 2024.06.23 |
---|---|
[Javascript] 양수0, 음수0 차이 쉽게 이해하기 (0) | 2024.06.23 |
[Javascript] 스크롤 이벤트로 비디오 재생 | 클론코딩 | 인터렉티브 웹 (0) | 2023.11.06 |
[Javascript] Hash 란? (0) | 2022.09.26 |
[토스페이먼츠 / Javascript] 자동 결제 연동하기 - SDK 결제창 이용 (0) | 2022.08.16 |