Spring
스프링 DI(Dependency Injection)란?
스프링을 사용하다보면 @Configuration, @Bean, @Autowired, @Qualifier 등을 많이 볼 수 있다.
각각 어떤 역할을 하는지, DI는 무엇이고 왜 DI를사용해야 하는 것인지를 알아보자.
스프링 DI란?
DI란 'Dependency Injection'의 약자로 '의존 주입'이라고 한다. 여기서 의존이란, 한 클래스가 다른 클래스의 메서드를 실행 하는 것을 말한다. 만약 MemberRegisterService에서 MemberDao를 사용하여 DB에 접근한다면, 이 때 MemberRegisterService는 MemberDao 클래스에 의존한다고 표현한다.
일반적으로 우리가 객체를 사용할 때에는, 아래와 같이 객체를 얻어왔다.
public class MemberRegisterService {
private MemberDao memberDao = new MemberDao();
..
}
위와 같이 클래스 내부에서 직접 의존 객체를 생성하는 것은 사용하기 쉽지만, 유지보수 관점에서 문제점을 유발할 수 있다. DI를 사용할 경우에는 의존 객체를 변경하는데 유연하기 때문에 유지보수하는데 직접 객체를 생성하는 것보다 수월하다는 장점이 있다.
예를 들어보자. 기존에 MemberDao의 데이터에 접근하는데, 보다 빠른 회원 데이터 조회를 위해 캐시를 적용해야 하는 상황이 발생했을 때, 우리는 기존에 있던 MemberDaO를 CachedMemberDao로 변경해야 하는 상황이다. CachedMemberDao는 MemberDaO와 거의 비슷하지만 일부 메서드가 달라지기 때문에 상속을 받아 구현했다고 가정한다. 이런 경우 기존에 여러 Service에서 MemberDaO를 직접 생성하던 모든 코드를 변경해야 한다는 번거로움이 있다. 이때 만약 의존 객체를 주입받도록 구현했다면 MemberDao 객체를 생성하는 코드에서만 수정하면 되기 때문에 코드를 유지보수하기 쉽다.
public class MemberRegisterService {
private MemberDao memberDao;
public MemberregisterService(MemberDao memberDao){
this.memberDao = memberDao;
}
...
}
public class ChangePasswordService {
private MemberDao memberDao;
public ChangePasswordService(MemberDao memberDao){
this.memberDao = memberDao;
}
...
}
MemberDao memberDao = new MemberDao();
MemberRegisterService regSvc = new MemberRegisterService(memberDao);
ChangePasswordService pwdSvc = new ChangePasswordService(memberDao);
객체 조립기
앞에서 설명할 때, 서비스에서 직접 객체를 생성하는 방식이 아니라, 서비스를 부를 때 객체를 주입 받는 식으로 사용한다고 설명했다. 그렇다면 객체를 생성하고 주입해주는 코드는 어디에 위치할까?
public class Main {
public static void main(String[] args) {
MemberDao memberDao = new MemberDao();
MemberRegisterService regSvc = new MemberRegisterService(memberDao);
ChangePasswordService pwdSvc = new ChangePasswordService();
pwdSvc.setMemberDao(memberDao);
... // regSvc와 pwdSvc를 사용하는 코드
}
}
위와 같이, 의존 객체를 주입한다는 것은 서로 다른 두 객체를 조립한다고 생각할 수 있다. 그래서 이렇게 각 객체들을 생성하고 주입해주는 클래스를 객체 조립기라고 부른다.
그리고 바로 스프링이 이렇게 각 객체들을 생성해주고, 주입해주는 DI의 역할, 즉 객체 조립기와 유사한 역할을 한다.
DI 방식
DI의 방식에는 총 두가지 방식이 있다.
1. 첫 번째는 생성자 방식이다.
public class MemberRegisterService {
private MemberDao memberDao;
// 생성자를 통해 의존 객체를 주입 받음.
public MemberRegisterService(MemberDao memberDao) {
// 주입 받은 객체를 필드에 할당
this.memberDao = memberDao;
}
public Long regist(RegisterRequest req) {
// 주입 받은 의존 객체의 메서드를 사용
Member member = memberDao.selectByEmail(req.getEmail());
memberDao.insert(newMember);
return newMember.getId();
}
}
@Bean
public MemberDao memberDao() {
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegSvc() {
return new MemberRegisterService(memberDao());
}
위와 같이 생성자를 통해 의존 객체를 주입받고, 주입 받은 의존 객체의 메서드를 사용하는 방식이 있다.
2. 두 번째는 세터 메서드 방식이다.
public class MemberInfoPrinter {
private MemberDao memDao;
private MemberPrinter printer;
public void printMemberInfo(String email) {
Member member = memDao.selectByEmail(email);
if (member == null) {
System.out.println("데이터 없음\n");
return;
}
printer.print(member);
System.out.println();
}
public void setMemberDao(MemberDao memberDao) {
this.memDao = memberDao;
}
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
@Configuration
public class AppCtx {
... 생략
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setMemberDao(memberDao());
infoPrinter.setPrinter(memberPrinter());
return infoPrinter;
}
}
세터 메서드를 활용하여 의존을 주입하는 방식은 위와 같다. setMemberDao와 setPrinter와 같은 세터 메서드를 생성해두고, 다른 클래스에서 해당 메서드를 사용해야 할 일이 있으면 infoPrinter()에서 한 것과 같이 사용하여 주입한다.
그렇다면 우리는 의존을 주입할 때마다 항상 생성자를 만들거나, 세터 메서드를 만들어서 사용해야 할까?
스프링은 이런 서로 의존성이 있는 객체를 @Bean 어노테이션을 통해 자동으로 관리해준다.
관련글
[스프링 DI] 1. 스프링 DI(Dependency Injection)란? (링크)
[스프링 DI] 2. Spring DI 설정 (링크)
[스프링 DI] 3. Spring에서 두 개 이상의 설정 파일 이용하기 (링크)
[스프링 DI] 4. getBean() 메서드 사용과 Bean 관련 Exception 정리 (링크)
'개발 > Spring' 카테고리의 다른 글
[Spring DI] 4. getBean() 메서드 사용과 Bean 관련 Exception 정리 (0) | 2020.11.08 |
---|---|
[Spring DI] 3. Spring에서 두 개 이상의 설정 파일 사용하기 (0) | 2020.11.08 |
[Spring DI] 2. Spring DI 설정 (0) | 2020.11.08 |
[Spring 프로젝트] 예외 처리 가이드 (0) | 2020.11.06 |
[Spring 프로젝트] 개발 가이드 (0) | 2020.11.06 |