JSON → Zod 변환

JSON에서 Zod 스키마를 생성하고 각각에 z.infer 타입을 붙입니다. 배열 요소 병합, 선택적 키와 nullable 표시 — 샘플에서 아무것도 추측하지 않습니다.

입력
Zod 스키마
import * as z from "zod"

export const Customer = z.object({
  name: z.string(),
  email: z.string(),
  phone: z.null(),
})
export type Customer = z.infer<typeof Customer>

export const LineItem = z.object({
  sku: z.string(),
  quantity: z.number(),
  price: z.number(),
  note: z.string().nullable(),
  backordered: z.boolean().optional(),
})
export type LineItem = z.infer<typeof LineItem>

export const Root = z.object({
  id: z.string(),
  customer: Customer,
  lineItems: z.array(LineItem),
  tags: z.array(z.unknown()),
})
export type Root = z.infer<typeof Root>

Zod 4 문법이며 Zod 3에서도 유효합니다. z.object는 나열하지 않은 키를 모두 버리므로, 샘플에 없던 필드는 파싱한 데이터에서 아무 경고 없이 사라집니다.

  • 아래 각 위치에서 샘플은 null만 담고 있었으므로, 스키마는 그곳에서 다른 값을 받지 않습니다.

    위치: 1

    • Customer.phone
  • 아래 각 위치에서 모든 숫자가 정수였습니다. z.number()는 7.5도 받아들이고 .int()는 정수를 요구하므로, 값이 반드시 정수여야 한다고 알고 있는 곳에만 직접 추가하세요.

    위치: 1

    • LineItem.quantity
  • 아래 각 위치에서는 아무것도 추론할 수 없었으므로, 스키마는 그곳에 있는 것을 검사하지 않습니다. z.unknown()은 어떤 값이든, z.record(z.string(), z.unknown())은 어떤 객체든 받아들입니다.

    위치: 1

    • Root.tags[]

데이터가 도착할 때마다 실행되는 검사

TypeScript 타입은 코드를 컴파일할 때 검사되고, 코드가 실행될 즈음에는 이미 사라지고 없습니다. Zod 스키마는 실행 시점까지 남는 부분입니다. 프로그램 안에서 실행되면서 들어오는 페이로드를 — API 응답이든 웹훅 본문이든 큐에서 꺼낸 메시지든 — 코드가 그것에 기대기 전에 하나하나 검사합니다. 그 JSON의 샘플을 붙여넣으면 이 페이지가 모듈에 그대로 붙여넣을 수 있는 스키마를 써 줍니다. 키가 있는 객체는 모두 이름 있는 "z.object"가 되고, 배열의 요소는 하나로 병합되며, 각 스키마 뒤에는 그 스키마가 만들어 내는 TypeScript 타입이 이어집니다.

같은 JSON에 대해 JSON → TypeScript 변환 페이지가 내놓는 답을, 타입이 아니라 검증기로 적은 것입니다. 두 페이지는 하나의 추론을 읽으므로, 어떤 키가 선택적인지, 어떤 값이 union이 되는지, null을 없는 키와 어디서 구분하는지, 중첩된 각 객체를 무엇이라 부르는지는 한 번 정해지고 두 번 적힐 뿐입니다. 이런 병합 규칙은 JSON → TypeScript 변환 페이지에 있는 가이드가 다루는 주제이며 여기서 다시 설명하지 않습니다. 이 가이드는 스키마가 실행된 뒤 실제 데이터로 무엇을 하는지, 그리고 무엇을 여러분의 판단에 남겨 두는지를 다룹니다.

무엇이 통과하고, 무엇이 거부되는가

