API 테스트 하는데... 인증까지 매번..?

2025. 4. 22. 12:11·프로젝트 일기/한편의 수학 학원

🙈 1. 문제 파악

API 구성에는 인증과 인가가 항상 뒷따르게 됩니다.
이와 관련된 보안 로직은 필수적이지만, API 응답을 직접적으로 테스트하려면, 인증과 인가 로직을 테스트 시 불필요하게 포함하지 않고, 원하는 로직만 검증하는 것이 중요합니다.

기존에는 보안 관련 로직이 중복되면서, 입력 형식을 테스트하는 과정에서도 인증 정보가 반드시 필요하게 되어 다음과 같은 문제가 발생했습니다.

입력 형식을 위한 테스트에 불필요한 인증 로직을 포함해야 하는 문제
예: JsonBody가 매핑되는지 확인하기 위해 인증을 포함해야 했음.
인가 테스트를 위해 현재 사용하고 있는 로그인 구현체에 의존해야 하는 문제
예: Authorization 헤더를 통해 직접 토큰을 생성하는 방식에 의존하게 됨.


💡 2. 해결 방안

    1. 테스트 각각에 @Filter를 통해 인증 및 인가 기능을 제공하는 빈들을 등록하지 않는다.
    1. 인가정보 테스트를 위한 어노테이션을 제공한다.

✍️ 3. 해결 과정

-1 테스트 각각에 @Filter를 통해 인증 및 인가 기능을 제공하는 빈들을 등록하지 않는다.

테스트 시, 해당 구성 클래스를 빈으로 등록하지 않도록 한다.

먼저 인증, 인가에 사용되는 빈을 등록하는 WebAuthorizationConfiguration 구성클래스를 정의한다.

@Configuration
public class WebAuthorizationConfiguration {
    @Bean
    public JwtUtils jwtUtils() {
        return new JwtUtils();
    }

    @Bean
    public OncePerRequestFilter jwtAuthenticationFilter(final JwtUtils jwtUtils) {
        return new JwtAuthenticationFilter(jwtUtils);
    }

    @Bean
    public WebMvcConfigurer authenticationWebMvcConfigurer() {
        return new WebAuthorizationConfig();
    }

    private static class WebAuthorizationConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new AuthorizationInterceptor())
                    .addPathPatterns("/api/**");
        }

        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new LoginIdMethodArgumentResolver());
        }
    }
}

테스트 시, 다음과 같은 필터를 등록함으로써 위 구성클래스를 빈으로 등록하지 않을 수 있었다.
이를 통해 테스트 시, 인증인가를 피할 수 있었다.

@WebMvcTest(
        controllers = {AccountController.class},
        excludeFilters = {
                @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebAuthorizationConfiguration.class)
        }
)

빈 등록을 @Component로 하는것이 아닌, @Configuration을 통해 한 클래스로 모음으로써, 테스트에서 더 간편히 해당 클래스들을 제외할 수 있었다.

-2.인가정보 테스트를 위한 어노테이션을 제공한다.

인가정보 테스트를 위해 아래와 같은 커스텀 필터를 정의한다.

class WebAuthorizationTestConfiguration {

    @Bean
    OncePerRequestFilter testAuthorizationFilter() {
        return new TestAuthorizationFilter();
    }

    @Bean
    WebMvcConfigurer webMvcConfigurer() {
        return new WebAuthorizationConfig();
    }

    private static class WebAuthorizationConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new AuthorizationInterceptor())
                    .addPathPatterns("/api/**");
        }
    }

    private static class TestAuthorizationFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            final Role role = (Role) request.getAttribute("role");
            logger.debug("Testing role: " + role);
            if (role != null) {
                filterChain.doFilter(new JwtRequestWrapper(request, new MemberPrincipal(-1L, role)), response);
            }
        }
    }
}

해당 클래스를 테스트 시에 적용하도록 하여, 요청에 attribute를 포함함으로써 인증을 테스트할 수 있다.

하지만, 조금 긴 느낌이라, 다음과 같이 어노테이션을 통하도록 구성했다
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(WebAuthorizationTestConfiguration.class) public @interface TestAuthorization { }

