온룸 파일 다운로드 관련 이슈와 해결한 방법
내보내기 const downloadConsultingReportAsync = createAsyncThunk( '사용자/DOWNLOAD_APPLICATION', 비동기(고객: 문자열) => { const 응답 = await axios({ URL: `https://api.ownroom.link/api/consultings/report/download?nickname=${고객}`, 메소드: `get`, 헤더: { 권한 부여: `Bearer ${getCookie('token')}`, }, }); const a = document.createElement('a'); a.href = 응답.data.url; a.click(); { ...response.data }를 반환합니다. } );
들어가며
온룸 프로젝트에는 컨설턴트와 고객 간에 보고서/신청서 파일을 교환할 수 있는 기능이 있다. 아래 사진처럼 폼에 파일을 올리고 저장을 하면, 마이페이지 상에서 진행되고 있는 컨설팅 현황을 살펴볼 수가 있고, 이를 다운로드할 수도 있다.



이 기능을 개발하면서 생겼던 문제와 해결했던 방법을 이 글을 통해 다뤄보고자 한다.
무엇이 문제였나
내가 해결에 참여했던 문제는 문제 3인데, 이를 위해 앞서 설명이 필요해서 문제 1, 2에 대해 간략히 설명해보려고 한다.
문제1: S3에 파일 업로드 시 파일명 중복관련 문제
온룸 프로젝트는 AWS S3를 이용해서 파일을 관리한다. S3에 파일을 업로드하면 해당 파일을 다운로드할 수 있는 객체 url이 생성된다.

해당 url에 접속해서 파일을 다운로드하면 파일명 그대로 파일을 다운로드할 수 있다. 그런데 이렇게 하면 동일한 파일명으로 업로드했을 때 문제가 생긴다. 이 문제를 해결하기 위하여 uuid로 파일을 저장하기로 했다.
문제2: uuid로 업로드 시 클라이언트 사이드에서 파일명이 uuid로 보이는 문제
문제 1을 해결하기 위해 uuid를 도입했더니 고객이 실제로 받는 파일에 그대로 uuid가 노출되는 문제가 생겼다. 이를 해결하기 위해서 데이터 구조 상에서 원본 파일명을 따로 저장하고, 서버에서 S3로부터 올바른 파일명으로 다운받아온 후 클라이언트에게 응답을 주고 서버 내 파일을 삭제하는 방향으로 구현이 수정되었다.

문제3: 다운로드가 안 되는 문제가 발생
사실 문제 1, 2는 백엔드 파트에서 처리한 사항이고, 협업 당시 저런 식으로 구사했다는 것은 잘 몰랐다. 그런데 막상 백엔드에서 전달해준 api문서대로 다운로드 버튼을 누르면 axios로 서버에 다운로드 요청을 보내도록 구현을 했는데, 아래 형태의 응답이 돌아왔다.

무언가 인코딩 되었는데 디코딩이 잘 안 된 것 같이 보였다. 우리 팀의 백엔드 개발자는 인코딩 된 것은 아마 with open('~','rb')에서 바이너리로 인코딩 된 형태가 넘어가는 것 같다고 했다.
백엔드 개발자의 말로는 강제로 다운로드가 진행되게끔 코딩을 했다고 했고, 로컬 환경에서 테스트해봤을 때는 잘 수행이 된다고 했다. 어떤 방식으로 테스트가 진행되는 지 몰랐기 때문에 테스트 방식을 물어봤고, 백엔드 개발자는 테스트 영상을 직접 찍어서 전달해주었다. 전달받은 영상은 아래와 같다.
정말로 다운이 잘 되는 것을 확인할 수 있었다. 그런데, 내가 구현했던 방식과 백엔드 개발자가 보여준 테스트 영상에는 한 가지 큰 차이점이 있었다. 바로 POST 요청을 하는 것이 아니라 직접 주소창에 쳐서 확인을 했다는 점이다. 왜 뜬금없이 POST 요청에 대한 언급이 나왔냐면, 그때 당시 전달받은 API문서에는 POST로 토큰을 헤더에 넣고 Body에 userId를 넣어서 요청을 보내도록 가이드가 나왔기 때문이다.

내가 직접 백엔드 로직을 작성하지는 않았지만, 이 문제 해결에 기여하고 싶었다. 그래서 프론트 사이드에서 이 문제를 해결할 방법이 있는지 고민해보았다.
일단 이상하게 인코딩된 응답으로 무언가를 하는 것은 불가능해 보였고, 현재 구현되어 있는 기능 -테스트 영상에서 나왔던, 특정 url에 진입하면 다운로드가 진행되는 기능-을 활용하고 싶었다. 그래서 프론트 사이드로 응답을 줄 때 파일을 넘기는 것이 아니라, 저 해당하는 url을 body에 실어서 넘기고 프론트 사이드에서 내부적으로 a 태그를 만들어서 접근하면 어떻겠냐는 제안을 했다.

