1. 개요
이전 실습에서는 이미지 분석 서비스를 위해 Google Cloud Storage 트리거 Cloud 함수를 사용하고, 썸네일 서비스를 위해 Pub/Sub를 통해 GCS 트리거 Cloud Run 컨테이너를 사용하고, Cloud Run에서 이미지 가비지 수집기 서비스를 트리거하기 위해 Eventarc를 사용하는 이벤트 기반 버전의 Pic-a-daily 앱을 빌드했습니다. Cloud Scheduler로 트리거된 콜라주 서비스도 있었습니다.

이 실습에서는 조정된 버전의 앱을 만듭니다. 시스템을 통해 흐르는 다양한 유형의 이벤트 대신 Workflows를 사용하여 다음과 같이 서비스를 조정하고 호출합니다.

학습할 내용
- App Engine
- Cloud Firestore
- Cloud Functions
- Cloud Run
- 워크플로
2. 설정 및 요구사항
자습형 환경 설정
- Cloud 콘솔에 로그인하고 새 프로젝트를 만들거나 기존 프로젝트를 다시 사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.



모든 Google Cloud 프로젝트에서 고유한 이름인 프로젝트 ID를 기억하세요(위의 이름은 이미 사용되었으므로 사용할 수 없습니다). 이 ID는 나중에 이 Codelab에서 PROJECT_ID라고 부릅니다.
- 그런 후 Google Cloud 리소스를 사용할 수 있도록 Cloud Console에서 결제를 사용 설정해야 합니다.
이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 가이드를 마친 후 비용이 결제되지 않도록 리소스 종료 방법을 알려주는 '삭제' 섹션의 안내를 따르세요. Google Cloud 신규 사용자에게는 미화$300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.
Cloud Shell 시작
Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.
GCP 콘솔에서 오른쪽 상단 툴바의 Cloud Shell 아이콘을 클릭합니다.

환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 완료되면 다음과 같이 표시됩니다.

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

워크플로를 사용하여 정의한 순서로 일련의 서버리스 작업을 연결하는 서버리스 워크플로를 만들 수 있습니다. Google Cloud API, Cloud Functions 및 Cloud Run과 같은 서버리스 제품, 외부 API 호출의 장점을 결합하여 유연한 서버리스 애플리케이션을 만들 수 있습니다.
오케스트레이터에서 예상할 수 있듯이 Workflows를 사용하면 YAML/JSON 기반 워크플로 정의 언어로 비즈니스 로직의 흐름을 정의할 수 있으며, 이러한 흐름을 트리거하는 Workflows 실행 API 및 Workflows UI를 제공합니다.
다음과 같은 내장 및 구성 가능한 기능을 갖춘 단순한 오케스트레이터 이상의 기능을 제공합니다.
- 단계의 안정적인 실행을 위해 단계 간 유연한 재시도 및 오류 처리
- 글루 코드를 방지하기 위해 단계 간 JSON 파싱 및 변수 전달
- 결정을 위한 표현식 수식을 사용하면 조건부 단계 실행이 가능합니다.
- 모듈식 재사용 가능한 워크플로를 위한 하위 워크플로
- 외부 서비스 지원을 통해 Google Cloud를 넘어 서비스를 오케스트레이션할 수 있습니다.
- 안전한 단계 실행을 위한 Google Cloud 및 외부 서비스 인증 지원
- 간편한 통합을 위해 Pub/Sub, Firestore, Tasks, Secret Manager와 같은 Google Cloud 서비스에 대한 커넥터
Workflows는 완전 관리형 서버리스 제품입니다. 구성하거나 확장할 서버가 없으며 사용한 만큼만 지불하면 됩니다.
4. API 사용 설정
이 실습에서는 Cloud Functions 및 Cloud Run 서비스를 워크플로에 연결합니다. App Engine, Cloud Build, Vision API 및 기타 서비스도 사용합니다.
Cloud Shell에서 필요한 모든 서비스가 사용 설정되어 있는지 확인합니다.
gcloud services enable \ appengine.googleapis.com \ cloudbuild.googleapis.com \ cloudfunctions.googleapis.com \ compute.googleapis.com \ firestore.googleapis.com \ run.googleapis.com \ vision.googleapis.com \ workflows.googleapis.com \
잠시 후 작업이 성공적으로 완료됩니다.
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
5. 코드 가져오기
이전 코드 랩에서 아직 코드를 가져오지 않은 경우 코드를 가져옵니다.
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
이 실습과 관련된 폴더 구조는 다음과 같습니다.
frontend | workflows | ├── functions ├── |── trigger-workflow ├── |── vision-data-transform ├── services ├── |── collage ├── |── thumbnails ├── workflows.yaml
관련 폴더는 다음과 같습니다.
frontend에는 실습 4에서 재사용할 App Engine 프런트엔드가 포함되어 있습니다.functions에는 워크플로를 위해 생성된 Cloud 함수가 포함되어 있습니다.services에는 워크플로에 맞게 수정된 Cloud Run 서비스가 포함되어 있습니다.workflows.yaml은 워크플로 정의 파일입니다.
6. 워크플로 YAML 살펴보기
workflows.yaml은 일련의 단계로 워크플로를 정의합니다. 자세히 살펴보겠습니다.
워크플로 시작 시 전달되는 매개변수가 있습니다. 워크플로를 트리거하는 두 Cloud Functions에 의해 전달됩니다. 이러한 함수는 나중에 다루겠지만 워크플로는 다음과 같이 시작됩니다.

