🎈 제어의 역전 IoC(Inversion of Control)
제어의 역전 Inversion of Control (IoC)는 소프트웨어 디자인 패턴 중 하나입니다. IoC는 제어의 역전이라고도 불리며, 객체 간의 의존성을 해결하기 위한 개념입니다.
기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행합니다. 구현 객체가 프로그램의 제어 흐름을 스스로 조종하는 개념입니다.
반면, 역할과 구현을 분리하게되면 구현 객체는 자신의 로직을 실행하는 역할만 담당하기 때문에 프로그램 흐름을 구성 영역이 가져가게됩니다. 구성 영역이 프로그램 제어 흐름 대한 권한을 갖게 된다면 사용 영역의 클래스들은 필요한 인터페이스들은 호출하지만 어떤 구현객체들이 실행되는지는 알 수가 없습니다.
이러한 의존성을 해결하기 위해 제어의 흐름을 역전시키는 개념입니다. 기존에는 객체가 직접 의존하는 객체를 생성하고 관리하는 주체였지만, IoC에서는 객체가 직접 의존하는 객체를 생성하고 관리하는 책임을 외부 컨테이너 또는 프레임워크에게 위임합니다.
즉, IoC는 객체의 생명주기와 의존성 관리를 외부에 위임함으로써 코드의 결합도를 낮추고 유연성과 재사용성을 향상시킵니다. 이를 통해 소프트웨어의 확장성과 테스트 용이성을 개선할 수 있습니다.
IoC의 구현 방법으로는 의존성 주입(Dependency Injection)이 주로 사용됩니다. 의존성 주입은 객체가 필요로 하는 의존성을 외부에서 주입받는 방식으로 이루어집니다. 이를 통해 객체 간의 결합도를 줄이고 테스트하기 쉬운 코드를 작성할 수 있습니다.
의존성 주입(Dependency Injection, DI)은 객체 간의 의존 관계를 개발자가 직접 관리하지 않고, 외부에서 의존하는 객체를 주입하여 사용하는 디자인 패턴입니다. DI는 객체 간의 결합도를 낮추고 유연하고 재사용 가능한 코드를 작성할 수 있도록 도와줍니다.
일반적으로 객체 간의 의존 관계는 하나의 객체가 다른 객체를 생성하거나 참조하여 사용하는 것으로 나타납니다. 이러한 의존 관계는 코드 내에서 강한 결합도를 만들어내고, 객체의 생성과 관리를 담당하는 클래스가 다른 객체들에 대한 정보를 갖고 있어야 한다는 문제를 발생시킵니다. 이로 인해 코드의 유연성과 재사용성이 저하되는 문제가 발생할 수 있습니다.
DI는 이러한 문제를 해결하기 위해 의존하는 객체를 외부에서 주입하는 방식을 채택합니다. 주입된 객체는 해당 객체가 필요한 시점에 사용됩니다. 주로 생성자(Constructor) 주입, Setter 주입, 인터페이스 주입 등의 방식을 사용합니다.
DI의 주요 이점은 다음과 같습니다:
- 결합도 감소: 객체 간의 의존 관계를 외부에서 관리하기 때문에 객체들 간의 결합도가 낮아집니다. 이로 인해 하나의 객체 수정이 다른 객체에 미치는 영향을 최소화하고, 코드의 유지 보수성이 향상됩니다.
- 재사용성과 확장성: DI를 통해 의존하는 객체를 외부에서 주입받기 때문에 해당 객체의 변경 없이 쉽게 다른 객체와 조합하여 재사용하거나 기능을 확장할 수 있습니다.
- 테스트 용이성: 의존 객체를 모의(Mock) 객체로 대체하여 단위 테스트를 수행할 수 있습니다. 의존 객체의 동작을 외부에서 주입하여 테스트를 진행할 수 있기 때문에 테스트 용이성이 높아집니다.
Spring Framework는 DI를 통해 객체 간의 의존 관계를 관리하고 주입하는 기능을 제공합니다. 이를 통해 Spring에서는 객체 지향적인 애플리케이션을 개발할 수 있으며, 유연하고 확장 가능한 코드를 작성할 수 있습니다.
OrderServiceImple은 DiscountPolicy 인터페이스에 의존합니다. 실제 어떤 구현객체가 사용될지는 모르는 상황입니다. 의존관계는 “정적인 클래스 의존 관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계” 등을 분리해서 생각해야 합니다.
- 정적인 클래스 의존 관계
클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단할 수 있습니다. 정적인 의존관계는 애플리케이션을 실행하지 않아도 분석할 수 있습니다. 클래스 다이어그램을 살펴보면 OrderServiceImpl은 MemberRepository, DiscountPolicy를 참조하고 있습니다. 이러한 클래스 의존관계 만으로는 실제 어떤 객체가 OrderServiceImpl에 주입되는 지 알기 어렵습니다. - 동적인 객체 인스턴스 의존관계
AppConfig에서 OrderService()가 생성될 때 매개변수로 memberRepository()와 discountPolicy() 인스턴스를 담아주게 되는데, 인스턴스 다이어그램은 정적인 것이 아니라 애플리케이션 실행 시 동적으로 변하게 됩니다. 애플리케이션 “실행 시점(런타임)”에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 “의존 관계 주입”이라고 합니다.의존 관계 주입을 사용하면 클라이언트 코드를 변경하지 않고 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있습니다. 정적인 클래스 의존 관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있습니다. - 객체 인스턴스를 생성하고, 그 참조값을 전달해서 연결됩니다. 의존 관계 주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있습니다.
정적인 클래스 의존관계에서는 어떤 객체가 주입될 지 알기가 힘들기 때문에 애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존관계
- IoC 컨테이너 , DI 컨테이너DI 컨테이너는 Dependency Injection 컨테이너로, 객체 간의 의존성을 주입하는 기능을 담당합니다. DI 컨테이너는 IoC 컨테이너의 일부로, 객체가 필요로 하는 의존성을 외부에서 주입해주는 역할을 합니다. 즉, 객체가 직접 의존하는 객체를 생성하고 관리하는 대신 DI 컨테이너에게 의존성을 위임하여 객체 간의 결합을 낮추고 유연한 코드를 작성할 수 있습니다.
Spring Framework에서는 ApplicationContext라는 IoC/DI 컨테이너를 제공합니다. ApplicationContext는 애플리케이션의 빈(Bean) 객체를 생성하고 관리하며, 의존성 주입을 수행합니다. 개발자는 ApplicationContext를 사용하여 필요한 빈을 가져오고, 의존성을 주입받을 수 있습니다.
AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라고합니다. 의존관계 주입에 초점을 맞추어 최근에는 주로 DI 컨테이너라고 하며, 어셈블러, 오브젝트 팩토리 등으로 불리기도 합니다.
//AppConfig에 MemberService와 OrderServive의 역할은 잘 보여지지만 MemberRepository와 DiscountPolicy의 역할을 같이 구현
//아래와 같은 코드 변경의 장점은 각 메서드의 역할명만 보아도 역할을 알 수 있으며, 역할과 구현 클래스들을 한눈에 알 수 있습니다.
//애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악이 가능
public class AppConfig {
//↓ 클라이언트 (주문생성 -회원 ID)
1. public MemberService memberService() {
return new MemberServiceImple(memberRepository());
}
//↓ 회원 저장소 역할 (메모리 회원 저장소 / DB 회원 저장소)
2. public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
//↓ 주문 서비스 역할 (회원 조회 / 할인 적용)
3. public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
//↓ 할인 정책 역할 (정액 할인 정책 / 정률 할인 정책)
4. public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
- memberService(): MemberService 빈을 생성하는 메서드로, MemberServiceImple 객체를 생성하고 의존하는 memberRepository() 빈을 주입하여 반환합니다.
- memberRepository(): MemberRepository 빈을 생성하는 메서드로, MemoryMemberRepository 객체를 생성하여 반환합니다.
- orderService(): OrderService 빈을 생성하는 메서드로, OrderServiceImpl 객체를 생성하고 의존하는 memberRepository()와 discountPolicy() 빈을 주입하여 반환합니다.
- discountPolicy(): DiscountPolicy 빈을 생성하는 메서드로, 주석 처리된 FixDiscountPolicy 객체 대신 RateDiscountPolicy 객체를 생성하여 반환합니다.
'[ BACKEND] > Spring' 카테고리의 다른 글
[SPRING] Redirect와 Fofrward (0) | 2023.07.19 |
---|---|
[SPRING] 서블릿과 JSP (0) | 2023.07.18 |
[SPRING] 관심사를 분리 ✅ (0) | 2023.07.14 |
[SPRING] 💡JPA (0) | 2023.07.13 |
[SPRING] HTTP 요청과 응답 (1) | 2023.07.10 |