diff --git a/components/UserProfile.tsx b/components/UserProfile.tsx index 714afd7..ac945a4 100644 --- a/components/UserProfile.tsx +++ b/components/UserProfile.tsx @@ -52,7 +52,7 @@ function UserProfile({ } return ( -

+

{label} {data[field]}

@@ -122,7 +122,7 @@ function UserProfile({
void) => { const [isLoading, setIsLoading] = useState(false); const [previewImage, setPreviewImage] = useState(null); const fileInputRef = useRef(null); + const { showSnackbar } = useSnackbar(); useEffect(() => { return () => { @@ -18,24 +26,55 @@ export const useProfileImage = (onImageChange: (url: string) => void) => { fileInputRef.current?.click(); }; + // 파일명 바꿔주기 + const sanitizeFileName = (file: File): File => { + // 현재 시간을 이용한 유니크한 파일명 생성 + const timestamp = new Date().getTime(); + const randomString = Math.random().toString(36).substring(7); + const extension = file.name.split('.').pop()?.toLowerCase() || ''; + const newFileName = `profile_${timestamp}_${randomString}.${extension}`; + + // 새로운 파일 객체 생성 + return new File([file], newFileName, { type: file.type }); + }; + + const validateFile = (file: File): boolean => { + const validExtensions = ['jpg', 'jpeg', 'png', 'gif']; + const extension = file.name.split('.').pop()?.toLowerCase(); + + if (!extension || !validExtensions.includes(extension)) { + showSnackbar(NOT_ALLOWED_TYPE_MESSAGE, 'fail'); + return false; + } + + if (file.size > MAX_FILE_SIZE) { + showSnackbar(SIZE_LIMIT_MESSAGE, 'fail'); + return false; + } + + return true; + }; + const handleFileChange = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; - const fileExtension = file.name.toLowerCase().split('.').pop(); - if (fileExtension === 'svg') { - alert('SVG 파일은 업로드할 수 없습니다.'); - e.target.value = ''; + // 파일 유효성 검증 + if (!validateFile(file)) { + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } return; } - const objectUrl = URL.createObjectURL(file); + const sanitizedFile = sanitizeFileName(file); + const objectUrl = URL.createObjectURL(sanitizedFile); setPreviewImage(objectUrl); setIsLoading(true); try { const formData = new FormData(); - formData.append('image', file); + formData.append('image', sanitizedFile); const response = await instance.post<{ url: string }>( '/images/upload', @@ -47,11 +86,30 @@ export const useProfileImage = (onImageChange: (url: string) => void) => { } ); onImageChange(response.data.url); - } catch (error) { - alert('이미지 업로드에 실패했습니다.'); + } catch (error: unknown) { + // 서버 에러 상세 처리 + let errorMessage = '이미지 업로드에 실패했습니다.'; + + if (error instanceof AxiosError && error.response) { + // HTTP 에러 상태 코드별 처리 + switch (error.response.status) { + case 400: + errorMessage = '잘못된 파일 형식입니다.'; + break; + case 500: + errorMessage = + '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'; + break; + } + } + + showSnackbar(errorMessage, 'fail'); setPreviewImage(null); } finally { setIsLoading(false); + if (fileInputRef.current) { + fileInputRef.current.value = ''; // 입력 필드 초기화 + } } };