Whiteship's Note


[하이버네이트 툴] hbm2doc

Hibernate/etc : 2009.06.30 20:37


하이버네이트 설정 정보를 통해, Entity 정보와 Table 정보를 HTML 문서로 만들어 주는 도구를 제공합니다.
하이버네이트 툴 3.2.0 베타9 버전 설명에 보면, 다이어그램까지 만들어 주는데.. 좀.. 멋진 툴인 듯 합니다.



문제는 Description에 부가 설명을 넣을 길이 없다는 건데 @_@.. 이 부분에 대한 패치를 만든 사람이 있길래 고대로 따라서 구현해볼까 합니다. 부디 잘 되기를...;;
top


[JBoss Tools] 이클립스 업데이트 사이트

Hibernate/etc : 2009.06.30 11:31


정리가 완전 잘 되어 있네요. http://jboss.org/tools/download.html

정식 버전: JBoss Tools 3.0(for 이클립스 3.4) 
http://download.jboss.org/jbosstools/updates/stable/

개발자 버전: JBoss Tools 3.1(for 이클립스 3.5)
http://download.jboss.org/jbosstools/updates/development/

나이틀리 버전: JBoss Tools 3.1(for 이클립스 3.5)
http://download.jboss.org/jbosstools/updates/nightly/trunk/

이 플러긴에 포함되어 있는 툴 목록은 다음과 같습니다.

JBoss AS Tools
Birt Tools
Core Tools
ESB Tools
Project Examples
Hibernate Tools
jBPM Tools
JMX Tools
JST/JSF tools
Portal tools
Seam Tools
Smooks Tools
Visual Page Editor
JBoss Webservice Tools

와우. 많기도 하죠.
top


[하이버네이트] Session-Per-XXX

Hibernate/Chapter 11 : 2009.06.27 17:52


참조: JPWH 11장

Session-Per-Operation: 479p. 안티 패턴, 하나의 오펴레이션(메서드) 당 새로운 세션을 만들어 사용하는 것. 성능상 병목지점이 될 수 있다.

Session-Per-Request: 479p. pesistence context 범위를 데이터베이스 트랜재션 범위와 동일하게 유지하는 것. 즉 트랜잭션 당 새로운 세션을 만들어 사용하는 것으로 볼 수 있다. 짧은 conversation(하나의 request-response 쌍)을 처리할 때 적당하다.

Session-Per-Conversation: 489p. persistence context를 복잡한 conversation(여러 request-response 쌍) 스콥으로 확장(extending)하는 방법. conversation 마다 새로운 session을 만든다. detached 객체를 사용하여 conversation을 구현하는 방법의 대안책이 될 수 있다.

ps: 복잡하다. persist(), saveOrUpdate()가 괜히 있는게 아니였다. SPC의 경우 인터셉터를 이용할 수 있고, currentSession을 어딘가에 저장해 두었다가 다음 request 처리시에 재사용해야 한다. 또한, detached 객체를 사용한 Conversation 구현과, SPC를 사용한 Conversation 구현은 맘대로 정하는게 아니다. 경우에 따라, SPC를 사용해야만 하는 경우가 있다. 자세한건.. 내일??

'Hibernate > Chapter 11' 카테고리의 다른 글

[하이버네이트] Session-Per-XXX  (0) 2009.06.27
top


[하이버네이트] detached 객체의 동일성

Hibernate/Chapter 9 : 2009.06.25 13:54


참조: JPWH 9.2.2 ~ 9.2.3

두 가지 동일성이 있다. 자바 객체 동일성과 DB 동일성이 있다. 자바 동일성은 == 으로 비교를 하고, DB 동일성은 주키 값을 비교한다. 자바 동일성과 DB 동일성이 모두 같을 조건을 Scope of object identity 라고 한다.

그 중에 세 가지 조건은 다음과 같다.
- No identity scope
- Persistence context-scoped identity
- Process-scoped identity

이 중에 하이버네이트는 Persistence context-scoped identity를 구현했다.

Session session1 = sessionFactory.openSession();
Transaction tx1 = session1.beginTransaction();

// "1234" 식별자로 Item 가져오기
Object a = session1.get(Item.class, new Long(1234) );
Object b = session1.get(Item.class, new Long(1234) );

( a==b ) // True, a와 b는 동일하다.
tx1.commit();
session1.close();

// a와 b 레퍼런스는 detached 상태 객체가 된다.

Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();

Object c = session2.get(Item.class, new Long(1234) );
( a==c ) // False, detached 객체 a와 persistent 객체 c는 동일하지 않다.(session context가 다르기 때문에..)

tx2.commit();
session2.close();

따라서 이렇게 된다. 하지만 여전히 DB id는 가지고 있기 때문에, a, b, c를 id 값으로 비교하면 모두 같은 객체로 인식할 수 있다. equlas()로 비교할 땐 equals()를 어떻게 구현하느냐에 따라 달리질 것이다. equals()를 별도로 구현하지 않으면 Object의 equals()를 사용할테니 == 비교와 다를 바가 없다.

equals() 비교가 중요해지는 시기는 Set 컬렉션을 사용할 때다. 아시다시피 Set은 컬렉션에 요소를 추가하기 전에 기본에 추가되어 있는 것들과 equals() 비교를 해본 다음에 false인 것만 추가한다.

그렇다면, 위의 코드가 모두 깥는 뒤 (detached) 객체 3개(a, b, c)를 Set에 추가하면 어떻게 될까? 과연 Set에는 몇 개의 객체가 들어갈까?

2개다. 일단, equals()를 별도로 구현하지 않아서, == 비교를 할 텐데 이미 위에서 == 비교를 해봤더니 a와 b는 같은 객체를 참조하고 있고, c는 또 다른 객체기 때문이다.

원하던 결과는 몇 개 일까? 한 개일 것이다. 셋 다 같은 DB 레코드를 가리키기 때문에, Set에는 한 개만 들어가는게 맞을 것이다. 그렇게 하려면 어떻게 해야 할까?

DB id를 사용해서 동일성을 비교하는 equals()를 재정의하면 될 것이다. equals()를 재정의하면 hashCode()도 반드시 재정의해서 같은 객체는 같은 해쉬코드를 반환하도록 해야겠다.

결론적으로, detached 객체를 다룰 때는 동일성에 주의하고, 동일성 문제가 발생할 시 equals()와 hashCode()를 적절하게 재정의 할 것.




top


이클립스 3.5 갈릴레오 설치 인증샷

Good Tools : 2009.06.24 22:58


http://www.eclipse.org/downloads/

이클립스 JEE Mac OS X carbon 버전을 설치했습니다.

http://blog.springsource.com/2009/06/24/installing-sts-into-eclipse-35/

좀 전에 올라온 스프링 소스 블로그 글을 보고 STS 설치를 시도했지만, AJDT 플러그인 사이트에는 연결이 안되고, 스프링 IDE 사이트에서 접근 가능한 플러긴은 AJDT 관련 플러긴 밖에 없길래 포기했습니다. @_@ 뭐.. 조만간 STS 2.1.0 RC 버전이 나온다고 하니.. 아직은 3.5 기반 STS를 좀 더 오래 기다려야 할 것 같습니다.


top


[하이버네이트 퀴즈] Flush

