Whiteship's Note


Eclipse에서 톰캣 웹 모듈 설정하기

모하니?/Coding : 2007.09.20 14:12


Server 뷰에 있는 서버 중에 하나 선택
사용자 삽입 이미지

Overview 탭이 기본으로 보이는데 여기서 Module을 선택합니다.
사용자 삽입 이미지

docBase에 뭔가 있다면 전부 선택해서 제거(remove)합니다. 그리고 오른쪽에 Add External web module을 선택합니다.
사용자 삽입 이미지

그리고 Document base에서 서버에 구동한 프로젝트의 웹 기본 폴더(보통은 web 또는 이클립스는 기본으로 WebContent) 를 선택합니다.
사용자 삽입 이미지

Auto reloading enabled는 선택해도 되고 안해도 되지만 하지 않겠습니다. 그다음 서버를 구동합니다.
사용자 삽입 이미지

웹 브라우저에서 localhost:8080 으로 접속하면 해당 웹 프로젝트의 index 페이지를 볼 수 있습니다.
만약에 콘솔에 다음과 같은 에러가 보인다면 Docbase가 잘못 설정된 것입니다.
심각: Error starting static Resources java.lang.IllegalArgumentException: Document base D:\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\studywith does not exist or is not a readable directory


top


인터페이스를 사용할 때와 안 할 때의 테스팅 차이

모하니?/Coding : 2007.08.22 14:49


/**
 * KnightOfTheRoundTable 클래스 단위 테스트 불가능.
 * HolyGrailQuest 클래스에 종속성을 가지고 있기 때문에
 * 1. HolyGrailQuest 클래스까지 같이 테스트하게 된다.
 * 2. 여러 경우(null을 반환하거나, exception 발생 시키거나, 제대로 된 경우)를 테스트 할 수 없다.
**/
public class KnightOfTheRoundTableTest extends TestCase {
    public void testEmbarkOnQuest() throws GrailNotFoundException {
        KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");
        HolyGrail grail = knight.embarkOnQuest();
        assertNotNull(grail);
        assertTrue(grail.isHoly());
    }
}

/**
 * Quest 인터페이스를 사용했기 때문에
 * 1. 종속성을 가지는 인터페이스(Quest)의 Mock 객체를 만들어서 단위 테스트를 할 수 있다.
 * 2. 여러 Mock 객체를 만들어서 다양한 상황을 테스트 할 수 있다.
**/
public class KnightOfTheRoundTableTest extends TestCase {

    //Best Case :: embarkOnQuest()에서 HolyGrail 반환 받는 경우.
    public void testEmbarkOnQuest() throws GrailNotFoundException {
        KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");

        Quest mockQuest = createMock(Quest.class);
        expect(mockQuest.embark()).andReturn(new HolyGrail());
        replay(mockQuest);

        knight.setQuest(mockQuest);
        HolyGrail grail = (HolyGrail) knight.embarkOnQuest();
        assertNotNull(grail);
        assertTrue(grail.isHoly());
    }

    //Bad Case :: embarkOnQuest()에서 null 반환 받는 경우.
    public void testEmbarkObQuest2() throws Exception {
        KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");

        Quest mockQuest = createMock(Quest.class);
        expect(mockQuest.embark()).andReturn(null);
        replay(mockQuest);

        knight.setQuest(mockQuest);
        Object grail = knight.embarkOnQuest();
        assertNull(grail);
    }

    //Bad Case :: embarkOnQuest()에서 GrailNotFoundException 예외 발생
    public void testEmbarkOnQuest3() throws Exception {
        KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");

        Quest mockQuest = createMock(Quest.class);
        expect(mockQuest.embark()).andThrow(new GrailNotFoundException());
        replay(mockQuest);

        knight.setQuest(mockQuest);
        try{
            knight.embarkOnQuest();
            fail();
        } catch (GrailNotFoundException e) {

        }
    }
}

얼핏 아래의 코드가 더 길고 코딩하기 귀찮아 보이지만, 그렇게 코딩이라도 해서 테스트를 해볼 수 있다는 것이 어딜까요?
사용자 삽입 이미지
위에 있는 코드는 바로 위의 그림처럼 테스트 대상인 KnightOfTheRoundTable 클래스가 종속하고 있는 HolyGrailQuest까지 테스트에 포함이 되게 됩니다. 원하던 상황이 아닙니다. KnightOfTheRoundTable 클래스에서 사용하고 있는 HolyGrailQuest의 행위 하나에 종속이 되게 됩니다. 테스트 할 수 있는 여지가 매우 적어집니다.
사용자 삽입 이미지
하지만 이렇게 중간에 Quest라는 인터페이스를 두기만 하면(코딩도 별로 어렵지 않습니다. 그냥 인터페이스 만들고 그거 implements하게만 하면 되죠.) EasyMock 같은 걸로 Mock 객체 만들어서 Quest를 구현한 클래스가 없더라도 KnightOfTheRoundTable 클래스의 테스트가 가능합니다. Mock 객체의 행동을 맘대로 정할 수 있기 때문에 테스트 할 여지도 많아지죠.

인터페이스 하나 추가로 이렇게 테스팅이 유연해 지는데 어떻게 안 쓸 수 있을까요. 사실 테스팅이 유연해 진 이유는 인터페이스 사용으로 인해 종속성이 완화 되었기 때문이지만 말이죠.
top


단어장

모하니?/Coding : 2007.08.15 09:54


번역을 하거나 독해를 할 때 자주 나오는데 뜻이 햇갈리는 단어, 한글로 옮기기 힘든 단어, 어떻게 읽어야 할지 고민되는 단어 등을 정리하고 공유할 수 있는 애플리케이션을 만들고 있습니다.

처음엔 매우 간단할 거라고 생각하고 심심풀이로 만들어 볼 생각이였는데, 생각하면 생각할 수록 새로운 기능들이 머리속에 떠올라서 점차 간단하지 않은 애플리케이션 쪽으로 가고 있습니다. 따라서 정리를 해봅니다.

1. 영어, 독음, 한글 뜻. 이렇게 세 쌍을 입력할 수 있어야 한다.
    1-1. 아무나 간단하게 입력한다.
    1-2. 영어가 중복되면 안 된다.
2. 검색할 수 있어여 한다.
    2-1. 검색어를 한글자씩 입력할 때마다 DB에 등록된 단어가 뭐가 있는지 바로 알 수 있게 AJAX 쓰면 좋겠다.
    2-2. 단어가 많아지면 디렉터리 검색 방식도 제공한다. A(23), B(10), ... Z(3) 이런식으로 각 알파벳 단어의 갯수를 표시해준다.
3. 최근 등록된 단어 목록을 보여준다.
    3-1. RSS 피드로 구독 할 수 있으면 좋겠다.

top


제 3회 스프링 세미나 발표 예제 코드.

모하니?/Coding : 2007.07.28 00:18



KSUG에 파일 업로드가 되지 않아서;; 일단 급한대로 제 블로그에 올려두었습니다.
위의 기본 예제코드를 개선해 나가는 과정을 발표할 것입니다.

1차 코드 개선 결과 코드(MutiActionController 적용)
2차 코드 개선 결과 코드(컨트롤러 추상화)
3차 코드 개선 결과 코드(추가/수정 컨트롤러 단일화)

발표자료


top


WhiteshipController

모하니?/Coding : 2007.07.17 17:10


지금 만든 Controller는 사실은 원래 계획 했던 컨트롤러는 아닙니다. 원래 계획 했던 컨트롤러는 폼 요청이 여러개가 올 수 있는 컨트롤러 입니다. 하지만 지금 만든 컨트롤러는 폼 처리 요청 하나와 폼처리 요청이 아닌 요청들 다수를 묶을 수 있는 컨트롤러 입니다.

사용자 삽입 이미지
제가 만든 컨트롤러는 즉 위의 그림에 보이듯이 하나의 폼 처리 요청을 하는 "SimpleFormController + 알파" 를 한 것입니다. 따라서 폼 처리 워크 프로우가 필요한 요청 하나와 그렇지 않은 요청들 다수를 하나의 컨트롤러에서 처리할 수 있습니다.
사용자 삽입 이미지
사용하는 방법은 위의 그림과 같이 SimpleFormController를 사용하는 것과 동일하게 사용하면서 MultiActionController를 사용하는 것과도 역시 동일하게 사용할 수 있습니다.

