DOCS/journey/plans/archive/250819_gmail_item_implementation_plan.md
happybell80 0252dd1a7f fix: 51123 서버 IP 주소 업데이트 (성수 이전)
192.168.219.45 → 192.168.0.100 일괄 변경

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:52:26 +09:00

12 KiB

Gmail 아이템 구현 계획

작성일: 2025-08-19

작성자: Claude (with 종태)

상태: 계획 수립


1. 개요

목적

Gmail 자격증명을 "API 아이템"으로 정의하고, robeing의 장착형 아이템 시스템에 통합

핵심 원칙

  • Gmail OAuth 토큰을 극비 데이터로 취급
  • 아이템 장착/해제 메타포로 권한 관리
  • RBAC+ABAC 하이브리드 접근 제어
  • 레벨 5 이상에서 사용 가능

관련 문서

  • /250817_email_skill_integration_status.md - 현황 분석
  • /250818_gmail_token_database_setup.md - DB 구성 완료
  • /ideas/250818_claude_robeing_레벨업_체감_시스템_설계.md - 아이템 시스템 설계

2. 아이템 정의

기본 속성

{
  type: 'accessory',
  slot: 'passport_gmail',
  name: 'Gmail Passport',
  level_requirement: 5
  // rarity 제거 - 불필요
}

데이터 모델

type GmailCredentialItem = {
  id: string;                 // gmail_token.id
  userId: string;             // gmail_token.user_id
  robeing_id?: string;        // gmail_token.robeing_id (통일)
  type: 'accessory';
  slot: 'passport_gmail';
  name: string;               // metadata.display_name || email
  email: string;              // metadata.email
  scopes: string[];           // scopes 배열
  status: 'ready' | 'reauth_required' | 'insufficient_scope';
  capabilities: {
    send: boolean;           // gmail.send 스코프 존재 여부
    read: boolean;           // gmail.readonly 스코프 존재 여부
    modify: boolean;         // gmail.modify 스코프 존재 여부
  };
  expiry?: string;
  createdAt: string;
  updatedAt: string;
};

3. 아키텍처

서비스 구조

프론트엔드 (React)
    ↓
robeing-gateway (인증/라우팅)
    ↓
rb8001:8001 (메인 robeing)
    ↓                    ↓
skill-email:8501    robeing-monitor:9024
(이메일 발송)        (아이템 상태 관리)
    ↓                    ↓
PostgreSQL (gmail_token 테이블)

서비스별 역할

auth-server

  • Gmail OAuth 플로우 처리
  • gmail_token 테이블 관리
  • 토큰 갱신 로직

robeing-monitor (9024)

  • 아이템 상태 관리
  • 장착/해제 로직
  • 레벨 요구사항 체크
  • 아이템 인벤토리 조회

rb8001

  • 아이템 사용 로직
  • skill-email 호출
  • 사용자 컨텍스트 관리

skill-email (8501)

  • Gmail API 실제 호출
  • 이메일 작성/발송
  • DB 기반 토큰 조회

4. 시퀀스 다이어그램

아이템 장착 플로우

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

    User->>Front: Gmail 아이템 장착 클릭
    Front->>Gateway: POST /api/items/gmail/:userId/equip
    Gateway->>Auth: JWT 토큰 검증
    Auth-->>Gateway: 사용자 정보 확인
    
    alt 인증 실패
        Gateway-->>Front: UNAUTHORIZED
    else 인증 성공
        Gateway->>Monitor: 장착 요청 전달
        Monitor->>DB: gmail_token 소유권 확인
        Monitor->>DB: robeing_stats 조회 (레벨 체크)
        DB-->>Monitor: 레벨 정보
        
        alt 레벨 < 5
            Monitor-->>Front: INSUFFICIENT_LEVEL
        else 레벨 >= 5
            Monitor->>DB: gmail_token 업데이트
            Note over DB: is_equipped=true<br/>equipped_to=robeing_id
            Monitor->>DB: gmail_audit_logs 기록
            Monitor-->>RB: 장착 완료 알림
            Monitor-->>Front: 장착 성공
        end
    end

