DOCS/300_architecture/320_Slack_기반_인터페이스_설계.md
happybell80 9eaa83a76e 아키텍처 문서 대규모 업데이트: JWT/UUID 변환 체계 정립
- JWT 검증 플로우: Gateway 내부 처리로 변경
- Username → UUID 변환 메커니즘 문서화
- UUID5 체계: Slack 사용자용 결정적 UUID 생성
- Gateway 프록시 패턴 상세 문서화
- 데이터베이스: gmail_tokens, robeing 스키마 추가
- 서비스 포트 매핑 및 역할 명확화
- auth_db → main_db 마이그레이션 반영
2025-08-22 20:12:35 +09:00

6.6 KiB

Slack 기반 인터페이스 아키텍처

작성일: 2025-08-21

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


1. 개요

Slack을 통한 로빙 서비스 접근 아키텍처. 실시간 대화형 인터페이스를 제공하며, UUID5 기반 사용자 식별 체계를 사용합니다.


2. 시스템 구조

2.1 전체 플로우

sequenceDiagram
    participant User as Slack 사용자
    participant Slack as Slack API
    participant RB as rb10508_micro
    participant Gateway as Gateway(8100)
    participant Auth as auth-server(9000)
    participant DB as PostgreSQL
    participant Skill as 스킬 서비스

    User->>Slack: 메시지 전송
    Slack->>RB: Event Webhook
    Note over RB: Slack User ID: U0925SXQFDK
    
    RB->>RB: UUID5 생성
    Note over RB: uuid5(namespace, slack_id)
    
    RB->>DB: 사용자 조회 (UUID5)
    DB-->>RB: 사용자 정보
    
    RB->>RB: 의도 분류
    RB->>Skill: 필요시 스킬 호출
    Skill-->>RB: 처리 결과
    
    RB->>Slack: 응답 전송
    Slack-->>User: 메시지 표시

2.2 사용자 식별 체계

UUID5 변환 로직

import uuid

NAMESPACE = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')

def get_user_uuid(slack_user_id: str) -> str:
    """Slack User ID를 UUID5로 변환"""
    return str(uuid.uuid5(NAMESPACE, slack_user_id))

# 예시
slack_id = "U0925SXQFDK"
user_uuid = get_user_uuid(slack_id)  # 결정적 UUID 생성

사용자 데이터 구조

