# 13 예외처리

p230 - src/main/java/mvc/error/controller/ErrorController

```java
@Slf4j
@Controller
@RequestMapping("/error")
public class ErrorController {

    @GetMapping("/test")
    public void getTestError(HttpServletResponse resp) throws IOException {
        resp.sendError(400, "에러 발생");
    }

    @GetMapping("/404")
    public String get400Error(){
        return "error/404";
    }

    // 404 에러 처리 - JSON 응답
    @GetMapping(value = "/404", produces = "application/json")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> get404ErrorJson() {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("status", 404);
        errorDetails.put("error", "Not Found");
        errorDetails.put("message", "Page not found");
        errorDetails.put("path", "/error/404");
        return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }


    @GetMapping("/500")
    public String get500Error() {
        return "error/500";
    }

    // 500 에러 처리 - JSON 응답
    @GetMapping(value = "/500", produces = "application/json")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> get500ErrorJson() {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("status", 500);
        errorDetails.put("error", "Internal Server Error");
        errorDetails.put("message", "An unexpected error occurred");
        errorDetails.put("path", "/error/500");
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }


    @GetMapping("/runtime")
    public void runtimeException() {
        throw new RuntimeException("runtime exception 발생");
    }

    // Runtime Exception 처리 - JSON 응답
    @GetMapping(value = "/runtime", produces = "application/json")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> runtimeExceptionJson() {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("status", 500);
        errorDetails.put("error", "Internal Server Error");
        errorDetails.put("message", "Runtime exception 발생");
        errorDetails.put("path", "/error/runtime");
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @GetMapping(value = "/illegal")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> IllegalExceptionHtml() {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "타입 에러", new IllegalArgumentException());
    }

    @GetMapping(value = "/illegal", produces = "application/json")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> IllegalExceptionJson() {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "타입 에러", new IllegalArgumentException());
    }

}

```

p232 - src/main/java/mvc/error/CustomWebServerFactoryCustomizer

```java
@Component
public class CustomWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        // 404 에러 페이지 등록
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"));
        // 500 에러 페이지 등록
        factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"));
    }

}

```

p233 - src/main/resources/templates/error/4xx.html, 404.html, 500.html

```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>400 - Bad Request</title>
</head>
<body>
<h1>400 - Bad Request</h1>

<!-- 오류 정보를 출력 -->
<p><strong>Timestamp:</strong> <span th:text="${timestamp}">2024-10-02T10:15:30</span></p>
<p><strong>Status:</strong> <span th:text="${status}">400</span></p>
<p><strong>Error:</strong> <span th:text="${error}">Bad Request</span></p>
<p><strong>Exception:</strong> <span th:text="${exception}">org.springframework.web.bind.MissingServletRequestParameterException</span></p>
<p><strong>Message:</strong> <span th:text="${message}">Required String parameter 'param' is not present</span></p>
<p><strong>Path:</strong> <span th:text="${path}">/requested/path</span></p>

<!-- 커스텀 에러 메시지 -->
<p th:text="${errorMessage}">요청하신 페이지를 찾을 수 없습니다.</p>

</body>
</html>

```

```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>404 - Bad Request</title>
</head>
<body>
<h1>404 - Bad Request</h1>

<!-- 오류 정보를 출력 -->
<p><strong>Timestamp:</strong> <span th:text="${timestamp}">2024-10-02T10:15:30</span></p>
<p><strong>Status:</strong> <span th:text="${status}">400</span></p>
<p><strong>Error:</strong> <span th:text="${error}">Bad Request</span></p>
<p><strong>Exception:</strong> <span th:text="${exception}">org.springframework.web.bind.MissingServletRequestParameterException</span></p>
<p><strong>Message:</strong> <span th:text="${message}">Required String parameter 'param' is not present</span></p>
<p><strong>Path:</strong> <span th:text="${path}">/requested/path</span></p>

<!-- 커스텀 에러 메시지 -->
<p th:text="${errorMessage}">요청하신 페이지를 찾을 수 없습니다.</p>

</body>
</html>

```

```html
<!DOCTYPE html>
<html>
<head>
  <title>500 - Internal Server Error</title>
</head>
<body>
<h1>500 - Internal Server Error</h1>
<p th:text="${errorMessage}">서버에 오류가 발생했습니다. 잠시 후 다시 시도해주세요.</p>
</body>
</html>

```

p234 - src/main/java/mvc/error/ExceptionHandlingFilter

