LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까

2026년 6월 28일·9분 읽기

이 시리즈를 왜 시작하는가

자바스크립트 퀴즈북 리마인드는 책의 모든 문제를 다시 풀이하는 연재가 아닙니다. 목적은 조금 좁습니다. 프론트엔드 코드를 읽고 리뷰할 때 자주 헷갈리는 언어 모델을 다시 잡는 것입니다.

자바스크립트는 익숙한 만큼 위험합니다. number, ==, 객체 참조, 클로저, 이벤트 루프 같은 주제는 평소에는 자연스럽게 넘어가지만, 버그가 생기면 “왜 이렇게 동작하지?”로 돌아옵니다. 이 연재는 그 순간에 필요한 판단 기준을 남기는 쪽에 가깝습니다.

진행 방식은 매일 하나의 문제 상황에서 시작합니다. 먼저 흔한 직관을 꺼내고, 실제 엔진/스펙 모델을 정리한 뒤, 프론트엔드 코드 리뷰에서 무엇을 확인해야 하는지로 내려옵니다. 마지막에는 짧은 퀴즈를 붙여서 읽은 내용을 바로 점검합니다.

Day 1은 숫자입니다. 화면에는 단순한 숫자로 보이지만, 내부에서는 IEEE 754, safe integer, NaN, -0, BigInt가 같이 움직입니다.

---

오늘의 문제

먼저 이런 코드를 봅니다.

js
console.log(0.1 + 0.2 === 0.3);
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2);
console.log(NaN === NaN);
console.log(Object.is(-0, 0));

처음 보면 이상합니다. 전부 숫자를 비교하는 코드인데 결과가 직관과 자주 어긋납니다.

text
false
true
false
false

오늘 목표는 “자바스크립트 숫자는 이상하다”에서 끝내는 게 아닙니다. 프론트엔드 코드를 읽을 때 다음 질문을 할 수 있게 되는 것입니다.

text
이 값은 계산용 숫자인가?
정확한 정수 식별자인가?
소수점 오차가 화면/검증/서버 요청에 영향을 주는가?
NaN, Infinity, -0 같은 예외값이 UI 상태로 흘러올 수 있는가?

이 질문을 할 수 있으면 숫자 버그를 단순 암기가 아니라 코드 리뷰의 체크포인트로 다룰 수 있습니다.

---

흔한 착각: 숫자는 그냥 숫자다

자바스크립트에는 number 타입 하나로 정수와 실수를 대부분 표현합니다.

js
typeof 1; // "number"
typeof 1.5; // "number"
typeof NaN; // "number"
typeof Infinity; // "number"

그래서 처음에는 이렇게 생각하기 쉽습니다.

text
1은 정수고,
1.5는 실수고,
NaN은 숫자가 아니고,
Infinity는 특별한 값이겠지.

하지만 자바스크립트의 number는 대체로 IEEE 754 double precision floating-point입니다. 즉 많은 값이 “정확한 십진수”가 아니라 이진 부동소수점으로 표현됩니다.

이 말은 두 가지를 뜻합니다.

소수 계산은 사람이 기대하는 십진수 계산과 다를 수 있다. 안전한 정수 범위를 벗어나면 정수도 정확히 구분하지 못할 수 있다.

---

Number 모델을 한 장으로 보기

JavaScript Number, safe integer, NaN, -0, BigInt를 한 장으로 정리한 다이어그램

이 그림에서 중요한 건 Number가 하나의 단순한 정수 상자가 아니라는 점입니다. 같은 number 안에 일반 소수, 큰 정수 근사값, NaN, Infinity, -0 같은 값이 같이 들어옵니다.

프론트엔드에서 이 차이는 생각보다 자주 튀어나옵니다.

가격/비율/퍼센트 계산 입력값 파싱 서버에서 내려온 큰 ID 차트 축 계산 페이지네이션 카운트 애니메이션 진행률 날짜/시간 timestamp

숫자는 화면에서는 그냥 텍스트로 보이지만, 내부에서는 표현 방식의 한계를 계속 갖고 있습니다.

---

1. `0.1 + 0.2`는 왜 `0.3`이 아닐까

js
0.1 + 0.2; // 0.30000000000000004
0.1 + 0.2 === 0.3; // false

자바스크립트가 덧셈을 못해서가 아닙니다. 0.1과 0.2가 이진 부동소수점으로는 정확히 떨어지지 않기 때문입니다.

십진수에서 1 / 3을 정확히 적기 어려운 것과 비슷합니다.

text
1 / 3 = 0.3333333333...

이진수에서도 어떤 십진 소수는 끝없이 반복됩니다. 0.1, 0.2가 그런 값입니다. 그래서 아주 작은 오차가 생기고, 그 오차가 === 비교에서 드러납니다.

프론트엔드에서는 보통 이런 방식으로 다룹니다.

