Whiteship's Note

'2008/10'에 해당되는 글 65건

  1. 2008.10.23 Maven 프로젝트 의존성 파일들 패키징하기 (2)
  2. 2008.10.22 보고 싶은 피아노 공연
  3. 2008.10.21 Maven 같지 않은 Maven 프로젝트 만들기 (2)
  4. 2008.10.21 AspectJ의 @DeclareError를 사용해서 컴파일 시점에 아키텍처 에러 검증하자. (6)
  5. 2008.10.21 Spring Architecture - Eberhard Wolff
  6. 2008.10.21 OSGi 툴 세트 Pax
  7. 2008.10.21 게시판 페이징은 어떤 기능을 갖추고 있어야 할까? (2)
  8. 2008.10.20 Maven 멀티 모듈 프로젝트 구성하기 (2)
  9. 2008.10.19 오랜만에 스프링 MVC 다시 정리
  10. 2008.10.17 약간 어설픈.. OSAF 그리드 태그 완성
  11. 2008.10.17 12월 Whiteship's 미국 비행기 일정표
  12. 2008.10.16 스프링 DM 프로젝트 빌드하기
  13. 2008.10.14 OSAF 메이븐 저장소 사용하기 (13)
  14. 2008.10.14 스프링 AOP 선택, 활용, 이슈 - 발표자료 & 소스코드 (8)
  15. 2008.10.14 태그 파일에서 객체 노출시키기
  16. 2008.10.13 DisplayTag에서 팝업 링크 달기
  17. 2008.10.12 제 9회 스프링 세미나 후기 (23)
  18. 2008.10.11 맥북에 Postgres 설치하기
  19. 2008.10.10 Spring 2.5 on the Way to 3.0 - 유겐 휄러
  20. 2008.10.09 내사랑 내곁에
  21. 2008.10.08 AspectJ와의 연동을 고려한다면, 포인트컷을 최소화 해야합니다. (2)
  22. 2008.10.08 스프링에서 하이버네이트와 JDBC 같이 사용할 때 트랜잭션 처리는?
  23. 2008.10.06 개발에 필요한 AOP 뭐가 있을까? (2)
  24. 2008.10.06 그리움만 쌓이네
  25. 2008.10.05 나이쓰~!!! 계층형 아키텍처 검증 용 Aspect (8)
  26. 2008.10.04 Spring DM 1.1.2 릴리즈~
  27. 2008.10.04 AOP를 설명하는 그림 두 장 (2)
  28. 2008.10.04 FUD는 Fear, Uncertainty, Doubt (2)
  29. 2008.10.02 AspectJ로 final method에도 위빙하기
  30. 2008.10.02 CGLIB 프록시 제약 사항 테스트. (2)

Maven 프로젝트 의존성 파일들 패키징하기

Build/Maven : 2008.10.23 10:03


보통, mvn package를 하면, 해당 프로젝트의 의존성 라이브러리들은 그대로 둔채 패키징을 합니다. 그런데, 배포 할 때는 maven을 사용하지 않는 분들을 위해서 이 프로젝트가 의존하고 있는 다른 라이브러리들도 같이 배포해주는 것이 좋을 겁니다. 그럴 때 유요한 플러그인은 바로 maven-dependency-plugin 입니다.

maven-assembly-plugin이 이런 일을 해주는게 아닌지 싶어서 이것 저것 해봤는데, 이 플러그인은 컴파일된 class 파일들을 만들어서 의존하고 있는 클래스들까지 통째로 가지고 있는 jar 파일을 만들 때 사용하는 거더군요. 뭐 의존성 관리 안하고 한 방 파일로 마치 cglib-nodep 같은 걸 만들 때 사용하면 모를까. 의존성 라이브러리를 계속 버전업 하며 교체할 수 있는 형태로 제공하려면 이 플러그인 보단 maven-dependency-plugin을 사용해서 의존성 라이브러리를 별도의 폴더(보통 lib에 두죠)에 같이 배포해주는 것이 좋겠습니다.

방법은 간단합니다.

<build>
...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${project.build.directory}/lib
                            </outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>
                                false
                            </overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
...
</build>

이런식으로 pom.xml의 <build> 안에 <plugins> 안에 넣어주면, target 폴더 아래 lib 폴더를 만들고 그 안에 의존성 라이브러리들을 쫘르륵~ 복사해 줍니다.
top


보고 싶은 피아노 공연



어렸을 때 피아노 배운걸 다시 되돌리고 싶어서, 요즘 피아노 학원에 다니고 있습니다. 최근에 다시 시작한지는 얼마 안 됐습니다. 성인 피아노 학원을 찾기 힘들어서 어딜 다닐까 고민하다가 신촌에 하나 있다는 검색 결과를 보고 찾아갔는데, 정말 옛날 피아노 학원 분위기가 나더군요.ㅋㅋ 아주 쫍은 방에 피아노 한 대씩 있고 선생님이 잠깐 들어와서 어떻게 치는지 보여주고 연습하는 식입니다. 바이엘 부터 시작해서 요즘은 체르니 100번 이랑 무슨 곡 모음집 같은걸 치고 있는데 그 책에 '아기 공룡 둘리', '또 만나요.', '검은 고양이 네로', '빨간 머리 앤'ㅋㅋㅋ 이런게 들어있어서 아주 재미납니다.

피아노 관련 공연이 뭐 없을까 찾아봤더니 예상외로 공연이 상당히 여러 개여서 놀랐습니다. 그 중에 두 개 정도가 눈에 띄는군요.

사용자 삽입 이미지

하나는 너무 비싸서 못갈지 싶은 빌리 조엘 내한 공연입니다. 볼만 할 것 같은데, 가격이 정말... ㄷㄷㄷ입니다. 이건 뭐 1년에 한 번 정도는 이런 공연을 봐도 괜찮을 것 같긴한데, 글쎄요.. 피아노맨 떄문에 보고싶긴 한데 그 정도로 땡기지는 않습니다.

사용자 삽입 이미지

일정표 more..


 다른 하나는 그나마 갈 수 있어 보이는 가격의 2008 피아니스트 김정원 전국 투어 리사이틀입니다. 김정원씨는 '호로비츠를 위하여'라는 영화 맨 마지막에 나오는 피아니스트더군요. 그 영화볼 때 제가 젤 좋아하는 라흐마니노프 피아노 협주곡 2번을 연주해서 정말 좋았던 기억이 나는데, 이번엔 직접 볼 수 있기를 기대해 봅니다.

흠냐.. 아무래도 두 번째 걸 가지 않을까 싶습니다. 누구랑 간담;;

'모하니? > 그냥 놀아' 카테고리의 다른 글

지금은 공항이에요  (8) 2008.11.30
LA, 마이애미, 아틀란타 날씨  (0) 2008.11.28
회사 이사 중  (2) 2008.11.21
개발자들의 수다 후기  (0) 2008.11.09
꺄오~~ 살꺼야. 키보드. 카시오 PX-320  (8) 2008.10.30
보고 싶은 피아노 공연  (0) 2008.10.22
방송탔네~  (0) 2008.10.01
KSUG 도배 성공  (2) 2008.10.01
HP CP1215 맥에서 사용하기  (0) 2008.09.25
KSUG 9번째 스프링 세미나  (2) 2008.09.24
야후 블로그 순위 whiteship's note 156위  (4) 2008.09.08
top


Maven 같지 않은 Maven 프로젝트 만들기

Build/Maven : 2008.10.21 15:28


사용자 삽입 이미지

위 프로젝트는 일반적인 이클립스의 웹 프로젝트와 다름 없이 src와 test 소스 폴더를 가지고 있고, 웹 폴더도 루트바로 밑에 webapp를 사용하고 있습니다. 하지만, 이 녀석은 메이븐 프로젝트 입니다. 맨 아래에 pom.xml 보이시죠?

사부님이 전에 작성하셨던 메이븐 기본 디렉터리 설정 방법과 Arawn님의 메이븐 웹 폴더 설정 플러긴 사용방법을 조합하면 위와 같은 프로젝트를 만들 수 있습니다.

둘 다 pom.xml의 build 엘리먼트 내부에 적절하게 넣어주면 됩니다.

<build>
...
        <sourceDirectory>${project.basedir}/src</sourceDirectory>
        <scriptSourceDirectory>
            ${project.basedir}/scripts
        </scriptSourceDirectory>
        <testSourceDirectory>
            ${project.basedir}/test
        </testSourceDirectory>
        <resources>
            <resource>
                <directory>${project.basedir}/src</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
        </resources>
...
        <plugins>
...
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1-alpha-2</version>
                <configuration>
                    <warSourceDirectory>webapp</warSourceDirectory>
                </configuration>
            </plugin>
