JSON 명세는 객체, 배열, 문자열, 숫자, 불리언, null의 여섯 가지 데이터 타입을 정의하여 단순함을 강조합니다. 그러나 이러한 단순함은 해석의 여지를 남기며, 각 언어와 라이브러리가 이 여지를 다르게 채우면서 ‘보편적인’ 데이터 형식이 덜 보편적이게 됩니다.
1. 숫자 처리의 불일치
-
정수 정밀도 손실: JavaScript는 64비트 부동소수점 표현으로 인해 특정 크기 이상의 정수에서 정밀도를 잃습니다. 반면 Python, Java(
BigInteger), Go(int64) 등은 큰 정수를 정확히 처리할 수 있어, 데이터베이스 ID나 금융 데이터와 같은 중요한 값에서 문제가 발생합니다. -
부동소수점 정밀도: JSON 자체의 문제는 아니지만, IEEE 754 부동소수점 표준으로 인해 십진수 연산에서 미묘한 오차가 발생할 수 있습니다. 금융 애플리케이션에서는 Python의
Decimal이나 Java의BigDecimal과 같은 전용 타입을 사용해야 합니다.
2. 문자열 인코딩 및 정규화
- 유니코드 정규화: 유니코드 문자는 단일 코드포인트 또는 결합 문자로 표현될 수 있어, 시각적으로 동일하더라도 바이트 시퀀스가 달라질 수 있습니다. 이는 문자열 비교 시 불일치를 야기하며,
NFC와 같은 표준 정규화가 필수적입니다.
3. 객체 키 순서 및 Null 처리
-
객체 키 순서: JSON 명세는 키 순서의 중요성을 부정하지만, JavaScript, Python, Ruby 등 일부 언어는 삽입 순서를 유지합니다. 반면 Go는 이터레이션 순서가 무작위이며 직렬화 시 키를 정렬합니다. 이러한 불일치는 암호화 해시(HMAC) 생성 등 바이트 일관성이 요구되는 상황에서 치명적인 문제를 일으킵니다.
-
Null vs. Undefined vs. Missing: 각 언어는 값의 부재를 다르게 해석합니다. JavaScript의
undefined는JSON.stringify에서 생략되지만, 다른 언어에서는null로 변환되거나 키 부재와 혼동될 수 있습니다.
4. 날짜 및 시간 형식
- JSON은 날짜 타입을 명시하지 않아 ISO 8601 문자열, Unix 타임스탬프(초/밀리초) 등 다양한 형식이 사용됩니다. JavaScript의
Date객체는 밀리초를 기준으로 하므로, Unix 초 단위를 그대로 사용하면 잘못된 날짜로 해석될 수 있습니다.
5. 오류 처리 불일치
- 중복 키, 후행 쉼표, 선행 0, 작은따옴표 사용 등 JSON 명세에 어긋나는 형식에 대해 각 파서가 오류를 발생시키거나 특정 규칙에 따라 처리하는 방식이 다릅니다. 이는 예상치 못한 동작이나 파싱 실패로 이어질 수 있습니다.
6. 실제 사례 및 완화 전략
-
실제 사례: Twitter ID는 JavaScript의 안전 정수 범위를 초과하여
id_str필드를 별도로 제공합니다. PostgreSQL의JSON타입은 원본 형식을 유지하지만JSONB는 키를 정렬하여 정규화합니다. MongoDB의 확장 JSON($date,$oid)은 표준 JSON 파서로는 처리할 수 없습니다. -
완화 전략:
- 스키마 유효성 검사: JSON Schema를 통한 엄격한 데이터 형식 정의 및 검증.
- 데이터 타입 표준화: 대규모 정수를 문자열로, 날짜를 ISO 8601로 변환하는 등 명확한 컨벤션 수립.
- 라이브러리 신중 선택: 정밀도와 안전성을 보장하는 JSON 라이브러리 사용 및 적절한 설정.
- 교차 언어 호환성 테스트: 다양한 언어로 구현된 서비스 간 데이터 왕복 테스트를 CI/CD 파이프라인에 통합하여 잠재적 불일치 조기 발견.