JSON Formatter JavaScript — JSON.stringify()
무료 JSON 포맷터을 브라우저에서 직접 사용하세요 — 설치 불필요.
JSON 포맷터 온라인으로 사용하기 →Node.js에서 API 응답을 디버깅할 때 압축된 JSON 덩어리가 가장 먼저 작업을 느리게 만듭니다—— JSON.stringify(data, null, 2) 를 한 번 호출하면 구조가 즉시 읽기 쉬워집니다. JavaScript에서 JSON을 포매팅하는 데 런타임 외에는 아무것도 필요 없습니다: JSON.stringify 는 모든 브라우저와 Node.js 환경에 내장되어 있고 설치가 필요 없습니다. 코드 작성 없이 빠른 결과만 필요하다면 ToolDeck의 JSON Formatter 가 즉시 처리해 드립니다. 이 가이드는 실용적인 모든 내용을 다룹니다: space 파라미터, replacer 배열과 함수, Date, BigInt, 순환 참조 처리, Node.js(18+)에서 JSON 파일 읽기/쓰기, CLI 예쁜 출력, 그리고 프로덕션 직렬화를 위한 fast-json-stringify 라이브러리.
- ✓JSON.stringify(data, null, 2)는 모든 브라우저와 Node.js에 내장되어 있습니다——설치 불필요.
- ✓replacer 파라미터는 배열(키 화이트리스트) 또는 함수(값 변환)를 받습니다——민감한 필드를 마스킹하는 데 사용하세요.
- ✓Date 객체는 toJSON()을 통해 ISO 8601 문자열로 자동 직렬화됩니다; BigInt는 TypeError를 발생시키며 커스텀 replacer가 필요합니다.
- ✓순환 참조는 TypeError를 발생시킵니다——replacer 함수의 seen Set으로 수정하거나 flatted 라이브러리를 사용하세요.
- ✓CLI 예쁜 출력에는 node -p "JSON.stringify(require('./file.json'),null,2)"를 사용하세요——추가 도구 불필요.
JSON 포매팅이란?
JSON 포매팅(pretty-printing이라고도 함)은 압축된 JSON 문자열을 일관된 들여쓰기와 줄 바꿈이 있는 사람이 읽기 쉬운 레이아웃으로 변환합니다. 기본 데이터는 동일합니다——공백만 바뀝니다. 컴팩트한 JSON은 모든 바이트가 중요한 네트워크 전송에 최적이고, 포맷된 JSON은 디버깅, 코드 리뷰, 로그 검사에 최적입니다. JavaScript의 JSON.stringify() 는 space 파라미터를 전환하는 것만으로 한 번의 호출로 둘 다 처리합니다.
{"orderId":"ord_8f2a91bc","status":"shipped","items":[{"sku":"HDMI-4K-2M","qty":2,"unitPrice":12.99}],"total":25.98}{
"orderId": "ord_8f2a91bc",
"status": "shipped",
"items": [
{
"sku": "HDMI-4K-2M",
"qty": 2,
"unitPrice": 12.99
}
],
"total": 25.98
}JSON.stringify() — 내장 포매터
JSON.stringify() 는 브라우저, Node.js, Deno, Bun 등 모든 JavaScript 환경의 전역 함수로, import가 필요 없습니다. 세 번째 인수인 space 가 들여쓰기를 제어합니다: 숫자를 전달하면 레벨당 그 수만큼의 공백이 들어가고, 문자열 '\t' 을 전달하면 탭이 됩니다. 생략하거나 null 을 전달하면 컴팩트한 한 줄 출력이 됩니다.
const serverConfig = {
host: "api.payments.internal",
port: 8443,
workers: 4,
tls: { enabled: true, cert: "/etc/ssl/certs/api.pem" },
rateLimit: { requestsPerMinute: 1000, burst: 50 }
}
console.log(JSON.stringify(serverConfig, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "workers": 4,
// "tls": {
// "enabled": true,
// "cert": "/etc/ssl/certs/api.pem"
// },
// "rateLimit": {
// "requestsPerMinute": 1000,
// "burst": 50
// }
// }space 파라미터는 숫자(1~10 공백) 또는 문자열을 받습니다. 탭 문자를 전달하면 많은 에디터와 diff 도구가 선호하는 출력이 됩니다. 다음은 포맷된 JSON을 설정 파일에 쓸 때 제가 사용하는 실제 패턴입니다:
const telemetryEvent = {
eventId: "evt_3c7f9a2b",
service: "checkout-api",
severity: "warn",
latencyMs: 342,
region: "ap-northeast-2",
tags: ["payment", "timeout", "retry"]
}
// 2칸 들여쓰기 (JS 프로젝트에서 가장 일반적)
JSON.stringify(telemetryEvent, null, 2)
// 탭 들여쓰기 (일부 린터와 설정 도구가 선호)
JSON.stringify(telemetryEvent, null, '\t')
// 컴팩트 — 공백 없음 (네트워크 전송용)
JSON.stringify(telemetryEvent)
// {"eventId":"evt_3c7f9a2b","service":"checkout-api",...}undefined, 함수, Symbol 값은 출력에서 경고 없이 생략됩니다. 프로퍼티 값이 undefined이면 해당 키는 직렬화된 문자열에 전혀 나타나지 않습니다—— 선택적 필드가 있는 객체를 로깅할 때 흔한 버그의 원인입니다.Replacer 함수 — 출력 필터링 및 변환
JSON.stringify() 의 두 번째 인수가 replacer입니다. 두 가지 형태가 있습니다: 특정 키를 화이트리스트하는 배열과, 모든 키/값 쌍에 대해 호출되어 필터링, 변환 또는 값을 숨길 수 있는 함수입니다. 빠른 서브셋이 필요할 때는 배열 형태를, 로깅 전에 민감한 데이터를 마스킹해야 할 때는 함수 형태를 사용합니다.
배열 Replacer — 특정 키 화이트리스트
const order = {
orderId: "ord_8f2a91bc",
customer: {
id: "usr_4421",
email: "kim.minjun@example.kr",
passwordHash: "bcrypt:$2b$12$XKzV..."
},
items: [{ sku: "HDMI-4K-2M", qty: 2, unitPrice: 12.99 }],
createdAt: "2026-03-10T14:22:00Z"
}
// 로그 출력에는 안전한 필드만 포함
const safeLog = JSON.stringify(order, ["orderId", "items", "createdAt"], 2)
// {
// "orderId": "ord_8f2a91bc",
// "items": [{ "sku": "HDMI-4K-2M", "qty": 2, "unitPrice": 12.99 }],
// "createdAt": "2026-03-10T14:22:00Z"
// }
// passwordHash와 customer.email은 제외됨함수 Replacer — 값 변환
const auditRecord = {
requestId: "req_7d2e91",
user: { id: "usr_4421", email: "kim.minjun@example.kr", apiKey: "sk-live-eKx9..." },
action: "update_billing",
timestamp: new Date("2026-03-10T14:22:00Z"),
durationMs: 87
}
function safeReplacer(key, value) {
// 시크릿이나 개인정보처럼 보이는 필드 마스킹
if (key === "apiKey") return "[REDACTED]"
if (key === "email") return value.replace(/(?<=.{2}).+(?=@)/, "***")
return value
}
console.log(JSON.stringify(auditRecord, safeReplacer, 2))
// {
// "requestId": "req_7d2e91",
// "user": { "id": "usr_4421", "email": "ki***@example.kr", "apiKey": "[REDACTED]" },
// "action": "update_billing",
// "timestamp": "2026-03-10T14:22:00.000Z",
// "durationMs": 87
// }this가 현재 키를 포함하는 객체로 설정되어 호출됩니다. 첫 번째 호출에서는 빈 문자열이 키로, 직렬화되는 전체 값이 값으로 전달됩니다—— 일반 직렬화를 계속하려면 변경 없이 반환하세요.직렬화할 수 없는 타입 처리
모든 JavaScript 값이 JSON에 깔끔하게 매핑되는 것은 아닙니다. 각 타입의 동작을 이해하면 프로덕션 코드에서의 무언의 데이터 손실과 예기치 않은 런타임 오류를 방지할 수 있습니다.
Date — toJSON()을 통한 자동 처리
Date 객체에는 ISO 8601 문자열을 반환하는 toJSON() 메서드가 구현되어 있습니다. JSON.stringify() 는 직렬화 전에 자동으로 toJSON() 를 호출하므로 커스텀 처리가 필요 없습니다.
const webhook = {
eventType: "payment.succeeded",
occurredAt: new Date("2026-03-10T14:22:00Z"),
processedAt: new Date()
}
JSON.stringify(webhook, null, 2)
// {
// "eventType": "payment.succeeded",
// "occurredAt": "2026-03-10T14:22:00.000Z",
// "processedAt": "2026-03-10T14:22:01.347Z"
// }
// toJSON() 메서드를 가진 객체는 동일하게 처리됩니다:
const custom = { toJSON: () => "custom-value", hidden: 42 }
JSON.stringify(custom) // '"custom-value"'커스텀 클래스 — toJSON() 구현
모든 클래스가 toJSON() 메서드를 구현할 수 있고, JSON.stringify() 가 직렬화 중 자동으로 호출합니다. 코드베이스 전반에 걸쳐 나타나는 도메인 타입에는 전역 replacer보다 더 깔끔합니다.
class Money {
constructor(amount, currency) {
this.amount = amount
this.currency = currency
}
toJSON() {
// JSON.stringify가 자동으로 호출
return { amount: this.amount, currency: this.currency, formatted: `${this.currency} ${this.amount.toFixed(2)}` }
}
}
class OrderId {
constructor(id) { this.id = id }
toJSON() { return this.id } // 순수 문자열로 직렬화
}
const invoice = {
invoiceId: new OrderId('inv_8f2a91bc'),
subtotal: new Money(199.00, 'KRW'),
tax: new Money(15.92, 'KRW'),
issuedAt: new Date('2026-03-10T14:22:00Z')
}
JSON.stringify(invoice, null, 2)
// {
// "invoiceId": "inv_8f2a91bc",
// "subtotal": { "amount": 199, "currency": "KRW", "formatted": "KRW 199.00" },
// "tax": { "amount": 15.92, "currency": "KRW", "formatted": "KRW 15.92" },
// "issuedAt": "2026-03-10T14:22:00.000Z"
// }toJSON()은 replacer 함수보다 우선합니다. 둘 다 존재하면 toJSON()이 먼저 실행됩니다—— replacer는 원래 클래스 인스턴스가 아닌 이미 변환된 값을 받습니다.BigInt — Replacer 없이는 TypeError
// 이것은 TypeError를 발생시킵니다: Do not know how to serialize a BigInt
// JSON.stringify({ sessionId: 9007199254741234n })
// 수정: replacer에서 BigInt를 문자열로 변환
function bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const metrics = {
requestCount: 9007199254741234n, // Number.MAX_SAFE_INTEGER 초과
service: "ingestion-worker",
region: "ap-northeast-2"
}
JSON.stringify(metrics, bigIntReplacer, 2)
// {
// "requestCount": "9007199254741234",
// "service": "ingestion-worker",
// "region": "ap-northeast-2"
// }순환 참조 — seen Set 없이는 TypeError
// 이것은 TypeError를 발생시킵니다: Converting circular structure to JSON
// const node = { id: "n1" }; node.self = node; JSON.stringify(node)
// 수정: WeakSet으로 참조된 객체 추적
function circularReplacer() {
const seen = new WeakSet()
return function (_key, value) {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
const parent = { id: "node_parent", label: "root" }
const child = { id: "node_child", parent }
parent.child = child // 순환 참조
JSON.stringify(parent, circularReplacer(), 2)
// {
// "id": "node_parent",
// "label": "root",
// "child": { "id": "node_child", "parent": "[Circular]" }
// }
// 또는: npm install flatted
// import { stringify } from 'flatted'
// stringify(parent) // 순환 참조를 네이티브로 처리JSON.stringify() 파라미터 참조
세 가지 파라미터 모두 모든 최신 JavaScript 런타임에서 완전히 지원됩니다. 기본값은 컴팩트한 한 줄 JSON을 생성합니다——읽기 좋은 출력을 위해서는 명시적으로 파라미터를 전달하세요.
파일과 API 응답의 JSON 포매팅
Node.js(18+)에서는 JSON 설정 파일을 재포맷하거나 디버깅을 위해 API 응답을 예쁘게 출력해야 할 때가 많습니다. 두 패턴 모두 동일한 두 단계 접근 방식을 사용합니다: JSON.parse() 로 원시 텍스트를 파싱한 다음, JSON.stringify() 로 재직렬화합니다.
JSON 설정 파일 읽기 및 재작성
import { readFileSync, writeFileSync } from 'fs'
try {
const raw = readFileSync('./config/database.json', 'utf8')
const config = JSON.parse(raw)
writeFileSync('./config/database.json', JSON.stringify(config, null, 2))
console.log('설정 파일이 성공적으로 재포맷되었습니다')
} catch (err) {
console.error('설정 재포맷 실패:', err.message)
// 파일에 잘못된 JSON이 있으면 JSON.parse가 SyntaxError를 발생시킴
// 파일이 없으면 readFileSync가 ENOENT를 발생시킴
}비동기 파일 재포맷 (fs/promises)
import { readFile, writeFile } from 'fs/promises'
async function reformatJson(filePath) {
const raw = await readFile(filePath, 'utf8')
const parsed = JSON.parse(raw)
const formatted = JSON.stringify(parsed, null, 2)
await writeFile(filePath, formatted, 'utf8')
return { keys: Object.keys(parsed).length }
}
// 사용 예시
const { keys } = await reformatJson('./config/feature-flags.json')
console.log(`최상위 키 ${keys}개가 재포맷되었습니다`)fetch() 응답에서 JSON 예쁘게 출력하기
Node.js 18+ 또는 브라우저에서 API 클라이언트를 구축하거나 디버깅할 때, 응답 본문을 예쁘게 출력하는 것이 서버가 반환한 내용을 가장 빠르게 파악하는 방법입니다. 표준 패턴은 response.json()(파싱된 객체)를 JSON.stringify() 에 전달하는 것입니다. 해시 계산이나 그대로 로깅하기 위해 먼저 원시 문자열이 필요하다면 response.text() 후 JSON.parse() 를 사용하세요.
// 패턴 1: response.json() → 예쁜 출력
async function debugEndpoint(url) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
})
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
const data = await res.json()
console.log(JSON.stringify(data, null, 2))
}
await debugEndpoint('https://api.stripe.com/v1/charges?limit=3')// 패턴 2: response.text() → 파싱 → 포맷
// 원시 응답과 예쁜 출력 모두 로깅하고 싶을 때 유용
async function inspectRawResponse(url) {
const res = await fetch(url)
const raw = await res.text()
console.log('원시 응답 길이:', raw.length)
try {
const parsed = JSON.parse(raw)
console.log('포맷된 결과:')
console.log(JSON.stringify(parsed, null, 2))
} catch {
console.error('응답이 유효한 JSON이 아닙니다:', raw.slice(0, 200))
}
}커맨드라인 예쁜 출력
Node.js는 추가 도구 없이도 터미널에서 JSON을 예쁘게 출력할 수 있는 충분한 기능을 갖추고 있습니다. 다음 원라이너들은 CI 스크립트, 배포 파이프라인, 쉘 별칭에서 유용합니다. 더 빠른 인터랙티브 사용을 위해 jq를 설치하세요—— 커맨드라인에서 JSON 조작의 사실상 표준입니다.
# node -p (print)로 package.json 예쁘게 출력
node -p "JSON.stringify(require('./package.json'), null, 2)"
# 표준 입력에서 예쁘게 출력 (파이프 친화적, CI에서 동작)
echo '{"port":3000,"env":"production"}' | node -e "
const d = require('fs').readFileSync(0, 'utf8')
console.log(JSON.stringify(JSON.parse(d), null, 2))
"
# 파일에 저장된 API 응답 예쁘게 출력
cat api-response.json | node -e "
process.stdin.setEncoding('utf8')
let s = ''
process.stdin.on('data', c => s += c)
process.stdin.on('end', () => console.log(JSON.stringify(JSON.parse(s), null, 2)))
"# Python 폴백 (macOS와 대부분의 Linux 배포판에 사전 설치됨)
cat api-response.json | python3 -m json.tool
# jq — 가장 빠르고 기능이 풍부함 (brew install jq / apt install jq)
cat api-response.json | jq .
# jq — 예쁜 출력과 필터링을 한 번에
cat api-response.json | jq '.data.users[] | {id, email}'"type": "module"이 있는 경우)로 Node.js를 실행할 때, 원라이너에서는 require()를 사용할 수 없습니다. 대신 --input-type=module과 fs.readFileSync를 사용하거나, 위와 같이 CommonJS 스니펫이 있는 node -e로 전환하세요.터미널을 아예 사용하지 않는 경우——Postman 응답이나 로그 파일을 붙여넣는 경우——ToolDeck의 JSON Formatter 를 사용하면 구문 강조 표시와 내장 유효성 검사와 함께 한 단계로 붙여넣기, 포매팅, 복사가 가능합니다.
고성능 대안 — fast-json-stringify
fast-json-stringify 는 JSON Schema에서 전용 직렬화 함수를 생성합니다. 데이터의 형태를 미리 알기 때문에 타입 검사를 건너뛰고 재귀 순회 대신 문자열 연결을 사용할 수 있습니다—— 벤치마크에서는 크고 반복적인 페이로드에 대해 JSON.stringify() 보다 일반적으로 2~5배의 처리량 개선을 보입니다. 프로파일러 추적에서 직렬화 비용이 나타나는 고빈도 API 라우트에서 사용합니다.
npm install fast-json-stringify
import fastJson from 'fast-json-stringify'
const serializeTelemetryEvent = fastJson({
title: 'TelemetryEvent',
type: 'object',
properties: {
eventId: { type: 'string' },
service: { type: 'string' },
severity: { type: 'string', enum: ['info', 'warn', 'error'] },
latencyMs: { type: 'integer' },
timestamp: { type: 'string' },
region: { type: 'string' }
},
required: ['eventId', 'service', 'severity', 'latencyMs', 'timestamp']
})
const event = {
eventId: 'evt_3c7f9a2b',
service: 'checkout-api',
severity: 'warn',
latencyMs: 342,
timestamp: new Date().toISOString(),
region: 'ap-northeast-2'
}
const json = serializeTelemetryEvent(event)
// '{"eventId":"evt_3c7f9a2b","service":"checkout-api","severity":"warn",...}'fast-json-stringify는 구조화된 데이터의 프로덕션 직렬화를 위해 설계되었습니다—— 항상 컴팩트한 출력을 생성합니다(예쁜 출력 없음). 개발 중에 사람이 읽기 쉬운 출력이 필요하다면 평소처럼 JSON.stringify(data, null, 2)를 사용하세요.구문 강조가 있는 터미널 출력
Node.js 내장 util.inspect() 는 터미널 표시에 최적화된 색상 있는 사람이 읽기 쉬운 출력을 생성합니다. 순환 참조와 BigInt를 네이티브로 처리하고 중첩된 객체를 임의 깊이까지 재귀적으로 렌더링합니다. 출력은 유효한 JSON이 아닙니다——JavaScript 구문을 사용합니다 (함수와 Symbol을 생략하는 대신 렌더링하는 등). Node.js 디버깅 스크립트에 이상적이지만 API 응답이나 파일 출력에는 적합하지 않습니다.
import { inspect } from 'util'
const payload = {
requestId: "req_7d2e91",
user: { id: "usr_4421", roles: ["admin", "billing"] },
metadata: { ipAddress: "203.0.113.42", userAgent: "Mozilla/5.0" },
createdAt: new Date()
}
// depth: null → 모든 중첩 레벨 펼치기; colors: true → ANSI 터미널 색상
console.log(inspect(payload, { colors: true, depth: null }))util.inspect()의 출력을 사용하지 마세요. 그 출력은 유효한 JSON이 아니며 JSON.parse()를 호출하는 모든 다운스트림 시스템에서 파싱 오류가 발생합니다. 인터랙티브 터미널 디버깅에만 사용하세요.대용량 JSON 파일 처리
JSON.parse() 는 파싱 전에 전체 파일을 메모리에 로드합니다——작은 페이로드에는 괜찮지만, 데이터베이스 내보내기, 애플리케이션 로그 덤프, 분석 배치 같은 50~100 MB 이상의 파일에는 실용적이지 않습니다. 이런 경우 Node.js Streams와 stream-json 라이브러리를 사용하면 힙을 날리지 않고 레코드를 하나씩 처리할 수 있습니다.
stream-json으로 스트리밍 파싱
npm install stream-json
import { pipeline } from 'stream/promises'
import { createReadStream } from 'fs'
import { parser } from 'stream-json'
import { streamArray } from 'stream-json/streamers/StreamArray.js'
// events.json = 수백만 개의 객체 배열 — 메모리에 완전히 로드되지 않음
await pipeline(
createReadStream('./events.json'),
parser(),
streamArray(),
async function* (source) {
for await (const { value: event } of source) {
if (event.severity === 'error') {
console.log(JSON.stringify(event, null, 2))
}
}
}
)NDJSON / JSON Lines — 추가 의존성 없음
NDJSON(줄 바꿈으로 구분된 JSON)은 한 줄에 하나의 JSON 객체를 저장하며 Kafka 내보내기, BigQuery 출력, 구조화된 로그 파이프라인에서 일반적입니다. Node.js 내장 readline 이 서드파티 패키지 없이 처리합니다.
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// 형식: 한 줄에 하나의 JSON 객체 (로그, Kafka 내보내기, BigQuery)
const rl = createInterface({
input: createReadStream('./logs.ndjson'),
crlfDelay: Infinity
})
for await (const line of rl) {
if (!line.trim()) continue
const entry = JSON.parse(line)
if (entry.level === 'error') {
console.log(JSON.stringify(entry, null, 2))
}
}JSON.parse()에서 스트리밍으로 전환하세요. NDJSON / JSON Lines에는 readline을 사용하세요——추가 의존성이 필요 없습니다.흔한 실수
다음 네 가지 실수는 코드 리뷰와 프로덕션 버그 보고서에서 반복해서 등장합니다. 각각은 JSON.stringify() 의 놓치기 쉽고 나중에 디버깅하기 어려운 미묘한 동작과 관련이 있습니다.
문제: undefined 값을 가진 객체 프로퍼티는 JSON 출력에서 완전히 생략됩니다——경고나 오류가 없습니다. 객체에 선택적 필드가 있을 때 보이지 않는 데이터 손실이 발생합니다.
해결책: 직렬화된 출력에 반드시 나타나야 하는 의도적으로 없는 값에는 null을 사용하세요. undefined는 JSON에서 제외되어야 하는 필드에만 예약하세요.
const userProfile = {
userId: "usr_4421",
displayName: "김민준",
avatarUrl: undefined, // 조용히 사라짐
bio: undefined // 조용히 사라짐
}
JSON.stringify(userProfile, null, 2)
// { "userId": "usr_4421", "displayName": "김민준" }
// avatarUrl과 bio가 사라졌다——경고 없음const userProfile = {
userId: "usr_4421",
displayName: "김민준",
avatarUrl: null, // 명시적 부재 — 출력에 나타남
bio: null // 명시적 부재 — 출력에 나타남
}
JSON.stringify(userProfile, null, 2)
// {
// "userId": "usr_4421",
// "displayName": "김민준",
// "avatarUrl": null,
// "bio": null
// }문제: BigInt 값을 JSON.stringify()에 전달하면 런타임에 TypeError가 발생합니다. 이것은 조용한 생략이 아닌 하드 크래시입니다——숫자 필드가 Number.MAX_SAFE_INTEGER를 초과하면 프로덕션에서 드러납니다.
해결책: 직렬화 전에 BigInt 값을 문자열로 변환하는 replacer 함수를 사용하세요. 또는 JSON.stringify에 전달하기 전에 데이터 레이어에서 BigInt를 문자열이나 Number로 변환하세요.
const session = {
sessionId: 9007199254741234n, // BigInt 리터럴
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, null, 2)
// Uncaught TypeError: Do not know how to serialize a BigIntfunction bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const session = {
sessionId: 9007199254741234n,
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, bigIntReplacer, 2)
// {
// "sessionId": "9007199254741234",
// "userId": "usr_4421",
// "startedAt": "2026-03-10T14:00:00Z"
// }문제: 자기 자신을 참조하는 객체——DOM 트리, 연결 리스트, 일부 ORM 결과 세트에서 흔함——를 JSON.stringify()에 전달하면 TypeError가 발생합니다. 오류는 직렬화 시점에만 발생하며, 순환 참조가 생성된 곳에서 멀리 떨어진 곳에서 나타나는 경우가 많습니다.
해결책: WeakSet이 있는 replacer 함수를 사용하여 순환 참조를 감지하고 교체하거나, 순환 구조를 네이티브로 처리하는 flatted 라이브러리를 설치하세요.
const dept = { id: "dept_eng", name: "개발팀" }
const team = { id: "team_frontend", dept }
dept.teams = [team] // 순환: dept → team → dept
JSON.stringify(dept, null, 2)
// Uncaught TypeError: Converting circular structure to JSONimport { stringify } from 'flatted' // npm install flatted
const dept = { id: "dept_eng", name: "개발팀" }
const team = { id: "team_frontend", dept }
dept.teams = [team]
// flatted가 순환 참조를 처리——주의: 출력은 flatted 형식으로 표준 JSON이 아님
stringify(dept)
// 또는 표준 JSON 출력이 필요하다면 WeakSet 기반 replacer 사용:
function circularReplacer() {
const seen = new WeakSet()
return (_key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
JSON.stringify(dept, circularReplacer(), 2)문제: JSON.stringify()에 10보다 큰 space 값을 전달해도 오류가 발생하지 않습니다——값은 조용히 10으로 잘립니다. 깊은 중첩을 위해 20 공백 들여쓰기를 기대하는 개발자는 10만 얻게 되어 생성된 파일에서 예상치 못한 포매팅이 발생합니다.
해결책: 최대 들여쓰기는 10 공백입니다. 깊은 구조에는 2 공백 들여쓰기(JavaScript 프로젝트에서 가장 일반적인 관습)를 사용하고 접을 수 있는 에디터로 네비게이션하세요.
const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// 20 공백 들여쓰기를 기대——하지만 space는 10으로 잘림
JSON.stringify(deepConfig, null, 20)
// JSON.stringify(deepConfig, null, 10)과 동일한 출력
// 오류 없음, 경고 없음——조용히 잘림const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// 2 (대부분의 프로젝트) 또는 4 공백 사용——10을 넘지 마세요
JSON.stringify(deepConfig, null, 2)
// {
// "server": {
// "tls": {
// "certs": {
// "primary": "/etc/ssl/api.pem"
// }
// }
// }
// }JSON.stringify vs 대안 — 빠른 비교
상황에 따라 다른 도구가 필요합니다. replacer가 있는 JSON.stringify 는 의존성 없이 대부분의 프로덕션 사용 사례를 커버합니다. 색상 출력이 필요하고 유효한 JSON이 필요 없을 때 util.inspect 가 빠른 터미널 디버깅의 올바른 선택입니다.fast-json-stringify 는 프로파일링에서 직렬화 비용이 보이는 고처리량 라우트에서 효과를 발휘합니다—— 그 외의 경우에는 스키마 유지 관리 오버헤드가 가치 없습니다.
자주 묻는 질문
JavaScript에서 JSON을 예쁘게 출력하려면?
JSON.stringify(data, null, 2)를 호출하세요——세 번째 인수가 들여쓰기를 제어합니다. 공백에는 2나 4를, 탭에는 "\t"를 전달하세요. 가져오기나 설치가 필요 없습니다: JSON은 브라우저와 Node.js를 포함한 모든 JavaScript 환경의 전역 객체입니다.
const config = { host: "api.payments.internal", port: 8443, tls: true }
console.log(JSON.stringify(config, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "tls": true
// }JSON.stringify()의 space 파라미터는 무엇을 하나요?
space 파라미터는 출력의 들여쓰기를 제어합니다. 숫자(1~10)를 전달하면 그 수만큼의 공백이 레벨별로 들여쓰기되고, "\t" 같은 문자열을 전달하면 탭 문자가 사용됩니다. 10을 초과하는 값은 조용히 10으로 잘립니다. null, 0 또는 생략하면 컴팩트한 한 줄 JSON이 출력됩니다.
const data = { service: "payments", version: 3, active: true }
JSON.stringify(data, null, 2) // 2칸 들여쓰기
JSON.stringify(data, null, 4) // 4칸 들여쓰기
JSON.stringify(data, null, '\t') // 탭 들여쓰기
JSON.stringify(data) // 컴팩트: {"service":"payments","version":3,"active":true}JSON.stringify()가 일부 값에서 undefined를 반환하는 이유는?
JSON.stringify는 undefined, 함수, Symbol 값을 가진 객체 프로퍼티를 경고 없이 생략합니다——이 타입들은 JSON 표현이 없습니다. 최상위 값 자체가 undefined면 함수는 undefined를 반환합니다("undefined" 문자열이 아닙니다). 출력에 반드시 포함되어야 하는 선택적 필드에는 undefined 대신 null을 사용하세요.
const event = {
traceId: "tr_9a2f",
handler: () => {}, // 함수 — 생략됨
requestId: undefined, // undefined — 생략됨
sessionId: Symbol("s"), // Symbol — 생략됨
status: "ok"
}
JSON.stringify(event, null, 2)
// { "traceId": "tr_9a2f", "status": "ok" }JSON 포매팅 시 Date 객체는 어떻게 처리하나요?
Date 객체에는 ISO 8601 문자열을 반환하는 내장 toJSON() 메서드가 있어서 JSON.stringify가 자동으로 처리합니다. 날짜용 커스텀 replacer는 필요 없습니다——직렬화된 값은 "2026-03-10T14:22:00.000Z" 같은 문자열이 됩니다.
const order = {
orderId: "ord_8f2a91bc",
placedAt: new Date("2026-03-10T14:22:00Z"),
total: 42.98
}
JSON.stringify(order, null, 2)
// {
// "orderId": "ord_8f2a91bc",
// "placedAt": "2026-03-10T14:22:00.000Z",
// "total": 42.98
// }JavaScript에서 JSON 문자열(객체가 아닌)을 포매팅하려면?
먼저 JSON.parse()로 문자열을 파싱한 다음, JSON.stringify()로 다시 직렬화하세요. 두 호출을 한 줄로 체이닝해서 빠른 디버깅에 사용할 수 있습니다.
const raw = '{"endpoint":"/api/v2/users","timeout":30,"retry":true}'
const formatted = JSON.stringify(JSON.parse(raw), null, 2)
console.log(formatted)
// {
// "endpoint": "/api/v2/users",
// "timeout": 30,
// "retry": true
// }브라우저에서 JSON.stringify()를 사용할 수 있나요?
네. JSON은 IE8 이후의 모든 최신 브라우저에서 내장된 전역 객체입니다——script 태그나 import가 필요 없습니다. DevTools 콘솔을 열고 직접 JSON.stringify()를 호출하세요. Node.js 버전과 동일하게 동작하며, 파라미터 시그니처와 BigInt, 순환 참조에 대한 제한도 같습니다.
// Chrome, Firefox, Safari, Edge에서 동작——import 불필요
const payload = { userId: "usr_7b3c", action: "checkout", cart: ["SKU-001", "SKU-002"] }
copy(JSON.stringify(payload, null, 2)) // copy()는 DevTools 헬퍼 함수JavaScript는 완전한 제어권을 제공합니다——replacer 함수, 커스텀 toJSON(), Node.js에서 대용량 파일 처리. 포매팅된 스니펫을 검사하거나 공유하기만 하면 될 때, ToolDeck의 JSON Formatter 가 가장 빠른 방법입니다: JSON을 붙여넣으면 환경 설정 없이 포매팅되고 강조 표시된 결과를 얻을 수 있습니다.
관련 도구
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.