Filter

๋ž ๋œจยท2025๋…„ 2์›” 18์ผ

๐Ÿ”Ž Overview

ย  ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ๋ณต์Šตํ•˜๋˜ ๋„์ค‘, '์ถ”ํ›„ ํ•™์Šตํ•ด์•ผ์ง€' ํ•˜๊ณ  ๋„˜์–ด๊ฐ”๋˜ ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

ย ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ CustomFilter ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, OncePerRequestFilter ๋ผ๋Š” ๊ฒƒ์„ ์ƒ์†๋ฐ›๋Š”๋‹ค.
์ด๋ฆ„๋งŒ ๋ณด๊ณ ๋„ ์š”์ฒญ๋งˆ๋‹ค ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š” ํ•„ํ„ฐ๋ผ๋Š” ๊ฒƒ์€ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
ํ•˜์ง€๋งŒ ์ด ํ•„ํ„ฐ๊ฐ€ ์–ธ์ œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด๊ณ , ์ •ํ™•ํžˆ ์–ด๋–ค ๊ฒƒ์ธ์ง€๋Š” ํ™•์‹คํžˆ ์•Œ์ง€ ๋ชปํ•œ๋‹ค.

ย  ์ด๋ฒˆ ๊ธฐํšŒ์— ์ด์ „์— ๋„˜๊ฒผ๋˜ ์ด OncePerRequestFilter ๋ฅผ ๋ฉ”์ธ์œผ๋กœ, ํ•„ํ„ฐ์—๋Š” ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๊ณ  ์–ด๋–ป๊ฒŒ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š”์ง€ ๋“ฑ์„ ์ •๋ฆฌํ•ด๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๋ณด๊ณ ์ž ์ด ํฌ์ŠคํŒ…์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.


๐Ÿ“‘ OncePerRequestFilter

  • Spring ์—์„œ ์ œ๊ณตํ•˜๋Š” Filter ๋กœ, ํ•œ ์š”์ฒญ ๋‹น ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ํ•„ํ„ฐ
    • ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด, ํ•˜๋‚˜์˜ ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ํ•„ํ„ฐ ์ฒด์ธ์„ ํ†ต๊ณผํ•  ์ˆ˜๋„ ์žˆ์Œ
    • OnceperRequestFilter ์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๊ฒƒ์„ ๋ฏธ์—ฐ์— ๋ฐฉ์ง€ํ•˜๊ณ , ํ•œ ์š”์ฒญ์— ๋Œ€ํ•ด ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ
  • ๋‚ด๋ถ€์ ์œผ๋กœ isAsyncDispatch() ์™€ shouldNotFilter() ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์š”์ฒญ ์ด๋‚˜ ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ํ•„ํ„ฐ ์‹คํ–‰ ์ œ์™ธ ๋„ ๊ฐ€๋Šฅ
public abstract class OncePerRequestFilter implements Filter {
	@Override
    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // ๋น„๋™๊ธฐ ์š”์ฒญ์€ ํ•„ํ„ฐ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋„๋ก ์„ค์ •
        if (isAsyncDispatch(httpRequest)) {
        	filterchain.doFilter(request, response);
            return;
        }
        
        doFilterInternal(httpRequest, httpResponse, filterChain);
    }
}
  • ์ธ์ฆ๊ณผ ์ธ๊ฐ€์˜ ๊ฒฝ์šฐ, ์š”์ฒญ ๋‹น ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์—(ex: JWT ํ† ํฐ ๊ฒ€์‚ฌ), ์ธ์ฆ, ์ธ๊ฐ€์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
	@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	String token = request.getHeader("Authorization");
        
        if (token != null && token.startsWith("Bearer ")) {
        	String jwt = token.substring(7);
        }
        
        // jwt ํ™œ์šฉ
        
        filterChain.doFilter(request, response);
    }
}
  • ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•œ ํ•œ ๋ฒˆ์˜ ๋กœ๊น…๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋„ ์‚ฌ์šฉ
