티스토리 뷰

스프링을 이해하는 데 POJO(Plain Old Java Object)를 기반으로 스프링 삼각형이라는 애칭을 가진 IoC/DI, AOP, PSA라고 하는 스프링의 3대 프로그래밍 모델에 대한 이해가 필수입니다.

 

스프링프레임워크를 익히며 3대 모델을 차례대로 소개해보겠습니다.

 

 

1) IoC/DI - 제어의 역전/의존성 주입

 

의존성!? 대체 무엇일까요?

 

의사 코드)

운전자가 자동차를 생산한다.

자동차는 내부적으로 타이어를 생산한다.

 

자바 표현)

new Car( );

Car 객체 생성자에서 new Tire( );

 

의존성 단순 정의)

의존성은 new 다!

new를 실행하는 Car와 Tire 사이에서 Car가 Tire에 의존한다.

 

결론적으로 전체가 부분에 의존한다고 표현할 수 있습니다. 여기서 관계를 구분하는 것은 2가지가 있는데,

1. 집합 관계는 부분이 전체와 다른 생명 주기를 가질수 있습니다 ( 집 vs 냉장고 )

2. 구성 관계는 부분은 전체와 같은 생명 주기를 갖습니다. ( 사람 vs 심장 )

 

클래스 다이어그램

 

이런 식으로 진행할 것이고, eclipse -> Spring Legacy Project로 들어가고 Templates에서 Spring mvc Project 체크!

 

Spring MVC project creating...

 

여기 까지 잘 따라오셨다면 다이어그램 처럼 작성을 하겠습니다!

 

패키지 expert001_01 구성, Car 클래스
Tire 인터페이스, 그리고 구현된 클래스들

 

메인 함수에 찍어보기!

 

예상대로 한국 타이어가 찍힙니다. 만약 아메리카 타이어를 생성한다면 바뀌게 되겠죠!

 

이정도만 코드를 적어보고 말로서 설명드리겠습니다.

 

스프링 없이 의존성을 주입하는 방법 1)

 

생성자 인자를 이용해 주입합시다.

 

public Car(Tire tire) {
     this.tire = tire;
}

 

생성자 선언 후,

 

=>

 

// 익명 클래스 대입

Car car = new Car(new Tire() {

      @Override
      public String getBrand() {
               return "구현해줘요!";
       }
});


또는,

 

// 기존 클래스 대입
Car car = new Car(new KoreaTire());

 

 

이러한 구현 방식의 장점은 유연성입니다. 자동차가 생산될 때 어떤 타이어를 생산해서 장착할까를 자동차가 고민하지 않고, 운전자가 차량을 생산할 때 운전자가 어떤 타이어를 장착할까를 고민하게 하는 것입니다.

 

여기서 사용한 디자인 패턴은 무엇일까요? 바로! 저번에 배운 전략패턴 입니다. 이 처럼 스프링이 자동 적용 되는 것에 디자인 패턴이 숨겨져있는 것입니다!(물론 적용은 안했지만요 ㅠㅠ 구동방식은 같습니다.)  

 

 

스프링 없이 의존성을 주입하는 방법 2)

 

속성을 통한 의존성 주입입니다.

 

Car 클래스에 void setTire(Tire tire){ this.tire = tire; } 코드를 넣을 수 있죠.

 

 

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

 

여기서는 속성을 통한 의존성 주입만을 살펴보겠습니다. Driver 클래스에 변화가 시작됩니다.

 

실행 결과

 

ApplicationConext는 스프링 프레임워크에 대한 정보를 가집니다. 자세한 것은 스프링 구조를 찾아보시면 편합니다!

 

1. new ClassPathXmlApplicationContext의 생성자에서 우리가 적용할 xml 파일을 적습니다.

2. ApplicationContext 인스턴스의 getBean("id value", Class) 를 통해 xml에서 적은 빈(자바 객체)을 갖고옵니다.

 

그럼 xml 파일을 만들어야 하겠죠? 만드는 방법은 아래와 같습니다!!

 

 

Spring Bean Configuration File Select.
xml 파일 bean 설정.

 

이런 식으로 빈은 id와 class 파일 위치를 나타내며 후에 가져올 수 있게 됩니다.

 

 

여기서 중요한 것은 Driver에서 스프링 설정 부분과 xml 파일 추가만 바뀌었을 뿐입니다. 나중에 수많은 클래스 파일 배포할 환경이 된다면 엄청난 효과를 누리게 되는 것이죠.

 

 

또 속성을 XML 파일에서 추가할 수 있는데,

 

