From e469d5e043a4b94301da47879cc097e326275228 Mon Sep 17 00:00:00 2001 From: Jung Seonghun <80201773+seonghun-dev@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:18:06 +0900 Subject: [PATCH] :recycle: Refactor : refactoring errorCode by domain (#415) * :recycle: Refactor : refactoring errorCode by domain * :sparkles: feat: change reflection to ClassPathScanningCandidateComponentProvider * :white_check_mark: test : add errorcode duplication test --- backend/streetdrop-api/build.gradle | 2 +- .../common/error/GlobalExceptionHandler.java | 73 +++++++------- .../common/error/dto/CommonErrorCode.java | 50 ++++++++++ .../depromeet/common/error/dto/ErrorCode.java | 59 ------------ .../common/error/dto/ErrorCodeMapper.java | 12 +-- .../common/error/dto/ErrorResponseDto.java | 25 ----- .../common/error/dto/StreetDropErrorCode.java | 15 +++ .../error/dto/StreetDropErrorCodeList.java | 69 ++++++++++++++ .../error/dto/interfaces/ErrorCode.java | 26 +++++ .../interfaces/ErrorCodeCovertInterface.java | 11 +++ .../interfaces/ErrorCodeFieldInterface.java | 42 ++++++++ .../dto/interfaces/ErrorCodeInterface.java | 8 ++ .../common/error/event/ErrorEvent.java | 14 +++ .../exception/common/BusinessException.java | 19 ---- .../exception/common/NotFoundException.java | 17 ---- .../external/ExternalAuthException.java | 4 + .../external/ExternalInternalException.java | 4 + .../external/ExternalServerException.java | 23 +++++ .../ExternalToomanyRequestException.java | 4 + .../exception/internal/BusinessException.java | 20 ++++ .../exception/internal/InfraException.java | 4 + .../exception/internal/NotFoundException.java | 21 ++++ .../common/error/handler/ErrorEvent.java | 12 --- .../error/handler/ErrorEventHandler.java | 10 +- .../error/http/dto/HttpErrorResponseDto.java | 50 ++++++++++ .../domains/geo/controller/GeoController.java | 3 +- .../domains/geo/error/GeoErrorCode.java | 30 ++++++ .../item/controller/ItemClaimController.java | 10 +- .../item/controller/ItemController.java | 11 +++ .../domains/item/error/ItemErrorCode.java | 32 +++++++ .../item/service/ItemClaimService.java | 13 ++- .../domains/item/service/ItemLikeService.java | 12 +-- .../domains/item/service/ItemService.java | 20 ++-- .../domains/music/service/MusicService.java | 6 +- .../service/SearchRecommendService.java | 6 +- .../user/controller/UserBlockController.java | 9 +- .../user/controller/UserController.java | 4 +- .../domains/user/error/UserErrorCode.java | 32 +++++++ .../user/service/UserBlockService.java | 13 +-- .../user/service/UserLevelService.java | 6 +- .../village/service/VillageAreaService.java | 8 +- .../village/service/VillageItemService.java | 6 +- .../swagger/annotation/ApiErrorResponse.java | 4 +- .../config/CustomOperationCustomizer.java | 95 +++++++------------ .../config/ErrorRequestHeaderCustomizer.java | 61 ++++++++++++ .../ErrorResponseExampleCustomizer.java | 63 ++++++++++++ .../security/config/WebSecurityConfig.java | 11 ++- .../handler/CustomAccessDeniedHandler.java | 32 +++++++ .../CustomAuthenticationEntryPoint.java | 32 +++++++ .../error/filter/ErrorTestHeaderFilter.java | 11 ++- .../common/error/StreetDropErrorCodeTest.java | 34 +++++++ .../item/service/ItemClaimServiceTest.java | 8 +- .../item/service/ItemLikeServiceTest.java | 20 ++-- .../user/service/UserBlockServiceTest.java | 8 +- .../service/VillageAreaServiceTest.java | 4 +- .../service/VillageItemServiceTest.java | 4 +- 56 files changed, 871 insertions(+), 331 deletions(-) create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCode.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCode.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCodeList.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCode.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeCovertInterface.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeFieldInterface.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeInterface.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/event/ErrorEvent.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/BusinessException.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/NotFoundException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalAuthException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalInternalException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalServerException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalToomanyRequestException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/BusinessException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/InfraException.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/NotFoundException.java delete mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEvent.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/common/error/http/dto/HttpErrorResponseDto.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/error/GeoErrorCode.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/item/error/ItemErrorCode.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/domains/user/error/UserErrorCode.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorRequestHeaderCustomizer.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorResponseExampleCustomizer.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAccessDeniedHandler.java create mode 100644 backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAuthenticationEntryPoint.java create mode 100644 backend/streetdrop-api/src/test/java/unit/common/error/StreetDropErrorCodeTest.java diff --git a/backend/streetdrop-api/build.gradle b/backend/streetdrop-api/build.gradle index 84fd36f8..56d4b81c 100644 --- a/backend/streetdrop-api/build.gradle +++ b/backend/streetdrop-api/build.gradle @@ -81,4 +81,4 @@ test { tasks.named('test') { useJUnitPlatform() finalizedBy jacocoTestReport -} \ No newline at end of file +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/GlobalExceptionHandler.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/GlobalExceptionHandler.java index ae3b9ba2..edb60cda 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/GlobalExceptionHandler.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/GlobalExceptionHandler.java @@ -1,10 +1,10 @@ package com.depromeet.common.error; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.dto.ErrorResponseDto; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; -import com.depromeet.common.error.handler.ErrorEvent; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.event.ErrorEvent; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; +import com.depromeet.common.error.http.dto.HttpErrorResponseDto; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,6 +15,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.client.HttpClientErrorException; import java.sql.SQLException; @@ -25,70 +26,60 @@ public class GlobalExceptionHandler { private final ApplicationEventPublisher eventPublisher; + @ExceptionHandler(HttpClientErrorException.Forbidden.class) + protected ResponseEntity handleForbiddenException( + final HttpClientErrorException.Forbidden e, + final HttpServletRequest request + ) { + return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.FORBIDDEN); + } + @ExceptionHandler(BusinessException.class) - protected ResponseEntity handleBusinessException( + protected ResponseEntity handleBusinessException( final BusinessException e, final HttpServletRequest request ) { log.error("BusinessException: {} {}", e.getErrorCode(), request.getRequestURL()); - return ResponseEntity - .status(e.getErrorCode().getStatus().value()) - .body(new ErrorResponseDto(e.getErrorCode())); + return HttpErrorResponseDto.toResponseEntity(e.getErrorCode()); } @ExceptionHandler(NotFoundException.class) - protected ResponseEntity handleNotFoundException( - final NotFoundException e, - final HttpServletRequest request) { - - log.error("MemberNotFoundException: {} {}", e.getErrorCode(), request.getRequestURL()); - return ResponseEntity - .status(e.getErrorCode().getStatus().value()) - .body(new ErrorResponseDto(e.getErrorCode())); + protected ResponseEntity handleNotFoundException( + final NotFoundException e + ) { + return HttpErrorResponseDto.toResponseEntity(e.getErrorCode()); } @ExceptionHandler(MethodArgumentNotValidException.class) - protected ResponseEntity handleMethodArgumentNotValidException( + protected ResponseEntity handleMethodArgumentNotValidException( final MethodArgumentNotValidException e, final HttpServletRequest request ) { log.error("MethodArgumentNotValidException: {} {}", e.getMessage(), request.getRequestURL()); - ErrorResponseDto errorResponseDto = ErrorResponseDto.builder() - .code(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getCode()) - .status(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value()) - .error(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().name()) + HttpErrorResponseDto httpErrorResponseDto = HttpErrorResponseDto.builder() + .errorResponseCode(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getErrorResponseCode()) + .status(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value()) + .title(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getTitle()) .message(e.getAllErrors().get(0).getDefaultMessage()).build(); return ResponseEntity - .status(ErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value()) - .body(errorResponseDto); + .status(CommonErrorCode.METHOD_ARGUMENT_NOT_VALID.getStatus().value()) + .body(httpErrorResponseDto); } @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - protected ResponseEntity handleMethodNotSupportException( - final HttpRequestMethodNotSupportedException e - ) { - ErrorResponseDto errorResponseDto = ErrorResponseDto.builder() - .code(ErrorCode.METHOD_NOT_ALLOWED.getCode()) - .status(ErrorCode.METHOD_NOT_ALLOWED.getStatus().value()) - .error(ErrorCode.METHOD_NOT_ALLOWED.getStatus().name()) - .message(e.getMessage()).build(); - - return ResponseEntity - .status(ErrorCode.METHOD_NOT_ALLOWED.getStatus().value()) - .body(errorResponseDto); + protected ResponseEntity handleMethodNotSupportException() { + return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.METHOD_NOT_ALLOWED); } @ExceptionHandler(value = {Exception.class, RuntimeException.class, SQLException.class, DataIntegrityViolationException.class}) - protected ResponseEntity handleInternalException( + protected ResponseEntity handleInternalException( final Exception e, final HttpServletRequest request ) { log.error("Exception: {} {}", e.getMessage(), request.getRequestURL()); - eventPublisher.publishEvent(new ErrorEvent(ErrorCode.INTERNAL_SERVER_ERROR, request, e)); - return ResponseEntity - .status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus().value()) - .body(new ErrorResponseDto(ErrorCode.INTERNAL_SERVER_ERROR)); + eventPublisher.publishEvent(new ErrorEvent<>(CommonErrorCode.INTERNAL_SERVER_ERROR, request, e)); + return HttpErrorResponseDto.toResponseEntity(CommonErrorCode.INTERNAL_SERVER_ERROR); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java new file mode 100644 index 00000000..c7b5c4ca --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/CommonErrorCode.java @@ -0,0 +1,50 @@ +package com.depromeet.common.error.dto; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CommonErrorCode implements ErrorCodeInterface { + /* + * Basic Client Error + */ + BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_BAD_REQUEST", "Bad Request", "The request could not be understood or was missing required parameters."), + METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "COMMON_METHOD_ARGUMENT_NOT_VALID", "Method Argument Not Valid", "One or more method arguments are not valid."), + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON_UNAUTHORIZED", "Unauthenticated", "Authentication is required and has failed or has not been provided."), + FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON_FORBIDDEN", "Forbidden", "Access to the requested resource is forbidden."), + NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON_NOT_FOUND", "Not Found", "The requested resource could not be found."), + METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "COMMON_METHOD_NOT_ALLOWED", "Method Not Allowed", "The method received in the request-line is known by the origin server but not supported."), + CONFLICT(HttpStatus.CONFLICT, "COMMON_CONFLICT", "Conflict", "The request could not be completed due to a conflict with the current state of the target resource."), + + /* + * StreetDrop Common Error + */ + CANNOT_USE_BANNED_WORD(HttpStatus.BAD_REQUEST, "COMMON_CAN_NOT_USE_BANNED_WORD", "Can Not Use Banned Word", "Cannot Use Banned Word"), + + /* + * Basic Server Error + */ + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_INTERNAL_SERVER_ERROR", "Internal Server Error", "An unexpected error occurred"), + NOT_IMPLEMENTED(HttpStatus.NOT_IMPLEMENTED, "COMMON_NOT_IMPLEMENTED", "Not Implemented", "The server does not support the functionality required to fulfill the request."); + + + private final HttpStatus status; + private final String errorResponseCode; + private final String title; + private final String message; + + @Override + public ErrorCode toErrorCode() { + return ErrorCode + .builder() + .status(status) + .errorResponseCode(errorResponseCode) + .title(title) + .message(message) + .build(); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCode.java deleted file mode 100644 index a78ca6b3..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCode.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.depromeet.common.error.dto; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; - -@Getter -@RequiredArgsConstructor -public enum ErrorCode { - /* - * 400 BAD_REQUEST: 잘못된 요청 - */ - BAD_REQUEST(HttpStatus.BAD_REQUEST, "C-0000", "Bad Request"), - - /* - * 404 NOT_FOUND: 리소스를 찾을 수 없음 - */ - NOT_FOUND(HttpStatus.NOT_FOUND, "C-0001", "Not Found the Contents"), - - /* - * 405 METHOD_NOT_ALLOWED: 허용되지 않은 Request Method 호출 - */ - METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "C-0002", "Method Not Allowed"), - - /* - * 500 INTERNAL_SERVER_ERROR: 내부 서버 오류 - */ - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "C-0003", "Internal Server Error"), - - /* - * 400 BAD_REQUEST: 요청 인자가 유효하지 않음 - */ - METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST, "C-0004", "Method Argument Not Valid"), - - /* - * ALREADY_ITEM_LIKED_ERROR: 이미 좋아요한 아이템 - */ - ALREADY_ITEM_LIKED_ERROR(HttpStatus.CONFLICT, "C-0005", "User Already Item liked"), - - ITEM_NOT_LIKED_ERROR(HttpStatus.BAD_REQUEST, "C-0006", "Item Not liked"), - - NOT_SUPPORT_LOCATION(HttpStatus.BAD_REQUEST, "C-0007", "Not Support Location"), - - ALREADY_ITEM_REPORTED_ERROR(HttpStatus.CONFLICT, "C-0008", "User Already Item reported"), - - INVALID_USER_EXCEPTION(HttpStatus.FORBIDDEN, "C-0009", "Modify or Delete Not Permitted"), - - INVALID_INPUT_EXCEPTION(HttpStatus.BAD_REQUEST, "C-0015", "Nickname must be at least 1 character and not more than 10 characters"), - - CAN_NOT_BLOCK_SELF(HttpStatus.BAD_REQUEST, "C-0016", "Can not block myself"), - - SEARCH_TERM_NOT_FOUND(HttpStatus.NOT_FOUND, "C-0010", "Search Term Not Found"), - - CANNOT_USE_BANNED_WORD(HttpStatus.BAD_REQUEST, "C-0010", "Cannot Use Banned Word"); - - private final HttpStatus status; - private final String code; - private final String message; -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCodeMapper.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCodeMapper.java index 056ab7f2..02eed3fa 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCodeMapper.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorCodeMapper.java @@ -1,14 +1,14 @@ package com.depromeet.common.error.dto; +import com.depromeet.common.error.dto.interfaces.ErrorCode; + import java.util.Optional; public class ErrorCodeMapper { public static Optional findByErrorCode(String code) { - for (ErrorCode errorCode : ErrorCode.values()) { - if (errorCode.getCode().equals(code)) { - return Optional.of(errorCode); - } - } - return Optional.empty(); + var result = StreetDropErrorCodeList.getInstance().getStreetDropErrorCodeList(); + return result.stream().filter(streetDropErrorCode -> streetDropErrorCode.getErrorResponseCode().equals(code)) + .findFirst().map(StreetDropErrorCode::getErrorCode); } + } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorResponseDto.java deleted file mode 100644 index e2d73956..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/ErrorResponseDto.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.depromeet.common.error.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -import java.time.LocalDateTime; - -@Getter -@Builder -@AllArgsConstructor -public class ErrorResponseDto { - private final LocalDateTime timestamp = LocalDateTime.now(); - private final int status; - private final String error; - private final String code; - private final String message; - - public ErrorResponseDto(ErrorCode errorCode) { - this.status = errorCode.getStatus().value(); - this.error = errorCode.getStatus().name(); - this.code = errorCode.getCode(); - this.message = errorCode.getMessage(); - } -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCode.java new file mode 100644 index 00000000..3bca4cd4 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCode.java @@ -0,0 +1,15 @@ +package com.depromeet.common.error.dto; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class StreetDropErrorCode { + + private String errorResponseCode; + + private ErrorCode errorCode; + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCodeList.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCodeList.java new file mode 100644 index 00000000..70611954 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/StreetDropErrorCodeList.java @@ -0,0 +1,69 @@ +package com.depromeet.common.error.dto; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Getter +@Slf4j +public class StreetDropErrorCodeList { + + private static volatile StreetDropErrorCodeList instance; + private final List streetDropErrorCodeList; + + private StreetDropErrorCodeList() { + streetDropErrorCodeList = createStreetDropErrorCodeList(); + } + + public static StreetDropErrorCodeList getInstance(){ + if (instance == null) { + synchronized (StreetDropErrorCodeList.class) { + if (instance == null) { + instance = new StreetDropErrorCodeList(); + } + } + } + return instance; + } + + private synchronized List createStreetDropErrorCodeList() { + List streetDropErrorCodeList = new ArrayList<>(); + + try { + ClassPathScanningCandidateComponentProvider s = new ClassPathScanningCandidateComponentProvider(false); + + TypeFilter tf = new AssignableTypeFilter(ErrorCodeInterface.class); + s.addIncludeFilter(tf); + + Set components = s.findCandidateComponents("com.depromeet"); + + for (BeanDefinition component : components) { + Class className = Class.forName(component.getBeanClassName()); + + if (className.isEnum()) { + for (var errorCode : className.getEnumConstants()) { + if (errorCode != null) { + String errorResponseCode = (String) errorCode.getClass().getMethod("getErrorResponseCode").invoke(errorCode); + ErrorCode error = (ErrorCode) errorCode.getClass().getMethod("toErrorCode").invoke(errorCode); + var streetDropError = new StreetDropErrorCode(errorResponseCode, error); + streetDropErrorCodeList.add(streetDropError); + } + } + } + } + + } catch (Exception ignored) { + } + + return streetDropErrorCodeList; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCode.java new file mode 100644 index 00000000..6d38b052 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCode.java @@ -0,0 +1,26 @@ +package com.depromeet.common.error.dto.interfaces; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +@Builder +public class ErrorCode implements ErrorCodeInterface { + private HttpStatus status; + private String errorResponseCode; + private String title; + private String message; + + @Override + public ErrorCode toErrorCode() { + return this; + } + + public void appendMessage(String additionalMessage) { + this.message += " " + additionalMessage; + } + +} \ No newline at end of file diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeCovertInterface.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeCovertInterface.java new file mode 100644 index 00000000..163e09ec --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeCovertInterface.java @@ -0,0 +1,11 @@ +package com.depromeet.common.error.dto.interfaces; + +public sealed interface ErrorCodeCovertInterface permits ErrorCodeInterface { + + /** + * Converts this to an ErrorCode object. + * + * @return An ErrorCode object. + */ + ErrorCode toErrorCode(); +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeFieldInterface.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeFieldInterface.java new file mode 100644 index 00000000..8b9e09a2 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeFieldInterface.java @@ -0,0 +1,42 @@ +package com.depromeet.common.error.dto.interfaces; + +import org.springframework.http.HttpStatus; + +/** + * This interface provides a basic specification for defining domain error codes. + * This interface used to extend errorCodeEnum. + * Recommend to using Lombok Getter + */ +public sealed interface ErrorCodeFieldInterface permits ErrorCodeInterface { + + /** + * Returns the HTTP status code. + * + * @return The HTTP status code. + */ + HttpStatus getStatus(); + + + /** + * Returns the error code. All errors are categorized by domain, The first start of any code is a domain code. It is not duplicated by domain at all. + * + * @return The error code. + */ + String getErrorResponseCode(); + + + /** + * Returns the error title - Short Human Readable Error Title. + * + * @return The error title. + */ + String getTitle(); + + /** + * Returns the error message - Human Readable Error Message, It is also recommended that you write solutions together. + * + * @return The error message. + */ + String getMessage(); + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeInterface.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeInterface.java new file mode 100644 index 00000000..8c6ba031 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/dto/interfaces/ErrorCodeInterface.java @@ -0,0 +1,8 @@ +package com.depromeet.common.error.dto.interfaces; + +/** + * To handle error codes by domain, this interface must be extended and used. + * Check ErrorCodeFieldInterface for fields and ErrorCodeCurtInterface for transducers. + */ +public non-sealed interface ErrorCodeInterface extends ErrorCodeCovertInterface, ErrorCodeFieldInterface { +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/event/ErrorEvent.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/event/ErrorEvent.java new file mode 100644 index 00000000..2f941040 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/event/ErrorEvent.java @@ -0,0 +1,14 @@ +package com.depromeet.common.error.event; + +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ErrorEvent { + public T errorCode; + public HttpServletRequest request; + public Exception e; +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/BusinessException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/BusinessException.java deleted file mode 100644 index fdf2d101..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/BusinessException.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.depromeet.common.error.exception.common; - -import com.depromeet.common.error.dto.ErrorCode; -import lombok.Getter; - -@Getter -public class BusinessException extends RuntimeException { - private final ErrorCode errorCode; - - public BusinessException(ErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; - } - - public BusinessException(ErrorCode errorCode, String message) { - super(message); - this.errorCode = errorCode; - } -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/NotFoundException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/NotFoundException.java deleted file mode 100644 index b0748897..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/common/NotFoundException.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.depromeet.common.error.exception.common; - -import com.depromeet.common.error.dto.ErrorCode; - -public class NotFoundException extends BusinessException { - public NotFoundException(ErrorCode errorCode) { - super(errorCode); - } - - public NotFoundException(ErrorCode errorCode, String content) { - super(errorCode, content + " is not found"); - } - - public NotFoundException(ErrorCode errorCode, Long id) { - super(errorCode, id + " is not found"); - } -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalAuthException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalAuthException.java new file mode 100644 index 00000000..8512dde6 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalAuthException.java @@ -0,0 +1,4 @@ +package com.depromeet.common.error.exception.external; + +public class ExternalAuthException { +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalInternalException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalInternalException.java new file mode 100644 index 00000000..60afad49 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalInternalException.java @@ -0,0 +1,4 @@ +package com.depromeet.common.error.exception.external; + +public class ExternalInternalException { +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalServerException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalServerException.java new file mode 100644 index 00000000..c05b3b77 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalServerException.java @@ -0,0 +1,23 @@ +package com.depromeet.common.error.exception.external; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.Getter; + +@Getter +public class ExternalServerException extends RuntimeException { + private final Exception e; + private final ErrorCode errorCode; + + public ExternalServerException(Exception e, T errorCode) { + super(errorCode.getMessage()); + this.e = e; + this.errorCode = errorCode.toErrorCode(); + } + + public ExternalServerException(T errorCode, String message, Exception e) { + super(message); + this.errorCode = errorCode.toErrorCode(); + this.e = e; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalToomanyRequestException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalToomanyRequestException.java new file mode 100644 index 00000000..db924a36 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/external/ExternalToomanyRequestException.java @@ -0,0 +1,4 @@ +package com.depromeet.common.error.exception.external; + +public class ExternalToomanyRequestException { +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/BusinessException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/BusinessException.java new file mode 100644 index 00000000..6ef0192b --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/BusinessException.java @@ -0,0 +1,20 @@ +package com.depromeet.common.error.exception.internal; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.Getter; + +@Getter +public class BusinessException extends RuntimeException { + private final ErrorCode errorCode; + + public BusinessException(T errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode.toErrorCode(); + } + + public BusinessException(T errorCode, String additionalMessage) { + this.errorCode = errorCode.toErrorCode(); + this.errorCode.appendMessage(additionalMessage); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/InfraException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/InfraException.java new file mode 100644 index 00000000..438a88a2 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/InfraException.java @@ -0,0 +1,4 @@ +package com.depromeet.common.error.exception.internal; + +public class InfraException extends RuntimeException{ +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/NotFoundException.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/NotFoundException.java new file mode 100644 index 00000000..297b1d32 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/exception/internal/NotFoundException.java @@ -0,0 +1,21 @@ +package com.depromeet.common.error.exception.internal; + +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.Getter; + +@Getter +public class NotFoundException extends BusinessException { + + public NotFoundException(T errorCode) { + super(errorCode.toErrorCode()); + } + + public NotFoundException(T errorCode, String additionalMessage) { + super(errorCode.toErrorCode(), additionalMessage); + } + + public NotFoundException(T errorCode, Long id) { + super(errorCode.toErrorCode(), id + " is not found"); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEvent.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEvent.java deleted file mode 100644 index 70d8162c..00000000 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.depromeet.common.error.handler; - -import com.depromeet.common.error.dto.ErrorCode; -import jakarta.servlet.http.HttpServletRequest; -import lombok.AllArgsConstructor; - -@AllArgsConstructor -public class ErrorEvent { - public ErrorCode errorCode; - public HttpServletRequest request; - public Exception e; -} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEventHandler.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEventHandler.java index cfcd7ae2..3e2ed149 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEventHandler.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/handler/ErrorEventHandler.java @@ -1,23 +1,25 @@ package com.depromeet.common.error.handler; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import com.depromeet.common.error.event.ErrorEvent; import com.depromeet.monitoring.MonitoringProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import static com.depromeet.common.error.dto.ErrorCode.INTERNAL_SERVER_ERROR; +import static com.depromeet.common.error.dto.CommonErrorCode.INTERNAL_SERVER_ERROR; @Component @RequiredArgsConstructor -public class ErrorEventHandler { +public class ErrorEventHandler { private final MonitoringProvider monitoringProvider; @EventListener @Async - public void handleErrorEvent(ErrorEvent event) { - switch (event.errorCode) { + public void handleErrorEvent(ErrorEvent event) { + switch (event.getErrorCode().getStatus()) { case INTERNAL_SERVER_ERROR: monitoringProvider.sendError(INTERNAL_SERVER_ERROR.getMessage(), event.request, event.e); break; diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/common/error/http/dto/HttpErrorResponseDto.java b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/http/dto/HttpErrorResponseDto.java new file mode 100644 index 00000000..7b26bb39 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/common/error/http/dto/HttpErrorResponseDto.java @@ -0,0 +1,50 @@ +package com.depromeet.common.error.http.dto; + +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import org.slf4j.MDC; +import org.springframework.http.ResponseEntity; + +import java.time.LocalDateTime; + +/** + * Represents a Data Transfer Object (DTO) for handling error responses in a standardized way. + * This class is designed to encapsulate information about an error, including timestamp, trace ID, + * status code, error response code, title, and a detailed error message. + * All HTTP errors are processed based on the following error responses + */ +@Getter +@Builder +@AllArgsConstructor +public class HttpErrorResponseDto { + + private final LocalDateTime timestamp = LocalDateTime.now(); + + private final String traceId = MDC.get("traceId"); + + private final int status; + + private final String errorResponseCode; + + private final String title; + + private final String message; + + public static HttpErrorResponseDto from(T errorCode) { + return HttpErrorResponseDto.builder() + .status(errorCode.getStatus().value()) + .errorResponseCode(errorCode.getErrorResponseCode()) + .title(errorCode.getTitle()) + .message(errorCode.getMessage()) + .build(); + } + + public static ResponseEntity toResponseEntity(T errorCode) { + return ResponseEntity + .status(errorCode.getStatus()) + .body(HttpErrorResponseDto.from(errorCode)); + } + +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/controller/GeoController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/controller/GeoController.java index 3ddefc38..aba71586 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/controller/GeoController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/controller/GeoController.java @@ -2,7 +2,6 @@ import com.depromeet.common.dto.ResponseDto; -import com.depromeet.common.error.dto.ErrorCode; import com.depromeet.domains.geo.dto.request.ReverseGeocodeRequestDto; import com.depromeet.domains.geo.dto.response.ReverseGeocodeResponseDto; import com.depromeet.domains.geo.service.GeoService; @@ -27,7 +26,7 @@ public class GeoController { @Operation(summary = "Reverse Geocoding") @ApiResponse(responseCode = "200", description = "좌표 주소 변환 성공") - @ApiErrorResponse(errorCode = ErrorCode.NOT_SUPPORT_LOCATION, description = "유효하지 않은 좌표 입니다") + @ApiErrorResponse(errorCode = "GEO_NOT_SUPPORT_LOCATION", description = "유효하지 않은 좌표 입니다") @GetMapping("/reverse-geocode") public ResponseEntity reverseGeocode( @Valid ReverseGeocodeRequestDto reverseGeocodeRequestDto diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/error/GeoErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/error/GeoErrorCode.java new file mode 100644 index 00000000..d74aa317 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/geo/error/GeoErrorCode.java @@ -0,0 +1,30 @@ +package com.depromeet.domains.geo.error; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum GeoErrorCode implements ErrorCodeInterface { + NOT_SUPPORT_LOCATION(HttpStatus.BAD_REQUEST, "GEO_NOT_SUPPORT_LOCATION", "Not Support Location", "The Location is currently not serviced"); + + private final HttpStatus status; + private final String errorResponseCode; + private final String title; + private final String message; + + + @Override + public ErrorCode toErrorCode() { + return ErrorCode + .builder() + .status(status) + .errorResponseCode(errorResponseCode) + .title(title) + .message(message) + .build(); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemClaimController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemClaimController.java index 7bb6165f..122eb7e7 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemClaimController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemClaimController.java @@ -1,7 +1,6 @@ package com.depromeet.domains.item.controller; import com.depromeet.common.dto.ResponseDto; -import com.depromeet.common.error.dto.ErrorCode; import com.depromeet.domains.item.dto.request.ItemClaimRequestDto; import com.depromeet.domains.item.service.ItemClaimService; import com.depromeet.external.swagger.annotation.ApiErrorResponse; @@ -14,7 +13,10 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/items") @@ -26,8 +28,8 @@ public class ItemClaimController { @Operation(summary = "아이템 신고하기") @ApiResponse(responseCode = "204", description = "신고 성공") @ApiErrorResponses(value = { - @ApiErrorResponse(errorCode = ErrorCode.NOT_FOUND, description = "아이템을 찾을 수 없습니다."), - @ApiErrorResponse(errorCode = ErrorCode.ALREADY_ITEM_REPORTED_ERROR, description = "이미 신고한 아이템입니다.") + @ApiErrorResponse(errorCode = "ITEM_NOT_FOUND", description = "아이템을 찾을 수 없습니다."), + @ApiErrorResponse(errorCode = "ITEM_ALREADY_ITEM_REPORTED_ERROR", description = "이미 신고한 아이템입니다.") }) @PostMapping("/claim") public ResponseEntity claimItem( diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemController.java index 2e9212b3..2fa03d1f 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/controller/ItemController.java @@ -10,6 +10,8 @@ import com.depromeet.domains.item.dto.response.ItemsResponseDto; import com.depromeet.domains.item.dto.response.PoiResponseDto; import com.depromeet.domains.item.service.ItemService; +import com.depromeet.external.swagger.annotation.ApiErrorResponse; +import com.depromeet.external.swagger.annotation.ApiErrorResponses; import com.depromeet.security.annotation.ReqUser; import com.depromeet.user.User; import io.swagger.v3.oas.annotations.Operation; @@ -28,6 +30,7 @@ public class ItemController { @Operation(summary = "주변 아이템 조회 - POI") @GetMapping("/points") + @ApiErrorResponse(errorCode = "GEO_NOT_SUPPORT_LOCATION", description = "현재 서비스가 제공되지 않는 지역입니다.") public ResponseEntity findNearItemsPoints( @ReqUser User user, @Valid NearItemPointRequestDto nearItemPointRequestDto) { @@ -37,6 +40,10 @@ public ResponseEntity findNearItemsPoints( @Operation(summary = "아이템 드랍 - 등록") @PostMapping + @ApiErrorResponses(value = { + @ApiErrorResponse(errorCode = "GEO_NOT_SUPPORT_LOCATION", description = "현재 서비스가 제공되지 않는 지역입니다."), + @ApiErrorResponse(errorCode = "COMMON_CAN_NOT_USE_BANNED_WORD", description = "사용할 수 없는 금칙어가 포함되어 있습니다.") + }) public ResponseEntity create( @ReqUser User user, @Valid @RequestBody ItemCreateRequestDto itemRequestDto) { @@ -46,6 +53,7 @@ public ResponseEntity create( @Operation(summary = "아이템 드랍 - 단건조회") @GetMapping("/{itemId}") + @ApiErrorResponse(errorCode = "ITEM_NOT_FOUND", description = "아이템을 찾을 수 없습니다.") public ResponseEntity findOneItem( @ReqUser User user, @PathVariable(value = "itemId") Long itemId @@ -56,6 +64,7 @@ public ResponseEntity findOneItem( @Operation(summary = "아이템 드랍 - 수정") @PatchMapping("/{itemId}") + @ApiErrorResponse(errorCode = "ITEM_NOT_FOUND", description = "아이템을 찾을 수 없습니다.") public ResponseEntity update( @ReqUser User user, @PathVariable(value = "itemId") Long itemId, @@ -67,6 +76,7 @@ public ResponseEntity update( @Operation(summary = "드랍 아이템 삭제") @DeleteMapping("/{itemId}") + @ApiErrorResponse(errorCode = "ITEM_NOT_FOUND", description = "아이템을 찾을 수 없습니다.") public ResponseEntity delete( @ReqUser User user, @PathVariable(value = "itemId") Long itemId @@ -77,6 +87,7 @@ public ResponseEntity delete( @Operation(summary = "주변 아이템 상세 조회") @GetMapping + @ApiErrorResponse(errorCode = "GEO_NOT_SUPPORT_LOCATION", description = "현재 서비스가 제공되지 않는 지역입니다.") public ResponseEntity findNearItems( @ReqUser User user, @Valid NearItemRequestDto nearItemRequestDto diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/error/ItemErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/error/ItemErrorCode.java new file mode 100644 index 00000000..224ae168 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/error/ItemErrorCode.java @@ -0,0 +1,32 @@ +package com.depromeet.domains.item.error; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ItemErrorCode implements ErrorCodeInterface { + ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "ITEM_NOT_FOUND", "Item Not Found", "Item Not Found"), + ITEM_ALREADY_LIKED(HttpStatus.CONFLICT, "ITEM_ALREADY_LIKED", "Item Already Liked", "User already item liked"), + ITEM_ALREADY_REPORTED(HttpStatus.CONFLICT, " ITEM_ALREADY_REPORTED", "Item Already Reported", "User already item reported"); + + private final HttpStatus status; + private final String errorResponseCode; + private final String title; + private final String message; + + + @Override + public ErrorCode toErrorCode() { + return ErrorCode + .builder() + .status(status) + .errorResponseCode(errorResponseCode) + .title(title) + .message(message) + .build(); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemClaimService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemClaimService.java index d65ea4e0..f9442997 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemClaimService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemClaimService.java @@ -1,16 +1,15 @@ package com.depromeet.domains.item.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.item.dto.request.ItemClaimRequestDto; +import com.depromeet.domains.item.error.ItemErrorCode; import com.depromeet.domains.item.repository.ItemClaimRepository; import com.depromeet.domains.item.repository.ItemRepository; import com.depromeet.item.ItemClaim; import com.depromeet.report.claim.dto.ItemClaimReportDto; -import com.depromeet.report.claim.service.DiscordItemClaimReportService; import com.depromeet.report.claim.service.ItemClaimReportService; -import com.depromeet.report.claim.service.SlackItemClaimReportService; import com.depromeet.user.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -29,7 +28,7 @@ public class ItemClaimService { public void claimItem(User user, ItemClaimRequestDto itemClaimRequestDto) { var itemId = itemClaimRequestDto.getItemId(); var item = itemRepository.findById(itemId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, String.valueOf(itemId))); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, String.valueOf(itemId))); checkUserAlreadyReport(user.getId(), itemId); var itemClaim = ItemClaim.builder() @@ -56,7 +55,7 @@ public void claimItem(User user, ItemClaimRequestDto itemClaimRequestDto) { private void checkUserAlreadyReport(Long userId, Long itemId) { boolean alreadyReported = itemClaimRepository.existsByUserIdAndItemId(userId, itemId); if (alreadyReported) { - throw new BusinessException(ErrorCode.ALREADY_ITEM_REPORTED_ERROR); + throw new BusinessException(ItemErrorCode.ITEM_ALREADY_REPORTED); } } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemLikeService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemLikeService.java index 956855ae..e13a833a 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemLikeService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemLikeService.java @@ -2,19 +2,19 @@ import com.depromeet.common.dto.InfiniteScrollMetaResponseDto; import com.depromeet.common.dto.InfiniteScrollResponseDto; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.domains.item.dao.UserItemLikeDao; import com.depromeet.domains.item.dto.response.ItemGroupByDateResponseDto; import com.depromeet.domains.item.dto.response.ItemGroupResponseDto; +import com.depromeet.domains.item.dto.response.ItemLikeResponseDto; import com.depromeet.domains.item.dto.response.ItemLocationResponseDto; -import com.depromeet.domains.item.dao.UserItemLikeDao; +import com.depromeet.domains.item.error.ItemErrorCode; +import com.depromeet.domains.item.repository.ItemLikeRepository; import com.depromeet.domains.music.dto.response.MusicResponseDto; import com.depromeet.domains.user.dto.response.UserResponseDto; import com.depromeet.item.Item; import com.depromeet.item.ItemLike; -import com.depromeet.domains.item.dto.response.ItemLikeResponseDto; import com.depromeet.user.User; -import com.depromeet.domains.item.repository.ItemLikeRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -45,7 +45,7 @@ public ItemLikeResponseDto likeItem(User user, Long itemId) { private void checkUserAlreadyLike(User user, Item item) { boolean alreadyLiked = itemLikeRepository.existsByUserIdAndItemId(user.getId(), item.getId()); if (alreadyLiked) { - throw new BusinessException(ErrorCode.ALREADY_ITEM_LIKED_ERROR); + throw new BusinessException(ItemErrorCode.ITEM_ALREADY_LIKED); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemService.java index 0b33e1ed..0e384e84 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/item/service/ItemService.java @@ -1,16 +1,18 @@ package com.depromeet.domains.item.service; import com.depromeet.area.village.VillageArea; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.item.dto.request.*; import com.depromeet.domains.item.dto.response.*; +import com.depromeet.domains.item.error.ItemErrorCode; import com.depromeet.domains.item.repository.ItemLikeRepository; import com.depromeet.domains.item.repository.ItemLocationRepository; import com.depromeet.domains.item.repository.ItemRepository; import com.depromeet.domains.music.service.MusicService; import com.depromeet.domains.user.dto.response.UserResponseDto; +import com.depromeet.domains.user.error.UserErrorCode; import com.depromeet.domains.user.repository.BlockUserRepository; import com.depromeet.domains.village.service.VillageAreaService; import com.depromeet.item.Item; @@ -81,7 +83,7 @@ public ItemResponseDto create(User user, ItemCreateRequestDto itemCreateRequestD @Transactional(readOnly = true) public ItemDetailResponseDto findOneItem(User user, Long itemId) { var item = itemRepository.findById(itemId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, itemId)); + .orElseThrow(() -> new NotFoundException(ItemErrorCode.ITEM_NOT_FOUND, itemId)); var itemLikeCount = itemLikeRepository.countByItemId(itemId); var isLiked = itemLikeRepository.existsByUserIdAndItemId(user.getId(), itemId); @@ -125,17 +127,17 @@ private List getBlockedUserIds(User user) { @Transactional(readOnly = true) public Item getItem(Long itemId) { return itemRepository.findById(itemId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, String.valueOf(itemId))); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, String.valueOf(itemId))); } @Transactional public void delete(User user, Long itemId) { var item = itemRepository.findById(itemId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, itemId)); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, itemId)); var userIdfv = item.getUser().getIdfv(); if (!userIdfv.equals(user.getIdfv())) { - throw new BusinessException(ErrorCode.INVALID_USER_EXCEPTION); + throw new BusinessException(UserErrorCode.INVALID_USER_EXCEPTION); } itemRepository.deleteById(itemId); } @@ -143,10 +145,10 @@ public void delete(User user, Long itemId) { @Transactional public ItemResponseDto update(User user, Long itemId, ItemUpdateRequestDto itemRequestDto) { var item = itemRepository.findById(itemId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, itemId)); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, itemId)); if (!item.getUser().getIdfv().equals(user.getIdfv())) { - throw new BusinessException(ErrorCode.INVALID_USER_EXCEPTION); + throw new BusinessException(UserErrorCode.INVALID_USER_EXCEPTION); } item.updateContent(itemRequestDto.getContent()); return new ItemResponseDto(item); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java index 73bb2f49..758a50f8 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/music/service/MusicService.java @@ -1,7 +1,7 @@ package com.depromeet.domains.music.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.music.album.repository.AlbumRepository; import com.depromeet.domains.music.artist.repository.ArtistRepository; import com.depromeet.domains.music.dto.request.MusicRequestDto; @@ -118,6 +118,6 @@ public Song createSong(String songName, Album album) { public MusicResponseDto getMusic(Long songId) { return songRepository.findSongById(songId) .map(MusicResponseDto::new) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, songId)); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, songId)); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java index 0dc6df00..c7654149 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/recommend/service/SearchRecommendService.java @@ -1,7 +1,7 @@ package com.depromeet.domains.recommend.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.BusinessException; import com.depromeet.domains.recommend.dto.response.SearchTermRecommendResponseDto; import com.depromeet.domains.recommend.dto.response.TextColorDto; import com.depromeet.domains.recommend.repository.SearchRecommendTermRepository; @@ -16,7 +16,7 @@ public class SearchRecommendService { public SearchTermRecommendResponseDto recommendSearchTerm() { var recommendTerm = searchRecommendTermRepository.getRandomSearchRecommendTerm() - .orElseThrow(() -> new BusinessException(ErrorCode.SEARCH_TERM_NOT_FOUND)); + .orElseThrow(() -> new BusinessException(CommonErrorCode.INTERNAL_SERVER_ERROR)); var description = recommendTerm.getDescription().stream() .map((textColorVo) -> diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserBlockController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserBlockController.java index c9f888dc..505da39f 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserBlockController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserBlockController.java @@ -1,10 +1,10 @@ package com.depromeet.domains.user.controller; import com.depromeet.common.dto.ResponseDto; -import com.depromeet.common.error.dto.ErrorCode; import com.depromeet.domains.user.dto.response.BlockUserResponseDto; import com.depromeet.domains.user.service.UserBlockService; import com.depromeet.external.swagger.annotation.ApiErrorResponse; +import com.depromeet.external.swagger.annotation.ApiErrorResponses; import com.depromeet.security.annotation.ReqUser; import com.depromeet.user.User; import io.swagger.v3.oas.annotations.Operation; @@ -25,7 +25,10 @@ public class UserBlockController { @Operation(summary = "사용자 차단하기") @ApiResponse(responseCode = "200", description = "차단 성공") - @ApiErrorResponse(errorCode = ErrorCode.NOT_FOUND, description = "차단하려는 사용자가 존재하지 않음") + @ApiErrorResponses(value = { + @ApiErrorResponse(errorCode = "USER_NOT_FOUND", description = "차단하려는 사용자가 존재하지 않음"), + @ApiErrorResponse(errorCode = "USER_CAN_NOT_BLOCK_SELF", description = "자기 자신을 차단할 수 없습니다.") + }) @PostMapping("/users/block") public ResponseEntity blockUser( @ReqUser User user, @@ -37,7 +40,7 @@ public ResponseEntity blockUser( @Operation(summary = "사용자 차단 해제하기") @ApiResponse(responseCode = "204", description = "차단 해제 성공") - @ApiErrorResponse(errorCode = ErrorCode.NOT_FOUND, description = "차단 해제하려는 사용자가 존재하지 않음") + @ApiErrorResponse(errorCode = "USER_NOT_FOUND", description = "차단 해제하려는 사용자가 존재하지 않음") @DeleteMapping("/users/unblock") public ResponseEntity unBlockUser( @ReqUser User user, diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserController.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserController.java index 0d8b8dad..b35a841e 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserController.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/controller/UserController.java @@ -1,7 +1,6 @@ package com.depromeet.domains.user.controller; import com.depromeet.common.dto.ResponseDto; -import com.depromeet.common.error.dto.ErrorCode; import com.depromeet.domains.user.dto.request.NicknameChangeDto; import com.depromeet.domains.user.dto.response.UserLevelResponseDto; import com.depromeet.domains.user.dto.response.UserResponseDto; @@ -17,7 +16,6 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @RestController @@ -62,7 +60,7 @@ public ResponseEntity changeMusicApp( @Operation(summary = "사용자 레벨 조회") @ApiResponse(responseCode = "200", description = "사용자 레벨 조회 성공") - @ApiErrorResponse(errorCode = ErrorCode.NOT_FOUND, description = "사용자 유저 레벨이 존재하지 않음") + @ApiErrorResponse(errorCode = "COMMON_NOT_FOUND", description = "사용자 유저 레벨이 존재하지 않음") @GetMapping("/me/level") public ResponseEntity getUserLevel(@ReqUser User user) { var response = userLevelService.getUserLevel(user); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/error/UserErrorCode.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/error/UserErrorCode.java new file mode 100644 index 00000000..e7a28097 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/error/UserErrorCode.java @@ -0,0 +1,32 @@ +package com.depromeet.domains.user.error; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum UserErrorCode implements ErrorCodeInterface { + USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_NOT_FOUND", "User Not Found", "User with the corresponding id could not be found"), + INVALID_USER_EXCEPTION(HttpStatus.FORBIDDEN, "USER_FORBIDDEN", "User does not have permission", "Modify or Delete Not Permitted"), + USER_NICKNAME_INVALID(HttpStatus.BAD_REQUEST, "USER_NICKNAME_INVALID", "User nickname is invalid", "Nickname must be at least 1 character and not more than 10 characters"), + USER_CAN_NOT_BLOCK_SELF(HttpStatus.BAD_REQUEST, "USER_CAN_NOT_BLOCK_SELF", "Can Not Block Myself", "You can't block yourself"); + + private final HttpStatus status; + private final String errorResponseCode; + private final String title; + private final String message; + + @Override + public ErrorCode toErrorCode() { + return ErrorCode + .builder() + .status(status) + .errorResponseCode(errorResponseCode) + .title(title) + .message(message) + .build(); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserBlockService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserBlockService.java index dbb2087c..43956c26 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserBlockService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserBlockService.java @@ -1,9 +1,10 @@ package com.depromeet.domains.user.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.user.dto.response.BlockUserResponseDto; +import com.depromeet.domains.user.error.UserErrorCode; import com.depromeet.domains.user.repository.BlockUserRepository; import com.depromeet.domains.user.repository.UserRepository; import com.depromeet.report.block.dto.UserBlockReportDto; @@ -29,10 +30,10 @@ public class UserBlockService { @Transactional public BlockUserResponseDto blockUser(User user, Long blockUserID) { var blockUser = userRepository.findUserById(blockUserID) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, user.getId())); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, user.getId())); if (blockUser.getId().equals(user.getId())) { - throw new BusinessException(ErrorCode.CAN_NOT_BLOCK_SELF); + throw new BusinessException(UserErrorCode.USER_CAN_NOT_BLOCK_SELF); } BlockUser block = BlockUser.builder() @@ -57,7 +58,7 @@ public BlockUserResponseDto blockUser(User user, Long blockUserID) { public void unBlockUser(User user, Long unblockUserId) { var userId = user.getId(); var blockedUser = blockUserRepository.findBlockUserByBlockerIdAndBlockedId(userId, unblockUserId) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, unblockUserId)); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, unblockUserId)); blockUserRepository.delete(blockedUser); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java index 5d9bc447..e93d4482 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/user/service/UserLevelService.java @@ -1,7 +1,7 @@ package com.depromeet.domains.user.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.user.dto.response.UserLevelResponseDto; import com.depromeet.domains.user.repository.UserLevelRepository; import com.depromeet.user.User; @@ -18,7 +18,7 @@ public class UserLevelService { @Transactional(readOnly = true) public UserLevelResponseDto getUserLevel(User user) { var level = userLevelRepository.findById(user.getUserLevelId()) - .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND, user.getUserLevelId())); + .orElseThrow(() -> new NotFoundException(CommonErrorCode.NOT_FOUND, user.getUserLevelId())); return new UserLevelResponseDto(user, level); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageAreaService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageAreaService.java index 1bab35e7..b3e8ff26 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageAreaService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageAreaService.java @@ -1,9 +1,9 @@ package com.depromeet.domains.village.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.domains.village.repository.VillageAreaRepository; import com.depromeet.area.village.VillageArea; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.domains.geo.error.GeoErrorCode; +import com.depromeet.domains.village.repository.VillageAreaRepository; import lombok.RequiredArgsConstructor; import org.locationtech.jts.geom.Point; import org.springframework.stereotype.Service; @@ -18,7 +18,7 @@ public class VillageAreaService { @Transactional(readOnly = true) public VillageArea getVillageByLocationPoint(Point point) { return villageAreaRepository.findVillageByLocationPoint(point).orElseThrow( - () -> new BusinessException(ErrorCode.NOT_SUPPORT_LOCATION) + () -> new BusinessException(GeoErrorCode.NOT_SUPPORT_LOCATION) ); } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageItemService.java b/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageItemService.java index 93d64f4c..17118d4f 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageItemService.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/domains/village/service/VillageItemService.java @@ -1,8 +1,8 @@ package com.depromeet.domains.village.service; import com.depromeet.area.village.VillageArea; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.domains.geo.error.GeoErrorCode; import com.depromeet.domains.village.dto.request.VillageItemsRequestDto; import com.depromeet.domains.village.dto.response.VillageItemsCountResponseDto; import com.depromeet.domains.village.repository.VillageAreaRepository; @@ -25,7 +25,7 @@ public VillageItemsCountResponseDto countItemsByVillage(String villageName) { public VillageItemsCountResponseDto countItemsInVillageByLocation(VillageItemsRequestDto villageItemsRequestDto) { Point point = GeomUtil.createPoint(villageItemsRequestDto.getLongitude(), villageItemsRequestDto.getLatitude()); VillageArea villageArea = villageAreaRepository.findVillageByLocationPoint(point).orElseThrow( - () -> new BusinessException(ErrorCode.NOT_SUPPORT_LOCATION) + () -> new BusinessException(GeoErrorCode.NOT_SUPPORT_LOCATION) ); var response = villageAreaRepository.countItemsByVillageName(villageArea.getVillageName()); return new VillageItemsCountResponseDto(response, villageArea.getVillageName()); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/annotation/ApiErrorResponse.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/annotation/ApiErrorResponse.java index d6119455..4d14cc4f 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/annotation/ApiErrorResponse.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/annotation/ApiErrorResponse.java @@ -1,7 +1,5 @@ package com.depromeet.external.swagger.annotation; -import com.depromeet.common.error.dto.ErrorCode; - import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @@ -12,6 +10,6 @@ String description() default ""; - ErrorCode errorCode() default ErrorCode.INTERNAL_SERVER_ERROR; + String errorCode() default "COMMON_INTERNAL_SERVER_ERROR"; } \ No newline at end of file diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/CustomOperationCustomizer.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/CustomOperationCustomizer.java index 5affcb72..6f9db26b 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/CustomOperationCustomizer.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/CustomOperationCustomizer.java @@ -1,15 +1,12 @@ package com.depromeet.external.swagger.config; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.dto.ErrorResponseDto; +import com.depromeet.common.error.dto.ErrorCodeMapper; +import com.depromeet.common.error.dto.interfaces.ErrorCode; import com.depromeet.external.swagger.annotation.ApiErrorResponse; import com.depromeet.external.swagger.annotation.ApiErrorResponses; import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.examples.Example; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.OperationCustomizer; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; @@ -18,75 +15,53 @@ import java.util.List; import java.util.Map; -import static java.util.stream.Collectors.groupingBy; +import static java.util.Collections.emptyMap; @Component +@Slf4j +@AllArgsConstructor public class CustomOperationCustomizer implements OperationCustomizer { + private final ErrorRequestHeaderCustomizer errorRequestHeaderCustomizer; + private final ErrorResponseExampleCustomizer errorResponseExampleCustomizer; + @Override public Operation customize(Operation operation, HandlerMethod handlerMethod) { - ApiErrorResponse apiErrorResponseAnnotation = - handlerMethod.getMethodAnnotation(ApiErrorResponse.class); - ApiErrorResponses apiErrorResponsesAnnotation = - handlerMethod.getMethodAnnotation(ApiErrorResponses.class); + ApiErrorResponse apiErrorResponseAnnotation = handlerMethod.getMethodAnnotation(ApiErrorResponse.class); + ApiErrorResponses apiErrorResponsesAnnotation = handlerMethod.getMethodAnnotation(ApiErrorResponses.class); + if (apiErrorResponseAnnotation != null) { - generateErrorResponseExample(operation, apiErrorResponseAnnotation.errorCode(), apiErrorResponseAnnotation.description()); + handleApiErrorResponse(operation, apiErrorResponseAnnotation); } + if (apiErrorResponsesAnnotation != null) { - Arrays.stream(apiErrorResponsesAnnotation.value()) - .forEach(apiErrorResponse -> - generateErrorResponseExample(operation, apiErrorResponse.errorCode(), apiErrorResponse.description())); + handleApiErrorResponses(operation, apiErrorResponsesAnnotation); } + return operation; } + private void handleApiErrorResponse(Operation operation, ApiErrorResponse apiErrorResponseAnnotation) { + ErrorCodeMapper.findByErrorCode(apiErrorResponseAnnotation.errorCode()) + .ifPresent(errorCode -> { + var errorCodeExampleList = List.of(Map.of(errorCode, apiErrorResponseAnnotation.description())); + errorResponseExampleCustomizer.generateErrorResponseExample(operation, errorCodeExampleList); + errorRequestHeaderCustomizer.generateErrorRequestHeader(operation, errorCode); + }); + } - private void generateErrorResponseExample(Operation operation, ErrorCode type, String description) { - ApiResponses responses = operation.getResponses(); - - ErrorCode[] errorCodes = {type}; - - Map>> httpErrorCodeToErrorResponseExample = - Arrays.stream(errorCodes) - .map( - errorCode -> { - ErrorResponseDto errorResponseDto = new ErrorResponseDto(errorCode); - return Map.of(errorResponseDto, description); - } - ) - .collect(groupingBy(errorResponseDtoStringMap -> errorResponseDtoStringMap.keySet().iterator().next().getStatus())); + private void handleApiErrorResponses(Operation operation, ApiErrorResponses apiErrorResponsesAnnotation) { + List> errorCodeExampleList = Arrays.stream(apiErrorResponsesAnnotation.value()) + .map(apiErrorResponse -> ErrorCodeMapper.findByErrorCode(apiErrorResponse.errorCode()) + .map(code -> Map.of(code, apiErrorResponse.description())) + .orElse(emptyMap())) + .filter(map -> !map.isEmpty()) + .toList(); - addExamplesToResponses(responses, httpErrorCodeToErrorResponseExample); - } + List resultList = errorCodeExampleList.stream().flatMap(map -> map.keySet().stream()).toList(); - private void addExamplesToResponses( - ApiResponses responses, - Map>> httpErrorCodeToErrorResponseExample - ) { - httpErrorCodeToErrorResponseExample.forEach( - (status, v) -> { - Content content = new Content(); - MediaType mediaType = new MediaType(); - ApiResponse apiResponse = new ApiResponse(); - v.forEach( - map -> map.forEach( - (errorResponse, description) -> { - mediaType.addExamples( - errorResponse.getCode(), - generateErrorResponseExample(errorResponse, description) - ); - } - )); - content.addMediaType("application/json", mediaType); - apiResponse.setContent(content); - responses.addApiResponse(status.toString(), apiResponse); - }); + errorRequestHeaderCustomizer.generateErrorRequestHeader(operation, resultList); + errorResponseExampleCustomizer.generateErrorResponseExample(operation, errorCodeExampleList); } - private Example generateErrorResponseExample(ErrorResponseDto errorResponse, String description) { - Example example = new Example(); - example.setValue(errorResponse); - example.setDescription(description); - return example; - } } diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorRequestHeaderCustomizer.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorRequestHeaderCustomizer.java new file mode 100644 index 00000000..240ea7a3 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorRequestHeaderCustomizer.java @@ -0,0 +1,61 @@ +package com.depromeet.external.swagger.config; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static java.util.Collections.singletonList; + +@Component +public class ErrorRequestHeaderCustomizer { + + public void generateErrorRequestHeader(Operation operation, List errorCodeList) { + var defaultParameters = getDefaultParameter(operation); + var newErrorCodeParameters = generateErrorRequestHeader(errorCodeList); + defaultParameters.add(newErrorCodeParameters); + if (defaultParameters.size() == 1) { + operation.setParameters(defaultParameters); + } + } + + public void generateErrorRequestHeader(Operation operation, ErrorCode errorCode) { + var defaultParameters = getDefaultParameter(operation); + var newErrorCodeParameters = generateErrorRequestHeader(singletonList(errorCode)); + defaultParameters.add(newErrorCodeParameters); + if (defaultParameters.size() == 1) { + operation.setParameters(defaultParameters); + } + } + + private List getDefaultParameter(Operation operation) { + List parameters = operation.getParameters(); + if (parameters == null) { + parameters = new ArrayList<>(); + } + return parameters; + } + + private Parameter generateErrorRequestHeader(List errorCode) { + var parameter = new Parameter(); + parameter.setName("STREET-DROP-ERROR-TEST-CODE"); + parameter.setIn("header"); + parameter.setDescription("에러를 확인하기 위한 코드입니다. 테스트 서버 환경에서 해당 에러코드를 STREET-DROP-ERROR-TEST-CODE 헤더에 추가하여 테스트할 수 있습니다."); + parameter.setRequired(false); + parameter.setDeprecated(false); + parameter.setSchema(generateErrorRequestHeaderSchema(errorCode)); + return parameter; + } + + private Schema generateErrorRequestHeaderSchema(List errorCode) { + var schema = new Schema<>(); + schema.setEnum(Arrays.asList(errorCode.stream().map(ErrorCode::getErrorResponseCode).toArray())); + schema.setType("string"); + return schema; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorResponseExampleCustomizer.java b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorResponseExampleCustomizer.java new file mode 100644 index 00000000..4f20a3c3 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/external/swagger/config/ErrorResponseExampleCustomizer.java @@ -0,0 +1,63 @@ +package com.depromeet.external.swagger.config; + +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.http.dto.HttpErrorResponseDto; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.examples.Example; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.groupingBy; + +@Component +public class ErrorResponseExampleCustomizer { + + public void generateErrorResponseExample(Operation operation, List> errorCodeExampleList) { + ApiResponses responses = operation.getResponses(); + + Map>> errorCodeExampleByStatus = errorCodeExampleList.stream() + .collect(groupingBy(errorCodeStringMap -> errorCodeStringMap.keySet().iterator().next().getStatus().value())); + + errorCodeExampleByStatus + .forEach( + (statusCode, value) -> { + MediaType mediaType = new MediaType(); + value.forEach(map -> + map.forEach( + (errorResponse, description) -> { + Example example = generateExample(errorResponse, description); + mediaType.addExamples(errorResponse.getErrorResponseCode(), example); + } + )); + Content content = generateContent(mediaType); + ApiResponse apiResponse = generateApiResponse(content); + responses.addApiResponse(statusCode.toString(), apiResponse); + }); + } + + private ApiResponse generateApiResponse(Content content) { + ApiResponse apiResponse = new ApiResponse(); + apiResponse.setContent(content); + return apiResponse; + } + + private Content generateContent(MediaType mediaType) { + Content content = new Content(); + content.addMediaType("application/json", mediaType); + return content; + } + + private Example generateExample(ErrorCode errorCode, String description) { + Example example = new Example(); + HttpErrorResponseDto httpErrorResponseDto = HttpErrorResponseDto.from(errorCode); + example.setValue(httpErrorResponseDto); + example.setDescription(description); + return example; + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/security/config/WebSecurityConfig.java b/backend/streetdrop-api/src/main/java/com/depromeet/security/config/WebSecurityConfig.java index 543e7ec4..336d3952 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/security/config/WebSecurityConfig.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/security/config/WebSecurityConfig.java @@ -2,6 +2,8 @@ import com.depromeet.domains.user.service.UserService; import com.depromeet.security.filter.IdfvAuthenticationFilter; +import com.depromeet.security.handler.CustomAccessDeniedHandler; +import com.depromeet.security.handler.CustomAuthenticationEntryPoint; import com.depromeet.security.provider.IdfvUserDetailsService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -20,7 +22,8 @@ public class WebSecurityConfig { private final UserService userService; - + private final CustomAccessDeniedHandler customAccessDeniedHandler; + private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http @@ -31,7 +34,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/users/**").authenticated() .requestMatchers("/items/**").authenticated() .requestMatchers(HttpMethod.POST, "notifications/tokens").authenticated() - .anyRequest().permitAll().and() + .anyRequest().permitAll() + .and().exceptionHandling() + .authenticationEntryPoint(customAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler) + .and() .anonymous().and() .formLogin().disable() .apply(new IdfvFilter()); diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAccessDeniedHandler.java b/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAccessDeniedHandler.java new file mode 100644 index 00000000..cedd380b --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAccessDeniedHandler.java @@ -0,0 +1,32 @@ +package com.depromeet.security.handler; + +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.http.dto.HttpErrorResponseDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.MDC; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.UUID; + +@Component +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + if (MDC.get("traceId") == null) { + MDC.put("traceId", UUID.randomUUID().toString()); + } + response.setContentType("application/json"); + response.setStatus(HttpStatus.FORBIDDEN.value()); + ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + response.getWriter().write(mapper.writeValueAsString(HttpErrorResponseDto.from(CommonErrorCode.FORBIDDEN.toErrorCode()))); + } +} \ No newline at end of file diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAuthenticationEntryPoint.java b/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAuthenticationEntryPoint.java new file mode 100644 index 00000000..6ba6e623 --- /dev/null +++ b/backend/streetdrop-api/src/main/java/com/depromeet/security/handler/CustomAuthenticationEntryPoint.java @@ -0,0 +1,32 @@ +package com.depromeet.security.handler; + +import com.depromeet.common.error.dto.CommonErrorCode; +import com.depromeet.common.error.http.dto.HttpErrorResponseDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.MDC; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.UUID; + +@Component +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + if (MDC.get("traceId") == null) { + MDC.put("traceId", UUID.randomUUID().toString()); + } + response.setContentType("application/json"); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + response.getWriter().write(mapper.writeValueAsString(HttpErrorResponseDto.from(CommonErrorCode.UNAUTHORIZED.toErrorCode()))); + } +} diff --git a/backend/streetdrop-api/src/main/java/com/depromeet/test/error/filter/ErrorTestHeaderFilter.java b/backend/streetdrop-api/src/main/java/com/depromeet/test/error/filter/ErrorTestHeaderFilter.java index 9c1d0de7..d438b71c 100644 --- a/backend/streetdrop-api/src/main/java/com/depromeet/test/error/filter/ErrorTestHeaderFilter.java +++ b/backend/streetdrop-api/src/main/java/com/depromeet/test/error/filter/ErrorTestHeaderFilter.java @@ -1,8 +1,9 @@ package com.depromeet.test.error.filter; -import com.depromeet.common.error.dto.ErrorCode; import com.depromeet.common.error.dto.ErrorCodeMapper; -import com.depromeet.common.error.dto.ErrorResponseDto; +import com.depromeet.common.error.dto.interfaces.ErrorCode; +import com.depromeet.common.error.dto.interfaces.ErrorCodeInterface; +import com.depromeet.common.error.http.dto.HttpErrorResponseDto; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -44,12 +45,12 @@ protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServlet filterChain.doFilter(request, response); } - private void throwErrorResponse(HttpServletResponse response, ErrorCode errorCode) throws IOException { + private void throwErrorResponse(HttpServletResponse response, T errorCode) throws IOException { response.setContentType(APPLICATION_JSON_VALUE); response.setStatus(errorCode.getStatus().value()); - ErrorResponseDto errorResponseDto = new ErrorResponseDto(errorCode); - String errorResponseJson = objectMapper.writeValueAsString(errorResponseDto); + HttpErrorResponseDto httpErrorResponseDto = HttpErrorResponseDto.from(errorCode); + String errorResponseJson = objectMapper.writeValueAsString(httpErrorResponseDto); response.getWriter().write(errorResponseJson); } diff --git a/backend/streetdrop-api/src/test/java/unit/common/error/StreetDropErrorCodeTest.java b/backend/streetdrop-api/src/test/java/unit/common/error/StreetDropErrorCodeTest.java new file mode 100644 index 00000000..a0a3c351 --- /dev/null +++ b/backend/streetdrop-api/src/test/java/unit/common/error/StreetDropErrorCodeTest.java @@ -0,0 +1,34 @@ +package unit.common.error; + +import com.depromeet.common.error.dto.StreetDropErrorCode; +import com.depromeet.common.error.dto.StreetDropErrorCodeList; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; + + +@DisplayName("에러 코드 테스트") +public class StreetDropErrorCodeTest { + @Test + @DisplayName("다른 도메인의 에러 코드가 중복되지 않는지 테스트") + public void testNoDuplicateErrorResponseCodes() { + + StreetDropErrorCodeList streetDropErrorCodeList = StreetDropErrorCodeList.getInstance(); + List errorCodes = streetDropErrorCodeList.getStreetDropErrorCodeList(); + + List errorResponseCodes = errorCodes.stream() + .map(StreetDropErrorCode::getErrorResponseCode) + .toList(); + + var duplicatedList = errorResponseCodes.stream() + .filter(i -> errorResponseCodes.stream().filter(i::equals).count() > 1) + .distinct() + .toList(); + + assertTrue(duplicatedList.isEmpty(), "There are duplicated error response codes: " + duplicatedList); + } + +} diff --git a/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemClaimServiceTest.java b/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemClaimServiceTest.java index 38009a25..f34340d8 100644 --- a/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemClaimServiceTest.java +++ b/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemClaimServiceTest.java @@ -1,10 +1,10 @@ package unit.domains.item.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.item.dto.request.ItemClaimRequestDto; +import com.depromeet.domains.item.error.ItemErrorCode; import com.depromeet.domains.item.repository.ItemClaimRepository; import com.depromeet.domains.item.repository.ItemRepository; import com.depromeet.domains.item.service.ItemClaimService; @@ -136,7 +136,7 @@ void claimItemFail_AlreadyReportItem() { assertThat(thrown) .isInstanceOf(BusinessException.class) - .hasMessageContaining(ErrorCode.ALREADY_ITEM_REPORTED_ERROR.getMessage()); + .hasMessageContaining(ItemErrorCode.ITEM_ALREADY_REPORTED.getMessage()); } } diff --git a/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemLikeServiceTest.java b/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemLikeServiceTest.java index 7b73f39c..f61dafee 100644 --- a/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemLikeServiceTest.java +++ b/backend/streetdrop-api/src/test/java/unit/domains/item/service/ItemLikeServiceTest.java @@ -1,14 +1,14 @@ package unit.domains.item.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; -import com.depromeet.item.Item; -import com.depromeet.item.ItemLike; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.item.dto.response.ItemLikeResponseDto; +import com.depromeet.domains.item.error.ItemErrorCode; import com.depromeet.domains.item.repository.ItemLikeRepository; import com.depromeet.domains.item.service.ItemLikeService; import com.depromeet.domains.item.service.ItemService; +import com.depromeet.item.Item; +import com.depromeet.item.ItemLike; import com.depromeet.user.User; import com.depromeet.user.vo.MusicApp; import org.junit.jupiter.api.DisplayName; @@ -21,9 +21,11 @@ import java.util.Optional; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.verify; @ExtendWith(MockitoExtension.class) @DisplayName("[Service] ItemLikeService 테스트") @@ -106,7 +108,7 @@ void likeItemAlreadyLiked() { // Then assertThat(thrown).isInstanceOf(BusinessException.class) - .hasMessageContaining(ErrorCode.ALREADY_ITEM_LIKED_ERROR.getMessage()); + .hasMessageContaining(ItemErrorCode.ITEM_ALREADY_LIKED.getMessage()); } @Test diff --git a/backend/streetdrop-api/src/test/java/unit/domains/user/service/UserBlockServiceTest.java b/backend/streetdrop-api/src/test/java/unit/domains/user/service/UserBlockServiceTest.java index 1ed7c7c2..2b797124 100644 --- a/backend/streetdrop-api/src/test/java/unit/domains/user/service/UserBlockServiceTest.java +++ b/backend/streetdrop-api/src/test/java/unit/domains/user/service/UserBlockServiceTest.java @@ -1,9 +1,9 @@ package unit.domains.user.service; -import com.depromeet.common.error.dto.ErrorCode; -import com.depromeet.common.error.exception.common.BusinessException; -import com.depromeet.common.error.exception.common.NotFoundException; +import com.depromeet.common.error.exception.internal.BusinessException; +import com.depromeet.common.error.exception.internal.NotFoundException; import com.depromeet.domains.user.dto.response.BlockUserResponseDto; +import com.depromeet.domains.user.error.UserErrorCode; import com.depromeet.domains.user.repository.BlockUserRepository; import com.depromeet.domains.user.repository.UserRepository; import com.depromeet.domains.user.service.UserBlockService; @@ -146,7 +146,7 @@ void userBlockTestFail_Can_Not_Block_Self() { Throwable thrown = catchThrowable(() -> userBlockService.blockUser(requestUser, requestUserId)); Assertions.assertThat(thrown).isInstanceOf(BusinessException.class) - .hasMessageContaining(ErrorCode.CAN_NOT_BLOCK_SELF.getMessage()); + .hasMessageContaining(UserErrorCode.USER_CAN_NOT_BLOCK_SELF.getMessage()); } } diff --git a/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageAreaServiceTest.java b/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageAreaServiceTest.java index d1eefcb7..5380ef9f 100644 --- a/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageAreaServiceTest.java +++ b/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageAreaServiceTest.java @@ -1,7 +1,7 @@ package unit.domains.village.service; import com.depromeet.area.village.VillageArea; -import com.depromeet.common.error.dto.ErrorCode; +import com.depromeet.domains.geo.error.GeoErrorCode; import com.depromeet.domains.village.repository.VillageAreaRepository; import com.depromeet.domains.village.service.VillageAreaService; import com.depromeet.util.GeomUtil; @@ -94,7 +94,7 @@ void VillageItemCountTest_NotSupportLocation() { Throwable thrown = catchThrowable(() -> villageAreaService.getVillageByLocationPoint(invalidPoint)); assertThat(thrown).isInstanceOf(RuntimeException.class) - .hasMessageContaining(ErrorCode.NOT_SUPPORT_LOCATION.getMessage()); + .hasMessageContaining(GeoErrorCode.NOT_SUPPORT_LOCATION.getMessage()); } } } diff --git a/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageItemServiceTest.java b/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageItemServiceTest.java index 94671d6f..904301a4 100644 --- a/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageItemServiceTest.java +++ b/backend/streetdrop-api/src/test/java/unit/domains/village/service/VillageItemServiceTest.java @@ -1,7 +1,7 @@ package unit.domains.village.service; import com.depromeet.area.village.VillageArea; -import com.depromeet.common.error.dto.ErrorCode; +import com.depromeet.domains.geo.error.GeoErrorCode; import com.depromeet.domains.village.dto.request.VillageItemsRequestDto; import com.depromeet.domains.village.dto.response.VillageItemsCountResponseDto; import com.depromeet.domains.village.repository.VillageAreaRepository; @@ -111,7 +111,7 @@ void VillageItemCountTest() { Throwable thrown = catchThrowable(() -> villageItemService.countItemsInVillageByLocation(villageItemsRequestDto)); assertThat(thrown).isInstanceOf(RuntimeException.class) - .hasMessageContaining(ErrorCode.NOT_SUPPORT_LOCATION.getMessage()); + .hasMessageContaining(GeoErrorCode.NOT_SUPPORT_LOCATION.getMessage()); } }