-
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.
* Feat: API 공통 핸들러 추가 * Feat: ErrorType 추가 * Feat: ObjectMapper bean 추가 * Feat: api response 형식을 swagger에 적용 * Feat: null인 응답도 ApiRepsonse 형식으로 반환하도록 수정
- Loading branch information
1 parent
ef26f2b
commit e648714
Showing
13 changed files
with
399 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
21 changes: 21 additions & 0 deletions
21
src/main/java/com/dnd/runus/global/exception/BaseException.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,21 @@ | ||
package com.dnd.runus.global.exception; | ||
|
||
import com.dnd.runus.global.exception.type.ErrorType; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public abstract class BaseException extends RuntimeException { | ||
private final ErrorType type; | ||
private final String message; | ||
|
||
public BaseException(ErrorType type, String message) { | ||
super(message); | ||
this.type = type; | ||
this.message = message; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "에러 타입: " + type + ", 사유: " + message; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/dnd/runus/global/exception/BusinessException.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,9 @@ | ||
package com.dnd.runus.global.exception; | ||
|
||
import com.dnd.runus.global.exception.type.ErrorType; | ||
|
||
public class BusinessException extends BaseException { | ||
public BusinessException(ErrorType type, String message) { | ||
super(type, message); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/dnd/runus/global/exception/NotFoundException.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,9 @@ | ||
package com.dnd.runus.global.exception; | ||
|
||
import com.dnd.runus.global.exception.type.ErrorType; | ||
|
||
public class NotFoundException extends BaseException { | ||
public NotFoundException(String message) { | ||
super(ErrorType.ENTITY_NOT_FOUND, message); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/com/dnd/runus/global/exception/type/ApiErrorType.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 com.dnd.runus.global.exception.type; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.ElementType.METHOD; | ||
|
||
@Target({METHOD}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface ApiErrorType { | ||
ErrorType[] value(); | ||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/com/dnd/runus/global/exception/type/ErrorType.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,30 @@ | ||
package com.dnd.runus.global.exception.type; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.experimental.Accessors; | ||
import org.springframework.http.HttpStatus; | ||
|
||
import static lombok.AccessLevel.PRIVATE; | ||
import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||
import static org.springframework.http.HttpStatus.*; | ||
|
||
@Getter | ||
@Accessors(fluent = true) | ||
@RequiredArgsConstructor(access = PRIVATE) | ||
public enum ErrorType { | ||
// WebErrorType | ||
UNHANDLED_EXCEPTION(INTERNAL_SERVER_ERROR, "WEB_001", "직접적으로 처리되지 않은 예외, 문의해주세요"), | ||
FAILED_VALIDATION(BAD_REQUEST, "WEB_002", "Request 요청에서 올바르지 않은 값이 있습니다"), | ||
FAILED_PARSING(BAD_REQUEST, "WEB_003", "Request JSON body를 파싱하지 못했습니다"), | ||
UNSUPPORTED_API(BAD_REQUEST, "WEB_004", "지원하지 않는 API입니다"), | ||
COOKIE_NOT_FOND(BAD_REQUEST, "WEB_005", "요청에 쿠키가 필요합니다"), | ||
|
||
// DatabaseErrorType | ||
ENTITY_NOT_FOUND(NOT_FOUND, "DB_001", "해당 엔티티를 찾을 수 없습니다"), | ||
VIOLATION_OCCURRED(NOT_ACCEPTABLE, "DB_002", "저장할 수 없는 값입니다"), | ||
; | ||
private final HttpStatus httpStatus; | ||
private final String code; | ||
private final String message; | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/dnd/runus/presentation/config/ObjectMapperConfig.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,16 @@ | ||
package com.dnd.runus.presentation.config; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class ObjectMapperConfig { | ||
@Bean | ||
ObjectMapper objectMapper() { | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
objectMapper.registerModule(new JavaTimeModule()); | ||
return objectMapper; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/dnd/runus/presentation/config/WebConfig.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,18 @@ | ||
package com.dnd.runus.presentation.config; | ||
|
||
import com.dnd.runus.presentation.handler.NullResponseHandler; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
public class WebConfig implements WebMvcConfigurer { | ||
private final NullResponseHandler nullResponseHandler; | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
registry.addInterceptor(nullResponseHandler); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/main/java/com/dnd/runus/presentation/dto/response/ApiErrorDto.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,34 @@ | ||
package com.dnd.runus.presentation.dto.response; | ||
|
||
import com.dnd.runus.global.exception.type.ErrorType; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.ToString; | ||
|
||
import static lombok.AccessLevel.PRIVATE; | ||
|
||
@Schema(description = "API 애러 응답 형식") | ||
@Getter | ||
@ToString | ||
@Builder(access = PRIVATE) | ||
public class ApiErrorDto { | ||
@Schema(description = "응답 상태 코드", example = "400") | ||
private final int statusCode; | ||
@Schema(description = "응답 코드 이름 (디버깅용)", example = "FAILED_AUTHENTICATION") | ||
private final String code; | ||
@Schema(description = "응답 메시지 (디버깅용)", example = "인증에 실패했습니다") | ||
private final String message; | ||
|
||
public static ApiErrorDto of( | ||
@NotNull ErrorType type, | ||
String message | ||
) { | ||
return ApiErrorDto.builder() | ||
.statusCode(type.httpStatus().value()) | ||
.code(type.code()) | ||
.message(type.message() + ", " + message) | ||
.build(); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/com/dnd/runus/presentation/dto/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,21 @@ | ||
package com.dnd.runus.presentation.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
|
||
import static java.util.Collections.emptyMap; | ||
import static java.util.Objects.requireNonNullElse; | ||
|
||
@JsonInclude(JsonInclude.Include.NON_NULL) | ||
public record ApiResponse<T>( | ||
boolean success, | ||
T data, | ||
ApiErrorDto error | ||
) { | ||
public static <T> ApiResponse<?> success(T data) { | ||
return new ApiResponse<>(true, requireNonNullElse(data, emptyMap()), null); | ||
} | ||
|
||
public static ApiResponse<?> fail(ApiErrorDto error) { | ||
return new ApiResponse<>(false, null, error); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/main/java/com/dnd/runus/presentation/handler/ApiResponseHandler.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,60 @@ | ||
package com.dnd.runus.presentation.handler; | ||
|
||
import com.dnd.runus.presentation.dto.response.ApiErrorDto; | ||
import com.dnd.runus.presentation.dto.response.ApiResponse; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.annotation.Nonnull; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.core.MethodParameter; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.server.ServerHttpRequest; | ||
import org.springframework.http.server.ServerHttpResponse; | ||
import org.springframework.http.server.ServletServerHttpResponse; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
@RestControllerAdvice(basePackages = "com.dnd.runus") | ||
public class ApiResponseHandler implements ResponseBodyAdvice<Object> { | ||
private final ObjectMapper objectMapper; | ||
|
||
@Override | ||
public boolean supports(@Nonnull MethodParameter returnType, @Nonnull Class converterType) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Object beforeBodyWrite(Object body, @Nonnull MethodParameter returnType, | ||
@Nonnull MediaType selectedContentType, @Nonnull Class selectedConverterType, | ||
@Nonnull ServerHttpRequest request, @Nonnull ServerHttpResponse response) { | ||
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse(); | ||
|
||
HttpStatus resolve = HttpStatus.resolve(servletResponse.getStatus()); | ||
if (resolve == null || !resolve.is2xxSuccessful()) { | ||
if (body instanceof ApiErrorDto error) { | ||
return ApiResponse.fail(error); | ||
} | ||
log.error("Unreachable response handling! request: {}, response: {}, body: {}", request, response, body); | ||
throw new UnsupportedOperationException("Unreachable response handling!" + body); | ||
} | ||
|
||
if (body instanceof String) { | ||
// String 타입을 Wrapper로 감싸면 StringConverter에서 처리할 수 없음 | ||
// 따라서 ObjectMapper를 통해 직렬화하여 반환 | ||
ApiResponse<?> res = ApiResponse.success(body); | ||
try { | ||
String stringRes = objectMapper.writeValueAsString(res); | ||
response.getHeaders().setContentType(MediaType.APPLICATION_JSON); | ||
return stringRes; | ||
} catch (JsonProcessingException err){ | ||
throw new RuntimeException("Failed to convert BaseResponse to JSON"); | ||
} | ||
} | ||
return ApiResponse.success(body); | ||
} | ||
} |
Oops, something went wrong.