Dependency Injection(의존성 주입) 간단한 정리
Dependency Injection(의존성 주입)이란?
아주 극단적인 설명을 드리자면, 스스로의 의존성을 능동적으로 설정하지 않고, 외부의 도움을 받아서 의존성을 설정한다는 말입니다.
일정 이상의 프로그램을 개발할때는 어떤 형태로든 객체들이 의존성을 맺어, 객체들간에 협력하면서 프로그램이 동작하게 마련인데, 이 의존성을 스스로 설정하지 않고 내가 의존해야(=사용해야) 하는 객체를 외부에서 할당(=주입)해주는 것을 의존성 주입이라고 합니다.
간단한 예를 들기 위해서, 어딘가에서 데이터를 읽어와서 화면에 출력하는 프로그램을 만든다고 해봅시다. 이 클래스는 데이터를 읽어오는 Reader 클래스를 필드로 갖고, 이 Reader 클래스의 인스턴스를 통해 데이터를 화면에 출력합니다.
class ContentWriter {
FileReader reader;
ContentWriter() {
this.reader = new FileReader("hello.txt");
}
public void print() {
String content = reader.readAll();
System.out.println(content);
}
}
이 ContentWriter 클래스는 파일을 읽어오기 위해서 FileReader 클래스에 의존하고 있습니다.
프로그램을 아래와 같이 고쳐보죠.
class ContentWriter {
FileReader reader;
ContentWriter(FileReader reader) {
this.reader = reader;
}
public void print() {
String content = reader.readAll();
System.out.println(content);
}
}
이제 이 클래스는 최초에 인스턴스 생성할때 생성자에 어떤 FileReader 객체를 넘겨받느냐에 따라서 다른 어떤 파일에도 대응할 수 있게 되었습니다.
이렇게 자신이 의존해야 하는 객체를 스스로 생성해서 의존성을 형성하지 않고, 의존해야 할 객체를 외부에서 넘겨받는 것을 의존성 주입이라고 합니다.
개발자가 직접
ContentWriter writer = new ContentWriter(new FileReader("hello.txt"));
처럼 직접 넘겨줄수도 있겠지만, 스프링을 이용해서 빈을 설정하고 reader 인스턴스에 @Autowired 어노테이션을 붙이면 의존해야 할 객체를 스프링이 넘겨주게 되는 것이죠
추가설명
Q. FileReader를 가져오기 때문에 이를 외부에서 객체를 주입받는다는 의미인가요??
근데 궁금한게 생성자 안에서 인스턴스화를 하는 거랑, 생성자의 인자로 받는 거랑 어떤 차이가 있나요?? 어차피 FileReader가 아닌 다른 값을 받아오려면 수정 해야하는 것은 똑같지 않나요??
A. 생성자든 스프링이든, 어떤 형태로든 외부에서 주입해준 객체를 사용한다면 모두 의존성 주입이라고 할 수 있습니다.
이게 어떤 의미가 있느냐고 물어보셨는데... 굉장히 추상적인 답변이 될 수밖에 없겠는데, 특정 클래스의 기능을 고치기 위해서 기능을 수행하는 클래스 그 차제를 수정하지 않고, 의존성을 주입해주는 부분을 고쳐서 기능을 고칠 수 있다는 특징 때문에 DI를 이용합니다.
스프링 없이 자바로 프로그래밍을 해 보시면 느끼시겠지만, 어떤 단위 기능을 처리하기 위한 기능 단위의 프로그램(=클래스)을 이것저것 만들어 놓고, 이것들의 인스턴스를 생성해서 조합해서 큰 작업을 처리하는 이른바 메인 클래스 같은게 존재하게 마련인데요. DI를 이용하면 기능 클래스의 수정을 최소화하고 메인 클래스만을 수정해서(혹은 스프링을 사용한다면 환경설정만을 수정해서) 기능을 변경할 수 있다는 장점이 있습니다. 기능 클래스들은 이미 잘 쓰고 있는 신뢰할 수 있는 프로그램들인데, 이것들을 괜히 수정하지 않고도 기능을 변경할 수 있는 것이지요.
여기서 기능 클래스를 수정하지 않는다는 특성이 가장 중요합니다. 전체 프로그램을 전혀 수정하지 않고도 기능을 변경할 수 있는 것은 당연히 아닙니다.
저 개인적으로는 스프링이나 DI 언급하면서 항상 나오는 "프로그램의 수정 없이 어쩌구" 하는 부분을 무시하시는걸 권해드립니다. 오히려 이런 말이 더 헷갈리게 만든다고 생각하거든요. 프로그램의 기능이 변경되기 위해서는 기능 클래스건 메인 클래스이건 환경설정이건 어딘가가 변경되어야 합니다. 하지만 기능을 잘 분할해서 서로서로 의존성을 주입하는 식으로 작업을 처리하게 된다면, 이후 기능이 변경되어야 할 때 코드가 수정되어야 할 부분을 최소화 할 수 있게 되죠.