YAML에서 이러한 매개변수가 init 단계의 변수에 할당되는 것을 확인할 수 있습니다. 예를 들어 이벤트를 트리거하는 파일 및 버킷 이름과 워크플로가 호출할 일부 Cloud Functions 및 Cloud Run 서비스의 URL이 있습니다.
main:
params: [args]
steps:
- init:
assign:
- file: ${args.file}
- bucket: ${args.bucket}
- gsUri: ${"gs://" + bucket + "/" + file}
- projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
- urls: ${args.urls}
그런 다음 워크플로는 이벤트 유형을 확인합니다. 지원되는 이벤트 유형은 2가지입니다. object.finalize (파일이 클라우드 스토리지 버킷에 저장될 때 발생) 및 object.delete (파일이 삭제될 때 발생) 그 외의 경우에는 지원되지 않는 예외가 발생합니다.

다음은 YAML 워크플로 정의에서 파일 저장소 이벤트의 유형을 확인하는 단계입니다.
- eventTypeSwitch:
switch:
- condition: ${args.eventType == "google.storage.object.finalize"}
next: imageAnalysisCall
- condition: ${args.eventType == "google.storage.object.delete"}
next: pictureGarbageCollectionGCS
- eventTypeNotSupported:
raise: ${"eventType " + args.eventType + " is not supported"}
next: end
Workflows는 switch 명령어와 다양한 조건, 이벤트가 인식되지 않을 때 오류를 발생시키는 raise 명령어를 사용하여 switch 문과 예외 처리를 지원합니다.
다음으로 imageAnalysisCall를 살펴보겠습니다. 이는 워크플로에서 Vision API를 호출하여 이미지를 분석하고, Vision API 응답 데이터를 변환하여 사진에서 인식된 항목의 라벨을 정렬하고, 주요 색상을 선택하고, 이미지를 표시해도 안전한지 확인한 다음, 메타데이터를 Cloud Firestore에 저장하는 일련의 호출입니다.
Vision Transform Cloud Functions (나중에 배포할 예정)를 제외한 모든 작업은 워크플로에서 실행됩니다.

