스프링에 처음 입문하면 의존성 주입(DI)라는 키워드를 자주 듣게 됩니다.
의존성을 주입한다, 라는게 무슨 말일까요?
의존성을 왜 주입해야할까요?
애초에 의존성은 무엇인가요?
이번 아티클을 통해 모두 알아보고자 합니다.
의존성은 무엇인가요?
의존성(Dependency)이란 무엇이라고 생각하시나요?
객체지향은 변경에 용이하게 대처하기 위해 탄생했습니다.
기존의 절차지향 패러다임은 변경에 취약했습니다.
조그마한 비즈니스 요구사항의 변경이 코드 구조를 통째로 뒤흔들기도 했습니다.
그렇기에 객체지향 패러다임이 주목받았습니다.
추상화와 캡슐화를 통해 변경에 용이하게 대처할 수 있었기 때문입니다.
그렇다면 의존성이란 개념은 어떻게 객체지향과 연관이 있을까요?
의존성은 연결이다
레고를 한번 생각해보겠습니다.
레고는 팔, 다리, 머리 등 분리가 가능합니다.
만약 파란색 팔이 마음에 들지 않는다면 빨간색 팔로 대체해도 아무런 문제가 없습니다.
반면 팔, 다리, 머리 등이 분리가 불가능한 일체형 레고가 있다고 해봅시다.
파란색 팔을 가진 레고가 마음에 들지 않다고 해도 곧바로 이 요구사항을 충족시킬 수 있을까요?
유일한 해결책은 레고를 새로 사는 것입니다.
즉, 변경에 쉽게 대응하려면, 분리가 가능하고 갈아끼울 수 있어야 합니다.
분리가 가능하다는 것은 연결이 필요하다는 것을 의미합니다.
마치 레고의 팔, 몸통이 연결되듯이 말이죠.
객체지향 세계에서는 이 연결을 의존성이라는 키워드로 설명합니다.
의존성 주입(Dependency Injection)
의존성 주입은 그렇다면, 객체들 사이의 연결 통로를 개설하는 것과 동일합니다.
비슷하게, <오브젝트>의 저자 조영호님은 의존성의 한 종류인 연관관계에 대해 다음과 같이 설명합니다.
연관관계란 탐색 가능성이다.
의존성 주입을 하게 되면 런타임에 특정 객체가 어떤 객체와 연결될지를 결정할 수 있게 됩니다.
코드를 살펴보죠.
경찰관 객체를 한 번 만들어보려고 합니다.
public class Police {
private Gun gun = new Pistol();
public void shoot() {
gun.shoot();
}
}
경찰관은 총을 가지고 있을 수 있습니다.
그렇기에 Gun 인터페이스를 구현하고, Pistol 객체가 상속받도록 구현했습니다.
그리고 Pistol 객체를 Police가 인스턴스 변수로 가졌습니다.
즉, Police는 Pistol 객체와의 의존성이 존재하는 상황이네요.
총은 종류가 여러 개가 있죠.
샷건(Shotgun), 권총(Pistol), 라이플(Rifle) 등..
만약 경찰관마다 총기가 다르다면 위와 같은 코드는 유효할까요?
총기가 달라질 때마다 PistolPolice, ShotgunPolice 처럼 여러 객체를 만들어야 하지 않을까요?
결국 위 코드는 변경에 적극적으로 대응하지 못하는 설계입니다.
아래와 같은 코드는 어떤가요?
public class Police {
private Gun gun;
public Police(Gun gun) {
this.gun = gun;
}
public void shoot() {
gun.shoot();
}
}
Police tom = new Police(new Pistol());
Police jake = new Police(new Shotgun());
// 위처럼 의존성을 객체 외부에서 주입해주면 유연한 설계가 가능하다
경찰관은 구체적인 정보(어떤 총인지)는 모르고 추상적인 정보(총이라는 것)에만 의존합니다.
생각해보면 경찰관 입장에서는 어떤 총(샷건, 라이플, 권총)이 오더라도 상관이 없습니다.
그저 shoot이라는 책임만 수행할 수 있다면 되니까요.
이처럼 객체 내부에서는 추상적인 정보에 의존하게 하고, 구체적인 연결은 밖에서 결정하는 것을 의존성 주입(DI)라고 합니다.
의존성 주입을 통해 시스템의 유연성을 극대화시킬 수 있고, 변경 여파를 억제할 수 있습니다.
Spring에서 IoC 컨테이너가 사용하는 메커니즘도 바로 이 의존성 주입(DI)입니다.
정리
변경을 억제시키기 위해 객체지향과 의존성이라는 키워드가 등장했습니다.
의존성이라는 것은 모듈이나 객체 간의 연결통로를 의미합니다.
이러한 연결통로를 객체 스스로가 결정하게 된다면, 유연한 설계에서 멀어지게 됩니다.
따라서 외부에서 객체 간의 연결통로를 지정해주는 방법을 사용하게 되었고, 이를 의존성 주입(Dependency Injection)이라고 합니다.
스프링에서는 IoC 컨테이너가 이 의존성 주입이라는 메커니즘을 적절히 사용합니다.
마무리하며
의존성과 의존성 주입에 관해 알아보았습니다.
DI는 객체지향스러운 설계를 이끄는 하나의 설계 원칙입니다.
다만 DI가 무조건 정답은 아니라는 것도 알아야 합니다.
정말 좋은 설계는 비즈니스 요구사항에 따라 객체지향과 절차지향을 적절히 혼합한 설계입니다.
필요한 곳에 유연성을 부과하고, DI를 적용하려는 의식적인 노력이 중요하다고 생각됩니다.
혹시 오류 지적이나 궁금한 점, 그리고 토론하시고 싶은 부분이 있다면 댓글 부탁드립니다!
'Spring' 카테고리의 다른 글
Spring Interceptor (8) | 2023.05.03 |
---|---|
ResponseEntity, RequestEntity, HttpEntity (1) | 2023.05.01 |
Spring Boot에서 로깅해보기 (1) | 2023.05.01 |
빈 스코프 (0) | 2023.04.23 |
Bean, IoC 컨테이너의 기본 개념 정립하기 (0) | 2023.04.16 |