본문 바로가기
Java/[스프링 5 프로그래밍 입문]

[Spring] 스프링 애노테이션을 사용한 의존 주입(DI)

2021. 7. 28.
반응형

스프링은 필요한 객체를 생성하고 생성한 객체에 의존을 주입하며, 객체를 제공하는 기능을 정의한다. 의존 주입을 위해 생성자 또는 setter 메서드를 사용하는 방법이 있었다. 이번 포스트에서는 다양한 애노테이션을 이용해 의존 객체를 자동으로 주입하는 방법을 알아보자.

 

애노테이션을 이용한 스프링의 의존 주입

스프링에서는 다양한 애노테이션이 사용된다. 그중에서 @Configuration, @Autowired, @Import 애노테이션에 대해서 알아보자.

설정 클래스의 @Bean 설정 및 싱글톤 객체

 

[Spring] 스프링에서의 의존 주입(DI)의 의미와 사용법

의존은 변경에 따른 영향이 전파되는 관계를 의미하며 이를 처리하는 방식에 의존 주입이 있다. 이번 포스트에서는 스프링에서 의존 주입이 어떤 방식으로 일어나는지 알아보자. 또한 생성자

dct-wonjung.tistory.com

이전 글에서 @Bean 애노테이션을 이용해 의존 객체를 스프링 컨테이너에 생성하고 의존 주입을 하는 방법을 알아보았다. 아래 코드는 ExDao 객체를 주입받는 두 가지 객체에 대한 코드이다.

@Configuration
public class AppCtx {

	@Bean
	public ExDao exDao(){
		return new ExDao();
	}
	
	@Bean
	public ExService exService(){
		// exDao()가 생성한 객체를 주입
		return new ExService(exDao());
	}
	
	@Bean
	public SampleService sampleService(){
		// exDao()가 생성한 객체를 주입
		return new SampleService(exDao());
	}
}

exService() 메서드와 sampleService() 메서드에서 모두 exDao() 메서드를 실행하고 있다. 그리고 exDao() 메서드는 ExDao 객체를 생성해서 반환한다. 그렇다면 exService() 메서드를 실행할 때 생성되는 ExDao 객체와 sampleService() 메서드를 실행할 때 생성되는 ExDao 객체는 서로 다른 것일까?

 

정답은 X이다. 스프링 컨테이너가 생성한 빈은 싱글톤 객체이기 때문에 @Bean이 붙은 메서드에 대한 한 개의 객체만을 생성한다. 따라서 다른 설정 메서드에서 exDao() 메서드를 아무리 많이 호출하더라도 항상 같은 ExDao 객체를 반환하게 된다.

 

스프링은 설정 클래스를 그대로 사용하지 않고 설정 클래스를 상속한 새로운 설정 클래스를 만들어서 사용한다. 한번 생성한 객체를 보관했다가 이후에는 동일한 객체를 리턴하도록 구현되어 있다. 따라서 예제의 exService() 메서드와 sampleService() 메서드를 호출할 때 동일한 ExDao 객체를 주입받게 된다.

 

스프링에서 여러 개의 설정 파일을 사용하는 방법

스프링을 이용해서 개발을 진행하다 보면 수십, 수백 개의 빈을 설정하게 될 것인데, 이를 하나의 설정 파일에서 관리하는 것은 매우 어렵고 복잡한 일이다. 따라서 기능 또는 역할에 따라 설정 파일을 나누게 되면 관리하기 훨씬 수월해질 것이다. 스프링에서는 한 개 이상의 설정 파일을 이용해서 스프링 컨테이너를 생성하는 방법을 제공한다. 예제로 이전 포스트에서 작성했던 설정 파일 코드를 살펴보자.

@Configuration
public class AppCtx {

	@Bean
	public ExDao exDao(){
		return new ExDao();
	}
	
	@Bean
	public SampleDao sampleDao(){
		return new SampleDao();
	}
	
	@Bean
	public ExService exService(){
		// exDao()가 생성한 객체를 주입
		return new ExService(exDao());
	}
	
	@Bean
	public SampleService sampleService(){
		// setter를 이용하여 exDao 빈, sampleDao 빈 주입
		SampleService sampleService = new SampleService();
		sampleService.setExDao(exDao());
		sampleService.setSampleDao(sampleDao());
		return sampleService;
	}
}

위의 코드는 하나의 설정 파일에서 모든 빈 객체를 설정한다. 이 클래스를 두 개의 클래스로 분리하여 작성한 코드는 아래와 같다.

@Configuration
public class AppConf1 {

	@Bean
	public ExDao exDao(){
		return new ExDao();
	}
	
	@Bean
	public SampleDao sampleDao(){
		return new SampleDao();
	}
}

@Configuration
public class AppConf2 {

	@Autowired
	private ExDao exDao;
	@Autowired
	private SampleDao sampleDao;
	
