본문 바로가기

JAVA/Spring

스프링3 XML를 이용한 설정

반응형

XML을 이용한 설정

DaoFactory는 그 DI의 동작원리를 잘 활용한 독립적인 오브젝트 팩토리 클래스로 시작했지만, DI컨테이너인 스프링을 도입하면서부터 애노테이션을 추가해서 DI 갖업에 참고하는 일종의 참고정보로 사용되고 있다.

본격적인 범용 DI컨테이너를 사용하면서 오브젝트 사이의 의존정보는 일일이 자바코드로 만들어주려면 번거롭다.

DaoFactory를 자세히 살펴보면 대부분 틀에 박힌 구조가 반복된다.

또한 DI 구성이 바뀔때마다 자바코드를 수정하고 클래스를 다시 컴파일하는 것도 귀찮은 작업이다.

스프링은 DaoFactory와 같은 자바 클래스를 이용하는 것외에도, 다양한 방법을 통해 DI 의존관계 설정정보를 만들수 있다.

가장대표적인 것이 XML이다.

XML은 단순한 텍스트 파일이기 때문에 다루기 쉽다. 또 쉽게 이해할 수 있으며 컴파일과 같은 별도의 빌드 작업이 없다는 것도 장점이다. 환경이 달라져서 오브젝트의 관계가 바뀌는 경우에도 빠르게 변경사항을 반영할 수 있다.

스키마나 DTD를 이용해서 정해진 포맷을 따라 작성됐는지 손쉽게 확인할 수도 있다.


XML설정

스프링의 애플리케이션 컨텍스트는 XML에 담긴 DI 정보를 활용할 수 있다.

DI정보가 담긴 XML 파일은 <beans>를 루트 엘리먼트로 사용한다.

<beans>안에는 여러개의 <bean>을 정의할 수 있다.

XML설정은 @Configuratio과 @Bean이 붙은 자바 클래스로 만든 설정과 내용이 동일하다.

@Configuration을 <beans>, @Bean을 <bean>에 대응해서 생각하면 이해하기 쉽다.

하나의 @Bean 메서드를 통해 얻을 수 있는 빈의 DI정보

빈의 이름: @Bean 메서드 이름이 빈의 이름이다. 이 이름은 getBean()에서 사용된다.

빈의 클래스: 빈 오브젝트를 어떤 클래스를 이용해서 만들지를 정의한다.

빈의 의존 오브젝트: 빈의 생성자나 수정자 메서드를 통해 의존 오브젝트를 넣어준다.

의존오브젝트도 하나의 빈이므로 이름이 있을 것이고, 그 이름에 해당하는 메서드를 호출해서 의존오브젝트를 가져온다. 의존오브젝트는 하나 이상일 수도 있다.


XML에서 <bean>를 사용해도 이 세가지 정보를 정의할 수 있다.

ConnectionMaker처럼 더 이상 의존하고 있는 오브젝트가 없는 경우에는 세번째 의존 오브젝트 정보는 생략할 수 있다.

XML은 자바코드처럼 유연하기 정의될수 있는 것이 아니므로, 핵심요소를 잘 짚어서 그에 해당하는 태그와 애트리뷰트가 무엇인지 알아야한다.


connectionMaker()전환

DaoFactory의 connectionMaker() 메서드에 해당하는 빈을 XML로 정의해보자.

connectionMaker()로 정의되는 빈은 의존하는 다른 오브젝트는 없으니 DI 정보 세가지 중 두가지만 있으면 된다.


 

자바코드 설정정보

XML 설정정보 

빈 설정파일

@Configuration

<beans>

빈의 이름 

@Bean methodName()

<bean id="methodName">

빈의 클래스

return new BeanClass();

class="a.b.c.. BeanClass">


DaoFactory의 @Bean 메서드에 담긴 정보를 1:1로 XML의 태그와 애트리뷰트로 전환해주기만 하면된다.

단, <bean> 태그의 class 애트리뷰트에 지정하는 것은 자바 메서드에서 오브젝트르 만들 때 사용하는 클래스 이름이라는 점에 주의하자.

메서드의 리턴 타입을 class 애트리뷰트에 사용하지 않도록 하자.

