NHN Cloud Object Storage를 express에서 사용해보자!

janghoosa·2022년 7월 17일
0

Node.js

목록 보기
1/4
post-thumbnail
post-custom-banner

이번에는 NHN Cloud의 Object Storage를 사용하는 법을 배워보자.
아직 NHN Cloud를 사용하는 일반 유저들이 많지 않아서인지 검색을 해도 예제가 나오지 않아서 조금 애를 먹었지만 우리의 친절한 NHN Cloud 사이트에서는 친절하게 가이드를 제공해준다.

Object Storage 생성

Object Storage는 컨테이너를 생성해서 권한을 설정 할 수 있고 관리 할 수 있다.
생성을 하면 빈 깡통 컨테이너가 만들어졌을 것이다.

사용하기 위한 토큰 발급

TenantID, API Endpoint, ID, Password 확인

오브젝트 스토리지를 콘솔이 아닌 곳에서 사용하려면 인증 토큰을 발급받아야한다.
그런 인증 토큰은 Tenant ID, API Endpoint, ID, API Password의 4가지가 있어야 발급 가능하다.


이 4가지 중 3개는 NHN Cloud 콘솔창의 Object Storage탭에서 API 엔드포인트 설정버튼을 눌러서 모두 확인 가능하다.
ID는 로그인 할때 썼던 자신의 아이디이니 알아둘 것! 그리고 스토리지 계정은 내 오브젝트 스토리지를 가리키는 것이다.

Object Storage를 처음 사용하는 유저는 API 비밀번호가 설정이 안되어있을 것이다. 이 비밀번호는 로그인할때 쓰는 비밀번호랑은 다르다. 같은 오브젝트라도 사용자마다 다른 비밀번호를 입력할 수 있으며 API를 호출할때만 사용되는 비밀번호이다.

인증 토큰 발급 API 사용

POST    https://api-identity.infrastructure.cloud.toast.com/v2.0/tokens
Content-Type: application/json

요청을 보낼때에는 다음과 같은 정보를 json형식으로 body에 넣어서 post로 보내야한다.

{
  "auth": {
    "tenantId": "{Tenant ID}",
    "passwordCredentials": {
      "username": "{NHN Cloud ID}",
      "password": "{API Password}"
    }
  }
}

제대로 된 tenantID와 ID, Password를 입력했다면 아래와 같은 형식의 응답이 올 것이다.



{
  "access": {
    "token": {
      "expires": "{Expires Time}",
      "id": "{token-id}",
      "tenant": {
        "description": "",
        "enabled": true,
        "id": "{Tenant ID}",
        "name": "{NHN Cloud ID}",
        "groupId": "{NHN Cloud Project ID}",
        "project_domain": "NORMAL",
        "swift": true
      },
      "issued_at": "{Token Issued Time}"
    },
    "serviceCatalog": [],
    "user": {
      "id": "{User UUID}",
      "name": "{User Name}"
    }
  }
}

여기서 중요한 값은 token의 id 값과 expires 값이다.
토큰을 사용할 때 token-id값을 Header에 넣어주면 사용 가능하고 Expires Time이 지나면 토큰이 만료되므로 다시 발급을 받아야한다.

multer의 Custom Storage Engine 만들기

multer 소개

multerexpressjs에서 만든 파일 업로드를 위한 nodejs 미들웨어이다.
한글화된 multer의 소개 페이지에서는 이렇게 설명하고 있다.

Multer는 파일 업로드를 위해 사용되는 multipart/form-data를 다루기 위한 node.js 의 미들웨어 입니다. 효율성을 최대화 하기 위해 busboy 를 기반으로 하고 있습니다.
Multermultipart (multipart/form-data)가 아닌 폼에서는 동작하지 않습니다.

업로드시 주의사항

업로드를 위해서는 form-data로 파일을 보내야하며 보낸 키에 맞는 데이터을 받아 저장한다.
여기서는 img라는 키로 img를 전송하는 테스트를 하겠다.

Custom Storage Engine 만들기

공식문서에 나와 있는 Custom Storage Engine을 참고하여 만들었다.
https://github.com/expressjs/multer/blob/master/StorageEngine.md

