태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

Webwork의 Interceptor은 Webwork에서 중요한 위치를 차지 하고 있습니다. Webwork의 많은 기능이 Interceptor로 제공되어 있고, Webwork의 기능 확장은 거의 Interceptor로 이루어져 있습니다. 

가령 Webwork에서 TypeConversion이 실패 했을 경우,  자동으로 Action.INPUT으로 forward됩니다. 어떤 페이지의 입력(request)가 실패하면  다시 사용자 입력을 받기 위해  입력 화면을 그려주듯이 말이죠. 처음에 이부분때문에 무척 당황했는데요.. 이 부분 역시 Interceptor으로 제공되어 있습니다. 즉 converionError이라는 Interceptor을 제거하면 위와 같은 행동을 하지 않게 됩니다. (기본적인 webwork의 interceptor stack에 포함되어 있습니다.)

 프로젝트를 하면서 여러 액션에서 공통적으로-자주- 구성되는  루틴이 존재하게 됩니다. 가장 대표적인 예로는 사용자 인증과 권한에 대한 부분인데요. 대개의 경우  구현상속을 이용하여,  코드를 압축하거나, 위임을 하여 사용하기 편하게 제공하게 됩니다. (물론 서비스로 만들어서 사용 될수도 있지만, 여러 서비스를 사용한 액션처럼 구성하게 되겠지요.) 

 이번에 회사 동료와 버스를 타면서 논의 하다가 나온 아이디어는 (이것 말고 Interface 을 이용한 방법도 이야기 되었습니다.) Annotation을 가지고 Interceptor가 그러한 기능을 지원하는 건 어떠한가? 이었습니다. 

