본문 바로가기
golang

Go convention 정리

by marble25 2023. 10. 3.

Golang으로 코드를 작성할 때 알아두면 좋은 convention을 정리해두고자 한다.

참고 자료

https://github.com/golang/go/wiki/CodeReviewComments

Comment

주석은 full sentence로 달도록 하자.

// Request represents a request to run a command.
type Request struct { ...

// Encode writes the JSON encoding of req to w.
func Encode(w io.Writer, req *Request) { ...

Contexts

context는 security credential, deadline, cancelation 등을 담을 수 있다. 함수의 첫 번째 인자로 context를 넘겨주자.

func F(ctx context.Context, /* other arguments */) {}

context를 struct type에 멤버 변수로 추가하지 말아야 한다. 대신 메소드에 ctx parameter로 넘겨주자.

custom context type을 생성하지 말아야 한다.

Application data를 넘겨줄 때, parameter, receiver, global 등 여러 방법이 다 적절하지 않을 때에만 context에다 넣어주자. Context는 immutable이므로, 같은 context를 여러 번의 함수 호출에 넘겨주어도 괜찮다.

Crypto Rand

math/rand 대신 crypto/rand 의 reader를 사용하자.

Declaring Empty Slices

var t []string

대신

t := []string{}

를 사용하자.

전자는 nil slice value이고, 후자는 non-nil slice이고 zero-length이다.

후자는 JSON encoding 할 때 선호된다. nil slice는 JSON에서 null이 되지만, []string{}는 JSON에서 []가 되기 때문이다.

Doc Comments

아무리 사소한 것이더라도, exported names(대문자로 시작하는 변수, 함수)는 doc comments를 가져야 한다.

Don’t Panic

일반적으로 panic을 사용하지 말자. 대신, error를 사용하자.

Error Strings

Error string은 capitalized되지 않아야 하고, 마침표로 끝나지 않아야 한다. Error string이 다른 context에서 출력될 수 있기 때문이다. fmt.Errorf("Something bad”) 대신 fmt.Errorf(”something bad”) 로 작성하자. 로깅은 라인 단위로 이루어지기 때문에 로깅에는 이 원칙이 적용되지 않는다.

Examples

Package를 새로 추가할 때, 올바른 사용을 위해 example를 추가하자.

Goroutine Lifetimes

Goroutine을 생성할 때 언제 끝나는지를 명확히 하자. Goroutine은 leak할 수도 있고, 더이상 필요하지 않더라도 계속 실행될 수도 있다.

Handle Errors

Error를 _ variable를 이용해서 버리지 말자. Error handling을 포함하자.

Imports

이름 충돌이 일어나지 않는 이상 import를 renaming하지 말자.

In-Band Errors

C언어에서는 error가 있는 경우 -1을 리턴하는 것이 일반적이다. 하지만, go에서는 multiple return을 지원하므로 additional value를 두어 다른 value가 적합한지 알려줄 수 있다. 이 value는 error type일 수도 있고, boolean 타입일 수도 있다.

// Lookup returns the value for key or ok=false if there is no mapping for key.
func Lookup(key string) (value string, ok bool)

이 함수는 다음과 같이 사용할 수 있다.

value, ok := Lookup(key)
if !ok {
	return fmt.Errorf("no value for %q", key)
}
return Parse(value)

Indent Error Flow

Indentation을 최소화하자.

if err != nil {
	// error handling
} else {
	// normal code
}

대신

if err != nil {
	// error handling
	return // or continue, etc.
}
// normal code

로 작성하자.

if x, err := f(); err != nil {
	// error handling
	return
} else {
	// use x
}

위 코드는 추가적으로 하나의 indentation을 필요로 한다. 대신 아래와 같이 작성하자.

x, err := f()
if err != nil {
	// error handling
	return
}
// use x

Initialism

줄임말은 모두 대문자나 모두 소문자로 표현하자. 예를 들어, URL은 URL이나 url이어야 하지(urlPony, URLPony), Url이면 안된다. 또한, ServeHttp가 아닌, ServeHTTP이어야 한다. 또 ID의 경우 줄임말이기 때문에 appId가 아닌 appID로 표현해야 한다.

Line Length

Go code에는 line length 제한이 있지는 않지만, 너무 긴 line은 지양해야 한다. 그렇다고 의도적으로 line break를 하여 가독성을 올리지는 말자. 그 대신, 변수명을 더 짧게 바꾸는 것을 고려해보자.

Named Result Parameters

func (n *Node) Parent1() (node *Node) {}
func (n *Node) Parent2() (node *Node, err error) {}

대신

func (n *Node) Parent1() *Node {}
func (n *Node) Parent2() (*Node, error) {}

가 낫다. 만약 같은 타입의 return parameter가 많아서 의미가 명확하지 않은 경우에는 named result parameter를 사용하자.

func (f *Foo) Location() (float64, float64, error)

위 함수는 float64가 어떤 값인지 명확하지 않다. 아래와 같이 변경하도록 하자.

// Location returns f's latitude and longitude.
// Negative values mean south and west, respectively.
func (f *Foo) Location() (lat, long float64, err error)

Package Comments

패키지 주석은 대문자로 시작해서 빈줄 없이 시작해야 한다.

// Package math provides basic constants and mathematical functions.
package math

Package Names

Identifier에 중복된 의미를 제거하자. 예를 들어, chubby package에 있다면, chubby.ChubbyFile 대신 chubby.File 로 작성하자. 더 자세한 내용은 다음 문서에서 확인할 수 있다.

Pass Values

큰 구조체를 넘겨줘야 하거나 하는 경우가 아니면 pointer를 함수에 넘겨주는 것을 지양하자.

Receiver Names

Receiver name으로는 한두 글자의 abbreviation을 사용하자. ("c" or "cl" for "Client")

일관성을 유지하자: 한 메소드에서 “c”로 호출했다면 다른 메소드에서 “cl”로 호출하지 말자.

Receiver Type

Value receiver를 사용할지, Pointer receiver를 사용할지 결정하는 것은 어렵다. 잘 모르겠으면 pointer를 사용해라: 작고 변하지 않는 struct를 사용하거나 basic type을 사용하는 경우에는 효율성을 위해 value가 낫다.

  • receiver가 작은 배열이나 struct이고, mutable field 없고, pointer가 없으면 value receiver가 적합하다. Value receiver는 garbage를 줄일 수 있다: Value가 전달될 때, heap에 할당하는 것이 아닌, on-stack copy가 일어난다.

Synchronous Functions

비동기 함수보다 동기 함수를 주로 사용하자.

Variable Names

Go에서는 짧은 변수명을 선호한다. lineCount 대신 c를, sliceIndex 대신 i를, reader는 r를 사용하자. 일반적이지 않은 경우나 global variable은 더 자세한 이름이 필요하다.

원칙: 이름이 더 오래 사용될 수록 더 자세하게 정하자.

'golang' 카테고리의 다른 글

Go garbage collection  (1) 2023.10.30
[TIL] Go buffer read  (0) 2023.10.27
golang에서 for loop scoping  (1) 2023.10.02
Go 상속 vs 구성  (1) 2023.10.01
Go context 파헤치기  (0) 2023.09.29