Cloud Spanner를 사용한 Spring Boot 애플리케이션

1. 개요

Cloud Spanner는 가용성이 높고 수평적으로 확장 가능한 멀티 리전 RDBMS입니다. 이 Codelab에서는 가장 작은 Cloud Spanner 인스턴스를 사용하지만 작업이 끝나면 종료하는 것을 잊지 마세요.

학습 내용

  • Spring Boot에서 Cloud Spanner를 사용하여 데이터를 저장하고 검색하는 방법

필요한 항목

  • Google Cloud Platform 프로젝트
  • 브라우저(Chrome 또는 Firefox 등)

본 가이드를 어떻게 사용하실 계획인가요?

읽기만 할 계획입니다 읽은 다음 연습 활동을 완료할 계획입니다

귀하의 Google Cloud Platform 서비스 사용 경험을 평가해 주세요.

초급 중급 고급

2. 설정 및 요구사항

자습형 환경 설정

  1. Cloud Console에 로그인하고 새 프로젝트를 만들거나 기존 프로젝트를 다시 사용합니다. (Gmail 또는 G Suite 계정이 없으면 만들어야 합니다.)

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

모든 Google Cloud 프로젝트에서 고유한 이름인 프로젝트 ID를 기억하세요(위의 이름은 이미 사용되었으므로 사용할 수 없습니다). 이 ID는 나중에 이 Codelab에서 PROJECT_ID라고 부릅니다.

  1. 그런 후 Google Cloud 리소스를 사용할 수 있도록 Cloud Console에서 결제를 사용 설정해야 합니다.

이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 가이드를 마친 후 비용이 결제되지 않도록 리소스 종료 방법을 알려주는 '삭제' 섹션의 안내를 따르세요. Google Cloud 새 사용자에게는 미화 $300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화H7JlbhKGHITmsxhQIcLwoe5HXZMhDlYue4K-SPszMxUxDjIeWfOHBfxDHYpmLQTzUmQ7Xx8o6OJUlANnQF0iBuUyfp1RzVad_4nCa0Zz5LtwBlUZFXFCWFrmrWZLqg1MkZz2LdgUDQ를 클릭합니다.

zlNW0HehB_AFW1qZ4AyebSQUdWm95n7TbnOr7UVm3j9dFcg6oWApJRlC0jnU1Mvb-IQp-trP1Px8xKNwt6o3pP6fyih947sEhOFI4IRF0W7WZk6hFqZDUGXQQXrw21GuMm2ecHrbzQ

이전에 Cloud Shell을 시작하지 않았으면 설명이 포함된 중간 화면(스크롤해야 볼 수 있는 부분)이 제공됩니다. 이 경우 계속을 클릭합니다(이후 다시 표시되지 않음). 이 일회성 화면은 다음과 같습니다.

kEPbNAo_w5C_pi9QvhFwWwky1cX8hr_xEMGWySNIoMCdi-Djx9AQRqWn-__DmEpC7vKgUtl-feTcv-wBxJ8NwzzAp7mY65-fi2LJo4twUoewT1SUjd6Y3h81RG3rKIkqhoVlFR-G7w

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

