From fdb617d8d555e44f9ab36063de509e584d84b745 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:18:00 +0900 Subject: [PATCH 1/8] feat: Error default setting (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ErrorType, ApiException 초안 --- .../global/exception/ApiException.java | 15 ++++++++ .../TaveShot/global/exception/ErrorType.java | 35 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/main/java/com/api/TaveShot/global/exception/ApiException.java create mode 100644 src/main/java/com/api/TaveShot/global/exception/ErrorType.java diff --git a/src/main/java/com/api/TaveShot/global/exception/ApiException.java b/src/main/java/com/api/TaveShot/global/exception/ApiException.java new file mode 100644 index 0000000..3550413 --- /dev/null +++ b/src/main/java/com/api/TaveShot/global/exception/ApiException.java @@ -0,0 +1,15 @@ +package com.api.TaveShot.global.exception; + +public class ApiException extends RuntimeException { + + private final ErrorType errorType; + + public ApiException(ErrorType errorType) { + super(errorType.getMessage()); + this.errorType = errorType; + } + + public ErrorType getErrorType() { + return errorType; + } +} diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java new file mode 100644 index 0000000..f1ac809 --- /dev/null +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -0,0 +1,35 @@ +package com.api.TaveShot.global.exception; + +import lombok.Getter; +import lombok.ToString; +import org.springframework.http.HttpStatus; + +@Getter +@ToString +public enum ErrorType { + + // 각종 에러들 + ; + + private final HttpStatus status; + private final String errorCode; + private final String message; + + ErrorType(HttpStatus status, String errorCode, String message) { + this.status = status; + this.errorCode = errorCode; + this.message = message; + } + + public HttpStatus getStatus() { + return status; + } + + public String getErrorCode() { + return errorCode; + } + + public String getMessage() { + return message; + } +} From 33a4459aaae2575795ba5fb6b807925bcf3f3450 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:19:53 +0900 Subject: [PATCH 2/8] feat: ExceptionHandler, ApiExceptionResponse (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 에러 핸들러, 에러 응답 클래스 추가 --- .../global/exception/ApiExceptionHandler.java | 20 +++++++++++++++++++ .../exception/ApiExceptionResponse.java | 17 ++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/com/api/TaveShot/global/exception/ApiExceptionHandler.java create mode 100644 src/main/java/com/api/TaveShot/global/exception/ApiExceptionResponse.java diff --git a/src/main/java/com/api/TaveShot/global/exception/ApiExceptionHandler.java b/src/main/java/com/api/TaveShot/global/exception/ApiExceptionHandler.java new file mode 100644 index 0000000..32bc279 --- /dev/null +++ b/src/main/java/com/api/TaveShot/global/exception/ApiExceptionHandler.java @@ -0,0 +1,20 @@ +package com.api.TaveShot.global.exception; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ApiExceptionHandler { + + @ExceptionHandler({ApiException.class}) + public ResponseEntity handleApiException(ApiException e) { + ErrorType errorType = e.getErrorType(); + ApiExceptionResponse response = new ApiExceptionResponse( + errorType.getStatus().value(), + errorType.getErrorCode(), + errorType.getMessage() + ); + return ResponseEntity.status(errorType.getStatus()).body(response); + } +} diff --git a/src/main/java/com/api/TaveShot/global/exception/ApiExceptionResponse.java b/src/main/java/com/api/TaveShot/global/exception/ApiExceptionResponse.java new file mode 100644 index 0000000..e268d59 --- /dev/null +++ b/src/main/java/com/api/TaveShot/global/exception/ApiExceptionResponse.java @@ -0,0 +1,17 @@ +package com.api.TaveShot.global.exception; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class ApiExceptionResponse { + + private int status; + + private String errorCode; + + private String message; +} From 05fbc10b9f02174642479610ad73a0f81f6e3227 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:28:49 +0900 Subject: [PATCH 3/8] feat: Error Message Convention (#9) --- .../com/api/TaveShot/global/exception/ErrorType.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index f1ac809..2ab0ada 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -8,6 +8,18 @@ @ToString public enum ErrorType { + /** + * Error Message Convention + * status : HttpStatus + * + * errorCode : 400번 오류인 상황이 여러개 올텐데, 4001, 4002, 4003.. 이런식으로 설정 (해당 오류들은 MEMBER 와 관련된 400 오류들) + * ex) Member Error, Http Status Code: 400 -> MEMBER_4000 + * + * message : 사람이 알아볼 수 있도록 작성 + * ex) "인증이 필요합니다." + */ + + // 각종 에러들 ; From 4f88cd44fd6deae0db45fdfb2e720a1e83b753f7 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:34:16 +0900 Subject: [PATCH 4/8] feat: ErrorType _CANT_TRANCE_INSTANCE (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 인스턴스화 불가 예외 추가 --- .../com/api/TaveShot/global/constant/OauthConstant.java | 6 +++++- .../java/com/api/TaveShot/global/exception/ErrorType.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/api/TaveShot/global/constant/OauthConstant.java b/src/main/java/com/api/TaveShot/global/constant/OauthConstant.java index 0f384ad..96b3950 100644 --- a/src/main/java/com/api/TaveShot/global/constant/OauthConstant.java +++ b/src/main/java/com/api/TaveShot/global/constant/OauthConstant.java @@ -1,9 +1,13 @@ package com.api.TaveShot.global.constant; +import static com.api.TaveShot.global.exception.ErrorType._CANT_TRANCE_INSTANCE; + +import com.api.TaveShot.global.exception.ApiException; + public final class OauthConstant { private OauthConstant() { - throw new IllegalArgumentException("인스턴스화 불가"); + throw new ApiException(_CANT_TRANCE_INSTANCE); } public static final String ID_PATTERN = "id"; diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index 2ab0ada..bda6809 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -1,5 +1,7 @@ package com.api.TaveShot.global.exception; +import static org.springframework.http.HttpStatus.*; + import lombok.Getter; import lombok.ToString; import org.springframework.http.HttpStatus; @@ -10,7 +12,8 @@ public enum ErrorType { /** * Error Message Convention - * status : HttpStatus + * + * name : _(head) + Error Name status : HttpStatus * * errorCode : 400번 오류인 상황이 여러개 올텐데, 4001, 4002, 4003.. 이런식으로 설정 (해당 오류들은 MEMBER 와 관련된 400 오류들) * ex) Member Error, Http Status Code: 400 -> MEMBER_4000 @@ -19,6 +22,7 @@ public enum ErrorType { * ex) "인증이 필요합니다." */ + _CANT_TRANCE_INSTANCE(INTERNAL_SERVER_ERROR, "SERVER_5000", "상수는 인스턴스화 할 수 없습니다."), // 각종 에러들 ; From 04a36b73a54b0c13bed5dee19f396e8037701a32 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:37:48 +0900 Subject: [PATCH 5/8] feat: ErrorType _SERVER_USER_NOT_FOUND (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인이 성공한 소셜 로그인 유저가 DB에 존재하지 않는 서버의 예외 추가 --- .../java/com/api/TaveShot/global/exception/ErrorType.java | 2 +- .../TaveShot/global/oauth2/CustomOAuthSuccessHandler.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index bda6809..0cdf758 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -23,7 +23,7 @@ public enum ErrorType { */ _CANT_TRANCE_INSTANCE(INTERNAL_SERVER_ERROR, "SERVER_5000", "상수는 인스턴스화 할 수 없습니다."), - + _SERVER_USER_NOT_FOUND(INTERNAL_SERVER_ERROR, "SERVER_5001", "로그인이 성공한 소셜 로그인 유저가 DB에 존재하지 않을 수 없습니다.") // 각종 에러들 ; diff --git a/src/main/java/com/api/TaveShot/global/oauth2/CustomOAuthSuccessHandler.java b/src/main/java/com/api/TaveShot/global/oauth2/CustomOAuthSuccessHandler.java index a486f71..749aa60 100644 --- a/src/main/java/com/api/TaveShot/global/oauth2/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/api/TaveShot/global/oauth2/CustomOAuthSuccessHandler.java @@ -1,10 +1,12 @@ package com.api.TaveShot.global.oauth2; import static com.api.TaveShot.global.constant.OauthConstant.REDIRECT_URL; +import static com.api.TaveShot.global.exception.ErrorType._SERVER_USER_NOT_FOUND; import com.api.TaveShot.domain.Member.domain.Member; import com.api.TaveShot.domain.Member.dto.response.AuthResponse; import com.api.TaveShot.domain.Member.repository.MemberRepository; +import com.api.TaveShot.global.exception.ApiException; import com.api.TaveShot.global.jwt.JwtProvider; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -45,7 +47,8 @@ public void onAuthenticationSuccess(final HttpServletRequest request, final Http String mail = githubUserInfo.getMail(); String profileImageUrl = githubUserInfo.getProfileImageUrl(); - Member loginMember = memberRepository.findByGitId(Long.valueOf(id)).orElseThrow(() -> new RuntimeException("해당 gitId로 회원을 찾을 수 없음")); + Member loginMember = memberRepository.findByGitId(Long.valueOf(id)).orElseThrow( + () -> new ApiException(_SERVER_USER_NOT_FOUND)); String loginMemberId = String.valueOf(loginMember.getId()); registerHeaderToken(response, loginMemberId); From 1b6469fd02dfd326d83b2a11a3256082185cee75 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:45:39 +0900 Subject: [PATCH 6/8] feat: ErrorType _TOKEN_EXPIRED (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jwt Token 유효 기간 만료 예외 추가 --- .../java/com/api/TaveShot/global/exception/ErrorType.java | 3 ++- src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index 0cdf758..a6befe0 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -23,7 +23,8 @@ public enum ErrorType { */ _CANT_TRANCE_INSTANCE(INTERNAL_SERVER_ERROR, "SERVER_5000", "상수는 인스턴스화 할 수 없습니다."), - _SERVER_USER_NOT_FOUND(INTERNAL_SERVER_ERROR, "SERVER_5001", "로그인이 성공한 소셜 로그인 유저가 DB에 존재하지 않을 수 없습니다.") + _SERVER_USER_NOT_FOUND(INTERNAL_SERVER_ERROR, "SERVER_5001", "로그인이 성공한 소셜 로그인 유저가 DB에 존재하지 않을 수 없습니다."), + _TOKEN_EXPIRED(UNAUTHORIZED, "JWT_4010", "Jwt Token의 유효 기간이 만료되었습니다.") // 각종 에러들 ; diff --git a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java index 0eb0001..fe0c1e9 100644 --- a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java +++ b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java @@ -1,8 +1,10 @@ package com.api.TaveShot.global.jwt; import static com.api.TaveShot.global.constant.OauthConstant.ACCESS_TOKEN_VALID_TIME; +import static com.api.TaveShot.global.exception.ErrorType._TOKEN_EXPIRED; import com.api.TaveShot.domain.Member.repository.MemberRepository; +import com.api.TaveShot.global.exception.ApiException; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -65,7 +67,7 @@ public void isValidToken(final String jwtToken) { .parseClaimsJws(jwtToken); } catch (ExpiredJwtException e) { // 어세스 토큰 만료 - throw new IllegalArgumentException("Access Token expired"); + throw new ApiException(_TOKEN_EXPIRED); } catch (Exception e) { throw new IllegalArgumentException("User Not Authorized"); } From 0a840dbcc36063ff6ebb3296fedb8b5fb7731dd0 Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:55:22 +0900 Subject: [PATCH 7/8] feat: ErrorType _JWT_PARSING_ERROR (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jwt Parsing 예외 추가 --- .../api/TaveShot/global/exception/ErrorType.java | 13 +++++++++++-- .../com/api/TaveShot/global/jwt/JwtProvider.java | 7 ++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index a6befe0..b2939a7 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -22,10 +22,19 @@ public enum ErrorType { * ex) "인증이 필요합니다." */ + // ------------------------------------------ SERVER ------------------------------------------ _CANT_TRANCE_INSTANCE(INTERNAL_SERVER_ERROR, "SERVER_5000", "상수는 인스턴스화 할 수 없습니다."), _SERVER_USER_NOT_FOUND(INTERNAL_SERVER_ERROR, "SERVER_5001", "로그인이 성공한 소셜 로그인 유저가 DB에 존재하지 않을 수 없습니다."), - _TOKEN_EXPIRED(UNAUTHORIZED, "JWT_4010", "Jwt Token의 유효 기간이 만료되었습니다.") - // 각종 에러들 + + + // ---------------------------------------- JWT TOKEN ---------------------------------------- + _JWT_PARSING_ERROR(BAD_REQUEST, "JWT_4001", "JWT 토큰 파싱 중 오류가 발생했습니다."), + _JWT_EXPIRED(UNAUTHORIZED, "JWT_4010", "Jwt Token의 유효 기간이 만료되었습니다."), + + + + // ------------------------------------------ USER ------------------------------------------ + ; private final HttpStatus status; diff --git a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java index fe0c1e9..f702017 100644 --- a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java +++ b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java @@ -1,7 +1,8 @@ package com.api.TaveShot.global.jwt; import static com.api.TaveShot.global.constant.OauthConstant.ACCESS_TOKEN_VALID_TIME; -import static com.api.TaveShot.global.exception.ErrorType._TOKEN_EXPIRED; +import static com.api.TaveShot.global.exception.ErrorType._JWT_EXPIRED; +import static com.api.TaveShot.global.exception.ErrorType._JWT_PARSING_ERROR; import com.api.TaveShot.domain.Member.repository.MemberRepository; import com.api.TaveShot.global.exception.ApiException; @@ -67,9 +68,9 @@ public void isValidToken(final String jwtToken) { .parseClaimsJws(jwtToken); } catch (ExpiredJwtException e) { // 어세스 토큰 만료 - throw new ApiException(_TOKEN_EXPIRED); + throw new ApiException(_JWT_EXPIRED); } catch (Exception e) { - throw new IllegalArgumentException("User Not Authorized"); + throw new ApiException(_JWT_PARSING_ERROR); } } From c10dc68a45fa95fd3065bbe16e1e49e0daa6aeaf Mon Sep 17 00:00:00 2001 From: toychip Date: Thu, 30 Nov 2023 01:58:36 +0900 Subject: [PATCH 8/8] feat: ErrorType _USER_NOT_FOUND (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 제공된 토큰으로 사용자가 없을 때의 예외 추가 --- .../java/com/api/TaveShot/global/exception/ErrorType.java | 1 + src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java index b2939a7..68f64ee 100644 --- a/src/main/java/com/api/TaveShot/global/exception/ErrorType.java +++ b/src/main/java/com/api/TaveShot/global/exception/ErrorType.java @@ -34,6 +34,7 @@ public enum ErrorType { // ------------------------------------------ USER ------------------------------------------ + _USER_NOT_FOUND(NOT_FOUND, "USER_4040", "제공된 토큰으로 사용자를 찾을 수 없습니다.") ; diff --git a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java index f702017..cb32ea0 100644 --- a/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java +++ b/src/main/java/com/api/TaveShot/global/jwt/JwtProvider.java @@ -3,6 +3,7 @@ import static com.api.TaveShot.global.constant.OauthConstant.ACCESS_TOKEN_VALID_TIME; import static com.api.TaveShot.global.exception.ErrorType._JWT_EXPIRED; import static com.api.TaveShot.global.exception.ErrorType._JWT_PARSING_ERROR; +import static com.api.TaveShot.global.exception.ErrorType._USER_NOT_FOUND; import com.api.TaveShot.domain.Member.repository.MemberRepository; import com.api.TaveShot.global.exception.ApiException; @@ -85,7 +86,8 @@ public void getAuthenticationFromToken(final String jwtToken) { // token 으로부터 유저 정보 확인 private void getGitLoginId(final String jwtToken) { Long userId = Long.valueOf(getUserIdFromToken(jwtToken)); - memberRepository.findById(userId).orElseThrow(() -> new RuntimeException("token 으로 Member를 찾을 수 없음")); + memberRepository.findById(userId) + .orElseThrow(() -> new ApiException(_USER_NOT_FOUND)); } // 토큰에서 유저 아이디 얻기