direnv로 디렉토리(프로젝트) 별 개발환경 구축하기
루비(Ruby), 파이썬(Python), 노드(Node) 개발 환경 구축
들어가며
컴퓨터를 사용하는 환경은 크게 그래피컬 사용자 인터페이스GUI, Graphical User Interface와 커맨드라인 인터페이스CLI, Command Line Interface로 나눠집니다. 프로그래머나 시스템 관리자는 GUI 못지 않게 CLI 환경을 자주 사용합니다. 셸은 커맨드라인에서 대화형으로 명령어를 실행할 수 있도록 도와주는 프로그램으로 CLI와 동의어라고 해도 좋을 정도의 지위를 가지고 있습니다.
일반적으로 셸을 사용하는 경우 사용자 별로 환경이 구축됩니다. 배시 셸을 사용하는 경우 로그인하는 시점에 ~/.bashrc
, ~/.bash_profile
, ~/.profile
과 같은 파일들을 읽어서 사용자의 환경을 구축합니다. 셸을 가끔씩만 사용하는 경우에는 이러한 설정 파일들을 통해서 원하는 환경을 구축하는 데 어려움이 없습니다. 하지만 셸을 사용해 시스템 관리를 비롯해 다수의 프로젝트들을 관리하는 경우 이 설정 파일들이 복잡해지는 문제가 있습니다. 또한 이렇게 작성한 대부분의 설정은 특정한 디렉터리나 프로젝트에서만 의미가 있습니다.
direnv는 바로 이러한 문제를 해결하기 위한 도구입니다. direnv는 사용자가 현재 위치한 디렉터리의 .envrc
파일을 추가로 읽어들여 해당 디렉터리에서만 필요한 설정을 로드합니다. 또한 디렉터리에서 빠져나갈 때는 (가능하다면) 이 설정들을 언로드합니다. 따라서 ~/.profile
과 같은 파일에 모든 설정을 몰아넣는 대신 디렉터리 별로 필요한 설정을 정의하는 것이 가능해집니다.
이 글에서는 direnv의 사용법과 프로그래밍 언어와 조합해서 사용하는 방법에 대해서 소개합니다.
direnv 시작하기
direnv를 사용하려면 먼저 설치를 하고 셸에 따라서 적절히 설정을 해야합니다.
설치 및 설정
맥OSmacOS를 사용하는 경우 홈브류를 사용해 쉽게 설치할 수 있습니다.
$ brew install direnv
direnv는 주요 리눅스 배포판의 패키지로도 등록되어있습니다. (다른 패키지들에 대해서는 공식 저장소의 정보를 참고해주세요)
## Ubuntu, Debian
$ apt-get install direnv
## Fedora
$ dnf install direnv
패키지 매니저를 사용하기 어렵다면 릴리즈 페이지에서 바이너리를 다운로드 받아서 사용하거나, 직접 direnv 프로젝트를 컴파일해서 사용하는 것도 가능합니다.
version
명령어로 성공적으로 설치되었는지 확인할 수 있습니다.
$ direnv version
2.14.0
다음으로 자신이 사용하는 셸에 해당하는 내용을 셸의 설정 파일에 추가해줍니다. direnv는 공식적으로 BASH, ZSH, FISH, TCSH, Elvish를 지원합니다.
## BASH -> ~/.bashrc에 아래 내용 추가
eval "$(direnv hook bash)"
## ZSH -> ~/.zshrc에 아래 내용 추가
eval "$(direnv hook zsh)"
## FISH -> ~/.config/fish/config.fish에 아래 내용 추가
eval (direnv hook fish)
## TCSH -> ~/.cshrc에 아래 내용 추가
eval `direnv hook tcsh`
## Elvish -> 아래 내용 실행
$ direnv hook elvish > ~/.elvish/lib/direnv.elv
## ~/.elvish/rc.elv에 아래 내용 추가
use direnv
설정 추가 후 셸을 재실행하면 direnv를 사용할 수 있습니다.
추가적으로 셸 설정 파일에 EDITOR
환경변수를 정의해두면 편리합니다. direnv edit .
명령어는 이 환경변수에 지정된 에디터를 실행합니다.
export EDITOR=emacs
direnv의 기본적인 사용법
곧바로 direnv를 사용해보겠습니다. direnv는 셸 전체에 적용되는 설정 이외에 현재 디렉터리의 특정한 설정을 추가할 수 있도록 도와주는 도구입니다. 따라서 direnv를 사용할 디렉터리를 하나 준비합니다.
$ mkdir direnv
$ cd direnv
direnv를 통해 특정 디렉터리에 적용되는 설정은 .envrc
파일에 셸스크립트로 작성합니다. 이 파일을 임시로 생성합니다.
$ touch .envrc
.envrc
파일이 만들어지면 direnv가 이를 알아채고 경고 메시지를 출력합니다.
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
direnv는 특정 디렉터리의 특정 컨텐츠를 가진 .envrc
파일에 대해서 명시적으로 사용을 허가하지 않은 경우 이 파일을 읽어들이지 않습니다. 이는 의도치않게 원하지 않는 셸 스크립트가 실행되는 것을 방지하기 위해서입니다. 따라서 .envrc
파일을 추가하거나 변경하는 경우 명시적으로 사용을 허가해야만 이 파일을 읽어들입니다.
allow
명령어로 현재 디렉터리의 .envrc
파일의 사용을 허가할 수 있습니다.
$ direnv allow
direnv: loading .envrc
.envrc
가 바로 로드되는 것을 알 수 있습니다. 하지만 이 파일에는 아무 내용도 없습니다. 다음 내용을 .envrc
에 추가해봅니다.
$ echo 'echo "Hello, direnv"' > .envrc
다시 허가 되지 않은 파일이라고 경고 메시지가 출력됩니다.
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
변경된 내용을 허가해줍니다.
$ direnv allow
direnv: loading .envrc
Hello, direnv
.envrc
에 추가한 내용이 정상적으로 실행되는 것을 확인할 수 있습니다.
상위 디렉터리로 이동해봅니다.
$ cd ..
direnv: unloading
.envrc
의 내용이 언로딩 되었습니다. 현재 .envrc
에는 별 내용이 없기 때문에 특별한 의미는 없습니다. 다시 direnv 디렉터리로 이동해서 .envrc
의 내용이 적용되는 것을 확인해보겠습니다.
$ cd direnv
direnv: loading .envrc
Hello, direnv
direnv 디렉터리로 이동하니 다시 .envrc
에 작성한 내용이 실행되는 것을 확인할 수 있습니다.
deny
명령어를 사용하면 허가한 내용을 명시적으로 금지하는 것도 가능합니다.
$ direnv deny
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
direnv에서 allow
한 정보는 ~/.config/direnv/allow/
에서 관리됩니다. 이 디렉터리의 파일은 .envrc
의 경로와 내용을 조합으로 sha256sum
으로 작성됩니다.
.envrc에 환경변수 추가하기
셸에서는 환경변수를 통해서 작업 환경이나 애플리케이션 실핼 시에 사용하는 설정을 전달하는 방식을 자주 사용합니다. direnv의 가장 기본적인 활용법은 디렉터리(프로젝트) 별로 환경변수를 정의하는 일입니다.
.envrc
파일에 환경변수를 정의하는 내용을 추가해줍니다.
$ echo 'export HELLO=world' > .envrc
allow
명령어로 .envrc
를 허가해줍니다.
$ direnv allow
direnv: loading .envrc
direnv: export +HELLO
direnv는 .envrc
를 읽어들이고 추가한 환경변수 목록을 보여줍니다. printenv
는 현재 환경에 정의된 환경변수를 확인하는 명령어입니다. printenv
와 grep
으로 새로 추가한 HELLO
환경변수를 찾아봅니다.
$ printenv | grep HELLO
HELLO=world
정상적으로 환경변수가 추가된 것을 확인할 수 있습니다. 상위 디렉터리로 이동해보겠습니다.
$ cd ..
direnv: unloading
$ printenv | grep HELLO
<출력 결과 없음>
.envrc
파일이 언로딩 되고, 더 이상 HELLO
환경변수는 찾을 수 없습니다. 다시 direnv
디렉터리로 이동해서 환경변수를 확인해봅니다.
$ cd direnv
direnv: loading .envrc
direnv: export +HELLO
$ printenv | grep HELLO
HELLO=world
다시 환경변수가 추가된 것을 확인할 수 있습니다.
direnv에서는 특정한 디렉터리로 이동하지 않고도 해당 디렉터리의 환경을 사용할 수 있는 exec <DIR> <COMMAND>
명령어를 제공하고 있습니다.
$ direnv exec ./direnv printenv | grep HELLO
direnv: loading direnv/.envrc
HELLO=world
direnv
디렉터리로 이동하지 않았지만 HELLO
환경변수가 정의되어있는 것을 확인할 수 있습니다.
디렉터리 별로 환경변수를 추가하는 기능은 단순하지만 매우 강력합니다.
예를 들어서 2개의 프로젝트를 동시에 작업하고 있다고 가정해보겠습니다. 두 프로젝트는 아마존 웹 서비스Amazon Web Service를 사용하는데, 서로 다른 AWS 계정의 다른 리전과 연결되어있습니다. 프로젝트 A는 X 계정의 서울 리전을 사용합니다. 프로젝트 B는 Y 계정의 도쿄 리전을 사용합니다. AWS 커맨드라인 인터페이스AWS CLI는 프로필로 다중 계정을 지원하고 있습니다. 하지만 이를 사용하는 경우 모든 명령어에 어떤 프로필을 사용하는지를 --profile
옵션으로 지정해야합니다. direnv를 사용하는 경우 AWS CLI의 credentials
파일을 사용하는 대신 각 디렉터리에 환경변수로 인증 정보를 지정하는 전략을 사용할 수 있습니다.
프로젝트 A의 디렉터리에는 다음과 같이 .envrc
파일을 작성합니다.
export AWS_ACCESS_KEY_ID=ACCOUNT_X_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=ACCOUNT_X_SECRET_KEY
export AWS_DEFAULT_REGION=ap-northeast-2
이제 A 디렉터리로 이동해서 AWS CLI를 사용하는 경우 자동적으로 이 인증 정보를 사용합니다.
프로젝트 B의 경우도 마찬가지입니다. 프로젝트 B 디렉터리 아래에 다음과 같이 .envrc
파일을 작성합니다.
export AWS_ACCESS_KEY_ID=ACCOUNT_Y_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=ACCOUNT_Y_SECRET_KEY
export AWS_DEFAULT_REGION=ap-northeast-1
프로젝트 B 디렉터리에서는 이 인증 정보를 사용할 것입니다.
AWS CLI의 프로필이 정의되어있는 경우 직접 인증 정보를 기록 하는 대신 프로필을 환경 변수로 지정하는 것도 가능합니다.
프로젝트 A의 .envrc
를 다음과 같이 작성합니다.
export AWS_PROFILE=ACCOUNT_X
프로젝트 B의 .envrc
를 다음과 같이 작성합니다.
export AWS_PROFILE=ACCOUNT_Y
이처럼 direnv를 사용하면 디렉터리(프로젝트) 별 설정 관리가 훨씬 수월해집니다.
Git을 사용하는 경우 .gitignore에 추가
direnv는 프로젝트 단위로 사용하는 경우가 많습니다. 프로젝트 단위의 환경 설정이나 민감한 데이터가 포함될 수 있으므로 기본적으로 .envrc
파일은 저장소에 포함하지 않을 것을 권장합니다. 깃Git을 사용하는 경우 .envrc
파일을 .gitignore
파일에 추가해줍니다. 또한 direnv
에서 관습적으로 사용하는 .direnv
디렉터리도 추가해줍니다.
.envrc
.direnv
이 내용을 프로젝트 별로 매번 .gitignore
에 추가할 수도 있지만, 프로젝트에 따라서 누락이 될 수도 있기 때문에 시스템 전체에 적용되는 .gitignore
파일 작성을 추천합니다.
$ touch ~/.gitignore_global
$ git config --global core.excludesfile ~/.gitignore_global
$ echo '.envrc' > ~/.gitignore_global
$ echo '.direnv' > ~/.gitignore_global
이제 모든 깃 저장소 디렉터리에에서 기본적으로 .envrc
와 .diernv
가 무시될 것입니다.
direnv 활용: 프로그래밍 언어 버전 관리
앞에서는 direnv의 환경변수를 추가하는 등 기본적인 사용법에 대해서 알아보았습니다. direnv는 환경변수를 추가하는 기능 이외에도 셸 스크립트에 기반하고 있기 때문에 얼마든지 확장해서 사용할 수 있습니다. 이 중에서도 자주 사용되는 기법 중 하나가 프로그래밍 언어의 버전 관리입니다. 다수의 프로젝트를 사용하는 경우 같은 프로그래밍 언어라도 여러 버전을 동시에 사용하게 됩니다. 이러한 문제를 해결하기 위해서 각 언어 별로 rbenv, pyenv, NVM, asdf 등 각 언어의 버전을 관리해주는 도구들이 존재합니다. direnv를 사용하면 디렉터리를 기반으로 좀 더 깔끔하게 프로그래밍 언어의 버전 관리가 가능해집니다. 여기서는 루비Ruby, 파이썬Python, 노드.jsNode.js 환경을 구축하는 방법을 소개합니다.
루비Ruby
루비의 경우 direnv와 ruby-install의 조합으로 특별한 로직없이 간단하게 버전 관리를 할 수 있습니다. 여기서는 이 방법을 소개합니다. direnv에서는 이외에도 rbenv와 RVM과의 조합도 지원하고 있습니다.
ruby-install로 루비 버전 관리
ruby-install은 다양한 버전의 루비 설치를 도와주는 도구로 맥OS와 리눅스를 지원합니다. 맥OS를 사용하는 경우 홈브류를 사용해 설치할 수 있습니다.
$ brew install ruby-install
--version
옵션으로 설치되었는지 확인해봅니다.
$ ruby-install --version
ruby-install: 0.6.1
--latest
로 루비 각 배포판의 최신 버전을 확인할 수 있습니다.
ruby-install --latest
>>> Downloading latest ruby versions ...
>>> Downloading latest jruby versions ...
>>> Downloading latest rbx versions ...
>>> Downloading latest maglev versions ...
>>> Downloading latest mruby versions ...
Stable ruby versions:
ruby:
2.2.10
2.3.7
2.4.4
2.5.1
jruby:
1.7.27
9.2.0.0
rbx:
3.107
maglev:
1.0.0
mruby:
1.4.1
여기서는 ruby 2.5.1을 설치해보겠습니다. ruby-install은 루비 소스 코드를 직접 빌드하기 때문에 설치에 꽤 긴 시간이 걸립니다.
$ ruby-install ruby 2.5.1
>>> Installing ruby 2.5.1 into /Users/username/.rubies/ruby-2.5.1 ...
>>> Installing dependencies for ruby 2.5.1 ...
...
>>> Successfully installed ruby 2.5.1 into /Users/username/.rubies/ruby-2.5.1
설치를 시작하는 메시지에서 유추할 수 있듯이 ~/.rubies/ruby-<VERSION>
에 루비가 설치 됩니다. 루비 2.5.1이 설치되었는지 확인해봅니다. 설치 디렉터리 아래의 bin
디렉터리 아래의 ruby
를 실행합니다.
$ ~/.rubies/ruby-2.5.1/bin/ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
direnv로 루비 프로젝트 버전 관리
루비 프로젝트 디렉터리로 이동해서(없다면 하나 생성해줍니다 😅) .envrc
에 다음 내용을 추가해줍니다.
local ruby_dir=$HOME/.rubies/ruby-2.5.1
load_prefix $ruby_dir
layout ruby
ruby_dir
은 루비가 설치된 디렉터리의 경로입니다. load_prefix
와 layout_ruby
는 direnv의 표준 라이브러리에서 제공하는 함수입니다. load_prefix
함수는 매개변수로 주어진 경로 아래의 디렉터리들을 PATH
, CPATH
, LIBRARY_PATH
등 환경변수에 등록하는 역할을 합니다. layout_ruby
는 루비의 젬(라이브러리)을 프로젝트 단위로 관리할 수 있도록 설정해줍니다.
이 내용을 허가allow하고, $PATH
를 출력해봅니다.
$ echo $PATH
<CURRENT_DIR>/.direnv/bin:<CURRENT_DIR>/.direnv/ruby/bin:<HOME_DIR>/.rubies/ruby-2.5.1/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
기본적으로 사용하는 $PATH
이외에 3개의 디렉터리가 추가된 것을 확인할 수 있습니다.
-
<HOME_DIR>/.rubies/ruby-2.5.1/bin
-
load_prefix
에 의해 추가된 루비의 실행파일 경로입니다.
-
-
<CURRENT_DIR>/.direnv/bin
-
BUNDLE_BIN
디렉터리로 사용 됩니다.
-
-
<CURRENT_DIR>/.direnv/ruby/bin
-
GEM_HOME
디렉터리로 사용 됩니다.
-
처음에는 $PATH
에 설정만 되어있을 뿐 아래의 두 디렉터리는 존재하지 않습니다. 아래의 두 디렉터리들은 젬Gem이나 번들러Bundler를 사용할 때 자동적으로 만들어집니다. 여기서는 Gem을 하나 설치해보겠습니다.
$ gem install kramdown
이 젬은 현재 디렉터리의 .direnv
아래에 설치됩니다. tree
명령어로 구조를 확인해봅니다.
$ tree .direnv -L 3
.direnv
└── ruby
├── bin
│ └── kramdown
├── build_info
├── cache
│ └── kramdown-1.17.0.gem
├── doc
│ └── kramdown-1.17.0
├── extensions
├── gems
│ └── kramdown-1.17.0
└── specifications
└── kramdown-1.17.0.gemspec
특히 주목할만한 점은 .direnv/ruby/bin
아래에 젬의 실행 파일이 설치된다는 점입니다. 앞서 $PATH
에서 이 디렉터리가 추가된 것을 확인할 수 있었습니다. which
로 kramdown
을 찾아 보면 이 디렉터리가 나옵니다.
$ which kramdown
<CURRENT_DIR>/.direnv/ruby/bin/kramdown
$ kramdown --version
1.17.0
direnv와 번들러 설정
루비에서 많이 사용되는 의존성 관리자인 번들러Bundler도 루비 젬으로 배포됩니다. direnv로 디렉터리에 프로젝트 환경을 구축하면 bundle
실행 파일이 존재하지 않습니다. 맥OS의 경우 기본적으로 시스템에 루비가 설치되어있습니다. 이 상태에서 which
로 bundle
을 찾아보면 시스템에 설치된 bundle
의 경로를 반환합니다.
$ which bundle
/usr/local/bin/bundle
번들러는 $GEM_HOME
과 $BUNDLE_BIN
환경변수를 참조하므로 이 bundle을 그대로 사용하더라도 현재 디렉터리의 .direnv
아래에 정상적으로 젬을 설치합니다. 하지만 이는 시스템 환경에 설치된 번들러를 사용하기 때문에 의도치 않은 문제를 발생시킬 수 있습니다.*
따라서 명시적으로 번들러를 설치하는 것을 추천합니다.
$ gem install bundler
Successfully installed bundler-1.16.3
$ which bundle
<CURRENT_DIR>/.direnv/ruby/bin/bundle
bundle
의 경로가 현재 디렉터리 아래로 달라진 것을 확인할 수 있습니다. 이는 현재 $PATH
상 .direnv/ruby/bin
의 우선 순위가 /usr/local/bin
보다 높기 때문입니다.
이번에는 번들러를 사용해서 패키지를 설치해보겠습니다. 다음과 같이 Gemfile
을 작성합니다.
source "https://rubygems.org"
gem "sass"
bundle
을 실행해서 sass
젬을 설치합니다.
$ bundle
Fetching gem metadata from https://rubygems.org/............
...
Fetching sass 3.5.7
Installing sass 3.5.7
Bundle complete! 1 Gemfile dependency, 6 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
$ bundle info sass
* sass (3.5.7)
...
Path: <CURRENT_DIR>/.direnv/ruby/gems/sass-3.5.7
앞서 젬을 사용해 설치한 경우와 마찬가지로 .direnv/ruby/gems/
아래에 젬이 설치되었습니다. 젬으로 설치하는 경우 .direnv/ruby/bin
디렉터리에만 해당 패키지의 실행파일이 추가되지만 번들러로 설치하는 경우 $BUNDLE_BIN
에 해당하는 .direnv/bin
에도 같은 파일이 추가적으로 복사됩니다. $BUNDLE_BIN
은 $GEM_HOME
보다 우선순위가 높습니다.
$ which sass
<CURRENT_DIR>/.direnv/bin/sass
$BUNDLE_BIN
경로가 출력되는 것을 확인할 수 있습니다. direnv와 번들러를 사용하면 또 한가지 장점이 있습니다. 여러 프로젝트 간에 루비 패키지를 공유하는 경우 현재 프로젝트에 고정된 젬의 실행파일을 사용하기 위해 bundle exec
명령어를 사용해야합니다. 하지만 direnv를 사용하는 경우 위와 같이 $BUNDLE_BIN
이 $PATH
의 가장 앞에 등록되어있어서 bundle exec
를 사용하지 않더라도 이를 사용한 것과 같은 효과를 볼 수 있습니다. 개념도 훨씬 단순합니다.
use_ruby
함수를 direnv 전역 설정 파일에 등록하기
direnv에서는 각 디렉터리에 작성하는 .envrc
이외에 전역 설정 파일인 ~/.direnvrc
를 지원합니다.
앞서 루비 프로젝트 설정을 위해 세 줄짜리 .envrc
를 작성했습니다.
local ruby_dir=$HOME/.rubies/ruby-2.5.1
load_prefix $ruby_dir
layout ruby
이 내용을 ~/.direnvrc
에 함수로 정의하고 .envrc
에서는 이 함수를 사용하는 것이 가능합니다. ~/.direnvrc
에 아래 내용을 추가합니다.
use_ruby() {
local ruby_dir=$HOME/.rubies/ruby-$1
load_prefix $ruby_dir
layout ruby
}
이제 .envrc
는 다음과 같이 작성합니다.
use ruby 2.5.1
use_ruby
함수는 direnv 환경에서 use ruby
와 같이 사용할 수 있습니다. 그리고 인자로는 루비 버전을 넘겨줍니다. 이 내용을 허가하고 루비의 경로를 확인합니다.
$ direnv allow
$ which ruby
~/.rubies/ruby-2.5.1/bin/ruby
루비 2.5.1 버전을 찾는 것을 확인할 수 있습니다. 지금까지 작업에서 유추하실 수 있겠지만 다른 버전의 루비를 사용하는 것도 마찬가지입니다. 예를 들어 ruby-install ruby 2.4.4
로 루비 2.4.4 버전을 설치했다고 가정해보겠습니다. 특정 프로젝트 디렉터리에서 2.4.4 버전의 루비를 사용하고 싶다면 아래와 같이 .envrc
파일을 작성해줍니다.
use ruby 2.4.4
이 디럭터리에서 루비를 찾아봅니다.
$ which ruby
~/.rubies/ruby-2.4.4/bin/ruby
설정한대로 루비 2.4.4 버전을 사용하고있습니다.
파이썬Python
파이썬과 direnv을 조합해서 사용하는 경우 pyenv와 virtualenv를 사용합니다. pyenv는 파이썬의 버전 관리용으로 사용하고 virtualenv는 프로젝트 환경을 관리하는 용도로 사용합니다.
맥OS의 경우 홈브류를 사용해 pyenv를 설치할 수 있습니다. 여기서는 pyenv를 설치하고 파이썬3 최신 버전을 설치합니다. pyenv는 파이썬을 소스에서 직접 빌드하기 때문에 설치에 시간이 걸립니다.
$ brew install pyenv
$ pyenv install 3.7.0
...
Installed Python-3.7.0 to /Users/nacyot/.pyenv/versions/3.7.0
pyenv는 ~/.pyenv
아래에 버전별로 파이썬을 설치합니다. pyenv
프로그램 자체가 파이썬 버전 관리 역할을 수행합니다. direnv와 함께 사용하는 경우 단순히 파이썬을 버전들을 설치하는 용도로만 사용합니다.
이 버전의 direnv는 내부적으로 virtualenv를 사용해 특정 디렉터리의 파이썬 프로젝트를 셋업합니다. 따라서 사용하고자 하는 파이썬 버전의 pip로 virtualenv를 미리 설치해두어야합니다.
$ ~/.pyenv/versions/3.7.0/bin/pip install virtualenv
Successfully installed virtualenv-16.0.0
다음으로 파이썬 프로젝트를 셋업할 수 있도록 파일에 use_python
함수를 정의합니다.
use_python() {
local python_root=$HOME/.pyenv/versions/$1
load_prefix "$python_root"
layout_python "$python_root/bin/python"
}
파이썬 프로젝트 디렉터리 아래에 .envrc
파일을 만들고 아래 내용을 추가합니다.
use python 3.7.0
이 내용을 허가allow해줍니다. 처음 use python
을 로드하면 direnv가 자동적으로 현재 디렉터리의 virutalenv 환경을 구축해줍니다.
$ direnv allow
direnv: loading .envrc
direnv: using python 3.7.0
Running virtualenv with interpreter ~/.pyenv/versions/3.7.0/bin/python
Using base prefix '~/.pyenv/versions/3.7.0'
~/.pyenv/versions/3.7.0/lib/python3.7/site-packages/virtualenv.py:1041: DeprecationWarning: the imp module is deprecated in
favour of importlib; see the module's documentation for alternative uses
import imp
New python executable in <CURRENT_DIR>/.direnv/python-3.7.0/bin/python
Installing setuptools, pip, wheel...direnv: ([direnv export zsh]) is taking a while to execute. Use CTRL-C to give up.
done.
direnv: export +CPATH +LD_LIBRARY_PATH +LIBRARY_PATH +MANPATH +PKG_CONFIG_PATH +VIRTUAL_ENV ~PATH
virtualenv는 보통 virutalenvwrapper와 함께 사용되고 이 경우 모든 파이썬 가상 환경(virtualenv)이 ~/.virtualenvs
아래에서 관리됩니다. 이와 달리 direnv를 사용하는 경우 프로젝트 디렉터리의 .direnv
아래에 가상환경이 만들어집니다.
tree
를 사용해 .direnv
디렉터리의 구조를 확인해봅니다.
$tree .direnv -L 2
.direnv
└── python-3.7.0
├── bin
├── include
├── lib
└── pip-selfcheck.json
python
이나 pip
를 which
로 찾아보면 이 디렉터리 아래에 있는 실행 파일을 사용하는 것을 확인할 수 있습니다.
$ which python
<CURRENT_DIR>/.direnv/python-3.7.0/bin/python
$ which pip
<CURRENT_DIR>/.direnv/python-3.7.0/bin/pip
이 디렉터리에서 pip
로 파이썬 패키지를 설치하면 마찬가지로 .direnv/python-3.7.0/lib/python3.7/site-packages/
아래에 설치됩니다.
노드.jsNode.js
노드.js에서는 버전 관리 도구를 NVM을 많이 사용합니다. NVM의 경우 셸스크립트를 기반으로 작성되어있어서 사용하려면 셸에 추가적인 설정이 필요합니다. direnv를 사용하는 경우 버전 관리 기능을 직접 사용하지 않아도 되기 때문에 단순히 노드.js의 특정 버전을 설치하는 용도라면 nodebrew를 추천합니다. 맥OS를 사용하는 경우 홈브류로 설치할 수 있습니다. 추가적으로 소스코드 다운로드를 위한 디렉터리를 생성해줍니다.
$ brew install nodebrew
$ mkdir -p ~/.nodebrew/src
install-binary latest
명령어로 최신 버전을 설치할 수 있습니다.
$ nodebrew install-binary latest
Fetching: https://nodejs.org/dist/v10.7.0/node-v10.7.0-darwin-x64.tar.gz
######################################################################## 100.0%
Installed successfully
10.7.0 버전이 설치되었습니다.
이제 .envrc
에 노드를 셋업해봅니다. direnv의 표준 라이브러리에는 use_node
가 정의되어있습니다. 따라서 루비나 파이썬과 달리 노드.js의 경우 ~/.direnvrc
에 use_node
함수를 정의하지 않아도 됩니다. use_node
함수의 경우 노드가 설치된 경로를 NODE_VERSIONS
환경변수에서 찾습니다. 따라서 셸의 설정 파일이나 .envrc
에 이 환경변수를 정의해야합니다. 또한 버전 명 앞에 붙는 프리픽스를 NODE_VERSION_PREFIX
로 지정할 수 있습니다. 여기서는 이 환경변수들을 .envrc
에 정의하겠습니다. layout_node
도 추가해줍니다.
export NODE_VERSIONS=$HOME/.nodebrew/node
export NODE_VERSION_PREFIX=v
use node 10.7.0
layout node
노드.js의 패키지 관리자는 기본적으로 프로젝트 디렉터리 아래의 node_modules
를 사용합니다. 따라서 루비나 파이썬과 달리 .direnv
디렉터리를 사용하지 않습니다. direnv를 사용하는 경우 노드의 경로를 지정해주는 것 이외에 특별한 것은 없습니다.
노드 경로가 제대로 설정되었는지 확인해봅니다.
$ which node
/Users/nacyot/.nodebrew/node/v10.7.0/bin/node
$ which npm
/Users/nacyot/.nodebrew/node/v10.7.0/bin/npm
NodeBrew로 설치한 Node를 찾는 것을 확인할 수 있습니다.
다른 언어
direnv 저장소의 위키에서는 고go, 얼랭Erlang, 하스켈Haskell, 펄Perl 등 다른 언어들의 설정법도 다루고 있습니다. direnv에서 직접 지원하지 않더라도 use_xxx
나 layout_xxx
는 단순히 셸 함수이므로 직접 만들어서 사용할 수도 있습니다.
그 외의 활용법
위에서 다룬 내용 이외에 direnv를 사용하는 데 도움이 될만한 것들을 몇 가지 정리해보았습니다.
전역 설정 파일 .direnvrc
프로그래밍 언어 설정에서 다뤘듯이 .envrc
파일을 로드할 때 항상 먼저 ~/.direnvrc
파일을 읽습니다. 이 파일에 .envrc
에서 공통으로 사용할 헬퍼 함수를 정의하거나 항상 읽어야하는 내용을 추가해둘 수 있습니다.
dotenv 파일 불러오기
direnv에서는 dotenv 형식의 환경변수 설정파일을 읽어올 수 있습니다. 다음 내용을 가진 .env
이 있다고 가정해보겠습니다.
HELLO1=hello
HELLO2=world
dotenv
함수로 .env
파일을 읽어올 수 있습니다. 다음 내용을 .envrc
에 추가합니다.
dotenv .env
이 내용을 허가하면 .env
의 환경변수들이 셸 환경에 추가됩니다.
$ direnv allow
direnv: loading .envrc
direnv: export +HELLO1 +HELLO2
$ printenv | grep HELLO
HELLO1=hello
HELLO2=world
상위 디렉터리의 .envrc 읽어오기
.envrc
파일이 있는 경우 하위 디렉터리에서는 모두 이 내용을 따릅니다. 다음과 같은 .envrc
파일이 있다고 가정합니다.
export HELLO=world
foo
디렉터리를 만들고, 이 디렉터리에 들어가서 HELLO
환경변수가 있는지 확인해봅니다.
$ cd foo
$ printenv | grep HELLO
HELLO=world
이 디렉터리에 .envrc
를 생성하면 더 이상 상위 디렉터리의 .envrc
를 읽어오지 않습니다.
$ touch .envrc
$ direnv allow
$ printenv | grep HELLO
<출력 결과 없음>
이 파일을 추가하려면 source_up
함수를 사용합니다. foo
디렉터리의 .envrc
에서 source_up
함수를 실행합니다.
$ echo 'source_up' > .envrc
$ direnv allow
$ printenv | grep HELLO
HELLO=world
정상적으로 상위 디렉터리의 .envrc
파일을 읽어옵니다.
source_up
이외에도 source_env <DIR>
함수를 사용해 다른 디렉터리의 .env
를 읽어올 수 있습니다.
direnv 표준 라이브러리
direnv에는 .envrc
에서 사용할 수 있는 표준 라이브러리가 준비되어있습니다. 이 글에서 다룬 layout_node
, source_up
, dotenv
함수도 여기에 정의되어있습니다. 다른 함수들에 대해서는 공식 저장소의 stdlib.sh 파일을 참고해주세요.
마치며
개발자들은 커맨드 라인 인터페이스를 애용합니다. 그만큼 셸의 환경을 구축하기 위한 다양한 시도들이 있었습니다. 셸 환경의 경우 대표적으로 autoenv, dotenv, direnv 같은 도구들이 있었고, 프로그래밍 언어들의 경우 위에서 다룬 rbenv, RVM, NVM, pyenv, virtualenv 모두 셸 환경을 다루기 위한 도구들이라고 할 수 있습니다. direnv는 디렉터리 별로 셸 환경을 설정할 수 있게 해줌으로서 좀 더 심플하게 이 문제에 접근하고 있습니다. direnv가 만능은 아니더라도 프로젝트 별로 셸 환경을 구축할 수 있도록 도와줍니다. 즐거운 커맨드 라인 라이프에 조금이나마 도움이 되기를 바랍니다. 🎶