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

Mailchimp 캠페인 성과 → Sheets 자동 기록

  • ▸Mailchimp 이메일 캠페인 발송 후 성과 지표 자동 수집
  • ▸오픈율, 클릭률, 구독 취소율을 Google Sheets에 기록
  • ▸성과 기준 미달 시 마케팅팀 Slack 알림

✅사전 준비

  • □Mailchimp API 키 발급 (Account → Extras → API Keys)
  • □Mailchimp 서버 접두사 확인 (API 키 끝 예: us21)
  • □Google Sheets 성과 기록용 시트 생성
  • □KPI 기준값 설정 (예: 오픈율 20% 이상)

캠페인 목록 조회

Node: HTTP Request
Method: GET
URL: https://us21.api.mailchimp.com/3.0/campaigns
Query Parameters:
  status: sent
  count: 10
  sort_field: send_time
  sort_dir: DESC
Authentication: Basic Auth
Username: anystring
Password: YOUR_API_KEY

캠페인 리포트 조회

Node: HTTP Request
Method: GET
URL: https://us21.api.mailchimp.com/3.0/reports/{{ $json.id }}
Authentication: Basic Auth
Username: anystring
Password: YOUR_API_KEY

// 추출 필드
openRate: $json.opens.open_rate
clickRate: $json.clicks.click_rate
unsubscribeRate: $json.unsubscribes.unsubscribe_rate

성과 미달 알림 조건 (IF 노드)

Condition 1: {{ $json.opens.open_rate }} < 0.2 (오픈율 20% 미만)
Condition 2: {{ $json.clicks.click_rate }} < 0.02 (클릭률 2% 미만)

두 조건 중 하나라도 충족 시 → Slack 알림
💡 TIPMailchimp 서버 접두사(us1, us21 등)는 계정마다 다릅니다. API 키 마지막 부분(예: `abc123-us21`)에서 `us21`이 서버 접두사입니다.
⚡

워크플로우 JSON

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

📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
  "recipeId": "recipe060",
  "title": "HR KPI 대시보드 자동 업데이트",
  "chapter": 5,
  "category": "HR & 조직 운영 자동화",
  "difficulty": "⭐⭐",
  "estimatedTime": "35분",
  "usedApps": [
    "Google Sheets",
    "Slack"
  ],
  "description": "매주 월요일 오전 8시에 직원 명단 시트에서 입사자·퇴직자·현재 직원 수·이직률 등 HR 핵심 지표를 자동 집계하여 대시보드 시트를 업데이트하고 주요 변화를 Slack에 요약 발송합니다.",
  "nodes": [
    {
      "id": "trigger_schedule",
      "type": "trigger",
      "service": "Schedule",
      "name": "매주 월요일 오전 8시",
      "config": {
        "interval": "weekly",
        "dayOfWeek": "monday",
        "time": "08:00"
      }
    },
    {
      "id": "get_employee_data",
      "type": "action",
      "service": "Google Sheets",
      "name": "직원 명단 전체 조회",
      "config": {
        "operation": "readRows",
        "spreadsheetId": "{{$env.HR_SHEET_ID}}",
        "sheetName": "직원명단",
        "columns": [
          "이름",
          "입사일",
          "퇴직일",
          "재직상태",
          "부서",
          "직책"
        ]
      }
    },
    {
      "id": "calc_kpi",
      "type": "action",
      "service": "Function",
      "name": "HR KPI 계산",
      "config": {
        "code": "const employees = $items();\nconst today = new Date($now.toISO());\nconst thisWeekStart = new Date(today);\nthisWeekStart.setDate(today.getDate() - 7);\nconst thisMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);\n\n// 현재 재직자\nconst activeEmployees = employees.filter(e => e.json['재직상태'] === '재직');\n\n// 이번 주 입사자\nconst newHiresThisWeek = employees.filter(e => {\n  const joinDate = new Date(e.json['입사일']);\n  return joinDate >= thisWeekStart && joinDate <= today;\n});\n\n// 이번 달 퇴직자\nconst resignedThisMonth = employees.filter(e => {\n  if (!e.json['퇴직일']) return false;\n  const resignDate = new Date(e.json['퇴직일']);\n  return resignDate >= thisMonthStart && resignDate <= today;\n});\n\n// 이직률 (월간)\nconst turnoverRate = activeEmployees.length > 0 \n  ? ((resignedThisMonth.length / activeEmployees.length) * 100).toFixed(1)\n  : 0;\n\n// 평균 근속 기간 (개월)\nconst avgTenure = activeEmployees.length > 0\n  ? (activeEmployees.reduce((sum, e) => {\n      const joinDate = new Date(e.json['입사일']);\n      const months = (today - joinDate) / (1000 * 60 * 60 * 24 * 30);\n      return sum + months;\n    }, 0) / activeEmployees.length).toFixed(1)\n  : 0;\n\n// 부서별 인원\nconst deptCount = {};\nactiveEmployees.forEach(e => {\n  const dept = e.json['부서'] || '미분류';\n  deptCount[dept] = (deptCount[dept] || 0) + 1;\n});\n\nreturn [{ json: {\n  week: $now.format('YYYY-WW주'),\n  totalActive: activeEmployees.length,\n  newHiresThisWeek: newHiresThisWeek.length,\n  resignedThisMonth: resignedThisMonth.length,\n  turnoverRate: parseFloat(turnoverRate),\n  avgTenureMonths: parseFloat(avgTenure),\n  deptBreakdown: JSON.stringify(deptCount),\n  recordedAt: $now.format('YYYY-MM-DD HH:mm')\n} }];"
      }
    },
    {
      "id": "update_dashboard",
      "type": "action",
      "service": "Google Sheets",
      "name": "KPI 대시보드 업데이트",
      "config": {
        "operation": "appendRow",
        "spreadsheetId": "{{$env.HR_SHEET_ID}}",
        "sheetName": "HR_KPI대시보드",
        "row": {
          "기준주차": "{{$json['week']}}",
          "총재직자수": "{{$json['totalActive']}}",
          "이번주신규입사": "{{$json['newHiresThisWeek']}}",
          "이번달퇴직자": "{{$json['resignedThisMonth']}}",
          "이직률(%)": "{{$json['turnoverRate']}}",
          "평균근속(개월)": "{{$json['avgTenureMonths']}}",
          "부서별인원": "{{$json['deptBreakdown']}}",
          "기록일시": "{{$json['recordedAt']}}"
        }
      }
    },
    {
      "id": "get_last_week",
      "type": "action",
      "service": "Google Sheets",
      "name": "전주 데이터 조회",
      "config": {
        "operation": "readRows",
        "spreadsheetId": "{{$env.HR_SHEET_ID}}",
        "sheetName": "HR_KPI대시보드",
        "limit": 2
      }
    },
    {
      "id": "calc_change",
      "type": "action",
      "service": "Function",
      "name": "전주 대비 변화 계산",
      "config": {
        "code": "const rows = $items();\nif (rows.length < 2) return [{ json: { changeMsg: '(이전 데이터 없음)' } }];\n\nconst current = rows[rows.length - 1].json;\nconst previous = rows[rows.length - 2].json;\n\nconst headcountChange = current['총재직자수'] - previous['총재직자수'];\nconst turnoverChange = (current['이직률(%)'] - previous['이직률(%)']).toFixed(1);\n\nconst sign = n => n > 0 ? `+${n}` : `${n}`;\n\nreturn [{ json: {\n  ...current,\n  headcountChange: sign(headcountChange),\n  turnoverChange: sign(turnoverChange)\n} }];"
      }
    },
    {
      "id": "send_slack_summary",
      "type": "action",
      "service": "Slack",
      "name": "주간 HR KPI 요약 발송",
      "config": {
        "operation": "sendMessage",
        "channel": "{{$env.HR_SLACK_CHANNEL}}",
        "message": "📊 {{$json['week']}} HR KPI 현황\n\n👥 총 재직자: {{$json['totalActive']}}명 (전주 대비 {{$json['headcountChange']}}명)\n🆕 이번 주 신규 입사: {{$json['newHiresThisWeek']}}명\n👋 이번 달 퇴직: {{$json['resignedThisMonth']}}명\n📉 이직률: {{$json['turnoverRate']}}% (전주 대비 {{$json['turnoverChange']}}%p)\n⏳ 평균 근속: {{$json['avgTenureMonths']}}개월\n\n👉 대시보드 전체 보기: {{$env.HR_DASHBOARD_URL}}"
      }
    }
  ],
  "edges": [
    {
      "from": "trigger_schedule",
      "to": "get_employee_data"
    },
    {
      "from": "get_employee_data",
      "to": "calc_kpi"
    },
    {
      "from": "calc_kpi",
      "to": "update_dashboard"
    },
    {
      "from": "update_dashboard",
      "to": "get_last_week"
    },
    {
      "from": "get_last_week",
      "to": "calc_change"
    },
    {
      "from": "calc_change",
      "to": "send_slack_summary"
    }
  ],
  "tips": [
    "이직률이 전주 대비 2% 이상 급등한 경우 HR 팀장에게 별도 DM으로 경고 알림을 보내는 IF 분기를 추가해보세요.",
    "부서별 인원 현황은 Google Sheets 피벗 차트와 연동하면 시각적으로 표현할 수 있어요.",
    "채용 중인 포지션 수와 평균 채용 기간(Time to Fill)도 함께 추적하면 더욱 완성도 높은 HR 대시보드가 돼요."
  ],
  "qrUrl": "www.koreaaitimes.com/books/n8n-vol2/recipe060"
}
← 이전 레시피☰ 목차다음 레시피 →