Slack 사용자 매핑 구현 계획 추가 (로컬 개발자)
- 24서버팀 분석 기반 구현 계획 작성 - 임시 하드코딩 방안 제시 - Auth 서버 API 설계 - Gateway 라우팅 개선안 - SQL 스키마 및 테스트 계획 포함
This commit is contained in:
parent
a1271f3950
commit
8503cd7b0a
352
ideas/250812_로컬개발자_Slack사용자매핑_구현계획.md
Normal file
352
ideas/250812_로컬개발자_Slack사용자매핑_구현계획.md
Normal file
@ -0,0 +1,352 @@
|
||||
# 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(robing_id: str, user_id: str, memory_type: str) -> str:
|
||||
"""사용자별 컬렉션 이름 생성"""
|
||||
if user_id == "default_user":
|
||||
# 매핑 없는 사용자는 공용 컬렉션 사용
|
||||
return f"{robing_id}_{memory_type}"
|
||||
else:
|
||||
# 사용자별 전용 컬렉션
|
||||
return f"{robing_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.ROBING_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.robing_id
|
||||
FROM slack_user_mapping sum
|
||||
JOIN users u ON sum.user_id = u.id
|
||||
LEFT JOIN workspace_members 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"],
|
||||
"robing_id": result["robing_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()
|
||||
robing_id = mapping["robing_id"]
|
||||
user_id = mapping["user_id"]
|
||||
else:
|
||||
# 매핑 없으면 기본 로빙 사용
|
||||
robing_id = "rb10508_micro"
|
||||
user_id = "default_user"
|
||||
|
||||
# 해당 로빙으로 전달
|
||||
robing_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 = robing_urls.get(robing_id)
|
||||
if not target_url:
|
||||
return {"error": "Unknown robing"}
|
||||
|
||||
# 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_workspaces(id),
|
||||
user_id UUID REFERENCES users(id) NOT NULL,
|
||||
workspace_member_id UUID REFERENCES workspace_members(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 users 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_test_aaaaaaaa_episodic: N개
|
||||
# rb10508_test_bbbbbbbb_episodic: M개
|
||||
# rb10508_test_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서버팀의 분석을 기반으로 로컬 개발자가 작성한 구현 계획입니다.*
|
||||
Loading…
x
Reference in New Issue
Block a user