gmail-api

Gmail API

Gmail API는 Gmail 사서함(mailbox)에 접근하고 메일을 보낼 수 있게 해 주는 RESTful API예요. 웹 애플리케이션 대부분에서 Gmail API는 사용자 Gmail 데이터에 액세스하는 가장 좋은 선택이며, 메일을 읽는 백업/인덱싱, 자동 메일 발송, 계정 마이그레이션, 메일 정리(필터링·정렬) 등 다양한 용도로 활용할 수 있어요. 메일은 모두 사용자 계정의 인증(주로 OAuth 2.0)을 거쳐 접근하고, users·messages·drafts·threads·labels 같은 리소스 단위로 다뤄요.

출처: 문서

본문

핵심 용어와 기능

  • Delegator와 Delegate — delegator는 같은 Google Workspace 조직 안에서 다른 사용자에게 사서함 접근 권한을 부여한 사용자예요. 권한을 받은 사용자가 delegate이고, 대신 메일을 읽고, 보내고, 삭제하며 연락처를 조회·추가할 수 있어요.
  • Draft(임시저장) — 아직 보내지 않은 메일이에요. 임시저장에 담긴 메시지는 교체할 수 있고, 임시저장을 보내면 자동으로 임시저장이 삭제되면서 SENT 시스템 라벨이 붙은 메시지가 생성돼요. drafts 리소스로 표현해요.
  • Filter(필터) — 계정에 설정하는 고급 규칙으로, 수신 메일을 발신자·제목·크기 등 기준으로 평가해요. 조건에 맞으면 라벨 추가/제거나 특정 주소로 전달 같은 동작을 자동 실행해요.
  • Label(라벨) — 메시지와 스레드를 정리하는 방법이에요. 시스템 라벨(INBOX, TRASH, SPAM 등)은 삭제하거나 수정할 수 없고, 사용자 라벨은 사용자나 애플리케이션이 만들고 수정·삭제할 수 있어요. 사용자 라벨은 labels 리소스로 표현해요.
  • Thread(스레드) — 대화를 이루는 관련 메시지들의 모음이에요. 수신자가 메시지에 답장하면 스레드가 형성돼요. threads 리소스로 표현해요.
  • Push notifications — Google Cloud Pub/Sub과 연동되는 서버 측 알림 시스템이에요. 앱이 사서함을 "watch"하면 새 메일이 도착하는 등 변경이 있을 때 자동으로 webhook/알림을 받아서, 계속 폴링하지 않아도 돼요.

OAuth 인증

Gmail에 액세스하려면 사용자가 애플리케이션의 권한 요청에 동의해야 해요. Google Workspace 개발 문서에서 제공하는 OAuth 2.0 흐름을 따르며, 요청 범위(scope)로 어떤 권한을 요청할지 지정해요. 예를 들어 읽기 전용이면 https://www.googleapis.com/auth/gmail.readonly을 써요. 권한 범위와 접근 토큰은 애플리케이션 로직이 아니라 OAuth 동의 화면에서 사용자가 직접 승인해요.

SDK 설치 (Python)

Gmail API를 쓰려면 Google API 클라이언트 라이브러리를 설치해요.

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

사용 예시 — 라벨 목록 가져오기 (Quickstart)

Google Cloud 콘솔에서 OAuth 클라이언트 ID를 만들고 credentials.json을 작업 디렉터리에 저장한 뒤, 다음 코드를 실행해요. 첫 실행 시 인증 흐름이 열리고 사용자가 동의하면 token.json이 저장돼서 다음부터는 다시 인증받지 않아요.

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]


