diff --git a/.gitignore b/.gitignore index b01a9b3..2ff654f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ out/ build/generated/ application-local.properties application-test.properties -application-s3.properties \ No newline at end of file +application-s3.properties +test_img_dir \ No newline at end of file diff --git a/build.gradle b/build.gradle index d393a9f..f017ab5 100644 --- a/build.gradle +++ b/build.gradle @@ -41,14 +41,19 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' + //FCM 추가 implementation 'com.google.firebase:firebase-admin:9.2.0' + + // Health Check implementation 'org.springframework.boot:spring-boot-starter-actuator' + // AWS 서비스 연동을 쉽게 도와주는 Spring Cloud 통합 패키지 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + compileOnly 'org.projectlombok:lombok' +// runtimeOnly 'com.mysql:mysql-connector-j' + runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' - - runtimeOnly 'com.mysql:mysql-connector-j' -// runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' @@ -65,4 +70,4 @@ sourceSets { tasks.named('test') { useJUnitPlatform() -} +} \ No newline at end of file diff --git a/deploy/local.init.sql b/deploy/local.init.sql index b017b87..8ac668a 100644 --- a/deploy/local.init.sql +++ b/deploy/local.init.sql @@ -22,9 +22,9 @@ INSERT INTO users (user_id, created_date, updated_date, activity_area, birthday, VALUES (default, null, null, 'GYEONGGI', 'birthday', 'email', 'fcm_token', 'MAN', 180, null, 'BOTH', 'MANAGER', 'name', 'password', 'phone', 'refresh_token12345678910', 'USER', 'GK', 70); INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id) -VALUES (default, null, null, 'PRESIDENT', '2024-12-11', 2, 2, 1, 1); +VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 1, 1); ---- 3번 동아리에 가입한 유저들 INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id) -VALUES (default, null, null, 'VICE_PRESIDENT', '2024-12-11', 2, 2, 3, 3); +VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 3); INSERT INTO user_club (id, created_date, updated_date, club_role, join_date, match_count, schedule_count, club_id, user_id) -VALUES (default, null, null, 'PRESIDENT', '2024-12-11', 2, 2, 3, 4); \ No newline at end of file +VALUES (default, null, null, 'STAFF', '2024-12-11', 2, 2, 3, 4); \ No newline at end of file diff --git a/src/main/java/com/example/moim/club/dto/request/ClubImgInput.java b/src/main/java/com/example/moim/club/dto/request/ClubImgInput.java index 5fdc655..44c46f7 100644 --- a/src/main/java/com/example/moim/club/dto/request/ClubImgInput.java +++ b/src/main/java/com/example/moim/club/dto/request/ClubImgInput.java @@ -1,6 +1,6 @@ package com.example.moim.club.dto.request; -import com.example.moim.global.util.ValidFile; +import com.example.moim.global.util.file.exception.ValidFile; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java b/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java index d115e87..7dadc51 100644 --- a/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java +++ b/src/main/java/com/example/moim/club/service/ClubCommandServiceImpl.java @@ -1,7 +1,6 @@ package com.example.moim.club.service; import com.example.moim.club.dto.request.*; -import com.example.moim.club.dto.response.ClubOutput; import com.example.moim.club.dto.response.ClubSaveOutput; import com.example.moim.club.dto.response.ClubUpdateOutput; import com.example.moim.club.dto.response.UserClubOutput; @@ -14,8 +13,8 @@ import com.example.moim.club.repository.UserClubRepository; import com.example.moim.global.enums.ClubRole; import com.example.moim.global.exception.ResponseCode; -import com.example.moim.global.util.FileStore; import com.example.moim.global.util.TextUtils; +import com.example.moim.global.util.file.service.FileService; import com.example.moim.notification.dto.ClubJoinEvent; import com.example.moim.user.entity.User; import com.example.moim.user.repository.UserRepository; @@ -36,13 +35,13 @@ public class ClubCommandServiceImpl implements ClubCommandService { private final ClubRepository clubRepository; private final UserClubRepository userClubRepository; private final UserRepository userRepository; - private final FileStore fileStore; + private final FileService fileService; private final ClubSearchRepository clubSearchRepository; private final ApplicationEventPublisher eventPublisher; @Transactional public ClubSaveOutput saveClub(User user, ClubInput clubInput) throws IOException { - Club club = clubRepository.save(Club.createClub(clubInput, fileStore.storeFile(clubInput.getProfileImg()))); + Club club = clubRepository.save(Club.createClub(clubInput, fileService.upload(clubInput.getProfileImg(), "/club-profile"))); // 검색을 위한 저장 saveClubSearch(club); @@ -65,7 +64,7 @@ public ClubUpdateOutput updateClub(User user, ClubUpdateInput clubUpdateInput, L throw new ClubControllerAdvice(ResponseCode.CLUB_PASSWORD_INCORRECT); } - club.updateClub(clubUpdateInput, fileStore.storeFile(clubUpdateInput.getProfileImg())); + club.updateClub(clubUpdateInput, fileService.upload(clubUpdateInput.getProfileImg(), "/club-profile")); // 검색 정보 동기화를 위한 처리 club.getClubSearch().updateFrom(club); List userList = userClubRepository.findAllByClub(club).stream().map(UserClubOutput::new).toList(); diff --git a/src/main/java/com/example/moim/global/util/FileStore.java b/src/main/java/com/example/moim/global/util/FileStore.java deleted file mode 100644 index 0bdb9be..0000000 --- a/src/main/java/com/example/moim/global/util/FileStore.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.example.moim.global.util; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.UUID; -@Component -public class FileStore { - - @Value("${file.dir}") - private String fileDir; - - public String getFullPath(String filename) { - return fileDir + filename; - } - - public void storeFiles(List multipartFiles) throws IOException { - for (MultipartFile multipartFile : multipartFiles) { - if (!multipartFile.isEmpty()) { - storeFile(multipartFile); - } - } - } - - public String storeFile(MultipartFile multipartFile) throws IOException { - if (multipartFile == null ||multipartFile.isEmpty()) { - return null; - } - String fullPath = getFullPath(UUID.randomUUID() + "." + extractExt(multipartFile.getOriginalFilename())); - multipartFile.transferTo(new File(fullPath)); - return fullPath; - } - - private String extractExt(String originalFilename) { - int pos = originalFilename.lastIndexOf("."); - return originalFilename.substring(pos + 1); - } -} diff --git a/src/main/java/com/example/moim/global/util/file/config/AwsS3Config.java b/src/main/java/com/example/moim/global/util/file/config/AwsS3Config.java new file mode 100644 index 0000000..855a783 --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/config/AwsS3Config.java @@ -0,0 +1,39 @@ +package com.example.moim.global.util.file.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Slf4j +@Profile("!test") +@Configuration +public class AwsS3Config { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public BasicAWSCredentials basicAWSCredentials() { + return new BasicAWSCredentials(accessKey, secretKey); + } + + @Bean + public AmazonS3 amazonS3(BasicAWSCredentials basicAWSCredentials) { + return AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials)) + .build(); + } +} diff --git a/src/main/java/com/example/moim/global/util/file/config/LocalFileConfig.java b/src/main/java/com/example/moim/global/util/file/config/LocalFileConfig.java new file mode 100644 index 0000000..8c5fcbf --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/config/LocalFileConfig.java @@ -0,0 +1,30 @@ +package com.example.moim.global.util.file.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import java.io.File; + +@Slf4j +@Profile("test") +@Configuration +public class LocalFileConfig { + + @Value("${local.upload.directory}") + private String fileUploadDir; + +// @Bean +// public File file() { +// +// File directory = new File(fileUploadDir); +// +// if (!directory.exists()) { +// directory.mkdir(); +// } +// +// return directory; +// } +} diff --git a/src/main/java/com/example/moim/global/util/ValidFileValidator.java b/src/main/java/com/example/moim/global/util/file/exception/FileValidator.java similarity index 69% rename from src/main/java/com/example/moim/global/util/ValidFileValidator.java rename to src/main/java/com/example/moim/global/util/file/exception/FileValidator.java index 8b52232..7022b84 100644 --- a/src/main/java/com/example/moim/global/util/ValidFileValidator.java +++ b/src/main/java/com/example/moim/global/util/file/exception/FileValidator.java @@ -1,10 +1,10 @@ -package com.example.moim.global.util; +package com.example.moim.global.util.file.exception; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import org.springframework.web.multipart.MultipartFile; -public class ValidFileValidator implements ConstraintValidator { +public class FileValidator implements ConstraintValidator { @Override public boolean isValid(MultipartFile file, ConstraintValidatorContext context) { diff --git a/src/main/java/com/example/moim/global/util/ValidFile.java b/src/main/java/com/example/moim/global/util/file/exception/ValidFile.java similarity index 83% rename from src/main/java/com/example/moim/global/util/ValidFile.java rename to src/main/java/com/example/moim/global/util/file/exception/ValidFile.java index 34bd712..ed6dcbc 100644 --- a/src/main/java/com/example/moim/global/util/ValidFile.java +++ b/src/main/java/com/example/moim/global/util/file/exception/ValidFile.java @@ -1,4 +1,4 @@ -package com.example.moim.global.util; +package com.example.moim.global.util.file.exception; import jakarta.validation.Constraint; import jakarta.validation.Payload; @@ -10,7 +10,7 @@ @Target(value = ElementType.FIELD) @Retention(value = RetentionPolicy.RUNTIME) -@Constraint(validatedBy = ValidFileValidator.class) +@Constraint(validatedBy = FileValidator.class) public @interface ValidFile { String message() default "유효하지 않은 파일입니다."; diff --git a/src/main/java/com/example/moim/global/util/file/exception/advice/AwsS3ControllerAdvice.java b/src/main/java/com/example/moim/global/util/file/exception/advice/AwsS3ControllerAdvice.java new file mode 100644 index 0000000..3f3d8bc --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/exception/advice/AwsS3ControllerAdvice.java @@ -0,0 +1,10 @@ +package com.example.moim.global.util.file.exception.advice; + +import com.example.moim.global.exception.GeneralException; +import com.example.moim.global.exception.ResponseCode; + +public class AwsS3ControllerAdvice extends GeneralException { + public AwsS3ControllerAdvice(ResponseCode responseCode) { + super(responseCode); + } +} diff --git a/src/main/java/com/example/moim/global/util/file/exception/advice/LocalFileControllerAdvice.java b/src/main/java/com/example/moim/global/util/file/exception/advice/LocalFileControllerAdvice.java new file mode 100644 index 0000000..710e49d --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/exception/advice/LocalFileControllerAdvice.java @@ -0,0 +1,10 @@ +package com.example.moim.global.util.file.exception.advice; + +import com.example.moim.global.exception.GeneralException; +import com.example.moim.global.exception.ResponseCode; + +public class LocalFileControllerAdvice extends GeneralException { + public LocalFileControllerAdvice(ResponseCode responseCode) { + super(responseCode); + } +} diff --git a/src/main/java/com/example/moim/global/util/file/model/AwsS3.java b/src/main/java/com/example/moim/global/util/file/model/AwsS3.java new file mode 100644 index 0000000..b20b378 --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/model/AwsS3.java @@ -0,0 +1,14 @@ +package com.example.moim.global.util.file.model; + +import lombok.*; + +@Builder +@Getter @Setter +@NoArgsConstructor +@AllArgsConstructor +public class AwsS3 { + + private String key; + private String path; + +} diff --git a/src/main/java/com/example/moim/global/util/file/service/AwsS3FileService.java b/src/main/java/com/example/moim/global/util/file/service/AwsS3FileService.java new file mode 100644 index 0000000..e381bb5 --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/service/AwsS3FileService.java @@ -0,0 +1,78 @@ +package com.example.moim.global.util.file.service; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.example.moim.global.exception.ResponseCode; +import com.example.moim.global.util.file.model.AwsS3; +import com.example.moim.global.util.file.exception.advice.AwsS3ControllerAdvice; +import com.example.moim.global.util.uuid.UuidHolder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +//@Service +//@Profile("!test") +@RequiredArgsConstructor +public class AwsS3FileService implements FileService { + + private final AmazonS3 amazonS3; + private final UuidHolder uuidHolder; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + public String upload(MultipartFile multipartFile, String directoryName) { + + // 확장자 가져오기 + String originalFilename = multipartFile.getOriginalFilename(); + String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); + + String fileName = uuidHolder.randomUuid() + extension; + String key = directoryName + "/" + fileName; + log.info("upload key : {}", key); + + // 메타 데이터 설정 + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(multipartFile.getSize()); + metadata.setContentType(multipartFile.getContentType()); + + try { + // 파일 업로드 + PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, key, multipartFile.getInputStream(), metadata) + .withCannedAcl(CannedAccessControlList.PublicRead); // Public Read 권한 설정 + amazonS3.putObject(putObjectRequest); + } catch (IOException e) { + throw new AwsS3ControllerAdvice(ResponseCode.S3_UPLOAD_FAIL); + } + + String path = amazonS3.getUrl(bucket, key).toString(); + log.info("upload path : {}", path); + + AwsS3 result = AwsS3 + .builder() + .key(key) + .path(path) + .build(); + + return result.getPath(); + } + + public void remove(String path) { + + if (!amazonS3.doesObjectExist(bucket, path)) { + log.error("{} 이 존재하지 않습니다.", path); + throw new AwsS3ControllerAdvice(ResponseCode.S3_PATH_NOT_FOUND); + } + amazonS3.deleteObject(bucket, path); + + } + +} diff --git a/src/main/java/com/example/moim/global/util/file/service/FileService.java b/src/main/java/com/example/moim/global/util/file/service/FileService.java new file mode 100644 index 0000000..7b0fda3 --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/service/FileService.java @@ -0,0 +1,10 @@ +package com.example.moim.global.util.file.service; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface FileService { + String upload(MultipartFile multipartFile, String directoryName) throws IOException; + void remove(String path); +} diff --git a/src/main/java/com/example/moim/global/util/file/service/LocalFileService.java b/src/main/java/com/example/moim/global/util/file/service/LocalFileService.java new file mode 100644 index 0000000..a0d0c0c --- /dev/null +++ b/src/main/java/com/example/moim/global/util/file/service/LocalFileService.java @@ -0,0 +1,77 @@ +package com.example.moim.global.util.file.service; + +import com.example.moim.global.exception.ResponseCode; +import com.example.moim.global.util.file.exception.advice.LocalFileControllerAdvice; +import com.example.moim.global.util.uuid.UuidHolder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +@Service +@Slf4j +//@Profile("test") +@RequiredArgsConstructor +public class LocalFileService implements FileService { + + @Value("${local.upload.directory}") + private String fileUploadDir; + private final static int FILE_MAX_SIZE = 104_857_600; // 100MB + + private final UuidHolder uuidHolder; + + @Override + public String upload(MultipartFile multipartFile, String directoryName) throws IOException { + String originalFileName = multipartFile.getOriginalFilename(); + String mimeType = multipartFile.getContentType(); + + // 최대 용량 체크 + if (multipartFile.getSize() > FILE_MAX_SIZE) { + throw new LocalFileControllerAdvice(ResponseCode.FILE_MAX_SIZE_OVER); + } + + // MIMETYPE 체크 + if (!fileTypeCheck(mimeType)) { + throw new LocalFileControllerAdvice(ResponseCode.FILE_CONTENT_TYPE_NOT_IMAGE); + } + + String randomFileName = directoryName + "/" + uuidHolder.randomUuid() + originalFileName; + File file = new File(fileUploadDir + "/" + randomFileName); + + File parentDir = file.getParentFile(); + log.info("parentDir : {}", parentDir.getParentFile()); + if(!parentDir.exists() && !parentDir.mkdirs()) { + throw new LocalFileControllerAdvice(ResponseCode.FILE_SAVE_FAIL); + } + + // file 저장 + log.info(String.valueOf(file.exists())); + + if (file.createNewFile()) { + try(FileOutputStream fos = new FileOutputStream(file)) { + fos.write(multipartFile.getBytes()); + } catch (Exception e) { + throw new LocalFileControllerAdvice(ResponseCode.FILE_SAVE_FAIL); + } + } + + return file.getPath(); + } + + private boolean fileTypeCheck(String mimeType) { + return mimeType.startsWith("image"); + } + + @Override + public void remove(String path) { + File file = new File(path); + file.delete(); + } + +} diff --git a/src/main/java/com/example/moim/global/util/uuid/SystemUuidHolder.java b/src/main/java/com/example/moim/global/util/uuid/SystemUuidHolder.java new file mode 100644 index 0000000..b7d4841 --- /dev/null +++ b/src/main/java/com/example/moim/global/util/uuid/SystemUuidHolder.java @@ -0,0 +1,17 @@ +package com.example.moim.global.util.uuid; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Slf4j +//@Profile("!test") +@Component +public class SystemUuidHolder implements UuidHolder { + @Override + public String randomUuid() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/com/example/moim/global/util/uuid/UuidHolder.java b/src/main/java/com/example/moim/global/util/uuid/UuidHolder.java new file mode 100644 index 0000000..7d7c1fb --- /dev/null +++ b/src/main/java/com/example/moim/global/util/uuid/UuidHolder.java @@ -0,0 +1,5 @@ +package com.example.moim.global.util.uuid; + +public interface UuidHolder { + String randomUuid(); +} diff --git a/src/main/java/com/example/moim/user/service/UserService.java b/src/main/java/com/example/moim/user/service/UserService.java index 16e6160..3e22aa2 100644 --- a/src/main/java/com/example/moim/user/service/UserService.java +++ b/src/main/java/com/example/moim/user/service/UserService.java @@ -1,8 +1,8 @@ package com.example.moim.user.service; import com.example.moim.club.repository.UserClubRepository; -import com.example.moim.global.util.FileStore; import com.example.moim.config.jwt.JWTUtil; +import com.example.moim.global.util.file.service.FileService; import com.example.moim.user.dto.*; import com.example.moim.user.entity.User; import com.example.moim.user.repository.UserRepository; @@ -25,7 +25,7 @@ public class UserService { private final BCryptPasswordEncoder bCryptPasswordEncoder; private final AuthenticationManager authenticationManager; private final JWTUtil jwtUtil; - private final FileStore fileStore; + private final FileService fileService; public void signup(SignupInput signupInput) { signupInput.setPassword(bCryptPasswordEncoder.encode(signupInput.getPassword())); @@ -60,13 +60,13 @@ public List findUserClub(User user) { @Transactional public void saveUserInfo(User loginUser, SocialSignupInput socialSignupInput) throws IOException { User user = userRepository.findById(loginUser.getId()).get(); - user.fillUserInfo(socialSignupInput, fileStore.storeFile(socialSignupInput.getImg())); + user.fillUserInfo(socialSignupInput, fileService.upload(socialSignupInput.getImg(), "/user_profile")); } @Transactional public void updateUserInfo(User loginUser, UserUpdateInput userUpdateInput) throws IOException { User user = userRepository.findById(loginUser.getId()).get(); - user.updateUserInfo(userUpdateInput, fileStore.storeFile(userUpdateInput.getImg())); + user.updateUserInfo(userUpdateInput, fileService.upload(userUpdateInput.getImg(), "/user_profile")); } @Transactional diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5f2edb2..f606312 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ -spring.profiles.include=local \ No newline at end of file +spring.profiles.include=local, s3 \ No newline at end of file diff --git a/src/test/java/com/example/moim/club/service/ClubCommandServiceImplTest.java b/src/test/java/com/example/moim/club/service/ClubCommandServiceImplTest.java index 89af9ca..42626bf 100644 --- a/src/test/java/com/example/moim/club/service/ClubCommandServiceImplTest.java +++ b/src/test/java/com/example/moim/club/service/ClubCommandServiceImplTest.java @@ -12,7 +12,7 @@ import com.example.moim.club.repository.UserClubRepository; import com.example.moim.global.enums.*; import com.example.moim.global.exception.ResponseCode; -import com.example.moim.global.util.FileStore; +import com.example.moim.global.util.file.service.FileService; import com.example.moim.notification.dto.ClubJoinEvent; import com.example.moim.user.entity.User; import com.example.moim.user.repository.UserRepository; @@ -44,7 +44,7 @@ class ClubCommandServiceImplTest { @Mock private UserClubRepository userClubRepository; @Mock - private FileStore fileStore; + private FileService fileService; @Mock private ApplicationEventPublisher eventPublisher; @Mock @@ -97,7 +97,7 @@ void saveClub() throws IOException { //when when(clubRepository.save(any(Club.class))).thenReturn(club); when(userClubRepository.save(any(UserClub.class))).thenReturn(userClub); - when(fileStore.storeFile(any())).thenReturn(null); + when(fileService.upload(any(), any(String.class))).thenReturn(null); when(clubSearchRepository.save(any(ClubSearch.class))).thenReturn(clubSearch); ClubSaveOutput clubOutput = clubCommandService.saveClub(new User(), clubInput); @@ -105,7 +105,7 @@ void saveClub() throws IOException { assertThat(clubOutput).isNotNull(); verify(clubRepository, times(1)).save(any(Club.class)); verify(userClubRepository, times(1)).save(any(UserClub.class)); - verify(fileStore, times(1)).storeFile(any()); + verify(fileService, times(1)).upload(any(), any(String.class)); verify(clubSearchRepository, times(1)).save(any(ClubSearch.class)); } @@ -120,7 +120,7 @@ void updateClub() throws IOException { //when when(clubRepository.findById(any(Long.class))).thenReturn(Optional.of(club)); when(userClubRepository.findByClubAndUser(any(Club.class), any(User.class))).thenReturn(Optional.of(userClub)); - when(fileStore.storeFile(null)).thenReturn(null); + when(fileService.upload(any(), any(String.class))).thenReturn(null); ClubUpdateOutput clubOutput = clubCommandService.updateClub(new User(), clubUpdateInput, 1L); //then assertThat(clubOutput).isNotNull(); @@ -128,7 +128,7 @@ void updateClub() throws IOException { assertThat(clubOutput.getExplanation()).isEqualTo("update explanation"); verify(clubRepository, times(1)).findById(any(Long.class)); verify(userClubRepository, times(1)).findByClubAndUser(any(Club.class), any(User.class)); - verify(fileStore, times(1)).storeFile(any()); + verify(fileService, times(1)).upload(any(), any(String.class)); } @Test diff --git a/src/test/java/com/example/moim/global/util/file/service/LocalFileServiceTest.java b/src/test/java/com/example/moim/global/util/file/service/LocalFileServiceTest.java new file mode 100644 index 0000000..4e3ab92 --- /dev/null +++ b/src/test/java/com/example/moim/global/util/file/service/LocalFileServiceTest.java @@ -0,0 +1,64 @@ +package com.example.moim.global.util.file.service; + +import com.example.moim.global.util.uuid.UuidHolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class LocalFileServiceTest { + + @Mock + private UuidHolder testUuidHolder; + @InjectMocks + private LocalFileService localFileService; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(localFileService, "fileUploadDir", "test_img_dir"); + } + + @Test + void upload() throws IOException { + //given + MockMultipartFile mockMultipartFile = new MockMultipartFile("name", "originalName", "image/png", "ddd".getBytes()); + + //when + when(testUuidHolder.randomUuid()).thenReturn("aaaa-aaaa-aaaa"); + + //then + String filePath = localFileService.upload(mockMultipartFile, "/test"); + + assertThat(filePath).contains("/test"); + assertThat(filePath).contains("aaaa-aaaa-aaaa"); + assertThat(filePath).contains("originalName"); + assertThat(new File(filePath).exists()).isTrue(); + + localFileService.remove(filePath); + } + + @Test + void remove() throws IOException { + //given + MockMultipartFile mockMultipartFile = new MockMultipartFile("delete file", "delete originalName", "image/png", "delete".getBytes()); + when(testUuidHolder.randomUuid()).thenReturn("aaaa-aaaa-aaaa"); + String filePath = localFileService.upload(mockMultipartFile, "/test"); + + //when + localFileService.remove(filePath); + + //then + assertThat(new File(filePath).exists()).isFalse(); + } +} \ No newline at end of file