From 6de30b681aaa8a300d2a0e9d775eacef574c6006 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 12:24:40 +0000 Subject: [PATCH] Refactor SupabaseSettings component The SupabaseSettings component was refactored into smaller, more manageable components to improve code organization and maintainability. The UI and functionality remain unchanged. --- .../supabase/SupabaseConnectionTest.tsx | 116 ++++++++ .../supabase/SupabaseHelpSection.tsx | 30 ++ .../supabase/SupabaseSettingsForm.tsx | 177 +++++++++++ src/lib/supabase/client.ts | 2 +- src/pages/SupabaseSettings.tsx | 280 +----------------- 5 files changed, 335 insertions(+), 270 deletions(-) create mode 100644 src/components/supabase/SupabaseConnectionTest.tsx create mode 100644 src/components/supabase/SupabaseHelpSection.tsx create mode 100644 src/components/supabase/SupabaseSettingsForm.tsx diff --git a/src/components/supabase/SupabaseConnectionTest.tsx b/src/components/supabase/SupabaseConnectionTest.tsx new file mode 100644 index 0000000..c63d0b1 --- /dev/null +++ b/src/components/supabase/SupabaseConnectionTest.tsx @@ -0,0 +1,116 @@ + +import React, { useState } from 'react'; +import { Button } from "@/components/ui/button"; +import { RefreshCw } from "lucide-react"; +import { toast } from "@/hooks/useToast.wrapper"; +import { testSupabaseConnection } from '@/lib/supabase'; + +interface TestResult { + url: string; + proxyUrl: string; + usingProxy: boolean; + client: boolean; + restApi: boolean; + auth: boolean; + database: boolean; + errors: string[]; +} + +const SupabaseConnectionTest: React.FC = () => { + const [testResults, setTestResults] = useState(null); + const [isTesting, setIsTesting] = useState(false); + + const runConnectionTest = async () => { + setIsTesting(true); + try { + const results = await testSupabaseConnection(); + setTestResults(results); + + if (results.errors.length === 0 || (results.auth && results.restApi && results.database)) { + toast({ + title: "연결 테스트 성공", + description: "Supabase 서버 연결이 정상입니다.", + }); + } else { + toast({ + title: "연결 테스트 실패", + description: `오류 발생: ${results.errors[0]}`, + variant: "destructive" + }); + } + } catch (error: any) { + console.error("연결 테스트 중 오류:", error); + toast({ + title: "연결 테스트 오류", + description: "테스트 실행 중 예상치 못한 오류가 발생했습니다.", + variant: "destructive" + }); + } finally { + setIsTesting(false); + } + }; + + return ( +
+

연결 테스트

+ + + + {testResults && ( +
+
+ REST API: + + {testResults.restApi ? '✅ 성공' : '❌ 실패'} + +
+
+ 인증: + + {testResults.auth ? '✅ 성공' : '❌ 실패'} + +
+
+ 데이터베이스: + + {testResults.database ? '✅ 성공' : '❌ 실패'} + +
+ + {testResults.usingProxy && ( +
+

CORS 프록시 사용 중: {testResults.proxyUrl}

+
+ )} + + {testResults.errors.length > 0 && ( +
+ {testResults.errors.map((error: string, index: number) => ( +

{error}

+ ))} +
+ )} +
+ )} +
+ ); +}; + +export default SupabaseConnectionTest; diff --git a/src/components/supabase/SupabaseHelpSection.tsx b/src/components/supabase/SupabaseHelpSection.tsx new file mode 100644 index 0000000..fc50581 --- /dev/null +++ b/src/components/supabase/SupabaseHelpSection.tsx @@ -0,0 +1,30 @@ + +import React from 'react'; + +const SupabaseHelpSection: React.FC = () => { + return ( +
+

도움말

+

+ 온프레미스 Supabase 설정은 다음과 같이 구성됩니다: +

+ +
+ ); +}; + +export default SupabaseHelpSection; diff --git a/src/components/supabase/SupabaseSettingsForm.tsx b/src/components/supabase/SupabaseSettingsForm.tsx new file mode 100644 index 0000000..6f3108b --- /dev/null +++ b/src/components/supabase/SupabaseSettingsForm.tsx @@ -0,0 +1,177 @@ + +import React, { useState, useEffect } from 'react'; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { DatabaseIcon, Save, RefreshCw, Shield, AlertTriangle, Check } from "lucide-react"; +import { toast } from "@/hooks/useToast.wrapper"; +import { configureSupabase, isCorsProxyEnabled } from "@/lib/supabase/config"; +import { Switch } from "@/components/ui/switch"; + +interface SupabaseSettingsFormProps { + onSaved: () => void; +} + +const SupabaseSettingsForm: React.FC = ({ onSaved }) => { + const [supabaseUrl, setSupabaseUrl] = useState(''); + const [supabaseKey, setSupabaseKey] = useState(''); + const [useProxy, setUseProxy] = useState(false); + const [isSaving, setIsSaving] = useState(false); + + // 저장된 설정 불러오기 + useEffect(() => { + const savedUrl = localStorage.getItem('supabase_url'); + const savedKey = localStorage.getItem('supabase_key'); + const proxyEnabled = isCorsProxyEnabled(); + + if (savedUrl) setSupabaseUrl(savedUrl); + if (savedKey) setSupabaseKey(savedKey); + setUseProxy(proxyEnabled); + }, []); + + const validateUrl = (url: string): boolean => { + // URL 유효성 검사: http:// 또는 https://로 시작하는지 확인 + return /^https?:\/\/.+/.test(url); + }; + + const handleSave = () => { + if (!supabaseUrl || !supabaseKey) { + toast({ + title: "입력 오류", + description: "Supabase URL과 Anon Key를 모두 입력해주세요.", + variant: "destructive" + }); + return; + } + + if (!validateUrl(supabaseUrl)) { + toast({ + title: "URL 오류", + description: "유효한 URL 형식이 아닙니다. http:// 또는 https://로 시작하는 URL을 입력해주세요.", + variant: "destructive" + }); + return; + } + + setIsSaving(true); + + try { + // Supabase 설정 적용 + configureSupabase(supabaseUrl, supabaseKey, useProxy); + + toast({ + title: "설정 저장 완료", + description: "Supabase 설정이 저장되었습니다. 변경사항을 적용합니다.", + }); + + onSaved(); + } catch (error) { + console.error('Supabase 설정 저장 오류:', error); + toast({ + title: "설정 저장 실패", + description: "Supabase 설정을 저장하는 중 오류가 발생했습니다.", + variant: "destructive" + }); + setIsSaving(false); + } + }; + + const isHttpsUrl = supabaseUrl.startsWith("https://"); + const suggestProxy = supabaseUrl.startsWith("http://") && !useProxy; + + return ( +
+
+ +
+ + setSupabaseUrl(e.target.value)} + className="pl-10 neuro-pressed" + /> +
+ + {/* URL 관련 경고 및 안내 추가 */} + {suggestProxy && ( +
+ + HTTP URL을 사용하고 계십니다. CORS 프록시 활성화를 권장합니다. +
+ )} + + {isHttpsUrl && ( +
+ + HTTPS URL을 사용하고 있습니다. 권장됩니다. +
+ )} +
+ +
+ +
+ + + + setSupabaseKey(e.target.value)} + className="pl-10 neuro-pressed" + /> +
+
+ + {/* CORS 프록시 설정 추가 */} +
+
+ +
+ +
+ + +
+ ); +}; + +export default SupabaseSettingsForm; diff --git a/src/lib/supabase/client.ts b/src/lib/supabase/client.ts index 56870e9..75d48f2 100644 --- a/src/lib/supabase/client.ts +++ b/src/lib/supabase/client.ts @@ -34,7 +34,7 @@ try { console.log('Supabase fetch 요청:', url); // 기본 fetch 호출 - return fetch(...args) + return fetch(urlOrRequest, args[1]) .then(response => { console.log('Supabase 응답 상태:', response.status); return response; diff --git a/src/pages/SupabaseSettings.tsx b/src/pages/SupabaseSettings.tsx index 4e71824..2ad4e6e 100644 --- a/src/pages/SupabaseSettings.tsx +++ b/src/pages/SupabaseSettings.tsx @@ -1,112 +1,18 @@ -import React, { useState, useEffect } from 'react'; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; -import { DatabaseIcon, Save, RefreshCw, Shield, AlertTriangle, Check } from "lucide-react"; -import { toast } from "@/hooks/useToast.wrapper"; -import { configureSupabase, isCorsProxyEnabled } from "@/lib/supabase/config"; -import { Switch } from "@/components/ui/switch"; -import { testSupabaseConnection } from "@/lib/supabase"; +import React, { useState } from 'react'; import NavBar from '@/components/NavBar'; +import SupabaseSettingsForm from '@/components/supabase/SupabaseSettingsForm'; +import SupabaseConnectionTest from '@/components/supabase/SupabaseConnectionTest'; +import SupabaseHelpSection from '@/components/supabase/SupabaseHelpSection'; const SupabaseSettings = () => { - const [supabaseUrl, setSupabaseUrl] = useState(''); - const [supabaseKey, setSupabaseKey] = useState(''); - const [useProxy, setUseProxy] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [testResults, setTestResults] = useState(null); - const [isTesting, setIsTesting] = useState(false); + const [refreshCounter, setRefreshCounter] = useState(0); - // 저장된 설정 불러오기 - useEffect(() => { - const savedUrl = localStorage.getItem('supabase_url'); - const savedKey = localStorage.getItem('supabase_key'); - const proxyEnabled = isCorsProxyEnabled(); - - if (savedUrl) setSupabaseUrl(savedUrl); - if (savedKey) setSupabaseKey(savedKey); - setUseProxy(proxyEnabled); - }, []); - - const validateUrl = (url: string): boolean => { - // URL 유효성 검사: http:// 또는 https://로 시작하는지 확인 - return /^https?:\/\/.+/.test(url); + const handleSettingsSaved = () => { + // 설정이 저장될 때 테스트 컴포넌트를 다시 렌더링하기 위한 카운터 증가 + setRefreshCounter(prev => prev + 1); }; - const handleSave = () => { - if (!supabaseUrl || !supabaseKey) { - toast({ - title: "입력 오류", - description: "Supabase URL과 Anon Key를 모두 입력해주세요.", - variant: "destructive" - }); - return; - } - - if (!validateUrl(supabaseUrl)) { - toast({ - title: "URL 오류", - description: "유효한 URL 형식이 아닙니다. http:// 또는 https://로 시작하는 URL을 입력해주세요.", - variant: "destructive" - }); - return; - } - - setIsSaving(true); - - try { - // Supabase 설정 적용 - configureSupabase(supabaseUrl, supabaseKey, useProxy); - - toast({ - title: "설정 저장 완료", - description: "Supabase 설정이 저장되었습니다. 변경사항을 적용합니다.", - }); - } catch (error) { - console.error('Supabase 설정 저장 오류:', error); - toast({ - title: "설정 저장 실패", - description: "Supabase 설정을 저장하는 중 오류가 발생했습니다.", - variant: "destructive" - }); - setIsSaving(false); - } - }; - - const runConnectionTest = async () => { - setIsTesting(true); - try { - const results = await testSupabaseConnection(); - setTestResults(results); - - if (results.errors.length === 0 || (results.auth && results.restApi && results.database)) { - toast({ - title: "연결 테스트 성공", - description: "Supabase 서버 연결이 정상입니다.", - }); - } else { - toast({ - title: "연결 테스트 실패", - description: `오류 발생: ${results.errors[0]}`, - variant: "destructive" - }); - } - } catch (error: any) { - console.error("연결 테스트 중 오류:", error); - toast({ - title: "연결 테스트 오류", - description: "테스트 실행 중 예상치 못한 오류가 발생했습니다.", - variant: "destructive" - }); - } finally { - setIsTesting(false); - } - }; - - const isHttpsUrl = supabaseUrl.startsWith("https://"); - const suggestProxy = supabaseUrl.startsWith("http://") && !useProxy; - return (
@@ -118,180 +24,16 @@ const SupabaseSettings = () => {
-
-
- -
- - setSupabaseUrl(e.target.value)} - className="pl-10 neuro-pressed" - /> -
- - {/* URL 관련 경고 및 안내 추가 */} - {suggestProxy && ( -
- - HTTP URL을 사용하고 계십니다. CORS 프록시 활성화를 권장합니다. -
- )} - - {isHttpsUrl && ( -
- - HTTPS URL을 사용하고 있습니다. 권장됩니다. -
- )} -
- -
- -
- - - - setSupabaseKey(e.target.value)} - className="pl-10 neuro-pressed" - /> -
-
- - {/* CORS 프록시 설정 추가 */} -
-
- -
- -
- - -
+
{/* 연결 테스트 섹션 */}
-

