개요
1. 의존관계 주입이란?
2. 의존관계 검색
3. 의존관계 주입의 응용
본문
1. 의존관계 주입이란?
의존관계란 두 클래스 또는 모듈 사이에서 한쪽 방향으로 영향을 미치는 관계를 의미한다. A가 B에 의존하고 있다고 한다면 B가 변할 경우 A에게 영향이 미치는 관계이다. 반대로 A가 변한다고 해서 B가 변하지는 않는관계이다. 지금까지 작업해온 UserDao를 보면 UserDao가 ConnectionMaker에 의존하고 있는 것을 볼 수 있다. DConnectionMaker라는 클래스의 존재는 알지도 못한다. 이러한 느슨한 관계 속에서 런타임 시에는 DConnection과의 의존관계가 실체화된다. 이러한 관계를 런타임 의존관계 또는 오브젝트 의존관계라고 부르며 DConnection을 의존 오브젝트라고 부른다. 또한 이렇게 의존 오브젝트와 그것을 사용할 주체를 런타임시에 연결하는 작업을 의존관계 주입이라고 부른다.
아래는 관계 설정을 분리하기 전의 코드이다.
public UserDao(){
connectionMaker = new DConnectionMaker();
}
위 코드는 DConnectionMaker 클래스의 존재를 이미 알고 있고 구현 단계에서 이미 의존관계로서 연결이 결정되었다. 이를 인터페이스를 활용해서 느슨한 관계로 만들고 구현한 아래의 코드가 의존관계 주입위한 코드이다.
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
}
2. 의존관계 검색
런타임 시에 의존관계를 방법은 주입만이 있는 것은 아니다. 의존관계 검색이라고 불리는 방법도 있다. 검색은 자신이 필요로 하는 의존 오브젝트를 능동적으로 찾는다. 물론 자신이 어떤 오브젝트를 사용할지 결정하는 것은 아니다. 예시는 아래와 같다.
public UserDao( ) {
DaoFactory daoFactory = new DaoFactory();
this.connectionMaker = daoFactory.connectionMaker();
}
위의 코드에서도 UserDao는 여전희 어떤 ConnectionMaker오브젝트를 사용할지 알지 못한다. 방법이 생성자를 통해 주입 받는 것이 아닌 DaoFactory에 요청하는 것으로 바뀐 것 뿐이다. 이러한 방법을 의존관계 검색이라고 부른다.
두 방법 사이에 큰 차이가 있는 것은 아니나 코드가 좀 더 단순하고 깔끔한 쪽이 주입 쪽이기에 주입 쪽이 더 범용적으로 쓰인다.
3. 의존관계 주입의 응용
DI를 활용한 코드의 장점 중 가장 대표적인 이점은 기능 구현의 교환이 용이하다는 것이다. DI가 활용되지 않았더라면 사정에 따라 DB를 변경해야할 때 DAO 갯수만큼 수정해야할 일을 DI가 적용되어 있다면 DaoFactory의 코드 한 줄만 수정하면 된다.
//개발용과 운영용을 구분해서 코드를 작동해야할 경우 각각
@Bean
public ConnectionMaker connectionMaker() {
return new LocalDBConnectionMaker();
} //개발용 DB
@Bean
public ConnectionMaker connectionMaker() {
return new ProductionDBConnectionMaker();
}//운영용 DB
//로 한 줄씩만 바꾸고 UserDao코드에 손 한 번 안대도 기능한다.
//만약 DAO가 무수히 많다면 코드의 효율은 그만큼 증가한다.
기능을 확장하는 데에도 용이하다. DB연결 횟수를 세는 기능을 추가하려고 한다고 생각해보자. 각각의 DAO의 DB호출 메소드마다 카운터를 증가시키는 코드를 넣는 법을 생각해볼 수 있겠다. 하지만 이 경우 DAO가 무수히 많을 경우 무척 낭비이고 힘이 많이 든다. DI컨테이너라면 DAO와 DB커넥션을 만드는 오브젝트 사이에 연결 횟수를 카운팅하는 오브젝트를 하나 더 추가하는 것으로 간단히 만들 수 있다.
public class CountingConnectionMaker implements ConnectionMaker {//인터페이스 구현, Dao가 의존하고 있는 인터페이스를 구현하고 있다면 어떤 것이든 의존관계 주입 가능
int counter = 0;
private ConnectionMaker realConnectionMaker;
public CountingConnectionMaker(ConnectionMaker realConnectionMaker) {
this.realConnectionMaker = realConnectionMaker;
}
@Override
public Connection makeConnection() throws ClassNotFoundException, SQLException {
this.counter++;//DB커넥션은 안만듦. 대신 makeConnection이 호출될 때마다 카운터 증가.
return realConnectionMaker.makeConnection();//세는 게 끝나면 진짜 커넥션을 DAO에게 돌려줌.
}
public int getCounter() {
return this.counter;
}
}
위와 같이 ConnectionMaker를 구현함으로써 UserDao의 코드는 수정하지 않고 UserDao와 DB연결 사이에 세는 기능을 추가할 수가 있다. 이 클래스는 세는 기능만 구현하고 DB와의 연결은 기존에 존재하던 DConnectionMaker를 그대로 사용할 것이니 DConnectionMaker를 생성자의 매개 변수로 받아서 쓸 수 있게끔 코드를 구현한다. counter변수를 connectionMaker가 호출될 때마다 증가하게끔 만들고 realConnection(DConnectionMaker로 받은)의 형태로 DAO에 리턴해준다. 이에 맞춰서 DaoFactory를 수정하면 아래와 같아진다.
@Configuration
public class DaoFactory {
@Bean
public UserDao userDao() {
UserDao userDao = new UserDao(connectionMaker()); //connctionMaker()객체(DB연결 + 카운트) 주입
return userDao;
}
@Bean
public ConnectionMaker connectionMaker() {
return new CountingConnectionMaker(realConnectionMaker());//realConnectionMaker에서 커넥트 요소 주입.
}
@Bean public ConnectionMaker realConnectionMaker() {
return new DConnectionMaker();
}
}
정리
1. DI란 IoC라는 포괄적인 용어에서 스프링에서 핵심적인 작동방식을 일컫는 것으로 런타임 시에 의존관계가 형성되는 관계를 의미한다.
2. 의존관계는 주입만이 아니라 검색이라는 방법을 통해서도 형성 가능하다.
3. 의존관계 주입을 통해서 할 수 있는 여러가지 장점과 활용법이 있지만 그 중 가장 기초적이라 할 수 있는 수정과 확장에 관련된 응용을 알아보았다.
소스 깃허브
https://github.com/cholongbul/Tobyspring
cholongbul/Tobyspring
토비의 스프링 연습. Contribute to cholongbul/Tobyspring development by creating an account on GitHub.
github.com
'스프링' 카테고리의 다른 글
토비의 스프링 - 2.1 UserDaoTest다시 보기//2.2UserDaoTest개선 (0) | 2021.05.27 |
---|---|
토비의 스프링 - 1.8 XML을 이용한 설정 (0) | 2021.05.23 |
토비의 스프링 - 1.6 싱글톤 레지스트리와 오브젝트 스코프 (0) | 2021.05.20 |
토비의 스프링 - 1.5 스프링IoC (0) | 2021.05.20 |
토비의 스프링 - 1.4 제어의 역전 (0) | 2021.05.20 |