이메일 발송 플로우

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

    User->>Front: "이메일 보내줘" 요청
    Front->>RB: 메시지 전달
    RB->>Monitor: Gmail 아이템 장착 확인
    Monitor->>DB: gmail_token 조회
    DB-->>Monitor: 장착 상태 & 토큰 정보
    
    alt 아이템 미장착
        Monitor-->>RB: ITEM_NOT_EQUIPPED
        RB-->>Front: "Gmail 아이템을 먼저 장착해주세요"
    else 아이템 장착됨
        Monitor-->>RB: 장착 확인
        RB->>Skill: 이메일 발송 요청
        Skill->>DB: 토큰 조회
        DB-->>Skill: access_token
        Skill->>Gmail: API 호출
        Gmail-->>Skill: 발송 결과
        Skill-->>RB: 성공/실패
        RB-->>Front: 결과 메시지
    end

재인증 플로우

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: 토큰 만료 상태 표시
    User->>Front: 재인증 버튼 클릭
    Front->>Monitor: POST /api/items/gmail/:userId/reauth
    Monitor->>Auth: OAuth URL 요청
    Auth->>Auth: state 생성
    Auth-->>Monitor: OAuth URL
    Monitor-->>Front: 리다이렉트 URL
    Front->>Google: OAuth 페이지로 이동
    User->>Google: 로그인 & 권한 승인
    Google->>Auth: 인증 코드 콜백
    Auth->>Google: 토큰 교환
    Google-->>Auth: access_token, refresh_token
    Auth->>DB: gmail_token 업데이트
    Auth-->>Front: 인증 완료
    Front->>Front: 아이템 상태 갱신

5. API 설계

robeing-monitor (9024) 엔드포인트

아이템 목록 조회

GET /api/items/gmail
Response: {
  equipped: GmailCredentialItem | null,
  available: GmailCredentialItem[]
}

아이템 장착

POST /api/items/gmail/:userId/equip
Body: { robeing_id: string }
Response: {
  success: boolean,
  message: string,
  audit_id: string
}

재인증 시작

POST /api/items/gmail/:userId/reauth
Response: {
  auth_url: string,
  state: string
}

아이템 철회

DELETE /api/items/gmail/:userId
Response: {
  success: boolean,
  audit_id: string
}

권한 체크 플로우

  1. Gateway: JWT 토큰 검증
  2. robeing-monitor: 레벨 5 이상 확인
  3. robeing-monitor: 아이템 소유권 확인
  4. rb8001: 최종 사용 권한 확인

5. 보안 정책

데이터 등급

  • 극비: access_token, refresh_token, client_secret
  • 기밀: oauth_config, scopes
  • 일반: email, display_name, status

접근 제어

  • 토큰 본문은 절대 프론트엔드 노출 금지
  • capabilities는 scopes에서 파생하여 표시
  • 모든 민감 작업 감사 로그 기록

감사 로그

