JSON Formatter Python — json.dumps() 가이드

·Backend Developer·검토자Dmitri Volkov·게시일

무료 JSON 포맷터을 브라우저에서 직접 사용하세요 — 설치 불필요.

JSON 포맷터 온라인으로 사용하기 →

Python API 클라이언트를 디버깅할 때 가장 먼저 찾는 것이 Python pretty print JSON입니다—— json.dumps(data, indent=4) 한 번 호출로 읽기 어려운 단일 줄 덩어리가 즉시 탐색 가능한 형태로 변환됩니다. Python의 내장 json 모듈은 표준 라이브러리에서 완전히 처리합니다——서드파티 설치가 필요 없습니다. 코드 없이 빠른 결과가 필요하다면 ToolDeck의 JSON Formatter 즉시 처리할 수 있습니다. 이 가이드는 모든 실용적인 방법을 다룹니다: 모든 파라미터가 포함된 json.dumps(), pprint, 고성능 포맷용 orjson, json.tool CLI, API 응답 포맷 및 디스크에서 읽기 등 실제 시나리오——모두 Python 3.8+ 호환 코드로 작성되었습니다. 또한 datetime UUID 같은 커스텀 타입 직렬화, ijson으로 기가바이트 규모 파일 스트리밍, rich로 터미널 구문 강조도 다룹니다.

핵심 정리
  • json.dumps(data, indent=4)는 Python 2.6부터 stdlib에 내장되어 있습니다——설치 불필요.
  • 데이터에 한글, 악센트 문자, 이모지가 포함된 경우 ensure_ascii=False를 전달하세요.
  • datetime, UUID, 커스텀 클래스에는 default= 파라미터 또는 json.JSONEncoder 서브클래스를 사용하세요.
  • separators=(',', ':')는 모든 공백을 제거합니다——네트워크 전송 또는 URL 삽입에 사용하세요.
  • orjson은 stdlib보다 5–10배 빠르고 datetimeuuid.UUID를 네이티브로 처리합니다.
  • pprint.pprint()는 Python 구문(True/None)을 출력합니다, 유효한 JSON이 아닙니다——파일이나 API 응답에는 절대 사용하지 마세요.
  • 50 MB를 초과하는 JSON 파일은 json.load() 대신 ijson으로 스트리밍하여 MemoryError를 방지하세요.

JSON Pretty Printing이란 무엇인가요?

Pretty printing은 밀집된 최소화된 JSON 문자열을 일관된 들여쓰기와 줄바꿈이 있는 사람이 읽기 쉬운 형식으로 변환합니다. 이 변환은 순전히 외관상의 변화입니다: 데이터는 동일하고 표현 방식만 바뀝니다. Python의 json 모듈은 표준 라이브러리에서 완전히 처리합니다——설치할 것이 없습니다.

Before · json
After · json
{"id":"usr_9f3a2b","name":"김민수","roles":["admin","editor"],"prefs":{"theme":"dark","lang":"ko"}}
{
    "id": "usr_9f3a2b",
    "name": "김민수",
    "roles": [
        "admin",
        "editor"
    ],
    "prefs": {
        "theme": "dark",
        "lang": "ko"
    }
}

json.dumps() — JSON을 포맷하는 표준 방법

json.dumps()는 Python 2.6부터 표준 라이브러리의 일부입니다—— import json만 하면 되고 설치가 필요 없습니다. JSON 호환 Python 객체를 포맷된 문자열로 직렬화합니다. 핵심 파라미터는 indent입니다:4(또는 2)로 설정하면 읽기 쉬운 출력이 됩니다.

Python 3.8+ — 최소 예제
import json

user = {
    "id": "usr_9f3a2b",
    "name": "김민수",
    "roles": ["admin", "editor"],
    "prefs": {"theme": "dark", "lang": "ko"}
}

print(json.dumps(user, indent=4, ensure_ascii=False))
# 출력:
# {
#     "id": "usr_9f3a2b",
#     "name": "김민수",
#     "roles": [
#         "admin",
#         "editor"
#     ],
#     "prefs": {
#         "theme": "dark",
#         "lang": "ko"
#     }
# }

프로덕션 환경에서는 보통 sort_keys=True (실행 간 일관된 출력)와 ensure_ascii=False (비ASCII 문자를 읽기 쉽게 보존)도 필요합니다:

Python 3.8+ — sort_keys와 ensure_ascii 사용
import json

