Python의 HTTP Cloud Functions

1. 소개

b158ce75c3cccd6d.png

Python은 데이터 과학자, 웹 애플리케이션 개발자, 시스템 관리자 등이 사용하는 인기 있는 오픈소스 프로그래밍 언어입니다.

Cloud Functions는 이벤트 기반의 서버리스 컴퓨팅 플랫폼입니다. Cloud Functions를 사용하면 리소스 프로비저닝이나 요구사항 변경을 처리하기 위한 확장을 걱정할 필요 없이 코드를 작성할 수 있습니다.

Cloud Functions에는 다음과 같은 두 가지 유형이 있습니다.

  • HTTP 함수는 HTTP 요청에 응답합니다. 이 Codelab에서 몇 가지 항목을 빌드합니다.
  • 백그라운드 함수는 메시지가 Cloud Pub/Sub에 게시되거나 파일이 Cloud Storage에 업로드되는 등의 이벤트에 의해 트리거됩니다. 이 실습에서는 이 문제를 다루지 않지만 문서에서 자세히 알아볼 수 있습니다.

efb3268e3b74ed4f.png

이 Codelab에서는 Python에서 자체 Cloud Functions를 만드는 방법을 안내합니다.

빌드할 항목

이 Codelab에서는 HTTP를 통해 호출될 때 'Python 기반' 로고:

a7aaf656b78050fd.png

학습할 내용

  • HTTP Cloud 함수를 작성하는 방법
  • 인수를 사용하는 HTTP Cloud 함수를 작성하는 방법
  • HTTP Cloud 함수를 테스트하는 방법
  • 로컬 Python HTTP 서버를 실행하여 함수를 사용하는 방법
  • 이미지를 반환하는 HTTP Cloud 함수를 작성하는 방법

2. 설정 및 요건

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.

Cloud Shell 시작

Google Cloud를 노트북에서 원격으로 실행할 수도 있지만 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Cloud Shell을 사용합니다.

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화853e55310c205094.png를 클릭합니다.

3c1dabeca90e44e5.png

Cloud Shell을 처음 시작하는 경우에는 무엇이 있는지 설명하는 중간 화면이 표시됩니다. 중간 화면이 표시되면 계속을 클릭합니다.

92662c6a846a5c.png

Cloud Shell을 프로비저닝하고 연결하는 데 몇 분 정도만 걸립니다.

9f0e51b578fecce5.png

가상 머신에는 필요한 개발 도구가 모두 들어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab에서 대부분의 작업은 브라우저를 사용하여 수행할 수 있습니다.

Cloud Shell에 연결되면 인증이 완료되었고 프로젝트가 자신의 프로젝트 ID로 설정된 것을 확인할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 실행하여 인증되었는지 확인합니다.
gcloud auth list

명령어 결과

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

또는 다음 명령어로 설정할 수 있습니다.

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

Cloud Functions 및 Cloud Build API가 사용 설정되어 있는지 확인

Cloud Shell에서 다음 명령어를 실행하여 Cloud Functions 및 Cloud Build API가 사용 설정되어 있는지 확인합니다.

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

참고: Cloud Build는 gcloud functions deploy 명령어로 호출되며 코드를 컨테이너 이미지에 자동으로 빌드합니다.

소스 코드 다운로드하기

Cloud Shell 터미널에서 다음 명령어를 실행합니다.

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

소스 디렉터리의 콘텐츠를 확인합니다.

ls

다음과 같은 파일이 있어야 합니다.

main.py  python-powered.png  test_main.py  web_app.py

3. HTTP Cloud Functions 소개

Python의 HTTP Cloud Functions는 일반 Python 함수로 작성됩니다. 이 함수는 일반적으로 이름이 request인 단일 flask.Request 인수를 허용해야 합니다.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

원하는 명령줄 편집기 (nano, vim 또는 emacs)를 사용하여 파일을 열 수 있습니다. 소스 디렉터리를 작업공간으로 설정한 후 Cloud Shell 편집기에서 열 수도 있습니다.

cloudshell open-workspace .

gcloud functions deploy 명령어를 사용하여 이 함수를 HTTP Cloud 함수로 배포해 보겠습니다.

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

gcloud functions deploy 옵션에 관한 참고사항:

  • --runtime: 언어 런타임을 지정합니다. Python의 경우 현재 python37, python38, python39, python310 또는 python312일 수 있습니다. 런타임을 참고하세요.
  • --trigger-http: 함수에 엔드포인트가 할당됩니다. 엔드포인트에 대한 HTTP 요청 (POST, PUT, GET, DELETE, OPTIONS)은 함수 실행을 트리거합니다.
  • --allow-unauthenticated: 함수가 공개되어 인증 확인 없이 모든 호출자를 허용합니다.
  • 자세한 내용은 gcloud functions deploy를 참조하세요.

함수를 테스트하려면 위의 명령어 결과에 표시된 httpsTrigger.url URL을 클릭하면 됩니다. 프로그래매틱 방식으로 URL을 검색하고 다음 명령어를 사용하여 함수를 호출할 수도 있습니다.

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

다음과 같은 결과가 표시됩니다.

Hello World! 👋

4. 인수를 사용하는 HTTP Cloud 함수 작성

함수는 인수를 수락할 때 더 다양하게 활용할 수 있습니다. name 매개변수를 지원하는 새 함수 hello_name를 정의해 보겠습니다.

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

