티스토리 뷰

개발을 하다보면, 개발 공부를 하다보면 DI라는 용어를 많이 접하게 된다. 거기에 덧붙여 DI 컨테이너, IoC이라는 용어도 많이 접하게 되는데 처음에 이러한 용어들을 접하고 가지 않으면 이게 뭐지? 라고 어리둥절하다가 해당 용어의 뜻을 알고 아 그렇구나 라고 바로 이해를 하게 된다.

그래서 이번에 제대로 해당 용어들에 대한 개념들을 정리해보려 한다.

 

 

DI(Dependency Injection)이란?

우선 제일 기본적인 DI는 의존성 주입을 의미한다.

의존성 주입에 대해 제일 간단한 예를 들면 Controller에서 Service를 사용하는 것을 예로 들 수 있다.

public class MenuController {
	private tinal MenuService menuService;
    	...
}

 

Controller에서는 서비스 로직이 들어가 있는 Service를 꼭 사용해야하고 호출해야한다. 그러기 위해서는 해당 Service를 Controller를 생성할 때 초기화를 해야 한다.

Service는 아래와 같이 Controller 생성자에서 초기화할 수 있다.

public class MenuController {
	...
	public MenuController(MenuService menuService) {
            this.menuService = menuService
        }
        ...
}

 

이와 같이 해당 객체에서 꼭 필요한 것, 의존 받는 객체를 주입하는 것 이것을 바로 의존성 주입이라고 한다.

 

 

DI 컨테이너란?

여기서 DI할 때 객체를 생성해서 주입하는 것을 도와주는 것이 바로 DI 컨테이너이다.

DI 컨테이너에 대해서는 저마다 좋다 나쁘다 이야기가 많다.

대표적으로 Google의 guice가 있다. 하지만 지금 와서는 스프링 프레임워크에서 생성자를 통해 빈을 생성해서 사용하기 때문에 이와 같은 DI container를 사용하지 않아도 된다. 객체를 생성하고 의존성 주입을 할 수 있다. 스프링 프레임워크에서는 위의 예시 코드처럼 생성자를 통해 의존성 주입을 할 수 있다.

https://github.com/google/guice

 

GitHub - google/guice: Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 6 and above, brought

Guice (pronounced 'juice') is a lightweight dependency injection framework for Java 6 and above, brought to you by Google. - GitHub - google/guice: Guice (pronounced 'juice') is a l...

github.com

 

 

 

 

IoC(Inversion of Control)

그렇다면 DI와 같이 자주 언급되는 IoC는 무엇인가?

IoC는 간단하게 얘기하면 제어의 역전이라는 뜻이다. DI에 대해 개발자가 주입하려는 객체를 생성하고 주입하는 것이 아닌 프레임워크에서 주입하는 객체를 생성해주고 주입히시켜주는 것이다. 즉, 개발자에게 DI에 대한 제어권이 없는 것이다.

 

예를 들면 위에서 예시로 들었던 아래 코드는 생성자로 의존성 주입을 해서 스프링 프레임워크에서 주입하는 객체를 생성하고 자동으로 주입시켜준다. 개발자가 언제 객체를 생성하는지 등을 제어 하는 것이 아니므로 이게 마로 IoC이다.

public class MenuController {
	private tinal MenuService menuService;
    	...
        public MenuController(MenuService menuService) {
            this.menuService = menuService
        }
        ...
}

 

하지만 아래와 같이 생성자의 인자로 받지 않고 생성자 내에서 menuService를 생성해서 주입한다면? 그것은 개발자가 객체 생성부터 주입까지 제어할 수 있는 것으로 IoC라고 할 수 없다.

public class MenuController {
	private tinal MenuService menuService;
    	...
        public MenuController() {
            this.menuService = new MenuService();
        }
        ...
}

 

 

의존성 주입과 관련된 어노테이션

그렇다면 스프링 프레임워크를 사용할 떄는 생성자를 통해서나 별도 메소드를 통해서만 의존성 주입을 할 수 있는 것인가?

그건 또 아니다. 스프링 프레임워크에서는 별도로 프레임워크에서 자동으로 생성자를 주입할 수 있도록 어노테이션 또한 제공하고 있다.

 

단 이와 같은 어노테이션으로 스프링 프레임워크에서 의존성 주입을 하기 위해서는 Component Scan 기능을 활성화 시켜야 한다.

단, 스프링부트 어플리케이션의 경우 이러한 Component Scan에 대한 기능을 이미 @SpringBootApplication 안에 다 포함시켜놔서 빈으로 등록만 되어 있으면 Component Scan 기능을 활성화 시키지 않고도 빈 등록만으로 의존성 주입 어노테이션을 사용할 수 있다.

 

스프링부트가 아닌 스프링 프레임워크에서는 아래와 같이 Bean 설정 파일에 어노테이션을 통해 Component Scan 기능을 활성화 시킬 수 있다. (Autowired 공식 문서 참고)

