diff --git a/300_architecture/sequences/daily_briefing_sequences.md b/300_architecture/sequences/daily_briefing_sequences.md new file mode 100644 index 0000000..37c62c5 --- /dev/null +++ b/300_architecture/sequences/daily_briefing_sequences.md @@ -0,0 +1,257 @@ +# 일일 브리핑 시스템 시퀀스 다이어그램 + +## 작성일: 2025-08-25 +## 작성자: 서버 관리자 with Claude +## 상태: 구현 완료 (Gmail 토큰 문제로 부분 작동) + +--- + +## 목차 +1. [시스템 개요](#1-시스템-개요) +2. [정상 작동 시퀀스](#2-정상-작동-시퀀스) +3. [현재 실패 시퀀스](#3-현재-실패-시퀀스) +4. [토큰 갱신 플로우](#4-토큰-갱신-플로우-미구현) +5. [구현 상태 요약](#5-구현-상태-요약) + +--- + +## 1. 시스템 개요 + +### 1.1 아키텍처 +- **크론 서버**: 51123 (Gateway) +- **실행 서버**: 51124 (rb8001, skill-email, skill-news) +- **실행 시간**: 매일 오전 9시 (KST) +- **대상 사용자**: 슬랙 워크스페이스 멤버 3명 + +### 1.2 서비스 구성 +| 서비스 | 포트 | 역할 | 상태 | +|--------|------|------|------| +| robeing-gateway | 8100 | 크론 실행 | ✅ | +| rb8001 | 8001 | 브리핑 생성 | ✅ | +| skill-email | 8501 | 이메일 수집 | ⚠️ | +| skill-news | 8505 | 뉴스 수집 | ✅ | + +--- + +## 2. 정상 작동 시퀀스 + +```mermaid +sequenceDiagram + participant Cron as Gateway Cron
(51123:8100) + participant RB as rb8001
(51124:8001) + participant DM as dm_skill
(rb8001 내부) + participant Email as skill-email
(51124:8501) + participant News as skill-news
(51124:8505) + participant DB as PostgreSQL
(51123:5432) + participant Auth as auth-server
(51123:9000) + participant LLM as Gemini Service
(rb8001 내부) + participant Slack as Slack API + participant User as 사용자 DM + + %% 크론 트리거 + Note over Cron: 매일 09:00:00 KST + Cron->>RB: POST /api/cron/daily-summary
Authorization: Bearer cron-secret-2024 + + %% 인증 및 초기화 + RB->>RB: 토큰 검증
(main.py:229) + RB->>DM: send_daily_summary_dm()
(dm_skill.py:384) + + %% 사용자 목록 + Note over DM: 하드코딩된 사용자:
U091UNVE41M (전희재)
U0925SXQFDK (김종태)
U092F7FQ55L (HanYong) + + %% 병렬 데이터 수집 + par 이메일 수집 + loop 각 사용자별 + DM->>Email: GET /messages
?user_id={slack_id}
&limit=5
&query=category:primary + + %% 토큰 체크 및 갱신 + Email->>DB: SELECT * FROM gmail_tokens
WHERE user_id = ? + DB-->>Email: token_data, refresh_token + + alt 토큰 만료 + Email->>Auth: POST /api/gmail/refresh
Body: {user_id, refresh_token} + Auth->>Auth: Google OAuth 토큰 갱신 + Auth->>DB: UPDATE gmail_tokens + Auth-->>Email: 새 access_token + end + + Email->>Email: Gmail API 호출
Primary 탭 이메일 조회 + Email-->>DM: 이메일 목록 (최대 5개) + end + and 뉴스 수집 + DM->>News: POST /api/news/search
Body: {
keywords: ["AI", "스타트업",
"기술", "혁신", "투자"]
} + News->>News: Google News API 호출 + News->>News: 결과 필터링 및 정렬 + News-->>DM: 최신 뉴스 5개 + end + + %% LLM 요약 생성 + DM->>DM: 데이터 통합
- 이메일: 사용자별 5개
- 뉴스: 공통 5개 + + DM->>LLM: 요약 생성 요청 + Note over LLM: 프롬프트:
"한국어로 오늘의 브리핑 작성
1. 중요 이메일 요약
2. 주요 뉴스 요약
3. 액션 아이템 추출" + LLM-->>DM: 구조화된 브리핑 텍스트 + + %% Slack 전송 + loop 각 사용자별 + DM->>DM: 개인화 메시지 생성 + DM->>Slack: POST /api/chat.postMessage
Body: {
channel: {user_dm_id},
text: "🌅 모닝 브리핑",
blocks: [...]
} + Slack-->>DM: 전송 성공 + Slack->>User: 📨 개인 DM 수신 + end + + %% 완료 + DM-->>RB: 브리핑 전송 완료 + RB-->>Cron: 200 OK
Body: {status: "success"} +``` + +--- + +## 3. 현재 실패 시퀀스 + +```mermaid +sequenceDiagram + participant Cron as Gateway Cron
(51123) + participant RB as rb8001
(51124) + participant DM as dm_skill + participant Email as skill-email + participant News as skill-news + participant DB as PostgreSQL + participant Slack as Slack API + participant User as 사용자 + + Note over Cron: 09:00:00 트리거 ✅ + Cron->>RB: POST /api/cron/daily-summary ✅ + RB->>DM: send_daily_summary_dm() ✅ + + %% 이메일 수집 실패 + rect rgb(255, 230, 230) + Note over DM,Email: 이메일 수집 실패 + DM->>Email: GET /messages + Email->>DB: SELECT gmail_tokens + DB-->>Email: token_data: NULL ❌ + Email-->>DM: 500 Internal Server Error + Note over DM: 이메일 데이터 = [] + end + + %% 뉴스는 정상 + rect rgb(230, 255, 230) + Note over DM,News: 뉴스 수집 성공 + DM->>News: POST /api/news/search ✅ + News-->>DM: 뉴스 5개 반환 ✅ + end + + %% 불완전한 브리핑 + DM->>DM: 부분 데이터로 요약 생성 + Note over DM: 이메일 섹션 비어있음 + + loop 각 사용자 + DM->>Slack: 불완전한 브리핑 전송 + Slack->>User: 📨 뉴스만 있는 브리핑 + Note over User: "이메일 요약: 없음
뉴스 요약: 5개 항목" + end +``` + +--- + +## 4. 토큰 갱신 플로우 (미구현) + +```mermaid +sequenceDiagram + participant Email as skill-email + participant Auth as auth-server
(51123:9000) + participant DB as PostgreSQL + participant Google as Google OAuth + + Note over Email: 현재 미구현 상태
구현 필요한 플로우 + + rect rgb(255, 255, 230) + Note over Email: 토큰 만료 감지 + Email->>Email: check_token_expiry() + + alt 토큰 만료됨 + Email->>Auth: POST /api/gmail/refresh + Note over Auth: 엔드포인트는 구현됨
(gmail_refresh.py) + + Auth->>Google: POST /token
grant_type=refresh_token + Google-->>Auth: 새 access_token + + Auth->>DB: UPDATE gmail_tokens
SET access_token = ? + Auth-->>Email: {access_token, expires_at} + + Email->>Email: 토큰 캐시 갱신 + Email->>Email: Gmail API 재시도 + else 토큰 유효 + Email->>Email: 기존 토큰으로 진행 + end + end + + Note over Email: 이 로직 추가 시
자동 갱신 가능 +``` + +--- + +## 5. 구현 상태 요약 + +### 5.1 완료된 구현 ✅ +- **크론 설정**: Gateway에서 매일 9시 실행 +- **API 엔드포인트**: `/api/cron/daily-summary` (main.py:229) +- **DM 스킬**: `send_daily_summary_dm()` (dm_skill.py:384) +- **뉴스 서비스**: skill-news 독립 서비스 (포트 8505) +- **Slack 전송**: 정상 작동 + +### 5.2 문제점 ⚠️ +| 문제 | 원인 | 영향 | 해결 방안 | +|------|------|------|-----------| +| Gmail 토큰 NULL | 사용자 미인증 | 이메일 수집 실패 | 사용자 재인증 필요 | +| 자동 갱신 미구현 | skill-email 로직 부재 | 토큰 만료 시 실패 | refresh 로직 추가 | +| 사용자 하드코딩 | DB 연동 미구현 | 확장성 제한 | DB 조회로 변경 | + +### 5.3 관련 파일 +``` +51123 서버: +├── /home/admin/robeing-gateway/app/crontab.py (크론 설정) +├── /home/admin/auth-server/app/api/gmail_refresh.py (토큰 갱신 API) +└── /home/admin/DOCS/troubleshooting/250824_*.md (트러블슈팅) + +51124 서버: +├── /home/admin/ivada_project/rb8001/ +│ ├── main.py:229 (크론 엔드포인트) +│ └── app/skills/dm_skill.py:384 (브리핑 생성) +├── /home/admin/ivada_project/skill-email/ (이메일 서비스) +└── /home/admin/ivada_project/skill-news/ (뉴스 서비스) +``` + +### 5.4 모니터링 포인트 +```bash +# 크론 실행 확인 +docker exec robeing-gateway crontab -l | grep briefing + +# 서비스 상태 확인 +docker ps | grep -E "rb8001|skill-email|skill-news" + +# 로그 확인 +docker logs rb8001 --tail 100 | grep daily-summary +docker logs skill-email --tail 50 | grep ERROR + +# DB 토큰 상태 +psql -U robeings -d main_db -c "SELECT user_id, token_data IS NOT NULL as has_token FROM gmail_tokens;" +``` + +--- + +## 개선 제안 + +### 단기 (즉시 적용 가능) +1. skill-email에 토큰 만료 체크 로직 추가 +2. 에러 발생 시에도 뉴스만이라도 전송하도록 graceful degradation + +### 중기 (1-2주) +1. 사용자 목록 DB 연동 +2. Gmail refresh_token 자동 갱신 구현 +3. 브리핑 커스터마이징 (사용자별 선호도) + +### 장기 (1개월+) +1. 웹 UI에서 브리핑 설정 관리 +2. 브리핑 시간 개인화 +3. 다양한 데이터 소스 추가 (캘린더, 태스크 등) \ No newline at end of file diff --git a/troubleshooting/250825_robeing_stats_display_issue.md b/troubleshooting/250825_robeing_stats_display_issue.md new file mode 100644 index 0000000..40bc6c5 --- /dev/null +++ b/troubleshooting/250825_robeing_stats_display_issue.md @@ -0,0 +1,243 @@ +# 로빙 상태(Stats) 표시 안됨 문제 트러블슈팅 + +## 작성일: 2025-08-25 +## 작성자: 서버 관리자 +## 상태: 미해결 (51124 서버 코드 수정 필요) + +--- + +## 문제 요약 +사용자가 로그인 후 프론트엔드에서 로빙의 실제 상태(레벨, 경험치, 스탯)가 표시되지 않고 하드코딩된 기본값만 표시됨 + +--- + +## 증상 +- 프론트엔드에서 항상 "LV.12", "350 코인", "75% 경험치" 표시 (하드코딩) +- 실제 DB에는 rb8001이 레벨 20이지만 UI에 반영 안됨 +- 게이트웨이 로그: `/api/stats/undefined` 호출 (robeing_id 미전달) + +--- + +## 문제 분석 + +### 1. 프론트엔드 문제 (51123 서버) + +#### 1.1 하드코딩된 UI +**위치**: `/home/admin/frontend-customer/src/components/game-layout.tsx:97-101` +```jsx +
+ LV.12 // 하드코딩 +
+
+ 💎 350 // 하드코딩 +
+``` + +#### 1.2 Stats API 호출 누락 +- 프론트엔드에서 `/api/stats/{robeing_id}` 호출 로직 없음 +- useEffect나 데이터 fetching 코드 부재 +- SimpleStatsPanel도 props 기본값만 사용 + +#### 1.3 Robeing ID 미전달 +- 게이트웨이 로그 확인: +``` +GET /api/stats/undefined HTTP/1.0" 200 OK +Stats request from user: happybell80 for robeing: undefined +``` +- 프론트엔드가 사용자의 robeing_id를 가져오지 못함 + +### 2. 게이트웨이 동작 (51123 서버) - 정상 + +#### 2.1 사용자-로빙 매핑 정상 +```sql +-- DB 확인 결과 +happybell80 (goeun2dc@gmail.com) → rb8001 정상 매핑 +``` + +#### 2.2 프록시 정상 작동 +```bash +# 직접 테스트 시 정상 응답 +curl http://localhost:8100/api/stats/rb8001 +→ 51124 서버로 정상 프록시 +``` + +### 3. 51124 서버 문제 (rb8001 서비스) + +#### 3.1 하드코딩된 응답 +**문제 위치**: `rb8001/main.py:116` +```python +@app.get("/stats") +async def get_stats(): + # DB 조회 없이 하드코딩된 값 반환 + return { + "level": 1, # 항상 1 + "experience": 0, + "stats": { + "memory": 10, + "compute": 10, + "empathy": 10, + "leadership": 10, + "ethics": 0 + } + } +``` + +#### 3.2 State Service 미연결 +- `state_service.py`에 `/api/stats/{robeing_id}` 정의됨 +- 하지만 `main.py`에서 import/mount 안함 +- 결과: 엔드포인트 접근 불가 (404) + +#### 3.3 DB 값과 불일치 +```sql +-- DB의 실제 값 +robeing_stats 테이블: +- robeing_id: rb8001 +- level: 20 +- experience: 2000 + +-- API 응답값 +- level: 1 +- experience: 0 +``` + +--- + +## 문제 플로우 다이어그램 + +```mermaid +sequenceDiagram + participant User as 사용자
(goeun2dc) + participant Frontend as 프론트엔드
(51123) + participant Gateway as 게이트웨이
(51123:8100) + participant RB8001 as rb8001
(51124:8001) + participant DB as PostgreSQL
(51123:5432) + + User->>Frontend: 로그인 + Frontend->>Frontend: auth_token 저장 + Note over Frontend: ❌ robeing_id 가져오기 누락 + + Frontend->>Frontend: game-layout 렌더링 + Note over Frontend: ❌ 하드코딩된 값 표시
LV.12, 350코인, 75% + + Frontend->>Gateway: GET /api/stats/undefined ❌ + Note over Gateway: robeing_id가 undefined + + Gateway->>Gateway: 캐시에서 사용자 조회
happybell80 → rb8001 + Gateway->>RB8001: GET /stats (rb8001로 프록시) + + RB8001->>RB8001: ❌ DB 조회 없음 + RB8001-->>Gateway: 하드코딩된 응답
level: 1, 모든 스탯: 10 + Gateway-->>Frontend: 응답 전달 + + Frontend->>Frontend: ❌ 응답 무시
여전히 하드코딩 표시 + + Note over DB: 실제 값 (미사용):
level: 20
experience: 2000 +``` + +--- + +## 해결 방안 + +### 단기 (즉시 적용 가능) - 51123 서버 + +#### 1. 프론트엔드 수정 필요 +```typescript +// game-layout.tsx 수정 예시 +const [stats, setStats] = useState(null); + +useEffect(() => { + const fetchStats = async () => { + const userId = localStorage.getItem('user_id'); + // 1. 먼저 사용자의 robeing_id 가져오기 + const robeingResponse = await fetch(`/api/user/${userId}/robeing`); + const { robeing_id } = await robeingResponse.json(); + + // 2. stats 가져오기 + const statsResponse = await fetch(`/api/stats/${robeing_id}`); + const statsData = await statsResponse.json(); + setStats(statsData); + }; + + fetchStats(); +}, []); + +// 하드코딩 대신 동적 표시 +
LV.{stats?.level || 1}
+
💎 {stats?.coins || 0}
+``` + +### 중기 (51124 서버 수정 필요) + +#### 1. rb8001 서비스 수정 +- `main.py`에서 `state_service` import 및 mount +- `/stats` 엔드포인트가 DB 조회하도록 수정 +- `robeing_stats` 테이블과 동기화 + +#### 2. State Service 활성화 +```python +# main.py 수정 필요 +from app.services import state_service + +# 라우터 마운트 +app.include_router(state_service.router, prefix="/api") +``` + +--- + +## 임시 해결책 (권장하지 않음) + +### DB 직접 수정 +```sql +-- rb8001의 레벨을 1로 낮추기 (비추천) +UPDATE robeing_stats +SET level = 1, experience = 0 +WHERE robeing_id = 'rb8001'; +``` +→ 근본 해결 아님, API가 여전히 DB 무시 + +--- + +## 관련 파일 +- **프론트엔드**: + - `/home/admin/frontend-customer/src/components/game-layout.tsx` + - `/home/admin/frontend-customer/src/components/simple-stats-panel.tsx` + - `/home/admin/frontend-customer/src/contexts/auth-context.tsx` + +- **게이트웨이**: + - `/home/admin/robeing-gateway/app/main.py` (정상) + +- **51124 서버** (수정 필요): + - `rb8001/main.py:116` - 하드코딩된 /stats + - `rb8001/app/services/state_service.py` - 미연결 + +- **DB 테이블**: + - `robeing_stats` - 실제 스탯 저장 + - `workspace_members` - 사용자-로빙 매핑 + +--- + +## 모니터링 명령어 +```bash +# 게이트웨이 로그 확인 +docker logs robeing-gateway --tail 50 | grep stats + +# DB 상태 확인 +PGPASSWORD=robeings psql -h localhost -U robeings -d main_db \ + -c "SELECT * FROM robeing_stats WHERE robeing_id = 'rb8001';" + +# API 직접 테스트 +curl http://localhost:8100/api/stats/rb8001 +``` + +--- + +## 참고 문서 +- [일일 브리핑 시퀀스](/home/admin/DOCS/300_architecture/sequences/daily_briefing_sequences.md) - 비슷한 DB 연동 문제 +- [OAuth 스코프 문제](/home/admin/DOCS/troubleshooting/250824_gmail_oauth_scope_issue.md) + +--- + +## 추가 확인 사항 +1. 프론트엔드에서 사용자의 robeing_id를 어떻게 가져올지 결정 필요 +2. stats 업데이트 주기 (실시간 vs 폴링) +3. 캐싱 전략 (게이트웨이 캐시 활용) \ No newline at end of file