Spring

[Spring] 컴포넌트 스캔과 의존관계 자동 주입(생성자 주입을 해야하는 이유)

날아 2023. 1. 29. 15:54

1. @ComponentScan

스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.

  1. @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
  2. 이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
    • ex) MemberServic.class -> memberService
  3. 빈 이름을 지정하고 싶으면 @Componnent("member")이런 식으로 이름을 부여하면 된다. 

 

컴포넌트 스캔 기본 대상 

컴포넌트 스캔은 @Component 뿐만 아니라 다음의 내용도 추가로 대상에 포함한다.

컴포넌트 스캔의 용도 뿐만 아니라 다음 애노테이션이 잇으면 스프링은 부가 기능을 수행한다. 

  • @Component : 컴포넌트 스캔에서 사용
  • @Controller : 스프링 MVC 컨트롤러에서 사용. 스프링 MVC 컨트롤러로 인식
  • @Service : 스프링 비즈니스 로직에서 사용
  • @Repository : 스프링 데이터 접근 계층에서 사용. 스프링 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
  • @Configuration : 스프링 설정 정보에서 사용. 스프링 빈이 싱글톤을 유지하도록 추가처리를 한다. 

2. 다양한 의존관계 주입 방법 

  1. 생성자 주입
  2. 수정자 주입(setter 주입)
  3. 필드 주입
  4. 일반 메서드 주입 

 

생성자 주입 
  • 생성자를 통해서 의존관계를 주입받는 방법이다.
  • 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다.
  • 불변, 필수 의존관계에 사용 
@Component
public class MemberServiceImpl implements memberServie {
	
    private final MemberRepository memberRepository;
    
    @Autowired //생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입 된다.
    public MemberServiceImpl(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
 }
  • @RequiredArgsConstructor (롬복 애너테이션) 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다. 
@Component
@RequiredArgsConstructor
public class MemberServiceImpl implements memberServie {
	
    private final MemberRepository memberRepository;
    
 }

 

수정자 주입(setter 주입)
  • setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.
  • 선택, 변경 가능성이 있는 의존관계에 사용한다.
  • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다. 
    • 자바빈 프로퍼티 규약 : 필드의 값을 직접 변경하지 않고, set..., get...라는 메서드를 통해 값을 읽거나 수정하는 자바내의 규칙
@Component
public class MemberServiceImpl implements memberServie {
	
    private MemberRepository memberRepository;
    
    @Autowired 
    public void setMemberServiceImpl(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
 }

 

필드 주입
  • 필드에 바로 주입하는 방식
  • 코드가 간결하지만 외부에서 변경이 불가하여 테스트하기 힘들다는 단점이 있다.
  • DI 프레임워크가 없으면 아무것도 할 수가 없다. 
@Component
public class MemberServiceImpl implements memberServie {
	
    @Autowired 
    private MemberRepository memberRepository;
    
 }

 

일반 메서드 주입
  • 일반 메서드를 통해서 주입받을 수 있다.
  • 한번에 여러 필드를 주입 받을 수 있다.
  • 일반적으로 잘 사용하지 않는다. 
@Component
public class MemberServiceImpl implements memberServie {
	
    private MemberRepository memberRepository;
    
    @Autowired 
    public void init(MemberRepository memberRepository){
    	this.memberRepository = memberRepository;
    }
 }

3. 생성자 주입을 해야하는 이유  

  • 객체의 불변성 확보 
    • 실제 개발시 의존 관계의 변경이 필요한 상황은 거의 없다. 하지만 수정자 주입이나 일반 메소드 주입을 이용하면 불필요하게 수정의 가능성을 열어두어 유지보수성을 떨어트린다. 따라서 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋다.
    • 생성자 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다.
  • 테스트 코드 작성
    • 테스트가 특정 프레임워크에 의존하는 것은 침투적이므로 좋지 않다. 그러므로 가능한 순수 자바로 테스트를 작성하는 것이 가장 좋다. 생성자 주입이 아닌 다른 주입으로 작성된 코드는 순수한 자바 코드로 단위 테스트를 작성하는 것이 어렵다.
    • 생성자 주입을 사용하면 컴파일 시점에 객체를 주입받아 테스트 코드를 작성할 수 있으며, 주입하는 객체가 누락된 경우 컴파일 시점에 오류를 발견할 수 있다.
  • Final 키워드 작성 및 Lombok 과의 결합 
    • 생성자 주입을 사용하면 필드 객체에 final 키워드를 사용할 수 있으며, 컴파일 시점에 누락된 의존성을 확인할 수 있다. final 키워드는 롬복과 결합되어 코드를 간결하게 작성할 수 있다. (@RequiredArgsConstructor)
    • 가능한 이유: 스프링에서는 생성자가 1개인 경우 @Autowired를 생략할 수 있도록 도와주기 때문이며, 해당 생성자를 롬복으로 구현하였기 때문
  • 스프링에 비침투적인 코드 작성
    • 필드 주입시에는 @Autowired를 이용해야 하는데, 이것은 스프링이 제공하는 어노테이션이다. 그러므로 스프링 의존성이 침투하게 된다.
    • 우리가 사용하는 프레임워크가 언제 바뀔지 모르기 때문에 스프링 의존성이 import되는 것은 바람직하지 못하다. (실제 프레임워크가 자주 바뀌지 않을 뿐더러 스프링 코드가 침투하는게 치명적 문제는 아니지만, 그래도 생성자 주입이라는 더 깔끔한 방법이 있으니 이를 이용하는 것이 좋다.)
  • 순환 참조 에러 방지
    • 생성자 주입을 사용하면 애플리케이션 구동 시점(객체의 생성 시점)에 순환 참조 에러를 예방할 수 있다.
    • @Autowired는 모든 객체의 생성이 완료된 후에 의존관계 주입이 처리되므로 호출이 되고나서야 순환 이슈를 확인할 수 있는 반면, 생성자 주입은 객체의 생성과 의존관계 주입을 동시에 실행하여 에러를 사전에 잡을 수 있다. 
    • 기본적으로 스프링부트 2.6부터는 순환 참조가 허용되지 않는다... 그래도 알아두면 좋을 거 같다. 

 

 

출저

인프런 강의 스프링 핵심 원리 - 기본편 (김영한 강사님)

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8