DOCS/journey/troubleshooting/250911_happybell80_PostgreSQL_테이블명_단수형_통일.md
happybell80 0252dd1a7f fix: 51123 서버 IP 주소 업데이트 (성수 이전)
192.168.219.45 → 192.168.0.100 일괄 변경

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:52:26 +09:00

13 KiB

PostgreSQL 테이블명 단수형 통일 작업

작성 정보

  • 작성일: 250911
  • 작성자: happybell80
  • 문제: 코드는 복수형, DB는 단수형 테이블명 사용으로 불일치

1. 데이터베이스 연결 정보

메인 데이터베이스

  • main_db: postgresql://robeings:robeings@192.168.0.100:5432/main_db
  • robeing_metrics: postgresql://postgres:@172.17.0.1:5432/robeing_metrics

사용 라이브러리

저장소 라이브러리
auth-server SQLAlchemy, asyncpg, psycopg2
rb8001 SQLAlchemy
rb10508_micro asyncpg
frontend-base asyncpg
robeing-gateway SQLAlchemy, asyncpg
robeing-monitor SQLAlchemy, asyncpg
skill-email psycopg2

2. 테이블별 사용 위치 상세

2.1 user 테이블 (실제 DB: user, 코드: users 참조)

실제 DB 컬럼: id(UUID), team_id(FK), email, username, oauth_provider, oauth_id, created_at, updated_at 코드 모델 차이:

  • auth-server: picture 컬럼 (DB에 없음)
  • robeing-gateway: avatar_url, provider, provider_id (DB에 없음)
파일 경로 라인 번호 작업 내용
auth-server/app/models/user.py 21-35 테이블 모델 정의 (picture 컬럼)
auth-server/app/providers/gmail_passport.py 178 SELECT id FROM user WHERE username = $1
auth-server/app/providers/gmail_passport.py 187 SELECT username FROM user WHERE id = $1
robeing-gateway/app/database.py 98-112 JOIN 쿼리 (workspace_member, workspaces와 함께)
robeing-gateway/app/database.py 182-191 SELECT * FROM user WHERE username = $1
robeing-gateway/app/database.py 321-328 SELECT * FROM user (전체 조회)
robeing-gateway/app/models.py 11-24 테이블 모델 정의 (avatar_url 컬럼)

2.2 company/team 테이블 (실제 DB: company+team, 코드: workspaces 참조)

실제 DB: company(id, name, url) + team(id, company_id, name) 구조 코드 모델: workspaces 테이블 참조 (DB에 없음)

파일 경로 라인 번호 작업 내용
auth-server/app/models/workspace.py 15-31 테이블 모델 정의
auth-server/scripts/run_migration.py 54 SELECT id, name, subdomain, robeing_id FROM workspaces
auth-server/migrations/add_user_workspace_tables.sql 25-28 ALTER TABLE company RENAME TO workspaces
robeing-gateway/app/database.py 98-112 JOIN 쿼리
robeing-gateway/app/database.py 216-230 JOIN 쿼리 (workspace_member와 함께)
robeing-gateway/app/models.py 26-44 테이블 모델 정의

2.3 workspace_member 테이블 (실제 DB: workspace_member, 레코드 0건)

실제 DB 컬럼: user_id(FK), role(user_role enum), created_at, updated_at 코드 모델: workspace_member 테이블 참조 (복수형 차이)

파일 경로 라인 번호 작업 내용
auth-server/app/models/user.py 46-60 테이블 모델 정의
auth-server/migrations/add_user_workspace_tables.sql 41-52 CREATE TABLE 정의
robeing-gateway/app/database.py 98-112 JOIN 쿼리
robeing-gateway/app/database.py 263-266 중복 체크 쿼리
robeing-gateway/app/database.py 274-282 UPDATE 쿼리
robeing-gateway/app/database.py 285-299 INSERT 쿼리 (gen_random_uuid() 사용)
robeing-gateway/app/models.py 46-60 테이블 모델 정의

2.4 slack_user_mapping 테이블 (실제 DB: 없음)

코드에서 참조하나 실제 DB에 테이블 없음

파일 경로 라인 번호 작업 내용
auth-server/app/providers/slack.py 266 SELECT user_id FROM slack_user_mapping WHERE slack_user_id = :slack_user_id AND slack_workspace_id = :slack_workspace_id
auth-server/app/providers/slack.py 308-312 INSERT INTO slack_user_mapping ... DO UPDATE SET (slack_user_id, slack_workspace_id 조합 기준)
rb8001/app/router/router.py 92 SELECT user_id FROM slack_user_mapping WHERE slack_user_id = :slack_id
robeing-gateway/app/main.py 479-486 SELECT user_id FROM slack_user_mapping WHERE slack_user_id = $1

2.5 slack_workspace 테이블 (실제 DB: slack_workspace, 2건)