tire를 넣지 않는 car 인스턴스의 모습

 

여기서 property를 통해 Car 자바 객체에 ref 키워드를 이용해 AmericaTire를 넣는 모습.

 

 

결과는 예상대로 AmericaTire가 찍힙니다.

 

 

***

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

 

설정자 메서드를 이용하지 않고도 종합쇼핑몰인 스프링 프레임워크가 설정 파일을 통해 설정자 메서드 대신 속성 주입.

@Autowired를 사용하기 위해선 몇 가지 설정이 필요합니다.

 

XML - namespaces에서 context 체크박스 클릭.

 

<context:annotation-config/> 라인 추가

 

여기서 id = tire를 하지않으면 에러가 납니다. 일단 계속 보시죠.

Car class @Autowired

 

이유는 간단한데요. tire를 @Autowired 했는데 XML에서 같은 이름이 없으면 스프링은 자바객체를 찾을 수 없기에 에러를 띄웁니다. ( 주입 에러 )

 

 

번외1)

 

<bean class="expert001_01.KoreaTire"></bean>
<bean id="car" class="expert001_01.Car"></bean>

 

이런식이라면 Driver는 어떻게 될까요? id 값이 없으니 에러가 뜰가요?

 

정답은 KoreaTire가 자동 주입이 됩니다. 그런데 id 값이 없는데 어떻게 매칭이 된걸까요?

 

Car.java         : @Autowired Tire tire;

XML             : <bean class="~.KoreaTire"></bean>

KoreaTire.java : public class KoreaTire implements Tire

 

이것을 보면, 인터페이스의 구현 여부가 답인 것을 알 수 있습니다.

@Autowired의 마법은 바로 type 기준 매칭에 있습니다. 만약 같은 타입을 구현한 클래스가 여러개 있다면 그때 id로 구분해 매칭하는 것이죠.

@Autowired를 통한 속성 매칭 규칙

실험을 더 해보겠습니다.

 

@Autowired Tire tire;

<bean id="kor" class="~.KoreaTire"></bean> => id값이 달라도? 정상 구동

 

 

그렇다면?

 

@Autowired Tire tire;

<bean class="~.KoreaTire"></bean> , <bean class="~.AmericaTire"></bean>

 

둘 다 똑같은 Tire 인터페이스를 구현하며 id로도 구분이 안된다면 어떻게 될까요? 속성 규칙에 의하면,

No unique bean Error가 나타납니다. 무엇을 고를지 모르기 때문에 다시 설정하라는 메세지를 남기죠.

 

 

그러면 id 값을 줘보면 어떨가요?

 

@Autowired Tire tire;

<bean id="tire" class="~.KoreaTire"></bean>, <bean class="~.AmericaTire"></bean>

 

=> 정상 구동

 

 

그럼 id값은 속성의 이름과 일치 하는데 이상한 클래스가 있지만, Tire를 구현한 빈이 공동으로 존재할 때는요?

 

public class Door { }를 만들고,

@Autowired Tire tire;

<bean id="tire" class="~.Door"></bean>, <bean class="~.KoreaTire">

 

=> 정상 구동

 

 

 

 

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

 

쓰임새는 똑같지만 차이가 존재합니다.

 

1. @Autowired는 스프링 프레임워크, @Resource는 표준 자바 출처를 갖습니다.

2. @Autowired의 빈 검색 방식은 byType 못 찾으면 byName, @Resource의 빈 검색 방식은 byName, 못 찾으면 byType

    즉 type을 우선하는 점과 id를 우선하는 점이 다릅니다.

3. 특이사항으론 @Autowired @Qualifier("") 협업, @Resource("name=tire") 약간의 쓰는 차이점이 존재합니다.

 

책에서 추천하는 것은 @Resource를 지지한다고 합니다. 다른 프레임워크로 교체되는 경우를 대비하면 자바 표준인 @Resource를 쓰는 것이 좋죠. 그러면,

 

@Resource vs <bean> 태그의 자식 태그인 <property>

 

누가 더 좋을까요? 프로젝트 규모가 커지면 XML 파일의 규모도 커지며 용도별로 분리가 가능하기에 <property>를 더 추천합니다. 물론 개발하실 때 유지보수성을 더 점수를 친다면 @Resource를 이용하는 것도 나쁘지 않습니다.

 

new 라고 의존 관계를 단순히 말했지만, 사실 변수에 값을 할당하는 모든 곳에 의존 관계가 생깁니다. 즉 대입 연산자에 의해 값이 할당되는 순간에 의존이 생기죠. DI는 외부에 있는 의존 대상을 주입하는 것을 의미합니다. 의존 대상을 구현하고 배치할 때 SOLID와 응집도를 높이고 결합도는 낮추는 기본 원칙에 충실해야 합니다.

 

 

 

 

 

