Whiteship's Note

'모하니?'에 해당되는 글 934건

  1. 2009.10.27 대천 해수욕장과 청계산 (6)
  2. 2009.10.23 [봄싹] 모임 추가 시나리오 - web flow (구현)
  3. 2009.10.23 [봄싹] 모임 추가 시나리오 - web flow
  4. 2009.10.22 [Spring Wev Flow(2.0.8)] SecurityFlowExecutionListener 패치 for Spring Security 3.X
  5. 2009.10.21 [이직해야지] 들어가고 싶은 회사 Atlassian (4)
  6. 2009.10.21 [코드값] 애플리케이션에 DB id값이... (6)
  7. 2009.10.20 [봄싹] 메일 서비스를 알림 서비스로 통합하기
  8. 2009.10.20 [지름신 물럿거라] 누워서 노트북하기. Liadback (8)
  9. 2009.10.20 [리팩토링] 메일을 메시지로 통합하기
  10. 2009.10.16 [JUnit] @Rule TemporaryFolder 사용하기 (2)
  11. 2009.10.16 [DBUnit] 테이블 데이터를 엑셀로 export
  12. 2009.10.16 [ToDo] 20091016
  13. 2009.10.16 [JRebel] JRebel 설치 및 간단 사용법 (4)
  14. 2009.10.15 [기초 데이터] 백업/복구 방안
  15. 2009.10.15 [봄싹 버그]] JSON 뷰와 하이버가 가져온 Proxy 객체
  16. 2009.10.15 [ToDo] 오늘 할 일 - 할일(예상 소요 시간)(실제 소요 시간) (4)
  17. 2009.10.14 [권한] 3단 구조
  18. 2009.10.14 [우리 부부를 위한 랩] 네것인 내가 (2)
  19. 2009.10.13 [자바스크립트] confirm() 주의할 것
  20. 2009.10.13 [테스트] 스프링의 MappingJacksonJsonView 초간단 학습 테스트
  21. 2009.10.12 [제이쿼리] 마우스 오버/아웃 이벤트 사용하기 (6)
  22. 2009.10.08 2009년 마무리로 할 일 (4)
  23. 2009.10.08 별거 아닌 고민 (6)
  24. 2009.10.05 [작명 고민] 하이버네이트 get/find류 작명 규약 1 (2)
  25. 2009.10.05 보라카이 귿!! (2)
  26. 2009.10.03 여기는 마닐라 (2)
  27. 2009.09.29 [봄싹] 새 기능 소개 (4)
  28. 2009.09.29 [테스트 데이터] 테스트에 필요한 데이터 만들기
  29. 2009.09.29 [테스트 코드 리팩토링] extract method
  30. 2009.09.26 [JSP 리팩토링] 태그 파일로 중복 코드 제거하기

대천 해수욕장과 청계산

모하니?/그냥 놀아 : 2009. 10. 27. 21:53


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


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

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


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

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


top

  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.27 22:22 신고 PERM. MOD/DEL REPLY

    나도 놀러 ;;

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.10.28 00:17 PERM MOD/DEL

    놀아~ 많이 놀아~

  2. Favicon of http://blog.naver.com/j81811 BlogIcon aStRe 2009.10.27 22:32 PERM. MOD/DEL REPLY

    1만시간의 노력이라면 못 이룰 것도 없을 듯 합니다.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.10.28 00:17 PERM MOD/DEL

    아웃라이어 본다고 티내는거야?

  3. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.10.28 21:44 PERM. MOD/DEL REPLY

    즐거우셨겠습니다. :)

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

    네, 모처럼만에 스터디도 빼먹고 놀러간거였습니다.ㅋㅋ

Write a comment.


[봄싹] 모임 추가 시나리오 - 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

Write a comment.


[봄싹] 모임 추가 시나리오 - 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

Write a comment.


[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

Write a comment.


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

모하니?/Thinking : 2009. 10. 21. 16:13


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



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

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

파이팅!!
top

  1. 머큐짱 2009.10.22 13:04 PERM. MOD/DEL REPLY

    파이팅이요!!

    Favicon of http://whiteship.me BlogIcon 기선 2009.10.22 16:18 PERM MOD/DEL

    시니어 개발자 말고 그냥 일반 개발자도 뽑더라구요. 파이팅이요.

  2. 송목 2010.03.10 18:16 PERM. MOD/DEL REPLY

    안녕하세요.오늘 들어와 지난 글을 보고 있는 1인입니다. 제가 시드니에 있을 때 뭣도 모르고 무작정 레쥬메넣었는데 사라라는 분이 전화주셨죠 "졸업하구 생각해봐라" 음... 헛물만 잔뜩 캐버렸네용. 음하핫

    Favicon of http://whiteship.me BlogIcon 기선 2010.03.10 20:22 PERM MOD/DEL

    오호.. 시드니에서 학교를 다니셨나봐요. 부럽 +_+

Write a comment.


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

모하니?/Coding : 2009. 10. 21. 14:57


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

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

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

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

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

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

  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.21 15:42 신고 PERM. MOD/DEL REPLY

    위험한 발상?;;;

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

    어찌해야돼?

  2. truefree 2009.10.22 00:44 PERM. MOD/DEL REPLY

    코드 테이블과 동일하게 enum 을 만들어서 활용하는게 가장 적절한 방법인 것 같습니다.
    저도 동일한 문제로 이렇게 저렇게 고민해본적이 많은데...
    공부가 모자라서인지 어플리케이션 코드에서 완전히 분리 시키지는 못하겠더라구요.
    코드 테이블이 바뀔때마다 소스 수정이 뒤따르는 아픔은 여전합니다만-_-;;;
    그래도.......... (훌쩍)

    지나가던 고수분이 더 나은 해결책을 알려주시면 좋겠네요!
    올블에서 링크 타고 지나가다 끄적여봅니다 :)

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

    글쵸. 이건 아무래도 아닌것 같아요. DB 테이블 id 값이 애플리케이션에 하드코딩 되어야 하나디... 헐..

  3. sculove 2009.10.23 13:33 PERM. MOD/DEL REPLY

    SI에서는 일반적으로 enum으로 상수처리 하지만,
    자주 사용하는 거는 Xml Configure로 빼죠. ㅋㅋ

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

    흠.. XML로 빼다라도 어차피 애플리케이션에이 DB 테이블의 특정 id에 의존하는건 변함 없는거죠??

    애플리케이션에서 static final로 빼두는 것과 비슷하겠네요.

Write a comment.


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

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

Write a comment.


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

모하니?/그냥 놀아 : 2009. 10. 20. 11:18


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

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

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



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

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

TAG Liadback
  1. Favicon of http://bluepoetworld.tistory.com BlogIcon bluepoet 2009.10.20 11:42 PERM. MOD/DEL REPLY

    이거 참 괜찮네요~~

    근데 달러로 사면 어디서 사고 결제는 어케 하시는거에요??(궁금 ㅋㅋ)

    국내에 싼 제품 나왔으면 좋겠네요 ㅎ

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.10.20 12:35 신고 PERM MOD/DEL

    ebay에서 카드 결제하면 되요.

  2. Miracle 2009.10.20 13:10 PERM. MOD/DEL REPLY

    헐... 동영상보니... 지름신이 오실려나... 워이 워이~

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

    ㅋㅋ그냥 만드는게 나을 것 같아요.

  3. Favicon of http://www.zflex.co.kr BlogIcon poly 2010.05.04 16:55 PERM. MOD/DEL REPLY

    드디어 찾던게 나왔네요....폐인되기 딱 좋것네요^^

    Favicon of http://whiteship.me BlogIcon 기선 2010.05.06 09:30 PERM MOD/DEL

  4. Favicon of http://www.zflex.co.kr BlogIcon poly 2010.05.04 16:57 PERM. MOD/DEL REPLY

    폴리몰리 빈백 만드는 곳에서 이번에 나왔네요...www.zflex.co.kr

    Favicon of http://whiteship.me BlogIcon 기선 2010.05.06 09:31 PERM MOD/DEL

    오오!! 감사합니다.

