본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
AI Generated
2026. 1. 31. · 14 Views
Cron과 Webhooks 완벽 가이드
Node.js 환경에서 자동화의 핵심인 Cron 작업과 Webhooks를 활용하는 방법을 다룹니다. 정기적인 작업 스케줄링부터 외부 서비스 연동까지, 실무에서 바로 적용할 수 있는 자동화 기법을 배워봅니다.
목차
1. 자동화의 힘
김개발 씨는 매일 아침 9시마다 팀원들에게 일일 리포트를 보내야 했습니다. 처음에는 직접 버튼을 눌러 발송했지만, 어느 날 중요한 회의에 참석하느라 발송을 깜빡했습니다.
팀장님께 한소리 들은 김개발 씨는 생각했습니다. "이걸 자동으로 할 수는 없을까?"
자동화란 사람이 반복적으로 수행해야 하는 작업을 컴퓨터가 대신 처리하도록 만드는 것입니다. 마치 알람 시계가 매일 같은 시간에 울리는 것처럼, 서버도 정해진 시간에 정해진 일을 수행할 수 있습니다.
자동화를 통해 실수를 줄이고, 개발자는 더 중요한 일에 집중할 수 있게 됩니다.
다음 코드를 살펴봅시다.
// 자동화가 필요한 전형적인 상황들
const manualTasks = {
// 매일 반복되는 작업
dailyReport: '매일 아침 9시 리포트 발송',
backupDatabase: '매일 새벽 3시 데이터베이스 백업',
cleanupLogs: '매주 일요일 로그 파일 정리',
// 외부 이벤트에 반응해야 하는 작업
newUserSignup: '새 회원가입 시 환영 이메일 발송',
paymentComplete: '결제 완료 시 영수증 생성',
emailReceived: '이메일 수신 시 알림 전송'
};
// 이 모든 것을 Cron과 Webhooks로 자동화할 수 있습니다
김개발 씨는 스타트업에서 백엔드 개발을 담당하는 2년차 개발자입니다. 회사가 성장하면서 그가 처리해야 할 반복 작업들이 점점 늘어났습니다.
매일 아침 리포트 발송, 매주 사용자 통계 집계, 매월 청구서 생성까지. 어느 순간 김개발 씨는 하루의 절반을 이런 단순 반복 작업에 쓰고 있다는 것을 깨달았습니다.
선배 개발자 박시니어 씨가 이런 김개발 씨를 보며 말했습니다. "개발자가 반복 작업을 직접 하고 있으면 안 되죠.
그건 컴퓨터한테 시키는 겁니다." 그렇다면 자동화란 정확히 무엇일까요? 쉽게 비유하자면, 자동화는 마치 집안의 스마트 가전제품과 같습니다.
예전에는 매일 저녁 직접 보일러를 켜야 했지만, 이제는 타이머를 설정해두면 알아서 작동합니다. 로봇 청소기는 설정된 시간에 스스로 청소를 시작하고, 세탁기는 예약된 시간에 빨래를 돌립니다.
서버의 자동화도 이와 똑같은 원리입니다. 자동화가 없던 시절에는 어땠을까요?
개발자들은 매번 수동으로 스크립트를 실행해야 했습니다. 새벽 3시에 백업을 돌리려면 진짜로 새벽 3시에 일어나야 했습니다.
물론 그렇게 하는 사람은 없었고, 결국 백업을 잊어버리거나 불규칙하게 수행했습니다. 더 큰 문제는 외부 서비스와의 연동이었습니다.
결제 서비스에서 결제가 완료되었는지 확인하려면 계속해서 API를 호출해 상태를 확인해야 했습니다. 이런 방식은 서버 자원을 낭비하고, 실시간성도 떨어졌습니다.
바로 이런 문제를 해결하기 위해 두 가지 자동화 방식이 등장했습니다. 첫 번째는 Cron입니다.
Cron은 시간 기반 자동화를 담당합니다. "매일 아침 9시에", "매주 월요일 자정에", "매월 1일 오전 10시에"처럼 정해진 시간에 작업을 실행합니다.
마치 알람 시계처럼 작동한다고 생각하면 됩니다. 두 번째는 Webhooks입니다.
Webhooks는 이벤트 기반 자동화를 담당합니다. "결제가 완료되면", "새 이메일이 도착하면", "GitHub에 코드가 푸시되면"처럼 특정 이벤트가 발생했을 때 작업을 실행합니다.
마치 초인종이 울리면 문을 열어보는 것과 같습니다. 실제 현업에서는 이 두 가지를 조합해서 사용합니다.
예를 들어 이커머스 서비스를 운영한다고 가정해봅시다. Cron으로는 매일 새벽 배송 현황을 집계하고, 매주 판매 리포트를 생성합니다.
Webhooks로는 결제가 완료되면 즉시 주문 확인 이메일을 보내고, 배송이 시작되면 고객에게 알림을 전송합니다. 이렇게 하면 개발자가 직접 개입하지 않아도 시스템이 알아서 돌아갑니다.
박시니어 씨가 덧붙였습니다. "자동화의 핵심은 신뢰성이에요.
사람은 깜빡할 수 있지만, 잘 만든 자동화 시스템은 24시간 365일 쉬지 않고 일하니까요." 다시 김개발 씨의 이야기로 돌아가봅시다. 박시니어 씨의 조언을 들은 김개발 씨는 고개를 끄덕였습니다.
"그렇군요! 그럼 Cron부터 배워볼게요." 자동화를 제대로 이해하고 활용하면 개발자로서의 생산성이 크게 향상됩니다.
반복 작업에서 해방되어 정말 중요한 문제 해결에 집중할 수 있게 됩니다.
실전 팁
💡 - 자동화 대상 선정 시 "이 작업을 매번 똑같이 반복하고 있는가?"를 기준으로 판단하세요
- Cron은 시간 기반, Webhooks는 이벤트 기반이라는 점을 명확히 구분하세요
- 자동화 시스템에는 반드시 실패 알림과 로깅을 추가하세요
2. Cron 작업 설정 방법
김개발 씨는 자동화의 중요성을 깨달았지만, 막상 Cron을 설정하려니 막막했습니다. 구글링을 해보니 "0 9 * * *"같은 이상한 문자열이 나오는데, 이게 도대체 무슨 뜻인지 알 수가 없었습니다.
"이거 외계어 아니에요?" 박시니어 씨가 웃으며 대답했습니다. "처음엔 다 그래요.
규칙만 알면 아주 간단해요."
Cron 표현식은 작업이 실행될 시간을 지정하는 문자열입니다. 다섯 개의 필드로 구성되어 있으며, 각각 분, 시, 일, 월, 요일을 나타냅니다.
마치 시계의 분침, 시침처럼 각 필드가 특정 시간을 가리키는 것이라고 생각하면 됩니다. Node.js에서는 node-cron 라이브러리를 사용해 쉽게 구현할 수 있습니다.
다음 코드를 살펴봅시다.
// node-cron 라이브러리 사용
const cron = require('node-cron');
// Cron 표현식: 분 시 일 월 요일
// * * * * *
// │ │ │ │ │
// │ │ │ │ └── 요일 (0-7, 0과 7은 일요일)
// │ │ │ └────── 월 (1-12)
// │ │ └────────── 일 (1-31)
// │ └─────────────── 시 (0-23)
// └──────────────────── 분 (0-59)
// 매일 아침 9시에 실행
cron.schedule('0 9 * * *', () => {
console.log('아침 9시 리포트를 발송합니다');
sendDailyReport();
});
// 매주 월요일 오전 10시에 실행
cron.schedule('0 10 * * 1', () => {
console.log('주간 통계를 집계합니다');
generateWeeklyStats();
});
김개발 씨가 처음 마주한 Cron 표현식은 정말 외계어처럼 보였습니다. "0 9 * * *"라니, 도대체 이게 무슨 의미일까요?
박시니어 씨가 화이트보드에 그림을 그리며 설명하기 시작했습니다. "Cron 표현식은 다섯 개의 상자로 이루어져 있어요.
각 상자는 시간의 특정 부분을 담당하죠." 쉽게 비유하자면, Cron 표현식은 마치 약속 시간을 정하는 것과 같습니다. "매주 월요일 오전 10시에 만나자"라고 말하는 것처럼, Cron에게 "매주 월요일 오전 10시에 이 작업을 해줘"라고 말하는 것입니다.
다만 사람의 언어가 아닌 다섯 개의 숫자로 표현할 뿐입니다. 다섯 개의 필드는 왼쪽부터 순서대로 분, 시, 일, 월, 요일을 나타냅니다.
첫 번째 필드는 분입니다. 0부터 59까지의 값을 가질 수 있습니다.
"30"이라고 쓰면 매 시간의 30분에 실행됩니다. 두 번째 필드는 시입니다.
0부터 23까지의 값을 사용합니다. "9"라고 쓰면 오전 9시를 의미합니다.
24시간 형식이므로 오후 3시는 "15"로 표현합니다. 세 번째 필드는 일입니다.
1부터 31까지의 값으로 날짜를 지정합니다. "1"이라고 쓰면 매월 1일에 실행됩니다.
네 번째 필드는 월입니다. 1부터 12까지의 값을 사용합니다.
"6"이라고 쓰면 6월에만 실행됩니다. 다섯 번째 필드는 요일입니다.
0부터 7까지의 값을 사용하며, 0과 7은 모두 일요일입니다. 1은 월요일, 6은 토요일입니다.
여기서 핵심은 **별표(*)**입니다. 별표는 "모든 값"을 의미합니다.
"0 9 * * *"를 해석해보면, 분은 0분, 시는 9시, 일은 모든 날, 월은 모든 월, 요일은 모든 요일입니다. 즉, "매일 아침 9시 정각에"라는 뜻이 됩니다.
박시니어 씨가 몇 가지 예시를 더 들어주었습니다. "*/5 * * * *"에서 "/5"는 "5마다"라는 뜻입니다.
즉, 5분마다 실행됩니다. "0 0 * * 0"은 매주 일요일 자정에 실행됩니다.
"0 0 1 * *"은 매월 1일 자정에 실행됩니다. Node.js에서 Cron을 사용하려면 node-cron 라이브러리를 설치해야 합니다.
npm install node-cron 명령어로 설치한 후, require로 불러와서 사용합니다. schedule 함수의 첫 번째 인자로 Cron 표현식을, 두 번째 인자로 실행할 함수를 전달하면 됩니다.
주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 서버 시간대를 고려하지 않는 것입니다.
한국 시간으로 아침 9시를 원한다면, 서버가 UTC 시간대를 사용하는 경우 "0 0 * * *"로 설정해야 합니다. 9시간의 시차가 있기 때문입니다.
김개발 씨가 고개를 끄덕였습니다. "아, 생각보다 간단하네요!
다섯 개 상자에 숫자만 넣으면 되는 거잖아요." 박시니어 씨가 웃으며 말했습니다. "맞아요.
처음에만 어려워 보이지, 몇 번 써보면 금방 익숙해져요. 그리고 요즘은 crontab.guru 같은 사이트에서 표현식을 쉽게 만들 수도 있어요."
실전 팁
💡 - crontab.guru 웹사이트에서 Cron 표현식을 시각적으로 확인하고 생성할 수 있습니다
- 서버 시간대와 원하는 실행 시간대를 항상 확인하세요
- 처음에는 테스트를 위해 1분마다 실행되는 "* * * * *"로 설정해보세요
3. src cron 코드 분석
김개발 씨는 회사의 기존 프로젝트 코드를 살펴보기로 했습니다. src/cron 폴더를 열어보니 여러 개의 파일이 있었습니다.
"선배, 이 구조는 왜 이렇게 되어 있는 거예요?" 박시니어 씨가 대답했습니다. "실무에서는 Cron 작업을 체계적으로 관리해야 해요.
그래서 이런 구조를 사용하는 거예요."
실무에서 Cron 작업을 관리할 때는 모듈화된 구조가 필수입니다. 각 작업을 별도의 파일로 분리하고, 중앙에서 등록하는 방식을 사용합니다.
이렇게 하면 새로운 작업 추가, 기존 작업 수정, 작업 비활성화가 훨씬 쉬워집니다. 마치 레고 블록처럼 원하는 작업을 끼워 넣고 빼는 것이 가능해집니다.
다음 코드를 살펴봅시다.
// src/cron/index.js - Cron 작업 중앙 관리
const cron = require('node-cron');
const dailyReport = require('./jobs/dailyReport');
const weeklyBackup = require('./jobs/weeklyBackup');
const cleanupLogs = require('./jobs/cleanupLogs');
// 모든 Cron 작업을 등록하는 함수
function initializeCronJobs() {
// 매일 아침 9시 - 일일 리포트
cron.schedule('0 9 * * *', dailyReport, {
scheduled: true,
timezone: 'Asia/Seoul'
});
// 매주 일요일 새벽 3시 - 백업
cron.schedule('0 3 * * 0', weeklyBackup, {
scheduled: true,
timezone: 'Asia/Seoul'
});
// 매일 자정 - 로그 정리
cron.schedule('0 0 * * *', cleanupLogs, {
scheduled: true,
timezone: 'Asia/Seoul'
});
console.log('모든 Cron 작업이 등록되었습니다');
}
module.exports = { initializeCronJobs };
김개발 씨가 처음 src/cron 폴더를 열었을 때, 여러 개의 파일이 체계적으로 정리되어 있는 것을 발견했습니다. index.js 파일이 있고, jobs라는 하위 폴더 안에 dailyReport.js, weeklyBackup.js 같은 파일들이 있었습니다.
박시니어 씨가 설명을 시작했습니다. "처음에는 하나의 파일에 모든 Cron 작업을 때려 넣고 싶은 유혹이 들어요.
하지만 작업이 10개, 20개로 늘어나면 그 파일은 수백 줄의 괴물이 되어버리죠." 쉽게 비유하자면, 이 구조는 마치 회사의 조직도와 같습니다. 사장님(index.js)이 각 팀장(개별 job 파일)에게 업무를 지시하는 구조입니다.
사장님은 전체 일정을 관리하고, 각 팀장은 자기 업무만 책임집니다. 새로운 팀이 필요하면 팀장 하나를 추가하면 되고, 팀을 없애려면 그 팀장만 제거하면 됩니다.
먼저 index.js 파일의 역할을 살펴보겠습니다. 이 파일은 모든 Cron 작업의 중앙 관리 센터입니다.
각 job 파일을 불러오고, 언제 실행할지 스케줄을 설정합니다. 서버가 시작될 때 initializeCronJobs 함수가 호출되면, 모든 Cron 작업이 일괄 등록됩니다.
코드를 자세히 보면 timezone 옵션이 눈에 띕니다. 이 옵션으로 'Asia/Seoul'을 지정하면 한국 시간 기준으로 작업이 실행됩니다.
이 옵션이 없으면 서버의 기본 시간대를 따르는데, 클라우드 서버는 대부분 UTC를 사용하므로 시간이 맞지 않는 문제가 발생할 수 있습니다. 다음으로 개별 job 파일의 구조를 살펴보겠습니다.
각 job 파일은 하나의 함수를 export합니다. 이 함수 안에는 해당 작업의 로직만 담겨 있습니다.
dailyReport.js는 리포트 생성과 발송만 담당하고, weeklyBackup.js는 백업만 담당합니다. 서로 간섭하지 않고 독립적으로 동작합니다.
왜 이런 구조가 좋을까요? 첫째, 유지보수가 쉽습니다.
리포트 발송 로직을 수정해야 한다면 dailyReport.js만 열면 됩니다. 다른 코드를 건드릴 필요가 없습니다.
둘째, 테스트가 쉽습니다. 각 job 함수를 독립적으로 테스트할 수 있습니다.
전체 Cron 시스템을 돌리지 않아도 개별 작업의 동작을 확인할 수 있습니다. 셋째, 팀 협업이 쉽습니다.
한 사람이 리포트 기능을, 다른 사람이 백업 기능을 개발해도 충돌이 발생하지 않습니다. 실무에서는 여기에 에러 처리와 로깅을 추가합니다.
각 job 함수를 try-catch로 감싸고, 작업 시작과 완료, 그리고 에러 발생을 모두 로그에 기록합니다. 새벽에 작업이 실패해도 아침에 로그를 보고 원인을 파악할 수 있어야 합니다.
김개발 씨가 질문했습니다. "그러면 새로운 Cron 작업을 추가하려면 어떻게 해야 해요?" 박시니어 씨가 대답했습니다.
"간단해요. jobs 폴더에 새 파일을 만들고, index.js에서 그 파일을 불러와서 스케줄만 등록하면 끝이에요.
레고 블록 끼우듯이요." 김개발 씨는 이제 왜 프로젝트가 이런 구조로 되어 있는지 이해했습니다. 처음에는 복잡해 보였지만, 알고 보니 미래의 확장과 유지보수를 위한 현명한 선택이었습니다.
실전 팁
💡 - 각 job 파일에는 반드시 에러 처리와 로깅을 추가하세요
- 환경 변수로 Cron 작업의 활성화 여부를 제어하면 테스트 환경에서 편리합니다
- 작업 실행 시간이 긴 경우 중복 실행 방지 로직을 추가하세요
4. 웹훅 엔드포인트 구현
김개발 씨는 Cron으로 시간 기반 자동화를 마스터했습니다. 그런데 새로운 요구사항이 들어왔습니다.
"결제가 완료되면 즉시 알림을 보내야 해요." 김개발 씨는 고민에 빠졌습니다. Cron으로 1분마다 결제 상태를 확인할 수는 있지만, 그러면 최대 1분의 지연이 생깁니다.
더 좋은 방법이 없을까요?
Webhooks는 이벤트가 발생했을 때 외부 서비스가 우리 서버로 알림을 보내는 방식입니다. 마치 택배가 도착하면 문 앞에서 초인종을 누르는 것과 같습니다.
우리가 계속 문 앞을 확인할 필요 없이, 택배가 오면 알아서 알려주는 것입니다. 이를 위해 우리 서버에 알림을 받을 수 있는 엔드포인트를 만들어야 합니다.
다음 코드를 살펴봅시다.
// Express.js로 웹훅 엔드포인트 구현
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// 웹훅 서명 검증 함수
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return signature === expectedSignature;
}
// 결제 완료 웹훅 엔드포인트
app.post('/webhooks/payment', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.WEBHOOK_SECRET;
// 1. 서명 검증 - 보안을 위해 필수!
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: '유효하지 않은 서명' });
}
// 2. 이벤트 처리
const { eventType, data } = req.body;
if (eventType === 'payment.completed') {
console.log('결제 완료:', data.orderId);
sendPaymentNotification(data);
}
// 3. 빠른 응답 (200 OK)
res.status(200).json({ received: true });
});
김개발 씨는 결제 서비스 연동을 맡게 되었습니다. 처음에는 단순하게 생각했습니다.
Cron으로 매 분마다 결제 서비스 API를 호출해서 새로운 결제가 있는지 확인하면 되지 않을까? 박시니어 씨가 고개를 저었습니다.
"그건 폴링(Polling) 방식이에요. 택배가 왔는지 1분마다 문 앞을 확인하러 나가는 거죠.
비효율적이고, 최대 1분의 지연도 생겨요." 그렇다면 어떻게 해야 할까요? 박시니어 씨가 비유를 들어 설명했습니다.
"택배 알림 서비스를 생각해보세요. 택배가 도착하면 초인종을 누르거나 문자를 보내죠.
우리가 계속 확인하러 나갈 필요가 없어요. Webhooks도 마찬가지예요.
결제 서비스가 결제 완료되면 우리 서버로 직접 알려주는 거예요." Webhooks를 구현하려면 우리 서버에 알림을 받을 수 있는 엔드포인트를 만들어야 합니다. 엔드포인트란 외부에서 접근할 수 있는 URL입니다.
예를 들어 https://myserver.com/webhooks/payment 같은 주소를 만들고, 결제 서비스에 "결제가 완료되면 이 주소로 알려줘"라고 등록합니다. 그러면 결제 서비스는 결제 완료 시 해당 URL로 HTTP POST 요청을 보냅니다.
코드를 자세히 살펴보겠습니다. 가장 중요한 부분은 서명 검증입니다.
아무나 우리 웹훅 엔드포인트에 가짜 요청을 보낼 수 있기 때문입니다. 악의적인 사용자가 "결제 완료됐어!"라는 가짜 요청을 보내면 큰 문제가 됩니다.
이를 방지하기 위해 결제 서비스는 요청에 서명을 포함합니다. 이 서명은 우리와 결제 서비스만 아는 비밀 키로 생성됩니다.
verifyWebhookSignature 함수는 이 서명이 유효한지 검증합니다. 요청 본문과 비밀 키를 사용해 우리도 서명을 만들어보고, 받은 서명과 일치하는지 확인합니다.
일치하면 진짜 결제 서비스에서 보낸 요청이고, 일치하지 않으면 누군가 조작한 가짜 요청입니다. 다음으로 중요한 것은 빠른 응답입니다.
웹훅 요청을 받으면 가능한 한 빨리 200 OK 응답을 보내야 합니다. 대부분의 서비스는 5초 이내에 응답을 받지 못하면 요청이 실패했다고 판단하고 재시도합니다.
따라서 무거운 작업은 비동기로 처리하고, 응답은 즉시 보내는 것이 좋습니다. 주의할 점이 있습니다.
웹훅은 네트워크 문제로 실패할 수 있습니다. 좋은 서비스들은 실패 시 재시도를 하지만, 우리 서버도 멱등성을 보장해야 합니다.
즉, 같은 웹훅이 여러 번 와도 결과가 같아야 합니다. 주문 ID를 기준으로 중복 처리를 방지하는 로직이 필요합니다.
김개발 씨가 감탄했습니다. "아, 그래서 결제 서비스 문서에 웹훅 URL을 등록하라고 했군요!" 박시니어 씨가 고개를 끄덕였습니다.
"맞아요. 웹훅을 등록하면 결제가 완료되는 즉시 알림을 받을 수 있어요.
폴링보다 훨씬 효율적이고 실시간에 가깝죠."
실전 팁
💡 - 웹훅 서명 검증은 보안을 위해 반드시 구현하세요
- 응답은 최대한 빠르게 보내고, 무거운 작업은 비동기로 처리하세요
- 동일한 웹훅이 여러 번 올 수 있으므로 멱등성을 보장하세요
5. Gmail Pub Sub 통합
김개발 씨의 회사에서는 고객 문의 이메일을 Gmail로 받고 있었습니다. 기존에는 직원이 주기적으로 이메일을 확인했는데, 중요한 문의를 놓치는 일이 잦았습니다.
"이메일이 오면 자동으로 슬랙에 알림을 보낼 수 없을까요?" 팀장님의 요청에 김개발 씨는 Gmail과 Webhooks를 연동하는 방법을 찾아보기 시작했습니다.
Gmail Pub/Sub는 Gmail에 새 이메일이 도착하면 알림을 받을 수 있는 Google Cloud의 기능입니다. Pub/Sub는 Publisher(발행자)와 Subscriber(구독자) 패턴의 약자로, Gmail이 이벤트를 발행하면 우리 서버가 구독해서 알림을 받는 구조입니다.
마치 유튜브 채널을 구독하면 새 영상이 올라올 때 알림을 받는 것과 같습니다.
다음 코드를 살펴봅시다.
// Gmail Pub/Sub 웹훅 처리
const express = require('express');
const { google } = require('googleapis');
const app = express();
app.use(express.json());
// Gmail Pub/Sub 웹훅 엔드포인트
app.post('/webhooks/gmail', async (req, res) => {
// Pub/Sub 메시지 디코딩
const message = req.body.message;
const data = JSON.parse(
Buffer.from(message.data, 'base64').toString()
);
console.log('Gmail 알림 수신:', data);
// historyId를 사용해 변경된 메시지 조회
const { emailAddress, historyId } = data;
try {
// Gmail API로 새 메시지 가져오기
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
const history = await gmail.users.history.list({
userId: 'me',
startHistoryId: historyId,
historyTypes: ['messageAdded']
});
// 새 메시지가 있으면 처리
if (history.data.history) {
for (const item of history.data.history) {
for (const msg of item.messagesAdded || []) {
await processNewEmail(msg.message.id);
}
}
}
} catch (error) {
console.error('Gmail 처리 오류:', error);
}
res.status(200).send('OK');
});
김개발 씨는 Gmail 알림을 자동화하기 위해 구글 문서를 찾아보았습니다. 그런데 Pub/Sub라는 생소한 용어가 등장했습니다.
"이게 뭐예요?" 박시니어 씨가 쉬운 비유로 설명했습니다. "유튜브를 생각해보세요.
좋아하는 채널을 구독하면 새 영상이 올라올 때 알림을 받죠. Pub/Sub도 똑같아요.
Gmail이 새 이메일을 '발행'하면, 우리 서버가 '구독자'로서 알림을 받는 거예요." Gmail Pub/Sub를 설정하는 과정은 크게 세 단계입니다. 첫 번째, Google Cloud에서 Pub/Sub 토픽 생성입니다.
토픽은 메시지가 발행되는 채널이라고 생각하면 됩니다. projects/myproject/topics/gmail-notifications 같은 이름으로 토픽을 만듭니다.
두 번째, 구독(Subscription) 생성입니다. 토픽에 발행된 메시지를 어디로 보낼지 설정합니다.
Push 방식을 선택하면 우리 서버의 웹훅 URL로 메시지가 전송됩니다. 세 번째, Gmail API에서 watch 설정입니다.
Gmail API의 users.watch 메서드를 호출해 "이 메일함에 변화가 생기면 저 토픽으로 알려줘"라고 등록합니다. 코드를 자세히 살펴보겠습니다.
Pub/Sub 메시지는 Base64로 인코딩되어 옵니다. 따라서 먼저 디코딩을 해야 실제 데이터를 볼 수 있습니다.
디코딩된 데이터에는 emailAddress와 historyId가 포함되어 있습니다. 여기서 historyId가 핵심입니다.
Gmail은 "새 이메일이 왔어요"라고만 알려주고, 이메일 내용을 직접 보내주지 않습니다. 대신 historyId를 제공하고, 우리는 이 ID를 사용해 Gmail API에서 실제 변경 사항을 조회합니다.
users.history.list API를 호출하면 해당 historyId 이후에 발생한 모든 변경 사항을 가져올 수 있습니다. messageAdded 타입으로 필터링하면 새로 추가된 메시지만 확인할 수 있습니다.
실무에서 주의할 점이 있습니다. Gmail watch는 7일마다 갱신해야 합니다.
갱신하지 않으면 알림이 중단됩니다. 따라서 Cron 작업으로 6일마다 watch를 다시 호출하는 로직을 추가해야 합니다.
앞서 배운 Cron과 Webhooks가 함께 사용되는 좋은 예시입니다. 또한 Pub/Sub 메시지는 최소 한 번 전달을 보장합니다.
즉, 같은 알림이 여러 번 올 수 있습니다. 따라서 이미 처리한 historyId를 기록해두고 중복 처리를 방지해야 합니다.
김개발 씨가 감탄했습니다. "와, 이메일이 오는 즉시 슬랙에 알림을 보낼 수 있겠네요!" 박시니어 씨가 고개를 끄덕였습니다.
"맞아요. 이렇게 하면 중요한 고객 문의를 놓치는 일이 없을 거예요."
실전 팁
💡 - Gmail watch는 7일마다 갱신해야 합니다. Cron으로 자동 갱신을 설정하세요
- historyId를 저장해두고 중복 알림을 방지하세요
- 개발 중에는 ngrok 같은 도구로 로컬 서버를 외부에 노출해 테스트할 수 있습니다
6. 실전 정기 알림 봇 만들기
김개발 씨는 지금까지 배운 Cron과 Webhooks를 활용해 팀을 위한 알림 봇을 만들기로 했습니다. 매일 아침 9시에 오늘의 할 일을 슬랙에 보내고, 중요한 이메일이 오면 즉시 알림을 보내는 봇입니다.
"드디어 배운 걸 실전에 적용해볼 시간이네요!" 김개발 씨의 눈이 반짝였습니다.
정기 알림 봇은 Cron을 사용한 시간 기반 알림과 Webhooks를 사용한 이벤트 기반 알림을 결합한 실용적인 프로젝트입니다. 슬랙 Incoming Webhooks를 활용해 알림을 전송하고, 환경 변수로 설정을 관리합니다.
이 프로젝트를 통해 자동화의 모든 요소를 하나로 통합하는 방법을 배울 수 있습니다.
다음 코드를 살펴봅시다.
// 통합 알림 봇 - app.js
require('dotenv').config();
const express = require('express');
const cron = require('node-cron');
const axios = require('axios');
const app = express();
app.use(express.json());
// 슬랙으로 메시지 전송
async function sendSlackNotification(message) {
await axios.post(process.env.SLACK_WEBHOOK_URL, {
text: message,
username: '알림봇',
icon_emoji: ':robot_face:'
});
}
// Cron: 매일 아침 9시 일일 리포트
cron.schedule('0 9 * * *', async () => {
const tasks = await getTodayTasks();
const message = `오늘의 할 일:\n${tasks.map(t => `- ${t}`).join('\n')}`;
await sendSlackNotification(message);
}, { timezone: 'Asia/Seoul' });
// Webhook: 중요 이메일 수신 시 알림
app.post('/webhooks/important-email', async (req, res) => {
const { from, subject } = req.body;
await sendSlackNotification(`중요 이메일 도착!\n보낸이: ${from}\n제목: ${subject}`);
res.status(200).json({ received: true });
});
app.listen(3000, () => console.log('알림 봇 시작!'));
드디어 김개발 씨가 배운 모든 것을 하나로 합칠 시간입니다. 지금까지 Cron으로 시간 기반 자동화를, Webhooks로 이벤트 기반 자동화를 배웠습니다.
이제 이 둘을 결합해 실용적인 알림 봇을 만들어보겠습니다. 먼저 프로젝트 구조를 설계합니다.
김개발 씨는 간단하게 시작하기로 했습니다. 하나의 app.js 파일에 모든 로직을 담고, 나중에 규모가 커지면 앞서 배운 모듈화 구조로 리팩토링할 계획입니다.
환경 변수는 .env 파일로 관리합니다. 슬랙 Incoming Webhooks를 설정합니다.
슬랙은 Incoming Webhooks라는 기능을 제공합니다. 슬랙 앱을 만들고 웹훅 URL을 발급받으면, 해당 URL로 POST 요청을 보내 채널에 메시지를 전송할 수 있습니다.
마치 슬랙 채널에 연결된 문자 발송 서비스와 같습니다. sendSlackNotification 함수는 이 웹훅 URL로 메시지를 전송합니다.
axios 라이브러리를 사용해 HTTP POST 요청을 보내고, text 필드에 보낼 메시지를 담습니다. Cron 작업을 설정합니다.
매일 아침 9시에 오늘의 할 일을 슬랙에 보내는 작업입니다. '0 9 * * *' 표현식은 매일 9시 0분을 의미합니다.
timezone 옵션으로 한국 시간을 지정했으므로, 한국 시간 기준 아침 9시에 실행됩니다. getTodayTasks 함수는 데이터베이스나 외부 서비스에서 오늘의 할 일을 가져옵니다.
실제 구현에서는 Notion API, Google Tasks API, 또는 자체 데이터베이스를 연동할 수 있습니다. Webhook 엔드포인트를 구현합니다.
중요한 이메일이 도착하면 Gmail Pub/Sub에서 알림을 보내고, 우리 서버는 이를 가공해 슬랙으로 전달합니다. 앞서 배운 Gmail 연동 로직을 그대로 활용하되, 최종 알림은 슬랙으로 보냅니다.
이제 통합 테스트를 진행합니다. 김개발 씨는 먼저 Cron을 테스트하기 위해 표현식을 '* * * * *'로 변경해 매 분마다 실행되도록 했습니다.
1분 뒤 슬랙에 메시지가 도착하는 것을 확인하고, 다시 원래 표현식으로 복구했습니다. Webhook 테스트는 curl 명령어로 진행합니다.
curl -X POST http://localhost:3000/webhooks/important-email -H "Content-Type: application/json" -d '{"from":"boss@company.com","subject":"긴급!"}' 명령어를 실행하면 슬랙에 알림이 도착합니다. 운영 환경 배포를 준비합니다.
실제 운영을 위해서는 몇 가지를 추가해야 합니다. PM2나 Docker로 프로세스를 관리하고, 로깅 시스템을 연동하며, 에러 발생 시 관리자에게 알림을 보내는 로직도 필요합니다.
김개발 씨가 뿌듯해하며 말했습니다. "드디어 완성이네요!
이제 매일 아침 할 일을 깜빡할 일도, 중요한 이메일을 놓칠 일도 없겠어요." 박시니어 씨가 어깨를 두드리며 말했습니다. "잘했어요.
이게 바로 개발자의 힘이에요. 반복 작업은 컴퓨터에게 맡기고, 우리는 더 창의적인 일에 집중하는 거죠." 이렇게 김개발 씨는 Cron과 Webhooks를 마스터하고, 팀의 생산성을 높이는 알림 봇까지 완성했습니다.
여러분도 오늘 배운 내용을 활용해 자신만의 자동화 시스템을 만들어보세요.
실전 팁
💡 - 슬랙 Incoming Webhooks URL은 절대 코드에 직접 넣지 말고 환경 변수로 관리하세요
- 개발 중에는 별도의 테스트 채널을 만들어 알림을 보내세요
- PM2의 pm2-logrotate로 로그 파일을 자동 관리하면 디스크 용량 문제를 예방할 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
실전 인프라 자동화 프로젝트 완벽 가이드
Ansible을 활용하여 멀티 티어 웹 애플리케이션 인프라를 자동으로 구축하는 실전 프로젝트입니다. 웹 서버, 데이터베이스, 로드 밸런서를 코드로 관리하며 반복 가능한 인프라 배포를 경험합니다.
Ansible Role 구조화 완벽 가이드
Ansible에서 재사용 가능한 코드를 작성하기 위한 Role 구조화 방법을 알아봅니다. 디렉토리 구조부터 의존성 관리, 재사용 전략까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.
Feedback Loops Human-in-the-Loop 완벽 가이드
AI 시스템에서 인간의 판단을 효과적으로 통합하는 Human-in-the-Loop 패턴을 다룹니다. 인간 검토 루프 설계부터 신뢰도 기반 자동화까지, 안전하고 효율적인 AI 시스템 구축 방법을 실무 예제와 함께 설명합니다.
모델 Failover 및 프로바이더 관리 완벽 가이드
AI 서비스 개발 시 필수적인 모델 장애 대응과 다중 프로바이더 관리 전략을 다룹니다. Anthropic과 OpenAI를 통합하고, 안정적인 failover 시스템을 구축하는 방법을 실무 코드와 함께 설명합니다.
보안 모델 및 DM Pairing 완벽 가이드
Discord 봇의 DM 보안 정책과 페어링 시스템을 체계적으로 학습합니다. dmPolicy 설정부터 allowlist 관리, 페어링 코드 구현까지 안전한 봇 운영의 모든 것을 다룹니다.