Go 코드 작성 방법

Golang 코드의 작성 방법과 관련된 전반적인 내용을 정리합니다.

원문

본 문서는 다음 라이센스를 따릅니다.

https://creativecommons.org/licenses/by/3.0/

소개

이 문서는 간단한 Go 패키지의 개발을 설명하고 Go 패키지와 명령을 가져와서 빌드하고 설치하는 표준 방법인 go 도구를 소개합니다.

go 도구는 구체적인 방법으로 코드를 정리하도록 요구합니다. 문서를 주의깊게 읽으십시오. 이 문서는 Go를 설치하고 실행하는 가장 간단한 방법을 설명합니다.

비슷한 설명을 스크린캐스트로도 볼 수 있습니다.

코드 정리

개요

  • Go 프로그래머들은 주로 Go 코드를 하나의 *작업 공간(workspace)*에 보관합니다.
  • 작업 공간은 여러 (예를 들어 Git에 의해 관리되는)버전 관리 *저장소*를 가집니다.
  • 각 저장소는 하나 이상의 *패키지*를 가집니다.
  • 각 패키지는 단일 디렉터리에 하나 이상의 Go 소스 파일로 구성됩니다.
  • 패키지 디렉터리까지의 경로는 패키지의 임포트 경로를 결정합니다.

이는 프로젝트마다 구분된 작업 공간을 가지며 작업 공간들은 버전 관리 저장소와 밀접하게 연결되어 있는 다른 프로그래밍 환경과 다르다는 것을 알아두세요.

작업 공간

작업 공간은 루트에 두 개의 디렉터리를 갖는 디렉터리 계층입니다.

  • src는 Go 소스 파일을 가지며,
  • bin은 실행 가능한 명령을 갖습니다.

go 도구는 bin 디렉터리에 바이너리를 빌드하고 설치합니다.

src 서브디렉터리는 주로 하나 이상의 소스 패키지 개발을 추적하는 여러 (Git 또는 Mercurial과 같은)버전 관리 저장소를 갖습니다.

다음은 실제로 작업 공간이 어떻게 구성되는지 이해를 돕기 위한 예시입니다:

bin/
    hello                          # command executable
    outyet                         # command executable
src/
    github.com/golang/example/
        .git/                      # Git repository metadata
	hello/
	    hello.go               # command source
	outyet/
	    main.go                # command source
	    main_test.go           # test source
	stringutil/
	    reverse.go             # package source
	    reverse_test.go        # test source
    golang.org/x/image/
        .git/                      # Git repository metadata
	bmp/
	    reader.go              # package source
	    writer.go              # package source
    ... (many more repositories and packages omitted) ...

위의 트리는 두 개의 저장소(exampleimage)를 갖는 작업 공간을 보여줍니다. example 저장소는 두 개의 명령(hellooutyet)과 하나의 라이브러리(stringutil)를 가집니다. image 저장소는 bmp 패키지와 몇몇 다른 것들을 가집니다.

작업 공간은 주로 많은 패키지와 명령을 가진 많은 소스 저장소를 가집니다. 대부분의 Go 프로그래머들은 모든 Go 소스와 의존성을 하나의 작업 공간에 보관합니다.

심볼릭 링크는 파일이나 디렉터리를 작업 공간에 링크하는 데 사용되면 안된다는 것을 알아두세요.

명령과 라이브러리들은 다른 종류의 소스 패키지에서 빌드됐습니다. 이부분은 이후에 다루게 됩니다.

GOPATH 환경 변수

GOPATH 환경 변수는 작업 공간의 위치를 명시합니다. 기본적으로 여러분의 home 디렉터리 안에 go라는 이름을 가진 디렉터리입니다. 즉, Unix에서는 $HOME/go, Plan 9에서는 $home/go, Windows에서는 %USERPROFILE%\go(주로 C:\Users\YourName\go)입니다.

다른 위치에서 작업하고자 한다면, 해당 디렉터리로 GOPATH 설정이 필요합니다. (또다른 공용 설치는 GOPATH=$HOME으로 설정합니다.) GOPATH Go 설치와 같은 디렉터리면 안된다는 점을 알아두세요.

go env GOPATH 명령은 현재 GOPATH를 출력합니다. 환경 변수가 설정되지 않았다면 기본 위치를 출력합니다.

편의를 위해, 작업 공간의 bin 서브디렉터리를 PATH에 추가합니다:

$ export PATH=$PATH:$(go env GOPATH)/bin

이 문서의 나머지 부분에서는 간결성을 위해 $(go env GOPATH) 대신 $GOPATH를 사용합니다. GOPATH를 설정하지 않고 스크립트가 작성된 대로 동작하게 하려면, 명령들에서 $HOME/go로 대체하거나 다음을 실행합니다:

$ export GOPATH=$(go env GOPATH)

GOPATH 환경 변수에 대해 더 알아보려면, go help gopath를 보세요.

커스텀 작업 공간 위치를 사용하려면, GOPATH 환경 변수 설정을 보세요.

임포트 경로

