DOCS/book/300_architecture/320_Slack_기반_인터페이스_설계.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

7.1 KiB

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

작성일: 2025-08-21

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


1. 개요

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


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->>Auth: 매핑 API 호출
    Note over RB: /api/slack/mapping/{slack_id}
    Auth-->>RB: UUID 반환
    
    RB->>DB: 사용자 조회 (UUID)
    DB-->>RB: 사용자 정보
    
    RB->>RB: 의도 분류
    RB->>Skill: 필요시 스킬 호출
    Skill-->>RB: 처리 결과
    
    RB->>Skill: Slack 전송 요청
    Skill-->>Slack: chat.postMessage
    Slack-->>User: 메시지 표시

2.2 사용자 식별 체계

매핑 API 호출

import httpx

async def get_user_uuid(slack_user_id: str) -> str:
    """51123 매핑 API를 통해 UUID 조회"""
    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"http://192.168.0.100:9000/api/slack/mapping/{slack_user_id}"
        )
        if response.status_code == 200:
            return response.json()["user_id"]
    return None

# 예시
slack_id = "U0925SXQFDK"
user_uuid = await get_user_uuid(slack_id)  # DB에서 UUID 조회

사용자 데이터 구조

-- users 테이블
CREATE TABLE user (
    id UUID PRIMARY KEY,  -- slack_user_mapping에서 조회
    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[매핑 API 호출]
    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->>Auth: 매핑 API 호출
    Auth-->>RB: UUID 반환
    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. skill-slack을 통해 응답 전송
    async with httpx.AsyncClient() as client:
        await client.post(
            "http://skill-slack:8502/api/v1/send",
            json={"channel": event_data["channel"], "text": response}
        )

5.2 타이핑 인디케이터

async def show_typing(channel: str):
    """처리 중임을 표시 (전송은 skill-slack 경유)"""
    # 구현 시 skill-slack 지원 엔드포인트 사용 또는 안내 메시지를 /send로 전달

6. 데이터베이스 구조

6.1 Slack 관련 테이블

-- Slack 사용자 매핑 (slack_user_mapping 테이블 사용)
-- 대신 users 테이블에서 직접 관리

-- 대화 로그
CREATE TABLE conversation_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID,  -- slack_user_mapping에서 조회한 UUID
    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):
    """실패시 재시도하는 메시지 전송 (skill-slack 경유)"""
    async with httpx.AsyncClient() as client:
        return await client.post(
            "http://skill-slack:8502/api/v1/send",
            json={"channel": channel, "text": text}
        )

8. 모니터링

8.1 메트릭 수집

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

8.2 로그 구조

{
    "timestamp": "2025-08-21T10:30:00Z",
    "user_id": "매핑된-UUID",
    "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 활용
  • 슬래시 커맨드 확장

문서 끝