본문 바로가기

SERVER/Spring

[Spring] 스프링에서 의존성을 주입하는 방법

  • 스프링에서는 내부에서 new를 통해 의존성 객체를 직접 생성해서 쓰는 방법을 이용하지 않고, 외부에서 의존성을 주입해주는 방법을 통해 모듈 간 결합도를 낮추고 유연성을 높일 수 있다는 얘기를 했었다.
  • 스프링에서는 Bean으로 등록되면 IoC 컨테이너가 알아서 의존성 주입을 해준다!
  • Bean은? 스프링 컨테이너가 생성 및 관리하는 객체를 의미한다. IoC 컨테이너에 등록된 Bean들은 스프링이 의존성 주입 및 관리를 해주게 된다. Bean은 default 싱글톤 패턴으로 관리된다.(싱글톤 패턴이란 객체가 여러 차례 호출되어도, 한번만 생성한 후 참조하도록 하는 것이다)
  • 스프링 IoC 컨테이너는? Bean의 생성 및 의존성을 관리한다.
  • 스프링 컨테이너(IoC 컨테이너)의 종류는? 1) BeanFactory 2) ApplicationContext 로 나뉘어진다. BeanFactory는 Bean 객체를 생성 및 관리해주는 최상위 인터페이스로, Bean 자체가 필요해지기 전까지는 인스턴스화를 하지 않는다고 한다. 따라서 getBean() 메소드를 통해 요청이 오면 객체를 생성한다. ApplicationContext는 BeanFactory를 상속받는 서브인터페이스로, BeanFactory의 모든 기능과 그 외 추가 기능을 제공해주기 때문에 일반적으로 더 추천된다. BeanFactory와의 차이점은 컨텍스트 초기화 시점에 모든 싱글톤 Bean을 로드하기 때문에 구동 이후 지연없이 즉시 Bean을 얻을 수 있다. 

스프링에서는 xml 파일, java config를 통해 Bean을 등록하는 방법 등이 있었으나, 스프링부트에서는@SpringBootApplicatoin에 @ComponentScan 설정이 들어가있다! 따라서 해당 클래스의 하위 디렉토리에 대해서 애노테이션 스캔을 통해 Bean 등록을 할수 있다.

서론이 길었는데,,, 본론으로 들어가서, 스프링에서는 IoC 컨테이너에 Bean으로 등록된 것들을 @Autowired 애노테이션을 통해 의존성을 주입할 수 있다. @Autowired는 생성자, setter, 필드에 붙여 사용할 수 있는데, 스프링에서 의존성을 주입하는 3가지 방법에 대해 자세히 알아보자~(는게 오늘의 본론이었음...)


1. 생성자 주입

먼저 생성자 주입은 가장 추천되는 방식이라고 한다. 그래서 생성자가 하나면 @Autowired를 붙이지 않아도 알아서 주입을 해준다고...(물론 빈으로 등록되어 있는 경우에만) 

 

@RestController
public class PostController {
    
    private final PostService PostService;

    @Autowired
    public PostController(PostService PostService) {
        this.PostService = PostService;
    }
}

 

@RestController와 @Service 애노테이션이 붙어 있는 클래스는 IoC 컨테이너에 Bean으로 등록될 것이다. @Autowired를 통해 생성자 주입을 하고 있으므로 PostService 객체를 따로 생성하지 않고, 외부에서 의존성이 주입되는 것이다.

 

생성자 주입이 추천되는 이유는, 필수적으로 사용해야 하는 의존성 없이는 객체를 만들지 못하도록 강제하여 의존성 주입이 누락하여 발생하는 오류를 사전에 막아줄 수 있기 때문이다(컴파일 에러 발생시킴). 또한 대부분의 의존성 주입은 한번 일어나면 어플리케이션 종료까지 의존관계를 변경할 일이 없다. 따라서 생성자 주입을 이용하면 생성자 호출시점에 딱 1번만 호출되고 이후에 호출될 일이 없으므로 불변이 보장되고, 이는 final 키워드를 이용해서 더 안전하게 보장 가능하다. 

또한 순환 참조되는 설계 역시 사전에 막을 수 있다.

 

2. Setter(세터) 주입

 

@RestController
public class PostController {
    
    private PostService PostService;

    @Autowired
    public void setPostController(PostService PostService) {
        this.PostService = PostService;
    }
}

 

Setter 주입은 생성자 주입과는 다르게 의존성을 계속 변경할 수 있다. 따라서 final 키워드를 사용할 수 없고, 앞서 얘기했듯이 대부분의 의존관계는 한 번 주입이 일어나면 변경할 일이 없는데 setter는 언제든지 변경되게 할 위험이 있는 것이다. (setter method를 public으로 열어둬야 하기 때문에 언제 어디서든 누군가에 의해서 변경될 위험이 있다!) 

 

3. Field(필드) 주입

 

@RestController
public class PostController {
    
    @Autowired
    private PostService PostService;

}

 

사실상 가장 간단하고, 사용하기 편해보이는데, 인텔리제이에서 필드 주입을 하면 권장하지 않는 방법이라고 문구가 뜬다고 한다...! 

대표적인 단점으로는,

1) 반드시 DI 프레임워크가 존재해야 한다. 

생성자 주입은 외부에서 주입 가능하므로 우리가 원하는 의존관계를 주입하여 테스트하는 것이 가능한데, 필드 주입의 경우 DI 프레임워크에 의존하지 않고 직접 의존성을 넣어주는 것이 불가능하다. 따라서 순수 자바 코드로 테스트 작성이 불가능하다는 치명적인 단점이 존재한다.

2) 외부에서 변경이 불가능하다. -> 테스트 하기 힘들다.

 


결론은 생성자 주입을 사용하는 것이 가장 권장되는 방법이라는 것!

'SERVER > Spring' 카테고리의 다른 글

[Spring] Component와 @ComponentScan  (0) 2021.09.14
[Spring] Bean Scope의 종류  (0) 2021.09.12
[Spring] Bean Life Cycle(생명주기)  (0) 2021.09.11
[Spring] IoC와 DI  (0) 2021.09.09
[WEB] DAO, DTO 개념 및 실습  (0) 2021.08.10