mise: 런타임을 버전 별로 관리하여 프로젝트마다 버전 설정하기

@itsmo · March 14, 2024 · 16 min read

들어가기 전에: 본 게시글의 모든 명령어는 Windows Subsystem for Linux 2(WSL2) Ubuntu 22.04에서 진행되었습니다. macOS나 여타 Linux 환경에서는 명령어가 다를 수 있습니다.

진행하는 프로젝트가 많아지면서 프로젝트 별로 개발 버전이 다른 경우가 종종 생긴다. 현재 진행 중인 프로젝트의 node.js 버전이 18 이라면 새로운 프로젝트는 node.js 버전이 20이라던가, 아니면 오래 전에 개발했던 프로젝트를 열어보려는데 구버전의 런타임을 사용해야만 동작한다던가 하는 경우 말이다.

사용하는 언어마다 이러한 경우에 대비하여 버전 매니저를 설치하여 사용할 수 있다. Node.js의 경우 nvm, Python의 경우 pyenv, Ruby의 경우 rbenv 같이 말이다. 그러나 사용하는 언어가 많아진다면 각 언어 별로 버전 매니저를 설치해서 사용하는 것 자체가 피곤해질 수 있다.

이러한 문제를 해결하기 위해 런타임 언어의 버전을 하나의 명령어로 관리하기 위해 탄생한 툴들이 있다. 이 중 가장 유명한 툴은 asdf이고, 아래 소개할 툴은 이 asdfRust로 다시 작성한 툴인 mise이다.

설치

원문 및 보다 자세한 사용법은 https://mise.jdx.dev/getting-started.html을 참조할 것.

1. mise CLI 설치하기

아래의 명령어를 입력하여 mise를 설치한다.

curl https://mise.run | sh

mise가 잘 설치되었는지 확인하기 위해 아래의 명령어를 입력해 보자.

$ ~/.local/bin/mise --version
mise 2024.x.x

mise 2024.x.x 와 같이 버전이 출력된다면 제대로 설치된 것이다.

2. mise 활성화하기

mise activate 명령어 하나로 misemise에 설치된 각 런타임들의 경로를 PATH에 간단하게 추가할 수 있다. 해당 명령어가 쉘 시작 시점에 동작하도록 다음 명령어를 통해 각 쉘의 rc 파일에 입력하자.

  • bash
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
  • zsh
echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc

런타임 설치하고 사용하기

지금까지 설치한 것은 런타임을 관리하기 위한 툴이다. 이젠 이 툴을 사용하여 사용하고자 하는 런타임을 설치할 것이다. 실제 예시를 들어 사용법을 익혀보자.

시나리오

node.js를 활용한 두 개의 프로젝트 A, B가 있다. 프로젝트 A는 node.js 18버전으로 작업하였고, 프로젝트 B는 node.js 20버전으로 작업하였다. 두 프로젝트를 오가며 작업해야 해서 두 버전을 모두 설치해 사용해야 한다.

단, 앞으로 새롭게 진행할 프로젝트들은 모두 20버전으로 진행할 것이라고 가정한다.

이용 가능한 런타임 확인하기

mise ls-remote <runtime> 명령어를 사용하면 해당 런타임에서 설치 가능한 모든 버전이 표시된다.

# mise ls-remote <runtime>
$ mise ls-remote node
...
20.10.0
20.11.0
20.11.1
21.0.0
21.1.0
21.2.0
21.3.0
21.4.0
21.5.0
21.6.0
21.6.1
21.6.2
21.7.0
21.7.1
$ 

런타임 설치하기

mise install <runtime>@<version>명령어를 사용하면 해당하는 버전의 런타임을 설치할 수 있다.

version의 경우 정확히 명시하지 않고 node@20, node@latest와 같이 명시할 수도 있다. node@20과 같이 입력할 경우 20버전 중 가장 최신 버전이 설치되고, node@latest의 경우 가장 최신 버전의 node가 설치된다.

아래 명령어를 통해 두 가지 버전의 node를 설치하자.

# node 18 설치
$ mise install node@18
mise node@18.19.1 ✓ installed

# node 20 설치
$ mise install node@20
mise node@20.11.1 ✓ installed

자, 이제 두 버전의 node를 설치하였으므로 잘 설치되었는지 확인하기 위해 아래 명령어를 입력해보자.

$ node --version
zsh: command not found: node

분명 위에서 설치했음에도 불구하고 node를 찾을 수 없다고 나온다. 이는 우리가 node를 설치만 했을 뿐 어떤 버전을 사용한다고 명시하지 않았기 때문이다.

