비교할 문제

JSON 직렬화 병목에서 확인할 질문은 JSON.stringify가 event loop를 막는 크기와 입력 모양은 어디부터인가입니다. 동일한 field, 순서와 Unicode·BigInt 오류 계약을 유지합니다. field를 줄인 응답은 최적화가 아니라 API 계약 변경으로 따로 평가합니다. 결과값, 순서, 오류 처리와 부수 효과가 다르면 같은 성능 비교가 아닙니다. 먼저 두 후보가 같은 일을 하는지 작은 입력으로 확인합니다.

원인 분석과 재현

큰 객체의 JSON.stringify·parse는 main thread CPU와 임시 allocation을 사용하며 순환 참조와 BigInt도 별도 처리해야 합니다.

record 100·1만·10만 개, 짧은 문자열·Unicode·중첩 객체를 나누고 직렬화 시간, output byte, heap과 long task를 봅니다. runtime version, 운영체제, CPU와 memory 제한도 결과와 함께 기록합니다.

비교 질문은 JSON.stringify가 event loop를 막는 크기와 입력 모양은 어디부터인가입니다.

후보 유리한 조건 함께 치르는 비용
main thread stringify 작고 상한이 분명한 payload 큰 입력에서는 한 번의 동기 작업이 event loop를 점유
payload·field 축소 소비자가 일부 field만 필요 API version과 cache key 변경 필요
worker·streaming protocol 충분히 큰 CPU 작업 또는 첫 byte가 중요 복사, chunk framing과 오류 계약 비용
const sizes = [100, 10_000, 100_000];
for (const size of sizes) {
  const payload = Array.from({ length: size }, (_, id) => ({ id, name: `item-${id}`, active: id % 2 === 0 }));
  const samples = []; let bytes = 0;
  for (let warm = 0; warm < 10; warm += 1) JSON.stringify(payload); // warm-up
  for (let run = 0; run < 30; run += 1) {
    const start = performance.now(); const json = JSON.stringify(payload);
    samples.push(performance.now() - start); bytes ^= json.length;
  }
  samples.sort((a, b) => a - b);
  console.log({ size, median: samples[15], p95: samples[28], bytes });
}

해결 방안과 판정

warm-up과 측정 구간을 나누고 여러 번 반복합니다. 서비스 지연은 median과 p95를, 처리량 비교는 단위 시간당 완료 수와 오류율을 함께 기록합니다. CPU time, allocation, RSS·heap, GC, I/O 중 이 작업에 직접 관련된 지표만 선택합니다. serializer 교체 전에 불필요한 data 조회와 응답 크기를 줄이는 편이 end-to-end 효과가 큰 경우가 많습니다. stringify 시간과 network transfer를 분리합니다.

차이가 반복 실행의 흔들림보다 작다면 더 단순하고 읽기 쉬운 구현을 선택합니다. 차이가 충분히 크더라도 실제 요청 전체의 지연과 자원 사용이 개선되는지 확인한 뒤 적용합니다.

공식 문서와 적용 범위

문서의 기본값과 동작은 version에 따라 달라질 수 있습니다. 현재 runtime·framework version, 실제 입력과 요청 흐름에서 다시 확인합니다.