| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- javascript
- API
- mysql
- 옵션표
- 웹 프로그래밍
- 안드로이드
- CodeIgniter
- 웹개발
- 라이렌
- function
- Database
- 후크
- APK
- php
- 코드이그나이터
- 그누보드
- config
- json
- CI3
- jquery
- FCM
- 영카트
- 헬퍼
- html
- jw player
- 함수
- codeigniter3
- rairen
- 설정
- phpDocumentor
- Today
- Total
프로그램 개발서
JavaScript JSON.parse에서 큰 정수 정밀도 손실 막기: context.source와 BigInt 본문

문제와 사용 상황
주문번호, 결제번호, 데이터베이스의 64비트 정수처럼 자릿수가 긴 값을 JSON으로 전달할 때 화면에 나온 숫자가 원본과 달라지는 경우가 있습니다. JSON 문법은 긴 숫자를 허용하지만, JavaScript의 일반 숫자형인 Number가 모든 정수를 정확하게 표현하는 것은 아니기 때문입니다.
JavaScript에서 정확하게 다룰 수 있는 최대 안전 정수는 Number.MAX_SAFE_INTEGER, 즉 9007199254740991입니다. 이 범위를 넘는 숫자를 일반 JSON.parse()로 읽으면 파싱 자체는 성공해도 값이 반올림될 수 있습니다. 오류가 발생하지 않기 때문에 로그 ID나 주문번호가 조용히 바뀌는 문제가 더 위험합니다.
이번 글에서는 실제로 정밀도 손실을 재현하고, JSON.parse() reviver의 context.source와 BigInt를 사용해 원래 숫자를 보존하는 방법을 확인합니다.
테스트 환경·버전·날짜
- 테스트 날짜: 2026-07-27
- 실행 환경: Node.js v24.14.0
- 확인 대상:
JSON.parse()reviver의 세 번째 인수context - 기준 값:
Number.MAX_SAFE_INTEGER와 그보다 큰 양수·음수
context.source는 원시 값을 처리할 때 해당 값이 JSON에 어떻게 적혀 있었는지 원본 문자열로 제공합니다. 객체나 배열을 처리하는 reviver 호출에는 전달되지 않으므로 대상 필드와 값의 형태를 함께 검사해야 합니다.
최소 재현 코드
먼저 일반 JSON.parse() 결과를 확인합니다.
const payload = '{"orderId":12345678901234567890}';
const parsed = JSON.parse(payload);
console.log(parsed.orderId);
console.log(parsed.orderId.toString());
입력은 12345678901234567890이지만 테스트 환경에서 문자열로 바꾼 결과는 12345678901234567000이었습니다. 파싱이 실패한 것이 아니라 이미 다른 Number로 바뀐 것입니다.
테스트 케이스와 결과표
| 구분 | JSON 원본 | 일반 JSON.parse 결과 | context.source + BigInt | 결과 |
|---|---|---|---|---|
| 최대 안전 정수 | 9007199254740991 | 9007199254740991 | 9007199254740991 | 통과 |
| 안전 범위 초과 양수 | 12345678901234567890 | 12345678901234567000 | 12345678901234567890 | 정밀도 손실 재현·복구 통과 |
| 안전 범위 초과 음수 | -12345678901234567890 | -12345678901234567000 | -12345678901234567890 | 정밀도 손실 재현·복구 통과 |
| 끝에 쉼표가 있는 JSON | {"orderId":123,} | SyntaxError | 해당 없음 | 예외 확인 통과 |

