본문 바로가기
스터디/Spring

[Spring] IoC와 DI

by zoodi 2021. 7. 4.
728x90

Spring하면 항상 나오는 개념인 IoC와 DI에 대해서 정리를해본다. 

 

IoC (Inversion Of Control)

의역하면 "제어의 역전"이라고한다. 제어의 역전이란 무엇일까? 말그대로 메서드나 객체의 생성, 호출을 개발자가하는 것이 아닌 외부에서 결정하는 것이다. 스프링에서는 스프링 컨테이너가 제어권을 갖고 적절한 시점에 필요한 객체를 생성하고 메서드를 호출한다.

아래 코드 예시를 살펴보자.

 

과거에는 개발자들이 필요한 생성자를 new 키워드로 객체를 직접 생성하였다.

class OwnerController {
private OwnerRepository repository = new OwnerRepository();
}

제어권이 스프링 컨테이너로 넘어가고 객체의 생성 및 생명주기까지 관리 할 수 있게되면서 아래와 같은 코드 방식으로 바뀌었다.

class OwnerController {
private OwnerRepository repo;
  public OwnerController(OwnerRepository repo) {
  this.repo = repo;
}
// repo를 사용합니다.
}
class OwnerControllerTest {
@Test
public void create() {
  OwnerRepository repo = new OwnerRepository();
  OwnerController controller = new OwnerController(repo);
  }
}

제어의 권한이 스프링 컨테이너에게 넘어가면서 의존관계를 설정하게된다. 위 코드에서 OwnerRepository 객체를 OwnerController의 생성자 파라미터로 넘겨주면서 "의존성 주입"을 하게된다.

즉, 스프링 컨테이너는 빈(객체)을 관리하고 IoC를 통해 DI를 실현한다.

 

*빈(Bean, 객체)

Component Scanning : 같은 패키지 내의 빈을 스캔한다

  •  @Component
    • @Repository
      @Service
      @Controller

오직 Bean들만 의존성 주입을한다!!!

 

DI (Dependency Injection)

직역하면 "의존성 주입"이다. 의존성 주입은 제어의 역행이 발생할 때 객체들간의 관의 관계를 관리하는 기법이다.

자바에서는 보통 인터페이스를 이용해서 의존적인 객체의 관계를 처리한다. 우리가 인터페이스를 갖다쓰면 스프링이 알아서 필요한 객체를 생성하여 주입하는 형태이다.

 

DI를 왜 쓸까?

만약 DI를 안 쓴다면 -> 레스토랑 클래스와 요리사 클래스가 있다고 예상해보자. 레스토랑 클래스는 요리사 클래스를 필요로한다. 이러한 상황을 레스토랑 클래스가 요리사 클래스에 의존하고있다(의존성을 갖고있다) 라고 한다. 만약 요리사 클래스가 한식요리사 클래스로 변경된다면? 요리사 클래스에 의존하고있는 레스토랑 클래스도 직접 코드를 변경해주어야 한다. 객체간의 강한 결합력이 생기는 것이다. 이러한 결합력을 낮추기 위해 DI를 사용한다!

즉, 의존성 주입을 사용하면 모듈간의 결합도가 낮아지고 유연성이 높아진다는 장점이있다. (낮은 결합도, 높은 응집도)

그리고 개방-폐쇄 원칙(OCP)를 만족하여 기존 코드를 손대지 않고도 설정만으로 구현 클래스를 변경 할 수 있다.

 

DI 구현 방법으로는 1)필드주입, 2)setter주입, 3)생성자 주입 3가지 방법이있다. 의존관계가 실행 중 동적으로 변하는 경우가 거의 없으므로 생성자 주입을 주로 권장하는 추세이다.

 

1.필드주입

@Component
public class SampleController {
    @Autowired
    private SampleService sampleService;
 }

 

*@Autowired를 통한 DI는 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.

- 필드 주입 장점 : 1)읽기 편하고, 2)사용하기 편함

- 필드 주입의 문제점:

1) 단일책임의 원칙 위반(SRP), 다른 Injection 타입에 비해 위기감을 느끼게 해준다. Constructor의 parameter가 많아짐과 동시에 하나의 Class가 많은 책임을 떠안는다는 걸 알게된다.

2)필드 주입 방식은 final를 선언할 수 없다. 즉 객체가 변할 수 있는 위험성이 있다.

 

2. Setter 주입

@Component
public class SampleController {
    private SampleService sampleService;
 
    @Autowired
    public void setSampleService(SampleService sampleService) {
        this.sampleService = sampleService;
    }
}

Setter 주입을 통해 service의 객체를 주입하지 않아도 controller의 객체 생성이 가능하다. Controller 객체가 생성가능하다는 것은 내부에 있는 Service의 method 호출이 가능하다는 것인데, set메서드를 통해 Service의 구현체를 주입해주지 않았으므로, NullPointerException 이 발생한다. 주입이 필요한 객체가 주입이 되지 않아도 얼마든지 객체를 생성할 수 있다는 것이 문제이다.

 

이 문제를 해결하는 방법이 생성자 주입이다.

 

3.생성자 주입

@Controller
public class MemberController {

    private final MemberService memberService;

    //spring컨테이너에서 memberController가 생성될때 memberService를 가져옴
    //DI : Dependency Injection , 생성자 주입 방식
    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

생성자 주입 방법이 좋은 이유는 필수적으로 사용해야하는 의존성 없이는 Instance를 만들지 못하도록 강제할 수 있기 때문이다.

또한 단일 생성자에서는 @Autowired를 사용하지 않아도되고, final를 사용할 수 있다. final를 사용하면 객체가 불변하게 할 수 있다는 장점이있다. (Controller 내부에서 Service 객체를 변경할 수 없다.)

 

의존성 주입 DI는 생성자 주입 방법을 사용하는 것이 제일 좋다!!!

 

 


*참조

1.스프링 프레임워크 입문 (인프런) - 백기선

2.스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근 기술 (인프런) - 김영한

https://velog.io/@gillog/Spring-DIDependency-Injection-%EC%84%B8-%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95

 

[Spring] DI(Dependency Injection) 세 가지 방법

앞서 DI(Dependency Injection)에 대해서 알아보았는데, Spring에서 의존성을 주입하는 세 가지 방법에 대해서 다루어 보려고한다.DI는 Spring에서만 사용되는 용어가 아니라 객체지향 프로그래밍에서는

velog.io

 

728x90

댓글