본문 바로가기

JavaScript/NestJS

GCS에 파일 업로드(NestJS)

배경

이미지나 비디오 같은 정적 파일 로딩 레이턴시를 줄이기 위해 Cloud CDN 서비스를 사용해야 했고, GCP에서 백엔드로 운영 중인 NestJS에서 이미지를 저장할 때 이를 적용해야 해서 나중에 참고할 수 있도록 글로 정리하게 되었습니다.


버킷 생성

GCS에 업로드 하기 전에 먼저 저장소를 생성해야 합니다.

GCP 메뉴 중 Cloud Storage로 이동해서 버킷을 생성해 줍니다.

 

 

 

다음으로 백엔드가 GCS에 접근할 수 있도록 키를 발급해야 합니다.

'IAM > 서비스 계정'으로 이동해서 저장소 관리자 권한을 부여한 서비스 계정을 만들고 키를 생성해 줍니다.

 

 

 

생성한 서비스 계정으로 이동해서 json 키를 생성합니다.

 

다운받은 키 내용 모두를 환경설정 파일로 만들어서 프로젝트 내에 저장합니다.

깃에 푸시할 때는 키 파일을 ignore로 설정하고 리포지토리 환경변수로 생성해서 사용하는 것이 권장됩니다.

 

gcs-service-key.json

{
  "type": "service_account",
  "project_id": "...",
  "private_key_id": "...",
  "private_key": "...",
  "client_email": "...",
  "client_id": "...",
  "auth_uri": "...",
  "token_uri": "...",
  "auth_provider_x509_cert_url": "...",
  "client_x509_cert_url": "..."
}

 

다음으로 package.json script에 아래와 같이 GCP 인증을 추가합니다.

 

package.json

{
  ...
  "scripts": {
    ...
    "start:dev": "GOOGLE_APPLICATION_CREDENTIALS='gcs-service-key.json' nest start --watch",
    ...
  },
  ...
}

 

다음으로 multer 옵션을 작성해서 파일 저장 프로세스를 정의합니다.

 

multer-options.ts

import { diskStorage } from 'multer';
import * as fs from 'fs';
import * as uuid from 'short-uuid';

export const multerOptions = {
  storage: diskStorage({
    destination: function (req, file, cb) {
      const path = `${process.env.UPLOADS_TEMP_URL}`;
      cb(null, path);

      // 디렉터리 없으면 디렉터리를 새로 생성
      if (!fs.existsSync(path)) {
        fs.mkdirSync(path, { recursive: true });
      }
    },
    filename: function (req, file, cb) {
      const rightNow = new Date();
      const yymmdd = rightNow.toISOString().slice(0, 10).replace(/-/g, '');
      cb(null, `${yymmdd}${uuid.generate()}`);
    },
  }),
};

 

다음으로 temp파일에 저장된 이미지 파일을 GCS에 업로드 하면 됩니다.

아래 패키지를 먼저 설치해주세요.

 

npm i @google-cloud/storage --save​

 

image.controller.ts

import { Storage } from '@google-cloud/storage';

...

@Post()
@UseInterceptors(FileInterceptor('img', multerOptions))
uploadTempFile(@UploadedFile() image: Express.Multer.File) {
  const rightNow = new Date();
  const year = rightNow.toISOString().slice(0, 4);
  const month = rightNow.toISOString().slice(5, 7);
  const storage = new Storage({ projectId: process.env.GCP_PROJECT_ID });
  return storage
    .bucket(process.env.GCP_BUCKET_NAME)
    .upload(`${process.env.UPLOADS_TEMP_URL}/${image.filename}`, {
      destination: `${year}/${month}/${image.filename}`,
    });
  }

 

위와 같이 upload()를 하면 이미지 파일이 GCS에 업로드 되며, 폴더가 없으면 자동으로 생성해줍니다.