✅ 인터셉터를 활용하게 된 배경
게시판 프로젝트를 혼자서 공부하면서 만들던 중 드디어 로그인 이후 사용자의 요청을 구분해야하는 시기가 다가왔다!
로그인한 사용자들은 게시글 작성, 수정, 삭제 등을 할 수 있어야하고,
로그인하지 않은 사용자들은 게시글 조회 등의 작업을 할 수 있다.
그렇게 되면 게시글 작성, 수정, 삭제 로직마다 세션을 확인해서 세션을 갖고 있는 사용자인지 확인을 하는 로직을 아마 매번 넣어야할텐데
매요청마다 이러한 로직을 작성하는 것은 코드의 중복을 초래한다.
이를 위해 따로 메서드를 작성하여 해당 메서드를 컨트롤러 단에서 처리할 수 있겠지만
그러한 *공통 관심사는 다른 방법으로 해결하는 것이 바람직할 것 같다는 생각이 들었다.
사실 나는 부트캠프에서 프로젝트를 진행하면서 항상 스프링 시큐리티를 사용하며
시큐리티의 보안 필터를 사용해왔다.
손쉽게 설정 파일에서 어떤 작업에 로그인한 사용자들만 접근할 수 있도록 처리할 것인지 설정을 해줬었는데,
부트캠프가 끝난 지금 조금 여유가 생겨 직접 필터와 인터셉터에 대해서 공부해보고 구현해보고 싶다는 생각이 들었다.
이번에는 직접 인터셉터를 작성하여 컨트롤러로 요청이 진입하기 전에 요청을 가로채서 요청한 사람이 로그인을 한 사람인지 아닌지를 판단한 뒤 접근을 허용할 수 있도록 처리해보고 싶다는 생각이 들었다.
✅ 인터셉터 ? vs 필터 ?
일단 사용하기에 앞서 이 두가지를 비교해보고 어떤 것이 사용자 인증 인가 작업에 적합할지 고민해보았다.
나는 인터셉터를 활용하여 인증, 인가 작업을 처리하기로 결정을 했는데,
그 가장 큰 이유는 다음과 같다.
- 설정이 간편함.
- 컨트롤러 요청 전, 후 등에 인터셉터가 작동하기 때문에 조금 더 세밀한 작업을 처리할 수 있음.
- 로그인한 사용자인지 확인하는 코드를 비즈니스 로직에서 분리하는 이유는 요청을 처리하기 위해 확인하는 작업이라 필터에서 이 작업을 진행하는 것 보단 비즈니스 로직과 가까운 인터셉터에서 처리하는 것이 코드 분리하고자 하는 나의 목적에 더 부합한 것이라고 판단함.
- 필터는 조금 더 보안과 관련된 설정에 적용하는 개념이라는 생각이 들었음.
✅ 인터셉터 이해하기
스프링 MVC 흐름
request -> Dispatcher Servlet -> Handler Mapping -> *preHandle -> Controller -> *postHandle -> ViewResolver -> View -> response -> *afterCompletion()
Handler Adapter 의 세가지 메서드
1. preHandle
- 컨트롤러 실행 전, 즉 컨트롤러로 요청이 들어가기 전에 수행된다. 그리고 return 시 true 이면 컨트롤러 uri로 가고, false이면 컨트롤러 요청을 하지 않는다.
2. postHandle
- 컨트롤러(핸들러) 실행하고 나서 뷰 실행 전, 즉 컨트롤러의 핸들러 처리가 끝나 return 되고 뷰 화면을 response 하기 직전에 postHandle 메서드를 수행한다.
3. afterCompletion
- 뷰 화면을 response 끝난 뒤에 수행한다.
✅ 인터셉터 적용하기
PreHandle 메서드 : 컨트롤러 요청 진입 전 실행 코드
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("[=====preHandle]=====");
// 1. 세션에서 회원 정보 조회
HttpSession session = request.getSession();
// SessionConst는 세션의 key 값인 이름 속성을 항상 동일하게 작성해야하므로 상수 클래스를 생성함.
Long id = (Long) session.getAttribute(SessionConst.USER_ID);
log.info("id = {}", id);
// 세션에 정보가 없을 경우
if (id == null) {
String urlPrior = request.getRequestURL().toString() + "?" + request.getQueryString();
request.getSession().setAttribute("url_prior_login", urlPrior); // 직전 url 을 세션에 저장함.
response.sendRedirect(request.getContextPath() + "/auth/login");
return false; // 세션에 id 정보가 없을 경우 더이상 컨트롤러 요청 진입을 못하도록 return false 처리를 해줘야 함.
}
return true; // return true 시 컨트롤러 진입 후 요청 진행
}
preHandle 은 컨트롤러로 요청이 들어가기 전에 실행되는 메서드이다.
따라서 이때는 사용자의 정보가 세션에 있는지 확인한 뒤, 세션에 정보가 없을 경우
response.sendRedirect 를 통해서 login 화면으로 redirect 를 해주기 위해 코드를 작성했다.
(url을 세션에 저장하는 것은 아직 구현 전이라 미리 코드만 작성해뒀다. 필요 시 사용할 예정)
postHandle, afterComletion : 컨트롤러 로직 실행 후, 뷰(jsp) 진입 전에 처리할 코드
아직 컨트롤러 실행 후, 뷰 진입 전에 처리해야할 일은 없어서 비워두고 로그만 출력하도록 했다.
// 컨트롤러 실행 후, 뷰 진입 전
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info("=====[postHandle]=====");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// 뷰 진행 후
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
log.info("=====[afterCompletion]=====");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
💡 실제로 인터셉터를 사용하는 이유 중에 인증, 인가 뿐 아니라 로그를 찍어보기 위해서도 활용한다고 한다.
Config 파일 : 어떤 URL 에 인터셉터를 적용할지, 제외할지 설정
그리고 config 설정 파일에는 다음과 같이 설정해줬다.
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
// 모든 페이지 접근 시 preHandle 작동
// excludePathPatterns()를 이용해서 로그인 페이지, 로그아웃 페이지 URI는 인터셉터 실행에서 제외
// 필터링 할 URL 패턴
.addPathPatterns()
.excludePathPatterns("/auth/login", "/auth/join", "/", "/resources/**", "/board/detail/**"); // 제외 대상
}
}
- 메인 페이지 ("/")
- 로그인 페이지
- 회원가입 페이지
- css, js 파일 적용을 위해 resources 폴더 경로
- 게시글 상세보기 페이지 등을
인터셉터가 가로채지 않게 exclude 해주었다.
💡 참고로 처음에 resources 폴더를 제외 안시켜줘서 js, css 파일들이 모두 적용이 안되어 잠시 당황을 했었다.
✅ 적용 후 로그 확인해보기
interceptor 가 제대로 작동하는지 확인차 로그인 후 exclude 하지 않은 요청인
수정 기능 요청을 진행해보았다.
컨트롤러 호출 전 먼저
preHandle 로그가 찍히고,
수정 컨트롤러 메서드안에 들어가 serviceImpl 코드의 로그가 찍힌 뒤 모든 로직을 수행하고 난 다음
postHandle 로그가 찍히는 것을 확인할 수 있었다.
굿! 잘 작동한다!
✅ 적용 소감
시큐리티 인증 필터로 손쉽게 했던 처리들을 필터는 아니지만 인터셉터를 활용하여 직접 구현해보았는데
어떤 원리와 작동 과정으로 사용자의 인증, 인가 처리를 하는지 배울 수 있었고,
비즈니스 로직이 매번 사용자 인증, 인가 작업을 하지 않아도 인터셉터에서 처리할 수 있게 되니
컨트롤러에서의 요청 처리 과정을 생략하여 코드를 깔끔하게 분리할 수 있게 되어 아주 흡족했다!
✅ 참고 자료
Spring interceptor를 활용한 로그인 및 인가(Authorization)
1. 인증(Authentication)과 인가(Authorization) 1️⃣ 인증(Authentication) : 시스템 접근 시, 등록된 사용자인지 여부를 확인하는 것 ex) 로그인 2️⃣ 인가(Authorization) : 시스템 접근 후, 인증된 사용자에게 권
hyejin.tistory.com
'👩💻 BackEnd > 🌿 스프링 [Spring]' 카테고리의 다른 글
[ Spring ] Controller 에서 데이터를 어떤 형태로 받아야하는가? (@RequestBody, @RequestParam) (0) | 2024.05.21 |
---|---|
[ Spring / fileUpload ] File Upload / File Download 기능 구현 (0) | 2024.05.05 |
interceptor 3가지 메서드 (0) | 2024.05.04 |
[Spring MVC] spring mvc 실용적인 방식 (0) | 2024.01.19 |
[Spring MVC] DispatcherServlet / 핸들러 매핑과 핸들러 어댑터 (0) | 2024.01.19 |