	@Bean
	public ExService exService(){
		// 자동 주입된 exDao 객체를 주입
		return new ExService(exDao);
	}
	
	@Bean
	public SampleService sampleService(){
		SampleService sampleService = new SampleService();
		// 자동 주입된 exDao, sampleDao 객체를 주입
		sampleService.setExDao(exDao);
		sampleService.setSampleDao(sampleDao);
		return sampleService;
	}
}

의존 객체를 주입받는지의 여부에 따라 클래스를 두 개로 나누어보았다. 여기서 주목할 부분은 @Autowired라는 애노테이션이다. @Autowired는 스프링의 자동 주입 기능을 위한 애노테이션이다. 설정 클래스의 필드에 @Autowired를 붙이면 해당 타입의 빈을 찾아서 필드에 할당한다. 즉 exDao에는 ExDao 타입의 빈을, sampleDao에는 SampleDao 타입의 빈을 할당한다. 그 후에는 해당 빈이 필요한 곳에 이 필드를 사용해서 빈을 주입하면 된다.

private ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf1.class, AppConf2.class);

설정 클래스가 여러 개인 경우에는 위의 코드처럼 모든 설정 클래스를 파라미터로 전달해준다. 

@Configuration과 @Autowired 애노테이션

@Autowired 애노테이션은 스프링 빈에 의존하는 다른 빈을 자동으로 주입하고 싶을 때 사용한다.  의존 주입 대상에 @Autowired 애노테이션을 붙이면 스프링 설정 클래스의 @Bean 메서드에서 의존 주입을 위한 코드를 작성할 필요가 없다. 위의 예제에서도 @Autowired를 통해 설정 클래스에서 자동 주입을 사용하였다.

 

스프링 컨테이너는 @Configuration 애노테이션이 붙어있는 설정 클래스를 내부적으로 스프링 빈으로 등록하며, @Autowired가 붙은 대상에 대해 알맞은 빈을 자동으로 주입한다. 아래의 코드에서는 setter를 통해 의존 객체를 주입받던 SampleService 클래스를 @Autowired를 이용한 자동 주입 방식으로 변경한 예제이다.

// 1. setter를 통해 의존 객체를 주입
public class SampleService {

	private ExDao exDao;
	private SampleDao sampleDao;
	
	public void setExDao(ExDao exDao) {
		this.exDao = exDao;
	}
	
	public void setSampleDao(SampleDao sampleDao) {
		this.sampleDao = sampleDao;
	}
}
// 2. @Autowired 애노테이션을 통해 의존 객체를 자동 주입
public class SampleService {

	@Autowired
	private ExDao exDao;
	@Autowired
	private SampleDao sampleDao;
	
}

이 방식으로 SampleService를 구현하면 위에서 살펴봤던 AppConf2 클래스도 아래와 같이 고칠 수 있다. sampleService() 메서드에서 exDao와 sampleDao 객체를 직접 주입할 필요가 없어졌다.

@Configuration
public class AppConf2 {

	@Autowired
	private ExDao exDao;
	@Autowired
	private SampleDao sampleDao;
	
	@Bean
	public ExService exService(){
		// 자동 주입된 exDao 객체를 주입
		return new ExService(exDao);
	}
	
	@Bean
	public SampleService sampleService(){
		SampleService sampleService = new SampleService();
		return sampleService;
	}
}

 

두 개 이상의 설정 파일을 편리하게 사용하는 @Import 애노테이션

두 개 이상의 설정 파일은 앞선 예시처럼 각각 파라미터로 전달하여 사용할 수도 있지만, @Import 애노테이션을 이용해 사용할 수도 있다. @Import 애노테이션은 함께 사용할 설정 클래스를 지정하는 역할을 한다. 아래의 코드에서는 AppConf1 클래스에서 @Import로 AppConf2 클래스를 지정하고 스프링 컨테이너를 생성하는 위치에서 AppConf1 클래스 하나만 지정한다.

@Configuration
@Import(AppConf2.class)
public class AppConf1 {

	...

}
private ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf1.class);

 

@Import 애노테이션을 이용해 최상위 설정 클래스를 정의하여 사용하는 방법도 있다. 아래의 AppConfImport 클래스는 앞서 정의한 AppConf1 클래스와 AppConf2 클래스를 함께 사용하도록 지정하는 코드이다.

@Configuration
@Import({AppConf1.class, AppConf2.class})
public class AppConfImport {
}
private ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfImport.class);

위와 같이 최상위 설정 클래스를 이용하면 다른 설정 클래스를 추가했을 때 최상위 설정 클래스의 코드만 변경하면 된다. 즉 설정 클래스로 스프링 컨테이너를 생성하는 부분의 코드를 변경하지 않아도 된다.

 

 

참고 서적: <초보 웹 개발자를 위한 스프링 5 프로그래밍 입문>

 

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

COUPANG

www.coupang.com

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

728x90
반응형

댓글