본문 바로가기

JAVA/Spring

[Spring] 1. 오브젝트와 의존관계

해당 글에 사용된 토비의 스프링 3의 그림 및 소스 등에 대한 저작권은 이일민 님께 있습니다. 문제가 발생시 해당 글은 언제든지 내려질 수 있습니다.

해당 글은 이일민 님의 허락하에 작성되었습니다.

덧. 본인의 복습과 정리를 목적으로 작성하였기에 토비의 스프링을 읽지 않으셨다면 잘 안맞는 부분이 있을 수 있습니다.




토비의 스프링 1장은 초난감 DAO라 칭하는 하나의 DAO클래스를 보여주며 시작한다.


왜 초난감 DAO일까?

소스코드를 보면 기능 자체는 잘 작동한다. 하지만 잘 살펴보면 객체지향 설계원칙이 전혀 지켜지지 않았다.

특히나 (본인이 생각하기에) 객체지향 프로그래밍시 가장 먼저 우선되어야 할 '단일 책임의 원칙'이 완전히 깨져 있다.


1장의 내용은 이 초난감 DAO를 리팩토링 하면서 스프링의 중심철학(IoC 와 DI)을 설명하고 있다.

우선 다음을 기억하자.

관심사의 분리

관심이 같은 것기리는 하나의 객체안으로, 관심이 다른 것은 가능한 따로 떨어져서 서로 영향을 주지 않게 분리 하는 것.


관계설정 책임의 분리

특정한 동작을 하기위해선 여러 클래스끼리 관계를 맺어야 한다. 하지만 그런 관계를 맺는 메소드 등이 특정 클래스 안에 들어가 있다면 관계가 맺어진 클래스가 변경될 경우 수많은 클래스를 뜯어고쳐야 하는 일이 발생한다. 그렇기에 이러한 관계설정을 따로 분류한다.


토비의 스프링3를 예로 들자면 1장의 UserDao가 DB 커넥션을 하기위해 DConnectinoMaker와 클래스간 관계를 맺고 있다. 이는 ConnectionMaker 가 변경될경우 UserDao도 코드를 수정해야 하는 상황을 불러온다. 

이 클래스간 관계를 없애고 두 클래스간의 관계는 런타임시에 맺어지도록 하기위해 UserDao의 클라이언트(UserDaoTest)에 관계를 맺어주는 코드를 작성하였다. 즉 두 클래스간의 관계를 설정하는 관계설정 책임이 클라이언트(UserDaoTest)에 부과되었다.


개방페쇄 원칙

클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀있어야 한다.

코드는 변경하지 않으면서도 확장이 가능해야 한다.


높은 응집도와 낮은 결합도

응집도는 하나의 책임만을 담당하고 있는가를 뜻하고 결합도는 '하나의 오브젝트가 변경이 일어날 때 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도' 라 말할 수 있다.

소프웨어 설계에서는 자신의 책임에 대한 응집도는 높고, 다른 객체와 맺은 관계에서는 결합도가 낮아야 한다.


1차적으로 리팩토링이 완료된 초난감 DAO의 다이어그램은 다음과 같다


(ConnectionMaker는 인터페이스)


UserDao에서 데이터베이스 커넥션 생성 책임이분리 되었다.

UserDaoTest란 클라이언트가 커넥션을 생성하여 UserDao에게 제공해 준다.

ConnectionMaker에는 전략 패턴이 적용되어 DConnectionMaker 뿐만 아니라 NConnectionMaker등도 사용 할 수 있다.

UserDao는 ConnectionMaker만 알고 있으면 된다.


언듯 보면 객체지향 원칙들이 잘 적용된 것 같지만 한가지 문제가 남아있다.

UserDaoTest가 테스트 책임 이외에 어떤 ConnectionMaker 구현 클래스를 사용할지 결정하는 책임이 같이 들어가 있다.

여기서 '객체의 생성 방법을 결정하고 그렇게 만들어지는 오브젝트를 돌려주는 팩토리 클래스'를 만들어 해결함으로써 리팩토링을 완료 한다.

이 팩토리 클래스(책의 DaoFactory 클래스)는 사용할 ConnectionMaker를 결정하고 이 커넥션을 사용하는 UserDao를 리턴한다.



이쯤에서 스프링에서 사용되는 몇가지 용어 및 개념에 대해 알아보자

제어의 역전(IoC)

간단히 설명하면 '모든 종류의 작업을 사용하는 쪽에서 제어하는 구조'를 거꾸로 뒤집은 것이다. 

제어의 역전에서는 모든 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않으며, 자신도 어떻게 만들어지고 어디서 사용되는지 알 수 없다. 모든 제어 권한을 자신이 아닌 다른대상에서 위임한다.

이 개념에 대한 자세한 내용은 역시 마틴 파울러의 글을 보는 것이 가장 좋다고 생각한다. 아래 링크는 그에 대한 포스팅이다.

http://vandbt.tistory.com/43

토비의 스프링에 나오는 소스로 이야기 하자면 본래 UserDao 에게 있던 제어권이 DaoFactory에 넘어가있다. DaoFactory가 UserDao가 사용할 오브젝트도 공급해주고, 구현하고 사용할 ConnectionMaker도 선택한다.


