# 테이블 관계도 (ERD) ## 작성일: 2025-08-20 --- ## 관계 다이어그램 ```mermaid erDiagram users ||--o{ workspace_members : "has many" users ||--o{ slack_user_mapping : "has many" users ||--o{ gmail_tokens : "has" users ||--o{ robeing_stats : "has" users ||--o{ conversation_logs : "has many" workspaces ||--o{ workspace_members : "has many" workspaces }o--|| companies : "belongs to" slack_workspaces ||--o{ slack_user_mapping : "has many" workspace_members ||--o{ slack_user_mapping : "references" gmail_tokens }o--|| users : "belongs to" robeing_stats }o--|| users : "belongs to" conversation_logs }o--|| users : "belongs to" ``` --- ## 핵심 관계 설명 ### 1. User 중심 관계 ``` users (1) ─────┬──── (*) workspace_members ├──── (*) slack_user_mapping ├──── (0..1) gmail_tokens ├──── (0..1) robeing_stats └──── (*) conversation_logs ``` - 한 사용자는 여러 워크스페이스의 멤버가 될 수 있음 - 한 사용자는 여러 Slack 워크스페이스와 매핑될 수 있음 - 한 사용자는 하나의 Gmail 토큰만 가질 수 있음 (현재 구조) - 한 사용자는 하나의 로빙 통계를 가짐 - 한 사용자는 여러 대화 로그를 가짐 ### 2. Workspace 관계 ``` companies (1) ──── (*) workspaces workspaces (1) ──── (*) workspace_members ``` - 한 회사는 여러 워크스페이스를 가질 수 있음 - 한 워크스페이스는 여러 멤버를 가질 수 있음 ### 3. Slack 매핑 관계 ``` slack_workspaces (1) ──── (*) slack_user_mapping slack_user_mapping (*) ──── (1) users slack_user_mapping (*) ──── (0..1) workspace_members ``` - Slack 사용자 ID를 시스템 UUID로 변환하는 브릿지 역할 - workspace_members와 선택적 연결 --- ## JOIN 예시 ### 1. 사용자 전체 정보 조회 ```sql SELECT u.id, u.username, u.name, u.email, wm.robing_id, sum.slack_user_id, gt.is_equipped as gmail_equipped FROM users u LEFT JOIN workspace_members wm ON u.id = wm.user_id LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id LEFT JOIN gmail_tokens gt ON u.id = gt.user_id WHERE u.username = 'happybell80'; ``` ### 2. Slack ID로 사용자 찾기 ```sql SELECT sum.slack_user_id, u.id as user_uuid, u.username, u.name, u.email FROM slack_user_mapping sum JOIN users u ON sum.user_id = u.id WHERE sum.slack_user_id = 'U0925SXQFDK'; ``` ### 3. Gmail 아이템 장착 상태 확인 ```sql SELECT u.name, u.email, gt.is_equipped, gt.equipped_to, rs.level, gt.scopes FROM users u JOIN gmail_tokens gt ON u.id = gt.user_id LEFT JOIN robeing_stats rs ON u.id = rs.user_id WHERE gt.is_equipped = true; ``` ### 4. 워크스페이스 멤버 목록 ```sql SELECT w.name as workspace_name, u.name as user_name, u.email, wm.role, wm.robing_id FROM workspaces w JOIN workspace_members wm ON w.id = wm.workspace_id JOIN users u ON wm.user_id = u.id WHERE w.name = 'Ivada Robeing'; ``` --- ## 데이터 흐름 ### 1. Slack 메시지 처리 흐름 ``` Slack Message ↓ slack_user_id (U0925SXQFDK) ↓ slack_user_mapping 테이블 조회 ↓ user_id (UUID) 획득 ↓ 모든 서비스에서 UUID 사용 ``` ### 2. Gmail 아이템 사용 흐름 ``` User 요청 ↓ user_id (UUID)로 gmail_tokens 조회 ↓ is_equipped 확인 ↓ robeing_stats에서 레벨 확인 ↓ token_data에서 access_token 추출 ↓ Gmail API 호출 ``` ### 3. 프론트엔드 인증 흐름 ``` Google OAuth 로그인 ↓ users 테이블에 사용자 생성/업데이트 ↓ JWT 토큰 발급 (user_id 포함) ↓ 모든 API 요청에 JWT 사용 ↓ user_id로 권한 확인 ``` --- ## 데이터 정합성 규칙 ### 1. UUID 일관성 - 모든 `user_id` 컬럼은 `users.id`를 참조 - 외래키 제약으로 정합성 보장 - CASCADE 옵션 미사용 (명시적 삭제 필요) ### 2. 유니크 제약 - `users.email` - 중복 이메일 방지 - `users.username` - 중복 사용자명 방지 - `slack_workspaces.team_id` - 중복 Slack 팀 방지 - `robing_settings.robing_id` - 중복 설정 방지 ### 3. NULL 허용 정책 - 필수 관계: NOT NULL (user_id, workspace_id 등) - 선택적 관계: NULL 허용 (robeing_id, equipped_to 등) --- ## 트랜잭션 고려사항 ### 1. Gmail 아이템 장착 ```sql BEGIN; -- 1. 레벨 확인 SELECT level FROM robeing_stats WHERE user_id = ?; -- 2. 소유권 확인 SELECT id FROM gmail_tokens WHERE user_id = ?; -- 3. 장착 처리 UPDATE gmail_tokens SET is_equipped = true, equipped_to = ? WHERE user_id = ?; -- 4. 감사 로그 INSERT INTO gmail_audit_logs (user_id, action, success) VALUES (?, 'equip', true); COMMIT; ``` ### 2. 사용자 생성 ```sql BEGIN; -- 1. users 테이블 INSERT INTO users (id, email, name, username) VALUES (?, ?, ?, ?); -- 2. workspace_members 추가 INSERT INTO workspace_members (user_id, workspace_id, role) VALUES (?, ?, 'member'); -- 3. robeing_stats 초기화 INSERT INTO robeing_stats (user_id, robeing_id, level) VALUES (?, ?, 1); COMMIT; ``` --- **문서 끝**