Whiteship's Note

'2009/10'에 해당되는 글 46건

  1. 2009.10.31 개발 관련 Q&A 사이트 StackOverFlow!! (2)
  2. 2009.10.31 [하이버네이트] 컬럼 타입은 어떻게 명시하는게 좋을까?
  3. 2009.10.29 [IntelliJ] 답답한 것들 (10)
  4. 2009.10.29 [IntelliJ] 스프링 설정 파일에서 inline 리팩토링 사용하기
  5. 2009.10.29 구글 맵스 네비게이션 (4)
  6. 2009.10.29 [JRebel, IntelliJ] IntelliJ에 JRebel 플러그인 설치/설정/사용하기
  7. 2009.10.29 [봄싹] XP 적용 시나리오 3. 개발하기
  8. 2009.10.29 [봄싹] XP 적용 시나리오 2. 배포 계획하기
  9. 2009.10.29 [봄싹] XP 적용 시나리오 1. 스토리 만들기
  10. 2009.10.28 창의적인 일에 인센티브가 어떤 작용을 할까? (6)
  11. 2009.10.27 대천 해수욕장과 청계산 (6)
  12. 2009.10.27 [전자정부 개발프레임워크 교육자료] 표준 프레임워크 개요 PPT 감상문 (6)
  13. 2009.10.26 [IntelliJ] 이클립스 바이바이~~ 인텔리J 안녕!! (22)
  14. 2009.10.23 [봄싹] 모임 추가 시나리오 - web flow (구현)
  15. 2009.10.23 [봄싹] 모임 추가 시나리오 - web flow
  16. 2009.10.22 [Spring Wev Flow(2.0.8)] SecurityFlowExecutionListener 패치 for Spring Security 3.X
  17. 2009.10.21 [이직해야지] 들어가고 싶은 회사 Atlassian (4)
  18. 2009.10.21 [코드값] 애플리케이션에 DB id값이... (6)
  19. 2009.10.20 [봄싹] 메일 서비스를 알림 서비스로 통합하기
  20. 2009.10.20 [지름신 물럿거라] 누워서 노트북하기. Liadback (8)
  21. 2009.10.20 [리팩토링] 메일을 메시지로 통합하기
  22. 2009.10.19 [negative] 어른들 때문에 큰일이다. (6)
  23. 2009.10.18 [스프링 3.0] @Valid 실습
  24. 2009.10.18 [스프링 3.0] @Valid 이론
  25. 2009.10.16 [Spring Web Flow] booking 예제 분석 3 - webmvc-config.xml 설정
  26. 2009.10.16 [JUnit] @Rule TemporaryFolder 사용하기 (2)
  27. 2009.10.16 [DBUnit] 테이블 데이터를 엑셀로 export
  28. 2009.10.16 [ToDo] 20091016
  29. 2009.10.16 [JRebel] JRebel 설치 및 간단 사용법 (4)
  30. 2009.10.15 [JRebel] 오퀘!! 퍼스널 OSS 라이선스 득탬!! (4)

개발 관련 Q&A 사이트 StackOverFlow!!

Good Tools : 2009.10.31 23:13


http://stackoverflow.com/

사이트 이름부터가 굉장하지 않은가요. 일반적인 포럼 보다는 훨씬 재미있게 만든 사이트로 위키처럼 질문과 답변을 누구나 수정할 수 있고 밷지과 포임트로 보상을 해줍니다. ㅋㅋㅋ밷지 모으는 재미가 쏠쏠합니다. 질문에 대한 답변 채택과 추천/비추 등의 기능도 해당 질문에 대해 가장 적절한 답변을 바로 찾아 볼 수 있다는 점이 눈에 띄이며, 질문 자체에도 추천과 비추를 할 수 있다는 것이 특히 굳입니다. 사실.. 우문우답이라고, 좋은 질문을 해야 좋은 답변을 들을 수 있는게 당연한 이치죠.


이런식으로 질문에도 추천/비추 기능이 있는데, 저 질문은 3개의 추천을 받았군요.


이건, 제가 작성한 답변은데 4개의 추천을 받았고, 질문 작성자로부터 답변으로 채택을 받았습니다. 그리고 이 답변에 오타가 있어서 Robert가 수정을 해줬고, Jherico가 댓글로 중요한 추가사항을 첨부해 줬습니다.

하지만... 도무지 극복할 수 없는 단점이 하나 있는데..

바로 .. 영어라는 겁니다.

이 좋은 사이트에 한글로 의사표현을 할 수 없다는 것은 정말 괴로운 일입니다.

그래서 봄싹에서는 StackOverFlow를 본따 그와 비스무리 재밌게 만든, NullPE(NullPointerException)를 만들기로 결정했습니다. 다음 릴리즈(11월 30일)에는 포함되어 있지 않지만, 내년 초나 중순 중으로 NullPE를 오픈하겠습니다.

그렇다고해서 별도의 사이트는 아니고, 봄싹 사이트의 일부 모듈로 들어가게 될 겁니다. 지금 있는 사이트에 NullPE 메뉴만 하나 추가가 되겠죠.

흠.. 그러려면 어서 지금 정리중인 스터디쪽부터 마무리를 해야겠습니다. ㄱㄱ

ps: 사실 요즘같아서는 내가 부자여서 돈을 안 벌어도 된다면 직장도 그만두고 봄싹 사이트 개발에만 전념하고 싶습니다.
신고
top


[하이버네이트] 컬럼 타입은 어떻게 명시하는게 좋을까?

모하니?/Coding : 2009.10.31 22:30


       @Column(columnDefinition="TEXT")
       private String descr;

이런 방법이 있습니다. 별로 좋은 방법은 아닙니다. postgresql에서는 괜찮지만, HSQL에서는 저 TEXT라는 SQL 타입이  못해서 해당 테이블을 만들지 못할겁니다.

columnDefinition 이 속성 자체가 컬럼 만들 때 사용할 SQL을 입력하는 부분이기 때문에, 각기 다른 밴더 DB에서 못 인식하는 경우도 생길 수 있는거죠.

그래서 타입을 선언하고 싶을 때는 하이버네이트 타입을 선언할 수 있는 @Type을 사용하는 것이 좋겠습니다. 그러면 하이버네이트가 컬럼을 만들 때 @Type에 선언된 하이버네이트 타입을 보고 DB에 적당한 SQL을 이용해서 컬럼을 만들어 줄 것이기 때문이죠.

    @Column
    @Type(type = "text")
    private String descr;

그래서, 이렇게 하는게 좋겠습니다.



신고
top


[IntelliJ] 답답한 것들

Good Tools : 2009.10.29 19:44


1. 프로젝트 하나만 놓고 작업한다.

왜 한 번에 한 프로젝트 밖에 못 보는거지...
다른 프로젝트에서 코딩한것도 배껴야 하는데.. 그럴 때 마다 프로젝트를 열고 닫을 순 없자나. ㅠ.ㅠ

2. 서버 설정에 제약이 심하다.

WAR 배포도 하기 싫고, Exploded 배포도 하기 싫어 난 Inplace로 배포할꺼란 말이야..
이건 배포 경로를 어떻게 잘.. 설정해서 해결했습니다.

3. 단순한 단축키 시스템

이클립스처럼 알트 + 시프트 + X 누른담에 J, T 이런식으로 2단 단축키가 안 되는 것 같아요.
그래서 단축키가 부족하고, 계속 중복되고.. 아 짜증나. 중복되면 둘 다 실행을 해주던가..
뭐는 뭐에 우선되서 실행되고 뭐는 그냥 무시되고.. 단축키 누르다가 손가락 꼬이겠네..

4. 파일 시스템이 이상한건지..

왜 외부파일을 프로젝트에 복사해 넣을 수가 없는거지??
이건 뭔가 이상해.. 맥북에서는 잘 됐고, 아침에도 잘 됐었는데..
붙여넣기가 안먹는거야 뭐야..

아.. 답답해;. 이제 좀 쓸만한가 했더니.. 답답해 지기 시작하네..
그래도 이클립스보다 검색하는 것도 빠르고, 안정적인것 같긴한데... 뭔가 좀 부족하네;;
내가 잘 못쓰고 있는 걸지도...
신고
top


[IntelliJ] 스프링 설정 파일에서 inline 리팩토링 사용하기

Good Tools : 2009.10.29 16:18



빈설정이 여러개 있는데, 저 중에서 세 개는 다른 두 빈 설정 내부에 들어가도 됩니다. 개인적으로는 다른 빈에서 참조하는 빈이 아니라면 inner bean으로 설정하는게 보기 편합니다.

