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

A/B 테스트 결과 자동 비교 리포트

  • ▸n8n Webhook을 엔드포인트로 외부 서비스의 알림을 수신할 수 있다
  • ▸수신된 데이터에서 키워드를 감지해 조건별 알림을 라우팅할 수 있다
  • ▸Google 알리미(Google Alerts) 없이 맞춤 키워드 모니터링을 구현할 수 있다

✅사전 준비

  • □n8n Webhook 노드로 URL 생성 후 복사
  • □IFTTT 또는 Make(Integromat) 무료 계정으로 Google Alerts → Webhook 연결
  • □또는 RSS.app 서비스로 Google Alerts → RSS → n8n RSS 수신 구성
  • □Slack 자격증명 연결 완료

RSS.app으로 Google Alerts RSS 만들기

1. https://rss.app 접속 → Create RSS Feed
2. Google Alerts URL 붙여넣기
   (https://www.google.com/alerts?q=모니터링키워드)
3. 생성된 RSS URL을 n8n RSS Feed Read 노드에 입력

→ API 키 없이 Google Alerts를 RSS로 변환 가능

키워드 우선순위 분류 — Code 노드

const text = ($json.title + " " + $json.content).toLowerCase();

const critical = ["해킹", "데이터 유출", "서버 다운"];
const important = ["업데이트", "출시", "발표"];

let priority = "일반";
if (critical.some(k => text.includes(k))) priority = "긴급";
else if (important.some(k => text.includes(k))) priority = "중요";

return [{ json: { ...$json, priority } }];

Slack 알림 — 우선순위별 포맷

{{ $json.priority === "긴급" ? "🚨" : $json.priority === "중요" ? "⚠️" : "📌" }} [{{ $json.priority }}] {{ $json.title }}
{{ $json.link }}
💡 TIPGoogle Alerts 자체 이메일 알림을 Gmail Trigger로 직접 수신하는 방법도 있어요. Gmail에서 Google Alerts 전용 레이블을 만들고 Gmail Trigger의 Label 필터에 지정하면 됩니다.
⚡

워크플로우 JSON

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

📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
  "name": "recipe017_ab_test_report",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10 * * *"
            }
          ]
        }
      },
      "id": "schedule-017-01",
      "name": "매일 오전 10시",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        400
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://us1.api.mailchimp.com/3.0/campaigns/{{ $env.AB_TEST_CAMPAIGN_A_ID }}/reports",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "id": "http-017-02",
      "name": "A안 성과 가져오기",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        420,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://us1.api.mailchimp.com/3.0/campaigns/{{ $env.AB_TEST_CAMPAIGN_B_ID }}/reports",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "id": "http-017-03",
      "name": "B안 성과 가져오기",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        420,
        500
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "combinationMode": "mergeByPosition",
        "options": {}
      },
      "id": "merge-017-04",
      "name": "A/B 데이터 합치기",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        640,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "const a = $input.all()[0]?.json || {};\nconst b = $input.all()[1]?.json || {};\nconst aOpenRate = a.opens?.open_rate ? (a.opens.open_rate * 100).toFixed(1) : '0.0';\nconst bOpenRate = b.opens?.open_rate ? (b.opens.open_rate * 100).toFixed(1) : '0.0';\nconst winner = parseFloat(aOpenRate) >= parseFloat(bOpenRate) ? 'A안' : 'B안';\nconst diff = Math.abs(parseFloat(aOpenRate) - parseFloat(bOpenRate)).toFixed(1);\nreturn [{ json: {\n  aTitle: a.campaign_title || 'A안',\n  bTitle: b.campaign_title || 'B안',\n  aOpenRate,\n  bOpenRate,\n  winner,\n  diff\n}}];"
      },
      "id": "code-017-05",
      "name": "승리 버전 계산",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        860,
        400
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "post",
        "channel": "#marketing-content",
        "text": "=🏆 A/B 테스트 결과\nA안 \"{{ $json.aTitle }}\": 오픈율 {{ $json.aOpenRate }}%\nB안 \"{{ $json.bTitle }}\": 오픈율 {{ $json.bOpenRate }}%\n→ 승리: {{ $json.winner }} (+{{ $json.diff }}%p)"
      },
      "id": "slack-017-06",
      "name": "Slack 결과 발송",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1080,
        400
      ]
    }
  ],
  "connections": {
    "매일 오전 10시": {
      "main": [
        [
          {
            "node": "A안 성과 가져오기",
            "type": "main",
            "index": 0
          },
          {
            "node": "B안 성과 가져오기",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "A안 성과 가져오기": {
      "main": [
        [
          {
            "node": "A/B 데이터 합치기",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "B안 성과 가져오기": {
      "main": [
        [
          {
            "node": "A/B 데이터 합치기",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "A/B 데이터 합치기": {
      "main": [
        [
          {
            "node": "승리 버전 계산",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "승리 버전 계산": {
      "main": [
        [
          {
            "node": "Slack 결과 발송",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "timezone": "Asia/Seoul"
  },
  "meta": {
    "chapter": 2,
    "recipe": "017",
    "title": "A/B 테스트 결과 자동 비교 리포트",
    "difficulty": "⭐⭐⭐",
    "estimatedTime": "45분",
    "apps": [
      "Mailchimp API",
      "Google Sheets",
      "Slack"
    ]
  }
}
← 이전 레시피☰ 목차다음 레시피 →