YAML에서 단계는 다음과 같이 표시됩니다.
- imageAnalysisCall:
call: http.post
args:
url: https://vision.googleapis.com/v1/images:annotate
headers:
Content-Type: application/json
auth:
type: OAuth2
body:
requests:
- image:
source:
gcsImageUri: ${gsUri}
features:
- type: LABEL_DETECTION
- type: SAFE_SEARCH_DETECTION
- type: IMAGE_PROPERTIES
result: imageAnalysisResponse
- transformImageAnalysisData:
call: http.post
args:
url: ${urls.VISION_DATA_TRANSFORM_URL}
auth:
type: OIDC
body: ${imageAnalysisResponse.body}
result: imageMetadata
- checkSafety:
switch:
- condition: ${imageMetadata.body.safe == true}
next: storeMetadata
next: end
- storeMetadata:
call: http.request
args:
url: ${"https://firestore.googleapis.com/v1/projects/" + projectId + "/databases/(default)/documents/pictures/" + file + "?updateMask.fieldPaths=color&updateMask.fieldPaths=labels&updateMask.fieldPaths=created"}
auth:
type: OAuth2
method: PATCH
body:
name: ${"projects/" + projectId + "/databases/(default)/documents/pictures/" + file}
fields:
color:
stringValue: ${imageMetadata.body.color}
created:
timestampValue: ${imageMetadata.body.created}
labels:
arrayValue:
values: ${imageMetadata.body.labels}
result: storeMetadataResponse
이미지가 분석되면 다음 두 단계는 이미지의 썸네일과 가장 최근 이미지의 콜라주를 만드는 것입니다. 이는 thumbnailCall 및 collageCall 단계에서 2개의 Cloud Run 서비스를 배포하고 이를 호출하여 실행됩니다.

YAML의 단계:
- thumbnailCall:
call: http.post
args:
url: ${urls.THUMBNAILS_URL}
auth:
type: OIDC
body:
gcsImageUri: ${gsUri}
result: thumbnailResponse
- collageCall:
call: http.get
args:
url: ${urls.COLLAGE_URL}
auth:
type: OIDC
result: collageResponse
이 실행 브랜치는 finalizeCompleted 단계에서 각 서비스의 상태 코드를 반환하여 종료됩니다.
- finalizeCompleted:
return:
imageAnalysis: ${imageAnalysisResponse.code}
storeMetadata: ${storeMetadataResponse.code}
thumbnail: ${thumbnailResponse.code}
collage: ${collageResponse.code}
실행의 다른 브랜치는 고해상도 버전의 사진이 포함된 기본 스토리지 버킷에서 파일이 삭제되는 경우입니다. 이 브랜치에서는 썸네일이 포함된 버킷에서 이미지의 썸네일을 삭제하고 Firestore에서 메타데이터를 삭제하려고 합니다. 이 두 가지 모두 Workflows의 HTTP 호출을 통해 실행됩니다.

YAML의 단계:
- pictureGarbageCollectionGCS:
try:
call: http.request
args:
url: ${"https://storage.googleapis.com/storage/v1/b/thumbnails-" + projectId + "/o/" + file}
auth:
type: OAuth2
method: DELETE
result: gcsDeletionResult
except:
as: e
steps:
- dummyResultInOutVar:
assign:
- gcsDeletionResult:
code: 200
body: "Workaround for empty body response"
- pictureGarbageCollectionFirestore:
call: http.request
args:
url: ${"https://firestore.googleapis.com/v1/projects/" + projectId + "/databases/(default)/documents/pictures/" + file}
auth:
type: OAuth2
method: DELETE
result: firestoreDeletionResult
삭제 브랜치는 각 단계에서 결과 / 코드를 반환하여 종료됩니다.
- deleteCompleted:
return:
gcsDeletion: ${gcsDeletionResult}
firestoreDeletion: ${firestoreDeletionResult.code}
다음 단계에서는 워크플로의 모든 외부 종속 항목(버킷, Cloud 함수, Cloud Run 서비스, Firestore 데이터베이스)을 만듭니다.
7. 버킷 만들기
이미지용 버킷이 2개 필요합니다. 하나는 원본 고해상도 이미지를 저장하고 다른 하나는 이미지 썸네일을 저장합니다.
gsutil 도구를 사용하여 사용자가 사진을 업로드할 수 있는 균일한 액세스 권한이 있는 공개 리전 (이 경우 유럽) 버킷을 만듭니다.
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
gsutil mb -l EU gs://${BUCKET_PICTURES}
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
썸네일용 공개 리전 버킷을 하나 더 만듭니다.
export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
gsutil mb -l EU gs://${BUCKET_THUMBNAILS}
gsutil uniformbucketlevelaccess set on gs://${BUCKET_THUMBNAILS}
gsutil iam ch allUsers:objectViewer gs://${BUCKET_THUMBNAILS}
Cloud Console의 Cloud Storage 섹션에서 버킷이 생성되었고 공개 상태인지 다시 한번 확인할 수 있습니다.

