김찬진의 개발 블로그

23/02/13 [DI(Dependency Injection)] 본문

1일1배움/Spring (김영한 님)

23/02/13 [DI(Dependency Injection)]

kim chan jin 2023. 2. 13. 01:49
정리한 내용

 

DI의 목적은 다양하다.

  1. 중복된(심지어 다를 수 있는) 객체 생성을 피하기 위함
  2. Spring Container로부터 객체를 가져다 쓰기 위함

 

DI의 방식은 3가지이다.

  1. 필드 주입 (△) - 다형성 없음
  2. setter 주입(△) - setter가 public하게 노출되므로 위험
  3. 생성자 주입(O) - 처음 Spring이 뜰 때 Spring Bean이 조립되고 이후엔 변경 못함

 

1. 중복된(심지어 다를 수 있는) 객체 생성을 피하기 위함

MemberService클래스와 MemberServiceTest클래스의 멤버변수인 MemoryMemberRepository 객체가 다른 객체일 가능성이 있습니다. 각각의 두 클래스에서 굳이 동일한(총 2개) MemoryMemberRepository객체를 만들 필요는 없습니다. 왜냐하면 두 객체가 다른 객체일 가능성도 있고 목적상 다른 객체이어야 할 이유가 전혀 없기 때문입니다. 이러한 문제를 개선하기 위해 DI가 필요합니다.

 

DI를 하는 방법은 다음과 같습니다.

 

MemberService클래스에서 멤버변수에 MemoryMemberRepository 객체를 생성하지 않고 참조변수만을 선언합니다. 생성자를 만들어 MemoryMemberRepository 객체를 주입받고 그 객체를 멤버변수 MemoryMemberRepository로서 사용하도록 합니다.

 

MemberServiceTest클래스에서 MemberService 생성자를 사용하여 MemberService 객체를 생성하는데 이 때, 생성자의 인자에 MemoryMemberRepository 객체를 생성해 주입합니다. MemberService클래스에서 생성자를 만들었고 생성자의 인자로 MemoryMemberRepository 객체를 주입받고 그 객체를 멤버변수로 사용하기로 했다는 것을 잊지말아야 합니다. 이것이 핵심입니다.

 

물론 14분대 강의영상에서는 MemberServiceTest클래스에서 MemberService의 각 메서드들이 테스트되기 전 매번 MemberService 객체가 만들어져야 하므로 beforeEach() 메서드 안에서 구현이 되었을 뿐, DI의 원리는 지금까지 설명한 바와 동일합니다.

 

 

2. Spring Container로부터 객체를 가져다 쓰기 위함

(1) 컴포넌트스캔과 자동 의존관계 설정

@Service, @Repository, @Controller 어노테이션을 사용해서 Spring Container에 Member객체(Spring Bean) 등록

@Service
public class MemberService {...}
@Repository
public class MemoryMemberRepository implements MemberRepository {...}

MemberController는 MemberService객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberService객체를 생성하지 않고 참조변수만을 선언하고 MemberController생성자에 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberService객체를 주입(객체 생성하지 않음!). 객체를 생성하지 않고 참조변수만을 선언하는 이유는 앞서 말했듯 중복된 객체를 생성하지 않기 위해서임.

@Controller
public class MemberController {
 private final MemberService memberService;
 
 @Autowired
 public MemberController(MemberService memberService) {
  this.memberService = memberService;
 }
}

MemberService는 MemoryMemberRepository객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberRepository(다형성을 위한 구현체 참조타입이 아닌 인터페이스 참조타입) 객체를 생성하지 않고 참조변수만을 선언하고 MemberService생성자에 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberRepository객체를 주입(객체 생성하지 않음!)

@Service
public class MemberService {
 private final MemberRepository memberRepository;
 
 @Autowired
 public MemberService(MemberRepository memberRepository) {
  this.memberRepository = memberRepository;
 }
}

(2) 자바 코드로 직접 스프링빈 등록하기

@Repository, @Service 어노테이션을 사용하지 않고 @Controller 어노테이션만 사용하여 Spring Container에 객체(Spring Bean) 등록

MemberController 클래스에서는 MemberService객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberService객체를 생성하지 않고 참조변수만을 선언하고 MemberController생성자 @Autowired 어노테이션 사용하고 생성자의 인자에 MemberService객체를 주입(객체 생성하지 않음!). 객체를 생성하지 않고 참조변수만을 선언하는 이유는 앞서 말했듯 중복된 객체를 생성하지 않기 위해서임.

@Controller
public class MemberController {
 private final MemberService memberService;
 
 @Autowired
 public MemberController(MemberService memberService) {
  this.memberService = memberService;
 }
}

Config 클래스에서는 @Configuration 어노테이션을 사용하여 멤버(필드, memberService(), memberRepository())들을 Spring Container에 객체(Spring Bean)로 등록시켜야 함을 인식함.

meberService 메서드(필드) @Bean 어노테이션을 사용하여 자신이 Spring Container에 객체(Spring Bean)로 등록되어야 함을 알려줌. 반환은 MemberService 생성자로 MemberService객체를 만들어 반환함.

meberRepository 메서드(필드) @Bean 어노테이션을 사용하여 자신이 Spring Container에 객체(Spring Bean)로 등록되어야 함을 알려줌. MemoryMemberRepository생성자로 MemoryMemberRepository객체를 만들어 반환함.

이 때 MemberService는 MemberRepository를 의존하므로 memberService메서드가 반환하는 MemberService 생성자에 memoryRepository메서드를 주입함

@Configuration
public class SpringConfig {

 @Bean
 public MemberService memberService() {
  return new MemberService(memberRepository());
 }
 
 @Bean
 public MemberRepository memberRepository() {
  return new MemoryMemberRepository();
 }
}

 

MemberService 클래스에서는 MemoryMemberRepository객체(Spring Bean)를 의존하기(연결해서 가져오기) 위해 멤버변수에 MemberRepository(다형성을 위한 구현체 참조타입이 아닌 인터페이스 참조타입) 객체를 생성하지 않고 참조변수만을 선언하고 MemberService생성자 @Autowired 어노테이션 사용하지 않고 생성자의 인자에 MemberRepository객체를 주입(객체 생성하지 않음!)

public class MemberService {
 private final MemberRepository memberRepository;
 
 public MemberService(MemberRepository memberRepository) {
  this.memberRepository = memberRepository;
 }
}

 

 

Comments