간단한 예를 들자면,

  1. @MusyBeLogin(loginUrl="/auth/login.action")
  2. class BlogPostListAction extends ActionSupport {
  3.      @LoginMember 
  4.      private Member loginMember 

     

         @Permission

         private Permission permission
    ...

이런식으로 구성하게 됩니다. 

Annotation으로 BlogPostListAction에서의 필요한 여러 객체를 제공하거나(LoginMember,Permission),Action에 annotation된 조건이 아니라면 흐름을 제어하는 기능들을 할수 있습니다. 

이런식의 구성이 좋은지는 아직 모르겠지만. (http://forum.ksug.org/viewtopic.php?f=5&t=76#p175) Action에 존재할수 있는 코드를 압축한다는 개념을 좀 간단하게 구현했다고 볼수 있습니다. 
우선 간단하게 책임을 나누어서, 

  1. public abstract class ActionAnnotationInterceptor extend AroundInterceptor {
  2.    protected abstract ActionAnnotationHandling getHandling();
  3.    @Override
  4.    protected void before(ActionInvocation invocation)
  5.        Object action = invocation.getAction();
  6.        HttpServletRequest request = getHttpServletRequest(innovation);
  7.        getHandlling().handling(action,request);
  8.    }
  9. ...

ActionAnnotationInterceptor이라는 간단한 Interceptor을 만듭니다. 이 추상 클래스을 구현 상속한 객체에서 getHandling()을 구현합니다.

ActionAnnotationHandling은 말 그대로 ActionAnnotation을 핸들링하는 인터페이스입니다.

  1. public interface ActionAnnotationHandling {
  2.    public void handling(Object action,HttpServletRequest request) throws Exception
  3. }

여러 Annotation을 각각 핸들링하는 객체를 포함하고는 ActionAnnotationHandlers 을 만들었습니다.

  1. public class ActionAnnotationHandlers implements ActionAnnotationHandling {
  2.    public void handling(Object action,HttpServletRequest request)  {
  3.        for(ActionAnnotationHandling handling : handlers) {
  4.                    handler.handling(action,request);
  5.        }    
  6.    }  
  7.  ....
  8. }

 

그리고 각각 ActionAnnotationHandler들의 공통적인 기능을 AbstractActionAnnotationHandler에 넣었습니다.

  1. public abstract class AbstractActionAnnotationHandler implements ActionAnnotationHandling {
  2.    public boolean hasTypeAtAnnotation(Calss<? extends Object>klass,Class<? extends Annotation> annotationClass){
  3.      if(kclass.isAnnotationPresent(annotationClass) { return true ;} return false;
  4.    }
  5.     ...
  6.    public abstract void handling(Object action,HttpServletRequest request)  throws Exception;
  7.  ....
  8. }

 

 

그리고 각각 특정 AnnotationHandler을 작성했습니다. 여러 객체로 나누어서 작업하는게 불편해서 하나의 객체를 만들었는데요, action의 Type이나 fileld에 해당하는 annotation 이 존재하면 reflection api으로 필요한 객체를 넣어준다거나, 세션의 인증키가 맞지 않다면 예외상황을 발생하여 ExceptionHandler로 하여금 에러 페이지로 가도록 했습니다.

 

쓰다보니까 벌거 아니긴 해서 :-} 팀내에 아이디어 프로토타입으로 만들어 봤는데요.. 이미 프로젝트가 진행된지 절반은 되어서 (하지만 새로 사이트를 개발 들어가는 시점이긴 해서) 시간도 없고  이부분에 대한 비용이 많이 지출될거 같아서 이정도까지 해보았습니다.

 

지금 생각해보니, Handler으로 분리한게 좋은건지. 라는 생각도 드는 군요. Interceptor의 제어 흐름에서 멀어서져 특정 Annotation이흐름을 제어할때 쉽지 않을 듯 하고 ,  webwork은 ActionContext을 가지고 거의 모든 정보를 참조할수 있기 때문에 ActionAnnotationHandling.handling()에 HttpServletRequest을 넣어줄 필요는 없지 않을까 싶긴 하네요..

 

언제 다시 Webwork을 다시 사용하게 되면 그때 가서 다시 한번 끄집어 낼지도 모르겠습니다. :-)

 

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

Posted by anarch

이번 프로젝트는 이런저런 이유로 해서 Framework Stack을 SiteMesh+WebWork+Spring+SqlMap으로 구성하게 되었습니다.

Struts1을 사용할때 Webwork에 대한 관심이 있었지만,Webwork으로 사이트를 빌딩한 경험이 전무한 저로써는 Struts1/SpringMVC 스타일과 좀 (많이) 다른 Webwork이 신기했었습니다. 

특히 Webwork은 PageController 이라는 패턴을 사용할수 있는데요. 간단하게 말해, 대개의 WebMVC는 Controller/Action이 중앙 집권적인 제어를 위해 View 레이어가 랜더링 되기 전에 제어를 하지만. PageController 패턴은 View에서 특정 Action을 호출 할수 있는. 즉 페이지의 특정 논리적인 단위를 재사용하거나 파티셔닝 하는  좋은 방법이 있습니다. 사용법도 매우 간단한데요.

 

  1. <div>
  2. <ww:action name="noticeList" namespace="/tiles" executeResult="true" />
  3. </div>

 

mvc4.JPG

 

SiteMesh와 함께 자바에서 많이 사용하는 Tiles(원래 Struts의 서브 프로젝트이었지만,Top Project으로 승격되었습니다.)도   비슷한 것을 가지고 있습니다.

특히 SpringFramework와 함께 Tiles 사용한다면,

org.springframework.web.servlet.view.tiles.ComponentControllerSupport을 구현상속해서 쉽게 구성할수 있습니다.

 

FrontController에 비해, 몇가지 생각해야 하는 부분이 있습니다. 우선 제어의 선후가 바뀌기  때문에, Action에 대한 제어가 쉽지 않습니다. 가령. X,Y,Z에서 동일한 쿼리를 발생한다면 한 페이지를 랜더링 하기 위해 동일한 데이터를 얻기 위해 쓸모없는 퀴리를 날리게 됩니다. 물론 1초캐쉬같은 것등으로 해결이 가능합니다만.

 

물론 한 페이지에서 여러 독립적인 Action이 존재하여 페이지의 각 요소를 재사용하는 일도 있지만, Sitemesh와 같은 페이지 레이아웃/데이코레이션을 구성하는 라이브러리를 사용할 경우 복잡한 레이아웃이 아닌 경우, 아직까지는 크게 사용하지 않을 듯 합니다.

 

그래서 이번에 다시 레이아웃을 잡을때 SiteMesh의 Decorator마다 Action을 하나만 두는 방식은 어떤가 생각해봤습니다. 

webwork-sitemesh-dia.png

 

즉 SiteMesh/Decorator에서 호출하는 Webwork/Action이 Result을 가지고 있지 않고. 단지 해당하는 작업을 한 후 화면에 바인딩할 객체를 ServletRequest의 속성(attribute)에 담기만 하고 Decortator에서 그 정보를 출력하는 것입니다. 

즉 제어가 두군데의 Action으로만 나누어 지는 것입니다. 

 

 <ww:action> 태그에서 result을 실행하지 않기 위해서 (executeResult="true"으로해도 Action의 execute()메소드의 리턴이 NONE이기 때문에 view가 보이지 않습니다. )

  1. <html>
  2. <head>
  3. <ww:action name="noticeList" namespace="/tiles" executeResult="false" />
  4. </head>
  5. <body>
  6. ...
  7.   <c:out value="${tileNotice}" /> 
  8. ...

 

  1. public class NoticeTileAction extends ActionSupport {
  2.     public String execute() throws Exception {
  3.        ServletActionContext().getRequest().setAttribute("tileNotice","hello world!");
  4.        return NONE;
  5.     }
  6. }

 

사실 별거 아닌데. 글만 길어지고 그림만 생겼군요. :-) 물론 SiteMesh가 없이도 사용할수 있는 방법입니다.  

