The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "\ud30c\uc774\ud504\ub77c\uc778 \u2462 \ub9e4\ub274\uc5bc \uc790\ub3d9 \uc5c5\ub370\uc774\ud2b8",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hour": 9
}
]
}
},
"id": "schedule-trigger",
"name": "\ub9e4\uc77c \uc624\uc804 9\uc2dc",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.notion.com/v1/databases/{{ $env.NOTION_VOC_DB_ID }}/query",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "notionApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Notion-Version",
"value": "2022-06-28"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"filter\": {\n \"and\": [\n {\n \"property\": \"\uc218\uc9d1\uc77c\",\n \"date\": {\n \"past_week\": {}\n }\n },\n {\n \"property\": \"\ucc98\ub9ac\uc0c1\ud0dc\",\n \"select\": {\n \"equals\": \"\uc2e0\uaddc\"\n }\n }\n ]\n }\n}"
},
"id": "get-recent-voc",
"name": "\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
220,
0
]
},
{
"parameters": {
"jsCode": "// \uc9c8\ubb38 \uc720\ud615\ubcc4 \ud074\ub7ec\uc2a4\ud130\ub9c1 (\ud0a4\uc6cc\ub4dc \uae30\ubc18)\nconst vocData = $input.first().json;\n\nif (!vocData.results || vocData.results.length === 0) {\n return [{ json: { has_patterns: false, message: '\ubd84\uc11d\ud560 VOC \uc5c6\uc74c' } }];\n}\n\n// \uc0c1\ud488\ubcc4, \uce74\ud14c\uace0\ub9ac\ubcc4 \uc9c8\ubb38 \uadf8\ub8f9\ud551\nconst patterns = {};\n\nfor (const item of vocData.results) {\n const productName = item.properties['\uc0c1\ud488\uba85']?.rich_text?.[0]?.text?.content || 'Unknown';\n const category = item.properties['\uce74\ud14c\uace0\ub9ac']?.select?.name || '\uae30\ud0c0';\n const question = item.properties['\uc9c8\ubb38']?.rich_text?.[0]?.text?.content || '';\n \n const key = `${productName}::${category}`;\n \n if (!patterns[key]) {\n patterns[key] = {\n product_name: productName,\n category: category,\n questions: [],\n count: 0\n };\n }\n \n patterns[key].questions.push(question);\n patterns[key].count++;\n}\n\n// 3\ud68c \uc774\uc0c1 \ubc18\ubcf5\ub41c \ud328\ud134\ub9cc \ucd94\ucd9c\nconst repeatedPatterns = Object.values(patterns).filter(p => p.count >= 3);\n\nif (repeatedPatterns.length === 0) {\n return [{ json: { has_patterns: false, message: '\ubc18\ubcf5 \ud328\ud134 \uc5c6\uc74c (\uc784\uacc4\uac12: 3\ud68c)' } }];\n}\n\nreturn [{\n json: {\n has_patterns: true,\n patterns: repeatedPatterns,\n analyzed_at: new Date().toISOString()\n }\n}];"
},
"id": "cluster-questions",
"name": "\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.has_patterns }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "if-has-patterns",
"name": "\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
660,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.notion.com/v1/databases/{{ $env.NOTION_MANUAL_DB_ID }}/query",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "notionApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Notion-Version",
"value": "2022-06-28"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"filter\": {\n \"property\": \"\uc0c1\ud488\uba85\",\n \"title\": {\n \"contains\": \"{{ $json.patterns[0].product_name }}\"\n }\n }\n}"
},
"id": "get-existing-manual",
"name": "\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
880,
-100
]
},
{
"parameters": {
"jsCode": "// \uae30\uc874 \ub9e4\ub274\uc5bc\uacfc \uc2e0\uaddc \uc9c8\ubb38 \ub370\uc774\ud130 \ubcd1\ud569\nconst patternsData = $('\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?').first().json;\nconst manualData = $input.first().json;\n\nlet existingFaq = '';\nif (manualData.results && manualData.results.length > 0) {\n existingFaq = manualData.results[0].properties['FAQ']?.rich_text?.[0]?.text?.content || '';\n}\n\nreturn [{\n json: {\n product_name: patternsData.patterns[0].product_name,\n category: patternsData.patterns[0].category,\n existing_faq: existingFaq,\n new_questions: patternsData.patterns[0].questions,\n question_count: patternsData.patterns[0].count\n }\n}];"
},
"id": "merge-for-gemini",
"name": "Gemini \uc785\ub825 \uc900\ube44",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1100,
-100
]
},
{
"parameters": {
"method": "POST",
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent",
"authentication": "genericCredentialType",
"genericAuthType": "httpQueryAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"contents\": [{\n \"parts\": [{\n \"text\": \"\ub2f9\uc2e0\uc740 CS \ub9e4\ub274\uc5bc \uad00\ub9ac\uc790\uc785\ub2c8\ub2e4.\\n\\n[\uae30\uc874 \ub9e4\ub274\uc5bc FAQ]\\n{{ $json.existing_faq }}\\n\\n[\uc2e0\uaddc \uace0\uac1d \uc9c8\ubb38\ub4e4 - {{ $json.question_count }}\uac74 \ubc18\ubcf5]\\n{{ $json.new_questions.join('\\n') }}\\n\\n\uc704 \uc2e0\uaddc \uc9c8\ubb38\ub4e4\uc744 \ubd84\uc11d\ud558\uc5ec:\\n1. \uae30\uc874 \ub9e4\ub274\uc5bc\uc5d0\uc11c \ubd80\uc871\ud55c \ubd80\ubd84 \uc2dd\ubcc4\\n2. \ucd94\uac00\ud574\uc57c \ud560 FAQ \ud56d\ubaa9 \uc81c\uc548\\n3. \uc218\uc815\uc774 \ud544\uc694\ud55c \uae30\uc874 \ub2f5\ubcc0 \uac1c\uc120\uc548\\n\\n[\ucd9c\ub825 \ud615\uc2dd - JSON]\\n{\\n \\\"analysis\\\": \\\"\ubd80\uc871\ud55c \ubd80\ubd84 \ubd84\uc11d\\\",\\n \\\"new_faq\\\": [\\n {\\\"q\\\": \\\"\ucd94\uac00 \uc9c8\ubb381\\\", \\\"a\\\": \\\"\uad8c\uc7a5 \ub2f5\ubcc01\\\"}\\n ],\\n \\\"improvements\\\": \\\"\uae30\uc874 \ub2f5\ubcc0 \uac1c\uc120 \uc81c\uc548\\\"\\n}\"\n }]\n }],\n \"generationConfig\": {\n \"temperature\": 0.7,\n \"maxOutputTokens\": 2048\n }\n}"
},
"id": "gemini-analyze",
"name": "Gemini \ubcf4\uc644\uc548 \uc0dd\uc131",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1320,
-100
]
},
{
"parameters": {
"jsCode": "// Gemini \uc751\ub2f5 \ud30c\uc2f1\nconst response = $input.first().json;\nconst inputData = $('Gemini \uc785\ub825 \uc900\ube44').first().json;\n\nlet suggestion = {};\ntry {\n const text = response.candidates[0].content.parts[0].text;\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n suggestion = JSON.parse(jsonMatch[0]);\n }\n} catch (e) {\n suggestion = {\n analysis: '\ud30c\uc2f1 \uc2e4\ud328',\n new_faq: [],\n improvements: ''\n };\n}\n\nreturn [{\n json: {\n product_name: inputData.product_name,\n category: inputData.category,\n ...suggestion,\n generated_at: new Date().toISOString(),\n status: '\uac80\ud1a0\ub300\uae30'\n }\n}];"
},
"id": "parse-suggestion",
"name": "\ubcf4\uc644\uc548 \ud30c\uc2f1",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1540,
-100
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.JANDI_WEBHOOK_URL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"body\": \"[\ub9e4\ub274\uc5bc \uc5c5\ub370\uc774\ud2b8] \uac80\ud1a0 \uc694\uccad\",\n \"connectColor\": \"#FAC11B\",\n \"connectInfo\": [\n {\n \"title\": \"\uc0c1\ud488\",\n \"description\": \"{{ $json.product_name }}\"\n },\n {\n \"title\": \"\ubc18\ubcf5 \uc9c8\ubb38 \uc720\ud615\",\n \"description\": \"{{ $json.category }}\"\n },\n {\n \"title\": \"\ubd84\uc11d \uacb0\uacfc\",\n \"description\": \"{{ $json.analysis }}\"\n },\n {\n \"title\": \"\ucd94\uac00 FAQ \uc218\",\n \"description\": \"{{ $json.new_faq.length }}\uac74\"\n }\n ]\n}"
},
"id": "jandi-review-request",
"name": "\uc794\ub514 \uac80\ud1a0 \uc694\uccad",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1760,
-100
]
},
{
"parameters": {},
"id": "no-pattern",
"name": "\ud328\ud134 \uc5c6\uc74c - \uc885\ub8cc",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
880,
100
]
}
],
"connections": {
"\ub9e4\uc77c \uc624\uc804 9\uc2dc": {
"main": [
[
{
"node": "\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c",
"type": "main",
"index": 0
}
]
]
},
"\ucd5c\uadfc 7\uc77c VOC \uc870\ud68c": {
"main": [
[
{
"node": "\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1",
"type": "main",
"index": 0
}
]
]
},
"\uc9c8\ubb38 \uc720\ud615 \ud074\ub7ec\uc2a4\ud130\ub9c1": {
"main": [
[
{
"node": "\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?",
"type": "main",
"index": 0
}
]
]
},
"\ubc18\ubcf5 \ud328\ud134 \uc788\uc74c?": {
"main": [
[
{
"node": "\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud328\ud134 \uc5c6\uc74c - \uc885\ub8cc",
"type": "main",
"index": 0
}
]
]
},
"\uae30\uc874 \ub9e4\ub274\uc5bc \uc870\ud68c": {
"main": [
[
{
"node": "Gemini \uc785\ub825 \uc900\ube44",
"type": "main",
"index": 0
}
]
]
},
"Gemini \uc785\ub825 \uc900\ube44": {
"main": [
[
{
"node": "Gemini \ubcf4\uc644\uc548 \uc0dd\uc131",
"type": "main",
"index": 0
}
]
]
},
"Gemini \ubcf4\uc644\uc548 \uc0dd\uc131": {
"main": [
[
{
"node": "\ubcf4\uc644\uc548 \ud30c\uc2f1",
"type": "main",
"index": 0
}
]
]
},
"\ubcf4\uc644\uc548 \ud30c\uc2f1": {
"main": [
[
{
"node": "\uc794\ub514 \uac80\ud1a0 \uc694\uccad",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
About this workflow
파이프라인 ③ 매뉴얼 자동 업데이트. Uses scheduleTrigger, httpRequest, noOp. Scheduled trigger; 10 nodes.
Source: https://github.com/USEONGEE/n8n-example/blob/8d817d796e277931d4ecd47aaad930bf3afcca9c/.n8n/pipeline-3-manual-update.json — original creator credit. Request a take-down →