DOCS/300_architecture/sequences/email_sequences.md
happybell80 97e0888ce0 Fix more incorrect table names in documentation
- users → user in SQL contexts (94 occurrences)
- robeings → robeing in SQL contexts
- user_preferences → user_preference (14 files)
- slack_workspaces → slack_workspace in SQL contexts (17 files)

All table names now correctly match PostgreSQL schema
2025-09-26 00:52:15 +09:00

18 KiB

이메일 시스템 시퀀스 다이어그램

작성일: 2025-08-19

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

상태: 초안


목차

  1. Gmail OAuth 인증
  2. Gmail 아이템 장착
  3. 이메일 발송 - 프론트엔드
  4. 이메일 발송 - Slack
  5. 토큰 재인증
  6. 아이템 미장착 오류 처리

1. Gmail OAuth 인증

1.1 최초 Gmail 연결 (프론트엔드 시작) - Gmail Passport 방식

sequenceDiagram
    participant User as 사용자
    participant Front as 프론트엔드
    participant Auth as auth-server(9000)
    participant Google as Google OAuth
    participant DB as PostgreSQL

    User->>Front: Gmail 연결 버튼 클릭
    Note over Front: 현재 페이지 URL 저장
    Front->>Auth: GET /auth/gmail/passport
    Note over Auth: ?user_id={userId}<br/>&return_url={currentUrl}
    
    Auth->>Auth: state 데이터 생성
    Note over Auth: {user_id, timestamp,<br/>return_url} → JSON
    Auth->>Auth: OAuth URL 생성
    Note over Auth: client_id, redirect_uri,<br/>scopes, state 포함
    
    Auth-->>Front: 302 Redirect to Google
    Front->>Google: 브라우저 리다이렉트
    
    User->>Google: Google 계정 로그인
    User->>Google: 권한 승인
    Note over Google: gmail.send<br/>gmail.readonly<br/>gmail.modify
    
    Google->>Auth: GET /auth/gmail/passport/callback
    Note over Auth: ?code={auth_code}&state={state}
    
    Auth->>Auth: state 파싱 및 검증
    Note over Auth: user_id, return_url 추출
    Auth->>Google: 토큰 교환 요청
    Google-->>Auth: access_token, refresh_token
    
    Auth->>DB: gmail_token 테이블 저장
    Note over DB: user_id, email,<br/>tokens (암호화),<br/>scopes, metadata
    
    Auth->>Auth: 리다이렉트 URL 결정
    Note over Auth: return_url 있으면 해당 페이지<br/>없으면 /game 기본값
    
    Auth-->>Front: 302 Redirect
    Note over Front: {return_url}?gmail=success<br/>&email={email}
    Front->>Front: 원래 페이지에서 성공 메시지 표시

1.2 Slack에서 Gmail 연결

sequenceDiagram
    participant User as 사용자
    participant Slack as Slack Client
    participant RB as rb10508_micro
    participant Gateway as Gateway(8100)
    participant Auth as auth-server(9000)
    participant DB as PostgreSQL

    User->>Slack: "Gmail 연결해줘"
    Slack->>RB: 메시지 전달
    
    RB->>RB: 의도 분류 (Gmail 연결)
    RB->>Gateway: POST /api/auth/gmail/connect
    Gateway->>Auth: Slack user_id 포함 요청
    
    Auth->>DB: slack_user_mapping 조회
    Note over Auth: Slack ID → User ID 변환
    
    Auth->>Auth: OAuth URL 생성 (state 포함)
    Auth-->>Gateway: OAuth URL
    Gateway-->>RB: URL 반환
    
    RB-->>Slack: 인증 링크 메시지
    Note over Slack: "Gmail 연결을 위해<br/>아래 링크를 클릭하세요"
    
    User->>User: 링크 클릭 (브라우저 열림)
    Note over User: 이후 플로우는<br/>프론트엔드와 동일

2. Gmail 아이템 장착

