[스프링] 로그인 처리 3 - 스프링 인터셉터(Interceptor)

2025. 9. 29. 17:08·스프링

앞에서 살펴본 서블릿 필터는 웹 애플리케이션의 수문장 역할을 하며, 로그인 여부 검증이나 공통 로직 처리에 유용했다.
그런데 서블릿 필터는 서블릿 기술이 제공하는 기능이고, 스프링 MVC 안쪽과는 다소 거리가 있다.

오늘은 스프링 MVC가 제공하는 또 다른 수문장, 스프링 인터셉터에 대해 알아볼 것이다.
스프링 인터셉터는 필터와 유사하지만, MVC 구조와 더 밀접하게 동작하며 훨씬 정교하고 편리한 기능을 지원한다.

 

1. 스프링 인터셉터란 무엇인가

스프링 인터셉터는 디스패처 서블릿과 컨트롤러 사이에서 동작한다.

흐름을 그림으로 나타내면 다음과 같다.

HTTP 요청 → WAS → 필터 → 서블릿(DispatcherServlet) → 스프링 인터셉터 → 컨트롤러

즉, 필터는 서블릿 이전, 인터셉터는 서블릿 이후, 컨트롤러 이전에 동작한다.

 

특징

  • URL 패턴을 세밀하게 지정할 수 있다.
  • 컨트롤러 호출 전후, 뷰 렌더링 이후까지 단계별로 끼어들 수 있다.
  • 로그인 검증, 공통 로그 기록, 예외 처리 등 컨트롤러 레벨의 관심사를 다루기에 적합하다.

 

2. 인터셉터 기본 구조

스프링이 제공하는 HandlerInterceptor 인터페이스를 구현하면 된다.

public interface HandlerInterceptor {

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
    }
}
  • preHandle() : 컨트롤러 호출 전에 실행 (false 반환 시 더 진행되지 않음)
  • postHandle() : 컨트롤러 실행 후, 뷰 렌더링 전에 실행
  • afterCompletion() : 뷰 렌더링까지 끝난 뒤 실행 (예외가 발생해도 반드시 호출됨)

필터의 doFilter() 하나보다 더 세밀하게 나뉘어 있다는 점이 특징이다.

인터셉터는 스프링 MVC 구조에 특화된 필터 기능을 제공한다고 이해하면 된다. 스프링 MVC를 사용하고, 특별히 필터 를 꼭 사용해야 하는 상황이 아니라면 인터셉터를 사용하는 것이 더 편리하다.

 

3. 요청 로그 인터셉터

로그를 남기는 가장 단순한 예제를 보자.

@Slf4j
public class LogInterceptor implements HandlerInterceptor {
    public static final String LOG_ID = "logId";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String requestURI = request.getRequestURI();
        String uuid = UUID.randomUUID().toString();
        request.setAttribute(LOG_ID, uuid);

        log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
        return true; // false면 컨트롤러 호출 안 함
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        log.info("postHandle [{}]", modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        String requestURI = request.getRequestURI();
        String logId = (String) request.getAttribute(LOG_ID);
        log.info("RESPONSE [{}][{}]", logId, requestURI);

        if (ex != null) {
            log.error("afterCompletion error!!", ex);
        }
    }
}

여기서 UUID를 request.setAttribute() 에 담아두는 이유는,
preHandle → afterCompletion 으로 넘어가는 과정이 완전히 분리되어 있기 때문이다.
따라서 지역변수가 아닌 request 속성에 담아야 전체 흐름에서 같은 값을 참조할 수 있다.

 

실행 예시 로그

REQUEST  [6234a913-f24f-461f-a9e1-85f153b3c8b2][/members/add][MemberController#addForm]
postHandle [ModelAndView [view="members/addMemberForm"; model={...}]]
RESPONSE [6234a913-f24f-461f-a9e1-85f153b3c8b2][/members/add]

 

4. 인터셉터 등록

스프링에서는 WebMvcConfigurer 의 addInterceptors() 메서드를 통해 등록한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error");
    }
}

 

  • order(1) : 실행 순서 지정 (숫자가 낮을수록 먼저 실행됨)
  • addPathPatterns("/**") : 인터셉터를 적용할 URL 패턴
  • excludePathPatterns(...) : 제외할 경로 지정

이제 이를 실제로 로그인 체크에 적용해보자.

 

5. 로그인 체크 인터셉터

서블릿 필터에서 만들었던 로그인 체크 기능을 인터셉터로 바꿔보자.

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("인증 체크 인터셉터 실행 {}", requestURI);

        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
            log.info("미인증 사용자 요청");
            response.sendRedirect("/login?redirectURL=" + requestURI);
            return false; // 진행 중단
        }
        return true;
    }
}

 

 

