0Pricing
AI Agents · 강의

캘린더 일정 생성 및 조회

Google Calendar API로 일정을 조회하고 회의를 만들며 알림을 설정합니다.

캘린더 일정 생성 및 조회은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

Google Calendar API 개요

Google Calendar API를 사용하면 에이전트가 캘린더 일정을 읽고, 만들고, 업데이트하고, 삭제할 수 있습니다. Gmail과 동일한 Google OAuth 인증을 사용합니다. 주요 리소스는 일정 개체이며, 일정은 캘린더에 속합니다. 'primary' 캘린더 ID는 인증된 사용자의 기본 캘린더를 가리킵니다.

from googleapiclient.discovery import build

# Build calendar service (same credentials as Gmail)
calendar = build(
    'calendar', 'v3',
    credentials=creds,
    cache_discovery=False
)

# Get a list of the user's calendars
cal_list = calendar.calendarList().list().execute()
for cal in cal_list.get('items', []):
    print(f'{cal["summary"]}: {cal["id"]}')
    # Primary calendar ID is the user's email address

다가오는 일정 조회

service.events().list()를 사용하여 일정을 가져옵니다. timeMin 및 timeMax 매개변수로 시간 범위를 필터링합니다. 항상 시간대 오프셋이 포함된 RFC 3339 datetime 문자열을 사용하세요. 반복 일정을 개별 일정 인스턴스로 펼치려면 singleEvents=True로 설정하세요.

import datetime
import pytz

def get_upcoming_events(service, calendar_id='primary', max_results=10):
    now = datetime.datetime.now(pytz.utc)
    time_min = now.isoformat()  # RFC 3339 format
    time_max = (now + datetime.timedelta(days=7)).isoformat()

    events_result = service.events().list(
        calendarId=calendar_id,
        timeMin=time_min,
        timeMax=time_max,
        maxResults=max_results,
        singleEvents=True,      # expand recurring events
        orderBy='startTime'
    ).execute()

    events = events_result.get('items', [])
    for event in events:
        start = event['start'].get('dateTime', event['start'].get('date'))
        print(f'{start}: {event["summary"]}')

    return events

일정 시작 및 종료 시간 파싱

캘린더 일정의 시간은 두 가지 유형으로 나뉩니다. 일반 일정에는 dateTime(시간대가 포함된 특정 시각)을 사용하고, 종일 일정에는 date(시간 없이 날짜만)를 사용합니다. 파싱하기 전에 항상 어떤 필드가 존재하는지 확인하세요.

from datetime import datetime
import re

def parse_event_time(time_dict):
    if 'dateTime' in time_dict:
        # Parse RFC 3339 datetime: '2026-05-29T14:00:00+03:00'
        dt_str = time_dict['dateTime']
        # Python 3.7+ fromisoformat handles this
        return datetime.fromisoformat(dt_str), False  # not all-day
    else:
        # All-day event: '2026-05-29'
        date_str = time_dict['date']
        return datetime.strptime(date_str, '%Y-%m-%d'), True  # all-day

for event in events:
    start_dt, is_all_day = parse_event_time(event['start'])
    end_dt, _ = parse_event_time(event['end'])
    duration = end_dt - start_dt if not is_all_day else None
    print(f'{event["summary"]}: {start_dt.strftime("%H:%M")} '
          f'({"all-day" if is_all_day else str(duration)})')

간단한 일정 만들기

service.events().insert()를 사용하여 새 일정을 만듭니다. 최소 필수 필드는 summary(제목), start, end입니다. 시간은 시간대가 포함된 RFC 3339 형식이어야 합니다. 시작 및 종료의 timeZone 필드는 시간대 이름을 지정합니다.

def create_event(service, title, start_datetime, end_datetime,
                 description='', timezone='UTC', calendar_id='primary'):
    event = {
        'summary': title,
        'description': description,
        'start': {
            'dateTime': start_datetime.isoformat(),
            'timeZone': timezone
        },
        'end': {
            'dateTime': end_datetime.isoformat(),
            'timeZone': timezone
        }
    }

    created = service.events().insert(
        calendarId=calendar_id,
        body=event
    ).execute()

    print(f'Event created: {created["summary"]}')
    print(f'Event ID: {created["id"]}')
    print(f'Link: {created["htmlLink"]}')
    return created

# --- demo: minimal stand-in for the Calendar API's service object ---
import datetime

class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeEvents:
    def insert(self, calendarId, body):
        return _Exec({**body, 'id': 'evt_123',
                       'htmlLink': 'https://calendar.google.com/event?eid=evt_123'})

class _FakeService:
    def events(self):
        return _FakeEvents()

service = _FakeService()
start = datetime.datetime(2026, 8, 10, 9, 0)
end = start + datetime.timedelta(hours=1)
create_event(service, 'Team Sync', start, end, description='Weekly sync')

일정에 참석자 추가

이메일 주소가 포함된 attendees 목록을 추가하여 일정에 참석자를 지정합니다. 모든 참석자에게 Google Calendar 초대장을 자동으로 보내려면 sendUpdates='all'로 설정하세요. 외부 게스트에게만 알리려면 'externalOnly'를 사용하세요.

