의존 관계 주입 (DI)
의존성 주입이다.
DI는 오브젝트 레퍼런스를 외부로부터 주입 받고 이를 통해 여타 오브젝트와 다이내믹 하게 의존관계가 만들어지는 것이 핵심
두 클래스 또는 모듈이 의존 관계에 있다 말할때 항상 방향성을 부여해 줘야한다
UML 모델에서 의존관계를

다음과 같이 표현한다
그렇다면 의존하고 있다는것은 무슨 의미일까?
의존 한다는건 의존대상 즉 위 그림에서는 B가 변하면 A에게 영향을 미친다는 뜻이다.
B의 기능이 추가되거나 변경되거나, 형식이 바뀌거나 하면 영향이 A 로 전달된다.
지금까지 UserDao 를 예를 들어보자
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker;
}
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = connectionMaker.makeConnection();
PreparedStatement ps = c.prepareStatement("insert into users(id,name,password) values(?,?,?)");
ps.setString(1,user.getId());
ps.setString(2,user.getName());
ps.setString(3,user.getPassWord());
ps.executeUpdate();
ps.close();
c.close();
// 기존 코드와 동일
}
public User get(User user) throws ClassNotFoundException, SQLException{
Connection c = connectionMaker.makeConnection();
// 기존 코드와 동일
return user;
}
}
public interface ConnectionMaker {
Connection makeConnection() throws ClassNotFoundException, SQLException;
}
public interface ConnectionMaker {
Connection makeConnection() throws ClassNotFoundException, SQLException;
}
다음과 같은 코드를 보자
UserDao는 ConnectionMaker 에 의존하고 있는 형태다
Connection c = connectionMaker.makeConnection();
UserDao가 ConnectionMaker인터페이스를 사용하는 것을 나타낸다
이 그림에서는 UserDao는 ConnetionMaker 인터페이스에만 의존하고 있다.
따라서 ConnectionMaker 인터페이스가 변하면 그 영향을 UserDao 가 직접적으로 받게 된다.
하지만 COnnectionMaker 인터페이스를 구현한 클래스 즉, DConnectionMaker 등이 다른 것으로 바뀌거나 그 내부에서 사용하는 메서드 변화가 생겨도 UserDao에 영향을 주지 않는다.
이렇게 인터페이스에 대해 의존관계를 만ㄷ르어 두면 인터페이스 구현 클래스와의 관계는 느슨해지면서 변환에 영향을 덜 받는 상태가 된다.

즉 인터페이스를 통해 의존관계를 제한해주면 그만큼 변경에서 자유러워지는 셈
그림에서 알 수 있듯 UserDao 클래스는 ConnectionMaker 인터페이스에게만 직접 의존한다.
UserDao는 DConnectionMaker 라는 클래스의 존재도 알지 못한다.
모델 관점에서 보면 UserDao는 DconnectionMaker 클래스에 의존하지 않기 때문
UML에서 말하는 의존관계란 이렇게 설계 모델 관점에서 이야기하는것
설계 시점 의존관계가 실체화된 것
인터페이스 통해 설계 시점 느슨한 의존관계를 갖는 경우 UserDao의 오브젝트가 런타임 시 사용할 오브젝트가 어떤 클래스로 만든 것인지 미리 알 수 없다.
프로그램이 시작되고 UserDao 오브젝트가 만들어지고 나서 런타임 시 의존관계를 맺는 대상 , 즉 실제 사용 대상 오브젝트를 의존 오브젝트 라하낟.
의존 관계 주입은 이렇게 구체적인 의존 오브젝트와 그것을 사용할 주체 보통 클라이언트라고 부르는 오브젝트를 런타임시 연결해 주는 작업을 말한다.
UserDao는 ConnectionMaker 인터페이스라는 단순 조건만 만족하면 어떤 클래스로 부터 만들어졌든 상관없이 오브젝트를 받아들이고 사용한다.
의존 관계 주입이란
의존 관계 주입은 설계 시점에는 알지 못했던 두 오브젝트의 관계를 맺도록 도와주는 제 3의 존재가 있다는 것이다. (Factory, 컨테이너)
DI에서 말하는 제 3의 존재는 바로 관계 설정 책임을 가진 코들르 분리해서 만들어진 오브젝트라 볼수잇다.
기존 Factory 분리 전 코드
public UserDao(){
connectionMaker = new DconnectionMaker();
}
이 코드에 따르면 UserDao는 이미 설게 시점에서 DconnectionMaker라는 구체 클래스를 알고 있다.
즉 userDao 가 인테페이스 관계 뿐 아니라 런타임 의존관계 까지 관리하고 있는 셈이다
DI 컨테이너는 UserDao를 만드는 시점에서 생성자 파라미터로 이미 만들어진 DConnectionMaker의 오브젝트를 전달한다.
정확히 DConnectionMaker 오브젝트의 레퍼런스가 전달되는 것이다.
주입이란 외부에서 내부로 무엇인가를 넘겨줘야하는것인데 자바에서 오브젝트에 무엇인가를 넣어준다는 개념은 메소드를 실행하면서 파라미터로 오브젝트 레퍼런스를 전달하는 방법뿌니다
가장 손쉽게 상요할 수 있는 파라미터 전달 가능 메소드는 바로 생성자이다.
public class UserDao{
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker;
}
}
이렇게 DI 컨테이너에 의해 런타임시 오브젝트를 사용할 수 있도록 그 레퍼런스를 전달받는 과정이 마치 메소드를 DI 컨테이너가 UserDao 에게 주입하는 것과 같다고 해서 이를 의존관계 주입이라 부른다.

