---
sidebar_label: "직렬화"
title: "직렬화 - CyberGo env | 다중 형식 변환"
description: "CyberGo env 직렬화 가이드로 .env·JSON·YAML 간 Map·구조체 변환, Marshal/Unmarshal 함수군, Marshaler/Unmarshaler 인터페이스, DetectFormat 자동 감지와 env 태그·민감 필드 마스킹을 다룹니다."
sidebar_position: 2
---

# 직렬화

Marshal 및 Unmarshal 기능으로 환경 변수를 직렬화/역직렬화하며, `.env`, JSON, YAML 형식 변환을 지원합니다.

## 기본 직렬화

### Map 직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    data := map[string]string{
        "APP_NAME":    "my-app",
        "APP_VERSION": "1.0.0",
        "DEBUG":       "true",
    }

    // .env 형식으로 직렬화
    result, err := env.Marshal(data, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력:
    // APP_NAME=my-app
    // APP_VERSION=1.0.0
    // DEBUG=true
}
```

### JSON 형식

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    data := map[string]string{
        "HOST": "localhost",
        "PORT": "8080",
    }

    // JSON 으로 직렬화
    result, err := env.Marshal(data, env.FormatJSON)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력:
    // {
    //   "HOST": "localhost",
    //   "PORT": 8080
    // }
}
```

### YAML 형식

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    data := map[string]string{
        "DATABASE_HOST": "localhost",
        "DATABASE_PORT": "5432",
        "DATABASE_NAME": "myapp",
    }

    // YAML 로 직렬화
    result, err := env.Marshal(data, env.FormatYAML)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력:
    // DATABASE_HOST: localhost
    // DATABASE_NAME: myapp
    // DATABASE_PORT: 5432
}
```

## 구조체 직렬화

### 기본 직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

type Config struct {
    Host string `env:"HOST"`
    Port int64  `env:"PORT"`
    Debug bool  `env:"DEBUG"`
}

func main() {
    cfg := Config{
        Host:  "localhost",
        Port:  8080,
        Debug: true,
    }

    // 구조체를 .env 형식으로 직렬화
    result, err := env.Marshal(cfg, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력:
    // DEBUG=true
    // HOST=localhost
    // PORT=8080
}
```

### 중첩 구조체

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

type DatabaseConfig struct {
    Host string `env:"DB_HOST"`
    Port int64  `env:"DB_PORT"`
}

type AppConfig struct {
    Name     string         `env:"APP_NAME"`
    Database DatabaseConfig
}

