본문으로 건너뛰기

n8n 템플릿 매핑 가이드

🎯 개요

User Engagement 시스템에서 이벤트 → 알림톡 템플릿 매핑은 n8n 워크플로우에서 관리됩니다. 이를 통해 기획자가 코드 수정 없이 템플릿 매핑을 자유롭게 변경할 수 있습니다.

🏗️ 아키텍처

📋 n8n 워크플로우 구조

1. Event Reception & Parsing

// Step 1: Pub/Sub 이벤트 수신
const event = JSON.parse($input.body);
const eventType = event.type;
const eventData = event.payload;

// Step 2: 이벤트 타입별 분기
switch (eventType) {
case 'sleep.rtib.pre_notification.triggered':
return {
eventType: 'RTIB_PRE_NOTIFICATION',
userId: eventData.userId,
templateData: {
userName: eventData.userName || '고객',
nextRtibDay: eventData.nextRtibDayIndex,
currentDay: eventData.currentDayIndex
}
};

case 'user.registration.completion.notification.sent':
return {
eventType: 'REGISTRATION_COMPLETION',
userId: eventData.userId,
templateData: {
userName: eventData.userName || '고객',
phoneNumber: eventData.phoneNumber || '000-0000-0000'
}
};

// ... 기타 이벤트 타입들
}

2. Template Mapping Decision

// Step 2: 템플릿 매핑 (기획자가 자유롭게 수정 가능!)
const templateMappings = {
// === User Engagement Timeline Templates ===
'RTIB_PRE_NOTIFICATION': {
templateId: 'SLEEPQ_RTIB_PRE_001', // NHN 심의 완료 ID
templateName: 'rTIB 전날 알림',
priority: 'HIGH',
buttons: [
{
buttonType: 'WL',
buttonName: '지금 수면기록하기',
buttonUrl: 'sleepq://sleep-log'
}
]
},

'REGISTRATION_COMPLETION': {
templateId: 'SLEEPQ_REGISTRATION_001',
templateName: '회원가입 완료 안내',
priority: 'HIGH',
buttons: [
{
buttonType: 'WL',
buttonName: '슬립큐로 이동하기',
buttonUrl: 'sleepq://home'
}
]
},

'SLEEP_LOG_REMINDER_INSUFFICIENT': {
templateId: 'SLEEPQ_SLEEP_LOG_001',
templateName: '수면기록 부족 리마인드',
priority: 'HIGH',
buttons: [
{
buttonType: 'WL',
buttonName: '수면기록하러 가기',
buttonUrl: 'sleepq://sleep-log'
}
]
},

'INACTIVITY_REMINDER': {
templateId: 'SLEEPQ_INACTIVITY_001',
templateName: '미접속 사용자 리마인드',
priority: 'NORMAL',
buttons: [
{
buttonType: 'WL',
buttonName: '슬립큐 접속하기',
buttonUrl: 'sleepq://home'
}
]
},

'CRM_CALL_REMINDER': {
templateId: 'SLEEPQ_CRM_CALL_001',
templateName: 'CRM 전화상담 리마인드',
priority: 'NORMAL',
buttons: [
{
buttonType: 'WL',
buttonName: '상담 일정 조정하기',
buttonUrl: 'sleepq://crm-schedule'
}
]
},

'RTIB_PRESCRIPTION_NOTIFICATION': {
templateId: 'SLEEPQ_RTIB_PRESCRIPTION_001',
templateName: 'rTIB 처방 알림',
priority: 'HIGH',
buttons: [
{
buttonType: 'WL',
buttonName: '수면목표시간 확인하기',
buttonUrl: 'sleepq://rtib'
}
]
},

'QUESTIONNAIRE_REMINDER': {
templateId: 'SLEEPQ_QUESTIONNAIRE_001',
templateName: '설문조사 리마인드',
priority: 'HIGH',
buttons: [
{
buttonType: 'WL',
buttonName: '수면건강설문조사 참여하기',
buttonUrl: 'sleepq://questionnaire'
}
]
},

'FINAL_QUESTIONNAIRE_REMINDER': {
templateId: 'SLEEPQ_FINAL_QUESTIONNAIRE_001',
templateName: '최종 설문조사 리마인드',
priority: 'NORMAL',
buttons: [
{
buttonType: 'WL',
buttonName: '최종 설문하러 가기',
buttonUrl: 'sleepq://final-questionnaire'
}
]
},

'TREATMENT_END_WARNING': {
templateId: 'SLEEPQ_TREATMENT_END_001',
templateName: '치료 종료 7일 전 안내',
priority: 'NORMAL',
buttons: [
{
buttonType: 'WL',
buttonName: '문의하기',
buttonUrl: 'sleepq://contact'
}
]
},

'TREATMENT_COMPLETION': {
templateId: 'SLEEPQ_TREATMENT_COMPLETION_001',
templateName: '치료 완료 안내',
priority: 'NORMAL',
buttons: [
{
buttonType: 'WL',
buttonName: '슬립큐 재처방받기',
buttonUrl: 'https://soldoc.co.kr'
},
{
buttonType: 'WL',
buttonName: '문의하기',
buttonUrl: 'sleepq://contact'
}
]
}
};

