🚀 Exception Handler 처리 (HTTP 상태 코드)
📌 목표
- @ControllerAdvice, @RestControllerAdvice 개념 이해
- @ControllerAdvice vs @RestControllerAdvice 차이점
- 사용자 정의 예외 클래스 만들기
- @ControllerAdvice를 활용한 전역 예외 처리 구현
- 에러 페이지(errorPage.jsp) 코드 수정
- 직접 예외 발생하여 테스트
- HTTP 상태 코드 정리 (4xx & 5xx)
- @ControllerAdvice란?
1️⃣ @ControllerAdvice, @RestControllerAdvice란?
Spring에서 전역적으로 예외를 처리하는 어노테이션.
애플리케이션 전역에서 발생하는 예외를 중앙에서 관리 가능.
사용 이유?
- 컨트롤러마다 try-catch로 예외를 처리하는 대신, 전역에서 한 번만 처리하도록 한다.
- 예외 발생 시 일관된 응답을 제공할 수 있다.
2️⃣ @ControllerAdvice vs @RestControllerAdvice 차이점
어노테이션 | 설명 |
@ControllerAdvice | @Controller에서 발생한 예외를 처리하며, HTML 뷰를 반환하는 웹 애플리케이션에서 주로 사용 |
@RestControllerAdvice | @RestController에서 발생한 예외를 처리하며, JSON과 같은 데이터 응답을 반환하는 API 에서 주로 사용 |
👉 @RestControllerAdvice는 사실 @ControllerAdvice에 @ResponseBody가 추가된 것과 동일한 효과!
✅ 예제 코드 비교
@ControllerAdvice 예제 (HTML 페이지 반환)
@ControllerAdvice
public class GlobalExceptionHandler {
// 특정 예외 처리 (리소스를 찾을 수 없는 경우)
@ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView handleResourceNotFound(ResourceNotFoundException ex) {
// errorPage.jsp로 이동하면서 에러 코드와 메시지 전달
ModelAndView modelAndView = new ModelAndView("errorPage");
modelAndView.addObject("statusCode", HttpStatus.NOT_FOUND.value());
modelAndView.addObject("message", ex.getMessage());
return modelAndView;
}
}
@RestControllerAdvice 예제 (JSON 응답)
@RestControllerAdvice
public class GlobalExceptionHandler {
// 특정 예외 처리 (리소스를 찾을 수 없는 경우)
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Object> handleResourceNotFound(ResourceNotFoundException ex) {
// JSON 형식으로 응답 반환
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
3️⃣ 사용자 정의 예외 클래스 만들기
Spring에서 기본 제공하는 예외 클래스 외에도 사용자 정의 예외를 만들어서 관리할 수 있음.
위 그림처럼 패키지와 자바 파일을 만들어 주세요 (handler/GlobalControllerAdivce.java 파일 생성)
- handler/GlobalControllerAdivce.java 파일 생성
- handler/exception 패키지 생성
- handler/exception/UnAuthorizedException 자바 파일 생성
: UnAuthorizedException 클래스는 인증이 안된 사용자가 인증이 필요하 서비스에 접근 요청을 할 때 예외를 발생 시킬 사용자 정의 예외 클래스를 설계 합니다.
✅ 예외 클래스 예제
📌 예제 - 인증 예외 (UnAuthorizedException)
@Getter
public class UnAuthorizedException extends RuntimeException {
private HttpStatus status;
// 생성자에서 예외 메시지와 상태 코드 설정
public UnAuthorizedException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
📌 예제 - 에러 페이지 이동 예외 (RedirectException)
@Getter
public class RedirectException extends RuntimeException {
private HttpStatus status;
public RedirectException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
📌 예제 - 데이터 전달 예외 (DataDeliveryException)
@Getter
public class DataDeliveryException extends RuntimeException {
private HttpStatus status;
public DataDeliveryException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
🔹 4. @ControllerAdvice 구현해보기 (전역 예외 처리)
위에서 만든 사용자 정의 예외를 전역 예외 처리(Global Exception Handler) 에서 처리해봅니다.
@ControllerAdvice // 전역 예외 처리
public class GlobalControllerAdvice {
/**
* 🔹 모든 예외 로깅
* - 예외가 발생하면 해당 내용을 콘솔에 출력 (로그로 남길 수도 있음)
*/
@ExceptionHandler(Exception.class)
public void logException(Exception e) {
System.out.println("----------------");
System.out.println("예외 발생: " + e.getClass().getName());
System.out.println("예외 메시지: " + e.getMessage());
System.out.println("----------------");
}
/**
* 🔹 인증 예외 (`UnAuthorizedException`) 처리
* - alert 메시지로 클라이언트에게 알림
*/
@ResponseBody
@ExceptionHandler(UnAuthorizedException.class)
public String handleUnAuthorizedException(UnAuthorizedException e) {
return "<script>alert('" + e.getMessage() + "'); window.history.back();</script>";
}
/**
* 🔹 데이터 전달 예외 (`DataDeliveryException`) 처리
* - alert 메시지로 클라이언트에게 알림
*/
@ResponseBody
@ExceptionHandler(DataDeliveryException.class)
public String handleDataDeliveryException(DataDeliveryException e) {
return "<script>alert('" + e.getMessage() + "'); window.history.back();</script>";
}
/**
* 🔹 에러 페이지 이동 예외 (`RedirectException`) 처리
* - 에러 페이지로 이동하면서 상태 코드와 메시지를 전달
*/
@ExceptionHandler(RedirectException.class)
public ModelAndView handleRedirectException(RedirectException e) {
ModelAndView modelAndView = new ModelAndView("errorPage");
modelAndView.addObject("statusCode", e.getStatus().value());
modelAndView.addObject("message", e.getMessage());
return modelAndView;
}
}
🔹 5. 에러 페이지 (errorPage.jsp) 코드
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>${statusCode} Error - Page Not Found</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
body { display: flex; justify-content: center; align-items: center; height: 100vh; }
.error-message { text-align: center; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container text-center">
<h1 class="display-1">${statusCode}</h1>
<p class="error-message">Page Not Found</p>
<p>${message}</p>
<a href="/" class="btn btn-primary">홈으로 가기</a>
</div>
</body>
</html>
🔹 6. 직접 예외 발생시켜 보기
테스트를 위해 컨트롤러에서 강제로 예외를 발생시켜 봅니다.
@Controller
public class MainController {
/**
* 🔹 테스트 1: 잘못된 요청 → `RedirectException`
*/
@GetMapping("/error-test1/{isError}")
public String errorTest1(@PathVariable boolean isError) {
if (isError) throw new RedirectException("잘못된 요청입니다.", HttpStatus.BAD_REQUEST);
return "mainPage";
}
/**
* 🔹 테스트 2: 중복된 이메일 → `DataDeliveryException`
*/
@GetMapping("/error-test2/{isError}")
public String errorTest2(@PathVariable boolean isError) {
if (isError) throw new DataDeliveryException("중복된 이메일을 사용할 수 없습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
return "mainPage";
}
/**
* 🔹 테스트 3: 로그인 필요 → `UnAuthorizedException`
*/
@GetMapping("/error-test3/{isError}")
public String errorTest3(@PathVariable boolean isError) {
if (isError) throw new UnAuthorizedException("로그인 먼저 해주세요", HttpStatus.UNAUTHORIZED);
return "mainPage";
}
}
7️⃣ HTTP 상태 코드 정리 (4xx & 5xx)
📌 HTTP 상태 코드란?
**HTTP(Hypertext Transfer Protocol)**는 클라이언트(브라우저)와 서버 간에 데이터를 주고받는 프로토콜이다.
서버는 클라이언트의 요청을 처리한 후 **HTTP 상태 코드(응답 코드)**를 반환한다.
✅ 1xx (정보 응답) → 요청이 수신되었으며 계속 진행 중
✅ 2xx (성공 응답) → 요청이 정상적으로 처리됨
✅ 3xx (리다이렉션 응답) → 추가 작업이 필요함
✅ 4xx (클라이언트 에러) → 클라이언트 요청 오류
✅ 5xx (서버 에러) → 서버에서 요청을 정상적으로 처리하지 못함
📌 4xx 상태 코드 (클라이언트 에러)
클라이언트의 요청이 잘못되었거나 유효하지 않은 경우 발생
사용자의 요청을 수정해야 해결 가능
상태 코드 |
상태 텍스트 |
뜻 | 서버 측면에서의 의미 |
400 | Bad Request | 잘못된 요청 | 요청 구문, 유효하지 않은 요청 메시지 또는 디코딩할 수 없는 내용으로 인해 서버가 요청을 이해할 수 없음. |
401 | Unauthorized | 권한 없음 | 요청이 인증을 필요로 하며, 클라이언트가 해당 인증을 제공하지 않았음. WWW-Authenticate 헤더를 통해 인증 방식을 지정. |
403 | Forbidden | 금지됨 | 서버가 요청을 이해했으나, 권한 부족 또는 기타 사유로 인해 요청을 거부함. |
404 | Not Found | 찾을 수 없음 | 서버가 요청한 리소스를 찾을 수 없음. 주소 오타, 리소스 이동/삭제 등이 원인일 수 있음. |
📌 5xx 상태 코드 (서버 에러)
클라이언트의 요청은 유효하지만, 서버 측 문제로 요청이 처리되지 않은 경우 발생
클라이언트가 아닌 서버에서 원인을 분석하고 수정해야 함
코드 | 상태 텍스트 | 한국어 뜻 | 서버 측면에서의 의미 |
500 | Internal Server Error | 내부 서버 오류 | 서버 내부에서 오류가 발생하여 요청을 처리할 수 없음. 예상치 못한 예외 발생 시 반환됨. |
501 | Not Implemented | 구현되지 않음 | 서버가 요청된 메소드를 지원하지 않거나 구현하지 않음. 예: 서버가 PUT 요청을 지원하지 않는 경우 |
502 | Bad Gateway | 불량 게이트웨이 | 게이트웨이/프록시 서버가 뒤쪽 서버에서 잘못된 응답을 받았을 때 발생. |
503 | Service Unavailable | 서비스 제공 불가 | 서버가 일시적으로 과부하 상태이거나 유지 보수 중이라 요청을 처리할 수 없음. |
🚀 Spring Global Exception Handler (GlobalControllerAdvice) 정리
📌 개요
Spring에서는 컨트롤러별로 개별적으로 예외를 처리하는 것이 아니라,
전역적으로 예외를 처리할 수 있는 @ControllerAdvice를 제공합니다.
이 코드는 GlobalControllerAdvice 클래스를 통해 예외를 중앙에서 관리하도록 설계되어 있습니다.
즉, 컨트롤러에서 발생하는 특정 예외를 일괄적으로 처리하여 일관된 응답을 제공할 수 있습니다.
8️⃣ @ControllerAdvice란?
Spring에서 모든 컨트롤러에서 발생하는 예외를 한 곳에서 처리할 수 있도록 도와주는 어노테이션.
- @ExceptionHandler와 함께 사용하여 특정 예외를 처리할 수 있음.
- HTML 뷰를 반환할 경우 @ControllerAdvice 사용.
- JSON 데이터를 반환할 경우 @RestControllerAdvice 사용.
✅ @ControllerAdvice는 **View 반환(웹 애플리케이션)**에 사용
✅ @RestControllerAdvice는 **REST API(데이터 반환)**에 사용
GlobalControllerAdvice
package com.tenco.bank.handler;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
/**
* 📌 글로벌 예외 처리 핸들러
* - @ControllerAdvice : HTML 뷰(View)를 반환하는 예외 처리에 사용
* - @RestControllerAdvice : JSON 데이터를 반환하는 예외 처리에 사용
*/
@ControllerAdvice
public class GlobalControllerAdvice {
/**
* 🛠️ 모든 예외를 처리 (기본 로그 출력)
* - 로깅을 통해 어떤 예외가 발생했는지 확인 가능
*/
@ExceptionHandler(Exception.class)
public void exception(Exception e) {
System.out.println("----------------");
System.out.println("예외 클래스: " + e.getClass().getName());
System.out.println("예외 메시지: " + e.getMessage());
System.out.println("----------------");
}
/**
* 🔹 DataDeliveryException 처리
* - 데이터를 alert 창으로 클라이언트에게 전달
* - @ResponseBody 를 사용하여 HTML 코드 반환 (뷰 리졸버 탐색 X)
*/
@ResponseBody
@ExceptionHandler(DataDeliveryException.class)
public String dataDeliveryException(DataDeliveryException e) {
StringBuffer sb = new StringBuffer();
sb.append("<script>");
sb.append(" alert('" + e.getMessage() + "');");
sb.append(" window.history.back();");
sb.append("</script>");
return sb.toString();
}
/**
* 🔹 UnAuthorizedException 처리
* - 인증되지 않은 요청을 alert 창으로 클라이언트에게 알림
*/
@ResponseBody
@ExceptionHandler(UnAuthorizedException.class)
public String unAuthorizedException(UnAuthorizedException e) {
StringBuffer sb = new StringBuffer();
sb.append("<script>");
sb.append(" alert('" + e.getMessage() + "');");
sb.append(" window.history.back();");
sb.append("</script>");
return sb.toString();
}
/**
* 🔹 RedirectException 처리
* - 에러 발생 시 특정 JSP 페이지로 이동
* - ModelAndView를 사용하여 상태 코드 및 메시지 전달
*/
@ExceptionHandler(RedirectException.class)
public ModelAndView redirectException(RedirectException e) {
ModelAndView modelAndView = new ModelAndView("/errorPage");
modelAndView.addObject("statusCode", e.getStatus().value());
modelAndView.addObject("message", e.getMessage());
return modelAndView;
}
}
🔝 맨 위로 이동
'Spring' 카테고리의 다른 글
[Spring boot] Bank App - 회원가입 (0) | 2025.02.15 |
---|---|
[Spring boot] Bank App - MyBatis 설정 (DB 접근 기술) (0) | 2025.02.14 |
[Spring boot] Bank App - MainController, mainPage.jsp 구현 (2) | 2025.01.15 |
[Spring boot] Bank App - 화면 구현(레이아웃 분리) (0) | 2025.01.15 |
[Spring boot] Bank App - 화면 구현(템플릿 가져오기) (0) | 2025.01.15 |