JSON Toolkit

JSON5와 JSONC - 주석이 있는 JSON

핵심 요약: 두 방언 모두 사람이 작성하는 설정에 필요한 주석과 후행 쉼표 때문에 존재하지만, 문법과 생태계 범위가 달라서 잘못 고르면 툴체인이 막힙니다.

초급약 10분 소요2026-10-30 발행2026-10-30 업데이트
JSON5와 JSONC, 주석이 있는 JSON 표지

핵심부터 말하면, VS Code와 TypeScript 설정 내부의 주석에는 JSONC를 쓰고, 작은따옴표나 따옴표 없는 키처럼 더 넓은 ES5.1 리터럴 문법이 필요하면 JSON5를 쓰며, 공개 API는 표준 JSON으로 유지하세요.

표준 JSON은 주석을 허용하지 않지만, 모든 팀이 조용히 주석을 달고 있습니다. 어느 날 빌드가 깨질 때까지요. 몰래 하는 대신 적절한 방언을 고르세요.

1. 두 방언의 동기는 다르다

  • JSONC (JSON with Comments): 표준 JSON에 주석과 후행 쉼표를 더한 것이고, 다른 문법 변경은 거의 없습니다. VS Code 생태계의 사실상 표준이며 settings.json, tsconfig.json, .eslintrc.json이 모두 이를 씁니다.
  • JSON5: 훨씬 야심적이며 ES5.1 객체 리터럴에 바로 맞춰져 있습니다. 작은따옴표, 따옴표 없는 키, 16진수, Infinity/NaN, 역슬래시 줄 이어붙이기, 앞에 붙는 +까지 지원합니다.

한 문장으로 구분하면 이렇습니다. JSONC는 작성 편의만 높이고, JSON5는 설정 파일을 JavaScript처럼 쓸 수 있게 합니다.

2. 문법 비교 표

기능 표준 JSON JSONC JSON5
주석 // /* */ ✗ ✓ ✓
후행 쉼표 ✗ ✓ ✓
작은따옴표 문자열 ✗ ✗ ✓
따옴표 없는 키 ✗ ✗ ✓
16진수 ✗ ✗ ✓
앞뒤 소수점 .5 5. ✗ ✗ ✓
+로 시작하는 숫자 ✗ ✗ ✓
Infinity / NaN ✗ ✗ ✓
행 끝 \ 이어붙이기 ✗ ✗ ✓
여러 줄 문자열 ✗ ✗ ✓ (이어붙이기로 구현)
임의의 최상위 값 ✓ ✓ ✓

JSON5 명세 전체는 한 페이지를 겨우 넘고, json5 패키지는 오랫동안 커뮤니티가 유지해 왔으며, 브라우저와 Node에서 동작이 동일합니다.

3. 파싱: 두 가지 API, 두 종류의 위치 추적 능력

3.1 JSON5: 단순한 문서 전체 파싱

import JSON5 from 'json5';

const config = JSON5.parse(`
{
  // service port
  port: 8080,
  host: '0.0.0.0',
  features: ['json5', 'jsonc',],
  retries: 0x10,
}
`);

console.log(config.port, config.retries); // 8080 16

JSON5.parse는 오프셋 정보가 없는 일반 객체를 반환합니다. 편집 시나리오가 아니라 "설정 읽기"를 겨냥했기 때문입니다.

3.2 JSONC: 오프셋을 가진 구문 트리

jsonc-parser의 가치는 파싱이 아니라 편집에 있습니다. 세 층의 API를 제공합니다:

API 용도 대표적 사용
parse offset/length를 담은 트리로 파싱 구문 강조, 접기, 탐색
visit 깊이 우선 순회. 콜백이 노드 경로를 받음 JSONPath식 검사
modify + applyEdits 최소 텍스트 편집을 만들어 원문에 적용 주석을 유지한 채 값 변경
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';

const text = `{
  // port configuration
  "server": {
    "port": 8080 // 80 in production
  }
}`;

const tree = parseTree(text);
const portNode = findNodeAtLocation(tree!, ['server', 'port']);
console.log(portNode?.offset, portNode?.length);

const edits = modify(text, ['server', 'port'], 3000, {
  formattingOptions: { insertSpaces: true, tabSize: 2 },
});
console.log(applyEdits(text, edits));

출력은 원래 주석을 유지하고 8080의 세 문자만 교체합니다:

{
  // port configuration
  "server": {
    "port": 3000 // 80 in production
  }
}

이것이 "정규식으로 설정 편집하기"가 할 수 없는 일입니다. 정규식은 어느 8080을 바꿔야 하는지 모르고, 문자열 안의 8080은 건드리지 않아야 한다는 것도 모릅니다.

3.3 오류 메시지는 오프셋을 담아야 한다

jsonc-parser의 오류 코드는 열거 값이며, 자주 보는 것은 다음과 같습니다:

