자바스크립트 퀴즈북 리마인드 Day 13: 모듈은 파일 묶음이.
ESM live binding, default/named export, circular dependency, dynamic import, CJS와 ESM 차이를 프론트엔드 코드 리뷰 관점에서 정리합니다.
자바스크립트 퀴즈북 리마인드는 책의 모든 문제를 다시 풀이하는 연재가 아닙니다. 목적은 조금 좁습니다. 프론트엔드 코드를 읽고 리뷰할 때 자주 헷갈리는 언어 모델을 다시 잡는 것입니다.
자바스크립트는 익숙한 만큼 위험합니다. number, ==, 객체 참조, 클로저, 이벤트 루프 같은 주제는 평소에는 자연스럽게 넘어가지만, 버그가 생기면 “왜 이렇게 동작하지?”로 돌아옵니다. 이 연재는 그 순간에 필요한 판단 기준을 남기는 쪽에 가깝습니다.
진행 방식은 매일 하나의 문제 상황에서 시작합니다. 먼저 흔한 직관을 꺼내고, 실제 엔진/스펙 모델을 정리한 뒤, 프론트엔드 코드 리뷰에서 무엇을 확인해야 하는지로 내려옵니다. 마지막에는 짧은 퀴즈를 붙여서 읽은 내용을 바로 점검합니다.
Day 1은 숫자입니다. 화면에는 단순한 숫자로 보이지만, 내부에서는 IEEE 754, safe integer, NaN, -0, BigInt가 같이 움직입니다.
---
먼저 이런 코드를 봅니다.
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));처음 보면 이상합니다. 전부 숫자를 비교하는 코드인데 결과가 직관과 자주 어긋납니다.
false
true
false
false오늘 목표는 “자바스크립트 숫자는 이상하다”에서 끝내는 게 아닙니다. 프론트엔드 코드를 읽을 때 다음 질문을 할 수 있게 되는 것입니다.
이 값은 계산용 숫자인가?
정확한 정수 식별자인가?
소수점 오차가 화면/검증/서버 요청에 영향을 주는가?
NaN, Infinity, -0 같은 예외값이 UI 상태로 흘러올 수 있는가?이 질문을 할 수 있으면 숫자 버그를 단순 암기가 아니라 코드 리뷰의 체크포인트로 다룰 수 있습니다.
---
자바스크립트에는 number 타입 하나로 정수와 실수를 대부분 표현합니다.
typeof 1; // "number"
typeof 1.5; // "number"
typeof NaN; // "number"
typeof Infinity; // "number"그래서 처음에는 이렇게 생각하기 쉽습니다.
1은 정수고,
1.5는 실수고,
NaN은 숫자가 아니고,
Infinity는 특별한 값이겠지.하지만 자바스크립트의 number는 대체로 IEEE 754 double precision floating-point입니다. 즉 많은 값이 “정확한 십진수”가 아니라 이진 부동소수점으로 표현됩니다.
이 말은 두 가지를 뜻합니다.
소수 계산은 사람이 기대하는 십진수 계산과 다를 수 있다. 안전한 정수 범위를 벗어나면 정수도 정확히 구분하지 못할 수 있다.
---
이 그림에서 중요한 건 Number가 하나의 단순한 정수 상자가 아니라는 점입니다. 같은 number 안에 일반 소수, 큰 정수 근사값, NaN, Infinity, -0 같은 값이 같이 들어옵니다.
프론트엔드에서 이 차이는 생각보다 자주 튀어나옵니다.
가격/비율/퍼센트 계산 입력값 파싱 서버에서 내려온 큰 ID 차트 축 계산 페이지네이션 카운트 애니메이션 진행률 날짜/시간 timestamp
숫자는 화면에서는 그냥 텍스트로 보이지만, 내부에서는 표현 방식의 한계를 계속 갖고 있습니다.
---
0.1 + 0.2; // 0.30000000000000004
0.1 + 0.2 === 0.3; // false자바스크립트가 덧셈을 못해서가 아닙니다. 0.1과 0.2가 이진 부동소수점으로는 정확히 떨어지지 않기 때문입니다.
십진수에서 1 / 3을 정확히 적기 어려운 것과 비슷합니다.
1 / 3 = 0.3333333333...이진수에서도 어떤 십진 소수는 끝없이 반복됩니다. 0.1, 0.2가 그런 값입니다. 그래서 아주 작은 오차가 생기고, 그 오차가 === 비교에서 드러납니다.
프론트엔드에서는 보통 이런 방식으로 다룹니다.
const nearlyEqual = (a, b, epsilon = Number.EPSILON) =>
Math.abs(a - b) < epsilon;
nearlyEqual(0.1 + 0.2, 0.3); // true다만 Number.EPSILON을 무조건 쓰면 된다는 뜻은 아닙니다. 값의 규모가 커지면 허용 오차도 도메인에 맞게 잡아야 합니다.
const almostSamePrice = (a, b) => Math.abs(a - b) < 0.01;금액이면 보통 소수 계산을 직접 누적하기보다 정수 단위로 바꾸는 편이 안전합니다.
// 나쁜 쪽에 가까움: 달러 금액을 소수로 계속 계산
const total = 19.99 + 9.99;
// 더 안전한 쪽: 센트 단위 정수로 계산 후 표시
const totalCents = 1999 + 999;
const label = `$${(totalCents / 100).toFixed(2)}`;---
자바스크립트에는 안전하게 표현할 수 있는 정수 범위가 있습니다.
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MIN_SAFE_INTEGER; // -9007199254740991이 범위를 넘으면 정수 간격을 모두 표현하지 못합니다.
const max = Number.MAX_SAFE_INTEGER;
console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992
console.log(max + 1 === max + 2); // true서버가 큰 ID를 숫자로 내려주면 위험할 수 있습니다.
{
"id": 9007199254740993,
"name": "post"
}정확히는 API 응답이 JSON.parse를 지나 자바스크립트 값이 되는 순간 number 모델에 들어갑니다. 이때 이미 정확도가 깨질 수 있습니다. 이런 ID는 계산할 숫자가 아니라 식별자입니다. 문자열로 받는 편이 안전합니다.
{
"id": "9007199254740993",
"name": "post"
}코드 리뷰에서는 이렇게 물어봐야 합니다.
이 숫자는 더하고 빼는 값인가?
아니면 정확히 보존해야 하는 식별자인가?식별자라면 number보다 string이 더 안전한 모델일 때가 많습니다.
---
NaN은 “Not a Number”입니다. 이름만 보면 숫자가 아닌 것 같습니다. 그런데 타입은 number입니다.
typeof NaN; // "number"NaN은 주로 숫자로 해석할 수 없는 연산에서 나옵니다.
Number("hello"); // NaN
parseInt("px", 10); // NaN
Math.sqrt(-1); // NaN특이한 점은 NaN이 자기 자신과도 같지 않다는 것입니다.
NaN === NaN; // false그래서 NaN 확인에는 Number.isNaN을 쓰는 편이 안전합니다.
Number.isNaN(NaN); // true
Number.isNaN("hello"); // false
isNaN("hello"); // true전역 isNaN은 먼저 숫자 변환을 시도합니다. 그래서 문자열도 true가 나올 수 있습니다. 입력값 검증에서는 이 차이가 중요합니다.
const value = Number(input.value);
if (Number.isNaN(value)) {
showError("숫자로 입력해주세요.");
}---
자바스크립트에는 0과 -0이 둘 다 있습니다.
-0 === 0; // true
Object.is(-0, 0); // false대부분의 UI에서는 큰 문제가 아닙니다. 하지만 계산 방향이나 비율을 다루면 가끔 드러납니다.
1 / 0; // Infinity
1 / -0; // -Infinity차트, 좌표, 스크롤 방향, 제스처 속도처럼 “방향”이 의미 있는 값에서는 -0이 디버깅을 헷갈리게 만들 수 있습니다.
예를 들어 화면에는 0으로 보이는데 내부 계산은 음수 방향을 유지할 수 있습니다.
const velocity = -0;
Object.is(velocity, -0); // true이런 값을 화면에 보여줄 때는 도메인에 맞게 정규화하는 것도 방법입니다.
const normalizeZero = (value) => (Object.is(value, -0) ? 0 : value);다만 모든 곳에서 -0을 없애야 한다는 뜻은 아닙니다. 방향 정보가 필요하면 보존해야 합니다. 중요한 건 “그 값이 도메인에서 의미가 있는가”입니다.
---
안전한 정수 범위를 넘는 계산이 필요하면 BigInt를 쓸 수 있습니다.
const id = 9007199254740993n;
typeof id; // "bigint"BigInt는 정수를 정확하게 다룰 수 있지만, number와 자동으로 섞이지 않습니다.
1n + 1; // TypeError
1n + 1n; // 2n이 제약은 불편하지만 오히려 장점이기도 합니다. 서로 다른 숫자 모델을 조용히 섞으면 더 위험하기 때문입니다.
프론트엔드에서는 BigInt를 무조건 쓰기보다 먼저 경계를 봐야 합니다.
| 상황 | 더 나은 선택 |
|---|---|
| 큰 ID를 표시/전달만 한다 | string |
| 큰 정수를 실제로 계산한다 | bigint |
| 소수 계산이 필요하다 | number 또는 decimal 라이브러리 |
| 금액 계산이 필요하다 | 최소 단위 정수 또는 전용 decimal 모델 |
BigInt는 JSON에서 바로 직렬화되지 않는 점도 주의해야 합니다.
JSON.stringify({ id: 1n }); // TypeErrorAPI 경계에서는 문자열로 주고받는 설계가 더 단순할 때가 많습니다.
---
숫자 코드를 볼 때는 문법보다 의도를 먼저 봐야 합니다.
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)
---
`console.log(0.1 + 0.2 === 0.3);`Post Q&A
자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!
DOM 이벤트의 capture/bubble, delegation, default action, React synthetic event 경계를 프론트엔드 코드 리뷰 관점에서 정리합니다.