FieldInjectionAndTestIssues

필드 주입(Field Injection)의 문제점 ⚠️

필드 주입은 Spring에서 의존성을 주입하는 가장 간단한 방법 중 하나입니다. 하지만 테스트 시 여러 문제를 일으킬 수 있습니다.

@Service
public class UserService {
    // 필드 주입 방식
    @Autowired
    private UserRepository userRepository;
    
    // 서비스 로직...
}

1. 테스트 시 발생하는 문제점 🧪

  1. 단위 테스트의 어려움 - Spring 컨테이너 없이 테스트할 수 없음

    // 필드 주입을 사용한 클래스는 다음과 같이 직접 인스턴스화할 때 의존성이 null이 됨
    @Test
    void testUserService() {
        UserService userService = new UserService(); // userRepository는 null
        // 테스트가 실패할 가능성 높음
    }
  2. 불변성 보장 어려움 - final 키워드를 사용할 수 없어 의존성이 변경될 수 있음

  3. 숨겨진 의존성 - 클래스를 보는 것만으로는 필요한 의존성을 파악하기 어려움

  4. 순환 참조 감지 어려움 - 생성자 주입과 달리 컴파일 타임에 순환 의존성 감지 불가능

테스트에서의 필드 주입 활용 방안 🔄

테스트 코드에서는 오히려 필드 주입이 유용할 수 있습니다.

1. 테스트 클래스에서의 필드 주입 사용 📋

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class UserServiceIntegrationTest {
    // 테스트 코드에서는 필드 주입이 간결하고 유용할 수 있음
    @Autowired
    private UserService userService;
    
    @MockBean
    private EmailService emailService;
    
    @Test
    void testCreateUser() {
        // 테스트 로직...
    }
}

2. 테스트 유틸리티 클래스에서의 활용 🛠️

public class TestHelper {
    // ReflectionTestUtils를 사용하여 private 필드에 모의 객체 주입
    public static void injectMocks(Object target, String fieldName, Object mockObject) {
        ReflectionTestUtils.setField(target, fieldName, mockObject);
    }
}

@Test
void testWithMockInjection() {
    UserService userService = new UserService();
    UserRepository mockRepository = mock(UserRepository.class);
    
    // 필드 주입을 테스트 목적으로 활용
    TestHelper.injectMocks(userService, "userRepository", mockRepository);
    
    // 테스트 로직...
}

권장되는 의존성 주입 방식 🌟

1. 생성자 주입 (Constructor Injection)

@Service
public class UserService {
    private final UserRepository userRepository;
    
    // 생성자 주입 방식
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // 서비스 로직...
}

장점:

  • 불변성 보장 (final 키워드 사용 가능)

  • 필수 의존성 명시적 표현

  • 순환 참조 컴파일 타임 감지

  • 테스트 용이성

2. 수정자 주입 (Setter Injection)

@Service
public class UserService {
    private UserRepository userRepository;
    
    // 수정자 주입 방식
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // 서비스 로직...
}

장점:

  • 선택적 의존성에 적합

  • 런타임에 의존성 변경 가능

  • 테스트 시 모의 객체 주입 용이

결론: 언제 어떤 주입 방식을 사용할 것인가? 🤔

  1. 프로덕션 코드:

    • 필수 의존성은 생성자 주입 사용 (권장)

    • 선택적 의존성은 수정자 주입 고려

    • 필드 주입은 지양 (특별한 이유가 없다면)

  2. 테스트 코드:

    • 테스트 클래스에서는 필드 주입의 간결함 활용 가능

    • @SpringBootTest와 같은 통합 테스트에서는 필드 주입이 편리

    • 단위 테스트에서는 생성자 주입이나 수정자 주입을 통해 의존성 관리

    • 테스트 유틸리티에서는 ReflectionTestUtils를 활용한 필드 주입 기법 활용 가능

필드 주입은 프로덕션 코드에서는 피하는 것이 좋지만, 테스트 코드에서는 간결함과 편의성을 위해 적절히 활용할 수 있습니다.

Last updated