자바 객체지향의 원리와 이해 (3)



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

자동차가 생성되고 내부적으로 타이어를 생산한다고 가정하자.

자동차 클래스 내부에 Tire 속성이 있다.
자동차 클래스의 생성자에서 타이어를 생산한다.
new Tire() 에서 타이어를 생산하는 new 부분에서 의존 관계가 일어나고 있다.

결론은 다음과 같다.


스프링 없이 의존성 주입하기 1 - 생성자를 통한 주입

운전자가 자동차를 생산하면서 타이어를 장창한다고 가정하자.
Car car = new Car(tire);

주입이란, 외부에서라는 말을 내포하고 있다.
외부에서 생산된 타이어를 자동차에 장착하는 작업이 주입이다.
앞서 Car 객체가 Tire를 직접 생산하는 것은
Tire에 대한 의존성을 자체적으로 해결하는 방식이었다.

이번에는 외부에서 생산된 tire 객체를 자동차 생성자의 인자로 주입(장착)하는 형태로 구현해보자.
달라지는 것은 자동차의 생성자에 인자가 생기는 것이다.

변경된 코드에서의 장점은
기존에는 Car가 구체적으로 한국 타이어를 생산할지 미국 타이어를 생산할지 결정했다.
그러면 코드는 유연성이 떨어진다.

Car는 한국 타이어와 미국 타이어에 대해 정확히 알고 있어야만 그에 해당하는
객체를 생성할 구 있었다.

의존성 주입을 적용할 경우, 그저 Tire 인터페이스를 구현한 어떤 객체가 들어오면 정상적으로 작동된다.
의존성을 주입할 경우 확장성도 좋아지는데, Car.java 코드를 변경할 필요도 없어진다.

실제 제품화하게 되면 더 많은 코드를 재배포할 필요가 없도록 구성해야 코드 재컴파일과 재배포에 대한 부담을 줄일 수 있다.
이것은 인터페이스를 구현(준수)했기에 얻는 이점이라고 볼 수 있다.

표준화했다고 이해하면 될것이다.


스프링 없이 의존성 주입하기2 - 속성을 통한 의존성 주입

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

Tire tire = new KoreaTire();  
Car car = new Car();  
car.setTire(tire);    

생성자를 통해 의존성을 주입하는 것은 교체의 방법이 없다.
이러한 문제점을 해결하기 위해 속성을 통한 의존성 주입이 필요하다.

그러나 최근 속성을 통한 의존성 주입보다 생성자를 통한 의존성 주입을 선호하는 사람들이 많은데,
이는 프로그램에서는 한번 주입된 의존성을 계속 사용하는 경우가 더 일반적이기 때문이다.


스프링을 통한 의존성 주입 - XML 파일 사용

다음과 같은 시나리오를 갖는다고 가정하자.

스프링을 통한 의존성 주입은 생성자를 통한 의존성 주입과 속성을 통한 의존성 주입을 모두 지원하는데
속성을 통한 의존성 주입만 살펴보자.

스프링을 도입해서 달라지는 것은 Driver.java 클래스와 스프링 설정 파일 하나를 추가해 주는 것 뿐이다.

종합 쇼핑몰의 역할이 스프링 프레임워크라고 생각하면 된다.
그래서 Driver.java 에 상품을 구매할 종합 쇼핑몰에 대한 정보가 필요한 것이다.

XML 파일에 상품을 등록해놓는데.
상품을 등록할 때 bean 태그를 이용해 등록한다.

각 상품을 구분하기 위한 id 속성과 그 상품을 어떤 클래스를 통해
생산(인스턴스화) 해야 할지 나타내는 class 속성을 함께 지정하면 된다.

KoreaTire.java가 XML 파일에서 id=tire인 bean 태그와 연결돼 있고,

Driver.java 의 main() 메서드에서 아래 코드로 연결돼 있는 것을 볼 수 있다.

