Go 1.16 embed 패키지로 정적 파일 바이너리에 포함하기

문제 상황

사내 CLI 도구를 Go로 만들어 배포하는데, SQL 마이그레이션 파일과 설정 템플릿을 함께 배포해야 했다. 기존에는 바이너리와 별도로 assets/ 디렉토리를 같이 압축해서 전달했는데, 경로 문제로 이슈가 자주 발생했다.

embed 패키지

Go 1.16부터 표준 라이브러리에 embed 패키지가 추가되었다. //go:embed 디렉티브를 사용하면 컴파일 타임에 파일을 바이너리에 포함할 수 있다.

package main

import (
    "embed"
    "fmt"
    "io/fs"
)

//go:embed migrations/*.sql
var migrationsFS embed.FS

//go:embed templates
var templatesFS embed.FS

func main() {
    // 파일 읽기
    data, err := migrationsFS.ReadFile("migrations/001_init.sql")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    
    // 디렉토리 순회
    fs.WalkDir(templatesFS, ".", func(path string, d fs.DirEntry, err error) error {
        if !d.IsDir() {
            fmt.Println(path)
        }
        return nil
    })
}

적용 결과

  • 단일 바이너리로 배포 가능
  • 경로 관련 이슈 해결
  • CI/CD 파이프라인 단순화 (파일 복사 스크립트 제거)

주의사항

숨김 파일(.로 시작)은 명시적으로 지정해야 포함된다. 와일드카드만으로는 포함되지 않는다.

//go:embed templates/.env.example
//go:embed templates/*
var templatesFS embed.FS

바이너리 크기가 커질 수 있으니 포함할 파일은 최소화하는 게 좋다. 대용량 파일은 여전히 외부에서 관리하는 편이 낫다.