8. Vision 데이터 변환 (Cloud Functions)
Workflows.yaml은 init, eventTypeSwitch, eventTypeNotSupported 단계로 시작합니다. 이렇게 하면 버킷에서 발생하는 이벤트가 올바른 단계로 라우팅됩니다.
object.finalize 이벤트의 경우 imageAnalysisCall 단계에서 Vision API를 호출하여 생성된 이미지의 메타데이터를 추출합니다. 다음 단계는 모두 워크플로 내에서 실행됩니다.

그런 다음 Firestore에 저장하기 전에 Vision API에서 반환된 데이터를 변환해야 합니다. 구체적으로 다음을 수행해야 합니다.
- 이미지에 대해 반환된 라벨을 나열합니다.
- 이미지의 주요 색상을 가져옵니다.
- 사진이 안전한지 확인합니다.
이는 Cloud 함수의 코드에서 실행되며 Workflows는 이 함수를 호출하기만 합니다.

코드 살펴보기
Cloud 함수는 vision-data-transform로 호출됩니다. index.js에서 전체 코드를 확인할 수 있습니다. 보시다시피 이 함수의 유일한 목적은 Firestore에 사진 메타데이터를 편리하게 저장할 수 있도록 JSON을 JSON으로 변환하는 것입니다.
Cloud Functions에 배포
폴더로 이동합니다.
cd workflows/functions/vision-data-transform/nodejs
원하는 리전을 설정합니다.
export REGION=europe-west1
gcloud config set functions/region ${REGION}
다음을 사용하여 함수를 배포합니다.
export SERVICE_NAME=vision-data-transform
gcloud functions deploy ${SERVICE_NAME} \
--source=. \
--runtime nodejs10 \
--entry-point=vision_data_transform \
--trigger-http \
--allow-unauthenticated
함수가 배포되면 Workflows transformImageAnalysisData 단계에서 이 함수를 호출하여 Vision API 데이터 변환을 실행할 수 있습니다.
9. 데이터베이스 준비
다음으로 워크플로에서 이미지 데이터의 안전성을 확인한 다음 Vision API에서 반환된 사진에 관한 정보를 빠른 속도의 완전 관리형 서버리스 클라우드 기반 NoSQL 문서 데이터베이스인 Cloud Firestore 데이터베이스에 저장합니다.

