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

고객 리뷰 자동 수집 → 긍정/부정 분류

  • ▸데이터 수집 워크플로우의 병목 지점을 파악할 수 있다
  • ▸SplitInBatches + Wait 노드로 API Rate Limit에 안전하게 대응할 수 있다
  • ▸에러 발생 시 자동 재시도(Retry) 패턴을 구현할 수 있다

✅사전 준비

  • □최적화 대상 워크플로우 1개 선정
  • □해당 워크플로우의 Executions 탭에서 각 노드 실행 시간 확인
  • □사용 중인 API의 Rate Limit 문서 확인 (분당/시간당 호출 한도)

SplitInBatches + Wait 조합 — Rate Limit 대응

SplitInBatches 설정:
  Batch Size: 10  (API 한도에 맞게 조정)

Wait 노드 설정 (SplitInBatches 뒤에 연결):
  Wait Amount: 1
  Wait Unit:   seconds

→ 10개 처리 후 1초 대기 반복

자동 재시도 패턴 — Error Trigger 활용

1. Error Trigger 노드 추가 (별도 워크플로우)
2. 실패한 워크플로우 ID와 실행 ID를 수신
3. Wait 노드: 5분 대기
4. Execute Workflow 노드로 원본 워크플로우 재실행

※ 무한 루프 방지:
   Sheets에 재시도 횟수 기록 → 3회 초과 시 Slack 알림 후 중단

실행 시간 측정 — Code 노드

// 워크플로우 시작 시 저장
const startTime = Date.now();
return [{ json: { startTime } }];

// 워크플로우 종료 시 계산
const elapsed = Date.now() - $('시작노드').first().json.startTime;
return [{ json: { elapsedMs: elapsed, elapsedSec: (elapsed / 1000).toFixed(1) } }];
💡 TIPn8n Executions 탭에서 각 노드의 실행 시간을 확인할 수 있어요. 가장 느린 노드부터 최적화하는 게 효율적입니다. 대부분의 병목은 외부 API 호출이나 대량 Sheets 쓰기에서 발생합니다.
⚠️ 주의n8n 클라우드 무료 플랜은 실행 시간이 5분으로 제한됩니다. 대량 데이터 처리 워크플로우는 Self-hosted 환경이나 유료 플랜을 권장합니다.
⚡

워크플로우 JSON

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

📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
  "name": "recipe020_review_sentiment_classification",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * *"
            }
          ]
        }
      },
      "id": "schedule-020-01",
      "name": "매일 오전 9시",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        400
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $env.REVIEW_API_URL }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {}
      },
      "id": "http-020-02",
      "name": "리뷰 데이터 수집",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        420,
        400
      ]
    },
    {
      "parameters": {
        "model": "gpt-4o-mini",
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "다음 리뷰가 긍정적인지 부정적인지 판단해서 '긍정' 또는 '부정' 중 하나만 답하세요."
            },
            {
              "role": "user",
              "content": "={{ $json.review_text }}"
            }
          ]
        },
        "options": {}
      },
      "id": "openai-020-03",
      "name": "AI 감성 분류",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.7,
      "position": [
        640,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "leftValue": "={{ $json.message.content }}",
              "rightValue": "부정",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "if-020-04",
      "name": "부정 리뷰 여부 확인",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        860,
        400
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "post",
        "channel": "#cs-alerts",
        "text": "=⚠️ 부정 리뷰 감지!\n리뷰 내용: {{ $('리뷰 데이터 수집').item.json.review_text }}\n작성자: {{ $('리뷰 데이터 수집').item.json.author }}\n별점: {{ $('리뷰 데이터 수집').item.json.rating }}점"
      },
      "id": "slack-020-05",
      "name": "CS팀 즉시 알림",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1080,
        300
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "value": "={{ $env.REVIEW_SHEET_ID }}"
        },
        "sheetName": {
          "value": "reviews"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "reviewText": "={{ $('리뷰 데이터 수집').item.json.review_text }}",
            "author": "={{ $('리뷰 데이터 수집').item.json.author }}",
            "rating": "={{ $('리뷰 데이터 수집').item.json.rating }}",
            "sentiment": "={{ $('AI 감성 분류').item.json.message.content }}",
            "collectedAt": "={{ $now.format('YYYY-MM-DD HH:mm') }}"
          }
        }
      },
      "id": "sheets-020-06",
      "name": "전체 리뷰 시트 기록",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        1080,
        500
      ]
    }
  ],
  "connections": {
    "매일 오전 9시": {
      "main": [
        [
          {
            "node": "리뷰 데이터 수집",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "리뷰 데이터 수집": {
      "main": [
        [
          {
            "node": "AI 감성 분류",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI 감성 분류": {
      "main": [
        [
          {
            "node": "부정 리뷰 여부 확인",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "부정 리뷰 여부 확인": {
      "main": [
        [
          {
            "node": "CS팀 즉시 알림",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "전체 리뷰 시트 기록",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CS팀 즉시 알림": {
      "main": [
        [
          {
            "node": "전체 리뷰 시트 기록",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "timezone": "Asia/Seoul"
  },
  "meta": {
    "chapter": 2,
    "recipe": "020",
    "title": "고객 리뷰 자동 수집 → 긍정/부정 분류",
    "difficulty": "⭐⭐⭐",
    "estimatedTime": "50분",
    "apps": [
      "HTTP Request",
      "OpenAI",
      "Google Sheets",
      "Slack"
    ]
  }
}
← 이전 레시피☰ 목차다음 레시피 →