본문 바로가기

Development/Spring Framework

스프링 삼각형과 설정 정보 (1) IoC/DI

스프링 삼각형과 설정 정보

 

스프링을 이해하기 위해 POJO [ Plain Old Java Object ]를 기반으로 스프링 삼각형이라는 IoC/DI, AOP, PSA라고 하는 3대 프로그래밍 모델에 대한 이해가 필수이다.

POJO
오래된 방식의 간단한 자바 오브젝트라는 뜻으로, JAVA EE 등 중량 프레임워크들을 사용하며 해당 프레임워크에 종속된 무거운 객체를 만들게 된 것에 반발해 사용하게 된 용어이다. 쉽게 이야기하자면, 특정 기술에 종속되어 동작하는 것이 아닌 순수한 자바 객체를 이야기하는 것이다.

 

POJO에 대해서는 다음에 자세히 살펴보도록 하자.

 

 


 

IoC/DI :  제어의 역전/의존성 주입

 

프로그래밍에서의 의존성이란?

책에서 설명하기를..

 

의사 코드
운전사가 자동차를 생산한다.
자동차는 내부적으로 타이어를 생산한다.

자바로 표현
new Car( );
Car 객체 생성자에서 new Tire( );

의존성을 단순하게 정의하면 다음과 같다.
의존성은 new 이다.
new를 실행하는 Car와 Tire 사이에서 Car가 Tire에 의존한다.

 

우선.. 결론적으로 이야기하자면, 전체가 부분에 의존한다고 표현할 수 있으며, 기억할 사실은 프로그래밍에서 의존 관계는 new로 표현된다.

 

 

Jeong-sky-1003/springstudy1

Contribute to Jeong-sky-1003/springstudy1 development by creating an account on GitHub.

github.com

 

의존성 주입은 어떻게 이루어지는지 살펴보겠다.

 


스프링을 사용하지 않은 의존성 주입

(1) 생성자를 통한 의존성 주입

의사 코드
운전자가 타이어를 생산한다.
운전자가 자동차를 생산하면서 타이어를 장착한다.

자바로 표현 - 생성자 이용
Tire tire = new KoreaTire( );
Car car = new Car(tire);

 

주입이란 무엇인가? 이 말 자체로 해석하자면 외부에서 라는 뜻을 내포하고 있다. 결국 자동차 내부에서 타이어를 생산하는 것이 아니라 외부에서 생산한 타이어를 자동차에 장착하는 작업이 주입이다.

 

 

Jeong-sky-1003/springstudy1

Contribute to Jeong-sky-1003/springstudy1 development by creating an account on GitHub.

github.com

앞선 코드와 가장 큰 차이점은 생성자의 인자로 tire 참조 변수 객체를 받는다. 이로 코드의 유연성을 확보할 수 있다.

의존성 주입을 적용함으로 Car 생성자는 Tire 인터페이스를 구현한 어떠한 객체가 주입되었을 때 정상적으로 작동하게 된다. 이로 후에 어떠한 새로운 타이어 브랜드가 생겨도, 각 타이어 브랜드들이 Tire 인터페이스를 상속받아 구현한다면 Car의 코드 변경 없이 사용할 수 있다.

 

한 줄로 정리해보면 [ 현실 세계의 표준 규격 준수는 프로그래밍 세계의 인터페이스 구현 ] 이다.

 

하나 더 살펴보겠다.

해당 예제는 앞서 살펴본 디자인 패턴 중 '전략 패턴'이 적용되고 있다.

 

전략 객체: Tire 인터페이스를 구현한 KoreaTire, AmericaTire

전략 객체를 사용하는 컨텍스트: Car 클래스의 getTireBrand( ) 메서드

클라이언트: Driver의 run( ) 메서드

(2) 속성을 통한 의존성 주입

의사 코드
운전자가 타이어를 생산한다.
운전자가 자동차를 생산한다.
운전자가 자동차에 타이어를 장착한다.

자바로 표현 - 속성 접근자 메서드 사용
Tire tire = new KoreaTire( );
Car car = new Car( );
car.setTire(tire);

 

현실 세계로 이야기하자면, 운전자가 타이어 교체를 원할 때 교체할 수 있도록 한 것이다. 자바에서 이를 구현하려면 생성자를 통한 의존성 주입이 아닌 속성을 통한 의존성 주입이 더욱 필요하다.

 

속성 접근자는 getter와 setter로 코드 예시는 아래와 같다.

 

 


 

스프링을 통한 의존성 주입

(1) XML 파일 사용

의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다.
운전자가 종합 쇼핑몰에서 자동차를 구매한다.
운전자가 자동차에 타이어를 장착한다.

자바로 표현 - 속성 메서드 사용

ApplicationContext context = new ClassPathXmlApplicationCotext("expert002.xml", Driver.class);
Tire tire = (Tire) context.getBean("tire");
Car car = (Car) context.getBean("car");
car.setTire(tire);

 

위 의사 코드에 보면 기존에 없던 '종합 쇼핑몰'이 추가되었다. 이 역할은 Spring Framework로 이해하자.

