Agent UI 공통 패키지
Agent 제품마다 반복하던 입력·실행·결과 UI를 npm 패키지로 묶어 공통 영역의 약 60%를 재사용했습니다.
배경
Agent마다 입력과 결과 형식은 달랐지만, 사용자가 입력하고 실행한 뒤 결과를 확인하는 흐름은 같았습니다. 화면을 복사해서 새 제품을 만들면 공통 동작이 바뀔 때마다 여러 저장소를 함께 수정해야 했습니다.
문제
- 입력 UI와 실행 상태, 결과 화면을 제품마다 다시 구현했습니다.
- 에러 처리 기준이 바뀌면 여러 저장소에 같은 수정을 반복했습니다.
- 새 제품을 시작할 때마다 상태와 화면 구조가 조금씩 달라졌습니다.
선택
화면 일부만 공통 컴포넌트로 만드는 대신, 컴포넌트와 Hook, 상태 규약을 함께 npm 패키지로 배포했습니다. 공통 실행 흐름은 한 곳에서 고치되 고객사별 입력과 결과 형식은 각 앱이 소유하도록 경계를 정했습니다.
구현
반복되던 동작을 @ara/agent-ui 패키지로 분리했습니다. 제조 MLOps, 유통, 의료 등 세 도메인에 걸친 Agent 제품 4종은 이 패키지를 설치하고 도메인 전용 UI만 추가합니다.
flowchart TB pkg["@ara/agent-ui npm"] subgraph package [Published package] common[Common UI] core[Hooks + Zustand stores] parsers[Stream parsers + query keys] end pkg --> package package --> mlops[제조 MLOps] package --> retail[유통] package --> medical[의료]
공통 흐름과 레이어
flowchart LR subgraph flow [공통 사용자 흐름] in[입력] --> run[실행] run --> out[결과 동기화] end subgraph pkg [npm package] L1[Common] L2[Execution hooks] L3[State stores] end subgraph app [Consumer app] D[Domain UI only] end pkg --> app flow -.-> pkg
실행 상태와 도메인 확장
패키지는 idle, running, success, error 상태를 공통으로 관리합니다. SSE, WebSocket, polling 같은 전송 방식과 결과 화면은 제품별 요구에 맞게 앱에서 구현합니다.
flowchart TB state["공통 상태: idle → running → success / error"] state --> ml[제조 MLOps] state --> rt[유통] state --> md[의료] ml --> ml_run["SSE + Workflow 폴링"] ml --> ml_out["실행 큐 · 로그"] rt --> rt_run["SSE parser"] rt --> rt_out["매칭 · 다운로드"] md --> md_run["WebSocket partial"] md --> md_out["Question · Report"]
| 도메인 | 패키지에서 가져옴 | 앱에서만 구현 |
|---|---|---|
| 제조 MLOps | Common, execution store, SSE Hook | Workflow 노드 입력, 큐와 로그 패널 |
| 유통 | stream store, SSE parser, Common | 엑셀 업로드, 매칭 결과 버블 |
| 의료 | stream store, agent parser, Common | 선택지 UI, 리포트와 지식 그래프 |
공통 UI와 store, 실행 Hook, parser를 패키지에서 가져오는 비율을 기준으로 재사용 영역을 약 60%로 추정했습니다. API schema와 event type, 결과 렌더링, 인증, routing은 고객사 요구가 달라 각 앱에 남겼습니다.
결과
- Agent 제품 4종에 공통 패키지를 적용했습니다.
- 공통 영역의 약 60%를 재사용했습니다. 패키지와 앱 전용 화면의 비율을 기준으로 한 추정치입니다.
- 공통 동작은 패키지 한 곳에서 수정하고 버전으로 배포할 수 있게 됐습니다.
AI 코딩 도구 활용
Codex에는 기존 상태 흐름과 제품별 변경 지점, 공통화하지 않을 API 경계를 먼저 제공했습니다. 생성된 변경은 바로 병합하지 않고 다음 항목을 확인했습니다.
- 컴포넌트와 Hook의 책임이 기존 규약과 맞는지 diff에서 확인
- TypeScript 타입과 테스트로 계약 변경 확인
- 각 앱에서 입력, 실행, 결과, 에러 흐름을 브라우저로 확인
- 도메인 schema나 상태 소유권까지 패키지로 옮긴 제안은 직접 수정
반복 코드를 정리하고 변경 후보를 만드는 데 AI를 사용했지만, 패키지 경계와 실제 채택 여부는 제품 요구를 기준으로 판단했습니다.
다음 단계
- semver와 CHANGELOG 기준을 정리해 앱 사이의 의존성 충돌을 줄입니다.
- breaking change가 있을 때 패키지 README에 migration guide를 함께 제공합니다.