실제 DB 컬럼: id(UUID), team_id(FK), team_name, bot_token, bot_user_id, app_id, scope(json), created_at, updated_at 코드 모델: slack_workspaces 참조 (복수형 차이)

파일 경로 라인 번호 작업 내용
auth-server/app/models/workspace.py 43-62 테이블 모델 정의 (현재 company_id로 정의됨)
auth-server/migrations/add_user_workspace_tables.sql 60-76 ALTER TABLE 정의 (company_id → workspace_id 변경)
robeing-gateway/app/main.py 522-525 SELECT bot_token FROM slack_workspace WHERE team_id = $1
robeing-gateway/app/main.py 567-570 SELECT bot_token FROM slack_workspace WHERE team_id = $1

2.6 gmail_token 테이블 (실제 DB: gmail_token, 3건)

실제 DB 컬럼: user_id(FK), token_data(jsonb), oauth_config(jsonb), created_at, updated_at 코드 혼용: auth-server는 gmail_token(복수)와 gmail_token(단수) 혼용

파일 경로 라인 번호 작업 내용
auth-server/app/providers/gmail_passport.py 193 INSERT INTO gmail_token (단수)
auth-server/app/providers/gmail_passport.py 264, 313, 354, 370 gmail_token (단수) 사용
auth-server/app/api/gmail_refresh.py 36, 43, 115, 124, 173, 179 gmail_token (복수) 사용
skill-email/services/db_credentials_provider.py 60-70 SELECT 쿼리 (is_equipped 필터)
skill-email/services/db_credentials_provider.py 163-177 UPDATE 쿼리
skill-email/services/db_credentials_provider.py 231-235 COUNT 쿼리
robeing-monitor/app/api/items.py 97-111 SELECT 쿼리
robeing-monitor/app/api/items.py 185-190 소유권 확인 쿼리
robeing-monitor/app/api/items.py 197-211 UPDATE 쿼리 (장착/해제)
robeing-monitor/app/api/items.py 252-256 UPDATE 쿼리 (해제)
robeing-monitor/app/api/items.py 307-319 SELECT 쿼리 (장착된 토큰)
robeing-monitor/app/api/items.py 384-388 DELETE 쿼리

2.7 gmail_audit_logs 테이블 (실제 DB: 없음)

코드에서 참조하나 실제 DB에 테이블 없음

파일 경로 라인 번호 작업 내용
robeing-monitor/app/api/items.py 214-223 INSERT 쿼리
robeing-monitor/app/api/items.py 263-270 INSERT 쿼리
robeing-monitor/app/api/items.py 395-401 INSERT 쿼리

2.8 robeing 테이블 (실제 DB: robeing, 2건)

실제 DB 컬럼: id(UUID), product_id(FK), team_id(FK), memory, compute, react, empathy, leadership, created_at, updated_at 코드 모델: robeing_stats 테이블 참조 (테이블명 불일치)

파일 경로 라인 번호 작업 내용
rb8001/app/state/database.py 24-38 테이블 모델 정의
rb10508_micro/app/api/endpoints.py 155-161 SELECT 쿼리
robeing-monitor/app/state/database.py 19-33 테이블 모델 정의
robeing-monitor/app/state/state_service.py 55 SELECT 쿼리
robeing-monitor/app/state/state_service.py 59-62 INSERT 쿼리
robeing-monitor/app/state/state_service.py 81 SELECT 쿼리
robeing-monitor/app/state/state_service.py 215 SELECT 전체 조회
robeing-monitor/app/api/items.py 169-173 SELECT level 쿼리

2.9 robeing_settings 테이블 (실제 DB: 없음)

코드에서 참조하나 실제 DB에 테이블 없음

파일 경로 라인 번호 작업 내용
rb8001/app/state/database.py 40-50 테이블 모델 정의
robeing-monitor/app/state/database.py 35-45 테이블 모델 정의
robeing-monitor/app/state/state_service.py 110 SELECT 쿼리
robeing-monitor/app/state/state_service.py 114-117 INSERT 쿼리
robeing-monitor/app/state/state_service.py 133 SELECT 쿼리

2.10 conversation_log 테이블 (실제 DB: conversation_log, 0건)

실제 DB 컬럼: user_id(FK), message, response, intent, channel_id, timestamp 등 코드 수정: robeing_id, slack_user_id 컬럼 제거 (DB에 없음), channel_id만 사용

파일 경로 라인 번호 작업 내용
rb8001/app/state/database.py 52-65 테이블 모델 정의 (slack_user_id 포함)
rb8001/app/state/database.py 86-92 SELECT 쿼리 (최근 대화, slack_user_id 포함)
rb8001/app/router/router.py 464-496 INSERT 쿼리 (slack_user_id 포함)
robeing-monitor/app/state/database.py 47-59 테이블 모델 정의 (slack_user_id 없음)
robeing-monitor/app/state/state_service.py 156-159 INSERT 쿼리 (slack_user_id 없음)
robeing-monitor/app/state/state_service.py 173-177 SELECT 쿼리 (최근 로그, slack_user_id 없음)
robeing-monitor/app/state/state_service.py 194-203 SELECT 쿼리 (7일간, slack_user_id 없음)