Hibernate/etc : 2009.06.24 14:08


    @Transactional
    @Test
    public void crud() throws Exception {
        Emp emp = new Emp();
        emp.setName("ks");
        ed.save(emp);

        assertThat(ed.getAll().size(), is(1));
        assertThat(ed.get(emp.getId()).getName(), is("ks"));

        emp.setName("tb");
        ed.update(emp);
        assertThat(ed.get(emp.getId()).getName(), is("tb"));

        ed.delete(emp);
        assertThat(ed.getAll().size(), is(0));
    }

이런 테스트가 있는데, 콘솔 창에 쿼리를 봤더니.

Hibernate: insert into Emp (id, dept_id, name) values (null, ?, ?)
Hibernate: call identity()
Hibernate: select emp0_.id as id4_, emp0_.dept_id as dept3_4_, emp0_.name as name4_ from Emp emp0_
Hibernate: delete from Emp where id=?
Hibernate: select emp0_.id as id4_, emp0_.dept_id as dept3_4_, emp0_.name as name4_ from Emp emp0_

update문이 빠져있다. 여기서 발생하는 의문점이 한 두가지가 아니다.

1. DB에 update가 되지도 않았는데 테스트는 어떻게 통과한 것일까?

2. 왜 update 문은 날아가지 않은 것일까?

3. 역으로, 왜 insert와 delete는 날아간 것일까?

이 세 가지 의문을 해결하려면 위에서 작성한 코드를 좀 더 자세히 살펴볼 필요가 있다. 바로 ed.save(), ed.get(), ed.getAll(), ed.update(), ed.delete()  들이다. 이 녀석들이 어떻게 구현되어 있는지 보지 않고서는 알 수 없다. 또하나 Flush 모드 역시 알아야 한다.

- Flush 모드는 기본 모드인 AUTO를 사용했다.
- save(), get(), update(), delete()는 하이버네이트의 Session API와 동일하다고 생각하면 되며, getAll()은 다음과 비슷하게 구현되어 있다. session.createQuery("from Emp"); 실제로는 이 모든게 GenericDao 구현체에 들어있어서 약간 다르긴 하지만, 본질은 그렇다.
- 테스트는 @Transactional한 녀석으로 기본으로 rollback될 녀석이다.

자.. 이제 위 세가지 질문에 대답할 수 있을 것이다. 그랬다면, 다음 퀴즈도 덤으로 풀어보자.
update 쿼리를 볼 수 있는 방법은 현재 두 가지 정도가 떠오른다.

4. 위 테스트 코드에서 한 줄을 삭제하여 update 쿼리가 콘솔에 찍히게 해보자.

5. 위 코드에 ed.flush()를 어디에 추가하면 update문을 볼 수 있을까?

정답은 비공개.. 영원히..
top


[DDD] User-Familly 구현

JEDI/DDD : 2009.06.22 18:53


User-Familly 관계부터 생각해보면, User가 일방적으로 여러 Familly를 가지고 있다고 볼 수 있습니다. 다중성(?)은 User에서 Familly쪽으로 1:대 관계입니다. (형제 자매가 같이 입사한 경우 다:다가 될 수도 있지만, 드문 경우니깐 중복 데이터 상관하지 않고 일단은 1대다로 ㄱㄱ, ) 생명주기를 생각해보면, Familly가 User에 종속되어 있습니다. 타입을 생각해보면 User는 엔티티가 거의 확실하고, Familly는 Value 타입에 가까운 듯 합니다. 하지만 편의상 id를 주고 관리하렵니다.

자 그럼 이제 필요한 도메인 클래스를 추가하고 속성들을 만든 다음 관계를 명시합니다.

class User {
...
    @OneToMany(cascade = { CascadeType.ALL })
    Set<Familly> famillies;
...

}

여기서 준 @OneToMany로 다중성이 어떤지 나타나고, Familly의 생명주기가 User에 종속적이라는 것이 드러납니다.

다음은 UserController(UC)로 갑니다. FamillyController를 만들 필요 없이, Familly와 연관되는 부분과 관련하여 그 요청을 UC가 처리하게 합니다. 화면상으로는 User의 수정화면에서 Familly 그리드가 보이고, 거기에 추가하는 버튼과, 수정하는 버튼이 달려있습니다.


새로 개선한 OSAF를 사용하여 만든 컨트롤러에 Familly를 그리드, 추가, 수정을 다룰 핸들러 메서드를 추가했습니다.

@Controller
@RequestMapping("/base/user/*.do")
public class UserController extends GenericController<User, UserService, UserRef, UserValidator, UserParams>{

    public UserController() {
        super(User.class);
    }

    @Autowired FamillyValidator famillyValidator;

    @RequestMapping
    public void famillygrid(@RequestParam int userid, ModelMap model, OrderPage orderPage){
        if (orderPage.getOrder() == null) orderPage.setOrder("name asc");
        model.addAttribute("list", service.findFamilliesBy(userid));
        model.addAttribute("orderPage", orderPage);
    }

    @RequestMapping(value="/base/user/famillyadd.do", method=RequestMethod.GET)
    public void famillyadd(ModelMap model){
        model.addAttribute("model", new Familly());
    }

    @RequestMapping(method=RequestMethod.POST)
    public String famillyadd(int userid, @ModelAttribute("model") Familly familly, BindingResult result,
            SessionStatus status){
        famillyValidator.validate(familly, result);
        if (result.hasErrors())
            return "/base/user/famillyadd";
        else {
            service.addFamilly(userid, familly);
            status.setComplete();
            return CommonPages.CLOSE_GRID_REFRESH;
        }
    }

    @RequestMapping(value="/base/user/famillyupdate.do", method=RequestMethod.GET)
    public void famillyupdate(int famillyid, ModelMap model){
        model.addAttribute("model", service.findFamillyById(famillyid));
    }


}

thin facade 역할을 하고 있는 service를 이용하고 있습니다.

@Service
@Transactional
public interface UserService extends GenericService<User, UserParams>{

    Set<Familly> findFamilliesBy(int userid);

    void addFamilly(int userid, Familly familly);

    Familly findFamillyById(int famillyid);

}

인터페이스에 추가해주고,,, 실제로 하는 일은 대부분이 도메인 또는 dao로 위임하는 것입니다.

@Service
@Transactional
public class UserServiceImpl extends
        GenericServiceImpl<User, UserParams, UserDao> implements
        UserService {

    public Set<Familly> findFamilliesBy(int userid) {
        return get(userid).getFamillies();
    }

    public void addFamilly(int userid, Familly familly) {
        get(userid).addFamully(familly);
    }

    public Familly findFamillyById(int famillyid) {
        return dao.fingFamillyById(famillyid);
    }

}

그럼 도메인에서 할 일은 도메인에서 처리를 하고..

    public Set<Familly> getFamillies() {
        if (famillies == null)
            famillies = new HashSet<Familly>();
        return famillies;
    }

    public void addFamully(Familly familly) {
        getFamillies().add(familly);
    }

DAO에서 할일은 DAO에서 처리합니다,

    public Familly fingFamillyById(int famillyid) {
        return (Familly) getSession().createQuery(
                "from Familly where id = " + famillyid).uniqueResult();
    }

어디서 처리하든 tx facade 역할을 하고 있는 service를 거쳐왔기 때문에, 트랜잭션 처리가 됩니다.

