generated from gc/template-java-maven
백엔드: - JPA Entity 9개 + Repository 9개 (common 스키마) - JWT 인증 (jjwt, Access/Refresh 토큰) - AuthController (login/logout/refresh) - 공통 모듈 (BaseEntity, ErrorCode, BusinessException, PageResponse) - SecurityConfig JWT 필터 체인 통합 프론트엔드: - MainLayout (사이드바 + 헤더) + AuthLayout - 로그인 페이지 + ProtectedRoute - API 클라이언트 (fetch wrapper, JWT 자동 첨부, 401 refresh) - AuthContext + useAuth 훅 - 9개 플레이스홀더 페이지 + 라우팅 설정: - DB: snp_connection / snp_admin / common 스키마 - ddl-auto: update (개발), validate (운영) Closes #6 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
68 lines
1.8 KiB
TypeScript
68 lines
1.8 KiB
TypeScript
import { post } from './apiClient';
|
|
import type { LoginRequest, LoginResponse, User } from '../types/auth';
|
|
|
|
const TOKEN_KEY = 'snp_access_token';
|
|
const REFRESH_TOKEN_KEY = 'snp_refresh_token';
|
|
const USER_KEY = 'snp_user';
|
|
|
|
export const login = async (req: LoginRequest): Promise<LoginResponse> => {
|
|
const response = await post<LoginResponse>('/auth/login', req);
|
|
|
|
if (!response.success || !response.data) {
|
|
throw new Error(response.message || '로그인에 실패했습니다.');
|
|
}
|
|
|
|
const data = response.data;
|
|
localStorage.setItem(TOKEN_KEY, data.accessToken);
|
|
localStorage.setItem(REFRESH_TOKEN_KEY, data.refreshToken);
|
|
localStorage.setItem(USER_KEY, JSON.stringify({
|
|
loginId: data.loginId,
|
|
userName: data.userName,
|
|
role: data.role,
|
|
}));
|
|
|
|
return data;
|
|
};
|
|
|
|
export const logout = async (): Promise<void> => {
|
|
try {
|
|
await post('/auth/logout');
|
|
} finally {
|
|
localStorage.removeItem(TOKEN_KEY);
|
|
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
localStorage.removeItem(USER_KEY);
|
|
}
|
|
};
|
|
|
|
export const refreshToken = async (): Promise<void> => {
|
|
const storedRefreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
if (!storedRefreshToken) {
|
|
throw new Error('No refresh token available');
|
|
}
|
|
|
|
const response = await post<{ accessToken: string }>('/auth/refresh', {
|
|
refreshToken: storedRefreshToken,
|
|
});
|
|
|
|
if (!response.success || !response.data) {
|
|
throw new Error('Token refresh failed');
|
|
}
|
|
|
|
localStorage.setItem(TOKEN_KEY, response.data.accessToken);
|
|
};
|
|
|
|
export const getStoredUser = (): User | null => {
|
|
const userStr = localStorage.getItem(USER_KEY);
|
|
if (!userStr) return null;
|
|
|
|
try {
|
|
return JSON.parse(userStr) as User;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
export const getAccessToken = (): string | null => {
|
|
return localStorage.getItem(TOKEN_KEY);
|
|
};
|