분류 Nodejs

Lambda의 흔한 Node.js 오류

컨텐츠 정보

  • 조회 363 (작성일 )

본문

Node.js 서버리스 프로젝트로 많은 클라이언트를 도왔습니다. 그렇게 하면서 async/await 주위에서 반복되는 실수를 보았습니다.


https://dev.to/theburningmonk/common-node-js-mistakes-in-lambda-4a4j 



콜백을 계속 사용 중 


많은 사람들이 여전히 비동기 처리기 함수에서 콜백을 사용하고 있습니다.

module.exports.handler = async (event, context, cb) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  cb(null, response)
}

더 간단한 대안 대신 :

module.exports.handler = async (event, context) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({ message: 'hello world' })
  }

  return response
}


promisify을 사용하지 않음 


Node8 이전에는 블루 버드가 큰 격차를 채웠습니다. 콜백 기반 함수를 약속 기반으로 변환하는 유틸리티를 제공했습니다. 그러나 Node8의 내장 util 모듈은 약속 된 기능으로 그 차이를 메웠다.


예를 들어, 다음과 같이 fs 모듈에서 readFile 함수를 변환 할 수 있습니다.


const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)


더 이상 블루 버드를 사용할 필요가 없습니다. 이는 의존성이 낮아서 함수의 콜드 스타트 ​​시간을 줄이는 데 도움이 됩니다.


너무 순차적 


async/await를 사용하면 비동기식 코드를 동기식으로 작성할 수 있습니다. 더 이상 콜백 지옥을 다루지 않습니다!


반대로, 우리는 트릭을 놓칠 수 있고 적절한 곳에서 작업을 동시에 수행 할 수 없습니다.


다음 코드를 예로 들어 보겠습니다.


async function getFixturesAndTeam(teamId) {
  const fixtures = await fixtureModel.fetchAll()
  const team = await teamModel.fetch(teamId)
  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

이 기능은 이해하기 쉽지만 거의 최적화되지 않습니다. teamModel.fetch는 fixtureModel.fetchAll의 결과에 의존하지 않으므로 동시에 실행해야 합니다.


개선 할 수 있는 방법은 다음과 같습니다.


async function getFixturesAndTeam(teamId) {
  const fixturesPromise = fixtureModel.fetchAll()
  const teamPromise = teamModel.fetch(teamId)

  const fixtures = await fixturesPromise
  const team = await teamPromise

  return {
    team,
    fixtures: fixtures.filter(x => x.teamId === teamId)
  }
}

이 버전에서는 fixtureModel.fetchAll과 teamModel.fetch가 동시에 시작됩니다.


async/await와 함께 맵을 사용할 때도 주의해야 합니다. 다음은 teamModel.fetch를 차례로 호출합니다.

async function getTeams(teamIds) {
  const teams = _.map(teamIds, id => await teamModel.fetch(id))
  return teams
}

대신 다음과 같이 작성해야 합니다.

async function getTeams(teamIds) {
  const promises = _.map(teamIds, id => teamModel.fetch(id))
  const teams = await Promise.all(promises)
  return teams
}


이 버전에서는 teamIds를 Promise 배열에 매핑합니다. 그런 다음 Promise.all을 사용하여 이 배열을 팀의 배열을 반환하는 단일 Promise로 바꿀 수 있습니다.


이 경우 teamModel.fetch가 동시에 호출되며 실행 시간을 크게 향상 시킬 수 있습니다.


async/await inside forEach() 


이것은 까다롭고 때로는 숙련 된 Node.js 개발자조차도 잡을 수 있습니다.


문제는 이와 같은 코드가 예상대로 작동하지 않는다는 것입니다.


[ 1, 2, 3 ].forEach(async (x) => {
  await sleep(x)
  console.log(x)
})

console.log('all done.')


이것을 실행하면 다음과 같은 결과가 나타납니다.


all done. 


이것이 작동하지 않는 이유에 대한 자세한 설명은 이 게시물을 참조하십시오. 지금은 forEach 내부에서 async/await를 사용하지 마십시오.


Not using AWSSDK’s .promise() 


AWS SDK 클라이언트가 콜백과 약속을 모두 지원한다는 것을 알고 있습니까? AWS SDK와 함께 async/await를 사용하려면 다음과 같이 클라이언트 메소드에 .promise()를 추가하십시오.


const AWS = require('aws-sdk')
const Lambda = new AWS.Lambda()

async function invokeLambda(functionName) {
  const req = {
    FunctionName: functionName,
    Payload: JSON.stringify({ message: 'hello world' })
  }
  await Lambda.invoke(req).promise()
}


더 이상 콜백 함수가 없습니다.


마무리 


Lambda에서 Node.js로 작업 할 때 피해야 할 5 가지 실수입니다. 프로덕션 용 서버리스 응용 프로그램 및 운영 모범 사례를 구축하는 방법에 대한 자세한 내용은 저를 따르십시오.