2차 공부/TIL

24.09.06 개인과제 파일 input 트러블슈팅

공대탈출 2024. 9. 6. 23:48
import React, { useEffect, useState } from "react";
import { getUserData, updateUserData } from "../apis/authApi";
import { useLogin, useLogout, useUpdateUserInfo, useUserInfo } from "../zustand/useAuthStore";
import { useNavigate } from "react-router-dom";
import defaultProfile from "../asset/defaultProfile.png";
import useInput from "../hooks/useInput";

const Profile = () => {
    const navigate = useNavigate();
    const setLogout = useLogout();
    const setProfileUpdated = useUpdateUserInfo();
    const { avatar, userId, nickname } = useUserInfo();

    const getProfileData = async () => {
        try {
            const data = await getUserData();
            setProfileUpdated(data);
            console.log("data :>> ", data);
        } catch (error) {
            console.log("error :>> ", error);
            if (error.status == 401) {
                alert("토큰이 만료되어 로그아웃 되었습니다! 다시 로그인해주세요!");
                setLogout();
                localStorage.removeItem("accessToken");
                navigate("/login");
            }
        }
    };

    // const [updatedImgFile, handleUpdatedImgFile] = useInput(avatar);
    const [updatedImgFile, setUpdatedImgFile] = useState(undefined);
    const handleUpdatedImgFile = (e) => {
        setUpdatedImgFile(e.target.files[0]);
    };
    const [updatedNickname, handleupdatedNickname] = useInput(nickname);

    const [isUpdating, setIsUpdating] = useState(false);
    const toggleIsUpdating = (e) => {
        e.preventDefault();
        setIsUpdating(!isUpdating);
    };

    const updateUserInfo = async (e) => {
        e.preventDefault();
        const userFormData = new FormData();
        userFormData.append("avatar", updatedImgFile);
        userFormData.append("nickname", updatedNickname);
        try {
            const data = await updateUserData(userFormData);
            console.log("data :>> ", data);
        } catch (error) {
            console.log("error :>> ", error);
        }
    };

    useEffect(() => {
        getProfileData();
    }, []);
    return (
        <>
            {!isUpdating ? (
                <div>
                    <img src={avatar ? avatar : defaultProfile} alt="유저 프로필 이미지" />
                    <p>닉네임: {nickname}</p>
                    <p>ID: {userId}</p>
                    <button onClick={toggleIsUpdating}>수정하기</button>
                </div>
            ) : (
                <form onSubmit={(e) => updateUserInfo(e)}>
                    {/* Todo - 드래그드롭으로 이미지 직접넣기 */}
                    <img src={updatedImgFile ? updatedImgFile : defaultProfile} />
                    <label>이미지주소</label>
                    <input
                        type="file"
                        accept="image/jpeg,image/jpg,image/png"
                        value={updatedImgFile}
                        onChange={handleUpdatedImgFile}
                    />
                    <label>닉네임</label>
                    <input
                        type="text"
                        placeholder={`이전 닉네임: ${nickname}`}
                        value={updatedNickname}
                        onChange={handleupdatedNickname}
                    />
                    <p>ID: {userId}</p>
                    <div>
                        <button type="button" onClick={(e) => toggleIsUpdating(e)}>
                            취소
                        </button>
                        <button type="form">수정완료</button>
                    </div>
                </form>
            )}
        </>
    );
};

export default Profile;

개인과제 중 프로필 편집 관련 기능에서 문제가 발생했다.

파일첨부형태의 input을 아주 예전에 사용해봤었는데 기억이 나지 않아 대충 검색만 해보고 아~ type ='file'이면 되는거구나~ 했다.

이렇게 이미지와 닉네임을 수정할 수 있는 페이지인데 첨부파일 모달에서 파일을 선택하면...

끔찍한 콘솔현장

끔찍한 에러들이 날 반겨줬다....

 

문제는 이 input이었다.

HTML의 파일 입력은 특별한 방식으로 동작하여 파일 선택을 위해 파일경로를 직접설정하는 것을 허용하지 않는데, 이에따라 input의 value속성을 제어하려고 하면 오류가 발생한다는 것이다.

