Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.

네이버 오픈소스 세미나에서 발표한 "오픈소스 생태계 일원으로서의 개발자" 발표자료

지난 23일에 대학생 및 초급 개발자를 대상으로 한 2회 네이버 오픈소스 세미나에서 "오픈소스 생태계 일원으로서의 개발자"라는 제목으로 발표했다.

1월 초에 발표 요청을 받았다. 1회 때 발표도 영상으로 좀 보긴 했고 이전에 오픈소스 관련 세미나에도 참석해 본 경험이 있는데 기술적인 내용은 아니다 보니 오픈 소스에 참여하는 방법은 대부분 비슷하다고 생각하고 있었다. 결국은 메인테이너까지 가려면 결국은 해당 프로젝트의 기술적인 부분을 해결해야 할 수 있는 거고 그 외의 내용은 README 읽고 오타 수정하고 이슈 보고에 참여하는 등 처음 오픈소스를 모르는 사람들이 참여하는 방법에 대한 팁은 이미 글이나 발표가 많이 있다고 생각해서 처음에는 발표에 좀 회의적이었다. 최근에 괜찮은 프로젝트를 한 게 있으면 그 경험을 얘기하면 좋은데 그런 것도 아니라서 같은 내용을 또 할 필요가 있나 하는 생각이었다.

그러다가 최근 오픈소스에 관해 생각하는 걸 얘기해 보고 싶어서 "오픈소스 생태계 일원으로서의 개발자"라는 제목으로 참여하게 되었다.(난 역시 제목을 잘 못 짓는다. ㅠ)

제목에서 내가 하고 싶은 얘기를 잘 담은 지는 모르겠지만 한참 동안 오픈소스를 너무 좋아하다가(지금도 좋아하지만...) 최근에 내가 오픈소스는 좀 척박하게 보고 있다. "보고 있는 눈이 아주 많으면 찾지 못할 버그는 없다"는 얘기처럼 수많은 사람이 참여해서 만들어 내는 오픈소스 소프트웨어는 멋지지만 좀 더 가까이서 보면 몇 개 프로젝트 외에는 그렇게 많은 사람이 보고 있지도 않고 좋은 프로젝트인데도 메인테이너들이 지쳐서 프로젝트에서 물러나는 경우도 많이 보았다. 그래서 요즘 전과는 다른 자세로 오픈소스 프로젝트들을 보고 더 깊게 들어가 보려고 생각하고 있어서 그런 얘기를 하고 싶었다. 그렇다 보니 오픈소스를 하는 방법이라기보다는 오픈소스를 대하는 마음가짐에 더 가깝다고 할 수 있다.

사실 발표를 준비할 때는 좀 힘들었다. 보통은 기술이나 경험을 공유하는 접근을 더 선호하고 편해하는 편인데 이 발표에는 내가 말하고자 하는 메시지가 있고 그에 대한 근거와 이야기를 만들어 가야 했다. 하나로 이어지지 않은 여러 생각이 파편화되어 있어서 하나의 발표로 얘기하려니 여러 가지로 조합을 해도 흐름이 자연스럽지 않아서 준비하는 내내 좀 고생도 하고 시간이 오래 걸렸다.


원래 이 글에 내가 하고 싶은 얘기를 적으려다가 그냥 발표자료에 자막(?)을 넣어서 올렸다. 다행히 40분 발표자료도 딱 맞춰서 끝냈고 이어진 40분의 질문/답변 시간까지 끝내고 나니 꽤 피곤했지만, 사람들이 어떤 걸 궁금해하는지도 들을 수 있고 재미있는 시간이었다.

2018/02/27 17:36 2018/02/27 17:36

Go에서 dep으로 의존성 관리하기

Go로 간단한 프로젝트를 하려고 보니 처음 신경 쓰이는 건 의존성 관리를 어떻게 하는가였다. PackageManagementTools 문서에 패키지 관리 도구가 정리되어 있다.

dep, is an official experiment for the dependency tool. It is currently being implemented, in pre-alpha state and should be used with caution as "Lots of functionality is knowingly missing or broken".

