Skip to content

명령줄

Go 의 명령은 문서화, 포맷팅, 코드 검사, 컴파일, 테스트, 의존성 관리 등 다양한 측면을 포괄하는 전체 도구 체인을 포함하며, Go 개발의 모든 측면을 다루고 있다고 할 수 있습니다.

text
bug         취약성 보고
build       패키지 및 의존성 컴파일
clean       객체 파일 정리
doc         소스 코드의 문서 표시
env         Go 환경 변수 정보 조회
fix         go 버전 변경으로 인한 API 호환성 문제 수정
fmt         소스 코드 포맷팅
generate    코드 생성
get         의존성 추가
install     패키지 설치 및 컴파일
list        패키지/모듈 목록 명령
mod         모듈 유지 관리 명령
work        작업 영역 유지 관리 명령
run         컴파일 및 실행
test        테스트
tool        지정된 go 도구 실행
version     go 버전 정보 표시
vet         소스 코드의 잠재적 문제 스캔 및 보고

이 문서에서는 사용법에 대해 간단히 설명하며, 모든 내용은 공식 문서를 참고했습니다. 더 자세한 내용은 cmd/go 에서 확인할 수 있습니다.

help

먼저 알아야 할 것은 help 명령으로, 이를 통해 명령의 사용법을 확인할 수 있습니다. 두 가지 사용 방법이 있는데, 지정된 명령 뒤에 -h 플래그를 추가하면 간단한 사용 정보를 얻을 수 있습니다. 예를 들어:

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

go 는 명령의 사용법을 간단히 표시하며, 더 자세한 정보는 help 명령을 사용해야 함을 알려줍니다.

sh
$ go help env
usage: go env [-json] [-u] [-w] [var ...]

Env prints Go environment information.

By default env prints information as a shell script
(on Windows, a batch file). If one or more variable
names is given as arguments, env prints the value of
each named variable on its own line.

The -json flag prints the environment in JSON format
instead of as a shell script.

The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.

The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.

For more about environment variables, see 'go help environment'.

help 명령을 잘 활용하면 명령에 대한 많은 정보를 얻을 수 있습니다.

doc

sh
$ go doc -h
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<methodOrField>]
        go doc [<pkg>.]<sym>[.<methodOrField>]
        go doc [<pkg>.][<sym>.]<methodOrField>
        go doc <pkg> <sym>[.<methodOrField>]
For more information run
        go help doc

Flags:
  -C dir
        change to dir before running command
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported

doc 명령은 지정된 패키지, 상수, 함수, 타입, 변수, 메서드, 심지어 구조체 필드에 대한 문서 주석을 출력합니다. 매개변수 없이 실행하면 현재 패키지의 주석을 출력합니다.

sh
$ go doc

특정 패키지를 지정하여 볼 수도 있습니다. 예를 들어 runtime 패키지의 문서 주석을 확인하려면:

sh
$ go doc runtime
package runtime // import "runtime"

Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type
information used by the reflect package; see reflect's documentation for the
programmable interface to the run-time type system.
......

또는 특정 타입:

sh
$ go doc unsafe.Pointer
package unsafe // import "unsafe"

type Pointer *ArbitraryType
    Pointer represents a pointer to an arbitrary type. There are four special
    operations available for type Pointer that are not available for other
    types:
      - A pointer value of any type can be converted to a Pointer.
      - A Pointer can be converted to a pointer value of any type.
      - A uintptr can be converted to a Pointer.
      - A Pointer can be converted to a uintptr.
      ...

또는 특정 함수:

sh
$ go doc runtime.GC
package runtime // import "runtime"

func GC()
    GC runs a garbage collection and blocks the caller until the garbage
    collection is complete. It may also block the entire program.

다음과 같은 일반적인 플래그를 사용할 수 있습니다:

  • -u: 비공개 타입 확인
  • -all: 지정된 패키지의 모든 문서 확인
  • -short: 한 줄 간단한 설명만 표시
  • -src: 소스 코드 출력
  • -cmd: go 명령에 속하는 패키지도 패키지 내 코드 문서 출력

예를 들어 runtime.inf 변수를 확인하려면, 이는 외부에 노출되지 않는 변수입니다:

sh
$ go doc -u runtime.inf
package runtime // import "runtime"

var inf = float64frombits(0x7FF0000000000000)

doc 명령을 잘 활용하면 문서를 더 편리하게 읽을 수 있습니다.

명령 문서를 읽는 또 다른 방법은 소스 코드를 읽는 것입니다. 일부 명령 문서는 그렇게 자세히 작성되지 않은 반면, 소스 코드에는 더 자세한 설명이 있는 경우가 많습니다. 이러한 명령은 모두 Go 로 작성되었으며 src/cmd 패키지에 위치하며, 각 하위 패키지는 별도의 명령이며 진입점은 cmd/go/main.go 파일에 있습니다.

go
func init() {
    base.Go.Commands = []*base.Command{
       bug.CmdBug,
       work.CmdBuild,
       clean.CmdClean,
       doc.CmdDoc,
       envcmd.CmdEnv,
       fix.CmdFix,
       fmtcmd.CmdFmt,
       generate.CmdGenerate,
       modget.CmdGet,
       work.CmdInstall,
       list.CmdList,
       modcmd.CmdMod,
       workcmd.CmdWork,
       run.CmdRun,
       test.CmdTest,
       tool.CmdTool,
       version.CmdVersion,
       vet.CmdVet,

       help.HelpBuildConstraint,
       help.HelpBuildmode,
       help.HelpC,
       help.HelpCache,
       help.HelpEnvironment,
       help.HelpFileType,
       modload.HelpGoMod,
       help.HelpGopath,
       get.HelpGopathGet,
       modfetch.HelpGoproxy,
       help.HelpImportPath,
       modload.HelpModules,
       modget.HelpModuleGet,
       modfetch.HelpModuleAuth,
       help.HelpPackages,
       modfetch.HelpPrivate,
       test.HelpTestflag,
       test.HelpTestfunc,
       modget.HelpVCS,
    }
}

여기서 go 의 모든 하위 명령과 해당 도움말 정보를 찾을 수 있습니다.

bug

sh
$ go help bug
usage: go bug

Bug opens the default browser and starts a new bug report.
The report includes useful system information.

이 명령은 매개변수나 플래그가 없으며, 기본 브라우저로 github.com/golang/go 저장소의 issue 페이지를 열어 버그 보고를 할 수 있게 해줍니다. 그 외의 기능은 없습니다.

version

version 명령으로 현재 go 의 버전 정보를 확인할 수 있습니다.

go
$ go version -h
usage: go version [-m] [-v] [file ...]

매개변수 없이 실행하면 현재 go 언어의 버전을 출력합니다.

sh
$ go version
go version go1.21.0 windows/amd64

파일 경로를 매개변수로 전달할 수도 있으며, 해당 경로의 식별 가능한 모든 바이너리 파일이 컴파일될 때 사용된 go 버전을 출력합니다.

sh
$ go version -v ./
buf.exe: go1.20.2
cobra-cli.exe: go1.21.0
dlv.exe: go1.20.2
goctl.exe: go1.20.2
goimports.exe: go1.20.2
golangci-lint.exe: go1.20.2
gopls.exe: go1.19.3
kratos.exe: go1.20.2
main.exe: go1.19.1
protoc-gen-go-grpc.exe: go1.20.2
protoc-gen-go-http.exe: go1.20.2
protoc-gen-go.exe: go1.20.2
protoc-gen-openapi.exe: go1.20.2
swag.exe: go1.21.0
wire.exe: go1.21.0

-v 매개변수는 version 명령이 식별할 수 없는 파일의 go 버전을 출력하도록 하며, -m 매개변수는 바이너리 파일의 모듈 정보 및 일부 컴파일 매개변수를 출력합니다. 다음은 간단한 예시입니다.

sh
$ go version -v -m wire.exe
wire.exe: go1.21.0
        path    github.com/google/wire/cmd/wire
        mod     github.com/google/wire  v0.5.0  h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
        dep     github.com/google/subcommands   v1.0.1  h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
        dep     github.com/pmezard/go-difflib   v1.0.0  h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
        dep     golang.org/x/tools      v0.0.0-20190422233926-fe54fb35175b      h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
        build   -buildmode=exe
        build   -compiler=gc
        build   DefaultGODEBUG=panicnil=1
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=amd64
        build   GOOS=windows
        build   GOAMD64=v1

go 자체도 바이너리 파일이며, 실제로 매개변수 없이 go version 을 실행하면 자체 바이너리 파일의 go 언어 버전을 출력합니다. cmd/go 의 모든 도구 체인은 go 언어 자체로 구현되었기 때문입니다.

env

env 명령으로 모든 go 환경 변수의 값을 확인할 수 있으며, 이러한 환경 변수를 수정하면 go 도구 체인의 동작에 영향을 미칩니다.

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

매개변수 없이 실행하면 go 의 모든 환경 변수 값을 출력합니다.

sh
$ go env
set GO111MODULE=on
set GOARCH=amd64
...

특정 환경 변수 이름을 매개변수로 전달하면 해당 변수의 값만 출력할 수 있습니다.

sh
$ go env GO111MODULE
on

-json 을 추가하면 JSON 형식으로 출력할 수 있습니다.

sh
$ go env -json
{
        "AR": "ar",
        "CC": "gcc",
    ......
}

-w 플래그와 var=value 형식의 매개변수를 사용하면 특정 변수의 값을 영구적으로 수정할 수 있습니다.

sh
$ go env -w GO111MODULE=on

-u 플래그를 사용하면 특정 변수를 기본값으로 복원할 수 있습니다.

sh
$ go env -u GO111MODULE

go help environment 를 실행하면 각 환경 변수에 대한 설명을 확인할 수 있습니다.

sh
$ go help environment
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset or empty, the go
command uses a sensible default setting. To see the effective setting of
the variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.

General-purpose environment variables:

        GO111MODULE
                Controls whether the go command runs in module-aware mode or GOPATH mode.
                May be "off", "on", or "auto".
                See https://golang.org/ref/mod#mod-commands.
        GCCGO
                The gccgo command to run for 'go build -compiler=gccgo'.
        GOARCH
                The architecture, or processor, for which to compile code.
                Examples are amd64, 386, arm, ppc64.
        GOBIN
                The directory where 'go install' will install a command.
        GOCACHE
                The directory where the go command will store cached
                information for reuse in future builds.
    ......

다음은 몇 가지 일반적인 환경 변수입니다.

GOVERSION

이 환경 변수의 값은 go 언어의 버전에 따라 달라지며, 버전 번호는 $GOROOT/VERSION 파일에서 가져옵니다. 이 파일에는 현재 go 의 버전과 빌드 시간이 기록되어 있습니다.

sh
$ cat $GOROOT/VERSION
go1.21.3
time 2023-10-09T17:04:35Z

runtime.Version 변수 값은 GOVERSION 의 값과 동일하며, 이 환경 변수는 수정할 수 없습니다.

GOENV

$GOROOT 디렉토리에는 go.env 라는 기본 구성 파일이 있습니다.

sh
$ cat $GOROOT/go.env
# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.

# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org

# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=auto

형식은 간단한 key=value 형식이며, go env -w key=value 명령으로 수정한 환경 변수 값은 구성 파일에 기록됩니다. 그러나 기본 구성 파일을 사용하지 않고 GOENV 환경 변수로 env 구성 파일 경로를 수동으로 지정할 수 있으며, GOENV 환경 변수 값은 운영 체제의 환경 변수로만 덮어쓸 수 있고 go env -w 명령으로는 수정할 수 없습니다.

GOHOSTARCH

현재 컴퓨터의 CPU 아키텍처를 나타내며, 이는 표시용일 뿐이며 환경 변수 값은 구성 파일에서 읽은 것이 아니며 수정할 수도 없습니다.

GOHOSTOS

현재 컴퓨터의 운영 체제를 나타내며, 이는 표시용일 뿐이며 환경 변수 값은 구성 파일에서 읽은 것이 아니며 수정할 수도 없습니다.

GOOS

컴파일 시 GOOS 값은 소스 코드를 어떤 대상 시스템의 바이너리 파일로 컴파일할지 결정하며, 기본값은 GOHOSTOS, 즉 현재 컴퓨터의 운영 체제입니다. 다음 옵션이 있습니다.

  • linux
  • darwin
  • windows
  • netbsd
  • aix
  • android

실제로 지원되는 운영 체제는 이보다 더 많습니다. go tool dist list 명령으로 모든 지원되는 값을 확인할 수 있습니다.

sh
$ go tool dist list | awk -F '/' '{print $1}' | awk '!seen[$0]++'
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
wasip1
windows

GOARCH

컴파일 시 GOARCH 값은 컴파일 시 어떤 CPU 아키텍처의 명령을 사용할지 결정하며, 기본값은 GOHOSTARCH, 즉 현재 컴퓨터의 CPU 아키텍처입니다. 다음 옵션이 있습니다.

  • amd64
  • 386
  • arm
  • ppc64

