Go 언어의 의존성 관리

Go는 의존성 관리에 대해 특이한 접근 방식을 취합니다. 이는 아티팩트 기반이 아닌 소스 기반입니다. 아티팩트 기반의 의존성 관리 시스템에서는 패키지가 소스 코드에서 생성된 아티팩트로 구성되어 있으며, 소스 코드와 별도의 리포지터리 시스템에 저장됩니다. 예를 들어, 많은 NodeJS 패키지는 패키지 리포지터리로 npmjs.org을 사용하고 소스 리포지터리로 github.com을 사용합니다. 반면, Go의 패키지는 소스 코드 그 자체이며, 패키지를 릴리스할 때에는 아티팩트 생성이나 별도의 리포지터리가 관련되지 않습니다. Go 패키지는 VCS 서버의 버전 관리 리포지터리에 저장되어야 합니다. 의존성은 직접 해당 VCS 서버에서 가져오거나, 해당 VCS 서버에서 가져오는 중개 프록시를 통해 가져와야 합니다.

버전 관리

Go 1.11에서 모듈과 Go 생태계에 대한 일등급 패키지 버전 관리를 도입했습니다. 이전에는 Go에는 버전 관리를 위한 명확한 메커니즘이 없었습니다. 3rd party 버전 관리 도구는 존재했지만, 기본 Go 경험에는 버전 관리를 지원하는 것이 없었습니다.

Go 모듈은 semantic versioning을 사용합니다. 모듈의 버전은 유효한 시맨틱 버전인 VCS(버전 관리 시스템) 태그로 정의됩니다. 예를 들어, gitlab.com/my/project1.0.0 버전을 릴리스하려면 개발자는 v1.0.0인 Git 태그를 생성해야 합니다.

0과 1 이외의 주 버전의 경우, 모듈 이름에는 반드시 /vX가 접미사로 붙어야 합니다. 예를 들어, gitlab.com/my/projectv2.0.0 버전은 gitlab.com/my/project/v2로 이름을 지어야 합니다.

Go는 ‘의사 버전’을 사용하는데, 이는 특정 VCS 커밋을 참조하는 특별한 시맨틱 버전입니다. 시맨틱 버전의 사전 릴리스 구성요소는 타임스탬프로 시작하거나 끝나야 하며, 커밋 식별자의 처음 12자여야 합니다.

  • X에 대해 이전 태그된 커밋이 존재하지 않을 때: vX.0.0-yyyymmddhhmmss-abcdefabcdef
  • 가장 최근의 이전 태그가 vX.Y.Z-pre인 경우: vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef
  • 가장 최근의 이전 태그가 vX.Y.Z인 경우: vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef

만약 VCS 태그가 이러한 패턴 중 하나와 일치한다면, 무시됩니다.

Go 모듈 및 버전 관리에 대한 완전한 이해를 위해서는 공식 Go 웹사이트의 이 시리즈의 블로그 글을 참조하십시오.

‘모듈’ vs ‘패키지’

  • 패키지는 *.go 파일을 포함하는 폴더입니다.
  • 모듈은 go.mod 파일을 포함하는 폴더입니다.
  • 모듈은 보통 패키지이기도 하며, 즉 go.mod 파일과 *.go 파일을 포함하는 폴더입니다.
  • 모듈에는 하위 디렉터리가 있을 수 있으며, 이는 패키지가 될 수 있습니다.
  • 모듈은 일반적으로 VCS 리포지터리의 형태로 제공됩니다 (Git, SVN, Hg 등).
  • 해당 모듈의 하위 디렉터리 중 모듈 자체인 것은 별도의 모듈로 간주되어 해당 모듈에 포함되지 않습니다.
    • 모듈 repo가 제공될 때, 만약 repo/subgo.mod 파일이 존재한다면, repo/sub 및 해당 모듈에 포함된 모든 파일은 별도의 모듈로 간주되어 repo의 일부가 아닙니다.

명명

