Spring Security 테스트 환경에서 "인증된 사용자가 없습니다." 예외 해결하기

J_log·2025년 2월 4일
0

문제 발생

Spring Boot 어플리케이션에서 JUnit과 Mockito를 사용해 테스트하던 중 UserAuthorizationUtil.getLoginUser()를 호출하는 코드에서 다음과 같은 예외가 발생했다.

java.lang.IllegalArgumentException: 인증된 사용자가 없습니다.
at com.shineidle.tripf.common.util.UserAuthorizationUtil.getUserDetails(UserAuthorizationUtil.java:19)
at com.shineidle.tripf.common.util.UserAuthorizationUtil.getLoginUser(UserAuthorizationUtil.java:48)

원인 추론

1. SecurityContextHolder의 인증 정보 부족
UserAuthorizationUtil.getLoginUser() 내부에서는 SecurityContextHolder.getContext().getAuthentication() 을 호출해 현재 로그인한 사용자를 가져오는데, 테스트 환경에서는 SecurityContextHolder가 초기화되지 않이 null이 반환되었다.

2. Spring Security의 Authentication 객체 미설정
Spring Security는 HTTP 요청을 처리할 때 SecurityConext를 자동으로 설정하지만, 단위 테스트 환경에서는 이를 직접 설정해야 한다. 그렇지 않으면 authentication == null || !authentication.isAuthenticated() 조건을 만족하여 IllegalArgumentException("인증된 사용자가 없습니다.") 예외가 발생한다.

해결 방안

방법 1 : SecurityContextHolder에 가짜 인증 정보 설정

테스트 실행 전에 SecurityContextHolder에 가짜 인증 정보를 설정하여 UserAuthorizationUtil.getLoginUser()가 정상적으로 동작하도록 만든다

import org.junit.jupiter.api.BeforeEach;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import com.shineidle.tripf.common.security.UserDetailsImpl;

@BeforeEach
void setUp() {
    user = new User();
    user.setId(1L);
    user.setEmail("test@example.com");

    UserDetailsImpl userDetails = new UserDetailsImpl(user);
    UsernamePasswordAuthenticationToken auth =
        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    
    SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
    securityContext.setAuthentication(auth);
    SecurityContextHolder.setContext(securityContext);
}

이렇게 설정하면 SecurityContextHolder가 인증 정보를 갖게 되어, UserAuthorizationUtil.getLoginUser() 호출 시 정상적으로 User 객체를 반환한다.

방법 2 : Reflection을 사용해 UserAuthorizationUtil 내부 필드 강제 설정

UserAuthorizationUtilSecurityContextHolder에 의존하는 것이 아닌, 특정 객체를 참조하는 방식이라면 ReflectionTestUtils를 사용하여 내부 필드를 강제로 설정하는 방법도 있다.

import org.springframework.test.util.ReflectionTestUtils;

@BeforeEach
void setUp() {
    user = new User();
    user.setId(1L);
    
    UserDetailsImpl userDetails = new UserDetailsImpl(user);
    ReflectionTestUtils.setField(UserAuthorizationUtil.class, "authenticatedUser", userDetails);
}

이 방법은 SecurityContextHolder가 아닌 특정 필드를 참조하는 경우에만 유효하다.

결과 확인

위 방법을 적용한 후 다시 테스트를 실행한 결과 예외 발생 없이, 정상적으로 테스트 코드가 실행되었다.

  • Spring Security가 관리하는 SecurityContext는 단위 테스트 환경에서 자동으로 설정되지 않으므로, 명시적으로 Mock 데이터를 주입하는 것이 중요.
  • SecurityContextHolder를 활용하는 방식이 가장 일반적인 해결책이며, 특정 클래스 내부의 필드를 직접 수행해야 한다면 ReflectionTestUtils를 고려할 수 있다.

0개의 댓글

관련 채용 정보