ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SPRING] DI(의존성 주입)가 무엇이고, IoC 컨테이너는 어떻게 사용할까?
    Spring Boot 2022. 12. 12. 17:58

    DI (의존성 주입) 의 이해

    일반적인 구조의 한계

    그동안 프로젝트 구조를 살펴보면, Controller 클래스에서 new Service를 만들고, Service 클래스에서 new Repository를 만들어 각각의 객체에서 함수를 만들어 불러오는 식으로 작업의 역할분담을 했다.

    만약에 여기서 new Repository에 username, password를 파라미터로 전달받아 DB에 접속 시 사용해야 하는 기획으로 변경되었다면 어떻게 해야 변경이 되어야할까?

    // Controller에서 username과 password 전달
    new Service("이재원", "123456")
    
    // Service에서 전달받은 값을 또다시 Repository에 전달
    (username, password)-> new Repository(username, password)

    위와 같이 계속 내리전달 하는 식으로 불편함을 겪게 될 것이다. 전달할 Controller가 하나가 아니라 수백개가 있다면? 훨씬 많은 리소스를 낭비하게 될 것이다.

    이것을 보완하기 위해 '제어의 역전(IoC: Inversion of Control)'이 탄생했다.

     

    제어의 역전(IoC: Inversion of Control) : IoC 컨테이너의 탄생

    앞서 진행한 일반적인 구조는 '강한 결합'이라고 부르며 이것을 다른 방법을 사용해 '느슨한 결합'으로 바꾸어줄 수 있다.

    기획의 변경으로 전달해야할 username과 password를 애초에 Repository 클래스에서 해결해버리면 Controller->Service를 통해 전달 받을 필요가 없지 않을까?

     

    Bean 객체 생성을 이용해보자

    Repository에 추가될 username과 password를 미리 설정해주는 @Bean을 사용해 볼 것이다.

     

    1. config 폴더에 BeanConfiguration 클래스를 만들고 리턴해줄 Repository에 파라미터로 전달할 값을 넣어준다. 빈객체로 변경해야 하므로 @Configuration과 @Bean 어노테이션을 아래와 같이 추가해준다.

    @Configuration
    public class BeanConfiguration {
        @Bean
        public ProductRepository productRepository(){
            String dbUrl = "jdbc:h2:mem:db";
            String username = "sa";
            String password = "";
            return new ProductRepository(dbUrl, username, password);
        }
    }

     

    2. Repository 클래스에서 생성자함수를 추가해 파라미터로 받아온 것들(dbUrl, username, password)을 필드에 선언해준다.

    private final String dbUrl;
    private final String username;
    private final String password;
    
    public ProductRepository(String dbUrl, String username, String password) {
        this.dbUrl = dbUrl;
        this.username = username;
        this.password = password;
    }

     

    3. DB를 연결할 때 필드값을 사용하므로, DB연결 코드 부분을 바꾸어준다.

    Connection connection = DriverManager.getConnection("jdbc:h2:mem:db", "sa", "");
    -> Connection connection = DriverManager.getConnection(dbUrl, username, password);
    // 필드에 있는 dbUrl, username, password으로 교체해주었다.

     

     

    4. 이제 Repository는 Controller, Service 클래스에서 username, password를 넣어주지 않아도 이미 포함된 Repository를 불러와 사용할 수 있게 되었다.

     

    5. ProductService 클래스에서 new Repository 로 객체를 만들었던 것은 이제 필요가 없다. @Bean과 @Component를 사용해 만든 클래스는 자동으로 IoC컨테이너에 new 객체로 만들어져서 보관되어있기 때문이다. IoC 컨테이너에 보관된 Repository 빈 객체를 가져올 생성자함수에 @Autowired를 추가해주고 파라미터에 Repository를 넣어서 가져와 선언된 필드에 넣어준다. 

    //(수정전)
    public ProductService(){
        this.productRepository = new ProductRepository();
    }
    
    //(수정후)
    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

     

    6.@RequireArgsConstructor를 사용하면 IoC컨테이너의 빈객체를 자동으로 가져와주기 때문에 @Autowired와 생성자함수를 생략시킬 수 있다.

    @RequiredArgsConstructor
    public class ProductController {
    	private final ProductService productService;
        
        //아래는 생략이 가능해졌다.
        //@Autowired
        //public ProductController(ProductService productService) {
        //    this.productService = productService;
        //}
    }

     

Designed by Tistory.