실제로 지원되는 아키텍처는 이보다 더 많습니다. go tool dist list 명령으로 모든 지원되는 값을 확인할 수 있습니다.

sh
$ go tool dist list | awk -F '/' '{print $2}' | awk '!seen[$0]++'
ppc64
386
amd64
arm
arm64
riscv64
wasm
loong64
mips
mips64
mips64le
mipsle
ppc64le
s390x

GOOSGOARCH 는 임의로 조합할 수 없으며, 일부 운영 체제는 특정 CPU 아키텍처만 지원합니다.

GOROOT

GOROOT 는 go 언어 설치 위치의 루트 디렉토리를 나타내며, GOROOT 값은 직접 수정할 수 없으며 운영 체제의 환경 변수로만 덮어쓸 수 있습니다.

sh
$ ls $GOROOT -1
api
bin
codereview.cfg
CONTRIBUTING.md
doc
go.env
lib
LICENSE
misc
pkg
PATENTS
README.md
SECURITY.md
src
test
VERSION

루트 디렉토리에는 몇 가지 중요한 폴더나 파일이 있습니다.

  • lib: 일부 의존성을 저장하며, 현재는 세계 각국의 시간대 정보를 포함하는 라이브러리 하나만 있으며, $GOROOT/lib/time 에 위치합니다. 컴파일된 바이너리 파일에는 이러한 시간대 정보가 포함되지 않습니다.

  • pkg: 일부 도구 라이브러리와 헤더 파일을 저장하며, 예를 들어 go tool 명령은 $GOROOT/pkg/tool 디렉토리에서 go 도구 체인의 바이너리 파일을 찾습니다.

  • bin: 바이너리 파일을 저장하며, 기본적으로 gogofmt 두 개의 실행 파일만 있습니다. go 명령을 사용하려면 $GOROOT/bin 을 시스템 변수에 추가해야 합니다.

  • src: go 소스 코드를 저장합니다.

  • VERSION: go 언어의 버전 정보를 저장합니다.

  • go.env: 기본 env 구성 파일입니다.

GOPATH

GOPATH 의 기본값은 $HOME/go이며, 이 환경 변수 값은 import 문을 해석할 때 가져온 파일을 어디에서 찾을지 지정합니다. gomod 가 없던 초기에는 GOPATH 가 다양한 서드파티 라이브러리를 저장하는 데 사용되었으며, 구조는 다음과 같습니다.

sh
GOPATH=/home/user/go

/home/user/go/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object)

gomod 가 등장한 후 GOPATHgo get 으로 다운로드한 의존성을 저장하는 곳과 go install 로 다운로드하고 컴파일한 바이너리 파일을 저장하는 곳으로만 사용됩니다. GOPATH 위치는 GOROOT 와 같을 수 없으며, 그렇지 않으면 아무런 효과가 없습니다.

sh
$ go env GOBIN
warning: GOPATH set to GOROOT (/home/user/go) has no effect

이 글을 작성하는 시점에서 go 언어 버전은 이미 go1.21.3 이며, 매우 오래된 프로젝트를 제외하고는 더 이상 gopath 를 사용하여 의존성을 관리하는 사람은 거의 없습니다.

GOBIN

GOBINgo install 로 다운로드하고 컴파일한 서드파티 바이너리 실행 파일을 저장하는 데 사용되며, 기본값은 $GOPATH/bin 입니다. $GOROOT/bin 과 마찬가지로 이 디렉토리는 운영 체제의 환경 변수에 추가되어야 하며, 그렇지 않으면 GOBIN 디렉토리 아래의 바이너리 파일을 사용할 수 없습니다.

GOMODCACHE

GOMODCACHEgo get 으로 다운로드한 의존성이 저장되는 위치를 나타내며, 기본값은 $GOPATH/pkg/mod 입니다. 저장 형식은 다음과 같습니다.

$GOMODCACHE/domain/username/project@verion

동일한 레벨 디렉토리에는 sumdb 라는 폴더도 있으며, 의존성 검증 및 체크섬 데이터베이스 관련 정보를 저장합니다.

GOCACHE

컴파일에 사용되는 캐시 정보를 저장하며, 기본값은 $HOME/.cache/go-build 입니다. 이 디렉토리에는 README 파일이 생성됩니다.

sh
$ cat $(go env GOCACHE)/README
This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.

build 를 실행할 때마다 많은 파일이 생성되며, go 는 이러한 파일을 캐시하여 다음 컴파일 시 재사용합니다.

GOTEMPDIR

컴파일 시 생성되는 임시 파일 (예: go run 으로 실행할 임시 바이너리 파일) 을 저장합니다. 기본값은 운영 체제가 지정한 임시 디렉토리이며, mac 또는 linux 에서는 /tmp, windows 에서는 %TEMP%입니다. 사용자가 지정한 위치로 수정할 수도 있습니다.

GO111MODULE

이 환경 변수는 go 프로젝트의 의존성을 관리하는 방식을 나타내며, 다음 세 가지 값을 사용할 수 있습니다.

  • off: gomod 를 끄고 gopath 를 사용하며 모든 go.mod 파일을 무시합니다.
  • on: gomod 를 사용하고 gopath 를 사용하지 않습니다 (기본값).
  • auto: 자동 감지, 프로젝트 파일에 go.mod 가 포함되어 있으면 gomod 를 사용하여 관리합니다.

TIP

GOMODULE 이 아니라 GO111MODULE 일까요? gomod 는 go1.11 버전에서 처음 출시되었기 때문입니다.

GOPROXY

go 모듈 프록시로, 기본값은 https://proxy.golang.org,direct이며, url 은 쉼표로 구분됩니다. direct 는 모듈 프록시를 건너뛰고 직접 VCS 를 사용한다는 의미이며, 첫 번째를 사용할 수 없을 때만 두 번째를 실행합니다. 또 다른 사용 가능한 옵션은 off로, 모든 모듈 다운로드를 금지합니다. 또한 GOPROXY 는 파일 주소일 수도 있습니다. 예를 들어:

GOPROXY=file://$(go env GOMODCACHE)/cache/download

go get -x 로 의존성 다운로드 과정에서 실행되는 명령을 확인할 수 있으며, 이를 통해 프록시를 사용하는지 알 수 있습니다.

