Go 1.15에서 embed 패턴으로 정적 파일 관리하기

배경

회사에서 관리하던 내부 도구를 Node.js에서 Go로 마이그레이션했다. 단일 바이너리 배포가 목적이었는데, HTML 템플릿과 CSS 파일을 함께 번들링하는 방법이 필요했다.

go-bindata 사용

가장 많이 쓰이는 방법은 go-bindata였다.

go get -u github.com/go-bindata/go-bindata/...
go-bindata -o assets.go -pkg main static/...

생성된 assets.go에서 파일을 읽는다:

data, err := Asset("static/index.html")
if err != nil {
    log.Fatal(err)
}

문제는 빌드 과정이 복잡해진다는 점이다. CI/CD 파이프라인에 go-bindata 설치와 생성 스크립트를 추가해야 했다.

직접 구현

작은 프로젝트라 직접 구현도 고려했다.

var templates = map[string]string{
    "index": `<!DOCTYPE html>
<html>
<head><title>Tool</title></head>
<body>...</body>
</html>`,
}

func getTemplate(name string) string {
    return templates[name]
}

파일이 적을 때는 이게 더 명확했다. 하지만 템플릿이 늘어나면서 유지보수가 어려워졌다.

선택

결국 go-bindata를 선택했다. Makefile에 생성 과정을 추가하고, 생성된 파일은 .gitignore에 등록했다.

generate:
	go-bindata -o internal/assets/assets.go -pkg assets static/...

build: generate
	go build -o bin/tool cmd/main.go

Go 1.16에서 //go:embed 디렉티브가 추가된다는 소식을 들었다. 그때 가면 다시 마이그레이션할 예정이다.

참고

  • 개발 환경에서는 -debug 플래그로 실제 파일을 읽도록 해서 핫 리로드 구현
  • 프로덕션 빌드만 임베드된 파일 사용
Go 1.15에서 embed 패턴으로 정적 파일 관리하기