...
        </plugins>
<build>

설정 내용은 엘리먼트 이름을 보면 대충 알 수 있으면 자세한 설명은 사부님과 Arawn님의 블로그에 가셔서 보시면 되겠습니다. ㅋㅋ
top


AspectJ의 @DeclareError를 사용해서 컴파일 시점에 아키텍처 에러 검증하자.

AOP : 2008.10.21 11:26


참조: http://www.parleys.com/display/PARLEYS/Home#slide=1;title=Spring%20Architectures;talk=20676612

위 발표자료 내용 주에 아주 잼나는 코드를 건졌습니다. 지난 번 KSUG에서 발표한 내용과 겹치는데 아래 코드는 그때 제가 보여드린 코드보다 좀 더 좋은 것 같아서 가져왔습니다.

@Aspect
public class SystemArchitecture {
  @Pointcut("execution(* configurator.*.*(..))")
  public void configuratorLogic () {}
  @Pointcut("execution(* dao.*.*(..))")
  public void dao() {}
  @Pointcut("within(*.dao.*)")
  public void inDaoLayer() {}
  @Pointcut("call(* *.service.*.*(..))")
  public void callServiceLayer() {}
}


@Aspect
public class Layering {
  @DeclareError("SystemArchitecture.inDaoLayer() && "+
   "SystemArchitektur.callServiceLayer() ")
  public static final String DAOsNotFromServices =
   "DAO must not call Service!";
 @DeclareError(" (call(* java.sql.*.*(..)) && " +
  "!within(*.dao.*) ) ")
  public static final String JdbcOnlyInDAOs =
   "JDBC only in DAOs!";
}

좋은 건 이Aspectj를 사용하면 @DecalreError를 사용해서, 컴파일 시점에 아키텍처 에러를 검증할 수 있다는 것입니다. 제가 준비했던 코드는 cflow를 사용해서 런타임에 검증하는 방법이었습니다. 따라서 테스트 하지 않고 그냥 커밋하면 뭐 어떻게 찾아낼 방법이 없었습니다. 그런데 이 방법을 쓰면 코딩할 때 문제되는 코드를 발견할 수 있으니 훨씬 좋은 것 같습니다. 캬.. 귿..


top


Spring Architecture - Eberhard Wolff



참조: http://www.parleys.com/display/PARLEYS/Home#slide=1;title=Spring%20Architectures;talk=20676612

아키텍처
The software architecture of a program or computing system is the structure or structures of the system, which comprise software components, the externally visible properties of those components, and the relationships between them.

Wikipedia

객체: Information hiding

클래스: 객체의 타입을 정의. White box 재사용.

재사용:
A Software Component is a unit of composition with contractually-specified interfaces and explicit  context dependencies only. A software component can be deployed independently and is subject to composition by third parties.

C. Szyperski: Component Software - Beyond Object-
Oriented Programming, Addison-Wesley, 1999

컴포넌트: note the plugability.
- 계약 같은 인터페이스
- Only 명시적인 의존성
- 독립적인 배포
- 컴포지션
- 결과: 간편한 재사용(black box)
- 결과: 간편한 수정(인터페이스는 놔두고 내부만 바꿀 수 있으니.)

객체는 컴포넌트인가?
- 계약 같은 인터페이스? 있네, public 메소드
- 명시적인 의존성? 없네. 코드 내부에서 암거나 만들 수 있으니까.
- 독립적인 배포? 대부분은 안 돼지.
- 컴포지션? No.

DI를 사용하는 객체는 컴포넌트?
- 계약 기반인가? 응
- 명시적인 의존성? 응 설정하자나. setter/constuctor injection,
- 독립적인 배포? Yes. Everything else is injected
- 컴포지션? Yes. DI 컨테이너를 사용해서 조합하니까.

계층
- 각각의 계층은 하위 계층에만 의존해. -> 더 나은 의존성 관리(계층을 쉽게 바꿀 수 있으니까..)
- Typical technical
- 퍼사드를 사용할 수 있음.

스프링을 사용하는 컴포넌트
- 스프링 설정 파일을 각각의 컴포넌트 별로 만든다.
- 추가적인 기반 설정 파일은 javase.xml로..
- Facade를 인터페이스로 추가하고, bean 설정 파일에서 의존성을 정의한다.
- 각각의 컴포넌트는 JAR 파일로.
- 자신만의 빌드 스크립트를 가지고 있고
- 그들 컴포넌트를 묶을 때는 classpath*을 사용한다.

ApplicationContext applicationContext =
 new ClassPathXmlApplicationContext(
  "classpath*:/config/applicationContext.xml");

스프링 설정 파일 + Facade 사용했을 때 컴포넌트 체크 리스트
- Contractually-specific 인터페이스? Yes. Facade 사용했으니까.
- Only explicit context depedencies? No. 스프링 빈 의존성이 명시적이지 않을 수도 있다.
- 독립적인 배포 가능? Yes. 설정 파일 + 참조하는 클래스
- A way of composition? Yes. 스프링 DI.

계층은 어떤가?
- ApplicationContext 계층 구조를 사용해서 구현했다.
- DispatcherServlet과 ApplicationContext 같은 예.
- 예제 코드

ApplicationContext environmentApplicationContext =
 new ClassPathXmlApplicationContext(
  "javase.xml");

ApplicationContext persistenceApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:*-persistence.xml" },
 environmentApplicationContext);

ApplicationContext logicApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:*-logic.xml" },
  persistenceApplicationContext);

ApplicationContext guiApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:*-gui.xml" },
  logicApplicationContext);

Vertical Slices.
- 예제 코드

ApplicationContext environmentApplicationContext =
 new ClassPathXmlApplicationContext("javase.xml");

ApplicationContext catalogApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:/catalog-*.xml"},
 environmentApplicationContext);

ApplicationContext configuratorApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:/configurator-*.xml" },
 catalogApplicationContext);

ApplicationContext trackingApplicationContext =
 new ClassPathXmlApplicationContext(
  new String[] { "classpath*:/tracking-*.xml"},
configuratorApplicationContext);

이후 발표는...
- 스프링 JavaConfig를 사용해서 스프링을 사용해서 컴포넌트 설정 예제
- 스프링 DM을 사용하여 컴포넌트 개발 예제
가 이어집니다.

top


OSGi 툴 세트 Pax

Spring DM/OSGi : 2008.10.21 00:16


홈피 http://wiki.ops4j.org/confluence/display/ops4j/Open+Participation+Software+for+Java

관련글 http://www.jroller.com/habuma/entry/is_your_osgi_toolset_pax

'Spring DM > OSGi' 카테고리의 다른 글

OSGi 툴 세트 Pax  (0) 2008.10.21
Shared Mutable State  (2) 2008.09.25
The Price of Freedom  (0) 2008.09.25
Concurrency and OSGi  (0) 2008.09.25
BundleContext로 할 수 있는 일  (0) 2008.06.25
2 Security Layer  (0) 2008.02.18
1. Introduction  (0) 2008.02.17
top

TAG OSGi, tool

게시판 페이징은 어떤 기능을 갖추고 있어야 할까?



봄싹 스터디에서 게시판을 구현하기로 했습니다. 간단한 CRUD와 페이징처리가 주 목적이며, 저를 포함한 대, 여섯 명의 개발자 분들이 자기 나름대로 구현해온 코드를 서로 보여주고 피드백을 받는 방식으로 진행될 예정입니다. 이를 통해 스프링을 실제 프로젝트에 적용하는 방법은 물론이거니와, 게시판 구현 방법에 대한 고민도 나눌 수 있을 것 같고, 개발 전반에 좋은 코드, 좋은 개발 방법 및 현재 자신에게 필요한 학습은 무엇인가에 대한 고민까지도 해볼 수 있는 좋은 스터디로 생각하고 있습니다.

Anyway, 기존 애플리케이션들의 UI를 보면서 페이지 네비게이션을 어떻게 구현하면 좋을지 고민해봤습니다.

Gmail


- 처음은 일단 가장 최신 글(메일)을 보여주고, "이전" 과 "처음"이 있군요.
- "이전"을 클릭하면 "이전" 과 "처음"은 그대로 있고, "다음"이 추가됩니다.
- 또 한 번 "이전"을 클릭하면 "이전"과 "처음", "다음"은 그대로 있고, "최근"이 추가됩니다.

두 번째에서 "다음"만 추가한 건 "다음"이 곧 "최근"이기 때문이죠. 이런 페이징의 단점은 중간으로 이동할 수가 없다는 겁니다. "다음"만 10번 눌렀다가 세 번째 페이지로 이동하고 싶을 땐 어떻게 하죠?? 바로 이동할 수 있는 방법은 없습니다. "최근"으로 갔다가 "이전"을 세 번 클릭하는 수밖에..

