티스토리 뷰
Package names
패키지를 임포트하면, 패키지 이름은 해당 패키지에 포함된 컨텐츠에 대한 접근자로 사용됩니다.
import "bytes"
func main() {
_ = bytes.NewBuffer([]byte{})
}
좋은 패키지 이름은 짧고 간결하며 관련된 컨텐츠들을 연상시킬 수 있어야 합니다.
네이밍 컨벤션에 따라 패키지 이름은 소문자, 단일 단어여야 하며 _
언더스코어나 대문자를 혼합할 필요는 없습니다.
패키지 이름은 유니크할 필요는 없으며, 만약 동일한 이름의 패키지를 사용할 경우 별칭(alias)을 붙여줍니다.
패키지를 임포트할 때 붙여준 이름이 사용중인 패키지를 결정하므로 충돌은 거의 발생하지 않습니다.
임포트한 패키지의 경로가 hello/world
일지라도, 패키지 이름은 원본 디렉토리의 기본 이름인 world
를 사용합니다.
import (
"hello/world"
hw "hello/world" // add alias
bw "bye/world" // add alias
. "fmt" // avoid this way to import
)
func main() {
world.Print()
hw.Print()
bw.Print()
Println("Hello World")
}
패키지를 임포트한 사용자는 패키지를 사용하기 위해 패키지이름.컨텐츠
형식으로 접근해야 하므로 해당 패키지로부터 외부로 노출된(exposed) 컨텐츠의 이름은 패키지이름과 반복을 피하는 것이 좋습니다.
예를 들어, repository
패키지의 Repository
인스턴스를 생성하는 NewRepository()
라는 생성자 함수가 있다고 봅시다. 생성자 함수를 사용하기 위해서는 repository.NewRepository()
로 접근을 해야 합니다.
package main
import "repository"
func main() {
_ = repository.NewRepository()
}
여기서 패키지이름을 통해 사용자가 repository
와 관련된 인스턴스를 생성하는 것이 명백하므로NewRepository
보다는 반복을 줄여서 repository.New()
로 접근하는 것이 더 간결하고 명백해 집니다.
package main
import "repository"
func main() {
_ = repository.New()
}
물론 하나의 패키지 안에 여러 개의 타입이 있고 대응되는 생성자가 여러 개인 경우처럼 예외상황이 있을 수 있으므로, 패키지 구조를 잘 살펴보고 상황에 따라 좋은 이름을 선택하도록 합시다.
Getters
Go에서는 getter와 setter를 자동으로 지원하지 않습니다. 필요에 따라 정의해서 사용하면 되지만, getter의 이름에 Get
을 붙이는 것은 불필요해 보이는군요.
package contract
type Contract struct {
owner string // lower case, not exported field
}
Contract
구조체의 owner
필드는 소문자로 시작하여 해당 패키지 내부에서만 직접 접근하여 사용할 수 있는 private 필드입니다. 따라서 외부 패키지에서 임포트하여 사용할 경우 값을 직접 초기화할 수도 없을뿐더러 읽어올 수도 없습니다.
package main
import "contract"
func main() {
c := contract.Contract{owner: "Lee"} // unknown field owner in struct literal 컴파일 에러!
println(c.owner) // c.owner undefined (type contract.Contract has no field or method owner) 컴파일 에러!
}
private 필드의 값을 설정하고 가져오기 위한 getter와 setter 메서드를 아래와 같이 정의했습니다.
package contract
type Contract struct {
owner string
}
func (c Contract) Owner() string {
return c.owner
}
func (c *Contract) SetOwner(newOwner string) {
c.owner = newOwner
}
getter의 이름은 Get
으로 시작하지 않으면서 필드명과 동일하지만, 외부에서도 접근할 수 있도록 대문자로 시작합니다. setter의 이름은 Set
과 필드명을 더하여 파스칼 케이스로 적어주었습니다.
package main
import "contract"
func main() {
c := contract.Contract{}
c.SetOwner("Lee")
println(c.Owner())
c.SetOwner("Park")
println(c.Owner())
}
Lee
Park
만약 Contract
구조체의 필드가 아래와 같이 대문자로 시작한다면 외부 패키지에서도 접근하여 사용할 수 있으므로 getter와 setter는 굳이 필요하지 않을 것입니다.
package contract
type Contract struct {
Owner string
}
package main
import "contract"
func main() {
c := contract.Contract{Owner: "Lee"}
println(c.Owner)
c.Owner = "Park"
println(c.Owner)
}
Lee
Park
Interface names
관례적으로, 단일 메서드를 가진 인터페이스의 이름은 해당 메서드 이름뒤에 er
을 붙이거나 메서드 이름과 유사한 행위자 명사로 짓습니다.
type Stringer interface {
String() string
}
이 외에도 Write - Writer, Read - Reader, Format - Formatter, Close - Closer 등이 있습니다.
혼란을 방지하기 위해 이러한 단일 메서드 인터페이스가 가진 메서드 이름과 시그니처가 동일하지 않다면 해당 메서드 이름을 사용하는 것을 지양해야 합니다.
반대로 해당 기능이 필요하거나 구현해야 한다면 동일한 메서드 이름과 시그니처를 사용할 것을 권장합니다.
package main
import (
"fmt"
)
type Stringer interface {
String() string
}
type MyString string
func (s MyString) String() string {
return "MyString implements Stringer interface!"
}
// func (s MyString) String(x string) string {
// return "Signature doesn't match with method of Stringer interface"
// }
// func (s MyString) ToString() string {
// return "You'd better name this method as String..."
// }
func main() {
s := MyString("")
fmt.Println(s)
}
MyString implements Stringer interface!
MixedCaps
Go에서는 _
언더스코어(스네이크 케이스)보다는 MixedCaps
, mixedCaps
와 같이 대문자로 단어들을 연결(파스칼, 카멜 케이스)합니다
Reference
'Go > 문서 읽기' 카테고리의 다른 글
Go 1.22.0 업데이트 살펴보기 (0) | 2024.02.08 |
---|---|
[Go] Vault를 사용해 시크릿 저장하고 불러오기 기초 (0) | 2023.10.25 |
[Go] 컨텍스트(Context) (1) | 2023.10.05 |