Write a comment.


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

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

Write a comment.


[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

  1. Favicon of http://decoder.tistory.com BlogIcon decoder 2009.10.16 17:08 PERM. MOD/DEL REPLY

    어후, 이거 좋네요. ^^
    매번 테스트 할때마다 디렉토리 만들고 지우고 난리도 아니었는데 ㅠㅠ;

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.10.16 20:28 신고 PERM MOD/DEL

    네 사용법도 간단하고 좋아요

Write a comment.


[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

Write a comment.


[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

Write a comment.


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

모하니?/Screen Casting : 2009. 10. 16. 01:23




참~ 쉽죠~
top

TAG JRebel
  1. Favicon of http://naucika.pe.kr BlogIcon naucika 2009.10.16 16:09 PERM. MOD/DEL REPLY

    그런데 이녀석 클래스량이 많아지면, PermGen 오류가 발생하는군요. jvm -XX:MaxPermSize=128m 정도로 조정해줘야 한다네요.~ 기존 WAS의 autoreloding 도 wtp 에서 꺼주는 센스.

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

    넹.. 톰캣에서는 오토로딩 몇 번 하면 작은 프로젝트인데도 메모리 부족해서 죽어버리기 일수인데,

    저 녀석을 사용하면 저장 버튼 누른 순간까지 변경된 클래스들과 관련있는 부분만 다시 읽어주기 때문에 꽤 좋은 것 같아요.

  2. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.16 16:55 신고 PERM. MOD/DEL REPLY

    이따가 집에가서 ㄱㄱ 해볼끼요!!
    잘볼께요;;ㅋㅋ

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.10.16 20:28 신고 PERM MOD/DEL

    그랭. 해보고나서 말해. 선물 줄테니까 캬캬

Write a comment.


[기초 데이터] 백업/복구 방안

모하니?/Coding : 2009. 10. 15. 21:57


애플리케이션 동작에 필요한 최소한의 기본 데이터들이 있습니다. 이것들을 어떻게 관리해야 백업/복구가 쉬울지 고민입니다. 일단, 기초 데이터를 관리한다는 것 자체는 괜찮은 일인 것 같습니다. 기초 데이터가 매번 필요하긴 한데, 매번 애플리케이션을 새로 설치할 때 마다 입력해야 하는 수고를 덜 수 있으니까 말이죠. 그럼 어떻게 관리해야 할까요.. @_@

고민이 세부적으로 나뉩니다.

1. 우선, 기초 데이터를 SQL, Excel, XML 중 어떤 형태로 관리할 것인지.
2. 그 기초 데이터를 메이븐 빌드로 자동으로 넣을지, 별도의 자바 애플리케이션을 돌려서 넣을지, 사용자도 할 수 있도록 웹 애플리케이션에 기능을 만들어 넣어야 할지.
3. 매번 서버를 켤 때마다 기존의 기초 데이터로 운영/테스트 중인 데이터를 갈아 엎을지, 현재 운영/테스트 중인 데이터를 백업 받아와서 그것을 새로운 기초 데이터로 삼을지, 수동으로 관리할지.

일단 현재로써는 기초데이터가 자주 바뀔 가능성이 있고, 정확하지 않을 가능성이 높기 때문에 수동으로 관리해야 겠습니다. 수동으로 관리할 때는 Excel을 사용해서 일관적으로 바꾸는게 편하기 때문에 파일 형태로 Excel로 가져가는 것이 좋겠습니다. Excel로 데이터를 가져오고 넣을 때는 DBUnit을 활용하면 될 것 같습니다. 일단은 수동으로 관리자만 별도의 애프리케이션을 돌려서 넣을 수 있게 만들고, 그 일이 귀찮을 정도로 잦아 진다면 메이븐 빌드로 DBUnit 플러긴(당근 있겠죠?)을 이용해서 돌리면 될 듯 하군요.

그래서 내일의 회사일은
1. DBUnit으로 현재 DB의 데이터를 Excel로 뽑아보기.
2. 기초 데이터 마련하기.
3. 서버와 로컬 DB에 반영하기.



top

Write a comment.


[봄싹 버그]] JSON 뷰와 하이버가 가져온 Proxy 객체

모하니?/Coding : 2009. 10. 15. 18:27


JSON뷰랑 하이버 Proxy 객체가 만나면 JSON 뷰를 만들다 에러가 납니다.

하이버 객체가 Lazy 로딩을 할 수 없는 지점에서 Proxy 객체를 통해 collection에 접근해서 JSON 뷰로 만들 수 없는 데이터에 접근하여 발생하는 에러로 추측하고 있습니다. 에러가 좀 깔끔하게 떨어지면 해결책이나 원인을 찾기도 쉬울텐데.. 이건 뭐.. StackOverFlow 입니다. Q&A 게시판이 아니라. 정말 그 에러입니다.

이런 일이 발생한 대표적인 시나리오는 현재까지 세 군대 정도 됩니다.
- 로그인
- 출석체크
- 낙서장

이중에서 출석체크와 낙서장은 제가 해결했는데 그 방법이 비슷하지만 살짝 다릅니다.

먼저, 출첵의 경우 proxy객체를 직렬화 하는데, 필요 없는 객체를 JSON 뷰로 넘기고 있었습니다. @SessionAttribute에 등록한 객체들이 Model model 객체에 기본으로 들어가 있었고, 그 객체들(study, member, meeting)을 JSON 뷰로 직렬화 하다가 에러가 났습니다. 그래서 Model model을 깨끗하게 비워버리고 JSON 뷰로 넘겨주도록 헬퍼를 만들어 사용했습니다.

    @RequestMapping("/study/{studyId}/meeting/{meetingId}/confirm/{attendanceId}")
    public ModelAndView confirmMember(Model model, @PathVariable int studyId,
            @PathVariable int meetingId, @PathVariable int attendanceId,
            HttpSession session) {
        service.confirmAttendanceById(attendanceId);
        return JsonHelper.jsonViewWithCleanMap(model);
    }

두 번째 낙서장은 제가 맡은 부분은 아닌데, 계속 눈에 걸려서... 암튼 보니까 필요한건 Grafitty의 comments 뿐인데, Graffiy의 작성자(Member)를 포함한 모든 속성들을 다 가져오더군요. 그래서 JSON 뷰에서는 또 타고 타고 들어가다가 접근 못하는 부분(아마도 Member가 들고 있는 Set<Role> 타입의 프로퍼티)에서 JSON 뷰를 만들다 직렬화 에러를 냈을 겁니다. 이번에는 DAO에서 하이버네이트의 Projection을 이용해서 필요한 것만 가져오도록 쿼리를 수정해서 처리했습니다.

    @SuppressWarnings("unchecked")
    public List<Graffiti> getByWriteDate(Date writeDate) throws DataAccessException {
        Criteria c = getCurrentSession().createCriteria(Graffiti.class)
            .add(Expression.ge("writeDate", writeDate))
            .addOrder(Order.asc("writeDate"))
            .setProjection(Property.forName("contents"));
        return c.list();
    }

    @SuppressWarnings("unchecked")
    public List<Graffiti> getRecent10Contents() {
        Criteria c = getCurrentSession().createCriteria(Graffiti.class)
            .setMaxResults(10)
            .addOrder(Order.asc("writeDate"))
            .setProjection(Property.forName("contents"));
        return c.list();
    }

이 방법들은 완전한 대책이 아니라, 적당히 필요한 데이터만 간추리다보니 해결이 된겁니다. 땜빵이라고 하기도 좀 뭐하지만.. 해결책이라고 하기도 좀 뭐하지요.

좀 더 궁극적인 해결책을 생각해 봤는데;;

OSIV Fileter가 MappingJacksonJsonView와 뭔가 잘 안 맞는것 같습니다. JSON이 빌드할 때도 트랜잭션 경계 안에 들어있는 상태라면 하이버 프록시 객체에서 타고 타고 타고 들어갈 수 있는건데... 그게 안 되서 에러가 나는 거겠죠?? 결국은 AOP로 MappingJacksonJsonView의 특정 메서드를 실행하기 전에 트랜잭션을 열고.. 작업을 끝낸다음 트랜잭션을 닫는 작업을 해줘야 하는거 아닌지.. 고민입니다.

아이고;; 번역 헀어야 하는데.. 봄싹에 손을 대버리다니... 큰일이네... 큰일이야.. 에휴... 봄싹 중독인가. @_@

top

Write a comment.


[ToDo] 오늘 할 일 - 할일(예상 소요 시간)(실제 소요 시간)

모하니?/Planning : 2009. 10. 15. 10:30


1. 스프링 웹 플로우 예제 분석

1-1. 예제 돌리기.(10분) (30분)
1-2. 예제 코드 분석 및 정리.(2시간) (2시간)
1-3. 필요한 API, 레퍼런스 학습 및 정리.(2시간) (2시간)

2. JRebel 실험

2-1. 봄싹에 적용해서 돌려보기.(10분) (20분)
2-2. 사용 후기 정리.(30분) ) (40분)

3. 번역

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

4. 회사

4-1. 기초 데이터 백업/복원 방법 모색(1시간) (30분)

5. 영어

5-1. 유닛 2개 출력, 학습, 정리(3시간)
5-2. 두번째 동영상 강의 시청(40분)

6. 전철

6-1. 책읽기:아웃라이어(1시간) (1시간)

할일(예상 소요 시간)(실제 소요 시간)

할 일에 필요한 총 예상 시간: 12시간 30분.

현재 시간: 10시 30분.

달려!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

2. JRebel 실험 완료.

- 실제 소요 시간 1시간 (10시 30분 ~ 11시 30분)
- 예상보다 많은 작업(이클립스 플러긴 설치)을 수행하느라.. 조금 오래 걸림.

이제 SWF 가보자!!!! 달려 달려!!!!!!!!!!

1. 스프링 웹 플로우 예제 분석

- 실제 소요 시간 4시간 30분 (12시 30분 ~ 5시)
- 프로젝트 import가 제대로 되지 않아서 오래 걸림.
- 정리할 내용이 많음.
- 내일로 연장.

아.. 피곤한데;; 이제 번역좀 할까.

6. 책읽기

- 왔다 갔다. 읽었다 덮었다 하느라 실제 소요 시간 측정 실패. 대충 1시간일듯.
- 방행 받을 일이 없어서 책만 들고 나간다면 수행하기는 쉬운편

3. 번역

- 실제 소요 시간 1시간 (20시 30분 ~ 21시 30분)
- 한 시간 이상 하기가 힘드네. 자꾸만 다른게 하고 싶어져.. ㅠ.ㅠ

4. 회사

- 실제 소요 시간 30분 (21시 30분 ~ 20시)
- 하루에 회사일을 30분밖에 안 했네.. JRebel이랑 SWF 공부한 것도 회사일로 쳐준다면;;; 좀 되긴하지만;;

이제 남은건 영어 뿐인가.. 끝까지 달리자!!! 달려!!!!



top

TAG TODO
  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.15 11:06 신고 PERM. MOD/DEL REPLY

    예제 돌리는데 10분 ??

    5분으로 줄이삼..ㅋㅋㅋㅋ

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

    내 맘이야!!!
    1시간으로 늘릴까??ㅋㅋㅋ

  2. 머큐짱 2009.10.15 11:53 PERM. MOD/DEL REPLY

    이러다 애는 언제 가져요?ㅋㅋ

    가정도 생각하셔야죵~

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.10.15 12:12 신고 PERM MOD/DEL

    일단, 같이 자고.. 자다가 새벽에 일찍 일어나서 해야겠어요.

    요즘 와이프 혼자 먼저 자게 했더니 별로 안 좋은 것 같아요.

Write a comment.


[권한] 3단 구조

모하니?/Coding : 2009. 10. 14. 20:11


봄싹은 권한 관리는 3단계로 설계했습니다.

사용자 - 역할 - 권한
Member *---->1 Role *---->1 Right

현재 봄싹의 권한 관리 구조와 Role을 주로 사용하고 있습니다. 따라서 *와 **등을 이용해서 여러 URL을 큰 뭉탱이로 ROLE_ADMIN, ROLE_MEMBER로 구분하고 있죠. 굉장히 Coarse-grained 한 설정이죠. 이런 상태라면 설정하기는 간편하지만, 만약에...

"일반 회원 중에서 관리자는 아니지만 스터디를 관리할 수 있었으면 좋겠다."

이런 시나리오를 만족시키려면 다음과 해야합니다.

1. StudyManager라는 Role을 만듭니다.
2. 해당 사용자에게 StudyManager Role을 추가해줍니다.
3. URL 설정 및 메서드 보안 설정을 찾아다니며 hasRole(Role_StudyManager)를 추가해줘야 합니다.

만약에 URL 설정이 여러줄이고 메서드 보안이 여러줄이라면 어떻게 될까요??
이런 상태에서 RIght라는 것은 의미가 있기나 할까요?

그럼, URL 설정을 Right 기반으로 바꿨다고 가정하겠습니다. 아마 지금보다 더 세부적으로 URL 권한을 설정할테니 갈아타는데 손이 좀 갈 것으로 예상됩니다. Role 기반 설정에 비하면 fine-grained 합니다.

전부 Role 기반으로 바꿨다는 가정하에 다시 한 번 위의 시나리오를 적용하는 과정을 상상해 보겠습니다.

1. StudyManager라는 Role을 만들고
2. 해당 Role에 Add_Study, Update_Study, Delete_Study, End_Study, Start_Study 등 Study와 관련된 모든 권한을 추가해둡니다.
3. 해당 사용자에게 StudyManager 권한을 줍니다.

StudyManager라는 Role을 만드는 과정이 다소 귀찮을 순 있겠지만, A라는 사용자 말고 B 사용자에게 위와같은 시나리오를 적용해야 하는 상황이 온다면 어떨까요? 1, 2번은 필요없고 3번만 하면 됩니다.

"스터디 관리자여도 스터디 삭제는 못하게 하고 싶다. 그건 오직 관리자만 할 수 있게해야지"

이런 시나리오를 적용해야 한다면 Role 기반의 설정을 사용할때는 분명 study/34/delete.do URL이나 deleteStudy(study) 메서드에 hasRole(Role_StudyManager, Role_Admin)이라고 되어있을텐데, 저걸.. 다시 hasRole(Role_ADMIN)으로 바꿔줘야 할 겁니다.

그런데 Right기반으로 설정되어 있는 경우 hasRole(Right_Delete_Study)라고 설정되어 있을것이기 때문에 건드릴건 아무것도 없고, 단지 Role_StudyManager에서 Right_Delete_Study 만 빼주면 됩니다.

앗;; 전 이만 저녁먹으러...
top

Write a comment.


[우리 부부를 위한 랩] 네것인 내가

모하니?/Listening : 2009. 10. 14. 00:16


결혼식 때 와이프 친구분들께서 저희를 위해 만들어 준 곡입니다.
결혼식 축하 공연 때 불러 주셨었죠.



네것인 내가

작사 : 訂.SeN, EPsta
작곡 : 訂.SeN
편곡 : 訂.SeN
訂.SeN A.K.A 정세훈

[Verse.1]
벤츠 S class 보다 안락한 내 품에 안겨 God bless with us 비로써 행복함에 잠겨
Llfe is  봄날에  일창춘몽.. 눈을 뜨면 내가 보는 것은 너야. 분명..
첩첩산중에 턱턱숨이 막혀 올때 비가 내려 옷이 젖고 사람이 미울때
그때 내곁에 있을 사람이 그대.. 나 사랑이 그리울때 가득히 채워주네.
니가 만약 Make up의 퀸이라고 해도 니 맘이 너무 따뜻한 Sunshine
햇살받은 이불처럼, 꽃향기처럼 그래. 지금 그래. 내맘이 너땜에 행복해.

[싸비]
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.

[Verse.2]
나만의 나만의 유일한 Sunshine~ 두눈을 감고 나의 손을 잡아. 지금부터가
시작이야. 날믿어봐 우리는 하나 그 어떤것도 우리를 막을수는 없어.
장미보다 아름답고, 향수보다 향긋한 우리의 사랑. So 아름 다운 노래 속
사랑가득 담겠어. 같은영혼 속에서.. 주위의 부러움을 한바가지 쏟겠어.
아침에 눈뜰 때면은  니가 젤먼저 떠올랐어. 하지만 지금은 아침에 젤먼저 볼수있을 너야. 아!
요리도 함께하고, 소풍도 같이 가고.. 와우 와우~ Baby Yes I Will!
나만의 나만의 (영혼의 멜로디..) 너만의 너만의 (유일한 Song..)
사랑해. 사랑해. 너만을 사랑해. So fly I love so high.

[Verse.3]
널 내눈 안에 파묻어도 난 안파. 우린 같이해. 시작과 끝.. 알파와 오메가..
난 한때 그랬었지. 난 이제  아무사랑도 안해.
그렇게 살아도 난 이제 암시렁도 안해요.
하지만 You.. 내 맘속 깊은 바다속에 사랑을 끌어 올린 렉카..
나를 밝게 비추는 백합 In place 우리가 있는곳이 사랑의 메카..
이제는 반대로 삶에서 이끌어 줄게.  네것인 내가..

[싸비]
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.
지금 비록 모두 갖춰져 있지 않다해도 나에 대한 믿음을 굳혀.
난 자세를 낮춰 널 생각할께. I will understand  for you, and future.


ps: 설마 저작권에 걸리진... 않겠죠? 감사합니다!!

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

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

  1. Favicon of http://blog.naver.com/j81811 BlogIcon aStRe 2009.10.14 11:29 PERM. MOD/DEL REPLY

    저작권은 SeN에게 있어.
    우리도 시간 되면 작사는 작히가/ 작곡은 내가 해서
    녹음 하러가자~! ㅋㅋㅋ SeN의 멋진 녹음실 구경하로 ㅋㅋ
    이거 유포되도 괜찮어 ㅋㅋ

    Favicon of http://whiteship.me BlogIcon 기선 2009.10.14 15:15 PERM MOD/DEL

    ㅇㅇ재밌겠군

Write a comment.


[자바스크립트] confirm() 주의할 것

모하니?/Coding : 2009. 10. 13. 13:04


<%@ tag pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ attribute name="url" required="true" %>

<a id="btn_stop" href="<c:url value="${url}"/>"><img src="<c:url value="/images/study/stop.png"/>"/></a>

<script type="text/javascript">
$(document).ready( function(){
    $("#btn_stop").click( function() {
        if(confirm("종료 하시겠습니까?")) {
            $(this).parent().click();
        }
    });
});
</script>

분명히 confirm 팝업에서 "취소"를 눌렀는데도 그냥 진행이 되버리더군요. @_@;; 왜 이러나.. 싶어서 봄싹에 올렸더니 역시.. 성윤군이 원인과 해결책까지 제공! 캬~~ 어서 봄싹에 StackOverFlow 짭퉁 NullPE(NullPointerException) 메뉴를 추가해야 할텐데 말이죠.

원인은 간단하더군요. a 링크를 클릭할 때 이벤트를 등록했으니... 이벤트 처리하고 나서 링크 클릭한거 처리하느라 그렇게 된거더군요.

해결책도 역시 초간단...

$(document).ready( function(){
    $("#btn_stop").click( function() {
        if(confirm("종료 하시겠습니까?")) {
            $(this).parent().click();
        } else {
            return false;
        }
    });
});

else문 추가하고 false를 반환하면 끝. 크핫;

방법이 여러 개더군요.

    $("#btn_stop").click( function() {
        if(confirm("종료 하시겠습니까?") == false) {
            return false;
        }
    });

    $("#btn_stop").click( function(e) {
        if(confirm("종료 하시겠습니까?") == false) {
            e.preventDefault();
        }
    });

    $("#btn_stop").click( function(e) {
        return confirm("종료 하시겠습니까?");
    });

결국은 맨 마지막 걸로 고쳤습니다.

정우형/성유군/재일이형 쌩큐!
top

Write a comment.


[테스트] 스프링의 MappingJacksonJsonView 초간단 학습 테스트

모하니?/Coding : 2009. 10. 13. 12:10


public class SpringJsonVIewTest {
   
    class SampleObject{
        private String name;

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

        public String getName() {
            return name;
        }
    }
   
    MappingJacksonJsonView jsonView;
   
    @Test
    public void result() throws Exception {
        jsonView = new MappingJacksonJsonView();
        Map<String, Object> model = new HashMap<String, Object>();
        SampleObject s1 = new SampleObject();
        s1.setName("기선");
        model.put("s1", s1);
        SampleObject s2 = new SampleObject();
        s2.setName("갑수");
        model.put("s2", s2);
       
        MockHttpServletRequest request = new MockHttpServletRequest();
        MockHttpServletResponse response = new MockHttpServletResponse();
        jsonView.render(model, request, response);
       
        System.out.println(response.getContentAsString());
    }

}

이렇게 작성해봤습니다. Assertion 안했습니다.

JSON이 어떤 모양으로 생기는지 그냥.. 눈으로 확인하려구요. why not?

아참.. 결과는

{"s2":{"name":"갑수"},"s1":{"name":"기선"}}

이래요.
top

Write a comment.


[제이쿼리] 마우스 오버/아웃 이벤트 사용하기

모하니?/Coding : 2009. 10. 12. 20:59


출석체크를 할 때, 체크 모양의 버튼을 누르면 결석이 되고.. 결석 버튼을 누르면 출석이 되는 형태의... 비 직관적인 뷰를 만들었었습니다.

버튼을 두 개 만들기 귀찮아서 버튼 하나를 두고 On/Off 버튼 식으로 만들었는데, 현재 상태를 체크 모양 자체가 현재 상태를 나타내고, 그것을 한 번 누르면 결석으로 바뀌게 한 것이죠.

만든 저야.. 로직까지 포함해서 잘 이해가 되지만, 첨보는 팀원은 굉장히 햇갈려 하더군요. 이해합니다. 곰곰히 생각하다가 어제 맥북으로 사진 정리하다가 iPhoto에서 얼국 인식을 하는데, 내 얼굴인지 아닌지 확인할 때의 UI가 떠올랐습니다. 딱.. 그 UI가 출석체크와 비슷한 로직이었습니다.

다른 것은 하나.. 마우스가 올라갈 때, 해당 버튼이 어떤 이벤트를 할 지 알려준다는 것입니다.

"본인이면 클릭하세요." 에서 마우스를 클릭하면 "백기선님으로 확인되었습니다."로 바뀌고 거기서 마우스가 올라가면 "백기선님이 아닙니까?"로 바뀌죠. 그걸 또 다시 클릭하면, "백기선님이 아닙니다."로 바뀝니다.

오호.. mouse over 이벤트만 사용하면 되겠군...  생각하고 적용해봤습니다.

    $(".btn_attend_member").mouseover(function(){
        $(this).attr("src", "<c:url value='/images/remove-16x16.png'/>");
    }).mouseout(function(){
        $(this).attr("src", "<c:url value='/images/accept-16x16.png'/>");
    });

    $(".btn_absend_member").mouseover(function(){
        $(this).attr("src", "<c:url value='/images/accept-16x16.png'/>");
    }).mouseout(function(){
        $(this).attr("src", "<c:url value='/images/remove-16x16.png'/>");
    });

버튼이 아니라;; 이미지를 사용하고 있어서 좀.. 그렇긴 하지만 그건 나중에 바꾸기로 하고.. 일단 저렇게 이벤트를 등록해놓고 마우스를 올렸다 내렸다 해봤더니.. 잘 되는겁니다. ㅋㅋㅋ 그때까진 좋았죠.

하지만...

Ajax 요청을 보내고 나서 결석 상태를 출석으로 바꾸고 났을 때, 출석 상태인데도 마치 결석 상태처럼 마우스가 밖에 있으면 결석(현재 상태), 마우스가 이미지로 올라가면(다음 이벤트) 출석 상태를 보여주는 겁니다. @_@

문제 원인은 페이지 로딩시에 저 이벤트를 한 번 등록해두면 설령 Ajax 요청 처리에서 css를 바꾼다 하더라도 저 이벤트들을 다시 등록하진 않기 때문입니다.(좀 더럽지만... 화장실에서 볼 일 보며 곰곰히 생각해보니.. 왜 그런지 알겠더군요.)

그래서..

var changeImageSrcWhenMouseOverOut = function () {
    $(".btn_attend_member").mouseover(function(){
        $(this).attr("src", "<c:url value='/images/remove-16x16.png'/>");
    }).mouseout(function(){
        $(this).attr("src", "<c:url value='/images/accept-16x16.png'/>");
    });

    $(".btn_absend_member").mouseover(function(){
        $(this).attr("src", "<c:url value='/images/accept-16x16.png'/>");
    }).mouseout(function(){
        $(this).attr("src", "<c:url value='/images/remove-16x16.png'/>");
    });
};

이렇게 함수를 하나 만들고, 이 녀석을 페이지 로딩 할 때 한 번, Ajax 요청을 처리한 뒤 한 번 호출 하도록 코딩했더니.. 원하던데로 동작하게 되었습니다. 음하핫;;

안타깝지만, 관리자만 할 수 있는 기능이라 여러분을 볼 수가 없겠군요... @_@
top

  1. 지나가다 2009.10.12 21:13 PERM. MOD/DEL REPLY

    hover 메쏘드를 사용하시면 더 간결해 집니다. 이렇게...

    $(~~) . hover(
    function () {
    // mouseover 동작
    },
    function () {
    // mouseout 동작
    }
    );

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.10.12 21:19 신고 PERM MOD/DEL

    오홋!! 감사합니다~

  2. Miracle 2009.10.13 12:04 PERM. MOD/DEL REPLY

    Ajax 적용 시키면서 만들려다가 말았던 기능인데.. ^^;

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

    그때 만드셨어야죠!!
    일부러 저 제이쿼리 공부하라고 안 하시는거 아니에요?

  3. Miracle 2009.10.13 13:37 PERM. MOD/DEL REPLY

    그런 심오한 뜻은 없고... 단지 귀찮아서.. 그런거;;;

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

    ㅋㅋ넹

Write a comment.


2009년 마무리로 할 일

모하니?/Planning : 2009. 10. 8. 19:39


1. 번역

하이버네이트: 2, 3, 16 장 어여 하자.

2. 봄싹

1.0 M1 작업 범위 정하고 11월 말까지 마무리.

3. 회사

(물류, 제조, 생산, ERP)업무 학습 및 개발

이렇게 세 가지만 하자. 아니지;; 영어..

4. 영어

그래머 인 유즈 파란책 전부 다 풀기.
매일 매일 비슷한 문장 세 개씩 암기하기.
top

TAG TODO
  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.09 00:17 신고 PERM. MOD/DEL REPLY

    개발 서버에 파란책 동영상 올려져 있어요 ;;ㅎ 필요하면 땡겨가요;;ㅋㅋ

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

    앗.. 동강도 있단 말야?? 쌩큐!

  2. 2009.10.12 22:44 PERM. MOD/DEL REPLY

    비밀댓글입니다

    Favicon of http://whiteship.me BlogIcon 기선 2009.10.12 16:45 PERM MOD/DEL

    이메일을 노출하시면 스팸이 늘어나실텐데요;;

    그냥 가입 하시고 스터디에 참가신청 하시면 모임이 있을 때 마다 메일이 갑니다. 그거 보고 오시면 되요.

    초간단 참여 방법은 봄싹 홈피에 써있습니다.

    http://springsprout.org

    ps: 빈 수레가 요란하다자나요;; 저보다 훨씬 부지런한 친구가 위에 있네요.

Write a comment.


별거 아닌 고민

모하니?/Thinking : 2009. 10. 8. 12:20


회사가 이사를 가서 출퇴근 시간이 길어졌고 코딩과 별 관련없는 사람들(CF, 영화감독 겸 PHP 솔루션으로 홈피 만드시는 분, 자전거 용품 쇼핑몰을 운영하시는 분, 기자, 보드 타시는 분) 속에 낑겨들어가 생활하게 되었습니다. 차라리 상관없는 분들이라면 지금처럼 나 혼자 일하는게 훨씬 조용하고 편할텐데 부데끼게 생겼습니다. 이것 때문에 스트레스를 받아서 어제는 밤잠도 설쳤지요.

그래서 몇 가지 대안을 생각했었습니다.
1. 참고 다닌다.
2. 때려치고 한국 SI에 발을 담근다.
  2-1. 중소 SI 업체에 들어간다.
  2-2. 프리랜서로 뛴다.
3. 때려치고 논다.
  3-1. 집에서 공부만 한다.
  3-2. 대학원에 갈까.
  3-3. 바로 이민을 가?!

일단 3번은 지금 상황에서 말도 안되기 때문에 생각을 접었고, 나머지 둘 중에 고민을 하다가 결국은 1번으로 결정 했습니다.

총 세분과 면담이 있었습니다.

사부님: 1번과 2번의 장단점을 여러 가능성과 함께 제시해주고 마무리. "선택은 너의 것"
성윤군: "가정을 지키셔야죠."
장모님(뜨헉?!): 힘든 일을 겪어야 성장하는 법인데, 그런 시련을 준걸 마음 편히 받아 들이고 감사히 생각해야 한다.

처음에는 사부님하고 채팅으로 면담을 하다가 프리랜서 얘기가 나와서 성윤군한테 말을 걸었습니다. 그렇게 대화를 하고나서 이 글을 쓰며 생각을 마무리 하고 있을 무렵 이 내용과 관련하여 장모님한테서 전화가 왔습니다. 깜짝 놀랬죠.ㅎㅎ 아마도 어제 좀 시무룩한 표정으로 집에 들어갔더니 와이프가 얘길 듣고 장모님께 전했나 봅니다.

코딩과 관련된 시련이었다면 달갑게 여겼을텐데 저런 것들은 좀 그러기가 힘들었습니다. 아..흑...  애초에 미국 플젝 하려고 들어온 회사인데 그 플젝이 무산되다시피 하고 계속 꼬여가는 것만 같아서 스트레스였는데, 그냥.. 이게 다 좋은거다 생각하면서 다시 본업에 충실해야겠습니다.

집중하자.. 집중해. 집중.
top

TAG 고민, 집중
  1. Favicon of https://helols.tistory.com BlogIcon is윤군 2009.10.08 13:31 신고 PERM. MOD/DEL REPLY

    제가 있는 사무실은 사람이 100명정도 되는듯해요.. 그중에 아는 사람은 10명 내외? 시글시글 ~ ;; 하루에 한번쯤은 옆에있는 현대카드 사무실에서 아줌마가 와서 카드하나 만드세요ㅡㅡ; 이러고 잇죠.. 그냥 이어폰 딱 귀에 꼽고 !! 하던일~ 주욱 하면됨.. 여기서 중요한건.. 누군가가 부르면 한번만에 대답하지 말고 두세번 부르면 못들은척 대답해주어야 해요.. 집중한다는것처럼..ㅋ 간혹 음악듣는다고 쌩깐다는 오해를 받긴 할때도 있지만~ 그때는 슬쩍 그냥 시끄러워서 이어폰만 꼽는 거라는 정보를 흘려야 되요.. ( 물론 귀에는 신나는 음악을 듣던가 아님 귀로 영화 한편? 동강을 보던지 간에;;ㅋㅋ)
    뭐야 이거 댓글이 길어졌군요.. 무튼.!! 가정을 지키삼..ㅋㅋ

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

    응 봄싹도 지켜야지ㅋ

  2. Favicon of http://blog.naver.com/j81811 BlogIcon aStRe 2009.10.08 15:30 PERM. MOD/DEL REPLY

    오타:장모님의 의견중 (성장)

    나도 1번이 강추. 마인드컨트롤!!
    자꾸 우울해 하면 내가 더 우울해져.. 화이팅하는건 어때?

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

    응.. 마인드컨트롤.
    다크아콘 기술이었나.. 이젠 스타크레프트도 다 까먹겠군

  3. 머큐짱 2009.10.08 20:21 PERM. MOD/DEL REPLY

    고민 되시겠어요..ㅎ

    전 SI에서 탈출한 사람으로써,,

    "한국 SI에 발을 담근다"는 정말 비추..

    기선씨 정도의 실력이면 좋은데서 근무할 수 있을거에요.ㅎ

    좀만 참아보삼..^^

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

    제가 뭐 별거 있나요.
    허접하답니다. @_@;

Write a comment.


[작명 고민] 하이버네이트 get/find류 작명 규약 1

모하니?/Coding : 2009. 10. 5. 17:12


참조: http://blog.xebia.com/2009/04/03/jpa-implementation-patterns-retrieving-entities/

아시다시피, 하이버네이트에서 영속화 중인 객체를 가져오는 방법은 get()과 load()가 있습니다. 둘 다 가져올 객체 클래스와 id 값을 넘겨주면 원하는 객체를 가져올 수 있습니다.

일단 중요한 차이점 하나는 가져다 달라고 하는 객체가 없을 때 get()은 null을 반환하고 load()는 ObjectNotFountException을 던진다는 것입니다. 또, get()은 항상 DB에서 꺼내오게 쿼리가 날아가며 load()는 프록시를 반환한 뒤 실제로 객체의 다른 속성 값들이 필요할 때 쿼리가 날아가도록 Lazy loading을 활용할 수 있습니다.

그러나 여기서 말하려는건 get, load가 아니라 Session.createQuery() 또는 createCriteria()를 이용해서 만드는 DAO의 메서드 작명 지침과 그 규약에 대한 것입니다.

DAO를 만들다 보면, 검색 류의 메서드들을 많이 추가하게 되는데, 그 이름을 지을 때마다 get으로 할까 find로 할까 by 뒤에 매개변수 타입을 적어줄까 아니면 어차피 메서드 매개변수에 있으니까 생략할까.. 이런 고민을 하게 되는데 맨 위 참조 글을 적은 분이 잘 정리해 두셨더군요.

위 글을 적으신 분은 EntityManager API의 find() 메서드와 createQuery() 메서드에 따라 규약을 만들었더군요. 그것을 응용해서 Session API의 createQuery(), createSqlQuery(), createCriteria(), get(), load() 등을 사용하는 DAO의 메서드 이름과 그 동작방식에 대한 규약을 정할 수 있을 것 같습니다.

그래야만 아래처럼 애매한 이름의 DAO 메서드들이 만들어 지지 않을테니까요.


여러 Study 객체를 가져오는데, 어떤 것은 getStudyList 어떤 것은 findXXXStudies 라니..
- get과 find의 구분도 없고
- Study 복수형을 쓸지 List를 붙일지 규약도 없네요.
- 그리고 무엇으로 검색을 한다는 것도 추측이 안 되고 말이죠.

그리고 하나 더 Finder를 제공해주는 AridPOJOs라는 걸 사부님 블로그에서 얼핏 봤었는데 자세히 좀 살펴보고 싶어지는군요. Finder까지 기본 제공해주는 GenericDao!! 멋지자나요. 이미 AridPOJOs에서는 Finder를 만들면서 위와 같은 고민을 했었겠죠?

흠... 생각 좀 해봐야겠습니다.

top

  1. Favicon of http://blog.daum.net/gujjy BlogIcon 내사랑꿀떡 2009.10.06 09:36 PERM. MOD/DEL REPLY

    거시기...
    있다고 확실시 되는 것은 get이고
    있는지 없는지 알지 못하는 것은 find라는 얘기 인거죠?

    있어야 되는데 없는 것은 예외를 발생시키고
    찾아보는데 없다는 것은 없다는 것 자체로 결과 인것이구요?

    설마하고 봤는데.. 역시나 영어더라구요.. -_-;
    부럽습니다~

    Favicon of http://whiteship.me BlogIcon 기선 2009.10.06 09:53 PERM MOD/DEL

    넹. get류는 없을 때 null을 던지게 하고 find류는 없을 때 예외를 던지는.. 그런 규약입니다. 반대로 정해도 상관은 없을 것 같은데요. 중요한건 일관성이겠죠.

Write a comment.


보라카이 귿!!

모하니?/그냥 놀아 : 2009. 10. 5. 09:04




이건 첫 날 디몰에 저녁먹으러 갔을 때 해변에서 찍은 사진입니다. 노을이 정말 이쁘다고 하는데 저날은 구름이 많아서 장관을 건지지는 못했습니다.


이건 둘쨋날 호핑투어 하러 가는 길에 화이트비치를 따라 걸어가다가 찍었습니다. 이때까지만 해도 날씨가 좋았습니다. 모래도 어찌나 곱던지... @_@ 정말 발이 녹아버릴 것 같아요.

저~~ 뒤 오른쪽에 보이는 커플(?)이 부모님이십니다. 마닐라에서는 그닥 재미없게 지내셨을텐데 보라카이에서는 좋은 추억을 만들어 드린것 같아서 기분이 좋네요. 다음에는 장모님, 장인어른과 함께 여행을!!

ps: 다시 일상으로 돌아가야 하는데.. 아~ 역시 컨텍스트 스위칭은 비용이 좀 생기는군요.
top

  1. 머큐짱 2009.10.05 13:31 PERM. MOD/DEL REPLY

    와우~~ 재미있었겠어요~~

    부럽삼,,

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

    넹.. 보라카이 멋졌어요!

Write a comment.


여기는 마닐라

모하니?/그냥 놀아 : 2009. 10. 3. 00:41


지금은 비가 부슬부슬 내리고 있습니다.

어렸을 때 부모님하고 여행은 자주 다녔었지만 제가 부모님을 모시고 나온 여행은 처음이라 신경이 많이 쓰이네요. 사실 해외여행만 아니였어도 두 분이 다녀오시라고 했을텐데 간단한 인삿말도 어색해 하셔서 그러기가 힘들었습니다.

가장 걱정했었던 환승 문제는 운좋게(?) 잘 해결됐습니다. 1시로 알고 갔지만, 그 뱅기 일정은 없어지고 2시 뱅기로 미뤄지더군요.. +_+;; 티켓팅하는게 거리는 시간만 40분.. 스코틀랜드에서 온 어느 아저씨는 화가나서 직원에게 대놓고 "너 일한지 얼마나 됐냐?"라고 물어보시더군요. 뱅기 시간이 엄청나게 유동적이고, 직원이 일을 잘 못한다는 것, 비행기가 작아서 비행이 아주 스릴넘치다는 것을 빼면 씨에어도 나름 괜찮은 항공사 같습니다. 나름 까띠끌란에서 항구까지 무료 셔틀도 운행해주고, 보라카이에서 까띠끌란으로 무료 배편도 제공해주니까요.

가족여행을 와보니 느끼는 것이 많습니다.

저는 여기 오기 전까지 아버지가 이렇게까지 활달하신 분인줄은 몰랐습니다. 꼭두새벽에 일어나서 온 동네를 거침없이 돌아다시니는데.. 정말 두 손 두 발 다 들었습니다. 현지인들과 한국어로 너무도 자연스럽게 대화하는 모습이 인상깊었습니다. 외국에 나온지 이틀만에 저희 부부와 대화를 할 때도 "야스"로 대답하시는 놀라운 적응력!! 오늘은 마닐라에 오자마자 호텔에서 짐을 풀고 백화점가서 밥먹고나서 지하철을 구경하러 갔다가 즉흥적으로 지하철을 타고 어디있는지도 모르는 '시청'으로 가보자고 하시는 걸 겨우 말려서 다시 돌아왔습니다. @_@..

아버지에 비하면 적응이 느린것 같지만, 저에게는 어디로 튈지 모르는 아버지보다는 조용히 슬슬 잘 따라오시는 어머니가 훨씬 편하긴 합니다. 음식 적응이 힘드셔서 고생하고 계시기는 한데, 보라카이 화이트 비치에 맨발로 걸어다니실 때 어린이처럼 즐거워하시던 모습은 평생 기억에 남을 것 같습니다. 해변의 모래가 어찌나 부드럽던지;; 발바닥이 녹는것 같았어요. 캬~

와이프한테는 조금 미안합니다. 부모님 신경쓰느라 와이프한테 다도 소홀했더니 와이프가 아픈것도 모르고 있었어요. ㅠ.ㅠ 와이프 입장에서는 시부모님 모시고 오는 여행이라 저보다도 더 긴장되고 힘들 수 있을텐데 잘 좀 해줘야겠어요. 미안해 여보.. ㅠ.ㅠ 이제 잘 할 께~

여행 오기전에는 신경쓰이는게 너무 많아서 여행가기 싫다고 생각했었는데, 잘한 것 같습니다. 이제 내일 하루 마닐라에서 즐겁게 돌아다니고 내일모레에 한국으로 돌아갑니다. 생각 같아서는 보라카이 어느 리조트 전산직으로 근무하고 싶긴한데.. 봄싹 스터디 때문에라도 한국으로 돌아가야겠네요.ㅋㅋ

ps: 보라카이의 장사꾼들은 한국어를 꽤 잘해요. 마치 한국의 명동 장사꾼이 일본어 하듯이.
top

  1. Favicon of http://blog.naver.com/j81811 BlogIcon aStRe 2009.10.05 18:56 PERM. MOD/DEL REPLY

    나는 이제 괜찮아욤~!

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

    응~ 고마워!

Write a comment.


[봄싹] 새 기능 소개

모하니?/Coding : 2009. 9. 29. 18:41


1. 트위터 서비스!

http://twitter.com/springsprout

봄싹 트위터를 통해서 스터디와 모임 개설/변경 소식을 실시간으로 전해드립니다. 트위터 RSS를 구족하시면 RSS 리더기를 통해서도 스터디와 모임 정보를 쉽게 받아 보실 수 있겠죠.

2. 구글 토크 알리미!

s2cmailer@gmail.com

이 주소를 구글토크에 친구로 추가해두고, 구글 토크에서 사용하는 이메일로 봄싹에 가입했다면,
스터디와 모임 메시지를 바로 바로 받을 수 있습니다.

그것 뿐 아니라, 대화형 기능을 제공하여 study? 라고 입력하면 현재 운영중인 스터디 목록을 보여주고, meeting? 이라고 입력하면 현재 개설되어 있는 모임 목록을 보여줍니다.


3. 통계 기능 추가!

스터디에 얼마나 열성적으로 참석하는지..
스터디 별로 참석률은 어떤지..
모임 참가 신청을 하고나서 자주 불참하는 회원은 아닌지..
스터디 별로 신청을 해놓고 얼마나 약속을 잘 지키는지..

한눈에 알 수 있는 기능을 추가했습니다. (테스트 데이터가 엉망이라 통계가 좀 이상합니다.)


나중에는 참석률과 신뢰도를 게이지 형태로 디자인해서 마치.. 게임 캐릭터의 체력과 마력을 나타내듯이 표시할 생각입니다. ㅋㅋ

셋 다 제가 코딩했고 만들면서 많이 고민하고 재미를 느꼈던 기능들입니다.

- 트위터 알리미를 만들 때는 인터페이스 사용의 혜택을 만끽해 보았으며,

- 구글 토크 서비스를 만들 때는 smack API를 이용하는 JabberService를 만들면서 스프링 DI를 어떻게 하면 잘 활용할 수 있을까 고민할 수 있는 계기가 되었습니다.

- 마지막으로 통계 기능을 만들 때는 별로 안 해봤엇던 하이버네이트에 Map<Entity, Primitive Type> 형태의 맵핑도 해보고, 어떤 콜렉션 타입이 좋을지 고민도 해보고, EL로 map안에 있는 데이터 꺼내오기도 해봤네요. 하지만 뭐니뭐니해도 테스트를 통해서 얻을 수 있는 안정감.. 그걸 느껴볼 수 있었습니다.

현재 봄싹은 제가 만든 새로운 기능 말고도 다른 분들이 만들고 있는 새 기능도 많이 있습니다. 본격적인 운영은 추석이 지나고나서 될 것 같네요. 그전까지 지금 상태 그대로 갑니다.

모두 명절 잘 보내세요~
top

  1. Favicon of http://yunsunghan.tistory.com BlogIcon Max 2009.09.30 08:27 PERM. MOD/DEL REPLY

    트위터 RSS 추가 했습니다.
    앞으로 잘 구독할께요.
    좋은 활동 부탁해요 ^^*

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

    넹~ 추석 잘 보내세요!

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

    헉! 무시무시한 감시 기능인가요? 덜덜덜... :)

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

    후훗 그렇치요.