DI는 자신이 사용할 오브젝트에 대해 선택과 생성 제어권을 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다는 점에서 IoC 개념에 잘 들어 맞는다.
그래서 스프링을 IoC 컨테이너 외에도 DI 컨테이너 또는 DI 프레임 워크라고 부른다.
의존 관계를 맺는 방법이 외부로부터 주입이 아니라 스스로 검색을 이용하기 대문에 의존관계 검색 이라 불리는것도 있다.
의존관계 검색은 자신이 필요로 하는 의존 오브젝트를 능동적으로 찾는다.
물론 자신이 어떤 클래스를 이용할지 결정하지 않는다.
public UserDao(){
DaoFactory daoFactory = new DaoFactory();
this.connectionMaker = daoFactory.connectionMaker();
}
이렇게 해도 UserDao 는 여전히 어떤 ConnectionMaker 오브젝트를 사용할지 미리 알지 못한다.
여전히 코드의 의존대상은 ConnectionMaker 인터페이스 일뿐
런타임시 DaoFactory가 만들어서 돌려주는 오브젝트와 다이내믹하게 런타임 의존관계를 맺는다. 이는 IoC 개념을 잘 따른다고 볼 수 있다.
허나 적용방법은 외부로 부터 주입이 아닌 스스로 IoC 컨터이너인 DaoFactory 에게 요청하는 것이다.
이런 작업을 일반화 하면 스프링의 애플리케이션 컨텍스트라면 미리 정해놓은 이름을 전달해 그 이름에 해당하는 오브젝트를 찾게도니다.
따라서 일종의 검색이라 볼수 있다.
스프링 IoC 컨테이너인 애플리케이션 컨텍스트는 getBean() 이라는 메소드를 제공한다.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao userDao = context.getBean("userDao", UserDao.class);
의존 관계 검색은 의존관계 주입의 거의 모든 장점을 갖고 있으나
코드를 보면 느낄 수 있듯이 의존 관계 주입이 훨씬 단순하고 깔끔하다.
의존 관계 검색 방법은 코드 안에 오브젝트 팩토리 클래스나 스프링 API가 나타난다
애플리케이션 컴포넌트가 컨테이너와 같이 성격이 다른 오브젝트에 의존하게 되는 것이므로 그다지 바람직하지 않다.
사용자 대한 DB 정보를 어떻게 가져올 것인가에 집중해야하는 uSERdAO에서 스프링이나 오브젝트 팩토리를 만들고 API 를 이용하는 코드가 섞여있는 것은 어색하다. 따라서 대개는 의존 관계 주입 방식을 사용하는 편이 낫다.
의존 관계 검색을 사용할때가 있다
UserDaoTest를 보면
getBean() 을 사용했다. 스프링의 IoC와 DI 컨테이너를 적용했다 하더라도 애플리케이션의 기동 시점에서 적어도 한번은 의존 검색 방식을 사용해 오브젝트를 가져와야한다.
스테틱 메소드인 main 에서는 DI를 이용해 오브젝트 주입받을 방법이 없기 때문에
서버도 main 메소드와 비슷한 역할을 하는 서블릿에서 스프링 컨테이너에 담긴 오브젝트를 사용하려면 한번은 의존 관계 검색 방식을 사용해 오브젝트를 가져와야한다.
의존 관계 검색 하는 오브젝트는 자신이 스프링 빈일 필요가 없다.
반면 의존 관계 주입일시 UserDao 와 ConnectinMaker 사이에 DI 가 적용되려면 UserDao 도 반드시 컨테이너가 만드는 빈 오브젝트여야한다.
UserDao 에 ConnectionMaker 오브젝트를 주입해주려면 UserDao에 대한 생성과 초기화 권한을 갖고 있어야하며 그러려면 UserDao는 IoC방식으로 컨테이너에 생성되는 오브젝트 즉 빈이어야 하기 때문이다.
DI를 원하는 오브젝트는 먼저 자기 자신 컨테이너가 관리하는 빈이 돼야한다는 사실을 잊지 말자
수정자 메소드를 이용한 주입
setter 주입
일반 메서드를 이용한 주입
수정자 메소드 처람 set으로 시작해야하며 한번에 한개의 파라미터만 가질수 있다.
public class UserDao{
private ConnectionMaker connectionMaker;
public void setConnectionMaker(ConnectionMaker connectionMkaer){
this.connectionMaker = connectionMaker;
}
}
@ Bean
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setConnectionMaker(connectionMaker());
return userDao;
}
의존 관계 주입 시점과 방법이 달라졌을 뿐 결과는 동일하다
스프링이란 오브젝트가 설계되고 만들어지고 어떻게 관계를 맺고 사용되는지 관심을 갖는 프레임워크라는 것을 알 수 있었다.
허나 오브젝트를 어떻게 설계하고 분리하고 개선하고 의존관계를 가질지 결정하는 일은 스프링이 아니라 개발자 역할이며 책임이다.
스프링은 단지 원칙을 잘 따르는 설계를 적용하려 할때 등장하는 번거로운 작업을 편하게 할 수 있도록 도와주는 도구이다.
보통 웹 프로그램에서 사용하는 DAO 테스트 하는 방법은 다음과 같다.
DAO 를 만든 뒤 바로 테스트 하지 않고 서비스 계층 MVC 프레젠 테이션 계층까지 포함한 모든 입출력 기능을 대충이라도 코드로 다 만든다.
허나 이러한 테스트는 단점이 너무 많다.
DAO 뿐만 아니라 서비스 클래스 , 컨트롤러, 뷰 레이어 기능을 다만들고 테스트를 해야한 다는점 혹은 테스트중 에러 발생시 어디서 발생했는지 찾아내야하는 수고도 필요하다.
한꺼번에 너무 많은 것을 몰아서 테스트시 수행과정도 복잡해지고 오류 발생시 정확한 원인을 찾기 힘들어진다.
테스트는 가능하면 작은 단위로 쪼개서 집중해서 할 수 있어야한다.
UserTest는 한가지 관심에 집중할 수 있게 작은 단위로 만들어진 테스트다
이처럼 작은 단위 코드에 대해 테스트를 수행하는 것을 단위 테스트라고 한다.
여기서 말하는 단위란 무엇인지 그 크기와 범위가 어느정도인지 딱 정해진건 아니다.
크게는 사용자 관리 기능을 모두 통틀어 단위로 볼 수 있고, 작게 보자면 UserDao 의 add() 메서드 하나만 가지고 하나의 단위라 생각할 수있다.
충분히 하나의 관심에 집중해 효율적인 테스트할 만한 범위의 단위라고 보면 된다.
일반적으로 단위는 작을수록 좋다.
다른 코드들은 신경쓰지 않고 참여하지도 않고 테스트가 동작 할 수 있으면 좋다.