sequenceDiagram
    participant User as 사용자
    participant Front as 프론트엔드
    participant Gateway as Gateway(8100)
    participant Auth as auth-server(9000)
    participant Monitor as robeing-monitor(9024)
    participant RB as rb10508_micro
    participant DB as PostgreSQL

    User->>Front: 인벤토리 페이지 접속
    Front->>Gateway: GET /api/items/gmail
    Gateway->>Gateway: JWT 토큰 검증 (내부)
    Note over Gateway: username 추출
    
    Gateway->>DB: username → UUID 변환
    Note over DB: users 테이블 조회
    DB-->>Gateway: user_id (UUID)
    
    Gateway->>Monitor: 아이템 목록 요청
    Note over Monitor: X-User-Id 헤더로<br/>UUID 전달
    Monitor->>DB: gmail_token 조회
    Note over DB: user_id로 필터링
    
    Monitor->>DB: robeing_stats 조회
    Note over DB: 레벨 정보 확인
    
    DB-->>Monitor: 토큰 목록, 레벨 정보
    Monitor->>Monitor: capabilities 계산
    Note over Monitor: scopes → capabilities 변환<br/>토큰 본문은 제외
    
    Monitor-->>Gateway: 아이템 목록
    Gateway-->>Front: 응답
    Front->>Front: 아이템 카드 표시
    
    User->>Front: Gmail 패스포트 장착 클릭
    Front->>Gateway: POST /api/items/gmail/{userId}/equip
    Note over Gateway: Body: {robeing_id: "rb10508_micro"}
    
    Gateway->>Gateway: JWT 검증 (내부)
    Note over Gateway: username 추출
    
    Gateway->>DB: username → UUID 변환
    DB-->>Gateway: user_id (UUID)
    
    Gateway->>Monitor: 장착 요청
    Monitor->>DB: robeing_stats 레벨 확인
    
    alt 레벨 < 5
        Monitor-->>Gateway: INSUFFICIENT_LEVEL
        Gateway-->>Front: 레벨 부족 에러
        Front->>Front: "레벨 5가 되면<br/>사용 가능해요!" 표시
    else 레벨 >= 5
        Monitor->>DB: gmail_token 업데이트
        Note over DB: is_equipped = true<br/>equipped_to = "rb10508_micro"
        
        Monitor->>DB: gmail_audit_logs 기록
        Note over DB: action: "equip"<br/>user_id, robeing_id, timestamp
        
        Monitor->>RB: 장착 알림 (선택적)
        Note over RB: 캐시 갱신
        
        Monitor-->>Gateway: 장착 성공
        Gateway-->>Front: 성공 응답
        Front->>Front: UI 업데이트
        Note over Front: 장착 상태 표시<br/>애니메이션 효과
    end

3. 이메일 발송 - 프론트엔드

sequenceDiagram
    participant User as 사용자
    participant Front as 프론트엔드
    participant Gateway as Gateway(8100)
    participant RB as rb10508_micro
    participant Monitor as robeing-monitor(9024)
    participant Skill as skill-email(8501)
    participant DB as PostgreSQL
    participant Gmail as Gmail API

    User->>Front: 이메일 작성 폼 열기
    User->>Front: 수신자, 제목, 본문 입력
    User->>Front: 발송 버튼 클릭
    
    Front->>Gateway: POST /api/robeing/email/send
    Note over Gateway: JWT 토큰 포함<br/>to, subject, body
    
    Gateway->>Gateway: JWT 검증 (내부)
    Note over Gateway: username 추출
    
    Gateway->>DB: username → UUID 변환
    Note over DB: users 테이블 조회<br/>SELECT id FROM user<br/>WHERE username = ?
    DB-->>Gateway: user_id (UUID)
    
    Gateway->>RB: 이메일 발송 요청
    Note over RB: X-User-Id 헤더로<br/>UUID 전달
    
    RB->>Monitor: GET /api/items/gmail/status
    Note over Monitor: 장착 상태 확인
    Monitor->>DB: gmail_token 조회
    DB-->>Monitor: 장착 정보
    
    alt Gmail 아이템 미장착
        Monitor-->>RB: NOT_EQUIPPED
        RB-->>Gateway: 아이템 미장착 에러
        Gateway-->>Front: "Gmail 패스포트를<br/>먼저 장착해주세요"
    else Gmail 아이템 장착됨
        Monitor-->>RB: 장착 확인 (token_id)
        
        RB->>Skill: POST /send-email
        Note over Skill: user_id, to,<br/>subject, body
        
        Skill->>DB: gmail_token 조회
        Note over DB: user_id와 is_equipped 확인
        DB-->>Skill: access_token (복호화)
        
        Skill->>Gmail: Gmail API 호출
        Note over Gmail: POST /gmail/v1/users/me/messages/send
        
        alt 토큰 만료
            Gmail-->>Skill: 401 Unauthorized
            Skill->>DB: refresh_token으로 갱신
            Skill->>Gmail: 재시도
        end
        
        Gmail-->>Skill: 발송 성공 (message_id)
        
        Skill->>DB: 발송 로그 저장
        Note over DB: conversation_log 또는<br/>email_send_history
        
        Skill-->>RB: 발송 결과
        RB->>DB: robeing_stats 경험치 증가
        Note over DB: email_sent_count++<br/>experience += 10
        
        RB-->>Gateway: 성공 응답
        Gateway-->>Front: 발송 완료
        Front->>Front: 성공 메시지 표시
    end

