개발자 도구 · JSON 포맷터 및 유효성 검사기
JSON의 큰 정수 ID: JavaScript 포맷터가 이를 반올림할 수 있는 이유
· 그것이 중요한 이유
JSON 개발자 워크플로 검증
JSON은 모든 크기의 정수를 허용하지만 JavaScript는 숫자를 64비트 부동 소수점으로 표시하므로 2^53 위의 모든 항목은 구문 분석 및 재직렬화 시 변경될 수 있습니다. 이번 포스팅에서는 한도, 피해구제방법, ID보호방법에 대해 설명드리겠습니다.
하나 변경된 ID
하나씩 변경한 ID가 완벽하게 유효한 JSON로 도착하는 경우가 많습니다. `{"orderId":9007199254740993}`을 JavaScript에 넣으면 `JSON.parse`는 표시된 값이 `9007199254740992`인 숫자를 반환합니다. 토큰이 JSON 숫자 문법을 따르기 때문에 구문 분석이 성공합니다. 해당 십진수를 JavaScript의 숫자 표현으로 변환하는 동안 손상이 발생합니다. 구문 분석된 값을 직렬화하는 포맷터는 소스에 나타난 정확한 토큰이 아닌 반올림된 숫자를 충실하게 작성합니다.
같은 숫자를 인용하면 대비가 즉각적으로 나타납니다. `JSON.parse("{"orderId":"9007199254740993"}")`는 모든 문자를 보존하면서 문자열 `9007199254740993`을 반환하고, `JSON.stringify`는 따옴표 안에 변경되지 않은 해당 숫자를 내보냅니다. 이것이 구문 검증만으로는 숫자 식별자를 보호할 수 없는 이유입니다. 긴 정수가 나타날 때마다 입력과 출력을 비교하고, 산술이 의미의 일부가 아닌 경우 생성 경계에서 식별자를 문자열로 처리합니다.
숫자에 대한 RFC 8259의 내용
RFC 8259는 JSON 숫자의 철자를 정의하지만 모든 구현에 임의 정밀도 숫자 유형을 제공하지는 않습니다. 문법에서는 선택적 빼기 기호, 정수 부분, 선택적 분수 및 지수 부분을 허용합니다. 16진수 표기법, `NaN` 및 `Infinity`과 같은 편의 기능은 제외됩니다. 결과적으로 `9007199254740993`는 일반적인 JavaScript 소비자가 해당 정수를 숫자로 정확하게 표현할 수 없더라도 구문상 유효합니다.
사양의 상호 운용성 지침은 실질적인 경고입니다. 소프트웨어는 일반적으로 IEEE 754 바이너리64 숫자를 사용하며 음수 `2^53 + 1`부터 양수 `2^53 - 1` 범위의 정수는 정확한 일치라는 의미에서 상호 운용 가능합니다. 유효성 검사기는 나중에 파서가 이를 반올림하는 동안 더 큰 토큰을 올바르게 받아들일 수 있습니다.
2^53의 출처
`2^53` 경계는 바이너리64 유효숫자에서 사용할 수 있는 정밀도에서 비롯됩니다. JavaScript는 연속적으로 표현 가능한 가장 높은 정수를 `Number.MAX_SAFE_INTEGER`, 즉 `9007199254740991`로 노출합니다. 해당 크기 이하에서는 인접한 정수를 뚜렷하게 표현할 수 있습니다. 그 위에서는 표현 가능한 값 사이의 간격이 커지므로 인접한 일부 소수 정수가 동일한 숫자에 매핑됩니다. 런타임은 문자열을 자르지 않습니다. 유한한 이진 형식에서 사용 가능한 가장 가까운 값을 선택합니다.
공개 콘솔 검사는 `Number.isSafeInteger(9007199254740993)`입니다. 이는 거짓이지만 소스 리터럴은 함수가 이를 수신하기 전에 이미 반올림되었습니다. 또 다른 것은 JavaScript에서 true로 평가되는 `9007199254740992 === 9007199254740993`입니다. 이러한 예는 모든 더 큰 숫자를 사용할 수 없게 되는지 여부가 아니라 정확한 정수 ID에 관한 것입니다.
구문 분석 및 재직렬화가 숫자를 잃는 방법
구문 분석 및 재직렬화 형식 지정에는 숫자 문자 읽기, 메모리 내 값 생성, 해당 값에서 새 문자 생성의 세 단계가 있습니다. 어휘 세부사항은 중간 단계에서 사라집니다. `{"ticket":9223372036854775807}`을 사용하면 `JSON.parse`은 사용 가능한 가장 가까운 JavaScript 번호를 생성합니다. 그런 다음 `JSON.stringify`는 `9223372036854776000`을 내보냅니다. 직렬 변환기는 보존된 토큰을 독립적으로 손상시키지 않습니다. 직렬화 시간에 따라 원래 숫자 시퀀스는 구문 분석된 객체에 더 이상 존재하지 않습니다.
ToolAcre의 저장소 구현은 `JSON.parse` 및 `JSON.stringify`을 사용하므로 이 제한 사항은 형식화된 출력에 적용됩니다. 구문 분석이 실패한 후 안정적인 이유와 위치를 제공하기 위해 구문 스캐너가 실행됩니다. JavaScript 숫자를 임의 정밀도 표현으로 대체하지 않습니다. 따라서 성공적인 유효성 검사 결과는 문법을 확립하는 반면 형식 차이는 정밀도 손실을 드러낼 수 있습니다.
작업 예: 입력과 출력 비교
JavaScript 왕복 전후의 `{"numeric":9007199254740993,"text":"9007199254740993"}`을 비교합니다. `JSON.stringify(JSON.parse(source), null, 2)`을 실행하면 `numeric` 멤버가 `9007199254740992`인 형식화된 개체가 생성되고 `text`는 `"9007199254740993"`로 유지됩니다. 두 멤버 모두 입력에서 유효했으며 둘 다 출력에서도 유효한 상태로 유지됩니다. 인용된 표현만이 숫자가 아닌 문자 데이터로 디코딩되므로 식별자를 정확하게 보존합니다.
유용한 검토는 단순히 포맷터가 녹색으로 표시되었는지 묻는 것이 아닙니다. 중단되지 않은 숫자 시퀀스의 소스를 검색하고, 안전 범위보다 긴 값을 비교하고, 각 필드가 수량을 나타내는지 아니면 불투명한 레이블을 나타내는지 확인합니다. 생산자가 계약을 제어하는 경우 레이블을 문자열로 변경하고 소비자를 위해 해당 선택을 문서화합니다.
소스에서 ID 보호
JavaScript 클라이언트가 페이로드를 수신하기 전에 ID를 스키마의 문자열로 정의하고 문자열로 직렬화하여 소스에서 ID를 보호합니다. ID에는 숫자만 포함될 수 있으며 의미상 여전히 숫자가 아닐 수 있습니다. 추가, 반올림 및 크기별 정렬은 계정 키에 대한 합법적인 작업이 아닙니다. 문자열은 또한 크기가 안전한 범위 내에 있는 경우에도 숫자 표현에서 무시되는 선행 0을 유지합니다.
다른 런타임이 더 큰 정수를 보유할 수 있다는 사실로부터 언어 간 안전성을 추론하지 마십시오. 파서 및 대상 유형은 다양하며 JavaScript로 작성된 중개자는 이후 서비스에서 값을 확인하기 전에 값을 반올림할 수 있습니다. 일부 특수 파서는 숫자 토큰을 보존하거나 큰 정수를 구성하지만 모든 참가자는 해당 계약을 공유해야 합니다.
여기서 다루지 않는 내용
여기서 다루지 않는 것은 십진수 산술의 더 넓은 설계입니다. `0.1`과 같은 값에는 고유한 이진 부동 소수점 동작이 있으며 화폐에는 애플리케이션 계약에 따라 크기 조정된 정수 또는 소수 유형이 필요할 수 있습니다. 모든 숫자를 인용한다고 해서 자동으로 스키마가 개선되는 것도 아닙니다. 개수, 좌표 및 측정값은 종종 합법적인 숫자입니다. 결정은 정확한 십진수 철자 또는 정확한 정수 ID가 데이터 경로의 모든 소비자에게 남아 있어야 하는지 여부에 따라 달라집니다.
이 토론에서는 JSON 자체가 토큰을 반올림했거나 모든 파서가 JavaScript처럼 작동한다고 주장하지 않습니다. 구체적인 저장소 증거는 더 좁습니다. 이 포맷터는 `JSON.parse` 및 `JSON.stringify`를 호출하므로 여기서 JavaScript 숫자 의미론은 따옴표가 없는 값을 제어합니다. 임의 정밀도 JSON 라이브러리는 다양한 선택을 할 수 있지만 값이 노출되고 직렬화되는 방식을 정의해야 합니다.
요약: 2^53 위의 숫자는 문자열에 속합니다.
요점은 구체적입니다. JavaScript의 안전 범위를 벗어난 정수 식별자는 변경 없이 JavaScript를 통과해야 하는 경우 문자열에 속합니다. JSON 숫자인 `9007199254740993`은 유효한 구문이지만 `JSON.parse` 이후에는 `9007199254740992`이 됩니다. `"9007199254740993"`은 정확하게 유지됩니다. 따옴표는 장식이 아닙니다. 그들은 숫자를 데이터로 보존하고 소비자가 불투명한 라벨을 대략적인 수량으로 취급하는 것을 방지하는 표현을 선택합니다.
문서를 포맷터 출력으로 바꾸기 전에 긴 숫자를 원본과 비교하고 변경된 모든 숫자를 조사하십시오. 가능하면 모든 다운스트림 클라이언트가 안전한 양식을 일관되게 수신할 수 있도록 생산자와 스키마를 수정하세요. ToolAcre는 출력이 구문 분석된 JavaScript 값을 반영하기 때문에 결과를 노출할 수 있지만 구문 분석 중에 이미 손실된 숫자를 재구성할 수는 없습니다.