당니의 개발자 스토리
스프링 컨테이너 생성 본문
4. 스프링 컨테이너와 스프링 빈
이번 시간에는 스프링 컨테이너와 스프링 빈에 대해서 알아보겠습니다.

기존에는 스프링의 핵심 원리들, 객체 지향에 대한 것들에 대해서 설명을 했구요. 그렇게 해서 왜 스프링이 만들어졌는지 설명했다면, 이제부터는 진짜 스프링 그 자체에 대해서 쭉 설명을 해드릴 거에요.
자 먼저 가장 중요한 스프링 컨테이너와 스프링 빈에 대해서 쭉 설명을 해드릴게요.
첫 번째로 스프링 컨테이너가 어떤 식으로 생성이 되는지 먼저 설명을 해드리겠습니다.
먼저 스프링 컨테이너가 생성되는 과정을 한번 알아볼게요.

스프링 컨테이너는 AnnotationConfigApplicationContext를 객체로 생성하면서, AppConfig.class를 파라미터로 넘깁니다. 그렇게 하면 반환 값으로 ApplicationContext가 반환이 됩니다.

이 ApplicationContext를 스프링 컨테이너라고 얘기를 해요. 그리고 이 ApplicationContext는 인터페이스에요. 그럼 다형성이 적용되어 있겠죠.
따라서, ApplicationContext를 구현한 것 중에 하나가 AnnotationConfigApplicationContext인 거에요.
우리가 AppConfig를

@Configuration 이라는 어노테이션을 기반으로 만들었는데,

이름 그대로 Annotation 기반의 Java Config 설정을 기반으로 ApplicationContext, 그러니까 '스프링 컨테이너를 만들어라!' 라고 한겁니다. 그래서 이름이 AnnotationConfigApplicationContext 인 겁니다.
그래서 스프링 컨테이너는 XML을 기반으로 만들 수 있고, 또는 애노테이션 기반의 자바 설정 클래스로 만들 수 있습니다.
요즘에는 XML 기반으로 만드는 건 잘 사용하지 않아요. 스프링 부트 자체가 Annotation을 기반으로 편리하게 작동되도록 되어 있어서, Annotation 기반으로 Java 설정 클래스를 만드는 방식을 요즘에는 거의 쓴다고 보시면 돼요.
그리고 AppConfig를 사용했던 방식이 바로 애노테이션 기반의 java 설정 클래스로 스프링 컨테이너를 만든 겁니다.
그래서 자바 설정 클래스를 기반으로 스프링 컨테이너(ApplicationContext)를 만들려면,
new AnnotationConfigApplicationContext(AppConfig.class) 해서 AppConfig.class를 파라미터로 전달해줘야 합니다. '이 설정 정보로 스프링 컨테이너를 만들거야~' 라고 전달해주는 거죠.
AnnotationConfigApplicationContext은 ApplicationContext의 구현체입니다.

참고로 ApplicationContext를 스프링 컨테이너라고 표현을 하는데, 컨테이너는 안에 사용하는 객체들을 담고 있는 거예요. 더 정확히는 스프링 컨테이너를 부를 때 BeanFactory랑 ApplicationContext로 구분해서 이야기하는데, 이 부분은 뒤에서 설명할게요. BeanFactory를 직접 사용하는 경우는 거의 없기 때문에 일반적으로 ApplicationContext를 스프링 컨테이너라고 합니다.
최상위에 BeanFactory 라는 게 있고, 그 밑에 ApplicationContext가 있고, 유틸리티성 기능들이 추가가 되어있거든요.
그냥 단순하게 ApplicationContext가 스프링 컨테이너 라고 이해하시면 됩니다.
자 그래서 이제 스프링 컨테이너 생성 과정을 그림으로 쉽게 설명을 해드리겠습니다.


먼저, new AnnotationConfigApplicationContext(AppConfig.class)를 하면서 AppConfig 정보를 주는 거에요. 그게 그림상에서 1번입니다.
그러면 스프링 컨테이너가 딱 만들어져요. 그런데 스프링 컨테이너 안에는 스프링 빈 저장소 라는 게 있어요. 그래서 Key는 빈의 이름이 되고, Value은 빈의 객체가 되는 거예요. 이런 스프링 컨테이너 내부에는 빈 저장소라는 게 있고요.
스프링 컨테이너를 생성할 때는 이 AppConfig 라는 구성(설정) 정보를 지정을 해줘야 돼요. 그래서 파라미터로 AppConfig.class를 넘긴 거에요.
그러면 스프링 컨테이너가 이 AppConfig 클래스 정보를 보고, '아 내가 얘네를 기반으로 객체로 생성을 해야 되겠는데' 라고 인지를 합니다.
그러면 이제 2번 단계로,

스프링 컨테이너가 본인을 생성하면서 어떤 일을 하냐면, 스프링 빈 저장소에다가 스프링 빈들을 등록을 합니다. 어떻게 등록하냐면, 우리가 넘겼던 AppConfig 클래스 설정 정보를 하나씩 보면서, @Bean이 붙은 것들을 죄다 호출을 해요.
호출을 해보니까 'memberService 라는 method name이 있네.' 하면서, 그 메서드 이름을 key, 즉 빈 이름으로 지정을 하고요. Value는 return 값으로, new 해서 나오는 객체를 bean 객체로 등록 해줍니다.
여기엔 4개니까 4개를 다 Spring Bean으로 등록합니다.
자 그렇게 해서 4가지 new한 Instance 객체가 스프링 빈 저장소에 다 등록이 되구요.


