<< 학습 목표 >>

1. 컨테이너를 생성하고 빈 객체를 등록할 수 있다.

2. 의존 주입을 한 빈 객체를 컨테이너에 등록할 수 있다.

3. 컨테이너의 생성 과정을 설명할 수 있다.


전 글 ( https://codingaja.tistory.com/111 ) 에서 의존, 의존 주입을 배웠고 의존 주입을 대신 해주는 요소에 대해 배웠음

그러면서 우리가 직접 자바만 사용해 의존 주입을 대신 해주는 요소(SmartPhoneFactory)를 만들었음


Spring Framework의 경우 xml을 이용한 의존 주입, 직접 의존 주입, 애너테이션을 이용한 의존 주입 / 이렇게 3가지 의존 주입 기법을 제공함

 

특히, 우리처럼 Spring Boot 로 생성한 Spring Framework 프로젝트의 경우 일반적으로는 애너테이션을 이용한 의존 주입을 하도록 권장하고 있음


이번에는 Spring Framework가 제공하는 의존 주입 기법 중 직접 의존 주입에 대해서 알아보자

SPring Framework가 권장하는 방식인 애너테이션을 이용한 의존 주입 방법은 다음 글 ( ) 에서 알아 볼 예정


<< 직접 의존 주입 하는 방법 >>

전 글의 SmartPhoneFactory 역할을 할 Configuration 클래스가 필요함

프로젝트 -> com.study.chapter03 -> Config 클래스를 추가하고 아래 코드를 추가하자

package com.study.chapter03;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Config {
	private String batteryType = "리튬 폴리머 배터리";
	
	@Bean
	public GalaxyS23 galaxyS23() {
		return new GalaxyS23(batteryType);
	}
	
	@Bean
	public GalaxyFlip4 galaxyFlip4() {
		return new GalaxyFlip4(batteryType);
	}
	
	@Bean
	public IPhone14 iPhone14() {
		return new IPhone14(batteryType);
	}
}

<< 코드 설명 >>

왼쪽에 SmartPhoneFactory 는 자바만 사용해 IoC, DI 를 적용한 클래스이고 왼쪽에 Config 는 Spring Framework가 제공하는 IoC, DI 를 적용한 클래스임

 

같은 역할을 하는 두 클래스의 코드를 비교 해보자

(1). 의존 주입을 하기 위한 공통 분모 생성

(2), (3), (4). 객체를 생성하며 의존 주입 후 반환

 

여기서 사용된 @Configuration 애너테이션과 @Bean 애너테이션을 분석해보자

 

@Configuration 애너테이션은 빈(Bean) 들을 생성해 가지고 있고 이 Bean들이 필요한 곳에 제공을 해주는 클래스에 애너테이션을 붙임

@Configuration 애너테이션이 붙은 클래스를 컨테이너(Container) 라고 부름

실행활에서 컨테이너를 직접 보지못했어도 어떤 역할을 하는지 충분히 알고 있을 것

컨테이너는 물건들을 옮길 때 사용하는 것으로 컨테이너에 물건들을 넣고 뺄 수 있음

 

Spring Framework의 컨테이너( @Configuration 애너테이션이 붙은 클래스 ) 도 이와 같은 역할을 함

Spring Framework의 컨테이너에 넣고 빼는 물건을 빈(Bean) 이라고 부름

 

빈(Bean)은 자바에서 객체, 인스턴스로 부르는 것으로 SmartPhoneFactory 클래스에서 각 if문이 return 해주는 인스턴스를 SpringFramework의 용어로 빈(Bean)이라고 부름

 

이 빈들을 Spring Framework의 컨테이너에 넣어두면 빈이 필요한 곳에서 컨테이너에 들어있는 빈을 필요할 때 꺼낼 수 있음

Spring Framework의 컨테이너에 빈을 넣어두려면 @Bean 애너테이션이 붙은 메서드가 필요한대 아래 (1), (2), (3) 메서드가 컨테이너에 빈을 넣어두는 @Bean 애너테이션이 붙은 메서드임

 

Config 클래스에 @Configuration 애너테이션이 붙어있으므로 Config 클래스는 컨테이너가 됨

그리고 Config 컨테이너에 @Bean 애너테이션이 붙은 메서드를 선언했으므로 메서드가 return 해주는 빈들이 Config 컨테이너에 등록되는데 이때 빈의 이름은 메서드이름이 됨

 

이제 빈이 필요한 곳에서 Config 컨테이너를 불러온 다음 빈의 이름을 사용해 각 빈을 꺼낼 수 있음

 

 

프로젝트 -> com.study.chapter03 -> Ex02 클래스를 추가하고 아래 코드를 추가하자

package com.study.chapter03;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Ex02 {
	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
		
		GalaxyS23 phone1 = (GalaxyS23) ctx.getBean("galaxyS23");
		GalaxyS23 phone2 = (GalaxyS23) ctx.getBean("galaxyS23");
		GalaxyFlip4 phone3 = (GalaxyFlip4) ctx.getBean("galaxyFlip4");
		IPhone14 phone4 = (IPhone14) ctx.getBean("iPhone14");
		IPhone14 phone5 = (IPhone14) ctx.getBean("iPhone14");
		
		System.out.println("<< phone1 >>");
		phone1.printBatteryInfo();
		
		System.out.println("<< phone2 >>");
		phone2.printBatteryInfo();
		
		System.out.println("<< phone3 >>");
		phone3.printBatteryInfo();
		
		System.out.println("<< phone4 >>");
		phone4.printBatteryInfo();
		
		System.out.println("<< phone5 >>");
		phone5.printBatteryInfo();
	}
}

