우리가 웹 개발에서 흔히 사용하는 데이터 교환 형식인 JSON(JavaScript Object Notation)이 자바스크립트(JavaScript)의 모든 상태를 완벽하게 복제하지 못한다는 사실이 다시 한번 강조되었습니다. 많은 개발자가 JSON.parse(JSON.stringify(value))를 통해 자바스크립트 객체를 복사하는 방식으로 사용하지만, 이 과정에서 특정 데이터 타입들이 예기치 않게 변경되거나 아예 사라질 수 있어 주의가 필요합니다. JSON은 문자열, 숫자, 불리언, null, 배열, 객체만을 지원하는 간결한 형식이기 때문에 자바스크립트의 BigInt, undefined, Date, NaN 등은 제대로 처리되지 않을 수 있습니다.
JSON의 이러한 한계는 그 설계 목적에서 비롯됩니다. 2001년 브라우저와 서버 간의 가벼운 데이터 교환을 위해 등장한 JSON은 당시 지배적이던 XML보다 간결한 문법으로 빠르게 확산되었습니다. 하지만 JSON은 특정 언어의 전체 타입 시스템을 반영하기보다는 이식성 높은 '와이어 형식'을 우선했기 때문에, 자바스크립트의 모든 타입을 담아내지 못합니다. 예를 들어, 자바스크립트의 Number 타입은 2^53-1 이상의 큰 정수에서 정밀도 손실이 발생할 수 있으며, JSON은 이를 십진 텍스트로만 정의하여 다른 시스템에서 파싱할 때 잘못된 값으로 변환될 위험이 있습니다. 또한, 자바스크립트의 undefined는 JSON 직렬화 시 해당 속성 자체가 삭제되며, Date 객체는 ISO UTC 문자열로 변환되어 원래의 Date 타입 정보가 사라집니다. NaN, Infinity, -Infinity 같은 특수 숫자들은 null로 변환되어 원래의 의미를 잃게 됩니다.
이러한 특성 때문에 JSON은 단순한 값 복사가 아닌 '코드를 실행하는 변환 과정'으로 이해해야 합니다. JSON.stringify()는 toJSON() 메서드나 getter를 실행하며, replacer 함수를 통해 직렬화 방식을 커스터마이징할 수 있습니다. 이는 유연성을 제공하지만, 동시에 예측 불가능한 동작이나 보안 문제로 이어질 수 있습니다. 예를 들어, 순환 참조는 TypeError를 발생시키고, 파싱된 JSON 객체를 안전하지 않게 병합할 경우 프로토타입 오염(prototype pollution)과 같은 보안 취약점이 발생할 수 있습니다. 따라서 안전한 JSON 사용을 위해서는 큰 정수를 문자열로 인코딩하고, 타입 태그를 활용하며, 파싱 전 크기 제한과 파싱 후 런타임 스키마 검증을 적용하는 등 명시적인 '와이어 계약'과 전체 왕복 테스트가 필수적입니다. JSON은 고장 난 형식이 아니라, 자바스크립트의 무손실 스냅샷이 아닐 뿐이라는 점을 명확히 인지하고 사용해야 합니다.