// Step 3: 매핑 결과 반환
const mapping = templateMappings[$input.eventType];
if (!mapping) {
throw new Error(`Unknown event type: ${$input.eventType}`);
}

return {
templateId: mapping.templateId,
templateName: mapping.templateName,
priority: mapping.priority,
buttons: mapping.buttons,
userId: $input.userId,
templateData: $input.templateData
};

3. User Data Enrichment

// Step 3: 사용자 정보 보강 (User 도메인 API 호출)
const userInfo = await $http.get(`/api/v1/users/${$input.userId}/profile`);

return {
...previous_step_output,
phoneNumber: userInfo.phoneNumber,
userName: userInfo.name || '고객',
templateData: {
...previous_step_output.templateData,
userName: userInfo.name || '고객'
}
};

4. Direct Notification API Call

// Step 4: Direct 알림톡 API 호출
const response = await $http.post('/api/v1/notifications/alimtalk/direct', {
userId: $input.userId,
templateId: $input.templateId,
phoneNumber: $input.phoneNumber,
templateData: $input.templateData,
buttons: $input.buttons,
deduplicationKey: `${$input.eventType}_${$input.userId}_${new Date().toISOString().split('T')[0]}`,
priority: $input.priority
});

// Step 5: 결과 로깅
if (response.success) {
console.log(`✅ 알림톡 발송 성공: ${$input.userId} (${$input.templateId})`);
} else {
console.error(`❌ 알림톡 발송 실패: ${$input.userId} (${response.errorMessage})`);
}

return response;

🔧 기획자를 위한 템플릿 관리 가이드

새로운 메시지 추가 프로세스

1단계: NHN Cloud Console에서 템플릿 생성

  1. NHN Cloud Console 접속
  2. 알림톡 템플릿 생성
  3. 심의 신청 및 승인 대기
  4. 승인 완료 시 templateId 확보

2단계: n8n 워크플로우 업데이트

  1. n8n 관리 페이지 접속
  2. "User Engagement Template Mapping" 워크플로우 선택
  3. templateMappings 객체에 새로운 매핑 추가:
'NEW_MESSAGE_TYPE': {
templateId: 'SLEEPQ_NEW_001', // NHN에서 발급받은 ID
templateName: '새로운 메시지 이름',
priority: 'HIGH', // LOW, NORMAL, HIGH, URGENT
buttons: [ // 선택사항
{
buttonType: 'WL', // WL(웹링크), AL(앱링크) 등
buttonName: '버튼 이름',
buttonUrl: 'sleepq://some-action'
}
]
}
  1. 워크플로우 저장 및 활성화

3단계: 이벤트 발송 테스트

  1. 해당 도메인에서 이벤트 발송
  2. n8n 로그에서 매핑 결과 확인
  3. 실제 알림톡 발송 결과 확인

기존 메시지 수정

템플릿 내용 변경

  1. NHN Cloud Console에서 템플릿 수정
  2. 심의 재신청 (필요한 경우)
  3. n8n 워크플로우는 수정 불필요 ✅

버튼 변경

  1. n8n 워크플로우에서 해당 템플릿의 buttons 배열 수정
  2. 즉시 적용 (배포 불필요) ✅