api_response = {
    "timestamp": "2024-05-01T10:30:00Z",
    "status": "success",
    "data": {
        "user_id": "usr_9f3a2b",
        "display_name": "이지현",
        "city": "서울",
        "score": 4892.5,
        "tags": ["python", "backend", "api"]
    }
}

print(json.dumps(api_response, indent=4, sort_keys=True, ensure_ascii=False))
# 출력 (키 정렬, 한글 보존):
# {
#     "data": {
#         "city": "서울",
#         "display_name": "이지현",
#         "score": 4892.5,
#         "tags": ["api", "backend", "python"],
#         "user_id": "usr_9f3a2b"
#     },
#     "status": "success",
#     "timestamp": "2024-05-01T10:30:00Z"
# }
참고:json.dumps()는 문자열을 반환합니다. 포맷된 JSON을 파일에 직접 쓰려면 json.dump(data, f, indent=4)( s 없음)를 사용하세요——파일 객체에 쓰고 메모리에 중간 문자열을 만드는 것을 피할 수 있습니다.

json.dumps() 파라미터 참조

객체 자체를 제외한 모든 파라미터는 선택 사항입니다. 기본값은 컴팩트하고 ASCII 안전한 JSON을 생성합니다——사람이 읽기 쉬운 출력을 위해서는 파라미터를 명시적으로 전달하세요.

파라미터
타입
기본값
설명
obj
any
JSON 형식의 문자열로 직렬화할 Python 객체.
indent
int | str | None
None
들여쓰기 단계당 공백 수. None = 컴팩트 단일 줄, 0 = 줄바꿈만, 4 = 표준.
sort_keys
bool
False
모든 중첩 레벨에서 딕셔너리 키를 알파벳순으로 정렬.
ensure_ascii
bool
True
비ASCII 문자를 \uXXXX로 이스케이프. False로 설정하면 Unicode 문자를 그대로 보존.
separators
tuple | None
None
(item_sep, key_sep) 쌍. (",", ":") 를 사용하면 공백 없이 가장 컴팩트한 출력이 됨.
default
callable | None
None
기본적으로 직렬화할 수 없는 타입에 대해 호출됨. 값을 거부하려면 TypeError 발생.
allow_nan
bool
True
float("nan") 과 float("inf") 를 JS 리터럴로 직렬화. False로 설정하면 ValueError 발생.

separators 파라미터로 컴팩트한 JSON 출력하기

기본적으로 json.dumps()는 항목을 ", "로, 키와 값을 ": "로 구분합니다. separators 파라미터로 둘 다 재정의할 수 있습니다. (',', ':')를 전달하면 모든 공백이 제거되어 가장 컴팩트한 유효 JSON이 생성됩니다——네트워크 전송, URL 삽입, 또는 모든 바이트가 중요한 데이터베이스 컬럼에 JSON을 저장하는 데 유용합니다.

Python 3.8+
import json

payload = {
    "endpoint": "/api/v2/events",
    "filters": {"status": "active", "limit": 100},
    "sort": "desc"
}

# 기본 — 구분자 뒤에 공백 있음 (읽기 쉬움)
default_out = json.dumps(payload)
# {"endpoint": "/api/v2/events", "filters": {"status": "active", "limit": 100}, "sort": "desc"}
# len = 88

# 컴팩트 — 공백 없음
compact_out = json.dumps(payload, separators=(',', ':'))
# {"endpoint":"/api/v2/events","filters":{"status":"active","limit":100},"sort":"desc"}
# len = 80  (9% 절감; 더 크고 깊이 중첩된 payload에서 절감이 커짐)

# 컴팩트 + 정렬된 키 (재현 가능한 캐시 키 또는 콘텐츠 해시용)
canonical = json.dumps(payload, separators=(',', ':'), sort_keys=True)
print(canonical)
# {"endpoint":"/api/v2/events","filters":{"limit":100,"status":"active"},"sort":"desc"}
참고:indent= separators=를 함께 전달할 때,separators 인수는 인라인 구분자만 제어합니다——indent에 의한 줄바꿈과 들여쓰기는 유지됩니다. 컴팩트한 단일 줄 출력을 원한다면 indent를 생략하거나 (None 전달) separators=(',', ':')를 설정하세요.

default 파라미터로 커스텀 Python 객체 직렬화하기