오류 코드 의미
InvalidSymbol 있어서는 안 될 문자가 나옴
PropertyNameExpected 키 이름이 필요함
ColonExpected 콜론이 필요함
CommaExpected 쉼표가 필요함
CloseBraceExpected 객체가 닫히지 않음
UnexpectedEndOfComment 주석이 닫히지 않음
InvalidEscapeCharacter 잘못된 이스케이프

offset을 행과 열로 바꾸면 에디터가 문제 위치에 정확히 물결 밑줄을 그릴 수 있습니다.

4. 언제 무엇을 쓸까

시나리오 권장 이유
공개 API 요청/응답 본문 표준 JSON 범용 클라이언트는 방언을 지원하지 않음
package.json, composer.json 표준 JSON 패키지 관리자는 표준 JSON만 이해함
tsconfig.json, .vscode/settings.json JSONC 생태계 기본값이며 툴체인이 기본 지원
애플리케이션 설정(주석 필요) JSONC 또는 YAML 도구가 읽고 쓸 수 있으면서 주석을 보존해야 함
Infinity, 16진수, 복잡한 리터럴 필요 JSON5 표현력이 가장 강함
데이터 교환, 로그, 캐시 줄 구분 형식 또는 표준 JSON 스트리밍과 보편성이 필요함

경험 법칙: 파일을 "사람이 아닌 도구"가 읽는다면 표준 JSON을 우선하고, 오직 자체 코드만 읽는다고 확신할 때만 방언을 고려하세요.

5. 방언이 끌어들이는 세 가지 위험

5.1 요구 사항의 전이

다른 도구가 JSON5 설정 파일을 읽으며 JSON.parse를 호출하면 곧바로 크래시합니다. 전형적인 증상은 "로컬에서는 잘 되는데 Docker에서 실패"입니다. 컨테이너가 다른 읽기 경로를 타기 때문입니다.

대응: 저장소에 CI 검사를 추가해 방언을 써도 되는 파일을 명시적으로 나열하고, 나머지는 표준 JSON으로 검증하세요.

5.2 타입 추론 주변의 뜻밖의 결과

JSON5는 따옴표 없는 키와 작은따옴표를 허용하지만, 날짜나 정규식 같은 리터럴 구문을 가져올 수는 없습니다. 여전히 데이터 형식이지 JavaScript 평가가 아닙니다. JSON5로 new Date()를 표현할 수 있다고 가정했다가 파싱 실패를 본 사례가 있습니다. JSON5는 eval이 아닙니다.

5.3 조용히 사라지는 주석

JSON5.stringify도 JSON.stringify도 주석을 보존하지 않습니다. 그래서 "읽어서 값 하나를 고치고 다시 쓰기"를 하면 모든 주석이 사라집니다. 주석이 살아남아야 한다면 선택지는 jsonc-parser의 편집 API 또는 주석 보존을 지원하는 YAML 라이브러리뿐입니다.

6. 실무 권장 사항

  • [ ] 팀 문서에 "파일 타입 → 형식" 표를 두어 아무도 즉흥적으로 정하지 않게 합니다
  • [ ] 설정 읽기를 단일 loadConfig()로 감싸 내부에서 어떤 파서를 쓸지 결정하게 하고, 비즈니스 코드가 JSON.parse를 직접 호출하지 않게 합니다
  • [ ] 주석이 있는 설정을 프로그램으로 수정할 때는 modify + applyEdits를 쓰고 파일 전체를 다시 쓰지 않습니다
  • [ ] 방언 파일도 CI에서 검증합니다(JSONC는 jsonc-parser, JSON5는 JSON5.parse). 그렇지 않으면 문법 오류가 런타임에야 드러납니다
  • [ ] 공개 인터페이스, 캐시 키, 메시지 큐 페이로드에는 표준 JSON을 씁니다

FAQ

JSON5와 JSONC 중 무엇을 골라야 하나?

문법 개수보다 런타임으로 고르세요. VS Code, TypeScript, ESLint는 기본적으로 JSONC를 읽으므로 JSONC의 호환성이 가장 좋습니다. 작은따옴표, 따옴표 없는 키, Infinity처럼 더 넓은 ES5.1 리터럴 문법이 필요할 때만 JSON5를 꺼내세요. 어느 방언도 공개 API 응답에는 적합하지 않습니다.

표준 JSON 파서가 JSONC를 바로 읽지 못하는 이유는?

JSONC는 주석과 후행 쉼표를 허용하지만 JSON.parse는 RFC 8259 JSON만 받아들이고 둘 다에서 오류를 던집니다. JSONC를 읽으려면 jsonc-parser 같은 파서가 필요하거나, 주석을 제거한 뒤 표준 파서에 텍스트를 넘겨야 합니다.

JSONC의 주석은 파일을 편집해도 유지되어야 하나?

유지되어야 하지만, 편집 API를 통해서만 가능합니다. jsonc-parser의 modify와 applyEdits는 대상 노드를 바꾸면서 나머지 주석을 보존하고, 객체로 파싱했다가 다시 문자열로 만들면 모든 주석이 사라집니다.