์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฏ Go์–ธ์–ด๋กœ JSON๊ณผ XML ์ •๋ณตํ•˜๊ธฐ!

๐ŸŽฏ Go์–ธ์–ด๋กœ JSON๊ณผ XML ์ •๋ณตํ•˜๊ธฐ!

๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”์˜ ๋ชจ๋“  ๊ฒƒ, encoding ํŒจํ‚ค์ง€์™€ ํ•จ๊ป˜ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ๋ฐฐ์›Œ๋ณด์ž ๐Ÿš€

๐ŸŒŸ ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”, ์™œ ํ•„์š”ํ•œ ๊ฑธ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ Go์–ธ์–ด์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ์ฃผ์ œ๋ฅผ ๋‹ค๋ค„๋ณผ ๊ฑฐ์•ผ. ๋ฐ”๋กœ JSON๊ณผ XML ์ฒ˜๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ์•ผ. ๐Ÿ˜Š

๋จผ์ € ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”(Serialization)๊ฐ€ ๋ญ”์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ณผ๊นŒ? ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ํ”„๋กœ๊ทธ๋žจ ์•ˆ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ์ด๋‚˜ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด์•ผ. ๋งˆ์น˜ ํƒ๋ฐฐ๋ฅผ ๋ณด๋‚ผ ๋•Œ ๋ฌผ๊ฑด์„ ๋ฐ•์Šค์— ํฌ์žฅํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜์ง€!

๋ฐ˜๋Œ€๋กœ ์—ญ์ง๋ ฌํ™”(Deserialization)๋Š” ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ํ’€์–ด๋‚ด๋Š” ๊ฑฐ์•ผ. ํƒ๋ฐฐ ๋ฐ•์Šค๋ฅผ ์—ด์–ด์„œ ๋ฌผ๊ฑด์„ ๊บผ๋‚ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์•ผ. ๐Ÿ“ฆ

๐Ÿ’ก ์‹ค์ƒํ™œ ์˜ˆ์‹œ ์›น API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์„œ๋ฒ„์—์„œ JSON ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด์ฃผ์ง€? ์ด๊ฑธ ์šฐ๋ฆฌ Go ํ”„๋กœ๊ทธ๋žจ์˜ ๊ตฌ์กฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋ฐ”๋กœ ์—ญ์ง๋ ฌํ™”์•ผ. ๋ฐ˜๋Œ€๋กœ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†กํ•  ๋•Œ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ง๋ ฌํ™” ๊ณผ์ •์„ ๊ฑฐ์น˜๊ฒŒ ๋ผ!
Go ๊ตฌ์กฐ์ฒด type User struct { Name string Age int } ์ง๋ ฌํ™” JSON ๋ฐ์ดํ„ฐ { "name": "ํ™๊ธธ๋™", "age": 25 } ์—ญ์ง๋ ฌํ™” ๋„คํŠธ์›Œํฌ ์ „์†ก API ํ†ต์‹  ํŒŒ์ผ ์ €์žฅ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๐Ÿ”ง ๐Ÿ“„ ๐ŸŒ

๐Ÿ“š Go์˜ encoding ํŒจํ‚ค์ง€ ํŒจ๋ฐ€๋ฆฌ

Go ์–ธ์–ด๋Š” ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”๋ฅผ ์œ„ํ•ด ์ •๋ง ํ›Œ๋ฅญํ•œ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด. encoding ํŒจํ‚ค์ง€ ์•„๋ž˜์— ์—ฌ๋Ÿฌ ํ•˜์œ„ ํŒจํ‚ค์ง€๋“ค์ด ์žˆ๋Š”๋ฐ, ์˜ค๋Š˜ ์šฐ๋ฆฌ๊ฐ€ ์ง‘์ค‘์ ์œผ๋กœ ๋ณผ ๊ฑด ๋‹ค์Œ ๋‘ ๊ฐ€์ง€์•ผ:

โ€ข encoding/json - JSON ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข encoding/xml - XML ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

์ด ์™ธ์—๋„ encoding/csv, encoding/base64, encoding/hex ๋“ฑ ๋‹ค์–‘ํ•œ ํŒจํ‚ค์ง€๋“ค์ด ์žˆ์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฑด ๋‹จ์—ฐ JSON๊ณผ XML์ด์•ผ! ๐ŸŽฏ

๐ŸŽจ JSON ์ฒ˜๋ฆฌ์˜ ๊ธฐ๋ณธ - Marshal๊ณผ Unmarshal

JSON ์ฒ˜๋ฆฌ์˜ ํ•ต์‹ฌ์€ ๋‘ ๊ฐ€์ง€ ํ•จ์ˆ˜์•ผ. Marshal๊ณผ Unmarshal! ์ด๋ฆ„์ด ์ข€ ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์‰ฝ๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ๋ผ:

โ€ข Marshal: Go ๋ฐ์ดํ„ฐ โ†’ JSON ๋ฌธ์ž์—ด (์ง๋ ฌํ™”)
โ€ข Unmarshal: JSON ๋ฌธ์ž์—ด โ†’ Go ๋ฐ์ดํ„ฐ (์—ญ์ง๋ ฌํ™”)

์ž, ์ด์ œ ์‹ค์ œ ์ฝ”๋“œ๋กœ ์‚ดํŽด๋ณผ๊นŒ? ๐Ÿ˜Š

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

// ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‹ด์„ ๊ตฌ์กฐ์ฒด
type User struct {
    Name    string `json:"name"`
    Age     int    `json:"age"`
    Email   string `json:"email"`
    IsAdmin bool   `json:"is_admin"`
}

func main() {
    // 1. Marshal ์˜ˆ์ œ - ๊ตฌ์กฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜
    user := User{
        Name:    "๊น€์ฒ ์ˆ˜",
        Age:     28,
        Email:   "chulsoo@example.com",
        IsAdmin: false,
    }
    
    // JSON์œผ๋กœ ๋ณ€ํ™˜ (์ง๋ ฌํ™”)
    jsonData, err := json.Marshal(user)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("JSON ๋ฐ์ดํ„ฐ:", string(jsonData))
    // ์ถœ๋ ฅ: {"name":"๊น€์ฒ ์ˆ˜","age":28,"email":"chulsoo@example.com","is_admin":false}
    
    // 2. Unmarshal ์˜ˆ์ œ - JSON์„ ๊ตฌ์กฐ์ฒด๋กœ ๋ณ€ํ™˜
    jsonString := `{"name":"์ด์˜ํฌ","age":25,"email":"younghee@example.com","is_admin":true}`
    
    var newUser User
    err = json.Unmarshal([]byte(jsonString), &newUser)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("์ด๋ฆ„: %s, ๋‚˜์ด: %d, ๊ด€๋ฆฌ์ž: %v\n", 
        newUser.Name, newUser.Age, newUser.IsAdmin)
    // ์ถœ๋ ฅ: ์ด๋ฆ„: ์ด์˜ํฌ, ๋‚˜์ด: 25, ๊ด€๋ฆฌ์ž: true
}
๐ŸŽ“ ๊ตฌ์กฐ์ฒด ํƒœ๊ทธ(Struct Tag)์˜ ๋น„๋ฐ€ ์œ„ ์ฝ”๋“œ์—์„œ `json:"name"` ๊ฐ™์€ ๋ถ€๋ถ„์„ ๋ดค์ง€? ์ด๊ฒŒ ๋ฐ”๋กœ ๊ตฌ์กฐ์ฒด ํƒœ๊ทธ์•ผ! ์ด๊ฑธ ํ†ตํ•ด Go ํ•„๋“œ๋ช…๊ณผ JSON ํ‚ค ์ด๋ฆ„์„ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด Go์—์„œ๋Š” IsAdmin์ด์ง€๋งŒ JSON์—์„œ๋Š” is_admin์œผ๋กœ ํ‘œํ˜„๋˜๋Š” ๊ฑฐ์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Go์˜ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜๊ณผ JSON์˜ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜์„ ๋ชจ๋‘ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ์–ด!

๐ŸŽฏ JSON ํƒœ๊ทธ ์˜ต์…˜ ์™„์ „ ์ •๋ณต

๊ตฌ์กฐ์ฒด ํƒœ๊ทธ์—๋Š” ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์ค„ ์ˆ˜ ์žˆ์–ด. ์ด๊ฑธ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ JSON ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด! ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž. ๐Ÿ’ช

type Product struct {
    // ๊ธฐ๋ณธ ๋งคํ•‘
    ID          int     `json:"id"`
    
    // omitempty: ๊ฐ’์ด ๋น„์–ด์žˆ์œผ๋ฉด JSON์—์„œ ์ œ์™ธ
    Name        string  `json:"name,omitempty"`
    
    // -: JSON ๋ณ€ํ™˜ ์‹œ ์™„์ „ํžˆ ๋ฌด์‹œ
    InternalCode string `json:"-"`
    
    // string: ์ˆซ์ž๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
    Price       int     `json:"price,string"`
    
    // ํƒœ๊ทธ ์—†์Œ: ํ•„๋“œ๋ช… ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
    Category    string
}

๊ฐ ์˜ต์…˜์„ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•ด๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•ด:

func main() {
    product := Product{
        ID:           1,
        Name:         "", // ๋น„์–ด์žˆ์Œ
        InternalCode: "SECRET-123",
        Price:        15000,
        Category:     "Electronics",
    }
    
    jsonData, _ := json.Marshal(product)
    fmt.Println(string(jsonData))
    // ์ถœ๋ ฅ: {"id":1,"price":"15000","Category":"Electronics"}
    // name์€ omitempty๋กœ ์ œ์™ธ๋จ
    // InternalCode๋Š” - ์˜ต์…˜์œผ๋กœ ์ œ์™ธ๋จ
    // price๋Š” ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋จ
}
โšก omitempty ์ฃผ์˜์‚ฌํ•ญ omitempty๋Š” ์ •๋ง ์œ ์šฉํ•˜์ง€๋งŒ ์ฃผ์˜ํ•  ์ ์ด ์žˆ์–ด! ์ˆซ์ž ํƒ€์ž…์˜ ๊ฒฝ์šฐ 0์ด "๋นˆ ๊ฐ’"์œผ๋กœ ๊ฐ„์ฃผ๋˜๊ณ , bool ํƒ€์ž…์˜ ๊ฒฝ์šฐ false๊ฐ€ "๋นˆ ๊ฐ’"์œผ๋กœ ๊ฐ„์ฃผ๋ผ. ๊ทธ๋ž˜์„œ ์˜๋„์น˜ ์•Š๊ฒŒ ํ•„๋“œ๊ฐ€ ์ œ์™ธ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์กฐ์‹ฌํ•ด์•ผ ํ•ด!

์˜ˆ๋ฅผ ๋“ค์–ด Age int `json:"age,omitempty"`๋ผ๊ณ  ํ•˜๋ฉด, ๋‚˜์ด๊ฐ€ 0์ผ ๋•Œ JSON์—์„œ age ํ•„๋“œ๊ฐ€ ์•„์˜ˆ ์‚ฌ๋ผ์ ธ๋ฒ„๋ ค. ์ด๊ฒŒ ์˜๋„ํ•œ ๋™์ž‘์ด ์•„๋‹ˆ๋ผ๋ฉด omitempty๋ฅผ ๋นผ์•ผ๊ฒ ์ง€?

๐ŸŒˆ ์ค‘์ฒฉ ๊ตฌ์กฐ์ฒด์™€ ๋ณต์žกํ•œ JSON ๋‹ค๋ฃจ๊ธฐ

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ์ฒด๋ณด๋‹ค ๋ณต์žกํ•˜๊ฒŒ ์ค‘์ฒฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„. ์ด๋Ÿด ๋•Œ๋„ Go๋Š” ์ •๋ง ์šฐ์•„ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽจ

type Address struct {
    Street  string `json:"street"`
    City    string `json:"city"`
    ZipCode string `json:"zip_code"`
}

