diff --git a/server/src/main/java/server/haengdong/ServerApplication.java b/server/src/main/java/server/haengdong/HaengdongApplication.java similarity index 69% rename from server/src/main/java/server/haengdong/ServerApplication.java rename to server/src/main/java/server/haengdong/HaengdongApplication.java index 784978356..31b6e46e7 100644 --- a/server/src/main/java/server/haengdong/ServerApplication.java +++ b/server/src/main/java/server/haengdong/HaengdongApplication.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ServerApplication { +public class HaengdongApplication { public static void main(String[] args) { - SpringApplication.run(ServerApplication.class, args); + SpringApplication.run(HaengdongApplication.class, args); } } diff --git a/server/src/main/java/server/haengdong/application/BillActionService.java b/server/src/main/java/server/haengdong/application/BillActionService.java index 272eee7e8..257d33a3e 100644 --- a/server/src/main/java/server/haengdong/application/BillActionService.java +++ b/server/src/main/java/server/haengdong/application/BillActionService.java @@ -11,6 +11,8 @@ import server.haengdong.domain.action.ActionRepository; import server.haengdong.domain.action.BillActionRepository; import server.haengdong.domain.event.EventRepository; +import server.haengdong.exception.HaengdongErrorCode; +import server.haengdong.exception.HaengdongException; @RequiredArgsConstructor @Transactional(readOnly = true) @@ -24,7 +26,7 @@ public class BillActionService { @Transactional public void saveAllBillAction(String eventToken, List requests) { Event event = eventRepository.findByToken(eventToken) - .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이벤트 토큰입니다.")); + .orElseThrow(() -> new HaengdongException(HaengdongErrorCode.NOT_FOUND_EVENT)); Action action = createStartAction(event); for (BillActionAppRequest request : requests) { diff --git a/server/src/main/java/server/haengdong/application/EventService.java b/server/src/main/java/server/haengdong/application/EventService.java index ea4384148..dee023fbc 100644 --- a/server/src/main/java/server/haengdong/application/EventService.java +++ b/server/src/main/java/server/haengdong/application/EventService.java @@ -8,6 +8,8 @@ import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.event.EventTokenProvider; +import server.haengdong.exception.HaengdongErrorCode; +import server.haengdong.exception.HaengdongException; @RequiredArgsConstructor @Service @@ -25,7 +27,8 @@ public EventAppResponse saveEvent(EventAppRequest request) { } public EventDetailAppResponse findEvent(String token) { - Event event = eventRepository.findByToken(token).orElseThrow(() -> new IllegalArgumentException("")); + Event event = eventRepository.findByToken(token) + .orElseThrow(() -> new HaengdongException(HaengdongErrorCode.NOT_FOUND_EVENT)); return EventDetailAppResponse.of(event); } diff --git a/server/src/main/java/server/haengdong/application/MemberActionFactory.java b/server/src/main/java/server/haengdong/application/MemberActionFactory.java index 46fd9ed1a..cbe34f0ef 100644 --- a/server/src/main/java/server/haengdong/application/MemberActionFactory.java +++ b/server/src/main/java/server/haengdong/application/MemberActionFactory.java @@ -11,6 +11,8 @@ import server.haengdong.domain.action.MemberAction; import server.haengdong.domain.action.MemberActionStatus; import server.haengdong.domain.action.MemberGroupIdProvider; +import server.haengdong.exception.HaengdongErrorCode; +import server.haengdong.exception.HaengdongException; @RequiredArgsConstructor @Component @@ -45,7 +47,7 @@ private void validateMemberNames(MemberActionsSaveAppRequest request) { long uniqueCount = memberNames.stream().distinct().count(); if (uniqueCount != memberNames.size()) { - throw new IllegalArgumentException(); + throw new HaengdongException(HaengdongErrorCode.DUPLICATED_MEMBER_ACTION); } } @@ -62,7 +64,7 @@ private void validateActions(MemberActionsSaveAppRequest request, List memberActions) { MemberActionStatus memberActionStatus = MemberActionStatus.of(request.status()); if (isInvalidStatus(memberActions, request.name(), memberActionStatus)) { - throw new IllegalArgumentException(); + throw new HaengdongException(HaengdongErrorCode.INVALID_MEMBER_ACTION); } } diff --git a/server/src/main/java/server/haengdong/exception/ErrorResponse.java b/server/src/main/java/server/haengdong/exception/ErrorResponse.java new file mode 100644 index 000000000..5d1e33c9b --- /dev/null +++ b/server/src/main/java/server/haengdong/exception/ErrorResponse.java @@ -0,0 +1,10 @@ +package server.haengdong.exception; + +public record ErrorResponse( + String message +) { + + public static ErrorResponse of(HaengdongErrorCode errorCode) { + return new ErrorResponse(errorCode.getMessage()); + } +} diff --git a/server/src/main/java/server/haengdong/exception/GlobalExceptionHandler.java b/server/src/main/java/server/haengdong/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..7985721cd --- /dev/null +++ b/server/src/main/java/server/haengdong/exception/GlobalExceptionHandler.java @@ -0,0 +1,43 @@ +package server.haengdong.exception; + +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseEntity haengdongException() { + return ResponseEntity.badRequest() + .body(ErrorResponse.of(HaengdongErrorCode.BAD_REQUEST)); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + String errorMessage = e.getFieldErrors().stream() + .map(error -> error.getField() + " " + error.getDefaultMessage()) + .collect(Collectors.joining(", ")); + + return ResponseEntity.badRequest() + .body(new ErrorResponse(errorMessage)); + } + + @ExceptionHandler(HaengdongException.class) + public ResponseEntity haengdongException(HaengdongException e) { + return ResponseEntity.status(e.getStatusCode()) + .body(ErrorResponse.of(e.getErrorCode())); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + log.error(e.getMessage(), e); + return ResponseEntity.internalServerError() + .body(ErrorResponse.of(HaengdongErrorCode.INTERNAL_SERVER_ERROR)); + } +} diff --git a/server/src/main/java/server/haengdong/exception/HaengdongErrorCode.java b/server/src/main/java/server/haengdong/exception/HaengdongErrorCode.java new file mode 100644 index 000000000..792baa838 --- /dev/null +++ b/server/src/main/java/server/haengdong/exception/HaengdongErrorCode.java @@ -0,0 +1,24 @@ +package server.haengdong.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public enum HaengdongErrorCode { + BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + DUPLICATED_MEMBER_ACTION(HttpStatus.BAD_REQUEST, "올바르지 않은 인원 요청입니다."), + INVALID_MEMBER_ACTION(HttpStatus.BAD_REQUEST, "잘못된 맴버 액션입니다."), + + NOT_FOUND_EVENT(HttpStatus.NOT_FOUND, "존재하지 않는 행사입니다."), + + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 에러가 발생했습니다."), + ; + + private final HttpStatus httpStatus; + private final String message; + + HaengdongErrorCode(HttpStatus httpStatus, String message) { + this.httpStatus = httpStatus; + this.message = message; + } +} diff --git a/server/src/main/java/server/haengdong/exception/HaengdongException.java b/server/src/main/java/server/haengdong/exception/HaengdongException.java new file mode 100644 index 000000000..812df50f8 --- /dev/null +++ b/server/src/main/java/server/haengdong/exception/HaengdongException.java @@ -0,0 +1,32 @@ +package server.haengdong.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatusCode; + +@Getter +public class HaengdongException extends RuntimeException { + + private final HaengdongErrorCode errorCode; + private final String message; + + public HaengdongException(HaengdongErrorCode errorCode) { + this(errorCode, null); + } + + public HaengdongException(HaengdongErrorCode errorCode, String message) { + this.errorCode = errorCode; + this.message = message; + } + + public HttpStatusCode getStatusCode() { + return errorCode.getHttpStatus(); + } + + @Override + public String getMessage() { + if (message == null) { + return errorCode.getMessage(); + } + return message; + } +} diff --git a/server/src/test/java/server/haengdong/application/BillActionServiceTest.java b/server/src/test/java/server/haengdong/application/BillActionServiceTest.java index 178e850e3..195d4bdd9 100644 --- a/server/src/test/java/server/haengdong/application/BillActionServiceTest.java +++ b/server/src/test/java/server/haengdong/application/BillActionServiceTest.java @@ -14,6 +14,7 @@ import server.haengdong.domain.event.Event; import server.haengdong.domain.action.BillActionRepository; import server.haengdong.domain.event.EventRepository; +import server.haengdong.exception.HaengdongException; @SpringBootTest class BillActionServiceTest { @@ -59,7 +60,6 @@ void saveAllBillAction1() { ); assertThatThrownBy(() -> billActionService.saveAllBillAction("token", requests)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("존재하지 않는 이벤트 토큰입니다."); + .isInstanceOf(HaengdongException.class); } } diff --git a/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java b/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java index b8251a307..40bee2ff6 100644 --- a/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java +++ b/server/src/test/java/server/haengdong/application/MemberActionFactoryTest.java @@ -20,6 +20,7 @@ import server.haengdong.domain.action.ActionRepository; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.action.MemberActionRepository; +import server.haengdong.exception.HaengdongException; @SpringBootTest class MemberActionFactoryTest { @@ -59,7 +60,7 @@ void createMemberActionsTest() { Action startAction = new Action(event, 3L); assertThatThrownBy(() -> memberActionFactory.createMemberActions(request, unorderedMemberActions, startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } @DisplayName("인원 변동 액션을 생성한다.") @@ -147,7 +148,7 @@ void createMemberActionTest5() { Action startAction = new Action(event, 2L); assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(), startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } @DisplayName("행사에 이미 참여 중인 경우 다시 입장할 수 없다.") @@ -163,7 +164,7 @@ void createMemberActionTest6() { Action startAction = new Action(event, 2L); assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } @DisplayName("한 명의 사용자는 동시에 여러 번 입장할 수 없다.") @@ -177,7 +178,7 @@ void createMemberActionTest7() { Action startAction = new Action(event, 1L); assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(), startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } @DisplayName("한 명의 사용자는 동시에 여러 번 퇴장할 수 없다.") @@ -194,7 +195,7 @@ void createMemberActionTest8() { Action startAction = new Action(event, 2L); assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } @DisplayName("한 명의 사용자는 입장과 퇴장을 동시에 할 수 없다.") @@ -211,6 +212,6 @@ void createMemberActionTest9() { Action startAction = new Action(event, 2L); assertThatCode(() -> memberActionFactory.createMemberActions(request, List.of(memberAction), startAction)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(HaengdongException.class); } }