이때, 자바 코드에서처럼 inline refactoring을 사용할 수 있습니다. 제 환경에서는 단축키가 Alt + Shift + i 입니다.


흠.. 두 개의 핸들러 어댑터 설정이라는게 한 눈에 들어옵니다. 이 상태에서 하나 더, Alt + Enter를 사용해서 cacheSeconds 설정은 p 네임스페이스로 바꿀 수 있습니다.


참 쉽죠.


요렇게 됐습니다.

참, 덤으로 빈 의존 관계를 그래프로 볼 수 있는데, 이클립스에서보다 더 빠르게 볼 수 있고, 애노테이션으로 설정한 빈들도 전부 보여줍니다.


당연히 프린트도 가능하다는거~ 애노테이션으로 하면 설정이 한눈에 들어오네 안 들어오네 하면서 XML이 더 좋다고 주장하시는 분들이 있던데.. XML도 길어지면 어차피 한눈에 안들오죠. 저는 간단한 웹 플로우 설정도 한 눈에 안들어오던데. 어차피 둘 다 저런 비주얼 도구를 이용하면 한눈에 들어오는 A4 문서를 출력할 수 있으니 그런 논의는 이제 별 필요가 없는 것 같습니다.
신고
top


구글 맵스 네비게이션

Good Tools : 2009.10.29 15:13




이제는 안드로이드가 대세인가..

근데;; 운전하다가 인터넷이 끊기면 어떻게 되는거지..
신고
top


[JRebel, IntelliJ] IntelliJ에 JRebel 플러그인 설치/설정/사용하기

Good Tools : 2009.10.29 12:51


참조: http://www.zeroturnaround.com/intellij-idea-jrebel-tutorial-formerly-javarebel/

1. 플러그인 설치
- settings -> plugin -> available 탭에서 jre 검색 후 클릭해서 다운로드 설치 하고 재부팅.

2. 플러그인 설정.
- settings -> jrebel 에서 jrebel.jar 파일 찾아주기.
- settings -> Debugger -> Data View -> Show 탭에서 Synthetic fields 끄기
- 컴파일 output 폴더 변경
- 컴파일 이후에 웹 서버에 재배포 하지 않도록 설정하기
- 컴파일 단축키를 Ctrl+S로 설정하기
- rebel.xml 파일 만들기



3. 톰캣 Run with JRebel 실행하기


플러그인 설치까지는 수월했지만, 그 뒤에는 잡다하게 설정할 것들이 좀 있었네요. 하지만 한번만 설정해두면 두루 두루 좋은거니깐~

신고
top


[봄싹] XP 적용 시나리오 3. 개발하기

모하니?/Coding : 2009.10.29 00:57


본격적으로 개발을 해야하는데, 봄싹에서는 오프라인에서 페어로 작업을 해보기도 했지만, 그렇게 자주 충분히 페어 프로그래밍을 했다고 볼 수는 없습니다. 앞으로도 좀 더 꾸준히 시도를 해봐야 그 효용이나 장단점을 파악할 수 있을 것 같습니다. 개인적으로는 뭔가 대화를 나누면서 코딩할 상대가 있어서 안심이 되긴 합니다. DB update 쿼리가 어떻게 되더라? 이거 무슨 리팩토링이지? 이 메서드 이름 맘에 들어? 여기 중복인데 어떻게 제거하면 좋을지 잘 모르겠네.. 같은 식으로 대화를 나눌 수 상대와 함께라면 좋치 않겠어요?


먼저 개발을 진행하기에 앞서 구현하려는 기능을 한 번도 만들어 본적이 없다면, 어느 정도 자신있게 개발을 진행할만큼의 학습이 필요합니다. 그 과정을 파일럿이고 표현했는데, XP 책에서도 파일럿이라고 헀었는지 잘 모르겠습니다. (뭐라고 했는지 찾아보려고 다시 살펴 봤는데 못 찾아서 그냥 썼습니다.)

그다음 과정은 좀 특이하게 바로 개발을 진행하지 않고, 인수 테스트를 만듭니다. 고객이 해당 작업이 완료 됐다는 것을 확인할 수 있는 모종의 장치를 마련하는 것이죠. 고객이 코드를 볼 수 있다면 아주 행복할텐데, 봄싹은 다행히(?) 고객이 전부 개발자 입니다. 굳이 엑셀로 이쁜 포맷을 만들고, 테스트에서 엑셀 로딩해서, 결과를 엑셀에 다시 찍어주고, 고객은 엑셀에서 수식 비교로 해당 테스트가 잘 됐나 안 됐나 확인하는 귀찮은 일은 할 필요가 없습니다. 그렇치만, 인수 테스트 코드가 고객이 원하는 시나리오를 제대로 표현해주지 못하거나, 고객이 개발자인데도 테스트 코드를 읽기가 난해하다면 테스트를 수정해야겠죠.

그다음은 페어 프로그래밍과 TDD로 해당 작업을 구현하는 일입니다. 페어 프로그래밍은 사실 오프라인에서 만났을 때의 얘기지 주중 저녁이나 회사에서 틈틈히 코딩을 하는 봄싹 개발자에데는 다소 난해한 일입니다. 그래도 메신저등을 통해서 의견은 주고 받을 수 있으니 그것도 페어 프로그래밍으로 치도록 하죠.

그렇게헤서 작업이 끝나면, 담당자 두 명은 자신들이 예상했던 난이도와 시간에 비해 실제로는 난이도가 어땠으며 실제로 소요된 시간은 어느정도인지 기록합니다. 고객은 해당 작업 결과를 본 뒤 간략한 피드백을 줍니다. "담부턴 더 빨리 만들어 주세요" 라던지.. "참 잘했어요" 라던지 ㅋ

신고
top


[봄싹] XP 적용 시나리오 2. 배포 계획하기

모하니?/Coding : 2009.10.29 00:43


봄싹이 언제 배포됐었더라... 기억이 가물가물 합니다. 8월 30일이었나. 이제 두 달을 향해가고 있군요. 다음 배포 일정은 임의로 11월 30일로 잡아두었습니다. 세 달은 지나치게 긴 것 같습니다. 이미 봄싹 개발자들끼리는 새로운 UI를 만끽하고 있는데 봄싹 회원들에게 멋진 UI를 빨리 보여드리지 못하는게 아쉽습니다. 배포 일정과 스코프를 잘못 계획했기 때문입니다. UI 개선만을 1차 유지보수 스코프로 정하고 배포했다면 이미 봄싹 회원들은 멋진 UI를 감상하고 계실텐데 말이죠...


먼저 배포 일정을 잡은 다음, 모든 개발자들의 측정이 완료된 스토리 카드 중에서 해당 일정 안에 개발을 할 수 있을 것으로 보이는 스토리 카드들을 고릅니다. 이 일은 전적으로 고객이 합니다. 고객 생각에 어떤 것이 가장 필요하고 비즈니스에 도움이 되는지 생각해서 고르면 되겠죠. 일단 최대한 빨리 중요한 것부터 서비스하고 싶다면 다소 어렵고 일정이 긴 스토리 하나만 고를 수 있겠고, 중요한 건 나중에 공개하고 일단은 기반이 되는 소소한 것들 부터 서비스하고 싶다면, 쉽고, 개발 일정이 짧게 걸릴 것으로 측정된 것들을 고르면 되겠습니다.

그런다음, 하나의 스토리를 구현하는데 필요한 세부 작업들을 개발자들과 논의합니다.

"발표 도메인 객체를 추가해야겠습니다."
"발표와 모임 객체에 연관 관계를 설정해야 겠어요."
"발표에 댓글/첨부자료/발표자 정보가 필요하겠군요."
"새로운 모임을 추가할 때 아예 발표 정보도 추가할 수 있도록 하죠."
"스프링 웹 플로우 학습이 필요할 것 같습니다."
"발표정보 추가할 때 발표자를 선택하는 부분에서는 Ajax를 도입할 수도 있겠네요."
...

이런식으로 하나의 스토리를 구현하는데 필요한 세부적인 작업목록들을 만들어 나갑니다. 이때, 개발자들이 의견을 많이 주어야 하며, 작업 목록 작성은 고객이 담당합니다.

고객이 작업 목록 하나를 작성할 때 마다, 스토리를 등록했을 때 처럼, 평가하는 과정을 거칩니다. 해당 작업은 덩어리가 너무 크다거나, 좀 더 명시적으로 수정해 달라거나 의견을 제시하면서 투표를 할 수 있죠. 혹은 고객이 명시한 작업을 코딩할 수 있겠다는 판단이 들면, 난이도, 걸리는 시간, 같이 작업하고 싶은 사람을 적어서 측정해줍니다.