ps: 스프링 @MVC 혹은 OSAF의 버그(?) 발견..

    @RequestMapping(value="/base/user/famillyadd.do", method=RequestMethod.GET)
    public void famillyadd(ModelMap model){
        model.addAttribute("model", new Familly());
    }

여기서 @RequestMapping의 value를 주지 않아도(CoC로 인해 같은 결과가 나와야 함) 똑같이 동작해야 하는데, 생략할 경우, seach.do를 실행할 때(위 메서드와는 전혀 관계 없는 OSAF GenericController의 update() 메서드 시그너쳐 int id를 Integer로 바꾸라는 에러가 발생함.. @_@)


'JEDI > DDD' 카테고리의 다른 글

[DDD] User-Familly 구현  (0) 2009.06.22
[DDD] Whiteship's DDD 아키텍처 수정  (6) 2009.06.16
[DDD] Whiteship's DDD 아키텍처  (10) 2009.06.12
[DDD] DDD 입문에 좋은 글  (4) 2009.06.11
파트 3: Refactoring Toward Deeper Insight  (0) 2008.10.24
Factories  (0) 2007.12.25
Aggregates  (0) 2007.12.18
Modules  (2) 2007.12.14
Services  (2) 2007.12.13
DAO vs Repository  (4) 2007.12.04
DTO(Data Transfer Object)  (2) 2007.11.21
top

TAG 1대다, DDD, OSAF

스프링소스의 끼워주기(?) 패키지

모하니?/Thinking : 2009.06.17 11:13


STS 2.1 M2 버전을 다운 받았더니, tc 서버랑, dm 서버.. 게다가 roo 최신 버전까지 끼워 주는군요.. 캬~~


끼워팔기가 아니라 끼워주기를 하다니.. ㅎㅎㅎ 땡잡았습니다.
top


[DDD] Whiteship's DDD 아키텍처 수정

JEDI/DDD : 2009.06.16 18:17



Repository 역할이 DAO로 단순 위임 밖에 없어서, Repository를 없애고 그 자리에 DAO를 뒀습니다. Entity에서 이 객체를 사용하여 필요한 작업들을 할 겁니다. 이전의 Facade 역시 이름을 Service로 바꾸고, Transaction을 책임지게 했습니다. 이 서비스는 별다른 역할 없이 Domain 객체나 Dao로 역할을 위임하는 Thin Service입니다.

구조는 기존의 계층형 구조(Controller -> Service -> Dao)와 거의 비슷해 보이지만, 일단 Entity에서 Dao를 들고 예전에 서비스 계층에서 처리했던 비즈니스 로직을 담당하게 되었다는 것, 그 결과 Service의 일은 도메인 객체로의 단순 위임으로 바뀌었다는 점이 주요 특징으로 볼 수 있겠습니다. 또한, 모든 domain 클래스에 대한 C, S, D를 만들던 것과 달리 Entry Point에 해당하는 Entity에 대해 C, S, D를 제공하는 구조입니다. 즉 그림에서 Address라는 Value 타입과 OrderLine이라는 레퍼런스 타입에 대한 C, S, D는 볼 수 없죠.

'JEDI > DDD' 카테고리의 다른 글

[DDD] User-Familly 구현  (0) 2009.06.22
[DDD] Whiteship's DDD 아키텍처 수정  (6) 2009.06.16
[DDD] Whiteship's DDD 아키텍처  (10) 2009.06.12
[DDD] DDD 입문에 좋은 글  (4) 2009.06.11
파트 3: Refactoring Toward Deeper Insight  (0) 2008.10.24
Factories  (0) 2007.12.25
Aggregates  (0) 2007.12.18
Modules  (2) 2007.12.14
Services  (2) 2007.12.13
DAO vs Repository  (4) 2007.12.04
DTO(Data Transfer Object)  (2) 2007.11.21
top


[MAC]OmniGraffle

Good Tools : 2009.06.14 14:52


OmniGroup이라는 회사 제품 중 하나인데, 맥에서 UML 그릴 때 뭐가 좋을지 찾다가 발견했습니다. 여태 PPT로 그렸었는데, 이젠 좀 더 편하게 그릴 수 있겠네요.

http://www.omnigroup.com/applications/omnigraffle/



top

TAG UML2.0,

[AspectJ] LTW시 메이븐에 VM 옵션주기

AOP : 2009.06.12 19:59


스프링 @Configurable을 이용하려면 정적인 컴파일 시점 위빙으로는 안 됩니다. 객체가 필요해서 해당 클래스를 처음 클래스로더가 읽어갈 때.. 그 때 aop.xml 정보를 참조해서 위빙을 해야 합니다. 그게 바로 LTW죠.

-javaagent:/path/to/spring-agent.jar

그러려면 이런 옵션을 VM 인자로 넘겨줘야 합니다. 이클립스에서 JUnit 테스트를 실행할 때도 예외는 아닙니다.


이런식으로 모든 테스트에 인자를 줘야 하는데.. 여간 귀찮은 작업이 아닙니다. 프로젝트 별로 VM 인자를 설정하는 방법이 없나. 모르겠습니다.

메이븐으로 빌드를 하면 당연히 실패하겠죠. 그래서 메이븐에도 설정해줘야합니다. test 할 때 위빙 옵션을 다음과 같이 줄 수 있습니다.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.2</version>
                <configuration>
                    <argLine>-javaagent:weaving/spring-agent.jar</argLine>
                </configuration>
            </plugin>

이클립스 + 콘솔창 한개는 기본으로 들고 개발을 해야겠습니다. 안그래도 git와 mvn war:inplace 때문에 거의 항상 콘솔창 하나를 띄워서 같이 작업하는데, mvn clean test도 애용하게 생겼네요.
top


[AspectJ] 톰캣 6.X에 LTW(load time weaving) 설정하기

AOP : 2009.06.12 19:47


1. 톰캣 홈/lib 폴더에 spring-tomcat-weaver.jar를 복사해서 넣어둡니다.


2.5.6 버전 배포할 때 제공된 녀석인데, 3.0 M3에서 확인해본 결과 잘 동작하더군요.

2. server.xml에서 context 정보를 수정해줍니다.

<Context docBase="C:\workspace\koma-ddd\webapp" path="/" reloadable="false">
          <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Context 내부에서 위와 같이 loader 엘리먼트를 추가해주면 됩니다.

3. applicationContext.xml에 다음 엘리먼트를 추가합니다.

    <context:load-time-weaver />

끝입니다. @_@ 참.. 쉽죠~


top


[DDD] Whiteship's DDD 아키텍처

JEDI/DDD : 2009.06.12 11:23


기대는 하지 마시구요. @_@ 기존 틀(빈약한 도메인 객체 + 두꺼운 서비스 계층)에서 벗어난다는게 이렇게 머리 아픈 일인지 몰랐습니다. DDD 공부라도 제대로 했었어야 하는데.. 역시 개념없는 코드와 코드 없는 개념은 아무것도 아닌 것 같아요. 힘 없는 정의와 정의없는 힘처럼... 각설하고.. 오늘 구현해볼 아키텍처를 그려봤습니다.



일단 Web 계층에서는 Application 계층에 있는 Thin Facade 내지 서비스를 호출할 겁니다.

Thin Facade는 도메인 객체로 대부분의 역할을 위임할 겁니다.