표준 json 모듈은 dict, list, 문자열, 숫자, 불리언, None을 직렬화하지만 다른 타입에는 TypeError를 발생시킵니다. 프로덕션 코드에서 가장 흔한 두 가지는 datetime 객체와 UUID입니다.

Python 3.8+ — 커스텀 처리 없이 TypeError 발생
import json
from datetime import datetime, timezone
import uuid

order = {
    "order_id": uuid.uuid4(),            # ❌ TypeError: UUID is not JSON serializable
    "placed_at": datetime.now(timezone.utc),  # ❌ TypeError: datetime is not JSON serializable
    "total_usd": 142.50,
    "items": ["pro-subscription", "addon-storage"]
}

json.dumps(order)  # TypeError 발생

방법 1 — default= 파라미터

default=에 callable을 전달하세요. json.dumps()는 처리할 수 없는 모든 객체에 대해 이를 호출합니다. 직렬화 가능한 표현을 반환하거나, 명시적으로 지원하지 않는 타입에 대해서는 TypeError를 발생시키세요——알 수 없는 타입을 조용히 무시하지 마세요.

Python 3.8+
import json
from datetime import datetime, timezone, date
import uuid
from decimal import Decimal

def json_default(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, uuid.UUID):
        return str(obj)
    if isinstance(obj, Decimal):
        return float(obj)
    raise TypeError(f"Type {type(obj).__name__!r} is not JSON serializable")

order = {
    "order_id": uuid.uuid4(),
    "placed_at": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
    "total_usd": Decimal("142.50"),
    "items": ["pro-subscription", "addon-storage"]
}

print(json.dumps(order, indent=4, default=json_default))
# {
#     "order_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
#     "placed_at": "2024-05-01T10:30:00+00:00",
#     "total_usd": 142.5,
#     "items": ["pro-subscription", "addon-storage"]
# }

방법 2 — json.JSONEncoder 서브클래스화

여러 모듈에서 공유되는 재사용 가능한 인코딩 로직에는 json.JSONEncoder를 서브클래스화하는 것이 default 함수를 여기저기 전달하는 것보다 깔끔합니다. default 메서드를 오버라이드하고 최종 폴백으로 super().default(obj)를 호출하세요——이렇게 하면 지원되지 않는 타입에 대한 올바른 오류 동작이 유지됩니다.

Python 3.8+
import json
from datetime import datetime, timezone
import uuid
from decimal import Decimal

class AppEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, uuid.UUID):
            return str(obj)
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)  # 알 수 없는 타입에는 TypeError 발생

order = {
    "order_id": uuid.uuid4(),
    "placed_at": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
    "total_usd": Decimal("142.50"),
}

# cls= 로 인코더 클래스 전달
print(json.dumps(order, indent=4, cls=AppEncoder))
# default= 방법과 동일한 출력
참고:인식되지 않는 타입에는 항상 super().default(obj)를 호출하거나 (또는 명시적으로 TypeError를 발생시키세요). 모든 것에 대해 str(obj)를 조용히 반환하면 오류가 났어야 할 객체가 손상됩니다——프로덕션에서 추적하기 어려운 버그가 됩니다.

역방향 디코딩 — object_hook

인코딩은 절반에 불과합니다. JSON에서 커스텀 Python 객체를 재구성하려면json.loads() 또는 json.load()object_hook 함수를 전달하세요. 훅은 디코딩된 모든 JSON 객체(dict)에 대해 호출되며 대신 어떤 Python 값이든 반환할 수 있습니다——완전한 인코딩 ↔ 디코딩 왕복을 제공합니다.

Python 3.8+
import json
from datetime import datetime
from dataclasses import dataclass

@dataclass
class Event:
    name: str
    occurred_at: datetime
    user_id: str

def encode_event(obj):
    if isinstance(obj, Event):
        return {
            "__type__": "Event",
            "name": obj.name,
            "occurred_at": obj.occurred_at.isoformat(),
            "user_id": obj.user_id,
        }
    raise TypeError(f"Cannot serialize {type(obj)}")

def decode_event(d):
    if d.get("__type__") == "Event":
        return Event(
            name=d["name"],
            occurred_at=datetime.fromisoformat(d["occurred_at"]),
            user_id=d["user_id"],
        )
    return d

# 인코딩
event = Event("login", datetime(2024, 5, 1, 10, 30), "usr_9f3a2b")
json_str = json.dumps(event, default=encode_event, indent=4)

