Search
Duplicate
🍃

1.4 제어의 역전(IoC)

태그

1.4.1 오브젝트 팩토리

앞서 개방 폐쇄 원칙, 높은 응집력 & 낮은 결합도, 전략 패턴이 잘 적용된 UserDao를 만들기 위한 리팩토링 작업을 수행하였다.
하지만 이런 리팩토링 과정에서 UserDaoTest는 원래 UserDao의 기능이 잘 동작하는지를 테스트하기 위한 것이었지만, 기존 UserDao가 담당하던 어떤 ConnectionMaker 구현 클래스를 사용할지를 결정하는 기능까지 담당하게 되었다.
기존의 방식처럼 성격이 다른 책임이나 관심사를 분리하기 위하여 UserDaoTest의 기능을 분리하도록 한다.

팩토리

객체의 생성 방법을 결정하고, 그렇게 만들어진 오브젝트를 돌려주는 클래스를 새로이 생성한다. 이런 일을 하는 오브젝트를 흔히 팩토리(Factory)라고 부른다.
여기서 말하는 팩토리는 디자인 패턴에서 말하는 특별한 문제를 해결하기 위해 사용되는 추상 팩토리 패턴이나 팩토리 메서드 패턴과는 다르므로 혼동하면 안된다.
팩토리를 통해 기능을 분리하는 작업은 기존의 UserDao, ConnectionMaker 관련 생성 작업을 DaoFactory로 옮기고, UserDaoTest에서는 DaoFactory에 요청해서 미리 만들어진 UserDao 오브젝트를 가져와 사용하게 만드는 과정을 통해 이루어진다.
... public class DaoFactory{ public UserDao userDao(){ // 팩토리의 메서드는 UserDao 타입의 오브젝트를 어떻게 만들고, 준비시킬지를 결정한다. ConnectionMaker connectionMaker = new DconnectionMaker(); UserDao userDao = new UserDao(connectionMaker); return userDao; } }
Java
복사
public class UserDaoTest{ public static void main(String[] args) throws ClassNotFoundException, SQLException{ UserDao dao = new DaoFactory().userDao(); ... } }
Java
복사
이렇게 DaoFactory를 분리하는 과정을 거치게 되면 이제 UserDaoTest는 UserDao가 어떻게 만들어지는지, 어떻게 초기화되어 있는지에 신경쓰지 않고 팩토리로부터 UserDao 오브젝트를 받아다가, 자신의 관심사인 테스트를 위해 활용하기만 하면 그만이다.

설계도로서의 팩토리

이렇게 분리된 오브젝트들의 역할과 관계를 분석해보자. UserDao와 ConnectionMaker는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있다.
전자가 실질적인 로직을 담당하는 컴포넌트라면 , 후자는 애플리케이션을 구성하는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼수 있다. 간단히 설명하면 설계도는 어떤 오브젝트가 어떤 오브젝트를 사용하는지를 정의해놓은 코드라고 볼 수 있다.

1.4.2 오브젝트 팩토리의 활용

UserDaoTest로부터 DaoFactory를 분리함으로써 애플리케이션의 컴포넌트 역할을 하는 오브젝트와 애플리케이션의 구조를 결정하는 오브젝트를 분리했다는 장점을 얻을 수 있었다.
그렇다면 DaoFactory에 UserDao가 아닌 다른 DAO의 생성 기능을 넣는 경우를 가정해보자. 이 경우에 UserDao를 생성하는 userDao( ) 메서드를 복사해서 accountDao( ), messageDao( ) 메서드로 만든다면 ConnectionMaker 구현 클래스의 오브젝트를 생성하는 코드가 메서드마다 반복되는 문제가 발생한다. 이렇게 오브젝트 생성 코드가 중복되는건 좋지 않은 현상이다. DAO가 더 많아지면 ConnectionMaker의 구현 클래스를 바꿀 때마다 모든 메서드를 일일이 수정해야 하기 때문이다.
중복 문제를 해결하는 가장 좋은 방법은 분리이다. ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 코드를 별도의 메서드로 뽑아낸다. DAO를 생성하는 각 메서드에서는 새로 만든 ConnectionMaker 생성용 메서드를 이용하도록 수정함으로써 분리를 이뤄낼 수 있다.
// 기존 public class DaoFactory{ public UserDao userDao(){ return new UserDao(new DconnectionMaker()); // 코드 중복! } public AccountDao accountDao(){ return new AccountDao(new DconnectionMaker()); // 코드 중복! } public MessageDao messageDao(){ return new UserDao(new DconnectionMaker()); // 코드 중복! } }
Java
복사
// 분리 후 public class DaoFactory{ public UserDao userDao(){ return new UserDao(new connectionMaker()); } public AccountDao accountDao(){ return new AccountDao(new connectionMaker()); } public MessageDao messageDao(){ return new UserDao(new connectionMaker()); } public ConnectionMaker connectionMaker(){ return new DconnectionMaker(); // 분리해서 중복을 제거한 ConnectionMaker 타입 오브젝트 생성 코드 } }
Java
복사
위의 코드는 분리 작업을 수행하기 이전의 코드이고, 아래의 코드는 분리 작업을 수행한 이후의 코드이다. 하지만 "여전히 new connectionMaker()" 코드가 중복되고 오히려 코드 길이가 길어졌는데?" 라는 의문을 가질 수 있다.
하지만 이전의 코드에서 ConnectionMaker의 구현 코드가 변경되어 더이상 DconnectionMaker() 메서드를 사용하지 않는 경우를 생각해보자. 기존의 코드가 3번의 코드 수정을 해야하는데 반해, 새로운 코드는 1번의 수정만을 거치면 된다. 이는 메서드 개수가 늘어나면 늘어날수록 차이가 심해질 것이다.

1.4.3 제어권의 이전을 통한 제어관계 역전

일반적으로 프로그램의 흐름은 main( ) 메서드와 같이 프로그램의 시작 지점에서 다음에 사용할 오브젝트를 결정 → 생성 → 호출 → 다음 호출 식으로 진행된다. 이런 프로그램 구조에서는 각 오브젝트는 프로그램 흐름을 결정하거나 상요할 오브젝트를 구성하는 작업에 능동적으로 참여한다. 즉, 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조이다.
제어의 역전이란 이런 제어 흐름의 개념을 거꾸로 뒤집는 것이다. 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하지도 않는다. 또 자신도 어떻게 만들어지고 어디서 사용하는지를 알 수 없다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임하기 때문이다.
프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이다. 애플리케이션 코드가 애플리케이션 흐름을 직접 제어하는 도중에 필요한 기능이 있을 때 능동적으로 라이브러리를 사용하는 것과 달리, 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다. 프레임워크에는 분명한 제어의 역전 개념이 적용되어 있어야 하며, 애플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작해야 한다.
우리가 만든 UserDao와 DaoFactory에도 제어의 역전이 적용되어 있다. 원래 ConnectionMaker의 구현 클래스를 결정하고 오브젝트를 만드는 제어권은 UserDao에게 있었다. 하지만 그 제어권은 DaoFactory로 넘어갔다. UserDao 자신도 팩토리에 의해 수동적으로 만들어지고 자신이 사용할 오브젝트도 DaoFactory가 공급해주는 것을 수동적으로 사용해야 할 입장이 되었다. 이것이 제어의 역전(IoC)가 일어난 상황이다.
제어의 역전에서는 프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등을 관장하는 존재가 필요하다. DaoFactory는 오브젝트 수준의 가장 단순한 IoC 컨테이너 내지는 IoC 프레임워크라고 불릴 수 있다.