도메인 계층에서는 Thin Facade로 부터 어떤 일을 위임 받은 도메인 객체는 비즈니스 로직을 담당할 것이고
- 컬렉션에 저장, 조회 등의 작업을 할 때는 계층에 위치한 Repository를 이용할 겁니다. 여기서 Repository는 DAO와는 개념이 다르다(참조: http://aeternum.egloos.com/1160846)는 것에 주의해 주세요.
- Repository는 모든 Entity가 아닌, Entry Point에 해당하는 Entity에 대한 것만 만들 겁니다.
- Email, JMS 등의 기능이 필요하다면 Infrastructure에서 제공하는 서비스를 이용할 겁니다.

시나리오 1: User


UserController가 요청을 받으면 UserFacade가 이 요청을 받아서 User의 특정 메서드를 호출하고, User는 UserRepository를 사용하여 원하는 작업을 한다.

시나리오 2: User->Address


위 상황에서 Value 타입인 Address가 추가 됐을 때 모습으로 별반 달라진 건 없음. Value 타입이 Entity 타입과 어떤 차이가 있는지 보여주는 시나리오.

시나리오 3: User->Address, Order


Order라는 새로운 Entity이자 Entry Type이 추가되자 Controller, Facde, Repository, Dao가 추가된다. User와 Order는 양방향 관계로 서로가 서로를 참조한다. 비즈니스 요구사항에 따라 방향성은 달라질 수 있다. 또한 현재는 각자가 자신의 Repository만 참조하고 있는데, 이 모습도 비즈니스 요구사항에 따라 달라질 수 있다. 중요한 건, 비즈니스 요구사항에 따라 도메인 객체만 변경하면 될 뿐, 나머진 그대로라는 것이다.

주의 할 것은 전체 아키텍처에서도 그렸듯이 도메인 계층에서 Application 계층을 참조하지 않는다는 것이다. Application 계층을 참조할 일이 있다는 것은 도메인 계층이 다시 Application 계층으로 일을 넘긴다는 것인데, 그렇게 되면 Thin Facade라는 가정이 어긋나게 되고 비즈니스 로직이 Application 계층쪽으로 새어나가게 될 것이다.

시나리오 4: User->Address, Order->OrderLine


이번에는 새로운 Entity OrderLine을 추가했다. 하지만 이 Entity는 Entry Point가 아니라 Order에 종속된 생명주기를 지나고 있다. 따라서 Order가 OrderLine까지 같이 관리하며 OrdeLineRepository는 만들지 않는다. 대신 Infrastructure에 OrderLineDao를 추가하여 OderRepository가 OrderLine을 DB에 넣을 때 이용할 수 있게 도와준다.

이제 구현해보는 일만 남았군요. 캬~~ 코딩하기 전에 그림 그려보는 것도 좀 도움이 되네요. 코딩하면서 계속 디자인을 바꾸니깐.. 테스트고 뭐고.. @_@ 일단 저렇게 큰그림 잡고 조금씩 테스트 해 가면서 기왕이면 TDD로 ㄱㄱ 해보렵니다! 파이팅!! 오늘은 이거 못 만들면 퇴근 못하니깐...ㄷㄷㄷㄷㄷ... 내일 스터디는 밤새고 가야할지도...??? 헤헷 후딱 하고 퇴근해버려야지~

'JEDI > DDD' 카테고리의 다른 글

[DDD] User-Familly 구현  (0) 2009.06.22
[DDD] Whiteship's DDD 아키텍처 수정  (6) 2009.06.16
[DDD] Whiteship's DDD 아키텍처  (10) 2009.06.12
[DDD] DDD 입문에 좋은 글  (4) 2009.06.11
파트 3: Refactoring Toward Deeper Insight  (0) 2008.10.24
Factories  (0) 2007.12.25
Aggregates  (0) 2007.12.18
Modules  (2) 2007.12.14
Services  (2) 2007.12.13
DAO vs Repository  (4) 2007.12.04
DTO(Data Transfer Object)  (2) 2007.11.21
top


[DDD] DDD 입문에 좋은 글

JEDI/DDD : 2009.06.11 12:45


여기에 왕창있더군요. 사부님이 읽고 공부하라고 적극 추천해주는 바람에 출근해서 지금까지 모두 정독해버렸습니다. 개념 설명도 충분하고 중간 중간 코드도 같이 주셔서 이해하기가 더 좋았습니다. TDD까지 권하시는 센스.. 캬~~ 읽으면서 많이 감탄했네요.

이터니티님 감사합니다. 잘 읽었습니다~ 계속해서 연재해 주세요~

2009/03/25   Domain-Driven Design의 적용-4.ORM과 투명한 영속성 4부 [1]
2009/02/27   Domain-Driven Design의 적용-4.ORM과 투명한 영속성 3부
2009/02/23   Domain-Driven Design의 적용-4.ORM과 투명한 영속성 2부
2009/02/15   Domain-Driven Design의 적용-4.ORM과 투명한 영속성 1부
2009/01/18   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 7부 [2]
2009/01/02   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 6부
2008/12/24   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 5부 [2]
2008/12/17   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 4부
2008/12/13   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 3부
2008/12/09   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 2부
2008/12/05   Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 1부
2008/11/30   Domain-Driven Design의 적용-2.AGGREGATE와 REPOSITORY 5부
2008/11/27   Domain-Driven Design의 적용-2.AGGREGATE와 REPOSITORY 4부
2008/11/25   Domain-Driven Design의 적용-2.AGGREGATE와 REPOSITORY 3부
2008/11/23   Domain-Driven Design의 적용-2.AGGREGATE와 REPOSITORY 2부
2008/11/20   Domain-Driven Design의 적용-2.AGGREGATE와 REPOSITORY 1부
2008/11/17   Domain-Driven Design의 적용-1.VALUE OBJECT와 REFERENCE OBJECT 4부
2008/11/17   Domain-Driven Design의 적용-1.VALUE OBJECT와 REFERENCE OBJECT 3부
2008/11/16   Domain-Driven Design의 적용-1.VALUE OBJECT와 REFERENCE OBJECT 2부
2008/11/15   Domain-Driven Design의 적용-1.VALUE OBJECT와 REFERENCE OBJECT 1부 [2]


'JEDI > DDD' 카테고리의 다른 글

[DDD] User-Familly 구현  (0) 2009.06.22
[DDD] Whiteship's DDD 아키텍처 수정  (6) 2009.06.16
[DDD] Whiteship's DDD 아키텍처  (10) 2009.06.12
[DDD] DDD 입문에 좋은 글  (4) 2009.06.11
파트 3: Refactoring Toward Deeper Insight  (0) 2008.10.24
Factories  (0) 2007.12.25
Aggregates  (0) 2007.12.18
Modules  (2) 2007.12.14
Services  (2) 2007.12.13
DAO vs Repository  (4) 2007.12.04
DTO(Data Transfer Object)  (2) 2007.11.21
top

TAG DDD

AspectJ를 이용한 코드젠과 프레임워크 2

모하니?/Coding : 2009.06.10 19:57


이전 글 AspectJ를 이용한 코드젠과 프레임워크에 이어지는 글로 현재는 Service과 Controller까지도 적용을 마치고 실행 후 잘 돌아가는 모습을 살펴보습니다. 지금은 웹 쪽을 REST 스타일로 고칠겸, 새 프로젝트로 깔끔하게 정리하고, 새로 디자인한 것까지 모두 합치고 있습니다. (역시 적당한 단계별로 해야지 한꺼번에 하려니깐 정신없고 오래 걸리는 듯합니다.)

1. AJ 내에서 코드 젠을 이용하여 상속을 없앨 수 있다.

AJ 코드를 어떻게 작성하느냐에 따라 다르겠지만, ROO가 제공하는 AJ를 스타일대로 작성하면, 상속을 이용하지 않고도 추가 기능을 넣을 수 있다. 장담점이 있다고 생각된다. 상속을 없앤건 좋은 일이라고 생각된다. 프레임워크 코드가 아닌, 비즈니스 도메인에 의한 상속을 사용할 수 있을테니말이다. 하지만, 상속을 포기하고 일일히 다 주입하는 방법을 택하다보니, 비슷한 코드가 여러 AJ에 널려있게 된다. 이 때는 또 한 가지 변수 AJ 파일의 길이를 고려해야 할 것 같다. AJ가 일일히 작성하기에 너무 길다고 생각 된다면, AJ 상속 .. 보다는 AJ에도 Generic을 도입할 수 있다면 편할지 모르겠다. 또한 AJ 작성에 걸리는 시간을 감안할 때 AJ를 코드젠하는 기술은 거의 필수라고 생각된다. 하지만 AJ가 코드젠 없이도 만들만큼 분량이 적다면? 그렇다면 굳이 코드젠 없이 그냥 손수 AJ 파일을 만들어 쓰면 될 것 같다. 하지만 내가 오늘 작업해본 AJ 코드는 일일히 작성하기에는 너무 길었다. 코드젠이 필요하다고 느껴졌다. 

2. 테스트가 필요하다.

코드가 눈에 보이면 고치기도 간편하다. 그래서인지, 왠만큼 돌아갈 것으로 만단되는 코드는 테스트를 잘 하지 않는다. 복잡한 로직이나 확신이 없는 코드에 대한 테스트만 작성하는 편이다. 자신감이라기 보다는 게으름이 맞을 것이다. 아무튼.. 그런데 이 게으름을 부리지 못할 정도로 날 불안하게 만드는 것이 바로 AJ다. 내가 아직 AJ에 익숙하지 않아서 일지도 모른다. 그래서 인지 불안하다. 그래서 인지 테스트가 절실하다.


컨틀로러 코드는 비어있지만, 아래 보시다시피 엄청나게 많은 것들이 추가됐다. 잘 동작하는지 어떻게 확신할 수 있겠는가? 글쎄.. 확신하지 못하겠다. 오랜만에 컨트롤러 테스트를 해보게 생겼다.

가짜 데이터 -> 컨트롤러 메서드 -> 결과 확인

스타일로 해볼 생각이다. 예를 들어, 위에 보이는 것중에 delete(int)가 있다. 가짜 데이터는 int 타입 변수 하나가 될 것이고, 메서드에 넘겨주고, service.delete()가 호출되는지 확인할 것이다.

3. 자동완성과 코드 네비게이션에 대한 대안을 찾았다.

위에 보이는 Cross References 뷰를 이용하는 것이다. Ctrl + 스페이스와 Ctrl + 클릭에 비할바는 못 되지만, 어떤 것들이 주입되었는지 한눈에 볼 수 있고 클릭을 하면 바로 AJ 파일로 이동해 주기 때문에 어느 정도 B급 대안이 될 수 있지 않을까 생각된다.

4. 배포는 어떨까?

어제도 포스팅 했지만, 메이븐에서 AJ를 컴파일 하도록 설정해주기만 하면 배포에도 전혀 문제가 없다. AJ는 스프링 AOP와 달리 컴파일 시점 위빙을 사용할 수 있기 때문에 더 간편한 것 같다. 스프링 AOP처럼 agent 설정을 하지 않아도 되니 얼마나 좋은가. 심지어 메이븐 설정을 스프링 ROO예제에서 그대로 배낄 수도 있다.ㅋㅋ

5. 활용 방법이 여러 가지다.
 
잘 모르겠지만, 기존의 자바 프레임워크는 (이클립스 플러긴 같은 툴은 빼고) 아마도 Generic을 활용한 DAO, Service, Controller 추상 계층과, View 자동 생성 그리고 유틸 라이브러리를 제공해주는 것일 것이다. 여기에 AspectJ 활용을 극대화 하여 새로운 프레임워크를 만드는 방법이 몇 가지 있겠다.

a. 기존 프레임워크에 AJ 파일 소수(하나 내지 두개)를 추가하여 자동 (또는 수동으로) 생성되는 코드를 깔끔하게 만드는 방법이 있다.
b. 스프링 ROO처럼 기존에 Generic 클래스 상속으로 제공하던 기능들을 일일히 AJ로 주입해주는 것이다.
c. 위 두 가지 방법을 혼용하는 것이다.

이 셋의 차이점은 좀 더 코딩하면서 느껴봐야 확실해 지겠지만, 지금까지 느낀점을 간략하게 정리하자면 이렇다.

a: AJ 코드량과 생성된 코드량이 모두 적다. 생성된 코드가 POJO 처럼 보이지만 사실은 이전과 동일하다.(extends Generic머시기..) 코드젠이 필요없거나, 간단한 코드젠이 필요하다.

b: AJ 코드량이 엄청 많지만, 생성된 코드량은 적다. 생성된 코드는 POJO에 가깝다. 다소 복잡한 코드젠이 필요할 수 있다. AJ가 커서 여러 AJ로 나누는 것이 깔끔해 보일 수 있다.(스프링 ROO) 중요 로직이 눈에 확 띈다.

c: a방법을 사용하는 AJ와 b방법을 사용하는 AJ를 분리하는게 좋다. 결국엔 코드젠이 필요하다.

오늘의 결론

어떻게 하나 결국에 생성되는 코드는 (실제 그런지 어떤지를 떠나서) 깔끔해 보이기 때문에 핵심 로직은 눈에 확 들어 올 듯 하다. 하지만 그러기 위해서 드는 노고를 생각해보면... 이걸 내가 꼭... 해야되는건가.. 싶기도 하다. 특히 a 방법을 쓸꺼라면.. 그냥 겉치례에 불과하다고 느껴진다. 그럴바엔 그냥 기존 방식을 쓰는게 나아보인다. 많은 노고를 들여서 b 방법으로 개발해 낸다면.. 멋질 것이다. 스프링 ROO 처럼 말이다. 단, 스프링 ROO는 DDD 다른 또 다른 학습 부담이 있어서 망설여지지, 그렇지 않고 기존의 계층형 아키텍처 스타일로 b 형태의 프레임워크를 제공한다면, 어떨까?? OSAF-ROO가 되는건가..

top


[Spring 3.0] HiddenHttpMethodFilter

Spring/3.0 : 2009.06.10 10:47


어제 밤에 스프링 ROO가 제공하는 REST 코드를 보다가 잠들었는데, 아침에 사부님 댓글을 보니, DELETE와 PUT method를 현재 브라우저와 HTML에서는 완전히 지원하지를 않더군요. 그래서 스프링 레퍼런스를 봤더니 역시나..

org.springframework.web.filter.HiddenHttpMethodFilter

이 녀석을 제공해주고 있었습니다. 사용하는 방법은 간단합니다.

    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

이렇게 web.xml에 추가해주면 되죠. 어젯밤엔 졸려서 이부분을 그냥 지나간것 같습니다.

해주는 일은 post method로 넘겨온 파라메터 중에 _method가 가지고 온 값으로 HTTP method를 실제 원하는 method로 바꿔주는 겁니다.

<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>

스프링 폼 태그를 이용해서 저렇게 method를 명시해주면 히든 필드 _method에 delete라는 값을 요청에 같이 포함해서 보내주며 실제 보내는 요청은 post가 됩니다. 하지만 이 필터가 _method의 값인 delete를 읽고서 HTTP method를 DELETE로 변경해주고, 그 결과 아래에 있는 핸들러가 이 요청을 처리하게 됩니다.

@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}