# Event 인스턴스로 다시 디코딩
restored = json.loads(json_str, object_hook=decode_event)
print(type(restored))           # <class 'Event'>
print(restored.occurred_at)     # 2024-05-01 10:30:00
참고:object_hook은 문서 내의 모든 중첩된 dict에 대해 호출됩니다——최상위 레벨만이 아닙니다. 커스텀 객체와 그대로 두어야 할 일반 dict를 훅이 구별할 수 있도록 식별자 필드(예: "__type__")를 포함하세요.

pprint — 대안 모듈 (및 사용하지 말아야 할 때)

Python의 pprint 모듈(pretty printer)은 터미널에서 가독성을 위해 Python 데이터 구조를 포맷합니다. 파싱된 Python 객체에서 작동하며 JSON 문자열에서가 아닙니다——그 출력은 JSON 구문이 아닌 Python 구문을 사용합니다.

Python 3.8+
import json, pprint

raw = '{"sensor_id":"s-441","readings":[23.1,23.4,22.9],"unit":"celsius","active":true}'
data = json.loads(raw)

# pprint — 유효한 Python repr, 유효한 JSON이 아님
pprint.pprint(data, sort_dicts=False)
# {'sensor_id': 's-441',
#  'readings': [23.1, 23.4, 22.9],
#  'unit': 'celsius',
#  'active': True}        ← Python True, JSON true가 아님

# json.dumps — 유효한 JSON
print(json.dumps(data, indent=4))
# {
#     "sensor_id": "s-441",
#     "readings": [23.1, 23.4, 22.9],
#     "unit": "celsius",
#     "active": true      ← 유효한 JSON
# }
경고:pprint 출력을 API 엔드포인트에 보내거나.json 파일에 쓰지 마세요——표준 구문을 기대하는 JSON 파서는 모두 실패합니다. 유효한 JSON이어야 하는 모든 출력에는 json.dumps(indent=4)를 사용하세요.

pprint가 적합한 경우: REPL이나 디버그 로그에서 Python 객체를 빠르게 확인할 때, 특히 객체에 JSON 직렬화가 불가능한 타입(set, 커스텀 클래스 인스턴스, 변환 전 dataclass)이 포함된 경우.

Requests의 JSON 응답을 보기 좋게 출력하는 방법

가장 일반적인 실제 시나리오: 디스크에 JSON 파일이 있거나 API로부터 HTTP 응답이 있고 디버깅이나 로깅을 위해 포맷하고 싶은 경우. 두 경우 모두 같은 접근 방식을 사용합니다—— Python dict로 파싱한 다음 json.dumps()로 포맷합니다.

파일에서 읽기

Python 3.8+
import json

try:
    with open("config.json", "r", encoding="utf-8") as f:
        data = json.load(f)

    # 콘솔에 보기 좋게 출력
    print(json.dumps(data, indent=4, ensure_ascii=False))

    # 또는 포맷된 버전을 디스크에 다시 쓰기
    with open("config.pretty.json", "w", encoding="utf-8") as f:
        json.dump(data, f, indent=4, ensure_ascii=False)

except json.JSONDecodeError as e:
    print(f"유효하지 않은 JSON: {e}")
except FileNotFoundError:
    print(f"파일을 찾을 수 없음: config.json")

API 응답 포맷하기

Python 3.8+ (필요: pip install requests)
import json, requests
from requests.exceptions import HTTPError, ConnectionError, Timeout

def pretty_print_api(url: str) -> None:
    try:
        resp = requests.get(url, timeout=10)
        resp.raise_for_status()
        print(json.dumps(resp.json(), indent=4, ensure_ascii=False))
    except HTTPError as e:
        print(f"HTTP {e.response.status_code}: {e}")
    except (ConnectionError, Timeout) as e:
        print(f"네트워크 오류: {e}")
    except json.JSONDecodeError:
        print(f"응답 본문이 JSON이 아닙니다:\n{resp.text[:500]}")

pretty_print_api("https://api.github.com/repos/python/cpython")
참고:response.json()은 이미 응답 본문을 파싱합니다—— 별도로 json.loads()를 호출할 필요가 없습니다..json()에 접근하기 전에 항상 raise_for_status()를 추가하여 4xx/5xx 오류가 혼란스러운 파싱 오류를 일으키기 전에 잡아내세요.

명령줄 Pretty Printing