고객은 가장 낮은 난이도를 제시한 개발자, 가장 낮은 시간을 제시한 개발자, 가장 많은 지명도를 가진 개발자 등의 정보 통계를 보고 해당 작업의 적입자를 찾아서 작업 담당자와 그 파트너를 지정해줍니다. 이렇게해서 하나의 작업에 두 명의 담당자를 지정해 주는겁니다.

그럼 이제 그 두명의 담당자는 서로 의견을 나눠가며 개발을 진행하면 되겠죠!

다음은 개발 과정에 대해 생각해보죠.
신고
top


[봄싹] XP 적용 시나리오 1. 스토리 만들기

모하니?/Coding : 2009.10.29 00:21


봄싹 프로젝트 개발이 지나치게 자유롭다 보니 프로젝트 스코프도 애매해지기 시작했고, 현재 누가 어디를 얼만큼 개발했는지 파악하기가 힘들어졌습니다. 나름대로, 이슈트래커, CI 환경, VCS까지 갖출 건 다 갖추고 진행하고 있지만 그래도 뭔가 좀 부족한 감이 없지 않습니다. 그래서 XP 책에서 읽은 내용을 토대로 봄싹 나름대로의 개발 프로세스를 정리해볼까 합니다.

물론, XP installed 책에 나와있는 그대로 할 필요도 없고, 그 책도 일종의 권고사항이지 반드시 따라야 하는 건 아니것 같기 때문에 제 맘대로 봄싹에 필요하면서도 재미있게 개발을 진행할 수 있는 방법을 궁리해봤습니다. xp 책은 그런면에서 좋은 아이디어를 떠올리는데 아주 좋더군요!


먼저, 현재 이슈트래커나, 구글 그룹에 올리고 있는 할 일을 스토리로 정리하는 방법을 생각해봤습니다. 누군가가 고객의 입장에서 스토리를 하나 만듭니다.

"모임에서 있었던 발표 정보를 별도로 관리하면 좋겠다."

이런 카드를 만드는 순간, 그 사람은 고객이 됩니다. 그리고 해당 스토리를 수정/삭제/세부 스토리 등록 등을 하는 권한이 생기죠. 그리고 이 카드가 생기는 순간 모든 개발자에게 메시지가 갑니다. 평가해 달라고..

그럼 일부의 개발자들은 해당 스토리가 너무 애매하다고 더 구체적으로 설명해 달라고 "고치자"를 클릭하며 어떻게 고쳐달라며 '의견'을 제시합니다.

또 다른 일부의 개발자들은 해당 스토리가 너무 방대하다면 좀 더 세부적으로 쪼개 달라며 "나누자"를 클릭하고 어떻게 나누면 좋겠는지 '의견'을 제시합니다.

또 다른 일부의 개발자들은 현재 스토리를 개발할 수 있다는 판단하게 '얼마나 걸릴지', '난이도가 어느정도인지', '누구와 함께하고 싶은지' "측정"을 합니다.

모든 개발자들의 측정이 이루어지기 전까지 사용자는 개발자들의 의견을 반영하여 계속해서 스토리를 수정하거나, 스토리의 하위 스토리를 등록하게 되고, 개발자는 스토리가 변경되거나, 새로운 스터디가 추가될 때마다 계속해서 측정 또는 투표를 할 수 있습니다.

이런식으로 스토리를 정제해 나가는 겁니다. 재밌지 않을까요? JIRA를 어떻게 잘 설정해서 쓰면 이렇게 할 수 있을것도 같긴 한데.. 좀.. 복잡해 보이는 UI가 위 시나리오에 적당해 보이지는 않습니다. 적당한 툴이 있으면 좋겠는데 없으면 봄싹 이슈트래커도 나중에 만들어야겠네요.

다음은 이렇게 정리된 스토리들을 가지고 배포 계획을 세우는 방법에 대해 생각해보겠습니다.

신고
top


창의적인 일에 인센티브가 어떤 작용을 할까?

모하니?/Thinking : 2009.10.28 17:31



재미있군요. 자막을 클릭하시면 한글 자막으로도 보실 수 있습니다.
신고
top


대천 해수욕장과 청계산



지난 주말 바쁘게 다녀왔습니다. 토요일은 대천 해수욕장 일요일은 청계산에 다녀왔습니다. 대천 해수욕장은 과외 학생이 시험을 잘 봤기 때문에(전교 1등) 보상으로 바다에 다녀왔습니다. 재밌는 건 그 학생은 인천에 사는 학생이라는거죠. 바다 코앞에 사는 녀석이 왜 바다가 보고 싶다고 하는건지.. 원.. @_@


뭐 덕분에 저희는 핑계삼아 잘 다녀왔습니다.

일요일은 원래 장모님. 장인어른과 함께 설악산에 가려고 했으나, 토요일에 너무 늦게 도착한데다 설악산에 사람이 붐빈다고해서 가까운 청계산으로 다녀왔습니다.


산에 가니까 공기가 확실히 좋더군요. 여러 가지를 느끼고 왔는데, 우선은 몸이 많이 무거워졌다는 것. 오랜만에 산에 간탓도 있지만, 몸이 확실히 많이 불었나 봅니다. 예전엔 그렇게 힘들게 끌고 올라가진 않았었는데 말이죠. 그리고 장모님은 날아다니신다는 것. 운동을 자주 하신다지만~ 이건 뭐.. 차원이 다르더군요. 결국은 정상 근처까지(꼭대기 바위에는 사람이 붐벼서 포기) 올라갔습니다.

능력이 좀 부족해서 힘들더라도 계속 하다보면 결국은 할 수 있더군요. 뭐 세상사가 이번 등산처럼 단순하진 않겠지만 말이죠.


신고
top


[전자정부 개발프레임워크 교육자료] 표준 프레임워크 개요 PPT 감상문

Spring/etc : 2009.10.27 17:45


6 페이지

개발프레임워크는 단위컴포넌트, 래퍼(Wrapper), 정적인아키텍처, 동적인아키텍처로 구성되어 있으며, 일반적으로 단위 컴포넌트는 오픈소스나 상용 솔루션을 도입함.

음.. 그렇구나. 개발 프레임워크는 네 가지로 구성 되는 구나. 근데 도통 모르겠네, 래퍼는 대충 뭔지 알겠는데, 단위 컴포넌트, 정적 아키텍처, 동적 아키텍처는 뭐야.

단위 컴포넌트는 특정한 기술적/업무적 목적을 지원하는 블라브라.. 아 몰라 몰라;;
정적 아키텍처는 객체들간의 연관 관계를 다루고 동적 아키텍처는 객체들간의 상호작용을 다룬다고??? 이게 뭔소리야!!!!! 아 놔.. @_@... 패스

58 페이지(MVC)

// 업무로직은 controller가 담당, 여러 View에서 재사용.

으응??? 진짜????? 컨트롤러가 담당한다고? ㅋㅋㅋㅋㅋ 여러 뷰에서 뭘 재사용해.. 뭘.. 컨트롤러를?? 흠..;; 이것 참...

 59 페이지(ORM)

일반적인 개발
매핑 적용: 테이블 컬럼과 자바 클래스간의 직접적인 매핑 필요
유연성: SQL 변경시 코드를 직접 변경하고 배포해야 함
표준 패턴: 패턴 없음
DB 제어: 직접적으로 DB제어 가능
활용 기술: SQL

ORM 구축
매핑 적용: 개발자는 직접 객체지향 관점에서 처리 가능
유연성: 매핑 정보의 수정만으로 적용 가능
표준 패턴: 매핑 정보, XML 등이 템플릿 형태로 적용됨
DB 제어: Mapping의 경우 직접적인 제어가 어려울 수 있음
활용 기술: Persistence 프레임워크 (Hibernate, iBatis)

이야.. ORM 좋네. 매핑 정보만 수정하면 배포 안 해도 되는거야?? 대신 DB 제어는 직접적으로(?) 못해?? 진짜?? ORM도 SQL 쓰지 안쓰나.. 흠.. 희한하네..

61 페이지(DI)

DI(Dependency Injection)는 컴포넌트 간 연관성 및 참조 정보를 직접적으로 코드 내에서 가지는 것이 아니라, 외부 파일 등에 연관 정보를 설정하고, WAS가 기동 하는 Run-Time시에 연관 정보를 가지도록 하는 것임