REST 스타일을 적용할 때 꼭 필요한 라이브러리 인 듯 합니다.
top


스프링 ROO에서 RESTful 코드 보기

모하니?/Coding : 2009.06.09 22:58


vote.roo 예제에서 ChoiceController_Roo_Controller 애스팩트에서 URL 맵핑과 뷰 맵핑 정보를 위주로 살펴보겠습니다.

/choice, POST => create => "choice/create" or "redirect:/choice/" + choice.getId();
/choice/form, GET => createForm => "choice/create"
/choice/{id}, GET => show => "choice/show"
/choice, GET => list => "choice/list"
PUT => update => "choice/update" or "redirect:/choice/" + choice.getId();
/choice/{id}/form, GET => updateForm => "choice/update"
/choice/{id}, DELETE => delete => "redirect:/choice"

이 정보들이 개인적으로 제일 궁금했었기 때문에, 이렇게 간추려서 정리해봤습니다. 이 정보들을 보다가 뷰에서 요청을 보내는 방법이 궁금했습니다. 흠.. GET은 알겠고, POST는 폼에서 보냈을 테고 DELETE는 어케 보냈을까나... 하면서 /choice/list.jsp 파일을 열어 보았습니다.

                    <td>
                        <form:form action="/vote/choice/${choice.id}" method="GET">
                            <input alt="Show choice" src="/vote/static/images/show.png" title="Show choice" type="image" value="Show choice"/>
                        </form:form>
                    </td>
                    <td>
                        <form:form action="/vote/choice/${choice.id}/form" method="GET">
                            <input alt="Update choice" src="/vote/static/images/update.png" title="Update choice" type="image" value="Update choice"/>
                        </form:form>
                    </td>
                    <td>
                        <form:form action="/vote/choice/${choice.id}" method="DELETE">
                            <input alt="Delete choice" src="/vote/static/images/delete.png" title="Delete choice" type="image" value="Delete choice"/>
                        </form:form>
                    </td>