sh
$ go get -x github.com/spf13/cast
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/spf13/cast/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list: 404 Not Found (0.118s)
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.197s)
# get https://goproxy.cn/github.com/spf13/cast/@v/list: 200 OK (0.257s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info: 200 OK (0.013s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod: 200 OK (0.015s)
# get https://goproxy.cn/sumdb/sum.golang.org/supported
# get https://goproxy.cn/sumdb/sum.golang.org/supported: 200 OK (0.064s)
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1: 200 OK (0.014s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736: 200 OK (0.016s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334: 200 OK (0.023s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266: 200 OK (0.028s)
go: downloading github.com/spf13/cast v1.5.1
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip: 200 OK (0.024s)
go: added github.com/spf13/cast v1.5.1

모듈 프록시를 사용하면 모듈 다운로드 속도를 효과적으로 향상시킬 수 있습니다. 중국 사용자의 경우 기본적으로 공식 프록시에 접근할 수 없으므로 현재 공개되고 신뢰할 수 있는 서드파티 모듈 프록시는 다음과 같습니다.

  • https://proxy.golang.com.cn: 오픈소스이며 기업용 서비스도 제공
  • https://goproxy.cn: Qiniu Cloud 에서 제공하며 오픈소스

물론 오픈소스 자체 모듈 프록시 구축方案도 있습니다: goproxy

GOSUMDB

GOSUMDB 는 의존성 라이브러리의 검증 및 체크섬 데이터베이스 주소를 설정하는 데 사용되며, 기본값은 sum.golang.org입니다. 프록시를 설정하면 go 는 프록시를 통해 검증 데이터베이스에 접근합니다.

GOPRIVATE

GOPRIVATE 환경 변수는 개인 저장소를 설정하는 데 사용되며, 일치하는 라이브러리는 sumdb 를 통한 검증을 거치지 않으며 프록시를 통하지 않고 VCS 를 통해 직접 의존성을 다운로드합니다. 와일드카드 설정을 지원하며 쉼표로 구분합니다. 다음과 같이 모든 corp.example.com 접미사와 github.com/gohper/myproject 라는 이름의 의존성은 프록시와 sumdb 를 통과하지 않습니다.

GOPRIVATE=*.corp.example.com,github.com/gohper/myproject

특정 사용자나 조직을 직접 설정할 수도 있습니다.

GOPRIVATE=github.com/gopher,github.com/myorganization

GONOPROXY

어떤 의존성이 프록시를 통과하지 않아도 됨을 나타내며, 규칙은 GOPRIVATE 와 동일하며 GOPRIVATE 를 덮어씁니다.

GONOSUMDB

어떤 의존성이 검증 데이터베이스를 통과하지 않아도 됨을 나타내며, 규칙은 GOPRIVATE 와 동일하며 GOPRIVATE 를 덮어씁니다.

GOINSECURE

어떤 의존성이 VCS 를 통해 직접 다운로드됨을 나타내며, 규칙은 GOPRIVATE 와 동일하며 GONOPROXYGONOSUMDB 에 의해 덮어씁니다.

GOVCS

모듈 관리의 버전 제어 시스템을 설정하며, 기본값은 public:git|hg,private:all입니다. 지정된 도메인의 VCS 를 제한할 수도 있습니다. 예를 들어:

GOVCS=github.com:git,evil.com:off,*:git|hg

위의 제한에서 github 는 git 만 사용할 수 있으며, evil.com 은 사용할 수 없습니다. | 를 사용하여 여러 VCS 를 나타낼 수 있습니다. 제한을 하지 않으려면 다음과 같이 설정할 수 있습니다.

GOVCS=*:all

VCS 사용을 허용하지 않으려면 다음과 같이 설정할 수 있습니다.

GOVCS=*:off

GOWORK

작업 영역 활성화 여부를 설정하며, 기본값은 비어 있음 (활성화). off 로 설정하면 모든 go.work 파일을 무시합니다.

GOTOOLDIR

사용할 go 도구 체인의 위치를 설정하며, 기본값은 $GOROOT/pkg/tool이며 기본 도구 체인도 이 위치에 저장됩니다.

GODEBUG

디버그 옵션을 설정하며, 키 - 값 쌍 형식으로 go 프로그램의 일부 실행 동작을 제어합니다. 예를 들어:

GODEBUG=http2client=0,http2server=0

이러한 설정은 버전 업데이트 과정에서 호환되지 않는 변경이 발생했을 때 go 가 이전 동작으로 되돌아갈 수 있도록 하기 위한 것입니다. 예를 들어 1.21 에서 더 이상 panic(nil) 이 허용되지 않습니다. 이에 대해 go 공식은 GODEBUG History 를 기록했으며, GODEBUG 에서 자세한 내용을 확인할 수 있습니다.

CGO_ENABLED

cgo 활성화 여부를 나타내며, 기본값은 1(활성화) 이며, 0 으로 설정하면 비활성화됩니다.

위의 환경 변수는 모두 일반적으로 사용되는 것이며, CGO, WASM 등 덜 일반적으로 사용되지 않는 환경 변수는 여기서 자세히 설명하지 않습니다. 관심이 있으시면 직접 알아보세요.

build

go 는 gccgo 와 gc 두 가지 컴파일러를 지원합니다. gcc 는 오래된 c/c++ 컴파일러로 go 를 포함한 여러 언어를 지원하며, 후자인 gc 는 garbage collection 을 의미하는 것이 아니라 go compiler 를 의미합니다. go 언어는 go1.5 에서 자체 컴파일을 완료했으며, gc 는 완전히 go 언어로 작성된 컴파일러로, 소스 코드는 cmd/compile 패키지에 위치합니다. 완전히 go 언어로 구현되었기 때문에 내부 메커니즘을 이해하고 학습하기에 매우 편리합니다. 기본적으로 컴파일러는 gc 를 사용하여 컴파일합니다.顺便说一下,go 언어 디버거도 gdb 와 dlv 두 가지가 있으며, 전자는 오래된 c/c++ 디버거로 go 를 포함한 여러 언어를 지원하고, 후자는 go 언어로 작성된 디버거로 go 언어에 더 친숙한 지원을 제공합니다. 이 또한 오픈소스이며 후자를 사용하는 것을 권장합니다.

build 명령은 go 소스 파일을 실행 가능한 바이너리 파일로 컴파일하는 역할을 하며, 매우 빠른 컴파일 경험을 제공합니다. 이 또한 go 언어의 특징 중 하나입니다.

sh
$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.

세 가지 매개변수를 받습니다. 하나는 -o 플래그가 지시하는 파일 출력 경로, 하나는 컴파일 동작을 정의하는 빌드 플래그 build flags, 마지막 하나는 컴파일할 패키지입니다. 이 매개변수는 반드시 마지막에 있어야 합니다. 다음은 빌드 플래그를 사용하지 않는 간단한 예시입니다.

sh
# Windows
$ go build -o .\bin\golearn.exe golearn

# macOS / Linux
$ go build -o ./bin/golearn golearn

./bin/golearn.exe 는 출력 경로를 나타내며, golearn 은 컴파일할 모듈입니다. 진입 파일 이나 폴더 일 수도 있습니다. 다음은 main.go 진입 파일을 컴파일 대상으로 하는 간단한 예시입니다.

sh
# Windows
$ go build -o .\bin\golearn.exe main.go

# macOS / Linux
$ go build -o ./bin/golearn main.go

컴파일 시 _test.go 로 끝나는 모든 파일은 무시됩니다. 이러한 파일은 관례에 따라 테스트 파일이기 때문입니다.

또한 build 명령은 컴파일 시 일부 동작을 제어하기 위해 상당히 많은 빌드 플래그를 지원합니다.

  • -x: 컴파일 과정의 자세한 명령 출력
  • -n: -x 와 유사하지만 실제로 실행하지 않고 명령만 출력합니다.
  • -v: 컴파일된 패키지 출력
  • -p: 컴파일 과정의 동시성 수
  • -a: 이미 최신이더라도 강제 재빌드
  • -compiler: 사용할 컴파일러 지정, gccgo 또는 gc(후자는 go 로 작성된 컴파일러)
  • -race: 경합 감지 활성화
  • -msan: 메모리 분석 활성화
  • -asan: 주소 분석 활성화
  • -cover: 코드 커버리지 감지 활성화
  • -buildmode: 컴파일 모드 지정, archive, c-archive, c-shared, default, shared, exe, pie, plugin 옵션이 있습니다.
  • -pgo: pgo 파일 지정
  • -trimpath: 소스 파일 경로 접두사 제거, 예를 들어 상대 경로 /var/lib/go/src/main.go 는 제거 후 런타임을 통해 runtime 에서 가져온 파일 이름은 모듈 경로에 대한 상대 경로 /main.go 만 남습니다. 이 항목을 활성화하면 컴파일 시간이 약 20-40% 정도 증가하며 파일 수에 따라 다릅니다.
  • -toolexec: 컴파일 전에 실행할 일부 go 명령, 형식은 -toolexec 'cmd args'입니다.
  • -gcflags: 컴파일러 gc 의 일부 tag 지정
  • -gccgoflags: 컴파일러 gccgo 의 일부 tag 지정
  • -ldflags: link 도구의 일부 tag 지정

ldflags 와 같은 전달 매개변수의 경우 -help 와 같은 매개변수를 전달하여 가능한 값을 얻을 수 있습니다. 예를 들어:

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
......

위 내용은 일반적으로 사용되는 것이며, 다른 덜 일반적으로 사용되는 내용은 직접 알아보세요.

gcflags

gcflags 를 통해 컴파일러 gc 에 일부 매개변수를 전달하여 특정 동작을 제어할 수 있습니다. 사용 형식은 -gcflags="pattern=args list"이며, ages list 는 매개변수 목록이고 pattern 은 작용 범위입니다. 다음 값을 사용할 수 있습니다.

  • main: 진입 파일이 있는 최상위 패키지 경로
  • all: 현재 모듈 및 현재 모드의 모든 의존성
  • std: 표준 라이브러리
  • cmd: cmd 패키지 아래의 모든 소스 파일
  • 와일드카드, 예를 들어 ., ./..., cmd/....

pattern 규칙은 이 형식을 지원하는 모든 플래그 (예: ldflags) 에 적용됩니다. 다음 명령으로 사용 가능한 매개변수 값을 확인할 수 있습니다.

sh
$ go build -gcflags -help
用法:compile [选项] file.go...
  -%    调试非静态初始化器
  -+    编译运行时
  -B    禁用边界检查
  -C    禁用错误消息中的列号打印
  -D path
        设置本地导入的相对路径
  -E    调试符号导出
  -I directory
        添加目录到导入搜索路径
  -K    调试缺失的行号
  -L    对于受 //line 指令影响的错误位置,同时显示实际源文件名
  -N    禁用优化
  -S    打印汇编列表
  -V    打印版本并退出
  -W    类型检查后调试解析树
  ......

다음은 몇 가지 일반적으로 사용되는 매개변수입니다.

  • -S: 코드의 어셈블리 형식 출력
  • -N: 컴파일 최적화 비활성화
  • -m: 최적화 결정 출력
  • -l: 함수 인라인 비활성화
  • -c: 컴파일 동시성 수
  • -dwarf: DWARF 심볼 생성

예를 들어 코드의 어셈블리 형식을 확인하려면 -S 매개변수를 사용하고 최적화와 인라인을 비활성화해야 원래 형식을 복원할 수 있습니다. 다음과 같습니다.

sh
$ go build -trimpath -gcflags="-N -l -S" main.go
main.main STEXT size=171 args=0x0 locals=0x58 funcid=0x0 align=0x0
        0x0000 00000 (./main.go:9)      TEXT    main.main(SB), ABIInternal, $88-0
        0x0000 00000 (./main.go:9)      CMPQ    SP, 16(R14)
        0x0004 00004 (./main.go:9)      PCDATA  $0, $-2
        0x0004 00004 (./main.go:9)      JLS     161
        0x000a 00010 (./main.go:9)      PCDATA  $0, $-1
        0x000a 00010 (./main.go:9)      PUSHQ   BP
        0x000b 00011 (./main.go:9)      MOVQ    SP, BP
        0x000e 00014 (./main.go:9)      SUBQ    $80, SP
        0x0012 00018 (./main.go:9)      FUNCDATA        $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $1, gclocals·bDfKCdmtOiGIuJz/x+yQyQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $2, main.main.stkobj(SB)
        0x0012 00018 (./main.go:10)     MOVUPS  X15, main..autotmp_0+40(SP)
        0x0018 00024 (./main.go:10)     LEAQ    main..autotmp_0+40(SP), CX
        0x001d 00029 (./main.go:10)     MOVQ    CX, main..autotmp_2+32(SP)

ldflags

ldflags 를 통해 링커에 일부 매개변수를 전달하여 특정 동작을 제어할 수 있습니다. 다음 명령으로 ldflags 의 모든 사용 가능한 값을 확인할 수 있으며, 거의 20-30 개에 달합니다.

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
  -H type
        set header type
  -I linker
        use linker as ELF dynamic linker
  -L directory
        add specified directory to library path
  -R quantum
        set address rounding quantum (default -1)
  -T int
        set the start address of text symbols (default -1)
  -V    print version and exit
  -X definition
        add string value definition of the form importpath.name=value
  -a    no-op (deprecated)
  .....

ldflags-X 매개변수는 매우 실용적인 기능으로, 링크 시 지정된 패키지의 문자열 변수 값을 정의할 수 있습니다. 이 기능을 통해 컴파일 시 일부 메타정보를 주입할 수 있습니다. 또한 이는 변수이므로 런타임에 가져오기도 편리합니다. 다음은 간단한 예시입니다.

go
package main

import "fmt"

var (
  Version string
)

func main() {
  fmt.Println(Version)
}

명령 실행

sh
go build -ldflags "-X main.Version=$(git describe --always)" main.go

실행 후 git 커밋의 sha1 체크섬을 출력합니다.

5e3fd7a

또한 몇 가지 실용적인 매개변수는 다음과 같습니다.

  • -w: DWARF 생성 안 함 (소스 코드 디버깅을 위한 정보)
  • -s: 심볼 테이블 비활성화

이 두 가지는 일반적으로 함께 사용되며 컴파일된 바이너리 파일의 크기를 약 40%-50% 정도 줄일 수 있습니다. 단점은 디버깅을 할 수 없다는 것입니다. 다음은 예시입니다.

sh
$ go build -ldflags="-w -s" main.go

교차 컴파일

go 언어 컴파일의 두 가지 큰 특징 중 하나는 빠르다는 것이며, 다른 하나는 교차 컴파일입니다. 교차 컴파일은 로컬에서 다른 시스템의 대상 코드로 컴파일할 수 있음을 의미합니다. 예를 들어 windows 에서 linuxdarwin 의 바이너리 파일로 컴파일할 수 있으며, 그 반대도 마찬가지입니다. 교차 컴파일을 지원하는 언어는 매우 많지만 이는 특별한 일이 아닙니다. 그러나 go 언어의 교차 컴파일은 매우 간단하며 다음 두 단계만 필요합니다.

  1. GOOS 환경 변수 설정, 대상 운영 체제 선택
  2. GOARCH 환경 변수 설정, 대상 CPU 아키텍처 선택
  3. 평소와 같이 go build 로 컴파일

전체 과정은 매우 짧으며 추가 도구나 구성이 필요 없고 속도는 평소와 마찬가지로 빠릅니다. 다음과 같습니다.

makefile
build_linux:
  SET CGO_ENABLED=0
  SET GOOS="linux"
  SET GOARCH="amd64"
  go build -o golearn  main.go

build_mac:
  SET CGO_ENABLED=0
  SET GOOS="darwin"
  SET GOARCH="amd64"
  go build -o golearn main.go

build_win:
  SET CGO_ENABLED=0
  SET GOOS="win"
  SET GOARCH="amd64"
  go build -o golearn.exe main.go

.PHONY: build_linux \
    build_mac \
    build_win

첫 번째 단계 SET CGO_ENABLED=0 은 cgo 를 비활성화하며, 코드에서 cgo 를 사용하면 교차 컴파일을 정상적으로 사용할 수 없습니다. 두 번째 단계 SET GOOS 는 대상 시스템을 설정하며, 옵션은 linux, darwin, windwos, netbsd 가 있습니다. 세 번째 단계는 CPU 아키텍처를 설정하며, SET GOARCH 는 옵션으로 amd64, 386, arm, ppc64 가 있습니다. 마지막 단계는 평소와 같이 컴파일합니다.

컴파일 제어

build 명령은 tags 를 통해 컴파일을 제어할 수 있으며, 이는 소스 코드에 지시문 형태로 존재합니다. 예를 들어 product.go 파일을 보겠습니다.

go
// +build product

package main

import "fmt"

func main() {
  fmt.Println("product")
}

debug.go 파일

go
// +build debug

package main

import "fmt"

func main() {
  fmt.Println("debug")
}

두 파일 모두 // +build 지시문이 있으며, 이는 어떤 상황에서 컴파일될지를 나타냅니다. 기본 형식은 다음과 같습니다.

go
// +build tag1 tag2

package pkg_name

반드시 지켜야 할 몇 가지 규칙이 있습니다.

  1. //+build 는 한 칸 띄어야 합니다.
  2. 패키지 선언 위에 있어야 합니다.
  3. 패키지 선언과는 한 줄의 빈 줄을 두어야 합니다.

또한 간단한 간격을 통해 논리 제어를 할 수 있습니다. 공백은 OR, 쉼표는 AND, ! 는 NOT 을 나타냅니다. 예를 들어:

go
// +build windows linux

package pkg_name

이는 windows 또는 linux 플랫폼에서 현재 파일을 컴파일함을 나타냅니다.

go
// +build windows,amd64,!cgo linux,i386,cgo

package pkg_name

이 예는 windows 플랫폼 amd64 아키텍처且 cgo 를 사용하지 않거나 linux 플랫폼 i386 아키텍처且 cgo 를 사용할 때만 컴파일함을 나타냅니다. 단순히 특정 파일이 컴파일에 참여하지 않게 하려면 ignore 를 사용할 수 있습니다.

go
// +build ignore

package pkg_name

여러 줄의 지시문도 있을 수 있습니다.

go
// +build windows
// +build amd64

package pkg_name

여러 줄 지시문은 AND 방식으로 처리됩니다. 플랫폼과 아키텍처와 같은 tag 는 컴파일 시 go 가 자동으로 전달하며, 사용자 정의 tag 도 전달할 수 있습니다. 처음 두 파일을 예로 들면:

sh
$ go build -tags="debug" . && ./golearn.exe
debug

$ go build -tags="product" . && ./golearn.exe
product

서로 다른 tag 를 전달할 때 출력이 다르며 컴파일 제어 목적을 달성할 수 있습니다.

run

run 명령은 build 와 마찬가지로 소스 코드를 컴파일하지만, run 명령은 컴파일完成后 바로 실행합니다. run 명령은 컴파일 속도를 높이기 위해 컴파일 과정에서 디버그 정보를 생성하지 않으므로 디버깅을 지원하지 않으며, 임시 바이너리 파일만 생성하며 일반적으로 GOTMEPDIR 디렉토리 (예: /temp/go-build2822241271/b001/exe/main.exe) 에 저장됩니다.

sh
$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.

build 명령의 빌드 플래그도 지원하며, -exec 매개변수를 제공하여 바이너리 파일을 실행할 프로그램을 지정하고, [arguments...] 는 프로그램 실행 매개변수입니다. 다음은 예시입니다.

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

func main() {
  fmt.Println(Version)
  fmt.Println(os.Args[1:])
}

go run 으로 실행

sh
$ go run -ldflags="-X main.Version=$(git describe --always)" main.go hello
5e3fd7a
[hello]

전반적으로 go build 와 큰 차이가 없으므로 더 이상 자세히 설명하지 않습니다.

tool

tool 명령 자체는 기능이 없으며, cmd/ 디렉토리 아래의 도구를 직접 호출하는 역할을 합니다. 예를 들어 cmd/compile 은 기본 제공 컴파일러입니다. go tool 을 통해 이러한 도구를 직접 호출할 수 있으며, 이러한 도구의 바이너리 파일을 수동으로 실행할 필요가 없습니다.

sh
$ go tool -h
usage: go tool [-n] command [args...]

-n 매개변수를 사용하여 지원되는 모든 명령 매개변수를 출력합니다.

sh
$ go tool -n
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
test2json
trace
vet

이러한 도구는 GOROOT/pkg/tool 디렉토리에 저장되며, 운영 체제와 CPU 아키텍처에 따라 도구 그룹이 나뉩니다. 다음과 같습니다.

sh
$ ls $GOROOT/pkg/tool/windows_amd64/ -1
addr2line.exe*
asm.exe*
buildid.exe*
cgo.exe*
compile.exe*
covdata.exe*
cover.exe*
doc.exe*
fix.exe*
link.exe*
nm.exe*
objdump.exe*
pack.exe*
pprof.exe*
test2json.exe*
trace.exe*
vet.exe*

go doc cmd/command 형식으로 각 명령의 사용법을 확인할 수 있습니다. 예를 들어:

sh
$ go doc cmd/compile
Usage:

    go tool compile [flags] file...

The specified files must be Go source files and all part of the same package.
The same compiler is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.

Flags:

    -D path
        Set relative path for local imports.
    -I dir1 -I dir2
        Search for imported packages in dir1, dir2, etc,
        after consulting $GOROOT/pkg/$GOOS_$GOARCH.
    -L
        Show complete file path in error messages.
...

cmd/compile 이 지원하는 플래그 매개변수는 앞에서 언급한 gcflags 가 지원하는 매개변수와 동일합니다. go tool compilego build 의 차이점은 전자는 컴파일만 담당하며 파일만 매개변수로 사용할 수 있고, 후자는 폴더, 패키지, 파일을 매개변수로 사용할 수 있으며 소스 코드 컴파일뿐만 아니라 파일 링크, 불필요한 파일 제거 등도 담당한다는 것입니다. 전자는 후자의 일부입니다. build 과정에서 실행되는 명령을 출력할 수 있습니다.

sh
$ go build -n main.go

#
# internal/goarch
#

mkdir -p $WORK\b004\
cat >$WORK\b004\importcfg << 'EOF' # internal
# import config
EOF
"/golang/pkg/tool/windows_amd64/compile.exe" -o "$WORK/b004/_pkg_.a" -trimpath "$WORK/b004=>" -p internal/goarch -std -+ -complete -buildid 3gunEkUExGdhOPa2rFsh/3gunEkUExGdhOPa2rFsh -goversion go1.21.0 -c=4 -nolocalimports -importcfg "$WORK/b004/importcfg" -pack "/golang/src/internal/goarch/goarch.go" "/golang/src/internal/goarch/goarch_amd64.go" "/golang/src/internal/goarch/zgoarch_amd64.go"
"/golang/pkg/tool/windows_amd64/buildid.exe" -w "$WORK/b004/_pkg_.a" # internal
...

과정에서 /golang/pkg/tool/windows_amd64/compile.exe 라는 부분이 있으며, 이는 컴파일러를 호출한 것입니다. compile 외에도 호출할 수 있는 도구가 많으며, 많은 go 명령은 실제로 이들의 별명입니다.

clean

clean 명령은 컴파일 과정에서 생성된 객체 파일을 제거하는 데 사용됩니다.

sh
$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.

다음 플래그를 지원합니다.

  • -i: 해당 아카이브 파일 또는 바이너리 파일 제거
  • -n: 제거 과정에서 실행될 명령을 출력하지만 실제로 실행하지 않음
  • -x: 제거 과정에서 실행될 명령을 출력하고 실행
  • -r: import path 를 통해 재귀적으로 제거
  • -cache: go build 에서 생성된 모든 캐시 제거
  • -testcache: 생성된 모든 테스트 캐시 제거
  • -modcache: 다운로드된 모든 모듈 캐시 제거
  • -fuzzcache: fuzz test 에서 생성된 캐시 제거

go tool compile 을 사용할 때는 컴파일러 명령을 직접 호출하며, go build 처럼 많은 사후 처리를 하지 않으므로 객체 파일이 생성됩니다. 예를 들어 다음 명령을 실행하면

sh
go tool compile -N -S -l main.go

main.o 라는 파일이 생성되며, go clean 명령으로 제거할 수 있습니다. 또는 -n 매개변수를 사용하여 실행될 명령을 출력할 수 있습니다.

sh
$ go clean -n
rm -f golearn golearn.exe golearn golearn.exe golearn.test golearn.test.exe golearn.test golearn.test.exe api api.exe main main.exe

컴파일 캐시 제거는 GOCACHE 디렉토리에서 생성된 컴파일 캐시를 삭제합니다.

sh
$ go clean -cache -n
rm -r /cache/00 /cache/01 /cache/02

fuzz test 에서 생성된 캐시 제거는 이러한 캐시가 기본적으로 저장되는 GOCACHE/fuzz/ 디렉토리를 삭제합니다.

sh
$ go clean -fuzzcache -n
rm -rf /cache/fuzz

fix

go 언어는 이 글을 작성할 시점에서 이미 10 년이 되었습니다. 언어가 지속적으로 업데이트되고 수정되는 과정에서 API 변경으로 인한 호환성 문제가 발생할 수 있으며, fix 명령은 이를 위해 존재합니다. 이는 소스 파일에서 이미 구식이 된 API 를 감지하고 새로운 API 로 교체합니다.

sh
$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.

폴더, 파일 이름, 디렉토리를 매개변수로 받으며, -fix 플래그를 받아 어떤 수정을 수행할지 나타냅니다. got tool fix -help 명령으로 사용 가능한 값을 확인할 수 있습니다.

sh
$ go tool fix -help
usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]
  -diff
        display diffs instead of rewriting files
  -force string
        force these fixes to run even if the code looks updated
  -go string
        go language version for files
  -r string
        restrict the rewrites to this comma-separated list

Available rewrites are:

buildtag
        Remove +build comments from modules using Go 1.18 or later

cftype
        Fixes initializers and casts of C.*Ref and JNI types

context
        Change imports of golang.org/x/net/context to context

egl
        Fixes initializers of EGLDisplay

eglconf
        Fixes initializers of EGLConfig

gotypes
        Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}

jni
        Fixes initializers of JNI's jobject and subtypes

netipv6zone
        Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.

        https://codereview.appspot.com/6849045/

printerconfig
        Add element keys to Config composite literals.

다음은 예시로, 소스 코드에서 golang.org/x/net/context 패키지를 사용했습니다.

sh
package main

import (
  "fmt"
  "golang.org/x/net/context"
)

func main() {
  background := context.Background()
  fmt.Println(background.Err())
}

go fix 로 수정하여 표준 라이브러리의 context 패키지로 교체하려면 다음 명령을 사용할 수 있습니다.

sh
$ go fix -fix context main.go

교체하지 않고 전후 파일 변화를 확인할 수도 있습니다.

sh
$ go tool fix -r context -diff  main.go
main.go: fixed context
diff main.go fixed/main.go
--- main.go
+++ fixed/main.go
@@ -1,8 +1,8 @@
 package main

 import (
+       "context"
        "fmt"
-       "golang.org/x/net/context"
 )

 func main() {

go 언어는 10 년 이상 발전했지만 사용 가능한 교체 매개변수는 9 개뿐이며, 호환성이 잘 유지되고 있음을 알 수 있습니다.

fmt

fmt 명령은 go 언어 자체 포맷팅 도구로, go 소스 코드 파일을 포맷팅하는 데 사용됩니다.

sh
$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.

go doc gofmt 명령으로 자세한 문서를 확인할 수 있습니다.

sh
$ go doc cmd/gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
    -e
        Print all (including spurious) errors.
    -l
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, print its name
        to standard output.
    -r rule
        Apply the rewrite rule to the source before reformatting.
    -s
        Try to simplify code (after applying the rewrite rule, if any).
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.

gofmt 는 들여쓰기에 tab 을, 정렬에 공백을 사용합니다. 기본적으로 포맷팅된 코드는 표준 출력으로 출력되며 원본 파일을 덮어쓰지 않습니다. go fmt 명령은 실제로 gofmt 명령을 사용하며, 이는 독립적인 바이너리 파일로 GOROOT/bin 디렉토리에 위치합니다.

sh
$ ls $GOROOT/bin -1
go.exe*
gofmt.exe*

go fmt 명령에 -n 플래그를 추가하면 실행될 명령을 알 수 있습니다.

sh
$ go fmt main.go
/golang/bin/gofmt.exe -l -w main.go

go fmt 는 실제로 gofmt -l -w 의 별명이며, gofmt 명령에는 다음 매개변수가 있습니다.

  • -d: 포맷팅 전후 파일 차이 출력
  • -e: 모든 오류 출력
  • -l: 변경된 파일 이름 출력
  • -r: 포맷팅 규칙 적용
  • -s: 코드 단순화 시도
  • -w: 원본 파일 덮어쓰기, 오류 발생 시 백업 복원

다음과 같은 소스 파일이 있다고 가정합니다.

go
$ cat main.go
package main

import "fmt"

func main() {
fmt.Println("hello world!")}

-d 매개변수로 변화를 미리 볼 수 있습니다.

sh
$ gofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-fmt.Println("hello world!")}
-
+       fmt.Println("hello world!")
+}

