[Project] SpringBoot 테스트 시 Security 설정을 효율적으로 제어하기

bagt13·2025년 12월 30일

Project

목록 보기
21/23
post-thumbnail

기본 기능들을 구현하고, Spring Security를 통해 인증/인가를 적용했을때 통합 테스트에서 어려움을 겪었다.


먼저, 내 테스트 전략은 이러했다.

  1. 인증에 대한 테스트는 별도로 수행한다. (custom filter/security filter의 동작 유무)

  2. 따라서 slice test나 domain integration test에는 인증이 간섭해서는 안된다.

    • 이렇게 목적에 따라 테스트를 분리해야 가독성과 유지보수에 좋고, 테스트가 과도하게 무거워지는걸 방지할 수 있다.

이 목적을 달성하려면, 컨트롤러 단위 테스트와 통합 테스트 시 별도의 처리가 필요하다.
(service unit test는 시큐리티 설정의 영향을 받지 않음)


Controller Slice Test

먼저, controller 단위 테스트는 간단하게 인증 과정을 배제할 수 있다.

controller 테스트 클래스에 @AutoConfigureMockMvc(addFilters = false) 를 추가해주면 된다.

@WebMvcTest(MatchController.class)
@AutoConfigureMockMvc(addFilters = false)
class MatchControllerTest {

먼저, @AutoConfigureMockMvc의 기본 역할은 다음과 같다.

Spring Text Context에서 MockMvc를 자동으로 설정하고 Bean으로 등록해준다.

MockMvc는 기본적으로 실제 애플리케이션처럼 모든 서블릿 필터(Servlet Filters)를 거쳐가도록 설계되어 있다. (Spring Security Filter Chain 포함)

하지만, addFilters=false가 filter chain을 무력화시키기 때문에, 인증 설정을 배제할수 있게 되고, 순수 컨트롤러 로직만 검증할 수 있다.

만일 보안 로직을 포함해서 테스트하고싶다면 @WithMockUser를 쓰면 된다.



Integration Test

게시글의 커서 기반 조회 테스트에서도 같은 문제가 발생했다. service-repository 흐름을 보는 domain test였는데, 통합 테스트는 JwtProvider, JwtFilter, SecurityConfig 등 인증 관련 Bean을 포함한 모든 빈을 로드하고 주입하기 때문에 여기서도 인증 설정을 배제해야했다.

방법1: SecurityConfig를 테스트 환경에서 아예 배제한다. (@Profile(”!test”))

  • 이 방식은 성공했지만, 정작 시큐리티가 필요한 인증 로직 테스트를 수행할 수 없게 된다.

방법2: property 값에 따라 on/off를 설정한다. (@ConditionalOnProperty)

  • 이 방식은 profile에 따라 property를 분리하면 편하게 사용할 수 있다.
@RequiredArgsConstructor
@Configuration
@ConditionalOnProperty(name = "ssup.security.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig {
  • matchIfMissing = true: 값이 없으면 기본값을 true로 설정한다.

spring:
  config:
    activate:
      on-profile: test
ssup:
  security:
    enabled: false

이렇게 설정하면, 인증 테스트(auth-test profile)와 운영 환경에서는 true로 작동해서 Security Context가 동작하고, 기본 테스트(test profile)는 Security Context를 배제하고 테스트의 목적에 집중할 수 있게 된다.

또한, 인증 로직을 테스트할때 oauth2와 같은 설정을 모두 포함해서 테스트하는 경우는 드물다. 이때, 그러한 Bean들을 모두 Mock 처리하면 가독성이 떨어질 수 있다. 이때는 TestSecurityConfig와 같은 테스트용 Security Config를 따로 만들어서 사용하면 된다.



CI Step 분리

이렇게 하면, 인증 테스트와 인증을 제외한 domain/unit/slice 테스트들은 별도의 profile에서 수행하게 된다.

  • 인증 테스트: auth-test
  • 기본 테스트: test

따라서, CI 시에도 step을 2개로 나눠서 수행한다.

- name: Run tests (test profile)
	working-directory: backend
		env:
			SPRING_PROFILES_ACTIVE: test
        run: |
			chmod +x ./gradlew
			./gradlew test --tests "*" --tests "!*Auth*"

- name: Run tests (auth-test profile)
	working-directory: backend
		env:
			SPRING_PROFILES_ACTIVE: auth-test
        run: |
            chmod +x ./gradlew
            ./gradlew test --tests "*Auth*"

실패

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/com/ssup/backend/global/config/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 3: Error creating bean with name 'securityConfig': Requested bean is currently in creation: 
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)

성공

profile
백엔드 개발자입니다😄

0개의 댓글