다음 새 함수를 배포해 보겠습니다.

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

함수를 테스트하려면 위의 명령어 결과에 표시된 httpsTrigger.url URL을 클릭하면 됩니다. 프로그래매틱 방식으로 URL을 검색하고 다음 명령어를 사용하여 함수를 호출할 수도 있습니다.

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

다음과 같이 기본 결과가 표시됩니다.

Hello World! 🚀

name 인수가 설정되지 않아 기본 결과가 표시됩니다. 다음과 같이 URL에 매개변수를 추가합니다.

curl -w "\n" $URL?name=YOUR%20NAME

이번에는 맞춤 응답을 받게 됩니다.

Hello YOUR NAME! 🚀

다음 단계는 단위 테스트를 추가하여 소스 코드가 업데이트될 때 함수가 의도한 대로 계속 작동하는지 확인하는 것입니다.

5. 테스트 작성

Python의 HTTP Cloud Functions는 표준 라이브러리의 unittest 모듈을 사용하여 테스트됩니다. 에뮬레이터나 다른 시뮬레이션을 실행하여 함수를 테스트할 필요가 없으며 일반 Python 코드만 있으면 됩니다.

hello_worldhello_name 함수의 테스트는 다음과 같습니다.

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Python 테스트는 다른 Python 파일과 동일한 방식으로 작성됩니다. 가져오기 세트로 시작한 후 클래스와 함수를 정의합니다.
  2. 테스트 선언의 형식은 class TestHello(TestCase)입니다. unittest.TestCase에서 상속된 클래스여야 합니다.
  3. 테스트 클래스에는 메서드가 있으며 각 메서드는 개별 테스트 사례를 나타내는 test_로 시작해야 합니다.
  4. 각 테스트 사례에서는 request 매개변수를 모의 처리하여 함수 중 하나를 테스트합니다 (즉, 테스트에 필요한 특정 데이터가 있는 가짜 객체로 대체).
  5. 각 함수를 호출한 후 테스트는 HTTP 응답이 예상과 일치하는지 확인합니다.

main.pyflask에 종속되므로 Flask 프레임워크가 테스트 환경에 설치되어 있는지 확인합니다.

pip install flask

Flask를 설치하면 다음과 유사한 결과가 출력됩니다.

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

다음 테스트를 로컬에서 실행합니다.

python -m unittest

세 가지 단위 테스트를 통과해야 합니다.

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

다음으로 'Python 기반' 로고

6. 'Python 기반' 작성 HTTP Cloud 함수

'Python 기반' 이미지 1개를 추가합니다.

a7aaf656b78050fd.png

다음 목록은 이를 위한 코드를 보여줍니다.

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

python_powered 함수를 배포합니다.

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

명령어 결과 출력:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

함수를 테스트하려면 위의 명령어 결과에 표시된 httpsTrigger.url URL을 클릭합니다. 모든 것이 제대로 작동하면 'Python 기반' 새 브라우저 탭에 로고가 표시됨!

다음으로, 배포하기 전에 로컬에서 함수를 실행하고 사용해 볼 수 있도록 앱을 만듭니다.

7. 로컬에서 함수 실행

웹 앱을 만들고 경로에서 함수를 호출하여 HTTP 함수를 로컬에서 실행할 수 있습니다. 함수와 동일한 디렉터리에 추가할 수 있습니다. web_app.py이라는 파일에는 다음 내용이 포함됩니다.

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. 이 파일은 Flask 애플리케이션을 만듭니다.
  2. index()라는 함수로 처리되는 기본 URL에 경로를 등록합니다.
  3. 그런 다음 index() 함수는 python_powered 함수를 호출하여 현재 요청을 전달합니다.

Flask 프레임워크가 개발 환경에 설치되어 있는지 확인합니다.

pip install flask

Flask를 설치하면 다음과 유사한 결과가 출력됩니다.

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

이 애플리케이션을 로컬에서 실행하려면 다음 명령어를 실행합니다.

python web_app.py

이제 Cloud Shell 웹 미리보기를 사용하여 브라우저에서 웹 앱을 테스트합니다. Cloud Shell에서 '웹 미리보기'를 클릭합니다. '포트 8080에서 미리보기'를 선택합니다.

6c9ff9e5c692c58e.gif

Cloud Shell에서 새로운 브라우저 창이 열리면서 프록시 서비스의 미리보기 URL이 표시됩니다. 웹 미리보기는 HTTPS를 통한 액세스를 사용자 계정으로만 제한합니다. 모두 제대로 작동하면 'Python 기반' 로고!

8e5c3ead11cfd103.png

8. 축하합니다.

b158ce75c3cccd6d.png

Flask 프레임워크로 웹 요청을 처리하는 관용적인 함수를 사용해 HTTP Cloud Functions를 배포했습니다.

Cloud Functions 가격은 함수가 호출되는 빈도를 기준으로 책정되며 자주 실행되지 않는 함수를 위한 무료 등급도 포함됩니다. Cloud Functions 테스트를 완료한 후 gcloud를 사용하여 삭제할 수 있습니다.

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Google Cloud 콘솔에서 함수를 삭제할 수도 있습니다.

Python에서 Cloud Functions를 유용하게 사용하시기 바랍니다.