회원가입 기능을 구현하고 /api/auth/signup
경로로 API 요청을 테스트하던 중, HTTP 200 OK 응답만 반환되고, 요청이 컨트롤러에 도달하지 않는 문제가 발생했습니다. 잘못된 URL로 요청해도 동일하게 200 OK 응답이 반환되어, 필터 체인이 요청을 제대로 전달하지 않는 것 같았습니다. 이 문제를 해결하는 과정에서 서버 시작 후 동작하는 순서와 필터 체인의 흐름에 대해 깊이 이해하게 되었습니다.
SecurityFilterChain
설정을 재확인하며 인증이 필요한 요청과 허용된 요청을 제대로 구분하는지 점검했습니다./api/auth/**
경로에 대해서만 permitAll()
로 설정하여 인증 없이 접근 가능하도록 했으며, 그 외 모든 요청에 대해 authenticated()
로 설정하여 인증을 요구하도록 했습니다.STATELESS
로 설정했으나 여전히 모든 요청에서 200 OK 응답만 반환되는 상태였습니다.@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// csrf 해제
http.csrf((csrf) -> csrf.disable());
// 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
http.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resource 접근 허용 설정
.requestMatchers("/api/auth/**").permitAll() // '/api/auth/' 경로의 요청 모두 접근 허가 (회원가입, 로그인)
.anyRequest().authenticated() // 그 외 모든 요청 인증처리
);
// 필터 관리
http.addFilterBefore(jwtAuthorizationFilter(), JwtAuthenticationFilter.class);
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
JwtAuthenticationFilter
와 JwtAuthorizationFilter
에 디버그 로그를 추가했습니다.FilterChainProxy
가 요청을 잘 받아들이고 있었으나, 요청이 JwtAuthorizationFilter
에서 차단된 듯 보였습니다.FilterChainProxy
의 흐름을 자세히 추적하며 요청이 doFilter()
를 통해 다음 필터나 컨트롤러로 전달되지 않는 문제를 확인했습니다.doFilter()
호출 누락 발견JwtAuthorizationFilter
내부에 있는 filterChain.doFilter(request, response);
호출 위치에서 발생했습니다.filterChain.doFilter()
가 조건문 내부에 위치해 있어, 특정 조건을 만족하지 않으면 doFilter()
가 호출되지 않았고, 결국 요청이 컨트롤러로 전달되지 않았던 것입니다.FilterChainProxy
가 요청을 차단한 상태에서 기본 상태 코드인 200 OK와 비어 있는 응답 본문이 반환되었습니다.@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String tokenValue = jwtUtil.getJwtFromHeader(request);
if (StringUtils.hasText(tokenValue)) {
if (!jwtUtil.validateToken(tokenValue)) {
log.error("Token Error");
return;
}
Claims info = jwtUtil.getMemberInfoFromToken(tokenValue);
try {
setAuthentication(info.getSubject());
} catch (Exception e) {
log.error(e.getMessage());
return;
}
}
filterChain.doFilter(request, response); // 조건문 밖으로 이동하여 항상 다음 필터 호출 보장
}
이 문제를 해결하는 과정에서 서버가 시작될 때 Spring Security와 필터들이 어떻게 동작하는지, 그리고 필터 체인의 흐름을 이해할 수 있었습니다.
SecurityFilterChain
이 초기화되며, 등록된 필터들이 차례로 설정됩니다.FilterChainProxy
를 통해 필터 체인을 관리하고, 각 요청에 대해 적용할 필터를 순서대로 호출합니다.WebSecurityConfig
에서 등록한 필터들이 특정 순서대로 FilterChainProxy
에 추가됩니다. 이때 UsernamePasswordAuthenticationFilter
와 같은 필터를 상속받는 필터는 Spring Security의 인증 처리에 관련된 기본 필터들과 연결됩니다.FilterChainProxy
가 이를 가로채어 등록된 필터들을 통과시킵니다. 이때 각 필터가 doFilter()
메서드를 통해 다음 필터로 요청을 전달하며 인증과 인가를 처리합니다.이번 문제에서는 필터 체인의 흐름이 doFilter()
누락으로 끊겼고, FilterChainProxy
가 요청을 차단한 상태에서 200 OK 응답만 반환되었습니다.
조건문 안에 있던 filterChain.doFilter()
호출을 조건문 밖으로 이동한 후, 요청이 정상적으로 컨트롤러에 전달되어 회원가입 기능이 제대로 작동하였습니다. Postman 테스트에서도 요청이 잘 전달되고 JWT가 반환되는 것을 확인했습니다.
사소한 실수 하나가 전체 요청 흐름에 큰 영향을 미쳤습니다. doFilter() 위치 오류로 인해 컨트롤러에 도달하지 않는 상황을 해결하면서, 필터 체인과 Spring Security의 동작 방식을 깊이 이해할 수 있었습니다. 앞으로는 비슷한 문제가 발생할 때 내가 작성한 코드부터 꼼꼼히 살피며 실수를 점검하는 것이 중요하다는 점을 느꼈습니다.