docs: 대화 로그 사용자 매핑 불일치 문제 분석
This commit is contained in:
parent
c0f345e4c4
commit
2addd8edbf
131
troubleshooting/250827_claude_conversation_log_user_mapping.md
Normal file
131
troubleshooting/250827_claude_conversation_log_user_mapping.md
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# 대화 로그 사용자 매핑 불일치 문제
|
||||||
|
|
||||||
|
## 발생일시
|
||||||
|
2025-08-27 18:09 KST
|
||||||
|
|
||||||
|
## 문제 상황
|
||||||
|
동일한 사용자(김종태/happybell80)가 서로 다른 채널(Slack/웹)로 접속 시 conversation_logs 테이블에 별개 사용자로 저장되는 문제
|
||||||
|
|
||||||
|
### 증상
|
||||||
|
```sql
|
||||||
|
-- conversation_logs 테이블 데이터
|
||||||
|
ID 51: Slack=U0925SXQFDK, UUID=None, Channel=C0920L68267
|
||||||
|
ID 52: Slack=None, UUID=1e16e9d5-59f3-54da-a661-8abeabff4230, Channel=web
|
||||||
|
```
|
||||||
|
|
||||||
|
같은 사용자임에도:
|
||||||
|
- Slack 접속: `slack_user_id`만 저장, `user_id`는 NULL
|
||||||
|
- 웹 접속: `user_id`(UUID)만 저장, `slack_user_id`는 NULL
|
||||||
|
|
||||||
|
## 원인 분석
|
||||||
|
|
||||||
|
### 1. State Service 저장 로직
|
||||||
|
State Service가 사용자 식별자를 통합하지 않고 있음:
|
||||||
|
- Slack 이벤트: slack_user_id만 전달
|
||||||
|
- 웹 요청: JWT의 UUID만 전달
|
||||||
|
- 두 식별자를 연결하는 매핑 로직 부재
|
||||||
|
|
||||||
|
### 2. UUID 생성 규칙
|
||||||
|
```python
|
||||||
|
# Slack ID → UUID 변환 규칙
|
||||||
|
namespace = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
|
||||||
|
user_uuid = str(uuid.uuid5(namespace, slack_user_id))
|
||||||
|
|
||||||
|
# 예시
|
||||||
|
U0925SXQFDK → 1e16e9d5-59f3-54da-a661-8abeabff4230
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 데이터 흐름
|
||||||
|
```
|
||||||
|
Slack 경로:
|
||||||
|
Slack Event → rb8001 → State Service → DB (slack_user_id만)
|
||||||
|
|
||||||
|
웹 경로:
|
||||||
|
Browser → Gateway → rb8001 → State Service → DB (UUID만)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 영향도
|
||||||
|
- **통계 불일치**: 동일 사용자가 2명으로 계산
|
||||||
|
- **대화 연속성 손실**: 채널 간 대화 기록 분리
|
||||||
|
- **개인화 실패**: 사용자별 컨텍스트 유지 실패
|
||||||
|
|
||||||
|
## 해결 방안
|
||||||
|
|
||||||
|
### Option 1: State Service에서 매핑 (권장)
|
||||||
|
```python
|
||||||
|
# State Service의 save_conversation 수정
|
||||||
|
async def save_conversation(...):
|
||||||
|
# slack_user_id가 있으면 UUID로 변환
|
||||||
|
if slack_user_id and not user_id:
|
||||||
|
user_id = slack_id_to_uuid(slack_user_id)
|
||||||
|
|
||||||
|
# UUID가 있으면 slack_user_id 조회
|
||||||
|
elif user_id and not slack_user_id:
|
||||||
|
slack_user_id = await get_slack_id_from_uuid(user_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: rb8001에서 사전 변환
|
||||||
|
```python
|
||||||
|
# rb8001의 state_client.save_conversation 호출 전
|
||||||
|
if slack_user_id:
|
||||||
|
user_uuid = slack_id_to_uuid(slack_user_id)
|
||||||
|
else:
|
||||||
|
user_uuid = jwt_user_id
|
||||||
|
slack_user_id = await lookup_slack_id(user_uuid)
|
||||||
|
|
||||||
|
await state_client.save_conversation(
|
||||||
|
user_id=user_uuid,
|
||||||
|
slack_user_id=slack_user_id,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: DB 트리거로 자동 채우기
|
||||||
|
```sql
|
||||||
|
CREATE OR REPLACE FUNCTION fill_user_mapping()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
-- slack_user_id만 있으면 user_id 채우기
|
||||||
|
IF NEW.slack_user_id IS NOT NULL AND NEW.user_id IS NULL THEN
|
||||||
|
-- UUID 생성 로직
|
||||||
|
NEW.user_id = generate_uuid_from_slack(NEW.slack_user_id);
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 검증 쿼리
|
||||||
|
```sql
|
||||||
|
-- 불일치 건수 확인
|
||||||
|
SELECT COUNT(*) as total,
|
||||||
|
COUNT(user_id) as has_uuid,
|
||||||
|
COUNT(slack_user_id) as has_slack,
|
||||||
|
COUNT(*) FILTER (WHERE user_id IS NULL) as missing_uuid,
|
||||||
|
COUNT(*) FILTER (WHERE slack_user_id IS NULL) as missing_slack
|
||||||
|
FROM conversation_logs
|
||||||
|
WHERE timestamp > NOW() - INTERVAL '7 days';
|
||||||
|
|
||||||
|
-- 동일 사용자 중복 확인
|
||||||
|
SELECT u.username, u.email,
|
||||||
|
COUNT(DISTINCT cl.user_id) as uuid_count,
|
||||||
|
COUNT(DISTINCT cl.slack_user_id) as slack_count
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN conversation_logs cl ON u.id = cl.user_id
|
||||||
|
GROUP BY u.username, u.email
|
||||||
|
HAVING COUNT(DISTINCT cl.slack_user_id) > 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 교훈
|
||||||
|
1. **다채널 시스템에서는 사용자 식별자 통합이 필수**
|
||||||
|
2. **State Service는 모든 채널의 사용자 매핑을 처리해야 함**
|
||||||
|
3. **DB 설계 시 다중 식별자 고려 필요**
|
||||||
|
|
||||||
|
## 참고
|
||||||
|
- users 테이블: UUID 기반 사용자 정보
|
||||||
|
- slack_user_mapping 테이블: Slack-UUID 매핑 (활용 안됨)
|
||||||
|
- conversation_logs: 실제 대화 저장 테이블
|
||||||
|
|
||||||
|
---
|
||||||
|
*작성: Claude*
|
||||||
|
*검토 필요: State Service 개발자*
|
||||||
Loading…
x
Reference in New Issue
Block a user