-
리액트 파일 업로드, 파일 다운로드 File upload, download with javascript reactFront-end/React 2022. 6. 24. 17:24반응형
최근에 맡아본 업무 중 하나가 파일을 업로드하고 다운로드 할 수 있는 기능을 만드는 것이었다.
원래 어려운 것이 아닐 수 있으나... 적어도 나에게는 생각보다 복잡한 과정을 거쳐야 했으므로 정리를 해두고 싶은 생각이 들었다.
그리고 이런 것들을 이해 할 수 있는 이론 지식을 가져야겠다는 마음도 함께 생겼다.
나에게 주어진 옵션은 이러했다.
1. 파일은 어떤 종류의 확장자던지 올릴 수 있어야한다.
2. 업로드 하는 파일의 크기는 50MB를 넘어가서는 안된다.
3. 파일 이름 + 파일크기가 나와야하고, 파일 크기는 사이즈에 따라서 convert되어야 한다.
4. 드래그앤드롭으로 파일을 넣을 수 있어야한다.
나는 이런 옵션을 가지고 개발을 시작했다.
(** 추가 : 파일업로드, 다운로드에 초점을 맞추므로 UI상에서 보이는 파일 사이즈 convert와 같은 것은 내용을 피했다.)
이미 나와있는 디자인이 명확했기에 UI를 간단하게 먼저 만들었다.
파일은 크게 메인인 FileElement.tsx를 만들었고, 파일 올리기 전인 BeforeUploadFile.tsx, 파일업로드 로딩 중일때 쓰이는 LoadingUploadFile.tsx, 마지막으로 파일이 업로드 되었을 때 파일명과 크기까지 나오는 AfterUploadFile.tsx를 만들었다.
enum으로 Normal, Loading, Completed, Error를 만든 뒤에 각각에 맞춰서 파일을 나오게 할 생각이다.
이렇게 BeforeUploadFile.tsx UI를 만들었으니 이제 클릭했을 때 업로드 할 수 있도록 만들면 되겠다.
나는 MUI를 사용하고 있으므로 그 기준으로 말하겠지만 div태그 등으로 바꿔도 괜찮을것이다.
우선 가장 바깥 쪽에 Box 태그를 만들고 onDragOver, onDragEnter, onDragLeave, onDrop을 넣었다.
return ( <Box onDragOver={(e) => preventEvent(e)} onDragEnter={(e) => preventEvent(e)} onDragLeave={(e) => preventEvent(e)} onDrop={(e) => this.handleFileDrop(e)} > ... </Box> )
그리고 preventEvent, handleFileDrop으로 이벤트를 전달해주었다.
preventEvent는 단순히 똑같은 것을 반복하기 싫어서 만든 preventDefault와 stopPropagation을 합쳐놓은 함수이다.
이를 util.ts라고 만들어서 넣어두었다.
import React from 'react'; export const preventEvent = (e: React.SyntheticEvent) => { e.preventDefault(); e.stopPropagation(); };
handleFileDrop은 무엇일까?
이는 파일이 올라간 것을 감지하고 파일이 하나가 올라갔을 때 다음으로 이어지게만들며 파일이 없거나 여러개가 전달 될 경우 error message를 만들어낼 것이다.
handleFileDrop(e: React.DragEvent) { preventEvent(e); if (this.state.status !== FileUploadStatus.Loading) { const { files } = e.dataTransfer; if (files && files.length === 1) { this.handleCheckFile(files[0]); } else { // TODO > 선택된 파일이 없거나 여러개가 전달됨 } } }
계속해서 this.handleCheckFile로 이어가보면, 이곳에서는 Blob을 Buffer형식으로 만들어야한다.
[ Blob에 대해서 알고 싶다면?! ]
더보기비전공자로써, 그리고 공부를 해가는 주니어로써 모르는 것이 나올 때마다 그 때 그 때 익혀놓는 것이 좋다고 생각한다.
개인적으로 Blob이라는 형식을 들어보기는 했지만 정확하게 뭘 말하는지 몰랐는데 이번에 개발을 하면서 어떤 모양인지 확실하게 알게 된 것 같다.
** Blob 이란?
블롭(혹은 블랍)은 Binary Large Object를 말하며 멀티미디어 데이터(image, video etc.) 데이터를 다룰 때 유용하게 사용 될 수 있다.
아래 이미지는 나의 puppuy.jpg를 넣었을 때 나오는 Blob이다.
그 내용 정보를 보면 데이터 크기, MIME 타입 등을 사용하는데도 유용하다는 것을 알 수 있다.
그래서 아까전에 preventEvent가 있던 util.ts에 blobToDataUrl이라는 함수를 하나더 만들어주겠다.
import React from 'react'; export const blobToDataUrl = (blob: Blob) => { return new Promise<string | ArrayBuffer | null>((resolve) => { const fileReader = new FileReader(); fileReader.onload = () => resolve(fileReader.result); fileReader.onerror = () => resolve(null); fileReader.readAsDataURL(blob); }); }; export const preventEvent = (e: React.SyntheticEvent) => { e.preventDefault(); e.stopPropagation(); };
앞서 Blob에 대해서 설명한바 있는데, 그 Blob을 blobToDataUrl이라는 함수에 파라미터로 넣어주겠다.
new FileReader()라는 것이 있는데, 이를 통해서 클라이언트단에서 file과 blob을 사용 할 수 있도록 해준다.
이는 비동기적으로 데이터를 읽기 위해 파일 혹은 Blob 객체를 이용하여 파일의 내용을 버퍼로 읽고 사용자의 컴퓨터에 저장하게 도와준다.
fileReader 객체를 생성한 후 이용하는 메서드로는 readAsText, readAsDataURL, readAsArrayBuffer, readAsBinaryString이 있다.
나의 경우 백엔드에 보낼 때는 buffer로 보내지만, client 상에서는 파일명, 사이즈를 보여줘야하므로 앞서 blobToDataUrl에서 올바르게 resolve 되었다면 이제 따로 파일명, 사이즈 등을 빼놓았다.
그리고 변환에 실패했을 때를 if () else ()로 처리했다.
그렇게 하면 api 통신을 통해서 파일을 올릴 수 있을 것이고, 위와 같은 이미지가 나타나게 된다.
우리 회사의 경우 s3를 이용해서 관리를 하고 있으므로 s3에 가보면 내가 올린 파일들을 볼 수 있다.
이제 다운로드를 해보면 되겠다.
이는 오히려 쉽게 할 수 있을 것이다.
미리 UI를 만들어놓은 AfterUploadFile.tsx에 들어간다.
제일 최상단에 onClick을 만들고, handleDownload 함수를 만들어보자.
const handleClick = useCallback( (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); 백엔드API(id) .then(({ data: { status, data, message } }) => { if (status === 200 && data) { window.open(data.url); } }) .catch((err) => console.error(err)) .finally(() => {}); }, [id], );
다운받을 수 있는 방식은 두 가지가 있다.
하나는 버퍼를 받는 것, 또 하나는 url을 받는 것이다.
처음에는 버퍼를 받아서 처리를 하려고 했지만 자꾸 깨지는 파일이 오는 바람에 url을 받는 것으로 변경했다.
이렇게 백엔드API에 id를 보내어서 받은 url을 window.open해주기만 하면 쉽게 다운받을 수 있다!
그리고 당연히 catch, finally로 처리처리 해야하고요!~~
반응형'Front-end > React' 카테고리의 다른 글
TanStack Table v8 - Merge header cell (헤더 병합) (0) 2024.03.07 LocalStorage로 저장, 불러오기, 삭제 (JS, React) (2) 2023.02.05 React 상태 관리 : Recoil 로 해보자! (0) 2021.12.04 캘린더 날짜 바꾸기 - Welcome to Data structure (0) 2021.09.15 React - context API를 사용해보자! (0) 2021.09.07