WhiteshipController 자체 구현은 매우 간단하게 했습니다. Copy&Paste로...ㅋ;;
1. SimpleFormController 복사
2. 새로 만든 클래스에 복사
3. Alt + Shift + R을 사용하여 WhiteshipController로 바꾸기
4. MuliActionController 복사
5. 새로 만든 클래스에 복사
6. 4번 과정에서 붙여 넣은 createBinder 메소드 주석처리 또는 사게
7. 새로 만든 클래스에서 handleRequestInternal 재정의
(재정의를 통해서 요청을 처리할 메소드가 있으면 MultiActionController의 요청 처리 플로우를 타고 없으면 SImpleFormController의 요청 처리 플로우를 타도록 약간 수정 해주시면 됩니다.)
8. 구현 끝.. 테스트..

생각해 볼 것
1. 위의 구현 과정 중에 7번을 좀 더 다듬어야 함. 현재는 요청을 처리할 메소드를 못찾은 경우를 아예 SImpleFormController의 워크 플로우를 타도록 대체 했는데 사실 이러면 안됨.. 진짜로 요청을 처리할 메소드를 못찾을 경우도 고려해야 함.
2. 복사, 붙여 넣기로 난장판이 된 코드 다듬기

ps : 아직 제대로 만든 것이 아니라서 공개하기에는 상당히 부끄럽습니다. 나중에 좀 더 다듬게 되면 올리겠습니다.

top


SimpleFormAndMultiActionController 개발의 어려움

모하니?/Coding : 2007.07.17 16:58


생각했던 Controller를 개발하기 어려웠던 점은 일단 제 자신의 구현 능력이 그만큼 부실하다는 것이 가장 큰 이유이며 그에 파생되어 나오는 이유로는 저 컨트롤러를 사용하는 입장에서 어떻게 사용할 것이냐 하는 것을 고민이 됐습니다.

폼 처리 워크 플로우가 있으려면 폼뷰, 커맨드 객체의 클래스, 이름, Success 뷰가 필요하고 기타 바인더나 프로퍼티 에디터, formBackingObject 마지막엔 onSubmit 까지 다양한 메소드와 변수가 필요합니다. 이 모든 것을 MultiActionController의 형태로 쓰려면 메소드 하나 안에 모두 들어가야 한다는 것인데... 이부분이 가장 큰 걸림돌이였습니다.
사용자 삽입 이미지
즉 저 파란 부분을 아래와 같이 하나의 메소드 안으로...;;
사용자 삽입 이미지
구상한 컨트롤러가 있다고 치고 코딩을 한 것인데요. 메소드 하나 안에 변수 설정과 formBackingObject 메소드에서 하는 일 그리고 onSubmit에서 하는 일이 들어있습니다. 이때 바로 난감해 집니다.

하나의 메소드에 세 부분(변수 설정, 폼백킹, 온서밋)이 들어있습니다. 이 세 부분은 각각 워크 프롤우에 따라 모두 다 같이 실행이 되는 것이 아니라 어떤 때 는 폼백킹만 실행을 하고 어떤 때는 온서밋을 실행하고 종료하게 될 수 있다는 것입니다.

그럼 이 쯤에서 새로운 컨트롤러를 사용하는 방법을 바꿔야겠다는 생각이 듭니다.
사용자 삽입 이미지
즉 이런 모양이 되겠죠. 어떤 탬플릿을 가진 클래스가 있고 그 클래스의 훅 들을 구현하는 식으로 메소드 안에서 익명 클래스를 만들어서 사용하는 형태입니다. 이렇게 복잡하게 사용할 바에는 차라리 SimpleFormController를 그냥 쓰는 것이 좋겠다는 생각이 드는 것은 저뿐이 아니라 생각 됩니다.

그래서... 원래 만들고자 했던 컨트롤러는 포기하고 약간 다른 컨트롤러를 만들었습니다.


top


SimpleFormAndMultiActionController 개발 구상

모하니?/Coding : 2007.07.17 16:20



MultiActionController를 사용하면 여러 요청을 하나의 컨트롤러에서 다룰 수 있습니다. 하지만 폼 처리를 하는 Work Flow가 정의되어 있지 않기 때문에 SimpleFormController와 같이 명석하게 폼 처리를 하는 일을 못합니다.

0. Form 뷰와 Success 뷰, 이렇게 두 개의 뷰를 왔다 갔다하는 로직이 없다.
1. 위의 문제로 인해 binding과 validation을 지원하고는 있지만 Errors 객체에 들어있는 정보를 폼에 다시 보여줄 길이 없다.
2. 위의 문제로 인해 MultiActionController로 묶을 수 있는 요청의 형태가 제한적이다.

따라서 폼 처리를 할 수 있는 MultiActionController를 개발하면 요청의 형태에 제약을 받지 않고 특정 규약(예를 들어 한 화면 또는 하나의 구역에 있는 링크들을 하나의 컨트롤러를 구성한다거나 하나의 도메인과 관련된 요청들을 하나의 컨트롤러로 구성하는 등의 규약)에 따라 컨틀롤러를 구성할 수 있는 것이 가능할 것입니다. 물론 어떻게 구성하느냐에 따라 줄어드는 컨트롤러의 양의 차이가 있겠지만 지금 보다 더 줄일 수 있는 것은 사실이겠죠. 머릿속으로 이 방법 저 방법이 막 떠오르다가 '아니야.. 그렇게 하면 안되겠는데, 이렇게 하면 될까?' 이렇게 생각만 하다가 이제야 겨우 소스코드를 열었습니다. 진작에 소스코드를 열어 봤다면 공상을 줄일 수 있었을텐데 하는 아쉬움이 남지만.. anyway, 소스코드를 열고 보니..

0. 일단 MultiActionController와 SimpleFormController의 동작을 파악하기
1. 둘을 어떻게 합칠 것이지 고민하기
2. 합치기

이렇게 할 일이 단순해졌습니다. 이미 1번은 머릿속으로 계속 하고는 있었지만 소스 코드를 보면서 이론으로 알고 있던 동작을 실제 나사와 톱니가 맞물려 돌아가는 것을 보는 듯한 느낌으로 살펴보니 저 둘을 어떻게 합칠 것인지 대강 확신이 서기 시작했습니다.

이제 실제 구현을 해봐야겠습니다.
top


ServletRequestUtils 사용하기

모하니?/Coding : 2007.07.10 15:31


Spring 소스 코드 샘플을 보다가 발견한 유틸 클래스 입니다.

