💋 인트로
스프링의 DI 프레임워크에 대해서 공부를 하다 보니, 기존 자바 코드에서 하던 의존성 주입에 비해 어떤 점이 더 좋은 지에 대한 궁금증이 생겼다.
자바 코드를 사용해서도 의존성 주입을 받을 수 있다.
Spring이 없이도, 우리는 순수 자바 코드만으로도 이제까지 확장 가능성을 고려하면서 전략패턴 등을 사용해 효과적으로 의존성을 주입해 왔다.
그렇다면 Spring의 DI 프레임워크를 사용하는 것이 순수 자바 코드의 의존성 주입과 비교해서 더 좋은 이유는 무엇일까?
객체에 대해서 존재하는 모든 책임을 분리해서 생각하자면, 객체의 생성에 대한 책임과 객체의 사용에 대한 책임이 있다. 우리가 기존에 순수 자바 코드에서 의존성을 주입하는 방식을 사용한다고 했을 경우를 예를 들어 생각해 보자.
public final class ChessApplication {
public static void main(String[] args) {
final PieceDao chessGameDao = new DBPieceDao();
final ChessStatusDao chessStatusDao = new DBChessStatusDao();
final ChessService chessService = new ChessService(chessGameDao, chessStatusDao);
final ChessController chessController = new ChessController(chessService);
chessController.run();
}
}
ChessService 객체를 외부 객체인 ChessApplication에서 생성한 뒤, ChessController 객체의 생성자를 통해 주입했다. 이제 ChessService 객체는 ChessController 객체의 부품이 된다. ChessService 객체의 생성에 대한 책임을 외부 객체인 ChessApplication에서 한 뒤 ChessController 객체에 의존성을 주입했다. 여기서 조금 더 프로그램 확장에 대비하자면, ChessService 객체로부터 인터페이스를 뽑아낸다면 바꿔치기 해서 주입할 수 있다.
하지만 여전히 문제가 있다. 객체를 생성하는 ChessApplication이다. ChessApplication는 내부에서 생성한 ChessService 객체를 상당히 자유롭게 사용할 수 있다. ChessService 객체 내부의 public 필드나 메서드에는 모두 접근할 수 있다. ChessApplication가, ChessService의 생성만 담당했을 뿐인데, 자신이 사용하려고 의도한 클래스가 아닌데도 ChessService에 대한 정보를 모두 알 수 있다는 문제점이 발생했다.
이 문제점을 어떻게 해결할 수 있을까?
ChessApplication 외부에 ZZ 객체를 하나 더 만들어서 의존성을 주입하면 되나?
이렇게 하면 끝도 없다….
이처럼 객체의 생성에 대한 책임이 가지는 의미는 생각보다 크다.
객체의 생성 책임에 대한 부분을 코드에 작성하게 되면 ChessService 객체와 ChessApplication 객체가 상당히 강하게 결합된다는 것을 알 수 있다.
💋 Spring DI 프레임워크를 사용하면???
그렇다면 Spring DI 프레임워크를 사용하면 어떻게 될까?
Spring의 DI 프레임워크는 전체 애플리케이션에서 객체의 생성에 대한 모든 책임을 우리가 사용하는 코드에서 접근할 수 없는 아예 외부로 데려가 버렸다. 외부에서 객체를 모두 생성해주고, 각 객체를 부품이라고 생각한다면 심지어는 조립까지 해준다.
이 결과적으로 우리가 누리게 될 장점은 아래와 같다.
- 결합도를 낮춰 유지보수가 쉬워진다.
스프링 DI 프레임워크를 사용하면 순수 자바 코드를 사용하던 때에 비해 객체의 '생성'에 대한 책임조차 외부로 빼버리기 때문에 객체 간의 의존성이 더더더더더 낮아지므로, 유지보수가 쉬워진다.
모듈 간의 결합도가 낮아지면, 코드 수정 시 다른 모듈에 영향을 주지 않으므로 코드 수정 작업이 더욱 쉬워진다.
- 객체 생성과 초기화를 자동화할 수 있다.
스프링 DI 프레임워크는 객체 생성과 초기화를 자동화하여 개발 시간을 단축시킬 수 있다.
객체 생성과 초기화 작업을 개발자가 직접 작성하지 않으므로, 코드가 간결해진다.
- 테스트 용이성이 높아진다.
객체 안에서 다른 객체를 생성하게 되면 외부에서는 그 객체에 접근할 수가 없어서 테스트하기가 어렵다.
객체 간의 의존성을 인터페이스를 통해 관리하므로, 테스트하기 쉽다.
위에서 소개한 장점은, 자바 코드로도 가능하다.
복잡한 내용이라 이 포스팅에서는 설명하지 못하지만, 아래와 같은 장점도 있다.
코드에 큰 변화 없이 빈을 싱글톤으로 관리할 수 있다.
싱글톤으로 관리하는 것은 생각보다 많은 코드가 필요하고, 이 코드를 사용하는 데는 단점이 존재한다.
스프링은 모든 단점을 해결하면서 빈을 싱글톤으로 관리한다. (현재는 그렇구나, 하고 넘어가면 될 것 같음)