정리도 안되어 있고 두서도 없는 글 읽어 주셔서 감사합니다. (...) 

 

관련 링크

http://www.opensymphony.com/webwork/wikidocs/action.html

Webwork In Action

 

 


 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

Posted by anarch

 내가 사람들을 칭찬할때는 몇가지 이기적인 이유가 있는데,그중 하나는 그 사람의 강점을 강화하고 싶어서이다. 그 사람의 강점이 함께 하는 환경을 건강하게 하고 발전하게 한다면,그 환경에 있는 나 역시 덕분에 행복해하고 발전할수 있는 것 같다. 내 개인적인 생각으로는 내가 내 자신을 발전시키고 성장하게 하는 부분보다 환경이나 좋은 사람들이 나에게 그러게하게 해주는 경우가 많은 듯 하다. 그리고 그건 내가  어떻게 생각하고 행동하는데도   깊은 관계가 있는 것 같다. 

 그래서 이 친구에 대한 칭찬은 어찌보면  내가 얻는 이득에 대한 이야기가 될듯 하다. :-) 이 친구는 어찌보면 내가 가장 많은 칭찬을 하고 싶은 또 하는 사람이다. 어떤 문제에 대한 해결책을 들었을때, 그 결과에 대한 가치보다 나는 그 사람이 어떻게 그런 생각을 했는지 항상 궁금하다. 그리고 그런 서로의 생각하는 법을 이해하는 건 매우 개인적으로 발전할수 있는 좋은 점인 듯 하다. 이 친구의 문제 해결 능력은 종종 내가 생각한 영역을 뛰어 넘기도 하고,내가 간과하고 있는 부분에 대한 의미있는 통찰인 경우가 많고, 문제에 천착하여 숲을 보지 못할때, 문제의 의미를 재해석 해서 문제를 더욱더 간단한 문제로 만드는 능력이 있다. 프로그래머나 컨설턴트가 문제 해결이라는 행위에 대한 많은 의미가 있는 것 보면 이 친구의 이런 장점은 나로써는 한때 감탄을 금치 못하게 하고,또 많은 좋은 생각을 하는 공부가 된듯 하다. 

 프로그래밍은 전문적인 지식을 요하는 부분이긴 하지만, 내 개인적인 경험으로는 단지 학술적인 지식이 많다고 프로그래밍이라는 행위가 잘 되는 건 아닌 듯 하다.  아는 지식은 많지만 그에 비해 코드를 보면 그 지식에 어울리지는 상황을 보면 프로그래밍이라는 작업은 본질적으로 문제 해결이고 실천인듯 하다.  거칠게 이야기 해서, 한번 어떻게는 만들어보면 거기에서는 분명 배우는게 있고 그 다음에는 그 작업은 어렵지 않아진다. 

 빵집 개발자인 양병규씨의 홈페이지에 가보면 다음과 같은 말이 있다. "아이디어는 자기가 구현할 수 있는 기술의 범위 내에서 나온다. -양병규,빵집개발자-". 개인적으로 내가 그 기술에 익숙하지 못해서 그 기술로 구현하는 것이 더 적합하지만, 그러지 못한 적이 있었다. 개인이 기술 습득에 게으르면 어찌 되는지에 대한 뼈아픈 예라고 할수 있는데. 이 친구는 지식이나 기술에 대한 학습에 대한 마음을 열어두고 항상 공부하려고 한다. 특히 매우 매력적인 부분은 현재 필요한 지식에 대한 합리적인 선택을 한다는 점인데,어떤 배움이 그 쓸모가 없다면 얼마나 허망하겠는가? 물론 단기적인 안목으로 판단하는 건 개인적으로도 큰 도움이 되지는 않지만, 적재적소에 필요한 지식을 습득하고 그 쓸모를 풍부하게 하는 점은 정말 배울 점이 많은 듯 하다. 그리고 장기적인 안목 또한 좋다. 

 세번째로, 대화다. 어떤 거창한 회의가 아닐지라도 사석에서 기술적인 이야기가 있을 경우 대화 안에서 너와 나라는 구분안에 토론으로 옮고 그름을 나누는 것을 넘어서  어떤 주제에 대한 서로의 생각을 보완하고 고쳐가서 서로 공동의 이익을 얻는 듯한 서로의 생각의 일치가 없을 지라도 보물지도의 보물을 찾는 행동처럼 알지 못했던 부분에 대한 생각을 할수 있게 하는 점. 그것 또한 지난 6년동안 이 친구와의 대화에서 느꼈던 좋은 경험이었던것 같다. 그리고 초반보다 요즘 더 이런 모습을 보여주어서 좋다. 

 이런 메타-피지컬(meta-physical) 한 특징은 단순히 당대 유명한 언어를 몇개 잘 알고 있다거나 개발력이 좋다는 것 그 이상으로 이 친구의 좋은 잠재력이고, 프로그래머로써 미덕인 듯 하다.