import datetime

def schedule_meeting(service, title, start_iso, duration_minutes,
                     attendees, description=''):
    start = datetime.datetime.fromisoformat(start_iso)
    end = start + datetime.timedelta(minutes=duration_minutes)

    event = {
        'summary': title,
        'description': description,
        'start': {'dateTime': start.isoformat(), 'timeZone': 'UTC'},
        'end': {'dateTime': end.isoformat(), 'timeZone': 'UTC'},
        'attendees': [
            {'email': email} for email in attendees
        ],
        'conferenceData': {
            'createRequest': {'requestId': f'meet-{start.timestamp()}'}
        }  # creates a Google Meet link
    }

    created = service.events().insert(
        calendarId='primary',
        body=event,
        sendUpdates='all',           # send invites
        conferenceDataVersion=1      # enable Meet link creation
    ).execute()
    return created

# --- demo: minimal stand-in for the Calendar API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeEvents:
    def insert(self, calendarId, body, sendUpdates=None, conferenceDataVersion=None):
        return _Exec({**body, 'id': 'evt_456'})

class _FakeService:
    def events(self):
        return _FakeEvents()

service = _FakeService()
created = schedule_meeting(
    service, 'Sprint Planning', '2026-08-11T10:00:00', 30,
    attendees=['alice@example.com', 'bob@example.com']
)
print(f'Meeting scheduled: {created["summary"]}')
print(f'Attendees: {[a["email"] for a in created["attendees"]]}')

일정에 알림 추가

일정에는 일정 전에 전송되는 알림인 알림을 설정할 수 있습니다. 브라우저 또는 앱 알림에는 'popup'을 사용하고, 이메일 알림에는 'email'을 사용하세요. 캘린더 기본 설정을 사용자 지정 시간으로 바꾸려면 useDefault: False로 설정하세요.

def create_event_with_reminders(service, title, start_iso, end_iso):
    event = {
        'summary': title,
        'start': {'dateTime': start_iso, 'timeZone': 'UTC'},
        'end': {'dateTime': end_iso, 'timeZone': 'UTC'},
        'reminders': {
            'useDefault': False,
            'overrides': [
                {'method': 'email', 'minutes': 24 * 60},   # 1 day before
                {'method': 'popup', 'minutes': 30},         # 30 min before
                {'method': 'popup', 'minutes': 10}          # 10 min before
            ]
        }
    }

    return service.events().insert(
        calendarId='primary',
        body=event
    ).execute()

# --- demo: minimal stand-in for the Calendar API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeEvents:
    def insert(self, calendarId, body):
        return _Exec({**body, 'id': 'evt_789'})

class _FakeService:
    def events(self):
        return _FakeEvents()

service = _FakeService()
created = create_event_with_reminders(
    service, 'Deploy Review', '2026-08-12T15:00:00', '2026-08-12T15:30:00'
)
print(f'Event id: {created["id"]}')
print('Reminders:', created['reminders']['overrides'])

반복 일정 만들기

반복 일정은 iCalendar RFC 5545와 동일한 형식의 RRULE(반복 규칙) 문자열을 사용합니다. 일반적인 규칙으로는 월요일, 수요일, 금요일을 지정하는 FREQ=WEEKLY;BYDAY=MO,WE,FR과 매월 첫 번째 월요일을 지정하는 FREQ=MONTHLY;BYDAY=1MO가 있습니다.

def create_recurring_event(service, title, start_iso, end_iso,
                           rrule, timezone='UTC'):
    event = {
        'summary': title,
        'start': {'dateTime': start_iso, 'timeZone': timezone},
        'end': {'dateTime': end_iso, 'timeZone': timezone},
        'recurrence': [
            f'RRULE:{rrule}'  # recurrence rule string
        ]
    }

    return service.events().insert(
        calendarId='primary',
        body=event
    ).execute()

# Weekly team standup every Monday at 9am for 30 min
create_recurring_event(
    service=calendar_service,
    title='Team Standup',
    start_iso='2026-06-01T09:00:00',
    end_iso='2026-06-01T09:30:00',
    rrule='FREQ=WEEKLY;BYDAY=MO;COUNT=52',  # 52 weeks
    timezone='America/New_York'
)

기존 일정 업데이트

service.events().patch()를 사용하면 전체 일정을 바꾸지 않고 기존 일정의 특정 필드만 업데이트할 수 있습니다. 일정 본문 전체를 바꾸려면 .update()를 사용하세요. 두 방법 모두 원래 create 또는 list 응답에 포함된 일정 ID가 필요합니다.

def reschedule_event(service, event_id, new_start_iso, new_end_iso,
                     timezone='UTC', calendar_id='primary'):
    updated_fields = {
        'start': {'dateTime': new_start_iso, 'timeZone': timezone},
        'end': {'dateTime': new_end_iso, 'timeZone': timezone}
    }

    updated_event = service.events().patch(
        calendarId=calendar_id,
        eventId=event_id,
        body=updated_fields,
        sendUpdates='all'  # notify attendees of the change
    ).execute()

    print(f'Event rescheduled: {updated_event["summary"]}')
    print(f'New start: {updated_event["start"]["dateTime"]}')
    return updated_event

