From fe9f80d02d3ac6adfb2a1043c6cf459659bbb1f4 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Wed, 20 Aug 2025 01:01:29 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20PostgreSQL=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 300_architecture/database 폴더 생성 - tables.md: 모든 테이블 구조 상세 정의 - relationships.md: 테이블 관계도 및 JOIN 예시 - README.md: 데이터베이스 문서 구성 안내 주요 내용: - 12개 테이블 구조 정리 - user_id UUID 정규화 현황 - 외래키 관계 정의 - 데이터 흐름 다이어그램 - JOIN 쿼리 예시 --- 300_architecture/database/README.md | 30 +++ 300_architecture/database/relationships.md | 239 +++++++++++++++++++++ 300_architecture/database/tables.md | 237 ++++++++++++++++++++ 3 files changed, 506 insertions(+) create mode 100644 300_architecture/database/README.md create mode 100644 300_architecture/database/relationships.md create mode 100644 300_architecture/database/tables.md diff --git a/300_architecture/database/README.md b/300_architecture/database/README.md new file mode 100644 index 0000000..a569208 --- /dev/null +++ b/300_architecture/database/README.md @@ -0,0 +1,30 @@ +# Database 구조 문서 + +## 개요 +로빙 프로젝트의 PostgreSQL 데이터베이스 구조 및 관계를 정리한 문서입니다. + +## 문서 구성 + +### 1. 테이블 정의 +- `tables.md` - 모든 테이블 구조 정의 +- `relationships.md` - 테이블 간 관계 (FK, JOIN) + +### 2. 도메인별 정리 +- `auth_domain.md` - 인증/사용자 관련 테이블 +- `gmail_domain.md` - Gmail/이메일 관련 테이블 +- `robeing_domain.md` - 로빙 서비스 관련 테이블 +- `slack_domain.md` - Slack 통합 관련 테이블 + +### 3. 데이터 타입 표준 +- `data_types.md` - 사용되는 데이터 타입 정의 +- `naming_conventions.md` - 명명 규칙 + +## 데이터베이스 정보 +- **Host**: 192.168.219.45 (51123 서버) +- **Port**: 5432 +- **Database**: auth_db +- **User**: robeings +- **Password**: robeings + +## 최종 업데이트 +2025-08-20 \ No newline at end of file diff --git a/300_architecture/database/relationships.md b/300_architecture/database/relationships.md new file mode 100644 index 0000000..b3d94c6 --- /dev/null +++ b/300_architecture/database/relationships.md @@ -0,0 +1,239 @@ +# 테이블 관계도 (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; +``` + +--- + +**문서 끝** \ No newline at end of file diff --git a/300_architecture/database/tables.md b/300_architecture/database/tables.md new file mode 100644 index 0000000..44903e2 --- /dev/null +++ b/300_architecture/database/tables.md @@ -0,0 +1,237 @@ +# PostgreSQL 테이블 구조 + +## 작성일: 2025-08-20 +## 데이터베이스: auth_db + +--- + +## 1. 사용자 관련 테이블 + +### users +- **용도**: 시스템 전체 사용자 정보 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 사용자 고유 식별자 | +| email | VARCHAR(255) | NO | | 이메일 주소 (UNIQUE) | +| name | VARCHAR(255) | YES | | 사용자 실명 | +| username | VARCHAR(50) | YES | | 로그인 ID (UNIQUE) | +| picture | VARCHAR(500) | YES | | 프로필 이미지 URL | +| oauth_provider | VARCHAR(50) | YES | | OAuth 제공자 (google 등) | +| oauth_id | VARCHAR(255) | YES | | OAuth 고유 ID | +| is_active | BOOLEAN | YES | true | 활성 상태 | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | +| last_login_at | TIMESTAMP | YES | | 마지막 로그인 | + +--- + +## 2. Workspace 관련 테이블 + +### workspaces +- **용도**: 워크스페이스 정보 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 워크스페이스 ID | +| name | VARCHAR(255) | NO | | 워크스페이스 이름 | +| company_id | UUID | YES | | 회사 ID (FK → companies) | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +### workspace_members +- **용도**: 워크스페이스 멤버 관계 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 멤버십 ID | +| workspace_id | UUID | NO | | 워크스페이스 ID (FK → workspaces) | +| user_id | UUID | NO | | 사용자 ID (FK → users) | +| role | ENUM | YES | member | 역할 (admin/member) | +| robing_id | VARCHAR(50) | YES | | 할당된 로빙 ID | +| robing_url | VARCHAR(255) | YES | | 로빙 서비스 URL | +| is_active | BOOLEAN | YES | true | 활성 상태 | +| joined_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 가입 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +### companies +- **용도**: 회사 정보 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 회사 ID | +| name | VARCHAR(255) | NO | | 회사명 | +| domain | VARCHAR(255) | YES | | 도메인 | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +--- + +## 3. Slack 통합 테이블 + +### slack_workspaces +- **용도**: Slack 워크스페이스 정보 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 워크스페이스 ID | +| team_id | VARCHAR(50) | NO | | Slack Team ID (UNIQUE) | +| team_name | VARCHAR(255) | YES | | 팀 이름 | +| bot_token | TEXT | YES | | Bot User OAuth Token | +| bot_user_id | VARCHAR(50) | YES | | Bot User ID | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +### slack_user_mapping +- **용도**: Slack 사용자와 시스템 사용자 매핑 +- **Primary Key**: id (UUID) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | UUID | NO | | 매핑 ID | +| slack_user_id | VARCHAR(50) | NO | | Slack User ID (U로 시작) | +| slack_workspace_id | UUID | YES | | Slack 워크스페이스 ID (FK → slack_workspaces) | +| user_id | UUID | NO | | 시스템 사용자 ID (FK → users) | +| workspace_member_id | UUID | YES | | 워크스페이스 멤버 ID (FK → workspace_members) | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +--- + +## 4. Gmail 관련 테이블 + +### gmail_tokens +- **용도**: Gmail OAuth 토큰 저장 +- **Primary Key**: id (SERIAL) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | SERIAL | NO | | 토큰 ID | +| user_id | UUID | NO | | 사용자 ID (FK → users) | +| robeing_id | VARCHAR(50) | YES | | 로빙 ID | +| token_data | JSONB | NO | | access_token, refresh_token | +| oauth_config | JSONB | YES | | client_id, client_secret, token_uri | +| scopes | JSONB | YES | | Gmail API 권한 목록 | +| metadata | JSONB | YES | | email, imported_at 등 | +| expiry | TIMESTAMP | YES | | 토큰 만료 시각 | +| is_equipped | BOOLEAN | YES | false | 장착 상태 | +| equipped_to | VARCHAR(50) | YES | | 장착된 로빙 ID | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +### gmail_audit_logs +- **용도**: Gmail 아이템 작업 감사 로그 +- **Primary Key**: id (SERIAL) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | SERIAL | NO | | 로그 ID | +| user_id | VARCHAR(100) | YES | | 사용자 ID (⚠️ VARCHAR - 수정 필요) | +| robeing_id | VARCHAR(50) | YES | | 로빙 ID | +| action | VARCHAR(50) | YES | | 작업 유형 (equip/unequip/reauth) | +| success | BOOLEAN | YES | | 성공 여부 | +| details | JSONB | YES | | 상세 정보 | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | + +--- + +## 5. 로빙 관련 테이블 + +### robeing_stats +- **용도**: 로빙 통계 및 레벨 정보 +- **Primary Key**: id (SERIAL) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | SERIAL | NO | | 통계 ID | +| user_id | UUID | YES | | 사용자 ID (FK → users) | +| robeing_id | VARCHAR(50) | YES | | 로빙 ID | +| level | INTEGER | YES | 1 | 현재 레벨 | +| experience | INTEGER | YES | 0 | 경험치 | +| email_sent_count | INTEGER | YES | 0 | 이메일 발송 횟수 | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +### robing_stats (구버전) +- **용도**: 구버전 로빙 통계 (사용 중단 예정) +- **설명**: robeing_stats로 마이그레이션 필요 + +### robing_settings +- **용도**: 로빙 설정 정보 +- **Primary Key**: id (SERIAL) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | SERIAL | NO | | 설정 ID | +| robing_id | VARCHAR(50) | NO | | 로빙 ID (UNIQUE) | +| settings | JSONB | YES | | 설정 JSON | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | +| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 | + +--- + +## 6. 대화 로그 테이블 + +### conversation_logs +- **용도**: 대화 기록 저장 +- **Primary Key**: id (SERIAL) + +| 컬럼명 | 타입 | NULL | 기본값 | 설명 | +|--------|------|------|--------|------| +| id | SERIAL | NO | | 로그 ID | +| user_id | UUID | YES | | 사용자 ID (FK → users) | +| robeing_id | VARCHAR(50) | YES | | 로빙 ID | +| channel | VARCHAR(100) | YES | | 채널 (slack/web/api) | +| message_type | VARCHAR(50) | YES | | 메시지 유형 | +| user_message | TEXT | YES | | 사용자 메시지 | +| bot_response | TEXT | YES | | 봇 응답 | +| metadata | JSONB | YES | | 추가 메타데이터 | +| created_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 생성 시각 | + +--- + +## 인덱스 정보 + +### 주요 인덱스 +- users: email (UNIQUE), username (UNIQUE) +- gmail_tokens: user_id, robeing_id, oauth_config (GIN), token_data (GIN) +- slack_user_mapping: slack_user_id, user_id +- workspace_members: workspace_id, user_id +- conversation_logs: user_id, robeing_id, created_at + +--- + +## 외래키 제약 + +| 테이블 | 컬럼 | 참조 테이블 | 참조 컬럼 | +|--------|------|------------|----------| +| workspace_members | user_id | users | id | +| workspace_members | workspace_id | workspaces | id | +| slack_user_mapping | user_id | users | id | +| slack_user_mapping | slack_workspace_id | slack_workspaces | id | +| gmail_tokens | user_id | users | id | +| robeing_stats | user_id | users | id | +| conversation_logs | user_id | users | id | + +--- + +## 주의사항 + +### 데이터 타입 일관성 +- **user_id**: 모든 테이블에서 UUID 타입 사용 (gmail_audit_logs 제외) +- **robeing_id**: VARCHAR(50) 통일 +- **timestamp**: TIMESTAMP WITHOUT TIME ZONE 사용 + +### 개선 필요 사항 +1. gmail_audit_logs.user_id를 UUID로 변경 필요 (현재 VARCHAR) +2. robing_stats 테이블을 robeing_stats로 통합 필요 +3. 일부 테이블의 소유자가 postgres로 되어있어 권한 조정 필요 + +--- + +**문서 끝** \ No newline at end of file