파트 별로 따로 관리하고 있던 키 값을 한 번에 관리하기 위해 AWS Secret Manager를 사용했다.
먼저 AWS 시크릿매니저에 보안 암호 등록을 하고 AWS CLI 설치와 권한 설정을 했다.
보안 암호를 등록하면 이렇게 샘플 코드를 준다.
설치
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
권한 설정
$ aws configure
AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [None]:
Default output format [None]:
확인
$ aws sts get-caller-identity
implementation platform('software.amazon.awssdk:bom:2.21.44')
implementation 'software.amazon.awssdk:secretsmanager'
implementation 'software.amazon.awssdk:regions'
implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000')
implementation 'com.amazonaws:aws-java-sdk-dynamodb'
필요한 라이브러리를 추가해준다.
DynamoDB에 저장되어있는 이 데이터를 읽어 올 것이다.
먼저 AWSConfig 파일을 작성해준다.
@Getter
@Configuration
public class AWSConfig {
private final String accessKey;
private final String secretKey;
private final String region;
public AWSConfig() {
String key = getSecret();
this.accessKey = key.substring(19, 39);
this.secretKey = key.substring(66, 106);
this.region = "ap-northeast-2";
}
public static String getSecret() {
String secretName = "MySecretName";
Region region = Region.of("ap-northeast-2");
// Create a Secrets Manager client
SecretsManagerClient client = SecretsManagerClient.builder()
.region(region)
.build();
GetSecretValueRequest getSecretValueRequest = GetSecretValueRequest.builder()
.secretId(secretName)
.build();
GetSecretValueResponse getSecretValueResponse;
try {
getSecretValueResponse = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
// For a list of exceptions thrown, see
// https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
throw e;
}
return getSecretValueResponse.secretString();
}
}
AWS 시크릿 매니저에서 access key를 가져오는 코드다. 위에서 말한 보안 암호를 등록하면 주는 샘플 코드를 사용했다.
리턴 값이 "AWS_ACCESS_KEY:키 값, AWS_SECRET_ACCESS_KEY:시크릿 키 값"의 형태인 String으로 반환돼서 파싱을 해야한다. 찾아보니 해쉬 맵으로 하는 방법이 있는 거 같은데?? 자바 문법을 잘 몰라서;;; 일단은 substring으로 처리했다. 최악의 코드.
+) 수정한 코드 추가 (4/10)
public AWSConfig() {
Map<String, String> awsKey = stringToMap(getSecret());
this.accessKey = awsKey.get("AWS_ACCESS_KEY");
this.secretKey = awsKey.get("AWS_SECRET_ACCESS_KEY");
this.region = "ap-northeast-2";
}
private Map<String, String> stringToMap(String value) {
ObjectMapper objectMapper = new ObjectMapper();
TypeReference<Map<String, String>> typeReference = new TypeReference<Map<String, String>>() {
};
Map<String, String> awsKey = new HashMap<>();
try {
awsKey = objectMapper.readValue(value, typeReference);
} catch (JsonMappingException e) {
log.error("JSON 구조와 매핑 문제 발생: {}", e.getMessage());
} catch (JsonProcessingException e) {
log.error("JSON 처리 중 예외 발생: {}", e.getMessage());
}
return awsKey;
}
String host = secretsJson.get("host").textValue();
String port = secretsJson.get("port").textValue();
String dbname = secretsJson.get("dbname").textValue();
String username = secretsJson.get("username").textValue();
String password = secretsJson.get("password").textValue();
이런 식으로 해도 됨
다음으로 DynamoDBConfig 파일을 작성한다.
@Configuration
public class DynamoDBConfig {
@Autowired
private AWSConfig awsConfig;
@Bean
public DynamoDBMapper dynamoDBMapper() {
return new DynamoDBMapper(amazonDynamoDBClient());
}
private AmazonDynamoDBClient amazonDynamoDBClient() {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsConfig.getAccessKey(), awsConfig.getSecretKey());
return (AmazonDynamoDBClient) AmazonDynamoDBAsyncClientBuilder.standard()
.withRegion(awsConfig.getRegion())
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}
}
DynamoDBMapper로 DynamoDB에 CRUD를 할 수 있는데, Bean으로 등록해서 하나의 객체만 생성되게 해준다.
(참고: https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.CRUDExample1.html)
DynamoDB에서 데이터를 읽어올 테이블을 지정하고 데이터의 값을 매핑할 클래스를 만든다.
@Getter
@Setter
@Component
@NoArgsConstructor
@AllArgsConstructor
@DynamoDBTable(tableName = "MyTable")
public class DynamoDBResponse {
@DynamoDBHashKey(attributeName = "month")
private Integer month;
@DynamoDBRangeKey(attributeName = "timestamp")
private String timestamp;
@DynamoDBAttribute(attributeName = "id")
private String id;
@DynamoDBAttribute(attributeName = "description")
private String description;
}
HashKey(Partition Key)만 있으면 HashKey가 기본키 역할을 하고, HashKey랑 RangeKey가 같이 있으면 둘이 복합키로 기본키 역할을 한다.
(참고: https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey)
이제 드디어 데이터를 읽어오는 DynamoDBService 파일을 작성한다.
@Service
public class DynamoDBService {
@Autowired
private DynamoDBMapper mapper;
public DynamoDBResponse getData() {
DynamoDBResponse response = mapper.load(DynamoDBResponse.class, 0, "1");
if (response == null) {
throw new RuntimeException("Data not found for id: ");
}
return response;
}
}
mapper.load()에 들어가는 파라미터는 위에서 만든 데이터 클래스, 파티션 키 값, 정렬 키 값이다. 파라미터로 넣은 키 값으로 DynamoDB에서 데이터를 찾는다. 물론 파티션 키만 있는 테이블이면 파라미터로 파티션 키만 넣어주면 된다.
(참고: https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.load)
Service 파일에서
String description = dynamoDBService.getData().getDescription();
이런 식으로 쓰면 된다.
되는 건 확인했는데... 고쳐야 할 것들이 많다~
1. key 파싱 방법 바꾸기
2. yml 파일에 key를 저장할 순 없을까?
3. DynamoDB 데이터의 값 중 List 타입을 가져오려고 클래스에 List로 정의했는데 TypeError로 데이터를 가져오지 못 했음
그래도 DynamoDB에서 데이터 읽는 건 처음이었는데 구현해보고 작동하는 걸 확인해서 좋았다!!
참고자료:
(https://www.theprogrammerguide.com/post/aws-dynamodb_spring/)