Files
grpccanary/docs/Working with JSON/README-kr.md
T
2025-11-17 23:25:36 +09:00

5.3 KiB

Go 언어와 JSON 다루기

Go 언어에서 JSON 데이터를 다루는 방법을 배워봅시다.

Go 표준 라이브러리에는 JSON 데이터를 처리하기 위한 encoding/json 패키지가 포함되어 있습니다. Go는 구조체 태그(Struct Tag)를 사용하여 Go 구조체와 JSON 필드를 손쉽게 매핑할 수 있는 기능을 제공합니다. 이 태그는 Go 구조체를 JSON으로 변환하거나, JSON을 Go 구조체로 변환하는 과정을 제어합니다. 이 과정을 각각 Marshaling과 Unmarshaling이라고 부릅니다.

Marshal()Unmarshal() 이해하기

MarshalingUnmarshaling은 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 과정을 보여줍니다.

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 구조체처럼 사용하면 됩니다.

func main() {
    // Marshaling할 구조체 인스턴스 생성
	useall := UseAll{Name: "Mike", Surname: "Tsoukalos", Year: 2021}

	// Go 구조체를 JSON 바이트 슬라이스로 Marshaling합니다.
	t, err := json.Marshal(&useall)
}

json.Marshal() 함수는 Go 데이터(주로 구조체 포인터)를 인자로 받아, JSON으로 인코딩된 []byteerror를 반환합니다.

	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 데이터는 보통 문자열 형태로 다루어집니다.

	// json.Unmarshal 함수는 바이트 슬라이스를 인자로 받으므로, 문자열을 변환합니다.
	jsonRecord := []byte(str)

json.Unmarshal() 함수는 바이트 슬라이스 ([]byte)를 인자로 받기 때문에, 먼저 JSON 문자열을 []byte 타입으로 변환해야 합니다.

	// 변환된 JSON 데이터를 담을 구조체 변수를 선언합니다.
	var temp UseAll
	// JSON 바이트 슬라이스를 Go 구조체로 Unmarshaling합니다.
	err = json.Unmarshal(jsonRecord, &temp)

json.Unmarshal() 함수는 JSON 데이터가 담긴 바이트 슬라이스와, 데이터를 채워 넣을 Go 구조체 변수의 포인터를 인자로 받습니다. 포인터를 사용하는 이유는 함수가 temp 변수의 값을 직접 수정해야 하기 때문입니다. 함수가 종료된 후에도 변경된 값이 유지되려면 이처럼 변수의 메모리 주소를 전달해야 합니다.

	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("Data type: %T with value %v\n", temp, temp)
	}

encodeDecode.go를 실행하면 다음과 같은 결과가 출력됩니다.

# go run encodeDecode.go
Value {"username":"Mike","surname":"Tsoukalos","created":2021}
Data type: main.UseAll with value {M. Ts 2020}

전체 예제 코드

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)
	}
}