Python에는 json.tool이 포함되어 있습니다——터미널에서 직접 JSON을 포맷하는 CLI 모듈로 Python 스크립트가 필요 없습니다. Python이 설치된 모든 머신에서 사용 가능합니다.

bash
# 로컬 파일 포맷
python -m json.tool config.json

# API 응답을 포맷터로 파이프
curl -s https://api.github.com/users/gvanrossum | python -m json.tool

# stdin에서 포맷
echo '{"service":"api-gateway","version":"2.1.0","healthy":true}' | python -m json.tool

# 키를 알파벳순으로 정렬
python -m json.tool --sort-keys data.json

# 커스텀 들여쓰기 (Python 3.9+)
python -m json.tool --indent 2 data.json
참고:Python 3.9에서 --indent --no-indent 플래그가 추가되었습니다. 더 강력한 터미널 JSON 필터링을 위해서는 jq를 고려해 보세요——하지만 python -m json.tool은 추가 의존성 없이 포맷 사용 사례를 커버합니다.

터미널이 없는 환경 — Postman 응답이나 로그 파일을 붙여넣는 경우 — ToolDeck의 JSON Formatter 사용하면 신택스 하이라이팅과 유효성 검사가 내장된 상태로 한 번에 붙여넣기, 포매팅, 복사가 가능합니다.

대안 라이브러리: orjson과 rich

orjson — 5–10배 빠르고 네이티브 타입 지원

표준 json 모듈은 대부분의 사용 사례에 충분히 빠르지만, 초당 수천 개의 객체를 직렬화해야 한다면—— 로깅 파이프라인, 고처리량 API, 대규모 데이터 내보내기—— orjson은 5–10배 빠릅니다. 또한 표준 라이브러리가 커스텀 default 함수 없이는 직렬화할 수 없는 타입도 네이티브로 처리합니다: datetime, uuid.UUID, numpy 배열, dataclass.

bash — 설치
pip install orjson
Python 3.8+
import orjson
from datetime import datetime, timezone
import uuid

event = {
    "event_id": uuid.uuid4(),                  # str() 불필요 — orjson이 UUID 처리
    "timestamp": datetime.now(timezone.utc),   # isoformat() 불필요
    "service": "auth-service",
    "level": "INFO",
    "payload": {
        "user_id": "usr_9f3a2b",
        "action": "login",
        "ip": "192.168.1.42",
        "latency_ms": 34
    }
}

# orjson.dumps는 bytes를 반환; .decode()로 str로 변환
print(orjson.dumps(event, option=orjson.OPT_INDENT_2).decode())
# {
#   "event_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
#   "timestamp": "2024-05-01T10:30:00+00:00",
#   "service": "auth-service",
#   ...
# }

알아야 할 두 가지: orjson.dumps()는 문자열이 아닌 bytes를 반환합니다——문자열이 필요하면 .decode()를 호출하세요. OPT_INDENT_2를 통한 2칸 들여쓰기만 지원합니다; 4칸 출력은 표준 json.dumps(indent=4)를 사용하세요.

rich — 터미널에서 구문 강조

터미널이나 REPL에서 JSON을 자주 확인한다면, rich는 깊이 중첩된 구조를 한눈에 읽기 쉽게 만드는 색상 코딩된 구문 강조 출력을 렌더링합니다. 키, 문자열, 숫자, 불리언 각각 다른 색상을 가집니다——단색 텍스트 벽보다 훨씬 스캔하기 쉽습니다. 디버그 시에만 사용하는 도구이며 프로덕션 직렬화에는 사용하지 않습니다.

bash — 설치
pip install rich
Python 3.8+
from rich import print_json
import json

# print_json()은 JSON 문자열을 받음
raw = '{"event":"login","user_id":"usr_9f3a2b","timestamp":"2024-05-01T10:30:00Z","success":true,"meta":{"ip":"192.168.1.42","attempts":1}}'
print_json(raw)

# Python dict를 출력하려면 먼저 문자열로 변환
data = {
    "status": "success",
    "count": 42,
    "tags": ["python", "api", "backend"]
}
print_json(json.dumps(data))
경고:rich.print_json()은 터미널 색상을 위한 ANSI 이스케이프 코드를 출력합니다——이 출력을 .json 파일에 쓰거나 API 응답으로 보내지 마세요. 기계가 읽을 수 있는 출력에는 json.dumps(indent=4)를 사용하세요.

simplejson — 드롭인 호환성 대체품

