본문 바로가기
Java

[Java] 왜 DI(Dependency Injection)가 필요한가?

by bkuk 2023. 4. 10.

의존관계(Dependency)에 대해서

DI에 대해 이야기 하기 전에 먼저 의존관계에 대해서 알아봐야한다. 객체에게 의존관계란 무엇인가? 어느 순간 의존관계가 발생하는가?

의존관계란 객체 혼자 모든 일을 처리하기 힘들기 때문에 내가 해야 할 작업을 다른 객체에게 위임하면서 발생한다. 즉, 내가 가지고 있는 책임과 역할을 다른 객체에 위임하는 순간 발생하는 것이다.

예를 들면, Service 클래스와 DAO(Repository) 클래스가 있다고 가정해보자.

Service 클래스에서 하나의 기능을 구현하기 위해 DAO 클래스에게 데이터베이스 접근 로직을 위임하고 있다. 이때, Service 클래스는 작업(기능구현)을 완료하기 위해 DAO 클래스에게 의존하고 있다면, 이는 의존관계를 가지고 있는 것이다.

 

DI(Dependency Injection)에 대해서

DI객체 간의 의존관계를 어떻게 해결하느냐에 따른 새로운 접근 방식이다.

지금까지 의존관계에 있는 객체를 사용하기 위해 객체를 직접 생성하고, 사용하는 방식으로 구현했다면? 즉, Service 클래스와 의존관계가 있는 DAO 클래스를 사용하기 위해 이 클래스의 인스턴스를 직접 생성하고 사용하는 방식으로 구현했다면, 이는 유연한 개발을 하는데 한계가 있기 때문에 인스턴스를 생성하는 책임과 사용하는 책임을 분리하자는 것이 DI의 핵심이다.

다시 정리하자면, Service 클래스는 DAO 클래스에 대한 인스턴스 생성에 대한 책임은 없애고, 단순히 사용만 함으로써 유연성을 높이자는 DI 접근 방식이다.

 

간단한 예제

현재 시스템 시간에 따라 "오전" 또는 "오후"를 반환하는 클래스가 있다.

import java.util.Calendar;

public class DateMessageProvider {
	public String getDateMessage() {
        Calendar now = Calendar.getInstance();
        int hour = now.get(Calendar.HOUR_OF_DAY);

        if( hour < 12 ) {
            return "오전";
        } else {
            return "오후";
        }
	}
}

 

getDateMessage()에 대한 테스트 클래스는 다음과 같다.

public class DateMessageProviderTest {
	
	@Test
	public void morning() {
		DateMessageProvider provider = new DateMessageProvider();
		assertEquals("오전", provider.getDateMessage());
	}
	
	@Test
	public void afternoon() {
		DateMessageProvider provider = new DateMessageProvider();
		assertEquals("오후", provider.getDateMessage());
	}
}

 

DateMessageProviderTest 의 테스트를 실행하면 컴퓨터의 현재 시간에 따라 테스트 메서드 중 둘 중의 하나는 반드시 실패한다. 이 두개의 테스트가 모두 성공하도록 소스코드를 리팩토링한다면 어떻게 해야할까?

 

두 개의 테스트가 모두 성공할 수 없는 이유는 DateMessageProvider가 Calendar와 의존관계를 가지는데 테스트를 위해 Calendar의 시간을 변경할 수 있는 방법이 없다.

이와 같은 의존관계를 강하게 결합(Tightly Coupling)되어 있다고 한다.

이와 같은 상황에서 리팩토링을 하려면, Calendar 인스턴스에 대한 생성을 getDateMessage()가 결정하는 것이 아닌 DateMessageProvider 외부에서 Calendar 인스턴스를 생성한 후 전달하는 구조로 바꿔야 한다. 

따라서, 아래 두가지 방법을 생각할 수 있다.

  • 생성자를 통해 인스턴스를 전달
  • 메서드 인자로 전달

 

개선된 코드

public class DateMessageProvider {
	public String getDateMessage(Calendar now) {
		int hour = now.get(Calendar.HOUR_OF_DAY);
		
		if( hour < 12 ) {
			return "오전";
		} else {
			return "오후";
		}
	}
}

 

public class DateMessageProviderTest {
	
	@Test
	public void morning() {
		DateMessageProvider provider = new DateMessageProvider();
		assertEquals("오전", provider.getDateMessage(createCurrentDate(11)));
	}
	
	@Test
	public void afternoon() {
		DateMessageProvider provider = new DateMessageProvider();
		assertEquals("오후", provider.getDateMessage(createCurrentDate(20)));
	}
	
	private Calendar createCurrentDate( int hourOfDay ) {
		Calendar now = Calendar.getInstance();
		now.set(Calendar.HOUR_OF_DAY, hourOfDay);
		return now;
	}
}

 

위와 같이 DateMessageProvider가 의존하고 있는 Calendar 인스턴스의 생성을 DateMessageProvider가 결정하지 않고, 외부로부터 전달받음으로써 테스트가 가능해졌다. 

이처럼, 객체 간의 의존관계에 대한 결정권을, 의존관계를 가지는 개체가 가지는 것이 아닌 외부의 누군가가 담당하도록 맡겨 버림으로써 좀 더 유연한 구조로 개발하는 것을 DI라고 한다.

 

 

 

참고: 자바 웹 프로그래밍 Next Step 하나씩 벗겨가는 양파껍질 학습법(저자: 박재성)

 

댓글