< Lab >
Lab Overview
실습 개요
- Cloud Shell을 개발 환경으로 사용
- Datastore를 Quiz 애플리케이션에 통합
App Dev - Storing Application Data in Cloud Datastore: Java
앱 개발 - Cloud Datastore에 애플리케이션 데이터 저장: Java
- Google Cloud Shell 활성화
- GCP 콘솔의 오른쪽 상단 툴바에서 Cloud Shell 열기 버튼을 클릭
- 활성 계정 이름 나열
gcloud auth list
- 프로젝트 ID 나열
gcloud config list project
- Quiz 응용 프로그램 준비
- Cloud Shell에서 클래스의 저장소 소스 코드 복제
git clone --depth=1 https://github.com/GoogleCloudPlatform/training-data-analyst
- 작업 디렉토리에 대한 소프트 링크 생성
ln -s ~/training-data-analyst/courses/developingapps/v1.3/java/datastore ~/datastore
- 퀴즈 애플리케이션 구성 및 실행을 위해 샘플 파일이 포함된 디렉터리로 이동
cd ~/datastore/start
- GCLOUD_PROJECT GCP 프로젝트 ID를 참조하는 환경 변수 생성
export GCLOUD_PROJECT=$DEVSHELL_PROJECT_ID
- 애플리케이션 종속성 설치
mvn clean install
- 애플리케이션 실행
mvn spring-boot:run
- Quiz 애플리케이션 검토
- Cloud Shell에서 웹 미리 보기 > 포트 8080 에서 미리 보기를 클릭하여 퀴즈 응용 프로그램을 미리 봄
- Create Question 클릭
질문에 대한 텍스트 상자와 정답을 선택할 수 있는 라디오 버튼이 있는 양식
- Take Test 클릭
질문을 하지 않았으므로 공란
- 상단 탐색 모음에서 GCP 클릭
더미 제목과 더미 답변이 표시된 것을 확인할 수 있음
- 서버측 애플리케이션으로 돌아가려면 탐색 모음에서 Quite Interesting Quiz 클릭
- Quiz 응용 프로그램 코드 검사
- Cloud Shell 텍스트 편집기 실행
- 파일 브라우저 패널을 사용해 /training-data-analyst/courses/developingapps/v1.3/java/datastore/start 로 폴더 이동
- 파일 브라우저 패널을 사용해 src/main/java/com/google/training/appdev 로 폴더 이동
- Spring Boot 웹 애플리케이션 검토
QuizApplication.java 파일은 Spring Boot 애플리케이션에 대한 진입점
Question.java는 도메인 클래스로 질문 형식으로 제출된 질문 데이터와 퀴즈를 풀 때 표시되는 질문 가짐
QuestionsController.java는 양식을 표시하고 웹 응용 프로그램에서 퀴즈 작성자가 게시한 양식 데이터를 수집하는 처리기
- /training-data analyst/courses/developingapps/v1.3/java/datastore/start/src/main/resources/ 로 폴더 이동
templates에서 new_question.html 파일의 경우,
new_question.html 파일은 질문 만들기 (Create Question) 양식에 대한 템플릿을 포함
- start/src/main/java/com/google/training/appdv/api/QuizEndpoint.java 파일의 경우,
시험을 보는 학생에게 JSON 데이터를 보내는 핸들러가 포함되어 있음
- /services/gcp/datastore/QuestionService.java 파일의 경우,
Cloud Datastore에서 또는 Cloud Datastore에서 퀴즈 질문을 저장하고 로드하기 위해 Datastore 코드를 작성
웹 애플리케이션과 API는 이 클래스를 사용
- Cloud Datastore에 항목 추가
- 애플리케이션 중지 후, Cloud Datastore 프로비저닝을 위한 App Engine 애플리케이션 만들기
gcloud app create --region "us-central"
- Java Datastore 패키지 가져오기 및 사용
// QuestionService.java // (1) Editor에서 .../services/gcp/datastore/QuestionService.javaCloud Shell 파일을 연 후, package com.google.training.appdev.services.gcp.datastore; // TODO: Import the com.google.cloud.datastore.* package // (2) com.google.cloud.datastore.*패키지 에 대한 Java Datastore 패키지 가져오기 및 사용 작성 import com.google.cloud.datastore.*; import com.google.training.appdev.services.gcp.domain.Question; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.springframework.stereotype.Service; @Service public class QuestionService { // TODO: Create a Datastore client object, datastore // (3) 명명된 클라이언트 개체 Datastore를 선언하고 초기화 // The DatastoreOptions class has a getDefaultInstance() // static method. // Use the getService() method of the DatastoreOptions // object to get the Datastore client private Datastore datastore = DatastoreOptions.getDefaultInstance().getService();
- Cloud Datastore 항목을 만드는 코드 작성
// QuestionService.java // TODO: Declare a static final String named kind // (1) ENTITY_KIND값으로 초기화된 "Question"이라는 정적 최종 문자열을 선언 //The Datastore key is the equivalent of a primary key in a // relational database. // There are two main ways of writing a key: // 1. Specify the kind, and let Datastore generate a unique // numeric id // 2. Specify the kind and a unique string id private static final String ENTITY_KIND = "Question"; // TODO: Create a KeyFactory for Question entities // (2) 질문 엔티티에 대한 KeyFactory 생성 private final KeyFactory keyFactory = datastore.newKeyFactory().setKind(ENTITY_KIND); // The createQuestion(Question question) method // is passed a Question object using data from the form // Extract the form data and add it to Datastore // TODO: Modify return type to Key // (3) createQuestion(Question question) 메서드의 반환 유형을 Key로 수정 public Key createQuestion(Question question) { // TODO: Declare the entity key, // with a Datastore allocated id // (4) Datastore 클라이언트와 Key Factory를 사용하여 Question 엔티티에 할당된 ID로 키를 선언 Key key = datastore.allocateId(keyFactory.newKey()); // TODO: Declare the entity object, with the key and data // (5) questionEntity라는 엔티티를 선언하고 엔티티 빌더를 사용하여 초기화 // The entity's members are set using the Entity.Builder. // This has a set method for property names and values // Values are retrieved from the Domain object Entity questionEntity = Entity.newBuilder(key) .set(Question.QUIZ, question.getQuiz()) .set(Question.AUTHOR, question.getAuthor()) .set(Question.TITLE, question.getTitle()) .set(Question.ANSWER_ONE,question.getAnswerOne()) .set(Question.ANSWER_TWO, question.getAnswerTwo()) .set(Question.ANSWER_THREE,question.getAnswerThree()) .set(Question.ANSWER_FOUR, question.getAnswerFour()) .set(Question.CORRECT_ANSWER, question.getCorrectAnswer()) .build(); // TODO: Save the entity // (6) Datastore 클라이언트 개체 (put(questionEntity))를 사용하여 해당 메서드 datastore를 호출하여 항목을 저장 datastore.put(questionEntity); // TODO: Return the key // (7) 엔티티에 대한 키를 반환하도록 return 문을 수정 후 파일 저장 return key; }
- 애플리케이션 실행 및 Cloud Datastore 항목 만들기
- Open Terminal 아이콘을 클릭하고 애플리케이션을 실행
- Cloud Shell에서 웹 미리 보기 > 포트 8080 에서 미리 보기를 클릭하여 퀴즈 응용 프로그램을 미리 보기
- 질문 만들기 클릭 후 양식을 완성해 저장
- Datastore 콘솔창에서 새 질문이 추가된 것을 확인 가능
- 쿼리 클라우드 데이터 저장소
- Cloud Datastore 항목을 검색하는 코드 작성
// QuestionService.java // (1) Editor를 클릭한 후, 파일의 getAllQuestions(String quiz) 메소드로 이동하여 // .../services/gcp/datastore/QuestionService.java 기존 더미 질문에 대한 코드를 제거 // (2) getAllQuestions(String quiz) 메서드에서 쿼리 개체를 만들고 // Cloud Datastore에서 특정 퀴즈에 대한 질문 항목을 검색하는 쿼리로 초기화 public List<Question> getAllQuestions(String quiz){ // TODO: Create the query // The Query class has a static newEntityQueryBuilder() // method that allows you to specify the kind(s) of // entities to be retrieved. // The query can be customized to filter the Question // entities for one quiz. Query<Entity> query = Query.newEntityQueryBuilder() .setKind(ENTITY_KIND) .setFilter(StructuredQuery.PropertyFilter.eq( Question.QUIZ, quiz)) .build(); // TODO: Execute the query // (3) Datastore 클라이언트 개체의 datastore.run(query)메서드를 호출하고 결과를 entities이라는 항목 반복기에 할당 // The datastore.run(query) method returns an iterator // for entities Iterator<Entity> entities = datastore.run(query); // TODO: Return the transformed results // (4) Datastore 항목을 도메인 개체로 변환하는 buildQuestions(entities) 메서드를 사용하여 변환된 결과를 반환 // Use the buildQuestions(entities) method to convert // from Datastore entities to domain objects return buildQuestions(entities); } // TODO: Uncomment this block // (5) 클래스에 제공된 buildQuestions(...) 및 도우미 메서드 entityToQuestion(...)의 주석 처리를 // 제거하고 이 메서드를 사용하여 반복자를 질문 도메인 개체 목록에 매핑 private List<Question> buildQuestions(Iterator<Entity> entities){ List<Question> questions = new ArrayList<>(); entities.forEachRemaining(entity-> questions.add(entityToQuestion(entity))); return questions; } private Question entityToQuestion(Entity entity){ return new Question.Builder() .withQuiz(entity.getString(Question.QUIZ)) .withAuthor(entity.getString(Question.AUTHOR)) .withTitle(entity.getString(Question.TITLE)) .withAnswerOne(entity.getString(Question.ANSWER_ONE)) .withAnswerTwo(entity.getString(Question.ANSWER_TWO)) .withAnswerThree(entity.getString(Question.ANSWER_THREE)) .withAnswerFour(entity.getString(Question.ANSWER_FOUR)) .withCorrectAnswer(entity.getLong(Question.CORRECT_ANSWER)) .withId(entity.getKey().getId()) .build(); } }
전체 코드 )
// QuestionService.java
package com.google.training.appdev.services.gcp.datastore;
// TODO: Import the com.google.cloud.datastore.* package
import com.google.cloud.datastore.*;
// END TODO
import com.google.training.appdev.services.gcp.domain.Question;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class QuestionService {
// TODO: Create a Datastore client object, datastore
// The DatastoreOptions class has a getDefaultInstance()
// static method.
// Use the getService() method of the DatastoreOptions
// object to get the Datastore client
private Datastore datastore =
DatastoreOptions.getDefaultInstance().getService();
// END TODO
// TODO: Declare a static final String named kind
//The Datastore key is the equivalent of a primary key in a // relational database.
// There are two main ways of writing a key:
// 1. Specify the kind, and let Datastore generate a unique // numeric id
// 2. Specify the kind and a unique string id
private static final String ENTITY_KIND = "Question";
// END TODO
// TODO: Create a KeyFactory for Question entities
private final KeyFactory keyFactory =
datastore.newKeyFactory().setKind(ENTITY_KIND);
// END TODO
// The createQuestion(Question question) method
// is passed a Question object using data from the form
// Extract the form data and add it to Datastore
// TODO: Modify return type to Key
public Key createQuestion(Question question) {
// END TODO
// TODO: Declare the entity key,
// with a Datastore allocated id
Key key = datastore.allocateId(keyFactory.newKey());
// END TODO
// TODO: Declare the entity object, with the key and data
// The entity's members are set using the Entity.Builder.
// This has a set method for property names and values
// Values are retrieved from the Domain object
Entity questionEntity = Entity.newBuilder(key)
.set(Question.QUIZ, question.getQuiz())
.set(Question.AUTHOR, question.getAuthor())
.set(Question.TITLE, question.getTitle())
.set(Question.ANSWER_ONE,question.getAnswerOne())
.set(Question.ANSWER_TWO, question.getAnswerTwo())
.set(Question.ANSWER_THREE,question.getAnswerThree())
.set(Question.ANSWER_FOUR, question.getAnswerFour())
.set(Question.CORRECT_ANSWER,
question.getCorrectAnswer())
.build();
// END TODO
// TODO: Save the entity
datastore.put(questionEntity);
// END TODO
// TODO: Return the key
return key;
// END TODO
}
public List<Question> getAllQuestions(String quiz){
// TODO: Create the query
// The Query class has a static newEntityQueryBuilder()
// method that allows you to specify the kind(s) of
// entities to be retrieved.
// The query can be customized to filter the Question
// entities for one quiz.
Query<Entity> query = Query.newEntityQueryBuilder()
.setKind(ENTITY_KIND)
.setFilter(StructuredQuery.PropertyFilter.eq(
Question.QUIZ, quiz))
.build();
// END TODO
// TODO: Execute the query
// The datastore.run(query) method returns an iterator
// for entities
Iterator<Entity> entities = datastore.run(query);
// END TODO
// TODO: Return the transformed results
// Use the buildQuestions(entities) method to convert
// from Datastore entities to domain objects
return buildQuestions(entities);
// END TODO
}
// TODO: Uncomment this block
private List<Question> buildQuestions(Iterator<Entity> entities){
List<Question> questions = new ArrayList<>();
entities.forEachRemaining(entity-> questions.add(entityToQuestion(entity)));
return questions;
}
private Question entityToQuestion(Entity entity){
return new Question.Builder()
.withQuiz(entity.getString(Question.QUIZ))
.withAuthor(entity.getString(Question.AUTHOR))
.withTitle(entity.getString(Question.TITLE))
.withAnswerOne(entity.getString(Question.ANSWER_ONE))
.withAnswerTwo(entity.getString(Question.ANSWER_TWO))
.withAnswerThree(entity.getString(Question.ANSWER_THREE))
.withAnswerFour(entity.getString(Question.ANSWER_FOUR))
.withCorrectAnswer(entity.getLong(Question.CORRECT_ANSWER))
.withId(entity.getKey().getId())
.build();
}
}
- 애플리케이션 실행 및 Cloud Datastore 쿼리 테스트
- .../services/gcp/datastore/QuestionService.java 파일을 저장한 다음 Cloud Shell 로 돌아감
- Cloud Shell에서 터미널 열기 를 클릭하고 Ctrl + C 를 눌러 애플리케이션을 중지
- 응용 프로그램을 시작
- Cloud Shell에서 웹 미리 보기 > 포트 8080 에서 미리 보기를 클릭하여 퀴즈 응용 프로그램을 미리 봄
- 애플리케이션 URL 끝에 있는 쿼리 문자열을 /api/quizzes/gcp로 대체
https://8080-1a9ae155-0ec8-47f8-a012-dcb8177d771d.ql-asia-east1-xewj.cloudshell.dev/api/quizzes/gcp
- 애플리케이션 홈 페이지로 돌아가서 테스트 응시 를 클릭한 다음 GCP 를 클릭