request로 부터 파라미터를 받아와서 적절한 타입으로 형변환을 하는 일들은 자주 벌어지기 마련입니다. 그리고 굉장히 코딩하기가 귀찮은 부분 중 하나입니다. 예를 들어 다음과 같이 코등하는 일이 빈번합니다. Integer id =  Integer.parseInt(request.getParameter("id"); 매우 귀찮은 일이죠.

이 때 사용할 수 있는 유틸리티 클래스가 바로 ServletRequestUtils 입니다.
이 녀석의 전신은 RequestUtils 이지만 Spring 2.0부터는 ServletRequestUtils를 사용하도록 deprecated 됐습니다.

사용전
    public ModelAndView view(HttpServletRequest request, HttpServletResponse response){
        Integer memberId = Integer.parseInt(request.getParameter("memberId"));
        Member member = memberService.get(memberId);
        return new ModelAndView("view").addObject("member", member);
    }

사용후
    public ModelAndView delete(HttpServletRequest request, HttpServletResponse response) throws ServletRequestBindingException{
        Integer memberId = ServletRequestUtils.getIntParameter(request, "memberId");
        memberService.delete(memberId);
        return new ModelAndView("redirect:/member/list.html");
    }

형변환 할 때 예외가 발생할 수 있기 때문에 메소드에서 예외를 던지도록 수정합니다.

boolean, float, double, int, long, String 타입으로 받아 올 수 있으며 이 타입들의 배열([]) 타입으로도 바로 바인딩해서 넘겨 받을 수 있습니다. 꽤 편하겠죠?ㅋㅋ

String 쪽에 한가지 추가하고 싶은 API가 있는데...현재는 아래의 모습의 메소드가 있습니다.
static String     getStringParameter(ServletRequest request, String name)

제가 원하는 API는
static String     getStringParameter(ServletRequest request, String name, String encoding)

그래서 아래와 같이 사용할 수 있도록..
String koreanName = ServletRequestUtils.getIntParameter(request, "name", "UTF-8")

지금은 한글을 입력받으려면 복잡한 수를 써야 합니다. 도저히 제 머리로는 외울 수 없는;;;
String name = new String(request.getParameter("name").getBytes("8859_1"), "UTF-8");
이런 코딩을 해줘야 하는데 이런 것을 저 Util 클래스 내부로 감춰놓고 사용하고 싶네요.

만들어 달라고 하면 만들어 줄까요?? Issue Tracker에 올려볼까요. 흠...

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

단어장  (0) 2007.08.15
제 3회 스프링 세미나 발표 예제 코드.  (6) 2007.07.28
WhiteshipController  (0) 2007.07.17
SimpleFormAndMultiActionController 개발의 어려움  (0) 2007.07.17
SimpleFormAndMultiActionController 개발 구상  (0) 2007.07.17
ServletRequestUtils 사용하기  (6) 2007.07.10
DB 바꾸기(Hibernate 사용할 때)  (0) 2007.06.27
Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
top


DB 바꾸기(Hibernate 사용할 때)

모하니?/Coding : 2007.06.27 14:31


Hibernate를 사용하지 않으면 모든 SQL을 전부 손봐야 할지도 모르겠지만...
Hibernate와 같은 ORM을 사용하면 얘기가 달라집니다. 필요한 것은 딱 두 개

1. 설정 내용 변경
2. 필요한 jdbc jar파일 추가

MySQL -> PostgreSQL로 변경합니다.
1. 설정 내용 변경
#
# data source jdbc connection
#

db.driver=org.postgresql.Driver
db.url=jdbc:postgresql://localhost/seminar
db.username=jedi
db.password=

#
# hibernate
#

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

2. 필요한 jar 파일 추가
필요한 jdbc가 바꼈기 때문에 각 DB밴더 에서 제공해 주는 jar파일을 프로젝트의 클래스패스에 추가해줘야 합니다.
사용자 삽입 이미지

top


Spring MVC 리팩토링 4

모하니?/Coding : 2007.06.22 17:39


이전에서 단위테스트를 통해 Controller과 Service, DAO 계층을 테스트 했습니다.
  1. 13:17:31 Spring MVC 리팩토링 3
  2. 13:04:41 Spring MVC 리팩토링 2
  3. 12:28:58 Spring MVC 리팩토링 1
사실 DAO 계층 테스트를 단위테스트라고 할 수 있을지 모르겠지만 테스트용 DB와 테스트 데이터를 별로도 사용해서 실제 DB에는 전혀 지장이 없었습니다. 그리고 spring-mock.jar에 있는 AbstractTransactionalDataSourceSpringContextTests를 사용하여 자동 롤백이 되도록 하여 테스트의 흔적이 테스트 DB에 남지도 않았습니다. 그러면서도 DAO를 충분히 테스트 할 수 있었습니다.

이제는 Service Layer에서 DAO 계층과 함꼐 테스트를 해보고 그 다음은 Controller 에서 그 아래에 있는 Service 계층과 DAO 계층을 아울러서 테스트 할 차례입니다.
사용자 삽입 이미지
먼저 위 그림의 파란색은 이전에 테스트를 했고 빨간색 부분의 테스트를 작성합니다. 이 때도 이전 글에서 사용한 osaf의 클래스를 사용합니다.

public class MemberServiceIntegrationTest extends AbstractTransactionalDataSourceSpringContextControllerTest{

    protected MemberService memberService;

    @Override
    protected String[] getConfigLocations() {
        return new String[] {
            "file:web/WEB-INF/spring/applicationContext-dao.xml",
            "file:test/applicationContext-jdbc-datasource.xml",
            "file:web/WEB-INF/spring/dao/daoContext-member.xml",
            "file:web/WEB-INF/spring/application/applicationContext-member.xml",
        };
    }

    @Override
    protected void onSetUpInTransaction() throws Exception {
        insertFlatXmlDataSet("test/kr/co/springframework/member/dao/memberData.xml");
    }

    public void testDI() throws Exception {
        assertNotNull(memberService);
    }

    public void testGetAll() throws Exception {
        assertEquals(1, memberService.getAll().size());
    }

    public void testNotJoinedMail() throws Exception {
        Member member = memberService.isJoined("");
        assertEquals(null, member);
        assertEquals("기선", memberService.isJoined("keesun@mail.com").getName());
    }
}

간단하게 테스트 할 수 있습니다. 단위 테스트하고의 차이는 Spring의 컨테이너를 사용하여 의존성이 삽입된 객체를 사용하고 있으며 DB역시 DBUnit을 이용하여 사용하고 있습니다. 테스트용 DB라는 것만 빼고는 전부 같습니다. 아... 테스트용 DB(HSQL)와 실제 개발 DB(MySQL)와 다르군요. 하지만 ORM(하이버네이트)를 사용하고 있기 때문에 전~혀 문제가 없습니다. 테스트용 database.properties 파일 하나만 작성해주면 되죠.

사용자 삽입 이미지
이번에는 컨트롤러를 테스트 합니다. 이것도 역시 이전에 파란색 박스는 테스트를 했고 이번에는 빨간 박스를 테스트 합니다.

    public void testEmptyOrWhiteMail() throws Exception {
        mail = "";
        MemberCommand command = new MemberCommand();
        command.setMail(mail);
        mav = checkController.onSubmit(null, null, command, null);
        assertEquals("redirect:join.html", mav.getViewName());
    }

    public void testExistMemberMail() throws Exception {
        mail = "keesun@mail.com";
        mav = checkController.onSubmit(null, null, command, null);
        Map model = mav.getModel();
        assertNotNull(model.get("member"));
        assertEquals("confirm", mav.getViewName());
    }

서비스 계층의 통합 테스트 코드와 상당부분이 같기 때문에 테스트 메소드만 붙여넣었습니다. (테스트 코드에서만 볼 수 있는)단위 테스트와의 차이는 easymock의 사용이 없어졌다는 것입니다. 이전에는 MemberService의 Mock객체를 만들어 사용했었습니다. 하지만 이제는 진짜 MemberSerive 객체를 사용하고 있죠.

이것으로 매~~~우 간단한 리팩토링(메소드 이름 하나 바꾸는 것)을 매우 길~~~~게 돌아오며 테스트를 연습해 봤습니다.

요약 및 느낀점
- Easy Mock은 생각보다 간단하다.(createMock() -> expect() -> replay() -> verify() -> reset())
- spring-mock.jar 에 유용한 Mock 객체들(Spring MVC 책에서는 Stub이라고 얘기하고 있지만)이 많이 있는데 그 중에 자주 사용할 것은 MockHttpServletRequest와 MockHttpServletResponse
- 계층 별 테스트 할 대상, 목록, 요점을 명확히 해야겠다.
- 테스팅(단위 테스트와 통합 테스트)은 확실히 애플리케이션에 자신감을 불어넣어준다.
- View를 테스트 하는 방법은 무엇일까? (Spring view test라고 구글링을 해봤지만 원하는 결과는 못봤다.)
- 컨트롤러를 테스트 할 때 데이터 바인딩과 벨리데이션 역시 테스트 해야겠다.
- 테스트 코드를 작성할 때 참고할 명세서가 있어야겠다.(비즈니스 로직을 자세히 설명한 어떤 문서...)

top


Spring MVC 리팩토링 3

모하니?/Coding : 2007.06.22 13:17


이전 글에서 Controller와 Service Layer를 단위 테스트 했습니다. 이번에는 DAO Layer를 단위(?) 테스트 하겠습니다.

이번에는 OASF를 사용하여 DBUnit과 spring-mock.jar에 있는 AbstractTransactionalDataSourceSpringContextTests 를 확장한 클래스를 사용하겠습니다. 토비님께서 만드셨는데 DBUnit 사용하기도 좋고 설정파일들의 위치를 특정 컨벤션만 지켜주면 알아서 읽어들입니다.

하지만 제가 테스트 하는 애플리케이션은 OSAF 설정파일들의 컨벤션을 알지 못했기 때문에;; 그냥 getConfigLocations 를 사용해서 필요한 설정 파일들과 테스트 DB를 사용하도록 합니다. 테스트 DB는 java 6에 내장되어 있는 HSQL을 사용합니다.

먼저 테스트용 데이터를 XML 형식으로 준비합니다.
<dataset>
    <member id="1" name="기선" mail="keesun@mail.com" mobile="111-1111-1111" job="BIT" springAtHome="dd" springAtWork="dd" springModules="dd" attendType="N" />
</dataset>

다음 테스트를 작성합니다.
    public void testFindByMail() throws Exception {
        insertFlatXmlDataSet("test/kr/co/springframework/member/dao/memberData.xml");
        Member member = memberDao.findByMail("keesun@mail.com");
        assertNotNull(member);
        assertNull(memberDao.findByMail(""));
    }

테스트가 통과 하도록 findByMail을 구현합니다.
    @SuppressWarnings("unchecked")
    public Member findByMail(final String mail) {
        List<Member> memberList = getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session s) throws HibernateException, SQLException {
                Query q = s.createQuery("from Member m where m.mail = :mail")
                    .setParameter("mail", mail);
                return q.list();
            }
        });
        if(memberList.size() == 0)
            return null;
        else
            return memberList.get(0);
    }

