이 글은 백기선님의 Inflearn 강좌 예제로 배우는 스프링 입문(개정판)을 정리한 내용입니다.
DI는 의존성 주입
을 의미한다. 의존성 주입이란 하나의 객체 A가 다른 객체 B를 사용(의존)해야 할 때, A의 코드 내부에서 B를 만드는 것이 아니라, 외부(IoC 컨테이너)에서 B를 만들고 생성자 혹은 setter
메서드 등을 활용해 A 내부로 주입하는 것을 의미한다. 이러한 방식은 모듈 간의 결합을 낮춰 수정이 발생하였을 때 코드 변경지점을 최소화 할 수 있다는 장점이 있다.
IoC는 제어권 역전으로, 일반적으로 개발자가 프로그램의 흐름을 제어하는 것이 아니라, 프레임워크가 프로그램의 흐름을 주도하는 것을 의미한다. 일반적인 프로그램의 흐름은 다음과 같다.
객체 생성(A)
-> 내부에서 의존성 객체 생성(B)
-> 의존성 객체(B)의 메서드를 호출
class OwnerController {
private OwnerRepository repository = new OwnerRepository();
}
그러나, IoC가 적용된 경우에는 프로그램의 흐름이 다음과 같은 형태로 진행된다. 스프링에서는 모든 의존성 객체(Bean
)를 스프링 컨테이너에서 직접 관리하며, 필요한 곳에 주입해주는 역할을 한다.
객체 생성(A, B)
-> 의존성 객체 주입(B)
-> 의존성 객체(B)의 메서드 호출
class OwnerController {
private final OwnerRepository repository;
public OwnerController(OwnerRepository repository) {
this.repository = repository;
}
}
스프링 컨테이너는 스프링 프레임워크에서 사용되는 의존성 객체(이하 Bean
)들을 생성하고 적절한 곳에 주입하는 등 Bean
의 생애주기를 관리하는 역할을 한다. 스프링 컨테이너의 종류는 BeanFactory
와 ApplicationContext
2가지가 있다.
BeanFactory
는 단순한 형태의 스프링 컨테이너로, Bean
객체를 등록, 생성, 조회, 반환 하는 기본적인 작업만 수행한다. BeanFactory
는 Bean
객체가 실제로 요구되는 시점에 이를 생성하는 Lazy Loding
이 특징이다.
ApplicationContext
는 BeanFactory
의 확장판으로,Transaction 관리
, AOP(Proxy) 적용
과 같이 부가적인 기능들을 제공한다. 일반적으로 스프링 컨테이너로 ApplicationContext
가 사용된다.
BeanFactory
와 달리, ApplicationContext
는 생성되는 시점에 Bean
객체를 모두 생성하는 Pre-loading
방식을 사용한다.
Bean은 IoC 컨트롤러가 생성/관리하여 자동으로 DI를 제공하는 객체를 의미한다. 클래스를 Bean으로 등록하기 위한 방법은 크게 2가지가 있다.
@ComponentScan
어노테이션은 하위 패키지를 탐색하며 @Component
어노테이션이 붙어있는 클래스를 찾아 Bean
으로 등록하라고 지시하는 역할을 한다.스프링 어플리케이션의 메인 함수 위에 붙어있는 @SpringBootApplication
어노테이션에는 @ComponentScan
어노테이션이 포함되어 있다.
@Component
어노테이션 포함된 어노테이션은 대표적으로 @Controller
, @Repository
, @Service
, @Configuration
등이 있다.
xml 혹은 자바 설정파일을 사용하여 직접 Bean을 등록할 수 있다. 동일한 인터페이스 상에서 구현체가 종종 바뀌는 경우, 자바 설정 파일에 직접 등록하는 방식을 사용하면 변경지점을 최소화할 수 있어서 매우 유용하다.
@Configuration
public class SampleConfig {
@Bean
public SampleController sampleController() {
return new SampleController();
}
}
@Autowired
는 스프링 컨테이너에게 의존성을 주입해달라고 지시하는 어노테이션이다. @Autowired
를 사용하는 방법은 크게 3가지가 있다.
필수적으로 사용되는 객체를 생성 시점에 강제할 수 있기 때문에, 생성자를 사용하여 의존성을 주입하는 방법은 Spring 공식 레퍼런스에서 권장하는 방법이다. 단, 순환참조가 발생하는 경우는 예외적으로 아래의 방법을 활용하여 해결할 수 있다.
Spring 4.3버전 부터는 Bean으로 등록된 객체에 유일한 생성자에 매개변수들이 Bean으로 등록되어 있다면, IoC 컨트롤러에서 @Autowired
어노테이션을 생략해도 된다.
@Controller
class OwnerController {
private final OwnerRepository owners;
@Autowired
public OwnerController(OwnerRepository owners) {
this.owners = owners;
}
}
의존성 주입을 원하는 필드에 @Autowired
를 추가하는 방식으로도 사용할 수 있다. 테스트 코드를 작성할 때는, 코드의 간결성을 위해 이 방식을 활용하기도 한다.
@Controller
class OwnerController {
@Autowired
private OwnerRepository owners;
...
}
@Controller
class OwnerController {
private OwnerRepository owners;
@Autowired
public void setOwners(OwnerRepository owners) {
this.owners = owners;
}
}