5.6 KiB
5.6 KiB
에이전트 횡단 오버사이트 타임라인 — 설계
작성일: 2026-06-11
대상 repo: web-ui (프론트엔드)
연관 백엔드: ✅ 완료 (GET /api/agent-office/activity 필터 지원, main 2c2828c)
배경 / 목적
3개 자율 에이전트(stock 보유종목·insta 발급·lotto 진화)가 모두 도는 상태에서 "팀이 무엇을·언제·왜 했나"를 한 화면에서 보는 에이전트 횡단 오버사이트(CEO 가시화) 기능.
현재 web-ui에는 /lotto/evolver 탭의 lotto 전용 LottoActivityTimeline만 존재.
통합 /activity(전 에이전트 대상)를 소비하는 횡단 뷰가 없다.
백엔드 응답 shape (라이브 검증 완료)
GET /api/agent-office/activity?agent_id=&type=task|log&status=&days=&limit=&offset=
→ { items: [...], total: N }
- task item:
{ type:'task', agent_id, task_id, message, created_at, task_type, status, completed_at, duration_seconds } - log item:
{ type:'log', agent_id, task_id, message, created_at, level } status는 task 전용(type=log에 주면 무시). injection 안전(? 바인딩 + 브랜치 선택).
검증 메모:
- 무필터
total이 65,599건 → 기본days=7필터 필수(task 기준 110건으로 감소). requires_approval필드는 존재하지 않음 →status:'pending'을 진행/대기 강조로 처리.agent_id값이AGENT_META키(stock/music/insta/realestate/lotto)와 일치 → 색상/이미지 재사용.
아키텍처
AgentOffice는 단일 화면(TopBar + 3×3 AgentGrid + 우측 패널) 구조.
우측 패널은 selectedAgent 상태로 분기:
null→ (기존)EmptyDetailPanel variant="initial"→ActivityTimeline으로 교체placeholder-N→EmptyDetailPanel variant="placeholder"(유지)- active agent id →
SidePanel(유지)
즉 에이전트 미선택 시 기본 우측 패널이 횡단 타임라인이 되고, 그리드와 항상 동시 노출. 항목/그리드 클릭으로 해당 에이전트 SidePanel로 전환.
신규/변경 파일
| 파일 | 역할 |
|---|---|
src/api.js |
agentActivity({agent_id,type,status,days,limit,offset}) 추가 — 빈 값 제외 쿼리스트링 빌드 + GET /api/agent-office/activity |
src/pages/agent-office/AgentOffice.jsx |
selectedAgent===null 분기를 EmptyDetailPanel → ActivityTimeline(props: refreshTrigger, onSelectAgent)로 교체 |
src/pages/agent-office/hooks/useActivityFeed.js |
items/offset/total/hasMore/loading/error/filters 상태 관리 |
src/pages/agent-office/components/ActivityTimeline.jsx |
컨테이너: 헤더 + ActivityFilters + 리스트 + 무한스크롤 sentinel + 상태(loading/empty/error/end) |
src/pages/agent-office/components/ActivityFilters.jsx |
필터 4종(agent 색칩 / type / status / days). type==='log'일 때 status 비활성 |
src/pages/agent-office/components/ActivityItem.jsx |
한 행: agent 색·이미지 + message + 상태/level 뱃지 + 상대시간 + duration. 클릭 → onSelectAgent(agent_id) |
src/pages/agent-office/AgentOffice.css |
타임라인/필터/항목 스타일 (designer 스킬로 마감) |
데이터 흐름
AgentOffice (selectedAgent===null)
└─ <ActivityTimeline refreshTrigger={refreshTrigger} onSelectAgent={handleSelectAgent} />
└─ useActivityFeed(filters)
• mount / 필터 변경 → offset=0 fetch → items 교체
• loadMore (sentinel 교차) → offset += limit → items append
• refreshTrigger 변경 → offset=0 재조회 → items 교체 (WS 실시간 연동)
└─ ActivityItem onClick → onSelectAgent(agent_id) → SidePanel로 전환
handleSelectAgent는 기존 콜백 재사용(선택 + clearNotifications).
필터 기본값
days=7, type=all, status=all, agent=all, limit=30(페이지당).
상태 / 비주얼 매핑
- task
status:succeeded→ 초록 ✓ /failed→ 빨강 ✗ /pending·working→ 앰버 펄스 ⏳(강조) - log
level:error→ ❌ /warning→ ⚠️ /info→ · - agent 색상:
AGENT_META[agent_id].color, 미지정 agent → 회색#6b7280 offset >= total→ "더 이상 활동 없음" / 무한스크롤은 IntersectionObserver
상태 처리(엣지)
- 첫 페이지 로딩 → 스피너/스켈레톤
- 빈 결과 → "최근 N일 활동 없음"
- fetch 실패 → 인라인 에러 + 재시도 버튼
- 리스트 끝 → end-of-list 표시, sentinel 관찰 중단
테스트 (TDD, vitest + RTL — 기존 패턴 따름)
useActivityFeed: 필터 변경 시 offset 리셋 + items 교체 / loadMore append / refreshTrigger 재조회 /hasMore = items.length < total계산 (api mock)ActivityItem: task vs log 렌더 분기, status/level 뱃지 클래스, 클릭 시onSelectAgent(agent_id)호출ActivityFilters:type==='log'일 때 status select 비활성, 필터 변경 시 onChange 호출
비범위 (YAGNI)
- 별도 라우트(
/agent-office/activity) 미생성 — 기본 우측 패널 통합으로 충분 - 기존
getActivityFeed(limit, offset)헬퍼는 lotto evolver 등에서 사용 여부 확인 후 유지(신규agentActivity와 공존, 무리한 통합 안 함) LottoActivityTimeline(kind/ts/payloadshape)은 다른 엔드포인트 소비 → 건드리지 않음- CSV/export, 검색어 필터 등 부가기능 제외
구현 순서
agentActivityapi 헬퍼 추가useActivityFeed훅 (TDD)ActivityItem/ActivityFilters(TDD)ActivityTimeline컨테이너 조립AgentOffice.jsx분기 교체- designer 스킬로 CSS 마감
- lint + 테스트 + 빌드 검증