[Spring] 스프링 DI란? (Dependency Injection, 의존성 주입) (feat. 객체 조립기)
2023. 7. 23.
반응형

 

Spring에는 흔히 말하는 특징 삼대장이 있습니다.

DI, AOP, IoC가 바로 그것입니다.

이번 포스팅에서는 DI, 즉 의존 주입에 대해서 다룹니다.

 

의존이라는 게 도대체 무슨 소리일까요?

여기에서 말하는 의존이란, 객체 간의 의존을 뜻합니다. 변경에 의해 영향을 받는 관계를 의존 관계라고 할 수 있습니다.

말로만 하면 어려우니 코드를 같이 보겠습니다.

 

 

반응형

 

 

MemerService.java

public class MemberService {

  private MemberDao memberDao = new MemberDao();

  public void memberRegist(Member member) {
    memberDao.insert(member);
  }
}

 

 

MemberService라는 클래스에서 MemberDao라는 다른 클래스의 메서드를 불러와 member에 대한 정보를 삽입하는 로직입니다.

의존이란 한 클래스가 다른 클래스의 메소드를 실행하는 것을 의미합니다.

즉, MemberService 클래스가 MemberDao 클래스를 의존하고 있는 것입니다.

 

의존하는 대상을 사용하려면 당연히 그 대상이 존재해야 하겠죠?

그렇기 때문에 Memberdao를 선언하고 객체를 만들어 필드에 할당했습니다.

이 말은 즉, MemberService 객체를 만든다면, 동시에 MemberDao의 객체도 함께 생성된다는 뜻입니다.

 

하지만 이렇게 "직접" 객체를 생성해서 의존관계를 설정하는 것은 유지보수 관점에서 좋지 않습니다.

이번엔 스프링에서 지원하는 DI(의존 주입)을 통해 의존성을 설정해 보겠습니다.

 

스프링은 DI를 여러 가지 방법으로 지원합니다.

Setter, Constructor Injection, Autowiring(자동주입) 등이 있지만 이번엔 생성자를 통해 의존성을 주입해 보도록 하겠습니다.

 

 

반응형

 

public class MemberService {

  private MemberDao memberDao;

  public MemberService(MemberDao memberDao) {
    this.memberDao = memberDao;
  }

  public void memberRegist(Member member) {
    memberDao.insert(member);
  }
}

 

아까와 같은 MemberService 클래스입니다. 그런데 아까는 객체를 선언과 동시에 생성해 줬는데, 이 코드는 의존객체를 전달받는 방식입니다. 직접 객체를 생성하는 것이 아니라 생성자를 통해 의존하고 있는 객체를 주입받는 것입니다.

다른 클래스에서 MemberService객체를 생성해 사용하려면 MemberDao의 객체를 만들어서 주입시켜줘야 한다는 것을 의미합니다.

 

ex)

 

MemberDao memberDao = new MemberDao();
MemberService memberService = new MemberService(memberDao);

 

눈으로 보기엔 사실 직접 만드는 게 쉬워 보입니다. 그런데 왜 이렇게 복잡하게 해야만 하는 걸까요?

바로 코드를 수정할 때 유연하게 적용할 수 있기 때문입니다.

직접 객체를 생성하면 어떤 문제가 발생하길래 유연함이 필요하다고 하는 걸까요?

 

 

반응형

 

 

Case)

 

우리는 MemberDao를 활용해 회원의 정보를 DB에 삽입했습니다. 이제 조회를 하려고 하는데 조회의 성능을 높이기 위해 캐시를 적용하는 상황이 발생했습니다.

 

(Cache: 데이터의 값을 복사해 놓는 임시 장소, 조회 성능을 높이기 위해 자주 조회하는 데이터를 메모리를 사용하는 캐시에 보관)

 

캐시 기능을 구현하기 위해 MemberDao를 상속받아 추가 기능을 구현했다고 가정하겠습니다.

 

CachedMemberDao.java

 

public class CachedMemberDao extends MemberDao {
	...
}

 

