본문 바로가기
Everyday Study

2024.10.15(화) { WebMvcConfigurer, 인터셉터(Interceptor), redirect&forward, 필터체인 }

by xogns93 2024. 10. 15.

WebMvcConfigurer

WebMvcConfigurer는 Spring MVC에서 웹 애플리케이션의 설정을 커스터마이징할 수 있도록 제공되는 인터페이스입니다. Spring MVC는 기본적인 설정을 자동으로 처리해주지만, 때로는 개발자가 특정한 요구에 맞게 세부 설정을 변경해야 할 때가 있습니다. 이런 경우 WebMvcConfigurer를 구현하면 다양한 Spring MVC 기능을 재정의하거나 추가로 설정할 수 있습니다.

WebMvcConfigurer의 역할

WebMvcConfigurer 인터페이스는 Spring MVC에서 제공하는 여러 가지 설정 옵션을 쉽게 변경할 수 있도록 만들어졌습니다. 주로 다음과 같은 설정을 할 때 사용됩니다:

  1. View Controllers: 특정 URL을 컨트롤러 없이 바로 뷰로 매핑할 수 있습니다.
  2. 리소스 핸들링: 정적 리소스(CSS, JS, 이미지 파일 등)의 경로를 설정할 수 있습니다.
  3. 메시지 컨버터(Message Converters): JSON, XML 등과 같은 데이터를 변환하는 메시지 컨버터를 추가하거나 수정할 수 있습니다.
  4. CORS 설정: Cross-Origin Resource Sharing을 설정할 수 있습니다.
  5. 인터셉터 추가: 요청 전후에 추가 로직을 삽입하는 인터셉터를 설정할 수 있습니다.
  6. 포맷터, 변환기 등록: 데이터의 형식 변환을 처리하는 포맷터나 변환기를 추가할 수 있습니다.

WebMvcConfigurer 구현 예시

개발자가 원하는 설정을 추가하거나 변경하려면 @Configuration 애노테이션과 함께 WebMvcConfigurer를 구현하는 클래스를 만들면 됩니다.

예시 1: 간단한 WebMvcConfigurer 구현

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // "/" 요청을 home.jsp로 매핑
        registry.addViewController("/").setViewName("home");
        // "/about" 요청을 about.jsp로 매핑
        registry.addViewController("/about").setViewName("about");
    }
}

설명:

  • addViewControllers(): 별도의 컨트롤러 없이 특정 URL을 뷰로 매핑합니다. 이 예시에서는 "/"로 들어온 요청은 home.jsp, "/about" 요청은 about.jsp로 처리됩니다.

예시 2: 정적 리소스 경로 설정

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // "/images/**" 경로로 접근하는 요청을 "/resources/images/" 폴더에서 찾음
        registry.addResourceHandler("/images/**")
                .addResourceLocations("/resources/images/");
    }
}