CREATE TABLE gmail_audit_logs (
  id SERIAL PRIMARY KEY,
  user_id VARCHAR(100),
  robeing_id VARCHAR(50),
  action VARCHAR(50), -- equip, reauth, revoke
  success BOOLEAN,
  details JSONB,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

6. 구현 단계

Phase 1: 백엔드 기초

  • 필요 테이블 생성
    • robeing_stats 테이블 (레벨 관리)
    • gmail_audit_logs 테이블 (감사 로그)
    • gmail_token에 is_equipped, equipped_to 컬럼 추가
  • skill-email DB 연결 코드 작성 (희재)
    • FileCredentialsProvider → DBCredentialsProvider 전환
    • PostgreSQL 연결 설정 추가 (localhost:5433 SSH 터널)
    • oauth_config 컬럼에서 client_id/secret 가져오기 구현
  • robeing-monitor 서비스 구축
    • 기존 robeing-state-service 확장
    • 포트 9024로 변경
    • 아이템 관리 API 추가

Phase 2: 통합 연결

  • rb8001 환경변수 설정
    SKILL_EMAIL_URL=http://localhost:8501
    STATE_SERVICE_URL=http://localhost:9024
    
  • rb8001 ↔ skill-email 연결 테스트
  • rb8001 ↔ robeing-monitor 연결 테스트

Phase 3: 프론트엔드

  • 인벤토리 UI 컴포넌트 개발
    • 아이템 카드 리스트
    • 장착 상태 표시
    • 재인증 버튼
  • robeing-api.ts 확장
    • Gmail 아이템 API 호출 함수
    • 에러 핸들링

Phase 4: 테스트 및 검증

  • 장착/해제 플로우 테스트
  • 재인증 플로우 테스트
  • 권한 부족 시나리오 테스트
  • 감사 로그 확인

7. 환경변수 표준

공통 PostgreSQL 설정

# 51123 서버에서
POSTGRES_HOST=localhost
POSTGRES_PORT=5432

# 51124 서버에서
POSTGRES_HOST=192.168.0.100
POSTGRES_PORT=5432

# 공통
POSTGRES_DB=main_db
POSTGRES_USER=robeings
POSTGRES_PASSWORD=robeings

서비스별 설정

# rb8001 (51124 서버)
SKILL_EMAIL_URL=http://localhost:8501  # host 모드이므로 localhost
STATE_SERVICE_URL=http://localhost:9024
DATABASE_URL=postgresql://robeings:robeings@192.168.0.100:5432/main_db

# skill-email (51124 서버)
TOKEN_PROVIDER=database  # file → database
POSTGRES_CONNECTION_STRING=postgresql://robeings:robeings@192.168.0.100:5432/main_db

# robeing-monitor (51124 서버)
PORT=9024
SERVICE_NAME=robeing-monitor
DATABASE_URL=postgresql://robeings:robeings@192.168.0.100:5432/main_db

8. 데이터 매핑 (추후 구현)

Slack User ID ↔ Gmail 계정 매핑

  • 위치: 추후 결정 (workspace_member 또는 별도 테이블)
  • 구조:
-- 옵션 1: workspace_member 확장
ALTER TABLE workspace_member 
ADD COLUMN gmail_account VARCHAR(255);

-- 옵션 2: 별도 매핑 테이블
CREATE TABLE user_gmail_mapping (
  slack_user_id VARCHAR(100) PRIMARY KEY,
  gmail_account VARCHAR(255),
  robeing_id VARCHAR(50)
);

9. 성공 지표

기능적 요구사항

  • 레벨 5 이상 사용자만 Gmail 아이템 장착 가능
  • 토큰 만료 시 자동 재인증 안내
  • 권한 부족(gmail.send 없음) 시 명확한 안내

비기능적 요구사항

  • 토큰 정보 프론트엔드 미노출
  • 모든 민감 작업 감사 로그 기록
  • 서비스 간 통신 안정성 확보

10. 리스크 및 대응

리스크 1: 토큰 노출

  • 대응: 모든 API에서 토큰 필드 제거, capabilities만 반환

리스크 2: 서비스 간 통신 실패

  • 대응: 재시도 로직, Circuit Breaker 패턴 적용

리스크 3: 레벨 체크 우회

  • 대응: 다층 검증 (Gateway → Monitor → rb8001)

11. 결정된 표준

에러 메시지

  • 형식: 개발 친화적 에러 코드
    GMAIL_AUTH_FAILED
    INSUFFICIENT_LEVEL
    TOKEN_EXPIRED
    PASSPORT_REJECTED
    

응답 형식

{
  status: "success" | "error",
  data?: any,
  error?: string
}

UI/UX

  • 아이콘: Gmail 로고 사용
  • 애니메이션: 매우 간단한 페이드인/아웃
  • 메시지 톤: 친근한 안내
    • "레벨 5가 되면 사용 가능해요!"
    • "Gmail 재인증이 필요해요"

정책

  • 캐싱: 필요시에만 적용 (기본적으로 실시간)
  • 재시도: 3회, 지수 백오프
  • 감사 로그: 외부 백업 연동 (이미 구축됨)
  • 장착 제한: 계정당 1개 Gmail passport만 허용

12. 참고사항

현재 상태 (2025-08-19)

  • gmail_token 테이블 생성 완료
  • rb8001 정상 작동 (GEMINI_USE_CLI=false)
  • skill-email 파일 기반 동작 중 (DB 전환 필요)
  • 프론트 인벤토리 UI 미구현

주요 결정사항

  • 아이템 슬롯: passport_gmail (패스포트 슬롯)
  • 레벨 제한: 5 (robeing_stats 테이블 생성 필요)
  • 서비스 포트: skill-email(8501), robeing-monitor(9024)
  • 컬럼명: robeing_id로 통일 (DB 수정 완료)
  • Gateway 포트: 8100 (추후 8000으로 변경 예정)
  • 소유 구조: User가 아이템 소유 → Robeing에게 장착

다음 액션

  1. skill-email DB 연결 코드 작성
  2. robeing-monitor 서비스 구축
  3. 프론트 인벤토리 UI 개발
  4. 전체 통합 테스트

작업 분담

  • 종태: 프론트엔드 (인벤토리 UI, robeing-api.ts, Context 상태)
  • 희재: rb8001 통합, skill-email DB 연결
  • 서버 관리자: 테이블 생성, robeing-monitor(9024) 구축, Gateway 설정

문서 끝