이 두 가지 모두 워크플로에서 실행되지만 메타데이터를 저장할 Firestore 데이터베이스를 만들어야 작동합니다.
먼저 Firestore 데이터베이스를 원하는 리전에서 App Engine 앱을 만듭니다 (Firestore의 요구사항).
export REGION_FIRESTORE=europe-west2
gcloud app create --region=${REGION_FIRESTORE}
다음으로 동일한 리전에 Firestore 데이터베이스를 만듭니다.
gcloud firestore databases create --region=${REGION_FIRESTORE}
문서는 컬렉션에 프로그래매틱 방식으로 생성되며 다음 4개의 필드를 포함합니다.
- name (문자열): 업로드된 사진의 파일 이름입니다. 문서의 키이기도 합니다.
- labels (문자열 배열): Vision API에서 인식한 항목의 라벨
- color (문자열): 주요 색상의 16진수 색상 코드입니다 (예: #ab12ef)
- created (날짜): 이 이미지의 메타데이터가 저장된 타임스탬프
- thumbnail (불리언): 이 사진의 썸네일 이미지가 생성된 경우 표시되고 true가 되는 선택적 필드입니다.
Firestore에서 썸네일이 있는 사진을 검색하고 생성 날짜를 기준으로 정렬할 것이므로 검색 색인을 만들어야 합니다. 다음 명령어를 사용하여 색인을 만들 수 있습니다.
gcloud firestore indexes composite create --collection-group=pictures \ --field-config field-path=thumbnail,order=descending \ --field-config field-path=created,order=descending
색인을 만드는 데 최대 10분 정도 걸릴 수 있습니다.
색인이 생성되면 Cloud Console에서 확인할 수 있습니다.

이제 워크플로 storeMetadata 단계에서 이미지 메타데이터를 Firestore에 저장할 수 있습니다.
10. 썸네일 서비스 (Cloud Run)
다음은 이미지의 썸네일을 만드는 것입니다. 이는 Cloud Run 서비스의 코드에서 실행되며 워크플로는 thumbnailCall 단계에서 이 서비스를 호출합니다.

코드 살펴보기
Cloud Run 서비스의 이름은 thumbnails입니다. index.js에서 전체 코드를 확인할 수 있습니다.
컨테이너 이미지 빌드 및 게시
Cloud Run은 컨테이너를 실행하지만 먼저 컨테이너 이미지 (Dockerfile에 정의됨)를 빌드해야 합니다. Google Cloud Build를 사용하여 컨테이너 이미지를 빌드한 다음 Google Container Registry에 호스팅할 수 있습니다.
폴더로 이동합니다.
cd workflows/services/thumbnails/nodejs
빌드:
export SERVICE_SRC=thumbnails
export SERVICE_NAME=${SERVICE_SRC}-service
gcloud builds submit \
. \
--tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}
1~2분 후에 빌드가 성공하고 컨테이너가 Google Container Registry에 배포됩니다.
Cloud Run에 배포
필요한 변수와 구성을 설정합니다.
export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
gcloud config set run/region ${REGION}
gcloud config set run/platform managed
다음 명령어를 사용하여 배포합니다.
gcloud run deploy ${SERVICE_NAME} \
--image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
--no-allow-unauthenticated \
--memory=1Gi \
--update-env-vars BUCKET_THUMBNAILS=${BUCKET_THUMBNAILS}
서비스가 배포되면 워크플로 thumbnailCall 단계에서 이 서비스를 호출할 수 있습니다.
11. 콜라주 서비스 (Cloud Run)
다음 단계는 가장 최근 이미지로 콜라주를 만드는 것입니다. 이는 Cloud Run 서비스의 코드에서 실행되며 워크플로는 collageCall 단계에서 이 서비스를 호출합니다.

코드 살펴보기
Cloud Run 서비스의 이름은 collage입니다. index.js에서 전체 코드를 확인할 수 있습니다.
컨테이너 이미지 빌드 및 게시
Cloud Run은 컨테이너를 실행하지만 먼저 컨테이너 이미지 (Dockerfile에 정의됨)를 빌드해야 합니다. Google Cloud Build를 사용하여 컨테이너 이미지를 빌드한 다음 Google Container Registry에 호스팅할 수 있습니다.
폴더로 이동합니다.
cd services/collage/nodejs
빌드:
export SERVICE_SRC=collage
export SERVICE_NAME=${SERVICE_SRC}-service
gcloud builds submit \
. \
--tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}
1~2분 후에 빌드가 성공하고 컨테이너가 Google Container Registry에 배포됩니다.
Cloud Run에 배포
필요한 변수와 구성을 설정합니다.
export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
gcloud config set run/region ${REGION}
gcloud config set run/platform managed
배포:
gcloud run deploy ${SERVICE_NAME} \
--image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
--no-allow-unauthenticated \
--memory=1Gi \
--update-env-vars BUCKET_THUMBNAILS=${BUCKET_THUMBNAILS}
서비스가 배포되면 Cloud Console의 Cloud Run 섹션에서 두 서비스가 모두 실행 중인지 확인할 수 있으며 워크플로 collageCall 단계에서 이 서비스를 호출할 수 있습니다.