func main() {
    cfg := AppConfig{
        Name: "my-app",
        Database: DatabaseConfig{
            Host: "localhost",
            Port: 5432,
        },
    }

    result, err := env.Marshal(cfg, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
}
```

### MarshalStruct 함수

구조체를 `map[string]string`으로 변환:

```go
func MarshalStruct(v any) (map[string]string, error)
```

**매개변수:**
- `v` - 구조체 포인터 또는 값

**반환값:**
- `map[string]string` - 환경 변수 매핑
- `error` - 직렬화 오류

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

type Config struct {
    Host string `env:"HOST"`
    Port int64  `env:"PORT"`
    Debug bool  `env:"DEBUG"`
}

func main() {
    cfg := Config{
        Host:  "localhost",
        Port:  8080,
        Debug: true,
    }

    // map 으로 변환
    data, err := env.MarshalStruct(cfg)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", data)
    // 출력: map[DEBUG:true HOST:localhost PORT:8080]

    // 파일로 내보내기에 사용 가능
    content, _ := env.Marshal(data, env.FormatEnv)
    fmt.Println(content)
}
```

## 역직렬화

### Map 역직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    // .env 형식 문자열
    data := `
HOST=localhost
PORT=8080
DEBUG=true
`

    // map 으로 역직렬화
    result, err := env.UnmarshalMap(data, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", result)
    // 출력: map[DEBUG:true HOST:localhost PORT:8080]
}
```

### JSON 역직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    jsonData := `{
        "API_KEY": "secret123",
        "API_URL": "https://api.example.com",
        "TIMEOUT": "30"
    }`

    result, err := env.UnmarshalMap(jsonData, env.FormatJSON)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", result)
}
```

### YAML 역직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    yamlData := `
DATABASE_HOST: localhost
DATABASE_PORT: "5432"
DATABASE_USER: postgres
`

    result, err := env.UnmarshalMap(yamlData, env.FormatYAML)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", result)
}
```

## 구조체 역직렬화

### Map 에서 역직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

type Config struct {
    Host string `env:"HOST"`
    Port int64  `env:"PORT"`
}

func main() {
    data := map[string]string{
        "HOST": "example.com",
        "PORT": "443",
    }

    var cfg Config
    err := env.UnmarshalInto(data, &cfg)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", cfg)
    // 출력: {Host:example.com Port:443}
}
```

### 문자열에서 역직렬화

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

type ServerConfig struct {
    Host    string `env:"SERVER_HOST"`
    Port    int64  `env:"SERVER_PORT"`
    Enabled bool   `env:"ENABLED"`
}

func main() {
    envData := `
SERVER_HOST=0.0.0.0
SERVER_PORT=8080
ENABLED=true
`

    var cfg ServerConfig
    err := env.UnmarshalStruct(envData, &cfg, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", cfg)
}
```

## 커스텀 직렬화

::: tip 두 가지 커스텀 인터페이스의 동작 범위
- **필드 수준**: 구조체 필드의 커스텀 인코딩/디코딩은 표준 라이브러리 `encoding.TextMarshaler` / `encoding.TextUnmarshaler`(`MarshalText()` / `UnmarshalText([]byte)`) 를 구현합니다. 구조체가 `env.Marshal`/`env.UnmarshalInto`로 처리될 때 필드 단위 로직이 이 두 인터페이스를 인식합니다.
- **최상위**: `env.Marshaler`(`MarshalEnv()`) 와 `env.Unmarshaler`(`UnmarshalEnv(map[string]string)`) 인터페이스는 **`env.Marshal`/`env.MarshalStruct`/`env.UnmarshalInto`에 직접 전달된 최상위 값에서만 동작**합니다. 해당 타입을 필드로 포함한 외부 구조체를 전달하면 호출되지 않습니다.
:::

### 필드 수준: encoding.TextMarshaler 구현

```go
package main

import (
    "fmt"
    "strings"

    "github.com/cybergodev/env"
)

type LogLevel string

// encoding.TextMarshaler 구현 — 구조체 필드로 직렬화 시 호출됨
func (l LogLevel) MarshalText() ([]byte, error) {
    return []byte(strings.ToUpper(string(l))), nil
}

type LogConfig struct {
    Level LogLevel `env:"LOG_LEVEL"`
}

func main() {
    cfg := LogConfig{
        Level: LogLevel("debug"),
    }

    result, err := env.Marshal(cfg, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력: LOG_LEVEL=DEBUG
}
```

### 필드 수준: encoding.TextUnmarshaler 구현

```go
package main

import (
    "fmt"

    "github.com/cybergodev/env"
)

type LogLevel string

// encoding.TextUnmarshaler 구현 — 구조체 필드로 역직렬화 시 호출됨
func (l *LogLevel) UnmarshalText(text []byte) error {
    switch string(text) {
    case "debug", "info", "warn", "error":
        *l = LogLevel(text)
        return nil
    default:
        return fmt.Errorf("invalid log level: %s", string(text))
    }
}

type LogConfig struct {
    Level LogLevel `env:"LOG_LEVEL"`
}

func main() {
    data := map[string]string{
        "LOG_LEVEL": "info",
    }

    var cfg LogConfig
    err := env.UnmarshalInto(data, &cfg)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Level: %s\n", cfg.Level)
    // 출력: Level: info
}
```

### 최상위: env.Marshaler / env.Unmarshaler 구현

어떤 타입의 값을 `env.Marshal` / `env.UnmarshalInto`에 **직접** 전달할 때 (외부 구조체의 필드가 아닌), `env.Marshaler` / `env.Unmarshaler` 인터페이스가 해당 최상위 값에서 동작합니다:

```go
package main

import (
    "fmt"

    "github.com/cybergodev/env"
)

// 최상위 타입이 env.Marshaler 를 직접 구현
type EnvBlob string

func (e EnvBlob) MarshalEnv() ([]byte, error) {
    // 커스텀 전체 직렬화 출력
    return []byte("APP_NAME=custom\nAPP_VERSION=2.0.0"), nil
}

func main() {
    // 최상위 값을 직접 직렬화 (외부 구조체의 필드가 아님)
    result, err := env.Marshal(EnvBlob(""), env.FormatEnv)
    if err != nil {
        panic(err)
    }

    fmt.Println(result)
    // 출력:
    // APP_NAME=custom
    // APP_VERSION=2.0.0
}
```

## 형식 감지

### 자동 형식 감지

```go
package main

import (
    "fmt"
    "github.com/cybergodev/env"
)

func main() {
    // 자동 형식 감지
    format := env.DetectFormat("config.json")
    fmt.Println(format.String()) // json

    format = env.DetectFormat("settings.yaml")
    fmt.Println(format.String()) // yaml

    format = env.DetectFormat(".env")
    fmt.Println(format.String()) // dotenv

    // FormatAuto 로 자동 감지
    data := `{"KEY": "value"}`
    result, _ := env.UnmarshalMap(data, env.FormatAuto)
    fmt.Println(result)
}
```

## 실용 시나리오

### 설정을 파일로 저장

```go
package main

import (
    "os"
    "github.com/cybergodev/env"
)

func main() {
    cfg := map[string]string{
        "HOST": "localhost",
        "PORT": "8080",
    }

    // 직렬화
    content, err := env.Marshal(cfg, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    // 파일에 쓰기
    err = os.WriteFile(".env", []byte(content), 0644)
    if err != nil {
        panic(err)
    }
}
```

### 현재 환경 내보내기

```go
package main

import (
    "fmt"
    "os"
    "github.com/cybergodev/env"
)

func main() {
    env.Load(".env")

    // 모든 환경 변수 가져오기
    all := env.All()

    // JSON 으로 내보내기
    content, err := env.Marshal(all, env.FormatJSON)
    if err != nil {
        panic(err)
    }

    fmt.Println(content)

    // 또는 파일에 쓰기
    os.WriteFile("env-export.json", []byte(content), 0644)
}
```

### 설정 마이그레이션

```go
package main

import (
    "fmt"
    "os"
    "github.com/cybergodev/env"
)

func main() {
    // JSON 설정 읽기
    jsonContent, _ := os.ReadFile("config.json")

    // JSON 파싱
    data, err := env.UnmarshalMap(string(jsonContent), env.FormatJSON)
    if err != nil {
        panic(err)
    }

    // .env 형식으로 변환
    envContent, err := env.Marshal(data, env.FormatEnv)
    if err != nil {
        panic(err)
    }

    // .env 파일로 저장
    os.WriteFile(".env", []byte(envContent), 0644)

    fmt.Println("Config migrated from JSON to .env")
}
```

## 관련 문서

- [패키지 함수](/ko/env/api-reference/functions) - Marshal, UnmarshalMap 등 함수 참조
- [다중 형식 구성](/ko/env/guides/multi-format) - 다중 형식 로딩 가이드
- [구조체 매핑](/ko/env/guides/struct-mapping) - 구조체 매핑 가이드
