# Slack 사용자 매핑 구현 계획 (로컬 개발자) 작성일: 2025년 8월 12일 작성자: 로컬 개발자 (Claude) 관련 문서: 250812_claude_Slack_메시지_처리_아키텍처_분석.md ## 1. 구현 목표 24서버팀 분석을 기반으로 로컬 개발자가 구현할 수 있는 Slack 사용자 매핑 시스템을 설계합니다. ### 1.1 핵심 문제 해결 - Slack user_id (U0925SXQFDK)와 시스템 user_id 연결 - 사용자별 ChromaDB 컬렉션 분리 - 멀티 워크스페이스 지원 ## 2. 구현 가능한 코드 변경사항 ### 2.1 rb10508_micro 임시 매핑 구현 **파일**: `rb10508_micro/app/services/slack_service.py` ```python # 임시 매핑 (DB 연동 전까지 사용) TEMP_USER_MAPPING = { "U0925SXQFDK": { "username": "happybell80", "user_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }, "U04KJHGLS": { "username": "eagle0914", "user_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" }, "UHHYONG91": { "username": "hhyong91", "user_id": "cccccccc-cccc-cccc-cccc-cccccccccccc" } } async def get_system_user_id(slack_user_id: str) -> str: """Slack user_id를 시스템 user_id로 변환""" mapping = TEMP_USER_MAPPING.get(slack_user_id) if mapping: return mapping["user_id"] return "default_user" ``` ### 2.2 ChromaDB 컬렉션 분리 **파일**: `rb10508_micro/app/core/memory/storage.py` ```python def get_user_collection_name(robeing_id: str, user_id: str, memory_type: str) -> str: """사용자별 컬렉션 이름 생성""" if user_id == "default_user": # 매핑 없는 사용자는 공용 컬렉션 사용 return f"{robeing_id}_{memory_type}" else: # 사용자별 전용 컬렉션 return f"{robeing_id}_{user_id[:8]}_{memory_type}" ``` ### 2.3 Brain 모듈 수정 **파일**: `rb10508_micro/app/core/brain.py` ```python async def think_functional( input_data: Dict, search_memories_fn, store_memory_fn, get_identity_fn, store_identity_fn, get_system_user_id_fn # 새로 추가 ) -> Dict: """개선된 think 함수""" # Slack user_id 변환 slack_user_id = input_data.get("user_id") system_user_id = await get_system_user_id_fn(slack_user_id) # 사용자별 컬렉션 사용 collection_name = get_user_collection_name( settings.ROBEING_ID, system_user_id, "episodic" ) # 이후 로직은 동일... ``` ## 3. Auth 서버 API 엔드포인트 설계 ### 3.1 Slack 매핑 조회 API **파일**: `auth-server/app/api/slack.py` ```python from fastapi import APIRouter, HTTPException from typing import Optional router = APIRouter(prefix="/api/slack") @router.get("/mapping/{slack_user_id}") async def get_user_mapping( slack_user_id: str, workspace_id: Optional[str] = None ): """Slack user_id로 시스템 사용자 정보 조회""" query = """ SELECT sum.user_id, u.username, u.email, wm.robeing_id FROM slack_user_mapping sum JOIN user u ON sum.user_id = u.id LEFT JOIN workspace_member wm ON u.id = wm.user_id WHERE sum.slack_user_id = :slack_user_id """ result = await database.fetch_one( query, {"slack_user_id": slack_user_id} ) if not result: raise HTTPException(404, "Mapping not found") return { "user_id": str(result["user_id"]), "username": result["username"], "email": result["email"], "robeing_id": result["robeing_id"] or "rb10508_micro" } @router.post("/mapping") async def create_user_mapping( slack_user_id: str, slack_workspace_id: int, user_id: str ): """새로운 Slack 사용자 매핑 생성""" query = """ INSERT INTO slack_user_mapping (slack_user_id, slack_workspace_id, user_id) VALUES (:slack_user_id, :slack_workspace_id, :user_id) ON CONFLICT (slack_user_id, slack_workspace_id) DO UPDATE SET user_id = :user_id RETURNING id """ result = await database.fetch_one( query, { "slack_user_id": slack_user_id, "slack_workspace_id": slack_workspace_id, "user_id": user_id } ) return {"mapping_id": str(result["id"])} ``` ## 4. Gateway 라우팅 개선 ### 4.1 사용자별 로빙 라우팅 **파일**: `robeing-gateway/app/main.py` ```python @app.post("/api/slack/route") async def route_slack_message( request: Request, background_tasks: BackgroundTasks ): """Slack 메시지를 적절한 로빙으로 라우팅""" body = await request.json() slack_user_id = body.get("event", {}).get("user") # Auth 서버에서 매핑 조회 async with httpx.AsyncClient() as client: resp = await client.get( f"http://localhost:8001/api/slack/mapping/{slack_user_id}" ) if resp.status_code == 200: mapping = resp.json() robeing_id = mapping["robeing_id"] user_id = mapping["user_id"] else: # 매핑 없으면 기본 로빙 사용 robeing_id = "rb10508_micro" user_id = "default_user" # 해당 로빙으로 전달 robeing_urls = { "rb8001": "http://192.168.219.52:8001", "rb10508_micro": "http://192.168.219.52:10508", "rb10408": "http://192.168.219.52:10408" } target_url = robeing_urls.get(robeing_id) if not target_url: return {"error": "Unknown robeing"} # X-System-User-Id 헤더 추가 headers = {"X-System-User-Id": user_id} async with httpx.AsyncClient() as client: resp = await client.post( f"{target_url}/api/slack/events", json=body, headers=headers ) return {"ok": True} ``` ## 5. SQL 스키마 ### 5.1 slack_user_mapping 테이블 ```sql -- Auth DB에 추가할 테이블 CREATE TABLE IF NOT EXISTS slack_user_mapping ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slack_user_id VARCHAR(100) NOT NULL, slack_workspace_id INTEGER REFERENCES slack_workspace(id), user_id UUID REFERENCES user(id) NOT NULL, workspace_member_id UUID REFERENCES workspace_member(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(slack_user_id, slack_workspace_id) ); -- 인덱스 추가 CREATE INDEX idx_slack_user_mapping_slack_user ON slack_user_mapping(slack_user_id); CREATE INDEX idx_slack_user_mapping_user ON slack_user_mapping(user_id); -- 초기 데이터 INSERT INTO slack_user_mapping (slack_user_id, slack_workspace_id, user_id) SELECT 'U0925SXQFDK', sw.id, u.id FROM user u, slack_workspaces sw WHERE u.username = 'happybell80' AND sw.team_name = 'GoodGang Labs'; ``` ## 6. 테스트 계획 ### 6.1 로컬 테스트 ```python # test_slack_mapping.py import asyncio from app.services.slack_service import get_system_user_id async def test_mapping(): # 매핑 있는 사용자 user_id = await get_system_user_id("U0925SXQFDK") assert user_id == "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" # 매핑 없는 사용자 user_id = await get_system_user_id("UNKNOWN") assert user_id == "default_user" print("✅ 모든 테스트 통과") asyncio.run(test_mapping()) ``` ### 6.2 ChromaDB 컬렉션 확인 ```python # 서버에서 실행 from app.core.memory.storage import get_chroma_client client = get_chroma_client() collections = client.list_collections() for col in collections: print(f"{col.name}: {col.count()}개") # 예상 결과: # rb10508_micro_aaaaaaaa_episodic: N개 # rb10508_micro_bbbbbbbb_episodic: M개 # rb10508_micro_default_user_episodic: K개 ``` ## 7. 배포 순서 ### Phase 1: 임시 하드코딩 (즉시) 1. rb10508_micro에 TEMP_USER_MAPPING 추가 2. ChromaDB 컬렉션 분리 로직 구현 3. 테스트 후 배포 ### Phase 2: DB 연동 (1주 내) 1. slack_user_mapping 테이블 생성 (23서버팀 요청) 2. Auth 서버 API 구현 3. rb10508_micro DB 조회 로직 추가 ### Phase 3: Gateway 통합 (2주 내) 1. Gateway 라우팅 로직 구현 2. 모든 로빙 통합 테스트 3. OAuth 자동 매핑 구현 ## 8. 주의사항 ### 8.1 호환성 유지 - 기존 데이터 마이그레이션 계획 필요 - 매핑 없는 사용자도 서비스 가능하도록 ### 8.2 보안 - Slack 토큰 안전한 관리 - user_id 노출 방지 ### 8.3 성능 - DB 조회 캐싱 고려 - 컬렉션 수 증가에 따른 관리 방안 ## 9. 24서버팀에게 요청사항 1. **DB 테이블 생성** - slack_user_mapping 테이블 생성 - 초기 데이터 입력 2. **서비스 재배포** - rb10508_micro 코드 변경 후 재배포 - Auth 서버 API 추가 후 재배포 3. **테스트 지원** - Slack 메시지 전송 테스트 - ChromaDB 데이터 확인 ## 10. 예상 효과 - ✅ 사용자별 개인화된 메모리 관리 - ✅ 멀티 워크스페이스 지원 - ✅ 확장 가능한 아키텍처 - ✅ OAuth 연동 준비 --- *이 문서는 24서버팀의 분석을 기반으로 로컬 개발자가 작성한 구현 계획입니다.*