의외의 복병 개발환경, asdf로 개발 언어를 관리해 보자

배경

코딩을 할 때 의외의 복병은 개발 환경을 만드는 일이다. 개발 환경은 여러 층차를 가지는데, 그 하나가 시스템에 개발 언어와 관련된 환경을 설치하는 일이다. 컴파일 언어의 경우 컴파일러 등의 프로그램, 인터프리터 언어의 경우 인터프리터 등의 프로그램이 그것이다. 여기에서는 node, ruby, python과 같은 인터프리터 언어를 기준으로 이야기하려고 한다.

어떤 인터프리터든지 OS에 바로 설치하면 나중에 다른 버전의 인터프리터가 필요할 때 난처하게 된다. 예를 들어 OS에 바로 python 3.6을 설치했는데, 나중에 python 3.11이 필요하다면 난처할 수 있다. 따라서 version manager(이하 VM)라고 하는 앱을 통해 설치하는 편이 편리하다. node의 경우 nvm, ruby의 경우 rvm 등이 있다. python의 경우는 좀 복잡하지만 일단 conda가 그와 유사하다. 참고로 package 의존성을 관리하고 설치를 도와주는 dependency manager(이하 DM)와 VM은 서로 다르다.

각각 이미 좋은 VM들이 있지만, 하나의 머신에 여러가지 VM을 설치하고 서로 다른 인터페이스로 관리해야 한다는 번거로움이 있다.

NODE    RUBY    PYTHON
----    ----    ------
nvm     rvm     conda
----------------------
         OS

이런 문제를 해결하기 위해 만들어진 것이 asdf이다. 하나의 VM으로 여러가지 언어의 버전을 설치하고 관리할 수 있다. 사용 방법도 간단하고 무엇보다 동일한 인터페이스로 여러가지 언어의 버전을 관리할 수 있다는 장점이 있다. asdf를 사용하면 위의 개요는 아래와 같이 단순화 된다.

NODE    RUBY    PYTHON
----    ----    ------
        asdf
----------------------
         OS

사용법

asdf에서 개발 언어는 plugin을 통해 관리된다. 즉 새로운 언어를 설치하려면 해당 언어의 plugin을 추가해 주어야 한다. plugin 목록은 여기서 볼 수 있다.

asdf의 기본 사용법은 다음과 같다. 자세한 것은 공식 문서를 보자.

asdf list
asdf current
asdf exec <command>
# install a plugin
asdf plugin add nodejs
# install a version
asdf install nodejs latest
# set a global version
asdf global nodejs latest
# set a local version
asdf local nodejs latest

node 설치를 예로 들어 보자. 아래와 같이 하면 node 최신 버전을 설치할 수 있다.

# node plugin 추가
asdf plugin add nodejs
# node 최신 버전 설치
asdf install nodejs latest
# global 환경에서 사용할 기본 node 버전 지정.
asdf global nodejs latest
# 기본 node 버전을 지정하지 않으면 인식하지 않음
node -v
npm -v

다만 아직 커뮤니티가 활성화 되지 않아서 미리 컴파일된 이미지가 적다. 새로운 버전 설치 시에 pre-compile된 이미지를 설치하지 않고 로컬에서 직접 컴파일 하는 경우가 있으므로 시간이 걸리고 컴파일 에러가 발생할 수 있다. 컴파일 에러는 주로 컴파일에 필요한 library가 OS에 미리 준비되어 있지 않아서 발생한다. 따라서 언어에 따라 이를 고려해 주어야 할 수 있다. 아래는 ruby와 python의 예이다.

# https://github.com/rbenv/ruby-build/wiki#suggested-build-environment
# 컴파일에 필요한 linux library 설치 ( install build dependency )
apt-get install autoconf bison patch build-essential rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev
# ruby plugin 추가
asdf plugin add ruby
asdf install ruby latest
asdf global ruby latest
# 설치 확인
ruby -v
bundle -v
# https://stackoverflow.com/a/71682919
# 컴파일에 필요한 linux library 설치 ( install build dependency )
sudo apt install libssl-dev libffi-dev libncurses5-dev zlib1g zlib1g-dev libreadline-dev libbz2-dev libsqlite3-dev make gcc liblzma-dev
# python plugin 추가
asdf plugin add python https://github.com/asdf-community/asdf-python.git
asdf install python latest
asdf global python latest
# 설치 확인
python -V
pip -v

프로젝트에 적합한 버전이 있다면 해당 프로젝트 폴더에서 아래와 같이 선언해 준다. .tools-version 파일이 생성되는 것을 볼 수 있다.

# python 3.8.16 버전을 사용한다면 
asdf local python 3.8.16

package 의존성 관리자(DM)는 이 위에 설치되게 된다. DB을 포함시킨다면 다음과 같은 구조가 된다.

npm    bundler   pip/poetry
----    ----    ------
NODE    RUBY    PYTHON
----    ----    ------
        asdf
----------------------
         OS

TIP

간혹 package 가운데 terminal에서 바로 실행 가능하도록 만들어진 녀석들도 있다. 예를 들어 python의 Pygments package는 python에 의존하여 실행되지만 terminal에서 다음과 같이 직접 실행할 수도 있다.

pygmentize -V

만약 asdf를 통해 python을 설치했고, 이 python을 통해 Pygments package를 설치했을 때, terminal에서 실행되지 않는 경우가 있다. 이는 Pygments 설치 경로가 OS PATH에 추가되지 않았기 때문이다. 이를 해결하려면 PATH를 수작업으로 추가하지 말고 asdf에 마련된 reshim 명령을 실행하면 간단히 해결된다. 자세한 것은 asdf-python를 참고하자.

asdf reshim python

나가며

물론 python 생태계에서 conda와 같은 경우는 env 자체를 만들어 주므로 python 버전에서 package까지 수직으로 관리해준다. 여러 프로젝트가 하나의 env를 사용할 수 있다는 장점도 있지만, 그렇지 않을 경우 프로젝트별로 env를 만들어야 한다는 점이 불편할 수도 있다. 또 다른 언어를 함께 사용한다면 관리하기 어렵게 느껴질 수도 있다.

시스템 자체의 환경으로부터 더 자유롭고자 한다면 docker를 바탕으로 프로젝트를 만드는 것도 방법이다. 이런 VM이나 DM을 사용하지 않아도 된다.

결국 자신에게 맞는 툴을 사용하는 것이 가장 좋다.

... ... ... ...
Back