*임포트 경로*는 패키지를 고유하게 식별하는 문자열입니다. 패키지의 임포트 경로는 작업 공간이나 (아래에서 설명할)원격 저장소 안에서 자신의 위치에 대응합니다.

"fmt""net/http"과 같은 표준 라이브러리 패키지는 짧은 임포트 경로가 주어집니다. 여러분의 고유 패키지에 대해서는 미래에 추가될 표준 라이브러리나 외부 라이브러리와 충돌할 가능성이 없는 기본 경로를 선택해야 합니다.

코드를 소스 저장소 어딘가에 보관하면, 그 소스 저장소의 루트를 기본 경로로 사용하는 것이 좋습니다. 예를 들어, github.com/userGitHub 계정을 가지고 있다면 이것이 기본 경로가 되는 것이 좋습니다.

여러분이 빌드할 수 있기 전에 원격 저장소에 코드를 게시할 필요는 없다는 것을 알아두세요. 언젠가 게시할 것 처럼 코드를 정리하는 것은 좋은 습관입니다. 표준 라이브러리와 더 큰 Go 생태계에 고유하기만 하다면, 실제로 어떤 임의의 경로 이름도 선택할 수 있습니다.

github.com/user를 기본 경로로 사용합니다. 소스 코드를 보관할 디렉터리를 작업 공간 안에 만듭니다:

$ mkdir -p $GOPATH/src/github.com/user

첫 프로그램

간단한 프로그램을 컴파일하고 실행하기 위해, 먼저 패키지 경로를 선택하고 대응하는 패키지 디렉터리를 작업 공간 안에 생성합니다:

$ mkdir $GOPATH/src/github.com/user/hello

다음으로, 디렉터리 안에 hello.go라는 이름으로 파일을 생성합니다. 파일은 다음 Go 코드를 갖습니다:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world.")
}

이제 go 도구로 프로그램을 빌드하고 설치할 수 있습니다:

$ go install github.com/user/hello

이 명령을 시스템 어느 곳에서든 실행할 수 있다는 점을 기억하세요. go 도구는 GOPATH에 명시된 작업 공간 안에서 github.com/user/hello 패키지를 찾아서 소스 코드를 찾습니다.

또한 패키지 디렉터리에서 go install을 실행하면 패키지 경로를 생략할 수 있습니다:

$ cd $GOPATH/src/github.com/user/hello
$ go install

이 명령은 실행 가능한 바이너리를 만들어 hello 명령을 빌드합니다. 그 다음 이 바이너리를 작업 공간의 bin 디렉터리에 hello(또는, Windows의 경우 hello.exe)로 설치합니다. 이 예제에서는 $GOPATH/bin/hello, 즉 $HOME/go/bin/hello가 될 것입니다.

go 도구는 오류가 발생했을 때만 출력합니다. 때문에 이러한 명령들이 아무런 출력도 내지 않는다면 성공적으로 실행된 것입니다.

이제 커맨드 라인에서 전체 경로를 입력하여 프로그램을 실행할 수 있습니다:

$ $GOPATH/bin/hello
Hello, world.

또는, PATH$GOPATH/bin을 추가했기 때문에 바이너리 이름만 입력합니다:

$ hello
Hello, world.

버전 관리 시스템을 사용중이라면, 지금이 저장소를 초기화할 좋은 때입니다. 파일을 추가하고, 변경 사항을 커밋합니다. 다시 말하지만, 이 단계는 선택사항입니다: Go 코드를 작성하기 위해 버전 관리를 사용할 필요는 없습니다.

$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git repository in /home/user/work/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 1 insertion(+)
  create mode 100644 hello.go

코드를 원격 저장소에 푸시(push)하는 것은 독자 여러분의 몫으로 남겨두겠습니다.

첫 라이브러리

라이브러리를 작성하고 hello 프로그램에서 사용해봅시다.

다시한번 말하지만, 첫 단계는 패키지 경로(여기서는 github.com/user/stringutil)를 선택하고 패키지 디렉터리를 생성하는 것입니다:

$ mkdir $GOPATH/src/github.com/user/stringutil

다음으로, 디렉터리에 다음의 내용으로 reverse.go라는 이름의 파일을 생성합니다.

// Package stringutil contains utility functions for working with strings.
package stringutil

// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(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)
}

이제, go build로 패키지가 컴파일되는지 확인합니다:

$ go build github.com/user/stringutil

또는 패키지의 소스 디렉터리에서 작업중이라면:

go build

출력은 나오지 않을 것입니다. 대신 컴파일된 패키지를 로컬 빌드 캐시에 저장합니다.

stringutil 패키지 빌드를 환인한 뒤에, 이를 사용하기 위해 ($GOPATH/src/github.com/user/hello에 있는)원래의 hello.go 파일을 수정합니다:

package main

import (
	"fmt"

	"github.com/user/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("!oG ,olleH"))
}

hello 프로그램을 설치합니다:

$ go install github.com/user/hello

이 프로그램의 새 버전을 실행하여 반전된 메시지를 봅니다:

$ hello
Hello, Go!

위의 단계 이후에, 작업 공간은 다음과 같습니다:

bin/
    hello                 # command executable