js
const nearlyEqual = (a, b, epsilon = Number.EPSILON) =>
  Math.abs(a - b) < epsilon;

nearlyEqual(0.1 + 0.2, 0.3); // true

다만 Number.EPSILON을 무조건 쓰면 된다는 뜻은 아닙니다. 값의 규모가 커지면 허용 오차도 도메인에 맞게 잡아야 합니다.

js
const almostSamePrice = (a, b) => Math.abs(a - b) < 0.01;

금액이면 보통 소수 계산을 직접 누적하기보다 정수 단위로 바꾸는 편이 안전합니다.

js
// 나쁜 쪽에 가까움: 달러 금액을 소수로 계속 계산
const total = 19.99 + 9.99;

// 더 안전한 쪽: 센트 단위 정수로 계산 후 표시
const totalCents = 1999 + 999;
const label = `$${(totalCents / 100).toFixed(2)}`;

---

2. 안전한 정수 범위는 왜 필요할까

자바스크립트에는 안전하게 표현할 수 있는 정수 범위가 있습니다.

js
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MIN_SAFE_INTEGER; // -9007199254740991

이 범위를 넘으면 정수 간격을 모두 표현하지 못합니다.

js
const max = Number.MAX_SAFE_INTEGER;

console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992
console.log(max + 1 === max + 2); // true

서버가 큰 ID를 숫자로 내려주면 위험할 수 있습니다.

json
{
  "id": 9007199254740993,
  "name": "post"
}

정확히는 API 응답이 JSON.parse를 지나 자바스크립트 값이 되는 순간 number 모델에 들어갑니다. 이때 이미 정확도가 깨질 수 있습니다. 이런 ID는 계산할 숫자가 아니라 식별자입니다. 문자열로 받는 편이 안전합니다.

json
{
  "id": "9007199254740993",
  "name": "post"
}

코드 리뷰에서는 이렇게 물어봐야 합니다.

text
이 숫자는 더하고 빼는 값인가?
아니면 정확히 보존해야 하는 식별자인가?

식별자라면 number보다 string이 더 안전한 모델일 때가 많습니다.

---

3. `NaN`은 숫자가 아니지만 number다

NaN은 “Not a Number”입니다. 이름만 보면 숫자가 아닌 것 같습니다. 그런데 타입은 number입니다.

js
typeof NaN; // "number"

NaN은 주로 숫자로 해석할 수 없는 연산에서 나옵니다.

js
Number("hello"); // NaN
parseInt("px", 10); // NaN
Math.sqrt(-1); // NaN

특이한 점은 NaN이 자기 자신과도 같지 않다는 것입니다.

js
NaN === NaN; // false

그래서 NaN 확인에는 Number.isNaN을 쓰는 편이 안전합니다.

js
Number.isNaN(NaN); // true
Number.isNaN("hello"); // false
isNaN("hello"); // true

전역 isNaN은 먼저 숫자 변환을 시도합니다. 그래서 문자열도 true가 나올 수 있습니다. 입력값 검증에서는 이 차이가 중요합니다.

js
const value = Number(input.value);

if (Number.isNaN(value)) {
  showError("숫자로 입력해주세요.");
}

---

4. `-0`은 왜 신경 써야 할까

자바스크립트에는 0과 -0이 둘 다 있습니다.

js
-0 === 0; // true
Object.is(-0, 0); // false

대부분의 UI에서는 큰 문제가 아닙니다. 하지만 계산 방향이나 비율을 다루면 가끔 드러납니다.

js
1 / 0; // Infinity
1 / -0; // -Infinity

차트, 좌표, 스크롤 방향, 제스처 속도처럼 “방향”이 의미 있는 값에서는 -0이 디버깅을 헷갈리게 만들 수 있습니다.

예를 들어 화면에는 0으로 보이는데 내부 계산은 음수 방향을 유지할 수 있습니다.

js
const velocity = -0;

Object.is(velocity, -0); // true

이런 값을 화면에 보여줄 때는 도메인에 맞게 정규화하는 것도 방법입니다.

js
const normalizeZero = (value) => (Object.is(value, -0) ? 0 : value);

다만 모든 곳에서 -0을 없애야 한다는 뜻은 아닙니다. 방향 정보가 필요하면 보존해야 합니다. 중요한 건 “그 값이 도메인에서 의미가 있는가”입니다.

---

5. BigInt는 큰 정수를 위한 별도 길이다

안전한 정수 범위를 넘는 계산이 필요하면 BigInt를 쓸 수 있습니다.

js
const id = 9007199254740993n;
typeof id; // "bigint"

BigInt는 정수를 정확하게 다룰 수 있지만, number와 자동으로 섞이지 않습니다.

js
1n + 1; // TypeError
1n + 1n; // 2n