다음은 사용 예시이다.

@TestAuthorization
@WebMvcTest(
        controllers = {AccountController.class}
)
public class AccountControllerTest {

    @ParameterizedTest
    @MethodSource("provideIllegalArguments")
    void 잘못된_요청_테스트(Map<String, Object> requestDto) throws Exception {
        postRequestWithAdminRole(status().isBadRequest(), requestDto);
    }

    private void postRequestWithAdminRole(ResultMatcher resultMatcher, Map<String, Object> requestDto)
            throws Exception {
        mockMvc.perform(post(BASE_URL)
                .content(objectMapper.writeValueAsString(requestDto))
                .contentType(MediaType.APPLICATION_JSON_VALUE)

                // 핵심 포인트 
                .requestAttr("role", Role.ADMIN)


        ).andExpect(resultMatcher);
    }

    public static Stream<Arguments> provideLegalArguments() {
        return Stream.of(
                Arguments.of(createRequest("희종", 0, "0102010021", "student", null)),
                Arguments.of(createRequest("TOM", 11, "0102010021", "student", null)),
                Arguments.of(createRequest("COKE", 10, "01022", "manager", null))
        );
    }
}

⭐️ 4. 깨달은 점

  1. 보안 로직을 우회함으로써, 테스트 목적에만 집중할 수 있는 환경을 만들 수 있었습니다. 실제 로그인 구현체나 인증 절차에 의존하지 않고도 역할 기반 인증 시뮬레이션을 통해 인증/인가 로직을 독립적으로 테스트함으로써, 인증 구현체에 직접적으로 의존하지 않을 수 있었습니다.
  2. 이번 작업을 통해 스프링의 보안 및 테스트 설계에 대한 이해도가 향상되었습니다. 특히, 보안 로직을 테스트 환경에서 우회하고 제어하는 방법을 적용하면서 스프링의 구성 요소들이 어떻게 동작하고 호출되는지, 스프링의 테스트 환경을 어떻게 구성할 수 있을지에 대한 더 깊은 이해를 얻게 되었습니다.

'프로젝트 일기 > 한편의 수학 학원' 카테고리의 다른 글

질문 도메인 최적화 과정  (1) 2025.06.05
데코레이터 패턴을 통한 비동기 처리의 안정적 도입  (0) 2025.04.14
이미지 업로드 API에서의 트랜잭션 병목과 비동기 처리 전략  (0) 2025.04.13
[Refactor] Bean Validation Duplicated  (0) 2025.04.09
벌크연산을 통한 쿼리 최적화  (0) 2025.04.04
'프로젝트 일기/한편의 수학 학원' 카테고리의 다른 글
  • 질문 도메인 최적화 과정
  • 데코레이터 패턴을 통한 비동기 처리의 안정적 도입
  • 이미지 업로드 API에서의 트랜잭션 병목과 비동기 처리 전략
  • [Refactor] Bean Validation Duplicated
윤희종
윤희종
호기심을 잃지 말자 지적, 질문은 언제나 환영합니다 ;)
  • 윤희종
    서버견문록
    윤희종
  • 전체
    오늘
    어제
    • 분류 전체보기 (36)
      • 데일리 플랜 (1)
      • 이것저것 (4)
      • Java (6)
      • Spring (12)
        • SpringBoot (10)
        • Spring MVC (0)
      • Computer Science (4)
        • Network (1)
        • Operating System (0)
        • Data Structure (0)
        • Algorithm (2)
        • Database (0)
      • IOS (0)
      • 프로그래머스 문제풀이 (2)
      • 프로젝트 일기 (7)
        • 한편의 수학 학원 (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    알고리즘
    read through
    cache write back
    스프링 부트
    캐시
    mysql 쿼리 최적화
    인증 테스트
    SecurityFilterChain 구성
    SecurityFilterChain
    스프링 부트 인증 우회
    servlet
    제네릭
    스프링 시큐리티 구성
    스프링 시큐리티 사용법
    인증 우회 테스트
    비동기 처리 유의점
    Spring
    스프링
    성능 개선
    springboot
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
윤희종
API 테스트 하는데... 인증까지 매번..?
상단으로

티스토리툴바