[Spring] IoC/DI

zini9188·2023년 2월 7일
0

Spring

목록 보기
4/33

IoC(Inversion of Control)란?


애플리케이션 흐름은 라이브러리의 경우 주도권이 개발자에 있고, 프레임워크는 주도권이 프레임워크에게 있다.

IoC는 제어의 역전으로 애플리케이션 흐름의 주도권을 개발자가 아닌 외부에 위임하는 것을 의미한다.

DI(Dependency Injection)란?


의존성 주입이란 추상적이던 IoC의 개념을 구체화시킨 것으로 객체를 직접 생성하는 것이 아니라 외부(스프링 컨테이너)에서 생성한 후 주입시켜주는 방식이다. 객체간의 결합을 느슨하게 만들어 유연하고 확장성이 높아진다.

의존관계란?

의존 대상 B가 변할 때, 그 영향이 A에 미치는 관계를 말한다.

예를 들어 Cafe에서 Americano가 다른 종류의 커피로 변경된다면 Cafe에 영향이 가게 된다. 커피 종류의 변화가 카페에게 영향을 미치므로 Cafe는 Americano에 의존한다고 할 수 있다.

public class Cafe{
	private Americano americano
    
    public Cafe(){
    	americano = new Americano();
    }
}

이러한 구조는 다음과 같은 문제가 있다.

1. 강한 결합을 가진다.

Cafe와 Americano 클래스는 강하게 결합되어 있다. Cafe가 다른 종류의 커피인 CafeLatte를 사용해야 한다면 Cafe의 생성자를 변경해야 한다.

2. 객체들 간의 관계가 아닌 클래스 간의 관계가 맺어진다.

현재 Cafe 클래스는 Americano 클래스와 의존 관계를 가지고 있다. 객체지향원리에서 DIP 원칙를 지키기 위해 구체화에 의존하는 것이 아닌 추상화에 의존해야 된다.

의존성 주입을 통한 문제 해결

우선 커피의 종류를 추상화하기 위한 Coffee 인터페이스가 필요하다.

public interface Coffee{ ... }
public class Americano implements Coffee { ... }
public class CafeLatte implements Coffee { ... }
public class Cappuccino implements Coffee { ... }

Cafe 클래스의 생성자에서 외부로부터 커피의 종류를 주입받을 수 있다.

public class Cafe{
	private Coffee coffee
    
    public Cafe(Coffee coffee){
    	this.coffee = coffee;
    }
}

의존 관계 주입 방법


생성자 주입 방법

  • 생성자에 @Autowired를 붙여 의존성을 주입

  • 생성자 호출 시점에 1회 호출되는 것이 보장

  • 불변과 필수 의존 관계에 사용

  • Spring에서 가장 권장하는 방법

  • 주입 받을 필드를 final로 선언 가능

@Component
public class CoffeeService {
  private final MemberRepository memberRepository;
  private final CoffeeRepository coffeeRepository;

  @Autowired
  public CoffeeService(MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
    this.memberRepository = memberRepository;
    this.coffeeRepository = coffeeRepository;
  }
}

setter를 통한 주입 방법

  • 선택과 변경 가능성이 있는 의존 관계에 사용

  • set필드명 메서드를 생성하여 의존 관계를 주입한다.

  • @Autowired를 입력하지 않으면 실행되지 않는다.

@Component
public class CoffeeService {
  private MemberRepository memberRepository;
  private CoffeeRepository coffeeRepository;

  @Autowired
  public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }

  @Autowired
  public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
    this.coffeeRepository = coffeeRepository;
  }
}

필드 주입

필드에 @Autowired를 붙여 주입

  • 코드가 간결해서 이전에 많이 사용되던 방식이다.

  • 외부에서 변경이 불가능하여 테스트하기 힘들다.

@Component
public class CoffeeService {
  @Autowired
  private MemberRepository memberRepository;
  
  @Autowired
  private CoffeeRepository coffeeRepository;
}

일반 메서드 주입

  • 한번에 여러 필드를 주입 받을 수 있다.

  • 일반적으로 사용되지 않는다.

생성자를 통해 의존성 주입을 사용해야하는 이유


객체 불변성 확보

변경의 가능성을 배제하여 불변성을 보장할 수 있다.

테스트 코드의 작성

컴파일 시점에 객체를 주입받아 테스트 코드를 작성할 수 있으며, 주입하는 객체가 누락된 경우 컴파일 시점에 오류를 발견할 수 있다.

final 키워드 작성

final 키워드를 통해 컴파일 시점에 누락된 의존성을 확인할 수 있다.

순환 참조

  • 순환 참조를 방지할 수 있다.

  • 개발을 하다보면 여러 컴포넌트 간에 의존성이 생기게 되는데 필드 주입과 수정자 주입은 빈이 생성된 후에 참조하기 때문에 애플리케이션이 어떠한 오류, 경고 없이 구동되어 실제 코드가 호출될 때까지 문제를 알 수 없다.

  • 그러나 생성자를 통해 주입하게 되면 BeanCurrentlyInCreationException이 발생하게 되어 오류를 바로 파악할 수 있다.

profile
백엔드를 지망하는 개발자

0개의 댓글