Skip to content

Go 언어 encode 인코딩 디코딩

현재 인터넷 시대에서 가장 일반적으로 사용되는 언어 독립적인 데이터 포맷은 xml, Yaml, json, protobuf가 있으며, Go 도 이러한 데이터 포맷 관련 조작을 지원합니다. 아래는 비교 표입니다.

이름XMLYAMLJSONProtocol Buffers
데이터 구조복잡비교적 단순단순비교적 복잡
저장 방식텍스트텍스트텍스트바이너리
저장 크기중간중간작음
해석 효율느림중간중간빠름
언어 지원매우 많음많음매우 많음비교적 많음
개발 난이도번거로움비교적 단순단순단순
학습 비용낮음낮음낮음낮음
적용 범위데이터 교환설정 파일데이터 교환데이터 교환

TIP

Go 에서 구조체의 직렬화와 역직렬화를 하려면 필드가 반드시 외부에 노출되어야 합니다. 즉, 첫 글자가 대문자여야 합니다.

또한 TOML 도 점차 인기를 얻고 있으며, 문법적으로는 .ini의 개선 버전과 같습니다. 관심이 있다면 TOML: Tom's (의미상) 명확한, (설정) 최소화된 언어 를 참고하시기 바랍니다.

XML

xmleXtensible Markup Language의 약자로, 데이터를 저장하는 한 가지 형식이며 20 세기 60 년대에 기원하여 위의 여러 데이터 포맷 중 가장 오래된 것입니다. 용도는 매우 광범위하며 네트워크 전송, 데이터 교환, 설정 파일, 데이터 저장 등에 사용할 수 있습니다. 하지만 시대가 변함에 따라 점차 새로운 마크업 언어로 대체되고 있습니다.

먼저 구조체를 정의합니다.

go
type Person struct {
   UserId   string `xml:"id"`
   Username string `xml:"name"`
   Age      int    `xml:"age"`
   Address  string `xml:"address"`
}
go
func Marshal(v any) ([]byte, error) //xml 직렬화

func MarshalIndent(v any, prefix, indent string) ([]byte, error) //포맷팅

func Unmarshal(data []byte, v any) error //역직렬화

직렬화

go
func main() {
   person := Person{
      UserId:   "120",
      Username: "jack",
      Age:      18,
      Address:  "usa",
   }

   bytes, err := xml.MarshalIndent(person, "", "\t")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(bytes))
}

출력

xml
<Person>
        <id>120</id>
        <name>jack</name>
        <age>18</age>
        <address>usa</address>
</Person>

역직렬화

go
func main() {
   var person = Person{
      UserId:   "",
      Username: "",
      Age:      0,
      Address:  "",
   }

   xmlStr := "<Person>                      \n        <id>120</id>          \n        <name>jack</name>     \n        <age>18</age>         \n        <address>usa</address>\n</Person>  "

   err := xml.Unmarshal([]byte(xmlStr), &person)
   if err != nil {
      fmt.Println(err)
      return
   }
}

출력

go
{UserId:120 Username:jack Age:18 Address:usa}

하지만 전통적인 xml 해석 방식은 자주 구조체를 새로 만들어야 하므로 매우 번거롭습니다. 현재는 단순한 xml 구조만 해석하므로 복잡한 구조를 사용하면 매우 골치 아플 수 있습니다. 따라서 대부분 서드파티 오픈소스 라이브러리인 etree 를 사용하여 xml 을 해석합니다. 관심이 있다면 직접 알아보세요: Go 에서 사용하기 좋은 xml 파일 해석 플러그인 etree - 掘金 (juejin.cn).

YML

YAML 의 문법은 다른 고급 언어와 유사하며, 리스트, 해시 테이블, 스칼라 등 데이터 형태를 간단하게 표현할 수 있습니다. 공백 문자 들여쓰기와 외관에 크게 의존하는 특징을 사용하여 데이터 구조, 다양한 설정 파일을 표현하거나 편집하는 데 특히 적합하며, YML 은 많은 프로젝트에서 설정 파일 형태로 존재합니다. 내용 구조가 더욱 간결하여 한눈에 볼 수 있습니다. Go 공식에서는 YML 에 대한 지원을 제공하지 않으므로 서드파티 패키지를 사용해야 합니다.

powershell
go get github.com/go-yaml/yaml

주요 메서드

go
func Marshal(in interface{}) (out []byte, err error) //직렬화

func Unmarshal(in []byte, out interface{}) (err error) //역직렬화

먼저 구조체를 준비합니다.

go
type Config struct {
   Database string `yaml:"database"`
   Url      string `yaml:"url"`
   Port     int    `yaml:"port"`
   Username string `yaml:"username"`
   Password string `yaml:"password"`
}

설정 파일

yaml
database: mysql
url: 127.0.0.1
port: 3306
username: root
password: 123456

직렬화

go
func main() {
   config := Config{
      Database: "oracle",
      Url:      "localhost",
      Port:     3326,
      Username: "root",
      Password: "123456",
   }

   out, err := yaml.Marshal(config)
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(out))
}

출력

