Hyemi Lee

Hyemi Lee

주니어 개발자의 삽질과 기록

Spring, DI, IoC

Dependency Injection

  • 컨테이너가 직접 객체들 사이에 의존관계를 처리하는 것.
  • Setter Injection , Constructor(생성자) Injection
  • 의존성이 삽입된다는 의미로 IoC를 DI라는 표현으로 사용합니다.

Dependency ( 의존성 )

  • 객체지향언어에서 두 클래스 간의 협력하는 관계
  • 일반적으로 둘 중 하나가 다른 하나를 어떤 용도를 위해 사용함

Dependency는 위험하다

  • 하나의 모듈(클래스)이 바뀌면 다른 모듈까지 변경이 이루어지 지기 때문에 위험하다
  • 테스트 가능한 어플을 만들때 의존성이 있으면 유닛테스트 작성이 어렵다
    • 유닛테스트의 목적 자체가 다른 모듈로부터 독립적으로 테스트하는 것을 요구하기 때문이다(Mock객체로 대체가능하다)
  • Controller는 Repository에 의존한다 (ioc 구현전)

IoC

  • Inversion of Control ; 뒤바뀐 제어권

IoC란

  • 객체의 생성이나 객체사이의 의존관계를 개발자가 직접 자바 코드로 처리해야했었다. 이런 상황에서는 의존관계에 있는 객체를 변경하려면 반드시 자바코드를 수정해야 한다.
  • 하지만 IoC가 적용되면 객체 생성과 의존관계를 컨테이너가 처리하게 된다
  • 결과적으로 소스에 의존관계가 명시되지 않으므로 결합도가 떨어져서 유지보수가 편리해진다.

구현방법

  • 스프링은 두가지( @Component, @Autowired ) 어노테이션을 지원해서 IoC를 구현한다
  • 의존성이 삽입된다는 의미로 IoC를 DI라는 표현으로 사용합니다.

@Component

를 사용해서 우리는 스프링 프레임워크에게 이렇게 말한다: "스프링 프레임워크야 이것은 너가 관리해야 하는 빈이야"

@Autowired

를 사용해서 우리는 스프링 프레임워크에게 이렇게 말한다: "야 스프링 프레임워크야 이 타입에 맞는 것을 찾아서 그것을 이것과 연결 시켜줘""

@Component
public class WelcomeService {
 //Bla Bla Bla
}
@RestController
public class WelcomeController {
 @Autowired
 private WelcomeService service;
 @RequestMapping("/welcome")
 public String welcome() {
 return service.retrieveWelcomeMessage();
 }
}
@RestController
public class RestaurantController {
 // ★ IoC사용 전 ★
 // Contorller에서 Restaurant Repository를 직접 만들어준다
 //private RestaurantRepository repository = new RestaurantRepository();
 // ★ IoC사용 후 ★
 @Autowired // RestaurantController를 만들어줄때 spring이 알아서 RestaurantRepository의 객체를 만들어준다
 private RestaurantRepository repository;
 // ...이하 생략
}
// RestaurantRepository를 스프링이 직접 관리하도록 해당 애노테이션을 붙인다
@Component
public class RestaurantRepositoryImpl implements RestaurantRepository {
 // 이하 생략
}
//RestaurantRepository를 다른 형태로 쉽게 변경할수있다
public interface RestaurantRepository {
 List<Restaurant> findAll();
 Restaurant findById(Long id);
}
  • 이렇게 IoC를 사용하는것으로 변경하고 Test를 돌리면 오류가 발생한다
  • 해당 오류는 RestaurantControllerTest 클래스에 객체를 직접주입하여 해결할 수 있다
@SpyBean(RestaurantRepositoryImpl.class) // 컨트롤러에 원하는 객체를 주입할수있다
private RestaurantRepository restaurantRepository;

일반 제어권 vs IoC

  • 일반적인 (의존성에 대한) 제어권 : 내가 사용할 의존성은 내가 만든다
class OwnerController {
 private OwnerRepository repository = new OwnerRepository();
}


  • IoC : 내가 사용할 의존성을 누군가 알아서 주겠지
    • 내가 사용할 의존성의 타입(또는 인터페이스)만 맞으면 어떤거든 상관없다
    • 그래야 내 코드 테스트 하기도 편하지
  • 각 객체들의 강한 의존성을 유연하게 한다
