Projects

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

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

 

'Projects' 카테고리의 다른 글

[모모] 라이트하우스 성능 개선기  (0) 2023.10.25
[react-query] observer API와 react-query로 무한스크롤 구현하기  (4) 2023.08.31
[ESLint, Prettier, Git Hook, Husky] 팀원들과 코드 스타일을 맞추기 위해 설정하면 좋은 툴  (0) 2023.08.27
[모모] React-Query의 캐싱기능으로 api요청을 줄여보자  (6) 2023.08.10
'Projects' 카테고리의 다른 글
  • [모모] 라이트하우스 성능 개선기
  • [react-query] observer API와 react-query로 무한스크롤 구현하기
  • [ESLint, Prettier, Git Hook, Husky] 팀원들과 코드 스타일을 맞추기 위해 설정하면 좋은 툴
  • [모모] React-Query의 캐싱기능으로 api요청을 줄여보자
Summer.dev
Summer.dev
프론트엔드 개발자 Summer 입니다! 피드백은 언제나 환영입니다.
Summer.dev
꾸준함이 무기
Summer.dev
전체
오늘
어제
  • 분류 전체보기
    • Projects
      • Next.js board-project
      • MOMO
    • 원티드
    • 우테코 프리코스
    • JavaScript
    • React
    • TypeScript
    • Node.js
    • Algorithm
      • 코플릿
      • 개념정리
    • 네트워크
    • 오류해결
    • 회고
    • 기술면접준비
    • git,github
    • 소소하게 궁금한것
    • Next.js Beta Docs 번역
    • 디자인패턴
    • 트러블슈팅
    • 번역

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 메모이제이션
  • 알고리즘

최근 댓글

최근 글

hELLO · Designed By 정상우.
Summer.dev
[모모] 리액트에서 파일 업로드하기(사용자 프로필 사진 바꾸기)
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.