Whiteship's Note


[하이버네이트 툴] hbm2doc

Hibernate/etc : 2009.06.30 20:37


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



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

Write a comment.


[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

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.07.01 22:22 PERM. MOD/DEL REPLY

    깔아놨다가 다 지워버려서 제대로 써본적이 없는데, 좋은가요? ^^?
    툴이 너무 많이서 툴사용법 익히는데만도 오랜 시간이 필요한건 아닌지 모르겠습니다. @_@;

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.07.01 22:36 신고 PERM MOD/DEL

    저는 하이버네이트 툴 때문에 설치를 했는데, 아직은.. 그다지... 흠... 잘 모르겠네요.ㅋ

Write a comment.


[하이버네이트] 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

Write a comment.


[하이버네이트] 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

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.26 01:48 PERM. MOD/DEL REPLY

    제가 알기로 HashSet같은 Set은
    이미 저장된 객체들의 equals() 를 먼저 비교하는게 아니라
    저장된 객체들의 hashCode()를 먼저 다 비교해 본후에 동일 hash code가 없으면
    동일 객체가 없는것으로 판단해서 저장을 하고,
    동일 hash code가 발견되면 그때 equals() 비교를 해서
    true면 동일 객체로 판단해서 저장을 하지 않고,
    false면 다른 객체로 판단해서 저장을 하는것으로 알고 있습니다.

    위의 경우 말고도 문제가 되는 경우가 있겠죠.
    가령 DB에서 id로 쓰는 자동생성되는 surrogate key 같은걸
    equals()와 hashCode() 에 사용해 버린 객체를 DB저장전에
    HashSet에 넣었다가 DB에 저장을 하면
    저장 전에는 id값이 null (Integer 나 Long 타입) 이나 0 이었다가
    저장 되면서 id값을 받게 되면서, hashCode() 와 equals() 결과값이 변해서
    이미 저장해 놓은 HashSet에 존재하지 않는 새로운 객체로 인식을 해버리는
    문제가 생길수 있겠죠.
    (둘중 하나만 달라도 충분히 다른 객체로 인식하겠지만요.)

    아... 오늘도 묻어가기...ㅡ_ㅡ;;;

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.26 08:30 PERM MOD/DEL

    그렇군요; hashCode()가 먼저였군요.

    ㅎㅎ오늘도 감사합니다.

  2. Favicon of http://kwon37xi.egloos.com BlogIcon 권남 2009.06.29 09:30 PERM. MOD/DEL REPLY

    Kevin 님이 하신말씀에 부연설명 합니다.

    ID 값만으로 hashCode와 equals를 생성하면 id == null인 상태의 객체 여러개를 Set에 저장할 때 문제를 일으킵니다. 따라서 절대로 그런일 이 없게 하거나, 아니면 createdAt 같이 객체의 ID 값 처럼 절대 불변하는 어떤 값도 함께 equals와 hashCode에 추가해주어야 합니다.

    최선책 : 객체의 ID와 그외의 불변하는 어떤 을 더 추가하며 hashCode/equals를 만든다.
    차선책 : ID만으로 hashCode/equals를 구현하고 절대로 DB에 저장하기 전에 먼저 Set에 저장하는 일이 없도록 개발자들에게 경고한다.

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.29 18:32 PERM MOD/DEL

    넹. transient 상태 객체는 아직 id가 없을테니깐 보조키(?)도 같이 활용해서 hashCode와 equals를 구현하는게 좋겠네요.

Write a comment.


이클립스 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

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.25 11:52 PERM. MOD/DEL REPLY

    저거 설치하다가 잠도 제대로 못 잤네요...ㅡ_ㅡ;;;
    우여곡절끝에 설치 완료 했습니디만... 결국 당분간은 Galileo는 못 쓸것 같네요... @_@;;;
    Galileo JEE 자체에 버그가 있는거 같습니다. ㅠ_ㅠ
    'Web Resources' 기능이 제대로 작동 안 하는거 같아요.
    버그 리포팅 해야겠습니다.
    애초에 걱정은 사용중인 plug-in설치가 잘 될까 였는데,
    plug-in들은 어떻게 다 설치를 마쳤지만,
    (M2Eclipse stable development 버전이랑 Subclipse 1.6.x 버전도 설치가 잘 되네요).
    결국 Eclipse JEE 기본 기능 버그라는 엄청난 복병이... ㅠ_ㅠ
    아놔, 잠도 못자고 시간낭비 했네요.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:55 신고 PERM MOD/DEL

    고생하셨네요; 전 아직 서버시브 설치도 안 해봤어요.

    한 세달 정도 지나면 쓸만해 질지도...ㅎㅎ

  2. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.27 12:30 PERM. MOD/DEL REPLY

    오늘 SpringSource에서 Eclipse 3.5기반의 STS 2.1.0 RC1을 내놨네요. (3.4 기반도 RC1)
    plug-in 설치하려고 한 제노력은 삽질이 되어 버리는 희소식입니다... @_@;
    AspectJ 사용하기 편한 기능들이 추가 됐다고 해서 기대하고 있습니다. :)

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.27 14:44 PERM MOD/DEL

    네 저도 좀 전에 보고 다운 받아뒀습니다. ^^
    왠만하면 Subversive도 같이 설치해서 배포해주면 할 일이 하나 줄어들텐데 그건 직접해야겠네요.

    MoreUnit도..ㅋㅋ

  3. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.27 20:04 PERM. MOD/DEL REPLY

    제가 실수 했네요. 지금 보니까 Eclipse 3.5 기본 기능에 문제가 있는게 아닌거 같습니다.
    그동안 설치하면 plug-in부터 깔기 시작해서
    순수한 Eclipse JEE만 써본적이 거의 없어서 몰랐는데,
    기본 기능이 아니고,
    M2Eclipse 아니면, STS 에 있는 기능 하나가 제대로 안 돌아가는거 같네요.
    Web Resources 기능이 동작을 안 합니다.
    아주 중요한건 아니지만, 없으면 좀 불펀한데... :(

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.27 21:28 PERM MOD/DEL

    조만간 해결되겠죠?
    제대로 써보질 않아서 어떤 문제가 있는지 몰겠네요.

Write a comment.


[하이버네이트 퀴즈] 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

  1. Favicon of http://igooo.org/tc BlogIcon igooo 2009.06.24 23:37 PERM. MOD/DEL REPLY

    emp.setName("tb";); 위에서 flush시키면 update가 나오기

    assertThat(ed.get(emp.getId()).getName(), is("ks";)); 지우면 update가 나타나지 않을까요?


    이클립스 설치하느라 실행은 못시켜봤습니다 .ㅎ

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.25 08:33 PERM MOD/DEL

    안타깝게도.. 둘 다 땡입니다.

  2. koasu 2009.06.25 10:39 PERM. MOD/DEL REPLY

    4번: @Transactional 을 지우면 update 가 나타날꺼 같아요.
    5번: ed.update(emp); ed.flush(); 순으로 하면 update가 나타날꺼 같나요.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:02 신고 PERM MOD/DEL

    4번은 땡. 그거 없이 dao 테스트하기는 좀 그르치요. 당장엔 트랜잭션이 없다고 에러가 날 겁니다. 트랜잭션 설정을 롤백=false로 바꾼다 하더라도 결과는 마찬가지 입니다.

    5번은 맞추셨습니다.

  3. crystal 2009.06.25 11:34 PERM. MOD/DEL REPLY

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


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

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:41 신고 PERM MOD/DEL

    딩동댕!

  4. crystal 2009.06.25 11:38 PERM. MOD/DEL REPLY

    1. DB에 update가 되지도 않았는데 테스트는 어떻게 통과한 것일까?
    update전 emp객체가 컨택스트에 관리되는 객체, 즉 persistant 상태에 있기 때문이다.

    2. 왜 update 문은 날아가지 않은 것일까?
    아래 해당 객체의 delete가 있기 때문이다.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.25 11:41 신고 PERM MOD/DEL

    딩동댕~

Write a comment.


[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

Write a comment.


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

모하니?/Thinking : 2009.06.17 11:13


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


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

  1. Favicon of http://toby.epril.com BlogIcon 2009.06.17 14:38 PERM. MOD/DEL REPLY

    M1부터 끼워주고 있었는데... 뒷북?

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.17 17:56 PERM MOD/DEL

    넹 뒷북! ㅋㅋ

Write a comment.


[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

  1. Favicon of http://yunsunghan.tistory.com BlogIcon Max 2009.06.19 09:03 PERM. MOD/DEL REPLY

    Repository 를 DAO로 대체할수 있는건가요? (http://whiteship.me/1305 이글에서 토비님이 어떻게 설명하셨는지 궁금해요.) DDD에서 말하는 Service와 ROO에서 말하는 Facade(Middle-Tier)는 다른 것 같다는 생각도 듭니다.

    그런데 위에서 application tier가 꼭 필요하나요? Ben이 마지막에 DTO를 ROO에서 제거했다는데 왜 그랬을까요...

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.22 10:31 신고 PERM MOD/DEL

    DAO로 Repository를 대체한 거라기보단, Repository 역할을 하는 녀석 이름을 짧게 DAO라고 부르기로 했습니다. 하이버네이트를 쓸 경우 굳이 객체 컬렉션(Repository)을 별도로 다룰 필요도 없을 것 같고, 이름도 좀 짧으니까요.

    DDD의 Service를 두 종류로 나눠 볼 수 있겠는데요. 위에서 사용한 Service는 트랜잭션을 담당하는 퍼사드 역할을 합니다. 이 녀석이 둘 중 하나인 thin facade로 볼 수 있겠죠. 나머지 한 부류인 email, jms, jmx 같은 기반 시설들은 infra service로 보고, 별도의 패키징으로 구분하기로 했습니다. 이름은 둘 다 동일하게 Service라는 접미사를 쓰구요.

    application tier를 둔 이유는 트랜잭션 처리 때문입니다. ROO에 보면, Entity_~~.aj의 메서드에 @Transactional 애노테이션으로 트랜잭션 처리를 하고 있습니다. 즉, DAO 내기 Repository에서 트랜잭션을 담당한다는 건데.. 그럴 경우 여러 도메인에 걸치거나 여러 DAO를 걸쳐 어떤 로직을 처리한다고 할 때 트랜잭션 처리 위치가 애매해지고, 사방으로 퍼질 가능성이 있어 보여서 thin facade 격인 serivce를 두고, 단순 위임만 하도록 application tier를 두었습니다.

    Ben이 DTO를 제거한 이유는 잘 몰게네요. @_@

  2. Favicon of http://scroogy.tistory.com/ BlogIcon 스쿨쥐 2009.11.23 17:28 PERM. MOD/DEL REPLY

    안녕하세요^^ 이제 갓 2년차가 된 스쿨쥐라고 합니다. 기선님의 글을 보고 많이 배우고 있는 초보 개발자 중 한명입니다. 항상 좋은 글을 남겨주세셔 감사합니다.

    다름이 아니라 저도 DDD와 엔터프라이즈 애플리케이션 아키텍처 패턴을 보면서 프로젝트 규모에 따른 저만의 아키텍처를 구성하던 중 기선님의 DDD 아키텍처(수정전 버전)과 거의 동일하게 나오더군요. 수정본을 보니 약간 의문점이 생겨서요.

    이유인즉, 도메인 모델에서 Repository의 역할을 과연 DAO가 대체할 수 있는가 하는 문제입니다. Repository가 AGGREGATE별로 하나씩 생성되는데 위의 예제에서 Address나 OrderLine에 대해서 DAO가 관여하게 된다면 이미 DAO 구현체에 비지니스 로직이 섞여들어가는 것이 아닌가 생각됩니다. 만약 OrderLine에 대해서 별도의 DAO를 생성한다면 이는 Ropository의 개념에서 벗어나는 것이구요.

    제가 생각하기에는 기존 Ropository를 유지한 채로 OrderRepository에는 OrderDAO와 OrderLineDAO가 존재해야하기 때문에 Repository를 대체하기에는 조금 무리가 있지 않나 생각됩니다. 이는 이터너티님께서 언급한 글 중에도 나와있구요.(http://aeternum.egloos.com/1160846 - "결과적으로 REPOSITORY에서 제공하는 하나의 오퍼레이션이 DAO의 여러 오퍼레이션에 매핑되는 것이 일반적이다. 따라서 하나의 REPOSITORY 내부에서 다수의 DAO를 호출하는 방식으로 REPOSITORY를 구현할 수 있다." )

    프로젝트의 규모에 따라 차이가 있겠지만 Repository에서 전략이 동적으로 바뀌어야할 경우, 예를들어 도메인을 메모리에서 먼저 검색하고, 없으면 프록시에서 검색하고, 또 없으면 메인 DB에서 검색해야하는 경우와 같은 부분에서는 DAO가 하기 힘든 일이라고 생각됩니다.(엔터프라이즈 아키택처 패턴에서 저장소부분의 예제를 보고 떠오른 생각입니다.)

    단순 용어의 차이일수도 있으나 Repository의 개념으로 사용하면서 DAO로 표현한다면 오해의 여지가 있지 않을까 해서 댓글을 남깁니다.(이터너티님의 글의 마지막 부분은 저는 이렇게 이해를 했습니다.)

    혹여나 공격적인 글로 오해하셔서 기분상하지 않으셨으면 합니다. 항상 기선님 글에서 많은 도움을 받는 개발자 중 한명이니까요 ^^. 항상 좋은 글 감사합니다.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.11.23 22:43 PERM MOD/DEL

    와우. 멋진 의견이십니다. 저도 우연히 오늘 다시 이터너티님의 DAO와 리파지토리에 대한 글을 다시 읽으면서 스쿨쥐님과 비슷한 생각을 했었습니다.

    시간이 되면 이 구조의 아키텍처를 다시 개선해서 DDD 구현에 도전해봐야겠습니다.

    ps: 위 아키텍처 적용은 실패했습니다. T.T

  3. Favicon of http://scroogy.tistory.com/ BlogIcon 스쿨쥐 2009.11.23 17:54 PERM. MOD/DEL REPLY

    참, 규모가 작은 프로젝트라면 트랜잭션 스크립트 + 액티브레코드 + 서비스레이어(도메인 퍼사드 접근방법) 조합도 컴포넌트 분리의 측면에서 바라볼 때 괜찮다고 생각됩니다. ^^ 개발속도나 스터디 비용에 있어서는 굉장히 유용하다고 생각됩니다. (but 저는 이 조합을 선호하지는 않습니다. 다만 개발하다보면 개발자의 역량을 생각한 아키텍처도 중요하다고 생각되는군요.)

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.11.23 22:43 PERM MOD/DEL

    저는 ROR이나 Grails에 대한 경험이 없어서 잘 모르겠지만, 작은 프로젝트 부터 한 번 적용해봐야겠습니다. 조언 감사합니다. :)

Write a comment.


[MAC]OmniGraffle

Good Tools : 2009.06.14 14:52


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

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



top

TAG UML2.0,

Write a comment.


[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

Write a comment.


[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

Write a comment.


[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

  1. 2009.06.12 13:50 PERM. MOD/DEL REPLY

    비밀댓글입니다

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.12 15:08 신고 PERM MOD/DEL

    제 학습 로드맵을 요구하시는거군요?
    흠;; 하도 이것 저것 공부해서 로드맵이 어떻게 되는지 기억도 안나는데요.. 쉴 때 정리해 보겠습니다.

  2. Favicon of http://moova.tistory.com BlogIcon moova 2009.06.14 02:08 PERM. MOD/DEL REPLY

    기선군에게 로드맵을 달라고 하는 분이 있네요. 비밀댓글 상상하기~ㅋ
    그나저나 데이터베이스는 언제 공부할꺼임? 로드맵이 필요하면 단기 숙성 로드맵이 나한테 있긴 있는데....

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.14 09:34 PERM MOD/DEL

    그러게요. DB 공부도 해야되는데;; DB 만들고 지우는 일밖에 할 게 없으니.. @_@

  3. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.14 22:32 PERM. MOD/DEL REPLY

    공부하자 마자 또 뚝딱뚝딱 금새 만드셨네요. 역시 멋지십니다. ^O^_b
    한참 해오던거 탈피하는게 쉽지 않죠.
    전 2학년 1학기 마치고, 완전한 DDD는 아니지만
    자연스럽게 DDD 스타일의 디자인 습관이 생겼었는데...
    (DDD를 배운건 아닌데, 그 과목에서
    Domain Modeling의 중요성에 대해 강조를 하다보니 그렇게 되더군요.)
    근데, 마지막 학년 마지막 학기에
    Java web app. development쪽을 좀더 공부하려고
    J2EE 과목을 들었더니... 그 망할 EJB (그것도 2.0) 때문에
    혼란이 오더군요.

    가령 JavaBean 만드는데, Order 같은거 합계 계산하는 method를
    Order bean에 넣었는데, JavaBean은 자료 전달만 해야한다고... :O
    실제 돌아가는 EJB하나 만드는데, Home, Remote interface를 꼭 만들어야하고,
    암튼 그래도 믿었습니다. EJB가 표준인데다가, 여러 기업들이 참여해서 만든거라서...

    그러다보니 졸업후에 EJB의 문제점을 알게되서 이거 없이 개발을 해도,
    스타일이 비슷하게 가더라구요. 그러다가 로드 존슨이 강의한 영상을 보고,
    '아... EJB한테 당했다!' 라는 느낌이...ㅠ_ㅠ
    암튼 그 영상에서 DDD도 알게 되고, 바로 에릭 에반스가 강의한것도 찾아보고
    전에는 그냥 감으로 하던 도메인 모델링이
    체계가 잡혀 있다는것도 알게 되고, 참 좋더라구요. :)
    진작에 스프링에 손댈껄 그랬다는 후회도 되구요.
    암튼 늘 하던 방식 탈피하는데는 정말 노력이 필요하더군요.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.15 14:16 PERM MOD/DEL

    ㅋㅋ금새는 아니에요. 저 그림도 몇 번이나 고치고 다듬은건지 모르겠네요. 아직 DDD도 잘 모르는 상태에서 그린거라 DDD랑 안 어울리는 부분도 있을 것 같습니다.

    저는 애초에 EJB는 보지도 않고 스프링으로 개발을 시작해서 이전에 얼마나 불편했었는지 어깨너머로 들은게 전부라.. 공감할 만한 경험이 없네요.ㅎㅎ;

    로드존슨 아저씨 덕분에 개발자로 일하고 있는 것 같아서 항상 맘속에 고마움을 느끼고 있답니다.

  4. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.14 22:39 PERM. MOD/DEL REPLY

    아... 근데 저기 Repository에 DAO가 또 필요한 건가요?
    제 생각엔 좀 중복 되는거 같은데요.
    Repository interface를 implement 하는 class가 DAO를 대신해야 하는거
    아닐까 하는 생각이 듭니다만...
    따로 DAO를 가지게 되면 Repository 이외에도 해당 Entity를 찾을수 있는 객체가
    존재한다는 얘기니 중복되는게 아닌가 하는 생각이 들어서 말이죠.
    Infrastructure는 Hibernate 같은 ORM이 되어야 하는게 아닐까 싶습니다만...
    제가 뭐 잘알고 하는 얘긴 아니고, 저는 DDD초급자라서 제의견과 함께
    가르침을 구하는 겁니다...^^;

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.15 14:22 PERM MOD/DEL

    네, 저 둘은 어찌보면 중복으로 보이죠.

    그런데 제가 만든 DAO가 너무 하이버네이트 기술에 의존해 있다보니, 객체 컬렉션으로 인식해야 할 Repository랑은 잘 안 어울리는 것 같기도 하고,

    DAO는 GenericDAO를 상속 받아서 기본적인 DAO 기능들 + 하이버네이트 기능(flush, clean 같은것)까지 포함한 것을 제공하는 반면 Repository는 왠만하면 도메인에서 실제로 사용하는 메서드들만 두기 위해서 구분지었습니다.

    어느 글인지는 모르겠는데, 여기 저기 DDD관련 코드를 뒤적거리다가 Repository가 DAO를 가지고 있는 코드를 봤었는데, 거기서 착안해서 저렇게 그렸고 구현했습니다.

    기능적으로 중복이기는 한데, Entity가 DAO를 직접 사용하지 않고, Repository를 거쳐서 사용하게끔 할 생각입니다.

  5. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.15 21:41 PERM. MOD/DEL REPLY

    아... 그렇군요.
    제 생각에는 사용 기술보다 Problem Domain에게 영향을 더 받는
    Repository와 사용기술에 종속적인 DAO를 같이 쓰면
    기술적 제약에 따라 디자인 자체가 변경되는 문제가 생길것 같아서 말이죠.

    가령 DAO는 자체 디자인이 변경될 가능성이 Domain modeling 을 통해 만들어진
    object 보다 높으니, 이게 변경되면 DAO만 변경되는게 아니라
    이걸 쓰는 Repository 내부도 변경을 해야하니 유지보수에 도움이 되지 않을것 같아서요.

    디자인 자체가 바뀌지 않으면 내부적으로는 아무리 바뀌어도
    해당 디자인을 사용하는 객체들에게 크게 영향을 미치지 않을텐데
    DAO를 쓰다보면 이녀석의 디자인자체가 바뀔 가능성이 있어서 말이죠.

    아무튼 이건 제 생각이구요.
    기선님 방법도 궁금하니, 해보시고 가능하면
    결과 공유 좀 해주시면 좋겠네요. :)
    저도 나름대로 이거저거 해보고 공유하겠습니다. ^^;

    그리고 기선님께서 소개하신 글 보고 방문한 이터너티님의 블로그에서
    좋은글을 하나 발견했습니다.
    http://aeternum.egloos.com/1160846

    아...
    참고로, 이미 보셨을지도 모르지만,
    Eric Evans의 저서 Domain-Driven Design 에서
    Repository 안에 SQL 쿼리와 일정부분의 DB access 를
    구현한 코드를 본적이 있습니다.
    물론 개념을 제공하기 위한 예제 코드라서 완벽한 해답이라고
    할수는 없겠지만요.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.15 23:26 PERM MOD/DEL

    네 저도 저 글을 읽어봤습니다.

    Repository에 필요한 기능을 구현하려다 보니 DAO가 바뀔 수는 있을 지언정, DAO가 독립적으로 자주 바뀔 일이 있을지는 잘 모르겠습니다.

    아직 설계만 잡아두고 구현을 충분히 하지 못해서 실용적인 디자인이지는 잘 모르겠습니다. DDD 학습도 충분치 못해서 DDD 아키텍처라고 내세우기도 좀 그렇구요. 둘 모두 구현해 가면서 차차 다듬어 봐야겠습니다.

    물론, 모든 내용은 될 수 있는 한 공유할 생각입니다. 항상 좋은 의견 감사합니다.

Write a comment.


[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
  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.06.11 13:55 신고 PERM. MOD/DEL REPLY

    Thank U~~~~~~~~~~~~~ ㅋㅋ(영어 필터 대박..ㅋ)

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.11 13:58 신고 PERM MOD/DEL

    긁어오기 밖에 안했는데 고맙긴

  2. Favicon of http://flyburi.com BlogIcon 버리 2009.06.11 21:32 PERM. MOD/DEL REPLY

    와우 DDD가 이렇게 정리가 잘되어 있을줄이야.. 새로운 글의 발견 +_+ 감사~

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.11 22:44 PERM MOD/DEL

    넹~

Write a comment.


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

Write a comment.


[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

  1. Favicon of http://jjaeko.tistory.com BlogIcon 째코 2009.06.10 13:19 PERM. MOD/DEL REPLY

    ssimini에서도 restful을 지원할때 별도의 파라미터(_http__method)를 이용 하빈다...
    restful 정말 매력적인듯 하네요.

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.10 17:44 PERM MOD/DEL

    오호 ssimini도 REST를 지원하는군요~

  2. Favicon of http://kwon37xi.egloos.com BlogIcon 권남 2009.07.06 15:35 PERM. MOD/DEL REPLY

    저기 HiddenHttpMethodFilter 랑 org.tuckey.web.filters.urlrewrite.UrlRewriteFilter 가 무슨상관인지 잘 모르겠어요... org.tuckey.web.filters.urlrewrite.UrlRewriteFilter 를 설정하면 자동으로 HiddenHttpMethodFilter가 적용되는건가요??

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.07.06 15:39 신고 PERM MOD/DEL

    UrlRewriteFilter랑은 아무 관계가 없는 것 같습니다. 다만 이 글을 올릴 때 제가 전에 못 봤었던 부분을 같이 긁어다 붙인것 같네요.

Write a comment.


스프링 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

  1. Favicon of http://toby.epril.com BlogIcon 2009.06.10 07:30 PERM. MOD/DEL REPLY

    DELETE, PUT이 브라우저와 서버에서 잘 먹나?

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.10 09:45 신고 PERM MOD/DEL

    아니요. 잘 안 되나봐요.
    HTML5에서는 DELETE랑 PUT도 잘 지원해줄꺼라는데 뭐 IE8에서나 잘 돌아갈 것 같고 그 전꺼에서는 잘 안 되겠죠.
    그래서 org.springframework.web.filter.HiddenHttpMethodFilter
    이 녀석을 제공해주나봐요.

    이걸 빠트렸네요.

Write a comment.


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

Write a comment.


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

Write a comment.


[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

Write a comment.


[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

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.08 18:02 PERM. MOD/DEL REPLY

    Spring ROO 도 AspectJ의 privileged Aspect을 사용해서 mixin을 만들었죠. :)
    근데 M1 릴리즈 직전에 스테판이 한 얘기가, 나눠놓은거 합친다는거 였는데,
    M1 사용해볼 시간이 없어서 어떻게 됐는지 모르겠네요.
    전에 나눠 놓았던거 합쳐졌나요??? @_@?
    그당시 그거 옵션으로 mixin 여러개로 쪼갤지 말지 결정할수 있게 해주면
    안 되냐고 얘기는 했었는데... 뭐 제 의견이 반영됐을리는 없겠고... :D

    암튼 AspectJ 쓰면 쓸수록 정말 맘에 드는 녀석입니다.
    물론 어플 덩치가 커지면서 관리해야할 Aspect 들이 늘어나면 쬐끔 주의를 해야겠죠.
    거기다가 AJDT가 Eclipse의 일반 Java Editor보다는 좀 기능면에서 딸리는거 같구요.
    STS 2.1.0M2의 AJDT 지원이 훨씬 좋아졌다는데... 제가 쓰는 plug-in 설치가
    안되는 바람에 못 쓰고, 아직 2.0.0 쓰고 있네요... ㅠ_ㅠ

    AspectJ5는 annotation을 쓰면 Java 5 이상 컴파일러로 컴파일도 되고
    POJO를 바로 Aspect으로 만들수 있어서 좋긴한데,
    privileged Aspect은 만들수가 없다는게 좀 안타깝죠.

    그나저나 여행 후유증으로 늘어진다고 하셨는데, 전혀 아닌거 같습니다. :)

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.08 18:16 신고 PERM MOD/DEL

    아니요. M1에서도 여전히 여러개 aj 파일로 나눠져 있어요. 흠.. 옵션을 사용하지 않아서 그런가.. 옵션이 뭔지 잘 몰라서 그런걸 수도 있겠네요.

    AspectJ에 대해 잘 아시는군요~ @_@

  2. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.06.10 20:07 PERM. MOD/DEL REPLY

    절대 잘 아는건 아니구요...^^;;;
    그냥 좋아하는거라서요. 사실 저는 AOP도 꽤 늦게 알게 됐는데,
    늦게배운 도둑질이 날새는줄 모른다고 참 재밌더라구요. :)
    그래도 필요한 부분에만 쓰다보니 조금 만지는 수준이죠 뭐... :)

    아직 나눠져 있네요. :D
    그때 제가 Real Separation of Concerns 라고 칭찬을 막 해서 그랬...을리는 절대 없고...^^;
    아무래도 출시 직전이다보니 새로 바꾸는거보다 안전하게 가는걸 택한 모양인거 같습니다.
    (순전히 추측...ㅡ_ㅡ;;; )

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.10 22:06 신고 PERM MOD/DEL

    오호 그렇군요. 저는 예전에 Hello World 예제만 몇 번 돌려보다가 요즘 들어서 쬐끔 만져봤는데, 아주 재밌네요. 스프링 AOP보다 편한 것 같다는 느낌도 들구요. 멋진 프로젝트 같아요.ㅋㅋ

Write a comment.


콜 오브 듀티(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

Write a comment.


이제 다시 복귀 할 시간

모하니?/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

  1. Favicon of http://blog.naver.com/j81811 BlogIcon aStRe 2009.06.07 14:53 PERM. MOD/DEL REPLY

    당신은 지금도 충분히 자상하고 좋은 남편입니다~~!!
    지금처럼 유지함이 훨씬 좋지 않을까요?
    물론.. 더 잘 챙겨주고 도와주면 고맙겠지만 말이죠~

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.06.07 18:21 PERM MOD/DEL

    넹 장모님 말씀대로 초심을 잃지 않도록 할께요.

  2. Favicon of http://blog.outsider.ne.kr BlogIcon Outsider 2009.06.08 11:55 PERM. MOD/DEL REPLY

    신혼여행 잘 다녀오셨나요.. ㅎ
    행복한 신혼되시고... ㅎ 앞으로도 좋은 정보 많이 얻어가겠습니다.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.08 17:25 신고 PERM MOD/DEL

    넵 좋은 정보라고 하니.. 제가 주식이라도 하는 것 같은 기분이 드네요.ㅋㅋㅋ

  3. Kevin 2009.06.08 14:18 PERM. MOD/DEL REPLY

    복귀 축하드립니다.
    신혼 여행 동안 즐거우셨을텐데 복귀하셨으니
    복귀를 축하드려야 하는건지 아닌지...^^;
    암튼 두분 행복한 모습이 보기 좋네요. :)
    오래오래 행복하시길 바라겠습니다.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.08 17:26 신고 PERM MOD/DEL

    넵.. 복귀는 괜찮은데..
    점점 노는게 두려워지네요.
    1주일 놀았을 뿐인데 막.. 늘어져요.ㅋ

    행복하게 살겠습니다.
    감사합니다~

  4. Favicon of http://yunsunghan.tistory.com BlogIcon Max 2009.06.10 14:09 PERM. MOD/DEL REPLY

    아.... 결혼했군요....

    이런, 중대한 사건을 모르고 있었네요...
    뒤늦게나마 축하 드립니다.
    늘 행복하세요. ^^*

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.10 17:44 PERM MOD/DEL

    앗 그것 이제 아시다니ㅋㅋㅋ
    감사합니다~ :)

Write a comment.


스프링 시큐리티 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

  1. Favicon of http://ryys1993.tistory.com BlogIcon 윤성철 2009.06.08 17:47 PERM. MOD/DEL REPLY

    으아 언제나 느끼는거지만 정말 대단하셔요~~

    도데체 저런 그림은 어떻게 그리시는건지~ ^^

    머리에 쏘옥 쏙 들어오네요 ^^.

    저런 그림은 뭘로 그리시는지요? ^^..

    그림판은 아닐텐데 ^^.. 혹시 이클립스 플러긴이 있는건가 흐헉..

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.06.08 18:17 신고 PERM MOD/DEL

    절대로 제가 그린게 아니구요.
    원문에 있던거 퍼온거에요.
    오해를 불러일으켜 죄송합니다~

    아마도 이클립스에서 무슨 플러긴으로 자동 생성한 그림이겠거니 추측해 봅니다.

  2. Favicon of http://ryys1993.tistory.com BlogIcon 윤성철 2009.06.09 12:44 PERM. MOD/DEL REPLY

    직접 그리시지 않으셨다 하여도.. 위 기사를 쉽게 접할수 있게 해주시는것에 대해

    매우 감사합니다.

    사실 영어 울렁증때문에 --;;; ㅋ....

    앞으로 영어 공부좀 해야할거같아요 ^^.

    오늘 날 무지 꿀꿀스럽지만

    즐거운 날 되세요~...

    Favicon of http://whiteship.me BlogIcon 기선 2009.06.09 14:03 PERM MOD/DEL

    넵 ㅎㅎㅎ
    시원하고 좋네요. 좋은 하루 되세요~

Write a comment.