첨부파일 다운로드 하는 기능을 개발하면서 위와 같이 생긴 UI를 만들게 되었습니다.
AWS S3에 업로드한 파일을 브라우저를 통해 다운로드하는 기능입니다.
파일 다운로드 버튼을 만들 때는 먼저 아래와 같이 HTML을 작성합니다.
<a href="파일 다운로드 경로" download="다운로드 되는 파일명">UI에 표시되는 파일명</a>
파일 다운로드 경로만 href
에 잘 넣어주면 됩니다.
이를 구현하기 위해서 당연하게도 S3 파일 경로가 필요합니다. 다음과 같이 파일 경로가 있다고 할 때
https://파일경로
처음에는 이 파일 경로 그대로 href
에 넣어보았습니다.
<a href="https://파일경로" download="다운로드 되는 파일명">UI에 표시되는 파일명</a>
파일이 다운로드 되길 기대했으나 파일이 새 탭에서 열리더군요.
href
에 들어갈 다운로드 URL을 새로 생성해주어야 합니다. 다운로드 경로는 다음과 같이 생성합니다.
const res = await fetch('https://파일경로');
const blob = await res.blob();
const downloadUrl = window.URL.createObjectURL(blob);
이렇게 얻은 downloadUrl을 href
에 넣어주면 됩니다.
<a href={downloadUrl} download="다운로드 되는 파일명">UI에 표시되는 파일명</a>
그러나 웹 브라우저에서 바로 파일경로로 요청을 보냈을 때 CORS 에러가 발생했습니다ㅠㅠ
이를 해결하기 위한 방법으로 S3에서 권한을 설정할 수도 있겠지만 DevOps팀에 요청해야하는 번거로움이 있어서 직접 프론트 코드만 수정하여 해결하고자 했습니다.
어쨌든 이 CORS 에러는 브라우저에서만 발생하기 때문에 요청을 브라우저가 아닌 서버에서 하면 될 것 같았습니다.
우리가 사용하고 있는 Next.js에서는 브라우저가 아닌 서버에서 요청을 보낼 수 있습니다.
먼저 서버 코드를 작성하겠습니다.
기존에 있는 pages > api 폴더 하위에 file.ts 라는 파일을 생성합니다. 이 파일 이름이 곧 브라우저에서 요청을 보낼 주소가 됩니다.
file.ts에 아래와 같이 작성해줍니다.
import { NextApiRequest, NextApiResponse } from 'next';
export interface FileResponse {
type: string;
arrayBuffer: number[];
}
export default async function file(req: NextApiRequest, res: NextApiResponse<FileResponse>) {
const { url } = req.query;
// 파일 경로에 요청
const rawData = await fetch(url as string);
const blob = await rawData.blob();
// 브라우저에서 Blob 데이터를 만들기 위한 정보 응답
res.status(200).send({
type: blob.type,
arrayBuffer: Object.values(new Uint8Array(await blob.arrayBuffer())),
});
}
다음으로는 브라우저에서 방금 작성한 서버로 요청을 보내겠습니다.
이때 우리가 작성한 서버로 요청을 보내게 되면 같은 출처로 요청을 보내기 때문에 CORS 에러가 발생하지 않습니다.
다음은 브라우저단에서 다운로드 URL을 얻어내는 코드입니다.
async function getFile() {
const fileUrl = 'https://파일경로';
const {
data: { type, arrayBuffer },
} = await axios.get('/api/file', { params: { url: fileUrl } });
const blob = await new Blob([Uint8Array.from(arrayBuffer)], { type });
// <a> 태그의 href 속성값으로 들어갈 다운로드 URL
const downloadUrl = window.URL.createObjectURL(blob);
}
이제 이 downloadUrl을 href에 넣어주면 됩니다.
<a href={downloadUrl} download="다운로드 되는 파일명">UI에 표시되는 파일명</a>
덕분에 살았습니다ㅠ_ㅠ 감사합니다!!!!!!