src/
    github.com/user/
        hello/
            hello.go      # command source
        stringutil/
            reverse.go    # package source

패키지 이름

Go 소스 파일의 첫 문장은 다음과 같아야 합니다:

package name

*name*은 임포트를 위한 패키지의 기본 이름입니다. (패키지 안의 모든 파일은 같은 *name*을 사용해야 합니다.)

Go의 관례는 패키지 이름은 임포트 경로의 마지막 요소라는 것입니다: crypto/rot13으로 임포트된 패키지의 이름은 rot13이어야 합니다.

패키지 이름이 하나의 바이너리에 링크되는 모든 패키지에서 유일해야 한다는 조건은 없습니다, 오직 임포트 경로(완전한 파일 이름)만 유일합니다.

Go의 이름 짓기 관례에 대해 더 알아보려면 Effective Go를 보세요.

테스트

Go는 go test 명령과 testing 패키지로 구성된 경량 테스트 프레임워크를 가지고 있습니다.

시그니처가 func (t *testing.T)이면서 TestXXX 형태로 명명된 함수를 가지고 이름이 _test.go로 끝나는 파일을 생성하여 테스트를 작성합니다. 테스트 프레임워크는 이러한 함수들을 각각 실행합니다. 만약 함수가 t.Error 또는 t.Fail과 같은 실패 함수를 호출하면, 테스트는 실패한 것으로 간주됩니다.

다음 코드를 가진 $GOPATH/src/github.com/user/stringutil/reverse_test.go 파일을 생성하여 stringutil 패키지에 테스트를 추가합니다.

package stringutil

import "testing"

func TestReverse(t *testing.T) {
    cases := []struct {
        in, want string
    } {
        {"Hello, world", "dlrow ,olleH"},
        {"Hello, 世界", "界世 ,olleH"},
        {"", ""},
    }
    for _, c := range cases {
        got := Reverse(c.in)
        if got != c.want {
            t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
        }
    }
}

그 다음, go test로 테스트를 실행합니다:

$ go test github.com/user/stringutil
ok  	github.com/user/stringutil 0.165s

늘 그렇듯, 패키지 디렉터리에서 go 도구를 실행중이라면, 패키지 경로를 생략할 수 있습니다:

$ go test
ok  	github.com/user/stringutil 0.165s

더 자세한 내용은 go help test를 실행해보고 testing 패키지 문서를 보세요.

원격 패키지

임포트 경로는 Git이나 Mercurial과 같은 버전 관리 시스템을 사용하여 패키지 소스 코드를 얻는 방법을 기술합니다. go 도구는 원격 저장소에서 패키지를 자동으로 가져오는 데에 이 특성을 사용합니다. 예를 들어, 이 문서에 기술된 예시는 GitHub github.com/golang/example에 호스트된 Git 저장소에도 보관되어 있습니다. 만약 패키지의 임포트 경로에 저장소 URL을 포함하면, go get이 이를 자동으로 가져와서 빌드하고 설치할 것입니다:

$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

만약 명시된 패키지가 작업 공간에 존재하지 않으면, go getGOPATH에 명시된 첫 번째 작업 공간 안에 이를 위치시킬 것입니다. (만약 패키지가 이미 존재하면, go get은 원격 저장소에서 가져오는 과정을 건너뛰고 go install과 동일하게 동작합니다.)

go get 명령 이후에, 이제 작업 공간 디렉터리 트리는 다음과 같습니다:

bin/
    hello                           # command executable
src/
    github.com/golang/example/
	.git/                       # Git repository metadata
        hello/
            hello.go                # command source
        stringutil/
            reverse.go              # package source
            reverse_test.go         # test source
    github.com/user/
        hello/
            hello.go                # command source
        stringutil/
            reverse.go              # package source
            reverse_test.go         # test source

GitHub에서 호스트되는 hello 명령은 같은 저장소 안의 stringutil 패키지에 의존합니다. hello.go 파일의 임포트는 같은 임포트 경로 관례를 사용합니다, 따라서 go get 명령도 의존 패키지를 위치시키고 설치할 수 있습니다.

import "github.com/golang/example/stringutil"

이 관례는 Go 패키지를 다른 사람들이 사용할 수 있도록 만드는 가장 쉬운 방법입니다. Go Wikigodoc.org가 외부 Go 프로젝트 목록을 제공합니다.

go 도구를 이용한 원격 저장소 사용에 대해 더 많은 정보는 go help importpath를 보세요.

다음은

golang-announce 메일링 리스트를 구독해서 새로운 Go 안정 버전이 릴리즈됐을 때 알림을 받으세요.

깔끔하고 자연스러운 Go 코드를 작성하는 팁은 Effective Go를 보세요.

언어를 배우려면 A Tour of Go를 보세요.

Go 언어와 라이브러리와 도구들에 대해 더 깊이 다룬 글들은 documentation page를 방문하세요.

도움 받기

실시간 도움은 Freenode IRC 서버의 #go-nuts에서 gopher들에게 물어보세요.

Go 언어의 논의를 위한 공식 메일링 리스트는 Go Nuts입니다.

버그는 Go 이슈 트래커를 사용해서 보고해주세요.

목록으로