백기선의 토스3, 1부 코딩 동영상 공개

2009년에 처음으로 스프링 교육을 준비하던 시절 만들었던 코딩 동영상입니다. 교육은 하루 6~8시간씩 총 4일 과정이었고 코딩을 통해서 몸으로 직접 익히는 교육을 구상했었습니다. 그 뒤로 교육 제의가 들어오는 곳이 간간히 있었고 그때마다 교육 내용은 아주 조금씩 변형되었지만 저때 처음 만들어둔 코딩 동영상은 변하지 않고 매번 아주 유용하게 써먹을 수 있었습니다.

하필이면 교육하는 날 제가 몸이 안좋아서 도무지 코딩을 제대로 할 자신이 없을 때 큰 힘이 되었고, 시간이 촉박한데 갑자기 코딩이 막혔을 때에도 매우 유용했습니다. 무엇보다 손은 놀면서 입으로만 코딩할 수 있는 것도 편했고 그럴 때마다 매번 속으로 또는 겉으로 ‘이 동영상을 만들어 두길 진짜 잘했다’고 몇 번이나 되뇌였습니다.

순수하게 코딩만 찍은거라서 아무런 음성이 없습니다. 여기에 육성으로 설명 추가하면서 녹화를 하다가는 말실수 하거나 중간에 다른 소리가 섞여서 녹화를 다시하는 상황이 발생할 수 있기 때문에 일부러 처음 녹화할 때 소리는 빼고 녹화를 했죠.

여기에 동영상 중간 중간 멈춰 가면서 육성으로 설명을 추가로 입히고 소스 코드랑 교재도 다시 정리해서 올리려면 적지 않은 노력이 들기 때문에 그렇게까진 못하겠고, 수고스럽지만 그런걸 다 해서 DVD에 담아서 오프라인으로 팔까도 생각해봤지만 누가 얼마나 사겠냐 싶기도 하고…

이래저래 백기선의 토스3 교육은 이걸로 끝.

Whiteship’s 스프링 트러블슈팅 서비스

그동안 제 블로그 방명록이나 이메일로 스프링 관련 이슈를 문의해 오신 분들이 제법 있습니다. 블로그를 티스토리에서 워드프레스로 옮긴 뒤로는 질문이 많이 줄었지만 그래도 다시 간간히 질문이 올라오더군요. 그 중에서도 스프링 관련 질문, 또 그 중에서도 문제 상황만 골라서 해결해 드리고자 합니다. 제 블로그 방명록 옆에 새로 페이지를 만들어 뒀습니다.

whiteshipspring

많이 이용해 주세요.

무료랍니다.

[스프링 3.0] @Value 이용해서 기본값 설정하기

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ValueTest {
    @Autowired Whiteship whiteship;
    @Test
    public void defaultName(){
        assertThat(whiteship.getName(), is(“keesun”));
    }
}
@Component
public class Whiteship {
    @Value(“keesun”)
    private String name;
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}
악.. 저녁 약속이 있어서 길게 정리는 못하겠네요. 
이것도 DI용 애노테이션이라는거..

봄싹 9월 특강 Completed!! 후기랄까나…


켄트벡 세미나와 KSUG 번개에 나갔었다면, 볼 수 있었겠지만 개인 사정으로 그동안 토비님을 못 뵈었다가 드디어 봄싹 스터디에 초대하여 3시간짜리 스터디 진행을 부탁드렸습니다. 커피 한 잔과 감자탕 한 끼로 너무 많이 부려먹은듯(?)해서 죄송스럽지만, 뭐.. 제자에게 이정도쯤은.. 해주셔야.. ㅎㅎㅎ 그저 감사 할 따름입니다. (__)/

이번에 사부님을 만나 느낀점은 많지만 그 중에서 개발자로서의 고민이 좀 더 심화되었습니다. 사실 다음 DebDay를 다녀왔을 때부터 느끼던 것인데 털어놓진 않고 속으로 앓고 있었지요.

