Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#14 [feat] Spring Rest Controller Advice 구현 #16

Merged
merged 14 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.universe.uni.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
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.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.universe.uni.exception.ApiException;
import com.universe.uni.exception.dto.ErrorResponse;
import com.universe.uni.exception.dto.ErrorType;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class ControllerExceptionAdvice extends ResponseEntityExceptionHandler {

/**
* 400 BAD_REQUEST
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException exception,
HttpHeaders headers,
HttpStatus status,
WebRequest request
) {
ErrorResponse errorResponse = ErrorResponse.error(ErrorType.VALIDATION_REQUEST_MISSING_EXCEPTION);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

@Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(
MissingServletRequestParameterException exception,
HttpHeaders headers,
HttpStatus status,
WebRequest request
) {
ErrorResponse errorResponse = ErrorResponse.error(ErrorType.VALIDATION_REQUEST_MISSING_EXCEPTION);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

@Override
protected ResponseEntity<Object> handleMissingPathVariable(
MissingPathVariableException exception,
HttpHeaders headers,
HttpStatus status,
WebRequest request
) {
ErrorResponse errorResponse = ErrorResponse.error(ErrorType.VALIDATION_REQUEST_MISSING_EXCEPTION);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(MissingRequestHeaderException.class)
protected ResponseEntity<Object> handleMissingRequestHeaderException(
MissingRequestHeaderException exception
) {
ErrorResponse errorResponse = ErrorResponse.error(ErrorType.VALIDATION_EXCEPTION);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}

/**
* 500 Internal Server
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
protected ErrorResponse handleException(
final Exception exception
) {
return ErrorResponse.error(ErrorType.INTERNAL_SERVER_ERROR);
}

/**
* Api custom error
*/
@ExceptionHandler(ApiException.class)
protected ResponseEntity handleCustomException(
ApiException exception
) {
return ResponseEntity.status(exception.getHttpStatus()).body(ErrorResponse.error(exception.getError()));
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/universe/uni/exception/ApiException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.universe.uni.exception;

import com.universe.uni.exception.dto.ErrorType;

public class ApiException extends RuntimeException {
2zerozu marked this conversation as resolved.
Show resolved Hide resolved
private final ErrorType error;

public ApiException(ErrorType error, String message) {
super(message);
this.error = error;
}

public int getHttpStatus() {
return this.error.getHttpStatusCode();
}

public ErrorType getError() {
return this.error;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.universe.uni.exception;

import com.universe.uni.exception.dto.ErrorType;

public class BadRequestException extends ApiException {

public BadRequestException(ErrorType error) {
super(error, error.getMessage());
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/universe/uni/exception/ConflictException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.universe.uni.exception;

import com.universe.uni.exception.dto.ErrorType;

public class ConflictException extends ApiException {

public ConflictException(ErrorType error) {
super(error, error.getMessage());
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/universe/uni/exception/NotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.universe.uni.exception;

import com.universe.uni.exception.dto.ErrorType;

public class NotFoundException extends ApiException {

public NotFoundException(ErrorType error) {
super(error, error.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.universe.uni.exception;

import com.universe.uni.exception.dto.ErrorType;

public class UnauthorizedException extends ApiException {

public UnauthorizedException(ErrorType error) {
super(error, error.getMessage());
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BadRequestException, ConflictException, NotFoundException, UnauthorizedException 모두 ErrorType을 매개변수로 받아 ApiException에 넘겨주는 똑같은 역할을 하는데, 나눠서 커스텀 예외를 생성하신 이유가 궁금합니다...!

좀 더 명시적으로 어떤 관련 에러인지 보여주기 위함일까요...?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네!

  1. 예외 클래스 이름을 통해 예외의 원인을 빠르게 인지하여 가독성을 높임
  2. ApiException을 통해 구체적인 예외를 처리하기 위해서는 에러 메시지나 에러 타입을 체크하는 추가적인 로직이 필요함 (번거로움)

의 이유로 따로 나눴습니다

19 changes: 19 additions & 0 deletions src/main/java/com/universe/uni/exception/dto/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.universe.uni.exception.dto;

import com.fasterxml.jackson.annotation.JsonInclude;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResponse {

private final String uniErrorCode;

public static ErrorResponse error(ErrorType error) {
return new ErrorResponse(error.getUniErrorCode());
}
}
47 changes: 47 additions & 0 deletions src/main/java/com/universe/uni/exception/dto/ErrorType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.universe.uni.exception.dto;

import org.springframework.http.HttpStatus;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum ErrorType {

/**
* 400 BAD REQUEST
*/
VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "", "잘못된 요청입니다."),
VALIDATION_REQUEST_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "", "요청값이 입력되지 않았습니다."),

/**
* 404 NOT FOUND
*/
NOT_FOUND_USER_EXCEPTION(HttpStatus.NOT_FOUND, "", "존재하지 않는 유저입니다"),

/**
* 409 CONFLICT
*/
ALREADY_EXIST_USER_EXCEPTION(HttpStatus.CONFLICT, "", "이미 존재하는 유저입니다"),

/**
* 500 INTERNAL SERVER ERROR
*/
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "", "예상치 못한 서버 에러가 발생하였습니다.");

private final HttpStatus httpStatus;
private String uniErrorCode;
private final String message;

public int getHttpStatusCode() {
return httpStatus.value();
}

public String getUniErrorCode() {
return uniErrorCode;
}

public String getMessage() {
return message;
}
}