티스토리 뷰

Back-end/Spring

스프링 코어#1 (DI)

이안_ian 2019. 5. 18. 16:39




반응형

의존성 주입(DI) 또는 인젝션(injection)

어떤 클래스가 필요로 하는 컴포넌트를 외부에서 생성한 후, 내부에서 사용 가능하게 만들어 주는 과정 

DI의 개요

의존성 주입이라고도 하며, IoC라고 하는 소프트웨어 디자인 패턴 중 하나다. 이때 IoC는 인스턴스를 제어하는 주도권이 역전된다는 의미로 사용되는데, 컴포넌트를 구성하는 인스턴스의 생성과 의존 관계의 연결 처리를 해당 소스코드가 아닌 DI컨테이너에서 대신 해주기 때문에 제어가 역전됐다고 보는 것이다.

 

따라서 지금까지 인스턴스를 애플리케이션에서 직접 생성해서 쓰는 방법 대신 DI 컨테이너가 만들어주는 인슽너스를 가져오는 방법을 사용할 수 있다. 이때 취득한 인스턴스가 의존하는 또 다른 인스턴스 역시 DI 컨테이너에서 관리되기 때문에 연쇄적으로 의존성 주입이 발생해서 연관된 클래스의 인스턴스 모두를 사용할 수 있게 된다.

 

DI컨테이너의 장점

-인스턴스의 스코프를 제어할 수 있다.

-인스턴스의 생명주기를 제어할 수 있다.

-AOP방식으로 공통 기능을 집어넣을 수 있다.

-의존하는 컴포넌트 간의 결합도를 낮춰서 단위 테스트하기가 쉽다.

 

스프링 프레임워크 외에도 DI컨테이너를 제공하는 프레임워크는 많이 있다. 잘 알려진 것은 다음과 같고 기본적인 기능은 대체로 비슷하다.

-CDI(Contexts & Dependency Injection)

-Google Guice

-Dagger

ApplicationContext와 빈 정의

스프링 프레임워크에서는 ApplicationContext가 DI컨테이너 역할을 한다.

먼저 DI 컨테이너가 사용할 Bean객체를 등록해야한다. 빈 설정에는 크게 3가지 방식이 존재한다.

방법 설명
자바 기반 설정방식 자바 클래스에 @Configuration 애너테이션을, 메서드에 @Bean 애너테이션을 사용해 빈을 정의하는 방법으로 스프링 프레임워크 3.0부터 사용할 수 있다. 최근에는 스프링 기반 애플리케이션 개발에 자주 사용되고 특히 스프링 부트에서 이 방식을 많이 활용한다.
XML 기반 설정방식 XML파일을 사용하는 방법으로 <bean>요소의 class 속성에 FQCN(Fully-Qualified Class Name)을 기술하면 빈이 정의된다. <constructor-arg>나 <property>요소를 사용해 의존성을 주입한다. 
애너테이션 기반 설정방식 @Component 같은 마커 애너테이션(Marker Annotation)이 부여된 클래스를 탐색해서(Component Scan)DI 컨테이너에 빈을 자동으로 등록하는 방법이다.

위의 방식 중에 하나만 정해서 사용도 가능하지만 자바 기반과 애너테이션을 섞거나 XML기반과 애너테이션 기반을 섞어서 많이 사용한다.

다음은 각각의 방식으로 설정된 빈 객체를 호출하는 방법이다.

//자바 기반 설정 방식으로 생성자에 @Configuration 애너테이션이 붙은 클래스를 인수로 전달해 호출
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

//애너테이션 기반의 설정 방식으로 패키지명을 인수로 전달해 그 패키지 이하의 경로에서 컴포넌트 스캔함
ApplicationContext context = new AnnotationConfigApplicationContext("com.example.app");

//XML기반 설정 방식으로 경로에 접두어가 생략된 경우에는 클래스패스 안에서 상대경로로 설정 파일을 탐색
ApplicationContext context = new AnnotationConfigApplicationContext("META-INF/spring/applicationContext.xml");
//XML기반 설정 방식으로 상대경로로 탐색한다.
ApplicationContext context = new AnnotationConfigApplicationContext("./spring/applicationContext.xml");

ApplicationContext는 단독 애플리케이션에서 스프링 프레임워크를 사용하거나 JUnit으로 만든 테스트 케이스 안에서 스프링 프레임워크를 구동할 때 사용된다. 반면 웹 애플리케이션에서는 스프링 MVC를 활용하게 되는데, 이때는 ApplicationContext를 웹 환경에 맞게 확장한 WebApplicationContext를 사용한다.

자바 기반 설정 방식

@Configuration
public class AppConfig{
 @Bean
 UserRepository userRepository(){
  return new UserRepositoryImpl();
 }
 
 @Bean
 PasswordEncoder passwordEncoder(){
  return new BCryptPasswordEncoder();
 }
 
 @Bean
 UserService userSErvice(){
  return new UserServiceImpl(userRepository(), passwordEncoder());
 }
}

1. 클래스에 @Configuration 애너테이션을 붙여 설정 클래스를 선언한다. 설정 클래스는 여러 개 정의할 수 있다.

