Go를 배워보자 5일차 - Package

0

Go

목록 보기
5/12

package

1. 패키지란?

패키지는 유사한 기능을 수행하는 코드들의 집합이다. fmt, strings 등도 모두 패키지이다.

코드의 일부가 여러 프로그램 간에 공유되는 경우 코드를 패키지로 분리하는 것을 고려해 볼 수 있다.

그러나, golang의 패키지 시스템은 자바나 c/c++처럼 그렇게 간단하지는 않다.

일단, GOPATH와 GOROOT에 대해서 알아보자

2. GOPATH, GOROOT

  • GOROOT
    go를 설치했을 때 Go관련 실행 파일, sdk 등이 위치한 곳으로, 이곳에 파일을 위치하면 go파일들은 모두 참조할 수 있다.

  • GOPATH
    bin, pkg, src 폴더가 위치하는 path로 go get 명령어를 통해 받은 패키지나 라이브러리, 소스파일이 위치하는 곳이다.

    즉, ide를 통해서 go 개발을 진행하는 프로젝트 폴더가 되는 곳이다.

    • bin : 실행 프로그램들로 build시에 컴파일된 실행 가능한 바이너리 프로그램이 저장된다.

    • pkg : 컴파일된 바이너리 패키지 파일이 저장된다.

    • src : go 소스코드가 위치하는 곳이다.

즉, GOROOT는 우리가 이전까지 신나게 사용했던 fmt, strings 같은 패키지들이 있는 곳으로 설치 파일과 sdk 등이 있다.

GOPATH는 개발을 진행할 프로젝트가 있는 곳으로, GOPATH가 정확히 잡혀야지만 패키지들 간의 링킹이 잘된다.

GOPATH는 어떻게 확인할 수 있을까??

terminal이나, cmd의 다음의 명령어를 입력해보자

go env

그러면 쭉 환경변수들이 나오게 될 것이고, 자신의 GOPATH가 어딘지 알게 된다.

필자의 경우는 window이기 때문에

c:\Users\{user이름}\go

에 있었다.

여기에 들어가서 다음의 src 폴더를 만들자

다음과 같이 src폴더를 만들고, 계산기 프로그램을 만들어보자


다음의 폴더 두가지를 만들어놓고 코드를 넣어보도록 하자

src/calculate/calculate.go

package calculate

import "fmt"

func Add(a int, b int) int {
	return a + b
}

func Sub(a int, b int) int {
	return a - b
}

func Mul(a int, b int) int {
	return a * b
}

func Div(a int, b int) (int, error) {
	if b == 0 {
		return 0, fmt.Errorf("can't divide")
	}
	return a / b, nil
}

일반적으로 패키지 디렉터리의 이름은 패키지의 이름과 동일한 이름을 갖는다. 패키지의 이름이 calculate이므로, 폴더도 calculate로 한 것이다.

이제 calculator폴더로 가서 main 코드를 만든다음 다음의 calculate 패키지를 가져와보자

src/calculator/main.go

package main

import (
	"bufio"
	"calculate"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	a, b := input2Value()
	ret := calculate.Add(a, b)
	fmt.Println(ret)
}

func input2Value() (int, int) {
	reader := bufio.NewReader(os.Stdin)
	input1, err := reader.ReadString('\n')
	input1 = strings.TrimSpace(input1)
	if err != nil {
		fmt.Println(err)
	}
	input2, err := reader.ReadString('\n')
	input2 = strings.TrimSpace(input2)
	if err != nil {
		fmt.Println(err)
	}
	convertedInt1, err := strconv.Atoi(input1)
	if err != nil {
		fmt.Println(err)
	}
	convertedInt2, err := strconv.Atoi(input2)
	if err != nil {
		fmt.Println(err)
	}
	return convertedInt1, convertedInt2
}

그리고 main.go를 실행해보도록하자

입력 두개를 받아서 더해주는 코드이기 때문에 1 엔터 2엔터하면 자동으로 3이 나오게 될 것이다.

만약 다음의 에러가 발생했다면

~ package calculate is not in GOROOT ~

필자는 이것 때문에 꽤 난감했는데 해결 방법은 간단하다.

터미널에서 다음의 명령어를 입력하도록 하자

go env -w GO111MODULE=auto

GO111MODULE 은 모듈을 찾을 때 어디서 찾을꺼냐를 설정해주는 on/off/auto 환경변수이다.

