쿼카러버의 기술 블로그

[Golang] Go의 comment / formatting convention (주석 / 포맷팅 컨벤션) (TODO, FIXME ..) (Go스러운 코딩 시리즈) 본문

[Golang]

[Golang] Go의 comment / formatting convention (주석 / 포맷팅 컨벤션) (TODO, FIXME ..) (Go스러운 코딩 시리즈)

quokkalover 2022. 2. 20. 19:57

Writing is easy, but reading is painful.

개발자로 살다보면, 코드의 가독성을 높이고 협업하기 좋은 코드를 작성하고자 하는 욕심이 있을 것이다. 특정 프로그래밍 언어를 잘 사용할 수 있다고 하려면 복잡한 문제를 해결해내는 코드 작성뿐 아니라 누군가와 협업을 하고, 서로 이해 가능한 코드를 짤 수 있어야 하는데, 이를 위해서는 규칙과 convention이 필요하다.

 

Go에서는 이를 “Go를 Go답게 쓴다"라고 표현하는데 이와 관련해서 잘 정리된 글들과, Go Effective의 글들을 참고해서 “Go를 Go답게” 쓰는 방법에 대한 시리즈를 다루고자 한다.

 

다른 시리즈로는 Go의 Naming 컨벤셩에 대해 정리한 글이 있는데 참고 바란다. 

 

 

본 글은 https://go.dev/doc/effective_go 를 기반으로, 코드의 가독성을 높이고 협업을 더 원활하게 하기 위해 숙지하면 좋은 주석과 포맷팅의 convention에 대해 다루려고 한다.

 

주석이란? (Comment)

주석 또는 코멘트(comment)는 내용을 메모하는 목적으로 쓰인다. 즉, Compiler나 Interpreter에게는 무시되어 프로그램에 영향을 주지 않는다. 주석을 잘 활용하면 개발을 하는 프로그래머가 코드를 더 쉽게 이해할 수 있도록 도와준다. 미래의 나를 위해서도 있고, 협업을 할 개발자 혹은 훗날의 나의 코드를 활용할 누군가를 위해, 즉 유지보수 가능하고 접근성이 좋은 코드를 짜기 위해 꼭 필요 하다.

조금 복잡한 함수의 경우에는 설명을 적어두지 않으면 직관적으로 이 코드가 어떤 기능을 하는지에 대해 잊어먹기 쉽기 때문에, 코딩을 할 때는 주석을 다는 것을 습관화하는게 좋다. 하지만 물론 아무렇게나 달 경우에는 오히려 혼란을 초래할 수 있기 때문에, 각 언어마다도 주석을 다는 규칙이 있다.

 

 

Go에서의 주석

Go는 문서화를 중요하게 여긴다. 기본적으로 godoc 이 정한 규칙을 따른데, godoc은 주석을 포함한 소스코드를 분석하고, HTML이나 일반 텍스트로 문서를 자동으로 생성해주는 Go의 기본 프로그램이다.

최상위 선언문 이전에 여백 없이 주석이 있으면 해당 항목의 설명으로 추출되어 문서화 된다. 따라서 주석의 스타일과 유형을 잘 숙지해야 질 좋은 문서를 생성할 수 있다. 참고로 godoc은 public identifier(대문자로 시작하는 타입, 함수, 상수, 변수)에 대해서 문서를 작성하기 때문에, public한 identifier에 대해서는 주석을 달아주는게 좋다.

가장 기본적으로 커멘트의 첫 줄은 해당 identifer의 이름으로 시작하는 one-sentence summary여야 한다. 너무 길다면 각 line마다 // 를 사용해 개행 할 수 있다.

예를 들면 아래와 같다.

// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {

// sizeCalculationDisabled indicates whether it is safe
// to calculate Types' widths and alignments. See dowidth.
var sizeCalculationDisabled bool

참고로 주석을 달때는 multi-line의 경우 /* ... */syntax 를 사용할 수 있지만 debugging용으로만 주로 사용하고 일반적으로는 // syntax를 사용하는 것을 권장한다.

또한 주석은 주석을 다는 identifer와 동일한 들여쓰기를 해야 한다. 예를 들면 아래와 같이 모든 같은 indent level을 쓴다.

package main

import "fmt"

const favColor string = "blue"

func main() {
    var guess string
    // Create an input loop
    for {
        // Ask the user to guess my favorite color
        fmt.Println("Guess my favorite color:")
        // Try to read a line of input from the user. Print out the error 0
        if _, err := fmt.Scanln(&guess); err != nil {
            fmt.Printf("%s\n", err)
            return
        }
        // Did they guess the correct color?
        if favColor == guess {
            // They guessed it!
            fmt.Printf("%q is my favorite color!\n", favColor)
            return
        }
        // Wrong! Have them guess again.
        fmt.Printf("Sorry, %q is not my favorite color. Guess again.\n", guess)
    }
}

