Home [번역] Go Style Guide 2
Post
Cancel

[번역] Go Style Guide 2

간결성

간결한 Go 코드는 노이즈 대비 신호가 높습니다. 네이밍과 구조를 통해 관련된 세부사항을 쉽게 파악할 수 있습니다.

주어진 시점에서 가장 중요한 세부사항을 도출하는 데 방해가 되는 요소가 많이 있습니다:

  • 반복적인 코드
  • 불필요한 구문
  • 불투명한 이름
  • 불필요한 추상화
  • 공백

특히 반복적인 코드는 각각 거의 동일한 섹션들 간의 차이를 흐려지게 하며, 독자는 변경 사항을 찾기 위해 비슷한 코드 라인들을 시각적으로 비교해야 합니다. 테이블 기반 테스트는 각 반복의 중요한 세부사항에서 공통 코드를 간결하게 추출하는 좋은 예입니다. 그러나 테이블에 포함시킬 부분을 선택하는 것은 테이블의 이해하기 쉬움에 영향을 미칠 수 있습니다.

코드를 구조화하는 여러 가지 방법을 고려할 때, 가장 중요한 세부사항이 가장 눈에 띄게 되는 방법을 고려하는 것이 좋습니다.

일반적인 코드 구성과 관용구를 이해하고 사용하는 것도 노이즈 대비 신호를 유지하기 위해 중요합니다. 예를 들어, 다음과 같은 코드 블록은 에러 처리에서 매우 일반적이며, 독자는 이 블록의 목적을 빠르게 이해할 수 있습니다.

1
2
3
4
// Good:
if err := doSomething(); err != nil {
    // ...
}

만약 코드가 매우 유사하지만 섬세하게 다르다면, 독자는 변경 사항에 주의를 기울이지 못할 수 있습니다. 이러한 경우에는 주의를 끌기 위해 주석을 추가하여 에러 체크의 신호를 명확하게 강조하는 것이 가치가 있습니다.

1
2
3
4
// Good:
if err := doSomething(); err == nil { // if NO error
    // ...
}

유지보수성

코드는 작성되는 횟수보다 훨씬 많이 편집됩니다. 가독성 있는 코드는 작동 방식을 이해하려는 독자 뿐만 아니라 변경해야 할 프로그래머에게도 이해하기 쉽습니다. 명확성이 중요합니다.

유지보수 가능한 코드:

  • 미래의 프로그래머가 올바르게 수정하기 쉬워야 합니다.
  • API가 정교하게 구성되어 유연하게 확장할 수 있어야 합니다.
  • 코드 구조에 맞지 않고 문제의 구조에 맞는 추상화를 선택하며, 어떤 가정을 기반으로 하는지 명확해야 합니다.
  • 불필요한 결합을 피하고 사용되지 않는 기능을 포함하지 않아야 합니다.
  • 약속된 동작을 유지하고 중요한 논리가 올바른지 확인하기 위해 포괄적인 테스트 스위트가 있어야 하며, 테스트는 실패 시 명확하고 실행 가능한 진단 정보를 제공해야 합니다.
  • 인터페이스와 같은 추상화를 사용할 때는, 해당 추상화가 충분한 이점을 제공하는지 확인하는 것이 중요합니다. 편집기와 IDE는 구체적인 타입을 사용할 때 메소드 정의에 직접 연결하고 해당 문서를 표시할 수 있지만, 그렇지 않은 경우 인터페이스 정의만 참조할 수 있습니다. 인터페이스는 강력한 도구이지만, 인터페이스를 올바르게 사용하기 위해 기본 구현의 세부 사항을 이해해야 할 수 있으며, 이는 인터페이스 문서나 호출 지점에 설명되어야 합니다.

유지보수 가능한 코드는 중요한 세부 사항을 간과하기 쉬운 위치에 숨기지 않습니다. 예를 들어, 다음 코드 각 줄에는 한 문자의 존재나 부재가 중요한 의미를 이해하는 데 영향을 미칩니다:

1
2
3
4
5
// Bad:
// The use of = instead of := can change this line completely.
if user, err = db.UserByID(userID); err != nil {
    // ...
}
1
2
3
// Bad:
// The ! in the middle of this line is very easy to miss.
leap := (year%4 == 0) && (!(year%100 == 0) || (year%400 == 0))

이 코드는 잘못된 것은 아니지만, 보다 명시적인 방식으로 작성하거나 중요한 동작에 주의를 기울이는 주석을 추가할 수 있습니다:

1
2
3
4
5
6
// Good:
u, err := db.UserByID(userID)
if err != nil {
    return fmt.Errorf("invalid origin user: %s", err)
}
user = u
1
2
3
4
5
6
7
8
9
// Good:
// Gregorian leap years aren't just year%4 == 0.
// See https://en.wikipedia.org/wiki/Leap_year#Algorithm.
var (
    leap4   = year%4 == 0
    leap100 = year%100 == 0
    leap400 = year%400 == 0
)
leap := leap4 && (!leap100 || leap400)

마찬가지로, 중요한 로직이나 중요한 엣지 케이스를 숨기는 도우미 함수는 나중에 변경 시 적절하게 대응하지 못할 수 있습니다.

예측 가능한 이름은 유지보수 가능한 코드의 특징입니다. 패키지 사용자나 코드 유지자는 특정 문맥에서 변수, 메소드, 또는 함수의 이름을 예측할 수 있어야 합니다. 동일한 개념에 대한 함수 매개변수와 리시버 이름은 일반적으로 동일한 이름을 공유해야 하며, 이를 통해 문서를 이해하기 쉽고 코드 리팩토링을 최소한의 부담으로 수행할 수 있습니다.

유지보수 가능한 코드는 종속성을 최소화해야 합니다(암묵적 및 명시적으로 모두). 더 적은 패키지에 의존하는 것은 동작에 영향을 미칠 수 있는 코드 줄 수를 줄입니다. 내부적이거나 문서화되지 않은 동작에 대한 종속성을 피하는 것은 해당 동작이 미래에 변경될 때 유지 보수 부담을 줄일 수 있습니다.

코드를 구조화하거나 작성하는 방법을 고려할 때, 코드가 시간이 지남에 따라 어떻게 발전할 수 있는지 생각해보는 것이 가치가 있습니다. 향후 변경 작업을 더 쉽고 안전하게 수행할 수 있는 접근 방식이 있다면, 조금 복잡한 설계라도 일반적으로 좋은 트레이드 오프가 될 수 있습니다.

일관성

일관된 코드는 전체 코드베이스, 팀 또는 패키지의 맥락, 심지어 단일 파일 내에서도 유사한 코드처럼 보이고 느껴지며 동작하는 코드입니다.

일관성에 관한 고려 사항은 앞서 언급한 원칙을 우선시하지만, 결정을 내리는 데 있어서는 일관성을 우선시하는 것이 종종 유리할 수 있습니다.

패키지 내에서의 일관성은 종종 가장 중요한 수준의 일관성입니다. 동일한 문제가 패키지 전체에서 여러 가지 방식으로 접근되거나 파일 내에서 동일한 개념에 여러 개의 이름이 사용된다면 매우 어색할 수 있습니다. 그러나 이러한 경우에도 문서화된 스타일 원칙이나 전체적인 일관성을 우선시해야 합니다.

중요한 가이드라인

이 가이드라인은 모든 Go 코드가 따르기를 기대하는 Go 스타일의 가장 중요한 측면을 모아놓았습니다. 가독성이 허용되는 시점에 이러한 원칙들을 학습하고 따르기를 기대합니다. 이러한 가이드라인은 자주 변경되지 않으며, 새로운 추가 사항은 높은 기준을 통과해야 합니다.

아래 가이드라인은 Effective Go의 권장사항을 확장하여 전체 커뮤니티에서 Go 코드의 공통 기준을 제공합니다.

