스프링 의존관계 주입 정리

뇸뇸·2021년 8월 31일
4

Spring

목록 보기
1/1

✅의존관계란

의존관계를 설명하기 위해 콜라를 마시는 행위를 객체지향 관점에서 알아보겠다.

실세계에서 ""가 "콜라"를 마시면 콜라의 양이 줄어든다. 콜라의 양을 줄게 하는 주체는 다.

너무 당연한 소리지만 객체지향 세계에서는 아니다.

객체지향 세계에서는 "콜라"가 가지고 있는 "마신다"라는 행위를 통해 콜라 객체 스스로 콜라의 양을 줄인다.

코드로 표현하면 대략 이렇게 표현할 수 있을 것이다.

public class I {

    private Coke coke = new Coke();

    public void drink() {
        coke.isDrunk();
    }
}
public class Coke {

    private Integer capacity;

    public Coke() {
        this.capacity = 500;
    }

    public void isDrunk(){
        this.capacity -= 1;
    }
}
public static void main(String[] args) {
        I i = new I();
        i.drink();
    }

I 객체에 Coke가 없다면, I 객체는 콜라를 마시는 행위를 할 수 있을까? 없을 것이다. I는 Coke가 필요하다.

이렇게 I 객체가 drink라는 행위를 하기 위해서 Coke 객체를 I 객체 내부가지고 있는 것을 "의존관계 또는 의존성을 가지고 있다"고 한다.

I는 Coke와 의존관계고, I는 Coke에 대한 의존성을 가진 것이다.


객체지향 세계에서의 객체들은 상호작용하며 어떠한 목표를 달성하는 역할을 한다.
객체들은 또 다른 객체들과의 메시지를 주고받으며 그들이 가지고 있는 행위(메서드)를 호출한다.



✅다형성의 한계

보통 애플리케이션을 설계할 땐 인터페이스와 구현체들이 여럿 존재한다.

아래와 같이 I 객체 내부Cola 인터페이스가 있고 Pepsi 구현체가 있다면, 객체 I는 Cola에도 의존하고 Pepsi에도 의존하게 된다.

public class I {

    private Cola cola = new Pepsi();

    public void drink() {
        cola.isDrunk();
    }
}

만약 애플리케이션 전체에서 사용하고 있는 Pepsi를 일괄적으로 CocaCola(코카콜라)로 변경한다면 이에 따른 수정은 피할 수 없을 것이다.

그 이유는 I 객체가 추상화(인터페이스)에만 의존하지 않고 구체화(구현 클래스)에도 의존하기 때문이다.

SOLID 원칙 중 OCP와 DIP 위배

  • OCP : 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.
  • DIP : 추상화에 의존하고 구체와에 의존하면 안 된다.


✅의존관계 주입

위에서 본 코드 중에 CocaCola와 Pepsi의 의존관계를 프로그래머가 직접 정하지 않고 외부에서 정해준다면 소스의 수정 없이 외부의 도움을 받아 구현 클래스를 변경할 수 있을 것이다.

public class I {

    private Cola cola;

    public I(Cola cola){
        this.cola = cola;
    }

    public void drink() {
        cola.isDrunk();
    }
    
}
public static void main(String[] args) {
        Pepsi pepsi = new Pepsi();

        I i = new I(pepsi);
        i.drink();
}

편하게 설명하기 위해 메인메서드를 사용한 것뿐이지 다른 객체 혹은 설정 파일에서 의존관계를 외부에서 주입해줄 수 있다.

만약 이렇게 동작하는 애플리케이션이 있을 때, 메인메서드에 선언한 Pepsi를 CocaCola로 변경해준다면 Cola의존하는 모든 객체들이 소스 수정없이 Pepsi에서 CocaCola로 변경될 수 있을 것이다.

public static void main(String[] args) {
        CocaCola cocaCola = new CocaCola();

        I i = new I(cocaCola);
        i.drink();
}

의존관계 주입에서 수정하지 않아도 된다는 말은 외부 설정의 수정이 아닌 실제 의존하고 있는 객체 내에서 로직 수정이라든지 구현클래스를 갈아 끼우기 위한 수정이 필요 없다는 말이다.



✅스프링의 의존관계 주입

위에서 설명한 외부의 역할을 스프링은 DI 컨테이너(=IOC 컨테이너)가 해준다.

DI(Dependency Injection) 컨테이너는 말 그대로 의존성을 주입해주는 컨테이너로 스프링에서는 BeanFactory를 상속받은 ApplicationContext 인터페이스가 이 역할을 담당한다.

