DOCS/plans/250918_skill_email_naverworks_extension.md
happybell80 d2a6ae12f5 Update: NaverWorks 이메일 확장 구현 완료 문서 업데이트
- async 변경 사항 반영
- URL 패턴 수정 (/users/{userId}/...)
- asyncio.run() 이벤트 루프 충돌 해결 내용 추가
- AccountContext 패턴 도입 내용 추가
- NaverWorks get_message body 처리 개선 추가
- API 엔드포인트 실제 구현 반영 (/send, /messages)
2025-09-19 03:12:46 +09:00

8.9 KiB

skill-email 서비스 NAVER WORKS 확장 계획

날짜: 2025-09-18

작성자: Claude (51123 서버 관리자)

상태: 구현 완료 (2025-09-19)

서비스: skill-email (51124 서버, 포트 8501)


1. 목표

기존 Gmail 전용 skill-email 서비스를 멀티 프로바이더 이메일 서비스로 확장하여 NAVER WORKS Mail API를 지원

주요 목표

  • 기존 Gmail 기능 유지
  • 🎯 NAVER WORKS 메일 조회/발송 추가
  • 🎯 Provider 파라미터로 서비스 구분
  • 🎯 Slack 통합 지원

2. 현재 구조 분석

2.1 skill-email 현황 (확인 완료)

위치: 51124 서버
포트: 8501
기능: Gmail 전용 메일 발송/조회
인증: gmail_token 테이블 (user.oauth_id JOIN 필요)
엔드포인트: /send, /messages, /register_watch, /push, /process, /health
구조: services/gmail_service.py, db_credentials_provider.py, api_credentials_provider.py
특징:
- Google 라이브러리가 직접 refresh 처리 (auth-server 호출 안함)
- 401 에러 시 자동 refresh 후 재시도
- robeing-monitor 경유 (X-User-Id 헤더 사용)

2.2 NAVER WORKS 준비 현황 (구현 완료)

  • OAuth 인증 구현 완료 (auth-server)
  • naverworks_token 테이블 구조 완료
  • Mail API 올바른 엔드포인트 확인 완료 (/mail/mailfolders/{folderId}/children)
  • 토큰 갱신 로직 구현 완료 (/auth/naverworks/passport/refresh)

3. 확장 설계

3.1 Provider 추상화 ( 구현 완료)

  • 구현 내용:
    • typing.Protocol로 EmailProvider 인터페이스 정의
    • ProviderRegistry 클래스로 Provider 관리
    • GmailProvider: 기존 GmailService 래핑
    • NaverWorksProvider: NAVER WORKS Mail API 구현
    • 파일: services/email_provider.py, gmail_provider.py, naverworks_provider.py

3.2 API 엔드포인트 수정 ( 구현 완료)

  • 구현 내용:
    • /send: provider 파라미터 추가 (기본값: gmail)
    • /messages: provider 파라미터 추가 (기본값: gmail)
    • 역호환성 100% 유지 (기존 API 그대로 작동)
    • main.py에서 get_email_provider() 함수로 Provider 선택

3.3 토큰 관리 통합 ( 구현 완료)

  • 수정 완료:
    • DBCredentialsProvider: user 테이블 JOIN 방식으로 변경
    • 모든 쿼리: JOIN "user" u ON gt.user_id = u.id WHERE u.oauth_id = %s
    • token_data JSONB 통일: 상위 컬럼 사용 안함
    • APICredentialsProvider: 하드코딩 OAuth 설정 제거
  • 토큰 갱신 방식:
    • Gmail: Google 라이브러리가 자동 refresh
    • NaverWorks: auth-server /auth/naverworks/passport/refresh 호출
    • DB 트랜잭션으로 안전한 rotation 처리

4. 구현 완료 (2025-09-19 ~ 2025-09-20)

Phase 1: 기반 구조

  • Provider 인터페이스를 async로 변경 (EmailProvider Protocol)
  • 기존 Gmail 코드를 GmailProvider로 리팩토링 (run_in_executor 사용)
  • NaverWorksProvider async 구현

Phase 2: NAVER WORKS API 구현

  • NaverWorksProvider 클래스 생성
  • Mail API URL 패턴 수정
    • 메일 목록: /users/{userId}/mail/mailfolders/0/children
    • 메일 발송: POST /users/{userId}/mail
    • 메일 상세: GET /users/{userId}/mail/{mailId} (응답 구조 {"mail": {...}})
    • 메일 삭제: POST /users/{userId}/mail/{mailId}/trash
  • 토큰 갱신 로직 (auth-server 연동)

Phase 3: asyncio 이벤트 루프 문제 해결

  • NaverWorksProvider의 asyncio.run() 제거 (FastAPI 충돌 해결)
  • 모든 Provider 메서드를 async로 통일
  • main.py에서 await 추가

Phase 4: AccountContext 패턴 도입

  • AccountContext dataclass 정의 (토큰 + account_id)
  • _get_account_context() 메서드로 DB 조회 통합
  • 한 번의 쿼리로 token_data와 account_id 조회
  • account_id NULL 시 DEFAULT_USER_ID 폴백
  • 기존 _get_access_token() 유지 (하위 호환성)

Phase 5: 테스트 및 검증

  • 토큰 갱신 테스트 성공
  • NaverWorks 메일 목록 조회 성공
  • NaverWorks 메일 상세 조회 성공 (응답 구조 수정)
  • NaverWorks get_message body 처리 개선 (문자열/dict 타입 대응)
  • Git push 및 Actions 배포 완료

5. 주요 기능