type Company struct {
    Name    string  `json:"name"`
    Address Address `json:"address"`
}

type Employee struct {
    ID       int      `json:"id"`
    Name     string   `json:"name"`
    Company  Company  `json:"company"`
    Skills   []string `json:"skills"`
    Metadata map[string]interface{} `json:"metadata,omitempty"`
}

func main() {
    employee := Employee{
        ID:   101,
        Name: "๋ฐ•์ง€์„ฑ",
        Company: Company{
            Name: "ํ…Œํฌ์Šคํƒ€ํŠธ์—…",
            Address: Address{
                Street:  "ํ…Œํ—ค๋ž€๋กœ 123",
                City:    "์„œ์šธ",
                ZipCode: "06234",
            },
        },
        Skills: []string{"Go", "Python", "JavaScript"},
        Metadata: map[string]interface{}{
            "hire_date": "2023-01-15",
            "level":     "Senior",
            "remote":    true,
        },
    }
    
    // ์˜ˆ์˜๊ฒŒ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด MarshalIndent ์‚ฌ์šฉ
    jsonData, err := json.MarshalIndent(employee, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(string(jsonData))
}

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ์˜ˆ์˜๊ฒŒ ํฌ๋งท๋œ JSON์ด ์ถœ๋ ฅ๋ผ:

{
  "id": 101,
  "name": "๋ฐ•์ง€์„ฑ",
  "company": {
    "name": "ํ…Œํฌ์Šคํƒ€ํŠธ์—…",
    "address": {
      "street": "ํ…Œํ—ค๋ž€๋กœ 123",
      "city": "์„œ์šธ",
      "zip_code": "06234"
    }
  },
  "skills": [
    "Go",
    "Python",
    "JavaScript"
  ],
  "metadata": {
    "hire_date": "2023-01-15",
    "level": "Senior",
    "remote": true
  }
}
๐Ÿ’Ž MarshalIndent vs Marshal json.Marshal()์€ ํ•œ ์ค„๋กœ ์••์ถ•๋œ JSON์„ ๋งŒ๋“ค์–ด. ๋„คํŠธ์›Œํฌ ์ „์†ก์ด๋‚˜ ์ €์žฅ ๊ณต๊ฐ„์„ ์•„๊ปด์•ผ ํ•  ๋•Œ ์ข‹์ง€!

json.MarshalIndent()๋Š” ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์ข‹๊ฒŒ ๋“ค์—ฌ์“ฐ๊ธฐ๋œ JSON์„ ๋งŒ๋“ค์–ด. ๋””๋ฒ„๊น…์ด๋‚˜ ๋กœ๊ทธ ์ถœ๋ ฅํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•ด. ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๊ฐ ์ค„์˜ ์ ‘๋‘์‚ฌ(๋ณดํ†ต ๋นˆ ๋ฌธ์ž์—ด), ์„ธ ๋ฒˆ์งธ๋Š” ๋“ค์—ฌ์“ฐ๊ธฐ ๋ฌธ์ž(๋ณดํ†ต ์ŠคํŽ˜์ด์Šค 2๊ฐœ๋‚˜ 4๊ฐœ)์•ผ.

๐Ÿ” ๋™์  JSON ์ฒ˜๋ฆฌ - interface{}์˜ ํ™œ์šฉ

๋•Œ๋กœ๋Š” JSON ๊ตฌ์กฐ๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด. API ์‘๋‹ต์ด ๋งค๋ฒˆ ๋‹ค๋ฅด๊ฑฐ๋‚˜, ์„ค์ • ํŒŒ์ผ์ด ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์งˆ ๋•Œ ๋ง์ด์•ผ. ์ด๋Ÿด ๋•Œ๋Š” interface{}๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ผ! ๐ŸŽญ

func main() {
    // ๊ตฌ์กฐ๋ฅผ ๋ชจ๋ฅด๋Š” JSON ๋ฐ์ดํ„ฐ
    jsonString := `{
        "status": "success",
        "data": {
            "users": [
                {"name": "Alice", "age": 30},
                {"name": "Bob", "age": 25}
            ],
            "total": 2
        },
        "timestamp": 1234567890
    }`
    
    // interface{}๋กœ ๋ฐ›๊ธฐ
    var result map[string]interface{}
    err := json.Unmarshal([]byte(jsonString), &result)
    if err != nil {
        log.Fatal(err)
    }
    
    // ํƒ€์ž… ๋‹จ์–ธ(Type Assertion)์œผ๋กœ ์ ‘๊ทผ
    status := result["status"].(string)
    fmt.Println("์ƒํƒœ:", status)
    
    data := result["data"].(map[string]interface{})
    total := data["total"].(float64) // JSON ์ˆซ์ž๋Š” float64๋กœ ๋ณ€ํ™˜๋จ!
    fmt.Println("์ด ๊ฐœ์ˆ˜:", int(total))
    
    users := data["users"].([]interface{})
    for _, u := range users {
        user := u.(map[string]interface{})
        name := user["name"].(string)
        age := user["age"].(float64)
        fmt.Printf("์ด๋ฆ„: %s, ๋‚˜์ด: %.0f\n", name, age)
    }
}
โš ๏ธ ํƒ€์ž… ๋‹จ์–ธ ์ฃผ์˜์‚ฌํ•ญ interface{}๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํƒ€์ž… ๋‹จ์–ธ์„ ์กฐ์‹ฌํ•ด์•ผ ํ•ด! ์ž˜๋ชป๋œ ํƒ€์ž…์œผ๋กœ ๋‹จ์–ธํ•˜๋ฉด ํŒจ๋‹‰์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ์•ˆ์ „ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ์ด๋ ‡๊ฒŒ ํ•ด:

if status, ok := result["status"].(string); ok {
// status ์‚ฌ์šฉ
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํƒ€์ž… ๋ณ€ํ™˜์ด ์‹คํŒจํ•ด๋„ ํ”„๋กœ๊ทธ๋žจ์ด ์ฃฝ์ง€ ์•Š์•„!

๐ŸŽช ์ปค์Šคํ…€ JSON ๋งˆ์ƒฌ๋ง - MarshalJSON ์ธํ„ฐํŽ˜์ด์Šค

๋•Œ๋กœ๋Š” ๊ธฐ๋ณธ JSON ๋ณ€ํ™˜ ๋ฐฉ์‹์ด ๋งˆ์Œ์— ๋“ค์ง€ ์•Š์„ ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‚ ์งœ๋ฅผ ํŠน์ • ํฌ๋งท์œผ๋กœ ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ๋‹ค๊ฑฐ๋‚˜, ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋งˆ์Šคํ‚นํ•˜๊ณ  ์‹ถ์„ ๋•Œ ๋ง์ด์•ผ. ์ด๋Ÿด ๋•Œ๋Š” json.Marshaler ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ผ! ๐ŸŽจ

import (
    "encoding/json"
    "fmt"
    "time"
)

type CustomDate struct {
    time.Time
}

// MarshalJSON ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
func (cd CustomDate) MarshalJSON() ([]byte, error) {
    // ์›ํ•˜๋Š” ํฌ๋งท์œผ๋กœ ๋‚ ์งœ ๋ณ€ํ™˜
    formatted := cd.Format("2006๋…„ 01์›” 02์ผ")
    return json.Marshal(formatted)
}

// UnmarshalJSON ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
func (cd *CustomDate) UnmarshalJSON(data []byte) error {
    var dateStr string
    if err := json.Unmarshal(data, &dateStr); err != nil {
        return err
    }
    
    parsed, err := time.Parse("2006๋…„ 01์›” 02์ผ", dateStr)
    if err != nil {
        return err
    }
    
    cd.Time = parsed
    return nil
}

type Event struct {
    Name string     `json:"name"`
    Date CustomDate `json:"date"`
}

func main() {
    event := Event{
        Name: "Go ์ปจํผ๋Ÿฐ์Šค",
        Date: CustomDate{time.Now()},
    }
    
    jsonData, _ := json.MarshalIndent(event, "", "  ")
    fmt.Println(string(jsonData))
    // ์ถœ๋ ฅ: {"name":"Go ์ปจํผ๋Ÿฐ์Šค","date":"2024๋…„ 01์›” 15์ผ"}
}

๋˜ ๋‹ค๋ฅธ ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งˆ์Šคํ‚น์„ ํ•ด๋ณผ๊นŒ? ๐Ÿ”

type SecureUser struct {
    Username string `json:"username"`
    Password string `json:"-"` // ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ์™ธ
    Email    string `json:"email"`
}

// MarshalJSON์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋งˆ์Šคํ‚น
func (su SecureUser) MarshalJSON() ([]byte, error) {
    type Alias SecureUser // ๋ฌดํ•œ ์žฌ๊ท€ ๋ฐฉ์ง€
    return json.Marshal(&struct {
        *Alias
        Password string `json:"password"`
    }{
        Alias:    (*Alias)(&su),
        Password: "****", // ๋งˆ์Šคํ‚น๋œ ๊ฐ’
    })
}

func main() {
    user := SecureUser{
        Username: "gopher",
        Password: "super-secret-123",
        Email:    "gopher@example.com",
    }
    
    jsonData, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(jsonData))
    // ์ถœ๋ ฅ: {"username":"gopher","email":"gopher@example.com","password":"****"}
}
JSON ์ฒ˜๋ฆฌ ํ๋ฆ„๋„ Go ๊ตฌ์กฐ์ฒด type User struct JSON ๋ฐ”์ดํŠธ []byte JSON ๋ฌธ์ž์—ด string Marshal string() Unmarshal MarshalIndent ์˜ˆ์˜๊ฒŒ ํฌ๋งท๋œ JSON ๋””๋ฒ„๊น…์šฉ์œผ๋กœ ์ตœ์  ๋“ค์—ฌ์“ฐ๊ธฐ ์ง€์› ์ปค์Šคํ…€ ๋งˆ์ƒฌ๋ง MarshalJSON ๊ตฌํ˜„ UnmarshalJSON ๊ตฌํ˜„ ์›ํ•˜๋Š” ๋Œ€๋กœ ๋ณ€ํ™˜ ๋™์  ์ฒ˜๋ฆฌ interface{} ํ™œ์šฉ map[string]interface{} ํƒ€์ž… ๋‹จ์–ธ ํ•„์š” ๐ŸŽฏ โœจ ๐Ÿ”ง ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๋งž๋Š” JSON ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜์„ธ์š”!

๐Ÿ“ JSON Encoder์™€ Decoder - ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ ๋ณธ Marshal๊ณผ Unmarshal์€ ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ. ํ•˜์ง€๋งŒ ํฐ ํŒŒ์ผ์„ ์ฝ๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” Encoder์™€ Decoder๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋” ํšจ์œจ์ ์ด์•ผ! ๐Ÿš€

import (
    "encoding/json"
    "os"
)

type LogEntry struct {
    Timestamp string `json:"timestamp"`
    Level     string `json:"level"`
    Message   string `json:"message"`
}

func writeLogsToFile() error {
    file, err := os.Create("logs.json")
    if err != nil {
        return err
    }
    defer file.Close()
    
    // Encoder ์ƒ์„ฑ - ํŒŒ์ผ์— ์ง์ ‘ ์“ฐ๊ธฐ
    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ") // ์˜ˆ์˜๊ฒŒ ํฌ๋งท
    
    logs := []LogEntry{
        {Timestamp: "2024-01-15 10:00:00", Level: "INFO", Message: "์„œ๋ฒ„ ์‹œ์ž‘"},
        {Timestamp: "2024-01-15 10:05:00", Level: "WARN", Message: "๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๋†’์Œ"},
        {Timestamp: "2024-01-15 10:10:00", Level: "ERROR", Message: "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์‹คํŒจ"},
    }
    
    // ๊ฐ ๋กœ๊ทธ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ธ์ฝ”๋”ฉ
    for _, log := range logs {
        if err := encoder.Encode(log); err != nil {
            return err
        }
    }
    
    return nil
}

func readLogsFromFile() error {
    file, err := os.Open("logs.json")
    if err != nil {
        return err
    }
    defer file.Close()
    
    // Decoder ์ƒ์„ฑ - ํŒŒ์ผ์—์„œ ์ง์ ‘ ์ฝ๊ธฐ
    decoder := json.NewDecoder(file)
    
    // ์ŠคํŠธ๋ฆผ์—์„œ ํ•˜๋‚˜์”ฉ ๋””์ฝ”๋”ฉ
    for decoder.More() {
        var log LogEntry
        if err := decoder.Decode(&log); err != nil {
            return err
        }
        fmt.Printf("[%s] %s: %s\n", log.Level, log.Timestamp, log.Message)
    }
    
    return nil
}
๐ŸŽฏ Encoder/Decoder vs Marshal/Unmarshal Encoder/Decoder๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ:
โ€ข ํŒŒ์ผ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ
โ€ข ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ด ์ค‘์š”ํ•  ๋•Œ

Marshal/Unmarshal์„ ์‚ฌ์šฉํ•  ๋•Œ:
โ€ข ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋Š” ์ž‘์€ ๋ฐ์ดํ„ฐ
โ€ข ํ•œ ๋ฒˆ์— ์ „์ฒด ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข ๊ฐ„๋‹จํ•˜๊ณ  ์ง๊ด€์ ์ธ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ

๐ŸŽช XML ์ฒ˜๋ฆฌ - JSON์˜ ํ˜•์ œ

์ž, ์ด์ œ XML์„ ์‚ดํŽด๋ณผ ์ฐจ๋ก€์•ผ! XML์€ JSON๋ณด๋‹ค ์ข€ ๋” ๋ณต์žกํ•˜๊ณ  ์žฅํ™ฉํ•˜์ง€๋งŒ, ์—ฌ์ „ํžˆ ๋งŽ์€ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด. ํŠนํžˆ ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ์ด๋‚˜ SOAP API, ์„ค์ • ํŒŒ์ผ ๋“ฑ์—์„œ ๋ง์ด์•ผ. ๐Ÿ“œ

Go์˜ encoding/xml ํŒจํ‚ค์ง€๋Š” JSON ํŒจํ‚ค์ง€์™€ ๋งค์šฐ ์œ ์‚ฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ด์„œ ๋ฐฐ์šฐ๊ธฐ ์‰ฌ์›Œ!

import (
    "encoding/xml"
    "fmt"
)

// XML ๊ตฌ์กฐ์ฒด ์ •์˜
type Book struct {
    XMLName xml.Name `xml:"book"` // ๋ฃจํŠธ ์—˜๋ฆฌ๋จผํŠธ ์ด๋ฆ„
    Title   string   `xml:"title"`
    Author  string   `xml:"author"`
    Year    int      `xml:"year"`
    ISBN    string   `xml:"isbn,attr"` // ์†์„ฑ์œผ๋กœ ํ‘œํ˜„
    Pages   int      `xml:"pages,omitempty"`
}

type Library struct {
    XMLName xml.Name `xml:"library"`
    Name    string   `xml:"name,attr"`
    Books   []Book   `xml:"book"`
}

func main() {
    library := Library{
        Name: "์ค‘์•™๋„์„œ๊ด€",
        Books: []Book{
            {
                Title:  "Go ํ”„๋กœ๊ทธ๋ž˜๋ฐ",
                Author: "๊น€๊ณ ํผ",
                Year:   2024,
                ISBN:   "978-1234567890",
                Pages:  450,
            },
            {
                Title:  "ํด๋ฆฐ ์ฝ”๋“œ",
                Author: "๋กœ๋ฒ„ํŠธ ๋งˆํ‹ด",
                Year:   2008,
                ISBN:   "978-0132350884",
                Pages:  464,
            },
        },
    }
    
    // XML๋กœ ๋งˆ์ƒฌ๋ง
    xmlData, err := xml.MarshalIndent(library, "", "  ")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    // XML ์„ ์–ธ ์ถ”๊ฐ€
    output := xml.Header + string(xmlData)
    fmt.Println(output)
}

์œ„ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋Š” ์ด๋ ‡๊ฒŒ ๋‚˜์™€:

<?xml version="1.0" encoding="UTF-8"?>
<library name="์ค‘์•™๋„์„œ๊ด€">
  <book isbn="978-1234567890">
    <title>Go ํ”„๋กœ๊ทธ๋ž˜๋ฐ</title>
    <author>๊น€๊ณ ํผ</author>
    <year>2024</year>
    <pages>450</pages>
  </book>
  <book isbn="978-0132350884">
    <title>ํด๋ฆฐ ์ฝ”๋“œ</title>
    <author>๋กœ๋ฒ„ํŠธ ๋งˆํ‹ด</author>
    <year>2008</year>
    <pages>464</pages>
  </book>
</library>

๐Ÿท๏ธ XML ํƒœ๊ทธ ์˜ต์…˜ ์™„์ „ ๊ฐ€์ด๋“œ

XML ํƒœ๊ทธ๋„ JSON์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ์ œ๊ณตํ•ด. ํ•˜๋‚˜์”ฉ ์•Œ์•„๋ณด์ž! ๐ŸŽจ

ํƒœ๊ทธ ์˜ต์…˜ ์„ค๋ช… ์˜ˆ์‹œ
xml:"name" ์—˜๋ฆฌ๋จผํŠธ ์ด๋ฆ„ ์ง€์ • Title string `xml:"title"`
xml:"name,attr" ์†์„ฑ์œผ๋กœ ํ‘œํ˜„ ID string `xml:"id,attr"`
xml:",chardata" ํ…์ŠคํŠธ ๋‚ด์šฉ Content string `xml:",chardata"`
xml:",cdata" CDATA ์„น์…˜ HTML string `xml:",cdata"`
xml:",innerxml" ์›์‹œ XML ๋‚ด์šฉ Raw string `xml:",innerxml"`
xml:",omitempty" ๋นˆ ๊ฐ’ ์ œ์™ธ Optional string `xml:",omitempty"`
xml:"-" ์™„์ „ํžˆ ๋ฌด์‹œ Internal string `xml:"-"`

์‹ค์ œ ์˜ˆ์ œ๋กœ ์ด ์˜ต์…˜๋“ค์„ ํ™œ์šฉํ•ด๋ณด์ž:

type Article struct {
    XMLName xml.Name `xml:"article"`
    ID      string   `xml:"id,attr"`
    Lang    string   `xml:"lang,attr,omitempty"`
    Title   string   `xml:"title"`
    Content string   `xml:"content,cdata"` // HTML ํฌํ•จ ๊ฐ€๋Šฅ
    Author  Author   `xml:"author"`
    Tags    []string `xml:"tags>tag"` // ์ค‘์ฒฉ ๊ตฌ์กฐ
}

type Author struct {
    Name  string `xml:"name"`
    Email string `xml:"email,omitempty"`
}

func main() {
    article := Article{
        ID:    "article-001",
        Lang:  "ko",
        Title: "Go์™€ XML",
        Content: "<p>์ด๊ฒƒ์€ <strong>HTML</strong> ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.</p>",
        Author: Author{
            Name:  "๊น€๊ฐœ๋ฐœ",
            Email: "dev@example.com",
        },
        Tags: []string{"Go", "XML", "ํ”„๋กœ๊ทธ๋ž˜๋ฐ"},
    }
    
    xmlData, _ := xml.MarshalIndent(article, "", "  ")
    fmt.Println(xml.Header + string(xmlData))
}

๐ŸŽฏ XML ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ฒ˜๋ฆฌ

XML์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์•ผ. ์—ฌ๋Ÿฌ XML ์Šคํ‚ค๋งˆ๋ฅผ ํ˜ผํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ๋•Œ ์ด๋ฆ„ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€. Go์—์„œ๋„ ์ด๊ฑธ ์ง€์›ํ•ด! ๐ŸŒ

type RSS struct {
    XMLName xml.Name `xml:"rss"`
    Version string   `xml:"version,attr"`
    Channel Channel  `xml:"channel"`
    
    // ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์„ ์–ธ
    XMLNS   string `xml:"xmlns:atom,attr"`
    Content string `xml:"xmlns:content,attr"`
}

type Channel struct {
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`
    Items       []Item `xml:"item"`
    
    // atom ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ
    AtomLink AtomLink `xml:"atom:link"`
}

type AtomLink struct {
    Href string `xml:"href,attr"`
    Rel  string `xml:"rel,attr"`
    Type string `xml:"type,attr"`
}

type Item struct {
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`
    PubDate     string `xml:"pubDate"`
    
    // content ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์‚ฌ์šฉ
    ContentEncoded string `xml:"content:encoded"`
}

func main() {
    rss := RSS{
        Version: "2.0",
        XMLNS:   "http://www.w3.org/2005/Atom",
        Content: "http://purl.org/rss/1.0/modules/content/",
        Channel: Channel{
            Title:       "Go ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ธ”๋กœ๊ทธ",
            Link:        "https://example.com",
            Description: "Go ์–ธ์–ด์— ๋Œ€ํ•œ ๋ชจ๋“  ๊ฒƒ",
            AtomLink: AtomLink{
                Href: "https://example.com/feed",
                Rel:  "self",
                Type: "application/rss+xml",
            },
            Items: []Item{
                {
                    Title:          "Go 1.22 ์ถœ์‹œ",
                    Link:           "https://example.com/go-1.22",
                    Description:    "์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์†Œ๊ฐœ",
                    PubDate:        "Mon, 15 Jan 2024 10:00:00 GMT",
                    ContentEncoded: "<p>์ƒ์„ธํ•œ HTML ๋‚ด์šฉ...</p>",
                },
            },
        },
    }
    
    xmlData, _ := xml.MarshalIndent(rss, "", "  ")
    fmt.Println(xml.Header + string(xmlData))
}
๐ŸŒŸ ์‹ค๋ฌด ํŒ: RSS ํ”ผ๋“œ ์ƒ์„ฑ ์œ„ ์˜ˆ์ œ๋Š” ์‹ค์ œ๋กœ RSS ํ”ผ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ์•ผ! ๋ธ”๋กœ๊ทธ๋‚˜ ๋‰ด์Šค ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์ •๋ง ์œ ์šฉํ•˜์ง€. XML ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ‘œ์ค€ RSS ์ŠคํŽ™์„ ์ •ํ™•ํ•˜๊ฒŒ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ์ฝ˜ํ…์ธ  ํ”ผ๋“œ๋ฅผ ์ œ๊ณตํ•  ๋•Œ ์ด๋Ÿฐ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ”„ JSON๊ณผ XML ์ƒํ˜ธ ๋ณ€ํ™˜

์‹ค๋ฌด์—์„œ๋Š” JSON๊ณผ XML์„ ์„œ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ์€ XML์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ๋ชจ๋˜ ํ”„๋ก ํŠธ์—”๋“œ๋Š” JSON์„ ์„ ํ˜ธํ•˜๋Š” ๊ฒฝ์šฐ ๋ง์ด์•ผ. ์ด๋Ÿด ๋•Œ ์–ด๋–ป๊ฒŒ ํ• ๊นŒ? ๐Ÿ”„

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
)

