IoC(Inversion of Control, 제어의 역전)에서 "제어"는 객체의 생성과 그 객체 간의 의존성 관리를 의미합니다. 이를 더 구체적으로 설명하면 다음과 같습니다
전통적인 객체 지향 프로그래밍(OOP)에서의 제어 흐름
전통적인 방식에서는 객체가 다른 객체를 필요로 할 때, 직접적으로 그 객체를 생성하거나, 메서드를 호출하여 의존성을 해결합니다. 예를 들어, 클래스 A가 클래스 B를 필요로 한다면, A는 B를 직접 생성하거나 B를 생성해 줄 수 있는 어떤 방식으로 처리합니다. 이 경우 객체 A는 다음과 같은 역할을 합니다:
- 객체 B를 생성하거나 가져오는 책임: A는 B를 필요로 할 때, 스스로 B를 생성하거나 적절한 방식으로 얻어옵니다.
- 의존성 관리: A는 B와의 의존 관계를 직접 관리합니다.
이런 방식에서는 애플리케이션의 흐름과 객체 간의 제어가 코드 내부에 강하게 결합되어 있어, 변경이나 테스트가 어려워질 수 있습니다.
제어의 역전(Inversion of Control)
IoC는 이 전통적인 제어 흐름을 뒤집는 개념입니다. 여기서 "역전된" 제어는 객체의 생성과 의존성 관리에 대한 책임이 객체 자신이 아닌 외부 컨테이너나 프레임워크로 넘어가는 것을 의미합니다.
- 객체의 생성과 의존성 주입: 객체 A는 더 이상 직접 객체 B를 생성하거나 관리하지 않습니다. 대신, A는 외부에서 B를 주입받습니다. 이 작업을 Spring 같은 IoC 컨테이너가 담당합니다.
- 제어의 주체가 바뀜: 객체 A가 객체 B의 생성을 제어하는 것이 아니라, IoC 컨테이너가 이 제어를 담당합니다. A는 그저 필요한 B를 전달받기만 하면 됩니다.
전통적방식 IoC방식 비
// 전통적인 방식
class A {
private B b;
public A() {
this.b = new B(); // A가 B를 직접 생성함
}
}
// IoC 방식
class A {
private B b;
// 생성자 주입
public A(B b) {
this.b = b; // 외부에서 B를 주입받음
}
}
IoC 컨테이너는 A의 인스턴스를 생성할 때, A가 필요한 B를 찾아서 주입해줍니다. A는 더 이상 B의 생성에 대해 알 필요가 없고, 이를 신경 쓰지 않습니다.
IoC의 이점
- 유연성 증가: 객체의 생성과 의존성 관리가 외부로 이동하면서 코드의 유연성이 증가하고, 객체 간의 결합도가 낮아집니다.
- 테스트 용이성: 객체를 테스트할 때, 실제 구현 대신 테스트 목업(Mock)을 주입할 수 있어 단위 테스트가 쉬워집니다.
- 유지보수성 향상: 코드의 의존성 관리가 단순해지면서 유지보수가 용이해집니다.
스프링의 경우
Step 1: 컴포넌트 어노테이션 추가
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Repository;
@Service // 스프링이 관리할 서비스 객체로 표시
public class BookService {
private final BookRepository bookRepository;
// 생성자 주입 방식
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository; // 외부에서 주입 받음
}
public void saveBook(Book book) {
bookRepository.save(book);
}
}
@Repository // 스프링이 관리할 레포지토리 객체로 표시
public class BookRepository {
public void save(Book book) {
// 책 저장 로직
}
}
Step 2: 스프링 컨테이너 설정 (ApplicationContext)
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
BookService bookService = context.getBean(BookService.class);
bookService.saveBook(new Book("스프링 입문서"));
}
}
Step 3: 스프링 설정 클래스
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example") // 컴포넌트 스캔으로 BookService와 BookRepository 자동 등록
public class AppConfig {
// 추가적인 설정이 필요한 경우 여기서 빈(Bean)을 정의할 수 있습니다.
}
설명
- @Service와 @Repository 어노테이션: 이 어노테이션들은 해당 클래스가 스프링 컨테이너에 의해 관리되어야 하는 컴포넌트임을 나타냅니다. @Service는 비즈니스 로직을 수행하는 클래스에, @Repository는 데이터 접근을 담당하는 클래스에 붙입니다.
- 의존성 주입: BookService는 이제 BookRepository를 직접 생성하지 않고, 스프링 컨테이너가 BookRepository 객체를 주입해 줍니다. 이는 생성자 주입 방식으로 이루어지며, 스프링이 BookService 객체를 생성할 때 자동으로 BookRepository를 주입해 줍니다.
- 스프링 컨테이너(ApplicationContext): ApplicationContext는 스프링 컨테이너의 핵심으로, 설정된 컴포넌트를 스캔하여 관리하고, 필요한 경우 자동으로 의존성을 주입합니다.
이 방식으로 BookService는 BookRepository가 어떻게 생성되는지 알 필요가 없으며, 오직 주입받은 BookRepository를 사용하기만 하면 됩니다. 이는 코드의 결합도를 낮추고, 테스트와 유지보수가 훨씬 용이해집니다.
'스프링 공부' 카테고리의 다른 글
스프링 프레임 워크 구조 정리 (1) | 2024.08.09 |
---|---|
Spring 스프링 프레임워크 - 의존관계 주입(Dependency Injection, DI ) (0) | 2024.08.08 |
Spring Web MVC/ 스프링 RestAPI를 위한 어노테이션 (0) | 2024.08.07 |
스프링 빈 Spring Bean 정리 (0) | 2024.08.07 |
Spring Context 스프링 컨텍스트? 스프링 컨테이너? 개념 정리 (0) | 2024.08.06 |
댓글