Files
web-page-backend/docs/superpowers/specs/2026-04-27-agent-office-v2-design.md
gahusb c168656fe1 docs: Agent Office v2 pixel office UX 대규모 업데이트 설계 스펙
전체 화면 캔버스 중심 UX, BFS 배회, 3테마, 사이드 패널 4탭 구조 설계.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 08:13:04 +09:00

498 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent Office v2 — Pixel Office UX 대규모 업데이트 설계
> 참고 프로젝트: `pixel-agents` (VS Code 확장, React 19 + Canvas 2D)
> 대상: `web-ui/src/pages/agent-office/` (프론트엔드) + `web-backend/agent-office/` (백엔드)
---
## 1. 목표
기존 대시보드 칼럼 중심 UI를 **전체 화면 픽셀 오피스** 중심으로 전환하여, "가상 오피스를 사용한다"는 몰입감을 제공한다.
### 핵심 변경
- 캔버스가 메인 화면을 차지하고, 에이전트 클릭 시 사이드 패널로 상세 정보 표시
- BFS 경로 탐색 + 풀 배회 시스템으로 에이전트에 생동감 부여
- 3가지 오피스 테마 프리셋 (Modern / Retro / Minimal)
- 캐릭터 프로시저럴 고도화 + 스프라이트 로더 설계 (점진적 전환)
### 변경하지 않는 것
- 백엔드 FSM 5상태 (`idle`, `working`, `waiting`, `reporting`, `break`)
- WebSocket 프로토콜 메시지 타입 (init, agent_state, task_complete, agent_move, notification, command_result)
- REST API 엔드포인트
- 텔레그램 봇 연동
---
## 2. 화면 구성
### 2.1 데스크톱 레이아웃
```
┌──────────────────────────────────────────────────┬──────────────┐
│ [Agent Office] ● Connected [Theme ▾] [Zoom] │ │
├──────────────────────────────────────────────────┤ Side Panel │
│ │ 320px │
│ │ │
│ Pixel Office Canvas │ [Agent hdr] │
│ (flex: 1, 전체 높이) │ [Tabs····] │
│ │ [Content ] │
│ - 에이전트 클릭 → 패널 열림 │ [·········] │
│ - 빈 공간 클릭 → 패널 닫힘 │ │
│ │ │
└──────────────────────────────────────────────────┴──────────────┘
```
- **상단 바**: 타이틀, WebSocket 연결 상태(●), 테마 드롭다운, 줌 컨트롤 (1x~4x)
- **캔버스**: `flex: 1`로 남은 공간 전체 차지, `imageSmoothingEnabled = false`
- **사이드 패널**: 320px 고정폭, 에이전트 클릭 시 슬라이드 인, X 버튼 또는 빈 공간 클릭으로 닫힘
- **패널 닫힘 시**: 캔버스가 전체 너비로 확장
### 2.2 모바일 레이아웃 (< 768px)
```
┌──────────────────────────┐
│ [≡] Agent Office ● Conn │
├──────────────────────────┤
│ │
│ Pixel Office Canvas │
│ (전체 화면) │
│ 핀치 줌 + 패닝 │
│ │
│ │
├──────────────────────────┤ ← 바텀 시트 (드래그)
│ [Agent Header] │
│ [Tabs: Cmd|Task|Tok|Log]│
│ [Content area] │
└──────────────────────────┘
```
- 캔버스: 전체 화면, 터치 핀치 줌/패닝
- 사이드 패널 → 바텀 시트 (에이전트 탭 시 올라옴, 아래로 드래그 시 닫힘)
- 상단 바: 햄버거 메뉴로 테마/줌 접기
---
## 3. 사이드 패널 구조
### 3.1 헤더
```
┌─────────────────────────────┐
│ [🎵 32x32] 음악 프로듀서 │
│ ● working - ... │
└─────────────────────────────┘
```
- 에이전트 아이콘 (emoji 기반, 32x32 색상 배경)
- display_name + 현재 상태 + state_detail
### 3.2 탭 구성
| 탭 | 내용 |
|----|------|
| **Commands** (기본) | Quick Action 버튼 (에이전트별 고유), Custom Command 입력, Approval UI (waiting 상태 시) |
| **Tasks** | 최근 작업 이력 (상태 배지, 타임스탬프, 결과 펼치기) |
| **Tokens** | 일간/주간 토큰 사용량 차트, 캐시 히트율 |
| **Logs** | 에이전트 로그 스트림 (level별 색상, 자동 스크롤) |
### 3.3 에이전트별 Quick Actions
| 에이전트 | 버튼 |
|---------|------|
| Stock | Fetch News, Add Alert, Test Telegram |
| Music | Compose, Check Credits |
| Blog | Research, Add Keyword, List Keywords |
| Realestate | Fetch Matches, Dashboard |
| Lotto | Curate Now, Status |
---
## 4. 캔버스 엔진
### 4.1 타일맵
- **그리드**: 32 × 20 타일 (기존 20×14에서 확장)
- **타일 크기**: 32px × 32px (기본), 줌에 따라 스케일
- **타일 타입**: VOID(0), FLOOR(1), WALL(2), FURNITURE(3)
- **렌더링 순서**: 바닥 → 벽 → 가구 → 에이전트 (Y좌표 Z-sorting) → 오버레이
### 4.2 오피스 레이아웃 (고정)
```
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW (W=Wall)
W..............................W
W...[Stock]...[Music]..........W
W...desk+mon..desk+inst........W
W..............................W
W...[Blog]....[RE]....[Lotto]..W
W...desk+mon..desk+mon.desk+monW
W..............................W
W..............................W
W..........[Meeting]...........W
W..........table 4x2...........W
W..............................W
W..............................W
W....[Coffee]...[Sofa]........W
W....machine....couch.........W
W..............................W
W...[Plants]......[Bookshelf]..W
W..............................W
W..............................W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
```
- 각 에이전트 구역에 테마별 소품 (Stock: 모니터 3대, Music: 악기, Blog: 서류 등)
- 중앙: 회의 테이블 (4x2 타일)
- 하단: 휴게실 구역 (커피 머신 + 소파)
- waypoint 정의: `desk_stock`, `desk_music`, `desk_blog`, `desk_realestate`, `desk_lotto`, `meeting`, `break_room`, `coffee`
### 4.3 줌 & 패닝
- 줌 레벨: 1x, 2x, 3x, 4x (정수 배율만, 픽셀 선명도 유지)
- 데스크톱: 마우스 휠 줌, 드래그 패닝
- 모바일: 핀치 줌, 터치 패닝
- 기본값: 캔버스 크기에 맞춰 자동 fit
### 4.4 게임 루프
```javascript
function gameLoop(timestamp) {
const dt = (timestamp - lastTime) / 1000;
lastTime = timestamp;
update(dt); // 에이전트 이동, 애니메이션 프레임 업데이트
render(); // 타일맵 → 가구 → 에이전트(Y-sort) → 오버레이
requestAnimationFrame(gameLoop);
}
```
- 60fps requestAnimationFrame
- `imageSmoothingEnabled = false` (픽셀 선명도)
- devicePixelRatio 반영
---
## 5. 에이전트 캐릭터 시스템
### 5.1 프로시저럴 렌더링 (Phase 1)
- 해상도: 16 × 32px (기존 8×16에서 2배 확대)
- 에이전트별 고유 색상 (기존 유지)
- 애니메이션 프레임:
| 상태 | 프레임 수 | 속도 | 설명 |
|------|----------|------|------|
| idle | 2 | 0.8s/frame | 미세 움직임 (숨쉬기) |
| walk | 4 | 0.15s/frame | 걷기 사이클 [0,1,2,1] |
| type | 2 | 0.3s/frame | 타이핑 (팔 움직임) |
| wait | 2 | 0.5s/frame | 좌우 흔들림 (wobble) |
| break | 2 | 1.0s/frame | 커피 마시기 / 졸기 |
- 4방향 스프라이트: DOWN, UP, RIGHT, LEFT (LEFT = RIGHT 좌우반전)
### 5.2 스프라이트 로더 (Phase 2 준비)
```javascript
class SpriteLoader {
constructor() {
this.sprites = new Map(); // agent_id → spritesheet Image
this.fallback = 'procedural';
}
async load(agentId, sheetUrl) { /* PNG 로드 */ }
draw(ctx, agentId, state, direction, frame, x, y) {
if (this.sprites.has(agentId)) {
// 스프라이트시트에서 프레임 추출하여 그리기
} else {
// 프로시저럴 폴백
ProceduralSprite.draw(ctx, agentId, state, direction, frame, x, y);
}
}
}
```
- 스프라이트시트 규격: 각 프레임 16×32px, 가로로 프레임 나열
- 행: 방향 (DOWN/UP/RIGHT), 열: 상태별 프레임
- PNG 없으면 프로시저럴 폴백 → 에셋 제작 전에도 완전 동작
---
## 6. 이동 시스템
### 6.1 BFS 경로 탐색
```javascript
function findPath(grid, start, goal) {
// 4방향 BFS (상하좌우, 대각선 없음)
// blocked 타일(가구, 벽) 회피
// 반환: [{col, row}, ...] 경로 배열
}
```
- 가구 footprint → `blocked[]` 배열로 타일 마킹
- 의자/책상 뒤 타일은 walkable (backgroundTiles 개념)
- 경로 없으면 제자리 유지
### 6.2 이동 파라미터
| 파라미터 | 값 | 설명 |
|---------|-----|------|
| WALK_SPEED | 48 px/sec | pixel-agents 참고 |
| moveProgress | 0~1 | 현재 타일 → 다음 타일 선형 보간 |
| direction | DOWN/UP/RIGHT/LEFT | 이동 방향 → 스프라이트 방향 결정 |
### 6.3 배회 로직 (idle 상태)
```
idle 진입
→ 3~8초 대기 (seatTimer)
→ 자리에서 일어남
→ 인접 floor 타일로 랜덤 이동
→ 3~6회 반복 (wanderCount)
→ 자리로 BFS 복귀
→ 2~20초 자리에서 휴식 (restTimer)
→ 반복
```
### 6.4 상태 전환 시 이동 시퀀스
| 전환 | 동작 |
|------|------|
| `* → working` | 배회 중단, 자기 책상으로 BFS 이동 → 도착 후 type 애니메이션 |
| `* → waiting` | 자기 책상에서 wobble 애니메이션 + 말풍선 |
| `* → reporting` | 자기 책상에서 빠른 type 애니메이션 |
| `idle (배회 중)` | 랜덤 floor 타일로 이동, wanderCount 소진 시 복귀 |
| `* → break` | 휴게실(break_room/coffee) waypoint로 BFS 이동 → break 애니메이션 |
| `break → idle` | 자기 책상으로 BFS 이동 → idle 루프 시작 |
---
## 7. 오버레이 시스템
캔버스 위에 HTML이 아닌 Canvas 2D로 직접 렌더링.
### 7.1 항상 표시
- **이름 라벨**: 에이전트 아래, 에이전트 색상 텍스트, 12px
- **상태 배지**: 이름 아래, 배경색 + 텍스트 ("working", "idle", "break")
### 7.2 조건부 표시
- **말풍선**: `waiting` 상태에서만, 에이전트 위에 "승인 대기!" 텍스트
- 둥근 사각형 배경 (#fbbf24), 아래 삼각형 꼬리
- 2초 페이드인, 상태 변경 시 즉시 사라짐
- **알림 배지**: 미확인 notification 있을 때, 에이전트 우상단에 빨간 원 + 숫자
### 7.3 렌더링 순서
```
1. 타일맵 (바닥 + 벽)
2. 가구 (Y-sort)
3. 에이전트 (Y-sort, 가구와 혼합)
4. 오버레이 (말풍선, 이름, 배지) — 항상 최상위
```
---
## 8. 테마 시스템
### 8.1 테마 데이터 구조
```javascript
const THEMES = {
modern: {
name: 'Modern',
wall: { color: '#1a1a2e', border: '#333', accent: '#8b5cf6' },
floor: { color1: '#2a2a3e', color2: '#323248' },
furniture: { desk: '#3a3a5a', chair: '#4c1d95', monitor: '#5555aa', shelf: '#2a2a4e' },
decor: { plant: '#22c55e', pot: '#4a3a2a', lamp: '#fbbf24', ledStrip: '#8b5cf6' },
lighting: { ambient: 'rgba(139,92,246,0.05)', glow: 'rgba(139,92,246,0.15)' }
},
retro: {
name: 'Retro',
wall: { color: '#2a1a0a', border: '#6a4a2a', accent: '#cc8844' },
floor: { color1: '#4a3a1a', color2: '#3a2a10' },
furniture: { desk: '#6a4a1a', chair: '#8a5a2a', monitor: '#555555', shelf: '#5a3a1a' },
decor: { plant: '#44aa44', pot: '#6a4a2a', lamp: '#ffcc66', brick: '#8a5a2a' },
lighting: { ambient: 'rgba(255,200,100,0.05)', glow: 'rgba(255,200,100,0.2)' }
},
minimal: {
name: 'Minimal',
wall: { color: '#fafafa', border: '#ddd', accent: '#3b82f6' },
floor: { color1: '#e8e8e8', color2: '#f0f0f0' },
furniture: { desk: '#ffffff', chair: '#e0e0e0', monitor: '#333333', shelf: '#f5f5f5' },
decor: { plant: '#86efac', pot: '#ffffff', lamp: '#fbbf24', window: '#e0f0ff' },
lighting: { ambient: 'rgba(59,130,246,0.03)', glow: 'rgba(255,255,255,0.1)' }
}
};
```
### 8.2 테마 적용 방식
- `TileMap.render(theme)` — 바닥/벽 색상을 theme에서 읽어 렌더링
- `FurnitureRenderer.draw(type, theme)` — 가구별 프로시저럴 렌더링에 theme 팔레트 적용
- 테마 전환 시 전체 캔버스 리렌더 (레이아웃 변경 없음)
- 사용자 선택은 `localStorage`에 저장, 기본값: `modern`
### 8.3 테마별 고유 데코
| 테마 | 고유 요소 |
|------|----------|
| Modern | LED 스트립 (벽 하단), 네온 글로우, 미니멀 화분 |
| Retro | 벽돌 텍스처, CRT 모니터, 책장(컬러풀 책), 탁상 램프 |
| Minimal | 창문(자연광), 다육이, 깔끔한 화이트 선반 |
---
## 9. 히트 테스팅 & 인터랙션
### 9.1 클릭 처리
```javascript
canvas.onclick = (e) => {
const {col, row} = screenToTile(e.offsetX, e.offsetY, zoom, pan);
// 1. 에이전트 히트 테스트 (역순, 최상위 우선)
const agent = agents.findLast(a =>
Math.abs(a.x - col) < 1 && Math.abs(a.y - row) < 1.5
);
if (agent) {
openSidePanel(agent.id);
return;
}
// 2. 빈 공간 → 패널 닫기
closeSidePanel();
};
```
### 9.2 호버 (데스크톱만)
- 에이전트 위 호버 시 커서 `pointer`로 변경
- 툴팁 불필요 (이름+배지가 항상 표시되므로)
---
## 10. WebSocket 연동
기존 프로토콜 100% 유지. 프론트엔드에서 메시지 수신 시 캔버스 상태만 추가 업데이트.
| 메시지 타입 | 캔버스 반응 |
|------------|-----------|
| `agent_state` | 해당 에이전트 FSM 상태 전환 → 애니메이션/위치 변경 트리거 |
| `agent_move` | target에 따라 BFS 경로 계산 → 이동 시작 |
| `task_complete` | 에이전트 상태를 idle로 전환 |
| `notification` | 에이전트 위 알림 배지 카운트 증가 |
| `init` | 모든 에이전트 초기 위치/상태 설정 |
### agent_state 수신 시 이동 로직
```javascript
function onAgentState(agentId, newState) {
const agent = agents.get(agentId);
switch (newState) {
case 'working':
case 'waiting':
case 'reporting':
// 자리에 있지 않으면 자리로 이동
if (!agent.isAtDesk()) agent.moveTo(agent.deskWaypoint);
break;
case 'break':
agent.moveTo('break_room');
break;
case 'idle':
// 배회 루프 시작
agent.startWandering();
break;
}
agent.setState(newState);
}
```
---
## 11. 파일 구조 (프론트엔드)
```
src/pages/agent-office/
├── AgentOffice.jsx # 루트 컴포넌트 (재작성)
├── AgentOffice.css # 스타일 (재작성)
├── hooks/
│ ├── useAgentManager.js # WebSocket + 상태 (기존 확장)
│ └── useOfficeCanvas.js # 캔버스 셋업 (재작성)
├── components/
│ ├── TopBar.jsx # 상단 바 (신규)
│ ├── SidePanel.jsx # 사이드 패널 컨테이너 (신규)
│ ├── CommandTab.jsx # Commands 탭 (AgentColumn 리팩토링)
│ ├── TaskTab.jsx # Tasks 탭 (AgentColumn에서 분리)
│ ├── TokenTab.jsx # Tokens 탭 (신규)
│ ├── LogTab.jsx # Logs 탭 (신규)
│ ├── ApprovalCard.jsx # 승인 UI 카드 (신규)
│ └── MobileBottomSheet.jsx # 모바일 바텀 시트 (신규)
├── canvas/
│ ├── OfficeRenderer.js # 게임 루프 + 렌더 파이프라인 (재작성)
│ ├── TileMap.js # 타일맵 렌더링 + 테마 적용 (재작성)
│ ├── FurnitureRenderer.js # 가구 프로시저럴 렌더링 (신규)
│ ├── AgentSprite.js # 에이전트 이동 + 애니메이션 (재작성)
│ ├── ProceduralSprite.js # 프로시저럴 캐릭터 렌더링 (SpriteSheet 리팩토링)
│ ├── SpriteLoader.js # 스프라이트시트 로더 + 폴백 (신규)
│ ├── Pathfinder.js # BFS 경로 탐색 (신규)
│ ├── OverlayRenderer.js # 이름, 배지, 말풍선 (신규)
│ └── themes.js # 테마 데이터 (신규)
├── assets/
│ ├── office-map.json # 32x20 맵 데이터 (재작성)
│ └── sprites/ # Phase 2 스프라이트시트 PNG (빈 디렉토리)
```
### 삭제 대상
- `components/AgentColumn.jsx` → CommandTab + TaskTab으로 분리
- `components/CommandColumn.jsx` → SidePanel 내 CommandTab으로 통합
- `components/ChatPanel.jsx` → 미사용, 삭제
- `components/DocumentPanel.jsx` → LogTab으로 대체
- `canvas/SpriteSheet.js` → ProceduralSprite.js로 리팩토링
---
## 12. 백엔드 변경사항
**없음.** 기존 WebSocket 프로토콜과 REST API를 그대로 사용한다.
단, `agent_move` 메시지가 break 전환 시에도 정확히 발송되는지 확인 필요:
- `base.py``check_idle_break()``transition('break')` → WebSocket broadcast에 `agent_move` 포함 여부 확인
- 필요 시 `transition()` 메서드에서 break 상태 전환 시 `agent_move` 메시지 추가
---
## 13. 구현 순서 (Phase 개요)
| Phase | 내용 | 의존성 |
|-------|------|--------|
| **1. 캔버스 엔진** | 게임 루프, 타일맵, 줌/팬, 테마 시스템 | 없음 |
| **2. 에이전트 시스템** | 프로시저럴 캐릭터, BFS 경로 탐색, 상태별 애니메이션, 배회 로직 | Phase 1 |
| **3. 오버레이** | 이름 라벨, 상태 배지, 말풍선, 알림 배지 | Phase 2 |
| **4. 사이드 패널** | 4탭 구성, Quick Actions, Approval UI | Phase 1 |
| **5. 페이지 통합** | AgentOffice.jsx 재작성, WebSocket 연동, 히트 테스팅 | Phase 1-4 |
| **6. 모바일 대응** | 바텀 시트, 핀치 줌, 터치 이벤트, 반응형 | Phase 5 |
| **7. 스프라이트 로더** | SpriteLoader 구현, 폴백 연결 | Phase 2 |
---
## 14. 성공 기준
- [ ] 전체 화면 캔버스에서 5명의 에이전트가 상태에 맞게 애니메이션
- [ ] idle 에이전트가 사무실을 배회하다 자리로 복귀
- [ ] break 에이전트가 휴게실로 이동하여 휴식
- [ ] 에이전트 클릭 시 사이드 패널 열림, 4탭 모두 동작
- [ ] Commands 탭에서 명령 전송 + 승인/거부 동작
- [ ] 3가지 테마 전환 동작, localStorage에 저장
- [ ] 모바일에서 바텀 시트 + 핀치 줌 동작
- [ ] 기존 WebSocket 프로토콜과 100% 호환