Post

의존성 주입(DI)의 3가지 유형

DI의 주요 유형

  • 생성자 주입(Constructor Injection) : 의존성을 생성자를 통해 주입받는 방식
    1. 일반 생성자를 이용한 주입
1
2
3
4
5
6
7
8
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    ...
}


2. Lombok의 @AllArgsConstructor 사용

1
2
3
4
5
@AllArgsConstructor
public class UserService {
    private final UserRepository userRepository;
    ...
}
  • 세터 주입(Setter Injection) : 의존성을 세터 메서드를 통해 주입받는 방식
1
2
3
4
5
6
7
8
public class UserService {
    private UserRepository userRepository;

    // 세터를 통한 주입
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 필드 주입(Field Injection) : 의존성을 필드에 직접 주입하는 방식
1
2
3
4
5
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

결론

테스트 환경

  1. 생성자 주입(Constructor Injection)

    장점

    • 테스트 용이성: 객체를 생성할 때 모든 필수 의존성을 명시적으로 전달받기 때문에, 테스트 시 의존성을 쉽게 모의 객체(Mock)로 교체할 수 있습니다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
       public class UserServiceTest {
        @Test
        public void testUserService() {
            // Mock 객체 생성
            UserRepository mockRepository = Mockito.mock(UserRepository.class);
                  
            // 생성자 주입을 통해 의존성 주입
            UserService userService = new UserService(mockRepository);
            ...
        }
       }
      
    • 불변성: 의존성이 생성자를 통해 한 번 설정되면 변경되지 않기 때문에 객체의 상태를 안정적으로 유지할 수 있습니다.

    단점

    • 코드 길이: 필드가 많은 클래스의 경우 생성자 파라미터가 길어질 수 있어 코드가 복잡해질 수 있습니다.

  2. 세터 주입(Setter Injection)

    장점

    • 유연성: 런타임 시점에 의존성을 동적으로 변경할 수 있어 특정 테스트 케이스에 필요한 의존성만 주입할 수 있습니다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
       public class UserServiceTest {
        @Test
        public void testUserService() {
            // Mock 객체 생성
            UserRepository mockRepository = Mockito.mock(UserRepository.class);
                  
            // Setter 주입을 통해 의존성 주입
            UserService userService = new UserService();
            userService.setUserRepository(mockRepository);
            ...
        }
       }
      
    • 선택적 의존성: 필요에 따라 의존성이 없는 상태로 객체를 생성할 수 있어 테스트가 용이합니다.

    단점

    • 의존성 누락: 객체 생성 후에 의존성을 설정하지 않을 경우 NullPointerException과 같은 예외가 발생할 수 있습니다.

    • 객체의 상태 변경: 의존성이 외부에서 변경될 수 있어 객체의 상태를 예측하기 어려울 수 있습니다.

  3. 필드 주입(Field Injection)

    장점

    • 간결한 코드: 필드 주입은 코드가 간결하고 직관적입니다. 별도의 생성자나 세터 메서드를 작성할 필요가 없어 테스트 코드 작성이 편리합니다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
       public class UserServiceTest {
        @Mock
        private UserRepository mockRepository;
              
        @InjectMocks
        private UserService userService;
              
        @Before
        public void setUp() {
            MockitoAnnotations.initMocks(this);
        }
              
        @Test
        public void testUserService() {
            ...
        }
       }
      

    단점

    • 테스트 어려움: 필드 주입을 사용할 경우, 테스트에서 의존성을 목 객체로 교체하기 어려울 수 있습니다. 이로 인해 테스트가 실제 데이터베이스나 외부 서비스에 의존해야 할 때 문제가 발생할 수 있습니다.

어떤 DI 유형을 선택해야 할까?

  • 생성자 주입: 필수적인 의존성이 있는 경우나 불변성을 유지해야 할 때 좋은 선택입니다.

  • 세터 주입: 선택적인 의존성이 필요한 경우나, 기본 생성자를 사용할 때 유용합니다.

  • 필드 주입: 간단한 프로토타입 코드나 테스트용 코드에서 사용할 수 있지만, 일반적인 애플리케이션 코드에서는 지양하는 것이 좋습니다.

This post is licensed under CC BY 4.0 by the author.