RECIPE 019
경쟁사 가격 모니터링 → 변동 알림
- ▸여러 RSS 피드를 병렬로 수집해 Merge 노드로 합칠 수 있다
- ▸수집된 기사를 OpenAI로 요약해 뉴스레터 형식으로 가공할 수 있다
- ▸Gmail로 HTML 뉴스레터를 구독자 목록에 일괄 발송할 수 있다
✅사전 준비
- □수집할 RSS 피드 URL 3~5개 준비
- □OpenAI API 키 및 자격증명 연결 완료
- □Gmail 자격증명 연결 완료
- □Google Sheets에 구독자 이메일 목록 시트 생성
- □Schedule Trigger: 매주 금요일 오후 5시 (Cron: 0 17 * * 5)
Merge 노드 설정값
Mode: Append
→ 여러 RSS Feed Read 노드의 출력을 하나로 합침
연결 방법:
RSS Feed 1 → Merge
RSS Feed 2 → Merge
RSS Feed 3 → Merge (노드를 추가할수록 입력 핀이 늘어남)기사 요약 — OpenAI 노드 프롬프트
Model: gpt-4o-mini (비용 절감)
Prompt:
다음 뉴스 기사 제목을 보고 1~2줄로 핵심만 한국어로 요약해줘.
독자가 클릭 여부를 판단할 수 있게 간결하게 작성해.
제목: {{ $json.title }}
내용 (있을 경우): {{ $json.contentSnippet?.slice(0, 500) }}뉴스레터 HTML 조립 — Code 노드
const articles = $input.all();
const body = articles.map(a => `
<div style="margin-bottom:20px;padding-bottom:20px;border-bottom:1px solid #eee">
<a href="${a.json.link}" style="font-size:16px;font-weight:bold;color:#1a73e8">${a.json.title}</a>
<p style="color:#555;margin:6px 0">${a.json.summary ?? ""}</p>
<small style="color:#999">${a.json.pubDate ?? ""}</small>
</div>
`).join("");
return [{
json: {
html: `
<div style="max-width:600px;margin:0 auto;font-family:sans-serif">
<h2 style="color:#1a73e8">📰 주간 AI 뉴스레터</h2>
<p style="color:#666">${new Date().toLocaleDateString("ko-KR")} 발행</p>
${body}
<p style="color:#999;font-size:12px">n8n 자동화로 발행됩니다.</p>
</div>
`,
}
}];💡 TIP구독자 수가 많다면 Gmail 일일 발송 한도(무료 500건)에 주의하세요. 대량 발송이 필요하면 SendGrid 또는 Mailgun 노드로 교체하는 게 좋습니다.
⚡
워크플로우 JSON
아래 JSON을 복사해 n8n에 바로 임포트하세요
📥 n8n 임포트 방법
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
n8n 화면 우측 상단 메뉴 (⋮) → Import from JSON → 아래 JSON 전체 복사 후 붙여넣기 → Import 클릭
{
"name": "recipe019_competitor_price_monitoring",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9,18 * * *"
}
]
}
},
"id": "schedule-019-01",
"name": "하루 2회 (오전 9시, 오후 6시)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
200,
400
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.COMPETITOR_PRODUCT_URL }}",
"options": {
"response": {
"response": {
"responseFormat": "text"
}
}
}
},
"id": "http-019-02",
"name": "경쟁사 상품 페이지 가져오기",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
420,
400
]
},
{
"parameters": {
"jsCode": "const html = $json.data || '';\nconst priceMatch = html.match(/[\\d,]+원/) || html.match(/\\\\$[\\d,\\\\.]+/);\nconst currentPrice = priceMatch ? parseInt(priceMatch[0].replace(/[^0-9]/g, '')) : 0;\nreturn [{ json: { currentPrice, checkedAt: $now.format('YYYY-MM-DD HH:mm') } }];"
},
"id": "code-019-03",
"name": "가격 데이터 추출",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
640,
400
]
},
{
"parameters": {
"operation": "read",
"documentId": {
"value": "={{ $env.PRICE_MONITOR_SHEET_ID }}"
},
"sheetName": {
"value": "prices"
},
"options": {}
},
"id": "sheets-019-04",
"name": "이전 가격 조회",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
860,
400
]
},
{
"parameters": {
"jsCode": "const currentPrice = $('가격 데이터 추출').item.json.currentPrice;\nconst prevData = $input.all();\nconst prevPrice = prevData.length > 0 ? parseInt(prevData[prevData.length - 1].json.price || 0) : 0;\nconst changed = prevPrice > 0 && currentPrice !== prevPrice;\nreturn [{ json: { currentPrice, prevPrice, changed, diff: currentPrice - prevPrice } }];"
},
"id": "code-019-05",
"name": "가격 변동 감지",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1080,
400
]
},
{
"parameters": {
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.changed }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
]
}
},
"id": "if-019-06",
"name": "가격 변동 여부 확인",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1300,
400
]
},
{
"parameters": {
"resource": "message",
"operation": "post",
"channel": "#marketing-pricing",
"text": "=💰 가격 변동 감지!\n이전 가격: ₩{{ $json.prevPrice.toLocaleString() }}\n현재 가격: ₩{{ $json.currentPrice.toLocaleString() }} ({{ $json.diff > 0 ? '↑' : '↓' }} ₩{{ Math.abs($json.diff).toLocaleString() }})"
},
"id": "slack-019-07",
"name": "Slack 가격 변동 알림",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1520,
300
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"value": "={{ $env.PRICE_MONITOR_SHEET_ID }}"
},
"sheetName": {
"value": "prices"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"price": "={{ $('가격 변동 감지').item.json.currentPrice }}",
"checkedAt": "={{ $now.format('YYYY-MM-DD HH:mm') }}"
}
}
},
"id": "sheets-019-08",
"name": "가격 이력 기록",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
1520,
500
]
}
],
"connections": {
"하루 2회 (오전 9시, 오후 6시)": {
"main": [
[
{
"node": "경쟁사 상품 페이지 가져오기",
"type": "main",
"index": 0
}
]
]
},
"경쟁사 상품 페이지 가져오기": {
"main": [
[
{
"node": "가격 데이터 추출",
"type": "main",
"index": 0
}
]
]
},
"가격 데이터 추출": {
"main": [
[
{
"node": "이전 가격 조회",
"type": "main",
"index": 0
}
]
]
},
"이전 가격 조회": {
"main": [
[
{
"node": "가격 변동 감지",
"type": "main",
"index": 0
}
]
]
},
"가격 변동 감지": {
"main": [
[
{
"node": "가격 변동 여부 확인",
"type": "main",
"index": 0
}
]
]
},
"가격 변동 여부 확인": {
"main": [
[
{
"node": "Slack 가격 변동 알림",
"type": "main",
"index": 0
}
],
[
{
"node": "가격 이력 기록",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"timezone": "Asia/Seoul"
},
"meta": {
"chapter": 2,
"recipe": "019",
"title": "경쟁사 가격 모니터링 → 변동 알림",
"difficulty": "⭐⭐⭐",
"estimatedTime": "45분",
"apps": [
"HTTP Request",
"Google Sheets",
"Slack"
]
}
}