헛 글쿠나.. 걍 폼에서 method를 명시해 줬구나.. @_@ 설정은 별로도 해줘야 할 거 없으려나..하고 web.xml과 vote-servlet.xml을 살펴봤지만 별다른 건 없더군요 아.. web.xml에 urlrewriter 필터를 적용했던데 스프링에서 사용하는 녀석이니 한 번 봐봐야겠습니다.

org.tuckey.web.filters.urlrewrite.UrlRewriteFilter

vote-servlet.xml에 있던 설정은 간단했습니다. internalResourceViewResolver에 prefix suffix 줘서 뷰이름으로 jsp 뷰 찾아가도록 빈을 등록했고, 파일 업로드를 하는지 머티파트 머시기 빈을 하나 등록했고, 발생한 예외에 따라 보여줄 뷰 이름을 등록한 예외처리뷰도 보이고 컴포넌트 스캔까지 네 개가 명시적으로 등록되어 있습니다.

REST 적용 간단해 보이네요~

참고자료: RESTful한 웹 서비스 만들기
top


AspectJ 메이븐 플러그인

Build/Maven : 2009.06.09 12:21


aj 파일들은 AspectJ Runtime을 이용해서 컴파일 해줘야 합니다. 이 작업을 일반적인 java 컴파일 이전에 수행해줘야 제대로 빌드 할 수 있겠죠.

메이븐에 다음과 같이 설정해주면 됩니다.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compliancelevel>1.6</compliancelevel>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

1.6을 메이븐 properties를 이용해서 다음과 같이 수정해주는게 좋겠죠.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <compliancelevel>${java.version}</compliancelevel>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

1.5 미만은 다음과 같이 최소 설정만 해도 되지만.. 별로 그럴 일은 없을 것 같네요.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

사용법은...mvn compile 또는 mvn test-compile
해당 페이스 실행할 때 자동으로 aspectj:compile이 동작합니다.

top


AspectJ를 이용한 코드젠과 프레임워크

모하니?/Coding : 2009.06.08 20:54


오늘은 도메인과 DAO쪽에만 AOP를 적용하는 AspectJ 파일을 만들어보았습니다. ROO를 참고하면서 말이죠. ROO와 다른 점은 프레임워크 코드를 이용한다는 거죠. (ROO는 제품 코드에서 ROO 코드는 하나도 이용하지 않는 완전한 non-intrusive 내지 transparent 코드젠 기술을 제공합니다.) 이런 식으로 새로운 형태의 OSAF도 만들어 낼 수도 있겠습니다. 하지만.. 할지 말지는 고민을 해봐야겠네요.

그 고민에 대한 시작으로, 아직은 충분한 예제를 못 만들었지만, 일단 여기까지 AspectJ를 이용한 프레임워크를 만들면서 느낌점을 정리해봐야겠습니다.

1. 자동완성 기능 사용 못 합.

이전 글처럼, AspectJ로 (메서드를 추가하거나 클래스 또는 인터페이스 상속을 추가하여) 어떤 클래스에 추가적인 기능들을 줬지만, 막상 이클립스에서 해당 클래스를 써먹을 때 코드 자동 완성을 사용할 수 없다는 점입니다. 원래 해당 클래스가 가지고 있던 멤버는 당연히 자동 완성이 되지만, AspectJ로 주입한 기능들은 참조가 되지 않습니다. 이 점은 AJDT에서 개선해주면 가능할지 싶은데... STS 최신 버전에선 어떨지 모르겠네요. 아무튼.. 이게 안 된다면.. 아.. 불편해..

2. 대체 뭐하는 녀석이람?

위 얘기랑 이어지는 이야기일 수도 있는데 해당 클래스가 하는 일이 숨겨져(?) 있다보니, 대체 어떤 일을 하는 지 눈치 채기가 쉽지 않습니다. 작명을 잘 해줘야겠죠.

3. 핵심 로직은 눈에 확 들어올 듯.

AJ 파일로 빼내는 로직들은 대부분 공통적인 내용일 겁니다. CRUD가 대부분이고 ROO의 경우에는 finder도 제공해주겠죠. 즉 감춰져 있는 부분이 무엇인가를 명확히 인지하고 있다면, 그 뒤에는 핵심 로직만 작성하면 될테고 코드에서 확 들어나게 되어 있겠죠. 이렇게 되면 2번에서 대체 뭐하는 녀석인가?라고 고민하는 시간도 줄어들테지요.

4. 코드 네비게이션 불편.

역시나 AJDT가 개선해 주길 바라지만, 현재로서는 AspectJ로 추가한 메서드나 필드로 Ctrl + 클릭으로 이동하는 것이 안 됩니다. 툴 측면에서 보면 1번과 비슷한 불편사항으로 볼 수 있겠습니다.

