[Go 공식문서 한국어 정리] ⑯27. 효율적인 Go 프로그래밍
https://go.dev/doc/effective_go
[Go 공식문서 한국어 정리] ⑯27. 효율적인 Go 프로그래밍
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 서론
이 문서는 명확하고 관향적인(idiomatic) Go 코드를 작성하는 데 필요한 팁을 제공합니다. Go는 다른 언어에서 아이디어를 차용하지만, 고유의 특성으로 인해 C++이나 Java를 그대로 번역하는 것은 만족스럽지 않은 결과를 내빼니다. Go의 관점에서 문제를 생각해야 효율적인 코드를 쓸 수 있습니다.
2. 핵심 개념
• Go의 핵심은 단순성(simplicity), 신뢰성(reliability), 효율성(efficiency)입니다.
• 관향적인(idiomatic) Go 코드를 쓴다는 것은 다른 Go 프로그래머들이 쉽게 이해할 수 있도록 합니다.
• 표준 라이브러리의 소스코드는 특수한 경우가 아니면 직접 사용하지 않는 것이 라이브러리 사용법의 좋은 예시가 됩니다.
3. 주요 내용 상세
3.1 포맷팅(gofmt)
• gofmt을 사용하여 코드의 일관성을 유지합니다. 탭을 사용한 들여쓰기를 권장합니다.
• Go에는 줄 길이 제한이 없습니다. 네로 가득한 카드를 원하지 않습니다.
• if, for, switch등의 제어 구조는 궈호가 필요없습니다.
3.2 이름집징
• 패키지명은 소문자, 한 단어로, 언더스코어를 사용하지 않습니다.
• 가시적인 이름은 메서드 이름 + er 접미사를 사용합니다(Reader, Writer, Formatter).
• 대문자로 시작하는 이름은 패키지 밖에서 보일 수 있습니다(exported).
• 다음 단어를 대문자로 하여 MixedCaps을 사용합니다(myVariable, MyType).
• Getter는 Get을 접두어로 붙이지 않습니다. 필드 owner의 경우 메서드는 Owner()를 사용합니다.
3.3 제어 구조
• if: 필수 궈호를 사용하여 순수한 if을 여러 줄로 작성하는 것이 좋은 스타읽입니다.
• if err := file.Chmod(0664); err != nil { ... }: if문에서 지역변수를 초기화할 수 있습니다.
• for: C의 for과 while을 통합한 형태. for init; condition; post {}, for condition {}, for {}(무한루프).
• range: 배열, 슬라이스, 문자열, 매프, 채널을 순회할 때 사용. for key, value := range m {}
• switch: 표현식이 상수일 필요없고 자동으로 fallthrough를 하지 않습니다.
• type switch: 인터페이스의 동적 타입을 감지할 때 사용. switch t := t.(type) { case int: ... }
3.4 함수
• 다중 반환값: Go의 특징적인 기능. func Write(b []byte) (n int, err error)
• 명명된 결과 매개변수: 반환값에 이름을 붙이면 자동으로 영으로 초기화되고 return만으로 반환할 수 있습니다.
• defer: 함수가 반환하기 전에 특정 함수를 호출하도록 예약. 자원 해제, 무역해제에 사용. LIFO 순서로 실행됩니다.
3.5 데이터 할당
• new(T): 제로 화된 메모리를 할당하고 *T를 반환. 제로값이 유용한 타입을 설계하면 new로 추가 초기화없이 사용할 수 있습니다.
• make(T, args): 슬라이스, 매프, 채널만 생성. 초기화된 값을 반환합니다(T, 아니다 *T).
• 배열: 값이며 할당시 사본이 배열이 복사됩니다. 크기가 타입의 일부입니다([10]int와 [20]int는 다른 타입).
• 슬라이스: 배열을 감싼는 더 일반적인 인터페이스. 참조를 풀는 것이므로 할당시 사본을 공유합니다. append을 사용해 요소를 추가할 수 있습니다.
• 매프: 키-값 쌍으로 데이터를 저장. 참조형이므로 함수내에서 변경시 외부에도 반영됩니다. 없는 키를 조회하면 해당 타입의 영으값을 반환. comma ok 관용을 사용해 존재 여부를 확인할 수 있습니다.
3.6 출력(Print)
• fmt패키지의 Printf, Print, Println을 사용합니다.
• %v: 기본 포맷으로 모든 타입을 출력. %+v은 구조체 필드명을, %#v은 Go 문법으로 출력.
• %T: 값의 타입을 출력.
• String() string 메서드를 구현하면 사용자 정의 타입의 기본 출력을 제어할 수 있습니다. 재귀적인 String은 주의.
3.7 초기화
• 상수: 컴파일 시점에 생성되며 숫자, 문자, 문자열, 부울만 가능. iota를 사용해 열거형 상수를 만들 수 있습니다.
• init(): 패키지가 초기화될 때 마지막으로 호출되는 함수. 파일당 여러 개의 init을 정의할 수 있습니다.
3.8 메서드
• 값 수신자(value receiver): 수신자가 복사되므로 원본을 수정할 수 없습니다.
• 포인터 수신자(pointer receiver): 원본을 수정할 수 있습니다. 값에서도 자동으로 호출될 수 있습니다.
• 포인터 메서드는 포인터에서만 호출할 수 있으며, 값 메서드는 포인터와 값 모두에서 호출할 수 있습니다.
3.9 인터페이스
• 인터페이스는 타입이 아니라 메서드의 집합입니다.
• 약한 결합(Implicit interface): 정의와 별도로 구현을 선언하지 않아도 메서드만 구현하면 자동으로 인터페이스를 수행합니다.
• 타입 단언(type assertion): x.(T)를 사용해 인터페이스의 동적 타입을 확인할 수 있습니다. x.(T)의 결과는 T타입의 값입니다.
• 빈 인터페이스(interface{}): 모든 타입을 담을 수 있습니다.
3.10 임베딩
• 구조체에 인터페이스나 타입을 임베딩하면 포함된 메서드를 제공하는 것처럼 동작합니다.
• 이를 사용하여 코드의 중복을 줄이고 구조를 더 유연하게 만들 수 있습니다.
3.11 동시성(Concurrency)
• goroutine: 가벼운 스레드로 함수를 동시에 실행. go func() { ... }()
• channel: goroutine간의 통신을 위한 파이프라인. ch := make(chan int)
• select: 여러 채널의 얰션을 기다리는 제어구조.
• 채널은 소유권을 전달하여 동시성을 관리합니다. 정보를 하나의 goroutine가 소유하고 다른 goroutine에겐 도둑을 주는 패턴이 자주 사용됩니다.
3.12 에러 처리
• Go에서 에러는 반드시 값으로 반환되며, 다중 반환값의 두 번째 값으로 자주 사용됩니다.
• 예외적인 상황은 panic을 사용하며, recover를 통해 복구할 수 있습니다.
• 다정 오류를 하나의 에러로 통합하고 사용자에겐 설명하는 것이 좋은 스타읽입니다.
4. 실전 활용
• 표준 라이브러리의 소스코드를 읽으면서 관향을 배우십시오.
• gofmt을 사용해 코드의 포맷을 일관적으로 유지하십시오.
• 오류 처리시 다중 반환값과 comma ok 관용을 적응합니다.
• 포인터 vs 값 수신자의 차이를 이해하고 적절히 선택하십시오.
• 동시성이 필요한 경우 goroutine과 channel을 활용합니다.
5. 정리
• 효율적인 Go 코드를 쓴다는 것은 Go의 처험과 관향을 이해하는 것에 극해있습니다.
• gofmt, 명명균, 암물에 의한 가시성, 간결한 제어 구조, 다중 반환값, defer, 약한 결합 인터페이스, 동시성을 활용하십시오.
• 표준 라이브러리의 예시를 참고하고, go vet와 테스트를 작성하여 코드 품질을 높이십시오.
#Go #Golang #EffectiveGo #관향 #Idiomatic #공식문서

오뉴노노 님의 최근 댓글
ㅋㅋㅋㅋㅋ 2019 01.14 잘 읽었습니다 2018 12.30 포인트가 없어서 아직 시작을 못하고있는데요! 글은 잘 읽었습니다! 포인트 쌓고 도전할거에요 2018 12.30