빠른 흐름
data, err := os.ReadFile("config.json")
if err != nil {
return err
}
if err := os.WriteFile("out.txt", data, 0o644); err != nil {
return err
}Go 파일 코드는 작은 파일 일괄 처리, 스트림 처리, 빌드 타임 내장을 먼저 구분하면 선택이 쉬워집니다.
기본 흐름
작은 파일은 ReadFile과 WriteFile로 충분하다
data, err := os.ReadFile("users.json")
if err != nil {
return err
}파일 전체를 메모리에 올려도 되는 설정 파일, 작은 JSON, 테스트 fixture는 os.ReadFile이 가장 단순합니다. 반대로 큰 로그나 업로드 파일처럼 크기가 커질 수 있으면 스트림으로 읽는 편이 안전합니다.
파일 핸들은 열었으면 닫는다
f, err := os.Open("input.txt")
if err != nil {
return err
}
defer f.Close()os.Open은 파일 핸들을 돌려줍니다. 함수가 끝날 때 닫히도록 defer f.Close()를 붙이는 흐름이 기본입니다.
스트림 복사는 io.Copy로 읽는다
if _, err := io.Copy(dst, src); err != nil {
return err
}io.Reader에서 io.Writer로 옮기는 흐름은 io.Copy가 표준 표면입니다. 파일, HTTP body, 압축 스트림처럼 구체 타입이 달라도 reader/writer 인터페이스로 묶을 수 있습니다.
줄 단위 입력은 bufio.Scanner가 읽기 쉽다
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
_ = line
}
if err := scanner.Err(); err != nil {
return err
}Scanner는 줄 단위 텍스트를 다루기 좋습니다. 아주 긴 라인이나 바이너리 데이터는 bufio.Reader나 직접 버퍼링을 검토합니다.
내장 파일
문자열이나 바이트로 파일을 내장한다
import _ "embed"
//go:embed static/index.html
var indexHTML string//go:embed는 빌드 시점의 파일을 바이너리에 포함합니다. 런타임에 파일을 따로 배포하지 않아도 되는 템플릿, 작은 정적 파일, 기본 설정을 넣을 때 유용합니다.
여러 파일은 embed.FS로 다룬다
import "embed"
//go:embed templates/*
var templates embed.FS
data, err := templates.ReadFile("templates/index.html")
if err != nil {
return err
}embed.FS는 내장 파일을 파일 시스템처럼 읽는 표면입니다. 웹 서버에서 정적 파일을 서빙하거나 템플릿 묶음을 다룰 때 자주 씁니다.
선택 기준
| 상황 | 먼저 떠올릴 선택 |
|---|---|
| 작은 파일 전체 읽기 | os.ReadFile |
| 작은 파일 전체 쓰기 | os.WriteFile |
| 큰 데이터 복사 | io.Copy |
| 줄 단위 텍스트 | bufio.Scanner |
| 빌드에 파일 포함 | //go:embed |
| 여러 내장 파일 | embed.FS |
주의할 점
embed는 런타임 파일 읽기가 아니라 빌드 타임 내장입니다. 배포 후 파일을 바꿔도 바이너리에 들어간 값은 바뀌지 않습니다. 외부 설정처럼 실행 환경마다 달라져야 하는 값은 embed보다 파일, 환경 변수, 설정 서비스를 따로 두는 편이 맞습니다.
참고 링크
3 sources