yaml
database: oracle
url: localhost
port: 3326
username: root
password: "123456"

yml 자체에 엄격한 들여쓰기 문법이 있으므로 직렬화 포맷팅 문제는 없습니다.

역직렬화

go
func main() {
   bytes, err := os.ReadFile("./src/config.yml")
   if err != nil {
      fmt.Println(err)
      return
   }
   var config Config
   err = yaml.Unmarshal(bytes, &config)
   if err != nil {
      fmt.Println(err)
      return
   }

   fmt.Println(config)
}

출력

{mysql 127.0.0.1 3306 root 123456}

JSON

jsonRestful 스타일의 인터페이스 통신에서 자주 사용되며, xml에 비해 더 가볍고 학습 비용이 낮아 web 분야에서 주류 데이터 교환 포맷이 되었습니다.

Go 에서 encoding/json 패키지는 json 의 직렬화와 역직렬화를 위한 함수를 제공합니다. 주로 사용되는 함수는 다음과 같습니다.

go
func Marshal(v any) ([]byte, error) //Go 객체를 json 문자열로 직렬화

func Unmarshal(data []byte, v any) error //json 문자열을 Go 객체로 역직렬화

먼저 구조체를 정의합니다.

go
type Person struct {
   UserId   string
   Username string
   Age      int
   Address  string
}

직렬화

go
func main() {
  person := Person{
    UserId:   "120",
    Username: "jack",
    Age:      18,
    Address:  "usa",
  }

  bytes, err := json.Marshal(person)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(string(bytes))
}

결과

json
{ "UserId": "120", "Username": "jack", "Age": 18, "Address": "usa" }

필드 이름 변경

구조체 태그를 사용하여 이름 변경 효과를 얻을 수 있습니다.

go
type Person struct {
   UserId   string `json:"id"`
   Username string `json:"name"`
   Age      int    `json:"age"`
   Address  string `json:"address"`
}

이때 출력

json
{ "id": "1202", "name": "jack", "age": 19, "address": "USA" }

들여쓰기

직렬화 시 기본적으로 들여쓰기가 없으며, 이는 전송 과정에서의 공간 손실을 줄이기 위함이지만 사람이 관찰하기에는 불리합니다. 어떤 경우에는 사람이 관찰할 수 있는 형태로 직렬화해야 합니다. 이를 위해 함수만 변경하면 됩니다.

go
func MarshalIndent(v any, prefix, indent string) ([]byte, error)
go
func main() {
   person := Person{
      UserId:   "1202",
      Username: "jack",
      Age:      19,
      Address:  "USA",
   }
   bytes, err := json.MarshalIndent(person, "", "\t")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(string(bytes))
}

출력은 다음과 같습니다.

json
{
  "id": "1202",
  "name": "jack",
  "age": 19,
  "address": "USA"
}

역직렬화

역직렬화 시 구조체에 json 태그가 있으면 필드명은 우선적으로 json 태그를 기준으로 하며, 그렇지 않으면 구조체 속성명을 기준으로 합니다.

go
func main() {
  person := Person{}

  jsonStr := "{\"id\":\"120\",\"name\":\"jack\",\"age\":18,\"address\":\"usa\"}\n"

  err := json.Unmarshal([]byte(jsonStr), &person)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Printf("%+v", person)
}

출력

go
{UserId:120 Username:jack Age:18 Address:usa}

Protocol Buffers

protocol 은 구글이 2008 년에 오픈소스로 공개한 언어 중립적, 프로토콜 중립적, 확장 가능한 구조화된 데이터 직렬화 메커니즘입니다. 위의 세 가지에 비해 더 가볍고 언패키징 및 패키징 시 더 빠르며, 주로 RPC 분야 통신 관련에 사용됩니다. Protobuf에 대한 설명은 Protobuf 를 참고하시기 바랍니다.

의존성 설치

go get github.com/golang/protobuf/proto

person.proto 파일

protobuf
syntax = "proto3";

option go_package = "./;person";

package proto;

enum Gender{
  MAIL = 0;
  FE_MAIL = 1;
}

message person {
  string name = 1;
  int32 age = 2;
  Gender gender = 3;
}

파일 생성 후

go
package main

import (
   p "GoProject/src/proto"
   "fmt"
   "github.com/golang/protobuf/proto"
)

func main() {
  person := p.Person{
    Name:   "wyh",
    Age:    12,
    Gender: p.Gender_FE_MAIL,
  }

  data, err := proto.Marshal(&person)//직렬화
  if err != nil {
    fmt.Println(err)
    return
  }
  temp := &p.Person{}
  fmt.Println("proto buffer len: ", len(data), "bytes:", data)
  err = proto.Unmarshal(data, temp)//역직렬화
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(temp)
}

출력

proto buffer len:  9 bytes: [10 3 119 121 104 16 12 24 1]
name:"wyh"  age:12  gender:FE_MAIL

하지만 일반적으로 수동으로 직렬화하지 않으며, protoc 컴파일러는 정의된 proto 파일에 따라 해당 언어의 소스 코드를 생성할 수 있습니다.

Golang by www.golangdev.cn edit