노무현 전 대통령 서거 - 삼가 고인의 명복을 빕니다

자기 전에 잠깐 살펴본 Spring Roo

초인 블로거 Toby님의 친절한 설명 시리즈(1,2,3,4)와 기선님의 설치예제 실행법에 힘입어 저도 자기 전에 (사실 아직 잠을 안 잤으니 자기 전일지 아애 잠을 안 잘지는 잘 모르겠네요) 잠깐 Roo를 맛 봤습니다.

사실 Roo라는 것도 한 달 전 쯤 Toby님에게 우연히 듣게 되었고 몇가지 블로그 글 읽으면서 코끼리 다리 만지듯 이런 저런 상상만 하고 있었는데 이렇게 발표가 되니 반갑기도 하고 당황스럽기도 합니다.

일단 열어 본 첫 인상은 이 Roo가 그 Roo 맞나 싶습니다. 얘기 듣던 것과 구조가 많이 달라서요. 알렉스 아저씨가 지난 3년 동안 발전시킨 생각을 반영해서 원래 Roo와 다른 공개용 Roo를 새로 만든 건 아닌지 모르겠네요. (제가 좀 넘겨 짚기 대왕입니다. -_-);

자세한 것은 Toby님 글을 보면 될 것이고... Toby님 글을 보고 궁금했던 것만 따로 좀 살펴 보았습니다.

우선 Domain에 Repository의 로직을 통합했는데 이건 정확히 Active Record Pattern으로 보입니다. 사실 RoR의 ActiveRecord가 진짜 ActiveRecord는 아닌 것 같다고 생각하고 있었는데... (물론 큰 의미는 없는 구분입니다만) Roo에서 사전적인 구현을 보게 되었네요.

제가 Toby님 글에 답글로 너무 RoR과 비슷하게 만들려고 한 것 아니냐고 쓴 것에 대해 Toby님께서 Roo는 DDD를 구현했다는 점에서 다르다고 하셨는데 DDD가 가능하다는 것은 알겠지만 예제는 자동 생성된 CRUD와 Finder 몇개 외의 DDD의 맛을 볼 수 있는 부분은 아직 없는 듯 합니다. 앞으로 예제가 보완되기를 기대합니다.

다음은 Controller 부분을 봤습니다.

자동 생성된 컨트롤러는 아무런 로직도 없습니다.

@RooWebScaffold(automaticallyMaintainView = true, formBackingObject = PetType.class)
@RequestMapping("/pettype/**")
@Controller
public class PetTypeController {
}

그래서 믹스인되는 AspectJ 파일을 보니 여러가지가 메소드가 정의되어 있네요.
  • create() : RequestMapping(value = "pettype", method = RequestMethod.POST) 
  • createForm() : RequestMapping(value = "pettype/form", method = RequestMethod.GET)
  • show() : RequestMapping(value = "pettype/{id}", method = RequestMethod.GET)   
  • list() : RequestMapping(value = "pettype/{id}", method = RequestMethod.GET)   
  • update() : RequestMapping(method = RequestMethod.PUT)   
  • updateForm() : RequestMapping(value = "pettype/{id}/form", method = RequestMethod.GET)   
  • delete() : RequestMapping(value = "pettype/{id}", method = RequestMethod.DELETE)   
REST 방식입니다. Parameter가 아닌 PathInfo에서 id를 받고 있는 것도 그렇고 create에 POST, update에 PUT, delete에 DELETE HTTP Method를 사용하는 것도 그렇고 말이죠.
그러니 당연히 spring 3를 쓰고 있다는 거죠.

발표가 안 될 것 같았던 Roo가 Spring 3의 마일스톤 버전이 공개되는 이 시점에 갑자기 발표되는 것이 어떤 의미가 있는 듯 합니다. 마치 개발하기 시작한지 얼마 안 된 것 같은 모습으로 말이죠.

