- 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
239 lines
5.2 KiB
Markdown
239 lines
5.2 KiB
Markdown
# 테이블 관계도 (ERD)
|
|
|
|
## 작성일: 2025-08-20
|
|
|
|
---
|
|
|
|
## 관계 다이어그램
|
|
|
|
```mermaid
|
|
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. 사용자 전체 정보 조회
|
|
```sql
|
|
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로 사용자 찾기
|
|
```sql
|
|
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 아이템 장착 상태 확인
|
|
```sql
|
|
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. 워크스페이스 멤버 목록
|
|
```sql
|
|
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 아이템 장착
|
|
```sql
|
|
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. 사용자 생성
|
|
```sql
|
|
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;
|
|
```
|
|
|
|
---
|
|
|
|
**문서 끝** |