XML에서는 리턴하는 타입을 지정하지 않아도 된다.

class 애트리뷰트에 넣을 클래스 이름은 패키지까지 모두 포함해야한다.

IDE의 자바 에디터에서는 import문을 직접작성하지 않아도 코드 내에서 클래스 이름만 가지고 손쉽게 패키지 정보를 가져올 수 있는데, XML에서는 이를 일일이 적어야한다는 점이 처음에는 번거롭게 느껴질 것이다.

IDE를 사용하면 XML에서 클래스 이름을 작성할 때도 자동완성 기능을 활요할 수 있다.

connectionMaker() 메서드를 <bean> 태그로 전환해보자.

@Bean은 <bean> 태그로, 메서드 이름은 id 애트리뷰트로, new에 사용하는 클래스 이름은 class 애트리뷰트로 대응해서 전환해주면 된다.


@Bean  //<bean

public ConnectionMaker 

connectionMaker() {  //id="connectionMaker"

return new DConnection(); //class="springbook...DConnectionMaker" />

}


XML 설정파일의 <bean>태그를 보면 @Bean 메서드에서 같은 작업이 일어나겠구나라고 떠올릴 수 있으면 좋을것이다.

DI컨테이너는 이 <bean> 태그의 정보를 읽어서 connectionMaker()와 같은 작업을 진행한다.


userDao() 전환

userDao()는 DI 정보의 세가지 요소가 모두 들어있다.

그리고 수정자 메서드를 사용해 의존관계를 주입해주는 부분이 있다.

스프링 개발자가 수정자 메서드를 선호하는 이유 중에는 XML로 의존관계 정보를 만들때 편리하다는 점도 있다.

자바빈의 관계를 따라서 수정자 메서드는 프로퍼티가 된다.

프로퍼티 이름은 메서드 이름에서 set을 제외한 나머지 부분을 사용한다.

setConnectionMaker()라는 이름의 메서드가 있다면 connectionMaker라는 프로퍼리르 갖는다고 할 수 있다.

XML에서는 <property>태그를 사용해 의존 오브젝트와의 관계를 정의한다.

<property> 태그는 name과 ref라는 두개의 애트리뷰트를 갖는다.

name은 프로퍼티의 이름으로 수정자 메서드를 알 수 있다.

ref는 수정자 메서드를 통해 주입해줄 오브젝트의 빈 이름이다.

DI할 오브젝트도 역시 빈이다. 그 빈의 이름을 지정해주면된다.

@Bean 메서드에서라면 다른 @Bean 메서드를 호출해서 주입할 오브젝트를 가져온다.


userDao.setConnectionMaker(connectionMaker());


userDao.setConnectionMaker()는 userDao 빈의 connectionMaker 프로퍼티를 이용해 의존관계 정보를 주입한다는 뜻이다.

메서드의 파리미터로 넣는 connectionMaker()는 connectionMaker() 메서드를 호출해서 리턴하는 오브젝트를 주입하라는 의미다.

이 두가지 정보를 <property>의 name 애트리뷰트와 ref 애트리뷰트로 지정해주면된다.

각 정보를 <property> 태그에 대응하여 전환이 가능하다.


userDao.setConnectionMaker(connectionMaker());

<property id="connectionMaker" ref="connectionMaker" />


마지막으로 이 <property>태그를 userDao빈을 정의한 <bean>태그안에 넣어주면된다


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="connectionMaker" />

</bean>


XML의 의존관계 주입 정보

두 개의 <bean>태그를 이용해 @Bean 메서드를 모두 XML로 변환했다.

<beans>로 전환한 두 개의 <bean> 태그를 감싸주면 DaoFactory로부터 XML로의 전환작업이 끝난다.


<beans>

<bean id="connectionMaker" class="springbook.user.dao.DConnectionMaker" />


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="connectionMaker" />

</bean>

</beans>


<property> 태그의 name과 ref는 그 의미가 다르므로 이름이 같더라도 어떤 차이가 있는지 구별할 수 있어야 한다.

name 애트리뷰트는 DI에 사용할 수정자 메서드의 프로퍼티 이름이며, ref 애트리뷰트는 주입할 오브젝트를 정의한 빈의 DI다.

