# robeing-monitor DB 스키마 불일치 ## 작성일: 2025-09-14 ## 작성자: admin ## 환경: 51124 서버, robeing-monitor, PostgreSQL ## 서비스 정보 - **서버**: 51124 (192.168.219.52) - **포트**: 9024 - **컨테이너**: robeing_monitor (38시간 가동 중, healthy) - **DB 연결**: postgresql://robeings:robeings@192.168.219.45:5432/main_db ## 문제 증상 Gateway에서 stats API 호출 시 매분마다 500 에러 발생 ### 에러 로그 ``` sqlalchemy.exc.ProgrammingError: column robeing.robeing_id does not exist LINE 1: SELECT robeing.id AS robeing_id_1, robeing.robeing_id AS rob... ``` ## 원인 robeing-monitor가 잘못된 테이블/컬럼 조회 - **코드 예상**: `robeing_stats` 테이블의 `robeing_id` 컬럼 - **실제 DB**: `robeing` 테이블의 `robeing_container_id` 컬럼 ## 현재 영향 - `/api/stats/{robeing_id}` 엔드포인트 실패 (500 에러) - Gateway 프록시: `http://192.168.219.52:9024/api/stats/rb8001` 호출 실패 ## Gateway에서 robeing-monitor 호출 경로 ```python # /home/admin/robeing-gateway/app/main.py @app.get("/api/stats/{robeing_id}") # 라인 296-297 target_url = f"http://192.168.219.52:9024/api/stats/{robeing_id}" @app.api_route("/api/preferences/{path:path}") # 라인 364-391 monitor_url = "http://192.168.219.52:9024" @app.api_route("/api/items/{path:path}") # 라인 150-167 monitor_url = os.getenv("MONITOR_URL", "http://192.168.219.52:9024") ``` ## robeing-monitor 제공 API - `GET /api/stats/{robeing_id}` - 로빙 스탯 조회 (500 에러) - `PUT /api/stats/{robeing_id}` - 스탯 업데이트 - `GET /api/preferences/{user_id}` - 사용자 설정 - `PUT /api/preferences/{user_id}` - 설정 업데이트 - `GET /api/items/gmail` - Gmail 아이템 조회 - `GET /healthz` - 헬스체크 (정상) ## 상태 ✅ 해결됨 - 2025-09-19 ## 이전 해결 시도 (2025-09-15) - robeing 테이블 관련 코드: robeing_id → robeing_container_id 수정 시도 - ConversationLog 테이블: robeing_id → robeing_container_id 수정 - SQLAlchemy 쿼리: 일부 수정 - 결과: /api/stats는 일시적 해결, /api/items/gmail은 미해결 ## 2025-09-19 최종 해결 ### 해결 내역 1. **첫 번째 수정** (커밋 9f302f0): - gmail_token 조회 시 user 테이블과 JOIN - WHERE `u.oauth_id = $1` 사용 - robeing 조회 시 `robeing_container_id` 사용 2. **두 번째 수정** (커밋 d76f2b7): - 엔드포인트 중복 경로 제거 (`/items/gmail` 중복 선언 제거) - unequip API의 slack_user_id 참조도 user JOIN으로 변경 - 모든 gmail_token 조회를 user.oauth_id 기준으로 통일 ### 테스트 결과 - `/api/items/gmail` 200 OK 응답 확인 - DB 스키마 에러 없음 - 서버 자동 배포 완료 (17:06:30) ## 2025-09-19 문제 재발견 및 명확화 (해결 전 상태) ### 실제로는 두 개의 별개 문제 1. **robeing 테이블 문제** (2025-09-15 부분 해결) - 엔드포인트: `/api/stats/{robeing_id}` - 원인: robeing.robeing_id 컬럼 없음 (실제: id, robeing_container_id) - 상태: 일부 해결됨 2. **gmail_token 테이블 문제** (2025-09-19 신규 발견) - 엔드포인트: `/api/items/gmail` - 에러: `asyncpg.exceptions.UndefinedColumnError` - 원인: gmail_token.robeing_id, gmail_token.slack_user_id 컬럼 없음 - 컨테이너 ID: 2e52e648f02f ### 확인된 테이블 구조 (51123 서버 PostgreSQL - 2025-09-19 재확인) ```sql -- robeing 테이블 id | uuid (PK) robeing_container_id | varchar(64) -- 'rb8001' 형태의 값 level, experience, memory 등 | integer -- robeing_id 컬럼 없음 -- gmail_token 테이블 (현재 0 rows) id | uuid (PK) user_id | uuid (user 테이블 FK) token_data | jsonb equipped_to | varchar(50) -- 현재 값 없음 is_equipped | boolean -- robeing_id, slack_user_id, robeing_container_id 컬럼 없음 -- user 테이블 id | uuid (PK) oauth_id | varchar -- Slack ID ('U'로 시작) 또는 Google ID -- 총 14명: Slack 8명, Google 6명 ``` ### robeing-monitor 코드 문제점 (구체적 위치) 1. `/app/state/database.py`: - SQLAlchemy 모델이 Integer PK 정의 (실제 DB는 UUID) - RobeingStats는 클래스명, 실제 테이블은 robeing 2. `/app/api/items.py` 에러 발생 지점: - 101행: SELECT에서 `robeing_id,` 조회 → 컬럼 없음 에러 - 108행: WHERE `slack_user_id = $1` → 컬럼 없음 - 130행: `row['robeing_id']` 접근 시도 - 207행: `equipped_to = request.robeing_id` 설정 (이건 작동) 3. 실제 필요한 JOIN: - gmail_token.user_id = user.id - WHERE user.oauth_id = {slack_id} ## 2025-09-15 업데이트 ### DB 스키마 확인 (51123 서버) ```sql -- robeing 테이블 실제 구조 id | uuid product_id | uuid team_id | uuid name | varchar(32) level | integer experience | integer memory | integer compute | integer react | integer empathy | integer leadership | integer updated_at | timestamp created_at | timestamp robeing_container_id | varchar(64) robeing_container_url | varchar(128) ``` ### 확인된 사실 - **robeing_id 컬럼 없음** 확인 - robeing-monitor는 51124 서버에서 실행 중 - 51123 서버 DB에는 id(UUID) 컬럼만 존재 ### 문제 원인 분석 SQLAlchemy 쿼리 분석: ```sql SELECT robeing.id AS robeing_id_1, robeing.robeing_id AS rob... ``` - robeing.id를 robeing_id_1로 별칭 처리 - 존재하지 않는 robeing.robeing_id 컬럼도 조회 시도 - **원인**: robeing-monitor의 SQLAlchemy 모델에 id와 robeing_id 둘 다 정의됨 ### 해결 방안 (2025-09-19 최종) **즉시 수정 가능한 부분 (items.py)**: ```python # 101행: robeing_id, 제거 또는 equipped_to로 변경 SELECT id, user_id, equipped_to, is_equipped, token_data... # 108행: slack_user_id를 user JOIN으로 변경 FROM gmail_token gt JOIN "user" u ON gt.user_id = u.id WHERE u.oauth_id = $1 # 130행: row['robeing_id'] → row.get('equipped_to')로 변경 ``` **구조적 개선 필요**: - database.py: Integer → UUID 타입 수정 - 모든 robeing_id 참조를 id 또는 robeing_container_id로 통일 **로컬 작업 절차**: 1. robeing-monitor 레포 clone 2. 위 수정사항 적용 3. Git push → 51124 자동 배포 4. 테스트: `curl http://192.168.219.52:9024/api/items/gmail` **테스트**: ```bash # 51124 서버에서 curl http://192.168.219.52:9024/api/stats/rb8001 curl http://192.168.219.52:9024/api/items/gmail ```