refactor: Gmail 토큰 문서 날짜 및 파일명 변경
- 250825 → 250918 날짜 변경 - slack_id_migration → slack_user_id_column_missing - 실제 문제를 반영한 파일명으로 변경
This commit is contained in:
parent
fe487247d5
commit
11d5c32100
@ -1,210 +0,0 @@
|
|||||||
# Gmail Token 시스템 Slack ID 직접 사용 마이그레이션
|
|
||||||
|
|
||||||
## 개요
|
|
||||||
2025년 8월 25일, Gmail 토큰 조회 시스템을 UUID 변환 방식에서 Slack ID 직접 사용 방식으로 마이그레이션했습니다.
|
|
||||||
|
|
||||||
## 문제 상황
|
|
||||||
|
|
||||||
### 기존 시스템의 문제점
|
|
||||||
1. **UUID 불일치 문제**
|
|
||||||
- auth-server: `uuid.uuid4()` (랜덤 UUID) 사용
|
|
||||||
- skill-email/rb8001: `uuid.uuid5(namespace, slack_id)` (네임스페이스 기반 UUID) 사용
|
|
||||||
- 결과: 동일한 사용자에 대해 서로 다른 UUID가 생성되어 토큰 조회 실패
|
|
||||||
|
|
||||||
2. **데이터베이스 스키마 변경**
|
|
||||||
- 기존: `token_data` JSON 컬럼에 모든 토큰 정보 저장
|
|
||||||
- 변경: 개별 컬럼으로 분리 (`access_token`, `refresh_token`, `oauth_config`, `scopes`, `expiry`)
|
|
||||||
- ~~새 컬럼 추가: `slack_id` (Slack 사용자 ID 직접 저장)~~ **[2025-09-18 정정]**
|
|
||||||
- **실제 스키마**: `user_id`(UUID), `equipped_to`(Gmail 계정)
|
|
||||||
|
|
||||||
## 해결 방안
|
|
||||||
|
|
||||||
### 1. Slack ID 직접 사용
|
|
||||||
UUID 변환을 완전히 제거하고 Slack ID를 직접 사용하도록 변경
|
|
||||||
|
|
||||||
### 2. 변경된 파일
|
|
||||||
|
|
||||||
#### `/home/heejae/skill-email/services/db_credentials_provider.py`
|
|
||||||
|
|
||||||
**제거된 코드:**
|
|
||||||
```python
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
def __init__(self, connection_string: str):
|
|
||||||
# ...
|
|
||||||
self.uuid_namespace = uuid.NAMESPACE_DNS # 제거됨
|
|
||||||
|
|
||||||
def _convert_to_uuid(self, user_id: str) -> str: # 메서드 전체 제거
|
|
||||||
"""Slack user_id를 UUID로 변환"""
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
**변경된 쿼리:**
|
|
||||||
```python
|
|
||||||
# 이전
|
|
||||||
user_uuid = self._convert_to_uuid(user_id)
|
|
||||||
cur.execute("""
|
|
||||||
SELECT ... FROM gmail_tokens
|
|
||||||
WHERE user_id = %s AND is_equipped = true
|
|
||||||
""", (user_uuid,))
|
|
||||||
|
|
||||||
# 현재 (잘못된 코드 - 2025-09-18)
|
|
||||||
cur.execute("""
|
|
||||||
SELECT ... FROM gmail_tokens
|
|
||||||
WHERE slack_user_id = %s AND is_equipped = true # 존재하지 않는 컬럼
|
|
||||||
""", (user_id,))
|
|
||||||
|
|
||||||
# 올바른 방법: JOIN을 통한 조회
|
|
||||||
cur.execute("""
|
|
||||||
SELECT gt.* FROM gmail_token gt
|
|
||||||
JOIN user u ON gt.user_id = u.id
|
|
||||||
WHERE u.oauth_id = %s AND gt.is_equipped = true
|
|
||||||
""", (slack_id,))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `/home/heejae/skill-email/services/gmail_service.py`
|
|
||||||
|
|
||||||
**추가된 자동 토큰 갱신:**
|
|
||||||
```python
|
|
||||||
def _get_gmail_service(self, user_id: str):
|
|
||||||
# ...
|
|
||||||
# 토큰이 만료되었거나 곧 만료될 경우 자동 refresh
|
|
||||||
if creds and creds.expired and creds.refresh_token:
|
|
||||||
logger.info(f"Refreshing expired token for user: {user_id}")
|
|
||||||
from google.auth.transport.requests import Request
|
|
||||||
creds.refresh(Request())
|
|
||||||
|
|
||||||
# 갱신된 토큰 저장
|
|
||||||
if hasattr(self.creds_provider, 'save_credentials'):
|
|
||||||
self.creds_provider.save_credentials(user_id, creds)
|
|
||||||
logger.info(f"Refreshed token saved for user: {user_id}")
|
|
||||||
```
|
|
||||||
|
|
||||||
**401 에러 시 재시도 로직:**
|
|
||||||
```python
|
|
||||||
except HttpError as e:
|
|
||||||
if e.resp.status == 401:
|
|
||||||
logger.info(f"Got 401 error, refreshing token for user: {user_id}")
|
|
||||||
# 캐시 삭제 후 재시도
|
|
||||||
if user_id in self._service_cache:
|
|
||||||
del self._service_cache[user_id]
|
|
||||||
# 토큰 refresh 포함하여 다시 시도
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 데이터베이스 스키마
|
|
||||||
|
|
||||||
**gmail_token 테이블 구조 (실제 - 2025-09-18 확인):**
|
|
||||||
```sql
|
|
||||||
CREATE TABLE gmail_token ( -- 테이블명 주의: gmail_token (s 없음)
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
user_id UUID NOT NULL, -- user 테이블 FK
|
|
||||||
token_data JSONB, -- access_token, refresh_token 포함
|
|
||||||
oauth_config JSONB,
|
|
||||||
scopes JSONB,
|
|
||||||
metadata JSONB,
|
|
||||||
expiry TIMESTAMP,
|
|
||||||
is_equipped BOOLEAN DEFAULT false,
|
|
||||||
equipped_to VARCHAR(50), -- Gmail 계정 이메일
|
|
||||||
token_type VARCHAR DEFAULT 'Bearer',
|
|
||||||
expires_at DOUBLE PRECISION,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 올바른 조회 방법: user 테이블과 JOIN
|
|
||||||
-- user.oauth_id에 Slack ID가 저장됨
|
|
||||||
```
|
|
||||||
|
|
||||||
## 영향 받는 서비스
|
|
||||||
|
|
||||||
1. **skill-email** (포트 8501)
|
|
||||||
- db_credentials_provider.py 수정
|
|
||||||
- gmail_service.py 자동 refresh 로직 추가
|
|
||||||
|
|
||||||
2. **rb8001** (포트 8001)
|
|
||||||
- dm_skill.py의 일일 요약 기능
|
|
||||||
- Gmail 이메일 조회 시 skill-email API 사용
|
|
||||||
|
|
||||||
3. **auth-server** (포트 8088)
|
|
||||||
- 변경 없음 (이미 slack_id 저장 중)
|
|
||||||
|
|
||||||
4. **robeing-monitor** (포트 9024)
|
|
||||||
- 이미 수정 완료 (새 컬럼 구조 읽기)
|
|
||||||
|
|
||||||
## 테스트 및 검증
|
|
||||||
|
|
||||||
### 1. 이메일 조회 테스트
|
|
||||||
```bash
|
|
||||||
# 개별 사용자 이메일 조회
|
|
||||||
curl -X GET "http://localhost:8501/messages?user_id=U091UNVE41M&limit=5&query=category:primary"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 일일 요약 DM 테스트
|
|
||||||
```bash
|
|
||||||
# Cron API 호출로 전체 사용자에게 DM 전송
|
|
||||||
curl -X POST "http://localhost:8001/api/cron/daily-summary" \
|
|
||||||
-H "Authorization: Bearer cron-secret-2024" \
|
|
||||||
-H "Content-Type: application/json"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 토큰 자동 갱신 확인
|
|
||||||
```bash
|
|
||||||
# skill-email 로그에서 자동 refresh 확인
|
|
||||||
docker logs skill-email | grep "Refreshing"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 개선 사항 ~~(실제로는 미완성 - 2025-09-18)~~
|
|
||||||
|
|
||||||
1. **UUID 불일치 문제 해결** ~~(미해결)~~
|
|
||||||
- ~~모든 서비스가 동일한 식별자(slack_id) 사용~~
|
|
||||||
- ~~UUID 변환 과정 제거로 오류 가능성 감소~~
|
|
||||||
- **실제**: 코드는 존재하지 않는 `slack_user_id` 컬럼 사용 중
|
|
||||||
|
|
||||||
2. **자동 토큰 갱신**
|
|
||||||
- 만료된 토큰 자동 감지 및 갱신
|
|
||||||
- 401 에러 시 자동 재시도
|
|
||||||
- 갱신된 토큰 DB 자동 저장
|
|
||||||
|
|
||||||
3. **코드 단순화**
|
|
||||||
- UUID 변환 로직 제거
|
|
||||||
- 직관적인 slack_id 사용
|
|
||||||
- 디버깅 용이성 향상
|
|
||||||
|
|
||||||
## 배포 절차
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. skill-email 재빌드 및 재시작
|
|
||||||
cd /home/heejae/skill-email
|
|
||||||
docker compose down && docker compose up -d --build
|
|
||||||
|
|
||||||
# 2. 로그 확인
|
|
||||||
docker logs skill-email --tail 50
|
|
||||||
|
|
||||||
# 3. 헬스체크
|
|
||||||
curl http://localhost:8501/health
|
|
||||||
```
|
|
||||||
|
|
||||||
## 트러블슈팅
|
|
||||||
|
|
||||||
### SSL SYSCALL error: EOF detected
|
|
||||||
- 원인: PostgreSQL 연결 풀 타임아웃
|
|
||||||
- 해결: 재시도 시 정상 작동 (연결 풀 자동 재생성)
|
|
||||||
|
|
||||||
### 토큰 조회 실패
|
|
||||||
- 확인사항:
|
|
||||||
1. `slack_id` 컬럼에 올바른 Slack 사용자 ID 저장 여부
|
|
||||||
2. `is_equipped = true` 설정 여부
|
|
||||||
3. SSH 터널 연결 상태 (포트 5433)
|
|
||||||
|
|
||||||
### 자동 갱신 실패
|
|
||||||
- 확인사항:
|
|
||||||
1. `refresh_token` 존재 여부
|
|
||||||
2. Google OAuth 클라이언트 ID/Secret 설정
|
|
||||||
3. 네트워크 연결 상태
|
|
||||||
|
|
||||||
## 참고 사항
|
|
||||||
|
|
||||||
- SSH 터널: `localhost:5433 → 124.55.18.179:5432`
|
|
||||||
- 데이터베이스: `main_db`
|
|
||||||
- 환경 변수: `/home/heejae/skill-email/.env`
|
|
||||||
- Docker 네트워크: `network_mode: host`
|
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
# Gmail Token 시스템 Slack ID 마이그레이션
|
||||||
|
|
||||||
|
## 문제 상황 (2025-08-25)
|
||||||
|
- UUID 불일치: auth-server `uuid4()` vs skill-email `uuid5()` → 토큰 조회 실패
|
||||||
|
- DB 스키마 변경 시도했으나 미완성
|
||||||
|
|
||||||
|
## 실제 상태 (2025-09-18 확인)
|
||||||
|
|
||||||
|
### 코드 문제
|
||||||
|
```python
|
||||||
|
# 현재 코드 (잘못됨)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT ... FROM gmail_tokens
|
||||||
|
WHERE slack_user_id = %s # 존재하지 않는 컬럼
|
||||||
|
""", (user_id,))
|
||||||
|
|
||||||
|
# 올바른 방법
|
||||||
|
cur.execute("""
|
||||||
|
SELECT gt.* FROM gmail_token gt
|
||||||
|
JOIN user u ON gt.user_id = u.id
|
||||||
|
WHERE u.oauth_id = %s AND gt.is_equipped = true
|
||||||
|
""", (slack_id,))
|
||||||
|
```
|
||||||
|
|
||||||
|
### DB 스키마 (실제)
|
||||||
|
```sql
|
||||||
|
CREATE TABLE gmail_token (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
user_id UUID NOT NULL, -- user 테이블 FK
|
||||||
|
token_data JSONB, -- access_token, refresh_token 포함
|
||||||
|
equipped_to VARCHAR(50), -- Gmail 계정
|
||||||
|
is_equipped BOOLEAN DEFAULT false
|
||||||
|
);
|
||||||
|
-- user.oauth_id에 Slack ID 저장됨
|
||||||
|
```
|
||||||
|
|
||||||
|
## 토큰 자동 갱신 (정상 작동)
|
||||||
|
```python
|
||||||
|
if creds and creds.expired and creds.refresh_token:
|
||||||
|
creds.refresh(Request())
|
||||||
|
self.creds_provider.save_credentials(user_id, creds)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 트러블슈팅
|
||||||
|
|
||||||
|
### 토큰 조회 실패
|
||||||
|
- `slack_user_id` 컬럼 없음 → JOIN 사용 필요
|
||||||
|
- `is_equipped = true` 확인
|
||||||
|
|
||||||
|
### 자동 갱신 실패
|
||||||
|
- `refresh_token` 존재 여부
|
||||||
|
- Google OAuth 설정 확인
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
- [250831_skill-email_UUID_inconsistency_URGENT.md](./250831_happybell80_skill-email_UUID_inconsistency_URGENT.md)
|
||||||
Loading…
x
Reference in New Issue
Block a user