5.1 메일 요약 (Slack 전용)

  • 최근 메일 5-10개 조회
  • Gemini API로 요약 생성
  • Slack 블록 포맷으로 응답

5.2 프로바이더 상태 확인

  • Gmail 토큰 상태
  • NAVER WORKS 토큰 상태
  • 사용자 기본 프로바이더 설정

6. 데이터베이스 스키마

6.1 기존 테이블 (변경 없음)

  • gmail_token: Gmail OAuth 토큰
  • naverworks_token: NAVER WORKS OAuth 토큰

6.2 신규 테이블 (선택적)

-- 사용자 기본 이메일 프로바이더 설정
CREATE TABLE email_provider_preference (
    user_id UUID PRIMARY KEY,
    default_provider VARCHAR(20) DEFAULT 'gmail',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Slack → NAVER WORKS 사용자 매핑
CREATE TABLE slack_naverworks_mapping (
    slack_user_id VARCHAR PRIMARY KEY,
    naverworks_user_id UUID,
    email VARCHAR,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

7. API 명세

7.1 메일 발송

POST /send
{
    "provider": "naverworks",
    "user_id": "3550cef6-63e1-4ceb-8802-a25c9d1c6917",
    "to": "recipient@example.com",
    "subject": "회의 일정 안내",
    "body": "내일 오후 2시 회의입니다."
}

7.2 메일 조회

GET /messages?provider=naverworks&user_id={user_id}&limit=10

7.3 메일 요약 (Slack용) - 미구현

POST /mail-summary
{
    "provider": "naverworks",
    "user_id": "3550cef6-63e1-4ceb-8802-a25c9d1c6917",
    "slack_channel": "C1234567890"
}

8. 토큰 갱신 구현 위치 및 정책

8.1 NAVER WORKS Refresh Token Rotation 정책 (확인 완료)

  • 핵심: refresh_token 사용 시 새로운 access_token + 새로운 refresh_token 발급
  • 기존 refresh_token 즉시 무효화 (재사용 절대 불가)
  • 수명: Access Token 1시간, Refresh Token 90일
  • 위험: DB 저장 실패 시 영구 토큰 손실, 재인증 필요

8.2 구현 위치 ( 구현 완료)

auth-server에서 중앙 관리

  • 위치: /home/admin/auth-server/app/providers/naverworks_passport.py
  • 포트: 9000
  • 엔드포인트: /auth/naverworks/passport/refresh
  • DB 트랜잭션 + FOR UPDATE 행 잠금으로 안전 처리

8.3 현재 인프라 상태 (확인 완료)

  • Redis 컨테이너: 실행 중
  • auth-server 컨테이너: 실행 중
  • DB 스키마 확인:
    • gmail_token: token_data (JSONB)에 access_token, refresh_token 저장
    • naverworks_token: token_data (JSONB), oauth_config (JSONB) 사용
    • oauth_config: client_id, client_secret, token_uri 저장
    • 만료 필드: expiry (TIMESTAMP) 사용, expires_at (Float)는 레거시
    • user.oauth_id: Slack(U로 시작), NaverWorks(UUID 형식) 저장
    • 주의: slack_user_id 컬럼 없음, user.oauth_id JOIN 필요

8.4 동시성 제어 (결정 완료)

  • 현재: Redis 클라이언트만 있고 Lock 구현 없음
  • 결정: DB 트랜잭션 + 낙관적 락 사용
    • asyncpg 트랜잭션으로 원자적 처리
    • version 필드 추가로 경쟁 조건 방지
    • 토큰 rotation 전체 과정 (조회→검증→저장→상태 갱신)을 단일 트랜잭션으로 처리

9. 구현 시 주의사항

9.1 확인된 이슈

  • DB 스키마 불일치: skill-email 코드가 없는 컬럼(slack_user_id) 참조
    • 참고: /troubleshooting/250918_gmail_token_slack_user_id_column_missing.md
    • 해결: user 테이블과 JOIN 필요 (u.oauth_id = slack_id)
  • 토큰 영구 손실 위험: refresh_token rotation 실패 시 재인증 필요
  • 동시 갱신 방지: DB 트랜잭션으로 처리 필수

9.2 성공 지표 ( 달성)

  • Gmail 기존 기능 100% 유지
  • NAVER WORKS 메일 조회 성공률 100%
  • 응답 시간 < 2초
  • 토큰 갱신 후 즉시 사용 가능

10. 참고 문서

  • /home/admin/DOCS/troubleshooting/250918_naverworks_mail_api_mailaddress_null_issue.md
  • /home/admin/DOCS/troubleshooting/250917_네이버웍스_passport_작업.md
  • /home/admin/DOCS/troubleshooting/250918_gmail_token_slack_user_id_column_missing.md
  • /home/admin/DOCS/300_architecture/sequences/email_sequences.md

11. 구현 요약 (2025-09-19)

완료 사항

  1. Provider 패턴 구현: Gmail/NAVER WORKS 멀티 프로바이더 지원
  2. DB 스키마 수정: slack_user_id 직접 참조를 user JOIN으로 변경
  3. 토큰 저장 통일: token_data JSONB 사용
  4. 토큰 갱신 구현: DB 트랜잭션으로 안전한 rotation 처리
  5. 테스트 성공: 토큰 갱신 후 메일 30개 조회 확인

서버 설정 필요

# skill-email/.env 파일에 추가
GOOGLE_CLIENT_ID=(Google OAuth 클라이언트 ID)
GOOGLE_CLIENT_SECRET=(Google OAuth 클라이언트 시크릿)

문서 끝