애플리케이션의 안정성을 높이고 사용자에게 유용한 오류 메세지를 제공하는 데 중요한 역할을 함.
스프링 MVC는 다양한 방법으로 예외를 처리할 수 있으며, 이러한 방법들을 적절히 활용하면, 더 나은 사용자 경험과 유지보수성을 제공할 수 있음.
스프링 MVC는 기본적으로 @Controller 와 @RestController 에서 발생하는 예외를 처리할 수 있는 기능을 제공
@Controller
public class MyController {
	
    @GetMapping("/example")
    public String example() {
    	if (true) {
	        throw new RuntimeException("예외발생!");
        }
        return "exampleView";
    }
}
RuntimeException 이 발생하면, 기본적으로 스프링은 Whitelabel Error Page 를 사용자에게 보여줌.
ExceptionHandler 를 사용하여 특정 예외를 처리하는 메서드를 정의할 수 있음. 이 메서드는 해당 컨트롤러에서 발생하는 예외를 처리하는 데 사용됨.
@Controller
public class MyController {
	
    @GetMapping("/example")
    public String example() {
    	if (true) { //임의의 조건으로 예외 발생
        	throw new RuntimeException("예외발생!");
        }
     	return "exampleView";
    }
    
    @ExceptionHandler(RuntimeException.class)
    public String handleRuntimeException(RuntimeException e, Model model) {
    	model.addAttribute("errorMessage", e.getMessage());
        return "errorPage";
    }
}
ExceptionHandler 메서드는 예외가 발생하면 호출되며, 예외를 처리하고 적절한 뷰로 이동할 수 있음.
ControllerAdvice 를 사용하면 애플리케이션 전체에서 공통적으로 예외를 처리할 수 있음.
특정 컨트롤러에만 국한되지 않고, 모든 컨트ㅗㄹ러에서 발생하는 예외를 처리할 수 있음.
@ControllerAdvice
public class GlobalExceptionHandler {
	
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
    	return new ResponseEntity<>("글로벌 예외 처리: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
ControllerAdvice 클래스는 모든 컨트롤러에서 발생하는 예외를 전역적으로 처리
ExceptionHandler 메서드는 예외를 처리하고 적절한 응답을 반환
@ResponseStatus 를 사용하여 예외 클래스에 HTTP 상태 코드를 매필할 수 있음. 이를 통해 특정 예외가 발생했을 때 자동으로 HTTP 응답 상태를 설정할 수 있음.
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
	public ResourceNotFoundException(String message) {
    	super(message);
    }
}
@Controller
public class MyController {
	
    @GetMapping("/resource/{id}")
    public String getResource(@PathVariable("id") Long id) {
		if (id == null) { // 임의의 조건으로 예외 발생
        	throw new ResourceNotFoundException("리소스가 존재하지 않습니다.")
    	}
        return "resourceView";
    }
}
ResourceNotFoundException 이 발생하면 HTTP 404 상태 코드와 함께 적절한 메세지가 반환
스프링 부트에서는 ErrorController 인터페이스를 구현하여 애플리케이션의 전역 오류 페이지를 사용자 정의할 수 있음.
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class CustomErrorController implements ErrorController {
	
    @RequestMapping("/error")
    public String handleError() {
    	return "customErrorPage"; // 사용자 정의 오류 메세지
    }
    
    @Override
    public String getErrorPath() {
    	return "/error";
    }
}
/error 경로에 대한 요청이 발생하면 handleError() 메서드가 호출되어 사용자 정의 오류 페이지로 리디렉션됨.
RestControllerAdvice 는 @ControllerAdvice 와 @ResponseBody 의 조합.
이 어노테이션을 사용하면 RESTful 웹 서비스에서 발생하는 예외를 처리하고, JSON 또는 XML 형식으로 응답할 수 있음.
즉, @ControllerAdvice 와 동일한 방식으로 전역 예외 처리를 수행하면서, RESTful API 응답을 JSON 형태로 쉽게 반환할 수 있음.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.annotation.ResponseStatus;
// 사용자 정의 예외 클래스
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
	public ResourceNotFoundException(String message) {
    	super(message);
    }
}
// 글로벌 예외 처리 클래스
@RestControllerAdvice
public class GlobalRestExceptionHandler {
	
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
    	return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
    	ErrorResponse errorResponse = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred");
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
// 오류 응답 클래스
public class ErrorResponse {
	private String errorCode;
    private String message;
    
    // Constructors, getters, setters
    public ErrorResponse(String errorCode, String message) {
    	this.errorCode = errorCode;
        this.message = message;
    }
    
    // Getters and Setters
    public String getErrorCode() { return errorCode; }
    public void setErrorCode(String errorCode) { this.errorCode = errorCode }
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message }
}