IOC(Inversion of Control)란

Inversion of Control의 약자로써 제어의 역전이라고도 합니다.

보통 개발자가 코드를 작성하여 구현된 객체가 프로그램의 제어 흐름을 스스로 컨트롤하는것이 기본적이지만 이것을 외부에서 관리하는것을 제어의 역전이라고 합니다.

즉 개발자가 객체를 생성하는것이 아니라 컨테이너에서 가져오는것입니다.

Spring에서는 BeanFactory, ApplicationContext가 이러한 역할을 하게 됩니다.

스프링은 스프링 컨테이너를 통해 객체를 관리하는데, 스프링 컨테이너에서 관리되는 객체를 Bean이라고 합니다.

DI(Dependecy Injection)란?

DI(Dependecy Injection) : 의존관계, 의존성 주입이란 뜻을 가지고 있습니다.

스프링은 DI로 다형성, OCP등을 가능하게 지원하여 유연하고 결합도를 낮출수 있게 합니다.

  • OCP?

    Open Close Principle : 개방 폐쇄의 원칙

    • 시간이 지나도 유지보수와 확장이 쉬운 시스템을 만들고자 로버트 마틴이 명명한 객체 지향 설계 5대 원칙 SOLID중 하나입니다.
    • OCP는 소프트웨어 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에 대해서는 개방(OPEN)되어야 하지만 변경에 대해서는 폐쇄적(CLOSE)이여야 한다는 의미 입니다. 즉 기존의 코드를 변경하지 않으면서 기능을 추가할수 있도록 설계가 되어야 한다는 의미입니다. (상속, 컴포지션)

Spring에서는 인스턴스를 생성하는 코드는 보이지 않고 어노테이션을 사용하여 컨테이너에게 맡기면서 사용합니다.

Spring Container(IOC Container)

스프링에서의 컨테이너는 객체 생성 , 관계설정등의 작업을 담당합니다. 이렇게 컨테이너가 코드 대신 객체에 대한 제어권을 가지고 있어 IOC 컨테이너라고도 부릅니다.

스프링 컨테이너는 단순 DI 작업보다 더 많은일을 하는데 DI을 위한 BeanFactory 인터페이스에 여러가지 기능을 추가하여 ApplicationContext 인터페이스를 정의하였습니다.

Bean Factory

객체를 생성하고, 객체사이의 런타임 의존관계를 맺어주는 역활을 하는 스프링 컨테이너의 최상위 인터페이스입니다.

ApplicationContext

SpringContainer1.png

BeanFactory를 포함한 여러 인터페이스를 상속받은 인터페이스로, 스프링 컨테이너라고 하면 기본적으로 ApplicationContext를 의미합니다. BeanFactory를 상속받았기에 BeanFactory와 마찬가지로 객체를 생성하며 객체 사이의 런타임 의존관계를 맺어주는 역활 뿐만 아니라 메세지 국제화, 환경변수등 다양한 기능을 제공합니다.

  • MessageSource : 메세지소스를 활용한 국제화 기능 (한국어,영어 등)
  • EnvironmentCapable : 환경변수 (로컬,개발,운영등을 구분해서 처리)
  • ApplicationEventPublisher : 이벤트를 발행하고 구독하는 모델을 편리하게 지원
  • ResourceLoader : 파일, 클래스패스, 외부 등 리소스를 편리하게 조회

BeanFactory를 직접 사용하는 일은 거의 없으며, 부가 기능이 포함된 ApplicationContext를 사용합니다.

IOC 컨테이너를 동작시키기 위해서는 크게 2가지가 필요한데 하나는 POJO 클래스이며 다른 하나는 설정 메타 정보입니다.

SpringContainer2.png

  • POJO 클래스

    POJO 클래스는 Plain Old Java Object, 단순한 자바 오브젝트의 줄임말로써 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용 될수 있는 방식으로 설계된 오브젝트를 의미합니다.

    이러한 오브젝트를 결합도가 낮은 유연한 관계를 가질수 있도록 인터페이스를 이용해 연결해줍니다.

  • 설정 메타 정보

    POJO 클래스들중에 애플리케이션에서 사용 할것을 선정하고 이를 IOC 컨테이너가 제어할수 있도록 적절한 메타 정보를 만들어 제공하는 작업입니다.

    SpringContainer3.png

    스프링의 설정 메타 정보는 BeanDefinition 인터페이스로 표현되는 순수한 추상 정보로써 스프링 IOC 컨테이너는 바로 이 BeanDefinition 인터페이스로 만들어진 메타정보를 담은 오브젝트를 사용하여 IOC와 DI 작업을 수행하게됩니다.

    BeanDefinition 인터페이스로 정의되는 IOC 컨테이너가 사용하는 빈 메타정보는 아래와 같습니다.

    • 빈 아이디, 이름 , 별칭 : 빈 오브젝트를 구분할수 있는 식별자
    • 클래스 또는 클래스 이름 : 빈으로 만들 POJO 클래스 또는 서비스 클래스 정보
    • Scope : 싱글톤, 프로토 타입과 같은 빈의 생성 방식과 존재 범위 *
    • 프로퍼티 값 또는 참조 : DI에 사용될 프로퍼티 이름과 값 또는 참조하는 빈의 이름
    • 생성자 파라미터 값 또는 참조 : DI에 사용할 생성자 파라미터 이름과 값 또는 참조할 빈의 이름

애플리케이션을 구성하는 빈 오브젝트를 생성하는것이 IOC 컨테이너의 핵심 기능이며 IOC 컨테이너는 일단 빈 오브젝트가 생성되고 관계가 만들어지면 그뒤로는 거의 관여를 하지 않습니다.

스프링 IOC 컨테이너는 다양한 형식의 설정 정보를 받아들일수 있게 유연하게 설계되었습니다. 자바 코드, XML, Groovy등

SpringContainer4.png

  • 어노테이션 기반 자바 코드 사용
  • XML 설정 사용

싱글톤 컨테이너

싱글톤 패턴이 없는 DI 컨테이너에서 AppConfig는 요청 시마다 새 객체를 생성하게 된다.

요청이 많아질수록 객체 생성 소멸이 많아져 메모리 낭비가 심해진다.

필요한 객체는 딱 한 개만 생성하고 공유하도록 설계한 것이 싱글톤 패턴이다.

싱글톤 패턴

클래스의 인스턴스가 딱 한 개 생성되는 것을 디자인 패턴이다.

private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하게 한다.

자원을 효율적으로 사용하고 메모리 낭비도 줄일 수 있다.

그러나 싱글톤 패턴의 문제점도 존재하는데 싱글톤 패턴 구현하는 코드 자체가 많이 들어간다.

클라이언트가 구체 클래스에 의존 - DIP 위반, OCP 위반할 가능성

테스트 어렵다. 내부 속성 변경, 초기화가 어렵다. private으로 자식 클래스를 만들기 어려워 유연성이 떨어진다.

싱글톤 컨테이너

스프링은 이러한 싱글톤 패턴의 단점을 해결하고 유연하게 관리하게 해준다.

그러나 싱글톤은 여러 클라이언트가 같은 인스턴스를 공유하기 때문에 Stateless하게 설계해야 한다. 의존적인 필드가 있으면 안되고, 값을 변경할 수 없어야 한다. 읽기만 가능해야 한다.

Reference