디스코드 RSS 피드 봇 개발기
발행일 October 3, 2025 • 4분 소요 • 788 단어 • 다른 언어 선택: English
Table of contents
왜 디스코드 봇을 만들었나?
다소 뜬금 없게 여러가지 니즈들이 겹쳐서, 나는 여러 빅테크 기업들의 기술 블로그 피드를 가져와주는 “피드냥” 을 개발하기로 했다. 니즈들이란 다음과 같다.
- Ktor + Exposed 조합의 웹 애플리케이션 개발을 경험해보고 싶었다.
- Claude Code를 처음으로 한번 써보려 했다.
- 기술 블로그를 꾸준히 써야겠다는 필요성을 강하게 느꼈다.
뭘 만들어볼지 고민해보다가, 부트캠프에서 알게 된 지인이 디스코드 봇을 만들어본다길래 나도 한번 가볍게 만들어보고 싶어졌다. 가볍게 개발해볼 수 있는 개발 범위, 여태 해본 적 없는 디스코드 봇이라는 새로운 개발 영역, 그리고 나름 실제 사용자를 경험해볼 수 있다는 점이 나를 피드냥 개발로 이끌었다.
적정 아키텍처란 무엇일까?
하지만 시작하자마자 Ktor + Exposed 조합은 배제하기로 했다. AWS EventBridge 스케줄링을 통해 Lambda 함수를 호출하는 서버리스 구조가 비용 절감 측면에서도 효율적이고, 항상 실행되어 있을 필요도 없는 로직이니 타당하기도 했다. 그렇게 서버리스의 관점에서 바라보게 되니, JVM 계열 언어의 Lambda cold start 이슈가 부담스럽게 느껴졌다. 그래서 Ktor 사용 경험을 포기하고, 서버 프레임워크가 없는 간단한 함수를 만들기로 결정하게 되었다.
결론부터 정리하자면 단순히 경험해보고 싶었던 기술 스택들보다, 기술 블로그 피드 봇을 개발하기에 가장 적합한 적정 아키텍처를 최우선적으로 고려하게 되었다. 가벼운 프로젝트라도 그에 알맞는 적정 기술을 선택하는 요령을 길러놓는 것이 앞으로도 좋은 영향력을 발휘하지 않을까 생각했다.
Lambda를 구성하는 언어로는 Go 언어를 채택했다. TypeScript 또한 준수한 성능을 갖췄고 개발해본 경험도 있지만, 번들링과 트랜스파일링 과정을 거쳐야 한다는 점 때문에 근래에는 꺼리고 있는 중이다. 그래서 가볍고 빠른 성능을 갖춘 Go 언어로 개발을 진행하기로 했다.
그리고 MongoDB를 채택해서 채널에 등록된 피드 목록을 확인하고, 마지막으로 보낸 피드 정보를 등록해서 중복 전송을 방지하는 용도로 사용했다. DynamoDB도 잠깐 생각해봤지만 무료인 MongoDB의 M0 클러스터가 훨씬 더 매력적으로 다가왔다.
IaC 도구로 Pulumi를 채택했다. 이전 회사에서 써본 AWS CDK는 CloudFormation과 강결합되어 있는 느낌이라 진정한 의미의 IaC라고 보기엔 애매하다고 느꼈다. 그래서 과감하게 처음 써보는 IaC 도구를 냅다 도입했다. 써보니 확실히 AWS 내 서비스들만 구성할 수 있는 CDK와는 달리, MongoDB 클러스터 같은 컴포넌트도 함께 구성할 수 있다는 점이 큰 메리트로 느껴졌다.
마지막으로 Claude Code, AI Agent 첫 경험을 해보고 싶다는 것이 이번 간단 프로젝트를 시작하게 된 주요 계기였던 만큼 빼놓을 수 없는 요소다. 확실히 처음 써보는 Pulumi에 적응하는 데에도 큰 도움을 받았고, discordgo
와 같은 라이브러리도 허들 없이 자연스럽게 활용할 수 있었다.
간단한 앱에도 설계 과정이 필요하다
사실 초기에는 스케줄러 + 람다로 간단하게 구현할 수 있으리라 생각했다. 하지만 갑자기 커맨드를 지원해야 하지 않을까 하는 욕심이 생겼다. 고정된 피드 목록을 단방향으로 전송해주기 보다는, 피드 목록을 추가 및 삭제하고 현재 피드 목록을 확인해볼 수도 있는 커맨드가 있다면 확실히 상호작용도 가능한 봇으로서 의의를 갖출 수 있겠다는 생각이 들었다.
이렇게 된 김에 아키텍처 다이어그램 정도는 그리고 넘어가기로 했다. 이번 기회에 간단한 프로젝트를 만들더라도, 간단한 설계안 정도는 작성하고 넘어가는 것이 생각 정리에도 도움이 되고 개발 범위를 확정할 수 있어서 오히려 시간을 상당히 단축하는 과정이라는 것을 깨달았다.
또 무턱대고 MongoDB를 도입하기는 했지만, 사실 처음 써보는 것이었다. 그래서 MongoDB 스키마도 한번 정리하고 넘어가기로 했다. 문서형 NoSQL을 활용하기 위한 스키마는 어떻게 설계하는 것이 좋을까? 글쎄, 아직은 잘 모르겠다. 마치 JSON을 설계하듯이 디스코드 채널별로 피드 정보들을 모아놓는 방식으로 해놓긴 했는데, 나중에 MongoDB를 깊게 사용해보게 된다면 그 때 더 공부해봐야겠다.
{
"_id": ObjectId("discordChannelId"),
"feeds": [
{
"blogName": "Naver D2",
"rssUrl": "https://d2.naver.com/d2.atom",
"addedAt": ISODate("2024-12-30T10:00:00Z"),
"lastSentTime": ISODate("2024-12-30T10:00:00Z"),
"lastPostLink": "FE News 25년 9월 소식을 전해드립니다!",
"totalPostsSent": 100
}
],
"createdAt": ISODate("2024-12-30T10:00:00Z"),
"updatedAt": ISODate("2024-12-30T10:00:00Z")
}
트러블슈팅이라고 할 수 있을까
앞서 Pulumi로 MongoDB 클러스터도 구성할 수 있다고 얘기했지만 최초 구성하는 데에 3분 가량 소요되었고, M0 클러스터는 업데이트가 불가능하다는 이슈가 있었다. 따라서 Pulumi 인프라 구성은 사실상 AWS 위주로 진행하게 되었다. 하지만 그럼에도 시크릿 값을 관리하는 부분에서 CDK에 비해 압도적인 편의성을 느낄 수 있었다.
이번 프로젝트에서 가장 아쉬움으로 남는 부분은 바로 MongoDB IP 제한을 열어두었다는 점이다. IP 제한을 걸기 위해서는 NAT Gateway와 VPC 연결이 필요하다. 하지만 비용이 100배 가량 증가할 것으로 보여서 현재 프로젝트에서는 진행하지 않는 것으로 결정했다. 이것도 추후에 MongoDB를 또 쓰게 된다면 다시 도전해볼 만한 포인트로 남겨두어야겠다.
내부 로직 중에서는 중복 피드를 체크하는 로직이 꽤나 까다로웠다. 디스코드 채널 최초 등록 시 lastSentTime
값을 잘못 줘서 메시지 테러를 경험했다. 이는 새로운 피드 등록 시 해당 피드의 최근 글 기준으로 전송 시각 및 URL을 맞춰두는 것으로 해결했다. 또다른 문제는 한 피드에서 한 스케줄링 내에 2개 이상의 피드를 올렸을 때, 다음 스케줄링 때 동일한 피드가 올라오는 문제였다. 이는 rss.xml
같은 RSS 파일이 시간 역순으로 표현되기 때문에 시간 및 URL 기반 중복 체크 로직을 통과해버린 것으로 보인다. 그래서 플래그 값을 하나 두고 첫 아이템 대상으로 마지막 전송 글 정보를 갱신하도록 변경했다.
느낀 점
우선 AI Agent와 함께 만들어본 나름의 첫 아웃풋이라고 느껴져서 보람이 있었다. 앞으로도 AI 도구들을 적극적으로 활용하면서 아웃풋을 더 극대화할 수 있도록 해봐야겠다.
그리고 이제는 AI 덕분에 코드 작성 역량보다도 설계 역량이 더 중요해지고 있다는 걸 여실히 느꼈다. 이번에 이렇게 간단한 프로젝트를 만드는 데에도 설계 부분에서 턱턱 막히는 것을 보고, 스스로 설계 경험이 많이 부족했다는 점도 깨달았다.
그러니 이제 본격적으로 규모 있는 사이드 프로젝트를 만들어봐야 한다. 마음 같아서는 이번과 같은 연습 게임을 조금 더 해보는 것도 좋겠지만, 오래 전부터 꿈꿔 온 내 이름으로 만든 나만의 사이드 프로젝트를 이제는 서둘러서 만들어보고 싶어졌다.
그리고 블로그도 한달에 한번은 꼭 쓰도록 하자.