제어의 역전(IoC)
오브젝트 팩토리
초난감 DAO를 리팩토링 하는 과정에서 생긴 UserDaoTest를 살펴보자. 이 클래스는 어떤 ConnectionMaker구현 클래스를 사용할지를 결정하는 기능을 엉겁결에 떠맡았다. 하지만 원래 UserDaoTest는 UserDao의 기능이 잘 동작하는지를 테스트하기 위한 것이다. 성격이 다른 책임이나 관심사는 분리해버리는 과정은 지금까지 해왔던 주요한 작업이다. 따라서 이를 분리할 필요가 있다.
public classUserDaoTest {
public static voidmain(String[] args)throwsClassNotFoundException, SQLException {
ConnectionMaker connectionMaker =newDConnectionMaker();
UserDao dao =newUserDao(connectionMaker);
}
}
분리시킬 기능을 담당할 클래스를 하나 만들어보자. 이 클래스는 객체의 생성 방법을 결정하고 이를 리턴한다. 흔히 이런 일을 하는 오브젝트를 팩토리라고 부른다. DaoFactory를 만들어 UserDaoTest에서 어떤 ConnectionMaker 클래스를 사용할지 결정하는 기능을 옮겨주자.
public class DaoFactory {
public UserDao getUserDao() {
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao dao = new UserDao(connectionMaker);
return dao;
}
}
public class UserDaoTest {
public static voidmain(String[] args)throwsClassNotFoundException, SQLException {
UserDao dao =newDaoFactory().getUserDao();
...
}
}
이제 UserDaoTest는 UserDao가 어떻게 만들어지고 초기화되는지에 신경 쓰지 않고 팩토리로부터 오브젝트를 받아다가, 테스트에만 활용하면 된다.
- 이렇게 함으로써 Connection을 결정하고 준비하는 기능과 이를 사용하는 기능을 분리하였다.
- 여기서 UserDao와 ConnectionMaker는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있다. 애플리케이션 컴포넌트 역할을 하는 오브젝트와 애플리케이션의 구조를 결정하는 오브젝트를 분리했다는데 가장 의미가 있다.
오브젝트 팩토리의 활용
만약 새로운 Dao 클래스가 생겨서 각각의 dao 생성하는 기능이 추가되었다고 하자.
public class DaoFactory {
public UserDao userDao() {
return new UserDao(new DConnectionMaker());
}
public AccountDao accountDao() [
return new AccountDao(new DconnectionMaker());
}
public Message messageDao() {
return new MessageDao(new DConnectionMaker());
}
}
만약에 DB연결 Connection이 변경되어 DConnectionMaker()를 모두 변경이 된다고 하면 총 세 메소드의 모든 new DConnectionMaker() 코드를 수정해야 한다. 이런 중복 문제를 해결하기 위해 메소드 추출 기법을 통해 분리할 필요가 있다.
public class DaoFactory {
public UserDao userDao() {
return new UserDao(connectionMaker());
}
public AccountDao accountDao() [
return new AccountDao(connectionMaker());
}
public Message messageDao() {
return new MessageDao(connectionMaker());
}
public ConnectionMaker connectionMaker() {
return new DConnectionMaker();
}
제어의 역전(IoC)
일반적으로 프로그램 구조에서 각 오브젝트는 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다. 초기 UserDao에서는 UserDao 오브젝트를 직접 생성하고, 만들어진 오브젝트를 사용한다.
- 제어의 역전이란 이런 제어 흐름의 개념을 거꾸로 뒤집는 것이다. 자연스럽게 관심을 분리하고 책임을 나누고 유연하게 확장 가능한 구조로 만들기 위해 DaoFactory를 도입했던 과정이 바로 IoC를 적용하는 작업이었다고 볼 수 있다.
- 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않고 생성하지도 않는다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다.
스프링의 IoC
- 스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(bean)이라고 부른다. 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리라고 부른다.
- 현재는 IoC 방식을 따라 만들어진 일종의 빈 팩토리인 애플리케이션 컨텍스트를 주로 사용한다.
애플리케이션 컨텍스트의 동작방식
- 스프링에서는 @Configuration이라는 설정 파일을 참고해서 오브젝트 설정을 인식한다.
- 클라이언트에서 userDao라는 요청이 들어오면 ApplicationContext는 빈 목록에 userDao라는 빈이 있는지 체크하고 만약 없으면 DaoFactory로 빈 생성 요청을 한다.
- DaoFacotry는 등록되어있는 bean을 리턴하고 applicationContext에 알린다.
- applicationContext에 빈이 등록되면 클라이언트의 요청이 들어올 때 마다 등록된 빈을 리턴한다.
DaoFactory와 같은 오브젝트 팩토리에서 사용했던 IoC 원리를 그대로 사용하는데 애플리케이션 컨텍스트를 사용하는 이유에 대해 알아보자.
- 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
- 애플리케이션 컨텍스트는 종합 IoC 서비스를 제공해준다.
→ 오브젝트가 만들어지는 방식, 시점과 전략을 다르게 가져갈 수 있고, 인터셉트 등 오브젝트를 효과적으로 활용할 수 있는 다양한 기능을 제공한다.