HagiCode가 13개의 Agent CLI를 하나의 시스템에 통합한 방법
사실 이 일은 어렵지도 않고 쉬운 것도 아니다. 우리는 어떻게 같은 구조로 구성된 Claude Code, Codex, Copilot, Gemini 등 다양한 스타일의 Agent CLI를 하나의 시스템에서 통합하고 새로운 항목도 쉽게 추가할 수 있었는지 이야기해보자.
배경
이 이야기는 갑작스럽게 시작되었다. 우리가 겪었던 문제였다.
Agent CLI는 지난 몇 년간 급속히 등장했다. Claude Code, OpenAI Codex, GitHub Copilot, Gemini CLI, Kimi, Qoder, Kiro 등. 몇 달마다 새로운 것이 나오고 있다. 사용자가 하나의 HagiCode만 설치하면 모든 Agent를 사용할 수 있는 목표를 가지고 있기 때문에, 특정 CLI에만 의존할 수는 없었다. 하지만 각각의 CLI에 대해 설치, 상태 확인, 스케줄링과 같은 전체 로직을 따로 작성하는 것은 유지보수 측면에서 매우 곤란했고, 코드가 복잡해지고 관리하기 어려워졌다.
더욱이 각 CLI는 서로 다른 방식으로 작동한다. 일부는 stdio, 일부는 gRPC, 또 일부는 단순한 쉘 인터페이스만 제공하며 출력 형식도 모두 다르다. 비즈니스 로직 내에서 if (provider == ClaudeCode)와 같이 조건문을 사용하게 되면, 단기간 내에 불필요한 코드가 누적되어 유지보수의 문제가 생긴다. 누구나 이러한 '과거 코드'를 건드리고 싶어하지 않는다.
이러한 문제를 해결하기 위해 우리는 결정했다. 비즈니스 레이어와 실제 CLI 사이에 추상화 계층과 공유 실행 환경을 도입하는 것이다. 이 접근법은 HagiCode가 새로운 CLI를 빠르게 통합할 수 있도록 해준다. 이후 구체적인 방법을 설명하겠다.
HagiCode 소개
이 글에서 다루는 내용은 HagiCode 프로젝트에서 실제 적용한 방식이다. HagiCode는 주요 Agent CLI들을 하나의 설치 및 설정으로 통합하여 사용자에게 제공하는 AI 코드 지원 플랫폼이다.
"13개"라는 숫자는 어디서 나왔는가
질문이 자주 나오는 숫자인 "13개"에 대해 설명하자면, 이 숫자는 AIProviderType 열거형에 의해 결정된다. 아래는 원래 정의된 내용이다:
public enum AIProviderType
{
ClaudeCodeCli = 0,
CodexCli = 1,
GitHubCopilot = 2,
CodebuddyCli = 3,
OpenCodeCli = 4,
IFlowCli = 5, // 폐기됨
HermesCli = 6,
QoderCli = 7,
KiroCli = 8,
KimiCli = 9,
GeminiCli = 10,
DeepAgentsCli = 11,
ReasonixCli = 12,
PiCli = 13,
}
전체 14개의 값이 있지만, IFlowCli=5는 더 이상 사용되지 않으며 AIProviderFactory에서 명시적으로 제외되어 있다:
if (providerType == AIProviderType.IFlowCli)
{
throw new NotSupportedException("IFlowCli is no longer supported");
}
또한 IsActivelySupportedProviderType() 함수를 통해 필터링된 결과는 13개의 활성 CLI이다: Claude Code, Codex, GitHub Copilot, CodeBuddy, OpenCode, Hermes, Qoder, Kiro, Kimi, Gemini, DeepAgents, Reasonix, Pi.
이렇게 13이라는 수치가 정해진 것이다. 마케팅 수치가 아니라 코드에서 실제로 계산된 수치이다. 숫자는 거짓말을 하지 않으므로, 거짓말을 하는 것은 우리 자신뿐이다.
계층 구조: 변화를 통제하는 방법
13개의 CLI를 통합하는 핵심 전략은 다음과 같다: 비즈니스 코드가 어떤 CLI를 호출하는지 신경 쓰지 않도록 한다.
이를 위해 6개의 계층으로 구분했다:
1. 식별 계층 - AIProviderType
CLI의 "주민등록번호" 역할을 하는 열거형이다. 모든 CLI는 이 값을 사용하여 구분되며, 문자열과 열거형 간에는 ToStringValue() / ToAIProviderType() 메서드를 통해 변환한다. 간단하지만 필수적인 구성 요소이다.
2. 비즈니스 계약 계층 - IAIProvider / IAIProviderFactory
비즈니스 코드는 오직 IAIProvider 인터페이스만 인식하며, 여기에는 프롬프트 전송 및 스트림 응답 수신과 같은 일반적인 동작이 정의되어 있다. 그 뒤에 어떤 CLI인지 관심이 없다. 편지를 보내는 것처럼, 우편 배달원이 누군지 신경 쓰지 않는다.
3. 어댑터 계층 - *CliProvider
각 CLI는 해당하는 얇은 어댑터를 가지며, 예를 들어 PiCliProvider, ReasonixCliProvider, ClaudeCodeCliProvider 등이 있다. 이 어댑터는 일반적인 요청을 특정 CLI가 이해할 수 있는 형식으로 번역하고, 반대로 결과를 다시 변환하는 일을 한다. 새 CLI를 추가할 때는 기존 어댑터를 복사하여 수정하는 것으로 충분하다.
4. 공유 실행 환경 계층 - ICliProvider<TOptions>
이 계층은 HagiCode.Libs 내부에 있으며, 실제 작업을 처리하는 부분이다. 플랫폼 간 프로세스 생성, stdio 통신, 스트림 출력 파싱, 타임아웃 및 재시도 처리 등을 담당한다. 모든 어댑터가 동일한 실행 환경을 공유하므로, 새로운 CLI를 통합할 때 프로세스 관리는 거의 재작업 없이 가능하다.
예를 들면, 어댑터 계층은 "번역관", 공유 실행 환경 계층은 "택배 회사"이다. 번역관은 말만 잘하면 되고, 상품 배송은 택배 회사가 책임진다. 역할이 명확하면 세상은 깔끔해진다.
5. 팩토리 라우팅 계층 - AIProviderFactory
CreateProvider 메서드 내부의 switch 문에서 AIProviderType에 따라 적절한 어댑터를 인스턴스화하고, IsConfigured 검증도 수행한다. 이는 유일하게 실제 타입을 알고 있는 영역이며, 팩토리 내부에서만 제한적으로 관리된다. 변화는 한곳에서만 발생하도록 설계되어 있어 다른 부분은 깔끔하게 유지된다.
6. 디렉터리 / UI 프로젝션 계층 - main-professions.yaml
이 계층은 코드가 아닌 데이터이다.
주 직업 목록(예: "프론트엔드 개발자", "백엔드 개발자", "풀스택 개발자")은 main-professions.yaml 파일로 정의되며, HeroPrimaryProfessionPresetProvider를 통해 읽어 UI에 표시된다. 새로운 주 직업을 추가할 때 코드를 수정할 필요 없이 YAML만 변경하면 된다. 코드보다 데이터를 활용하면 유지보수에 큰 도움이 된다.
참고로, 이 부분은 HagiCode 리팩토링에서 가장 큰 변화였다. 초기 버전에는
AgentCliInstallRegistry라는 코드 내 등록 구조가 있었지만, 유지보수 비용이 너무 컸기 때문에 데이터 기반으로 변경되었다. 이것이 HagiCode가 주 직업을 쉽게 확장할 수 있는 이유이다.
설치 문제 해결
13개의 CLI 모두 설치해야 하며, 공식 설치 방식도 모두 다르다. 이 또한 큰 문제였다.
우리는 Docker Compose를 이용한 사전 설치 + 외부 관리 백업 방식을 선택했다. 이미지 내에 주요 CLI(Claude Code, Codex, Copilot, CodeBuddy, OpenCode, Qoder, Kiro, Kimi, Gemini, Pi)를 미리 설치하여 사용자가 이미지를 받기만 하면 바로 사용할 수 있게 했다. 설치 후에는 기분이 좋다.
로컬 환경에 직접 설치해야 하는 경우, 설치 명령어는 다음과 같다 (공식 문서 확인):
| CLI | 공식 설치 명령어 |
|---|---|
| Claude Code | npm install -g @anthropic-ai/claude-code |
| Codex | npm install -g @openai/codex |
| GitHub Copilot | npm install -g @github/copilot |
| CodeBuddy | npm install -g @tencent-ai/codebuddy-code |
| OpenCode | npm i -g opencode-ai@latest |
| Qoder | npm install -g @qoder-ai/qodercli |
| Kiro | curl -fsSL https://cli.kiro.dev/install | bash |
| Kimi | curl -LsSf https://code.kimi.com/install.sh | bash |
| Gemini | npm |
| Hermes | 공식 스크립트, docs-only 백업 |
| DeepAgents / Reasonix | 공식 문서 참조 |
프론트엔드의 PrimaryProfessionCard.tsx도 변경되었다. 더 이상 "CLI 설치" 버튼이 없고, 대신 CLI의 사용 가능 여부, 버전 감지 결과, 그리고 "외부 관리" 안내가 표시된다. 즉, 설치 성공 여부는 시스템 레벨에서 처리하고 UI는 상태를 그대로 보여주는 것이다. 상태와 로직이 분리되어 있으면 불일치가 생길 수 있으므로, 하나의 책임만 지도록 설계했다.
새로운 CLI 추가 방법
실제로 HagiCode에 새로운 CLI를 추가하는 과정은 다음과 같다:
AIProviderType에 새로운 열거형 값 추가- 기존
*CliProvider를 복사하여 새 CLI의 파라미터 및 출력 파싱 로직 수정 AIProviderFactory의switch문에 라우팅 추가- 주 직업 목록에 추가하고 싶다면
main-professions.yaml에 설정 - 이미지에 설치 명령어 추가 (또는 외부 관리 방식으로 대체)
이 과정은 전체 코드 변경량이 200줄 미만으로, 추상화의 진정한 가치를 보여준다. 하나의 CLI를 추가할 때마다 추가 비용은 매우 낮으며, 비즈니스 코드는 전혀 변경되지 않는다. 모든 길은 로마로 통한다. 다만, 우리 길은 조금 더 편하다.
결론
"13개의 CLI를 통합하는 것"은 처음에는 어렵게 느껴졌지만, 두 가지 핵심 요소로 나눌 수 있다:
첫째는 변화를 격리하는 것 - AIProviderType 열거형 + IAIProvider 계약 + 얇은 어댑터 + 공유 실행 환경을 통해 비즈니스 코드와 실제 CLI를 분리한다. 둘째는 구성을 데이터화하는 것 - main-professions.yaml을 통해 디렉터리 및 UI를 구성하고, 코드 변경 없이 새로운 항목을 추가할 수 있도록 한다.
이 방식은 HagiCode 개발 과정에서 여러 차례 실패와 개선을 거쳐 안정화된 것이다. 비슷한 "다중 Provider 통합" 시스템을 구축 중이라면 이 계층 구조 방식을 참고할 수 있을 것이다. 왜냐하면 Agent CLI는 앞으로도 계속 등장할 것이기 때문에, 새로운 CLI를 빠르게 통합할 수 있는 아키텍처가 "현재 몇 개를 지원하는가"보다 중요하기 때문이다.
원문 및 저작권 안내
본 글은 인공지능이 협력한 결과물이며, 최종 내용은 작성자가 검토 및 확인했습니다.
- 작성자: newbe36524
- 원문 링크: https://docs.hagicode.com/go?platform=cnblogs&target=%2Fblog%2F2026-06-22-hagicode-13-agent-cli-integration-architecture%2F
- 저작권: 본 블로그의 모든 글은 특별한 표시가 없는 한 BY-NC-SA 라이선스에 따라 사용 가능합니다. 출처를 밝혀주시면 감사하겠습니다.