2023.09.13 - [DEV book] - [토비의 스프링 3.1] 1장 오브젝트와 의존관계 (1) - 전략 패턴을 이용한 DAO 클래스 리팩토링
이전 포스팅에서 DAO를 리팩토링하는 과정에서 다양한 디자인 패턴, 객체지향 설계 등을 고려해 최적의 DAO 클래스를 만들어보는 실습을 하였다.
이번 포스팅에서는, 여기에서 사용된 제어의 역전(IoC)의 개념에 대해 배워보겠다.
오브젝트 팩토리
이전 포스팅에서, UserDaoTest 가 클라이언트라는 명목으로 UserDao에서 ConnectionMaker 인터페이스의 구체 클래스 중 하나를 선택하는 업무와, 두 오브젝트를 연결하는 업무를 떠맡았다.
이 두 가지 업무를 분리해보자.
팩토리
객체의 생성 방법을 결정하고, 만들어진 오브젝트를 반환하는 팩토리 클래스를 만들어보자. 오브젝트 생성과 사용하는 두 가지 역할을 분리하기 위한 목적이다.
이 팩토리 클래스를 사용해서 UserDaoTest 코드를 수정해보자.
팩토리 클래스는 Client - UserDao - Interface의 로직 흐름(컴포넌트) 에서 두 가지 이상의 오브젝트를 연결한다는 의미에서 하나의 설계도와 같은 역할을 한다.
여전히 ConnectionMaker 구현 클래스를 새로 선택해야 한다면, DaoFactory 코드를 수정해야하지만, 컴포넌트와 설계도를 분리했다는 것에 의의가 있다.
다양한 DAO의 도입에 대한 팩토리의 활용
DAO가 여러 개 늘어날 때마다 같은 코드가 반복되는 현상이 DaoFactory에서 발생한다.
이런 중복 코드를 분리하기 위해 getConnection() 함수를 뽑아낸 것처럼, DAO를 생성하는 통일된 함수를 만들 수 있다.
public class DaoFactory {
public UserDao userDao(){
return new UserDao(connectionMaker());
}
public AccountDao accountDao(){
return new UserDao(connectionMaker());
}
public MessageDao messageDao(){
return new UserDao(connectionMaker());
}
public ConnectionMaker connectionMaker(){
return new DConnectionMaker();
}
}
그래서 제어의 역전이 뭔데 ?
말 그대로, 프로그램의 제어 흐름 구조가 뒤바뀌는 것이다.
일반적으로 main() 함수에서 시작하여 생성된 오브젝트가 자신이 사용할 오브젝트를 선택, 호출하면서 프로그램이 진행된다. 그러나 제어의 역전 개념에서는 오브젝트가 다른 오브젝트에 의해 제어된다. 제어의 역전의 개념이 적용된 예시를 알아보자.
(1) 서블릿
서블릿에 대한 제어 권한을 가진 컨테이너가 서블릿 오브젝트를 만들고 메소드를 호출한다.
(2) 템플릿 메소드 패턴
추상 클래스인 UserDao를 상속한 서브 클래스는 getConnection() 추상 메소드를 구현하였다. 그러나 이 메소드는 UserDao의 템플릿 메소드인 add(), get()에서 필요할 때마다 꺼내 사용된다. 즉, 제어권이 상위 템플릿 메소드에게 있으며 자신은 픨요할 때마다 호출되는 제어의 역전 개념이 적용된 패턴이다.
(3) 프레임워크
애플리케이션의 흐름을 직접 제어하는 라이브러리와 다르게, 프레임워크는 흐름을 주도하는 도중 개발자가 만든 애플리케이션 코드를 사용하는 방식이다. 따라서 애플리케이션 코드는 프레임워크의 틀 내에서 수동적으로 동작하므로, 제어의 역전이라고 볼 수 있다.
앞에서 작성한 코드를 토대로 제어의 역전의 개념을 적용 전/후를 살펴볼 수 있다.
결국 DaoFactory를 도입한 과정으로 인해 구현 클래스를 결정하고, UserDao와 ConnectionMaker의 오브젝트가 생성되고, 두 오브젝트를 연결하는 제어의 역전이 만들어진 것이다.
스프링에서 제공하는 IoC
스프링에는 빈 팩토리와 애플리케이션 컨텍스트라는 개념이 있는데, DaoFactory가 하는 일을 조금 더 일반화한 것이다.
오브젝트 팩토리를 이용한 스프링 IoC
스프링에서 빈(bean)이라는 개념이 있다. 스프링이 제어권을 가지고 직접 생성하고 관계를 부여하는 오브젝트를 의미한다. UserDao, ConnectionMaker는 DaoFactory가 제어권이 있는 것이지 스프링이 제어권을 가지고 있는 것이 아니기 때문에 빈에 해당하지는 않는다. (뒤에서 DaoFactory를 ApplicationContext에 등록하고 나면 DaoFactory가 생성하는 두 오브젝트는 '빈'이라고 할 수 있다.)
이러한 빈의 생성과 관계 설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리 (Bean Factory) 라고 한다. 이를 확장한 개념이 애플리케이션 컨텍스트 (Application Context)이고, 위에서는 DaoFactory에 해당한다.
앞서서 DaoFactory가 컴포넌트 (클라이언트 - 컨텍스트 - 인터페이스) 의 설계도와 같다고 하였는데, 이것이 애플리케이션 컨텍스트와 그 설정 정보를 의미한다.
DaoFactory를 사용하는 Application Context를 만들기
DaoFactory를 빈 팩토리가 사용할 수 있는 설정 정보로 만들기 위해서는,
(1) @Configuration 애노테이션으로 오브젝트 설정을 담당하는 클래스임을 인식
(2) 오브젝트를 생성하는 메서드에는 @Bean 애노테이션을 붙인다.
userDao 라고 메소드 이름을 작성하였는데, 이것이 곧 빈의 이름이 된다.
@Configuration이 붙은 자바 코드를 설정 정보로 사용하려면 AnnotationConfigApplicationContext를 사용하면 된다. DaoFactory를 설정정보로 사용하는 애플리케이션 컨텍스트를 만들어 보자.
등록한 빈 이름으로 ApplicationContext에서 오브젝트를 요청한다.
이렇게 내가 만든 팩토리 클래스를 AppicationContext에 등록하고 빈을 생성하는 데에 사용할 수 있다.
ApplicationContext의 동작 방식
ApplicationContext는 DaoFactory와는 달리, 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없이 별도의 설정정보를 통해 얻는다.
위 사진을 보면, ApplicationContext는 DaoFactory를 설정 정보로 등록해둔 뒤, 메소드 이름을 가져와 빈 목록을 만들어둔다. 클라이언트가 getBean() 함수를 이용해 빈을 반환할 것을 요청하면, AppilcationContext는 빈 목록에서 빈을 찾고, 오브젝트를 생성하여 클라이언트에게 돌려준다.
이러한 흐름은 IoC의 원리를 그대로 적용한 방식이다. ApplicationContext를 사용하였을 때, 그렇지 않았을 때와 비교해 얻을 수 있는 장점을 정리해보자.
(1) 클라이언트가 구체적인 팩토리 클래스(DaoFactory .. )를 알 필요가 없다.
(2) 빈을 검색하는 다양한 방법 (타입 검색, 빈 이름 검색 .. )을 사용할 수 있다.
'DEV book > 토비의 스프링 3.1' 카테고리의 다른 글
[토비의 스프링 3.1] 3장 템플릿 (2) - JdbcContext를 UserDao에서 사용하는 두 가지 방법 (0) | 2023.09.24 |
---|---|
[토비의 스프링 3.1] 3장 템플릿 (1) - 디자인 패턴과 DI를 이용한 DAO 최적화 (0) | 2023.09.23 |
[토비의 스프링 3.1] 2장 테스트 - 단위 테스트와 테스트 코드 개선 (0) | 2023.09.20 |
[토비의 스프링 3.1] 1장 오브젝트와 의존관계 (3) - 싱글톤 레지스트리와 의존관계 주입 (0) | 2023.09.14 |
[토비의 스프링 3.1] 1장 오브젝트와 의존관계 (1) - 전략 패턴을 이용한 DAO 클래스 리팩토링 (0) | 2023.09.13 |