설명:

  • addResourceHandlers(): 정적 파일을 제공하기 위한 리소스 핸들러를 추가합니다. 이 경우, 사용자가 /images/**로 접근할 때 실제로는 /resources/images/ 폴더에서 파일을 찾도록 설정됩니다.

WebMvcConfigurer의 주요 메서드

  1. addViewControllers(ViewControllerRegistry registry)
    • 특정 URL을 컨트롤러 없이 뷰 이름에 직접 매핑할 수 있습니다.
    • 예시: 로그인 페이지, 메인 페이지 등 단순한 페이지로의 매핑.
  2. addResourceHandlers(ResourceHandlerRegistry registry)
    • 정적 자원의 경로를 설정합니다. 이미지, CSS, JS 파일 등을 특정 경로로 매핑하는 데 사용됩니다.
  3. configureMessageConverters(List<HttpMessageConverter<?>> converters)
    • JSON, XML 등 다양한 형태의 데이터를 변환하는 메시지 컨버터를 추가하거나 수정할 수 있습니다.
    • 예시: MappingJackson2HttpMessageConverter를 추가하여 JSON 응답을 처리.
  4. addCorsMappings(CorsRegistry registry)
    • CORS 설정을 추가할 수 있습니다. 특정 도메인이나 경로에 대해 외부에서 요청이 가능하게 허용하는 설정을 할 수 있습니다.
  5. addInterceptors(InterceptorRegistry registry)
    • HTTP 요청 전후에 실행될 로직을 추가할 수 있는 인터셉터를 등록합니다. 로깅, 인증, 인가 등의 작업을 처리하는 데 사용됩니다.
  6. configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
    • 서블릿 컨테이너에서 기본 서블릿을 활성화하여 특정 경로의 리소스를 처리하게 할 수 있습니다.
  7. addFormatters(FormatterRegistry registry)
    • 데이터를 포맷팅하거나 변환하는 포맷터나 변환기를 추가할 수 있습니다.
  8. configurePathMatch(PathMatchConfigurer configurer)
    • 경로 매칭과 관련된 설정을 변경할 수 있습니다. 예를 들어, URL 경로의 슬래시("/")를 무시하는 설정 등을 할 수 있습니다.

WebMvcConfigurer@EnableWebMvc의 관계

  • WebMvcConfigurer: Spring MVC의 설정을 커스터마이징할 수 있도록 도와주는 인터페이스로, Spring Boot에서는 기본적으로 제공되는 설정 위에서 사용자 정의 설정을 추가할 수 있습니다.
  • @EnableWebMvc: 이 애노테이션을 사용하면 Spring의 자동 설정이 비활성화되고, 모든 설정을 개발자가 직접 해야 합니다. 즉, Spring Boot의 기본 설정을 모두 무시하고 원하는 설정을 일일이 구현해야 합니다.

따라서 일반적으로 Spring Boot 애플리케이션에서는 @EnableWebMvc를 사용하지 않고 WebMvcConfigurer를 구현하여 필요한 설정만 추가하는 방식이 선호됩니다.

결론

WebMvcConfigurer는 Spring MVC의 설정을 세밀하게 제어하고 커스터마이징할 수 있는 유연한 인터페이스입니다. 이를 통해 정적 리소스 경로 설정, 뷰 컨트롤러 매핑, 메시지 컨버터 추가, CORS 설정 등 다양한 웹 관련 기능을 쉽게 설정할 수 있습니다. Spring Boot에서는 WebMvcConfigurer를 구현하여 기본 설정을 유지하면서도 필요에 맞는 설정을 추가하는 것이 일반적인 방식입니다.

인터셉터(Interceptor)는 Spring MVC에서 HTTP 요청과 응답을 가로채고, 전후 처리할 수 있는 기능을 제공합니다. 인터셉터는 클라이언트의 요청이 컨트롤러에 도달하기 전에, 또는 응답이 클라이언트로 전달되기 전에 특정 로직을 실행할 수 있는 훌륭한 도구입니다.


주요 역할

인터셉터는 필터(Filter)와 유사하게 동작하지만, Spring MVC의 요청 처리 흐름과 밀접하게 통합되어 있어 컨트롤러, 모델, 뷰 단계에서의 세밀한 조작이 가능합니다. 인터셉터는 주로 다음과 같은 기능을 제공합니다:

  1. 전처리(Pre-processing): 클라이언트의 요청이 컨트롤러에 도달하기 전에 특정 작업을 수행할 수 있습니다. (예: 인증, 권한 확인)
  2. 후처리(Post-processing): 컨트롤러에서 응답을 반환한 후, 응답이 클라이언트로 전달되기 전에 로직을 처리할 수 있습니다. (예: 응답 데이터 수정, 로깅)
  3. 뷰 렌더링 후(Post-completion): 뷰 렌더링까지 완료된 후 추가 작업을 할 수 있습니다.

인터셉터와 필터의 차이점

  • 필터(Filter)는 서블릿 레벨에서 작동하며, Spring MVC에 종속되지 않고 모든 요청에 대해 작동할 수 있습니다.
  • 인터셉터(Interceptor)Spring MVC에서 동작하며, 컨트롤러로의 요청 흐름을 제어할 수 있습니다. 인터셉터는 Spring 컨텍스트에서 관리되므로, Spring의 빈과 종속성을 쉽게 사용할 수 있습니다.

인터셉터 설정

Spring에서 인터셉터를 구현하려면 HandlerInterceptor 인터페이스를 구현하거나 HandlerInterceptorAdapter 클래스를 상속해야 합니다. 그런 다음 인터셉터를 등록하여 요청 흐름에 추가할 수 있습니다.

1. 인터셉터 구현

HandlerInterceptor 인터페이스는 세 가지 주요 메서드를 제공합니다:

  • preHandle(): 컨트롤러에 요청이 도달하기 전에 호출됩니다.
  • postHandle(): 컨트롤러가 요청을 처리한 후, 뷰가 렌더링되기 전에 호출됩니다.
  • afterCompletion(): 뷰 렌더링이 완료된 후 호출됩니다.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class CustomInterceptor implements HandlerInterceptor {

    // 요청이 컨트롤러에 도달하기 전에 실행 (전처리)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Pre Handle method is Calling");
        return true;  // true일 경우 요청을 계속 진행, false일 경우 요청을 중단
    }

    // 컨트롤러가 요청을 처리한 후, 뷰가 렌더링되기 전에 실행 (후처리)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Post Handle method is Calling");
    }

    // 뷰 렌더링까지 완료된 후 실행 (완료 처리)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
        System.out.println("Request and Response is completed");
    }
}

2. 인터셉터 등록

인터셉터를 등록하려면 WebMvcConfigurer 인터페이스를 구현하여 Spring의 요청 흐름에 인터셉터를 추가합니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 인터셉터 등록 및 적용할 경로 설정
        registry.addInterceptor(new CustomInterceptor())
                .addPathPatterns("/**")      // 모든 경로에 대해 인터셉터 적용
                .excludePathPatterns("/login", "/logout");  // 특정 경로는 제외
    }
}

3. preHandle(), postHandle(), afterCompletion() 설명

  • preHandle():
    • 요청이 컨트롤러에 전달되기 전에 호출됩니다.
    • 여기서 true를 반환하면 요청 처리를 계속 진행하고, false를 반환하면 요청 처리를 중단합니다. 이 메서드는 주로 인증이나 권한 확인 등의 작업에 사용됩니다.
  • postHandle():
    • 컨트롤러가 요청을 처리한 후, 뷰가 렌더링되기 전에 호출됩니다.
    • 이 메서드는 주로 모델에 데이터를 추가하거나, 뷰를 수정하는 작업에 사용됩니다.
  • afterCompletion():
    • 뷰 렌더링이 끝나고 클라이언트로 응답이 완료된 후에 호출됩니다.
    • 주로 요청 처리 중 발생한 예외 처리리소스 정리 작업을 수행합니다.

사용 사례

  1. 인증 및 권한 관리
    • 사용자가 특정 요청을 하기 전에 사용자의 권한을 확인하여 권한이 없는 경우 요청을 막는 용도로 preHandle()에서 검사를 할 수 있습니다.
  2. 로깅
    • 클라이언트의 요청 정보를 로깅하거나, 응답이 완료된 후 요청 처리 시간 등을 기록할 수 있습니다.
  3. 글로벌 처리 로직
    • 모든 요청에 대해 공통으로 처리해야 할 로직(예: 사용자 세션 체크, 특정 헤더 추가 등)을 전역적으로 관리할 수 있습니다.
  4. 리소스 정리
    • 응답이 완료된 후, 데이터베이스 연결 해제, 캐시 정리, 파일 삭제 등의 작업을 afterCompletion()에서 수행할 수 있습니다.

예시: preHandle()에서 인증 처리

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HttpSession session = request.getSession(false);
    if (session == null || session.getAttribute("user") == null) {
        response.sendRedirect("/login");
        return false;  // 인증 실패 시 요청 중단
    }
    return true;  // 인증 성공 시 요청 계속 진행
}

인터셉터와 필터의 차이

  • 필터(Filter)는 서블릿 기반으로, 요청을 받는 단계에서 동작하며 Spring과는 독립적으로 작동합니다. 필터는 DispatcherServlet 이전에 실행됩니다.
  • 인터셉터(Interceptor)는 Spring MVC와 밀접하게 통합되어 있으며, DispatcherServlet 이후에 실행됩니다. 또한, 인터셉터는 Spring 컨텍스트 내에서 정의된 빈들과 쉽게 상호작용할 수 있습니다.

결론

인터셉터(Interceptor)는 Spring MVC에서 HTTP 요청을 가로채어 전처리와 후처리를 수행할 수 있는 강력한 도구입니다. 이를 통해 인증, 권한 관리, 로깅, 리소스 정리 등 다양한 기능을 쉽게 구현할 수 있으며, 요청 흐름을 제어하는 데 중요한 역할을 합니다. preHandle(), postHandle(), afterCompletion()을 적절하게 사용하면 애플리케이션의 요청 처리를 더욱 유연하게 관리할 수 있습니다.


Spring MVC에서 자주 사용되는 문자열로 된 특별한 의미를 가진 키워드redirect: 외에도 forward:가 있습니다. 이 키워드는 redirect:와 마찬가지로 단순한 문자열이 아니라 Spring이 특별하게 처리하는 뷰 전환 명령을 나타냅니다.

주요 키워드:

  1. redirect:
    • 의미: 클라이언트에게 리다이렉션을 지시합니다. 클라이언트의 브라우저가 새로운 URL로 요청을 다시 보내도록 만듭니다.
    • 동작: HTTP 응답으로 302 상태 코드를 반환하고, 클라이언트가 새로운 URL로 요청을 보냅니다.
    • 특징: 브라우저의 URL이 변경됩니다.
    • 예시:
    • return "redirect:/new-url";
  2. forward:
    • 의미: 서버 내부에서 요청을 다른 리소스로 포워딩합니다. 이는 서버 내에서 처리되며, 클라이언트는 이를 인식하지 못합니다.
    • 동작: 서버 내에서 요청이 다른 컨트롤러나 JSP로 포워드됩니다. 새로운 HTTP 요청을 보내지 않으며, 클라이언트의 브라우저 URL도 변경되지 않습니다.
    • 특징: 브라우저의 URL이 변경되지 않습니다.
    • 예시:
    • return "forward:/new-url";

redirect:forward:의 차이점:

  • redirect:
    • 클라이언트가 새로운 URL로 다시 요청을 보냅니다.
    • HTTP 응답이 클라이언트에게 전달되면, 클라이언트가 지정된 URL로 새로운 HTTP 요청을 보냅니다.
    • URL이 브라우저에 표시됩니다 (URL 변경).
  • forward:
    • 서버가 내부적으로 다른 리소스에 요청을 전달합니다.
    • 클라이언트는 포워딩된 리소스를 요청했는지 알 수 없습니다.
    • 브라우저의 URL은 변경되지 않습니다.

예시를 통해 두 개의 차이 확인:

1. redirect:

@GetMapping("/old-url")
public String redirectExample() {
    return "redirect:/new-url";
}
  • 사용자가 /old-url에 접근하면, 클라이언트가 /new-url리다이렉트됩니다.
  • URL/new-url로 변경됩니다.

2. forward:

@GetMapping("/old-url")
public String forwardExample() {
    return "forward:/new-url";
}
  • 사용자가 /old-url에 접근하면, 서버 내부에서 /new-url로 요청이 포워딩됩니다.
  • URL이 브라우저에서 변경되지 않고, 여전히 /old-url로 표시됩니다.

그 외 자주 사용되는 키워드

  1. classpath:
    • 의미: 클래스 경로에 있는 리소스를 참조합니다. 주로 설정 파일이나 리소스 파일을 로드할 때 사용됩니다.
    • 예시:
    • Resource resource = new ClassPathResource("config.properties");
    • 여기서 "config.properties"는 클래스 경로에서 로드됩니다.
  2. file:
    • 의미: 파일 시스템에 있는 파일을 참조합니다. 경로에 있는 실제 파일을 읽을 때 사용됩니다.
    • 예시:
    • Resource resource = new FileSystemResource("/path/to/file.txt");
    • 여기서 "/path/to/file.txt"는 파일 시스템 경로에서 로드됩니다.
  3. http:https:
    • 의미: 외부 URL에 있는 리소스를 참조할 때 사용됩니다. 예를 들어, REST API 호출을 하거나 파일을 다운로드할 때 사용할 수 있습니다.
    • 예시:
    • RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject("https://api.example.com/data", String.class);

결론

Spring MVC에서는 redirect:forward:가 자주 사용되는 특별한 의미를 가진 키워드입니다. 이들은 단순한 문자열이 아니라, Spring이 이를 해석하여 리다이렉션 또는 포워딩 동작을 수행합니다. 또한, classpath:, file:, http: 같은 키워드들도 Spring에서 자주 사용되는 특별한 문자열 접두사입니다.


서블릿 필터필터 체인에 등록되어, 클라이언트 요청이 처리되기 전에 실행되는 컴포넌트입니다. 또한, 디스패처 서블릿(DispatcherServlet)보다 먼저 요청을 가로채어 필요한 작업을 수행하고, 필터 체인에서 설정된 순서대로 필터를 거친 후, 요청이 서블릿 또는 디스패처 서블릿으로 전달됩니다.

서블릿 필터의 동작 방식

  1. 필터 등록:
    • 필터는 필터 체인에 등록되며, web.xml 파일이나 애노테이션(@WebFilter)을 사용하여 필터를 등록할 수 있습니다.
    • 여러 개의 필터가 등록된 경우, 필터는 등록 순서대로 실행됩니다.
  2. 필터의 역할:
    • 필터는 요청과 응답을 가로채서 특정 작업을 수행할 수 있습니다. 예를 들어, 인증, 로깅, 캐싱, 데이터 압축, 요청 검증 등의 작업을 처리할 수 있습니다.
    • 필터는 요청(Request)뿐만 아니라 응답(Response)도 가로챌 수 있어, 서버에서 클라이언트로 응답을 보내기 전에 후처리를 수행할 수 있습니다.
  3. 필터 체인:
    • 요청이 들어오면 필터 체인에 등록된 필터들이 순차적으로 실행되며, 마지막 필터까지 처리된 후 디스패처 서블릿이나 다른 서블릿으로 요청이 전달됩니다.
    • 필터는 요청을 처리한 후, chain.doFilter(request, response)를 호출하여 다음 필터로 요청을 전달합니다. 만약 필터에서 이를 호출하지 않으면 요청 처리가 멈추고 해당 필터에서 중단됩니다.
  4. 디스패처 서블릿 전에 동작:
    • DispatcherServlet은 스프링 프레임워크에서 요청을 처리하는 핵심 컴포넌트로, 클라이언트의 요청을 적절한 컨트롤러로 전달하는 역할을 합니다.
    • 필터DispatcherServlet보다 먼저 실행되므로, 필터가 요청을 사전에 처리하고 나서 DispatcherServlet으로 넘깁니다.

필터 실행 흐름

  1. 클라이언트 요청 → 2. 필터 체인 (순차적으로 필터를 통과) → 3. 디스패처 서블릿 또는 서블릿 → 4. 컨트롤러 처리 (디스패처 서블릿) → 5. 필터 체인을 거쳐 응답 처리

필터의 등록 순서

  • 필터의 순서는 매우 중요합니다. web.xml 파일이나 애노테이션을 통해 필터를 등록할 때, 필터가 등록된 순서에 따라 처리 순서가 결정됩니다.
  • 일반적으로 인증 필터는 가장 먼저 실행되고, 이후 로깅 필터데이터 처리 필터들이 뒤따라 실행됩니다.

필터 예시 코드

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")  // 모든 요청에 대해 필터 적용
public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 로직
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 필터 전처리
        System.out.println("LoggingFilter: Request received.");

        // 다음 필터로 전달 (혹은 최종적으로 서블릿/디스패처 서블릿으로)
        chain.doFilter(request, response);

        // 필터 후처리
        System.out.println("LoggingFilter: Response generated.");
    }

    @Override
    public void destroy() {
        // 필터 종료 시 로직
    }
}

필터 체인에 따른 흐름:

  1. 클라이언트가 요청을 보냄.
  2. 필터 체인에 등록된 첫 번째 필터가 요청을 가로채고 처리 후, chain.doFilter()를 통해 다음 필터로 전달.
  3. 모든 필터가 순차적으로 요청을 처리.
  4. 마지막 필터가 chain.doFilter()를 호출하면, 요청은 디스패처 서블릿으로 전달됨.
  5. 디스패처 서블릿이 요청을 컨트롤러로 라우팅하여 로직을 처리.
  6. 응답이 다시 필터 체인을 통해 클라이언트에게 반환됨.

요약:

  • 서블릿 필터는 요청을 사전에 처리하고, 필터 체인에서 설정된 순서대로 필터를 거쳐 마지막에 디스패처 서블릿이나 다른 서블릿으로 요청을 전달합니다.
  • 필터디스패처 서블릿보다 먼저 실행되며, 인증, 로깅, 요청 검증 등의 작업을 수행할 수 있습니다.
  • 필터 체인에 등록된 순서에 따라 실행되며, 각 필터는 요청을 가로채어 필요한 작업을 처리한 후, 다음 필터로 요청을 전달합니다.