일반적인 개발
컴포넌트: EJB 컴포넌트 활용 (무겁고 복잡함)
유연성: 참조 변경이나 컴포넌트 재사용시 어려움 (컴포넌트 내부 코드 변경 필요)
표준 패턴: 패턴 없음
연관 제어: 연관성 파악이 어려움 (전체 코드 분석 필요)
활용 기술: EJB

DI 구축
컴포넌트: 순수 자바 코드 (POJO) 활용하여 컴포넌트 구현 Lightweight Component 체계
유연성: 쉽게 참조 변경 가능하고 재사용 가능 (컴포넌트 내부 코드 변경 불필요)
표준 패턴: 매핑 정보, XML 등이 템플릿 형태로 적용됨
연관 제어: 설정 파일 만으로 연관성 파악 가능
활용 기술: Component 프레임워크 (Spring, HiveMind)

DI하기 참~~ 어렵구만, WAS가 있어야 돼. 젠장...

근데, 정작 중요한 "테스트"에 대한 얘기는 거의 없네.. "테스트 도구" 얘기만 있고.. 테스트는 다른 PPT에서 다루나.

뭐 일단은 재밌네!! 캬캬캬.

다른 것도 봐야지
신고
top


[IntelliJ] 이클립스 바이바이~~ 인텔리J 안녕!!

Good Tools : 2009.10.26 18:35


와.. 대박!! 인텔리J 완전 좋아요!!

단축키는 이클립스 단축키로 바꾸고, 기본적인 설정 몇 가지를 변경하면 이클립스에서 인텔리J로 후딱 갈아탈 수 있습니다. 물론, 100%까진 아니지만, 어느정도 개발이 가능한 정도까지.. 대충 70% 가량은 인텔리J로 개발환경을 바꿀 수 있습니다. 그담부터는 인텔리J의 고유 기능들과 단축키를 만끽하는 일만 남은거죠.


설치 후 한 일

1. settings에서 keyset 변경
- eclipse 키셋을 카피 한 다음 원하는 단축키 설정.
  - close(Ctrl + W)
  - declaration(Ctrl + 마우스 버튼1)
  - goto test(Ctrl + J)

2. settings에서 코드 라인수 보이도록 설정.
- code line으로 검색 후 apperance에서 체크.

3. settings에서 수정 후 저장하지 않은 파일 표시 되도록 설정.
- mark modi로 검색 후 Editor Tabs에서 체크.

4. settings에서 폰트 변경
- font로 검색 후 블라 블라.

5. 서버 추가 및 실행
- settings의 Application Servers에서 추가.
- 서버 설정 뷰에서 war/폴더 배포 설정 가능.

6. 기본 최대 메모리 512로 늘리기
- IDEA 설치 폴더/bin/idea.exe.vmoptions 편집 -Xmx512m로 수정
- code inspection 실행 할 때 빼곤 거의 기본 350m 정도면 충분해 보임.

7. 폰트에 안티 얼라이징 효과 주기
- IDEA 설치 폴더/bin/idea.exe.vmoptions 편집 -Dawt.useSystemAAFontSettings=lcd 추가

놀란 기능

1. Git/SVN 및 다양한 VCS 그냥 지원.

2. UML 그냥 지원.

3. MoreUnit 기능 그냥 지원.

그밖에도 아직 해보지 않은 환상적인 기능들이 무궁 무진 함.

충분히 갈아탈만한 개발 툴로 보입니다. 갈아타실 수 있는 분들은 갈아타자구요.
사용하시는 분들이 많아야 소소한 팁을 나누면서 좀 더 잘 써먹죠.ㅋ


신고
top

TAG IntelliJ

[봄싹] 모임 추가 시나리오 - web flow (구현)

모하니?/Coding : 2009.10.23 17:57


<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

    <secured attributes="ROLE_MEMBER" />
   
    <input name="studyId" required="true" type="java.lang.Integer" />
   
    <on-start>
        <evaluate expression="meetingService.createMeeting(studyId)" result="flowScope.meeting" />
    </on-start>
   
    <view-state id="addMeetingForm" model="meeting" view="add.jsp">
        <binder>
            <binding property="openDate" converter="shortDate" required="true" />
            <binding property="closeDate" converter="shortDate" required="true" />
            <binding property="openTime" converter="shortTime" required="true" />
            <binding property="closeTime" converter="shortTime" required="true" />
            <binding property="title" required="true" />
            <binding property="maximum" required="true" />
            <binding property="location" required="true" />
            <binding property="contents" required="true" />
        </binder>
        <transition on="proceed" to="addPresentationForm" />
        <transition on="submit" to="confrimMeetingDetail" />
        <transition on="cancel" to="cancel" bind="false" validate="false" />
    </view-state>
   
    <view-state id="addPresentationForm" model="presentation" view="presentation/add.jsp">
        <binder>
            <binding property="key" required="true" />
            <binding property="title" required="true"/>
            <binding property="topic" required="true"/>
            <binding property="summary" required="true"/>
            <binding property="presenter" converter="memberConverter"/>
        </binder>
        <on-render>
            <evaluate expression="meetingService.createPresentation(meeting)" result="viewScope.presentation"/>
        </on-render>
        <transition on="proceed" to="presentationList" history="discard">
            <evaluate expression="meetingService.addPresentation(meeting, presentation)"/>
        </transition>
        <transition on="cancel" to="cancel" bind="false" validate="false" />
    </view-state>

    <view-state id="presentationList" view="presentation/list.jsp">
        <transition on="delete" to="presentationList">
            <set name="requestScope.presentationKey" value="requestParameters.presentationKey" />
            <evaluate expression="meetingService.deletePresentation(meeting, presentationKey)" />
        </transition>
        <transition on="new" to="addPresentationForm" />
        <transition on="submit" to="confrimMeetingDetail" />
        <transition on="cancel" to="cancel" bind="false" validate="false" />
    </view-state>

    <view-state id="confrimMeetingDetail" view="confirmMeeting.jsp">
        <transition on="submit" to="submit" />
        <transition on="cancel" to="cancel" bind="false" validate="false" />
    </view-state>
   
    <end-state id="meetingEnd" view="externalRedirect:contextRelative:/study/view/${studyId}.do" />
   
    <end-state id="submit" commit="true" parent="#meetingEnd">
        <on-entry>
            <evaluate expression="meetingService.addMeeting(studyId, meeting)"/>       
        </on-entry>
    </end-state>

    <end-state id="cancel" parent="#meetingEnd" />

</flow>

일단, 이 플로우로 진입하면, addMeetingForm 뷰로 이동, 여기서 나온 transition에 따라 contirmMeetingDetail로 바로 가거나, addPresentationForm으로 이동.

addPresentationForm에서 presentationList로 이동하고, 여기서는 back할 수 없도록 history를 discard로 설정함.

대략 80 줄 정도의 XML 설정으로 아침에 구상한 플로우를 구현했습니다. 이 시나리오를 구현하는데 필요한 자바 코드는 서비스 메서드 몇 개 정도. 컨트롤러 코드는 하나도 없습니다. 만약 웹 플로우 없이, 스프링 MVC만을 이용해서 비슷한 플로우를 구현했다면 훨씬 복잡하고 코드도 길었을 텐데 다행입니다. 웹 플로우 사용법도 생각보다 간편하고 쉬웠던 것 같네요.

이제는 웹 플로우 테스트와 <persistent-context />에 대해 좀 알아봐야겠습니다.

신고
top


[봄싹] 모임 추가 시나리오 - web flow

모하니?/Coding : 2009.10.23 11:49


모임을 추가할 때, 해당 모임에 있을 발표에 관한 정보도 추가하도록 웹 플로우를 구상했습니다. 단순 폼 처리보다 훨씬 복잡해질 수 있어서, 스프링 웹 플로우를 도입하기로 했죠. 그런데 막상 기본적인 사용법을 보고나니, 시나리오가 정해지지 않으면 개발을 진행하지 못하겠더군요. 그래서 구상에 들어갔습니다.

모임을 추가하고나서, 발표를 추가해야지.. 발표가 하나만 있는건 아니자나.. 그럼 한 화면에서 여러개를 추가할까?? 에이.. 뭔가 좀 거시기 하네.. 그럼 발표를 하나 추가하고, 발표 목록을 본 다음에 다시 하나 더 추가하고 이렇게 할까?


