In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans
(스프링에서, 당신의 어플리케이션의 뼈대를 형성하며 Spring IOC 컨테이너에 의해 관리되는 객체를 빈(bean)이라고 부릅니다.)
스프링에서 빈(bean)이란, 스프링 IOC 컨테이너에서 관리하는 객체들을 의미한다.
스프링은 이 빈들을 IOC 컨테이너에 보관하고 있다가, 필요한 곳에 DI를 통해 객체를 주입시켜준다.
스프링의 특징중 하나로, IoC(Inversion of Control)라는 특성이 있다.
IoC는 객체에 대한 제어권을 개발자가 아닌 Spring 프레임워크가 가지는 것을 의미한다.
우리가 왜 Spring에게 제어권을 넘겨줘야 하는 것일까? 그냥 우리가 전부 제어하면 안되는 것일까?
IoC가 필요한 이유에 대해 알아보기 위해, 다음 코드를 보자.
public class Car {
private Engine engine;
private Tire tires;
private Handle handle
Car(){
engine = new Engine_A();
tires = new Tire_A();
handle = new Handle_A();
}
}
public class Bus {
private Engine engine;
private Tire tires;
private Handle handle
Bus(){
engine = new Engine_A();
tires = new Tire_A();
handle = new Handle_A();
}
}
위 코드에서, Car와 Bus라는 클래스는 Engine, Tire, Handle 등의 다른 추상 클래스(내지 인터페이스)에 의존하고 있다.(Has-a 관계)
생성자를 살펴보면, 필드를 초기화 할 때 new 키워드를 사용해 각 차 부품의 자식 클래스(내지 구현체)의 객체를 직접 만들어서 사용하고 있다.
만약 이 상황에서, 프로그래머가 기존에 사용하던 Engine_A라는 클래스 대신 기능이 변경되거나 더 개선된 Engine_S라는 클래스를 새로 만들었고, 이것을 Engine_A 대신에 사용하기로했다고 하자.
프로그래머는 위 코드를 어떻게 고쳐야 할까?
Car라는 클래스에 생성자에 있는 engine = new Engine_A()
를 engine = new Engine_S()
로 고쳐줘야 할 것이다.
그리고 이 작업을 Bus라는 클래스에 대해서도 한번 더 수행해 주어야 한다.
즉, 같은 일을 2번 반복하게 되는 것이다.
public class Car {
private Engine engine;
private Tire tires;
private Handle handle
Car(){
engine = new Engine_S(); // 변경된 부분
tires = new Tire_A();
handle = new Handle_A();
}
}
public class Bus {
private Engine engine;
private Tire tires;
private Handle handle
Bus(){
engine = new Engine_S(); // 변경된 부분
tires = new Tire_A();
handle = new Handle_A();
}
}
변경 후에는 위와 같은 코드가 된다.
위 예시 코드는 클래스가 2개 밖에 없기 때문에 코드 수정 2번으로 Engine_A -> Engine_S의 변경이 가능해졌다.
하지만, 만약 Engine이라는 클래스가 다른 수많은 클래스의 의존되고 있다면, Engine_A를 Engine_S로 변환하는 과정은 굉장히 고통스러울 것이다.
수많은 프로젝트 파일을 뒤져가면서 new Engine_A()
를 new Engine_S()
로 바꿔야 할 것이고, 개중에 무엇 하나라도 빼먹는다면 그 즉시 치명적인 오류가 발생하게 될 수도 있다.
@Component
public class Car {
private Engine engine;
private Tire tires;
private Handle handle
@Autowired
Car(Engine e, Tire t, handle h){
engine = e;
tires = t;
handle = h;
}
}
@Component
public class Bus {
private Engine engine;
private Tire tires;
private Handle handle
@Autowired
Bus(Engine e, Tire t, handle h){
engine = e;
tires = t;
handle = h;
}
}
위 코드에서, 각 클래스에 생성자에 들어가는 파라미터는 이제부터 Spring이 직접 주입해준다.
즉, 개발자는 위 클래스의 생성자에 아무것도 터치할 것이 없는 것이다.
Spring에서 Car 클래스를 사용할 일이 생겼다고 해 보자.
그럼 Spring은 자신이 관리하고 있는 IoC 컨테이너에서 적절한 빈(Bean)을 찾아 스스로 생성자에 집어넣어서 Car 객체를 만들게 된다.
이를 DI(Dependency Injection)라고 부른다. 특정 클래스에 필요한 의존성을 외부에서 주입하는 것이다.
개발자가 해야 할 일은 어떤 클래스의 객체를 빈으로 등록할 것인지 설정만 해 주면 된다.
개발자가 new 키워드를 통해 직접 객체를 생성할 필요 없이, Spring이 대신 객체의 생성과 사용을 제어하게 되는 것이다. (Inversion of Control : 제어의 역전)
이렇게 되면 수정도 매우 용이해진다.
//@Component
public class Engine_A implements Engine {
...
}
@Component
public class Engine_S implements Engine {
...
}
기존 Engine_A클래스에 붙어있던 @Component
어노테이션을 떼 주고, Engine_S 클래스를 구현한 뒤 @Component
어노테이션을 붙여주기만 하면 수정이 끝난다.
Car, Bus 클래스와 같이 Engine 인터페이스(혹은 추상 클래스)에 의존성을 갖고 있는 클래스가 수십, 수백개가 되더라도 위 예시에서 본 것처럼 코드 변경 1번만에 모든 수정사항을 일괄적으로 적용시킬 수 있다.