diff --git a/build.gradle b/build.gradle index 98a44e9..001ba26 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,9 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + // S3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.1.RELEASE' + // test testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' diff --git a/src/main/java/donggukthon/volunmate/config/S3Config.java b/src/main/java/donggukthon/volunmate/config/S3Config.java new file mode 100644 index 0000000..2be802a --- /dev/null +++ b/src/main/java/donggukthon/volunmate/config/S3Config.java @@ -0,0 +1,34 @@ +package donggukthon.volunmate.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@Getter +public class S3Config { + @Value("${cloud.aws.access_key}") + private String accessKey; + + @Value("${cloud.aws.secret_key}") + private String secretKey; + + @Value("${cloud.aws.region}") + private String region; + + @Value("${cloud.aws.s3_bucket}") + private String bucket; + + @Bean + public AmazonS3Client amazonS3Client() { + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region).enablePathStyleAccess() + .build(); + } +} diff --git a/src/main/java/donggukthon/volunmate/config/WebMvcConfig.java b/src/main/java/donggukthon/volunmate/config/WebMvcConfig.java index e9bfcb1..a09a8d8 100644 --- a/src/main/java/donggukthon/volunmate/config/WebMvcConfig.java +++ b/src/main/java/donggukthon/volunmate/config/WebMvcConfig.java @@ -1,7 +1,7 @@ package donggukthon.volunmate.config; -import donggukthon.volunmate.interceptor.SocialIdArgumentResolver; -import donggukthon.volunmate.interceptor.SocialIdInterceptor; +import donggukthon.volunmate.interceptor.pre.SocialIdArgumentResolver; +import donggukthon.volunmate.interceptor.pre.SocialIdInterceptor; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; diff --git a/src/main/java/donggukthon/volunmate/exception/ErrorCode.java b/src/main/java/donggukthon/volunmate/exception/ErrorCode.java index 8bd112a..1e9af3d 100644 --- a/src/main/java/donggukthon/volunmate/exception/ErrorCode.java +++ b/src/main/java/donggukthon/volunmate/exception/ErrorCode.java @@ -30,6 +30,8 @@ public enum ErrorCode { * 404 Not Found */ NOT_FOUND_ERROR(404, HttpStatus.NOT_FOUND, "요청하신 리소스를 찾을 수 없습니다."), + FILE_NOT_FOUND(404, HttpStatus.NOT_FOUND, "요청하신 파일을 찾을 수 없습니다."), + FILE_UPLOAD_ERROR(404, HttpStatus.NOT_FOUND, "파일 업로드에 실패하였습니다."), /** * 405 Method Not Allowed diff --git a/src/main/java/donggukthon/volunmate/interceptor/post/SuccessResponseAdvice.java b/src/main/java/donggukthon/volunmate/interceptor/post/SuccessResponseAdvice.java new file mode 100644 index 0000000..0559f48 --- /dev/null +++ b/src/main/java/donggukthon/volunmate/interceptor/post/SuccessResponseAdvice.java @@ -0,0 +1,33 @@ +package donggukthon.volunmate.interceptor.post; + +import donggukthon.volunmate.dto.exception.ResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@RestControllerAdvice +@RequiredArgsConstructor +public class SuccessResponseAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> selectedConverterType, + ServerHttpRequest request, ServerHttpResponse response) { + if (body instanceof ResponseDto responseDto) { + HttpStatus status = responseDto.getHttpStatus(); + response.setStatusCode(status); + } + return body; + } +} diff --git a/src/main/java/donggukthon/volunmate/interceptor/SocialIdArgumentResolver.java b/src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdArgumentResolver.java similarity index 96% rename from src/main/java/donggukthon/volunmate/interceptor/SocialIdArgumentResolver.java rename to src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdArgumentResolver.java index a753a76..beff2c1 100644 --- a/src/main/java/donggukthon/volunmate/interceptor/SocialIdArgumentResolver.java +++ b/src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdArgumentResolver.java @@ -1,4 +1,4 @@ -package donggukthon.volunmate.interceptor; +package donggukthon.volunmate.interceptor.pre; import donggukthon.volunmate.annotation.SocialId; import donggukthon.volunmate.constant.Constants; diff --git a/src/main/java/donggukthon/volunmate/interceptor/SocialIdInterceptor.java b/src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdInterceptor.java similarity index 94% rename from src/main/java/donggukthon/volunmate/interceptor/SocialIdInterceptor.java rename to src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdInterceptor.java index 4a2bcc6..fb0962a 100644 --- a/src/main/java/donggukthon/volunmate/interceptor/SocialIdInterceptor.java +++ b/src/main/java/donggukthon/volunmate/interceptor/pre/SocialIdInterceptor.java @@ -1,4 +1,4 @@ -package donggukthon.volunmate.interceptor; +package donggukthon.volunmate.interceptor.pre; import donggukthon.volunmate.constant.Constants; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/donggukthon/volunmate/repository/HeartRepository.java b/src/main/java/donggukthon/volunmate/repository/HeartRepository.java index aa26d01..ec99dff 100644 --- a/src/main/java/donggukthon/volunmate/repository/HeartRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/HeartRepository.java @@ -2,6 +2,8 @@ import donggukthon.volunmate.domain.Heart; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface HeartRepository extends JpaRepository { } diff --git a/src/main/java/donggukthon/volunmate/repository/HelpRepository.java b/src/main/java/donggukthon/volunmate/repository/HelpRepository.java index 242a932..97304c9 100644 --- a/src/main/java/donggukthon/volunmate/repository/HelpRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/HelpRepository.java @@ -2,6 +2,8 @@ import donggukthon.volunmate.domain.Help; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface HelpRepository extends JpaRepository { } diff --git a/src/main/java/donggukthon/volunmate/repository/TagRepository.java b/src/main/java/donggukthon/volunmate/repository/TagRepository.java index 3d8ac2f..2bd7e69 100644 --- a/src/main/java/donggukthon/volunmate/repository/TagRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/TagRepository.java @@ -2,6 +2,8 @@ import donggukthon.volunmate.domain.Tag; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface TagRepository extends JpaRepository { } diff --git a/src/main/java/donggukthon/volunmate/repository/UserRepository.java b/src/main/java/donggukthon/volunmate/repository/UserRepository.java index 5c512ea..bfb6154 100644 --- a/src/main/java/donggukthon/volunmate/repository/UserRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/UserRepository.java @@ -5,9 +5,11 @@ import donggukthon.volunmate.type.EUserType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; import java.util.Optional; +@Repository public interface UserRepository extends JpaRepository { @Query("select u.socialId as id, u.userType as userType, u.password as password from User u where u.socialId = :socialId") Optional findBySecurityFormBySocialId(String socialId); diff --git a/src/main/java/donggukthon/volunmate/repository/VolunmateRepository.java b/src/main/java/donggukthon/volunmate/repository/VolunmateRepository.java index 3c6318c..15c8d75 100644 --- a/src/main/java/donggukthon/volunmate/repository/VolunmateRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/VolunmateRepository.java @@ -2,6 +2,8 @@ import donggukthon.volunmate.domain.Volunmate; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface VolunmateRepository extends JpaRepository { } diff --git a/src/main/java/donggukthon/volunmate/repository/VolunteerRepository.java b/src/main/java/donggukthon/volunmate/repository/VolunteerRepository.java index 07b7492..e9ab50f 100644 --- a/src/main/java/donggukthon/volunmate/repository/VolunteerRepository.java +++ b/src/main/java/donggukthon/volunmate/repository/VolunteerRepository.java @@ -2,6 +2,8 @@ import donggukthon.volunmate.domain.Volunteer; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface VolunteerRepository extends JpaRepository { } diff --git a/src/main/java/donggukthon/volunmate/service/OAuth2Service.java b/src/main/java/donggukthon/volunmate/service/OAuth2Service.java index 374b245..d792009 100644 --- a/src/main/java/donggukthon/volunmate/service/OAuth2Service.java +++ b/src/main/java/donggukthon/volunmate/service/OAuth2Service.java @@ -33,9 +33,9 @@ public class OAuth2Service { private final JwtUtil jwtUtil; private final JwtProvider jwtProvider; private final GoogleOAuth2Util oAuth2Util; - @Value("${security.oauth2.main-url}") + @Value("${security.oauth2.main_url}") private String MAIN_URL; - @Value("${security.oauth2.signup-url}") + @Value("${security.oauth2.signup_url}") private String SIGNUP_URL; public String getRedirectUrl(ELoginProvider provider) { diff --git a/src/main/java/donggukthon/volunmate/utils/S3Util.java b/src/main/java/donggukthon/volunmate/utils/S3Util.java new file mode 100644 index 0000000..b7b1963 --- /dev/null +++ b/src/main/java/donggukthon/volunmate/utils/S3Util.java @@ -0,0 +1,58 @@ +package donggukthon.volunmate.utils; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.PutObjectRequest; +import donggukthon.volunmate.config.S3Config; +import donggukthon.volunmate.exception.CustomException; +import donggukthon.volunmate.exception.ErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class S3Util { + private final AmazonS3Client amazonS3Client; + private final S3Config s3Config; + + public String upload(File file, String fileName, String dirName) { + try { + String fullPath = dirName + fileName; + amazonS3Client.putObject(new PutObjectRequest(s3Config.getBucket(), fullPath, file)); + + return amazonS3Client.getUrl(s3Config.getBucket(), fullPath).toString(); + } catch (AmazonS3Exception e) { + throw new CustomException(ErrorCode.FILE_UPLOAD_ERROR); + } + } + + /** + * 절대 경로를 상대 경로로 변환 + * @param path + * @return + * @throws URISyntaxException + */ + public String convertPath(String path) throws URISyntaxException { + URI uri = new URI(path); + String res = uri.getPath(); + return res.substring(s3Config.getBucket().length() + 2); + } + + public void deleteS3File(String path) { + try { + String relativePath = convertPath(path); + amazonS3Client.deleteObject(s3Config.getBucket(), relativePath); + } catch (AmazonS3Exception | URISyntaxException e) { + throw new CustomException(ErrorCode.FILE_NOT_FOUND); + } + } + + private String makeFileName(String originFileName) { + return originFileName + "~" + UUID.randomUUID() + ".jpg"; + } +} diff --git a/volunmate_properties b/volunmate_properties index c301316..13e527d 160000 --- a/volunmate_properties +++ b/volunmate_properties @@ -1 +1 @@ -Subproject commit c301316684440dfb91b65f4c5ff79315dd5e9830 +Subproject commit 13e527d6c1c9b17aa9dc84126ccb4050b53048e2