그리고 아직 Presentation 단에는 별다른 기술이 적용되지 않았습니다. 그냥 담백하게 jstl로 갈지, 아니면 다른 생산성 높은 기술이 적용될지 궁금합니다. 저는 웹 어플리케이션의 생산성 향상의 키는 Presentation 단에 있다고 생각합니다. 재사용성을 높이고 변경에 빨리 대응할 수 있는 기술이 적용되기를 기대해봅니다.

가장 감동 받은 부분은 domain에 static method로 믹스인 되는 finder 메소드입니다.
사실 제가 작년에 spring jdbc를 가지고 active record를 구현하려고 시도했었습니다. 한참 진행하다 바쁘고 만들어봤자 쓸 사람도 없고 (회사가 망했습니다. -_-) 해서 그냥 포기했는데요. 그 때 finder를 domain의 static method로 넣고 싶었지만 generic으로는 방법이 없더라고요.

그냥 범용 finder를 만들고 domain class를 parameter로 넘기거나

MyDomain domain = finder.findById(MyDomain.class, 100);


Generic을 사용해서 finder를 domain과 분리하여 따로 만드는 수 밖에 없는 것 같더군요.

MyDomain domain = new Finder<MyDomain>().findById(100);

제가 원하던 방식은 이런 건데 말이죠.

MyDomain domain = MyDomain.findById(100);

그런데 Roo는 AOP로 간단히(?) 구현했네요. 감동이 쓰나미 처럼 몰려옵니다. (다만 Roo의 XXX_Finder.aj에 있는 Finder 메소드들은 Domain 객체를 찾아주지는 않고 JPA Query 객체를 반환합니다. 유연성 때문인지... 반면에 XXX_Entity.aj의 FindXXX 메소드들은 도메인 객체를 직접 읽어서 반환합니다. )

솔직히 지금까지 Mixin이 복잡도를 너무 높이는 것 아니냐는 생각을 가지고 있었습니다. 그런데, 이미 객체지향기술도 한물갔다는 소리가 나오고있는 상황이고... 그동안 트랜젝션 처리나 로깅 정도로만 생각했던 AOP가 어떻게 변화를 줄 수 있는지 실감이 나네요.
 
우짰든 살펴보니 아직 개발 초기단계인 듯 하고 앞으로 계속 주목해야 할 것 같습니다. 완성되면 어떨지 몰라도 아직은 광고처럼 대단해 보이지는 않네요. Toby님 말씀처럼 DDD 대응이라는 점과 Spring을 그대로 사용할 수 있다는 부분이 경쟁 기술에 비해서 나은 점으로 보입니다.

살펴본 것은 30분도 안 되는데 글 쓰느라 한 시간도 넘게 지나갔네요. 글만 쓰면 왜 이렇게 머리 속이 하얘지는 지... 자겠습니다. -_-

by 박성철 | 2009/05/09 04:36 | 프로그래밍 이야기 | 트랙백 | 덧글(3)

스프링에서 스캔된 컴포넌트에 외부 Property 값 주입하기

서설

스프링 2.5에 새로 추가된 기능 중 예상 외로 인기 있는 기능이 컴포넌트 스캔이다. Classpath를 따라서 탐색 하다가 정해진 필터링 규칙에 맞는 스프링 빈을 찾아 컨테이너에 등록해주는 기능인데 처음 들었을 때 잘 못 쓰면 독이 되겠다 싶었다. Autowiring은 몰라도 최소한 어떤 빈이 사용되는지는 XML에 등록해 주는 것이 관리 하기 좋지 않겠냐는 생각이었다. 빈 등록까지 자동으로 해버리면 애플리케이션 구성을 추적하기 어려워 유지보수가 곤란할 것이기 때문이다. 결국 나는 서비스 계층 후면은 XML을 사용한 전통적인 빈 설정으로, Web Tier는 빈 스캐닝과 Autowiring을 이용하는 방식으로 용처를 정리해서 사용하고 있다.

