슬픈 야옹이 2023. 11. 7. 14:39

이번 섹션부터 본격적으로 스프링을 사용해본다.

 

먼저 스프링 컨테이너부터 생성해보자.

 

 

스프링 컨테이너

스프링의 핵심은 스프링 컨테이너와 스프링 빈이라고 할 수 있다.

 

스프링 컨테이너란 스프링 빈을 담아놓고 관리하는 것이라는 개념이다.

 

조금 추상적인데, 개념적으로는 스프링 빈을 담아놓고 관리하는 환경,

 

구체적으로는 ApplicationContext 인터페이스와 그 구현체들을 말한다.

 

컨테이너라는 개념 자체가 스프링에서만 사용하는 것이 아니라, 사용할 객체들을 담아놓는 것으로 여기저기서 사용하는 개념이다.

 

 

 

 

스프링 컨테이너 생성

스프링 컨테이너는 다음과 같이 생성한다.

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

 

스프링 컨테이너 생성

 

ApplicationContext를 스프링 컨테이너라고 하며, ApplicationContext는 인터페이스다.

 

ApplicationContext의 구현체는 여러가지가 존재하며, 대표적으로 XML 기반, 자바 어노테이션 기반의 구현체가 있다.

 

 

AnnotationConfigApplicationContext

그 중 AnnotationConfigApplicationContext는 이름에서 알 수 있듯 어노테이션 기반의 스프링 컨테이너인데,

@Configuration 어노테이션이 붙은 자바 설정 클래스의 내용을 기반으로 스프링 컨테이너를 구성한다.

 

생성자에는 설정 클래스의 클래스 정보(.class)를 전달하여 생성한다.

예시 코드에서는 지금까지 작성했던 AppConfig의 클래스 정보를 전달한다.

즉 직전까지 AppConfig를 사용한 방식이 어노테이션 기반의 자바 설정 클래스로 스프링 컨테이너를 생성한 것이다.

https://debuggingworld.tistory.com/97

 

19. 스프링으로의 전환

이제, 지금까지 작성한 내용을 스프링 기반으로 전환해보자. AppConfig AppConfig 클래스에 @Configuration 어노테이션을, 각 메서드에는 @Bean 어노테이션을 붙인다. 어노테이션을 붙일 때 import되는 클래

debuggingworld.tistory.com

 

AppConfig 클래스를 보면 클래스에 @Configuration 어노테이션이 붙어있는 것을 확인할 수 있다.

@Configuration 어노테이션

 

 

 

스프링 컨테이너를 만드는 다른 방법으로 XML을 기반으로 하는 방식이 있으나, 최근에는 잘 사용하지 않는다.

어노테이션 기반의 방식이 워낙에 편리하기 때문이며, 스프링 부트도 기본적으로 어노테이션 방식을 지원한다.

 

 

 

+) BeanFactory와 ApplicationContext

정확히는 스프링 컨테이너는 BeanFactory와 ApplicationContext로 구분하지만, BeanFactory를 직접 사용하는 경우는 거의 없기 때문에 일반적으로 ApplicationContext를 스프링 컨테이너라 부른다.

 

 

 

스프링 컨테이너의 생성 과정

스프링 컨테이너의 생성 과정은 크게 스프링 컨테이너 생성 -> 빈 등록 -> 빈 의존관계 설정 순으로 진행된다.

 

1. 스프링 컨테이너 생성

new AnnotationConfigApplicationContext(AppConfig.class)를 통해 스프링 컨테이너가 생성된다.

 

컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다. (예시에서는 AppConfig.class)

 

스프링 컨테이너 생성 (강의자료 발췌)

 

 

생성된 스프링 컨테이너 내부에는 스프링 빈 저장소가 존재한다.

 

빈 저장소는 빈 이름과 빈 객체를 key-value 쌍으로 저장한다.

 

 

2. 스프링 빈 등록

스프링 컨테이너는 파라미터로 넘어온 설정 정보를 바탕으로 스프링 빈을 등록한다.

 

스프링 빈 등록 (강의자료 발췌)

 

 

AnnotationConfigApplicationContext (어노테이션 기반)의 경우, 클래스 내부의 @Bean 어노테이션이 붙은 메서드를 모두 한번씩 실행하여 리턴값을 저장한다.

 