Write a comment.


[테스트 데이터] 테스트에 필요한 데이터 만들기

모하니?/Coding : 2009. 9. 29. 15:24


    @Test
    public void calcTotalAttendanceRate() throws Exception {
        Member member = new Member();
        Study study1 = new Study();
        Meeting meeting1 = new Meeting();
        Meeting meeting2 = new Meeting();
        Meeting meeting3 = new Meeting();
        Study study2 = new Study();

        study1.addMeeting(meeting1);
        study1.addMeeting(meeting2);
        study2.addMeeting(meeting3);
        member.addJoinedStudy(study1);
        member.addJoinedStudy(study2);
        member.applyAttendance(meeting1);
        member.applyAttendance(meeting2);

        int attendanceSize = member.getAttendances().size();
        assertThat(attendanceSize, is(2));
        assertThat(member.getStudies().size(), is(2));
        assertThat(study1.getMeetingCount(), is(2));
        assertThat(study2.getMeetingCount(), is(1));

        when(mockStudyRepository.getConfirmedAttendanceCountOf(member)).thenReturn(1);
        when(mockStudyRepository.getTotalAttandanceCountOf(member)).thenReturn(attendanceSize);
        memberService.calcRatesOf(member);
       
        assertThat(member.getTotalAttendanceRate(), is(33));
        assertThat(member.getTotalTrustRate(), is(50));
    }