그래서 그린게 위와 같은 그림입니다. 그런데, 발표자가 확정되지 않았거나, 특정 발표 없이 진행되는 모임이라면 어찌할까? 그때도 무조건 발표 입력 폼을 들려야 하나.. 파란 부분을 서브 플로우로 묶었을 때, MeetingDetial에서 PresentationList로 back 할 수 있을까?  흠 뭐. 일단은 된다는 가정하에.

발표 정보가 한 개 있다면, MF -> PF -> PL -> MD
발표 정보가 두 개 있다면, MF -> PF -> PL -> PF -> PL -> MD
발표 정보가 없다면, MF -> PF -> PL -> MD

이상해.. @_@ 발표정보가 없는데 발표 폼이랑 발표 리스트를 들려야 하다니... 만약에 발표 폼 바인딩에 validation을 해야 하는 상황이면, 발표가 한 개있을 떄랑 발표가 없을 떄를 어떻게 구분한담... @_@ 안돼 안돼..

흠.. 그럼 PresentationList를 먼저 보여주면 어찌 될까나? 그려보지뭐..


발표 정보가 한 개 있다면, MF -> PL -> PF -> PL -> MD
발표 정보가 두 개 있다면, MF -> PL -> PF -> PL -> PF -> PL -> MD
발표 정보가 없다면, MF -> PL -> MD

아... 아닌데, 발표도 없는데 PL을 들릴 필요는 없자나..
그리고 발표를 추가할껀데, PL 부터 들리는 것도 불편하고.
이건 뭐.. 이전 보다 오히려 들려야 하는 폼 수가 늘어났자나.. @_@;;

0. 다시 PF가 진입점이 되도록 수정.
1. MF에서 MD로 바로 갈 수 있는 transition 추가.
2. MD에서 PL로 진입할 수 있는지 확인.(진입할 수 없다면, 서브 플로우 포기)


보자...

발표가 한 개 일 때, MF -> PF -> PL -> MD
발표가 두 개 일 때, MF -> PF -> PL -> PF -> PL -> MD
발표가 없을 때, MF -> MD

오퀘.. 이렇게 가야겠군!!!

신고
top


[Spring Wev Flow(2.0.8)] SecurityFlowExecutionListener 패치 for Spring Security 3.X

모하니?/Coding : 2009.10.22 17:35


스프링 시큐리티 3.0 RC1이 나온지가 언젠데 스프링 웹 플로우는 아직도 시큐리티 2점대 기준이더군요. 스프링 웹 플로우 때문에 시큐리티 버전을 낮출수도 없는 노릇이고, 안 돌아가는 클래스 소스를 가져다 스프링 시큐리티 3.X에서 돌아가도록 수정했습니다.

웹 플로우 2.X는 아직 자바 5 기능을 도입하지 않았더군요. 스프링 플젝만 자바 5 기준으로 변경한건지.. 흠.. 그래서 고치는 김에 자바5 Generic을 도입해서 타입 세이프티를 보장하게 코드를 아주 약간만 손 봤습니다.

필요하신 분은 쓰세요~

/*
 * Copyright 2004-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package springsprout.common.webflow;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.EnterStateVetoException;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.security.SecurityRule;

/**
 * Flow security integration with Spring Security
 *
 * @author Scott Andrews
 * @author Keesun Baik(Whiteship)
 */
public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter {

    private AccessDecisionManager accessDecisionManager;

    /**
     * Get the access decision manager that makes flow authorization decisions.
     * @return the decision manager
     */
    public AccessDecisionManager getAccessDecisionManager() {
        return accessDecisionManager;
    }

    /**
     * Set the access decision manager that makes flow authorization decisions.
     * @param accessDecisionManager the decision manager to user
     */
    public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
        this.accessDecisionManager = accessDecisionManager;
    }

    public void sessionCreating(RequestContext context, FlowDefinition definition) {
        SecurityRule rule = (SecurityRule) definition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, definition);
        }
    }

    public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException {
        SecurityRule rule = (SecurityRule) state.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, state);
        }
    }

    public void transitionExecuting(RequestContext context, TransitionDefinition transition) {
        SecurityRule rule = (SecurityRule) transition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
        if (rule != null) {
            decide(rule, transition);
        }
    }

    /**
     * Performs a Spring Security authorization decision. Decision will use the provided AccessDecisionManager. If no
     * AccessDecisionManager is provided a role based manager will be selected according to the comparison type of the
     * rule.
     * @param rule the rule to base the decision
     * @param object the execution listener phase
     */
    protected void decide(SecurityRule rule, Object object) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        List<ConfigAttribute> configAttrs = getConfigAttributes(rule);
        if (accessDecisionManager != null) {
            accessDecisionManager.decide(authentication, object, configAttrs);
        } else {
            AbstractAccessDecisionManager abstractAccessDecisionManager;
            List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
            voters.add(new RoleVoter());
            if (rule.getComparisonType() == SecurityRule.COMPARISON_ANY) {
                abstractAccessDecisionManager = new AffirmativeBased();
            } else if (rule.getComparisonType() == SecurityRule.COMPARISON_ALL) {
                abstractAccessDecisionManager = new UnanimousBased();
            } else {
                throw new IllegalStateException("Unknown SecurityRule match type: " + rule.getComparisonType());
            }
            abstractAccessDecisionManager.setDecisionVoters(voters);
            abstractAccessDecisionManager.decide(authentication, object, configAttrs);
        }
    }

    /**
     * Convert SecurityRule into a form understood by Spring Security
     * @param rule the rule to convert
     * @return list of ConfigAttributes for Spring Security
     */
    protected List<ConfigAttribute> getConfigAttributes(SecurityRule rule) {
        List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
        Iterator<String> attributeIt = rule.getAttributes().iterator();
        while (attributeIt.hasNext()) {
            configAttributes.add(new SecurityConfig(attributeIt.next()));
        }
        return configAttributes;
    }
}

ps: 스프링 소스 직원님들.. 웹 플로우도 빨랑 3.0으로 올려줘요. 한 달에 이슈 한 개 처리하는건 너무 심한거 아니삼..??
신고
top


[이직해야지] 들어가고 싶은 회사 Atlassian

모하니?/Thinking : 2009.10.21 16:13


http://seek.com.au/job/senior-java-developers/sydney-inner/16089383/88/1/



컨플루언스(위키), 지라(이슈트래커), 밤부(CI 툴), 클로버(커버리지 툴), 피쉬아이(소스코드 뷰어)를 만들고 가꾸고 있는 회사입니다. 하나같이 멋진 제품 들이죠. 최신버전 JIRA에는 Agile에서 사용하는 카드 형태의 이슈 관리도 지원하는 것 같은데... 참.. 발전이 끊이질 않네요.

열공해서 꼭 이직 해야겠습니다.

파이팅!!
신고
top


[코드값] 애플리케이션에 DB id값이...

모하니?/Coding : 2009.10.21 14:57


c.add(Restrictions.eq("itemStatus", 5));

이게 뭔지 아시겠나요.. 5는 DB에 있는 id 값입니다.

이 값은 시스템 마다 달라질텐데... 저렇게 애플리케이션 코드에...
테이블의 특정 id값을 가지고 조회를 해도 되는건지....
뭔가.. 좀.. 아닌 것 같다는 느낌이 강하게 듭니다.

물론, 코드 데이터가 기본 데이터성이니까...
모든 시스템 마다 기본 데이터 셋을 정해서 그걸 올려놓고 쓰면 상관은 없겠지만..
그래도 모르는거죠.

기본 데이터로 올리지 않고 나중에 추가한 코드값을 가지고 DB에서 뭔가 조회를 해야 한다...
그럴 때도 위와 같은 코드가 생길텐데..

어찌해야 할지 몰라서 그냥 저렇게 놔뒀습니다. @_@;;
아.. 괴롭네요. 괴로워..
신고
top


[봄싹] 메일 서비스를 알림 서비스로 통합하기

모하니?/Coding : 2009.10.20 21:23




알림 서비스는 현재 구글 토크와 트위터에 스터디와 모임 소식을 전달해주고 있습니다. 그런데 이전에 만든 메일 서비스가 이 것과 굉장히 비슷한 구조였습니다.


녹색은 스프링에서 제공하는 라이브러리이고, 파란색 계열이 봄싹 코드입니다. 보시면 알림 서비스와 거의 같은 구조지만, 사용하는 클래스가 조금 다르고, SendMailService가 구현하는 인터페이스가 없다는 것만 다를 뿐입니다.

이 두 개의 서비스를 사용하는 NotificationAspect를 보면 통합의 필요성을 더 확실하게 느낄 수 있습니다.