-- users 테이블
CREATE TABLE users (
    id UUID PRIMARY KEY,  -- UUID5로 생성
    username VARCHAR(50),  -- slack_U0925SXQ 형태
    email VARCHAR(255),
    name VARCHAR(255),
    oauth_provider VARCHAR(50) DEFAULT 'slack',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3. 서비스 구성

3.1 서버 배치

서버 서비스 포트 역할
51123 auth-server 9000 OAuth 인증
51123 robeing-gateway 8100 API 프록시
51124 rb10508_micro 10508 Slack 이벤트 처리
51124 rb8001 8001 프로덕션 로빙

3.2 Slack 앱 설정

Event Subscriptions

Request URL: https://[도메인]/api/slack/events
Events:
  - app_mention
  - message.channels
  - message.groups
  - message.im
  - message.mpim

OAuth Scopes

Bot Token Scopes:
  - app_mentions:read
  - channels:read
  - chat:write
  - chat:write.public
  - im:read
  - im:write
  - users:read

4. 메시지 처리 플로우

4.1 일반 대화

flowchart TD
    A[Slack 메시지] --> B{이벤트 타입}
    B -->|app_mention| C[멘션 처리]
    B -->|message| D[일반 메시지]
    
    C --> E[UUID5 변환]
    D --> E
    
    E --> F[사용자 컨텍스트 로드]
    F --> G[의도 분류]
    
    G --> H{의도 타입}
    H -->|대화| I[LLM 응답 생성]
    H -->|이메일| J[skill-email 호출]
    H -->|뉴스| K[skill-news 호출]
    
    I --> L[Slack 응답]
    J --> L
    K --> L

4.2 Gmail 연동 플로우

sequenceDiagram
    participant User as Slack 사용자
    participant RB as rb10508_micro
    participant Monitor as robeing-monitor
    participant Skill as skill-email
    participant Gmail as Gmail API

    User->>RB: "이메일 보내줘"
    RB->>RB: UUID5 변환
    RB->>Monitor: Gmail 아이템 확인
    
    alt 아이템 미장착
        Monitor-->>RB: NOT_EQUIPPED
        RB-->>User: "Gmail 연결이 필요합니다"
    else 아이템 장착됨
        Monitor-->>RB: EQUIPPED
        RB->>Skill: 이메일 발송 요청
        Skill->>Gmail: API 호출
        Gmail-->>Skill: 발송 완료
        Skill-->>RB: 성공
        RB-->>User: "메일을 보냈습니다"
    end

5. 3초 룰 대응

5.1 비동기 처리 패턴

@app.post("/api/slack/events")
async def handle_slack_event(request: Request):
    # 1. 즉시 응답 (3초 내)
    background_tasks.add_task(process_event, event_data)
    return {"status": "ok"}

async def process_event(event_data):
    # 2. 실제 처리 (백그라운드)
    response = await generate_response(event_data)
    
    # 3. Slack API로 응답 전송
    await slack_client.chat_postMessage(
        channel=event_data['channel'],
        text=response
    )

5.2 타이핑 인디케이터

async def show_typing(channel: str):
    """처리 중임을 표시"""
    await slack_client.chat_postEphemeral(
        channel=channel,
        user=user_id,
        text="생각 중... 🤔"
    )

6. 데이터베이스 구조

6.1 Slack 관련 테이블

-- Slack 사용자 매핑 (더 이상 필요 없음 - UUID5 직접 사용)
-- 대신 users 테이블에서 직접 관리

-- 대화 로그
CREATE TABLE conversation_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID,  -- UUID5로 생성된 ID
    robeing_id VARCHAR(50),
    channel_id VARCHAR(50),
    message_type VARCHAR(20),  -- 'user' or 'bot'
    message TEXT,
    metadata JSONB,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

7. 에러 처리

7.1 일반 에러 응답

ERROR_MESSAGES = {
    "NO_USER": "사용자를 찾을 수 없습니다. 관리자에게 문의하세요.",
    "TIMEOUT": "처리 시간이 초과되었습니다. 다시 시도해주세요.",
    "SKILL_ERROR": "기능 실행 중 오류가 발생했습니다.",
    "AUTH_ERROR": "인증이 필요합니다."
}

7.2 재시도 로직

@retry(max_attempts=3, delay=1)
async def send_slack_message(channel: str, text: str):
    """실패시 재시도하는 메시지 전송"""
    return await slack_client.chat_postMessage(
        channel=channel,
        text=text
    )

8. 모니터링

8.1 메트릭 수집

  • 응답 시간
  • 에러율
  • 사용자별 요청 수
  • 스킬 사용 통계

8.2 로그 구조

{
    "timestamp": "2025-08-21T10:30:00Z",
    "user_id": "UUID5-생성값",
    "slack_user_id": "U0925SXQFDK",
    "channel": "C1234567890",
    "message": "사용자 메시지",
    "response": "로빙 응답",
    "processing_time": 1.5,
    "status": "success"
}

9. 보안 고려사항

9.1 인증

  • Slack 서명 검증 필수
  • 재전송 공격 방지 (timestamp 검증)

9.2 권한 관리

  • 채널별 접근 권한
  • 사용자별 기능 제한
  • 레벨 기반 스킬 접근

10. 향후 개선사항

10.1 단기

  • 스레드 대화 지원
  • 이모지 반응 처리
  • 파일 업로드 지원

10.2 장기

  • Slack 워크플로우 통합
  • 블록 UI 활용
  • 슬래시 커맨드 확장

문서 끝