Web

Spring boot gradle - filter 등록(적용) 및 특정 URL 제외

송코딩 songcoding 2023. 12. 5. 23:07

안녕하세요~!

오랜만에 스프링 글로 돌아온 김송아입니다.

 

 

11월 말 출강 했을 때 질문 주셨던 내용을, 꼼꼼히 대답해드리고 싶은 마음에 작성하게 된 글입니다 :)

질문 주신 분께서 이 글을 봐주시길 간절히 바라며🥹

 

시작하겠습니다.

 


 

🚀 배경

스프링이 제공하는 공통 프로세스 처리 방법은 크게 3가지가 있습니다. 

1) 필터 (Filter) 
2) 인터셉터 (Intercepter)
3) AOP

 

 

이 세가지의 순서는 대표적으로 다음과 같이 표현됩니다.

 

✔️ 요청 (Request)방향 : 사용자 👉🏻 서버

사용자에게 요청을 받으면, 필터 > 인터셉터 > AOP > 컨트롤러(Controller)로 들어가며

 

✔️ 응답 (Response) 방향 : 서버 👉🏻 사용자

컨트롤러 이후 비즈니스 로직을 수행한 뒤 컨트롤러 > AOP > 인터셉터 > 필터 > 응답으로 사용자에게 전달됩니다.

 

💡 본 글은, 스프링(Spring) 공통 프로세스 처리에 대한 순서(단계)는 이정도로만 간단하게 다룹니다.

 


 

🚀필터(Filter) 적용 (등록)

 

먼저 스프링에 필터를 적용해보도록 하겠습니다.

스프링에 필터를 적용(등록)하는 방법은 다음 단계를 순서대로 따라가시면 됩니다.

 

 

1. Application 클래스에 필터를 스프링 컴포넌트로 등록하기 위한 어노테이션 @ServletComponentScan을 작성해줍니다.

@ServletComponentScan // Filter 등록을 위한 어노테이션
@SpringBootApplication
public class DemoApplication {

 

 

2. Filter 인터페이스를 받아 추상 메서드를 구현해줘야 합니다.

1) 다음과 같이 CustomFilter라는 클래스를 생성하고, implements Filter를 작성해주고,

public class CustomFilter implements Filter {

 

2) 다음과 같이 빨간 Filter에 마우스를 갖다대고, import class를 눌러줍니다.

 

3) 여러가지 class 중에 javax.servlet 이라는 패키지를 선택합니다.

 

4) 그럼 다음과 같이 import가 하나 추가됩니다.

     빨간 클래스 구현부에 다시 한번 마우스를 갖다 대고, 

 

5) 구현이 필요한 추상 메소드들을 받아옵니다.

 

 

 

3. 자동으로 필요한 클래스들이 import되면서, 세 가지의 메소드 틀이 생성됩니다.

1️⃣ init() : Filter가 등록될 때 호출되는 메소드
2️⃣ doFilter() : Filter가 Request/Response 방향에서 사용될 때 호출되는 메소드
3️⃣ destroy() : Filter가 소멸될 때 호출되는 메소드
public class CustomFilter implements Filter {

    // 1) Filter가 등록될 때 호출되는 메소드
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }


    // 2) Filter가 Request/Response 방향에서 사용될 때 호출되는 메소드
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        
    }

    // 3) Filter가 소멸될 때 호출되는 메소드
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

 

 

4. 마지막으로 다음과 같이 @WebFilter 어노테이션을 붙여주고,

doFilter() 메소드에서 Request/Response에 각각 어떤 로그를 찍어볼지 채워줍니다.

package 여러분들의 패키지명;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter; // @WebFilter 어노테이션을 작성하고 import된 패키지
import java.io.IOException;

@WebFilter(urlPatterns = {"/*"}) // 모든 URI에 필터를 등록하겠다는 어노테이션
public class CustomFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter 등록");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;     // Request 꺼내기
        HttpServletResponse res = (HttpServletResponse) response;  // Response 꺼내기

		String requestURI = req.getRequestURI(); // 수행되는 URI
		
        System.out.println("Request 방향 Filter 수행" + requestURI); // Request 방향 Filter
        chain.doFilter(request, response);                         // Filter 실행 기준이 되어주는 체이닝(chanining)
        System.out.println("Response 방향 Filter 수행" + requestURI);// Response 방향 Filter
    }
    
    @Override
    public void destroy() {
        System.out.println("Filter 소멸");
    }
}

 

 