우선순위 변경

  1. n8n 워크플로우에서 priority 값 변경
  2. 즉시 적용 ✅

📊 템플릿 매핑 현황 관리

n8n 내부 모니터링

// 템플릿 사용 현황 로깅
const templateUsageLog = {
eventType: $input.eventType,
templateId: mapping.templateId,
userId: $input.userId,
timestamp: new Date().toISOString(),
success: response.success
};

// 외부 모니터링 시스템으로 전송 (선택사항)
await $http.post('/api/v1/analytics/template-usage', templateUsageLog);

템플릿 상태 체크

// 주기적으로 템플릿 상태 확인 (별도 워크플로우)
const templateCheck = await $http.get(`/api/v1/notifications/alimtalk/templates/${templateId}/status`);

if (!templateCheck.isActive || templateCheck.approvalStatus !== 'APPROVED') {
// Slack 또는 이메일로 알림
await $http.post('/webhook/slack', {
text: `⚠️ 알림톡 템플릿 비활성화됨: ${templateId} (${templateCheck.approvalStatus})`
});
}

🎯 장점

기획자 관점

  • 실시간 변경: 코드 배포 없이 즉시 템플릿 수정
  • 직관적 관리: n8n GUI에서 시각적으로 매핑 관리
  • 빠른 A/B 테스트: 템플릿 변경 후 즉시 테스트 가능

개발자 관점

  • 관심사 분리: 비즈니스 로직과 기술 구현 분리
  • 유지보수성: 템플릿 변경으로 인한 코드 수정 불필요
  • 확장성: 새로운 채널 추가 시에도 n8n에서 관리

운영 관점

  • 장애 대응: 템플릿 문제 시 n8n에서 즉시 대체 템플릿으로 변경
  • 모니터링: n8n 로그로 템플릿 사용 현황 추적
  • 롤백: 문제 발생 시 이전 버전으로 즉시 되돌리기

🔄 Migration Plan

현재 → 목표

- 도메인 → Strategy → TemplateMapper (하드코딩) → NHN API
+ 도메인 → n8n Webhook → Template Mapping (GUI) → Direct API → NHN API

단계별 이관

  1. Phase 1: Direct API 구현 완료 ✅
  2. Phase 2: n8n 워크플로우 템플릿 매핑 구현
  3. Phase 3: 기존 Strategy 제거 및 Direct API 전환
  4. Phase 4: 기획자 교육 및 권한 이양

📝 실제 API 명세

Direct 알림톡 API

// POST /api/v1/notifications/alimtalk/direct
{
"userId": "user123",
"templateId": "SLEEPQ_RTIB_PRE_001", // n8n에서 결정
"phoneNumber": "010-1234-5678",
"templateData": {
"userName": "홍길동",
"nextRtibDay": 8,
"currentDay": 7
},
"buttons": [
{
"buttonType": "WL",
"buttonName": "지금 수면기록하기",
"buttonUrl": "sleepq://sleep-log"
}
],
"deduplicationKey": "rtib_pre_user123_2025-08-20",
"priority": "HIGH"
}

Direct 푸시 알림 API

// POST /api/v1/notifications/push/direct
{
"userId": "user123",
"title": "슬립큐",
"body": "홍길동님, 수면 목표 시간 처방까지 하루 남았어요!",
"deepLink": "sleepq://sleep-log",
"icon": "sleepq_icon",
"badge": 1,
"data": {
"type": "rtib_pre_notification",
"nextRtibDay": 8
},
"deduplicationKey": "rtib_pre_push_user123_2025-08-20"
}

🎉 결론

이제 기획자가 n8n GUI에서 자유롭게 템플릿 매핑을 관리할 수 있습니다!

  • 🚀 새로운 메시지: NHN 승인 → n8n 매핑 추가 → 즉시 사용
  • 🔧 기존 메시지 수정: n8n에서 클릭 몇 번으로 즉시 변경
  • 📊 A/B 테스트: 다른 템플릿 ID로 쉽게 전환
  • 🛡️ 장애 대응: 문제 템플릿 즉시 비활성화 또는 대체

개발자는 더 이상 템플릿 매핑 변경으로 인한 배포를 하지 않아도 됩니다!