Spring Framework/Spring Core

[Spring Framework] 의존관계 자동주입

소재훈 2023. 5. 6. 04:35

본 포스트는 김영한 님의 스프링 핵심 원리 - 기본 편을 보고 정리한 내용입니다.

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

다양한 의존관계 주입 방법

의존관계를 주입하는 방법은 다음 네가지로 나눌 수 있습니다.

  • 생성자 주입
  • 수정자 Setter 주입
  • 필드 주입
  • 일반 메서드 주입

생성자 주입

말 그대로 생성자를 통해서 의존관계를 주입받는 방법입니다. 생성자 호출 시점에 딱 한 번만 호출되는 것이 보장되고, 값이 변하지 않고, 필수적으로 필요한 의존관계인 경우에 사용합니다.

위의 코드에서도 생성자로 주입받는 경우 필드 속성에 final을 붙일 수 있다는 장점이 있습니다. final을 붙이게 되면 필드자체에 초기값을 주거나 생성자로 주입해주지 않으면 오류가 발생합니다.

생성자가 딱 한개만 있으면 @Autowired애노테이션을 생략해도 자동주입이 된다고 한다. 물론 스프링 빈에만 해당한다. 클래스를 스프링 빈으로 등록하지 않으면 당연히 주입해 줄 수 없습니다.

수정자(Setter) 주입

set- 으로 시작하는 setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법입니다. 의존관계를 선택해야하고, 변경의 가능성이 있는 의존관계에 사용합니다.

주의할 점은 수정자를 사용할 때 @Autowired의 기본동작은 주입할 대상이 없으면 오류를 발생시키는 것입니다. 하지만 주입할 대상이 없어도 코드를 실행해야 하는 경우가 있고, 그렇게 하기 위해서는 @Autowired의 required 옵션을 false로 설정하면 됩니다. ex ) @Autowired(required = false)

필드주입

필드에 직접 접근해서 의존관계를 주입해주는 방법이다. 코드가 매우 간결해지지만 private 필드 같은 경우는 외부에서 변경이 불가능해서 테스트하기 힘들다는 치명적인 단점이 있는 방법이다. DI 프레임워크가 없으면 아무것도 할 수 없으므로 사용하지 말자. 애플리케이션의 실제코드와 관계없는 테스트 코드나 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용할 수 있다.

일반 메서드 주입

일반 메서드를 사용해서 주입받는 방법이고, 한번에 여러 필드를 주입받을 수 있다는 특징이 있다. 하지만 일반적으로 잘 사용되지 않는 방법이다. init이라는 일반 메서드를 정의해서 의존관계를 주입하는 모습이다.

당연한 이야기이지만 @Autowired를 이용한 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 스프링 빈이 아닌 클래스에서 @Autowired를 적용한다 한들 아무런 기능도 동작하지 않는다.

옵션처리

수정자를 언급할 때도 말했지만 스프링 빈이 없어도 제대로 동작해야 할 때가 있다. 하지만 @Autowired를 사용하면 required 옵션의 기본값이 true로 되어 있어서 자동주입 대상이 없으면 오류가 발생한다. 예를 들어서,

위 Setter에서 만약 스프링 빈에 MemberRepository 타입 빈이 없을 경우 오류가 발생한다는 의미이다.

자동 주입 대상을 옵션으로 처리하는데에는 다음 세 가지 방법이 있다.

  • @Autowired(required = false): 자동 주입할 대상이 없으면 수정자 메서드가 호출되지 않도록 한다.
  • org.springframework.lang.@Nullable: 자동주입할 대상이 없으면 null이 입력된다.
  • Optional<>: 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.

Member라는 클래스 객체가 스프링 빈으로 등록되지 않은 상태에서 TestBean클래스를 스프링 빈으로 등록하고자 하면 어떤 일이 벌어질까?

  • setNoBean1은 Member가 스프링 빈에 등록되어 있지 않으므로 호출자체가 되지 않을 것이다.
  • setNoBean2는 Member가 스프링 빈에 등록되어 있지 않으므로 noBean2 파라미터에 null이 들어갈 것이다.
  • setNoBean3는 Member가 스프링 빈에 등록되어 있지 않으므로 noBean3 파라미터에 Optional.empty가 들어갈 것이다.

생성자 주입을 선택해라!!

최근에는 스프링을 포함한 DI프레임워크 대부분이 생성자 주입을 권장한다고 한다. 그 이유는 다음과 같다.

  • 대부분의 의존관계가 한번 주입되면 애플리케이션이 종료되기 전까지 변경할일이 거의 없다. 오히려 대부분의 의존관계가 애플리케이션이 종료되기 전까지 불변해야 한다.
  • 수정자 주입을 사용하려 하면 setXXX메서드를 public으로 열어두어야 하고, 다른 개발자가 코드를 보았을 때 변경해도 되는 필드라고 착각할 수 있다. 따라서 변경하면 안 되는 메서드를 열어두는 것은 좋은 설계방법이 아니다.
  • 생성자 주입은 객체를 생성할 때 딱 한 번만 호출되기 때문에 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.

그리고 순수한 자바 코드를 사용하여 테스트를 진행하고, 개발자가 실수로 의존관계를 주입하는 것을 잊었다고 가정해 보자, 그러면 수정자(setter)를 사용했을 경우에는 실행 시 의존관계주입을 해주지 못했기 때문에 NPE(Null Poing Exception)이 발생하는데 반해서, 생성자 주입을 사용하면 필드에 final 키워드와 함께 사용하여 주입을 누락했을 때 컴파일 오류가 발생한다. 그래서 IDE에서 어떤 값을 추가 주입해야 하는지를 바로 알아챌 수 있다.

final 키워드

생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. final 키워드가 붙은 필드는 무조건 초기값이 주어지거나 생성자에서 값이 할당되어야 하기 때문에 혹시라도 생성자에서 값이 설정되지 않는 오류를 컴파일 시점에 막아준다는 장점이 있다.

컴파일 오류는 세상에서 가장 빠르고 좋은 오류다!

정리

  • 생성자 주입 방식을 선택하는 이유에는 여러 가지가 있지만, 프레임워크에 의존하지 않고 순수한 자바언어의 특징을 잘 살리는 방법이기도 하다.
  • 기본적으로는 생성자 주입 방식을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 도니다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
  • 항상 생성자 주입 방식을 선택하고, 가끔 옵션이 필요하면 수정자 주입을 선택하자. 필드 주입은 사용하지 않는 것이 좋다.

롬복 활용하기

하지만 결국에는 생성자를 만들어야 한다는 점이 개발자 분들을 귀찮게 만들었던 모양이다. lombok 라이브러리를 사용해서 코드를 더 적고 깔끔하게 만들 수 있다.