diff --git a/src/main/java/leets/weeth/domain/account/application/exception/AccountErrorCode.java b/src/main/java/leets/weeth/domain/account/application/exception/AccountErrorCode.java new file mode 100644 index 000000000..eb5bc2696 --- /dev/null +++ b/src/main/java/leets/weeth/domain/account/application/exception/AccountErrorCode.java @@ -0,0 +1,25 @@ +package leets.weeth.domain.account.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum AccountErrorCode implements ErrorCodeInterface { + + @ExplainError("요청한 회비 장부 ID가 존재하지 않을 때 발생합니다.") + ACCOUNT_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 장부입니다."), + + @ExplainError("이미 존재하는 장부를 중복 생성하려고 할 때 발생합니다.") + ACCOUNT_EXISTS(400, HttpStatus.BAD_REQUEST, "이미 생성된 장부입니다."), + + @ExplainError("요청한 영수증 내역이 존재하지 않을 때 발생합니다.") + RECEIPT_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 내역입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/account/application/exception/AccountExistsException.java b/src/main/java/leets/weeth/domain/account/application/exception/AccountExistsException.java index b6a509c30..e226f76e7 100644 --- a/src/main/java/leets/weeth/domain/account/application/exception/AccountExistsException.java +++ b/src/main/java/leets/weeth/domain/account/application/exception/AccountExistsException.java @@ -3,6 +3,8 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AccountExistsException extends BusinessLogicException { - public AccountExistsException() { super(400, "이미 생성된 장부입니다.");} + public AccountExistsException() { + super(AccountErrorCode.ACCOUNT_EXISTS); + } } diff --git a/src/main/java/leets/weeth/domain/account/application/exception/AccountNotFoundException.java b/src/main/java/leets/weeth/domain/account/application/exception/AccountNotFoundException.java index 5c04bca2d..2b11c5b50 100644 --- a/src/main/java/leets/weeth/domain/account/application/exception/AccountNotFoundException.java +++ b/src/main/java/leets/weeth/domain/account/application/exception/AccountNotFoundException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AccountNotFoundException extends BusinessLogicException { - public AccountNotFoundException() {super(404, "존재하지 않는 장부입니다.");} + public AccountNotFoundException() { + super(AccountErrorCode.ACCOUNT_NOT_FOUND); + } } diff --git a/src/main/java/leets/weeth/domain/account/application/exception/ReceiptNotFoundException.java b/src/main/java/leets/weeth/domain/account/application/exception/ReceiptNotFoundException.java index 054bda165..6dee5364a 100644 --- a/src/main/java/leets/weeth/domain/account/application/exception/ReceiptNotFoundException.java +++ b/src/main/java/leets/weeth/domain/account/application/exception/ReceiptNotFoundException.java @@ -3,6 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class ReceiptNotFoundException extends BusinessLogicException { - public ReceiptNotFoundException() { super(404, "존재하지 않는 내역입니다.");} + public ReceiptNotFoundException() { + super(AccountErrorCode.RECEIPT_NOT_FOUND); + } } - diff --git a/src/main/java/leets/weeth/domain/account/presentation/AccountAdminController.java b/src/main/java/leets/weeth/domain/account/presentation/AccountAdminController.java index 1dd79599e..355dad7ea 100644 --- a/src/main/java/leets/weeth/domain/account/presentation/AccountAdminController.java +++ b/src/main/java/leets/weeth/domain/account/presentation/AccountAdminController.java @@ -1,12 +1,12 @@ package leets.weeth.domain.account.presentation; -import static leets.weeth.domain.account.presentation.ResponseMessage.ACCOUNT_SAVE_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.account.application.dto.AccountDTO; +import leets.weeth.domain.account.application.exception.AccountErrorCode; import leets.weeth.domain.account.application.usecase.AccountUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -14,10 +14,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import static leets.weeth.domain.account.presentation.ResponseMessage.ACCOUNT_SAVE_SUCCESS; + @Tag(name = "ACCOUNT ADMIN", description = "[ADMIN] 회비 어드민 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/account") +@ApiErrorCodeExample(AccountErrorCode.class) public class AccountAdminController { private final AccountUseCase accountUseCase; diff --git a/src/main/java/leets/weeth/domain/account/presentation/AccountController.java b/src/main/java/leets/weeth/domain/account/presentation/AccountController.java index 0161624c1..6890ae403 100644 --- a/src/main/java/leets/weeth/domain/account/presentation/AccountController.java +++ b/src/main/java/leets/weeth/domain/account/presentation/AccountController.java @@ -1,21 +1,24 @@ package leets.weeth.domain.account.presentation; -import static leets.weeth.domain.account.presentation.ResponseMessage.ACCOUNT_FIND_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import leets.weeth.domain.account.application.dto.AccountDTO; +import leets.weeth.domain.account.application.exception.AccountErrorCode; import leets.weeth.domain.account.application.usecase.AccountUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + +import static leets.weeth.domain.account.presentation.ResponseMessage.ACCOUNT_FIND_SUCCESS; @Tag(name = "ACCOUNT", description = "회비 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/account") +@ApiErrorCodeExample(AccountErrorCode.class) public class AccountController { private final AccountUseCase accountUseCase; diff --git a/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java b/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java index 2559bb345..5855f3f5e 100644 --- a/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java +++ b/src/main/java/leets/weeth/domain/account/presentation/ReceiptAdminController.java @@ -4,7 +4,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.account.application.dto.ReceiptDTO; +import leets.weeth.domain.account.application.exception.AccountErrorCode; import leets.weeth.domain.account.application.usecase.ReceiptUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -14,6 +16,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/receipts") +@ApiErrorCodeExample(AccountErrorCode.class) public class ReceiptAdminController { private final ReceiptUseCase receiptUseCase; diff --git a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceCodeMismatchException.java b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceCodeMismatchException.java index d9387b007..681e4585e 100644 --- a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceCodeMismatchException.java +++ b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceCodeMismatchException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AttendanceCodeMismatchException extends BusinessLogicException { - public AttendanceCodeMismatchException() {super(400 ,"출석 코드가 일치하지 않습니다.");} -} \ No newline at end of file + public AttendanceCodeMismatchException() { + super(AttendanceErrorCode.ATTENDANCE_CODE_MISMATCH); + } +} diff --git a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceErrorCode.java b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceErrorCode.java new file mode 100644 index 000000000..9908d6687 --- /dev/null +++ b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceErrorCode.java @@ -0,0 +1,25 @@ +package leets.weeth.domain.attendance.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum AttendanceErrorCode implements ErrorCodeInterface { + + @ExplainError("출석 정보를 찾을 수 없을 때 발생합니다.") + ATTENDANCE_NOT_FOUND(404, HttpStatus.NOT_FOUND, "출석 정보가 존재하지 않습니다."), + + @ExplainError("입력한 출석 코드가 생성된 코드와 일치하지 않을 때 발생합니다.") + ATTENDANCE_CODE_MISMATCH(400, HttpStatus.BAD_REQUEST, "출석 코드가 일치하지 않습니다."), + + @ExplainError("사용자가 출석 일정을 직접 수정하려고 시도할 때 발생합니다. (출석 로직 위반)") + ATTENDANCE_EVENT_TYPE_NOT_MATCH(400, HttpStatus.BAD_REQUEST, "출석일정은 직접 수정할 수 없습니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceEventTypeNotMatchException.java b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceEventTypeNotMatchException.java index f282c64e4..845c63aeb 100644 --- a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceEventTypeNotMatchException.java +++ b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceEventTypeNotMatchException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AttendanceEventTypeNotMatchException extends BusinessLogicException { - public AttendanceEventTypeNotMatchException() {super(400, "출석일정은 직접 수정할 수 없습니다.");} + public AttendanceEventTypeNotMatchException() { + super(AttendanceErrorCode.ATTENDANCE_EVENT_TYPE_NOT_MATCH); + } } diff --git a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceNotFoundException.java b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceNotFoundException.java index 4eb441fa5..9d2a1b72d 100644 --- a/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceNotFoundException.java +++ b/src/main/java/leets/weeth/domain/attendance/application/exception/AttendanceNotFoundException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AttendanceNotFoundException extends BusinessLogicException { - public AttendanceNotFoundException() {super(404, "출석 정보가 존재하지 않습니다.");} -} \ No newline at end of file + public AttendanceNotFoundException() { + super(AttendanceErrorCode.ATTENDANCE_NOT_FOUND); + } +} diff --git a/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceAdminController.java b/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceAdminController.java index a1d5251e6..7be69f802 100644 --- a/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceAdminController.java +++ b/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceAdminController.java @@ -4,9 +4,11 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.attendance.application.dto.AttendanceDTO; +import leets.weeth.domain.attendance.application.exception.AttendanceErrorCode; import leets.weeth.domain.attendance.application.usecase.AttendanceUseCase; import leets.weeth.domain.schedule.application.dto.MeetingDTO; import leets.weeth.domain.schedule.application.usecase.MeetingUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -20,6 +22,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/attendances") +@ApiErrorCodeExample(AttendanceErrorCode.class) public class AttendanceAdminController { private final AttendanceUseCase attendanceUseCase; diff --git a/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceController.java b/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceController.java index 6426e0e4f..5ec4c1018 100644 --- a/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceController.java +++ b/src/main/java/leets/weeth/domain/attendance/presentation/AttendanceController.java @@ -4,9 +4,11 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import leets.weeth.domain.attendance.application.dto.AttendanceDTO; +import leets.weeth.domain.attendance.application.exception.AttendanceErrorCode; import leets.weeth.domain.attendance.application.usecase.AttendanceUseCase; import leets.weeth.global.auth.annotation.CurrentUser; import leets.weeth.domain.attendance.application.exception.AttendanceCodeMismatchException; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -20,6 +22,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/attendances") +@ApiErrorCodeExample(AttendanceErrorCode.class) public class AttendanceController { private final AttendanceUseCase attendanceUseCase; diff --git a/src/main/java/leets/weeth/domain/board/application/exception/BoardErrorCode.java b/src/main/java/leets/weeth/domain/board/application/exception/BoardErrorCode.java new file mode 100644 index 000000000..1e37af078 --- /dev/null +++ b/src/main/java/leets/weeth/domain/board/application/exception/BoardErrorCode.java @@ -0,0 +1,25 @@ +package leets.weeth.domain.board.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum BoardErrorCode implements ErrorCodeInterface { + + @ExplainError("검색 조건에 맞는 게시글이 하나도 없을 때 발생합니다.") + NO_SEARCH_RESULT(404, HttpStatus.NOT_FOUND, "일치하는 검색 결과를 찾을 수 없습니다."), + + @ExplainError("요청한 페이지 번호가 유효 범위를 벗어났을 때 발생합니다.") + PAGE_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 페이지입니다."), + + @ExplainError("일반 유저가 어드민 전용 카테고리에 접근하려 할 때 발생합니다.") + CATEGORY_ACCESS_DENIED(403, HttpStatus.FORBIDDEN, "어드민 유저만 접근 가능한 카테고리입니다"); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/board/application/exception/CategoryAccessDeniedException.java b/src/main/java/leets/weeth/domain/board/application/exception/CategoryAccessDeniedException.java index 184eb04fc..8af9a2ceb 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/CategoryAccessDeniedException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/CategoryAccessDeniedException.java @@ -4,6 +4,6 @@ public class CategoryAccessDeniedException extends BusinessLogicException { public CategoryAccessDeniedException() { - super(403, "어드민 유저만 접근 가능한 카테고리입니다"); + super(BoardErrorCode.CATEGORY_ACCESS_DENIED); } } diff --git a/src/main/java/leets/weeth/domain/board/application/exception/NoSearchResultException.java b/src/main/java/leets/weeth/domain/board/application/exception/NoSearchResultException.java index e15cef677..a2b884c6f 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/NoSearchResultException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/NoSearchResultException.java @@ -3,7 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class NoSearchResultException extends BusinessLogicException { - public NoSearchResultException(){ - super(404, "일치하는 검색 결과를 찾을 수 없습니다."); + public NoSearchResultException() { + super(BoardErrorCode.NO_SEARCH_RESULT); } } diff --git a/src/main/java/leets/weeth/domain/board/application/exception/NoticeErrorCode.java b/src/main/java/leets/weeth/domain/board/application/exception/NoticeErrorCode.java new file mode 100644 index 000000000..291a94e89 --- /dev/null +++ b/src/main/java/leets/weeth/domain/board/application/exception/NoticeErrorCode.java @@ -0,0 +1,22 @@ +package leets.weeth.domain.board.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum NoticeErrorCode implements ErrorCodeInterface { + + @ExplainError("요청한 공지사항 ID에 해당하는 공지사항이 없을 때 발생합니다.") + NOTICE_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 공지사항입니다."), + + @ExplainError("일반 게시판에서 공지사항을 수정하려 하거나, 그 반대의 경우 발생합니다.") + NOTICE_TYPE_NOT_MATCH(400, HttpStatus.BAD_REQUEST, "공지사항은 공지사항 게시판에서 수정하세요."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/board/application/exception/NoticeNotFoundException.java b/src/main/java/leets/weeth/domain/board/application/exception/NoticeNotFoundException.java index 66d825538..a524a20ac 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/NoticeNotFoundException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/NoticeNotFoundException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class NoticeNotFoundException extends BusinessLogicException { - public NoticeNotFoundException() {super(404, "존재하지 않는 공지사항입니다.");} + public NoticeNotFoundException() { + super(NoticeErrorCode.NOTICE_NOT_FOUND); + } } diff --git a/src/main/java/leets/weeth/domain/board/application/exception/NoticeTypeNotMatchException.java b/src/main/java/leets/weeth/domain/board/application/exception/NoticeTypeNotMatchException.java index 6bf482381..cee1f2086 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/NoticeTypeNotMatchException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/NoticeTypeNotMatchException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class NoticeTypeNotMatchException extends BusinessLogicException { - public NoticeTypeNotMatchException() {super(400, "공지사항은 공지사항 게시판에서 수정하세요.");} + public NoticeTypeNotMatchException() { + super(NoticeErrorCode.NOTICE_TYPE_NOT_MATCH); + } } diff --git a/src/main/java/leets/weeth/domain/board/application/exception/PageNotFoundException.java b/src/main/java/leets/weeth/domain/board/application/exception/PageNotFoundException.java index 1aca64111..e0d81fce6 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/PageNotFoundException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/PageNotFoundException.java @@ -4,7 +4,6 @@ public class PageNotFoundException extends BusinessLogicException { public PageNotFoundException() { - super(404, "존재하지 않는 페이지입니다."); - + super(BoardErrorCode.PAGE_NOT_FOUND); } -} \ No newline at end of file +} diff --git a/src/main/java/leets/weeth/domain/board/application/exception/PostErrorCode.java b/src/main/java/leets/weeth/domain/board/application/exception/PostErrorCode.java new file mode 100644 index 000000000..09fdbb224 --- /dev/null +++ b/src/main/java/leets/weeth/domain/board/application/exception/PostErrorCode.java @@ -0,0 +1,19 @@ +package leets.weeth.domain.board.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum PostErrorCode implements ErrorCodeInterface { + + @ExplainError("요청한 게시글 ID에 해당하는 게시글이 없을 때 발생합니다.") + POST_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 게시물입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/board/application/exception/PostNotFoundException.java b/src/main/java/leets/weeth/domain/board/application/exception/PostNotFoundException.java index b7b7158ac..86f373563 100644 --- a/src/main/java/leets/weeth/domain/board/application/exception/PostNotFoundException.java +++ b/src/main/java/leets/weeth/domain/board/application/exception/PostNotFoundException.java @@ -4,6 +4,6 @@ public class PostNotFoundException extends BusinessLogicException { public PostNotFoundException() { - super(404, "존재하지 않는 게시물입니다."); + super(PostErrorCode.POST_NOT_FOUND); } -} \ No newline at end of file +} diff --git a/src/main/java/leets/weeth/domain/board/presentation/EducationAdminController.java b/src/main/java/leets/weeth/domain/board/presentation/EducationAdminController.java index 196bc8cf2..bdd790f7f 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/EducationAdminController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/EducationAdminController.java @@ -5,9 +5,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.board.application.dto.PostDTO; +import leets.weeth.domain.board.application.exception.BoardErrorCode; +import leets.weeth.domain.board.application.exception.PostErrorCode; import leets.weeth.domain.board.application.usecase.PostUsecase; import leets.weeth.domain.user.application.exception.UserNotMatchException; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -19,6 +22,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/educations") +@ApiErrorCodeExample({BoardErrorCode.class, PostErrorCode.class}) public class EducationAdminController { private final PostUsecase postUsecase; diff --git a/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java b/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java index 520736d60..70271f2f6 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/NoticeAdminController.java @@ -5,9 +5,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.board.application.dto.NoticeDTO; +import leets.weeth.domain.board.application.exception.BoardErrorCode; +import leets.weeth.domain.board.application.exception.NoticeErrorCode; import leets.weeth.domain.board.application.usecase.NoticeUsecase; import leets.weeth.domain.user.application.exception.UserNotMatchException; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -18,6 +21,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/notices") +@ApiErrorCodeExample({BoardErrorCode.class, NoticeErrorCode.class}) public class NoticeAdminController { private final NoticeUsecase noticeUsecase; diff --git a/src/main/java/leets/weeth/domain/board/presentation/NoticeController.java b/src/main/java/leets/weeth/domain/board/presentation/NoticeController.java index fa2fb70a7..9e525c0b8 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/NoticeController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/NoticeController.java @@ -1,28 +1,26 @@ package leets.weeth.domain.board.presentation; -import static leets.weeth.domain.board.presentation.ResponseMessage.NOTICE_FIND_ALL_SUCCESS; -import static leets.weeth.domain.board.presentation.ResponseMessage.NOTICE_FIND_BY_ID_SUCCESS; -import static leets.weeth.domain.board.presentation.ResponseMessage.NOTICE_SEARCH_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import leets.weeth.domain.board.application.dto.NoticeDTO; +import leets.weeth.domain.board.application.exception.BoardErrorCode; +import leets.weeth.domain.board.application.exception.NoticeErrorCode; import leets.weeth.domain.board.application.usecase.NoticeUsecase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import static leets.weeth.domain.board.presentation.ResponseMessage.*; @Tag(name = "NOTICE", description = "공지사항 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/notices") +@ApiErrorCodeExample({BoardErrorCode.class, NoticeErrorCode.class}) public class NoticeController { private final NoticeUsecase noticeUsecase; diff --git a/src/main/java/leets/weeth/domain/board/presentation/PostController.java b/src/main/java/leets/weeth/domain/board/presentation/PostController.java index 6bfbf45be..64939a490 100644 --- a/src/main/java/leets/weeth/domain/board/presentation/PostController.java +++ b/src/main/java/leets/weeth/domain/board/presentation/PostController.java @@ -6,10 +6,13 @@ import jakarta.validation.Valid; import leets.weeth.domain.board.application.dto.PartPostDTO; import leets.weeth.domain.board.application.dto.PostDTO; +import leets.weeth.domain.board.application.exception.BoardErrorCode; +import leets.weeth.domain.board.application.exception.PostErrorCode; import leets.weeth.domain.board.application.usecase.PostUsecase; import leets.weeth.domain.board.domain.entity.enums.Part; import leets.weeth.domain.user.application.exception.UserNotMatchException; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; @@ -21,6 +24,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/board") +@ApiErrorCodeExample({BoardErrorCode.class, PostErrorCode.class}) public class PostController { private final PostUsecase postUsecase; diff --git a/src/main/java/leets/weeth/domain/comment/application/exception/CommentErrorCode.java b/src/main/java/leets/weeth/domain/comment/application/exception/CommentErrorCode.java new file mode 100644 index 000000000..1f5283d0e --- /dev/null +++ b/src/main/java/leets/weeth/domain/comment/application/exception/CommentErrorCode.java @@ -0,0 +1,19 @@ +package leets.weeth.domain.comment.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CommentErrorCode implements ErrorCodeInterface { + + @ExplainError("요청한 댓글 ID에 해당하는 댓글이 존재하지 않을 때 발생합니다.") + COMMENT_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 댓글입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/comment/application/exception/CommentNotFoundException.java b/src/main/java/leets/weeth/domain/comment/application/exception/CommentNotFoundException.java index dda78f2bc..514d1950c 100644 --- a/src/main/java/leets/weeth/domain/comment/application/exception/CommentNotFoundException.java +++ b/src/main/java/leets/weeth/domain/comment/application/exception/CommentNotFoundException.java @@ -4,6 +4,6 @@ public class CommentNotFoundException extends BusinessLogicException { public CommentNotFoundException() { - super(404, "존재하지 않는 댓글입니다."); + super(CommentErrorCode.COMMENT_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/domain/comment/presentation/NoticeCommentController.java b/src/main/java/leets/weeth/domain/comment/presentation/NoticeCommentController.java index fff250d80..7d8608234 100644 --- a/src/main/java/leets/weeth/domain/comment/presentation/NoticeCommentController.java +++ b/src/main/java/leets/weeth/domain/comment/presentation/NoticeCommentController.java @@ -1,25 +1,26 @@ package leets.weeth.domain.comment.presentation; -import static leets.weeth.domain.comment.presentation.ResponseMessage.COMMENT_CREATED_SUCCESS; -import static leets.weeth.domain.comment.presentation.ResponseMessage.COMMENT_DELETED_SUCCESS; -import static leets.weeth.domain.comment.presentation.ResponseMessage.COMMENT_UPDATED_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.comment.application.dto.CommentDTO; +import leets.weeth.domain.comment.application.exception.CommentErrorCode; import leets.weeth.domain.comment.application.usecase.NoticeCommentUsecase; -import leets.weeth.global.auth.annotation.CurrentUser; import leets.weeth.domain.user.application.exception.UserNotMatchException; +import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import static leets.weeth.domain.comment.presentation.ResponseMessage.*; + @Tag(name = "COMMENT-NOTICE", description = "공지사항 댓글 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/notices/{noticeId}/comments") +@ApiErrorCodeExample(CommentErrorCode.class) public class NoticeCommentController { private final NoticeCommentUsecase noticeCommentUsecase; diff --git a/src/main/java/leets/weeth/domain/comment/presentation/PostCommentController.java b/src/main/java/leets/weeth/domain/comment/presentation/PostCommentController.java index 83c30da1c..d8b9669a7 100644 --- a/src/main/java/leets/weeth/domain/comment/presentation/PostCommentController.java +++ b/src/main/java/leets/weeth/domain/comment/presentation/PostCommentController.java @@ -1,25 +1,26 @@ package leets.weeth.domain.comment.presentation; -import static leets.weeth.domain.comment.presentation.ResponseMessage.POST_COMMENT_CREATED_SUCCESS; -import static leets.weeth.domain.comment.presentation.ResponseMessage.POST_COMMENT_DELETED_SUCCESS; -import static leets.weeth.domain.comment.presentation.ResponseMessage.POST_COMMENT_UPDATED_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.comment.application.dto.CommentDTO; +import leets.weeth.domain.comment.application.exception.CommentErrorCode; import leets.weeth.domain.comment.application.usecase.PostCommentUsecase; -import leets.weeth.global.auth.annotation.CurrentUser; import leets.weeth.domain.user.application.exception.UserNotMatchException; +import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import static leets.weeth.domain.comment.presentation.ResponseMessage.*; + @Tag(name = "COMMENT-BOARD", description = "게시판 댓글 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/board/{boardId}/comments") +@ApiErrorCodeExample(CommentErrorCode.class) public class PostCommentController { private final PostCommentUsecase postCommentUsecase; diff --git a/src/main/java/leets/weeth/domain/penalty/application/exception/AutoPenaltyDeleteNotAllowedException.java b/src/main/java/leets/weeth/domain/penalty/application/exception/AutoPenaltyDeleteNotAllowedException.java index ee2fc0380..55a5e08a9 100644 --- a/src/main/java/leets/weeth/domain/penalty/application/exception/AutoPenaltyDeleteNotAllowedException.java +++ b/src/main/java/leets/weeth/domain/penalty/application/exception/AutoPenaltyDeleteNotAllowedException.java @@ -3,8 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class AutoPenaltyDeleteNotAllowedException extends BusinessLogicException { - public AutoPenaltyDeleteNotAllowedException(){ - super(400, "자동 생성된 패널티는 삭제할 수 없습니다"); + public AutoPenaltyDeleteNotAllowedException() { + super(PenaltyErrorCode.AUTO_PENALTY_DELETE_NOT_ALLOWED); } - } diff --git a/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyErrorCode.java b/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyErrorCode.java new file mode 100644 index 000000000..094074366 --- /dev/null +++ b/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyErrorCode.java @@ -0,0 +1,22 @@ +package leets.weeth.domain.penalty.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum PenaltyErrorCode implements ErrorCodeInterface { + + @ExplainError("요청한 패널티 ID가 존재하지 않을 때 발생합니다.") + PENALTY_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 패널티입니다."), + + @ExplainError("시스템에 의해 자동 부여된 패널티를 수동으로 삭제하려 할 때 발생합니다.") + AUTO_PENALTY_DELETE_NOT_ALLOWED(400, HttpStatus.BAD_REQUEST, "자동 생성된 패널티는 삭제할 수 없습니다"); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyNotFoundException.java b/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyNotFoundException.java index 49586b4d5..e1038c709 100644 --- a/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyNotFoundException.java +++ b/src/main/java/leets/weeth/domain/penalty/application/exception/PenaltyNotFoundException.java @@ -4,6 +4,6 @@ public class PenaltyNotFoundException extends BusinessLogicException { public PenaltyNotFoundException() { - super(404, "존재하지 않는 패널티입니다."); + super(PenaltyErrorCode.PENALTY_NOT_FOUND); } -} \ No newline at end of file +} diff --git a/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyAdminController.java b/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyAdminController.java index b2e07f561..c9bdd2c07 100644 --- a/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyAdminController.java +++ b/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyAdminController.java @@ -4,7 +4,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.penalty.application.dto.PenaltyDTO; +import leets.weeth.domain.penalty.application.exception.PenaltyErrorCode; import leets.weeth.domain.penalty.application.usecase.PenaltyUsecase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -17,6 +19,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/penalties") +@ApiErrorCodeExample(PenaltyErrorCode.class) public class PenaltyAdminController { private final PenaltyUsecase penaltyUsecase; diff --git a/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyUserController.java b/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyUserController.java index a22fa358b..99a03b9f2 100644 --- a/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyUserController.java +++ b/src/main/java/leets/weeth/domain/penalty/presentation/PenaltyUserController.java @@ -1,23 +1,26 @@ package leets.weeth.domain.penalty.presentation; -import static leets.weeth.domain.penalty.presentation.ResponseMessage.PENALTY_USER_FIND_SUCCESS; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import leets.weeth.domain.penalty.application.dto.PenaltyDTO; +import leets.weeth.domain.penalty.application.exception.PenaltyErrorCode; import leets.weeth.domain.penalty.application.usecase.PenaltyUsecase; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import static leets.weeth.domain.penalty.presentation.ResponseMessage.PENALTY_USER_FIND_SUCCESS; + @Tag(name = "PENALTY", description = "패널티 API") @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/penalties") +@ApiErrorCodeExample(PenaltyErrorCode.class) public class PenaltyUserController { private final PenaltyUsecase penaltyUsecase; diff --git a/src/main/java/leets/weeth/domain/schedule/application/exception/EventErrorCode.java b/src/main/java/leets/weeth/domain/schedule/application/exception/EventErrorCode.java new file mode 100644 index 000000000..33b32f2d1 --- /dev/null +++ b/src/main/java/leets/weeth/domain/schedule/application/exception/EventErrorCode.java @@ -0,0 +1,17 @@ +package leets.weeth.domain.schedule.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum EventErrorCode implements ErrorCodeInterface { + + EVENT_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 일정입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/schedule/application/exception/EventNotFoundException.java b/src/main/java/leets/weeth/domain/schedule/application/exception/EventNotFoundException.java index c4e0a15e6..933665a47 100644 --- a/src/main/java/leets/weeth/domain/schedule/application/exception/EventNotFoundException.java +++ b/src/main/java/leets/weeth/domain/schedule/application/exception/EventNotFoundException.java @@ -4,6 +4,6 @@ public class EventNotFoundException extends BusinessLogicException { public EventNotFoundException() { - super(404, "존재하지 않는 일정입니다."); + super(EventErrorCode.EVENT_NOT_FOUND); } -} \ No newline at end of file +} diff --git a/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingErrorCode.java b/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingErrorCode.java new file mode 100644 index 000000000..40f78438f --- /dev/null +++ b/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingErrorCode.java @@ -0,0 +1,17 @@ +package leets.weeth.domain.schedule.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum MeetingErrorCode implements ErrorCodeInterface { + + MEETING_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 정기모임입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingNotFoundException.java b/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingNotFoundException.java index b2e8e87ef..623d9c7a6 100644 --- a/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingNotFoundException.java +++ b/src/main/java/leets/weeth/domain/schedule/application/exception/MeetingNotFoundException.java @@ -3,5 +3,5 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class MeetingNotFoundException extends BusinessLogicException { - public MeetingNotFoundException() {super(404, "존재하지 않는 정기 모임입니다.");} + public MeetingNotFoundException() {super(MeetingErrorCode.MEETING_NOT_FOUND);} } diff --git a/src/main/java/leets/weeth/domain/schedule/presentation/EventAdminController.java b/src/main/java/leets/weeth/domain/schedule/presentation/EventAdminController.java index 830d6f140..e845f7c01 100644 --- a/src/main/java/leets/weeth/domain/schedule/presentation/EventAdminController.java +++ b/src/main/java/leets/weeth/domain/schedule/presentation/EventAdminController.java @@ -5,10 +5,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import leets.weeth.domain.schedule.application.dto.ScheduleDTO; +import leets.weeth.domain.schedule.application.exception.EventErrorCode; import leets.weeth.domain.schedule.application.usecase.EventUseCase; import leets.weeth.domain.schedule.application.usecase.MeetingUseCase; import leets.weeth.domain.schedule.domain.entity.enums.Type; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -19,6 +21,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/events") +@ApiErrorCodeExample(EventErrorCode.class) public class EventAdminController { private final EventUseCase eventUseCase; diff --git a/src/main/java/leets/weeth/domain/schedule/presentation/EventController.java b/src/main/java/leets/weeth/domain/schedule/presentation/EventController.java index a2906c6c7..daf46f0eb 100644 --- a/src/main/java/leets/weeth/domain/schedule/presentation/EventController.java +++ b/src/main/java/leets/weeth/domain/schedule/presentation/EventController.java @@ -2,7 +2,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import leets.weeth.domain.schedule.application.exception.EventErrorCode; import leets.weeth.domain.schedule.application.usecase.EventUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -17,6 +19,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/events") +@ApiErrorCodeExample(EventErrorCode.class) public class EventController { private final EventUseCase eventUseCase; diff --git a/src/main/java/leets/weeth/domain/schedule/presentation/MeetingAdminController.java b/src/main/java/leets/weeth/domain/schedule/presentation/MeetingAdminController.java index e434b510b..17434a5be 100644 --- a/src/main/java/leets/weeth/domain/schedule/presentation/MeetingAdminController.java +++ b/src/main/java/leets/weeth/domain/schedule/presentation/MeetingAdminController.java @@ -2,7 +2,9 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import leets.weeth.domain.schedule.application.exception.MeetingErrorCode; import leets.weeth.domain.schedule.application.usecase.MeetingUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.DeleteMapping; @@ -16,6 +18,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/meetings") +@ApiErrorCodeExample(MeetingErrorCode.class) public class MeetingAdminController { private final MeetingUseCase meetingUseCase; diff --git a/src/main/java/leets/weeth/domain/schedule/presentation/MeetingController.java b/src/main/java/leets/weeth/domain/schedule/presentation/MeetingController.java index 8b17ef843..cd538efbd 100644 --- a/src/main/java/leets/weeth/domain/schedule/presentation/MeetingController.java +++ b/src/main/java/leets/weeth/domain/schedule/presentation/MeetingController.java @@ -4,8 +4,10 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import leets.weeth.domain.schedule.application.dto.MeetingDTO; +import leets.weeth.domain.schedule.application.exception.MeetingErrorCode; import leets.weeth.domain.schedule.application.usecase.MeetingUseCase; import leets.weeth.global.auth.annotation.CurrentUser; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; @@ -19,6 +21,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/meetings") +@ApiErrorCodeExample(MeetingErrorCode.class) public class MeetingController { private final MeetingUseCase meetingUseCase; diff --git a/src/main/java/leets/weeth/domain/schedule/presentation/ScheduleController.java b/src/main/java/leets/weeth/domain/schedule/presentation/ScheduleController.java index ce17ab407..dd4463e65 100644 --- a/src/main/java/leets/weeth/domain/schedule/presentation/ScheduleController.java +++ b/src/main/java/leets/weeth/domain/schedule/presentation/ScheduleController.java @@ -2,7 +2,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import leets.weeth.domain.schedule.application.exception.EventErrorCode; +import leets.weeth.domain.schedule.application.exception.MeetingErrorCode; import leets.weeth.domain.schedule.application.usecase.ScheduleUseCase; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; @@ -23,6 +26,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/schedules") +@ApiErrorCodeExample({EventErrorCode.class, MeetingErrorCode.class}) public class ScheduleController { private final ScheduleUseCase scheduleUseCase; diff --git a/src/main/java/leets/weeth/domain/user/application/exception/CardinalNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/CardinalNotFoundException.java index d7931d6b9..925203ac9 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/CardinalNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/CardinalNotFoundException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class CardinalNotFoundException extends BusinessLogicException { - public CardinalNotFoundException() {super(404, "존재하지 않는 기수입니다.");} + public CardinalNotFoundException() { + super(UserErrorCode.CARDINAL_NOT_FOUND); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/DepartmentNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/DepartmentNotFoundException.java index fc1fe6066..978f093cf 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/DepartmentNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/DepartmentNotFoundException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class DepartmentNotFoundException extends BusinessLogicException { - public DepartmentNotFoundException() {super(404, "존재하지 않는 학과입니다.");} + public DepartmentNotFoundException() { + super(UserErrorCode.DEPARTMENT_NOT_FOUND); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/DuplicateCardinalException.java b/src/main/java/leets/weeth/domain/user/application/exception/DuplicateCardinalException.java index 11d1092dc..3e6291961 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/DuplicateCardinalException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/DuplicateCardinalException.java @@ -4,6 +4,6 @@ public class DuplicateCardinalException extends BusinessLogicException { public DuplicateCardinalException() { - super(400, "이미 존재하는 기수 입니다."); + super(UserErrorCode.DUPLICATE_CARDINAL); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/EmailNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/EmailNotFoundException.java index adc1d2d7f..89aed4e50 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/EmailNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/EmailNotFoundException.java @@ -4,6 +4,6 @@ public class EmailNotFoundException extends BusinessLogicException { public EmailNotFoundException() { - super(404, "Redis에 저장된 email이 없습니다."); + super(UserErrorCode.EMAIL_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/InvalidUserOrderException.java b/src/main/java/leets/weeth/domain/user/application/exception/InvalidUserOrderException.java index 4872cc215..ac005ef96 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/InvalidUserOrderException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/InvalidUserOrderException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class InvalidUserOrderException extends BusinessLogicException { - public InvalidUserOrderException() {super(400, "올바른 유저 조회 순서가 아닙니다.");} + public InvalidUserOrderException() { + super(UserErrorCode.INVALID_USER_ORDER); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/PasswordMismatchException.java b/src/main/java/leets/weeth/domain/user/application/exception/PasswordMismatchException.java index 918d60fe3..9a9b84adb 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/PasswordMismatchException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/PasswordMismatchException.java @@ -4,6 +4,6 @@ public class PasswordMismatchException extends BusinessLogicException { public PasswordMismatchException() { - super(400, "비밀번호가 일치하지 않습니다."); + super(UserErrorCode.PASSWORD_MISMATCH); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/RoleNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/RoleNotFoundException.java index 2bc15f207..00fa0f15d 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/RoleNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/RoleNotFoundException.java @@ -4,6 +4,6 @@ public class RoleNotFoundException extends BusinessLogicException { public RoleNotFoundException() { - super(404, "Redis에 저장된 role이 없습니다."); + super(UserErrorCode.ROLE_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/StatusNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/StatusNotFoundException.java index 3686e3ab4..225ec0b79 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/StatusNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/StatusNotFoundException.java @@ -3,7 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class StatusNotFoundException extends BusinessLogicException { - public StatusNotFoundException() { - super(400, "존재하지 않는 status 입니다."); - } + public StatusNotFoundException() { + super(UserErrorCode.STATUS_NOT_FOUND); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/StudentIdExistsException.java b/src/main/java/leets/weeth/domain/user/application/exception/StudentIdExistsException.java index 5203bf7f2..d55b321cb 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/StudentIdExistsException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/StudentIdExistsException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class StudentIdExistsException extends BusinessLogicException { - public StudentIdExistsException() {super(400, "이미 가입된 학번입니다.");} + public StudentIdExistsException() { + super(UserErrorCode.STUDENT_ID_EXISTS); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/TelExistsException.java b/src/main/java/leets/weeth/domain/user/application/exception/TelExistsException.java index eeb49ca34..f7abe6045 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/TelExistsException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/TelExistsException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class TelExistsException extends BusinessLogicException { - public TelExistsException() {super(400, "이미 가입된 전화번호 입니다.");} + public TelExistsException() { + super(UserErrorCode.TEL_EXISTS); + } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserCardinalNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserCardinalNotFoundException.java index 0b981b293..63bbf0758 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserCardinalNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserCardinalNotFoundException.java @@ -4,6 +4,6 @@ public class UserCardinalNotFoundException extends BusinessLogicException { public UserCardinalNotFoundException() { - super(404, "사용자가 해당 기수에 속해있지 않습니다."); + super(UserErrorCode.USER_CARDINAL_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserErrorCode.java b/src/main/java/leets/weeth/domain/user/application/exception/UserErrorCode.java new file mode 100644 index 000000000..3634a9645 --- /dev/null +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserErrorCode.java @@ -0,0 +1,68 @@ +package leets.weeth.domain.user.application.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum UserErrorCode implements ErrorCodeInterface { + // User 관련 에러 + @ExplainError("사용자 ID로 조회했으나 해당 사용자가 존재하지 않을 때 발생합니다.") + USER_NOT_FOUND(404, HttpStatus.NOT_FOUND, "존재하지 않는 유저입니다."), + + @ExplainError("가입 승인 대기 중인 사용자가 접근을 시도할 때 발생합니다.") + USER_INACTIVE(403, HttpStatus.FORBIDDEN, "가입 승인이 허가되지 않은 계정입니다."), + + @ExplainError("이미 가입된 이메일로 회원가입을 시도할 때 발생합니다.") + USER_EXISTS(400, HttpStatus.BAD_REQUEST, "이미 가입된 사용자입니다."), + + @ExplainError("요청한 사용자 정보와 실제 사용자 정보가 일치하지 않을 때 발생합니다.") + USER_MISMATCH(403, HttpStatus.FORBIDDEN, "사용자 정보가 일치하지 않습니다."), + + @ExplainError("다른 사용자의 리소스에 접근하려고 할 때 발생합니다.") + USER_NOT_MATCH(403, HttpStatus.FORBIDDEN, "해당 사용자가 아닙니다."), + + // 인증 관련 에러 + @ExplainError("로그인 시 비밀번호가 일치하지 않을 때 발생합니다.") + PASSWORD_MISMATCH(400, HttpStatus.BAD_REQUEST, "비밀번호가 일치하지 않습니다."), + + @ExplainError("입력한 이메일로 등록된 사용자가 없을 때 발생합니다.") + EMAIL_NOT_FOUND(404, HttpStatus.NOT_FOUND, "이메일을 찾을 수 없습니다."), + + // 검증 에러 + @ExplainError("이미 등록된 학번으로 회원가입을 시도할 때 발생합니다.") + STUDENT_ID_EXISTS(400, HttpStatus.BAD_REQUEST, "이미 존재하는 학번입니다."), + + @ExplainError("이미 등록된 전화번호로 회원가입을 시도할 때 발생합니다.") + TEL_EXISTS(400, HttpStatus.BAD_REQUEST, "이미 존재하는 전화번호입니다."), + + // Cardinal 관련 에러 + @ExplainError("존재하지 않는 기수 정보로 조회할 때 발생합니다.") + CARDINAL_NOT_FOUND(404, HttpStatus.NOT_FOUND, "기수를 찾을 수 없습니다."), + + @ExplainError("이미 존재하는 기수를 생성하려고 할 때 발생합니다.") + DUPLICATE_CARDINAL(400, HttpStatus.BAD_REQUEST, "이미 존재하는 기수입니다."), + + @ExplainError("사용자와 기수 간의 연결 정보를 찾을 수 없을 때 발생합니다.") + USER_CARDINAL_NOT_FOUND(404, HttpStatus.NOT_FOUND, "사용자 기수 정보를 찾을 수 없습니다."), + + // Enum 관련 에러 + @ExplainError("잘못된 학과 값이 입력되었을 때 발생합니다.") + DEPARTMENT_NOT_FOUND(400, HttpStatus.BAD_REQUEST, "학과를 찾을 수 없습니다."), + + @ExplainError("잘못된 권한 값이 입력되었을 때 발생합니다.") + ROLE_NOT_FOUND(400, HttpStatus.BAD_REQUEST, "권한을 찾을 수 없습니다."), + + @ExplainError("잘못된 상태 값이 입력되었을 때 발생합니다.") + STATUS_NOT_FOUND(400, HttpStatus.BAD_REQUEST, "상태를 찾을 수 없습니다."), + + @ExplainError("사용자 순서 지정 시 잘못된 값이 입력되었을 때 발생합니다.") + INVALID_USER_ORDER(400, HttpStatus.BAD_REQUEST, "잘못된 사용자 순서입니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserExistsException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserExistsException.java index 3e19c9ed2..85a527897 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserExistsException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserExistsException.java @@ -4,6 +4,6 @@ public class UserExistsException extends BusinessLogicException { public UserExistsException() { - super(400, "이미 가입된 사용자입니다."); + super(UserErrorCode.USER_EXISTS); } } \ No newline at end of file diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserInActiveException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserInActiveException.java index 1249b3ed3..ec8ce5c3f 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserInActiveException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserInActiveException.java @@ -4,6 +4,6 @@ public class UserInActiveException extends BusinessLogicException { public UserInActiveException() { - super(403, "가입 승인이 허가되지 않은 계정입니다."); + super(UserErrorCode.USER_INACTIVE); } } diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserMismatchException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserMismatchException.java index 35da8c041..54516564b 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserMismatchException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserMismatchException.java @@ -4,6 +4,6 @@ public class UserMismatchException extends BusinessLogicException { public UserMismatchException() { - super(400, "사용자가 현재 사용자와 일치하지 않습니다."); + super(UserErrorCode.USER_MISMATCH); } } \ No newline at end of file diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserNotFoundException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserNotFoundException.java index 0df93c62a..9681a3666 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserNotFoundException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserNotFoundException.java @@ -4,6 +4,6 @@ public class UserNotFoundException extends BusinessLogicException { public UserNotFoundException() { - super(404, "존재하지 않는 유저입니다."); + super(UserErrorCode.USER_NOT_FOUND); } } \ No newline at end of file diff --git a/src/main/java/leets/weeth/domain/user/application/exception/UserNotMatchException.java b/src/main/java/leets/weeth/domain/user/application/exception/UserNotMatchException.java index 45c9c330c..2c7702bd2 100644 --- a/src/main/java/leets/weeth/domain/user/application/exception/UserNotMatchException.java +++ b/src/main/java/leets/weeth/domain/user/application/exception/UserNotMatchException.java @@ -3,5 +3,7 @@ import leets.weeth.global.common.exception.BusinessLogicException; public class UserNotMatchException extends BusinessLogicException { - public UserNotMatchException() {super(400, "생성한 사용자와 일치하지 않습니다.");} + public UserNotMatchException() { + super(UserErrorCode.USER_NOT_MATCH); + } } diff --git a/src/main/java/leets/weeth/domain/user/presentation/CardinalController.java b/src/main/java/leets/weeth/domain/user/presentation/CardinalController.java index e2e121afd..81254c7b7 100644 --- a/src/main/java/leets/weeth/domain/user/presentation/CardinalController.java +++ b/src/main/java/leets/weeth/domain/user/presentation/CardinalController.java @@ -6,7 +6,10 @@ import leets.weeth.domain.user.application.dto.request.CardinalSaveRequest; import leets.weeth.domain.user.application.dto.request.CardinalUpdateRequest; import leets.weeth.domain.user.application.dto.response.CardinalResponse; +import leets.weeth.domain.user.application.exception.UserErrorCode; import leets.weeth.domain.user.application.usecase.CardinalUseCase; +import leets.weeth.global.auth.jwt.exception.JwtErrorCode; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -19,6 +22,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1") +@ApiErrorCodeExample({UserErrorCode.class, JwtErrorCode.class}) public class CardinalController { private final CardinalUseCase cardinalUseCase; diff --git a/src/main/java/leets/weeth/domain/user/presentation/UserAdminController.java b/src/main/java/leets/weeth/domain/user/presentation/UserAdminController.java index ea8ddc526..33d31c115 100644 --- a/src/main/java/leets/weeth/domain/user/presentation/UserAdminController.java +++ b/src/main/java/leets/weeth/domain/user/presentation/UserAdminController.java @@ -2,8 +2,11 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import leets.weeth.domain.user.application.exception.UserErrorCode; import leets.weeth.domain.user.application.usecase.UserManageUseCase; import leets.weeth.domain.user.domain.entity.enums.UsersOrderBy; +import leets.weeth.global.auth.jwt.exception.JwtErrorCode; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -18,6 +21,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/admin/users") +@ApiErrorCodeExample({UserErrorCode.class, JwtErrorCode.class}) public class UserAdminController { private final UserManageUseCase userManageUseCase; diff --git a/src/main/java/leets/weeth/domain/user/presentation/UserController.java b/src/main/java/leets/weeth/domain/user/presentation/UserController.java index 9c131428a..e72e1d0df 100644 --- a/src/main/java/leets/weeth/domain/user/presentation/UserController.java +++ b/src/main/java/leets/weeth/domain/user/presentation/UserController.java @@ -7,11 +7,14 @@ import leets.weeth.domain.user.application.dto.response.UserResponseDto; import leets.weeth.domain.user.application.dto.response.UserResponseDto.SummaryResponse; import leets.weeth.domain.user.application.dto.response.UserResponseDto.UserResponse; +import leets.weeth.domain.user.application.exception.UserErrorCode; import leets.weeth.domain.user.application.usecase.UserManageUseCase; import leets.weeth.domain.user.application.usecase.UserUseCase; import leets.weeth.domain.user.domain.service.UserGetService; import leets.weeth.global.auth.annotation.CurrentUser; import leets.weeth.global.auth.jwt.application.dto.JwtDto; +import leets.weeth.global.auth.jwt.exception.JwtErrorCode; +import leets.weeth.global.common.exception.ApiErrorCodeExample; import leets.weeth.global.common.response.CommonResponse; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; @@ -28,6 +31,7 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/users") +@ApiErrorCodeExample({UserErrorCode.class, JwtErrorCode.class}) public class UserController { private final UserUseCase userUseCase; diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java b/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java index 7cc774519..4d96d31f5 100644 --- a/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/AnonymousAuthenticationException.java @@ -4,6 +4,6 @@ public class AnonymousAuthenticationException extends BusinessLogicException { public AnonymousAuthenticationException() { - super(401, "인증정보가 존재하지 않습니다."); + super(JwtErrorCode.ANONYMOUS_AUTHENTICATION); } } diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/InvalidTokenException.java b/src/main/java/leets/weeth/global/auth/jwt/exception/InvalidTokenException.java index a1eda672b..616d349c3 100644 --- a/src/main/java/leets/weeth/global/auth/jwt/exception/InvalidTokenException.java +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/InvalidTokenException.java @@ -4,6 +4,6 @@ public class InvalidTokenException extends BusinessLogicException { public InvalidTokenException() { - super(400, "올바르지 않은 Token 입니다."); + super(JwtErrorCode.INVALID_TOKEN); } } diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/JwtErrorCode.java b/src/main/java/leets/weeth/global/auth/jwt/exception/JwtErrorCode.java new file mode 100644 index 000000000..e57078c3a --- /dev/null +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/JwtErrorCode.java @@ -0,0 +1,28 @@ +package leets.weeth.global.auth.jwt.exception; + +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExplainError; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum JwtErrorCode implements ErrorCodeInterface { + + @ExplainError("토큰의 구조가 올바르지 않거나(Malformed), 서명이 유효하지 않은 경우 발생합니다. 토큰을 재발급 받아주세요.") + INVALID_TOKEN(400, HttpStatus.BAD_REQUEST, "올바르지 않은 Token 입니다."), + + @ExplainError("Redis에 해당 리프레시 토큰이 존재하지 않습니다. 토큰이 만료되었거나, 이미 로그아웃(삭제)된 상태일 수 있습니다. 다시 로그인해주세요.") + REDIS_TOKEN_NOT_FOUND(404, HttpStatus.NOT_FOUND,"저장된 리프레시 토큰이 존재하지 않습니다."), + + @ExplainError("API 요청 헤더(Authorization)에 토큰 값이 포함되지 않았거나 비어있을 때 발생합니다.") + TOKEN_NOT_FOUND(404, HttpStatus.NOT_FOUND, "헤더에서 토큰을 찾을 수 없습니다."), + + @ExplainError("인증이 필요한 리소스에 인증 정보 없이(Anonymous) 접근을 시도했을 때 발생합니다. (Spring Security 필터 단계 차단)") + ANONYMOUS_AUTHENTICATION(401, HttpStatus.UNAUTHORIZED, "인증정보가 존재하지 않습니다."); + + private final int code; + private final HttpStatus status; + private final String message; +} diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/RedisTokenNotFoundException.java b/src/main/java/leets/weeth/global/auth/jwt/exception/RedisTokenNotFoundException.java index aa494e755..d50d99eab 100644 --- a/src/main/java/leets/weeth/global/auth/jwt/exception/RedisTokenNotFoundException.java +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/RedisTokenNotFoundException.java @@ -4,6 +4,6 @@ public class RedisTokenNotFoundException extends BusinessLogicException { public RedisTokenNotFoundException() { - super(404, "저장된 리프레시 토큰이 존재하지 않습니다."); + super(JwtErrorCode.REDIS_TOKEN_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/global/auth/jwt/exception/TokenNotFoundException.java b/src/main/java/leets/weeth/global/auth/jwt/exception/TokenNotFoundException.java index 54439a949..2b7afb05c 100644 --- a/src/main/java/leets/weeth/global/auth/jwt/exception/TokenNotFoundException.java +++ b/src/main/java/leets/weeth/global/auth/jwt/exception/TokenNotFoundException.java @@ -4,6 +4,6 @@ public class TokenNotFoundException extends BusinessLogicException { public TokenNotFoundException() { - super(404, "헤더에서 토큰을 찾을 수 없습니다."); + super(JwtErrorCode.TOKEN_NOT_FOUND); } } diff --git a/src/main/java/leets/weeth/global/common/controller/ExceptionDocController.java b/src/main/java/leets/weeth/global/common/controller/ExceptionDocController.java new file mode 100644 index 000000000..00c80091f --- /dev/null +++ b/src/main/java/leets/weeth/global/common/controller/ExceptionDocController.java @@ -0,0 +1,68 @@ +package leets.weeth.global.common.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import leets.weeth.domain.account.application.exception.AccountErrorCode; +import leets.weeth.domain.attendance.application.exception.AttendanceErrorCode; +import leets.weeth.domain.board.application.exception.BoardErrorCode; +import leets.weeth.domain.board.application.exception.NoticeErrorCode; +import leets.weeth.domain.board.application.exception.PostErrorCode; +import leets.weeth.domain.comment.application.exception.CommentErrorCode; +import leets.weeth.domain.penalty.application.exception.PenaltyErrorCode; +import leets.weeth.domain.schedule.application.exception.EventErrorCode; +import leets.weeth.domain.schedule.application.exception.MeetingErrorCode; +import leets.weeth.domain.user.application.exception.UserErrorCode; +import leets.weeth.global.auth.jwt.exception.JwtErrorCode; +import leets.weeth.global.common.exception.ApiErrorCodeExample; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/exceptions") +@Tag(name = "Exception Document", description = "API 에러 코드 문서") +public class ExceptionDocController { + + @GetMapping("/Account") + @Operation(summary = "Account 도메인 에러 코드 목록") + @ApiErrorCodeExample(AccountErrorCode.class) + public void accountErrorCodes() { + } + + @GetMapping("/Attendance") + @Operation(summary = "Attendance 도메인 에러 코드 목록") + @ApiErrorCodeExample(AttendanceErrorCode.class) + public void attendanceErrorCodes() { + } + + @GetMapping("/Board") + @Operation(summary = "Board 도메인 에러 코드 목록") + @ApiErrorCodeExample({BoardErrorCode.class, NoticeErrorCode.class, PostErrorCode.class, CommentErrorCode.class}) + public void boardErrorCodes() { + } + + @GetMapping("/Penalty") + @Operation(summary = "Penalty 도메인 에러 코드 목록") + @ApiErrorCodeExample(PenaltyErrorCode.class) + public void penaltyErrorCodes() { + } + + @GetMapping("/Schedule") + @Operation(summary = "Schedule 도메인 에러 코드 목록") + @ApiErrorCodeExample({EventErrorCode.class, MeetingErrorCode.class}) + public void scheduleErrorCodes() { + } + + @GetMapping("/User") + @Operation(summary = "User 도메인 에러 코드 목록") + @ApiErrorCodeExample(UserErrorCode.class) + public void userErrorCodes() { + } + + //todo: SAS 관련 예외도 추가 + @GetMapping("/Auth") + @Operation(summary = "인증/인가 에러 코드 목록") + @ApiErrorCodeExample({JwtErrorCode.class}) + public void authErrorCodes() { + } +} diff --git a/src/main/java/leets/weeth/global/common/exception/ApiErrorCodeExample.java b/src/main/java/leets/weeth/global/common/exception/ApiErrorCodeExample.java new file mode 100644 index 000000000..67cd4eb83 --- /dev/null +++ b/src/main/java/leets/weeth/global/common/exception/ApiErrorCodeExample.java @@ -0,0 +1,12 @@ +package leets.weeth.global.common.exception; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiErrorCodeExample { + Class[] value(); +} diff --git a/src/main/java/leets/weeth/global/common/exception/BusinessLogicException.java b/src/main/java/leets/weeth/global/common/exception/BusinessLogicException.java index 760da0bf2..3a3fa05d1 100644 --- a/src/main/java/leets/weeth/global/common/exception/BusinessLogicException.java +++ b/src/main/java/leets/weeth/global/common/exception/BusinessLogicException.java @@ -6,9 +6,17 @@ public abstract class BusinessLogicException extends RuntimeException { private final int statusCode; + private final ErrorCodeInterface errorCode; public BusinessLogicException(int code, String message) { super(message); this.statusCode = code; + this.errorCode = null; + } + + public BusinessLogicException(ErrorCodeInterface errorCode) { + super(errorCode.getMessage()); + this.statusCode = errorCode.getStatus().value(); + this.errorCode = errorCode; } } diff --git a/src/main/java/leets/weeth/global/common/exception/ErrorCodeInterface.java b/src/main/java/leets/weeth/global/common/exception/ErrorCodeInterface.java new file mode 100644 index 000000000..af2aeb7da --- /dev/null +++ b/src/main/java/leets/weeth/global/common/exception/ErrorCodeInterface.java @@ -0,0 +1,19 @@ +package leets.weeth.global.common.exception; + +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +public interface ErrorCodeInterface { + int getCode(); + HttpStatus getStatus(); + String getMessage(); + + // ExplainError 어노테이션에 작성된 설명을 조회하는 메서드 + default String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(((Enum) this).name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : getMessage(); + } +} diff --git a/src/main/java/leets/weeth/global/common/exception/ExampleHolder.java b/src/main/java/leets/weeth/global/common/exception/ExampleHolder.java new file mode 100644 index 000000000..547313e85 --- /dev/null +++ b/src/main/java/leets/weeth/global/common/exception/ExampleHolder.java @@ -0,0 +1,13 @@ +package leets.weeth.global.common.exception; + +import io.swagger.v3.oas.models.examples.Example; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ExampleHolder { + private Example holder; + private String name; + private int code; +} diff --git a/src/main/java/leets/weeth/global/common/exception/ExplainError.java b/src/main/java/leets/weeth/global/common/exception/ExplainError.java new file mode 100644 index 000000000..3fbf00fd7 --- /dev/null +++ b/src/main/java/leets/weeth/global/common/exception/ExplainError.java @@ -0,0 +1,12 @@ +package leets.weeth.global.common.exception; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExplainError { + String value() default ""; +} diff --git a/src/main/java/leets/weeth/global/config/swagger/SwaggerConfig.java b/src/main/java/leets/weeth/global/config/swagger/SwaggerConfig.java index 879e69218..72949bf93 100644 --- a/src/main/java/leets/weeth/global/config/swagger/SwaggerConfig.java +++ b/src/main/java/leets/weeth/global/config/swagger/SwaggerConfig.java @@ -4,14 +4,29 @@ import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; +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 io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; +import leets.weeth.global.common.exception.ApiErrorCodeExample; +import leets.weeth.global.common.exception.ErrorCodeInterface; +import leets.weeth.global.common.exception.ExampleHolder; +import leets.weeth.global.common.response.CommonResponse; +import org.springdoc.core.customizers.OperationCustomizer; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.Arrays; import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.groupingBy; @Configuration @OpenAPIDefinition( @@ -21,14 +36,16 @@ version = "v1.0.0" ) ) - public class SwaggerConfig { @Value("${weeth.jwt.access.header}") private String accessHeader; @Value("${weeth.jwt.refresh.header}") - private String refreshHeader; // 리프레시 토큰 헤더 이름 + private String refreshHeader; + + public SwaggerConfig(ApplicationContext applicationContext) { + } @Bean public OpenAPI openAPI() { @@ -46,6 +63,92 @@ public OpenAPI openAPI() { )); } + // 스웨거 문서를 커스텀하기 위한 설정 + @Bean + public OperationCustomizer operationCustomizer() { + return (operation, handlerMethod) -> { + // 메서드 레벨 어노테이션이 존재하는지 확인, 없으면 클래스 레벨 체크 + ApiErrorCodeExample apiErrorCodeExample = handlerMethod.getMethodAnnotation(ApiErrorCodeExample.class); + if (apiErrorCodeExample == null) { + apiErrorCodeExample = handlerMethod.getBeanType().getAnnotation(ApiErrorCodeExample.class); + } + + if (apiErrorCodeExample != null) { + for (Class type : apiErrorCodeExample.value()) { + generateErrorCodeResponseExample(operation.getResponses(), type); + } + } + + return operation; + }; + } + + // 예외 예시를 스웨거 문서에 추가하기 위한 객체를 생성하는 메서드 + private void generateErrorCodeResponseExample(ApiResponses responses, Class type) { + ErrorCodeInterface[] errorCodes = type.getEnumConstants(); + + Map> statusWithExampleHolders = + Arrays.stream(errorCodes) + .map(errorCode -> { + try { + String enumName = ((Enum) errorCode).name(); + + return ExampleHolder.builder() + .holder(getSwaggerExample(errorCode.getExplainError(), errorCode)) + .code(errorCode.getStatus().value()) + .name("[" + enumName + "] " + errorCode.getMessage()) // 한글로된 드롭다운을 만들기 위해 예외 메시지를 이름으로 사용 + .build(); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + }) + .collect(groupingBy(ExampleHolder::getCode)); + + addExamplesToResponses(responses, statusWithExampleHolders); + } + + // ExplainError 설명과 에러코드 객체를 받아 Swagger의 Example 객체를 생성하는 메서드 + private Example getSwaggerExample(String description, ErrorCodeInterface errorCode) { + CommonResponse errorResponse = CommonResponse.createFailure(errorCode.getCode(), errorCode.getMessage()); + Example example = new Example(); + example.description(description); + example.setValue(errorResponse); + + return example; + } + + // 스웨거의 Example 객체를 만들어 Operation.Responses에 예시 데이터를 추가하는 메서드 + private void addExamplesToResponses(ApiResponses responses, Map> statusWithExampleHolders) { + statusWithExampleHolders.forEach((status, exampleHolders) -> { + // ApiResponse가 없으면 생성 + ApiResponse apiResponse = responses.computeIfAbsent(String.valueOf(status), k -> new ApiResponse()); + + // application/json 타입의 MediaType 가져오기 (없으면 생성) + MediaType mediaType = getOrCreateMediaType(apiResponse); + + // 예시 데이터 추가 + exampleHolders.forEach(holder -> + mediaType.addExamples(holder.getName(), holder.getHolder()) + ); + }); + } + + private MediaType getOrCreateMediaType(ApiResponse apiResponse) { + Content content = apiResponse.getContent(); + if (content == null) { + content = new Content(); + apiResponse.setContent(content); + } + + MediaType mediaType = content.get("application/json"); + if (mediaType == null) { + mediaType = new MediaType(); + content.addMediaType("application/json", mediaType); + } + + return mediaType; + } + private SecurityScheme getAccessSecurityScheme() { return new SecurityScheme() .type(SecurityScheme.Type.HTTP) @@ -63,5 +166,4 @@ private SecurityScheme getRefreshSecurityScheme() { .in(SecurityScheme.In.HEADER) .name(refreshHeader); } - }