From 690f870ef750f9f9c5895cc350dd7c75f557633a Mon Sep 17 00:00:00 2001 From: kwonhee1 Date: Mon, 22 Sep 2025 19:40:36 +0900 Subject: [PATCH 1/3] feat img delete aop --- .../demo/img/MustLastParameterException.java | 7 +++ .../NextLevel/demo/img/service/ImgPath.java | 30 +++++++++++ .../demo/img/service/ImgService.java | 6 +-- .../demo/img/service/ImgServiceImpl.java | 32 ++++++------ .../demo/img/service/ImgTransactionAop.java | 24 +++++++-- .../notice/controller/NoticeController.java | 4 +- .../demo/notice/service/NoticeService.java | 15 +++--- .../notice/service/ProjectNoticeService.java | 9 ++-- .../project/service/ProjectService.java | 17 +++--- .../story/service/ProjectStoryService.java | 15 +++--- .../social/controller/SocialController.java | 2 +- .../demo/social/service/SocialService.java | 24 +++++---- .../demo/user/service/LoginService.java | 5 +- .../demo/user/service/UserService.java | 7 +-- .../NextLevel/demo/img/ImgServiceTest.java | 52 +++++++++---------- .../service/userService/UserServiceTest.java | 7 +-- 16 files changed, 160 insertions(+), 96 deletions(-) create mode 100644 src/main/java/NextLevel/demo/img/MustLastParameterException.java create mode 100644 src/main/java/NextLevel/demo/img/service/ImgPath.java diff --git a/src/main/java/NextLevel/demo/img/MustLastParameterException.java b/src/main/java/NextLevel/demo/img/MustLastParameterException.java new file mode 100644 index 0000000..406cca6 --- /dev/null +++ b/src/main/java/NextLevel/demo/img/MustLastParameterException.java @@ -0,0 +1,7 @@ +package NextLevel.demo.img; + +public class MustLastParameterException extends RuntimeException{ + public MustLastParameterException(String message ){ + super(message); + } +} diff --git a/src/main/java/NextLevel/demo/img/service/ImgPath.java b/src/main/java/NextLevel/demo/img/service/ImgPath.java new file mode 100644 index 0000000..2055749 --- /dev/null +++ b/src/main/java/NextLevel/demo/img/service/ImgPath.java @@ -0,0 +1,30 @@ +package NextLevel.demo.img.service; + +import lombok.Getter; +import lombok.Setter; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class ImgPath { + + private List saved; + private List deleted; + + public void save(Path path){ + saved.add(path); + } + + public void delete(Path path){ + deleted.add(path); + } + + public ImgPath(){ + saved = new ArrayList<>(); + deleted = new ArrayList<>(); + } + +} diff --git a/src/main/java/NextLevel/demo/img/service/ImgService.java b/src/main/java/NextLevel/demo/img/service/ImgService.java index 2cfe85d..6401073 100644 --- a/src/main/java/NextLevel/demo/img/service/ImgService.java +++ b/src/main/java/NextLevel/demo/img/service/ImgService.java @@ -11,9 +11,9 @@ public interface ImgService { - ImgEntity saveImg(MultipartFile imgFile, ArrayList imgPaths); - ImgEntity updateImg(MultipartFile imgFile, ImgEntity oldImg, ArrayList imgPaths); - void deleteImg(ImgEntity img); + ImgEntity saveImg(MultipartFile imgFile, ImgPath imgPath); + ImgEntity updateImg(MultipartFile imgFile, ImgEntity oldImg, ImgPath imgPath); + void deleteImg(ImgEntity img, ImgPath imgPath); ImgEntity saveSocialImg(String imgURL); void deleteImgFile(List filePath) throws IOException ; diff --git a/src/main/java/NextLevel/demo/img/service/ImgServiceImpl.java b/src/main/java/NextLevel/demo/img/service/ImgServiceImpl.java index 53e898a..76f4330 100644 --- a/src/main/java/NextLevel/demo/img/service/ImgServiceImpl.java +++ b/src/main/java/NextLevel/demo/img/service/ImgServiceImpl.java @@ -35,7 +35,7 @@ public class ImgServiceImpl implements ImgService { private final ImgRepository imgRepository; // @Transactional(propagation = Propagation.REQUIRES_NEW) - public ImgEntity saveImg(MultipartFile imgFile, ArrayList imgPaths) { + public ImgEntity saveImg(MultipartFile imgFile, ImgPath imgPath) { if(imgFile == null || imgFile.isEmpty()) return null; try { byte[] bytes = imgFile.getBytes(); @@ -43,10 +43,10 @@ public ImgEntity saveImg(MultipartFile imgFile, ArrayList imgPaths) { fileName = addImgNumber(fileName); - Path path = Paths.get(System.getProperty("user.dir") ,IMG_DEFAULT_PATH, fileName); + Path path = getPath(fileName); // Paths.get(System.getProperty("user.dir") ,IMG_DEFAULT_PATH, fileName); Files.write(path, bytes); - imgPaths.add(path); + imgPath.save(path); ImgEntity saved = imgRepository.save(new ImgEntity(fileName)); @@ -61,13 +61,13 @@ public ImgEntity saveImg(MultipartFile imgFile, ArrayList imgPaths) { } // img uri 변경 없이 진짜 파일 값만 덮어쓰기 - public ImgEntity updateImg(MultipartFile imgFile, ImgEntity oldImg, ArrayList imgPaths) { + public ImgEntity updateImg(MultipartFile imgFile, ImgEntity oldImg, ImgPath imgPath) { if(oldImg == null){ - return saveImg(imgFile, imgPaths); + return saveImg(imgFile, imgPath); } try { - Path path = Paths.get(System.getProperty("user.dir"), IMG_DEFAULT_PATH, oldImg.getUri()); + Path path = getPath(oldImg.getUri()); // Paths.get(System.getProperty("user.dir"), IMG_DEFAULT_PATH, oldImg.getUri()); Files.write(path, imgFile.getBytes()); @@ -84,18 +84,14 @@ public ImgEntity updateImg(MultipartFile imgFile, ImgEntity oldImg, ArrayList filePath) throws IOException { Files.deleteIfExists(path); } } + + private Path getPath(String uri) { + return Paths.get(System.getProperty("user.dir") ,IMG_DEFAULT_PATH, uri); + } } diff --git a/src/main/java/NextLevel/demo/img/service/ImgTransactionAop.java b/src/main/java/NextLevel/demo/img/service/ImgTransactionAop.java index 5c9fbdd..484c78e 100644 --- a/src/main/java/NextLevel/demo/img/service/ImgTransactionAop.java +++ b/src/main/java/NextLevel/demo/img/service/ImgTransactionAop.java @@ -2,6 +2,8 @@ import java.nio.file.Path; import java.util.ArrayList; + +import NextLevel.demo.img.MustLastParameterException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; @@ -16,17 +18,31 @@ public class ImgTransactionAop { private final ImgServiceImpl imgService; + /* + joinPoint :: args :: last argument is must ImgPath.class + success : delete all deleted Img File + fail : delete add saved Img File + */ + @Around("@annotation(imgTransaction)") public Object around(ProceedingJoinPoint joinPoint, ImgTransaction imgTransaction) throws Throwable { - ArrayList imgPaths = new ArrayList<>(); + ImgPath path = new ImgPath(); log.info("img transaction start"); try{ Object[] args = joinPoint.getArgs(); - args[args.length-1] = imgPaths; - return joinPoint.proceed(args); + Object lastParameter = args[args.length-1]; + + if(!(lastParameter instanceof ImgPath)) + throw new MustLastParameterException("last parameter must be of type ImgPath.class"); + + args[args.length-1] = path; + + Object result = joinPoint.proceed(args); + imgService.deleteImgFile(path.getDeleted()); + return result; } catch (Exception e){ log.info("img transaction :: error :: roll back all img files"); - imgService.deleteImgFile(imgPaths); + imgService.deleteImgFile(path.getSaved()); throw e; } } diff --git a/src/main/java/NextLevel/demo/notice/controller/NoticeController.java b/src/main/java/NextLevel/demo/notice/controller/NoticeController.java index 8edbd8c..cbb66d8 100644 --- a/src/main/java/NextLevel/demo/notice/controller/NoticeController.java +++ b/src/main/java/NextLevel/demo/notice/controller/NoticeController.java @@ -1,6 +1,7 @@ package NextLevel.demo.notice.controller; import NextLevel.demo.common.SuccessResponse; +import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.notice.dto.ResponseNoticeDto; import NextLevel.demo.notice.dto.SaveNoticeDto; import NextLevel.demo.notice.entity.NoticeEntity; @@ -52,8 +53,9 @@ public ResponseEntity updateNotice(@ModelAttribute SaveNoticeDto dto, @PathVa } @PostMapping("/admin/notice/{id}") + @ImgTransaction public ResponseEntity removeNotice(@PathVariable("id") Long id) { - noticeService.removeNotice(id); + noticeService.removeNotice(id, null); return ResponseEntity.ok(new SuccessResponse("success", null)); } } diff --git a/src/main/java/NextLevel/demo/notice/service/NoticeService.java b/src/main/java/NextLevel/demo/notice/service/NoticeService.java index 250419f..4df0dcf 100644 --- a/src/main/java/NextLevel/demo/notice/service/NoticeService.java +++ b/src/main/java/NextLevel/demo/notice/service/NoticeService.java @@ -3,6 +3,7 @@ import NextLevel.demo.exception.CustomException; import NextLevel.demo.exception.ErrorCode; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgService; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.notice.dto.SaveNoticeDto; @@ -34,13 +35,13 @@ public List getAllNotice() { @ImgTransaction @Transactional - public void addNotice(SaveNoticeDto dto, ArrayList imgPaths) { + public void addNotice(SaveNoticeDto dto, ImgPath imgPath) { NoticeEntity newNotice = dto.toEntity(); List newImgs = new ArrayList(); if(dto.getImgs()!=null && !dto.getImgs().isEmpty()) { dto.getImgs().stream().forEach(file -> { if(file != null && !file.isEmpty()) - newImgs.add(imgService.saveImg(file, imgPaths)); + newImgs.add(imgService.saveImg(file, imgPath)); }); newNotice.setImgs( newImgs @@ -76,7 +77,7 @@ public void updateNotice(SaveNoticeDto dto) { @ImgTransaction @Transactional - public void updateImg(Long noticeId, List imgFiles, ArrayList imgPaths) { + public void updateImg(Long noticeId, List imgFiles, ImgPath imgPath) { NoticeEntity notice = noticeRepository.findById(noticeId).orElseThrow( () -> {throw new CustomException(ErrorCode.NOT_FOUND, "notice");} ); @@ -84,7 +85,7 @@ public void updateImg(Long noticeId, List imgFiles, ArrayList oldImgs = oldNoticeImgs.stream().map(NoticeImgEntity::getImg).toList(); List newImgs = new ArrayList<>(); - imgFiles.stream().forEach(i->newImgs.add(imgService.saveImg(i, imgPaths))); + imgFiles.stream().forEach(i->newImgs.add(imgService.saveImg(i, imgPath))); List newNoticeImgs = new ArrayList<>(); newImgs .stream() @@ -100,11 +101,11 @@ public void updateImg(Long noticeId, List imgFiles, ArrayListimgService.deleteImg(img, imgPath)); } //@Transactional - public void removeNotice(Long noticeId) { + public void removeNotice(Long noticeId, ImgPath imgPath) { NoticeEntity notice = noticeRepository.findById(noticeId).orElseThrow( () -> {throw new CustomException(ErrorCode.NOT_FOUND, "notice");} ); @@ -112,6 +113,6 @@ public void removeNotice(Long noticeId) { noticeRepository.delete(notice); - oldImgs.forEach(imgService::deleteImg); + oldImgs.forEach((img)->imgService.deleteImg(img, imgPath)); } } diff --git a/src/main/java/NextLevel/demo/project/notice/service/ProjectNoticeService.java b/src/main/java/NextLevel/demo/project/notice/service/ProjectNoticeService.java index d542d7b..2cebd4e 100644 --- a/src/main/java/NextLevel/demo/project/notice/service/ProjectNoticeService.java +++ b/src/main/java/NextLevel/demo/project/notice/service/ProjectNoticeService.java @@ -3,6 +3,7 @@ import NextLevel.demo.exception.CustomException; import NextLevel.demo.exception.ErrorCode; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgServiceImpl; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.project.notice.dto.request.SaveProjectNoticeRequestDto; @@ -36,20 +37,20 @@ public List getAllNotice(Long projectId){ @Transactional @ImgTransaction - public void saveProjectNotice(SaveProjectNoticeRequestDto dto, ArrayList imgPaths) { + public void saveProjectNotice(SaveProjectNoticeRequestDto dto, ImgPath imgPath) { ProjectEntity project = projectValidateService.getProjectEntity(dto.getProjectId()); if(!project.getUser().getId().equals(dto.getUserId())) throw new CustomException(ErrorCode.NOT_AUTHOR); - ImgEntity savedImg = imgService.saveImg(dto.getImg(), imgPaths); + ImgEntity savedImg = imgService.saveImg(dto.getImg(), imgPath); projectNoticeRepository.save(dto.toEntity(savedImg, project)); } @Transactional @ImgTransaction - public void updateNotice(SaveProjectNoticeRequestDto dto, ArrayList imgPaths) { + public void updateNotice(SaveProjectNoticeRequestDto dto, ImgPath imgPath) { ProjectNoticeEntity notice = projectNoticeRepository.findByIdWithProject(dto.getNoticeId()).orElseThrow( ()->{return new CustomException(ErrorCode.NOT_FOUND, "notice");} ); @@ -60,7 +61,7 @@ public void updateNotice(SaveProjectNoticeRequestDto dto, ArrayList imgPa notice.update(dto); if(dto.getImg() != null && !dto.getImg().isEmpty()) { - ImgEntity savedImg = imgService.updateImg(dto.getImg(), notice.getImg(), imgPaths); + ImgEntity savedImg = imgService.updateImg(dto.getImg(), notice.getImg(), imgPath); if(notice.getImg() == null) notice.setImg(savedImg); } diff --git a/src/main/java/NextLevel/demo/project/project/service/ProjectService.java b/src/main/java/NextLevel/demo/project/project/service/ProjectService.java index 46f1fac..ec5dd32 100644 --- a/src/main/java/NextLevel/demo/project/project/service/ProjectService.java +++ b/src/main/java/NextLevel/demo/project/project/service/ProjectService.java @@ -5,6 +5,7 @@ import NextLevel.demo.funding.service.FundingRollbackService; import NextLevel.demo.funding.service.FundingValidateService; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgServiceImpl; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.project.project.dto.request.CreateProjectDto; @@ -50,18 +51,18 @@ public class ProjectService { // 추가 @ImgTransaction @Transactional - public void save(CreateProjectDto dto, ArrayList imgPaths) { + public void save(CreateProjectDto dto, ImgPath imgPath) { // user 처리 UserEntity user = userValidateService.getUserInfoWithAccessToken(dto.getUserId()); validateUser(user); if(dto.getTitleImg() == null || dto.getTitleImg().isEmpty()) throw new CustomException(ErrorCode.INPUT_REQUIRED_PARAMETER); - ImgEntity img = imgService.saveImg(dto.getTitleImg(), imgPaths); + ImgEntity img = imgService.saveImg(dto.getTitleImg(), imgPath); ProjectEntity newProject = projectRepository.save(dto.toProjectEntity(user, img)); - projectStoryService.saveNewProjectStory(newProject, dto.getImgs(), imgPaths); + projectStoryService.saveNewProjectStory(newProject, dto.getImgs(), imgPath); tagService.saveNewTags(newProject, dto.getTags()); } private void validateUser(UserEntity user) { @@ -72,7 +73,7 @@ private void validateUser(UserEntity user) { // 수정 @ImgTransaction @Transactional - public void update(CreateProjectDto dto, ArrayList imgPaths) { + public void update(CreateProjectDto dto, ImgPath imgPath) { Optional oldProjectOptional = projectRepository.findByIdWithAll(dto.getId()); if(oldProjectOptional.isEmpty()) @@ -85,7 +86,7 @@ public void update(CreateProjectDto dto, ArrayList imgPaths) { ImgEntity img = oldProject.getTitleImg(); if(dto.getTitleImg() != null) - img = imgService.updateImg(dto.getTitleImg(), oldProject.getTitleImg(), imgPaths); + img = imgService.updateImg(dto.getTitleImg(), oldProject.getTitleImg(), imgPath); // tag 처리 if(dto.getTags() != null && !dto.getTags().isEmpty()) @@ -93,7 +94,7 @@ public void update(CreateProjectDto dto, ArrayList imgPaths) { // img 처리 if(dto.getImgs() != null && !dto.getImgs().isEmpty()) - projectStoryService.updateProjectStory(oldProject, dto.getImgs(), imgPaths); + projectStoryService.updateProjectStory(oldProject, dto.getImgs(), imgPath); projectRepository.save(dto.toProjectEntity(oldProject.getUser(), img)); // 값이 있는 것만 update 형식으로 수정 필요 } @@ -101,7 +102,7 @@ public void update(CreateProjectDto dto, ArrayList imgPaths) { // 삭제 @Transactional @ImgTransaction - public void deleteProject(Long id, ArrayList imgPaths) { + public void deleteProject(Long id, ImgPath imgPath) { Optional oldProjectOptional = projectRepository.findById(id); if(oldProjectOptional.isEmpty()) throw new CustomException(ErrorCode.NOT_FOUND, "project"); @@ -113,7 +114,7 @@ public void deleteProject(Long id, ArrayList imgPaths) { // 다른 soft적 처리 필요한 부분 처리하기 // img 처리 - projectStoryService.updateProjectStory(oldProject, new ArrayList<>(), imgPaths); + projectStoryService.updateProjectStory(oldProject, new ArrayList<>(), imgPath); return; // 아직 구현하지 않음 + soft delete 처리 고민중 ..... } diff --git a/src/main/java/NextLevel/demo/project/story/service/ProjectStoryService.java b/src/main/java/NextLevel/demo/project/story/service/ProjectStoryService.java index 6b3b624..afddb62 100644 --- a/src/main/java/NextLevel/demo/project/story/service/ProjectStoryService.java +++ b/src/main/java/NextLevel/demo/project/story/service/ProjectStoryService.java @@ -1,6 +1,7 @@ package NextLevel.demo.project.story.service; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgServiceImpl; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.project.project.entity.ProjectEntity; @@ -27,13 +28,13 @@ public class ProjectStoryService { @ImgTransaction @Transactional - public void saveNewProjectStory(ProjectEntity project, List imgFiles, ArrayList imgPaths) { + public void saveNewProjectStory(ProjectEntity project, List imgFiles, ImgPath imgPath) { imgFiles.forEach(imgFile -> { projectStoryRepository.save( ProjectStoryEntity .builder() .project(project) - .img(imgService.saveImg(imgFile, imgPaths)) + .img(imgService.saveImg(imgFile, imgPath)) .build() ); }); @@ -41,24 +42,24 @@ public void saveNewProjectStory(ProjectEntity project, List imgFi @Transactional @ImgTransaction - public void updateProjectStory(ProjectEntity project, List imgFiles, ArrayList imgPaths) { + public void updateProjectStory(ProjectEntity project, List imgFiles, ImgPath imgPath) { List oldImgs = project.getStories().stream().map(projectImg -> projectImg.getImg()).toList(); projectStoryRepository.deleteAllByProjectId(project.getId()); - oldImgs.forEach(img->imgService.deleteImg(img)); - saveNewProjectStory(project, imgFiles, imgPaths); + oldImgs.forEach(img->imgService.deleteImg(img, imgPath)); + saveNewProjectStory(project, imgFiles, imgPath); } @ImgTransaction @Transactional - public void updateProjectStory(Long projectId, Long userId, List imgFiles, ArrayList imgPaths){ + public void updateProjectStory(Long projectId, Long userId, List imgFiles, ImgPath imgPath){ if(imgFiles == null || imgFiles.isEmpty()) return; ProjectEntity project = projectValidateService.validateAuthor(projectId, userId); - updateProjectStory(project, imgFiles, imgPaths); + updateProjectStory(project, imgFiles, imgPath); } public List getProjectStory(Long projectId) { diff --git a/src/main/java/NextLevel/demo/social/controller/SocialController.java b/src/main/java/NextLevel/demo/social/controller/SocialController.java index 78c6aea..e61820e 100644 --- a/src/main/java/NextLevel/demo/social/controller/SocialController.java +++ b/src/main/java/NextLevel/demo/social/controller/SocialController.java @@ -31,7 +31,7 @@ public ResponseEntity update(@ModelAttribute RequestSocialCreateDto dto) { @DeleteMapping("/api1/social/{socialId}") public ResponseEntity delete(@PathVariable("socialId") Long socialId) { - socialService.delete(socialId, JWTUtil.getUserIdFromSecurityContext()); + socialService.delete(socialId, JWTUtil.getUserIdFromSecurityContext(), null); return ResponseEntity.ok().body(new SuccessResponse("success", null)); } diff --git a/src/main/java/NextLevel/demo/social/service/SocialService.java b/src/main/java/NextLevel/demo/social/service/SocialService.java index e5d0561..09fe5ca 100644 --- a/src/main/java/NextLevel/demo/social/service/SocialService.java +++ b/src/main/java/NextLevel/demo/social/service/SocialService.java @@ -3,6 +3,7 @@ import NextLevel.demo.exception.CustomException; import NextLevel.demo.exception.ErrorCode; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgService; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.social.dto.RequestSocialCreateDto; @@ -38,16 +39,16 @@ public class SocialService { @ImgTransaction @Transactional - public void create(RequestSocialCreateDto dto, ArrayList imgPaths) { + public void create(RequestSocialCreateDto dto, ImgPath imgPath) { UserEntity user = userValidateService.getUserInfoWithAccessToken(dto.getUserId()); SocialEntity social = socialRepository.save(dto.toEntity(user)); - saveImgs(dto.getImgs(), social, imgPaths); + saveImgs(dto.getImgs(), social, imgPath); } @ImgTransaction @Transactional - public void update(RequestSocialCreateDto dto, ArrayList imgPaths) { + public void update(RequestSocialCreateDto dto, ImgPath imgPath) { SocialEntity social = socialRepository.findById(dto.getId()).orElseThrow( ()->{return new CustomException(ErrorCode.NOT_FOUND, "social");} ); @@ -56,15 +57,16 @@ public void update(RequestSocialCreateDto dto, ArrayList imgPaths) { throw new CustomException(ErrorCode.NOT_AUTHOR); if(dto.getImgs() != null && !dto.getImgs().isEmpty()){ - deleteImgs(social.getId(), social.getImgs().stream().map(SocialImgEntity::getImg).toList()); - saveImgs(dto.getImgs(), social, imgPaths); + deleteImgs(social.getId(), social.getImgs().stream().map(SocialImgEntity::getImg).toList(), imgPath); + saveImgs(dto.getImgs(), social, imgPath); } social.update(dto); } @Transactional - public void delete(Long socialId, Long userId) { + @ImgTransaction + public void delete(Long socialId, Long userId, ImgPath imgPath) { SocialEntity social = socialRepository.findById(socialId).orElseThrow( ()->{return new CustomException(ErrorCode.NOT_FOUND, "social");} ); @@ -72,7 +74,7 @@ public void delete(Long socialId, Long userId) { if(!social.getUser().getId().equals(userId)) throw new CustomException(ErrorCode.NOT_AUTHOR); - deleteImgs(socialId, social.getImgs().stream().map(SocialImgEntity::getImg).toList()); + deleteImgs(socialId, social.getImgs().stream().map(SocialImgEntity::getImg).toList(), imgPath); entityManager.flush(); entityManager.clear(); @@ -85,21 +87,21 @@ public List list(Long userId) { return socials.stream().map(ResponseSocialDto::of).toList(); } - private void saveImgs(List imgFiles, SocialEntity social, ArrayList imgPaths) { + private void saveImgs(List imgFiles, SocialEntity social, ImgPath imgPath) { imgFiles.forEach(imgFile -> socialImgRepository.save( SocialImgEntity .builder() .social(social) - .img(imgService.saveImg(imgFile, imgPaths)) + .img(imgService.saveImg(imgFile, imgPath)) .build() ) ); } - private void deleteImgs(Long socialId, List imgs) { + private void deleteImgs(Long socialId, List imgs, ImgPath imgPath) { socialImgRepository.deleteAllBySocialId(socialId); - imgs.forEach(img->imgService.deleteImg(img)); + imgs.forEach(img->imgService.deleteImg(img, imgPath)); } } diff --git a/src/main/java/NextLevel/demo/user/service/LoginService.java b/src/main/java/NextLevel/demo/user/service/LoginService.java index c1184e1..801f7e2 100644 --- a/src/main/java/NextLevel/demo/user/service/LoginService.java +++ b/src/main/java/NextLevel/demo/user/service/LoginService.java @@ -1,6 +1,7 @@ package NextLevel.demo.user.service; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgServiceImpl; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.user.dto.RequestUserCreateDto; @@ -64,13 +65,13 @@ public UserDetailEntity socialLogin(RequestUserCreateDto socialLoginDto) { // user UserController : post register @ImgTransaction @Transactional - public UserDetailEntity register(@Valid RequestUserCreateDto dto, ArrayList imgPaths) { + public UserDetailEntity register(@Valid RequestUserCreateDto dto, ImgPath imgPath) { userValidateService.checkEmailAndNickNameElseThrow(dto.getEmail(), dto.getNickName()); // save img get uri ImgEntity savedImg = null; try { - savedImg = imgService.saveImg(dto.getImg(), imgPaths); + savedImg = imgService.saveImg(dto.getImg(), imgPath); }catch (CustomException e) {;} dto.setImgEntity(savedImg); diff --git a/src/main/java/NextLevel/demo/user/service/UserService.java b/src/main/java/NextLevel/demo/user/service/UserService.java index 6482f1b..44e04d7 100644 --- a/src/main/java/NextLevel/demo/user/service/UserService.java +++ b/src/main/java/NextLevel/demo/user/service/UserService.java @@ -2,6 +2,7 @@ import NextLevel.demo.exception.CustomException; import NextLevel.demo.exception.ErrorCode; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgService; import NextLevel.demo.img.service.ImgTransaction; import NextLevel.demo.project.project.dto.response.ResponseProjectListDto; @@ -94,7 +95,7 @@ public void updateUserPassword(RequestUpdatePasswordDto dto) { @ImgTransaction @Transactional - public UserEntity updateUserImg(Long userId, MultipartFile img, ArrayList imgPaths) { + public UserEntity updateUserImg(Long userId, MultipartFile img, ImgPath imgPath) { UserEntity oldUser = userRepository.findById(userId).orElseThrow( ()->{throw new CustomException(ErrorCode.ACCESS_TOKEN_ERROR);} ); @@ -102,9 +103,9 @@ public UserEntity updateUserImg(Long userId, MultipartFile img, ArrayList throw new CustomException(ErrorCode.INPUT_REQUIRED_PARAMETER); if(oldUser.getImg() == null) - oldUser.setImg(imgService.saveImg(img, imgPaths)); + oldUser.setImg(imgService.saveImg(img, imgPath)); else - imgService.updateImg(img, oldUser.getImg(), imgPaths); + imgService.updateImg(img, oldUser.getImg(), imgPath); return oldUser; } diff --git a/src/test/java/NextLevel/demo/img/ImgServiceTest.java b/src/test/java/NextLevel/demo/img/ImgServiceTest.java index 764f078..27b94c9 100644 --- a/src/test/java/NextLevel/demo/img/ImgServiceTest.java +++ b/src/test/java/NextLevel/demo/img/ImgServiceTest.java @@ -72,32 +72,32 @@ public void addImgNumberTest() throws Exception { ); } - @Test - public void saveImgTest() throws Exception { - Field maxImgLen = ImgServiceImpl.class.getDeclaredField("MAX_IMG_LEN"); - maxImgLen.setAccessible(true); - maxImgLen.set(imgService, 20); - String fileName = "fileName"; - byte[] imgData = "img byte".getBytes(); - MultipartFile imgFile = new MockMultipartFile(fileName, fileName, "content type", imgData); - Mockito.mockStatic(Files.class).when(()->Files.write(Mockito.any(), Mockito.any(byte[].class))).thenReturn(Paths.get("uri")); - Mockito.mockStatic(Paths.class).when(()->Paths.get(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).then( - (joinPoint)->{ - String defaultPath = (String) joinPoint.getArguments()[1]; - String imgPath = (String) joinPoint.getArguments()[2]; - return Paths.get(defaultPath + imgPath); - } - ); - - ArrayList paths = new ArrayList<>(); - ImgEntity imgEntity = imgService.saveImg(imgFile, paths); - - Assertions.assertAll( - ()->Assertions.assertTrue(imgEntity != null,"return img entity is not null"), - ()->Assertions.assertTrue(imgEntity.getUri().contains(fileName), "img entity contains uri"), - ()->Assertions.assertTrue(paths.size() == 1, "check ArrayList") - ); - } +// @Test +// public void saveImgTest() throws Exception { +// Field maxImgLen = ImgServiceImpl.class.getDeclaredField("MAX_IMG_LEN"); +// maxImgLen.setAccessible(true); +// maxImgLen.set(imgService, 20); +// String fileName = "fileName"; +// byte[] imgData = "img byte".getBytes(); +// MultipartFile imgFile = new MockMultipartFile(fileName, fileName, "content type", imgData); +// Mockito.mockStatic(Files.class).when(()->Files.write(Mockito.any(), Mockito.any(byte[].class))).thenReturn(Paths.get("uri")); +// Mockito.mockStatic(Paths.class).when(()->Paths.get(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).then( +// (joinPoint)->{ +// String defaultPath = (String) joinPoint.getArguments()[1]; +// String imgPath = (String) joinPoint.getArguments()[2]; +// return Paths.get(defaultPath + imgPath); +// } +// ); +// +// ArrayList paths = new ArrayList<>(); +// ImgEntity imgEntity = imgService.saveImg(imgFile, paths); +// +// Assertions.assertAll( +// ()->Assertions.assertTrue(imgEntity != null,"return img entity is not null"), +// ()->Assertions.assertTrue(imgEntity.getUri().contains(fileName), "img entity contains uri"), +// ()->Assertions.assertTrue(paths.size() == 1, "check ArrayList") +// ); +// } // @Test // // testImg 폴더에 실제 저장! (resources/static/testImg 폴더 반듯이 필요!!) diff --git a/src/test/java/NextLevel/demo/user/service/userService/UserServiceTest.java b/src/test/java/NextLevel/demo/user/service/userService/UserServiceTest.java index a78768d..1a4a8e9 100644 --- a/src/test/java/NextLevel/demo/user/service/userService/UserServiceTest.java +++ b/src/test/java/NextLevel/demo/user/service/userService/UserServiceTest.java @@ -3,6 +3,7 @@ import NextLevel.demo.exception.CustomException; import NextLevel.demo.exception.ErrorCode; import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgService; import NextLevel.demo.user.dto.user.request.RequestUpdateUserInfoDto; import NextLevel.demo.user.entity.UserEntity; @@ -146,7 +147,7 @@ public void updateUserImgWithNull() { Assertions.assertThrows( CustomException.class, - ()->userService.updateUserImg(1L, null, new ArrayList()) + ()->userService.updateUserImg(1L, null, new ImgPath()) ); } @@ -158,7 +159,7 @@ public void updateUserImgSuccessWhenHaveOldImg() { Mockito.when(userRepository.findById(Mockito.anyLong())).thenReturn(Optional.of(mockUser)); ArgumentCaptor> captor = ArgumentCaptor.forClass(ArrayList.class); - UserEntity user = userService.updateUserImg(0L, Mockito.mock(MultipartFile.class) , new ArrayList()); + UserEntity user = userService.updateUserImg(0L, Mockito.mock(MultipartFile.class) , new ImgPath()); Assertions.assertEquals(oldImg, user.getImg()); } @@ -168,7 +169,7 @@ public void updateUserImgSuccessWhenNoOldImg() { mockUser.setImg(null); Mockito.when(userRepository.findById(Mockito.anyLong())).thenReturn(Optional.of(mockUser)); - UserEntity user = userService.updateUserImg(0L, Mockito.mock(MultipartFile.class), new ArrayList()); + UserEntity user = userService.updateUserImg(0L, Mockito.mock(MultipartFile.class), new ImgPath()); Assertions.assertEquals(mockImg, user.getImg()); } From 1b8883758b452d16951a175ce57038bae5adb4dd Mon Sep 17 00:00:00 2001 From: kwonhee1 Date: Wed, 24 Sep 2025 12:13:48 +0900 Subject: [PATCH 2/3] add test setting --- build.gradle | 4 ++ src/main/resources/application.yml | 19 ++++++- .../NextLevel/demo/img/ImgServiceTest.java | 26 ---------- src/test/java/NextLevel/demo/img/ImgTest.java | 50 +++++++++++++++++++ 4 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 src/test/java/NextLevel/demo/img/ImgTest.java diff --git a/build.gradle b/build.gradle index b4043e5..43871e6 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,10 @@ dependencies { // email implementation("com.sun.mail:javax.mail:1.6.2") + + // test h2 db + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'com.h2database:h2' } tasks.named('test') { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f563300..ab202f5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -122,4 +122,21 @@ DOMAIN: nextlevel.r-e.kr FRONT: baseUrl: - https://nextlevel.r-e.kr \ No newline at end of file + https://nextlevel.r-e.kr + +--- +spring: + config: + activate: + on-profile: test + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL + username: sa + password: + jpa: + properties: + hibernate: + show_sql: true + hibernate: + ddl-auto: create-drop diff --git a/src/test/java/NextLevel/demo/img/ImgServiceTest.java b/src/test/java/NextLevel/demo/img/ImgServiceTest.java index 27b94c9..d95ba8d 100644 --- a/src/test/java/NextLevel/demo/img/ImgServiceTest.java +++ b/src/test/java/NextLevel/demo/img/ImgServiceTest.java @@ -72,32 +72,6 @@ public void addImgNumberTest() throws Exception { ); } -// @Test -// public void saveImgTest() throws Exception { -// Field maxImgLen = ImgServiceImpl.class.getDeclaredField("MAX_IMG_LEN"); -// maxImgLen.setAccessible(true); -// maxImgLen.set(imgService, 20); -// String fileName = "fileName"; -// byte[] imgData = "img byte".getBytes(); -// MultipartFile imgFile = new MockMultipartFile(fileName, fileName, "content type", imgData); -// Mockito.mockStatic(Files.class).when(()->Files.write(Mockito.any(), Mockito.any(byte[].class))).thenReturn(Paths.get("uri")); -// Mockito.mockStatic(Paths.class).when(()->Paths.get(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).then( -// (joinPoint)->{ -// String defaultPath = (String) joinPoint.getArguments()[1]; -// String imgPath = (String) joinPoint.getArguments()[2]; -// return Paths.get(defaultPath + imgPath); -// } -// ); -// -// ArrayList paths = new ArrayList<>(); -// ImgEntity imgEntity = imgService.saveImg(imgFile, paths); -// -// Assertions.assertAll( -// ()->Assertions.assertTrue(imgEntity != null,"return img entity is not null"), -// ()->Assertions.assertTrue(imgEntity.getUri().contains(fileName), "img entity contains uri"), -// ()->Assertions.assertTrue(paths.size() == 1, "check ArrayList") -// ); -// } // @Test // // testImg 폴더에 실제 저장! (resources/static/testImg 폴더 반듯이 필요!!) diff --git a/src/test/java/NextLevel/demo/img/ImgTest.java b/src/test/java/NextLevel/demo/img/ImgTest.java new file mode 100644 index 0000000..993db9c --- /dev/null +++ b/src/test/java/NextLevel/demo/img/ImgTest.java @@ -0,0 +1,50 @@ +package NextLevel.demo.img; + +import NextLevel.demo.img.entity.ImgEntity; +import NextLevel.demo.img.service.ImgPath; +import NextLevel.demo.img.service.ImgService; +import NextLevel.demo.img.service.ImgTransaction; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Files; +import java.nio.file.Paths; + +@SpringBootTest +@ActiveProfiles("test") +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@ExtendWith(MockitoExtension.class) +public class ImgTest { + + @Autowired + private ImgService imgService; + + private MultipartFile mockImgFile; + private String imgName = "imgName"; + private byte[] imgData = "imgData".getBytes(); + + @BeforeEach + public void setUp() { + // db 초기화 + Mockito.mockStatic(Files.class).when(()->Files.write(Mockito.any(), Mockito.any(byte[].class))).thenReturn(Paths.get("uri")); + mockImgFile = new MockMultipartFile(imgName, imgName, "content type", imgData); + } + + @Test + @ImgTransaction + public void saveImgTest(ImgPath imgPath) { + ImgEntity img = imgService.saveImg(mockImgFile, imgPath); + Assertions.assertThat(img.getUri()).isEqualTo(imgName); + } + +} From a933f0c8cac36749c8b07209bab3f6d9fec98212 Mon Sep 17 00:00:00 2001 From: kwonhee1 Date: Sun, 28 Sep 2025 11:32:29 +0900 Subject: [PATCH 3/3] add img test gradle --- build.gradle | 5 ++ src/main/resources/application.yml | 4 ++ src/test/java/NextLevel/demo/img/ImgTest.java | 54 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/build.gradle b/build.gradle index 43871e6..dce819e 100644 --- a/build.gradle +++ b/build.gradle @@ -60,8 +60,13 @@ dependencies { implementation("com.sun.mail:javax.mail:1.6.2") // test h2 db +<<<<<<< Updated upstream testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'com.h2database:h2' +======= + testImplementation 'com.h2database:h2' + +>>>>>>> Stashed changes } tasks.named('test') { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ab202f5..546cc97 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -139,4 +139,8 @@ spring: hibernate: show_sql: true hibernate: +<<<<<<< Updated upstream ddl-auto: create-drop +======= + ddl-auto: update +>>>>>>> Stashed changes diff --git a/src/test/java/NextLevel/demo/img/ImgTest.java b/src/test/java/NextLevel/demo/img/ImgTest.java index 993db9c..a4c28be 100644 --- a/src/test/java/NextLevel/demo/img/ImgTest.java +++ b/src/test/java/NextLevel/demo/img/ImgTest.java @@ -1,20 +1,41 @@ package NextLevel.demo.img; import NextLevel.demo.img.entity.ImgEntity; +<<<<<<< Updated upstream import NextLevel.demo.img.service.ImgPath; import NextLevel.demo.img.service.ImgService; import NextLevel.demo.img.service.ImgTransaction; import org.assertj.core.api.Assertions; +======= +import NextLevel.demo.img.repository.ImgRepository; +import NextLevel.demo.img.service.ImgPath; +import NextLevel.demo.img.service.ImgService; +import NextLevel.demo.img.service.ImgServiceImpl; +import NextLevel.demo.img.service.ImgTransaction; +import org.junit.jupiter.api.Assertions; +>>>>>>> Stashed changes import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; +<<<<<<< Updated upstream +======= +import org.mockito.Spy; +>>>>>>> Stashed changes import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.context.SpringBootTest; +<<<<<<< Updated upstream import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.ActiveProfiles; +======= +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.springframework.transaction.annotation.Transactional; +>>>>>>> Stashed changes import org.springframework.web.multipart.MultipartFile; import java.nio.file.Files; @@ -26,8 +47,14 @@ @ExtendWith(MockitoExtension.class) public class ImgTest { +<<<<<<< Updated upstream @Autowired private ImgService imgService; +======= + private ImgService imgService; + @MockitoSpyBean + private ImgRepository imgRepository; +>>>>>>> Stashed changes private MultipartFile mockImgFile; private String imgName = "imgName"; @@ -35,16 +62,43 @@ public class ImgTest { @BeforeEach public void setUp() { +<<<<<<< Updated upstream +======= + Mockito.lenient().when(imgRepository.getImgCount(Mockito.anyInt(), Mockito.anyString())).thenReturn(1L); // img count return 1; + imgService = new ImgServiceImpl(imgRepository); +>>>>>>> Stashed changes // db 초기화 Mockito.mockStatic(Files.class).when(()->Files.write(Mockito.any(), Mockito.any(byte[].class))).thenReturn(Paths.get("uri")); mockImgFile = new MockMultipartFile(imgName, imgName, "content type", imgData); } +<<<<<<< Updated upstream @Test @ImgTransaction public void saveImgTest(ImgPath imgPath) { ImgEntity img = imgService.saveImg(mockImgFile, imgPath); Assertions.assertThat(img.getUri()).isEqualTo(imgName); +======= + @Transactional + @ImgTransaction + public ImgEntity testImgFail(MultipartFile imgFile, RuntimeException e, ImgPath imgPath) { + ImgEntity img = imgService.saveImg(imgFile, imgPath); + if(e != null) + throw e; + return img; + } + + @Test + public void saveImgTest() { + ImgEntity img = testImgFail(mockImgFile, null, null); + // db에 저장이 되었는지 + 파일이 존재하는지 + ImgEntity dbImg = imgRepository.findById(img.getId()).get(); + + Assertions.assertAll( + ()->Assertions.assertEquals(dbImg.getId(), img.getId(), "dbImg is not equal input img"), + ()->Assertions.assertEquals(dbImg.getUri(), img.getUri(), "dbImg not equal input img") + ); +>>>>>>> Stashed changes } }