6/13 회고 - Antd Upload+ customRequest + S3 PresignedURL

나건일·2022년 6월 13일
0

회고

목록 보기
3/4

문제들

1. Antd Upload 컴포넌트에 어떻게 다른 api로 파일을 업로드하도록 하는거지?

답은 언제나 그렇듯 대부분의 경우 api 문서를 잘 읽어보면 나온다

action에 넣어주면 해결이 가능하다

<Upload
  action={(file) => {
    //...대충 presignedURL 생성해서 return하는 내용
    ...
    return presignedURL
  }}
  method="PUT"
  ...
>
</Upload>

이런식으로 presignedURL을 넣어줬다. 기본 method는 "post"인데 presignedURL을 통해 업로드하면서 PUT으로 요청하는게 필요해서 method도 변경해주었다.

2. action으로 한건 성공, 근데 이미지 파일이 고장난다?!

presignedURL으로 정상적으로 업로드 되는것은 확인했다. 그런데 업로드한 이미지 파일을 다운로드 하면 파일이 고장났다.

Antd의 Upload 컴포넌트에서 내부적으로 업로드를 하면서 Content-type을 multipart/form-data로 요청하고 있었다. 정확히 이것 때문인지는 모르겠지만, Upload 컴포넌트 내부 동작이 우리가 원하는 상황과 맞지 않아서 문제가 생겼다. 이를 해결하기 위해 Upload 컴포넌트의 customRequest를 사용하기로 했다. 언제나 그렇듯 api 문서를 확인했는데,

"단 한줄"

https://github.com/react-component/upload/blob/master/docs/examples/customRequest.tsx

레포지토리에서 더 자세한 예시코드를 확인할 수 있었다.
(여기서는 onProgress에 parameter를 percent와 file을 넣는데, Upload 컴포넌트에는 parameter를 하나만 받을 수 있으니 file은 넣지 않아도 된다.)

<Upload
  action={(file) => {
    //...대충 presignedURL 생성해서 return하는 내용
    ...
    return presignedURL
  }}
  customRequest={async({file, action})=>{
    // file 의 type 이 string|RcFile|Blob이다.. 왜일까..
  	if(typeof file !== "string"){
    	await axios.put(action, file, {
          headers: {
            "Content-Type":file.type,
          },
        })
     }
  }
  ...
>
</Upload>

customRequest를 작성하고, 어차피 customRequest를 사용할 것이기 떄문에 method는 지워주었다.

3. 그런데... Progress는..?

이제 업로드도 잘 되고, 이미지 파일도 깨지지 않고 잘 열린다. 기본으로 Upload 컴포넌트에서 제공되던 progress가 사라져서 보이지 않는다. 이를 해결하기 위해서 다시 customRequest를 건드려 보기로 했다.

<Upload
  action={(file) => {
    //...대충 presignedURL 생성해서 return하는 내용
    ...
    return presignedURL
  }}
  customRequest={({file, action, onProgress, onSuccess, onError})=>{
    // file 의 type 이 string|RcFile|Blob이다.. 왜일까..
  	if(typeof file !== "string"){
    	axios.put(action, file, {
          headers: {
            "Content-Type":file.type,
          },
          onUploadProgress: ({ loaded, total }) => onProgress!({ percent: (loaded / total) * 100 }),
        })
      	.then((res) => onSuccess!(res))
      	.catch((e) => onError!(e));
     }
  }
  ...
>
</Upload>

axios의 option 부분에 onUploadProgress에 onProgress를 이런식으로 넣어주면 정상적으로 작동한다. (onProgress, onSuccess, onError에 !를 붙인 이유는, typescript가 해당 함수들이 Undefined일수 있다고 에러를 돌려주고 있었기 때문.. antd는 왜 이렇게 type을 해놓은 것인지 잘 모르겠다.)

물론 내 코드에는 잘 작동하지 않았다. 내가 삽질을 하고 있었기 때문

https://ant.design/components/upload/#components-upload-demo-fileList
처럼 fileList를 state로 정의해서 1개만 업로드할 수 있도록 관리하고 있었는데,

<Upload
  ...
  fileList={fileList}
  onChange={({fileList})=> {
    	const newFileList = fileList.slice(-1);
    	setFileList(newFileList);
    	...
  	}
  }
>
</Upload>

이런 부분에서 setFileList(newFileList)에 해당하는 부분을 내가 beforeUpload로 옮기고 지워버렸기 때문에 작동을 하지 않는 것이였다. 만약 이렇게 fileList를 직접 관리 하고 있다면 setFileList이 부분이 꼭 onChange안에 있어야 progress도 정상적으로 작동한다!

파일 업로드 중에 취소도 가능한가요?

가능하더라구요..?
axios 문서와 react-component upload의 문서를 참고해서 작성했다.

<Upload
  ...
  customRequest={({file, action, onProgress, onSuccess, onError})=>{
    // file 의 type 이 string|RcFile|Blob이다.. 왜일까..
  	if(typeof file !== "string"){
      	const controller = new AbortController();
      
    	axios.put(action, file, {
          headers: {
            "Content-Type":file.type,
          },
          onUploadProgress: ({ loaded, total }) => onProgress!({ percent: (loaded / total) * 100 }),
          signal: controller.signal,
        })
      	.then((res) => onSuccess!(res))
      	.catch((e) => onError!(e));
      
      return {
      	abort() {
          controller.abort();
        }
      }
     }
  }
>
</Upload>
  1. const controller = new AbortController()
  2. axios의 option에 signal을 추가해 준 뒤
  3. customRequest에 이런식으로 abort를 return해주면 된다.

참 쉽죠?

참고
https://github.com/react-component/upload/blob/master/docs/examples/customRequest.tsx

https://github.com/react-component/upload/blob/0d0ca2f65acca13f64199ffbb05054fb13c210ea/src/request.ts#L103
https://axios-http.com/docs/cancellation

profile
프론트엔드 개발자

0개의 댓글