제어의 역전(IoC) & 스프링 IoC
23 Dec 2020 | Spring 스터디 객체지향 IoC 스프링 IoC 제어의 역전책 토비의 스프링 3.1의 1장 오브젝트와 의존관계를 정리한 내용입니다.
Index
- Review
- 오브젝트 팩토리 - IoC 설명을 위한 마지막 개선
- 제어의 역전(IoC)
- 일반적인 프로그램의 흐름
- 제어의 역전
- 스프링 IoC
- ApplicationContext와 설정 정보
- ApplicationContext의 동작 방식
- 용어 정리
Review
필요한 작업을 최소화하여 변경과 확장에 유연한 구조를 만들기 위해, 우리는 관심사 분리라는 작업을 해왔다.
여기서 관심사란, 하나의 객체가 수행해야하는 기능/역할을 의미하며, 객체가 하나의 책임/관심사에만 집중할 수 있도록 관심사가 같은 것끼리는 모아두고, 다른 것과는 멀리 떨어뜨려놓는 작업을 관심사의 분리라 한다. => 높은 응집도와 낮은 결합도
책에서는 UserDao의 ‘DB 연결 방법’에 대한 관심을 외부로 분리시켜 UserDao가 하나의 관심에만 집중할 수 있도록 다음 3가지의 과정을 거쳐 개선해나갔다.
- 중복 코드를 메소드로 분리
- 상속을 통한 분리 - 변경 가능한 기능은 각 서브 클래스에서 구현
- 인터페이스를 통한 분리 - 변경 가능한 기능은 인터페이스로 추상화하여 인터페이스 타입으로 생성자 파라미터를 통해 외부에서 전달받게함(다형성)
UserDao가 어떤 DB 연결 방법(ConnectionMaker 구현 클래스)을 사용할지 결정하는 책임(관계 설정)을 외부로 위임시킴으로써 관심사를 완전히 분리시켰다.
오브젝트 팩토리 - IoC 설명을 위한 마지막 개선
이렇게 객체의 생성 방법을 결정해서 생성된 오브젝트를 전달하는 역할을 하는 오브젝트를 팩토리(Factory)라 부른다. 디자인 패턴의 추상 팩토리 패턴이나 팩토리 메소드 패턴과는 다르니 혼동하지 말자.
/* Factory는 UserDao를 어떻게 만들지에만 관심을 갖는다 */
public class DaoFactory {
public UserDao userDao() {
UserDao dao = new UserDao(connectionMaker());
return dao;
}
public ConnectionMaker connectionMaker() {
ConnectionMaker connectionMaker = new NConnectionMaker();
return connectionMaker;
}
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
/* UserDaoTest는 UserDao가 어떻게 만들어지는 신경쓰지 않는다. UserDao를 활용해서 테스트만 할 수 있으면 되니까 */
DaoFactory factory = new DaoFactory();
UserDao dao = factory.userDao();
……
}
팩토리 오브젝트를 사용함으로써, UserDaoTest는 UserDao가 어떻게 만들어지는 신경 쓰지 않고 팩토리로부터 필요한 오브젝트를 받아 자신의 관심사인 테스트에만 집중할 수 있게 된다.
이는 마치 client(앱)에서 API를 호출할 때, 서버에서 Data를 어떻게 만들어 내려주는지 전혀 신경 쓰지 않아도 되는 것과 같다. 클라이언트에서는 화면 구성에 필요한 데이터만 가져오면(클라이언트 자신의 관심사)되기 때문. 따라서 서버와 클라이언트의 관심사가 분리되어 있으니 서버 코드가 변경되었다고 해서 클라이언트 코드도 변경해줄 필요가 없게 된다. 물론 API 명세 변경 없이 서버 내부 로직만 변경하면 된다는 가정하에. 역으로도 성립한다. 클라이언트 코드가 바뀌었다해서 서버로직도 같이 변경해줄 필요는 없게된다. 서버도 본인이 내려준 데이터가 어떻게 사용되고 있는지 신경 쓸 필요가 없다.
설계도로서의 팩토리
실질적인 로직을 담당하는게 컴포넌트라면, 각 컴포넌트의 생성과 의존관계 - 어떤 오브젝트가 어떤 오브젝트를 사용하는지 정의해놓은 코드 - 즉 애플리케이션의 구조를 결정하는 설계도가 Factory다.
이제 N사와 D사에 UserDao를 공급할 때 UserDao, ConnectionMaker와 함께 DaoFactory도 제공하면 된다. UserDao와 달리 DaoFactory는 소스를 제공한다. 새 ConnectionMaker 구현 클래스로 변경시 없이 각 사에서는 DaoFactory 코드만 수정해서 사용하면 된다. 그 결과 우리의 핵심 기술이 담긴 UserDao는 안전하게 코드를 보존할 수 있으며 동시에 DB 연결 방식은 자유로운 확장이 가능해진다.
제어의 역전 (IoC)
일반적인 프로그램의 흐름
main() 메소드 처럼 프로그램이 시작되는 시점에서
능동적으로 자신이 사용할 클래스를 결정하고, 객체를 직접 생성하고, 그 객체와 메소드를 언제 어떻게 사용할지 모든 작업을 스스로 관장/제어한다. 흐름의 주도권이 사용하는 쪽에 있다.
제어의 역전이란 이런 제어의 흐름을 거꾸로 뒤집는 것
제어의 역전(Inversion of Control)
자신이 사용할 오브젝트를 스스로 택하지 않고 생성하지도 않는다. 객체와 관련된 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다. 흐름의 주도권이 외부에 있다.
제어의 역전 개념은 이미 폭넓게 사용되고 있다.
대표적인 예가 서블릿이다. 서블릿의 실행을 개발자가 직접 제어할 수 없다. 서블릿에 대한 제어 권한은 컨테이너에 있으며, 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출한다. 또 다른 예로 디자인 패턴의 템플릿 메소드이다. 추상 UserDao를 상속한 서브클래스는 getConnection()을 구현한다. 하지만 이 메소드가 언제 어떻게 사용될지 자신은 모른다. 서브클래스에서 결정되는 것이 아니다. 제어권은 슈퍼클래스 상위 템플릿 메소드 - add(), get()등에서 필요할 때 사용 - 에 있고 자신은 필요할 때 호출되어 사용되도록 한다.
프레임워크 vs 라이브러리
마지막 예로 프레임워크이다. 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다. 동작 중에 필요한 기능이 있을 때마다 능동적으로 라이브러리를 사용한다. 하지만, 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다.프레임워크 위에 개발한 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다.
대표적인 IoC 프레임워크가 바로 스프링이다. IoC는 프레임워크만의 기술도 아니고 프레임워크가 꼭 필요한 개념도 아니다. 폭넓게 사용되는 프로그래밍 모델일 뿐이다. IoC를 적용하면 설계가 깔끔해지고 확장성이 좋아지기 때문에 앞에서 우리가 해온 것처럼 직접 IoC 스타일의 설계와 코드를 만들어 사용해도 된다.
제어의 역전에서는 프레임워크 또는 컨테이너와 같이 애플리케이션 컴포넌트의 라이프 사이클 - 생성, 관계 설정, 사용, 생명주기 관리-을 관장하는 존재가 필요하다. DaoFactory를 오브젝트 수준의 가장 단순한 IoC 컨테이너 내지는 IoC 프레임워크라고 부를 수 있지만, IoC를 애플리케이션 전반에 걸쳐 적용한다면 스프링과 같은 IoC 프레임워크의 도움을 받는게 더 유리하다. 스프링은 IoC를 모든 기능의 기초가 되는 기반 기술로 삼고 있기 때문이다.
이제 본격적으로 스프링이 제공하는 IoC에 대해 알아보자.
스프링 IoC
스프링은 BeanFactory와 ApplicationContext로, IoC 컨테이너를 제공한다. ApplicationContext는 BeanFactory의 서브타입으로 오브젝트에 대한 생성과 관계 설정을 담당하며 AOP 기능, 메시지 리소스 핸들링 등 스프링 부가 서비스를 추가로 제공한다.
애플리케이션 컨텍스트는 DaoFactory와 달리 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없고, 그런 생성정보와 관계정보를 별도의 설정정보를 통해 얻는다. 설정정보는 다양한 방식으로 작성이 가능하다.(XML 파일, Java 파일, 프로퍼티 파일 등)
ApplicationContext와 설정 정보
설정정보는 어노테이션을 이용해 만든다. 이제 빈 팩토리가 DaoFactory를 설정 정보로 사용할 수 있도록 만들어보자. 여기서 빈이란 스프링이 제어권을 가지고 직접 관리하는 오브젝트를 의미한다.
import.org.springframework.context.annotation.Bean;
import.org.springframework.context.annotation.Configuration;
...
@Configuration -> 컨텍스트/빈팩토리가 사용하게될 설정 클래스임을 표시
public class DaoFactory {
@Bean -> 스프링의 관리 대상이 될 객체 생성 메소드임을 표시
public UserDao userDao() {
return new UserDao(connectionMaker());
}
@Bean -> 메소드 이름이 빈 이름이 된다.
public ConnectionMaker connectionMaker() {
return new DConnectionMaker();
}
}
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// DaoFactory 클래스에 정의된 객체들을 컨테이너(외부)에서 관리하겠다.
ApplicationContext contxt =
new AnnotaionConfigApplicationContext(DaoFactory.class);
UserDao dao = context.getBean("userDao", UserDao.class);
}
}
getBean() 메소드는 Application Context에 등록된 빈 객체를 요청하는 메소드이다. 위 코드에서 첫번째 인자인 “UserDao”는 컨텍스트에 등록된 빈의 이름이며, 두번째 인자는 반환될 빈의 타입이다. 메소드 이름을 사용하는 이유는, 동일한 객체를 생성하는 방식이나 구성을 다르게 해서 가져갈 수 있기 때문에 그것들의 구분을 위해 메소드 이름을 사용한다.
ApplicationContext의 동작 방식
어플리케이션 컨텍스트는 먼저 @Configuration 어노테이션이 붙은 클래스(DaoFactory)를 설정정보로 등록해두고, @Bean이 붙은 메소드들의 이름을 가져와 빈 목록을 만들어둔다. 클라이언트가 getBean() 메소드를 호출하면 그때 애플리케이션 컨텍스트가 요청한 이름으로 빈을 검색하고, 빈이 있다면 빈을 생성하는 메소드를 호출해 객체를 생성 및 초기화한 후 반환한다.
용어 정리
빈(Bean)
스프링이 IoC방식으로 관리하는 오브젝트. 애플리케이션 컨텍스트가 직접 만들고 관계를 부여하는 오브젝트이다.
빈 팩토리(Bean Factory)
스프링 IoC를 담당하는 핵심 컨테이너. 빈 등록/생성/검색에 대한 기능을 정의한 인터페이스이다. 생성된 객체를 검색하는 getBean() 메소드와 싱글톤/프로토타입 빈인지 확인하는 기능 제공. 보통 빈 팩토리를 바로 사용하지 않고 이를 확장한 애플리케이션 컨텍스트를 이용한다.
애플리케이션 컨텍스트(Application Context)
빈 팩토리를 확장한 IoC 컨테이너. 빈 팩토리가 제공해주는 기능 + 스프링이 제공하는 각종 부가 서비스(AOP 기능, 메시지 리소스 핸들링, 이벤트 발생, 프로필/환경변수 처리 기능 등)를 제공. 빈 팩토리는 빈을 생성과 제어의 관점(IoC 기본 기능)에서 본다면, 애플리케이션 컨텍스트는 애플리케이션 지원 기능을 모두 포함해서 본다. ApplicationContext 인터페이스를 구현한 오브젝트를 애플리케이션 컨텍스트라 부르기도 하며, 다음은 BeanFactory와 ApplicationContext를 구현한 클래스들
- AnnotationConfigApplicationContext: 자바 어노테이션을 이용해 객체 설정 정보를 가져온다.
- GenericXmlApplicationContext: XML로 부터 객체 설정 정보를 가져온다.
- GenericGroovyApplicationContext: 그루비 코드를 이용해 설정 정보를 가져온다.
설정 정보 (Configuration)
IoC 컨테이너에 의해 관리될 빈의 정보(생성, 의존관계 등)이 정의되어 있다.
컨테이너 또는 IoC 컨테이너
IoC 방식으로 빈을 관리한다는 의미에서 애플리케이션 컨텍스트와 빈 팩토리를 컨테이너 or IoC 컨테이너 or 스프링이라고 부른다. 애플리케이션 컨텍스트 오브젝트는 보통 하나의 애플리케이션에서 여러개가 만들어져 사용된다. 이를 통틀어 스프링 컨테이너라고도 부른다.