Spring/Spring Boot

[Spring] 09. DI(의존성 주입) 과 IOC 컨테이너

민돌v 2021. 11. 12. 23:54

이번에는 DI - 의존성 주입이라는 스프링의 주요한 기능에대해서 알아보자

후... 너무누무 어려워

 

DI - Dependence Injection


문자그래도, 의존성의 주입, 독립성의 주입이다.

스프링은 자바언어로 이루어진 프레임워크로써, 객체지향을 지향한다.

 

따라서 각 객체간의 종속성을 제거하기위해 DI를 사용한다,

 

그럼 DI란 뭘까?

 

내가 이해한 바로, 스프링은 메모리를 Bean 이라는 객체로 관리한다.

각 객체가 각각에 맞는 Bean객체(많은 종류의 빈객체가 존재함)임을 명시해주면(어노테이션) Spring은, 그 객체(Class)를 명시해준 Bean 객체(어떤 역할을 하는 객체인지) 파악해, Bean 객체로서 관리한다.

 

이러한 Bean 객체는 한 네이밍당 하나의 공간을 차지하고 있다.

그렇다면, 이렇게 각각의 클래스를 빈이라는 종류의 객체로 만들어 메모리에 저장하고있으니까, 

객체가 필요할 때 마다, New 객체를 하지않고, 만들어진 빈객체를 이용한다면 메모리도 줄고, 결합도도 줄어들지 않을까?? (한번만 호출하면 되니, 코드가 짧아지고 여기저기 섞이지 않는다)

 

이것이 이제 DI 라고 한다. (아마도??)

 

그리고 이 Bean객체를 보관하고 관리하는 공간을 IOC 컨테이너라고 한다!

그리고 이렇게 DI를 주입하는 제어흐름을 제어의 역전!! (IoC - Inversion of Control) 이라고 한다!

 

 


 

 

의존관계 다이어그램


 

 

 

 

 

 

 

 

 

 

 

스프링의 아키텍쳐 구조를 이용하여 프로그램을 작성할 때, 필요한 기능에 1대1 매핑으로 new 객체를 생성해 위의 그림과 같이 생성을 해주었다.

 

이렇게 되면, 단점이 

메모리 낭비

소스코드의 중복성

컨트롤을 사용하기위해, 컨트롤의 모든 구조를 알아야한다는점, 컨트롤 레포지토리까지,

이래서 결합도가 매우 높다는점이다

 

 

DI 다이어그램

하지만 이렇게 미리 만들어준 Bean 객체를 필요할 때마다 불러와서 사용한다면, 이렇게 제어의 역전을 일으킬 수 잇다.

 

이해가 잘 안되겠지만!! 이해해보자(?)

 

Cotroller 부분을 예로 들어보면

 

DI 주입 전

요롷게 필요한 메소드마다, ProductService 객체를 생성 하고 있다.

하지만, 프로덕트서비스를 만들 때 Bean 객체 (예를들어 Restcontroller 처럼) 로 만들 었다면, 어차피 Ioc 컨테이너에서 인스턴스로서 메모리 공간을 차지하고 있기때문에, 미리 만들어진 빈 객체에 연결하여 불러오기만 하면 된다.

@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {
    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() throws SQLException {
        ProductService productService = new ProductService();
        List<Product> products = productService.getProducts();
        // 응답 보내기
        return products;
    }

    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        ProductService productService = new ProductService();
        Product product = productService.createProduct(requestDto);
        // 응답 보내기
        return product;
    }

    // 설정 가격 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
        ProductService productService = new ProductService();
        Product product = productService.updateProduct(id, requestDto);
        return product.getId();
    }
}

 

 

DI 주입 후

컨트롤러 객체를 생성할 때, 생성자로 서비스 하나만 불러와 사용한다.

외부에서 정의된 프로덕트 서비스를 내부에서 사용할 프로덕트 서비스로 명시해주고 있다.

@AutoWired : 외부에서 불러온 ProducService에 맞는 빈객체를 연결해 주는 것이다.

@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {
    // 멤버 변수 선언
    private final ProductService productService;

    // 생성자: ProductController() 가 생성될 때 호출됨
	@Autowired
    public ProductController(ProductService productService) {
        // 멤버 변수 생성
        this.productService = productService;
    }

    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() throws SQLException {
        List<Product> products = productService.getProducts();
        // 응답 보내기
        return products;
    }

    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        Product product = productService.createProduct(requestDto);
        // 응답 보내기
        return product;
    }

    // 설정 가격 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
        Product product = productService.updateProduct(id, requestDto);
        return product.getId();
    }
}

 

 

조금 더 짧은 코드를 위해서는 @RequiredArgsConstructor를 사용한다.

@RequiredArgsConstructor 어노테이션은 @NonNull이나 final이 붙은 필드에 대한 생성자를 생성한다.

@RestController // JSON으로 데이터를 주고받음을 선언합니다.
@RequiredArgsConstructor //final 변수의 생성자 생성
public class ProductController {
    // 멤버 변수 선언
    private final ProductService productService;

    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() throws SQLException {
        List<Product> products = productService.getProducts();
        // 응답 보내기
        return products;
    }

    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        Product product = productService.createProduct(requestDto);
        // 응답 보내기
        return product;
    }

    // 설정 가격 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
        Product product = productService.updateProduct(id, requestDto);
        return product.getId();
    }
}

이렇게 스프링 DI 개념 정리 끝!