ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [리액트/자바스크립트] S3파일 다운로드
    web 2023. 4. 2. 21:00

    음악 관련 프로젝트를 진행하면서 파일을 다운로드해야하는 기능을 구현해야하는 상황, 음악 파일을 다운받아서 재가공할 수 있는 점이 가장 큰 기능 중 하나였기 때문에 반드시 파일 다운로드 기능을 구현해야했다. 

     

    하.지.만.

    정말 몇 날 며칠을 해보아도 해결하지 못했다. 구글링해서 나오는 코드들을 그대로 따라쳐보아도 성공한 적이 없었다. 다운로드를 해도 빈 껍데기 파일만 다운되거나 하는 식이었다. 결국 그대로 따라치는 코드들은 소용이 없었고, 검색해서 나온 모든 글을 다 섭렵하며 이해를 하고 나서야 코드를 짤 수 있었다... 

     

    우선 전체 코드는 다음과 같다.

     

    버튼이 위치한 컴포넌트 

      const { data: fileLink } = useQuery(["beatId", download], () => getFileLink(state), {
        refetchOnWindowFocus: false,
        retry: 0,
        onSuccess: (data) => {
          if (download) {
            let blob = new Blob([data?.data], { type: "audio/mpeg" });
            let url = window.URL.createObjectURL(blob); //s3링크
    
            setLink(url);
            var a = document.createElement("a");
            a.href = url;
            a.download = `${trackInfoData?.title}`;
            document.body.appendChild(a);
            a.click();
            setTimeout((_: any) => {
              window.URL.revokeObjectURL(url);
            }, 60000);
            a.remove();
            setDownload(false);
          }
        },
        onError: (error) => {
          console.log(error);
        },
      });
    
    
    return (
    	<DownloadBtnIcon onClick={getFile} />
    )

    링크 다운로드 함수

    export async function getFileLink(beatId: number) {
      const data = await axios.get(`${process.env.REACT_APP_BASE_URL}/tracks/${beatId}/download`, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${getCookie("accessToken")}`,
        },
      });
      const res = await axios.get(data.data.data.wavFile, {
        responseType: "blob",
      });
      return res;
    }

     

    여기서 getFileLink라는 링크 다운로드 함수는 서버와의 통신을 S3 url 링크를 얻는 함수이다. 서버에서 get을 해온 데이터에서 data.dat.dat.wavFile이 S3 url 링크이다. 항상 여기까지만 코딩을 했었기 때문에 다운로드를 하면 빈 껍데기가 왔었다. 그래서 여기서 중요한 부분이 바로 이 코드이다. 

    const res = await axios.get(data.data.data.wavFile, {
      responseType: "blob",
    });

    얻어진 S3 url링크를 blob형태로 다시 get하는 코드이다. 이 코드가 매우 중요한 이유는 html에서 사진이나 파일은 그 자체로 읽어들일 수 없어 blob형태로 변환해주어야하기 때문이다. 바로 이 코드가 S3링크를 blob형태로 변환하는 코드이다. 

     const { data: fileLink } = useQuery(["beatId", download], () => getFileLink(state), {
        refetchOnWindowFocus: false,
        retry: 0,
        onSuccess: (data) => {
          if (download) {
            let blob = new Blob([data?.data], { type: "audio/mpeg" });
            let url = window.URL.createObjectURL(blob); //blob화된 s3링크

    이렇게 res를 리턴해주었기 때문에, 버튼이 위치한 컴포넌트에서 받은 data가 res라는 이름으로 리턴한 파일blob이다. 여기서 if(download)는 필자가 임의로 다운로드 버튼을 클릭했는지 안했는지를 확인하는 state이다. 처음에는 적용해주지 않았는데, 적용을 안 하니 페이지에 접속하자마자 마구마구 자동 다운로드가 되는 현상이 발생하여서... download가 true일 때에만 진행되도록 했다. 내가 맡은 프로젝트에서는 wav나 mp3와 같은 오디오 파일을 다운받는 기능이었기 때문에 타입을 audio/mpeg로 설정해주었다. 이 타입의 경우는 이미지나 다른 형태일 경우에는 타입이 다르다. 무엇보다도 처음에는 audio/wav, audio/mp3로 작성했었는데, S3 url에 접속해서 타입을 확인해보니 오디오 형태의 파일 타입은 audio/mpeg이라는 것을 알게 되었다. 

    	setLink(url);
            var a = document.createElement("a");
            a.href = url;
            a.download = `${trackInfoData?.title}`;
            document.body.appendChild(a);
            a.click();
            setTimeout((_: any) => {
              window.URL.revokeObjectURL(url);
            }, 60000);
            a.remove();
            setDownload(false);

    아래의 코드 내용은 위와 같은데, 위의 내용은 html a 태그를 만드는 과정이다. 원래는 이렇게 createElement를 하지 않고 download 버튼 자체를 a태그로 만들었었는데, 그렇게 하니 처음 다운로드는 잘 되지만 이후 다운로드부터는 껍데기파일이 다운되고 실제 파일이 다운되는 방식으로 다운로드 버튼을 두 번 눌러야 다운로드가 되는 버그가 발생했다. 이렇게 직접 a태그를 만들어주면 a태그를 remove하는 과정이 포함되기 때문에 이러한 버그가 발생하지 않는다. 

     

    위의 코드는 말 그대로 createElement로 a채그를 만들고 blob화된 S3링크를 href 경로로 잡아준다. 위의 trackInfoData.title은 다운로드받을 때 파일의 이름을 의미한다. 모두 다 다른 파일인데 같은 이름으로 다운받아지는 것보다 각 파일마다의 고유 이름으로 다운받아지는 것이 더 좋다고 생각해서 다음과 같이 제목을 설정하였는데, 이 부분은 본인이 원하는 방식으로 제목을 설정하면된다. 그리고 a태그를 바디에 추가한뒤 클릭되고, 파일 다운로드가 진행되고도 남았을 6초 뒤에 remove를 한다. 또한 앞서 다운로드 버튼을 클릭했을 때 download state가 true가 되기 때문에 다운로드 완료 후에는 state를 false로 변경해준다. 

     


    정말 고군분투하면서... 다운로드를 구현했는데, 사실상 blob로 변경해주는 axios.get 코드 3줄이 없어서 그동안 그렇게 고생한 것이라니... 정말 코딩은 단 한 줄이라도 명확하고 정확하게 짜야하는구나하는 생각이 들었다. 

    댓글

Designed by Tistory.