문제의 핵심은 제가 작성한 코드가 개떡같다는 거였습니다.

그 개떡같은 코드는 현재 제 노트북에만 있고 그 어디에도 공개하지 않았습니다. 저번 달인가 이번 달 초에 다음 DevDay 때문에 제주도에  갔었을 때 작성한 코드가 정말 최악이었습니다. 스프링 코드를 거의 사용하지 않고 Smack과 java.net 패키지를 주로 이용하여 코드를 작성했었는데 정말 끔찍했습니다. 조금씩 계속 지져분해지기 시작하더니 어느 순간 걷잡을 수 없는 형태의 코드가 되어버렸고… 그 뒤는… 동작하긴 하지만 속은 다 썪어서 도무지 남에게 보여줄 수 없는 코드가 되었습니다.

그러던 중… 봄싹 스터디에서 스프링의 가장 기초이자 핵심은 DI가 어떤 것인지 보여주는 명쾌한 코드와 설명을 보면서 다시 한 번 자극을 받을 수 있었습니다. 스프링의 핵심인 DI가 어떤 과정으로 탄생되는 것인지 살펴보았는데, 그 과정이 굉장히 논리적이고 깔끔했습니다.

거기에다, 비밀리에 베타리딩 중인 책에서도 스프링이 어떤 문제를 어떤 방법으로 해결해나가는지 순차적으로 보여주는 내용이 저에게 많은 도움이 되고 있습니다.

스프링의 가장 초기 모습을 볼 수 있는 빨간책 1권과 코드를 중심으로 살펴보면서 스프링 DI 감각을 익히는 것이 중요하고 재밌겠다고 생각했습니다. 그래서 갑작스래 봄싹 스터디에 ‘오리지널 스프링’ 스터디를 만들었는데, 생각보다 많은 분들이 참여하고 계십니다. 슬슬 스터디 홍보로 전환되는 듯 한데 어서 마무리하고 좀 더 공부하다 자야겠습니다.

밤늦게 글을 써서 그런지 두서가 없는데, 결론은 토비님 덕분에 스프링을 좀 더 진지하게 공부하게 되었다는 것입니다. 부디.. 개떡같은 코드가 찰떡같은 코드로 거듭나길 바라며~~ 열공!!

스프링의 이메일 기능 지원과 테스트를 살펴보자

스프링이 지원하는 이메일은 JavaMail과 JAF라는 것이 있습니다. 사용법은 간단하니.. 다음에 심심할 때 살펴보기로 하고, 지금은 사부님이 올리신 글과 관련 된 부분을 찾아보는게 급선무입니다.

스프링 이메일 기능은 context.support 모듈에 들어있습니다. 주요 클래스는 o.s.mail.javamail에 들어있는 JavaMailSender 인터페이스와 그 구현체인 JavaMailSenderImpl 클래스입니다. 인터페이스의 API를 읽어보면 다음과 같은 내용이 있습니다.

Clients should talk to the mail sender through this interface if they need mail functionality beyond SimpleMailMessage. The production implementation is JavaMailSenderImpl; for testing, mocks can be created based on this interface. Clients will typically receive the JavaMailSender reference through dependency injection.

The entire JavaMail Session management is abstracted by the JavaMailSender. Client code should not deal with a Session in any way, rather leave the entire JavaMail configuration and resource handling to the JavaMailSender implementation. This also increases testability.

A JavaMailSender client is not as easy to test as a plain MailSender client, but still straightforward compared to traditional JavaMail code: Just let createMimeMessage() return a plain MimeMessage created with a Session.getInstance(new Properties()) call, and check the passed-in messages in your mock implementations of the various send methods.