context.getBean("tire", Tire.class);     

위 코드는 아래와 같이 해석된다.
“KoreaTire라고 하는 상품이 tire라는 이름으로 진열돼 있고, 구매(getBean)할 수 있다.”

스프링을 도입해서 얻는 이득은 무엇일까?
가장 큰 이득은 자동차의 타이어 브랜드를 변경할 때
그 무엇도 재컴파일/재배포하지 않아도 XML 파일만 수정하면 프로그램의 실행 결과를 바꿀 수 있다는 것이다.


스프링을 통한 의존성 주입 - 스프링 설정 파일(XML)에서 속성 주입

XML 파일에 property 속성을 사용해보자.
자바에서 접근자 및 설정자 메서드를 속성 메서드라고 하는데 영어로 속성은 property다.
결국 car.setTire(tire)라고 하던 부분을 XML 파일의 property 태그를 이용해 대체하는 것이다.

그래서 Driver.java 에는 Car car = context.getBean("car", Car.class); 코드만 남는다.


스프링을 통한 의존성 주입 - @Autowired 를 통한 속성 주입

반드시 설정자 메서드를 통해서 Tire 값을 주입해야 하는 걸까?
스프링의 속성 주입 방법 가운데 @Autowired를 이용하는 방법을 살펴보자.

@Autowired의 의미를 이해해보자.
이것은 스프링 설정 파일을 보고 자동으로 속성의 설정자 메서드에 해당하는
역할을 해주겠다는 의미이다.

이렇게 구현하면 XML 설정 파일에서 property 태그가 필요없다.
@Autowired를 통해 자동으로 엮어줄 수 있기(의존성 주입) 때문이다.

(1) 그렇가면 AmericaTire로 변경해서 사용하려면 어떻게 해야할까?
bean 태그의 class 속성을 AmericaTire로 변경한다.

(2) KoreaTire 부분을 완전히 삭제하고, AmaricaTire의 bean 태그에서 id를 삭제하면 어떻게 될까?
정상적으로 구동된다.

빈의 id 속성이 없는데 어떻게 매칭된걸까?

@Autowired는 type 기준 매칭이 우선되기 때문이다.
만약 같은 타입으로 구현한 클래스가 여러 개 있다면 그때 빈 태그의 id로 구분하여 매칭한다.
type을 구현한 빈이 있고 하나라면 그 유일한 빈을 객체에 할당한다.


스프링을 통한 의존성 주입 - @Resource를 통한 속성 주입

@Resource 는 자바 표준 어노테이션이다.
매칭 우선순위는 @Autowired 다르게 id가 더 높다.


마무리
변수에 값을 할당하는 모든 곳에 의존 관계가 생긴다.
의존 대상이 내부에 있을 수도, 외부에 있을 수도 있다.
DI는 외부에 있는 의존 대상을 주입하는 것을 말한다.

의존 대상을 구현하고 배치할 때 SOLID 와 응집도는 높이고 결합도는 낮추라는 기본 원칙에 충실해야 한다.
그래야 프로젝트의 구현과 유지보수가 수월해진다.


AOP - Aspect? 관점? 핵심 관심사? 횡단 관심사?

AOP 글 보러가기


PSA (Potable Service Abstraction) 일관성있는 서비스 추상화

PSA는 일관성 있는 추상화이다.

서비스 추상화의 예로 JDBC를 들 수 있다. JDBC라고 하는 표준 스펙이 있기 때문에 오라클을 사용하든, MySQL을 사용하든,
Connection, Statement, ResultSet을 이용해 공통된 방식으로 코드를 작성할 수 있다.
데이터베이스 종류에 관계없이 같은 방식으로 제어할 수 있는 이유는 어댑터 패턴을 활용했기 때문이다.
이처럼 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게한 것을 서비스 추상화라고 한다.

스프링은 ORM, 캐시, 트랜잭션 등 다양한 기술에 대한 PSA를 제공한다.