Whiteship's Note


역시 개발자는 모니터가 빵빵한듯...



진짜 집에 가려고 했는데;; 아주 특이한 분석자료가 있어서요..ㅋㅋ


1위인 1280이 요즘은 대세가 되서 그런걸지도 모르겠지만,
2위인 1680*1050 해상도로 거의 절반 가까이가 접속하셨네요..
세상에...

다들.. 그정도는 쓰시는군요. ^^
1680*1050 안쓰시는 분들만 조금 답답한 거죠 뭐.ㅋㅋㅋ


top


역시 개발자들은 파이어폭스인가..




봄싹 사이트에 접속한 사용자들(대부분이 개발자시겠죠?)이 사용하는 브라우저 분석을 봤더니 IE 보다 파이어폭스 유저가 더 많네요.

참고로 봄싹사이트에 IE6로 접근하시면 재미난 걸 보실 수 있습니다. ㅋㅋ

ps: 이젠 진짜 집에가야지;

top


[NotificationService] NotificationService 인터페이스 기반 프로그래밍

모하니?/Coding : 2009.09.23 20:08


봄싹 사이트에서는 여러 종류(이메일, 구글토크, 트위터, 미투데이 등등)의 알림 서비스를 제공할 계획입니다. 현재는 이 중에서 미투데이를 뺀 나머지 세 개의 서비스가 구현되어 있습니다.

이메일은 스프링의 JavaMailSender를 이용했고, 구글 토크는 Smack 라이브러를 이용했고 트위터는 twiiter4j 라이브러를 이용했습니다.

메일 기능을 만들 때는 인터페이스를 고려하지 않았었는데, 구글 토크 서비스를 만들 때는 인터페이스를 생가해서 미리 MessangerService라는 걸 만들었었습니다.

public interface MessagangerService {
   
    void sendMessage(SpringSproutMessage ssm);

}

SpringSproutMesage 타입의 객체를 받아서 어떤 메시지를 전송하는 인터페이스를 만들고, 그것을 구현한 JabberMessagangerService를 만들었습니다.

public class JabberMessangerService implements NotificationService {
   
...
   
    public void sendMessage(SpringSproutMessage ssm) {
      ...
    }

}


그런다음 메시지들을 몇 개 만들었습니다.

public abstract class SpringSproutMessage {
   
    protected StringBuilder msg;
    protected Collection<String> tos;
   
    public SpringSproutMessage() {
        msg = new StringBuilder();
    }
   
    public String getMessage() {
        return msg.toString();
    }

    public Collection<String> getTos() {
        return tos;
    }
   
}

메시지에서 기본으로 필요한 속성들을 가지고 있는 상위 클래스를 제공하기로 했습니다.

public class MeetingMessage extends SpringSproutMessage{

    public MeetingMessage(Study study, Meeting meeting, MeetingStatus status) {
      ...
    }

    public MeetingMessage(Meeting meeting, MeetingStatus status) {
        this(meeting.getStudy(), meeting, status);
    }

}


이런식으로 상속 받아서 필요한 메시지는 주로 생성자에서 만들어 채웁니다.

    @AfterReturning(pointcut = "addStudyPointcut() && args(study)", argNames="study")
    public void sendMailAfterAddStudy(Study study){
        sendMailService.sendMail(new StudyMail(study, StudyStatus.OPEN));
        messangerService.sendMessage(new StudyMessage(study, StudyStatus.OPEN););
    }

사용할 땐, 이렇게 어드바이스 내에서 넘겨받은 매개변수들을 이용해서 메시지를 마들고 만든 메시지를 messangerService에 넘겨주도록 말이죠.

이렇게.. 인터페이스를 설계하지 않고 그냥 만든 메일 서비스가 있는 상태에서 비슷하지만 조금 다른 새로운 알림 서비스.. 구글토크서비스를 추가해보니 거의 모든 부분을 새로 만들었습니다.

하지만 이번에는 인터페이스를 만들어 둔 상태에서 트위터 서비스를 추가해봤습니다. 추가하려니까 기본적인 골격이 이미 구글토크서비스와 상당히 비슷하더군요.

그래서 구글토크서비스의 인터페이스를 다음과 같이 바꿨습니다.

public interface NotificationService {
   