전역으로 사용할 런타임 버전 설정하기

위의 시나리오에서 우리는 20버전의 node를 전역으로 사용한다고 가정하였다. 모든 환경에서 특정 버전을 사용하도록 명시하려면 mise use -g <runtime>@<version> 명령어를 사용하면 된다. 이 명령어를 사용할 경우 mise의 전역 설정 파일에 해당 버전을 기록하고, 만약 설치되어 있지 않은 버전의 런타임일 경우 자동으로 설치까지 해준다.

$ mise use -g node@20 # 전역에서 사용할 node버전을 20(20.x.x)으로 설정
mise ~/.config/mise/config.toml tools: node@20.11.1

설치할 때와 달리 node의 버전을 20으로 명시한 이유는 20버전의 minor version change가 일어나 새롭게 업데이트하는 경우 버전을 다시 명시해 주어야 하는데, 이는 매우 번거로울 수 있기 때문이다. 혹시나 정확한 버전으로 명시하고 싶은 경우 node@20.11.1과 같이 표기하면 된다.

위와 같이 전역 버전 설정 후 다시 node의 버전을 확인해 보자.

$ node --version
v20.11.1

우리가 전역으로 설정했던 20버전의 node가 잘 실행되는 것을 볼 수 있다.

특정 프로젝트에서 런타임 특정 버전 설정하기

B프로젝트는 전역으로 설정된 20버전의 노드를 사용해도 문제가 없지만. A프로젝트의 경우 18버전의 노드가 필요하기 때문에 추가적인 설정이 필요하다. 해당 프로젝트의 경로로 이동해서 다음 명령어를 사용해 버전을 명시하자.

$ cd projectA # 해당 프로젝트의 경로로 이동한다.

$ mise use node@18 # node 버전을 18로 명시한다.
mise ~/projectA/.mise.toml tools: node@18.19.1

$ node --version # node 버전을 다시 확인한다.
v18.19.1 # 18 버전의 node가 사용되고 있음을 확인할 수 있다.

$ cd .. && node --version # 이전 폴더로 돌아가 node 버전을 확인해 본다.
v20.11.1

해당 폴더에서 mise use node@18명령어를 사용하자 폴더 아래에 .mise.toml이라는 문서가 생성되면서 node의 버전이 18로 변경되었다. 상위 폴더로 돌아가 node 버전을 다시 확인해보면 전역으로 설정했던 20버전이 호출되는 것을 확인할 수 있다.

이와 같이 전역으로 런타임의 특정 버전을 설정할 때에는 mise use -g, 특정 로컬에서 버전을 설정할 때에는 mise use를 사용하면 된다. 다음은 이 명령어를 사용했을 때 생성되는 .mise.toml파일에 대해서 간단하게 알아보자.

.mise.toml 파일 톺아보기

세부 설정 방법은 https://mise.jdx.dev/configuration.html을 참고.

.mise.toml 파일은 asdf에서 사용하는 .tool-versions파일과 같이 mise에서 사용할 버전을 명시해 둔 파일이다. 해당 파일을 사용하면 사용할 런타임의 버전 설정 외에도 env변수나 Python에서 사용하는 venv 설정 파일의 경로 등을 관리할 수 있다.

위의 로컬 프로젝트의 버전을 명시할 때 생성됐던 .mise.toml파일을 확인해 보자.

# cat .mise.toml
[tools]
node = "18"

[tools] 안에 node의 버전이 18로 명시되어 있는 것을 확인할 수 있다. 다음은 전역으로 설정한 노드 버전을 확인해 보자. 전역 설정 파일은 기본적으로 ~/.config/mise/config.toml 에 위치하고 있다.

# cat ~/.config/mise/config.toml
[tools]
node = "20"

앞서 전역으로 사용하기로 명시했던 20버전의 node가 적혀 있는 것을 확인할 수 있다.

mise는 프로젝트 폴더에 로컬 설정 파일이 있는지 확인하고, 로컬 파일이 있다면 거기에 적힌 런타임의 버전을 실행하고, 로컬 파일이 없다면 전역으로 설정한 런타임의 버전을 실행한다는 사실을 알 수 있다.

IDE에서 mise 올바르게 사용하기

세부 설정 방법은 https://mise.jdx.dev/ide-integration.html을 참고.

