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

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

@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를 상속받도록 수정했습니다. 그랬더니 아무 문제없이 잘 돌아가더군요. 이상태에서 일단 정지입니다. 돌아가게 만든 상태에서 어떻게 리팩토링 해야할지 고민좀 해야겠습니다.

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

<%@ 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로 하고 사용해도 될테지만.. 태그 파일을 만드는 비용이 꽤 많이 들겠지요. 결국 선택의 기로에 서게 되는데, 저는 귀찮아서;; 그냥 최소한의 속성만 정의해서 쓰는 편입니다.

Introduce Parameter Object

참조 : http://www.refactoring.com/catalog/introduceParameterObject.html

여러개의 파라미터를 가지는 메소드의 경우 그 일련의 파라미터들을 가지는 새로운 객체 타입을 받도록 변경할 수 있습니다.

사용자 삽입 이미지
Eclipse의 refactoring 기능 중에 저런 리팩터링을 지원하는 것이 없는지 찾아봤지만 못찾았습니다. 있으면 좋으련만…
http://dev.eclipse.org/mhonarc/lists/eclipse-pmc/msg00188.html
http://download.eclipse.org/eclipse/downloads/drops/S-3.3M7-200705031400/eclipse-news-M7.html
위 글들을 보니 Eclipse3.3의 refactoring 기능에 추가해 줄 것 같습니다.

하나의 메소드 안에 뭉탱이로 들어가는 인자들이 있다면 그 인자들끼리 관계가 밀접할 수 있으며 해당 객체에서 따로 빼내는 것이 애플리케이션을 더 유연하게 만들 수 있습니다.

메소드에 넘겨줄 인자가 단순해집니다.

Report Validator 개선 사항

검색 API를 여러 개 붙이는 것이 생각보다 쉽지 않더군요. 이 말은 확장이 불편하다는 거고 다시 또 이 말은 중복된 코드로 인해 코드 끼리 의존성이 심해졌다는 것입니다.

소스 코드 문제점

현재까지 크게 두 부분이 문제가 됩니다.

public static int search(Sentence sentence) {
        int totalResult = useNaverAPI(sentence, “blog”)
            + useNaverAPI(sentence, “webkr”)
            + useNaverAPI(sentence, “kin”)
            + useDaumAPI(sentence, “blog”);
        sentence.setPoint(totalResult);
        return totalResult;
    }

이런식으로 API를 사용하는 메소드를 호출하여 사용하게 되는데 useNaverAPI와 useDaumAPI는 요청을 보내는 문자열만 다를 뿐 하는 일은 완전히 같기 떄문에 엄청난 양의 중복된 코드를 발생시켰습니다.

이것과 더불어 Sentence 객체를 보면 다음과 같이 만들어져 있습니다.

public class Sentence {
    private String line;

    private int copyLebel;

    private String naverBlogURL;

    private String naverWebURL;

    private String naverKnowURL;

    private String daumBlogURL;

어처구니가 없는거죠.ㅋㅋ Map을 쓰면 되는데 API 붙일 때 마다 속성을 하나씩 추가 해야 된다니.. 왜 naverWebURL 속성을 만들 때 미쳐 중복을 제거할 생각을 못했을까요..ㅠ.ㅠ 아니면 생각은 했지만 Map을 쓰는게 익숙하지 않아서 못본척 하고 넘어려는 자아가 공부하려는 자아를 속였을 수 있습니다. 그리고는 마치 몰랐었던 척..하는…나쁜 기선.

화면의 문제점

사용자 삽입 이미지첫 화면 입니다. 간단한 메뉴얼이 눈에 걸리적 거립니다. 좀 더 이쁘게 보여줄 수 없을런지.ㅠ.ㅠ 전체적인 색은 제가 좋아하는 파란색과 하얀색 계열로 바꿔야 겠습니다.

사용자 삽입 이미지흠..텍스트 위주의 화면..저도 이쁜 아이콘과 그림으로 꾸미고 싶습니다. 표는 AJAX로 멋지게 “전체 결과 보기” 같은 탭을 열어 주면 쫙~ 펼처지도록 하고 싶…지…만… 31일까진 무리라는거.. CSS는 역시..색을 좀 조절하고.. 테이블 모양도 좀 더 괜찮은 모양으로 바꿔줄까 합니다.

화면 코드 문제점

jsp 파일 두 개에서도 헤더와 푸터가 중복되어 나타나고 있는데요. 그걸 include 인가를 사용해서 제거 해야겠습니다.

그리고 “있다” “없다”를 판단해서 찍어주는 JSTL 코드 부분이 계속 반복해서 나타납니다. 이건 태그파일을 사용해서 코드를 간결하게 줄일 수 있겠습니다.

Eclipse에서 Rename Method 리팩토링

1288767890.bmp
getNumberOfMembers() 메소드 이름을 count()로 바꾸는 것이 좋겠다는 의견을 받았습니다. 생각해보니 주소록 어플리케이션에서 숫자를 셀 것이 몇명이나 등록되어 있는지 밖에 없는데 굳이 “몇 명이 있는지 세어라” 라고 할 필요 없이 “몇이냐” 라고 물어보는게 타당하다는 생각이 듭니다.

문제는 getNumberOfMembers() 메소드를 사방에서 사용하고 있다는 것입니다. memberDao 인터페이스에 만들어둔 이 메소드를 count()로 변경하게 되면 여러 클래스에서 에러가 발생하게 되고 각 클래스들을 돌아가니며 수정을 해도 되겠지만…Eclipse가 그 모습을 본다면 매우 안타까워 할 것 같습니다.

Alt + Shift + R 을 클릭합니다.
1046755591.bmp변경하고 싶은 이름을 입력한 뒤 Preview를 눌러 확인 해 볼 수 도 있습니다.
1404238966.bmp어디서(class) 어떤 부분이 바뀌게 될지 확인 할 수 있습니다.

OK를 눌러주면 리팩토링 끝입니다. 🙂

참고 :
Rename Method -> http://www.refactoring.com/catalog/renameMethod.html