React, express, NCP를 활용한 파일 업로드 및 접근 (2)

박기범·2021년 11월 16일
1

본 게시글은 부스트캠프 6기 웹 풀스택 트랙의 SWS (ShallWeSound) 프로젝트를 진행하며 겪었던 문제점과 해결방안을 정리합니다.

더불어 본 게시글의 SWS 프로젝트는 doggydeok2, seongjunme, jsl0149님과 같이 개발했으며 문제 해결에 도움을 받았음을 명확히 밝힙니다.

0. 개요

  1. 겪었던 문제점
  2. 리서치
  3. 해결방안
  4. 참고자료

1편에서 겪었던 문제점을 이미 서술했다. 따라서 리서치와 해결방안의 추가 내용을 본 게시글에 서술하도록 한다.

2. 리서치

1. stream, buffer

multer의 memory storage는 stream.
aws S3는 buffer.
정말 최고의 궁합이 아닐 수 없다. (아니다.)
이를 해결하기 위해 stream과 buffer가 무엇인지 조사했다. 대체 buffer와 stream이라는건 뭐길래 이렇게 우리를 괴롭힐까?

우리 모두는 이미 버퍼링과 스트리밍이라는 단어를 들어봤다. 이렇게 생각하니 왠지 친숙하게 느껴진다. 그 의미를 한번 간략하게 알아보자.

  • 버퍼링(buffering) : 특정 파일의 데이터를 일정 크기만큼 모으는 행위
  • 스트리밍(streaming) : 특정 파일의 데이터를 잘게 쪼개어 나눠받는 행위

그렇다. 버퍼링을 위해 일정 크기만큼 모인 데이터를 buffer, 스트리밍을 위해 쪼개어진 데이터가 바로 stream이다.

buffer는 사실 컴퓨터에 저장되는 0과 1로 이루어진 이진 데이터를 읽어들여 임시로 데이터를 저장해두는 일종의 저장공간이다. 입출력장치 등을 통해 생성된 데이터가 buffer에 쌓이고, buffer가 가득 차면 특정 대상에게 해당 데이터를 묶어서 전송해주는 방식이다.그런데, javasciprt는 high-level language이기에 이러한 buffer를 직접 다루지 않는다. 이에따라 nodejs는 js에서 buffer를 다룰 api를 제공해준다.

buffer를 이용한 buffering은 분명히 편리하지만, 다음과 같은 문제점이 있다.

  1. 만약 buffering을 통해 읽어올 데이터의 용량이 너무 크다면, 그 큰 데이터를 한번에 담아둘 메모리를 확보하기 어렵다.
  2. 1번 이유에서, 큰 데이터를 한번에 읽어오기까지의 시간이 너무 오래 걸린다.

위와 같은 문제점을 해결하기 위해 stream을 이용한다. stream은 거대한 데이터를 소규모의 단위로 나누어서 전송하게 된다. 그리고 이러한 stream은 소규모의 buffer에 담기고, 해당 buffer가 꽉 찼을때 데이터가 전송되기도 한다.

..????

뭔가 이상하다. 그러면 buffering에 사용되는 buffer 자체를 소규모로 두면 되는것 아닌가?

다음 그림을 보자.

위에서 버퍼를 사용하지 않는경우, 입출력 장치로부터의 데이터를 프로그램으로 전송시켜주는 보조 전달장치가 바로 stream이다. 즉, 데이터들을 잘개 쪼개고 하나하나 직접 전달할 수 있는것이다. 만약 전달 속도에 굉장히 민감한 매체가 있다면,(ex. 게임, 실시간 방송) buffer의 크기를 최소화하거나 stream을 통해 바로바로 잘개 쪼개진 데이터를 전송하는 방법을 택해야 한다.

이런 stream을 구축하기 위한 nodejs의 라이브러리가 있는데, 바로 readable 이다. 자세한 내용은 공식문서로 갈음한다.

nodejs readable stream

3. 해결방안

자. 이제 stream과 buffer를 알아보았으니 문제를 해결하자.

나는 multer가 memory에 저장해둔 buffer를 다음과 같은 방법을 사용하여 readable stream으로 변경했다.