// ๊ณตํ†ต ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
type Product struct {
    ID          int     `json:"id" xml:"id,attr"`
    Name        string  `json:"name" xml:"name"`
    Price       float64 `json:"price" xml:"price"`
    Category    string  `json:"category" xml:"category"`
    InStock     bool    `json:"in_stock" xml:"in_stock"`
    Description string  `json:"description,omitempty" xml:"description,omitempty"`
}

// JSON์„ XML๋กœ ๋ณ€ํ™˜
func jsonToXML(jsonData []byte) ([]byte, error) {
    var product Product
    
    // JSON ํŒŒ์‹ฑ
    if err := json.Unmarshal(jsonData, &product); err != nil {
        return nil, err
    }
    
    // XML๋กœ ๋ณ€ํ™˜
    xmlData, err := xml.MarshalIndent(product, "", "  ")
    if err != nil {
        return nil, err
    }
    
    return append([]byte(xml.Header), xmlData...), nil
}

// XML์„ JSON์œผ๋กœ ๋ณ€ํ™˜
func xmlToJSON(xmlData []byte) ([]byte, error) {
    var product Product
    
    // XML ํŒŒ์‹ฑ
    if err := xml.Unmarshal(xmlData, &product); err != nil {
        return nil, err
    }
    
    // JSON์œผ๋กœ ๋ณ€ํ™˜
    jsonData, err := json.MarshalIndent(product, "", "  ")
    if err != nil {
        return nil, err
    }
    
    return jsonData, nil
}

