패키지

다른 언어에서의 네임스페이스와는 다르게, Go에서 코드들을 묶는 가장 큰 단위는 패키지를 사용한다.

그 중 메인 패키지는 프로그램의 시작점인 메인 함수를 포함하는 패키지를 뜻한다.

OS가 실행파일을 메모리로 로드한 뒤 코드를 실행할 메인 함수가 반드시 포함되어야 하며, 이러한 메인함수는 하나만 존재해야 한다.

메인 패키지 외 다른 패키지들은 임포트해서 사용할 수 있는데, 훌륭한 개발자들이 이미 만들어놓은 패키지가 굉장히 많기 때문에 찾아보고 사용할 수 있다.

일반적으로 처음 배울 때 가장 먼저 사용하게되는 패키지로는 표준입출력을 담당하는 fmt가 대표적이다.

만약 표준 패키지로 제공되지 않더라도 사람들이 공개해놓은 외부 패키지들도 찾아보도록 하자.


패키지는 import 명령어로 사용할 수 있으며, 아래처럼 하나의 패키지를 임포트하거나, 괄호로 묶어서 여러 패키지들을 한번에 가져올 수도 있다.

import "fmt"

import (
    "bufio"
    "os"
    "math/rand"
)

이렇게 패키지를 가져올 경우 패키지에서 제공하는 여러 함수나 구조체들을 사용할 수 있는데, 패키지명에 .을 붙여 가져온다.

대표적으로 fmt.Println()과 같은 함수가 있는데, 이때 패키지 멤버들 중에 이름이 대문자로 시작하는 것들에 대해서만 가져올 수 있다는 점을 주의해야 한다.

예를 들어 fmt 패키지 안에 test 라는 함수가 있을 경우에는 이는 외부에 공개되지 않기 때문에 내가 패키지를 임포트했다고 할지라도 사용할 수 없는 것이다.

math/rand 패키지 같은 경우에는 경로가 여러개 붙어있는데, 이런 경우 패키지명은 경로 전체가 아닌 마지막 폴더명으로 간주된다.

rand.Int()처럼 사용한다(math/rand.Int() 아님).

패키지를 임포트할 때 별명을 붙일 수 있는데, 별명을 붙여줄 경우에는 해당 별명으로 패키지 멤버들에 접근할 수 있다.

아래처럼 서로 다른 패키지들 사이에 경로는 다르지만 패키지명이 중복될 수도 있는데, 이럴 때 별명을 붙임으로써 문제 해결이 가능하다.

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

func main() {
    ttemplate.New()
    htemplate.New()
}


임포트된 패키지들은 컴파일러에 의해서 전역 변수가 초기화되고, 패키지 내에 존재하는 init() 함수가 실행된다(없을 경우 실행 안됨).

예를 들어 아래의 test 패키지를 누군가가 임포트해갔다고 생각해보자.

package test

import "fmt"

var (
    a = 1
    b = foo()
    c = 3
)

func foo() int {
    c += 100
    return 2
}

func init() {
    a *= 10
    b *= 10
    c *= 10
}

func CheckValue() (int, int, int) {
    return a, b, c
}

먼저 전역변수 a,b,c 값이 초기화되는데, b 가 foo 함수를 호출함으로써 전역변수 c 는 103 으로 변경된다.

전역 변수들의 초기화 이후에는 init 함수를 찾아 실행하기 때문에, 각 값들에 10씩 곱해지는 것이다.

이때 foo 나 init 등의 함수명이 소문자로 시작하기 때문에 외부에서 해당 함수들에 접근할 수 없고, 변수 역시 마찬가지이므로 따로 CheckValue 라는 함수를 만들어서 호출할 수 있게 해준다.

이 패키지를 임포트해간 사람은 test.CheckValue() 와 같이 사용함으로써 (10, 20, 1030)의 값을 반환받게 되는 것이다.

이렇게 임포트할 때 컴파일러가 init 함수 등을 호출함으로써 세팅을 하기 때문에, 아래처럼 패키지를 임포트하는 케이스도 생긴다.

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

go-sqlite3 패키지를 임포트해 이 안에 있는 init() 함수를 실행시킴으로써 sql 패키지에 sqlite3 을 사용하는 방식의 코드인데, 이 때 go-sqlite3 패키지는 초기화를 위해서 불러온 것이고 실질적으로 사용은 하지 않는다.

Go에서는 패키지를 임포트만 하고 사용하지 않을 경우에 에러를 발생시키기 때문에, 해당 패키지 앞에 _ 라는 더미값의 별명을 붙여줌으로써 문제를 해결할 수 있다.