한 페이지에 보이는 목록의 갯수 설정은 환경 설정에서 할 수 있습니다. 기본으로 50개씩 보여주는데, 이 설정을 별도의 페이지로 옮겨둔건 좋은 것 같습니다. 자주 바꾸는 설정도 아닐텐데 자꾸 눈에 들어오면 괜히 한 번 해보고싶고, 그럼 괜히 불필요한 요청만 늘어날 뿐이니까요. 이런 경우에는 대신 모델 설계가 좀 달라질 것 같습니다. 게시판에 대한 설정을 담고 있는 별도의 모델이 있고 그 모델의 속성으로 한 페이지당 목록 갯수가 들어있을 것 같습니다. 반대로, 만약 페이지와 한 페이지에 대한 목록 갯수가 같이 따라 다닌다면, 페이징 관련 모델에 한 페이지 목록 갯수가 속성으로 들어있겠죠?

Forum


KSUG 포럼 게시판의 페이징을 봤습니다. 처음 모습 그대로를 유지하고, 버튼의 색으로 현재 페이지를 구분하고 있습니다. 일단 위에서 언급했던 중간 페이지로의 이동이 가능해졌습니다. 대신, 여기서도 한 페이지당 갯수 설정 화면은 보이지 않습니다. 어딘가 게시판 설정 페이지에 있을 것 같습니다. 포럼과 Gmail과의 공통점은 둘 다 거의 한 페이지의 내용이 브라우저 범위를 벗어나서 스크롤리 필요하다는 겁니다. 그래서인지, 페이지 네비게이션이 화면의 가장 상단과 가장 하단 두 곳에 모두 위치하고 있습니다. 이런 UI가 사용자에 편하다는 반증으로 생각됩니다. 따라서 화면을 만들 때 네비게이션 부분을 컴포넌트화 해야겠다는 생각이 듭니다. 그래야 코드 중복도 최소화 하고 관리하기 쉬울테니까요.

게시판


제가 자주가는 어떤 사이트의 게시판에 있는 페이지 네비게이션 부분을 봤습니다. 고정이라는 측면에선 포럼과 동일했습니다만, 게시물 갯수에 대한 정보는 없었습니다. 게시물 목록에서 해당 게시물의 번호를 가지고 있는데 그 번호가 그런 정보를 줄테니 굳이 필요 없었던 것 같습니다. 다만, 게시물 번호가 그다지 사실상 사용자에게 그리 의미가 있는지는 모르겠습니다. 게시물을 어떤 URL로 접근하는 것과, 게시물의 번호로 게시물에 접근하는 것의 차이는 좀 크다고 봅니다. 개인적으로는 URL로 게시물에 접근하는게 더 타당해 보이니, 번호는 사실 필요 없다는 생각입니다. 그리고 이 게시판은 한 페이지 목록 수가 작아서 네비게이션을 자주 해야 합니다. 대신에 네비게이션 바는 화면의 맨 하단에 하나만 위치하고 있습니다. 한 화면에 전부 눈에 들어오기 때문에 위의 두 경우처럼 위, 아래 두 곳에 네비게이션 바를 둘 필요는 없다고 생각했을지도 모르는데.. 뭐 타당해 보입니다. 여기서도 한 페이지당 목록 수를 설정하는 화면은 보이지 않습니다.

하아~~ 페이징 어떻게 구현할까나..
top


Maven 멀티 모듈 프로젝트 구성하기

Build/Maven : 2008.10.20 18:02


간단하네요~ 상위 프로젝트(부모-자신 관계가 아닌, 다단 구조에서 상위)의 pom.xml에서 해당 프로젝트의 packaging을 pom으로 하고, <modules> 엘리먼트를 사용해서 하위 프로젝트의 이름을 명시해주면 됩니다. 그럼 상위 프로젝트에서 빌드를 하면, 자신은 물론 자신이 <modules>에 명시한 모든 프로젝트를 동일하게 빌드해줍니다.

상위 pom.xml
...
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.opensprout</groupId>
    <artifactId>osaf</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-m1</version>
    <name>OpenSprout Application Framework</name>
    <url>http://www.opensprout.org</url>
...
    <modules>
        <module>osaf-core</module>
    </modules>
...

프로젝트 구조
사용자 삽입 이미지

바로 하위 폴더를 만들고 그 안에 파일들을 복사해두면 됩니다.


top


오랜만에 스프링 MVC 다시 정리

Spring/Chapter 13 : 2008.10.19 13:19


오늘 오후 네 시에 스터디가 있어서 오랜만에 13장을 다시 정리해봤습니다. 그 중 몇 개만 정리해둡니다.

MultiActionController 사용 방법은 두 가지
- 상속
- 위임

WebApplicationContext가 관리하는 빈
- 컨트롤러(controller)
- 핸들러 맵핑(handler mappings)
- 뷰 리졸버(view resolver)
- 로케일 리졸버(locale resolver)
- 테마 리졸버(theme resolver)
- 멀티파트 파일 리졸버(multipart file resolver)
- 예외 처리 리졸버(Handler exception resolver)

애노테이션 기반 컨트롤러 설정시 필요한 빈(자동 등록해줌)
- DefaultAnnotaionHandlerMapping
- AnnotationMethodHandlerAdapter

@RequestMapping 사용 방법
- 클래스 레벨
- 메소드 레벨(MAC와 비슷한 효과)
- 클래스 + 메소드 레벨 혼합(클레스 레벨에 Ant 패턴 사용해서 거르고, 메소드 레벨로 세부적으로.)

요청 처리 메소드 인자
- Servlet API(Session 사용시 Thread-safety 문제가 생기면, AnnotationMethodHandlerAdapter의 synchronizeOnSession 속성을 true로 설정.)
- WebRequest, NativeWebRequest
- Locale
- InputStream/Reader, OutputStream/Writer
- @RequestParam
- Map, Model, ModelMap
- Command/form objects
- Errors/BindingResult
- SessionStatus

요청 처리 메소드 반환 타입
- ModelAndView
- Model (뷰 이름은 CoC 사용)
- Map (위와 동일)
- View (모델은 커맨드 객체와 @ModelAttribute를 사용한 메소드가 반환하는 객체)
- String (위와 동일)
- void (응답을 response 객체를 사용해서 직접 처리하거나, CoC 사용)
- Other return type (해당 객체를 model attribute로 뷰에서 사용가능)

@RequestParam
- 요청 매개변수 바인딩

@ModelAttribute
- 메서드 매개변수 레벨: 모델 속성을 특정 메서드 매개변수로 맵핑할 때 사용.
- 메서드 레벨: 화면에서 사용할 implicite object를 제공할 때 사용.

@SessionAttributes
- @ModelAttribute의 이름 목록을 지니고 있다. 해당 모델 객체들을 세션에 저장하여 여러 요청에서 공통으로 사용.

@InitBinder
- 커스텀 프로퍼티 에디터 등록.
top


약간 어설픈.. OSAF 그리드 태그 완성

모하니?/Coding : 2008.10.17 15:39


조금 어설픈 그리드 태그를 완성하고 공개합니다. 사실상 그리드 태그의 핵심 요소는 DisplayTag이고, 회사에서 사용한 OSAF 태그는 상용제품을 사용하고 있어서 공개할 수가 없었습니다. 그리드 구조도 조금 바껴서, 엑셀 기능을 제대로 활용할 수 없었습니다. ㅠ.ㅠ.. 이제 더이상 시간을 끌다가는 언제 공개할 수 있을지 몰라서, 일단 마일스톤 버전 공개를 목표로, 현 상태를 정리하여 공개하겠습니다.

<o:gridpage popupheight="400" popupwidth="700" >
<c:set var="startNumValue" value="${startNum}" />

    <d:table name="list" class="maingrid" id="maingrid">
        <d:column title="No">
            <a href="javascript:sendToUpdate(<c:out value="${maingrid.id}" />)">
                ${startNumValue}
            </a>
<c:set var="startNumValue" value="${startNumValue + 1}" />
        </d:column>
        <d:column property="name" title="이름" />
        <d:column property="loginId" title="아이디" />
        <d:column property="sex" title="성별" decorator="org.opensprout.sample.model.enumeration.SexType" />
        <d:column property="location" title="사는곳" />
        <d:column property="birthday" title="생일" />
        <d:column property="hobbies" title="취미" />
    </d:table>
</o:gridpage>

이런식으로 그리드 태그를 작성하면, 화면에서 다음과 같은 그리드를 만들어 줍니다. 약간 어설플이라고 한 이유는 많은데, 차차 개선해 나가기로 하겠습니다.


