출시·고도화 중
Go 안내서 · 4/6
Go 코드는 패키지로 나누고, 패키지들은 모듈로 묶어서 버전을 관리합니다. 이 장에서는 go.mod와 go.sum의 역할, 패키지와 공개 범위, 외부 의존성 추가와 갱신, go install, 그리고 vendoring의 기초를 살펴봅니다.
모듈은 함께 버전이 매겨지는 패키지들의 묶음이며, 루트에 있는 go.mod가 그 정의입니다. go.mod에는 모듈 경로, 이 모듈이 요구하는 Go 언어 버전, 그리고 의존하는 다른 모듈과 그 버전이 적힙니다.
module example.com/shop
go 1.xx // go mod init 이 설치된 Go 버전으로 채운다
require (
github.com/google/uuid v1.6.0
golang.org/x/sync v0.7.0 // indirect
)// indirect는 코드에서 직접 가져오지 않고 다른 의존성을 통해 필요한 모듈이라는 표시입니다. 위의 버전 번호는 예시일 뿐이며 실제 값은 도구가 채웁니다. go.sum에는 내려받은 모듈 내용의 암호학적 체크섬이 기록됩니다. Go 도구는 모듈을 내려받을 때마다 이 값과 비교하고, 기본 설정에서는 공개 체크섬 데이터베이스(sum.golang.org)와도 대조해 내용이 바뀌지 않았는지 확인합니다. 두 파일 모두 버전 관리에 커밋해야 합니다.
한 디렉터리가 한 패키지이며, 가져오기 경로는 "모듈 경로 + 디렉터리 경로"입니다. 이름의 첫 글자가 대문자면 다른 패키지에서 쓸 수 있고(exported), 소문자면 패키지 안에서만 쓸 수 있습니다. 이 규칙은 함수, 타입, 변수, 상수, 구조체 필드, 메서드에 모두 적용됩니다.
// 파일: greet/greet.go
package greet
import "fmt"
// Hello 는 공개된다(대문자 시작). 공개 이름에는 이름으로 시작하는 문서 주석을 단다.
func Hello(name string) string {
return fmt.Sprintf("%s, %s!", prefix(), name)
}
// prefix 는 greet 패키지 안에서만 쓸 수 있다.
func prefix() string { return "안녕하세요" }
type Config struct {
Lang string // 공개 필드
debug bool // 비공개 필드
}// 파일: main.go
package main
import (
"fmt"
"example.com/shop/greet"
)
func main() {
fmt.Println(greet.Hello("고퍼"))
// greet.prefix() 는 컴파일 오류: 공개되지 않은 이름
}패키지 이름은 짧은 소문자 한 단어를 쓰고, 사용하는 쪽에서 greet.Hello처럼 패키지 이름과 함께 읽히도록 짓습니다. 그래서 greet.GreetHello처럼 이름을 반복하지 않습니다. 패키지 사이의 순환 가져오기(A가 B를, B가 A를 가져오기)는 허용되지 않습니다. 앞 장에서 본 internal 디렉터리를 쓰면 같은 모듈 안에서만 쓸 패키지를 분명히 표시할 수 있습니다.
외부 패키지를 쓰려면 코드에서 가져오기 경로를 적고 go get이나 go mod tidy를 실행합니다. go mod tidy는 코드가 실제로 가져오는 모듈을 추가하고, 더 이상 쓰지 않는 모듈은 지웁니다.
# 최신 버전 추가(또는 갱신)
go get github.com/google/uuid@latest
# 특정 버전 고정, 하위 버전으로 내리기도 같은 방식
go get github.com/google/uuid@v1.6.0
# 의존성 목록을 코드에 맞게 정리
go mod tidy
# 의존성 목록과 갱신 가능한 버전 보기
go list -m all
go list -m -u all
# 의존성 제거
go get github.com/google/uuid@noneGo 모듈은 유의적 버전(semver)을 따릅니다. 큰 버전이 2 이상이면 가져오기 경로 끝에 /v2처럼 큰 버전이 붙어서, 서로 호환되지 않는 버전을 다른 패키지로 다룹니다. 여러 의존성이 같은 모듈의 다른 버전을 요구하면 Go는 최소 버전 선택(MVS) 방식으로 요구된 버전 가운데 가장 높은 것을 고릅니다. 새 버전이 나왔다고 저절로 올라가지 않으므로 빌드가 재현 가능합니다.
go install은 패키지를 빌드해서 실행 파일을 GOBIN(지정하지 않으면 GOPATH/bin)에 넣습니다. 버전을 붙이면 현재 모듈의 go.mod를 건드리지 않고 도구를 설치할 수 있습니다.
# 도구 설치(현재 모듈과 무관)
go install golang.org/x/tools/cmd/goimports@latest
# 지금 작업 중인 모듈의 main 패키지를 설치
go install ./cmd/server
# 설치 위치 확인
go env GOBIN GOPATH아직 공개하지 않은 다른 모듈을 고치면서 함께 시험하고 싶을 때는 go.mod의 replace 지시어나 작업 공간(go work)을 씁니다. replace는 특정 모듈을 로컬 디렉터리로 바꿔 끼우고, go work는 여러 모듈을 한 작업 공간으로 묶어 go.mod를 고치지 않고 함께 빌드합니다.
# replace: go.mod 에 기록된다
go mod edit -replace example.com/lib=../lib
# 작업 공간: go.work 파일이 생긴다(보통 커밋하지 않는다)
go work init . ../libgo mod vendor는 빌드에 필요한 모든 의존성 소스를 모듈 루트의 vendor 디렉터리에 복사합니다. 네트워크 없이 빌드해야 하거나, 의존성 코드를 저장소 안에서 검토해야 할 때 씁니다. vendor 디렉터리가 있고 go.mod의 Go 버전 조건이 맞으면 go build는 기본으로 vendor를 사용합니다.
go mod vendor # vendor/ 와 vendor/modules.txt 생성
go build -mod=vendor # vendor 사용을 명시
go build -mod=mod # vendor 를 무시하고 모듈 캐시 사용의존성을 바꾼 뒤에는 go mod vendor를 다시 실행해 vendor를 맞춰야 합니다. 대부분의 프로젝트는 vendoring 없이 모듈 캐시와 프록시(GOPROXY)만으로 충분합니다. 사내 비공개 저장소의 모듈을 쓸 때는 GOPRIVATE에 그 경로를 지정해 공개 프록시와 체크섬 데이터베이스를 거치지 않게 합니다.
go.mod는 모듈 경로와 의존성을, go.sum은 체크섬을 기록하며 둘 다 커밋합니다.internal로 범위를 더 좁힐 수 있습니다.go get 모듈@버전으로 추가·변경하고 go mod tidy로 정리합니다.go install 경로@버전은 현재 모듈을 바꾸지 않고 도구를 설치합니다.go mod vendor는 의존성을 저장소 안에 복사하며, 필요할 때만 씁니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.