<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/style/rss/style.xsl" type="text/xsl" media="screen"?>
<rss version="2.0">
	<channel>
		<title>a crossbow of  anarcher</title>
		<link>http://anarch.tistory.com/</link>
		<description>그냥저냥 블로그</description>
		<language>ko</language>
		<pubDate>Sun, 28 Sep 2008 17:42:42 +0900</pubDate>
		<generator>Tistory 1.1 (http://www.tistory.com/)</generator>
		<image>
		<title>a crossbow of  anarcher</title>
		<url><![CDATA[http://cfs6.tistory.com/upload_control/download.blog?fhandle=YmxvZzEzOTY5QGZzNi50aXN0b3J5LmNvbTovYXR0YWNoLzAvMDEwMDAwMDAwMDAwLmdpZg==]]></url>
		<link>http://anarch.tistory.com/</link>
		<description>그냥저냥 블로그</description>
		</image>
		<item>
			<title>SView : S(cala|imple) View for Spring MVC = {</title>
			<link>http://anarch.tistory.com/104</link>
			<description>&lt;h3&gt;SView : S(cala|imple) View for Spring MVC&amp;nbsp;&lt;/h3&gt;
&lt;h4&gt;들어가는 말&lt;/h4&gt;
&lt;p&gt;smalltalk부터 시작된 MVC 패턴은 SoC(Separation of Concerns, 관심 분리)이라는 관점에서 응용 프로그램의 복잡도를 관리 할수 있는 아키텍쳐 패턴으로 인기가 있습니다.&lt;/p&gt;
&lt;p&gt;특히 웹에서는 MVC 패턴을 구현한 많은 프레임워크가 있는데, 다른 대부분의 웹 프레임워크와 마찬가지로 Spring WebMVC는 여러 뷰/프리젠테이션 기술을 통합하여 사용할수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Scala는&amp;nbsp; JavaVM이나 .NET CLR위에서 동작하는 객체지향 프로그래밍과 함수형 프로그래밍을 혼합한 multi-paradigm 언어입니다.&amp;nbsp; 여러가지 흥미로운 특징이 있지만, 객체지향 기반의 패턴 매칭과 언어 신택스차원에서 지원하는 XML Processing은 XML 처리에 용이한 특징이 있습니다.&lt;/p&gt;
&lt;p&gt;위의 이러한 특징들을 이용하여 &amp;nbsp;SpringMVC의 View을 Scala의 XML Processing을 이용해서 구현해보았습니다.&lt;/p&gt;
&lt;p&gt;관련 소스는 &lt;a href=&quot;http://github.com/anarcher/sview/tree/master&quot;&gt;http://github.com/anarcher/sview/tree/master&lt;/a&gt; 에 있으며, git을 이용해서 받으실수 있습니다. ( &lt;code&gt;git clone git://github.com/anarcher/sview.git&lt;/code&gt; )&lt;/p&gt;
&lt;h4&gt;SpringMVC의 View,ViewResolver&lt;/h4&gt;
&lt;p&gt;대개의 MVC 프레임워크가 그러하듯이 Spring WebMVC도 View을 선택하는 기능을 제공합니다. 이 부분에 대한 중요한 두개의 인터페이스가 있는데, org.springframework.web.servlet.View와 org.springframework.web.servlet.ViewResolver입니다.&lt;/p&gt;
&lt;p&gt;ViewResolver은 말 그대로 viewName 과 실제 View을 결정해 주는 객체입니다. View는 넘어온 Request을 해당 View 구현체에게 위임하여 처리하는 객체입니다.&lt;/p&gt;
&lt;p&gt;특히 ViewResolver는 대개 Ordered 인터페이스를 구현하여 여러 ViewResolver을&amp;nbsp; 등록하여 &amp;nbsp;사용 할 수 있게 구현합니다. (Chain of responsibility)&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&amp;lt;bean id=&quot;sviewBeanNameViewResolver&quot; class=&quot;sview.SViewBeanNameViewResolver&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;order&quot; value=&quot;1&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;prettyPrint&quot; value=&quot;true&quot; /&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;sviewFactoryBeanName&quot; value=&quot;sviewSpringBeanFactory&quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/bean&amp;gt;&lt;br /&gt;
&amp;lt;bean id=&quot;sviewSpringBeanFactory&quot; class=&quot;sview.SViewSpringBeanFactory&quot;&amp;gt;&amp;lt;/bean&amp;gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;lt;bean id=&quot;jspViewResolver&quot; class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;viewClass&quot;&amp;gt;&amp;lt;value&amp;gt;org.springframework.web.servlet.view.JstlView&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;prefix&quot;&amp;gt;&amp;lt;value&amp;gt;/WEB-INF/views/&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;suffix&quot;&amp;gt;&amp;lt;value&amp;gt;.jsp&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt;&lt;br /&gt;
&amp;lt;/bean&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ViewResolver의 구현상속을 하려면&amp;nbsp; View resolveViewName(String viewName,Locale locale)이라는 메소드를 구현해야 합니다.&lt;/p&gt;
&lt;p&gt;즉 전달받은 ViewName으로 View을 결정해 달라는 부분이라고 할수 있습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public class SViewBeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver,Ordered {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private final static String DELIMITER = &quot;sview:&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private int order = Integer.MAX_VALUE;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private String sviewFactoryBeanName = &quot;sviewSpringBeanFactory&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private boolean isPrettyPrint = true;&lt;/li&gt;
&lt;li&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setOrder(int order) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.order = order;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public int getOrder() {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return order;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setSviewFactoryBeanName(String sviewFactoryBeanName) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.sviewFactoryBeanName = sviewFactoryBeanName;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setPrettyPrint(boolean isPrettyPrint) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.isPrettyPrint = isPrettyPrint;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public View resolveViewName(String viewName,Locale locale) throws Exception {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(viewName.indexOf(DELIMITER) &amp;lt; 0) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return null;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String sviewName = viewName.substring(viewName.indexOf(DELIMITER)+6);&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WebApplicationContext ctx = super.getWebApplicationContext();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SViewFactory sviewFactory = (SViewFactory) ctx.getBean(this.sviewFactoryBeanName);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ViewAdapter view = new ViewAdapter();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; view.setSViewName(sviewName);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; view.setSViewFactory(sviewFactory);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; view.setPrettyPrint(isPrettyPrint);&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return view;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 코드는 ViewResolver의 구현클래스으로, viewName중 DELIMITER으로 시작되는 viewName인지를 검출하고, 스프링 컨텍스트에서 해당 이름으로 등록된 sviewFactory을 얻어 옵니다.&lt;/p&gt;
&lt;p&gt;ViewAdapter 는 View 인터페이스의 구현클래스이고 전달받은 sviewFactory을 가지고 해당 sview을 얻어서, 랜더링하는(sviewRenderer)하는 책임이 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;Scala의 XML Processing&lt;/h4&gt;
&lt;p&gt;Scala의 XML Processing은 xml을 문법 신택스처럼 사용할수 있습니다. 특히 xml을 코드상에 작성하면 scala 컴파일러가 xml 신택스를 scala.xml에 있는 객체로 교체해줍니다. 정확한 비교는 아니지만, 우리가 문자열을 변수에 할당 할때, String s = &quot;hello&quot; 과 String s = new String(&quot;hello&quot;)와 같은 것 처럼, scala에서는 val xml =&amp;lt;xml&amp;gt;hello&amp;lt;/xml&amp;gt;이 val xml = Elem(null, &quot;xml&quot;, Null, TopScope,Text(&quot;hello&quot;))와 같습니다.&lt;/p&gt;
&lt;p&gt;특히 내장된 표현(embedded expresions)을 사용하면&amp;nbsp; 별도의 템플릿 엔진을 사용하지 않아도 될 정도로 XQuery을 사용하는 듯한 xml 처리가&amp;nbsp; 용이해 질수 있습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&amp;nbsp;val date = &amp;lt;date&amp;gt;{ df.format(new java.util.Date()) }&amp;lt;/date&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Console.println(date)&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Hello World&lt;/h4&gt;
&lt;p&gt;sview을 이용한 Hello World을 출력하는 간단한 프로그램을 만들어 보겠습니다.&lt;/p&gt;
&lt;p&gt;Spring2.5 의 AnnotationBased Controller와 DI을 사용합니다. 우선 SpringMVC의 View,ViewResolver에 나오는 설정을 springmvc-context.xml에 합니다.&lt;/p&gt;
&lt;p&gt;그리고 MVC의 Controller과 View을 작성합니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;@Controller&lt;br /&gt;
public class IndexController&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; @RequestMapping(&quot;/index.sview&quot;)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public String indexScala(ModelMap models) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Model model = new Model();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; model.setName(&quot;HELLO WORLD&quot;);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; models.addAttribute(&quot;model&quot;,model);&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return &quot;sview:index:sampleLayout&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;IndexController.java&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;@Component&lt;br /&gt;
@Scope(&quot;prototype&quot;)&lt;br /&gt;
class Index(b:ViewBinding) extends SViewContent(b){&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def head()&amp;nbsp; = {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt; this is index &amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def content() = {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; val m : Model = b.getModels.get(&quot;model&quot;).asInstanceOf[Model]&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; val df : java.text.DateFormat = java.text.DateFormat.getDateInstance()&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;html&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;head&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { head }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;body&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div&amp;gt; this is index List ! &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div&amp;gt; { m.getName } &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;div&amp;gt; today is { df.format(new java.util.Date()) } &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;index.scala&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;IndexController의 indexScala에서 리턴하는&amp;nbsp; viewName이 &quot;sview:index:sampleLayout&quot;입니다. 앞의 prefix는 sviewResolver에서 검출하는 용도로 쓰이고, index:sampleLayout은 각각&amp;nbsp; SView의 이름입니다.&lt;/p&gt;
&lt;p&gt;sampleLayout.scala는 SiteMesh처럼&amp;nbsp; index의 html의 head와 body을 자신의 head와 body으로 추가하여 출력하게 됩니다. 간단한 레이아웃 기능이라고 할수 있습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;@Component&lt;br /&gt;
@Scope(&quot;prototype&quot;)&lt;br /&gt;
class SampleLayout(b:ViewBinding,v:SView) extends SViewComposite(b,v) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def content() = {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;html&amp;gt;&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; &amp;lt;head&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; {&amp;nbsp; v.content \\ &quot;head&quot; \\ &quot;_&quot; }&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; &amp;lt;/head&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; &amp;lt;body&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; { v.content \\ &quot;body&quot; \\ &quot;_&quot; }&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; &amp;lt;/body&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p style=&quot;margin-left: 4em;&quot;&gt;&amp;nbsp; &amp;lt;/html&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; }&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;sampleLayout.scala&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;그림_3.png&quot; class=&quot;attachment&quot; src=&quot;http://anarch.springnote.com/pages/1574342/attachments/688366&quot; alt=&quot;그림_3.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;대략적인 구성 요소&lt;/h4&gt;
&lt;p&gt;&lt;img class=&quot;attachment&quot; title=&quot;sview-pattern-dia.jpg&quot; src=&quot;http://anarch.springnote.com/pages/1574342/attachments/688268&quot; alt=&quot;sview-pattern-dia.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;SView&amp;nbsp;&lt;/h5&gt;
&lt;p&gt;SView는&amp;nbsp; Scala의 trait입니다. 자바의 Interface에 해당합니다. SView에는 다음과 같은 메소드가 있습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;trait SView&lt;br /&gt;
{&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def doctype() : String&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def contenttype() : String&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def content() : Elem&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def render(render : SViewRenderer) : Unit&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;SView은 View에서 필요한 행동을 정의합니다. 이중 기본적인 정보를 지원할수 있는 것은&amp;nbsp; 구현 상속으로 제공하게 됩니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;abstract class AbstractSView extends SView {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def contenttype() : String = {&quot;text/html&quot;}&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def doctype() : String = {&quot;&quot;}&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def render(render : SViewRenderer) : Unit = {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; render.setContentType(contenttype())&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; render.setDocType(doctype())&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; render.setContent(content())&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;SView,SViewContent,ViewComposite : Composite , case classes&lt;/h5&gt;
&lt;p&gt;SView,AbstractSView을 상속받는 클래스에는 SViewContent와 SViewComposite가 있습니다. 이 두 클래스는 Scala의 케이스 클래스입니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;abstract case class SViewContent(binding : ViewBinding) extends AbstractSView {}&lt;br /&gt;
abstract case class SViewComposite(binding : ViewBinding , view : SView) extends AbstractSView {}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;어떤 패턴매칭을 하기 위해서 case class으로 만든 건 아니었습니다.(처음에는 패턴 매칭으로 Render의 책임을 다르게 구성하려고&amp;nbsp; 그랬습니다.)&lt;/p&gt;
&lt;p&gt;케이스 클래스는 객체 타입을 패턴으로 분해(decomposition)하기 때문에 erlang의 애리티(arity)처럼 패턴 매칭에 매칭변수로 쓰입니다.&lt;/p&gt;
&lt;p&gt;그래서 하위 구현 클래스들의 생성자에 대한 제한을 줄수 있는 장점을 이용했습니다.&lt;/p&gt;
&lt;p&gt;SViewComposite는 위의 레이아웃처럼 또 다른 SView을 인자로 얻어서 자기 자신의 content에 추가 할수 있습니다. 즉 공통적인&amp;nbsp; SView을 사용하기때문에 전체/부분이라는 구조를 쉽게 구성할수 있습니다.(Compositie패턴) Client가 보기에 Decorator패턴처럼 하나의 인터페이스을 가지고 다른 기능을 하는 것 처럼, 포함받은 SView의 내용을 자기 자신의 content에 추가하기 때문에 Decorator패턴의 실체화라고도 생각합니다.&lt;/p&gt;
&lt;h5&gt;SViewFactory,SViewSpringBeanFactory : AbstractFactory , ConcreteFactory&lt;/h5&gt;
&lt;p&gt;&amp;nbsp;SViewFactory는 SView을 생성하는 책임이 있습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public interface SViewFactory {&lt;br /&gt;
&amp;nbsp;&amp;nbsp; public SView getSView(String viewName,ViewBinding viewBinding);&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;여기서 ViewBinding은 구현패턴에서 이야기하는 파라미터 객체입니다.&lt;/p&gt;
&lt;p&gt;원래 View 인터페이스는 HttpServletRequest,HttpServletResponse와 Model을 인자로 받는 메소드가 있습니다. ViewBinding은 이 인수를 파라미터객체로 합성합니다.&lt;/p&gt;
&lt;p&gt;SViewSpringBeanFactory은 SViewFactory의 인터페이스 상속을 하여 SpringBeanContext에서&amp;nbsp;viewName으로 SView을 얻어옵니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public class SViewSpringBeanFactory extends WebApplicationObjectSupport implements SViewFactory {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private final String DEFAULT_VIEW_DELIMITER= &quot;:&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public SView getSView(String viewName,ViewBinding viewBinding) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] str = viewName.split(DEFAULT_VIEW_DELIMITER);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String sviewName = null;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String scompositeViewName = null;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(str.length &amp;gt;= 1 &amp;amp;&amp;amp; StringUtils.hasText(str[0])) sviewName = str[0];&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(str.length &amp;gt;= 2 &amp;amp;&amp;amp; StringUtils.hasText(str[1])) scompositeViewName = str[1];&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WebApplicationContext ctx = super.getWebApplicationContext();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SView sview = (SView) ctx.getBean(sviewName,new Object[]{viewBinding});&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(StringUtils.hasText(scompositeViewName)) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SView sviewcomposite = (SView) ctx.getBean(scompositeViewName,&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;new Object[]{viewBinding,sview});&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return sviewcomposite;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return sview;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;ViewAdapter : Adapter&lt;/h5&gt;
&lt;p&gt;SView을 상속한 객체들은 이미 상속했으므로 View 인터페이스를 상속하지 않는 한 ViewResolver에 추가할수 없습니다.&lt;/p&gt;
&lt;p&gt;물론 SView가 View을 extends해도 되지만. ViewAdapter을 사용함으로써, 기존의 클래스(SView)에 대한 변경을 하지 않고,&amp;nbsp;전혀 다른 타입의 클래스(View)처럼 사용할수 있습니다.&lt;/p&gt;
&lt;p&gt;특히 이부분은 스프링 프레임워크의 View Interface의 render의 Map model이 제너릭 타입 파라미터가 되어 않아서, Scala에서 인터페이스 상속을 하지 못해,&amp;nbsp;전혀 다른 타입인 View인척하는 객체입니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;public class ViewAdapter implements View {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private final static String DEFAULT_CONTENT_TYPE = &quot;text/html&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private String sviewName;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private SViewFactory sviewFactory;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private boolean isPrettyPrint = true;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private String contentType;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public ViewAdapter() {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setSViewName(String sviewName) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.sviewName = sviewName;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setSViewFactory(SViewFactory sviewFactory) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.sviewFactory = sviewFactory;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setPrettyPrint(boolean isPrettyPrint) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.isPrettyPrint = isPrettyPrint;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getContentType() {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return contentType;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void render(Map model, HttpServletRequest request,HttpServletResponse response)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;throws Exception {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ViewBinding viewBinding = new ViewBinding((Map&amp;lt;String,Object&amp;gt;) model,request,response);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SView sview = sviewFactory.getSView(sviewName,viewBinding);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.contentType = sview.contenttype();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SViewWriteRenderer renderer = new SViewWriteRenderer();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sview.render(renderer);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; response.setContentType(this.contentType);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PrintWriter out = response.getWriter();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; renderer.print(out,isPrettyPrint);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; out.close();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h5&gt;SViewRenderer , SViewWriteRenderer : Abstract Builder, Concrete Builder&lt;/h5&gt;
&lt;p&gt;공통적인 연산을 정의한 AbstractSView의 build()의 파라미터로 전달되는 SViewRenderer는 출력에 필요한 정보에 대한 메소드만을 가지고 있습니다. 즉 Director인 SView는 Renderer가 어떻게 표현할지에 대하여 알지 못합니다.&amp;nbsp; SViewRenderer의 구체 클래스인 SViewWriteRenderer는 HttpServletResponse에서 전달 받는 PrintWriter을 이용해서 표현합니다. 만일 &lt;code&gt;ServletOutputStream&lt;/code&gt;이나 기타 다른 방식으로의 표현을 바꾸더라도 변화는 ViewAdpater에서 다른 방식의 표현을 하는 Renderer으로 교체로 국한됩니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;trait SViewRenderer {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def setDocType(docType : String)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def setContentType(contentType : String)&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; def setContent(content : Elem)&lt;br /&gt;
}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;public class SViewWriteRenderer implements SViewRenderer {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private String docType = &quot;&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private String contentType = &quot;&quot;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; private Elem content = null;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setDocType(String docType) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.docType = docType;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getContentType() {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this.contentType;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setContentType(String contentType) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.contentType = contentType;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void setContent(Elem content) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.content = content;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void print(PrintWriter writer,boolean isprettyprint) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String result = null;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if(isprettyprint == true) {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PrettyPrinter pp = new PrettyPrinter(120,4);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result = pp.format(content);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else {&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result = content.toString();&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; writer.print(docType);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; writer.print(result);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;맺는 말&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;작성하고 나서 혹시 이와 비슷한 생각을 한 사람이 있는지 알아보니까&amp;nbsp; Object-Oriented Pattern Matching 논문을 작성한 burak emir 의&amp;nbsp; scalaservlet 이 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;http://burak.emir.googlepages.com/&quot;&gt;http://burak.emir.googlepages.com&lt;/a&gt; &lt;a href=&quot;http://burak.emir.googlepages.com/scalaservlet.html&quot;&gt;http://burak.emir.googlepages.com/scalaservlet.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;그리고 scala의 웹 프레임워크인 lift의 SHtml에서도 다음과 같이 사용합니다.&amp;nbsp; &lt;a href=&quot;http://github.com/dpp/liftweb/tree/master/lift/src/main/scala/net/liftweb/http/SHtml.scala&quot;&gt;http://github.com/dpp/liftweb/tree/master/lift/src/main/scala/net/liftweb/http/SHtml.scala&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;XML/HTML의 각 노드마다 객체를 생성하기 때문에 일반적인 JSP보다 비용 발생이 많은 듯 합니다. 실제 필드에서 적용하려면, 캐쉬전략이라든가 Scala의 XML Processing말고 &amp;nbsp; 다른 DSL이나 Template 엔진을 구성할 필요도 있다고 생각합니다. SViewSpringBeanFactory에서 sviewName에 해당하는 SView 객체를 얻지 못했을 경우에 좀더 명확한 Exception(SViewNotFoundException 등등)을 발생한다던가 하는 부분과 성능에 대한 부분은 다시 한번 고려해볼만 하다고 생각합니다.&lt;/p&gt;
&lt;p&gt;처음 생각은 자바에서의&amp;nbsp; 동적 스크립트 언어의 효과적인 사용에 적합한 레이어가 View가 아닐까해서 View에 대한 가볍고 쉬운 언어를 찾다가 여기까지 왔습니다. :-) 웹 프로그래밍에서 특히 자바 웹 프로그래밍에서 Html과 데이터를 바인딩 하는 방법은 많지만. 효과적으로&amp;nbsp; 관리 가능한 (특히 테스트 가능한) 뷰 기술은 많지 않아 보입니다.&lt;/p&gt;
&lt;p&gt;지금까지 읽어 주셔서 감사합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;참고자료&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/servlet/View.html&quot;&gt;http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/servlet/View.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/servlet/ViewResolver.html&quot;&gt;http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/servlet/ViewResolver.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.scala-lang.org/intro/xml.html&quot;&gt;http://www.scala-lang.org/intro/xml.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://burak.emir.googlepages.com/scalaxbook.docbk.html&quot;&gt;http://burak.emir.googlepages.com/scalaxbook.docbk.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p style=&quot;text-align:right&quot;&gt;이 글은 &lt;a href=&quot;http://anarch.springnote.com/pages/1574342&quot;&gt;스프링노트&lt;/a&gt;에서 작성되었습니다.&lt;/p&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/104</guid>
			<comments>http://anarch.tistory.com/104#entry104comment</comments>
			<pubDate>Sat, 23 Aug 2008 03:54:18 +0900</pubDate>
		</item>
		<item>
			<title>ActionAnnotations : Webwork Interceptor와 Annotation의 간단한 활용</title>
			<link>http://anarch.tistory.com/103</link>
			<description>&lt;p&gt;Webwork의 Interceptor은 Webwork에서 중요한 위치를 차지 하고 있습니다. Webwork의 많은 기능이 Interceptor로 제공되어 있고, Webwork의 기능 확장은 거의 Interceptor로 이루어져 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가령 Webwork에서 TypeConversion이 실패 했을 경우, &amp;nbsp;자동으로&amp;nbsp;Action.INPUT으로 forward됩니다. 어떤 페이지의 입력(request)가 실패하면 &amp;nbsp;다시 사용자 입력을 받기 위해&amp;nbsp;&amp;nbsp;입력 화면을 그려주듯이 말이죠. 처음에 이부분때문에 무척 당황했는데요.. 이 부분 역시 Interceptor으로 제공되어 있습니다. 즉 converionError이라는 Interceptor을 제거하면 위와 같은 행동을 하지 않게 됩니다. (기본적인 webwork의 interceptor stack에 포함되어 있습니다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;프로젝트를 하면서 여러 액션에서 공통적으로-자주- 구성되는&amp;nbsp;&amp;nbsp;루틴이 존재하게 됩니다. 가장 대표적인 예로는 사용자 인증과 권한에 대한 부분인데요. 대개의 경우 &amp;nbsp;구현상속을 이용하여, &amp;nbsp;코드를 압축하거나, 위임을 하여 사용하기 편하게 제공하게 됩니다. (물론 서비스로 만들어서 사용 될수도 있지만, 여러 서비스를 사용한 액션처럼 구성하게 되겠지요.)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이번에 회사 동료와 버스를 타면서 논의 하다가 나온 아이디어는 (이것 말고 Interface 을 이용한 방법도 이야기 되었습니다.) Annotation을 가지고 Interceptor가 그러한 기능을 지원하는 건 어떠한가? 이었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;간단한 예를 들자면,&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;@MusyBeLogin(loginUrl=&quot;/auth/login.action&quot;)&lt;/li&gt;
&lt;li&gt;class BlogPostListAction extends ActionSupport {&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; @LoginMember&amp;nbsp;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; private Member loginMember&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; @Permission&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; private Permission permission&lt;br /&gt;
...&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이런식으로 구성하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Annotation으로 BlogPostListAction에서의 필요한 여러 객체를 제공하거나(LoginMember,Permission),Action에 annotation된 조건이 아니라면 흐름을 제어하는 기능들을 할수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런식의 구성이 좋은지는 아직 모르겠지만. (&lt;a href=&quot;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76#p175%29&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76#p175%29&quot;&gt;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76#p175)&lt;/a&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;line-height: 18px;&quot;&gt;&lt;a href=&quot;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76%29&quot; style=&quot;&quot;&gt;&lt;/a&gt;&lt;/span&gt; Action에 존재할수 있는 코드를 압축한다는 개념을 좀 간단하게 구현했다고 볼수 있습니다.&amp;nbsp;&lt;br /&gt;
우선&amp;nbsp;간단하게 책임을 나누어서,&amp;nbsp;&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public abstract class ActionAnnotationInterceptor extend AroundInterceptor {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;protected abstract ActionAnnotationHandling getHandling();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;@Override&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;protected void before(ActionInvocation invocation)&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Object action = invocation.getAction();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;HttpServletRequest request = getHttpServletRequest(innovation);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;getHandlling().handling(action,request);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;}&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ActionAnnotationInterceptor이라는 간단한 Interceptor을 만듭니다. 이 추상 클래스을 구현 상속한 객체에서 getHandling()을 구현합니다.&lt;/p&gt;
&lt;p&gt;ActionAnnotationHandling은 말 그대로 ActionAnnotation을 핸들링하는 인터페이스입니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;line-height: 18px;&quot;&gt;&lt;a href=&quot;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76%29&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://forum.ksug.org/viewtopic.php?f=5&amp;amp;t=76%29&quot;&gt;&lt;/a&gt;&lt;/span&gt;public interface ActionAnnotationHandling {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public void handling(Object action,HttpServletRequest request) throws Exception&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;여러 Annotation을 각각 핸들링하는 객체를 포함하고는 ActionAnnotationHandlers 을 만들었습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public class ActionAnnotationHandlers implements ActionAnnotationHandling {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public void handling(Object action,HttpServletRequest request) &amp;nbsp;{&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;for(ActionAnnotationHandling handling : handlers) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;handler.handling(action,request);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;} &amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;} &amp;nbsp;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;....&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 각각 ActionAnnotationHandler들의 공통적인 기능을 AbstractActionAnnotationHandler에 넣었습니다.&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public abstract class AbstractActionAnnotationHandler implements ActionAnnotationHandling {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public boolean hasTypeAtAnnotation(Calss&amp;lt;? extends Object&amp;gt;klass,Class&amp;lt;? extends Annotation&amp;gt; annotationClass){&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;if(kclass.isAnnotationPresent(annotationClass) { return true ;} return false;&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;}&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; ...&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public abstract void handling(Object action,HttpServletRequest request) &amp;nbsp;throws Exception;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;....&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 각각 특정 AnnotationHandler을 작성했습니다. 여러 객체로 나누어서 작업하는게 불편해서 하나의 객체를 만들었는데요, action의 Type이나 fileld에 해당하는 annotation 이 존재하면 reflection api으로 필요한 객체를 넣어준다거나, 세션의 인증키가 맞지 않다면 예외상황을 발생하여 ExceptionHandler로 하여금 에러 페이지로 가도록 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;쓰다보니까 벌거 아니긴 해서 :-} 팀내에 아이디어 프로토타입으로 만들어 봤는데요.. 이미 프로젝트가 진행된지 절반은 되어서 (하지만 새로 사이트를 개발 들어가는 시점이긴 해서) 시간도 없고&amp;nbsp; 이부분에 대한 비용이 많이 지출될거 같아서 이정도까지 해보았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지금 생각해보니, Handler으로 분리한게 좋은건지. 라는 생각도 드는 군요. Interceptor의 제어 흐름에서 멀어서져 특정 Annotation이흐름을 제어할때 쉽지 않을 듯 하고 , &amp;nbsp;webwork은 ActionContext을 가지고 거의 모든 정보를 참조할수 있기 때문에 ActionAnnotationHandling.handling()에 HttpServletRequest을 넣어줄 필요는 없지 않을까 싶긴 하네요..&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;언제 다시 Webwork을 다시 사용하게 되면 그때 가서 다시 한번 끄집어 낼지도 모르겠습니다. :-)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align:right&quot;&gt;이 글은 &lt;a href=&quot;http://anarch.springnote.com/&quot;&gt;스프링노트&lt;/a&gt;에서 작성되었습니다.&lt;/p&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/103</guid>
			<comments>http://anarch.tistory.com/103#entry103comment</comments>
			<pubDate>Sun,  6 Jul 2008 21:41:26 +0900</pubDate>
		</item>
		<item>
			<title>WebWork의 PageController Pattern와 SiteMesh 사용</title>
			<link>http://anarch.tistory.com/102</link>
			<description>&lt;p&gt;이번 프로젝트는 이런저런 이유로 해서 Framework Stack을 SiteMesh+WebWork+Spring+SqlMap으로 구성하게 되었습니다.&lt;/p&gt;
&lt;p&gt;Struts1을 사용할때 Webwork에 대한 관심이 있었지만,Webwork으로 사이트를 빌딩한 경험이 전무한 저로써는 Struts1/SpringMVC 스타일과 좀 (많이) 다른 Webwork이 신기했었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;특히 Webwork은 PageController 이라는 패턴을 사용할수 있는데요. 간단하게 말해, 대개의 WebMVC는 Controller/Action이 중앙 집권적인 제어를 위해 View 레이어가 랜더링 되기 전에 제어를 하지만. PageController 패턴은 View에서 특정 Action을 호출 할수 있는. 즉 페이지의 특정 논리적인 단위를 재사용하거나 파티셔닝 하는&amp;nbsp;&amp;nbsp;좋은 방법이 있습니다. 사용법도 매우 간단한데요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&amp;lt;div&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;ww:action name=&quot;noticeList&quot; namespace=&quot;/tiles&quot; executeResult=&quot;true&quot; /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;/div&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;mvc4.JPG&quot; class=&quot;attachment&quot; src=&quot;http://anarch.springnote.com/pages/1308286/attachments/561814&quot; alt=&quot;mvc4.JPG&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;SiteMesh와 함께 자바에서 많이 사용하는 Tiles(원래 Struts의 서브 프로젝트이었지만,Top Project으로 승격되었습니다.)도 &amp;nbsp; 비슷한 것을 가지고 있습니다.&lt;/p&gt;
&lt;p&gt;특히 SpringFramework와 함께 Tiles 사용한다면,&lt;/p&gt;
&lt;p&gt;org.springframework.web.servlet.view.tiles.ComponentControllerSupport을 구현상속해서 쉽게 구성할수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FrontController에 비해, 몇가지 생각해야 하는 부분이 있습니다. 우선 제어의 선후가 바뀌기 &amp;nbsp;때문에, Action에 대한 제어가 쉽지 않습니다. 가령. X,Y,Z에서 동일한 쿼리를 발생한다면 한 페이지를 랜더링 하기 위해 동일한 데이터를 얻기 위해 쓸모없는 퀴리를 날리게 됩니다. 물론 1초캐쉬같은 것등으로 해결이 가능합니다만.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 한 페이지에서 여러 독립적인 Action이 존재하여 페이지의 각 요소를 재사용하는 일도 있지만, Sitemesh와 같은 페이지 레이아웃/데이코레이션을 구성하는 라이브러리를 사용할 경우 복잡한 레이아웃이 아닌 경우, 아직까지는 크게 사용하지 않을 듯 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 이번에 다시 레이아웃을 잡을때 SiteMesh의 Decorator마다 Action을 하나만 두는 방식은 어떤가 생각해봤습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;webwork-sitemesh-dia.png&quot; class=&quot;attachment&quot; src=&quot;http://anarch.springnote.com/pages/1308286/attachments/563764&quot; alt=&quot;webwork-sitemesh-dia.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉 SiteMesh/Decorator에서 호출하는 Webwork/Action이 Result을 가지고 있지 않고. 단지 해당하는 작업을 한 후 화면에 바인딩할 객체를 ServletRequest의 속성(attribute)에 담기만 하고 Decortator에서 그 정보를 출력하는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉 제어가 두군데의 Action으로만 나누어 지는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;lt;ww:action&amp;gt; 태그에서 result을 실행하지 않기 위해서 (executeResult=&quot;true&quot;으로해도 Action의 execute()메소드의 리턴이 NONE이기 때문에 view가 보이지 않습니다. )&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;&amp;lt;html&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;head&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;ww:action name=&quot;noticeList&quot; namespace=&quot;/tiles&quot; executeResult=&quot;false&quot; /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;/head&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;body&amp;gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;lt;c:out value=&quot;${tileNotice}&quot; /&amp;gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol class=&quot;code&quot;&gt;
&lt;li&gt;public class NoticeTileAction extends ActionSupport {&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public String execute() throws Exception {&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;ServletActionContext().getRequest().setAttribute(&quot;tileNotice&quot;,&quot;hello world!&quot;);&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;return NONE;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: Courier; font-size: 11px; line-height: 13px;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: courier; font-size: 12px; line-height: 24px; white-space: normal;&quot;&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 별거 아닌데. 글만 길어지고 그림만 생겼군요. :-) 물론 SiteMesh가 없이도 사용할수 있는 방법입니다. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;정리도 안되어 있고 두서도 없는 글 읽어 주셔서 감사합니다. (...)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4&gt;관련 링크&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;http://www.opensymphony.com/webwork/wikidocs/action.html&quot;&gt;http://www.opensymphony.com/webwork/wikidocs/action.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Webwork In Action&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align:right&quot;&gt;이 글은 &lt;a href=&quot;http://anarch.springnote.com/&quot;&gt;스프링노트&lt;/a&gt;에서 작성되었습니다.&lt;/p&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/102</guid>
			<comments>http://anarch.tistory.com/102#entry102comment</comments>
			<pubDate>Sun,  8 Jun 2008 03:16:57 +0900</pubDate>
		</item>
		<item>
			<title>Tabla - 간단한 태그 기반 템플릿 유틸리티</title>
			<link>http://anarch.tistory.com/100</link>
			<description>&lt;p&gt;Tabla는 간단한 태그 기반의 템플릿 유틸리티입니다. &amp;nbsp;컨텐츠의 특정부분을 마크업을 이용하여 컨텐츠의 내용을 가공하기 위해서 작성했었습니다. :-)&lt;/p&gt;
&lt;h3&gt;간단한 예제&lt;/h3&gt;&lt;br /&gt;

&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;Tags tags = new Tags();
tags.addTag(new NamedTag() {
&amp;nbsp; &amp;nbsp; public String execute(ParseTag pt,TagContext tc) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; return &quot;hello world!&quot;;
&amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; public String tagName() { return &quot;t:hello&quot;; }
};
&amp;nbsp;
Parser p = new Parser(tags);
String result = p.parse(&quot;&amp;lt;t:hello /&amp;gt;&quot;);
&amp;nbsp;
&amp;gt;&amp;gt; hello world!
&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;h3&gt;클래스들&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;1. Tag - 태그의 인터페이스. &amp;nbsp;태그의 행동을 정의합니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;2. NamedTag - 태그(Tag)의 인터페이스 상속. &amp;nbsp;태그이름(tagName)을 등록할수 있습니다. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;3. Tags - 태그들 &lt;/p&gt;
&lt;p&gt;&amp;nbsp;4. TagBinding - 파싱된 컨텐츠(Parse)와 Tag을 바인딩(Binding)하는 책임이 있습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;5. TagContext - 외부 환경이나 태그 간의 문맥적으로 값을 전달할때 사용됩니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;6. Parse - 파싱된 컨텐츠(Parse)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;7. ParseTag &amp;nbsp;- 파싱된 컨텐츠중 태그 정보 &lt;/p&gt;
&lt;p&gt;&amp;nbsp;8. Parser - 파서 &lt;/p&gt;
&lt;p&gt;&amp;nbsp;9. ParseFailureException - 파싱 실패 예외 상황 &lt;/p&gt;
&lt;p&gt;&amp;nbsp;10. utils.ParseTagRemover - 태그 시그니처 제거자 유틸리티. &lt;/p&gt;
&lt;br /&gt;
&lt;h3&gt;태그 정의 하기&lt;/h3&gt;
&lt;p&gt;태그를 정의하는 건 매우 쉽습니다. Tag,NamedTag을 구현하면 됩니다. &lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;Tag t = new Tag() {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; public String execute(ParseTag pt,TagContext tc) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return pt.getContent();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; }&lt;/li&gt;
&lt;li&gt;};&lt;/li&gt;
&lt;li&gt;Tags tags = new Tags(); &lt;/li&gt;
&lt;li&gt;tags.addTag(&quot;t:*&quot;,t);&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 코드는 t:으로 시작하는 모든 태그에 반응하게 됩니다. 물론 &amp;nbsp;ParseTag.getTagName()으로는 정확한 태그 이름을 얻을수 있습니다. &lt;/p&gt;
&lt;br /&gt;
&lt;h3&gt;파싱된 컨텐츠의 태그정보 - ParseTag&lt;/h3&gt;
&lt;p&gt;모든 태그는 ParseTag와 TagContext이라는 파라미터 객체를 얻게 됩니다. ParseTag은 Parser가 파싱한 컨텐츠중에 현재 태그에 해당하는 영역을 ParseTag에 담아서 전달하게 됩니다. &amp;nbsp;&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;class NewsDataTag implements NamedTag {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public NewsDataTag(NewsDataService newsDataService) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; this.newsDataService = newsDataService;&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;}&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public String execute(ParseTag pt,TagContext tc) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; String size = pt.getTagAttrMap(&quot;size&quot;);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; String category = pt.getTagAttrMap(&quot;category&quot;);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; List&amp;lt;NewsData&amp;gt; newsData = newsDataService.findBy(size,category);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return newsData.toBuildHtml();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;}&lt;/li&gt;
&lt;li&gt;}&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;태그의 글로벌 영역? - TagContext&lt;/h3&gt;
&lt;p&gt;태그와 태그(중첩된 태그 등등)나 태그를 실행중으로 값을 전달할때가 있습니다.&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;tags.add(&quot;t:container&quot;,new Tag() {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;public String execute(ParseTag pt,TagContext tc) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ...&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; for(Item item : items) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; tc.addLocalAttribute(&quot;itemTitle&quot;,item.getTitle());&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; tc.addLocalAttribute(&quot;itemUrl&quot;,item.getUrl());&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; result.append(pt.getContent());&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; tc.clearLocalAttibutes();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return result.toString();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp;} &lt;/li&gt;
&lt;li&gt;});&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;tags.add(&quot;t:itemTitle&quot;,new Tag() {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; public String execute(ParseTag pt,TagContext tc) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;String itemTitle = (String) tc.getLocalAttribute(&quot;itemTitle&quot;);&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if(itemTitle != null) {&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return itemTitle;&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;return pt.getContent();&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; }&lt;/li&gt;
&lt;li&gt;});&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;items: a,b,c &lt;/li&gt;
&lt;li&gt;&amp;lt;t:containter &amp;gt;&amp;lt;t:itemTitle /&amp;gt;,&amp;lt;/t:container&amp;gt; &amp;nbsp; &lt;/li&gt;
&lt;li&gt;&amp;gt;&amp;gt; a,b,c,&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;h3&gt;기타&lt;/h3&gt;
&lt;p&gt;뭐 그리 잘 만들건  아니지만. 그리고 만든지 몇달이 지나서 그닥 머리속에 남아 있는 것이 별루 없긴 하네요. 좀더 수정할것은 많은 듯 합니다만. :-)&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;a href=&quot;http://anarch.springnote.com/pages/1307938/attachments/561764&quot; title=&quot;tabla-0.0.2-sources.jar&quot; class=&quot;attachment&quot;&gt;tabla-0.0.2-sources.jar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://anarch.springnote.com/pages/1307938/attachments/561766&quot; title=&quot;tabla-0.0.2.jar&quot; class=&quot;attachment&quot;&gt;tabla-0.0.2.jar&lt;/a&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;p style=&quot;text-align: right;&quot;&gt;이 글은 &lt;a href=&quot;http://anarch.springnote.com/pages/1307938&quot;&gt;스프링노트&lt;/a&gt;에서 작성되었습니다.&lt;/p&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/100</guid>
			<comments>http://anarch.tistory.com/100#entry100comment</comments>
			<pubDate>Sat,  7 Jun 2008 02:19:44 +0900</pubDate>
		</item>
		<item>
			<title>[펌] 스스로 만족할 수 있는 일하기</title>
			<link>http://anarch.tistory.com/99</link>
			<description>&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;인생에서 70년은 20억초에 불과한 시간이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;스스로 자랑스럽게 여기지 않는 일을 하며 세월을 낭비하고 싶지는 않다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;프로그래머로써 여러분은 소중한 시간과 재능, 돈, 기회를 가지고 있다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;이러한 선물들을 책임있게 사용하기 위해, 여러분은 무엇을 할 것인가?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;이 질문에 대한 나의 대답은 다음과 같다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;자신과 컴퓨터뿐만 아니라 다른 이를 생각하며 코드를 작성하라. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;(code for others as well as myself and my buddy the CPU) &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p align=&quot;right&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;by Kent Beck&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-size: 10pt;&quot;&gt;
&lt;p align=&quot;right&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;span style=&quot;font-family: 굴림;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 10pt; font-family: 굴림;&quot;&gt;※ 서문에 나오는 내용인데 임의로 가감편집했다. 원문은 더 길다.&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;http://blog.naver.com/django44/40044982845 에서 무단 도용했습니다. :-) &lt;br /&gt;&lt;p align=&quot;right&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/99</guid>
			<comments>http://anarch.tistory.com/99#entry99comment</comments>
			<pubDate>Thu, 13 Dec 2007 18:09:09 +0900</pubDate>
		</item>
		<item>
			<title>Spring 2.5의 Annotation-based SpringMVC Configuration</title>
			<link>http://anarch.tistory.com/98</link>
			<description>&lt;p&gt;Spring2.5의 Annotation Configuration의 도입에 가장 큰 영향을 받는 곳이 spring-mvc이다. &amp;nbsp;특히 AbstractController이나 SimpleFormController의 구체적인 상속 없이 URL과 객체가 매핑이 가능하다. &lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;@Controller&lt;br /&gt;
@RequestMapping(&quot;/hello.html&quot;)&lt;br /&gt;
public class HelloWorldController {&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; @RequestMapping(method=RequestMethod.GET)&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; public String hello(ModelMap model) {&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return &quot;hello&quot;;&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이전&amp;nbsp; ModelAndView에서 ModelMap으로 Model과 View을 나누었다. ModelMap을 보면 Map 인터페이스가 가지고 있는 메소드를 가지고 있다. 즉 Map 인터페이스의 구현체중 하나(LinkedHashMap)를 상속했다.&lt;/p&gt;
&lt;p&gt;SpringMVC의 UrlMapping에서 하나 불만 사항이 있다면&amp;nbsp; package개념이 없다는 점이다. WebWork/Struts2의 경우 패캐지 태그안에 하나의 네임스페이스처럼 구성할수가 있는데.. 많은 url mapping이 존재하게 되면 package개념(관례적으로라도) 구성하게 되는데 이런 부분이 없는게 아쉽다. 가령&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;@Controller&lt;br /&gt;
@RequestMapping(&quot;/hello&quot;)&lt;br /&gt;
public class HelloWorldController {&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; @RequestMapping(&quot;/world.html&quot;)&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; public String hello(ModelMap model) {&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return &quot;hello/world&quot;;&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 구성해도 Exception은 발생하지만 않지만 Mapping을 찾지 못한다. 버그인지도 :-)&lt;/p&gt;
&lt;p&gt;또 이렇게 Annotation 기반으로 mapping을 할때 고민되는 점이 Interceptor부분이다. XML의 경우 각각 Interceptor마다 UrlHandlerMapping 빈을 만들어서 해결했지만,&amp;nbsp; Annotation 기반의 매핑의 경우 불가능하다. 결국 생각해 보면,&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;xml&quot;&gt;
&lt;li&gt;&lt;br /&gt;
&amp;lt;bean class=&quot;org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping&quot;&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;lt;property name=&quot;interceptors&quot;&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;bean class=&quot;kata.springmvc.interceptors.UrlPatternMatchingInterceptor&quot; &amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;property name=&quot;allowUrlPatterns&quot;&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;list&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;value&amp;gt;/app/board/*&amp;lt;/value&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;value&amp;gt;/app/client/*&amp;lt;/value&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/props&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/property&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/bean&amp;gt;&lt;br /&gt;
 &amp;nbsp; &amp;lt;/property&amp;gt;&lt;br /&gt;
&amp;lt;/bean&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;처럼 UrlPatternMatchingInterceptor을 하나 만들어서 (상속해서) handle하도록 해야 할듯 하다.&lt;/p&gt;
&lt;p&gt;SpringMVC에서 가장 많이 쓰는 Controller 구현체는 대개 AbstractController과 SimpleFormController,MultiActionController등이 될듯 한데. Annotation기반 설정은 특정 Controller 구현체를 상속받지 않으므로 (더군다나 Servlet API과도 &lt;span class=&quot;tit16 b&quot;&gt;decoupling되는데.) &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;SimpleFormController 처럼 HTTP Method가 GET 일경우와 POST일 경우 Controller의 움직임을 나누고 싶다면&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;@Controller&lt;br /&gt;
@RequestMapping(&quot;/board_form.html&quot;)&lt;br /&gt;
public class BoardFormController {&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; @RequestMapping(method = RequestMethod.GET)&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; public String onForm(ModelMap model) {&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; Board board = new Board();&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; model.addAttribute(&quot;board&quot;, board);&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return &quot;form&quot;;&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; }&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; @RequestMapping(method = RequestMethod.POST)&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; public String onSubmit(&lt;/li&gt;
&lt;li&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; @ModelAttribute(&quot;board&quot;) Board board,BindingResult result,ModelMap model) {&lt;br /&gt;
 &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return &quot;goto_list&quot;;&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; }&lt;br /&gt;
}&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;으로 간단히 구성된다.&amp;nbsp; initBinder의 경우 annotation으로 존재하고 onValidate같은 경우는 onSubmit에서 메소드를 호출하면 될듯 하다(흐름을 강제적으로 제약되지 않는다.)&amp;nbsp; SimpleFormController을 쓰면 기본적인 흐름에 대한 필요한 요소만 채워준다는 느낌이 들었는데(Framework과 대화한다고 할까?) 이런 강제적인 부분이 사라진듯 하지만, SimpleFormController에서 약간만 예외적인 흐름을&lt;/p&gt;
&lt;p&gt;구성하려고 하면 복잡함이 다가오는 걸 볼때, 이런 간단한 흐름만 제공하는것도 맞을 것 같다.&lt;/p&gt;
&lt;p&gt;MultiActionController은 좀더 간단한데,&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;&amp;nbsp;@RequestMapping(&quot;/multi/list.html&quot;)&lt;br /&gt;
 public String list(ModelMap model) {&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; model.addAttribute(&quot;test&quot;, new String(&quot;test.....&quot;));&lt;br /&gt;
 &amp;nbsp;&amp;nbsp; return &quot;list&quot;;&lt;br /&gt;
 }&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;당연하겠지만 메소드의 return이 void 일 경우 CoC으로 메소드 이름으로 view을 찾는다.&lt;/p&gt;
&lt;p&gt;특히 Servlet API을 직접 사용하지 않아도 되는 @RequestParam,@RequestAttribute,@SessionAttributes등이 있다.&lt;/p&gt;
&lt;p&gt;이 부분에서 약간 불만인 점은 @RequestAttribute(&quot;board&quot;) Board board 같은 Form을 Command객체 하나로만 Binding된다는 점이다.&amp;nbsp; 폼이 하나의 객체로만 환원 되는 일이 얼마나 될까. 복잡한 폼일수록 여러 객체를 한 화면에서 Form에 그릴 일이 많아지는데&lt;/p&gt;
&lt;p&gt;말이다. 물롬 FormBean처럼 Command 객체를 만들고&amp;nbsp; Model을 Composite하면 해결되는 문제이긴 하지만.&lt;/p&gt;
&lt;p&gt;만일 Form Tag에서&lt;/p&gt;
&lt;ol name=&quot;code&quot; class=&quot;xml&quot;&gt;
&lt;li&gt;&amp;lt;from:input path=&quot;board.title&quot; ...&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;&amp;lt;form:input path=&quot;menu.title&quot; ...&lt;/li&gt;
&lt;/ol&gt;
&lt;ol name=&quot;code&quot; class=&quot;java&quot;&gt;
&lt;li&gt;public String list(@ModelAttribute(&quot;board&quot;)Board board,@RequestAttribute(&quot;menu&quot;)Menu menu...&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이러게 되면 어떨까? &#039;_&#039;&amp;nbsp; 되는데 모르는 걸수도 있겠다. .이 부분은 그냥 나의 짧은 생각.. :-) (해보지 않았음)&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;약간 2시간 정도면 간단한게 알아 볼수 있을 꺼라 생각했지만..한 4시간은 쓴거 같다.. T_T 사실 tomcat 설정도 기억 안나서 servlet 띠우는 거만으로 삽질한거 보면.. 대충보면 쉬워 보이는 일이라도 막상 하면 쉬운 일은 하나도 없는거 같다.&lt;/p&gt;
&lt;p&gt;기술을 하나 제대로 배운다는게 결코 쉬운 일이 아니라는 생각이 든다. 단지 이런 사용법을 안다고 어떤 기술의 가지고 있다고 할수가 없는 거 같다. 배면의 움직임을 이해하고 습득하는게 중요하다고 생각되기도 하지만. 어느 도메인을 추구하는지에 따라 해답을 달라 질듯 하다.&amp;nbsp; Over Layered Architecture... Layered Developer? :-)&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;p align=&quot;right&quot;&gt;이 글은 &lt;a href=&quot;http://anarch.springnote.com/&quot;&gt;스프링노트&lt;/a&gt;에서 작성되었습니다.&lt;/p&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/98</guid>
			<comments>http://anarch.tistory.com/98#entry98comment</comments>
			<pubDate>Sat,  1 Dec 2007 22:42:09 +0900</pubDate>
		</item>
		<item>
			<title>Spring2.5에서 SpringJDBC 사용하기</title>
			<link>http://anarch.tistory.com/97</link>
			<description>&lt;P&gt;SpringFramework 2.5의 릴리즈에서 많은 부분이 추가/개선되었지만, SpringJDBC에서도 몇가지 개선/추가가 이루어졌다.&lt;/P&gt;
&lt;P&gt;우선 SimpleJdbcTemplate에서 SqlParameterSource의 사용과 SimpleJdbcInsert의 추가가 아닐까 싶다.&lt;/P&gt;
&lt;P&gt;SimpleJdbcTemplate는 Java5의 varargs와 autoboxing을 지원하여 (특히 목록을 얻어오는 퀴리의 경우) 캐스팅을 별도로 고민 할 필요가 없긴 한데.&lt;/P&gt;
&lt;P&gt;문제는 복잡한 퀴리의 경우 classic placeholder (&#039;?&#039;)으로는 불편해서 결국 NamedParameterJdbcTemplate으로 작성하곤 했는데.&lt;/P&gt;
&lt;P&gt;SimpleJdbcTemplate에서 SqlParameterSource가 가능하게 되었다.&lt;/P&gt;&lt;br /&gt;
&lt;OL class=java name=&quot;code&quot;&gt;
&lt;LI&gt;String sql = &quot;SELECT id,title,content FROM boards &quot;+ 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &quot;WHERE fl_public = :fl_public &quot;; 
&lt;LI&gt;MapSqlParameterSource params = new MapSqlParameterSource(); 
&lt;LI&gt;params.addValue(&quot;fl_public&quot;,fl_public); 
&lt;LI&gt;List&amp;lt;Board&amp;gt; list = this.simpleJdbcTemplate.query(sql, 
&lt;LI&gt;&amp;nbsp;&amp;nbsp; new ParameterizedRowMapper&amp;lt;Board&amp;gt;() 
&lt;LI&gt;&amp;nbsp; {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; public Board mapRow(ResultSet rs, int rowNum) 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throws SQLException 
&lt;LI&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; { &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; Board b = new Board(); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; b.setId(rs.getLong(&quot;id&quot;)); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; b.setTitle(rs.getString(&quot;title&quot;)); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; b.setContent(rs.getString(&quot;content&quot;)); &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; return b; &amp;nbsp; 
&lt;LI&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; }, 
&lt;LI&gt;&amp;nbsp; params 
&lt;LI&gt;); &lt;/LI&gt;&lt;/OL&gt;&lt;br /&gt;
&lt;P&gt;사실 SimpleJdbcTemplate이 추가될때 추가되어야 할 기능일지도. (...)&lt;/P&gt;
&lt;P&gt;그리고 추가된 부분이 SimpleJdbcInsert,SimpleJdbcCall이다. SimpleJdbcCall은 stored procedure을 잘 사용하지 않아서 패스. SimpleJdbcInsert는 QueryObject[PoEAA(316)]패턴과 비슷하게 쿼리를 객체화 할수 있다고 볼수가 있는데. (물론 접근 스타일은 다르지만.)&lt;/P&gt;
&lt;OL class=java name=&quot;code&quot;&gt;
&lt;LI&gt;jdbcInsert = new SimpleJdbcInsert(this.dataSource).withTableName(&quot;boards&quot;); 
&lt;LI&gt;Map&amp;lt;String, Object&amp;gt; parameters = new HashMap&amp;lt;String, Object&amp;gt;(); 
&lt;LI&gt;parameters.put(&quot;id&quot;,id); 
&lt;LI&gt;parameters.put(&quot;title&quot;, board.getTitle()); 
&lt;LI&gt;parameters.put(&quot;content&quot;,board.getContent()); 
&lt;LI&gt;jdbcInsert.execute(parameters); &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;빈의 프로퍼티과 테이블의 컬럼이름이 잘 맞는다면.&lt;/P&gt;
&lt;OL class=java name=&quot;code&quot;&gt;
&lt;LI&gt;jdbcInsert = new SimpleJdbcInsert(this.dataSource).withTableName(&quot;boards&quot;); 
&lt;LI&gt;SqlParameterSource parameters = new BeanPropertySqlParameterSource(board); 
&lt;LI&gt;jdbcInsert.execute(parameters); &lt;/LI&gt;&lt;/OL&gt;&lt;br /&gt;
&lt;P&gt;&amp;nbsp;데이터베이스의 시퀀스 키를 얻을수 있지만(executeAndReturnKey(),usingGeneratedKeyColumns()). PgSql에서 지원하지 않는 데이터베이스라고 나와서 패스. -_- (org.springframework.jdbc.core.metadata에 PostgresqlCallMataDataProvider가 없다.)&lt;/P&gt;
&lt;P&gt;SimpleJdbcUpdate나 SimpleJdbcSelect는 없고 단지 SimpleJdbcInsert만 나온 이유가 Insert 문이 가장 생성하기 쉽고.&lt;/P&gt;
&lt;P&gt;또 개인적인 경험에 따르면 가장 작성할때 오타가 가장 많이 나는 퀴리인 듯 하다. Update문의 경우 key-value 스타일이기 때문에 Insert문보다는 작성하기 수월한 면이 있다는 걸 부정할수 없겠다.&lt;/P&gt;
&lt;P&gt;그리고 Spring이 버젼업하면 SimpleJdbcUpdate나 SimpleJdbcSelect가 나와서 ORM이 없는 어둠의 장소,약속의 저편...에서도 광명이 찾아오지 않을까? :-)&lt;/P&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;P align=right&gt;이 글은 &lt;A href=&quot;http://anarch.springnote.com/&quot;&gt;스프링노트&lt;/A&gt;에서 작성된걸 가지고 티스토리에서 삽질했습니다.&lt;/P&gt;</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/97</guid>
			<comments>http://anarch.tistory.com/97#entry97comment</comments>
			<pubDate>Mon, 26 Nov 2007 18:46:08 +0900</pubDate>
		</item>
		<item>
			<title>그냥저냥 스크럼(Scrum) 이야기.</title>
			<link>http://anarch.tistory.com/87</link>
			<description>이번달 마소에 스크럼 이야기를 잼 있게 읽고 하나 만들어 봤다. &lt;br /&gt;
표절과 왜곡이 컨셉이고. &lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_159447&quot;&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=intro-scrum-1194523802166284-3&quot;/&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=intro-scrum-1194523802166284-3&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/?src=embed&quot;&gt;&lt;img src=&quot;http://static.slideshare.net/swf/logo_embd.png&quot; style=&quot;border:0px none;margin-bottom:-5px&quot; alt=&quot;SlideShare&quot;/&gt;&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/anarcher/intro-scrum&quot; title=&quot;View &#039;intro scrum&#039; on SlideShare&quot;&gt;View&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/upload&quot;&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
나는 방법론을 좋아하지 않는다.(관심도 없다)  대개 개발자들이 안 좋아한다고는 하는데. &lt;br /&gt;
내가 개발자이라서 안 좋아하는지. 천성이 체계적이지 않아서 그런지. &lt;br /&gt;
(그래서 개발을 발로 하나? &#039;_&#039;)&lt;br /&gt;
(우리 회사 유행어다. 발로하는거.)&lt;br /&gt;
개인적인 생각은 그 프로세스라는게 무겁고 권위적이기만 하지 말고  엔터테인먼트 요소가 있어야 한다고 생각한다. &lt;br /&gt;
또 하나의 걱정은 나이 들어서 현실에 너무 밀착되어서 생각이 편협해지지 않기를 바란다. &lt;br /&gt;
이리저리 생각만 많고 몸은 무겁다. &lt;br /&gt;
또 하나 기억해야 하는 것. &lt;br /&gt;
삶에 대한 고뇌가 깊어질수록 웃을수 있는 여유 또한 넓어진다는  것. :-) &lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;
덧. 나도 &quot;감사합니다!&quot;라고 한번 넣고 싶었다.. 회식후 술먹고 작성한것 치고는 잘하지 않았냐? 일필휘지로 이정도면..ㅋㅋ;
</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/87</guid>
			<comments>http://anarch.tistory.com/87#entry87comment</comments>
			<pubDate>Fri,  9 Nov 2007 00:49:46 +0900</pubDate>
		</item>
		<item>
			<title>어떤 책의 서평중에서.</title>
			<link>http://anarch.tistory.com/82</link>
			<description>&lt;br /&gt;
다소 허망하지만 
좋은 프로그래가 어떤 기법을 쓰느냐와 상관없이 좋은 프로그램을 만드는 것과 같이 &lt;br /&gt;
좋은 모델러는 어떤 기법을 쓰느냐와 상관없이 좋은 모델을 만듭니다. &lt;br /&gt;
&lt;br /&gt;
이 책이 좋은 모델러를 만드는데 기여할수는 있겠지만 &lt;br /&gt;
책의 방법을 따른다고 &quot;해당 프로젝트의 기간&quot;내에서 좋은 모델이 만들어지지는 않을 것 같습니다. &lt;br /&gt;
&lt;br /&gt;

마틴파울러의 리팩토링책이 그러했던 것처럼 &lt;br /&gt;
단지 우리는 실수를 통해 더 빠른 피드백을 얻고 더 많은 생각을 하게 됨으로써&lt;br /&gt;
조금씩이나마 조금 더 나은 모델러로 성장하여 &quot;다음 프로젝트에서는&quot; 조금 더 나은 모델을 만들수 있을것입니다.&lt;br /&gt;

&lt;br /&gt;
&lt;br /&gt;

</description>
			<category>격언</category>
			<category>책</category>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/82</guid>
			<comments>http://anarch.tistory.com/82#entry82comment</comments>
			<pubDate>Sun,  4 Nov 2007 19:01:32 +0900</pubDate>
		</item>
		<item>
			<title>스프링 세미나 갔다가 오다.</title>
			<link>http://anarch.tistory.com/77</link>
			<description>&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_147733&quot;&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://s3.amazonaws.com/slideshare/ssplayer2.swf?doc=springwebflow-kr-1193547798518553-2&quot;/&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://s3.amazonaws.com/slideshare/ssplayer2.swf?doc=springwebflow-kr-1193547798518553-2&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/?src=embed&quot;&gt;&lt;img src=&quot;http://s3.amazonaws.com/slideshare/logo_embd.png&quot; style=&quot;border:0px none;margin-bottom:-5px&quot; alt=&quot;SlideShare&quot;/&gt;&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/anarcher/springwebflow-kr&quot; title=&quot;View &#039;SpringWebFlow - kr &#039; on SlideShare&quot;&gt;View&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/upload&quot;&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;

&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_147734&quot;&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://s3.amazonaws.com/slideshare/ssplayer2.swf?doc=springsecurity-kr-1193547863380687-4&quot;/&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://s3.amazonaws.com/slideshare/ssplayer2.swf?doc=springsecurity-kr-1193547863380687-4&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/?src=embed&quot;&gt;&lt;img src=&quot;http://s3.amazonaws.com/slideshare/logo_embd.png&quot; style=&quot;border:0px none;margin-bottom:-5px&quot; alt=&quot;SlideShare&quot;/&gt;&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/anarcher/springsecurity-kr&quot; title=&quot;View &#039;SpringSecurity - kr &#039; on SlideShare&quot;&gt;View&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/upload&quot;&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
졸려서 제대로 듣지 못했음. (...)&lt;br /&gt;

</description>
			<author>anarch</author>
			<guid>http://anarch.tistory.com/77</guid>
			<comments>http://anarch.tistory.com/77#entry77comment</comments>
			<pubDate>Sun, 28 Oct 2007 14:13:11 +0900</pubDate>
		</item>
	</channel>
</rss>