```java
@WebFilter("/*") // 모든 요청에 대해 이 필터를 적용합니다.
public class ExceptionHandlingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 (필요한 경우)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        try {
            // 다음 필터 또는 서블릿으로 요청 전달
            chain.doFilter(request, response);
        } catch (Exception e) {
            // 예외 발생 시 500 에러 페이지로 포워딩
            handleException(e, httpRequest, httpResponse);
        }

        // 404 오류를 감지하여 처리
        if (httpResponse.getStatus() == HttpServletResponse.SC_NOT_FOUND) {
            httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "페이지를 찾을 수 없습니다.");
        }
    }

    @Override
    public void destroy() {
        // 필터 종료 작업 (필요한 경우)
    }

    // 예외를 처리하는 메서드
    private void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 로그 작성 (선택적)
        e.printStackTrace();

        // 500 에러 페이지로 포워딩
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다.");
    }
}


```

p238, 240 - src/main/java/mvc/error/LoggingIntercetor

```java
@Slf4j
@Component
public class LoggingInterceptor implements HandlerInterceptor {

    // 요청이 컨트롤러에 전달되기 전에 호출
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 요청별로 고유한 요청 UUID 생성
        String requestUUID = UUID.randomUUID().toString();

        // 요청 객체에 UUID를 저장
        request.setAttribute("requestUUID", requestUUID);

        log.info("preHandle 요청이 컨트롤러에 전달되기 전에 호출 [{}]: {} {}", requestUUID, request.getMethod(), request.getRequestURI());
        return true;
    }

    // 컨트롤러 실행 후, 뷰 렌더링 전에 호출
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        String requestUUID = (String) request.getAttribute("requestUUID");
        log.info("postHandle 컨트롤러 실행 후, 뷰 렌더링 전에 호출 [RequestUUID: {}]", requestUUID);
    }

    // 요청 완료 후 (뷰 렌더링 완료 후) 호출
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        String requestUUID = (String) request.getAttribute("requestUUID");
        log.info("afterCompletion 요청 완료 후 (뷰 렌더링 완료 후) 호출 [RequestUUID: {}]", requestUUID);
        if (ex != null) {
            log.info("afterCompletion 예외 발생 [RequestUUID: {}]: {}", requestUUID, ex.getMessage());
        }
    }
}

```

p239 , 241- src/main/java/mvc/error/WebConfig

```java
@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final LoggingInterceptor loggingInterceptor;

    @Autowired
    public WebConfig(LoggingInterceptor loggingInterceptor) {
        this.loggingInterceptor = loggingInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 모든 요청에 대해 인터셉터 적용
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/**") // 특정 경로에만 적용하려면 "/api/**" 등으로 변경 가능
                .excludePathPatterns("/static/**", "/css/**", "/js/**", "/public/**", "/error");  // 특정 경로는 제외
    }
}

```

p243 - src/resources/templates/error/404.html

```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>404 - Bad Request</title>
</head>
<body>
<h1>404 - Bad Request</h1>

<!-- 오류 정보를 출력 -->
<p><strong>Timestamp:</strong> <span th:text="${timestamp}">2024-10-02T10:15:30</span></p>
<p><strong>Status:</strong> <span th:text="${status}">400</span></p>
<p><strong>Error:</strong> <span th:text="${error}">Bad Request</span></p>
<p><strong>Exception:</strong> <span th:text="${exception}">org.springframework.web.bind.MissingServletRequestParameterException</span></p>
<p><strong>Message:</strong> <span th:text="${message}">Required String parameter 'param' is not present</span></p>
<p><strong>Path:</strong> <span th:text="${path}">/requested/path</span></p>

<!-- 커스텀 에러 메시지 -->
<p th:text="${errorMessage}">요청하신 페이지를 찾을 수 없습니다.</p>

</body>
</html>

```

p246 - src/main/java/mvc/error/GlobalExceptionHandler

```java
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body("잘못된 요청입니다: " + ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("서버에서 오류가 발생했습니다: " + ex.getMessage());
    }
}

```

p247 - src/main/java/mvc/error/PackageSpecificExceptionHandler, SpecificControllerExceptionHandler

```java
@ControllerAdvice("mvc.error.controller")
public class PackageSpecificExceptionHandler {

}
```

```java
@ControllerAdvice(assignableTypes = {ErrorController.class})
public class SpecificControllerExceptionHandler {

}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://team-everywhere.gitbook.io/backend/spring/spring-boot/13.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
