leelee.log

[Spring] IoC 컨테이너와 Bean, DI 본문

개발/Backend

[Spring] IoC 컨테이너와 Bean, DI

leeleelee3264 2020. 2. 23. 13:21

spring framework

 오늘은 IoC 컨테이너와 Bean, DI(dependency injection)에 대해서 동시에 다루려고 한다. 조금 분량이 많아보이지만 사실 세 개는 겹치고 이어지는 개념이 많아 함께 다루는 게 더 효율적이라고 판단했다. 게다가 Inversion of Control 포스트에서도 IoC 컨테이너에 대해서 다루고 있기 때문에 IoC에 대한 설명이 더 필요하다면 그 포스트를 다시 봐도 좋을 것 같다.

https://calgaryhomeless.tistory.com/2

 

(개념) Inversion of Control

*얼마전부터 유튜브에 열려있는 백기선님의 '스프링 입문' 이라는 강좌를 듣기 시작했다. 몇몇 개념들은 정리를 해두면 좋을 것 같아서 블로그 포스팅을 하려고 한다. 다른 분들을 위해 강좌 링크를 밑에 남겨두..

calgaryhomeless.tistory.com


 

  IoC 컨테이너는 사실 스프링이 알아서 관리를 하기 때문에 개발자가 직접 접근을 할 일은 많이 없다고 한다. 대부분 Spring의 @Autowired 를 사용해서 객체를 꺼내오니까.  IoC 컨테이너에 접근을 할 수 있는 방법은 BeanFactory와 ApplicationContext를 사용하는건데 예전에 ApplicationContext가 BeanFactory를 상속받았고, 더 넓게 쓰인다는 걸 본적이 있다. 실제로 여기서도 예제를 학습할 때 ApplicationContext를 사용하였다. IoC 컨테이네의 특성에 대해서 알고 있을 필요가 있는 부분은 IoC 컨테이너가 오로지 Bean으로 만들어진 객체만을 생성, 관리, 연결해준다는 점이다.

 일반 자바 class는 Spring을 실행할 때 객체가 저절로 만들어지지 않고, new 연산자로 생성을 직접 해야 한다. 얼마전에 해쉬맵에 정보를 저장해서 필요할 때 불러오는 코드를 작성했었는데, 호출을 해도 불려지지가 않고 오류가 나서 이상하다고 찾아보니 @Component 어노테이션을 쓰지 않은 일반 자바코드라서 만들어지지 않았던 거였다. 이런 실수를 하면서 팁을 하나 배웠는데 IntelliJ가 Bean인지 아닌지를 표시해준다고 한다.

 빈이면 라인 옆에 초록색 그림이 뜨고 빈이 아닌 자바 class는 아무것도 뜨지 않는다. 유용해보니 앞으로 적극 활용해보기로 했다.

빈이 아닌 자바 class
SimpleConfig안의 Bean과 또 다른 Bean인 SimpleConfig

 

 ApplicationContext.getBean()을 사용하면 특정 Bean을 가져올 수 있다. 그런데 재미있는 사실은 Spring에서 관리하고 있는 Bean과 내가 getBean()을 사용해서 호출하는 Bean이 결국은 같다. 왜냐하면 Spring에서 만들어진 Bean은 다 Singleton 형식으로 만들어지는데 이 말은 결국 여기저기서 사용하는 객체가 다 똑같은 하나의 객체라는 걸 알 수 있다. 실험용 코드를 하나 짜봤다. Spring에서 @Autowired한 TestService의 정보와 ApplicationContext에서 getBean으로 가져온 TestService의 정보, 나머지 하나는 new를 사용하여 임의로 만든 TestService의 정보를 가져왔다. 

 