이 때, 빈 이름은 해당 메서드의 이름, 빈 객체는 메서드가 리턴하는 객체로 등록된다.

즉 메서드 이름이 빈 이름으로서 key로 등록되는 것이다.

 

+) 빈 이름은 임의로 지정할 수도 있다.

@Bean(name="beanName2")

 

+)

빈 이름은 빈 저장소의 key이기 때문에, 중복되어선 안 된다.

 

빈 이름은 항상 서로 다른 이름을 부여해야 하며, 중복될 경우 다른 빈이 무시되거나 기존 빈을 덮어버리는 등의 문제가 생긴다. (이름이 중복되는 빈들이 어떻게 처리되는지 모두 안다면 어떻게든 처리할 수 있겠지만.. 굳이 그럴 이유가 없다. 중복되지 않게 이름을 지정하자.)

 

프로젝트 규모에 따라 빈이 많아지면서 빈 이름이 중복될 수도 있는데, 최신 버전의 스프링은 빈 이름이 중복될 경우 오류를 발생시킨다. (예전 버전은 오류도 없이 그냥 덮어씌웠다고 한다.)

 

 

3. 스프링 빈 의존관계 설정

마지막으로, 설정 정보를 바탕으로 스프링 빈 의존관계를 주입(DI)한다.

스프링 빈 의존관계 설정 (강의자료 발췌)

 

예시의 AppConfig처럼 자바 클래스를 설정 정보로 넘기면 코드가 실행되면서 자동으로 의존관계가 주입되는 것처럼 보인다.

 

memberService()를 호출할 때 자연스럽게 memberRepository()도 함께 호출되듯이 말이다.

 

즉 빈 등록과 의존관계 주입이라는 과정이 단순히 자바 메서드를 실행하는 것처럼 보이지만, 조금 다르다.

 

단순히 AppConfig의 메서드를 호출하면 memberRepository()가 두 번 호출되므로 서로 다른  두 개의 MemorymemberRepsitory 객체가 생성된다.

 

memberService와 orderService가 의존하는 memberRepository가 각각 서로 다른 객체라는건 뭔가 이상하다.

 

이러한 일을 방지하기 위해 스프링 빈은 기본적으로 싱글톤으로 관리되며,

빈을 등록하고 의존관계를 주입할 때, 단순히 메서드를 실행하는 것과는 다르게 동작한다.

 

자세한 내용은 싱글톤 컨테이너에서 다룬다.

 

 

 

정리

이번 포스트 내용을 정리하자면 다음과 같다.

 

스프링 컨테이너: 빈을 저장해놓고 관리하는 것. 실체는 ApplicationContext와 그 구현체. (+ BeanFactory)

 

AnnotationConfigApplicationContext는 자바 설정 클래스 기반의 스프링 컨테이너다.

 

 

스프링 컨테이너 생성과정

  1. 스프링 컨테이너 생성 (new AnnotationConfigApplicationContext(AppConfig.class))
    1. 스프링 컨테이너를 구성할 때는 구성 정보를 지정해주어야 함. (AppConfig.class)
  2. 스프링 빈 등록
    1.  파라미터로 넘어온 설정 클래스 정보를 이용해 스프링 빈 저장소에 스프링 빈을 등록함.
    2. @Bean 어노테이션이 붙은 메서드들을 실행해 리턴값을 등록해놓음. (빈 이름은 메서드 이름(key), 빈 객체는 리턴된 객체로(value))
      1. 빈 이름은 직접 지정할 수도 있다.
      2. 빈 이름은 항상 서로 다른 이름을 부여해야 한다. 빈 이름이 중복될 경우 다른 빈이 무시되거나 기존 빈을 덮어버리는 등 상황에 따라 복잡해지고, 설정에 따라 오류가 발생할 수도 있다.
      3. 어플리케이션 규모가 커지고 복잡해지면 빈 이름이 중복될 수도 있는데, 그러한 경우 최근 버전의 스프링에서는 빈 이름이 중복되면 기본적으로 오류를 발생시킨다. (예전에는 별도의 알림도 없이 그냥 덮어씌워버렸다고 한다.)
  3. 스프링 빈 의존관계 설정
    1. 설정 정보를 참고해서 스프링 빈 간의 의존관계를 주입한다.
    2. 동적인 객체 인스턴스 의존관계를 주입하는 것.