[Spring] IoC와 Bean

정은영·2022년 12월 13일
0

CS

목록 보기
15/18

Spring은 경량 컨테이너로 자바 객체를 직접 Spring 안에서 관리합니다.
객체의 생성 및 소멸과 같은 생명 주기(Life Cycle)을 관리하며, Spring 컨테이너에서 필요한 객체를 가져와 사용합니다.

  • 우리가 알던 기존의 Java Programming에서는 Class를 생성하고 new를 입력하여 원하는 객체를 직접 생성한 후에 사용했었습니다.
  • 하지만 Spring에서는 직접 new를 이용하여 생성한 객체가 아니라 Spring에 의하여 관리당하는 자바 객체(Bean)를 사용합니다.
  • Spring Framework에서는 Spring Bean을 얻기 위하여 ApplicationContext.getBean()와 같은 메소드를 사용하여 Spring에서 직접 자바객체를 얻어서 사용합니다.

  • 위 그림은 Spring의 구조인데 Beans(스프링 IoC 컨테이너가 관리하는 객체)가 포함되어 있는 것을 볼 수 있습니다.

IoC(Inversion of Control)

일반적인 의존성에 대한 제어권 : 개발자가 직접 의존성을 만든다.

의존성은 쉽게 말해서 어떤 객체가 사용해야 할 객체라고 할 수 있습니다.

직접 new 등을 써서 만들어 쓰면 의존성을 내가 직접 만들어 쓴다고 할 수 있습니다.

이 때 각 객체들이 프로그램의 흐름을 결정하고 각 객체를 직접 생성하고 조작하는 작업(객체를 직접 생성하여 메소드 호출)을 사용자가 하게 됩니다.

  • 예를 들어 A객체에서 B객체에 있는 메소드를 사용하고 싶으면, B객체를 직접 A객체 내에서 생성하고 메소드를 호출합니다.
public class OwnerController {
	private OwnerRepository ownerRepository = new OwnerRepository();
}
  • 위 코드는 OwnerController가 필요한 OwnerRepository의 객체를 직접 생성하는 경우 입니다.

IoC(Inversion of Control) : 제어의 역전

위처럼 개발자가 직접 의존성을 만들지 않고, 외부에서 의존성을 가져오는 것을 말합니다.

밖에서 나에게 의존성을 주입해주는 것을 DI(Dependency Injection)이라고 하는데 DI를 통해서 IoC가 적용된다고 생각하면 됩니다.

DI(Dependency Injection) : 의존성 주입
어떤 객체(B)를 사용하는 주체(A)가 객체(B)를 직접 생성하는 것이 아니라 객체를 외부(Spring)에서 생성해서 사용하려는 주체 객체(A)에게 주입시켜주는 방식입니다. 사용하는 주체(A)가 사용하려는 객체(B)를 직접 생성하는 경우 의존성이 높아집니다. 하지만 외부(Spring)에서 직접 생성하여 관리하는 경우에는 A와 B의 의존성이 줄어듭니다.

  • 의존성이 높다: 변경사항이 있는 경우 서로에게 영향을 많이 준다.

이렇게 IoC가 적용된 경우, 사용자는 객체를 직접 생성하지 않고, 객체의 생명주기를 컨트롤 하는 주체는 다른 주체가 됩니다. 즉, 사용자의 제어권을 다른 주체에게 넘기는 것을 IoC(제어의 역전)이라고 합니다.

요약하면 Spring IoC란 클래스 내부의 객체 생성 → 의존성 객체의 메소드 호출 이 아닌 스프링에게 제어를 위임하여 스프링이 만든 객체를 주입 → 의존성 객체의 메소드 호출 구조 입니다.

스프링에서는 모든 의존성 객체를 스프링이 실행될 때 만들어주고 필요한 곳에 주입해줍니다.

public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
		this.owners = clinicService;
		this.visits = visits;
}
  • 위 코드에서는 OwnerController의 생성자에서 OwnerRepository를 인자로 받고, owners에 담고 있습니다.
  • 이는 앞의 예시처럼 객체를 직접 생성하지 않고, 외부의 객체를 받고 있는 것을 볼 수 있습니다.

Bean과 스프링 IoC 컨테이너

스프링 IoC 컨테이너가 관리하는 객체들을 Bean이라고 부릅니다. 스프링은 이러한 Bean들의 의존성을 관리하고, 객체를 만들어주며, Bean으로 등록을 해주고, 만들어진 Bean들을 관리합니다.

위 관리는 스프링 IoC 컨테이너가 알아서 해주게 됩니다. 개발자가 이 부분까지 신경 쓰지 않아도 프레임워크가 알아서 해주는 것입니다.

이러한 Bean들을 담고 있는 스프링 IoC 컨테이너는 두 가지 중 하나를 사용합니다.

  • ApplicationContext 혹은 BeanFactory
    • ApplicationContextBeanFactory를 상속받기 때문에 결국은 둘 다 같은 일을 한다고 할 수 있습니다.

