Gemini Code Assist로 개발 워크플로 강화

1. 소개

e5b98fd4e417c877.png

이 Codelab에서는 Gemini Code Assist가 설계, 빌드, 테스트, 배포와 같은 소프트웨어 개발 수명 주기 (SDLC)의 주요 단계에서 개발자를 어떻게 지원하는지 살펴보겠습니다. 전체 애플리케이션을 설계 및 개발하여 Google Cloud에 배포합니다.

기술 이벤트의 여러 세션에서 검색할 수 있는 API와 애플리케이션을 개발할 예정입니다. 각 세션에는 제목, 요약, 기간, 카테고리 및 한 명 이상의 발표자가 있습니다.

실행할 작업

  • 처음부터 OpenAPI 사양을 기반으로 웹 애플리케이션을 설계, 빌드, 테스트, 배포합니다.

학습 내용

  • Gemini Code Assist를 사용하여 OpenAPI 사양을 생성하는 방법
  • Gemini Code Assist 코드 생성 기능을 사용하여 OpenAPI 사양에 맞는 Python Flask 애플리케이션을 개발하는 방법
  • Gemini Code Assist를 사용하여 Python Flask 애플리케이션의 웹 프런트엔드를 생성하는 방법
  • Gemini Code Assist를 사용하여 Google Cloud Run에 애플리케이션을 배포하는 방법에 대한 지원을 받는 방법
  • 애플리케이션을 빌드하고 테스트하는 동안 코드 설명, 테스트 사례 생성과 같은 Gemini Code Assist 기능을 사용합니다.

필요한 항목

  • Chrome 웹브라우저
  • Gmail 계정
  • 결제가 사용 설정된 Cloud 프로젝트
  • Cloud 프로젝트에 Gemini Code Assist가 사용 설정됨

이 실습은 초보자를 포함한 모든 수준의 개발자를 대상으로 합니다. 샘플 애플리케이션은 Python 언어로 작성되어 있지만 Python 프로그래밍에 익숙하지 않아도 내용을 이해할 수 있습니다. 여기서는 Gemini Code Assist의 기능에 익숙해지는 데 중점을 두겠습니다.

2. Gemini Code Assist 설정

이 섹션에서는 실습을 시작하는 데 필요한 모든 사항을 다룹니다.

Cloud Shell IDE에서 Gemini Code Assist 사용 설정

Codelab의 나머지 부분에서는 완전 관리형 Code OSS 기반 개발 환경인 Cloud Shell IDE를 사용합니다. Cloud Shell IDE에서 Code Assist를 사용 설정하고 구성해야 하며 단계는 다음과 같습니다.

  1. ide.cloud.google.com을 방문하세요. IDE가 표시되는 데 다소 시간이 걸릴 수 있으므로 잠시 기다렸다가 설정 기본 선택을 수락하세요. IDE 설정에 관한 안내가 표시되면 기본 설정으로 완료하세요.
  2. 그림과 같이 하단 상태 표시줄에서 Cloud Code - 로그인 버튼을 클릭합니다. 안내에 따라 플러그인을 승인합니다. 상태 표시줄에 'Cloud Code - 프로젝트 없음'이 표시되면 이를 선택한 다음 작업하려는 프로젝트 목록에서 특정 Google Cloud 프로젝트를 선택합니다.

6f5ce865fc7a3ef5.png

  1. 오른쪽 하단에 있는 Code Assist 버튼을 클릭하고 올바른 Google Cloud 프로젝트를 마지막으로 한 번 선택합니다. Cloud AI Companion API를 사용 설정하라는 메시지가 표시되면 사용 설정하고 진행하세요.
  2. Google Cloud 프로젝트를 선택한 후 상태 표시줄의 Cloud Code 상태 메시지에서 아래와 같이 오른쪽의 상태 표시줄에서 Code Assist도 사용 설정되어 있는지 확인합니다.

709e6c8248ac7d88.png

이제 Gemini Code Assist를 사용할 수 있습니다.

3. Firestore 설정

Cloud Firestore는 애플리케이션 데이터의 백엔드로 사용할 완전 관리형 서버리스 문서 데이터베이스입니다. Cloud Firestore의 데이터는 문서컬렉션으로 구조화됩니다.

기본 Firestore 데이터베이스에 sessions라는 컬렉션을 만들어야 합니다. 이 컬렉션에는 애플리케이션에서 사용할 샘플 데이터 (문서)가 들어 있습니다.

