728x90

의존관계와 의존관계 주입

서로 의존하고 있는 관계라는 것은 한 쪽의 변화가 다른 한 쪽에 영향을 끼친거나

한 쪽이 다른 한 쪽의 멤버를 사용하는 경우 등이 의존관계에 있다고 볼 수 있다.

 

의존관계는 설계 모델에서의 의존 관계런타임 시에 만들어지는 의존관계가 있는데

스프링에서의 의존관계 주입은 이런 런타임 시의 의존관계가 있고,

이러한 런타임 시의 의존관계를 오브젝트 의존관계라고 한다.

 

즉, 실제 사용대상인 의존 오브젝트와 그것을 사용하는 주체(클라이언트) 오브젝트

런타임 시에 연결해주는 작업을 해주는 것을 의존관계 주입(DI)이라고 한다.

 

의존관계 주입은 아래와 같은 세 가지 조건을 만족해야 한다.

  • 클래스 모델 및 코드에는 런타임 시점의 의존관계가 드러나지 않게, 클래스와 클래스 간의 의존이 아닌 인터페이스를 통해서만 의존하고 있어야 함
  • 런타임 시점의 의존관계는 컨테이너와 팩토리 같은 제 3자가 결정
  • 의존관계는 사용할 오브젝트(의존 오브젝트)에 대한 참조를 외부에서 주입하여 만들어짐

쉽게 정리하자면 개발자가 코드를 설계하고 작성하는 시점에는 오브젝트 간의 연결(의존)이 없지만

런타임 시(서버가 동작할 때)에 오브젝트 간에 연결되는 것이다.

 

이전 게시글부터 현재까지 코드를 수정하는 과정을 살펴보면 

코드를 작성할 때는 인터페이스를 통해 느슨한 연결을 유지하는 방향으로 작성하고

애플리케이션 컨텍스트를 통해 런타임 시의 의존관계를 주입하였는데

이러한 방식을 스프링의 의존관계 주입이라고 이해할 수 있다.

의존관계 검색과 주입

스프링이 제공하는 IoC 방법에는 의존관계 주입말고도 의존관계 검색이라는 방법이 있는데

의존관계를 외부로부터 주입 받는 것이 아니라 스스로 검색을 이용하는 방법이다.

 

자신이 필요로 하는 의존 오브젝트를 능동적으로 검색하는 기능으로

검색만 할 뿐이지 주입까지 하는 기능은 아니다.

 

이러한 의존관계 검색을 해주는 메서드를 이전에 사용해본 적이 있는데

바로 빈 팩토리가 제공하는 getBean 메서드다.

 

일반적인 의존관계 주입의 코드를 살펴보면

public UserDao() {
	DaoFactory daoFactory = new DaoFactory();
    this.connectionMaker = daoFactory.connectionMaker;
}

이렇게 메서드나 생성자를 통해 의존관계를 주입해주지만

아래와 같이 검색, 즉 컨테이너(애플리케이션 컨텍스트)에게 요청하여 처리할 수도 있다.

AnnotationConfigApplicationContext context = 
	new AnnotationConfigApplicationContext(DaoFactory.class);
    
this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class);

애플리케이션 컨텍스트를 통해 getBean 메서드를 사용하여 의존관계를 검색해서 주입할 수 있다.

어떤 방식을 사용하는 것이 좋은가?

의존관계 검색을 통해 의존관계를 주입하는 방법은 코드에 스프링 API가 직접적으로 나타나기에

애플리케이션 컴포넌트가 서버와 관련된 성격이 다른 오브젝트에 의존하게 되어 좋은 방법이 아니라서

보통 애플리케이션 컴포넌트에 의존관계를 주입하는 경우에는 일반적인 의존관계 주입을 사용한다.

 

하지만 의존관계 검색이 쓸 필요가 없었다면 알아보지도 않았을 것인데, 이러한 방법도 다 사용하는 곳이 있다.

 

애플리케이션의 기동 시점에는 최소한 한 번은 의존관계 검색을 사용해 오브젝트를 가져와야 하는데

static 메서드인 메인 메서드, 즉 스프링이 실행되는 메서드와 서버는

의존성 주입을 이용해 오브젝트를 주입 받을 방법이 없기 때문이다.

 