포맷팅

모든 Go 소스 파일은 gofmt 도구로 생성된 형식을 따라야 합니다. 이 형식은 Google 코드베이스의 사전 제출 체크에 의해 강제화됩니다. 생성된 코드도 일반적으로 포맷팅되어야 합니다(예: format.Source를 사용함으로써), Code Search에서 탐색 가능하도록 합니다.

MixedCaps

Go 소스 코드는 밑줄 대신 MixedCaps 또는 mixedCaps (카멜 케이스)를 사용하여 다단어 이름을 작성합니다.

이는 다른 언어의 규칙을 어길 경우에도 적용됩니다. 예를 들어, 상수는 exported 되었을 때는 MaxLength(대문자로 시작)이며, unexported 되었을 때는 MAX_LENGTH가 아닌 maxLength(소문자로 시작)입니다.

로컬 변수는 초기 대문자화를 선택하기 위해 unexported로 간주됩니다.

줄 길이

Go 소스 코드에는 고정된 줄 길이가 없습니다. 한 줄이 너무 길다고 느껴진다면, 줄을 나누는 대신 리팩토링해야 합니다. 이미 가능한 한 짧은 줄인 경우, 줄이 긴 상태로 유지되어야 합니다.

다음 상황에서는 줄을 나누지 않아야 합니다:

  • 들여쓰기 변경 전(예: 함수 선언, 조건문)
  • 긴 문자열(예: URL)을 여러 줄로 나누기 위해

네이밍

네이밍은 과학보다는 예술입니다. Go에서는 다른 언어보다 이름이 다소 짧은 편이지만, 동일한 일반적인 지침이 적용됩니다. 이름은 다음과 같아야 합니다:

  • 사용될 때 반복적이지 않아야 합니다.
  • 맥락을 고려해야 합니다.
  • 이미 명확한 개념을 반복하지 않아야 합니다.

이와 관련된 보다 구체적인 지침은 결정 사항에서 찾을 수 있습니다.

로컬 일관성

스타일 가이드에서 특정한 스타일에 대해 언급하지 않는 경우, 작성자는 해당 문제에 대해 선호하는 스타일을 선택할 수 있습니다. 단, 인접한 코드(보통 동일한 파일이나 패키지 내에 있지만 때로는 동일한 팀이나 프로젝트 디렉터리 내에 있을 수도 있음)가 해당 문제에 대해 일관된 입장을 취한 경우를 제외하고는 그렇지 않습니다.

유효한 로컬 스타일 고려 사항의 예:

  • 오류의 서식화된 출력을 위해 %s 또는 %v의 사용
  • 뮤텍스 대신 버퍼링된 채널의 사용

유효하지 않은 로컬 스타일 고려 사항의 예:

  • 코드의 줄 길이 제한
  • 어설션 기반의 테스트 라이브러리 사용

로컬 스타일이 스타일 가이드와 다르지만 가독성 영향이 한 파일로 제한된 경우, 일반적으로 일관된 수정이 해당 CL의 범위를 벗어나기 때문에 코드 검토에서 문제가 발견됩니다. 그 시점에서 문제를 추적하기 위해 버그를 보고하는 것이 적절합니다.

스타일 가이드 위반으로 인해 기존 스타일 편차가 악화되거나, 더 많은 API 표면에 노출되거나, 편차가 존재하는 파일 수가 증가하거나, 실제 버그가 도입된다면, 로컬 일관성은 새로운 코드에 대해 스타일 가이드를 위반할 수 있는 타당한 근거가 아닙니다. 이러한 경우에는 작성자가 현재 CL 이전에 기존 코드베이스를 정리하거나, 리팩토링을 수행하거나, 로컬 문제를 악화시키지 않는 대체 방법을 찾아야 합니다.

This post is licensed under CC BY 4.0 by the author.

[번역] Go Style Guide

[번역] Go Style Decision

Comments powered by Disqus.