@Component
@Slf4j
public class LoggingFilter extends OncePerRequestFilter {
	@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	log.info("ํ•„ํ„ฐ ๋กœ๊ทธ");
        filterChain.doFilter(request, response);
    }
}
  • ํŠน์ • ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ํ—ค๋” ๋ณ€๊ฒฝ ์‹œ์—๋„ ์‚ฌ์šฉ
@Component
public class CustomHeaderFilter extends OncePerRequestFilter {
	@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	response.setHeader("newHeader", "header_value")
        filterChain.doFilter(request, response);
    }
}
  • CORS (Cross-Origin Resource Sharing) ํ•„ํ„ฐ ๋กœ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅ
  • ๋‹ค๋ฅธ ๋„๋ฉ”์ธ(์ถœ์ฒ˜)์—์„œ ์š”์ฒญ์ด ์˜ฌ ๊ฒฝ์šฐ, ์ด์— ๋”ฐ๋ฅธ CORS ๊ทœ์น™์„ ์ ์šฉ
  • ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋„ ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ํ•œ ๋ฒˆ๋งŒ ์ ์šฉํ•˜๋ฉด ๋˜๋ฏ€๋กœ, OncePerRequestFilter ์‚ฌ์šฉ
@Component
public class CustomCorsFilter extends OncePerRequestFilter {
	@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        
        filterChain.doFilter(request, response);
    }
}
๋ชจ๋“  `GET` ์š”์ฒญ์— ๋Œ€ํ•œ `CORS` ์ •์ฑ… ์‚ฌ์šฉ

๐Ÿค” OncePerRequestFilter ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 


1. ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€

  • ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ ์ฒด์ธ์€ ๋‹ค์–‘ํ•œ ํ•„ํ„ฐ๋ฅผ ๊ฑฐ์น˜๋ฉฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, ๊ฐ™์€ ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ๋™์ผํ•œ ํ•„ํ„ฐ๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜๋„ ์žˆ์Œ
  • RequestDispatcher.forward() ๋‚˜ include() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ™์€ ์š”์ฒญ์ด ํ•„ํ„ฐ๋ฅผ ๋‹ค์‹œ ํƒˆ ์ˆ˜ ์žˆ์Œ
  • ์ด๋Ÿฌํ•œ ์ค‘๋ณต ์‹คํ–‰์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž OncePerRequestFilter ๋ฅผ ์‚ฌ์šฉ

2. ์š”์ฒญ ์Šค๋ ˆ๋“œ์˜ ์ผ๊ด€์„ฑ ์œ ์ง€

  • ํ•„ํ„ฐ ๋‚ด๋ถ€์—์„œ SecurityContext ๊ฐ™์€ ์š”์ฒญ ๊ด€๋ จ ์ •๋ณด๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ํ•„ํ„ฐ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋  ๊ฒฝ์šฐ ์ •๋ณด๊ฐ€ ๋ฎ์–ด์”Œ์›Œ์งˆ ์ˆ˜๋„ ์žˆ์Œ
  • OncePerRequestFilter ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„ํ„ฐ๊ฐ€ ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ ์ •๋ณด๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Œ

3. ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ

  • ๋ณด์™„ ๊ด€๋ จ ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ
  • UsernamePasswordAuthenticationFilter ๊ฐ™์€ ๊ธฐ๋ณธ ํ•„ํ„ฐ๋“ค์ด ๋‚ด๋ถ€์ ์œผ๋กœ OncePerRequestFilter ๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉ

โญ ํ•„ํ„ฐ์—์„œ ์ž์ฃผ ์“ฐ์ด๋Š” ๊ธฐ๋Šฅ

