ERP-node/frontend/hooks/useLogin.ts

183 lines
5.1 KiB
TypeScript
Raw Normal View History

2025-08-21 09:41:46 +09:00
"use client";
import { useState, useEffect, useCallback } from "react";
import { useRouter } from "next/navigation";
import { LoginFormData } from "@/types/auth";
2025-08-21 09:41:46 +09:00
import { AUTH_CONFIG, FORM_VALIDATION } from "@/constants/auth";
import { apiCall } from "@/lib/api/client";
2025-08-21 09:41:46 +09:00
/**
*
* API lib/api/client의 apiCall(Axios) (fetch )
2025-08-21 09:41:46 +09:00
*/
export const useLogin = () => {
const router = useRouter();
// 상태 관리
const [formData, setFormData] = useState<LoginFormData>({
userId: "",
password: "",
});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [isPopMode, setIsPopMode] = useState(false);
// localStorage에서 POP 모드 상태 복원
useEffect(() => {
const saved = localStorage.getItem("popLoginMode");
if (saved === "true") setIsPopMode(true);
}, []);
const togglePopMode = useCallback(() => {
setIsPopMode((prev) => {
const next = !prev;
localStorage.setItem("popLoginMode", String(next));
return next;
});
}, []);
2025-08-21 09:41:46 +09:00
/**
*
*/
const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
// 입력 시 에러 메시지 제거
if (error) setError("");
},
[error],
);
/**
* /
*/
const togglePasswordVisibility = useCallback(() => {
setShowPassword((prev) => !prev);
}, []);
/**
*
*/
const validateForm = useCallback((): string | null => {
if (!formData.userId.trim()) {
return FORM_VALIDATION.MESSAGES.USER_ID_REQUIRED;
}
if (!formData.password.trim()) {
return FORM_VALIDATION.MESSAGES.PASSWORD_REQUIRED;
}
return null;
}, [formData]);
/**
* (apiCall )
2025-08-21 09:41:46 +09:00
*/
const checkExistingAuth = useCallback(async () => {
try {
const token = localStorage.getItem("authToken");
if (!token) return;
2025-08-21 09:41:46 +09:00
const result = await apiCall<{ isAuthenticated?: boolean }>("GET", AUTH_CONFIG.ENDPOINTS.STATUS);
2025-08-21 09:41:46 +09:00
if (result.success && result.data?.isAuthenticated) {
2025-08-21 09:41:46 +09:00
router.push(AUTH_CONFIG.ROUTES.MAIN);
} else {
localStorage.removeItem("authToken");
document.cookie = "authToken=; path=/; max-age=0; SameSite=Lax";
2025-08-21 09:41:46 +09:00
}
} catch {
2025-08-21 09:41:46 +09:00
localStorage.removeItem("authToken");
document.cookie = "authToken=; path=/; max-age=0; SameSite=Lax";
2025-08-21 09:41:46 +09:00
}
}, [router]);
2025-08-21 09:41:46 +09:00
/**
* (apiCall - Axios , fetch )
2025-08-21 09:41:46 +09:00
*/
const handleLogin = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
const validationError = validateForm();
if (validationError) {
setError(validationError);
return;
}
setIsLoading(true);
setError("");
try {
const result = await apiCall<{
token?: string;
firstMenuPath?: string;
popLandingPath?: string;
}>("POST", AUTH_CONFIG.ENDPOINTS.LOGIN, {
userId: formData.userId,
password: formData.password,
2025-08-21 09:41:46 +09:00
});
if (result.success && result.data?.token) {
// JWT 토큰 저장 (localStorage와 쿠키 모두에 저장)
2025-08-21 09:41:46 +09:00
localStorage.setItem("authToken", result.data.token);
// 쿠키에도 저장 (미들웨어에서 사용)
document.cookie = `authToken=${result.data.token}; path=/; max-age=86400; SameSite=Lax`;
if (isPopMode) {
const popPath = result.data?.popLandingPath;
if (popPath) {
router.push(popPath);
} else {
setError("POP 화면이 설정되어 있지 않습니다. 관리자에게 메뉴 관리에서 POP 화면을 설정해달라고 요청하세요.");
setIsLoading(false);
return;
}
2025-10-28 14:55:41 +09:00
} else {
const firstMenuPath = result.data?.firstMenuPath;
if (firstMenuPath) {
router.push(firstMenuPath);
} else {
router.push(AUTH_CONFIG.ROUTES.MAIN);
}
2025-10-28 14:55:41 +09:00
}
2025-08-21 09:41:46 +09:00
} else {
// 로그인 실패
setError(result.message || FORM_VALIDATION.MESSAGES.LOGIN_FAILED);
console.error("로그인 실패:", result);
}
} catch (error) {
console.error("로그인 오류:", error);
setError(FORM_VALIDATION.MESSAGES.CONNECTION_FAILED);
} finally {
setIsLoading(false);
}
},
[formData, validateForm, router, isPopMode],
2025-08-21 09:41:46 +09:00
);
// 컴포넌트 마운트 시 기존 인증 상태 확인
2025-08-21 09:41:46 +09:00
useEffect(() => {
checkExistingAuth();
}, [checkExistingAuth]);
2025-08-21 09:41:46 +09:00
return {
// 상태
formData,
isLoading,
error,
showPassword,
isPopMode,
2025-08-21 09:41:46 +09:00
// 액션
handleInputChange,
handleLogin,
togglePasswordVisibility,
togglePopMode,
2025-08-21 09:41:46 +09:00
};
};