참고로 Bean의 이름은 보통 method 이름을 사용하는데요. 임의로 부여할 수도 있습니다. annotation 에다가 이런식으로 이름을 다르게 부여할 수도 있습니다.

그런데 주의할 게 있어요. 빈 이름은 항상 다른 이름을 부여해야 돼요. 같은 이름을 부여하면 상황에 따라 빈의 이름이 무시되거나 기존 빈을 덮어버리거나, 설정에 따라서 오류가 발생하기도 해요.
단순하게 빈의 이름은 항상 다른 이름을 부여하자. 이렇게 기억하시면 됩니다.
문제는 단순하게 해결할 수 있어야 합니다. 실무에서 "빈 이름이 중복이 됐는데" 이런 상황을 만들면 안돼요. 절대 중복되게 만들면 안 돼요. 실무에서는 헷갈리게 만들면 절대 안됩니다. 무조건 단순하고 명확하게 개발해야 돼요. 충돌이 안나게 무조건 이름을 따로 부여한다! 라고 기억을 해주세요. 스프링 부트에서도 이게 디폴트에요.
그 다음에 스프링이 어떤 단계를 거치냐면, 이 스프링 빈 의존관계를 설정할 준비를 합니다.

객체를 생성을 했죠. 4가지 객체를 생성을 한 다음에 의존관계 주입했던 거 기억나시죠.
스프링 빈 컨테이너는 스프링 빈을 등록한 다음 단계로, 의존관계를 넣어줍니다.

지금 보면, memberService의 의존 관계를 뭘 넣어줬죠? memberRepository를 넣어줬죠. 그러면 MemoryMemberRepository가 세팅이 됩니다.
그 다음에 orderService는 memberRepository도 의존하고, discountPolicy도 의존하죠. 그래서 MemoryMemberRepository랑 RateDiscountPolicy가 주입됩니다.
이렇게 해서 동적인 의존관계와 정적인 의존관계에 대해서 설명 드렸는데,
실제로 동적인 객체 인스턴스 의존관계는 스프링이 다 연결해 줍니다. 그래서 실제 스프링 빈이 다 연결이 되는 거에요. 그 레퍼런스, 객체의 참조 값들이 다 연결되는 겁니다.
스프링 컨테이너는 이 설정 정보를 참고해서 의존관계를 주입해요. 설정 정보를 참고한다는 말은 AppConfig에서 뭘 필요해하는지를 보고 의존관계를 주입하는 걸 말합니다.
'단순히 자바 코드를 호출하는 것 같은데요?' 라고 할 수 있지만, 사실 차이가 있어요. 그래서 이렇게 나눠서 설명을 드린거고, 그 차이는 뒤에 싱글톤 컨테이너라는 장에서 쭉 설명을 해드릴 거에요.
참고로,


스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있어요. 그래서 먼저 스프링 빈 객체를 쭉 생성을 하고, 빈이 다 생성이 된 다음에 쫙 엮어주는 거에요.
그런데 이렇게 지금 자바 코드로 스프링 빈을 등록하면, 생성자를 호출하면서 의존관계 주입도 한 번에 처리가 돼요.
왜냐면, 스프링이 "@Bean을 보고 memberService를 호출해!"라고 했을 때, memberService를 호출하면서 MemberServiceImpl을 생성함과 동시에 memberRepository를 호출해버립니다. 그렇게 memberService를 부르는 순간 의존관계가 다 엮여져 버린단 말이에요.
그런데 사실은 이것뿐만 아니라, 뒤에 의존관계 자동주입(ex. 컴포넌트 스캔)에서 설명을 또 할 건데요.
실제로 의존관계가 자동으로 막 연결이 되는 경우도 있습니다. 그래서 실제 단계가 두 단계로 나눠져 있어요.

그래서 위처럼 Java Config으로 하면, 실제로는 한 번에 의존관계가 설정이 되고 이렇게 단계가 나눠지지 않아요.
그런데 실제 스프링의 라이프 사이클은 빈 생성과 의존관계의 단계가 나누어져 있거든요. 뒤에서 설명하면 왜 나눠설명했는지 아실 겁니다.
정리하면,

스프링 컨테이너도 생성하고, 설정(구성) 정보를 참고해서 스프링 빈도 등록하고, 의존관계도 설정했습니다.
다음 시간부터는 스프링 컨테이너에서 데이터를 조회해봅시다. 컨테이너에 등록된 빈을 한번 조회해볼게요. 지금까지 등록한게 다 잘 등록이 됐나 전체에서 한번 조회하는 거를 보여드리겠습니다.
'스프링 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
| 스프링 빈 조회 - 기본 (0) | 2024.01.20 |
|---|---|
| 컨테이너에 등록된 모든 빈 조회 (0) | 2024.01.20 |
| 스프링으로 전환하기 (0) | 2024.01.19 |
| IoC, DI, 그리고 컨테이너 (0) | 2024.01.19 |
| 좋은 객체 지향 설계의 5가지 원칙의 적용 (0) | 2024.01.18 |