proxyMiddleware(express.Router) 사용 시 파일 다운로드 주의사항

clean·2023년 6월 2일
0

파일 다운로드

문제의 발생

  • 엑셀 다운로드 API 를 통해 엑셀 다운로드 기능 구현 중 아래 문제가 발생했다.
  • API 서버로 Download API 요청 시 응답 데이터와 Proxy 서버로 Download API 요청 시 응답 데이터가 다른 문제

배경

어떤 환경 문제로 인해 해당 프로젝트는 이런 구조로 되어있다.

browser ⇒ front server (요청을 proxy) ⇒ api 서버

  • 위 요청을 프록시 하는 부분은 proxy-middleware 을 사용한다.

즉 요청 시 이렇게 처리되고 있었다.

  1. browser 에서 proxy 서버로 API 를 요청
axios({
	method: 'get',
  url: '...생략.../download',
  //... 
	**responseType: 'blob'**
})
  1. proxyMiddleware 에서 api 서버로 API 요청
router.get('~', async (req, res) => {
  const res = await	axios({
	  baseURL: 'API 서버 주소',
		method: req.method,
    url: req.url, 
    data: req.body
  })
	res.status(200).send(res.data);
  // 에러 처리 생략!
  // ...
})

처리

문제는 2. proxyMiddleware 에서 api 서버로 API 요청할 때 responseType 을 지정하지 않아서 발생했다.

api 서버의 응답값으로는 binary 형태의 데이터가 리턴되는데, axios 로 요청 시 responseType을 binary 타입으로 지정해주지 않으면 깨진 형태로 받게된다.

  • responseType
    • 종류
      • arraybuffer: 응답을 ‘ArrayBuffer’ 형태로 받음. 이진 데이터를 다루는 데 사용됨
      • blob: 응답을 ‘Blob’ 형태로 받음. 이진 데이터로, 주로 파일 다운로드와 관련된 데이터를 처리하는데 사용됨
      • document: 응답을 ‘Document’ 형태로 받음. XML 이나 HTML 과 같은 문서 유형의 데이터를 처리하는 데 사용됨
      • json: 응답을 JSON 형태로 받음. (기본값)
      • text: 응답을 문자열로 받음
      • stream: 응답을 스트림 형태로 받음. nodejs 환경에서 사용할 수 있음
    • 기본값: json

타입 중 blob 과 arraybuffuer 가 binary 타입의 형태인데 이 중 blob 은 브라우저 전용 타입이다.

  1. proxyMiddleware 에서 api 서버로 API 요청이 실행되는 곳은 proxy 서버로, nodejs 환경에서 실행되므로 responseType 을 arraybuffer 로 설정하여 해결했다. (blob 사용 불가)

기타

요청에 대한 분기 (필요시에만 responseType 을 지정하도록 처리)

추가로 모든 요청에 대해 responeType 을 arraybuffer로 보낼 수는 없어 responseType 이 필요한 경우에만 요청하도록 분기처리가 필요했다. 두가지 방법이 있었다.

  1. 요청 헤더에 특정 변수를 넘기기
  2. 쿼리 매개변수로 특정 변수를 넘기기

proxy 서버에 요청할 때 요청 헤더에 넘긴 후 proxyMiddleware 에서 headers 값을 확인하고, headers 에서 다시 특정 변수를 제거해주는 게 깔끔할 것 같아 1번 방법으로 처리했다.

ProxyAPI 응답 헤더에 파일명 전달

또한 파일 다운로드 시 응답 헤더의 파일명을 사용해야하는 경우가 있어서 responseType 이 arraybuffer 로 지정된 경우 응답 헤더에 content-disposition 값을 그대로 append 해주도록 처리했다.

// proxyMiddleware.js

// 파일 다운로드 일 때 헤더에 파일명 전달
if (responseType === 'arraybuffer') {
  const contentDispositionKey = 'content-disposition'
  res.append(contentDispositionKey, response.headers[contentDispositionKey])
}

참고문서

0개의 댓글