4. 이메일 발송 - Slack

sequenceDiagram
    participant User as 사용자
    participant Slack as Slack Client
    participant RB as rb10508_micro
    participant Monitor as robeing-monitor(9024)
    participant Skill as skill-email(8501)
    participant DB as PostgreSQL
    participant Gmail as Gmail API

    User->>Slack: "종태님한테 회의 일정 메일 보내줘"
    Slack->>RB: 메시지 전달 (user_id: U0925SXQFDK)
    
    RB->>RB: 의도 분류
    Note over RB: INTENT: EMAIL_SEND<br/>수신자: 종태님<br/>내용: 회의 일정
    
    RB->>Auth: 매핑 API 호출
    Note over RB: GET /api/slack/mapping/U0925SXQFDK
    Auth-->>RB: UUID 반환
    
    RB->>DB: UUID로 사용자 조회
    Note over DB: SELECT * FROM user<br/>WHERE id = '매핑된 UUID'
    DB-->>RB: user_id, name: "김종태"
    
    RB->>Monitor: Gmail 아이템 상태 확인
    Monitor->>DB: gmail_token 조회
    
    alt 아이템 미장착
        DB-->>Monitor: is_equipped = false
        Monitor-->>RB: NOT_EQUIPPED
        RB-->>Slack: "Gmail 패스포트를 먼저 장착해주세요.<br/>설정에서 Gmail을 연결할 수 있어요."
    else 아이템 장착됨
        DB-->>Monitor: is_equipped = true
        Monitor-->>RB: EQUIPPED (token_id)
        
        RB->>RB: 이메일 내용 생성
        Note over RB: LLM으로 메일 본문 작성<br/>"안녕하세요 종태님,<br/>회의 일정 관련..."
        
        RB->>DB: robeing.contacts 조회
        Note over DB: SELECT email FROM robeing.contacts<br/>WHERE robeing_id = $1<br/>AND name ILIKE '%종태%'
        DB-->>RB: 이메일 주소 (goeun2dc@gmail.com)
        
        RB->>Skill: POST /send-email
        Note over Skill: to: goeun2dc@gmail.com<br/>subject: "회의 일정 안내"<br/>body: LLM 생성 내용
        
        Skill->>DB: gmail_token 조회
        DB-->>Skill: access_token
        
        Skill->>Gmail: 이메일 발송
        Gmail-->>Skill: 성공 (message_id)
        
        Skill-->>RB: 발송 완료
        
        RB->>DB: 대화 로그 저장
        Note over DB: conversation_log에<br/>이메일 발송 기록
        
        RB-->>Slack: "종태님께 회의 일정 메일을<br/>발송했습니다."
    end

5. 토큰 재인증

