배송 알림 서비스 구축
카카오톡, SMS, 이메일로 실시간 배송 알림 보내기
배송 알림 서비스를 구축하면
- 고객 만족도 상승 - 고객이 직접 배송 조회할 필요 없음
- CS 문의 감소 - "제 택배 어디 있어요?" 문의 대폭 감소
- 브랜드 경험 향상 - 세심한 배송 알림으로 신뢰도 상승
- 마케팅 접점 확보 - 배송완료 알림에 리뷰 요청, 쿠폰 포함 가능
지원 알림 채널
💬
카카오 알림톡
가장 높은 도달률
📱
SMS/LMS
모든 휴대폰 지원
📧
이메일
상세 정보 전달
🔔
앱 푸시
자체 앱 알림
동작 흐름
송장 등록 → 배송 상태 변경 → 웹훅 수신 → 알림 발송
알림 이벤트
| 이벤트 | 설명 | 알림 예시 |
|---|---|---|
tracking.polled | 폴링 완료 (상태 변경 시) | currentStatus에 따라 분기: "발송되었습니다" / "배송 출발" / "배송 완료" |
tracking.completed | 전체 배송 완료 | "배송이 완료되었습니다. 리뷰를 남겨주세요!" |
구현 가이드
1단계: 웹훅 수신 서버 구축
1 웹훅 엔드포인트 생성
// Express.js 웹훅 서버
const express = require('express');
const app = express();
app.post('/webhook/delivery', express.json(), async (req, res) => {
const { event, items } = req.body;
console.log(`이벤트 수신: ${event}`);
for (const item of items) {
switch (event) {
case 'tracking.polled':
await sendNotification(item, '상태 변경');
break;
case 'tracking.completed':
await sendNotification(item, '전체 배송완료');
break;
}
}
res.json({ received: true });
});
app.listen(3000); 2단계: 카카오 알림톡 연동
2 알림톡 발송 함수
const axios = require('axios');
async function sendKakaoAlimtalk(phone, templateCode, variables) {
// 카카오 비즈메시지 API (예시)
const response = await axios.post(
'https://api.kakao.com/v2/api/talk/memo/send',
{
template_object: {
object_type: 'text',
text: formatMessage(templateCode, variables),
link: {
web_url: `https://yourshop.com/tracking/${variables.trackingNumber}`
}
}
},
{
headers: { 'Authorization': `Bearer ${KAKAO_ACCESS_TOKEN}` }
}
);
return response.data;
}
// 템플릿 메시지 포맷
function formatMessage(templateCode, vars) {
const templates = {
'DELIVERY_START': `[${vars.shopName}] 주문하신 상품이 발송되었습니다.\n\n` +
`상품: ${vars.productName}\n` +
`송장번호: ${vars.trackingNumber}\n` +
`택배사: ${vars.courierName}\n\n` +
`배송조회: ${vars.trackingUrl}`,
'DELIVERY_COMPLETE': `[${vars.shopName}] 배송이 완료되었습니다!\n\n` +
`상품: ${vars.productName}\n` +
`배송완료: ${vars.deliveredAt}\n\n` +
`상품은 만족스러우셨나요?\n` +
`리뷰 작성하기: ${vars.reviewUrl}`
};
return templates[templateCode];
} 3단계: SMS 발송 연동
3 SMS 발송 함수
// NHN Cloud SMS API 예시
async function sendSMS(phone, message) {
const response = await axios.post(
`https://api-sms.cloud.toast.com/sms/v3.0/appKeys/${APP_KEY}/sender/sms`,
{
body: message,
sendNo: '15881234', // 발신번호
recipientList: [
{ recipientNo: phone }
]
},
{
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': SECRET_KEY
}
}
);
return response.data;
}
// 알림 발송 메인 함수
async function sendNotification(data, eventType) {
const order = await Order.findOne({
where: { trackingNumber: data.trackingNumber }
});
if (!order) return;
const message = createMessage(eventType, order, data);
// 채널 우선순위: 카카오 → SMS → 이메일
try {
// 카카오 알림톡 먼저 시도
await sendKakaoAlimtalk(order.phone, `DELIVERY_${eventType}`, {
shopName: '쇼핑몰명',
productName: order.productName,
trackingNumber: data.trackingNumber,
courierName: getCourierName(data.courierCode),
trackingUrl: `https://yourshop.com/tracking/${data.trackingNumber}`,
deliveredAt: new Date().toLocaleString('ko-KR'),
reviewUrl: `https://yourshop.com/review/${order.id}`
});
} catch (kakaoError) {
// 카카오 실패 시 SMS로 대체
console.log('카카오 알림톡 실패, SMS로 대체 발송');
await sendSMS(order.phone, message);
}
} 4단계: 이메일 알림
4 이메일 발송 (상세 정보 포함)
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
async function sendEmail(to, subject, trackingData) {
const progressHtml = trackingData.progresses
.map(p => `
${p.dateTime}
${p.location}
${p.description}
`).join('');
const html = `
배송 안내
택배사: ${trackingData.courierName}
송장번호: ${trackingData.trackingNumber}
현재 상태: ${trackingData.statusText}
배송 진행 상황
시간
위치
상태
${progressHtml}
`;
await transporter.sendMail({
from: '"쇼핑몰" ',
to,
subject,
html
});
} 고급 기능
알림 중복 방지
// Redis를 사용한 중복 알림 방지
const Redis = require('ioredis');
const redis = new Redis();
async function shouldSendNotification(trackingNumber, eventType) {
const key = `notification:${trackingNumber}:${eventType}`;
// 이미 발송된 알림인지 확인
const exists = await redis.exists(key);
if (exists) {
return false; // 이미 발송됨
}
// 7일간 중복 방지 (배송 완료 후 충분한 기간)
await redis.setex(key, 7 * 24 * 60 * 60, 'sent');
return true;
}
// 웹훅 핸들러에서 사용
app.post('/webhook/delivery', async (req, res) => {
const { event, items } = req.body;
for (const item of items) {
if (await shouldSendNotification(item.trackingNumber, event)) {
await sendNotification(item, event);
}
}
res.json({ received: true });
}); 사용자 알림 설정
// 사용자별 알림 선호 설정
const userPreferences = {
channels: ['kakao', 'email'], // 수신 채널
events: ['IN_TRANSIT', 'DELIVERED'], // 수신할 이벤트
quietHours: { start: 22, end: 8 } // 야간 알림 금지
};
async function sendNotificationWithPreference(userId, data, eventType) {
const prefs = await UserPreference.findOne({ where: { userId } });
// 이벤트 설정 확인
if (!prefs.events.includes(eventType)) {
return;
}
// 야간 시간 확인
const hour = new Date().getHours();
if (hour >= prefs.quietHours.start || hour < prefs.quietHours.end) {
// 야간에는 큐에 저장 후 아침에 발송
await NotificationQueue.create({
userId, data, eventType,
scheduledAt: getNextMorning()
});
return;
}
// 선호 채널로 발송
for (const channel of prefs.channels) {
await sendByChannel(channel, data);
}
} 알림 메시지 템플릿
효과적인 알림 메시지 작성 팁
- 간결하게 - 핵심 정보만 포함 (상품명, 송장번호, 현재 상태)
- CTA 포함 - 배송조회 링크, 리뷰 작성 버튼
- 브랜드 일관성 - 쇼핑몰 이름, 로고 활용
- 적절한 타이밍 - 중요한 이벤트만 알림 (너무 잦으면 차단)