<< 코드 설명 >>

(1). Config 컨테이너를 불러와 ctx 객체에 저장

(2). Config 컨테이너에 있는 빈을 꺼내 각 객체에 저장

  이때 빈의 이름은 메서드 이름이라는 점을 기억하자

  또한 getBean 메서드로 어떤 빈을 꺼낼지 모르기 때문에 getBean 메서드는 반환 타입이 Object 라는점

  그래서 getBean으로 빈을 꺼낸 다음 적절한 형태로 형변환해야함


우리가 직접 만든 컨테이너인 SmartPhoneFactory 컨테이너와 Spring Framework가 제공하는 컨테이너의 사용 방식을 보면 다를게 없음

SmartPhoneFactory는 컨테이너를 흉내낸 우리가 직접 만든 컨테이너로 실무에서 활용할 수 있는 수준의 컨테이너가 되려면 굉장히 많은 기반 지식과 개발 경험이 있어야함

SmartPhoneFactory 처럼 이렇게 개발에 필요한 부분을 처음부터 끝까지 다 만들려면 상당한 개발 지식, 경험이 필요하고  개발자가 관리해야할 코드가 많아지고 개발 기간이 상당히 길어짐

또한 그것을 직접 만든 개발자가 퇴사를 하거나 어떤 일로 자리를 비우면 프로젝트에 마비가 올 것

 

프레임워크를 사용하면 개발에 필요한 상당 부분을 프레임워크의 도움을 받을 수 있고 개발 기간이 단축될 수 있음

또한 특정한 개발자가 직접 만든게 아니기 때문에 프레임워크가 제공하는 것들을 사용해 개발했다면 개발자가 퇴사를 하거나 어떤 일로 자리를 비우더라도 프레임워크를 알고 있는 다른 개발자가 대신 할 수 있으므로 프로젝트에 마비가 오지 않음


이제 Spring Framework가 제공하는 컨테이너에 대해서 좀 더 깊게 들어가보자

 

다시 한번 우리가 직접 만든 컨테이너와 Spring Framework가 제공하는 컨테이너를 비교해보자

아래와 같이 두 방식 모두 두 대의 핸드폰을 생성했고 각 핸드폰의 배터리 타입을 바꿨음

위와 같이 프로젝트 -> com.study.chapter03 -> Ex03 소스 파일을 추가하고 위에서 왼쪽에 있는 코드를 입력하자

또한 프로젝트 -> com.study.chapter03 -> Ex04 소스 파일을 추가하고 위에서 오른쪽에 있는 코드를 입력하자

 

 

우리가 직접 만든 컨테이너를 사용한 코드에서는 우리가 알고 있는대로 각 객체의 배터리 타입을 바꿨으므로 phone1, phone2의 배터리 타입이 다르게 출력됨

 

 

