Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 공통 응답 포멧 통일 #116

Merged
merged 9 commits into from
Jan 10, 2024
20 changes: 0 additions & 20 deletions src/main/java/com/depromeet/ExampleController.java

This file was deleted.

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.depromeet.global.common.response;

import com.depromeet.global.error.ErrorResponse;
import java.time.LocalDateTime;

public record GlobalResponse(boolean success, int status, Object data, LocalDateTime timestamp) {
public static GlobalResponse success(int status, Object data) {
return new GlobalResponse(true, status, data, LocalDateTime.now());
}

public static GlobalResponse fail(int status, ErrorResponse errorResponse) {
return new GlobalResponse(false, status, errorResponse, LocalDateTime.now());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.depromeet.global.config.response;
package com.depromeet.global.common.response;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.MethodParameter;
Expand Down Expand Up @@ -33,7 +33,7 @@ public Object beforeBodyWrite(
return body;
}
if (resolve.is2xxSuccessful()) {
return GlobalResponse.of(status, body);
return GlobalResponse.success(status, body);
}
return body;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.depromeet.global.config;
package com.depromeet.global.config.querydsl;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.depromeet.global.config;
package com.depromeet.global.config.swagger;

import static com.depromeet.global.util.SpringEnvironmentUtil.*;

Expand Down
9 changes: 3 additions & 6 deletions src/main/java/com/depromeet/global/error/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.depromeet.global.error;

import java.time.LocalDateTime;
import org.springframework.http.HttpStatus;
public record ErrorResponse(String errorClassName, String message) {

public record ErrorResponse(int status, String message, LocalDateTime timestamp) {

public static ErrorResponse of(HttpStatus status, String message) {
return new ErrorResponse(status.value(), message, LocalDateTime.now());
public static ErrorResponse of(String errorClassName, String message) {
return new ErrorResponse(errorClassName, message);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
package com.depromeet.global.error;

import com.depromeet.global.common.response.GlobalResponse;
import com.depromeet.global.error.exception.CustomException;
import com.depromeet.global.error.exception.ErrorCode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
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.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -37,7 +35,7 @@ protected ResponseEntity<Object> handleExceptionInternal(
HttpStatusCode statusCode,
WebRequest request) {
ErrorResponse errorResponse =
ErrorResponse.of(HttpStatus.resolve(statusCode.value()), ex.getMessage());
ErrorResponse.of(ex.getClass().getSimpleName(), ex.getMessage());
return super.handleExceptionInternal(ex, errorResponse, headers, statusCode, request);
}

Expand All @@ -53,23 +51,16 @@ protected ResponseEntity<Object> handleMethodArgumentNotValid(
HttpStatusCode status,
WebRequest request) {
log.error("MethodArgumentNotValidException : {}", e.getMessage(), e);
List<FieldError> errors = e.getBindingResult().getFieldErrors();
Map<String, Object> fieldAndErrorMessages =
errors.stream()
.collect(
Collectors.toMap(
FieldError::getField, FieldError::getDefaultMessage));

String errorsToJsonString = new ObjectMapper().writeValueAsString(fieldAndErrorMessages);
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
final ErrorResponse errorResponse =
ErrorResponse.of(HttpStatus.resolve(status.value()), errorsToJsonString);

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(e.getClass().getSimpleName(), errorMessage);
GlobalResponse response = GlobalResponse.fail(status.value(), errorResponse);
return ResponseEntity.status(status).body(response);
}

/** Request Param Validation 예외 처리 */
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(
public ResponseEntity<GlobalResponse> handleConstraintViolationException(
ConstraintViolationException e) {
log.error("ConstraintViolationException : {}", e.getMessage(), e);

Expand All @@ -92,21 +83,23 @@ public ResponseEntity<ErrorResponse> handleConstraintViolationException(
});

final ErrorResponse errorResponse =
ErrorResponse.of(HttpStatus.BAD_REQUEST, bindingErrors.toString());

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(e.getClass().getSimpleName(), bindingErrors.toString());
final GlobalResponse response =
GlobalResponse.fail(HttpStatus.BAD_REQUEST.value(), errorResponse);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}

/** enum type 일치하지 않아 binding 못할 경우 발생 주로 @RequestParam enum으로 binding 못했을 경우 발생 */
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(
protected ResponseEntity<GlobalResponse> handleMethodArgumentTypeMismatchException(
MethodArgumentTypeMismatchException e) {
log.error("MethodArgumentTypeMismatchException : {}", e.getMessage(), e);
final ErrorCode errorCode = ErrorCode.METHOD_ARGUMENT_TYPE_MISMATCH;
final ErrorResponse errorResponse =
ErrorResponse.of(errorCode.getStatus(), errorCode.getMessage());

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage());
final GlobalResponse response =
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse);
return ResponseEntity.status(errorCode.getStatus()).body(response);
}

/** 지원하지 않은 HTTP method 호출 할 경우 발생 */
Expand All @@ -119,30 +112,33 @@ protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
log.error("HttpRequestMethodNotSupportedException : {}", e.getMessage(), e);
final ErrorCode errorCode = ErrorCode.METHOD_NOT_ALLOWED;
final ErrorResponse errorResponse =
ErrorResponse.of(errorCode.getStatus(), errorCode.getMessage());

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage());
final GlobalResponse response =
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse);
return ResponseEntity.status(errorCode.getStatus()).body(response);
}

/** CustomException 예외 처리 */
@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
public ResponseEntity<GlobalResponse> handleCustomException(CustomException e) {
log.error("CustomException : {}", e.getMessage(), e);
final ErrorCode errorCode = e.getErrorCode();
final ErrorResponse errorResponse =
ErrorResponse.of(errorCode.getStatus(), errorCode.getMessage());

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(errorCode.name(), errorCode.getMessage());
final GlobalResponse response =
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse);
return ResponseEntity.status(errorCode.getStatus()).body(response);
}

/** 500번대 에러 처리 */
@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleException(Exception e) {
protected ResponseEntity<GlobalResponse> handleException(Exception e) {
log.error("Internal Server Error : {}", e.getMessage(), e);
final ErrorCode internalServerError = ErrorCode.INTERNAL_SERVER_ERROR;
final ErrorResponse errorResponse =
ErrorResponse.of(internalServerError.getStatus(), internalServerError.getMessage());

return ResponseEntity.status(errorResponse.status()).body(errorResponse);
ErrorResponse.of(e.getClass().getSimpleName(), internalServerError.getMessage());
final GlobalResponse response =
GlobalResponse.fail(internalServerError.getStatus().value(), errorResponse);
return ResponseEntity.status(internalServerError.getStatus()).body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ class 미션_기록_이미지_PresignedUrl을_생성할_때 {
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(
jsonPath("$.message")
.value("{\"missionRecordId\":\"미션 기록 ID는 비워둘 수 없습니다.\"}"))
jsonPath("$.data.errorClassName")
.value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("미션 기록 ID는 비워둘 수 없습니다."))
.andDo(print());
}

Expand All @@ -64,10 +66,12 @@ class 미션_기록_이미지_PresignedUrl을_생성할_때 {
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(
jsonPath("$.message")
.value("{\"imageFileExtension\":\"이미지 파일의 확장자는 비워둘 수 없습니다.\"}"))
jsonPath("$.data.errorClassName")
.value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("이미지 파일의 확장자는 비워둘 수 없습니다."))
.andDo(print());
}

Expand Down Expand Up @@ -109,10 +113,12 @@ class 미션_기록_이미지_업로드_완료_처리할_때 {
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(
jsonPath("$.message")
.value("{\"missionRecordId\":\"미션 기록 ID는 비워둘 수 없습니다.\"}"))
jsonPath("$.data.errorClassName")
.value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("미션 기록 ID는 비워둘 수 없습니다."))
.andDo(print());
}

