피드로 돌아가기
Go BlogBackend
원문 읽기
Go 팀이 encoding/json/v2와 encoding/json/jsontext를 실험 패키지로 도입해 UTF-8 검증, 중복 키 거부, nil 슬라이스/맵 처리 등 15년간 쌓인 구조적 결함을 해결
A new experimental Go API for JSON
AI 요약
Context
encoding/json은 Go 출시 초기부터 사용되어 온 표준 라이브러리이지만, RFC 8259 표준화 이후 UTF-8 유효성 검증 미흡, 중복 멤버명 허용, nil 슬라이스/맵을 JSON null로 직렬화하는 문제들이 누적되었다. 또한 case-insensitive 역직렬화, MarshalJSON 포인터 리시버의 불일치한 호출, 스트리밍 미지원, io.Reader 대한 정확한 역직렬화 어려움 등 API 수준의 결함이 쌓여있었다.
Technical Solution
- UTF-8 검증 강화: RFC 8259 준수하여 유효하지 않은 UTF-8 입력을 기본 동작으로 거부하여 무음 데이터 손상 방지
- 중복 키 거부: JSON 객체의 중복 멤버명을 명시적으로 거부하는 기본 동작 도입으로 보안 취약점(CVE-2017-12635 유형) 차단
- nil 슬라이스/맵 처리 개선: nil 슬라이스와 맵을 JSON null 대신 빈 배열/객체로 기본 직렬화하여 다른 JSON 구현체와의 상호운용성 향상
- Case-sensitive 역직렬화: JSON 객체 멤버명을 Go 구조체 필드명으로 해석할 때 대소문자를 구분하는 방식으로 변경하여 보안 및 성능 개선
- 일관된 MarshalJSON 호출: 포인터 리시버로 선언된 MarshalJSON 메서드를 일관되게 호출하도록 수정
- 옵션 전파: Encoder/Decoder의 옵션을 Marshal/Unmarshal 함수 및 커스텀 Marshaler/Unmarshaler 인터페이스에서도 사용 가능하도록 구조화
- 스트리밍 토큰 API: 개별 JSON 토큰을 읽고 쓰는 스트리밍 API 추가로 메모리 버퍼링 제거
- 유연한 출력 대상: Compact, Indent, HTMLEscape 함수를 bytes.Buffer 대신 []byte 또는 io.Writer에 쓸 수 있도록 변경
- 효율적인 역직렬화: UnmarshalJSON 호출 전 전체 JSON 값을 파싱하고 재파싱하는 이중 파싱 제거
- v1 호환성 보장: encoding/json/v2를 기반으로 v1을 재구현하여 Go 1 호환성 약속 범위 내에서 동일 동작 유지
Key Takeaway
새로운 메이저 API 버전 도입 시 기존 패키지는 절대 폐기하지 않고 점진적 마이그레이션을 지원하는 방식으로, 장기간 누적된 설계 결함을 동시에 해결할 수 있다는 것을 보여준다. Go 팀의 실험 단계(GOEXPERIMENT=jsonv2 빌드 플래그)를 통해 프로덕션 환경에서의 실제 피드백을 수집한 후 표준화를 진행하는 전략은 대규모 라이브러리 개선의 모범 사례이다.
실천 포인트
Go 서비스에서 JSON 처리 시 기본 encoding/json을 사용하는 팀은 GOEXPERIMENT=jsonv2로 기존 테스트를 실행해 회귀 여부를 확인함으로써, 새 구현의 UTF-8 검증, 중복 키 거부, nil 처리 개선 등 보안 및 상호운용성 이점을 조기에 검증할 수 있다. 특히 외부 시스템과의 JSON 상호운용 또는 보안 감사가 필요한 서비스는 v2 마이그레이션을 우선 검토하는 것이 권장된다.