핵심은 reviver의 value가 아니라 context.source를 사용하는 것입니다. value는 reviver가 호출되기 전에 이미 Number로 변환되어 정밀도를 잃었을 수 있습니다.
최종 코드
특정 필드가 정수라는 사실을 알고 있을 때는 다음처럼 대상 필드만 BigInt로 바꿀 수 있습니다.
function parseJsonWithBigInts(text, bigIntKeys) {
return JSON.parse(text, (key, value, context) => {
if (!bigIntKeys.has(key)) {
return value;
}
const source = context?.source;
if (typeof source !== "string" || !/^-?\d+$/.test(source)) {
throw new TypeError(`${key} 값은 정수 JSON 리터럴이어야 합니다.`);
}
return BigInt(source);
});
}
const payload = '{"orderId":12345678901234567890,"count":3}';
const data = parseJsonWithBigInts(payload, new Set(["orderId"]));
console.log(data.orderId.toString());
// 12345678901234567890
console.log(data.count);
// 3
서버 응답 형식을 바꿀 수 있다면 큰 정수를 처음부터 문자열로 보내는 방식이 호환성 면에서 더 단순합니다.
{"orderId":"12345678901234567890"}
const data = JSON.parse(
'{"orderId":"12345678901234567890"}',
(key, value) =>
key === "orderId" && /^-?\d+$/.test(value)
? BigInt(value)
: value,
);
실패 조건·브라우저·버전 차이
context.source를 지원하지 않는 구형 환경에서는 이미 손실된Number에서 원래 숫자를 복구할 수 없습니다.- 구형 환경도 지원해야 한다면 API가 큰 정수를 JSON 문자열로 보내도록 정하는 것이 안전합니다.
- 모든 숫자를 무조건
BigInt로 바꾸면 소수, 지수 표기, 일반 계산 코드에서 문제가 생깁니다. 스키마상 큰 정수인 필드만 선택해야 합니다. - reviver에서 값을 반환하지 않으면 해당 속성이 삭제됩니다. 변환 대상이 아닌 값은 반드시 그대로 반환해야 합니다.
- 잘못된 JSON은
SyntaxError를 던지므로 외부 입력을 처리할 때는try...catch가 필요합니다.
보안·호환성 주의사항
BigInt는 Number와 직접 섞어 계산할 수 없습니다. 화면 표시나 식별자 비교가 목적이라면 문자열로 유지하는 방법도 고려해야 합니다. 특히 주문번호나 사용자 ID는 산술 연산 대상이 아니므로 문자열이 더 자연스러운 경우가 많습니다.
또한 JSON을 검사할 때 실제 고객 정보, 인증 토큰, 결제 데이터처럼 민감한 값을 공개 도구에 붙여넣지 않는 것이 좋습니다. 테스트할 때는 구조만 같은 가상 데이터를 사용하세요.
직접 확인 방법
Node.js가 설치되어 있다면 이 글의 최소 재현 코드를 json-bigint-test.mjs로 저장하고 node json-bigint-test.mjs로 실행할 수 있습니다. 브라우저에서 확인하려면 개발자 도구의 Console을 열고 같은 코드를 붙여넣으면 됩니다.
JSON 문법이 유효하다는 것과 JavaScript가 모든 숫자를 정확하게 보존한다는 것은 서로 다른 문제입니다.
공식 참고자료
공식 자료 확인 날짜는 2026-07-27입니다.
변경 이력
- 2026-07-27: Node.js v24.14.0에서 큰 정수 정밀도 손실,
context.source,BigInt변환, 잘못된 JSON 예외를 테스트하고 초안을 작성했습니다.
'Javascript' 카테고리의 다른 글
| JavaScript RegExp.escape()로 동적 정규식 안전하게 만들기: 점·하이픈·역참조 처리 (0) | 2026.08.01 |
|---|---|
| JavaScript URLSearchParams 중복 키 처리: getAll(), append(), set() 차이 (0) | 2026.07.28 |
| 그누보드5 이윰빌더 시즌4 CHEditor5에 에드온 이모티콘 삽입 (0) | 2024.06.24 |
| Javascript 함수 모음 (0) | 2023.08.29 |
| 카드번호 마스킹 처리 html, js 예제 (4) | 2023.07.11 |