AOP)

 

 

프로그램을 작성하다 보면 이처럼 다수의 모듈에 공통적으로 나타나는 부분이 존재하는데, 바로 이것을 횡단 관심사라고 합니다.

 

코드는 핵심로직(바뀌는)을 담당하는 핵심 관심사와 공통적인 로직을 담당하는 횡단 관심사가 존재합니다.

 

 

과정으로 설명드리겠습니다!!

 

Person interface와 상속받는 Boy Class

 

aop.xml, <aop:aspectj-autoproxy /> 작성

 

aop.xml의 네임스페이스-> aop 체크박스 클릭!

 

그럼 실험할 인터페이스와 클래스, XML 설정을 완료했습니다!

 

AOP Annotation을 이용해 MyAspect Class 작성

 

@Aspect : 이 클래스는 이제 AOP에서 사용하겠다!

@Before : execution 실행을 할건데 public void aop200 패키지 안에 Boy 클래스의 runSomething 메서드가 실행되기 전에 이 어노테이션이 달린 메서드를 실행하겠다!

 

그렇다면? Boy.class.runSomething( )이 실행되기전에 Home in이 찍히겠군요!

 

 

만약 오류가 나신다면.. pom.xml에서 디펜던시를 추가해야 합니다!

 

 

<dependency>

       <groupId>org.aspectj</groupId>

       <artifactId>aspectjrt</artifactId>

        <version>1.9.5</version>

</dependency>

<dependency>

       <groupId>org.aspectj</groupId>

       <artifactId>aspectjweaver</artifactId>

       <version>1.9.5</version>

</dependency>

 

 

 

이제 오류도 끝났으니 결과는?

 

 

예상대로 Boy 클래스의 runSomething이 실행되기 전에 MyAspect - AOP가 관리하는 메서드가 찍힙니다.

이렇듯 횡단 관심사를 분리하는 예제를 보았으니 이론적인 토대를 닦아보겠습니다.

 

AOP를 적용하면 XML 파일, AOP를 넣는 클래스가 추가되며 파일이 늘어나지만, 추가 개발과 유지보수 관점에서 보면 무척이나 관리하기 편하게 된 것을 알 수 있습니다. AOP를 적용하면서 단일 책임 원칙을 자연스럽게 적용한 것도 특징이죠. 그리고 AOP 하나만 개발해놓으면 후발 개발자들은 횡단 관심사에 신경쓰지 않고 핵심 관심사만 코딩하는 용이성까지!!!

 

그런데, Aspect 에서 

 

@Before("execution(public void aop200.Boy.runSomething ())")

이 코드를 보시면 유추할 수 있으다시피 Boy 클래스의 runSomething( )만 호출하겠다는 의미같습니다. 실제로도 맞고요.

 

그럼,

Girl 클래스

 

Boy, Girl 클래스 양쪽에 횡단 관심사를 두고싶다면 어떻게 해야할까요?

aop가 적용안된 Girl Class!

결과 :

 

Girl 클래스도 역시 aop를 적용해 runSomething 메서드 실행전에 Home in을 찍히게 하고싶습니다!

 

바뀐  MyAspect Class

 

바뀐점이 보이시나요? public void ... 다 사라지고 * 하나만 남았습니다.

이 뜻은 모든 클래스의 runSomething이 실행되면 before 메서드를 실행하라는 뜻이죠.

 

결과는?

잘 찍히는 것을 볼 수 있습니다!

 

 

 

aop.xml에서,

 

Boy, Girl, MyAspect 세 개의 빈이 설정돼 있습니다. 빈이 설정되는 이유는 객체의 생성과 의존성 주입(DI)을 스프링 프레임워크에 위임하기 위함입니다. 물론 객체 생성뿐만 아니라 생명주기 전반을 관리하긴 하죠.

그리고 MyAspect는 AOP의 Aspect이니 등록해야하고 AOP 적용 대상 Girl, Boy는 등록해야 되는 것은 알겠는데,

 

<aop:aspectj-autoproxy/>

 

이건 뭘까요?

 

여기서 디자인패턴 프록시를 생각하신다면 관찰의 재능이 있으신겁니다.

 