왼쪽에 [1/6]은 [첫번째 row 넘버/전체 갯수] 입니다. 따라서 두 번째 페이지에서는 [6/6]으로 보이겠죠.
중간에 있는 [1|2] 에서 굵은 글씨체가 현제 페이지고 2를 누르면 두 번째 페이지로 이동합니다.
오른쪽에 있는 건, 한 페이지당 목록의 갯수 입니다.

파이어폭스/인터넷 익스플로러/구글 크롬 에서 확인해봤을 때 모두 동일하게 보였습니다.
top


12월 Whiteship's 미국 비행기 일정표

모하니?/Planning : 2008.10.17 13:17


일단 크게 컨퍼런스를 마친 다음 아틀란타에 가서 볼 일을 보고 돌아옵니다.



top


스프링 DM 프로젝트 빌드하기

Spring DM/exercise : 2008.10.16 14:59


메이븐 기반 프로젝트기 때문에, 간단하게 mvn package로 빌드 할 수 있을 거라고 생각했는데;;; 땡.. 틀렸습니다. mvn packae로 빌드하면 다음과 같은 에러를 볼 수 있습니다.

[ERROR]

Mojo:

    org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile

FAILED for project:

    org.springframework.osgi:spring-osgi-mock:bundle:1.2.0-m1

Reason:

C:\java\spring-osgi-1.2.0-m1\src\mock\src\main\java\org\springframework\osgi\mock\MockServiceReference.java:[23,25] package org.osgi.framework does not exist

OSGi 플랫폼이 없어서 발생하는 에러로, 스프링 DM 프로젝트를 빌드 할 때 프로파일을 선택해줘야 한다는 군요. 그래서 스프링 DM 프로젝트에 있는 프로파일을 살펴봤습니다.

사용자 삽입 이미지

여러가지가 있었습니다. 맨 위에 세 개는 OSGi 플랫폼이고, jdk 버전을 1.5 이상용도로 빌드해서 애노테이션 지원 기능을 사용할 수 있게 빌드 할 수 있나보네요. it은 통합 테스트(이 옵션을 안 주면 단위테스트만 합니다.)

mvn -Pequinox

통합 테스트까지 하면서 빌드 하려면

mvn -Pequinox,it

굳이 빌드를 안 해도 dist 폴더에 들어있긴 하지만... 오픈 소스쓰는 기본 자세라는 사부님 말씀. 캬~ 덕분에 오늘도 한 수 배웠습니다.

메이븐 프로파일은 안 써봤는데, 저걸 사용해서 빌드 하면 여러 환경에 따른 빌드 커스터마이징이 가능하군요. 특히 저 통합테스트가 눈에 띄는데 빌드 시간을 엄청 오래 잡아먹는 통합 테스트들은 주기 적으로만 실행하고 한 번의 커밋당 실행하는 빌드는 단위테스트만 실행하게 할 수도 있겠습니다.. 흠.. 프로파일 좋구나.


top


OSAF 메이븐 저장소 사용하기

Build/Maven : 2008.10.14 15:04


참조 : http://www.sonatype.com/book/reference/repository-manager.html#sect-conf-maven-nexus

기본으로 메이븐 저장소를 사용하게 되는데, 멀리 있어서 느린데다가, 라이브러리 업데이트도 느리기 때문에 최신 라이브러리를 참조할 수가 없습니다. 반면, 최신 라이브러리를 빠르게 참조할 수 있는 OSAF 메이븐 저장소를 간편하게 사용하실 수 있습니다.

일단 .m2 폴더(자기 계정 기본 폴더에 히든 폴더로 생깁니다.)에 settings.xml 파일을 열고(없으면 만들고.)

<settings>
...
 <mirrors>
...
  <mirror>
  <id>Nexus</id>
  <name>Nexus Public Mirror</name>
  <url>http://www.epril.com:8082/nexus/content/groups/public</url>
  <mirrorOf>central</mirrorOf>
  </mirror>
... 
</mirrors>
...
</settings>

이렇게 하나의 미러 사이트를 추가해주시면 됩니다. 기본 메이븐 저장소, 아파치, 코드하우스, 아틀라시안, 스프링, 스프링 DM 등 유명한 메이븐 저장소를 전부 프록시로 등록해뒀기 때문에, 저렇게 설정하시면 기본 설정에서 보다 더 많은 최신 라이브러리를 쉽고 빠르게 참조하여 사용할 수 있으실 겁니다.

간혹, 정말 간혹 다운받지 못하는 라이브러리가 있을 때는, 저한테 요청하시면 (원하는 그룹 iD와 원하는 artifactID, 버전으로..)라이브러리 설치 서비스도 해드립니다.ㅋㅋㅋ  라이브러리 설치야 워낙 간단하기 땜시~
top


스프링 AOP 선택, 활용, 이슈 - 발표자료 & 소스코드

Spring/KSUG : 2008.10.14 14:49


원래 계획대로면 스크린캐스팅도 제공해야 하는데, 제가 이번 주에는 여유가 없을 것 같아서 일단 발표자료만 올립니다. KSUG에서 별다른 연락이 없기 때문에 일단 제 블로그에 올리죠. 나중에 KSUG에서 일괄적으로 정리해서 발표 자료 묶음을 올리면 좋겠네요.


방명록에 보니 소스코드 요청도 있어서, 같이 올려둡니다. 메이븐 프로젝트인데, 몇몇 라이브러리는 메이븐 중앙 저장소에서 받지 못할 수도 있습니다. 그럴 때는, OSAF 메이븐 저장소를 원격 저장소로 등록해서 사용하시면 됩니다. OSAF 메이븐 저장소 주소는 다음과 같습니다.

http://www.opensprout.org:8082/nexus/content/groups/public/


top


태그 파일에서 객체 노출시키기

View/JSP : 2008.10.14 13:27


태그 파일에 어떤 객체를 전달하는게 아니라, 그 반대로 태그 파일에서 어떤 객체를 외부로 노출 시킬 수 있습니다. 태그 파일을 사용하고 있는 쪽에서 태그 파일이 가지고 있는 어떤 값에 접근하고자 할 때 유용하게 사용할 수 있겠습니다. jspContext를 사용하여 원하는 객체를 원하는 이름으로 저장해두면 됩니다.

상위 태그 파일

gridpage.tag

<%@ variable name-given="startNum" %>

<%
jspContext.setAttribute("startNum", count);
%>

JSP 파일

grid.jsp
<o:gridpage>
   ${startNum}
</o:gridpage>


top


DisplayTag에서 팝업 링크 달기

View/JSP : 2008.10.13 12:13


DisplayTag를 계속해서 사용해보니, 한계점들이 들어납니다. 역시 직접 사용해서 뭔가 만들어보기 전까진 모르는건가 봅니다. 암튼, 찾아보니 어떻게든 방법은 있길래 적용했습니다.

    <d:column title="No">
        <a href="javascript:sendToUpdate(<c:out value="${maingrid.id}" />)">
            <%out.print(count++);%>
        </a>
    </d:column>

거의뭐.. displaytag가 해주는 일은 없습니다. 그냥 자바스크립트를 이용해서 팝업창 띄우기라고 글 제목을 바꿔야할지도 몰겠습니다.

<script type="text/javascript">
function sendToUpdate(selectedId) {
    popup("update.do?id=" + selectedId + "&gridid=maingrid", "maingridpopup", 500, 600, "yes", "yes");
}

이제, 그리드 완성입니다. 태그 파일로 다듬고, 태그 파일 서술자 만들어서 url로 태그 파일 참조해서 사용할 수 있는지만 확인하면 OSAF 배포합니다. 이번 주를 목표로 합니다. 아자 아자 파이팅!!!!
top


제 9회 스프링 세미나 후기

모하니?/Thinking : 2008.10.12 20:37




시작이 안 좋군
(10:00 ~ 11:20)

