This workflow follows the Gmail → Google Calendar recipe pattern — see all workflows that pair these two integrations.
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": "\ud83d\udce7 \uc774\uba54\uc77c \uc790\ub3d9 \ubd84\ub958 \uc2dc\uc2a4\ud15c (Sender Filter)",
"nodes": [
{
"parameters": {},
"id": "bc805125-feeb-48db-97a5-f3d0a12d5565",
"name": "\ucc28\ub2e8\ub41c \ubc1c\uc2e0\uc790 - \ubb34\uc2dc2",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
-1264,
96
],
"notes": "\ube14\ub799\ub9ac\uc2a4\ud2b8 \ubc1c\uc2e0\uc790\ub294 \ucc98\ub9ac\ud558\uc9c0 \uc54a\uace0 \uc885\ub8cc"
},
{
"parameters": {
"operation": "getAll",
"limit": 20,
"simple": false,
"filters": {},
"options": {}
},
"id": "15070c55-a471-46ef-ae07-a1ee8661b776",
"name": "2. \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
-2640,
-64
],
"credentials": {}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// (1) Execute Once \ub044\uace0, \uc774 \ucf54\ub4dc\ub9cc \ub0a8\uaca8\ub450\uae30\nconst item = $input.item.json;\n\n// OpenAI \uc751\ub2f5 \ud14d\uc2a4\ud2b8 \ucd94\ucd9c\nconst response =\n item.message?.content ??\n (item.choices?.[0]?.message?.content ?? '');\n\nif (!response) {\n return {\n json: {\n ...item, // \ud639\uc2dc\ub77c\ub3c4 \ub4e4\uc5b4\uc628 \ud544\ub4dc\ub294 \uc720\uc9c0\n category: '\ubd84\ub958 \uc2e4\ud328',\n priority: '\ubcf4\ud1b5',\n summary: 'OpenAI \uc751\ub2f5\uc774 \ube44\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.',\n has_schedule: false,\n schedule_info: '',\n action_required: '',\n days_until: null,\n processed_at: new Date().toISOString(),\n error: 'No content from OpenAI',\n },\n };\n}\n\ntry {\n let jsonStr = response;\n if (jsonStr.includes('```json')) {\n jsonStr = jsonStr.split('```json')[1].split('```')[0];\n } else if (jsonStr.includes('```')) {\n jsonStr = jsonStr.split('```')[1].split('```')[0];\n }\n\n const analysis = JSON.parse(jsonStr.trim());\n\n return {\n json: {\n // \u2460 \uc6d0\ub798 \ub4e4\uc5b4\uc628 \ubaa8\ub4e0 \ud544\ub4dc \uba3c\uc800 \uc720\uc9c0\n ...item,\n\n // \u2461 \ud2b9\ud788 emailId\ub294 \ud639\uc2dc id\ub85c\ub9cc \uc640\ub3c4 \ucee4\ubc84\n emailId: item.emailId ?? item.id,\n\n // \u2462 \ubc1c\uc2e0\uc790 \uc815\ubcf4\n senderType: item.senderType,\n isVIP: item.senderType === 'VIP',\n\n // \u2463 AI \ubd84\uc11d \uacb0\uacfc\n category: analysis.category,\n priority: analysis.priority,\n summary: analysis.summary,\n has_schedule: analysis.has_schedule,\n schedule_info: analysis.schedule_info || '',\n action_required: analysis.action_required || '',\n days_until: analysis.days_until ?? null,\n\n processed_at: new Date().toISOString(),\n },\n };\n} catch (error) {\n return {\n json: {\n ...item,\n emailId: item.emailId ?? item.id ?? null,\n senderType: item.senderType,\n isVIP: false,\n category: '\ubd84\ub958 \uc2e4\ud328',\n priority: '\ubcf4\ud1b5',\n summary: '\ubd84\uc11d \uc911 \uc624\ub958 \ubc1c\uc0dd: ' + error.message,\n has_schedule: false,\n schedule_info: '',\n action_required: '',\n days_until: null,\n processed_at: new Date().toISOString(),\n error: error.message,\n },\n };\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2112,
-64
],
"id": "8d665ad8-8c2f-45d5-9f30-2ff458200180",
"name": "3. \uc774\uba54\uc77c \ub370\uc774\ud130 \ucd94\ucd9c"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// \ud83c\udd95 Sender Filter - \ubc1c\uc2e0\uc790 \uac80\uc99d \ub85c\uc9c1\n\nconst item = $input.item;\n\n// fromEmail \uc774 \uc5c6\uc744 \uc218\ub3c4 \uc788\uc73c\ub2c8 \ubc29\uc5b4 \ucf54\ub4dc\nconst rawFromEmail = item.json.fromEmail || item.json.from || '';\nconst fromEmail = rawFromEmail.toString().toLowerCase();\nconst from = item.json.from || '';\n\n\n// VIP \ub9ac\uc2a4\ud2b8 - \ucd5c\uc6b0\uc120 \ucc98\ub9ac\nconst vipList = [\n \"boss@company.com\",\n \"ceo@company.com\",\n \"important@client.com\"\n];\n\n// \ud654\uc774\ud2b8\ub9ac\uc2a4\ud2b8 - \uc2e0\ub8b0\ud560 \uc218 \uc788\ub294 \ub3c4\uba54\uc778/\uc774\uba54\uc77c\nconst whitelist = [\n \"@yourcompany.com\", // \ud68c\uc0ac \ub3c4\uba54\uc778\n \"@partner.com\", // \ud30c\ud2b8\ub108\uc0ac\n \"@client.com\" // \uace0\uac1d\uc0ac\n];\n\n// \ube14\ub799\ub9ac\uc2a4\ud2b8 - \ucc28\ub2e8\ud560 \ub3c4\uba54\uc778/\uc774\uba54\uc77c\nconst blacklist = [\n \"@spam.com\",\n \"noreply@marketing\",\n \"@promotional\",\n \"@ads.\",\n \"newsletter@\"\n];\n\n// \ub3c4\uba54\uc778 \u2192 \uce74\ud14c\uace0\ub9ac \ud78c\ud2b8 \ub9e4\ud551\nconst domainCategoryMap = {\n \"@yourcompany.com\": \"\uc5c5\ubb34\",\n \"@marketing.com\": \"\uad11\uace0\",\n \"calendar.google.com\": \"\ubbf8\ud305\",\n \"noreply@system\": \"\uacf5\uc9c0\"\n};\n\n// \ub9cc\uc57d fromEmail \uc790\uccb4\uac00 \ube44\uc5b4 \uc788\uc73c\uba74 \uadf8\ub0e5 \"\ubcf4\ud1b5 \uba54\uc77c\" \ucc98\ub9ac\nif (!fromEmail) {\n return {\n json: {\n ...item.json,\n isValid: true,\n senderType: \"\uc54c \uc218 \uc5c6\uc74c\",\n priorityBoost: false,\n categoryHint: null,\n note: \"fromEmail \uc774 \ube44\uc5b4 \uc788\uc74c\"\n }\n };\n}\n\n// ================== \uc544\ub798\ub294 \uae30\uc874 \ub85c\uc9c1 \uadf8\ub300\ub85c ==================\n\n// 1. VIP \uccb4\ud06c\nconst isVIP = vipList.some(vip => fromEmail.includes(vip.toLowerCase()));\n\nif (isVIP) {\n return {\n json: {\n ...item.json,\n isValid: true,\n senderType: \"VIP\",\n priorityBoost: true,\n categoryHint: \"\uc5c5\ubb34\"\n }\n };\n}\n\n// 2. \ube14\ub799\ub9ac\uc2a4\ud2b8 \uccb4\ud06c\nconst isBlocked = blacklist.some(blocked =>\n fromEmail.includes(blocked.toLowerCase())\n);\n\nif (isBlocked) {\n return {\n json: {\n ...item.json,\n isValid: false,\n senderType: \"\ucc28\ub2e8\ub428\",\n reason: \"\ube14\ub799\ub9ac\uc2a4\ud2b8\"\n }\n };\n}\n\n// 3. \ud654\uc774\ud2b8\ub9ac\uc2a4\ud2b8 \uccb4\ud06c\nconst isWhitelisted = whitelist.some(allowed =>\n fromEmail.includes(allowed.toLowerCase())\n);\n\n// 4. \uce74\ud14c\uace0\ub9ac \ud78c\ud2b8 \ucc3e\uae30\nlet categoryHint = null;\nfor (const [domain, category] of Object.entries(domainCategoryMap)) {\n if (fromEmail.includes(domain.toLowerCase())) {\n categoryHint = category;\n break;\n }\n}\n\n// \ucd5c\uc885 \uacb0\uacfc\nreturn {\n json: {\n ...item.json,\n isValid: true, // \uae30\ubcf8\uc801\uc73c\ub85c \ubaa8\ub4e0 \uc774\uba54\uc77c \ucc98\ub9ac (\ube14\ub799\ub9ac\uc2a4\ud2b8 \uc81c\uc678)\n senderType: isWhitelisted ? \"\ud654\uc774\ud2b8\ub9ac\uc2a4\ud2b8\" : \"\uc77c\ubc18\",\n priorityBoost: false,\n categoryHint: categoryHint\n }\n};\n"
},
"id": "c6f7c7ec-e0af-4a38-90f6-df51793ef7ad",
"name": "4. Sender Filter",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1744,
-64
],
"notes": "\ubc1c\uc2e0\uc790 \uac80\uc99d: VIP, \ud654\uc774\ud2b8\ub9ac\uc2a4\ud2b8, \ube14\ub799\ub9ac\uc2a4\ud2b8 \uccb4\ud06c"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.isValid }}",
"value2": true
}
]
},
"options": {}
},
"id": "892e3966-1610-48c7-96c1-8ff13c4a416b",
"name": "5. \ubc1c\uc2e0\uc790 \uc720\ud6a8\uc131 \ud655\uc778",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-1552,
-64
]
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"messages": {
"values": [
{
"content": "=\ub108\ub294 \uc774\uba54\uc77c \ube44\uc11c\uc57c. \uc544\ub798 \uc774\uba54\uc77c\uc744 \uc77d\uace0 JSON \ud558\ub098\ub97c \ub9cc\ub4e4\uc5b4\uc918.\n\n**\uc911\uc694: \ubc18\ub4dc\uc2dc \uc544\ub798 \ud615\uc2dd\uc758 \uc21c\uc218 JSON\ub9cc \ubc18\ud658\ud574.**\n**\uc808\ub300 ```json \uac19\uc740 \ucf54\ub4dc \ube14\ub85d\uc774\ub098 \uc124\uba85 \ubb38\uc7a5\uc744 \ubd99\uc774\uc9c0 \ub9c8.**\n\n\uc624\ub298 \ub0a0\uc9dc: {{ $json.today }}\n\n\ud544\ub4dc \uc815\uc758:\n\n- category: \"\uacf5\uc9c0\" | \"\uc5c5\ubb34\" | \"\uad11\uace0\" | \"\uc2a4\ud338\" | \"\ud30c\uc77c\uc804\uc1a1\" | \"\uc790\ub3d9\uc54c\ub9bc\" \uc911 \ud558\ub098\n- priority: \"\ub192\uc74c\" | \"\ubcf4\ud1b5\" | \"\ub0ae\uc74c\" \uc911 \ud558\ub098\n- summary: \ud55c\uad6d\uc5b4\ub85c 1~3\ubb38\uc7a5\uc73c\ub85c \ud575\uc2ec \ub0b4\uc6a9 \uc694\uc57d (\ubc18\ub4dc\uc2dc \uc2e4\uc81c \ub0b4\uc6a9 \uae30\ubc18)\n- has_schedule: \ub0a0\uc9dc\uc640 \uc2dc\uac04(\ub610\ub294 \ucd5c\uc18c\ud55c \ub0a0\uc9dc)\uc774 \uba85\ud655\ud788 \uc22b\uc790\ub85c \ud45c\ud604\ub41c \uacbd\uc6b0\uc5d0\ub9cc true\n- schedule_info: \uc0ac\ub78c\uc774 \uc77d\uae30 \uc88b\uc740 \ud55c\uad6d\uc5b4 \uc77c\uc815 \uc124\uba85 (\uc77c\uc815 \uc788\uc744 \ub54c\ub9cc)\n- schedule_start: ISO8601 \ud615\uc2dd \uc2dc\uc791 \uc2dc\uac01 (\ud55c\uad6d \uc2dc\uac04 KST, UTC+9)\n- schedule_end: ISO8601 \ud615\uc2dd \uc885\ub8cc \uc2dc\uac01 (\uc5c6\uc73c\uba74 null)\n- action_required: \uc218\uc2e0\uc790\uac00 \ud574\uc57c \ud560 \ud589\ub3d9 (\uc5c6\uc73c\uba74 \ube48 \ubb38\uc790\uc5f4)\n- days_until: \uc624\ub298 \uae30\uc900\uc73c\ub85c \uc77c\uc815\uae4c\uc9c0 \ub0a8\uc740 \ub0a0\uc9dc \uc218 (\uc77c\uc815 \uc5c6\uc73c\uba74 null)\n\n---\n\n**Category \ubd84\ub958 \uaddc\uce59**\n\n1. **\uc790\ub3d9\uc54c\ub9bc**:\n - \ubc1c\uc2e0\uc790: \"notify@\", \"noreply@\", \"no-reply@\", \"automation@\"\n - \ub0b4\uc6a9: \uc2dc\uc2a4\ud15c \uc790\ub3d9 \ubc1c\uc1a1\n\n2. **\uad11\uace0**:\n - \ubc1c\uc2e0\uc790: \"marketing@\", \"promo@\", \"newsletter@\", \"master@\"\n - \ub0b4\uc6a9: \"\ud560\uc778\", \"\ud504\ub85c\ubaa8\uc158\", \"\uc138\uc77c\", \"\ud2b9\uac00\", \"\uc774\ubca4\ud2b8\"\n\n3. **\uc2a4\ud338**:\n - \ub0b4\uc6a9\uc774 \uac70\uc758 \uc5c6\uac70\ub098 \uc758\ubbf8 \uc5c6\ub294 \ud14d\uc2a4\ud2b8\n\n4. **\ud30c\uc77c\uc804\uc1a1**:\n - \ucca8\ubd80\ud30c\uc77c\uc774 \uc788\uace0 \ubcf8\ubb38\uc774 \uac04\ub2e8\n\n5. **\uacf5\uc9c0**:\n - \ud68c\uc0ac/\ub2e8\uccb4\uc758 \uacf5\uc2dd \uacf5\uc9c0\uc0ac\ud56d\n - \"\uacf5\uc9c0\", \"\uc548\ub0b4\", \"\uc54c\ub9bc\"\n\n6. **\uc5c5\ubb34**:\n - \uc704 \uce74\ud14c\uace0\ub9ac\uc5d0 \ud574\ub2f9\ud558\uc9c0 \uc54a\ub294 \uc77c\ubc18 \uc5c5\ubb34 \uba54\uc77c\n\n**\ud310\ub2e8 \uc6b0\uc120\uc21c\uc704**: \uc790\ub3d9\uc54c\ub9bc > \uc2a4\ud338 > \uad11\uace0 > \ud30c\uc77c\uc804\uc1a1 > \uacf5\uc9c0 > \uc5c5\ubb34\n\n---\n\n**\ub0a0\uc9dc/\uc2dc\uac04 \uc778\uc2dd \uaddc\uce59 (\ub9e4\uc6b0 \uc911\uc694!)**\n\n### 1. \ub2e8\uc77c \ub0a0\uc9dc/\uc2dc\uac04\n```\n\uc608\uc2dc: \"12\uc6d4 8\uc77c \uc624\ud6c4 2\uc2dc\"\n\u2192 schedule_start: \"2025-12-08T14:00:00+09:00\"\n\u2192 schedule_end: null\n```\n\n### 2. \uae30\uac04 (\uc2dc\uc791~\uc885\ub8cc) \u2b50\u2b50\u2b50\n```\n\uc608\uc2dc 1: \"11\uc6d4 18\uc77c ~ 11\uc6d4 24\uc77c\"\n\u2192 schedule_start: \"2025-11-18T09:00:00+09:00\" (\uc2dc\uc791\uc77c 09:00)\n\u2192 schedule_end: \"2025-11-24T23:59:59+09:00\" (\uc885\ub8cc\uc77c 23:59)\n\n\uc608\uc2dc 2: \"11\uc6d4 18\uc77c\ubd80\ud130 24\uc77c\uae4c\uc9c0\"\n\u2192 schedule_start: \"2025-11-18T09:00:00+09:00\"\n\u2192 schedule_end: \"2025-11-24T23:59:59+09:00\"\n\n\uc608\uc2dc 3: \"9\uc6d4 21\uc77c 17:00\ubd80\ud130 \uba74\uc811 \uc885\uc2dc \uae30\uac04\"\n\u2192 schedule_start: \"2025-09-21T17:00:00+09:00\"\n\u2192 schedule_end: null (\uc885\ub8cc \uc2dc\uac01 \ubd88\uba85\ud655)\n```\n\n### 3. \ud2b9\uc815 \uc2dc\uac04\ub300\n```\n\uc608\uc2dc: \"11\uc6d4 23\uc77c 9:00-10:00\"\n\u2192 schedule_start: \"2025-11-23T09:00:00+09:00\"\n\u2192 schedule_end: \"2025-11-23T10:00:00+09:00\"\n```\n\n### 4. \ub9c8\uac10\uc77c\n```\n\uc608\uc2dc: \"11\uc6d4 30\uc77c\uae4c\uc9c0 \uc81c\ucd9c\"\n\u2192 schedule_start: \"2025-11-30T23:59:59+09:00\" (\ub9c8\uac10 \uc2dc\uac01)\n\u2192 schedule_end: null\n```\n\n### 5. \uc5f0\ub3c4 \ucd94\ub860 \uaddc\uce59\n- \uc624\ub298\uc774 2025\ub144 11\uc6d4 19\uc77c\uc778 \uacbd\uc6b0:\n * \"1\uc6d4 1\uc77c\" \u2192 2026-01-01 (\ubbf8\ub798 \ub0a0\uc9dc)\n * \"12\uc6d4 8\uc77c\" \u2192 2025-12-08 (\uc62c\ud574)\n * \"11\uc6d4 18\uc77c\" \u2192 2025-11-18 (\uc62c\ud574)\n\n### 6. \uc2dc\uac04 \uae30\ubcf8\uac12\n- \uc2dc\uac04 \uba85\uc2dc \uc5c6\uc73c\uba74: 09:00 (\uc624\uc804 9\uc2dc)\n- \ub9c8\uac10\uc77c/\uc885\ub8cc\uc77c: 23:59:59\n- \"\uc624\ud6c4 2\uc2dc\" = 14:00\n- \"\uc624\uc804 9\uc2dc\" = 09:00\n\n### 7. \uc778\uc2dd \ubd88\uac00\ub2a5\ud55c \uacbd\uc6b0\n```\n\u274c \"\ub2e4\uc74c \uc8fc\", \"\uc870\ub9cc\uac04\", \"\ucd94\ud6c4\"\n\u274c \"30\uc77c \uc774\ub0b4\", \"15\uc77c \uc774\ub0b4\"\n\u2192 has_schedule: false\n\u2192 schedule_start: null\n```\n\n---\n\n**\ud30c\uc77c \ucca8\ubd80 \uc774\uba54\uc77c \ucc98\ub9ac**\n\n\ucca8\ubd80\ud30c\uc77c \uc815\ubcf4: {{ $json.hasAttachments ? '\uc788\uc74c (' + $json.attachmentCount + '\uac1c)' : '\uc5c6\uc74c' }}\n\ucca8\ubd80\ud30c\uc77c \ubaa9\ub85d: {{ $json.attachmentNames }}\n\n**\uaddc\uce59**:\n- \ucca8\ubd80\ud30c\uc77c \uc788\uace0 \ubcf8\ubb38\uc774 \uac04\ub2e8\ud558\uba74 category = \"\ud30c\uc77c\uc804\uc1a1\"\n- \uc911\uc694 \ud30c\uc77c(.xlsx, .pdf, .doc)\uc774\uba74 priority = \"\ub192\uc74c\"\n- summary\uc5d0 \ud30c\uc77c \uc815\ubcf4 \ud3ec\ud568\n\n---\n\n**Priority \uaddc\uce59**\n\n1. **\ub192\uc74c**:\n - \"\uae34\uae09\", \"urgent\", \"ASAP\", \"\uae09\ud568\"\n - \uc77c\uc815\uc774 3\uc77c \uc774\ub0b4\n - \uc911\uc694 \ud30c\uc77c \ucca8\ubd80\n - \"\ud68c\uc2e0 \ubc14\ub78d\ub2c8\ub2e4\", \"\ud655\uc778 \ubd80\ud0c1\"\n\n2. **\ubcf4\ud1b5**:\n - \uc77c\ubc18 \uc5c5\ubb34 \uba54\uc77c\n - \uc77c\uc815\uc774 3~14\uc77c\n\n3. **\ub0ae\uc74c**:\n - \uc790\ub3d9\uc54c\ub9bc\n - \uad11\uace0\n - \uc77c\uc815 \uc5c6\uc74c\n\n---\n\n**JSON \uc608\uc2dc**\n\n**\uc608\uc2dc 1 - \ub2e8\uc77c \uc77c\uc815**:\n```json\n{\n \"category\": \"\uacf5\uc9c0\",\n \"priority\": \"\ub192\uc74c\",\n \"summary\": \"\uc0c8\ud574 \uccab \ub9cc\ub0a8 \ucd08\ub300, 1\uc6d4 1\uc77c \uc624\uc804 11\uc2dc\",\n \"has_schedule\": true,\n \"schedule_info\": \"2026\ub144 1\uc6d4 1\uc77c \uc624\uc804 11\uc2dc \uba85\uc131\uad50\ud68c\",\n \"schedule_start\": \"2026-01-01T11:00:00+09:00\",\n \"schedule_end\": null,\n \"action_required\": \"\ucc38\uc11d \uc5ec\ubd80 \ud68c\uc2e0\",\n \"days_until\": 43\n}\n```\n\n**\uc608\uc2dc 2 - \uae30\uac04 \uc77c\uc815** \u2b50:\n```json\n{\n \"category\": \"\uc5c5\ubb34\",\n \"priority\": \"\ubcf4\ud1b5\",\n \"summary\": \"11\uc6d4 18\uc77c\ubd80\ud130 24\uc77c\uae4c\uc9c0 \uc5c5\ub370\uc774\ud2b8 \uc77c\uc815\",\n \"has_schedule\": true,\n \"schedule_info\": \"11\uc6d4 18\uc77c ~ 11\uc6d4 24\uc77c\",\n \"schedule_start\": \"2025-11-18T09:00:00+09:00\",\n \"schedule_end\": \"2025-11-24T23:59:59+09:00\",\n \"action_required\": \"\uc77c\uc815 \ud655\uc778\",\n \"days_until\": -1\n}\n```\n\n**\uc608\uc2dc 3 - \ub9c8\uac10\uc77c**:\n```json\n{\n \"category\": \"\uc5c5\ubb34\",\n \"priority\": \"\ub192\uc74c\",\n \"summary\": \"\uc785\ucc30 \uc11c\ub958 \uc81c\ucd9c, 11\uc6d4 30\uc77c\uae4c\uc9c0\",\n \"has_schedule\": true,\n \"schedule_info\": \"11\uc6d4 30\uc77c\uae4c\uc9c0 \uc81c\ucd9c\",\n \"schedule_start\": \"2025-11-30T23:59:59+09:00\",\n \"schedule_end\": null,\n \"action_required\": \"\uae30\ud55c \ub0b4 \uc81c\ucd9c \uc644\ub8cc\",\n \"days_until\": 11\n}\n```\n\n**\uc608\uc2dc 4 - \uad11\uace0 (\uc77c\uc815 \uc5c6\uc74c)**:\n```json\n{\n \"category\": \"\uad11\uace0\",\n \"priority\": \"\ub0ae\uc74c\",\n \"summary\": \"\ub300\uc2e0\uc99d\uad8c \ud22c\uc790\uc815\ubcf4 \ubc0f \ucd94\ucc9c\uc885\ubaa9 \uc548\ub0b4\",\n \"has_schedule\": false,\n \"schedule_info\": \"\",\n \"schedule_start\": null,\n \"schedule_end\": null,\n \"action_required\": \"\",\n \"days_until\": null\n}\n```\n\n---\n\n**\uc911\uc694 \uc8fc\uc758\uc0ac\ud56d**\n\n1. **\uae30\uac04 \ud615\uc2dd \ubc18\ub4dc\uc2dc \uc778\uc2dd**:\n - \"A\ubd80\ud130 B\uae4c\uc9c0\", \"A ~ B\", \"A - B\"\n - schedule_start: A\uc758 \uc2dc\uc791 \uc2dc\uac01\n - schedule_end: B\uc758 \uc885\ub8cc \uc2dc\uac01\n\n2. **ISO8601 \ud615\uc2dd \uc5c4\uc218**:\n - \ud615\uc2dd: `YYYY-MM-DDTHH:mm:ss+09:00`\n - \uc608: `2025-12-08T14:00:00+09:00`\n - \ubc18\ub4dc\uc2dc `+09:00` \ud3ec\ud568 (\ud55c\uad6d \uc2dc\uac04\ub300)\n\n3. **null vs \ube48 \ubb38\uc790\uc5f4**:\n - schedule_start/end: null (ISO8601 \uc544\ub2c8\uba74 null)\n - schedule_info: \"\" (\uc77c\uc815 \uc5c6\uc73c\uba74 \ube48 \ubb38\uc790\uc5f4)\n - action_required: \"\" (\uc5c6\uc73c\uba74 \ube48 \ubb38\uc790\uc5f4)\n\n4. **has_schedule \ud310\ub2e8**:\n - schedule_start\uac00 null\uc774 \uc544\ub2c8\uba74 \u2192 true\n - schedule_start\uac00 null\uc774\uba74 \u2192 false\n\n5. **\ubc1c\uc2e0\uc790 \uc8fc\uc18c\ub85c category 1\ucc28 \ud310\ub2e8**:\n - master@, marketing@ \u2192 \uad11\uace0\n - notify@, noreply@ \u2192 \uc790\ub3d9\uc54c\ub9bc\n\n6. **summary\ub294 \ud56d\uc0c1 \uc2e4\uc81c \ub0b4\uc6a9 \uae30\ubc18**:\n - \u274c \"\uc774\uba54\uc77c \ubcf8\ubb38\uc5d0 \ub0b4\uc6a9\uc774 \uc5c6\uc5b4...\"\n - \u2705 \uc81c\ubaa9\uc774\ub098 \ubc1c\uc2e0\uc790 \uae30\ubc18 \uc694\uc57d",
"role": "system"
},
{
"content": "=\uc624\ub298 \ub0a0\uc9dc: {{ $json.today }}\n\n\uc81c\ubaa9: {{ $json.subject }}\n\ubc1c\uc2e0\uc790: {{ $json.from }}\n\ubc1c\uc2e0\uc790 \ud0c0\uc785: {{ $json.senderType }}\n\ub0a0\uc9dc: {{ $json.date }}\n\ucca8\ubd80\ud30c\uc77c: {{ $json.hasAttachments ? '\uc788\uc74c (' + $json.attachmentCount + '\uac1c)' : '\uc5c6\uc74c' }}\n\ucca8\ubd80\ud30c\uc77c \ubaa9\ub85d: {{ $json.attachmentNames }}\n\n\ub0b4\uc6a9:\n\uc544\ub798\ub294 \uc774\uba54\uc77c\uc758 \uc2e4\uc81c \ubcf8\ubb38\uc774\ub2e4:\n{{ $json.cleanBody }}"
}
]
},
"simplify": false,
"options": {
"temperature": 0.3
}
},
"id": "081f6436-420f-4b9d-a3d9-ab0f9c4766c8",
"name": "6. OpenAI\ub85c \uc774\uba54\uc77c \ubd84\uc11d",
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.5,
"position": [
-1024,
-64
],
"credentials": {}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// 7. JSON \ud30c\uc2f1 \ubc0f \ud1b5\ud569\n\n// \ud83d\udc49 \uc5ec\uae30 \uc774\ub984\uc744 \ub124 Sender Filter \ub178\ub4dc \uc774\ub984\uc774\ub791 \ub611\uac19\uc774 \ub9de\ucdb0\uc918\nconst email = $node[\"4. Sender Filter\"].json;\n\n// OpenAI \uc751\ub2f5\uc5d0\uc11c content \uac00\uc838\uc624\uae30\nconst response =\n $json.message?.content ??\n ($json.choices?.[0]?.message?.content ?? '');\n\nif (!response) {\n return {\n json: {\n ...email,\n category: '\ubd84\ub958 \uc2e4\ud328',\n priority: '\ubcf4\ud1b5',\n summary: 'OpenAI \uc751\ub2f5\uc774 \ube44\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.',\n has_schedule: false,\n schedule_info: '',\n schedule_start: null,\n schedule_end: null,\n action_required: '',\n days_until: null,\n processed_at: new Date().toISOString(),\n error: 'No content from OpenAI',\n },\n };\n}\n\nfunction formatSender(email) {\n const fe = email.fromEmail ?? email.from;\n if (!fe) return '';\n\n // \ubb38\uc790\uc5f4\uc774\uba74 \uadf8\ub300\ub85c \uc0ac\uc6a9\n if (typeof fe === 'string') return fe;\n\n // Gmail \ud615\uc2dd: { value: [{ address, name }], text, ... }\n if (Array.isArray(fe.value) && fe.value[0]) {\n const v = fe.value[0];\n if (v.name && v.address) return `${v.name} <${v.address}>`;\n return v.address || v.name || '';\n }\n\n if (fe.text) return fe.text;\n if (fe.address) return fe.address;\n\n return String(fe);\n}\n\nfunction formatDate(value) {\n const d = new Date(value);\n if (isNaN(d)) return value;\n const pad = (n) => String(n).padStart(2, '0');\n const yyyy = d.getFullYear();\n const mm = pad(d.getMonth() + 1);\n const dd = pad(d.getDate());\n const hh = pad(d.getHours());\n const mi = pad(d.getMinutes());\n return `${yyyy}-${mm}-${dd} ${hh}:${mi}`;\n}\n\ntry {\n const analysis = JSON.parse(response.trim());\n\n return {\n json: {\n // \uc6d0\ubcf8 \uc774\uba54\uc77c \uc815\ubcf4\n ...email,\n\n // \uc2dc\ud2b8\uc6a9 \uae30\ubcf8 \ud544\ub4dc\n emailId: email.emailId ?? email.id,\n date: formatDate(email.date),\n sender: formatSender(email),\n subject: email.subject,\n\n // \ubc1c\uc2e0\uc790 \uc815\ubcf4\n senderType: email.senderType,\n isVIP: email.senderType === 'VIP',\n\n // OpenAI \ubd84\uc11d \uacb0\uacfc\n category: analysis.category,\n priority: analysis.priority,\n summary: analysis.summary,\n has_schedule: analysis.has_schedule,\n schedule_info: analysis.schedule_info || '',\n schedule_start: analysis.schedule_start || null,\n schedule_end: analysis.schedule_end || null,\n action_required: analysis.action_required || '',\n days_until: analysis.days_until ?? null,\n\n processed_at: new Date().toISOString(),\n },\n };\n} catch (error) {\n return {\n json: {\n ...email,\n emailId: email.emailId ?? email.id ?? null,\n date: formatDate(email.date),\n sender: formatSender(email),\n subject: email.subject,\n senderType: email.senderType,\n isVIP: false,\n category: '\ubd84\ub958 \uc2e4\ud328',\n priority: '\ubcf4\ud1b5',\n summary: '\ubd84\uc11d \uc911 \uc624\ub958 \ubc1c\uc0dd: ' + error.message,\n has_schedule: false,\n schedule_info: '',\n schedule_start: null,\n schedule_end: null,\n action_required: '',\n days_until: null,\n processed_at: new Date().toISOString(),\n error: error.message,\n },\n };\n}\n"
},
"id": "f9e6aaa7-22df-4e26-9eff-e03c28b20f6a",
"name": "7. JSON \ud30c\uc2f1 \ubc0f \ud1b5\ud569",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-688,
-64
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_GOOGLE_SHEET_ID",
"mode": "list",
"cachedResultName": "\uc774\uba54\uc77c \uc790\ub3d9\ud654 (\uc0d8\ud50c)",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit"
},
"sheetName": {
"__rl": true,
"value": "email-log",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"date": "={{ $json.date }}",
"sender": "={{ $json.from }}",
"subject": "={{ $json.subject }}",
"category": "={{ $json.category }}",
"priority": "={{ $json.priority }}",
"summary": "={{ $json.summary }}",
"has_schedule": "={{ $json.has_schedule }}",
"schedule_info": "={{ $json.schedule_info }}",
"sender_type": "={{ $json.senderType }}",
"action_required": "={{ $json.action_required }}",
"days_until": "={{ $json.days_until }}",
"processed_at": "={{ $json.processed_at }}"
},
"matchingColumns": [],
"schema": [
{
"id": "date",
"displayName": "date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "sender",
"displayName": "sender",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "subject",
"displayName": "subject",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "category",
"displayName": "category",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "priority",
"displayName": "priority",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "summary",
"displayName": "summary",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "has_schedule",
"displayName": "has_schedule",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "schedule_info",
"displayName": "schedule_info",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "sender_type",
"displayName": "sender_type",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "action_required",
"displayName": "action_required",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "days_until",
"displayName": "days_until",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "processed_at",
"displayName": "processed_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"id": "d191e6cf-b962-40a0-8b85-176009a9355d",
"name": "8. Google Sheets\uc5d0 \uae30\ub85d",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
-240,
-80
],
"credentials": {}
},
{
"parameters": {
"calendar": {
"__rl": true,
"value": "your-email@example.com",
"mode": "list",
"cachedResultName": "your-email@example.com"
},
"start": "={{ $json.schedule_start }}",
"end": "={{ $json.schedule_end || $json.schedule_start }}",
"additionalFields": {
"description": "=\ubc1c\uc2e0\uc790: {{ $json.from }}\n\uce74\ud14c\uace0\ub9ac: {{ $json.category }}\n\uc6b0\uc120\uc21c\uc704: {{ $json.priority }}\n\n{{ $json.summary }}\n\n\ud544\uc694 \uc561\uc158: {{ $json.action_required }}",
"summary": "=[\uc774\uba54\uc77c] {{ $json.subject }}"
}
},
"type": "n8n-nodes-base.googleCalendar",
"typeVersion": 1.3,
"position": [
912,
-96
],
"id": "a7087fc9-eb27-46aa-a25e-0683f3ca2210",
"name": "8.6 Google Calendar \ub4f1\ub85d",
"credentials": {}
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"id": "9f4e649b-b47a-4842-a894-21978bfc3a67",
"name": "1. \ub9e4 1\uc2dc\uac04\ub9c8\ub2e4 \uc2e4\ud589",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-2944,
-64
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "978fd25f-de09-4101-b5bb-27ce8173cf5a",
"leftValue": "={{ $json.has_schedule }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
592,
-80
],
"id": "8f35f9cb-666b-4cff-af68-c08146f64a17",
"name": "8.56 \uce98\ub9b0\ub354 \ub4f1\ub85d \uc5ec\ubd80 \ud655\uc778"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// ===== schedule_start \uac15\uc81c \uc0dd\uc131 \ub178\ub4dc =====\n// schedule_info\uc5d0\uc11c \ub0a0\uc9dc\ub97c \ucd94\ucd9c\ud558\uc5ec schedule_start \uc0dd\uc131\n\nconst item = $input.item.json;\n\nconsole.log(\"===== schedule_start \uc0dd\uc131 \uc2dc\uc791 =====\");\nconsole.log(\"has_schedule:\", item.has_schedule);\nconsole.log(\"schedule_info:\", item.schedule_info);\nconsole.log(\"\uae30\uc874 schedule_start:\", item.schedule_start);\n\n// \uc774\ubbf8 schedule_start\uac00 \uc788\uc73c\uba74 \uadf8\ub300\ub85c \uc0ac\uc6a9\nif (item.schedule_start && item.schedule_start !== null && item.schedule_start !== \"null\") {\n console.log(\"\u2713 \uae30\uc874 schedule_start \uc0ac\uc6a9:\", item.schedule_start);\n return { json: item };\n}\n\n// schedule_info\uac00 \uc5c6\uc73c\uba74 \uc2a4\ud0b5\nif (!item.schedule_info || item.schedule_info === \"\") {\n console.log(\"\u2717 schedule_info \uc5c6\uc74c, \uc2a4\ud0b1\");\n return { json: item };\n}\n\nconst scheduleInfo = item.schedule_info;\nconst now = new Date();\nconst currentYear = now.getFullYear();\nconst currentMonth = now.getMonth() + 1;\n\nlet startDate = null;\nlet endDate = null;\n\n// ===== \ud328\ud134 1: \"2026\ub144 1\uc6d4 1\uc77c \uc624\uc804 11\uc2dc\" =====\nconst pattern1 = /(\\d{4})\ub144\\s*(\\d{1,2})\uc6d4\\s*(\\d{1,2})\uc77c(?:\\s*(\uc624\uc804|\uc624\ud6c4)\\s*(\\d{1,2})\uc2dc)?/;\nconst match1 = scheduleInfo.match(pattern1);\nif (match1) {\n const year = parseInt(match1[1]);\n const month = parseInt(match1[2]);\n const day = parseInt(match1[3]);\n const ampm = match1[4]; // \"\uc624\uc804\" \ub610\ub294 \"\uc624\ud6c4\"\n let hour = match1[5] ? parseInt(match1[5]) : 9; // \uae30\ubcf8 9\uc2dc\n \n // \uc624\ud6c4\uc774\uace0 12\uc2dc\uac00 \uc544\ub2c8\uba74 +12\n if (ampm === \"\uc624\ud6c4\" && hour !== 12) {\n hour += 12;\n } else if (ampm === \"\uc624\uc804\" && hour === 12) {\n hour = 0;\n }\n \n startDate = new Date(year, month - 1, day, hour, 0, 0);\n console.log(\"\u2713 \ud328\ud134 1 \ub9e4\uce6d:\", startDate);\n}\n\n// ===== \ud328\ud134 2: \"11\uc6d4 18\uc77c\" (\uc5f0\ub3c4 \uc5c6\uc74c) =====\nif (!startDate) {\n const pattern2 = /(\\d{1,2})\uc6d4\\s*(\\d{1,2})\uc77c(?:\\s*(\uc624\uc804|\uc624\ud6c4)\\s*(\\d{1,2})\uc2dc)?/;\n const match2 = scheduleInfo.match(pattern2);\n if (match2) {\n const month = parseInt(match2[1]);\n const day = parseInt(match2[2]);\n const ampm = match2[3];\n let hour = match2[4] ? parseInt(match2[4]) : 9;\n \n if (ampm === \"\uc624\ud6c4\" && hour !== 12) {\n hour += 12;\n } else if (ampm === \"\uc624\uc804\" && hour === 12) {\n hour = 0;\n }\n \n // \uc5f0\ub3c4 \ucd94\ub860: \uacfc\uac70 \ub0a0\uc9dc\uba74 \ub0b4\ub144\n let year = currentYear;\n const testDate = new Date(year, month - 1, day);\n if (testDate < now && currentMonth > month) {\n year = currentYear + 1;\n }\n \n startDate = new Date(year, month - 1, day, hour, 0, 0);\n console.log(\"\u2713 \ud328\ud134 2 \ub9e4\uce6d:\", startDate);\n }\n}\n\n// ===== \ud328\ud134 3: \"11\uc6d4 18\uc77c ~ 11\uc6d4 24\uc77c\" (\uae30\uac04) =====\nif (!startDate) {\n const pattern3 = /(\\d{1,2})\uc6d4\\s*(\\d{1,2})\uc77c\\s*[~\\-\ubd80\ud130]\\s*(\\d{1,2})\uc6d4?\\s*(\\d{1,2})\uc77c/;\n const match3 = scheduleInfo.match(pattern3);\n if (match3) {\n const startMonth = parseInt(match3[1]);\n const startDay = parseInt(match3[2]);\n const endMonth = match3[3] ? parseInt(match3[3]) : startMonth;\n const endDay = parseInt(match3[4]);\n \n let year = currentYear;\n const testDate = new Date(year, startMonth - 1, startDay);\n if (testDate < now && currentMonth > startMonth) {\n year = currentYear + 1;\n }\n \n startDate = new Date(year, startMonth - 1, startDay, 9, 0, 0);\n endDate = new Date(year, endMonth - 1, endDay, 23, 59, 59);\n console.log(\"\u2713 \ud328\ud134 3 \ub9e4\uce6d (\uae30\uac04):\", startDate, \"~\", endDate);\n }\n}\n\n// ===== \ud328\ud134 4: \"2025\ub144 11\uc6d4 18\uc77c\ubd80\ud130 24\uc77c\uae4c\uc9c0\" =====\nif (!startDate) {\n const pattern4 = /(\\d{4})\ub144\\s*(\\d{1,2})\uc6d4\\s*(\\d{1,2})\uc77c\ubd80\ud130\\s*(\\d{1,2})\uc77c\uae4c\uc9c0/;\n const match4 = scheduleInfo.match(pattern4);\n if (match4) {\n const year = parseInt(match4[1]);\n const month = parseInt(match4[2]);\n const startDay = parseInt(match4[3]);\n const endDay = parseInt(match4[4]);\n \n startDate = new Date(year, month - 1, startDay, 9, 0, 0);\n endDate = new Date(year, month - 1, endDay, 23, 59, 59);\n console.log(\"\u2713 \ud328\ud134 4 \ub9e4\uce6d (\uae30\uac04):\", startDate, \"~\", endDate);\n }\n}\n\n// ===== \ud328\ud134 5: \"12\uc6d4 8\uc77c \uc6d4\uc694\uc77c \uc624\ud6c4 2\uc2dc\" =====\nif (!startDate) {\n const pattern5 = /(\\d{1,2})\uc6d4\\s*(\\d{1,2})\uc77c\\s*\\w*\\s*(\uc624\uc804|\uc624\ud6c4)\\s*(\\d{1,2})\uc2dc/;\n const match5 = scheduleInfo.match(pattern5);\n if (match5) {\n const month = parseInt(match5[1]);\n const day = parseInt(match5[2]);\n const ampm = match5[3];\n let hour = parseInt(match5[4]);\n \n if (ampm === \"\uc624\ud6c4\" && hour !== 12) {\n hour += 12;\n } else if (ampm === \"\uc624\uc804\" && hour === 12) {\n hour = 0;\n }\n \n let year = currentYear;\n const testDate = new Date(year, month - 1, day);\n if (testDate < now && currentMonth > month) {\n year = currentYear + 1;\n }\n \n startDate = new Date(year, month - 1, day, hour, 0, 0);\n console.log(\"\u2713 \ud328\ud134 5 \ub9e4\uce6d:\", startDate);\n }\n}\n\n// ===== ISO8601 \ud615\uc2dd \ubcc0\ud658 (\ud55c\uad6d \uc2dc\uac04\ub300 KST) =====\nlet schedule_start_iso = null;\nlet schedule_end_iso = null;\n\nif (startDate && !isNaN(startDate.getTime())) {\n const year = startDate.getFullYear();\n const month = String(startDate.getMonth() + 1).padStart(2, '0');\n const day = String(startDate.getDate()).padStart(2, '0');\n const hour = String(startDate.getHours()).padStart(2, '0');\n const minute = String(startDate.getMinutes()).padStart(2, '0');\n const second = String(startDate.getSeconds()).padStart(2, '0');\n \n schedule_start_iso = `${year}-${month}-${day}T${hour}:${minute}:${second}+09:00`;\n \n console.log(\"\u2713 schedule_start \uc0dd\uc131 \uc644\ub8cc:\", schedule_start_iso);\n}\n\nif (endDate && !isNaN(endDate.getTime())) {\n const year = endDate.getFullYear();\n const month = String(endDate.getMonth() + 1).padStart(2, '0');\n const day = String(endDate.getDate()).padStart(2, '0');\n const hour = String(endDate.getHours()).padStart(2, '0');\n const minute = String(endDate.getMinutes()).padStart(2, '0');\n const second = String(endDate.getSeconds()).padStart(2, '0');\n \n schedule_end_iso = `${year}-${month}-${day}T${hour}:${minute}:${second}+09:00`;\n \n console.log(\"\u2713 schedule_end \uc0dd\uc131 \uc644\ub8cc:\", schedule_end_iso);\n}\n\nconsole.log(\"============================\");\n\n// ===== \ucd5c\uc885 \ubc18\ud658 =====\nreturn {\n json: {\n ...item,\n schedule_start: schedule_start_iso || item.schedule_start,\n schedule_end: schedule_end_iso || item.schedule_end\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
-80
],
"id": "ce895490-6891-461f-b9ff-837500a5880b",
"name": "8.55 \uc815\ud655\ud55c \ub0a0\uc9dc \ubc31\uc5c5"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// ===== \uc774\uba54\uc77c \uc804\ucc98\ub9ac \ub178\ub4dc (Gmail API \uc751\ub2f5 \uad6c\uc870 \ucd5c\uc801\ud654) =====\n\nconst item = $input.item.json;\n\nconsole.log(\"===== \uc218\uc815\uc911 \ub178\ub4dc \ub514\ubc84\uadf8 (v2) =====\");\nconsole.log(\"\uc804\uccb4 item keys:\", Object.keys(item));\nconsole.log(\"html \uc874\uc7ac:\", !!item.html);\nconsole.log(\"text \uc874\uc7ac:\", !!item.text);\nconsole.log(\"textPlain \uc874\uc7ac:\", !!item.textPlain);\nconsole.log(\"payload \uc874\uc7ac:\", !!item.payload);\n\n// ===== 1. \ubcf8\ubb38 \ucd94\ucd9c (6\uac00\uc9c0 \uc18c\uc2a4) =====\nlet bodyText = \"\";\n\n// \uc18c\uc2a4 1: Gmail API\uc758 text \ud544\ub4dc (\uac00\uc7a5 \uc6b0\uc120)\nif (item.text && item.text.trim().length > 0) {\n bodyText = item.text.trim();\n console.log(\"\u2713 Source 1: item.text (\" + bodyText.length + \" chars)\");\n}\n\n// \uc18c\uc2a4 2: textPlain \ud544\ub4dc\nif (!bodyText && item.textPlain && item.textPlain.trim().length > 0) {\n bodyText = item.textPlain.trim();\n console.log(\"\u2713 Source 2: item.textPlain (\" + bodyText.length + \" chars)\");\n}\n\n// \uc18c\uc2a4 3: html \ud544\ub4dc (HTML \ud0dc\uadf8 \uc81c\uac70)\nif (!bodyText && item.html && item.html.trim().length > 0) {\n // \uac04\ub2e8\ud55c HTML \ud0dc\uadf8 \uc81c\uac70\n bodyText = item.html\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim();\n console.log(\"\u2713 Source 3: item.html (HTML \ud0dc\uadf8 \uc81c\uac70, \" + bodyText.length + \" chars)\");\n}\n\n// \uc18c\uc2a4 4: payload.parts (Gmail API Full format)\nif (!bodyText && item.payload && item.payload.parts) {\n for (const part of item.payload.parts) {\n if (part.mimeType === \"text/plain\" && part.body && part.body.data) {\n try {\n bodyText = Buffer.from(part.body.data, 'base64').toString('utf-8').trim();\n console.log(\"\u2713 Source 4: payload.parts text/plain (\" + bodyText.length + \" chars)\");\n break;\n } catch (e) {\n console.log(\"\u2717 Source 4 decode error:\", e.message);\n }\n }\n }\n}\n\n// \uc18c\uc2a4 5: payload.parts (text/html)\nif (!bodyText && item.payload && item.payload.parts) {\n for (const part of item.payload.parts) {\n if (part.mimeType === \"text/html\" && part.body && part.body.data) {\n try {\n const htmlContent = Buffer.from(part.body.data, 'base64').toString('utf-8');\n bodyText = htmlContent\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/\\s+/g, ' ')\n .trim();\n console.log(\"\u2713 Source 5: payload.parts text/html (HTML \uc81c\uac70, \" + bodyText.length + \" chars)\");\n break;\n } catch (e) {\n console.log(\"\u2717 Source 5 decode error:\", e.message);\n }\n }\n }\n}\n\n// \uc18c\uc2a4 6: payload.body.data (\uc9c1\uc811 \ubcf8\ubb38)\nif (!bodyText && item.payload && item.payload.body && item.payload.body.data) {\n try {\n bodyText = Buffer.from(item.payload.body.data, 'base64').toString('utf-8').trim();\n console.log(\"\u2713 Source 6: payload.body.data (\" + bodyText.length + \" chars)\");\n } catch (e) {\n console.log(\"\u2717 Source 6 decode error:\", e.message);\n }\n}\n\n// \ucd5c\uc885 \uc815\ub9ac\nlet cleanText = bodyText.substring(0, 3000); // OpenAI \ud1a0\ud070 \uc808\uc57d\nconsole.log(\"\ucd5c\uc885 cleanBody length:\", cleanText.length);\nconsole.log(\"cleanText preview:\", cleanText.substring(0, 100));\n\n// ===== 2. \uc624\ub298 \ub0a0\uc9dc \uc0dd\uc131 =====\nconst now = new Date();\nconst days = ['\uc77c', '\uc6d4', '\ud654', '\uc218', '\ubaa9', '\uae08', '\ud1a0'];\nconst year = now.getFullYear();\nconst month = now.getMonth() + 1;\nconst date = now.getDate();\nconst day = days[now.getDay()];\nconst today = `${year}\ub144 ${month}\uc6d4 ${date}\uc77c (${day}\uc694\uc77c)`;\n\n// ===== 3. \ucca8\ubd80\ud30c\uc77c \uc815\ubcf4 =====\nlet hasAttachments = false;\nlet attachmentCount = 0;\nlet attachmentNames = [];\n\n// Gmail API payload.parts\uc5d0\uc11c \ucca8\ubd80\ud30c\uc77c \ucc3e\uae30\nif (item.payload && item.payload.parts) {\n for (const part of item.payload.parts) {\n if (part.filename && part.filename.length > 0) {\n hasAttachments = true;\n attachmentCount++;\n attachmentNames.push(part.filename);\n }\n }\n}\n\nconst attachmentNamesStr = attachmentNames.length > 0 \n ? attachmentNames.join(\", \") \n : \"\uc5c6\uc74c\";\n\nconsole.log(\"hasAttachments:\", hasAttachments);\nconsole.log(\"attachmentCount:\", attachmentCount);\nconsole.log(\"attachmentNames:\", attachmentNamesStr);\n\n// ===== 4. \ubc1c\uc2e0\uc790 \ud0c0\uc785 \ubd84\ub958 =====\nconst fromEmail = (item.from?.value?.[0]?.address || item.from || \"\").toLowerCase();\n\nlet senderType = \"\uc77c\ubc18\";\nif (fromEmail.includes(\"notify@\") || \n fromEmail.includes(\"noreply@\") || \n fromEmail.includes(\"no-reply@\") ||\n fromEmail.includes(\"automation@\")) {\n senderType = \"\uc790\ub3d9\uc54c\ub9bc\";\n} else if (fromEmail.includes(\"marketing@\") || \n fromEmail.includes(\"promo@\") ||\n fromEmail.includes(\"newsletter@\")) {\n senderType = \"\uad11\uace0\";\n}\n\nconsole.log(\"\ubc1c\uc2e0\uc790:\", fromEmail);\nconsole.log(\"\ubc1c\uc2e0\uc790 \ud0c0\uc785:\", senderType);\nconsole.log(\"============================\");\n\n// ===== 5. \ucd5c\uc885 \ubc18\ud658 =====\nreturn {\n json: {\n ...item,\n cleanBody: cleanText,\n today: today,\n hasAttachments: hasAttachments,\n attachmentCount: attachmentCount,\n attachmentNames: attachmentNamesStr,\n senderType: senderType\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2320,
-64
],
"id": "9922d548-375c-47a3-8cbe-a5932373a316",
"name": "2.5 \uc774\uba54\uc77c \uc804\ucc98\ub9ac \ub178\ub4dc"
},
{
"parameters": {
"content": "## Schedule Trigger\n\ub9e4\uc77c \uc624\uc804 8\uc2dc \uc790\ub3d9 \uc2e4\ud589",
"height": 304,
"width": 256
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-2992,
-208
],
"typeVersion": 1,
"id": "9d103c0c-b932-43fe-a95c-f74d20805934",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30\n\nGmail\uc5d0\uc11c \uc77d\uc9c0 \uc54a\uc740 \uc774\uba54\uc77c \uc218\uc9d1\n",
"height": 304,
"width": 288,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-2688,
-208
],
"typeVersion": 1,
"id": "1e473327-4e58-4dbd-ad75-58ff5e346e0d",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## \uc774\uba54\uc77c \uc804\ucc98\ub9ac (Code)\n\n\uc774\uba54\uc77c \ubcf8\ubb38 \ucd94\ucd9c \ubc0f \uc815\uc81c\nHTML \ud0dc\uadf8 \uc81c\uac70, 3000\uc790 \uc81c\ud55c\n\ucca8\ubd80\ud30c\uc77c \uc815\ubcf4 \ucd94\ucd9c",
"height": 304,
"width": 368,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-2336,
-208
],
"typeVersion": 1,
"id": "d686ba98-a100-4f5d-9fd5-a66759373eea",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## Sender Filter (\ubc1c\uc2e0\uc790 \ud544\ud130)\n\n\ubc1c\uc2e0\uc790 \uc8fc\uc18c\ub85c \ubd84\ub958:\n\nVIP: \uc911\uc694 \ubc1c\uc2e0\uc790 (\ud56d\uc0c1 \ucc98\ub9ac)\nWhitelist: \ud5c8\uc6a9 \ubaa9\ub85d\nBlacklist: \ucc28\ub2e8 \ubaa9\ub85d (\uc2a4\ud0b5)\n\n\u2192 API \ube44\uc6a9 30% \uc808\uac10",
"height": 304,
"width": 336,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-1936,
-208
],
"typeVersion": 1,
"id": "131fcd96-4409-4a2e-baba-e9ce78d6c44d",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## \ubd84\uc11d \uc81c\uc678 \ud544\ud130\n\n\ube14\ub799\ub9ac\uc2a4\ud2b8 \uc774\uba54\uc77c OpenAI \ubd84\uc11d \uc81c\uc678",
"height": 304,
"width": 272,
"color": 2
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-1584,
-208
],
"typeVersion": 1,
"id": "47a7244a-9de2-47b8-be3c-d37eefb82616",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "## Google Sheets \uae30\ub85d\n\n\ubaa8\ub4e0 \uc774\uba54\uc77c \uc815\ubcf4\ub97c \uc2a4\ud504\ub808\ub4dc\uc2dc\ud2b8\uc5d0 \uc800\uc7a5\n(\ub0a0\uc9dc, \ubc1c\uc2e0\uc790, \uc81c\ubaa9, \uce74\ud14c\uace0\ub9ac, \uc6b0\uc120\uc21c\uc704, \uc694\uc57d \ub4f1)",
"height": 304,
"width": 336,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
-240
],
"typeVersion": 1,
"id": "c1791d3c-1fe1-4795-beec-f7e8704be391",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "## JSON \ud30c\uc2f1\n\nOpenAI \uc751\ub2f5\uc744 \uad6c\uc870\ud654\ub41c \ub370\uc774\ud130\ub85c \ubcc0\ud658",
"height": 272,
"width": 272,
"color": 3
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-736,
-208
],
"typeVersion": 1,
"id": "f296215c-2f62-4eb8-a2eb-a1791b0e4b5b",
"name": "Sticky Note6"
},
{
"parameters": {
"content": "## OpenAI \ubd84\uc11d\n\nGPT-4o-mini\ub85c \uc774\uba54\uc77c \uc790\ub3d9 \ubd84\uc11d:\n\n\uce74\ud14c\uace0\ub9ac (\uacf5\uc9c0/\uc5c5\ubb34/\uad11\uace0/\uc790\ub3d9\uc54c\ub9bc)\n\uc6b0\uc120\uc21c\uc704 (\ub192\uc74c/\ubcf4\ud1b5/\ub0ae\uc74c)\n\ud575\uc2ec \uc694\uc57d\n\uc77c\uc815 \uc815\ubcf4 \ucd94\ucd9c\n\ud544\uc694\ud55c \ud589\ub3d9",
"height": 288,
"width": 448,
"color": 3
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-1232,
-240
],
"typeVersion": 1,
"id": "50172511-238c-4bac-8fad-a378b4329f81",
"name": "Sticky Note7"
},
{
"parameters": {
"content": "## schedule_start \uc0dd\uc131 (Code)\n\n\uc77c\uc815 \ud14d\uc2a4\ud2b8\ub97c \uc815\ud655\ud55c \ub0a0\uc9dc/\uc2dc\uac04\uc73c\ub85c \ubcc0\ud658:\n\n\"11\uc6d4 18\uc77c\" \u2192 2025-11-18T09:00:00+09:00\n\"12\uc6d4 8\uc77c \uc624\ud6c4 2\uc2dc\" \u2192 2025-12-08T14:00:00+09:00\n\n\u2192 \uc815\ud655\ud55c \uce98\ub9b0\ub354 \ub4f1\ub85d",
"height": 320,
"width": 416,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-432,
-256
],
"typeVersion": 1,
"id": "ee47e87f-d6c9-47f2-95e2-ada40e17178e",
"name": "Sticky Note8"
},
{
"parameters": {
"content": "## \uce98\ub9b0\ub354 \ub4f1\ub85d \uc5ec\ubd80 \ud655\uc778 (IF)\n\n\uae30\ub2a5: \uc77c\uc815\uc774 \uc788\ub294 \uc774\uba54\uc77c\ub9cc Calendar\ub85c \ubd84\uae30\n\nTRUE \u2192 Google Calendar \ub4f1\ub85d\nFALSE \u2192 \uc77d\uc74c \ucc98\ub9ac\ub85c \uc774\ub3d9",
"height": 288,
"width": 352,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
400,
-224
],
"typeVersion": 1,
"id": "7a2429cc-55c7-42c5-b39d-04d6a9353055",
"name": "Sticky Note9"
},
{
"parameters": {
"content": "## Google Calendar \ub4f1\ub85d\n\n\uae30\ub2a5: \uc77c\uc815\uc774 \uc788\ub294 \uc774\uba54\uc77c\uc744 \uce98\ub9b0\ub354 \uc774\ubca4\ud2b8\ub85c \uc0dd\uc131\nCalendar \ub4f1\ub85d \uc131\uacf5 \uc5ec\ubd80\ub97c Sheets\uc5d0 \uc5c5\ub370\uc774\ud2b8",
"height": 272,
"width": 352,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [
784,
-224
],
"typeVersion": 1,
"id": "315b5c49-3f0b-46af-b736-98658761fd47",
"name": "Sticky Note10"
},
{
"parameters": {
"operation": "markAsRead",
"messageId": "={{ $node[\"2. \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30\"].json[\"id\"] }}"
},
"id": "f3f613d8-30e6-49d9-b6aa-27e0224bdeec",
"name": "9 \uc774\uba54\uc77c \uc77d\uc74c \ud45c\uc2dc",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1440,
224
],
"credentials": {}
},
{
"parameters": {
"operation": "markAsRead",
"messageId": "={{ $node[\"2. \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30\"].json[\"id\"] }}"
},
"id": "13f04623-16d2-40de-be10-e520af763a20",
"name": "9. \uc774\uba54\uc77c \uc77d\uc74c",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1408,
-96
],
"credentials": {}
},
{
"parameters": {
"content": "## \uc774\uba54\uc77c \uc77d\uc74c \ud45c\uc2dc\n\n\uae30\ub2a5: \ucc98\ub9ac \uc644\ub8cc\ub41c \uc774\uba54\uc77c\uc744 \uc77d\uc74c\uc73c\ub85c \ucc98\ub9ac\n\uc124\uc815: Add Label: UNREAD \uc81c\uac70\n\uc6a9\ub3c4: \uc911\ubcf5 \ucc98\ub9ac \ubc29\uc9c0\n\n",
"height": 272,
"width": 400,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"position": [
1168,
-224
],
"typeVersion": 1,
"id": "f09150e5-64be-4e77-88f3-d4a90a22e635",
"name": "Sticky Note12"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// 8.55 \ub0a0\uc9dc\ucc3e\uae30 (v2 - \uac1c\uc120 \ubc84\uc804)\n\nconst item = $input.item.json;\n\n// ISO \ubb38\uc790\uc5f4\uc778\uc9c0 \uac80\uc0ac\ud558\ub294 \ud568\uc218\nfunction isValidISO(str) {\n if (!str || typeof str !== 'string') return false;\n const d = new Date(str);\n return !isNaN(d.getTime());\n}\n\n// KST(+09:00) \ud615\uc2dd\uc73c\ub85c \ubcc0\ud658\nfunction toKSTString(d) {\n const pad = (n) => String(n).padStart(2, '0');\n const yyyy = d.getFullYear();\n const mm = pad(d.getMonth() + 1);\n const dd = pad(d.getDate());\n const hh = pad(d.getHours());\n const mi = pad(d.getMinutes());\n const ss = pad(d.getSeconds());\n return `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}+09:00`;\n}\n\nconsole.log('===== \ub0a0\uc9dc\ucc3e\uae30 \ub514\ubc84\uadf8 =====');\nconsole.log('schedule_start:', item.schedule_start);\nconsole.log('schedule_end:', item.schedule_end);\nconsole.log('has_schedule:', item.has_schedule);\n\n// 1\ufe0f\u20e3 schedule_start\uac00 \uc788\uc73c\uba74 \ucc98\ub9ac (end\ub294 \uc120\ud0dd\uc0ac\ud56d)\nif (isValidISO(item.schedule_start)) {\n const startDate = new Date(item.schedule_start);\n \n // \uc885\ub8cc \uc2dc\uac04\uc774 \uc788\uace0 \uc720\ud6a8\ud558\uba74 \uc0ac\uc6a9, \uc5c6\uc73c\uba74 +1\uc2dc\uac04\n let endDate;\n if (item.schedule_end && isValidISO(item.schedule_end)) {\n endDate = new Date(item.schedule_end);\n // \uc2dc\uc791\ubcf4\ub2e4 \ube60\ub974\uba74 \uc548 \ub418\ubbc0\ub85c \uccb4\ud06c\n if (endDate <= startDate) {\n endDate = new Date(startDate.getTime() + 60 * 60 * 1000);\n console.log('\u26a0\ufe0f \uc885\ub8cc \uc2dc\uac04\uc774 \uc2dc\uc791\ubcf4\ub2e4 \ube60\ub984, +1\uc2dc\uac04\uc73c\ub85c \ubcf4\uc815');\n }\n } else {\n endDate = new Date(startDate.getTime() + 60 * 60 * 1000);\n console.log('\u2705 \uc885\ub8cc \uc2dc\uac04 \uc5c6\uc74c, +1\uc2dc\uac04\uc73c\ub85c \uc790\ub3d9 \uc124\uc815');\n }\n \n console.log('\u2705 \uce98\ub9b0\ub354 \ub4f1\ub85d:', toKSTString(startDate), '~', toKSTString(endDate));\n \n return {\n json: {\n ...item,\n calendarStart: toKSTString(startDate),\n calendarEnd: toKSTString(endDate),\n dateSource: 'OpenAI_parsed'\n },\n };\n}\n\n// 2\ufe0f\u20e3 schedule_start\uac00 \uc5c6\uc73c\uba74 \uce98\ub9b0\ub354 \ub4f1\ub85d \uc548 \ud568\nconsole.log('\u26a0\ufe0f schedule_start \uc5c6\uc74c - \uce98\ub9b0\ub354 \ub4f1\ub85d \uac74\ub108\ub700');\n\nreturn {\n json: {\n ...item,\n calendarStart: null,\n calendarEnd: null,\n dateSource: 'no_schedule_skip'\n },\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
48,
-80
],
"id": "babcb755-ed30-4a26-8a1f-d1062f59e313",
"name": "8.5 \ub0a0\uc9dc\ucc3e\uae30"
},
{
"parameters": {
"content": "## \uc774\uba54\uc77c \uc77d\uc74c \ud45c\uc2dc\n\n\uae30\ub2a5: \ucc98\ub9ac \uc644\ub8cc\ub41c \uc774\uba54\uc77c\uc744 \uc77d\uc74c\uc73c\ub85c \ucc98\ub9ac\n\uc124\uc815: Add Label: UNREAD \uc81c\uac70\n\uc6a9\ub3c4: \uc911\ubcf5 \ucc98\ub9ac \ubc29\uc9c0\n\n",
"height": 272,
"width": 400,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"position": [
1168,
80
],
"typeVersion": 1,
"id": "0b8bd81d-e00b-4a73-bbbe-6b90aa0ba99d",
"name": "Sticky Note15"
},
{
"parameters": {
"content": "## \uc804\uccb4 \uc6cc\ud06c\ud50c\ub85c\uc6b0\n\nGmail \uc548\uc77d\uc740 \uc774\uba54\uc77c \uc218\uc9d1 \u2192 \ubc1c\uc2e0\uc790 \ud544\ud130\ub9c1 \u2192 AI \ubd84\uc11d \u2192 \uc77c\uc815 \ucd94\ucd9c \u2192 Calendar/Sheets \uc800\uc7a5 \u2192 \uc77d\uc74c \ucc98\ub9ac\n\n## \ud83d\udcca \uc8fc\uc694 \ud6a8\uacfc\n\n\u23f1\ufe0f \uc2dc\uac04 \uc808\uc57d: \uc774\uba54\uc77c \uc218\ub3d9 \ubd84\ub958 \ubd88\ud544\uc694\n\ud83d\udcc5 \uc77c\uc815 \uad00\ub9ac: \uc790\ub3d9 \uce98\ub9b0\ub354 \ub4f1\ub85d\n\ud83d\udcb0 \ube44\uc6a9 \uc808\uac10: \ubc1c\uc2e0\uc790 \ud544\ud130\ub9c1\uc73c\ub85c API \ube44\uc6a9 30% \uac10\uc18c\n\ud83d\udcc8 \uc815\ud655\ub3c4: AI + \ucf54\ub4dc \uc870\ud569\uc73c\ub85c \uc77c\uc815 \uc778\uc2dd \uc815\ud655\ub3c4 \uadf9\ub300\ud654",
"height": 320,
"width": 688,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-1152,
-640
],
"typeVersion": 1,
"id": "df1d8cb8-8b58-4c0b-85c2-8022aea8f2f7",
"name": "Sticky Note11"
}
],
"connections": {
"2. \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30": {
"main": [
[
{
"node": "2.5 \uc774\uba54\uc77c \uc804\ucc98\ub9ac \ub178\ub4dc",
"type": "main",
"index": 0
}
]
]
},
"3. \uc774\uba54\uc77c \ub370\uc774\ud130 \ucd94\ucd9c": {
"main": [
[
{
"node": "4. Sender Filter",
"type": "main",
"index": 0
}
]
]
},
"4. Sender Filter": {
"main": [
[
{
"node": "5. \ubc1c\uc2e0\uc790 \uc720\ud6a8\uc131 \ud655\uc778",
"type": "main",
"index": 0
}
]
]
},
"5. \ubc1c\uc2e0\uc790 \uc720\ud6a8\uc131 \ud655\uc778": {
"main": [
[
{
"node": "6. OpenAI\ub85c \uc774\uba54\uc77c \ubd84\uc11d",
"type": "main",
"index": 0
}
],
[
{
"node": "\ucc28\ub2e8\ub41c \ubc1c\uc2e0\uc790 - \ubb34\uc2dc2",
"type": "main",
"index": 0
}
]
]
},
"6. OpenAI\ub85c \uc774\uba54\uc77c \ubd84\uc11d": {
"main": [
[
{
"node": "7. JSON \ud30c\uc2f1 \ubc0f \ud1b5\ud569",
"type": "main",
"index": 0
}
]
]
},
"7. JSON \ud30c\uc2f1 \ubc0f \ud1b5\ud569": {
"main": [
[
{
"node": "8. Google Sheets\uc5d0 \uae30\ub85d",
"type": "main",
"index": 0
}
]
]
},
"8. Google Sheets\uc5d0 \uae30\ub85d": {
"main": [
[
{
"node": "8.5 \ub0a0\uc9dc\ucc3e\uae30",
"type": "main",
"index": 0
}
]
]
},
"8.6 Google Calendar \ub4f1\ub85d": {
"main": [
[
{
"node": "9. \uc774\uba54\uc77c \uc77d\uc74c",
"type": "main",
"index": 0
}
]
]
},
"1. \ub9e4 1\uc2dc\uac04\ub9c8\ub2e4 \uc2e4\ud589": {
"main": [
[
{
"node": "2. \uc548\uc77d\uc740 \uc774\uba54\uc77c \uac00\uc838\uc624\uae30",
"type": "main",
"index": 0
}
]
]
},
"8.56 \uce98\ub9b0\ub354 \ub4f1\ub85d \uc5ec\ubd80 \ud655\uc778": {
"main": [
[
{
"node": "8.6 Google Calendar \ub4f1\ub85d",
"type": "main",
"index": 0
}
],
[
{
"node": "9 \uc774\uba54\uc77c \uc77d\uc74c \ud45c\uc2dc",
"type": "main",
"index": 0
}
]
]
},
"8.55 \uc815\ud655\ud55c \ub0a0\uc9dc \ubc31\uc5c5": {
"main": [
[
{
"node": "8.56 \uce98\ub9b0\ub354 \ub4f1\ub85d \uc5ec\ubd80 \ud655\uc778",
"type": "main",
"index": 0
}
]
]
},
"2.5 \uc774\uba54\uc77c \uc804\ucc98\ub9ac \ub178\ub4dc": {
"main": [
[
{
"node": "3. \uc774\uba54\uc77c \ub370\uc774\ud130 \ucd94\ucd9c",
"type": "main",
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
📧 이메일 자동 분류 시스템 (Sender Filter). Uses gmail, openAi, googleSheets, googleCalendar. Scheduled trigger; 30 nodes.
Source: https://github.com/ggplab/n8n-playbook/blob/c92116ebe998c248a50bd6e397ee4e8b7d212bac/02-usecases/05-workflow-email-data/email_automation_workflow_cleaned.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Stop wasting billable hours on manual time-tracking. AutoTimesheet Pro uses AI to collect emails, meetings, and GitHub work, then writes a clean timesheet straight into Google Sheets. Perfect for deve
Business owners and service providers who want to reduce no-show rates for appointments booked via Google Calendar.
Women and healthcare providers who want to automate menstrual cycle tracking with personalized AI-powered health insights delivered through multiple channels.
Personalized Outreach & Follow-Up - Phase 2. Uses googleSheets, openAi, gmail, gmailTrigger. Scheduled trigger; 59 nodes.
This advanced workflow automates brand monitoring and media coverage tracking for musicians, bands, and music labels. The system uses multiple search queries (dorky) to discover mentions across the we