티스토리 뷰

2) Spring IoC/DI 컨테이너

컨테이너(Container)

  • 컨테이너는 보통 인스턴스의 생명주기를 관리하며, 생성된 인스턴스들에게 추가적인 기능을 제공하는 것을 말합니다. 즉, 인스턴스를 직접 생성해서 실행하고 이 인스턴스가 소멸되는 이런 작업들, 라이프 사이클 관리를 여러분이 직접 하지 않고 누군가가 대신해주는 것을 의미합니다.
  • Servlet 공부할 당시 Servlet 클래스를 정의하긴 했지만 해당 Servlet 클래스를 직접 인스턴스화 하진 않았습니다. 해당 작업은 Tomcat이 대신해줬습니다. 다시 말해 Servlet을 실행시켜주는 WAS Servlet 컨테이너를 가지고 있다는 뜻입니다.
  • WAS는 웹브라우저로부터 Servlet URL에 해당하는 요청을 받으면 해당 Servlet을 메모리에 올린 후에 실행을 하게 됩니다.
    개발자가 Servlet 클래스를 작성했지만 실제로 메모리에 올리고 실행하는 것은 WAS가 가지고 있는 Servlet 컨테이너가 해주는 겁니다.
    Servlet
    컨테이너는 동일한 Servlet에 해당하는 요청을 받으면 또 메모리에 올리지 않고 기존에 메모리에 올라간 Servlet을 실행해서 그 결과를 웹브라우저에게 전달하는 역할을 합니다.
  • JSP 파일도 마찬가지입니다. 직접 JSP 파일을 생성했지만 이 JSP Servlet으로 바뀌고, Servlet이 인스턴스를 만드는 작업은 Servlet 컨테이너가 진행합니다. 이렇게 컨테이너는 인스턴스의 생명주기를 관리하는 작업을 합니다.

IoC

  • IoC Inversion of Control의 약어로 inversion은 사전적 의미로는 '도치, 역전'입니다. 보통 IoC를 제어의 역전이라고 번역합니다.
  • 개발자는 프로그램의 흐름을 제어하는 코드를 작성합니다. 그런데, 이 흐름의 제어를 개발자가 하는 것이 아니라 다른 프로그램이 그 흐름을 제어하는 것을 IoC라고 말합니다.
  • 예를 들어 서블릿 클래스는 개발자가 만들지만, 그 서블릿의 메소드를 알맞게 호출하는 것은 WAS입니다.
  • 이렇게 개발자가 만든 어떤 클래스나 메소드를 개발자가 아닌 다른 프로그램이 대신 실행해주는 것을 제어의 역전이라고 합니다.
  • 예시를 들어보겠습니다. TV를 하나 가지고 있는데 이 TV S 사의 TV입니다. TV와 사용하다가 고장이 나서 L TV를 샀습니다. 그런데 L사의 리모컨과 S사의 리모컨의 구성이 너무 달라서 너무 불편합니다.
  • 그래서 현실세계에서도 리모컨의 버튼 구성이나 배치를 거의 통일시켰습니다. 그래야 사용자의 입장에서 S_TV를 이용하다가 L_TV로 바꾸든, L_TV를 이용하다가 S_TV로 바꾸든 편하게 쓸 수 있을 것입니다.
  • 우리는 이런 껍데기를 인터페이스라고 합니다. 프로그래밍에서 인터페이스를 사용하는 이유도 마찬가지입니다.
  • 지금 TV를 보고 싶다 했을 때 이것을 코드를 구현하는 거라고 생각해보세요.
    S_TV stv = new S_TV(), 이렇게 생성했을 겁니다그런데 S_TV와 L_TV의 기능, 메서드가 다 달랐다면 사용하기 어려울 것입니다.
  • 여기서 인터페이스를 통일을 해놓으면 TV 객체는 바뀌지만 내부적으로 사용하는 메서드들은 같기 때문에 훨씬 편하게 사용할 수 있습니다그래서 TV라는 인터페이스를 하나 만들어가지고 L_TV, S_TV 모두 TV를 구현하도록 합니다.
  • 그러면 이제 대부분의 TV가 꼭 가져야 되는 기본적인 기능들은 다 갖고 있게 했고 사용자 입장에서 사용할 때 어떻게 사용하면 되냐면 이렇게 TV tv = new TV() 하겠죠. 그러면 new TV()에 L_TV가 오든, S_TV가 오든 어떤 TV가 와도 상관없습니다. tv라는 변수를 사용할 때에도 tv.기능, turnOn, turnOff, 이런 기능들을 이용하게 되는 거죠.
  • 그래서 각각 그냥 구현하는 것보다는 인터페이스를 만들어서 구현을 하면 나중에 사용할 때 new L_TV가 들어온 거냐, S_TV가 들어올 거냐 이것만 달라지면 다른 코드들은 변함 없이 사용될 수 있습니다. 이러한 부분 때문에 인터페이스를 잘 이용해야 합니다.
  • 그런데 상황에 따라 new 부분의 코드가 바뀔 것입니다. 프로그램 코드가 바뀐다는 것은 컴파일을 모두 다시 해야 하는데 이는 매우 번거롭고 가볍게 여길 일이 아닙니다.
  • 그래서 객체를 생성하는 코드를 고치지 않고 쓰기 위해 공장을 이용하는 방법을 고안하였습니다.
    TV 공장, TV Factory 같은 것을 하나 만들어놓고 여기에서 TV를 생성하는 것은 이 공장에서 알아서 해주는 겁니다getTV() 같은 메서드가 있다고 가정하고 parameter로 "S"를 주면 S_TV를, "L"이라고 넣어주면 L_TV를 만들어주도록 메서드를 코딩합니다. 그럼 사용자는 어쨌든 TV 타입으로 사용을 할 테니까 공장에서 나한테 S_TV를 만들어서 줬든, L_TV를 만들어서 줬든 아무런 상관없이 사용할 겁니다.
  • 혹시 인자에다가 "L"이라고 써야 되는데 여기 코드가 바뀌는 아닌가 하고 생각하실 수도 있는데요. 실제 사용할 때는 인자에 값을 직접 넣지는 않습니다.
  • Servlet 사용했을 당시 Servlet의 어노테이션으로 약속된 어노테이션으로 값을 넣어주거나 web.xml 파일에 URL 패턴을 넣어주고 값이 들어오면 해당하는 실제 Servlet 클래스를 만들어서 WAS가, Servlet 컨테이너가 생성해서 줬습니다. 이것처럼 Factory도 비슷하게 동작합니다.
  • 이러한 과정 덕분에 실제 바뀌더라도, S_TV에서 L_TV로 바뀌더라도 객체를 생성하던 쪽의 코드가 Tv tv = new S_TV() 코드가 달라질 일이 없습니다.
  • 결론은 IoC가 좋다는 것입니다. 다만 객체마다 공장을 직접 만들어야 해서 번거롭습니다. 그리고 이를 Spring이 대신 해줍니다. Spring이 갖고 있는 BeanFactory라든가 ApplicationContext 같은 것이 이런 공장의 역할을 해줄 것입니다.

