개발 환경을 세팅하던 중, 더 편리하고 용이하게 개발을 진행하기 위해 HTTP Request/Response 전체 로그를 남기는 기능을 추가하고 싶었다. 이를 어떻게 구현할 수 있을지 자료를 찾아보던 중, 대부분의 글에서 Filter 단이나 Interceptor 단에서 구현하는 방식을 소개하고 있었고, 이에 대해 보다 명확히 이해하고자 내용을 정리해본다.
Filter
- 위치/영역 : 서블릿 컨테이너 레벨 (DispatcherServlet 외부). Spring과 무관한 요청도 관여 (정적 리소스, 에러 페이지 등)
- 시점 : 요청 진입 직후 → 필터의 체인 진행 → 응답 직전/후
- 목적 : 보안(인증 전 단계), 공통 헤더, 압축/디코딩, CORS, 원시적인 HTTP 로깅에 적절(헤더/바디).
- 장점 : 애플리케이션 전역, 프레임워크 독립적, Request / Response 원문 접근
- 한계 : Spring MVC 컨텍스트(핸들러, 모델 등)에 대한 정보는 접근 불가
Interceptor
- 위치/영역 : Spring MVC 핸들러 체인 (DispatcherServlet 내부)
- 시점 : preHandle(컨트롤러 호출 전) → postHandle(뷰 렌더 전) → afterCompletion (완료/예외 후처리)
- 목적 : 인증/인가, 로깅(URI, 핸들러, 파라미터), Controller / View 레벨 문맥에 활용.
- 장점 : 어떤 핸들러(컨트롤러 메서드)인지 알고, HandlerMethod 정보에 접근 가능.
- 한계 : Response Body를 직접 읽거나 다루기는 까다로움 (body는 메시지 컨버전 단계에서 기록됨)
AOP
- 위치/영역 : 메서드 호출 단위(Proxy). Controller, Service, Repository 등 Spring Bean에 Advice 삽입
- 시점 : @Around, @Before, @AfterReturning, @AfterThrowing 등 메서드 관점.
- 목적 : 트랜잭션, Retry, 캐싱, 비즈니스 계층 로깅/추적, 성능 계측.
- 장점 : URL/HTTP에 종속적이지 않음. Service / Repository 호출 흐름을 횡단 관심사로 캡처.
- 한계 : HTTP 원문(서블릿 Request/Response)과는 거리가 있음. 컨트롤러 반환값은 잡아도 변환 후 실제 전송된 바디와는 다를 수 있음.
+ 추가적으로 ApplicationConfig 생성 시점
web.xml에는 ContextLoaderListener와 DispatcherServlet의 2가지 설정(기본적으로 xml)이 있다.
WAS를 처음 작동시키면, web.xml(컨테이너)를 로딩하여 Servlet Container가 구동되고, Servlet Container는 ContextLoaderListener 객체를 자동으로 메모리에 생성(pre-loading)한다.
ContextLoaderListener 객체는 먼저 <context-param>으로 등록된 설정 파일(ex. ApplicationConfig.java)을 읽어서 첫번째 Spring Container(Root Container)를 구동하고 비즈니스 객체들(Service, DAO)을 생성한다. 이를 부모 컨테이너라고 부른다. 부모 컨테이너는 @ComponentScan으로 DAO・Service Bean을 찾고 메모리에 생성한다.
다음으로, '/' 요청(=> 모든 요청)이 들어오면, Servlet Container가 DispatcherServlet 객체를 생성한다. DispatcherServlet이 내부적으로 init()을 호출할 때, <init-param>으로 등록된 Spring 설정 파일을 찾아 두번째 Spring Container를 구동한다.
이를 자식 컨테이너라고 부르기로 하며, @ComponentScan으로 'Controller' Bean(+ 'HandlerMapping', 'ViewResolver')을 찾고 메모리에 생성한다.
- 자식 컨테이너는 부모 컨테이너의 것을 사용할 수 있다. (부모 - 자식은 상속의 개념이 아니다)
- url-pattern을 다르게 해서 여러 개의 DispatcherServlet이 있을 수 있다. (자주 있는 경우는 아님)
- DispacherServlet이 Controller들을 bean으로 등록하고, HandlerMapping 객체에 컨트롤러 내의 메소드마다 붙은 @RequestMapping 정보를 담아 둔다.
- 원래닌 '/'에 대한 요청 (즉, 모든 요청)은 WAS(ex. tomcat)가 제공하는 DefaultServlet이 처리한다. 요청 URL에 해당하는 HandlerMapping 정보를 찾아 해당 Controller가 비즈니스 로직을 처리한다. 해당 HandlerMapping 정보가 없으면, 로직 처리를 DefaultServlet에게 넘긴다. 그래도 없으면 404 error가 발생한다.
Ref - https://starkying.tistory.com/entry/Spring-MVC-동작원리-구성요소
'Spring' 카테고리의 다른 글
쌍용강북교육센터 국비 학원 Day 89일차 Spring (AOP) (0) | 2023.05.22 |
---|---|
쌍용강북교육센터 국비 학원 Day 88일차 Spring (Controller, tiles 적용) (0) | 2023.05.10 |
쌍용강북교육센터 국비 학원 Day 87일차 Spring Framework (0) | 2023.05.09 |