@Configuration
@ComponentScan("com.pamyferret.test.component")
public class AppConfig {}

 

또는 xml 파일을 통해 빈을 등록하고 scan 하도록 할 수 있다. (baeldung 사이트 참고)

<bean id="accountService" class="AccountService"></bean>
<bean id="userService" class="UserService"></bean>

 

스프링부트의 @SpringBootApplication 어노테이션에 들어가보면 아래와 같이 ComponentScan이 이미 설정 되어 있는 것을 알 수 있다.

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

 

 

의존성 주입 기능을 하는 어노테이션은 @Autowired, @Resource, @Inject 3가지가 있다. 각 어노테이션에는 차이점이 있으니 각 특징들을 인지하고 사용해야 한다.

 

@AutoWired

제일 기본으로 사용하는 의존성 주입 어노테이션이다. 참고로 스프링 전용으로 스프링 프레임워크를 사용하지 않을 경우 사용하지 못한다.

@AutoWired는 기본적으로 등록되어 있는 빈 중에서 타입이 일치하는 빈을 찾아서 주입한다. 또는 등록된 빈의 이름과 어노테이션이 붙어 있는 변수의 이름과 일치하는 것을 찾아 주입한다.

예를 들어 아래와 같은 경우에는 Test 타입인 빈을 먼저 찾고 많이 Test 타입인 빈이 여러 개 등록되어 있다면 test라는 이름을 가진 빈을 주입하게 되어 있다.

@Autowired
private Test test;

 

옵션 값으로는 required를 넣을 수 있다. 즉, 반드시 주입해야하는지 아닌지에 대해 true/false를 넣을 수 있는데 default 값을 true이다. 만일 false를 넣을 경우 빈 객체를 주입하는데 문제가 있어도 에외를 발생시키지 않고 빈을 주입시키지 않는다.

@Autowired(required = false)
private Test test;

 

 

@Resource

@AutoWired 다음으로 많이 사용했던 어노테이션이다. @AutoWired와의 차이점은 Resource는 기본적으로 이름을 통해 빈을 찾아 주입시킨다는 것이다. 아래와 같이 작성했을 때 Test 타입의 빈을 먼저 찾는 것이 아닌 test이름을 가진 빈을 먼저 찾는다 그리고 그게 여러 개일경우(그렇지 않겠지만) Test 타입인지 아닌지를 확인해서 주입시킨다.

@Resource
private Test test;

 

@Resource 어노테이션은 여러 가지 옵션이 있지만 그 중 name을 넣어서 주입시킬 빈의 이름을 아예 특정 지어서 넣을 수 있다. 물론 이건 변수의 이름과 별도로 작용해 변수의 이름과 다르게 설정할 수 있다.

@Resource(name = "testBean")
private Test test;

 

 

@Inject

@Inject 어노테이션은 위 두 어노테이션보다 덜 사용해봤다. @Inject는 @Autowired와 동일하게 동작해 의존서 주입을 한다. 단, 차이점은 @Inject는 자바 자체에 있는 어노테이션으로 자바를 사용하면 사용할 수 있는 어노테이션다. @Autowired는 스프링 프레임워크를 사용할 경우 사용할 수 있는 어노테이션으로 보통 나는 스프링으로 개발을 해서 @Inject가 아닌 @Autowired를 사용했고 @Resource 어노테이션을 때에 따라 사용했다.

 

사용 방법은 @Autowired와 동일하다. 차이점은 스프링 프레임워크에 포함되어 있느냐 포함되어 있지 않느냐의 차이이다.

@Inject 또한 타입으로 해당 빈을 찾아 주입 시키고 만일 해당 타입의 빈이 여러 개라면 그 다음에 이름으로 찾아 주입시킨다.

@Inject
private Test test;

 

 

 

그 동안 사용은 계속 하고 있었지만 알고 사용하지 않았던 DI. 이에 대해 이번 글에서 대략적으로 관련된 것에 대해 다 정리를 해봤다.

스프링부트에 기본으로 설정되어 있는 빈 스캔하는 기능과 생성자를 통해 의존성 주입을 하고 있어서 @Autowired, @Resource, @Inject를 사용하지는 않아서 해당 어노테이션들은 새로운 마음으로 공부해볼 수 있었다.

 

앞으로 뭔가 사용하면 그것을 구현하는 원리 등 딥하게 공부하지는 않더라도 그것이 무엇이고 왜 존재하고 사용하는지에 대해서는 공부하자.

 

 

 

🤚 @Resource, @Inject, @Autowired

https://www.baeldung.com/spring-annotations-resource-inject-autowire

 

✋ spring @Configuration

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

 

Configuration (Spring Framework 5.3.9 API)

Specify whether @Bean methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct @Bean method calls in user code. This feature requires method interception, implemented thro

docs.spring.io

 

 

 

 

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함