하지만 내가 결정권이 없는 여러 프로젝트에서 빈 스캐닝 기능을 사용하는 일이 많은 것이 현실이다. 그리고 절대 저질러서는 안 되는 범죄도 아니고 대부분 그리 복잡한 애플리케이션도 아니니 잘못 되었다고 말할 것도 아니다. 결국 두어 번 이런 상황에서 일을 하게 되었다. (사실 XML로 설정을 한다고 해도 관리가 허술하면 복잡하기는 마찮가지다. 설정 관리자라는 롤이라도 두어야 하는 걸까?)

이런 저런 것 떠나서 빈 스캔 기능을 사용하면 가장 곤란한 것이 특정 값을 빈에 설정할 수 없다는 것이다. 스프링은 단순히 빈 간의 종속성 문제만 해결해줄 뿐 아니라 빈의 초기 상태를 설정할 수도 있다. 그런데 XML이 아닌 방법으로 스프링에 의존하지 않고 POJO의 순수성을 유지한채 특정 값을 설정하는 방법이 마땅치 않다. (적어도 내 지식으로는 그렇다.)

애노테이션 환경에서 Property 값 설정

PropertyInjectionConfigurer는 스프링의 PropertyPlaceholderConfigurer나 PropertyOverrideConfigurer와 비슷한 기능을 한다. 즉, 빈 초기화 단계에서 지정된 Property 파일의 값을 특정 빈의 필드에 넣거나 특정 메소드를 이 값을 가지고 호출한다. 다만 XML 설정이 아닌 애노테이션 방식을 사용한다는 것이 다르다. @Property 애노테이션을 Property 값을 받기 원하는 필드나 메소드에는 달아 놓으면 PropertyInjectionConfigurer가 빈 초기화 단계에 이 애노테이션을 인식해서 원하는 값을 찾아 설정을 해준다.

물론 지금 1.0 M4 상태인 Spring JavaConfig나 1.0 M3 상태인 스프링 3가 정식 발표 된다면 문제가 안 되는 부분일 수 있다. 그러니 스프링 2.5만을 사용하고 JavaConfig를 기다릴 수 없는 (또는 JavaConfig를 사용 안 할) 프로젝트에만 사용하면 될 듯 하다. 그리고 JavaConfig의 @ExternalValue가 맘에 안 드는 사람도 있을 듯 하다.

파일 다운로드

property_injection.tar.gz

소스 파일을 이클립스에서 Java 프로젝트(Spring IDE plugins가 설치되어 있으면 spring project)로 Import해서 열면 바로 사용이 가능하다. 일단 처음에는 테스트가 있으니 작동 유무를 이를 통해서 확인 한다.

기존에 프로젝트 코드 베이스에 포함되어 있던 클래스 하나만 분리해서 만든 프로젝트이니 굳이 jar로 만들어 쓰기 보다는 그냥 사용할 프로젝트의 코드 베이스에 복사해서 패키지를 적절하게 변경한 다음에 사용하는 것이 좋을 듯 하다.

설정

가장 먼저 PropertyInjectionConfigurer를 XML에 등록한다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="location" value="property_injection/test.properties"/>
    </bean>

location 속성에 설정 값이 등록되어 있는 propertis 파일의 경로를 지정한다. 만약 properties 파일이 여러개라면 이렇게 할 수도 있다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="locations">
            <list>
                <value>classpath:property_injection/test.properties</value>
                <value>classpath:property_injection/another.properties</value>
            </list>
        </property>
    </bean>

물론 간단히 직접 Properties 값을 주는 것도 가능하다.

    <bean class="property_injection.PropertyInjectionConfigurer">
        <property name="properties">
            <value>
                mail.server=mail.domain.net
                mail.sender=admin@domain.net
            </value>
       </property>
    </bean>

설정 방법은 PropertyPlaceholderConfigurer와 같으니 레퍼런스를 참조하도록 한다.

필드에 @Property 사용

