Go도 당연히 다른 언어처럼 작성한 코드의 재사용이 가능하다. 쉬이 예상 가능하듯, 패키지를 만들고 이를 임포트해 재사용할 수 있다.
그런데 Go는 C++이나 Java와는 달리 접근제한자(access modifier)가 없다. 접근제한자 대신 매우 단순한 한 가지 규칙에 따라 외부 접근 가능 여부를 판단한다.
외부 패키지에서 접근이 가능하게 하려면—export 하려면— 첫 번째 문자를 대문자로 하라.
이 규칙은 함수나 변수, 상수뿐만 아니라 구조체와 관련된 필드, 메서드 등에도 그대로 적용된다.
대문자에 대해서 좀 더 자세히 언급하면, Unicode “Lu” class에 포함되는 문자를 의미한다. 즉, 𝓐bc
이런 이름도 외부에서 접근할 수 있지만, 안타깝게도 한글은 포함되지 않는다.
Python도 접근제한자 없이 underscore(_)나 double underscore(__) 등을 사용해 접근 범위를 지정하지만 ‘우린 모두 19금을 볼 수 있지만 굳이 볼 필요가 없는 것은 안 볼 수도 있는 성숙한 어른이잖아요’ 라며 접근 시도를 자율에 맡기는 반면, Go는 아직 충분히 크지 않은 경우—대문자가 아닌 경우— 에는 접근을 강제로 불허한다.
패키지 만들기
디렉토리 구조는 다음과 같다.
$GOPATH -+- myapp --- main.go
|
+- mypack -+- packa1 -+- packa1.go
| |
| +- packb.go
|
+- packa2 --- packa2.go
패키지 경로와 이름
관례상(by convention) 패키지의 경로와 이름을 같게 할 뿐 둘은 엄연히 다르다.
위 디렉토리 구조에서 packa2, packa2.go, packb.go는 없고, packa1.go를 다음과 같이 작성한 상황을 가정해 보자.
// packa1.go
package packa
import "fmt"
func PackA1() {
fmt.Println("This is PackA1()")
}
이 패키지를 사용하기 위한 main.go는 다음과 같다.
// main.go
package main
import "mypack/packa1" // PATH
func main() {
packa.PackA1() // NAME
}
한 경로에 두 가지 이상의 패키지를 두면…
그럼 한 디렉토리에 있는 소스 파일들이 서로 다른 패키지 이름을 갖는다면 어떻게 될까?
위 예에서 packb.go 파일을 만들고 다음과 같이 작성한 것을 가정한 것이다.
// packb.go
package packb
import "fmt"
func PackB() {
fmt.Println("This is PackB()")
}
이 경우 빌드 시 다음과 같은 오류가 발생한다.
found packages packa (packa1.go) and packb (packb.go) in ~~~/mypack/packa1
한 디렉토리에 다른 이름을 갖는 패키지들이 있다고 Go가 싫어한다.
한 패키지를 둘 이상의 경로에 두면…
반대로 같은 패키지 이름을 갖는 소스 파일들을 여러 경로에 분산해 두면 어떻게 될까?
packb.go는 삭제하고, packa2 디렉토리를 만들어 그 아래에 packa2.go를 다음과 같이 작성한 것을 가정한 것이다.
// packa2.go
package packa
import "fmt"
func PackA2() {
fmt.Println("This is PackA2()")
}
PackA2()
를 호출하기 위해 main.go도 다음과 같이 수정한다.
// main.go
package main
import "mypack/packa1"
import "mypack/packa2"
func main() {
packa.PackA1()
packa.PackA2()
}
이 경우에는 빌드 시 다음과 같은 오류가 발생한다.
./main.go:5:8: packa redeclared as imported package name
previous declaration at ./main.go:4:8
./main.go:8:2: undefined: "mypack/packa2".PackA1
mypack/packa2 경로에 있는 패키지를 임포트하면서 mypack/packa1에서 이미 임포트한 packa가 재정의 되었다는 오류와 이로 인해—mypack/packa2에서 임포트한 패키지가 mypack/packa1에서 임포트한 패키지를 덮어 씀으로 인해— 패키지 packa에서는 PackA1
함수를 찾을 수 없다는 오류이다.
이 문제는 임포트하는 패키지에 별칭(alias)을 달아 해결 가능하다.
// main.go
package main
import a1 "mypack/packa1"
import a2 "mypack/packa2"
func main() {
a1.PackA1()
a2.PackA2()
}
결론
관례라는 용어가 특수활동비 문제로 인해 답습과 비슷한 뜻으로 이해되기는 하지만 최소환 Go의 패키지 경로와 이름에 관련해서는 다 이유가 있어 생긴 것이다. 어떻게 되는지 봤으니 괜히 이상한 시도 하지 말자. 복잡해진다.