그러나 Spring Framework가 제공하는 컨테이너를 사용한 코드에서는 우리가 알고 있는 것과 다르게 두 핸드폰의 배터리 타입이 모두 리튬 폴리머 배터리로 바꼈음

 

왜이럴까??

이는 Spring Framework 컨테이너의 생성 과정과 동작 과정을 보면 어렵지 않게 이해할 수 있음

 

Spring Framework 프로젝트를 실행시키면 @Configuration 애너테이션이 붙은 컨테이너를 생성함

컨테이너의 이름은 @Configuration 애너테이션이 붙은 클래스의 이름을 따라감

 

 

바로 이어서 빈들(galaxyS23, galaxyFlip4, iPhone14)이 의존하고 있는 의존 객체인 batteryType을 생성함

 

 

그 후 @Bean 애너테이션이 붙은 메서드들이 차례대로 호출됨

 

메서드들이 호출되면서 컨테이너에 빈 객체가 등록되는데 메서드의 이름이 빈 객체의 이름이 됨

 

빈은 프로젝트에 필요한 인스턴스를 갖고 있는데 그 인스턴스는 메서드가 반환해주는 인스턴스임

메서드가 인스턴스를 생성할 때 우리가 지정한 방식인 생성자를 통해 의존 주입을 하고 반환함

따라서 빈은 의존 주입이 된 프로젝트에 필요한 인스턴스를 갖고 있음

 

나머지 빈 객체들도 마찬가지로 의존 주입이 된 인스턴스를 갖고 있음

 

이렇게 의존 주입이 된 빈 객체를 갖고 있는 컨테이너가 생성됨

 

Ex04에서 사용할 컨테이너가 생성되는 과정을 다시 한번 알아봤음

다시 Ex04 코드를 보자

 

(1). 이렇게 생성된 Config 컨테이너를 불러와 ctx에 저장함

 

(2). ctx를 통해 Config 컨테이너에 저장되어있는 galaxyS23 빈을 가져와 phone1 객체에 저장함

 

여기서 주의할 점은 빈을 가져온다는건 빈을 꺼낸다와는 다름

 

실실행활에서 컨테이너에서 물건을 가져온다는건 꺼낸다는 것이고 컨테이너에서 물건을 가져오면 그 컨테이너에는 가져온 물건이 빠짐

Spring Framework의 컨테이너에서 빈을 가져온다는건 빈을 꺼낸다가 아님

galaxyS23 빈은 GalaxyS23 클래스의 인스턴스가 저장된 주소를 갖고 있는 것

( 이는 자바의 기초적인 내용이니 빈이 왜 인스턴스가 저장된 주소를 갖고 있는지에 대한 설명은 하지 않겠음, 만약 왜? 라는 생각이 들면 자바의 클래스를 다시 처음부터 공부하고 오자 )

 

그래서 getBean 메서드로 빈을 가져오면 빈이 가지고 있는 의존 주입된 GalaxyS23 클래스의 인스턴스 주소를 반환함

phone1 객체는 이 인스턴스의 주소를 갖고 있게되는 것

 

결국 phone1 객체는 아래와 같이 GalaxyS23 클래스의 인스턴스를 참조하게 됨

 

 

(3). 이번에도 (2)와 마찬가지로 ctx를 통해 Config 컨테이너에 저장되어있는 빈을 가져옴

따라서 phone2 객체도 phone1 객체와 마찬가지로 GalaxyS23 클래스의 인스턴스를 참조함

 

(4). 이런 상태에서 phone1, phone2 객체의 setter 를 사용해 의존 객체를 수정하면 같은 인스턴스의 의존 객체를 수정하는 것이므로 GalaxyS23 클래스의 인스턴스의 의존 객체는 "리튬 폴리머 배터리"가 됨

 

여기까지 의존 주입이 적용된 컨테이너를 생성해봤고 IoC를 활용해 빈이 필요한곳에서 빈을 꺼내봤음

 

DI, IoC는 Spring Framework에서 굉장히 중요한 부분이므로 이 글에서 설명하는 것들을 이해할 수 있을 때까지 여러번 반복해서 읽어보고 여러번 반복해서 읽었는데도 이해가 안되면 다른 블로그를 더 찾아보고서라도 반드시 이해해야함

728x90
LIST