모두가 설치하라고 권하는 도구들을 삭제하여 터미널 속도를 7.5배 높인 방법
요약
AI 에이전트가 터미널 명령어를 실행할 때 발생하는 셸 시작 오버헤드를 분석하고, 불필요한 플러그인을 제거하여 속도를 7.5배 개선한 사례를 다룹니다. 에이전트는 매 명령어마다 새로운 셸 프로세스를 생성하므로, 인간 중심의 설정이 에이전트 워크플로우에서는 심각한 비용이 될 수 있음을 경고합니다.
핵심 포인트
- AI 에이전트는 매 명령어 실행 시 새로운 셸 프로세스를 생성함
- 인간용 터미널 플러그인은 에이전트에게 반복적인 '세금'으로 작용함
- 셸 시작 시간을 2.05초에서 0.27초로 단축하여 7.5배 성능 향상
- 에이전트 워크플로우 최적화 시 셸 초기화 오버헤드 고려 필수
제 셸(shell)의 시작 시간이 2초나 걸렸습니다. 그러다 AI 에이전트가 하루에도 수백 번씩 그 비용을 지불하고 있다는 사실을 깨달았습니다.
지난 15년 동안 조언은 정해져 있었습니다. oh-my-zsh를 설치하고, 몇 가지 플러그인을 추가하며, 자동 완성(autosuggestions) 기능과 예쁜 git 인식 프롬프트(git-aware prompt)를 갖추라는 것이었죠. 이는 터미널을 앞에 두고 앉아 있기에 더 좋게 만들어 주었습니다. 하지만 사람이 앉아서 한 번에 하나의 명령어를 타이핑한다는 그 가정은 이제 대부분의 경우 틀렸습니다.
제 컴퓨터에서는 터미널 명령어의 압도적인 대다수가 제가 아닌 AI 에이전트에 의해 실행됩니다. 그리고 이 관계가 역전되는 순간, 인간의 편의를 위해 설치했던 모든 플러그인은 기계가 당신을 대신해 반복적으로 지불해야 하는 세금(tax)으로 변합니다.
저는 제 설정을 프로파일링(profile)했고, 에이전트 워크플로우(agent workflow)에 아무런 가치를 더하지 않는 도구들을 삭제함으로써 셸 시작 시간을 2.05초에서 0.27초로 — 약 7.5배 — 단축했습니다. 제가 무엇을 발견했는지, 이것이 왜 그 어느 때보다 중요한지, 그리고 어떻게 여러분의 컴퓨터를 감사(audit)할 수 있는지 알려드리겠습니다.
아무도 말해주지 않는 사실: 에이전트는 당신의 셸을 재사용하지 않습니다
당신이 터미널에서 작업할 때는 한 번 열면 됩니다. ~/.zshrc는 단 한 번 실행되고, 시작 비용을 한 번 지불하면, 그 따뜻하게 유지된 세션(session)에서 몇 시간 동안 타이핑을 하면 됩니다. oh-my-zsh를 불러오는(sourcing) 데 시작 시 600ms가 걸린다고 해도 누가 신경 쓰겠습니까? 오전 9시에 한 번 지불하고 잊어버리면 그만이니까요.
AI 에이전트는 그렇게 작동하지 않습니다. 터미널 명령어를 실행하는 에이전트는 일반적으로 **명령어당(또는 작은 배치 단위당) 새로운 셸 프로세스를 생성(spawn)**합니다. 에이전트가 정성스럽게 관리하는 장기 유지 세션(long-lived session) 같은 건 없습니다. git status 실행 → 셸 시작 → 전체 초기화 파일(init) 로드 → 명령어 실행 → 종료. 그다음 bun install 실행 → 완전히 새로운 셸 시작 → 전체 초기화 파일을 다시 로드 → 종료. 모든 명령어가 전체 시작 비용을 매번 다시 지불하는 것입니다.
이제 현대적인 에이전트 도구들이 실제로 어떻게 작동하는지 결합해 봅시다:
- 병렬 서브 에이전트 (Parallel sub-agents). 복잡한 작업은 여러 에이전트 스레드에 걸쳐 동시에 전개됩니다. 하나는 컨텍스트를 수집하고, 하나는 테스트를 실행하며, 다른 하나는 파일을 편집합니다. 각각 자체 셸을 생성합니다.
- 백그라운드 프로세스 (Background processes). 개발 서버(Dev servers), 워처(watchers), 테스트 러너(test runners)가 자체 셸 인스턴스를 가집니다.
- 재시도 및 검증 루프 (Retry and verification loops). 명령을 실행하고, 출력을 확인하고, 수정하고, 다시 실행합니다. 더 많은 셸이 필요합니다.
따라서 비용은 startup_time이 아닙니다. 다음과 같습니다:
startup_time × commands_per_task × parallel_agents
제가 줄인 1.78초는 한 번 절약되는 것이 아니라, 모든 에이전트 스레드의 모든 셸 생성 시마다, 매일 절약됩니다.
대략적인 예시를 들자면: 50개의 명령을 실행하는 에이전트 작업은 실제 작업을 수행하기 전에 오직 '셸 시작'만으로 약 100초를 소모했습니다. 만약 큰 작업을 위해 세 개의 에이전트를 병렬로 실행한다면, 순수 초기화 오버헤드(init overhead)로 몇 분을 날리는 것이며, 여기에 CPU와 배터리까지 소모합니다.
프로파일링을 통해 알게 된 것들
저는 zsh의 내장 프로파일러를 사용하여 셸당 2초가 어디에 쓰이는지 확인했습니다:
# ~/.zshrc 맨 위에 임시로 추가하세요
zmodload zsh/zprof
# ... 나머지 설정 ...
...
새로운 셸을 열고 표를 읽어보세요. 결과는 거의 우스꽝스러울 정도로 한쪽으로 치우쳐 있었습니다.
문제의 원인 #1: Yandex Cloud CLI의 완성 스크립트 — 827ms (총 시간의 52%)
가장 큰 비용을 차지한 것은 심지어
이것이 문제의 가장 순수한 형태입니다. 탭 완성 (Tab-completion) 기능은 '직접 타이핑하고 Tab 키를 누르는' _사람_에게는 도움이 됩니다. 하지만 에이전트 (Agent)는 언어 모델 (Language Model)로부터 전체 명령 문자열을 한 번에 생성합니다. 에이전트는 결코 Tab 키를 누르지 않습니다. 이 기능의 전체 가치 제안 (Value proposition)은 셸 (Shell)의 주요 사용자와 무관함에도 불구하고, 시작 경로 (Startup path)에서 가장 많은 비용을 차지하고 있었습니다.
가해자 #2: 완성 프레임워크 자체 — 약 640ms
oh-my-zsh의 메커니즘 (compinit, compdump, 그리고 약 2,500개의 compdef 호출)이 나머지 비용의 대부분을 차지했습니다. compinit은 완성 캐시 (Completion cache)를 재구축하고 완성 디렉토리에 대해 보안 감사 (Security audit)를 실행합니다. 셸 환경에서 직접 작업한다면 유용하겠지만, 모델이 명령어를 생성하는 상황에서는 불필요한 짐 (Dead weight)일 뿐입니다.
가해자 #3: Forge의 셸 플러그인
아이러니하게도, 다른 AI 코딩 도구인 Forge (CLI 에이전트)가 자체적인 zsh 플러그인(명령어, 완성, 키 바인딩)을 설치해 두었으며, 이것이 실행 경로 (Hot path) 내에서 eval "$(forge zsh plugin)"을 통해 매 실행 시마다 작동하고 있었습니다. AI 도구가 인간의 속도를 높이기 위해 셸 상호작용 (Shell-interactivity) 기능을 추가함으로써, 정작 명령어를 실행하는 AI 도구 자체의 속도를 늦추고 있었던 것입니다.
정리 과정, 영향도 순서대로
저는 각 단계마다 측정하며 비용이 높은 순서대로 항목들을 제거했습니다:
| 단계 | 제거한 항목 | 시작 시간 |
|---|---|---|
| 기준점 (Baseline) | — | 2.05s |
| ... |
단순히 "무언가를 삭제하는 것"을 넘어, 논리적으로 추론할 수 있게 해주는 몇 가지 세부 사항은 다음과 같습니다:
대화형(Interactive) 셸과 비대화형(Non-interactive) 셸의 차이가 중요합니다. 히스토리 확장 (커밋 메시지를 망가뜨리는 ! 기호), 완성 (Completion), 그리고 프롬프트 테마 (Prompt theming)는 대화형 기능이며, 오직 대화형 셸에서만 로드됩니다. 이러한 기능들이 에이전트에게 영향을 주었다는 사실 자체가, 에이전트가 ~/.zshrc를 불러오는(source) 대화형 셸을 실행하고 있었음을 확인시켜 주었으며, 이것이 바로 파일을 다듬는 것이 명령어당 비용을 줄이는 데 효과적이었던 이유입니다.
도구를 삭제한다는 것은 그것에 의존하던 것들도 함께 삭제됨을 의미합니다. 제가 oh-my-zsh를 삭제했을 때, Forge의 플러그인에서 command not found: compdef 오류가 발생하기 시작했습니다. oh-my-zsh가 자동 완성 시스템 (completion system)을 초기화하고 있었고, Forge는 이를 통해 자신의 자동 완성 기능을 등록했기 때문입니다. 프레임워크를 그냥 뽑아버릴 수는 없습니다. 의존성 항목들이 필요로 하는 단 하나의 기본 요소(가벼운 compinit)를 복구하거나, 아니면 의존성 항목들도 함께 제거해야 합니다. (저는 결국 Forge와 자동 완성 기능을 모두 제거했습니다.)
캐시 로직은 거꾸로 구현하기 쉽습니다.
인간의 편의를 위한 기능 중 에이전트 (Agent)에게 도움이 되는 것은 거의 없으며, 오히려 몇몇 기능은 적극적으로 방해가 됩니다. git log 실행 시 less를 열고 키 입력을 기다리는 페이저 (Pager)는 에이전트를 무기한 대기 상태로 만듭니다. 에이전트는 출력이 없다고 판단하고 타임아웃 (Timeout)이 발생할 때까지 멈춰 있게 됩니다. -m 옵션 없이 커밋을 시도할 때 팝업되는 에디터 (Editor)는 오지 않을 인간을 영원히 기다립니다. 일치하는 항목이 없을 때 에러를 발생시키는 글로브 (Glob)는 에이전트가 성공할 것이라고 예상했던 명령을 중단시켜 버립니다.
따라서 속도를 높이기 위해 수행한 동일한 감사 (Audit) 과정은 에이전트에게 올바른 환경을 만들어 주기도 합니다:
# Non-blocking defaults — nothing waits for a human
export PAGER=cat
export GIT_PAGER=cat
...
이것들은 엄밀히 말해 속도 최적화라기보다는, "에이전트가 멈춰서 대기 상태를 복구하느라 토큰 (Tokens)을 낭비하는 것을 방지하는" 수정 사항입니다. 에이전트 중심의 워크플로우 (Agentic workflow)에서 차단된 명령은 느린 명령보다 더 나쁩니다. 단순히 시간만 소모하는 것이 아니라, 모델이 왜 응답을 받지 못했는지 파악하려고 시도하는 동안 컨텍스트 (Context)와 토큰을 소모하며, 종종 다른 도구들을 휘두르며 허우적거리게 만들기 때문입니다.
셸 (Shell)뿐만이 아닙니다 — IDE의 전체 표면이 대상입니다
동일한 논리가 ~/.zshrc 너머로 확장됩니다.
파일 워처 (File watcher). 제 에디터는 약 163,000개의 파일을 감시하고 있었으며, 그중 64,000개는 node_modules에 있었습니다. 파일 워처는 파일이 변경될 때 UI를 업데이트하기 위해 존재하지만, 에이전트는 끊임없이 파일을 변경하며 자신이 변경했다는 것을 알기 위해 워처가 필요하지 않습니다. 스스로 변경을 수행했기 때문입니다. node_modules, 빌드 결과물, 캐시 (Caches)를 제외하면 지속적인 CPU/메모리 소모를 제거할 수 있으며, 결정적으로 저장을 지연시킬 수 있는 이벤트 큐 (Event-queue) 지연을 제거할 수 있습니다.
// settings.json
"files.watcherExclude": {
"**/node_modules/**": true,
...
}
더티 버퍼 (Dirty-buffer) 충돌. 자동 저장 (Auto-save) 기능이 없다면, 에이전트가 파일을 직접 수정하는 순간 인간의 저장되지 않은 에디터 버퍼와 디스크 상의 파일이 서로 어긋나게 됩니다. 그러면 "저장 / 되돌리기 / 덮어쓰기" 충돌 프롬프트가 나타나며, 에이전트는 오래되었거나 충돌하는 콘텐츠를 보고 스스로를 의심하며 턴 (Turns)을 낭비하게 됩니다. 공격적인 자동 저장은 디스크와 버퍼를 동기화하여 충돌이 발생하지 않도록 유지합니다.
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000
이것은 순수하게 에이전트 시대(agent-era)의 실패 모드입니다. 편집과 저장을 동일한 사람이 수행하는 인간 중심의 워크플로우(human-only workflow)에서는 말 그대로 발생할 수 없는 일입니다.
자신의 설정을 감사(Audit)하는 방법
- 추측하지 말고 프로파일링(Profile) 하세요. 상단에
zmodload zsh/zprof를, 하단에zprof를 추가한 뒤 셸을 열어 표를 읽으세요. 문제를 일으키는 주범은 여러분이 예상하는 것과 거의 일치하지 않습니다. - 정직하게 시간을 측정하세요:
for i in 1 2 3; do /usr/bin/time -p zsh -i -c exit; done. 가벼운 설정은 0.3초보다 훨씬 빠릅니다. 1초가 넘는다면 문제가 있는 것입니다. - 영향도에 따라 삭제하세요. 프로파일에서 가장 긴 줄부터 시작하세요. 무엇이 실제로 지표를 변화시켰는지 알 수 있도록 삭제할 때마다 다시 측정하세요.
- 프레임워크뿐만 아니라 의존성(Dependents)도 제거하세요. 삭제한 항목에 연결된 모든 것은 명시적 혹은 암묵적으로 오류를 일으킬 것입니다. 원시 기능(Primitive)이 필요한지, 아니면 의존 항목이 필요한지 결정하세요.
- 비차단(Non-blocking) 방식으로 만드세요. 페이저(Pager)는
cat으로, 에디터(Editor)는 아무 작업도 하지 않도록(no-op) 설정하고, 히스토리 확장(History expansion)을 비활성화하며, 일치하지 않는 글로브(Globs) 패턴에서 에러가 발생하지 않도록 하세요. 단순히 속도뿐만 아니라, 비대화형 호출자(Non-interactive caller)를 위한 정확성을 확보해야 합니다. - IDE까지 확장하세요. 와처(Watcher)와 검색 대상에서 무거운 디렉토리를 제외하세요. 에이전트의 편집이 오래된 버퍼(Stale buffers)와 충돌하지 않도록 자동 저장(Auto-save)을 켜두세요.
핵심 요약 (The takeaway)
oh-my-zsh, 화려한 프롬프트(Fancy prompts), 그리고 자동 완성 프레임워크(Completion frameworks)는 나쁜 도구가 아니었습니다. 그것들은 그 시대에 적합했습니다 — 즉, 한 명의 인간이 하나의 장기 실행되는 터미널 앞에 앉아 있었고, 시작 비용(Startup cost)이 거의 무시할 수 있는 수준이었던 시대 말입니다. 그 시대는 끝나가고 있습니다. AI 에이전트가 병렬 스레드(Parallel threads)를 통해 수백 개의 수명이 짧은 셸(Short-lived shells)을 생성할 때, 계산 방식은 완전히 뒤집힙니다. 한 번의 편의성이 명령당 세금(Per-command tax)이 되어 여러분의 동시성(Concurrency)만큼 배수로 불어나게 됩니다.
해결책은 도구 사용 자체를 반대하는 것이 아닙니다. 여러분의 도구가 현재 실제로 누구를 위해 봉사하고 있는지를 인식하는 것입니다. 제 터미널이 7.5배 빨라진 것은 어떤 영리한 기술 덕분이 아니라, 더 이상 거의 존재하지 않는 사용자를 위해 최적화하는 것을 멈추고, 대부분의 작업을 수행하는 사용자를 위해 최적화하기 시작했기 때문입니다.
만약 어떤 프로그램이 당신의 명령어를 입력하고 있다면, 그 프로그램에 필요한 환경 — 빠르고, 논블로킹 (non-blocking)이며, 문자 그대로의 (literal) 환경 — 을 구축하십시오. 그리고 인간을 위한 편의 계층 (human-comfort layer)은 당신이 실제로 운전석에 앉아 있는 드문 순간들을 위해서만 남겨두십시오.
에이전트 워크플로 (agent workflow)를 위해 쉘 (shell)을 비워보셨나요? zprof에서 가장 큰 원인을 차지했던 것은 무엇이었나요? 여러분의 전후 수치를 댓글로 남겨주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기