GOROOT, GOPATH, 모듈 캐시
이 챕터에서 다루는 것
Go 관련 글을 검색하면 GOPATH 이야기가 잔뜩 나오는데, 정작 지금 Go를 설치하면 GOPATH를 설정하라는 안내가 없다. 이 불일치가 입문자를 가장 많이 헷갈리게 한다. 이 챕터는 어떤 경로가 아직 살아 있고 무엇이 유물인지를 정리한다.
go env: 모든 답이 여기 있다
go 명령이 참고하는 설정은 전부 go env로 볼 수 있다.
go env GOROOT GOPATH GOMODCACHE GOCACHE GOBIN
/usr/local/go
/Users/sgn04088/go
/Users/sgn04088/go/pkg/mod
/Users/sgn04088/Library/Caches/go-build
마지막 줄이 빈 것은 GOBIN이 설정돼 있지 않다는 뜻이다. 인자 없이 go env만 치면 전체
목록이 나온다.
:::info 이 문서의 경로에 대해
위는 이 기계(macOS, 사용자 sgn04088)의 실제 출력이다. Linux에서는 GOCACHE가
~/.cache/go-build, Windows에서는 %LocalAppData%\go-build가 된다. GOROOT와 GOPATH는
OS와 무관하게 같은 형태다.
:::
GOROOT — Go 자신이 설치된 곳
ls /usr/local/go
CONTRIBUTING.md
LICENSE
PATENTS
README.md
SECURITY.md
VERSION
api
bin
codereview.cfg
doc
go.env
lib
misc
pkg
src
test
bin/—go와gofmt실행 파일src/— 표준 라이브러리 전체 소스.fmt,net/http구현이 여기 있다. 나중에 "이 함수가 실제로 뭘 하지?" 싶을 때 직접 열어 보게 될 디렉터리다.pkg/tool/— 컴파일러, 링커,vet같은 내부 도구go.env— GOTOOLCHAIN·GOPROXY 등의 기본값이 적힌 파일
GOROOT는 직접 설정할 필요가 없다. go 실행 파일이 자기 위치로부터 알아서 계산한다.
export GOROOT=...를 시키는 문서를 보면 대개 아주 오래된 글이다. 툴체인을 두 개 깔고
GOROOT를 손으로 지정했다가 서로 엇갈리는 사고가 흔하다.
GOPATH — 남은 역할은 두 개뿐
ls -l ~/go
drwxr-xr-x@ 8 sgn04088 staff 256 Aug 8 13:11 bin
drwxr-xr-x@ 5 sgn04088 staff 160 Oct 16 2025 pkg
기본값은 $HOME/go(Windows는 %USERPROFILE%\go)다. 모듈 시대에 GOPATH가 하는 일은 이 두
디렉터리를 담아 두는 것뿐이다.
$GOPATH/pkg/mod— 내려받은 의존성이 저장되는 모듈 캐시$GOPATH/bin—go install이 만든 실행 파일이 놓이는 곳
GOPATH 시대에는 무엇이 달랐나
2018년 이전(모듈 이전)에는 이랬다.
- 모든 Go 코드가
$GOPATH/src안에 있어야 했다. 프로젝트를~/projects/myapp에 두면 빌드가 안 됐다.~/go/src/github.com/you/myapp이어야 했다. - 버전 개념이 없었다.
go get은 저장소의 기본 브랜치를 그냥 받아 왔다. 두 프로젝트가 같은 라이브러리의 다른 버전을 요구하면 방법이 없었다.dep,glide,govendor같은 서드파티 도구가 그 틈을 메웠다. $GOPATH/src가 곧 import 경로였다. 디렉터리 구조가 이름 공간이었다.
Go 1.11의 모듈, Go 1.16의 모듈 기본화로 이 전부가 사라졌다. 지금은 아무 디렉터리에서나
go mod init을 하면 그게 프로젝트다.
:::warning 오래된 글을 구별하는 신호 다음 중 하나라도 있으면 GOPATH 시대의 글이다. 그대로 따라 하지 말자.
export GOPATH=...를 설정하라고 한다- 프로젝트를
$GOPATH/src아래에 만들라고 한다 GO111MODULE=on을 켜라고 한다 (지금은 그게 기본값이다)dep init,glide install같은 도구가 나온다 :::
GOMODCACHE — 의존성이 실제로 저장되는 곳
go env GOMODCACHE
/Users/sgn04088/go/pkg/mod
내려받은 모듈은 버전별 디렉터리로 풀려서 저장된다.
ls -d ~/go/pkg/mod/golang.org/x/*
/Users/sgn04088/go/pkg/mod/golang.org/x/arch@v0.28.0
/Users/sgn04088/go/pkg/mod/golang.org/x/exp
/Users/sgn04088/go/pkg/mod/golang.org/x/exp@v0.0.0-20231110203233-9a3e6036ecaa
/Users/sgn04088/go/pkg/mod/golang.org/x/exp@v0.0.0-20250620022241-b7579e27df2b
/Users/sgn04088/go/pkg/mod/golang.org/x/mod@v0.23.0
버전이 경로에 박혀 있어서 여러 버전이 동시에 공존한다. 프로젝트 A가 x/exp의 2023년
버전을, 프로젝트 B가 2025년 버전을 써도 충돌하지 않는다. node_modules처럼 프로젝트마다
복사본을 두지 않으므로 디스크도 아낀다.
두 가지 특이한 점
1. 캐시는 읽기 전용이다.
ls -ld ~/go/pkg/mod/golang.org/x/tools@v0.20.0
dr-xr-xr-x@ 26 sgn04088 staff 832 Jul 31 2025 /Users/sgn04088/go/pkg/mod/golang.org/x/tools@v0.20.0
쓰기 권한이 없다. 실수로 고치면 이렇게 된다.
permission denied: /Users/sgn04088/go/pkg/mod/golang.org/x/tools@v0.20.0/LICENSE
의도적이다. 의존성 소스를 로컬에서 몰래 고치고 "내 기계에서는 되는데"가 되는 상황을
막는다. 정말로 고쳐야 한다면 포크하고 replace 지시자를 쓴다(6-4에서 다룬다).
2. 대문자가 !소문자로 인코딩된다.
ls ~/go/pkg/mod/github.com | head -8
!abirdcfly
!admin!benni
!alwx!sin
!antonboom
!burnt!sushi
!click!house
!djarvur
!masterminds
github.com/BurntSushi/toml이 github.com/!burnt!sushi/toml로 저장돼 있다. 대소문자를
구분하지 않는 파일 시스템(macOS 기본값, Windows)에서 Foo와 foo가 충돌하는 것을 막기
위한 인코딩이다. 처음 보면 캐시가 깨진 줄 알기 쉽다. 정상이다.
원본 다운로드는 따로 보관된다
ls ~/go/pkg/mod/cache
download
lock
cache/download에는 압축된 원본 .zip과 해시가 들어 있다. go.sum 검증에 쓰이는 게 이쪽
사본이다. 풀린 디렉터리를 지워도 여기서 다시 복원할 수 있어서, 네트워크 없이도 재빌드가
된다.
GOCACHE — 빌드 결과 캐시
go env GOCACHE
/Users/sgn04088/Library/Caches/go-build
컴파일된 패키지 오브젝트와 테스트 실행 결과가 여기 저장된다. 앞 챕터에서 본 콜드 빌드 3.4초 / 웜 빌드 0.06초의 차이가 바로 이 캐시다.
내용물은 해시로 나뉜 디렉터리라 사람이 읽을 만한 게 없다.
ls ~/Library/Caches/go-build | head -8
00
01
02
03
04
05
06
07
go test를 두 번 연속 돌렸을 때 (cached)가 뜨는 것도 이 캐시 덕분이다. 입력(소스, 빌드
플래그, 환경 변수)이 하나도 안 바뀌면 결과를 그대로 재사용한다.
GOCACHE는 손으로 지워도 안전하다. 지우면 다음 빌드가 느려질 뿐이다.
GOBIN — go install의 목적지
go env GOBIN
비어 있으면 $GOPATH/bin이 쓰인다. 이 기계에서는 이렇다.
ls ~/go/bin
dlv
gofumpt
goimports
golangci-lint
gopls
staticcheck
전부 go install로 깐 개발 도구들이다.
$GOPATH/bin이 PATH에 없으면 go install로 설치한 도구를 이름으로 실행할 수 없다.
셸 설정에 다음을 넣어 두자.
export PATH=$PATH:$(go env GOPATH)/bin
go env -w로 설정 바꾸기
환경 변수를 셸 설정 파일에 넣는 대신, Go 전용 설정 파일에 저장할 수 있다.
go env GOENV
/Users/sgn04088/Library/Application Support/go/env
go env -w GOBIN=$HOME/go/bin
go env GOBIN
/Users/sgn04088/go/bin
파일에는 이렇게 기록된다.
GOBIN=/Users/sgn04088/go/bin
되돌리려면 -u(unset)다.
go env -u GOBIN
우선순위는 셸 환경 변수 > go env -w 설정 파일 > 기본값이다. 그래서 셸에
export GOFLAGS=...가 남아 있으면 go env -w GOFLAGS=...가 무시되는 것처럼 보인다.
"분명히 바꿨는데 안 먹는다" 싶으면 env | grep GO로 셸 쪽을 먼저 확인하자.
캐시 비우기
디스크가 얼마나 차 있는지부터 보자.
du -sh ~/go/pkg/mod ~/Library/Caches/go-build
724M /Users/sgn04088/go/pkg/mod
290M /Users/sgn04088/Library/Caches/go-build
지우는 명령은 두 개다. -n을 붙이면 실행하지 않고 무슨 일을 할지만 보여 준다. 위험한
명령은 항상 이걸로 먼저 확인하는 습관을 들이자.
go clean -n -modcache
rm -rf /Users/sgn04088/go/pkg/mod
go clean -modcache # 실제로 삭제
go clean -cache # 빌드 캐시 삭제
go clean -modcache는 모듈 캐시를 통째로 날린다. 다음 빌드에서 의존성을 전부 다시
내려받는다. 이걸 쓸 상황은 사실 드물다.
- 캐시가 손상됐다는 확신이 있을 때 (해시 불일치 에러가 계속 날 때)
- 디스크를 확보해야 할 때
:::danger 읽기 전용 권한 때문에 실패할 수 있다
캐시 디렉터리는 읽기 전용이라 rm -rf ~/go/pkg/mod를 직접 치면 권한 오류가 난다.
반드시 go clean -modcache를 쓰자. 이 명령은 권한을 먼저 풀고 지운다.
:::
흔히 하는 실수
- GOROOT를 손으로 export한다. Go를 업그레이드하거나 GOTOOLCHAIN이 다른 버전으로 전환하면 값이 어긋난다. 설정하지 말자.
- 프로젝트를
~/go/src아래에 만든다. 지금은 아무 데나 두면 된다.~/go안에 두면 오히려 모듈 캐시와 섞여 헷갈린다. $GOPATH/bin을 PATH에 넣지 않는다.go install은 성공했는데 명령을 못 찾는다.go clean -modcache를 문제 해결의 첫 수단으로 쓴다. 재다운로드에 몇 분이 날아간다. 대부분의 의존성 문제는go mod tidy로 해결된다(6-3).- 모듈 캐시의 소스를 편집한다. 권한이 막아 주지만,
chmod로 뚫고 고치면 다음go clean때 사라진다.
정리
| 변수 | 이 기계의 값 | 역할 | 직접 설정? |
|---|---|---|---|
GOROOT | /usr/local/go | Go 설치 위치, 표준 라이브러리 소스 | 하지 말 것 |
GOPATH | ~/go | pkg/mod와 bin을 담는 상위 디렉터리 | 거의 불필요 |
GOMODCACHE | ~/go/pkg/mod | 내려받은 의존성 (읽기 전용, 버전별) | 거의 불필요 |
GOCACHE | ~/Library/Caches/go-build | 빌드·테스트 결과 캐시 | 불필요 |
GOBIN | (비어 있음 → ~/go/bin) | go install 결과물 위치 | 선택 |
- 소스 코드는 아무 디렉터리에나 두면 된다. GOPATH 시대는 끝났다.
- 설정 확인은
go env, 변경은go env -w, 되돌리기는go env -u. $GOPATH/bin은 PATH에 넣어 두자.
연습문제
-
go env전체 출력을 보고, 이 챕터에서 다루지 않은 변수 중 이름만으로 역할을 짐작할 수 있는 것 다섯 개를 골라 보자.go help environment로 답을 맞춰 보자. -
go env -w GOFLAGS=-v를 설정한 뒤 아무 프로젝트나go build해 보자. 출력이 어떻게 달라지는가? 끝나면go env -u GOFLAGS로 되돌리자. -
모듈 캐시에서 아무 라이브러리나 하나 골라 소스를 열어 읽어 보자 (
ls ~/go/pkg/mod/github.com/...). 그 디렉터리에 파일을 하나 만들려고 하면 어떤 일이 생기는가? 왜 그렇게 설계했을지 설명해 보자.