[React] 다운로드 이력 남기기 기능(feat.withCredentials)

hyejinJo·2025년 1월 10일
0

React

목록 보기
13/13
post-thumbnail

KCMF 의 관리자 페이지의 다운로드 기능에서 페이지에 대한 값인 파라미터(pageName)을 추가하는 작업을 진행했다. 각 페이지마다 첨부파일을 다운로드 받으면 그 이력이 남아 다운로드 이력 페이지 목록에 등록되는 과정이었는데, 백엔드 개발자분이 다운로드가 발생한게 어느 페이지인지도 값을 받아야 작업이 수월할 것 같다고 요청을 주신 것이다.

그래서 현재 페이지가 어디인지 url 에서 추출하여 현재 페이지에 대한 string 값을 pageName 파람키로 전달해주었다.

// 다운로드 할 파일 리스트 컴포넌트

... 
// PageNameType: 페이지별로 추가될 파라미터키(pageName)의 value 값 모음
const pageNameTypeText = (path: string) => {
  switch (path) {
    case 'program-apply':
      return PageNameType.PROGRAM_APPLY;
    case 'program-apply-fee':
      return PageNameType.FEE;
    case 'review':
      return PageNameType.REVIEW;
    case 'review-result':
      return PageNameType.REVIEW_RESULT;
    case 'internal-review':
      return PageNameType.INTERNAL_EVALUATION_APPLY;
    case 'internal-review-result':
      return PageNameType.INTERNAL_EVALUATION;
    case 'file':
      return PageNameType.FILE;
    case 'legacy-file':
      return PageNameType.LEGACY_FILE;
    default:
      return '';
  }
};

const FileDownloadList: React.FC<FileDownloadListProps> = ({ fileList, isLegacyFile = false }) => {
  const { downloadFile, downloadLegacyFile } = useFileDownload();
  
  const location = useLocation();
   // 파라미터로 보내질, 정해진 페이지 string 값 리스트 불러오기 
  const enumsSelector = useMemo(() => selectEnums(['page-name']), []);
  const enums = useSelector(enumsSelector);

  const selectedPageName = useMemo(() => {
    const firstSegment = location.pathname.split('/')[1]; // 현재 페이지에 대한 이름 url 에서 추출
    const pageName = pageNameTypeText(firstSegment);

    const findKeyByValue = (targetKey: string): string | undefined => {
      const [pageNameEnum] = enums;
      const foundItem = pageNameEnum?.find((item) => item?.key === targetKey);
      return foundItem?.key;
    };

    return findKeyByValue(pageName);
  }, [location.pathname, enums]);

  const handleDownloadFile = async (id: number, filename: string, selectedPageName?: string) => {
    try {
      downloadFile(id, filename, selectedPageName);
    } catch (error) {}
  };

  if (!fileList) return null;

  const fileArray = Array.isArray(fileList) ? fileList : [fileList];

  return (
    <div>
      {fileArray?.map(({ filename, size, attachId, legacyAttachId }) => (
        <Box key={shortid.generate()}>
          <AttachFileIcon color="primary" sx={{ width: '20px', height: '20px' }} />
          <button
            type="button"
            onClick={() => handleDownloadFile(attachId, filename, selectedPageName)}
          >
            ...
        </Box>
      ))}
    </div>
  );
};

export default memo(FileDownloadList);

문제 발생:

로그인한 상태에서 분명 요청을 보냈는데 백엔드 측에서 쿠키값이 안내려왔다고 한다. 그래서 계속 권한 관련 에러코드가 내려오는 상황이었다…

원인:

다른 페이지에서api 요청을 할 때 아래의 공통으로 쓰이는 HTTP 클라이언트를 사용했지만, 다운로드 요청의 경우 추가 로직을 위해 api 요청하는 부분을 따로 하드코딩 했었다.

결정적으로 withCredentials: true 설정이 걸려있지 않아 쿠키가 보내지지 않은 것이 원인이었고, 어이없게도 해당 설정을 추가했더니 정상적으로 다운로드 요청이 갔다.

// 공통 http 클라이언트

import axios, { AxiosInstance } from 'axios';

const httpClient: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_ENDPOINT,
  withCredentials: true,
});

export default httpClient;
import axios from 'axios';

const useFileDownload = () => {
  const downloadFile = async (id: number, filename: string, pageName: string = '') => {
    try {
      const { data } = await axios({
        url: `${import.meta.env.VITE_API_ENDPOINT}admin/v1/attach/${id}/download?pageName=${pageName}`, // your url
        method: 'GET',
        responseType: 'blob',
        withCredentials: true, // 추가
      });

      if (data) { // 브라우저에서 다운받을 수 있도록 설정된 로직
        const url = window.URL.createObjectURL(new Blob([data]));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', filename);
        link.style.cssText = 'display:none';
        link.click();
        setTimeout(() => window.URL.revokeObjectURL(url), 0);
      }
    } catch (error: any) {
      console.error(error.response || error);
    }
  };

  return {
    downloadFile
  };
};

export default useFileDownload;

결과:

다운로드 실행시, 이력에도 데이터가 잘 들어간 것을 알 수 있다.
(참여프로그램 신청 관리란 페이지에서 파일을 다운로드 했고, 그 결과가 'PROGRAM_APPLY' 라는 이름으로 요청 값에 담김)

권한 제약이 있는 요청에 있어 withCredentials 속성에 대해 넣어야 한다는 내용은 익히 들었지만, 실제로 요청 실패가 뜨는 것을 경험했으니 해당 속성에 대한 중요성을 더 알게된 계기라고 생각한다.

profile
Frontend Developer 💡

0개의 댓글