이 페이지의 스키마는 자신을 만든 바탕인 샘플을 Zod 3에서도 Zod 4에서도 똑같이 받아들입니다. 예외는 따로 알림이 있는 한 가지 경우, 곧 Zod 4에서의 무한대 숫자뿐입니다. 그 샘플을 벗어나면 스키마가 말하는 범위 안에 머무는 것은 무엇이든 받아들이며, 실제로 받게 될 데이터에서는 다음과 같이 됩니다.

  • 깊이 한도에 이르기 전까지는 ".optional()" 없이 적힌 키가 필수입니다. 그 키를 빠뜨린 페이로드는 거부되고, 스키마가 이름을 대지 않은 종류의 값을 그 자리에 둔 페이로드도 거부됩니다 — "z.number()"라고 적힌 곳의 문자열이나 "z.string()"이라고 적힌 곳의 객체가 그렇습니다.
  • ".optional()"을 붙여 적은 키는 빠져 있어도 되고, ".nullable()"을 붙여 적은 키는 null을 담아도 됩니다. 둘 다 그 밖의 종류의 값은 통과시키지 않으므로, 선택적 키가 있다면 여전히 스키마가 이름을 댄 것을 담고 있어야 합니다.
  • "z.union"은 자신의 멤버 가운데 어느 것이든 받아들이고, 그 밖의 것은 받아들이지 않습니다. "z.array"는 각 요소가 요소를 위해 적힌 스키마에 맞기만 하면, 하나도 없는 경우까지 포함해 요소가 몇 개든 받아들입니다.
  • 샘플이 아무것도 보여 주지 않은 곳 — 빈 배열의 요소, 키가 없는 객체, 깊이 한도를 넘어 중첩된 값 — 에서는 스키마가 그곳에 있는 것을 검사하지 않습니다. 어떤 값이든 받아들이고, 샘플의 객체에 키가 없던 곳에서는 어떤 객체든 받아들이며, 그곳이 어디인지는 알림이 알려 줍니다.

스키마가 나열하지 않은 키는 통과된 뒤 결과에서 버려집니다. 이것이 버리는 동작이며, 이에 관해서는 따로 절이 있습니다. 이 모든 것 바깥에 있는 키가 하나 있습니다. "__proto__"라고 적힌 키는 대괄호 안에 계산된 키로 적힙니다. 그냥 적으면 키에 이름을 붙이는 대신 그 키가 놓인 객체의 프로토타입을 설정하게 되기 때문입니다 — 그런데도 어느 버전의 Zod도 파싱이 돌려주는 결과에 그 키를 담지 않으며, Zod 4는 그 값을 아예 검사하지 않습니다.

왜 스키마는 스스로 엄격해지지 않는가

모든 샘플에는 그 샘플이 증명할 수 없는 공통점이 있습니다. 모든 ID가 정수이고, 모든 이메일이 이메일 모양이며, 역할에는 늘 admin이라고만 적혀 있는 식입니다. 그것은 규칙성이고, 규칙성은 검사가 아닙니다. 생성기는 그래도 검사를 적어 넣고 싶어지기 마련입니다 — ID에 ".int()"를, 주소에 "z.email()"을, 역할에 리터럴을 — 하지만 이 생성기는 결코 그러지 않습니다. 샘플이 보여 주는 것은 데이터가 담을 수 있는 것이지 반드시 담아야 하는 것이 아니기 때문입니다. 데이터보다 엄격한 타입의 대가는 자기 기기에서 나는 컴파일 오류입니다. 데이터보다 엄격한 스키마의 대가는 운영 환경에서 거부되는 요청입니다. admin이 아닌 첫 역할, 7.5인 첫 ID, 패턴이 내다보지 못한 첫 주소가 그렇습니다.

게다가 그런 검사는 발밑에서 바뀝니다. Zod 4의 "z.uuid()"는 변형 비트를 확인하기 때문에 Zod 3의 ".uuid()"가 받아들이던 UUID 모양의 문자열을 거부하고, Zod 4의 ".int()"는 Zod 3의 것이 통과시키던 안전한 범위 밖의 정수를 거부합니다 — 그러니 오늘의 샘플에서 추측한 검사는 어느 Zod를 설치하느냐에 따라 다른 검사가 됩니다. 그래서 샘플이 그저 암시할 뿐인 것은 스키마에 적히지 않습니다. 데이터가 도착할 때 문제가 되는 곳에서는 대신 페이지가 알림으로 알려 주고, 수정은 여러분에게 맡겨집니다.

스키마가 스스로 말하지 않는 것

