Files
zellyy-finance/docs/02_기술_문서/데이터베이스_스키마_스크립트.sql
2025-03-21 16:08:43 +09:00

293 lines
12 KiB
PL/PgSQL

-- Zellyy Finance 데이터베이스 스키마 스크립트
-- 작성일: 2023년
-- UUID 확장 활성화
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 타임스탬프 함수 생성 (자동 업데이트용)
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 1. 사용자 테이블
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
full_name TEXT NOT NULL,
profile_image_url TEXT,
monthly_income DECIMAL(12, 2),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 사용자 테이블 업데이트 트리거
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 사용자 테이블 RLS 정책
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 정보만 볼 수 있음" ON users
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "사용자는 자신의 정보만 수정할 수 있음" ON users
FOR UPDATE USING (auth.uid() = id);
-- 2. 카테고리 테이블
CREATE TABLE categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT NOT NULL,
icon TEXT,
is_income BOOLEAN DEFAULT FALSE,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id, name)
);
-- 카테고리 테이블 업데이트 트리거
CREATE TRIGGER update_categories_updated_at
BEFORE UPDATE ON categories
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 카테고리 테이블 RLS 정책
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 카테고리와 기본 카테고리를 볼 수 있음" ON categories
FOR SELECT USING (auth.uid() = user_id OR is_default = TRUE);
CREATE POLICY "사용자는 자신의 카테고리만 수정할 수 있음" ON categories
FOR UPDATE USING (auth.uid() = user_id AND is_default = FALSE);
CREATE POLICY "사용자는 자신의 카테고리만 삭제할 수 있음" ON categories
FOR DELETE USING (auth.uid() = user_id AND is_default = FALSE);
CREATE POLICY "사용자는 자신의 카테고리만 생성할 수 있음" ON categories
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 3. 카드 테이블
CREATE TABLE cards (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
card_number TEXT,
card_type TEXT NOT NULL,
bank_name TEXT,
color TEXT,
billing_day INTEGER,
payment_day INTEGER,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id, name)
);
-- 카드 테이블 업데이트 트리거
CREATE TRIGGER update_cards_updated_at
BEFORE UPDATE ON cards
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 카드 테이블 RLS 정책
ALTER TABLE cards ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 카드만 볼 수 있음" ON cards
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 카드만 수정할 수 있음" ON cards
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 카드만 삭제할 수 있음" ON cards
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 카드만 생성할 수 있음" ON cards
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 4. 한도 테이블
CREATE TABLE 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 DECIMAL(12, 2) NOT NULL,
period TEXT NOT NULL, -- 'monthly', 'weekly', 'daily'
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)
);
-- 한도 테이블 업데이트 트리거
CREATE TRIGGER update_limits_updated_at
BEFORE UPDATE ON limits
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 한도 테이블 RLS 정책
ALTER TABLE limits ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 한도만 볼 수 있음" ON limits
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 한도만 수정할 수 있음" ON limits
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 한도만 삭제할 수 있음" ON limits
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 한도만 생성할 수 있음" ON limits
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 5. 지출 테이블
CREATE TABLE expenses (
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 SET NULL,
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
amount DECIMAL(12, 2) NOT NULL,
description TEXT,
date TIMESTAMP WITH TIME ZONE NOT NULL,
location TEXT,
receipt_image_url TEXT,
is_income BOOLEAN DEFAULT FALSE,
is_recurring BOOLEAN DEFAULT FALSE,
recurring_period TEXT, -- 'monthly', 'weekly', 'daily'
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 지출 테이블 업데이트 트리거
CREATE TRIGGER update_expenses_updated_at
BEFORE UPDATE ON expenses
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 지출 테이블 RLS 정책
ALTER TABLE expenses ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 지출만 볼 수 있음" ON expenses
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 지출만 수정할 수 있음" ON expenses
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 지출만 삭제할 수 있음" ON expenses
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 지출만 생성할 수 있음" ON expenses
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 6. 템플릿 테이블
CREATE TABLE templates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
card_id UUID REFERENCES cards(id) ON DELETE SET NULL,
category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
amount DECIMAL(12, 2) NOT NULL,
description TEXT,
location TEXT,
is_income BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id, name)
);
-- 템플릿 테이블 업데이트 트리거
CREATE TRIGGER update_templates_updated_at
BEFORE UPDATE ON templates
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 템플릿 테이블 RLS 정책
ALTER TABLE templates ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 템플릿만 볼 수 있음" ON templates
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 템플릿만 수정할 수 있음" ON templates
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 템플릿만 삭제할 수 있음" ON templates
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 템플릿만 생성할 수 있음" ON templates
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 7. 알림 테이블
CREATE TABLE notifications (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
message TEXT NOT NULL,
type TEXT NOT NULL, -- 'limit_warning', 'payment_due', 'tip', 'system'
related_id UUID, -- 관련된 항목의 ID (카드, 한도 등)
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 알림 테이블 업데이트 트리거
CREATE TRIGGER update_notifications_updated_at
BEFORE UPDATE ON notifications
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 알림 테이블 RLS 정책
ALTER TABLE notifications ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 알림만 볼 수 있음" ON notifications
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 알림만 수정할 수 있음" ON notifications
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 알림만 삭제할 수 있음" ON notifications
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "시스템만 알림을 생성할 수 있음" ON notifications
FOR INSERT WITH CHECK (auth.uid() = user_id OR auth.uid() IS NULL);
-- 8. 분석 설정 테이블
CREATE TABLE analysis_settings (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
budget_period TEXT DEFAULT 'monthly', -- 'monthly', 'weekly', 'daily'
saving_goal DECIMAL(12, 2),
analysis_period INTEGER DEFAULT 3, -- 분석에 사용할 과거 데이터 기간 (개월)
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id)
);
-- 분석 설정 테이블 업데이트 트리거
CREATE TRIGGER update_analysis_settings_updated_at
BEFORE UPDATE ON analysis_settings
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 분석 설정 테이블 RLS 정책
ALTER TABLE analysis_settings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "사용자는 자신의 분석 설정만 볼 수 있음" ON analysis_settings
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 분석 설정만 수정할 수 있음" ON analysis_settings
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 분석 설정만 삭제할 수 있음" ON analysis_settings
FOR DELETE USING (auth.uid() = user_id);
CREATE POLICY "사용자는 자신의 분석 설정만 생성할 수 있음" ON analysis_settings
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- 기본 카테고리 데이터 삽입
INSERT INTO categories (name, color, icon, is_income, is_default) VALUES
('식비', '#FF5722', 'restaurant', FALSE, TRUE),
('교통', '#2196F3', 'directions_car', FALSE, TRUE),
('주거/통신', '#9C27B0', 'home', FALSE, TRUE),
('쇼핑', '#4CAF50', 'shopping_bag', FALSE, TRUE),
('의료/건강', '#F44336', 'local_hospital', FALSE, TRUE),
('문화/여가', '#FFEB3B', 'movie', FALSE, TRUE),
('교육', '#795548', 'school', FALSE, TRUE),
('경조사/기부', '#607D8B', 'card_giftcard', FALSE, TRUE),
('기타', '#9E9E9E', 'more_horiz', FALSE, TRUE),
('급여', '#4CAF50', 'payments', TRUE, TRUE),
('용돈', '#8BC34A', 'savings', TRUE, TRUE),
('상여금', '#CDDC39', 'card_giftcard', TRUE, TRUE),
('환불', '#FFC107', 'replay', TRUE, TRUE),
('투자수익', '#00BCD4', 'trending_up', TRUE, TRUE),
('기타수입', '#9E9E9E', 'more_horiz', TRUE, TRUE);
-- 인덱스 생성
CREATE INDEX idx_expenses_user_id ON expenses(user_id);
CREATE INDEX idx_expenses_date ON expenses(date);
CREATE INDEX idx_expenses_category_id ON expenses(category_id);
CREATE INDEX idx_expenses_card_id ON expenses(card_id);
CREATE INDEX idx_limits_user_id ON limits(user_id);
CREATE INDEX idx_cards_user_id ON cards(user_id);
CREATE INDEX idx_categories_user_id ON categories(user_id);
CREATE INDEX idx_notifications_user_id ON notifications(user_id);
CREATE INDEX idx_notifications_is_read ON notifications(is_read);