의존성 주입이다.
이는 필요한 객체를 직접 생성하는 것이 아닌 외부로부터 객체를 받아서 사용하는 것이다.
이를 통해 객체간의 결합도를 줄이고 코드의 재사용성을 높일 수 있습니다.
@Service
public class UserServiceImpl implements UserService{
private UserRepository userRepository;
private MemberService memberService;
@Autowired // 생략 가능
public UserServiceImpl(UserRepository userRepository, MemberService memberService){
this.userRepository = userRepository;
this.memberService = memberService;
}
}
Spring에서 권장하는 의존성 주입 방법이다.
이유는 1. 순환 참조 방지 2. 불변성 가짐 3. 테스트에 용이 하기 때문이다.
컴파일 시점에서 순환 참조를 알 수 있다(다른 DI주입 방식에선 알 수 없음)
생성자 주입은 호출 시점에 1회 보장(불변)
final을 사용할 수 있기 때문에 주입을 강제할 수 있고, 리플랙션 같은 공격으로 재할당도 불가능하다
순수한 자바 코드로 단위 테스트를 작성하기 가장 좋다
@Service
public class UserServiceImpl implements UserService{
private UserRepository userRepository;
private MemberService memberService;
@Autowired
public setUserRepository(UserRepository userRepository){
this.userRepository = userRepository;
}
@Autowired
public setMemberService(MemberService memberService){
this.memberService = memberService;
}
}
@Autowired로 주입할 대상 없는 경우(빈에 존재하지 않는 경우) 오류 발생@Autowired(required = falase) 설정 가능@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
@Autowired
private MemberService memberService;
}
public class Bus {
private String name;
public Bus(String name){
this.name = name;
}
public String getName() {
return name;
}
}
이동수단 중 버스인 Bus 클래스이다. 이름 속성을 가진다.
public class Person {
public String move(String moveType){
// 대중교통 종류
Bus bus = new Bus("버스");
// 이동 종류
return bus.getName() + "로 " + moveType;
}
}
이동하는 사람인 Person 클래스다. 목적지를 가기 위해 이동 방법을 고르는 것이다.
class PersonTest {
@DisplayName("버스로 이동하기.")
@Test
void test(){
// given
Person person = new Person();
String moveType = "타고 가다.";
// when
String move = person.move(moveType);
// then
assertThat(move).isEqualTo("버스 타고 가다.");
}
}
이동 방법으로 생각하면 될 거 같다. 약속 장소를 가는 사람이 버스를 타고 이동했다. 이동 방법에 맞게 잘 가는 지 테스트 하는 코드이다.
public class Walk {
private String name;
public Walk(String name){
this.name = name;
}
public String getName() {
return name;
}
}
하지만 다른 이동 방법이 더 좋으면 매번 클래스를 생성해 같은 코드를 작성해야 될까?? 사람이란 클래스가 아니라 이동 수단 클래스에서 원하는 이동 방법을 가져와 사용하게 된다면 보다 유연해진다. 의존 객체를 외부에서 주입(DI)해보자!
public class MoveWay {
private String name;
public MoveWay(String name){
this.name = name;
}
public String getName() {
return name;
}
}
버스, 걷기 등 이동 방법을 관리하는 부모 클래스이다.
이동 방법들은 MoveWay 클래스를 상속받아 사용한다.
public class Bus extends MoveWay{
public Bus(String name) {
super(name);
}
}
public class Walk extends MoveWay{
public Walk(String name) {
super(name);
}
}
public class Transportation {
public MoveWay get(String moveType) {
switch (moveType) {
case "타고 가다":
return new Bus("버스");
case "걸어 가다":
return new Walk("두 발로");
default:
return null;
}
}
}
이동 방법을 이동 수단으로 묶어 원하는 방법에 따라 이동 방법을 정한다.
이런식으로 DI를 주입해 역할을 분리하고 각 객체 간의 의존성을 줄인다.
public class Person {
// 이동 수단
private Transportation transportation;
// 의존성 주입(DI)
@Autowired
public Person(Transportation transportation){
this.transportation = transportation;
}
public String move(String moveType){
// 이동 방법
MoveWay moveWay = transportation.get(moveType);
// 이동
return moveWay.getName()+ " " + moveType;
}
}
사람은 이동 방법을 생성하지 않고, 이동 수단에서 방법에 맞게 원하는 방법을 선택하게 했다.
역할을 분담해줘서 사람은 더욱 쉽게 방법을 택할 수 있게 되었다.
class PersonTest {
@DisplayName("버스로 이동하기.")
@Test
void busMove(){
// given
Transportation transportation = new Transportation();
Person person = new Person(transportation);
String moveType = "타고 가다";
// when
String move = person.move(moveType);
// then
assertThat(move).isEqualTo("버스 타고 가다");
}
@DisplayName("두 발로 이동하기")
@Test
void walkMove(){
// given
Transportation transportation = new Transportation();
Person person = new Person(transportation);
String moveType = "걸어 가다";
// when
String move = person.move(moveType);
// then
assertThat(move).isEqualTo("두 발로 걸어 가다");
}
}
의존성 주입(DI)을 통해 객체 간의 결합을 낮춰 더욱 유연하고 객체지향적은 코드를 만들어줄 수 있다.
이동 방법을 코드 변경 없이 만들어질 수 있게 코드가 개선되었다.
의존성 주입에 대한 설명이 정말 상세하고 이해하기 쉽네요. 특히, 코드 예시를 통해 실제로 어떻게 적용될 수 있는지 보여주신 부분이 좋았습니다. 이런 글을 공유해주셔서 감사합니다!