12. 워크플로 배포
워크플로의 모든 외부 종속 항목이 배포되었습니다. 나머지 단계 (finalizeCompleted, pictureGarbageCollectionGCS, pictureGarbageCollectionFirestore, deleteCompleted)는 Workflows 자체에서 완료할 수 있습니다.
이제 워크플로를 배포할 시간입니다.
workflows.yaml 파일이 포함된 폴더로 이동하여 다음 명령어로 배포합니다.
export WORKFLOW_REGION=europe-west4
export WORKFLOW_NAME=picadaily-workflows
gcloud workflows deploy ${WORKFLOW_NAME} \
--source=workflows.yaml \
--location=${WORKFLOW_REGION}
몇 초 후 워크플로가 배포되고 Cloud Console의 워크플로 섹션에 표시됩니다.

원하는 경우 워크플로를 클릭하여 수정할 수 있습니다. 수정하는 동안 워크플로가 시각적으로 잘 표현됩니다.

올바른 매개변수를 사용하여 Cloud 콘솔에서 워크플로를 수동으로 실행할 수도 있습니다. 대신 다음 단계에서 Cloud Storage 이벤트에 응답하여 자동으로 실행합니다.
13. Workflows 트리거 (Cloud Functions)
워크플로가 배포되어 준비되었습니다. 이제 Cloud Storage 버킷에서 파일이 생성되거나 삭제될 때 워크플로를 트리거해야 합니다. 각각 storage.object.finalize 및 storage.object.delete 이벤트입니다.
워크플로에는 워크플로를 만들고, 관리하고, 실행하는 데 사용할 수 있는 API 및 클라이언트 라이브러리가 있습니다. 이 경우 Workflows Execution API, 더 구체적으로는 Node.js 클라이언트 라이브러리를 사용하여 워크플로를 트리거합니다.
Cloud Storage 이벤트를 수신 대기하는 Cloud 함수에서 워크플로를 트리거합니다. Cloud 함수는 하나의 이벤트 유형만 리슨할 수 있으므로 생성 및 삭제 이벤트를 모두 리슨하려면 두 개의 Cloud 함수를 배포해야 합니다.

코드 살펴보기
Cloud 함수는 trigger-workflow로 호출됩니다. index.js에서 전체 코드를 확인할 수 있습니다.
Cloud Functions에 배포
폴더로 이동합니다.
cd workflows/functions/trigger-workflow/nodejs
필요한 변수와 구성을 설정합니다.
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
export WORKFLOW_NAME=picadaily-workflows
export WORKFLOW_REGION=europe-west4
export COLLAGE_URL=$(gcloud run services describe collage-service --format 'value(status.url)')
export THUMBNAILS_URL=$(gcloud run services describe thumbnails-service --format 'value(status.url)')
export VISION_DATA_TRANSFORM_URL=$(gcloud functions describe vision-data-transform --format 'value(httpsTrigger.url)')
gcloud config set functions/region ${REGION}
완료 이벤트에 응답하는 함수를 배포합니다.
export SERVICE_NAME=trigger-workflow-on-finalize
gcloud functions deploy ${SERVICE_NAME} \
--source=. \
--runtime nodejs10 \
--entry-point=trigger_workflow \
--trigger-resource=${BUCKET_PICTURES} \
--trigger-event=google.storage.object.finalize \
--allow-unauthenticated \
--set-env-vars GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},WORKFLOW_REGION=${WORKFLOW_REGION},WORKFLOW_NAME=${WORKFLOW_NAME},THUMBNAILS_URL=${THUMBNAILS_URL},COLLAGE_URL=${COLLAGE_URL},VISION_DATA_TRANSFORM_URL=${VISION_DATA_TRANSFORM_URL}
삭제 이벤트에 응답하는 두 번째 함수를 배포합니다.
export SERVICE_NAME=trigger-workflow-on-delete
gcloud functions deploy ${SERVICE_NAME} \
--source=. \
--runtime nodejs10 \
--entry-point=trigger_workflow \
--trigger-resource=${BUCKET_PICTURES} \
--trigger-event=google.storage.object.delete \
--allow-unauthenticated \
--set-env-vars GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},WORKFLOW_REGION=${WORKFLOW_REGION},WORKFLOW_NAME=${WORKFLOW_NAME},THUMBNAILS_URL=${THUMBNAILS_URL},COLLAGE_URL=${COLLAGE_URL},VISION_DATA_TRANSFORM_URL=${VISION_DATA_TRANSFORM_URL}
배포가 완료되면 Cloud Console에 두 함수가 모두 표시됩니다.

