IOC의 필요성 - 4. 오브젝트 팩토리, 제어관계 역전

2022. 1. 9. 20:12북리뷰/토비의 봄

728x90

오브젝트 팩토리

지금까지는 문제가 많은 초난감 DAO를 깔끔한 구조로 리펙토링하는 작업을 수행했다. 하지만, 여태껏 얼렁뚱땅 넘긴 게 하나 있다. 바로 클라이언트인 UserDaoTest이다.
기존에 코드의 성능 및 동작여부를 테스트하면 UserDaoTest가 갑자기 어떤 ConnectionMaker 구현 클래스를 결정해하는 책임을 엉겁결에 떠맡았다.
즉, 관계설정을 결정해하는 책임을 담당할 클래스를 생성해야한다. 이 클래스의 역할은 객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 것인데, 이런 일을 하는 오브젝트를 흔히 팩토리라고 부른다.
DaoFactory를 생성하여 기존에 UserDaoTest가 책임지고 있었던 관계 설정 부분을 옮기자

package springbook.user.dao;

import springbook.util.ConnectionMaker;
import springbook.util.impl.DConnectionMaker;

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 springbook.util.ConnectionMaker;
import springbook.util.impl.DConnectionMaker;

import java.sql.SQLException;

public class UserDaoTest {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        UserDao dao = new DaoFactory().userDao();

        User user = new User();
        user.setId("whiteship");
        user.setName("test");
        user.setPassword("1234");

        dao.add(user);

        System.out.println(user.getId() + "등록성공");

        User user2 = dao.get(user.getId());
        System.out.println(user2.g etName());
        System.out.println(user2.getPassword());

        System.out.println(user2.getId() + "조회성공");
    }
}

아키텍쳐 분석

이렇게 분리된 오브젝트들의 역할과 관계를 분석해보자. UserDao와 ConnectionMaker는 각각 애플리케이션의 핵심적인 데이터 로직과 기술 로직을 담당하고 있고, DaoFactory는 이런 애플리케이션의 오브젝트들을 구성하고 그 관계를 정의하는 책임을 맡고 있다. 즉, DaoFactory는 애플리케이션을 구성하는 컴포넌트(UserDao, ConnectionMaker같은)의 구조와 관계를 정의한 설계도 같은 역할을 한다.

오브젝트 팩토리 활용

DaoFactory를 좀 더 활용해보자. DaoFactory에 UserDao가 아닌 다른 DAO 생성 기능을 넣으면 어떻게 될까? AccountDao, MessageDao등을 만들었다고 해보자. 이 경우에 UserDao를 생성하는 userDao() 메서드를 복사해서 accountDao(), messageDao() 메서드로 만든다면 어떨까? ConnectionMaker 구현 클래스의 오브젝트를 생성한 코드가 메서드마다 반복될 것이다.

public class DaoFactory {
    public UserDao userDao() {
        ConnectionMaker connectionMaker = new DConnectionMaker();
        UserDao userDao = new UserDao(connectionMaker);

        return userDao;
    }
    public AccountDao accountDao() {
        ConnectionMaker connectionMaker = new DConnectionMaker(); // 구현 클래스 중복
        AccountDao accountDao = new AccountDao(connectionMaker);

        return accountDao;
    }

     public MessageDao messageDao() {
        ConnectionMaker connectionMaker = new DConnectionMaker(); // 구현 클래스 중복
        MessageDao messageDao = new MessageDao(connectionMaker);

        return messageDao;
    }


}

위 코드를 보면 세 개의 DAO를 만드는 팩토리 메서드 안에 모두 new DConnectionMaker라는 ConnectionMaker 구현 클래스의 인스턴스를 만드는 부분이 반복해서 나타난다. 이렇게 오브젝트 생성 코드가 중복되는 건 좋지 않은 현상이다. DAO가 더 많아지면 ConnectionMaker의 구현 클래스를 바꿀 때 마다 모든 메서드를 일일이 수정해야 하기 때문이다.
메서드 추출 리펙터링 기법을 이용하여 Connection 생성 부분을 추출하자.

package springbook.user.dao;

import springbook.util.ConnectionMaker;
import springbook.util.impl.DConnectionMaker;

public class DaoFactory {
    public UserDao userDao() {
        return new UserDao(connectionMaker());
    }

    public ConnectionMaker connectionMaker() {
        return new DConnectionMaker();
    }
}

제어관계 역전 IOC, Onversion of Control

제어의 역전이라는 건, 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 설명할 수 있다.
일반적으로 프로그램 흐름은 main() 메서드가 시작되는 지점에서 다음 사용할 오브젝트를 결정하고, 결정한 오브섹트에서 오브젝트를 생성하고, 메서드를 호출하고...식으로 반복된다.
이런 프로그램 구조에서 각 오브젝트는 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다. 초기 UserDao를 보면 위와 같이 모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고, 어떻게 만들지를 스스로 결정한다. 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조였다.
제어의 역전이란 이런 제어 흐름의 개념을 거꾸로 뒤집는 것이다. 제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지도 않고, 생성하지도 않고, 자신도 어떻게 만들어지고 어디서 사용되는지 알 수 없다. 모든 제어권한을 자신이 아닌 다른 대상에게 위임하기 떄문이다.
프레임워크도 제어의 역전 개념이 적용된 대표적인 기술이다. 프레인워크는 단순히 라이브러리의 다른 이름이 아니다. 라이브러리는 애플리케이션 흐름을 직접 제어하는 반면, 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 애플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작한다.

위에 우리가 만든 UserDao와 DaoFactory에도 제어의 역전이 적용되어있다. 원래 ConnectionMaker의 구현클래스를 결정하고 오브젝트를 만드는 제어권은 UserDao에게 있었다. 그러나 지금은 DaoFactory에게 있다. 바로 이것이 제어의 역전(IOC) 이 일어난 상황이다. 자연스럽게 관심을 분리하고 책임을 나누고 유연하게 확장 가능한 구조로 만들기 위해 DaoFactory를 도입했던 과정이 IOC를 적용한 작업이었다고 볼 수 있다.

728x90