사용자 삽입 이미지

단위 테스트가 끝났습니다. 이제 남은 건
1. Service Layer에서 통합테스트를 해보고
2. Controller Layer에서 통합테스트를 하면

테스트&구현이 끝이납니다. 그럼 톰캣 돌려서 돌려보면 끝이겠지만 지금도 어느정도 잘 돌아갈 것 같은 자신이 생긴 상태입니다. 나머지는 밥먹고..계속 합니다.
top


Spring MVC 리팩토링 2

모하니?/Coding : 2007.06.22 13:04


이전 글에서 Controller 단위 테스트를 변경하여 Controller 수정에 성공했습니다.
이번에는 MemberService를 단위 테스트 해서 MemberService의 구현을 수정하겠습니다.

이번에는 MemberService의 테스트가 만들어져 있지 않은 것을 확인했습니다. 만들어야겠습니다.

public class MemberServiceImplTest {

    private MemberService memberService;
    private MemberDao memberDao;

    @Before
    public void setUp() {
        memberService = new MemberServiceImpl();
        memberDao = createMock(MemberDao.class);
        memberService.setMemberDao(memberDao);
    }

    @Test
    public void testNotJoinedMail() {
        String mail = "";
        expect(memberDao.findByMail(mail)).andReturn(null);
        replay(memberDao);
        Member member = memberService.isJoined(mail);
        assertEquals(null, member);
        verify(memberDao);
    }
}

일단 Bad Case를 테스트 하는 코드를 작성했습니다.

사용자 삽입 이미지
이전 글에서 컨트롤러를 테스트 할 때 인터페이스 만들고 구현체에서 에러 나길래 퀵픽스로 구현해둔 곳에서 에러가 발생했습니다.

이 방법은 KSUG 2회 세미나에서 영회형이 발표하실 때 언급하셨고 J2EE Development without EJB에서 Rod Johnson이 추천하는 방법이기도 합니다.

어쨋든 테스트가 돌아가도록 ServiceImple 구현을 변경합니다.

이런 여기서도 또하나 결정을 해야겠군요. Service 계층에서 사용할 DAO의 인터페이스를 결정해야 합니다. 이전에는 findByMail을 사용하고 있었는데 썩 나쁘지 않은 것 같습니다. 따라서 그대로 사용을 하고 대신 리턴 타입을 List<Member> 로 할지 Member로 할지 고민이 됩니다. 같은 mail을 가진 데이터가 들어갈 수 없다는 제약 사항이 있다면 리턴 타입을 Member로 해야하고... 혹시라도 그런 제약 사항이 없다면 List<Member>로 받아서 뭔가 복잡한 처리가 필요하면 View까지 그 영향이 갈 것 같네요;;;

이번에도 저는 혼자 개발하고 있기 때문에 다음과 같은 장점이 있습니다.

"니가 곧 개발자며 사용자이자 설계자다. 니가 다 알아서 해라! 그리고 심심하면 DBA도 해라."

OK 저는 같은 mail을 가진 Member 데이터가 데이터베이스에 들어가지 않기로 정했습니다. 이 제약 사항은 다음에 DAO를 테스트할 때 확인 해야 겠습니다.

    public Member isJoined(String mail) {
        return memberDao.findByMail(mail);
    }

Service 구현은 여전히 변함없이 간단합니다. 이 순간 저는 지금 상당히 많이 돌아가고 있다는 것을 느낍니다. 단지 메소드 이름 하나 변경하는 일이였는데 너무 돌아가는 거 아닌가 싶지만 목적은 구현보다는 테스트에 익숙해지는 것이기 때문에 계속해서 진행하겠습니다.

이번에는 새로운 테스트를 작성하여 해당 mail을 가지고 있는 Member를 검색했을 경우를 테스트 합니다.

    @Test
    public void testJoinedMail() {
        String mail = "keesun@mail.com";
        Member member = new Member();
        member.setMail(mail);
        expect(memberDao.findByMail(mail)).andReturn(member);
        replay(memberDao);
        assertEquals(member, memberService.isJoined(mail));
        verify(memberDao);
    }

테스트는 통과합니다.

사용자 삽입 이미지

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

SimpleFormAndMultiActionController 개발 구상  (0) 2007.07.17
ServletRequestUtils 사용하기  (6) 2007.07.10
DB 바꾸기(Hibernate 사용할 때)  (0) 2007.06.27
Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
top


Spring MVC 리팩토링 1

모하니?/Coding : 2007.06.22 12:28


Service Layer의 인터페이스를 살펴보니 다음과 같습니다.
사용자 삽입 이미지
마지막에 이는 저 메소드의 이름과 하는 일이 맘에 안듭니다. 구현도 제가 했고 구현한지 2틀 정도밖에 안됐습니다. 더 늦기 전에 수정해야겠습니다.

public Member findByMail(String mail)

다분히 DAO Layer에 있어야 할 것 같은 이름이며 하는 일 역시 그저 DAO 계층에 있는 메소드 하나를 호출하는 겁니다. 본래 저 메소드를 만들게 된 의도는 해당 mail로 가입한 멤버가 있는지 확인하려는 의도였습니다. 하지만 저 메소드의 이름으로는 도무지 그런 의도가 안보입니다. 따라서 다음과 같이 수정해야 겠습니다.

public boolean isJoinedMail(String mail)

이러한 인터페이스로 변경되어야 겠습니다. 근데 어디 부터 손을 대야 할까요;;;;

일단 Controller로 가봐야겠습니다. 그곳에서 Service Layer의 메소드를 호출하고 있기 때문에 그곳에서 새로운 isJoinedMail(mail)을 호출하도록 수정하여 구현을 변경하면 될 것 같습니다.

아니군요. Controller로 바로 가지 않고 해당 컨트롤러의 테스트 클래스로 갑니다. 이 곳이야 말로 진짜 리팩토링을 시작할 지점입니다.

수정 전 테스트 메소드 입니다.

    public void testEmptyOrWhiteMail() throws Exception {
        mail = "";
        expect(mockMemberService.findByMail(mail)).andReturn(null);
        replay(mockMemberService);
        command.setMail(mail);
        ModelAndView mav = controller.onSubmit(null, null, command, null);
        assertEquals("redirect:join.html", mav.getViewName());
        assertViewName(mav, "redirect:join.html");
        verify(mockMemberService);
    }

수정 후 테스트 메소드 입니다.

public void testEmptyOrWhiteMail() throws Exception {
        mail = "";
        expect(mockMemberService.isJoined(mail)).andReturn(false);
        replay(mockMemberService);
        command.setMail(mail);
        ModelAndView mav = controller.onSubmit(null, null, command, null);
        assertEquals("redirect:join.html", mav.getViewName());
        assertViewName(mav, "redirect:join.html");
        verify(mockMemberService);
    }

