제가 공부하려고 적은 글이기 때문에, 정확하지 않은 정보가 포함되어 있을 수 있습니다.
틀린 점을 발견하신다면 댓글로 알려주시면 최대한 빨리 수정하겠습니다.
스프링부트을 공부하다보면,꼭 나오는 단어가 있다
IoC가 적용된 SpringBoot의 특성상..
DI를 사용하면 간결해지며, 이러면 개체의 의존성 중복이..
IoC는 DI를 사용하여 작동하며...
이러한 개념을 처음 접했다면, 정말 외계언지 뭔지 못알아 들을 것이다. 따라서, 이에 대한 정확한 개념을 Java 코드와 함께 이해해보자.
일단 DI에 대한 이해 전, 의존성이 뭔지부터 알아보고 가자.
의존성을 쉽게 설명하자면, A코드가 변할때 B코드도 영향을 받는다면 B가 A에 의존한다고 한다.
이해를 위해 예를 하나 들어보겠다.
/*
class name: calculate
description: 계산을 담당하는 클래스
*/
public class Calculate {
// constructor
public Calculate() {}
public int sum(int A, int B) {
return A + B;
}
}
/*
class name: Computer
description: 컴퓨터 구현한 클래스
*/
pulic class Computer {
Calculate calculate = new Calculate();
// ...이하 생략
}
여기서 Computer Class는 Calculate클래스에 변동이 생기면 Computer Class의 작동 결과도 달라진다. 즉 Computer가 Calculater에 의존중인 것이다.
자, 그럼 여기서 이 코드의 문제점이 무엇일까? 대표적으로 2개만 말해보겠다.
public Calculate(int testNumber) {
this.testNumber = testNumber;
}이렇게 코드를 변경해버리면, Computer에서 Calculate를 생성할때도 testNumber 값을 넘겨줘야 한다.이러한 문제때문에, 이러한 방식으로 코드를 작성하게 되면 확장성이 떨어지고, 무슨 기능 추가만 하려하면 의존하는 클래스를 하나하나 고쳐야 하는 엄청난 일거리가 생기는 것이다. 당연히 이러면 유지보수성이 떨어지고, 서비스는 유지보수 불가 사태에 다다르게 된다. 이러한 문제점 해결을 위하여 DI를 사용하는 것이다.
그래서, 이러한 문제점을 어떻게 해결해야 할까..?
당연히 DI를 이용하면 된다! 그럼 지금부터 본격적으로 DI를 알아보자.
DI의 정확한 정의는 다음과 같다
의존성을 외부에서 불러오는 디자인 패턴을 뜻한다.
정의만 보면 뭐라고 하는지 모를 수 있는데, 정확하게 설명해 주겠다. Spring에서의 DI는 객체를 내부에서 생성하지 않고 외부에서 생성후 필요한 객체에 주입해 주는 것이다.
더욱 정확한 이해와 작동흐름의 파악을 위하여 예제를 봐보도록 하자
DI를 적용하지 않은 코드의 경우
pulic class Computer {
Calculate calculate = new Calculate();
//..이하 생략
}
아까랑 똑같은 코드이다. 이러한 코드는 아까도 말했다시피 문제점이 많다. 이러한 코드를 DI를 사용해서 바꾸어보자.
DI를 적용한 코드의 경우
public class Calculate {
// constructor
public Calculate() {}
public int sum(int A, int B) {
return A + B;
}
}
pulic class Computer {
private final Calculate calculator;
// constructor
public Computer(Calculate calculate) {
this.calculator = calculate;
}
//..이하 생략
}
자, 가장 눈에 띄는 변화는 Calculate형의 calculator 변수가 생성되었다는 것과, 생성자로 calculator에 외부에서 받아온 calculate라는 매개변수를 할당하고 있다는 것이다. 이렇게 코드를 짜면 spring의 IoC엔진이 인식하여 자동으로 생성자에 Calculate 클래스를 인스턴스화 시켜 넣어준다.
하지만, 이런 방식은 매개변수가 2개가 넘어가게 되면, 가독성도 없고 복잡하고 유지보수가 힘들어진다.
진정하고, 이럴땐 IoC를 사용하게되면 해결된다.
위에서 잠깐 Spring IoC 컨테이너를 언급했는데.. Spring은 알겠고, 컨테이너는 무언갈 저장하는 공간이라고 대충 추측해봐도, IoC는 뭔지 몰랐을 것이다.

자자, 포기하지 말고 거의 다왔으니깐 차근차근 배워보자.
IoC의 기본적인 개념은 다음과 같다.
프로그래머에게 있던 객체 제어권을 프레임워크에게 넘기는 프로그래밍 기법.
즉, 생성, 관리, 의존성 관리를 위한 할당등을 프레임워크에서 해주는 것이다.
여기서, 스프링이 객체를 관리를 하게되면, 해당 객체를 Bean이라고 한다. 그리고 이 Bean은 IoC 컨테이너에 들어가서 보관되다가, 코드에서 호출될시 자동으로 연결되어 할당된다.
이러한 방식은 프로그램의 유연성, 확장성을 높혀주고, 프로그래머가 의존 관계에 시달리지 않고 비지니스 로직에만 집중 할 수 있게 도와준다.
또한, 앞서 언급했던 DI와 함께 사용하게 된다면, 객체간의 의존성이 훨씬 낮아지게 된다.
DI와 IoC를 사용한 최종 코드를 보여주겠다.
@Controller // Bean으로 제작해준다는 뜻입니다.
public class Calculate {
// constructor
public Calculate() {}
public int sum(int A, int B) {
return A + B;
}
}
@Controller
@RequiredArgsConstructor // final 옵션이 붙어있거나 @NonNull 어노테이션이 붙어있는 모든 변수에 대한 생성자를 생성합니다
pulic class Computer {
private final Calculate calculator;
// 생성자는 RequiredArgsConstructor가 만들어주므로 생략해도 됩니다.
/*
@Autowired // IoC 컨테이너의 Bean객체를 매핑해주라는 어노테이션입니다.
public Computer(Calculate calculate) {
this.calculator = calculate;
}
*/
//..이하 생략
}
https://velog.io/@roro/DI-Dependency-Injection-%EC%82%AC%EC%9A%A9%EC%9D%B4%EC%9C%A0
https://engineerinsight.tistory.com/44#google_vignette