이 테스트에서 절반 이상이 테스트 데이터를 만들고 그 부분을 검증하는 코드입니다. 이 부분을 다음과 같이 바꿀 수 있다면.. 좋을까요? 안 좋을까요?

        Meeting meeting1 = Builder.Create(Meeting.class).Build();
        Meeting meeting2 = Builder.Create(Meeting.class).Build();
        Meeting meeting3 = Builder.Create(Meeting.class).Build();
       
        Builder.Create(Member.class)
            .addStudy(Builder.Create(Study.class)
                    .addMeeting(meeting1)
                    .addMeeting(meeting2)
                    .Build())
            .addStudy(Builder.Create(Study.class)
                    .addMeeting(meeting3)
                    .Build())
            .addAttendance(Builder.Create(Attendance.class)
                    .addMeeting(meeting1)
                    .Build())
            .addAttendance(Builder.Create(Attendance.class)
                    .addMeeting(meeting2)
                    .Build())
            .Build();

Object Mother에 대한 글을 찾아 보다가 Test Data Builder에 대한 글과 거기에 달린 댓글을 통해 닷넷에서 사용하는 NBuilder라는 것까지 대충 살펴봤는데.. 이거 뭐.. 해보지 않고서는 어떨지 잘 상상이 안 되네요.

http://martinfowler.com/bliki/ObjectMother.html
http://c2.com/cgi/wiki?ObjectMother
http://geekswithblogs.net/Podwysocki/archive/2008/01/08/118362.aspx
http://www.nbuilder.org/