pTv5mEKzWMWp5VBrg2eGcuRPv9dLInPToS-mohlrqDASyYGWnZ_SwE-MzOWHe76ZdCSmw0kgWogSJv27lrQE8pvA5OD6P1I47nz8vrAdK7yR1NseZKJvcxAZrPb8wRxoqyTpD-gbhA

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

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`
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

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

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

3. Cloud Spanner 초기화

gcloud CLI를 사용하여 Cloud Spanner API를 사용 설정합니다.

gcloud services enable spanner.googleapis.com

Cloud Spanner 인스턴스를 만듭니다.

gcloud spanner instances create spanner-instance \
 --config=regional-us-central1 \
 --nodes=1 --description="A Spanner Instance"

인스턴스 내에 데이터베이스를 만듭니다.

gcloud spanner databases create orders \
  --instance=spanner-instance

데이터 스키마를 설명하는 schema.ddl 파일을 만듭니다.

cat << EOF > schema.ddl
CREATE TABLE orders (
  order_id STRING(36) NOT NULL,
  description STRING(255),
  creation_timestamp TIMESTAMP,
) PRIMARY KEY (order_id);

CREATE TABLE order_items (
  order_id STRING(36) NOT NULL,
  order_item_id STRING(36) NOT NULL,
  description STRING(255),
  quantity INT64,
) PRIMARY KEY (order_id, order_item_id),
  INTERLEAVE IN PARENT orders ON DELETE CASCADE;
EOF

Cloud Spanner 데이터베이스에 스키마를 적용합니다.

gcloud spanner databases ddl update orders \
  --instance=spanner-instance \
  --ddl="$(<schema.ddl)"

4. 새 Spring Boot Java 애플리케이션 부트스트랩

Cloud Shell 환경에서 다음 명령어를 사용하여 새 Spring Boot 애플리케이션을 초기화하고 부트스트랩합니다.

$ curl https://start.spring.io/starter.tgz \
  -d packaging=jar \
  -d dependencies=cloud-gcp,web,lombok \
  -d baseDir=spanner-example \
  -d type=maven-project \
  -d bootVersion=3.2.6 | tar -xzvf -

$ cd spanner-example

이렇게 하면 새 Maven 프로젝트와 Maven의 pom.xml, Maven 래퍼, 애플리케이션 진입점이 있는 새 spanner-example/ 디렉터리가 생성됩니다.

pom.xml 파일에서 Spring Data Cloud Spanner 시작 조건을 추가합니다.

spanner-example/pom.xml

<project>
  ...
  <dependencies>
        ...
        <!-- Add Spring Cloud GCP Spanner Starter -->
        <dependency>
                <groupId>com.google.cloud</groupId>
                <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
        </dependency>

        ...

  </dependencies>

  ...
</project>

application.properties에서 Spanner 데이터베이스 연결 정보를 구성합니다.

spanner-example/src/main/resources/application.properties

spring.cloud.gcp.spanner.instance-id=spanner-instance
spring.cloud.gcp.spanner.database=orders

JAVA_HOME이 올바른 버전으로 설정되어 있는지 확인합니다.

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/

앱을 다시 빌드하여 Maven 구성이 올바른지 확인합니다.

./mvnw package

5. 항목 만들기

Spring Cloud GCP의 Spring Data Spanner 지원으로 Spring Data를 통해 쉽게 Java 객체를 만들고 Spanner 테이블에 대한 관용적인 ORM 매핑을 만들 수 있습니다.

먼저 주문 항목 클래스를 만듭니다.

spanner-example/src/main/java/com/example/demo/OrderItem.java

package com.example.demo;

import com.google.cloud.spring.data.spanner.core.mapping.Column;
import com.google.cloud.spring.data.spanner.core.mapping.PrimaryKey;
import com.google.cloud.spring.data.spanner.core.mapping.Table;

@Table(name="order_items")
@Data
class OrderItem {
  @PrimaryKey(keyOrder = 1)
  @Column(name="order_id")
  private String orderId;

  @PrimaryKey(keyOrder = 2)
  @Column(name="order_item_id")
  private String orderItemId;

  private String description;
  private Long quantity;
}

Spanner의 상위/하위 관계의 경우 복합 기본 키를 사용해야 합니다. 이 예에서 복합 키는 order_idorder_item_id입니다.

다음으로 Order 클래스를 만듭니다.

spanner-example/src/main/java/com/example/demo/Order.java

package com.example.demo;

import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import com.google.cloud.spring.data.spanner.core.mapping.Column;
import com.google.cloud.spring.data.spanner.core.mapping.Interleaved;
import com.google.cloud.spring.data.spanner.core.mapping.PrimaryKey;
import com.google.cloud.spring.data.spanner.core.mapping.Table;

@Table(name="orders")
@Data
public class Order {
  @PrimaryKey
  @Column(name="order_id")
  private String id;

  private String description;

  @Column(name="creation_timestamp")
  private LocalDateTime timestamp;

  @Interleaved
  private List<OrderItem> items;
}

이 클래스는 @Interleaved 주석을 사용하여 주문 항목과 일대다 관계를 만듭니다.

6. OrderRepository 인터페이스 만들기

다음 콘텐츠로 OrderRepository 클래스를 만듭니다.

spanner-example/src/main/java/com/example/demo/OrderRepository.java

package com.example.demo;

import com.google.cloud.spring.data.spanner.repository.SpannerRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends SpannerRepository<Order, String> {
}

인터페이스는 SpannerRepository<Order, String>를 확장합니다. 여기서 Order는 도메인 클래스이고 String는 기본 키 유형입니다. Spring Data는 이 인터페이스를 통해 자동으로 CRUD 액세스를 제공하므로 추가 코드를 만들 필요가 없습니다.

7. 기본 작업을 위한 REST 컨트롤러 만들기

기본 애플리케이션 DemoApplication 클래스를 열고 다음과 같이 수정합니다.

spanner-example/src/main/java/com/example/demo/DemoApplication.java

package com.example.demo;

import java.time.LocalDateTime;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

@SpringBootApplication
public class DemoApplication {
        public static void main(String[] args) {
                SpringApplication.run(DemoApplication.class, args);
        }
}

@RestController
class OrderController {
  private final OrderRepository orderRepository;

        OrderController(OrderRepository orderRepository) {
                this.orderRepository = orderRepository;
        }

        @GetMapping("/api/orders/{id}")
        public Order getOrder(@PathVariable String id) {
          return orderRepository.findById(id)
                                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, id + " not found"));
        }

        @PostMapping("/api/orders")
        public String createOrder(@RequestBody Order order) {
                // Spanner currently does not auto generate IDs
                // Generate UUID on new orders
                order.setId(UUID.randomUUID().toString());
                order.setTimestamp(LocalDateTime.now());

                order.getItems().forEach(item -> {
                        // Assign parent ID, and also generate child ID
                        item.setOrderId(order.getId());
                        item.setOrderItemId(UUID.randomUUID().toString());
                });

          Order saved = orderRepository.save(order);
          return saved.getId();
        }
}

8. 애플리케이션 실행

애플리케이션을 다시 빌드하고 실행합니다.

./mvnw spring-boot:run

그러면 제대로 시작되고 포트 8080에서 수신 대기해야 합니다.

엔드포인트에 주문 레코드를 게시할 수 있습니다.

curl -H"Content-Type: application/json" -d'{"description": "My orders", "items": [{"description": "Android Phone", "quantity": "1"}]}' \
  http://localhost:8080/api/orders

주문의 UUID로 응답해야 합니다.

그런 다음 UUID를 사용하여 주문을 검색할 수 있습니다.

curl http://localhost:8080/api/orders/REPLACE_WITH_ORDER_UUID

Cloud Spanner 데이터가 저장되는 방식을 확인하려면 Cloud 콘솔로 이동하여 Spanner → Spanner 인스턴스 → 주문 데이터베이스 → 주문 테이블 → 데이터로 이동합니다.

780f4947e4a864f6.png

9. 삭제

삭제하려면 더 이상 요금이 발생하지 않도록 Spanner 인스턴스를 삭제하세요.

gcloud spanner instances delete spanner-instance -q

10. 수고하셨습니다

이 Codelab에서는 Cloud Spanner에서 데이터를 저장하고 검색할 수 있는 대화형 CLI 애플리케이션을 만들었습니다.

자세히 알아보기

라이선스

이 작업물은 Creative Commons Attribution 2.0 일반 라이선스에 따라 사용이 허가되었습니다.