지난 6년동안 이런 좋은 사람과 함께 일을 했다는 것이 개인적으로 어떤 결과보다 의미 있다고 생각하고 마지막으로 사족을 부치자면. 내가 좋아하는 SF 드라마인 배틀스타 갈렉티카에서 나오는 대사가 생각난다. "그동안 함께 할수 있어서 영광이었다."


-2008/06/03 에 신명수(anarch@gmail.com)가 작성함 :-)


ps) 우선 내가 이 친구에게 부탁한것이 있다는 건, 읽은 동안은 생각하지 말자 

ps) 그리고 이런 점을 볼수 있는 나도 역시 좋은 프로그래머다!!!!!!!!! (강조) 

 

이 글은 스프링노트에서 작성되었습니다.

Posted by anarch

Tabla는 간단한 태그 기반의 템플릿 유틸리티입니다.  컨텐츠의 특정부분을 마크업을 이용하여 컨텐츠의 내용을 가공하기 위해서 작성했었습니다. :-)

간단한 예제


  1. Tags tags = new Tags(); tags.addTag(new NamedTag() {     public String execute(ParseTag pt,TagContext tc) {       return "hello world!";     }     public String tagName() { return "t:hello"; } };   Parser p = new Parser(tags); String result = p.parse("<t:hello />");   >> hello world!

클래스들

 1. Tag - 태그의 인터페이스.  태그의 행동을 정의합니다.

 2. NamedTag - 태그(Tag)의 인터페이스 상속.  태그이름(tagName)을 등록할수 있습니다.  

 3. Tags - 태그들

 4. TagBinding - 파싱된 컨텐츠(Parse)와 Tag을 바인딩(Binding)하는 책임이 있습니다.

 5. TagContext - 외부 환경이나 태그 간의 문맥적으로 값을 전달할때 사용됩니다.

 6. Parse - 파싱된 컨텐츠(Parse)

 7. ParseTag  - 파싱된 컨텐츠중 태그 정보

 8. Parser - 파서

 9. ParseFailureException - 파싱 실패 예외 상황

 10. utils.ParseTagRemover - 태그 시그니처 제거자 유틸리티.


태그 정의 하기

태그를 정의하는 건 매우 쉽습니다. Tag,NamedTag을 구현하면 됩니다.

  1. Tag t = new Tag() {
  2.     public String execute(ParseTag pt,TagContext tc) {
  3.        return pt.getContent();
  4.     }
  5. };
  6. Tags tags = new Tags();
  7. tags.addTag("t:*",t);
  8. ...

위의 코드는 t:으로 시작하는 모든 태그에 반응하게 됩니다. 물론  ParseTag.getTagName()으로는 정확한 태그 이름을 얻을수 있습니다.


파싱된 컨텐츠의 태그정보 - ParseTag

모든 태그는 ParseTag와 TagContext이라는 파라미터 객체를 얻게 됩니다. ParseTag은 Parser가 파싱한 컨텐츠중에 현재 태그에 해당하는 영역을 ParseTag에 담아서 전달하게 됩니다.  

  1. class NewsDataTag implements NamedTag {
  2.    public NewsDataTag(NewsDataService newsDataService) {
  3.       this.newsDataService = newsDataService;
  4.    }
  5.    public String execute(ParseTag pt,TagContext tc) {
  6.       String size = pt.getTagAttrMap("size");
  7.       String category = pt.getTagAttrMap("category");
  8.       List<NewsData> newsData = newsDataService.findBy(size,category);
  9.       return newsData.toBuildHtml();
  10.    }
  11. }


태그의 글로벌 영역? - TagContext

태그와 태그(중첩된 태그 등등)나 태그를 실행중으로 값을 전달할때가 있습니다.

  1. tags.add("t:container",new Tag() {
  2.    public String execute(ParseTag pt,TagContext tc) {
  3.       ...
  4.       for(Item item : items) {
  5.           tc.addLocalAttribute("itemTitle",item.getTitle());
  6.           tc.addLocalAttribute("itemUrl",item.getUrl());
  7.           result.append(pt.getContent());
  8.           tc.clearLocalAttibutes();
  9.       }
  10.       return result.toString();
  11.    }
  12. });
  13.  
  14. tags.add("t:itemTitle",new Tag() {
  15.     public String execute(ParseTag pt,TagContext tc) {
  16.        String itemTitle = (String) tc.getLocalAttribute("itemTitle");
  17.        if(itemTitle != null) {
  18.            return itemTitle;
  19.        }
  20.        return pt.getContent();
  21.     }
  22. });
  23.  

  1. items: a,b,c
  2. <t:containter ><t:itemTitle />,</t:container>  
  3. >> a,b,c,

기타

뭐 그리 잘 만들건 아니지만. 그리고 만든지 몇달이 지나서 그닥 머리속에 남아 있는 것이 별루 없긴 하네요. 좀더 수정할것은 많은 듯 합니다만. :-)


