도대체 빈이 아닌 객체가 왜 빈을 받고 싶은 일들이 생기는 것이고 어떻게 해결해야 할까?

스프링 IoC 컨테이너가 관리하는 객체를 빈(bean)이라고 부르고 스프링은 빈에만 의존성을 주입해 줍니다. 물론 아주 고오오오오급진 방법을 쓰면 빈이 아닌 객체에도 빈을 주입하는 방법이 있긴하지만 10년 전에 그 기술을 레퍼런스에서 보고 공부 삼아 해본 적 이후로 한번도 실무에서 그 방법이 쓰인걸 본적이 없습니다. 그러니까 그 고오오오급진 방법은 패스. (궁금하신 분들은 스프링, @Configurable 이라는 키워드로 검색해보세요.)

그럼 다시 본론으로 돌아가서 어떤 경우에 빈이 아닌 객체에 빈을 주입하고 싶어질까? 다양한 경우가 있겠지만, 웹 애플리케이션을 개발하다보면 종종 스프링 데이터 JPA를 통해 만든 Repository를 Validator 또는 Interceptor에서 쓰고 싶은 경우가 있다.

그런데 Validator나 Interceptor를 사용하는 예제를 보면 대부분 new를 사용해서 다음과 같이 등록하는 모습을 볼 수 있다.

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new NotificationInterceptor());
}
@InitBinder("studyForm")
public void studyFormInitBinder(WebDataBinder webDataBinder) {
    webDataBinder.addValidators(new StudyValidator);
}

그럼 이제 어떻게 해야 할까?

NotificationInterceptorStudyValidator는 아직 빈이 아니라서, 다른 빈을 주입 받지 못하는데 어떻게 하지?

빈으로 만들거나, 의존성을 직접 주입하면 된다.

  1. 빈으로 만들기
@Component
@RequiredArgsConstructor
public class NotificationInterceptor implements HandlerInterceptor {

    private final NotificationRepository notificationRepository;

}

이런식으로 NotificationInterceptor@Component 애노테이션을 추가해서 빈으로 등록하면, 이제부터는 다른 빈을 주입받을 수 있으니 생성자를 통해 스프링이 자동으로 NotificationRepository 타입의 의존성을 주입해준다.

빈으로 등록했으니 이제부터는 쓸때로 new로 직접 생성하는게 아니라 NotificationInterceptor를 주입 받아서 쓰면 된다. 이런식으로.

private final NotificationInterceptor notificationInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(notificationInterceptor);
}

2. 직접 주입하기

굳이  해당 객체를 빈으로 만들고 싶지 않다면 스프링이 해주는 의존성 주입 말고 직접 의존성을 주입하는 방법도 있다. 대신 NotificationIntercptor에 의존성을 주입 받을 수 있는 장치를 마련해야 하는 것은 이전 방법과 동일하다. 생성자 또는 Setter가 있어야한다는 말이다. 생성자가 너무 많아서 일일히 다 하나씩 추가해주는게 힘들다면 세터를 통한 주입을 고민해 보는것도 좋겠다. 아무튼! 이전 코드와의 차이라면 @Component를 빼서 빈이 되지 않게끔 하는것 정도.

쓸때는 이제 직접 의존성을 주입해야 하니까 NotificationRepository를 받아서 NotificationInterceptor를 만들 때 생성자가 세터를 통해 의존성을 주입해주면 된다.

private final NotificationRepository notificationRepository;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new NotificationInterceptor(notificationRepository));
}

개인적으로는 전자의 방법 즉 빈으로 등록하는 방법을 선호한다. 굳이 싱글톤 객체로 쓰는데 문제가 없는 객체라면 빈으로 등록을 하지 말아야 할 이유가 전혀 없기 때문이다. 또한 후자의 방법은 불필요하게 NotificationRepository를 다른 코드에 노출 시킨다는 문제도 있다.

스프링과 JPA 기반 웹 애플리케이션 개발 - 인프런
이 강좌에서 여러분은 실제로 운영 중인 서비스를 스프링, JPA 그리고 타임리프를 비롯한 여러 자바 기반의 여러 오픈 소스 기술을 사용하여 웹 애플리케이션을 개발하는 과정을 학습할 수 있습니다. 이 강좌를 충분히 학습한다면 여러분 만의 웹 서비스를 만들거나 취직에 도움이 될만한 포트폴리오를 만들 수 있을 겁니다.