# --- demo: minimal stand-in for the Calendar API's service object ---
class _Exec:
    def __init__(self, result):
        self._result = result
    def execute(self):
        return self._result

class _FakeEvents:
    def patch(self, calendarId, eventId, body, sendUpdates=None):
        return _Exec({'summary': 'Team Sync', 'id': eventId, **body})

class _FakeService:
    def events(self):
        return _FakeEvents()

service = _FakeService()
reschedule_event(service, 'evt_123', '2026-08-13T09:00:00', '2026-08-13T10:00:00')

일정 삭제

service.events().delete()를 사용하여 일정을 취소하고 삭제합니다. sendUpdates='all'로 설정하면 참석자에게 알립니다. 반복 일정의 경우 사용한 일정 ID에 따라 단일 인스턴스만 삭제하거나 전체 시리즈를 삭제할 수 있습니다.

from googleapiclient.errors import HttpError

def cancel_event(service, event_id, notify_attendees=True,
                 calendar_id='primary'):
    try:
        service.events().delete(
            calendarId=calendar_id,
            eventId=event_id,
            sendUpdates='all' if notify_attendees else 'none'
        ).execute()
        print(f'Event {event_id} cancelled')
        return True
    except HttpError as e:
        if e.resp.status == 404:
            print(f'Event {event_id} not found (already deleted?)')
        elif e.resp.status == 403:
            print('No permission to delete this event')
        else:
            print(f'Delete failed: {e.resp.status}')
        return False

일정 가능 여부 확인

회의를 예약하기 전에 freebusy API를 사용하여 참석자의 일정 가능 여부를 확인하세요. 이 API는 지정된 시간 범위에서 여러 캘린더의 바쁜 시간 블록을 반환하므로, 일정을 예약하는 에이전트가 빈 시간을 찾는 데 적합합니다.

import datetime
import pytz

def check_availability(service, attendees, duration_hours=1):
    now = datetime.datetime.now(pytz.utc)
    time_min = now.isoformat()
    time_max = (now + datetime.timedelta(days=5)).isoformat()

    body = {
        'timeMin': time_min,
        'timeMax': time_max,
        'timeZone': 'UTC',
        'items': [{'id': email} for email in attendees]
    }

    result = service.freebusy().query(body=body).execute()

    for email in attendees:
        busy_slots = result['calendars'].get(email, {}).get('busy', [])
        print(f'{email}: {len(busy_slots)} busy slots in next 5 days')
        for slot in busy_slots[:3]:
            print(f'  Busy: {slot["start"]} -> {slot["end"]}')

    return result

쿼리로 일정 검색

events().list()의 q 매개변수를 사용하면 텍스트로 일정을 검색할 수 있습니다. 검색 대상에는 제목, 설명, 위치, 참석자 이름이 포함됩니다. timeMin/timeMax와 함께 사용하면 특정 기간으로 결과를 좁힐 수 있습니다.

def search_events(service, query, days_back=30, days_forward=30,
                  calendar_id='primary'):
    import datetime, pytz
    now = datetime.datetime.now(pytz.utc)

    result = service.events().list(
        calendarId=calendar_id,
        q=query,
        timeMin=(now - datetime.timedelta(days=days_back)).isoformat(),
        timeMax=(now + datetime.timedelta(days=days_forward)).isoformat(),
        singleEvents=True,
        orderBy='startTime',
        maxResults=50
    ).execute()

    events = result.get('items', [])
    print(f'Found {len(events)} events matching "{query}"')
    for event in events:
        start = event['start'].get('dateTime', event['start'].get('date'))
        print(f'  {start}: {event["summary"]}')
    return events

빠른 확인: singleEvents 매개변수

Calendar API에 대한 이해도를 확인해 보세요.

Calendar API 복습

이제 에이전트가 Google Calendar를 프로그래밍 방식으로 관리할 수 있습니다.

  • 일정 조회: events().list(timeMin=now, singleEvents=True, orderBy='startTime')
  • 일정 만들기: events().insert(body={summary, start, end, attendees})
  • 반복 일정: recurrence: ['RRULE:FREQ=WEEKLY;BYDAY=MO'] 추가
  • 알림: reminders.overrides와 method: popup/email 및 minutes 사용
  • 업데이트: 부분 업데이트에는 events().patch() 사용, 참석자에게 알리려면 sendUpdates='all' 사용
  • 일정 가능 여부: 예약하기 전에 가능 여부를 확인하려면 freebusy().query() 사용

자주 묻는 질문

“캘린더 일정 생성 및 조회” 강의는 무료인가요?

네 — “캘린더 일정 생성 및 조회” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“캘린더 일정 생성 및 조회”에서 뭘 배우나요?

Google Calendar API로 일정을 조회하고 회의를 만들며 알림을 설정합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“캘린더 일정 생성 및 조회” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. API로 Gmail 연결하기
  2. 프로그래밍으로 이메일 읽기 및 보내기
  3. 캘린더 일정 생성 및 조회
  4. 간단한 이메일 도우미 에이전트 만들기
← AI Agents(으)로 돌아가기