-l 매개변수는 수정될 파일 이름을 출력합니다.

$ gofmt -l .
main.go

문법 오류가 있으면 -e 매개변수로 더 자세히 출력할 수 있습니다.

sh
$ gofmt -d -e main.go
main.go:6:27: missing ',' in argument list
main.go:6:28: expected operand, found newline
main.go:7:2: expected ')', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected '}', found 'EOF'
main.go:7:2: missing ',' in argument list

-w 는 수정 사항을 소스 파일에 적용합니다.

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world!")
}

포맷팅 도구로서 gofmt 는 사용자 정의 구성을 전혀 제공하지 않는다는 것을 알 수 있습니다. 반면 js 코드를 미화하는 포맷터인 prettify 는 코드를 포맷팅하기 위해 상당히 많은 구성을 제공합니다. 이는 go 공식의 태도를 보여줍니다. 개인화를 원하지 않으며 모든 사람의 코드 스타일이 일치하는 것이 좋습니다. 적어도 코드를 읽을 때 다른 사람의 습관에 적응할 필요가 없다는 장점이 있습니다. 하지만 실제로는 사용자 정의 항목이 하나 있습니다. 바로 포맷팅 코드 교체 규칙으로, 규칙은 사용자 정의할 수 있으며 형식은 다음과 같습니다.

pattern -> replacement

예를 들어 불필요한 괄호 제거:

(a) -> a

파일 변경 확인:

sh
$ gofmt -r "(a) -> a" -d -l .
main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-       fmt.Println(("hello world!"))
+       fmt.Println("hello world!")
 }

gofmt 가 불필요한 괄호를 제거하는 것을 볼 수 있습니다.

get

get 명령은 go 개발 과정에서 가장 일반적으로 사용되는 명령으로, 지정된 주소의 패키지 소스 코드를 GOMODCACHE 가 가리키는 디렉토리에 다운로드하는 역할을 합니다.

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u: 패키리의 부 버전 및 패치 버전 업데이트 시도, v1->v2 와 같은 주 버전 변경이涉及되면 업데이트되지 않습니다.
  • -t: 테스트 중 의존성 버전 업데이트
  • -v: 컴파일된 패키지 출력, 실제로는 build flags 의 매개변수 중 하나입니다.

옛날에는 go get 의 역할이 go install 과 유사했으며, 이러한 패키지를 다운로드하고 컴파일했습니다. 그러나 go 모듈의 등장과 완비로 인해 이 부분의 역할은 점차 폐기되었으며, get 명령은 이제 go 모듈 다운로드 및 의존성 해석에 가장 일반적으로 사용됩니다. 따라서 go get 명령이 build flags 와 같은 빌드 플래그를 지원하는 것을 볼 수 있으며, go install 처럼 모듈 외부에서 go get 을 사용하려고 하면 이 사용법이 폐기되었다는 메시지가 표시됩니다.

sh
$ go get github.com/wire/wire
go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.

