[Golang] Package

2021-11-05 hit count image

Golang에서 Package(패키지)에 대해 자세히 알아보고 사용하는 방법에 대해서도 알아봅시다.

개요

이번 블로그 포스트에서는 Golang의 Package(패키지)를 자세히 알아보고 사용하는 방법에 대해서 살펴보려고 합니다. 이 블로그 포스트에서 소개하는 코드는 다음 링크를 통해 확인하실 수 있습니다.

패키지

Golang에서 패키지는 코드를 묶는 기본 단위입니다. 따라서 모든 코드는 반드시 패키지로 묶어야 합니다.

또한 Golang으로 프로그램을 작성한다면, 반드시 main 패키지가 존재해야 하며, main 패키지안에 main 함수가 정의되어야 합니다. Golang에서는 이 main 패키지의 main 함수가 프로그램의 시작 위치가 됩니다.

main 패키지이외에 패키지들은 실행 시점(main 함수)을 포함하지 않은 패키지들로써, main 패키지의 보조 패키지로 동작하게 됩니다.

이런 모든 패키지를 가지고 있는 것이 모듈입니다. 모듈에 대해서는 아래에 링크를 참고하시기 바랍니다.

패키지 사용

그럼 패키지의 사용을 확인하기 위해 main.go 파일을 다음과 같이 수정합니다.

package main

import (
  "fmt"
  "math/rand"
)

func main() {
  fmt.Println(rand.Int())
}

이 예제에서는 fmt 패키지와 math 패키지 하위의 rand 패키지를 가져왔으며, 화면에 숫자를 랜덤하게 표시하고 있습니다. 이렇게 작성한 코드를 실행하면, 다음과 같은 결과를 얻을 수 있습니다.

# go run main.
5577006791947779410

패키지 외부 공개

Golang에서는 패키지내 타입, 전역 변수, 상수, 함수, 메서드 등을 외부로 공개하여 다른 패키지에서 사용할 수 있습니다. 패키지에서 외부로 공개할 때에는 대문자로 시작하며, 외부로 공개하지 않을 때에는 소문자로 시작합니다.

패키지를 외부에 공개하고 공개된 패키지를 사용할 때에는 모듈을 사용해야 합니다. 모듈에 대한 자세한 내용은 아래에 링크를 참고하시기 바랍니다.

외부에 공개하는 패키지를 만들어보기 위해 greeting 폴더를 만들고 greeting.go 파일을 생성하고 다음과 같이 수정합니다.

package greeting

import "fmt"

const PI = 3.14

func PrintGreeting() {
  print()
}

func print() {
  fmt.Println("Hello, World!")
}

이렇게 수정하였다면, 다음 명령어를 사용하여 greeting 모듈을 생성합니다.

go mod init github.com/dev-yakuza/study-golang/package/greeting

greeting 패키지는 greeting 모듈을 통해 공유될 예정입니다. 이렇게 공유되는 greeting 패키지를 사용하는 main 패키지를 만들어 봅시다.

greeting 폴더와 같은 위치에 main 폴더를 생성하고 해당 폴더에 main.go 파일을 만들고 다음과 같이 수정합니다.

package main

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/package/greeting"
)

func main() {
  fmt.Println(greeting.PI)
  greeting.PrintGreeting()
}

이제 다음 명령어를 사용하여 모듈을 생성합니다.

go mod init github.com/dev-yakuza/study-golang/package/main

모듈을 생성하였다면, 로컬에 존재하는 greeting 모듈을 사용하기 위해 다음 명령어를 실행합니다.

go mod edit -replace github.com/dev-yakuza/study-golang/package/greeting=../greeting

마지막으로 다음 명령어를 실행하여 모듈을 설정합니다.

go mod tidy

이렇게 만든 프로그램을 실행하면 다음과 같은 결과를 확인할 수 있습니다.

# go run main.go
3.14
Hello, World!

init 함수

패키지는 init 함수를 가질 수 있으며, 이 init 함수는 패키지가 import될 때 한번 호출됩니다. 패키지의 init 함수는 패키지내의 전역 변수를 초기화할 때 사용합니다.

이를 확인하기 위해 greeting/greeting.go 파일을 다음과 같이 수정합니다.

package greeting

import "fmt"

const PI = 3.14

var globalVal = 0

func init() {
  fmt.Println("init()", globalVal)
  globalVal++
}

func PrintGreeting() {
  print()
}

func print() {
  fmt.Println("Hello, World!", globalVal)
}

그리고 main 폴더에서 다음 명령어를 실행하여 수정된 내용을 확인합니다.

go run main.go

프로그램이 실행되면 다음과 같은 결과를 확인할 수 있습니다.

init() 0
3.14
Hello, World! 1