아침 열시 몇 분. 전철역에서 나가려는데 찬욱이를 만났습니다. 최근에 안경을 바꿔서 그런지 하두 오랜만에 만나서 그런지 처음 몇 초 동안 '누구지..' 하다가 금방 알아보곤 반갑게 세미나장으로 발걸음을 옮기기 시작했습니다. 하지만.. 얼마후 찬욱이한테 전화가 오더군요. 영회형 전화인 것 같은데 전화받는 소리를 들어선 좋은 소식이 아니구나 싶었습니다. '뭐?? 영회형이 못 오신다고?' 당황스러웠지만, 지난 번에도 비슷한 경험이 있기 때문에 그때 보단 덜 당황스러웠습니다. 일단 준비해야 할 것은 점심. 그리고 일정. 일정 조정은 비교적 간단한데 문제는 점심. PM용어로 완벽한 Critical Path. 찬욱이는 전화국에 전화해서 주변 도시락집 알아보고, 대엽이는 맥가이버같이 세미나 시설 조작을 완벽하게 해냈습니다. 음향, 조명, 스크린까지 캬.. 전 도시락집이 전화 안받길래. 나가서 점심 때울만한거 찾아봤으나. 샌드위치 만한게 없더군요. 문제는 주문을 어떻게 하고 돈을 어떻게 걷고. 분명 발표 중에 사람들은 또 올텐데. 주문은 몇 시간 전에 해야 제때 다 만들 수 있을지... 등등 머릿속이 발표 말고 이런 자봉활동으로 가득 찹니다. 하나 하나 대엽, 찬욱과 셋이서 결정하고, 봄싹 스터디 회원분들(김재진, Arawn, HeloIs)의 도움을 받아 무사히 점심 준비를 제때 완벽하게 끝낼 수 있었습니다.

첫 번째 발표 시작(11:30 ~ 1:05)

일정 변경과 대표님 부재에 대해 죄송한 마음을 전달하고, 첫번째 발표가 시작 됐습니다. 찬욱군의 스프링 JDBC. 오랜만에 보는 라이브코딩. 그래 이게 스프링 세미나였지. 라는 옛 추억이 새록 새록 떠오르는 발표였습니다. 썡 JDBC 코드를 하나씩 점진적으로 리팩토링 해가면서 Pluggable Behavior 패턴을 적용해 나가는 모습이 멋졌습니다. 스프링 JDBC를 직접 구현해버리는 건가 의심스러울 정도 였습니다. 찬욱군 발표가 끝나갈 즈음, 대엽군과 주문해둔 샌드위치를 가지러 다녀오고, 저는 단상에 올라 안내 말씀을 드리고, 봄싹 스터디 회원분들은 그 사이 음료수를 준비해 왔습니다. 타이밍 맞추기 숨가쁜 순간이었는데, 얼추 잘 맞았습니다.

점심시간(1:10 ~ 2:00)

먹고나니 이제 한시름 놨다..라는 생각이 들더군요. 인원수를 정확히 예측하기 힘든 상황이어서, 2만원 가량 개인적인 손해를 보긴 했지만, 그 정도는 그냥 KSUG에 기부한걸로 생각하기로 하고, 남아있는 점심 시간에 탁구를 치며 놀았습니다. 자주 왔던 곳이라 탁구를 하고 놀 수 있다는 걸 알고 있었거든요.ㅋㅋ 오랜만에 보는 선배(성일이형)가 세미나에 오셨길래, 탁구 한 판. 룰도 잘 몰라서 대강 대강. 대강 대강 승리. ㅋㅋ. 점심 시간에 짧게나마 맥스님, 박재성님과 짱가님을 볼 수 있어서 좋았습니다.

두 번째 발표 시작(2:00 ~ 3:10)

두 번째 발표는 현준이형의 스프링 Security. 캬오. 어렵죠. Acegi 부터 공부를 했었는데 전혀 쉽지 않았습니다. 하긴 AOP도 쉽진 않았었지만, 개인적으론. Acegi가 더 어려웠던 것 같습니다. Acegi는 레퍼런스 보다는 Spring In Action같이 재밌고 쉽게 설명한 책을 통해 학습하는것이 좋겠다는 생각입니다. 기본 개념 설명 부터 DAO 기반 예제까지 볼 수 있었습니다. 개인적으론 공부한지 오래되서 그런지 클래스 이름들이며 역할들이 잘 기억나지 않았습니다. 복습의 소중함을 느낄 수 있었습니다. 두 번째 발표에는 참가해주신 분들의 참여가 눈부셨습니다. 특히 박재성님의 질문들은 식곤증으로 졸까 말까 고민하는 저를 깨워주셨습니다. 질문 내용은 "(URL 기준 권환 확인으로 부족한 경우로 인해) 메소드 단위의 권한 확인을 하는 경우는 없는가?" 였습니다. 이 부분은 제가 몇일 전에 본 TSE 2008 동영상에 등장하는 내용이라 그 내용을 조금 언급해었습니다.

마지막 발표 시작(3:20 ~ 4:50)

마지막 발표 전에 양수열님을 아주 잠깐 뵐 수 있었습니다. 발표하러 올라가야 할 순간이라..ㅎㅎ 마지막 발표는 제가 스프링 AOP를 발표했습니다. 원래 계획 대로였으면 4시 40분부터 발표를 시작했을텐데 일정이 바껴서, 1시간 20분이 앞당겨진 3시 20분부터 발표를 시작했습니다. 이제와서 생각이지만, 다행이라는 생각이 들더군요. 원래 시간에 발표를 했었으면, 듣는 분들이나 발표하는 저까지 지칠뻔 했습니다. 마지막 발표라고 생각하니 그 동안 아껴뒀던 모든 힘과 그 동안 준비해온 모든 걸 다 보여줘야겠다는 생각이 들어군요. 그런 결심을 하고 단상에 올라갔습니다. 발표 준비를 할 때는 한 주제 당 시간까지 계산해 뒀었는데, 이번 발표에선 그럴 필요가 없겠다는 생각에 정말 모든 걸 쏟아내듯이 발표했습니다. 슬슬 지쳐가는 모습이 보이는 청중들을 농담과 질문으로 깨워가면서 잼나게 진행했습니다. 역시.. 발표 할 때는 뭔가가 잘 안 됩니다. 잘 돌아가던 이클립스가 갑자기 터무니 없이 느려지고, PPT 슬라이드 쇼가 중간에 정지 되서 강제종료후에 다시 실행하는 사태가 있었습니다. 그런 상황에서도 아무런 동요없이 잘 봐주시는 청중분들의 자세가 고마웠습니다. 그래서 더 열심히 했습니다. 발표를 끝내고 마지막 인사를 하며 남는 아쉬움은 거의 없었습니다. 준비한 모든걸 보여드릴 수 있어서 만족스러웠습니다.

집으로

발표가 끝나고 뒷 정리를 하고 나와보니, 그새 많은 분들이 가셨습니다. 아쉬운건.. 오늘 오신 분들 중에 말을 걸고 싶은 분이 있었는데 말을 못 걸었습니다. -_-;; 너무 바빴습니다.. 는 핑계고 왠지 먼저 다가가 인사를 나누는걸 아직도 잘 못하는 제 자신 때문이었습니다. 그게 좀 아쉽다고 하면 아쉬웠습니다. 썬 테크 데이에 가서는 먼저 인사를 나누는 연습도 좀 해야겠습니다. 암튼.. 남아있던 분들과 저녁 식사를 간단하게 하고, 집에 가는 전철에 오르니까 피로가.. 한 방에 몰려오기 시작했습니다. 아침부터 이것 저것 신경쓰고 마지막에 시원하게 발표를 했더니 그 피로 때문에 눈이 자꾸 감겼습니다. 그래도 동네에 사는 분이 계셔서 잼나게 이 얘기 저 얘기 나누면서 올 수 있었습니다. 집으로 들어오기 전 맥주 두 캔와 과자 한 봉지를 샀는데 아직 뜯지도 않고 후기를 정리했습니다. 이제 다 썼으니까 기선이랑 한 잔 할까 합니다. 수고했다.

감사의 인사

이번 세미나에서 고마움을 느낀 분들에게 감사의 인사를 드립니다.

대엽님: 완전 맥가이버. 만능입니다. 세미나 환경 세팅, 점심 주문, 이것 저것 시작부터 끝까지 정말 감사합니다.
김재진님, Awarn님, HeloIs님: 감사합니다. 이 분들 안 계셨으면 음료수 73개를 어떻게 들고 오고, 오신 분들에게 나눠 드리고, 쓰레기 정리하고.. 캬오. 감사합니다.
참석하신 모든 분들: 스프링 사용자들은 다들 매너 남/녀 인가요? 뒷정리 할 것도 없더군요. 쓰레기도 잘 분리해서 버려주시고, 발표 중에 한 분도 조는 모습을 못 봤습니다. (행씌도 안 자던데? 오오..)
아. 오늘의 유일한 지원 썬.: 박안나님께서 카드 형 USB 2GB 두 개를 지원해주셨습니다. 감사합니다. :)

top


맥북에 Postgres 설치하기

Good Tools : 2008.10.11 23:46


먼저 PostgreSQL 코어를 설치합니다.
http://www.postgresql.org/download/macosx

공유 메모리가 부족하다는 메시지가 뜨던데, 재부팅해서 다시 설치를 시도하면 설치가 진행됩니다.

코더 설치를 마친 다음에는 pgAdmin을 설치했습니다.
http://www.pgadmin.org/download/macosx.php

