DOCS/250817_slack_user_mapping_troubleshooting.md
happybell80 6690919bad docs: 문서 파일명에 작성 날짜 추가
- 250814_rb8001_통합_트러블슈팅.md (8월 14일 작성)
- 250817_email_skill_integration_status.md (8월 17일 작성)
- 250817_slack_user_mapping_troubleshooting.md (8월 17일 작성)

일관된 파일명 형식: YYMMDD_제목.md
2025-08-17 23:49:13 +09:00

4.2 KiB

Slack User ID → 이름 매핑 문제 해결

문제점

rb8001에서 Slack 사용자 ID (예: U0925SXQFDK)를 실제 이름으로 변환하지 못함

현재 상태

  1. 메모리 검색: 정상 작동

    • ChromaDB에 22개 메모리 저장됨
    • search_memories() 함수 정상 작동
    • 과거 대화 검색 가능
  2. 사용자 이름 매핑: 미구현

    • slack_user_mapping 테이블 없음
    • Slack API를 통한 사용자 정보 조회 미구현

해결 방안

1. slack_user_mapping 테이블 생성

CREATE TABLE slack_user_mapping (
    id SERIAL PRIMARY KEY,
    slack_user_id VARCHAR(50) UNIQUE NOT NULL,
    user_name VARCHAR(100),
    display_name VARCHAR(100),
    real_name VARCHAR(100),
    email VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2. Slack API로 사용자 정보 가져오기

from slack_sdk import WebClient

class SlackUserService:
    def __init__(self, slack_token):
        self.client = WebClient(token=slack_token)
        
    async def get_user_info(self, user_id: str):
        """Slack API로 사용자 정보 조회"""
        try:
            response = self.client.users_info(user=user_id)
            if response["ok"]:
                user = response["user"]
                return {
                    "slack_user_id": user_id,
                    "user_name": user.get("name"),
                    "display_name": user["profile"].get("display_name"),
                    "real_name": user["profile"].get("real_name"),
                    "email": user["profile"].get("email")
                }
        except Exception as e:
            logger.error(f"Failed to get user info: {e}")
        return None

3. 캐싱 전략

class UserNameCache:
    def __init__(self):
        self.cache = {}  # {user_id: user_info}
        self.db = StateServiceClient()
        
    async def get_user_name(self, user_id: str) -> str:
        # 1. 캐시 확인
        if user_id in self.cache:
            return self.cache[user_id]["display_name"]
            
        # 2. DB 확인
        user_info = await self.db.get_user_mapping(user_id)
        if user_info:
            self.cache[user_id] = user_info
            return user_info["display_name"]
            
        # 3. Slack API 호출
        slack_service = SlackUserService(settings.SLACK_BOT_TOKEN)
        user_info = await slack_service.get_user_info(user_id)
        
        if user_info:
            # DB에 저장
            await self.db.save_user_mapping(user_info)
            self.cache[user_id] = user_info
            return user_info["display_name"]
            
        return user_id  # 실패 시 ID 반환

4. 메시지 처리 시 이름 변환

# slack_handler.py
async def handle_message(event):
    user_id = event.get("user")
    
    # 사용자 이름 가져오기
    user_name = await user_name_cache.get_user_name(user_id)
    
    # 메시지 처리
    message = event.get("text")
    logger.info(f"Message from {user_name} ({user_id}): {message}")
    
    # 메모리 저장 시 이름 포함
    await memory_manager.add_memory(
        content=f"{user_name}: {message}",
        metadata={
            "user_id": user_id,
            "user_name": user_name,
            "channel_id": channel_id
        }
    )

구현 우선순위

  1. Phase 1: 인메모리 캐시만 구현

    • Slack API로 실시간 조회
    • 메모리에 캐싱
  2. Phase 2: DB 저장 추가

    • PostgreSQL에 slack_user_mapping 테이블 생성
    • 조회한 정보 영구 저장
  3. Phase 3: 배치 업데이트

    • 주기적으로 사용자 정보 갱신
    • 변경사항 감지

테스트 시나리오

# 1. 새로운 사용자 메시지
assert get_user_name("U0925SXQFDK") == "희재"

# 2. 캐시된 사용자
assert get_user_name("U0925SXQFDK") == "희재"  # API 호출 없음

# 3. 알 수 없는 사용자
assert get_user_name("UNKNOWN") == "UNKNOWN"

참고 사항

  • Slack API rate limit: 1분당 50회
  • users.info API는 user:read 스코프 필요
  • 사용자 정보는 변경될 수 있으므로 주기적 갱신 필요