JSON to Go 구조체 생성기
JSON에서 Go 구조체 생성
JSON 입력
Go 출력
JSON to Go 변환이란?
JSON to Go 변환은 원시 JSON 데이터를 Go 표준 라이브러리의 encoding/json 패키지에서 사용할 수 있는 Go 타입 정의로 변환합니다. Go는 정적 타입 언어이므로 모든 JSON 필드에는 대응하는 구조체 필드, 올바른 타입, 그리고 직렬화 시 어떤 JSON 키와 매핑할지 알려주는 구조체 태그가 필요합니다. 크거나 깊게 중첩된 API 응답에 대해 이런 정의를 수동으로 작성하는 것은 느리고 오류가 발생하기 쉽습니다.
Go 1.0부터 정의된 encoding/json 패키지는 리플렉션을 사용해 JSON 키를 내보내진(exported) 구조체 필드와 매핑합니다. 기본적으로 대소문자를 구분하지 않지만, 명시적인 json 구조체 태그를 사용하는 것이 표준 관행입니다. 모호성을 없애고 JSON이 camelCase나 snake_case를 유지하면서 Go의 PascalCase 관례를 사용할 수 있기 때문입니다. 변환기는 이 과정을 자동화합니다: JSON을 읽고, 값에서 Go 타입을 추론하며, 올바른 태그가 포함된 구조체 정의를 출력합니다.
Go의 타입 시스템은 JSON과 깔끔하게 대응됩니다. 문자열은 string, 불리언은 bool, 정수는 int, 부동소수점은 float64가 됩니다. 중첩된 JSON 객체는 별도의 명명된 구조체가 되고, 배열은 슬라이스가 됩니다. 한 가지 차이점은 null입니다: Go에는 범용 nullable 타입이 없으므로, null 값은 보통 포인터 타입(*string, *int) 또는 interface{}'가 됩니다. 변환기는 이 모든 것을 밀리초 안에 처리합니다.
JSON to Go 변환기를 사용하는 이유
JSON에서 Go 구조체를 수동으로 정의하면 중괄호를 세고, 타입을 추측하고, API가 변경될 때마다 태그를 다시 작성해야 합니다. 변환기는 이런 번거로움을 없애줍니다.
JSON to Go 활용 사례
JSON to Go 타입 매핑
encoding/json 패키지는 JSON 값을 Go 타입에 매핑할 때 특정 규칙을 따릅니다. 아래 표는 기본 매핑과 일반적인 대안을 보여줍니다. '기본값' 열은 대부분의 생성기가 출력하는 타입입니다. '대안' 열은 대용량 ID에 int64를 사용하거나 nullable 필드에 포인터 타입을 사용하는 것처럼 요구사항에 따라 선택할 수 있는 타입을 보여줍니다.
| JSON 타입 | 예시 값 | 기본값 | 대안 |
|---|---|---|---|
| string | "hello" | string | string |
| number (integer) | 42 | int | int64 |
| number (float) | 3.14 | float64 | float64 |
| boolean | true | bool | bool |
| null | null | interface{} | *string / *int |
| object | {"k": "v"} | struct | struct |
| array of strings | ["a", "b"] | []string | []string |
| array of objects | [{"id": 1}] | []Item | []Item |
| mixed array | [1, "a"] | []interface{} | []interface{} |
Go JSON 구조체 태그 참조
구조체 태그는 encoding/json이 필드를 직렬화·역직렬화하는 방식을 제어합니다. json 태그가 가장 일반적이지만, 같은 필드에 다른 태그(db, yaml, xml)와 조합할 수 있습니다. 태그 문법은 필드 타입 뒤에 백틱으로 구분된 문자열입니다. 다음은 encoding/json이 지원하는 json 태그 옵션들입니다.
| 태그 | 동작 | 사용 시점 |
|---|---|---|
| json:"name" | Maps struct field to JSON key "name" | Always generated |
| json:"name,omitempty" | Omits field from output if zero value | Optional fields |
| json:"-" | Field is never serialized or deserialized | Internal fields |
| json:"name,string" | Encodes int/bool as JSON string | String-encoded numbers |
json.Unmarshal vs json.NewDecoder
Go는 JSON을 디코딩하는 두 가지 방법을 제공합니다: 바이트 슬라이스용 json.Unmarshal과 io.Reader 스트림용 json.NewDecoder. 둘 다 동일한 구조체 태그 규칙을 사용하지만, 적합한 상황이 다릅니다.
코드 예제
다음 예제는 JSON에서 생성된 Go 구조체를 실제 프로그램에서 사용하는 방법과 다른 언어에서 생성하는 방법을 보여줍니다. Go 예제는 표준 라이브러리의 encoding/json을 사용합니다.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
Tags []string `json:"tags"`
}
func main() {
data := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","is_active":true,"tags":["admin","editor"]}`)
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Println(user.Name) // → Alice
fmt.Println(user.Tags) // → [admin editor]
}// JSON response from API — this is the shape you'd convert to Go:
const res = await fetch("https://api.example.com/users/1");
const user = await res.json();
// user → { "id": 1, "name": "Alice", "email": "alice@example.com" }
// Equivalent Go struct:
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }import json
def json_to_go(data: dict, name: str = "Root") -> str:
lines = [f"type {name} struct {{"]
type_map = {str: "string", int: "int", float: "float64", bool: "bool"}
for key, value in data.items():
go_type = type_map.get(type(value), "interface{}")
field = key.title().replace("_", "") # snake_case → PascalCase
lines.append(f'\t{field} {go_type} `json:"{key}"`')
lines.append("}")
return "\n".join(lines)
data = json.loads('{"user_name": "Alice", "age": 30, "verified": true}')
print(json_to_go(data))
# type Root struct {
# UserName string `json:"user_name"`
# Age int `json:"age"`
# Verified bool `json:"verified"`
# }package main
import (
"encoding/json"
"fmt"
"strings"
)
type Event struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
func main() {
stream := strings.NewReader(`{"type":"click","payload":"btn-1"}
{"type":"scroll","payload":"page-2"}`)
dec := json.NewDecoder(stream)
for dec.More() {
var ev Event
if err := dec.Decode(&ev); err != nil {
break
}
fmt.Printf("%s: %s\n", ev.Type, ev.Payload)
}
// → click: btn-1
// → scroll: page-2
}