# 대화 히스토리 조회 플로우

## 작성일: 2025-09-02
## 대상: rb8001/rb10508_micro 대화 히스토리
## 상태: ✅ 구현 완료

---

## 1. 전체 플로우

```mermaid
sequenceDiagram
    participant F as Frontend
    participant G as Gateway
    participant R as rb8001
    participant DB as PostgreSQL

    F->>F: localStorage에서 JWT 토큰 가져오기
    F->>F: JWT decode하여 user_id 추출
    F->>G: GET /gateway/api/history?limit=30<br/>Authorization: Bearer {JWT}
    G->>G: JWT 검증 (서명, 만료시간)
    G->>G: JWT에서 sub(UUID) 추출
    G->>G: workspace_members 테이블 조회<br/>robeing_id 확인 (rb8001)
    G->>R: GET /api/history?limit=30<br/>Authorization: Bearer {JWT}<br/>X-User-Id: {UUID}
    R->>R: JWT 검증 및 user_id 추출
    R->>DB: SELECT * FROM conversation_logs<br/>WHERE user_id = (:user_id)::uuid
    DB-->>R: 대화 기록 반환
    R->>R: DB row를 Frontend 형식으로 변환<br/>(user/robeing 메시지 분리)
    R-->>G: {"messages": [...], "has_more": true}
    G-->>F: 응답 전달
    F->>F: 화면에 메시지 렌더링
```

---

## 2. JWT 토큰 구조

### 2.1 auth-server 발급 토큰
```json
{
  "sub": "1e16e9d5-59f3-54da-a661-8abeabff4230",  // UUID
  "email": "goeun2dc@gmail.com",
  "name": "김종태",
  "username": "happybell80",
  "picture": "https://...",
  "exp": 1759046456
}
```

### 2.2 중요 필드
- `sub`: 사용자 UUID (users.id)
- `username`: 사용자명
- `exp`: 만료 시간 (Unix timestamp)

---

## 3. Gateway 처리

### 3.1 JWT 검증
```python
# Gateway main.py
def verify_jwt_token(token: str):
    payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
    return payload["sub"]  # UUID 반환
```

### 3.2 robeing 라우팅
```python
# workspace_members 테이블 조회
SELECT robeing_id FROM workspace_members 
WHERE user_id = :user_id

# 결과: rb8001 또는 rb10508_micro
```

### 3.3 헤더 전달
```python
headers = {
    "Authorization": request.headers.get("Authorization"),  # JWT 전달
    "X-User-Id": user_id  # UUID 전달
}
```

---

## 4. rb8001 처리

### 4.1 JWT 인증
```python
# rb8001 main.py
async def get_current_user(authorization: str = Header(None)):
    token = authorization.replace("Bearer ", "")
    payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
    return payload["sub"]  # UUID
```

### 4.2 DB 조회
```python
# database.py
async def get_paginated_conversations(user_id: str, before: float = None, limit: int = 30):
    query = """
        SELECT id, message, response, timestamp 
        FROM conversation_logs 
        WHERE user_id = (:user_id)::uuid 
          AND robeing_id = 'rb8001'
          AND (:before::timestamp IS NULL OR timestamp < :before)
        ORDER BY timestamp DESC 
        LIMIT :limit
    """
```

### 4.3 응답 변환
```python
# DB row → Frontend 형식
messages = []
for row in results:
    # User 메시지
    messages.append({
        "id": f"{row['id']}_user",
        "text": row["message"],
        "sender": "user",
        "timestamp": row["timestamp"].isoformat()
    })
    # Robeing 응답
    messages.append({
        "id": f"{row['id']}_robeing",
        "text": row["response"],
        "sender": "robeing",
        "timestamp": row["timestamp"].isoformat()
    })
```

---

## 5. Frontend 처리

### 5.1 API 호출
```typescript
// robeing-api.ts
export async function getHistory(before?: string, limit: number = 30) {
  const params = new URLSearchParams({ limit: limit.toString() });
  if (before) params.append('before', before);
  
  const response = await fetch(`${API_URL}/api/history?${params}`, {
    headers: {
      'Authorization': `Bearer ${localStorage.getItem('token')}`
    }
  });
  return response.json();
}
```

### 5.2 무한 스크롤
```typescript
// Intersection Observer로 스크롤 감지
const observer = new IntersectionObserver((entries) => {
  if (entries[0].isIntersecting && hasMore) {
    const oldestMessage = messages[0];
    loadMoreMessages(oldestMessage.timestamp);
  }
});
```

---

## 6. 데이터베이스 스키마

### 6.1 users 테이블
```sql
CREATE TABLE users (
    id UUID PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    username VARCHAR(50) UNIQUE,
    name VARCHAR(255)
);
```

### 6.2 workspace_members 테이블
```sql
CREATE TABLE workspace_members (
    user_id UUID REFERENCES users(id),
    workspace_id UUID,
    robeing_id VARCHAR(50)  -- rb8001, rb10508_micro 등
);
```

### 6.3 conversation_logs 테이블
```sql
CREATE TABLE conversation_logs (
    id INTEGER PRIMARY KEY,
    robeing_id VARCHAR,
    message VARCHAR,
    response VARCHAR,
    timestamp TIMESTAMP,
    user_id UUID REFERENCES users(id)
);
```

---

## 7. 환경변수 설정

### 7.1 Gateway
```env
JWT_SECRET_KEY=9cc562b6296b87b02dd89045a2e7e11c249713a59a5ac0160d852121f1289664
DEFAULT_ROBEING_HOST=192.168.219.52
DEFAULT_ROBEING_PORT=8001
```

### 7.2 rb8001
```env
JWT_SECRET_KEY=9cc562b6296b87b02dd89045a2e7e11c249713a59a5ac0160d852121f1289664
MESSAGE_BATCH_SIZE=30
MAX_MESSAGES_IN_DOM=200
```

---

## 8. 트러블슈팅 참고

자세한 문제 해결 과정은 다음 문서 참조:
- troubleshooting/250901_rb8001_chat_history_implementation_issues.md