Everyday Dev System

IoC와 DI(의존성 주입) 본문

내배캠 주요 학습/Spring 입문

IoC와 DI(의존성 주입)

chaeyoung- 2023. 6. 13. 19:56

Data를 전달해오는 방식이 변경된 경우에는 Controller
DB랑 작업하는데에 코드 변경이 필요한 경우엔 Repository
비즈니스 로직 수정이 필요한 경우엔 Service

 

 

 

IoC는 설계 원칙, DI는 디자인 패턴이다.

 

 

1. 일반 클래스를 Bean 개체로 등록하는 방법 - @Component

Bean은 Spring이 관리하고 있는 객체를 의미.

IoC Container는 Bean들을 모아둔 Container 이다.

위와 같은 생성자에서 외부에서 주입을 받을 때는 Bean 객체만 주입 가능.

이유는, 이를 넣어주는 역할을 하는 건 Spring이기 때문에 Bean 객체로 등록이 되어야 주입이 가능하다고 한다.

그리하여, 현재는 MemoService 타입의 Bean이 없는데 넣으라고 해서 오류가 생기는 상황이다.

 

이를 해결하기 위해서 Bean으로 등록하고자 하는 MemoService 클래스에

@Component 어노테이션 기재

이 어노테이션을 기재하면 해당 클래스를 Bean으로 등록을 한다는 의미

@Component
public class MemoService {
	--중략--
]
@Component
public class MemoService {	
	--중략--
}

 

 

클래스 왼쪽에 커피콩 모양의 아이콘이 나올 경우

Bean으로 등록된 클래스임을 명시한 것이다.

이렇게 등록된 Bean이 IoC 컨테이너에 저장이 된다.

 

 

 

Select in Spring View를 누르면 Bean으로 등록된 클래스들을 아래 사진과 같이 볼 수 있다.

 

클래스명에서 맨 앞을 소문자의 형태로 Bean 등록

 

MemoService 클래스는 memoService 로 Bean 등록.

 

** jbdcTemplate이 Bean으로 등록이 되어 있음. (Spring이 자동 생성해줌)

 

더보기

이를 스프링부트에서 어떻게 Bean으로 등록하는지 알아보기

 

main 메서드가 있는 클래스 위에 @SpringBootApplication을 ctrl + 클릭하기.

@ComponentScan@Component를 찾아주는 어노테이션이다. (해당 패키지와 하위 패키지가 내에서 찾는다.)

@SpringBootApplication에 default로 @ComponentScan을 달아뒀기 때문에 @Component가 달려있는 클래스를 Bean의 개체로 등록이 가능하다.

스프링부트에서 자체적으로 편리하게 default로 달아준것이므로, 원래는 @ComponentScan을 달고  설정해야 함.

 

주입받기 위해 원래는 @Autowired를 기재해야 했다.

하지만, 스프링 4.3 버전 이후로는 생성자가 1개일 경우, @Autowired 어노테이션 생략 가능

 

 

 

 

 

2. 의존성 주입 방법

** DI 의존성 주입을 하기 위해서는 객체 생성이 선행되어야 한다.

1. 필드를 통한 주입

- 불안정 하므로 추천X

@Component
public class MemoService {
    // 비즈니스 로직 수정이 필요한 경우엔 Service

    // 필드를 통한 주입 방법
    @Autowired
    private MemoRepository memoRepository;

2. 메서드를 통한 주입

@Component
public class MemoService {
    // 비즈니스 로직 수정이 필요한 경우엔 Service

    private MemoRepository memoRepository;

    @Autowired
    public void setDi(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
    
    --중략--
}

3. 생성자를 통한 주입 (가장 많이 사용)

- 객체의 불변성을 지켜줄 수 있기 때문에 많이 사용.

@Component
public class MemoService {
	private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
    --중략--
}

4. Lombok 활용

@Component
@RequiredArgsConstructor
public class MemoService {
    // 비즈니스 로직 수정이 필요한 경우엔 Service

    private final MemoRepository memoRepository;
    
    --중략--
}

 

제어의 역전

 

 

 

3. 강한 결합을 약한 결합으로 바꾸기

1. 강한 결합이 되어 있는 경우 

제어의 흐름 MemoController -> MemoService -> MemoRepository

@RestController
@RequestMapping("/api")
public class MemoController {
    // Data를 전달해오는 방식이 변경된 경우에는 Controller

    private final MemoService memoService;

    public MemoController(JdbcTemplate jdbcTemplate) {
        this.memoService = new MemoService(jdbcTemplate);
    }
-- 중략 --
}
public class MemoService {
    // 비즈니스 로직 수정이 필요한 경우엔 Service

    private final MemoRepository memoRepository;

    public MemoService(JdbcTemplate jdbcTemplate) {
        this.memoRepository = new MemoRepository(jdbcTemplate);
    }
-- 중략 --    
}

 

2. 약한 결합으로 바꾼 경우

강한 결합을 약한 결합으로 하기 위해서는

미리 만들어진 Service 객체를 , Repository 객체를 줘야 함.

@RestController
@RequestMapping("/api")
public class MemoController {
    // Data를 전달해오는 방식이 변경된 경우에는 Controller

    private final MemoService memoService;

    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }
    --중략--
}
public class MemoService {
    // 비즈니스 로직 수정이 필요한 경우엔 Service

    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
    --중략--
}

 

 

 

4. Autowired 적용 조건

@Autowired를 적용하려면 IoC 컨테이너에서 관리하는 Bean class에서만 가능하다,

스프링 컨테이너에 의해서 등록된 Bean Class만 주입이 가능하다.

@Component가 기재되어 있지 않은 클래스에서는 @Autowired 어노테이션을 기재할 수 없다.

 

 

 

IoC Container에서 Bean을 찾을 때는 이름으로도 찾을 수 있고, 

Bean에 해당하는 클래스 타입으로도 찾을 수 있다.

public MemoService(ApplicationContext context) {
	MemoRepository memoRepository1 = (MemoRepository) context.getBean("memoRepository");
    
    MemoRepository memoRepository2 = (MemoRepository) context.getBean(MemoRepository.class);
    this.memoRepository = memoRepository1;
    // this.memoRepository1 = memoRepository2;
}