    void sendMessage(SpringSproutMessage ssm) throws NotificationException ;

}

노티로 바꾸고, 노티 예외를 하나 만들어서 그걸 던지도록 했습니다. smack이나 twitter4j가 checked exception을 던지는데.. 별로 뭐 예외를 잡아서 할 일이 없기 때문에 unchecked exception 계층 구조가 필요해서 저렇게 바꿨습니다.

그리고 트위터 서비스를 구현했습니다.

@Service
public class TwitterService implements NotificationService {

    @Autowired Twitter twitter;

    public void sendMessage(SpringSproutMessage ssm)
            throws NotificationException {
        try {
            twitter.updateStatus(ssm.getMessage());
        } catch (TwitterException e) {
            throw new TwitterServiceException("HTTP status code: "
                    + e.getStatusCode() + " 메시지: " + ssm.getMessage(), e);
        }
    }

}


초간단입니다. 이 클래스랑 NotificationExcpetion을 상속받은 예외 클래스를 하나 만들었습니다. SpringSproutMessage는 구글토크 서비스에서 사용하던 것을 그대로 재사용합니다.

애스팩트를 수정해 줍니다.

    @AfterReturning(pointcut = "addStudyPointcut() && args(study)", argNames="study")
    public void sendMailAfterAddStudy(Study study){
        sendMailService.sendMail(new StudyMail(study, StudyStatus.OPEN));
        StudyMessage msg = new StudyMessage(study, StudyStatus.OPEN);
        messangerService.sendMessage(msg);
        twitterService.sendMessage(msg);
    }

이렇게 됐습니다. 메일 서비스가 이 인터페이스를 따르지 않고 있는것이 좀 불만입니다. 게다가 인터페이스는 있지만 인터페이스 기반 프로그래밍이 제대로 되고 있지 않은 것 같습니다. 이건 그림으로 보면 보다 명확합니다.


이런 상태죠. MailService역시 NotificationService를 구현하도록 수정하고 MailService에서 사용하던 메시지들도 SpringSproutMessage를 상속받아 구현하게 하면 다음과 같은 구조가 될 겁니다. 그나마 다행인 것은 이메일 서비스를 만들 때 3차인가 4차 수정을 거쳐 Aspect를 빼놨다는 것입니다. 서비스 코드가 무한정 정신없어질뻔했는데 역시 Aspect로 빼내길 잘한것 같습니다.


자. 이렇게 말이죠. NotificationService 타입의 콜렉션을 가지고 해당 콜렉션을 순회하면서 메시지를 보내도록 코딩을 하면 새로운 NotifiacationService를 추가할 때마다 애스팩트를 고치지 않아도 됩니다.

간단한 빈 설정으로 콜렉션에 추가만 해주면 되죠. 이 얼마나 멋진 인터페이스 기반 프로그래밍인가요. 왜 인터페이스 기반 프로그래밍이 중요한지 몸소 체험할 수 있는 기회였습니다.

자.. 이제 설계가 끝났고 이게 훨씬 좋은 아키텍트라는 확신이 생겼으니... 비용은 좀 크겠지만, 코드를 뜯어고쳐야겠습니다.

뜯어고치는 일은.. 집에가서~~


ps: 여기서도 로컬 환경과 운영 환경 설정 분리와 구분이 필요한데, 로컬 서버에 띄우고 테스트 하느라 스터디/모임 등을 만들고 수정하면서 트위터에 잘 게시가 되나 확인했습니다. 그러나 실제로는 이 내용들이 실제 봄싹 트위터 계정이 아니라 테스트용 계정으로 만든 곳으로 올린다던지... 해야겠죠.


top


테스트 환경와 운영 환경용 설정 분리/구분하기

모하니?/Coding : 2009.09.23 19:06


public class SpringSprout2System {
    public static final String ENCODING = "UTF-8";
    public static final String S2C_HOME = "http://www.springsprout.org/";
    public static final String AVATAR_URL = "http://www.gravatar.com/avatar/";
...
    public static final String JSON_VIEW = "mappingJacksonJsonView";
}


이런식으로 시스템에서 자주 사용하는 상수 값들을 모아두었습니다. 그런데 문제는 저 중에서 S2C_HOME의 값이 로컬 서버에서 돌 때랑 실제 운영 서버에서 돌 때랑 달라져야 하는데 그렇지 않고 있다는 것이었죠.

