[Spring] Filter, Interceptor, AOP*

[verify$y]ยท2025๋…„ 5์›” 26์ผ

Spring

๋ชฉ๋ก ๋ณด๊ธฐ
7/16

๐Ÿ’ก Spring ํ•ต์‹ฌ ๊ฐœ๋… ์ธํ„ฐ๋ทฐ Q&A


Q. Filter๋ž€

  • filter๋Š” servlet์— ์ •์˜๋œ ์ปดํฌ๋„ŒํŠธ๋กœ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ DispatcherServlet์ „์— ๊ฐ€๋กœ์ฑ„์–ด ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ

ํŠน์ง•

  • ์ „์—ญ์œผ๋กœ ๋ชจ๋“  ์š”์ฒญ,์‘๋‹ต ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • Spring๊ณผ ๋ฌด๊ด€ํ•˜๋ฉฐ ServeltContainer์—์„œ ๋™์ž‘
  • ์ธ์ฆ, ์ธ์ฝ”๋”ฉ, CORS์ฒ˜๋ฆฌ, ๋กœ๊ทธ ๊ธฐ๋ก ๋“ฑ์— ์ž์ฃผ ์‚ฌ์šฉ

๋™์ž‘

  • ํด๋ผ์ด์–ธํŠธ์š”์ฒญ -> Filter -> DispathcerServlet
  • ์‘๋‹ต -> Filter -> DispathcerServlet

์˜ˆ์‹œ

  • CORS์ œ์–ด๋ฐฉ๋ฒ•1 : SpringMVC ์ „์—ญ CORS ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ• (WebMvcConfigurer, Spring ์ˆ˜์ค€ ์„ค์ •)

     @Configuration
     public class WebConfig implements WebMvcConfigurer {
    
         @Override
         public void addCorsMappings(CorsRegistry registry) {
             registry.addMapping("/**") // ๋ชจ๋“  ๊ฒฝ๋กœ์— ๋Œ€ํ•ด
                     .allowedOrigins("https://example.com") // ํ—ˆ์šฉํ•  Origin
                     .allowedMethods("GET", "POST", "PUT", "DELETE") // ํ—ˆ์šฉํ•  ๋ฉ”์„œ๋“œ
                     .allowedHeaders("*")
                     .allowCredentials(true) // ์ฟ ํ‚ค ํฌํ•จ ์—ฌ๋ถ€
                     .maxAge(3600); // preflight ์š”์ฒญ ์บ์‹œ ์‹œ๊ฐ„
         }
     }


  • CORS์ œ์–ด๋ฐฉ๋ฒ•2 : Filter๋กœ CORS ์ œ์–ด(Servlet ์ˆ˜์ค€, ๋‚ฎ์€ ์ˆ˜์ค€์—์„œ ์ง์ ‘ ์ œ์–ด ๊ฐ€๋Šฅ)ํ•˜๋Š” ๋ฐฉ๋ฒ•
@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {

        HttpServletResponse res = (HttpServletResponse) response;
        HttpServletRequest req = (HttpServletRequest) request;

        res.setHeader("Access-Control-Allow-Origin", "https://example.com");
        res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
        res.setHeader("Access-Control-Allow-Headers", "*");
        res.setHeader("Access-Control-Allow-Credentials", "true");

        if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
            res.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        chain.doFilter(request, response);
    }
}



Q.interceptor๋ž€?

  • SpringMVC์—์„œ ์ œ๊ณต, controller์‹คํ–‰ ์ „ํ›„์— ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์–ด ์ „์ฒ˜๋ฆฌํ›„์ฒ˜๋ฆฌ ๋กœ์ง์„ ์‚ฝ์ž…

ํŠน์ง•

  • DispatcherServlet ์ดํ›„, Controller ์‹คํ–‰ ์ „/ํ›„์— ๋™์ž‘
  • ๋น„์ฆˆ๋‹ˆ์Šค ๊ด€๋ จ ์ฒ˜๋ฆฌ(๊ถŒํ•œ ๊ฒ€์‚ฌ, ์ธ์ฆ, ์„ธ์…˜ ์ฒดํฌ) ์— ์‚ฌ์šฉ
  • ์ผ๋ฐ˜์ ์œผ๋กœ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ์— ์‚ฌ์šฉ