이러한 스프링에서의 의존성 주입은 반드시 Bean으로 등록된 객체들끼리만 가능합니다. 스프링 IoC 컨테이너는 Bean으로 등록되지 않은 객체에는 의존성 주입을 해주지 않습니다.

  • IntelliJ 에서는 Bean 인 경우 아래 사진처럼 왼쪽에 아이콘으로 표시해 줍니다.

  • OwnerController 생성자에서 의존성 주입이 일어나고 있습니다. 이 때 의존성 주입을 하기 위해서는 OwnerControllerOwnerRepository는 모두 Bean이어야 합니다.

  • 둘 다 Bean으로 등록되어 있는 것을 확인할 수 있습니다.

Bean으로 등록하는 방법은?

특정 객체를 Bean으로 등록하는 방법으로는 무엇이 있는지 알아봅시다.

  • Component Scanning

  • Bean 설정 파일에 직접 빈을 등록하는 방법

Component Scanning

개발자가 직접 작성한 Class를 Bean으로 등록 해줄 때 사용합니다.

  • @Component Annotation을 활용합니다.

  • @Component 어노테이션이 붙어 있는 모든 클래스들을 찾아, 그 클래스의 인스턴스를 만들고, Bean으로 등록해주는 복잡한 작업을 하는 어노테이션 처리기가 스프링에 있습니다.

  • Pet Clinic 프로젝트에서 main 시작부분을 보면 @SpringBootApplication 어노테이션이 붙어 있습니다. 이 어노테이션을 정의한 곳으로 가보면

  • @ComponentScan 어노테이션이 붙어있음을 확인할 수 있습니다. @ComponentScan은 어느 지점부터 컴포넌트를 찾아보라고 알려주는 것인데, 이것을 사용한 SpringBootApplication 어노테이션이 PetClinicApplication 클래스에 붙어 있습니다.

  • 따라서 이 PetClinicApplication 클래스부터 하위 모든 패키지들을 훑어보면서 @Component 어노테이션을 활용한 모든 클래스들을 찾고 Bean으로 등록하게 됩니다.


  • 위에서 보았던 OwnerController를 살펴보도록 합시다. 위에서 Bean으로 등록되었다는 표시를 확인할 수 있었습니다. @Controller 어노테이션이 붙어 있는 것을 볼 수 있습니다.

  • @Controller를 정의한 곳을 보면 @Component 어노테이션이 붙어있음을 알 수 있습니다.
  • 따라서 스프링은 OwnerController@Component 어노테이션을 활용했으므로 이를 Bean으로 등록해준 것입니다.

Bean 설정파일에 직접 빈을 등록하는 방법

위와 같이 @Component 어노테이션을 사용하는 방법 말고도 빈 설정파일에 직접 빈으로 등록할 수 있습니다.

빈 설정파일은 XML과 자바 설정파일로 작성할 수 있는데 최근 추세는 자바 설정파일을 좀 더 많이 사용합니다.

  • 자바 설정파일은 자바 클래스를 생성해서 작성할 수 있으며 일반적으로 xxxConfigration과 같이 명명합니다.
@Configuration
public class SampleConfiguration {
    @Bean
    public SampleController sampleController() {
        return new SampleController;
    }
}
  • 클래스에 @Configuration을 붙여 자바 설정파일을 만듭니다.

  • 그리고 @Bean 어노테이션을 사용하여 직접 빈을 정의합니다.

  • sampleController()에서 리턴되는 객체가 IoC 컨테이너 안에 빈으로 등록됩니다.

  • 이렇게 빈을 직접 정의해서 등록하면 @Component 어노테이션을 붙이지 않아도 됩니다. @Configuration 어노테이션을 보면 이 어노테이션도 @Component를 사용하기 때문에 @ComponentScan의 스캔 대상이 되고 그에 따라 빈 설정파일이 읽힐 때 그 안에 정의한 빈들이 IoC 컨테이너에 등록되기 때문입니다.

왜 자바 객체를 IoC 컨테이너의 빈으로 만들까?

객체의 Scope

자바 객체를 IoC 컨테이너의 빈으로 만들면 객체의 scope를 관리하기 용이합니다.
IoC 컨테이너에서 관리되는 빈들은 기본적으로 싱클톤 scope로 등록되는데 어플리케이션 전반적으로 오로지 하나의 인스턴스만 사용해도 되는 객체라면 IoC 컨테이너에 빈으로 등록해서 편리하게 사용할 수 있습니다.
어플리케이션 전반에 하나의 인스턴스만 필요한 객체는 싱글톤 scope이 메모리 측면에서 효율적이며 성능 최적화에 유리합니다.

라이프 사이클 인터페이스 지원

라이프 사이클 인터페이스는 spring IoC 컨테이너에 등록된 빈들에만 국한되어 일반 자바 객체는 사용할 수 없습니다.
라이프 사이클 인터페이스를 이용해 어떤 빈이 만들어질 때 빈에 추가적인 작업을 할 수 있습니다.
예를 들어 클래스들이 모두 구현되지 않은 상태에서는 의존 객체를 사용하는 클래스의 단위 테스트를 만들기 어려운데 라이프 사이클 인터페이스를 사용하면 이러한 상황에서도 테스트를 수행할 수 있습니다.

Reference

0개의 댓글