import {Readable} from 'stream';
...코드중략
Readable.from(files.userFile1[0].buffer)

위와같이 stream의 Readable을 이용하면 저장된 buffer를 readable stream 형태로 변환할 수 있다. 이렇게 S3.upload의 body에 실어서 보내면 정상적으로 object storage에 업로드 가능하다.

자! 이렇게 다음의 문제점들을 해결했다.

  1. 근본적으로, 어떻게 파일을 업로드할것인가? 네이버 클라우드 플랫폼의 object storage에 접근하기 위한 라이브러리, SDK를 알고있는가?

  2. 클라이언트가 서버를 거쳐 object storage에 파일을 업로드한다면, 서버의 디스크에 지속적으로 read/write가 일어나지 않겠는가? 이러한 io 작업은 서버에 많은 부담을 주지 않는가?

하지만 아직 두가지가 남았다.

  1. 1번 문제점과 마찬가지로, object sotrage에서 서버를 거쳐 음악 스트리밍을 제공받는다면, 이 과정에서도 디스크게 read/write가 빈번하게 일어나지 않겠는가?

  2. 만약 서버에 음악을 지속적으로 다운로드 받고 지우는 방법을 택한다면, 특정 사용자가 음악을 재생하는 중에 다른 사용자가 음악을 다 청취하여 음악이 자동으로 삭제되는 현상이 발생할수도 있다. 이런 문제는 어떻게 해결할것인가?

사실 이는 클라이언트 측에서 서버를 거치지 않고 곧바로 object storage의 음악에 접근할 수 있다면 해결되는 문제였다. 하지만, object storage에 접근하려면 aws-sdk의 인증정보가 필요했다. 필자는 이러한 부분 때문에 서비스에 접근하는 모든 사용자가 직접 object storage에 붙는것은 어렵다고 판단했다.

당연하게도, 필자의 완벽한 오판이었다!

aws-sdk는 서버의 aws 인증정보를 이용해 클라이언트가 파일에 직접 접근할 수 있는 signed url을 발급해주고 있었다. 그리고 이러한 사실을 부스트캠프 기술공유 시간에 다른 캠퍼의 발표를 통해 알 수 있었다. 중요한 기술들을 공유해준 캠퍼님들에게 무한한 감사를..

도움을 받은 프로젝트를 소개한다. 조회한 사람은 star를 찍도록 하자..
boostcampwm-2021/web22-boost-world-cup

이제 aws-sdk의 공식 문서를 보자. signed url은 어떻게 발급받을 수 있는가?

원문 : https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property

세상에나... 너무 간단하다.
이제 내 코드를 보자.

export const makeSignedURL = (S3:AWS.S3, contentHash:String, musicName:String):string =>{
    const params = {Bucket: 'sws', Key: `${contentHash}/${musicName}`, Expires: 5000000};
    const url = S3.getSignedUrl('getObject', params);
    return url;
}

param에 부여한 Expires는 만료기간인데, 단위가 second이다. 감안해서, 필요한 기간을 설정해주면 된다.

이렇게 발급받은 url을 브라우저로 접근하면, 정상적으로 파일이 다운로드 되는 모습을 볼 수 있다. 즉, html의 src를 통해 파일에 바로 접근이 가능한것이다!
이렇게 2번, 3번문제를 모두 해결했다.

이번 프로젝트를 통해 클라우드를 통한 파일처리를 정말 진득하게 경험하고 있다.
다음 게시글은 아마 react에서 typescript를 이용해 drag&drop을 구현하는 방법을 서술할것 같다.

그럼, 안녕히!

4. 참고자료

profile
원리를 좋아하는 개발자

2개의 댓글

comment-user-thumbnail
2021년 11월 17일

우와아 저희 조 언급되었다는 소식에 헐레벌떡 들어왔습니다.
저희도 네이버 클라우드 파일처리로 고생 많이 했는데, 1편부터 읽어보면서 또 정리할수 있었어요ㅎㅎ
좋은 글 감사합니다!!

(엇.. 그런데 왜! 스타 안달아주시나요.. 저는 1편읽으면서 스타했는뎁 )

1개의 답글