여러 도구가 있지만 dep을 공식 의존성 관리 도구로 언급하고 있다. 아직 실험적으로 구현 중이지만 공식이라고 하니 그냥 dep을 쓰기로 했다. dep 저장소를 보면 프로덕션에서 사용해도 문제없다고 하니(dep is safe for production use.)아직 Go로 뭔가 큰 걸 만들 건 아니지만 써도 괜찮을 것 같다.

dep 설치

dpe은 go 1.8 이상의 버전이 필요하다고 한다. 현재 최신 버전이 1.10이니 나한테는 영향은 없을 것 같다. go get으로 설치해도 되지만(go get -u github.com/golang/dep/cmd/dep) 소스 파일까지는 필요 없을 것 같아서 저장소에 안내된 대로 Homebrew로 설치했다.

$ brew install dep

==> Installing dependencies for dep: go
==> Installing dep dependency: go
==> Downloading https://homebrew.bintray.com/bottles/go-1.10.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring go-1.10.high_sierra.bottle.tar.gz
==> Caveats
A valid GOPATH is required to use the `go get` command.
If $GOPATH is not specified, $HOME/go will be used by default:
  https://golang.org/doc/code.html#GOPATH

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin
==> Summary
  /usr/local/Cellar/go/1.10: 8,150 files, 336.9MB
==> Installing dep
==> Downloading https://homebrew.bintray.com/bottles/dep-0.4.1_1.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring dep-0.4.1_1.high_sierra.bottle.tar.gz
  /usr/local/Cellar/dep/0.4.1_1: 7 files, 8.8MB

dep으로 버전을 출력해 볼 수 있으면 잘 설치된 것이다.

$ dep version
dep:
 version     : v0.4.1
 build date  : 2018-01-27
 git hash    : 37d9ea0
 go version  : go1.9.3
 go compiler : gc
 platform    : darwin/amd64

새 버전으로 업그레이드하려면 brew upgrade dep 명령어를 사용하면 된다.

dep으로 의존성 관리하기

go get이 이미 있고 goimports 등을 사용하려고 tools를 설치할 때는 go get으로 설치했다. go get으로 설치하면 $GOPATH/src 아래 소스코드가 들어가는데 이 아래 있으면 로컬에서 import로 불러서 사용할 수 있으므로 go get으로 의존성을 설치해서 사용할 수도 있다.(Go 개발자들이 정확히 어떻게 쓰는지는 아직 잘 모르지만...)

하지만 소스 코드에 import 구문이 있다고 하더라도 go get으로 설치해서 사용하면 본인은 괜찮지만 다른 사람은 의존성을 일일이 찾아서 설치해야 해서 협업하기만 어려우므로 프로젝트의 의존성은 dep같은 도구가 적합하다고 생각한다.

새 프로젝트에서 dep으로 의존성을 관리하는 방법은 dep 문서에 잘 나와 잇다. 처음에 dep으로 의존성을 관리하려면 초기화를 해야 한다.

$ dep init

초기화를 하면 아래처럼 2개의 파일과 1개의 디렉터리가 생성된다.

├── Gopkg.lock
├── Gopkg.toml
└── vendor/


Gopkg.toml

Gopkg.toml은 초기화할 때 생성되는 파일로 dep의 동작을 감시하는 여러 규칙을 담고 있다고 한다.

  • 의존성 규칙: constraints, overrides로 사용자가 적용할 의존성의 버전과 어디서 가져와야 하는지를 지정한다.
  • 패키지 그래프 규칙: required, ignored로 임포트 경로를 추가하거나 제외해서 임포트 그래프를 조작할 수 있다.
  • metadata: 사용자가 정의한 키-값의 맵으로 dep은 무시할 것이다.
  • prune: 어떤 파일과 디렉터리가 불필요하다고 판단할지 설정해서 vendor/에서 자동으로 제거한다.

아래는 dep init했을 때 생긴 Gopkg.toml의 내용이다.

# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#   name = "github.com/x/y"
#   version = "2.4.0"
#
# [prune]
#   non-go = false
#   go-tests = true
#   unused-packages = true


[prune]
  go-tests = true
  unused-packages = true


Gopkg.lock

프로젝트의 의존성 그래프의 스냅숏을 가진 파일이다. 이 파일은 dep ensure 할 때 변경된다.

아래는 생성된 Gopkg.lock 파일의 내용이다.

# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.


[solve-meta]
  analyzer-name = "dep"
  analyzer-version = 1
  inputs-digest = "ab4fef131ee828e96ba67d31a7d690bd5f2f42040c6766b1b12fe856f87e0ff7"
  solver-name = "gps-cdcl"
  solver-version = 1

의존성 추가

웹 프레임워크로 gin을 사용하기 위해 dep ensure로 의존성을 추가해 보자. ensure가 프로젝트에 의존성을 설치하는 명령어고 새로운 의존성을 추가하려면 dep ensure -add를 사용해야 한다.

$ dep ensure -add github.com/gin-gonic/gin
no dirs contained any Go code

현재는 Go 코드를 전혀 작성하지 않아서 오류가 난다. 여기선 Go 코드는 중요하지 않으므로 다음 내용으로 main.go 파일을 만들자.

package main

다시 gin을 추가해 보자.

$ dep ensure -add github.com/gin-gonic/gin
Fetching sources...

"github.com/gin-gonic/gin" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.

아까와 달리 추가되었다. 안내 문구를 보면 아직 github.com/gin-gonic/gin를 아직 임포트 한 곳이 없어서 Gopkg.lockvender/에는 추가되었지만 임포트하지 않고 dep ensure를 실행하면 지워질 것이라고 한다.

Gopkg.toml 파일을 보면 하단에 gin이 추가된 것을 볼 수 있다.

[[constraint]]
  name = "github.com/gin-gonic/gin"
  version = "1.2.0"

Gopkg.lock 파일은 아래처럼 의존성의 자세한 정보가 추가되고 [solve-meta]inputs-digest 정보가 변경되게 된다.

[[projects]]
  name = "github.com/gin-gonic/gin"
  packages = [
    ".",
    "binding",
    "render"
  ]
  revision = "d459835d2b077e44f7c9b453505ee29881d5d12d"
  version = "v1.2"


dep 관련 파일 버전 관리

위에서 보았듯이 dep이 생성한 파일은 Gopkg.toml, Gopkg.lockvender 디렉터리다. 좀 찾아봤는데 Gopkg.toml, Gopkg.lock는 둘 다 커밋하는 것 같다. Gopkg.lock가 꼭 필요한지는 확인 안해 봤지만 npm을 생각해 보면 package-lock.json과 같은 역할을 하는 거로 보인다. 내 취향으로는 프로덕션에서 서비스하기 전까지는 lock 파일을 저장소에서 관리하는 편은 아니지만 요즘 추세는 lock 파일도 보통 넣는 것 같고 해서 dep에서도 두 파일을 모두 Git에 넣기로 했다.

vender 디렉터리의 경우 딱 봐도 Git에서 관리할 필요가 없어 보이는데 dep의 FAQ 문서를 보면 이 디렉터리를 커밋해서 관리할 때의 장단점이 나와 있다. 말이 장단점이지 그냥 의존성 파일을 다 저장소에 관리할지 말지의 얘기라서 관례대로 나는 vender 디렉터리는 git에서 제외했다.

의존성 설치

프로젝트를 협업하는 경우 Gopkg.toml, Gopkg.lock를 가진 저장소를 받아서 의존성을 설치해서 개발해야 한다.

$ dep ensure

이때는 dep ensure만 실행해야 한다. 참고로 여기서는 dep만 설명하고 있지만, 앞에서 살펴본 대로 go 소스코드에서 import "github.com/gin-gonic/gin" 처럼 임포트하고 있지 않다면 이 의존성이 지워지게 된다.

2018/02/21 22:33 2018/02/21 22:33