본문 바로가기

Issue

[Java][Spring] Dependency Injection : How to @Autowired fail case and The better case

반응형

Java로 웹 프로젝트를 만들다보면, 반드시 필요한 것 중 하나인 의존성 주입(dependency injection)을 고려하게 된다.

의존성 주입이라 하면 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉을 뜻한다.

 

필자는 작업하던 프로젝트에서 @Autowired를 계속 쓰고 당연하게 주입해서 쓰곤 했다.

(사실 대학에서도 의존성 주입 중 하나의 방법이라고 위 어노테이션을 설명없이 쓰라고만 했었다..)

 

그러던 중 문득 드는 생각.

내가 왜 저걸 써야하는가.

 

우선 이를 이해하기 위해서는 의존성 주입이 대해 좀더 알아봐야했다.

DI, dependency injection 은 spring 프레임워크에서만 사용하는 것이 아닌 객체지향 프로그래밍에서 통용되는 개념이다.

 

 강한 결합 

객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 가지는 구조로,
A 클래스 내부에서 B 라는 객체를 직접 생성하고 있다면, B 객체를 C 객체로 바꾸고 싶은 경우에 A 클래스도 수정해야 하는 방식이다.

 

 느슨한 결합 

객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해서 넘겨받는 구조로,

결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기 때문에 유연한 구조를 가지는 방식이다.

 

Spring 프레임워크를 이용하고 있다면, 예시로 @Autowired를 사용하여 필드로 service 객체를 주입하는 방법을 종종 볼 수 있을 것이다.

 

@Service
public class OrderService {

    @Autowired
    private PayService payService;

    public void purchase() {
        payService.getCard();
    }

}

 

 How to @Autowired fail case 

물론 위와 같이 사용하다보면 간혹 @Autowired가 해당 service 객체를 찾지 못해 null 이 발생하는 경우가 발생하곤 한다.

권장하진 않지만 contextApplicationContextAware를 이용해 참조할 수도 있기도 하다.

[참고] https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null

 

Why is my Spring @Autowired field null?

Note: This is intended to be a canonical answer for a common problem. I have a Spring @Service class (MileageFeeCalculator) that has an @Autowired field (rateService), but the field is null when I...

stackoverflow.com

이러한 주입 방식으로 사용하다 보면 어느새 여러 서비스들 간에 의존관계가 생길 수 있다.

예를 들어 A class 의 method 1 에서 B class의 method 2 을 호출하고, B class의 method 2 에서 A class의 method 1 을 호출하는 상황이다.

 

이는 서로서로 주거니 받거니 호출하다가 결국 StackOverflowError 를 발생하고 죽는데,

이를 실제 코드가 호출되기 전까지 아무도 알 수 없다.

 

즉, @Autowired 주입은 객체 생성 시점에 는 순환참조가 일어나는지 아닌지는 알 수 없다.

 

The better case :  생성자 주입을 이용해 순환참조 방지 

그래서 아래와 같이 @NoArgsConstructor 를 이용해 생성자 자동 주입으로 service 객체를 주입하도록 설정하는 것이 더 좋은 것 같다.

 

@NoArgsConstructor
@Service
public class OrderService {

    private final PayService payService;

    public void purchase() {
        payService.getCard();
    }

}

 

이 방식의 장점은 아래와 같다.

  • Spring 프레임워크가 구동될때 Bean을 생성하면서 순환참조가 발생하면 StackOverflow가 발생하면서 Application 자동 종료
  • 의존 관계가 설정되어 있지 않으면 객체 생성 불가해 컴파일에 인지 가능 ⇢ NPE 방지
  • final로 service 객체를 바꿔치지 할 수 없어 Immutable 유지 가능

 

메모장을 정리하던 중 잊고 있던 원리를 다시금 이해할 수 있는 시간이 된 것 같다.

... 성찰 중 ...

반응형

❥ CHATI Github