[DynamoDB] Spring에서 DynamoDB 사용

Spring boot에서 AWS DynamoDB를 사용하여 CRUD를 하는 법에 대하여 간단히 정리해 보았다.

기타 No-sql DB와 다르게 기능 구현을 쉽게 할 수 있었던것 같다. 본 글의 spring 튜토리얼를 참고하여 작성 되었다.

준비

Local에서 dynamoDB을 docker로 구동하는 방법을 공유 한다.(이렇게 구동시 Key값은 공백으로 연결한다.)

version: '3.7'
services:
  dynamodb:
    image:  amazon/dynamodb-local
    container_name: my-dynamodb
    hostname: dynamodb
    restart: always
    volumes:
      -  ./my-dynamodb-data:/home/dynamodblocal/data
    ports:
      - 8000:8000
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal/data/"

구동 명령어는 아래와 같다.

docker-compose -f dynamodb.yml up -d

Maven 설정

<dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-releasetrain</artifactId>
        <version>Lovelace-SR16</version>
        <type>pom</type>
        <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-dynamodb</artifactId>
        <version>1.11.64</version>
    </dependency>
    <dependency>
        <groupId>com.github.derjust</groupId>
        <artifactId>spring-data-dynamodb</artifactId>
        <version>5.1.0</version>
    </dependency>
</dependencies>

Spring data와 asw dynamoDB에 대한 종속을 추가하면 기본적인 dependency는 해결 된다.

설정

DynamoDB를 사용하기 위해서는 전역에서의 설정이 필요한다. 본 글에서는 dynamoDBMaper를 사용하여 테이블 생성을 처리 했다.

@Configuration
@EnableDynamoDBRepositories
        (basePackages = "com.baeldung.spring.data.dynamodb.repositories")
public class DynamoDBConfig {

    @Value("${amazon.dynamodb.endpoint}")
    private String amazonDynamoDBEndpoint;

    @Value("${amazon.aws.accesskey}")
    private String amazonAWSAccessKey;

    @Value("${amazon.aws.secretkey}")
    private String amazonAWSSecretKey;

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        AmazonDynamoDB amazonDynamoDB
                = new AmazonDynamoDBClient(amazonAWSCredentials());

        if (!StringUtils.isEmpty(amazonDynamoDBEndpoint)) {
            amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint);
        }

        return amazonDynamoDB;
    }

    @Bean
    public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB){
        return  new DynamoDBMapper(amazonDynamoDB, DynamoDBMapperConfig.DEFAULT);
    }

    @Bean
    public AWSCredentials amazonAWSCredentials() {
        return new BasicAWSCredentials(
                amazonAWSAccessKey, amazonAWSSecretKey);
    }
}

DynamoDB 접근 정보는 환경 변수로 설정하고 그 값을 읽어 오는 방식으로 작업 했다.

Entity 생성

@DynamoDBTable(tableName = "ProductInfo")
public class ProductInfo {
    private String id;
    private String name;
    private String cost;
    private LocalDateTime createAt;

    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    public String getId() {
        return id;
    }

    public void setCost(String cost) {
        this.cost = cost;
    }

    public void setName(String name) {
        this.name = name;
    }

    @DynamoDBAttribute
    public String getName() {
        return name;
    }

    @DynamoDBAttribute
    public String getCost() {
        return cost;
    }

    @DynamoDBAttribute
    public LocalDateTime getCreateAt(){
        return createAt;
    }

    @Builder
    public ProductInfo(String name, String cost, LocalDateTime createAt){
        this.name = name;
        this.cost = cost;
        this.createAt = createAt;
    }
}

@DynamoDBTable(tableName = "Comment"): Entity 클래스로 정의

@DynamoDBHashKey(attributeName = "id"): HashKey 필드로 유일한 값을 가지는 필드로 설정

@DynamoDBAttribute: 테이블 칼럼으로 설정

CRUD Repository 생성

@EnableScan
public interface ProductInfoRepository extends
        CrudRepository<ProductInfo, String> {
    Optional<ProductInfo> findById(String id);
}

CrudRepository에서 상속받으면 자동으로 CRUD을 구현 해준다.

CRUD에 대한 Test 작성

@Test
void TestCRUD(){
    //create
    ProductInfo productInfo = productInfoRepository.save(ProductInfo.builder()
            .name("1")
            .cost("20")
            .createAt(LocalDateTime.now()).build());
    //select
    List<ProductInfo> foundProductInfos = (List<ProductInfo>) productInfoRepository.findAll();
    //update
    Optional<ProductInfo> optionalProductInfo = productInfoRepository.findById(productInfo.getId());
    if(!optionalProductInfo.isPresent()){
        //throw exception
    }
    ProductInfo resProductInfo = optionalProductInfo.get();
    resProductInfo.setCost("30");
    productInfoRepository.save(resProductInfo);
    //delete
    productInfoRepository.deleteById(productInfo.getId());

    assertThat(productInfo.getId().isEmpty());

    dynamoDBMapper.save(productInfo);
    assertThat(!productInfo.getId().isEmpty());
}

Test class에서 @Befor && @after

@Test 호출 되기전과 된 후 호출되도록하는 Annotation이다. 본 예제에서는 이 Annotation을 이용하여 Test전 Table 생성과 삭제를 구현했다.

@BeforeEach
void createTable(){

    CreateTableRequest createTableRequest = dynamoDBMapper.generateCreateTableRequest(ProductInfo.class)

            .withProvisionedThroughput(new ProvisionedThroughput(1L, 1L));

    createTableRequest.getGlobalSecondaryIndexes().forEach(

            idx ->idx

                    .withProvisionedThroughput(new ProvisionedThroughput(1L,1L))

                    .withProjection(new Projection().withProjectionType("ALL"))

    );

    TableUtils.createTableIfNotExists(amazonDynamoDB, createTableRequest);

}

@AfterEach
void deleteTable(){

    DeleteTableRequest deleteTableRequest= dynamoDBMapper.generateDeleteTableRequest(ProductInfo.class);

    TableUtils.deleteTableIfExists(amazonDynamoDB, deleteTableRequest);

}

Ref.

https://techblog.woowahan.com/2633/#spring-data-dynamodb로-쿼리-메서드-사용하기

https://reflectoring.io/spring-dynamodb/#accessing-dynamodb-with-spring-data

https://www.baeldung.com/spring-data-dynamodb

https://medium.com/platform-engineer/running-aws-dynamodb-local-with-docker-compose-6f75850aba1e

https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.Annotations.html

https://javatodev.com/spring-boot-dynamo-db-crud-tutorial/

'DataBase' 카테고리의 다른 글

[DynamoDB] Partition Key 설계  (0) 2021.12.21
[Database] DynamoDB GSI 사용  (0) 2021.12.19
PostgreSQL Transaction isolation level  (0) 2021.12.13
ORM 장단점  (0) 2021.12.12
Postgres Failover 방법  (0) 2021.12.12