DI란?
'Dependency Injection', 의존 주입이다.
여기서 말하는 '의존'이란 객체 간의 의존을 뜻한다.
public class ChatServiceImpl implements ChatService{
@Override
public List<ChatDTO> selectAllChat() throws RuntimeException{
List<ChatDTO> chatList = chatDAO.selectAll( userSession.getChannel_id());
...
}
}
실제 프로젝트에서 작성한 코드이다.
ChatServiceImpl 클래스의 selectAllChat 메소드에서chatDAO 객체의 selectAll()메소드를 사용하고 있다.
이렇게 한 클래스가 다른 클래스의 메소드를 실행할 때 '의존'한다고 표현할 수 있다.
즉, ChatServiceImpl 클래스가 ChatDAO 클래스에 의존한다.
여기서 알 수 있는 것은 의존은 변경에 의해 영향을 받는 관계를 의미한다.
만약에 ChatDAO의 selectAll() 메소드를 selectAllByUserId로 변경한다면 ChatServiceImpl 클래스의selectAllChat 메소드의 코드 또한 함께 변경된다.
따라서, 변경에 따른 영향이 전파되는 관계를 '의존' 관계라고 볼 수 있다.
DI를 통한 의존 처리
public class ChatServiceImpl implements ChatService{
// 의존 객체 직접 생성
private ChatDAO chatDAO = new ChatDao();
@Override
public List<ChatDTO> selectAllChat() throws RuntimeException{
List<ChatDTO> chatList = chatDAO.selectAll( userSession.getChannel_id());
...
}
}
의존하는 객체를 위 코드처럼 직접 생성할 수 있지만 유지보수 관점에서의 문제점이 발생할 수 있다.
이처럼 객체를 직접 생성하는 방법 대신 DI는 의존 객체를 전달받는 방식을 사용한다.
public class ChatServiceImpl implements ChatService{
private ChatDAO chatDAO;
public ChatServiceImpl(ChatDAO chatDAO){
this.chatDAO = chatDAO;
}
...
}
생성자 ChatServiceImpl(ChatDAO chatDAO)는 ChatDAO 객체를 매개변수로 받고,
이를 클래스 내부의 chatDAO 멤버 변수에 할당하게 된다.
즉 생성자를 통해 ChatServiceImpl가 의존하고 있는 ChatDAO 객체를 주입 받은 것이다.
❓굳이 생성자를 통해서 의존하는 객체를 주입하는 이유
객제 지향 설계에 기반한 것이다.
쉽게 말해서 변경의 유연함을 위한 것이다.
DI와 의존 객체 변경의 유연함
만약 생성자를 직접 생성할 때의 코드 변경 상황을 살펴보자.
직접 객체 생성.ver
public class UserRegisterService{
// 의존 객체 직접 생성
private UserDAO userDAO = new UserDao();
...
}
여기서 사용자 데이터를 데이터베이스에 저장하기 위해서 캐시를 사용할 것이다.
UserDao 클래스를 상속받은 CachedUserDAO 클래스를 만들어 보자.
public class CachedUserDAO extends UserDAO{
...
}
CachedUserDAO를 사용하기 위해서 UserRegisterService 클래스와 ChangePasswordService 클래스의 코드를 모두 바꿔야 한다.
만약 UserDAO 객체가 필요한 클래스가 3개 혹은 그 이상이라면 하나씩 다 바꿔줘야 한다는 번거로움이 따라온다.
하지만, DI를 사용한다면 한 번의 코드 수정만 필요하다.
DI.ver
public class UserRegisterService{
private UserDAO userDAO;
public UserRegisterService(UserDAO userDAO){
this.userDAO = userDAO;
}
}
public class ChangePasswordService{
private UserDAO userDAO;
public ChangePasswordService(UserDAO userDAO){
this.userDAO = userDAO;
}
}
위 코드와 같이 생성자를 통해서 의존 객체를 주입받았다.
UserDAO userDAO = new UserDAO();
UserRegisterService registerService = new UserRegisterService(userDAO);
ChangePasswordService changePwdService = new ChangePasswordService(userDAO);
두 클래스의 객체를 생성하는 코드를 작성하였다.
UserDAO를 CachedUserDAO로 변경하고자 한다면 수정되는 코드는 한 곳이다.
위 상황과 동일하게 만약 UserDAO 객체가 필요한 클래스가 3개 혹은 그 이상이여도 의존 주입 대상 객체만 변경해주면 된다.
객체 조립기
main 메서드에서 의존 대상 객체를 생성하고 주입하는 것보다 의존 객체 생성 및 주입 클래스를 따로 만들어주어 관리해보자.
...
public class Assembler {
private UserDAO userDAO;
private UserRegisterService registerService;
private ChangePasswordService changePwdService;
public Assembler() {
userDAO = new UserDAO();
registerService = new UserRegisterService(userDAO);
changePwdService = new ChangePasswordService();
changePwdService.setUserDAO(userDAO);
}
public UserDAO getUserDAO(){
return userDAO;
}
public UserRegisterService getUserRegisterService(){
return registerService;
}
public ChangePasswordService getChangePasswordService(){
return changePwdService;
}
}
위 코드에서 UserRegisterService 객체와 ChangePasswordService 객체에 대한 의존을 주입한다.
UserRegisterService는 생성자를 통해 UserDAO 객체를 주입받고,
ChangePasswordService는 setter를 통해 주입받는다.
이후 Assembler 객체를 생성한 후 get 메소드를 사용하여 필요한 객체를 사용하면 된다.
Assembler assembler = new Assembler();
ChangePasswordService changePwdService = assembler.getChangePasswordService();
changePwdService.changePasword("ererink@gamil.com", "1111")
assembler.getChangePasswordService()에서의 ChangePasswordService 객체는
Assembler 클래스에서 생성하였고, setter를 통해 UserDAO 객체를 주입받은 객체이다.
CachedUserDAO로 변경하고자 한다면 Assembler 클래스에서 UserDAO를 초기화 하는 코드만 변경하면 된다.
public Assembler() {
// 변경된 코드
userDAO = new CachedUserDAO();
registerService = new UserRegisterService(userDAO);
changePwdService = new ChangePasswordService();
changePwdService.setUserDAO(userDAO);
}
매우매우 간편해졌다!
'Spring' 카테고리의 다른 글
@Autowired Annotation (0) | 2023.07.04 |
---|---|
싱글톤 Singleton (0) | 2023.06.19 |
[Spring] DI, IoC (0) | 2023.05.09 |