# Go 언어와 JSON 다루기 Go 언어에서 JSON 데이터를 다루는 방법을 배워봅시다. Go 표준 라이브러리에는 JSON 데이터를 처리하기 위한 `encoding/json` 패키지가 포함되어 있습니다. Go는 구조체 태그(Struct Tag)를 사용하여 Go 구조체와 JSON 필드를 손쉽게 매핑할 수 있는 기능을 제공합니다. 이 태그는 Go 구조체를 JSON으로 변환하거나, JSON을 Go 구조체로 변환하는 과정을 제어합니다. 이 과정을 각각 Marshaling과 Unmarshaling이라고 부릅니다. ## `Marshal()`과 `Unmarshal()` 이해하기 **Marshaling**과 **Unmarshaling**은 Go 구조체로 JSON 데이터를 다룰 때 핵심적인 과정입니다. - **Marshaling**: Go 구조체(메모리 상의 데이터)를 JSON 문자열(텍스트 데이터)로 변환하는 과정입니다. 주로 API 응답으로 JSON을 보내거나, 데이터를 파일로 저장할 때 사용됩니다. - **Unmarshaling**: JSON 문자열을 Go 구조체로 변환하는 과정입니다. 주로 외부 API로부터 받은 JSON 데이터를 다루거나 파일에서 데이터를 읽어올 때 사용됩니다. > **가장 흔히 겪는 문제**: JSON과 Go 구조체 간 변환 시 가장 흔한 버그는 구조체의 필드명을 소문자로 시작하여 발생하는 문제입니다. `encoding/json` 패키지가 구조체의 필드에 접근하려면, 해당 필드는 **반드시 대문자로 시작해야 합니다 (Exported field)**. Marshaling 또는 Unmarshaling이 제대로 동작하지 않는다면, 가장 먼저 구조체 필드명이 대문자로 시작하는지 확인해 보세요. # 코딩 예제 아래 `encodeDecode.go` 코드는 간단한 예제를 통해 JSON 레코드의 Marshaling과 Unmarshaling 과정을 보여줍니다. ```go package main import ( "encoding/json" "fmt" ) // UseAll 구조체는 JSON 데이터와 매핑됩니다. type UseAll struct { Name string `json:"username"` Surname string `json:"surname"` Year int `json:"created"` } ``` 구조체 필드 옆의 `` `json:"..."` `` 부분을 **구조체 태그**라고 부릅니다. 이 태그는 각 필드가 JSON 데이터에서 어떤 키(key)와 매핑되는지를 명시합니다. - `Name` 필드는 JSON에서 `username` 키와 매핑됩니다. - `Surname` 필드는 `surname` 키와 매핑됩니다. - `Year` 필드는 `created` 키와 매핑됩니다. 이 태그 정보는 Marshaling과 Unmarshaling 과정에서 사용되며, 이 외에는 `UseAll`을 일반적인 Go 구조체처럼 사용하면 됩니다. ```go func main() { // Marshaling할 구조체 인스턴스 생성 useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021} // Go 구조체를 JSON 바이트 슬라이스로 Marshaling합니다. t, err := json.Marshal(&useall) } ``` `json.Marshal()` 함수는 Go 데이터(주로 구조체 포인터)를 인자로 받아, JSON으로 인코딩된 `[]byte`와 `error`를 반환합니다. ```go if err != nil { fmt.Println(err) } else { // t는 []byte 타입이므로, 출력을 위해 문자열로 변환합니다. fmt.Printf("Value %s\n", t) } // Unmarshaling할 JSON 문자열 데이터 str := `{"username": "M.", "surname": "Ts", "created":2020}` ``` JSON 데이터는 보통 문자열 형태로 다루어집니다. ```go // json.Unmarshal 함수는 바이트 슬라이스를 인자로 받으므로, 문자열을 변환합니다. jsonRecord := []byte(str) ``` `json.Unmarshal()` 함수는 바이트 슬라이스 (`[]byte`)를 인자로 받기 때문에, 먼저 JSON 문자열을 `[]byte` 타입으로 변환해야 합니다. ```go // 변환된 JSON 데이터를 담을 구조체 변수를 선언합니다. var temp UseAll // JSON 바이트 슬라이스를 Go 구조체로 Unmarshaling합니다. err = json.Unmarshal(jsonRecord, &temp) ``` `json.Unmarshal()` 함수는 JSON 데이터가 담긴 바이트 슬라이스와, 데이터를 채워 넣을 Go 구조체 변수의 **포인터**를 인자로 받습니다. 포인터를 사용하는 이유는 함수가 `temp` 변수의 값을 직접 수정해야 하기 때문입니다. 함수가 종료된 후에도 변경된 값이 유지되려면 이처럼 변수의 메모리 주소를 전달해야 합니다. ```go if err != nil { fmt.Println(err) } else { fmt.Printf("Data type: %T with value %v\n", temp, temp) } ``` `encodeDecode.go`를 실행하면 다음과 같은 결과가 출력됩니다. ```bash # go run encodeDecode.go Value {"username":"Mike","surname":"Tsoukalos","created":2021} Data type: main.UseAll with value {M. Ts 2020} ``` --- ### 전체 예제 코드 ```go package main import ( "encoding/json" "fmt" ) type UseAll struct { Name string `json:"username"` Surname string `json:"surname"` Year int `json:"created"` } func main() { useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021} // Marshaling: Go 구조체 -> JSON t, err := json.Marshal(&useall) if err != nil { fmt.Println(err) } else { fmt.Printf("Value %s\n", t) } // Unmarshaling할 JSON 문자열 str := `{"username": "M.", "surname": "Ts", "created":2020}` jsonRecord := []byte(str) // 결과를 저장할 구조체 변수 var temp UseAll // Unmarshaling: JSON -> Go 구조체 err = json.Unmarshal(jsonRecord, &temp) if err != nil { fmt.Println(err) } else { fmt.Printf("Data type: %T with value %v\n", temp, temp) } } ```