Engineering Note

[Spring] 의존성 주입(DI)과 테스트의 관계 본문

Server/Spring

[Spring] 의존성 주입(DI)과 테스트의 관계

Software Engineer Kim 2025. 10. 1. 18:24

1. 의존성 주입이란?

의존성 주입(Dependency Injection, DI)은 객체가 사용할 다른 객체를 직접 생성하지 않고, 외부에서 주입받는 방식을 말합니다.
스프링에서는 컨테이너(ApplicationContext)가 객체를 Bean으로 관리하고, 필요한 곳에 자동으로 주입해주기 때문에 @Autowired나 생성자 주입을 통해 쉽게 DI를 구현할 수 있습니다.
 
 

2. 왜 필요한가? (순수 자바 관점)

순수 자바에서 보자면, 클래스가 의존 객체를 직접 생성하는 경우 테스트와 유지보수가 어렵습니다.
 
의존성을 직접 생성하는 경우

class A {
    private B b = new B(); // A가 B를 직접 생성

    public void doSomething() {
        b.work();
    }
}

 

  • A는 항상 B라는 구체 클래스에 묶여 있습니다.
  • B를 C로 교체하거나, 테스트에서 Mock 객체를 사용하고 싶어도 불가능합니다.

 
 
의존성을 주입받는 경우

class A {
    private final B b;

    public A(B b) {   // 생성자 주입
        this.b = b;
    }

    public void doSomething() {
        b.work();
    }
}

 
 

  • 이제 A는 “나에게 B가 필요하다”라는 의존성만 선언합니다.
  • 실제 어떤 객체를 넣을지는 외부에서 결정합니다.
B b = new B();
A a = new A(b);   // 외부에서 주입

 
 

3. 테스트 관점에서의 장점

테스트할 때 진짜 B 대신 MockB를 주입할 수 있습니다.

class MockB extends B {
    @Override
    public void work() {
        System.out.println("Mock 동작");
    }
}

@Test
void testA() {
    B mockB = new MockB(); // 진짜 대신 Mock
    A a = new A(mockB);

    a.doSomething(); // "Mock 동작" 검증 가능
}

 
이렇게 하면 A의 로직만 검증할 수 있고, B의 실제 동작에 의존하지 않아 단위 테스트가 깔끔해집니다.
유연한 설계 + 테스트 용이성 확보
 
 

3-1. 필드 주입의 문제점

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
}

 
 

  • 불변성 없음
    : private 필드인데도 리플렉션을 통해 주입되므로, 테스트나 리팩토링 과정에서 실수로 값이 바뀔 수 있음.
  • 테스트 어려움
    : 단위 테스트 시 new OrderService()로 직접 객체 생성하면 orderRepository가 null → 직접 주입 불가.
  • 숨겨진 의존성
    : 생성자를 보면 어떤 객체가 필요한지 알 수 없음. 코드를 열어봐야 함.
  • 순환 참조 감지 불가
    : 필드 주입은 순환 참조 발생 시 런타임에만 에러가 납니다.

 

3-2. 생성자 주입의 장점

@Service
public class OrderService {
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
}

 

  • 불변성 확보
    : final 키워드로 의존성을 변경 불가능하게 유지. Java 에서 final이 붙은 필드는 선언과 동시에 초기화를 하거나 생성자를 통해 반드시 초기화를 해야한다.
  • 테스트 용이
    : 스프링 컨테이너 없이도

 

new OrderService(new FakeOrderRepository());

 
 

  • 이런 식으로 테스트 가능.
  • 의존성 명시적 표현
    : 생성자에 파라미터로 다 드러나므로 “이 서비스가 무엇을 필요로 하는지” 명확히 알 수 있음.
  • 순환 참조 조기 감지
    : 스프링이 애플리케이션 실행 시점에 바로 순환 참조 에러를 알려줌.

 

3-3. 스프링 공식 권장

스프링 팀도 공식 문서, 강의, 코드 컨벤션에서 생성자 주입을 기본으로 삼고 있다.
 
 
 

4. 결론

  • DI는 단순히 스프링의 기능이 아니라 객체지향 설계 원칙(DIP)을 지키기 위한 방법입니다.
  • 생성자 주입을 사용하면 테스트에서 Mock 객체를 쉽게 넣을 수 있고, 코드 결합도를 낮출 수 있습니다.
  • 따라서 스프링 진영에서는 “생성자 주입 권장”이라는 가이드가 자연스럽게 따라옵니다.

 
 
 

Comments