[SpringBoot] 의존성 주입(DI)과 제어의 역전(IoC)

yoonthegarden·2024년 4월 3일
0

SpringBoot

목록 보기
1/1
post-custom-banner

SpringBoot를 얼렁뚱땅 배운 탓에 요즘 학교 수업을 들으면서 처음부터 하나하나 정리하는 중이다..^^


스프링 프레임워크

스프링 프레임워크는 자바 플렛폼을 위한 오픈 소스 애플리케이션 프레임워크로 동적인 웹 사이트 개발을 위해 여러가지 서비스를 제공해준다.
스프링 프레임 워크는 IoC/DI, PSA, AOP 이렇게 세가지 핵심 프로그래밍 모델을 지원하는데, 오늘 IoC/DI에 대해 알아보려 한다.

의존성

우선 의존성 주입을 알기 위해선 의존성이 무엇인지부터 아는 것이 중요하다.
다음 코드를 보며 의존성을 알아보자!

public class TodoService {
	private final FileTodoPersistence persistence;
	
	public TodoService() {
		this.persistence = new FileTodoPersistence();
	}
	
	public void create() {
		...
		persistence.create(~);
	}
}

TodoService는 FileTodoPersistence에 의존한다. 이 말은 즉, TodoService 객체는 FileTodoPersistence 없이는 제 기능을 못하며, TodoService가 FileTodoPersistence 객체를 생성하고 관리한다.

이렇게 의존성이 있는 코드의 문제점은 FileTodoPersistence를 다른 클래스로 변경시(ex. DatabaseTodoPersistence나 S3TodoPersistence) TodoService의 코드를 수정해야 한다.
이러한 문제는 FileTodoPersistence에 의존하는 클래스가 많을수록 수정해야 하는 코드가 늘어난다는 점이다.


의존성 주입

이러한 의존성의 단점으로 의존성 주입이 필요하다.
의존성 주입(Dependency Injection, DI)이란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴이다. 인터페이스를 사이에 둬서 클래스 레벨에서 의존 관계가 고정되지 않게 해준다.
또한, 객체 생성 시 의존하는 객체를 외부에서 주입한다. 외부에서 의존하는 객체의 종류를 바꿀 수 있기 때문에 의존성의 단점을 해결할 수 있다.

재사용이 쉬우며, 테스트나 유지 관리가 쉬워진다. 또한, 유연성을 향상시킨다.


단점도 있다. 책임을 분리하기 위해 Class가 늘어나 복잡해질 수 있다. 주입된 객체의 코드 추적이 어려우며, 프레임워크에 대한 의존도를 높인다.

그렇다면 의존성 주입은 어떻게 할 수 있는가?

Spring에서 의존성을 주입하는 덴 다음과 같이 여러 방법이 있다.

  1. 생성자 주입
  2. 수정자 주입 (메서드 주입)
  3. 필드 주입

생성자 주입

생성자를 통해 의존 관계를 주입받는 방법이다.

다음은 생성자 주입의 예시 코드이다.

@Component
public class TodoService {
	private ITodoPersistence persistence; // Interface
	
    @Autowired
	public TodoService (ITodoPersistence persistence) {
		this.persistence = persistence;
	}
	
	public void create() {
		...
		persistence.create(~);
	}
}

@Autowired는 스프링 컨테이너에 의존성을 주입해달라고 지시하는 어노테이션이다.
생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입해주기 때문에 @Autowired를 붙였다.

이러한 주입 방법은 생성자 호출 시점에 1번만 호출되는 것을 보장해주며, 불변과 필수 의존 관계에 사용한다. NPE(NullPointException)을 방지 가능하며, 주입 받을 필드를 final로 선언이 가능하다는 장점이 있다.

Spring 기준 4.3부터는 생성자가 하나일 경우, 생성자의 파라미터 타입이 빈으로 등록 가능할 경우 @Autowired 어노테이션을 생략할 수 있다고 한다.

수정자 주입(메서드 주입)

메서드로 의존 관계를 주입받는 방법이다. 선택과 변경 가능성이 있는 의존 관계에서 사용된다.
자바 빈 property 규약의 수정자 메서드 방식을 사용하는 방법이다. set필드명 메서드를 생성하여 의존관계를 주입한다.

다음은 수정자 주입의 예시 코드이다.

