AutomationFlowsAI & RAG › 이메일 자동 분류 시스템 (sender Filter)

이메일 자동 분류 시스템 (sender Filter)

Original n8n title: 📧 이메일 자동 분류 시스템 (sender Filter)

📧 이메일 자동 분류 시스템 (Sender Filter). Uses gmail, openAi, googleSheets, googleCalendar. Scheduled trigger; 30 nodes.

Cron / scheduled trigger★★★★★ complexityAI-powered30 nodesGmailOpenAIGoogle SheetsGoogle Calendar
AI & RAG Trigger: Cron / scheduled Nodes: 30 Complexity: ★★★★★ AI nodes: yes Added:

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 →

Download .json
{
  "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(/&nbsp;/g, ' ')\n    .replace(/&lt;/g, '<')\n    .replace(/&gt;/g, '>')\n    .replace(/&amp;/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(/&nbsp;/g, ' ')\n          .replace(/&lt;/g, '<')\n          .replace(/&gt;/g, '>')\n          .replace(/&amp;/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",
     
Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

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

Google Calendar, Gmail, GitHub +3
AI & RAG

Business owners and service providers who want to reduce no-show rates for appointments booked via Google Calendar.

Google Calendar, Google Sheets, OpenAI +2
AI & RAG

Women and healthcare providers who want to automate menstrual cycle tracking with personalized AI-powered health insights delivered through multiple channels.

Google Sheets, OpenAI, Telegram +2
AI & RAG

Personalized Outreach & Follow-Up - Phase 2. Uses googleSheets, openAi, gmail, gmailTrigger. Scheduled trigger; 59 nodes.

Google Sheets, OpenAI, Gmail +2
AI & RAG

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

Google Sheets, Gmail, @Brave/N8N Nodes Brave Search +1