발행일: 2026-10-06(화) 11:44
← 목차로 돌아가기|하루 30분 n8n 시리즈 ② 실무 자동화 레시피 100
RECIPE P4

미니 프로젝트 — 소규모 CS팀의 24시간 응대 자동화

  • ▸이메일·채팅 문의를 자동 분류해 담당자에게 즉시 라우팅할 수 있다
  • ▸FAQ 자동 답변으로 야간·주말 응대 공백을 메울 수 있다
  • ▸월간 CS 성과 지표를 자동 집계해 대시보드에 반영할 수 있다

✅사전 준비

  • □레시피 041~050 핵심 워크플로우 동작 확인
  • □Freshdesk 또는 Zendesk API 키 연결 완료
  • □FAQ 답변 목록 Google Sheets에 정리 (키워드 / 답변 컬럼)
  • □Slack #cs-team 채널 및 담당자별 DM 채널 확인
  • □Gmail 또는 SendGrid 자격증명 연결 완료

24시간 응대 자동화 전체 흐름

[실시간 문의 접수]
  이메일 수신 Trigger / 채팅 Webhook
       ↓
[분류 단계]
  키워드 매칭 → 문의 유형 판단
  (일반/VIP/기술/환불)
       ↓
[자동 처리 단계]
  FAQ 매칭 성공 → 자동 답변 발송 → 티켓 닫기
  FAQ 매칭 실패 → 담당자 배정 → Slack 알림
       ↓
[에스컬레이션]
  VIP 고객 → 즉시 팀장 DM
  SLA 30분 초과 → 경고 알림
       ↓
[일간 집계 - 매일 23:50]
  전체 티켓 조회 → 지표 계산 → Sheets 기록

문의 유형 분류 — Code 노드

const subject = $json.subject?.toLowerCase() ?? '';
const body    = $json.body?.toLowerCase() ?? '';
const text    = subject + ' ' + body;

const VIP_EMAILS = ['ceo@bigclient.com', 'director@vip.co.kr'];

let type = 'general';
if (VIP_EMAILS.includes($json.from_email))      type = 'vip';
else if (text.includes('환불') || text.includes('취소')) type = 'refund';
else if (text.includes('오류') || text.includes('버그')) type = 'tech';
else if (text.includes('결제') || text.includes('청구')) type = 'billing';

return [{ json: { ...$json, ticketType: type } }];

FAQ 자동 매칭 — Google Sheets Lookup

노드: Google Sheets → Read Rows
시트: FAQ 목록

이후 Code 노드에서 키워드 매칭:
  const faqs = $('FAQ Sheets').all();
  const text = $json.subject + ' ' + $json.body;
  const matched = faqs.find(f =>
    text.includes(f.json.keyword)
  );

  if (matched) {
    return [{ json: {
      autoReply: true,
      replyText: matched.json.answer
    }}];
  }
  return [{ json: { autoReply: false } }];

일간 CS 지표 집계 — Sheets 기록

날짜:           {{ $today.toFormat("yyyy-MM-dd") }}
총 문의수:      {{ $json.total }}
자동처리:       {{ $json.autoResolved }}
수동처리:       {{ $json.manualResolved }}
자동처리율:     {{ ($json.autoResolved / $json.total * 100).toFixed(1) }}%
평균응답시간:   {{ $json.avgResponseMin }}분
SLA 초과:       {{ $json.slaBreached }}건
💡 TIPFAQ 시트에 '키워드' 컬럼을 쉼표 구분으로 여러 개 입력하면 (예: '환불,반품,취소') Code 노드에서 split(',')으로 분리해 다중 키워드 매칭이 가능합니다. 매칭률이 크게 올라갑니다.
⚠️ 주의자동 답변 발송 전 반드시 테스트 이메일로 충분히 검증하세요. 잘못된 FAQ가 자동 발송되면 고객 신뢰를 크게 손상시킵니다. 초기에는 '자동 답변 초안 작성 후 담당자 승인' 방식으로 운영하는 것을 권장합니다.
⚡

