[DevOps] 환경설정 2시간에서 15분으로 줄이는 방법
방금 전에는 됐는데?
[ AI_Todo 프로젝트 개발기 - DevOps #3] 환경설정 통일 Docker가 아닌 MIse를 선탁한 이유
1. 마침내 마주한 최종 보스: 런타임 환경
지난 글에서, 저는 WSL(Windows Subsystem for Linux) 환경을 통해 Spring 컴파일 시간을 절반으로 줄이는 극적인 성능 향상을 경험했습니다. 환호도 잠시, 곧이어 절망적인 에러 메시지가 터미널을 뒤덮었습니다…
pnpm: command not found
husky - pre-commit hook failed (code 127)
애써 구축했던 품질 파이프라인의 문지기, Husky
가 완전히 동작 불능이 된 것…
원인은 명확했습니다. Windows 의 Git 이 실행하는 셸과 WSL 의 셸은 서로 다른 세상이었고, 각자의 PATH
환경 변수를 가졌는데 요약하자면 한쪽 세상에 설치된 pnpm
을 다른 쪽 세상에서는 찾지 못하는, 전형적인 환경 불일치 문제 였습니다.
pnpm
+ Turborepo
로 빌드 속도를 잡기 -> OK
ESLint
+ Husky
로 코드 품질 잡기 -> OK
이제 마지막으로 이 모든 것을 안정적으로 실행시킬 환경 자체를 통일해야 하는, DevOps의 ‘최종 보스’와 마주하게 되었습니다.
2. 가장 강력한 무기, Docker를 쓰지 않은 이유
이 문제의 가장 확실하고 강력한 해결책은 Docker 라고 생각했습니다. OS 레벨까지 통째로 가상화하여 모든 개발자에게 100% 동일한 환경을 제공할 수 있고 저 역시 가장 먼저 Docker Dev Container 를 고려했습니다.
( 사실 아는 게 이거밖에 없었습니다. )
하지만 해결하려는 문제는 “Node.js와 pnpm의 버전 및 PATH 불일치” 인데 매번 이 문제를 해결하기 위해 OS 전체를 컨테이너를 뛰우는 것은 너무 귀찮다고 생각했습니다.
분명 세상에는 저보다 똑똑한 개발자들이 너무나 많기 때문에 이런 상황에 적합한 솔루션이 있다고 생각했고 정보를 찾아봤습니다.
그러던 중 개발스터디 모임에서 예전에 Mise 가 언급이 되었었는데 이게 갑자기 떠올랐었고 해당 도구와 Docker랑 한 번 비교했습니다.
관점 | Docker Dev Container | Mise (이번에 선택한 도구) |
---|---|---|
문제 해결 범위 | OS, 라이브러리, 런타임 등 환경 전체 | Node.js, pnpm 등 런타임 버전/PATH |
자원 사용량 | CPU/RAM 상시 점유 (높음) | 호스트 직접 실행 (오버헤드 최소) |
시작 속도 | 컨테이너 빌드 → 수 분 | 네이티브 캐시 → 수 초 |
IDE 통합 | 별도 플러그인 및 설정 필요 | 기존 IDE 설정 그대로 사용 |
개발자 경험(DX) | 컨테이너 셸에 대한 학습 곡선 | 기존 셸 경험 유지, 명령어 몇 개만 추가 |
결론
Docker 는 훌륭한 도구지만, 지금 상황에서는 좀 과하기도하고 귀찮기도 했습니다.
왜냐하면 제가 필요한건 네이티브 환경 속도 그리고 ‘런타임 버전’이라는 핵심 문제만 해결 되면 충분한데, 도커컴포즈로 변경사항이 있을 때마다 빌드를 다시 하는 건 오히려 비용이 더 든다고 생각했기 때문입니다.
물론 도커에서도 이미지를 만들 때 캐싱을 할 수 있지만 레이어의 변경부분이 초반 부분이라면 뒤에 변경된 부분이 없는 레이어도 전부 빌드를 다시 해야하기 때문에 여러모로 Mise
가 낫다고 생각했습니다.
3. Mise 도입: 3단계 환경 표준화
mise
를 통해 엉망이 된 런타임 환경을 바로잡는 과정은 간단했습니다.
1단계: .tool-versions
로 런타임 명세화
프로젝트의 루트에 모든 환경이 따라야 할 단일 진실 공급원(SSoT)을 만들고 이 파일 하나면 충분!
# .tool-versions
# 이 프로젝트는 반드시 아래 버전의 도구를 사용해야 한다.
nodejs 22.17.0
pnpm 10.12.4
2단계: 깨진 Husky 훅 되살리기
mise
의 가장 강력한 기능인 mise exec
를 사용하여, 셸 환경에 구애받지 않는 pre-commit
훅을 만들기!
# .husky/pre-commit
#!/usr/bin/env sh
set -e
# mise가 없다면 경고 후 스킵 (온보딩 편의성)
if ! command -v mise >/dev/null 2>&1; then
echo "⚠️ mise not found. Skipping pre-commit hooks."
exit 0
fi
# 핵심: 현재 셸의 PATH를 무시하고,
# .tool-versions에 명시된 버전의 pnpm을 찾아 실행한다.
mise exec -- pnpm exec lint-staged
mise exec -- <command>
는 현재 셸의 상태와 무관하게, .tool-versions
에 정의된 정확한 버전의 도구가 포함된 임시 환경을 만들어 <command>
를 실행 합니다.
이 덕분에 Windows Git Bash , WSL , macOS , CI 등 어떤 환경에서도 pre-commit
훅은 100% 동일하게 동작하게 만들 수 있었습니다.
( 물론 OS 차이에서 발생하는 미묘한 차이 ( 파일경로, OS 명령어등 )등을 100% 통제할 수는 없지만 현재 개발환경 셋팅 상황에서는 아주 적당 )
3단계: CI 파이프라인과 완벽한 동기화
로컬에서의 경험을 CI에 그대로 이식 -> jdx/mise-action
은 이 모든 과정을 단 한 줄로 처리!
# .github/workflows/ci.yml
steps:
- uses: actions/checkout@v4
# mise 설치, .tool-versions 기반 런타임 설치, 캐싱까지 한번에 처리
- uses: jdx/mise-action@v2
- run: pnpm install --frozen-lockfile
- run: pnpm run test
4. 최종 교훈: 무거운 문제와 가벼운 문제
이 3부작의 여정은 복잡성을 단계적으로 정복해나가는 과정이었습니다.
- 빌드 복잡성은
pnpm
+Turborepo
로 해결 - 코드 품질은
ESLint
+Husky
로 자동화 - 환경 불일치는 마침내
mise
로 통일
모든 여정이 끝나고 얻은 가장 큰 교훈은 이것입니다.
문제의 무게에 맞는 도구를 선택해야 한다는 것.
Docker 는 여러 서비스와 데이터베이스까지 포함된 복잡한 ‘시스템’을 통째로 복제하는 무거운 문제에 가장 효과적인 망치라고 생각합니다. 하지만 제가 마주했던 ‘런타임 버전 불일치’는 상대적으로 가벼운 문제였고, 이 문제에는 mise
라는 정밀 드라이버가 더 빠르고, 더 효율적이었습니다.
이제 저는 어떤 OS를 쓰든, 어떤 동료와 협업하든, git clone
후 mise install
단 한 번이면 모든 준비가 끝납니다.
길고 길었던 환경 설정과의 싸움 끝에, 마침내 진짜 중요한 ‘개발’에만 집중할 수 있는 평화를 얻었습니다….
“방금 전에는 됐는데?” 라는 말은, 적어도 제 프로젝트에서는 이제 과거의 유물이 되었습니다.
환경 통합의 매직, Mise.
Very Good
댓글남기기