simplejson은 Python의 표준 json 모듈이 된 라이브러리입니다——지금도 독립적으로 유지 관리되며 마이너 기능에서 stdlib보다 앞서 있습니다. 진정한 드롭인 대체품입니다: import를 교체하면 나머지 코드는 변경되지 않습니다. 커스텀 인코더 없이 Decimal 지원이 필요하거나 오래된 Python 환경을 타겟으로 할 때 유용합니다.

bash — 설치
pip install simplejson
Python 3.8+
import simplejson as json  # stdlib와 동일한 API
from decimal import Decimal

order = {
    "item": "API subscription",
    "price": Decimal("49.99"),   # stdlib json은 여기서 TypeError 발생
    "quantity": 3,
}

# simplejson은 Decimal을 네이티브로 직렬화 — default= 불필요
print(json.dumps(order, indent=4, use_decimal=True))
# {
#     "item": "API subscription",
#     "price": 49.99,
#     "quantity": 3
# }
참고:순수 성능을 위해서는 orjson이 더 나은 선택입니다. 커스텀 인코더를 작성하지 않고 네이티브 Decimal 직렬화가 필요하거나 이미 사용 중인 코드베이스를 유지 관리할 때 simplejson을 선택하세요.

메모리 부족 없이 대용량 JSON 파일 처리하기

json.load()는 단 하나의 필드에 접근하기 전에 전체 파일을 메모리에 읽어 들입니다. 수백만 개의 레코드가 있거나 기가바이트를 넘는 파일에서는 MemoryError가 발생하거나, 최소한 프로세스가 디스크 스왑을 사용하여 극도로 느려집니다.

ijson으로 스트리밍

ijson은 파일 객체에서 한 번에 하나씩 항목을 생성하는 스트리밍 JSON 파서입니다. 전체 데이터셋을 메모리에 보유하지 않고 배열 요소를 반복할 수 있습니다—— 최대 메모리는 파일 크기가 아닌 단일 객체에 비례합니다.

bash — 설치
pip install ijson
Python 3.8+
import ijson
from decimal import Decimal

# events.json 구조: {"events": [...수백만 개의 객체...]}
total_revenue = Decimal("0")
login_count = 0

with open("events.json", "rb") as f:   # ijson은 바이너리 모드 필요
    for event in ijson.items(f, "events.item"):
        if event.get("type") == "purchase":
            total_revenue += Decimal(str(event["amount_usd"]))
        elif event.get("type") == "login":
            login_count += 1

print(f"Revenue: ${total_revenue:.2f}  |  Logins: {login_count}")
# 2 GB 파일을 ~30 MB 최대 메모리 사용량으로 처리
참고:파일이 약 50–100 MB를 초과하면 json.load()에서 ijson으로 전환하세요. 그 임계값 이하에서는 json.load()가 더 간단하고 내부적으로 C 확장 파서를 사용하기 때문에 훨씬 빠릅니다. 100 MB 이상에서는 스트리밍의 메모리 절감이 추가 오버헤드를 상쇄합니다.

NDJSON — 줄당 JSON 객체 하나

NDJSON(줄바꿈 구분 JSON, JSON Lines 또는 .jsonl이라고도 함)은 줄당 하나의 완전한 JSON 객체를 저장합니다. 로그 내보내기, Kafka 소비자, 데이터 파이프라인이 이 형식을 자주 생성합니다——각 줄을 독립적으로 추가하고 읽을 수 있기 때문입니다. 추가 의존성 없이 표준 라이브러리로 처리할 수 있습니다.

Python 3.8+
import json
from pathlib import Path

# NDJSON 쓰기 — 이벤트당 한 줄
events = [
    {"ts": "2024-05-01T10:00:00Z", "user": "usr_9f3a2b", "action": "login"},
    {"ts": "2024-05-01T10:01:03Z", "user": "usr_9f3a2b", "action": "purchase", "sku": "pro-plan"},
    {"ts": "2024-05-01T10:15:42Z", "user": "usr_4ab1d9", "action": "login"},
]

with open("events.ndjson", "w", encoding="utf-8") as f:
    for event in events:
        f.write(json.dumps(event, ensure_ascii=False) + "\n")

# NDJSON 읽기 — 파일 크기에 관계없이 일정한 메모리
purchase_count = 0
with open("events.ndjson", "r", encoding="utf-8") as f:
    for line in f:
        line = line.strip()
        if not line:           # 빈 줄 건너뛰기
            continue
        event = json.loads(line)
        if event.get("action") == "purchase":
            purchase_count += 1
            print(f"{event['ts']} — {event['user']} bought {event['sku']}")