일단 PropertyInjectionConfigurer를 등록했으면 Property 값을 지정하고 싶은 필드나 메소드에 @Property 애노테이션을 표기해야 한다. 직접 필드에 값을 지정하도록 할 수 있다.

    @Property(name="mail.server")
    private String mailServer;

메소드에 @Property 사용

메서드에 @Property를 달아 놓으면 초기화 단계에 해당 메소드를 호출한다. 이 메소드는 매개 변수가 하나 이상이어야 한다.
   
    @Property(name="mail.server")
    public void setMailServer(String mailServer) {
        this.mailServer = mailServer;
    }

필수가 아닌 Property

만약 지정한 Property 값을 찾지 못하면 예외를 발생시키기 때문에 빈 생성을 하지 못하게 된다. Property 값이 있어도 되고 없어도 되는 값이라면 required 속성를 false 값으로 바꾸어 예외 발생 없이 넘어가도록 할 수 있다.

  @Property(name="property.name" required=false)

required 속성을 사용할 때에는 기본값을 필드에 지정하는 것이 좋을 것이다.

  @Property(name="mail.server" required=false)
  private String mailServer = "default.mailserver.com";

by 박성철 | 2009/05/05 05:09 | 프로그래밍 이야기 | 트랙백 | 덧글(0)

Linux Mint 6에서 Ubuntu 9.04로 Upgrade하는 불친절한 가이드

얼마 전 gnome이 2.26이 나왔습니다. 그리고 ubuntu 2009년 4월 버전도 발표가 되었죠. 일 년에 두 번 딱딱 약속 잘 지켜 출시하는 이 두 오픈소스 프로젝트 때문에 사는 게 조금은 더 재미있는 것 같습니다.

일 때문에 며칠 기다렸다가 노트북에 먼저 설치하기로 했습니다. 그런데 한동안 안 썼던 이 노트북, 켜고 보니 Unbuntu가 아니라 Mint Linux이더군요. 두어 달 전에 Ubuntu가 이상 작동을 해서 새로 설치한답시고 설치한 게 이놈이었습니다.

새로 설치할까 하다가 포맷에 부담도 있고 10여 년 데비안 계열을 쓰면서 지금까지 OS를 밀고 새로 설치한 경우는 거의 없었기 때문에  Upgrade를 시도해 봤습니다.

Debian과 Ubuntu는 패키지 시스템이 같을 뿐 전혀 따로 관리가 되는 별개 배포본입니다. 하지만, Mint는 Ubuntu 배포본을 그대로 쓰면서 거기에 몇 가지 패키지를 패치 하는 형태입니다. Ubuntu 네 집에 더부살이하는 격이죠. 그래서 Upgrade가 가능합니다.

그냥 생각나는 대로 진행한 것이니 참고만 하세요. 글로 정리하려는 생각 없이 했기 때문에 화면 갈무리나 상세한 절차는 생략합니다. (사실 생략이 아니라 까먹었습니다. -_-);

먼저 소스 리스트 설정을 바꿉니다.

sudo vi /etc/apt/preferences


열어보면 민트 리눅스의 패키지 우선순위를 우분투보다 앞서게 되어 있습니다. 이것을 변경해서 우분투가 더 앞서도록 해줍니다. 전 민트를 500으로 우분투를 700으로 했습니다.

Package: *
Pin: release o=linuxmint
Pin-Priority: 500

Package: *
Pin: origin packages.linuxmint.com
Pin-Priority: 500

Package: *
Pin: release o=Ubuntu
Pin-Priority: 700


이렇게 하고 패키지 업데이트를 했습니다. 뭘 열심히 깔더군요.

그리고 ubuntu-desktop 패키지를 설치합니다. 이 패키지를 설치할 때에 몇 가지 디펜던시 에러가 나서 수동으로 이 패키지 두어 개를 설치해주었습니다.

ubuntu-desktop을 설치한 후에 민트 소스 리스트를 제거했습니다.

sudo vi /etc/apt/sources.list


~ 생략 ~

## -----------------------
## LINUX MINT REPOSITORIES
## -----------------------

