8
n8n 한국어amn8n.com

집중 시간

중급

이것은Personal Productivity분야의자동화 워크플로우로, 11개의 노드를 포함합니다.주로 If, Code, ItemLists, GoogleCalendar, ScheduleTrigger 등의 노드를 사용하며. 바쁜 일정 중 Google 캘린더에 집중 시간 자동 차단

사전 요구사항
  • 특별한 사전 요구사항 없이 가져와 바로 사용 가능합니다

카테고리

워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "H1q8R1MfhEk8F0ni",
  "meta": {
    "instanceId": "bdc54da2c96840612a04bf3fd3a4cd97a7a7bd7c1152bbe41d5615f09311c097"
  },
  "name": "FocusTime",
  "tags": [
    {
      "id": "SauVYJKjA9yiw2uI",
      "name": "calendar-automation",
      "createdAt": "2025-07-20T22:06:21.623Z",
      "updatedAt": "2025-07-20T22:06:21.623Z"
    }
  ],
  "nodes": [
    {
      "id": "965166ee-bf7e-48e6-b559-624c4a5bb72f",
      "name": "스케줄 트리거",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -880,
        96
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * *"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "af8970e0-6d2c-48d4-907b-5e022ab8d5ac",
      "name": "주간 전체 일정 가져오기",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        -656,
        96
      ],
      "parameters": {
        "options": {},
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "primary",
          "cachedResultName": "Primary"
        },
        "operation": "getAll"
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "Isqbn5j7Czj35HLS",
          "name": "Google Calendar account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "76c503e8-926f-4254-be1b-f2c5b55938e8",
      "name": "주간 전체 집중 시간 슬롯 계산",
      "type": "n8n-nodes-base.code",
      "position": [
        -448,
        96
      ],
      "parameters": {
        "jsCode": "const events = $input.all();\nconst today = new Date();\n\n// Get Sunday of current week (start of week)\nconst sunday = new Date(today);\nsunday.setDate(today.getDate() - today.getDay());\nsunday.setHours(0, 0, 0, 0);\n\n// Create array of all 7 days (Sunday-Saturday)\nconst allDays = [];\nfor (let i = 0; i < 7; i++) {\n  const day = new Date(sunday);\n  day.setDate(sunday.getDate() + i);\n  allDays.push(day);\n}\n\nconst dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\nconsole.log('Analyzing full week:', allDays.map((d, i) => `${dayNames[i]} ${d.toDateString()}`));\n\nconst allFocusSlots = [];\n\n// Process each day of the week\nfor (let dayIndex = 0; dayIndex < allDays.length; dayIndex++) {\n  const currentDay = allDays[dayIndex];\n  const dayName = dayNames[dayIndex];\n  \n  const workStart = new Date(currentDay);\n  workStart.setHours(9, 0, 0, 0);\n  const workEnd = new Date(currentDay);\n  workEnd.setHours(17, 0, 0, 0);\n  \n  // Filter events for this specific day\n  const dayEvents = events\n    .filter(event => {\n      const eventStart = new Date(event.json.start.dateTime || event.json.start.date);\n      return eventStart.toDateString() === currentDay.toDateString();\n    })\n    .sort((a, b) => {\n      const aStart = new Date(a.json.start.dateTime || a.json.start.date);\n      const bStart = new Date(b.json.start.dateTime || b.json.start.date);\n      return aStart - bStart;\n    });\n\n  // Calculate total booked time for this day\n  let totalBookedMinutes = 0;\n  for (const event of dayEvents) {\n    const startTime = new Date(event.json.start.dateTime || event.json.start.date);\n    const endTime = new Date(event.json.end.dateTime || event.json.end.date);\n    \n    const effectiveStart = new Date(Math.max(startTime.getTime(), workStart.getTime()));\n    const effectiveEnd = new Date(Math.min(endTime.getTime(), workEnd.getTime()));\n    \n    if (effectiveStart < effectiveEnd) {\n      totalBookedMinutes += (effectiveEnd - effectiveStart) / (1000 * 60);\n    }\n  }\n\n  const totalBookedHours = totalBookedMinutes / 60;\n  console.log(`${dayName} ${currentDay.toDateString()}: ${totalBookedHours.toFixed(1)} hours booked`);\n\n  // Only process days with 6+ hours booked\n  if (totalBookedHours >= 6) {\n    console.log(`Creating focus time for ${dayName} (${totalBookedHours.toFixed(1)} hours booked)`);\n    \n    const freeSlots = [];\n    let currentTime = new Date(workStart);\n\n    // Check time before first event\n    if (dayEvents.length > 0) {\n      const firstEventStart = new Date(dayEvents[0].json.start.dateTime || dayEvents[0].json.start.date);\n      if (currentTime < firstEventStart) {\n        const slotEnd = new Date(Math.min(firstEventStart.getTime(), workEnd.getTime()));\n        if (currentTime < slotEnd) {\n          freeSlots.push({\n            start: new Date(currentTime),\n            end: slotEnd\n          });\n        }\n      }\n    }\n\n    // Check time between events\n    for (let i = 0; i < dayEvents.length - 1; i++) {\n      const currentEventEnd = new Date(dayEvents[i].json.end.dateTime || dayEvents[i].json.end.date);\n      const nextEventStart = new Date(dayEvents[i + 1].json.start.dateTime || dayEvents[i + 1].json.start.date);\n      \n      const slotStart = new Date(Math.max(currentEventEnd.getTime(), workStart.getTime()));\n      const slotEnd = new Date(Math.min(nextEventStart.getTime(), workEnd.getTime()));\n      \n      if (slotStart < slotEnd) {\n        freeSlots.push({\n          start: slotStart,\n          end: slotEnd\n        });\n      }\n    }\n\n    // Check time after last event\n    if (dayEvents.length > 0) {\n      const lastEventEnd = new Date(dayEvents[dayEvents.length - 1].json.end.dateTime || dayEvents[dayEvents.length - 1].json.end.date);\n      const slotStart = new Date(Math.max(lastEventEnd.getTime(), workStart.getTime()));\n      \n      if (slotStart < workEnd) {\n        freeSlots.push({\n          start: slotStart,\n          end: new Date(workEnd)\n        });\n      }\n    } else {\n      // No events this day, but 6+ hours requirement met somehow (shouldn't happen, but handle it)\n      console.log(`${dayName}: No events found but met 6+ hour criteria`);\n    }\n\n    // Filter slots that are at least 15 minutes long and add to all slots\n    const validSlots = freeSlots.filter(slot => \n      (slot.end - slot.start) >= 15 * 60 * 1000\n    );\n\n    validSlots.forEach(slot => {\n      allFocusSlots.push({\n        start: slot.start.toISOString(),\n        end: slot.end.toISOString(),\n        duration: Math.round((slot.end - slot.start) / (1000 * 60)),\n        summary: \"Focus Time\",\n        day: currentDay.toDateString(),\n        dayName: dayName,\n        bookedHours: totalBookedHours.toFixed(1)\n      });\n    });\n  }\n}\n\nconsole.log(`Found ${allFocusSlots.length} focus time slots across the full week`);\n\nif (allFocusSlots.length === 0) {\n  return [{\n    json: {\n      shouldCreateFocusTime: false,\n      message: 'No days this week have 6+ hours booked. Focus time not needed.',\n      weekAnalyzed: allDays.map((d, i) => `${dayNames[i]} ${d.toDateString()}`)\n    }\n  }];\n}\n\nreturn [{\n  json: {\n    shouldCreateFocusTime: true,\n    totalSlotsFound: allFocusSlots.length,\n    freeSlots: allFocusSlots,\n    weekAnalyzed: allDays.map((d, i) => `${dayNames[i]} ${d.toDateString()}`)\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a6d17dd6-3aea-4860-b3c1-cd95d8e6a78c",
      "name": "집중 시간 생성 필요 여부 확인",
      "type": "n8n-nodes-base.if",
      "position": [
        -224,
        96
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition-001",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.shouldCreateFocusTime }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "49a4675b-2a0f-4219-9500-aa1803fd794b",
      "name": "여유 시간 슬롯 분할",
      "type": "n8n-nodes-base.itemLists",
      "position": [
        0,
        0
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "freeSlots"
      },
      "typeVersion": 3
    },
    {
      "id": "a7191014-8661-41c2-b121-7f7f924c895e",
      "name": "집중 시간 이벤트 생성",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        224,
        0
      ],
      "parameters": {
        "end": "={{ $json.end }}",
        "start": "={{ $json.start }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "primary",
          "cachedResultName": "Primary"
        },
        "additionalFields": {}
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "Isqbn5j7Czj35HLS",
          "name": "Google Calendar account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "019babfa-6ac8-4028-951c-0cb776ef24cd",
      "name": "결과 로그 기록",
      "type": "n8n-nodes-base.code",
      "position": [
        448,
        0
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst summary = {\n  message: 'Focus time blocks created successfully for the week',\n  totalSlots: items.length,\n  slots: items.map(item => ({\n    start: item.json.start.dateTime,\n    end: item.json.end.dateTime,\n    summary: item.json.summary\n  }))\n};\n\nconsole.log('Weekly Focus Time Creation Summary:', JSON.stringify(summary, null, 2));\n\nreturn [{\n  json: summary\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7ecbdb56-9195-4d13-891b-e307d10417f5",
      "name": "집중 시간 불필요 로그 기록",
      "type": "n8n-nodes-base.code",
      "position": [
        0,
        208
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconsole.log('Weekly Focus Time Analysis:', data.message);\n\nreturn [{\n  json: {\n    message: data.message,\n    weekAnalyzed: data.weekAnalyzed,\n    action: 'no_focus_time_needed'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "5a76f16a-5931-45d8-8319-440b39491a65",
      "name": "스티키 노트",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -80
      ],
      "parameters": {
        "height": 400,
        "content": "Set the schedule time the workflow would run (recommend to set at a time before user's work day starts). "
      },
      "typeVersion": 1
    },
    {
      "id": "f9e74c3f-ab4e-4e70-bddc-08f4903dc6f5",
      "name": "스티키 노트1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -80
      ],
      "parameters": {
        "width": 592,
        "height": 400,
        "content": "Workflow checks user's calendar from Sunday to Saturday of current week. Goal of this part is to determine if a day has 6 or more hours booked already\n\nNote: Update the credentials used in the \"Get Full Weeks Events\" node to use your Google credentials. "
      },
      "typeVersion": 1
    },
    {
      "id": "8ac0657c-c682-414e-b56e-884f517bcda2",
      "name": "스티키 노트2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -176
      ],
      "parameters": {
        "width": 704,
        "height": 544,
        "content": "For days with 6 or more hours already booked, the workflow automatically blocks out the remaining hours for dedicated focus time. The workflows assumes an 8 hour work week. For example, if Monday has 6.5 booked already (for meetings, tasks etc.), the workflow will block the remaining 1.5 hours. The results are logged to help with any troubleshooting\n\nNote: Update the credentials used in the \"Create Focus Time Event\" node to use your Google credentials. "
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "9c49a780-08ab-4198-9a16-8cdb241b5425",
  "connections": {
    "965166ee-bf7e-48e6-b559-624c4a5bb72f": {
      "main": [
        [
          {
            "node": "af8970e0-6d2c-48d4-907b-5e022ab8d5ac",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "49a4675b-2a0f-4219-9500-aa1803fd794b": {
      "main": [
        [
          {
            "node": "a7191014-8661-41c2-b121-7f7f924c895e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "af8970e0-6d2c-48d4-907b-5e022ab8d5ac": {
      "main": [
        [
          {
            "node": "76c503e8-926f-4254-be1b-f2c5b55938e8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a7191014-8661-41c2-b121-7f7f924c895e": {
      "main": [
        [
          {
            "node": "019babfa-6ac8-4028-951c-0cb776ef24cd",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a6d17dd6-3aea-4860-b3c1-cd95d8e6a78c": {
      "main": [
        [
          {
            "node": "49a4675b-2a0f-4219-9500-aa1803fd794b",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "7ecbdb56-9195-4d13-891b-e307d10417f5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "76c503e8-926f-4254-be1b-f2c5b55938e8": {
      "main": [
        [
          {
            "node": "a6d17dd6-3aea-4860-b3c1-cd95d8e6a78c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

중급 - 개인 생산성

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
중급
노드 수11
카테고리1
노드 유형6
난이도 설명

일정 경험을 가진 사용자를 위한 6-15개 노드의 중간 복잡도 워크플로우

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34