Skip to content

Commit

Permalink
feat: 예외 처리 Advice 구현 #8
Browse files Browse the repository at this point in the history
  • Loading branch information
PgmJun committed Jan 19, 2024
1 parent bfbec9a commit 88d6c50
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.nice.petudio.domain.member.repository.MemberRepository;
import com.nice.petudio.global.auth.jwt.JwtTokenService;
import com.nice.petudio.global.config.redis.constant.RedisKey;
import com.nice.petudio.global.exception.UnAuthorizedException;
import com.nice.petudio.global.exception.model.UnAuthorizedException;
import com.nice.petudio.global.exception.error.ErrorCode;
import java.util.List;
import java.util.Objects;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import com.nice.petudio.domain.member.Member;
import com.nice.petudio.domain.member.SocialType;
import com.nice.petudio.domain.member.repository.MemberRepository;
import com.nice.petudio.global.exception.ConflictException;
import com.nice.petudio.global.exception.NotFoundException;
import com.nice.petudio.global.exception.model.ConflictException;
import com.nice.petudio.global.exception.model.NotFoundException;
import com.nice.petudio.global.exception.error.ErrorCode;
import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.nice.petudio.external.client.auth.kakao;

import com.nice.petudio.external.client.auth.kakao.dto.response.KakaoProfileResponse;
import com.nice.petudio.global.exception.BadGatewayException;
import com.nice.petudio.global.exception.ValidationException;
import com.nice.petudio.global.exception.model.BadGatewayException;
import com.nice.petudio.global.exception.model.ValidationException;
import com.nice.petudio.global.exception.error.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatusCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import com.nice.petudio.domain.member.MemberRole;
import com.nice.petudio.domain.member.repository.MemberRepository;
import com.nice.petudio.global.auth.jwt.JwtTokenService;
import com.nice.petudio.global.exception.ForbiddenException;
import com.nice.petudio.global.exception.UnAuthorizedException;
import com.nice.petudio.global.exception.ValidationException;
import com.nice.petudio.global.exception.model.ForbiddenException;
import com.nice.petudio.global.exception.model.UnAuthorizedException;
import com.nice.petudio.global.exception.model.ValidationException;
import com.nice.petudio.global.exception.error.ErrorCode;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
Expand All @@ -33,7 +33,7 @@ public Long validateAuthority(HttpServletRequest request, List<MemberRole> requi
return memberId;
}
throw new ForbiddenException(ErrorCode.FORBIDDEN_EXCEPTION,
String.format("memberId(%d) 접근 권한이 없어, 요청이 수행되지 않았습니다.", memberId));
String.format("memberId(%d) 접근 권한이 없어, 요청이 수행되지 않았습니다.", memberId));
}

private String getJwtAccessTokenFromHttpHeader(HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import static com.nice.petudio.global.auth.auth.AuthInterceptor.MEMBER_ID;

import com.nice.petudio.global.auth.auth.Auth;
import com.nice.petudio.global.exception.InternalServerException;
import com.nice.petudio.global.exception.model.InternalServerException;
import com.nice.petudio.global.exception.error.ErrorCode;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
Expand All @@ -22,8 +23,8 @@ public boolean supportsParameter(MethodParameter parameter) {
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
Optional<Auth> auth = Optional.ofNullable(parameter.getMethodAnnotation(Auth.class));
if (auth.isEmpty()) {
throw new InternalServerException(ErrorCode.INTERNAL_SERVER_EXCEPTION,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.nice.petudio.global.exception;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.nice.petudio.api.dto.ApiResponse;
import com.nice.petudio.global.exception.error.ErrorCode;
import com.nice.petudio.global.exception.model.BadGatewayException;
import com.nice.petudio.global.exception.model.InternalServerException;
import com.nice.petudio.global.exception.model.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@Slf4j
@RestControllerAdvice
public class ExceptionController {

// 사용자가 요청 값 전달은 성공했지만, 해당 값이 유효하지 않은 경우 발생
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ValidationException.class)
protected ApiResponse<Object> handleValidateException(ValidationException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(exception.getErrorCode());
}

// @Valid or @Validated 바인딩 에러시 발생
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ApiResponse<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.BAD_REQUEST_EXCEPTION);
}

// API 메서드의 매개변수와 사용자 요청 값의 Type을 매핑할 수 없는 경우에 발생
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ApiResponse<Object> handleMethodArgumentTypeMismatchException(
MethodArgumentTypeMismatchException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.BAD_REQUEST_EXCEPTION);
}

// HTTP 메시지를 읽을 수 없는 경우, 데이터의 형식이 유효한 자바 타입으로 변환되지 못할 경우, 서블릿 요청과 관련된 바인딩 오류가 발생한 경우
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({
HttpMessageNotReadableException.class,
InvalidFormatException.class,
ServletRequestBindingException.class
})
protected ApiResponse<Object> handleInvalidFormatException(final Exception exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.BAD_REQUEST_EXCEPTION);
}