func main() {
    // JSON ๋ฐ์ดํ„ฐ
    jsonStr := `{
        "id": 101,
        "name": "Go ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฑ…",
        "price": 35000,
        "category": "๋„์„œ",
        "in_stock": true,
        "description": "Go ์–ธ์–ด ์™„๋ฒฝ ๊ฐ€์ด๋“œ"
    }`
    
    // JSON โ†’ XML
    xmlData, err := jsonToXML([]byte(jsonStr))
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    fmt.Println("=== XML ์ถœ๋ ฅ ===")
    fmt.Println(string(xmlData))
    
    // XML โ†’ JSON
    jsonData, err := xmlToJSON(xmlData)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    fmt.Println("\n=== JSON ์ถœ๋ ฅ ===")
    fmt.Println(string(jsonData))
}
JSON โ†” XML ์ƒํ˜ธ ๋ณ€ํ™˜ JSON { "id": 101, "name": "์ƒํ’ˆ๋ช…", "price": 35000 } โœ“ ๊ฐ„๊ฒฐํ•จ โœ“ ์›น ์นœํ™”์  XML <product id="101"> <name>์ƒํ’ˆ๋ช…</name> <price>35000</price> </product> โœ“ ๊ตฌ์กฐ์  โœ“ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋ณ€ํ™˜ ๋ณ€ํ™˜ ๐Ÿ”„

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”๋Š” ์„ฑ๋Šฅ์— ๋ฏผ๊ฐํ•œ ์ž‘์—…์ด์•ผ. ํŠนํžˆ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰์ด ํ•„์š”ํ•œ ์„œ๋น„์Šค์—์„œ๋Š” ์ตœ์ ํ™”๊ฐ€ ์ •๋ง ์ค‘์š”ํ•ด! ๋ช‡ ๊ฐ€์ง€ ์‹ค์šฉ์ ์ธ ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ. ๐Ÿš€

๐Ÿ’ก ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ 1. ๊ตฌ์กฐ์ฒด ํƒœ๊ทธ ์ตœ์ ํ™”
โ€ข ๋ถˆํ•„์š”ํ•œ ํ•„๋“œ๋Š” json:"-"๋กœ ์ œ์™ธ
โ€ข omitempty๋กœ ๋นˆ ๊ฐ’ ์ œ๊ฑฐํ•ด์„œ ๋ฐ์ดํ„ฐ ํฌ๊ธฐ ์ค„์ด๊ธฐ

2. ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ตœ์†Œํ™”
โ€ข ํฐ ๋ฐ์ดํ„ฐ๋Š” Encoder/Decoder ์‚ฌ์šฉ
โ€ข ๋ฒ„ํผ ์žฌ์‚ฌ์šฉ์œผ๋กœ GC ์••๋ ฅ ์ค„์ด๊ธฐ

3. ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
โ€ข ์—ฌ๋Ÿฌ ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ ๊ณ ๋ฃจํ‹ด ํ™œ์šฉ
โ€ข ์ฑ„๋„๋กœ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ

4. ํ”„๋กœํŒŒ์ผ๋ง
โ€ข pprof๋กœ ๋ณ‘๋ชฉ ์ง€์  ์ฐพ๊ธฐ
โ€ข ๋ฒค์น˜๋งˆํฌ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ
import (
    "bytes"
    "encoding/json"
    "sync"
)

// ๋ฒ„ํผ ํ’€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์žฌ์‚ฌ์šฉ
var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

// ์ตœ์ ํ™”๋œ JSON ๋งˆ์ƒฌ๋ง
func optimizedMarshal(v interface{}) ([]byte, error) {
    buf := bufferPool.Get().(*bytes.Buffer)
    buf.Reset()
    defer bufferPool.Put(buf)
    
    encoder := json.NewEncoder(buf)
    if err := encoder.Encode(v); err != nil {
        return nil, err
    }
    
    // ๋งˆ์ง€๋ง‰ ๊ฐœํ–‰ ๋ฌธ์ž ์ œ๊ฑฐ
    result := buf.Bytes()
    if len(result) > 0 && result[len(result)-1] == '\n' {
        result = result[:len(result)-1]
    }
    
    return result, nil
}

// ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์˜ˆ์ œ
func processMultipleFiles(filenames []string) error {
    var wg sync.WaitGroup
    errChan := make(chan error, len(filenames))
    
    for _, filename := range filenames {
        wg.Add(1)
        go func(fname string) {
            defer wg.Done()
            
            // ํŒŒ์ผ ์ฒ˜๋ฆฌ ๋กœ์ง
            if err := processFile(fname); err != nil {
                errChan <- err
            }
        }(filename)
    }
    
    wg.Wait()
    close(errChan)
    
    // ์—๋Ÿฌ ์ฒดํฌ
    for err := range errChan {
        if err != nil {
            return err
        }
    }
    
    return nil
}

func processFile(filename string) error {
    // ํŒŒ์ผ ์ฒ˜๋ฆฌ ๊ตฌํ˜„
    return nil
}

๐ŸŽฏ ์‹ค์ „ ์˜ˆ์ œ: RESTful API ํด๋ผ์ด์–ธํŠธ

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ RESTful API ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ์ด๊ฑด ์ •๋ง ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด์•ผ. ๐Ÿ˜Š

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

// API ์‘๋‹ต ๊ตฌ์กฐ์ฒด
type APIResponse struct {
    Success bool        `json:"success"`
    Data    interface{} `json:"data,omitempty"`
    Error   string      `json:"error,omitempty"`
    Meta    Meta        `json:"meta"`
}

type Meta struct {
    Timestamp time.Time `json:"timestamp"`
    Version   string    `json:"version"`
}

// ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ
type User struct {
    ID        int       `json:"id,omitempty"`
    Username  string    `json:"username"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at,omitempty"`
}

// API ํด๋ผ์ด์–ธํŠธ
type APIClient struct {
    BaseURL    string
    HTTPClient *http.Client
}

func NewAPIClient(baseURL string) *APIClient {
    return &APIClient{
        BaseURL: baseURL,
        HTTPClient: &http.Client{
            Timeout: 10 * time.Second,
        },
    }
}

// GET ์š”์ฒญ
func (c *APIClient) GetUser(userID int) (*User, error) {
    url := fmt.Sprintf("%s/users/%d", c.BaseURL, userID)
    
    resp, err := c.HTTPClient.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("API error: status %d", resp.StatusCode)
    }
    
    var apiResp APIResponse
    if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
        return nil, err
    }
    
    if !apiResp.Success {
        return nil, fmt.Errorf("API error: %s", apiResp.Error)
    }
    
    // interface{}๋ฅผ User๋กœ ๋ณ€ํ™˜
    userData, _ := json.Marshal(apiResp.Data)
    var user User
    if err := json.Unmarshal(userData, &user); err != nil {
        return nil, err
    }
    
    return &user, nil
}

// POST ์š”์ฒญ
func (c *APIClient) CreateUser(user *User) (*User, error) {
    url := fmt.Sprintf("%s/users", c.BaseURL)
    
    // ์š”์ฒญ ๋ฐ”๋”” ์ƒ์„ฑ
    jsonData, err := json.Marshal(user)
    if err != nil {
        return nil, err
    }
    
    resp, err := c.HTTPClient.Post(url, "application/json", bytes.NewBuffer(jsonData))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusCreated {
        body, _ := io.ReadAll(resp.Body)
        return nil, fmt.Errorf("API error: status %d, body: %s", resp.StatusCode, string(body))
    }
    
    var apiResp APIResponse
    if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
        return nil, err
    }
    
    userData, _ := json.Marshal(apiResp.Data)
    var createdUser User
    if err := json.Unmarshal(userData, &createdUser); err != nil {
        return nil, err
    }
    
    return &createdUser, nil
}

// PUT ์š”์ฒญ
func (c *APIClient) UpdateUser(userID int, user *User) (*User, error) {
    url := fmt.Sprintf("%s/users/%d", c.BaseURL, userID)
    
    jsonData, err := json.Marshal(user)
    if err != nil {
        return nil, err
    }
    
    req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonData))
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var apiResp APIResponse
    if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
        return nil, err
    }
    
    userData, _ := json.Marshal(apiResp.Data)
    var updatedUser User
    if err := json.Unmarshal(userData, &updatedUser); err != nil {
        return nil, err
    }
    
    return &updatedUser, nil
}

// DELETE ์š”์ฒญ
func (c *APIClient) DeleteUser(userID int) error {
    url := fmt.Sprintf("%s/users/%d", c.BaseURL, userID)
    
    req, err := http.NewRequest(http.MethodDelete, url, nil)
    if err != nil {
        return err
    }
    
    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
        return fmt.Errorf("API error: status %d", resp.StatusCode)
    }
    
    return nil
}

func main() {
    client := NewAPIClient("https://api.example.com")
    
    // ์‚ฌ์šฉ์ž ์ƒ์„ฑ
    newUser := &User{
        Username: "gopher",
        Email:    "gopher@example.com",
    }
    
    createdUser, err := client.CreateUser(newUser)
    if err != nil {
        fmt.Println("Error creating user:", err)
        return
    }
    
    fmt.Printf("Created user: %+v\n", createdUser)
    
    // ์‚ฌ์šฉ์ž ์กฐํšŒ
    user, err := client.GetUser(createdUser.ID)
    if err != nil {
        fmt.Println("Error getting user:", err)
        return
    }
    
    fmt.Printf("Retrieved user: %+v\n", user)
    
    // ์‚ฌ์šฉ์ž ์—…๋ฐ์ดํŠธ
    user.Email = "newemail@example.com"
    updatedUser, err := client.UpdateUser(user.ID, user)
    if err != nil {
        fmt.Println("Error updating user:", err)
        return
    }
    
    fmt.Printf("Updated user: %+v\n", updatedUser)
    
    // ์‚ฌ์šฉ์ž ์‚ญ์ œ
    if err := client.DeleteUser(user.ID); err != nil {
        fmt.Println("Error deleting user:", err)
        return
    }
    
    fmt.Println("User deleted successfully")
}
๐ŸŒŸ ์‹ค๋ฌด ํŒ: ์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ์žฌ์‹œ๋„ ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ์ผ์‹œ์ ์ธ ์„œ๋ฒ„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด. ๊ทธ๋ž˜์„œ ์žฌ์‹œ๋„ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒŒ ์ข‹์•„:

โ€ข ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„(Exponential Backoff) ์ „๋žต ์‚ฌ์šฉ
โ€ข ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜ ์„ค์ •
โ€ข ํƒ€์ž„์•„์›ƒ ์ ์ ˆํžˆ ์กฐ์ •
โ€ข ๋กœ๊น…์œผ๋กœ ๋ฌธ์ œ ์ถ”์ 

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์™ธ๋ถ€ API๋ฅผ ์—ฐ๋™ํ•  ๋•Œ ์ด๋Ÿฐ ํŒจํ„ด์ด ์ •๋ง ์ค‘์š”ํ•ด!

๐ŸŽจ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•: ์ œ๋„ค๋ฆญ์„ ํ™œ์šฉํ•œ ๋ฒ”์šฉ ์ง๋ ฌํ™”