14. 프런트엔드 (App Engine)
이 단계에서는 사용자가 웹 애플리케이션에서 사진을 업로드하고 업로드된 사진과 미리보기 이미지를 탐색할 수 있는 Pic-a-daily: 실습 4 - 웹 프런트엔드 만들기의 Google App Engine에 웹 프런트엔드를 만듭니다.

App Engine에 대해 자세히 알아보고 Pic-a-daily: 실습 4—웹 프런트엔드 만들기에서 코드 설명을 확인할 수 있습니다.
코드 살펴보기
App Engine 앱의 이름은 frontend입니다. index.js에서 전체 코드를 확인할 수 있습니다.
App Engine에 배포
폴더로 이동합니다.
cd frontend
원하는 리전을 설정하고 app.yaml의 GOOGLE_CLOUD_PROJECT를 실제 프로젝트 ID로 바꿉니다.
export REGION=europe-west1
gcloud config set compute/region ${REGION}
sed -i -e "s/GOOGLE_CLOUD_PROJECT/${GOOGLE_CLOUD_PROJECT}/" app.yaml
배포:
gcloud app deploy app.yaml -q
1~2분 후 애플리케이션이 트래픽을 처리하고 있다는 메시지가 표시됩니다.
Beginning deployment of service [default]... ╔════════════════════════════════════════════════════════════╗ ╠═ Uploading 8 files to Google Cloud Storage ═╣ ╚════════════════════════════════════════════════════════════╝ File upload done. Updating service [default]...done. Setting traffic split for service [default]...done. Deployed service [default] to [https://GOOGLE_CLOUD_PROJECT.appspot.com] You can stream logs from the command line by running: $ gcloud app logs tail -s default To view your application in the web browser run: $ gcloud app browse
Cloud Console의 App Engine 섹션을 방문하여 앱이 배포되었는지 확인하고 버전 관리 및 트래픽 분할과 같은 App Engine 기능을 살펴볼 수도 있습니다.

15. 워크플로 테스트
테스트하려면 앱의 기본 App Engine URL (https://<YOUR_PROJECT_ID>.appspot.com/)로 이동하세요. 프런트엔드 UI가 실행 중인 것을 확인할 수 있습니다.

사진을 업로드합니다. 이렇게 하면 워크플로가 트리거되고 Cloud Console에서 워크플로 실행이 Active 상태로 표시됩니다.

워크플로가 완료되면 실행 ID를 클릭하여 다양한 서비스의 출력을 확인할 수 있습니다.

사진 3장을 더 업로드합니다. Cloud Storage 버킷과 App Engine 프런트엔드에 있는 이미지의 썸네일과 콜라주도 업데이트됩니다.

16. 정리(선택 사항)
앱을 유지하지 않으려면 전체 프로젝트를 삭제하여 비용을 절감하고 전반적으로 우수한 클라우드 시민이 될 수 있습니다.
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
17. 축하합니다.
Workflows를 사용하여 서비스를 조정하고 호출하는 조정된 버전의 앱을 만들었습니다.
학습한 내용
- App Engine
- Cloud Firestore
- Cloud Functions
- Cloud Run
- 워크플로