본문 바로가기

프로그래밍/Spring

전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-sqlMap.xml, context-mapper.xml 변환)

지난 글에서는 DataSource 설정인 ContextDataSource 클래스를 제작하는 방법에 대해 설명하였다. 이번 글에서는 이러한 DataSource를 ibatis와 Mybatis에서 사용하는 환경설정 파일인 context-sqlMap.xml, context-mapper.xml을 Java Config 파일로 바꾸는 작업에 대해 설명하도록 하겠다. 그러나 이 파일들에 대한 변환을 본격적으로 설명하기에 앞서 Spring에서 Resource를 다루는 방법에 대해 먼저 설명을 하도록 하겠다. 왜냐면 오늘 설명하는 Resource 설정하는 방법이 지금까지 설명한 방법과는 다른 방법으로 구현하는지라 이 부분에 대한 설명을 먼저 해야 그 다음 부분을 넘어갈 수 있어서 그렇다.

 

Spring에서는 0개 이상의 Resource를 사용할 수 있다. 극단적으로 Resource 없이 Spring Project를 구성할 수도 있다. 그러나 그렇게 구성할 경우 유지보수 하는 작업이 번거롭기 때문에 대부분 특정 파일을 매개체로 해서 Resource를 구성하게 된다. 이 Resource는 여러 형태가 존재한다. 흔히 자주 사용되는 형태로 properties 파일, XML 파일이 될 수도 있으며, 우리가 Web Project에서 사용하게 되는 바이너리 이미지 파일일수도 있다. 심지어 특정 URL이 될 수도 있다. 이렇듯 Spring Project에서 사용되는 Resource의 형태는 여러 형태를 지니고 있다. 때문에 Spring에서는 이런 여러가지 형태의 Resource를 접근할 수 있게끔 Resource라는 인터페이스를 제공하고 있다.

 

package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

public interface Resource extends InputStreamSource {

    boolean exists();
    boolean isOpen();
    URL getURL() throws IOException;
    File getFile() throws IOException;
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();
    String getDescription();

}

 

이 Resource 인터페이스에서 제공되는 메소드의 기능 설명은 이 글에서는 다루지는 않겠다. 그 내용은 Spring API 문서에 보면 나오고 있으며 영어가 약한 분은 토비의 스프링에서 해당 메소드에 대한 설명이 되어 있다. 요점은 이렇게 여러 형태의 Resource를 Spring에서는 Resource 인터페이스를 구현한 클래스의 객체로 객체화시켜서 이를 각 Spring의 Bean에서 사용한다는 것이다. 그럼 이제 이런 다양한 형태의 Resource를 어떤식으로 Spring에서는 Resource 인터페이스를 구현한 클래스 객체로 만들어주는가에 대해 설명을 하도록 하겠다.

 

위에서 얘기한대로 여러 다양한 형태의 Resource가 존재하는데 이를 Spring에서는 구체화시켜서 사용해야 한다.  이 역할을 하는 것이 Spring에서는 ResourceLoader가 그 역할을 한다.

 

package org.springframework.core.io;

import org.springframework.util.ResourceUtils;

public interface ResourceLoader {

	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
	Resource getResource(String location);
	ClassLoader getClassLoader();
	
}

 

