티스토리 뷰

Java/spring

Spring DI

LichKing 2018. 1. 21. 14:50

음..아마 2~3년전에 DI에 관한 포스팅을 하나 했던걸로 기억하는데 그때는 DI가 무엇인지에 대해 적었다면 이번엔 Spring이 어떤방식으로 DI를 해주는지 적어보려한다. 이 글을 적게된 계기는 가장 마지막에 소개할 생성자 주입 방식을 요근래에 알게됐기때문이다.


1. Field Injection

가장 간단하고 코드량이 적고, 그때문에 가장 많은 분들이 이용하는 방식이 아닐까싶다. 3개 회사를 다녀봤지만 전부 필드 주입방식을 사용하고있었다.


@Service
class PersonService{
@Autowired
private PersonRepository personRepository;
}


이 방식은 가장 간단하고 편리하지만 스프링에 종속적이게된다는 단점이 있다. 뭐 사실 코드의 변경없이 기반 프레임워크가 변경될 일은 거의 없기때문에 스프링에 종속적이면 어때? 라고 생각할 수도 있지만 이 방식은 그런 거대한 이슈가 아니라도 가끔 불편함을 유발한다. 특히 테스트할때인데 단위 테스트를 하기위해 저 코드에서 PresonRepository를 Mock 객체로 끼워넣기위해서는 리플렉션을 쓰는수밖에 없다. 생성자도, 수정자(setter)도 없기때문이다. MockitoRunner 같은걸 이용하면 이 문제를 해결할 수 있기는한데 테스트하는 방식이 복잡해서 Runner를 여러개쓰는경우 SpringRunner와 MockitoRunner 를 같이 사용할 수가 없어서 곤란했던 적이 있다. 어디까지나 이는 실제 내 경험을 바탕으로 느낀 불편함이기때문에 이 외에도 몇가지 문제가 더 있을수있으나 근본적인 문제는 'POJO를 지향하는 스프링인데 필드주입때문에 스프링에 종속적이게 된다.' 라는 문제다.


2. Constructor Injection

생성자를 이용한 주입이다. @Autowired 애노테이션은 생성자에도 달릴 수 있기때문에 생성자만 만들어주면 크게 작업해줄일은 없다. 객체 생성시 주입객체를 받기때문에 생성한 이후에는 주입된 객체를 수정할 수 없다.


@Service
class PersonService{
private PersonRepository personRepository;

@Autowired
public PersonService(PersonRepository personRepository){
this.personRepository = personRepository;
}
}


필드주입에 비해 단점은 생성자 코드가 늘어난다는것? 개인적으로 내가 좋아하는 방식이다. 생성자가 명시적으로 존재하기때문에 Mock 객체를 주입하기에도 편하다.


3. Setter Injection

setter 를 이용한 주입이다. 위 두가지 방식에 비해 가장 유연하다. 객체가 생성된 이후에도 주입된 객체들을 바꿔줄 수 있다. 하지만 내부 객체가 프로덕션 코드에서도 변경될 수 있기때문에 위험하기도 하다.


@Service
class PersonService{
private PersonRepository personRepository;

@Autowired
public void setPersonRepository(PersonRepository personRepository){
this.personRepository = personRepository;
}
}


4. Constructor Injection - No Annotation

추가된지 1년도 더 된 기능인데 얼마전에 알고 놀랬던 기능이다.( https://spring.io/blog/2016/03/04/core-container-refinements-in-spring-framework-4-3 )


@Service
class PersonService{
private PersonRepository personRepository;

public PersonService(PersonRepository personRepository){
this.personRepository = personRepository;
}
}


이게 왜 놀랄정도냐면... 필드주입, 생성자주입, 세터주입 모두 일단 기본적으로 주입 애노테이션을 달아줘야한다. 이 때문에 롬복(Lombok)을 사용하면서도 롬복이 만들어주는 생성자는 사용할 수가 없는 문제가 있었다. 롬복이 뭔지 모른다면 여기를 참고해보자. 하지만 생성자만 만들어주면 주입 애노테이션이 없어도 주입을 해주는 기능이 추가되어 이제는 롬복을 이용해서도 의존성 주입을 사용할 수 있게 된것이다.


@AllArgConstructor
@Service
class PersonService{
private PersonRepository personRepository;
}


필드주입이 편하다고 필드주입을 사용해왔다면 이제 애노테이션을 클래스에 하나만 달아주면되니 오히려 더 편해진 셈이기도하다. 이거때문에 이 기능을 추가했나 싶기도하고... 한번 사용해보면서 이 편리해진 점때문에 생기는 부작용은 어떤게 있을지 한번 경험해봐야할것 같다.

댓글
댓글쓰기 폼