[Java] DI(Dependency Injection), IOC(Inversion of Control)

SMI·2024년 12월 16일

IOC(Inversion of Control)

"제어의 역전"이라고도 불리며, 객체 지향 프로그래밍에서 제어 흐름을 개발자가 아닌 프레임워크나 컨테이너가 관리하도록 하는 디자인 원리입니다.

스프링 프레임워크를 적용하기 전엔 객체의 생명 주기를 클라이언트 구현 객체가 직접 관리했습니다. 하지만 스프링을 사용하면 객체들의 로직은 우리가 직접 구현하지만, 해당 객체가 언제 호출되는지 신경 쓸 필요가 없습니다. 즉, 프로그램의 제어권이 역전된 것입니다.

IOC 장점

  • 프로그램의 진행 흐름과 구체적인 구현을 분리시킬 수 있습니다.
  • 개발자는 비즈니스 로직에 집중할 수 있습니다.
  • 구현체 사이의 변경이 용이합니다.
  • 객체 간 의존성이 낮아집니다.

Dependency 의존관계란 무엇인가?

"A가 B를 의존한다."
"A가 B에서 정의된 메서드를 사용한다."
"의존 대상 B가 변하면, 그것이 A에 영향을 미친다."

예를 들어 햄버거 가게 요리사는 햄버거 레시피에 의존한다. 햄버거 레시피가 변화하게 되면, 변화된 레시피에 따라 셰프는 햄버거를 만드는 방법을 수정해야 합니다.
때문에 셰프는 햄버거 레시피에 의존하고 있습니다.

class BurgerChef {
	private HamBurgerRecipe hamburgerRecipe;

	public BurgerChef(){
    	hamburgerRecipe = new HamBurgerRecipe();
	}
}

또는

@Component
public class BurgerChef {
    private final HamBurgerRecipe hamburgerRecipe;

    // 생성자 주입 방식으로 의존성 주입
    // IOC에 의해 스프링 컨테이너가 의존성 주입
    @Autowired
    public BurgerChef(HamBurgerRecipe hamburgerRecipe) {
        this.hamburgerRecipe = hamburgerRecipe;
    }
}

DI

IOC/DI 개념에선 의존하고자 하는 객체를 더 이상 직접 생성하지 않습니다. 스프링 컨테이너가 생성된 인스턴스를 주입해줍니다.
DI를 통해 모듈 간의 결합도가 낮아지고 유연성이 높아집니다.

의존성 주입 방법으로 크게 3가지가 있습니다. 스프링에선 생성자 주입 방식을 추천하고 있습니다.

생성자를 통한 의존성 주입

스프링에서 권장하는 방식

@RestController
class MyController {
	private final MyService myService;
    
    @Autowired
    public MyController(MyService myService){
    	this.myService = myService;
    }
}
  • 불변성 보장: 생성자 방식에서는 의존성 객체가 final로 선언될 수 있기 때문에, 객체 생성 시 의존성 객체가 반드시 주입되어야 합니다. 이를 통해 객체가 생성될 때 불변성을 보장할 수 있습니다.
  • 의존성 누락 방지: 객체가 생성될 때 의존성이 반드시 주입되므로, 의존성이 누락될 일이 없습니다.

Setter를 통한 의존성 주입

@RestController
class MyController {
	private MyService myService;
    
    @Autowired
    public void setMyService(MyService myService){
    	this.myService = myService;
    }
}
  • 선택적 의존성: 세터 방식은 객체 생성 후 의존성을 주입할 수 있기 때문에, 의존성이 선택적일 때 유용합니다. 예를 들어, 어떤 객체는 의존성 없이 생성할 수 있고, 나중에 의존성만 주입하면 되는 경우에 적합합니다.

  • 수정 가능성: 세터 방식은 의존성 주입 후에도 변경이 가능하므로, 객체 생성 후에 의존성을 변경할 수 있는 유연성을 제공합니다. 이로 인해 한 번 주입된 의존성을 나중에 변경할 수 있는 상황에서 유리합니다.

  • 불변성 부족: 객체 생성 후 메서드 호출 시 의존성이 주입되기 때문에 의존성이 주입되지 않거나 변경될 가능성이 있어 객체가 예기치 않게 변할 수 있습니다. 특히 final을 사용하여 불변 객체를 보장할 수 있는 생성자 방식에 비해 이 부분에서 약점이 있습니다.

필드를 통한 의존성 주입

@RestController
class MyController {
	@Autowired
	private MyService myService;
}
  • 불변성 부족
  • 유연성 부족
  • 테스트 어려움
  • 코드의 간결화

Reference
https://mozzi-devlog.tistory.com/18
https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/

profile
끈기있게 파고드는 개발자가 되기 위해 노력하고 있습니다.

0개의 댓글