@Aspect
public class NotificationAspect {
  
    @Autowired SendMailService sendMailService;
    @Autowired JabberService messangerService;
    @Autowired TwitterService twitterService;
    @Autowired MemberService memberService;
...
    @AfterReturning(pointcut = "addStudyPointcut() && args(study)", argNames="study")
    public void sendMailAfterAddStudy(Study study){
        sendMailService.sendMail(new StudyMail(study, StudyStatus.OPEN, memberService.getMemberList()));
        StudyMessage msg = new StudyMessage(study, StudyStatus.OPEN, memberService.getMemberList());
        messangerService.sendMessage(msg);
        twitterService.sendMessage(msg);
    }
...

    @AfterReturning(pointcut = "addMeetingPointcut() && args(study, meeting)", argNames="study, meeting")
    public void sendMailAfterAddMeeting(Study study, Meeting meeting){
        sendMailService.sendMail(new MeetingMail(study, meeting, MeetingStatus.OPEN));
        MeetingMessage msg = new MeetingMessage(study, meeting, MeetingStatus.OPEN);
        messangerService.sendMessage(msg);
        twitterService.sendMessage(msg);
    }
}

sendMailService도 다른 알림 서비스와 같은 인터페이스를 구현하고, SpringSproutMessage 타입의 객체를 받아서 사용할 수 있다면. 어떤 일이 벌어질지 보이시나요?

저에게는 콜렉션이 보이고, 애스팩트의 여러 어브다이스에서 연달아 메서드를 호출해주는 구조의 중복을 제거할 수 있어 보입니다.

그래서 통합했습니다.


메시지쪽이 좀 까다로웠지만, 상속을 이용해서 나름 열심히 작업했습니다.

결과는 어떨까요?
1. 메시지 작성이 간편해졌습니다.
2. NotificationAspect가 다이어트를 했습니다.

원했던 성과를 거뒀습니다.

    <util:list id="notificationServices">
        <value>sendMailService</value>
        <value>jabberService</value>
        <value>twitterService</value>
    </util:list>

이렇게 빈 설정을 해 놓고..

@Aspect
public class NotificationAspect {
  
    @Autowired List<NotificationService> notificationServices;
    @Autowired MemberService memberService;

...

    @AfterReturning(pointcut = "addStudyPointcut() && args(study)", argNames="study")
    public void sendMailAfterAddStudy(Study study){
        sendMsg(new StudyMailMessage(study, StudyStatus.OPEN, memberService.getMemberList()));
    }

    @AfterReturning(pointcut = "addMeetingPointcut() && args(study, meeting)", argNames="study, meeting")
    public void sendMailAfterAddMeeting(Study study, Meeting meeting){
        sendMsg(new MeetingMailMessage(study, meeting, MeetingStatus.OPEN));
    }

    private void sendMsg(SpringSproutMessage msg) {
        for(NotificationService service : notificationServices)
            service.sendMessage(msg);
    }

...
}

NotificationAspect 코드를 이렇게 리팩토링 했습니다.

캬~~~ 엄청난 대규모 리팩토링이었는데 생각했던대로 되서 다행입니다. 이 작업을 진행하는 내내 테스트가 충분하지 않아서 굉장히 불안했었는데, 그래도 최소한의 테스트가 마지막 검증을 하는데 도움이 됐습니다. 역시.. 테스트는 있어야 합니다. 매우 필요한 존재에요;

다음 과제는 모임 등록시 SWF 적용하기!!

신고
top


[지름신 물럿거라] 누워서 노트북하기. Liadback



오랜만에 찾아온 지름신. 제품은 100달러인데 배송비가 40달러구나. 너무한거 아니니...

http://cgi.ebay.com/Laptop-Laidback-Table-portable-ergonomic-computing_W0QQitemZ280352012044QQcmdZViewItemQQptZLH_DefaultDomain_0?hash=item414648370c

침대에 누워서 책을 보거나 노트북을 하면, 목이 아프거나, 허리가 아프거나, 무릎이 뜨겁고, 팔이 아프고,
침대에 엎드려서 하면 목 아프고 어깨가 결리죠.



그래서 찾다가 이녀석을 발견했지만, 배송비가 너무 비싸서 포기~.

이것보다 좀 더 가볍고 미려하게 생겼으면서 가격도 싸고 쓸만해 보이는게 있으면 좋겠습니다.
신고
top

TAG Liadback

[리팩토링] 메일을 메시지로 통합하기

모하니?/Coding : 2009.10.20 00:11


이메일 서비스를 알림 서비스쪽으로 통합중입니다. 서비스 클래스를 통합하는 건 간단했습니다.

@Service
public class SendMailService implements NotificationService{
   
    @Autowired JavaMailSender mailSender;

    public void sendMessage(SpringSproutMessage ssm) {
        SpringSproutMail mail = (SpringSproutMail)ssm;
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, SpringSprout2System.ENCODING);
        try {
            helper.setTo(mail.getRecievers());
            helper.setFrom(mail.getFrom());
            helper.setSubject(mail.getSubject());
            helper.setText(mail.getContent(), mail.isHTML());
        } catch (MessagingException e) {
            throw new MailPreparationException(e);
        }
        mailSender.send(message);
    }

}

NotificationService를 구현하기만 하면 끝이죠. 그런데 문제는 이제부터 시작이었습니다. SpringSproutMessage.. 이 녀석을 받아서 메시지를 보내도록 해야하는데, SendMailService는 기존에 SpringSproutMail을 상속받아 만든 Mail 클래스들을 받아서 사용하고 있었습니다.

SSMessage와 SSMail은 상당부분 비슷한 속성과 메서드가 있으면서도 메시지 내용 상으로는 다소 차이가 있었습니다. 구글 토크나 트위터에는 간단한 메시지와 링크주소만 보내지만, 이메일로는 좀 더 구체적인 정보들까지 HTML 형태로 보내기 때문이죠.

public class SpringSproutMail extends SpringSproutMessage {
   
    public static final String SUBJECT_PREFIX = "[봄싹]";
    public static final String SENDER_MAIL = "s2cmailer@gmail.com";
   
    private String subject;
    private String content;
    private String[] recievers;
    private String from;
    private boolean isHTML;
   
    public SpringSproutMail() {
        this.from = SENDER_MAIL;
        this.isHTML = true;
    }
   
  ...
   
    protected void setTo(Member member) {
        String[] tos = new String[1];
        tos[0] = member.getEmail();
        setRecievers(tos);
    }
   
    protected void setTo(Collection<Member> members) {
        String[] recievers = new String[members.size()];
        Iterator<Member> memberIterator = members.iterator();
        for(int i = 0 ; i < recievers.length ; i++){
            recievers[i] = memberIterator.next().getEmail();
            tos.add(recievers[i]);
        }
        setRecievers(recievers);
    }
   
}

일단은 Mail 쪽의 최상위 클래스인 SpringSproutMail이 SpringSproutMessage를 상속받도록 수정했습니다. 그랬더니 아무 문제없이 잘 돌아가더군요. 이상태에서 일단 정지입니다. 돌아가게 만든 상태에서 어떻게 리팩토링 해야할지 고민좀 해야겠습니다.
신고
top


[negative] 어른들 때문에 큰일이다.



매일 매일 대중교통을 이용하는데, 새치기를 하거나 줄을 개떡같이 서서 얌채같이 먼저 타는 사람들은 대부분 나보다 나이가 많다. 나보다 어려보이거나 비슷한 나이 또래 혹은 나보다 조금 더 나이가 많은 정도(+-10년)로 보이는 사람들은 거의 얌채짓을 하지 않는다.(물론 예외도 있다.)

지옥같은 열차가 '신도림'역에서 설때면 정말 긴장된다. 오늘은 또 누가 날 밀쳐댈까... 제발 살살 밀쳐주길.. 내가 화가나지 않을 정도로만... 아침부터 화를 내거나 신경을 곤두세우기 싫은 마음에 계속해서 뒤를 살핀다. '젠장... 걸렸다' 누군가 밀기 시작한다. 뒤를 쳐다보면 95%가 아줌마다. 눈을 마주치고 잠시 쳐다봐주면 그중 50%는 손을 때고 밀기를 멈춘다. '다행이다. 최소한의 개념은 있군..', 그러나 절반은 그대로 계속 밀어부친다. '아 젠장.. 오늘은 정말 재수가 없구만..' 나도 모르게 인상이 구겨진다.

