본문 바로가기
카테고리 없음

Spring에서 `@Autowired`를 사용하면 안되는 이유

by terrid 2025. 3. 28.
반응형

Spring Framework는 의존성 주입(Dependency Injection, DI)이라는 개념을 사용하여 객체 간의 결합도를 낮추고, 코드의 유연성과 테스트 용이성을 높이는 것을 목표로 합니다. @Autowired 어노테이션은 Spring에서 의존성 주입을 간편하게 처리하기 위해 사용되는 대표적인 방법입니다. 하지만, @Autowired를 사용하는 것이 항상 최선의 선택이 아닐 수 있습니다. 이 블로그 글에서는 @Autowired를 사용하지 말아야 하는 이유에 대해 설명하겠습니다.

1. 명시적인 의존성 주입이 불가능

@Autowired를 사용하면 의존성을 자동으로 주입하게 되지만, 이는 의존성이 명확하지 않다는 단점이 있습니다. 예를 들어, 클래스 A가 클래스 B에 의존한다고 가정할 때, @Autowired를 사용하면 A 클래스의 생성자나 메서드에서 의존성 주입이 자동으로 처리되므로 클래스 B가 A의 의존성임을 명확하게 알기 어려워집니다.

해결 방법: 생성자 주입

생성자 주입을 사용하면 의존성 주입이 명확해지고, 객체 생성 시 의존성을 바로 알 수 있습니다. 이는 코드의 가독성과 유지보수성을 높여줍니다.

public class A {
    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}

이렇게 생성자 주입을 사용하면 의존성 관계가 명확하게 드러나고, 코드가 더 안전해집니다.

2. 테스트 용이성 저하

@Autowired를 사용하면 의존성이 자동으로 주입되므로, 해당 클래스의 테스트가 어려워질 수 있습니다. 테스트를 작성할 때 @Autowired가 의존성 주입을 자동으로 처리하기 때문에, 필요한 mock 객체나 더미 객체를 직접 주입하는 것이 어려워질 수 있습니다.

해결 방법: 명시적인 의존성 주입

명시적으로 의존성을 주입하는 방식(특히 생성자 주입)을 사용하면, 테스트 시 mock 객체를 주입하거나 의존성을 쉽게 제어할 수 있습니다.

@RunWith(MockitoJUnitRunner.class)
public class ATest {

    @InjectMocks
    private A a;

    @Mock
    private B b;

    @Test
    public void testMethod() {
        // test logic
    }
}

이 방식으로 의존성을 쉽게 모킹(mocking)할 수 있어 테스트 작성이 용이해집니다.

3. 순환 의존성 문제

@Autowired를 사용할 때 순환 의존성 문제가 발생할 수 있습니다. 예를 들어, 클래스 A가 클래스 B를 의존하고, 클래스 B가 다시 클래스 A를 의존하는 상황에서, Spring은 순환 의존성을 해결하지 못할 수 있습니다. 이 경우, @Autowired가 자동으로 의존성을 주입하려 할 때 오류가 발생하게 됩니다.

해결 방법: 생성자 주입 및 순환 의존성 관리

순환 의존성은 생성자 주입을 통해 쉽게 해결할 수 있습니다. 순환 의존성이 발생하지 않도록 구조를 변경하거나, 경우에 따라 @Lazy를 사용하여 지연 로딩 방식으로 해결할 수 있습니다.

4. null 안전성 문제

@Autowired를 사용할 때, 의존성이 주입되지 않는 경우가 있을 수 있습니다. @Autowired는 기본적으로 의존성을 찾지 못하면 NullPointerException을 발생시킬 수 있습니다. @Autowired(required = true) 속성을 사용하면 필수 의존성으로 처리할 수 있지만, 여전히 명시적이지 않기 때문에 문제가 발생할 수 있습니다.

해결 방법: 생성자 주입 및 @NonNull 사용

생성자 주입을 사용하면 객체가 생성될 때 의존성이 반드시 주입되므로 NullPointerException이 발생하지 않습니다. 또한, @NonNull과 같은 어노테이션을 활용하여 null 안전성을 보장할 수 있습니다.

public class A {
    private final B b;

    @Autowired
    public A(@NonNull B b) {
        this.b = b;
    }
}

5. 유연성 부족

@Autowired는 자동 주입을 통해 의존성을 관리하지만, 의존성의 주입 방식을 변경하거나 제어하는 데 제한이 있을 수 있습니다. 특히, 여러 구현체가 있을 경우 어떤 구현체를 주입할지 명시적으로 선택할 수 없게 됩니다.

해결 방법: @Qualifier와 인터페이스 사용

여러 구현체가 있을 때는 @Qualifier를 사용하여 명시적으로 의존성을 주입할 수 있습니다. 그러나 @Qualifier를 사용하는 것 자체가 조금 더 복잡성을 더할 수 있습니다.

@Autowired
@Qualifier("myImplementation")
private MyInterface myInterface;

하지만, 이런 방식이 적절하게 관리되지 않으면 코드가 불필요하게 복잡해질 수 있습니다.

6. 스프링 버전 간 호환성 문제

@Autowired는 Spring Framework의 여러 버전에서 사용될 수 있지만, 특정 버전에서는 의존성 주입 처리 방식이 다를 수 있습니다. 이로 인해 특정 버전에서만 작동하는 코드가 생길 수 있습니다. 예를 들어, Spring 5 이후에는 생성자 주입을 권장하고 있지만, 이전 버전에서는 필드 주입을 사용한 코드가 많았기 때문에 버전 간 호환성 문제를 야기할 수 있습니다.

해결 방법: 표준화된 의존성 주입 방식 사용

Spring의 권장 방안인 생성자 주입을 사용하여 버전 간 호환성 문제를 최소화할 수 있습니다. 생성자 주입은 Spring의 모든 버전에서 일관된 방식으로 동작하므로, 버전 업그레이드 시 발생할 수 있는 문제를 방지할 수 있습니다.


결론

@Autowired는 Spring에서 매우 유용하게 사용될 수 있는 어노테이션이지만, 무조건 사용하는 것이 최선의 선택은 아닙니다. 생성자 주입을 사용하면 의존성 주입이 명확하고, 테스트 작성이 쉬워지며, 순환 의존성 문제를 예방할 수 있습니다. 따라서, Spring에서는 가능하면 @Autowired 대신 생성자 주입을 사용하고, @Autowired의 사용을 최소화하는 것이 더 좋은 설계로 이어질 수 있습니다.

728x90
반응형