사용자 삽입 이미지
오~ 좋네 좋아.
top


Spring 2.5 on the Way to 3.0 - 유겐 휄러



참조 : Spring 2.5 on the Way to 3.0

Spring One 2008에서 유겐 휄러의 발표 동영상을 보여줍니다. 별점은. 3.2/5 정도 됩니다. 지난 번에 봤던 Using Spring Security (별점 4/5)보다 평점이 조금 낮네요.

JDK 6 지원
- JDK 1.4, 1.5 호환(1.3은 안 함)
- JDBC 4.0 지원(native connections, LOB 핸들링)
- JMX MXBenas

AspectJ LTW 지원

Java EE 5 지원

JSR-250 애노테이션 지원
- @PostConstruct, @PreDestroy
- @Resource
- self describing.

Further Java EE 5 Annotations
- @WebServiceRef/@EJB
- @TransactionAttrubute
- @PersistenceContext/@PersistenceUnit

Autowiring Annotation
- specific autowiring by type
- @Qualifier

Autodetectable Component
- @Component

@Configurable with AspectJ
- <context:load-time-weaver aspectj-weaving="on" />
- <context:spring-configured />
- @Configurable

@Transactional with AspectJ
- <context:load-time-weaver aspectj-weaving="on" />
- <tx:annotation-driven mode="aspectj" />

Annotated MVC Controllers
- @Controller
- @RequestMapping
- @RequestParam
- @ModelAttribute

Test Context Framework
- @ContextConfiguration
- @TransactionConfiguration
- JUnit 4.4 지원.

Tradeoffs
- 재컴파일(XML 설정 변경은 재컴파일 필요 없다.)
- 설정의 외부화(애노테이션은 클래스를 보면 내용을 알 수 있다.)
- 설정 재정의 가능 여부(애노테이션 설정 바꾸면 컴파일 필요하다.)

Spring 2.5 정리
- Java 5와 Java EE 5 완전 지원
- ApsectJ와 보다 긴밀한 연동
- 애노테이션 설정 강화

The Roadmap for Spring 3.0
- 7월까지 2.5.6
- 8월에 3.0 M1
  - REST 지원
  - 다양한 EL 지원
- Spring 3.0 GA는 4분기 중으로..
=> 흠.. 이미 8월 지난지 오래 됐는데, M1 소식도 못들었네요. 내년 초를 기대해봐야겠네요.

Spring 3: Core Revisions
- Java 5+ 지원
  - 스프링 코어 API에 Generic 적용
- J2EE 1.4+ 호환(웹스피어 6.1, 웹로직 9.2, JBoss 4.2)
- 스프링 EL
- 새로운 커테이너 기능 제공(annotated factory methods)
=> 흠. 제레닉 코드가 코어 API에 들어가면.. 혹시 GenericDAO 같은 거도 스프링이 제공하는건가.. 캬오..

Spring 3 and the Web Space
- 개정된 자바 웹 표준 지원(포틀릿 2.0, 서블릿 3.0)
- REST 지원
- conversation 관리
- 애노테이션 기반 위자드 컨트롤러
=> 스프링 3이 conversation이랑 애노테이션 기반 위자드 마법사를 지원해주면.. 캬오 멋질듯.

Spring 2.5 Mission Continued

Pruning & Deprecation in 3.0
- 가지칠것
  - Commons Attuributes 지원
  - 예전 TopLink API 지원
- deprecation 계획
  - 예전 MVC 컨트롤러 클래스 계층 구조
  - 예전 JUnit 3.8 테스트 클래스 계층 구조
=> 애노테이션 기반 시설 중심으로 가면서 예전 시설은 deprecation.

Spring 3.0 Summary
- REST, EL
- RESTful URI 맵핑, 포틀릿 2.0
- Java 5+, Spring 2.5 환경에서 그대로 호환 가능.

아음.. 발표 시간이 64분인데, 55분동안 2.5 얘기만 하다가 3.0 얘기는 빠르게 지나가 버려서 아쉽습니다. 그래서 별 세개만 줬어요. ㅋㅋ 유겐 횽님 Spring One America에서는 스프링 3.0 얘기 좀 더 해주세요. ㅠ.ㅠ 소스도 배포해 주시구요. 3.0에서 저는 컨버세이션 관리와 위자드 마법사가 제일 궁금해요. 그 다음으로는 코어 API에 추가할 제네릭 클래스들 중에 GenericDao같은 것들도 제공할 것인지도 궁금하구요. 마지막으론 스프링 EL도 궁금한데.. 그건 JSF 확장 기능이겠죠? JSP에서도 사용 가능한건가? 어쨋든 S1A에서 뵙겠습니다. 바이바이
top


내사랑 내곁에

모하니?/Listening : 2008.10.09 09:23




힘겨운 날에 너마저 떠나면
비틀거릴 내가 안길 곳은 어디에

ps: 우와.. 이것도 내 노래야.

'모하니? > Listening' 카테고리의 다른 글

[우리 부부를 위한 랩] 네것인 내가  (2) 2009.10.14
내사랑 내곁에  (0) 2008.10.09
그리움만 쌓이네  (0) 2008.10.06
독백  (4) 2008.08.19
Radiohead - Exit Music  (0) 2008.07.17
If You Want Me  (0) 2007.11.29
살다보면 그런거지  (0) 2007.08.31
Podcast: Rod Johnson and Spring 2.1  (0) 2007.08.11
내 마음의 강물  (0) 2007.03.22
멋진 사람이군요.  (0) 2007.02.07
아침에는 신나는 노래로  (0) 2006.11.24
top


AspectJ와의 연동을 고려한다면, 포인트컷을 최소화 해야합니다.

AOP : 2008.10.08 16:37


무슨 이야기냐면, 최소 권한 원칙인가... 그런거랑 비슷한겁니다.

바로 예제를 보면서 살펴보죠.

@Aspect
public class HibernateExceptionToDataAccessException {

    @Pointcut("@within(org.springframework.stereotype.Repository)")
    public void accountHibernateExceptionInDao(){}

    @AfterThrowing(pointcut="accountHibernateExceptionInDao()", throwing="e")
    public void translateHibernateException(HibernateException e){
        throw SessionFactoryUtils.convertHibernateAccessException(e);
    }

}

이 애스팩트는 하이버네이트 예외를 스프링의 DataAccessException으로 변환해주는 애스팩트입니다. 이 녀석은 하이버네이트로 DAO 만드는 여러 방법 중에 가장 깔끔한 방법을 사용할 때 쓰면 좋고 안 써도 별로 상관없는(하이버네이트 버전이 올라가면서 하이버네이트 예외도 uncatched exception으로 바꼈으며 계층 구조도 세밀하게 나눠뒀기 때문입니다.) 그런 애스팩트입니다. 그래도, 버전 올리기 힘든 하이버 예전 버전을 사용하는 라이브러리를 사용해야 한다면 유용하겠죠.

어쨋든, 본론으로 돌아가서..

저렇게 만들어둔 애스팩트에 문제점이 보이나요? 저도 방금전까진 몰랐습니다. 일단, 저 애스팩트의 포인트컷은 이해가 가시죠? @Repository 애노테이션을 가지고 있는 클래스의 모든 조인포인트를 나타낸 겁니다.

문제는 바로 이 조인포인트.. 이게 핵심입니다. 스프링에서는 메소드 실행 조인포인트만 사용하기 때문에, 저 애스팩트를 스프링 AOP에서 사용할 땐, 원하던 메소드에만 적용이 될 겁니다. 하지만, 만약 저 애스팩트를 AspectJ와 연동해서 사용한다면? 어떤 일이 벌어질까요?

사용자 삽입 이미지

캬오.. 43개??? 말도 안돼. 내가 테스트 할려고 만든 DAO가 몇개나 된다고.. 그 안에 메소드도 거의 한 두 개밖엔 안만들었는데.. 왠 43개.... 바로 크로스 레퍼런스 뷰를 열고 확인해봤습니다.

사용자 삽입 이미지

캬오~~~~ 마이 미스테이크... 처방이 필요합니다. 처방은 간단하기 때문에 비밀! 캬캬캬.(이번주 KSUG 세미나에서 공개하도록 하죠.) 처방후에는..

사용자 삽입 이미지

이렇게 AspectJ에서도 메소드 실행 조인포인트에만 걸 수 있습니다. 음하하하..
top


스프링에서 하이버네이트와 JDBC 같이 사용할 때 트랜잭션 처리는?

Hibernate/etc : 2008.10.08 16:23


별로 할 일이 없습니다.

PlatformManager는 하이버네이트가 사용하는
org.springframework.orm.hibernate3.HibernateTransactionManager

