본문 바로가기

Spring boot/Bank App 만들기(deployment)

Bank App 만들기 ( deployment ) - Exception Handler 처리 ( HTTP 상태 코드가 뭘까? )

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 서비스 제공불가 서버가 일시적으로 요청을 처리할 수 없는 상태임. 일반적으로 서버 과부하나 유지 보수로 인해 일시적으로 서비스를 제공할 수 없을 때 반환됨.

 

728x90