Ver3.0 ๐ฏ Go์ธ์ด๋ก JSON๊ณผ XML ์ ๋ณตํ๊ธฐ!

๐ฏ Go์ธ์ด๋ก JSON๊ณผ XML ์ ๋ณตํ๊ธฐ!
๋ฐ์ดํฐ ์ง๋ ฌํ์ ๋ชจ๋ ๊ฒ, encoding ํจํค์ง์ ํจ๊ป ์ฝ๊ณ ์ฌ๋ฏธ์๊ฒ ๋ฐฐ์๋ณด์ ๐
๐ ๋ฐ์ดํฐ ์ง๋ ฌํ, ์ ํ์ํ ๊ฑธ๊น?
์๋
! ์ค๋์ Go์ธ์ด์์ ์ ๋ง ์ค์ํ ์ฃผ์ ๋ฅผ ๋ค๋ค๋ณผ ๊ฑฐ์ผ. ๋ฐ๋ก JSON๊ณผ XML ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ ์ง๋ ฌํ์ ๋ํ ์ด์ผ๊ธฐ์ผ. ๐
๋จผ์ ๋ฐ์ดํฐ ์ง๋ ฌํ(Serialization)๊ฐ ๋ญ์ง๋ถํฐ ์์๋ณผ๊น? ์ฝ๊ฒ ๋งํ๋ฉด, ํ๋ก๊ทธ๋จ ์์์ ์ฌ์ฉํ๋ ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฅธ ์์คํ
์ด๋ ๋คํธ์ํฌ๋ฅผ ํตํด ์ ์กํ ์ ์๋ ํํ๋ก ๋ณํํ๋ ๊ณผ์ ์ด์ผ. ๋ง์น ํ๋ฐฐ๋ฅผ ๋ณด๋ผ ๋ ๋ฌผ๊ฑด์ ๋ฐ์ค์ ํฌ์ฅํ๋ ๊ฒ๊ณผ ๋น์ทํ์ง!
๋ฐ๋๋ก ์ญ์ง๋ ฌํ(Deserialization)๋ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ํ๋ก๊ทธ๋จ์์ ์ฌ์ฉํ ์ ์๋ ํํ๋ก ํ์ด๋ด๋ ๊ฑฐ์ผ. ํ๋ฐฐ ๋ฐ์ค๋ฅผ ์ด์ด์ ๋ฌผ๊ฑด์ ๊บผ๋ด๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ๐ฆ
๐ 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
}
๐ฏ 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๋ ๋ฌธ์์ด๋ก ๋ณํ๋จ
}
์๋ฅผ ๋ค์ด 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
}
}
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)
}
}
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 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
}
โข ํ์ผ์ด๋ ๋คํธ์ํฌ ์คํธ๋ฆผ ์ฒ๋ฆฌ
โข ํฐ ๋ฐ์ดํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ
โข ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ด ์ค์ํ ๋
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))
}
๐ 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:"-"๋ก ์ ์ธ
โข 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)
}
}
๐ก๏ธ ๋ณด์ ๊ณ ๋ ค์ฌํญ
๋ฐ์ดํฐ ์ง๋ ฌํ๋ฅผ ๋ค๋ฃฐ ๋๋ ๋ณด์๋ ์ ๋ง ์ค์ํด! ํนํ ์ธ๋ถ์์ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ง๋ ฌํํ ๋๋ ๋์ฑ ์กฐ์ฌํด์ผ ํด. ๋ช ๊ฐ์ง ์ค์ํ ๋ณด์ ํ์ ์๋ ค์ค๊ฒ. ๐
โข ์ญ์ง๋ ฌํ ์ ์ ๋ฐ์ดํฐ ํฌ๊ธฐ ํ์ธ
โข ์์์น ๋ชปํ ํ๋๋ ๊ฐ ๊ฑฐ๋ถ
โข ํ์ดํธ๋ฆฌ์คํธ ๋ฐฉ์์ผ๋ก ํ์ฉ๋ ๊ฐ๋ง ์ฒ๋ฆฌ
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
}
โข 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)
}
๐ง ๋๋ฒ๊น ๊ณผ ๋ฌธ์ ํด๊ฒฐ
JSON๊ณผ XML ์ฒ๋ฆฌ ์ค์ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ์ด๋ป๊ฒ ํด๊ฒฐํ ๊น? ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์์๋ณด์! ๐
โข ์์ธ: 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 ํ๋ก๊ทธ๋๋ฐ ๋๊ธธ! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.