@Component
public class TodoService {
	private ITodoPersistence persistence; // Interface
	
    @Autowired
	public void setITodoPersistence (ITodoPersistence persistence) {
		this.persistence = persistence;
	}
}

필드 주입

필드에서 바로 주입하는 방법이다.

다음은 필드 주입의 예시 코드이다.

@Component
public class TodoService {
    @Autowired
	private final ITodoPersistence persistence; // Interface
	
}

코드가 간결하다는 장점이 있다. 하지만 외부에서 변경 불가능해서 테스트하기 어려움이 있으며, DI 프레임워크(Spring)가 없으면 아무 것도 하지 못한다.

의존성 주입 결론?!

의존성 주입에는 다양한 방법이 있지만 생성자 주입이 권장되고 있다.
(Spring 프레임워크에서도 생성자 주입 사용을 권장하고 있다.)

생성자 주입이 권장되는 이유는 1. 필드 주입의 단점 2. 수정자 주입의 단점 3. 객체의 불변성 확보 4. 테스트 코드 작성의 편리함 5. 순환 참조 방지 6. 개발자의 의존성 주입 실수 방지(final) 이 있다.

각각의 이유는 찾아보길 바란다 :)

@Component
@RequiredArgsConstructor
public class TodoService {

	private final ITodoPersistence persistence; // Interface
	
}

다음 코드는 내가 DI 하기 위해 사용하는 유형의 예시이다.(생성자 주입 방식이다.)
@RequiredArgsConstructor를 통해 final 변수, 필드를 매개변수로 하는 생성자를 만들 수 있기에 주입 받을 객체를 final로 선언해줬다. 또한, Spring 기준 4.3부터는 생성자가 하나일 경우, 생성자의 파라미터 타입이 빈으로 등록 가능할 경우 @Autowired 어노테이션을 생략할 수 있기에 위와 같이 @Autowired를 생략하고 작성했다.

제어의 역전

IoC(Inversion of Control)란 제어의 역전이다.
내가 사용할 객체를 내가 결정하지 않고 외부에서 결정하는 것을 말한다. 즉, 메서드나 객체의 호출 작업을 개발자가 아닌 스프링에게 제어권을 넘기는 것을 말한다.
제어의 흐름을 바꾸는 것이며, 객체의 의존성을 역전시킴으로써 객체 간의 결합도를 줄이며, 유연성을 높인다.

DI는 객체를 직접 생성하는 것이 아닌 외부(IOC컨테이너)에서 생성한 후 주입시켜주는 방식이므로, DI를 통해 IoC가 일어나기 때문에 둘이 같이 묶여서 개념이 정의된다. (Spring에서 위와 같이 DI 시킨다는 한해서 정의한다면!)

하지만 DI와 IoC를 찾다보니 DI를 사용한다고 IoC 컨테이너가 무조건 필요한 것은 아니라고 한다. IoC 개념을 프레임워크와 라이브러리 차이에서도 볼 수 있듯이 IoC는 제어권의 흐름이 변하는 것을 의미하기에 DI보다 @Autowired이 하는 역할이라고 이해하는게 맞는 것 같다.

DI == IoC이 아닌 DI ⊂ IoC라고 생각했는데 둘다 아닌.. (심지어 첨부한 아래 글엔 교집합이 있는 두 개의 집합이라 표현한다.) Spring의 IoC 컨테이너(DI 컨테이너)가 IoC를 발생한다고 생각하는 것이 가장 정확한 표현인 것 같다.

https://jwchung.github.io/DI%EB%8A%94-IoC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80-%EC%95%8A%EC%95%84%EB%8F%84-%EB%90%9C%EB%8B%A4

해당 개념에 대해 이해하는데 읽어보면 좋을 글일 것 같아 첨부한다!




글을 쓰면서도 느끼지만 부족한 부분이 정말 많았고, 여전히 많음을 느낀다.
글을 쓰면서 정확하게 정리하기 위해 더 자세히 찾아보게 되기에.. 블로그를 통해 개념을 정리하는 것은 정말 좋은 습관인거 같다. 이 글을 시작으로 개발 블로그를 본격 진행해봐야겠다는 후기^_^

반박 언제나 받습니다!!!!!


레퍼런스

profile
https://garden-ying.tistory.com/
post-custom-banner

0개의 댓글