우선 전체 소스코드를 한번 보자.

// ObjectStorage.js
const axios = require("axios");

const OS_ENDPOINT = process.env.OS_ENDPOINT;
const OS_TENANTID = process.env.OS_TENANTID;
const OS_USERNAME = process.env.OS_USERNAME;
const OS_PASSWORD = process.env.OS_PASSWORD;
const containerName = "/tutor";

var token;
var tokenExpiresTime;

const getAcessToken = async () => {
  // 만료되지않았다면 기존에 있던 토큰 사용
  if (tokenExpiresTime > new Date().toISOString()) {
      return token, tokenExpiresTime;
  }
  // 발급되지않았거나 만료되면 토큰 발급
  let tokenURL =
      "https://api-identity.infrastructure.cloud.toast.com/v2.0/tokens";
  let tokenHeader = {
      headers: {
          "Content-Type": "application/json",
      },
  };
  let body = {
      auth: {
          tenantId: OS_TENANTID,
          passwordCredentials: {
              username: OS_USERNAME,
              password: OS_PASSWORD,
          },
      },
  };
  let result = await axios.post(tokenURL, body, tokenHeader);
  token = result.data.access.token.id;
  tokenExpiresTime = result.data.access.token.expires;
  logger.info("Get Token Success", token, tokenExpiresTime)
  return token, tokenExpiresTime;
};

const putHeader = (token, file) => {
  return {
      headers: {
          "X-Auth-Token": `${token}`,
          "Content-type": `${file.mimetype}`,
      },
  };
};

function ObjectStorage(opts) {
  this.getDestination = opts.destination;
}

ObjectStorage.prototype._handleFile = function _handleFile(req, file, cb) {
  this.getDestination(req, file, async function (err, container) {
      if (err) {
          return cb(err);
      }
      // 토큰 발급
      await getAcessToken();
      let filename = encodeURI(Date.now() + "_" + file.originalname);
      let url = OS_ENDPOINT + containerName + container + filename;
      // 파일 전송
      axios
          .put(url, file.stream, putHeader(token, file))
          .then((response) => {
              logger.info("Put File Successed");
              logger.info(filename);
              // req.file에서 쓸 인자 추가
              cb(null, {
                  uri: url,
                  filename: filename,
              });
          })
          .catch((err) => {
              cb(err);
          });
  });
};

ObjectStorage.prototype._removeFile = function _removeFile(req, file, cb) {};

module.exports = function (opts) {
  return new ObjectStorage(opts);
};

입력해야하는 연결 정보들은 모두 env파일로 뺐다.
그리고 변수로 tokenexpiresTime을 관리하였다.
발급된 적이 없었다면 tokenTokenExpiresTime이 없을 것이고 다시 발급 받게 된다.
발급된 적이 있다면 둘다 존재할 것이고 TokenExpiresTime이 지금보다 크다면 (시간이 남았다면) 다시 발급받지 않는다.

지금보니 토큰이 invaild할때는 expire되기전까지 계속 오류가 날 것이라 다시 발급 받을 수 있게 해야하는 코드를 추가해야될것같다.

handleFilegetDestination 안의 axios안의 thencb(콜백)함수에서 multer를 쓰면 쓸 수 있는 req.file에서 쓸 인자들을 추가 할 수 있다.

// multerOS.js
const multer = require("multer");
const ObjectStorage = require("./ObjectStorage");

const multerOS = multer({
    storage: ObjectStorage({
        destination(req, file, cb) {
            cb(null, `/files/`);
        },
    }),
});

module.exports = multerOS;

사용을 위해서는 아래와 같이 만든 Custom Storage Engine을 적용시켜주면 된다.

const multerOS = require("../module/multerOS");

// ...
router.all("/api/uploadImage", multerOS.single("img") ,candyCtrl.process.setCandyImage);

위와 같이 미들웨어로 적용시키면 사용가능하다. OS로 직접 업로드가 되고 받은 정보들은 req.file에 있으니 요리조리 잘 사용하길 바란다.

profile
백엔드 개발자 지망생입니다 :)
post-custom-banner

0개의 댓글