그렇다면 MemberService에서 이를 활용해 조회를 하기 위해서는 MemberDao가 아니라 CachedMemebrDao를 활용해야 합니다.

그런데 직접 객체를 만드는 방식을 사용했다면 어떻게 될까요?

 

 

반응형

 

 

 

MemberService.java

 

public class MemberService {

  private MemberDao memberDao = new CachedMemberDao();

  public void memberRegist(Member member) {
    memberDao.insert(member);
  }
}

 

객체를 생성할 때 CachedMemberDao로 코드를 바꿔줘야 합니다.

그냥 한 줄 바꾸는 거 뭐가 문제냐고요?

이 예제에서는 그럴 수 있지만 실제 프로젝트에서는 회원을 조회하는 기능을 MemberService 단 한 개의 파일에서만 사용하지 않습니다.

이 말은 즉, 회원을 조회하는 기능을 사용하기 위해 MemberDao를 선언해 놓은 모든 클래스를 찾아서 코드를 수정해야 한다는 뜻입니다.

할 수야 있겠지만 정말 비효율적이고 객체지향적이지 않은 방법입니다.

 

하지만 생성자를 활용해 의존주입을 했다면 각 클래스의 코드를 수정할 필요가 없습니다.

 

ex)

 

MemberDao memberDao = new CachedMemberDao();
MemberService memberService = new MemberService(memberDao);

 

아까 봤던 이 코드에서 MemeberDao를 CachedMemberDao라고 바꿔서 객체를 생성하면 되기 때문입니다.

코드를 유연하게 수정할 수 있다는 것은 이런 의미입니다.

 

 

반응형

 

 

방금 우리는 스프링에서 DI를 하는 법을 배웠습니다.

이번엔 DI 컨테이너라고도 불리는 '객체 조립기'에 대해서 배워보겠습니다.

조립기라는 이름과 같이 객체를 생성하고 의존 객체를 주입하는 기능을 제공합니다.

 

그래서 이 객체 조립기가 도대체 뭐라고요?

한 마디로 말하자면 위에서 정의한 여러 가지 코드들의 객체를 생성하고 의존 객체를 주입해 주는 코드를 따로 모아놓은 것을 말합니다.

그럼 이 조립기 클래스 코드를 작성해 보겠습니다.

 

Assembler.java

 

public class Assembler {

  private MemberDao memberDao;
  private MemberRegisterService memberReg;
  private ChangePasswordService changepass;
  
  public Assembler() {
  	memberDao = new MemberDao();
	memberReg = new MemberRegisterService(memberDao);
	changePass = new ChangePasswordService();
	changePass.setMemberDao(memberDao);
  }
  
  public MemberDao getMemberDao() {
  	return memberDao;
  }
  
  public MemberRegisterService getMemberRegisterService() {
  	return memberReg;
  }
  
  public ChangePasswordService getChangePasswordService() {
  	return changePass;
  }
}

 

이 클래스 안에서 Assembler 생성자가 조립기의 역할을 해줍니다.

즉, 의존주입을 해주는 역할을 합니다. 의존 객체를 변경하려면 조립기의 코드만 수정하면 됩니다.

Assembler의 생성자에서 필요한 객체를 생성하고 의존해 주입하고 나면 이 객체들을 리턴하는 메서드를(getter) 제공합니다.

그럼 우리는 생성된 값을 받아서 사용하기만 하면 됩니다.

 

 

반응형

 

 

Main.java

 

public class Main {

    private static Assembler assembler = new Assembler();
    
    public static void main(String[] args) {
	MemberRegisterService memberReg = assembler.getMemberRegisterService();
	ChangePasswordService changePass = assembler.getChangePasswordService();
	...
     
    }
}

 

Assembler 객체 하나만 생성하면 우리가 사용하려는 여러 객체 한 번에 생성되어 가지고 와서 사용할 수 있습니다.

이 Assembler가 바로 '객체 조립기'입니다.

 

 

 

 

출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문

반응형
myoskin