일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- mybatis
- eGov
- 개념
- 과정평가형
- input
- array
- Database
- jQuery
- TO_DATE
- 배열
- JVM
- Java
- 함수
- controller
- html
- Ajax
- POI
- was
- json
- spring
- 태그
- Oracle
- javascript
- select
- web.xml
- sql
- CSS
- 암호화
- 오류
- eGovFramework
- Today
- Total
web developer
[java] try-catch를 이용한 직접 예외 처리와 @ControllerAdvice를 통한 공통 예외 처리 본문
[java] try-catch를 이용한 직접 예외 처리와 @ControllerAdvice를 통한 공통 예외 처리
trueman 2024. 9. 19. 13:391. 서비스 단에서 커스텀 예외와 일반적인 예외를 throw
서비스 계층에서는 예외를 발생시키기만 하고, 컨트롤러에서 처리하도록 합니다.
- 커스텀 예외 : 비즈니스 로직에 맞는 예외를 정의하고 서비스에서 명시적으로 발생시킬 수 있습니다.
- 일반적인 예외 : Java 표준 예외나 라이브러리에서 제공하는 예외를 발생시킬 수 있습니다. 예를 들어, 데이터 검증이나 null 처리 같은 경우에는 *IllegalArgumentException*이나 *NullPointerException*을 사용합니다.
@Service
public class UserService {
public User getUserById(Long userId) {
if (userId == null) {
// 일반적인 예외 throw
throw new IllegalArgumentException("사용자 ID는 null일 수 없습니다.");
}
User user = userRepository.findById(userId);
if (user == null) {
// 커스텀 예외 throw
throw new UserNotFoundException("사용자를 찾을 수 없습니다: " + userId);
}
return user;
}
public void updateUser(User user) {
if (user == null) {
// 일반적인 예외 throw
throw new NullPointerException("업데이트할 사용자 정보가 null입니다.");
}
// 업데이트 로직...
}
}
2. Controller에서 직접 try-catch를 통한 예외 처리
Controller에서는 서비스에서 발생한 예외 중 중요한 예외만 처리합니다. 예를 들어, 사용자에게 특정한 메시지를 반환해야 하거나, 특정 상황에서만 처리해야 하는 예외를 Controller에서 잡아 처리합니다
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
try {
User user = userService.getUserById(id);
return new ResponseEntity<>(user, HttpStatus.OK);
} catch (UserNotFoundException e) {
// 사용자에게 보여줄 예외는 여기서 처리
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
}
예를 들어 AJAX 요청에 대한 서버 측의 에러 처리 방식에 대해서 직접 try-catch를 통해 에러를 처리하고 리다이렉트하거나 에러 페이지를 전송하는 경우는 다음과 같이 처리된다.
- 서버가 클라이언트로 HTTP 응답을 반환할 때 발생하는 HTTP 상태 코드에 맞게 web.xml에서 정의한 에러 페이지로 리다이렉트
- 웹 애플리케이션에서 서버 측의 에러를 특정 HTTP 상태 코드에 매핑하여, 에러 페이지로 리다이렉트하는 방식입니다. 이 방식은 web.xml에 정의된 에러 페이지 매핑을 활용합니다. try-catch를 사용하여 수동으로 에러를 처리하고 리다이렉트하는 대신, web.xml 설정을 통해 자동으로 에러 페이지로 이동하게 됩니다.
- SQL 데이터베이스 관련 에러 발생 시 response.sendError를 통해 에러 페이지로 이동
- try-catch 블록 내에서 예외를 처리하고, response.sendError를 호출하여 특정 에러 페이지로 리다이렉트하는 방식입니다. 이 방법은 특정 예외를 잡아내고, 이를 클라이언트에게 전송하여 적절한 에러 페이지를 표시하도록 합니다.
3. @ControllerAdvice를 통한 공통 예외 처리
- @ControllerAdvice: 모든 컨트롤러에서 발생하는 예외를 전역적으로 처리할 수 있는 클래스입니다. 이를 통해 여러 컨트롤러에서 공통적인 예외 처리 로직을 구현할 수 있습니다.
- @ExceptionHandler: 특정 예외가 발생했을 때 호출되는 메서드를 정의합니다. 이 메서드는 예외 타입에 따라 호출되어 적절한 처리를 할 수 있습니다.
3-1. web.xml 설정 확인
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Spring의 DispatcherServlet이 설정되어 있어야 합니다. 이는 Spring MVC 컨텍스트에서 필수적이며, 보통 web.xml 파일에 설정되어 있습니다.
3-2. dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 어노테이션 기반의 Spring MVC 설정 -->
<mvc:annotation-driven />
<!-- 뷰 리졸버, 메시지 컨버터, 인터셉터 등의 추가 설정 -->
</beans>
해당 파일에 <mvc:annotation-driven />을 추가하면, Spring MVC의 어노테이션 기반 기능(@Controller, @RequestMapping, @ExceptionHandler, @ControllerAdvice 등)이 활성화됩니다.
3-3. pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.25.RELEASE</version>
</dependency>
pom.xml에서 Spring의 버전에 따른 의존성 주입은 프로젝트의 Spring 버전에 맞춰서 이루어져야 합니다.
3-4. AbstractAnnotationExceptionHandler 클래스를 상속받아 @ControllerAdvice 구현
이 클래스를 상속받아 표준 프레임워크에서 정의한 예외들을 처리할 수 있습니다. AbstractAnnotationExceptionHandler는 예외 처리 메서드를 구현할 때, 공통된 처리를 위한 기본적인 기능을 제공합니다.
GlobalExceptionHandler.java [ver1]
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.ui.Model;
import com.example.framework.AbstractAnnotationExceptionHandler;
import com.example.framework.EgovBizException;
import com.example.framework.FdlException;
@ControllerAdvice
public class GlobalExceptionHandler extends AbstractAnnotationExceptionHandler {
@ExceptionHandler(EgovBizException.class)
public String handleEgovBizException(EgovBizException ex, Model model) {
model.addAttribute("errorMessage", "Business Error: " + ex.getMessage());
return "bizErrorPage";
}
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(RuntimeException ex, Model model) {
model.addAttribute("errorMessage", "Runtime Error: " + ex.getMessage());
return "runtimeErrorPage";
}
@ExceptionHandler(FdlException.class)
public String handleFdlException(FdlException ex, Model model) {
model.addAttribute("errorMessage", "FDL Error: " + ex.getMessage());
return "fdlErrorPage";
}
@ExceptionHandler(Exception.class)
public String handleGenericException(Exception ex, Model model) {
model.addAttribute("errorMessage", "Unknown Error: " + ex.getMessage());
return "genericErrorPage";
}
}
@ExceptionHandler 메서드는 각 예외 타입에 대해 처리 메서드를 정의합니다. model.addAttribute()를 사용하여 에러 메시지를 뷰에 전달하고, 적절한 에러 페이지를 반환합니다.
GlobalExceptionHandler.java [ver2]
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.bind.annotation.ResponseStatus;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(EgovBizException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorResponse handleEgovBizException(EgovBizException ex) {
logger.error("EgovBizException 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("처리 중 문제가 발생했습니다. 다시 시도해 주세요.");
}
@ExceptionHandler(DataAccessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleDataAccessException(DataAccessException ex) {
logger.error("DataAccessException 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("데이터베이스 오류가 발생했습니다. 나중에 다시 시도해 주세요.");
}
@ExceptionHandler(FdlException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleFdlException(FdlException ex) {
logger.error("FdlException 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("시스템 설정 오류가 발생했습니다. 나중에 다시 시도해 주세요.");
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleGenericException(Exception ex) {
logger.error("Unexpected Exception 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("예상치 못한 오류가 발생했습니다. 나중에 다시 시도해 주세요.");
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNoHandlerFoundException(NoHandlerFoundException ex) {
logger.error("NoHandlerFoundException 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("찾을 수 없는 페이지입니다.");
}
}
에러 페이지를 반환하지 않고, 에러를 사용자와 기술자에게 반환합니다.
ErrorResponse.java
public class ErrorResponse {
private String userMessage; // 사용자에게 표시할 메시지
// 생성자
public ErrorResponse(String userMessage) {
this.userMessage = userMessage;
}
// 사용자에게 보여줄 메시지
public String getUserMessage() {
return userMessage;
}
public void setUserMessage(String userMessage) {
this.userMessage = userMessage;
}
}
오류 응답을 사용자에게 전달하기 위한 간단한 ErrorResponse 클래스입니다.
4. 테스트
ErrorResponse.java
public class ErrorResponse {
private String userMessage; // 사용자에게 표시할 메시지
// 생성자
public ErrorResponse(String userMessage) {
this.userMessage = userMessage;
}
// 사용자에게 보여줄 메시지
public String getUserMessage() {
return userMessage;
}
public void setUserMessage(String userMessage) {
this.userMessage = userMessage;
}
}
GlobalExceptionHandler.java
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(HttpStatus.CONFLICT)
@ResponseBody
public ErrorResponse handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
logger.error("[GlobalExceptionHandler][test] | DataIntegrityViolationException 발생: ", ex); // 기술적 정보 로그
return new ErrorResponse("[GlobalExceptionHandler][test] | Data integrity error");
}
}
EgovController.java
@GetMapping("/exception.do")
public String exception1(){
throw new DataIntegrityViolationException("[EgovController][test] | Duplicate user found");
}
log
2024-09-19 21:25:20,816 DEBUG [org.springframework.web.servlet.DispatcherServlet] DispatcherServlet with name 'action' processing GET request for [/exception.do]
2024-09-19 21:25:20,817 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Looking up handler method for path /exception.do
2024-09-19 21:25:20,823 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] Returning handler method [public java.lang.String board.sample.web.EgovSampleController.exception1()]
2024-09-19 21:25:20,823 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'egovSampleController'
2024-09-19 21:25:20,825 DEBUG [org.springframework.web.servlet.DispatcherServlet] Last-Modified value for [/exception.do] is: -1
2024-09-19 21:25:20,849 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] Returning cached instance of singleton bean 'globalExceptionHandler'
2024-09-19 21:25:20,849 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver] Invoking @ExceptionHandler method: public board.cmmn.ErrorResponse board.cmmn.GlobalExceptionHandler.handleDataIntegrityViolationException(org.springframework.dao.DataIntegrityViolationException)
2024-09-19 21:25:20,849 ERROR [board.cmmn.GlobalExceptionHandler] [GlobalExceptionHandler][test] | DataIntegrityViolationException 발생:
org.springframework.dao.DataIntegrityViolationException: [EgovController][test] | Duplicate user found
at board.sample.web.EgovSampleController.exception1(EgovSampleController.java:2930) ~[classes/:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:854) ~[spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:765) ~[spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) [spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) [spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:489) [servlet-api.jar:?]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:583) [servlet-api.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:212) [catalina.jar:8.5.91]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:156) [catalina.jar:8.5.91]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) [tomcat-websocket.jar:8.5.91]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:181) [catalina.jar:8.5.91]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:156) [catalina.jar:8.5.91]
at egovframework.rte.ptl.mvc.filter.HTMLTagFilter.doFilter(HTMLTagFilter.java:51) [egovframework.rte.ptl.mvc-3.10.0.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:181) [catalina.jar:8.5.91]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:156) [catalina.jar:8.5.91]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:181) [catalina.jar:8.5.91]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:156) [catalina.jar:8.5.91]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) [catalina.jar:8.5.91]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [catalina.jar:8.5.91]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) [catalina.jar:8.5.91]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [catalina.jar:8.5.91]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [catalina.jar:8.5.91]
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:682) [catalina.jar:8.5.91]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:8.5.91]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:8.5.91]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:617) [tomcat-coyote.jar:8.5.91]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-coyote.jar:8.5.91]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:932) [tomcat-coyote.jar:8.5.91]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1694) [tomcat-coyote.jar:8.5.91]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-coyote.jar:8.5.91]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-util.jar:8.5.91]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-util.jar:8.5.91]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.5.91]
at java.lang.Thread.run(Thread.java:834) [?:?]
2024-09-19 21:25:21,017 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor] Written [board.cmmn.ErrorResponse@446e0fd9] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@4be59e33]
2024-09-19 21:25:21,017 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver] Resolved [org.springframework.dao.DataIntegrityViolationException: [EgovController][test] | Duplicate user found]
2024-09-19 21:25:21,017 DEBUG [org.springframework.web.servlet.DispatcherServlet] Null ModelAndView returned to DispatcherServlet with name 'action': assuming HandlerAdapter completed request handling
2024-09-19 21:25:21,017 DEBUG [org.springframework.web.servlet.DispatcherServlet] Successfully completed request
view 화면
참조
- https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte:bsl:exception_handling
- https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte:fdl:aop:egovrteaopguide
- https://incheol-jung.gitbook.io/docs/q-and-a/spring/controlleradvice-exceptionhandler
'Language > Java' 카테고리의 다른 글
[java] Form 전송 시 트랜잭션 예외 발생에 따른 PrintWriter를 통한 업데이트 실패 알림 및 페이지 이동 처리 (0) | 2024.10.29 |
---|---|
[java] AJAX 통신에서 Impl의 트랜잭션 예외 발생 시 예외 처리와 롤백 처리 (0) | 2024.09.24 |
[java] JSON 데이터 파싱 (1) | 2024.09.10 |
[java] custom 페이징 처리와 페이지 네비게이션 구현 (0) | 2024.08.09 |
[java] SHA-256 암호화 [단방향] (0) | 2024.07.23 |