이 제약은 불편하지만 오히려 장점이기도 합니다. 서로 다른 숫자 모델을 조용히 섞으면 더 위험하기 때문입니다.

프론트엔드에서는 BigInt를 무조건 쓰기보다 먼저 경계를 봐야 합니다.

상황더 나은 선택
큰 ID를 표시/전달만 한다string
큰 정수를 실제로 계산한다bigint
소수 계산이 필요하다number 또는 decimal 라이브러리
금액 계산이 필요하다최소 단위 정수 또는 전용 decimal 모델

BigInt는 JSON에서 바로 직렬화되지 않는 점도 주의해야 합니다.

js
JSON.stringify({ id: 1n }); // TypeError

API 경계에서는 문자열로 주고받는 설계가 더 단순할 때가 많습니다.

---

프론트엔드 코드 리뷰 체크포인트

숫자 코드를 볼 때는 문법보다 의도를 먼저 봐야 합니다.

text
1. 이 값은 계산해야 하는 숫자인가, 정확히 보존해야 하는 식별자인가?
2. 소수 계산 결과를 ===로 비교하고 있지 않은가?
3. 서버에서 내려온 큰 정수를 number로 받고 있지 않은가?
4. 입력값 파싱 결과가 NaN일 때 UI 상태가 깨지지 않는가?
5. Infinity, -Infinity, -0이 화면/차트/요청 파라미터로 흘러가지 않는가?
6. BigInt가 필요한 문제인지, 문자열 ID면 충분한 문제인지 구분했는가?

특히 AI가 만든 코드를 리뷰할 때는 number를 너무 쉽게 쓰는지 봐야 합니다. AI는 id: number, price: number, amount: number처럼 그럴듯한 타입을 자주 만듭니다. 하지만 프론트엔드에서 중요한 건 “숫자 타입이 맞나”가 아니라 “이 값의 정확도와 표현 방식이 도메인에 맞나”입니다.

---

오늘의 정리

자바스크립트 number는 대부분 IEEE 754 double이다. 소수 계산은 작은 오차를 가질 수 있다. 안전한 정수 범위는 ±(2^53 - 1)이다. 큰 ID는 number보다 string으로 보존하는 편이 안전할 수 있다. NaN은 number 타입이지만 자기 자신과 같지 않다. -0은 0처럼 보이지만 Object.is와 나눗셈에서 차이가 드러난다. BigInt는 큰 정수를 정확히 다루지만 number와 자동으로 섞이지 않는다.

---

참고한 자료

『프런트엔드 레벨을 높이는 자바스크립트 퀴즈북』 CHAPTER 01 타입 MDN Web Docs, [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) MDN Web Docs, [Number.isNaN()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) MDN Web Docs, [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) MDN Web Docs, [Object.is()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)

---

퀴즈

Quiz1 / 4
Q.다음 코드의 결과로 가장 맞는 것은 무엇일까요?
js
`console.log(0.1 + 0.2 === 0.3);`

Post Q&A

오케이징에게 물어보기

자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/study/javascript-quizbook
파일 13개, 폴더 0개
자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네 가지 방법자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와 ToPrimitive 흐름자바스크립트 퀴즈북 리마인드 Day 4: 프로퍼티 descriptor와 freeze의 경계자바스크립트 퀴즈북 리마인드 Day 5: 참조, 복사, Map/Set의 메모리 감각자바스크립트 퀴즈북 리마인드 Day 6: 스코프 체인과 호이스팅자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된 값자바스크립트 퀴즈북 리마인드 Day 8: 이벤트 루프와 Promise 타이밍자바스크립트 퀴즈북 리마인드 Day 9: this는 호출한 쪽이 정한다자바스크립트 퀴즈북 리마인드 Day 10: 함수는 값이고 경계다자바스크립트 퀴즈북 리마인드 Day 11: class를 봐도 프로토타입을 읽는다자바스크립트 퀴즈북 리마인드 Day 12: 이벤트는 target에서 끝나지 않는다자바스크립트 퀴즈북 리마인드 Day 13: 모듈은 파일 묶음이 아니라 의존성 그래프다

같은 섹션의 대표 이미지

13 posts · latest first
ESM live binding과 dynamic import, circular dependency 리뷰 포인트를 보여주는 module graph 다이어그램
Study26. 07. 09.

자바스크립트 퀴즈북 리마인드 Day 13: 모듈은 파일 묶음이.

ESM live binding, default/named export, circular dependency, dynamic import, CJS와 ESM 차이를 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 07. 09.SEOJing
DOM 이벤트가 capture 단계로 내려가고 bubble 단계로 올라가는 경로 다이어그램
Study26. 07. 08.

자바스크립트 퀴즈북 리마인드 Day 12: 이벤트는.

