IoC
Inversion of Control의 약자로, 제어의 역전이라고 한다.spring에서는 빈의 생성, 의존관계 설정, 사용, 제거 등의 작업을 스프링 컨테이너에서 담당한다.이 때 스프링 컨테이너가 제어 역할을 하기 때문에 IoC라고 부르고, 스프링 컨테이너를 IoC 컨테이너라고 한다.
IoC 컨테이너
스프링에서는 IoC를 담당하는 컨테이너를 빈 팩토리, DI 컨테이너, 애플리케이션 컨텍스트라고 부른다.
오브젝트의 생성과 오브젝트 사이의 런타임 관계를 설정하는 DI 관점으로 보면, 컨테이너를 빈 팩토리 또는 DI 컨테이너라고 부른다.
그러나 스프링 컨테이너는 단순한 DI 작업보다 더 많은 일을 하는데, DI를 위한 빈 팩토리에 여러 가지 기능을 추가한 것을 애플리케이션 컨텍스트라고 한다.
빈 팩토리
스프링 컨테이너의 최상위 인터페이스. 스프링 빈을 관리하고 조회하는 역할을 담당한다.
애플리케이션 컨텍스트
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher,
ResourcePatternResolver {
애플리케이션 컨텍스트는 빈 팩토리 기능을 모두 상속 받아서 제공한다.
빈 팩토리 interface의 서브 interface들을 extends하며, 빈 팩토리의 기능 이외의 다양한 기능을 제공한다.
설정 메타 정보
IoC 컨테이너의 역할 중 가장 기초적인 역할은 오브젝트 및 관리이다.
이때 관리하는 오브젝트를 Bean이라 부른다.
설정 메타 정보는 이 Bean들을 어떻게 동작하게 할것인가에 관한 정보이다.
애노테이션 기반 설정과 XML 기반 설정 방법이 있다.
//애노테이션
@Configuration
public class Config {
@Bean
public UserService userService() {
return new UserServiceImpl(userRepository());
}
}
DI
의존 관계를 외부에서 결정하는 것. 스프링에서는 외부의 대상이 IoC 컨테이너가 되고, 빈을 알아서 주입해 준다.
DI 구현 방법
필드 주입(@Autowired)
@Service
public class BurgerService {
@Autowired
private BurgerRecipe burgerRecipe;
}
장점
- 사용하기 편하다.
단점
- 단일 책임 원칙 위반 가능성이 커진다.
- @Autowired 선언만 하면 되므로 의존성을 주입하기 쉽고, 하나의 클래스가 많은 책임을 갖게 될 가능성이 높다.
- 생성자 주입에 비해 의존 관계를 한 눈에 파악하기 어렵다.
- DI 컨테이너와의 결합도가 커지고, 테스트하기 어렵다.
- 불변성을 보장할 수 없다.
- 순환 참조가 발생할 수 있다.
수정자 주입
@Service
public class BurgerService {
private BurgerRecipe burgerRecipe;
@Autowired
public void setBurgerRecipe(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
장점
- 선택적인 의존성을 사용할 수 있다.
단점
- 선택적인 의존성을 사용할 수 있다는 것은 BurgerService에 모든 구현체를 주입하지 않아도 burgerRecipe 객체를 생성할 수 있고, 객체의 메소드를 호출할 수 있다. 즉, 주입받지 않은 구현체를 사용하는 메소드에서 NPE가 발생한다.
- 순환 참조 문제가 발생할 수 있다.
생성자 주입
@Service
public class BurgerService {
private BurgerRecipe burgerRecipe;
@Autowired
public BurgerRecipe(BurgerRecipe burgerRecipe) {
this.burgerRecipe = burgerRecipe;
}
}
생성자에 @Autowired 어노테이션을 붙여 의존성을 주입받을 수 있으며, 가장 권장되는 주입 방식이다.
장점
- 의존 관계를 모두 주입 해야만 객체 생성이 가능하므로 NPE를 방지할 수 있다.
- 불변성을 보장할 수 있다.
- 순환 참조를 컴파일 단계에서 찾아낼 수 있다.
순환참조
서로 다른 여러 빈들이 서로를 참조하고 있음을 의미한다.
서로 서로 호출을 반복하면서 끊임없이 반하다가 결국 StackOverflowError를 발생시키고 죽는다.
필드 주입이나 수정자 주입은 객체 생성 후 비즈니스 로직 상에서 순환 참조가 일어나기 때문에 컴파일 단계에서 순환 참조를 잡아낼 수 없다.
예상 질문
IoC 컨테이너란?
스프링 애플리케이션에서는 객체(빈)의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 스프링 컨테이너가 담당하는데, 이를 IoC 컨테이너라고 한다.
IoC 컨테이너의 장점은?
스프링 애플리케이션의 객체(빈)을 IoC 컨테이터가 관리해줌으로써 개발자의 부담이 줄고 비즈니스 로직에 더욱 집중할 수 있다는 장점이 있다.
DI란?
DI는 객체(빈)들 간의 의존관계를 외부에서 결정하고 주입하는 것이다. 생성자 삽입, 수정자 삽입, 필드 주입 방법이 있다.
DI의 장점은?
의존성이 줄어든다.
재사용성이 높은 코드가 된다.
테스트하기 좋은 코드가 된다.
가독성이 높아진다.
DI의 종류는?
DI는 생성자 삽입, 수정자 삽입, 필드 주입이 있다.
생성자 주입은 생성자에 @Autowired 어노테이션을 붙여 의존성을 주입받는 방법으로
생성자 호출시점에 딱 1번만 호출되는 것을 보장하며 불변, 필수 의존관계에 사용한다.
수정자 주입은 setter를 사용한 주입방법으로
선택, 변경 가능성이 있는 의존관계에 사용되며 빈을 선택적으로 주입이 가능하다.
필드 주입은 변수 선언부에 @Autowired를 사용하는 것으로
외부에서 변경이 불가능하여 테스트 하기 힘들다. DI 프레임워크 없이는 작동하기 힘들며, 주로 애플리케이션과 관계없는 테스트코드나 @Configuration 같은 스프링 설정 목적으로 사용한다.
순환 참조가 무엇이고 언제 발생하는가?
순환 참조란 서로 다른 여러 빈들이 서로를 참조하고 있음을 의미한다.
필드 주입이나 수정자 주입은 객체 생성 후 비즈니스 로직 상에서 순환 참조가 일어나기 때문에 컴파일 단계에서 순환 참조를 잡아낼 수 없다.
반면에 생성자 주입을 사용하면 스프링 컨테이너가 빈을 생성하는 시점에 순환 참조를 확인하기 때문에 컴파일 단계에서 순환 참조를 잡아낼 수 있다
생성자 주입을 사용해야 하는 이유는?
의존관계를 모두 주입하지 않은 경우에 객체를 생성할 수 없기 때문에 NPE를 방지할 수 있다.
final 키워드를 사용할 수 있어 불변성을 보장할 수 있다.
생성자 주입은 컴파일 단계에서 순환 참조를 잡아 낼 수 있다.
Spring IoC/DI의 동작 과정은?
IoC(제어의 역전)은 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것으로 코드의 최종 호출은 개발자가 제어하는 것이 아닌 프레임워크의 내부에서 결정된 대로 이루어진다.
DI(의존관계 주입)은 스프링 프레임워크에서 지원하는 IoC의 형태로 객체(빈) 사이의 의존관계를 빈 설정 정보를 바탕으로 DI 컨테이너가 자동으로 연결한다.
Autowiring 동작 과정은?
스프링 서버가 올라갈 때 애플리케이션 컨텍스트가 @Bean이나 @Service, @Controller 등 어노테이션을 이용하여 등록한 스프링 빈을 생성하고, @Autowired 어노테이션이 붙은 위치 또는 생성자, 수정자를 통해 주입한다.
DI와 IoC의 차이는?
DI는 의존관계를 어떻게 가질 것인가에 대한 문제고, IoC는 누가 소프트웨어의 제어권을 갖고 있느냐의 문제다.
IoC 컨테이너가 빈을 생성할 때 빈들간의 의존관계를 DI를 통해 해결한다.
DI는 IoC 사용을 필수로 요구하지 않는다는 점을 주의해야 한다.
reference
'면접' 카테고리의 다른 글
Spring AOP란? (0) | 2023.08.07 |
---|---|
Spring MVC (0) | 2023.08.02 |
기술 질문 (0) | 2023.08.01 |