빈 bean (호지롷...)

스프링이 IoC 방식으로 관리하는 오브젝트라는 뜻. 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 빈이라고 부른다.


빈 팩토리 bean factory

스프링의 IoC를 담당하는 핵심 컨테이너. 빈의 등록, 생성, 조회 등등... 빈을 관리한다


애플리케이션 컨텍스트 application context

빈 팩토리를 확장한 IoC 컨테이너. 빈 팩토리의 기본적인 기능에 스프링이 제공하는 각종 부가 서비스를 제공한다.

이 용어를 더 많이 사용한다

애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다.




설정정보/설정 메타정보 configuration metadate

애플리케이션 컨텍스트 혹은 빈팩토리가 IoC 적용을 위해 사용하는 메타정보.


싱글톤 빈 스코프

스프링에서 기본적인 빈의 스코프는 싱글톤 스코프를 가진다. 즉 하나의 빈 오브젝트만이 생성되며 계속 재사용 된다는 의미 이다.



의존관계 주입(Dependency Injection)

  위의 블로그 포스팅 링크에서도 잠시 언급되었지만 IoC라는 용어는 굉장히 광범위한 용어이다. 그렇기에 스프링이 제공하는 IoC방식의 핵심을 짚어주는 의존관계 주입(DI)라는 의도가 명확한 이름을 사용하는 것이 좋다고 한다.

  그렇다면 의존관계 주입이란 무엇일까. 토비의 스프링 1장에서 작성된 코드를 보면 UserDao는 DConnectionMaker에서 제공하는 Connection을 사용하여야 한다. 즉 UserDao와 DConnectionMaker는 의존관계에 있다고 볼 수 있는데, 이 둘이 직접적인 의존관계를 맺는 것이 아니라 외부에서 이 둘의 의존관계를 맺어주는 것이다. 또한 UserDao는 ConnectionMaker인터페이스에만 의존관계를 만들어두어 낮은 결합도를 유지하고 있다.

  즉 코드만 보아서는 UserDao와 DConnectionMaker와의 의존관계를 알 수가 없으며, 런타임시 UserDaoFactory가 둘의 의존관계를 주입하여 준다. 이러한 사항을 정리하면 의존관계 주입이란 다음의 세가지 조건을 충종하는 작업이라 할 수 있다.


- 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스에만 의존하고 있어야 한다.

- 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.

- 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.


  UserDao를 보면 setConnectionMaker 라는 메소드가 존재한다. 이 메소드가 바로 의존관계를 주입받기위한 메소드 이다. 보통 set으로 시작하는 수정자 메소드(setter Method)를 이용하는 방식외애 생성자 메소드, 일반 메소드를 이용하여 의존관계를 주입받을 수 있다.


의존관계 검색(Dependency Lookup)

  외부로 부터 주입받는 DI가 아니라 스스로 의존관계를 검색하는 방식이 있다. 이를 의존관계 검색(DL)이라 부른다. 스프링이 사용된 코드를 보면 getBean() 이란 메소드를 볼 수 있다. 이 메소드가 DL을 이용하는 메소드이다. 

  DL은 언제 사용해야 할까? 스프링의 IoC와 DI컨테이너를 적용했다고 하더라도 어플리케이션의 기동 시점에 적어도 한번은 DL을 사용해 오브젝트를 가져와야 한다.  main()등의 메소드에서는 DI를 통해 오브젝트를 주입받을 방법이 없기때문이다.


스프링

  토비의 스프링 1장에서 만든 스프링이 적용되지 않은 코드만 보아도 충분히 DI가 작성되었다. 스프링은 그 코드중 UserDaoFactory에 작성된 정보들을 어노테이션이나 XML을 이용하여 설정정보로 읽어들여서 스프링이 직접 빈 오브젝트를 관리한다. 이때 사용되는것이 빈 팩토리와 어플리케이션 컨텍스트라 불리는 두가지 핵심적인 기능이다.

  스프링은 DaoFactory가 하는 일을 좀더 일반화 시킨것이며, 그 외에 직접 DaoFactory등을 구현해서는 활용하기 어려운 기능들을 쉽게 이용할 수 있게 해준다고 한다. 앞으로의 공부에서 이제 이런 기능들을 배우며 '왜 스프링을 사용하는가' 에 대해 더욱 정확히 알아갈 것이다.




샘플 코드

기존의 UserDao를 스프링의 코드로 변환한 소스는 다음의 깃허브 저장소를 참조하면 된다.

어노테이션, xml, 데이터소스를 이용하는 방법을 다 넣었다. 사용해보고자 하는 부분의 주석코드를 제거 하고 다른 코드를 주석처리 함으로써 각각의 방법으로 실행해 볼 수 있다.


https://github.com/maximinhan/mytobyspring3/tree/master/spring01


덧. DB는 직접 구현하셔야 합니다.



'JAVA > Spring' 카테고리의 다른 글

[spring] 2. 테스트  (0) 2014.02.20
[Spring] 스프링 정리 시작합니다.  (0) 2013.10.17