• Spring DI
    BOOK 2024. 4. 27. 21:47

    그림으로 배우는 스프링 6 입문
    그림으로 배우는 스프링 6 입문

     "한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 제공 받아 작성된 서평입니다."

    DI의 개념

    • DI는 'Dependency Injection'의 약자로, 프로그램을 설계할 때 사용되는 개념
    • 사용되는 객체를 사용하는 객체 외부에서 생성하여 전달받는 사고 방식

    DI
    DI

    1에서 사용되는 객체를 준비

    2에서 사용하는 객체에 생성자의 인수 등을 사용해 전달

    사용하는 쪽은 3에서 전달받은 객체의 메서드를 호출

    • DI의 D는 의존(dependency)을 의미하고, I는 주입(injection)을 의미

    DI와 인터페이스

    • 일반적으로 DI를 설명할 때는 의존 객체가 인터페이스를 구현한다고 가정
    • 하지만 의존 객체가 반드시 인터페이스를 구현할 필요는 없다.
    • 예를 들어 아래와 같이 인터페이스를 사용하지 않는 클래스도 DI를 적용할 수 있다.

    인터페이스를 사용하지 않는 DI
    인터페이스를 사용하지 않는 DI

    • TrainingRepository는 인터페이스가 아닌 구상 클래스
    public class TrainingRepository {
    	...
        public List<Training> selectAll() {
        	// 데이터 가져온다.
            ...
            return trainings;
        }
    }
    
    public class TrainingService {
    	private TrainingRepository trainingRepository;
        
        public TraingingService(TrainingRepository trainingRepository) {
        	this.trainingRepository = trainingRepository;
        }
        
        public List<Training> findAll() {
        	retrun trainingRepository.selectAll();
        }
    }
    
    //외부 소스 코드
    TrainingRepository trainingRepository = new TrainingRepository();
    TrainingService traingingService = new TrainingService(trainingRepository);
    • TrainingService 객체와는 다른 곳에서 TrainingRepository 객체를 생성하고 전달받았으므로 DI를 적용했다고 할 수 있다.
    • 인터페이스를 사용하지 않으면 테스트할 때 의존 객체를 Mock 객체로 전환할 수 없지 않을까? 하는 생각이 들 수도 있지만
      Mock용 라이브러리를 사용하면 간단히 전환할 수 있다.
    • Mock용 라이브러리는 의존 객체의 구상클래스(TrainingRepository)의 서브 클래스를 자동으로 생성해 준다.
    public class MockTrainingRepository extends TrainingRepository {
    	....
        @Override
        public List<Training> selectAll() {
        	//적당한 테스트 데이터
            ...
            return trainings;
        }
    }
    
    TrainingRepository trainingRepository = new MockTrainingRepository();
    TrainingService trainingService = new TrainingService(trainingRepository);
    • 자동 생성된 서브 클래스의 객체를 인젝션 해준다.

    인터페이스를 사용하는 DI
    인터페이스를 사용하는 DI

    DI 컨테이너(IoC 컨테이너)

    • DI를 적용하려면 객체를 외부에서 준비할 필요가 있다.
    • 하지만 외부에서 직접 객체를 생성하려고 하면 문제가 생길 우려가 있고, 그 문제를 해결해 주는 것이 스프링 DI 컨테이너

    외부에서 직접 객체를 준비할 때의 문제점

    public static void main(String[] args) {
    	TrainingRepository trainingRepository = new JdbcTrainingRepository();
        TrainingService trainingService = new TrainingServiceImpl(trainingRepository);
        
        List<Training> trainings = trainingService.findAll();
        ....
    }
    • 애플리케이션의 규모가 커지면 외부에서 준비할 객체 수가 많아진다.
    • 실제 개발 프로젝트에서는 Service, Repository 역할을 가진 객체가 수백 개에 달하는 경우가 많다.
    • 이런 경우에 객체를 일일이 준비해서 다른 객체에 인젝션 하는 처리를 작성하는 것은 매우 번거로우며 유지 보수성도 좋지 않다.

    DI 컨테이너를 사용한 솔루션

    • DI 컨테이너는 외부에서 하는 처리를 담당하는 스프링의 기능
    • 자바에서 컨테이너라는 용어가 나오면 객체를 담는 상자로 생각하면 된다.
    • 컨테이너에 객체를 넣으면 컨테이너는 객체에 부가적인 기능을 제공

    DI 컨테이너
    DI 컨테이너

    • DI 컨테이너는 다양한 기능을 제공하는데, 그중 대량의 객체 생성이나 인젝션을 간결한 코드로 처리해 주는 기능이 있다.
    • 이 기능으로 외부에서 직접 생성할 때 발생하는 문제를 해결할 수 있다.

    DI 컨테이너 관련 기본 용어

    • Bean, Bean 정보, 설정 정보, 애플리케이션 컨텍스트

    DI 컨테이너 관련 기본 용어

    Bean

    • DI 컨테이너가 관리하는 객체
    • Java Beans의 Beans와는 직접적인 관계가 없다.

    Bean 정의

    • Bean을 정의하는 정보를 가리킨다.
    • 예를 들어 관리할 객체의 구상 클래스는 무엇인지, 어느 의존 객체를 인젝션할 것인지와 같은 정보

    설정 정보 (configuration)

    • DI 컨테이너로 불러올 정보
    • Bean 정의도 포함, Bean 정의 외에도 DI 컨테이너의 특정 기능을 활성화, 비활성화할 수 있는 정보가 포함
    • 설정 정보를 작성해서 DI 컨테이너에서 불러오면 설정 정보에 따라 Bean 객체가 생성, 의존 객체가 인젝션 됨

    어플리케이션 컨텍스트

    • DI 컨테이너의 다른 이름
    • DI 컨테이너에 해당하는 객체가 ApplicationContext 인터페이스를 구현
    • DI 컨테이너를 종종 어플리케이션 컨텍스트라고 부르기도 한다.

    Bean 정의

    Bean 정의를 작성하는 대표적인 방법은 3가지

    • 스테레오타입 어노테이션
    • @Bean 메서드
    • <bean> 태그

    대표적인 Bean 정의 방법
    대표적인 Bean 정의 방법

    728x90
go.