DI란?
DI는 Dependency Injection의 준말로서 의존관계 주입이라고 해석할 수 있다.
먼저 의존(Dependency)에 대해서 이해해보자.
의존은, 변경에 의해 영향을 받는 관계를 의미한다. 따라서 A의 변경에 따른 영향이 B에게 전파된다면 A와 B의 관계를 서로 의존한다고 이야기할 수 있다.
다음과 같은 MemberService 객체가 있다고 가정해보자.
public class MemberService { private MemberDao memberDao; public void register(Member member){ // 해당 회원이 이미 존재하는지 확인 memberDao.isExist(member); } }
MemberService 객체는 DB 에 접근하기 위해 MemberDao 객체를 사용한다. 이렇게 한 클래스가 다른 클래스의 메서드를 실행할 때 이 두 클래스는 서로 의존한다고 표현한다.
그리고 MemberService는 MemberDao를 멤버로 갖는데, 이 멤버는 생성자에서 다음과 같이 초기화되고 있다고 가정하자.
public class MemberService { private MemberDao memberDao; public MemberService() { memberDao = new MemberDao(); } ... }
그렇다면, 만약 MemberDao에서 변경이 일어났을 때 (ex. 생성을 생성자가 아닌 정적 팩토리 메서드로 하도록 변경되었다는 둥 ..) 우리는 MemberService까지 수정해야하는 번거로움이 있다.
또한 (이건 조금 이상한 예시인 것 같지만) 이 MemberService를 명수는 memberDao를 사용해 디비에 접근하고, 조앤은 자기 이름을 붙이는 것을 좋아해서 joMemberDao를 사용해 디비에 접근하고 싶다고 하자. 그러한 경우에는 조앤은 직접 MemberService 내부를 수정해야한다. 이러한 경우, 구현 클래스가 아닌 인터페이스를 이용하여 분리해낼 수 있고, 조앤과 명수는 각각 그 인터페이스를 구현한 클래스를 이용해 사용할 수도 있다. 하지만 그렇게 된 경우에도 MemberService 의 생성자 안에서 특정 Dao를 명시해주어야하는 것은 같다. (여전히 의존 관계를 갖는다.)
이러한 의존관계를 끊기 위해서 우리는 의존성 주입을 이용할 수 있다.
의존성 주입이란, 말 그대로 의존성을 주입시켜준다는 의미인데, 객체를 직접 생성하는 것이 아니라 외부에서 생성한 후 주입을 시켜주는 방식이다. 주입을 시켜주는 방법에는 두 가지가 있는데, 첫번째는 생성자를 통한 주입, 두번째는 setter를 통한 주입이 있다.
생성자를 통한 주입은 다음과 같이 객체가 생성될 때의 인자로 주입할 객체를 넘겨주는 것이다.
public class MemberService { private MemberDao memberDao; public MemberService(MemberDao memberDao){ this.memberDao = memberDao; } }
setter를 통한 주입은 외부에서 setter 메서드를 이용해 주입시켜주는 방법이다.
MemberService memberService = new MemberService(); memberService.setMemberDao(new MemberDao());
그렇다면 우리는 왜 굳이 의존관계 주입을 사용해야할까?
우선, 클래스 간의 의존성이 줄어들어 응집도를 높이고 결합도를 낮출 수 있다.
의존한다는 것은 변화가 전파되는 범위가 넓다는 것이다. 따라서 한 클래스가 변화하게 되면 그 영향은 수많은 클래스들에게 미치게 되고, 이는 유지보수하는 데에 어려움을 줄 수 있다. (나는 코드 한 줄을 수정했는데 그로인해 여러 클래스에서 빨간줄이 뜨는 경우..)
또한 의존성 주입을 이용하면 객체를 조립해서 사용하는 것과 유사하기 때문에 재사용성을 높일 수 있으며, 분리되어있기 때문에 테스트하기에도 좋다.