스키마 아래에 페이지는 알아차렸지만 적어 넣지 않은 것을 나열합니다. 아래 종류 가운데 해당하는 것마다 알림이 하나씩, 그 알림이 들어맞는 모든 위치와 함께 한 번 표시됩니다. 위치는 출력이 이름을 붙이는 방식 그대로 적힙니다 — 키는 "Customer.phone", 배열의 요소는 "Root.tags[]", 식별자가 아닌 키와 "__proto__"는 따옴표와 대괄호로 감싼 키로 — 그래서 스키마에서 한눈에 찾을 수 있습니다. 페이지를 열 때 불러와지는 예제는 Infinity에 관한 것을 뺀 모든 종류를 보여 줍니다.

  • null만. 샘플은 그 위치에서 null 말고는 아무것도 담은 적이 없으므로, 스키마는 "z.null()"이라고 적고 첫 실제 값을 거부합니다. 그 필드가 채워질 때 무엇을 담을지 정해 직접 적거나 — 예를 들어 "z.string().nullable()" — 값이 들어 있는 샘플을 붙여넣으세요.
  • Infinity. 1e999처럼 JavaScript의 숫자가 담을 수 있는 범위를 넘는 숫자는 JSON을 파싱할 때 Infinity 또는 -Infinity가 됩니다. Zod 4의 "z.number()"는 무한대 숫자를 거부하고 Zod 3의 것은 받아들이므로, Zod 4에서는 이것이 스키마가 자신이 만들어진 바로 그 샘플을 거부하는 유일한 경우입니다. 이것이 던지는 질문은 스키마가 아니라 데이터에 관한 것입니다. 어떤 검증기가 보기도 전에 자릿수는 이미 사라졌으므로, 그 값이 애초에 숫자여야 하는지는 그것을 써낸 쪽이 답할 일입니다.
  • 정수. 그 위치의 숫자는 모두 정수였고, "z.number()"는 7.5도 받아들입니다. 값이 반드시 정수여야 하는 곳 — ID, 개수, 수량 — 에는 ".int()"를 직접 추가하고, 우연히 딱 떨어졌을 뿐인 가격이라면 그대로 두세요. "Number.MIN_SAFE_INTEGER"부터 "Number.MAX_SAFE_INTEGER"까지의 안전한 범위 밖에 있는 정수를 담은 위치에는 이 알림을 내지 않습니다. Zod 4의 ".int()"가 그런 정수를 거부하기 때문이며, 스키마가 자기 샘플을 거부하게 만들 조언이야말로 페이지가 결코 하지 않는 단 한 종류의 조언입니다.
  • 추론할 것이 없음. 빈 배열의 요소, 키가 없는 객체, 그리고 100단계보다 깊이 중첩된 값은 추론에 아무런 단서도 주지 않으므로, 스키마는 그곳에 있는 것을 검사하지 않습니다. "z.unknown()"은 어떤 값이든 받아들이고, "z.record(z.string(), z.unknown())"은 어떤 객체든 받아들입니다. 그 배열에 요소가 있고 그 객체에 키가 있는 샘플을 붙여넣거나, 스키마의 그 부분을 손으로 작성하세요.

알림은 스키마를 단 한 바이트도 바꾸지 않습니다. 알림은 스키마 곁에 페이지의 언어로 놓인 한 문장이며, 그것이 가리키는 수정을 할지 말지는 여러분의 몫입니다. 문자열 형식, 리터럴, enum에 관한 알림은 없습니다. 어느 것이든 샘플이 보여 줄 수 없는 규칙을 추측하는 일이 되기 때문입니다.

스키마가 나열하지 않은 키는 버려진다

"z.object"는 나열하지 않은 키를 지닌 객체를 통과시키고, 돌려주는 결과에서는 그 키들을 뺍니다. 이것은 두 버전 모두에서 Zod의 기본 동작이며, 스키마가 하는 일 가운데 가장 조용한 일입니다. 샘플에 우연히 없던 필드는 그렇다고 알리는 오류 하나 없이 코드가 받는 데이터에서 사라집니다. 이 페이지의 모든 스키마 아래에 있는 문장이 이것을 언급하는 까닭이 여기에 있습니다.

