'use client' import Link from 'next/link' import { useEffect, useState } from 'react' import { clearStoredApiKey, getStoredApiKey, maskApiKey, setStoredApiKey, } from '@/lib/api-key-storage' type TestState = | { status: 'idle' } | { status: 'testing' } | { status: 'success'; message: string; reply?: string } | { status: 'failed'; message: string } export default function SettingsPage() { const [storedKey, setStoredKey] = useState(null) const [draftKey, setDraftKey] = useState('') const [showKey, setShowKey] = useState(false) const [envConfigured, setEnvConfigured] = useState(null) const [testState, setTestState] = useState({ status: 'idle' }) const [savedToast, setSavedToast] = useState(false) useEffect(() => { setStoredKey(getStoredApiKey()) fetch('/api/settings/status') .then((r) => r.json()) .then((d) => setEnvConfigured(!!d.envConfigured)) .catch(() => setEnvConfigured(false)) }, []) function handleSave() { const trimmed = draftKey.trim() if (!trimmed) return setStoredApiKey(trimmed) setStoredKey(trimmed) setDraftKey('') setSavedToast(true) setTimeout(() => setSavedToast(false), 2000) } function handleClear() { if (!confirm('브라우저에 저장된 키를 삭제할까요? 환경변수가 설정되어 있다면 그것을 사용합니다.')) { return } clearStoredApiKey() setStoredKey(null) setTestState({ status: 'idle' }) } async function handleTest() { const keyToTest = draftKey.trim() || storedKey if (!keyToTest) { setTestState({ status: 'failed', message: '테스트할 키가 없습니다.' }) return } setTestState({ status: 'testing' }) try { const res = await fetch('/api/settings/test', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: keyToTest }), }) const data = await res.json() if (data.ok) { setTestState({ status: 'success', message: data.message, reply: data.reply, }) } else { setTestState({ status: 'failed', message: data.error ?? '실패' }) } } catch (err) { setTestState({ status: 'failed', message: err instanceof Error ? err.message : '네트워크 오류', }) } } const activeSource = storedKey ? '브라우저 (LocalStorage)' : envConfigured ? '환경변수 (서버)' : '없음' const activeBadgeStyle = storedKey ? 'bg-purple-100 text-purple-700' : envConfigured ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' return (
← 홈으로 📚 저장된 회의록

⚙️ 설정

Gemini API 키를 브라우저에 저장합니다. 키는 서버에 저장되지 않으며, 요청 시점에만 전송되어 사용됩니다.

현재 키 소스

{activeSource}

브라우저 키:{' '} {storedKey ? ( {maskApiKey(storedKey)} ) : ( 미설정 )}

환경변수 키:{' '} {envConfigured === null ? ( 확인 중... ) : envConfigured ? ( 설정됨 ) : ( 미설정 )}

💡 우선순위: 브라우저 키 → 환경변수 → 단순 변환 폴백

Gemini API 키

setDraftKey(e.target.value)} placeholder="AIzaSy..." className="flex-1 rounded-xl border border-neutral-300 px-4 py-3 font-mono text-sm focus:border-purple-400 focus:outline-none focus:ring-2 focus:ring-purple-100 transition-all" />

Google AI Studio 에서 무료로 발급할 수 있습니다.

{storedKey && ( )}
{savedToast && (
✓ 브라우저에 저장되었습니다.
)} {testState.status === 'success' && (
✓ {testState.message} {testState.reply && (

Gemini 응답: {testState.reply}

)}
)} {testState.status === 'failed' && (
✗ {testState.message}
)}

보안 안내

  • 키는 이 브라우저의 LocalStorage에만 저장됩니다 (서버 DB에 저장되지 않음).
  • 요약 요청 시 body로 함께 전송되어 일회성으로 사용됩니다.
  • 다른 브라우저/기기에서는 다시 입력해야 합니다.
  • 시크릿 모드 종료, 캐시 삭제 시 사라집니다.
  • HTTPS 환경에서 사용하는 것을 권장합니다 (현재 localhost는 예외).
) }