보통 프로퍼티이름과 DI되는 빈의 이름이 같은경우가 많다.

프로퍼티 이름은 주입할 빈 오브젝트의 인터페이스를 따르는 경우가 많고, 빈 이름도 역시 인터페이스 이름을 사용하는 경우가 많기 때문이다. 바뀔수 있는 클래스 이름보다는 대표적인 인터페이스 이름을 따르는 편이 자연스럽다.

하지만 프러퍼티 이름과 빈의 이름은 인터페이스 이름과 다르게 정해도 상관없다.

의미를 좀 더 잘 드러낼 수 있은 이름이 있다거나, 같은 이름이 중복되는 상황이라면 적절한 다름 이름을 부여해주자.

빈의 이름을 바꾸는 경우 그 이름을 참조하는 다른 빈의 <property> ref 애트리뷰트의 값도 함께 변경해줘야한다.

예) connectionMaker 빈을 myConnectionMaker이라는 이름으로 변경햇다면 userDao빈의 connectionMaker 프로퍼티 ref 값도 따라서 변경해줘야 한다.

connectionMaker 빈을 DI하는 DAO가 여러개 있다면 모두 변경해야한다.

이런경우 자바 코드로 된 설정이라면 IDE의 이름변경rename 리팩토링 기능을 사용하면 간단하다.

하지만 XML은 텍스트 파일을 수정하는 것이므로 텍스트 치환text replace 기능을 사용해야 하는데, 잘못하면 엉뚱한 것까지 수정할 수 있으니 조심해야한다.

가능하면 처음부터 이름을 잘 정하고 수정하지 않는 편이 좋다.


<beans>

<bean id="myConnectionMaker" class="springbook.user.dao.DConnectionMaker" />


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="myConnectionMaker" />

</bean>

</beans>


때로는 같은 인터페이스를 구현한 의존 오브젝트를 여러개 정의해두고 그 중에서 원하는 걸 골라서 DI하는 경우도 있다.

이때는 각 빈의 이름을 독립적으로 만들어두고 ref 애트리뷰트를 이용해 DI받을 빈을 지정해주면된다.


<beans>

<bean id="localDBConnectionMaker" class="springbook.user.dao.localDBConnectionMaker" />

<bean id="testDBConnectionMaker" class="springbook.user.dao.testDBConnectionMaker" />

<bean id="productDBConnectionMaker" class="springbook.user.dao.productDBConnectionMaker" />


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="localDBConnectionMaker" />

</bean>

</beans>


DTD와 스키마


XML을 이용하는 애플리케이션 컨텍스트

이제 애플리케이션 컨텍스타가 DaoFactory 대신 XML 설정정보를 활요하도록 만들어보자.

XML에서 빈의 의존관계 정보를 이용하는 IoC/DI 작업에는 GenericXmlApplicationContext를 사용한다.

GenericXmlApplicationContext의 생성자 파라미터로 XML파일의 클래스 패스를 지정해주면된다.

XML 설정파일은 클래스패스 최상단에 두면 편하다.

애플리케이션 컨텍스트가 사용하는 XML 설정파일의 이름은 관례를 따라 applicationContext.xml이라고 만든다.

DaoFactory의 DI 정보를 전환해서 만든, <beans>로 시작하는 XML에 스프링 스키마까지 적용해서 applicationContext.xml를 만든다.


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http//www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0xsd">

<bean id="myConnectionMaker" class="springbook.user.dao.DConnectionMaker" />


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="myConnectionMaker" />

</bean>

</beans>


UserDaoTest의 애플리케이션 컨텍스트 생성부분을 수정한다.

DaoFactory를 설정정보로 사용햇을 때 썼던 AnnotationConfigApplicationContext 대신 GenericXmlApplicationContext를 이용해 애플리케이션 컨텍스트를 생성하게 만든다.

생성자에는 applicationContext.xml의 클래스패스를 넣는다.

클래스패스를 시작하는 /는 넣을수도 있고 생략할수도 있다.

시작하는 /가 없는 경우에도 항상 루트에서부터 시작하는 클래스패스라는 점을 기억하자.


ApplicationContext context = new GenericXmlApplicationContext("applicatonContext.xml");


