AWS SDK V2을 사용하여 Spring Boot + AWS DynamoDB 연동하기

dev-well-being·2023년 6월 19일
1
post-thumbnail

Spring Boot에서 AWS DynamoDB를 연동해야할 일이 생겼다.

DynamoDB는 AWS에서 제공하는 NoSQL 데이터베이스이다. key-value 형태로 데이터를 제공한다.

Aurora나 Document DB처럼 DB커넥션을 맺는 형태가 아닌 AWS SDK를 활용하여 인터페이스를 한다.

AWS SDK도 버전마다 설정하는 방법이 상이하여 AWS SDK V2버전을 기준으로 설정을 하였다.

Gradle library 추가

dependencies {
    // aws
	implementation platform('software.amazon.awssdk:bom:2.20.85')
	implementation 'software.amazon.awssdk:dynamodb-enhanced'
}

AWS DynamoDB Client 설정

    @Bean
	public DynamoDbClient dynamoDbClient(){
		return DynamoDbClient.builder().region(Region.AP_NORTHEAST_2).credentialsProvider(StaticCredentialsProvider.create(basicAWSCredentials())).build();
	}

	@Bean
	public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient){
		return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build();
	}

DynamoBean 설정

DynamoDB 테이블에 해당하는 객체를 설정해준다. 해당 객체는 JPA의 Entity와 같은 역할을 한다. @DynamoDbBean 애노테이션을 선언해주어야 한다. @DynamoDbPartitionKey, @DynamoDbSortKey 애노테이션으로 파티션키, 정렬키를 지정할 수 있으며 @DynamoDbAttribute로 DynamoDB의 key 매칭되는 필드를 지정할 수 있다.

특이한 점은 보통 다른 data 라이브러리는 key관련 애노테이션을 변수 위에다 선언하는데 getter 메소드 위에다 지정한다는 점이다. 왜 그런지는 모르겠다. AWS Developer Guide에서 아래와 같이 가이드가 되어 있다.

@Builder
@DynamoDbBean
@NoArgsConstructor
@AllArgsConstructor
@Setter
public class DynamoDbSample {
	private String id;
	private String date;
	private String desc;

	@DynamoDbPartitionKey
	@DynamoDbAttribute("id")
	public String getId() {
		return id;
	}

	@DynamoDbSortKey
	@DynamoDbAttribute("date")
	public String getDate() {
		return date;
	}

	@DynamoDbAttribute("desc")
	public String getDesc() {
		return desc;
	}

}

repository

final로 선언한 dynamoDbSampleDynamoDbTable에 dynamoDbEnhancedClient.table메소드를 활용하여 대상 테이블 선언해주면 그 다음부터는 dynamoDbSampleDynamoDbTable에서 제공하는 메소드를 활용하여 데이터를 조회하고 입/출력을 할 수 있다.

하지만 현 프로젝트는 JPA를 위주로 사용하고 있기 때문에 JPA처럼 repository 형태로 쓰고 싶어서 비슷하게 구조를 잡아보았다. getItem, putItem, deleteItem 메소드가 직관적이여서 사용하기는 편했다.

특이한 점은 파티션키로만 대상을 조회할 때 인데 정렬키를 사용할 경우 파티션키와 정렬키를 동시에 입력해줘야 대상값을 조회해온다. 파티션키만 지정하여 getItem을 하면 에러가 발생한다.

그래서 파티션키만으로 데이터를 조회할 때는 QueryConditional, QueryEnhancedRequest 를 사용하여 데이터를 조회하도록 하니깐 조회가 가능했다.

@Repository
public class DynamoDbSampleRepository {
	private final DynamoDbTable<DynamoDbSample> dynamoDbSampleDynamoDbTable;

	public DynamoDbSampleRepository(DynamoDbEnhancedClient dynamoDbEnhancedClient) {
		this.dynamoDbSampleDynamoDbTable = dynamoDbEnhancedClient.table("DYNAMO_DB_SAMPLE", TableSchema.fromBean(DynamoDbSample.class));
	}

	public void insert(DynamoDbSample dynamoDbSample){
		dynamoDbSampleDynamoDbTable.putItem(dynamoDbSample);
	}

	public DynamoDbSample findBy(DynamoDbSample dynamoDbSample){
		return dynamoDbSampleDynamoDbTable.getItem(dynamoDbSample);
	}

	public DynamoDbSample findById(String id){
		QueryConditional conditional = QueryConditional.keyEqualTo(
				Key.builder()
					.partitionValue(id)
					.build()
			);
		QueryEnhancedRequest queryRequest = QueryEnhancedRequest.builder()
				.queryConditional(conditional)
				.limit(1)
				.build();
		return dynamoDbSampleDynamoDbTable.query(queryRequest).items().stream()
			.findAny()
			.orElseGet(()-> null);
	}

	public DynamoDbSample findByIdAndDate(String id, String date){
		Key key = Key.builder().partitionValue(id).sortValue(date).build();
		return dynamoDbSampleDynamoDbTable.getItem(key);
	}

	public DynamoDbSample update(DynamoDbSample dynamoDbSample){
		return dynamoDbSampleDynamoDbTable.updateItem(dynamoDbSample);
	}

	public void delete(DynamoDbSample dynamoDbSample){
		dynamoDbSampleDynamoDbTable.deleteItem(dynamoDbSample);
	}

	public void deleteByIdAndDate(String id, String date){
		Key key = Key.builder().partitionValue(id).sortValue(date).build();
		dynamoDbSampleDynamoDbTable.deleteItem(key);
	}
}

Test

아래와 같이 Junit 테스트를 해보았고 정상 수행함으로 확인하였다.

    @Autowired
	DynamoDbSampleRepository dynamoDbSampleRepository;

	@Test
	void test() {

		DynamoDbSample dynamoDbSampleInput = DynamoDbSample.builder()
													.id(UUID.randomUUID().toString())
													.date(LocalDateTime.now().toString())
													.desc("DynamoDb Test")
													.build();
		dynamoDbSampleRepository.insert(dynamoDbSampleInput);
		//DynamoDbSample dynamoDbSampleOutput = dynamoDbSampleRepository.findBy(dynamoDbSampleInput);
		DynamoDbSample dynamoDbSampleOutput = dynamoDbSampleRepository.findBy(dynamoDbSampleInput);
		DynamoDbSample dynamoDbSampleOutput2 = dynamoDbSampleRepository.findById(dynamoDbSampleOutput.getId());
		DynamoDbSample dynamoDbSampleOutput3 = dynamoDbSampleRepository.findByIdAndDate(dynamoDbSampleOutput.getId(), dynamoDbSampleOutput.getDate());
		assertThat(dynamoDbSampleOutput.getId()).isEqualTo(dynamoDbSampleInput.getId());
		assertThat(dynamoDbSampleOutput.getId()).isEqualTo(dynamoDbSampleOutput2.getId());
		assertThat(dynamoDbSampleOutput.getId()).isEqualTo(dynamoDbSampleOutput3.getId());

		dynamoDbSampleInput.setDesc("DynamoDb Test2");
		DynamoDbSample dynamoDbSampleUpdateOutput = dynamoDbSampleRepository.update(dynamoDbSampleInput);
		assertThat(dynamoDbSampleUpdateOutput.getId()).isEqualTo(dynamoDbSampleOutput.getId());
		assertThat(dynamoDbSampleUpdateOutput.getDesc()).isNotEqualTo(dynamoDbSampleOutput.getDesc());

		dynamoDbSampleRepository.delete(dynamoDbSampleInput);
		//dynamoDbSampleRepository.deleteByIdAndDate(dynamoDbSampleInput.getId(), dynamoDbSampleInput.getDate());
		DynamoDbSample dynamoDbSampleDeleteOutput = dynamoDbSampleRepository.findBy(dynamoDbSampleOutput);
		assertThat(dynamoDbSampleDeleteOutput).isNull();
	}
profile
안녕하세요!! 좋은 개발 문화를 위해 노력하는 dev-well-being 입니다.

0개의 댓글