매우 간단한 변경입니다. 위에서 언급한 것과 하나 차이가 있다면 메소드 명을 isJoined()로 변경하였습니다. 어차피 가입 확인을 mail로 만 할 것이기 때문에 굳이 적어줄 필요가 없다고 느꼈습니다. 이 메소드를 사용할 때 다음과 같이 이쁜 문장이 만들어 지겠죠. :-)

isJoined(mail);

사용자 삽입 이미지

테스트가 실패한 이유는 컨트롤러를 테스트 할 때(onSubmit()이 호출 됐을 때) 각본에는 MemberService의 isJoined()가 주인공으로 열연을 펼친뒤 false를 리턴하라고 했는데 실제로 재생을 해보니까 주연은 커녕 엑스트라로도 보이지 않았기 때문입니다.

자 이제 이 테스트가 돌아갈 수 있도록 컨트롤러의 구현을 변경합니다.

    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException exception) throws Exception {
        MemberCommand memberCommand = (MemberCommand)command;
        if(memberService.isJoined(memberCommand.getMail()) != null)
            return new ModelAndView(getSuccessView())
                .addObject("member", member);
        else
            return new ModelAndView("redirect:join.html");
    }

이런 문제가 생겼습니다. isJoined() 메소드를 사용하여 boolean 값을 받아왔는데 그걸로는 View에 넘겨줄 정보가 부족합니다. 다시 말하면 View에서는 member 객체를 필요로 하는데 여기서는 그 객체를 받아 오려면 memberService에 새로운 mail 로 해당 member를 가져오는 것을 구현해야 할 것 같습니다.

이럴 때 혼자 개발할 때의 장점이 저를 살려줍니다.

"니가 곧 개발자며 사용자이자 설계자다. 니가 다 알아서 해라!"

저는 isJoined(mail)의 인터페이스를 다시 한번 변경합니다.

public Member isJoined(mail)

만약 해당 mail 이 없다면 null을 반환합니다. 따라서 컨틀롤러의 테스트 코드르 다시 수정합니다.

    public void testEmptyOrWhiteMail() throws Exception {
        mail = "";
        expect(mockMemberService.isJoined(mail)).andReturn(null);
        replay(mockMemberService);
        command.setMail(mail);
        ModelAndView mav = controller.onSubmit(null, null, command, null);
        assertEquals("redirect:join.html", mav.getViewName());
        assertViewName(mav, "redirect:join.html");
        verify(mockMemberService);
    }

이제 다시 컨트롤러의 구현을 수정합니다.

    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException exception) throws Exception {
        MemberCommand memberCommand = (MemberCommand)command;
        Member member = memberService.isJoined(memberCommand.getMail());
        if(member != null)
            return new ModelAndView(getSuccessView())
                .addObject("member", member);
        else
            return new ModelAndView("redirect:join.html");
    }

끝!! 테스트는 돌아갑니다.

사용자 삽입 이미지

이제 남은 일은 Controller를 단위 테스트가 아닌 통합 테스트를 해보는 것입니다. 그러면 당연히 에러가 나겠죠. 아직 MemberService와 MemberDAO 구현을 변경하지 않았기 때문이죠. 이 때 두가지 선택 사항이 있습니다.

1. Controller를 통합 테스트 해버려서 구현한다
2. MemberService를 단위 테스트 -> MemberDAO를 단위(?) 테스트 -> MemberService 통합 테스트 -> Controller 통합테스트

두 가지 선택 사항이 있습니다. 테스팅 공부를 제대로 할려면 돌아가야겠죠. 저는 2번을 선택하겠습니다.

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

ServletRequestUtils 사용하기  (6) 2007.07.10
DB 바꾸기(Hibernate 사용할 때)  (0) 2007.06.27
Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
top


Seminar Helper v0.8

모하니?/Coding : 2007.06.20 12:40


1. 퀴즈 참가자 인원수 설정 기능
사용자 삽입 이미지
Winner(뽑힌 사람 수/뽑아야 하는 사람 수/대상 인원)
형태로 보여주고 퀴즈를 시작하기 전에 "뽑아야 하는 사람 수"를 설정할 수 있도록 변경.

2. 로그인 구현
전체 참가자 리스트를 관리자만 볼 수 있도록 프로그램을 변경해야 함.

3. 관리자 페이지 추가
3-1. 장소 안내 링크 변경
3-2. 세미나 접수 시작 & 종료 설정
3-3. 현장 접수 시작 & 종료 설정

4. 상단에 메뉴 필요함
URL 직접 입력으로 네비게이션을 했는데;; 너무 불편함.

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

DB 바꾸기(Hibernate 사용할 때)  (0) 2007.06.27
Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
Gmail 계정 사용해서 이메일 보내는 프로그램  (6) 2007.05.23
top

TAG TODO

어떤 계층 순서대로 구현 하시나요?

모하니?/Coding : 2007.06.18 21:53


다양한 계층이 존재하는데 과연 어떤 계층의 테스트 코드 부터 작성해야 할 것인가 하는 것이 고민이 되서 적어 봅니다.

개발을 할 때 나뉘는 주요 계층들은 보통 Spring MVC책에 나와있는 계층과 비슷할 것입니다.
사용자 삽입 이미지
다른 모든 계층에서 사용될 수 있는 도메인 계층
JSP와 같이 사용자와 접촉을 하는 지점인 UI 계층
UI 계층과 Service 계층을 이어주고, 사용자의 입력을 바인딩, 검증 하는 등의 책임이 있는 Web 계층
Web 계층과 Persistence 계층을 이어주며 그 사이에서 데이터를 가지고 별도의 작업을 수행하는 Service 계층
데이터베이스에 쿼리를 날리는 Persistence 계층

이 중에서 어떤 순서대로 테스트를 작성해야 할지 고민이 됩니다. 테스트 작성이 곧 개발이기 때문에 이 질문은 다시 어떤 순서대로 개발을 해야 할지 고민하는 것이 됩니다.

도메인은 비즈니스 로직을 담고 있으며 모든 계층에서 사용해야 하기 때문에 즉 이말은 다시 모든 계층에서 필요로 하기 때문에 가장 먼저 개발 해야 할 것 같습니다.

그러나...사용자의 요구 사항을 바탕으로 비즈니스 로직을 구현하게 될 텐데 그 요구 사항이라는 것은 오히려 화면을 통해서 더 잘 뽑아 낼 수 있으니까 화면을 먼저 가장 먼저 개발 해야 할 것 같다는 생각도 듭니다.

하지만 DB가 역시 먼저 자리를 잡고 있어야 모든 계층을 구현할 때 안정적인 느낌이 들며  그래서 그런지 모르겠지만 항상 모델 -> DB만들기 -> DAO -> Service -> Controller -> JSP 순으로 보통 만들어 왔습니다.

이렇게 만들다 보면... DAO 계층에는 Service 계층에서 사용할 것들이 만들어져 있고 Service 계층에서는 Controller들에서 사용할 것들이 만들어져 있어야 할텐데 그러한 것들을 미리 예측해서 만들게 됩니다.

그러다 보면 결국 어떤 코드는 사용자의 요청 -> JSP -> Controller -> Service -> DAO 와 같이 이전과 완전히 반대 방향으로 구현이 진행되기도 합니다.

@.@

어떤 순서대로 구현해야 할까요;;;

잠정적인 결론
도메인 -> UI -> Controller -> Service -> DB 스키마 -> DAO

이때당시 이렇게 결론지은 이유.

UI가 꼭 최종 산출물인 JSP가 아니더라도, 손으로 그린 그림이든, HTML이나 PPT로 그린 프로토타입이 필요하다고 생각했습니다. 그걸 보고 컨트롤러에를 구성하고.. 쭈루룩.  내려가는 순이었는데. 막상 개발은 DAO부터 역행해서 하고 있었습니다. ㅋㅋ

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

Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
Gmail 계정 사용해서 이메일 보내는 프로그램  (6) 2007.05.23
앗.. 이런 바보;;  (2) 2007.05.15
top


Clover 설치하기

모하니?/Coding : 2007.06.18 16:25


1. 30일 짜리 라이센스 얻기
- 오픈 소스 프로젝트 검증을 거치면 공짜로 사용할 수 있는 것 같습니다.
사용자 삽입 이미지

2. 플러그인 다운 받기
- 다양한 IDE를 지원하고 있네요.
사용자 삽입 이미지

