From eb658511d82cd2d05c2076b9defe94a5bb32276d Mon Sep 17 00:00:00 2001 From: KIM SEON WOO Date: Fri, 22 Nov 2024 01:31:15 +0900 Subject: [PATCH] week7 --- .DS_Store | Bin 6148 -> 6148 bytes build.gradle | 1 + src/.DS_Store | Bin 6148 -> 6148 bytes .../umc/study/apiPayload/ApiResponse.java | 39 ++++++ .../umc/study/apiPayload/code/BaseCode.java | 8 ++ .../study/apiPayload/code/BaseErrorCode.java | 7 ++ .../study/apiPayload/code/ErrorReasonDTO.java | 17 +++ .../umc/study/apiPayload/code/ReasonDTO.java | 17 +++ .../apiPayload/code/status/ErrorStatus.java | 49 ++++++++ .../apiPayload/code/status/SuccessStatus.java | 39 ++++++ .../apiPayload/exception/ExceptionAdvice.java | 119 ++++++++++++++++++ .../exception/GeneralException.java | 20 +++ .../exception/handler/TempHandler.java | 10 ++ .../umc/study/converter/TempConverter.java | 16 +++ .../TempService/TempCommandService.java | 6 + .../TempService/TempCommandServiceImpl.java | 10 ++ .../service/TempService/TempQueryService.java | 5 + .../TempService/TempQueryServiceImpl.java | 17 +++ .../web/controller/TempRestController.java | 29 +++++ .../java/umc/study/web/dto/TempRequest.java | 4 + .../java/umc/study/web/dto/TempResponse.java | 25 ++++ 21 files changed, 438 insertions(+) create mode 100644 src/main/java/umc/study/apiPayload/ApiResponse.java create mode 100644 src/main/java/umc/study/apiPayload/code/BaseCode.java create mode 100644 src/main/java/umc/study/apiPayload/code/BaseErrorCode.java create mode 100644 src/main/java/umc/study/apiPayload/code/ErrorReasonDTO.java create mode 100644 src/main/java/umc/study/apiPayload/code/ReasonDTO.java create mode 100644 src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java create mode 100644 src/main/java/umc/study/apiPayload/code/status/SuccessStatus.java create mode 100644 src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java create mode 100644 src/main/java/umc/study/apiPayload/exception/GeneralException.java create mode 100644 src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java create mode 100644 src/main/java/umc/study/converter/TempConverter.java create mode 100644 src/main/java/umc/study/service/TempService/TempCommandService.java create mode 100644 src/main/java/umc/study/service/TempService/TempCommandServiceImpl.java create mode 100644 src/main/java/umc/study/service/TempService/TempQueryService.java create mode 100644 src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java create mode 100644 src/main/java/umc/study/web/controller/TempRestController.java create mode 100644 src/main/java/umc/study/web/dto/TempRequest.java create mode 100644 src/main/java/umc/study/web/dto/TempResponse.java diff --git a/.DS_Store b/.DS_Store index 7e95c758ba902cf0d0685046687a37981b10ea14..27c5368b88bc3d4486988c84def1671902b66ac2 100644 GIT binary patch delta 482 zcmZoMXffEJ$`Z%SV8y_|z`~%%kj{|FP?DSP;*yk;p9B=+;P`X6GNbsIBdUA~UipFy z!{Frn+ybB;1_nNd$;~Vqm_^QLO*UhdWBclJ__t2slv5=APz`kCg!~sbRr9da-Fr*+m zfSF-`8Q1}ES!4&8`d{>&od%^Mc8X*%sI`ta$~riJ768p@<%p$5$W delta 23 ecmZoMXffEJ%E-*H>e6Hl#z-cn1Dl%}8^r)wlLuu0 diff --git a/src/main/java/umc/study/apiPayload/ApiResponse.java b/src/main/java/umc/study/apiPayload/ApiResponse.java new file mode 100644 index 0000000..5db2eac --- /dev/null +++ b/src/main/java/umc/study/apiPayload/ApiResponse.java @@ -0,0 +1,39 @@ +package umc.study.apiPayload; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.study.apiPayload.code.BaseCode; +import umc.study.apiPayload.code.status.SuccessStatus; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class ApiResponse { + + @JsonProperty("isSuccess") + private final Boolean isSuccess; + private final String code; + private final String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + + // 성공한 경우 응답 생성 + + public static ApiResponse onSuccess(T result){ + return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); + } + + public static ApiResponse of(BaseCode code, T result){ + return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result); + } + + + // 실패한 경우 응답 생성 + public static ApiResponse onFailure(String code, String message, T data){ + return new ApiResponse<>(false, code, message, data); + } +} diff --git a/src/main/java/umc/study/apiPayload/code/BaseCode.java b/src/main/java/umc/study/apiPayload/code/BaseCode.java new file mode 100644 index 0000000..3c3dd87 --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/BaseCode.java @@ -0,0 +1,8 @@ +package umc.study.apiPayload.code; + +public interface BaseCode { + + ReasonDTO getReason(); + + ReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/study/apiPayload/code/BaseErrorCode.java b/src/main/java/umc/study/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..b3fa13e --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,7 @@ +package umc.study.apiPayload.code; + +public interface BaseErrorCode { + ErrorReasonDTO getReason(); + + ErrorReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/umc/study/apiPayload/code/ErrorReasonDTO.java b/src/main/java/umc/study/apiPayload/code/ErrorReasonDTO.java new file mode 100644 index 0000000..f790186 --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/ErrorReasonDTO.java @@ -0,0 +1,17 @@ +package umc.study.apiPayload.code; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ErrorReasonDTO { + private HttpStatus httpStatus; + + private final boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/study/apiPayload/code/ReasonDTO.java b/src/main/java/umc/study/apiPayload/code/ReasonDTO.java new file mode 100644 index 0000000..64f8b5e --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/ReasonDTO.java @@ -0,0 +1,17 @@ +package umc.study.apiPayload.code; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ReasonDTO { + private HttpStatus httpStatus; + + private final boolean isSuccess; + private final String code; + private final String message; + + public boolean getIsSuccess(){return isSuccess;} +} diff --git a/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java new file mode 100644 index 0000000..a0912ce --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -0,0 +1,49 @@ +package umc.study.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.study.apiPayload.code.BaseErrorCode; +import umc.study.apiPayload.code.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public enum ErrorStatus implements BaseErrorCode { + // 가장 일반적인 응답 + _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."), + _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."), + _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), + _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), + + + // 멤버 관려 에러 + MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), + NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."), + + // 예시,,, + TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001","이거는 테스트"), + ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReasonDTO getReason() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .build(); + } + + @Override + public ErrorReasonDTO getReasonHttpStatus() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .httpStatus(httpStatus) + .build(); + } +} diff --git a/src/main/java/umc/study/apiPayload/code/status/SuccessStatus.java b/src/main/java/umc/study/apiPayload/code/status/SuccessStatus.java new file mode 100644 index 0000000..3eb4812 --- /dev/null +++ b/src/main/java/umc/study/apiPayload/code/status/SuccessStatus.java @@ -0,0 +1,39 @@ +package umc.study.apiPayload.code.status; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import umc.study.apiPayload.code.BaseCode; +import umc.study.apiPayload.code.ReasonDTO; + +@Getter +@AllArgsConstructor +public enum SuccessStatus implements BaseCode { + + // 일반적인 응답 + _OK(HttpStatus.OK, "COMMON200", "성공입니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDTO getReason() { + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .build(); + } + + @Override + public ReasonDTO getReasonHttpStatus() { + return ReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(true) + .httpStatus(httpStatus) + .build() + ; + } +} diff --git a/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java b/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java new file mode 100644 index 0000000..79b7ef5 --- /dev/null +++ b/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java @@ -0,0 +1,119 @@ +package umc.study.apiPayload.exception; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +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.RestController; +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 umc.study.apiPayload.ApiResponse; +import umc.study.apiPayload.code.ErrorReasonDTO; +import umc.study.apiPayload.code.status.ErrorStatus; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +@Slf4j +@RestControllerAdvice(annotations = {RestController.class}) +public class ExceptionAdvice extends ResponseEntityExceptionHandler { + + + @ExceptionHandler + public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { + String errorMessage = e.getConstraintViolations().stream() + .map(constraintViolation -> constraintViolation.getMessage()) + .findFirst() + .orElseThrow(() -> new RuntimeException("ConstraintViolationException 추출 도중 에러 발생")); + + return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY,request); + } + @Override + public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + + Map errors = new LinkedHashMap<>(); + + e.getBindingResult().getFieldErrors().stream() + .forEach(fieldError -> { + String fieldName = fieldError.getField(); + String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); + errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); + }); + + return handleExceptionInternalArgs(e,HttpHeaders.EMPTY,ErrorStatus.valueOf("_BAD_REQUEST"),request,errors); + } + + @ExceptionHandler + public ResponseEntity exception(Exception e, WebRequest request) { + e.printStackTrace(); + + return handleExceptionInternalFalse(e, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(),request, e.getMessage()); + } + + @ExceptionHandler(value = GeneralException.class) + public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) { + // GeneralException에 대해 다시 한번 오버로딩 된 함수를 호출 + ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus(); + return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request); + } + + private ResponseEntity handleExceptionInternal(Exception e, ErrorReasonDTO reason, + HttpHeaders headers, HttpServletRequest request) { + // 상속받은 부모 클래스의 생성자를 호출 + ApiResponse body = ApiResponse.onFailure(reason.getCode(),reason.getMessage(),null); +// e.printStackTrace(); + + WebRequest webRequest = new ServletWebRequest(request); + return super.handleExceptionInternal( + e, + body, + headers, + reason.getHttpStatus(), + webRequest + ); + } + + private ResponseEntity handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorPoint); + return super.handleExceptionInternal( + e, + body, + headers, + status, + request + ); + } + + private ResponseEntity handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus, + WebRequest request, Map errorArgs) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorArgs); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } + + private ResponseEntity handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus, + HttpHeaders headers, WebRequest request) { + ApiResponse body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null); + return super.handleExceptionInternal( + e, + body, + headers, + errorCommonStatus.getHttpStatus(), + request + ); + } +} diff --git a/src/main/java/umc/study/apiPayload/exception/GeneralException.java b/src/main/java/umc/study/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..b59824c --- /dev/null +++ b/src/main/java/umc/study/apiPayload/exception/GeneralException.java @@ -0,0 +1,20 @@ +package umc.study.apiPayload.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import umc.study.apiPayload.code.BaseErrorCode; +import umc.study.apiPayload.code.ErrorReasonDTO; + +@Getter +@AllArgsConstructor +public class GeneralException extends RuntimeException { + private BaseErrorCode code; + + public ErrorReasonDTO getErrorReason() { + return this.code.getReason(); + } + + public ErrorReasonDTO getErrorReasonHttpStatus(){ + return this.code.getReasonHttpStatus(); + } +} diff --git a/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java b/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java new file mode 100644 index 0000000..c0a4275 --- /dev/null +++ b/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java @@ -0,0 +1,10 @@ +package umc.study.apiPayload.exception.handler; + +import umc.study.apiPayload.code.BaseErrorCode; +import umc.study.apiPayload.exception.GeneralException; + +public class TempHandler extends GeneralException { + public TempHandler(BaseErrorCode errorCode){ + super(errorCode); // 상위클래스의 생성자를 호출하여, GeneralException의 로직대로 errorCode를 초기화 + } +} diff --git a/src/main/java/umc/study/converter/TempConverter.java b/src/main/java/umc/study/converter/TempConverter.java new file mode 100644 index 0000000..2e65642 --- /dev/null +++ b/src/main/java/umc/study/converter/TempConverter.java @@ -0,0 +1,16 @@ +package umc.study.converter; + +import umc.study.web.dto.TempResponse; + +public class TempConverter { + public static TempResponse.TempTestDTO toTempTestDTO() { + return TempResponse.TempTestDTO.builder() + .testString("This is Test!") + .build(); + } + public static TempResponse.TempExceptionDTO toTempExceptionDTO(Integer flag) { + return TempResponse.TempExceptionDTO.builder() + .flag(flag) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/umc/study/service/TempService/TempCommandService.java b/src/main/java/umc/study/service/TempService/TempCommandService.java new file mode 100644 index 0000000..43b4414 --- /dev/null +++ b/src/main/java/umc/study/service/TempService/TempCommandService.java @@ -0,0 +1,6 @@ +package umc.study.service.TempService; + +public interface TempCommandService { + // GET요청이 아닌 요청에 대햇너느 비즈니스 로직을 CommandService로 네이밍한다. + // 서비스를 만들 경우 인터페이스를 먼저 두고 이를 구체화한다 +} diff --git a/src/main/java/umc/study/service/TempService/TempCommandServiceImpl.java b/src/main/java/umc/study/service/TempService/TempCommandServiceImpl.java new file mode 100644 index 0000000..cda23ed --- /dev/null +++ b/src/main/java/umc/study/service/TempService/TempCommandServiceImpl.java @@ -0,0 +1,10 @@ +package umc.study.service.TempService; + +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@NoArgsConstructor +public class TempCommandServiceImpl implements TempCommandService { + // 컨트롤러는 인터페이스를 의존하며 실제 인터페이스에 대한 구체화 클래스는 스프링부트의 의존성 주입을 이용한다 +} diff --git a/src/main/java/umc/study/service/TempService/TempQueryService.java b/src/main/java/umc/study/service/TempService/TempQueryService.java new file mode 100644 index 0000000..251029d --- /dev/null +++ b/src/main/java/umc/study/service/TempService/TempQueryService.java @@ -0,0 +1,5 @@ +package umc.study.service.TempService; + +public interface TempQueryService { + void CheckFlag(Integer flag); +} diff --git a/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java b/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java new file mode 100644 index 0000000..2a0e232 --- /dev/null +++ b/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java @@ -0,0 +1,17 @@ +package umc.study.service.TempService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.TempHandler; + +@Service +@RequiredArgsConstructor +public class TempQueryServiceImpl implements TempQueryService{ + // 컨트롤러는 인터페이스를 의존하며 실제 인터페이스에 대한 구체화 클래스는 스프링부트의 의존성 주입을 이용한다 + @Override + public void CheckFlag(Integer flag){ + if(flag == 1) //if문 내부로 들어오면 Service 이후 controller로 가지 않고, 바로 Exception handler에 의해 응답이 보내진다. + throw new TempHandler(ErrorStatus.TEMP_EXCEPTION); + } +} diff --git a/src/main/java/umc/study/web/controller/TempRestController.java b/src/main/java/umc/study/web/controller/TempRestController.java new file mode 100644 index 0000000..fa5af1c --- /dev/null +++ b/src/main/java/umc/study/web/controller/TempRestController.java @@ -0,0 +1,29 @@ +package umc.study.web.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import umc.study.apiPayload.ApiResponse; +import umc.study.converter.TempConverter; +import umc.study.service.TempService.TempQueryService; +import umc.study.web.dto.TempResponse; + +@RestController +@RequestMapping("/temp") +@RequiredArgsConstructor +public class TempRestController { + private final TempQueryService tempQueryService; + + @GetMapping("/test") + public ApiResponse testAPI() { + + return ApiResponse.onSuccess(TempConverter.toTempTestDTO()); + } + @GetMapping("/exception") + public ApiResponse exceptionAPI(@RequestParam Integer flag) { + tempQueryService.CheckFlag(flag); + return ApiResponse.onSuccess(TempConverter.toTempExceptionDTO(flag)); + } +} \ No newline at end of file diff --git a/src/main/java/umc/study/web/dto/TempRequest.java b/src/main/java/umc/study/web/dto/TempRequest.java new file mode 100644 index 0000000..ae31723 --- /dev/null +++ b/src/main/java/umc/study/web/dto/TempRequest.java @@ -0,0 +1,4 @@ +package umc.study.web.dto; + +public class TempRequest { +} diff --git a/src/main/java/umc/study/web/dto/TempResponse.java b/src/main/java/umc/study/web/dto/TempResponse.java new file mode 100644 index 0000000..81bc6b0 --- /dev/null +++ b/src/main/java/umc/study/web/dto/TempResponse.java @@ -0,0 +1,25 @@ +package umc.study.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class TempResponse { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempTestDTO{ + String testString; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TempExceptionDTO{ + Integer flag; + } +} \ No newline at end of file