따라서 type='file'의 input은 value를 지정하지 않고, onchange로만 사용해야한다.

 

const handleUpdatedImgFile = (e) => {
    setUpdatedImgFile(e.target.files[0]);
};

<input type="file" accept="image/jpeg,image/jpg,image/png" onChange={handleUpdatedImgFile} />

이렇게 value를 직접 컨트롤하지않고 onChange로만 state를 변경한다.

이렇게 바꾸니 파일첨부가 잘 작동한다!

 

그리고 사용자가 등록한 파일이 어떤건지 화면에 보여주고 싶었다.

URL.createObjectURL(updatedImgFile)

파일이 auth서버에서 aws주소로 바뀐다. 해당 주소로 들어가면 해당 이미지파일이 다운로드 된다.

이러한 url형태는 위의 코드를 사용하면 src형태로 변경해준다고 한다!

 

잘된다!

import React, { useEffect, useState } from "react";
import { getUserData, updateUserData } from "../apis/authApi";
import { useLogin, useLogout, useUpdateUserInfo, useUserInfo } from "../zustand/useAuthStore";
import { useNavigate } from "react-router-dom";
import defaultProfile from "../asset/defaultProfile.png";
import useInput from "../hooks/useInput";

const Profile = () => {
    const navigate = useNavigate();
    const setLogout = useLogout();
    const setProfileUpdated = useUpdateUserInfo();
    const { avatar, userId, nickname } = useUserInfo();

    const getProfileData = async () => {
        try {
            const data = await getUserData();
            setProfileUpdated(data);
            console.log("data :>> ", data);
        } catch (error) {
            console.log("error :>> ", error);
            if (error.status == 401) {
                alert("토큰이 만료되어 로그아웃 되었습니다! 다시 로그인해주세요!");
                setLogout();
                localStorage.removeItem("accessToken");
                navigate("/login");
            }
        }
    };

    const [updatedImgFile, setUpdatedImgFile] = useState(undefined);
    const handleUpdatedImgFile = (e) => {
        setUpdatedImgFile(e.target.files[0]);
    };
    const [updatedNickname, handleupdatedNickname] = useInput(nickname);

    const [isUpdating, setIsUpdating] = useState(false);
    const toggleIsUpdating = (e) => {
        e.preventDefault();
        setIsUpdating(!isUpdating);
    };

    const updateUserInfo = async (e) => {
        e.preventDefault();
        const userFormData = new FormData();
        userFormData.append("avatar", updatedImgFile);
        userFormData.append("nickname", updatedNickname ? updatedNickname : nickname);
        try {
            const data = await updateUserData(userFormData);
            setProfileUpdated(data);
            getProfileData();
            setIsUpdating(!isUpdating);
            alert(data.message);
            console.log("data :>> ", data);
        } catch (error) {
            console.log("error :>> ", error);
        }
    };

    useEffect(() => {
        getProfileData();
    }, []);
    return (
        <>
            {!isUpdating ? (
                <div>
                    <img src={avatar ? avatar : defaultProfile} alt="유저 프로필 이미지" />
                    <p>닉네임: {nickname}</p>
                    <p>ID: {userId}</p>
                    <button onClick={toggleIsUpdating}>수정하기</button>
                </div>
            ) : (
                <form onSubmit={(e) => updateUserInfo(e)}>
                    {/* Todo - 드래그드롭으로 이미지 직접넣기 */}
                    <img src={updatedImgFile ? URL.createObjectURL(updatedImgFile) : avatar} />
                    <label>이미지주소</label>
                    <input type="file" accept="image/jpeg,image/jpg,image/png" onChange={handleUpdatedImgFile} />
                    <label>닉네임</label>
                    <input type="text" placeholder={`이전 닉네임: ${nickname}`} onChange={handleupdatedNickname} />
                    <p>ID: {userId}</p>
                    <div>
                        <button type="button" onClick={(e) => toggleIsUpdating(e)}>
                            취소
                        </button>
                        <button type="form">수정완료</button>
                    </div>
                </form>
            )}
        </>
    );
};

export default Profile;