RECIPE 064
월간 지출 내역 자동 분석 → 리포트 생성
- ▸이메일/CSV로 받은 카드 명세서 자동 파싱
- ▸카테고리별 지출 자동 분류 및 합산
- ▸월간 지출 리포트를 Notion 또는 Sheets에 자동 생성
✅사전 준비
- □카드사 이메일 명세서 수신 Gmail 설정
- □Google Sheets 지출 기록용 시트 생성 (컬럼: 날짜, 금액, 가맹점, 카테고리)
- □카테고리 키워드 매핑 테이블 준비
- □OpenAI API 키 (카테고리 자동 분류용)
카테고리 자동 분류 (Code 노드)
const categoryMap = {
'식비': ['맥도날드', '스타벅스', '배달의민족', 'GS25', 'CU'],
'교통': ['카카오택시', 'T머니', '주유'],
'쇼핑': ['쿠팡', '11번가', '마켓컬리'],
'구독': ['Netflix', 'Spotify', 'ChatGPT', 'Adobe'],
'의료': ['병원', '약국', '의원']
};
const merchant = $json.merchant;
let category = '기타';
for (const [cat, keywords] of Object.entries(categoryMap)) {
if (keywords.some(k => merchant.includes(k))) {
category = cat;
break;
}
}
return [{ json: { ...$json, category } }];월간 합산 (Code 노드)
const items = $input.all();
const summary = {};
items.forEach(item => {
const cat = item.json.category;
summary[cat] = (summary[cat] || 0) + item.json.amount;
});
const total = Object.values(summary).reduce((a, b) => a + b, 0);
return [{ json: { summary, total, month: $now.toFormat('yyyy-MM') } }];💡 TIP카드사마다 이메일 명세서 형식이 다릅니다. 처음에는 HTML Extract 노드로 금액/가맹점 패턴을 직접 파싱하고, 이후 OpenAI로 검증하는 2단계 방식을 권장합니다.
⚡
워크플로우 JSON
아래 JSON을 복사해 n8n에 바로 임포트하세요
📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
"recipeId": "recipe064",
"title": "미수금 자동 리마인더 (D+7, D+14, D+30)",
"chapter": 6,
"category": "재무 & 정산 자동화",
"difficulty": "⭐⭐⭐",
"estimatedTime": "45분",
"usedApps": [
"Google Sheets",
"Gmail",
"Slack"
],
"description": "미결제 인보이스 목록을 매일 자동 점검해, 결제 기한 초과 후 D+7에 정중한 첫 번째 리마인더, D+14에 두 번째 리마인더와 담당자 Slack 알림, D+30에 최후 통보 이메일과 대표 긴급 알림을 3단계로 자동 발송합니다.",
"nodes": [
{
"id": "node1",
"type": "n8n-nodes-base.scheduleTrigger",
"name": "매일 오전 9시 실행",
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * *"
}
]
}
}
},
{
"id": "node2",
"type": "n8n-nodes-base.googleSheets",
"name": "미결제 인보이스 조회",
"parameters": {
"operation": "getAll",
"spreadsheetId": "={{$env.INVOICE_SHEET_ID}}",
"sheetName": "인보이스목록",
"filters": {
"conditions": [
{
"column": "상태",
"condition": "equals",
"value": "미결제"
}
]
}
}
},
{
"id": "node3",
"type": "n8n-nodes-base.code",
"name": "초과 일수 계산",
"parameters": {
"jsCode": "const today = new Date();\nreturn items.map(item => {\n const dueDate = new Date(item.json['결제기한']);\n const diffDays = Math.floor((today - dueDate) / (1000 * 60 * 60 * 24));\n return { ...item, json: { ...item.json, overdueDays: diffDays } };\n}).filter(item => item.json.overdueDays > 0);"
}
},
{
"id": "node4",
"type": "n8n-nodes-base.switch",
"name": "초과 단계 분기",
"parameters": {
"dataPropertyName": "={{$json['overdueDays']}}",
"rules": {
"rules": [
{
"operation": "equals",
"value": 7,
"output": 0
},
{
"operation": "equals",
"value": 14,
"output": 1
},
{
"operation": "equals",
"value": 30,
"output": 2
}
]
}
}
},
{
"id": "node5",
"type": "n8n-nodes-base.gmail",
"name": "D+7 첫 번째 리마인더",
"parameters": {
"operation": "send",
"to": "={{$json['고객이메일']}}",
"subject": "[안내] {{$json['프로젝트명']}} 결제 기한이 지났습니다",
"message": "안녕하세요, {{$json['고객명']}}님.\n\n{{$json['프로젝트명']}}의 결제 기한({{$json['결제기한']}})이 지났습니다.\n아직 결제가 확인되지 않아 안내 드립니다.\n\n청구 금액: ₩{{$json['금액']}}\n\n불편하셨다면 말씀해 주세요. 감사합니다."
}
},
{
"id": "node6",
"type": "n8n-nodes-base.gmail",
"name": "D+14 두 번째 리마인더",
"parameters": {
"operation": "send",
"to": "={{$json['고객이메일']}}",
"subject": "[재안내] {{$json['프로젝트명']}} 미결제 확인 요청",
"message": "안녕하세요, {{$json['고객명']}}님.\n\n결제 기한 초과 14일이 지났습니다.\n빠른 시일 내 결제 부탁드립니다.\n\n청구 금액: ₩{{$json['금액']}}\n\n감사합니다."
}
},
{
"id": "node7",
"type": "n8n-nodes-base.slack",
"name": "D+14 담당자 Slack 알림",
"parameters": {
"channel": "={{$env.FINANCE_SLACK_CHANNEL}}",
"text": "⚠️ 미수금 D+14 알림\n고객: {{$json['고객명']}}\n금액: ₩{{$json['금액']}}\n프로젝트: {{$json['프로젝트명']}}\n→ 직접 연락이 필요할 수 있어요."
}
},
{
"id": "node8",
"type": "n8n-nodes-base.gmail",
"name": "D+30 최후 통보 이메일",
"parameters": {
"operation": "send",
"to": "={{$json['고객이메일']}}",
"subject": "[긴급] {{$json['프로젝트명']}} 미결제 최종 안내",
"message": "안녕하세요, {{$json['고객명']}}님.\n\n결제 기한 초과 30일이 되었습니다.\n5영업일 내 결제가 되지 않을 경우 추가 조치를 취할 수 있음을 안내드립니다.\n\n청구 금액: ₩{{$json['금액']}}\n\n문의: {{$env.FINANCE_CONTACT_EMAIL}}"
}
},
{
"id": "node9",
"type": "n8n-nodes-base.slack",
"name": "D+30 대표 긴급 알림",
"parameters": {
"channel": "={{$env.CEO_SLACK_ID}}",
"text": "🚨 미수금 D+30 긴급 알림\n고객: {{$json['고객명']}}\n금액: ₩{{$json['금액']}}\n프로젝트: {{$json['프로젝트명']}}\n→ 즉각 대응이 필요합니다."
}
}
],
"envVariables": {
"INVOICE_SHEET_ID": "Google Sheets 인보이스 시트 ID",
"FINANCE_SLACK_CHANNEL": "재무팀 Slack 채널 ID",
"CEO_SLACK_ID": "대표 Slack DM ID",
"FINANCE_CONTACT_EMAIL": "재무팀 연락 이메일"
},
"tips": [
"Switch 노드는 overdueDays가 정확히 7, 14, 30인 날만 발송합니다. 중복 발송을 방지하려면 구글 시트에 '마지막 리마인더 발송일' 열을 추가해 확인하세요.",
"결제가 완료되면 시트 상태를 '결제완료'로 업데이트해야 자동 리마인더 대상에서 제외됩니다.",
"레시피 062(인보이스 발송)와 연결해 발행 즉시 미수금 추적을 시작할 수 있어요."
],
"qrUrl": "www.koreaaitimes.com/books/n8n-vol2/recipe064"
}