
Vite 프로젝트에 tman 설정하기
요약
AI 코딩 에이전트가 프론트엔드 저장소에서 빌드 및 개발 서버를 중복 실행하며 발생하는 충돌 문제를 해결하기 위한 tman 설정 방법을 소개합니다. tman은 실행 프로세스에 이름과 슬롯을 부여하고 대기열을 관리하여 효율적인 개발 환경을 제공합니다.
핵심 포인트
- AI 에이전트의 중복 빌드 및 포트 충돌 문제 해결
- 실행 프로세스에 이름, 슬롯, 기록(JSON) 부여 기능
- NativeAOT 기반의 가볍고 의존성 없는 바이너리
- Vite 프로젝트에 간단한 명령어로 도입 가능
AI 코딩 에이전트(AI coding agents)를 프론트엔드 저장소(repo)에 풀어놓으면, 이들은 npm run build와 npm run dev를 끊임없이 실행하며, 종종 여러 세션에서 동시에 실행하기도 합니다. 딱히 무엇이 고장 난 것은 아닙니다. 단지 하나의 체크아웃(checkout)에서 네 개의 빌드가 경주하고 있고, 두 개의 개발 서버(dev servers)가 포트(port)를 두고 싸우고 있으며, 이들을 구분할 방법도 없고, 그들이 무엇을 했는지에 대한 기록도 없는 상태일 뿐입니다.
tman은 이러한 일상적인 문제를 해결합니다. 모든 실행에 이름(name), 슬롯(slot), 그리고 **기록(record)**을 부여합니다. 동일한 스위트(suite)가 동시에 두 번 실행되지 않으며, 초과된 실행은 코어(cores)를 점령하는 대신 대기열(queue)에 쌓이고, 모든 실행은 나중에 쿼리(query)할 수 있는 JSON 기록을 남깁니다. 그 밑바탕에는 실제로 멈추거나 메모리 누수(leak)가 발생하는 드문 실행을 강제로 종료하는 백스톱(backstop)이 자리 잡고 있습니다.
이것은 런타임 의존성(runtime dependencies)이 전혀 없는 약 ~3.8 MB 크기의 NativeAOT 바이너리입니다 (Linux, macOS, Windows 지원). 이 포스트에서는 일반적인 Vite 프로젝트에 이를 도입하는 과정을 살펴봅니다.

설치 (Install)
npm install -g @standardbeagle/tman
또는 글로벌 node modules에 설치하고 싶지 않다면 쉘(shell) 한 줄 명령어를 사용하세요:
curl -fsSL https://raw.githubusercontent.com/standardbeagle/tman/main/install.sh | sh
일반적인 Vite 프로젝트 (A stock Vite project)
대상 프로젝트에는 특별한 점이 없습니다. 다음과 같은 평범한 스캐폴드(scaffold)를 사용합니다:
npm create vite@latest vite-demo -- --template react-ts
cd vite-demo
npm install
그러면 package.json에 평소와 같은 스크립트(scripts)가 생성됩니다:
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
...
1단계 — tman 도입하기 (Step 1 — adopt tman)
단 한 줄의 명령어로 가능합니다:
tman init --shims --gitignore

tman init은 package.json을 읽고, 관리할 가치가 있는 스크립트를 감지하며, 세 가지를 작성합니다:
.tman.kdl— 감지된 스크립트당 별칭 (alias)을 포함하는 프로젝트별 설정 파일- shims (심) — 저장소 루트에 위치한 아주 작은
./build및./lint래퍼 (wrapper)로, 기존의 습관(및 단순히./build를 실행하는 에이전트들)이 tman을 통해 투명하게 실행되도록 합니다. - 심 (shims)을 위한
.gitignore항목
tman이 스캐폴딩(scaffold)하지 않은 점에 주목하세요. 새로운 Vite 앱에는 test 스크립트가 없으므로, 마치 있는 것처럼 속이는 ./test 심도 생성되지 않습니다. tman이 감지할 수 없는 별칭은 주석 처리된 상태로 남겨둡니다. 이는 테스트 스위트(suite)가 누락되었을 때, 통과한 것처럼 속이는 대신 명확하게 실패를 알리기 위함입니다.
2단계 — .tman.kdl 살펴보기

defaults 블록은 의도적으로 보수적으로 설정되어 있습니다:
stall "30m"— 실행 시간 예산(runtime budget)이 아닌, 프로세스 멈춤(hang)에 대한 방어책입니다. 실행 중 아무런 출력이 없고 동시에 30분 동안 유휴(idle) 상태일 때만 작동합니다. 차가운 상태(cold)의tsc -b는 출력을 내지 않으면서도 정당하게 오랫동안 생각할 수 있습니다. 예상 실행 시간과 같은 크기로 stall을 설정하면 정상적인 작업도 중단될 수 있습니다.max-parallel 2— 버킷(bucket)당 한 번에 최대 두 개의 실행만 허용하며, 나머지는 경합하는 대신 대기열(queue)에 들어갑니다.retain "24h"— 완료된 실행 기록이 유지되는 기간입니다.- 메모리/CPU/실제 경과 시간(wall-clock) 제한은 주석 처리되어 있습니다. 빌드는 정당하게 코어를 포화시키고 RAM을 점유하기 때문입니다. 테스트 스위트가 실제로 오작동할 때 직접 선택하여 활성화하면 됩니다.
각 alias 블록은 이름을 명령에 매핑하므로, tman build (또는 ./build 심)는 관리되는 npm run build를 의미합니다.
3단계 — 관리되는 빌드 실행하기
./build