/**
* 405 Method Not Supported
*/
// 해당 요청에 대해 지원하는 메서드가 존재하지 않는 경우 발생
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
protected ApiResponse<Object> handleHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.METHOD_NOT_ALLOWED_EXCEPTION);
}


/**
* 415 UnSupported Media Type
*/
// 사용자가 보낸 요청의 미디어 타입을 서버에서 지원하는 않는 경우 발생
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler(HttpMediaTypeException.class)
protected ApiResponse<Object> handleHttpMediaTypeException(final HttpMediaTypeException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.UNSUPPORTED_MEDIA_TYPE);
}


/**
* 502 Bad Gateway
*/
// 서버 내에서 외부 요청 수행 중, 문제가 발생한 경우에 발생
@ResponseStatus(HttpStatus.BAD_GATEWAY)
@ExceptionHandler(BadGatewayException.class)
protected ApiResponse<Object> handleBadGatewayException(final BadGatewayException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(exception.getErrorCode());
}


/**
* 500 Internal Server Error
*/
// 서버 내부에서 오류가 발생한 경우
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(InternalServerException.class)
protected ApiResponse<Object> handleInternalServerException(final InternalServerException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(exception.getErrorCode());
}

// 서버 내부에서 예상치 못한 오류가 발생한 경우
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
protected ApiResponse<Object> handleUnexpectedException(final Exception exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(ErrorCode.INTERNAL_SERVER_EXCEPTION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
@Getter
public enum ErrorCode {
// Validation Exception
VALIDATION_EXCEPTION("V001", "잘못된 요청입니다."),
METHOD_NOT_ALLOWED_EXCEPTION("V002", "지원하지 않는 메소드입니다."),
UNSUPPORTED_MEDIA_TYPE("V003", "허용하지 않는 미디어 타입입니다."),
INVALID_JWT_TOKEN_EXCEPTION("V004", "존재하지 않거나 잘못된 JWT 토큰 형식입니다."),
INVALID_OAUTH2_TOKEN_EXCEPTION("V005", "존재하지 않거나 잘못된 OAuth2 토큰 입니다."),

// Unauthorized Exception
VALIDATION_EXCEPTION("V001", "유효하지 않은 요청 값입니다."),
BAD_REQUEST_EXCEPTION("V002", "잘못된 요청입니다."),
METHOD_NOT_ALLOWED_EXCEPTION("V003", "지원하지 않는 HTTP 메소드입니다."),
UNSUPPORTED_MEDIA_TYPE("V004", "허용하지 않는 미디어 타입입니다."),
INVALID_JWT_TOKEN_EXCEPTION("V005", "존재하지 않거나 잘못된 JWT 토큰 형식입니다."),
INVALID_OAUTH2_TOKEN_EXCEPTION("V006", "존재하지 않거나 잘못된 OAuth2 토큰 입니다."),

// UnAuthorized Exception
UNAUTHORIZED_JWT_EXCEPTION("U001", "JWT 토큰이 유효하지 않습니다. 다시 로그인 해주세요."),

// Forbidden Exception
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nice.petudio.global.exception;
package com.nice.petudio.global.exception.model;

import com.nice.petudio.global.exception.error.ErrorCode;

Expand Down

0 comments on commit 88d6c50

Please sign in to comment.