class OwnerController{
 // ★★★해당 객체를 사용을 하지만 여기서는 만들지 않는다.★★★
 // 밖에서 누군가 만들겠지. -> 의존성 역전
 private OwnerRepository repo;
 public OwnerController(OwnerRepository repo) {
 this.repo = repo; // 의존성을 누군가 알아서 주겠지
 }
 public doSomething(){
 }
 // repo를 사용한다
}
class OwnerControllerTest {
 @Test
 public void create(){
 // OwnerRepository의 객체 없이는 OwnerController의 doSomething()을 호출할수없다.
 // 즉, OwnerController는 안전하다
 OwnerRepository repo = new OwnerRepository();
 OwnerController controller = new OwnerController(repo);
 controller.doSomething();
 }
}

IoC 컨테이너

  • ApplicationContext or Bean Factory 가 있다
  • 빈(Bean , spring에서 사용할 객체)을 만들어 제공하고 , 빈들 사이의 의존성을 엮어준다


  • 빈 설정
    • 이름 or ID
    • 타입
    • 스코프
  • but 컨테이너를 직접 쓸 일은 많지 않다

  • 의존성 주입은 빈끼리만 가능하다, 즉 spring IoC컨테이너 안에 들어있는 객체끼리만 컨테이너가 의존성 주입을 해준다

  • 객체 하나를 매번 새로 만들지 않고 계속 해서 사용한다. IoC컨테이너에 등록되어있는 Bean은 객체 하나이고 그것을 계속 사용한다

Bean(빈)

  • 스프링 IoC컨테이너가 관리하는 객체
  • 개발자가 일반적으로 생성한 객체(new 사용)은 bean이 아니다 !!
  • 이런 Bean들만 의존성 주입이 된다

빈 등록방법

  1. Component Scaning
    • 해당 에노테이션이 있으면 빈으로 등록해준다
    • @Component
    • @Repository
    • @Service
    • @Controller
    • @Configuration
  2. 또는 직접 일일히 XML이나 자바 설정 파일에 등록

예제

  • 직접 빈 설정파일 만들어주기
@Configuration
public class SampleConfig{
 @Bean
 public SampleController sampleController(){
 return new SampleController();
 }
}

빈을 꺼내는 방법

  • @Autowired 사용
  • IoC컨테이너안에 있는 빈을 주입받아서 사용할 수 있다
    @Autowired
    private OwnerRepository owners;
    

의존성 주입

: 필요한 의존성을 어떻게 받아올것인가

  • 두 클래스가 서로 관계있게 어떻게 만들어줄수있는지
  • 빈이 있어야 의존성을 주입한다
  • @Autowired / @Injection 을 어디에 붙일까?
    • 생성자 (권장 !)
    • 필드
    • Setter
  • 생성자에게 의존성을 주입하는것을 권장한다 , 해당객체가 생성되지 않으면 해당 클래스를 사용할수 없게 만들어버린다
    @Controller
    class OwnerController {
    private VisitRepository visits;
     private final OwnerRepository owners; // 빈 껍데기
    /*
     1. 이 클래스에 있는 OwnerRepository는 빈 껍데기 이다
     2. 이 클래스의 객체를 만들고 싶으면 OwnerRepository객체가 필요하다
     3. 따라서 OwnerController클래스의 객체를 만들고자 하는 곳에서 OwnerRepository객체를 만들어서 파라메타로 전달해줘야 객체 생성이 가능하다
    */
    @Autowired
     public OwnerController(OwnerRepository clinicService, VisitRepository visits) {
     this.owners = clinicService;
     this.visits = visits;
     }
    /*
     ... 이하 생략
    */
    }
    

Reference

Share on

Twitter Facebook LinkedIn

You may also enjoy

Redis Stream

2021年04月28日

Stream Stream은 로그 데이터를 처리하게위해 5.0에서 새로 도입된 데이터 타입입니다. 대량의 데이터가 연속적으로 발생할때 처리하기 위해 등장했습니다. 기존 데이터를 수정하지 않고 오직 추가로 발생합니다. 이런 종류의 데이터를 stream or log데이터...

Study, Object, chapter2&3 presentation

2021年04月20日

chapter03. 역할, 책임, 협력 객체지향 설계란, 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다.

Spring, chatting 프로그램 만들기, Reactive란?

2020年06月16日

Reactive 막힘없이 흘러다니는 data(event)를 통해 사용자에게 자연스러운 응답을 주고 규모 탄력적으로 리소스를 사용하며 실패에 있어서 유연하게 대처한다 모든 지점에서 블럭 되지 않게 하자 oop와 같은 패러다임 모든 것을 비동기적인 data의 strea...