결과

 Spring에서 가져온 빈과 ApplicationContext로 퍼 올린 Bean은 동일하고, 내가 new로 만든 TestService는 Spring에서 관리하지 않는(엄밀히 말하면 이것은 Bean이 아니다) 다른 결과를 반환하고 있다. IoC 컨트롤러에 대한 설명은 이것으로 마치겠다.

 


 알게 모르게 이미 Bean에 대한 애기를 많이 했다. 여기서는 Bean의 생성에 대해 좀 더 다루고자 한다. 우리가 자주 사용하는 어노테이션인 @Controller, @Service, @Configuration 을 까보면 항상 @Component라는 어노테이션을 가지고 있다. 이 어노테이션을 달고 있는 class는 Spring에게 "나는 당신이 관리하는 Bean으로 만들어 주세요~" 하는 뜻이 된다. Spring이 제일 처음 시작되는 지점은 @SpringBootApplication을 달고 있는 메인 메소드 안인데, 이 어노테이션을 까보면 @ComponentScan이라는 어노테이션이 나온다. @ComponentScan은 Spring에게 어디부터 스캔을 해서 Bean을 만들지 알려주는 역할을 한다.

 예전에 @SpringBootApplication과 같은 위치에 class를 만들어서 Spring이 못 찾는 오류를 만든적이 있다. @SpirgBootApplication이 무사히 Bean을 만들기 위해서는 무조건 나머지 class들을 아래로 두는 형태의 소스 트리를 만들어야한다. 그리고 이걸 준비하면서 알았는데 JPA기능을 사용하면 Repository class에 @Repository 어노테이션을 달지 않아도 된다고 한다... 맨날 달았었는데 Spring이 알아서 해준다고 한다.

 

 

 


 

  마지막으로 DI에 대해 말을 해보도록 하겠다. DI는 간단하게 말하면 IoC 컨트롤러 안에서 살고 있는 Bean을 불러와서 내 코드에 넣어주는 기능이다. 이렇게 DI 기능을 사용하면 컴파일러가 "너 이 클래스 진짜 만들었어? 어디있어?" 하면서 오류를 뿌려내지 않고 나중에 알아서 찾아오겠거니 하고 넘어간다고 한다.

 DI에는 필드, 생성자, setter 인젝션 세 가지 종류가 있다. 그 중에 스프링은 생성자를 이용한 방법을 권장하고 있는데 나는 class에서 이용하려는 Bean이 만들어져 있지 않으면 생성자를 통해서 class 자체를 만들지 못 하니까 안전성을 높힐 수 있기 때문이라고 이해했다. 생성자를 이용해서 autowired를 할 때 잘못하면 순환 참조가 발생한다. 예를 들어 SController를 만드는데 SService를 파라메터로 받고, SService에서도 SController를 생성자 파라메터로 받으면 순환참조가 일어난다. 최대한 안 일어나게 조심하는게 좋겠지만 일럴때는 필드 Injection을 쓰기도 한다. setter 인젝션은 써본적이 없었는데 다른 객체들의 setter처럼 사용해서 Injection이 된다는게 굉장히 신기하다.

 

 

이 블로그에 왜 생성자 injection을 해야하는지 잘 나와있다. 참고하면 좋을 것 같다.

https://zorba91.tistory.com/238

 

[Spring]필드 주입(Field Injection) 대신 생성자 주입(Constructor Injection)을 사용해야 하는 이유

Field Injection is not recommended.md Field Injection을 추천하지 않는 이유(+Constructor Injection을 추천하는 이유) 의존성 주입을 할 때 Field Injection은 좋지 않다는 글을 읽고 왜 그런지 찾아봤다. 스..

zorba91.tistory.com

 

'개발 > Backend' 카테고리의 다른 글

[Spring] Bean LifeCycle  (0) 2020.03.13
[Spring] PSA란 무엇인가?  (2) 2020.03.05
[Spring] @annotaion으로 Spring AOP 기능 구현  (0) 2020.02.18
[Spring] Spring AOP를 구현하는 Proxy Pattern  (0) 2020.02.16
[Spring] Inversion of Control  (0) 2020.02.08