반응형

Exception을 발생시키는 방법

  1. 잘못된 요청 전송
    • ex) 존재하지 않는 URL 접근 => 404 Error 발생
    • 인증하지 않음 => 401 Error 발생
    • 서버 내부 에러 => 500 Error 발생
    • 이와 같이 직접 잘못된 요청을 전송하여 에러를 발생시킬 수 있음
  2. throw를 사용해 직접 에러를 던져주기
    throw new RuntimeException();
    throw new IllegalArgumentException();
  3. HttpServletResponse에 에러를 담아서 전송
    response.sendError(403);
    response.sendError(404, "404에러가 발생했습니다!");
  • 아래와 같이 Path Variable을 활용하여 원하는 에러를 발생 시킬 수도 있음
@GetMapping("/throw-exception/{errorCode}")
public void throwException(@PathVariable int errorCode, HttpServletResponse response) throws IOException {
    response.sendError(errorCode, errorCode + "번 에러가 발생하였습니다!");
}

에러 페이지 적용 예제

  • Spring Boot에서는 Exception이 발생하면 아래와 같이 Whitelabel Error Page가 출력됨
    • Spring Boot에서는 Controller에서 에러가 발생하면 WAS(TOMCAT)로 에러가 전달되고 WAS에서는 다시 Filter, Servlet, Interceptor을 거쳐 Error Page Controller을 실행시키는 방식
    • 이 때, 기본 설정이 아래와 같이 Whitelabel Error Page를 출력하게 되어있음

  • 만약 이 에러 페이지 대신 내가 원하는 디자인의 에러 페이지를 적용하고 싶다면?
  • 프로젝트의 resources/templates 또는 resources/static 경로에 "error" 라는 디렉토리 생성 후 404.html을 생성해 주면 됨
    • 400 번대 에러를 모두 통일시키고 싶다면 4xx.html로 지정하면 되지만, 404.html과 같이 자세히 설정해 준 것이 우선순위가 더 높음
    • (Thymeleaf 설치 후 resources/templates에 설치해야 밑에 나오는 예제 모두 진행 가능)

구현

  • resources/templates/error/4xx.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>400 번대 에러가 발생했습니다!</h1>
</body>
</html>
  • resources/templates/error/404.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>404 에러가 발생했습니다!</h1>
</body>
</html>

결과

  • 401, 402, 403 에러 발생 시 출력 결과

  • 404 에러 발생 시 출력 결과

에러 페이지에 에러 정보 출력 예제

  • 에러 정보는 화면에 출력하는 것 보다는 로그로 남기는 것이 더 좋은 방법이라 하지만 화면에 에러 정보를 출력해보는 예제를 구현해 봄

구현

  • applicaation.yml(혹은 application.properties)에 아래 코드 추가
# 화면에 에러 정보 출력
server:
  error:
    include-exception: true
    include-message: always
    include-binding-errors: always
    include-stacktrace: always
  • 위에서 만든 404.html 을 아래와 같이 수정 (Thymeleaf 사용)
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>404 에러가 발생했습니다!</h1>
    <li>에러 정보</li>
    <ul>
        <li th:text="|timestamp: ${timestamp}|"></li>
        <li th:text="|path: ${path}|"></li>
        <li th:text="|status: ${status}|"></li>
        <li th:text="|message: ${message}|"></li>
        <li th:text="|error: ${error}|"></li>
        <li th:text="|exception: ${exception}|"></li>
        <li th:text="|errors: ${errors}|"></li>
        <li th:text="|trace: ${trace}|"></li>
    </ul>
</body>
</html>

결과

  • 404 에러 발생 시 출력되는 화면

Error Code 적용 예제

  • 프로젝트를 개발하면서 발생할 수 있는 에러들이 존재
  • 이 에러들을 미리 정리해 놓는다면 나중에 해당 에러들이 발생했을 때 쉽게 사용할 수 있음

ErrorCode (enum)

  • 발생할 수 있을만한 에러들을 몇 개 정리해서 만들어 봄
  1. 로그인하거나 username으로 User을 찾는 등의 작업에서 username에 해당하는 User가 없는 경우 => USERNAME_NOT_FOUND
  2. 사용자가 인증, 인가에 실패한 경우 => INVALID_PERMISSION
  3. 회원가입 시 username이 중복되는 경우 => DUPLICATED_USER_NAME
  4. DB에서 에러가 나는 경우 => DATABASE_ERROR
  • 위에서 정리한 에러들을 아래와 같이 정리해 봄
@Getter
@AllArgsConstructor
public enum ErrorCode {

    USERNAME_NOT_FOUND(HttpStatus.NOT_FOUND, "유저를 찾을 수 없습니다"),
    INVALID_PERMISSION(HttpStatus.UNAUTHORIZED, "권한이 없습니다"),
    DUPLICATED_USER_NAME(HttpStatus.CONFLICT, "유저명이 중복됩니다"),
    DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "DB에러가 발생하였습니다");

    private HttpStatus status;
    private String message;

}

ExceptionDto

  • 위에서 정리한 에러들을 JSON 타입으로 출력하기 위한 DTO
  • Method Overloading을 사용해 ErrorCode만 parameter로 넘어오면 기본 Error Message를 출력하고, ErrorCode와 message가 같이 넘어온다면 넘어온 Error Message를 출력하게 함
@Getter
public class ExceptionDto {

    private String result;
    private ErrorCode errorCode;
    private String message;

    public ExceptionDto(ErrorCode errorCode) {
        this.result = "ERROR";
        this.errorCode = errorCode;
        this.message = errorCode.getMessage();
    }

    public ExceptionDto(ErrorCode errorCode, String message) {
        this.result = "ERROR";
        this.errorCode = errorCode;
        this.message = message;
    }
}

ExceptionController

  • 에러가 발생한 경우를 가정하고 알맞는 ErrorCode로 설정
  • 해당 ErrorCode로 Response의 status와 body를 만들어서 전송
@GetMapping("/throw-exception/1")
public ResponseEntity<?> throwException1() {
    // ex) username으로 User을 찾을 수 없는 경우 발생한 경우
    ErrorCode errorCode = ErrorCode.USERNAME_NOT_FOUND;

    return ResponseEntity.status(errorCode.getStatus())
            .body(new ExceptionDto(errorCode));
}

@GetMapping("/throw-exception/2")
public ResponseEntity<?> throwException2() {
    // ex) 사용자가 인증, 인가에 실패한 경우
    ErrorCode errorCode = ErrorCode.INVALID_PERMISSION;

    return ResponseEntity.status(errorCode.getStatus())
            .body(new ExceptionDto(errorCode, "ADMIN만 접근 가능합니다"));
}

@GetMapping("/throw-exception/3")
public ResponseEntity<?> throwException3() {
    // ex) 회원가입 시 username이 중복되는 경우
    ErrorCode errorCode = ErrorCode.DUPLICATED_USER_NAME;

    return ResponseEntity.status(errorCode.getStatus())
            .body(new ExceptionDto(errorCode));
}

@GetMapping("/throw-exception/4")
public ResponseEntity<?> throwException4() {
    // ex) DB 에러가 발생한 경우
    ErrorCode errorCode = ErrorCode.DATABASE_ERROR;

    return ResponseEntity.status(errorCode.getStatus())
            .body(new ExceptionDto(errorCode));
}

결과

 

반응형

↓ 클릭시 이동

복사했습니다!