Tire와 Car는 달라질 내용이 없으나, Driver 코드는 변화한다. 기존에는 Driver에서 Car와 Tire를 모두 생산하고 조립했지만, 이제는 종합 쇼핑몰을 통해 구매하기에 구매처에 대한 정보가 필요하다.

 

springframework 패키지를 import 받아 코드를 작성한다.

 

(2) 스프링 설정 파일(XML) 에서 속성 주입

의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생산한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.

자바로 표현 - 속성 메서드 사용

ApplicationContext context = new ClassPathXmlApplicationCotext("expert003/expert003.xml");
Car car = (Car) context.getBean("car");

XML로 표현
<bean id="koreaTire" class="expert003.KoreaTire"></bean>
<bean id="americanTire" class="expert003.americanTire"></bean>
<bean id="car" class="expert003.Car">
     <property name="tire" ref = "koreaTire"></property>
</bean>

 

영어로 속성은 property이다. XML에서 setTire를 property로 대신하여 주입하는 것이다.

 

(3) @Autowired를 통한 속성 주입

의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생산한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.

 

의사 코드는 의전과 동일하다. 해당 책의 저자는 프로그래머의 3대 스킬을 이야기하고 있다.

  • C&P : Copy & Paste [ 복사 & 붙여넣기 ]
  • D&C : Devide & Conquer [ 분할 & 정복 ]
  • C&I : Creative Idleness [ 창조적 게으름 ]

스프링에서 마찬가지로 창조적 게으름의 산물을 볼 수 있다. 바로 @Autowired 어노테이션을 활용하는 것이다. 이를 활용한다면 xml 파일에서 property 태그를 통해 설정해주지 않아도 자동으로 의존성 주입이 가능하다.

 

하지만 xml 파일에 ID로 tire가 존재하지 않는다면 어떻게 될까?

 

 

문제 없이 작동한다. 그 이유는 인터페이스 구현의 여부로 가능한 것이다.

@Autowired는 type기준 매칭을 해주는 것이다. 만약 같은 타입을 구현한 클래스가 여러 개 존재한다면, bean 태그의 id로 구분해 매창하는 것이다.

 

그렇다면 같은 type으로 구현된 클래스가 여러 개 존재하고, bean 태그로도 모두 존재한다면 어떻게 매칭하는가?

 

 

위는 id로도 구분할 수 없는 상황이다. 그래서 이 경우에는 에러가 발생한다.

[ 에러: expected single matching bean but found 2 ] 이렇게 말이다.

 

잊지 말아야 하는 것! @Autowired는 Type을 기준으로 매칭해주는 것이다. 즉 우선순위는 Type이며 그 다음이 ID이다.

 

(4) @Resource를 통한 속성 주입

의사 코드
운전자가 종합 쇼핑몰에서 타이어를 구매한다.
종합 쇼핑몰은 자동차를 생산한다.
종합 쇼핑몰은 타이어를 생산한다.
종합 쇼핑몰은 자동차에 타이어를 장착한다.
종합 쇼핑몰은 운전자에게 자동차를 전달한다.

의사코드는 동일하다. Annotation만 @Autowired에서 @Resource로 변경한다.

 

 

@Resource와 @Autowired 차이를 간단히 살펴보자면, 아래와 같다.

 

  • @Autowired
    Spring Annotation이며, type과 id 중 type의 우선순위가 더 높다.

  • @Resource
    자바 표준 Annotation으로, type과 id 중 id의 우선순위가 더 높다.

(5) @Autowired   VS   @Resource   VS   <property> 태그

  @Autowired @Resource
출처 Spring Framework 표준 자바
소속 패키지 org.springframework javax.annotation
빈 검색 방식 (1) Type
(2) ID
(1) ID
(2) Type
특이사항 @Qualifier(" ") 와 협업 name 어트리뷰트
byName (ID) 강제하기 @Autowired
@Qualifier("tire1")
@Resource(name = "tire1")

 

그렇다면 자동으로 의존성 주입을 할 때 어떤 것을 사용하는 것이 좋을까?

 

  • 스프링이 아닌 다른 프레임워크로 교체될 수 있다는 점에 대비한다면 @Resource를 사용하는 것이 유리하며,

  • 유지보수에 부관한 관곅 존재하는 경우에는 @Resource

  • 만약 유지보수와 밀접하거나 자주 변경되는 관계가 존재한다면 <property> 태그를 이용하는 것이 유리하다.

 

이와 같이 설정 파일을 사용하는 이유는 간단하다. 재컴파일과 재배포의 필요 없이 실행 결과를 변경할 수 있다.

 


 

초반에 의존 관계가 new라고 단순화했으나, 변수에 값을 할당하는 모든 곳에 의존 관계가 발생한다. 즉 대입 연산자에 의해 변수에 값이 할당되는 순간에 의존이 생기는 것이다. DI는 외부에 존재하는 의존 대상을 주입하는 것을 이야기하며, 의존대상을 구현하고 배치할 때는 외부와 결합도는 낮추고 내부 응집도를 높이는 것이 향후 구현과 유지보수가 수월해진다.