
개요
해당 글은 모듈에 있는 간단한 go 패키지를 개발하고, 기본적인 go 모듈, 패키지, 명령어 설치, 빌드 방법인 go tool에 대해서 소개한다.
주의할 점: 해당 글은 go 1.13 버전 이거나 그 이후버전일 경우를 가정하고, GO111MODULE 환경 변수가 설정되어 있지 않다고 가정한다.
코드 구조화
go 프로그램은 패키지 라고 하는 단위로 구조화되어 있다. 패키지 란, 한 디렉토리에 존재하며 함께 컴파일 되는 소스코드 묶음을 의미한다. 하나의 소스 파일안에 정의된 함수, 타입, 변수, 상수 들은 모두 같은 패키지에 속한 파일에서 접근이 가능하다.
한 레포지토리는 하나 또는 여러개의 모듈을 포함하고 있다. 여기서 모듈이란, 함께 릴리즈 되는 패키지들의 집합이라고 할 수 있다. go 레포지토리 하나는 보통 하나의 모듈만을 포함하고 있다. 모듈 path 를 명시하고 있는 파일을 go.mod 라고 한다. 이는 해당 모듈에 있는 모든 패키지들에 접근하기 위한 path prefix 이다. 모듈에는 go.mod 파일이 포함된 디렉토리의 패키지와 해당 디렉토리의 하위 디렉토리가 포함되며, 다른 go.mod 파일(있는 경우)이 포함된 다음 하위 디렉토리까지다.
주의할 것은, 코드를 등록하기 위해 원격 레포지토리에 넣을 필요 없이 그전에 코드를 빌드 가능하다는 것이다. 모듈은 한 레포지토리에 굳이 포함될 필요 없이 로컬로 정의가 가능하다. 하지만, 코드를 순조롭게 관리하려면 언젠가는 원격 레포지토리에 등록하는 것이 좋다.
각 모듈의 path 는 패키지를 임포트 할 때 경로를 제공해주는것 뿐만 아니라, go 커맨드가 해당 패키지를 다운로드하기 위해 찾아봐야할 위치를 알려준다. 예를 들어, golang.org/x/tools 라는 모듈을 다운로드 하기 위해서 go 커맨드는 https://golang.org/x/tools 해당 레포지토리를 탐색할 것이다.
import path 란 임포트를 할때 사용되는 특정 문자열을 의미한다. 패키지의 import path 는 자기 자신의 모듈 path 와 하위 디렉토리에 있는 모듈과 결합한 형태이다. 예를 들어, github.com/google/go-cmp 라는 모듈은 cmp/ 라고 하는 패키지를 포함하고 있으면, 해당 패키지의 import path 는 github.com/google/go-cmp/cmp 가 된다. 마지막으로, 표준 라이브러리는 모듈 path prefix 를 포함할 필요는 없다.
첫번째 프로그램
단일 프로그램을 컴파일 하고 실행하기 위해, 모듈 path 를 정의해야 하고 go.mod 파일을 생성해야 한다.
$ mkdir hello # Alternatively, clone it if it already exists in version control.
$ cd hello
$ go mod init example/user/hello
go: creating new go.mod: module example/user/hello
$ cat go.mod
module example/user/hello
go 1.16
go 소스 파일의 첫번째 줄은 패키지 선언이 되어야 한다. 실행이 필요한 명령은 package main 으로 정의해야 한다.
다음으로, hello.go 라는 파일을 생성하고 코드는 아래와 같이 정의한다.
package main
import "fmt"
func main() {
fmt.Println("Hello, world.")
}
이제 위 코드를 빌드하고 필요한 패키지들을 설치하기 위해 아래 명령어를 입력한다.
$ go install example/user/hello
$
위 명령어는 hello 라는 실행파일을 만들어준다. 설치 디렉토리는 GOPATH 혹은 GOBIN 환경 변수에 의해 제어된다. 만약 GOBIN 이 설정되어 있지 않다면, 해당 디렉토리에 저장되고, 설정되어 있다면 GOBIN 이 정의한 경로에 설치된다. go env 를 통해 go 에 등록된 환경변수들을 확인할 수 있으며 아래와 같이 등록하고 다시 제거할 수 있다.
$ go env -w GOBIN=/somewhere/else/bin
$ go env -u GOBIN
go install 과 같은 명령어는 현재 실행되고 있는 디렉토리에 포함되어있는 모듈들에 모두 적용된다. 만약, 현재 디렉토리가 example/user/hello 모듈에 있지 않다면, go install 명령은 실패할 것이다.
편의를 위해, go 명령어는 상대적인 경로를 설정할 수 있도록 제공한다.
$ go install example/user/hello
$ go install .
$ go install
이제, 프로그램이 동작하도록 실행해보자. 또 편리를 위해 설치 디렉토리를 PATH 환경변수에 추가해 둘 것이다. 아래와 같이
# Windows users should consult https://github.com/golang/go/wiki/SettingGOPATH
# for setting %PATH%.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.
$
혹시, 소스 관리 도구를 사용한다면, 이제 레포지토리를 초기화 하고 코드를 등록해보자. (옵션)
$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
1 file changed, 7 insertion(+)
create mode 100644 go.mod hello.go
$
로컬 모듈의 패지지 임포트하기
morestrings 라고 하는 패키지를 하나 만들어 보고 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)
}
ReverseRuns 함수는 첫글자가 대문자로 쓰여져있다. 이는 외부에서 임포트하여 쓸 수 있음을 의미한다. package 가 main 이 아니기 때문에 build 를 해도 따로 실행파일이 생기지는 않는다. main 에서 사용하기 위해서는 아래와 같이 선언한다.
package main
import (
"fmt"
"example/user/hello/morestrings"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}
외부 모듈의 패지지 임포트하기
import path 는 패키지들을 원격 관리 시스템인 git 이나 mercurial 에서 어떻게 받아올 수 있는지 설명한다. go tools 은 이러한 원격 시스템으로 부터 패치해오는 과정을 자동으로 한다. 예를 들어, github.com/google/go-cmp/cmp 를 가져오기 위해선 아래와 같이 선언한다.
package main
import (
"fmt"
"example/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"))
}
이제 외부 모듈에 의존송을 갖게 되었다. 이제 go.mod 파일에 해당 모듈과 그 버전을 기록하는것이 좋다. go mod tidy 명령어는 놓친 필요 패키지들을 다시 생성해주고, 필요없는 패키지를 삭제해준다.
$ go mod tidy
go: finding module for package github.com/google/go-cmp/cmp
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4
$ go install example/user/hello
$ hello
Hello, Go!
string(
- "Hello World",
+ "Hello Go",
)
$ cat go.mod
module example/user/hello
go 1.16
require github.com/google/go-cmp v0.5.4
$
모듈 의존성들은 자동으로 $GOPATH/pkg/mod 서브 디렉토리에 저장된다. 주어진 버전으로 설치된 컨텐츠는 해당 모듈을 필요로 하는 다른 모듈끼리 서로 공유하게 된다. 그렇기 때문에, go 는 해당 디렉토리의 파일들을 읽기-모드로 만들어준다. 해당 디렉토리의 모듈들을 모두 삭제하고 싶다면 아래 명령어를 입력한다.
$ go clean -modcache
테스트
go 는 경량화된 테스트 프레임워크가 내장되어 있어서 go test 명령어와 testing 패키지를 사용할 수 있다. 파일은 _test.go 라는 접미사를 붙여서 생성하고, 함수 이름은 TestXXX 와 같은 형식으로 만들며, 인수는 (t *testing.T) 를 갖도록 설정한다. 테스트 프레임워크는 각 함수를 실행하고, 해당 함수가 실패한다면 테스트는 실패한 것으로 간주한다.
morestrings 패키지에 테스트를 추가하고 싶다면 reverse_test.go 라는 이름으로 아래와 같이 정의할 수 있다.
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)
}
}
}
실제 테스트는 아래와 같이 진행한다.
$ cd $HOME/hello/morestrings
$ go test
PASS
ok example/user/hello/morestrings 0.165s
$
'번역' 카테고리의 다른 글
[번역] Go 를 위한 OpenStack SDK 사용법 (0) | 2022.01.27 |
---|---|
[번역] [go] Defer, Panic, Recover 에 대해서 (0) | 2022.01.27 |
[번역] 쿠버네티스 네트워크 이해하기: Pod (0) | 2022.01.27 |
[번역] reloader (0) | 2022.01.27 |
[번역] Iptables 및 Netfilter 구조에 대한 심층분석 (0) | 2022.01.25 |
댓글