연결 테스트

- - - - {testResults && ( -
-
- REST API: - - {testResults.restApi ? '✅ 성공' : '❌ 실패'} - -
-
- 인증: - - {testResults.auth ? '✅ 성공' : '❌ 실패'} - -
-
- 데이터베이스: - - {testResults.database ? '✅ 성공' : '❌ 실패'} - -
- - {testResults.usingProxy && ( -
-

CORS 프록시 사용 중: {testResults.proxyUrl}

-
- )} - - {testResults.errors.length > 0 && ( -
- {testResults.errors.map((error: string, index: number) => ( -

{error}

- ))} -
- )} -
- )} +
-

도움말

-

- 온프레미스 Supabase 설정은 다음과 같이 구성됩니다: -

-
    -
  • - Supabase URL: 온프레미스로 설치한 Supabase 인스턴스의 URL을 입력합니다. - (예: https://192.168.1.100:8000) -
  • -
  • - Anon Key: Supabase 대시보드의 API 설정에서 찾을 수 있는 anon/public 키를 입력합니다. -
  • -
  • - CORS 프록시: HTTP URL에 대한 브라우저 보안 제한을 우회하기 위해 사용합니다. HTTPS가 아닌 URL에 접근 문제가 있는 경우 활성화하세요. -
  • -
  • - 가능하면 HTTPS URL을 사용하는 것이 좋습니다. HTTP URL을 사용해야 하는 경우 CORS 프록시를 활성화하세요. -
  • -
+