ResourceLoader 인터페이스 소스를 보면 getResource 메소드에 해당 Resource를 접근할 수 있는 경로 문자열을 입력받아 이를 Resource 인터페이스를 구현한 객체로 return 하게 된다. 근데 Resource 인터페이스를 구현한 객체로 만들어야 하는 Spring 입장에서 보면 이게 어떤 경로에서의 Resource인지를 단지 파라미터로 넘겨받는 접근경로 문자열만으로 알아내는데는 한계가 있다. 그래서 Spring에서는 이를 좀더 잘 판단하기 위해 파라미터로 넘겨받는 접근경로 앞에 지정된 접두어를 붙여서 이를 알아내도록 하고 있다. 예를 들어 Local File을 이용해서 Resource 인터페이스 구현 객체로 만들어야 할 경우 접근경로에 file을 접두어로 사용한다(ex: file:/C:/myprj.txt). 만약 Classpath에서 접근이 가능한 파일을 이용해서 만들어야 하는 경우에는 classpath를 접두어로 사용한다(ex: classparth:egovframework/sample/resource/prop.properties). 또 URL 형태이면 직접 그 URL을 입력하면 된다. URL엔 해당 URL이 사용할 프로토콜이 명시되기 때문에 이를 기반으로 만들어가게 된다(ex: http://www.myjob.com/sample/test.jpg) 마지막으로 접두어로 아무것도 명시하지 않으면 ResourceLoader 인터페이스의 구현에 따라 접근하게 된다(ex: WEB-INF/config/myconfig.properties)

ResourceLoader 인터페이스의 구현체로는 대표적인 것이 우리가 흔히 Context라고 말하는 ApplicationContext 인터페이스를 구현한 구현체이다(ApplicationContext 인터페이스는 ResourceLoader 인터페이스를 상속받고 있기 때문에 ApplicationContext 인터페이스를 구현한 우리가 흔히 말하는 Context 클래스들은 이 ResourceLoader 인터페이스가 제공하는 메소드들을 구현해야 한다).

 

지금까지 Resource에 대해 줄구장창 소개했다. 사실 Resource는 우리가 무의식적으로 사용해서 그렇지 실제로는 많이 사용되는 인터페이스다. 왜 그런지는 본격적으로 지금 설명하는 내용을 들어보면 알수 있으리라 생각한다. 이제 Resource에 대한 설명은 이쯤하고 이전글에서 정의했던 DataSource를 이용하는 ibatis 환경 설정파일인 context-sqlMap.xml을 보도록 하자. 이 파일에는 하나의 <bean> 태그만 있다. 이 <bean> 태그를 Java Config  파일로 바꿔주면 된다.

 

<bean id="sqlMapClient" class="egovframework.rte.psl.orm.ibatis.SqlMapClientFactoryBean">
	<property name="configLocation" value="classpath:/egovframework/sqlmap/example/sql-map-config.xml"/>
	<property name="dataSource" ref="dataSource"/>
</bean>

 

태그를 보면 SqlMapClientFactoryBean 클래스 객체를 만드는데 여기에 configLocation 속성에 위에서 잠깐 언급했던 classpath를 통해 접근하는 Resource를 설정하고 dataSource 속성에서는 이전글에서 만든 DataSource bean 객체를 설정하고 있다. 야기서 보면 classpath를 통해 접근하는 Resource를 classpath란 접두어를 붙인 문자열로 설정하고 있다. 이것은 당연한거다. XML이기 때문에 여기서 가능한 방법은 문자열로 지정하는게 없는거니까..

 

<bean> 태그에 대한 설명은 여기까지 하고 그럼 이제 이 내용을 Java Config 방법으로 바꾸는지에 대해 논의해보자. 사실 Spring 4.X부터는 ibatis를 더는 지원하지 않기 때문에 SqlMapClientFactoryBean 클래스도 없어져버렸다(deprecated로 설정되어 있는 상태로 해놓고 Spring에 클래스가 존재하는게 아니라 아예 이 클래스가 없어져 버렸다) 이런 이유로 Spring 4.X 버전의 API javadoc 에서는 이 SqlMapClientFactoryBean 클래스를 찾을 수 없다. 그러나 전자정부 프레임워크에서는 ibatis 연동을 계속 지원하는 관계로 예전의 SqlMapClientFactoryBean 클래스를 egovframework.rte.psl.orm.ibatis package에 넣어서 제공해주고 있다. 이 관계를 알아두고 SqlMapClientFactoryBean 클래스의 configLocation의 setter 메소드 정의를 보자(위에서 얘기했다시피 javadoc으로는 파악할 수 없고 전자정부 프레임워크에서 context-sqlMap.xml 파일을 열은 뒤에 SqlMapClientFactoryBean 클래스에 마우스를 클릭하고 F3 키를 누르면 SqlMapClientFactoryBean 클래스의 Source를 볼 수 있다)

 

private Resource[] configLocations;

public void setConfigLocation(Resource configLocation) {
	this.configLocations = (configLocation != null ? new Resource[] {configLocation} : null);
}

 

설명을 하면 ibatis 설정과 관련된 Resource 타입 객체가 1개 이상 존재할 수가 있기 때문에 이를 저장하기 위해 Resource 타입의 배열로 된 configLocations 변수를 만들고 이 Resource 타입 객체를 1개만 사용해서 환경설정 할 경우엔 Resource 타입 배열을 하나 만드는데 그 안에 사용하고자 하는 Resource 타입 객체 1개만 넣어서 이 배열을 멤버변수 configLocations에 셋팅하는 것으로 구현하는 식이다. SqlMapClientFactoryBean 클래스는 configLocations 변수에 저장된 Resource 객체들을 이용해서 ibatis 연동작업을 진행하는 것이다.

 

자 그럼 여기까지 읽었으면 여기서 난감한 문제가 하나 발생한 것을 눈치챘을것이다. 이전 글에서 DataSource를 Java Config 파일로 변환할때 classpath 문자열이 앞에 붙었던 문자열 설정작업이 기억나실꺼다. Embedded  데이터베이스가 로딩되면서 테이블과 데이터를 넣는 작업을 실행하게끔 지정했던 스크립트 추가작업이 그것이었다. 그때는 String 변수로 classpath 문자열이 앞에 붙었던 문자열을 바로 넣어주는 것으로 해결이 되었다. 근데 지금은 그런 설정방법을 사용할 수가 없다. setConfigLocation 메소드를 보면 알다시피 Resource 인터페이스를 구현한 객체를 사용해야 하는 것이다.

 

그래서 이러한 설정을 할려면 Resource를 나타낼 수 있는 문자열을 Resource 인터페이스를 구현한 클래스 객체를 만들어야 하는 작업이 필요한 것이다. 이러한 역할을 하는데 사용되는 클래스가 Spring에서 제공하는 PathMatchingResourcePatternResolver 클래스이다. 이 클래스의 getResource 메소드에 파라미터로 위에서 설명했던 Resource를 가르키는 문자열을 넣어주어 Resource 인터페이스를 구현한 객체를 return 해준다(개인적으로 PathMatchingResourcePatternResolver의 getResource 메소드 소스를 따라가보면서 분석해보는 것을 추천한다. PathMatchingResourcePatternResolver는 위에서 언급한 ResourceLoader 인터페이스를 구현한 클래스인 DefaultResourceLoader를 가지고 있기 때문에 이를 이용해서 어떻게 Resource 인터페이스 구현 객체를 가져오는지를 알아보는것이 가능하다) 이 클래스를 어떻게 사용하는지는 다음에 보여주는 SqlMapClientFactoryBean 클래스를 Java Config 방식으로 bean 설정하는 코드를 보는 것으로 알 수 있다.

 

@Bean
public SqlMapClientFactoryBean sqlMapClient(DataSource dataSource){
	SqlMapClientFactoryBean smcfb = new SqlMapClientFactoryBean();
	PathMatchingResourcePatternResolver pmrpr = new PathMatchingResourcePatternResolver();
	
	smcfb.setConfigLocation(pmrpr.getResource("classpath:/egovframework/sqlmap/example/sql-map-config.xml"));
	smcfb.setDataSource(dataSource);
	
	return smcfb;
}

 

지금까지 내용을 잘 읽은 사람이면 위의 코드를 알아보는데 문제가 없을 것이다. 위에서 언급했듯이 getResource 메소드에 XML에서 설정할 당시의 configLocation 속성에 넣은 문자열을 넣어주는 것으로 Resource 인터페이스를 구현한 객체를 얻게된다(여기서는 classpath라는 접두어가 붙었기 때문에 실제로는 Resource 인터페이스를 구현한 객체인 ClassPathResource 클래스 객체를 return 한다) 이렇게 ibatis 환경설정 파일을 Resource 인터페이스를 구현한 클래스 객체로 얻어낸뒤 이를 SqlMapClientFactoryBean 클래스 객체에 설정하고 앞에서 bean으로 설정한 DataSource 또한 설정하고 있다.

 

이렇게 해서 ibatis 연동하는 설정인 context-sqlMap.xml을 Java Config 방식으로 설정하는 방법에 대한 설명을 마쳤다. 이 내용을 잘 이해했으면 다음에 설명할 Mybatis 연동하는 설정 파일인 context-mapper.xml을 Java Config 방식으로 설정하는 방법에 대해 이해하기가 어렵지 않으리라 생각한다. 구체적인 설명은 일단 생략하고 XML 설정 파일과 이를 Java Config 방식으로 바꾼 코드를 차례대로 보여주겠다. 비교해보면 이해하는데 별 문제가 없을 것이다.

 

<!-- SqlSession setup for MyBatis Database Layer -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<property name="configLocation" value="classpath:/egovframework/sqlmap/example/sql-mapper-config.xml" />
	<property name="mapperLocations" value="classpath:/egovframework/sqlmap/example/mappers/*.xml" />
</bean>

<!-- MapperConfigurer setup for MyBatis Database Layer with @Mapper("deptMapper") in DeptMapper Interface -->
<bean class="egovframework.rte.psl.dataaccess.mapper.MapperConfigurer">
	<property name="basePackage" value="egovframework.example.sample.service.impl" />
</bean>

 

@Bean
public SqlSessionFactoryBean sqlSession(DataSource dataSource) throws IOException{
	SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
	PathMatchingResourcePatternResolver pmrpr = new PathMatchingResourcePatternResolver();
	sqlSessionFactoryBean.setDataSource(dataSource);
	sqlSessionFactoryBean.setConfigLocation(pmrpr.getResource("classpath:/egovframework/sqlmap/example/sql-mapper-config.xml"));
	sqlSessionFactoryBean.setMapperLocations(pmrpr.getResources("classpath:/egovframework/sqlmap/example/mappers/*.xml"));
	return sqlSessionFactoryBean;
}

@Bean
public MapperConfigurer mapperConfigurer(DataSource dataSource){
	MapperConfigurer mapperConfigurer = new MapperConfigurer();
	mapperConfigurer.setBasePackage("egovframework.example.sample.service.impl");
	return mapperConfigurer;
}

 

configLocation 속성과 setConfigLocation 메소드를 통해 Mybatis 환경설정 XML 파일을 설정하고, mapperLocations 속성과 setMapperLocations 메소드를 통해 Mybatis에서 사용하는 Mapper 파일들의 위치를 설정한다. 

 

이 글의 주제와는 약간 벗어난 내용이긴 한데 약간 곁가지(?)의 내용을 설명한다면 MapperConfigurer 란 클래스 객체 bean을 설정하는 부분이다. 이 클래스는 전자정부 프레임워크에서 제공하는 클래스(egovframework.rte.psl.dataaccess.mapper Package에 있는 클래스이다)로서 내부적으로는 Mybatis에서 제공하는 MapperScannerConfigurer 클래스를 상속받아 구현한 클래스이다. setBasePackage 메소드를 이용해 스캔해야 할 Package를 지정한 뒤 이 Package를 스캔하면서 @Mapper 어노테이션이 붙은 인터페이스를 Mybatis Mapper 파일과 연계하여 Mybatis 기능을 구현하는 것이다. 이러한 설정도 쭉 따라가보면서 분석해보는 것을 추천한다. 이 부분에 대해서는 차후에 조금 더 언급하는 부분이 있을것이다.

 

마지막으로 context-sqlMap.xml 파일을 Java Config 방식으로 변경한 ContextSqlMap 클래스 소스와 context-mapper.xml 파일을 Java Config 방식으로 변경한 ContextMapper 클래스 소스를 보여주면서 이 글을 마무리하겠다.

 

package egovframework.example.config.root;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import egovframework.rte.psl.orm.ibatis.SqlMapClientFactoryBean;

@Configuration
public class ContextSqlMap {

	@Bean
	public SqlMapClientFactoryBean sqlMapClient(DataSource dataSource){
		SqlMapClientFactoryBean smcfb = new SqlMapClientFactoryBean();
		PathMatchingResourcePatternResolver pmrpr = new PathMatchingResourcePatternResolver();
		
		smcfb.setConfigLocation(pmrpr.getResource("classpath:/egovframework/sqlmap/example/sql-map-config.xml"));
		smcfb.setDataSource(dataSource);
		
		return smcfb;
	}
	
}

 

package egovframework.example.config.root;

import java.io.IOException;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import egovframework.rte.psl.dataaccess.mapper.MapperConfigurer;

@Configuration
public class ContextMapper {
	
	@Bean
	public SqlSessionFactoryBean sqlSession(DataSource dataSource) throws IOException{
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		PathMatchingResourcePatternResolver pmrpr = new PathMatchingResourcePatternResolver();
		sqlSessionFactoryBean.setDataSource(dataSource);
		sqlSessionFactoryBean.setConfigLocation(pmrpr.getResource("classpath:/egovframework/sqlmap/example/sql-mapper-config.xml"));
		sqlSessionFactoryBean.setMapperLocations(pmrpr.getResources("classpath:/egovframework/sqlmap/example/mappers/*.xml"));
		return sqlSessionFactoryBean;
	}
	
	@Bean
	public MapperConfigurer mapperConfigurer(DataSource dataSource){
		MapperConfigurer mapperConfigurer = new MapperConfigurer();
		mapperConfigurer.setBasePackage("egovframework.example.sample.service.impl");
		return mapperConfigurer;
	}
}

 

 

목     차

 1. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(환경구축)

 2. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(기초)

 3. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-aspect.xml 변환)

 4. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-common.xml 변환)

 5. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-datasource.xml 변환)

 6. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-sqlMap.xml, context-mapper.xml 변환)

 7. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-transaction.xml 변환)

 8. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(context-idgen.xml, context-properties.xml, context-validator.xml 변환)

 9. 작성된 Java Config 클래스 파일들을 실제 등록해보자(RootContext 클래스 제작)

 10. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(dispatcher-servlet.xml 변환) - 1

 11. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(dispatcher-servlet.xml 변환) - 2

 12. 전자정부 프레임워크를 Java Config 방식으로 설정해보자(dispatcher-servlet.xml 변환) - 3