문서 설명에서 왜 이러한 내용을 유지하는지는 알 수 없지만, get 명령의 소스 코드를 보면 이전 플래그를 유지하고 있는 것을 발견할 수 있습니다.

go
var (
  getD        = CmdGet.Flag.Bool("d", true, "")
  getF        = CmdGet.Flag.Bool("f", false, "")
  getFix      = CmdGet.Flag.Bool("fix", false, "")
  getM        = CmdGet.Flag.Bool("m", false, "")
  getT        = CmdGet.Flag.Bool("t", false, "")
  getU        upgradeFlag
  getInsecure = CmdGet.Flag.Bool("insecure", false, "")
  // -v is cfg.BuildV
)

본론으로 돌아가서, get 명령은 지정된 패키지의 소스 코드를 로컬 전역 의존성 디렉토리 (즉, GOCACHE 가 가리키는 디렉토리) 에 다운로드한 후 정보를 go.modgo.sum 파일에 기록합니다. 전자는 버전을 기록하고, 후자는 sha1 체크섬을 기록하여 보안을 보장합니다. get 명령은 실제로 VCS, 즉 로컬 버전 제어 시스템을 기반으로 하며, 다음 몇 가지를 지원합니다.

  • git
  • hg (Mercurial)
  • bzr (Bazaar)
  • svn
  • fossil

그 중 기본적으로 git 과 hg 만 지원하며, GOVCS 에서 구성할 수 있습니다. 형식은 다음과 같습니다.

GOVCS=github.com:git,example.com:hg,*:git|hg,*:all

GOVCS 는 git 과 hg 만 VCS 로 지원하며, 다른 세 가지는 GOPRIVATE 에서 구성해야 합니다.

go get 명령에는 다음 몇 가지 사용법이 있습니다. 의존성 주소를 매개변수로 직접 사용할 수 있습니다.

sh
$ go get golang.org/x/net

버전을 지정할 수도 있습니다.

sh
$ go get golang.org/x/net@0.17.0

최신 버전을 지정합니다.

sh
$ go get golang.org/x/net@latest

버전 업데이트를 시도합니다.

sh
$ go get -u golang.org/x/net

특정 의존성을 제거합니다.

sh
$ go get golang.org/x/net@none

위는 일반 의존성을 관리하는 데 사용되며, 일반적이지 않은 의존성도 관리할 수 있습니다. 예를 들어 go 언어 버전을 업데이트합니다.

sh
$ go get go@latest
go: updating go.mod requires go >= 1.21.3; switching to go1.21.3
go: downloading go1.21.3 (windows/amd64)
go: upgraded go 1.21.0 => 1.21.3

go 도구 체인 버전도 업데이트할 수 있습니다.

sh
$ go get toolchain@latest

go get 으로 go 와 도구 체인 버전을 업데이트하면 GOMODCACHE/golang.org/ 디렉토리에 새 버전의 go 를 설치합니다.

sh
$ ls $(go env GOMODCACHE)/golang.org -1
'toolchain@v0.0.1-go1.21.3.windows-amd64'/
x/

이때 GOROOT 를 수동으로 수정하면 지정된 버전으로 전환할 수 있습니다.

install

install 명령은 get 명령과 유사하며, 둘 다 서드파티 의존성을 다운로드하는 데 사용됩니다. 그러나 차이점은 get 명령은 소스 코드를 다운로드하고, install 명령은 소스 코드를 컴파일하여 로컬에서 실행 가능한 바이너리 파일로 만든다는 것입니다. 바이너리 파일 저장 경로는 먼저 GOBIN 디렉토리이며, 그 다음은 GOPATH/bin 입니다. 이 명령의 주요 기능은 서드파티 공개 명령줄 도구를 다운로드하는 데 사용되며, go 언어의 컴파일 속도와 이식성 덕분에 바이너리 파일을 다운로드할 필요가 없고 소스 코드를 다운로드한 후 로컬에서 컴파일합니다.

sh
$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.

install 명령은 빌드 플래그와 패키지 이름을 매개변수로 받으며, gomod 가 활성화된 경우 패키지 이름은 반드시 버전 번호를 포함해야 합니다. 예를 들어 delve 디버거 다운로드:

sh
$ go install -x github.com/go-delve/delve/cmd/dlv@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/@v/list
# get https://goproxy.cn/github.com/go-delve/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.014s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/go-delve/@v/list: 404 Not Found (0.027s)
WORK=/home/user/tmp/go-build2033992495
mkdir -p $WORK/b001/
cat >/home/user/tmp/go-build2033992495/b001/importcfg.link << 'EOF' # internal
packagefile github.com/go-delve/delve/cmd/dlv=/home/user/.cache/go-build/f1/f11d552287458c0fce625abe50bf928c487064c36bbb1251ad8b1968772c3e4b-d
......
......
mkdir -p /home/wyh/gomod/bin/
mv $WORK/b001/exe/a.out /home/wyh/gomod/bin/dlv
rm -r $WORK/b001/

먼저 소스 코드를 GOMODCACHE 가 가리키는 경로에 다운로드하며, 이는 get 명령과 동일합니다. 그런 다음 임시 작업 디렉토리로 전환하여 컴파일하고, 컴파일完成后 바이너리 파일을 GOPATH/bin 디렉토리로 이동한 후 임시 폴더를 삭제합니다. install 명령에는 또 다른 제한 사항이 있으며, 다운로드할 패키지는 프로젝트의 진입 패키지여야 합니다. 즉, main.go 진입 파일이 포함되어야 합니다. 그렇지 않으면 설치할 수 없다는 메시지가 표시됩니다. 예를 들어, go install 로 gin 을 다운로드합니다.

sh
$ go install -x github.com/gin-gonic/gin@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list
# get https://goproxy.cn/github.com/gin-gonic/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.022s)
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/gin-gonic/@v/list: 404 Not Found (0.028s)
package github.com/gin-gonic/gin is not a main package

gin 은 웹 프레임워크 의존성 라이브러리이며 명령줄 도구가 아니므로 진입 파일이 없어 설치가 실패합니다.

list

list 명령은 지정된 위치의 패키지를 나열하며, 한 줄에 하나씩 출력하며 사용자 정의 포맷팅 출력을 지원하며 많은 매개변수를 지원합니다. 사용하려면 gomod 를 지원하는 프로젝트 내에 있어야 합니다.

sh
$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.

지원하는 매개변수는 다음과 같습니다.

  • -f: 포맷팅 매개변수
  • -json: JSON 형식 출력
  • -compiled: 컴파일러가 컴파일할 모든 패키지 표시
  • -deps: 각 패키지와 해당 의존성의 모든 패키지 이름 표시
  • -test: 각 패키지의 테스트 패키지 표시
  • -e: 오류가 있는 패키지를 만날 때 정상 출력
  • -find: 이러한 패키지의 의존성 관계 해석 안 함
  • -export: 이 매개변수를 사용할 때 구조체 Package.Export 필드 값을 지정된 패키지가 포함된 최신 내보내기 정보 파일로 설정하고 Package.BuildID 필드 값을 패키지의 BuildID 로 설정합니다. 주로 포맷팅 출력용입니다.

모듈 정보 매개변수:

  • -m: 패키지 대신 모듈 출력

  • -versions: 모듈의 모든 사용 가능한 정보 표시

  • -retracted: 모듈의 철회 버전 표시

[packages] 매개변수는 지정된 패키지 이름이거나 폴더일 수 있으며, all 일 수도 있으며, 이는 어디든 의미합니다. -m 매개변수를 사용할 때 all 은 현재 모듈이 참조하는 모든 의존성을 의미합니다.

예를 들어, 현재 파일에 main.go 파일 하나만 있으며, "hello world" 를 출력하는 코드 한 줄만 있는 경우, go list -deps . 를 실행하면 현재 프로젝트에서 fmt 및 해당 참조의 모든 의존성 패키지를 출력합니다.

bash
$ ls
go.mod  go.sum  main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world")
}

$ go list -deps .
internal/goarch
unsafe
internal/abi
internal/unsafeheader
internal/cpu
internal/bytealg
internal/coverage/rtcov
internal/godebugs
internal/goexperiment
internal/goos
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime
......
......
path
io/fs
os
fmt
golearn

또는 현재 프로젝트의 모든 모듈 의존성을 출력합니다.

bash
$ go list -m all
golearn
cloud.google.com/go v0.26.0
github.com/246859/containers v0.0.1
github.com/246859/river v0.1.0 => D:\WorkSpace\Code\riverdb
github.com/BurntSushi/toml v0.3.1
github.com/Jleagle/steam-go v0.0.0-20230725082712-1053b441b1f2
github.com/Jleagle/unmarshal-go v0.0.0-20210227002040-694f544f9265
github.com/KyleBanks/depth v1.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/PuerkitoBio/purell v1.1.1
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/andeya/ameda v1.5.3
github.com/andeya/goutil v1.0.1
...

format

list 명령의 출력은 행 단위이며, 각 행 출력은 패키지입니다. 공식에서는 사용자 정의 행 출력 형식을 위한 매개변수 -f 를 제공하며, 이는 template/text 템플릿 엔진 패키지가 정의한 템플릿 구문을接受的 값으로 사용합니다. 예를 들어:

sh
-f "package {{ .Dir }} {{ .Name }}"

각 반복되는 패키지는 다음 구조체 형식으로 전달되며, 이 구조체의 모든 필드는 템플릿 매개변수로 사용할 수 있습니다.

go
type Package struct {
    Dir            string   // directory containing package sources
    ImportPath     string   // import path of package in dir
    ImportComment  string   // path in import comment on package statement
    Name           string   // package name
    Doc            string   // package documentation string
    Target         string   // install path
    Shlib          string   // the shared library that contains this package (only set when -linkshared)
    Goroot         bool     // is this package in the Go root?
    Standard       bool     // is this package part of the standard Go library?
    Stale          bool     // would 'go install' do anything for this package?
    StaleReason    string   // explanation for Stale==true
    Root           string   // Go root or Go path dir containing this package
    ConflictDir    string   // this directory shadows Dir in $GOPATH
    BinaryOnly     bool     // binary-only package (no longer supported)
    ForTest        string   // package is only for use in named test
    Export         string   // file containing export data (when using -export)
    BuildID        string   // build ID of the compiled package (when using -export)
    Module         *Module  // info about package's containing module, if any (can be nil)
    Match          []string // command-line patterns matching this package
    DepOnly        bool     // package is only a dependency, not explicitly listed
    DefaultGODEBUG string  // default GODEBUG setting, for main packages

    // Source files
    GoFiles           []string   // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    CgoFiles          []string   // .go source files that import "C"
    CompiledGoFiles   []string   // .go files presented to compiler (when using -compiled)
    IgnoredGoFiles    []string   // .go source files ignored due to build constraints
    IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
    CFiles            []string   // .c source files
    CXXFiles          []string   // .cc, .cxx and .cpp source files
    MFiles            []string   // .m source files
    HFiles            []string   // .h, .hh, .hpp and .hxx source files
    FFiles            []string   // .f, .F, .for and .f90 Fortran source files
    SFiles            []string   // .s source files
    SwigFiles         []string   // .swig files
    SwigCXXFiles      []string   // .swigcxx files
    SysoFiles         []string   // .syso object files to add to archive
    TestGoFiles       []string   // _test.go files in package
    XTestGoFiles      []string   // _test.go files outside package

    // Embedded files
    EmbedPatterns      []string // //go:embed patterns
    EmbedFiles         []string // files matched by EmbedPatterns
    TestEmbedPatterns  []string // //go:embed patterns in TestGoFiles
    TestEmbedFiles     []string // files matched by TestEmbedPatterns
    XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
    XTestEmbedFiles    []string // files matched by XTestEmbedPatterns

    // Cgo directives
    CgoCFLAGS    []string // cgo: flags for C compiler
    CgoCPPFLAGS  []string // cgo: flags for C preprocessor
    CgoCXXFLAGS  []string // cgo: flags for C++ compiler
    CgoFFLAGS    []string // cgo: flags for Fortran compiler
    CgoLDFLAGS   []string // cgo: flags for linker
    CgoPkgConfig []string // cgo: pkg-config names

    // Dependency information
    Imports      []string          // import paths used by this package
    ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
    Deps         []string          // all (recursively) imported dependencies
    TestImports  []string          // imports from TestGoFiles
    XTestImports []string          // imports from XTestGoFiles

    // Error information
    Incomplete bool            // this package or a dependency has an error
    Error      *PackageError   // error loading package
    DepsErrors []*PackageError // errors loading dependencies
}

type PackageError struct {
    ImportStack   []string // shortest path from package named on command line to this one
    Pos           string   // position of error (if present, file:line:col)
    Err           string   // the error itself
}

모듈을 반복하는 경우 다음 구조체 형식으로 전달되며, 이 구조체의 모든 필드도 템플릿 매개변수로 사용할 수 있습니다.