GenericXmlApplicationContext 외에도 ClassPathXmlApplicationContext를 이용해 XMl로부터 설정정보를 가져오는 애플리케이션 컨텍스틀 만들수도 있다.

GenericXmlApplicationContext는 클래스 패스뿐아니라 다양한 소스로부터 설정파일을 읽어올수 있다.

ClassPathXmlApplicationContext는 XML파일을 클래스패스에서 가져올 때 사용할 수 있는 편리한 기능이 추가된것이다.

ClassPathXmlApplicationContext의 기능중에는 클래스패스의 경로 정보를 클래스에서 가져오게 하는 것이 있다.

예) springbook.user.dao 패키기잔에 daoContext.xml이라는 설정파일을 만들었다고 하자.

GenericXmlApplicationContext가 이 XML설정파일 사용하게 하려면 클래스패스루트로부터 파일의 위치를 저정해야 하므로 패키지가 길어지면 클래스패스를 모두 적어야 한다.


new GenericXmlApplicationContext("springbook/user/dao/daoContext.xml");


반면에 ClassPathXmlApplicationContxt는 XML파일과 가은 클래스패스에 있는 클래스 오브젝트를 넘겨서 클래스패스에 대한 힌트를 제공할 수 있다.

UserDao는 springbook.user.dao 패키지에 있으므로 daoContext.xml과 같은 클래스패스 위에 있다.

이 UserDao를 함께 넣어주면 XML파일의 위치를 UserDao의 위치로부터 상대적으로 지정할 수 있다.


new ClassPathXmlApplicationContext("daoContext.xml", UserDao.class);


이방법으로 클래스패스를 지정해야할 경우가 아니라면 GenericXmlApplicationContext를 사용하는 편이 무난한다.


DataSoure 인터페이스로 변환

DataSource 인터페이스 적용

ConnectionMaker는 DB 커넥션을 생성해주는 기능 하나만을 정의한 매우 단순한 인터페이스다.

IoC와 DI의 개념을 설명하기 위해 직접 이 인터페이스를 정의하고 사용했지만, 사실 자바에서는 DB 커넥션을 가져오는 오브젝트의 기능을 추상화해서 비슷한 용도로 사용할 수 있게 만들어진 DataSource라는 인터페이스가 이미 존재한다.

따라서 실전에서 ConnectionMaker와 같은 인터페이스를 만들어서 사용할 일은 없을 것이다.

단, DataSource는 getConnection()이라는 DB 커넥션을 가져오는 기능 외에도 여러 개의 메서드를 갖고 있어서 인터페이스를 직접 구현하기는 부답스럽다.

일반적으로 DataSource를 구현해서 DB 커넥션을 제공하는 클래스를 만들 일은 거의 없다.

이미 다양한 방법으로 DB 연결과 풀링(pooling)기능을 갖춘 많은 DataSource구현 클래스가 존재하고, 이를 가져다 사용하면 충분하기 때문이다.

대부분의 DataSource구현 클래스는 DB 종류나 아이디, 비밀번호 정도 DataSource 구현 클래스를 다시 만들지 않고도 지정할 수 있는 방법을 제공한다.

DataSource인터페이스에서 실제로 관심을 가질 것은 getConnection() 메서드 하나뿐이다.

이름만 다르지 ConnectionMaker 인터페이스의 makeConnection()과 목적이 동일한 메서드다.

DAO에서는 DataSource의 getConnection() 메서드를 사용해 DB 커넥션을 가져오면 된다.


package javax.sql


public interface DataSource extends CommonDataSource, Wrapper {

Connection getConnection() throws SQLException;

}


DataSource인터페이스와 다양한 DataSource 구현 클래스를 사용할 수 있도록 UserDao를 리팩토링해보자.

UserDao에 주입될 의존 오브젝트의 타입을 ConnectionMaker에서 DataSource로 변경한다.

그리고 DB 커넥션을 가져오는 코드를 makeConnection()에서 getConnection() 메서드로 바꾼다.


import javax.sql.DataSource;


public class UserDao {

private DataSource dataSource;


public void setDataSourece(DataSource dataSource) {

this.dataSource = dataSource;

}


public void add(User user) throws SQLException {

Connection c = dataSource.getConnection();

}

}