또한 의존관계 주입은 주입 받으려는 대상 모두가 빈 오브젝트여야 하지만

의존관계 검색은 빈 오브젝트가 아닌 경우에서도 사용할 수 있다.

 

의존관계 주입은 특정 클래스 타입으로 고정된 것은 주입 받을 수 없는데

의존관계 주입이 런타임 시에 다이나믹하게 결정해서 제공받아야 하기 때문이다.

 

즉, 클래스를 주입 받는 것이 아닌 인터페이스를 주입 받아야만 한다.

의존관계 주입의 장점과 응용

지금까지 알아본 스프링, IoC, DI 같은 모든 것들이 객체지향 설계를 지향하는 방식이라고 했듯이

이러한 의존관계 주입을 적용하면 객체지향 설계로 얻을 수 있는 장점들을 모두 챙길 수 있다.

 

간단하게 요약하면 결합도가 낮은 코드책임을 철저하게 분리하여 코드의 수정, 확장 등이 자유로워진다.

 

이러한 장점을 통해 여러가지 다양한 응용을 할 수 있다.

 

우선 이전의 내용 중에서 초난감 DAO 코드를 수정하면서 알 수 있었듯이

데이터베이스의 설정을 바꿔야 하는 경우 인터페이스를 통해 느슨하게 연결되어 있기 때문에

해당 인터페이스를 구현한 데이터베이스의 설정을 모두 사용할 수 있다는 것이다.

 

서버에서 사용될 설정, 개발시에 사용될 설정, 테스트 시에 사용될 설정들을

하나의 인터페이스를 통해 복잡한 코드 수정 없이 모두 사용할 수 있고,

이것은 객체지향 설계의 장점인 다형성과 같다고 볼 수 있다.

 

새로운 기능을 추가하고 싶은 경우도 마찬가지로

기능을 사용하고자 하는 오브젝트와 같은 인터페이스를 구현하는

새로운 오브젝트를 추가하여 의존관계를 추가/수정해주기만 하면 된다.

메서드를 이용하여 의존관계 주입하기

메서드를 이용하여 의존관계를 주입할 수 있는데 생성자를 통한 의존관계 주입보다 자주 사용된다.

 

수정자 메서드와 일반 메서드를 이용한 주입 방법이 있는데

수정자 메서드를 이용한 주입 방법부터 살펴보겠다.

 

수정자 메서드를 이용한 주입

수정자 메서드를 이용한 주입은 외부에서 내부의 속성 값을 변경할 때

주로 사용되는데 여기서 수정자는 Setter를 말한다.

 

수정자 메서드는 파라미터로 값을 전달 받을 수 있기 때문에

외부로부터 제공받은 오브젝트 참조를 저장해뒀다

내부 메서드에서 사용하는 의존관계 주입 방식에서 활용하기 좋지만

하나의 파라미터만 가질 수 있다는 단점이 있다.

private ConnectionMaker connectionMaker;

// 기존 생성자 사용
public UserDao(ConnectionMaker connectionMaker) {
    this.connectionMaker = connectionMaker;
}

// 수정자 메서드 사용
public void setConnectionMaker(ConnectionMaker connectionMaker) {
    this.connectionMaker = connectionMaker;
}

 

@Bean //기존
public UserDao userDao() {
    UserDao dao = new UserDao(connectionMaker());
    return dao;
}

@Bean //수정자 메서드
public UserDao userDao() {
    UserDao dao = new UserDao();
    dao.setConnectionMaker(connectionMaker());
    return dao;
}

일반 메서드를 이용한 주입

수정자 메서드와 비슷하지만 수정자 메서드의 단일 파라미터 단점을 보완한

여러 개의 파라미터를 받기 위해 수정자 메서드 방식 대신 사용하는 방법이다.

 

보통은 수정자 메서드를 가장 많이 사용하며, 이외에도 다양한 의존관계 주입 방법이 있다.

 

'Back-End > Spring' 카테고리의 다른 글

테스트  (0) 2023.06.07
XML 설정  (0) 2023.06.07
싱글톤 레지스트리 / 오브젝트 스코프  (0) 2023.06.07
스프링 IoC  (0) 2023.06.05
IoC(Inversion of Control, 제어의 역전)  (0) 2023.06.05

+ Recent posts