3. 플러그인 설치하기
- 2번에서 받은 압축 파일을 풀어서 "이클립스 설치 폴더"/plugins 폴더로 복사합니다.
- 1번에서 받은 라이선스 파일을 위에서 압축을 풀고 plugins에 넣어둔 폴더에 복사를 합니다.
사용자 삽입 이미지
- Eclipse를 사용중이라면 재시작합니다.

흠... 이 뒤로 Eclipse에서 사용해보려고 이것 저것 시도해봤지만;;; 실패..
http://www.cenqua.com/forums/thread.jspa?messageID=16616
위 링크와 같은 메시지를 보게 됩니다.

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

Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
Gmail 계정 사용해서 이메일 보내는 프로그램  (6) 2007.05.23
앗.. 이런 바보;;  (2) 2007.05.15
윽.. 순탄치 않아..ㅠ.ㅠ  (2) 2007.05.15
top

TAG Clover

16강 토너먼트 알고리즘

모하니?/Coding : 2007.06.12 21:26


아이고 머리야~
토너먼트 16강 대전 프로그램을 만들고 있는 중입니다.
이것 때문에 코드가 점점 복잡해 지고 있습니다.

간단하게 정리를 해봐야겠습니다.

1. 먼저 16강에 진출할 사람을 뽑습니다.
2. 16강에 진출한 사람 두 명이 대결을 펼치고 이긴 사람은 8강에 진출합니다.
3. 8강에 진출한 사람 두 명이 대결을 펼치고 이긴 사람은 준결승에 진출합니다.
4. 준경승에 진출한 사람 두 명이 대결을 펼치고 이긴 사람은 결승에 진출합니다.
5. 결승에 진출한 사람 두 명이 대결을 펼치고 이긴 사람은 짱입니다.

1. 16강에 진출할 사람 뽑기
- 간단한 조건을 통과하면 16강에 진출하게 됩니다.
    - 조건을 통과하면 '16강 리스트'에 추가.
    - '16강 진출 대상자' 리스트에서 제거.
    - 다른 사람의 조건 테스트
- 조건을 통과하지 못하면 '16강 진출 대상자'에서 제거하고 다음 사람에게 기회가 주어집니다.
    - '16강 진출 대상자' 리스트에서 제거.
    - 다음 사람의 조건 테스트
- 16명이 될 때 까지 반복합니다.
    - '16강 리스트'가 16이 되면 '16강 진출 대상자' 리스트 제거.
    - '16강 리스트'를 '8강 진출 대상자' 리스트로 사용
- 16명이 되기 전에 모든 인원이 조건을 통과하지 못하면 '16강 진출 대상자'를 리셋합니다.
    - '16강 진출 대상자' 리스트 갱신.
    - '16강 진출 대상자' 리스트에서 '16강 리스트'에 포함된 사람들 제거.
    - "16강 진출할 사람 뽑기" 전 과정 반복

2. 8강에 진출할 사람 뽑기
- '16강 리스트'에서 두명을 뽑아서 대결을 합니다.
    - '16강 리스트'에서 두명을 뽑습니다.
    - 무승부는 없습니다.
    - 이긴 사람을 '8강 리스트' 추가합니다.
    - 진 사람과 이긴 사람을 '16강 리스트'에서 제거합니다.
- '8강 리스트'가 8이 될 때 까지 반복합니다.

3. 준결승에 진출할 사람 뽑기
- 8강에 진출할 사람 뽑기와 동일합니다.

4. 결승에 진출할 사람 뽑기
- 준결승에 진출할 사람 뽑기와 동일합니다.

5. 짱 뽑기
- 역시 동일합니다.

시작이 반이라는 말이 근거없는 말이 아니였군요.

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

Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
Gmail 계정 사용해서 이메일 보내는 프로그램  (6) 2007.05.23
앗.. 이런 바보;;  (2) 2007.05.15
윽.. 순탄치 않아..ㅠ.ㅠ  (2) 2007.05.15
AJN Member Address Book  (0) 2007.05.15
top


GmailSender.java

모하니?/Coding : 2007.05.23 14:46



Test 코드는 다음과 같습니다.
    public void testSendEmail() {
        GmailSender sender = new GmailSender();
        //1
        String id = "gmail계정";
        String password = "gmail비번";
        //2
        String title = "Email test";
        String text = "Thank you for ordering 메일이 잘 가지?";
        //3
        String email1 = "whiteship2000@gmail.com";
        String email2 = "whiteship200@naver.com";
        String email3 = "whiteship2000@daum.net";
        List<String> emails = new ArrayList<String>();
        emails.add(email1);
        emails.add(email2);
        emails.add(email3);
        sender.setAccount(id, password);
        sender.setTitle(title);
        sender.setText(text);
        sender.setEmail(email1);
        sender.setEmails(emails);
        //4
        sender.send();
    }

사용법은 매우 간단합니다.
0. mail.jar 파일을 클레스패스에 넣어줍니다.
1. gmail 계정 세팅하고
2. 보낼 이메일 제목과 내용을 세팅할 수 있습니다.
3. 받을 사람은 email 하나를 세팅할 수도 있고 List<String> 타입으로 여러개의 email을 세팅할 수도 있습니다.
4. 마지막으로 보내면 됩니다.

문제
Gmail, Daum, Naver로 제대로 보내지는 것을 확인했습니다.
Gmail로 보낼 경우 한글이 깨지는 현상이 발생하는데 아직 해결책을 못찾았습니다;;

대엽님 도움으로 인코딩 문제 됐던 부분 수정해서 다시 올렸습니다. :)

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

Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
GmailSender.java  (2) 2007.05.23
Gmail 계정 사용해서 이메일 보내는 프로그램  (6) 2007.05.23
앗.. 이런 바보;;  (2) 2007.05.15
윽.. 순탄치 않아..ㅠ.ㅠ  (2) 2007.05.15
AJN Member Address Book  (0) 2007.05.15
Spring 설정파일에서 <util:list/> 사용하기  (0) 2007.04.29
top


Gmail 계정 사용해서 이메일 보내는 프로그램

모하니?/Coding : 2007.05.23 12:27


사용자 삽입 이미지
사용자 삽입 이미지

원래는 Spring 22장 Email을 보고 코딩하고 있었는데 잘 안되더군요. 그래서 Gmail 설정에 가서 도움말을 봤더니 SSL 인증을 사용하는데 그와 관련된 Spring 코드를 아직 찾아보지 않았습니다. MailSender 에서 SSL 설정하는 메소드가 있어야 할 것 같은데 일단 JavaMail API로 어떻게 돌아가는지 확인해 봤습니다.