go
type Module struct {
    Path       string        // module path
    Query      string        // version query corresponding to this version
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
    Origin     any           // provenance of module
    Reuse      bool          // reuse of old module info is safe
}

type ModuleError struct {
    Err string // the error itself
}

모든 패키지 확인:

bash
$ go list -f "package {{.Dir}} {{.Name}}" ./...
package /golearn main
package /golearn/app cmd
package /golearn/cmd cmd
package /golearn/docs docs
package /golearn/tool tool
package /golearn/tool_test tool

모듈 확인:

bash
$ go list -m -f "mod {{.Path}} {{.Version}} {{.GoVersion}} {{.GoMod}}"
mod golearn  1.21.3 /golearn/go.mod

mod

go mod 는 go 모듈을 관리하는 전용 명령입니다.

sh
$ go mod help
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

        go mod <command> [arguments]

The commands are:

        download    download modules to local cache
        edit        edit go.mod from tools or scripts
        graph       print module requirement graph
        init        initialize new module in current directory
        tidy        add missing and remove unused modules
        vendor      make vendored copy of dependencies
        verify      verify dependencies have expected content
        why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

다음 하위 명령이 있습니다.

  • download: go.mod 파일에 명시된 모든 의존성을 로컬 캐시에 다운로드
  • edit: go.mod 파일 편집, 주로 다른 도구나 스크립트에서 호출하는 명령줄 인터페이스 제공
  • init: 현재 디렉토리에서 gomod 프로젝트 초기화
  • tidy: 누락된 의존성 다운로드, 사용하지 않는 의존성 제거
  • graph: 의존성 그래프 출력
  • verify: 로컬 의존성 검증
  • why: 왜 이러한 모듈에 의존하는지 설명
  • vendor: 프로젝트 의존성을 vendor 디렉토리에 내보내기

init

sh
$ go help mod init
usage: go mod init [module-path]

init 명령은 gomod 프로젝트를 초기화하며, 유일한 매개변수는 모듈 경로입니다. 나중에 다른 사람이 의존성을 다운로드하려면 이 모듈 경로를 기준으로 합니다. 명명 규칙은 일반적으로 다음과 같습니다.

domain_name/user_name/repo_name

예를 들어, 일반적으로 프로젝트를 github 에 올리므로 다음과 같을 수 있습니다.

github.com/jack/gotour

모듈 경로로 특수 기호를 사용하는 것은 권장되지 않습니다. 사용 예를 보겠습니다.

sh
$ mkdir gotour

$ cd gotour

$ go mod init "github.com/jack/gotour"
go: creating new go.mod: module github.com/jack/gotour

tidy

$ go help mod tidy
usage: go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]

tidy 명령은 go.mod 에서 사용하지 않는 의존성 (즉, 참조되지 않는 의존성) 을 제거하고, 참조되지만 존재하지 않는 의존성을 다운로드합니다. 다음 매개변수를 지원합니다.

  • -v: 삭제된 모듈 의존성 출력
  • -e: 과정에서 오류가 발생하면 무시하고 계속 실행
  • -x: 실행 과정 출력
  • -go=version: go.mod 파일의 go 버전 업데이트
  • -compact=version: 지정된 주요 Go 버전에서 필요한 추가 체크섬을 유지하여 모듈 그래프를 성공적으로 로드하며, 이 버전의 go 명령이 다른 모듈 버전에서 가져온 패키지를 로드할 때 tidy 가 오류를 발생시킵니다. 일반적으로 이 매개변수는 거의 사용되지 않으며, 일반적으로 버전 변경 시에만 오류가 발생할 수 있습니다. stackoverflow 의 이 답변을 참조하세요: go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

사용 예를 보겠습니다.

sh
$ go mod tidy -v
unused github.com/246859/containers
unused github.com/246859/river
unused github.com/Jleagle/steam-go
unused github.com/Jleagle/unmarshal-go
unused github.com/KyleBanks/depth
unused github.com/Microsoft/go-winio
unused github.com/PuerkitoBio/purell
unused github.com/PuerkitoBio/urlesc
unused github.com/andeya/ameda
unused github.com/andeya/goutil
unused github.com/asaskevich/govalidator
unused github.com/buger/jsonparser
unused github.com/bwmarrin/snowflake
unused github.com/bytedance/go-tagexpr/v2
unused github.com/bytedance/sonic
unused github.com/cespare/xxhash/v2
unused github.com/chenzhuoyu/base64x
......

download

sh
$ go help mod download
usage: go mod download [-x] [-json] [-reuse=old.json] [modules]

download 명령의 이름은 번역하면 다운로드이지만, 실제로는 의존성을 로컬 의존성 캐시에만 다운로드하며 go.mod 파일을 수정하지 않습니다. 역할은 의존성을 로컬 파일 캐시에 미리 다운로드하는 것이며, 특정 의존성을 다운로드하려면 go get 또는 go mod tidy 를 사용하는 것이 좋습니다.

다음은 몇 가지 사용 예입니다.

sh
$ go mod download -x gorm.io/gorm
# get https://goproxy.cn/gorm.io/gorm/@v/list
# get https://goproxy.cn/gorm.io/gorm/@v/list: 200 OK (0.084s)

매개변수 없이 실행하면 go.mod 파일에 존재하지만 로컬 의존성 캐시에 존재하지 않는 모든 의존성을 다운로드합니다. 다운로드할 것이 없으면 다음을 출력합니다.

go: no module dependencies to download

edit

sh
$ go help mod edit
usage: go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

editgo.mod 파일을 수정하는 명령줄 인터페이스로, 일반적으로 다른 프로그램에서 사용합니다. 일부 편집기 IDE 는 gomod 지원을 제공하기 위해 이러한 명령을 사용합니다. 다음 매개변수를 지원합니다.

  • -module: 모듈 경로 수정
  • -go=version: 예상 go 버전 수정
  • -require=path@version: 의존성 추가
  • -droprequire=path@version: 의존성 제거
  • -exclude=path@version: 제외 의존성 추가
  • -dropexclude=path@version: 제외 의존성 제거
  • -replace=old@version=new@version: 교체 의존성 추가
  • -dropreplace=old@version: 교체 의존성 제거
  • -retract=version: 버전 롤백 항목 추가
  • -dropretract=version: 버전 롤백 항목 제거

표시를 위한 다른 매개변수도 있습니다.

  • -print: 파일 내용 출력
  • -json: JSON 형식으로 출력

다음은 예시입니다.

$ go mod edit -print
module golearn

go 1.21.3

require (
        github.com/dstgo/task v1.2.0
        github.com/spf13/cast v1.5.1
        github.com/swaggo/swag v1.16.2
        golang.org/x/net v0.19.0
        gorm.io/gorm v1.25.5
)

graph

sh
$ go help mod graph
usage: go mod graph [-go=version] [-x]

graph 명령은 현재 프로젝트의 의존성 그래프를 출력하며, 가독성이 매우 나쁘며 대부분의 경우 사람이 읽기 위한 것이 아닙니다. 결과는 일반적으로 처리되어 시각적 형식으로 표시됩니다. 각 행은 의존성이며 형식은 다음과 같습니다.

인용자  피인용자

예를 들어:

golearn go@1.21.3

두 가지 매개변수를 지원합니다.

  • -go=version: 지정된 go 버전으로 의존성 그래프 로드, 값은 go.mod 파일의 버전보다 작을 수 없습니다.
  • -x: 과정에서 실행된 명령 표시.

간단한 사용 예를 보겠습니다.

sh
$ go mod graph
golearn github.com/246859/containers@v0.0.1
golearn github.com/246859/river@v0.1.0
golearn github.com/Jleagle/steam-go@v0.0.0-20230725082712-1053b441b1f2
golearn github.com/Jleagle/unmarshal-go@v0.0.0-20210227002040-694f544f9265
golearn github.com/KyleBanks/depth@v1.2.1
golearn github.com/Microsoft/go-winio@v0.6.1
golearn github.com/PuerkitoBio/purell@v1.1.1
golearn github.com/PuerkitoBio/urlesc@v0.0.0-20170810143723-de5bf2ad4578
golearn github.com/andeya/ameda@v1.5.3
golearn github.com/andeya/goutil@v1.0.1
golearn github.com/asaskevich/govalidator@v0.0.0-20230301143203-a9d515a09cc2
golearn github.com/buger/jsonparser@v1.1.1
golearn github.com/bwmarrin/snowflake@v0.3.0
golearn github.com/bytedance/go-tagexpr/v2@v2.9.11
......

vendor

sh
$ go help mod vendor
usage: go mod vendor [-e] [-v] [-o outdir]

vendor 는 gomod 가 출시되기 전 gopath 의 대체方案이었으며, 모든 go 프로젝트 아래에는 vendor 디렉토리가 있으며, domain/user/project 형식으로 각 프로젝트의 의존성을 별도로 저장합니다. 마치 옆집 nodeJs 의 비대해진 node_module 처럼 각 프로젝트의 의존성을 분리하여 저장합니다. 이러한 의존성 관리 방식은 현재 보면 정말 어리석어 보이지만, 당시에는 더 나은方案이 없었습니다. vendor 를 유지하는 이유는 go 가 아래로 호환성을 유지하겠다는 약속을 지키기 때문이며, 일부 오래된 프로젝트를 포함하여 go 소스 코드 안에서도 아직 vendor 를 사용할 수 있습니다.

본론으로 돌아가서, vendorgo mod 의 하위 명령으로, 현재 모듈이 참조하는 전역 의존성을 vendor 디렉토리에 내보낼 수 있습니다.

sh
$ go mod vendor -h
usage: go mod vendor [-e] [-v] [-o outdir]
Run 'go help mod vendor' for details.

다음 매개변수가 있습니다.

  • -o: 출력 경로 폴더 지정
  • -v: 각 의존성 출력
  • -e: 오류 발생 시 종료하지 않고 계속 진행

다음은 예시로, 먼저 go list -m all 로 현재 프로젝트가 참조하는 의존성을 확인합니다.

sh
$ go list -m all
github.com/dstgo/task
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.5.0
github.com/stretchr/testify v1.8.4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/yaml.v3 v3.0.1

현재 vendor 디렉토리에 내보냅니다.

sh
$ go mod vendor -v -e -o vendor
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.8.4
## explicit; go 1.20
github.com/stretchr/testify/assert
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3

내보낸 후의 디렉토리 구조는 다음과 같습니다.

└─vendor
    ├─github.com
    │  ├─davecgh
    │  │  └─go-spew
    │  │      └─spew
    │  ├─pkg
    │  │  └─errors
    │  ├─pmezard
    │  │  └─go-difflib
    │  │      └─difflib
    │  └─stretchr
    │      └─testify
    │          └─assert
    └─gopkg.in
    |    └─yaml.v3
    |
    |--modules.txt

그 중 modules.txt 는 모든 의존성 항목을 설명하는 파일로, 현재의 go.mod 와 유사합니다.

verify

sh
$ go help mod verify
usage: go mod verify

이 명령은 프로젝트의 의존성이 로컬에 다운로드된 후 수정되었는지 확인합니다. 예를 들어, 문제가 없으면 all modules verified 를 출력합니다.

sh
$ go mod verify
all modules verified

그렇지 않으면 어디가 변경되었는지 보고하며 비정상 상태로 명령을 종료합니다. 예를 들어:

sh
$ go mod verify
gorm.io/gorm v1.25.5: dir has been modified (/go/mod/libs/gorm.io/gorm@v1.25.5)

why

$ go help mod why
usage: go mod why [-m] [-vendor] packages...

왜 이 패키지에 의존하는지 설명하며, 실제로는 해당 의존성 그래프를 출력합니다. 예를 들어:

sh
$ go mod why gorm.io/gorm
# gorm.io/gorm
golearn
gorm.io/gorm

기본적으로 main 의 가져오기만 해석하며, -m 매개변수를 추가하면 각 패키지의 가져오기 상황을 분석할 수 있습니다.

work

work 명령은 go 다중 모듈 관리를 위한 로컬 개발 도구입니다.

bash
$ go work help
Work provides access to operations on workspaces.

Note that support for workspaces is built into many other commands, not
just 'go work'.

The commands are:

        edit        edit go.work from tools or scripts
        init        initialize workspace file
        sync        sync workspace build list to modules
        use         add modules to workspace file
        vendor      make vendored copy of dependencies

Use "go help work <command>" for more information about a command.

init

init 하위 명령은 workspace 를 초기화하는 데 사용되며, go.work 라는 파일을 생성합니다.

bash
$ go work init -h
usage: go work init [moddirs]
Run 'go help work init' for details.

[moddirs] 매개변수를 받아 어떤 모듈을 관리에 포함할지 지정합니다. 예를 들어:

bash
$ go work init ./service ./api

use

use 하위 명령은 go.work 에 관리에 포함될 모듈 디렉토리를 추가하는 데 사용됩니다.

bash
$ go help work use
usage: go work use [-r] [moddirs]