위와 같이 설정하고 IntelliJWebStorm, VSCode와 같은 IDE에서 프로젝트를 실행하면 PATH가 없다고 나오며 제대로 동작하지 않을 수 있다. 이는 우리가 mise activate 명령어를 쉘의 rc파일에서만 진행했기 때문이다. 스크립트 파일이나 IDE는 명령어를 실행할 때 .bashrc.zshrc가 아닌 .bash_profile.zprofile이 작동한다.

mise에서 제공하는 shimsPATH에 등록하는 명령어를 profile 파일에 올려 IDE에서 런타임을 올바르게 인식할 수 있도록 하자.

# bash
$ echo 'eval "$(mise activate bash --shims)"' >> ~/.bash_profile

# zsh
$ echo 'eval "$(mise activate zsh --shims)"' >> ~/.zprofile

여기서 생소할 수 있는 shims 라는 단어가 나오는데, 이 shims는 무엇일까?

Shims

구글에 Shim을 검색하면 MDN 용어 사전이 튀어나온다. MDN에서는 Shim을 다음과 같이 정의한다.

심Shim은 이미 존재하는 코드의 동작을 바로잡는 데 사용되는 코드 모음이며, 보통 문제를 야기시키는 신규 API에 대응한다.

위키백과에서는 Shim을 다음과 같이 정의한다.

컴퓨터 프로그래밍에서 shim은 API 호출을 투명하게 가로채고 전달된 인수를 변경하고 작업 자체를 처리하거나 작업을 다른 곳으로 리디렉션하는 라이브러리입니다. Shim을 사용하여 최신 환경에서 이전 API를 지원하거나 이전 환경에서 새 API를 지원할 수 있습니다.

그리고 이 shims이 작성되어 있는 폴더로 이동해 내용을 확인해 보면, 각 파일들이 심볼릭 링크로 연결되어 있는 것을 확인할 수 있다.

shims 폴더의 실제 내용
shims 폴더의 실제 내용

정리해보면, shims 아래에 있는 파일 이름으로 PATH를 연결하면 해당 명령어를 실행할 때 mise를 통해서 실행되고, mise가 .mise.toml 파일을 읽어 사용자가 설정한 런타임 버전에 맞게 이를 실행해 준다는 의미가 된다. 즉, IDE에서 런타임을 mise의 shims를 가리키도록 설정하면, 프로젝트가 바뀌어서 런타임의 버전이 바뀌더라도 IDE의 설정을 다시 건드릴 필요 없이 정상 동작하게 되는 것이다.

JetBrains IDE에서 mise 자동 인식시키기

JetBrains IDE는 node, ruby와 같은 몇몇 언어들의 경우 asdf에서 설치한 런타임 버전들을 자동으로 인식하도록 설정되어 있다(유료 IDE의 힘인가보다.).

즉, miseasdf의 경로로 생각하도록 symlink만 걸어주면 JetBrains의 IDE에서도 SDK를 자동 인식시킬 수 있다.

다음 명령어를 통해서 심볼릭 링크를 생성하자.

ln -s ~/.local/share/mise ~/.asdf

Java의 경우에는 해당 명령어를 사용하더라도 자동 인식이 되지 않는 것 같다(Java 환경에서 asdf와 같은 버전 매니저를 많이 사용하지 않아서 그런지…). 따라서 직접 경로를 설정해야 하는데, mise에서 설치한 모든 런타임의 경로는 ~/.local/share/mise/installs에 위치해 있다.

IDE를 재시작하면 정상 인식하는 것을 확인할 수 있다.

마무리

mise는 이 외에도 (아직 실험 기능이긴 하나) 특정 작업을 자동화할 수 있는 Tasks와 같은 기능도 제공한다. 나는 아직까지 IDE를 사용해서 코딩을 하고 있어 큰 필요성은 느끼지 못했으나 만약 CLI로 많은 작업을 하는 사람에게는 크게 유용할 것 같아 보인다.

여담으로 여태 이 툴의 이름을 마이스 혹은 마이즈라고 읽었는데, 정확한 발음은 미즈[MEEZ]라고 한다. 사실 풀 네임은 mise-en-place[미장플라스]라는데, 기존 이 툴의 이름이었던 rtx가 Nvidia의 RTX 그래픽카드 시리즈와 이름이 같아 변경했음에도 검색하기가 여전히 너무 어렵다. 업데이트도 꾸준하고 asdf보다 사용성도 좋아 애용하는 툴인데, 하루빨리 유명해져서 구글 검색 가장 위에 떴으면 좋겠다...사실 그러길 바라는 마음에 이 글을 썼다.

@itsmo
배운 것을 잊지 않기 위해 틈틈히 기록합니다.