Golang 에서의 명명 규칙

무언가에 이름을 지을 때는 우리가 공통적으로 짓는 규칙들이 있다. 한국에서는 사람 이름을 대부분 세글자로 짓는다던지, 멍멍이와 같이 반려견의 이름으로 사용되는 것들은 피한다던지, 발음하기 어려운 단어를 피한다던지 하는 것들이다. 이렇게 규칙을 가지고 이름을 짓게 되면, 나중에 어떤 이름을 보아도 이게 사람 이름인지, 반려견 이름인지, 회사 이름인지 어느정도 구분이 가능하다.

프로그래밍의 세계에서 이름짓기도 마찬가지다. 우리는 이를 명명규칙이라고 하는데, 이름을 지을 때 공통적으로 지키는 규칙들을 이야기한다. 언어마다 일반적으로 가이드 되는 패턴들이 있고, 대부분 이에 맞춰서 사용하는 것을 추천한다. 명명 규칙을 따라 작성한다면, 표준 라이브러리와 같이 다른 사람이 작성한 코드를 가져다 쓸때에도 통일성이 있어 관리가 편하며, 특정 명명 방법에는 기능적인 의미가 부여되어 있어서 코드를 읽지 않아도 이해가 되도록 도울 수 있다. 예를 들어 Go에서는 대문자로 된 변수명은 패키지 외부에서 사용 할 수 있다라는 기능적 의미를 가지고 있어서 사용자가 이해하기 쉬운 측면이 있다.

아래에서는 Go에서 가이드하는 명명 규칙을 알아보려고 한다. Go에서 명명규칙과 관련해서 알 수 있는 문서는 2가지인데, 하나는 Effective Go 에 이름 짓기와 관련된 파트가 있고, Go Wiki의 Code Review Comment에 명명법과 관련된 일부분이다.


기본적으로, Go에서 두 단어 이상이 결합할 때에는 MixedCaps 혹은 mixedCaps 형태로 붙여서 사용한다. 1 만약 URL, IP, NATO 처럼 이니셜이나 줄임말로 이루어진 단어들의 경우에는 consistent case, 즉 대문자나 소문자로 일정하게 사용해야 한다. 2 즉 IP도 되고, ip도 되지만 Ip처럼 사용하지는 말라는 뜻이다.

그리고 위에서 잠시 말했지만, Go에서 함수나 변수, 구조체 등의 이름 첫 글자는 매우 중요하다. 이 첫 글자가 대문자면, 패키지 외부에서 사용할 수 있다 라는 뜻이다.3

package main

var internalValue int
var ExternalValue int

func internalFunc() int {
  ...
}

func ExternalFunc() int {
  ...
}

위의 코드에서, internalValueinternalFunc는 해당 패키지 내부에서만 호출이 가능하나, ExternalValueExternalFunc는 패키지 외부에서도 호출이 가능하다. 다른 언어의 public private개념을 대소문자를 가지고 구분 한 것이다. Go에서는 변수나 함수 이름에 특정 유니코드 문자를 지원하는데4, “카운트”, “인덱스” 와 같은 단어를 변수나 함수 명으로 사용할 수 있다는 뜻이다. 이런 경우일때는 대/소문자가 없다. 그러면 외부에서 호출이 가능할까 불가능할까? 위에서 말 했던 것처럼, 첫 글자가 대문자면, 패키지 외부에서 사용 가능하기 때문에, 첫 글자가 대문자가 아니므로, 패키지 외부에서는 사용이 불가능하다. 이 경우에는, “T카운트” 처럼 앞에 영 대문자를 붙여야 한다.5


또한 패키지 명을 지을 때도 일정한 규칙이 있다. 소문자로, 한 단어로 패키지 명을 지어야한다. 그리고 패키지 명은 소스 디렉터리의 basename 이어야 한다. 6basename이란 디렉터리의 가장 끝에 있는 이름을 말한다.7 즉, src/encoding/base64"encoding/base64"로 import 되지만, 패키지명은 "base64"여야 한다. 또 패키지 안의 내용은 동어 반복을 삼가야한다. bufio 패키지 안의 Readerbufio.Reader로 이름지어야 하지, bufio.BufReader와 같이 사용해서는 안된다.

인터페이스에도 규칙이 하나 있다. interface가 메서드 하나 만을 가질 떄, 메서드의 이름에 + er을 붙여 인터페이스를 짓는다.8 즉 Read 함수 하나만을 가지는 인터페이스를 Reader, Close 함수 하나만을 가지는 인터페이스를 Closer 라고 명명한다. 많은 Go의 standard library들이 해당 형식으로 interface를 사용한다.

상수의 경우에는 타 언어에서는 MAX_VALUE 와 같이 SCREAMING_SNAKE_CASE 를 사용하는 경우가 많다. 하지만 Go에서는 일반 변수 짓는 것과 같이 변수를 짓는다. 9

const maxValue int = 5

위와 같이 일반 mixedCaps 형태로 사용하므로, 혼란이 없어야 겠다.

그리고 함수 이름이 Must로 시작하는 것과 관련된 규칙이 하나 있다. 함수 이름이 Must로 시작한다면, 해당 동작을 실행 할 수 없을 때 panic을 내야 한다는 규칙이다. 예를 들면 regexp.MustCompile 이 있는데, 이는 regexp.Compile 함수와 비슷하나, expression이 파싱 될 수 없으면 panic을 뱉는다. 이는 공식적인 가이드는 아니나, 많은 standard library에서 찾아 볼 수 있다.10


Go에서도 export를 결정하는 대/소문자 규칙을 제외하고는 네이밍 컨벤션을 따르지 않아도 같게 동작한다. 작업하는 팀에서 그들만의 네이밍 컨벤션이 있는 경우를 제외한다면, 많은 사람들과 함께 작업 할 때, 코드를 오픈소스화 해야 할 때 등은 Go의 네이밍 컨벤션을 지키는 것이 더 관리 잘 되는 코드를 만들 수 있을 것이다.


  1. https://golang.org/doc/effective_go.html#mixed-caps ↩︎

  2. https://github.com/golang/go/wiki/CodeReviewComments#initialisms ↩︎

  3. https://golang.org/doc/effective_go.html#names ↩︎

  4. https://golang.org/ref/spec#Identifiers ↩︎

  5. https://golang.org/doc/faq#unicode_identifiers ↩︎

  6. https://golang.org/doc/effective_go.html#package-names ↩︎

  7. https://en.wikipedia.org/wiki/Basename ↩︎

  8. https://golang.org/doc/effective_go.html#interface-names ↩︎

  9. https://stackoverflow.com/a/22688926 ↩︎

  10. https://stackoverflow.com/questions/39590458/golang-guard-assert-functions-naming-convention ↩︎