본문 바로가기
자바스크립트 궁금증

a태그 사용한 첨부파일 다운로드 방법

by 띠리에이터 2024. 7. 5.

프로젝트를 진행하던 중 관리자 페이지에서 첨부파일을 다운로드하는 기능을 구현해야했다. 

자바스크립트의 a태그에는 download속성이 있다. 

<span className={styles.file}>
  {list.files?.length > 0
    ? list.files.map((file, index) => (
      <a key={index} 
      	href={file.fileUrl} 
        download={file.fielName} 
        rel="noreferrer">
        {file.fileName}
      </a>
    ))
      : "첨부파일 없음"}
</span>

 

서버에서 받아오는 file의 이름을 a태그로 감싸고 a태그 속성에 href를 받아오는 파일의 url로 설정하고 download 적용해주면 다운로드가 될 줄 알았지만 단순히 file의 url링크로만 들어가지는 문제였다.

 

검색해보니 a태그의 download속성은  동일출처 정책에 의해 다운로드 파일의 경로가 현재 파일의 경로와 동일해야 적용된다는 것! 

그니까 예를 들면 현재 사용되는 웹페이지의 url이 ' https://example.com ' 이면 다운로드 할 파일의 경로는 

' https://cdn.example.com/files/abc.pdf ' 이렇게 되어야 download속성이 적용되는 것이었다.

 

현재 진행하고 있는 프로젝트에서 file url은 aws의 s3에서 받아오는 것이라 url이 'https://aws어쩌고 ' 이러한 경로라 안되는 것이었다. 

 

하지만 click이벤트를 사용해 다운로드 할 수 있는 방법이 있었다. 

 

export const useFileDownload = () => {

};

일단 다른 곳에서 사용할 수 있도록 커스텀 훅으로 만드려고 use를 설정해주고 

 

export const useFileDownload = () => {
  const fileDownload = async (url: string, name: string) => {
    const res = await fetch(url);
    //1. 받아오는 파일에서 url을 가져오고
    
    const arrayBuffer = await res.arrayBuffer();
    //2. fetch로 데이터를 가져온 후 arrayBuffer로 변환
    
    const blob = new Blob([arrayBuffer], { type: "application/octet-stream" });
    // 3. ArrayBuffer를 사용하여 Blob 객체를 생성.
    
    const blobURL = URL.createObjectURL(blob);
    //4. Blob 객체를 위한 객체 URL을 생성.
    
    const a = document.createElement("a");
    // 5. 다운로드를 위한 <a> 태그를 동적으로 생성.
    
    a.href = blobURL;
    //6. <a> 태그의 href 속성에 Blob URL을 설정.
    
    a.style.display = "none";
    if (name && name.length) {
      a.download = name;
    }
    // 8. 다운로드할 파일의 이름(name)이 제공된 경우, download 속성을 설정.

    document.body.appendChild(a);
    //9. a태그를 바디에 추가하고 
    a.click();
    //10. 클릭하여 다운로드 후 
    a.remove();
    //11. 다운로드 끝나면 remove실행
  };
  return { fileDownload };
  //fileDownload함수를 객체로 반환한다. 
};

 

ArrayBuffer는 데이터를 메모리에 효울적으로 저장하고 다룰 때 사용된다. 주로 네트워크 통신에서 데이터를 읽어오거나 클라이언트측에서 바이너리데이터를 조작할 때 사용, 

 

 

Blob은  이진 데이터를 나타내는 javaScript의 객체이다. 보통 이미지 파일은 바이너리 데이터로 구성되기 때문에 이미지를 다룰 때 클라이언트에서는 blob객체를 사용한다.

mine타입을 지정해 데이터의 종류를 브라우저에 알려주는데 

{type: "application/octet-stream}

내가 사용한 이 타입은 특정한 파일 형식을 지정하지 않고 단순히 다운로드 할 바이너리 데이터에 적용할 때 사용한다. 

다운로드 할 파일은 이미지, 동영상, pdf등 다양한 타입이 올 수 있기 때문! 

 

const { fileDownload } = useFileDownload();


onClick={(e) => {
	e.preventDefault();
	fileDownload(file.fileUrl, file.fileName);
}}

 

그리고 사용할 곳에서 useFileDownload훅을 불러와 fileDownload를 할당해주고

fileDownload 인자로 파일의 url과 name을 받아주면

pdf형태의 파일이라도 적용이 되고 

파일 이름을 클릭했을 때 다운로드도 잘 되는 것을 확인할 수 있다. 


2024.07.20 추가)

 

  • 로컬 http -> 서버 http
  • 로컬http -> 서버 https 이 두가지 경우에는 모두 fetch함수를 사용하지 않고도 a태그의 download속성만 사용해서 다운로드가 잘 되었는데 
    프론트에서 서버로 쿠키가 잘 전달되지 않아 로컬을 https 로 변경한 후 로컬 https -> 서버 https, a태그 download속성만을 사용해서 첨부파일을 다운로드 하면 에러가 발생했다. 

cors 에러인데 해결했던 방법은 단순히 서버에서 저장소를 디스크로 바꾸니까 https -> https에서도 a태그 download속성만을 사용해도 다운로드가 잘 되었다.  

 

access -control- allow-origin 을 *로 설정해줬어야하나?

근데 서버 디스크는 뭐가 다르길래 되었던 걸까? (찾아봐야함)