Use provides a command-line interface for adding
directories, optionally recursively, to a go.work file.

[moddirs] 를 매개변수로 받으며, -r[moddirs] 경로에서 모듈을 재귀적으로 검색함을 나타냅니다. 예를 들어:

bash
$ go work use -r ./oss-api ./multi_modules

edit

edit 하위 명령의 역할은 go mod edit 와 동일하며, 모두 명령줄 인터페이스를 다른 도구와 스크립트에 제공합니다.

$ go help work edit
usage: go work edit [editing flags] [go.work]

Edit provides a command-line interface for editing go.work,
for use primarily by tools or scripts. It only reads go.work;
it does not look up information about the modules involved.
If no file is specified, Edit looks for a go.work file in the current
directory and its parent directories

매개변수는 다음과 같습니다.

  • -fmt: go.work 파일 포맷팅

  • -use, -dropuse: 모듈 경로 추가 및 제거

  • -replace=old[@v]=new[@v], -dropreplace=old[@v]=new[@v]: 교체할 모듈 추가 및 제거

  • -go, -toolchain=name: go 버전 지정 및 사용할 도구 체인 지정

  • -print: 마지막 수정 사항을 출력하며 파일에 다시 쓰지 않음

  • -json: json 형식으로 출력하며 -print 와 동시에 존재할 수 없음, 해당 타입 구조체는 다음과 같습니다.

    go
    type GoWork struct {
            Go        string
            Toolchain string
            Use       []Use
            Replace   []Replace
    }
    
    type Use struct {
            DiskPath   string
            ModulePath string
    }
    
    type Replace struct {
            Old Module
            New Module
    }
    
    type Module struct {
            Path    string
            Version string
    }

다음은 몇 가지 사용 예시입니다. 포맷팅 출력:

bash
$ go work edit -fmt -print
go 1.22.0

use (
        ./ab/cd
        ./auth
        ./user
)

json 출력:

bash
$ go work edit -fmt -json
{
        "Go": "1.22.0",
        "Use": [
                {
                        "DiskPath": "./ab/cd"
                },
                {
                        "DiskPath": "./auth"
                },
                {
                        "DiskPath": "./user"
                }
        ],
        "Replace": null
}

sync

sync 하위 명령은 go.work 의 모듈 목록을 workspace 의 각 모듈로 되돌리는 데 사용됩니다.

bash
$ go help work sync
usage: go work sync

Sync syncs the workspace's build list back to the
workspace's modules

이 과정은 주로 로컬 개발完成后 각 모듈이 발행 작업을 완료한 후 발생하며, 이때 sync 를 사용하면 각 모듈의 의존성 관계에 따라 workspace 의 모든 모듈의 go.mod 에 있는 의존성을 업데이트하므로 수동으로 업데이트할 필요가 없습니다.

vendor

vendor 명령은 workspace 의 모든 모듈이 의존하는 라이브러리의 복사본을 vendor 디렉토리에 만듭니다.

bash
$ go work help vendor
usage: go work vendor [-e] [-v] [-o outdir]

기능은 go mod vendor 와 동일하므로 더 이상 자세히 설명하지 않습니다.

vet

vet 명령은 go 언어 소스 코드의 정적 오류 검사 도구로, 다른 언어의 lint 도구 (예: Eslint) 와 유사합니다.

sh
$ go vet -h
usage: go vet [build flags] [-vettool prog] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.

먼저 간단한 예를 보겠습니다. 다음 소스 코드가 있습니다.

sh
$ cat main.go
package main

import "fmt"

