From 36ae2b33c0447ab4c534f02577e36ab00b9db2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EB=94=94?= <38103085+EunjiShin@users.noreply.github.com> Date: Fri, 5 Jul 2024 22:51:36 +0900 Subject: [PATCH] =?UTF-8?q?[BSVR-63]=20custom=20exception=20&=20global=20e?= =?UTF-8?q?xception=20handler=20=EC=B6=94=EA=B0=80=20(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: common 모듈 추가 및 errorCode enum 생성 * feat: spot application global exception handler 추가 * feat: custom exception 예제 코드 추가 * feat: 예제 수정 * feat: gitignore에 docker db 추가 --- .gitignore | 3 +- application/build.gradle.kts | 1 + .../config/SpotApplicationConfig.java | 2 +- .../common/exception/ErrorResponse.java | 10 +++++ .../exception/SpotAppExceptionHandler.java | 27 ++++++++++++ common/.gitignore | 42 +++++++++++++++++++ common/build.gradle.kts | 6 +++ .../common/exception/BusinessException.java | 18 ++++++++ .../spot/common/exception/ErrorCode.java | 12 ++++++ .../exception/member/MemberErrorCode.java | 27 ++++++++++++ .../exception/member/MemberException.java | 20 +++++++++ settings.gradle.kts | 1 + usecase/build.gradle.kts | 1 + .../spot/usecase/service/MemberService.java | 10 +++-- 14 files changed, 175 insertions(+), 5 deletions(-) rename application/src/main/java/org/depromeet/spot/application/{ => common}/config/SpotApplicationConfig.java (89%) create mode 100644 application/src/main/java/org/depromeet/spot/application/common/exception/ErrorResponse.java create mode 100644 application/src/main/java/org/depromeet/spot/application/common/exception/SpotAppExceptionHandler.java create mode 100644 common/.gitignore create mode 100644 common/build.gradle.kts create mode 100644 common/src/main/java/org/depromeet/spot/common/exception/BusinessException.java create mode 100644 common/src/main/java/org/depromeet/spot/common/exception/ErrorCode.java create mode 100644 common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java create mode 100644 common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java diff --git a/.gitignore b/.gitignore index cf09c91f..2e735616 100644 --- a/.gitignore +++ b/.gitignore @@ -373,4 +373,5 @@ gradle-app.setting # Java heap dump *.hprof -# End of https://www.toptal.com/developers/gitignore/api/macos,windows,intellij,intellij+iml,intellij+all,visualstudiocode,java,gradle,kotlin \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/macos,windows,intellij,intellij+iml,intellij+all,visualstudiocode,java,gradle,kotlin +/db/ diff --git a/application/build.gradle.kts b/application/build.gradle.kts index b48dbec5..165b43db 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -1,4 +1,5 @@ dependencies { + implementation(project(":common")) implementation(project(":domain")) implementation(project(":usecase")) implementation(project(":infrastructure:jpa")) diff --git a/application/src/main/java/org/depromeet/spot/application/config/SpotApplicationConfig.java b/application/src/main/java/org/depromeet/spot/application/common/config/SpotApplicationConfig.java similarity index 89% rename from application/src/main/java/org/depromeet/spot/application/config/SpotApplicationConfig.java rename to application/src/main/java/org/depromeet/spot/application/common/config/SpotApplicationConfig.java index e99fe8c0..fbeb93db 100644 --- a/application/src/main/java/org/depromeet/spot/application/config/SpotApplicationConfig.java +++ b/application/src/main/java/org/depromeet/spot/application/common/config/SpotApplicationConfig.java @@ -1,4 +1,4 @@ -package org.depromeet.spot.application.config; +package org.depromeet.spot.application.common.config; import org.depromeet.spot.jpa.config.JpaConfig; import org.depromeet.spot.usecase.config.UsecaseConfig; diff --git a/application/src/main/java/org/depromeet/spot/application/common/exception/ErrorResponse.java b/application/src/main/java/org/depromeet/spot/application/common/exception/ErrorResponse.java new file mode 100644 index 00000000..00fb235d --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/exception/ErrorResponse.java @@ -0,0 +1,10 @@ +package org.depromeet.spot.application.common.exception; + +import org.depromeet.spot.common.exception.BusinessException; + +public record ErrorResponse(String code, String message) { + + public static ErrorResponse from(BusinessException e) { + return new ErrorResponse(e.getCode(), e.getMessage()); + } +} diff --git a/application/src/main/java/org/depromeet/spot/application/common/exception/SpotAppExceptionHandler.java b/application/src/main/java/org/depromeet/spot/application/common/exception/SpotAppExceptionHandler.java new file mode 100644 index 00000000..58efd9ac --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/exception/SpotAppExceptionHandler.java @@ -0,0 +1,27 @@ +package org.depromeet.spot.application.common.exception; + +import org.depromeet.spot.common.exception.BusinessException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestControllerAdvice +public class SpotAppExceptionHandler { + + private static final String EXCEPTION_LOG_TEMPLATE = "code = {}, message = {}"; + + @ExceptionHandler(BusinessException.class) + protected ResponseEntity handleBusinessException(BusinessException e) { + var code = e.getCode(); + var message = e.getMessage(); + var httpStatus = e.getHttpStatus(); + + log.error(EXCEPTION_LOG_TEMPLATE, code, message); + var response = ErrorResponse.from(e); + + return ResponseEntity.status(httpStatus).body(response); + } +} diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 00000000..b63da455 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 00000000..54f77f44 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,6 @@ +dependencies { + implementation("org.springframework.boot:spring-boot-starter-web") +} + +tasks.jar { enabled = true } +tasks.bootJar { enabled = false } \ No newline at end of file diff --git a/common/src/main/java/org/depromeet/spot/common/exception/BusinessException.java b/common/src/main/java/org/depromeet/spot/common/exception/BusinessException.java new file mode 100644 index 00000000..121936b7 --- /dev/null +++ b/common/src/main/java/org/depromeet/spot/common/exception/BusinessException.java @@ -0,0 +1,18 @@ +package org.depromeet.spot.common.exception; + +import org.springframework.http.HttpStatus; + +import lombok.Getter; + +@Getter +public class BusinessException extends RuntimeException { + + private final HttpStatus httpStatus; + private final String code; + + public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.httpStatus = errorCode.getStatus(); + this.code = errorCode.getCode(); + } +} diff --git a/common/src/main/java/org/depromeet/spot/common/exception/ErrorCode.java b/common/src/main/java/org/depromeet/spot/common/exception/ErrorCode.java new file mode 100644 index 00000000..d9308182 --- /dev/null +++ b/common/src/main/java/org/depromeet/spot/common/exception/ErrorCode.java @@ -0,0 +1,12 @@ +package org.depromeet.spot.common.exception; + +import org.springframework.http.HttpStatus; + +public interface ErrorCode { + + HttpStatus getStatus(); + + String getCode(); + + String getMessage(); +} diff --git a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java new file mode 100644 index 00000000..be953a57 --- /dev/null +++ b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java @@ -0,0 +1,27 @@ +package org.depromeet.spot.common.exception.member; + +import org.depromeet.spot.common.exception.ErrorCode; +import org.springframework.http.HttpStatus; + +import lombok.Getter; + +@Getter +public enum MemberErrorCode implements ErrorCode { + MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "M001", "요청 유저가 존재하지 않습니다."), + ; + + private final HttpStatus status; + private final String code; + private String message; + + MemberErrorCode(HttpStatus status, String code, String message) { + this.status = status; + this.code = code; + this.message = message; + } + + public MemberErrorCode appended(Object o) { + message = message + " {" + o.toString() + "}"; + return this; + } +} diff --git a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java new file mode 100644 index 00000000..adf73795 --- /dev/null +++ b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java @@ -0,0 +1,20 @@ +package org.depromeet.spot.common.exception.member; + +import org.depromeet.spot.common.exception.BusinessException; + +public abstract class MemberException extends BusinessException { + + protected MemberException(MemberErrorCode errorCode) { + super(errorCode); + } + + public static class MemberNotFoundException extends MemberException { + public MemberNotFoundException() { + super(MemberErrorCode.MEMBER_NOT_FOUND); + } + + public MemberNotFoundException(Object o) { + super(MemberErrorCode.MEMBER_NOT_FOUND.appended(o)); + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 9fc46dae..974ab8ca 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,3 +11,4 @@ include("infrastructure") include("infrastructure:jpa") findProject(":infrastructure:jpa")?.name = "jpa" include("usecase") +include("common") diff --git a/usecase/build.gradle.kts b/usecase/build.gradle.kts index 506b4ad6..7f88f73d 100644 --- a/usecase/build.gradle.kts +++ b/usecase/build.gradle.kts @@ -1,4 +1,5 @@ dependencies { + implementation(project(":common")) implementation(project(":domain")) // spring diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java index 3e04c948..cba1b8d6 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java @@ -2,13 +2,13 @@ import java.util.List; +import org.depromeet.spot.common.exception.member.MemberException.MemberNotFoundException; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.usecase.port.in.MemberUsecase; import org.depromeet.spot.usecase.port.out.MemberRepository; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; -import lombok.val; @Service @RequiredArgsConstructor @@ -18,12 +18,16 @@ public class MemberService implements MemberUsecase { @Override public Member create(final String name) { - val member = new Member(null, name); + var member = new Member(null, name); return memberRepository.save(member); } @Override public List findByName(final String name) { - return memberRepository.findByName(name); + var members = memberRepository.findByName(name); + if (members.isEmpty()) { + throw new MemberNotFoundException("name : " + name); + } + return members; } }