Spring

[Spring] @WebMvcTest 401 에러 해결: @AutoConfigureMockMvc vs excludeFilters

leevigong 2025. 12. 4. 23:00
반응형

문제 상황

Spring Security + JWT가 적용된 프로젝트에서 @WebMvcTest로 컨트롤러 테스트 시 JWTFilter 및 SecurityConfig도 제외하면 정상적으로 테스트가 성공할 줄 알았지만 401 Unauthorized 에러가 발생했다

@WebMvcTest(
    controllers = RankController.class,
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JWTFilter.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
    }
)
class MockRankControllerTest {
	...
}

문제 해결 방안

은 엄청 간단하다

@AutoConfigureMockMvc(addFilters = false)를 추가하면 된다

@WebMvcTest(
    controllers = RankController.class,
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JWTFilter.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
    }
)
@AutoConfigureMockMvc(addFilters = false)
class MockRankControllerTest {
	...
}

@AutoConfigureMockMvc(addFilters = false) 와 @WebMvcTest의 excludeFilters 차이점

간단하게 JWTFilter와 SecurityConfig는 다음과 같이 구현되어 있다

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	
}
@Component
public class JWTFilter extends OncePerRequestFilter {

}

 

mockMvc를 디버깅해보면 filters 부분에 springSecurityFilterChain, JWTFilter가 있는 걸 볼 수 있다

- springSecurityFilterChain 는 기본적으로 spring-security 의존성을 추가했을 때 자동으로 생성된다

- JWTFilter는 Filter를 상속받아 구현하여서 생성된다

 

@WebMvcTest의 excludeFilters 

excludeFilters는 Component Scan 할 때 특정 클래스를 제외하는 필터이다

 

문제 상황에서 mockMvc를 다시 디버깅해보면 springSecurityFilterChain 유지, JWTFilter는 없어진 걸 볼 수 있다

excludeFilters로 SecurityConfig를 제외해도, Spring Security 자체는 여전히 활성화되어 있어 springSecurityFilterChain 유지가 된다

왜냐하면 SecurityConfig는 Filter가 아니라 설정 클래스이기 때문에 단지 SecurityConfig로 구현한 부분이 springSecurityFilterChain에 등록이 된다

 

동작 방식

1. Spring Context 로딩 시작
   
2. Component Scan 실행
   ├─ excludeFilters 확인
      └─ SecurityConfig.class 제외
      └─ JWTFilter.class 제외
   
3. Bean 등록
   └─ RankController -> Bean 등록
   
4. Auto-configuration 실행
   └─ springSecurityFilterChain
      (SecurityConfig가 없어도 기본 설정 적용)
   
5. MockMvc 생성
   └─ addFilters = true (기본값)
   └─ Spring Context에서 모든 Filter Bean 수집
      └─ springSecurityFilterChain 포함
   
6. MockMvc 최종 구조
   mockMvc {
     filters = [springSecurityFilterChain]
   }
   
7. 테스트 실행
   요청 → springSecurityFilterChain → 인증 체크 → 401 에러 발생 !!

 

@AutoConfigureMockMvc(addFilters = false)

MockMvc 생성 시, 모든 Servlet Filter를 비활성화하는 옵션으로 모든 filters가 제외되어 인증 없이 컨트롤러 테스트만 가능하다!

 

mockMvc 디버깅 결과: Filter 배열이 0이다

 

동작 방식

1. Spring Context 로딩 시작
   
2. Component Scan 실행

3. Bean 등록
   ├─ SecurityConfig -> Bean 등록
   ├─ JWTFilter -> Bean 등록
   └─ RankController -> Bean 등록
   
4. Auto-configuration 실행
   └─ springSecurityFilterChain -> Bean 생성
   
5. MockMvc 생성
   └─ @AutoConfigureMockMvc 확인
      └─ addFilters = false 발견 -> 모든 필터 제거!
   
6. MockMvc 최종 구조
   mockMvc {
     filters = [] 
   }
   
7. 테스트 실행
   요청 → (필터 없음) → DispatcherServlet → Controller 테스트 성공

 

 

결론

- excludeFilters: Bean 로딩만 제어 → Auto-configuration은 막을 수 없다

- addFilters = false: 필터 실행 자체를 차단 → Security 완전 비활성화한다

 

따라서, 컨트롤러 단위 테스트에는 @AutoConfigureMockMvc(addFilters = false) 사용하면 필터 관련 문제를 해결할 수 있다

반응형