From 63f6cc3e3d122cb9e7a1182cbed8cad65072c574 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:11:22 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[#2]=20delete:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ApplicationException.java | 16 -------- .../global/exception/ExceptionCode.java | 37 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 src/main/java/run/backend/global/exception/ApplicationException.java delete mode 100644 src/main/java/run/backend/global/exception/ExceptionCode.java diff --git a/src/main/java/run/backend/global/exception/ApplicationException.java b/src/main/java/run/backend/global/exception/ApplicationException.java deleted file mode 100644 index 533acb0..0000000 --- a/src/main/java/run/backend/global/exception/ApplicationException.java +++ /dev/null @@ -1,16 +0,0 @@ -package run.backend.global.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class ApplicationException extends RuntimeException { - - public ExceptionCode exceptionCode; - - @Override - public String toString() { - return exceptionCode.getMessage(); - } -} diff --git a/src/main/java/run/backend/global/exception/ExceptionCode.java b/src/main/java/run/backend/global/exception/ExceptionCode.java deleted file mode 100644 index 4b41f2f..0000000 --- a/src/main/java/run/backend/global/exception/ExceptionCode.java +++ /dev/null @@ -1,37 +0,0 @@ -package run.backend.global.exception; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; - -@Getter -@RequiredArgsConstructor -public enum ExceptionCode { - - // 1000: Success Code - - // 2000: Common Error - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 2000, "서버 에러가 발생하였습니다. 관리자에게 문의해 주세요."), - BAD_REQUEST_ERROR(HttpStatus.BAD_REQUEST, 2001, "잘못된 요청입니다."), - - // 3000: Auth Error - INVALID_SIGNUP_TOKEN(HttpStatus.UNAUTHORIZED, 4001, "유효하지 않은 가입 토큰입니다."), - USER_ALREADY_EXISTS(HttpStatus.CONFLICT, 4002, "이미 가입된 사용자입니다."), - OAUTH_REQUEST_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, 4003, "외부 인증 서버와 통신 중 오류가 발생했습니다."), - TOKEN_MISSING_AUTHORITY(HttpStatus.UNAUTHORIZED, 4004, "토큰에 권한 정보가 없습니다."), - INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, 4005, "유효하지 않은 리프레시 토큰입니다."), - REFRESH_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, 4006, "리프레시 토큰을 찾을 수 없습니다."), - REFRESH_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, 4007, "리프레시 토큰이 만료되었습니다."), - - // 5000: File Error - FILE_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, 5001, "파일 업로드에 실패했습니다."), - FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, 5002, "파일 크기가 10MB를 초과합니다."), - INVALID_FILE_NAME(HttpStatus.BAD_REQUEST, 5003, "유효하지 않은 파일명입니다."), - INVALID_FILE_EXTENSION(HttpStatus.BAD_REQUEST, 5004, "지원하지 않는 파일 형식입니다. (jpg, jpeg, png, gif만 허용)"), - INVALID_FILE_TYPE(HttpStatus.BAD_REQUEST, 5005, "이미지 파일만 업로드 가능합니다."), - FILE_NOT_FOUND(HttpStatus.NOT_FOUND, 5006, "파일을 찾을 수 없습니다."); - - private final HttpStatus httpStatus; - private final int code; - private final String message; -} From 9ee3899110d4af5e51906d4acc32229456626da3 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:11:50 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[#2]=20feat:=20AuthException=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/exception/AuthErrorCode.java | 21 ++++++++ .../domain/auth/exception/AuthException.java | 52 +++++++++++++++++++ .../domain/auth/service/AuthService.java | 20 ++++--- .../domain/auth/service/AuthServiceTest.java | 19 +++---- 4 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 src/main/java/run/backend/domain/auth/exception/AuthErrorCode.java create mode 100644 src/main/java/run/backend/domain/auth/exception/AuthException.java diff --git a/src/main/java/run/backend/domain/auth/exception/AuthErrorCode.java b/src/main/java/run/backend/domain/auth/exception/AuthErrorCode.java new file mode 100644 index 0000000..23079ef --- /dev/null +++ b/src/main/java/run/backend/domain/auth/exception/AuthErrorCode.java @@ -0,0 +1,21 @@ +package run.backend.domain.auth.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import run.backend.global.exception.ErrorCode; + +@Getter +@AllArgsConstructor +public enum AuthErrorCode implements ErrorCode { + + INVALID_SIGNUP_TOKEN(3001, "유효하지 않은 가입 토큰입니다."), + USER_ALREADY_EXISTS(3002, "이미 가입된 사용자입니다."), + OAUTH_REQUEST_FAILED(3003, "외부 인증 서버와 통신 중 오류가 발생했습니다."), + TOKEN_MISSING_AUTHORITY(3004, "토큰에 권한 정보가 없습니다."), + INVALID_REFRESH_TOKEN(3005, "유효하지 않은 리프레시 토큰입니다."), + REFRESH_TOKEN_NOT_FOUND(3006, "리프레시 토큰을 찾을 수 없습니다."), + REFRESH_TOKEN_EXPIRED(3007, "리프레시 토큰이 만료되었습니다."); + + private final int errorCode; + private final String errorMessage; +} diff --git a/src/main/java/run/backend/domain/auth/exception/AuthException.java b/src/main/java/run/backend/domain/auth/exception/AuthException.java new file mode 100644 index 0000000..b1a8e5f --- /dev/null +++ b/src/main/java/run/backend/domain/auth/exception/AuthException.java @@ -0,0 +1,52 @@ +package run.backend.domain.auth.exception; + +import run.backend.global.exception.CustomException; + +public class AuthException extends CustomException { + + public AuthException(final AuthErrorCode authErrorCode) { + super(authErrorCode); + } + + public static class InvalidSignupToken extends AuthException { + public InvalidSignupToken() { + super(AuthErrorCode.INVALID_SIGNUP_TOKEN); + } + } + + public static class UserAlreadyExists extends AuthException { + public UserAlreadyExists() { + super(AuthErrorCode.USER_ALREADY_EXISTS); + } + } + + public static class OauthRequestFailed extends AuthException { + public OauthRequestFailed() { + super(AuthErrorCode.OAUTH_REQUEST_FAILED); + } + } + + public static class TokenMissingAuthority extends AuthException { + public TokenMissingAuthority() { + super(AuthErrorCode.TOKEN_MISSING_AUTHORITY); + } + } + + public static class InvalidRefreshToken extends AuthException { + public InvalidRefreshToken() { + super(AuthErrorCode.INVALID_REFRESH_TOKEN); + } + } + + public static class RefreshTokenNotFound extends AuthException { + public RefreshTokenNotFound() { + super(AuthErrorCode.REFRESH_TOKEN_NOT_FOUND); + } + } + + public static class RefreshTokenExpired extends AuthException { + public RefreshTokenExpired() { + super(AuthErrorCode.REFRESH_TOKEN_EXPIRED); + } + } +} diff --git a/src/main/java/run/backend/domain/auth/service/AuthService.java b/src/main/java/run/backend/domain/auth/service/AuthService.java index dbbfb03..21d1b02 100644 --- a/src/main/java/run/backend/domain/auth/service/AuthService.java +++ b/src/main/java/run/backend/domain/auth/service/AuthService.java @@ -24,13 +24,11 @@ import run.backend.domain.auth.dto.response.SignupResponse; import run.backend.domain.auth.dto.response.TokenResponse; import run.backend.domain.auth.entity.RefreshToken; +import run.backend.domain.auth.exception.AuthException; import run.backend.domain.auth.repository.RefreshTokenRepository; import run.backend.domain.member.entity.Member; import run.backend.domain.member.enums.OAuthType; -import run.backend.domain.member.enums.Role; import run.backend.domain.member.repository.MemberRepository; -import run.backend.global.exception.ApplicationException; -import run.backend.global.exception.ExceptionCode; import run.backend.global.oauth2.OAuth2UserInfo; import run.backend.global.oauth2.OAuth2UserInfoFactory; import run.backend.global.security.CustomUserDetails; @@ -76,7 +74,7 @@ public SignupResponse socialLogin(String providerName, String authorizationCode) public TokenResponse completeSignup(SignupRequest signupRequest, MultipartFile profileImage) { if (!jwtTokenProvider.validateToken(signupRequest.signupToken())) { - throw new ApplicationException(ExceptionCode.INVALID_SIGNUP_TOKEN); + throw new AuthException.InvalidSignupToken(); } Claims claims = jwtTokenProvider.parseClaims(signupRequest.signupToken()); @@ -86,7 +84,7 @@ public TokenResponse completeSignup(SignupRequest signupRequest, MultipartFile p String name = claims.get("name", String.class); memberRepository.findByOauthId(oauthId).ifPresent(m -> { - throw new ApplicationException(ExceptionCode.USER_ALREADY_EXISTS); + throw new AuthException.UserAlreadyExists(); }); String profileImageName = fileService.saveProfileImage(profileImage); @@ -129,7 +127,7 @@ private String getAccessToken(String authorizationCode, ClientRegistration provi ResponseEntity response = restTemplate.postForEntity(tokenUri, request, String.class); Map responseBody = objectMapper.readValue(response.getBody(), new TypeReference<>() {}); return (String) responseBody.get("access_token"); - } catch (Exception e) { throw new ApplicationException(ExceptionCode.OAUTH_REQUEST_FAILED); } + } catch (Exception e) { throw new AuthException.OauthRequestFailed(); } } private Map getUserAttributes(String accessToken, ClientRegistration provider) { @@ -140,21 +138,21 @@ private Map getUserAttributes(String accessToken, ClientRegistra try { ResponseEntity response = restTemplate.exchange(userInfoUri, HttpMethod.GET, request, String.class); return objectMapper.readValue(response.getBody(), new TypeReference<>() {}); - } catch (Exception e) { throw new ApplicationException(ExceptionCode.OAUTH_REQUEST_FAILED); } + } catch (Exception e) { throw new AuthException.OauthRequestFailed(); } } public TokenResponse refreshTokens(String authorizationCode) { String refreshToken = extractTokenFromHeader(authorizationCode); if (!jwtTokenProvider.validateToken(refreshToken)) { - throw new ApplicationException(ExceptionCode.INVALID_REFRESH_TOKEN); + throw new AuthException.InvalidRefreshToken(); } RefreshToken refreshTokenEntity = refreshTokenRepository.findByToken(refreshToken) - .orElseThrow(() -> new ApplicationException(ExceptionCode.REFRESH_TOKEN_NOT_FOUND)); + .orElseThrow(AuthException.RefreshTokenNotFound::new); if (refreshTokenEntity.isExpired()) { - throw new ApplicationException(ExceptionCode.REFRESH_TOKEN_EXPIRED); + throw new AuthException.RefreshTokenExpired(); } Member member = refreshTokenEntity.getMember(); @@ -180,7 +178,7 @@ private void saveRefreshToken(String refreshToken, Member member) { private String extractTokenFromHeader(String authorizationHeader) { if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { - throw new ApplicationException(ExceptionCode.INVALID_REFRESH_TOKEN); + throw new AuthException.InvalidRefreshToken(); } return authorizationHeader.substring(7); } diff --git a/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java b/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java index 828c058..0a4ae23 100644 --- a/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java +++ b/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java @@ -41,14 +41,13 @@ import run.backend.domain.auth.dto.request.SignupRequest; import run.backend.domain.auth.dto.response.SignupResponse; import run.backend.domain.auth.dto.response.TokenResponse; +import run.backend.domain.auth.exception.AuthException; import run.backend.domain.auth.repository.RefreshTokenRepository; import run.backend.domain.member.entity.Member; import run.backend.domain.member.enums.Gender; import run.backend.domain.member.enums.OAuthType; import run.backend.domain.member.enums.Role; import run.backend.domain.member.repository.MemberRepository; -import run.backend.global.exception.ApplicationException; -import run.backend.global.exception.ExceptionCode; import run.backend.global.oauth2.GoogleUserInfo; import run.backend.global.oauth2.OAuth2UserInfo; import run.backend.global.oauth2.OAuth2UserInfoFactory; @@ -257,9 +256,7 @@ void socialLogin_OAuthRequestFailed_ThrowsException() { // when & then assertThatThrownBy(() -> authService.socialLogin(providerName, authorizationCode)) - .isInstanceOf(ApplicationException.class) - .hasFieldOrPropertyWithValue("exceptionCode", ExceptionCode.OAUTH_REQUEST_FAILED); - } + .isInstanceOf(AuthException.OauthRequestFailed.class);} } @Nested @@ -322,8 +319,7 @@ void completeSignup_InvalidSignupToken_ThrowsException() { given(jwtTokenProvider.validateToken(invalidToken)).willReturn(false); assertThatThrownBy(() -> authService.completeSignup(signupRequest, null)) - .isInstanceOf(ApplicationException.class) - .hasFieldOrPropertyWithValue("exceptionCode", ExceptionCode.INVALID_SIGNUP_TOKEN); + .isInstanceOf(AuthException.InvalidSignupToken.class); verify(jwtTokenProvider).validateToken(invalidToken); verify(jwtTokenProvider, never()).parseClaims(anyString()); @@ -350,8 +346,7 @@ void completeSignup_UserAlreadyExists_ThrowsException() { // when & then assertThatThrownBy(() -> authService.completeSignup(signupRequest, null)) - .isInstanceOf(ApplicationException.class) - .hasFieldOrPropertyWithValue("exceptionCode", ExceptionCode.USER_ALREADY_EXISTS); + .isInstanceOf(AuthException.UserAlreadyExists.class); verify(jwtTokenProvider).validateToken(signupToken); verify(jwtTokenProvider).parseClaims(signupToken); @@ -376,8 +371,7 @@ void socialLogin_NullAuthorizationCode_ThrowsException() { // when & then assertThatThrownBy(() -> authService.socialLogin(providerName, authorizationCode)) - .isInstanceOf(ApplicationException.class) - .hasFieldOrPropertyWithValue("exceptionCode", ExceptionCode.OAUTH_REQUEST_FAILED); + .isInstanceOf(AuthException.OauthRequestFailed.class); } @Test @@ -395,8 +389,7 @@ void completeSignup_NullToken_ThrowsException() { // when & then assertThatThrownBy(() -> authService.completeSignup(signupRequest, null)) - .isInstanceOf(ApplicationException.class) - .hasFieldOrPropertyWithValue("exceptionCode", ExceptionCode.INVALID_SIGNUP_TOKEN); + .isInstanceOf(AuthException.InvalidSignupToken.class); } } From b5ec672a99d51661145090202413166c76b1e734 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:12:06 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[#2]=20feat:=20FileException=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/file/exception/FileErrorCode.java | 20 ++++++++ .../domain/file/exception/FileException.java | 46 +++++++++++++++++++ .../domain/file/service/FileService.java | 21 ++++----- 3 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 src/main/java/run/backend/domain/file/exception/FileErrorCode.java create mode 100644 src/main/java/run/backend/domain/file/exception/FileException.java diff --git a/src/main/java/run/backend/domain/file/exception/FileErrorCode.java b/src/main/java/run/backend/domain/file/exception/FileErrorCode.java new file mode 100644 index 0000000..1452b3e --- /dev/null +++ b/src/main/java/run/backend/domain/file/exception/FileErrorCode.java @@ -0,0 +1,20 @@ +package run.backend.domain.file.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import run.backend.global.exception.ErrorCode; + +@Getter +@AllArgsConstructor +public enum FileErrorCode implements ErrorCode { + + FILE_UPLOAD_FAILED(4001, "파일 업로드에 실패했습니다."), + FILE_SIZE_EXCEEDED(4002, "파일 크기가 10MB를 초과합니다."), + INVALID_FILE_NAME(4003, "유효하지 않은 파일명입니다."), + INVALID_FILE_EXTENSION(4004, "지원하지 않는 파일 형식입니다. (jpg, jpeg, png, gif만 허용)"), + INVALID_FILE_TYPE(4005, "이미지 파일만 업로드 가능합니다."), + FILE_NOT_FOUND(4006, "파일을 찾을 수 없습니다."); + + private final int errorCode; + private final String errorMessage; +} diff --git a/src/main/java/run/backend/domain/file/exception/FileException.java b/src/main/java/run/backend/domain/file/exception/FileException.java new file mode 100644 index 0000000..6e3efc7 --- /dev/null +++ b/src/main/java/run/backend/domain/file/exception/FileException.java @@ -0,0 +1,46 @@ +package run.backend.domain.file.exception; + +import run.backend.global.exception.CustomException; + +public class FileException extends CustomException { + + public FileException(final FileErrorCode fileErrorCode) { + super(fileErrorCode); + } + + public static class FileUploadFailed extends FileException { + public FileUploadFailed() { + super(FileErrorCode.FILE_UPLOAD_FAILED); + } + } + + public static class FileSizeExceeded extends FileException { + public FileSizeExceeded() { + super(FileErrorCode.FILE_SIZE_EXCEEDED); + } + } + + public static class InvalidFileName extends FileException { + public InvalidFileName() { + super(FileErrorCode.INVALID_FILE_NAME); + } + } + + public static class InvalidFileExtension extends FileException { + public InvalidFileExtension() { + super(FileErrorCode.INVALID_FILE_EXTENSION); + } + } + + public static class InvalidFileType extends FileException { + public InvalidFileType() { + super(FileErrorCode.INVALID_FILE_TYPE); + } + } + + public static class FileNotFound extends FileException { + public FileNotFound() { + super(FileErrorCode.FILE_NOT_FOUND); + } + } +} diff --git a/src/main/java/run/backend/domain/file/service/FileService.java b/src/main/java/run/backend/domain/file/service/FileService.java index d14ab42..0bc87c3 100644 --- a/src/main/java/run/backend/domain/file/service/FileService.java +++ b/src/main/java/run/backend/domain/file/service/FileService.java @@ -6,8 +6,7 @@ import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import run.backend.global.exception.ApplicationException; -import run.backend.global.exception.ExceptionCode; +import run.backend.domain.file.exception.FileException; import java.io.IOException; import java.net.MalformedURLException; @@ -53,7 +52,7 @@ public String saveProfileImage(MultipartFile file) { return newFilename; } catch (IOException e) { - throw new ApplicationException(ExceptionCode.FILE_UPLOAD_FAILED); + throw new FileException.FileUploadFailed(); } } @@ -67,13 +66,13 @@ public Resource getFileResource(String filename) { Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists() || !resource.isReadable()) { - throw new ApplicationException(ExceptionCode.FILE_NOT_FOUND); + throw new FileException.FileNotFound(); } return resource; } catch (MalformedURLException e) { - throw new ApplicationException(ExceptionCode.FILE_NOT_FOUND); + throw new FileException.FileNotFound(); } } @@ -94,32 +93,32 @@ public String getContentType(String filename) { private void validateFile(MultipartFile file) { if (file.getSize() > MAX_FILE_SIZE) { - throw new ApplicationException(ExceptionCode.FILE_SIZE_EXCEEDED); + throw new FileException.FileSizeExceeded(); } String contentType = file.getContentType(); if (contentType == null || !contentType.startsWith("image/")) { - throw new ApplicationException(ExceptionCode.INVALID_FILE_TYPE); + throw new FileException.InvalidFileType(); } String originalFilename = file.getOriginalFilename(); if (originalFilename == null || originalFilename.isEmpty()) { - throw new ApplicationException(ExceptionCode.INVALID_FILE_NAME); + throw new FileException.InvalidFileName(); } String extension = getFileExtension(originalFilename); if (!ALLOWED_EXTENSIONS.contains(extension.toLowerCase())) { - throw new ApplicationException(ExceptionCode.INVALID_FILE_EXTENSION); + throw new FileException.InvalidFileExtension(); } } private void validateFilename(String filename) { if (filename == null || filename.trim().isEmpty()) { - throw new ApplicationException(ExceptionCode.INVALID_FILE_NAME); + throw new FileException.InvalidFileName(); } if (!filename.matches("^[a-fA-F0-9-]+\\.(jpg|jpeg|png|gif)$")) { - throw new ApplicationException(ExceptionCode.INVALID_FILE_NAME); + throw new FileException.InvalidFileName(); } } From 1a329db84cfc7e67a3a45d508c9337e5ca7ca02c Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:12:20 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[#2]=20feat:=20ErrorCode=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/run/backend/global/exception/ErrorCode.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/run/backend/global/exception/ErrorCode.java diff --git a/src/main/java/run/backend/global/exception/ErrorCode.java b/src/main/java/run/backend/global/exception/ErrorCode.java new file mode 100644 index 0000000..1e35bf7 --- /dev/null +++ b/src/main/java/run/backend/global/exception/ErrorCode.java @@ -0,0 +1,7 @@ +package run.backend.global.exception; + +public interface ErrorCode { + + int getErrorCode(); + String getErrorMessage(); +} From 77c7ef14893999d421f707d81a6388948570e9c3 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:12:51 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[#2]=20feat:=20HttpErrorCode=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/httpError/HttpErrorCode.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/run/backend/global/exception/httpError/HttpErrorCode.java diff --git a/src/main/java/run/backend/global/exception/httpError/HttpErrorCode.java b/src/main/java/run/backend/global/exception/httpError/HttpErrorCode.java new file mode 100644 index 0000000..8947be1 --- /dev/null +++ b/src/main/java/run/backend/global/exception/httpError/HttpErrorCode.java @@ -0,0 +1,15 @@ +package run.backend.global.exception.httpError; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import run.backend.global.exception.ErrorCode; + +@Getter +@AllArgsConstructor +public enum HttpErrorCode implements ErrorCode { + + INTERNAL_SERVER_ERROR(2001, "요청 처리 중 알 수 없는 오류가 발생했습니다."); + + private final int errorCode; + private final String errorMessage; +} From 86204274a2d0967a265086696cff7bedcea99e32 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:13:08 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[#2]=20feat:=20CustomException=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/CustomException.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/run/backend/global/exception/CustomException.java diff --git a/src/main/java/run/backend/global/exception/CustomException.java b/src/main/java/run/backend/global/exception/CustomException.java new file mode 100644 index 0000000..87366b2 --- /dev/null +++ b/src/main/java/run/backend/global/exception/CustomException.java @@ -0,0 +1,23 @@ +package run.backend.global.exception; + +import lombok.Getter; + +@Getter +public class CustomException extends RuntimeException { + + private final int errorCode; + private final String errorMessage; + + public & ErrorCode> CustomException(final T errorType) { + errorCode = errorType.getErrorCode(); + errorMessage = errorType.getErrorMessage(); + } + + @Override + public String toString() { + + return String.format("errorCode=%d | errorMessage=%s", + errorCode, + errorMessage ); + } +} From 0a7cb4c93871acdf18a62bef838cfbd17442c3f8 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:13:28 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[#2]=20fix:=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../run/backend/global/common/response/CommonResponse.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/run/backend/global/common/response/CommonResponse.java b/src/main/java/run/backend/global/common/response/CommonResponse.java index f4954af..c8d09b5 100644 --- a/src/main/java/run/backend/global/common/response/CommonResponse.java +++ b/src/main/java/run/backend/global/common/response/CommonResponse.java @@ -1,7 +1,7 @@ package run.backend.global.common.response; import com.fasterxml.jackson.annotation.JsonFormat; -import run.backend.global.exception.ExceptionCode; +import run.backend.global.exception.ErrorCode; import java.time.LocalDateTime; @@ -24,7 +24,7 @@ public CommonResponse(String message) { } // api 응답이 실패인 경우 - public CommonResponse(ExceptionCode exceptionCode) { - this(LocalDateTime.now(), exceptionCode.getCode(), exceptionCode.getMessage(), null); + public CommonResponse(int errorCode, String errorMessage) { + this(LocalDateTime.now(), errorCode, errorMessage, null); } } \ No newline at end of file From 5df99035e17d7724a1c786c1b4cae7722e74ce99 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:13:59 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[#2]=20feat:=20=EC=A0=84=EC=97=AD=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=EA=B8=B0=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=B6=84=EB=A5=98=20=EC=84=B8=EB=B6=84=ED=99=94,?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=20=ED=8F=AC=EB=A7=B7=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/src/main/java/run/backend/global/exception/GlobalExceptionHandler.java b/src/main/java/run/backend/global/exception/GlobalExceptionHandler.java index 44fc549..f569aa4 100644 --- a/src/main/java/run/backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/run/backend/global/exception/GlobalExceptionHandler.java @@ -1,66 +1,74 @@ package run.backend.global.exception; -import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import run.backend.domain.auth.exception.AuthException; +import run.backend.domain.file.exception.FileException; import run.backend.global.common.response.CommonResponse; - -import static run.backend.global.exception.ExceptionCode.INTERNAL_SERVER_ERROR; +import run.backend.global.exception.httpError.HttpErrorCode; @Slf4j @RestControllerAdvice @RequiredArgsConstructor public class GlobalExceptionHandler { - @ExceptionHandler(ApplicationException.class) - protected ResponseEntity> handleApplicationException(ApplicationException e, HttpServletRequest request){ + @ExceptionHandler({ + AuthException.RefreshTokenNotFound.class, + FileException.FileNotFound.class + }) + public ResponseEntity> handleNotFound(final CustomException e) { - applicationLogFormat(e, request); + log.warn("[NOT_FOUND_EXCEPTION] {}", e.toString()); - return ResponseEntity.status(e.getExceptionCode().getHttpStatus()) - .body(new CommonResponse(e.getExceptionCode())); + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(new CommonResponse<>(e.getErrorCode(), e.getErrorMessage())); } - @ExceptionHandler(Exception.class) - public ResponseEntity> handleException(Exception e, HttpServletRequest request){ + @ExceptionHandler({ + AuthException.UserAlreadyExists.class, + FileException.FileSizeExceeded.class, + FileException.InvalidFileName.class, + FileException.InvalidFileExtension.class, + FileException.InvalidFileType.class + }) + public ResponseEntity> handleConflict(final CustomException e) { - logFormat(e, request); + log.warn("[CONFLICT_EXCEPTION] {}", e.toString()); - return ResponseEntity.internalServerError() - .body(new CommonResponse(INTERNAL_SERVER_ERROR)); + return ResponseEntity + .status(HttpStatus.CONFLICT) + .body(new CommonResponse<>(e.getErrorCode(), e.getErrorMessage())); } - private void applicationLogFormat(ApplicationException e, HttpServletRequest request) { + @ExceptionHandler({ + AuthException.InvalidSignupToken.class, + AuthException.OauthRequestFailed.class, + AuthException.TokenMissingAuthority.class, + AuthException.InvalidRefreshToken.class, + AuthException.RefreshTokenExpired.class, + FileException.FileUploadFailed.class + }) + public ResponseEntity> handleBadRequest(final CustomException e) { + + log.warn("[BAD_REQUEST_EXCEPTION] {}", e.toString()); - log.warn( - "\n[{} 발생]\n" + - "exception code: {}\n" + - "uri: {}\n" + - "method: {}\n" + - "message: {}\n", - e.getExceptionCode().name(), - e.getExceptionCode().getCode(), - e.getMessage(), - request.getRequestURI(), - request.getMethod(), - e - ); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(new CommonResponse<>(e.getErrorCode(), e.getErrorMessage())); } - private void logFormat(Exception e, HttpServletRequest request) { + @ExceptionHandler(Exception.class) + public ResponseEntity> handleUnknownException(final Exception e) { + + log.error("[INTERNAL_SERVER_ERROR]", e); - log.error( - "\n[Exception 발생]\n" + - "uri: {}\n" + - "method: {}\n" + - "message: {}\n", - request.getRequestURI(), - request.getMethod(), - e.getMessage(), - e - ); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new CommonResponse<>(HttpErrorCode.INTERNAL_SERVER_ERROR.getErrorCode(), HttpErrorCode.INTERNAL_SERVER_ERROR.getErrorMessage())); } } From 8019235a8d06e2194ff7353726145ac6a2e95486 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sat, 12 Jul 2025 00:14:17 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[#2]=20feat:=20AuthException=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/run/backend/global/util/JwtTokenProvider.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/run/backend/global/util/JwtTokenProvider.java b/src/main/java/run/backend/global/util/JwtTokenProvider.java index 42a8339..7760b00 100644 --- a/src/main/java/run/backend/global/util/JwtTokenProvider.java +++ b/src/main/java/run/backend/global/util/JwtTokenProvider.java @@ -25,8 +25,7 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import run.backend.domain.auth.dto.response.TokenResponse; -import run.backend.global.exception.ApplicationException; -import run.backend.global.exception.ExceptionCode; +import run.backend.domain.auth.exception.AuthException; @Slf4j @Component @@ -74,7 +73,7 @@ public Authentication getAuthentication(String accessToken) { Claims claims = parseClaims(accessToken); if (claims.get("auth") == null) { - throw new ApplicationException(ExceptionCode.TOKEN_MISSING_AUTHORITY); + throw new AuthException.TokenMissingAuthority(); } Collection authorities =