주말이 되면, 등산에 가거나 다녀오는 아줌마 아저씨들로 대중교통은 점령당한다. 배낭에는 뭘 그렇게 바리바리 싸들고 다닐께 많을까? 무슨 산에서 몇 일 자다 올 것도 아니면서 이해가 되지 않을 만큼 큰 배낭과 장비들을 주렁주렁 달고 다니신다. 나도 어렸을 때 주말 마다 가족끼리 가까운 산에 다녔었지만, 한번도 그렇게 짐을 바리바리 싸들고 다녀본적이 없다. 물 한 통과 ABC 초콜릿 한 봉지이면 충분했다. (가끔은 오이나 귤도 몇 개). 뭐 짐이 많은게 큰 문제는 아니지만 사람이 지나다니면 비켜줘야 할 것 아닌가.. 그리고 짐으로 사람을 치고 지나가진 않을지 조심 해야 정상인데.. 오히려 부딪혀도 미안하다는 말 한마디 듣기가 힘들다. 왜? 그 사람들은 나이가 많기 때문에~ 나이가 어린 나에게 미안하다는 말을 하기가 껄끄러울 것이다. 이건 대체 무슨 사상일까? 유교사상?? 어르신들은 유교사상에서 "나이 따지는 방법"만 배웠지 "예의"는 까맣게 지워버린 것 같다.

어디 그뿐인가. 떠들지. 드럽지. 말 안 듣지. 이건 뭐 초딩들이랑 비교해도 막상 막하다. 그나마 어린애들이야 어리니까 크면서 달라질 가능성이라도 있지. 어른들은 어쩔꺼냐. 이제 대라기에 피가 말라서 남들 말을 듣지도 못하니 큰일이다.

돈이 많다고 귀족이 아니고, 돈이 없다고 천민이 아닌데 사람들은 점점 천민이 되어간다. 사실 더 큰일은.. 그 밑에서 자라는 아이들이다. 윗물이 맑아야 아랫물이 맑고, 콩심은데 콩나고 팥심은데 팥난다고 하지 않았나.. 그리고 왜 그렇게 나쁜 짓들은 잘도 따라하는지...

그래도.. 한국에서는 일반인의 총기소지가 불법이라 다행이다. 음.. 그러고보니 그리 나쁘지도 않구나. 적어도 길가다가 총맞을 일은 없으니까.

결론은 총이 없어서 행복한 나라입니다
신고
top

TAG

[스프링 3.0] @Valid 실습

Spring/3.0 : 2009.10.18 12:57


1. 라이브러리 추가.

        <!-- Validation -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>com.springsource.org.hibernate.validator</artifactId>
            <version>4.0.0.GA</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>com.springsource.javax.xml.bind</artifactId>
            <version>2.1.7</version>
        </dependency>

2. 빈 설정.

    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="cacheSeconds" value="0" />
        <property name="webBindingInitializer">
            <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
                   <property name="validator" ref="validator" />
               </bean>
        </property>
    </bean>
   
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

3. 애노테이션 기반 검증 설정

    @Column(length = 100)
    @NotEmpty(message="제목을 입력하세요!")
    private String title;
    @Column(columnDefinition="TEXT")
    @NotEmpty(message="제목을 입력하세요!")
    private String contents;

4. 컨트롤러 코드 수정.

    @RequestMapping(value = "/notice/update/{id}", method = RequestMethod.POST)
    public String updateForm(@Valid Notice notice, BindingResult result, SessionStatus status) {
        if (result.hasErrors()) {
            return "notice/update";
        } else {
            noticeService.update(notice);
            status.setComplete();
            return "redirect:/notice/" + notice.getId() + ".do";
        }
    }

끝~!! 기본 에러 메시지는 애노테이션에서 변경할 수 있습니다.

이제 JSR 303 애노테이션과 그 확장 애노테이션에 뭣들이 있는지 살펴봐야겠네요.
신고
top


[스프링 3.0] @Valid 이론

Spring/3.0 : 2009.10.18 11:45


http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch05s07.html#validation.mvc

스프링 3.0에서 검증(validation) 설정에 대한 자바 표준인 JSR-303을 지원하기 시작했습니다.

public class Person {

    @NotNull
    @Max(64)
    private String name;
   
    @Min(0)
    private int age;

}

이런식으로 도메인 모델에 검증 메타데이터를 정의할 수 있는거죠. 이렇게 정의해둔 정보들은 JSR-303 Validator가 검증할 때 활용합니다. 이 검증기는 하이버네이트가 구현한것도 있고, 스프링 3.0에서 제공하는 것도 있습니다.

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

스프링에서 제공하는 검증기는 위에 있는 클래스입니다 이 클래스는 javax.validation.Validator 인터페이스와 org.springframework.validation.Validator 인터페이스를 구현했기 때문에 필요한 곳에서 적당한 인터페이스 타입으로 참조해서 사용하면 됩니다.

이밖에도 커스텀 @Constraint 만드는 방법과 DataBinder에서 스프링 검증기 사용하는 방법이 나와있으나 그 부분은 생략하겠습니다. 저는 지금 스프링 3.0 MVC에 적용하는 방법을 익히려고 정리중입니다.

이전까지는 @Controller 기반 컨트롤러를 사용할 때는 Validation 과정을 일일히 손수 코딩해줘야했습니다. 그러나 이제는 스프링이 자동으로 검증로직을 호출도록 설정할 수 있습니다. 그 방법이 바로 이 글의 제목인 @Valid죠.

@Controller
public class MyController {

    @RequestMapping("/foo", method=RequestMethod.POST)
    public void processFoo(@Valid Foo foo) { ... }

}

스프링 바인딩이 끝나면, 해당 객체에 설정된 Validator를 호출해 줍니다. 이때 호출해야 할 Validator를 설정하는 방법은 두 가지가 있습니다. 하나는 @Controller의 @InitBinder 마다 설정하는 방법이고, 다른 하나는 글로벌한 InitBinder라고 볼 수 있는 WebBindingInitializer에 설정하는 방법입니다.

@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.setValidator(new FooValidator());
    }
   
    @RequestMapping("/foo", method=RequestMethod.POST)
    public void processFoo(@Valid Foo foo) { ... }
   
}

이 방법과~

<!-- Invokes Spring MVC @Controller methods -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <!-- Configures Spring MVC DataBinder instances -->
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
            <property name="validator" ref="validator" />
        </bean>
    </property>
</bean>

<!-- Creates the JSR-303 Validator -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
               
이런 방법이 있군요.

그럼? 스프링이 검증 해준 다음은 어떻게 되는걸까요?? 검증 에러는 어떻게 받아오나? 검증 에러가 있으면 알아서 이전 폼을 다시 보여주나? 핸들러에서는 그럼 서브밋 됐을 때의 로직만 구현하는 되는건가? 실험해봐야겠군요.
신고
top


[Spring Web Flow] booking 예제 분석 3 - webmvc-config.xml 설정

Spring Web Flow/etc : 2009.10.16 20:28


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!-- Scans for application @Components to deploy -->
    <context:component-scan base-package="org.springframework.webflow.samples.booking" />

    <!-- Imports the configurations of the different infrastructure systems of the application -->
    <import resource="webmvc-config.xml" />
    <import resource="webflow-config.xml" />
    <import resource="data-access-config.xml" />
    <import resource="security-config.xml" />

</beans>

web.xml에 유일하게 등록되어 있는 빈 설정파일이었는데, 열어보니 네 개의 다른 빈 설정 파일들을 import 하고 있습니다. 이 중에서 webmvc-config.xml 파일을 보겠습니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- Maps request paths to flows in the flowRegistry; e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
        <property name="order" value="0" />
    </bean>

    <!-- Maps request paths to @Controller classes; e.g. a path of /hotels looks for a controller named HotelsController -->
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
        <property name="order" value="1" />
        <property name="defaultHandler">
            <!-- If no @Controller match, map path to a view to render; e.g. the "/intro" path would map to the view named "intro" -->   
            <bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
        </property>
    </bean>

    <!-- Resolves logical view names returned by Controllers to Tiles; a view name to resolve is treated as the name of a tiles definition -->
    <bean id="tilesViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTilesView"/>
    </bean>

    <!-- Configures the Tiles layout system -->
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/layouts/layouts.xml</value>
                <value>/WEB-INF/views.xml</value>
                <value>/WEB-INF/hotels/views.xml</value>
                <value>/WEB-INF/hotels/booking/views.xml</value>
            </list>
        </property>
    </bean>
   
    <!-- Dispatches requests mapped to POJO @Controllers implementations -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

    <!-- Dispatches requests mapped to org.springframework.web.servlet.mvc.Controller implementations -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

    <!-- Dispatches requests mapped to flows to FlowHandler implementations -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
        <property name="flowExecutor" ref="flowExecutor"/>
    </bean>

    <!-- Custom FlowHandler for the hotel booking flow -->
    <bean name="hotels/booking" class="org.springframework.webflow.samples.booking.BookingFlowHandler" />   
       
