192.168.219.45 → 192.168.0.100 일괄 변경 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.1 KiB
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 활용
- 슬래시 커맨드 확장
문서 끝