5. 성능?

AspectJ를 사용해서 컴파일 시점에 위빙을 하면 런타임 시에 성능 문제는 거의 없겠지만, 이 컴파일 작업이 매번 테스트를 돌릴 때 마다 일어나기 때문에 일반적인 테스트를 돌리는 것 보다는 조금 오래 걸리는 것이 사실입니다. 하지만 뭐 그정도 차이는 무시할만 하더군요.

오늘의 결론..

툴 지원이 조금만 더 보완된다면, AspectJ를 활용한 코드젠과 프레임워크를 활용하여 좀 더 깔끔하고 유연한 코딩을 즐길 수 있을 것으로 예상 됩니다. 코드 자동 완성과 네비게이션이 불편한 지금도 만약 AspectJ로 추가한 코드에 접근 할 필요가 없다는 가정을 한다면, 해볼 만 하다고 생각합니다.

Roo처럼 콘솔까지 제공하고, 변경 사항을 트래킹하여 롤백한다거나 코드젠 이후에 직접 코드를 수정해도 유기적으로 반영해주는 기능을 구현하긴 힘들겠지만, 단순한 코드젠으로 AspectJ를 생성하고 이 AspectJ가 (OSAF 같은) 프레임워크 코드를 이용하도록 한다면, 기존의 프레임워크를 한 단계 업그레이드 할 수 있는 방안이 되지 않을까 생각합니다.

ps: 흠.. 일주일을 쉬고 왔더니 머리가 빙빙 도네요. 색시한테 9까지 간다고 했는데, 9시에 출발하겠네.. 쏘리 쏘리 쏘리 쏘리~
top


[AspectJ] Extension and Implementation

AOP : 2009.06.08 20:16


http://www.eclipse.org/aspectj/doc/released/progguide/semantics-declare.html#extension-and-implementation

    declare parents: EmpDao extends GenericDao<Emp, EmpParams>;
    declare parents: EmpDaoImpl extends HibernateGenericDao<Emp, EmpParams>;
    declare @type: EmpDaoImpl: @org.springframework.stereotype.Repository;

EmpDao 클래스가 GenericDao 클래스를 상속 받도록 설정.
EmpDaoImpl 클래스가 HibernateGenericDao 클래스를 상속 받도록 설정.
EmpDaoImpl 클래스에 @Repository 애노테이션 추가.

문법이 복잡해 보였는데 막상 사용해보니 간단 간단 하네요.

public interface EmpDao {

}

public class EmpDaoImpl implements EmpDao {

}

이런 기초적인 코드만 존재하지만..

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-datasource.xml"})
@Transactional
public class EmpDaoImplTest {

    @Autowired
    EmpDaoImpl daoImpl;

    @Test
    public void daoInterface() throws Exception {
        assertNotNull(daoImpl);
        daoImpl = new EmpDaoImpl();
        GenericDao<Emp, EmpParams> gdao = daoImpl;
        HibernateGenericDao<Emp, EmpParams> hgdao = daoImpl;
    }

    @Test
    public void crud() throws Exception {
        Emp emp = new Emp();
        daoImpl.add(emp);
        daoImpl.flush();
        assertEquals(1, daoImpl.getAll().size());
    }

}

이런 테스트를 돌릴 수 있다는거...



top


[AspectJ] privileged aspect

AOP : 2009.06.08 17:24


public class Emp {

    private String name;

    private String email;

}

이런 클래스가 있습니다. 이게 전부입니다.

public class EmpTest {

    @Test
    public void javaBean() throws Exception {
        Emp emp = new Emp();
        emp.setName("keesun");
        assertEquals("keesun", emp.getName());
        emp.setEmail("keesun@email.com");
    }

}

이런 테스트가 돌아갈까요? 훗.. 그럴리가요. 있지도 않은 메서드(게터, 세터)를 마구 썼는데 될리가 없죠. 그러나..  잘 돌아갑니다.


어떻게 된걸까요? privileged aspect를 사용하면 타겟의 private 또는 protected 멤버에도 접근할 수 있습니다.

http://www.eclipse.org/aspectj/doc/released/progguide/semantics-aspects.html#aspect-declaration

스프링 AOP로 이런 일을 하려면 Introduction을 사용 해야겠는데.. 그게 참.. 그리 쉽지 않았던 기억이 납니다. 하지만 AspectJ로는 간단하네요~

privileged aspect EmpAspect {

    //JavaBean
    public String smdis.model.Emp.getName() {
        return this.name;
    }

    public void smdis.model.Emp.setName(String name) {
        this.name = name;
    }

    public String smdis.model.Emp.getEmail() {
        return this.email;
    }

    public void smdis.model.Emp.setEmail(String email) {
        this.email = email;
    }

}

이렇게 추가할 메서드를 넣어주고 마치 자기가 가지고 있는 변수처럼 사용하면 됩니다.
top


콜 오브 듀티(COD) Modern Warfare 2 어여 나와라~

모하니?/Watching : 2009.06.08 08:34




캬... 게임을 정말 예술로 만드네요~

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

[WCG 2009] 요즘 하고 있었군요.  (4) 2009.11.13
초고속 로보트 손  (0) 2009.09.15
허경영의 콜미  (0) 2009.08.13
한국의 비양심  (0) 2009.07.24
러시아의 양심  (4) 2009.07.24
콜 오브 듀티(COD) Modern Warfare 2 어여 나와라~  (0) 2009.06.08
프랑스도 멋진 나라구나..  (6) 2009.05.25
핀란드 멋진 나라네요.  (11) 2009.05.21
못살겠다 대한민국  (7) 2009.04.17
한국에도 이런 개그가 통할까?  (0) 2009.01.15
http://www.parleys.com 멋진 싸이트  (2) 2008.09.27
top


이제 다시 복귀 할 시간

모하니?/Thinking : 2009.06.07 10:20


어제는 원래 오전에 스터디에 참석하려 했으나, 빨래, 짐정리, 집청소가 더 시급했기 때문에 불참하고, 낮에는 본가에 찾아가서 어르신들께 인사하고 저녁에 다시 돌아와서 남은 정리를 했습니다. 제 와이프도 저만큼 피곤할텐데 저보다 훨씬 많은 일을 해서 제가 미안할 정도더군요. @_@ 암튼 와이프 짱!!

오늘 아침에는 밀린 RSS와 이메일을 정리했습니다. RSS에서 제일 눈에 띈 글들은 MAX님의 스프링 ROO 플러그인 관련 글과 사부님 블로그에 egit 관련 글과 스프링 블로그에 있는 스프링 시큐리티 3.0 M1 배포관련 포스팅이 눈길을 끌었습니다. 마지막 글은 공부도 할겸 번역/요약해서 올렸습니다. 이메일은 죽죽 보면서 필요한 글들에 답신을 보냈습니다.

이제 낮에는 예식장과 신혼여행 때 찍은 사진과 캠코더 동영상을 정리하고, 피자나 치킨을 시켜먹을까 생각 중입니다. 캬캬캬.. 맛있겠다.

이제부터 다시 꾸준히 블로깅도 하고 회사일도 열심히하고 번역도 열심히 하고 스터디도 열심히 하고 집안일도 잘하는 좋은 남편이 되야겠습니다.

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