DI 컨테이너가 의존관계 주입을 지원하는 형식은 Java, XML, Groovy 등이 있지만 대부분의 개발자가 선호하는 방식은 Java이다.

Java에서 의존관계를 주입하는 방법은 여러 방식이 존재한다.

  • 생성자 주입방식
  • 필드 주입방식
  • 수정자 주입방식(setter)

스프링생성자 주입을 권고하므로 생성자 주입방식에 대해 알아보겠다.

I 객체가 의존하고 있는 Cola는 Interface이고, Cola는 Pepsi와 CocaCola를 구현체로 가지고 있다.

@Component
public class Pepsi implements Cola{

	...
}
@Component
public class CocaCola implements Cola{

	...
    
}
@Component
public class I {

    private Cola cola;

    @Autowired
    public I(Cola cola){
        this.cola = cola;
    }

    ...
    
}

DI 컨테이너는 애플리케이션이 시작될 때, @Component 어노테이션이 붙은 Class들을 스프링 빈으로 등록시키고, 스프링 빈으로 등록된 Class들이 필요한 곳(= @Autowired이 붙은 곳)에 의존관계를 주입해준다.

위 코드로 풀어서 설명하면,

  • 스프링 빈 : I, Pepsi, CocaCola
  • 의존관계 주입 : DI 컨테이너가, @Autowired가 붙은 곳에 주입되어야 할 또 다른 스프링 빈을 주입해준다.

즉, Pepsi, CocaCola 둘 중 어느 것이든 @Autowired가 붙은 곳에 Cola 타입으로 주입이 될 것이다.

참고로 위 코드는 DI 컨테이너가 Pepsi, CocaCola 둘 중 어느 구현체를 주입해줄지 모르기 때문에 에러가 난다.
이 부분은 해결하는 방법은 다양한데, 아래 키워드를 참고하면 좋을 것 같다.

  • @Primary
  • @Qualifier
  • 필드 명 의존관계 주입
  • 파라미터 명 의존관계 주입

Spring BeanDI 컨테이너가 관리(=등록된)하는 자바 객체다.

ApplicationContext을 직역하면 애플리케이션의 문맥, 상황 같은 의미인데 결국 애플리케이션의 흐름을 담당하는 추상적인 의미로 이해하면 좋을 것 같다.

BeanFactory는 스프링 빈에 관련된 내용을, ApplicationContext는 국제화, 애플리케이션 환경 등을 관리하는 기능을 가진 Interfcae이다.




✅마치며..

스프링 공부를 처음 시작할 때 이해가 잘 안되었던 의존관계 주입을 정리해봤다.
전체적인 흐름을 간략히 정리하고 싶었기 때문에 기술적으로 생략한 부분이 많다.
의존관계 주입의 틀을 잡는 글이라고 생각해주셨으면 한다.

아래는 생략 및 간소화한 내용이다. 처음 보는 내용이 있다면 한 번쯤 관심 가져보는 것을 추천드린다.

  • 객체지향 패러다임
  • SOLID
  • 의존관계 주입방식
  • @Component
    • 어노테이션 기반 의존관계 주입
    • @ComponentScan
  • @Bean
  • @Configuration
  • ApplicationContext
    • AnnotationConfigApplicationContext
    • GenericXmlApplicationContext
  • 스프링 빈
    • Bean Definition
  • 편리한 외부 라이브러리
    • Lombok을 이용한 의존관계 주입


의존관계 주입 글을 쓰려고 해도 가정해야 하는 부분이 얼마나 되며, 설명해야 하는 범위는 얼만큼이며.. 이런 것들을 정의하는 게 정말 어렵다ㅠ
3번 째 글을 통채로 지웠다 썼다 반복 했는데.. 이 마저도 만족을 못 하겠는데 계속 끌다 보면 평생 못 쓸 것 같아서 출간한다..

과거 나처럼 의존관계 주입을 이해하는 데 어려움을 겪고 계신 분들이 내 글을 읽고 이해가 잘 됐으면 좋겠다.



소중한 피드백 주시면 깊은 성찰을 통해 이 글을 업그레이드시키겠습니다!

✅참고

객체지향의 사실과 오해 (도서)
스프링 핵심원리 (인프런. 우아한형제들 김영한)
오라클 - 자바의 다형성
스프링 - 코어

profile
여러 기술을 학습하고 응용하는 것을 좋아합니다. 나무보다 숲을 보려합니다 :)

0개의 댓글