즉, 이 API를 만든 의도에 사용성 편의 뿐만 아니라, 테스트 편의성도 포함되어 있다는 암시를 읽어낼 수 있습니다. JavaMail의 Session API를 사용하지 않고 스프링의 JavaMailSender를 목킹해서 테스트 하라는 것인데, 왜 그렇게 했는지는 맨 마지막 부분의 JavaMail의 Session API 사용법(Session.getInstance(new Properties())에서 볼 수 있습니다. 바로 static 메서드입니다.

JavaMail 레퍼런스에서 그 사용법을 보면, 다음과 같은 코드로 JavaMail을 이용하는 모습을 볼 수 있습니다.

//메시지를 만들고,,
     Properties props = new Properties();
     props.load(new FileInputStream(propfile));

     Session session = Session.getInstance(props, null);
     MimeMessage msg = new MimeMessage(session);

//전송합니다.
    // Set the content for the message and transmit
    msg.setContent(mp);
    Transport.send(msg);

코드 대부분을 생략했습니다. 중요한 부분은 위에 다 나와있습니다. 바로 static 메서드를 사용한다는 것이 중요한 부분입니다.

이런 API 사용을 클래스를 단위 테스트 하려면 막막합니다. 도무지 static 메서드를 호출하는 부분을 mock으로 바꿀 수가 없습니다. 그렇다고 테스트를 하는데 실제로 메일을 매번 보내기도 뭐하고 말이죠. 그래서 테스트 하려면 static 메서드 호출을 사용한 클래스를 거의 다시 만들다시피 구현한 stub을 만들어서 테스트 해야 하는데 이건 엄청난 수고가 필요합니다. JavaMail 예를 들면 거의 Transport를 테스트용으로 다시 구현해서.. sendMessage에서 실제로 메일을 보내지 않고 그냥 보내는 메시지 목록에 메시지만 모아두는 식의 작업이 필요해집니다. 그리고 테스트 할 떄는 그런 스텁 Transport를 사용하는 또 다른 스텁 MailSender가 필요해지겠죠.(실제로 이 작업들은 스프링의 JavaMailSenderTests에서 수행하고 있습니다.)

하지만, 테스트를 편하게 하는 방법이 아주 없는 건 아니었습니다. 오늘 톱님께서 올리신 글을 보면 static 메서드를 호출하는 코드를 비교적 편하게 테스트하는 방법 세 가지를 알 수 있습니다.

하나는 JavaMailSender처럼 static 메서드 호출 부분을 랩핑한 클래스를 만들고, 그 클래스를 목킹한 다음 해당 메서드가 호출되는지 테스트하는 것입니다. 그렇게 만들어 두면, 테스트 하려는 대상이 JavaMail의 Transport.send 같은 static 메서드를 호출하지 않고, 그것을 사용한 JavaMailSender를 사용하기 때문에 JavaMailSender의 mock을 만들고 그 객체의 send가 호출될 때 어떤걸 하라고 mocking 한다던지 해서 호출이 제대로 됐는지, 어떤 메시지가 넘어갔는지 등을 확인할 수 있겠습니다.

두 번째 방법은 로드존슨이 만들었다는 AspectJ를 이용하는 방법이고, 세 번째 방법은 PowerMock을 이용하는 방법인데, 둘 다 결국 바이트코드를 조작해서 static 호출 부분을 mock으로 호출로 교체하는 기술 인듯합니다. 이 두 가지는 일단 논외로 치

결론은.. 스프링의 JavaMailSender를 사용하면 JavaMail의 static 메서드 호출과 관련하여 테스트를 어떻게 작성할까 고민할 필요없이, JavaMailSender를 목킹해서 테스트를 만들면 된다는 것입니다.

덤으로 JavaMail의 static 메서드 호출을 사용한 JavaMailSender의 테스트 클래스를 보면, static 메서드 호출을 하는 클래스에 대한 테스트 실마리를 얻을 수 있습니다. static 메서드를 호출해서 가져오는 객체를 가져오는 부분을 별도의 메서드로 분리하고 그 부분을 테스트 용으로 재구현하고 그것을 테스트에서 사용하는 방법인데.. 이거 이거.. 귀찮아서 원… @_@..  그래서 static은 테스트의 적인가 봅니다. 그래도 이길 수 있는 적이라는거..