페이지는 엄격한 쪽도 느슨한 쪽도 여러분 대신 고르지 않습니다. 어느 쪽이든 샘플이 보여 줄 수 있는 것보다 많은 것을 주장하기 때문입니다. 엄격한 객체는 나열하지 않은 키를 모두 거부합니다. 이 키들 말고는 없다는 뜻이지만, 어떤 샘플도 다음 페이로드에 대해 그것을 증명할 수 없습니다. 느슨한 객체는 여분의 키를 남겨 두고, 그 추론된 타입에는 그 키들을 위한 인덱스 시그니처가 더해지므로 더는 TypeScript 페이지의 답이 아니게 됩니다. 버리는 동작은 TypeScript 타입이 받아들이는 것을 받아들이면서도 그 타입을 그대로 추론하는 유일한 동작입니다 — 여분의 속성이 있는 값도 인터페이스를 만족하기 때문입니다. 다르게 고르려면 스키마를 손으로 바꾸세요.

  • 스키마가 나열하지 않은 키를 거부하려면, Zod 4에서는 출력이 "z.object"라고 적은 곳에 "z.strictObject"를 적고, Zod 3에서는 "z.object"에 ".strict()"를 체인으로 붙이세요.
  • 그 키들을 남기려면 Zod 4에서는 "z.looseObject"를 적고, Zod 3에서는 ".passthrough()"를 체인으로 붙이세요. Zod 4는 Zod 3의 그 두 메서드를 지금도 실행하며, 레거시라고 부릅니다.

출력의 각 객체는 저마다 하나의 스키마이므로, 선택은 객체 하나씩 이루어집니다. 루트를 엄격하게 만들어도 그 안에 중첩된 객체에는 아무것도 달라지지 않는데, 고집할 수 있는 것이 가장 바깥 껍데기뿐일 때는 흔히 그것이 원하는 바입니다.

스키마와 그 타입에 하나의 이름

각 스키마 뒤에는 그 타입이 이어집니다 — "export const Customer" 바로 뒤에 "export type Customer = z.infer<typeof Customer>" — 이것이 zod.dev가 자신의 예제를 쓰는 방식입니다. 데이터를 검사하는 값과 그 값이 만들어 내는 타입에 하나의 이름을 쓰는데, TypeScript가 값과 타입을 서로 다른 네임스페이스에 두기 때문입니다. 그 타입은 JSON → TypeScript 변환 페이지가 같은 JSON에 대해 출력하는 타입과 이름 하나하나, 키 하나하나까지 같고, 선택적 키도 union도 같으며 null도 같은 자리에 있습니다 — 아래의 예외를 빼면 말입니다.

저장소는 그 약속을 믿는 대신 확인합니다. 샘플 모음이 두 페이지를 거친 다음 Zod의 각 버전과 함께 TypeScript 컴파일러를 거치고, 컴파일러는 이름마다 두 타입이 동일한지, 그리고 각각이 서로에게 할당 가능한지를 질문받습니다. 다른 답이 나오는 곳은 문서의 깊은 곳입니다. 깊이 한도를 넘어 키가 "z.unknown()"을 담는 곳에서는 Zod 3이 그 키를 선택적으로 추론하는 반면 TypeScript 페이지는 필수로 만듭니다. 그리고 Zod 4에서는 수십 단계 깊이로 중첩된 배열의 타입을 컴파일러가 포기하고 그 타입 자신의 줄에서 오류 TS2589를 보고합니다. 그 줄 위의 스키마는 여전히 실행되고 여전히 샘플을 받아들이며, 잃는 것은 추론된 타입뿐입니다.

두 비교 모두 선택적 키를 TypeScript가 기본으로 읽는 방식대로 읽습니다. 프로젝트가 켜지 않는 한 꺼져 있는 "exactOptionalPropertyTypes" 아래에서는 선택적 키의 "z.infer"가 어느 버전의 Zod에서든 명시적인 undefined도 허용하지만, TypeScript 페이지의 타입은 허용하지 않습니다.

Zod 4를 위해 쓰였고, Zod 3에서도 여전히 유효하다

출력은 두 메이저 버전에 모두 있는 것만 씁니다 — "z.object", "z.array", 두 개 이상의 멤버에 걸친 "z.union", "z.string()", "z.number()", "z.boolean()", "z.null()", "z.unknown()", "z.record(z.string(), z.unknown())", ".optional()", ".nullable()", 그리고 "z.infer" — 그것도 zod.dev의 예제가 맨 앞에 두는 import 줄 아래에서입니다. 그 안에는 Zod 4에서 새로 생긴 것도, 거기서 사용 중단 예정인 것도 없으므로, 아직 Zod 3에서 옮겨 가지 않은 프로젝트도 그대로 붙여넣을 수 있습니다.