tabla-0.0.2-sources.jar

tabla-0.0.2.jar






이 글은 스프링노트에서 작성되었습니다.

Posted by anarch

인생에서 70년은 20억초에 불과한 시간이다.

스스로 자랑스럽게 여기지 않는 일을 하며 세월을 낭비하고 싶지는 않다.

 

프로그래머로써 여러분은 소중한 시간과 재능, 돈, 기회를 가지고 있다.

이러한 선물들을 책임있게 사용하기 위해, 여러분은 무엇을 할 것인가?

이 질문에 대한 나의 대답은 다음과 같다. 자신과 컴퓨터뿐만 아니라 다른 이를 생각하며 코드를 작성하라.

(code for others as well as myself and my buddy the CPU)

 

by Kent Beck

※ 서문에 나오는 내용인데 임의로 가감편집했다. 원문은 더 길다.


http://blog.naver.com/django44/40044982845 에서 무단 도용했습니다. :-)




Posted by anarch

Spring2.5의 Annotation Configuration의 도입에 가장 큰 영향을 받는 곳이 spring-mvc이다.  특히 AbstractController이나 SimpleFormController의 구체적인 상속 없이 URL과 객체가 매핑이 가능하다.

  1. @Controller
    @RequestMapping("/hello.html")
    public class HelloWorldController {
       @RequestMapping(method=RequestMethod.GET)
       public String hello(ModelMap model) {
           return "hello";
       }
    }