</beans>

1. org.springframework.webflow.mvc.servlet.FlowHandlerMapping

이 클래스는 (FlowUrlHandler를 사용하여) URL을 분석하여 해당 URL을 처리할 FlowHandler를 찾아서 반환해주는 역할을 합니다.

먼저, ApplicationContext에서 찾고, 다음은 FlowRegistry에서 찾아보고, 그래도 없으면 null을 반환합니다. 그럼 이제 이 다음 핸들러 맵핑 체인을 타게 되겠죠.

따라서 이 클래스에 설정할 수 있는 주요 속성은 FlowUrlHandler와 FlowRegistry입니다. 하지만 이중에서 FlowUrlHandler는 스프링이 기본으로 제공하는 DefaultFlowHandler를 사용하는게 일반적일 것 같군요.

2. org.springframework.webflow.mvc.servlet.FlowHandlerAdapter

이 녀석은 FlowHandler가 담당하기로 한 요청을 FlowExecutor를 이용해서 처리하는 일련의 워크 플로우에 따라 실행시켜주는 곳입니다. 실질적인 작업들은 상당 부분을 위임하고 있으면서 그 골격을 만들어둔 곳입니다.

플로우 id에 해당하는 플로우 정의를 가져오고, 플로우 정의로 플로우 실행 객체(FlowExecution)을 만들고, FlowUrlHandler로 필요한 execution 매개변수도 만들는 등.. 한 번의 코드리뷰로 파악하기에는 분량이 많더군요. 특히 FlowExecutor 쪽으로 들어가면.. 크헉.. 클래스가 팍팍 늘어납니다.

3. org.springframework.webflow.mvc.servlet.FlowHandler

특정 플로우 정의에 접근하는 것을 커스터마이징 할 때 이 녀석을 구현한다고 합니다.
- Launch executions of that flow with data in the execution input map
- 플로우 결과는 맘대로 처리하고 싶을 때.
- 플로우에서 다루지 않은 예외를 맘대로 다루고 싶을 때.

위 예제에서 BookingFlowHandler 이녀석이 구현한 건 두 개.
- String handleExecutionOutcome: 실행이 끝났을 때 갈 위치
- String handleException: 에러가 발생했을 때 갈 위치



신고
top


[JUnit] @Rule TemporaryFolder 사용하기

모하니?/Coding : 2009.10.16 16:26


public class DBUnitSupportTest {
   
    DBUnitSupport dbUnitSupport;

    @Rule public TemporaryFolder folder = new TemporaryFolder();
    @Autowired DataSource dataSource;
    File tempFile;
   
    @Before
    public void setUp() throws Exception{
        dbUnitSupport = new DBUnitSupport(dataSource);
    }
   
    @Test
    public void exportXls() throws Exception {
        tempFile = folder.newFile("temp.xls");
        dbUnitSupport.setExportedFile(tempFile);
        assertThat(tempFile.length(), is(0L));
        dbUnitSupport.exportXlsFrom("code", "item", "users", "role", "users_role");
        assertThat(tempFile.length(), not(0L));
    }
   
    @After
    public void after(){
        assertFalse(tempFile.exists());
    }

흠.. 좋군요!! 파일이 생겨나는 테스트를 하고 나서 매번 수동이든 코드로든 지워야 하는 번거로움이 없어졌습니다.

사실 파란색 코드는 지워도 그만인데, 아직은 TemporaryFolder Rule이 어떻게 동작하는지 확실하게 파악된 상태가 아니여서 남겨뒀습니다.

파일이 delete() 되지 않을 경우에 대비해서 말이죠. 사실 이 아래 글에 작성한 코드에는 치명적인 결함이 있었는데, 그 사실을 이번 테스트를 작성하다가 알게됐습니다. @_@;;

테스트 작성이 여러모로 도움을 주는군요.

참조: http://www.catosplace.net/blogs/personal/?p=116
신고
top


[DBUnit] 테이블 데이터를 엑셀로 export

모하니?/Coding : 2009.10.16 12:48


테스트
    @Test
    public void exportXls() throws Exception {
        dbUnitSupport.exportXlsFrom("code", "item", "users", "role", "users_role");
        assertTrue(new File(DBUnitSupport.EXPORTED_XLS_FILE).exists());
    }

구현
    protected void exportXlsFrom(String... tableNames) throws Exception {
        IDataSet dataSet = getConnection().createDataSet(tableNames);
        XlsDataSet.write(dataSet, new FileOutputStream(new File(EXPORTED_XLS_FILE)));
    }

흠.. 간단하네요. 간단 간단..

그런데. 테스트 할 때 생기는 파일들은 수동으로 지우기도 귀찮고.. 버전관리에 들어가면 더더욱 안 될 듯하고..
JUnit을 4.7로 올리고, @Rule 이라는 걸 써봐야겠습니다.
신고
top


[ToDo] 20091016

모하니?/Planning : 2009.10.16 10:24


1. 회사

1-1. DBUnit으로 현재 DB의 데이터를 Excel로 뽑아보기. (1시간) (1시간)
1-2. 기초 데이터 편집하기. (20분) (20분)
1-3. DBUnit으로 서버와 로컬 DB에 기초 데이터 넣기. (1시간) (1시간)

2. SWF

2-1. booking 예제 분석 및 정리 (2시간)
2-2. 봄싹에 적용 (2시간)

3. 번역

3-1. 하이버네이트 2장 (2시간)

4. 영어

4-1. Unit 2개 풀기 (2시간)
4-2. 동강 듣기 (1시간)

오늘도 달리자 달려!!!!!!!

1. 회사

- 소요 시간: 2시간 20분 (10시 30분 ~ 12시 50분)
- 예상 시간 적중.
- 스프링과 JUnit 라이브러리 업데이트와 테스트에 @Rule 적용하기는 번외로 추가.

@Rule 적용해 봅시다. ㄱㄱㄱㄱ!

5. 예외상황

- 소요 시간: 3시간 (1시 30분 ~ 4시 30분)
- 이클립스가 죽어버림. 젠장... 얼마나 됐나고..
- 이클립스 설치/플러긴 설치
- @Rule 적용

이제 SWF ㄱㄱ

2. SWF

- 소요 시간: 3시간(5시 ~ 8시)
- 뭘 했다고 시간이 이렇게 후딱 가버리지.. 아..

이제 집에가서 번역하고 영어만 하면 되나..
오늘은 영어부터 해야겠다.


신고

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

백기선 2010 목표  (12) 2010.02.15
봄싹 Career Path  (6) 2009.12.17
이번주 토요일 IBM dW "웹 개발 다반사"  (2) 2009.12.03
봄싹을 알리러 갑니다.  (6) 2009.11.24
[Atlassian] 이직 계획  (2) 2009.11.02
[ToDo] 20091016  (0) 2009.10.16
[ToDo] 오늘 할 일 - 할일(예상 소요 시간)(실제 소요 시간)  (4) 2009.10.15
2009년 마무리로 할 일  (4) 2009.10.08
9월에 할 일 정리  (10) 2009.08.31
공부할 것 정리  (6) 2009.03.16
요즘 내 우선순위  (2) 2009.01.22
top

TAG TODO

[JRebel] JRebel 설치 및 간단 사용법





참~ 쉽죠~
신고
top

TAG JRebel

[JRebel] 오퀘!! 퍼스널 OSS 라이선스 득탬!!

Good Tools : 2009.10.15 22:08


개인용 오픈 소스 라이선스를 받았습니다.

1년에 무려 7만원 가량(59달러)이나 하는 제품의 무료 라이선스를 특탬했습니다. 냐하하하~ OSAF 말고도 WebTUnit 이나 SES 플젝으로 받아도 되긴할텐데 일단 컨플루언스에 그럴싸하게 올라가있는 걸로 받았습니다. 오늘 낮 1시쯤 메일을 보냈을텐데 9시간 정도 걸렸군요. 빠른편입니다.



저는 이제 귀찮은 서버 껐다 켜기에서 해방입니다. 냐하하하~~

기념으로 스크린캐스팅 하나 찍어줘야겠군요.
신고
top

TAG JRebel






티스토리 툴바