happybell80 88a15e3809 DB 스키마 문서: emotion_readings 테이블 추가
- robeing_metrics DB의 emotion_readings 테이블 문서화
- Top-p 기반 복합 감정 저장 (top_emotions, cumulative_p)
- TimescaleDB 하이퍼테이블 인덱스 및 제약 조건
- 예시 데이터 포함
- DB 분리 정보 (main_db vs robeing_metrics) 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 18:37:04 +09:00

377 lines
14 KiB
Markdown

# PostgreSQL 테이블 구조
## 작성일: 2025-08-20
## 최종 수정일: 2025-09-24
## 데이터베이스: main_db
---
## 1. 조직 관련 테이블
### company
- **용도**: 회사 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 회사 ID |
| name | VARCHAR(255) | NO | | 회사명 |
| url | VARCHAR(255) | YES | | 회사 URL |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
### team
- **용도**: 팀 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 팀 ID |
| company_id | UUID | NO | | 회사 ID (FK → company) |
| name | VARCHAR(32) | NO | | 팀 이름 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
### user
- **용도**: 시스템 사용자 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 사용자 ID |
| team_id | UUID | NO | | 팀 ID (FK → team) |
| email | VARCHAR(255) | NO | | 이메일 (UNIQUE) |
| name | VARCHAR(255) | YES | | 사용자명 |
| picture | VARCHAR(500) | YES | | 프로필 이미지 |
| oauth_provider | VARCHAR(50) | YES | | OAuth 제공자 |
| oauth_id | VARCHAR(255) | YES | | OAuth ID |
| is_active | BOOLEAN | YES | | 활성 상태 |
| last_login_at | TIMESTAMPTZ | YES | | 마지막 로그인 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
| username | VARCHAR(64) | YES | | 사용자명 |
### workspace_member
- **용도**: 워크스페이스 멤버십
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 멤버십 ID |
| user_id | UUID | NO | | 사용자 ID (FK → user) |
| role | user_role | NO | GUEST | 역할 (OWNER/MEMBER/GUEST) |
| is_active | BOOLEAN | YES | | 활성 상태 |
| joined_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 가입 시각 |
| updated_at | TIMESTAMP | YES | CURRENT_TIMESTAMP | 수정 시각 |
---
## 2. 제품 및 로빙 테이블
### product
- **용도**: 제품 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 제품 ID |
| name | VARCHAR(32) | NO | | 제품명 |
| app_id | VARCHAR(16) | YES | | 앱 ID |
| scope | JSON | YES | | 권한 범위 |
| description | VARCHAR(256) | YES | | 설명 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
### robeing
- **용도**: 로빙 엔티티 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 로빙 ID |
| product_id | UUID | NO | | 제품 ID (FK → product) |
| team_id | UUID | NO | | 팀 ID (FK → team) |
| name | VARCHAR(32) | YES | | 로빙 이름 |
| level | INTEGER | YES | 1 | 레벨 |
| experience | INTEGER | YES | 0 | 경험치 |
| memory | INTEGER | YES | 10 | 메모리 스탯 |
| compute | INTEGER | YES | 10 | 연산 스탯 |
| react | INTEGER | YES | 10 | 반응 스탯 |
| empathy | INTEGER | YES | 10 | 공감 스탯 |
| leadership | INTEGER | YES | 10 | 리더십 스탯 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| robeing_container_id | VARCHAR(64) | YES | | 컨테이너 ID |
| robeing_container_url | VARCHAR(128) | YES | | 컨테이너 URL |
---
## 3. Slack 통합 테이블
### slack_workspace
- **용도**: Slack 워크스페이스 정보
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 워크스페이스 ID |
| team_id | UUID | NO | | 팀 ID (FK → team) |
| slack_team_id | VARCHAR(32) | YES | | Slack 팀 ID |
| bot_token | VARCHAR(255) | YES | | 봇 토큰 |
| is_enterprise_install | BOOLEAN | YES | | 엔터프라이즈 설치 |
| is_active | BOOLEAN | YES | | 활성 상태 |
| installed_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 설치 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
---
## 4. 사용자 설정 및 토큰 테이블
### user_preference
- **용도**: 사용자 개인 설정
- **Primary Key**: id (INTEGER)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | INTEGER | NO | | 설정 ID |
| user_id | UUID | NO | | 사용자 ID (FK → user) |
| slack_user_id | VARCHAR(32) | YES | | Slack 사용자 ID |
| news_keywords | VARCHAR(128)[] | YES | | 뉴스 키워드 |
| email_filter | VARCHAR(128)[] | YES | | 이메일 필터 |
| briefing_enabled | BOOLEAN | YES | true | 브리핑 활성화 |
| briefing_time | TIME | YES | 09:00:00 | 브리핑 시간 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
### gmail_token
- **용도**: Gmail OAuth 토큰
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 토큰 ID |
| user_id | UUID | NO | | 사용자 ID (FK → user) |
| token_data | JSONB | YES | | 토큰 데이터 |
| oauth_config | JSONB | YES | | OAuth 설정 |
| scopes | JSONB | YES | | 권한 범위 |
| metadata | JSONB | YES | | 메타데이터 |
| expiry | TIMESTAMP | YES | | 만료 시각 |
| is_equipped | BOOLEAN | YES | false | 장착 상태 |
| equipped_to | VARCHAR(50) | YES | | 장착 대상 |
| token_type | VARCHAR | YES | Bearer | 토큰 타입 |
| expires_at | DOUBLE PRECISION | YES | | 만료 시간 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
---
## 5. 로그 및 데이터 테이블
### conversation_log
- **용도**: 대화 기록
- **Primary Key**: id (INTEGER)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | INTEGER | NO | | 로그 ID |
| user_id | UUID | NO | | 사용자 ID (FK → user) |
| channel_id | VARCHAR(16) | YES | | 채널 ID |
| message | VARCHAR | YES | | 사용자 메시지 |
| response | VARCHAR | YES | | 응답 메시지 |
| intent | VARCHAR(256) | YES | | 의도 분석 |
| confidence | DOUBLE PRECISION | YES | | 신뢰도 |
| thread_ts | VARCHAR(128) | YES | | 스레드 타임스탬프 |
| timestamp | TIMESTAMPTZ | YES | | 대화 시각 |
| channel_type | VARCHAR(32) | YES | | 채널 타입 |
### news
- **용도**: 뉴스 데이터
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | | 뉴스 ID |
| user_id | UUID | YES | | 사용자 ID (FK → user) |
| data | JSONB | YES | | 뉴스 데이터 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
### rb_news
- **용도**: 뉴스 발행 관리
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | gen_random_uuid() | 뉴스 ID |
| title | TEXT | NO | | 뉴스 제목 |
| url | TEXT | NO | | 뉴스 URL (UNIQUE) |
| summary | TEXT | YES | | 뉴스 요약 |
| slack_message_ts | VARCHAR(100) | YES | | Slack 메시지 타임스탬프 |
| slack_channel_id | VARCHAR(100) | YES | | Slack 채널 ID |
| is_published | BOOLEAN | YES | false | 발행 상태 |
| published_at | TIMESTAMPTZ | YES | | 발행 시각 |
| created_at | TIMESTAMPTZ | YES | now() | 생성 시각 |
**인덱스**:
- `rb_news_pkey`: PRIMARY KEY (id)
- `rb_news_url_key`: UNIQUE (url)
- `idx_rb_news_url`: btree (url)
- `idx_rb_news_slack_message_ts`: btree (slack_message_ts)
- `idx_rb_news_created_at`: btree (created_at DESC)
### team_document
- **용도**: 팀 문서 RAG 시스템
- **Primary Key**: id (UUID)
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | gen_random_uuid() | 문서 ID |
| team_id | UUID | NO | | 팀 ID (FK → team) |
| filename | VARCHAR(255) | NO | | 파일명 |
| file_hash | VARCHAR(64) | NO | | SHA256 해시 |
| file_size | BIGINT | YES | | 파일 크기 (bytes) |
| mime_type | VARCHAR(100) | YES | | MIME 타입 |
| storage_path | TEXT | NO | | 저장 경로 (/mnt/hdd/uploads/{team_id}/) |
| text_content | TEXT | YES | | 추출된 텍스트 |
| chunk_count | INTEGER | YES | 0 | ChromaDB 청크 수 |
| processing_status | VARCHAR(20) | YES | 'pending' | 처리 상태 (pending/completed/failed) |
| metadata | JSONB | YES | '{}' | 메타데이터 (uploaded_by, tags, summary 등) |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
**인덱스**:
- `idx_team_doc_hash`: UNIQUE (team_id, file_hash) - 팀별 파일 중복 방지
---
### naverworks_token
- **용도**: NAVER WORKS OAuth 토큰 및 Passport 정보
- **Primary Key**: id (UUID)
- **Unique**: user_id
- **Foreign Key**: user_id → user(id) ON DELETE CASCADE
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| id | UUID | NO | gen_random_uuid() | 토큰 ID |
| user_id | UUID | NO | | 사용자 ID (FK → user) |
| access_token | TEXT | YES | | 액세스 토큰 (개별 저장용) |
| refresh_token | TEXT | YES | | 리프레시 토큰 (개별 저장용) |
| token_type | VARCHAR(50) | YES | 'Bearer' | 토큰 타입 |
| expires_at | TIMESTAMP | YES | | 토큰 만료 시각 |
| scopes | JSONB | YES | | OAuth 스코프 목록 |
| domain_id | VARCHAR(255) | YES | | NAVER WORKS 도메인 ID |
| service_account | VARCHAR(255) | YES | | 서비스 계정 |
| metadata | JSONB | YES | | 사용자 메타데이터 |
| created_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 생성 시각 |
| updated_at | TIMESTAMPTZ | YES | CURRENT_TIMESTAMP | 수정 시각 |
| username | VARCHAR(255) | YES | | 사용자명 |
| account_id | VARCHAR(255) | YES | | NAVER WORKS 계정 ID |
| token_data | JSONB | YES | | 토큰 데이터 (통합 저장용) |
| oauth_config | JSONB | YES | | OAuth 설정 정보 |
| is_equipped | BOOLEAN | YES | false | Passport 장착 여부 |
**인덱스**:
- idx_naverworks_token_user_id (user_id)
- idx_naverworks_token_expires_at (expires_at)
**트리거**:
- update_naverworks_token_updated_at: updated_at 자동 갱신
## 6. 기타 정보
### Custom Types
- **user_role**: ENUM (OWNER, MEMBER, GUEST)
### Functions
- **update_column_updated_at**: 모든 테이블의 updated_at 자동 갱신 트리거
### 외래키 관계
| 테이블 | 컬럼 | 참조 테이블 | 참조 컬럼 |
|--------|------|------------|----------|
| team | company_id | company | id |
| user | team_id | team | id |
| robeing | product_id | product | id |
| robeing | team_id | team | id |
| slack_workspace | team_id | team | id |
| workspace_member | user_id | user | id |
| conversation_log | user_id | user | id |
| gmail_token | user_id | user | id |
| news | user_id | user | id |
| user_preference | user_id | user | id |
| team_document | team_id | team | id |
---
---
## 7. 감정 분석 테이블 (robeing_metrics DB)
### emotion_readings
- **용도**: 사용자 및 로빙 감정 분석 시계열 데이터
- **데이터베이스**: robeing_metrics (TimescaleDB)
- **특징**: 시계열 하이퍼테이블, Top-p 기반 복합 감정 저장
| 컬럼명 | 타입 | NULL | 기본값 | 설명 |
|--------|------|------|--------|------|
| created_at | TIMESTAMPTZ | NO | | 분석 시각 (파티션 키) |
| user_id | UUID | YES | | 사용자 ID (user 테이블 참조) |
| company_id | UUID | YES | | 회사 ID |
| robeing_id | VARCHAR(50) | YES | | 로빙 ID |
| emotion_type | VARCHAR(20) | YES | | 'user' 또는 'robeing' |
| probs | JSONB | NO | | 7개 감정 확률 분포 |
| entropy | DOUBLE PRECISION | YES | | 감정 엔트로피 값 |
| model_version | VARCHAR(50) | YES | | 모델 버전 (예: onnx-7emotions-v1) |
| meta | JSONB | YES | | 메타데이터 (source, message_length 등) |
| text_hash | VARCHAR(64) | YES | | 텍스트 SHA256 해시 |
| top_emotions | JSONB | YES | | Top-p 복합 감정 배열 [{"label", "probability"}] |
| cumulative_p | DOUBLE PRECISION | YES | | 누적 확률 (Top-p 70%) |
**인덱스**:
- `emotion_readings_created_at_idx`: btree (created_at DESC)
- `idx_emotion_user_time`: btree (user_id, created_at DESC)
- `idx_emotion_company_time`: btree (company_id, created_at DESC)
- `idx_emotion_robeing_time`: btree (robeing_id, created_at DESC)
- `idx_emotion_type`: btree (emotion_type, created_at DESC)
**제약 조건**:
- `emotion_readings_emotion_type_check`: emotion_type IN ('user', 'robeing')
**트리거**:
- `ts_insert_blocker`: TimescaleDB 하이퍼테이블 삽입 제어
**예시 데이터**:
```json
{
"top_emotions": [
{"label": "fear", "probability": 0.499},
{"label": "neutral", "probability": 0.335}
],
"cumulative_p": 0.833,
"probs": {
"fear": 0.499, "neutral": 0.335, "anger": 0.056,
"sadness": 0.048, "disgust": 0.032, "surprise": 0.018, "happiness": 0.012
},
"entropy": 1.234
}
```
---
## 주의사항
### 데이터베이스 정보
- **main_db**: 주요 서비스 데이터 (PostgreSQL)
- 소유자: robeings
- 테이블: user, team, robeing, conversation_log 등
- **robeing_metrics**: 시계열 메트릭 데이터 (TimescaleDB)
- 테이블: emotion_readings
- 특징: 시간 기반 파티셔닝, 자동 압축, retention 정책
- **타임스탬프**: TIMESTAMPTZ 사용 (WITH TIME ZONE)
- **자동 갱신**: update_column_updated_at 트리거로 updated_at 자동 관리
- **UUID 기반**: 주요 테이블 모두 UUID 사용
---
**문서 끝**