sequenceDiagram
    participant User as 사용자
    participant Front as 프론트엔드
    participant Monitor as robeing-monitor(9024)
    participant Auth as auth-server(9000)
    participant Google as Google OAuth
    participant DB as PostgreSQL

    Note over Front: 토큰 만료 감지<br/>(API 호출 시 401)
    
    Front->>Front: 재인증 필요 알림 표시
    User->>Front: 재인증 버튼 클릭
    
    Front->>Monitor: POST /api/items/gmail/{userId}/reauth
    Monitor->>Auth: 재인증 요청
    
    Auth->>DB: 기존 토큰 정보 조회
    Note over DB: email, scopes 확인
    
    Auth->>Auth: OAuth URL 생성
    Note over Auth: login_hint로<br/>기존 이메일 전달
    
    Auth-->>Monitor: OAuth URL
    Monitor-->>Front: 리다이렉트 URL
    
    Front->>Google: 브라우저 리다이렉트
    User->>Google: 재인증 (간소화된 플로우)
    Google->>Auth: 인증 코드 콜백
    
    Auth->>Google: 토큰 교환
    Google-->>Auth: 새 access_token, refresh_token
    
    Auth->>DB: gmail_token 업데이트
    Note over DB: 새 토큰으로 교체<br/>updated_at 갱신
    
    Auth->>DB: gmail_audit_logs 기록
    Note over DB: action: "reauth"
    
    Auth-->>Front: 재인증 완료
    Front->>Front: 아이템 상태 갱신
    Note over Front: "재인증 완료" 메시지

6. 아이템 미장착 오류 처리

sequenceDiagram
    participant User as 사용자
    participant Slack as Slack/프론트
    participant RB as rb10508_micro
    participant Monitor as robeing-monitor(9024)
    participant DB as PostgreSQL

    User->>Slack: "이메일 보내줘"
    Slack->>RB: 이메일 요청
    
    RB->>Monitor: Gmail 아이템 확인
    Monitor->>DB: gmail_token 조회
    
    DB-->>Monitor: 결과
    
    alt 토큰 없음
        Monitor-->>RB: NO_TOKEN
        RB-->>Slack: "Gmail 계정을 먼저 연결해주세요.<br/>설정 → Gmail 연결"
    else 토큰 있지만 미장착
        Monitor-->>RB: NOT_EQUIPPED
        RB-->>Slack: "Gmail 패스포트가 인벤토리에 있어요.<br/>먼저 장착해주세요."
    else 레벨 부족 (레벨 < 5)
        Monitor-->>RB: INSUFFICIENT_LEVEL
        RB-->>Slack: "Gmail 기능은 레벨 5부터 사용 가능해요.<br/>현재 레벨: 3"
    else 권한 부족 (gmail.send 없음)
        Monitor-->>RB: INSUFFICIENT_SCOPE
        RB-->>Slack: "이메일 발송 권한이 필요해요.<br/>Gmail 재인증이 필요합니다."
    else 토큰 만료
        Monitor-->>RB: TOKEN_EXPIRED
        RB-->>Slack: "Gmail 인증이 만료되었어요.<br/>재인증이 필요합니다."
    end

7. 이메일 조회 (받은 메일함)

sequenceDiagram
    participant User as 사용자
    participant Slack as Slack Client
    participant RB as rb10508_micro
    participant Skill as skill-email(8501)
    participant DB as PostgreSQL
    participant Gmail as Gmail API

    User->>Slack: "최근 메일 확인해줘"
    Slack->>RB: 메시지 전달
    
    RB->>RB: 의도 분류 (EMAIL_LIST)
    RB->>DB: Gmail 아이템 확인
    
    alt 아이템 장착 확인됨
        RB->>Skill: GET /list-emails
        Note over Skill: maxResults: 10<br/>q: "is:unread"
        
        Skill->>DB: gmail_token 조회
        DB-->>Skill: access_token
        
        Skill->>Gmail: GET /gmail/v1/users/me/messages
        Gmail-->>Skill: 메일 목록 (message_ids)
        
        Skill->>Gmail: 각 메일 상세 조회 (병렬)
        Note over Gmail: GET /messages/{id}
        Gmail-->>Skill: 메일 상세 정보
        
        Skill->>Skill: 메일 요약 생성
        Note over Skill: 발신자, 제목,<br/>시간, 미리보기
        
        Skill-->>RB: 메일 목록
        
        RB->>RB: 포맷팅
        Note over RB: "📧 받은 메일 (10건)<br/>1. 김종태 - 회의 일정<br/>2. GitHub - PR 리뷰..."
        
        RB-->>Slack: 메일 목록 표시
    end

8. AI 메일 작성 + 발송

