본문 바로가기
it

리액트 api axios 엑셀 다운로드 버튼 구현 react.JS filesaver.saveAs javascript blob response.headers 값에서 filename추출하기

by st공간 2025. 2. 12.

목차

    리액트 api axios 엑셀 다운로드 버튼 구현 react.JS filesaver.saveAs javascript blob response.headers 값에서 filename추출하기

    아래는 리액트 환경에서 API를 통해 엑셀 파일을 다운로드 받을 수 있도록 Axios와 FileSaver, Blob을 활용하는 방법에 대한 포스팅입니다. 이 글에서는 엑셀 다운로드 버튼 구현 과정과 함께 백엔드에서 전달받은 응답 헤더(response.headers)에서 파일 이름(filename)을 추출하는 꼼수까지 자세하게 다루도록 하겠습니다.


    리액트에서 엑셀 다운로드 기능 구현의 필요성

    리액트를 처음 접하는 개발자라면 PHP나 Angular와 같이 기존 프레임워크에서 당연하게 제공되던 기능들이 리액트에서는 제약이 있거나 다르게 접근해야 하는 부분들이 많다는 것을 느낄 수 있습니다. 그 중에서도 파일 다운로드, 특히 엑셀 다운로드와 같이 백엔드에서 생성한 파일을 그대로 클라이언트에 전달받아 저장하는 기능은 그 복잡성이 배가됩니다.

    일반적으로 백엔드에서는 엑셀 파일을 생성하여 API 응답의 blob 형태로 전달합니다. 클라이언트에서는 Axios를 통해 이 파일을 받아 FileSaver 컴포넌트를 이용해 로컬에 저장하는 방식으로 구현할 수 있습니다. 그러나 여기서 한 가지 난관은 응답 헤더(response.headers)에 포함된 파일 이름 정보를 올바르게 추출하는 문제입니다.


    Axios를 활용한 API 통신과 Blob 응답 처리

    Axios는 HTTP 요청을 보내기 위한 강력한 라이브러리로, 리액트 프로젝트에서 API 호출에 많이 사용됩니다. 엑셀 다운로드와 같은 파일 전송의 경우, 응답 데이터 형식을 blob으로 지정해야 합니다. 이를 위해 Axios 인스턴스나 개별 요청 시 responseType"blob"으로 설정합니다.

    예를 들어, 아래와 같이 API 요청을 보내면 서버에서 엑셀 파일이 blob 형태로 전달됩니다.

    const res = await authAxios.get(
      `/api/v1/campaign-report/download?send_start_dt=${downXls.send_start_dt}&send_end_dt=${downXls.send_end_dt}&cmpgn_title=${downXls.cmpgn_title}`,
      { responseType: "blob" }
    );

    이렇게 받아온 blob 데이터는 그대로 FileSaver를 이용해 사용자의 로컬 시스템에 저장할 수 있습니다.


    FileSaver를 통한 파일 저장 처리

    FileSaver.js 라이브러리는 브라우저에서 파일을 클라이언트 측으로 저장할 수 있도록 도와주는 도구입니다. 설치는 아래와 같이 Yarn 혹은 npm을 이용해 진행할 수 있습니다.

    yarn add file-saver

    혹은

    npm install file-saver --save

    설치 후에는 다음과 같이 import 하여 사용할 수 있습니다.

    import FileSaver from "file-saver";

    이제 받아온 blob 데이터를 FileSaver의 saveAs 메서드를 통해 파일로 저장할 수 있습니다. 기본 사용법은 다음과 같습니다.

    FileSaver.saveAs(res.data, filename);

    여기서 두 번째 인자인 filename은 백엔드에서 전달받은 파일의 이름이어야 하는데, 이를 추출하는 데에 특별한 처리가 필요합니다.


    Response Headers에서 Filename 추출하기

    백엔드에서 엑셀 파일을 전달할 때, 보통 응답 헤더의 content-disposition 항목에 파일 이름 정보가 포함됩니다. 예를 들어 다음과 같은 형태로 전달됩니다.

    Content-Disposition: attachment; filename="sample.xlsx"

    문제는 리액트 환경에서 Axios의 응답 헤더를 바로 접근할 때, response.headers.content-disposition과 같이 단순히 값을 얻으려고 하면 문제가 발생할 수 있다는 점입니다. Angular에서는 response.headers.get('content-disposition')와 같이 접근하면 값이 잘 반환되지만, 리액트에서는 해당 방식이 제대로 동작하지 않아 null을 반환하는 경우가 있습니다.

    따라서 아래와 같이 문자열 변환과 분리(split)를 이용한 꼼수로 파일 이름을 추출하는 방법을 사용하게 됩니다.

    let filename = JSON.stringify(res.headers)
      .split("filename=")[1]
      .split('",')[0];

    이 방법은 우선 응답 헤더 객체를 JSON 문자열로 변환한 후, "filename="을 기준으로 분리하여 파일 이름 부분을 추출하는 방식입니다. 물론 이 방식은 응답 헤더의 포맷이 일정하다는 가정 하에 동작하므로, 서버에서 전달하는 헤더 포맷이 변경된다면 수정이 필요할 수 있습니다.


    전체 구현 코드 예제

    아래는 앞서 설명한 과정을 하나의 함수로 통합한 예제 코드입니다. 이 코드는 특정 검색 조건(예: 시작일, 종료일, 캠페인 제목)을 기반으로 엑셀 다운로드 API를 호출하고, 응답으로 받은 blob 데이터를 FileSaver를 사용해 바로 로컬에 저장하는 역할을 수행합니다.

    // 엑셀 다운로드 버튼 클릭 시 실행되는 함수
    const handleDown = async (e) => {
      // 예시: 검색 데이터에서 필요한 값 추출
      const { cmpgn_title, send_start_dt, send_end_dt } = campaignSearchData;
    
      // API 호출에 사용할 파라미터 구성
      let downXls = {
        cmpgn_title: cmpgn_title,
        send_start_dt: "",
        send_end_dt: ""
      };
    
      if (send_start_dt !== null) {
        downXls.send_start_dt = changeFormat(send_start_dt, "YYYY-MM-DD");
      }
      if (send_end_dt !== null) {
        downXls.send_end_dt = changeFormat(send_end_dt, "YYYY-MM-DD");
      }
    
      try {
        // Axios를 사용하여 엑셀 파일 다운로드 API 호출 (blob 응답)
        const res = await authAxios.get(
          `/api/v1/campaign-report/download?send_start_dt=${downXls.send_start_dt}&send_end_dt=${downXls.send_end_dt}&cmpgn_title=${downXls.cmpgn_title}`,
          { responseType: "blob" }
        );
    
        // 응답 헤더에서 filename 추출
        console.log("res headers:", res.headers);
        let filename = JSON.stringify(res.headers)
          .split("filename=")[1]
          .split('",')[0];
    
        // FileSaver를 사용하여 파일 저장
        FileSaver.saveAs(res.data, filename);
      } catch (error) {
        // 에러 발생 시 처리 로직 추가 (예: 사용자에게 알림)
        console.error("엑셀 파일 다운로드 실패:", error);
      }
    };

    이 코드를 프로젝트에 적용하면, 별도의 새 창이나 링크(a href)를 사용하지 않고도 버튼 클릭 한 번으로 API를 통해 전달받은 엑셀 파일을 바로 다운로드 받을 수 있습니다.


    구현 시 주의사항 및 개선점

    1. 응답 헤더 포맷
      위에서 사용한 파일 이름 추출 방식은 응답 헤더가 특정 포맷(즉, filename="...")으로 구성되어 있다는 전제 하에 작동합니다. 만약 서버 측에서 헤더 포맷이 변경되거나 추가적인 인코딩 문제가 발생한다면, 정규식을 사용하는 방법 등으로 개선할 필요가 있습니다.
    2. 보안 이슈
      파일 다운로드 기능은 민감한 데이터가 포함될 수 있으므로, 인증 토큰을 통한 보안 조치가 반드시 필요합니다. Axios 인스턴스를 생성할 때, 토큰 헤더를 자동으로 포함시키는 등의 처리를 잊지 말아야 합니다.
    3. 에러 핸들링
      파일 다운로드 도중 발생할 수 있는 다양한 에러(네트워크 오류, 응답 데이터 형식 오류 등)에 대비하여 사용자에게 적절한 안내 메시지를 제공하는 것이 좋습니다.
    4. 대용량 파일 처리
      만약 대용량의 엑셀 파일을 다운로드할 경우, blob 처리나 브라우저 메모리 관리에 주의해야 합니다. 필요한 경우 스트리밍 방식의 다운로드 방법을 고려할 수 있습니다.
    5. 파일 이름에 특수 문자 포함 여부
      파일 이름이 한글이나 특수 문자를 포함할 경우, URL 인코딩이나 디코딩 문제로 인해 파일 저장 시 문제가 발생할 수 있으므로, 이를 사전에 처리하는 로직이 필요할 수 있습니다.

    결론

    리액트에서 엑셀 다운로드 버튼을 구현하는 것은 단순히 파일을 받아 저장하는 기능 이상의 의미를 가집니다. 백엔드와의 API 통신, blob 데이터 처리, 그리고 응답 헤더에서 파일 이름 추출 등 여러 과정을 하나씩 해결해 나가야 하기 때문입니다.
    이번 포스팅에서는 Axios를 통해 blob 형태의 데이터를 받아 FileSaver.saveAs를 활용해 파일을 저장하는 방법과, 응답 헤더에서 filename 정보를 추출하는 꼼수를 알아보았습니다.
    실제 프로젝트에 이 기능을 적용할 때는 위에서 언급한 주의사항들을 반드시 고려하여, 보다 안정적이고 확장 가능한 코드를 작성하는 것이 중요합니다.

    리액트의 유연함과 함께 API 통신의 다양한 기법들을 잘 활용하면, 사용자가 원하는 파일 다운로드 기능을 보다 직관적이고 빠르게 구현할 수 있습니다. 앞으로도 다양한 파일 처리 기법을 꾸준히 학습하고 개선해 나가시길 바랍니다.


    반응형

    댓글