그래서 일단은 위와 같이 상상 코딩을 해보았는데.. 어떨런지요.. 흠..
top

Write a comment.


[테스트 코드 리팩토링] extract method

모하니?/Coding : 2009. 9. 29. 15:01


    @Test
    public void getAttendanceCountOf() throws Exception {
        insertXmlData("testData.xml");
        Member member = new Member();
        Study study = new Study();
        member.setId(1);
        study.setId(1);
        assertThat(sr.getAttendanceCountOf(member, study), is(2));
        member.setId(1);
        study.setId(2);
        assertThat(sr.getAttendanceCountOf(member, study), is(1));
        member.setId(2);
        study.setId(2);
        assertThat(sr.getAttendanceCountOf(member, study), is(1));
    }

다음과 같이 코드를 리팩토링할 수 있습니다.

    @Test
    public void getAttendanceCountOf() throws Exception {
        insertXmlData("testData.xml");
        checkAttendanceCountOf(1, 1, 2);
        checkAttendanceCountOf(1, 2, 1);
        checkAttendanceCountOf(2, 2, 1);
    }
   
    private void checkAttendanceCountOf(int memberId, int studyId, int count){
        Member member = new Member();
        Study study = new Study();
        member.setId(memberId);
        study.setId(studyId);
        assertThat(sr.getAttendanceCountOf(member, study), is(count));
    }