이걸 그대로 사용하면 되고, JDBC 코딩을 할 때는 그냥 JdbcTemplate을 사용하면 알아서 트랜잭션이 적용됩니다.

그런데 만약에 JdbcTemplate을 사용하지 못하고, DataSource를 직접 사용해야 할 경우에는 다음과 같이 TransactionAwareDataSourceProxy를 사용하면 된다고 합니다.

<bean id="rawDataSource" class="whatover youuse"/>

<bean id="dataSource" class="TransactionAwareDataSourceProxy">
  <constructor-arg ref="rawDataSource" />
</bean>

DataSource를 직접 사용하는 코드가 엄청나게 많아서 손을 못댈 경우에는 저렇게 dataSource를 스프링이 관리하는 트랜잭션을 알고 있는 데이터소스로 바꾸면 된다고 하는데, 해보진 않았습니다. 제가 해보고 싶었던 건 하이버네이트가 flush()를 하지 않은 데이터에 대한 JDBC쿼리로 인한 예외 상황인데...

테스트를 잘못 짠건지.. 잘 안 되더군요.

@Repository
public class MemberDao {

    @Autowired
    SessionFactory sessionFactory;
   
    SimpleJdbcTemplate jdbcTemplate;
   
    @Autowired
    public MemberDao(DataSource dataSource) {
        jdbcTemplate = new SimpleJdbcTemplate(dataSource);
    }
   
    public void add(Member member){
        sessionFactory.getCurrentSession().save(member);
    }
   
    public int update(Member member){
        return jdbcTemplate.update(
                "UPDATE Member SET age = ? WHERE name = ?", member.getAge(), member.getName());
    }

}

이런 DAO를 만들었습니다. add()는 하이버네이트로하고 update()는 JdbcTemplate으로 했습니다.

@Service
@Transactional
public class MemberService {

    @Autowired
    MemberDao memberDao;
   
    public void foo(){
        Member member = new Member();
        member.setName("keesun");
        memberDao.add(member);
       
        member.setAge(20);
        memberDao.update(member);
    }
   
}


그리고 서비스 코드는 저렇게 트랜잭션 처리를 하고, keesun이라는 객체를 하나 만들어서 저장하고, 나이를 추가한다음에 JDBC로 update문을 날립니다.

제가 원했던 결과는..

에러가 나는 겁니다.

그러나..

Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into Member (age, name, id) values (?, ?, ?)
Hibernate: update Member set age=?, name=? where id=?

에러가 나질 않고, 너무도 자연스럽게 동작해버려서 당황했습니다. 특히 마지막 줄의 쿼리는 제가 JdbcTemplate으로 날린 쿼리랑은 완전 다른 하이버네이트가 만든 쿼리가 날아갔습니다. 이게 대체;;; 무슨 일인지..  흠..

결과적으로는 아~무 걱정없이 하이버네이트랑 JdbcTemplate을 같이 사용할 수 있다는 것이지만, 제가 원했던 상황이 발생하지 않아서 좀 우울합니다.
top


개발에 필요한 AOP 뭐가 있을까?

AOP : 2008.10.06 17:22


1. 간단한 메소드 성능 검사

 개발 도중 특히 DB에 다량의 데이터를 넣고 빼는 등의 배치 작업에 대해서 시간을 측정해보고 쿼리를 개선하는 작업은 매우 의미가 있을 겁니다. 그럴 때 귀찮게 매번 해당 메소드 처음과 끝에 System.currentTimeMillis();를 쓰거나, 스프링이 제공하는 StopWatch 코드를 집어넣고 빼긴 뭐 합니다. 그런식으로 테스트 해보고 싶은 메소드가 여러 개일 때도 귀찮겠죠?

2. 트랜잭션 처리

트랜잭션 처리는 거의 필수라는 생각이 듭니다. 저야.. 여태까지 스프링의 도움으로 아무런 어려움없이 간단하게 트랜잭션 기능을 사용하고 있지만, 이걸 스프링 없이 사용하려고 해보니 코딩 하기가 싫어졌습니다. 보기 싫은 try-catch 코드는 계속 늘어나고, 핵심 로직은 정글 속에 숨어버리니 말이죠.

3. 예외 변환

스프링에는 DataAccessException이라는 매우 잘 정의되어 있는 예외 계층 구조가 있습니다. 예전 하이버네이트 예외들은 몇 개 없었고 그나마도 Uncatched Exception이 아니였습니다. 이렇게 구조가 별로 안 좋은 예외들이 발생했을 때, 그걸 잡아서 잘 정의되어 있는 예외 계층 구조로 변환해서 다시 던지는 애스팩트는 제 3의 프레임워크를 사용할 때, 본인의 프레임워크나 애플리케이션에서 별도의 예외 계층 구조로 변환하고 싶을 때 유용합니다.

4. 아키텍처 검증

이것에 관련된 내용과 애스팩트는 어젯 밤에 공개했습니다. 유용하겠죠?

5. 기타

- 하이버네이트와 JDBC를 같이 사용할 때, DB 동기화 문제 해결.
- 멀티쓰레드 Safety 관려하여 롹을 가지고 수행해야 하는 메소드들에 일괄적으로 메소드 수행 전에 롹을 가지게 하고, 메소드 실행 후에롹을 반환하는 애스팩트
- 데드롹 등으로 인해, PessimisticLockingFailureException 이런 예외를 만났을 떄 재시도를 하는 애스팩트.
- 로깅, 인증, 권환, ...

찾아보니, 유용한 것들이 많이 있었습니다. 물론 이밖에도 상당히 여러 경우에 AOP를 활용할 수 있을 겁니다. 단지, 익숙하지 않다보니.. 잘 안 쓰게 되는데, 이번 주 주말 KSUG 세미나 마지막 발표를 통해서 어느 정도 AOP와 가까워지는 시간이 되길 바랍니다. 이번 세미나에서 위에 나열 한 것 중에 절반 정도를 살펴보겠습니다.
top


그리움만 쌓이네

모하니?/Listening : 2008.10.06 09:16


아아~ 이별이 그리 쉬운가
세월가버렸다고~ 이젠 나를 잊고서 멀리~ 멀리 떠나가는가~
아아아~ 나는 몰랐네, 그대 마음 변할 줄 난 정말 몰랐었네~
오오~ 난 너 하나만을 믿고 살았네. 그대 만을 믿었네..
오오~ 네가 보고파서 나는 어쩌나. 그리움만 쌓이네.



캬오.. 나랑 똑같네. 와... 내 노래다.

'모하니? > Listening' 카테고리의 다른 글

[우리 부부를 위한 랩] 네것인 내가  (2) 2009.10.14
내사랑 내곁에  (0) 2008.10.09
그리움만 쌓이네  (0) 2008.10.06
독백  (4) 2008.08.19
Radiohead - Exit Music  (0) 2008.07.17
If You Want Me  (0) 2007.11.29
살다보면 그런거지  (0) 2007.08.31
Podcast: Rod Johnson and Spring 2.1  (0) 2007.08.11
내 마음의 강물  (0) 2007.03.22
멋진 사람이군요.  (0) 2007.02.07
아침에는 신나는 노래로  (0) 2006.11.24
top


나이쓰~!!! 계층형 아키텍처 검증 용 Aspect

AOP : 2008.10.05 23:57


@Aspect
public class SystemArchitecture {

    @Pointcut("execution(* org.opensprout.dao..*(..))")
    public void executeDao(){}
    
    @Pointcut("call(* org.opensprout.service..*(..))")
    public void callToService(){}
    
    @Before("cflowbelow(executeDao()) && callToService()")
    public void checkDao(JoinPoint jp){
        System.out.println("daoToService architechure checking");
        throw new RuntimeException("Dao can't call Service's method.");
    }
    
}

뭐하는 녀석이냐면, DAO에서 Service에 있는 메소드 호출을 하지 못하게하는.. 그런 애스팩트입니다. 이걸 응용하면, 개발자 100여명에 제각각 코딩 실력일 경우 컨트롤러에서 DAO를 직접 호출한다던가, 서비스 계층에서 컨트롤러에 있는 메소드를 사용한다던가 하는 어처구니 없는 코드를 찾아낼 수 있습니다.

흐아~ 이 녀석 어떻게 구현할지 생각해내느라, PPT 만들다가 고민하고, 고민하다가 집에 맥주 사들고와서 코딩해보고 테스트까지 완료, call이랑 cflowbelow 포인트컷을 아직 스프링 @AOP에서 지원하지 않기 때문에, AspectJ를 써야 하는데, 클래식 스프링 AOP를 사용하면 구현 가능합니다.

CFlow뭐시기Pointcut 클래스를 사용하면 되고, call대신에 method execution 포인트컷을 사용해도 무관합니다. 그 구현은 자바 5 미만 버전을 사용하시는 분에게 맡기겠습니다. 전 자바 5 미만이 싫어효. ^^;