Go 1.18๋ถ€ํ„ฐ ์ œ๋„ค๋ฆญ์ด ๋„์ž…๋˜๋ฉด์„œ ๋” ์œ ์—ฐํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด! ์ง๋ ฌํ™” ์ฝ”๋“œ์—๋„ ์ œ๋„ค๋ฆญ์„ ํ™œ์šฉํ•ด๋ณด์ž. ๐Ÿš€

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
    "io"
)

// ์ œ๋„ค๋ฆญ ์ง๋ ฌํ™” ์ธํ„ฐํŽ˜์ด์Šค
type Serializer[T any] interface {
    Marshal(v T) ([]byte, error)
    Unmarshal(data []byte, v *T) error
}

// JSON ์ง๋ ฌํ™”๊ธฐ
type JSONSerializer[T any] struct{}

func (s JSONSerializer[T]) Marshal(v T) ([]byte, error) {
    return json.Marshal(v)
}

func (s JSONSerializer[T]) Unmarshal(data []byte, v *T) error {
    return json.Unmarshal(data, v)
}

// XML ์ง๋ ฌํ™”๊ธฐ
type XMLSerializer[T any] struct{}

func (s XMLSerializer[T]) Marshal(v T) ([]byte, error) {
    return xml.Marshal(v)
}

func (s XMLSerializer[T]) Unmarshal(data []byte, v *T) error {
    return xml.Unmarshal(data, v)
}

// ๋ฒ”์šฉ ์ €์žฅ/๋กœ๋“œ ํ•จ์ˆ˜
func Save[T any](filename string, data T, serializer Serializer[T]) error {
    bytes, err := serializer.Marshal(data)
    if err != nil {
        return err
    }
    
    // ํŒŒ์ผ ์ €์žฅ ๋กœ์ง (๊ฐ„๋žตํ™”)
    fmt.Printf("Saving to %s: %s\n", filename, string(bytes))
    return nil
}

func Load[T any](filename string, serializer Serializer[T]) (T, error) {
    var result T
    
    // ํŒŒ์ผ ์ฝ๊ธฐ ๋กœ์ง (๊ฐ„๋žตํ™”)
    data := []byte(`{"name":"test","value":123}`)
    
    if err := serializer.Unmarshal(data, &result); err != nil {
        return result, err
    }
    
    return result, nil
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
type Config struct {
    Name  string `json:"name" xml:"name"`
    Value int    `json:"value" xml:"value"`
}

func main() {
    config := Config{Name: "MyApp", Value: 123}
    
    // JSON์œผ๋กœ ์ €์žฅ
    jsonSer := JSONSerializer[Config]{}
    if err := Save("config.json", config, jsonSer); err != nil {
        fmt.Println("Error:", err)
    }
    
    // XML๋กœ ์ €์žฅ
    xmlSer := XMLSerializer[Config]{}
    if err := Save("config.xml", config, xmlSer); err != nil {
        fmt.Println("Error:", err)
    }
    
    // JSON์—์„œ ๋กœ๋“œ
    loadedConfig, err := Load("config.json", jsonSer)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Printf("Loaded: %+v\n", loadedConfig)
    }
}

๐Ÿ›ก๏ธ ๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ณด์•ˆ๋„ ์ •๋ง ์ค‘์š”ํ•ด! ํŠนํžˆ ์™ธ๋ถ€์—์„œ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ญ์ง๋ ฌํ™”ํ•  ๋•Œ๋Š” ๋”์šฑ ์กฐ์‹ฌํ•ด์•ผ ํ•ด. ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ๋ณด์•ˆ ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ. ๐Ÿ”’

โš ๏ธ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ 1. ์ž…๋ ฅ ๊ฒ€์ฆ
โ€ข ์—ญ์ง๋ ฌํ™” ์ „์— ๋ฐ์ดํ„ฐ ํฌ๊ธฐ ํ™•์ธ
โ€ข ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ•„๋“œ๋‚˜ ๊ฐ’ ๊ฑฐ๋ถ€
โ€ข ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์œผ๋กœ ํ—ˆ์šฉ๋œ ๊ฐ’๋งŒ ์ฒ˜๋ฆฌ

2. ๋ฏผ๊ฐ ์ •๋ณด ๋ณดํ˜ธ
โ€ข ๋น„๋ฐ€๋ฒˆํ˜ธ, API ํ‚ค ๋“ฑ์€ ์ ˆ๋Œ€ ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š๊ธฐ
โ€ข json:"-" ํƒœ๊ทธ๋กœ ์ œ์™ธ
โ€ข ๋กœ๊ทธ์— ๋ฏผ๊ฐ ์ •๋ณด ์ถœ๋ ฅ ๊ธˆ์ง€

3. DoS ๊ณต๊ฒฉ ๋ฐฉ์–ด
โ€ข ์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ ์„ค์ •
โ€ข ์ค‘์ฒฉ ๊นŠ์ด ์ œํ•œ
โ€ข ํƒ€์ž„์•„์›ƒ ์„ค์ •

4. ํƒ€์ž… ์•ˆ์ „์„ฑ
โ€ข interface{} ์‚ฌ์šฉ ์‹œ ํƒ€์ž… ๊ฒ€์ฆ
โ€ข ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํƒ€์ž… ๊ฑฐ๋ถ€
import (
    "encoding/json"
    "errors"
    "fmt"
    "io"
    "strings"
)

// ์•ˆ์ „ํ•œ ์—ญ์ง๋ ฌํ™” ํ•จ์ˆ˜
func SafeUnmarshal(data []byte, v interface{}, maxSize int) error {
    // 1. ํฌ๊ธฐ ๊ฒ€์ฆ
    if len(data) > maxSize {
        return fmt.Errorf("data too large: %d bytes (max: %d)", len(data), maxSize)
    }
    
    // 2. ๊ธฐ๋ณธ JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    if !json.Valid(data) {
        return errors.New("invalid JSON")
    }
    
    // 3. ์—ญ์ง๋ ฌํ™”
    if err := json.Unmarshal(data, v); err != nil {
        return fmt.Errorf("unmarshal error: %w", err)
    }
    
    return nil
}

// ์ œํ•œ๋œ Reader๋กœ DoS ๋ฐฉ์–ด
type LimitedReader struct {
    R io.Reader
    N int64
}

func (l *LimitedReader) Read(p []byte) (n int, err error) {
    if l.N <= 0 {
        return 0, errors.New("read limit exceeded")
    }
    if int64(len(p)) > l.N {
        p = p[0:l.N]
    }
    n, err = l.R.Read(p)
    l.N -= int64(n)
    return
}

// ์•ˆ์ „ํ•œ Decoder
func SafeDecode(r io.Reader, v interface{}, maxBytes int64) error {
    limitedReader := &LimitedReader{R: r, N: maxBytes}
    decoder := json.NewDecoder(limitedReader)
    
    // ์ค‘์ฒฉ ๊นŠ์ด ์ œํ•œ (Go 1.10+)
    decoder.DisallowUnknownFields() // ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ•„๋“œ ๊ฑฐ๋ถ€
    
    if err := decoder.Decode(v); err != nil {
        return fmt.Errorf("decode error: %w", err)
    }
    
    return nil
}

// ๋ฏผ๊ฐ ์ •๋ณด ๋งˆ์Šคํ‚น
type SecureConfig struct {
    AppName    string `json:"app_name"`
    APIKey     string `json:"-"` // ์ง๋ ฌํ™”์—์„œ ์ œ์™ธ
    DBPassword string `json:"-"` // ์ง๋ ฌํ™”์—์„œ ์ œ์™ธ
    PublicURL  string `json:"public_url"`
}

// ๋กœ๊น…์šฉ ์•ˆ์ „ํ•œ ๋ฌธ์ž์—ด ๋ณ€ํ™˜
func (sc SecureConfig) SafeString() string {
    return fmt.Sprintf("SecureConfig{AppName: %s, PublicURL: %s, APIKey: [REDACTED], DBPassword: [REDACTED]}", 
        sc.AppName, sc.PublicURL)
}

// ์ž…๋ ฅ ๊ฒ€์ฆ ์˜ˆ์ œ
type ValidatedUser struct {
    Username string `json:"username"`
    Email    string `json:"email"`
    Age      int    `json:"age"`
}

func (u *ValidatedUser) Validate() error {
    if len(u.Username) < 3 || len(u.Username) > 20 {
        return errors.New("username must be 3-20 characters")
    }
    
    if !strings.Contains(u.Email, "@") {
        return errors.New("invalid email format")
    }
    
    if u.Age < 0 || u.Age > 150 {
        return errors.New("invalid age")
    }
    
    return nil
}

func main() {
    // ์•ˆ์ „ํ•œ ์—ญ์ง๋ ฌํ™” ์˜ˆ์ œ
    jsonData := []byte(`{"username":"gopher","email":"gopher@example.com","age":5}`)
    
    var user ValidatedUser
    if err := SafeUnmarshal(jsonData, &user, 1024); err != nil {
        fmt.Println("Unmarshal error:", err)
        return
    }
    
    // ๊ฒ€์ฆ
    if err := user.Validate(); err != nil {
        fmt.Println("Validation error:", err)
        return
    }
    
    fmt.Printf("Valid user: %+v\n", user)
}

๐Ÿ“Š ๋ฒค์น˜๋งˆํ‚น๊ณผ ์„ฑ๋Šฅ ์ธก์ •

์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ํ•˜๋ ค๋ฉด ๋จผ์ € ์ธก์ •๋ถ€ํ„ฐ ํ•ด์•ผ ํ•ด! Go๋Š” ํ›Œ๋ฅญํ•œ ๋ฒค์น˜๋งˆํ‚น ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด. ์‹ค์ œ๋กœ JSON๊ณผ XML์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด๋ณด์ž. ๐Ÿ“ˆ

package main

import (
    "encoding/json"
    "encoding/xml"
    "testing"
)

type BenchData struct {
    ID          int      `json:"id" xml:"id"`
    Name        string   `json:"name" xml:"name"`
    Description string   `json:"description" xml:"description"`
    Tags        []string `json:"tags" xml:"tags>tag"`
    Active      bool     `json:"active" xml:"active"`
}

var testData = BenchData{
    ID:          12345,
    Name:        "Test Product",
    Description: "This is a test product with some description",
    Tags:        []string{"tag1", "tag2", "tag3", "tag4", "tag5"},
    Active:      true,
}

// JSON Marshal ๋ฒค์น˜๋งˆํฌ
func BenchmarkJSONMarshal(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _, err := json.Marshal(testData)
        if err != nil {
            b.Fatal(err)
        }
    }
}

// JSON Unmarshal ๋ฒค์น˜๋งˆํฌ
func BenchmarkJSONUnmarshal(b *testing.B) {
    data, _ := json.Marshal(testData)
    b.ResetTimer()
    
    for i := 0; i < b.N; i++ {
        var result BenchData
        err := json.Unmarshal(data, &result)
        if err != nil {
            b.Fatal(err)
        }
    }
}

// XML Marshal ๋ฒค์น˜๋งˆํฌ
func BenchmarkXMLMarshal(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _, err := xml.Marshal(testData)
        if err != nil {
            b.Fatal(err)
        }
    }
}

// XML Unmarshal ๋ฒค์น˜๋งˆํฌ
func BenchmarkXMLUnmarshal(b *testing.B) {
    data, _ := xml.Marshal(testData)
    b.ResetTimer()
    
    for i := 0; i < b.N; i++ {
        var result BenchData
        err := xml.Unmarshal(data, &result)
        if err != nil {
            b.Fatal(err)
        }
    }
}

// ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ธก์ •
func BenchmarkJSONMarshalAllocs(b *testing.B) {
    b.ReportAllocs()
    
    for i := 0; i < b.N; i++ {
        _, err := json.Marshal(testData)
        if err != nil {
            b.Fatal(err)
        }
    }
}

๋ฒค์น˜๋งˆํฌ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ์ด๋ ‡๊ฒŒ ์ž…๋ ฅํ•ด:

# ๊ธฐ๋ณธ ๋ฒค์น˜๋งˆํฌ ์‹คํ–‰
go test -bench=.

# ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์ •๋ณด ํฌํ•จ
go test -bench=. -benchmem

# ํŠน์ • ๋ฒค์น˜๋งˆํฌ๋งŒ ์‹คํ–‰
go test -bench=BenchmarkJSON

# CPU ํ”„๋กœํŒŒ์ผ ์ƒ์„ฑ
go test -bench=. -cpuprofile=cpu.prof

# ๋ฉ”๋ชจ๋ฆฌ ํ”„๋กœํŒŒ์ผ ์ƒ์„ฑ
go test -bench=. -memprofile=mem.prof
๐Ÿ“Š ์ผ๋ฐ˜์ ์ธ ์„ฑ๋Šฅ ๋น„๊ต ๊ฒฐ๊ณผ ์‹ค์ œ ๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅด์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ:

JSON
โ€ข Marshal: ~1000-2000 ns/op
โ€ข Unmarshal: ~2000-3000 ns/op
โ€ข ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น: ์ ์Œ

XML
โ€ข Marshal: ~3000-5000 ns/op
โ€ข Unmarshal: ~5000-8000 ns/op
โ€ข ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น: ๋งŽ์Œ

JSON์ด ์ผ๋ฐ˜์ ์œผ๋กœ 2-3๋ฐฐ ๋น ๋ฅด๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋„ ์ ๊ฒŒ ์‚ฌ์šฉํ•ด! ํ•˜์ง€๋งŒ XML์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋„ ์žˆ์œผ๋‹ˆ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•˜์ž. ๐Ÿ˜Š

๐ŸŽฏ ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ์ž

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ชจ๋‘ ํ™œ์šฉํ•ด์„œ ์‹ค์šฉ์ ์ธ ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ์ž๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! JSON๊ณผ XML์„ ๋ชจ๋‘ ์ง€์›ํ•˜๊ณ , ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค๋ฅธ ์„ค์ •์„ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์ด์•ผ. ๐Ÿ› ๏ธ

package main

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
    "os"
    "path/filepath"
    "strings"
)

// ์„ค์ • ๊ตฌ์กฐ์ฒด
type AppConfig struct {
    XMLName xml.Name `xml:"config" json:"-"`
    
    App struct {
        Name        string `json:"name" xml:"name"`
        Version     string `json:"version" xml:"version"`
        Environment string `json:"environment" xml:"environment"`
        Debug       bool   `json:"debug" xml:"debug"`
    } `json:"app" xml:"app"`
    
    Server struct {
        Host string `json:"host" xml:"host"`
        Port int    `json:"port" xml:"port"`
        TLS  bool   `json:"tls" xml:"tls"`
    } `json:"server" xml:"server"`
    
    Database struct {
        Driver   string `json:"driver" xml:"driver"`
        Host     string `json:"host" xml:"host"`
        Port     int    `json:"port" xml:"port"`
        Name     string `json:"name" xml:"name"`
        Username string `json:"username" xml:"username"`
        Password string `json:"-" xml:"-"` // ๋ณด์•ˆ์ƒ ์ง๋ ฌํ™” ์ œ์™ธ
        MaxConns int    `json:"max_connections" xml:"max_connections"`
    } `json:"database" xml:"database"`
    
    Logging struct {
        Level  string `json:"level" xml:"level"`
        Format string `json:"format" xml:"format"`
        Output string `json:"output" xml:"output"`
    } `json:"logging" xml:"logging"`
}

// ์„ค์ • ๊ด€๋ฆฌ์ž
type ConfigManager struct {
    config      *AppConfig
    configPath  string
    environment string
}

func NewConfigManager(environment string) *ConfigManager {
    return &ConfigManager{
        environment: environment,
    }
}

// ์„ค์ • ํŒŒ์ผ ๋กœ๋“œ
func (cm *ConfigManager) Load(basePath string) error {
    // ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ํŒŒ์ผ ๊ฒฝ๋กœ ๊ฒฐ์ •
    // ์˜ˆ: config.json, config.dev.json, config.prod.json
    paths := []string{
        filepath.Join(basePath, fmt.Sprintf("config.%s.json", cm.environment)),
        filepath.Join(basePath, "config.json"),
        filepath.Join(basePath, fmt.Sprintf("config.%s.xml", cm.environment)),
        filepath.Join(basePath, "config.xml"),
    }
    
    for _, path := range paths {
        if _, err := os.Stat(path); err == nil {
            cm.configPath = path
            return cm.loadFromFile(path)
        }
    }
    
    return fmt.Errorf("no config file found in %s", basePath)
}

func (cm *ConfigManager) loadFromFile(path string) error {
    data, err := os.ReadFile(path)
    if err != nil {
        return fmt.Errorf("failed to read config file: %w", err)
    }
    
    cm.config = &AppConfig{}
    
    // ํŒŒ์ผ ํ™•์žฅ์ž๋กœ ํฌ๋งท ํŒ๋‹จ
    ext := strings.ToLower(filepath.Ext(path))
    switch ext {
    case ".json":
        if err := json.Unmarshal(data, cm.config); err != nil {
            return fmt.Errorf("failed to parse JSON config: %w", err)
        }
    case ".xml":
        if err := xml.Unmarshal(data, cm.config); err != nil {
            return fmt.Errorf("failed to parse XML config: %w", err)
        }
    default:
        return fmt.Errorf("unsupported config format: %s", ext)
    }
    
    // ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œ
    cm.applyEnvironmentOverrides()
    
    // ๊ฒ€์ฆ
    if err := cm.validate(); err != nil {
        return fmt.Errorf("config validation failed: %w", err)
    }
    
    return nil
}

// ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ • ์˜ค๋ฒ„๋ผ์ด๋“œ
func (cm *ConfigManager) applyEnvironmentOverrides() {
    if port := os.Getenv("SERVER_PORT"); port != "" {
        fmt.Sscanf(port, "%d", &cm.config.Server.Port)
    }
    
    if dbHost := os.Getenv("DB_HOST"); dbHost != "" {
        cm.config.Database.Host = dbHost
    }
    
    if dbPass := os.Getenv("DB_PASSWORD"); dbPass != "" {
        cm.config.Database.Password = dbPass
    }
    
    if debug := os.Getenv("DEBUG"); debug == "true" {
        cm.config.App.Debug = true
    }
}

// ์„ค์ • ๊ฒ€์ฆ
func (cm *ConfigManager) validate() error {
    if cm.config.App.Name == "" {
        return fmt.Errorf("app name is required")
    }
    
    if cm.config.Server.Port < 1 || cm.config.Server.Port > 65535 {
        return fmt.Errorf("invalid server port: %d", cm.config.Server.Port)
    }
    
    if cm.config.Database.MaxConns < 1 {
        return fmt.Errorf("database max connections must be at least 1")
    }
    
    validLogLevels := map[string]bool{
        "debug": true, "info": true, "warn": true, "error": true,
    }
    if !validLogLevels[strings.ToLower(cm.config.Logging.Level)] {
        return fmt.Errorf("invalid log level: %s", cm.config.Logging.Level)
    }
    
    return nil
}

// ์„ค์ • ์ €์žฅ
func (cm *ConfigManager) Save(path string, format string) error {
    var data []byte
    var err error
    
    switch strings.ToLower(format) {
    case "json":
        data, err = json.MarshalIndent(cm.config, "", "  ")
    case "xml":
        data, err = xml.MarshalIndent(cm.config, "", "  ")
        if err == nil {
            data = append([]byte(xml.Header), data...)
        }
    default:
        return fmt.Errorf("unsupported format: %s", format)
    }
    
    if err != nil {
        return fmt.Errorf("failed to marshal config: %w", err)
    }
    
    if err := os.WriteFile(path, data, 0644); err != nil {
        return fmt.Errorf("failed to write config file: %w", err)
    }
    
    return nil
}

// Getter ๋ฉ”์„œ๋“œ๋“ค
func (cm *ConfigManager) Get() *AppConfig {
    return cm.config
}

func (cm *ConfigManager) GetServerAddress() string {
    protocol := "http"
    if cm.config.Server.TLS {
        protocol = "https"
    }
    return fmt.Sprintf("%s://%s:%d", protocol, cm.config.Server.Host, cm.config.Server.Port)
}

func (cm *ConfigManager) GetDatabaseDSN() string {
    return fmt.Sprintf("%s://%s:%s@%s:%d/%s",
        cm.config.Database.Driver,
        cm.config.Database.Username,
        cm.config.Database.Password,
        cm.config.Database.Host,
        cm.config.Database.Port,
        cm.config.Database.Name,
    )
}

// ์„ค์ • ์ •๋ณด ์ถœ๋ ฅ (๋ฏผ๊ฐ ์ •๋ณด ์ œ์™ธ)
func (cm *ConfigManager) Print() {
    fmt.Println("=== Application Configuration ===")
    fmt.Printf("App: %s v%s (%s)\n", 
        cm.config.App.Name, 
        cm.config.App.Version, 
        cm.config.App.Environment)
    fmt.Printf("Server: %s\n", cm.GetServerAddress())
    fmt.Printf("Database: %s@%s:%d/%s\n",
        cm.config.Database.Driver,
        cm.config.Database.Host,
        cm.config.Database.Port,
        cm.config.Database.Name)
    fmt.Printf("Logging: %s (%s)\n", 
        cm.config.Logging.Level, 
        cm.config.Logging.Format)
    fmt.Println("================================")
}

func main() {
    // ํ™˜๊ฒฝ ๊ฒฐ์ • (๊ธฐ๋ณธ๊ฐ’: development)
    env := os.Getenv("APP_ENV")
    if env == "" {
        env = "development"
    }
    
    // ์„ค์ • ๊ด€๋ฆฌ์ž ์ƒ์„ฑ
    configMgr := NewConfigManager(env)
    
    // ์„ค์ • ๋กœ๋“œ
    if err := configMgr.Load("./config"); err != nil {
        fmt.Println("Error loading config:", err)
        
        // ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ
        fmt.Println("Creating default configuration...")
        configMgr.config = createDefaultConfig(env)
        
        // JSON์œผ๋กœ ์ €์žฅ
        if err := configMgr.Save("./config/config.json", "json"); err != nil {
            fmt.Println("Error saving config:", err)
            return
        }
        fmt.Println("Default config saved to ./config/config.json")
    }
    
    // ์„ค์ • ์ •๋ณด ์ถœ๋ ฅ
    configMgr.Print()
    
    // ์„ค์ • ์‚ฌ์šฉ ์˜ˆ์ œ
    config := configMgr.Get()
    if config.App.Debug {
        fmt.Println("\n[DEBUG MODE ENABLED]")
        fmt.Printf("Database DSN: %s\n", configMgr.GetDatabaseDSN())
    }
}

// ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ
func createDefaultConfig(env string) *AppConfig {
    config := &AppConfig{}
    
    config.App.Name = "MyApp"
    config.App.Version = "1.0.0"
    config.App.Environment = env
    config.App.Debug = (env == "development")
    
    config.Server.Host = "localhost"
    config.Server.Port = 8080
    config.Server.TLS = false
    
    config.Database.Driver = "postgres"
    config.Database.Host = "localhost"
    config.Database.Port = 5432
    config.Database.Name = "myapp"
    config.Database.Username = "user"
    config.Database.Password = ""
    config.Database.MaxConns = 10
    
    config.Logging.Level = "info"
    config.Logging.Format = "json"
    config.Logging.Output = "stdout"
    
    return config
}
๐ŸŽ“ ์„ค์ • ๊ด€๋ฆฌ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค 1. ํ™˜๊ฒฝ๋ณ„ ๋ถ„๋ฆฌ
โ€ข development, staging, production ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค๋ฅธ ์„ค์ •
โ€ข ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๋ฏผ๊ฐํ•œ ์ •๋ณด ๊ด€๋ฆฌ