흔한 실수

거의 모든 코드 리뷰에서 이 네 가지 실수를 봐왔습니다——특히 JSON.stringify가 인코딩을 자동으로 처리하는 JavaScript에서 온 개발자들에게서 자주 보입니다.

json.dumps() 대신 print(data) 사용하기

문제: dict에 print()를 사용하면 Python repr이 사용됩니다——출력에 True/None(Python 구문)이 표시되며 true/null(JSON 구문)이 아닙니다. 유효한 JSON이 아닙니다.

해결책: 유효하고 읽기 쉬운 JSON 출력을 위해 항상 json.dumps(data, indent=4)를 사용하세요.

Before · Python
After · Python
data = {"active": True, "count": None}
print(data)
# {'active': True, 'count': None}
print(json.dumps(data, indent=4))
# {
#     "active": true,
#     "count": null
# }
비ASCII 텍스트에서 ensure_ascii=False를 잊기

문제: 특수 문자(한글, 악센트 문자, 이모지)가 \\uXXXX 시퀀스로 이스케이프되어 출력을 읽기 어렵게 만듭니다.

해결책: 원래 Unicode 문자를 보존하기 위해 ensure_ascii=False를 전달하세요.

Before · Python
After · Python
user = {"name": "김민수", "city": "부산"}
json.dumps(user, indent=2)
# {"name": "\uae40\ubbfc\uc218", "city": "\ubd80\uc0b0"}
json.dumps(user, indent=2, ensure_ascii=False)
# {"name": "김민수", "city": "부산"}
파일 쓰기에 json.dumps() 사용하기

문제: json.dumps()는 문자열을 반환합니다; 그런 다음 별도의 f.write() 호출이 필요하여 불필요한 중간 문자열을 만듭니다.

해결책: json.dump(data, f, indent=4)를 사용하세요——파일 객체에 직접 씁니다.

Before · Python
After · Python
with open("out.json", "w") as f:
    f.write(json.dumps(data, indent=4))