JavaMail API 에서 SMTPSSLTransport 클래스가 MailSender의 하위 클래스인 JavaMailSenderImpl에 대응하는 클래스인데 특징은 이름에서 알 수 있듯이 SSL을 지원하는 클래스인 듯 하네요.
    public void greetingEmail2(MemberInfo memberInfo){
        URLName urln = new URLName("smtp", "smtp.gmail.com", 587, "", "", "");

        Properties props = new Properties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.starttls.enable","true");
        props.put("mail.smtp.host", "smtp.gmail.com");
        props.put("mail.smtp.auth", "true");

        Authenticator auth = new SMTPAuthenticator();
        Security.addProvider(new Provider());
        Session session = Session.getDefaultInstance(props, auth);
        SMTPSSLTransport trans = new SMTPSSLTransport(session, urln);

        trans.setStartTLS(true);
        Message message = new MimeMessage(session);
        try {
            message.setSubject("hi i'm keesun");
            message.setText("hehehehe");

            InternetAddress[] addressTo = new InternetAddress[1];
            addressTo[0] = new InternetAddress(memberInfo.getEmail());

            message.setRecipients(Message.RecipientType.TO, addressTo);

            trans.send(message);
            trans.close();
        }
        catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    private class SMTPAuthenticator extends javax.mail.Authenticator {

        public PasswordAuthentication getPasswordAuthentication() {
            String username = "Gmail 계정";
            String password = "Gmail 비번";
            return new PasswordAuthentication(username, password);
        }
    }

코드를 조금만 바꾸면 모든 회원에게 공지메일을 보낼 수도 있습니다.ㅋㅋ;;
코드를 조금 더 바꾸면 간단하게 사용할 수 있게 Util 클래스로 만들 수 있을 것 같습니다.

근데 Naver는 우수회원(네이버 메일 자주 쓰는 사람)만 smtp 사용할 수 있고 DAUM은 아예 smtp 를 지원하는지 안하는지도 모르겠고.. 너무들해열!!! 이메일이 아니라 FTP가 되가고 있는 국내 이메일 서비스의 경향인가;;
top


앗.. 이런 바보;;

모하니?/Coding : 2007.05.15 23:00


이전 글에 올렸던 상황의 코드에서 두 개의 문제를 발견 했습니다.

1. 멀티파트 리졸버를 등록하지 않았습니다.
엄청난 실수 입니다. -_-;;; 바보 -_- 멍청이 =_=
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000" />
    </bean>

2. 필요없는 캐스팅.
//        response.setContentType("text/plain");
//        if (! (request instanceof MultipartHttpServletRequest)) {
//            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
//            return null;
//        }
//        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
//        MultipartFile file = multipartRequest.getFile("picture");

이렇게 어렵게 가져올 필요없이 그냥 커맨드 객체에서 가져오면 되는데 말이죠. 책에 나온 코드 그대로 붙였더니 괜히 복잡해 졌네요. 물론 리퀘스트가 리졸버 거치면서 멀티파트리퀘스트로 바꼈는지 확인하는 코드를 위의 1번과 같은 잘못을 했을 때 유추하기에 좋습니다.

사용자 삽입 이미지

사용자 삽입 이미지

사용자 삽입 이미지

와~ 파일이 웹서버 위에 올라가 있지 않아서 보이지 않는 것 같은데요. Anyway 파일 업로드를 사용하여 사진을 올리고 보여주는데 성공하였습니다. 음하하하 이제 운동해야쥐~
top


윽.. 순탄치 않아..ㅠ.ㅠ

모하니?/Coding : 2007.05.15 18:12


사용자 삽입 이미지

사용자 삽입 이미지

사용자 삽입 이미지
왜 multipart request가 아니지.. 흠 분명히 form에 표시해 줬는데;;
top


AJN Member Address Book

모하니?/Coding : 2007.05.15 13:57


드디어 어제 이후로 파일업로드 스킬이 생겼습니다. 하하하

저걸 이용해서 이전에 계속 만들다 도중 하차했던 주소록을 이번엔 기필코 만들고 말겠습니다. 최대한 간단하고 단순하게..

0. 로그인
- 이메일, 비번

1. 회원 정보 입력/수정
- 이메일, 비번, 이름 << 현재 AJN 회원은 블로그 DB에서 가져오기
, 전화번호, 사진, 소개

2. 전체 회원 조회
- 이름, 이메일

3. 회원 정보 조회
- 이메일, 비번(본인일 경우에만), 이름, 전화번호, 사진, 소개

4. 탈퇴 못 함.
- ㅋㅋㅋ

top


Spring 설정파일에서 <util:list/> 사용하기

모하니?/Coding : 2007.04.29 13:56


사용자 삽입 이미지
Hibernate + Spring 연동할 때 사용하는 sesstionFactory를 등록하는 설정파일 입니다. 이 곳에서는 Persistence 객체에 대한 정보를 XML 형식이든 어노테이션 형식이든 알아야 합니다. 따라서 파란색 부분에 어노테이션이 붙은 클래스들을 추가해 줍니다. 요걸 아래 처럼 바꿨습니다.

사용자 삽입 이미지
사용자 삽입 이미지

Spring에서 기본으로 List를 위해 사용하는 클래스는 ArrayList입니다.
참조 : Collection 형태의 프로퍼티 설정의 이면에서 무슨 결정이 일어날까?

LinkedList를 사용하고 싶을 때는 다음과 같이 list-class 속성을 사용하여 LinkedList를 사용할 수 있습니다.
<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<util:list id="emails">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:list>

top


Spring 설정파일에서 'p' 네임스페이스 사용하기

모하니?/Coding : 2007.04.29 13:23


앞에서 만든 스키마를 사용하면 지난번 Epril에서 봤던 p 네임스페이스를 사용하여 <property> 태그를 대체 할 수 있으며 하나의 bean이 전부 하나 태그로(<bean 뭐뭐뭐뭐~ />) 처리 될 수 있습니다.
사용자 삽입 이미지

기존에 사용하던 스키마와 프로퍼티 설정 방식입니다. 이것을 p를 사용하여 다음과 같이 바꿀 수 있습니다.

사용자 삽입 이미지
util 스키마는 사용하지 않기 때문에 삭제 해도 되겠네요. 흐흐..

이전에 비해 불편한 점은 첫번째 그림에서 보이듯이 자동완성 기능을 사용할 수 없다는 것이 단점이라고 생각됩니다.

top


Eclipse에 Spring XML 기반 Configuration 탬플릿 등록

모하니?/Coding : 2007.04.29 13:06


참조 :
자주 쓰는 구문 템플릿(Templates)으로 등록하기

추가할 탬플릿은 Spring 에서 bean 설정 파일로 사용 할 때 기본적으로 사용하는 구문입니다. 보통은 예전 프로젝트에서 복사하거나 레퍼런스에서 복사해서 사용을 하지만 지난번 Epril 세미나에서 토비님께서 라이브 코딩 하실 때 사용하시는 걸 보고 '나도 등록해야겠다.' 해놓고 이제서야 등록하게 됐습니다. :)

Eclipse 를 키고 -> preference -> Web and XML -> XML Files -> XML Templates 에서 new 버튼을 사용하여 등록합니다.

등록할 탬플릿은 두 개

1. 일반 Spring 설정 파일 :: spring(normal)
<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
    ${cursor}
</beans>

2. Schema based Spring AOP와 Transaction용 :: spinrg(advanced)
<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
default-autowire="byName">
   
</beans>

설정 내용을 보시면 아시겠지만 p 와 util name 스페이스를 사용할 수 있으며 byName으로 autowiring을 사용합니다.

s만 입력한 뒤 자동완성 단축키(컨트롤+스페이스)를 클릭하면 다음과 같이 간단하게 사용할 수 있습니다. 이제 더이상 레퍼런스와 다른 프로젝트를 뒤질 필요가 없어졌습니다. 나이수..

사용자 삽입 이미지

사용자 삽입 이미지

자신이 등록한 탬플릿을 xml 형식으로 공유할 수 있습니다. 아래 그림에 보이는 import 버튼을 사용하여 다운받은 xml 파일을 불러 들이면 됩니다.
사용자 삽입 이미지

top


태그파일 멋쟁이!

모하니?/Coding : 2007.04.23 23:07


사용자 삽입 이미지
매우 쉬운 벨리데이션 js 파일을 사용해서 AJAX를 사용한 벨리데이션을 거의 날로 먹었습니다.

하지만 거의 날로 먹었다는 말을 괜히 한 것이 아니였습니다. JSP 코드가 다음과 같이 정신없어졌거든요.
사용자 삽입 이미지

그래서 태그파일을 만들기로 작심 했습니다. 그리고 만들었었습니다.
사용자 삽입 이미지

저런 저런...input의 type마다 태그를 하나씩 만들고야 말았습니다. spring form 태그들 중에 input엘리먼트에 type 속성이 없었기 때문에 저런 일이 발생했습니다.

하지만 다른 방법이 떠올랐습니다. type을 받아 오고 안에서 jstl로 그때 그때 적당한 spring form태그를 사용하도록 코딩하면 될 것 같았습니다. 그리고는 코딩에 들어갔습니다.
사용자 삽입 이미지

결과는 성공적이였습니다.

태그파일을 적용한 JSP 코드는 이전보다 훨~씬 간단해 졌습니다.
사용자 삽입 이미지

태그파일 귿!!! 멋쟁이!! 태그파일 갈쳐주신 물개 선생님도 멋쟁이!!


top


문자열 생성기 prototype

모하니?/Coding : 2007.04.18 17:37


1. 텍스트 박스 위에 있는 버튼을 누를 때 마다 문자열을 이어 줍니다.
2. "취소" 버튼을 클릭하면  제일 나중에 추가한 문자열을 삭제 합니다.
3. "모두 삭제" 버튼을 클릭하면 모든 문자열을 삭제 합니다.

