DOCS/troubleshooting/250823_happybell80_rb8001_레벨1_표시_문제.md

5.6 KiB

rb8001 레벨 1 표시 문제 해결

작성일: 2025-08-23 (수정: 2025-08-28)

작성자: happybell80 with Claude

상태: 해결 완료


1. 문제 상황

증상

  • DB에 rb8001의 레벨이 20으로 저장되어 있음
  • 프론트엔드에서는 계속 레벨 1로 표시됨
  • /api/stats/rb8001 요청시 기본값(레벨 1) 반환

영향

  • 사용자가 로빙의 실제 성장 상태를 확인할 수 없음
  • Gmail 아이템 등 레벨 기반 기능 사용 제한

2. 원인 분석

2.1 Gateway의 하드코딩 문제

위치: robeing-gateway/app/main.py:267

# 문제 코드
response = await http_client.get(
    f"http://192.168.219.52:10508/api/stats/{robeing_id}"  # 항상 rb10508_micro로!
)

문제점:

  • 모든 stats 요청을 rb10508_micro(포트 10508)로 강제 라우팅
  • 사용자별 로빙 무시

2.2 프론트엔드 헤더 누락

위치: frontend-customer/src/services/robeing-api.ts:447

// 문제 코드
const response = await fetch(`${ROBEING_API_URL}/api/stats/${robeingId}`);
// X-User-Id 헤더 없음!

문제점:

  • Gateway가 사용자를 식별할 수 없음
  • 기본값 반환

2.3 잘못된 엔드포인트 경로

실제 엔드포인트:

  • rb8001: /stats (api 없음)
  • rb10508_micro: /stats/{robeing_id} (api 없음)

Gateway 요청:

  • /api/stats/{robeing_id} → 404 에러

2.4 서버 위치 오류

Gateway 설정:

  • rb8001: http://localhost:8001 → 51123 서버에 없음

실제 위치:

  • rb8001: http://192.168.219.52:8001 (51124 서버)

2.5 State Service 연결 실패 (제거됨 - State Service 사용 안 함)

  • State Service는 더 이상 사용하지 않음
  • rb8001이 직접 DB 접근하여 스탯 조회

3. 해결 과정

3.1 Gateway 사용자별 라우팅 구현

# robeing-gateway/app/main.py
@app.get("/api/stats/{robeing_id}")
async def get_stats(
    robeing_id: str,
    x_user_id: Optional[str] = Header(None)  # 사용자 식별 추가
):
    # 사용자별 로빙 정보 조회
    robeing_info = await get_robeing_info(x_user_id)
    
    # 로빙별 올바른 서버로 라우팅
    if robeing_id == "rb8001":
        target_url = f"http://192.168.219.52:8001/stats"
    elif robeing_id == "rb10508_micro":
        target_url = f"http://192.168.219.52:10508/stats/{robeing_id}"

3.2 프론트엔드 헤더 추가

// frontend-customer/src/services/robeing-api.ts
const headers: HeadersInit = {
    'X-User-Id': userId,
    'Authorization': `Bearer ${authToken}`
};

const response = await fetch(`${ROBEING_API_URL}/api/stats/${robeingId}`, {
    headers
});

3.3 응답 형식 정규화

# rb8001 응답 형식 변환
if robeing_id == "rb8001" and "stats" in data:
    return {
        "robeing_id": robeing_id,
        "level": data.get("level", 1),
        "experience": stats_data.get("experience", 0),
        # ... 표준 형식으로 변환
    }

4. 해결 진행 (2025-08-28)

4.1 완료된 작업

  • robeing-monitor: state_service를 /api 경로로 마운트
  • Gateway: 모든 로빙을 robeing-monitor로 통합
    # 조건문 제거, 모든 로빙 통합
    target_url = f"http://192.168.219.52:9024/api/stats/{robeing_id}"
    
  • DB: robeing_stats에 스탯 컬럼 추가 (memory, compute 등)
  • 테스트: robeing-monitor 직접 호출 시 레벨 20 반환 확인

4.2 새로운 문제 발견 및 해결 완료

robeing_id가 undefined로 전달됨

Gateway 로그:
GET /api/stats/undefined HTTP/1.0" 200 OK
GET /api/user/robeing → 404 Not Found

원인: Frontend가 잘못된 엔드포인트 호출

  • 시도: /api/user/robeing (없음)
  • 실제: /api/robeing/info (Gateway에 있음)

해결 완료 (로컬 개발자):

  1. Gateway: /api/robeing/info JWT 인증 적용
    x_user_id: str = Depends(get_verified_user)
    
  2. Frontend: 엔드포인트 변경
    // 변경 전: /api/user/robeing
    // 변경 후: /api/robeing/info
    

4.3 라우트 우선순위 문제 (2025-08-28 추가)

문제 발견: /api/robeing/info가 작동 안함

GET http://192.168.219.52:8001/api/robeing/info → 404 Not Found

원인: FastAPI 라우트 정의 순서

  • /api/{path:path} (line 408) - catch-all 패턴이 먼저 매칭
  • /api/robeing/info (line 431) - 도달하지 못함
  • 결과: robeing/info가 rb8001로 프록시되어 404

해결 필요:

  • /api/robeing/info/api/{path:path}보다 앞에 정의
  • 또는 catch-all 패턴에서 robeing/info 제외

4.4 최종 플로우 해결 완료

Frontend (JWT) → Gateway /api/stats/rb8001 → robeing-monitor → DB
                              ↓
                    레벨 20 정상 반환 및 표시

해결 완료 (2025-08-28 16:40):

  • Frontend가 레벨 20 정상 표시
  • DB 값이 UI에 올바르게 반영됨

5. 교훈

아키텍처 일관성

  • 모든 로빙이 동일한 API 경로 규칙을 따라야 함
  • /api/stats vs /stats 혼용 문제

헤더 전달 중요성

  • 프론트엔드는 항상 사용자 식별 헤더 포함 필요
  • X-User-Id, Authorization 등

환경변수 관리

  • 필수 서비스 URL은 반드시 설정
  • 누락시 fallback 처리 필요

디버깅 체인

  1. 프론트엔드 요청 확인
  2. Gateway 라우팅 확인
  3. 실제 서비스 엔드포인트 확인
  4. 서비스 내부 로직 확인

6. 참고 파일

  • robeing-gateway/app/main.py
  • frontend-customer/src/services/robeing-api.ts
  • rb8001/main.py
  • rb8001/app/router/router.py
  • rb8001/docker-compose.yml