1. forward

  • ํ˜„์žฌ ์š”์ฒญ์„ ์ƒˆ๋กœ์šด URL๋กœ ์ด๋™์‹œํ‚ด
  • ๋‹จ, ํด๋ผ์ด์–ธํŠธ๋„ ์ด๋™ ์‚ฌ์‹ค์„ ๋ชจ๋ฅด๊ฒŒ ์‹คํ–‰
  • ์š”์ฒญ์„ ๋‹ค๋ฅธ Servlet ๋ฐ JSP ๋กœ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉ
  • ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ (๊ธฐ์กด ์š”์ฒญ ๊ฐ์ฒด ์œ ์ง€)
  • ํด๋ผ์ด์–ธํŠธ๋Š” URL์ด ๋ฐ”๋€Œ์ง€ ์•Š์Œ
request.getRequestDispatcher("/").forward(request, response);

2. redirect

  • ํ˜„์žฌ ์š”์ฒญ์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ƒˆ๋กœ์šด URL๋กœ ์ด๋™ํ•˜๋ผ๊ณ  ์‘๋‹ต
  • ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋ณด๋ƒ„
  • ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ์šด ์š”์ฒญ์ด ์ƒ์„ฑ๋จ
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ƒˆ๋กœ์šด URL์„ ์š”์ฒญํ•˜๋„๋ก ์œ ๋„ (๋ธŒ๋ผ์šฐ์ €์˜ URL๋„ ๋ณ€๊ฒฝ)
  • ์ผ๋ฐ˜์ ์œผ๋กœ POST ์š”์ฒญ ํ›„ redirect ๋ฅผ ํ†ตํ•ด GET ์š”์ฒญ์œผ๋กœ ๋ฐ˜ํ™˜
response.sendRedirect("/");

3. include

  • ํ˜„์žฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์„œ๋ธ”๋ฆฟ ๋˜๋Š” JSP์˜ ์ถœ๋ ฅ์„ ํฌํ•จ
  • ๊ณตํ†ต ํ—ค๋”, ํ‘ธํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์—์„œ ํฌํ•จํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ
  • ๊ธฐ์กด ์š”์ฒญ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ผ๋ถ€ ์‘๋‹ต ํฌํ•จ
  • forward ์™€์˜ ์ฐจ์ด์ ์€, ๊ธฐ์กด ํŽ˜์ด์ง€ ์ผ๋ถ€์— ๋‹ค๋ฅธ ์„œ๋ธ”๋ฆฟ ๋‚ด์šฉ์„ ํฌํ•จํ•œ๋‹ค๋Š” ์ 
RequestDispatcher dispatcher = request.getRequestDispatcher("/header.jsp");
dispatcher.include(request, response);

4. setAttribute

  • ์š”์ฒญ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ ๋‹ค๋ฅธ ์„œ๋ธ”๋ฆฟ/JSP์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ฆ
  • request.setAttribute() ๋กœ ์ €์žฅํ•œ ๋ฐ์ดํ„ฐ๋Š” ์š”์ฒญ์ด ์œ ์ง€๋˜๋Š” ๋™์•ˆ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • forward ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์šฉํ•จ
request.setAttribute("userRole", "ADMIN");

๐Ÿ“– ๊ทธ ์™ธ ํ•„ํ„ฐ์˜ ์ข…๋ฅ˜

1. GenericFilterBean

  • ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ํ•„ํ„ฐ
  • ์Šคํ”„๋ง์˜ Bean ์œผ๋กœ ๋“ฑ๋ก๋˜์–ด DI ๋ฅผ ์‰ฝ๊ฒŒ ๊ฐ€๋Šฅ
  • ์š”์ฒญ๋‹น ํ•œ ๋ฒˆ ์‹คํ–‰์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
  • ํ•„์š”ํ•˜๋‹ค๋ฉด ์ง์ ‘ ์‹คํ–‰ ํšŸ์ˆ˜ ์กฐ์ ˆ์ด ํ•„์š”
@Component
public class CustomFilter extends GenericFilterBean {
	@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    	filterChain.doFilter(request, response);
    }
}