sequenceDiagram
    participant User as 사용자
    participant Front as 프론트엔드
    participant RB as rb10508_micro
    participant LLM as LLM Service
    participant Skill as skill-email(8501)
    participant Gmail as Gmail API

    User->>Front: "프로젝트 진행 상황 메일 작성"
    Front->>RB: AI 작성 요청
    
    RB->>RB: 컨텍스트 수집
    Note over RB: 프로젝트 정보<br/>최근 대화 내용<br/>사용자 스타일
    
    RB->>LLM: 메일 생성 요청
    Note over LLM: 프롬프트:<br/>"프로젝트 진행 상황을<br/>전문적으로 작성"
    
    LLM-->>RB: 생성된 메일
    Note over RB: 제목: "8월 프로젝트 진행 현황"<br/>본문: 상세 내용
    
    RB-->>Front: 초안 표시
    Front->>Front: 편집 가능한 폼 표시
    
    User->>Front: 수정 후 발송 클릭
    Front->>RB: 최종 발송 요청
    
    RB->>Skill: 이메일 발송
    Note over Skill: 수정된 내용으로 발송
    
    Skill->>Gmail: 발송
    Gmail-->>Skill: 성공
    
    Skill-->>RB: 완료
    RB-->>Front: "메일이 발송되었습니다"

주요 오류 코드 및 처리

오류 코드 설명 사용자 메시지
NO_TOKEN Gmail 토큰 없음 "Gmail 계정을 먼저 연결해주세요"
NOT_EQUIPPED 아이템 미장착 "Gmail 패스포트를 먼저 장착해주세요"
INSUFFICIENT_LEVEL 레벨 부족 "레벨 5가 되면 사용 가능해요"
TOKEN_EXPIRED 토큰 만료 "Gmail 재인증이 필요합니다"
INSUFFICIENT_SCOPE 권한 부족 "추가 권한이 필요합니다"
NETWORK_ERROR 네트워크 오류 "일시적인 오류입니다. 잠시 후 다시 시도해주세요"

서비스 간 통신 포트

서비스 포트 역할
Gateway 8100 API 라우팅, JWT 검증
auth-server 9000 OAuth 처리, 토큰 관리
robeing-monitor 9024 아이템 상태 관리
rb10508_micro 10508 메인 로빙 서비스
skill-email 8501 Gmail API 호출
PostgreSQL 5432 데이터 저장소

데이터베이스 테이블

gmail_token

  • id: UUID
  • user_id: UUID (users 테이블 참조)
  • robeing_id: VARCHAR (통일된 컬럼명)
  • email: VARCHAR
  • access_token: TEXT (암호화)
  • refresh_token: TEXT (암호화)
  • scopes: TEXT[]
  • is_equipped: BOOLEAN
  • equipped_to: VARCHAR
  • metadata: JSONB
  • created_at: TIMESTAMP
  • updated_at: TIMESTAMP

gmail_audit_logs

  • id: SERIAL
  • user_id: VARCHAR
  • robeing_id: VARCHAR
  • action: VARCHAR (equip, unequip, reauth, revoke)
  • success: BOOLEAN
  • details: JSONB
  • created_at: TIMESTAMP

robeing_stats

  • id: UUID
  • user_id: UUID (users 테이블 참조)
  • robeing_id: VARCHAR
  • level: INTEGER
  • experience: INTEGER
  • email_sent_count: INTEGER
  • created_at: TIMESTAMP
  • updated_at: TIMESTAMP

9. UUID 변환 체계

9.1 일반 사용자 (프론트엔드)

JWT Token (username: "happybell80")
    ↓
Gateway: username → UUID 변환
    ↓ (users 테이블 조회)
UUID4: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa

9.2 Slack 사용자

Slack User ID: U0925SXQFDK
    ↓
51123 매핑 API 호출
    ↓
UUID: slack_user_mapping에서 조회

다음 단계

  1. 구현 우선순위

    • skill-email DB 연결 (FileProvider → DBProvider)
    • rb10508_micro Gmail 의도 분류 추가
    • 아이템 장착 확인 미들웨어
    • 에러 처리 표준화
  2. 테스트 시나리오

    • E2E: OAuth → 장착 → 발송
    • 오류 케이스: 미장착, 레벨 부족, 토큰 만료
    • 부하 테스트: 동시 다발 요청
  3. 모니터링

    • Gmail API 사용량 추적
    • 토큰 만료 예측
    • 발송 성공률 대시보드

문서 끝