본문 바로가기

개발/Javascript

[Javascript] NaN 알아보기

반응형

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`함수를 지원합니다.

[mdn : 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 isNaN()

그래서 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] 양수0, 음수0 차이 쉽게 이해하기

+0과 -0 알아보기수학적으로 0은 음수와 양수의 사이에 차이가 없기 때문에 같은 값으로 봅니다.하지만 컴퓨터 세계에서는 `+0`과 `-0`은 엄밀히 말하면 다릅니다.javascript 콘솔에서 `-0`을 입력하면

shelly-log.tistory.com

 

[Javascript] 0.1 + 0.2가 0.3이 아닌 이유 쉽게 이해하기

지난 글 복습하기지난 포스팅에서 javascript에서 `-0`이 존재하는 이유에 대해 알아보았어요.[양수 0, 음수 0 차이 쉽게 이해하기] 위의 글에서 이야기했던 `-0`이 존재하는 이유와 오늘 글의 주제와

shelly-log.tistory.com

 

반응형