2. ๊ธฐ๋ณธ๊ฐ’ ์ œ๊ณต
โ€ข ์„ค์ • ํŒŒ์ผ์ด ์—†์–ด๋„ ๋™์ž‘ํ•˜๋„๋ก
โ€ข ํ•ฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •

3. ๊ฒ€์ฆ ๋กœ์ง
โ€ข ํ•„์ˆ˜ ํ•„๋“œ ํ™•์ธ
โ€ข ๊ฐ’์˜ ๋ฒ”์œ„ ๊ฒ€์ฆ
โ€ข ์˜์กด์„ฑ ์ฒดํฌ

4. ๋ฌธ์„œํ™”
โ€ข ๊ฐ ์„ค์ • ํ•ญ๋ชฉ์˜ ์˜๋ฏธ ๋ช…ํ™•ํžˆ
โ€ข ์˜ˆ์ œ ์„ค์ • ํŒŒ์ผ ์ œ๊ณต
โ€ข ๋ณ€๊ฒฝ ์ด๋ ฅ ๊ด€๋ฆฌ

๐ŸŒ ์‹ค์ „ ์˜ˆ์ œ: ๋‹ค๊ตญ์–ด ์ง€์› ์‹œ์Šคํ…œ

JSON์„ ํ™œ์šฉํ•œ ๋˜ ๋‹ค๋ฅธ ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋กœ ๋‹ค๊ตญ์–ด ์ง€์› ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด๋ณด์ž! ์ด๊ฑด ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ ์ •๋ง ํ•„์ˆ˜์ ์ด์•ผ. ๐ŸŒ

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "path/filepath"
    "strings"
)

// ๋ฒˆ์—ญ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
type Translation map[string]interface{}

// ๋‹ค๊ตญ์–ด ๊ด€๋ฆฌ์ž
type I18nManager struct {
    defaultLang  string
    currentLang  string
    translations map[string]Translation
    fallback     Translation
}

func NewI18nManager(defaultLang string) *I18nManager {
    return &I18nManager{
        defaultLang:  defaultLang,
        currentLang:  defaultLang,
        translations: make(map[string]Translation),
    }
}

// ์–ธ์–ด ํŒŒ์ผ ๋กœ๋“œ
func (i18n *I18nManager) LoadLanguage(lang string, path string) error {
    data, err := os.ReadFile(path)
    if err != nil {
        return fmt.Errorf("failed to read language file: %w", err)
    }
    
    var translation Translation
    if err := json.Unmarshal(data, &translation); err != nil {
        return fmt.Errorf("failed to parse language file: %w", err)
    }
    
    i18n.translations[lang] = translation
    
    // ๊ธฐ๋ณธ ์–ธ์–ด๋ฉด fallback์œผ๋กœ ์„ค์ •
    if lang == i18n.defaultLang {
        i18n.fallback = translation
    }
    
    return nil
}

// ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ๋ชจ๋“  ์–ธ์–ด ํŒŒ์ผ ๋กœ๋“œ
func (i18n *I18nManager) LoadDirectory(dir string) error {
    files, err := filepath.Glob(filepath.Join(dir, "*.json"))
    if err != nil {
        return err
    }
    
    for _, file := range files {
        // ํŒŒ์ผ๋ช…์—์„œ ์–ธ์–ด ์ฝ”๋“œ ์ถ”์ถœ (์˜ˆ: en.json -> en)
        lang := strings.TrimSuffix(filepath.Base(file), ".json")
        
        if err := i18n.LoadLanguage(lang, file); err != nil {
            fmt.Printf("Warning: failed to load %s: %v\n", file, err)
            continue
        }
        
        fmt.Printf("Loaded language: %s\n", lang)
    }
    
    if len(i18n.translations) == 0 {
        return fmt.Errorf("no language files loaded")
    }
    
    return nil
}

// ํ˜„์žฌ ์–ธ์–ด ์„ค์ •
func (i18n *I18nManager) SetLanguage(lang string) error {
    if _, exists := i18n.translations[lang]; !exists {
        return fmt.Errorf("language not loaded: %s", lang)
    }
    
    i18n.currentLang = lang
    return nil
}

// ๋ฒˆ์—ญ ๊ฐ€์ ธ์˜ค๊ธฐ (์ค‘์ฒฉ ํ‚ค ์ง€์›)
func (i18n *I18nManager) Get(key string, args ...interface{}) string {
    // ํ˜„์žฌ ์–ธ์–ด์—์„œ ์ฐพ๊ธฐ
    if value := i18n.getValue(i18n.translations[i18n.currentLang], key); value != "" {
        return i18n.format(value, args...)
    }
    
    // fallback ์–ธ์–ด์—์„œ ์ฐพ๊ธฐ
    if value := i18n.getValue(i18n.fallback, key); value != "" {
        return i18n.format(value, args...)
    }
    
    // ๋ชป ์ฐพ์œผ๋ฉด ํ‚ค ์ž์ฒด ๋ฐ˜ํ™˜
    return key
}

// ์ค‘์ฒฉ๋œ ํ‚ค์—์„œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ (์˜ˆ: "user.profile.name")
func (i18n *I18nManager) getValue(translation Translation, key string) string {
    parts := strings.Split(key, ".")
    var current interface{} = translation
    
    for _, part := range parts {
        switch v := current.(type) {
        case map[string]interface{}:
            current = v[part]
        case Translation:
            current = v[part]
        default:
            return ""
        }
        
        if current == nil {
            return ""
        }
    }
    
    if str, ok := current.(string); ok {
        return str
    }
    
    return ""
}

// ํฌ๋งทํŒ… (๊ฐ„๋‹จํ•œ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์น˜ํ™˜)
func (i18n *I18nManager) format(template string, args ...interface{}) string {
    result := template
    for i, arg := range args {
        placeholder := fmt.Sprintf("{%d}", i)
        result = strings.ReplaceAll(result, placeholder, fmt.Sprint(arg))
    }
    return result
}

// ๋ณต์ˆ˜ํ˜• ์ฒ˜๋ฆฌ
func (i18n *I18nManager) Plural(key string, count int, args ...interface{}) string {
    var pluralKey string
    
    if count == 1 {
        pluralKey = key + ".one"
    } else {
        pluralKey = key + ".other"
    }
    
    allArgs := append([]interface{}{count}, args...)
    return i18n.Get(pluralKey, allArgs...)
}

// ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์–ธ์–ด ๋ชฉ๋ก
func (i18n *I18nManager) AvailableLanguages() []string {
    langs := make([]string, 0, len(i18n.translations))
    for lang := range i18n.translations {
        langs = append(langs, lang)
    }
    return langs
}

// ํ˜„์žฌ ์–ธ์–ด
func (i18n *I18nManager) CurrentLanguage() string {
    return i18n.currentLang
}

func main() {
    // ๋‹ค๊ตญ์–ด ๊ด€๋ฆฌ์ž ์ƒ์„ฑ
    i18n := NewI18nManager("ko")
    
    // ์–ธ์–ด ํŒŒ์ผ๋“ค ๋กœ๋“œ
    if err := i18n.LoadDirectory("./locales"); err != nil {
        fmt.Println("Error loading languages:", err)
        
        // ํ…Œ์ŠคํŠธ์šฉ ์–ธ์–ด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
        createSampleLanguageFiles()
        
        // ๋‹ค์‹œ ๋กœ๋“œ
        if err := i18n.LoadDirectory("./locales"); err != nil {
            fmt.Println("Error:", err)
            return
        }
    }
    
    fmt.Println("Available languages:", i18n.AvailableLanguages())
    fmt.Println()
    
    // ํ•œ๊ตญ์–ด๋กœ ์ถœ๋ ฅ
    i18n.SetLanguage("ko")
    fmt.Println("=== ํ•œ๊ตญ์–ด ===")
    fmt.Println(i18n.Get("welcome"))
    fmt.Println(i18n.Get("greeting", "๊น€์ฒ ์ˆ˜"))
    fmt.Println(i18n.Get("user.profile.name"))
    fmt.Println(i18n.Plural("items", 1))
    fmt.Println(i18n.Plural("items", 5))
    fmt.Println()
    
    // ์˜์–ด๋กœ ์ถœ๋ ฅ
    i18n.SetLanguage("en")
    fmt.Println("=== English ===")
    fmt.Println(i18n.Get("welcome"))
    fmt.Println(i18n.Get("greeting", "John"))
    fmt.Println(i18n.Get("user.profile.name"))
    fmt.Println(i18n.Plural("items", 1))
    fmt.Println(i18n.Plural("items", 5))
    fmt.Println()
    
    // ์ผ๋ณธ์–ด๋กœ ์ถœ๋ ฅ
    i18n.SetLanguage("ja")
    fmt.Println("=== ๆ—ฅๆœฌ่ชž ===")
    fmt.Println(i18n.Get("welcome"))
    fmt.Println(i18n.Get("greeting", "็”ฐไธญ"))
    fmt.Println(i18n.Get("user.profile.name"))
    fmt.Println(i18n.Plural("items", 1))
    fmt.Println(i18n.Plural("items", 5))
}

// ์ƒ˜ํ”Œ ์–ธ์–ด ํŒŒ์ผ ์ƒ์„ฑ
func createSampleLanguageFiles() {
    os.MkdirAll("./locales", 0755)
    
    // ํ•œ๊ตญ์–ด
    ko := map[string]interface{}{
        "welcome": "์žฌ๋Šฅ๋„ท์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!",
        "greeting": "์•ˆ๋…•ํ•˜์„ธ์š”, {0}๋‹˜!",
        "user": map[string]interface{}{
            "profile": map[string]interface{}{
                "name": "์ด๋ฆ„",
                "email": "์ด๋ฉ”์ผ",
                "phone": "์ „ํ™”๋ฒˆํ˜ธ",
            },
        },
        "items": map[string]interface{}{
            "one": "{0}๊ฐœ์˜ ํ•ญ๋ชฉ",
            "other": "{0}๊ฐœ์˜ ํ•ญ๋ชฉ๋“ค",
        },
    }
    
    // ์˜์–ด
    en := map[string]interface{}{
        "welcome": "Welcome to JaeNung.net!",
        "greeting": "Hello, {0}!",
        "user": map[string]interface{}{
            "profile": map[string]interface{}{
                "name": "Name",
                "email": "Email",
                "phone": "Phone",
            },
        },
        "items": map[string]interface{}{
            "one": "{0} item",
            "other": "{0} items",
        },
    }
    
    // ์ผ๋ณธ์–ด
    ja := map[string]interface{}{
        "welcome": "ๆ‰่ƒฝใƒใƒƒใƒˆใธใ‚ˆใ†ใ“ใ!",
        "greeting": "ใ“ใ‚“ใซใกใฏใ€{0}ใ•ใ‚“!",
        "user": map[string]interface{}{
            "profile": map[string]interface{}{
                "name": "ๅๅ‰",
                "email": "ใƒกใƒผใƒซ",
                "phone": "้›ป่ฉฑ",
            },
        },
        "items": map[string]interface{}{
            "one": "{0}ๅ€‹ใฎใ‚ขใ‚คใƒ†ใƒ ",
            "other": "{0}ๅ€‹ใฎใ‚ขใ‚คใƒ†ใƒ ",
        },
    }
    
    // ํŒŒ์ผ๋กœ ์ €์žฅ
    saveLanguageFile("./locales/ko.json", ko)
    saveLanguageFile("./locales/en.json", en)
    saveLanguageFile("./locales/ja.json", ja)
    
    fmt.Println("Sample language files created in ./locales/")
}