다만 같은 텍스트가 두 버전에서 똑같이 동작하지는 않으며, 각 차이는 이 가이드에서 그것이 중요한 곳마다 말해 두었습니다. Zod 4의 "z.number()"는 무한대 숫자를 거부하는데 Zod 3의 것은 받아들이며, 이것이 Infinity 알림입니다. 값이 "z.unknown()"인 키는 Zod 3의 추론된 타입에서는 선택적이고 Zod 4의 타입에서는 필수이며 — Zod 4.4부터는 데이터를 파싱할 때도 필수입니다 — 이 출력은 그런 키를 깊이 한도를 넘어선 곳에서만 적습니다. Zod 3은 "__proto__"라고 적힌 키를 검사하고 Zod 4는 검사하지 않습니다. 그리고 수십 단계 깊이로 중첩된 배열에 대해 컴파일러는 Zod 4의 타입은 포기하지만 Zod 3의 타입은 알아냅니다.

이것은 메서드를 쓰는 일반 Zod를 위해 적은 것으로, "z.string().nullable().optional()"과 같은 모양입니다. Zod Mini는 같은 스키마를 대신 함수로 "z.optional(z.nullable(z.string()))"처럼 적으므로, Zod Mini는 이 출력을 있는 그대로 실행할 수 없습니다.

왜 루트가 마지막에 오는가

TypeScript 페이지는 루트를 먼저, 루트가 쓰는 객체를 그 뒤에 출력합니다. 타입은 그것을 선언하는 줄보다 앞에서 써도 되기 때문입니다. 스키마는 그럴 수 없습니다. 스키마는 값이고, 자기 선언보다 위에서 읽힌 "const"는 모듈이 아직 불러와지는 동안 ReferenceError를 던집니다. 그래서 여기서는 모든 스키마가 자신이 쓰는 각 스키마 뒤에 오고, 루트가 마지막에 옵니다 — 예제에서는 Customer와 LineItem이 먼저 오고 그 둘을 담는 Root가 그 뒤에 옵니다 — 이 때문에 TypeScript 페이지를 여는 루트가 이 페이지에서는 끝을 맺습니다.

그런 순서는 언제나 존재합니다. 추론은 트리이고 추출되는 각 객체는 정확히 한 곳에서만 쓰이므로, 자기 자신이나 자기보다 뒤에 출력되는 스키마를 참조해야 하는 스키마는 없으며, 출력에는 "z.lazy"가 필요한 일이 없습니다.

자주 묻는 질문