즉, 모듈의 동작이 GO111MODULE에 의해 제어된다는 것이다.

  • GO111MODULE
    • off : 빌드 중에 GOPATH를 사용한다.
    • on : 빌드 중에 GOPATH 대신 모듈에 있는 패키지를 사용한다.
    • auto : 현재 디렉터리가 GOPATH 외부에 있고, go.mod 파일이 포함된 경우 모듈을 사용한다. 그렇지 않으면 GOPATH의 패키지를 사용한다.
    • : 아무것도 없는 경우이다. 이 경우는 모듈 링킹이 GOAPTH에 있어도 안되고, go.mod파일이 있어도 안된다.

필자의 경우 GO111MODULE에 아무것도 없어서 파일을 실행하지 못 했었다.

위의 설정 코드를 돌리면 실행가능하다.

  • go.mod
    갑자기 go.mod 이야기가 나왔는데, nodejs의 package.json, java의 maven, graddle과 비슷하다고 생각하면 된다.

    즉, 의존성을 관리해주는 파일이다. 이후에 좀 더 자세히 배우도록 하자

3. 패키지 네이밍 컨벤션과 상수

  • 패키지 이름에는 소문자만을 사용한다.

  • 의미가 명확한 경우 축약어를 사용한다.

  • 가능한 단어 하나를 두도록 하고 두 단어가 붙는다해도 camelCase를 사용하지 않는다.

  • 임포트된 패키지의 변수와 지역 변수가 충돌하지 않도록 한다.

  • 패키지에서 외부로 노출될 함수는 앞 글자가 대문자로 시작한다.

  • 상수
    많은 패키지가 외부로 상수를 제공하곤 한다. 상수를 제공하는 이유는 해당 패키지가 가진 기본 설정 값이 있기 때문이다.

    만약 상수로 값을 설정하지 않으면 해당 값을 외부 패키지에서 바꿀 수 있게되고ㅓ, 런타임 중에 어떤 패키지가 값을 바꾸었는 지도 모르게되는 수렁텅이에 빠질 수 있다.

    따라서 각 패키지마다에 유효한 설정값들은 const 인 상수로 설정해주는 것이 좋다.

    기본적으로 변수 선언 및 초기화 방식은 같고 var이 아니라 const를 사용해야 하는 차이점 밖에 없다.

    const 상수이름 타입 =

    한 가지 큰 차이점은 const는 선언과 동시에 할당을 해주어야 한다. 즉, 초기화 작업이 필수적이라는 것이다.

    변수만 선언해놓고, 값은 나중에 할당해줄 수 없다는 것이다.

    또한, 가장 큰 차이점 중 하나는 단축 변수 선언을 사용할 수 없다.

    상수이름 :=// 불가

    이유는 간단하다. 단축 선언은 var인지 const인지 모르기 때문이다.

4. 중첩된 패키지에서의 임포트 경로

위의 코드는 다음의 경로였다

go  - bin  
    - pkg  
    - src
        - calculate
            - calculate.go
        - calculator
            - main.go 

그렇다면 이번에는 calculate아래에 info 패키지를 만들어서 main에서 부르도록해보자

src/calculate/info/info.go

package info

const CALCULATE_NAME string = "sn-12"
const CALCULATE_DUE_DATE string = "1990-02-01"

다음의 info정보를 담은 패키지를 만들고 main.go에서 불러보자

package main

import (
	"bufio"
	"calculate"
	"calculate/info"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	a, b := input2Value()
	ret := calculate.Add(a, b)
	fmt.Println(info.CALCULATE_NAME) // sn-12
	fmt.Println(ret)
}

func input2Value() (int, int) {
	reader := bufio.NewReader(os.Stdin)
	input1, err := reader.ReadString('\n')
	input1 = strings.TrimSpace(input1)
	if err != nil {
		fmt.Println(err)
	}
	input2, err := reader.ReadString('\n')
	input2 = strings.TrimSpace(input2)
	if err != nil {
		fmt.Println(err)
	}
	convertedInt1, err := strconv.Atoi(input1)
	if err != nil {
		fmt.Println(err)
	}
	convertedInt2, err := strconv.Atoi(input2)
	if err != nil {
		fmt.Println(err)
	}
	return convertedInt1, convertedInt2
}

이처럼 중첩된 패키지에서는 마치 폴더안에 폴더에 접근하듯이 경로를 써주면 된다. 무척이나 간단하다.

"calculate/info"

src가 하나의 루트가되는 것이고, 여기서부터 쭉 타고들어오면 된다.

중첩된 패키지를 import했다면 패키지 명으로 간단히 접근이 가능하다.

fmt.Println(info.CALCULATE_NAME) // sn-12

5. package 배포하기

golang은 패키지 관리 프로그램으로 go get을 이용하여 쉽게 패키지를 다운받을 수 있다.

이 처럼 go get으로 다른 사람이 내 패키지를 다운받을 수 있도록 올릴 수 있는데, 이는 추후에 더 배워보고 하도록하자

0개의 댓글