-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 사용하지 않는 이미지 제거 기능 #193
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다 :)
코맨트 확인부탁드리고, 기능들 테스트도 함께 추가되면 좋을 것 같네요 👍
import org.springframework.transaction.event.TransactionalEventListener; | ||
|
||
@Component | ||
@Async(value = "asyncTaskExecutor") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
생각해볼만한점이 하나 있을 것 같은데요. (이전에 저희가 만들때는 고려하지 않았던 내용들도 있지만 고민해보면 좋을 것 같아서 코맨트 남겨두어요.)
asyncTaskExecutor
를 사용하면 다른 컴포넌트랑 동일한 스레드를 사용하게 될 텐데 괜찮을까요? 분리하지 않고 사용하는 이유가 있을까요?@Transactional
을 호출하고 있는데, async하게 돌아가는 다른 스레드들이 메인 스레드의 connection을 가져가서 사용해도 괜찮은가요?
} | ||
|
||
@Transactional | ||
public ImageUploadResponse uploadImage(final MultipartFile file) { | ||
String extension = validateExtension(file); | ||
String key = createKey(extension); | ||
PutObjectRequest request = createRequest(file, key); | ||
PutObjectRequest request = createPutRequest(file, key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
unusedImageRepository.save( | ||
UnusedImage.builder() | ||
.key(key) | ||
.build() | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s3Client.putObject
를 넘어서 save 동작 시 문제가 발생한다면, s3에 저장된 object는 어떻게 되나요?
|
||
@Transactional | ||
public void deleteUsingImage(final String imageUrl) { | ||
if (imageUrl != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null 체크를 하시는 이유가 있을까요?
|
||
@Scheduled(cron = "0 0 4 * * *") | ||
@Transactional | ||
public void deleteUnusedImages() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요정도 사이즈가 되니 ImageService
가 담기에는 책임이 너무 많아보이기는 하네요.
public void deleteUnusedImages() { | ||
LocalDateTime deleteBoundary = LocalDateTime.now().minusDays(1L); | ||
List<UnusedImage> unusedImages = unusedImageRepository.findAllByCreatedAtBefore(deleteBoundary); | ||
unusedImageRepository.deleteAllByCreatedAtBefore(deleteBoundary); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- deleteAll은 쿼리가 어떻게 날아가나요?
- 개인적으로는 deleteBoundary를
LocalDateTime
을 받는 것보단LocalDate
만 받는건 어떨까싶긴하네요. 스케쥴 돌아가는 시점에 따라서 시분초까지 계산하기에는 누락건이 발생할 수 있을 것 같아서요.
Quality Gate passedThe SonarCloud Quality Gate passed, but some issues were introduced. 1 New issue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 주드
잊어버리신 줄 알았는데 해오셨네요 감사합니다
개인적으로 궁금한 부분 질문 남겼는데 확인해주세요 😄
|
||
public ImageService(final S3Client s3Client, @Value("${cloud.aws.s3.bucket}") final String bucketName) { | ||
public ImageService(final S3Client s3Client, @Value("${cloud.aws.s3.bucket}") final String bucketName, unusedImageRepository unusedImageRepository) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
이 빠진 것 같아여 👍
} | ||
|
||
@Transactional | ||
public void deleteImageWhenReviewDeleted(final String imageUrl) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
메서드 이름 굿 👍
.build(); | ||
} | ||
|
||
@Scheduled(cron = "0 0 4 * * *") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신기하네요 이거
|
||
import java.util.List; | ||
|
||
public interface ImageUploader { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
의존성 분리 굿 👍🏻
import static com.woowacourse.matzip.environment.ProfileUtil.PROD; | ||
|
||
@Profile({LOCAL, PROD}) | ||
@Component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Primary
같이 빈의 우선순위를 표현해줄 필요는 없나요? 아직은 없지만 여러 ImageUploader
가 생긴다면 빈 우선순위가 필요할 것 같아서요
); | ||
private static final String DELIMITER = "."; | ||
|
||
public static void validateExtension(final String fileName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extension 이 아닌 fileName 을 매개변수로 받고, 전체적인 fileName 에 대한 검증을 수행하고 있으니 validateFileName
이 더 괜찮은 네이밍 같다는 생각이 듭니당 어떻게 생각하세요?
import static com.woowacourse.matzip.environment.ProfileUtil.LOCAL; | ||
import static com.woowacourse.matzip.environment.ProfileUtil.PROD; | ||
|
||
@Profile({LOCAL, PROD}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOCAL
도 포함하신 이유가 있나요?
|
||
void deleteImage(final String imageUrl); | ||
|
||
void deleteImages(final List<UnusedImage> unusedImages); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
매개변수의 타입 자체가 UnusedImage
이기 때문에 메서드 이름도 그와 동일하게 deleteUnusedImages
로 하는 게 더 좋을 것 같아요. 매개변수 타입이 추상화된 클래스였다면 deleteImages
로 추상화해서 표현해도 될 것 같은데, 현재는 구체 클래스이다 보니 명확하게 명시해주는 건 어떨까요?
|
||
String uploadImage(final MultipartFile file); | ||
|
||
void deleteImage(final String imageUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deleteByImageUrl
이라는 네이밍은 어떠신가요 매개변수가 Image
나 파일이 아니라서 네이밍이 조금 어색할 수도 있을 것 같아요
private final ImageUploader imageUploader; | ||
private final UnusedImageRepository unusedImageRepository; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
의존성이 훨씬 깔끔하네요
Quality Gate passedIssues Measures |
변경사항
todo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바쁜 일정 중에 기능 구현을 또 해주셨네요 👍 트랜잭션 관련해서 궁금한 게 있어 리뷰 남겼습니다. 시간 될 때 확인해주세요 감사합니다
|
||
@Profile(TEST) | ||
@Component | ||
public class TestImageManager implements ImageManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
|
||
@Override | ||
public String uploadImage(final MultipartFile file) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
만약 이미지 업로드 실패하면 어떻게 되나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
실패하는 경우 s3에서 어떤 응답을 주는지 못찾아서 그 로직이 없는데 추가하겠습니다 감사합니다
|
||
@Override | ||
public void deleteImages(final List<UnusedImage> unusedImages) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
불필요한 공백 🫡
} | ||
|
||
@Override | ||
public void deleteImages(final List<UnusedImage> unusedImages) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 메서드도 실패하는 경우에는 어떻게 처리되나요?
this.unusedImageRepository = unusedImageRepository; | ||
} | ||
|
||
@Scheduled(cron = "0 0 4 * * *") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Transactional
은 없어도 되나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 select와 delete가 한 트랜잭션으로 묶일 필요가 없다고 생각했고 시간이 오래걸리는 s3 api 사용 시간동안 db 커넥션을 물고 있지 않는게 나아서 제거 했습니다.
issue: #192
as-is
to-be