메뉴

문서정보

GitLab의 Go 표준 스타일 가이드라인

이 문서의 원문은 https://docs.gitlab.com/ee/development/go_guide/ 이다. 원문은 개요를 설명하고 있으며, 이 문서는 상세하게 검토한 내용을 담고 있다.

Go 표준 가이드 라인

GitLab 시스템은 루비 온 레일즈(Ruby on Rails)를 이용해서 개발됐지만, 여러 프로젝트에 Go를 사용하고 있다. Go는 많은 장점을 가지고 있는 강력한 언어로 IO(디스크/네트워크)처리, HTTP 요청, 병렬 처리등의 프로젝트에 적합하다. GitLab은 프로젝트를 시작할 때 Ruby on Rails와 Go 어느 것을 선택할지 신중하게 평가해서 선택한다.

의존성 관리

Go는 2018년 8월까지 자체적인 의존성 관리 시스템을 제공하지 않았다. 개발자들은 각자가 적당한 의존성 관리 툴을 사용해야 했었는데, 다행히 Go 1.11(2018년 8월 24일) 버전 부터 Go Modules 기능이 들어가면서 의존성 관리 환경을 통일 할 수 있게 됐다.

Go는 아티팩트 기반이 아닌 소스 기반으로 종속성을 관리하는 특이한 접근 방식을 취하고 있다. 아티팩트 기반 종속성 관리 시스템에서 패키지는 소스코드에서 생성된 아티팩트로 구성되며, 이 아티팩트는 소스코드와는 별도의 저장소 시스템에 저장된다. 예를들어 NodeJS의 경우 패키지 저장소 npmjs.org와 코드 저장소 github.com은 서로 분리된다. 반면 Go는 소스코드가 곧 패키지이며, 패키지로 부터 생성된 아티팩트를 저장하는 별도의 저장소가 없다. 즉 Go는 VCS로 부터 직접 종속성을 관리한다.

Go 모듈은 의미론적 버전 관리 체계를 사용한다. Go 모듈의 버전은 VCS 태그로 정의된다. 예여기에 "v"를 붙여줘야 하는데, 예를 들어 gitlab.com/my/project 의 버전이 1.0.0 이라면 태그는 v1.0.0 이 된다.

모듈 가져오기

개발자는 go get을 이용해서 모듈을 가져올 수 있는데, 각 모듈과 모듈이 가진 종속성을 읽어와서 go.modgo.sum 파일을 업데이트 한다.

모듈 테스트

go mod init 명령을 이용해서 모듈관리 환경을 만들 수 있다.

실행하고나면 go.mod 파일이 만들어진다. 지금은 프로젝트 초기단계로 의존성이 없으므로 아무 내용이 없다.

우리는 "labstck/echo" 모듈을 이용해서 간단한 웹 애플리케이션 서버를 만들 것이다. go get 명령으로 echo 모듈을 다운로드 하자.

그러면 자동으로 go.mod와 go.sum 파일이 업데이트된다.

코드리뷰

Golang의 CodeReviewComments를 따른다.

Gofmt

gofmt를 실행하여 코딩 스타일을 자동으로 수정한다. 거의 모든 IDE 들이 gofmt를 자동으로 실행해주기 때문에, 개발자들은 들여쓰기 간격과 같은 기본적인 코딩 스타일을 신경슬 필요가 없다. 이런 이유로 go로 작성된 코드들은 모두 유사한 코딩 스타일을 보여준다. gofmt를 자동으로 실행해주는 환경이 아니라면 gofmt 명령으로 포맷을 수정 할 수 있다.

주석

주석은 약간 장황하더라도 완전한 문장이어야 한다. 이 접근 방식을 이용하면 godoc 으로 문서를 만들 때, 훌륭한 출력물을 기대할 수 있다. 주석은 설명되는 객체의 이름으로 시작하고 마침표로 끝낸다.
// Request는 클라이언트의 요청을 처리한다.
type Request struct {......}

// JSONEncode는 req의 내용을 JSON 인코딩하여 w에 쓴다. 
func JSONEncode(w io.Writer, req *Request) {......}

Context

context.Context를 이용해서 API 및 프로세스의 경계를 넘어 보안자격증명, 추적 정보, 시간제한 및 취소(cancel) 신호를 전달 할 수 있다. Go 프로그램은 RPC 및 HTTP 요청이 들어오면 context를 이용해서 전체 호출함수에 명시적으로 요청을 전달한다.

Context를 사용하는 대부분의 함수는 context를 첫 번째 매개변수로 받아들여야 한다.
func F(ctx context.Context, /* other arguments */){}
구조체 멤버에 Context를 추가하지 말라. 대신 메서드의 파라메터로 전달한다.

사용자 정의 컨텍스트를 생성하거나 함수에 컨텍스트 이외의 인터페이스를 사용하지 않는다.

복사

다른 패키지의 구조체를 복사 할 때는 주의해야 한다. 예를 들어 bytes.Buffer []byte 슬라이스를 사용한다. 슬라이스를 복사할 경우 의되치 않은 효과를 일으킬 수 있다.
package main

import "fmt"

func main() {
	slice := []int{0, 10, 20, 30, 40}

	newslice := slice
	newslice[0] = 100

	fmt.Println(slice)
	fmt.Println(newslice)
}
newslice의 값을 변경했는데, 원본 값도 변경됐다. 새로운 변수에 대입하는 경우 메모리를 참조하는 포인터를 복사하는 것이기 때문에, 복사한 변수의 값을 변경하면 메모리의 값이 변경된다.

이런 경우에는 copy 함수를 이용한다. ... 계속