잠잘 시간 전에 해내서 정말 기쁩니다. 아~ 맘편히 잘 수 있겠다. 휴우~~~

ps: 헤어진 여친 나오는 꿈은 제발 그만, 나 정말 꿈에서 깨기 싫었다. 나타나지 말아줘. 마이 뉴 여친. 원더걸스 나와라~ 원더걸스 나와라~ 원더걸스 나와라~

top


Spring DM 1.1.2 릴리즈~

Spring DM/etc : 2008.10.04 18:58


http://www.springframework.org/node/776

이번엔 그닥 눈의 띄는 changelog가 없네요.
top


AOP를 설명하는 그림 두 장

AOP : 2008.10.04 18:24


역사, 이론, 개념... 등등도 중요하지만, 무엇보다.. '감'이 중요한거 아닐까요. 딱 보고 '감'이 잡힐 만하면 충분하다고 봅니다. 그 뒤에 정말 궁금해서 역사, 이론, 개념들을 살펴보면 되겠죠. 처음부터 장황하게 이러 저러해서 이러 저러한걸 말들었고 어쩌구 저쩌구...  제가 봤었던 AOP 관련 자료 중에 가장 AOP에 대한 '감'을 잡게 해준 그림은 아래와 같은 그림입니다.

사용자 삽입 이미지
애니매이션 기능을 사용해서, 각각의 (횡단)로직들이 여러 클래스에 분산되어 들어가는 모습을 보여주면 더 멋질 것 같습니다.

사용자 삽입 이미지
캬오.. AOP에 대한 '감'을 잡기엔 충분한 그림이 아닌가 생각해봅니다. 이 그림을 보고도 AOP가 OO를 대체하는 프로그래밍 패러다임으로 인식한다면... 그건 좀.. 흠...
top

TAG AOP

FUD는 Fear, Uncertainty, Doubt



참조: http://www.catb.org/jargon/html/F/FUD.html

Defined by Gene Amdahl after he left IBM to found his own company: “FUD is the fear, uncertainty, and doubt that IBM sales people instill in the minds of potential customers who might be considering [Amdahl] products.” The idea, of course, was to persuade them to go with safe IBM gear rather than with competitors' equipment. This implicit coercion was traditionally accomplished by promising that Good Things would happen to people who stuck with IBM, but Dark Shadows loomed over the future of competitors' equipment or software. See IBM. After 1990 the term FUD was associated increasingly frequently with Microsoft, and has become generalized to refer to any kind of disinformation used as a competitive weapon.

번역하다가 등장해서 난항을 겪다가 대엽님 도움으로 알게 됐습니다. 캬오~ 저런 단어가 있었군요. 흠.. 저런 단어는 한글로 옮기기가 좀;; 그냥 FUD라고 쓰고 주석을 달거나, 풀어서 써야겠습니다.
top

TAG FUD

AspectJ로 final method에도 위빙하기

Spring/Chapter 6 : 2008.10.02 10:03


소스 코드는 이전과 동일합니다. 그 상태에서 프록젝트에 AspectJ Nature를 추가해줍니다.

사용자 삽입 이미지

그런 다음에 프로젝트 클린을 하여 기존의 클래스파일을 비우고 다시 컴파일하게 합니다.(Alt + P, N, 엔터) 지금, 저는 아무런 .aj 파일도 만들지 않았습니다. 그냥 이전에 만들어둔 Spring @AOP를 사용하고 있을 뿐입니다.

@Aspect
public class FinalHelloAspect {

    @Before("execution(* org.opensprout.sandbox.proxy.withfinal.FinalHello.*(..))")
    public void withFinalAdvice() {
        System.out.println("before advice from FinalHelloAspect");
    }

}

이 녀석이죠. AspecJ 프로젝트가 됐기 때문에, 빌드타임에 저 애스팩트의 포인트컷에 해당하는 클래스 파일을 조작해서 "새로운 .class 파일"을 생성하게 될 겁니다.

사용자 삽입 이미지

컴파일이 끝난 후 AspectJ IDE가 발동한 모습이니다. 포인트컷 마다 저런 화살표가 표시됩니다. 보시면 final 메소드에도 화살표가 표시되어 있습니다. 해당 코드와 Cross References 뷰를 동기화 시켜두면, 해당 지점에 적용될 어드바이스까지 표시됩니다.

사용자 삽입 이미지

놀랍습니다. 안 그러세요? @Aspect 캬오~ 하긴, 그리 놀랍지도... @Aspect 애노테이션이 aspectj 프로젝트에 속한 애노테이션이니까,..

사용자 삽입 이미지

이 결과를 보면 더이상 프록시를 사용하고 있는것이 아니기 때문에, Advised 라는 인터페이스는 사용할 수 없다는 것을 볼 수 있습니다. ClassCastException이 보이시죠?

흠냐;;

회사 청소해야되서 이만.. 황급히 마무리 합니다.
top


CGLIB 프록시 제약 사항 테스트.

Spring/Chapter 6 : 2008.10.02 09:10


@Component
public class FinalHello implements Hello {

    public String hi() {
        return "hi";
    }
    
    public final void finalHi(){
        System.out.println("안녕");
    }
    
}

위 클래스에 모든 메소드 호출을 포인트컷으로 간단한 Adivce를 적용하여 CGLIB 프록시를 생성했을 때, 다음과 같은 결과를 확인할 수 있었습니다.

1. final 메소드를 호출할 경우 Advice를 적용하지 않음.
2. final이 아닌 메소드에는 Advice가 적용됨.

이런 간단한 테스트를 해보기 전에는 막연히 final 메소드가 있으면 CGLIB 프록시를 못 만드는 건가 싶었는데, 그게 아니었습니다.

한 가지 더 확인해봤습니다. 기본 생성자(인자가 없는 생성자)가 반드시 있어야 한다고 본것 같아서, 그걸 확인해봤습니다. 위의 경우에는 기본 생성자가 있는 상태로 테스트를 했으니까 기본 생성자 없이 테스트를 한 번 더 해보면 되겠죠.

@Component
public class FinalHello implements Hello {
   
    String hi;
   
    public FinalHello(String hi) {
        this.hi = hi;
    }

    public String hi() {
        return hi;
    }
   
    public final void finalHi(){
        System.out.println("안녕");
    }
   
}

그리고 위에서 실행했던 테스트 코드와 애스팩트를 그대로 사용해봤습니다.


java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:203)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:255)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:93)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:130)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'finalHello' defined in file [C:\workspace\osaf\target\test-classes\org\opensprout\sandbox\proxy\withfinal\FinalHello.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.opensprout.sandbox.proxy.withfinal.FinalHello]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:883)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:839)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:729)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:381)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:42)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:173)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:199)
    ... 16 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.opensprout.sandbox.proxy.withfinal.FinalHello]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:58)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:877)
    ... 33 more
Caused by: java.lang.NoSuchMethodException: org.opensprout.sandbox.proxy.withfinal.FinalHello.<init>()
    at java.lang.Class.getConstructor0(Class.java:2706)
    at java.lang.Class.getDeclaredConstructor(Class.java:1985)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:54)
    ... 34 more

맞네요. 두 번쨰 제약 사항까지 살펴봤습니다.

CGLIB 프록시가 JDK 프록시에 비해 성능도 좋고, concrete 클래스의 프록시도 만들어 주기 때문에, 좋긴 한데, 위의 두 개의 제약 사항(final 메소드에는 어드바이스 적용 불가(상속을 못하니까.), 기본 생성자 필요.)에 주의 하면서 사용해야겠습니다.

만약에 이런 경우엔 어떻게 해야 할까요.

1. 구현하는 인터페이스 없음 ==> JDK 프록시 사용불가
2. 기본 생성자 없음 ==> CGLIB 사용불가
3. 어드바이스 적용하고자 하는 메소드가 fianl ==> 역시 CGLIB 사용불가
4. 하지만 AOP 하고파. ==> Spring AOP로는 불가능.

결론(지금은 머리로만 생각한 겁니다. 검증은 조금 뒤에..)
==> Spring AOP + AspectJ 연동해서, 빌드 타임에 aspectj-waever를 사용하던가, 클래스 로딩 타임에 loadtime-weaving을 하면 될 것입니다. AspectJ를 사용한 위빙은 프록시를 만드는게 아니라, 바이트코드랑 .aj 파일을 조작해서 타겟 클래스에 대한 .class파일을 다시 생성하고 그 코드를 사용하는 것이기 때문에 런타임시에 부하도 없고 위와 같은 Spring AOP 제약에서 벗어날 수 있으리라 봅니다.
top