아래와 같이 기본 메뉴를 통해 Cloud Shell IDE 내에서 터미널을 엽니다.

f1535e14c9beeec6.png

sessions라는 컬렉션을 만들어야 합니다. 여기에는 샘플 세션 문서 목록이 포함됩니다. 각 문서는 다음과 같은 속성을 갖습니다.

  1. title: 문자열
  2. categories: 문자열 배열
  3. speakers: 문자열 배열
  4. duration: 문자열
  5. summary: 문자열

샘플 데이터가 포함된 파일을 자체 프로젝트의 버킷으로 복사하여 이 컬렉션을 샘플 데이터로 채웁니다. 그런 다음 gcloud firestore import 명령어를 통해 컬렉션을 가져올 수 있습니다.

Firestore 데이터베이스 초기화

Cloud 콘솔에서 Firestore 페이지로 이동합니다.

이전에 프로젝트에서 Firestore 데이터베이스를 초기화하지 않았으면 default 데이터베이스를 만듭니다. 데이터베이스를 만드는 동안 다음 값을 사용합니다.

  • Firestore 모드: Native
  • 위치: 위치 유형을 Region로 선택하고 애플리케이션에 적합한 리전을 선택합니다. 버킷 위치의 다음 단계에서 필요하므로 이 위치를 기록해 둡니다.
  • 데이터베이스를 만듭니다.

504cabdb99a222a5.png

이제 아래 단계에 따라 sessions 컬렉션을 만듭니다.

  1. 아래 제공된 gsutil 명령어를 사용하여 프로젝트에 버킷을 만듭니다. 아래 명령어의 <PROJECT_ID> 변수를 Google Cloud 프로젝트 ID로 바꿉니다. <BUCKET_LOCATION>을 이전 단계에서 설명한 대로 기본 Firestore 데이터베이스의 지리적 영역에 해당하는 리전 이름으로 바꿉니다. US-WEST1, EUROPE-WEST1, ASIA-EAST1일 수 있습니다.
gsutil mb -l <BUCKET-LOCATION> gs://<PROJECT_ID>-my-bucket
  1. 이제 버킷이 생성되었으므로 준비한 데이터베이스 내보내기를 이 버킷에 복사해야 Firebase 데이터베이스로 가져올 수 있습니다. 아래 명령어를 사용합니다.
gsutil cp -r gs://sessions-master-database-bucket/2024-03-26T09:28:15_95256  gs://<PROJECT_ID>-my-bucket

이제 가져올 데이터가 있으므로 생성한 Firebase 데이터베이스 (default)로 데이터를 가져오는 마지막 단계로 이동할 수 있습니다.

  1. 아래의 gcloud 명령어를 사용하세요.
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2024-03-26T09:28:15_95256

가져오기는 몇 초 정도 걸리며 준비가 완료되면 아래와 같이 https://console.cloud.google.com/firestore/databases로 이동해 default 데이터베이스와 sessions 컬렉션을 선택하여 Firestore 데이터베이스와 컬렉션의 유효성을 검사할 수 있습니다.

d3e294d46ba29cd5.png

이렇게 하면 애플리케이션에서 사용할 Firestore 컬렉션 생성이 완료됩니다.

4. 애플리케이션 템플릿 만들기

Codelab의 나머지 부분에서 사용할 샘플 애플리케이션 (Python Flask 애플리케이션)을 만들어 보겠습니다. 이 애플리케이션은 기술 컨퍼런스에서 제공되는 세션을 검색합니다.

다음 단계를 따르세요.

  1. 아래의 상태 표시줄에서 Google Cloud 프로젝트 이름을 클릭합니다.

f151759c156c124e.png

  1. 옵션 목록이 표시됩니다. 아래 목록에서 새 애플리케이션을 클릭합니다.

91ea9836f38b7f74.png

  1. Cloud Run 애플리케이션 (앱의 런타임)을 선택합니다.
  2. Python(Flask): Cloud Run 애플리케이션 템플릿을 선택합니다.
  3. 애플리케이션에 이름을 지정하고 원하는 위치에 저장합니다.
  4. 애플리케이션이 생성되었음을 알리는 알림이 표시되며 아래와 같이 애플리케이션이 로드된 새 창이 열립니다. README.md 파일이 열립니다. 이제 이 뷰를 닫아도 됩니다.

aaa3725b17ce27cf.png

5. Gemini Code Assist와 상호작용