DI

  • 공장이 인스턴스를 만들었다면 그 만들어진 인스턴스를 사용해야겠죠. 그러려면 공장이 만든 인스턴스를 내가 사용하는 프로그램에서 가져올 수 있어야 합니다. 가져올 수 있는 방법은 여러 가지가 있는데 그 중에 DI라는 것을 보겠습니다DI는 그렇게 만들어낸 의존성을 주입받는 방법입니다.
  • DI Dependency Injection의 약자로, 의존성 주입이란 뜻을 가지고 있습니다. 즉, DI는 클래스 사이의 의존 관계를 빈(Bean) 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것을 말합니다.

DI 예시

  • DI가 적용이 되지 않은 상태는 직접 인스턴스를 생성해야 됩니다. 자동차 클래스 내에서 엔진 객체를 new 해서 인스턴스를 직접 생성하고 있는 코드를 볼 수가 있습니다.
  • Spirng에서는 DI를 적용하게 되면 엔진이라는 인스턴스를 만드는 주체가 개발자에서 Spring으로 바뀝니다. 제어가 역전됐죠? 이제 개발자가 사용하는 코드에는 어디에도 new 엔진 인스턴스를 생성하는 코드가 보이지 않을 겁니다. 약속된 어노테이션들을 이용해서 여러분들은 선언만 해주면 Spring 컨테이너가 알아서 생성해서 주입해줄 겁니다. 그리고 여기서 바로 v5 변수를 사용하면 됩니다.
  • 정리하면 이제 new를 이용하여 메모리에 올리는 것은 직접 하는 게 아니라 컨테이너한테 맡긴다고 생각하면 됩니다. Servlet 사용할 때 new 하지 않고 WAS가 다 만들어준 것과 비슷합니다.

DI가 적용 안 된 예

Spring에서 DI가 적용된 예

class 엔진 {

}

 

class 자동차 {

     엔진 v5 = new 엔진();

}

@Component

class 엔진 {

}

 

@Component

class 자동차 {

     @Autowired

     엔진 v5;

}

개발자가 직접 인스턴스를 생성합니다.

엔진 type v5변수에 아직 인스턴스가 할당되지 않았습니다.

컨테이너가 v5변수에 인스턴스를 할당해주게 됩니다.

 

Spring에서 제공하는 IoC/DI 컨테이너

  • BeanFactory : IoC/DI에 대한 기본 기능을 가지고 있습니다.
  • ApplicationContext : BeanFactory의 모든 기능을 포함하며, 일반적으로 BeanFactory보다 추천됩니다. 트랜잭션처리, AOP등에 대한 처리를 할 수 있습니다. BeanPostProcessor, BeanFactoryPostProcessor등을 자동으로 등록하고, 국제화 처리, 어플리케이션 이벤트 등을 처리할 수 습니다.
  • BeanPostProcessor : 컨테이너의 기본로직을 오버라이딩하여 인스턴스화와 의존성 처리 로직 등을 개발자가 원하는 대로 구현 할 수 있도록 합니다.
  • BeanFactoryPostProcessor : 설정된 메타 데이터를 커스터마이징 할 수 있습니다.

생각해보기

  • 스프링 프레임워크는 DI Container라고도 말을 합니다. 스프링 프레임워크 이외에도 DI Container는 존재할까요?

참고 자료

[참고링크] Spring - IoC & DI                                      http://isstory83.tistory.com/91

[참고링크] 세 가지 DI 컨테이너로 향하는 저녁 산책           http://www.nextree.co.kr/p11247/

 

'부스트코스 웹 프로그래밍 > 3. 웹 앱 개발: 예약서비스 1' 카테고리의 다른 글

7. Spring Core - BE (4)  (0) 2019.08.04
7. Spring Core - BE (3)  (0) 2019.08.03
7. Spring Core - BE (1)  (0) 2019.08.03
6. Tab UI 실습 - FE (2)  (0) 2019.08.03
6. Tab UI 실습 - FE (1)  (0) 2019.08.03
Comments