2. CharacterEncodingFilter

  • ์š”์ฒญ๊ณผ ์‘๋‹ต์˜ ๋ฌธ์ž ์ธ์ฝ”๋”ฉ์„ ์„ค์ •ํ•˜๋Š” ํ•„ํ„ฐ
  • ์ผ๋ฐ˜์ ์œผ๋กœ UTF-8 ์ธ์ฝ”๋”ฉ์„ ์ ์šฉํ•  ๋•Œ ์‚ฌ์šฉ
  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•ด๋‹น ์ธ์ฝ”๋”ฉ์„ ๊ฐ•์ œํ•˜๋„๋ก ์„ค์ • ๊ฐ€๋Šฅ
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodiginFilter() {
	CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("UTF-8");
    filter.setForceEncoding(true);
    
    FilterRegistrationBean<CharacterEncodingFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(filter);
    return registrationBean;
}

3. CorsFilter

  • CORS ์„ค์ •์„ ๋‹ด๋‹นํ•˜๋Š” ํ•„ํ„ฐ
  • ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์˜ ์š”์ฒญ์„ ํ—ˆ์šฉํ• ์ง€ ๊ฒฐ์ •
  • @CrossOrigin ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ํŠน์ • ๋„๋ฉ”์ธ์—์„œ๋งŒ ์š”์ฒญ์„ ํ—ˆ์šฉํ•˜๋„๋ก ์„ค์ •๋„ ๊ฐ€๋Šฅ
@Bean
public CorsFilter corsFilter() {
	UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*")	// ๋ชจ๋“  ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์š”์ฒญ ํ—ˆ์šฉ
    config.addAllowedMethod("*")	// ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ ์š”์ฒญ ํ—ˆ์šฉ
    config.addAllowedHeader("*")	// ๋ชจ๋“  ํ—ค๋” ์š”์ฒญ ํ—ˆ์šฉ
    
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

4. HiddenHttpMethodFilter

  • PUT , DELETE HTTP ๋ฉ”์„œ๋“œ๋ฅผ POST ์š”์ฒญ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
  • <form> ํƒœ๊ทธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ GET ๊ณผ POST ๋งŒ ์ง€์›ํ•˜๋ฏ€๋กœ, ์ด๋ฅผ ํ•ด๊ฒฐํ•  ๋•Œ ์œ ์šฉ

๐Ÿ“š doFilter()์™€ doFilterInternal()

1. doFilter()

  • javax.servlet.Filter ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ
  • ๋ชจ๋“  ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋Š” doFilter() ์˜ ์ง์ ‘ ๊ตฌํ˜„์ด ํ•„์š”
  • ์š”์ฒญ ์‹œ doFilter() ๊ฐ€ ์‹คํ–‰๋˜๊ณ , ๋‹ค์Œ ํ•„ํ„ฐ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” filterChain.doFilter(request, response) ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•จ
  • ์ฆ‰, Filter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” doFilter() ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์•ผ ํ•จ

2. doFilterInternal()

  • OncePerRequestFilter ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒ ๋ฉ”์„œ๋“œ
  • doFilter() ์˜ ๋‚ด๋ถ€์—์„œ doFilterInternal() ์ด ํ˜ธ์ถœ
  • ๋”ฐ๋ผ์„œ, ๊ฐœ๋ฐœ์ž๋Š” doFilter() ๋Œ€์‹  doFilterInternal() ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋จ
  • doFilter() ๋Š” OncePerRequestFilter ๊ฐ€ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋ฒˆ ํ•„ํ„ฐ ์ฒด์ธ์„ ํ†ต๊ณผํ•˜๋Š” ๊ฒƒ์„ ๋ฏธ์—ฐ์— ๋ฐฉ์ง€

โœ’๏ธ ์ •๋ฆฌ

  • ์ผ๋ฐ˜์ ์ธ Filter ์ธํ„ฐํŽ˜์ด์Šค์—์„œ๋Š” doFilter() ๋ฅผ ๊ตฌํ˜„
  • OncePerRequestFilter ๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค๋ฉด, ๋Œ€์‹  doFilterInternal() ๋งŒ ๊ตฌํ˜„

์ฐธ๊ณ ) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/

profile
๊ธฐ๋ก

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