## +++ Linux Mint 6 Felicia (stable) +++
# deb http://packages.linuxmint.com/ felicia main upstream import

## +++ Backports (not as stable) +++
# deb http://packages.linuxmint.com/ felicia backport

## +++ Community (not as stable) +++
# deb http://packages.linuxmint.com/ felicia community

## +++ Romeo (unstable) +++
# deb http://packages.linuxmint.com felicia romeo

~ 생략 ~


저장 후 다시 패키지 업데이트를 하고 mint라는 이름이 들어간 모든 패키지를 삭제했습니다.

이제 업그레이드를 해야 하는데 그 전에 설치된 배포판의 정보를 Mint 6에서 Ubuntu 8.10으로 변경해야 합니다.

/etc/issue파일을 열어서 다음처럼 수정합니다.

sudo vi /etc/issue

Ubuntu 8.10 \n \l


이번에는 /etc/lsb-release 파일을 수정합니다.

sudo‎ vi /etc/lsb-release

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=8.10
DISTRIB_CODENAME=intrepid
DISTRIB_DESCRIPTION="Ubuntu 8.10"


이제 설치할 Ubuntu 9.04의 ISO 이미지를 내려받습니다. Upgrade에 사용할 배포 이미지는 Desktop도 Server도, 그리고 이번에 추가된 넷북 버전도 아닌 alternate 입니다. 다음 페이지의 아래쪽에 내려가면 iso 이미지가 여러 개 있는데 이 중 가지고 있는 장치의 CPU 타입에 맞는 alternate 배포 이미지를 선택해서 받으십시오.

http://releases.ubuntu.com/jaunty/

제가 쓰는 노트북은 코어 듀오이기 때문에 ubuntu-9.04-alternate-i386.iso를 받았습니다.

다 내려받았으면 이 이미지를 cdrom으로 마운트 합니다.

sudo mount -o loop ubuntu-9.04-alternate-i386.iso /media/cdrom0

마운트 하면 업그레이드 프로그램이 자동 실행됩니다. 만약 실행되지 않으면 Alt-F2를 누른 후 다음 명령줄을 입력합니다.

gksu "sh /cdrom/cdromupgrade"

그리고 안내에 따라서 업그레이드를 실시!

약 두 시간 정도 걸린다고 해서 켜놓고 잤습니다. 아침에 일어나면 다 끝나 있겠지... 하고 말이죠.

하지만! /etc/ 디렉토리 아래 설정 파일 중에서 수작업으로 변경한 것을 어찌할 거냐는 질문이 뚝! 떠 있고 한 시간이 더 넘는 분량이 남아있더군요. 혹시 무조건 yes를 누르는 방법이 있는지 알아보고 하세요.

거의 다 설치가 되었나 싶었는데 거의 마지막 단계에서 커널 이미지가 설치되지 않고 오류가 나더군요. 메시지를 보니 update-grub이라는 명령이 없다는 오류...

그래서 보니까 grub과 gfx 테마가 설치 안 되어 있더군요. 그래서 수작업으로 설치했습니다.

sudo apt-get install grub gfxboot-theme-ubuntu

이렇게 하고 upgrade

sudo aptitude upgrade

마지막으로 grub 설정을 grub2에 맞춰서 변경해줍니다.

sudo upgrade-from-grub-legacy

와! 정말 쉽지요? ;;;;

P.S. 1 그냥 밀고 새로 설치하는 시간보다 2-3배밖에 안 걸립니다.  ㅡ,.ㅡ);
P.S. 2 이렇게 업그레이드하면 새로 추가된 기능이 빠지는 일이 많습니다. 열심히 업그래이드 해서 잘 쓰다가 옆에 있는 친구의 우분투를 보면 뭔가 멋지고 신기하고... 같은 버전인데... -_-);

by 박성철 | 2009/05/01 14:03 | 프로그래밍 이야기 | 트랙백 | 덧글(2)

◀ 이전 페이지다음 페이지 ▶