def main():
    """Shows basic usage of the Gmail API.
    Lists the user's Gmail labels.
    """
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists("token.json"):
        creds = Credentials.from_authorized_user_file("token.json", SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                "credentials.json", SCOPES
            )
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open("token.json", "w") as token:
            token.write(creds.to_json())

    try:
        # Call the Gmail API
        service = build("gmail", "v1", credentials=creds)
        results = service.users().labels().list(userId="me").execute()
        labels = results.get("labels", [])

        if not labels:
            print("No labels found.")
            return
        print("Labels:")
        for label in labels:
            print(label["name"])

    except HttpError as error:
        # TODO(developer) - Handle errors from gmail API.
        print(f"An error occurred: {error}")


if __name__ == "__main__":
    main()

실행은 다음과 같이 해요.

python3 quickstart.py

메일 보내기

Gmail API로 이메일을 보내는 방법은 두 가지예요. messages.send 메서드로 직접 보내거나, 임시저장(draft)에서 drafts.send 메서드로 보내는 방법이에요. Gmail 메시지는 messages 리소스의 raw 필드에 base64URL로 인코딩된 문자열로 전달돼요. 즉, 메일 내용을 만들어 base64URL로 인코딩하고, 메시지 리소스의 raw 속성에 넣은 뒤 messages.send(또는 임시저장이면 drafts.send)를 호출해요. 메시지는 RFC 2822 표준 MIME 형식이어야 해요.

import base64
from email.message import EmailMessage

import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def gmail_send_message():
    """Create and send an email message
    Print the returned message id
    Returns: Message object, including message id

    Load pre-authorized user credentials from the environment.
    TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application.
    """
    creds, _ = google.auth.default()

    try:
        service = build("gmail", "v1", credentials=creds)

        message = EmailMessage()

        message.set_content("This is automated draft mail")

        message["To"] = "[email protected]"
        message["From"] = "[email protected]"
        message["Subject"] = "Automated draft"

        # encoded message
        encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

        create_message = {"raw": encoded_message}
        # pylint: disable=E1101
        send_message = (
            service.users()
            .messages()
            .send(userId="me", body=create_message)
            .execute()
        )
        print(f'Message Id: {send_message["id"]}')
    except HttpError as error:
        print(f"An error occurred: {error}")
        send_message = None
    return send_message


if __name__ == "__main__":
    gmail_send_message()

cURL로는 다음과 같이 호출해요.

curl --request POST \
  'https://gmail.googleapis.com/gmail/v1/users/me/messages/send' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"raw":"MESSAGE"}'

ACCESS_TOKEN은 API에 접근하는 access token, MESSAGE는 base64URL로 인코딩된 RFC 2822 형식 MIME 메시지예요.

임시저장(Draft) 만들기

보내기 전에 임시저장부터 만들어 두고 싶다면 drafts.create를 사용해요.

import base64
from email.message import EmailMessage

import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def gmail_create_draft():
    """Create and insert a draft email.
    Print the returned draft's message and id.
    Returns: Draft object, including draft id and message meta data.

    Load pre-authorized user credentials from the environment.
    TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application.
    """
    creds, _ = google.auth.default()

    try:
        # create gmail api client
        service = build("gmail", "v1", credentials=creds)

        message = EmailMessage()

        message.set_content("This is automated draft mail")

        message["To"] = "[email protected]"
        message["From"] = "[email protected]"
        message["Subject"] = "Automated draft"

        # encoded message
        encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

        create_message = {
            "message": {"raw": encoded_message}
        }
        # pylint: disable=E1101
        draft = (
            service.users()
            .drafts()
            .create(userId="me", body=create_message)
            .execute()
        )

        print(f"Draft id: {draft['id']}\nDraft message: {draft['message']}")

    except HttpError as error:
        print(f"An error occurred: {error}")
        draft = None

    return draft


if __name__ == "__main__":
    gmail_create_draft()

스레드(Thread)로 묶어 답장하기

답장을 보내고 메일이 하나의 스레드로 묶이게 하려면 다음 조건을 지켜야 해요.

  • Subject 헤더가 일치해야 해요.
  • ReferencesIn-Reply-To 헤더가 RFC 2822 표준을 따라야 해요.

더 알아보기 (Learn more)