웹 애플리케이션을 운영하면서 발생되는 주요 에러들을 효율적으로 처리하고자, 필요한 개념들을 정리.
Front Controller 패턴은 웹 애플리케이션에서 사용되는 디자인 패턴 중 하나로, 클라이언트로부터 들어오는 모든 요청을 한 곳에서 중앙 집중적으로 처리하는 아키텍처 패턴입니다. 이 패턴은 중앙 집중적인 컨트롤러가 모든 요청을 받고, 해당 요청을 처리하기 위해 적절한 처리 로직을 호출하거나 분배하는 구조를 가지고 있습니다.
1-1. Front Controller 구성 요소
Front Controller: 애플리케이션의 진입점으로서, 클라이언트로부터 들어오는 모든 요청을 처리하는 중앙 컨트롤러입니다. 웹 애플리케이션의 DispatcherServlet이나 서블릿이 이 역할을 수행하는 것이 일반적입니다.
Handler: Front Controller가 요청을 처리하기 위해 호출하는 구성 요소로서, 실제로 요청을 처리하고 비즈니스 로직을 실행하는 컴포넌트입니다. 핸들러는 각각의 요청 유형(예: URL 경로, HTTP 메서드)에 따라 다른 처리 로직을 가질 수 있습니다.
View: 핸들러가 처리한 결과를 표시하는 데 사용되는 사용자 인터페이스 컴포넌트입니다. 주로 HTML 페이지 또는 JSON/XML 응답으로 표현될 수 있습니다.
1-2. Front Controller 패턴 이점
중복 코드 제거: 모든 요청 처리 로직을 중앙 집중적으로 관리하므로 중복 코드를 최소화할 수 있습니다.
유연성: 새로운 요청 유형이 추가되거나 기존 요청의 처리 방식이 변경되더라도 Front Controller만 수정하면 됩니다.
보안: 애플리케이션의 진입점을 중앙에서 관리하기 때문에 보안을 강화할 수 있습니다.
Java에서 Front Controller 패턴을 구현하기 위해서는 주로 Servlet 또는 Spring MVC의 DispatcherServlet을 사용합니다. 이들은 모든 요청을 중앙에서 받아 처리하고, 해당 요청을 처리하는 적절한 핸들러를 호출하여 애플리케이션 로직을 실행하고, 그 결과를 사용자에게 표시하는 역할을 수행합니다.
DispatcherServlet은 웹 애플리케이션의 진입점이며, 클라이언트로부터 들어오는 모든 요청을 처리하는 중앙 컨트롤러 역할을 합니다. 이후에는 Spring 구성 파일에 따라 요청을 처리하는데 필요한 각각의 구성 요소를 찾아내고 실행합니다. 이를 통해 Spring MVC는 유연하고 확장 가능한 웹 애플리케이션을 만들 수 있게 됩니다.
DispatcherServlet을 사용하면 애플리케이션의 모든 클라이언트 요청을 하나의 진입점(Front Controller)에서 처리하므로, 요청 처리 과정을 중앙에서 효과적으로 제어할 수 있고, 애플리케이션의 구조를 효율적으로 관리할 수 있습니다. 또한, 필요에 따라서 Interceptor, Handler Mapping, View Resolver 등을 활용하여 요청 처리를 세부적으로 커스터마이징할 수 있습니다. 이렇게 DispatcherServlet을 통해 Front Controller 패턴을 적용하는 것은 Spring Web MVC의 핵심 개념 중 하나이며, 많은 웹 애플리케이션에서 사용되고 있습니다.
2-1. Spring MVC 흐름

2-2. DispatcherServlet Diagram

HttpServlet.service() -> FrameworkServlet.service() -> HttpServlet.service() -> FrameworkServlet.doXXX(), processRequest(), doService() -> DispatcherServlet.doService(), doDispatch()
DispatcherServelt doDispatch
mappedHandler = getHandler(processedRequest); : HandlerMapping 조회 (mapperHandler)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); : HandlerAdapter 조회 (ha)
ModelAndView mv = null; mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); : Controller 호출

3-1. Filter
Filter는 Servlet 스펙의 일부로서, Servlet의 요청 및 응답 처리에 개입하여 전/후 처리 작업을 수행할 수 있습니다. 일반적으로 Filter는 요청이 Servlet에 도달하기 전에 사전 처리를 수행하거나, Servlet이 요청을 처리한 후에 사후 처리를 수행하는데 사용됩니다. 이를 통해 공통적인 기능을 중앙에서 처리하거나, 요청과 응답에 대해 추가적인 작업을 수행할 수 있습니다. 또한 필터(Filter)는 JavaEE 표준 스펙 기능으로 Dispatcher Servlet에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대해 부가 작업을 처리할 수 있는 기능을 제공합니다.