with open("out.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=4, ensure_ascii=False)
pprint로 유효한 JSON을 기대하기

문제: pprint.pprint()는 Python 구문(True, None, 작은따옴표)을 사용하며 JSON 파서는 이를 거부합니다.

해결책: JSON으로 파싱될 수 있어야 하는 모든 출력에는 json.dumps(indent=4)를 사용하세요.

Before · Python
After · Python
import pprint
pprint.pprint({"running": True, "last_error": None})
# {'running': True, 'last_error': None}
import json
print(json.dumps({"running": True, "last_error": None}, indent=4))
# {"running": true, "last_error": null}

메서드 비교 — json.dumps, orjson, simplejson, rich

일상적인 포맷과 파일 쓰기에는 json.dumps()를 사용하세요——의존성 없이 95%의 사용 사례를 커버합니다. 핫 경로에서 직렬화하거나 객체에 datetime UUID 필드가 포함된 경우 orjson을 선택하세요. stdlib 호환성과 즉시 사용 가능한 Decimal 지원이 필요하면 simplejson을 사용하세요. rich.print_json() pprint는 로컬 터미널 확인에만 엄격히 한정하세요——둘 다 기계가 읽을 수 있는 출력을 생성하지 않습니다.

메서드
출력
유효한 JSON
속도
Non-ASCII
커스텀 타입
설치
json.dumps(indent=4)
문자열
표준
ensure_ascii=False
default= / JSONEncoder
내장
json.dump(f, indent=4)
파일
표준
ensure_ascii=False
default= / JSONEncoder
내장
pprint.pprint()
Python repr
표준
네이티브
✅ (모든 repr)
내장
orjson.dumps(OPT_INDENT_2)
Bytes
5–10× 빠름
네이티브
datetime, UUID, numpy
pip install orjson
python -m json.tool
CLI stdout
표준
내장
simplejson.dumps()
문자열
~1.5× 빠름
ensure_ascii=False
Decimal 네이티브 지원
pip install simplejson
rich.print_json()
터미널 전용
✅ (입력)
표준
pip install rich

자주 묻는 질문

Python에서 JSON을 보기 좋게 출력하려면 어떻게 하나요?

json.dumps(data, indent=4) 를 호출하세요. indent 파라미터는 중첩 레벨당 공백 수를 설정합니다. 먼저 json 모듈을 임포트하세요——Python 표준 라이브러리에 포함되어 있어 pip install이 필요 없습니다. 데이터에 한글이나 특수 문자가 포함된 경우 ensure_ascii=False를 전달하세요.

python
import json

user = {"username": "kim_minsoo", "plan": "enterprise", "permissions": ["read", "write", "deploy"]}
print(json.dumps(user, indent=4, ensure_ascii=False))

json.dumps()와 json.dump()의 차이는 무엇인가요?

json.dumps()("s" 포함)는 메모리에 포맷된 문자열을 반환합니다. json.dump()("s" 없음)는 파일 유사 객체에 직접 씁니다——열린 파일 핸들을 두 번째 인수로 전달하세요. 포맷된 JSON을 디스크에 쓰려면 json.dump(data, f, indent=4)가 관용적이고 중간 문자열 생성을 피할 수 있습니다.

python
# dumps → 메모리의 문자열
formatted = json.dumps(data, indent=4)

# dump → 파일에 직접 쓰기
with open('output.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, indent=4)

json.dumps()가 실제 문자 대신 \uc11c\uc6b8을 표시하는 이유는 무엇인가요?

기본적으로 ensure_ascii=True는 모든 비ASCII 문자를 \uXXXX 시퀀스로 이스케이프합니다. 원래 Unicode 문자를 보존하려면 ensure_ascii=False를 설정하세요. 이는 한국어 이름, 주소 및 비라틴 문자를 사용하는 모든 사용자 생성 콘텐츠에 특히 중요합니다.

python
data = {"city": "서울", "greeting": "안녕하세요"}

# 기본 — 이스케이프됨
json.dumps(data, indent=4)
# {"city": "\uc11c\uc6b8", "greeting": "\uc548\ub155\ud558\uc138\uc694"}

# 읽기 쉬운 형태
json.dumps(data, indent=4, ensure_ascii=False)
# {"city": "서울", "greeting": "안녕하세요"}

JSON 문자열(dict가 아닌)을 보기 좋게 출력하려면 어떻게 하나요?

먼저 json.loads()로 문자열을 파싱한 다음 json.dumps()로 포맷하세요. 두 호출을 한 줄로 체인화하여 터미널에서 빠르게 확인할 수 있습니다.

python
import json

raw = '{"endpoint":"/api/v2/users","timeout":30,"retry":true}'
print(json.dumps(json.loads(raw), indent=4))

Python에서 pprint로 JSON을 포맷할 수 있나요?

pprint.pprint()는 Python 객체 표현을 생성하며 유효한 JSON이 아닙니다. true/false/null(JSON 구문) 대신 True/False/None(Python 구문)을 사용합니다. pprint 출력을 API나 JSON 파서에 전달하지 마세요——유효한 JSON이 필요한 경우 json.dumps(indent=4)를 사용하세요.

python
import pprint, json

data = {"active": True, "score": None}

pprint.pprint(data)     # {'active': True, 'score': None}  ← JSON이 아님
json.dumps(data, indent=4)  # {"active": true, "score": null}  ← 유효한 JSON

Python에서 JSON 키를 알파벳순으로 정렬하려면 어떻게 하나요?

json.dumps()에 sort_keys=True를 추가하세요. 명령줄에서는 python -m json.tool --sort-keys data.json을 사용하세요. 정렬된 키는 JSON diff를 읽기 쉽게 만들고 변경된 값을 한눈에 파악하는 데 도움이 됩니다.

python
import json

server = {"workers": 4, "host": "0.0.0.0", "port": 8080, "debug": False}
print(json.dumps(server, indent=4, sort_keys=True))
# {
#     "debug": false,
#     "host": "0.0.0.0",
#     "port": 8080,
#     "workers": 4
# }

Python은 커스텀 직렬화, 스트리밍, 파이프라인 통합 등 완전한 제어를 제공합니다. 포매팅된 스니펫을 확인하거나 공유하기만 하면 될 때는 ToolDeck의 JSON Formatter 더 빠른 방법입니다: JSON을 붙여넣으면 환경 설정 없이 들여쓰기 및 하이라이트된 결과를 얻을 수 있습니다.

관련 도구

다른 언어로도 제공됩니다:GoJavaScriptBash
MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

DV
Dmitri Volkov기술 검토자

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.