AWS Lambda 로 가벼운 API 만들기 - 3

엄현태·2020년 3월 4일
0

AWS-Lambda

목록 보기
3/3
post-thumbnail

node 프로젝트를 바탕으로 가벼운 (하지만 가볍지는 않은) API 를 만들고 있습니다.

AWS Lambda 로 가벼운 API 만들기 - 1
AWS Lambda 로 가벼운 API 만들기 - 2

앞서 2가지 시리즈는 Lambda내에 함수 코드를 통하여 테스트를 진행했었는데 이번에는 제가 직접 만든 node 프로젝트를 올려보도록 하겠습니다.

일단 Lambda 에서는 50MB 이상을 올리는것을 지원하지 않습니다.
따라서 문제가 생겼었는데 바로 제가 크롤링에 사용하는 puppeteer 모듈 자체가 이미 50MB가 넘어버리는 일이 생겼었죠.

그래서 폭풍검색을 통하여 해결방안을 찾아냈죠.

일단 먼저 방대했었던 저의 크롤링 프로젝트를 따로 분리하여 정말 필요한 모듈만 깔아주도록 하였습니다.

기존에는 express 모듈, body-parser 모듈 등 REST API를 처리하기 위한 모듈들이 깔려 있었습니다.

그리하여 저의 pacakge.json은 다음과 같이 바뀌었죠.

pacakge.json

{
  "name": "movie-crawl",
  "version": "1.0.0",
  "description": "## Movie crawling for use in AWS Lambda",
  "main": "index.js",
  "dependencies": {
    "chrome-aws-lambda": "^2.1.1",
    "puppeteer-core": "^2.1.1"
  },
  "devDependencies": {
    "puppeteer": "^2.1.1"
  },
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/eomttt/movie-crawl.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/eomttt/movie-crawl/issues"
  },
  "homepage": "https://github.com/eomttt/movie-crawl#readme"
}

개발 시에는 puppeteer를 설치해주고 배포 시에는 정말 필요한 puppeteer-core, chrome-aws-lambda 만 설치하여 빌드하도록 변경하였습니다.

puppeteer-core 는 puppeteer 에서 chrome? 모듈만 빼고 정말 필요한 기능만 들어있는 간소화 모듈입니다. 따라서 puppeteer를 실행하기 위해서는 chrome이 있어야하는데 따라서 chrome-aws-lambda 를 통해서 부족한 부분을 채워주는 것이죠.

그리고 이를 실행하고 .zip파일로 만들어줘야하는데 이를 하기위해 스크립트를 작성합니다.

./build.sh

#! /bin/bash

echo "Start for build"

rmPreviousZip="rm -rf build.zip"
buildProd="npm prune --production"
makeZip="zip -r build.zip ./*"
buildDev="npm install"

$rmPreviousZip
$buildProd
$makeZip
$buildDev

exit

내용은 기존에 build.zip 을 지워주고, production에 있는 package 만 다시 설치한 뒤, zip을 만들어 줍니다. 그리고 개발을 위해 다시 전체 모듈로 설치해주는 아주 간단한 스크립트 입니다.

이렇게 만들어 놓은 build.zip (그래서 결국 43MB 로 조절...) 을 Lambda에 올려줍니다.

여기서 주의할 점은 node 프로젝트 내에 index.js 가 무조건 있어야하고 그 안에 handler라는 함수가 export되어야 합니다.
그래야 Lambda에서 index.handler를 부를 수 있기 때문이죠.

index.js

const cgvController = require('./controller/movie-cgv.controller');
const megaController = require('./controller/movie-megabox.controller');
const lotteController = require('./controller/movie-lotte.controller');

const MOVIE_TYPE = {
    MEGA: 'megaBox',
    CGV: 'cgv',
    LOTTE: 'lotte'
};

const CONTROLLER = {
    [MOVIE_TYPE.MEGA]: megaController,
    [MOVIE_TYPE.CGV]: cgvController,
    [MOVIE_TYPE.LOTTE]: lotteController
};

const getRegion = async (type) => {
    const result = await CONTROLLER[type].getRegions();

    return result;
};

const getTheatersByRegion = async (type, regionIndex) => {
    const result = await CONTROLLER[type].getTheatersByRegions(regionIndex);

    return result;
};

const getTimeTalbe = async (type, theaterLink) => {
    const result = await CONTROLLER[type].getTimeTable(theaterLink);

    return result;
};

const getBoxOffice = async () => {
    const result = await CONTROLLER[MOVIE_TYPE.CGV].getBoxOffice();

    return result;
};

const handler = async (event) => {
  let response = '';
  
  const { queryStringParameters } = event;

  try {
    if (queryStringParameters) {
      const { request, theater } = queryStringParameters;
      if (request === 'region') {
        response = await getRegion(theater);
      } else if (request === 'theaters') {
        response = await getTheatersByRegion(theater);
      } else if (reqeust === 'timetable') {
        response = await getTimeTalbe(theater);
      } else if (request === 'box-office') {
        response = await getBoxOffice(theater);
      }
    }

    return {
      statusCode: 200,
      body: JSON.stringify(response)
    };
  } catch (error) {
    console.log('Error' + error);
  }
};

module.exports.getRegion = getRegion;
module.exports.getTheatersByRegion = getTheatersByRegion;
module.exports.getTimeTalbe = getTimeTalbe;
module.exports.getBoxOffice = getBoxOffice;

exports.handler = handler;

controller 내에 내용은 공개하지 않겠습니다.
위 처럼 exports.handler 를 해줘서 Lambda 에서 부를 수 있도록 해줍니다.

마지막으로 return 타입을 정해주어야하는데, return 타입을 마음대로 정할 경우 에러가 나게 됩니다.
이유는 API GateWay 에서

클라이언트 요청 -> 메서드 요청 -> 통합 요청 -> Lambda -> 통합 응답 -> 메서드 응답 -> 클라이언트

순으로 응답이 전달 되는데 Lambda에서 특정 포맷에 맞지 않게 응답을 던져주면 에러가 나더라고요.

그래서 꼭 다음과 같은 포맷으로 Lambda 함수에 리턴값을 맞추어줘야합니다.

    return {
      statusCode: 200,
      body: JSON.stringify(response)
    }

참고 링크

이렇게 build.zip 파일을 람다에 올려주고 그 전에 만들었던 API 를 통해서 콜해보겠습니다.

그 전에 스크롤을 조금 밑으로 내려서 기본 설정 으로 갑니다. 여기서 응답 제한 시간 및 메모리를 수정하시면 되는데 제가 크롤링할 때에는 시간도 조금 걸리고 CPU도 조금 잡아 먹는것 같아서 시간과 크기를 조금 늘렸습니다.
한번 해보고 문제 없으시다면 그냥 디폴트 설정으로 사용하셔도 무관할것 같네요.

무튼 이제 진짜 테스트입니다 !!!!

제가 요청한 내용은 megaBox 에서 지원하는 지역들 리스트들입니다.

크롤링하는 내용은 추후에 올리거나 기회가 되면 써보도록 할게요.
딱히 어려운 내용이 아니니

무튼 AWS Lambda -> API Gateway -> Node server 를 통한 API구성하기가 끝이 났네요.

앞서 말햇듯이 아주 간단하게 Node 프로젝트를 구성하고 API를 만들 때 유용할것 같네요.

앞으로도 쭈욱 포스팅을 하면서 프로젝트를 생성해 나갈 예정인데 기록용으로도 좋고 보시는 분에게 도움이 되면 더더욱 좋을것 같네요.

profile
개발을 취미로 하는 개발자가 되고픔

0개의 댓글