스프링

스프링5 프로그래밍 입문 - 스프링 DI

담쏙 2023. 10. 2. 11:14
728x90

의존이란?

  • DI (Dependency Injection), 의존 주입이라고 함
  • 객체 간의 의존을 의미
import java.time.LocalDateTime;

public class MemberRegisterService{
	private MemberDao memberDao = new MemberDao();

	public void regist(RegisterRequest req){
		// 이메일로 회원 데이터(Member) 조회
		Member member = memberDao.selectByEmail(req.getEmail());
		if(member != null){
			// 같은 이메일을 가진 회원이 이미 존재하면 익셉션 발생
			throw new DuplicateMemberException("dup email" + req.getEmail());
		}
		// 같은 이메일을 가진 회원이 존재하지 않으면 DB에 삽입
		Member newMember = new Member(req.getEmail(), req.getPassword(), req.getName(), 
																	LocalDateTime.now());
		memberDao.insert(newMember);
	}
}
  • MemberRegisterService 클래스가 DB 처리를 위해 MemberDao 클래스의 메서드를 사용한다
  • 한 클래스가 다른 클래스의 메서드를 실행할 때 이를 ‘의존’한다고 표현
💡 의존은 변경에 의해 영향을 받는 관계를 의미한다. MemberDao의 insert 메서드의 이름을 변경하면 이 메서드를 사용하는 클래스의 소스코드도 함께 변경된다. 이렇게 변경에 따른 영향이 전파되는 관계를 ‘의존’한다고 표현한다.
  • 의존하는 대상이 있으면 그 대상을 구하는 방법이 필요
    • 가장 쉬운 방법은 의존 대상 객체를 직접 생성하는 것
      • MemberRegisterService 객체를 생성하는 순간에 MemberDao 객체도 함께 생성됨
      • 쉽긴 하지만 유지보수 관점에서 무제가 있음
    • DI와 서비스 로케이터로 의존 객체를 구할 수 있음

 

DI를 통한 의존 처리

  • 의존하는 객체를 직접 생성하는 대신 의존 객체를 전달받는 방식 사용
import java.time.LocalDateTime;

public class MemberRegisterService{
	private MemberDao memberDao;

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

	public void regist(RegisterRequest req){
		Member member = memberDao.selectByEmail(req.getEmail());
		if(member != null){
			throw new DuplicateMemberException("dup email" + req.getEmail());
		}
		Member newMember = new Member(req.getEmail(), req.getPassword(), req.getName(), 
																	LocalDateTime.now());
		memberDao.insert(newMember);
	}
}
  • 생성자를 통해서 의존 객체를 전달 받음. 즉 의존하고 있는 객체를 주입(Injection) 받은 것
  • MemberRegisterService 클래스를 사용하는 코드는 객체를 생성할 때 생성자에 MemberDao 객체를 전달해야 함
MemberDao dao = new MemberDao();
// 의존 객체를 생성자를 통해 주입
MemberRegisterService svc = new MemberRegisterService(dao);

 

DI와 의존 객체 변경의 유연함

  • 회원의 암호 변경 기능을 제공하는 ChangePasswordService 클래스도 의존 객체를 직접 생성한다고 가정
public class ChangePasswordService{
	private MemberDao memberDao = new MemberDao();
}
  • 회원 데이터의 빠른 조회를 위해 캐시를 적용해야하는 상황이 발생해 MemberDao 클래스를 상속받은 CachedMemberDao 클래스 생성
public class CachedMemberDao extends MemberDao{
}
  • 캐시 기능을 적용한 CachedMemberDao를 사용하려면 MemberDao 객체가 필요한 클래스의 소스를 모두 변경해야 함
  • 동일한 상황에서 DI를 사용하면 수정할 코드가 줄어듦

@Configuration : 스프링 설정 클래스

@Bean : 해당 메서드가 생성한 객체를 스프링 빈이라고 설정. 메서드마다 한 개의 빈 객체 생성

 

@Bean 설정과 싱글톤

  • 스프링 컨테이너가 생성한 빈은 싱글톤 객체
  • @Bean 이 붙은 메서드에 대해 한 개의 객체만 생성 (몇 번을 호출하더라도 항상 같은 객체 리턴)
  • 스프링은 설정 클래스를 그대로 사용하지 않고, 설정 클래스를 상속한 새로운 설정 클래스를 만들어 사용

 

두 개 이상의 설정 파일 사용

  • 설정하는 빈의 개수가 증가하면 한 개의 클래스 파일에 설정하는 것보다 영역별로 설정 파일을 나누면 관리하기가 편해짐
  • AnnotationConfigApplicationContext 의 생성자의 인자는 가변 인자이기 때문에 목록을 콤마로 구분해서 전달 가능

 

@Configuration , @Autowired

  • @Autowired 는 스프링의 자동 주입 기능을 위한 것. 해당 타입의 빈을 찾아서 필드에 할당.
    • 스프링 빈에 의존하는 다른 빈을 자동으로 주입하고 싶을 때 사용
  • 스프링 컨테이너는 @Configuration 어노테이션을 붙인 설정 클래스를 스프링 빈으로 등록

 

@Import

  • 함께 사용할 설정 클래스를 지정
  • @Import를 사용해서 포함한 설정 클래스가 다시 @Import를 사용할 수 있음
  • 설정 클래스를 변경해도 AnnotationConfigApplicationContext를 생성하는 코드는 최상위 설정 클래스 한 개만 사용하면 됨