이전  ModelAndView에서 ModelMap으로 Model과 View을 나누었다. ModelMap을 보면 Map 인터페이스가 가지고 있는 메소드를 가지고 있다. 즉 Map 인터페이스의 구현체중 하나(LinkedHashMap)를 상속했다.

SpringMVC의 UrlMapping에서 하나 불만 사항이 있다면  package개념이 없다는 점이다. WebWork/Struts2의 경우 패캐지 태그안에 하나의 네임스페이스처럼 구성할수가 있는데.. 많은 url mapping이 존재하게 되면 package개념(관례적으로라도) 구성하게 되는데 이런 부분이 없는게 아쉽다. 가령

  1. @Controller
    @RequestMapping("/hello")
    public class HelloWorldController {
       @RequestMapping("/world.html")
       public String hello(ModelMap model) {
           return "hello/world";
       }
    }

이렇게 구성해도 Exception은 발생하지만 않지만 Mapping을 찾지 못한다. 버그인지도 :-)

또 이렇게 Annotation 기반으로 mapping을 할때 고민되는 점이 Interceptor부분이다. XML의 경우 각각 Interceptor마다 UrlHandlerMapping 빈을 만들어서 해결했지만,  Annotation 기반의 매핑의 경우 불가능하다. 결국 생각해 보면,


  1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
      <property name="interceptors">
          <bean class="kata.springmvc.interceptors.UrlPatternMatchingInterceptor" >
  2.          <property name="allowUrlPatterns">
                <list>
                <value>/app/board/*</value>
                <value>/app/client/*</value>
                </props>
            </property>
          </bean>
      </property>
    </bean>

처럼 UrlPatternMatchingInterceptor을 하나 만들어서 (상속해서) handle하도록 해야 할듯 하다.

SpringMVC에서 가장 많이 쓰는 Controller 구현체는 대개 AbstractController과 SimpleFormController,MultiActionController등이 될듯 한데. Annotation기반 설정은 특정 Controller 구현체를 상속받지 않으므로 (더군다나 Servlet API과도 decoupling되는데.)

SimpleFormController 처럼 HTTP Method가 GET 일경우와 POST일 경우 Controller의 움직임을 나누고 싶다면

  1. @Controller
    @RequestMapping("/board_form.html")
    public class BoardFormController {
       @RequestMapping(method = RequestMethod.GET)
       public String onForm(ModelMap model) {
           Board board = new Board();
           model.addAttribute("board", board);
           return "form";
       }
       @RequestMapping(method = RequestMethod.POST)
       public String onSubmit(
  2.         @ModelAttribute("board") Board board,BindingResult result,ModelMap model) {
           return "goto_list";
       }
    }

으로 간단히 구성된다.  initBinder의 경우 annotation으로 존재하고 onValidate같은 경우는 onSubmit에서 메소드를 호출하면 될듯 하다(흐름을 강제적으로 제약되지 않는다.)  SimpleFormController을 쓰면 기본적인 흐름에 대한 필요한 요소만 채워준다는 느낌이 들었는데(Framework과 대화한다고 할까?) 이런 강제적인 부분이 사라진듯 하지만, SimpleFormController에서 약간만 예외적인 흐름을

구성하려고 하면 복잡함이 다가오는 걸 볼때, 이런 간단한 흐름만 제공하는것도 맞을 것 같다.

MultiActionController은 좀더 간단한데,

  1.  @RequestMapping("/multi/list.html")
    public String list(ModelMap model) {
       model.addAttribute("test", new String("test....."));
       return "list";
    }

당연하겠지만 메소드의 return이 void 일 경우 CoC으로 메소드 이름으로 view을 찾는다.

특히 Servlet API을 직접 사용하지 않아도 되는 @RequestParam,@RequestAttribute,@SessionAttributes등이 있다.

이 부분에서 약간 불만인 점은 @RequestAttribute("board") Board board 같은 Form을 Command객체 하나로만 Binding된다는 점이다.  폼이 하나의 객체로만 환원 되는 일이 얼마나 될까. 복잡한 폼일수록 여러 객체를 한 화면에서 Form에 그릴 일이 많아지는데

말이다. 물롬 FormBean처럼 Command 객체를 만들고  Model을 Composite하면 해결되는 문제이긴 하지만.

만일 Form Tag에서

  1. <from:input path="board.title" ...

  2. <form:input path="menu.title" ...
  1. public String list(@ModelAttribute("board")Board board,@RequestAttribute("menu")Menu menu...

이러게 되면 어떨까? '_'  되는데 모르는 걸수도 있겠다. .이 부분은 그냥 나의 짧은 생각.. :-) (해보지 않았음)


약간 2시간 정도면 간단한게 알아 볼수 있을 꺼라 생각했지만..한 4시간은 쓴거 같다.. T_T 사실 tomcat 설정도 기억 안나서 servlet 띠우는 거만으로 삽질한거 보면.. 대충보면 쉬워 보이는 일이라도 막상 하면 쉬운 일은 하나도 없는거 같다.

기술을 하나 제대로 배운다는게 결코 쉬운 일이 아니라는 생각이 든다. 단지 이런 사용법을 안다고 어떤 기술의 가지고 있다고 할수가 없는 거 같다. 배면의 움직임을 이해하고 습득하는게 중요하다고 생각되기도 하지만. 어느 도메인을 추구하는지에 따라 해답을 달라 질듯 하다.  Over Layered Architecture... Layered Developer? :-)




이 글은 스프링노트에서 작성되었습니다.

Posted by anarch

SpringFramework 2.5의 릴리즈에서 많은 부분이 추가/개선되었지만, SpringJDBC에서도 몇가지 개선/추가가 이루어졌다.

우선 SimpleJdbcTemplate에서 SqlParameterSource의 사용과 SimpleJdbcInsert의 추가가 아닐까 싶다.

SimpleJdbcTemplate는 Java5의 varargs와 autoboxing을 지원하여 (특히 목록을 얻어오는 퀴리의 경우) 캐스팅을 별도로 고민 할 필요가 없긴 한데.

문제는 복잡한 퀴리의 경우 classic placeholder ('?')으로는 불편해서 결국 NamedParameterJdbcTemplate으로 작성하곤 했는데.

SimpleJdbcTemplate에서 SqlParameterSource가 가능하게 되었다.


  1. String sql = "SELECT id,title,content FROM boards "+
  2.                                      "WHERE fl_public = :fl_public ";
  3. MapSqlParameterSource params = new MapSqlParameterSource();
  4. params.addValue("fl_public",fl_public);
  5. List<Board> list = this.simpleJdbcTemplate.query(sql,
  6.    new ParameterizedRowMapper<Board>()
  7.   {
          public Board mapRow(ResultSet rs, int rowNum)
  8.           throws SQLException
  9.      {               
  10.          Board b = new Board();               
  11.          b.setId(rs.getLong("id"));               
  12.          b.setTitle(rs.getString("title"));               
  13.          b.setContent(rs.getString("content"));               
  14.          return b;  
  15.      }
      },
  16.   params
  17. );

사실 SimpleJdbcTemplate이 추가될때 추가되어야 할 기능일지도. (...)

그리고 추가된 부분이 SimpleJdbcInsert,SimpleJdbcCall이다. SimpleJdbcCall은 stored procedure을 잘 사용하지 않아서 패스. SimpleJdbcInsert는 QueryObject[PoEAA(316)]패턴과 비슷하게 쿼리를 객체화 할수 있다고 볼수가 있는데. (물론 접근 스타일은 다르지만.)

  1. jdbcInsert = new SimpleJdbcInsert(this.dataSource).withTableName("boards");
  2. Map<String, Object> parameters = new HashMap<String, Object>();
  3. parameters.put("id",id);
  4. parameters.put("title", board.getTitle());
  5. parameters.put("content",board.getContent());
  6. jdbcInsert.execute(parameters);

빈의 프로퍼티과 테이블의 컬럼이름이 잘 맞는다면.

  1. jdbcInsert = new SimpleJdbcInsert(this.dataSource).withTableName("boards");
  2. SqlParameterSource parameters = new BeanPropertySqlParameterSource(board);
  3. jdbcInsert.execute(parameters);

 데이터베이스의 시퀀스 키를 얻을수 있지만(executeAndReturnKey(),usingGeneratedKeyColumns()). PgSql에서 지원하지 않는 데이터베이스라고 나와서 패스. -_- (org.springframework.jdbc.core.metadata에 PostgresqlCallMataDataProvider가 없다.)

SimpleJdbcUpdate나 SimpleJdbcSelect는 없고 단지 SimpleJdbcInsert만 나온 이유가 Insert 문이 가장 생성하기 쉽고.

또 개인적인 경험에 따르면 가장 작성할때 오타가 가장 많이 나는 퀴리인 듯 하다. Update문의 경우 key-value 스타일이기 때문에 Insert문보다는 작성하기 수월한 면이 있다는 걸 부정할수 없겠다.

그리고 Spring이 버젼업하면 SimpleJdbcUpdate나 SimpleJdbcSelect가 나와서 ORM이 없는 어둠의 장소,약속의 저편...에서도 광명이 찾아오지 않을까? :-)






이 글은 스프링노트에서 작성된걸 가지고 티스토리에서 삽질했습니다.

Posted by anarch
이번달 마소에 스크럼 이야기를 잼 있게 읽고 하나 만들어 봤다.
표절과 왜곡이 컨셉이고.



나는 방법론을 좋아하지 않는다.(관심도 없다) 대개 개발자들이 안 좋아한다고는 하는데.
내가 개발자이라서 안 좋아하는지. 천성이 체계적이지 않아서 그런지.
(그래서 개발을 발로 하나? '_')
(우리 회사 유행어다. 발로하는거.)
개인적인 생각은 그 프로세스라는게 무겁고 권위적이기만 하지 말고 엔터테인먼트 요소가 있어야 한다고 생각한다.
또 하나의 걱정은 나이 들어서 현실에 너무 밀착되어서 생각이 편협해지지 않기를 바란다.
이리저리 생각만 많고 몸은 무겁다.
또 하나 기억해야 하는 것.
삶에 대한 고뇌가 깊어질수록 웃을수 있는 여유 또한 넓어진다는 것. :-)


덧. 나도 "감사합니다!"라고 한번 넣고 싶었다.. 회식후 술먹고 작성한것 치고는 잘하지 않았냐? 일필휘지로 이정도면..ㅋㅋ;
Posted by anarch

다소 허망하지만 좋은 프로그래가 어떤 기법을 쓰느냐와 상관없이 좋은 프로그램을 만드는 것과 같이
좋은 모델러는 어떤 기법을 쓰느냐와 상관없이 좋은 모델을 만듭니다.

이 책이 좋은 모델러를 만드는데 기여할수는 있겠지만
책의 방법을 따른다고 "해당 프로젝트의 기간"내에서 좋은 모델이 만들어지지는 않을 것 같습니다.

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


Posted by anarch
TAG 격언,




졸려서 제대로 듣지 못했음. (...)
Posted by anarch