1. 모든 컨트롤러에서 발생하는 예외를 한 곳에서 처리
2. 예외 처리 로직의 중앙 집중화
3. 코드 중복 제거 및 유지보수성 향상
4. 일관된 에러 응답 제공
개념 확인용 코드
// 전통적인 방식 (각 컨트롤러마다 try-catch)
@Controller
public class BoardController {
@GetMapping("/board/{id}")
public String detail(@PathVariable Long id) {
try {
Board board = boardRepository.findById(id);
return "board/detail";
} catch (RuntimeException e) {
// 매번 이런 처리가 필요함
request.setAttribute("msg", e.getMessage());
return "err/404";
}
}
}
// @ControllerAdvice 방식 (중앙 집중 처리)
@Controller
public class BoardController {
@GetMapping("/board/{id}")
public String detail(@PathVariable Long id) {
Board board = boardRepository.findById(id); // 예외 발생 시 자동으로 핸들러가 처리
return "board/detail";
}
}
GlobalExceptionHandler 코드
package com.tenco.blog._core.errors;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
// 모든 컨트롤러에서 발생하는 예외를 이 클래스에서 처리 하겠다.
// RuntimeException 이 발생되면 해당 이 파일로 예외 처리가 오게 됨.
@Slf4j
@ControllerAdvice // IoC
public class GlobalExceptionHandler {
@ExceptionHandler(Exception400.class)
public String ex400(Exception400 e, HttpServletRequest request) {
log.warn("=== 400 Bad Request 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", e.getMessage());
return "err/400";
}
@ExceptionHandler(Exception401.class)
public String ex401(Exception401 e, HttpServletRequest request) {
log.warn("=== 401 Unauthorized 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", e.getMessage());
return "err/401";
}
@ExceptionHandler(Exception403.class)
public String ex403(Exception403 e, HttpServletRequest request) {
log.warn("=== 403 Forbidden 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", e.getMessage());
return "err/403";
}
@ExceptionHandler(Exception404.class)
public String ex404(Exception404 e, HttpServletRequest request) {
log.warn("=== 404 Not Found 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", e.getMessage());
return "err/404";
}
@ExceptionHandler(Exception500.class)
public String ex500(Exception500 e, HttpServletRequest request) {
log.warn("=== 500 Internal Server Error 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", e.getMessage());
return "err/500";
}
// 기타 모든 RuntimeException 처리 (최후의 보루)
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(RuntimeException e, HttpServletRequest request) {
log.warn("=== 예상치 못한 런타임 에러 발생 ===");
log.warn("요청 URL: {}", request.getRequestURL());
log.warn("에러메시지: {}", e.getMessage());
request.setAttribute("msg", "시스템 오류가 발생했습니다. 관리자에게 문의해주세요");
return "err/500";
}
}
package com.tenco.blog._core.errors;
// 400 Bad Request
public class Exception400 extends RuntimeException {
// 예외 메시지를 외부에서 받아서 내 부모클래스 RuntimeException 에게 생성자로 전달
public Exception400(String msg) {
super(msg); // 즉, 부모 클래스 메세지도 내가 직접 작성 부분으로 설정 됨.
}
// throw new Exception400("잘못된 요청"); 사용 예시
}
401
package com.tenco.blog._core.errors;
public class Exception401 extends RuntimeException {
// 예외 메시지를 외부에서 받아서 내 부모클래스 RuntimeException 에게 생성자로 전달
public Exception401(String msg) {
super(msg); // 즉, 부모 클래스 메세지도 내가 직접 작성 부분으로 설정 됨.
}
}
403
package com.tenco.blog._core.errors;
public class Exception403 extends RuntimeException {
// 예외 메시지를 외부에서 받아서 내 부모클래스 RuntimeException 에게 생성자로 전달
public Exception403(String msg) {
super(msg); // 즉, 부모 클래스 메세지도 내가 직접 작성 부분으로 설정 됨.
}
}
404
package com.tenco.blog._core.errors;
public class Exception404 extends RuntimeException {
// 예외 메시지를 외부에서 받아서 내 부모클래스 RuntimeException 에게 생성자로 전달
public Exception404(String msg) {
super(msg); // 즉, 부모 클래스 메세지도 내가 직접 작성 부분으로 설정 됨.
}
}
500
package com.tenco.blog._core.errors;
public class Exception500 extends RuntimeException {
// 예외 메시지를 외부에서 받아서 내 부모클래스 RuntimeException 에게 생성자로 전달
public Exception500(String msg) {
super(msg); // 즉, 부모 클래스 메세지도 내가 직접 작성 부분으로 설정 됨.
}
}