Expand All @@ -128,10 +134,12 @@ class 미션_기록_이미지_업로드_완료_처리할_때 {
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(
jsonPath("$.message")
.value("{\"imageFileExtension\":\"이미지 파일의 확장자는 비워둘 수 없습니다.\"}"))
jsonPath("$.data.errorClassName")
.value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("이미지 파일의 확장자는 비워둘 수 없습니다."))
.andDo(print());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ private Slice<MissionFindResponse> checkLastPage(
.with(csrf())
.content(objectMapper.writeValueAsString(createRequest)));
perform.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(jsonPath("$.message").value("{\"name\":\"이름은 비워둘 수 없습니다.\"}"))
.andExpect(
jsonPath("$.data.errorClassName").value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("이름은 비워둘 수 없습니다."))
.andDo(print());
}

Expand Down Expand Up @@ -257,8 +260,11 @@ private Slice<MissionFindResponse> checkLastPage(
.content(objectMapper.writeValueAsString(updateRequest)));

perform.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(jsonPath("$.message").value("{\"name\":\"이름은 비워둘 수 없습니다.\"}"))
.andExpect(
jsonPath("$.data.errorClassName").value("MethodArgumentNotValidException"))
.andExpect(jsonPath("$.data.message").value("이름은 비워둘 수 없습니다."))
.andDo(print());
}

Expand Down