Lambda 로 데일리 알람 받아보기
본 글에서는 AWS Lambda를 활용하여 매일 원하는 뉴스 기사를 이메일로 받아보는 방법에 대해 자세히 설명합니다.
예전에 재미로 6시간마다 날씨 정보를 [카카오톡 나와의 메세지] API 를 통해 보내는 코드를 작성한 적이 있었다. (https://github.com/iceCreamParlor/DailyAlarm)
JSON 라이브러리를 제외하고는 순수 자바 코드로 작성했고, 공공데이터포털의 [기상청 동네예보 정보조회 API] 와 카카오톡의 [나에게 보내기 API] 를 사용해서 구현했었다. 운영하는 서버는 가지고 있던 EC2 인스턴스에 직접 Java 프로세스를 돌려서 사용했었다. 이 때의 문제점은 크게 두 가지가 있었다.
1.하루에 몇 번 호출되지 않음에도, EC2 인스턴스 자원을 계속 사용해야 했던 점
- 사실 낭비가 된 것은 아니고, 그 기회로 다른 웹서비스, DB 도 호스팅했었다.
2.DB 를 사용하고 있지 않아서, 카카오톡 관련 Access Token, Refresh Token 을 메모리에 저장시켜서 사용해야 했다.
- 이러한 이유료, 한 번 Refresh Token 이 만료되어 Token 값이 꼬여버리자, 문제가 발생하였고 서비스 자체가 죽어버렸다.
3. 날씨 데이터가 생각보다 궁금하지 않았다 (?)
- 재미삼아 만들어 보긴 했지만, 생각보다 실용적이지 않다는 생각이 들었다.
이러한 문제점들 때문에, 새로운 방식, 더 생산성있는 컨텐츠, 더 효율성있는 개발 도구 를 이용해서 비슷한 역할을 하는 시스템을 만들었다. 사용한 스택은 다음과 같다.
- AWS CloudWatch (Lambda 를 하루에 한 번 Trigger 한다.)
- AWS Lambda (실질적인 로직을 수행함)
- AWS S3
- Typescript (자바에 비해 높은 생산성을 지니는 Javascript / 타입 안정성을 위한 Typescript)
- Serverless (Infrastructure As Code 를 도와주는 라이브러리)
제공하는 컨텐츠도 날씨 데이터에서, 크롤링한 뉴스 기사를 이메일로 보내주는 것으로 바꾸었다. (더 높은 흥미 + 쉬운 유지보수성)
Lambda Function 수행 코드는 다음과 같다. (실질적으로 메일 보내는 함수, 크롤링하는 함수는 생략)
https://github.com/iceCreamParlor/daily
// handler.ts
import { sendMail } from "./gmail";
import { Handler, Context, Callback } from "aws-lambda";
import AWS from "aws-sdk";
import "dotenv/config";
import { getNaverNews } from "./newsCrawler";
interface Response {
statusCode: number;
body: string;
}
const retry = 10;
const handler: Handler = async (
event: any,
context: Context,
callback: Callback<any>
) => {
// 실패할 경우를 대비해서 10회 시도한다.
for (var i = 1; i <= retry; i++) {
try {
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_IAM_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_IAM_SECRET_ACCESS_KEY,
});
/**
* S3 로부터 정보를 얻을 JSON 파일을 읽어온다.
*/
const s3Response: any = await s3
.getObject({
Bucket: "heejae-bucket",
Key: "daily.json",
})
.promise();
const s3Json: any = JSON.parse(s3Response["Body"].toString("utf-8"));
const s3Result: any = {
keyword: s3Json.crawler.keyword as string[],
email: s3Json.crawler.email as string[],
};
console.log(`s3Result ${s3Result}`);
/**
* 네이버에서 키워드들을 하나씩 크롤링해서,
* 메일 컨텐츠를 채워 나간다.
*/
let mailContent = (
await Promise.all(
s3Result.keyword.map(async (k: string) => await getNaverNews(k))
)
).join("\n");
/**
* 전송할 이메일 리스트를 보고, 이메일을 발송한다.
*/
await Promise.all(
s3Result.email.map(
async (e: string) =>
await sendMail(
e,
`${new Date().toISOString().slice(0, 10)} 기사`,
mailContent
)
)
);
break;
} catch (err) {
console.log(`${i} 번째 시도 실패`);
console.error(err);
if (i >= retry) {
// 10 회 이상 시도했는데도 실패하면 실패코드 반환
return {
statusCode: 500,
body: JSON.stringify({
message: "Fail",
}),
};
}
// 10회보다 적게 시도되었으면 다시 시도
continue;
}
}
return {
statusCode: 200,
body: JSON.stringify({
message: "Success",
}),
};
};
export { handler };
이를 배포할 serverless 설정은 다음과 같다.
service: daily
frameworkVersion: '2'
plugins:
- serverless-plugin-typescript
- serverless-offline
- serverless-dotenv-plugin
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-2
environment:
NODE_ENV: production
functions:
dailyInfo:
handler: src/handler.handler
events:
- schedule:
name: daily-info-cloudwatch
description: 'daily info cloudwatch'
# 오전 9시에 Trigger
rate: cron(0 0 * * ? *)
이것도 읽어보세요