Skip to content

Commit

Permalink
Merge pull request #6 from Team-Capple/feature/#5/BaseResponse
Browse files Browse the repository at this point in the history
[FEAT] Base Response ์…‹ํŒ…
  • Loading branch information
kyxxgsoo authored Feb 26, 2024
2 parents e78fc5d + d6e48ac commit 8f9a2af
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/server/capple/global/common/BaseResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.server.capple.global.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"timeStamp", "code", "message", "result"})
public class BaseResponse<T> {

private final LocalDateTime timeStamp = LocalDateTime.now();
private final String code;
private final String message;
@JsonInclude(Include.NON_NULL)
private T result;

// ์„ฑ๊ณต ์‹œ ์‘๋‹ต
public static <T> BaseResponse<T> onSuccess(T result) {
return new BaseResponse<>("COMMON200", "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.", result);
}

public static <T> BaseResponse<T> onFailure(String code, String message, T data) {
return new BaseResponse<>(code, message, data);
}
}
116 changes: 116 additions & 0 deletions src/main/java/com/server/capple/global/exception/ControllerAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.server.capple.global.exception;

import com.server.capple.global.common.BaseResponse;
import com.server.capple.global.exception.errorCode.GlobalErrorCode;
import jakarta.validation.ConstraintViolationException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
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.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
@RestControllerAdvice(annotations = {RestController.class})
public class ControllerAdvice extends ResponseEntityExceptionHandler {

/**
* ์ •์˜ํ•œ RestApiException ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
*/
@ExceptionHandler(value = RestApiException.class)
public ResponseEntity<BaseResponse<String>> handleRestApiException(
RestApiException e) {
ErrorCode errorCode = e.getErrorCode();
return handlerExceptionInternal(errorCode);
}

/**
* ์ผ๋ฐ˜์ ์ธ ์„œ๋ฒ„ ์—๋Ÿฌ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
*/
@ExceptionHandler
public ResponseEntity<BaseResponse<String>> handleException(
Exception e) {
e.printStackTrace();
return handleExceptionInternalFalse(GlobalErrorCode.SERVER_ERROR.getErrorCode(), e.getMessage());
}

/**
* @Validated ๊ฒ€์ฆ ์‹คํŒจ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<BaseResponse<String>> handleConstraintViolationException(
ConstraintViolationException e) {
return handleExceptionInternal(GlobalErrorCode.VALIDATION_ERROR.getErrorCode());
}

/**
* ๋ฉ”์„œ๋“œ์˜ ์ธ์ž ํƒ€์ž…์ด ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<BaseResponse<String>> handleMethodArgumentTypeMismatch(
MethodArgumentTypeMismatchException e) {
// ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋กœ์ง
return handleExceptionInternal(GlobalErrorCode.NOT_VALID_ARGUMENT_ERROR.getErrorCode());
}

/**
* @RequestBody ๋‚ด๋ถ€ ์ฒ˜๋ฆฌ ์‹คํŒจ,
* @Valid ๊ฒ€์ฆ ์‹คํŒจํ•œ ๊ฒฝ์šฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
*/
@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException e,
HttpHeaders headers,
HttpStatusCode statusCode,
WebRequest request) {
Map<String, String> 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(GlobalErrorCode.VALIDATION_ERROR.getErrorCode(), errors);

}

private ResponseEntity<BaseResponse<String>> handlerExceptionInternal(
ErrorCode errorCode) {
return ResponseEntity
.status(errorCode.getHttpStatus().value())
.body(BaseResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), null));
}

private ResponseEntity<BaseResponse<String>> handleExceptionInternal(ErrorCode errorCode) {
return ResponseEntity
.status(errorCode.getHttpStatus().value())
.body(BaseResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), null));
}

private ResponseEntity<Object> handleExceptionInternalArgs(
ErrorCode errorCode,
Map<String, String> errorArgs) {
return ResponseEntity
.status(errorCode.getHttpStatus().value())
.body(BaseResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), errorArgs));
}
private ResponseEntity<BaseResponse<String>> handleExceptionInternalFalse(
ErrorCode errorCode,
String errorPoint) {
return ResponseEntity
.status(errorCode.getHttpStatus().value())
.body(BaseResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), errorPoint));
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/server/capple/global/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.server.capple.global.exception;

import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@Builder
public class ErrorCode {
private final String code;
private final String message;
private final HttpStatus httpStatus;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.server.capple.global.exception;


public interface ErrorCodeInterface {
ErrorCode getErrorCode();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.server.capple.global.exception;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class RestApiException extends RuntimeException {
private final ErrorCodeInterface errorCode;

public ErrorCode getErrorCode() {
return this.errorCode.getErrorCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.server.capple.global.exception.errorCode;

import com.server.capple.global.exception.ErrorCode;
import com.server.capple.global.exception.ErrorCodeInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum GlobalErrorCode implements ErrorCodeInterface {
BAD_REQUEST("GLOBAL001", "์ž˜๋ชป๋œ ์š”์ฒญ์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST),
NOT_SUPPORTED_URI_ERROR("GLOBAL002", "์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ URI์ž…๋‹ˆ๋‹ค.", HttpStatus.NOT_FOUND),
NOT_SUPPORTED_METHOD_ERROR("GLOBAL003", "์ง€์›ํ•˜์ง€ ์•Š๋Š” Method์ž…๋‹ˆ๋‹ค.", HttpStatus.METHOD_NOT_ALLOWED),
NOT_SUPPORTED_MEDIA_TYPE_ERROR("GLOBAL004", "์ง€์›ํ•˜์ง€ ์•Š๋Š” Media type์ž…๋‹ˆ๋‹ค.", HttpStatus.UNSUPPORTED_MEDIA_TYPE),
SERVER_ERROR("GLOBAL005", "์„œ๋ฒ„ ์—๋Ÿฌ, ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•ด์ฃผ์„ธ์š”.", HttpStatus.INTERNAL_SERVER_ERROR),
ACCESS_DENIED("GLOBAL006", "์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ๊ถŒํ•œ์ž…๋‹ˆ๋‹ค.", HttpStatus.FORBIDDEN),
NOT_VALID_ARGUMENT_ERROR("GLOBAL007", "์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ Argument Type์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST),
VALIDATION_ERROR("GLOBAL007", "Validation Error์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST),
;

private final String code;
private final String message;
private final HttpStatus httpStatus;

@Override
public ErrorCode getErrorCode() {
return ErrorCode.builder()
.code(code)
.message(message)
.httpStatus(httpStatus)
.build();
}
}

0 comments on commit 8f9a2af

Please sign in to comment.