DI와 IoC 개념 정리 #1

안녕하세요, 괴짜 개발자 namedboy 입니다. 😎

오늘은 DI(Dependency Injection) 와 IoC(Inversion of Control) 에 대한 내용을 정리해볼까 합니다.

DI와 IoC는 스프링에서 중요하게 쓰이는 개념이고 생각보다 많은 부분을 다루고 있습니다.
그래서 이번 글에서는 DI만 다루어 보려고 합니다.

그럼 시작해 볼까요?

프로그램을 개발하다 보면 Dependency 라는 것이 생깁니다. 바로 의존성이죠.

이 의존성을 해결하는 방법에는 여러가지가 있는데 그 중 하나가 바로 코드에 직접적으로 의존하는 객체를 명시하고 해당 객체를 사용하는 것이죠.

그 외의 방법으로는 Factory 패턴이나 JNDI 등을 사용해 의존하는 클래스를 검색하는 방법, 외부의 조립기를 사용하는 방법이 있습니다.

스프링에서 사용하는 DI는 패턴이며 외부의 조립기를 사용하는 방식입니다.

다음은 코드에 직접적으로 명시하는 방법입니다.

Taste 라는 클래스가 있다고 생각해봅시다.
코드를 보면 fruityGradelightGrade에 기본값이 있고 값을 설정 하거나 불러올 수 있게 되어 있습니다.

class Taste {
    private int fruityGrade = 1;
    private int lightGrade = 1;

    public int getFruityGrade() {
        return fruityGrade;
    }

    public int getLightGrade() {
        return lightGrade;
    }

    public void setFruityGrade(int fruityGrade) {
        this.fruityGrade = fruityGrade;
    }

    public void setLightGrade(int lightGrade) {
        this.lightGrade = lightGrade;
    }
}

여기 Product라는 클래스도 있습니다.
Product에서는 객체 내부에 제품의 이름과 가격 그리고 Taste 클래스를 내부에서 선언해 두었습니다. Taste 클래스와 마찬가지로 gettersetter 메소드 들이 있지만 여기선 중요하지 않아 생략해두었습니다.

public class Product {

    private String name = "";
    private int price = 0;
    private Taste taste;

    /**
    getter & setter 생략
    **/

    public void setFruityTasteGrade(int fruityGrade, int lightGrade) {

        // Taste 클래스를 사용할 수 있는 객체로 생성합니다.
        taste = new Taste();
        taste.setFruityGrade(fruityGrade);
        taste.setLightGrade(lightGrade);
    }
}

Product라는 클래스에서는 Taste를 맛으로 표현하기 위해 내부에서 선언해서 사용했는데 이 부분이 바로 코드에 직접적으로 명시하는 방법입니다.

직접적인 명시를 하는 방법은 가장 큰 불편함이 있습니다. Product클래스에서 주석이 표시되어 있는 taste = new Taste() 부분입니다.
Taste 클래스를 객체로 사용하려면 new라는 생성자 명령어를 통해 객체화 시켜주어야 하기 때문에 바로 다음줄에 taste.setFruityGrade(fruityGrade)를 사용하기 위해서는 매번 taste = new Taste(); 를 사용해 주어야 하죠. taste라는 변수도 선언이 되어 있지 않다면 Taste 클래스 타입으로 변수도 선언을 해주어야 합니다.

매번 하면 되지 무엇이 문제냐라는 생각이 드실 수 있을꺼라 생각합니다. 지금은 어쨌든 Product라는 클래스에서만 쓰고 있으니 매번 생성하면 될 것 같습니다.

하지만 Product가 아니라 WineType이나 YearOfWine같은 클래스가 만들어지고 이 3개의 클래스에서 전부 Taste를 쓰는 상황이 만들어지고, 3개의 클래스에 전부 같은 Taste 값이 입력되어야 한다면 어떤가요?

3개의 클래스에 모두 Taste를 넣어주고 모두 같은 값을 세팅하도록 호출을 해야 할 것입니다. 심지어 매번 호출해야 하죠. 같은 값이 제대로 입력되었는지 검증하는 단계도 필요하게 될 것입니다.

수 많은 작업을 추가로 해야 하는 상황이 발생하죠.

이러한 문제점들 때문에 Spring 에서는 DI 패턴을 통해 외부의 조립기로 객체를 사용하는 구조를 가지고 있습니다.

그것도 아주 쉽게 어노테이션으로 추가를 할 수 있도록 되어 있죠.

Spring에서는 ContainerBean이라는 개념을 사용해서 DI를 구현해두고 있습니다.

Container라는 큰 바구니에 클래스들을 객체화 하여 Bean이라는 이름으로 담아두게 됩니다.
Container는 Spring 내에서는 @Autowire 라는 어노테이션을 통해 접근을 할 수 있도록 하여 사용할 수 있게 해두는 거죠.

아까의 상황에서 3개의 클래스 Product, WineType, YearOfWine 에서 모두 Taste를 사용한다고 해도 데이터의 정확성이나 새롭게 객체를 생성해야 하는 문제를 ContainerBean이라는 개념으로 객체들을 관리하게 하면서 해결한 겁니다.

Spring에서는 이러한 방법으로 비효율적으로 객체를 선언하는 방식에서 벗어나게 되었습니다.

다음글에서는 IoC를 한번 다뤄보겠습니다.

참고사항으로 이 내용들은 제가 학습하기 위해 작성한 내용으로 틀린 내용이 있을 수 있습니다. :)

그럼 저는 이만 뿅!