2025-12-04 추가:

  • rb8001/app/state/conversation_repository.py에서 ConversationLog 생성 시 실제 스키마에 맞춰 user_id+channel_id만 저장하도록 수정, ensure_conversation_log_sequence_aligned()로 시퀀스 정렬 후 INSERT 일관 유지

2.11 user_preference 테이블 (실제 DB: user_preference, 0건)

실제 DB 컬럼: user_id(FK), slack_user_id, news_keywords[], briefing_enabled, briefing_time, created_at, updated_at 코드 모델: user_preference 참조 (복수형 차이)

파일 경로 라인 번호 작업 내용
rb8001/app/skills/dm_skill.py 466 SELECT news_keywords WHERE user_id = :user_id
rb8001/app/skills/dm_skill.py 471 SELECT news_keywords WHERE slack_user_id = :slack_user_id
robeing-monitor/app/api/monitor.py 148-165 SELECT 쿼리
robeing-monitor/app/api/monitor.py 276-292 UPDATE 쿼리 (동적 쿼리 생성)
robeing-monitor/app/api/monitor.py 309-316 INSERT 쿼리

2.12 news 테이블 (실제 DB: news, 0건)

실제 DB 컬럼: user_id(FK), data(jsonb), created_at, updated_at 코드 참조: 확인 필요

2.13 product 테이블 (실제 DB: product, 1건)

실제 DB 컬럼: id(UUID), name, app_id, scope(json), created_at, updated_at 코드 참조: 확인 필요

파일 경로 라인 번호 작업 내용
frontend-base/backend/metrics_database.py 58-60 INSERT INTO system_metrics
frontend-base/backend/metrics_database.py 93-100 SELECT (time_bucket 사용)

3. 수정 작업 체크리스트

3.1 테이블 구조 변경 시

  • SQLAlchemy 모델 클래스 수정
  • Migration 스크립트 작성
  • 기존 데이터 마이그레이션 계획

3.2 컬럼명 변경 시

  • 모든 SELECT 쿼리 수정
  • 모든 INSERT 쿼리 수정
  • 모든 UPDATE 쿼리 수정
  • 모든 DELETE 쿼리 수정
  • SQLAlchemy 모델 속성명 수정
  • API 응답 필드명 확인

3.3 저장소별 작업

  • auth-server: 이미 완료 상태
  • rb8001: 테이블명 단수형 변경 완료
  • rb10508_micro: 사용 안함 (폐기 예정)
  • frontend-base: system_metrics 비활성화 완료
  • robeing-gateway: 이미 완료 상태
  • robeing-monitor: 테이블명 변경 및 import 수정 완료
  • skill-email: gmail_token 변경 완료

3.4 테스트 계획

  • 각 저장소별 단위 테스트
  • 통합 테스트 (API 엔드포인트)
  • 데이터 마이그레이션 테스트
  • 롤백 계획 수립

4. 위험 요소 - 치명적 불일치

  1. 테이블명 전체 불일치:

    • 코드: users, workspaces, workspace_member (복수형)
    • DB: user, company/team, workspace_member (단수형)
  2. 존재하지 않는 테이블 참조:

    • slack_user_mapping (코드 참조, DB 없음)
    • gmail_audit_logs (코드 참조, DB 없음)
    • robeing_settings (코드 참조, DB 없음)
    • workspaces (코드 참조, DB는 company+team)
  3. 구조 불일치:

    • 코드: workspace 중심 구조
    • DB: company → team → user 계층 구조
  4. 비어있는 주요 테이블 (레코드 0건):

    • conversation_log, gmail_token, news, user_preference, workspace_member

5. 수행 결과

  1. 완료: 모든 복수형 테이블명을 단수형으로 통일
  2. 주석 처리: 존재하지 않는 테이블 참조 코드
  3. 추가 수정: conversation_log의 robeing_id, slack_user_id 컬럼 제거 (DB에 없음)
  4. 배포 성공: 모든 서비스 정상 동작 확인

6. 롤백 계획

  1. 변경 전 전체 데이터베이스 백업
  2. 각 테이블별 데이터 덤프
  3. 이전 버전 코드 태그 생성
  4. 단계별 배포 (테스트 환경 → 프로덕션)

교훈:

  • DB 테이블명은 단수형 사용이 PostgreSQL 관례
  • 코드와 DB 구조 일치 필수
  • 존재하지 않는 테이블은 주석 처리로 명시