AWS에 서버리스 Rust로 수익 알리미 만든 이야기
제가 뭔가 판매하는 어떤 웹사이트에서 발생한 수익을 그때그때 알고 싶었습니다. 이 수익 발생 이벤트를 제 휴대폰으로 즉시 알려주는 시스템을 구축한 이야기를 공유합니다. AWS 환경에서 서버리스(Serverless)로 간단한 알림 시스템을 Rust로 만든 이야기입니다. AWS의 많은 기능 중 이런 것도 있어서 유용하게 쓸 수 있다는 정보가 되리라 기대합니다.
내용
(목표) 무얼 왜 만들려 했나?
(수단) 뭘로 만들었나? 왜 그걸로 만들었나? 뭐가 좋았나?
(경험) Rust를 왜 써봤나? 뭐가 좋던가?
(추천) AWS Lambda, EventBridge, SNS, DynamoDB 이용후기
원하는 결과물
몇 달 전부터, 온라인에서 뭔가 팔기 시작했습니다. 아쉽게도 비공개 API를 써야 하는지라 어떤 웹사이트인지를 말씀드리기는 조심스럽습니다. 아무튼, 어떤 웹사이트에서 판매 수익이 발생하면, 그 수익을 곧바로 알고 싶더군요. 가끔씩 해당 웹사이트에 들어가서 수익금 현황 페이지를 새로고침하다가, 내가 가서 확인하는 게 아니라, 발생할 때마다 내 휴대폰으로 알림이 왔으면 좋겠다는 생각이 든 거죠.
- 판매 이벤트가 발생하면 내 휴대폰으로 알림을 받아보자.
어떻게 알림을 받을까?
가만 째려보니, 아래와 같은 구조로 개발하면 되겠다는 그림이 나왔습니다.
큰 관점에서 보면 위 그림으로 구현하면 될 것 같았고, 세부적인 내용은 AWS에서 제공하는 서비스들의 특성을 활용해서 조정해 가며 개발하기로 마음먹었습니다.
AWS Lambda 함수
AWS 람다 서비스는, 어떤 이벤트에 발생할 때, 내가 등록해 놓은 함수를 실행할 수 있는데, 함수는 다양한 언어로 작성할 수 있습니다. 자바나 파이썬, 자바스크립트는 물론이고, 다양한 언어환경을 기본으로 지원합니다. 인스턴스를 미리 하루종일 실행하는 것이 아니라, 함수를 등록해 놓으면, 이벤트에 대응해 실행한 시간만큼만 과금되는 서비스입니다. EC2나 라이트세일 같은 서비스의 인스턴스는 아무리 작은 인스턴스를 띄워도 내내 띄워두면 개인 사이드프로젝트 입장에서는 적지 않은 비용이 발생하는 부담이 있습니다만, 람다 같은 경우 사실상 거의 비용이 발생하지 않는 매력이 있습니다.
Rust 프로그래밍 언어
최근 Rust프로그래밍 언어를 조금 공부했습니다. 고성능의 저수준 프로그래밍을 할 수 있으면서, 언어 자체는 매우 고수준이라서 마음에 쏙 들었습니다. 기회가 되면 적극 활용해보고 싶다는 생각이 들었는데, 이번이 기회다 싶었습니다.
이번에는 람다 함수를 Rust언어로 작성해 보기로 마음먹었습니다. 기존에는 Scala랑 TypeScript(node.js)로 만들어서 배포해 본 경험이 있어요.
새로운 언어 적용 기회
새로운 언어를 적용하려면 새로 익혀야 할 것도 많고 시간이 오래 걸리기 마련입니다. 언어만 새로 알면 되는 게 아니라, 그 언어에서 주로 쓰는 주요 프레임워크들을 같이 공부해야 하는 시간과 노력이 꽤 필요해서, 이미 익숙한 언어와 프레임워크를 쓰는 게 더 현명한 판단일 가능성이 높습니다.
하지만, Rust를 새로 공부하기로 마음먹었으니, 이 좋은 기회를 살려야겠죠. 다행히 Rust언어 같은 경우 AWS에서 꽤 공격적으로 지원해 주려는 것으로 추측됩니다. 무려 공식 SDK가 있어요. (아직 개발자 프리뷰 버전입니다만 그게 어딘가요?!)
이번에도, 사실은 너무 오랜 시간이 걸릴 것 같으면 중도에 포기하고 그냥 스칼라로 개발하려고 했습니다만, 하고자 하는 작업이 간단했던 데다, Rust로 진행해 보니 생각보다 진척이 빨라서 무리 없이 끝까지 마무리할 수 있었습니다.
Cargo Lambda
Rust는 기본으로 포함된 cargo라는 패키지 매니저를 쓰는데, 이 cargo라는 툴의 플러그인 중에 AWS Lambda 서비스를 개발하고 배포하는데 특화된 게 있습니다.
Cargo Lambda is a subcommand for Cargo, the Rust package manager, to work with AWS Lambda.
Cargo Lambda provides tools and workflows to help you get started building Rust functions for AWS Lambda from scratch. When you’re ready to put your work in production, Cargo Lambda helps you build and deploy your functions on AWS Lambda in an efficient manner.
별다른 고민 없이 해당 플러그인에 있는 커맨드로 로컬에서 테스트해 보고, 람다함수로 배포하기도 쉬웠습니다. 새로운 언어를 쓰더라도, 실무 환경에 있는 사람들이 많이 쓰기 시작하면 문제없이 적용할 수 있게 되는 것 같습니다. Rust는 그런 면에서 충분한 합격점인 상황입니다.
AWS SDK for Rust
아직, 개발자 프리뷰상태이니 프로덕션 환경에서 쓰지 말라는 경고가 붙어있기는 합니다만, 그래도 AWS에서 공식적으로 만들어준 SDK가 있습니다. 평소 마이너 언어를 종종 쓰는 입장이다 보니, 공식 SDK의 존재가 새삼 감사하게 느껴집니다. 덕분에 Rust 환경에서 편리하게 AWS서비스에 접근할 수 있었습니다.
AWS EventBridge
이번에 처음 알게 된 서비스인데, AWS EventBridge라는 서비스가 있습니다. 이 EventBridge 서비스 안에 Scheduler라는 기능이 들어있어서, 이 기능을 통해 특정 시점이나 반복주기에 맞게 이벤트를 발행할 수 있습니다.
람다 함수 (1) — 목표 API를 폴링해서 신규 판매실적을 파악하자
이번 시스템은 간단한 람다 함수 2개로 구성했습니다. 첫 번째 함수는, 실행되면 목적 API에 접근해 JSON데이터를 읽어서 그중 새로운 판매정보를 읽는 역할을 담당하게 했어요. 위에 소개한 EventBridge에 등록해 둔 스케줄에 따라서 이 람다함수를 실행하고, 이 람다함수는 새로운 판매실적 정보를 기록하고 알림을 보낼 수 있도록 했어요.
이 첫 번째 람다함수가 매 10분마다 목표 API를 조회해서, 새 판매실적에 대해서만 SNS 토픽에 메시지로 발행하는 역할을 담당했어요. Rust로 개발하는 작업이 처음이라 조금 시간이 걸렸지만, 그래도 생각보다는 금방 결과물이 나왔습니다. 이 함수 개발에만 하루 쓴 것 같네요.
신규 데이터 확인
조회하고자 하는 비공식 API는 최근순서로 N건의 판매건수를 조회할 수 있었습니다.
[
{"amount": 10000, "at": "2023-08-11T02:30:00Z"},
{"amount": 11000, "at": "2023-08-10T12:00:00Z"}
]
이런 식으로 판매 실적이 담겨있었는데, 아쉽게도 PK 값으로 추정되는 건 따로 보이지는 않았습니다. 아쉬운 대로 몇 가지 고유해 보이는 값들을 조합해서 해쉬값을 만들어서 임의의 PK값을 만들어서 쓰기로 했습니다. 해당 데이터를 AWS DynamoDB 테이블에 넣어두고, 나름의 방식으로 생성한 PK값을 기준으로 기존 데이터인지 신규 데이터인지 파악하도록 합니다. 이미 기록된 판매실적이라면 무시하면 되겠죠.
AWS DynamoDB
AWS DynamoDB는 AWS가 관리해 주는 NoSQL데이터베이스인데, 이런 간단한 서비스에 활용하기에 매우 좋은 것 같습니다. 본래 목적은 대규모 트래픽에 유연하게 대처하는 데이터베이스 서비스입니다. 접근량이나 데이터 규모에 맞게 과금되는 식이라, 이처럼 작은 서비스에서 조금의 데이터만 활용하게 되면, 사실상 비용이 발생하지 않아서 더욱 매력적입니다. 아무리 작은 RDB인스턴스라도 개인 수준에서 비용은 적지가 않은데, 성능도 빠르게 쓸 수 있어서 아주 좋습니다.
AWS SNS
AWS SNS 서비스는 메시지 버스로 활용할 수 있습니다. 이벤트 메시지를 발행하면, 해당 토픽을 구독하고 있는 대상들에게 메시지가 전달됩니다. 토픽을 구독하는 대상은 다양한 종류가 올 수 있는데, 저는 Email과 Lambda실행을 연결해 두었습니다. 새로운 판매실적데이터가 특정 SNS토픽에 전달되면, 그 토픽을 구독하고 있는 이메일 주소로 해당 데이터가 메일로 발송되고, 지정한 람다함수(2)도 실행하도록 설정했습니다.
두 번째 람다함수를 개발하는 과정에는 이메일로만 구독해서, 신규 판매실적이 메시지로 잘 발행되는지를 확인하며 진행했고, 람다함수(2)의 경우 제가 이용하는 슬랙 채널에 메시지를 발행하도록 했어요.
람다함수 (2) — 신규 판매 실적을 내 휴대폰으로 알리자
두 번째 람다 함수는 이미 만든 첫번째 람다 함수를 살짝 바꾸면 됐기 때문에 금방 만들었습니다. SNS Topic에 구독하는 항목 중에 Lambda 함수를 실행할 수 있는 항목이 기본으로 있고, 특정 람다함수를 실행하면서 SNS에 전달된 메시지를 전달할 수 있습니다. 그럼 Lambda함수 입장에서는 해당 메시지를 읽어서 원하는 처리를 할 수 있게 됩니다. 어떤 서비스를 이용해서 제 휴대폰으로 메시지를 보낼까 생각해 보니, 곧바로 떠오르는 것은 카카오톡과 슬랙이었습니다. 카톡으로 메시지를 받으면 편리하겠는데, 개발자 문서를 좀 뒤져보니, 좀 시간이 걸릴 것 같습니다. 일단 예전에 연결해 봤던 슬랙을 쓰기로 합니다.
슬랙도 워낙 많이 쓰는 서비스이니까, 잘 찾아보면 SNS 토픽 구독해서 슬랙 메시지로 발행해 주는 기능이나 누군가 이미 만들어 공개해 둔 Lambda함수를 가져다 쓸 수 있을 것 같았지만, 뭐 어려운 기능은 아니니까 애써 찾느니 그냥 만들기로 합니다.
첫번째 람다함수를 좀 손봐서, 슬랙 메시지 발행하는 람다함수로 만들었어요.
전체 구조
두 파트로 나눠서 그렸던 전체 구조를 다시 그리면 아래와 같습니다.
람다 함수로 기능을 잘게 쪼개서 작은 일들만 처리하게 한 점이 제게는 재밌었습니다.
결론 및 정리
서버리스 시스템 참 편리한 것 같습니다. 이미 있는 탄탄한 서비스들에 설정만 걸어서, 내가 원하는 작업을 하되, 요금 비용과 시간 비용을 모두 절약할 수 있는 환경이라고 생각합니다.
과거 근무한 환경에서는 카프카나 별도 MQ도 쓰고 했었지만, 요새는 클라우드 환경에 이미 잘 갖춰진 메시지 버스나 큐 시스템이 있기 때문에 별도로 고민할 필요 없이 잘 쓰기만 하면 되는 상황이라 편리합니다.
러스트로 람다 함수를 만들어서 쓰는 것은 종종 더 해볼 것 같습니다. 이 글을 정리하는 동안 슬랙 알림을 한 건 받았습니다. 어쩌다 한 건 받으니까 더 자주 알고 싶어서 이런 시스템을 만들어봤나 봅니다. 사실 판매량이 많으면, 하루에 한번 총 판매액만 보면 될 텐데 말이죠.
암튼, AWS 서버리스 환경을 아직 안써보신 분들께는 한번 써보시는 것도 좋겠다고 추천드리면서 글을 마치겠습니다.
감사합니다.