DOCS/300_architecture/database/relationships.md
happybell80 97e0888ce0 Fix more incorrect table names in documentation
- users → user in SQL contexts (94 occurrences)
- robeings → robeing in SQL contexts
- user_preferences → user_preference (14 files)
- slack_workspaces → slack_workspace in SQL contexts (17 files)

All table names now correctly match PostgreSQL schema
2025-09-26 00:52:15 +09:00

5.2 KiB

테이블 관계도 (ERD)

작성일: 2025-08-20


관계 다이어그램

erDiagram
    users ||--o{ workspace_member : "has many"
    users ||--o{ slack_user_mapping : "has many"
    users ||--o{ gmail_token : "has"
    users ||--o{ robeing_stats : "has"
    users ||--o{ conversation_log : "has many"
    
    workspaces ||--o{ workspace_member : "has many"
    workspaces }o--|| company : "belongs to"
    
    slack_workspaces ||--o{ slack_user_mapping : "has many"
    
    workspace_member ||--o{ slack_user_mapping : "references"
    
    gmail_token }o--|| users : "belongs to"
    robeing_stats }o--|| users : "belongs to"
    conversation_log }o--|| users : "belongs to"

핵심 관계 설명

1. User 중심 관계

users (1) ─────┬──── (*) workspace_member
               ├──── (*) slack_user_mapping  
               ├──── (0..1) gmail_token
               ├──── (0..1) robeing_stats
               └──── (*) conversation_log
  • 한 사용자는 여러 워크스페이스의 멤버가 될 수 있음
  • 한 사용자는 여러 Slack 워크스페이스와 매핑될 수 있음
  • 한 사용자는 하나의 Gmail 토큰만 가질 수 있음 (현재 구조)
  • 한 사용자는 하나의 로빙 통계를 가짐
  • 한 사용자는 여러 대화 로그를 가짐

2. Workspace 관계

company (1) ──── (*) workspaces
workspaces (1) ──── (*) workspace_member
  • 한 회사는 여러 워크스페이스를 가질 수 있음
  • 한 워크스페이스는 여러 멤버를 가질 수 있음

3. Slack 매핑 관계

slack_workspaces (1) ──── (*) slack_user_mapping
slack_user_mapping (*) ──── (1) users
slack_user_mapping (*) ──── (0..1) workspace_member
  • Slack 사용자 ID를 시스템 UUID로 변환하는 브릿지 역할
  • workspace_member와 선택적 연결

JOIN 예시

1. 사용자 전체 정보 조회

SELECT 
    u.id,
    u.username,
    u.name,
    u.email,
    wm.robeing_id,
    sum.slack_user_id,
    gt.is_equipped as gmail_equipped
FROM user u
LEFT JOIN workspace_member wm ON u.id = wm.user_id
LEFT JOIN slack_user_mapping sum ON u.id = sum.user_id
LEFT JOIN gmail_token gt ON u.id = gt.user_id
WHERE u.username = 'happybell80';

2. Slack ID로 사용자 찾기

SELECT 
    sum.slack_user_id,
    u.id as user_uuid,
    u.username,
    u.name,
    u.email
FROM slack_user_mapping sum
JOIN user u ON sum.user_id = u.id
WHERE sum.slack_user_id = 'U0925SXQFDK';

3. Gmail 아이템 장착 상태 확인

SELECT 
    u.name,
    u.email,
    gt.is_equipped,
    gt.equipped_to,
    rs.level,
    gt.scopes
FROM user u
JOIN gmail_token gt ON u.id = gt.user_id
LEFT JOIN robeing_stats rs ON u.id = rs.user_id
WHERE gt.is_equipped = true;

4. 워크스페이스 멤버 목록

SELECT 
    w.name as workspace_name,
    u.name as user_name,
    u.email,
    wm.role,
    wm.robeing_id
FROM workspaces w
JOIN workspace_member wm ON w.id = wm.workspace_id
JOIN user 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_token 조회
    ↓
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 팀 방지
  • robeing_settings.robeing_id - 중복 설정 방지

3. NULL 허용 정책

  • 필수 관계: NOT NULL (user_id, workspace_id 등)
  • 선택적 관계: NULL 허용 (robeing_id, equipped_to 등)

트랜잭션 고려사항

1. Gmail 아이템 장착

BEGIN;
-- 1. 레벨 확인
SELECT level FROM robeing_stats WHERE user_id = ?;

-- 2. 소유권 확인
SELECT id FROM gmail_token WHERE user_id = ?;

-- 3. 장착 처리
UPDATE gmail_token 
SET is_equipped = true, equipped_to = ?
WHERE user_id = ?;

-- 4. 감사 로그
INSERT INTO gmail_audit_logs (user_id, action, success)
VALUES (?, 'equip', true);

COMMIT;

2. 사용자 생성

BEGIN;
-- 1. users 테이블
INSERT INTO user (id, email, name, username) 
VALUES (?, ?, ?, ?);

-- 2. workspace_member 추가
INSERT INTO workspace_member (user_id, workspace_id, role)
VALUES (?, ?, 'member');

-- 3. robeing_stats 초기화
INSERT INTO robeing_stats (user_id, robeing_id, level)
VALUES (?, ?, 1);

COMMIT;

문서 끝