이번 경우에는 코드 라인수 차이가 얼마 나지 않지만, 대부분의 경우 훨씬 깔끔해집니다.
top

Write a comment.


[JSP 리팩토링] 태그 파일로 중복 코드 제거하기

모하니?/Coding : 2009. 9. 26. 16:15


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="page" tagdir="/WEB-INF/tags/page"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="s" tagdir="/WEB-INF/tags/study"%>

<page:studypage>
<s:defaultpage>
    <h1>스터디 추가</h1>
    <form:form commandName="study" method="post">
    <p>
        <label>스터디명</label>
        <form:input path="studyName" cssClass="text" />
        <form:errors path="studyName" />
    </p>
    <p>
        <label>최대인원수</label>
        <form:input path="maximum" cssClass="text" />
        <form:errors path="maximum" />
    </p>
    <p>
        <label>시작일</label>
        <form:input path="startDay" cssClass="text"/>
        <form:errors path="startDay" />
    </p>
    <p>
        <label>종료일</label>
        <form:input path="endDay" cssClass="text"/>
        <form:errors path="endDay" />
    </p>
    <p>
        <label>설명</label>
        <form:textarea path="descr" rows="4" cols="60" cssClass="text"/>
        <form:errors path="descr" />
    </p>
    <br/><hr/><br/>
    <a href="<c:url value="/study/list.do"/>">취소</a>
    <input type="submit" value="저장" class="s_waitblock" />
    </form:form>
