Projects

[모모] 리액트에서 파일 업로드하기(사용자 프로필 사진 바꾸기)

Summer.dev 2023. 9. 6. 14:27

모모 프로젝트에서는 회원정보의 프로필사진, 닉네임, 지역, 자기소개 내용을 수정할 수 있습니다.

이 중에 프로필 사진 변경 (사용자가 파일을 직접 업로드하는 경우) 기능을 포스팅해 보겠습니다.

 


우선 사용자는 input에 파일을 업로드할 수 있어야 합니다.

              <ImgEditButton>
                <label className="editLabel" htmlFor="file-input">
                  <img src={imgEdit} alt="img-edit-button" />
                </label>
                <ImgEditInput
                  id="file-input"
                  accept="image/png, image/gif, image/jpeg"
                  type="file"
                  onChange={handleImgChange}
                />
              </ImgEditButton>

type 속성은 "file"로 설정해 줍니다.

accept 속성은 허용할 확장자 명을 입력해 줍니다.

 

💡 파일 업로드 하는 부분을 못생긴 input창이 아닌 이미지를 보여주고 싶다면 <lable> 안에 위치시키면 됩니다.

💡 만약 여러 개의 파일을 업로드하고 싶으시면 multiple 속성을 추가해 주시면 됩니다.

 <input type="file" multiple />

input에서 파일을 업로드하여 변경이 일어나면

이미지를 변경하는 함수 handleImgChange를 실행시켜줍니다.

 

다음은 handleImgChange 함수에 대한 내용입니다.

프로젝트에서는 사용자가 프로필 사진 변경 시

수정 완료 요청을 보내기 전에

수정 페이지에서 업로드된 사진을 미리 보기 할 수 있게 구현하였습니다.

//handleImgChange
const input = event.target;
    if (input.files) {
      const file = input.files[0]; // 파일 추출
	}

event객체의 target의 files 속성에 사용자가 업로드한 파일이 들어있습니다.

 

console.log(input.files)를 찍어보면 아래와 같이 업로드한 파일을 확인할 수 있습니다.

      const reader = new FileReader();

파일과 관련된 작업을 수행하는 FileReader 생성자 함수를 호출해 줍니다.

      reader.onload = function (e) {
        const imageDataURL = e.target?.result as string;
        setUploadedImage(imageDataURL);
      };
      reader.readAsDataURL(file);

1. onload 이벤트 핸들러함수를 정의합니다.

2. e 객체를 통해 파일의 Data URL을 얻어와서

미리 보기를 할 수 있도록 상태로 저장해 둡니다.  setUploadedImage(imageDataURL)

3. readAsDataURL 메소드로 위에서 추출한 파일을 읽고 2.로 돌아가 Data URL로 변환하고

onload 이벤트 핸들러를 호출하여 Data URL을 얻을 수 있습니다.

 

console.log(imageDataURL)을 찍어보면 아래와 같은 어마무시하게 긴 문자열들을 확인할 수 있습니다.

      setUploadedFile(file);

마지막으로 서버에 전송하기 위해 파일을 상태로 저장해 줍니다.

 

//userEdit.tsx

  const [uploadedImage, setUploadedImage] = useState<string>('');
  const [uploadedFile, setUploadedFile] = useState<File | null | string>(null);
  
    const handleImgChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const input = event.target;
    if (input.files) {
      const file = input.files[0]; // 파일 추출

      const reader = new FileReader();
      reader.onload = function (e) {
        const imageDataURL = e.target?.result as string;
        setUploadedImage(imageDataURL);
      };
      reader.readAsDataURL(file); // 추출한 파일을 읽어 데이터 URL로 변환

      setUploadedFile(file); // 파일 상태 변수에 저장
    }
  };
  
  // 중략
  
              <ImageContainer>
              {uploadedImage ? (
                <ProfileImage src={uploadedImage} />
              ) : (
                <ProfileImage src={myData?.profileImage || profile} />
              )}
              <ImgEditButton>
                <label className="editLabel" htmlFor="file-input">
                  <img src={imgEdit} alt="img-edit-button" />
                </label>
                <ImgEditInput
                  id="file-input"
                  accept="image/png, image/gif, image/jpeg"
                  type="file"
                  onChange={handleImgChange}
                />
              </ImgEditButton>
            </ImageContainer>

서버에 patch 요청하는 함수는 다음과 같습니다.

data.memberPatchDto는 문자열 값을 가지는 유저정보 객체이고

data.file은 유저가 업로드한 이미지 파일입니다.

export const patchMyDataImg = async (url: string, data: EditMember) => {
  const formData = new FormData();
  if (data.file) {
    formData.append('image', data.file);
  }

  const jsonBlob = new Blob([JSON.stringify(data.memberPatchDto)], {
    type: 'application/json',
  });

  formData.append('memberPatchDto', jsonBlob);

  const headers = {
    'Content-Type': 'multipart/form-data',
  };

  const res = await instance.patch(url, formData, { headers });
  return res.data;
};

https://developer.mozilla.org/en-US/docs/Web/API/FileReader/FileReader

 

FileReader: FileReader() constructor - Web APIs | MDN

The FileReader() constructor creates a new FileReader.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob

 

Blob: Blob() constructor - Web APIs | MDN

The Blob() constructor returns a new Blob object. The content of the blob consists of the concatenation of the values given in the parameter array.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData

 

FormData: FormData() constructor - Web APIs | MDN

The FormData() constructor creates a new FormData object.

developer.mozilla.org