이 실습에서는 VS Code의 Cloud Code 확장 프로그램의 일부로 Cloud Shell IDE 내에서 사용할 수 있는 Gemini Code Assist 채팅을 사용합니다. 왼쪽 탐색 메뉴에서 Code Assist 버튼을 클릭하여 불러올 수 있습니다. 왼쪽 탐색 툴바에서 Code Assist 아이콘 a489f98a34898727.png을 찾아 클릭합니다.

그러면 Cloud Shell IDE 내에 Code Assist 채팅 창이 표시되어 Code Assist와 채팅할 수 있습니다.

14ad103efaa0ddaa.png

상단에 있는 휴지통 아이콘을 보면 Code Assist 채팅 기록의 컨텍스트를 재설정할 수 있습니다. 또한 이 채팅 상호작용은 IDE에서 작업 중인 파일과 관련이 있습니다.

6. API 설계

첫 번째 단계는 설계 단계에서 Gemini Code Assist의 지원을 받는 것입니다. 이 단계에서는 검색하고자 하는 항목 (이벤트의 기술 세션)에 대한 OpenAPI 사양을 생성합니다.

다음 프롬프트를 입력합니다.

Generate an Open API spec that will allow me to retrieve all sessions, sessions by category, session by id. Each session has the following attributes: id, title, list of speakers, list of categories, summary and duration.

이렇게 하면 다양한 쿼리 매개변수를 통해 여러 세션에서 검색하기 위한 OpenAPI 사양이 생성됩니다. 사양 샘플은 아래와 같습니다.

openapi: 3.0.0
info:
 title: Sessions API
 description: This API allows you to retrieve all sessions, sessions by category, and session by id.
 version: 1.0.0
servers:
 - url: https://sessions.example.com
paths:
 /sessions:
   get:
     summary: Get all sessions
     operationId: getSessions
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
 /sessions/{id}:
   get:
     summary: Get session by id
     operationId: getSessionById
     parameters:
       - name: id
         in: path
         required: true
         description: The id of the session
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/Session'
 /sessions/categories/{category}:
   get:
     summary: Get sessions by category
     operationId: getSessionsByCategory
     parameters:
       - name: category
         in: path
         required: true
         description: The category of the sessions
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
components:
 schemas:
   Session:
     type: object
     properties:
       id:
         type: string
         description: The id of the session
       title:
         type: string
         description: The title of the session
       speakers:
         type: array
         items:
           type: string
         description: The list of speakers for the session
       categories:
         type: array
         items:
           type: string
         description: The list of categories for the session
       summary:
         type: string
         description: The summary of the session
       duration:
         type: string
         description: The duration of the session

사양의 내용은 다음과 같습니다.

  • 세션 유형에 정의된 스키마입니다.
  • 다음과 같은 여러 API 경로가 정의됩니다.
  • /sessions
  • /sessions/{id}
  • /sessions/categories/{category}

상단 폴더에 sessionsapi.yaml라는 파일을 만들고 '현재 파일에 삽입' 옵션(+ 버튼)을 사용하여 Code Assist 채팅 창에서 콘텐츠를 복사하고 Cloud Shell IDE에서 파일을 열어 둡니다.

이제 Gemini Code Assist의 흥미로운 기능인 인용을 살펴보실 수 있습니다. 이 정보는 생성된 코드가 기존 오픈소스 코드 등 다른 소스에서 가져온 장문의 인용문을 직접 인용할 때 개발자에게 표시됩니다. 개발자가 데이터로 무엇을 할지 결정할 수 있도록 소스와 라이선스를 제공합니다.

생성된 콘텐츠에 문제가 없다고 가정하고 이제 이 사양 문서를 사용하여 Python Flask 애플리케이션을 생성할 수 있습니다.

7. 애플리케이션 생성

이제 Code Assist에 애플리케이션을 생성하도록 요청합니다. sessionsapi.yaml 파일을 열고 다음 프롬프트를 입력합니다.

Generate a Python Application using the Flask framework, based on the sessionsapi.yaml file. This application uses a local in memory list of sessions. Do not use any Flask extensions.

그러면 OpenAPI 사양 파일에 지정된 기능과 경로를 기반으로 하는 Python Flask 애플리케이션의 뼈대가 제공됩니다.

제공되는 Python Flask 애플리케이션 코드는 다음과 유사합니다.

from flask import Flask, jsonify, request

app = Flask(__name__)