3-2. Filter 메소드
init()
Filter 인스턴스가 초기화될 때 호출.
웹 애플리케이션이 시작될 때, 서블릿 컨테이너에 의해 한 번만 호출한다.
보통 필터 초기화에 필요한 설정이나 리소스 로딩 등을 수행하는데 사용.
FilterConfig 객체를 인자로 받는다.
doFilter()
실제 filter 동작이 수행되는 메서드.
모든 요청과 응답이 발생할 때마다 호출한다.
fitler chain 의 다음 filter(or servlet)로 요청과 응답을 전달하거나, 필터가 직접 응답을 생성할 수 있음.
filter chain 다음 요소로 넘기기 위해 FilterChain의 doFilter() 메서드를 호출.
ServletRequest, ServletResponse, FilterChain 객체를 인자로 받는다.
destroy()
filter 인스턴스가 소멸될 때 호출.
웹 애플리케이션이 종료될 때, 서블릿 컨테이너에 의해 한 번만 호출한다.
보통 필터에서 사용한 리소스를 해제하거나 정리하는데 사용.
init() 메서드에서 초기화한 리소스들의 정리를 수행하는 것이 일반적이다.
3-3. Interceptor
스프링(Spring) Interceptor(인터셉터)는 스프링 프레임워크에서 웹 애플리케이션의 요청과 응답을 가로채는 기능을 제공하는 인터페이스입니다. Interceptor를 사용하면 웹 애플리케이션의 요청과 응답을 사전 또는 사후 처리하고, 특정 작업을 수행할 수 있습니다. Interceptor는 주로 요청에 대한 보안 검사, 로깅, 트랜잭션 관리 등과 같은 공통 기능을 구현하는데 사용됩니다.
Interceptor는 주로 MVC 패턴에서 컨트롤러(Controller)와 뷰(View) 사이에서 작동하며, 요청이 컨트롤러에 도달하기 전에 사전 처리를 수행하거나, 컨트롤러가 요청을 처리한 후에 사후 처리를 수행하는 역할을 합니다. 이로 인해 중복 코드를 줄이고 애플리케이션 전반에 걸쳐 공통적인 작업을 쉽게 구현할 수 있습니다.
3-4. Interceptor 메소드
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
컨트롤러 메서드가 실행되기 전에 호출.
반환값으로 true를 반환하면 정상적으로 컨트롤러 메서드가 실행되며, false를 반환하면 컨트롤러 메서드를 실행하지 않고 요청을 종료.
인증, 권한 검사 등 수행 가능.
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
컨트롤러 메서드가 실행된 후, 뷰가 렌더링 되기 전에 호출.
주로 모델 데이터를 가공하거나 추가 작업을 수행하는데 사용한다.
ModelAndView 객체를 통해 컨트롤러에서 뷰로 전달된 모델 데이터에 접근.
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
요청의 처리가 완료된 후에 호출.
뷰가 렌더링된 후에 실행되며, 주로 리소스 정리나 로깅 작업 등을 수행하는데 사용.
Exception 객체를 통해 요청 처리 중 발생한 예외에 대한 정보에 접근.
3-5. DispatcherServlet과 Filter
DispatcherServlet과 Filter는 스프링 MVC의 요청 처리 흐름에서 각각 다른 역할을 수행하며, 함께 협력하여 클라이언트의 요청을 처리합니다.
순서: DispatcherServlet은 요청 처리를 위해 Filter 체인보다 나중에 실행됩니다. 즉, Filter가 먼저 클라이언트의 요청을 가로채서 필터 체인을 통해 전처리 작업을 수행하고, 그 다음 DispatcherServlet이 해당 요청을 컨트롤러에게 전달하여 처리합니다.
협력: Filter와 DispatcherServlet은 함께 클라이언트의 요청 처리를 돕기 위해 협력합니다. Filter는 DispatcherServlet 이전에 요청을 가로채서 필요한 전처리 작업을 수행하고, DispatcherServlet은 필터 체인의 나머지 필터를 거쳐서 컨트롤러에게 적절한 요청을 전달합니다.
동시 사용: 스프링 애플리케이션에서는 DispatcherServlet과 함께 필터(Filter)를 사용하여 클라이언트의 요청을 처리하는 것이 일반적입니다. 각각의 역할에 따라 DispatcherServlet은 컨트롤러와 뷰와의 통신을 담당하고, Filter는 요청과 응답에 대한 필터링 작업을 담당합니다.
요약하면, DispatcherServlet과 Filter는 서로 다른 역할을 하며, 서로 협력하여 스프링 MVC의 클라이언트 요청 처리 흐름을 구성하는 데 기여합니다. DispatcherServlet은 컨트롤러와 뷰와의 통신을 관리하고, Filter는 요청과 응답에 대한 필터링 작업을 수행하여 웹 애플리케이션의 동작을 보완합니다.
3-6. DispatcherServlet과 Interceptor
DispatcherServlet과 Interceptor의 연관성은 다음과 같습니다:
처리 순서: DispatcherServlet이 요청을 처리하는 과정 중에 Interceptor가 개입될 수 있습니다. DispatcherServlet이 컨트롤러를 호출하기 전에 preHandle() 메서드가 실행되고, 컨트롤러가 처리한 후 postHandle() 메서드가 실행되며, 응답이 클라이언트로 전송된 이후 afterCompletion() 메서드가 실행됩니다.
요청 전후 처리: Interceptor를 사용하여 DispatcherServlet에서 처리되기 전에 요청의 전처리 작업을 수행할 수 있으며, 컨트롤러의 처리 결과를 후처리 작업으로 가공하거나 추가적인 작업을 수행할 수 있습니다.
공통 기능의 분리: Interceptor를 사용하여 DispatcherServlet에서 공통적으로 필요한 기능들을 분리하여 구현할 수 있습니다. 예를 들어, 로깅, 권한 검사, 트랜잭션 관리 등과 같은 횡단 관심사를 Interceptor로 처리할 수 있습니다.
적용 범위: DispatcherServlet은 모든 클라이언트 요청에 대해 동작하며, Interceptor 역시 모든 컨트롤러에 대해 공통 기능을 적용하므로, 전역적인 기능을 구현하는 데 활용됩니다.
요약하면, DispatcherServlet과 Interceptor는 스프링 MVC에서 클라이언트의 요청 처리 흐름을 함께 구성하는데 기여합니다. DispatcherServlet이 모든 요청을 중앙에서 처리하고 컨트롤러로 요청을 전달하는 역할을 하며, Interceptor는 DispatcherServlet의 처리 전후에 공통 기능을 추가하고, 횡단 관심사를 처리하는 역할을 합니다. 이 두 요소는 스프링 MVC의 핵심 개념 중 하나이며, 웹 애플리케이션의 요청 처리를 효율적으로 제어하는 데 사용됩니다.
3-7. Filter와 Interceptor
스프링에서 Filter와 Interceptor는 모두 웹 애플리케이션에서 요청과 응답을 처리하는 데 사용되는 기능입니다. 그러나 두 가지는 목적과 작동 방식에서 차이가 있습니다.
Filter
서블릿 컨테이너에서 동작하는 기능으로, 클라이언트가 서블릿에 도달하기 전과 후에 요청 및 응답을 수정, 변환 또는 감시할 수 있습니다.
보통, 인증, 로깅, 문자 인코딩 변환 등과 같은 일반적인 태스크를 수행하는 데 사용됩니다.
Filter는 웹 애플리케이션 내에서 전역적으로 적용되며, web.xml 파일이나 자바 기반 설정으로 등록됩니다.
Interceptor
스프링 프레임워크에서 동작하는 기능으로, 스프링의 컨트롤러 수행 전, 후 또는 수행 도중에 요청과 응답을 가로채고 수정할 수 있습니다.
주로 특정 컨트롤러의 호출 전후에 공통적인 로직을 적용하는 데 사용됩니다.
Interceptor는 스프링 빈으로 정의되며, HandlerInterceptor 인터페이스를 구현하여 만듭니다.
요약하면, Filter는 서블릿 컨테이너에서 동작하며 전역적으로 적용되는 반면, Interceptor는 스프링 프레임워크에서 동작하며 컨트롤러 호출 전후에 적용되는 차이가 있습니다. 선택 시점과 목적에 따라 두 가지를 적절히 사용하여 웹 애플리케이션을 개발하면 됩니다.
3-8. Interceptor와 AOP
스프링에서 Interceptor와 AOP(Aspect-Oriented Programming)는 모두 횡단 관심사(Cross-cutting Concerns)를 처리하기 위한 기술이지만, 목적과 적용 방식에서 차이가 있습니다.
Interceptor
Interceptor는 Spring Framework에서 제공하는 기능 중 하나로, 스프링 MVC에서 사용됩니다.
컨트롤러의 요청 전후에 공통적인 기능을 수행하기 위해 사용됩니다.
HandlerInterceptor 인터페이스를 구현하여 사용하며, preHandle(), postHandle(), afterCompletion()과 같은 메서드를 오버라이드하여 요청 전후에 수행할 작업을 구현합니다.
주로 로깅, 권한 검사, 트랜잭션 관리 등과 같이 컨트롤러에서 발생하는 횡단 관심사를 처리하기 위해 사용됩니다.
컨트롤러 수준에서만 동작하며, 컨트롤러 내부에서만 호출되는 메서드에 대해 적용됩니다.
AOP (Aspect-Oriented Programming)
AOP는 애플리케이션의 핵심 비즈니스 로직과는 별도로, 횡단 관심사를 분리하여 모듈화하는 프로그래밍 패러다임입니다.
애플리케이션 전체의 여러 모듈에서 반복되는 공통 기능(로깅, 보안, 트랜잭션 등)을 관리하는데 사용됩니다.
AOP는 프록시를 사용하여 핵심 로직과 횡단 관심사를 분리하고, 공통 모듈(Aspect)로 모듈화하여 재사용성과 유지보수성을 높입니다.
메서드 실행 전, 후, 예외 발생 시 등과 같은 특정 지점(Pointcut)에서 공통 기능(Advice)을 실행합니다.
프록시를 통해 핵심 로직의 메서드 호출을 감싸서 필요한 시점에 횡단 관심사를 적용합니다.
요약하자면 Interceptor는 주로 스프링 MVC에서 컨트롤러의 요청 전후에 공통 기능을 처리하기 위해 사용되는 기능이며, 컨트롤러 수준에서 동작합니다. AOP는 애플리케이션 전체에서 공통 기능을 분리하여 처리하기 위해 사용되는 프로그래밍 패러다임으로, 핵심 비즈니스 로직과는 별도로 모듈화됩니다.
3-9. Resolver
스프링 프레임워크에서 사용하는 Resolver 패턴은 주로 다양한 종류의 이름을 다른 객체나 값과 매핑하는 데 사용됩니다. 이러한 Resolver 패턴들은 주로 스프링의 다양한 모듈과 기능을 구현하는 데 활용됩니다. 일반적으로 스프링에서 사용되는 Resolver 패턴에는 다음과 같은 것들이 있습니다:
View Resolver : View Resolver는 웹 애플리케이션에서 사용되는 Resolver로, 뷰 이름과 실제 뷰 템플릿과의 매핑을 담당합니다. 주로 스프링 MVC에서 사용되며, 컨트롤러에서 반환한 뷰 이름을 기반으로 실제 뷰 템플릿을 찾아서 렌더링합니다.
Locale Resolver : Locale Resolver는 다국어 지원을 위해 사용되는 Resolver로, 사용자의 언어 설정에 따라 애플리케이션의 메시지 번역을 처리합니다. 사용자의 로케일 정보를 기반으로 적절한 메시지 번역 파일을 찾아주는 역할을 합니다.
Handler Mapping : Handler Mapping은 URL과 컨트롤러 메서드 또는 핸들러와의 매핑을 담당하는 Resolver입니다. 요청 URL을 기반으로 적절한 컨트롤러나 핸들러를 찾아주는 역할을 합니다.
Handler Adapter: Handler Adapter는 컨트롤러나 핸들러를 실행하는 Resolver입니다. Handler Mapping을 통해 찾아낸 컨트롤러나 핸들러를 실행하는 역할을 수행합니다.
Argument Resolver : Argument Resolver는 컨트롤러의 메서드 매개변수를 바인딩하는 Resolver로, 컨트롤러 메서드에서 특정 매개변수의 값을 얻어오는 데 사용됩니다. 사용자 정의 타입이나 특정 조건에 맞는 매개변수를 바인딩하기 위해 사용됩니다.
Exception Resolver : Exception Resolver는 예외 처리를 담당하는 Resolver로, 컨트롤러 또는 서비스 레이어에서 발생하는 예외를 적절하게 처리하는 역할을 합니다.
3-10. ArgumentResolver
ArgumentResolver는 스프링 프레임워크에서 컨트롤러의 메서드 매개변수를 바인딩하는 역할을 담당하는 인터페이스입니다. 컨트롤러의 메서드에서 특정 매개변수의 값을 얻어오는데 사용되며, 사용자 정의 매개변수를 처리하고자 할 때 유용합니다.
Spring MVC에서 HTTP 요청과 컨트롤러의 메서드 사이에는 여러 매개변수들을 전달하는데, 이러한 매개변수들을 바인딩하는 방법에는 여러 가지가 있습니다. 기본적으로 스프링은 많은 유형의 매개변수를 자동으로 바인딩해줍니다. 예를 들어, HttpServletRequest, HttpServletResponse, @RequestParam, @PathVariable 등은 자동으로 바인딩됩니다.
3-11. ArgumentResolver 메소드
supportsParameter(MethodParameter parameter): 특정 매개변수를 해당 ArgumentResolver가 지원하는지 여부를 판단하는 메서드입니다. 지원하는 매개변수인 경우 true를 반환해야 합니다.
resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory): 지원하는 매개변수에 대해 실제 값을 바인딩하는 메서드입니다. 이 메서드에서 커스텀 바인딩 로직을 구현하고 값을 반환합니다.
구현
public class CustomDtoResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(CustomDto.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
// 커스텀 바인딩 로직 구현 및 CustomDto 인스턴스 생성 후 반환
}
}
빈 등록
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List resolvers) {
resolvers.add(new CustomDtoResolver());
}
}
3-12. ArgumentResolver 사용
스프링에서 ArgumentResolver는 주로 컨트롤러 메서드의 매개변수를 바인딩하는 데 사용됩니다. 컨트롤러의 메서드에 특정 매개변수를 선언하면, 스프링은 해당 매개변수의 값을 결정하기 위해 ArgumentResolver를 사용합니다. 주로 로그인 사용자 정보를 체크하기 위해 사용합니다. 컨트롤러 메서드에서 로그인한 사용자의 정보를 쉽게 얻기 위해 ArgumentResolver를 사용할 수 있습니다. 사용자 정보를 세션에서 가져와서 컨트롤러 메서드의 매개변수로 주입하는 작업을 커스텀 ArgumentResolver를 구현하여 처리할 수 있습니다.
3-13. ArgumentResolver와 Spring Security
ArgumentResolver를 사용한 JWT 토큰 체크는 컨트롤러 메서드에서 JWT 토큰을 처리하는 방식으로 상대적으로 간단한 상황에서 사용되며, 스프링 시큐리티를 이용한 JWT 토큰 체크는 애플리케이션 전체에 대한 보안 처리와 인증, 권한 관리 등을 제공하는 더 포괄적인 방식입니다. 선택은 프로젝트의 요구사항과 복잡성에 따라 달라질 수 있습니다
ExceptionResolver는 웹 애플리케이션에서 발생하는 예외(Exception)를 처리하는 기능을 제공하는 인터페이스입니다. 웹 애플리케이션에서 예외가 발생하면, 해당 예외를 적절하게 처리하여 사용자에게 오류 메시지를 보여주거나, 에러 페이지로 리다이렉션하거나, 커스텀한 응답을 생성하는 등의 작업을 수행할 수 있습니다.
즉, ExceptionResolver 인터페이스를 구현하여, 예외 타입에 따라 다양한 방식으로 전역적인 예외를 처리할 수 있습니다.
4-1. ExceptionResolver의 흐름
예외 발생:
웹 애플리케이션에서 컨트롤러가 요청을 처리하는 과정에서 예외가 발생합니다. 이 예외는 일반적으로 RuntimeException 또는 그 하위 클래스인 NullPointerException, IllegalArgumentException, 사용자 정의 예외 등이 될 수 있습니다.
탐색:
발생한 예외를 처리하기 위해 등록된 ExceptionResolver들이 탐색됩니다.
스프링은 HandlerExceptionResolver 인터페이스를 구현한 모든 빈(Bean)을 검색하여 등록된 예외 처리기들을 모아둡니다.
ExceptionResolver 선택:
여러 개의 ExceptionResolver가 등록되어 있을 경우, 예외 처리 우선 순위에 따라 가장 적절한 ExceptionResolver가 선택됩니다.
예를 들어, 구체적인 예외 타입에 대한 처리가 우선적으로 수행될 수 있습니다.
예외 처리:
선택된 ExceptionResolver는 예외를 처리하는 로직을 수행합니다.
예외를 처리하는 방식은 ExceptionResolver 구현체에 따라 다를 수 있습니다.
일반적으로 커스텀한 응답 데이터를 생성하거나, 에러 페이지로 리다이렉션하거나, HTTP 상태 코드를 변경하는 등의 작업을 수행합니다.
예외 처리 결과 반환:
ExceptionResolver가 예외를 처리한 결과를 반환합니다.
결과는 커스텀한 응답 데이터, 에러 페이지의 뷰 이름, HTTP 상태 코드 등의 정보를 포함할 수 있습니다.
응답 처리:
ExceptionResolver가 반환한 처리 결과를 사용하여 최종적으로 응답을 생성합니다.
예외가 성공적으로 처리되었다면 커스텀한 응답 데이터가 클라이언트에게 반환됩니다.
그렇지 않다면, 에러 페이지로 리다이렉션하거나 적절한 HTTP 상태 코드와 함께 에러 응답을 반환합니다.
응답 전달:
최종적으로 생성된 응답이 클라이언트에게 전달됩니다.
4-2. ExceptionResolver 메소드 및 구현
resolveException : 예외를 처리하는 메서드로서, 예외가 발생했을 때 해당 메서드가 호출됩니다. 이 메서드는 HttpServletRequest, HttpServletResponse, 예외가 발생한 handler(일반적으로는 컨트롤러), 그리고 발생한 예외 ex를 인자로 받습니다. 메서드는 ModelAndView를 반환하는데, 이를 통해 컨트롤러에서 뷰에 전달할 모델 데이터와 뷰 이름 등을 설정할 수 있습니다.
ExceptionResolver를 구현하는 방법은 HandlerExceptionResolver 인터페이스를 구현하고 resolveException 메서드를 오버라이드하여 구현하는 것입니다. 이를 통해 예외가 발생했을 때, 원하는 방식으로 예외를 처리하고 적절한 응답을 생성할 수 있습니다.
4-3. ExceptionResolver 구현체
SimpleMappingExceptionResolver:
가장 간단한 형태의 ExceptionResolver입니다.
예외 클래스와 예외에 대한 처리할 뷰 이름을 매핑하여 정의합니다.
클래스 패스에 properties 파일 또는 xml 파일을 설정하여 예외와 뷰 이름을 매핑할 수 있습니다.
ResponseStatusExceptionResolver:
@ResponseStatus 어노테이션과 함께 사용되는 ExceptionResolver입니다.
컨트롤러 메서드에 @ResponseStatus 어노테이션으로 상태 코드를 지정하면, 해당 상태 코드로 응답을 생성합니다.
DefaultHandlerExceptionResolver:
스프링의 기본 예외 처리 클래스입니다.
일반적인 예외들에 대해 미리 정의된 처리 방식을 제공합니다. (예: 404 Not Found 처리 등)
ExceptionHandlerExceptionResolver:
@ExceptionHandler 어노테이션과 함께 사용되는 ExceptionResolver입니다.
컨트롤러 내의 @ExceptionHandler 메서드를 찾아 예외를 처리합니다.
각 컨트롤러 내에서 발생하는 예외에 대한 지역적인 처리를 가능하게 합니다.
@ExceptionHandler는 스프링(Spring) 프레임워크에서 컨트롤러(Controller) 내에서 발생하는 특정 예외(Exception)을 처리하는데 사용되는 어노테이션입니다. 이 어노테이션을 이용하여 특정 예외가 발생했을 때 해당 예외를 처리하고, 사용자에게 적절한 응답을 반환하도록 설정할 수 있습니다.
@ExceptionHandler 어노테이션을 메서드 위에 붙여 해당 메서드가 특정 예외를 처리하도록 지정합니다. 해당 예외가 발생하면 스프링은 @ExceptionHandler 어노테이션이 붙은 메서드를 호출하여 예외를 처리합니다.
HTTP 상태코드 제어
ResponseEntity 사용 : return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Resource not found");
@ResponseStatus 사용 : @ResponseStatus(HttpStatus.NOT_FOUND) + ModelAndView
@ControllerAdvice는 Spring Framework에서 제공하는 기능 중 하나로, 전역으로 컨트롤러(Controller)에 적용되는 공통적인 처리를 정의할 수 있도록 도와주는 어노테이션입니다. 주로 예외 처리, 바인딩 설정, 모델 객체 초기화 등을 전역으로 적용하고자 할 때 사용됩니다.
6-1. @ControllerAdvice로 에러 처리에 주로 사용하는 이유
주로 컨트롤러에서 발생하는 예외들을 효율적으로 처리하기 위해 사용됩니다. 다음과 같은 이점들이 있습니다.
전역적인 예외 처리: @ControllerAdvice를 사용하여 전역적으로 예외 처리 로직을 정의하면, 해당 예외가 발생하는 모든 컨트롤러에서 일관성 있게 처리할 수 있습니다.
중복 제거: 모든 컨트롤러 메서드마다 예외 처리를 반복해서 작성하는 것을 방지하여 코드의 중복을 제거합니다.
코드 가독성 향상: 예외 처리 로직이 별도의 클래스로 분리되어 관리되기 때문에 컨트롤러 클래스가 예외 처리 로직으로 인해 복잡해지는 것을 방지하고 코드 가독성을 향상시킵니다.
통합된 예외 응답: @ControllerAdvice를 사용하면 예외 처리 로직에서 일관된 예외 응답을 생성할 수 있으며, JSON 형태의 응답을 반환하거나, 특정 에러 페이지로 리다이렉트하는 등의 작업을 할 수 있습니다.
@ControllerAdvice를 통해 예외 처리를 하면 애플리케이션의 안정성과 유지보수성을 향상시킬 수 있습니다. 다만, 각 예외에 대한 적절한 처리 로직을 정의해야 하므로, 신중하게 예외 처리를 구현하는 것이 중요합니다. 또한, @ControllerAdvice를 사용하는 것이 모든 예외를 처리할 수 있는 것은 아니므로, 예외 처리 로직과 함께 컨트롤러 메서드에서도 필요에 따라 추가적인 예외 처리를 수행해야 합니다.
6-2. @ControllerAdvice 다른 용도?
@ControllerAdvice는 예외 처리뿐만 아니라 다른 용도로도 컨트롤러 전체를 탐색하는 기능에 사용될 수 있습니다. @ControllerAdvice는 전역으로 컨트롤러에 적용되는 기능을 정의하는데 사용되며, 다음과 같은 용도로 활용될 수 있습니다
모델 초기화: @ModelAttribute 어노테이션과 함께 사용하여, 모든 컨트롤러의 핸들러 메서드에 전달되는 모델 객체에 초기 값을 설정하는데 사용할 수 있습니다. 이를 통해 각 핸들러 메서드에서 반복적으로 모델 객체를 초기화할 필요가 없어집니다.
데이터 바인딩 설정: @InitBinder 어노테이션과 함께 사용하여, 컨트롤러의 데이터 바인딩 설정을 커스터마이징하는데 사용할 수 있습니다. 특정 타입의 데이터를 바인딩할 때 변환을 지정하거나, 특정 필드를 제외하거나 포함하는 등의 설정을 할 수 있습니다.
요청/응답 변환: 요청과 응답의 변환 작업을 처리할 수 있습니다. 예를 들어 요청에서 받은 JSON 데이터를 자바 객체로 변환하거나, 자바 객체를 JSON 형태로 응답하는 등의 작업을 수행할 수 있습니다.
권한 검사: 전역적으로 권한 검사를 수행할 수 있습니다. 사용자의 권한을 확인하여 특정 요청에 대한 인가 처리를 구현할 수 있습니다.
로깅: 모든 요청과 응답에 대한 로깅을 수행할 수 있습니다. 요청의 정보, 응답의 정보, 처리 시간 등을 로깅하여 모니터링 및 디버깅에 활용할 수 있습니다.
기타: 컨트롤러에 적용되는 공통적인 기능을 구현할 때 활용할 수 있습니다.
즉, @ControllerAdvice는 예외 처리 뿐만 아니라 여러 가지 용도로 활용할 수 있으며, 전역으로 적용되는 기능을 효율적으로 구현하기 위해 사용됩니다. 애플리케이션의 요구사항과 개발자의 필요에 따라 다양한 방식으로 활용할 수 있습니다.
6-3. @ControllerAdvice 와 @ExceptionHandler 사용
@ExceptionHandler는 @ControllerAdvice 클래스 내에서 특정 예외 클래스에 대한 예외 처리 메서드를 정의하기 위해 사용되는 어노테이션입니다. @ControllerAdvice와 함께 사용되며, 해당 예외가 발생하면 컨트롤러의 메서드가 아닌 @ExceptionHandler에 정의된 메서드가 실행되어 예외를 처리합니다
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFoundException(UserNotFoundException ex) {
return new ResponseEntity<>("User not found: " + ex.getMessage(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<String> handleProductNotFoundException(ProductNotFoundException ex) {
return new ResponseEntity<>("Product not found: " + ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
6-4. @ControllerAdvice와 AOP
스프링에서 @ControllerAdvice와 AOP(Aspect-Oriented Programming)은 둘 다 애플리케이션의 전체적인 관점에서 공통적인 기능을 구현하는 데 사용되는 기능이지만, 그 목적과 적용 방식에 차이가 있습니다.
@ControllerAdvice
스프링 프레임워크에서 사용되는 어노테이션으로, 전역적으로 컨트롤러에 영향을 미치는 데 사용됩니다.
이 어노테이션을 사용하여 하나 이상의 클래스를 만들고, 이 클래스에서 @ExceptionHandler, @InitBinder, @ModelAttribute 어노테이션을 사용하여 컨트롤러에서 발생하는 예외 처리, 데이터 바인딩, 모델 속성 설정 등을 중앙에서 관리할 수 있습니다.
AOP (Aspect-Oriented Programming)
소프트웨어 개발에서 모듈화 중 하나의 형태로, 핵심 비즈니스 로직과 공통적인 부가 기능(로깅, 트랜잭션, 보안 등)을 분리하는 프로그래밍 패러다임입니다.
AOP를 사용하면 핵심 로직에 영향을 주지 않으면서, 여러 모듈 간의 공통 관심사(Aspect)를 쉽게 적용할 수 있습니다.
스프링은 AOP를 지원하며, @Aspect 어노테이션을 사용하여 관점(Aspect)를 정의하고, @Before, @After, @Around 등의 어노테이션을 사용하여 핵심 로직 전/후 또는 대신 실행할 공통 기능을 구현할 수 있습니다.
따라서, @ControllerAdvice는 주로 예외 처리와 모델 설정 등 컨트롤러에 관련된 공통 작업을 처리하는 데 사용되고, AOP는 핵심 비즈니스 로직과 분리된 공통 관심사를 적용하는 데 사용됩니다. 두 가지 모두 애플리케이션의 모듈화와 유지 보수를 향상시키는 데 도움이 됩니다.
7-1. ErrorController
ErrorController는 웹 애플리케이션에서 발생하는 오류와 예외를 처리하는데 사용되는 인터페이스입니다. ErrorController를 구현하여 커스텀 에러 핸들링을 수행할 수 있습니다. 스프링은 기본적으로 특정 오류에 대한 기본 처리 방법을 제공하지만, 개발자가 직접 ErrorController를 구현하여 원하는 방식으로 에러 페이지를 처리할 수 있습니다.
ErrorController 인터페이스는 org.springframework.boot.web.servlet.error.ErrorController 패키지에 위치하며, 하나의 메서드인 getErrorPath()를 가지고 있습니다. 이 메서드는 에러가 발생했을 때 사용자가 에러 페이지로 이동할 경로를 반환하는 역할을 합니다.
기본적으로 Spring Boot는 /error 경로로 오류를 처리하며, 이 경로에 대한 처리를 하기 위해 ErrorController를 사용합니다. 따라서 직접 커스텀한 에러 페이지를 제공하고 싶다면, ErrorController를 구현하고 /error 경로에 대해 사용자 정의 에러 페이지로 리다이렉션하는 작업을 수행해야 합니다.
7-2. ErrorController 와 BasicErrorController
ErrorController와 BasicErrorController는 스프링(Spring)에서 오류 처리를 담당하는 두 가지 서로 다른 구현체입니다. 둘 다 스프링 프레임워크의 일부로서, 웹 애플리케이션에서 오류 페이지를 처리하는데 사용됩니다.
ErrorController :
org.springframework.boot.web.servlet.error.ErrorController 인터페이스 구현
개발자가 직접 커스텀한 에러 페이지를 제공하기 위해 사용되며, 주로 Spring Boot와 같이 사용됩니다.
getErrorPath() 메서드를 구현하여 /error 경로에 대한 사용자 정의 에러 페이지 경로를 반환할 수 있습니다.
BasicErrorController :
org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 클래스
Spring Boot에서 기본적으로 제공되는 기본 에러 처리 컨트롤러입니다.
/error 경로와 기타 에러 상태 코드에 대한 기본 에러 페이지를 처리합니다.
ErrorProperties를 사용하여 커스텀할 수 있으며, 사용자가 직접 구현할 필요가 없습니다.
즉, ErrorController는 개발자가 직접 에러 페이지를 커스터마이즈하기 위해 구현하는 인터페이스이고, BasicErrorController는 Spring Boot에서 기본적으로 제공되는 기본 에러 페이지를 처리하는 클래스입니다. 만약 기본 에러 페이지가 충분하다면 BasicErrorController를 사용하고, 더욱 세부적인 커스터마이즈가 필요하다면 ErrorController를 구현하여 사용할 수 있습니다.
8-1. 레이어별 에러 대응
공통
예외 처리: DAO나 Repository 레이어에서 발생하는 예외를 적절히 처리하여 응용 프로그램이 예측 가능하고 안정적으로 동작하도록 합니다. try-catch 문을 사용하여 예외를 잡아서 로깅하거나, 사용자에게 적절한 오류 메시지를 제공하도록 합니다.
Checked vs. Unchecked 예외: 일반적으로 비즈니스 로직과 관련된 예외는 Checked 예외로 정의하고, 기술적인 문제나 예기치 않은 상황과 관련된 예외는 Unchecked 예외로 정의합니다. Checked 예외는 호출하는 쪽에서 처리하도록 강제하므로, 발생 가능성이 있는 상황에 대해 명시적으로 대응할 수 있습니다.
Custom 예외: 프로젝트에 맞는 커스텀 예외 클래스를 정의하여 특정 상황에 대한 예외를 더욱 구체적으로 처리할 수 있습니다. 이를 통해 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.
응답 처리: Service 레이어에서 발생한 예외에 대한 적절한 응답 처리를 구현합니다. 예를 들어, 에러 응답 객체를 생성하여 클라이언트에게 정확한 상태 코드와 메시지를 반환하거나, REST API의 경우 JSON 형태로 에러 응답을 제공하는 방법이 있습니다.
서비스
@Transactional 애너테이션 활용: Service 레이어에서 트랜잭션을 관리할 때 @Transactional 애너테이션을 사용하여 트랜잭션 처리를 적절히 구성합니다. 이렇게 하면 예외가 발생했을 때 롤백이 되도록 하여 데이터 일관성과 안정성을 유지할 수 있습니다.
에러 핸들러 구현: Spring의 @ExceptionHandler 어노테이션을 사용하여 Service 레이어에서 발생하는 특정 예외를 처리하는 에러 핸들러를 구현합니다. 이를 통해 예외 처리 로직을 Service 레이어와 분리하여 관리할 수 있습니다.
응답 처리: Service 레이어에서 발생한 예외에 대한 적절한 응답 처리를 구현합니다. 예를 들어, 에러 응답 객체를 생성하여 클라이언트에게 정확한 상태 코드와 메시지를 반환하거나, REST API의 경우 JSON 형태로 에러 응답을 제공하는 방법이 있습니다.
인프라
Spring의 DataAccessException 활용: Spring은 DataAccessException이라는 런타임 예외 계층을 제공합니다. 이를 활용하여 데이터 액세스 예외를 런타임 예외로 처리하고, 더 상위 계층에서 예외를 처리할 수 있도록 합니다. 이렇게 하면 불필요한 예외 처리 코드를 줄일 수 있습니다.
트랜잭션 관리: DAO 또는 Repository 레이어에서 트랜잭션을 적절히 관리하여 데이터베이스 작업 중에 에러가 발생하더라도 롤백이 되도록 합니다. 이를 통해 데이터 일관성과 안정성을 유지할 수 있습니다.
AOP를 활용한 예외 로깅: AOP(Aspect-Oriented Programming)를 사용하여 DAO나 Repository 레이어에서 발생하는 예외를 중앙 로깅하는 등의 공통 로직을 적용할 수 있습니다. 이를 통해 에러 로깅을 효율적으로 관리할 수 있습니다.
8-2. @ControllerAdvice 사용한 에러 처리
8-3. Filter 내 에러 처리(Srping Security)
ServletException: Filter 내에서 처리되지 않은 일반적인 서블릿 예외입니다. 일반적으로 Filter에서 예외가 발생할 때 ServletException으로 래핑되어 전달됩니다.
IOException: Filter 내에서 I/O 작업 중 발생하는 예외입니다. 주로 HTTP 요청 또는 응답의 스트림 처리 중 문제가 발생할 때 발생할 수 있습니다.
RuntimeException: Filter 처리 중 발생하는 런타임 예외입니다. 예를 들어, null 참조 등의 오류로 인해 발생할 수 있습니다.
IllegalArgumentException: 주로 Filter 설정이 잘못되거나 인자가 잘못 전달된 경우 발생할 수 있습니다.
IllegalStateException: Filter가 호출되는 순서 등의 상태가 맞지 않는 경우에 발생할 수 있습니다.
SecurityException: 보안 관련 이슈로 인해 Filter에서 발생할 수 있는 예외입니다. 보안 권한 문제가 있을 경우 발생할 수 있습니다.
정보 감사합니다.