1. 의존이란?

DI 는 Dependency Injection 의 약자로 우리말로는 의존 주입 이라고 번역한다. 이 단어의 의미를 이해하려면 먼저 의존(Dependency) 이 뭔지 알아야 한다. 여기서 말하는 의존은 객체 간의 의존을 의미한다.

public class ExampleService {
		
		private ExampleDao exampleDao = new ExampleDao();

		// 생략
}

이렇게 한 클래스가 다른 클래스의 메서드를 실행할 때 이를 의존한다고 표현한다. 앞서 코드에선 ExampleService 가 ExampleDao 를 의존한다고 표현할 수 있다.

의존은 변경에 의해 영향을 받는 관계를 의미한다. 예를 들어 ExampleDao 의 메서드의 내용을 변경하면 이 메서드를 사용하는 ExampleService 클래스의 소스 코드도 함께 변경된다. 이렇게 변경에 따른 영향이 전파되는 관계를 ‘의존'한다고 표현한다.

의존하는 대상이 있으면 그 대상을 구하는 방법이 필요하다. 가장 쉬운 방법은 의존 대상 객체를 직접 생성하는 것이다. 앞서 설명한 코드도 소스 코드 내부에서 직접 생성하였다.

클래스 내부에서 직접 의존 객체를 생성하는 것이 쉽긴 하지만 유지보수 관점에서 문제점을 유발할 수는 있다.

2. DI 를 통한 의존 처리

DI 는 의존하는 객체를 직접 생성하는 대신 의존 객체를 전달받는 방식을 사용한다. 예를 들어 앞서 의존 객체를 직접 생성한 클래스에 DI 를 적용하면 다음과 같이 구현할 수 있다.

public class ExampleService {
		
		private ExampleDao exampleDao;

		public ExampleService(ExampleDao exampleDao) {
				this.exampleDao = exampleDao; // 생성자를 통해 의존 객체 전달
		}

		// 생략
}

의존 객체를 직접 구하지 않고 생성자를 통해서 전달받기 때문에 이 코드는 DI(의존 주입) 패턴을 따르고 있는 것이다.

DI 를 적용한 결과 ExampleService 를 생성할 때에는 다음과 같이 ExampleDao 객체를 전달해야 한다.

ExampleDao exampleDao = new ExampleDao();

ExampleService exampleService = new ExampleService(exampleDao);

오히려 더 복잡해진 느낌이 난다. 객체 내부에서 필요한 객체를 생성하면 되는데 왜 주입받는 형태로 사용하는 걸까? 그건 바로 변경의 유연함 때문이다.

3. DI 와 의존 객체 변경의 유연함

의존 객체를 직접 생성하는 방식은 필드나 생성자에서 new 연산자를 이용해서 객체를 생성한다. 회원 등록 기능을 제공하는 MemberRegisterService 클래스에서 다음 코드처럼 의존 객체를 직접 생성할 수 있다.

public class MemberRegisterService {
		
		private MemberDao memberDao = new MemberDao();

		// 생략
}

회원의 암호 변경 기능을 제공하는 ChangePasswordService 클래스도 다음과 같이 의존 객체를 직접 생성한다고 하자.

public class ChangePasswordService {
		
		private MemberDao memberDao = new MemberDao();

		// 생략
}

MemberDao 클래스는 회원 데이터를 데이터베이스에 저장한다고 가정해보자. 이 상태에서 회원 데이터의 빠른 조회를 위해 캐시를 적용해야 하는 상황이 발생했다. 그래서 MemberDao 클래스를 상속받은 CachedMemberDao 클래스를 만들었다.