sessions = [
    {
        "id": "1",
        "title": "Session 1",
        "speakers": ["Speaker 1", "Speaker 2"],
        "categories": ["Category 1", "Category 2"],
        "summary": "This is a summary of session 1.",
        "duration": "1 hour",
    },
    {
        "id": "2",
        "title": "Session 2",
        "speakers": ["Speaker 3", "Speaker 4"],
        "categories": ["Category 3", "Category 4"],
        "summary": "This is a summary of session 2.",
        "duration": "1 hour 30 minutes",
    },
]

@app.route('/sessions', methods=['GET'])
def get_sessions():
    return jsonify(sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
    session = next((session for session in sessions if session['id'] == id), None)
    if session is None:
        return jsonify({}), 404
    return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
    sessions_by_category = [session for session in sessions if category in session['categories']]
    return jsonify(sessions_by_category)

if __name__ == '__main__':
    app.run()

이전 단계의 일부로 생성된 기존 app.py 파일이 있습니다. 파일의 내용을 Code Assist에서 생성된 코드로 바꾸고 파일을 저장하기만 하면 됩니다.

포트 8080, 호스트 주소 0.0.0.0을 사용하고 로컬 실행 중에 디버그 모드에서도 실행되도록 app.run() 줄을 변경하려고 합니다.방법은 다음과 같습니다. 먼저 선을 강조 표시하거나 선택해 보겠습니다.

app.run()

그런 다음 Code Assist 채팅 인터페이스에 프롬프트를 입력합니다. Explain this.

그러면 특정 행에 대한 자세한 설명이 표시됩니다. 아래 예를 참고하세요.

58ec896a32a4fb68.png

이제 다음 프롬프트를 사용합니다.

update the code to run the application on port 8080, host address 0.0.0.0, and in debug mode

생성된 추천 코드는 다음과 같습니다.

app.run(host='0.0.0.0', port=8080, debug=True)

이 스니펫으로 app.py 파일을 업데이트해야 합니다.

로컬에서 애플리케이션 실행

이제 애플리케이션을 로컬에서 실행하여 시작에 따라 애플리케이션 요구사항을 확인해 보겠습니다.

첫 번째 단계는 가상 환경에 설치할 requirements.txt의 Python 패키지 종속 항목으로 가상 Python 환경을 만드는 것입니다. 이렇게 하려면 Cloud Shell IDE에서 명령어 팔레트 (Ctrl+Shift+P)로 이동하여 Python 환경 만들기를 입력합니다. 다음에 나오는 몇 단계에 따라 가상 환경 (venv), Python 3.x 인터프리터, requirements.txt 파일을 선택합니다.

환경이 생성되면 새 터미널 창 (Ctrl+Shift+`)을 실행하고 다음 명령어를 제공합니다.

python app.py

샘플 실행은 다음과 같습니다.

(.venv) romin@cloudshell: $ python app.py 
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080
 * Running on http://10.88.0.3:8080
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 132-247-368

이제 다음 URL에서 API를 미리 볼 수 있습니다. 여기서는 개발 서버가 포트 8080에서 실행 중이라고 가정합니다. 그렇지 않은 경우 적절한 포트 번호로 변경하세요.

  • https://<host-name>:8080/sessions
  • https://<host-name>:8080/sessions/{id}
  • https://<host-name>:8080/sessions/categories/{category}

아래 단계에 따라 이러한 URL(app.py 파일에 포함된 JSON 데이터)을 사용하여 검색할 수 있는지 확인하세요.

새 터미널 창을 열고 다음 명령어 중 하나를 시도합니다.

curl -X GET http://127.0.0.1:8080/sessions
curl -X GET http://127.0.0.1:8080/sessions/<ID>
curl -X GET http://127.0.0.1:8080/sessions/categories/<CATEGORY_NAME> 

8. 코드 리팩터링

app.py에 하드 코딩된 샘플 JSON 데이터를 포함하는 대신 코드와 데이터를 명확하게 구분할 수 있도록 이를 분리/추출하는 것이 좋습니다. 시작해 봅시다!

app.py 파일을 열어 두고 다음 프롬프트를 입력합니다.

Can I improve this code and separate out the sessions data from this app.py file?

이렇게 하는 방법에 관한 몇 가지 제안사항이 제공됩니다. Google이 얻은 샘플 제안은 다음과 같습니다.

9b9c56cb527dac4c.png

이를 따라 Code Assist에서 권장하는 대로 데이터를 sessions.py 파일로 분리해 보겠습니다.

이름이 sessions.py인 새 파일을 만듭니다.

, 생성된 데이터에 따른 콘텐츠는 JSON 목록입니다.

sessions = [
   {
       "id": "1",
       "title": "Session 1",
       "speakers": ["Speaker 1", "Speaker 2"],
       "categories": ["Category 1", "Category 2"],
       "summary": "This is a summary of session 1.",
       "duration": "1 hour",
   },
   {
       "id": "2",
       "title": "Session 2",
       "speakers": ["Speaker 3", "Speaker 4"],
       "categories": ["Category 3", "Category 4"],
       "summary": "This is a summary of session 2.",
       "duration": "1 hour 30 minutes",
   },
]

이제 app.py 파일이 훨씬 단순화되었으며 아래와 같습니다.

from flask import Flask, jsonify, request
from sessions import sessions

app = Flask(__name__)

@app.route('/sessions', methods=['GET'])
def get_sessions():
   return jsonify(sessions.sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
   session = next((session for session in sessions.sessions if session['id'] == id), None)
   if session is None:
       return jsonify({}), 404
   return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   return jsonify(sessions_by_category)

if __name__ == '__main__':
   app.run(host='0.0.0.0', port=8080, debug=True)

여전히 애플리케이션을 로컬에서 실행할 수 있는지 확인하고 이렇게 리팩터링된 변경사항으로 API를 실행해 봅니다. Python 개발 서버가 아직 실행 중일 가능성이 높으므로 이전 단계의 curl 명령어만 회수하면 됩니다.

9. Firestore 컬렉션과 통합

다음 단계는 세션에 있는 로컬 인메모리 JSON 목록에서 벗어나 애플리케이션을 이 Codelab의 시작 부분에서 만든 Firestore 데이터베이스의 sessions 컬렉션에 연결하는 것입니다.

sessions.py 파일을 열어 두고 다음 프롬프트를 입력합니다.

Can you further refactor the sessions.py code to read from a Firestore database that has a collection named sessions. The collection has the same attributes as the session object that we have defined. Use the Python module google-cloud-firestore. 

Firestore 컬렉션의 모든 세션을 읽으라는 다음과 같은 추천이 수신되었습니다.

import google.cloud.firestore

# Create a Firestore client
db = google.cloud.firestore.Client()

# Get the sessions collection
sessions_collection = db.collection("sessions")

# Create a list of sessions
sessions = []

# Iterate over the documents and add them to the list
for doc in sessions_collection.stream():
   session = doc.to_dict()
   session["id"] = doc.id
   sessions.append(session)

계속해서 sessions.py에 코드를 통합합니다.

Flask 개발 서버를 로컬에서 실행 중인 경우 Python 모듈을 찾을 수 없다는 불만사항으로 애플리케이션이 종료되었을 수 있습니다.

예를 들어 requirements.txt 파일에 추가해야 하는 Python 모듈에 대해 다음과 같이 Code Assist에 문의할 수 있습니다.

Which Python package needs to be installed to make the firestore code work?

그러면 Python 모듈의 이름이 제공됩니다 (예: google-cloud-firestore). 이를 requirements.txt 파일에 추가합니다.

새로 추가된 모듈 (google-cloud-firestore)으로 Python 환경을 다시 만들어야 합니다. 이렇게 하려면 기존 터미널 창에 다음 명령어를 입력합니다.

pip install -r requirements.txt

애플리케이션을 다시 실행하고 (python app.py로 다시 시작) /sessions URL을 방문합니다. 이제 sessions 컬렉션에 추가한 샘플 문서가 표시됩니다.

975d05e6518f1a6a.png

이전 단계에서 설명한 것처럼 언제든지 다른 URI를 쿼리하여 특정 카테고리의 특정 세션이나 모든 세션을 검색할 수 있습니다.

10. 코드 설명

이제 Gemini Code Assist의 "Explain this" 기능을 사용하여 코드에 대해 알아보겠습니다. 언제든지 파일로 이동하거나 특정 코드 스니펫을 선택한 후 다음 프롬프트를 사용하여 Code Assist에 요청하세요. Explain this

연습 삼아 sessions.py 파일로 이동하여 Firestore 관련 코드를 강조 표시하고 이에 대한 코드 설명을 확인합니다. Python 코드뿐만 아니라 프로젝트의 다른 파일에서도 이 기능을 사용해 보세요.

11. 웹 애플리케이션 생성

이제 API를 생성하고 라이브 Firestore 컬렉션과 통합했으므로 애플리케이션의 웹 기반 프런트엔드를 생성해 보겠습니다. 현재 Google의 웹 프런트엔드는 기능을 최소한으로 유지하며, 즉 특정 카테고리에 속하는 세션을 검색할 수 있습니다. 이를 위한 API 경로, 즉 /sessions/categories/{category}가 있으므로 웹 애플리케이션에서 이를 호출하고 결과를 검색해야 합니다.

바로 알아보겠습니다. Code Assist에 다음 프롬프트를 표시합니다.

Generate a web application that allows me to search for sessions by category and uses the Flask application that we created. Please use basic HTML, CSS and JS. Embed all the Javascript and CSS code into a single HTML file only.

그러면 자바스크립트와 CSS가 삽입된 웹 애플리케이션 HTML이 생성됩니다. 또한 루트 또는 기본 URL을 방문하는 모든 사용자에게 홈페이지가 표시되도록 app.py 파일에 새 경로를 추가하라는 메시지가 표시됩니다. 이메일에 해당 정보가 나와 있지 않으면 질문하거나 아래의 스니펫을 사용하세요.

@app.route('/')
def index():
   return render_template('index.html')

index.html로 저장할 수 있지만 파일을 저장할 위치 (예: 어떤 폴더)가 궁금할 수 있습니다. Code Assist에 후속 질문을 할 수 있습니다.

Given that I am using the flask framework, where should I put the index.html file?

render_template 프레임워크를 사용한다는 명확한 정보를 제공하므로 index.html 파일을 templates 폴더 내에 배치해야 합니다. 이 Codelab을 시작할 때 Flask 기반 애플리케이션을 생성했으므로 이 폴더를 사용할 수 있습니다. 따라서 기존 index.html 파일이 있으며 파일의 콘텐츠를 여기에서 생성된 새 파일로 바꾸기만 하면 됩니다. 또한 Code Assist는 app.py 파일에서 render_template를 가져오도록 언급합니다.

웹 애플리케이션 코드를 index.html 파일에 저장하고 이 파일을 templates 폴더에 저장해야 합니다.

로컬에서 애플리케이션 실행

이제 애플리케이션을 로컬에서 실행하여 시작에 따라 애플리케이션 요구사항을 확인해 보겠습니다.

로컬 Flask 서버가 여전히 실행 중이며 오류가 표시되지 않는지 확인하세요. 오류가 발생하면 오류를 해결하고 서버를 시작합니다.

설정이 완료되면 애플리케이션의 홈 URL을 방문합니다. 아래와 같이 제공되는 index.html 파일이 표시됩니다.

8ca586acc4536879.png

데이터의 카테고리 중 하나를 입력으로 제공하고 (예: AI) Search 버튼을 클릭합니다. 그러면 AI 카테고리로 태그가 지정된 세션이 표시됩니다.

165faded790a6c.png

연습으로 각 세션의 길이, 카테고리, 발표자, 요약과 같은 추가 메타데이터를 표시할 수 있습니다.

12. 테스트 사례 생성

세션 API를 생성했으므로 이제 Gemini Code Assist를 사용하여 다양한 API 경로에 대한 단위 테스트 사례를 생성해 보겠습니다.

app.py 파일을 열어 두고 다음 프롬프트를 입력합니다.

Generate unit test cases for app routes. Use unittest module.

다음과 같은 응답을 받았습니다.

import unittest
from app import app

class FlaskAppTestCase(unittest.TestCase):

    def test_index(self):
        tester = app.test_client()
        response = tester.get('/')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions(self):
        tester = app.test_client()
        response = tester.get('/sessions')
        self.assertEqual(response.status_code, 200)

    def test_get_session_by_id(self):
        tester = app.test_client()
        response = tester.get('/sessions/1')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions_by_category(self):
        tester = app.test_client()
        response = tester.get('/sessions/categories/category1')
        self.assertEqual(response.status_code, 200)

if __name__ == '__main__':
    unittest.main()

위의 코드를 사용하여 tests.py라는 파일을 만듭니다.

테스트 사례 생성에 관한 참고사항

위와 다른 코드 목록이 표시될 수 있으며 이로 인해 테스트 사례를 실행하는 데 문제가 발생할 수 있습니다. 예를 들어, 일부 실행에서 다음과 같은 핵심 코드가 빠진 것을 확인했습니다.

from app import app

위의 코드는 테스트 사례를 호출할 기존 Flask 앱을 가져오는 데 필요합니다.

if __name__ == '__main__':

`unittest.main()`

위의 코드는 테스트 사례를 실행하는 데 필요합니다.

각 테스트 사례를 살펴보고 생성된 코드에서 assertEqual 및 기타 조건을 확인하여 작동하는지 확인하는 것이 좋습니다. 데이터가 Firestore 컬렉션의 외부에 있으므로 이 데이터에 액세스할 수 없고 더미 데이터를 사용할 수 있어 테스트가 실패할 수 있습니다. 따라서 테스트 사례를 적절하게 수정하거나 즉시 필요하지 않을 수 있는 일부 테스트 사례를 주석 처리합니다.

시연을 위해 다음 명령어를 사용하여 테스트 사례를 실행했습니다. 로컬 API 엔드포인트가 호출되므로 로컬 개발 서버를 실행해야 합니다.

python tests.py

다음과 같은 요약 결과를 얻었습니다.

Ran 4 tests in 0.274s

FAILED (failures=2)

세 번째 테스트에서 세션 ID가 정확하지 않았고 category1(이)라는 카테고리가 없는 것이 맞습니다.

.

따라서 테스트 사례를 적절하게 조정하고 테스트합니다.

13 테스트 기반 개발

이제 테스트 기반 개발 (TDD) 방법론에 따라 세션 API에 새로운 검색 방법을 추가하는 방법을 살펴보겠습니다. 테스트 사례를 먼저 작성하여 구현 부족으로 테스트 사례가 실패하게 하고, Gemini Code Assist를 사용하여 누락된 구현을 생성하여 테스트를 통과하는 것입니다.

test.py 파일로 이동합니다 (모든 테스트를 통과하도록 tests.py 파일을 수정했다고 가정). Code Assist에 다음 프롬프트로 질문합니다.

Generate a new test case to search for sessions by speaker

이를 통해 다음과 같이 tests.py 파일에 적절하게 삽입한 테스트 사례 구현을 사용할 수 있습니다.

  def test_get_sessions_by_speaker(self):
        tester = app.test_client()
        response = tester.get('/sessions/speakers/speaker1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json, [sessions.sessions[0], sessions.sessions[1]])

테스트를 실행하면 다음 오류가 표시됩니다.

$ python tests.py 
.F.
======================================================================
FAIL: test_get_sessions_by_speaker (__main__.FlaskAppTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/romin/hello-world-5/tests.py", line 21, in test_get_sessions_by_speaker
    self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200

----------------------------------------------------------------------
Ran 3 tests in 0.010s

FAILED (failures=1)

이는 테스트 사례에서 (/sessions/speakers/) 경로를 호출했으며 app.py에 이 경로가 구현되지 않았기 때문입니다.

Code Assist에 구현을 요청하세요. app.py 파일로 이동하여 Code Assist에 다음 프롬프트를 입력합니다.

Add a new route to search for sessions by a specific speaker

Code Assist에서 제안한 다음 구현을 app.py 파일에 추가했습니다.

@app.route('/sessions/speakers/<speaker>', methods=['GET'])
def get_sessions_by_speaker(speaker):
    sessions_by_speaker = [session for session in sessions.sessions if speaker in session['speakers']]
    return jsonify(sessions_by_speaker)

tests.py 파일을 다시 살펴보면서 빠르게 점검하기 위해 테스트 사례를 다음과 같이 수정했습니다.

   def test_get_sessions_by_speaker(self):
       tester = app.test_client()
       response = tester.get('/sessions/speakers/Romin Irani')
       self.assertEqual(response.status_code, 200)
       self.assertEqual(len(response.json), 1)

테스트는 잘 실행되었습니다. 생성된 테스트 사례를 살펴보고, Firestore에 있을 수 있는 데이터에 따라 약간 조정하고, Python 단위 테스트 사례에 적절한 assert* 메서드를 포함하도록 연습해 보겠습니다.

14. Google Cloud Run에 배포

개발 품질에 만족했으므로 이제 마지막 단계는 이 애플리케이션을 Google Cloud Run에 배포하는 것입니다. 하지만 좋은 조치를 위해 잊고 있는 것이 있는지 Code Assist에 물어봐야 할 수도 있습니다. app.py를 열고 다음 프롬프트를 제출합니다.

Is there something here I should change before I deploy to production?

디버깅 플래그를 off로 설정하는 것을 잊었기 때문에 좋은 질문입니다.

2f87ed3a811fb218.png

표시된 대로 디버깅을 사용 중지하고 계속해서 컨테이너를 빌드하지 않고도 소스에서 Cloud Run에 애플리케이션을 직접 배포하는 데 사용할 수 있는 gcloud 명령어에 대해 Gemini Code Assist에 문의하세요.

다음 프롬프트를 입력합니다.

I would like to deploy the application to Cloud Run directly from source. What is the gcloud command to do that?

위 프롬프트를 몇 가지 변형하여 시도해 보세요. 우리가 시도한 또 다른 방법은 다음과 같습니다.

I would like to deploy this application to Cloud Run. I don't want to build a container image locally but deploy directly from source to Cloud Run. What is the gcloud command for that?

다음 gcloud 명령어가 표시되어야 합니다.

gcloud run deploy sessions --source .

다음과 같은 혜택도 제공됩니다.

gcloud run deploy <service-name> --source . \
—-platform managed \
—-allow-unauthenticated

애플리케이션의 루트 폴더에서 위의 명령어를 실행합니다. region 권한을 요청하는 메시지가 표시되면 us-central1 아이콘을 선택하고 unauthenticated invocations 허용 여부를 묻는 메시지가 표시되면 Y 아이콘을 선택합니다. Artifact Registry, Cloud Build, Cloud Run과 같은 Google Cloud API를 사용 설정하고 Artifact Registry 저장소를 만들 수 있는 권한을 사용 설정하라는 메시지가 표시될 수도 있습니다. 권한을 부여하세요.

배포 프로세스를 완료하는 데 약 2분이 걸리므로 기다려 주시기 바랍니다.

배포가 완료되면 Cloud Run 서비스 URL이 표시됩니다. 해당 공개 URL을 방문하면 동일한 웹 애플리케이션이 성공적으로 배포되어 실행되는 것을 볼 수 있습니다.

c5322d0fd3e0f616.png

축하합니다. 잘하셨습니다.

15. (선택사항) Cloud Logging 사용

애플리케이션 로그가 Google Cloud 서비스 중 하나 (Cloud Logging)에 중앙 집중화되도록 애플리케이션에 로깅을 도입할 수 있습니다. 그런 다음 관측 가능성 Gemini 기능을 사용하여 로그 항목을 파악할 수도 있습니다.

이를 위해서는 먼저 Google Cloud의 기존 Python Cloud Logging 라이브러리를 사용하고 이를 로그 / 심각도 수준에 따라 정보, 경고 또는 오류 메시지를 로깅하는 데 사용해야 합니다.

먼저 Code Assist에 요청해 보겠습니다. 다음 프롬프트를 사용해 보세요.

How do I use the google-cloud-logging package in Python?

아래와 같이 이와 관련된 몇 가지 정보를 제공하는 응답이 표시됩니다.

2472e1ccaf8a217d.png

카테고리별로 세션을 검색하는 함수에 로깅 구문을 추가해 보겠습니다.

먼저 google-cloud-logging Python 패키지를 requirements.txt 파일에 추가합니다.

다음은 로깅을 구현하기 위해 코드를 통합한 방법을 보여주는 코드 스니펫입니다.

...
from google.cloud import logging
...
app = Flask(__name__)

# Create a logger
logger = logging.Client().logger('my-log')

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   logger.log_text(f"Fetching sessions with category {category}")
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   logger.log_text(f'Found {len(sessions_by_category)} sessions with category {category}')
   return jsonify(sessions_by_category)

# # Other App Routes

이전 섹션과 동일한 명령어를 사용하여 Cloud Run에 서비스를 다시 배포하고, 배포가 완료되면 /sessions/categories/<category> 엔드포인트에 대한 호출을 몇 번 실행합니다.

Cloud Console → Logs Explorer(으)로 이동

59e297577570695.png

...그리고 아래와 같이 이러한 로깅 구문으로 필터링할 수 있습니다.

914f1fb6cac30a89.png

로그 구문을 클릭하여 펼친 다음 Explain this log entry를 클릭하면 Gemini가 로그 항목을 설명합니다. Google Cloud를 위한 Gemini를 사용 설정하지 않은 경우 Cloud AI Companion API를 사용 설정하라는 메시지가 표시됩니다. 안내에 따라 진행하세요.

샘플 응답은 다음과 같습니다.

7fc9783910fa92cc.png

16. 축하합니다

수고하셨습니다. 애플리케이션을 처음부터 성공적으로 빌드하고 설계, 빌드, 테스트, 배포를 포함한 SDLC의 여러 측면에서 Gemini Code Assist를 사용하셨습니다.

다음 단계

다음 Codelab을 확인하세요.

참조 문서