DataSource의 getConnection()은 SQLException만 던지기 때문에 makeConnection() 메서드의 throws에 선언했던 ClassNotFoundException은 제거해도 된다.


다음은 DataSource 구현클래스가 필요하다.

앞에서 만들었던 DriverManager를 사용하는 SimpleConnectionMaker 처럼 단순한 DataSource 구현클래스를 하나 가져다 사용하자.

스프링이 제공해주는 DataSource 구현클래스 중에 테스트환경에서 간단히 사용할 수 있는 SimpleDriverDataSource 이라는 것이 있다.

이 클래스를 사용하도록 DI를 재구성할 것이다.

SimpleDriverDataSource는 DB 연결에 필요한 필수 정보를 제공받을 수 있도록 여러 개의 수정자 메서드를 갖고 있다.

예) JDBC 드라이버 클래스, JDBC URL, 아이디, 비밀번호 등이다.


추가할 라이브러리

org.springframework.jdbc-3.0.3.RELEASE.jar


자바코드 설정방식

DaoFactory설정방식

기존의 connectionMaker() 메서드를 dataSource()로 변경하고  SimpleDriverDataSource의 오브젝트를 리턴하게 한다.

이 오브젝트를 넘기기 전에 DB연결과 관련된 정보를 수정자 메서드를 이용해서 지정해줘야 한다.


SimpleDriverDataSource를 사용하도록 만든 DaoFactory의 dataSource() 메서드

@Bean

public DataSource dataSource() {

SimpleDriverDataSource dataSource = new SimpleDriverDataSource();


datasource.setDriverClass(com.mysql.jdbc.Driver.class);

datasource.setUrl("jdbc:mysql://localhost/springbook");

datasource.setUsername("spring");

datasource.setPassword("book");

//DB연결정보를 수정자 메서드를 통해 넣어준다.

// 이렇게 하면 오브젝트 레벨에서 DB 연결 방식을 변경할 수 있다.


retrun dataSource;

}


DaoFactory의 userDao() 메서드 수정

userDao는 이제 DataSource 타입의 dataSource()를 DI받는다.


@Bean

public UserDao userDao() {

UserDao userDao = new UserDao();

userDao.setDataSource(dataSource());

return userDao;

}


이렇게 해서 UserDao에 DataSource 인터페이스를 적용하고 SimpleDriverDataSource의 오브젝트를 DI로 주입해서 사용할 수 있는 준비가 끝났다.

UserDaoTest를 DaoFactory를 사용하도록 수정하고 테스트를 실행한다.


XML설정방식

먼저 id가 connectionMaker인 <bean>을 없애고 dataSource이라는 이름의 <bean>을 등록한다.

그리고 클래스를 SimpleDriverDataSource로 변경해준다.


<bean id="dataSource" class="org.srpingframework.jdbc.datasource.SimpleDriverDataSource" />


그런데 문제는 이 <bean>설정으로 SimpleDriverDataSource의 오브젝트를 만드는 것까지는 가능하지만, dataSource() 메서드에서 SimpleDriverDataSource 오브젝트의 수정자로 넣어준 DB 접속정보는 나타나 있지 않다는 점이다.

UserDao처럼 다른 빈에 의존하는 경우에는 <property> 태그와 ref  애트리뷰트로 의존할 빈 이름을 넣어주면된다.

하지만 SimpleDriverDataSource 오브젝트의 경우는 단순 Class 타입의 오브젝트나 텍스트 값이다.

dataSource() 메서드에서 처럼 DB 연결 정보를 넣기위해서는 프로퍼티 값의 주입을 하면된다.


프로퍼티 값의 주입

값 주입

이미 DaoFactory의 dataSource() 메서드에서 본 것처럼, 수정자 메서드에는 다른 빈이나 오브젝트 뿐만아니라 스트링 같은 단순값을 넣어줄수도 있다.

setDriverClass() 메서드의 경우에는 Class 타입의 오브젝트이긴 하지만 다른 빈 오브젝트를 DI 방식으로 가져와 넣는 것은 아니다.

이렇게 다른 빈의 오브젝트 레퍼런스가 아닌 단순 정보도 오브젝트를 초기화하는 과정에서 수정자 메서드에 넣을 수 있다.