왜 ID가 "z.number().int()"가 아니라 "z.number()"인가요?
샘플은 지금까지의 모든 ID가 정수였다는 것은 보여 줄 수 있어도, 다음 ID도 그러리라는 것은 보여 줄 수 없기 때문입니다. 대신 페이지가 그렇다고 말해 줍니다. 정수 알림은 모든 숫자가 정수였던 위치를 하나하나 나열합니다. 값이 계속 정수여야 한다고 알고 있는 곳이라면 ".int()"를 붙이는 것은 한 단어짜리 수정입니다. 숫자가 안전한 범위 밖의 정수인 위치에서는 이 알림이 빠지는데, Zod 4의 ".int()"가 그 샘플 자체를 거부할 것이기 때문입니다.
왜 이메일 주소가 그냥 "z.string()"으로 나오나요?
샘플에서 이메일처럼 보이는 문자열은 다음 문자열에 대해 아무것도 말해 주지 않고, 올바른 형식 검사는 Zod 자신의 패턴을 따라야 하는데 그 패턴이 버전마다 바뀌기 때문입니다 — Zod 4의 "z.uuid()"는 Zod 3의 ".uuid()"가 통과시키던 문자열을 이미 거부합니다. 날짜, URL, UUID가 문자열로 남는 것도 같은 이유이며, 몇 가지 값만 담아 온 필드가 enum이나 리터럴이 되는 일도 없습니다. 규칙을 안다면 직접 적어 넣으세요. 스키마는 여러분이 소유한 평범한 코드입니다.
왜 제 샘플 자체가 Zod 4에서 실패하나요?
샘플에 JavaScript의 숫자가 담을 수 있는 범위를 넘는 숫자 — 이를테면 1e999 — 가 들어 있어서 JSON을 파싱할 때 Infinity 또는 -Infinity가 되었고, Zod 4의 "z.number()"는 무한대 숫자를 거부하는데 Zod 3의 것은 받아들이기 때문입니다. 관련된 위치는 Infinity 알림이 모두 짚어 줍니다. 그 경우를 빼면 스키마는 자신이 나온 샘플을 언제나 받아들입니다. 샘플의 모든 값이 스키마에 들어갔기 때문이며, 저장소는 샘플 모음으로 두 버전 모두에서 그것을 확인합니다.
보낸 필드가 왜 파싱 결과에서 사라졌나요?
스키마가 그것을 나열하지 않기 때문입니다. "z.object"는 어느 버전에서든 여분의 키가 있는 객체를 받아들이고 그 키들을 뺀 채로 돌려주며, 샘플에 없던 키는 스키마가 한 번도 배운 적 없는 키입니다. 그 키를 스키마에 추가하거나, 알 수 없는 키가 손대지 않은 채로 통과해야 한다면 그 객체 하나만 느슨하게 만드세요 — Zod 4에서는 "z.looseObject", Zod 3에서는 ".passthrough()"입니다.
스키마 하나를 다른 스키마 아래로 옮겼더니 ReferenceError가 났습니다. 왜 그런가요?
스키마는 값이고, JavaScript는 값을 그것을 정의하는 줄보다 위에서 읽지 못하게 하기 때문입니다. 페이지가 각 스키마를 그 스키마가 쓰는 모든 스키마 뒤에, 루트를 마지막에 두어 출력하는 것도 바로 그 때문입니다. 스키마를 그것을 참조하는 모든 것보다 위에 두면 오류가 사라집니다.
왜 스키마 이름이 CustomerSchema가 아니라 Customer인가요?
그것이 zod.dev 자신의 관례입니다. 스키마와 그 스키마가 추론하는 타입은 하나의 이름을 함께 쓰며, TypeScript에서는 값과 타입이 서로 다른 네임스페이스에 살기 때문에 그렇게 할 수 있습니다. 이름 자체는 JSON → TypeScript 변환 페이지가 같은 객체에 붙이는 이름이므로, 스키마에도 그 타입에도 똑같이 두 페이지에서 같은 단어가 됩니다.
출력은 어느 버전의 Zod를 위해 작성되었나요?
Zod 4입니다. Zod 3에 없는 것은 아무것도 쓰지 않으므로 어느 쪽에서든 바꾸지 않고 실행됩니다. 둘은 몇 군데에서 다르며 — 무한대 숫자, 깊이 한도를 넘은 키, "__proto__"라고 적힌 키, 그리고 아주 깊이 중첩된 배열의 타입 — 각각을 위에서 설명했습니다. 이것은 메서드를 체인으로 잇는 일반 Zod이며, Zod Mini는 같은 스키마를 함수로 적으므로 이것을 있는 그대로 실행할 수 없습니다.
자격 증명까지 포함해 실제 데이터를 붙여넣어도 되나요?
네. 스키마는 여러분의 브라우저 안에서 만들어집니다. 붙여넣은 것은 여러분 자신의 기기에서 읽히며, 서버로 전송되지도, 저장되거나 기록되지도 않습니다. 또한 스키마는 여러분의 값을 하나도 담지 않고 키와 각 키 아래에 있는 값의 종류만 담으므로, 샘플 속 토큰은 "z.string()"으로 나올 뿐 그 이상은 아닙니다.

관련 도구

  • JSON → TypeScript 변환

    이 페이지의 스키마는 코드의 일부로 실행되며 데이터가 도착할 때마다 그것을 검사합니다. 저 페이지는 같은 JSON에 대해 같은 답을 같은 이름의 평범한 TypeScript 타입으로 출력하며, 타입은 코드를 컴파일할 때 검사되고 실행 시점에는 아무것도 더하지 않습니다.

  • JSON → Go 변환

    이 페이지는 샘플의 모든 숫자가 정수였던 곳에서도 정수를 요구하지 않으며, 그 검사를 더할지는 사용하는 쪽에 맡깁니다. 저 페이지는 같은 형태를 같은 타입 이름으로 정하지만, Go에는 그저 JSON 숫자인 타입이 없으므로 모든 숫자가 정수로 쓰여 있는 곳에서는 필드를 정수 타입으로 만들고, 소수점을 넣어 쓴 값은 거기로 디코딩되지 않습니다.

  • JSONPath 테스터

    RFC 9535 JSONPath 쿼리를 JSON에 대해 테스트.

  • Markdown 표 생성기

    CSV, TSV, JSON에서 Markdown 표를 만들고 정렬합니다.