Shadow AI 에이전트의 웹 대시보드 및 API 서버
Next.js + TypeScript + Tailwind CSS v4 + Supabase + TanStack Query + Zustand
Shadow Web은 Shadow 에이전트 시스템의 프론트엔드, 백엔드, 데이터베이스를 담당하는 풀스택 프로젝트입니다.
┌─────────────┐
│ Slack │ Slack Bot으로 사용자 인터랙션
│ Bot │
└──────┬──────┘
│ webhook
↓
┌─────────────────────────────────┐
│ shadow-web (이 프로젝트) │
│ ┌───────────────────────────┐ │
│ │ 프론트엔드 (React) │ │ 대시보드, 분석 결과 시각화
│ │ - Next.js 15 │ │
│ │ - TanStack Query │ │
│ │ - Tailwind CSS v4 │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ 백엔드 (API Routes) │ │ Slack 연동, 비즈니스 로직
│ │ - app/api/slack/ │ │ ← Webhook, Commands
│ │ - app/api/analyze/ │ │ ← 분석 요청 처리
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ 데이터베이스 (Supabase) │ │ 데이터 저장 및 관리
│ │ - PostgreSQL │ │
│ │ - Storage │ │ ← 녹화 파일, 스크린샷
│ │ - Auth │ │
│ │ - RLS │ │
│ └───────────────────────────┘ │
└─────────────┬───────────────────┘
│ HTTP API 호출
↓
┌─────────────────────────────────┐
│ shadow-py (별도 프로젝트) │ AI 분석 엔진
│ - 화면 녹화 + 입력 추적 │
│ - Vision AI 분석 (Claude/Gemini)│
│ - 반복 패턴 감지 │
│ - FastAPI로 API 제공 │
└─────────────────────────────────┘
| 프로젝트 | 역할 | 기술 스택 |
|---|---|---|
| shadow-web (이 프로젝트) | 웹 UI + API 서버 + DB | Next.js, Supabase, TypeScript |
| shadow-py | AI 분석 엔진 | Python, FastAPI, Claude/Gemini |
- Next.js 15 - React 프레임워크 (App Router)
- TypeScript - 타입 안정성
- Tailwind CSS v4 - 유틸리티 기반 CSS 프레임워크
- Supabase - Backend as a Service (Database + Storage + Auth)
- TanStack Query v5 - 서버 상태 관리
- Zustand - 클라이언트 상태 관리
- pnpm - 패키지 매니저
shadow-web/
├── app/ # Next.js App Router
│ ├── layout.tsx
│ ├── page.tsx # 대시보드 메인 페이지
│ ├── globals.css
│ └── api/ # API Routes (백엔드)
│ ├── slack/ # Slack Bot 연동
│ │ ├── webhook/ # Slack 이벤트 수신
│ │ └── commands/ # Slash Commands
│ └── analyze/ # shadow-py 연동 API
├── components/ # 재사용 컴포넌트
│ └── dashboard/ # 대시보드 컴포넌트
├── lib/
│ ├── supabase.ts # Supabase 클라이언트
│ └── services/ # 비즈니스 로직
│ └── shadow-py.ts # shadow-py API 호출
├── store/ # Zustand stores
├── types/ # TypeScript 타입 정의
│ ├── slack.ts # Slack 타입
│ └── shadow.ts # Shadow AI 타입
├── constants/ # 상수 관리
│ └── index.ts
├── supabase/
│ ├── migrations/ # DB 마이그레이션
│ │ └── 001_initial.sql # 초기 스키마
│ └── config.toml
└── public/ # 정적 파일
- 분석 결과 대시보드
- 패턴 시각화
- 에이전트 설정 UI
- 세션 히스토리
- Slack Bot 연동
- Webhook 이벤트 수신 (
/api/slack/webhook) - Slash Commands 처리 (
/api/slack/commands)
- Webhook 이벤트 수신 (
- Shadow AI 통합
- shadow-py API 호출 (
/api/analyze) - 분석 결과 저장 및 조회
- shadow-py API 호출 (
- 비즈니스 로직
- 세션 관리
- 사용자 권한 제어
- PostgreSQL 테이블
sessions: 녹화 세션 정보analysis_results: AI 분석 결과patterns: 감지된 패턴slack_events: Slack 이벤트 로그
- Storage
- 녹화 파일 저장
- 스크린샷 저장
- RLS (Row Level Security)
- 사용자별 데이터 접근 제어
pnpm installDocker Desktop을 실행한 후:
# Supabase CLI 설치 (Mac)
brew install supabase/tap/supabase
# Supabase 초기화
supabase init
# 로컬 Supabase 시작
supabase start.env.local 파일을 생성하고 다음 내용을 입력:
# Supabase Configuration
# 로컬 개발
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<supabase status에서 확인한 anon key>
# 프로덕션 (Vercel 배포 시)
# NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
# NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your-publishable-key
# Slack Bot Configuration
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_APP_TOKEN=xapp-your-app-token # Socket Mode 사용 시
# Shadow-py API Configuration
SHADOW_PY_API_URL=http://localhost:8000 # shadow-py FastAPI 서버 주소환경 변수 확인 방법:
NEXT_PUBLIC_SUPABASE_*:supabase status명령어로 확인SLACK_*: Slack App 설정 페이지에서 확인- Bot Token: Settings > Install App > Bot User OAuth Token
- Signing Secret: Settings > Basic Information > Signing Secret
- App Token: Settings > Basic Information > App-Level Tokens
pnpm run dev브라우저에서 http://localhost:3000 열기
supabase migration new <migration_name>supabase db resetsupabase db push# http://127.0.0.1:54323 에서 확인
supabase status# 개발 서버 실행
pnpm run dev
# 프로덕션 빌드
pnpm run build
# 프로덕션 서버 실행
pnpm run start
# Lint 검사
pnpm run lint
# Supabase 로컬 시작
supabase start
# Supabase 로컬 중지
supabase stop
# Supabase 상태 확인
supabase statusshadow-web은 AI 분석 기능을 위해 shadow-py API를 호출합니다.
별도 터미널에서 shadow-py 실행:
cd /Users/doyoonlee/Desktop/dev_else/_my-projects/shadow-py
# FastAPI 서버 시작
uvicorn main:app --reload --port 8000# shadow-py API 상태 확인
curl http://localhost:8000/
# 분석 요청 테스트
curl -X POST http://localhost:8000/analyze \
-H "Content-Type: application/json" \
-d '{"session_id": "test-session"}'- Supabase 시작:
supabase start - shadow-py 시작:
uvicorn main:app --reload(포트 8000) - shadow-web 시작:
pnpm run dev(포트 3000)
- Slack API에서 앱 생성
- OAuth & Permissions에서 Bot Token Scopes 추가:
chat:writecommandsim:history
- Event Subscriptions 활성화
- Request URL:
https://your-domain.com/api/slack/webhook
- Request URL:
- Slash Commands 생성
- Command:
/shadow - Request URL:
https://your-domain.com/api/slack/commands
- Command:
# macOS
brew install ngrok
# 또는 https://ngrok.com/download-
Next.js 개발 서버 실행
pnpm run dev
-
ngrok으로 로컬 서버 노출
ngrok http 3000
출력 예시:
Forwarding https://abc123.ngrok.io -> http://localhost:3000 -
Slack App Events API 설정
- Slack API Apps > 앱 선택 > Event Subscriptions
- Enable Events ON
- Request URL 입력:
https://abc123.ngrok.io/api/slack/events - URL verification 성공 확인 (✅ Verified)
-
Subscribe to bot events 추가
message.channels- 공개 채널 메시지message.groups- 비공개 채널 메시지message.im- DM 메시지message.mpim- 그룹 DM 메시지- Save Changes 클릭
-
앱을 워크스페이스에 재설치
- Settings > Install App > Reinstall to Workspace
-
테스트 메시지 전송
- Slack에서 봇이 추가된 채널에 메시지 전송
- 터미널에서 로그 확인:
[Mock] Analyzing pattern for messages: 1 Pattern analysis result: { simple_patterns: [...], sequence_patterns: [] }
-
Supabase에서 데이터 확인
# Supabase Studio 열기 supabase status # Studio URL: http://127.0.0.1:54323 # slack_messages 테이블 확인
- ngrok 무료 버전은 세션이 종료되면 URL이 변경됨
- URL 변경 시 Slack App 설정도 업데이트 필요
- ngrok 터널 유지를 위해 터미널 창 유지
Shadow는 사용자와 상호작용이 필요할 때 Interactive Messages를 전송합니다.
- Slack API Apps > 앱 선택 > Interactivity & Shortcuts
- Interactivity ON
- Request URL 입력:
(로컬 개발 시:
https://your-domain.com/api/slack/interactionshttps://abc123.ngrok.io/api/slack/interactions)
연동 포인트 A: Shadow → Slack (질문 전송)
import { slackClient } from '@/lib/slack'
await slackClient.sendInteractiveQuestion('C01234ABCD', {
trigger_type: 'anomaly_detection',
confidence: 0.85,
question: {
title: '평소와 다른 패턴 감지',
context: {
usual_pattern: 'A→B→C',
current_pattern: 'A→D→C',
},
options: ['의도적', '실수', '도구 제약'],
priority: 'medium',
},
session_id: 'session_20250131_001',
})연동 포인트 B: Slack → Shadow (답변 수신)
사용자가 버튼을 클릭하면 /api/slack/interactions 엔드포인트로 답변이 전송되고, slack_interaction_answers 테이블에 저장됩니다.
import { slackClient } from '@/lib/slack'
// 텍스트로 전송 (코드 블록)
await slackClient.sendAgentSpec({
channel: 'C01234ABCD',
spec: agentSpecMarkdown,
format: 'text',
})
// 파일로 전송
await slackClient.sendAgentSpec({
channel: 'C01234ABCD',
spec: agentSpecMarkdown,
format: 'file',
filename: 'agent_spec.md',
})app/globals.css에서 @theme 블록 사용:
@theme {
--color-brand: #3b82f6;
--spacing-tight: 0.125rem;
}@layer 디렉티브 사용:
@layer components {
.btn-primary {
@apply rounded-lg bg-blue-500 px-4 py-2 text-white;
}
}-
Vercel 프로젝트 생성
# Vercel CLI 설치 pnpm add -g vercel # 프로젝트 배포 vercel
-
환경 변수 설정
- Vercel Dashboard에서 프로덕션 환경 변수 설정
NEXT_PUBLIC_SUPABASE_URL,SLACK_BOT_TOKEN등
-
Supabase 프로덕션 연결
# Supabase 프로젝트와 연결 supabase link --project-ref your-project-ref # 마이그레이션 배포 supabase db push
-
Slack Webhook URL 업데이트
- Slack App 설정에서 Request URL을 Vercel 도메인으로 변경
- 예:
https://your-app.vercel.app/api/slack/webhook
shadow-py는 별도로 배포해야 합니다. 권장 옵션:
- Railway: Python FastAPI 배포에 최적화
- Render: 무료 티어 제공
- AWS Lambda: 서버리스 배포
배포 후 .env.local의 SHADOW_PY_API_URL을 프로덕션 URL로 변경
프로젝트 코딩 규칙은 CLAUDE.md 참고
# Supabase 재시작
supabase stop
supabase start
# 상태 확인
supabase status# shadow-py 서버 상태 확인
curl http://localhost:8000/
# 포트 충돌 확인
lsof -ti:8000- Signing Secret이 올바른지 확인
- ngrok URL이 만료되지 않았는지 확인 (로컬 개발 시)
- shadow-py: AI 분석 엔진 (Python FastAPI)
- 위치:
/Users/doyoonlee/Desktop/dev_else/_my-projects/shadow-py - 문서: shadow-py 프로젝트의 README 참고
- 위치: