Spring Boot 개발 중 학습이 필요한 내용을 정리하고,
트러블 슈팅 과정을 기록하는 포스팅입니다.
Spring boot
를 활용해서 RestAPI
개발 중 Nosql
인 DynamoDB
에 Entity
를 저장해야할 일이 생겼습니다. AWS
의 서비스들은 Spring boot
에서 활용하기 쉽도록 라이브러리를 잘 만들어 놓았고, 정식 Docs와 관련 자료도 많아서 활용하기 간편했습니다.
수월하게 개발 중, DynamoDB
의DynamoDBMapper
를 사용해서 간단하게 하나의 객체를 save
하는데에서 발생했습니다.
저장하려는 Entity 객체
는 아래의 TextMemoState
입니다. 해당 Entity
는 Redis
에도 저장이 되며, DynamoDB
에도 저장이 되는 객체입니다.
@Getter
@RedisHash(value = "text_memo_state")
@DynamoDBTable(tableName = "text_memo_state")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TextMemoState {
@Id
@DynamoDBHashKey(attributeName = "id")
private String id;
@DynamoDBAttribute
@Column(name = "individual_video_id")
protected UUID individualVideoId;
@DynamoDBAttribute
@Column(name = "state_json")
private String stateJson;
@DynamoDBAttribute
@Column(name = "video_time")
private LocalTime videoTime;
@DynamoDBAttribute
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdDate;
@Builder
public TextMemoState(String id, UUID individualVideoId, String stateJson, LocalTime videoTime) {
this.id = id;
this.individualVideoId = individualVideoId;
this.stateJson = stateJson;
this.videoTime = videoTime;
this.createdDate = LocalDateTime.now();
}
}
간단하게 DynamoDBMapper
를 활영해서 위의 TextMemoState
하나를 save
하는 로직입니다.
@Service
@Transactional
public class TextMemoStateDao {
private final DynamoDBMapper dynamoDBMapper;
// .. 중략
public TextMemoState saveToDynamo(TextMemoState textMemoState) {
dynamoDBMapper.save(textMemoState);
return textMemoState;
}
}
DynamoDB
가이드 코드를 참고했으며, 크게 어렵지 않고 짧은 코드라 문제 없이 바로 될 것 같았지만 역시나 한번에 되지는 않습니다ㅎㅎ,,
not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted
오류문만 봐도 Type이 호환되지 않아~ 라고 외치는 것 같습니다!
무엇이 문제일까~ 하고 구글링을 해보니 id
와 관련된 내용이 나왔습니다.
@Id
@DynamoDBHashKey(attributeName = "id")
private String id;
현재 이 Entity
는 Redis
와도 연결이 돼있어서 @Id
어노테이션이 붙어있는데, DynamoDB
의 Key
값을 지정해주는 @DynamoDBHashKey
어노테이션이 같이 붙으면서 문제가 발생한 것일 수도 있다고 생각이 됐습니다.
@Id
어노테이션을 지우고 시도하면 Redis
관련 코드부터 오류가 나서 실행되지 않을 것 같았습니다. 때문에 클래스를 분리해서 DynamoDB
만의 Entity
클래스를 하나 만들고 시도해봤으나, 같은 오류가 발생했습니다.
그러던 중, AWS 콘솔에서 DynamoDB의 키값의 데이터 타입이 자바 코드 상의 형식과 다르다는 부분을 발견했습니다.
DynamoDB
의 키값을 Number
타입으로 지정돼 있었지만, 자바 코드 상의 id
는 위 처럼 String
형식이었습니다.
이게 문제였네~ 하고 테이블을 삭제하고 다시 생성했습니다.(한번 테이블을 생성하면 key의 형식을 바꾸지 못한다고 하네요ㅠ)
되겠다 싶어서 돌려봤지만,, 오류는 계속해서 발생했습니다.
(해결하고 나서 생각해봤는데, 이 부분도 분명 문제였습니다!)
@DynamoDBAttribute
@Column(name = "video_time")
private LocalTime videoTime;
@DynamoDBAttribute
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdDate;
구글링을 하면서 여러가지 방법을 보았지만, 저와 비슷한 경우는 아니었습니다. 그러던 중 Time
과 관련된 데이터 타입들이 눈에 들어왔습니다. 이러한 데이터 타입들이 자동으로 String
으로 변환이 안되는 것이 문제인가 싶었습니다.
아래는 AWS DynamoDb
라이브러리 안의 코드 중 일부입니다.
String/S types,
Character/char
String
java.net.URL
java.net.URI
java.util.Calendar
java.util.Currency
java.util.Date
java.util.Locale
java.util.TimeZone
java.util.UUID
S3Link
Number/N types,
java.math.BigDecimal
java.math.BigInteger
Boolean/boolean
Byte/byte
Double/double
Float/float
Integer/int
Long/long
Short/short
위에 부터 char
타입 ~ S3Link
타입까지는 String
타입으로 변환되며,
BigDecimal
타입 ~ short
타입까지는 Number
타입으로 변환된다고 합니다.
Date
, TimeZone
타입은 자동으로 String
타입으로 바뀌지만,
제가 사용하는 LocalTime
타입과 LocalDateTime
타입은 자동으로 String
타입으로 변환되지 않는 것입니다.
@DynamoDBAttribute
@DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S)
@Column(name = "video_time")
private LocalTime videoTime;
@DynamoDBAttribute
@CreatedDate
@DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S)
@Column(name = "created_at", updatable = false)
private LocalDateTime createdDate;
Entity
의 LocalTime
과 LocalDateTime
선언부에 @DynamoDBTyped
어노테이션을 추가했습니다.
해당 어노테이션은 DynamoDB
에 저장될 타입을 지정해주는 기능을 합니다.
DynamoDBAttributeType.S
로 타입을 지정함으로써 String형
으로 저장되게끔 강제할 수 있습니다.
자바의 데이터 타입과 DB의 데이터 타입이 호환되지 않는 경우는 매우 흔한 경우라 쉽게 파악할 수 도 있었을 것 같은데, Redis와 엮인 문제 같은데? 라는 생각에 잡혀서 길을 돌아온 것 같습니다.
dynamodb 테이블을 만들 때 hashkey를 code 상의 hashkey 이름이 일치해야 하는 군요. LocalDataTime 문제도 있었네요. 참고가 많이 되었습니다. 잘 보고 갑니다.