의존관계 자동 주입

Single Ko·2023년 4월 19일
0

Spring 강의 정리

목록 보기
5/31

의존관계 자동주입의 4가지 방법

생성자 주입으로 대부분의 사용, 필요할때 가끔 수정자 주입 방식을 사용. 여기서는 스프링을 사용하는 경우의 예제를 대부분 볼 것이다.

1. 생성자 주입(이 방법을 사용)

  • 생성자 호출시점에 딱 한번만 호출하는 것이 보장.(자바의 생성자에 대해 알면 된다.) '불변, 필수' 의존관계에 사용.
1. MessageService 인터페이스 정의 
  
public interface MessageService {}
 
2.MessageService 인터페이스를 이용한 EmailService, SMSService 구현
  
public class EmailService implements MessageService {}

public class SMSService implements MessageService {}
  
3."MessageService" 인터페이스를 사용하는 "MessageClient"라는 클래스를 만듭니다
  
  public class MessageClient {
    private MessageService messageService;

    public MessageClient(MessageService messageService) {
        this.messageService = messageService;
    }
}
  
4. 생성자 주입.

public class Main {
    public static void main(String[] args) {
  
        MessageClient emailClient = new MessageClient(new EmailService());
        
        MessageClient smsClient = new MessageClient(new SMSService());
    }
}

"MessageClient" 클래스는 "MessageService" 인터페이스에 의존하며, 해당 생성자를 통해 의존성이 제공됩니다. "MessageClient"가 인스턴스화되면 생성자 주입을 사용하여 "EmailService" 또는 "SMSService" 구현을 제공합니다.

문제점

순수한 자바 코드로 작성시, Main에서 생성자를 사용할때, new EmailService() 나 new SMSService() 등의 구현체를 알아야한다. 이는 객체지향 설계 방법을 위반한다. 구현체에 의존하면 안되고 추상화에 의존해야 하는데, 우리는 EmailService나 SMSService라는 구현체를 확실하게 알아야 사용.

Spring 프레임워크를 통한 문제점 극복

위의 예제에서 사용한 MessageService 인터페이스를 그대로 활용.

 1. 구현체인 EmailService와 SMSService에 Spring의 애노테이션 @Service를 붙여준다. 
 이렇게 되면 자동으로 Spring이 구현체를 인식해서 DI를 할 수 있다.
  
@Service
public class EmailService implements MessageService {}

2. MessageClient도 @Component 애노테이션을 붙여서 Scan의 대상임을 알려준다.
  @Autowired는 생성자가 하나라면 생략이 가능하다.

@Component
public class MessageClient {
  
    private final MessageService messageService;
	
    @Autowired
    public MessageClient(MessageService messageService) {
        this.messageService = messageService;
    }
}

Spring Framework를 사용시, 구현체(Email, SMSService)를 직접 입력할 필요가 없이 Spring이 자동으로 관리해서 의존성주입을 해준다. 이를 통해 우리는 MessageService라는 추상적인 객체만 알면 되고, 구현체는 알 필요가 없다. 또한 이를 통해 구현체만 만들어주면 Spring이 자동으로 의존성을 관리해주기 때문에 쉽게 확장을 할 수 있다.

2. 수정자 주입

  • setter를 이용하기 때문에 나중에 의존관계 주입이 일어난다. '선택,변경' 가능한 의존관계에 사용.
  • 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법.
    자바 빈 프로퍼티규약 => getter setter 이용하는것.
  • @Autowired의 기본 동작은 주입할 대상이 없다면 오류가 발생한다. (required = false)를 지정해줘야된다.
@Component
public class MessageClient {
  
 private MessageService messageService;
  
 @Autowired
 public void setMessageService(MessageService messageService) {
 	this.messageService = messageService;
 }

3. 필드 주입.

  • 필드에다 @Autowired를 사용하는 방법.(옛날에 많이 사용한 방법, 문제점이 많아 이제 사용하지 않음.)
  • 외부에서 변경이 불가능해서 테스트하기 힘들다는 점이 있다.
  • DI 프레임워크가 없으면 아무것도 할 수 없다.
  • 애플리케이션의 실제 코드와 관계 없는 테스트 코드, 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용. 테스트에서 빠르게 테스트를 위해 사용하긴함.(테스트는 테스트만을 위한코드. 밖에서 사용하지 않기때문에)
@Component
public class MessageClient {
 
