docs: rb8001 대화 히스토리 구현 전체 문서화
- plans: 완료 상태로 정리 - troubleshooting: 전체 과정과 실수 상세 기록 - sequences: JWT → Gateway → rb8001 → DB 플로우 문서화 - 정확한 정보만 남기고 추측/틀린 내용 모두 명시
This commit is contained in:
parent
79758fa496
commit
04832ee48e
222
300_architecture/sequences/chat_history_flow.md
Normal file
222
300_architecture/sequences/chat_history_flow.md
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
# 대화 히스토리 조회 플로우
|
||||||
|
|
||||||
|
## 작성일: 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
|
||||||
@ -1,142 +1,29 @@
|
|||||||
# rb8001 카톡 스타일 대화 히스토리 구현 계획
|
# rb8001 카톡 스타일 대화 히스토리 구현 완료
|
||||||
|
|
||||||
## 작성일: 2025-09-01
|
## 작성일: 2025-09-01
|
||||||
|
## 완료일: 2025-09-02
|
||||||
## 대상: rb8001 (프로덕션 로빙)
|
## 대상: rb8001 (프로덕션 로빙)
|
||||||
## 참조: rb10508_micro 구현 코드
|
## 상태: ✅ 완료
|
||||||
## 수정: PostgreSQL 전용 히스토리 구현으로 변경
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 현재 상태 분석
|
## 구현 완료 사항
|
||||||
- **rb10508_micro**: /api/config 엔드포인트 구현됨 (endpoints.py:407)
|
|
||||||
- **rb8001**:
|
|
||||||
- database.py: ConversationLog 모델 있음 (line 52)
|
|
||||||
- get_recent_conversations() 함수 있음 (line 81)
|
|
||||||
- get_paginated_conversations() 함수 없음 (추가 필요)
|
|
||||||
- main.py: @app.get() 직접 사용, router 분리 없음
|
|
||||||
|
|
||||||
## UUID 및 인증 체계
|
### rb8001 백엔드
|
||||||
- **내부 처리**: 모든 user_id는 UUID 형식으로 통일
|
1. ✅ `/api/config` 엔드포인트 추가
|
||||||
- **JWT 토큰**: X-User-Id 헤더로 UUID 전달
|
2. ✅ `/api/history` 엔드포인트 추가
|
||||||
- **Slack 사용자**: slack_user_mapping 테이블로 UUID 매핑
|
3. ✅ get_paginated_conversations() 함수 구현
|
||||||
- **Frontend**: JWT에서 user_id(UUID) 추출하여 API 호출
|
4. ✅ SQL 파라미터 바인딩 문법 수정 ((:user_id)::uuid)
|
||||||
|
|
||||||
## 수정 필요 파일
|
### Gateway
|
||||||
|
1. ✅ /api/history 라우팅 추가
|
||||||
|
2. ✅ Authorization 헤더 전달 추가
|
||||||
|
|
||||||
### 백엔드 (rb8001)
|
### Frontend
|
||||||
1. ✅ `app/core/config.py` - 환경변수 추가 완료
|
1. ✅ 이미 구현됨 (rb10508_micro 참조)
|
||||||
2. ✅ `app/state/database.py` - get_paginated_conversations() 추가 완료
|
|
||||||
3. `main.py` - 엔드포인트 수정 필요
|
|
||||||
- ✅ `/api/config` 이미 추가됨
|
|
||||||
- ❌ `/api/messages` 삭제 필요 (잘못 추가함)
|
|
||||||
- ⚠️ `/api/history` 추가 필요 (이것만!)
|
|
||||||
4. ✅ `.env` 파일 - 환경변수 설정 완료
|
|
||||||
|
|
||||||
### 프론트엔드 (frontend-customer)
|
|
||||||
1. ✅ 이미 완료됨 (250818 rb10508_micro에서 구현)
|
|
||||||
2. ✅ `/api/history` 호출 중 (수정 불필요)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## API 스펙
|
## 참고 문서
|
||||||
|
- troubleshooting/250901_rb8001_chat_history_implementation_issues.md
|
||||||
```typescript
|
- 300_architecture/sequences/chat_history_flow.md
|
||||||
// GET /api/config
|
|
||||||
{ message_batch_size: 30, max_messages_in_dom: 200 }
|
|
||||||
|
|
||||||
// GET /api/history?before={timestamp}&limit=30
|
|
||||||
// Frontend 기대 형식 (robeing-api.ts:154-161)
|
|
||||||
{
|
|
||||||
messages: Array<{
|
|
||||||
id: string;
|
|
||||||
text: string;
|
|
||||||
sender: 'user' | 'robeing';
|
|
||||||
timestamp: string;
|
|
||||||
metadata?: any;
|
|
||||||
}>;
|
|
||||||
has_more: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 현재 rb8001 /api/messages 반환 형식 (변환 필요!)
|
|
||||||
{
|
|
||||||
"user_message": "...",
|
|
||||||
"robeing_response": "...",
|
|
||||||
"timestamp": "...",
|
|
||||||
"user_id": "..."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### PostgreSQL 쿼리 예시
|
|
||||||
```sql
|
|
||||||
-- user_id는 항상 UUID 형식
|
|
||||||
SELECT id, message, response, timestamp, user_id
|
|
||||||
FROM conversation_logs
|
|
||||||
WHERE user_id = $1::uuid -- UUID 타입 캐스팅
|
|
||||||
AND robeing_id = 'rb8001'
|
|
||||||
AND timestamp < $2
|
|
||||||
ORDER BY timestamp DESC
|
|
||||||
LIMIT 30;
|
|
||||||
```
|
|
||||||
|
|
||||||
### API 호출 예시
|
|
||||||
```javascript
|
|
||||||
// Frontend에서 JWT의 user_id 사용
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
const decoded = jwt_decode(token);
|
|
||||||
const userId = decoded.user_id; // UUID 형식
|
|
||||||
|
|
||||||
// API 호출 (Frontend가 이미 호출 중)
|
|
||||||
await fetch(`/api/history?before=${timestamp}&limit=30`);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 데이터베이스 스키마
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- conversation_logs 테이블 (실제)
|
|
||||||
CREATE TABLE conversation_logs (
|
|
||||||
id INTEGER PRIMARY KEY (auto-increment),
|
|
||||||
robeing_id VARCHAR,
|
|
||||||
channel_id VARCHAR,
|
|
||||||
message VARCHAR,
|
|
||||||
response VARCHAR,
|
|
||||||
intent VARCHAR,
|
|
||||||
confidence DOUBLE PRECISION,
|
|
||||||
timestamp TIMESTAMP,
|
|
||||||
user_id UUID (FK → users),
|
|
||||||
slack_user_id VARCHAR(100),
|
|
||||||
thread_ts VARCHAR(128),
|
|
||||||
channel_type VARCHAR(32)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 주의사항
|
|
||||||
|
|
||||||
### API 경로
|
|
||||||
```python
|
|
||||||
# main.py에 직접 추가 (rb8001은 router 분리 없음)
|
|
||||||
@app.get("/api/config")
|
|
||||||
@app.get("/api/messages")
|
|
||||||
```
|
|
||||||
|
|
||||||
### rb10508_micro 참조 코드
|
|
||||||
```python
|
|
||||||
# rb10508_micro/app/config.py:101-103
|
|
||||||
MESSAGE_BATCH_SIZE: int = int(os.getenv("MESSAGE_BATCH_SIZE", 30))
|
|
||||||
SCROLL_THRESHOLD: int = int(os.getenv("SCROLL_THRESHOLD", 100))
|
|
||||||
MAX_MESSAGES_IN_DOM: int = int(os.getenv("MAX_MESSAGES_IN_DOM", 200))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 실제 필요 작업
|
|
||||||
1. ✅ 백엔드 대부분 완료 (bbf9c50 커밋)
|
|
||||||
2. ⚠️ **rb8001 main.py 수정 필요**:
|
|
||||||
- `/api/messages` → `/api/history` 이름 변경
|
|
||||||
- **응답 형식 변환 필수**:
|
|
||||||
- DB 각 row를 2개 메시지로 분리 (user, robeing)
|
|
||||||
- text, sender, timestamp 형식 맞추기
|
|
||||||
- 배열로 반환
|
|
||||||
3. ✅ Frontend 수정 불필요 (이미 /api/history 호출 중)
|
|
||||||
4. ✅ 배포는 Gitea Actions 자동 처리
|
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
# rb8001 대화 히스토리 구현 트러블슈팅
|
||||||
|
|
||||||
|
## 작성일: 2025-09-02
|
||||||
|
## 작성자: 51123 서버 관리자
|
||||||
|
## 대상: rb8001 카톡 스타일 대화 히스토리
|
||||||
|
## 최종 상태: ✅ 해결 완료
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 초기 계획과 실제 구현의 차이
|
||||||
|
|
||||||
|
### 1.1 잘못된 초기 계획
|
||||||
|
- **착각**: `/api/messages` 엔드포인트 추가하려 함
|
||||||
|
- **실제**: `/api/history`가 정답 (rb10508_micro 문서 확인 미흡)
|
||||||
|
- **원인**: 250818 문서를 제대로 읽지 않음
|
||||||
|
|
||||||
|
### 1.2 놓친 문서 확인
|
||||||
|
- Frontend 응답 형식 확인 안 함 (robeing-api.ts:154-161)
|
||||||
|
- Gateway 라우팅 구조 확인 안 함
|
||||||
|
- rb10508_micro 구현 내용 상세 확인 미흡
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Gateway 라우팅 문제
|
||||||
|
|
||||||
|
### 2.1 문제 상황
|
||||||
|
```bash
|
||||||
|
# Frontend 요청
|
||||||
|
GET /gateway/api/history?limit=1
|
||||||
|
|
||||||
|
# 응답
|
||||||
|
{} # 빈 객체 (content-length: 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 원인 분석
|
||||||
|
- Gateway에 `/api/history` 라우팅 없었음
|
||||||
|
- `/api/config`는 있었지만 `/api/history`는 누락
|
||||||
|
|
||||||
|
### 2.3 해결
|
||||||
|
- Gateway main.py에 `/api/history` → rb8001 프록시 라우팅 추가
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. UUID 변환 문제
|
||||||
|
|
||||||
|
### 3.1 잘못된 추측
|
||||||
|
- **추측**: users 테이블 id가 integer
|
||||||
|
- **실제**: users.id는 UUID 타입
|
||||||
|
- **확인 방법**: `\d users` 명령으로 확인 필요했음
|
||||||
|
|
||||||
|
### 3.2 Gateway UUID 변환 제거
|
||||||
|
- JWT의 sub 필드는 이미 UUID
|
||||||
|
- username 변환 시도가 불필요했음
|
||||||
|
- 직접 UUID 전달로 해결
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. JWT Authorization 헤더 미전달
|
||||||
|
|
||||||
|
### 4.1 문제 상황
|
||||||
|
```python
|
||||||
|
# rb8001 응답
|
||||||
|
{"detail": "Invalid authentication credentials"} # 401 Unauthorized
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 원인
|
||||||
|
- Gateway가 X-User-Id 헤더만 전달
|
||||||
|
- Authorization 헤더 전달 안 함
|
||||||
|
- rb8001은 JWT 토큰 검증 필요
|
||||||
|
|
||||||
|
### 4.3 해결
|
||||||
|
```python
|
||||||
|
# Gateway main.py Line 463
|
||||||
|
headers = {
|
||||||
|
"X-User-Id": x_user_id,
|
||||||
|
"Authorization": request.headers.get("Authorization") # 추가
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. SQL 파라미터 파싱 에러
|
||||||
|
|
||||||
|
### 5.1 에러 메시지
|
||||||
|
```
|
||||||
|
syntax error at or near ":"
|
||||||
|
WHERE user_id = :user_id::uuid
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 원인 분석
|
||||||
|
- SQLAlchemy text()는 `:user_id`를 파라미터로 인식
|
||||||
|
- PostgreSQL `::uuid` 캐스팅과 충돌
|
||||||
|
- `:user_id::uuid` 전체를 하나의 토큰으로 파싱
|
||||||
|
|
||||||
|
### 5.3 해결 방법
|
||||||
|
```sql
|
||||||
|
-- 잘못된 문법
|
||||||
|
WHERE user_id = :user_id::uuid
|
||||||
|
|
||||||
|
-- 올바른 문법 (괄호로 분리)
|
||||||
|
WHERE user_id = (:user_id)::uuid
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Frontend 응답 형식 불일치
|
||||||
|
|
||||||
|
### 6.1 Frontend 기대 형식
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
messages: Array<{
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
sender: 'user' | 'robeing';
|
||||||
|
timestamp: string;
|
||||||
|
}>;
|
||||||
|
has_more: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 rb8001 초기 반환 형식
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user_message": "...",
|
||||||
|
"robeing_response": "...",
|
||||||
|
"timestamp": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 해결
|
||||||
|
- DB 각 row를 2개 메시지로 분리 (user, robeing)
|
||||||
|
- text, sender, timestamp 형식으로 변환
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 전체 작업 순서 (실제)
|
||||||
|
|
||||||
|
### 7.1 rb8001 백엔드
|
||||||
|
1. ✅ config.py 환경변수 추가 (MESSAGE_BATCH_SIZE=30)
|
||||||
|
2. ✅ database.py get_paginated_conversations() 구현
|
||||||
|
3. ❌ `/api/messages` 잘못 추가 → ✅ `/api/history`로 수정
|
||||||
|
4. ✅ SQL 문법 수정: `(:user_id)::uuid`
|
||||||
|
|
||||||
|
### 7.2 Gateway
|
||||||
|
1. ✅ `/api/history` 라우팅 추가
|
||||||
|
2. ✅ UUID 변환 제거 (JWT sub 직접 사용)
|
||||||
|
3. ✅ Authorization 헤더 전달 추가
|
||||||
|
|
||||||
|
### 7.3 Frontend
|
||||||
|
- 수정 불필요 (이미 250818에 구현됨)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 교훈
|
||||||
|
|
||||||
|
### 8.1 문서 확인의 중요성
|
||||||
|
- 기존 구현 문서 먼저 상세히 읽기
|
||||||
|
- Frontend 코드 확인 후 백엔드 작업
|
||||||
|
- Gateway 라우팅 구조 먼저 파악
|
||||||
|
|
||||||
|
### 8.2 테스트 우선
|
||||||
|
- curl로 직접 테스트
|
||||||
|
- JWT 토큰 생성해서 확인
|
||||||
|
- 로그 확인 필수
|
||||||
|
|
||||||
|
### 8.3 추측 금지
|
||||||
|
- DB 스키마는 직접 확인
|
||||||
|
- 에러 메시지 정확히 읽기
|
||||||
|
- 문서화된 내용 신뢰
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 최종 작동 확인
|
||||||
|
|
||||||
|
### 9.1 테스트 결과
|
||||||
|
```bash
|
||||||
|
# Gateway 통해 요청
|
||||||
|
GET /gateway/api/history?limit=30
|
||||||
|
Authorization: Bearer {JWT_TOKEN}
|
||||||
|
|
||||||
|
# 정상 응답
|
||||||
|
{
|
||||||
|
"messages": [...],
|
||||||
|
"has_more": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 Frontend
|
||||||
|
- 무한 스크롤 정상 작동
|
||||||
|
- 날짜 구분선 표시
|
||||||
|
- 대화 히스토리 로드 성공
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 참고 문서
|
||||||
|
- plans/250901_rb8001_chat_history_implementation_plan.md (완료)
|
||||||
|
- troubleshooting/250818_happybell80_대화히스토리구현.md (rb10508_micro)
|
||||||
|
- 300_architecture/sequences/chat_history_flow.md (신규 작성)
|
||||||
Loading…
x
Reference in New Issue
Block a user