- frontend/ 폴더로 프론트엔드 전체 이관 - signal-batch API 연동 (한국 선박 위치 데이터) - Tailwind CSS 4 + CSS 변수 테마 토큰 (dark/light) - i18next 다국어 (ko/en) 인프라 + 28개 컴포넌트 적용 - 레이어 패널 트리 구조 재설계 (카테고리별 온/오프, 범례) - Google OAuth 로그인 화면 + DEV LOGIN 우회 - 외부 API CORS 프록시 전환 (Airplanes.live, OpenSky, CelesTrak) - ShipLayer 이미지 탭 전환 (signal-batch / MarineTraffic) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
1.7 KiB
TypeScript
79 lines
1.7 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import {
|
|
googleLogin,
|
|
getMe,
|
|
logout as logoutApi,
|
|
} from '../services/authApi';
|
|
import type { AuthUser } from '../services/authApi';
|
|
|
|
interface AuthState {
|
|
user: AuthUser | null;
|
|
isLoading: boolean;
|
|
isAuthenticated: boolean;
|
|
}
|
|
|
|
const DEV_USER: AuthUser = {
|
|
email: 'dev@gcsc.co.kr',
|
|
name: 'Developer',
|
|
};
|
|
|
|
export function useAuth() {
|
|
const [state, setState] = useState<AuthState>({
|
|
user: null,
|
|
isLoading: true,
|
|
isAuthenticated: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
getMe()
|
|
.then((user) => {
|
|
if (!cancelled) {
|
|
setState({ user, isLoading: false, isAuthenticated: true });
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled) {
|
|
setState({ user: null, isLoading: false, isAuthenticated: false });
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
const login = useCallback(async (credential: string) => {
|
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
try {
|
|
const user = await googleLogin(credential);
|
|
setState({ user, isLoading: false, isAuthenticated: true });
|
|
} catch {
|
|
setState({ user: null, isLoading: false, isAuthenticated: false });
|
|
throw new Error('Login failed');
|
|
}
|
|
}, []);
|
|
|
|
const devLogin = useCallback(() => {
|
|
setState({ user: DEV_USER, isLoading: false, isAuthenticated: true });
|
|
}, []);
|
|
|
|
const logout = useCallback(async () => {
|
|
try {
|
|
await logoutApi();
|
|
} finally {
|
|
setState({ user: null, isLoading: false, isAuthenticated: false });
|
|
}
|
|
}, []);
|
|
|
|
return {
|
|
user: state.user,
|
|
isLoading: state.isLoading,
|
|
isAuthenticated: state.isAuthenticated,
|
|
login,
|
|
devLogin,
|
|
logout,
|
|
};
|
|
}
|