<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Toby's Epril</title>
	
	<link>http://toby.epril.com</link>
	<description>떠돌이 개발자 토비의 대충 쓰는 블로그</description>
	<lastBuildDate>Thu, 09 Feb 2012 22:26:42 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/tobyepril" /><feedburner:info uri="tobyepril" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>스프링 3.1 (4) Static @Bean 메소드</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/a9JQoLNcMl0/</link>
		<comments>http://toby.epril.com/?p=1177#comments</comments>
		<pubDate>Thu, 09 Feb 2012 22:26:42 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring 3.1]]></category>
		<category><![CDATA[@Bean]]></category>
		<category><![CDATA[spring 3.1]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1177</guid>
		<description><![CDATA[오래전 Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유라는 글을 쓴 적이있다. 나름 흥미로운 분석을 통해서 정리한 재밌는 글이라고 생각했는데 별 반응이 없었다. 굳이 스프링 내에서 어떤 일이 일어나는지, BeanFactoryPostProcessor(BFPP)의 동작원리가 뭔지 이해해야 필요를 못 느껴서였을 수도 있고 PPC를 XML에서 사용하면 그만이지, 당시엔 활용도가 낮았던 @Configuration 클래스 내에서 @Bean으로 정의해서 @Value로 값을 받는데 사용할 필요가 <a href='http://toby.epril.com/?p=1177'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1177"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1177&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>오래전 <a href="http://toby.epril.com/?p=964">Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유</a>라는 글을 쓴 적이있다. 나름 흥미로운 분석을 통해서 정리한 재밌는 글이라고 생각했는데 별 반응이 없었다. 굳이 스프링 내에서 어떤 일이 일어나는지, BeanFactoryPostProcessor(BFPP)의 동작원리가 뭔지 이해해야 필요를 못 느껴서였을 수도 있고 PPC를 XML에서 사용하면 그만이지, 당시엔 활용도가 낮았던 @Configuration 클래스 내에서 @Bean으로 정의해서 @Value로 값을 받는데 사용할 필요가 없었기 때문일 수도 있다. </p>
<p>그런데 이제는 No XML이라는 슬로건을 걸친 스프링 3.1이 나와서 자바 코드만으로 XML의 모든 설정을 대체할 수 있을 뿐만 아니라 XML보다 더욱 편리하게 사용할 수 있다는 점을 강조하고 있다. 물론 스프링 3.1이라고 XML을 사용하면 안되는 것은 아니며 @Configuration을 사용할 의무도 없다. 하지만 3.0에서 자바 콘픽을 도입한 이후로 이를 충분히 성숙한 레벨로 올려서 실전에서 본격적으로 사용할 수 있도록 만든 것이 스프링 3.1 변화의 핵심이니 3.1을 사용한다면 한번쯤 XML 없는 스프링 설정에 도전해볼만 하다.</p>
<p>스프링 3.1에서 새롭게 소개된 자바코드 설정용 각종 애노테이션과 기법들은 천천히 얘기하기로 하고(실은 지금 그 내용을 담은 토비의 스프링 3 개정판을 열심히 준비하고 있다) 오늘은 BFPP를 @Bean으로 사용할 수 없었던 문제만 다시 짚어보고 스프링 3.1은 이를 어떻게 해결했는지(해결하기는 했는지?) 살펴보자.</p>
<p>PPC를 @Bean의 정의하면 기대한대로 동작하지 않는 이유에 대해서는 이전 글에서 자세히 설명했다. @Configuration 클래스를 이용한 빈 설정을 가능하게 하는 것도 BFPP이며 프로퍼티 파일의 내용을 가져와 ${..}으로 지정된, 빈 프로퍼티 내의 치환자에 값을 넣어주는 PropertyPlaceholderConfigurer도 BFPP이다. 문제는 @Bean으로 정의된 빈은 @Configuration을 담당하는 ConfigurationClassPostProcessor(CCPP)에 의해서 빈 인스턴스가 만들어지는데, 그렇게 만들어진 BFPP 빈은 이미 빈 인스턴스가 만들어져 사용된 @Configuration 빈의 @Value 프로퍼티 값을 바꿀 수 없기 때문이다.</p>
<p>이 이야기가 어려운 듯 보이지만 사실.. 어렵지 않아요.</p>
<p>BFPP와 BPP(BeanPostProcessor)의 차이점만 이해하면 쉽게 이해할 수 있다. BFPP는 빈의 메타 데이터(클래스는 뭐고 프로퍼티 값의 정의는 뭐고 등등)를 조작하는 후처리기고, BPP는 빈의 인스턴스 값을 조작하는 후처리기다. PPC는 BFPP이므로 메타 데이터만 조작할 수 있다. 하지만 PPC가 @Bean에 의해서 만들어지는 시점에 이미 @Configuration 클래스로 정의된 빈은 메타 데이터 단계가 아니라 인스턴스까지 만들어진 상태다. @Bean 메소드를 실행해서 PPC빈을 만들어야 하니 당연히 이미 @Configuration 인스턴스가 필요할 수 밖에. 결국 PPC가 아무리 메타 데이터에 있는 ${..}을 찾아서 봐꿔봐야. 이미 인스턴스 생성이 끝난 @Configuration 빈의 @Value가 붙은 프로퍼티의 값은 변경할 수가 없다. 빈을 다시 만들지 않을테니까. new XXX() 해서 인스턴스를 만들고 나서 XXX.class 파일을 아무리 조작해봐야 이미 만들어진 인스턴스는 변하지 않는 것과 마찬가지라고 보면 된다. </p>
<p>그렇다면 이를 피할 방법은 뭐가 있을까? </p>
<p>방법은 간단하다. BFPP가 만들어진 후에 @Value가 붙은 @Configuration 클래스가 빈으로 만들어지도록 하면 된다.</p>
<p>가장 쉬운 방법은 BFPP를 @Configuration내의 @Bean이 아닌 XML에 정의하고 이를 @ImportResource로 가져오는 것이다. XML에는 다음과 같이 PPC가 생성되도록 빈을 정의한다. 친절한 스프링 덕에 커스톰 태그 하나면 충분하다.</p>
<p>&lt;context:property-placeholder location=&quot;message.properties&quot; /&gt;</p>
<p>그리고 이 XML을 @Configuration 클래스에서 @ImportResource로 불러온다.</p>
<p>@Configuration   <br /><strong>@ImportResource(&quot;context.xml&quot;)     <br /></strong>public class Config {    <br />&#160;&#160;&#160; <strong>@Value(&quot;${name}&quot;) </strong>String name;</p>
<p>이렇게 해두면 친절한 스프링은 Config 빈을 만들기 전에 XML의 빈들을 먼저 만들고 이때 만들어진 PPC는 Config 빈의 메타 정보에 적용이되서 ${name}을 이용해서 name 프로퍼티 값이 설정이 된다.</p>
<p>또는, 이전 글에서 예로 보였던 것처럼 @Value가 붙은 @Configuration 클래스가 아닌 다른 빈 클래스를 정의해서 거기서 PPC가 만들어지게 하면 된다. PPC를 확장해서 @Component로 정의하는 것도 되고, 또 다른 @Configuration 클래스를 만들어서 그 안에 PPC를 만드는 @Bean을 넣어도 좋다.</p>
<p>@Configuration의 @Value에 ${..}를 적용할 수 있는 방법은 이 두가지 정도.</p>
<p>여기까지는 스프링 3.0의 경우고. 스프링 3.1의 새로운 방법을 살펴보자.</p>
<p>스프링 3.1에선 더이상 PPC를 쓰지 않는다. PropertySource라는 프로퍼티 값을 다루는 새로운 추상화 방식이 등장하면서 이전에 사용되던 다양한 접근방법들을 하나로 통합해버렸다.&#160; 이에 대한 자세한 설명은 나중에.</p>
<p>그래서 스프링 3.1에선 PPC 대신 PropertySourcePlaceholderConfigurer(PSPC)가 사용된다. &lt;context:property-placeholder /&gt;에서도 PPC 대신 PSPC가 만들어진다. 이 두가지는 사실 차이점이 제법 있는데 일단 제껴두고 BFPP관점에서만 살펴보자.</p>
<p>PSPC도 여전히 BFPP다. 따라서 3.0에서와 마찬가지로 @Value에 ${..}을 적용하기 위해서는 같은 @Configuration 클래스에서 @Bean으로 PSPC를 정의하면 안된다. 물론 앞에서 소개한 두가지 방법을 이용해서 이를 피할 수 있다. 하지만 스프링 3.1은 이 문제를 보다 세련된 방식으로 해결해버렸다. 그래서 이제는 @Value와 PSPC @Bean 메소드가 같은 클래스에 정의해도 된다.</p>
<p>단, <strong>이 PSPC @Bean은 다음과 같이 스태틱 메소드여야 한다.</strong></p>
<p>@Bean <strong>static </strong>PropertySourcesPlaceholderConfigurer placeholderConfigurer() {    <br />&#160;&#160;&#160; return new PropertySourcesPlaceholderConfigurer();    <br />}</p>
<p>3.0에선 @Bean 메소드는 final 또는 private이어도 안되고, static 이어도 안됐다. 앞에 두개는 CGLib으로 확장할 때 문제가 되기 때문에 안 되는 것이고, static은 @Bean 메소드 호출을 통한 빈 생성과 참조라는 목적에 맞지 않기 때문이다.</p>
<p>반면에 스프링 3.1은 static @Bean 메소드를 특별한 용도에 한해서 허용한다. 바로 BFPP 빈을 정의할 때다.</p>
<p>@Configuration 빈이 생성되기 전에, 즉 메타 데이터 상태로 있을 때 적용되야할 BFPP 빈이 있다면 이를 static 메소드로 정의한다. 스태틱이므로 빈 인스턴스를 만들지 않아도 호출할 수 있다. 스프링은 @Configuration 클래스 내에 @Bean이 붙은 스태틱 메소드가 있으면 이를 먼저 호출해서 빈을 생성해두고, 그 후에 @Configuration 빈을 만든다. 따라서 BFPP @Bean 빈은 @Configuration 클래스에도 영향을 줄 수 있는 것이다.</p>
<p>스태틱 @Bean 메소드는 BFPP 빈의 문제를 해결하기 위해서 사용되도록 의도된 것이므로 BFPP가 아닌 일반 빈을 만들 때는 사용하지 않는 것이 좋다. 스태틱 메소드이므로 다른 @Bean 인스턴스 메소드를 호출할 수도 없고, @Autowired로 DI받은 빈을 참조할 수도 없다. 그래도 억지로 BFPP가 아닌 빈을 스태틱 @Bean으로 정의하면 스프링이 WARN 레벨의 로그로 경고를 때려줄 것이다. 에러가 나지는 않지만 나중에 코드 리뷰하다 쪽팔릴 수 있으니 쓸데없이 사용하지는 말자.</p>
<p>그런데 위에서 정의한 PropertySourcesPlaceholderConfigurer 빈 메소드는 예전에 PPC를 만드는 @Bean 메소드와 다른 점이 있다. 메시지 파일의 location을 지정하지 않는다는 것인데 그 이유는 스프링 3.1의 프로퍼티 체계가 완전히 다르기 때문이다. PSPC는 프로퍼티 정보를 특정 파일이 아니라 Environment로부터 가져온다. 이 얘기를 마저 하려면 너무 길어져서 여기서 그만. 자세한 것은 조만간 나올 토비의 스프링 3 개정판을 참고 하&#8230;</p>
<p>오늘은 여기까지.</p>
<p>혹시 이 설명을 듣고 머리가 너무 아프다면, 잠시 머리를 식힐 겸 <a href="http://www.yes24.com/24/goods/6271069?scode=032&amp;OzSrank=1">아웃사이더 님의 node.js 프로그래밍</a> 책을 구해서 보는 것이 좋겠다. 최근 스프링 프레임워크 개발자들이 관심을 가지는 기술의 하나는 node.js다. 작년 VMForum에서 스프링 소스 호주 지부장이자 스프링 시큐리티, ROO를 만든 벤 알렉스는 키노트에서 스프링과 node.js를 함께 사용하는 멋진 예제를 소개하며 스프링의 미래를 전망했다. 스프링 소스가 참여해서 개발되고 있는 PaaS인 CloudFoundry에도 스프링과 함께 node.js 런타임이 지원되고, 스프링과 손쉽게 연동해서 사용할 수 있는 다양한 방법이 제공되고 있다. 스프링 개발자라면 한번쯤 관심을 가져야 할 기술이다. 아웃사이더님의 책은 내가 node.js를 공부(리뷰에 참여한 덕분에 원고를 좀 일찍 받아봤다)하면서 참고하고 있는데 아주 맘에 든다. 뭐, 내가 이 책의 추천사를 썼기 때문에 하는 말은 아니고&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1177</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1177</feedburner:origLink></item>
		<item>
		<title>스프링 3.1 (3) @Enable~</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/nPS4aOCJwWM/</link>
		<comments>http://toby.epril.com/?p=1174#comments</comments>
		<pubDate>Tue, 27 Dec 2011 21:01:22 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring 3.1]]></category>
		<category><![CDATA[spring 3.1]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1174</guid>
		<description><![CDATA[스프링 3.0에서 코어에 통합된 자바콘픽(@Configuration)은 여러모로 매력적이다. 하지만 그게 왜 매력적인지 느끼기는 쉽지 않다. 일반 개발자들이라면 애플리케이션 빈은 @Component로 스캔해서 만들면 그만이다. 트랜잭션, AOP, 유틸 따위의 인프라 빈은 XML의 &#60;namespace:*&#62;을 이용하면 간편하게 등록하고 설정할 수 있다. 반면 &#60;tx:*&#62;나 &#60;aop:*&#62; 등을 자바코드로 빈을 만들어서 등록하라고 하면 막막하다. @Transactional이 적용되게 하려면 &#60;tx:annotation-driven /&#62;을 XML에 넣어주기만 하면 되는데, <a href='http://toby.epril.com/?p=1174'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1174"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1174&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>스프링 3.0에서 코어에 통합된 자바콘픽(@Configuration)은 여러모로 매력적이다. 하지만 그게 왜 매력적인지 느끼기는 쉽지 않다. </p>
<p>일반 개발자들이라면 애플리케이션 빈은 @Component로 스캔해서 만들면 그만이다. 트랜잭션, AOP, 유틸 따위의 인프라 빈은 XML의 &lt;namespace:*&gt;을 이용하면 간편하게 등록하고 설정할 수 있다. </p>
<p>반면 &lt;tx:*&gt;나 &lt;aop:*&gt; 등을 자바코드로 빈을 만들어서 등록하라고 하면 막막하다. @Transactional이 적용되게 하려면 &lt;tx:annotation-driven /&gt;을 XML에 넣어주기만 하면 되는데, 이걸 @Cofiguration 클래스의 @Bean메소드로 직접 관련된 빈을 만들려면 어떤 빈 클래스를 어떻게 만들고 설정해야 할지 알 수 없기 때문이다. 스프링 소스를 파보기 전에는 파악하기도 불가능하고, 소스를 본다고 해도 스프링 소스중에서도 가장 난해한 NamespaceHandler류와 BeanDefinitionParser는 해석하려면 상당한 노력이 필요하다. 혹시 자신있으면 ConfigBeanDefinitionParser따위를 한번 들여다 보고 어떤 경우+옵션에 어떤 빈이 어떤 설정으로 만들어지는지 파악해보든가.</p>
<p>그래서 임의의 자바코드를 이용한 빈 등록과 설정 방식을 지원하고, 자동 빈 스캔을 이용할 수 있는 3.0에서도 설정용 XML은 반드시 필요했다. </p>
<p>하지만 스프링 3.0에서는 XML의 전용 설정 태그를 대체할 수 있는 @Enable류의 자바콘픽 설정방식이 등장했다. 그래서 XML없이도 간편하고, 타입체크도 쉬우면서, 훨씬 유연한 방법으로 각종 인프라 빈의 등록과 설정이 가능해졌다. 원하면 스프링관련 XML을 모두 제거하는 것도 가능하다.</p>
<p>XML 몇줄 들어가는 것이 무슨 상관이냐, 스키마도 있어서 IDE에서 자동완성과 타입체크도 된는데라고 생각한다면 그냥 XML 써도 그만이겠지만. 새로운 설정방식에 관심이 있다면 @Enable로 대표되는 자바코드 설정 방식에 도전해보는 것도 좋겠다. </p>
<p>XML을 쓸 때 빈의 종류에 따라서 XML파일을 분리해서 사용하는 경우는 흔치 않다. 기껏해야 웹 관련 여부에 따라서 루트 컨텍스트와 웹 컨텍스트 정도를 구분하는 수준이다. 반면에 자바코드를 이용하면 성격에 맞게 각각의 설정을 독립된 @Configuration 클래스에 분리하는 것이 간편하고, 깔끔하게 보일 수 있다. </p>
<p>예를 들면, &lt;tx:*&gt;류에 대응되는 @EnableTransactionManagement가 붙은 클래스라면 관련된 DataSource나 SessionFactory 빈 등만 넣어두면 깔끔하다. 이러면 설정 클래스를 조합해서 재사용하기도 쉬고, 코드도 직관적이고, 테스트할 때도 원하는 설정 클래스만 모아서 돌리면, 애플리케이션 전체 인프라 빈을 다 가동시키지 않아도 되기 때문에 빠른 실행이 가능하다. </p>
<p>@Enable~류의 가장 큰 매력은 단순히 애노테이션 엘리먼트를 통한 설정만 가능한게 아니라, WebMvcConfigurer와 같은 discoverable config. API를 이용해서 좀더 복잡한 설정이 가능하다는 점이다. 대표적인 것이 3.0에서 가장 어정쩡했던 &lt;mvc:*&gt;를 대체할 수 있는 @EnableWebMvc와 WebMvcConfigurer이다. 종류도 많고 설정 방식도 복잡한 DispatcherServlet의 전략을 &lt;mvc:*&gt;로 꾸미면 상당히 지저분한데,&#160; 이를 자바코드만으로 편리하게 만들 수 있다.</p>
<p>@Enable~이 가지는 또 다른 매력은, XML 네임스페이스 활용방법이 그랬던 것처럼, 개발자가 프로젝트에 필요한 인프라 빈 또는 앱 빈을 간결한 방식으로 등록, 설정 가능하도록 직접 활용할 수 있다는 점이다. 만들기 까다로운 스키마-전용태그-빈정의파서 등등 방식보다 훨씬 쉽고 편하게 개발가능하다.</p>
<p>프로젝트에 적용한 사내 프레임워크의 기본 설정등을 @Enable 애노테이션과 엘리먼트 값 정도만 가지고 한방에 설정하도록 하는 것도 가능하다. 예를 들어 @EnbaleHibernate 라는 것을 하나 만들어서, database.properties 정보를 활용해서 DataSource를 만들고, SessionFactory도 만들고, 트랜잭션 AOP도 걸고, 기타 관련 빈 등을 한방에 세팅할 수 있도록 만들 수 있다. 몇가지 @Enable~을 조합하는 것만으로 사내 표준 프레임워크의 인프라 빈을 모두 세팅하는 것도 괜찮은 방법일테다. 매번 샘플에 나온 스픠링 XML을 카피카피 해서 조금씩 고쳐쓰다가, 깜빡하고 한줄 잘못 지워서 엉뚱하게 동작하는 따위를 막아줄 수도 있을테고.</p>
<p>다음 번엔 @Enable~등을 어떻게 만들 수 있는지, 스프링 3.1이 제공하고 실제로 적용한 방식이 무엇인지 정리해보자.</p>
<p>그리고 지금은 구상단계긴한데, 1월 2~3째주쯤에 이에 대해서 관심있는 분들과 함께 흥미로운 세미나를 할까도 생각중이다. </p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1174</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1174</feedburner:origLink></item>
		<item>
		<title>스프링 3.1 (2) HandlerInterceptor의 적용순서</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/ql35W6Ce6Sc/</link>
		<comments>http://toby.epril.com/?p=1172#comments</comments>
		<pubDate>Thu, 22 Dec 2011 21:53:27 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring 3.1]]></category>
		<category><![CDATA[spring 3.1]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1172</guid>
		<description><![CDATA[사실 3.1 얘기를 인터셉터로 시작하려고 했던 것은 아니다. 지난 포스팅의 핵심은 자바콘픽의 사용방법이었고, @MVC 콘픽 클래스에서 인터셉터를 적용하는 메소드의 예를 든 것은 문제가 생겼을 때 난처한 결과를 맞을 수 있는 적절한 예이기 때문이다. 자세한 설명은 내년중에 마무리할 토비의 스프링 3 개정판에서 다루겠지만, 그래도 눈치를 채고 문제를 짚어낼 분이 있을까 싶어서 질문으로 마무리해본 것인데, 대부분 별 <a href='http://toby.epril.com/?p=1172'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1172"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1172&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>사실 3.1 얘기를 인터셉터로 시작하려고 했던 것은 아니다. 지난 포스팅의 핵심은 자바콘픽의 사용방법이었고, @MVC 콘픽 클래스에서 인터셉터를 적용하는 메소드의 예를 든 것은 문제가 생겼을 때 난처한 결과를 맞을 수 있는 적절한 예이기 때문이다. 자세한 설명은 내년중에 마무리할 토비의 스프링 3 개정판에서 다루겠지만, 그래도 눈치를 채고 문제를 짚어낼 분이 있을까 싶어서 질문으로 마무리해본 것인데, 대부분 별 관심이 없는듯하고, 몇몇분은 그냥 답 내놓으라고만 하니&#8230;</p>
<p>유일하게 SuHong Lee님이 <a href="http://toby.epril.com/?p=1168&amp;fb_comment_id=fbc_10150451698536137_20103573_10150456368041137#f7fd0253">성의있는 의견</a>을 주셨다.</p>
<blockquote><p>혹시 인터셉터의 적용 순서를 알 수 없다는 것에대한 문제 인가요??     <br />정확히는 모르겠네요      <br />&lt;mvc:&gt;를 사용 하면 적용 순서를 알 수가 있을텐데      <br />위와 같이 적용하면 순서가 어떻게 적용 될지 모를 수도 있을 듯하네요</p>
</blockquote>
<p>스프링에는 Ordered 인터페이스를 구현하고 order프로퍼티를 노출시켜서 적용 순서를 지정할 수 있도록 만들어진 빈들이 제법 많다. 자주 눈에 띄는 것을 꼽자면 HandlerMapping류 빈들이 있다. 여러 핸들러매핑 전략을 동시에 적용할 때, 우선순위를 지정해서 가장 먼저 걸리는 핸들러를 적용하도록 만들 수 있다. 일부 뷰리졸버도 그렇고.</p>
<p>하지만 MVC 인터셉터의 API인 HandlerInterceptor나 그 구현 빈들은 Ordered가 적용되어있지 않다. 따라서 숫자값을 설정해서 그걸로 인터셉터의 적용 순서를 결정하지도 않는다. </p>
<p>인터셉터는 핸들러매핑 안에서 만들어지는 핸들러 호출용 HandlerExecutionChain안에 핸들러(컨트롤러라고도 부르는)와 함께 들어가서 실행체인의 일부로 만들어진다. 이렇게 만들어진 핸들러 체인은 DispatcherServlet에 의해서 적절한 순서에 의해서 인터셉터와 핸들러가 실행된다. </p>
<p>여러개의 인터셉터가 적용됐을 때, 각각이 독립적이고 서로 간섭이 없다면(핸들러체인 실행을 중단하는 따위) 실행 순서가 어떻게 되든 상관없다.&#160; 반면에 실행순서가 의미가 있는 경우라면 일정한 실행 순서가 보장이 되야 한다. HandlerInterceptor는 pre와 post가 반대의 반향으로 실행된다. 인터셉터 1,2,3이 있고, 1,2,3의 순서가 정해져있다면 1-2-3-handler-3-2-1-view-3-2-1의 순서로 실행될 것이다. </p>
<p>그럼 실행 순서는 어떻게 결정되는가? </p>
<p>일단 3.1얘기니까 3.1의 핵심인 WebMvcConfigurationSupport이나 WebMvcConfigurer를 사용했다고 보면, addInterceptors() 메소드를 구현 또는 오버라이딩하고, 파라미터로 전달된 InterceptorRegistry를 이용해서 인터셉터를 추가해주면 된다. 3.0에서 등장한 &lt;mvc:interceptors&gt;를 이용한 방법과 거의 동일하다. </p>
<p>InterceptorRegistry는 HandlerInterceptor 타입의 인터셉터와 WebRequestInterceptor 타입의 인터셉트 두 가지를 등록할 수 있게 되어있다. 일반적으로 HandlerInterceptor가 인터셉터를 만들 때 쓰는 대표적인 인터페이스지만, HandlerInterceptor는 Servlet API에 종속적이라는 단점이 있기 때문에, Portlet을 본격적으로 지원(원래 1.3에 들어가기로 한 기능인데, S1 2005인가 하면서 1.3을 건너뛰고 2.0으로 가기로 결정 했고, 그래서 2.0에 뒤늦게 등장했다)기 시작한 2.0부터는 웹 기술 API에 독립적인 WebRequestInterceptor가 등장해서 HandlerInterceptor와 함께 사용되기 시작했다. 서블릿 환경에서만 사용할 것이라면 HandlerInterceptor로 충분하겠지만, 서블릿, 포트릿 환경에 가릴 것 없이 사용될 수 있다면 WebRequestInterceptor가 적합하다. 그래서 지난번에 예로 들었던 OSIV인터셉터도 WebRequestInterceptor를 구현하고 있다. </p>
<p>이렇게 두가지 종류의 인터셉터가 섞여서 등록된다면 그 실행 순서는 어떻게 될까? </p>
<p>기본적으로는 인터셉터의 구현 방식은 순서에 영향을 주지 않는다. 실행순서는 레지스트리에 등록한 순서다. 내부에서 List로 저장해서 관리하기 때문에 순서가 보장된다. WebRequestInterceptor도 결국 실행 환경을 보고 적절한 아답터(WebRequestHandlerInterceptorAdapter 같은)를 통해서 HandlerInterceptor 타입으로 전환되서 실행체인으로 넘어가므로 사실 구분이 없다. 스프링(정확히는 Spring-mvc 모듈)은 내부적으로 이런 인터페이스들을 adapted interceptor라고 부른다.</p>
<p>그러면 그냥 인터셉터는 InterceptorRegistry에 add한 순서라고 보면 될까?</p>
<p>그런데, 그게 또 아니다.</p>
<p>인터셉터는 또 다시 단순 adapted interceptor와 mapped interceptor로 구분될 수 있다. 후자도 결국은 같은 HandlerInterceptor를 구현한 인터셉터지만 특정 URL패턴에 매칭되는 경우에만 실행되는 놈이다. &lt;mvc:&gt;를 썼다면 &lt;mvc:mapping path=&quot;/secure/**&quot;/&gt;과 같이 사용했던 바로 그 방식이다.</p>
<p>그런데, 이렇게 매핑패턴을 가지는 mapped interceptor는 일반 인터셉터보다 실행 우선순위가 밀린다. 레지스트리 등록순서에 상관없이 나중에 실행된다. </p>
<p>정리하자면, 인터셉터는 단순 인터셉터(adapted interceptor)가 먼저 실행되고 매핑 인터셉터(mapped interceptor)가 그 다음에 실행되는데, 종류별로는 인터셉터 레지스트리에 등록된 순서대로 실행이 된다.&#160; 따라서 아무리 mapped interceptor를 먼저 등록해도 나중에 실행될 것으로 판단해야 한다.</p>
<p>만약 URL매핑을 하는 인터셉터가 우선적으로 실행되게 만들고 싶다면? 그때는 HandlerInterceptor를 구현해서, 거기서 URL비교를 해서 인터셉터 로직을 적용할지 말지를 결정하도록 만들고 URL패턴 없이 일반 인터셉터로 등록해서 사용하면 될 듯.</p>
<p>일단 여기까지.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1172</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1172</feedburner:origLink></item>
		<item>
		<title>SpringOne2GX 2011 남의 후기</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/Ib3IC9yyB28/</link>
		<comments>http://toby.epril.com/?p=1170#comments</comments>
		<pubDate>Tue, 20 Dec 2011 21:32:36 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Event]]></category>
		<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[springone]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1170</guid>
		<description><![CDATA[지금까지 4번 참석했던 SpringOne 컨퍼런스. 초기 오픈소스 커뮤니티의 축제 같던 모습에서 점차 딱딱한 마케팅용 기업 컨퍼런스로 변모해가는 모습들이 기억에. 스프링OSGi나 스프링 배치를 최초로 공개하는 자리에 있었다는 뿌듯함. 그리고 전세계에서 몰려든 고수들 사이에서 내 실력은 정말 보잘 것 없구나라고 느끼며 겸손해졌던 순간들. 한번 방문하려면 적지 않은 돈과 시간을 들여야 하는데, 언제부턴가는(사실은 책 쓴다고 일도 안하고 인생을 <a href='http://toby.epril.com/?p=1170'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1170"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1170&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>지금까지 4번 참석했던 SpringOne 컨퍼런스. 초기 오픈소스 커뮤니티의 축제 같던 모습에서 점차 딱딱한 마케팅용 기업 컨퍼런스로 변모해가는 모습들이 기억에. 스프링OSGi나 스프링 배치를 최초로 공개하는 자리에 있었다는 뿌듯함. 그리고 전세계에서 몰려든 고수들 사이에서 내 실력은 정말 보잘 것 없구나라고 느끼며 겸손해졌던 순간들. </p>
<p>한번 방문하려면 적지 않은 돈과 시간을 들여야 하는데, 언제부턴가는(사실은 책 쓴다고 일도 안하고 인생을 탕진했던 때) 그럴 여유도 없어졌고 그래서 최근 몇년은 회사의 빵빵한 지원을 받으며 남이 다녀온 이야기만 침흘리며 들어야 했는데, 올해도 마찬가지. </p>
<p>올해는 제끼고 내년에 같이 가기로 약속했던 기선군은 배신을 때리고 혼자 가버렸다.</p>
<p>기선이의 뽐뿌.</p>
<ul>
<li><a href="http://whiteship.me/?p=13333">SpringOne 2011 드디어 내일 간다</a> </li>
<li><a href="http://whiteship.me/?p=13347">[SpringOne 2011] 첫날 후기</a> </li>
<li><a href="http://whiteship.me/?p=13351">[SpringOne 2011] Welcome Keynote</a> </li>
<li><a href="http://whiteship.me/?p=13366">[SpringOne 2011] insight 클파버전</a> </li>
<li><a href="http://whiteship.me/?p=13368">[SpringOne 2011] Spring 발표 자료</a> </li>
</ul>
<p>그리고 표준프레임워크 오픈 커뮤니티 커미터인 정호열님의 방문기</p>
<ul>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3181">SpringOne 2GX 2011: Conference 참석 출사표!!!</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3205">SpringOne 2GX 2011: Conference 여행기_1일차_오전10시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3206">SpringOne 2GX 2011: Conference 여행기_1일차_오후4시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3215">SpringOne 2GX 2011: Conference 여행기_2일차_새벽 1 시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3216">SpringOne 2GX 2011: Conference 여행기_2일차_밤 12 시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3223">SpringOne 2GX 2011: Conference 여행기_3일차_밤 11 시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3227">SpringOne 2GX 2011: Conference 여행기_4일차_낮 1 시쯤</a></li>
<li><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3228">SpringOne 2GX 2011: Conference 여행기_4일차(마지​막 밤)</a></li>
</ul>
<p>그리고 다음과 같은 참석 나늠 세미나를 한다고.</p>
<p><a href="http://open.egovframe.go.kr/projects/opencommunity/event/3473">16차 기술세미나(2011.12.28)- Springone2GX 참석 후기 나눔 세미나</a></p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1170</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1170</feedburner:origLink></item>
		<item>
		<title>스프링 3.1 (1) JavaConfig의 위험성</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/MXddutfea3U/</link>
		<comments>http://toby.epril.com/?p=1168#comments</comments>
		<pubDate>Mon, 19 Dec 2011 21:46:37 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1168</guid>
		<description><![CDATA[100년만에 쓰는 블로그. 그동안 너무 바빴다. 간단한 생각은 구글+에 남기는 것으로 충분하기도 했고. 그래도 스프링 3.1이 나왔으니 블로그에 좀 정리해둘 필요를 느껴서 다시 시작. 3.0에서 시작된 본격적인 Java 코드를 이용한 DI설정은 3.1에서 한층 더 강화됐다. 전용 스키마 태그(tx, aop 등등)에 대응되는 편리한 자바 코드 설정방법도 등장했다. 빈 스캔은 물론이고, 트랜잭션 설정, MVC 설정등등도 모두 자바 <a href='http://toby.epril.com/?p=1168'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1168"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1168&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>100년만에 쓰는 블로그. 그동안 너무 바빴다. 간단한 생각은 구글+에 남기는 것으로 충분하기도 했고.</p>
<p>그래도 스프링 3.1이 나왔으니 블로그에 좀 정리해둘 필요를 느껴서 다시 시작.</p>
<p>3.0에서 시작된 본격적인 Java 코드를 이용한 DI설정은 3.1에서 한층 더 강화됐다. 전용 스키마 태그(tx, aop 등등)에 대응되는 편리한 자바 코드 설정방법도 등장했다. 빈 스캔은 물론이고, 트랜잭션 설정, MVC 설정등등도 모두 자바 코드를 이용해서 정의할 수 있다.</p>
<p>가장 눈에 띄는 것은 @EnableWebMvc을 이용한 스프링MVC 관련 설정이다. 몇가지 방법이 있는데, 나는 가장 정교한 설정이 가능한 WebMvcConfigurationSupport를 선호한다. WebMvcConfigurationSupport를 확장해서 만든 웹 설정 클래스는 &lt;mvc&gt;를 완전히 대체할 수 있다. 물론 그 이상의 정교한 DispatcherServlet 전략 튜닝도 가능하다.</p>
<p>예를 들면, 기존 3.0에선 XML에서 다음과 같이 모든 핸들러 매핑에 하이버네이트 OSIV 패턴을 적용해주는 인터셉터를 정의할 수 있었다.</p>
<p>&lt;mvc:interceptors&gt;   <br />&lt;bean class=&quot;org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor&quot;&gt;    <br />&lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot; /&gt;    <br />&lt;/bean&gt;    <br />&lt;/mvc:interceptors&gt;</p>
<p>이를 WebMvcConfigurationSupport를 이용해서 정의한다면 다음과 같이 addInterceptors메소드를 오버라이딩해서 레지스트리에 인터셉터 오브젝트를 직접 등록해주면 된다.</p>
<p>@Override   <br />protected void addInterceptors(InterceptorRegistry registry) {    <br />OpenSessionInViewInterceptor osivInterceptor = new OpenSessionInViewInterceptor();    <br />osivInterceptor.setSessionFactory(sessionFactory);    <br />registry.addWebRequestInterceptor(osivInterceptor);    <br />}</p>
<p>이때 OSIVI에 프로퍼티로 주입해줄 sessionFactory는 어디서 정의되어있든 상관없이 다음 한 줄을 추가해주면 된다.</p>
<p>@Autowired SessionFactory sessionFactory;</p>
<p>SF를 XML에 정의했든, 다른 자바 설정 클래스에 정의했든 상관없이 알아서 가져온다.</p>
<p>이렇게 직접 OSIV인터셉터를 만들어서 등록해주면 &lt;mvc&gt;를 사용했을 때와 동일한 설정 결과를 얻는다.</p>
<p>하지만,</p>
<p>나는 이런 방식이 상당히 위험하다고 생각한다. 물론 지금 이 addInterceptors()의 설정은 별 문제가 없어보인다. 하지만 이런 스타일로 자바 코드를 이용한 설정방식을 사용하게 된다면, 매우 난감한 결과를 맞을 가능성이 높아질 것이다. 그래서 별로 권장하고 싶지 않다.</p>
<p>뭐가 문제일까?</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1168</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1168</feedburner:origLink></item>
		<item>
		<title>자바 세상의 빌드를 이끄는 메이븐을 읽고 (1)</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/n7AGLuiVXdI/</link>
		<comments>http://toby.epril.com/?p=1153#comments</comments>
		<pubDate>Sat, 30 Apr 2011 00:26:21 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Book]]></category>
		<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[메이븐]]></category>
		<category><![CDATA[자빌메]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1153</guid>
		<description><![CDATA[재성이가 보내준 자빌메(자바 세상의 빌드를 이끄는 메이븐)을 두번째 읽기 시작했다. 이 책을 처음 읽은 것은 몇 주 전이다. 책을 받은 날 저녁, 반가운 마음에 바로 읽기 시작해서 그날 밤 잠자리에 들기 전에 끝냈다. 책의 내용은 내가 이미 다 알고 있는 것들이라 친숙한데다, 블로그 글을 읽는 듯한 느낌이 들정도로 흥미롭고 편안하게 쓰여져서인지 술술 잘 읽혔다. KSUG <a href='http://toby.epril.com/?p=1153'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1153"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1153&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>재성이가 보내준 자빌메(자바 세상의 빌드를 이끄는 메이븐)을 두번째 읽기 시작했다. </p>
<p>이 책을 처음 읽은 것은 몇 주 전이다. 책을 받은 날 저녁, 반가운 마음에 바로 읽기 시작해서 그날 밤 잠자리에 들기 전에 끝냈다. 책의 내용은 내가 이미 다 알고 있는 것들이라 친숙한데다, 블로그 글을 읽는 듯한 느낌이 들정도로 흥미롭고 편안하게 쓰여져서인지 술술 잘 읽혔다. KSUG 운영진의 한 명인 benelog님은 <a href="http://me2day.net/benelog/2011/04/20#04:32:06">한 시간 반만에 이 책을 다 읽어다</a>고 하니 메이븐을 이용한 개발과 경험이 있는 사람이라면 그리 어렵지 않게 끝낼 수 있는 책인게 분명하다.</p>
<p>꽤 오래전부터 책을 읽고 서평을 쓰거나 감상을 적는 일은 삼가해 오고 있다. 여러가지 이유가 있겠지만, 가볍고 진지하지 못한 인상비평을 수준의 글을 공개된 매체에 남겨서 다른 이들에게 어떤 방향으로든 오해를 주고 싶지 않기 때문이다. 누군가는 소중한 가치를 얻을 수 있는 책을 꼼꼼히 읽고 고민하지도 않은 주제에 몇 가지 트집을 잡고 빈정대서 책을 제대로 접할 기회조차 박탈시켜 버리거나, 성의 없는 두리뭉실 칭찬으로 자신이 필요로 하지도 않은 책을 읽게 만들어서 바쁜 개발자들의 시간을 낭비하게 만들고 싶지 않았다. 책 읽고 감상 남기는게 뭐 그리 대수라고 깐깐하게 생각하는가 싶겠지만, 내가 스스로 책을 쓰는 데 적지 않은 인생을 소비해보니 그 창작의 수고에 대해 어떤 면으로든 갖춰야 할 예의가 있다는 생각이 절실히 들었던 것 같다.</p>
<p>혹은 남의 책 잘못 깠다가, 나중에 내 책이 역공을 당해 판매가 저조해지면 출판사에 불려가 혼날까봐 겁이 났던 것도 있다. 자기 생각을 F워드를 섞어가며 거침없이 쏟아내던 개빈 킹 조차, VC로부터 거액의 투자를 받고난 뒤&#160; 회사의 이미지에 나쁜 영향을 주는 직원의 말과 행동에 강력한 제제가 가해지던 JBoss에 입사하고는 예의바르고 순한 범생처럼 변해간 것과 비슷할지도 모르겠다.</p>
<p>그러던 중에 재성이가 자기가 쓴 &quot;책을 보낼테니 주소를 알려달라&quot;고 연락이 왔다. 앗싸를 외치고는, 메신저로 주소를 적어주고 있었는데, 뒤 따르는 말이 &quot;책에 대한 냉정한 평가를 해줘&quot;였다. &quot;에이, 우리 사이에 어찌 그런 짓을&#8230;&quot;이라고 넘기려는데, &quot;그럼 안 보내줄거야&quot;라는 협박이 돌아왔다. 그래서 어쩔 수 없이 책을 읽고 감상을 공개하기로 약속했고, 지금 다시 내 느낌과 생각을 정리하기 위해서 책을 읽고 있다.</p>
<p>&#160;</p>
<p>내 스타일이 원래 그러니, 한번 시작하면 얘기가 길어질 지도 모른다. 그래서 결론부터 얘기하고 가야겠다. </p>
<p>이 책은 메이븐의 도입/사용을 진지하게 고려하고 있는 사람이라면 반드시 읽어야 할 책이다. 메이븐에 관한 책이 영어나 한글로 여러 권이 이미 나와있고, 인터넷에도 많은 자료가 존재한다. 하지만 이 책, 자.빌.메.는 다른 책과 다르다. 다른 모든 책보다 낫다고 쉽게 말할 수는 없다. 하지만 분명한 것은 다른 책과 차별될만한 요소를 가진 독특한 책이다. 그 점이 매력이고, 장점이다. 물론 그것이 어떤 독자에게는 단점될 수도 있겠지만. (재성이는 내 책의 리뷰를 쓰면 스프링 개발 경험을 몇년 쌓은 뒤에나 보라고 할거라고 했는데, 나는 재성이와 달리 까칠하지 않고 착하기 때문에 &quot;빌드 경험 몇년 쌓고 난 뒤에 이 책을 보라&quot;고 하지는 않을 거다. 삽질 경험을 쌓고 나면 더 얻을 수 있는 가치게 있겠지만, 일단 이해는 안되도 책을 읽으며 이런 것이 있구나 하고 기억만 해둬도 큰 도움이 될 것이기 때문이다)</p>
<p>결론 끝.</p>
<p>참, 노안이 시작되기 시작한, 또는 안구건조증이나 기타 질병으로 눈이 침침한 분들은 절대로 흐릿한 실내 조명 아래서 이 책을 읽지 말기 바란다. 책의 인쇄상태는 가장 큰 단점이다. 특히 명령창에 나온 메이븐 실행화면은 뭉개지고 흐릿해서 아주 밝은 조명아래가 아닌 곳에서 읽으려면 눈이 충혈되고 아파올 수 있다. 나도 한밤중에 스탠드 불빛 아래서 책을 다 읽느라고, 눈이 빠지는 줄 알았다. 그마나 메이븐 실행 결과 내용에 익숙해서 대충 보고 넘어가서 그렇지, 명령창의 내용을 자세히 읽으려면 눈에 힘주다 짜증나서 책을 덮어버렸을 지도 모른다. 다음 인쇄에서는 개선되기를 가장 바라는 부분이다. 이 문제는 출판사의 실책이 아닌가 싶다. 물론 시력이 1.2이상 되고, 명암 구분에 뛰어나며, 눈에 총기가 도는 젊은 분들이라면 괜찮을지도 모르겠지만.</p>
<p>&#160;</p>
<p>다음 할 얘기는 &quot;이 책이 말하는 빌드란 도대체 무엇인가? 메이븐이 과연 빌드툴인가?&quot; 이다. 처음부터 시비를 걸고싶지는 않지만, 어쨌든 책이 다루는 주제의 정체와 책이 사용하는 용어에 대해 언급하지 않고 시작할 수는 없기에, 이 책이 빌드라는 개념에 대해서 두리뭉실 넘어가고, 일관성 없이 쓴 것을 지적하지 않을 수 없겠다. 이 얘기는 다음 편에서 계속.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1153</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1153</feedburner:origLink></item>
		<item>
		<title>Maven POM에 attribute 사용하기 (2)</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/G6Z4VKSYrfA/</link>
		<comments>http://toby.epril.com/?p=1152#comments</comments>
		<pubDate>Thu, 14 Apr 2011 23:04:22 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[maven]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1152</guid>
		<description><![CDATA[갑작스럽게 시드니 출장을 다녀와야 했던 데다 며칠 전에 도착한 iPad 2 가지고 놀기 바빠서 미뤘던 메이븐 애트리뷰트 적용기 두번째 이야기. 먼저 메이븐 pom.xml에 애트리뷰트를 적용하면 어떤 모습이 되야 할지 생각해봤다. 가장 이상적인 것은 엘리먼트와 애트리뷰트 양쪽을 모두 사용할 수 있는 것이다. 스프링이 그런 식이다. 스프링도 처음에는 XML 설정에 엘리먼트의 사용 비율이 높았다. 예를 들어 값 <a href='http://toby.epril.com/?p=1152'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1152"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1152&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>갑작스럽게 시드니 출장을 다녀와야 했던 데다 며칠 전에 도착한 iPad 2 가지고 놀기 바빠서 미뤘던 메이븐 애트리뷰트 적용기 두번째 이야기.</p>
<p>먼저 메이븐 pom.xml에 애트리뷰트를 적용하면 어떤 모습이 되야 할지 생각해봤다. 가장 이상적인 것은 엘리먼트와 애트리뷰트 양쪽을 모두 사용할 수 있는 것이다. 스프링이 그런 식이다. 스프링도 처음에는 XML 설정에 엘리먼트의 사용 비율이 높았다. 예를 들어 값 하나와 빈 레퍼런스 하나를 프로퍼티로 가지는 빈이라면 다음과 같이 작성해야 했다.</p>
<p>&lt;bean id=”hello” class=”Hello”&gt;   <br />&#160; &lt;property name=&quot;name&quot;&gt;    <br />&#160;&#160;&#160; &lt;value&gt;ahnpig&lt;/value&gt;    <br />&#160; &lt;/property&gt;    <br />&#160; &lt;property name=&quot;message&quot;&gt;    <br />&#160;&#160;&#160; &lt;ref bean=&quot;hellomessage&quot; /&gt;    <br />&#160; &lt;/property&gt;    <br />&lt;/bean&gt;</p>
<p>id, name 등은 애트리뷰트로 설정할 수 있어서 그나마 메이븐의 100% 엘리먼트 방식보다는 낫긴 했지만, 그래도 설정이 많아지고 애플리케이션의 규모가 커지면 XML을 읽고 수정하기가 매우 벅찼던 기억이 있다. 그래서 1.2인가부터 &lt;value&gt;, &lt;ref&gt; 태그 등을 다음과 같이 &lt;property&gt;의 애트리뷰트로도 설정할 수 있게 했다. </p>
<p>&lt;bean id=”hello” class=”Hello”&gt;   <br />&#160; &lt;property name=&quot;name&quot; value=”ahnpig” /&gt;    <br />&#160; &lt;property name=&quot;message&quot; ref=”hellomessage” /&gt;    <br />&lt;/bean&gt;</p>
<p>덕분에 설정 파일은 훨씬 간결해졌다. 동시에 스프링은 리스트와 같은 콜렉션을 값으로 설정하는 경우나 이전 버전에서 작성된 XML 설정을 그대로 사용할 수 있게 자식 엘리먼트로 &lt;value&gt;, &lt;ref&gt;를 사용하는 것을 동시에 지원했다. </p>
<p>2.0에서는 더 나아가서 p 네임스페이스를 이용해서 아예 모든 property 설정을 &lt;bean&gt; 태그 안으로 다 가져올 수 있도록도 만들었다. 또한 일정 분량의 설정 자체를 모듈화 해서 아예 전용태그로 만들 수 있는 기능도 제공했다. </p>
<p>뭐, 그렇다고 메이븐에서도 네임스페이스를 이용해서 기존 설정을 더 간결하고 의미를 잘 드러낼 수 있는 방식으로 재정의 하는 것까지는 바라지 않는다. 그저 바라기는 일부 설정 단위를 애트리뷰트로 묶어 낼 수 있는 정도이고, 가능하면 기존 pom.xml 설정과 호환성을 유지할 수 있도록 애트리뷰트와 엘리먼트 양쪽으로 다 설정이 가능하도록 만드는 것이다.</p>
<p>&#160;</p>
<p>하지만 막상 메이븐을 열어보니 애트리뷰트와 엘리먼트의 동시 지원 방식은 쉽지 않아보였다.</p>
<p>스프링과 같이 DI 원리를 충실하게 지켜가며 만들어진 프레임워크라면 당연히 정보를 담고 있는 모델과 이 메타 모델을 직렬화해서 저장해둔 설정파일의 형식이나 구조는 서로 결합되지 않고 깔끔하게 분리되는 것이 기본이다. </p>
<p>메이븐도 Plexus라는 IoC/DI 컨테이너를 사용한다. 메이븐 말고 Plexus를 사용하는 제품이 있는지는 모르겠지만, 아무튼 3.0은 Plexus를 이용해서 나름 충실한 DI 스타일의 개발을 했다고 알고 있다. 심지어 3.1에서는 구글주스 DI도 추가적으로 이용할 수 있게 만드는 계획도 있다고 들었다. </p>
<p>메이븐에서 pom.xml의 정보는 Model 오브젝트와 그 계층구조로 표현된다. pom.xml의 루트 엘리먼트인 &lt;project&gt;가 바로 Model 클래스에 매핑된다. 하위 엘리먼트로 정의되지만 사실상 project의 속성에 해당하는 &lt;modelVersion&gt;, &lt;artifactId&gt;, &lt;name&gt; 따위는 Model 클래스가 프로퍼티 값으로 가지고 있다. 반면에 하나 이상의 정보를 가지고 있어야 하는 &lt;developers&gt;나 &lt;contributors&gt;는 List 프로퍼티로 정의되어 있고, 독립적으로 계층 구조를 가지는 &lt;build&gt;, &lt;dependcie&gt;등은 Build, Dependency&#160; 정의되어서 Model의 프로퍼티로 등록되어있다. </p>
<p>결국 pom.xml에는 모두 다 엘리먼트로 만들어지지만 값에 해당하는 부분은 Model 클래스 계층구조 안에서는 String, int와 같이 값으로 정의되고, 하위 구조를 가지는 경우에는 Dependency, Plugin 등의 클래스로 표현된다는 것을 알 수 있다. 이렇게 프로퍼티 값으로 정의되는 부분이라면 XML에서 얼마든지 애트리뷰트로 정의해주어도 문제가 없고, 사실 더 자연스럽다.</p>
<p>Model 클래스 계층구조로 추상적인 메타모델을 가지고 있으니, pom.xml과 같은 외부 설정을 읽고 파싱해서 이를 Model로 만들어주는 부분이 독립적이고, 선택가능하다면 얼마든지 애트리뷰트 확장도 가능하고 XML 외의 소스로부터 프로젝트 Model 정보를 읽어드리는 것이 가능할 것 처럼 보인다.</p>
<p>실제로 IoC/DI가 적용된 부분도 인상적이다.</p>
<p>Model 오브젝트로 표현되는 모델 정보는 ModelReader를 통해서 생성된다. DI 원칙에 충실하게 ModelReader는 인터페이스로 정의되어있고, 이를 구현하는 클래스는 얼마든지 새롭게 만들 수 있다. 그리고 어떤 ModelReader 구현을 사용할지는 IoC/DI를 통해서 ModelProcessor의 구현 클래스로 전달되도록 정의되어있다.</p>
<p>메이븐 모델을 담당하는 역할을 맡은 DefaultModelProcessor의 코드는 다음과 같이 정의되어있다.</p>
<p>@Component( role = ModelProcessor.class )   <br />public class DefaultModelProcessor&#160; implements ModelProcessor {    <br />&#160;&#160;&#160; @Requirement    <br />&#160;&#160;&#160; private ModelLocator locator;    <br />&#160;&#160;&#160; @Requirement    <br />&#160;&#160;&#160; private ModelReader reader;    <br />&#160;&#160;&#160; &#8230;</p>
<p>스프링의 @Component와 @Autowired에 익숙하다면 이 코드가 어떤 의미를 가지는지 이해할 수 있을 것이다. ModelLocator는 모델이 위치한 폴더를 주면 모델 파일을 찾아내는 책임을 정의한 것이다. 기본은 pom.xml 파일을 찾는 것이지만, 꼭 pom.xml 파일일 필요는 없으니 이 책임을 분리하고 DI로 적용할 수 있게 만든 것이다. </p>
<p>ModelReader는 File이나 InputStream으로 모델 정보를 담은 물리적인 파일 정보를 넘기면 Model 오브젝트 계층구조를 리턴하도록 정의된 메소드를 가진 인터페이스다. 스프링의 BeanDefinitionReader와 비슷하다. XmlBeanDefinitionReader를 사용하면 XML에서, PropertiesBeanDefinitionReader를 사용하면 프로퍼티 파일에서 빈 설정을 읽어오는 것처럼 메이븐도 ModelReader를 구현한 PomXmlModelReader를 DI하도록 해주면 pom.xml에서, GroovyModelReader를 주면 Groovy로 작성된 모델정보를 가져올 수 있다는 식이다. 일단 IoC/DI를 적용해 놓아서 일단 이런 가능성은 열어주긴 했지만 아직까지 ModelReader를 구현한 클래스는 pom.xml을 읽는 DefaultModelReader 뿐이다.</p>
<p>어쨌든 ModelReader 구현을 새롭게 만들거나, 기본 DefaultModelReader를 일부 수정해서 애트리뷰트까지 적용가능하도록 만들면 될 것 같다.</p>
<p>그런데 좀 더 코드를 살펴보니 생각보다 쉽지 않겠다는 생각이 들었다. 메이븐 개발자들이 왜 pom.xml에 애트리뷰트를 사용하도록 쉽게 만들지 못하고 있는지도 알게되었다. 그 이유는 다음에.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1152</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1152</feedburner:origLink></item>
		<item>
		<title>아이패드2로 블로그 글쓰기</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/keHPmlfJYmc/</link>
		<comments>http://toby.epril.com/?p=1150#comments</comments>
		<pubDate>Mon, 11 Apr 2011 22:27:52 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Etc]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1150</guid>
		<description><![CDATA[아침 일찍 아이패드2가 도착했다. 호주 발매일에 온라인 주문한 것이라 백오더가 들어가 이제야 왔다. 오늘은 아이패드2 가지고 노느라고 메이븐/애트리뷰트 연재는 미뤄야겠다. 내 블로그가 깨지지 않고 깔끔하게 나오고, 이렇게 글도 직접 쓸 수 있으니 좋구나. 오랜만에 두벌식으로 타이핑 했더니 피곤하네. 오늘은 이만 끝.]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1150"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1150&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>아침 일찍 아이패드2가 도착했다. 호주 발매일에 온라인 주문한 것이라 백오더가 들어가 이제야 왔다.<br />
오늘은 아이패드2 가지고 노느라고 메이븐/애트리뷰트 연재는 미뤄야겠다.</p>
<p>내 블로그가 깨지지 않고 깔끔하게 나오고, 이렇게 글도 직접 쓸 수 있으니 좋구나.</p>
<p>오랜만에 두벌식으로 타이핑 했더니 피곤하네. 오늘은 이만 끝. </p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1150</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1150</feedburner:origLink></item>
		<item>
		<title>Maven POM에 attribute 사용하기 (1)</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/uCwjpPdRmgc/</link>
		<comments>http://toby.epril.com/?p=1146#comments</comments>
		<pubDate>Sun, 10 Apr 2011 22:43:35 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[maven]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1146</guid>
		<description><![CDATA[재성이가 보내준 메이븐 책을 읽다가 예전에 생각만 하고 실행에 옮기지 못했던 일이 하나 떠올랐다.&#160; pom.xml 파일의 작성 방식을 바꾸는 일이다. 메이븐의 pom.xml을 직접 다뤄본 경험이 있는 사람이라면 의존 라이브러리가 늘어나고 플러그인 설정이 복잡해지면서 급격하게 양이 증가하는 pom.xml 파일에 당황해본 경험이 있을 것이다. 메이븐의 기본 관례와 페이즈를 그대로 준수하고 플러그인의 기본 설정을 바꾸지 않고 사용하는 경우라면 <a href='http://toby.epril.com/?p=1146'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1146"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1146&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>재성이가 보내준 메이븐 책을 읽다가 예전에 생각만 하고 실행에 옮기지 못했던 일이 하나 떠올랐다.&nbsp; pom.xml 파일의 작성 방식을 바꾸는 일이다.</p>
<p>메이븐의 pom.xml을 직접 다뤄본 경험이 있는 사람이라면 의존 라이브러리가 늘어나고 플러그인 설정이 복잡해지면서 급격하게 양이 증가하는 pom.xml 파일에 당황해본 경험이 있을 것이다. 메이븐의 기본 관례와 페이즈를 그대로 준수하고 플러그인의 기본 설정을 바꾸지 않고 사용하는 경우라면 그나마 낫다. 하지만 의존 라이브러리 갯수가 늘어나는 데다 엉망으로 설정된 의존성 전이를 맞추느라 &lt;exclusion&gt;을 가득 차워야 하고, 플러그인의 기본 설정을 바꿔쓰고, 여러가지 추가 플러그인을 적용하다 보면 pom.xml이 수 백라인으로 늘어나는 것을 경험하는 것은 어렵지 않다.</p>
<p>Spring ROO가 만들어 주는 프로젝트는 메이븐 기반으로 되어있다. ROO 배포판에 있는 샘플 프로젝트인 clininc은 스프링과 하이버네이트 기본 기능만 사용하는 매우 간단한 웹 애플리케이션이다. 그런데 이 clinic 프로젝트의 pom.xml은 503라인이나 된다. 메이븐을 수 년간 써오면서 나름 pom.xml 파일에 익숙한 나 조차도 한 눈에 빌드 내용을 파악하기가 쉽지 않다. 프로젝트 pom.xml에는 나오지 않는 기본 설정(effective pom을 봐야 알 수 있는)까지 의식하면서 봐야 하는 이유도 있지만, 스크롤 하면서 앞 뒤의 내용을 살펴야 하는 불편 때문이기도 하다.</p>
<p>메이븐 pom.xml을 살펴보면 XML을 설정 파일로 사용하는 다른 제품들과 확연하게 드러나는 특징을 발견할 수 있다. 메이븐 pom.xml에서는 애트리뷰트 없이 엘리먼트만 사용한다는 점이다. pom.xml에는 메이븐 개발자들이 가진 똥고집 수준의 강력한 XML 문서 작성 철학이 적용되어있다. 엘리먼트만 사용해서 XML 문서를 작성하면 간단한 문서의 경우에 깔끔해 보인다는 장점도 있다. 파싱하기도 더 편할지 모르겠다. 하지만 문서의 길이가 대폭적으로 증가하고, 그 때문에 가독성이 떨어진다는 단점이 있다. 물론 엘리먼트만 사용해서 문서를 작성하는 것이 더 낫다고 생각하는 사람도 있을 것이다. 반대로 애트리뷰트로 세부 설정을 빼주는 것을 선호하는 개발자들도 많다.&nbsp;</p>
<p>예를 들어보자.&nbsp;</p>
<p>다음은 전형적인 메이븐의 의존 라이브러리 설정이다. 하나만 놓고 보면 나쁘지는 않다. 하지만 의존 라이브러리가 수 십개쯤 되는 프로젝트라면 여러 페이지를 스크롤 해야 전체 라이브러리의 종류가 어떤 것인지 파악할 수 있을 것이다.</p>
<p>&nbsp;</p>
<p>&lt;dependency&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;org.hsqldb&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;hsqldb&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;version&gt;1.8.0.10&lt;/version&gt;<br />
<span style="white-space: pre;"> </span>&lt;scope&gt;provided&lt;/scope&gt;<br />
&lt;/dependency&gt;</p>
<p>&nbsp;</p>
<p>애트리뷰트를 이용해서 작성할 수 있다면 어떨까? 그렇다면 다음과 같이 한 줄이면 충분할 것이다.</p>
<p>&nbsp;</p>
<p>&lt;dependency groupId=&#8221;org.hsqldb&#8221; artifactId=&#8221;hsqldb&#8221; version=&#8221;1.8.0.10&#8243; scope=&#8221;provided&#8221; /&gt;</p>
<p>&nbsp;</p>
<p>실제로 메이븐의 &lt;dependency&gt;와 비슷한 역할을 하는 Ant/Ivy의 &lt;dependency&gt;는 다음과 같이 애트리뷰터를 사용해서 간결하게 작성할 수 있다.</p>
<p>&nbsp;</p>
<p>&lt;dependency org=&#8221;com.jamonapi&#8221; name=&#8221;com.springsource.com.jamonapi&#8221; rev=&#8221;2.4.0&#8243; conf=&#8221;optional, jamon-&gt;compile&#8221;/&gt;</p>
<p>&nbsp;</p>
<p>Ivy의 경우 적지 않은 라이브러리를 사용하는 프로젝트라도 한 눈에 사용하는 라이브러리의 목록을 다 파악할 수 있고, 쉽게 편집이 가능하다.</p>
<p>메이븐의 엘리먼트 전용 XML 문서의 장황스러움은 엉성하게 작성된 전이적 의존 설정과 맞물리면 더 심각해진다. A 프로젝트가 B라는 라이브러리를 사용했을 경우에, B가 다시 의존하는 C,D,E,F,G 중에서 A 프로젝트가 실제 필요한 것은 일부인 경우가 대부분이다. B 라이브러리의 모든 잡다한 기능을 다 사용하는 것이 아니기 때문이다. 그렇다면 마땅히 B 라이브러리의 pom.xml을 작성하는 개발자는 C,D,E,F,G를 optional로 설정해주고, A 프로젝트 개발자가 이 중에서 원하는 것만 등록해서 쓰도록 해야 한다. 스프링의 경우 자신이 의존하는 라이브러리가 100개가 넘지만, commons-logging 빼고는 거의 다 optional로 되어있다. 반면에 간단한 유틸리티인데도 코드 내에서 참고하는 모든 라이브러리를 필수 의존 라이브러리로 잡아놓는 경우가 종종 있다. 그래서 테스트 라이브러리 하나 의존했을 뿐인데, 각종 DB관련 라이브러리가 모두 딸려 오거나, 심지어 jboss, tomcat이 통채로 끌려오는 경우가 있다. 메이븐의 의존성 전이 기능이 도움이 되기 보다는 피해를 주는 경우인데, 이런 폐혜가 많이 알려진 지금도 이런 문제는 자주 발견된다.</p>
<p>그래서 프로젝트의 pom을 작성하는 사람이 &lt;dependency&gt;로 가져오는 라이브러리의 pom.xml을 다시 살펴서 필요없는 라이브러리를 일일이 제외시켜줘야 한다.&nbsp;</p>
<p>ROO가 만들어준 프로젝트에 포함된 commons-dbcp 라이브러리 설정을 살펴보자.</p>
<p>&lt;dependency&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;commons-dbcp&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;commons-dbcp&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;version&gt;1.3&lt;/version&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusions&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;commons-logging&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;commons-logging&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;commons-pool&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;commons-pool&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;xerces&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;xerces&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;xerces&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;xercesImpl&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;groupId&gt;xml-apis&lt;/groupId&gt;<br />
<span style="white-space: pre;"> </span>&lt;artifactId&gt;xml-apis&lt;/artifactId&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusion&gt;<br />
<span style="white-space: pre;"> </span>&lt;/exclusions&gt;<br />
&lt;/dependency&gt;</p>
<div>commons-dbcp가 의존하는 라이브러리 중에서 4가지를 제외시켜주는 것이다. 이 설정 하나에 30줄이나 된다. 의존 라이브러리 전이 문제는 그렇다 치더라도 이 30줄이나 되는 설정을 애트리뷰트로 사용할 수 있게만 해줘도 훨씬 간결해질 것이다. 다음과 같이 고치면 6줄이면 끝이다.</div>
<p>&lt;dependency groupId=&#8221;commons-dbcp&#8221; artifactId=&#8221;commons-dbcp&#8221; version=&#8221;1.3&#8243;&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion groupId=&#8221;commons-logging&#8221; artifactId=&#8221;commons-logging&#8221; /&gt;<br />
<span style="white-space: pre;"> </span>&lt;exclusion groupId=&#8221;commons-pool&#8221; artifactId=&#8221;commons-pool&#8221; /&gt;&nbsp;<br />
<span style="white-space: pre;"> </span>&lt;exclusion groupId=&#8221;xerces&#8221; artifactId=&#8221;xercesImpl&#8221; /&gt;&nbsp;<br />
<span style="white-space: pre;"> </span>&lt;exclusion groupId=&#8221;xml-apis&#8221; artifactId=&#8221;xml-apis&#8221; /&gt;&nbsp;<br />
&lt;/dependency&gt;</p>
<p>애트리뷰트를 쓰면 더 읽기가 불편하다고 생각하는 삐딱한 눈을 가진 사람이라면 메이븐 스타일에 만족하고 쓰면 되겠다. 하지만 나는 애트리뷰트를 쓰는 게 더 좋다.&nbsp;</p>
<p>문제는 메이븐은 이런 개발자의 취향을 인정하지 않는다는 것이다. 메이븐은 /src/main/java 같은 디렉토리 구조부터 시작해서 메이븐 개발자들의 독특한 취향을 강요하고 강제한다. 그나마 디렉토리 구조는, 잘 알려져 있지 않긴 하지만 그래도 변경할 여지라도 줬다. 하지만 XML 작성 방법은 개발자들의 수 많은 요구가 있었음에도 3.0.3까지 나온 지금까지 요지부동이다.&nbsp;</p>
<p>베타 버전이기는 하지만 &nbsp;메이븐 Polyglot 프로젝트가 만들어져서 ruby, scala, atom 등으로 메이븐 POM 파일을 작성하는 방법까지 가능해졌다. 하지만 여전히 XML 작성 방식은 그대로다.</p>
<p>그래서 지난 주말에 잠시 짬을 내서 메이븐 pom.xml을 애트리뷰트를 사용해서 작성할 수 있도록 메이븐을 조금 수정해봤다. 생각보다 어렵지 않게 가능했다.&nbsp;</p>
<p>올해 들어서 처음 쓰는 블로그는 그 얘기를 해보려고 하는데&#8230; 벌써 업무시간이 되었네. 다음에 계속.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1146</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1146</feedburner:origLink></item>
		<item>
		<title>테스트 코드에서 static import를 편하게 넣는 방법</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/LZi0ckaU-Xg/</link>
		<comments>http://toby.epril.com/?p=1126#comments</comments>
		<pubDate>Thu, 09 Dec 2010 06:42:11 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Tool]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1126</guid>
		<description><![CDATA[import static으로 시작하는 static import는 주로 유틸리티성 메소드에 사용하는 스태틱 메소드나 스태틱 상수값 등을 사용할 때 적용하기 좋다. 지저분하게 클래스 이름이 주렁주렁 나오는 대신 깔끔하게 메소드 이름이나 값만 사용할 수 있으니 코드가 단순해지고 이해하기도 좋다. 물론 쓰기 편하다고 스태틱 메소드를 남용하면 안되겠지만. 자바 프로젝트에서 스태틱 메소드가 가장 활발하게 사용되는 곳은 테스트 코드다. JUnit 4.x 는 <a href='http://toby.epril.com/?p=1126'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1126"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1126&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>import static으로 시작하는 static import는 주로 유틸리티성 메소드에 사용하는 스태틱 메소드나 스태틱 상수값 등을 사용할 때 적용하기 좋다. 지저분하게 클래스 이름이 주렁주렁 나오는 대신 깔끔하게 메소드 이름이나 값만 사용할 수 있으니 코드가 단순해지고 이해하기도 좋다. 물론 쓰기 편하다고 스태틱 메소드를 남용하면 안되겠지만.</p>
<p>자바 프로젝트에서 스태틱 메소드가 가장 활발하게 사용되는 곳은 테스트 코드다. JUnit 4.x 는 3.x까지와 다르게 수퍼 클래스를 상속하지 않고 테스트 클래스를 작성한다. 그래서 테스트 클래스가 별도의 상속구조를 가지거나 테스트 할 대상을 상속해서 테스트 클래스를 만드는 등의 기법을 적용하기 좋다. 반면에 상속을 하지 않으니 예전에 TestCase와 같은 수퍼클래스에 정의되어있던 assertEquals() 따위를 바로 사용할 수 없다. 어짜피 assertXXX 메소드는 독립적으로 실행되는 유틸리티성 메소드다. 스태틱 메소드로 만들어 적용하기 적절한 대상이다. </p>
<p>JUnit 4.x는 이런 메소드들을 org.junit.Assert 클래스에 스태틱 메소드로 정의해 두었다. JUnit 4.x에서 테스트 코드를 만들면 Assert.assertEquals() 처럼 스태틱 메소드를 써야 한다. 매번 Assert 클래스 이름이 나오는 것은 지저분하니 보통 static import를 사용하고 assertEquals()와 같은 식으로 간단히 코드에서 사용하게 된다. </p>
<p>Assert 메소드 뿐 아니다. 내가 애용하는 assertThat()의 경우는 hamcrest의 Matcher를 사용하는데 이때도 CoreMatchers 클래스의 is() 같은 스태틱 메소드를 사용하게 된다. Hamcrest Matcher를 확장한 JUnit이나 다른 오픈소스 Matcher를 이용한다면 해당 Matcher 스태틱 메소드도 import할 필요가 있다. </p>
<p>또 목 오브젝트를 사용한다면 EasyMock이나 Mockito 등에서 정의한 목 생성, 조작용 스태틱 메소드나 Mockito Matcher 스태틱 메소드도 사용해야 할 것이다. </p>
<p>이러다 보니 테스트 클래스를 만들 때마다 넣어줘야 할 static import가 제법 많다. </p>
<p>문제는 이클립스의 편리한 타입 검색, 자동 import 등의 기능이 스태틱 메소드에는 기본적으로 적용되자 않는다는 것이다. 프로젝트에서 사용하는 타입 정보야 미리 다 읽어들여서 몇 글자만 넣어도 빠르게 찾도록 만들어두었지만, 메소드 레벨까지 모두 지원하는 것은 너무 큰 부담이기 때문이다. 그래서 테스트 코드를 작성할 때 제법 많은 static import 문을 작성해야 하는데&nbsp; 상당히 번거롭다. </p>
<p>그래서 최신 이클립스에서는 static import를 보다 편하게 추가할 수 있도록 Favorites 라는 기능을 제공한다. 코드에서 자주 사용하는 스태틱 메소드나 스태틱 메소드를 가진 타입을 미리 Favorites에 등록해두면 해당 스태틱 메소드도 자동검색/완성 기능의 대상이 된다. 코드에서 assertThat이라고 입력하고 ctrl-space를 누르면 assertThat() 메소드를 바로 선택할 수 있고, 메소드를 사용하기 위해서 필요한 import 문도 자동으로 추가된다. 그래서 자주 쓰는 테스트용 스태틱 메소드를 등록해두고 사용하면 편리하다. </p>
<p>Favorites는 Preference의 Java – Editor &#8211; ContentAssist 아래 있다.</p>
<p>&nbsp;</p>
<p>그런데 나는 Favorites을 사용하지 않는다. 한때 잠깐 쓰기는 했지만.</p>
<p>나는 코드를 작성할 때, 특히 테스트 코드를 작성할 때 리듬을 타면서 빠르게 코드를 완성해나가는 것을 즐긴다. 그런데 테스트 코드를 작성하다가 자꾸 static import를 위해서 타이핑을 멈추고 ctrl-space을 누르고 선택하는 것은 번거롭다. 한 두개래야지. 게다가 자동완성을 안하고 타이핑을 다 해버리면 끝장이다. 예를 들어 assertThat에서 멈추고 자동완성을 해서 import를 추가해줘야 하는데 그냥 빠르게 assertThat(xxx, is( yyyy )); 까지 치고 나면 당황스럽다. 그제서 키보드를 옮겨서 assertThat 뒤로 가서 ctrl-space를 누르고 assertThat() 메소드를 선택하면 import 만 깔끔하게 추가되야 할텐데, 바보 같은 이클립스는 메소드 템플릿을 다 추가해버린다. 그래서 코드가 assertThat(actual, matcher)xxx, is( yyy )); 처럼 볼쌍 사납게 변해버린다. (actual, matcher)를 지우고, 또 is 뒤에 가서 같은 짓을 반복해야 한다.</p>
<p>리듬은 끊기고 짜증만 난다. 그래서 테스트를 많이 작성하는 편인 나에게는 Favorites 기능이 그다지 매력적으로 느껴지지 않는다. 테스트 코드 작성을 위해서 Favorites 기능을 추천해주는 사람을 보면 과연 테스트 코드를 제대로 작성하고 있는 사람인지 의심부터 간다. –_-;;</p>
<p>&nbsp;</p>
<p>그래서 Favorites는 조금 사용하다가 때려쳤다. 대신 static import를 위해서 Templates을 사용한다. Preference &#8211; Java – Editor 밑에 있는 Templates은 미리 간단한 이름를 정해놓고 이를 타이핑하고 ctrl-space를 누르면 미리 넣어둔 코드 조각(템플릿)이 한방에 나오는 기능이다. main은 main() 메소드를 만들 때 사용하고 sysout는 Sytem.out.println()을 만들어준다. 반복적으로 나오는 코드 패턴에 적용하기 좋다. 나는 여기에 ti라는 이름으로 다음과 같은 코드를 넣어놨다. ti는 test imports&#8230;</p>
<p>import static org.hamcrest.CoreMatchers.*;<br />import static org.junit.Assert.*;<br />import static org.junit.matchers.JUnitMatchers.*;<br />import static org.mockito.Matchers.*;<br />import static org.mockito.Mockito.*;</p>
<p>자주 쓰는 static import 문장을 * 와일드카드를 적용해서 통채로 넣어둔 것이다. 그리고 테스트 클래스를 만들면 제일 먼저 위에서 ti라고 치고 ctrl-space를 눌러서 이 import문을 넣는다. 그러면 끝. 이제 스태틱 메소드를 맘것 사용하면 된다. 테스트 코드를 복사할 때도 아무런 문제가 없다. 그냥 빠르게 타이핑 해도 된다. Favorites로 매번 번거롭게 하나씩 import 를 추가하는 것보다 100배 낫다.</p>
<p>대충 테스트 코드가 완성되면 ctrl-shift-O를 눌러서 organize imports를 한번 해주면 깔끔한 import 문으로 정렬된다. 언제든지 import 하지 않은 것이 있으면 한번만 ti 템플릿을 적용해주면 그만이다. </p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1126</wfw:commentRss>
		<slash:comments>11</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1126</feedburner:origLink></item>
		<item>
		<title>톰캣 앞에 아파치 웹 서버(Httpd)를 두어야 할까?</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/NUYhgfdorgk/</link>
		<comments>http://toby.epril.com/?p=1125#comments</comments>
		<pubDate>Tue, 30 Nov 2010 06:53:21 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Tool]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[tomcat]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1125</guid>
		<description><![CDATA[얼마전에 성철이 형이 물어봐서 답해줬던 내용이다. 많은 개발자들이 애플리케이션 서버로 톰캣을 사용하는 경우에 스태틱 파일(html, css, js, 이미지)은 톰캣 앞에 아파치 웹 서버(Httpd)를 두어서 처리하게 하는 것이 좋다고 생각한다. 외부의 요청은 일단 Apache Httpd가 받고, 톰캣 내에서 처리할 자바 애플리케이션만 톰캣으로 다시 전달해서 처리하고 그 외의 리소스는 Apache Httpd가 직접 처리하게 만들어야 성능이 좋다고 생각한다. <a href='http://toby.epril.com/?p=1125'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1125"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1125&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>얼마전에 성철이 형이 물어봐서 답해줬던 내용이다. </p>
<p>많은 개발자들이 애플리케이션 서버로 톰캣을 사용하는 경우에 스태틱 파일(html, css, js, 이미지)은 톰캣 앞에 아파치 웹 서버(Httpd)를 두어서 처리하게 하는 것이 좋다고 생각한다. 외부의 요청은 일단 Apache Httpd가 받고, 톰캣 내에서 처리할 자바 애플리케이션만 톰캣으로 다시 전달해서 처리하고 그 외의 리소스는 Apache Httpd가 직접 처리하게 만들어야 성능이 좋다고 생각한다. 자바로 만든 서버인 톰캣은 스태틱 파일 처리에서 Apache Httpd만 못하다는 것이 그 이유다.</p>
<p>하지만 톰캣과 Httpd의 개발자에 따르면 이는 개발자들이 잘못 알고 있는 미신이다. 아직도 톰캣 3를 사용하고 있는 것이 아니라면 말이다.</p>
<p>자세한 내용은 <a href="http://www.tomcatexpert.com/blog/2010/03/24/myth-or-truth-one-should-always-use-apache-httpd-front-apache-tomcat-improve-perform">Myth or truth: One should always use Apache httpd in front of Apache Tomcat to improve performance?</a>에 잘 나와있다. 지금은 스프링 소스의 직원이 된 아파치 톰캣과 Httpd 의 핵심 개발자들이 직접 작성한 내용이다.</p>
<p>톰캣은 5.5부터 Httpd의 native 모듈을 사용해서 스태틱 파일을 처리하는 기능을 제공한다. 이 경우 Httpd와 톰캣이 같은 모듈을 사용하는 셈이니 성능에서 차이가 날 이유가 없다. 실제 테스트 한 결과를 봐도 톰캣에서 아파치 Native 모듈을 사용하는 것이 순수하게 아파치 Httpd만 사용하는 것과 비교해서 성능이 전혀 떨어지지 않는다.</p>
<p>따라서 단지 스태틱 파일 처리의 성능만을 위해서라면 굳이 톰캣 앞에 Apache Httpd를 두는 것은 불필요하다. 오히려 메모리만 많이 먹고, 관리부담은 커지고, 불필요한 부하만 걸릴 뿐이다.</p>
<p>물론 Httpd의 다른 기능이나 모듈을 사용해야 할 필요가 있다면 그때는 Httpd를 앞에 두고 사용해야겠지만. 예를 들어 하나의 서버에서 PHP 애플리케이션과 자바 애플리케이션을 함께 사용하거나, Httpd 서버를 간단한 로드밸런싱을 위해서 사용해야 하는 경우라면 Httpd를 앞에 두고 톰캣을 연결해서 사용하도록 하면 될 것이다.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1125</wfw:commentRss>
		<slash:comments>16</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1125</feedburner:origLink></item>
		<item>
		<title>Maven 3.0과 버전 포맷 문제</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/Fo5rEvp6drY/</link>
		<comments>http://toby.epril.com/?p=1124#comments</comments>
		<pubDate>Fri, 29 Oct 2010 00:48:07 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[maven]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1124</guid>
		<description><![CDATA[Maven 3.0 정식버전이 출시됐다. (http://maven.apache.org/docs/3.0/release-notes.html) Maven 3.0은 2.x와의 호환성을 최대한 유지하면서 성능을 대폭 개선했고 안정성을 높였다고 한다. 이클립스에서 m2eclipse 플러그인을 사용하고 있다면 이미 Maven 3.0 엔진을 사용하고 있는 셈이다. m2e 플러그인 설정에서 별도로 설치된 2.x버전을 선택했다고 하더라도 프로젝트에 직접 맞물려서 돌아가는 의존 라이브러리 관리 기능은 내장형 3.0 엔진으로 동작한다. Maven 2.x로 작업하면서 하도 고생을 많이 <a href='http://toby.epril.com/?p=1124'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1124"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1124&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>Maven 3.0 정식버전이 출시됐다. (<a href="http://maven.apache.org/docs/3.0/release-notes.html">http://maven.apache.org/docs/3.0/release-notes.html</a>)</p>
<p>Maven 3.0은 2.x와의 호환성을 최대한 유지하면서 성능을 대폭 개선했고 안정성을 높였다고 한다. 이클립스에서 m2eclipse 플러그인을 사용하고 있다면 이미 Maven 3.0 엔진을 사용하고 있는 셈이다. m2e 플러그인 설정에서 별도로 설치된 2.x버전을 선택했다고 하더라도 프로젝트에 직접 맞물려서 돌아가는 의존 라이브러리 관리 기능은 내장형 3.0 엔진으로 동작한다. </p>
<p>Maven 2.x로 작업하면서 하도 고생을 많이 해서 한동안 ANT/Ivy로 넘어갈까도 생각했지만, m2eclipse라는 화려한 이클립스 플러그인의 지원과 Sonatype을 중심으로 한 Maven 개발자들의 열정 때문에 그대로 Maven으로 남기로 했다. 사실 ANT/Ivy로 깔끔한 빌드 체계를 만드는 것은 결코 쉬운 일도 아니기도 하고. 또, Maven의 많은 단점이 3.0에서 대폭 개선될 것이라는 기대도 한몫했을 것이다.</p>
<p>최근에 시작한 프로젝트에 Maven 3.0을 적용하고 있는데 여러모로 맘에 든다. 확실히 경고 메시지가 이전보다 꼼꼼하고 상세하게 나와서 POM 작성에 도움이 된다. 성능은 멀티 모듈을 쓰지 않으니 잘 모르겠고. </p>
<p>사실 가장 맘에 드는 것은 예전에 나를 괴롭혔던 Maven 버전 체계다. 특히 OSGi 버전 포맷과의 충돌 때문에 스프링소스 리포지토리의 번들을 사용할 때 짜증났던 점들이 모두 개선됐다. </p>
<p>Maven 2.x대의 버전 문제에 대해서는 예전에 기록해둔 글이 두 개 있다. </p>
<ul>
<li>Maven의 version range를 사용할 때 주의할 점 (<a href="http://toby.epril.com/?p=610">http://toby.epril.com/?p=610</a>)</li>
<li>Maven과 OSGi(Spring)의 버전포맷 비호환 문제 (<a href="http://toby.epril.com/?p=647">http://toby.epril.com/?p=647</a>)</li>
</ul>
<p>Maven의 버전 포맷과 이를 해석하는 방법을 정확하게 이해하지 않으면 버전 범위를 적용하거나 버전 충돌이 있는 경우에 까다로운 문제가 발생할 수 있다.</p>
<p>Maven의 기본 버전 포맷은 a.b.c-d 구조로 되어있다. a, b, c는 각각 숫자로 된 버전 번호고 d는 추가 식별자이다. 1.0.0-M1 이라고 하면 각각 1, 0, 0, M1으로 해석된다. 문제는 기존 Maven은 정확히 이 포맷을 따르지 않으면 버전 전체를 식별자 취급을 해버린다는 점이다. 그래서 OSGi 버전 포맷을 따르는 스프링 번들이 3.0.0.RELEASE고 되어있으면 3, 0, 0, RELEASE로 해석되는 대신 0, 0, 0, 3.0.0.RELASE라고 해석되버린다. OSGi 버전포맷은 -대신 .을 사용하기 때문이다. 그래서 3.0.0.RELEASE는 1.0.0보다도 더 낮은 버전이 되버리는 문제가 발생한다.</p>
<p>Maven 2.x에서 스프링 3.0.x 대의 가장 최신 버전을 자동 선택하고 싶어서 [3.0.0, 3.1.0)와 같은 버전 범위를 사용하면 3.0.0.RELASE, 3.0.5.RELASE와 같은 버전은 아예 찾지 못해서 에러가 난다. 의존 관계를 따져보다 버전이 충돌이 났을 때 2.5.6과 3.0.5.RELASE 두개의 같은 라이브러리가 발견되면 2.5.6을 더 최신으로 해석해버리기도 한다. 3.0.5.RELASE와 같은 포맷을 제대로 인식 못하기 때문이다. 게다가 Maven은 a,b,c 없이 d만 존재하는 버전을 허용하기 때문에 에러도 안난다. 그냥 0.0.0 버전이면서 식별자가 3.0.5.RELEASE라고 지정된 것이라고 해석해버린다. 게다가 내부에서 해석한 버전 정보는 아무리 로그레벨을 낮춰도 출력이 안되기 때문에 개발자 입장에서는 이게 안되면 매우 난처할 수 밖에 없다.</p>
<p>이 문제는 오래전 2.0.x 시절부터 계속 지적된 것이지만 Maven 개발자들은 3에서 개선하겠다고만 하고 2.x대는 버전 처리 방식은 그대로 방치해 두었다. 오픈소스긴 하지만 오픈 프로젝트는 아닌지라 커뮤니티의 불만이나 의견 따위는 독선적인 소수 커미터들에게 그렇게 무시당하기 일수다.</p>
<p>아무튼.. 소스를 디버깅해가면서 버전 처리 방식을 연구하게 만들만큼 한동안 나를 괴롭혔던 이 문제는 3.0에서 해결 됐다.</p>
<p>이제는 [3.0.0,3.1.0)이라고 버전을 지정하면 정확하게 3.0.5.RELASE를 가져온다. STS 2.5.0에서 Maven 2.2.1과 3.0을 바꿔가며 비교해봤을 때 확실하게 버전 처리 방식이 달라진 것을 확인할 수 있었다. 이제는 3.0.0.RELASE를 3, 0, 0, RELEASE로 정확히 해석하는 것 같다. 내부 리포지토리로 Nexus 1.8을 적용했는데 3.0의 새로운 버전 해석 방식과 잘 호환된다.</p>
<p>개선된 버전 처리 방식 하나 때문에라도 앞으로는 3.0을 사용해야겠다.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1124</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1124</feedburner:origLink></item>
		<item>
		<title>행복한 IT인</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/TICxEdCSMw0/</link>
		<comments>http://toby.epril.com/?p=1123#comments</comments>
		<pubDate>Thu, 28 Oct 2010 06:14:16 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Java & IT]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1123</guid>
		<description />
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1123"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1123&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><object width="640" height="390"><param name="movie" value="http://www.youtube.com/v/23ZhbZzMorU&amp;hl=en_US&amp;feature=player_embedded&amp;version=3"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/23ZhbZzMorU&amp;hl=en_US&amp;feature=player_embedded&amp;version=3" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="640" height="390"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1123</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1123</feedburner:origLink></item>
		<item>
		<title>추억의 오픈시드 사진</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/euUSp45J2bg/</link>
		<comments>http://toby.epril.com/?p=1122#comments</comments>
		<pubDate>Thu, 30 Sep 2010 13:02:16 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Etc]]></category>
		<category><![CDATA[오픈시드]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1122</guid>
		<description><![CDATA[&#50724;&#54536;&#49548;&#49828; &#44592;&#49696;&#51060; &#50644;&#53552;&#54532;&#46972;&#51060;&#51592; &#54872;&#44221;&#50640;&#49436; &#49324;&#50857;&#46104;&#44592;&#47484; &#45000;&#44984;&#47728; &#47564;&#46308;&#50612;&#51652; &#50724;&#54536;&#49884;&#46300;. &#50668;&#47084;&#48264; &#50724;&#54532;&#46972;&#51064; &#47784;&#51076;&#51012; &#54664;&#51648;&#47564; &#51648;&#44552; &#52286;&#51012; &#49688; &#51080;&#45716; &#49324;&#51652;&#51008; &#51060;&#44163; &#49104;&#51060;&#45348;. &#48268;&#50024; 4&#45380; &#51204;&#51060;&#44396;&#45208;.]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1122"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1122&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>&#50724;&#54536;&#49548;&#49828; &#44592;&#49696;&#51060; &#50644;&#53552;&#54532;&#46972;&#51060;&#51592; &#54872;&#44221;&#50640;&#49436; &#49324;&#50857;&#46104;&#44592;&#47484; &#45000;&#44984;&#47728; &#47564;&#46308;&#50612;&#51652; &#50724;&#54536;&#49884;&#46300;. &#50668;&#47084;&#48264; &#50724;&#54532;&#46972;&#51064; &#47784;&#51076;&#51012; &#54664;&#51648;&#47564; &#51648;&#44552; &#52286;&#51012; &#49688; &#51080;&#45716; &#49324;&#51652;&#51008; &#51060;&#44163; &#49104;&#51060;&#45348;. &#48268;&#50024; 4&#45380; &#51204;&#51060;&#44396;&#45208;.</p>
<p><img src="http://cfs1.tistory.com/upload_control/download.blog?fhandle=YmxvZzU4NzRAZnMxLnRpc3RvcnkuY29tOi9hdHRhY2gvMC85LmpwZw%3D%3D" /></p>
<p><img src="http://cfs1.tistory.com/upload_control/download.blog?fhandle=YmxvZzU4NzRAZnMxLnRpc3RvcnkuY29tOi9hdHRhY2gvMC84LmpwZw%3D%3D" /></p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1122</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1122</feedburner:origLink></item>
		<item>
		<title>시드니 여행 (1)</title>
		<link>http://feedproxy.google.com/~r/tobyepril/~3/COuqrWDZvBs/</link>
		<comments>http://toby.epril.com/?p=1121#comments</comments>
		<pubDate>Fri, 17 Sep 2010 21:05:49 +0000</pubDate>
		<dc:creator>Toby</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[시드니]]></category>
		<category><![CDATA[여행]]></category>

		<guid isPermaLink="false">http://toby.epril.com/?p=1121</guid>
		<description><![CDATA[원래 책을 다 쓰고 나면 차를 몰고 호주 일주 여행을 할 생각이었다. 원래 처음 호주에 정착할 때부터 하려고 마음 먹었던 것인데 계속 일과 삶에 치이면서 미뤄졌다. 나는 비행기로 도시 사이를 이동하면서 여행하는 것보다는 차로 여행하는 것을 더 좋아한다. 멋진 자연을 마음 것 즐길 수 있고 사람 사는 동네 모습을 구경할 수 있기 때문이다. 무엇보다도 여정을 <a href='http://toby.epril.com/?p=1121'>[...]</a>]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1121"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Ftoby.epril.com%2F%3Fp%3D1121&amp;style=normal&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>원래 책을 다 쓰고 나면 차를 몰고 호주 일주 여행을 할 생각이었다. 원래 처음 호주에 정착할 때부터 하려고 마음 먹었던 것인데 계속 일과 삶에 치이면서 미뤄졌다. 나는 비행기로 도시 사이를 이동하면서 여행하는 것보다는 차로 여행하는 것을 더 좋아한다. 멋진 자연을 마음 것 즐길 수 있고 사람 사는 동네 모습을 구경할 수 있기 때문이다. 무엇보다도 여정을 마음대로 선택하고 변경할 수 있는 자유가 있어서 좋다. 하지만 호주 일주 여행의 꿈은 하늘이를 가졌다는 것을 알면서 날라가버렸다. 아이 둘을 엄마한테 맡겨놓고 나 혼자 몇 달의 시간을 낼 수는 없으니까.</p>
<p>그래서 소박하게 시드니나 다녀오기로 했다. 책을 마칠 때쯤이면 아내 입덧도 끝날테고 하늘이가 나오기 전에 시드니로 가볍게 여행할 생각이었다. 하지만 책의 마무리가 늦어지면서 그것도 때를 놓쳤다. 결국 시드니를 다녀오는 것도 포기.</p>
<p>하늘이가 태어나니 호주 일주, 시드니는 커녕 가까이에 있는 골드코스트나 선샤인코스트도 한번 다녀오기 힘들었다. 다행히 장모님이 오셔서 산후조리를 도와주고 계시니 시간은 낼 수 있었지만 그럴만한 마음의 여유가 없었다. 책 쓰는 동안 미뤄두었던 회사 업무를 본격적으로 다시 시작해야 했기 때문이다. 책을 쓰는 데 전념했던 지난 2년 동안은 시간을 들여야 하는 다른 일은 가능한 하지 않으려고 했기 때문에 수입이 많지 않아서 계속 적자였다. 그러니 회사 일을 다시 셋업할 때까지는 한가하게 여행이나 다닐 수는 없었다. </p>
<p>그러는 중에 시드니에 있는 한 업체로부터 컨설팅 요청을 받았다. 내가 살고 있는 브리즈번은 호주에서 세 번째로 큰 도시긴 하지만, 호주 자체가 워낙 작은 나라(인구가 겨우 2천만)인데다 모든 경제와 산업이 시드니와 멜번에 집중되어있기 때문에 여기서는 IT관련 일을 찾기가 쉽지 않다. 종종 컨설팅이나 개발 의뢰가 들어오긴 하지만 대부분 시드니나 멜번에 상주하면서 일을 하기 원해서 거절할 수 밖에 없었다. 그런데 이번에 요청 받은 일은 다행히도 원격에서 작업이 가능한 것이었다. 그래도 초기에 중요한 기술적인 결정을 내리기 위해서는 고객사를 방문해서 긴밀히 협의할 필요도 있고, 또 신뢰감을 쌓을 수 있도록 얼굴을 보며 만남을 가질 필요도 있기 때문에 며칠 시간을 내서 다녀오기로 했다.</p>
<p>브리즈번에서 시드니까지 거리는 대략 1000킬로쯤 된다. 비행기로 가면 한시간이 조금 넘게 걸리는 거리다. 하루에도 수십번씩 비행기가 다니는, 호주에서 가장 인기있는 코스라 간단히 다녀올 수도 있지만 이번에는 일부러 차를 가져가기로 했다. 비즈니스 여행이지만 개인적인 여행의 기회로도 활용하고 싶어서다. 그래서 여유있게 차로 출발해서 중간에 하루 적당한 곳에서 쉬었다 갈 생각이다. 시드니에서도 고객을 만나는 시간 외에는 추억의 장소를 찾아보기도 하고 그동안 보고 싶었던 시드니의 지인들을 만나고 싶다.</p>
<p>브리즈번에서 시드니로 가는 경로는 크게 두 가지다. 하나는 바닷가를 따라가는 Pacific highway를 이용하는 것이고, 다른 하나는 내륙으로 돌아가는 New English highway를 이용하는 것이다. 바닷가를 따라가는 길은 경로도 짧고 다양한 볼 거리도 많다. 반면에 내륙으로 가는 길은 거리는 조금 먼 대신 10분을 달려야 차 한대 구경할까 싶을 정도로 여유가 있다. 넓은 평야와 멋진 산과 같은 자연을 마음 것 즐기면서 달릴 수 있다. </p>
<p><img src="http://lh4.ggpht.com/_kkTwptcCrA0/TJPUfT1n7dI/AAAAAAAABo4/CvPqCPP0GG0/s800/ScreenHunter_01 Sep. 18 06.47.gif" /> </p>
<p>일단 시드니로 갈때는 Pacific Highway를 이용할 생각이다. 구글 맵으로 뽑아보니 시드니 목적지 까지 표준 속도로 가면 12시간쯤 걸린다. 마음 먹고 달리면 하루에 충분히 갈 수 있는 거리지만, 나는 중간 중간에 구경도 하면서 슬슬 가다가 적당한 곳에서 하루쯤 묶을 생각이다. 돌아올 때는 New Englad Highway를 이용할 생각이고.&#160; 이름은 둘다 hightway지만 한국의 고속도로처럼 자동차 전용도로인 고속 구간은 그리 많지 않다. 대부분은 한국의 국도처럼 되어있고 가다보면 동네 사이로 지나가는 곳도 많다. 그래서 더 볼거리가 많다.</p>
<p>이번 여행에서 가장 기대되는 것은 다음 아닌 스마트폰이다. 예전엔 지도 책 한 권에 의지해서 길을 찾아 다니고 동네마다 있는 information centre에 가서 지역 정보를 얻어서 참고하곤 했는데 이번엔 지도나 다른 정보 없이 스마트폰에만 의지해서 다녀볼 생각이다. 구글맵으로 지도를 찾아보고 가까운 숙소나 식당, 구경거리도 살펴볼 생각이다. 카메라를 따로 가져가기는 하지만 간단히 사진을 남기고 싶을 때는 스마트폰으로 찍어서 버즈와 미투데이로 바로 올릴 수 있을 것이다. 스마트폰을 이용해서 여행하는 것은 어떤 느낌일지 궁금하다. 한가지 안타까운 것은 내가 쓰던 아이폰이 지금 수리 때문에 서비스 센터에 가있다는 것. 스피커 소리가 작아져서 서비스 신청을 하고 우편으로 발송한 바로 그날 시드니를 방문해달라는 요청을 받았다. 그래서 이번에는 아내의 갤스를 빌려서 가기로 했다. 여행정보도 풍부한 아이폰만큼은 못하겠지만 그럭저럭 쓸만하겠지.</p>
<p>차. 이제 짐을 싣고 출발이다. </p>
<p>달려 보실까.</p>
]]></content:encoded>
			<wfw:commentRss>http://toby.epril.com/?feed=rss2&amp;p=1121</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://toby.epril.com/?p=1121</feedburner:origLink></item>
	</channel>
</rss>