WebConfig에 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error");

        registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
                .excludePathPatterns("/", "/members/add", "/login", "/logout",
                                     "/css/**", "/*.ico", "/error");
    }
}

홈(/), 로그인(/login), 회원가입(/members/add), 정적 리소스 등은 인증 검증에서 제외한다.

그 외 나머지 경로는 모두 LoginCheckInterceptor 를 거친다.

 

흐름

  1. 사용자가 /items/add 같은 보호된 URL을 요청한다.
  2. LoginCheckInterceptor 의 preHandle() 이 실행된다.
  3. 세션에 로그인 정보가 없으면 /login?redirectURL=/items/add 로 리다이렉트하고 흐름을 중단한다.
  4. 로그인에 성공하면 세션에 회원 정보가 저장된다.
  5. 이후 다시 /items/add 요청을 보내면 이번에는 통과되어 컨트롤러가 실행된다.

즉, 비로그인 사용자는 자동으로 로그인 페이지로 보내지고, 로그인 후에는 원래 가려던 페이지로 자연스럽게 복귀하는 흐름이다.

 

 

6.서블릿 필터와 비교했을 때 더 간편한 점

  • 코드가 단순하다: 필터에서는 doFilter 안에서 요청/응답/체인 처리까지 모두 신경써야 했지만, 인터셉터는 preHandle 하나로 인증 검증을 끝낼 수 있다.
  • MVC 친화적이다: 컨트롤러 호출 직전에 실행되므로 “로그인한 사용자만 컨트롤러를 실행시킨다”는 의도를 코드 그대로 표현할 수 있다.
  • 설정이 유연하다: addPathPatterns, excludePathPatterns 로 경로를 세밀하게 지정할 수 있어, 정적 리소스나 에러 페이지는 쉽게 제외할 수 있다.

따라서 로그인 체크처럼 컨트롤러 실행 여부를 제어하는 기능은 서블릿 필터보다 스프링 인터셉터로 구현하는 것이 훨씬 간편하고 직관적이다.

 

 

마무리하며:

오늘은 스프링 인터셉터에 대해 살펴보았다.

먼저, 서블릿 필터와의 차이를 짚으면서 인터셉터가 어떤 위치에서 동작하는지 확인했고, preHandle, postHandle, afterCompletion 단계로 나뉘어 보다 세밀한 제어가 가능하다는 점을 확인했다.

 

이후 요청 로그를 남기는 단순한 예제를 통해 인터셉터의 기본 동작 방식을 이해했고, 실제로 로그인 체크 기능을 구현해보며 서블릿 필터 때보다 훨씬 간결하고 직관적인 코드를 작성할 수 있음을 확인했다.

정리하면, 인터셉터는 다음과 같은 장점이 있었다.

  • 코드 구조가 단순하고 MVC 흐름과 밀접하다.
  • URL 패턴을 정밀하게 제어할 수 있다.
  • 예외 발생 여부와 관계없이 afterCompletion 단계에서 공통 처리를 보장한다.

따라서 로그인 검증, 공통 로그 기록, 보안 처리처럼 컨트롤러 실행 여부와 직결되는 기능은 필터보다 인터셉터를 활용하는 것이 훨씬 효율적이다.

 

 

감사합니다.

'스프링' 카테고리의 다른 글

[스프링] API 예외 처리  (0) 2025.10.05
[스프링] 예외 처리와 오류 페이지  (0) 2025.10.01
[스프링] 로그인 처리 2 - 서블릿 필터  (0) 2025.09.29
[스프링] 로그인 처리 1- 쿠키, 세션  (0) 2025.09.28
[스프링] Validation에 대하여  (0) 2025.09.27
'스프링' 카테고리의 다른 글
  • [스프링] API 예외 처리
  • [스프링] 예외 처리와 오류 페이지
  • [스프링] 로그인 처리 2 - 서블릿 필터
  • [스프링] 로그인 처리 1- 쿠키, 세션
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    타임리프
    fetch join
    LocalDateTime
    dfs
    SOLID
    mvc
    객체지향
    자바
    SpringDataJpa
    불변객체
    다형성
    컬렉션
    재귀
    백준
    스프링 컨테이너
    QueryDSL
    코딩테스트
    Java
    BFS
    최적화
    쿼리
    쿼리dsl
    spring
    스프링
    코딩 테스트
    스프링 데이터 JPA
    예외처리
    thymeleaf
    예외 처리
    JPA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[스프링] 로그인 처리 3 - 스프링 인터셉터(Interceptor)
상단으로

티스토리툴바