Package comments

모든 패키지에는 패키지 선언 구문 이전에 blank line없이 블럭주석의 형태로 패키지 주석이 있어야 한다. 내용으로는 패키지 소개 및 상세를 작성한다.

여러 파일로 구성된 패키지의 경우에는 하나의 파일에만 있으면 된다.

regex 패키지의 예를 들면 아래와 같다.

/*
Package template implements data-driven templates for generating textual
output such as HTML.
....
*/
package template

Function comments

함수 주석은 위에 설명한 기본 구조처럼 이름으로 시작하는 문장을 쓰고, 아래를 주의해서 작성한다.

  • 정확한 철자
  • 구두법
  • 짧고 완전한 문장

위에서도 작성했지만 설명을 위해 똑같이 예를 들면 아래와 같다.

// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {

이렇게 해야 godocgrep명령어의 조합으로 해당 함수를 찾을 수 있다.

$ godoc regexp | grep parse

Inline comments

code와 같은 line에 따라오는 주석을 Inlie comment라 한다. 설명보단 아래 예시를 보면 바로 이해할 수 있다.

[code]  // Inline comment about the code

z := x % 2  // Get the modulus of x
x := 8  // Initialize x with an arbitrary number

다른 커멘트와 같이 // 로 시작하고, 그 뒤에는 주로 띄어쓰기 한번 해주고 설명을 작성한다. 주로 잘 쓰지는 않지만 tricky하거나 너무 불분명한 경우 혹은 특정 값을 assign하는 이유를 작성해야 할때 쓴다.

Grouping comments

서로 관련있는 constant나 variable에 대한 설명을 묶어서 작성할 수도 있다. 예를 들면 아래와 같다.

// Error codes returned by failures to parse an expression.
var (
    ErrInternal      = errors.New("regexp: internal error")
    ErrUnmatchedLpar = errors.New("regexp: unmatched '('")
    ErrUnmatchedRpar = errors.New("regexp: unmatched ')'")
    ...
)

특정 변수들의 관계를 표현하기 위해 Grouping을 하는데, 아래 예시의 경우에는 mutex에 활용되는 변수들을 하나로 묶었다.

var (
    countLock   sync.Mutex
    inputCount  uint32
    outputCount uint32
    errorCount  uint32
)

 

TODO / FIXME / NOTE 주석

코딩을 하다보면 아래와 같은 키워드를 사용해 주석을 달기도 한다.

  1. TODO : 좀더 최적화시키고 리팩토링시킬 수 있을만한 구석이 있을때. 미래에 뭔가 의미있는 작업을 더 해야 할 필요성을 느낄때.
  2. FIXME : 문제가 있는것이 확실하지만, 그걸 지금 당장 그것을 수정할 필요는 없을 때.
  3. XXX : 해당 부분에 대해서는 더 생각해볼 필요성이 있을 때. 또는 해당 부분에 질문이 생길 때. 또는 코드에서 문제가
  4. NOTE : 해당 부분에 대해 주의해야 하는 부분이 있을 때.

코딩을 하다가 당장은 수행하지 못하는 경우에 주로 사용하는데, 위와 같은 키워드를 사용하면 아래와 같이 특정 툴을 활용해 TODO가 적힌 부분만 찾는다던지, FIXME가 적힌 부분을 찾는다던지 할 수 있다.

필자는 주로 위 키워드와 : 를 붙여서 주석을 작성한다.

// TODO: handle codes more.
// FIXME: remove this log. this is just for testing
// NOTE: it will skip a field if untagged.

 

포맷팅 (Formatting)

포맷팅은 협업과 가독성의 측면에서 중요하다. Go는 언어 자체에서 지원하는 gofmt를 통해 일관된 포맷을 유지할 수 있다.

 

GO의 formatting

Go의 포맷팅 컨벤션에 관한 요약은 다음과 같다.

  • 들여쓰기(indent) : 기본값으로 탭(tabs)을 사용하며, 꼭 써야 하는 경우에만 스페이스(spaces) 사용
  • 한 줄 길이 : 한 줄 길이에 제한이 없으나, 너무 길게 느껴진다면 별도의 탭을 들여써서 감싼다.
  • 괄호 : C와 Java에 비해 적은 수의 괄호가 필요함.

 

go답게 코딩하자

 

참고자료

https://ko.wikipedia.org/wiki/주석_(프로그래밍)

https://www.digitalocean.com/community/tutorials/how-to-write-comments-in-go

https://meoru-tech.tistory.com/74

Comments