"use client"; import { useState, useCallback, useEffect } from "react"; import { ProfileFormData, ProfileModalState } from "@/types/profile"; import { LAYOUT_CONFIG, MESSAGES } from "@/constants/layout"; import { apiCall } from "@/lib/api/client"; // 알림 모달 상태 타입 interface AlertModalState { isOpen: boolean; title: string; message: string; type: "success" | "error" | "info"; } /** * 프로필 관련 비즈니스 로직을 관리하는 커스텀 훅 */ export const useProfile = (user: any, refreshUserData: () => Promise, refreshMenus?: () => Promise) => { // 상태 관리 const [modalState, setModalState] = useState({ isOpen: false, formData: { userName: "", email: "", deptName: "", positionName: "", locale: "", }, selectedImage: "", selectedFile: null, isSaving: false, }); // 알림 모달 상태 const [alertModal, setAlertModal] = useState({ isOpen: false, title: "", message: "", type: "info", }); // 부서 목록 상태 const [departments, setDepartments] = useState< Array<{ deptCode: string; deptName: string; }> >([]); // 알림 모달 표시 함수 const showAlert = useCallback((title: string, message: string, type: "success" | "error" | "info" = "info") => { setAlertModal({ isOpen: true, title, message, type, }); }, []); // 알림 모달 닫기 함수 const closeAlert = useCallback(() => { setAlertModal((prev) => ({ ...prev, isOpen: false })); }, []); // 부서 목록 로드 함수 const loadDepartments = useCallback(async () => { try { const response = await apiCall("GET", "/admin/departments"); if (response.success && response.data) { setDepartments(response.data as Array<{ deptCode: string; deptName: string }>); } } catch (error) { console.error("부서 목록 로드 실패:", error); } }, []); /** * 프로필 모달 열기 */ const openProfileModal = useCallback(() => { if (user) { console.log("🔍 프로필 모달 열기 - 사용자 정보:", { userName: user.userName, email: user.email, deptName: user.deptName, locale: user.locale, }); // 부서 목록 로드 loadDepartments(); setModalState((prev) => ({ ...prev, isOpen: true, formData: { userName: user.userName || "", email: user.email || "", deptName: user.deptName || "", positionName: user.positionName || "", locale: user.locale || "KR", // 기본값을 KR로 설정 }, selectedImage: user.photo || "", selectedFile: null, isSaving: false, })); } }, [user, loadDepartments]); /** * 프로필 모달 닫기 */ const closeProfileModal = useCallback(() => { setModalState((prev) => ({ ...prev, isOpen: false, })); }, []); /** * 프로필 폼 데이터 변경 */ const updateFormData = useCallback((field: keyof ProfileFormData, value: string) => { setModalState((prev) => ({ ...prev, formData: { ...prev.formData, [field]: value, }, })); }, []); /** * 이미지 선택 처리 */ const selectImage = useCallback( (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { // 파일 크기 검사 if (file.size > LAYOUT_CONFIG.FILE_UPLOAD.MAX_SIZE) { showAlert("파일 크기 오류", MESSAGES.FILE_SIZE_ERROR, "error"); return; } // 이미지 파일인지 확인 if (!file.type.startsWith("image/")) { showAlert("파일 형식 오류", MESSAGES.FILE_TYPE_ERROR, "error"); return; } // 선택된 파일 저장 setModalState((prev) => ({ ...prev, selectedFile: file, })); // 미리보기 이미지 생성 const reader = new FileReader(); reader.onload = (e) => { setModalState((prev) => ({ ...prev, selectedImage: e.target?.result as string, })); }; reader.readAsDataURL(file); } }, [showAlert], ); /** * 이미지 삭제 */ const removeImage = useCallback(() => { setModalState((prev) => ({ ...prev, selectedImage: "", selectedFile: null, })); // 파일 input 초기화 const fileInput = document.getElementById("profile-image-input") as HTMLInputElement; if (fileInput) { fileInput.value = ""; } }, []); /** * 프로필 저장 */ const saveProfile = useCallback(async () => { if (!user) return; setModalState((prev) => ({ ...prev, isSaving: true })); try { // 선택된 이미지가 있으면 Base64로 변환, 없으면 기존 이미지 유지 let photoData = user.photo || ""; if (modalState.selectedFile) { // 새로 선택된 파일을 Base64로 변환 const reader = new FileReader(); photoData = await new Promise((resolve) => { reader.onload = (e) => { resolve(e.target?.result as string); }; reader.readAsDataURL(modalState.selectedFile!); }); } else if (modalState.selectedImage && modalState.selectedImage !== user.photo) { // 미리보기 이미지가 변경된 경우 사용 photoData = modalState.selectedImage; } // 사용자 정보 저장 데이터 준비 const updateData = { userName: modalState.formData.userName, email: modalState.formData.email, locale: modalState.formData.locale, photo: photoData !== user.photo ? photoData : undefined, // 변경된 경우만 전송 }; console.log("프로필 업데이트 요청:", updateData); // API 호출 (JWT 토큰 자동 포함) const response = await apiCall("PUT", "/admin/profile", updateData); console.log("프로필 업데이트 응답:", response); if (response.success || (response as any).result) { // locale이 변경된 경우 전역 변수와 localStorage 업데이트 const localeChanged = modalState.formData.locale && modalState.formData.locale !== user.locale; if (localeChanged) { // 🎯 useMultiLang의 콜백 시스템을 사용하여 모든 컴포넌트에 즉시 알림 const { notifyLanguageChange } = await import("@/hooks/useMultiLang"); notifyLanguageChange(modalState.formData.locale); console.log("🌍 사용자 locale 업데이트 (콜백 방식):", modalState.formData.locale); } // 성공: 사용자 정보 새로고침 await refreshUserData(); // locale이 변경된 경우 메뉴도 새로고침 if (localeChanged && refreshMenus) { console.log("🔄 locale 변경으로 인한 메뉴 새로고침 시작"); await refreshMenus(); console.log("✅ 메뉴 새로고침 완료"); } setModalState((prev) => ({ ...prev, selectedFile: null, isOpen: false, })); showAlert("저장 완료", "프로필이 성공적으로 업데이트되었습니다.", "success"); } else { throw new Error((response as any).message || "프로필 업데이트 실패"); } } catch (error) { console.error("프로필 저장 실패:", error); const errorMessage = error instanceof Error ? error.message : MESSAGES.PROFILE_SAVE_ERROR; showAlert("저장 실패", errorMessage, "error"); } finally { setModalState((prev) => ({ ...prev, isSaving: false })); } }, [user, modalState.selectedFile, modalState.selectedImage, modalState.formData, refreshUserData, showAlert]); return { // 상태 isModalOpen: modalState.isOpen, formData: modalState.formData, selectedImage: modalState.selectedImage, isSaving: modalState.isSaving, departments, // 알림 모달 상태 alertModal, closeAlert, // 액션 openProfileModal, closeProfileModal, updateFormData, selectImage, removeImage, saveProfile, }; };