그럼 일단 프로퍼티 파일로 빼고 싶어질 겁니다. 그런데.. 프로퍼티 파일로 빼면 다인가요? 프로퍼티 파일로 빼고 그걸 빈으로 등록해서 참조해서 쓰면 끝인가요? 프로퍼티 파일이 일단 로털 테스트용/운영 서버용 이렇게 두 개로 나뉠텐데.. 그럼 로컬에서 작업할 땐 로컬용 프로퍼티 파일 쓰다가 버전 관리 올릴 때는 빼고 운영 서버용을 쓸건가요 아니면 프로퍼티 파일 하나에 모두 넣어 놓고 일부는 로컬용으로 일부는 서버용으로 해놓고 주석처리를 바꿔가며 쓸건가요??

도무지 깔끔한 방법이 아니라는 생각이 들어서 고민하다가 봄싹에서 토론도 하고 사부님이랑 전화통화 하다 생각나서 물어보기도 했습니다. 결국은 다음 세 가지 방법으로 정리가 됐습니다.

1. 프로그래밍을 이용한 설정(스마트 설정)

빈을 하나 등록해서 해당 빈이 현재 애플리케이션이 배포된 서버 환경에 대한 정보를 분석해서 이게 운영 서버인지 아닌지 판단해서 거기에 맞게 환경 변수를 샤샥 등록하는 방법입니다. 자바의 System.getProperty()에 os.name os.arch os.version 키 값을 이용해서 지금 돌고 있는 운영체제의 정보를 알 수 있습니다.

2. 설정파일을 버전관리에서 제외시키기(설정파일 외부화)

별도의 프로퍼티 파일로 배포 환경에 따라 달라지는 속성들을 빼놓고, 이 파일을 버전관리에서 제외시켜서 개별적으로 관리하는 방법입니다. 공유되지 않기 때문에 운영 서버에 초기 세팅할 때나 로컬 개발환경에서 처음 세팅할 때 설정 파일을 만들어 주거나 기본 템플릿 파일을 수정해줘야겠죠.

3. 빌드 툴을 사용하는 방법(빌드 다양화)

앤트라면 간단하게 타겟 하나를 만들어서 특정 프로퍼티 파일은 지우고 다른 걸 추가하는 식으로 운영용 타겟과 빌드용 타겟을 만들어서 쓸 수 있을 것이고, 메이븐이라면 메이븐 프로파일을 사용할 수 있겠습니다.

이 세 가지 방법 이외에도 어떤 것이 있고 어떤 방법이 좋을지 논의 중입니다.

'스마트 설정' 방식과 '빌드 다양화'는 딱 한번만 설정 해두면 계속해서 쓸 수 있기 때문에 비슷한데, 프로그래밍을 할 것이냐 빌드 설정을 할 것이냐 그것이 고민이고, '설정파일 외부화'는 프로젝트를 새로 받을 때 마다 한번씩 수고해야 하기 때문에 다른 두개 보다는 좀 더 귀찮은 것 같습니다. 하지만... 로컬 서버 환경이라고 해서 전부 다 같은 설정을 사용하진 않겠죠. 누구는 localhost:8080에 설치하고 누구는 loclahost:80을 사용할 수도 있을테니까요. 그런면에서 보면 누구나 하나씩 프로퍼티 파일을 별도로 만들게 하는 '설정파일 외부화' 방법이 가장 적절해 보입니다.

뭘 선택할지 고민이네요. 아~~~~~~ 고민이로세. 고민이야.. @_@

이것들 말고 또 다른 방법은 뭐 없을까요. 획기적으로.. 스크립트 언어를 활용한다던가, 스프링 EL을 쓴다던가...

이럴 땐 찍어서 하나 써보면 될까요? 써보다 뭔가 불편하면 다른 방법으로 갈아타고..
그렇다면, 나중에 갈아타도 시스템에 별로 지장이 없어보이는 녀석을 선택하는게 좋겠네요.

그렇다면... '설정파일 외부화' --> '빌드 다양화' --> '스마트 설정' 순으로 시도해보는건 어떨까 싶습니다.

top