그 결과 제한된 시간 안에 기능 구현에 성공할 수 있었다.
솔루션 코드
아래는 프론트 사이드에서 문제 해결을 위해 작성한 코드이다.
export const downloadConsultingReportAsync = createAsyncThunk( 'user/DOWNLOAD_APPLICATION', async (customer: string) => { const response = await axios({ url: `https://api.ownroom.link/api/consultings/report/download?nickname=${customer}`, method: `get`, headers: { Authorization: `Bearer ${getCookie('token')}`, }, }); const a = document.createElement('a'); a.href = response.data.url; a.click(); return { ...response.data }; } );
백엔드 개발자가 테스트환경에 맞게 API를 바꿔주었다. POST에서 GET으로, 응답 body에 다운로드가 진행되는 url을 실어서 주도록 API가 변경되었다. 나는 프론트에서 document.createElement를 이용하여 내부적으로 링크를 만들고 그 링크가 클릭되도록 하는 방식으로 구현했다.
마치며
이것은 일종의 우회적인 구현방법이지만, 당일에 바로 기능을 시연해야하는 상황이었기에 백엔드 내에서 해결이 안된다면 이렇게라도 구현을 해야한다고 생각했다. 구현 방식의 수정 이후 기능은 정상적으로 잘 작동하였고, 우리 조는 성공적으로 발표를 마칠 수 있었다.😊 나중에 알게 된 사실이지만 근본적인 원인은 백엔드에서 mimetype을 잘못 지정했던 데 있었다. 그 부분이 백엔드 차원에서 해결되었다면 나는 문제를 겪어보지 못했겠지만, 문제적인 상황에 함께 놓이고 프론트 나름대로의 해결 방법을 제시하고 도전해보았기에 한 걸음 더 성장할 수 있었던 것 같다.👍
'Dev Log' 카테고리의 다른 글
gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기 (0) | 2022.08.07 |
---|---|
플러터로 웹뷰 세팅하기 (0) | 2022.08.01 |
TypeScript Object, object, {} 비교 (0) | 2021.11.26 |
오픈소스에 기여하는 법 : MDN 문서에 기여하기 (0) | 2021.11.10 |
크롬 글자 일그러짐 현상 해결법 (0) | 2021.11.09 |
댓글
이 글 공유하기
다른 글
-
gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기
gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기
2022.08.07프로젝트를 깃랩에서 관리하고 배포를 위해 깃허브로 미러링을 시켰습니다. 액세스 토큰을 다시 발급받으면서 토큰이 갱신되었는데, 갱신된 토큰을 다시 입력해도 깃랩에서 계속 오류가 뜨면서 미러링이 안되는 문제가 있었습니다. 해결 방법 Mirroring Repository에 username 뒤에 토큰을 같이 입력해주면 됩니다. https://username:personalacesstoken@gihub.com/repo.git Reference https://forum.gitlab.com/t/authentication-fails-when-trying-to-mirror-gitlab-com-repo-to-github/3137 -
플러터로 웹뷰 세팅하기
플러터로 웹뷰 세팅하기
2022.08.01Flutter 앱에 WebView 추가 를 읽으면서 정리한 글입니다. pubspec.yaml에 의존성 추가 아래 명령어로 플러그인을 추가합니다. flutter pub add webview_flutter flutter pub get main.dart 에 웹뷰 추가 import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() { runApp( const MaterialApp( home: WebViewApp(), ), ); } class WebViewApp extends StatefulWidget { const WebViewApp({Key? key}) : super(key: key… -
TypeScript Object, object, {} 비교
TypeScript Object, object, {} 비교
2021.11.26들어가며 타입스크립트의 객체는 세 가지 타입이 있는데, 헷갈릴 수 있기 때문에 정리해보았다. Object 자바스크립트 객체와 완전히 동일하다. 즉, toString(), hasOwnProperty() 등을 가지고 있다. Object.prototype. ….으로 쓸 수 있는 API들을 다 쓸 수 있다. primitive type, none-primitive 모두 할당될 수 있다. {} Object와 비슷하지만 비어있다. runtime에는 Object와 같지만 컴파일 시에는 다르다. 컴파일 시에는 Object의 멤버를 가지고 있지 않고, Object가 더 엄격한 객체이다. object typescript에서 추가된 객체 타입으로, non-primitive type을 할당할 수 있지만, primitive t… -
오픈소스에 기여하는 법 : MDN 문서에 기여하기
오픈소스에 기여하는 법 : MDN 문서에 기여하기
2021.11.10들어가며 MDN에서 이벤트 관련 내용을 학습 중이었는데, 연결된 문서인 Web/API/AbortSignal에 어체가 단순히 번역만 되어 있는 것을 발견했다. 이것을 고쳐보고 싶었다. Mozilla 한국어 문서 번역 스타일 가이드에 따르면 경어체, 합쇼체로 수정이 필요해 보였다. 별 것 아닌 일이지만 그동안 오픈 소스에 기여해보고 싶었는데, 내친 김에 해봐야겠다 싶었다. MDN에 기여하는 법 1. 우선 가장 먼저 MDN Korea 오픈 톡방에 들어갔다. 2. 아래의 가이드라인을 따랐다. [ko] 한국 첫 기여자들을 위한 가이드라인 [ko] 한국 첫 기여자들을 위한 가이드라인 · Issue #827 · mdn/translated-content Progress 한국 첫 기여자들을 위한 가이드라인 우선 작성 한…
댓글을 사용할 수 없습니다.