완전 부럽다...  (22) 2009.09.09
쓰리좝 시작인가...  (6) 2009.08.31
약속의 힘  (2) 2009.07.23
벌써 한 달이 넘었군요.  (12) 2009.07.07
스프링소스의 끼워주기(?) 패키지  (2) 2009.06.17
이제 다시 복귀 할 시간  (8) 2009.06.07
재밌는 상상~ 누드 비치에 사무실 차리기  (6) 2009.04.28
외국 IT 기업에서 원하는 걸 보자꾸나~  (5) 2009.04.17
'개발' 역시 참 맛있다.  (2) 2009.04.16
저 결혼해요~~  (92) 2009.03.02
난 몇 살?  (0) 2009.01.28
top


스프링 시큐리티 3.0.0 M1 배포



참조 편역 요역: http://blog.springsource.com/2009/06/03/spring-security-300m1-released/

http://www.springsource.com/download 에서 직접 다운로드 할 수도 있고, 메이븐을 사용한다면, http://maven.springframework.org/milestone 메이븐 저장소를 추가하면 M1 의존성을 추가할 수 있다. JDK 1.5 이상, 스프링 3.0이 필요함.

표현식-기반 접근 제어

스프링 EL 기반 권한 관리를 지원한다. 메서드 애노테이션이나 웹 시큐리티에서 표현식을 사용할 수 있다. 속성이나 보터(voter)-기반 매커니즘에 비해 새로운 조합을 꾀할 수 있다. 다음은 웹 시큐리티에서 시큐리티 네임스페이스를 사용하는 간단한 예제다.

<http>
   <intercept-url pattern="/secure/**" access="hasRole('ROLE_SUPERVISOR') and hasIpAddress('192.168.1.0/24')" />
   ...
</http>

hasRole('ROLE_SUPERVISOR')은 사용자의 권한 목록을 확인하고 사용자가 해당 롤을 가지고 있다면, true를 반환한다. 여기에 IP를 확인할 수 있는 새로운 표현식을 추가했다.

@Pre와 @Post 애노테이션

메서드 시큐리티는 웹 요청을 수락하거나 거부하는 것과는 달리 조금 더 복잡하다. 메서드 시큐리티에 표현식을 사용해서 좀 더 다양한 기능을 제공하기 위해, 4개의 새로운 애노테이션을 추가했다. 이 애노테이션들을 사용하여 메서드 호출 전과 후에 특정 로직을 실행할 수 있다. 이 기능을 사용하려면 global-method-security 네임스페이스 엘리먼트에 새로운 속성을 사용해야한다.

<global-method-security pre-post-annotations="enabled"/>

가장 유용한 것으로 @PreAuthorize가 있는데, 이 애노테이션은 메서드를 실제로 실행할지 말지 여부를 제어한다. 예를 들어(예제 애플리케이션의 Contacts에서) 다음 메서드를 보자.

@PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);

이것은 ROLE_USER라는 롤을 가진 사용자만 접근을 허용한다는 뜻이다. 별 다른게 없다.

@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);

이번에는 메서드 인자를 표현식에서 참조하고 있다. 해당 contact에 대해 현재 사용자가 admin 권한이 있는지 확인한다. hasPermission() 표현식은 애플리케이션 컨텍스트를 통해서 스프링 시큐리티 ACL 모듈과 연결되어 있다.(어떻게 연결되어 있는지는 Contacts 예제를 통해 살펴보기 바란다.) 메서드 인자를 표현식 변수로 참조할 수 있다. 스프링 EL의 모든 기능을 사용할 수 있기 때문에 인자의 속성에도 접근할 수 있다. 따라서 특정 사용자의 이름이 contact의 이름과 대응할 경우로 제한하고 싶을 때 다음과 같은 표현식을 사용할 수도 있다.

@PreAuthorize("#contact.name == principal.name)")
public void doSomething(Contact contact);

여기서는 내장된 표현식 principal을 사용했다. 이 것은 현재 스프링 시큐리티의 Authentication 객체를 기반한 것으로 시큐리티 컨텍스트에서 가져온 것이다. Authenticatino 객체에 직접 접근하려면 authentication 표현식이름을 사용할 수도 있다. 메서드 호출 후에 권한 작업을 수행할 수 있는데, 이 때는 @PostAuthotize 애노테이션을 사용하고, 반환값은 "returnObject"로 참조할 수 있다.

필터링

스프링 시큐리티는 컬렉션과 배열 필터링을 이미 제공하고 있었는데, 이제는 표현식을 사용할 수도 있다.

@PreAuthorize("hasRole('ROLE_USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List getAll();

여기서 filterObject는 반환하는 컬렉션에 들어있는 각각의 요소들을 지칭하고, 해당 요소에 대한 사용자의 권한이 read 이거나 admin이 아닌 것은 컬렉션에서 빼낸다. @PreFilter를 사용해서 메서드 호출 전에 필터링을 할 수도 있지만, 거의 사용하지 않는듯 하다. 문법은 같은데, 인자에 두 개 이상의 컬렉션이 있을 때 filterTarget 속성을 사용하여 어떤것을 사용하는지 지칭한다.

코드기반 재구성

3.0에서 대부분의 코드는 spring-security-core.jar로 들어갔다. 몇 년에 걸쳐 여러 기능이 추가되다보니 의존성간에 CR(circular reference)도 생기고 복잡한 의존성 구조가 되어버렸다. 또한 여러 jar에 나눠져서 들어간 패키지가 OSGi에서 말썽을 일으킨다는 이슈도 있었다. 이로인해 유지보수 오버헤드가 발생했고, 그 걸과 3.0에서 코드기반을 재구성하기로 결정했다.

프로젝트 JAR 파일

- 이 부분은 별도로 포스팅. 여기서는 생략.

이로인해 코드를 순회하며 참조하거나 이해하기 쉬워졌다.

스프링 시큐리티 3.0 JAR 의존성

패키지 구조

더이상 CR도 없고 훨씬 깔끔해졌다.


기타 변경사항

클래스 이름 변경: 이름들이 훨씬 명시적으로 바꼈군요.
AbstractProcessingFilter -> AbstractAuthenticationProcessingFilter
AuthenticationProcessingFilter -> UsernamePasswordAuthenticationProcessingFilter
AuthenticationEntryPoint -> LoginUrlAuthenticationEntryPoint
ObjectDefinitionSource -> SecurityMetadataSource
HttpSessionContextIntegrationFilter -> SecurityContextPersistenceFilter

인증 성공 또는 실패시 리다이렉션/포워딩: 인증 성공 또는 실패시에 브라우저가 이동할 목적지를 제어하는 방법 제공.
AuthenticationSuccessHandler
AuthenticationFailureHandler
http://jira.springsource.org/browse/SEC-745

레퍼런스 매뉴얼과 웹 사이트 업데이트: 아직 작업 중이지만 몇 개 챕터(네임스페이스, 기술 개요)는 업데이트 했다. 프로젝트 사이트의 FAQ도 업데이트 해서 몇몇 발표 비디오와 온라인 기사를 참조할 수 있다.

결론

스프링 EL을 사용하여 기능이 좀 더 풍부해졌고, 코드기반을 깔끔하게 정리했다.
JIRA 변경로그
커뮤니티 포럼
JIRA 이슈
top