[react] S3 이미지 url 다운로드 (with cors)

woohyuk·2023년 11월 3일
0
post-thumbnail

회사 프로젝트에서 s3에 업로드 되어있는 이미지 url을 서버로부터 받아서 화면에 뿌려준뒤
다운로드 버튼을 클릭하면 해당 이미지를 다운로드 받는 기능을 구현하고 있었다.

보통 이미지를 다운받는 코드라 하면 아래와같이 간단하게 구현할 수 있다.

<a href = "/이미지 경로" download="파일이름.jpg">이미지 다운로드 버튼</a>

또는

const handleDownloadClick = (url: string) => {
 const aTag = document.createElement("a");

  aTag.href = url;
  aTag.download = "파일이름.jpg";

  aTag.click();
}

문제점

하지만 보안 이슈로 인하여 2018년 말부터 다운로드할 리소스가 동일한 출처 또는 동일한 서버에서 제공되지 않은 경우
링크를 클릭해도 다운로드가 트리거되지 않고 해당 이미지 주소로 이동만 될 뿐이였다.

이를 해결하는 방법으로 s3 url을 blob URL형식으로 바꿔주면 된다.

const s3ImageURL = "https..."

const handleDownloadClick = (url: string) => {
    fetch(s3URL, {
      method: "GET",
    })
      .then((res) => {
        return res.blob();
      })
      .then((blob) => {
        const blobURL = URL.createObjectURL(blob);

        const aTag = document.createElement("a");

        aTag.href = blobURL;
        aTag.download = "image.jpg";

        aTag.click();
      })
      .catch((e) => {
        console.error(e);
      });
  };

cors 에러

이미지 주소에 요청을 하였을때 s3 버킷에 따로 cors 관련 처리를 해주지 않았다면
cors 에러가 나타날 것이다.

이를 해결하는 방법은 다음과 같다.

1. 이미지가 저장된 버킷의 권한 페이지로 이동후 맨밑에 CORS 섹션에서 편집을 누른다

2. cors 설정

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "HEAD",
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

이 예시에서는 origins 설정을 모든 출처 접근 허용인 "*"로 설정하였지만 원하는 페이지 주소로 따로 설정을 하면된다.

여전히 발생되는 문제

하지만 여전히 cors 에러가 사라지지 않고 나타나고있었다..
찾아보니 safari나 다른 브라우저에서는 정상 작동하지만 chrome에서만 나타나는 문제였다.

그래도 해결은 해야하니 방법을 찾아보던중 cloudfront을 사용하여 cors를 우회하는 방법을 택했다.

cloudfront 설정

1. cloudfront에 배포 생성 클릭 후 원본도메인 선택

연결할 s3 버킷을 찾아서 선택해주면 된다.

2. 원본 요청에 CORS-S3Origin 선택

밑으로 내리면 기본 캐시 동작 섹션이 보이는데
섹션안에 캐시 키 원본 요청에서 원본 요청 정책을 CORS-S3Origin 으로 바꿔주면 된다.

그리고 배포 생성을 해준뒤 코드에서 요청주소를 s3에서 cloudfront로 바꿔주면 된다.
(배포가완료되면 배포id가 생성이 되는데 클릭하면 배포 도메인 이름을 확인할 수 있다.)

const cloudfrontImageURL = "https..."

const handleDownloadClick = (url: string) => {
    fetch(cloudfrontImageURL, {
      method: "GET",
    })
      .then((res) => {
        return res.blob();
      })
      .then((blob) => {
        const blobURL = URL.createObjectURL(blob);

        const aTag = document.createElement("a");

        aTag.href = blobURL;
        aTag.download = "image.jpg";

        aTag.click();
      })
      .catch((e) => {
        console.error(e);
      });
  };

그러면 성공적으로 요청이 완료된뒤 이미지 저장도 작동하는것을 확인할 수 있다!

profile
기록하는 습관을 기르자

0개의 댓글