[React+Typscript] 파일다운로드 기능 구현(With. Springboot)

anonymous-planet·2022년 6월 21일
2

React+Typescript

목록 보기
5/6

저의 포스팅에서는 자세한 지식보다는 사용법 위주로 작성되며, 개인적인 내용을 포함하고 있습니다!

이번 포스팅은 파일 업로드/다운로드 중 파일 다운로드를 할 예정이다.

1. 흐름

업로드 할 파일 선택 > 업로드 버튼 클릭 > 파일 업로드 * 2 > 2가지 유형의 다운로드 버튼 클릭

2. 구현

2.1. 구현할 기능

  • 방법1 파일 다운로드

    • herf속성에 직접 파일 다운로드가 가능한 API주소 삽입
  • 방법2 파일 다운로드

    • Axios를 이용해서 파일 다운로드

파일다운로드 방법을 2가지로 해보았다.
href속성의 직접 파일 다운로드가 가능한 API주소를 넣는 방법과
Axios를 사용해서 파일데이터를 받아오고 다운 받는 방법
아마도 후자의 방법을 더 많이 사용할것같지만 이런것도 있구나 하고 보고 넘어가면 좋을것 같다.
후자의 방법을 더 사용할것 같은 이유는 API의 접근을 위해서 Header에 인증 정보를 포함해야한다던지 하는 경우가 많을 것 같기 때문이다.
(두가지 방법 모두 Back-End API는 같은 걸 사용했다.)

2.2. 소스

2.2.1. FileDownForm.tsx

import { axiosDefaultInstance } from 'apis';

/**
 * 다운로드 컴포넌트
 * 방법1. href 속성에 직접 파일을 다운로드 받을 수 있는 api endpoint를 삽입한다.
 * 방법2. Axios를 이용해서 다운로드 받을 파일 데이터를 요청하고, a태그를 만들고 href속성에 파일 정보를 넣고 해서 다운을 받는다.
 * 
 * (방법1)이 (방법2) 보다 간단해 보인다.
 * 하지만 (방법2)로 밖에 못하는 경우가 있을 수 있다고 생각한다.
 * 예를 들어서 axios instance를 반드시 이용해서 API요청이 필요한 경우다.
 * (axios instance header에 로그인 정보 같은게 있는경우 랄까... 이부분은 확실치 않다 개인적인 생각이라) 
 * @param param0 
 * @returns 
 */
export default function FileDownForm({fileItems} : {fileItems:Array<FileItem>}) {

  const downloadHandler = async (event:React.MouseEvent<HTMLButtonElement, MouseEvent>, idx:number) => {
    // 파일 데이터 조회(!중요! responseType에 blob으로 준다, arryBuffer을 주는 방법도 있는것 같다.)
    // !!responseType을 설정해주는 위치를 헷갈리지 말자(Header에 주는게 아니다)!!
    const axiosResponse = await axiosDefaultInstance.get(`/files/download/${idx}`,{"responseType" : "blob"});

    // a태그 Element를 생성해준다.
    const aElement = document.createElement('a');
    // 위에서 생성한 aElement변수에 href속성에 넣을 데이터를 설정해준다.
    const blobFile = window.URL.createObjectURL(new Blob([axiosResponse.data]));
    aElement.href = blobFile;
    
    // !중요! 파일명 설정 부분이다.
    // download속성에는 확장자를 포함한 파일명 값이 들어간다.
    // 서버에서 응답데이터를 binary 파일 데이터만 줄것이다.
    // 파일명을 설정해야 할텐데 파일명은 Header의 값중 content-disposition에 들어있다.(없다면 백엔드개발자에게 넣어 달라고하자)
    // content-disposition에 undefined로 나올 경우 서버쪽에서 해당 Header에 접근 할수 있도록 설정을 해줘야한다.(CORS)
    // contentDisposition = attachment; filename=test.png
    const contentDisposition:string = axiosResponse.headers['content-disposition'];
    if(contentDisposition) {
      // X이부분은 참고하지말자 너무 위험하다..X
      const filename = contentDisposition.split(';')[1].trim().split('=')
      // 파일명 설정
      aElement.download = filename[1];
    }

    // document body 위에 생성한 a태그를 부착 시킨다.
    document.body.appendChild(aElement);

    // 부착 시킨 a태그를 Click!(이벤트 발생 시키기) 그러면 다운로드가 된다.
    aElement.click();
    setTimeout(() => {
      // 이제 더이상 필요 없으니 생성한 a태그를 1초후 삭제 시켜준다.
      aElement.remove();
    }, 1000);
  }

  return (
    <>
      <table>
        <thead>
          <tr>
            <th>No</th>
            <th>원본 파일명</th>
            <th>확장자</th>
            <th>파일용량(KB)</th>
            <th>다운로드</th>
          </tr>
        </thead>
        <tbody>
          {fileItems.map(item => (
            <tr key={item.idx}>
              <td>{item.idx}</td>
              <td>{item.originalFilename}</td>
              <td>{item.fileExtension}</td>
              <td>{item.fileSize/1024}</td>
              {/* 왼쪽 : 방법1, 오른쪽 : 방법2 */}
              <td>{item.idx%2 ? <a href={`http://localhost:9999/files/download/${item.idx}`} download>다운로드</a> : <button onClick={(e) => {downloadHandler(e, item.idx)}}>다운로드</button>}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  )
}

2.3. 결과 화면

3. 후기

파일다운은 많이 받아봤어도 구현 해본적은 몇번 없어서 많이 헤맸던것 같다.
Axios를 이용했을 때 responseType을 Header에 줘야하는걸로 알았어서 헤메고, 파일 이름 갖고오는 Header의 있는 content-disposition값을 front에서 접근하기 위해서는 Back-End에서 설정해줘야 하는지도 모르고 살았다.. 본캐가 Back-End인데 많이 부끄럽다... 앞으로라도 모르고 쓰는 습관을 조금씩 버리도록 노력해야겠다.

profile
취미로 Front-End를 즐기는 Back-End개발자 입니다.

0개의 댓글