개발자로 후회없는 삶 살기
[문법] 필터, 인터셉터 본문
서론
※ 과거에 기록한 내용에서 중요한 부분만 발췌하여 모두가 이해하기 쉽게 다시 서술한다.
본론
- 서블릿 필터
로그인한 사용자만 상품 목록을 볼 수 있도록 해야하는데, 현재는 URL을 직접 입력하면 로그인 안 한 사용자도 접근 가능하다.
이를 해결하는 방법으로, 모든 컨트롤러에 세션 검증 코드를 넣을 수 있다. 근데 이는 같은 기능을 반복하여 중복이 발생하고, 번거롭다.
지금, 사용자가 인증이 된 사용자인지 인증에 대해 공통으로 관심을 가지고 있다. 공통 관심사는 AOP로도 해결할 수 있지만 웹과 관련된 것은 서블릿 필터와 스프링 인터셉터를 사용하여 Http 관련 공통 관심사를 처리해준다. 이들은 특정 URL이 오는 건 다 막는다는 등 웹 관련 부가 사항을 제공하여 로그인 여부 체크하는 로직을 한 방에 해결할 수 있다.
→ 필터의 흐름
웹 서버가 WAS를 호출하고 디스패쳐 서블릿을 생성하고 호출하는 줄 알았는데, 필터를 먼저 호출한다. 그래서 [모든 고객의 요청 로그를 다 남겨 주세요] 요구사항을 필터로 하면 된다. 필터는 호출 URL을 적용하여 모든 URL 요청을 다 필터로 받을 수 있다.
→ 필터 제한
로그인 안 한 사용자는 필터에서 딱 걸려서 서블릿을 호출하지 않는다. 필터는 적절하지 않은 요청이라고 판단되면 끝낼 수 있어서, 로그인 여부를 체크하기 좋다.
→ 필터 체인
필터를 여러 개(로그인, 로그) 순서대로 적용할 수 있다.
- 인터페이스
doFilter가 중요한데, was에서 do를 호출하고 체인에 연결된 모든 do를 호출하고 더 이상 필터가 없으면 서블릿을 호출한다. 인터를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 컨테이너로 관리한다.
1) init : 서블릿 컨테이너 생성 시 호출
2) doFilter : 요청마다 메서드가 호출되고 필터의 로직을 구현 (비즈니스 로직 작성)
3) destroy : 서블릿 컨테이너 종료 시 호출
- 로그 필터
모든 요청을 로그로 남기는 필터를 만들어보자, ServletRequest는 기능이 없어서 다운 케스팅하여 사용한다. 다음 필터의 do 호출을 반드시 해야한다. 다음 필터가 없으면 서블릿이 호출된다. do를 작성하지 않으면 아예 다음으로 진행되지 않고 컨트롤러도 호출되지 않는다.
필터는 빈으로 등록해서 사용해야 한다. FilterRegistrationBean을 사용하면 빈을 등록할 수 있다. 체이닝을 하기 때문에 순서를 정하고 UrlPatterns로 모든 요청이 필터로 오게 한다.
→ 로그인 인증 필터
반드시 3가지 메서드를 구현해야 하는 것은 아니다. doFilter 외 메서드는 default 메서드라서 구현하지 않아도 된다.
로그인 검증을 하지 않아도 되는 url을 정하고, 그 외 url은 로그인 검증을 한다.
do에서 지금 요청에서 URI가 화이트 리스트인지 보고, 아니면 검증을 한다. 여기에 로그인 세견 검증 로직을 작성한다. 만약 로그인 한 사용자라면 chain.do로 다음 필터로 넘어간다.
호출해보면 미인증 사용자의 경우 상품 목록을 조회할 수 없다. 로그 필터가 1순위, 인증 필터가 2순위로 출력된다.
- 스프링 인터셉터
필터와 같은 공통 관심사 처리 기술로 적용되는 순서와 범위가 다르고 편리하다.
컨트롤러 호출 직전에 호출된다. 제한, 체인 역시 동일하다.
→ 인터페이스
pre : 컨트롤러 호출 전
post : 컨트롤러 호출 후
after : 완전 호출 후
3가지 메서드가 제공된다.
필터와 다르게 세분화되어 있고 필터의 경우 http 관련만 인자로 받았는데 어떤 핸들러가 호출되는지, 어떤 모델엔뷰가 반환되는지 등 제공하는게 많다.
디스패쳐 서블릿의 어댑터가 컨트롤러를 호출하는데 흐름을 알아보자
인터셉터 프리 호출 : 응답값이 true면 다음으로 진행하고 f면 더 진행하지 않고 핸들러도 호출 X
핸들러 어댑터가 핸들러 호출 : 모델 뷰 반환
인터셉터 포스트 호출 : 모델 뷰를 인자로 호출
view 랜더링
인터셉터 after 호출
→ 예외 사항
포스트와 에프터 핸들의 차이점을 알아보자
프리 핸들 이후 컨트롤러가 호출된 후, 예외가 발생해서 서블릿으로 넘어오면 포스트는 호출되지 않는데, 에프터는 호출된다. 에프터는 반드시 호출되는 것을 보장한다. 인자로 예외가 들어오며, 예외가 발생하지 않으면 null 값이다.
- 인터셉터 요청 로그
1. 프리 핸들
필터와 다르게 HttpServletRequest가 들어온다. 포스트는 예외 상황에서 안 찍힌다고 했으니 에프터에서 응답을 찍어야 하며 request의 저장소에 로그를 위해 uuid를 저장한다. request는 하나의 요청에서 살아있는게 보장이 되는데 렌더링이 끝나고 에프터가 호출될 까지 request에 넣어놓은 set 값이 생명주기가 이어진다.
필터는 do에서 왔다 갔다하는 로그를 전부 찍어야 하는데 인터셉터는 세분화 되어 있어서 용이하다. RequestMapping하는 경우 핸들러가 HandlerMethod가 사용된다. 이처럼 프리 이후 호출할 컨트롤러도 찍어볼 수 있다.
프리 : 핸들러
포스트 : 모델 뷰
에프터 : 예외
이것이 스프링 MVC에 특화되어 있다는 것이다. return을 true로 하면 핸들러 어댑터가 호출되고, f로 하면 여기서 더 이상 나아가지 않고 끝난다.
2. 포스트 핸들
모델 엔 뷰가 인자로 들어온다.
3. 에프터 핸들
완전히 끝날 때 호출이 되는데 핸들러와 예외가 인자로 들어온다. 만약 예외가 있으면 에러를 찍는다. 에프터는 예외가 발생해도 호출되는 것이 보장된다.
→ 등록
add로 등록하고 순서와 url 패턴을 넣는다. /**로 해야 하며 인터셉터를 하지 않을 경로를 넣으면 해당 경로는 인터셉터가 호출되지 않는다.
→ 실행
프리에서는 다음으로 호출될 컨트롤러 정보를 찍을 수 있고
RequestMapping의 경우 호출되는 핸들러가 HandlerMethod인데 호출되는 매핑 메서드의 파라미터까지 get을 꺼내서 사용할 수 있다.
포스트의 모델 뷰, 에프터도 잘 동작한다. 필터와 비교했을 때 정말 다양하고 세분화된 로그를 찍을 수 있다.
→ 정리
필터 : 서블릿보다 앞에 호출되는 수문장으로 모든 요청이 다 들어옴
인터셉터 : 컨트롤러가 호출되기 직전에 모든 요청이 들어오는 수문장
- 인터셉터 인증 체크
로그인 인증 체크 인터셉터는 프리에서 컨트롤러 호출 전에 막아버려야 한다. return을 false를 해서 끝내버린다. 인터셉터가 프리핸들만 구현하면 되어서 간결해진다.
→ 등록
화이트 리스트는 여기서 처리하면 된다. 필터는 do에 전부 다 구현해야하니 복잡해지는 것인데 인터셉터의 세분화로 간결해진다.
→ ArgumentResolver 활용
로그인 정보를 가져오는 것을 간단하게 할 수 있다. @Login만 있으면 되는데, 리졸버가 컨트롤러에게 파라미터 넣어주는 모듈로 리졸버를 직접 구현하여 파라미터를 넣을 수 있다.
→ 애너테이션
@Login 만 붙어있으면 리졸버가 동작하도록 할 수 있다.
→ 리졸버 구현
HandlerMethod 핸들러의 리졸버를 구현한다.
서포트에서 컨트롤러 매핑 메서드가 @Login을 가지고 있고 Member 클래스에 데이터가 담기도록 하면 리졸버가 동작하도록 한다.
이제 resolve 메서드에서 사용자 세션을 꺼내서 원하는 정보를 반환하면, 컨트롤러가 호출될 때 리졸버가 파라미터를 반환한다.
→ 등록
리졸버는 등록해야 하며, 리졸버를 적절히 사용하면 공통으로 사용되는 파라미터 처리를 간결하게 만들 수 있다.
'[백엔드] > [spring | 학습기록]' 카테고리의 다른 글
[문법] 파일 업로드 (0) | 2024.08.05 |
---|---|
[문법] 스프링 예외처리 (0) | 2024.08.05 |
[문법] 검증 (0) | 2024.08.04 |
[문법] 요청과 응답, HttpMessageConverter (0) | 2024.08.03 |
[문법] Spring 핸들러 어댑터, API 설계 실전 (0) | 2024.08.02 |