</s:defaultpage>
<script type="text/javascript">
  $(document).ready(function(){
    $("#startDay").datepicker({ dateFormat: 'yy/mm/dd' });
    $("#endDay").datepicker({ dateFormat: 'yy/mm/dd' });
  });
</script>
</page:studypage>

이미 태그 파일로 <html> </html>과 js, css 임포트 하는 부분을 제거 해 두었습니다. 태그 파일을 여러 추상화 계층으로 세분화 해서 로우 레벨 태그파일과 하이 레벨 태그파일로 나눌 수도 있겠습니다. 저 위에 보이는 page 태그는 하이 레벨 태그 파일로 볼 수 있고, s 태그는 로우 레벨로 볼 수 있습니다. 하이 레벨이라고 해서 뭔가 더 여러운 태그라는게 아니라, 로우 레벨 태그를 조합하여 한 단계 더 추상화시킨 태그파일 입니다. 이런 구분이 원래 있는 것이 아니라 제가 생각하는 걸 정리한 것 뿐이오니,,, 괜히 "하이 레벨 태그 파일" 이런식으로 구글링을 하는 사태가 없기를 바랍니다.

사설을 좀 길었네요, 일단락하기로 하고, 위 코드를 태그파일로 리팩토링하면 다음과 같이 됩니다.