이때 DI에서 처럼 오브젝트의 구현 클래스를 다이나믹하게 바꿀 수 있게 해주는 것이 목적은 아니다. 대신 클래스 외부에서 DB 연결정보와 같이 변경 가능한 정보를 설정해줄 수 있도록 만들기 위해서다.

예) DB 접속아이디가 바뀌었더라도 클래스 코드는 수정해줄 필요가 없게 해주는것이다.

텍스트나 단순 오브젝트 등을 수정자 메서드에 넣어주는 것을 스프링에서는 '값을 주입한다'라고 말한다. 이것도 성격은 다르지만 일종의 DI라고 볼수 있다.

사용할 오브젝트 자체를 바꾸지는 않지만 오브젝트의 특성은 외부에서 변경할 수 있기 때문이다.

스프링의 빈으로 등록될 클래스에 수정자 메서드가 정의되어 있다면 <property>를 사용해 주입할 정보를 지정할 수 있다는 점에서 <property ref="">와 동일하다.

하지만 다른 빈의 오브젝트의 레퍼런스가 아니라 단순 값을 주입해주는 것이기 때문에 ref 애트리뷰트 대신 value 애트리뷰트를 사용한다.

따라서 dataSource() 메서드에서 수정자 메서드를 호출해서 DB 연결정보를 XML로 전환할 수 있다.


<property name="driverClass" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost/springbook" />

<property name="username" value="spring" />

<property name="password" value="book" />


ref 대신 value를 사용했을 뿐이지 기존의 <property> 태그를 사용했던 것과 내용과 방법은 동일하다.

value 애트리뷰트에 들어가는 것은 다른 빈의 이름이 아니라 실제로 수정자 메서드의 파라미터로 전달되는 스프링 그 자체다.


value 값의 자동변환

그런데 url, username, password는 모두 스트링 타입이니 월래 텍스트로 정의되는 value 애트리뷰트의 값을 사용한다.

그런데 driverClass는 스트링타입이 아니라 java.lang.Class 타입이다.

DaoFactory에 적용한 예로 Driver.class라는 Class 타입 오브젝트를 전달한다.

그런데 XML에서는 별다른 타입정보 없이 클래스의 이름이 텍스트 형태로 value에 들어가 있다.

그렇다면 어떻게 "com.mysql.jdbc.Driver"라는 스트링 값이 Class 타입의 파라미터를 갖는 수정자 메서드에 사용될수 있는 것일까?

스트링 값을 Class타입 변수에 넣을 수는 없다.

Class driverClass = "com.mysql.jdbc.Driver";

컴파일조차 안될 것이다.

이런 설정이 가능한 이유는 스프링이 프로퍼티의 값을 수정자 메서드의 파리미터 타입을 참고해서 적절한 형태로 변환해주기 때문이다.

setDriverClass() 메서드의 파라미터 타입이 Class임을 확인하고 "com.mysql.jdbc.Driver"라는 텍스트 값을 com.mysql.jdbc.Driver.class 오브젝트로 자동 변경해주는 것이다.

내부적으로 변환작업이 일어난다고 생각하면된다.


Class driverClass = Class.forName("com.mysql.jdbc.Driver");

dataSource.setDriverClass(driverClass);


스프링은 value에 지정한 텍스트 값을 적절한 자바 타입으로 변환해준다.

Integer, Double, String, Boolean 같은 기본 타입은 물론이고, Class, URL, File, Charset 같은 오브젝트로 변환할 수도 있다.

또한 값이 여러개 라면 List, Map, Properties나 배열 타입으로도 값의 주입이 가능하다.

이렇게 해서 DataSource 인터페이스로의 전환 작업을 모두 마쳤다.


XML을 이용하는 애플리케이션 컨텍스트를 만들어 사용하도록 UserDaoTest를 수정하고 실행해보자.

이제 UserDao는 XML 설정파일을 사용해 스프링 컨테이너 위에서 동작하는 좀 더 깔끔한 코드가 되었다.


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http//www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0xsd">


<bean id="dataSource" class="org.srpingframework.jdbc.datasource.SimpleDriverDataSource">

