feat: React 18 프론트엔드 추가 및 프로젝트 구조 정리

- wtm-frontend → wtm-frontend-vue 이름 변경
- wtm-frontend-react 추가 (React 18 + PrimeReact + Zustand)
  - 동일한 모듈 구조 및 API 연동 (Vue 버전과 기능 동일)
  - Vue:5173 / React:5174 포트 분리
- 개발자 가이드에 React 프론트엔드 안내 추가
- .gitignore: Claude/OMC, 문서 생성 스크립트, package-lock 제외
- 불필요 파일 git 추적 제거 (.omc, generate_*.py, regenerate_*.py)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이 Commit은 다음에 포함되어 있습니다:
2026-03-30 20:50:23 +09:00
부모 dd263a6e46
커밋 cda5f9591e
212개의 변경된 파일3633개의 추가작업 그리고 5244개의 파일을 삭제

파일 보기

@@ -0,0 +1,64 @@
import { create } from 'zustand';
import { authService as tokenService } from '@/core/auth/auth.service';
import { authApi } from './auth.service';
import type { AuthUser } from '@/core/auth/auth.types';
interface AuthState {
currentUser: AuthUser | null;
loading: boolean;
unreadCount: number;
login: (email: string, password: string) => Promise<void>;
fetchMe: () => Promise<void>;
logout: () => void;
reset: () => void;
}
function mapUser(u: Record<string, unknown>): AuthUser {
return {
id: u.id as number,
email: u.email as string,
fullName: (u.full_name ?? u.fullName ?? '') as string,
roles: Array.isArray(u.roles) && u.roles.length ? u.roles : (u.is_admin ? ['SA'] : ['USER']),
departmentId: (u.department_id ?? u.departmentId) as number | undefined,
};
}
export const useAuthStore = create<AuthState>((set) => ({
currentUser: null,
loading: false,
unreadCount: 0,
login: async (email, password) => {
set({ loading: true });
try {
const { data } = await authApi.login({ email, password });
const accessToken = (data.access_token ?? data.accessToken) as string;
const refreshToken = (data.refresh_token ?? data.refreshToken) as string;
tokenService.setTokens(accessToken, refreshToken);
set({ currentUser: mapUser(data.user as unknown as Record<string, unknown>) });
} finally {
set({ loading: false });
}
},
fetchMe: async () => {
try {
const { data } = await authApi.me();
set({ currentUser: mapUser(data as unknown as Record<string, unknown>) });
} catch {
tokenService.clearTokens();
set({ currentUser: null });
window.location.href = '/login';
}
},
logout: () => {
tokenService.clearTokens();
set({ currentUser: null });
window.location.href = '/login';
},
reset: () => {
set({ currentUser: null, loading: false, unreadCount: 0 });
},
}));