-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
✨ FEAT. S3 연동
- Loading branch information
Showing
10 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
src/main/java/fairytale/tbd/global/aws/s3/AmazonS3Manager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package fairytale.tbd.global.aws.s3; | ||
|
||
import java.io.IOException; | ||
|
||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.ObjectMetadata; | ||
import com.amazonaws.services.s3.model.PutObjectRequest; | ||
import com.amazonaws.services.s3.model.PutObjectResult; | ||
|
||
import fairytale.tbd.global.config.AmazonConfig; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class AmazonS3Manager { | ||
|
||
private final AmazonS3 amazonS3; | ||
private final AmazonConfig amazonConfig; | ||
|
||
public String uploadFile(String keyName, MultipartFile file) { | ||
ObjectMetadata metadata = new ObjectMetadata(); | ||
metadata.setContentType(file.getContentType()); | ||
metadata.setContentLength(file.getSize()); | ||
try { | ||
PutObjectResult putObjectResult = amazonS3.putObject( | ||
new PutObjectRequest(amazonConfig.getBucket(), keyName, file.getInputStream(), metadata)); | ||
log.info("result={}", putObjectResult.getContentMd5()); | ||
} catch (IOException e) { | ||
log.error("error at AmazonS3Manager uploadFile : {}", (Object)e.getStackTrace()); | ||
} | ||
|
||
return amazonS3.getUrl(amazonConfig.getBucket(), keyName).toString(); | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
src/main/java/fairytale/tbd/global/config/AmazonConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package fairytale.tbd.global.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
import com.amazonaws.auth.AWSCredentials; | ||
import com.amazonaws.auth.AWSCredentialsProvider; | ||
import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
import com.amazonaws.auth.BasicAWSCredentials; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.AmazonS3ClientBuilder; | ||
|
||
import jakarta.annotation.PostConstruct; | ||
import lombok.Getter; | ||
|
||
@Configuration | ||
@Getter | ||
public class AmazonConfig { | ||
|
||
private AWSCredentials awsCredentials; | ||
|
||
@Value("${cloud.aws.s3.credentials.access-key}") | ||
private String accessKey; | ||
|
||
@Value("${cloud.aws.s3.credentials.secret-key}") | ||
private String secretKey; | ||
|
||
@Value("${cloud.aws.s3.region.static}") | ||
private String region; | ||
|
||
@Value("${cloud.aws.s3.bucket}") | ||
private String bucket; | ||
|
||
|
||
|
||
@PostConstruct | ||
public void init(){ | ||
this.awsCredentials = new BasicAWSCredentials(accessKey, secretKey); | ||
} | ||
|
||
@Bean | ||
public AmazonS3 amazonS3(){ | ||
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); | ||
return AmazonS3ClientBuilder.standard() | ||
.withRegion(region) | ||
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public AWSCredentialsProvider awsCredentialsProvider(){ | ||
return new AWSStaticCredentialsProvider(awsCredentials); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package fairytale.tbd.global.entity; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.annotation.LastModifiedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.MappedSuperclass; | ||
import lombok.Getter; | ||
|
||
@MappedSuperclass | ||
@EntityListeners(AuditingEntityListener.class) | ||
@Getter | ||
public abstract class BaseEntity { | ||
@CreatedDate | ||
private LocalDateTime createdAt; | ||
|
||
@LastModifiedDate | ||
private LocalDateTime updatedAt; | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/fairytale/tbd/global/enums/statuscode/BaseCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package fairytale.tbd.global.enums.statuscode; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public interface BaseCode { | ||
String getCode(); | ||
|
||
String getMessage(); | ||
|
||
HttpStatus getHttpStatus(); | ||
|
||
Integer getStatusValue(); | ||
} |
46 changes: 46 additions & 0 deletions
46
src/main/java/fairytale/tbd/global/enums/statuscode/ErrorStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package fairytale.tbd.global.enums.statuscode; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public enum ErrorStatus implements BaseCode{ | ||
// common | ||
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."), | ||
_BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청입니다."), | ||
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "인증이 필요합니다."), | ||
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), | ||
|
||
|
||
// ElevenLabs | ||
_FILE_CONVERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "FILE5001", "파일 변환에 실패했습니다."); | ||
|
||
|
||
private final HttpStatus httpStatus; | ||
private final String code; | ||
private final String message; | ||
|
||
ErrorStatus(HttpStatus httpStatus, String code, String message) { | ||
this.httpStatus = httpStatus; | ||
this.code = code; | ||
this.message = message; | ||
} | ||
|
||
@Override | ||
public String getCode() { | ||
return code; | ||
} | ||
|
||
@Override | ||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
@Override | ||
public HttpStatus getHttpStatus() { | ||
return httpStatus; | ||
} | ||
|
||
@Override | ||
public Integer getStatusValue() { | ||
return httpStatus.value(); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
src/main/java/fairytale/tbd/global/enums/statuscode/SuccessStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package fairytale.tbd.global.enums.statuscode; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public enum SuccessStatus implements BaseCode{ | ||
_OK(HttpStatus.OK, "COMMON200", "성공입니다."), | ||
_ACCEPTED(HttpStatus.ACCEPTED, "COMMON204", "별도의 응답 데이터가 없으며, 정상 처리되었습니다."); | ||
|
||
private final HttpStatus httpStatus; | ||
private final String code; | ||
private final String message; | ||
|
||
SuccessStatus(HttpStatus httpStatus, String code, String message) { | ||
this.httpStatus = httpStatus; | ||
this.code = code; | ||
this.message = message; | ||
} | ||
|
||
@Override | ||
public String getCode() { | ||
return code; | ||
} | ||
|
||
@Override | ||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
@Override | ||
public HttpStatus getHttpStatus() { | ||
return httpStatus; | ||
} | ||
|
||
@Override | ||
public Integer getStatusValue() { | ||
return httpStatus.value(); | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/fairytale/tbd/global/exception/ExceptionAdvice.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package fairytale.tbd.global.exception; | ||
|
||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
import org.springframework.web.context.request.ServletWebRequest; | ||
import org.springframework.web.context.request.WebRequest; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||
|
||
import fairytale.tbd.global.enums.statuscode.ErrorStatus; | ||
import fairytale.tbd.global.response.ApiResponse; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@RestControllerAdvice | ||
public class ExceptionAdvice extends ResponseEntityExceptionHandler { | ||
@ExceptionHandler(value = GeneralException.class) | ||
public ResponseEntity<Object> handleGeneralException(GeneralException exception, HttpServletRequest request) { | ||
return handleExceptionInternal(exception, HttpHeaders.EMPTY, request); | ||
} | ||
|
||
@Override | ||
public ResponseEntity<Object> handleMethodArgumentNotValid( | ||
MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatusCode status, WebRequest request) { | ||
|
||
log.info("handleGeneralException 발생 ={}", exception); | ||
Map<String, String> errors = new LinkedHashMap<>(); | ||
exception.getBindingResult().getFieldErrors().stream() | ||
.forEach(fieldError -> { | ||
|
||
String fieldName = fieldError.getField(); | ||
String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); | ||
errors.merge(fieldName, errorMessage, | ||
(existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); | ||
}); | ||
|
||
exception.getBindingResult().getGlobalErrors().stream() | ||
.forEach(globalError -> { | ||
log.info("globalError = {}", globalError); | ||
String objectName = globalError.getObjectName(); | ||
String errorMessage = Optional.ofNullable(globalError.getDefaultMessage()).orElse(""); | ||
errors.merge(objectName, errorMessage, | ||
(existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); | ||
}); | ||
|
||
return handleExceptionInternalArgs(exception, HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"), request, | ||
errors); | ||
} | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<Object> handlingException(Exception exception, WebRequest request) { | ||
return handleExceptionInternal(exception, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, | ||
ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(), request, exception.getMessage()); | ||
} | ||
|
||
private ResponseEntity<Object> handleExceptionInternal(GeneralException exception, HttpHeaders headers, | ||
HttpServletRequest request) { | ||
ApiResponse<Object> body = ApiResponse.onFailure(exception.getErrorCode(), exception.getErrorReason(), null); | ||
WebRequest webRequest = new ServletWebRequest(request); | ||
return super.handleExceptionInternal(exception, body, headers, exception.getHttpStatus(), webRequest); | ||
} | ||
|
||
private ResponseEntity<Object> handleExceptionInternal(Exception exception, ErrorStatus errorStatus, | ||
HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) { | ||
ApiResponse<String> body = ApiResponse.onFailure(errorStatus.getCode(), errorStatus.getMessage(), errorPoint); | ||
return super.handleExceptionInternal(exception, body, headers, status, request); | ||
} | ||
|
||
private ResponseEntity<Object> handleExceptionInternalArgs(Exception e, HttpHeaders headers, | ||
ErrorStatus errorCommonStatus, WebRequest request, Map<String, String> errorArgs) { | ||
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), | ||
errorArgs); | ||
return super.handleExceptionInternal(e, body, headers, errorCommonStatus.getHttpStatus(), request); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/fairytale/tbd/global/exception/GeneralException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package fairytale.tbd.global.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
import fairytale.tbd.global.enums.statuscode.BaseCode; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequiredArgsConstructor | ||
public class GeneralException extends RuntimeException{ | ||
private final BaseCode errorStatus; | ||
|
||
public String getErrorCode() { | ||
return errorStatus.getCode(); | ||
} | ||
|
||
public String getErrorReason() { | ||
return errorStatus.getMessage(); | ||
} | ||
|
||
public HttpStatus getHttpStatus() { | ||
return errorStatus.getHttpStatus(); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/fairytale/tbd/global/response/ApiResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package fairytale.tbd.global.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
|
||
import fairytale.tbd.global.enums.statuscode.BaseCode; | ||
import fairytale.tbd.global.enums.statuscode.SuccessStatus; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) | ||
public class ApiResponse<T> { | ||
|
||
private final Boolean isSuccess; | ||
private final String code; | ||
|
||
private final String message; | ||
private T result; | ||
|
||
// 성공한 경우 응답 생성 | ||
|
||
public static <T> ApiResponse<T> onSuccess(T result) { | ||
return new ApiResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result); | ||
} | ||
|
||
public static <T> ApiResponse<T> of(boolean isSuccess, BaseCode code, T result) { | ||
return new ApiResponse<>(isSuccess, code.getCode(), code.getMessage(), result); | ||
} | ||
|
||
// 실패한 경우 응답 생성 | ||
public static <T> ApiResponse<T> onFailure(String code, String message, T data) { | ||
return new ApiResponse<>(false, code, message, data); | ||
} | ||
} |