<property name="driverClass" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost/springbook" />

<property name="username" value="spring" />

<property name="password" value="book" />

</bean>


<bean id="userDao" class="springbook.user.dao.UserDao">

<property id="connectionMaker" ref="dataSource" />

</bean>


</beans>



정리

  • 먼저 책임이 다른 코드를 분리해서 두 개의 클래스로 만들었다.(관심사의 분리, 리팩토링)
  • 그중에서 바뀔수 있는 쪽의 클래스는 인터페이스를 구현하도록 하고, 다른 클래스에서 인터페이스를 통해서만 접근하도록 만들었다. 이렇게 해서 인터페이스를 정의한 쪽의 구현방법이 달라져 클래스가 바뀌더라도, 그 기능을 사용하는 클래스의 코드는 같이 수정할 필요가 없도록 만들었다(전략패턴)
  • 이를 통해 자신의 책임 자체가 변경되는 경우 외에는 불필요한 변화가 발생하지 않도록 막아주고, 자신이 사용하는 외부 오브젝트의 기능은 자유롭게 확장하거나 변경할 수 있게 만들었다.(개방폐쇄원칙)
  • 결국 한쪽의 기능 변화가 다른쪽의 변경을 요구하지 않아도 되게했고(낮은 결합도), 자신의 책임과 관심사에만 순수하게 집중하는(높은 응집도) 깔끔한 코드를 만들 수 있었다.
  • 오브젝트가 생성되고 여타 오브젝트의 관계를 맺는 작업의 제어권을 별도의 오브젝트 팩토리를 만들어 넘겼다. 또는 오브젝트 팩토리의 기능을 일반화한 IoC 컨테이너로 넘겨서 오브젝트가 자신이 사용할 대상의 생성이나 선택에 관한 책임으로부터 자유롭게 만들어줬다(제어의 역전 IoC)
  • 전통적인 싱글톤패턴 구현방식의 단점을 살펴보고, 서버에서 사용되는 서비스 오브젝트로서의 장점을 살릴 수 있는 싱글톤을 사용하면서도 싱글톤 패턴의 단점을 극복할 수 있도록 설계된 컨테이너를 활용하는 방법에 대해 알아봤다(싱글톤 레지스트리)
  • 설계시점과 코드에는 클래스와 인터페이스 사이의 느스한 의존관계만 만들어놓고, 런타임시에 실제 사용할 구체적인 의존오브젝트를 제 3자(DI 컨테이너)의 도움으로 주입받아서 다이나믹한 의존관계를 갖게 해주는 IoC의 특별한 케이스를 알아봤다(의존관계 주입/DI)
  • 의존오브젝트를 주입할 때 생성자를 이용하는 방법과 수정자 멧더를 이용하는 방법을 알아봤다(생성자 주입과 수정자 주입)
  • 마지막으로 XML을 이용해 DI설정정보를 만드는 방법과 의존오브젝트가 아닌 일반 값을 외부에서  설정해서 런타임 시에 주입하는 방법을 알아봤다(XML설정)


스프링이란 '어떻게 오브젝트가 설계되고, 만들어지고, 어떻게 관계를 맺고 사용되는지에 관심을 갖는 프레임워크'라는 사실을 꼭 기억하자.

스프링의 관심은 오브젝트와 그 관계다.

하지만 오브젝트를 어떻게 설계하고, 분리하고, 개선하고, 어떤 의존관계를 가질지 결정하는 일은 스프링이 아니라 개발자의 역할이며 책임이다.

스프링은 단지 원칙을 잘 따르는 설계를 적용하려고 할때 필연적으로 등장하는 번거로운 작업을 편하게 할 수 있도록 도와주는 도구 일뿐임을 잊지말자.

스프링을 사용한다고 좋은 객체지향 설계와 깔끔하고 유연한 코드가 저절로 만들어질까?

그건 절대 아니다. 그부분은 객체지향 설계와 프로그래밍에 대한 학습과 훈련, 경험이 필요한 부분이며, 각자가 공부해야 할 책임이 있는 과제다.

다만, 스프링은 그런 좋은 설계와 코드를 적용하고자 할대 좋은 동반자가 돼줄것이다.

반응형