func main(){
        fmt.Println("hello world!"
}

동일한 디렉토리에서 매개변수 없이 go vet 을 실행합니다.

sh
$ go vet
vet: ./main.go:6:28: missing ',' before newline in argument list (and 1 more errors)

vet 은 어떤 파일의 어떤 행에 어떤 문제가 있는지 보고합니다. 빌드 플래그 (예: -n-x) 를 매개변수로 지원하며, 패키지, 폴더, 파일 이름을 매개변수로 받습니다.

sh
$ go vet .
$ go vet main.go
$ go vet ./cmd
$ go vet runtime

다음 명령으로 더 자세한 매개변수와 설명을 확인할 수 있습니다.

sh
$ go tool vet help
vet is a tool for static analysis of Go programs.

vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.

Registered analyzers:

    asmdecl      report mismatches between assembly files and Go declarations
    assign       check for useless assignments
    atomic       check for common mistakes using the sync/atomic package
    bools        check for common mistakes involving boolean operators
    buildtag     check //go:build and // +build directives
    ......

go tool vet 명령은 코드를 직접 검사하는 데 사용할 수 없으며 go vet 을 사용해야 합니다. go vet 매개변수의 [vet flag] 는 코드 분석기를 설정하는 것을 지원하며, 사용 가능한 값은 다음과 같습니다.

asmdecl      어셈블리 파일이 go 선언과 일치하지 않는지 확인
assign       무용한 변수 할당 확인
atomic       sync/atomic 사용 시 원자성을 파괴했는지 확인
bools        논리 연산자 오류 사용 확인
buildtag     build tag 확인
cgocall      cgo 포인터 전달 규칙 위반 동작 확인
composites   초기화되지 않은 복합 구조 (예: map, chan) 확인
copylocks     락 값 복사 발생 확인
directive     go 도구 체인 지시문 확인
errorsas      errors.As 에 비포인터 타입 또는 비 error 타입 값 전달 확인
framepointer  컴파일 최적화된 어셈블리 코드가 프레임 포인터를 저장하기 전에 클리어했는지 확인
httpresponse  httpresponse 오류 사용 확인
ifaceassert   인터페이스에서 인터페이스 타입 단정 확인
loopclosure   루프 변수 참조 문제
lostcancel    context.WithCancel 이 cancel 함수를 사용하지 않음
nilfunc       함수와 nil 간 무용한 비교 확인
printf        printf 포맷 매개변수 올바른지 확인
shift         정수 너비와 같거나 초과하는 시프트 확인
sigchanyzer   버퍼 없는 chan os.Signal 확인
slog          불법 구조화 로그 호출 확인
stdmethods    알려진 인터페이스 메서드 서명 올바른지 확인
stringintconv 문자열 정수 변환 확인
structtag     구조체 tag 올바른지 확인
testinggoroutine   테스트에서 코루틴으로 testing.Fatal 호출 확인
tests         테스트 및 예제 일반 오류 사용 확인
timeformat    (time.Time).Format 또는 time.Parse 의 시간 형식 올바른지 확인
unmarshal     unmarshal 에 비포인터 또는 비인터페이스 타입 전달 확인
unreachable   도달할 수 없는 코드 확인
unsafeptr     uintptr 에서 unsafe.Pointer 로不正确 변환 확인
unusedresult  사용하지 않는 함수 반환값 확인

이들은 모두 특정 지점을 분석하는 분석기로, 예를 들어 timeformat 분석기는 time.Format 호출 형식이 올바른 문법과 일치하는지 확인합니다. 기본적으로 위의 모든 분석기가 활성화되며,单独로 활성화하려면 다음 형식을 사용할 수 있습니다.

sh
$ go vet -timeformat main.go

单独로 비활성화:

sh
$ go vet -timeformat=false main.go

이러한 분석기의 소스 코드는 cmd/vendor/golang.org/x/tools/go/analysis/passes 에 위치하며, 각 분석기는 go 언어가 쉽게 범할 수 있는 함정입니다. 따라서 vet 명령으로 코드를 검사하는 것을 강력히 권장합니다. 이 외에도 몇 가지 다른 플래그 매개변수를 지원합니다.

  • -V: 버전만 출력하고 종료
  • -json: JSON 형식으로 출력
  • -c=n: 컨텍스트에서 지정된 수의 충돌 행 표시 (아무런 효과도 없는 것 같습니다)

외부 분석기도 있습니다. 예를 들어 shadows 는 짧은 변수 이름의 변수 숨김 문제를 감지하며, 외부이므로 go install 로 다운로드해야 합니다.

sh
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

사용 형식은 다음과 같습니다.

sh
$ go vet -vettool=$(which shadow)

test

sh
$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.

test 명령은 go 언어 도구 체인에서 테스트 기능을 제공하는 명령으로, 이 기능은 매우 중요합니다. 소프트웨어而言, 완벽한 테스트는 필수적입니다. 여기서는 test 명령 사용법에 대해 간단히 소개하며, 테스트 관련 자세한 내용은 테스트 를 참조하세요.

build 명령의 컴파일 매개변수 외에도 test 는 다음 매개변수를 지원합니다.

  • -args: 프로그램 진입 매개변수
  • -c: 현재 패키지의 테스트 바이너리 파일을 현재 디렉토리에 컴파일하지만 실행하지 않음, pkg.test 방식으로 명명
  • -exec: 테스트 시작 전에 다른 명령 실행
  • -json: 테스트 출력 스타일을 json 으로 변경
  • -o: 테스트 바이너리 파일 경로 이름 지정

또한 많은 testflag 를 지원하며, help 명령으로 모든 testflag 를 확인할 수 있습니다.

sh
$ go help testflag
`go test` 命令既接受作用于 `go test` 本身的标志,
也接受作用于生成的测试二进制文件的标志。

`go test` 命令识别以下标志,并用于控制任何测试的执行:
        -bench regexp
        -benchtime t
        -count n
    ......

다음은 몇 가지 일반적으로 사용되는 것입니다.

  • -v: 각 테스트 케이스의 테스트 결과 출력.
  • -timeout duration: 테스트 실행 초과 시간
  • -skip regexp: 지정된 테스트 케이스 건너뛰기
  • -short: 실행 시간이 긴 테스트 케이스 실행 시간 단축
  • -shuffle: 모든 테스트 케이스 실행 순서 섞기
  • -run regexp: 지정된 테스트 케이스 실행
  • -list regexp: 각 테스트 케이스 나열
  • -cpu 1,2,4: cpu 수 지정
  • -count n: 각 테스트 케이스 실행 횟수 지정

가장 간단한 사용법은 매개변수 없이 실행하는 것으로, 현재 패키지의 모든 테스트 케이스를 실행하고 결과를 출력합니다.

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

특정 테스트 파일 지정:

sh
$ go test hello_test.go
ok      command-line-arguments  0.041s

-v 매개변수를 추가하면 더 자세한 출력을 확인할 수 있으며, 이는 매우 일반적으로 사용됩니다.

sh
$ go test -v hello_test.go
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      command-line-arguments  0.041s

특정 테스트 케이스 지정:

sh
$ go test -v -run TestHello
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      golearn 0.028s

테스트 시 test 명령에는 두 가지 모드가 있습니다. 먼저 폴더 모드에 대해 설명하겠습니다. package 매개변수 없이 test 명령을 실행하면 폴더 모드로 실행됩니다. 예를 들어 다음 명령들이 있습니다.

sh
$ go test
$ go test -v

이 모드에서는 테스트 캐시가 비활성화됩니다. 다른 모드는 목록 모드로, package 매개변수가 비어 있지 않으면 목록 모드로 테스트합니다. 이전과의 차이점은 테스트 캐시 활성화 여부입니다. 예를 들어 다음 명령들이 있습니다.

sh
$ go test -v .
$ go test -v ./...
$ go test .
$ go test -v net/http

목록 모드에서 go 는 지정된 패키지의 각 패키지의 테스트 파일을 바이너리 파일로 컴파일하여 실행합니다. 테스트 반복 실행을 피하기 위해 go 는 기본적으로 결과를 캐시하며, 두 번째 실행 시 다시 컴파일하지 않습니다. 다음 매개변수를 사용할 때 기본적으로 캐시가 활성화됩니다.

  • -benchtime
  • -cpu
  • -list
  • -parallel
  • -run
  • short
  • -timeout
  • -failfast
  • -v

이러한 매개변수 외의 다른 매개변수를 사용하면 캐시를 끌 수 있으며, 공식에서 권장하는 방법은 -count=1 을 사용하여 캐시를 비활성화하는 것입니다. 예를 들어:

sh
$ go test -v -count=1 ./...

지시문

명령과 달리 go 의 지시문은 소스 파일에 하드코딩 형태로 존재하며, 또 다른 전문적인 이름인 컴파일 지시 (progma directives) 라고도 합니다.

컴파일러와 링커는 이로 인해 동작을 변경하여 컴파일을 제어하는 효과를 얻으며, c 언어의 매크로와 유사합니다. 물론 모든 지시문이 컴파일에 영향을 미치는 것은 아니며, 일부는 다른 기능적 동작 (예: generate 지시문은 일반적으로 코드 생성 기능에 사용됨) 에 사용됩니다. 이러한 지시문은 일반적으로 주석 형태로 존재하며, //go: 를 접두사로 하며 중간에 공백을 포함할 수 없습니다. 예를 들어 //go:generate 지시문입니다. 모든 지시문 유형은 총 두 가지로 나뉩니다.

  • 기능적 지시문: 이는 go 에서 제공하는 기능적 지시문으로 자유롭게 사용할 수 있습니다. 예를 들어 generate, embed, build 가 있습니다.
  • 컴파일러 지시문: 이는 신중하게 사용해야 하며, 함부로 사용하면 예측할 수 없는 결과가 발생할 수 있습니다.

기능적 지시문을 제외한 대부분의 지시문은 함수 서명에서만 작동할 수 있습니다. 컴파일러 지시문의 경우 go doc compile 명령을 실행하여 지시문을 확인할 수 있습니다. 모든 지시문의 경우 cmd/compile/internal/ir/node.go: 440 에서 관련 정보를 찾을 수 있습니다.

generate

sh
$ go help generate
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

generate 지시문은 이름에서 알 수 있듯이 생성과 관련되며, 일반적으로 코드를 생성하고 소스 코드를 업데이트하는 명령을 실행하는 데 사용되지만, 실제로는 모든 명령을 실행할 수 있습니다. 또한 generate 지시문은 다른 지시문과 달리 소스 파일에 있는 모든 generate 지시문을 실행하는 전용 명령이 있습니다. 파일 이름이나 패키지 이름을 입력 매개변수로 사용하여 어떤 파일의 generate 지시문을 실행할지 나타낼 수 있습니다. 다음은 다른 매개변수입니다.

  • -run=regex: 지정된 generate 지시문 실행
  • -skip=regex: 지정된 generate 지시문 건너뛰기
  • -n: 실행될 명령 출력
  • -x: 과정에서 실행된 명령 출력
  • -v: 처리된 파일 출력

또한 generate 지시문에서 실행되는 명령은 다음 내장 매개변수를 지원합니다.

  • $GOARCH: cpu 아키텍처
  • $GOOS: 운영 체제
  • $GOFILE: 파일 이름
  • $GOLINE: 행 번호
  • $GOPACKAGE: 패키지 이름
  • $GOROOT: go root
  • $DOLLAR: 달러 기호
  • $PATH: path 환경 변수

예를 보겠습니다. 코드 없이 주석 한 줄만 있습니다.

go
package main

//go:generate echo "hello world!"

명령 실행

$ go generate .
hello world!

이 예는 go 명령을 실행합니다.

go
package main

//go:generate go version

명령 실행

sh
$ go generate .
go version go1.21.3 windows/amd64

generate 지시문은 swagger 로 API 문서 생성 또는 Wire 로 IOC 코드 생성 등 모든 명령을 실행하는 데 사용할 수 있습니다. 그러나 이 지시문은 너무 복잡한 명령을 실행하는 데 적합하지 않으며, 간단한 명령을 실행하는 데 적합합니다. 복잡한 요구 사항이 있으면 스크립트나 makefile 을 대체할 수 있습니다.

embed

embed 지시문은 1.16 에서 새로 추가되었으며, 정적 파일을 바이너리 파일에 함께 패키징할 수 있습니다. 예를 들어 HTML 템플릿과 같은 것입니다. 형식은 다음과 같습니다.

go
//go:embed pattern

pattern 은 glob 표현식일 수 있으며, 폴더나 특정 파일일 수도 있습니다. 예를 보겠습니다.

go
package main

import "embed"

//go:embed *
var static embed.FS

embed 지시문은 반드시 embed.Fs 타입의 전역 변수 위에 있어야 하며, 주의해야 할 점은 반드시 전역 변수여야 하며, 이를 사용하려면 embed 패키지를 가져와야 합니다. 이 예에서 * 는 현재 폴더의 모든 파일을 바이너리 파일에 패키징함을 나타내지만, . 로 시작하는 폴더는 허용되지 않습니다.

다음 예는 임베드된 파일에서 내용을 읽는 것을 보여줍니다.

go
package main

import (
  "embed"
  "fmt"
)

//go:embed *.txt
var static embed.FS

func main() {
  bytes, err := static.ReadFile("hello.txt")
  if err != nil {
    panic(err)
  }
  fmt.Println(string(bytes))
}

세 가지 메서드만 있으며, 일반적인 파일 시스템과 다르게 사용할 수 없습니다. 또한 io/Fs 인터페이스를 구현했으므로 Fs 객체로 전달할 수도 있습니다.

go
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadFile(name string) ([]byte, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

다음 예는 embed 지시문으로 html 파일을 임베드하고 http 서비스로 액세스하는 것을 보여줍니다.

go
package main

import (
  "embed"
  "net/http"
)

//go:embed index.html
var htmlFs embed.FS

func main() {
  http.Handle("/", http.FileServer(http.FS(htmlFs)))
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

액세스 결과는 다음과 같습니다.

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

embed 지시문은 전역 변수 타입을 []byte 로 할 수도 있습니다. 다음은 예시입니다.

go
package main

import (
  _ "embed"
  "net/http"
)

//go:embed index.html
var rawdata []byte

func main() {
  http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    writer.Write(rawdata)
  })
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

이전 예와 비슷한 효과를 구현합니다.

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

build

build-컴파일 제어 부분에서 // +build 지시문으로 컴파일 동작을 제어하는 것에 대해 설명했습니다. //go:build 지시문은 1.17 에서 새로 추가되었으며 이전 지시문을 대체하는 것을 목표로 했지만, 이제 1.21 이 되었는데도 아직 대체되지 않았습니다. 아마도 공존 방식으로 존재할 것으로 예상됩니다. 이 새로운 지시문에 대해 공식 문서에도 소개가 있습니다: build constraints. 기능은 이전과 차이가 없지만 문법이 더 엄격하며 불리언 표현식을 지원합니다. 예를 보겠습니다.

go
//go:build (linux && 386) || (darwin && !cgo)

package pkg_name

이 방식은 이전 방식보다 가독성이 훨씬 높습니다.

line

line 지시문은 다음 행의 행 번호, 열 번호 및 파일 이름에 영향을 미치며, 역할은 이에 한정되며 대부분 오류 등을 디버깅하는 데 사용될 수 있습니다. 예를 들어 오류 발생 시 컴파일러가 출력하는 정보를 변경합니다.

go
package main

var a undefinedType

func main() {

}

일반적으로 컴파일러는 다음을 출력합니다.

.\main.go:3:7: undefined: undefinedType

하지만 line 지시문을 사용하면 다릅니다.

go
package main

//line abc.go:10:100
var a undefinedType

func main() {

}

그러면 출력은 다음과 같습니다.

abc.go:10:106: undefined: undefinedType

또한 역사적 이유로 인해 line 지시문은 다른 지시문과 사용법이 다른 유일한 지시문입니다. 형식은 다음과 같습니다.

go
//line filename:line:column

다른 지시문과 달리 go: 를 접두사로 사용하지 않아도 됩니다.

linkname

이 지시문의 작업은 다른 패키지의 함수나 전역 변수를 링크할 수 있으며, 비공개 타입이라도 가능합니다. 이러한 작업은 표준 라이브러리, 특히 runtime 에서 자주 나타나며, 일부 함수는 함수 본문이 없으며 이러한 방식으로 구현됩니다. 또 다른 일부 빈 함수 본문의 함수는 어셈블리로 구현됩니다. 사용법을 보겠습니다. 사용 형식은 다음과 같습니다.

go
//go:linkname 링크 타입 이름 링크될 타입

사용 전에 예를 들어 unsafe 패키지를 가져와야 합니다. 표준 라이브러리의 비공개 타입을 링크하는 간단한 예를 보겠습니다.

go
import (
  "fmt"
  "unsafe"
)

//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

func MemHash(data []byte) uint64 {
  ptr := unsafe.Pointer(unsafe.SliceData(data))
  return uint64(memhash(ptr, 0, uintptr(len(data))))
}

func main() {
  fmt.Println(MemHash([]byte("hello")))
}

출력

15395306441938000233

runtime.memhash 라는 비공개 함수를 우리가 선언한 함수와 링크했습니다. 이 함수는 함수 본문이 없고 서명만 있으며 운반체 역할만 합니다. memhash 의 역할은 포인터, 해시 시드 및 메모리 오프셋을给定하여 메모리를 기반으로 해시 값을 계산하는 것입니다. 이 링크 과정은 컴파일 시기에 완료됩니다.

표준 라이브러리가 아닌 경우 상황이 다소 다릅니다. 예를 들어 example 패키지에 test 함수가 있는 경우, 링크 전에 먼저 이 패키지를 익명으로 가져와야 합니다.

go
package example

// 비공개 타입, 외부에서 액세스할 수 없습니다.
func test() string {
  return "a"
}
go
package main

import (
  "fmt"
  _ "golearn/example"
  _ "unsafe"
)

//go:linkname test golearn/example.test
func test() string

func main() {
  fmt.Println(test())
}

출력

a

링크가 성공했음을 볼 수 있습니다. 이 방법은 go 모듈 시스템을 우회하여 원하는 대로 할 수 있지만, 대규모로 사용하는 것은 권장되지 않습니다. 자신이 무엇을 하는지 알고 있는 경우가 아니라면요.

noinline

noinline 지시문은 함수가 매우 간단하더라도 인라인 최적화를 금지함을 나타냅니다. 간단한 예를 보겠습니다.

go
package main

func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

val 은 매우 간단한 함수로, 문자열 리터럴을 반환하는 역할을 합니다. 너무 간단하고 결과가 항상 예측 가능하기 때문에 컴파일 시 다음 형식으로 최적화됩니다.

go
package main

func main() {
  var c = "val"
  _ = c
}

어셈블리 형태를 보면 val 함수 호출이 없음을 알 수 있습니다.

TEXT    main.val(SB), NOSPLIT|NOFRAME|ABIInternal, $0-0
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
LEAQ    go:string."val"(SB), AX
MOVL    $3, BX

FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
RET

다음으로 noinline 지시문을 추가합니다.

go
package main

//go:noinline
func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

어셈블리 형태를 다시 보면

CMPQ    SP, 16(R14)
PCDATA  $0, $-2
JLS     17
PCDATA  $0, $-1
PUSHQ   BP
MOVQ    SP, BP
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
PCDATA  $1, $0
CALL    main.val(SB)
POPQ    BP
RET

이번에는 main.val 호출이 매우 명확하게 보이며, 이는 noinline 지시문이 수행하는 기능으로, 컴파일러 최적화 시 함수 인라인을 방지합니다.

nosplit

nosplit 지시문은 스택 오버플로 감지를 건너뜁니다. go 의 동시성 스케줄링 모델이 선점형 스케줄링이기 때문에, 함수가 매우 낮은 수준의 코드를 실행하고 다른 코루틴이 이 함수를 호출할 때 선점하기에 적합하지 않은 경우 이 지시문을 사용하여 나타낼 수 있습니다.

go
//go:nosplit
func nosplitFn()

이 지시문을 사용하면 더 이상 스택 증가가 수행되지 않습니다.

noescape

noescape 는 이름에서 쉽게 추측할 수 있듯이 이스케이프와 관련이 있으며, 현재 함수에서 메모리 이스케이프 동작이 발생하지 않음을 나타냅니다. 실행完成后 모든 리소스가 회수되며, 이 함수는 반드시 서명만 있고 함수 본문이 없어야 합니다. 이 경우 일반적으로 함수 구현은 어셈블리로 구현됩니다.

예를 들어 이전에 사용한 memhash 는 이 지시문을 사용합니다.

go
//go:noescape
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

이렇게 하면 컴파일러가 이스케이프 분석을 수행하지 않으며, 전제는 이스케이프가 발생하지 않는다는 것을 보장해야 합니다. 이스케이프가 발생하면 어떤 결과가 발생할지 알 수 없습니다.

uintptrescapes

uintptrescapes 지시문은 해당 함수에서 uintptr 타입의 매개변수가 포인터 값으로 변환되어 힙으로 이스케이프되었으며, 이를 계속存活시켜야 함을 나타냅니다. 이 지시문은 일반적으로 일부 낮은 수준의 시스템 호출에 사용되며, 대부분의 경우 알아볼 필요가 없습니다.

go
//go:uintptrescapes
func nosplit(ptr uintptr) uintptr

예전에는 notinheaps 라는 지시문이 있어 타입이 힙에 메모리를 할당할 수 없음을 나타냈지만, 어느 버전에서 삭제되었는지 알 수 없습니다.

norace

norace 지시문은 함수의 메모리 액세스가 더 이상 경합 분석이 필요하지 않음을 나타내며, 일반적으로 낮은 수준의 코드를 실행할 때 경합 분석을 수행하기에 적합하지 않은 경우 사용됩니다.

go
//go:norace
func low_level_code(ptr uintptr) uintptr

TIP

일부 지시문은 runtime 패키지에서만 사용할 수 있도록 제한되어 있으며, 외부에서는 사용할 수 없습니다. 이들은 더 깊은 것과 관련이 있으며, Runtime-only compiler directives 에서 관련 소개를 볼 수 있습니다.

Golang by www.golangdev.cn edit