표준 라이브러리를 제외한 모듈 또는 패키지의 이름은 일반적으로 (sub.)*domain.tld(/path)* 형식이어야 합니다. 이는 URL과 유사하지만 URL은 아닙니다. 패키지 이름에는 스키마(예: https://://)가 없고 포트 번호가 포함될 수 없습니다. example.com:8443/my/package는 유효한 이름이 아닙니다.

패키지 가져오기

Go 1.12 이전에 패키지를 가져오는 과정은 다음과 같았습니다:

  1. https://{package name}?go-get=1을 조회합니다.
  2. 응답에서 go-import 메타 태그를 찾습니다.
  3. 메타 태그에서 나타난 리포지터리를 지정된 VCS를 사용하여 가져옵니다.

메타 태그는 <meta name="go-import" content="{prefix} {vcs} {url}">의 형식을 가져야 합니다. 예를 들어, gitlab.com/my/project git https://gitlab.com/my/project.gitgitlab.com/my/project로 시작하는 패키지는 Git을 사용하여 https://gitlab.com/my/project.git에서 가져와야 함을 나타냅니다.

모듈 가져오기

Go 1.12에서는 체크섬 데이터베이스와 모듈 프록시가 도입되었습니다.

체크섬

go.mod 외에도 모듈은 go.sum 파일을 가지고 있습니다. 이 파일은 모듈이나 모듈의 하위 의존성 중 하나에 의해 참조되는 모든 버전의 코드 및 go.mod 파일의 SHA-256 체크섬을 기록합니다. 새로운 의존성이 참조될 때마다 Go는 go.sum을 지속적으로 업데이트합니다.

Go가 모듈의 의존성을 가져올 때, 만약 이러한 의존성이 이미 go.sum에 기록되어 있다면, Go는 이러한 의존성의 체크섬을 확인합니다. 체크섬이 go.sum에 기록된 값과 일치하지 않는 경우, 빌드가 실패합니다. 이는 모듈의 특정 버전이 개발자나 악의적인 당사자에 의해 변경될 수 없도록 보장합니다.

Go 1.12+는 체크섬 데이터베이스 사용을 구성할 수 있습니다. 구성된 경우, Go가 의존성을 가져오고 go.sum에 해당하는 항목이 없는 경우, Go는 다운로드된 의존성의 체크섬을 계산하지 않고 대신 구성된 체크섬 데이터베이스에서 해당 의존성의 체크섬을 조회합니다. 의존성을 체크섬 데이터베이스에서 찾을 수 없는 경우, 빌드가 실패합니다. 다운로드된 의존성의 체크섬이 체크섬 데이터베이스의 결과와 일치하지 않는 경우, 빌드가 실패합니다. 이는 다음 환경 변수들로 제어됩니다:

  • GOSUMDB는 체크섬 데이터베이스의 이름 및 선택적으로 공개 키와 서버 URL을 식별합니다.
    • off로 설정하면 체크섬 데이터베이스 조회가 완전히 비활성화됩니다.
    • Go 1.13 이상에서는 GOSUMDB가 정의되지 않은 경우 sum.golang.org가 사용됩니다.
  • GONOSUMDB는 체크섬 데이터베이스 조회가 비활성화되어야 하는 모듈 접미사의 쉼표로 구분된 디렉터리입니다. 와일드카드가 지원됩니다.
  • GOPRIVATE는 다른 기능을 비활성화하는 데에 추가로 사용되는 모듈 이름의 쉼표로 구분된 디렉터리입니다.

프록시

Go 1.12+에서는 모듈을 모듈의 VCS에서 직접 가져오는 대신 Go 프록시에서 모듈을 가져오도록 구성할 수 있습니다. 그렇게 구성된 경우 Go가 의존성을 가져올 때 설정된 프록시에서 의존성을 순서대로 가져오려고 시도합니다. 다음 환경 변수가 이를 제어합니다.

  • GOPROXY는 조회할 모듈 프록시의 쉼표로 구분된 디렉터리입니다.
    • direct의 값은 모듈 프록시 조회를 완전히 비활성화합니다.
    • 디렉터리의 마지막 항목이 direct인 경우, 프록시가 의존성을 제공할 수 없는 경우, Go는 위에서 설명한 프로세스로 되돌아갑니다.
    • Go 1.13+에서는 GOPROXY가 정의되지 않은 경우 proxy.golang.org,direct를 사용합니다.
  • GONOPROXY는 프록시에서 가져오지 말아야 하는 모듈 접미사의 쉼표로 구분된 디렉터리입니다. 와일드카드가 지원됩니다.

  • GOPRIVATEGONOPROXY와 동일한 기능을 하는 모듈 이름의 쉼표로 구분된 디렉터리입니다. 기타 기능 비활성화도 포함됩니다.

가져오기

Go 1.12부터 모듈이나 패키지를 가져오는 프로세스는 다음과 같습니다.

  1. GOPROXY가 프록시 디렉터리이고 모듈이 GONOPROXYGOPRIVATE에 의해 제외되지 않은 경우, 처음 유효한 응답에서 중지할 때까지 순서대로 조회합니다.
  2. GOPROXYdirect이거나 모듈이 제외된 경우, 또는 GOPROXY,direct로 끝나고 프록시가 모듈을 제공하지 않은 경우, 되돌아갑니다.
    1. https://{module 또는 package 이름}?go-get=1을 조회합니다.
    2. go-import 메타 태그를 응답에서 찾습니다.
    3. 메타 태그에서 지정된 VCS를 사용하여 나타낸 리포지터리를 가져옵니다.
    4. {vcs} 필드가 mod인 경우, URL은 VCS 대신 모듈 프록시로 처리해야 합니다.
  3. 모듈이 직접 가져오고 의존성이 아닌 경우에 중지합니다.
  4. go.sum에 해당 모듈에 대한 항목이 포함되어 있는 경우 체크섬을 유효화하고 중지합니다.
  5. GOSUMDB가 체크섬 데이터베이스를 식별하고 모듈이 GONOSUMDBGOPRIVATE에 의해 제외되지 않은 경우, 모듈의 체크섬을 검색하고 go.sum에 추가하며 다운로드된 소스를 이를 통해 유효화합니다.
  6. GOSUMDBoff인 경우나 모듈이 제외된 경우, 다운로드된 소스에서 체크섬을 계산하고 go.sum에 추가합니다.

다운로드된 소스에는 go.mod 파일이 포함되어 있어야 합니다. go.mod 파일에는 모듈의 이름을 지정하는 module 지시어가 포함되어야 합니다. go.mod에서 지정된 모듈 이름이 모듈을 가져오는 데 사용된 이름과 일치하지 않으면 모듈은 컴파일에 실패합니다.

모듈이 직접 가져오고 버전이 지정되지 않았거나 의존성으로 추가되고 버전이 지정되지 않은 경우, Go는 모듈의 가장 최근 버전을 사용합니다. 모듈이 프록시에서 가져온 경우, Go는 프록시에 버전 디렉터리을 요청하고 가장 최신 버전을 선택합니다. 모듈이 직접 가져온 경우, Go는 리포지터리에서 태그 디렉터리을 요청하고 유효한 의미있는 버전을 선택합니다.

인증

Go 1.13 이전의 버전에서는 Go에서 만든 요청을 인증하는 지원이 다소 일관성이 없었습니다. Go 1.13에서는 .netrc 인증을 지원하는 데 향상되었습니다. HTTPS를 통해 요청이 이루어지고 해당 요청에 맞는 .netrc 항목을 찾을 수 있는 경우, Go는 HTTP 기본 인증 자격 증명을 요청에 추가합니다. Go는 HTTP를 통해 만든 요청을 인증하지 않습니다. GOPROXY에 포함된 HTTP만 지원하는 항목에는 포함된 자격 증명이 거부됩니다.

향후 버전에서 Go는 임의의 인증 헤더를 지원할 수 있습니다. 자세한 내용은 golang/go#26232를 참조하세요.