오늘은 IoC(제어의 역전)와 DI(의존성 주입) 에 대해 알아보도록 하겠습니다.
1. 제어의 역전(IoC, Inversion of Control)
IoC(제어의 역전)는 객체 생성 및 애플리케이션의 흐름 제어를 개발자가 직접 관리하는 대신, 프레임워크나 컨테이너가 이를 관리하는 디자인 원칙입니다.
Spring에서 IoC가 동작하는 방식
전통적인 방식에서는 객체를 직접 생성하고 그 객체들을 조립하여 사용하는 방식이었습니다. 하지만 Spring에서는 객체의 생성과 의존성 주입, 라이프사이클 관리 등을 개발자가 아닌 Spring 컨테이너가 담당합니다. 이로 인해 객체 간의 결합도를 낮추고, 유연한 설계를 할 수 있게 됩니다.
Spring에서 이러한 역할을 하는 것이 IoC컨테이너이고, 보통 ApplicationContext나 BeanFactory가 이 역할을 합니다. 이 컨테이너는 애플리케이션에 필요한 객체(빈, Bean)를 생성하고 관리합니다.
2. 의존성 주입(DI, Dependency Injection)
DI(의존성 주입)는 객체 간의 의존성을 Spring이 관리하며, 필요한 객체를 주입해주는 방식입니다. 즉, 어떤 객체가 다른 객체를 필요로 할 때 개발자가 직접 의존 객체를 생성하는 것이 아니라 Spring이 알아서 주입해주는 것을 말합니다.
DI의 주요 방식
1. 생성자 주입(Constructor Injection): 객체 생성 시 생성자를 통해 의존성을 주입하는 방식입니다.
예시)
@Service
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
2. 세터 주입(Setter Injection): 객체 생성 후 세터 메서드를 통해 의존성을 주입하는 방식입니다.
예시)
@Service
public class MyService {
private MyRepository myRepository;
@Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
3. 필드 주입(Field Injection): 직접 필드에 의존성을 주입하는 방식으로, Spring에서 가장 간단한 형태입니다. 다만 테스트하기 어려워 가급적이면 생성자 주입을 권장합니다.
예시)
@Autowired
private MyRepository myRepository;
DI의 장점:
- 객체 간의 결합도가 낮아져 유연한 코드 작성이 가능
- 유지보수가 쉬워짐
- 객체 관리가 편리해짐 (Spring 컨테이너가 관리)
3. IoC와 DI의 관계
IoC는 객체의 제어를 개발자가 아닌 Spring 컨테이너에 맡기는 것이고, DI는 이 과정에서 객체 간의 의존성을 주입해주는 방식입니다. 즉, Spring에서 IoC를 구현하는 주요 메커니즘이 DI라고 볼 수 있습니다.
이 두 개념이 결합되어 Spring은 유연하고 테스트 가능한 구조를 제공합니다. Spring을 사용하면 필요한 객체를 직접 생성하거나 관리할 필요가 없이, Spring 컨테이너가 자동으로 객체를 관리하고 필요한 곳에 주입해 줍니다.
위의 내용을 쉽게 이해하기 위한 용어 정리
4. Spring 컨테이너란?
Spring의 IoC 컨테이너는 대표적으로 AppliationContext 와 BeanFactory 가 있습니다.
Spring 컨테이너는 Spring 프레임워크가 제공하는 핵심 구성 요소 중 하나로, 애플리케이션에서 사용할 객체(빈, Bean)를 생성하고, 관리하며, 주입하는 역할을 담당합니다. 간단히 말해, 애플리케이션의 객체 생명주기 및 의존성 관리를 전담하는 "객체 공장" 이라고 볼 수 있습니다.
주요 기능:
- 객체 생성 및 관리: 개발자가 애플리케이션에 필요한 객체를 직접 생성하지 않아도, 컨테이너가 대신 객체를 생성해 줍니다. 예를 들어, 클래스에 @Component, @Service, @Repository, @Controller와 같은 애너테이션을 붙이면, Spring 컨테이너는 해당 클래스를 애플리케이션 실행 시 빈으로 등록하고, 필요할 때 자동으로 객체를 생성해 줍니다.
- 의존성 주입(DI): Spring 컨테이너는 각 객체가 필요로 하는 다른 객체들을 자동으로 연결(주입)해 줍니다. 이 과정에서 개발자는 객체를 직접 생성하고 연결하는 코드를 작성할 필요가 없습니다.
- 객체 생명주기 관리:Spring 컨테이너는 객체의 생성부터 소멸까지의 생명주기를 관리합니다. 객체가 언제 생성되고, 언제 소멸될지, 그리고 필요할 때마다 객체를 재사용할지(싱글톤 패턴) 등을 관리해줍니다.
4.1 Bean이란 무엇인가?
Bean(빈)은 Spring IoC 컨테이너가 관리하는 객체를 뜻합니다. 즉, Spring에서 빈이란 Spring 컨테이너가 생성하고 관리하는 모든 객체를 의미합니다.
빈의 정의:
- 빈(Bean)은 Spring 컨테이너 안에 등록된 객체로, 애플리케이션에서 필요한 인스턴스들을 나타냅니다.
- Spring은 빈을 생성하고, 의존성을 주입하며, 필요에 따라 관리합니다.
- 빈은 개발자가 직접 생성하는 것이 아니라, Spring 컨테이너가 객체를 대신 생성하고 필요한 곳에
주입합니다.
빈 등록 방식:
1. XML 설정: 전통적으로 빈을 XML 파일에서 설정하던 방식입니다.
<bean id="myBean" class="com.example.MyBean" />
이 XML을 통해 MyBean 이라는 클래스의 객체가 Spring 컨테이너에 의해 관리됩니다.
2. 애너테이션 설정: 현재는 애너테이션 기반 방식이 더 많이 사용됩니다.
@Component
public class MyBean {
// ...
}
이 코드에서 @Component 애너테이션은 Spring에게 MyBean 클래스를 빈으로 등록하도록 지시합니다.
3. Java Config: 자바 설정 파일을 통해 빈을 정의할 수도 있습니다.
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
이 방식은 애플리케이션의 설정을 Java 코드로 명시적으로 관리할 수 있게 해줍니다.
빈의 생명주기:
Spring 컨테이너가 빈을 관리하는 과정은 생성, 의존성 주입, 초기화, 사용, 소멸 의 순서로 이루어집니다.
1. 생성: Spring 컨테이너가 빈의 인스턴스를 생성합니다.
2. 의존성 주입: 생성된 빈에 필요한 의존성을 주입합니다. 이를 통해 빈이 다른 객체와 연결됩니다.
3. 초기화: 빈이 초기화되고 필요한 설정 작업을 수행합니다.
4. 사용: 빈이 사용되며, Spring 컨테이너는 이 빈을 계속 관리합니다.
5. 소멸: 애플리케이션이 종료될 때 컨테이너는 빈을 소멸시킵니다.
Bean의 Scope (범위):
빈의 범위(Scope)는 해당 빈이 Spring 컨테이너 내에서 어떻게 관리될지를 결정합니다. 기본적으로 빈은 싱글톤(Singleton) 범위로 관리되지만, 필요에 따라 다른 범위로 설정할 수도 있습니다.
1. Singleton: 기본 스코프로, 애플리케이션 내에서 단 하나의 빈 인스턴스만 생성됩니다.
모든 요청은 같은 빈을 공유합니다.
@Scope("singleton")
2. Prototype: 각 요청마다 새로운 인스턴스가 생성됩니다.
@Scope("prototype")
3. Request, Session, Application, WebSocket: 주로 웹 애플리케이션에서 사용되며, 각각 요청, 세션, 애플리케이션, 웹소켓 범위로 빈을 관리합니다.
4.2 ApplicationContext 구현체
예시)
// ScheduleService.java
@Service
public class ScheduleService {
private final ScheduleRepository scheduleRepository;
// 의존성 주입
public ScheduleService(ScheduleRepository scheduleRepository) {
this.scheduleRepository = scheduleRepository;
}
}
// ScheduleController.java
@Controller
public class ScheduleController {
private final ScheduleService scheduleService;
// 의존성 주입
public ScheduleController(ScheduleService scheduleService) {
this.scheduleService = scheduleService;
}
// 컨트롤러에서 서비스 사용
}
여기서 ScheduleService 와 ScheduleController 는 각각 @Service, @Controller 애너테이션이 붙어있고, Spring 컨테이너는 이들을 빈으로 등록한 후, 의존성을 주입해서 객체들을 관리합니다.
4.3 BeanFactory란?
BeanFactory는 Spring의 IoC 컨테이너 중 하나로, Spring 에서 빈(Bean) 객체를 생성하고 관리하는 최상위 인터페이스입니다.
주요 기능:
- 빈(bean) 관리: 애플리케이션에서 사용할 객체들을 빈으로 등록하고, 필요할 때마다 이를 생성하거나 재사용하는 역할을 합니다.
- 지연 초기화(Lazy Initialization): BeanFactory는 요청이 있을 때 빈을 생성하는 지연 초기화 방식을 사용합니다. 즉, 필요할 때까지 객체를 생성하지 않고 대기합니다. 이로 인해 메모리 사용을 최소화하고, 애플리케이션의 초기 로딩 속도를 개선할 수 있습니다.
하지만, 일반적으로 Spring 애플리케이션에서는 BeanFactory보다 ApplicationContext를 더 많이 사용합니다. ApplicationContext는 BeanFactory를 확장한 인터페이스로, 더 많은 기능을 제공합니다.
(예: 이벤트 처리, 메시지 소스 처리 등)
BeanFactory를 사용하는 상황
- 리소스가 제한된 환경에서 메모리 사용을 최적화해야 할 때
- 매우 기본적인 빈 관리만 필요할 때
BeanFactory예시)
// BeanFactory를 이용한 빈 설정 예시
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
MyBean myBean = (MyBean) factory.getBean("myBean");
위 코드는 XML 파일에서 빈 정의를 읽어 BeanFactory가 관리하게 만드는 방법입니다. 이 방식은 최근 잘 쓰이지 않고, 대신 ApplicationContext가 널리 사용됩니다.
'Spring' 카테고리의 다른 글
Spring - H2 Database (0) | 2024.11.08 |
---|---|
Spring - @Transactional (1) | 2024.11.05 |
Spring - REST API 와 RESTful (0) | 2024.10.18 |
Spring - JPA, Entity, 영속성 컨텍스트 (4) | 2024.10.10 |
Spring - JBDC (3) | 2024.09.27 |