DOM 이벤트의 capture/bubble, delegation, default action, React synthetic event 경계를 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 07. 08.SEOJing
인스턴스에서 프로토타입과 Object.prototype으로 이어지는 프로퍼티 조회 경로 다이어그램
Study26. 07. 07.

자바스크립트 퀴즈북 리마인드 Day 11: class를 봐도.

프로토타입 조회, class 문법, 인스턴스 필드와 prototype 메서드 차이를 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 07. 07.SEOJing
입력, 순수 계산, 출력, 부수 효과 경계를 나누어 함수 설계를 보여주는 다이어그램
Study26. 07. 06.

자바스크립트 퀴즈북 리마인드 Day 10: 함수는 값이고 경계다.

고차 함수, 순수 함수, 부수 효과, async 함수의 Promise 반환을 코드 리뷰에서 어떻게 읽을지 정리합니다.

26. 07. 06.SEOJing
메서드 호출, 분리된 함수, 이벤트 콜백, bind와 arrow function의 this 차이를 보여주는 다이어그램
Study26. 07. 05.

자바스크립트 퀴즈북 리마인드 Day 9: this는 호출한 쪽이.

this 바인딩을 정의 위치가 아니라 호출 형태, 메서드 분리, arrow function, bind 기준으로 읽는 방법을 코드 리뷰 관점에서 정리합니다.

26. 07. 05.SEOJing
콜 스택이 비워진 뒤 마이크로태스크 큐가 먼저 실행되고 다음 태스크로 넘어가는 이벤트 루프 다이어그램
Study26. 07. 04.

자바스크립트 퀴즈북 리마인드 Day 8: 이벤트 루프와.

콜 스택, 태스크 큐, 마이크로태스크 큐를 기준으로 Promise와 setTimeout 실행 순서를 코드 리뷰 관점에서 정리합니다.

26. 07. 04.SEOJing
클로저가 생성 위치의 바인딩을 기억해 나중 실행에서 오래된 값을 읽을 수 있음을 보여주는 다이어그램
Study26. 06. 29.

자바스크립트 퀴즈북 리마인드 Day 7: 클로저 버그와 오래된.

클로저가 값 복사가 아니라 렉시컬 환경 참조라는 점을 stale closure, 루프 콜백, React 이벤트 리뷰 관점에서 정리합니다.

26. 06. 29.SEOJing
스코프 체인과 호이스팅 관계를 단순화한 다이어그램
Study26. 06. 28.

자바스크립트 퀴즈북 리마인드 Day 6: 스코프 체인과 호이스팅.

스코프 체인, 렉시컬 환경, var/let/const 호이스팅 차이를 콜백·상태 버그 리뷰 관점에서 다시 정리합니다.

26. 06. 28.SEOJing
JavaScript 객체 참조와 Map, Set, WeakMap의 참조 구조를 비교한 다이어그램
Study26. 06. 27.

자바스크립트 퀴즈북 리마인드 Day 5: 참조, 복사,.

객체 참조와 얕은 복사, Object와 Map/Set의 차이, WeakMap/WeakSet이 필요한 메모리 상황을 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 27.SEOJing
JavaScript 프로퍼티 descriptor가 data descriptor와 accessor descriptor로 갈라지는 구조 다이어그램
Study26. 06. 26.

자바스크립트 퀴즈북 리마인드 Day 4: 프로퍼티.

객체 프로퍼티를 key/value가 아니라 descriptor로 읽는 법, getter/setter와 defineProperty, preventExtensions/seal/freeze의 경계를 코드 리뷰 관점에서 정리합니다.

26. 06. 26.SEOJing
객체가 ToPrimitive를 거쳐 + 연산자의 문자열 연결 또는 숫자 덧셈으로 갈라지는 흐름 다이어그램
Study26. 06. 25.

자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와.

객체가 원시값으로 바뀌는 ToPrimitive 흐름, + 연산자의 문자열 연결과 숫자 덧셈 분기, Symbol.toPrimitive가 코드 리뷰에서 왜 중요한지 정리합니다.

26. 06. 25.SEOJing
==, ===, Object.is, SameValueZero의 차이를 정리한 JavaScript equality 다이어그램
Study26. 06. 24.

자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네.

==, ===, Object.is, SameValueZero가 각각 어떤 비교 알고리즘을 쓰는지 정리하고, includes와 indexOf, Map/Set 키 비교에서 생기는 프론트엔드 리뷰 포인트를 잡습니다.

26. 06. 24.SEOJing
JavaScript Number, safe integer, NaN, -0, BigInt를 한 장으로 정리한 다이어그램
Study26. 06. 23.

자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을.

자바스크립트의 Number가 왜 정수처럼 보여도 부동소수점 모델 위에서 움직이는지, NaN과 -0, safe integer, BigInt를 프론트엔드 코드 리뷰 관점에서 다시 정리합니다.

26. 06. 23.SEOJing