import { useCallback, useEffect, useState } from 'react' import { ApiError, getOrgs, type Org } from './api/client' import LoginPage from './components/LoginPage' import OrgDetail from './components/OrgDetail' import OrgList from './components/OrgList' import SeedUpload from './components/SeedUpload' // 'checking'이 필요하다. true로 시작하면 첫 렌더에서 getOrgs()가 아직 안 끝났는데도 // 로그인한 것처럼 빈 껍데기를 먼저 보여주고, 401이 온 뒤에야 로그인 화면으로 튄다. // // 'error'는 401이 아닌 실패(서버 500, 네트워크 단절 등)를 위한 상태다. 이런 오류까지 // '로그아웃'으로 취급해 로그인 화면으로 보내면, 로그인해도 같은 오류가 반복되고 // 원인을 알 수 없게 된다 - 세션은 멀쩡한데 서버/네트워크만 문제인 경우이므로 // 오류 메시지와 재시도 버튼을 보여주는 편이 맞다. type AuthState = 'checking' | 'authenticated' | 'anonymous' | 'error' export default function App() { const [orgs, setOrgs] = useState([]) const [selectedId, setSelectedId] = useState(null) const [auth, setAuth] = useState('checking') const [authErrorMessage, setAuthErrorMessage] = useState(null) const reload = useCallback(async () => { try { setOrgs(await getOrgs()) setAuth('authenticated') } catch (e) { if (e instanceof ApiError && e.status === 401) { setAuth('anonymous') } else { setAuthErrorMessage((e as Error).message) setAuth('error') } } }, []) useEffect(() => { void reload() }, [reload]) if (auth === 'checking') { return
불러오는 중…
} if (auth === 'anonymous') { return void reload()} /> } if (auth === 'error') { return (

{authErrorMessage}

) } const selected = orgs.find((org) => org.id === selectedId) ?? null return (
void reload()} />
{selected ? ( void reload()} /> ) : (

왼쪽에서 기관을 선택하세요.

)}
) }