From f335a381e3940cdbe447736923442bf441c19a56 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 13:10:22 +0000 Subject: [PATCH] Refactor Supabase setup file Refactor src/lib/supabase/setup.ts into smaller files for better maintainability and readability. --- src/lib/supabase/index.ts | 5 +- src/lib/supabase/setup.ts | 225 +------------------------------ src/lib/supabase/setup/index.ts | 59 ++++++++ src/lib/supabase/setup/status.ts | 68 ++++++++++ src/lib/supabase/setup/tables.ts | 175 ++++++++++++++++++++++++ 5 files changed, 308 insertions(+), 224 deletions(-) create mode 100644 src/lib/supabase/setup/index.ts create mode 100644 src/lib/supabase/setup/status.ts create mode 100644 src/lib/supabase/setup/tables.ts diff --git a/src/lib/supabase/index.ts b/src/lib/supabase/index.ts index 48d83f8..0cdf13b 100644 --- a/src/lib/supabase/index.ts +++ b/src/lib/supabase/index.ts @@ -1,9 +1,12 @@ import { supabase, isValidUrl } from './client'; import { testSupabaseConnection } from './tests'; +import { createRequiredTables, checkTablesStatus } from './setup'; export { supabase, isValidUrl, - testSupabaseConnection + testSupabaseConnection, + createRequiredTables, + checkTablesStatus }; diff --git a/src/lib/supabase/setup.ts b/src/lib/supabase/setup.ts index da82d41..c44b2f5 100644 --- a/src/lib/supabase/setup.ts +++ b/src/lib/supabase/setup.ts @@ -1,224 +1,3 @@ -import { supabase } from './client'; - -/** - * Supabase 데이터베이스에 필요한 테이블을 생성합니다. - * 이 함수는 로그인 후 실행되어야 합니다. - */ -export const createRequiredTables = async (): Promise<{ success: boolean; message: string }> => { - try { - console.log('데이터베이스 테이블 생성 시작...'); - - // 테이블이 이미 존재하는지 확인 - const { data: existingTables, error: tableError } = await supabase - .rpc('get_tables'); - - if (tableError) { - console.error('테이블 목록 가져오기 실패:', tableError); - - // RPC가 존재하지 않는 경우, 다른 방법으로 테이블 생성 시도 - return await createTablesWithRawSQL(); - } - - console.log('기존 테이블:', existingTables); - - // 이미 테이블이 존재하는 경우 - if (Array.isArray(existingTables) && - existingTables.some(t => t.name === 'transactions') && - existingTables.some(t => t.name === 'budgets')) { - return { - success: true, - message: '필요한 테이블이 이미 존재합니다.' - }; - } - - return await createTablesWithRawSQL(); - - } catch (error: any) { - console.error('테이블 생성 중 오류 발생:', error); - return { - success: false, - message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}` - }; - } -}; - -/** - * SQL 쿼리를 직접 실행하여 테이블을 생성합니다. - */ -const createTablesWithRawSQL = async (): Promise<{ success: boolean; message: string }> => { - try { - // transactions 테이블 생성 - const { error: transactionsError } = await supabase.rpc('execute_sql', { - sql_query: ` - CREATE TABLE IF NOT EXISTS transactions ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) NOT NULL, - title TEXT NOT NULL, - amount NUMERIC NOT NULL, - category TEXT NOT NULL, - date TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - type TEXT NOT NULL, - notes TEXT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() - ); - - -- Row Level Security 설정 - ALTER TABLE transactions ENABLE ROW LEVEL SECURITY; - - -- 사용자 정책 설정 (읽기) - CREATE POLICY "사용자는 자신의 트랜잭션만 볼 수 있음" - ON transactions FOR SELECT - USING (auth.uid() = user_id); - - -- 사용자 정책 설정 (쓰기) - CREATE POLICY "사용자는 자신의 트랜잭션만 추가할 수 있음" - ON transactions FOR INSERT - WITH CHECK (auth.uid() = user_id); - - -- 사용자 정책 설정 (업데이트) - CREATE POLICY "사용자는 자신의 트랜잭션만 업데이트할 수 있음" - ON transactions FOR UPDATE - USING (auth.uid() = user_id); - - -- 사용자 정책 설정 (삭제) - CREATE POLICY "사용자는 자신의 트랜잭션만 삭제할 수 있음" - ON transactions FOR DELETE - USING (auth.uid() = user_id); - ` - }); - - if (transactionsError) { - console.error('transactions 테이블 생성 실패:', transactionsError); - return { - success: false, - message: `transactions 테이블 생성 실패: ${transactionsError.message}` - }; - } - - // budgets 테이블 생성 - const { error: budgetsError } = await supabase.rpc('execute_sql', { - sql_query: ` - CREATE TABLE IF NOT EXISTS budgets ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - user_id UUID REFERENCES auth.users(id) NOT NULL, - month INTEGER NOT NULL, - year INTEGER NOT NULL, - total_budget NUMERIC NOT NULL DEFAULT 0, - categories JSONB NOT NULL DEFAULT '{}', - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - UNIQUE (user_id, month, year) - ); - - -- Row Level Security 설정 - ALTER TABLE budgets ENABLE ROW LEVEL SECURITY; - - -- 사용자 정책 설정 (읽기) - CREATE POLICY "사용자는 자신의 예산만 볼 수 있음" - ON budgets FOR SELECT - USING (auth.uid() = user_id); - - -- 사용자 정책 설정 (쓰기) - CREATE POLICY "사용자는 자신의 예산만 추가할 수 있음" - ON budgets FOR INSERT - WITH CHECK (auth.uid() = user_id); - - -- 사용자 정책 설정 (업데이트) - CREATE POLICY "사용자는 자신의 예산만 업데이트할 수 있음" - ON budgets FOR UPDATE - USING (auth.uid() = user_id); - - -- 사용자 정책 설정 (삭제) - CREATE POLICY "사용자는 자신의 예산만 삭제할 수 있음" - ON budgets FOR DELETE - USING (auth.uid() = user_id); - ` - }); - - if (budgetsError) { - console.error('budgets 테이블 생성 실패:', budgetsError); - return { - success: false, - message: `budgets 테이블 생성 실패: ${budgetsError.message}` - }; - } - - // _tests 테이블 생성 (연결 테스트용) - const { error: testsError } = await supabase.rpc('execute_sql', { - sql_query: ` - CREATE TABLE IF NOT EXISTS _tests ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - test_name TEXT NOT NULL, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() - ); - - -- 모든 사용자가 접근 가능하도록 설정 - ALTER TABLE _tests ENABLE ROW LEVEL SECURITY; - CREATE POLICY "모든 사용자가 테스트 테이블에 접근 가능" - ON _tests FOR SELECT - USING (true); - ` - }); - - if (testsError) { - console.error('_tests 테이블 생성 실패:', testsError); - // _tests 테이블은 필수가 아니므로 오류가 발생해도 진행 - } - - return { - success: true, - message: '모든 필수 테이블이 성공적으로 생성되었습니다.' - }; - } catch (error: any) { - console.error('테이블 생성 중 오류 발생:', error); - return { - success: false, - message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}` - }; - } -}; - -/** - * 테이블 상태를 확인합니다. - */ -export const checkTablesStatus = async (): Promise<{ - transactions: boolean; - budgets: boolean; - tests: boolean; -}> => { - const tables = { - transactions: false, - budgets: false, - tests: false - }; - - try { - // transactions 테이블 확인 - const { count: transactionsCount, error: transactionsError } = await supabase - .from('transactions') - .select('*', { count: 'exact', head: true }); - - tables.transactions = !transactionsError; - - // budgets 테이블 확인 - const { count: budgetsCount, error: budgetsError } = await supabase - .from('budgets') - .select('*', { count: 'exact', head: true }); - - tables.budgets = !budgetsError; - - // _tests 테이블 확인 - const { count: testsCount, error: testsError } = await supabase - .from('_tests') - .select('*', { count: 'exact', head: true }); - - tables.tests = !testsError; - - return tables; - } catch (error) { - console.error('테이블 상태 확인 중 오류 발생:', error); - return tables; - } -}; +// 리팩토링된 파일로 리다이렉션 +export { createRequiredTables, checkTablesStatus } from './setup/index'; diff --git a/src/lib/supabase/setup/index.ts b/src/lib/supabase/setup/index.ts new file mode 100644 index 0000000..5890a49 --- /dev/null +++ b/src/lib/supabase/setup/index.ts @@ -0,0 +1,59 @@ + +import { createTransactionsTable, createBudgetsTable, createTestsTable } from './tables'; +import { checkTablesStatus, getExistingTables } from './status'; + +/** + * Supabase 데이터베이스에 필요한 테이블을 생성합니다. + * 이 함수는 로그인 후 실행되어야 합니다. + */ +export const createRequiredTables = async (): Promise<{ success: boolean; message: string }> => { + try { + console.log('데이터베이스 테이블 생성 시작...'); + + // 테이블이 이미 존재하는지 확인 + const existingTables = await getExistingTables(); + + if (existingTables !== null) { + console.log('기존 테이블:', existingTables); + + // 이미 테이블이 존재하는 경우 + if (existingTables.includes('transactions') && existingTables.includes('budgets')) { + return { + success: true, + message: '필요한 테이블이 이미 존재합니다.' + }; + } + } + + // 필요한 테이블 생성 + const transactionsResult = await createTransactionsTable(); + if (!transactionsResult.success) { + return transactionsResult; + } + + const budgetsResult = await createBudgetsTable(); + if (!budgetsResult.success) { + return budgetsResult; + } + + // 테스트 테이블은 선택적으로 생성 (실패해도 진행) + const testsResult = await createTestsTable(); + if (!testsResult.success) { + console.warn('테스트 테이블 생성 실패 (무시됨):', testsResult.message); + } + + return { + success: true, + message: '모든 필수 테이블이 성공적으로 생성되었습니다.' + }; + } catch (error: any) { + console.error('테이블 생성 중 오류 발생:', error); + return { + success: false, + message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}` + }; + } +}; + +// 기존 checkTablesStatus 함수 내보내기 +export { checkTablesStatus }; diff --git a/src/lib/supabase/setup/status.ts b/src/lib/supabase/setup/status.ts new file mode 100644 index 0000000..ebba95e --- /dev/null +++ b/src/lib/supabase/setup/status.ts @@ -0,0 +1,68 @@ + +import { supabase } from '../client'; + +/** + * 테이블 상태를 확인합니다. + */ +export const checkTablesStatus = async (): Promise<{ + transactions: boolean; + budgets: boolean; + tests: boolean; +}> => { + const tables = { + transactions: false, + budgets: false, + tests: false + }; + + try { + // transactions 테이블 확인 + const { count: transactionsCount, error: transactionsError } = await supabase + .from('transactions') + .select('*', { count: 'exact', head: true }); + + tables.transactions = !transactionsError; + + // budgets 테이블 확인 + const { count: budgetsCount, error: budgetsError } = await supabase + .from('budgets') + .select('*', { count: 'exact', head: true }); + + tables.budgets = !budgetsError; + + // _tests 테이블 확인 + const { count: testsCount, error: testsError } = await supabase + .from('_tests') + .select('*', { count: 'exact', head: true }); + + tables.tests = !testsError; + + return tables; + } catch (error) { + console.error('테이블 상태 확인 중 오류 발생:', error); + return tables; + } +}; + +/** + * 기존 테이블 목록을 가져옵니다. + */ +export const getExistingTables = async (): Promise => { + try { + const { data, error } = await supabase.rpc('get_tables'); + + if (error) { + console.error('테이블 목록 가져오기 실패:', error); + return null; + } + + if (Array.isArray(data)) { + return data.map(t => t.name); + } + + return []; + } catch (error) { + console.error('테이블 목록 확인 중 오류 발생:', error); + return null; + } +}; diff --git a/src/lib/supabase/setup/tables.ts b/src/lib/supabase/setup/tables.ts new file mode 100644 index 0000000..e5614ec --- /dev/null +++ b/src/lib/supabase/setup/tables.ts @@ -0,0 +1,175 @@ + +import { supabase } from '../client'; + +/** + * 트랜잭션 테이블을 생성합니다. + */ +export const createTransactionsTable = async (): Promise<{ success: boolean; message: string }> => { + try { + const { error } = await supabase.rpc('execute_sql', { + sql_query: ` + CREATE TABLE IF NOT EXISTS transactions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES auth.users(id) NOT NULL, + title TEXT NOT NULL, + amount NUMERIC NOT NULL, + category TEXT NOT NULL, + date TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + type TEXT NOT NULL, + notes TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + ); + + -- Row Level Security 설정 + ALTER TABLE transactions ENABLE ROW LEVEL SECURITY; + + -- 사용자 정책 설정 (읽기) + CREATE POLICY "사용자는 자신의 트랜잭션만 볼 수 있음" + ON transactions FOR SELECT + USING (auth.uid() = user_id); + + -- 사용자 정책 설정 (쓰기) + CREATE POLICY "사용자는 자신의 트랜잭션만 추가할 수 있음" + ON transactions FOR INSERT + WITH CHECK (auth.uid() = user_id); + + -- 사용자 정책 설정 (업데이트) + CREATE POLICY "사용자는 자신의 트랜잭션만 업데이트할 수 있음" + ON transactions FOR UPDATE + USING (auth.uid() = user_id); + + -- 사용자 정책 설정 (삭제) + CREATE POLICY "사용자는 자신의 트랜잭션만 삭제할 수 있음" + ON transactions FOR DELETE + USING (auth.uid() = user_id); + ` + }); + + if (error) { + console.error('transactions 테이블 생성 실패:', error); + return { + success: false, + message: `transactions 테이블 생성 실패: ${error.message}` + }; + } + + return { + success: true, + message: 'transactions 테이블 생성 성공' + }; + } catch (error: any) { + console.error('트랜잭션 테이블 생성 중 오류 발생:', error); + return { + success: false, + message: `트랜잭션 테이블 생성 실패: ${error.message || '알 수 없는 오류'}` + }; + } +}; + +/** + * 예산 테이블을 생성합니다. + */ +export const createBudgetsTable = async (): Promise<{ success: boolean; message: string }> => { + try { + const { error } = await supabase.rpc('execute_sql', { + sql_query: ` + CREATE TABLE IF NOT EXISTS budgets ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES auth.users(id) NOT NULL, + month INTEGER NOT NULL, + year INTEGER NOT NULL, + total_budget NUMERIC NOT NULL DEFAULT 0, + categories JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE (user_id, month, year) + ); + + -- Row Level Security 설정 + ALTER TABLE budgets ENABLE ROW LEVEL SECURITY; + + -- 사용자 정책 설정 (읽기) + CREATE POLICY "사용자는 자신의 예산만 볼 수 있음" + ON budgets FOR SELECT + USING (auth.uid() = user_id); + + -- 사용자 정책 설정 (쓰기) + CREATE POLICY "사용자는 자신의 예산만 추가할 수 있음" + ON budgets FOR INSERT + WITH CHECK (auth.uid() = user_id); + + -- 사용자 정책 설정 (업데이트) + CREATE POLICY "사용자는 자신의 예산만 업데이트할 수 있음" + ON budgets FOR UPDATE + USING (auth.uid() = user_id); + + -- 사용자 정책 설정 (삭제) + CREATE POLICY "사용자는 자신의 예산만 삭제할 수 있음" + ON budgets FOR DELETE + USING (auth.uid() = user_id); + ` + }); + + if (error) { + console.error('budgets 테이블 생성 실패:', error); + return { + success: false, + message: `budgets 테이블 생성 실패: ${error.message}` + }; + } + + return { + success: true, + message: 'budgets 테이블 생성 성공' + }; + } catch (error: any) { + console.error('예산 테이블 생성 중 오류 발생:', error); + return { + success: false, + message: `예산 테이블 생성 실패: ${error.message || '알 수 없는 오류'}` + }; + } +}; + +/** + * 테스트 테이블을 생성합니다. + */ +export const createTestsTable = async (): Promise<{ success: boolean; message: string }> => { + try { + const { error } = await supabase.rpc('execute_sql', { + sql_query: ` + CREATE TABLE IF NOT EXISTS _tests ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + test_name TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + ); + + -- 모든 사용자가 접근 가능하도록 설정 + ALTER TABLE _tests ENABLE ROW LEVEL SECURITY; + CREATE POLICY "모든 사용자가 테스트 테이블에 접근 가능" + ON _tests FOR SELECT + USING (true); + ` + }); + + if (error) { + console.error('_tests 테이블 생성 실패:', error); + return { + success: false, + message: `_tests 테이블 생성 실패: ${error.message}` + }; + } + + return { + success: true, + message: '_tests 테이블 생성 성공' + }; + } catch (error: any) { + console.error('테스트 테이블 생성 중 오류 발생:', error); + return { + success: false, + message: `테스트 테이블 생성 실패: ${error.message || '알 수 없는 오류'}` + }; + } +};