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

18 KiB
Raw Blame History

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 게임 루프

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 준비)

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 경로 탐색

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 테마 데이터 구조

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 클릭 처리

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 수신 시 이동 로직

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.pycheck_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% 호환