JSON 개발 가이드라인
GitLab v19.2요약
GitLab에서는 방대한 양의 JSON 데이터를 처리합니다. 이 클래스는 기본 JSON 클래스 호출, .to_json 호출 등을 대체하여 사용해야 합니다. 차이점은 모든 JSON 처리를 Gitlab::Json을 통해 수행함으로써 내부적으로 사용하는 gem을 교체할 수 있다는 점입니다.
GitLab에서는 방대한 양의 JSON 데이터를 처리합니다. 대용량 JSON 인코딩 또는 디코딩 시 최적의 성능을 보장하기 위해, 기본 메서드 대신 자체 JSON 클래스를 사용합니다.
Gitlab::Json#
이 클래스는 기본 JSON 클래스 호출, .to_json 호출 등을 대체하여 사용해야 합니다.
.parse, .generate, .dump 등 JSON이 제공하는 대부분의 공개 메서드를 구현하며, 응답 결과는 기본 클래스와 완전히 동일합니다.
차이점은 모든 JSON 처리를 Gitlab::Json을 통해 수행함으로써 내부적으로 사용하는 gem을 교체할 수 있다는 점입니다.
기본 json gem 대신 C 확장을 사용하는 oj를 사용하여 현저히 빠른 성능을 제공합니다.
이 클래스가 만들어진 이유는, GitLab 애플리케이션의 오랜 역사로 인해 기본 json gem을 oj로 단순 교체하는 것이 불가능했기 때문입니다:
-
응답 결과에 대한 정확한 기댓값을 갖는 테스트 케이스가 방대하게 존재합니다.
-
서로 다른 JSON 프로세서 간, 특히 포매팅과 관련하여 미묘한 차이가 있습니다.
Gitlab::Json 클래스는 이러한 점을 고려하여 사용 사례에 따라 어댑터를 유연하게 변경할 수 있으며, 구식 포매팅 기댓값도 처리할 수 있습니다.
Gitlab::Json.safe_parse 클래스 메서드는 더 이상 사용되지 않습니다(deprecated). 신뢰할 수 없는
입력을 파싱하려면 대신 Gitlab::Json::SafeParser를 사용하세요.
Gitlab::Json::SafeParser#
Gitlab::Json::SafeParser는 Oj::Parser.safe를 감싸는 얇은 래퍼로, JSON 파싱에 구조적 제한을
적용하고 저수준 파서 오류를 사용자에게 안전한 JSON::ParserError 메시지로 변환합니다. 신뢰할 수 없는
소스에서 JSON을 파싱할 때는 항상 이 클래스를 사용하세요.
기반 파서#
Gitlab::Json::SafeParser는 Oj의 최신 Oj::Parser.safe 파서를 사용합니다.
Gitlab::Json.parse와 더 이상 사용되지 않는 Gitlab::Json.safe_parse는 :rails 모드에서
Oj.load를 사용합니다. 이 둘은 서로 다른 Oj 파서이며, 다음 측면에서 동작이 다를 수 있습니다:
-
지원되는 옵션.
Gitlab::Json::SafeParser는PARSE_LIMITS에 정의된 키만 허용합니다.Gitlab::Json.parse에서 동작하는symbolize_keys와 같은 옵션은 전달되지 않습니다. -
매우 큰 숫자 값,
NaN및Infinity, 중복 키, 후행 데이터와 같은 엣지 케이스 값의 처리. -
잘못된 형식의 JSON에 대한 정확한 오류 메시지 텍스트.
Gitlab::Json.parse 또는 Gitlab::Json.safe_parse에서 마이그레이션할 때는 대표적인 페이로드를
테스트하고, 파서별 동작이나 오류 문자열에 의존하는 어설션을 업데이트하세요.
파싱 제한#
Gitlab::Json::SafeParser는 구성 가능한 제한 집합에 대해 입력을 검증합니다. 기본값은
Gitlab::Json의 PARSE_LIMITS에서 가져옵니다:
| 옵션 | 기본값 | 설명 |
|---|---|---|
| max_depth | 32 | 최대 중첩 깊이. |
| max_array_size | 50,000 | 단일 배열의 최대 요소 수. |
| max_hash_size | 50,000 | 단일 해시의 최대 키-값 쌍 수. |
| max_total_elements | 100,000 | 페이로드 전체에서 파싱된 요소의 최대 총 개수. |
| max_json_size_bytes | 20 MB | 입력 JSON 문자열의 최대 크기(바이트). |
다른 키를 전달하면 Gitlab::Json::SafeParser::UnknownConfigurationError가 발생합니다.
신뢰할 수 없는 JSON 파싱#
거의 모든 사용 사례에서 Gitlab::Json::SafeParser.parse가 올바른 선택입니다. 이 메서드는 파서
캐싱, 제한 병합, 오류 래핑을 대신 처리합니다. 동일한 제한으로 호출하면 동일한 기반 파서를
재사용합니다:
Gitlab::Json::SafeParser.parse(payload)
Gitlab::Json::SafeParser.parse(
payload,
max_depth: 10,
max_json_size_bytes: 1.megabyte
)
.parse는 제한의 고유 조합마다 하나의 파서를 할당하고 현재 스레드의 수명 동안 해당 스레드에
캐시합니다. 여러 호출에서 동일한 제한을 재사용하면 그 제한이 기본값이든 고정된 사용자 정의 집합이든
동일한 파서를 재사용합니다. 주의해야 할 비용은 변화하는 제한입니다. 예를 들어 요청이나 페이로드에서
제한을 계산하는 경우, 새 조합마다 스레드 캐시에 항목이 추가되기 때문입니다. 사용자 정의 제한이
필요하다면 안정적인 집합을 선택하여 유지하세요. 워크플로가 실제로 호출마다 달라지는 제한을 필요로
한다면, 캐시가 커지지 않도록 대신 .new로 전용 파서를 할당하세요.
고급: 전용 파서 인스턴스#
대부분의 코드는 .new를 사용해서는 안 됩니다. 반환된 인스턴스는 이를 할당한 스레드에 바인딩되며,
기반이 되는 Oj::Parser.safe는 스레드 안전(thread-safe)하지 않습니다. 호출자는 인스턴스를 스레드에
고정된 상태로 유지할 전적인 책임이 있습니다. 이를 스레드, 파이버, 워커 간에 공유하면 할당 시점이 아니라
파싱 시점에 Gitlab::Json::SafeParser::ConcurrencyError가 발생합니다. .new는 동일한 사용자 정의
제한으로 많은 페이로드를 처리하는 스크립트나 작업처럼, 수명 동안 전용 파서를 소유하는 것이 유리한
명확히 단일 스레드 워크플로가 있을 때만 사용하세요.
전용 인스턴스가 필요하다고 판단했다면, .new로 할당하세요:
parser = Gitlab::Json::SafeParser.new(max_array_size: 1_000)
parser.parse(payload)
오류 처리#
parse는 잘못된 형식의 JSON, 페이로드 크기 위반, 제한 위반에 대해 JSON::ParserError를
발생시킵니다. 오류 메시지는 사용자에게 노출해도 안전합니다:
-
Parameters nested too deeply -
Array parameter too large -
Hash parameter too large -
Too many total parameters -
JSON body too large
언제 안전하게 파싱해야 하는지와 요청 흐름에서 오류를 처리하는 방법에 대한 지침은 보안 코딩 가이드라인의 JSON 파싱 섹션을 참조하세요.
Gitlab::Json::PrecompiledJson#
이 클래스는 Grape 프레임워크에 대한 훅에서 사용됩니다. 이미 생성된 JSON이 응답 반환 시 JSON 생성 과정을 다시 한 번 거치지 않도록 보장합니다.
Gitlab::Json::LimitedEncoder#
이 클래스는 JSON을 생성하되, 결과 JSON이 너무 커지면 오류를 발생시키는 데 사용할 수 있습니다.
.encode 메서드의 기본 제한값은 25 MB이지만, 메서드 사용 시 이 값을 사용자 정의할 수 있습니다.