AWS는 사실 키 유출되서 요금폭탄 맞은 사례가 많아서 멀리했었는데, 이번 기회에 사용해 볼 수 있었네요!
그래서 사용하면서 보안쪽이 굉장히 중요하다고 생각이 되서 Jasypt를 사용해서 계정 AccessKey, SecretKey를 암호화 했습니다.
그래도 혹시 몰라서 이걸로 충분한지 싶어서, application.yml 파일자체를 git에서 빼버려야 되는건지 싶어서 질문을 드렸는데,
파일 자체를 빼버리는 건 좋은 방법은 아니라고, 암호화 하는게 좋은 방법이라고 답변해주셨습니다. 😄
CompletableFuture를 이용해서 비동기를 사용해볼 수 있었고, 코드 리뷰 세션을 통해서도 많이 배울 수 있었습니다.
@ControllerAdvice 에서는 처리할 수 없다.@Async vs CompletableFuture ?@Async는 CompletableFuture에서 지원되는 등의 체이닝 이벤트(thenAccept, exceptionally)와는 거리가 멀다.@Async의 경우에는 별도의 쓰레드풀을 지정하는 반면, CompletableFuture는 JVM이 실행될때 기본 쓰레드풀에서 실행된다.나름 고심하면서 코딩 했던것 같은데, 코드 리뷰를 받고나서 코드가 참 지저분했구나... 를 매우 많이 느낄 수 있었습니다.
AmazonServiceException, SdkClientException, 그 외의 예외로 나누어 처리할 필요는 없음!profileImageUrl 필드 추가
변경 전
public User(...) {
// 💩 → 지저분!
if (profileImageUrl != null)
checkArgument(profileImageUrl.length() > 250, "profileImageUrl length must be less than 255 characters");
this.profileImageUrl = profileImageUrl;
}
...
public void updateProfileImage(String profileImageUrl) {
// 💩 → 체크 부분 빠짐!
this.profileImageUrl = profileImageUrl;
}
변경 후
public User(...) {
// ✨✨
checkArgument(profileImageUrl == null || profileImageUrl.length() <= 255, "profileImageUrl length must be less than 255 characters.");
this.profileImageUrl = profileImageUrl;
}
public void updateProfileImage(String profileImageUrl) {
// ✨✨
checkArgument(profileImageUrl == null || profileImageUrl.length() <= 255, "profileImageUrl length must be less than 255 characters.");
this.profileImageUrl = profileImageUrl;
}
이미지 업로드
S3Client - upload 구현
변경 전
if (metadata != null && metadata.size() > 0)
metadata.forEach(objectMetadata::addUserMetadata); // 💩 → 불필요한 for문 사용!
변경 후
if (metadata != null && metadata.size() > 0)
objectMetadata.setUserMetadata(metadata); // ✨✨
예외처리
변경 전
toAttachedFile(file).ifPresent(attachedFile ->
supplyAsync(...
).thenAccept(...
).exceptionally(e -> { // 💩 → exception을 나누어 처리할 필요가 없다!!!
if (e instanceof AmazonServiceException)
log.warn("AmazonServiceException occurred: {}", e.getMessage(), e);
else if (e instanceof SdkClientException)
log.warn("SdkClientException occurred: {}", e.getMessage(), e);
else
log.error("Unexpected aws-s3 error occurred: {}", e.getMessage(), e);
return null;
})
);
변경 후
toAttachedFile(file).ifPresent(attachedFile ->
supplyAsync(...
).thenAccept(...
).exceptionally(e -> { // ✨✨
log.warn("aws s3 - upload error occurred: {}", e.getMessage(), e);
return null;
})
);
}
업로드 관련
변경 전
private static final String BASE_URL = "user/"; // 💩 불필요한 상수 선언!
private static final String DEFAULT_EXTENSION = "png"; // 💩 불필요한 상수 선언!
// 💩 불필요한 함수 정의!
// 경로명 가져오기
// User 폴더 → UserId → FileName 으로 저장 될 수 있도록!
public String getBaseUrl(Long userId) {
return userId == null ? "" : BASE_URL + userId.toString();
}
// 💩 → CompletableFuture - supplyAsync 함수에서 예외 처리를 했다면, 여기서는 Optional로 리턴할 필요가 없음
// 💩 → 이유는 예외 발생을 한다면 거기서 throw exception을 하고 끝이기 때문!
public Optional<String> uploadProfileImage(AttachedFile profileFile, String baseUrl) {
Map<String, String> map = new HashMap<>();
String key = profileFile.randomName(baseUrl, DEFAULT_EXTENSION);
return Optional.of(
s3Client.upload(profileFile.inputStream(), profileFile.length(), key, profileFile.getContentType(), map)
);
}
변경 후
// ✨✨
public String uploadProfileImage(AttachedFile profileFile) {
String key = profileFile.randomName("profile", "png");
return s3Client.upload(profileFile.inputStream(), profileFile.length(), key, profileFile.getContentType(), null);
}
Comment 관련 비지니스 로직 구현
변경 전
// 💩 → comment.postId, comment.userId vs postId, userId 체크 구문이 빠졌음!
@Transactional
public Comment write(...) {
return findPost(...).map(post -> {
postRepository.updateCommentsUp(postId);
return commentRepository.insert(comment);
}).orElseThrow(() -> new NotFoundException(...));
}
// 👍
@Transactional(readOnly = true)
public List<Comment> findAll(...) {
return findPost(...)
.map(post -> commentRepository.findAll(postId))
.orElse(emptyList());
}
변경 후
@Transactional
public Comment write(...) {
// ✨✨
checkArgument(comment != null, "comment must be provided");
checkArgument(comment.getPostId().equals(postId), "comment.postId must equals postId");
checkArgument(comment.getUserId().equals(userId), "comment.userId must equals userId");
return findPost(...).map(post -> {
postRepository.updateComments(postId, postWriterId);
return insert(comment);
}).orElseThrow(() -> new NotFoundException(...));
}