원본 : https://golang.org/doc/code
이 문서는 module내에서 간단한 Go 패키지의 개발을 보여주고 Go 모듈, 패키지 및 명령을 가져오고 빌드하고 설치하는 표준 방법인 go tool을 소개한다.
참고 : 이 문서에서는 Go 1.13 이상을 사용하고 있고 GO111MODULE 환경변수가 설정되어 있지 않다고 가정한다.
go 프로그램은 패키지로 구성된다. 패키지는 함께 컴파일되는 동일한 디렉토리에 있는 소스 파일의 모음이다. 동일한 패키지내에서는 하나의 소스 파일에 정의된 함수, type, 변수, 상수는 다른 모든 소스 파일에서도 볼 수 있다.
repository는 하나 이상의 module을 포함한다. module은 함께 릴리즈 되는 관련 Go package의 모음이다. Go repository는 일반적으로 repository의 root에 있는 하나의 module만 포함한다. go.mod 라는 파일이 모듈 path를 선언한다. module path : 모듈 내의 모든 패키지에 대한 import path prefix. 모듈은 go.mod 파일이 포함된 디렉토리의 패키지와 해당 디렉토리의 하위 디렉토리, 다른 go.mod파일(있는 경우)이 포함된 다음 하위 디렉토리까지 포함한다.
코드를 빌드할 수 있기 전에 원격 repository에 publish할 필요가 업다. 모듈은 repository에 속하지 않고 로컬로 정의될 수 있다. 그러나 언젠가 publish할 것처럼 코드를 구성하는 것은 좋은 습관이다.
각 모듈의 path는 패키지에 대한 import path prefix로 역할을 할 뿐만아니라 go 커맨드가 다운로드 할 위치를 나타낸다. 예를 들어, golang.org/x/tools 모듈을 다운로드하기 위해 go 커맨드는 https://golang.org/x/tools에 지정된 repository를 참조한다.
import path는 package를 import하기 위해 사용되는 string이다. package의 import path는 모듈 내의 하위 디렉토리와 결합된 그것의 module path 이다. 예를 들어, module github.com/google/go-cmp 모듈은 cmp/ 디렉토리에 패키지를 포함한다. 해당 패키지의 import path는 *github.com/google/go-cmp/cmp이다. 표준 라이브러리 패키지에는 모듈 path prefix가 없다.
간단한 프로그램을 컴파일하고 실행하기 위해, 먼저 module path를 선택하라. (여기에서는 example.com/user/hello)를 사용할 것이다. 그리고 그것을 선언하는 go.mod 파일을 만들어라.
$ mkdir hello
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello
go 1.16
go 소스 파일의 첫 번째 구문은 반드시 package name 이어야 한다. 실행 가능한 명령은 반드시 main 패키지를 사용하여야 한다.
다음으로, hello.go 파일을 아래 내용과 함께 해당 디랙토리에 만든다.
package main
import "fmt"
func main() {
fmt.Println("Hello, world.")
}
이제 go tool을 사용해서 해당 프로그램을 빌드와 install할 수 있다.
$ go install example.com/user/hello
이 명령어는 실행가능한 바이너리를 만들면서, hello 커맨드를 빌드한다. 그런 다음 그 바이너리를 $HOME/go/bin/hello(윈도우라면 %USERPROFILE%\go\bin\hello.exe)에 설치한다.
$ ls -al ~/go/bin
-rwxr-xr-x 1 username staff 2023776 3 15 21:14 hello
go env 커맨드를 사용하여 향후 go 커맨드에 대한 환경변수의 디폴트값을 portable하게 세팅할수 있다.
$ go env -w GOBIN=/somewhere/else/bin
go env -w 로 세팅된 값을 원래대로 돌리려면 go env -u를 사용한다.
$ go env -u GOBIN
go install과 같은 커맨드는 현재 작업 디렉토리를 포함하는 모듈 컨텍스트 내에서 적용된다. 만약 작업 디렉토리가 example.com/user/hello 모듈 내에 없으면 go install이 실패할 수 있다.
편의를 위해 go 커맨드는 작업 디렉토리에 상대적인 경로를 허용하고 다른 경로가 제공되지 않으면 현재 작업 디렉토릐의 패키지를 기본값으로 사용한다. 따라사 작업 디렉토리에서 아래 커맨드는 모두 동일하다.
$ go install example.com/user/hello
$ go install .
$ go install
다음으로, 이 프로그램이 작동되는지 실행해보자. 편의를 위해 바이너리를 쉽게 실행할 수 있도록 PATH에 install 디렉토리를 추가한다.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.
소스 컨트롤 시스템을 사용한다면, repository를 초기화할 좋은 때이다. 파일을 추가하고 첫번째 커밋을 한다. 다시 말하지만 이 과정은 옵션이다. go code를 짤 때, 소스컨트롤이 꼭 필요하지는 않다.
$ git init
$ git add .
$ git commit -m "inital commit"
go 커맨드는 해당 HTTPS URL을 요청하고 HTML response에 포함된 메타데이터를 읽어 주어진 모듈 path가 포함된 repository를 찾는다.(go help importpath 참조) 많은 호스팅 서비스는 이미 Go 코드가 포함된 repository에 해당 메타데이터를 제공하므로 다른 사용자가 모듈을 사용할 수 있도록 만드는 가장 쉬운 방법은 일반적으로 모듈 경로를 저장소의 URL과 일치시키는 것입니다.
morestrings 패키지를 작성하고 hello 프로그램에서 사용해보자. 먼저, $HOME/hello/morestrings라는 패키지에 대한 디렉토리를 만든 다음 해당 디렉토리에 아래의 내용으로 reverse.go라는 파일을 만든다.
// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings
// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
ReverseRunes 함수가 대문자로 시작했기 때문에 export 되었고, 다른 패키지에서 morestrings 패키지를 import하면 사용할 수 있다.
go build로 package 컴파일을 테스트 해보자.
$ cd $HOME/hello/morestrings
$ go build
이것은 output 파일을 생성하지 않는다. 대신 컴파일된 패키지를 빌드캐시에 저장한다.
morestrings 패키지가 빌드되었는지 확인한 후 hello 프로그램에서 사용하겠다. 이것을 위해서 $HOME/hello/hello.go를 수정하여 morestrings 패키지를 사용한다.
package main
import (
"fmt"
"example.com/user/hello/morestrings"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}
hello 프로그램을 install 한다
$ go install example.com/user/hello
새로운 버전의 프로그램을 실행한다. 다음과 같이 reversed된 메시지를 보아야 한다.
$ ~/go/bin/hello
Hello, Go!
"import path"는 git 또는 Mercurial과 같은 revision control system을 사용하여 패키지 소스 코드를 얻을 수 있는지 알려준다. go tool은 이 속성을 사용하여 remote repository에서 자동으로 패키지를 가져온다. 예를 들어 프로그램에서 github.com/google/go-cmp/cmp를 사용하려면:
package main
import (
"fmt"
"example.com/user/hello/morestrings"
"github.com/google/go-cmp/cmp"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}
이제 외부 모듈에 대한 dependency를 갖게 되었다. 해당 모듈을 다운로드 하고, go.mod 파일에 그것의 버전을 기록해야한다. go mod tidy 커맨드는 import된 package에 대해 없는 module requirements를 추가하고, 더이상 사용하지 않는 모듈에 대한 requirements는 제거한다.
$ go mod tidy
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.5.5
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.5
$ cat go.mod
module example.com/user/hello
go 1.16
require github.com/google/go-cmp v0.5.5
모듈 dependecy는 $GOPATH/pkg/mod 하위 디렉토리에 자동으로 다운로드 된다. 모듈의 특정 버전에 대해 다운로드한 내용은 해당버전이 필요한 다른 모든 모듈간에 공유되므로 go 커맨드는 해당 파일과 디렉토리를 읽기전용으로 표시한다. 다운로드 한 모든 모듈을 제거하려면 -modcache 플래그를 go clean과 함께 사용한다.
$ go clean -modcache
go에는 got test 커맨드와 testing 패키지로 구성된 가벼운 test framework가 있다.
signature func (t *testing.T)와 함께 TestXXX 함수를 포함하는 "_test.go"로 끝나는 이름의 파일을 생성하면 테스트를 작성할 수 있다. test framework은 이러한 각 func를 실행한다. 만약 함수가 t.Error 또는 t.Fail과 같은 실패 함수를 호출하면 테스트가 실패한 것으로 간주된다.
다음 Go 코드가 포함된 $HOME/hello/morestrings/reverse_test.go파일을 만들어 morfestrings 패키지에 테스트를 추가한다.
package morestrings
import "testing"
func TestReverseRunes(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := ReverseRunes(c.in)
if got != c.want {
t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
}
}
}
그리고 go test로 테스트 해본다.
$ go test
PASS
ok example.com/user/hello/morestrings 0.544s
더 자세한 사항은 "go help test"를 실행하거나 testing package documentation을 참조하라.