 @Autowired
 private MessageService messageService;
 
 }

4. 일반 메서드 주입

-> 한번에 여러 필드에 주입 받을 수 있다. 사용하지 않는다.

옵션 처리.

@Autowired(required = false) => 자동 주입할 대상이 없으면 수정자 매서드 자체가 호출이 안됨.
@Nullabe => 자동 주입할 대상이 없으면 Null이 입력된다.
Optional<> -> 자동 주입할 대상이 없으면 Optional.empty가 입력된다.
@Nullable , Optional =>>스프링전반에걸쳐지원된다. 예를 들어서 생성자 자동 주입에서 특정 필드에만 사용해도 됨.

생성자 주입을 선택해라!

  1. "불변" -> 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안된다.
  2. 수정자 주입할 사용하면 setXxxx메서드를 public으로 열어둬야 된다. 다른 곳에서 아무나 사용 가능.
  3. 누군가 실수로 변경 할 수 도있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
  4. 생성자 주입은 객체를 생성할 때 딱 한번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.
  5. final 키워드를 붙일 수 있다. (초기화하면 값을 변경 불가능하다)
  6. 프레임워크에 의존하지 않고 순수한 자바 언어의 특징을 살리는 방법이다.
  7. 기본으로 생성자 주입을 사용하고, 필수값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. (생성자 주입과 수정자 주입을 동시에 사용 가능)
  • 프레임워크 없이 순수한 자바 코드로 테스트 하는 경우가 많다. 이때 생성자 의존관계 주입이 아니라면 테스트 코드를 짤 때 프레임 워크에 의존적이게 된다.
  • 생성자 주입을 받을때 필드에 선언을 할때 final 키워드를 사용해라! => final 필드는 무조건 값이 있어야 되는데, 만들때 값을 넣어주거나 or 생성자를 만들어서 호출될때 값이 들어가거나 해서 둘중에 한가지 방법으로 값이 무조건 들어가야된다. 값이 들어가지 않으면 오류가 나는데 컴파일 오류가 난다. 컴파일 오류는 가장 빠르고 좋은 오류다. => 실행도 되지 않고, 바로 IDE에서 말해줘서 문제를 바로 파악가능. 나머지 주입 방법들은 final키워드를 붙일 수 없다. 모두 생성자 이후에 호출 됨.

구현체가 여러개일시 문제

  1. @Autowired -> 타입으로 조회한다.
    *조회 빈이 2개 이상 일시 -문제
    NoUniqueBeanEfinitionException -오류

  2. 문제 해결 방법
    @Autowired 필드명 ->여러 빈이 있으면 필드이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
    @Qualifier => 따로 @Qualifier에 이름을 붙여 @Qualifier를 찾는 용도로 사용해서 매칭시켜 준다.
    @Primary -> 자주 사용하는 방법. 우선 순위를 지정하는 방법이다. @Primary가 붙어있다면 우선순위로 시작된다.

  • @Primary를 우선 순위로 사용하고, @Qualifier를 보조적으로 활용하는것이 좋다. 또한 둘다 있다면 무엇이 먼저 우선순위를 가질까?
    -> 항상 좁은 범위의 선택권이 넓은 범위의 선택권보다 우선순위가 높다. @Qualifier가 우선순위가 높다는 뜻.

@Qualifier의 문제점

@Qualifier("emailService") 이렇게 문자를 적으면 컴파일시 타입 체크가 안된다.(실수로 잘못된 문자를 입력가능) -> 애노테이션을 만들어서 문제를 해결 할 수 있다.

@emailService ->라는 애노테이션을 만들어서 @Qualifier를 조합해서 사용하면 된다. => 또한 이렇게 만든다면 이 애노테이션을 이용해 사용된 곳을 바로 다 조회 할 수도있다.

  • 조회한 빈이 모두 필요할때 - List, Map

  • 전략 패턴을 쉽게 구성할 수 있다 -> ex)메일을 Email과 SMS를 선택해서 적용가능.

  • 자동, 수동의 올바른 실무 기준 -> 자동 주입이 기본.
    그렇다면 언제 수동 빈 등록을 사용하나?
    업무로직 - > 웹을 지원하는 컨트롤러. 데이터 계층의 로직을 처리하는 리포지토리등이 모두 업무로직
    기술 지원 빈 -> 기술적인 문제나 공통의 관심사(AOP)를 처리할 때 주로 사용.
  • 기술 지원빈은 가급적 수동 빈을 사용해 명확하게 들어내는 것이 좋다. -> 광범위하게 애플리케이션 전반적으로 영향을 미치는데, 그 수는 적음. 내가 직접 기술 지원 객체를 등록해야 될때. (예외 - 스프링부트가 자동으로 등록해주는 기술 지원은 자동 등록임.)

참고 : 본 글은 김영한님의 스프링 강의를 정리한 것이다.

profile
공부 정리 블로그

0개의 댓글