1. @ControllerAdvice, @RestControllerAdvice 란?
HTTP 통신을 통해 예외 상황을 클라이언트에게 알려주는 방법은 여러 가지가 있으며, 이들을 적절히 사용하는 것이 중요하다.
@ControllerAdvice와 @RestControllerAdvice는 Spring Framework에서 제공하는 어노테이션들로, 애플리케이션 전역에 걸쳐 발생하는 예외를 효과적으로 관리하고 처리하는 데 사용된다. 이들은 일종의 "예외 처리의 중앙 집중화"를 가능하게 해주며, 애플리케이션 내 여러 컨트롤러나 서비스에서 공통적으로 발생할 수 있는 예외를 한 곳에서 관리할 수 있게 해준다.
2. @ControllerAdivce 와 @RestControllerAdivce 에 차이점
@ControllerAdvice와 @RestControllerAdvice 차이점
- @ControllerAdvice : 이 어노테이션은 주로 @Controller 또는 @RequestMapping 어노테이션이 적용된 클래스(즉, MVC 컨트롤러)에서 발생하는 예외를 처리하기 위해 사용됩니다. HTML 뷰를 반환하는 전통적인 웹 애플리케이션에서 주로 사용된다.
- @RestControllerAdvice : @ControllerAdvice와 유사한 기능을 제공하지만, @RestController에서 발생하는 예외를 처리하는 데 특화되어 있다. 즉, RESTful 웹 서비스에서 JSON이나 XML 같은 응답을 반환할 때 사용된다. 사실상, @RestControllerAdvice는 @ControllerAdvice에 @ResponseBody를 추가한 것과 동일한 효과를 제공하여, 응답 본문에 직접 데이터를 매핑할 수 있다.
@ControllerAdvice와 @RestControllerAdvice 차이점
- @ControllerAdvice : 이 어노테이션은 주로 @Controller 또는 @RequestMapping 어노테이션이 적용된 클래스(즉, MVC 컨트롤러)에서 발생하는 예외를 처리하기 위해 사용된다. HTML 뷰를 반환하는 전통적인 웹 애플리케이션에서 주로 사용된다.
- @RestControllerAdvice: @ControllerAdvice와 유사한 기능을 제공하지만, @RestController에서 발생하는 예외를 처리하는 데 특화되어 있다. 즉, RESTful 웹 서비스에서 JSON이나 XML 같은 응답을 반환할 때 사용된다. 사실상, @RestControllerAdvice는 @ControllerAdvice에 @ResponseBody를 추가한 것과 동일한 효과를 제공하여, 응답 본문에 직접 데이터를 매핑할 수 있다.
예시 코드 확인 1 - ControllerAdvice 에서 데이터를 반환하는 예시 코드
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = ResourceNotFoundException.class)
@ResponseBody
public ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex) {
// 에러 메시지와 함께 404 상태 코드를 반환
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
예시 코드 확인 2 - RestControllerAdvice 에서 반환
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = ResourceNotFoundException.class)
// @ResponseBody 사용 안해도 됨(데이터를 반환 처리 함)
public ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex) {
// 에러 메시지와 함께 404 상태 코드를 반환
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
3. 사용자 정의 예외 클래스를 만들기
RuntimeException은 프로그램의 실행 도중 발생하는 예외를 나타낸다. 컴파일 시 예외가 아닌 실행 시 예외로 분류되며, 따로 try-catch 블록으로 처리하지 않아도 컴파일러가 오류로 인식하지 않는다.
예시로는 NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException 등이 있다. 여기서는 RuntimeException 확장해서 사용자 정의 예외 클래스를 만들어 보자.
위 그림처럼 패키지와 자바 파일을 만들자 ( handler/GlobalControllerAdivce.java 파일 생성 )
- handler/GlobalControllerAdivce.java 파일 생성
- handler/exception 패키지 생성
- handler/exception/UnAuthorizedException 자바 파일 생성
: UnAuthorizedException 클래스는 인증이 안된 사용자가 인증이 필요하 서비스에 접근 요청을 할 때 예외를 발생 시킬 사용자 정의 예외 클래스를 설계한다.
package com.tenco.bank.handler.exception;
import org.springframework.http.HttpStatus;
import lombok.Getter;
@Getter
public class UnAuthorizedException extends RuntimeException {
private HttpStatus status;
// throw new UnAuthorizedException( , )
public UnAuthorizedException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
package com.tenco.bank.handler.exception;
import org.springframework.http.HttpStatus;
import lombok.Getter;
// 에러 발생시에 여러 페이지로 이동 시킬 때 사용 예정
@Getter
public class RedirectException extends RuntimeException {
private HttpStatus status;
// throw new RedirectException(???, ???);
public RedirectException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
package com.tenco.bank.handler.exception;
import org.springframework.http.HttpStatus;
import lombok.Getter;
@Getter
public class DataDeliveryException extends RuntimeException {
private HttpStatus status;
public DataDeliveryException(String message, HttpStatus status) {
super(message);
this.status = status;
}
}
4. @ControllerAdvice 구현해 보기
package com.tenco.bank.handler;
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;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
@ControllerAdvice // IoC 대상 (싱글톤 패턴) --> HTML 렌더링 예외에 많이 사용
public class GlobalControllerAdvice {
/**
* (개발시에 많이 활용)
* 모든 예외 클래스를 알 수 없기 때문에 로깅으로 확인할 수 있도록 설정
* 로깅처리 - 동기적 방식(System.out.println), @slf4j (비동기 처리 됨)
*/
@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("----------------------");
}
/**
* Data로 예외를 내려주는 방법
* @ResponseBody 활용
* 브라우저에서 자바스크립트 코드로 동작 하게 됨
*/
// 예외를 내릴 때 데이터를 내리고 싶다면 1. @RestControllerAdvice 를 사용하면 된다.
// 단. @ControllerAdvice 사용하고 있다면 @ResponseBody 를 붙여서 사용하면 된다.
@ResponseBody
@ExceptionHandler(DataDeliveryException.class)
public String dataDeleveryExcption(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();
}
@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();
}
/*
* 에러 페이지로 이동 처리
* JSP로 이동시 데이터를 담아서 보내는 방법
* ModelAndView, Model 사용 가능
* throw new RedirectException('페이지 없는데???', 404);
*/
@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; // 페이지 반환 + 데이터 내려줌
}
}
5. 에러 페이지 생성 ( errorPage.jsp )
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${statusCode} Error - Page Not Found</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
<style type="text/css">
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">
<div class="text-center">
<h1>${statusCode}</h1>
<p class="error--message"> Page Not Found </p>
<p>${message}</p>
<a href="/index" class="btn btn-primary">Go to Home Page</a>
</div>
</div>
</body>
</html>
6. 직업 예외 발생해보기
package com.tenco.bank.controller;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.handler.exception.UnAuthorizedException;
import jakarta.annotation.PostConstruct;
@Controller // IoC 대상(싱글톤 패턴 관리가 된다.) --> 제어의 역전
public class MainController {
// REST API 기반으로 주소설계 가능
// // http:localhost:8080/main/main-page
// 주소설계
// http:localhost:8080/main-page
@GetMapping({"/main-page", "/index"})
// @ResponseBody
public String mainPage() {
System.out.println("mainPage() 호출 확인");
// [JSP 파일 찾기 (yml 설정) ] - 뷰 리졸버
// prefix: /WEB-INF/view
// /main
// suffix: .js
return "main";
}
// todo - 삭제 예정
// 주소 설계
// http://localhost:8080/error-test1/true
// http://localhost:8080/error-test1/false
@GetMapping("/error-test1")
public String errorPage() {
if(true) {
throw new RedirectException("잘못된 요청입니다.", HttpStatus.NOT_FOUND);
}
return "main";
}
// http://localhost:8080/error-test2
@GetMapping("/error-test2")
public String errorData2() {
if(true) {
throw new DataDeliveryException("잘못된 데이터 입니다", HttpStatus.BAD_REQUEST);
}
return "main";
}
@GetMapping("/error-test3")
public String errorData3() {
if(true) {
throw new UnAuthorizedException("인증 안된 사용자 입니다.", HttpStatus.UNAUTHORIZED);
}
return "main";
}
}
HTTP 상태 코드란?
HTTP(Hypertext Transfer Protocol)는 웹 서버와 웹 클라이언트 사이에서 데이터를 주고받기 위해 사용하는 통신 방식으로 TCP/IP 프로토콜 위에서 동작한다. 즉, 우리가 웹을 이용하려면 웹 서버와 웹 클라이언트는 각각 TCP/IP 동작에 필수적인 IP 주소를 가져야 한다는 의미이다.
HTTP란 이름대로라면 하이퍼텍스트(Hypertext)만 전송할 수 있어 보이지만, 실제로는 HTML이나 XML과 같은 하이퍼텍스트뿐 아니라 이미지, 음성, 동영상, Javascript, PDF와 각종 문서 파일 등 컴퓨터에서 다룰 수 있는 데이터라면 무엇이든 전송할 수 있다.
우리가 웹 브라우저의 주소창에 https://www.naver.com을 입력하고 Enter 를 누르면 웹 클라이언트와 웹 서버 사이에 HTTP 연결이 맺어지고 웹 클라이언트는 웹 서버에 HTTP 요청 메시지를 보낸다. 웹 서버는 요청에 따른 처리를 진행한 후에 그 결과를 웹 클라이언트에 HTTP 응답 메시지로 보낸다.
100~500번 대까지 상태 코드 정의
- 1XX: Informational (정보 제공)
- 임시 응답으로 현재 클라이언트의 요청까지는 처리되었으니 계속 진행하라는 의미이다. HTTP 1.1 버전부터 추가되었다.
- 2XX: Success (성공)
- 클라이언트의 요청이 서버에서 성공적으로 처리되었다는 의미이다.
- 3XX: Redirection (리다이렉션)
- 완전한 처리를 위해서 추가 동작이 필요한 경우이다. 주로 서버의 주소 또는 요청한 URI의 웹 문서가 이동되었으니 그 주소로 다시 시도하라는 의미이다.
- 4XX: Client Error (클라이언트 에러)
- 없는 페이지를 요청하는 등 클라이언트의 요청 메시지 내용이 잘못된 경우를 의미한다.
- 5XX: Server Error (서버 에러)
- 서버 사정으로 메시지 처리에 문제가 발생한 경우이다. 서버의 부하, DB 처리 과정 오류, 서버에서 익셉션이 발생하는 경우를 의미한다.
주요 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 boot > Bank App 만들기(deployment)' 카테고리의 다른 글
Bank App 만들기 ( deployment ) - MyBatis 설정 ( DB 접근 기술이란? ) (0) | 2024.08.07 |
---|---|
Bank App 만들기 ( deployment ) - 어노테이션 정리 (0) | 2024.08.07 |
Bank App 만들기 ( deployment ) - MainController, mainPage.jsp 구현 (1) | 2024.08.07 |
Bank App 만들기 ( deployment ) - 화면 구현2 ( 레이아웃 분리 ) (0) | 2024.08.07 |
Bank App 만들기 ( deployment ) - 화면 구현1 ( 템플릿 가져오기 ) (0) | 2024.08.07 |