From 11d5c32100d3bfcffa096a11882ebba27267dc94 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Thu, 18 Sep 2025 18:19:32 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20Gmail=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EB=82=A0=EC=A7=9C=20=EB=B0=8F=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 250825 → 250918 날짜 변경 - slack_id_migration → slack_user_id_column_missing - 실제 문제를 반영한 파일명으로 변경 --- .../250825_gmail_token_slack_id_migration.md | 210 ------------------ ...mail_token_slack_user_id_column_missing.md | 55 +++++ 2 files changed, 55 insertions(+), 210 deletions(-) delete mode 100644 troubleshooting/250825_gmail_token_slack_id_migration.md create mode 100644 troubleshooting/250918_gmail_token_slack_user_id_column_missing.md diff --git a/troubleshooting/250825_gmail_token_slack_id_migration.md b/troubleshooting/250825_gmail_token_slack_id_migration.md deleted file mode 100644 index cf3181d..0000000 --- a/troubleshooting/250825_gmail_token_slack_id_migration.md +++ /dev/null @@ -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` \ No newline at end of file diff --git a/troubleshooting/250918_gmail_token_slack_user_id_column_missing.md b/troubleshooting/250918_gmail_token_slack_user_id_column_missing.md new file mode 100644 index 0000000..c2b77ab --- /dev/null +++ b/troubleshooting/250918_gmail_token_slack_user_id_column_missing.md @@ -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) \ No newline at end of file