본문 바로가기
Back end/Spring

[Spring] DI(Dependency Injection), IoC(Inversion of Control)

by 더 이프 2023. 7. 25.
728x90

목차

    1. DI(Dependency Injection)

    ■ DI란?

    DI는 의존성 주입으로 의존 관계를 외부에서 결정해주는 것을 말한다. 예를 들어 A가 B를 의존한다라는 표현은 B의 기능이 추가되거나 변경되면 그 영향이 A에 미치는 것입니다.

     

    2. DI 예시

    ■ StudyApplication

    • Test 인터페이스를 구현한 TestA, TestB, TestC라는 부품을 가지고 교체하여 사용 가능
    package com.web.study;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    import com.web.study.IocAndDi.IocTest;
    import com.web.study.IocAndDi.TestB;
    
    @SpringBootApplication
    public class StudyApplication {
    	
    	public static void main(String[] args) {
    		SpringApplication.run(StudyApplication.class, args);
    		iocAndDiTest();
    	}
    	
    	public static void iocAndDiTest() {
    		IocTest iocTest = new IocTest(new TestB());
    		iocTest.run();
    	}
    
    }
    • console 결과

    ■ Test

    package com.web.study.IocAndDi;
    
    public interface Test {
    	public void printTest();
    }
    package com.web.study.IocAndDi;
    
    public class TestA implements Test {
    	
    	@Override
    	public void printTest() {
    		System.out.println("TestA 클래스!!!");
    	}
    }
    package com.web.study.IocAndDi;
    
    public class TestB implements Test{
    
    	@Override
    	public void printTest() {
    		System.out.println("TestB 클래스!!!");
    	}
    }
    package com.web.study.IocAndDi;
    
    public class TestC implements Test{
    
    	@Override
    	public void printTest() {
    		System.out.println("TestC 클래스!!!");
    	}
    }

    ■ IocTest

    package com.web.study.IocAndDi;
    
    public class IocTest {
    	
    	private Test test;
    	
    	// DI로 외부로부터 test를 받아서 옴
    	public IocTest(Test test) {
    		this.test = test;
    	}
    	
    	public void run() {
    		test.printTest();
    		System.out.println("IoCTest 출력!");
    	}
    }

     

    3. IoC(Inversion of Control)

    ■ IoC란?

    스프링 애플리케이션에서는 빈의 생성과 의존 관계 설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 스프링 컨테이너가 담당합니다. 이를 스프링 컨테이너가 코드 대신 빈에 대한 제어권을 가지고 있다고 하여 IoC 즉, 제어의 역전이라고 부릅니다. 스프링 컨테이너는 주로 IoC 컨테이너라고 부릅니다.

     

    4. IoC 예시

    ■ IoCTestController

    • @Component가 IoC 컨테이너에 객체를 생성하여 등록하는 것이 가능하며 이를 Bean이라고 부름
    • @Controller에도 @Component가 있어 Bean으로 등록 가능
    • @Autowired는 IoC 컨테이너에서 등록되어 있는 빈으로 추적하여 적절한 빈을 사용
    • @Autowired는 사용하고자 하는 객체에 모두 적용해야 하기 때문에 변수가 많은 경우 변수에 final을 선언한 뒤 @requiredArgsConstructor를 사용하여 생성자에서 적용되도록 하여 사용
    package com.web.study.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.web.study.IocAndDi.IocTest;
    import com.web.study.IocAndDi.IocTest2;
    
    import lombok.RequiredArgsConstructor;
    
    @RestController
    // @RequiredArgsConstructor
    // 변수의 양이 많을 경우 RequiredArgsConstructor를 사용하는 것이 좋음
    public class IoCTestController {
    
    	@Autowired
    	private IocTest iocTest;
    	@Autowired
    	private IocTest2 iocTest2;
    	
    	// @Autowired와 같은 역할
    //	private final IocTest iocTest;
    //	private final IocTest2 iocTest2;
    //	public IoCTestController(IocTest iocTest, IocTest2 iocTest2){
    //		this.iocTest = iocTest;
    //		this.iocTest2 = iocTest2;
    //	}
    	
    	@GetMapping("/ioc/test")
    	public Object test() {
    		iocTest.run();
    		iocTest2.run();
    		return null;
    	}
    }
    • console 결과

    ■ BeanConfig

    • @Configuration은 기본 설정시 적용하며 1개 이상의 빈을 제공하는 클래스의 경우 반드시 명시
    • @Beam은 클래스를 빈으로 등록할 때 사용
    • 보통 외부에서 가져온 라이브러리의 클래스에는 어노테이션을 적용할 수 없기 때문에 여기 가져와서 등록할 때 사용
    package com.web.study.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.web.study.IocAndDi.TestC;
    
    @Configuration
    public class BeanConfig {
    	// 라이브러리에서 가져온것들은 어노테이션을 등록이 안되기 때문에 여기 가져와서 등록
    	@Bean
    	public TestC testC() {
    		return new TestC();
    	}
    }

    ■ Test

    • @Component로 IoC 컨테이너에 등록 시 클래스의 이름으로 등록되며 앞은 소문자로 변경하여 등록
    • @Component뒤의 괄호안에 등록할 때의 이름을 정하여 사용 가능
    • TestC의 경우 Config에서 빈으로 등록
    package com.web.study.IocAndDi;
    
    import org.springframework.stereotype.Component;
    
    @Component("t1")
    public class TestA implements Test {
    	
    	@Override
    	public void printTest() {
    		System.out.println("TestA 클래스!!!");
    	}
    }
    package com.web.study.IocAndDi;
    
    import org.springframework.stereotype.Component;
    
    @Component("t2")
    public class TestB implements Test{
    
    	@Override
    	public void printTest() {
    		System.out.println("TestB 클래스!!!");
    	}
    }
    package com.web.study.IocAndDi;
    
    public class TestC implements Test{
    
    	@Override
    	public void printTest() {
    		System.out.println("TestC 클래스!!!");
    	}
    }

    ■ IocTest

    • @Qualifier 괄호안에 IoC에 등록된 빈들의 등록 시 이름을 추적하여 연결할 수 있게 해줌
    • @Qualifier는 같은 인터페이스를 구현한 클래스를 구분하기 위하여 사용
    package com.web.study.IocAndDi;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    
    // componet 어노테이션을 사용한 것은 ioc컨테이너에 객체들을 넣고 bean이라고 부름
    @Component
    public class IocTest {
    	
    	@Qualifier("t1")
    	@Autowired
    	private Test test;
    	
    	// DI로 외부로부터 test를 받아서 옴
    //	public IocTest(Test test) {
    //		this.test = test;
    //	}
    	
    	public void run() {
    		test.printTest();
    		System.out.println("IoCTest 출력!");
    	}
    }
    package com.web.study.IocAndDi;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
    
    // componet 어노테이션을 사용한 것은 ioc컨테이너에 객체들을 bean이라고 부름
    @Component
    public class IocTest2 {
    
    	@Qualifier("testC")
    	@Autowired
    	private Test test;
    	
    	// DI로 외부로부터 test를 받아서 옴
    //	public IocTest(Test test) {
    //		this.test = test;
    //	}
    	
    	public void run() {
    		test.printTest();
    		System.out.println("IoCTest2 출력!");
    	}
    }