299 lines
11 KiB
Markdown
299 lines
11 KiB
Markdown
# 적자 탈출 가계부 - 데이터베이스 스키마 구현
|
|
|
|
## 1. 개요
|
|
|
|
적자 탈출 가계부 애플리케이션의 데이터베이스 스키마 구현에 대한 문서입니다. 이 문서는 Supabase 자체 호스팅 환경에서 PostgreSQL 데이터베이스를 사용하여 구현된 데이터베이스 스키마를 설명합니다.
|
|
|
|
## 2. 마이그레이션 파일 구조
|
|
|
|
데이터베이스 스키마는 다음과 같은 마이그레이션 파일로 구성되어 있습니다:
|
|
|
|
1. `01_users.sql`: 사용자 정보 관리
|
|
2. `02_categories.sql`: 지출 카테고리 관리
|
|
3. `03_expenses.sql`: 지출 내역 관리
|
|
4. `04_budgets.sql`: 예산 관리
|
|
5. `05_cards.sql`: 카드 정보 관리
|
|
6. `06_limits.sql`: 지출 한도 관리
|
|
7. `07_templates.sql`: 템플릿 관리
|
|
8. `08_notifications.sql`: 알림 시스템
|
|
9. `09_analysis_settings.sql`: 분석 설정
|
|
|
|
각 마이그레이션 파일은 테이블 생성, 인덱스 생성, RLS(Row Level Security) 정책 설정, 관련 함수 및 트리거를 포함합니다.
|
|
|
|
## 3. 테이블 구조
|
|
|
|
### 3.1 사용자(users) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password VARCHAR(255) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자 정보 저장
|
|
- **RLS 정책**: 사용자는 자신의 정보만 조회/수정 가능
|
|
- **관련 트리거**: Supabase Auth와 연동하여 사용자 생성 시 프로필 자동 생성
|
|
|
|
### 3.2 카테고리(categories) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS categories (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
name VARCHAR(50) NOT NULL,
|
|
icon VARCHAR(50) NOT NULL,
|
|
color VARCHAR(20) NOT NULL,
|
|
parent_id UUID REFERENCES categories(id),
|
|
is_income BOOLEAN DEFAULT FALSE,
|
|
is_default BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 지출/수입 카테고리 관리
|
|
- **RLS 정책**: 모든 사용자가 조회 가능, 수정은 관리자만 가능
|
|
- **기본 데이터**: 40개 이상의 기본 카테고리 제공 (지출 및 수입 카테고리)
|
|
- **계층 구조**: 부모-자식 관계를 통한 카테고리 계층화
|
|
|
|
### 3.3 지출(expenses) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS expenses (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
amount NUMERIC(12,2) NOT NULL,
|
|
date DATE NOT NULL,
|
|
category_id UUID REFERENCES categories(id),
|
|
memo TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자의 지출/수입 내역 저장
|
|
- **RLS 정책**: 사용자는 자신의 지출 데이터만 접근 가능
|
|
- **관련 함수**: 월별 지출 합계 조회 함수 제공
|
|
|
|
### 3.4 예산(budgets) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS budgets (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
amount NUMERIC(12,2) NOT NULL,
|
|
month INTEGER NOT NULL CHECK (month BETWEEN 1 AND 12),
|
|
year INTEGER NOT NULL,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
UNIQUE(user_id, month, year)
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자의 월별 예산 관리
|
|
- **RLS 정책**: 사용자는 자신의 예산 데이터만 접근 가능
|
|
- **관련 함수**: 예산 대비 지출 비율 조회 함수 제공
|
|
|
|
### 3.5 카드(cards) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS cards (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
card_type VARCHAR(20) NOT NULL CHECK (card_type IN ('credit', 'debit', 'prepaid')),
|
|
card_number_last4 VARCHAR(4),
|
|
issuer VARCHAR(50),
|
|
color VARCHAR(20),
|
|
payment_day INTEGER CHECK (payment_day BETWEEN 1 AND 31),
|
|
monthly_limit NUMERIC(12,2),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자의 카드 정보 관리
|
|
- **RLS 정책**: 사용자는 자신의 카드 데이터만 접근 가능
|
|
- **관련 함수**: 사용자별 카드 목록 조회 함수 제공
|
|
|
|
### 3.6 한도(limits) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS limits (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
card_id UUID REFERENCES cards(id) ON DELETE CASCADE,
|
|
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
|
amount NUMERIC(12,2) NOT NULL CHECK (amount > 0),
|
|
period VARCHAR(20) NOT NULL CHECK (period IN ('daily', 'weekly', 'monthly')),
|
|
start_date DATE NOT NULL,
|
|
end_date DATE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
CONSTRAINT card_or_category_required CHECK (card_id IS NOT NULL OR category_id IS NOT NULL),
|
|
CONSTRAINT valid_date_range CHECK (end_date IS NULL OR end_date >= start_date)
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 카드 또는 카테고리별 지출 한도 관리
|
|
- **RLS 정책**: 사용자는 자신의 한도 데이터만 접근 가능
|
|
- **관련 함수**: 활성화된 한도 조회 및 사용량 계산 함수 제공
|
|
|
|
### 3.7 템플릿(templates) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS templates (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
card_id UUID REFERENCES cards(id) ON DELETE SET NULL,
|
|
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
|
amount NUMERIC(12,2) NOT NULL CHECK (amount > 0),
|
|
description TEXT,
|
|
location VARCHAR(255),
|
|
is_income BOOLEAN DEFAULT FALSE,
|
|
is_favorite BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
UNIQUE(user_id, name)
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 자주 사용하는 지출/수입 템플릿 관리
|
|
- **RLS 정책**: 사용자는 자신의 템플릿 데이터만 접근 가능
|
|
- **관련 함수**: 템플릿으로 지출 생성 함수 제공
|
|
|
|
### 3.8 알림(notifications) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS notifications (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title VARCHAR(100) NOT NULL,
|
|
message TEXT NOT NULL,
|
|
type VARCHAR(20) NOT NULL CHECK (type IN ('limit_warning', 'payment_due', 'tip', 'system')),
|
|
related_id UUID,
|
|
related_type VARCHAR(50),
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
read_at TIMESTAMP WITH TIME ZONE
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자 알림 관리 (한도 경고, 결제일 알림 등)
|
|
- **RLS 정책**: 사용자는 자신의 알림 데이터만 접근 가능
|
|
- **관련 트리거**: 지출 한도 초과 시 자동 알림 생성
|
|
|
|
### 3.9 분석 설정(analysis_settings) 테이블
|
|
|
|
```sql
|
|
CREATE TABLE IF NOT EXISTS analysis_settings (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
budget_period VARCHAR(20) DEFAULT 'monthly' CHECK (budget_period IN ('daily', 'weekly', 'monthly')),
|
|
saving_goal NUMERIC(12,2),
|
|
analysis_period INTEGER DEFAULT 3 CHECK (analysis_period > 0),
|
|
notification_frequency VARCHAR(20) DEFAULT 'weekly' CHECK (notification_frequency IN ('daily', 'weekly', 'monthly', 'none')),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
|
UNIQUE(user_id)
|
|
);
|
|
```
|
|
|
|
- **주요 기능**: 사용자별 분석 설정 관리
|
|
- **RLS 정책**: 사용자는 자신의 분석 설정 데이터만 접근 가능
|
|
- **관련 트리거**: 사용자 생성 시 기본 분석 설정 자동 생성
|
|
- **관련 함수**: 지출 분석 및 추세 분석 함수 제공
|
|
|
|
## 4. Row Level Security(RLS) 정책
|
|
|
|
모든 테이블에는 Row Level Security 정책이 적용되어 있습니다. 이를 통해 사용자는 자신의 데이터만 접근할 수 있으며, 다른 사용자의 데이터에는 접근할 수 없습니다.
|
|
|
|
기본적인 RLS 정책 패턴:
|
|
|
|
```sql
|
|
-- 테이블에 RLS 활성화
|
|
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- 조회 정책
|
|
CREATE POLICY "select_policy" ON table_name
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
|
|
-- 삽입 정책
|
|
CREATE POLICY "insert_policy" ON table_name
|
|
FOR INSERT WITH CHECK (auth.uid() = user_id);
|
|
|
|
-- 수정 정책
|
|
CREATE POLICY "update_policy" ON table_name
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
|
|
-- 삭제 정책
|
|
CREATE POLICY "delete_policy" ON table_name
|
|
FOR DELETE USING (auth.uid() = user_id);
|
|
```
|
|
|
|
## 5. 인덱스
|
|
|
|
성능 최적화를 위해 각 테이블에 적절한 인덱스가 생성되어 있습니다:
|
|
|
|
- 외래 키 컬럼에 대한 인덱스
|
|
- 자주 조회되는 컬럼에 대한 인덱스
|
|
- 복합 인덱스 (예: `user_id`와 `date` 조합)
|
|
|
|
## 6. 함수 및 트리거
|
|
|
|
다양한 비즈니스 로직을 지원하기 위해 여러 함수와 트리거가 구현되어 있습니다:
|
|
|
|
### 6.1 주요 함수
|
|
|
|
- `get_monthly_expenses`: 월별 지출 합계 조회
|
|
- `get_budget_usage`: 예산 대비 지출 비율 조회
|
|
- `get_user_cards`: 사용자별 카드 목록 조회
|
|
- `get_active_limits`: 활성화된 한도 및 사용량 조회
|
|
- `create_expense_from_template`: 템플릿으로 지출 생성
|
|
- `mark_notification_as_read`: 알림 읽음 표시
|
|
- `get_expense_analysis`: 카테고리별 지출 분석
|
|
- `get_monthly_expense_trend`: 월별 지출 추세 분석
|
|
|
|
### 6.2 주요 트리거
|
|
|
|
- `update_updated_at_column`: 레코드 업데이트 시 `updated_at` 자동 갱신
|
|
- `create_user_profile`: Supabase Auth 사용자 생성 시 프로필 자동 생성
|
|
- `create_limit_warning_notification`: 지출 한도 초과 시 알림 자동 생성
|
|
- `create_default_analysis_settings`: 사용자 생성 시 기본 분석 설정 자동 생성
|
|
|
|
## 7. 마이그레이션 실행 방법
|
|
|
|
마이그레이션 파일을 실행하기 위한 스크립트가 제공됩니다:
|
|
|
|
```bash
|
|
# 실행 권한 부여
|
|
chmod +x run_migrations.sh
|
|
|
|
# 스크립트 실행
|
|
./run_migrations.sh
|
|
```
|
|
|
|
환경 변수를 통해 데이터베이스 연결 정보를 설정할 수 있습니다:
|
|
|
|
```bash
|
|
DB_HOST=localhost DB_PORT=5432 DB_NAME=postgres DB_USER=postgres DB_PASSWORD=postgres ./run_migrations.sh
|
|
```
|
|
|
|
## 8. 향후 확장 계획
|
|
|
|
1. **데이터 동기화**: 오프라인 모드 지원을 위한 동기화 메커니즘 구현
|
|
2. **데이터 분석**: 더 다양한 분석 함수 및 뷰 추가
|
|
3. **데이터 마이그레이션**: 버전 관리 및 롤백 메커니즘 개선
|
|
4. **성능 최적화**: 대규모 데이터셋에 대한 쿼리 성능 최적화
|
|
|
|
## 9. 참고 사항
|
|
|
|
- 모든 날짜/시간 데이터는 타임존 정보를 포함합니다 (`TIMESTAMP WITH TIME ZONE`).
|
|
- 금액 데이터는 `NUMERIC(12,2)` 타입을 사용하여 정확한 소수점 계산을 보장합니다.
|
|
- UUID를 기본 키로 사용하여 분산 환경에서의 확장성을 고려했습니다.
|
|
- 모든 테이블에는 생성 시간(`created_at`)이 자동으로 기록됩니다.
|