[1.3 DAO의 확장] 마지막 단계에서 만든 UserDaoTest는 클라이언트 역할을 하는 클래스로 생성하였다.
다른 책임이나 관심사를 독립적으로 분리시키며서 DAO를 만들어왔지만, 결국 클라이언트 역할을 하는 이 테스트 클래스에서 '어떤 DB커넥션을 가져올지 결정하는 책임'을 맡게 되었다.
다시 이 클라이언트를 분리시키는 작업을 해보겠다. 여기서 팩토리(Factory)라는 개념이 등장한다.
팩토리(Factory)
객체의 생성 방법을 결정하고, 객체를 돌려주는 역할을 한다.
오브젝트를 생성하는 것과, 생성된 오브젝트를 사용하는 것을 분리하기 위한 목적으로 사용한다.
클라이언트 UserDaoTest 속 UserDAO, ConnectionMaker의 생성과정을 새로운 팩토리로 옮기고,
클라이언트에서 팩토리에서 요청한 UserDAO를 사용해보겠다.
오브젝트를 생성하는 곳은 팩토리, 여기서 생성된 오브젝트를 사용하는 곳은 클라이언트로 분리한다.
package springbook.user.dao;
public class DaoFactory {
public UserDAO userDAO(){
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDAO userDao = new UserDAO(connectionMaker);
return userDao;
}
}
package springbook.user.dao;
import springbook.user.domain.User;
import java.sql.SQLException;
public class UserDaoTest {
public static void main(String[] args) {
UserDAO dao = new DaoFactory().userDAO();
...
}
ConnectionMaker는 인터페이스. D회사는 이 인터페이스로 UserDAO에 접근하였다.
UserDAO의 코드를 자세히 알지 않아도, 수정하지 않아도 D회사에 맞도록 DB커넥션을 호출하여 연결하였다.
코드 수정이 여러번 되었으니 SQL작업이 제대로 작동되는지 테스트를 해보면서 진행해야 한다.
기존에 실행시킨 적이 있으니 기본 테이블을 깔끔하게 만들어주지 않으면 오류가 발생할 것이다.
나는 DROP TABLE
을 통해 테이블 자체를 삭제하고, 다시 만들어 주면서 테스트하였다.
package springbook.user.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DConnectionMaker implements ConnectionMaker{
@Override
public Connection makeConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection c = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/tobiSpring", "root", "password");
return c;
}
}
DConnectionMaker에 잊지 말고, 사용하고 있는 DB를 연결해주도록 하자.
팩토리를 통해 우리의 DB가 UserDAO에 연결해 사용할 수 있도록 오브젝트를 생성해주었다. 클라이언트인 UserDaoTest의 main()에서 이렇게생성된 오브젝트 dao를 통해 정보를 저장하고, 조회할 수 있게 해준다.
실행은 이전과 똑같이 나와야 한다.
UserDao와 ConnectionMaker는 실질적인 로직을 담당하는 모듈화된 구성 요소 컴포넌트로 볼 수 있고, DaoFactory는 오브젝트와 구조를 결정하는 설계도의 역할로 볼 수 있다. 어떤 오브젝트가 어떤 오브젝트를 사용하는지 그를 결정하고 있다.
이렇게 역할을 분리시켜주는 것이 팩토리 사용의 목적이다.
만약 DAO가 User만 있는게 아니라 여러가지가 생성되면 Factory에서 다른 DAO에 접근하는 메소드를 생성해주어야 한다.
맨 처음 [1.1 초난감 DAO]에서 getConnection()이라는 메소드를 새로 생성해준 것을 기억하자.
ConnectionMaker connectionMaker = new DConnectionMaker();
UserDAO userDao = new UserDAO(connectionMaker);
위 코드에서 ConnectionMaker를 가져오는 메소드를 새로 생성하여 사용하면 여러 가지 DAO가 만들어져도 해당 메소드를 사용할 수 있다.
package springbook.user.dao;
public class DaoFactory {
public UserDAO userDAO(){
return new UserDAO(cM());
}
public ConnectionMaker cM(){
return new DConnectionMaker();
}
}
조금 더 구분할 수 있도록 메소드 이름은 connectionMaker()가 아닌 cM()으로 만들어주었다.
ConnectionMaker의 객체를 반환하여, UserDAO에서 그 객체를 이용하도록 만들었다.
이 방법으로 UserDAO 뿐만 아니라, accountDAO나 messageDAO가 생기더라도 같은 방식으로 메소드를 통해 만든 객체를 넘겨주기만 하면 된다.
일반적인 흐름은 프로그램이 시작되는 지점에서 사용할 오브젝트를 결정하고, 생성하여 만들어진 메소드를 호출하고, 다음에 사용할 것을 결정하는 식으로 반복된다. 우리가 만든 초기의 UserDAO에서도 오브젝트를 직접 생성하고, 오브젝트의 메소드를 사용하였다. 지금의 UserDAO에서도 사용할 구현 클래스를 직접 결정하고, 필요한 시점에 오브젝트를 생성하여 각 메소드에서 이를 사용한다.
모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고 스스로 관장한다. 작업을 사용하는 쪽에서 흐름을 제어한다. 제어의 역전은 이 흐름을 뒤집는 것이다.
제어의 역전(IoC; Inversion of Control)
프로그램 제어 흐름 구조가 뒤바뀌는 것
자신이 사용할 오브젝트를 스스로 선택하지 않는다. 자신도 어떻게 만들어지고 어디서 사용되는지 알 수 없다. 모든 제어 권한을 다른 대상에게 위임하여 어떤 흐름으로 흘러갈지 알지 못한다.
서블릿을 생각해보면, 개발자는 직접 제어하거나 실행할 수 없다. 서블릿에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 객체를 생성하여 호출한다. 이와 같은 방식이 역전 방식이다.
제어권을 상위 템플릿 메소드에 넘기고 자신을 필요할 때 호출되어 사용되도록 하는 것, 템플릿 메소드는 제어의 역전을 통해 문제를 해결하는 디자인패턴으로 볼 수 있다.
프레임워크, 컨테이너처럼 컴포넌트 생성과 관계 설정, 사용, 생명주기 관리 등을 관장할 수 있는 것이 필요하다. 우리가 만든 DaoFactory는 가장 단순한 IoC로 볼 수 있다.
Spring은 IoC 모든 기능의 기초가 되는 기반 기술로 IoC를 극한까지 적용하고 있다.
'Framework > Spring' 카테고리의 다른 글
[Spring] 싱글톤 레지스트리(Singleton Registry)란? (0) | 2024.12.09 |
---|---|
[토비의스프링] 1.5 스프링의 IoC (0) | 2024.12.03 |
[토비의스프링] 1.3 DAO의 확장 (0) | 2024.11.28 |
[토비의스프링] 1.2 DAO의 분리 (0) | 2024.11.27 |
[토비의스프링] 1.1 초난감 DAO (0) | 2024.11.26 |