스프링 AOP는 프록시를 사요합니다. 그런데 스프링 AOP에서 재미있는 것은 호출하는 쪽(Girl, Boy 객체의 메서드호출)에서나 호출당하는 쪽(Girl, Boy 객체) 둘 다 프록시가 존재한다는 것을 모릅니다. 오직 스프링 프레임워크만 프록시의 존재를 아는 것이죠.

 

결국, <aop:aspectj-autoproxy/>는 스프링 프레임워크에게 AOP 프록시를 사용하라고 알려주는 지시자인 것입니다. auto가 붙었으니 자동은 덤이구요.

 

 

그다음 간단히 AOP 용어를 익혀보겠습니다. 이 책은 간단히 설명드리니 자세히 아시고 싶은 분은 스프링 홈페이지를 참고해주세요!! 찡긋<

 

 

 

Pointcut - Aspect 적용위치 시정자)

 

@Before("execution(public void aop200.Boy.runSomething ())") => @Before("execution(* runSomething ())")

 

바뀐부분이 Pointcut 부분입니다. 횡단 관심사를 적용할 타깃 메서드를 선택하는 지시자인 것이죠.

스프링 AOP에서 보자면 Aspect를 메서드에만 적용할 수 있지만 AspectJ처럼 스프링AOP 이전부터 있었고 지금도 유용하게 사용되는 다른 AOP 프레임워크에서는 메서드뿐만 아니라 속성 등에도 Aspect를 적용할 수 있으니,

타깃 메서드 지정자라는 말보다 'Aspect 적용위치 시정자'가 맞는 표현이 됩니다.

 

더 자세한 표현식은 스프링 API 참고 부탁드립니다!

 

 

JoinPoint - 연결가능한 지점)

 

Pointcut은 JoinPoint의 부분 집합입니다. 스프링 AOP는 인터페이스를 기반으로 한다고 설명했는데 Pointcut의 후보가 되는 모든 메서드들이 JoinPoint, 즉 Aspect 적용이 가능한 지점이 됩니다.

 

그래서 JoinPoint란 Aspect 적용이 가능한 모든 지점을 말한다고 결론을 짓습니다. Aspect를 적용할 수 있는 지점 일부가 Pointcut이니 Pointcut은 JoinPoint의 부분 집합이라고 볼 수 있는 것입니다.

 

스프링 AOP에서 JoinPoint란 스프링 프레임워크가 관리하는 빈의 모든 메서드에 해당합니다.

 

MyAspect Class

실체는 그때그때마다 다릅니다. Girl 또는 Boy의 runSomething( )이 될 수 있기 때문입니다.

 

 

 

Advice - 조언? 언제 무엇을!)

 

Advice는 pointcut에 적용할 로직, 즉 메서드를 의미하는데 여기에 언제라는 개념을 더합니다.

결국 Advice란 Pointcut에 언제, 무엇을 적용할지 정의한 메서드죠.

 

@Before 같은 경우 지정된 Pointcut이 실행되기 전 before 메서드를 실행하라고 돼있음을 알수 있죠.

 

 

 

Aspect- 집합체)

 

Advice는 언제, 무엇을 의미하는 것이고, Pointcut은 어디에를 의미하는 것입니다. 결국 Aspect는 언제 무엇, 어디서를 합치는 것이 되죠. MyAspect Class를 해석하자면,

 

Pointcut인 public void aop200.Boy.runSomething( ) 메서드[어디서] Advice인, @Before 실행되기 전[언제] before 메서드를 실행하는[무엇을] 것으로 이해하시면 됩니다.

 

->Advisor라는 말도 있지만 궁금하신 분은 구글링 고고!

 

 

 

그렇다면 XML만으로 AOP가 가능할까요?

 

가능합니다.

 

XML에,

 

<aop:config>
    <aop:aspect ref="myAspect">
        <aop:before method="before" pointcut="execution(* runSomething ())"/>
     </aop:aspect>
</aop:config>

 

이것을 추가하고 기존 MyAspect의 어노테이션을 삭제하더라도 동일한 결과가 나오죠.

 

 

 

 

PSA - 일관성 있는 서비스 추상화)

 

POJO의 마지막 PSA입니다.

 

서비스 추상화의 예로는 JDBC를 들 수 있습니다 . JDBC를 사용하기에 오라클이든 MySQL이든 무엇을 사용하든 Connection 방법과 Statement, ResultSet으로 주거니받거니 공통적으로 적용이 가능합니다. 이는 어댑터패턴을 사용했기 때문이죠. 이렇게 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것을 서비스 추상화라고 합니다.

 

 

그다음은 게시판 구현인데 천천히 해보도록 하겠습니다. 전반적인 이론과 실습은 여기서 끝~

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함