- Inversion of Control의 약자로, 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.
- 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하는 것을 말한다.
일반적인 프로그램의 흐름은 main() 메소드와 같이 프로그램이 시작되는 지점에서 다음에 사용할 오브젝트를 결정하고 생성하고, 생성된 오브젝트에 있는 메소드를 호출하고, 그 오브젝트 메소드 안에서 다음에 사용할 것을 결정하고 호출하는 식의 작업이 반복된다. 이런 흐름을 완전히 뒤바꾼 것을 제어의 역전이라고 표현한다.
예제로 제어의 흐름을 설명하고자 한다.
아래는 일반적인 프로그램의 흐름으로 Client는 UserDao 클래스에 대해서 결정 및 생성을, ConnectionMaker 클래스에 대해서는 생성을 직접 담당하고 있다.
public class MainTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException{
UserDao userDao = new UserDao();
User user = new User();
user.setId("testId");
user.setName("홍길동");
user.setPassword("1234");
userDao.insert(user);
...
}
}
MainTest는 UserDao 클래스의 오브젝트를 직접 생성하고, 만들어진 오브젝트의 메서드를 사용하고 있다.
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao() {
this.connectionMaker = new DConnectionMaker();
}
public void insert() {
Connection conn = connectionMaker.makeConnection();
...
}
public User select() {
Connection conn = connectionMaker.makeConnection();
...
}
}
UserDao 클래스 또한 어떤 커넥션 구현체를 사용할지 직접 결정하고 있고, 필요한 시점에 생성하고 사용하고 있다.
public interface ConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException;
}
public class DConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException {
...
return connection;
}
}
위의 예제와 같이 일반적인 프로그램의 흐름을 완전히 뒤바꾸는 것을 제어의 흐름이라고 한다.
지금부터는 🤔모든 제어의 권한을 아래의 설계도 처럼 자신이 아닌 다른 대상으로 흐름을 완전히 바꿔보려고 한다!
아래의 설계도는 일반적은 프로그램에서 제어의 역전을 활용한 흐름도로 몇 가지의 특징을 살펴볼 수 있다.
public class DaoFactory {
public UserDao userDao() {
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
return userDao;
}
}
DaoFactory 클래스를 추가하여 커넥션 결정권, UserDao 생성 권한을 주었다.
public class MainTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException{
UserDao userDao = new DaoFactory().userDao();
User user = new User();
user.setId("testId");
user.setName("홍길동");
user.setPassword("1234");
userDao.insert(user);
...
}
}
Client에서는 커넥션 결정권, UserDao 생성 권한을 DaoFactory 클래스에게 모두 위임하여 모든 애플리케이션의 흐름을 자신이 아닌 외부로 돌렸다.
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
public void insert() {
Connection conn = connectionMaker.makeConnection();
...
}
public User select() {
Connection conn = connectionMaker.makeConnection();
...
}
}
UserDao 클래스는 ConnectionMaker를 외부에서 주입할 수 있도록 생성자 파라미터를 추가했다. 이제 UserDao는 DB커넥션을 맺어주는 역할은 하지만 어떤 커넥션을 구현할지는 모르는 상태가 되었다. 즉, 능동적으로 커넥션을 주입하지 않고, 수동적으로 사용해야 할 입장이 된 것이다.
public interface ConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException;
}
public class DConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException {
...
return connection;
}
}
제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않아야 하며, 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하여야 한다.
위의 예제에서 ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 제어권은 UserDao에게 있었다. 하지만 IoC를 적용하고 나서는 모든 제어권은 DaoFactory에게 있다. 모든 결정권한을 DaoFactory에 넘겼으니 UserDao는 능동적이 아니라 수동적인 존재가 된 것이다.
MainTest 클래스 또한 main() 메소드에서 DaoFactory가 만들고 초기화해서 자신에게 사용하도록 공급해주는 ConnectionMaker만을 사용할 수 있다. 더욱이 UserDao와 ConnectionMaker의 구현체를 생성하는 책임도 DaoFactory가 맡고 있다.
이러한 모든 상황들이 제어의 역전이 일어난 상황이라고 표현한다.
[참고자료]
토비의 스프링 3.1 (Vol.1 스프링 이해와 원리)