2. 메서드에 @Bean 애너테이션을 부여해서 빈을 정의한다. 메서드명이 빈의 이름이 되고 그 빈의 인스턴스가 반환값이 된다. 만약 빈 이름을 다르게 명시하고 싶으면 @Bean(name="userRepo")와 같이 재정의 가능

3.다른 컴포넌트를 참조해야 할 때는 해당 컴포넌트의 메서드를 호출한다. 의존성 주입이 프로그램적인 방법으로 처리된다.

 

자바 기반 설정 방식에서는 메서드에 매개변수를 추가하는 방법으로 다른 컴포넌트의 의존성을 주입할 수 있다. 단, 인수로 전달될 인스턴스에 대한 빈은 별도로 정의되어 있어야 한다.

 @Bean
 UserService userSErvice(UserRepository userRepository, PasswordEncoder passwordEncoder){
  return new UserServiceImpl(userRepository, passwordEncoder);
 }

XML 기반 설정 방식

<?xml version="1.0" encoding="UTF-8"?>
<beans
 xmlns="http://www,springframework.org/schema/beans"
 <bean id="userRepository" class="com.example.demo.UserRepositoryImpl" />
 <bean id="passwordEncoder" class="com.example.demo.BCryptPasswordEncoder" />
 <bean id="userService" class="com.example.demo.ServiceImpl"/>
  <constructor-arg ref="userRepository" />
  <constructor-arg ref="passwordEncoder" />
 </bean>
</beans>

1.<beans> 요소 안에 빈 정의를 여러 개 한다.

2.<bean> 요소에 빈 정의를 한다. id 속성에서 지정한 값이 빈의 이름이 되고, class속성에서 지정한 클래스가 해당 빈의 구현 클래스이다. 이때 class속성은 FQCN으로 패키지명부터 클래스명까지 명확하게 기재해야한다.

3.<constructor-arg>요소에서 생성자를 활용한 의존성 주입을 한다. ref 속성에 주입할 빈의 이름을 기재한다.

 

자바 기반 설정 방식이나 XML 기반 설정 방식 모두 이렇게 다 일일히 정의하기엔 좀 많을 수 있기 때문에 보통 애너테이션 기반 설정과 섞어서 많이 사용된다. 참고로 의존성 주입에서 주입할 대상이 다른 빈이 아니라 특정 값인 경우, ref 속성을 이용하지 않고 value 속성을 사용한다.

 

XML파일의 value 속성에 문자열을 지정한다고해서 실제 자바코드에서 그 값을 받는 타입이 반드시 문자열 타입일 필요는 없다. XML파일에는 문자열로 기재돼 있더라도 필요한 경우 DI를 하는 과정에서 숫자나 날짜 타입, 혹은 파일이나 프로퍼티 같은 여러가지 타입으로 형변환할 수 있다.

애너테이션 기반 설정 방식

DI 컨테이너에 관리할 빈을 빈 설정 파일에 정의하는 대신 빈을 정의하는 애너테이션을 빈의 클래스에 부여하는 방식을 사용한다. 이후 이 애너테이션이 붙은 클래스를 탐색해서 DI 컨테이너에 자동으로 등록하는데 이러한 탐색 과정을 컴포넌트 스캔이라 한다. 또한 의조선 주입도 이제까지처럼 명시적으로 설정하는 것이 아니라 애너테이션이 붙어 있으면 DI 컨테이너가 자동으로 필요로 하는 의존 컴포넌트를 주입하게 한다. 이러한 주입 과정을 오토와이어링(Auto Wiring)이라한다.

@Component
 public class UserRepositoryImpl implements UserRepository {
  // 생략
 }
 
@Component
 public class BCryptPasswordEncoder implements passwordEncoder{
  // 생략
 }
 
 @Component
 public class UserServiceImpl implenets UserService{
  @Autowired
  public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder){
   // 생략
  }
 }

1.빈 클래스에 @Component 애너테이션을 붙여 컴포넌트 스캔이 되도록 만든다.

2.생성자에 @Autowired 애너테이션을 부여해서 오토와이어링되도록 만든다. 오토와이어링을 사용하면 기본적으로 주입 대상과 같은 타입의 빈을 DI 컨테이너에서 찾아 와이어링 대상에 주입하게 된다.

 

컴포넌트 스캔을 수행할 때는 스캔할 범위를 지정해야 하는데 설정 방식으로는 자바 기반 방식이나 XML 기반 방식으로 할 수 있다.

//자바 기반 설정 방식
@Configuration
@ComponentScan("com.example.demo")
 public class AppConfig{
  //생략
 }

//XML 기반 설정 방식
<beans
 <context:component-scan base-package="com.example.demo" />
</beans>

DI 컨테이너에 등록되는 빈의 이름은 기본적으로 클래스명의 첫 글자를 소문자로 바꾼 이름과 같다. 단, 첫 글자 이후에 대문자가 연속되는 경우에는 첫 글자를 소문자로 변환하지 않고 클래스명이 그대로 빈 이름으로 사용된다.

 

 

 

 

반응형
댓글
반응형
최근에 달린 댓글
글 보관함
Total
Today
Yesterday
최근에 올라온 글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31