<page:studypage>
<s:defaultpage>
    <h1>스터디 추가</h1>
    <form:form commandName="study" method="post">
    <s:ftext title="스터디명" path="studyName" />
    <s:ftext title="최대인원수" path="maximum" />
    <s:fdate title="시작일" path="startDay" />
    <s:fdate title="종료일" path="endDay" />
    <s:ftextarea title="설명" path="descr" rows="4" cols="60" />
    <hr/>
    <s:back-button url="/study/list.do" />
    <input type="submit" value="저장" class="s_waitblock" />
    </form:form>
</s:defaultpage>
<script type="text/javascript">
  $(document).ready(function(){
    $(".fdate").datepicker({ dateFormat: 'yy/mm/dd' });
  });
</script>
</page:studypage>

이렇게 했을 때 좋은 점은 소스 코드에서 중복을 제거 했을 때 얻을 수 있는 장점과 같습니다.

그러나,,, 단점도 있는데 태그 파일에 정의해준 속성만 받아서 사용하기 때문에 그만큼 사용할 수 있는 기능이 제한 될달까.. 그런게 좀 있습니다. 해결책은 있습니다. 태그 파일에 거의 모든 속성을 다 정의해 놓고 정말 필요한 것만 required로 하고 사용해도 될테지만.. 태그 파일을 만드는 비용이 꽤 많이 들겠지요. 결국 선택의 기로에 서게 되는데, 저는 귀찮아서;; 그냥 최소한의 속성만 정의해서 쓰는 편입니다.

top

Write a comment.




: 1 : ··· : 3 : 4 : 5 : 6 : 7 : 8 : 9 : ··· : 32 :