1. 에러 페이지가 필요한 이유

애플리케이션 운영시, 사용자가 존재하지 않는 페이지에 접근 접근(404 error)하거나 시스템 상의 문제(500 error)로 인해 에러가 발생했을 때,

사용자에게 올바른 에러 페이지를 응답해줘야 합니다.



SpringBoot에서는 상단의 이미지처럼 Whitelabel 기본 에러 페이지를 보여주도록 활성화 되어 있습니다.

해당 이미지는 일반 사용자에게 너무 불친절하고 디자인도 예쁘지 않으며, 메시지 내용이 개발자 친화적입니다.

심지어 콘솔에서 보여지는 에러가 응답 페이지에 노출된다면, 시스템 보안적으로도 위협이 됩니다.


그래서 깃헙 에러 페이지처럼 사용자에게 메시지를 전달하는 적절한 에러페이지를 응답해주는 것이 필요합니다.


이 글에서는 기본 에러페이지와 상태 코드에 따른 에러 페이지를 응답하는 방법에 대해 알아보겠습니다.





2. 에러 페이지 응답 구현

SrpingBoot에서 기본 에러 페이지는 src/main/resources/templates/error.html 파일이 됩니다.

상태 코드에 따른 에러 페이지를 응답하려면 src/main/resources/templates/error 디렉토리를 만들고, 그 하위에 {상태코드}.html 파일을 만들면 됩니다.


403, 404, 500 에러에 대한 테스트를 해보면서 구현해보도록 하겠습니다.



1) 프로젝트 구조




2) 구현하기 - Controller

src/main/java/com/victolee/errorhandling/controller/SomeController.java

package com.victolee.errorhandling.controller;

import com.victolee.errorhandling.exception.ForbiddenException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SomeController {
@GetMapping("/internalerror")
public void internalerror() {
throw new RuntimeException("500 Internal Error !!");
}

@GetMapping("/forbidden")
public void forbidden() {
throw new ForbiddenException("403 Forbidden !!");
}
}

Exception을 발생시켜서 의도적으로 에러를 반환합니다.

ForbiddenExcetpion은 403 에러를 반환하는 custom exception이며, 이어서 구현해보겠습니다.




3) 구현하기 - ForbiddenException

src/main/java/com/victolee/errorhandling/exception/ForbiddenException.java

package com.victolee.errorhandling.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.FORBIDDEN)
public class ForbiddenException extends RuntimeException {

public ForbiddenException(String message) {
super(message);
}
}

  • @ResponseStatus(HttpStatus.FORBIDDEN)
    • 해당 예외가 호출되면 Forbidden status, 즉 403 error code를 반환하도록 합니다.
  • public ForbiddenException(String message)
    • message를 전달할 수 있도록 생성자도 추가해줍니다.




4) 구현하기 - error.html

src/main/resources/templates/error.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>기본 에러 페이지</title>
</head>
<body>
<h1>기본 에러 페이지 입니다.</h1>
<div>
status : <span th:text="${status}"></span>
</div>
<div>
path : <span th:text="${path}"></span>
</div>
<div>
error : <span th:text="${error}"></span>
</div>
<div>
timestamp : <span th:text="${timestamp}"></span>
</div>
<div>
message : <span th:text="${message}"></span>
</div>
<div>
exception : <span th:text="${exception}"></span>
</div>
<div>
trace : <span th:text="${trace}"></span>
</div>
</body>
</html>
  • error.html은 기본 에러 페이지입니다.
    • 반환된 상태코드에 맞는 {상태코드}.html 파일이 정의되어 있지 않다면, error.html 페이지가 응답됩니다.
  • 에러 페이지 모델에는 위와 같은 속성들이 정의되어 있습니다.
    • status
      • 상태 코드
    • path
      • 예외가 발생한 URL 경로
    • error
      • 에러 발생 원인
    • timestamp
      • 에러 발생 시간
    • message
      • 예외 발생시 전달한 메시지
    • exception
      • 예외 처리 클래스 명
    • trace
      • 예외 발생 stack trace


exception과 trace 속성이 노출되려면, application.yml 파일에 아래의 속성이 추가되어 있어야 합니다.
  • server.error.include-exception: true
    • 기본값 false
  • server.error.include-stacktrace: always
    • 기본값 never
server:
error:
include-exception: true
include-stacktrace: always

기본값이 각각 false와 never인 이유는 공격자가 에러코드를 분석하여 공격할 수 있으므로 보안상 위험하기 때문입니다.

여기서는 예제이므로 설정해준 것이지, 기본 값을 그대로 사용하는 것이 좋습니다.





5) 구현하기 - 404.html / 500.html

상태 코드 에러 페이지의 내용은 error.html 파일과 동일하게 구성하면 됩니다.

다만 파일 경로가 error 디렉토리 하위에 있어야 한다는 점이 중요합니다.

ex) src/main/resources/templates/error/{상태코드}.html


src/main/resources/templates/error/404.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>404 error</title>
</head>
<body>
<h1>404 error 페이지 입니다.</h1>
<div>
status : <span th:text="${status}"></span>
</div>
<div>
path : <span th:text="${path}"></span>
</div>
<div>
error : <span th:text="${error}"></span>
</div>
<div>
timestamp : <span th:text="${timestamp}"></span>
</div>
<div>
message : <span th:text="${message}"></span>
</div>
<div>
exception : <span th:text="${exception}"></span>
</div>
<div>
trace : <span th:text="${trace}"></span>
</div>
</body>
</html>
  • 500.html 파일도 동일하게 작성해주시면 되고, <h1> 내용만 수정하면 됩니다.





3. 테스트

1) 403 error 테스트

  • 403.html 파일이 정의되어 있지 않았으므로, 기본 에러 페이지는 error.html 페이지가 응답됩니다.
  • 상태는 403이며, message도 forbidden이므로 의도대로 동작한 것을 확인할 수 있습니다.




2) 404 error 테스트

  • 404.html 페이지가 응답되었습니다.
  • 예외가 발생하지 않았으므로 exception, trace 속성 값은 비어있습니다.




3) 500 error 테스트

  • 500.html 페이지가 응답되었고, 상태코드 및 메시지도 확인할 수 있습니다.




이상으로 에러 페이지를 컨트롤하는 방법에 대해 알아보았습니다.


실제로는 대부분의 서비스들이 처음에 봤듯이 이미지를 통해 에러 페이지를 처리하고 있습니다.

그래서 각 상태 코드에 맞는 html 파일을 만들고, 해당 페이지에 이미지만 붙여줘도 좋습니다.

이 글은 예제니까... 이런 것도 있으니 참고만 하는 용도로만 보셔도 될것 같습니다.