npm run build와 동일한 출력을 보여줍니다. 왜냐하면 이것이 바로 npm run build이기 때문입니다. 여기에 이름, 중복 방지 잠금(dedup lock), 대기열의 슬롯, 그리고 실행 기록이 추가되었을 뿐입니다. 이러한 관리(supervision) 기능은 실제로 필요해지는 날 전까지는 눈에 보이지 않습니다.
Step 4 — 개발 서버 문제 (the dev server problem)
제가 이 도구에 매료된 사례는 다음과 같습니다. 두 개의 에이전트 세션이 모두 개발 서버(dev server)를 실행해야 한다고 결정한 경우입니다:
tman run --name dev -- npm run dev &
tman run --name dev -- npm run dev

두 번째 호출은 즉시 거부됩니다:
tman: run 'dev' already active (pid 2244513, id f281b5c2ad30); use --replace to kill it
두 번째 Vite 인스턴스가 생성되지 않으며, 포트가 5174로 폴백(fallback)되지도 않고, 서버를 시작한 세션이 종료된 후에도 포트를 점유하고 있는 좀비 서버(zombie server)가 남지도 않습니다. 만약 설정 변경 후 새로운 서버를 띄우는 식의 제어(takeover) 의미론을 원한다면 --replace를 사용하면 됩니다. 이 옵션은 기존 실행을 종료하고 해당 프로세스가 이름을 반환할 때까지 기다립니다.
잠금(Locks)은 이름과 디렉터리 모두를 기준으로 이루어지므로, 이 저장소(repo)에서의 dev 실행이 다른 체크아웃(checkout)에서의 dev 실행을 차단하는 일은 절대 없습니다.
Vite를 위한 특별한 튜닝 팁: 개발 서버에 --max-time을 절대 부여하지 마세요. 개발 서버는 영구적으로 실행되도록 설계되었습니다. 실제 시간(wall-clock) 제한은 테스트 스위트(suites)나 빌드(builds)를 위한 것이지, 서버를 위한 것이 아닙니다.
Step 5 — 무엇이 실행되었고, 어떻게 진행되었는가?
tman list --all
tman status

모든 실행은 하나의 JSON 레코드를 남깁니다: 명령(command), 현재 작업 디렉터리(cwd), 종료 코드(exit code), 피크 메모리(peak memory), 시작 및 종료 시간, 그리고 실행 시 적용된 제한 사항(caps)입니다. tman status --json은 동일한 정보를 기계가 읽을 수 있는 형식으로 출력하며, 이는 에이전트가 "제가 시작한 빌드가 실제로 끝났나요?"라고 묻거나, 혹은 여러분이 밤새 에이전트들이 무엇을 실행했는지 물을 때 정확히 필요한 기능입니다.
고아 프로세스 수거(Orphan reaping) 기능도 기본으로 제공됩니다. 모든 tman 명령은 러너(runner)가 종료된 자식 프로세스들을 종료시키고 만료된 레코드를 정리합니다. 에이전트 세션이 충돌하더라도 Vite 서버가 포트를 점유한 채 남아있는 일은 발생하지 않습니다.
Step 6 — shim을 우회하는 실행 건 처리하기
Shim(심)은 셸 조회(shell lookup)를 거치는 명령만 포착합니다. 에이전트가 아무런 접두사 없이 npm run build와 같이 Bash 도구를 호출하면 shim을 그대로 통과해 버립니다. Claude Code의 경우, 한 단계 상위 레벨에서 동일한 정책을 적용하는 하나의 hook(훅)이 적용됩니다:
{
"hooks": {
"PreToolUse": [
...
이 훅은 아무 접두사가 없는 테스트/빌드 명령을 tman run을 통해 재작성하며, 그 외의 모든 것은 건드리지 않고, 절대로 차단하지 않습니다: 모든 실패 경로(tman 누락, 잘못된 요청, 읽을 수 없는 프로젝트 등)에서 명령은 작성된 그대로 유지됩니다. Codex CLI, Gemini CLI, Cursor, opencode 및 기타 도구를 위한 유사한 가이드도 제공됩니다.
마무리
Vite 프로젝트의 총 설정 비용은 npm install -g 한 번, tman init 한 번입니다. 그 이후부터는 다음과 같습니다:
- 중복된 빌드와 개발 서버는 경쟁(race)하는 대신 **거부(refused)**됩니다.
- 과도한 실행은 코어를 포화시키는 대신 슬롯을 위해 **대기(queue)**합니다.
- 모든 실행은 귀하(또는 귀하의 에이전트)가 조회할 수 있는 **기록(record)**을 남깁니다.
- 멈춤(hang) 및 누수(leak) 현상은 정상적인 주간에는 발생하지 않을 안전장치(backstop)에 의해 종료됩니다.
Docs: standardbeagle.github.io/tman · Source: github.com/standardbeagle/tman · npm: @standardbeagle/tman
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기