272 lines
8.5 KiB
Markdown
272 lines
8.5 KiB
Markdown
# UUID vs Username 혼용 - CRITICAL 시스템 전체 ID 체계 혼란
|
|
|
|
## 작성일: 2025-08-27
|
|
## 작성자: 51123 서버 관리자
|
|
## 상태: ⚠️ PARTIAL - 2025-08-27 16:45 Gateway 쿼리 문제 발견
|
|
## 영향 범위: 전체 시스템 (Auth, Gateway, Frontend, rb8001, DB)
|
|
## 위험 수준: ~~매우 높음~~ → 해결됨
|
|
|
|
---
|
|
|
|
## 1. 문제 요약
|
|
|
|
### ~~🚨 핵심 문제~~ ✅ 해결 완료
|
|
**~~시스템 전체가 UUID와 username을 구분하지 못해 데이터 무결성 파괴~~**
|
|
- ~~JWT `sub` 클레임에 UUID 대신 username 저장~~ → ✅ UUID 사용으로 수정
|
|
- ~~Frontend가 하드코딩된 문자열 사용~~ → ✅ UUID 저장 및 전송
|
|
- ~~DB에 UUID 대신 username/slack_id 저장~~ → ✅ UUID 정상 처리
|
|
|
|
### 실제 사례
|
|
- User: happybell80
|
|
- UUID: 1e16e9d5-59f3-54da-a661-8abeabff4230 (UUID4 랜덤 생성)
|
|
- JWT sub: "happybell80" (username, UUID 아님)
|
|
- ChromaDB user_id: "happybell80" (username으로 저장됨)
|
|
- PostgreSQL: "Non-UUID user_id" 로그 발생
|
|
- conversation_logs: user_id NULL, slack_user_id에 우회 저장
|
|
|
|
---
|
|
|
|
## 2. 영향 분석
|
|
|
|
### 2.1 auth-server (51123) - 확인 완료
|
|
```python
|
|
# /home/admin/auth-server/app/providers/gmail.py
|
|
# Line 191: user_id_str = str(user.id) # UUID 접근 가능
|
|
# Line 209: "sub": username # 🔴 UUID 대신 username 사용
|
|
jwt_token = create_access_token(data={
|
|
"sub": username, # username 사용 (UUID 사용 가능한데도!)
|
|
"email": user_email,
|
|
"name": user_name,
|
|
"username": username,
|
|
})
|
|
# Line 223: Redis에도 "user_id": username 저장
|
|
```
|
|
**확인된 문제**:
|
|
- user.id (UUID) 접근 가능함에도 JWT sub에 username 사용
|
|
- Redis에도 username을 user_id로 저장
|
|
|
|
### 2.2 robeing-gateway (51123) - 확인 완료
|
|
```python
|
|
# /home/admin/robeing-gateway/app/main.py:232-235
|
|
user_info = await get_user_by_username(x_user_id)
|
|
if user_info:
|
|
user_uuid = user_info['user_id'] # DB 조회로 UUID 획득
|
|
|
|
# database.py:184
|
|
SELECT id::text as user_id # PostgreSQL UUID를 문자열로 변환
|
|
```
|
|
**확인된 사항**:
|
|
- username → UUID 변환은 DB 조회로만 수행 (계산된 변환 없음)
|
|
- UUID4 형식만 사용 (UUID5 사용 안 함)
|
|
- users 테이블에 username 없으면 변환 실패
|
|
|
|
### 2.3 rb8001 (51124) - 확인 완료
|
|
```python
|
|
# auth.py:36
|
|
user_id = payload.get("sub") # JWT sub(username)를 그대로 사용
|
|
# UUID 변환 없이 ChromaDB에 저장
|
|
metadata={"user_id": user_id} # username이 저장됨
|
|
```
|
|
**확인된 문제**:
|
|
- ChromaDB에 "happybell80" (username) 형식으로 저장
|
|
- PostgreSQL 저장 시 "Non-UUID user_id" 에러 로그 발생
|
|
- user_id NULL, slack_user_id로 우회 저장
|
|
|
|
### 2.4 Frontend - 확인 완료
|
|
```typescript
|
|
// robeing-api.ts:157
|
|
const userId = localStorage.getItem('user_id') || 'default_user';
|
|
// localStorage의 'user_id'는 Slack ID (U0925SXQFDK 형식)
|
|
```
|
|
**확인된 문제**:
|
|
- localStorage 'user_id' = Slack ID (예: U0925SXQFDK)
|
|
- JWT sub도 아니고 UUID도 아님
|
|
- 완전히 다른 체계 사용 중
|
|
|
|
### 2.5 Database
|
|
```sql
|
|
-- conversation_logs 테이블
|
|
user_id UUID -- UUID 타입 요구
|
|
slack_user_id VARCHAR(100) -- username이 여기 저장됨
|
|
|
|
-- 실제 데이터
|
|
user_id: NULL -- UUID 아니라서 저장 실패
|
|
slack_user_id: 'happybell80' -- 우회 저장
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 근본 원인
|
|
|
|
### 3.1 설계 문제
|
|
- user_id, username, slack_id 개념 미구분
|
|
- UUID 사용 표준 부재
|
|
- 타입 검증 없는 문자열 전달
|
|
|
|
### 3.2 구현 문제 (확인 완료)
|
|
- auth-server: JWT `sub`에 username 사용 (UUID 대신)
|
|
- Gateway: DB 조회로만 UUID 변환 (실패 시 원래 값 전달)
|
|
- Frontend: localStorage에 Slack ID 저장 (UUID도 username도 아님)
|
|
- rb8001: JWT `sub`를 변환 없이 그대로 user_id로 사용
|
|
- skill-email: slack_user_id 컬럼으로 Slack ID 조회
|
|
|
|
---
|
|
|
|
## 4. 해결 방안
|
|
|
|
### 4.1 긴급 조치 (Phase 1)
|
|
|
|
#### auth-server 수정 (즉시 가능)
|
|
```python
|
|
# gmail.py:209 수정 필요 (Line 191에서 이미 user.id 사용 가능)
|
|
jwt_token = create_access_token(data={
|
|
"sub": str(user.id), # Line 191의 user_id_str 사용
|
|
"username": username, # username은 별도 필드
|
|
"email": user_email,
|
|
"name": user_name,
|
|
})
|
|
|
|
# Line 223 Redis 저장도 수정
|
|
"user_id": str(user.id), # username 대신 UUID
|
|
```
|
|
|
|
#### Gateway username → UUID 매핑 (확인 완료)
|
|
```python
|
|
# database.py:170-199 이미 완벽 구현
|
|
async def get_user_by_username(username: str):
|
|
# SELECT id::text as user_id FROM users WHERE username = :username
|
|
# username으로 UUID 조회 정상 작동
|
|
# JWT sub가 UUID로 바뀌면 이 변환 불필요해짐
|
|
```
|
|
|
|
### 4.2 중기 개선 (Phase 2)
|
|
|
|
#### Frontend localStorage 정리
|
|
```typescript
|
|
// JWT 디코딩 후
|
|
const payload = jwt_decode(token);
|
|
localStorage.setItem('user_id', payload.sub); // UUID
|
|
localStorage.setItem('username', payload.username);
|
|
```
|
|
|
|
#### rb8001 JWT 처리 수정
|
|
```python
|
|
# auth.py 수정
|
|
user_uuid = payload.get("sub") # UUID
|
|
username = payload.get("username") # username
|
|
```
|
|
|
|
### 4.3 장기 개선 (Phase 3)
|
|
- 전체 시스템 ID 타입 표준화
|
|
- UUID 전용 타입 클래스 생성
|
|
- 타입 검증 미들웨어 추가
|
|
|
|
---
|
|
|
|
## 5. 테스트 시나리오
|
|
|
|
### 5.1 현재 상태 확인 (51123 검증 완료)
|
|
```bash
|
|
# Users 테이블 확인 (51123)
|
|
SELECT id, username FROM users WHERE username='happybell80';
|
|
# id: 1e16e9d5-59f3-54da-a661-8abeabff4230 (정상 UUID4)
|
|
# username: happybell80
|
|
|
|
# auth-server 코드 확인
|
|
# Line 191: user_id_str = str(user.id) # UUID 문자열 생성
|
|
# Line 209: "sub": username # 하지만 JWT에 username 사용
|
|
# Line 223: "user_id": username # Redis에도 username
|
|
|
|
# Gateway 변환 함수 확인
|
|
# database.py:184: SELECT id::text as user_id
|
|
# username → UUID 변환 정상 작동
|
|
|
|
# conversation_logs 확인 (51124 보고)
|
|
# user_id: NULL, slack_user_id: 'happybell80'
|
|
# "Non-UUID user_id" 에러 발생
|
|
```
|
|
|
|
### 5.2 수정 후 검증
|
|
- JWT `sub` 필드가 UUID 형식 (36자)
|
|
- conversation_logs.user_id에 UUID 저장
|
|
- ChromaDB metadata.user_id가 UUID
|
|
|
|
---
|
|
|
|
## 6. 영향도 및 우선순위
|
|
|
|
| 컴포넌트 | 영향도 | 우선순위 | 담당 |
|
|
|---------|-------|---------|-----|
|
|
| auth-server JWT | 🔴 극심 | 1 | 로컬 개발자 |
|
|
| Frontend localStorage | 🔴 극심 | 2 | 로컬 개발자 |
|
|
| rb8001 JWT 처리 | 🟡 높음 | 3 | 로컬 개발자 |
|
|
| Gateway 매핑 | 🟢 중간 | 4 | 이미 구현됨 |
|
|
|
|
---
|
|
|
|
## 7. 관련 문서
|
|
|
|
- [ID 체계 정리 (250826)](./250826_id_체계_정리_및_conversation_logs_문제_해결.md)
|
|
- [JWT 인증 구현 (250827)](./250827_JWT_인증_구현_COMPLETED.md)
|
|
- [Slack ID 표준화 (250826)](./250826_slack_id_column_standardization.md)
|
|
|
|
---
|
|
|
|
## 8. 결론
|
|
|
|
**⚠️ UUID vs username 혼용 문제 부분 해결**
|
|
|
|
### 적용된 해결책:
|
|
1. auth-server: JWT `sub`에 UUID 사용 ✅
|
|
2. Frontend: localStorage에 UUID 저장 ✅
|
|
3. rb8001: UUID와 username 구분 처리 ✅
|
|
4. Gateway: UUID 기반 인증 ✅, DB 조회 ❌
|
|
|
|
### 추가 발견 및 해결:
|
|
- workspace_members.robeing_url 불일치 문제 해결
|
|
- ~~happybell80 → rb10508_micro로 정상 라우팅~~
|
|
|
|
### 🔴 잔여 문제 (16:45 발견):
|
|
- Gateway `get_robeing_info()` 함수가 UUID를 username으로 조회
|
|
- database.py:108 `WHERE u.username = :username` - UUID 처리 불가
|
|
- 결과: "No active workspace found" → 기본 10508 사용
|
|
|
|
## 9. 해결 과정 (2025-08-27 15:45 ~ 16:45)
|
|
|
|
### 완료된 작업
|
|
|
|
### Phase 1: auth-server JWT 수정 (15:45)
|
|
- Line 209: `"sub": username` → `"sub": str(user.id)`
|
|
- Line 223: `"user_id": username` → `"user_id": str(user.id)`
|
|
- ✅ 배포 완료
|
|
|
|
### Phase 2: Frontend localStorage 수정 (16:00)
|
|
- JWT 디코딩 후 UUID와 username 분리 저장
|
|
- localStorage.setItem('user_id', payload.sub) - UUID
|
|
- ✅ 자동 배포 완료
|
|
|
|
### Phase 3: rb8001 JWT 처리 수정 (16:15)
|
|
- verify_jwt_token 반환 타입: str → tuple[str, str]
|
|
- user_id = payload.get("sub") - UUID
|
|
- username = payload.get("username") - username
|
|
- ✅ 51124 배포 완료
|
|
|
|
### Phase 4: 통합 검증 (16:30)
|
|
- JWT sub: `1e16e9d5-59f3-54da-a661-8abeabff4230` (UUID) ✅
|
|
- Gateway 인증: UUID 사용 확인 ✅
|
|
- Frontend: UUID 전송 확인 ✅
|
|
|
|
### 미해결 문제 (16:45)
|
|
- Gateway `get_robeing_info(username: str)` 함수
|
|
- JWT sub에서 UUID 받음: `1e16e9d5-59f3-54da-a661-8abeabff4230`
|
|
- DB 쿼리: `WHERE u.username = :username` (UUID로 조회 불가)
|
|
- 결과: workspace 찾기 실패 → default robeing(10508) 사용
|
|
|
|
### 해결 방안
|
|
1. database.py 수정: `WHERE u.username = :username OR u.id::text = :username`
|
|
2. 또는 Gateway가 JWT payload의 username 사용
|
|
3. 또는 별도 함수 `get_robeing_info_by_uuid()` 추가
|
|
|
|
---
|
|
|
|
*작성 완료: 2025-08-27 15:45* |