func saveLanguageFile(path string, data interface{}) {
    jsonData, _ := json.MarshalIndent(data, "", "  ")
    os.WriteFile(path, jsonData, 0644)
}
๋‹ค๊ตญ์–ด ์ง€์› ์‹œ์Šคํ…œ ๊ตฌ์กฐ I18n Manager ๋‹ค๊ตญ์–ด ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ํ•œ๊ตญ์–ด (ko.json) "welcome": "์žฌ๋Šฅ๋„ท์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!" "greeting": "์•ˆ๋…•ํ•˜์„ธ์š”, {0}๋‹˜!" ๐Ÿ‡ฐ๐Ÿ‡ท ์˜์–ด (en.json) "welcome": "Welcome to JaeNung.net!" "greeting": "Hello, {0}!" ๐Ÿ‡บ๐Ÿ‡ธ ์ผ๋ณธ์–ด (ja.json) "welcome": "ๆ‰่ƒฝใƒใƒƒใƒˆใธ ใ‚ˆใ†ใ“ใ!" "greeting": "ใ“ใ‚“ใซใกใฏใ€{0}ใ•ใ‚“!" ๐Ÿ‡ฏ๐Ÿ‡ต JSON ํŒŒ์ผ๋กœ ์–ธ์–ด๋ณ„ ๋ฒˆ์—ญ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๋™์ ์œผ๋กœ ๋กœ๋“œ

๐Ÿ”ง ๋””๋ฒ„๊น…๊ณผ ๋ฌธ์ œ ํ•ด๊ฒฐ

JSON๊ณผ XML ์ฒ˜๋ฆฌ ์ค‘์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊นŒ? ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”

โš ๏ธ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค 1. "json: cannot unmarshal ... into Go value of type ..."
โ€ข ์›์ธ: JSON ํƒ€์ž…๊ณผ Go ํƒ€์ž…์ด ๋งž์ง€ ์•Š์Œ
โ€ข ํ•ด๊ฒฐ: ๊ตฌ์กฐ์ฒด ํ•„๋“œ ํƒ€์ž… ํ™•์ธ, interface{} ์‚ฌ์šฉ ๊ณ ๋ ค

2. "invalid character ... looking for beginning of value"
โ€ข ์›์ธ: ์ž˜๋ชป๋œ JSON ํ˜•์‹
โ€ข ํ•ด๊ฒฐ: JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ, ํŠน์ˆ˜๋ฌธ์ž ์ด์Šค์ผ€์ดํ”„

3. "EOF" ์—๋Ÿฌ
โ€ข ์›์ธ: ๋นˆ ๋ฐ์ดํ„ฐ ๋˜๋Š” ๋ถˆ์™„์ „ํ•œ JSON
โ€ข ํ•ด๊ฒฐ: ๋ฐ์ดํ„ฐ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ, ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๊ฐ•ํ™”

4. ํ•„๋“œ๊ฐ€ ๋ฌด์‹œ๋จ
โ€ข ์›์ธ: ๋Œ€์†Œ๋ฌธ์ž ๋ถˆ์ผ์น˜, ํƒœ๊ทธ ์˜คํƒ€, unexported ํ•„๋“œ
โ€ข ํ•ด๊ฒฐ: ํ•„๋“œ๋ช… ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘, ํƒœ๊ทธ ํ™•์ธ
import (
    "encoding/json"
    "fmt"
    "strings"
)

// ๋””๋ฒ„๊น… ํ—ฌํผ ํ•จ์ˆ˜๋“ค

// JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
func ValidateJSON(data []byte) error {
    if !json.Valid(data) {
        // ์–ด๋””๊ฐ€ ์ž˜๋ชป๋๋Š”์ง€ ์ฐพ๊ธฐ
        var v interface{}
        err := json.Unmarshal(data, &v)
        return fmt.Errorf("invalid JSON: %w", err)
    }
    return nil
}

// JSON ์˜ˆ์˜๊ฒŒ ์ถœ๋ ฅ
func PrettyPrintJSON(data []byte) {
    var obj interface{}
    if err := json.Unmarshal(data, &obj); err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    pretty, err := json.MarshalIndent(obj, "", "  ")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    
    fmt.Println(string(pretty))
}

// ๊ตฌ์กฐ์ฒด์™€ JSON ๋น„๊ต (๋””๋ฒ„๊น…์šฉ)
func CompareStructAndJSON(v interface{}, jsonData []byte) {
    // ๊ตฌ์กฐ์ฒด๋ฅผ JSON์œผ๋กœ
    structJSON, _ := json.MarshalIndent(v, "", "  ")
    
    // ์ž…๋ ฅ JSON ์ •๋ฆฌ
    var obj interface{}
    json.Unmarshal(jsonData, &obj)
    inputJSON, _ := json.MarshalIndent(obj, "", "  ")
    
    fmt.Println("=== Struct as JSON ===")
    fmt.Println(string(structJSON))
    fmt.Println("\n=== Input JSON ===")
    fmt.Println(string(inputJSON))
    
    // ์ฐจ์ด์  ์ฐพ๊ธฐ (๊ฐ„๋‹จํ•œ ๋ฒ„์ „)
    structLines := strings.Split(string(structJSON), "\n")
    inputLines := strings.Split(string(inputJSON), "\n")
    
    fmt.Println("\n=== Differences ===")
    maxLen := len(structLines)
    if len(inputLines) > maxLen {
        maxLen = len(inputLines)
    }
    
    for i := 0; i < maxLen; i++ {
        var structLine, inputLine string
        if i < len(structLines) {
            structLine = structLines[i]
        }
        if i < len(inputLines) {
            inputLine = inputLines[i]
        }
        
        if structLine != inputLine {
            fmt.Printf("Line %d differs:\n", i+1)
            fmt.Printf("  Struct: %s\n", structLine)
            fmt.Printf("  Input:  %s\n", inputLine)
        }
    }
}

// ํƒ€์ž… ๋ถˆ์ผ์น˜ ์ฐพ๊ธฐ
func FindTypeMismatches(jsonData []byte, targetType interface{}) []string {
    var mismatches []string
    
    // JSON์„ map์œผ๋กœ ํŒŒ์‹ฑ
    var jsonMap map[string]interface{}
    if err := json.Unmarshal(jsonData, &jsonMap); err != nil {
        return []string{fmt.Sprintf("Failed to parse JSON: %v", err)}
    }
    
    // ๊ตฌ์กฐ์ฒด ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ (๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ)
    // ์‹ค์ œ ๊ตฌํ˜„์€ reflect ํŒจํ‚ค์ง€ ํ•„์š”
    
    return mismatches
}

// ์•ˆ์ „ํ•œ ์—ญ์ง๋ ฌํ™” (์ƒ์„ธํ•œ ์—๋Ÿฌ ์ •๋ณด)
func SafeUnmarshalWithDetails(data []byte, v interface{}) error {
    // 1. JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    if err := ValidateJSON(data); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }
    
    // 2. ์—ญ์ง๋ ฌํ™” ์‹œ๋„
    if err := json.Unmarshal(data, v); err != nil {
        // ์—๋Ÿฌ ํƒ€์ž…๋ณ„ ์ƒ์„ธ ์ •๋ณด
        switch e := err.(type) {
        case *json.UnmarshalTypeError:
            return fmt.Errorf(
                "type mismatch at field '%s': expected %s but got %s (offset %d)",
                e.Field, e.Type, e.Value, e.Offset,
            )
        case *json.SyntaxError:
            return fmt.Errorf(
                "syntax error at offset %d: %v",
                e.Offset, e,
            )
        default:
            return fmt.Errorf("unmarshal error: %w", err)
        }
    }
    
    return nil
}

func main() {
    // ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” JSON
    badJSON := []byte(`{
        "name": "Test",
        "age": "not a number",
        "active": true
    }`)
    
    type User struct {
        Name   string `json:"name"`
        Age    int    `json:"age"`
        Active bool   `json:"active"`
    }
    
    var user User
    
    // ์ƒ์„ธํ•œ ์—๋Ÿฌ ์ •๋ณด์™€ ํ•จ๊ป˜ ์—ญ์ง๋ ฌํ™”
    if err := SafeUnmarshalWithDetails(badJSON, &user); err != nil {
        fmt.Println("Error:", err)
        // ์ถœ๋ ฅ: type mismatch at field 'age': expected int but got string
    }
    
    // JSON ๋น„๊ต
    goodJSON := []byte(`{"name":"Test","age":25,"active":true}`)
    json.Unmarshal(goodJSON, &user)
    CompareStructAndJSON(user, badJSON)
}

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿ˜Š ์šฐ๋ฆฌ๋Š” Go ์–ธ์–ด์—์„œ JSON๊ณผ XML์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ง ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„๋ดค์–ด. Marshal๊ณผ Unmarshal์˜ ๊ธฐ๋ณธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ, ์ปค์Šคํ…€ ์ง๋ ฌํ™”, ์„ฑ๋Šฅ ์ตœ์ ํ™”, ์‹ค์ „ ํ”„๋กœ์ ํŠธ๊นŒ์ง€ ์ •๋ง ๋งŽ์€ ๋‚ด์šฉ์„ ๋ฐฐ์› ์–ด!

์ด์ œ ๋„ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด:

โœ… JSON๊ณผ XML ๋ฐ์ดํ„ฐ๋ฅผ ์ž์œ ์ž์žฌ๋กœ ๋‹ค๋ฃจ๊ธฐ
โœ… ๊ตฌ์กฐ์ฒด ํƒœ๊ทธ๋ฅผ ํ™œ์šฉํ•œ ์œ ์—ฐํ•œ ๋งคํ•‘
โœ… ์ปค์Šคํ…€ ๋งˆ์ƒฌ๋ง์œผ๋กœ ํŠน๋ณ„ํ•œ ์š”๊ตฌ์‚ฌํ•ญ ์ฒ˜๋ฆฌ
โœ… ์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•œ ํšจ์œจ์ ์ธ ์ฝ”๋“œ ์ž‘์„ฑ
โœ… ์‹ค์ „ ํ”„๋กœ์ ํŠธ์— ๋ฐ”๋กœ ์ ์šฉ ๊ฐ€๋Šฅํ•œ ํŒจํ„ด ํ™œ์šฉ
โœ… ๋ณด์•ˆ์„ ๊ณ ๋ คํ•œ ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

๋ฐ์ดํ„ฐ ์ง๋ ฌํ™”๋Š” ํ˜„๋Œ€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ๊ธฐ์ˆ ์ด์•ผ. API ํ†ต์‹ , ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ, ๋ฐ์ดํ„ฐ ์ €์žฅ ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ฑฐ๋“ . Go์˜ encoding ํŒจํ‚ค์ง€๋Š” ์ด๋Ÿฐ ์ž‘์—…์„ ์ •๋ง ์‰ฝ๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ค˜!

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋‹ค์–‘ํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋ฉด, ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์ด ์ •๋ง ์œ ์šฉํ•˜๊ฒŒ ์“ฐ์ผ ๊ฑฐ์•ผ. ์™ธ๋ถ€ API ์—ฐ๋™, ๋ฐ์ดํ„ฐ ๊ตํ™˜, ์„ค์ • ๊ด€๋ฆฌ ๋“ฑ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ๋“ค์ด๋‹ˆ๊นŒ! ๐Ÿ’ช

๊ณ„์† ์—ฐ์Šตํ•˜๊ณ , ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๋ฉด์„œ ๊ฒฝํ—˜์„ ์Œ“์•„๊ฐ€๊ธธ ๋ฐ”๋ผ! ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“ ์ง€ Go ์ปค๋ฎค๋‹ˆํ‹ฐ๋‚˜ ์žฌ๋Šฅ๋„ท์˜ ์ง€์‹์ธ์˜ ์ˆฒ์—์„œ ์งˆ๋ฌธํ•˜๊ณ  ๋‹ต์„ ์ฐพ์•„๋ด. ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์ž! ๐Ÿš€

Happy Coding! ์ฆ๊ฑฐ์šด Go ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋˜๊ธธ! ๐ŸŽ‰

๐ŸŽ“ ์ˆ˜๊ณ ํ–ˆ์–ด์š”! Go ๋งˆ์Šคํ„ฐ์˜ ๊ธธ์„ ํ–ฅํ•ด ๊ณ„์† ์ „์ง„! ๐Ÿš€
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.