스프링 프레임워크에 대해 공부하다 보면 IoC, 제어의 역전이라는 키워드가 자주 등장한다. 스프링을 사용하는 개발자라면 숨쉬듯이 사용하고 있을 이 개념을 예제를 통해 다시 한번 정리해 보려고 한다.
IoC란?
프로그램의 제어 권한을 자신이 아닌 다른 대상에게 위임하는 방식
추상적인 개념이기에 한 문장으로 표현하기가 어렵다. 따라서 폭넓게 적용되어 있는 다양한 예시를 들어 살펴보자.
1. 서블릿
일반적인 자바 프로그램은 main()
메소드에서 시작해서 개발자가 미리 정한 순서를 따라 오브젝트가 생성되고 실행된다. 그런데 서블릿을 개발해 서버에 배포할 수는 있지만, 그 실행을 개발자가 직접 제어할 수 있는 방법은 없다. 대신 서블릿에 대한 제어 권한을 가진 서블릿 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출한다. 정리하자면 서블릿의 제어 권한을 자신이 아닌 서블릿 컨테이너에게 위임했다고 볼 수 있다.
2. 템플릿 메소드 패턴
디자인 패턴 중 템플릿 메소드 패턴도 제어의 역전 개념을 사용하는 대표적인 사례이다. 템플릿 메소드 패턴은 변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만들도록 하는 것이다. 다음 템플릿 메소드 패턴이 적용된 DAO를 통해 제어의 역전이 적용된 사례를 살펴보자
1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// 사용자 추가 로직
}
public void get(String id) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// 사용자 조회 로직
}
public abstract Connection getConnection() throws ClassNotFoundException, SQLException{};
}
위 예제를 보면 추상 UserDao를 상속한 서브클래스는 getConnection()을 구현한다. 하지만 서브클래스는 getConnection()에 대한 제어권을 가지지 않는다. 정리하자면 getConnection()의 제어 권한을 자신이 아닌 상위 템플릿 메소드(add, get 등)에게 위임 했다고 볼 수 있다.
3. 스프링 프레임워크
프레임워크 또한 제어의 역전 개념이 적용된 대표적인 기술이다. 애플리케이션 코드는 직접 실행되지 않고 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중 개발자가 만든 애플리케이션 코드를 사용하도록 만든다. 정리하자면 애플리케이션 코드의 제어 권한을 자신이 아닌 프레임워크에게 위임했다고 볼 수 있다.
4. 애플리케이션 컨텍스트
빈의 생명주기를 관리하며 의존관계를 맺어주는 애플리케이션 컨텍스트 또한 제어의 역전 개념이 적용된다. DI를 사용하는 다음 예제를 보자.
1
2
3
4
5
6
7
8
9
10
11
12
public class UserDao{
@Autowired Connection connection;
public void add(User user) throws ClassNotFoundException, SQLException {
// ...
PreparedStatement ps = connection.prepareStatement(
"insert into users(id, name, password) values(?,?,?)");
// ...
}
}
add() 메소드는 직접 커넥션을 만들어 사용하지 않고 @Autowired
애노테이션을 통해 컨테이너에 등록된 빈을 사용한다. 정리하자면 빈의 생성 및 생명주기 관리에 대한 제어 권한을 자신이 아닌 애플리케이션 컨텍스트에 위임했다고 볼 수 있다.
IoC의 장점
그렇다면 IoC를 사용함으로써 얻을 수 있는 장점이 무엇이기에 이렇게 많은 곳에서 사용하는 걸까? 그것은 각 오브젝트들의 관심과 책임을 분리하여 다양한 환경에 효과적으로 대응할 수 있기 때문일 것이다. 위 4번 예제에서 직접 커넥션을 생성하고 사용한다고 가정하자. 요구사항이 변하여 다른 DB를 사용해야 한다면 소스코드를 수정해야 할 것이다. 하지만 IoC를 적용한 애플리케이션 컨텍스트를 사용한다면 커넥션의 생성을 고민하지 않아도 될 것이다.