워크플로우 JSON

아래 JSON을 복사해 n8n에 바로 임포트하세요

📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
  "projectId": "miniproject04",
  "title": "소규모 CS팀의 24시간 응대 자동화",
  "chapter": 4,
  "difficulty": "⭐⭐⭐",
  "estimatedTime": "90분",
  "description": "혼자 또는 2~3명이 CS를 담당하는 팀에서, 아무도 없는 새벽에도 고객 문의가 자동으로 분류되고 처리되는 24시간 고객 응대 자동화 시스템입니다. 레시피 041~050을 연결해 구성합니다.",
  "usedRecipes": [
    "recipe041",
    "recipe042",
    "recipe043",
    "recipe044",
    "recipe045",
    "recipe046",
    "recipe050"
  ],
  "pipeline": [
    {
      "phase": "24시간 1차 자동 응대",
      "description": "새 이메일 문의가 수신되면 FAQ 키워드와 매칭하여 자동 답변을 발송하거나, 매칭 실패 시 티켓을 자동 생성하고 담당자를 배정합니다.",
      "recipes": [
        "recipe042",
        "recipe041"
      ],
      "trigger": "Gmail Trigger — 새 이메일 수신 시 즉시 실행",
      "nodes": [
        {
          "id": "trigger_gmail",
          "type": "trigger",
          "service": "Gmail",
          "name": "새 고객 문의 이메일 수신",
          "config": {
            "event": "messageReceived",
            "pollInterval": "every 5 minutes",
            "filters": {
              "to": "{{$env.SUPPORT_EMAIL}}"
            }
          }
        },
        {
          "id": "load_faq",
          "type": "action",
          "service": "Google Sheets",
          "name": "FAQ 목록 로드",
          "config": {
            "operation": "readRows",
            "spreadsheetId": "{{$env.FAQ_SHEET_ID}}",
            "sheetName": "FAQ",
            "columns": [
              "키워드",
              "답변"
            ]
          }
        },
        {
          "id": "match_keyword",
          "type": "action",
          "service": "Function",
          "name": "키워드 매칭",
          "config": {
            "code": "const emailBody = $node['trigger_gmail'].json['text'] + ' ' + $node['trigger_gmail'].json['subject'];\nconst faqList = $node['load_faq'].json;\n\nlet matched = null;\nfor (const faq of faqList) {\n  const keywords = faq['키워드'].split(',').map(k => k.trim());\n  if (keywords.some(k => emailBody.includes(k))) {\n    matched = faq['답변'];\n    break;\n  }\n}\n\nreturn [{ json: { ...($node['trigger_gmail'].json), matchedAnswer: matched, isMatched: !!matched } }];"
          }
        },
        {
          "id": "check_match",
          "type": "condition",
          "service": "IF",
          "name": "FAQ 매칭 여부",
          "config": {
            "condition": "{{$json['isMatched'] === true}}"
          }
        },
        {
          "id": "send_auto_reply",
          "type": "action",
          "service": "Gmail",
          "name": "자동 FAQ 답변 발송",
          "config": {
            "operation": "replyEmail",
            "messageId": "{{$json['id']}}",
            "body": "안녕하세요!\n\n문의 주셔서 감사합니다. 😊\n\n{{$json['matchedAnswer']}}\n\n추가 문의 사항이 있으시면 언제든지 연락 주세요!\n{{$env.BRAND_NAME}} CS팀 드림"
          }
        },
        {
          "id": "classify_with_ai",
          "type": "action",
          "service": "OpenAI",
          "name": "AI 문의 유형 분류",
          "config": {
            "operation": "chat",
            "model": "gpt-4o-mini",
            "messages": [
              {
                "role": "system",
                "content": "고객 이메일 문의 유형을 분류합니다. 반드시 다음 중 하나만 답하세요: 배송문의 / 환불요청 / 기술지원 / 계정문의 / 기타"
              },
              {
                "role": "user",
                "content": "제목: {{$json['subject']}}\n내용: {{$json['text'].slice(0, 500)}}"
              }
            ]
          }
        },
        {
          "id": "create_ticket",
          "type": "action",
          "service": "Zendesk",
          "name": "티켓 자동 생성",
          "config": {
            "operation": "createTicket",
            "subject": "{{$node['trigger_gmail'].json['subject']}}",
            "requesterEmail": "{{$node['trigger_gmail'].json['from']['value'][0]['address']}}",
            "description": "{{$node['trigger_gmail'].json['text']}}",
            "tags": [
              "{{$json['choices'][0]['message']['content'].trim()}}"
            ],
            "priority": "normal"
          }
        },
        {
          "id": "assign_team",
          "type": "condition",
          "service": "Switch",
          "name": "담당팀 배정",
          "config": {
            "rules": [
              {
                "condition": "배송문의",
                "output": "delivery_team"
              },
              {
                "condition": "환불요청",
                "output": "refund_team"
              },
              {
                "condition": "기술지원",
                "output": "tech_team"
              },
              {
                "default": "general_team"
              }
            ]
          }
        },
        {
          "id": "notify_team",
          "type": "action",
          "service": "Slack",
          "name": "담당팀 Slack 알림",
          "config": {
            "operation": "sendMessage",
            "channel": "{{$env.CS_SLACK_CHANNEL}}",
            "message": "🎫 새 티켓 배정!\n\n유형: {{$node['classify_with_ai'].json['choices'][0]['message']['content'].trim()}}\n발신자: {{$node['trigger_gmail'].json['from']['value'][0]['address']}}\n제목: {{$node['trigger_gmail'].json['subject']}}\n\n👉 Zendesk에서 확인하세요!"
          }
        }
      ],
      "edges": [
        {
          "from": "trigger_gmail",
          "to": "load_faq"
        },
        {
          "from": "load_faq",
          "to": "match_keyword"
        },
        {
          "from": "match_keyword",
          "to": "check_match"
        },
        {
          "from": "check_match",
          "to": "send_auto_reply",
          "branch": "true"
        },
        {
          "from": "check_match",
          "to": "classify_with_ai",
          "branch": "false"
        },
        {
          "from": "classify_with_ai",
          "to": "create_ticket"
        },
        {
          "from": "create_ticket",
          "to": "assign_team"
        },
        {
          "from": "assign_team",
          "to": "notify_team"
        }
      ]
    },
    {
      "phase": "VIP 즉시 대응",
      "description": "VIP 고객 이메일 수신 시 FAQ 매칭 여부와 관계없이 팀장 Slack DM으로 즉시 에스컬레이션합니다.",
      "recipes": [
        "recipe043"
      ],
      "trigger": "Gmail Trigger — 새 이메일 수신 시 즉시 실행 (1차 자동 응대 워크플로우와 병렬)",
      "nodes": [
        {
          "id": "check_vip_list",
          "type": "action",
          "service": "Google Sheets",
          "name": "VIP 명단 대조",
          "config": {
            "operation": "readRows",
            "spreadsheetId": "{{$env.VIP_SHEET_ID}}",
            "sheetName": "VIP고객목록"
          }
        },
        {
          "id": "is_vip",
          "type": "condition",
          "service": "IF",
          "name": "VIP 여부 확인",
          "config": {
            "condition": "{{$node['check_vip_list'].json.map(r => r['이메일']).includes($json['from']['value'][0]['address'])}}"
          }
        },
        {
          "id": "escalate_to_manager",
          "type": "action",
          "service": "Slack",
          "name": "팀장 즉시 에스컬레이션",
          "config": {
            "operation": "sendDM",
            "userId": "{{$env.MANAGER_SLACK_ID}}",
            "message": "⭐ VIP 고객 문의 도착!\n\n👤 발신자: {{$json['from']['value'][0]['address']}}\n📌 제목: {{$json['subject']}}\n\n💬 내용:\n{{$json['text'].slice(0, 400)}}...\n\n👉 즉시 대응 부탁드립니다!"
          }
        }
      ],
      "edges": [
        {
          "from": "trigger_gmail",
          "to": "check_vip_list"
        },
        {
          "from": "check_vip_list",
          "to": "is_vip"
        },
        {
          "from": "is_vip",
          "to": "escalate_to_manager",
          "branch": "true"
        }
      ]
    },
    {
      "phase": "SLA 관리",
      "description": "30분마다 Zendesk에서 SLA 마감 1시간 미만 티켓을 조회하여 담당자에게 경고 알림을 발송합니다.",
      "recipes": [
        "recipe044"
      ],
      "trigger": "Schedule Trigger — 30분마다 실행",
      "nodes": [
        {
          "id": "trigger_sla_check",
          "type": "trigger",
          "service": "Schedule",
          "name": "30분마다 SLA 점검",
          "config": {
            "interval": "every 30 minutes"
          }
        },
        {
          "id": "get_sla_breach",
          "type": "action",
          "service": "Zendesk",
          "name": "SLA 임박 티켓 조회",
          "config": {
            "operation": "searchTickets",
            "query": "status:open sla_breach_at:<1h"
          }
        },
        {
          "id": "loop_sla_tickets",
          "type": "action",
          "service": "SplitInBatches",
          "name": "티켓별 알림 발송",
          "config": {
            "batchSize": 1
          }
        },
        {
          "id": "warn_assignee",
          "type": "action",
          "service": "Slack",
          "name": "담당자 경고",
          "config": {
            "operation": "sendDM",
            "userId": "{{$json['assignee_slack_id']}}",
            "message": "🚨 SLA 초과 임박!\n\n🎫 티켓 #{{$json['id']}}: {{$json['subject']}}\n⏰ 마감까지 {{$json['sla_remaining_minutes']}}분 남음\n\n👉 즉시 처리해주세요!"
          }
        }
      ],
      "edges": [
        {
          "from": "trigger_sla_check",
          "to": "get_sla_breach"
        },
        {
          "from": "get_sla_breach",
          "to": "loop_sla_tickets"
        },
        {
          "from": "loop_sla_tickets",
          "to": "warn_assignee"
        }
      ]
    },
    {
      "phase": "품질 관리",
      "description": "구매 3일 후 만족도 설문을 자동 발송하고, 부정 리뷰 감지 시 CS팀에 즉시 알림을 보냅니다.",
      "recipes": [
        "recipe045",
        "recipe046"
      ],
      "trigger": "Webhook(주문 완료) + Schedule Trigger(리뷰 수집 2시간마다)",
      "nodes": [
        {
          "id": "trigger_order",
          "type": "trigger",
          "service": "Webhook",
          "name": "주문 완료 수신",
          "config": {
            "httpMethod": "POST",
            "path": "/order-complete"
          }
        },
        {
          "id": "wait_3days",
          "type": "action",
          "service": "Wait",
          "name": "3일 대기",
          "config": {
            "waitType": "duration",
            "duration": 3,
            "unit": "days"
          }
        },
        {
          "id": "send_survey",
          "type": "action",
          "service": "Gmail",
          "name": "만족도 설문 발송",
          "config": {
            "operation": "sendEmail",
            "to": "{{$json['customerEmail']}}",
            "subject": "구매하신 상품은 만족스러우셨나요? 😊",
            "body": "안녕하세요, {{$json['customerName']}}님!\n\n구매해 주셔서 감사합니다. 짧은 설문에 참여해주시면 더 좋은 서비스를 만드는 데 큰 힘이 됩니다.\n\n👉 설문 참여하기 (1분): {{$env.SURVEY_FORM_URL}}\n\n감사합니다!\n{{$env.BRAND_NAME}} 드림"
          }
        },
        {
          "id": "trigger_review_check",
          "type": "trigger",
          "service": "Schedule",
          "name": "2시간마다 리뷰 수집",
          "config": {
            "interval": "every 2 hours"
          }
        },
        {
          "id": "get_reviews",
          "type": "action",
          "service": "HTTP Request",
          "name": "신규 리뷰 수집",
          "config": {
            "method": "GET",
            "url": "{{$env.REVIEW_API_URL}}",
            "headers": {
              "Authorization": "Bearer {{$env.REVIEW_API_KEY}}"
            },
            "queryParams": {
              "since": "{{$now.minus({hours: 2}).toISO()}}"
            }
          }
        },
        {
          "id": "analyze_review",
          "type": "action",
          "service": "OpenAI",
          "name": "리뷰 감성 분석",
          "config": {
            "operation": "chat",
            "model": "gpt-4o-mini",
            "messages": [
              {
                "role": "system",
                "content": "리뷰 감성 분석 전문가입니다. '긍정' 또는 '부정' 중 하나만 답하세요."
              },
              {
                "role": "user",
                "content": "별점: {{$json['rating']}}점\n내용: {{$json['content']}}"
              }
            ]
          }
        },
        {
          "id": "check_negative_review",
          "type": "condition",
          "service": "IF",
          "name": "부정 리뷰 여부",
          "config": {
            "condition": "{{$json['choices'][0]['message']['content'].trim() === '부정'}}"
          }
        },
        {
          "id": "alert_negative_review",
          "type": "action",
          "service": "Slack",
          "name": "CS팀 부정 리뷰 알림",
          "config": {
            "operation": "sendMessage",
            "channel": "{{$env.CS_SLACK_CHANNEL}}",
            "message": "🚨 부정 리뷰 감지!\n\n⭐ 별점: {{$node['get_reviews'].json['rating']}}점\n👤 작성자: {{$node['get_reviews'].json['author']}}\n\n💬 내용:\n{{$node['get_reviews'].json['content']}}\n\n👉 빠른 대응이 필요합니다!"
          }
        }
      ],
      "edges": [
        {
          "from": "trigger_order",
          "to": "wait_3days"
        },
        {
          "from": "wait_3days",
          "to": "send_survey"
        },
        {
          "from": "trigger_review_check",
          "to": "get_reviews"
        },
        {
          "from": "get_reviews",
          "to": "analyze_review"
        },
        {
          "from": "analyze_review",
          "to": "check_negative_review"
        },
        {
          "from": "check_negative_review",
          "to": "alert_negative_review",
          "branch": "true"
        }
      ]
    },
    {
      "phase": "월간 보고",
      "description": "매월 1일 Zendesk에서 지난 달 CS 성과 지표를 수집하여 Google Sheets에 기록하고 Slack으로 요약 리포트를 발송합니다.",
      "recipes": [
        "recipe050"
      ],
      "trigger": "Schedule Trigger — 매월 1일 오전 9시",
      "nodes": [
        {
          "id": "trigger_monthly",
          "type": "trigger",
          "service": "Schedule",
          "name": "매월 1일 오전 9시",
          "config": {
            "interval": "monthly",
            "dayOfMonth": 1,
            "time": "09:00"
          }
        },
        {
          "id": "get_monthly_stats",
          "type": "action",
          "service": "HTTP Request",
          "name": "Zendesk 월간 성과 조회",
          "config": {
            "method": "GET",
            "url": "https://{{$env.ZENDESK_DOMAIN}}.zendesk.com/api/v2/reports/overview.json",
            "authentication": "basicAuth",
            "username": "{{$env.ZENDESK_EMAIL}}/token",
            "password": "{{$env.ZENDESK_API_TOKEN}}",
            "queryParams": {
              "start_time": "{{$now.minus({months:1}).startOf('month').toUnixInteger()}}",
              "end_time": "{{$now.minus({months:1}).endOf('month').toUnixInteger()}}"
            }
          }
        },
        {
          "id": "record_monthly",
          "type": "action",
          "service": "Google Sheets",
          "name": "월간 성과 시트 기록",
          "config": {
            "operation": "appendRow",
            "spreadsheetId": "{{$env.CS_REPORT_SHEET_ID}}",
            "sheetName": "월간성과",
            "row": {
              "기준월": "{{$now.minus({months:1}).format('YYYY년 M월')}}",
              "총처리건수": "{{$json['tickets_solved']}}",
              "평균응답시간(분)": "{{$json['avg_reply_time_in_minutes']}}",
              "CSAT(%)": "{{$json['csat_score']}}"
            }
          }
        },
        {
          "id": "send_monthly_report",
          "type": "action",
          "service": "Slack",
          "name": "월간 리포트 발송",
          "config": {
            "operation": "sendMessage",
            "channel": "{{$env.CS_SLACK_CHANNEL}}",
            "message": "📊 {{$now.minus({months:1}).format('YYYY년 M월')}} CS 성과 리포트\n\n🎫 총 처리 건수: {{$json['tickets_solved']}}건\n⏱ 평균 응답 시간: {{$json['avg_reply_time_in_minutes']}}분\n😊 CSAT: {{$json['csat_score']}}%\n\n👉 상세 보기: {{$env.CS_REPORT_SHEET_URL}}"
          }
        }
      ],
      "edges": [
        {
          "from": "trigger_monthly",
          "to": "get_monthly_stats"
        },
        {
          "from": "get_monthly_stats",
          "to": "record_monthly"
        },
        {
          "from": "record_monthly",
          "to": "send_monthly_report"
        }
      ]
    }
  ],
  "envVariables": {
    "SUPPORT_EMAIL": "고객 응대 전용 이메일 주소 (예: support@mycompany.com)",
    "FAQ_SHEET_ID": "FAQ 구글 시트 ID",
    "VIP_SHEET_ID": "VIP 고객 명단 구글 시트 ID",
    "MANAGER_SLACK_ID": "팀장 Slack 사용자 ID",
    "CS_SLACK_CHANNEL": "CS팀 Slack 채널명 (예: #cs-team)",
    "ZENDESK_DOMAIN": "Zendesk 서브도메인",
    "ZENDESK_EMAIL": "Zendesk 로그인 이메일",
    "ZENDESK_API_TOKEN": "Zendesk API 토큰",
    "REVIEW_API_URL": "리뷰 수집 API 엔드포인트",
    "REVIEW_API_KEY": "리뷰 API 인증 키",
    "SURVEY_FORM_URL": "만족도 설문 구글 폼 URL",
    "CS_REPORT_SHEET_ID": "CS 성과 리포트 구글 시트 ID",
    "CS_REPORT_SHEET_URL": "CS 성과 리포트 구글 시트 공유 URL",
    "BRAND_NAME": "브랜드명"
  },
  "tips": [
    "소규모 팀일수록 이 시스템의 효과가 더 커요. 혼자서도 'CS팀이 있는 회사'처럼 운영할 수 있어요.",
    "FAQ 시트는 주 1회 업데이트를 권장해요. 자주 반복되는 문의를 추가할수록 자동 답변 커버율이 높아져요.",
    "VIP 명단은 영업팀·마케팅팀과 공유 시트로 관리하면 최신 상태를 유지하기 쉬워요.",
    "Wait 노드를 사용하는 만족도 설문 워크플로우는 n8n 클라우드 또는 상시 운영 서버 환경이 필요해요."
  ],
  "qrUrl": "www.koreaaitimes.com/books/n8n-vol2/recipe050p"
}
← 이전 레시피☰ 목차다음 레시피 →