
나의 코딩 에이전트(Coding Agents)를 위한 로컬 컨트롤 플레인(Local Control Plane) 구축기
요약
다양한 코딩 에이전트(Copilot, Claude Code, Codex 등)를 사용할 때 발생하는 설정 파편화 문제를 해결하기 위한 로컬 컨트롤 플레인 구축 경험을 다룹니다. 각 도구마다 제각각인 설정, MCP, 메모리, 스킬 폴더를 통합하여 공유된 컨텍스트를 유지하는 방법을 모색합니다.
핵심 포인트
- 다양한 AI 에이전트 사용 시 발생하는 설정 및 환경의 파편화 문제 지적
- 도구별로 상이한 MCP 설정, 스킬 폴더, 메모리 관리의 불편함 설명
- 단순 파일 공유를 넘어선 통합된 공유 컨텍스트(Shared Context)의 필요성 강조
- 규칙, 권한, 기술, 메모리를 한 곳에서 관리하는 로컬 컨트롤 플레인 지향
한동안 저의 로컬 AI 설정을 가지고 이것저것 시도해 왔는데, 그 시간의 대부분 동안 다섯 개의 터미널을 열고 폴더를 가리키며 설명하지 않고서는 상황을 설명하기가 매우 어려웠습니다.
도구 자체는 괜찮았습니다. 그것이 문제는 아니었습니다.
저는 Copilot을 사용했습니다. Claude Code를 사용했습니다. Codex를 사용했습니다. OpenCode도 시도해 보았습니다. 때로는 이미 구독 중인 Claude를 원했고, 때로는 워크플로우(Workflow)가 마음에 드는 Codex를 원했습니다. 때로는 OpenRouter를 통해 모델을 전환하는 것이 더 쉬운 OpenCode를 원하기도 했습니다.
짜증 나는 부분은 에이전트(Agents)를 둘러싼 모든 환경이었습니다.
모든 도구는 각자의 홈 디렉토리(Home directory), 각자의 설정 형식(Settings format), 각자의 MCP 설정(MCP config), 각자의 스킬 폴더(Skills folder), 각자의 메모리 스토리(Memory story)를 가지고 있었습니다. 그래서 유용한 MCP를 추가하거나 좋은 규칙(Rule)을 작성할 때마다, 저는 다시 그 바보 같은 질문을 던져야 했습니다. "이 도구는 이것을 어디에 두기를 기대하는가?"
이런 일이 반복되자, 설정은 더 이상 "강력한 로컬 AI 스택 (Local AI stack)"처럼 느껴지지 않고, 채팅창이 붙어 있는 중복된 도트파일(Dotfiles) 뭉치처럼 느껴지기 시작했습니다.
원래의 난장판
정리하기 전의 대략적인 모습은 다음과 같았습니다:
저의 첫 번째 임시방편은 마크다운(Markdown)이었습니다.
한 에이전트에게 기능 계획을 세우게 한 뒤, 그 계획을 .md 파일로 저장하고, 그 파일을 다른 에이전트에게 전달하여 구현하게 하는 방식이었습니다. 채팅 기록(Chat history)에 의존하는 것보다는 나았습니다. 적어도 계획이 하나의 세션 외부에 존재했으니까요.
하지만 그것이 문제를 진정으로 해결해주지는 못했습니다.
두 번째 에이전트가 계획을 읽고, 어떤 가정을 놓치거나, 다른 트레이드오프(Tradeoff)를 선택하여 약간 다른 결과물을 만들어냈습니다. 그러면 저는 다시 첫 번째 에이전트에게 돌아가 검토를 요청하게 되는데, 구현된 내용이 원래 계획했던 것과 다르기 때문에 에이전트는 혼란에 빠지게 됩니다.
네, 파일이 도움이 되긴 했습니다. 하지만 그것은 공유된 컨텍스트(Shared context)는 아니었습니다.
제가 원했던 것은 훨씬 덜 극적인 것이었습니다:
- 상위 수준의 규칙(high-level rules)을 한 번만 작성
- 권한 규칙(permission rules)을 한 곳에 유지
- 기술(skills)을 수동으로 복사하는 작업 중단
- 동일한 MCP를 반복해서 선언하는 작업 중단
- 프로젝트와 세션을 가로질러 유지되는 메모리(memory) 확보
- 홈 디렉토리(home directory)를 읽기 쉬운 상태로 유지
마지막 항목은 그 반대의 상황을 겪어보기 전까지는 단순히 미관상의 문제처럼 들립니다. 서로 부분적으로 겹치는 AI 폴더들로 가득 찬 홈 디렉토리는 설정(setup)이 아닙니다. 그것은 잡동사니 서랍(junk drawer)입니다.
메모리의 늪 (The memory rabbit hole)
제가 메모리부터 시작한 이유는 그것이 제가 명확히 정의할 수 있는 고통이었기 때문입니다.
Claude 전용 메모리는 유용했지만, 여전히 Claude 전용이었습니다. MCP를 통한 AgentMemory는 작동했지만, 제가 필요로 하는 것보다 더 무겁게 느껴졌습니다. 너무 많은 가동 부품(moving parts)과, 아마도 제가 한 번도 건드리지 않을 너무 많은 기능들이 있었습니다.
Headroom은 구조의 형태가 머릿속에서 딱 맞아떨어지게 만든 첫 번째 요소였습니다.
유용한 부분은 단지 "메모리"뿐만이 아니었습니다. 그것은 제가 정신적으로 혼동하고 있었던 두 가지 작업 사이의 분리였습니다:
- 압축(compaction) 및 라우팅(routing): 모델 요청이 제공자(provider)에 도달하기 전에 로컬 프록시(local proxy)를 거칩니다.
- 메모리(memory): 오래 지속되는 사실과 결정 사항들이 로컬 저장소(local store)에 저장되며 MCP를 통해 접근됩니다.
이 둘은 동일한 것이어서는 안 됩니다.
제 설정에서 Headroom의 프록시는 127.0.0.1:8787에서 대기합니다. 모델 트래픽은 이를 통해 전달될 수 있습니다. 메모리 MCP는 공유된 SQLite 데이터베이스와 통신합니다. 동일한 프로젝트 내의 관련 요소들이지만, 책임(responsibilities)은 다릅니다.
이 두 가지 개념을 분리하고 나니, 나머지 아키텍처(architecture)를 추론하기가 더 쉬워졌습니다.
현재 폴더 레이아웃 (The current folder layout)
중심은 이제 ~/.ai입니다.
~/.ai가 마법 같아서가 아닙니다. 그저 도구들이 가리킬 수 있는 단 한 곳이 필요했을 뿐입니다.
[

예전 스타일의 점폴더(dotfolders) 대부분은 이제 심볼릭 링크(symlinks)입니다. 도구들은 여전히 예상하는 항목들을 찾을 수 있지만, 실제 구조는 ~/.ai 아래에 있습니다.
제 셸(shell)은 이를 명시적으로 보여줍니다:
export OPENCODE_CONFIG_DIR="$HOME/.ai/opencode"
export CLAUDE_CONFIG_DIR="$HOME/.ai/claude"
export CODEX_HOME="$HOME/.ai/codex"
...
Small Codex 각주: 저는 ~/.ai/codex와 ~/.ai/codex-desktop을 모두 가지고 있습니다. 이는 의도된 것입니다. 데스크톱 앱(desktop app)과 CLI/런타임(runtime) 상태는 정확히 같은 것이 아닙니다. 저는 여전히 이 둘을 같은 지붕 아래 두고 싶습니다.
이것은 완벽한 추상화(abstraction)는 아닙니다. 그저 제가 기억하기 쉬운 디렉터리 구조일 뿐입니다.
OpenCode가 나의 주요 진입점이 되다
이제 OpenCode는 제가 일반적인 CLI 코딩 세션을 시작하고 싶을 때 주로 사용하는 곳입니다.
그 이유는 프로바이더(provider) 전환 때문입니다. OpenRouter를 백엔드로 사용함으로써, 나머지 설정을 다시 구축할 필요 없이 모델 간을 이동할 수 있습니다. 어떤 작업은 더 강력한 모델이 필요하고, 어떤 작업은 더 저렴한 모델로도 충분합니다. 어떤 날은 특정 프로바이더가 그냥 느리기도 합니다.
설정(config)에 있는 정확한 모델은 핵심이 아닙니다. 구조는 다음과 같습니다:
{
"instructions": [
"~/.ai/instructions.md",
...
OpenCode는 ~/.ai에서 공유된 지침(instruction) 파일들을 읽어옵니다. OpenCode의 OpenRouter 트래픽은 Headroom을 통해 전달됩니다.
MCP 목록도 그곳에 있습니다. 현재 제 목록에는 Headroom, Headroom memory, Firecrawl, Tavily, Deepgram docs, Sentrux, 그리고 Context7이 포함되어 있습니다. 이 목록은 변경될 것입니다. 중요한 점은 제가 이제 MCP를 특정 도구 하나를 위해 설치하고 잊어버리는 무작위 플러그인이 아니라, 제 로컬 에이전트 환경(local agent environment)의 일부로 취급한다는 것입니다.
런타임 흐름 (Runtime flow)
이 부분은 제가 좀 더 일찍 그렸더라면 좋았을 부분입니다:
두 가지 경로가 있습니다.
모델 호출(Model calls)은 프록시(proxy)를 통해 전달됩니다. 메모리(Memory)는 메모리 MCP를 통해 전달됩니다.
제가 이 말을 반복하는 이유는 매우 특정한 종류의 디버깅 고통을 방지하기 위해서입니다. 만약 모델 호출이 이상하게 동작한다면, 저는 프록시 경로를 확인합니다. 만약 에이전트가 무언가를 잊어버렸다면, 저는 메모리 경로를 확인합니다. 질문이 다르면, 조사해야 할 곳도 다릅니다.
의도적으로 지루하게 설계된 메모리 규칙 (Memory rules)
공유 메모리(Shared memory)는 headroom-memory를 통해 노출되는 로컬 SQLite 데이터베이스입니다.
저는 모든 것을 저장하고 싶지 않습니다. 그렇게 되면 메모리는 그저 또 다른 로그 더미(log pile)가 될 뿐입니다. 저는 다시 찾아내는 데 비용이 많이 드는 것들을 기억하기를 원합니다:
- 프로젝트 결정 사항 (project decisions)
- 아키텍처 노트 (architecture notes)
- 워크플로우 선호도 (workflow preferences)
- 반복되는 버그 (recurring bugs)
- 이전 세션에서의 교훈 (lessons from previous sessions)
저의 워크플로우를 위해 저는 두 가지 명령어를 사용합니다:
/remember: 무언가가 향후 세션에서도 유지되어야 할 때/recall: 에이전트가 이전 작업의 컨텍스트(context)가 필요할 때
유용한 메모리의 예시:
- "이 프로젝트는 Info.plist를 직접 수정하는 대신 빌드 스크립트를 사용합니다."
- "실제 iOS 빌드가 실행될 때까지 SourceKit 진단 결과를 신뢰하지 마세요."
- "VPN 프로젝트는 공유 Headroom DB 아래에 메모리를 저장합니다."
- "이 리포지토리(repo)는 기능 브랜치(feature branches)와 conventional commits를 선호합니다."
또한 저는 비밀(secrets), 자격 증명(credentials), 원시 로그(raw logs) 또는 개인적인 민감 데이터를 저장하는 것에 대해 엄격한 규칙을 가지고 있습니다. 메모리는 향후 작업을 더 쉽게 만들어야 합니다. 부채(liability)가 되어서는 안 됩니다.
권한(Permissions)은 장식이 아닙니다
예전에는 권한을 안전을 위한 체크박스 정도로 생각했습니다. 하지만 이제는 권한을 에이전트 환경(agent environment)의 일부로 생각합니다.
만약 Claude Code, Codex, OpenCode가 무엇을 읽거나 실행할 수 있는지에 대해 서로 의견이 다르다면, 에이전트를 전환하는 것만으로도 작업의 동작이 변하게 됩니다. 이는 나중에 문제를 일으키는 바로 그 유형의 보이지 않는 차이입니다.
그래서 저는 공통 정책을 공유 파일로 옮겼습니다:
~/.ai/rules.md: 코딩 및 워크플로우 표준용~/.ai/security.md: 금지된 명령어 및 경로용~/.ai/instructions.md: 메모리 동작 및 에이전트 간 노트용
보안 파일은 의도적으로 단순하게 작성되었습니다:
## denied paths (금지된 경로)
- ~/.ssh/**
- ~/.aws/**
...
각 도구는 여전히 자신만의 설정 형식을 가지고 있습니다. 저는 그것을 없애려고 시도하는 것이 아닙니다. 단지 오늘 어떤 에이전트가 더 위험한지 기억할 필요가 없을 정도로 정책들이 충분히 유사하게(rhyme) 맞물리기를 바랄 뿐입니다.
아직 미흡한 점
메모리 여유 공간(Headroom memory)을 위한 UI가 필요합니다.
CLI는 메모리를 나열하는 데 적합하며, 저는 아마 대부분의 사람들보다 CLI를 더 선호할 것입니다. 그럼에도 불구하고, 메모리를 검사하고, 문구를 수정하고, 중복된 항목을 병합하며, 거기에 있어서는 안 될 것들을 삭제할 수 있는 작은 브라우저 뷰(browser view)를 원합니다.
일부 설정은 공유되는 것을 거부하기도 하는데, 그것은 괜찮습니다. Claude Code는 Claude 전용 플러그인 설정이 있습니다. Codex는 데스크톱 런타임 상태(desktop runtime state)를 가집니다. OpenCode는 프로바이더(provider) 및 TUI 설정을 가지고 있습니다.
저는 그것들을 하나의 형태로 강제하려는 시도를 그만두었습니다.
유용한 경계는 간단합니다: 공유되는 것들은 로컬 AI 홈(local AI home)으로 가고, 도구별 특화된 것들은 해당 도구에 머뭅니다.
현재는 로컬 모델을 사용 중
저는 로컬 코딩 모델(local coding models)도 이 이야기의 일부로 만들려고 시도했습니다.
제 MacBook은 16GB 메모리를 가진 구형 Apple Silicon 기기입니다. 5B(50억 파라미터) 범위 정도의 작은 코딩 모델들은 실행할 수 있지만, 아직 그 모델들로부터 충분한 가치를 얻지는 못하고 있습니다.
제가 실제로 중요하게 생각하는 작업들을 위해서는, 이 하드웨어가 편안하게 제공할 수 있는 수준보다 더 강력한 추론(reasoning), 더 나은 코드베이스 탐색(codebase navigation), 그리고 더 신뢰할 수 있는 지시 이행(instruction following) 능력이 여전히 필요합니다.
따라서 제 설정은 "오프라인 AI"가 아닙니다. 그것은 원격 모델 실행(remote model execution)을 동반한 로컬 오케스트레이션(local orchestration)입니다.
이것이 덜 낭만적일지는 몰라도, 더 정확한 표현입니다.
내가 도달한 지점
현재 이 설정은 저에게 부족했던 것을 제공해 줍니다:
-
하나의 로컬 AI 디렉토리 (local AI directory)
-
공유된 규칙 (shared rules)
-
공유된 보안 정책 (shared security policy)
-
중복 제거된 기술 (deduplicated skills)
-
로컬 인프라로 취급되는 MCP (MCPs treated as local infrastructure)
-
로컬 프록시 (local proxy)를 통해 라우팅되는 모델 트래픽
-
에이전트, 프로젝트, 세션 간에 공유되는 메모리 (memory)
-
$HOME디렉토리 내의 무분별한 점 폴더 (dotfolders) 감소
제가 가장 중요하게 생각하는 부분은 연속성 (continuity)입니다.
저는 채팅 기록 (chat transcript)만으로 충분하다고 가식적으로 굴지 않고도, 한 도구에서 계획을 세우고 다른 도구에서 구현할 수 있습니다. 모델의 유연성이 필요할 때는 OpenCode를 사용할 수 있습니다. 구독 워크플로우가 더 편리할 때는 여전히 Claude Code를 사용할 수 있습니다. Codex를 별개의 세계로 취급하지 않고도 사용할 수 있습니다.
이것은 다른 에이전트들을 대체하기 위한 단 하나의 에이전트를 만드는 것이 아닙니다.
에이전트들이 충분한 컨텍스트 (context)를 공유하여, 에이전트 사이를 전환할 때 처음부터 다시 시작하는 느낌이 들지 않도록 하는 로컬 설정입니다.
요약 버전
마이그레이션 과정은 다음과 같습니다:
저는 단 하나의 완벽한 코딩 에이전트를 선택할 필요가 없었습니다.
그 대신, 그들 모두가 무엇을 공유해야 하는지를 결정해야 했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

