[The Go Programming Language] 2장 프로그램 구조 - 2.6 패키지와 파일

in kr-dev •  6 years ago  (edited)

modolee_logo
안녕하세요. 개발자 모도리입니다.
The Go Programming Language 라는 책으로 Go를 공부하고 있으며, 해당 책의 내용을 요약 정리해서 올리려고 합니다. 저는 번역본을 구매해서 공부하고 있습니다.
게시물에 예제코드 라고 나오는 것들은 https://github.com/modolee/tgpl.git 에서 다운 받으실 수 있습니다.

지난 게시물


2장 프로그램 구조

2.6 패키지와 파일

패키지

  • Go의 패키지는 다른 언어의 라이브러리나 모듈과 마찬가지로 모듈화, 캡슐화, 분할 컴파일 및 재사용 등을 지원합니다.
  • 패키지의 소스코드는 하나 이상의 .go 로 끝나는 파일 내에 있으며, 일반적으로 임포트 경로의 마지막 이름과 같은 디렉토리 안에 있습니다.

예시
tgpl/ch1/helloworld 패키지 파일들은 $GOPATH/src/tgpl/ch1/hellworld 디렉토리에 저장됩니다.

  • 패키지는 또한 패키지 외부에서 보이거나 익스포트되는 이름을 제어해 정보를 숨길 수 있게 합니다.
  • Go에서는 식별자를 대문자로 시작하는 것으로 익스포트되는지 여부가 결정됩니다.

패키지 작성 예제

  • 2.5에서 작성했던 패키지를 변형해서 tgpl/ch2/tempconv라는 패키지를 만들어 봅니다.

  • 패키지에서 개별 파일의 선언에 접근하는 방법을 보여주기 위해 패키지 자체는 두 개의 파일에 저장돼 있습니다.

  • 실제로는 이런 작성 패키지는 파일 하나면 충분합니다.

  • 타입, 상수, 메소드 선언들을 tempconv.go에 넣습니다.

//tempconv 패키지는 섭씨와 화씨 변화를 수행합니다.
package tempconv

import "fmt"

type Celsius float64
type Fahrenheit float64

const (
 AbsoluteZeroC Celsius = -273.15
 FreezingC     Celsius = 0
 BoilingC      Celsius = 100
)

func (c Celsius) String() string    { return fmt.Sprintf("%gºC", c) }
func (f Fahrenheit) String() string { return fmt.Sprintf("%gºF", c) }

예제코드 [ch2/tempconv/tempconv.go]

  • 변환 함수들은 conv.go에 넣습니다.
package tempconv

// CToF는 섭씨온도를 화씨온도로 변환합니다.
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }

// FToC는 화씨온도를 섭씨온도로 변환합니다.
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }

예제코드 [ch2/tempconv/conv.go]

  • 각 파일은 패키지명을 정의하는 package 선언으로 시작합니다.
  • 패키지가 임포트되면 패키지 멤버는 tempconv.CToF 등으로 참조됩니다.
  • 패키지 수준의 이름은 모든 소스가 하나의 파일에 있을 때와 마찬가지로 패키지 내의 다른 모든 파일에서 볼 수 있습니다.
  • 패키지 수준의 const 이름들이 대문자로 시작하므로 이들도 tempconv.AbsoulteZeroC와 같은 적합한 이름으로 접근할 수 있습니다.
    fmt.Printf("Brrrr! %v\n", tempconv.AbsoulteZeroC) // "Brrrr! -273.15ºC"

2.6.1 임포트

  • Go 프로그램 안의 모든 패키지는 임포트 경로라는 고유한 문자열로 식별됩니다.
  • Go 도구를 사용 할 때 임포트 경로는 패키지를 구성하는 하나 이상의 Go 소스 파일을 포함한 디렉토리를 의미합니다.

사용자 패키지 임포트 사용 예제

//Cf는 숫자 인수를 섭씨와 화씨로 변환합니다.
package main

import (
    "fmt"
    "os"
    "strconv"
    "tgpl/ch2/tempconv"
)