์˜ˆ์‹œ

  • ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ
  • ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ HttpSession์— ์ €์žฅ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , Interceptor๋ฅผ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ  ๋น„๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ์˜ˆ์‹œ

  1. Interceptor ๊ตฌํ˜„
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        HttpSession session = request.getSession(false);

        if (session == null || session.getAttribute("loginUser") == null) {
            // ๋กœ๊ทธ์ธ ์•ˆ๋œ ๊ฒฝ์šฐ
            response.sendRedirect("/login");
            return false; // ์š”์ฒญ์„ Controller๋กœ ๋ณด๋‚ด์ง€ ์•Š์Œ
        }

        return true; // ๋กœ๊ทธ์ธ ๋˜์–ด์žˆ์œผ๋ฉด Controller๋กœ ์ง„ํ–‰
    }
}


  1. WebMvcConfigurer์— Interceptor ๋“ฑ๋ก
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginCheckInterceptor())
                .addPathPatterns("/**") // ์ „์ฒด ๊ฒฝ๋กœ ๊ฒ€์‚ฌ
                .excludePathPatterns("/", "/login", "/logout", "/css/**", "/js/**", "/images/**"); // ์˜ˆ์™ธ ๊ฒฝ๋กœ
    }
}



์ธํ„ฐ์…‰ํ„ฐ ๋™์ž‘ํ๋ฆ„

ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ
    โ†“
DispatcherServlet
    โ†“
LoginCheckInterceptor (preHandle)
    โ†“
Controller
    โ†“
LoginCheckInterceptor (postHandle / afterCompletion)



์ธํ„ฐ์…‰ํ„ฐ ์ฃผ์˜ํ•  ์ 

  • preHandle()์—์„œ false๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์š”์ฒญ์ด ๊ฐ€์ง€ ์•Š์Œ
  • ๋กœ๊ทธ์ธ๋œ ์œ ์ € ์ •๋ณด๋Š” ๋ณดํ†ต session.getAttribute("loginUser") ๋˜๋Š” Spring Security ์‚ฌ์šฉ ์‹œ SecurityContextHolder๋กœ ํ™•์ธ
  • postHandle() โ†’ ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰ ํ›„ View ๋ Œ๋”๋ง ์ „
  • afterCompletion() โ†’ ์‘๋‹ต ์™„๋ฃŒ ์งํ›„, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์šฉ์œผ๋กœ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ



JWT ๊ธฐ๋ฐ˜ ๋กœ๊ทธ์ธ ๊ฒ€์‚ฌ Interceptor ์˜ˆ์‹œ

  • JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ์—์„œ๋Š” Interceptor์—์„œ Authorization ํ—ค๋”์— ํฌํ•จ๋œ ํ† ํฐ์„ ํŒŒ์‹ฑํ•˜๊ณ  ๊ฒ€์ฆํ•˜์—ฌ,์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์š”์ฒญ์„ ์ฐจ๋‹จํ•˜๊ณ , ์œ ํšจํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

  1. JWT ๊ฒ€์ฆ ๋กœ์ง์ด ํฌํ•จ๋œ Interceptor
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {

    private final JwtTokenProvider jwtTokenProvider; //์ง์ ‘๊ตฌํ˜„ํ•œJWT ์œ ํ‹ธํด๋ž˜์Šค

    // ์ƒ์„ฑ์ž ์ฃผ์ž…
    public JwtAuthInterceptor(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        String authHeader = request.getHeader("Authorization");

        // ํ† ํฐ ์—†์Œ
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        String token = authHeader.substring(7); // "Bearer " ์ œ๊ฑฐ

        // ํ† ํฐ ๊ฒ€์ฆ
        if (!jwtTokenProvider.validateToken(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        // ์‚ฌ์šฉ์ž ์ •๋ณด ์ถ”์ถœ (Optional)
        String userId = jwtTokenProvider.getUserIdFromToken(token);
        request.setAttribute("userId", userId); // ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ™œ์šฉ ๊ฐ€๋Šฅ

        return true;
    }
}


  1. WebMvcConfigurer์— ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final JwtAuthInterceptor jwtAuthInterceptor;

    public WebConfig(JwtAuthInterceptor jwtAuthInterceptor) {
        this.jwtAuthInterceptor = jwtAuthInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtAuthInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/auth/**"); // ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž…์€ ์˜ˆ์™ธ
    }
}





Q. Filter, Interceptor, AOP์˜ ์ฐจ์ด

  • ์œ„ 3๊ฐ€์ง€๋Š” ์š”์ฒญ-์‘๋‹ต ํ๋ฆ„์„ ๊ฐ€๋กœ์ฑ„๋Š” ๊ตฌ์กฐ์ด์ง€๋งŒ, ์ ์šฉ ๊ณ„์ธต๊ณผ ๋ชฉ์ ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.
  • Filter๋Š” ์„œ๋ธ”๋ฆฟ ๋ ˆ๋ฒจ์—์„œ ๋™์ž‘ํ•˜๋ฉฐ, DispatherServlet์ด์ „์— ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„์–ด ์ธ์ฝ”๋”ฉ, ์ธ์ฆ, ๋กœ๊น… ๊ฐ™์€ ์ „์—ญ ์ฒ˜๋ฆฌํ•œ๋‹ค
  • interceptor๋Š” SpringMVC์ˆ˜์ค€์—์„œ ๋™์ž‘ํ•˜๋ฉฐ ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰ ์ „/ํ›„์— ์„ธ์…˜์ฒดํฌ, ๊ถŒํ•œ ๊ฒ€์‚ฌ ๋“ฑ์„ ์ˆ˜ํ–‰
  • AOP๋Š” ๋ฉ”์„œ๋“œ ๋‹จ์œ„์—์„œ ๋™์ž‘ํ•˜๋ฉฐ, ํŠธ๋žœ์žญ์…˜ ๋กœ๊น… ์˜ˆ์™ธ ๋“ฑ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ํ•ต์‹ฌ ๋กœ์ง๊ณผ ๋ถ„๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ

์š”์•ฝ

  • Filter๋Š” ์„œ๋ธ”๋ฆฟ ๋ ˆ๋ฒจ์—์„œ ๋ชจ๋“  ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ , Interceptor๋Š” Spring MVC ๋ ˆ๋ฒจ์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰ ์ „/ํ›„ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ํˆด



Q. AOP๋ฅผ ์“ฐ๋Š” ์ด์œ 

  • AOP๋Š” ์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ€๊ฐ€์ ์ธ ๊ณตํ†ต ๊ด€์‹ฌ์‚ฌ(ํŠธ๋žœ์žญ์…˜, ๋กœ๊น…, ์ธ์ฆ ๋“ฑ)๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ์ˆ 
  • ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ณ , ํ•ต์‹ฌ ๋กœ์ง์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์–ด ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.



Q.AOP๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ(๋™์ž‘์กฐ๊ฑด)

  • Spring AOP๋Š” ํ”„๋ก์‹œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ž˜ ์กฐ๊ฑด์—์„œ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Spring Bean์ด ์•„๋‹Œ ๊ฐ์ฒด์— ์ ์šฉํ•œ ๊ฒฝ์šฐ
  • private, final, static ๋ฉ”์„œ๋“œ์—๋Š” AOP ์ ์šฉ ๋ถˆ๊ฐ€
  • ๊ฐ™์€ ํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ ์ž๊ธฐ ์ž์‹ ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ (ํ”„๋ก์‹œ ์šฐํšŒ ๋ฐœ์ƒ)



Q. Filter์™€ Interceptor์ค‘ ์„ ํƒ ๊ธฐ์ค€์€

  • ์ „์—ญ์ ์œผ๋กœ HTTP ์š”์ฒญ ์ „์ฒด๋ฅผ ์ œ์–ดํ•ด์•ผ ํ•œ๋‹ค๋ฉด โ†’ Filter(์˜ˆ: CORS, ์ธ์ฝ”๋”ฉ, XSS ํ•„ํ„ฐ๋ง ๋“ฑ)
  • ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰ ์ „ํ›„์˜ ๋น„์ฆˆ๋‹ˆ์Šค ์ „์ฒ˜๋ฆฌ/ํ›„์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด โ†’ Interceptor
    (์˜ˆ: ๋กœ๊ทธ์ธ ์ฒดํฌ, ํŠน์ • ์š”์ฒญ ๋กœ๊น… ๋“ฑ)


Q. Interceptor์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰ ๋ง‰์„ ์ˆ˜ ์žˆ๋Š”์ง€?

  • ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
  • Interceptor์˜ preHandle() ๋ฉ”์„œ๋“œ์—์„œ false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด, DispatcherServlet์€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์š”์ฒญ์„ ์ฆ‰์‹œ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.
  • false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด Spring MVC๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์š”์ฒญ์„ ์ค‘๋‹จํ•˜๋ฏ€๋กœ, ๋กœ๊ทธ์ธ ๋˜๋Š” ๊ถŒํ•œ ๊ฒ€์ฆ์‹œ ํšจ๊ณผ์ 

๋™์ž‘์ˆœ์„œ

1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋ƒ„
2. DispatcherServlet์ด ์š”์ฒญ์„ ์ˆ˜์‹ 
3. ๋“ฑ๋ก๋œ Interceptor์˜ preHandle() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
   โ†’ ์ด๋•Œ preHandle()์—์„œ false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด 4๋ฒˆ
4. ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์—†์ด ์š”์ฒญ ์ข…๋ฃŒ
   โ†’ ์ดํ›„ View๋„ ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Œ
   โ†’ ์‘๋‹ต ์ฝ”๋“œ(์˜ˆ: 401) ๋˜๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฒ˜๋ฆฌ ํ•„์š” 
   ๋

(๋ฐ˜๋Œ€๋กœ preHandle()์—์„œ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด 5๋ฒˆ)

5. ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰
6. postHandle(), afterCompletion() ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰
	๋
profile
welcome

0๊ฐœ์˜ ๋Œ“๊ธ€