정말 그냥 프로토타입에 불과 합니다. 버튼들을 세팅할 수 있고 재사용하려면 결국 DB를 사용해서 버튼 세팅 정보를 읽어와서 보여줘야 할 것 같습니다. 화면도 복잡해 질 텐데 AJAX나 JavaScript 공부를 위해서 웹 기반으로 바꿔야 겠습니다.

top


문자열 생성기

모하니?/Coding : 2007.04.18 14:17


교수님께서 소개 시켜주신 업체로 부터 요구사항이 들어왔습니다.

"네이트온에서 음악을 다운받아서 들어 보세요." 라는 주문을 하면 실험자가 핸드폰을 가지고 알아서 작업을 합니다. 그리고 그 내용을 기록 합니다.

네이트온 -> OK -> 검색 메뉴 선택 -> 미리 듣기 선택 -> 다운로드 선택 -> 메뉴 -> 멀티미디어 -> 뮤직 -> 방향키로 노래 검색 -> OK

이렇게 일일히 수작업으로 타이핑을 했는데 이걸 자동으로 생성 할 수 있으면 좋겠다고 합니다.  그러려면 핸드폰에 로깅을 하는 프로그램을 심어야 하는데 임베디드 프로그래밍은 전혀 모르기 때문에 핸드폰 버튼 비슷하게 UI를 만들고 그걸 클릭하면 자동으로 위처럼 텍스트를 만들어 주는 간단한 프로그램을 만들어 보기로 해습니다.

필요한 기능으로는
1. 단순한 버튼 클릭으로 텍스트 연달아 붙여주기.
2. 버튼을 클릭할 때 붙여줄 텍스트를 설정할 수 있어야 함.
3. 여러개의 버튼을 추가할 수 있어야 함.

일단은 이 정도로 해서 보내드리고 피드백을 받아봐야겠네요. 문제는 역시 입력 폼을 뭘로 만드느냐인데 브라우져에서 하게 하려면 AJAX로 이쁘게 꾸며드리고 싶지만 그건 나중에 더 공부하고 해드리고 일단은 SWT로 할까 생각중입니다.
top


Really easy field validation 사용하기

모하니?/Coding : 2007.04.18 01:14



흐흐흐 역시나 css는 같이 딸려 온 것을 사용했더니 깔끔하고 좋습니다. js 파일들 경로를 못 찾아서 삽질을 했네요. 이젠 css랑 js파일 경로 지정해 주는 방법을 완벽히 파악했습니다.

사용하는 방법
1. 먼저 필요한 js 파일들을 다운로드 합니다.
2. js파일들을 head 태그 사이에서 경로를 지정해 줍니다.
3. input 태그에서 class와 name 속성을 잘 사용합니다.
4. validator를 사용하는 스크립트를 작성합니다.

1. 여기서 다운로드 할 수 있습니다.

2. js나 css파일들의 경로는 web폴더 기준입니다.
사용자 삽입 이미지
패키지 구조가 위와 같을 때 addMember.jsp에서 ../../../js/뭐시기.js 이런식으로 접근이 안되더군요.

web폴더를 기준으로 js/뭐시기.js 이렇게 써주면 됩니다. 따라서 다음과 같이 코딩하면 됩니다.
사용자 삽입 이미지

3. ID 필드 부분의 코드를 보시면 다음과 같습니다.
<div class="form-row">
      <div class="field-label"><label for="field1">ID</label>:</div>
      <div class="field-widget"><input name="member.id" id="field1" class="required" title="Enter your id" /></div>
</div>

div는 css를 먹이기 위해 사용했습니다. 빨간 부분이 핵심 부분인데요. 그 중에서도 id와 class 속성이 초 핵심 부분입니다.

id는 해당 컴포넌트를 지칭하기 위한 것입니다. 나중에 자바스크립트에서 저 이름을 사용합니다

class 속성에는 다음과 같은 값을 줄 수 있는데요. 저기에 적당한 값을 넣어 주면 그에 해당하는 validation을 해줍니다.
    * required (not blank)
    * validate-number (a valid number)
    * validate-digits (digits only)
    * validate-alpha (letters only)
    * validate-alphanum (only letters and numbers)
    * validate-date (a valid date value)
    * validate-email (a valid email address)
    * validate-url (a valid URL)
    * validate-date-au (a date formatted as; dd/mm/yyyy)
    * validate-currency-dollar (a valid dollar value)
    * validate-selection (first option e.g. 'Select one...' is not selected option)
    * validate-one-required (At least one textbox/radio element must be selected in a group - see below*)

4. validator 객체를 사용하는 스크립트를 작성합니다. 위 예제에 있던 스크립트를 그대로 사용했습니다.

<script type="text/javascript">
    function formCallback(result, form) {
        window.status = "valiation callback for form '" + form.id + "': result = " + result;
    }

    var valid = new Validation('test', {immediate : true, onFormValidate : formCallback});
    Validation.addAllThese([
        ['validate-password', 'Your password must be more than 6 characters and not be \'password\' or the same as your name', {
            minLength : 7,
            notOneOf : ['password','PASSWORD','1234567','0123456'],
            notEqualToField : 'field1'
        }],
        ['validate-password-confirm', 'Your confirmation password does not match your first password, please try again.', {
            equalToField : 'field8'
        }]
    ]);
</script>

대강 해석은 되고 내용 추가나 변경은 하겠는데 막상 이렇게 작성하라면 못하겠네요. 뭐 걍 당분간은 컨트롤 C + V 로~  :)

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

Spring 설정파일에서 'p' 네임스페이스 사용하기  (2) 2007.04.29
Eclipse에 Spring XML 기반 Configuration 탬플릿 등록  (0) 2007.04.29
태그파일 멋쟁이!  (2) 2007.04.23
문자열 생성기 prototype  (0) 2007.04.18
문자열 생성기  (0) 2007.04.18
Really easy field validation 사용하기  (4) 2007.04.18
Semina Helper v0.7  (2) 2007.04.13
Semina Helper v0.5  (0) 2007.04.06
아 이런 바보..ㅠ.ㅠ  (2) 2007.03.31
Report Validator v1.0  (0) 2007.03.29
JAR, WAR 에피소드 해결 방법  (0) 2007.03.29
top


Semina Helper v0.7

모하니?/Coding : 2007.04.13 20:57


추가 기능 1 - 같은 이메일로 재 신청 방지.
Spring MVC의 HandlerExceptionResolver 를 사용한 예외 처리 를 참조 하여 이미 세미나 신청을 했던 이메일로 다시 또 세미나 신청을 하려고 했을 경우 에러 페이지로 이동하도록 구현했습니다.
사용자 삽입 이미지
리스트에 보시면 email 이라는 메일이 등록 되어 있습니다. 여기에 email로 다시 신청을 하려고 하면...
사용자 삽입 이미지
에러 페이지로 이동합니다.

사용자 삽입 이미지

추가 기능 2
- 카운팅!
메인 화면에 현재 까지 세미나 참가 신청을 한 총 인원수를 뿌려줍니다.
사용자 삽입 이미지

SVN 으로 받으실 수 있습니다.
사용자 삽입 이미지
프로젝트 오너로 등록되어 있지 않은 분께서는 User에 anonymous로 받으시면 되구요 URL중에 맨앞이 https 가 아니라 http로 바꿔주시면 됩니다.

구현해야 할 것
1. 참가 신청 취소 기능
2. 신청자 제한 기능
3. 대기자 자동 관리 기능

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

Eclipse에 Spring XML 기반 Configuration 탬플릿 등록  (0) 2007.04.29
태그파일 멋쟁이!  (2) 2007.04.23
문자열 생성기 prototype  (0) 2007.04.18
문자열 생성기  (0) 2007.04.18
Really easy field validation 사용하기  (4) 2007.04.18
Semina Helper v0.7  (2) 2007.04.13
Semina Helper v0.5  (0) 2007.04.06
아 이런 바보..ㅠ.ㅠ  (2) 2007.03.31
Report Validator v1.0  (0) 2007.03.29
JAR, WAR 에피소드 해결 방법  (0) 2007.03.29
JAR, WAR 에피소드  (2) 2007.03.29
top




: 1 : ··· : 6 : 7 : 8 : 9 : 10 :