5. 끝!

이제 테스트를 하기 위해 간단하게 컨트롤러를 만들어서

package 여러분들의_패키지명;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    
    @GetMapping("/test1")
    String test() {
        return "test1";
    }

    @GetMapping("/test2")
    String test2() {
        return "test2";
    }

    @GetMapping("/test3")
    String test3() {
        return "test3";
    }
}

 

Spring Boot App을 구동 시켜봅니다.

 

1) 스프링이 올라오면서, Filter 등록이 되고 = init 메소드가 호출되고

 

2) 각 URL (/test1, test2, test3) 으로 접속해보면

- localhost:8080/test1

- localhost:8080/test2

- localhost:8080/test3

 

 

이렇게 각 Request / Response 방향 로그가 찍히는 것을 확인할 수 있습니다👍🏻

 

 


 

🚀 특정 URI 필터(Filter) 제외

 

하지만, 모든 URI (또는 URL)에 필터를 적용하고 싶지 않을 수 있겠죠?

예를 들어 Request 방향으로 들어가면서 필터가 로그인을 체크해야하는 URI와 그렇지 않은 URI가 있다면

우리는 특정 URI에는 로그인을 체크하는 필터를 적용하지 않도록, 제외해줘야 할 것입니다.

 

💡 스프링에서 특정 URI의 필터를 제외하는 방법은 크게 두가지 (.xml 방식과 java 코드에서 구현) 가 있지만,
다른 멋진 블로거 분들이 .xml 방식을 많이들 설명해주셔서 저는 java 코드로 보여드리도록 하겠습니다.

 

 

RequestURI를 활용해서 Request 방향의 필터를 제어해보도록 하겠습니다.

 

 

1. 제외하고 싶은 URI를 배열로 선언합니다.

private static final String[] exceptURIs = {"/test2", "/test3"};

 

 

2. PatternMatchUtils 클래스의 simpleMatch 메소드를 활용해서,

    현재 요청받은 RequestURI가 제외하고 싶은 URI 배열에 포함되는지 확인합니다.

private boolean isFilterURI(String requestURI) {
    return !PatternMatchUtils.simpleMatch(exceptURIs, requestURI);
}

 

 

3. 만약 포함되지 않으면, 다음과 같이 필터를 적용하고 포함되지 않는 URI라면 필터 없이 지나갑니다.

if (isFilterURI(requestURI)) 
    System.out.println("Request 방향 Filter 수행" + requestURI);

 

 

전체 코드를 보면 다음과 같습니다.

package 여러분들의_패키지명;

import org.springframework.util.PatternMatchUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(urlPatterns = {"/*"})
public class CustomFilter implements Filter {

    private static final String[] exceptURIs = {"/test2", "/test3"};

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter 등록");
    }

    @Override
    public void destroy() {
        System.out.println("Filter 소멸");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        String requestURI = req.getRequestURI();

        if (isFilterURI(requestURI))  // 필터 적용할 URI만 필터 적용
            System.out.println("Request 방향 Filter 수행" + requestURI);
            
        chain.doFilter(request, response);
    }

    private boolean isFilterURI(String requestURI) {
        return !PatternMatchUtils.simpleMatch(exceptURIs, requestURI);
    }
}

 

 

 

마찬가지로 Spring Boot App을 구동 시켜서 각 URL (/test1, test2, test3) 으로 접속해보면

 

1) 스프링이 올라오면서, Filter 등록이 되고 = init 메소드가 호출되는 것은 동일하지만

 

2) 제외하기 위해 배열로 선언해둔 URI(URL)는 스프링 필터 없이 지나가는 것을 확인할 수 있습니다.

- localhost:8080/test1 : 스프링 필터 적용

 

- localhost:8080/test2 : 스프링 필터 제외

 

- localhost:8080/test3 : 스프링 필터 제외

 

 

이렇게 특정 URL은 필터를 제외되는 것을 확인할 수 있습니다👍🏻

 

 

 

여기까지 함께 오시느라 고생 많으셨습니다😊

구현해보시고 궁금한 내용 있으시면 언제든지 말씀주세요~!

 

 

감사합니다.

김송아 드림

 

 

 

아니 근데 끄기 전에 잠깐, 

옆집 사는 김송아가 유데미 강의를 냈다고?

 

옆집 개발자 지금 구경가기 👇🏻