별칭 패키지명

Golang에서는 이처럼 미리 만들어진 패키지들을 활용하여 프로그램을 작성하게 됩니다. 하지만, 모든 개발자가 약속하고 패키지를 제작하는 것이 아니기 때문에, 때로는 패키지명이 중복되는 경우가 있습니다.


import (
  "text/template"
  "html/template"
)

template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)
template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)

이처럼 중복된 패키지명을 사용해야하는 경우, 별칭 패키지명(Alias Package Name)을 사용할 수 있습니다.


import (
  htmlplate "html/template"
  "text/template"
)

template.New("foo").Parse(`{{define "T"}}Hello{{end}}`)
htmlplate.New("foo").Parse(`{{define "T"}}Hello{{end}}`)

이를 확인하기 위해 아래와 같이 main.go 파일을 수정합니다.


package main

import (
  "fmt"
  htmlplate "html/template"
  "text/template"
)

func main() {
  fmt.Println(template.New("foo").Parse(`{{define "T"}}Hello{{end}}`))
  fmt.Println(htmlplate.New("foo").Parse(`{{define "T"}}Hello{{end}}`))
}

이를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

# go run main.go
&{foo 0xc00010e480 0xc00006c050  } <nil>
&{<nil> 0xc000104140 0xc00010e900 0xc00006c0a0} <nil>

빈칸 지시자

Golang에서는 패키지도 변수와 같이 선언하였다면 반드시 사용해야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다. 하지만, 패키지를 직접 사용은 하지 않지만 import를 통해 init() 함수를 실행시켜 패키지를 초기화해야 하는 경우가 있습니다. 이때 별칭 패키지명빈칸 지시자(Blank identifier, _)를 사용하여 에러를 해결할 수 있습니다.

import (
  "database/sql"
  _ "github.com/mattn/go-sqlite3"
)

패키지를 import하면 기본적으로 패키지 안에 있는 init() 함수가 호출되게 됩니다. 즉, 패키지를 import하여 init() 함수를 호출할 필요는 있지만, 다른 기능을 사용하지 않을 때, 위와 같이 별칭 패키지명과 빈칸 지시자를 활용합니다.

이를 확인하기 위해 init 함수의 예제에서 main.go 파일을 다음과 같이 수정합니다.

package main

import (
  "fmt"

  _ "github.com/dev-yakuza/study-golang/package/greeting"
)

func main() {
  fmt.Println("Hello")
}

그리고 이를 실행하면 다음과 같은 결과를 확인할 수 있습니다.

# go run main.go
init() 0
Hello

import cycle

Golang에서 서로 다른 패키지가 서로 서로를 import하면 import가 계속 반복되므로 import cycle 에러가 발생합니다.

이를 확인하기 위해 greeting/greeting.go 파일을 생성한 후, 다음과 같이 수정합니다.

package greeting

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/package/name"
)

func Print() {
  fmt.Println("Hello, ", name.Name)
}

이제 다음 명령어를 실행하여 모듈을 생성합니다.

# cd greeting
go mod init github.com/dev-yakuza/study-golang/package/greeting

이제 import cycle 에러를 발생시키기 위해, greeting 폴더와 동일한 위치에 name/name.go 파일을 생성한 후, 다음과 같이 수정합니다.

package name

import (
  "github.com/dev-yakuza/study-golang/package/greeting"
)

var Name = "John"

func print() {
  greeting.Print()
}

이렇게 생성한 name 패키지도 다음 명령어를 실행하여 모듈을 생성합니다.

# cd name
go mod init github.com/dev-yakuza/study-golang/package/name

이렇게 만든 각각의 모듈에서 서로 참고하는 모듈을 연결하기 위해 다음 명령어를 실행합니다.

cd greeting
go mod edit -replace github.com/dev-yakuza/study-golang/package/name=../name
go mod tidy
cd ..
cd name
go mod edit -replace github.com/dev-yakuza/study-golang/package/greeting=../greeting
go mod tidy

이제 name.go 파일을 열고 greeting 패키지를 추가하는 곳을 살펴보면 다음과 같이 import cycle not allowed 에러가 발생하는 것을 확인할 수 있습니다.

import cycle not allowed

완료

이것으로 Golang에서 패키지란 무엇이며 어떻게 사용하는지에 대해서 알아보았습니다. 이제 패키지를 제작하여 외부에 공유하거나, 외부로 공유된 패키지를 사용하여 프로그램을 제작해보시기 바랍니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

책 홍보

스무디 한 잔 마시며 끝내는 React Native 책을 출판한지 벌써 2년이 다되었네요.
이번에도 좋은 기회가 있어서 스무디 한 잔 마시며 끝내는 리액트 + TDD 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
Posts