func main() {
    for _, arg := range os.Args[1:] {
        t, err := strconv.ParseFloat(arg, 64)
        if err != nil {
            fmt.Fprintf(os.Stderr, "cf: %v\n", err)
            os.Exit(1)
        }
        f := tempconv.Fahrenheit(t)
        c := tempconv.Celsius(t)
        fmt.Printf("%s = %s, %s = %s\n", f, tempconv.FToC(f), c, tempconv.CToF(c))
    }
}

예제코드 [tgpl/ch2/cf/main.go]

실행결과
$ go build tgpl/ch2/cf
$ ./cf 32
32ºF = 0ºC, 32ºC = 89.6ºF
$ ./cf 212
212ºF = 100ºC, 212ºC = 413.6ºF
$ ./cf -40
-40ºF = -40ºC, -40ºC = -40ºF

2.6.2 패키지 초기화

  • 패키지 초기화는 시작 시 패키지 수준 변수를 선언된 순서대로 초기화화며, 의존성이 있을 때는 의존하는 변수부터 초기화합니다.
var a = b + c // a가 3으로 세 번째로 초기화 됨
var b = f() // b는 f를 호출함으로써 두 번째로 초기화 됨
var c = 1 // c가 먼저 1로 초기화 됨
func f() int { return c + 1 }
  • init 함수를 사용하면 초기화를 더 간단하게 할 수 있습니다.
    func init() { /* ... */ }
  • init 함수는 호출하거나 참조할 수 없다는 것 외에는 일반 함수와 같습니다.
  • 프로그램이 시작할 때는 각 파일 내의 init 함수들이 선언된 순서대로 자동으로 실행됩니다.
  • 한 패키지는 프로그램 안에서 의존하는 패키지들이 초기화된 후 임포트 순서에 따라 초기화 되며, 따라서 q를 임포트하는 p 패키지에서는 p의 초기화 과정이 시작되기 전에 q의 초기화가 완료된다는 것을 확신할 수 있습니다.

예제 : PopCount

  • uint64 안의 값이 1로 지정된 비트 개수를 반환하며, 이 값을 인구수라고 합니다.
  • 8비트 값을 미리 계산해 PopCount 함수에서 64번의 조회 대신 8개 테이블만 조회해 그 합을 반환하게 합니다.
package popcount

// pc[i]은 i의 인구수입니다.
var pc [256]byte

func init() {
  for i := range pc {
    pc[i] = pc[i/2] + byte(i&1)
  }
}

// PopCount는 x의 인구수(1로 지정된 비트 수)를 반환합니다.
func PopCount(x uint64) int {
  return int(pc[byte(x>>(0*8))] +
      pc[byte(x>>(1*8))] +
      pc[byte(x>>(2*8))] +
      pc[byte(x>>(3*8))] +
      pc[byte(x>>(4*8))] +
      pc[byte(x>>(5*8))] +
      pc[byte(x>>(6*8))] +
      pc[byte(x>>(7*8))])
}

예제코드 [tgpl/ch2/popcount/popcount.go]

package main

import (
    "fmt"
    "os"
    "strconv"
    "tgpl/ch2/popcount"
)

func main() {
    for _, arg := range os.Args[1:] {
        pc, err := strconv.ParseUint(arg, 10, 10)
        if err != nil {
            fmt.Fprintf(os.Stderr, "popcount: %v\n", err)
            os.Exit(1)
        }
        fmt.Println(popcount.PopCount(pc))
    }
}

예제코드 [tgpl/ch2/popcount_main.go]

  • 256을 2진수로 표현하면 11111111 이고, 이것을 8bit 단위로 잘라서 카운팅할 수 있는 테이블을 init 함수에서 미리 만들어 놓았습니다. 그래서 빠르게(?) 1의 개수를 구할 수 있습니다.

실행결과
$ go run ch2/popcount_main.go 255
8


이어보기

[The Go Programming Language] 2장 프로그램 구조 - 2.7 범위

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!