스프링 없이 IoC, DI 구현

HUSII·2023년 1월 18일
0

스프링

목록 보기
2/8

스프링 컨테이너를 이용하면 객체 지향적으로 애플리케이션을 설계할 수 있다.

인터페이스와 구현 객체를 스프링 컨테이너에 등록한다.

여기서 스프링 컨테이너에 들어가는 객체들을 스프링 빈(Bean)이라고 한다.

스프링 컨테이너는 스프링 빈들의 생명주기를 관리한다.


예를 들어보자.

멤버저장소라는 인터페이스가 있고,
멤버저장소의 구현 객체가 두가지 있다고 가정하자.

(일단 스프링 컨테이너 없이 객체들을 설계해본다.)

public interface MemberRepository {
	void save();
}

public class MemberRepository1 implements MemberRepository {
	@Override
    void save();
}

public class MemberRepository2 implements MemberRepository {
	@Override
    void save();
}

이를 실행하기 위한 코드

public class Test {
	MemberRepository memberRepository = new MemberRepository1();        
    
    ```
}

MemberRepository1말고 MemberRepository2를 구현한다고 해보자.

public class Test {
	//MemberRepository memberRepository = new MemberRepository1();       
    MemberRepository memberRepository = new MemberRepository2();        
    
    ```
}

우리가 기대하는 의존관계

실제 의존관계

구현 객체를 의존하지 않고, 인터페이스만을 의존하여 설계를 하려고 했지만,
구현 객체를 의존하게 됐다.

이는 SOLID(좋은 객체지향 설계의 5가지 원칙)에서 OCP와 DIP를 지키지 않은 것이다.

OCP(Open/closed principle): 개방-폐쇄 원칙
소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
-> 변경할때 코드를 바꿔야 했음. (MemberRepository1 -> MemberRepository2)

DIP(Dependency inversion principle): 의존관계 역전 원칙
프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.”
-> 구체화도 의존하고 있다. ( new MemberRepository1(); )


이를 해결하기 위해 인터페이스만 의존하도록 코드를 변경하자.
(AppConfig 클래스 추가)

public class AppConfig {
	public Test test() {
    	return new Test(memberRepository());
    }
    
    public MemberRepository memberRepository() {
    	return new MemberRepository1();
    }
}

public class Test {
	MemberRepository memberRepository;
    
    Test(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
}

AppConfig에 있는 test 함수를 실행하면 Test 클래스에 있는 MemberRepository에 MemberRespository1이 주입된다.

실제 의존관계


이때 MemberReopsitroy2로 구현한다고 해보자.

public class AppConfig {
	public Test test() {
    	return new Test(memberRepository());
    }
    
    public MemberRepository memberRepository() {
    	//return new MemberRepository1();
    	return new MemberRepository2();
    }
}

public class Test {
	MemberRepository memberRepository;
    
    Test(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
}

실제 의존관계


AppConfig를 사용하여 인터페이스와 구현 객체를 분리했다.
-> OCP와 DIP 모두 지키면서 구현할 수 있다.
(구현 객체를 변경할때는 AppConfig만 변경하면된다.)
(Test 클래스는 구현 객체에 의존하지 않고 인터페이스만 의존하고 있다.)


IoC (Inversion of Control) - 제어의 역전

AppConfig를 적용하기 전의 프로그램은 객체는 인터페이스를 의존하지만, 직접 구현 객체를 연결해야 했다.
AppConfig를 적용하면서, 기존의 객체는 인터페이스만 의존하면서 실행할 수 있게 됐다.
인터페이스를 의존하고 있는 객체는 어떤 구현 객체가 실행될지 모른다.
프로그램의 제어 흐름에 대한 권한은 모두 AppConfig가 가지고 있다.

이렇듯 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라고 한다.


DI (Dependency Injection) - 의존관계 주입

Test 클래스는 MemberRepository 인터페이스를 의존하고 있다. 어떤 구현 객체가 사용될지 모른다.
AppConfig를 실행해야 Test 클래스에 구현객체가 주입된다.

이처럼 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입이라고 한다.
-> 클라이언트 코드를 변경하지 않고, 클라이언트가 호출되는 대상의 타입 인스턴스를 변경할 수 있다.


AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라 한다.


지금까지 자바코드만으로 DI를 적용했다. 다음 장에서 스프링을 이용해서 DI를 구현해본다.

profile
공부하다가 생긴 궁금한 것들을 정리하는 공간

0개의 댓글