2010-04-27

JUnit 이야기

JUnit에 대해서 새롭게 알게 된 사실들.


실행 시간을 기준으로 테스트 실패 여부를 판단할 수 있다.

@Test(timeout = 130)
public void something() {
    ...
}


Ignore Annotation으로 테스트가 실행되지 않도록 할 수 있다.

@Test
@Ignore
public void something() {
   ...
}


TestSetup 클래스로 전체 테스트에 대한 초기화를 수행할 수 있다.

동일한 테스트를 파라미터화해서 여러번 수행할 수 있다.

@RunWith(value = Parameterized.class)
public class SomeTest {

    @Parameters
    public static Collection<Some> getSomes() {
        return ...
    }

    public SomeTest(Some some) {
        ...
    }

    @Test
    ...
}



EasyMock과 JMock이라는 Mock 라이브러리가 있다.

Ant를 사용할 때 Ivy를 이용해서 라이브러리를 관리할 수 있다.


Winstone이라는 서블릿 엔진이 있다.

hamcrest 라이브러리로 assert 기능을 확장할 수 있다.

HtmlUnit, Selenium, HtmlUnit, JsUnit, Rhinounit, Jslint, jslint4java

Cobertura
라는 테스트 커버리지 툴이 있다.

2010-04-26

서평 - 웹 폼

웹 폼(Web Form)에 대한 Web Form Design: Filling in the BlanksForms that Work: Designing Web Forms for Usability에 대한 서평이다.


웹 폼에 대한 책을 쓸 생각을 하다니... 제한적인 소재 때문인지 두 책 모두 200페이지 정도로 읽기에 부담이 없다. 창조적이지는 않지만 애플리케이션을 개발하면서 한번쯤 고민하거나 해야할 내용들을 잘 정리해 두었다.

당연하게 생각되는 내용들이 많지만 그건 책을 읽었기 때문일 게다. 이 두 책은 애플리케이션을 개발하면서 당면할 소소한 문제들을 해결하기 위해 투자해야 하는 시간을 크게 줄여주는 미덕이 있다.


이 책은 같은 주제를 다루지만 차이가 있다.  첫번째 책은 How(구현이 아닌 디자인 측면에서)에, 두번째 책은 Why에 방점이 찍혀있다. 따라서 두 책을 모두 읽는 것이 도움이 될 듯 싶다.

2010-04-20

서평 - Effective UI

Effective UI에 대한 서평이다.

30 페이지는 정독하고 나머지는 그냥 큰 글자만 보는 수준에서 훌터봤다.


어쩌자고 책 제목을 그렇게 지웠을까? Effective JavaEffective C++ 등과 같은 명저 반열에 오르고 싶었던건가. UX를 하는 사람들이라서 네이밍 혹은 마케팅에 탁월한걸까? 그 탁월함이 글쓰기에 발휘되었다면 좋았을 것을...

물론 오해다. 저자들 다수가 일하는 회사 이름이 그럴 뿐이다.


책에 UI는 없다. UX도 없다. 이 단어들이 등장하지 않는 것은 아니다. 자주 등장하지만 이 책은 좋게 평가해도 소프트웨어 개발 방법론에 지나지 않는다. 재미없기로는 대기업 SI 방법론 매뉴얼과 우열을 가르기 쉽지 않다. 대기업 SI 방법론 매뉴얼은 간결하다는 미덕이라도 있다만 이 책은 그나마도 없다.

소프트웨어 개발 방법론이라면 이 책보다 월등히 좋은 책들이 많다는 점에서 이름 하나는 잘지었다.


자 그럼 Effective UI는 어디에 있는가?


2010-04-19

서평 - Modular Java: Creating Flexible Applications with OSGi and Spring

Modular Java: Creating Flexible Applications with OSGi and Spring에 대한 서평이다.

200 페이지가 조금 넘는 분량으로 OSGi 기반 웹 애플리케이션 개발 방법을 Step-by-Step으로 설명하고 있다.

소스코드, OSGi 설정 파일 내용, Maven(Pax Construct) 실행 결과 등이 1/3을 가까이 차지하니 내용은 많지 않다.

OSGi가 무엇인지, OSGi를 이용해서 웹 애플리케이션을 어떻게 개발해야 하는지를 파악하는데 부담없으면서 유용한 책이다.

부정적으로 본다면 다음 아마존 독자 서평에 동의한다.


스스로도 이클립스가 OSGi에 기반하고 있다는 정도로만 OSGi를 접하고 있었는데 이 책을 통해서 다음 내용들을 알 수 있었다.

  • OSGi를 구현한 플랫폼에는 Eclipse EquinoxApache Felix 등이 있다. 이 책에서는 주로 Equinox(뭐라고 발음해야 하는지^^)를 다룬다.

  • 모든 것이 모듈이다. 라이브러리도, 유틸리티나 모델(Bean이든 Domain 객체 등)도... 그리고 톰켓(혹은 Jetty)도 모듈이다.

  • Maven 기반 Pax Construct로 개발을 진행한다. EJB 만큼은 아니지만 소스코드보다 설정 파일이 많은 느낌이다. 이를 편리하게 해주는게 Pax Construct라는 도구이다.

  • Spring Dynamics Module로 OSGi 개발을 편리하게 진행할 수 있다(라고 쓰고 주장한다라고 읽는다). 잘 알지도 못하지만 여전히 Spring에는 정이 가지 않는다.


실패한 EJB보다는 여러가지 장점을 갖는다.

  • 모듈이 갖는 적절한 크기. EJB는 너무 작았다. 그래서 모듈이 아닌 컴포넌트라는 이름을 사용했겠지만...

  • SOA in JVM. 빌어먹을 Remote^^

  • 확장이 유연하다. Fragments를 통한 모듈 확장이 가능하다. Eclipse가 보여준 Extenion과 Extension Point를 이용한 확장이 이것이 단순한 마케팅 용어가 아님을 증명한다.

  • 버전 관리. 동일한 모듈의 다양한 버전이 공존할 수 있다.

  • 라이브러리나 공통 클래스(유틸리티, Bean, Domain 객체)를 모듈로 공유하는 방법이 명확하다.

2010-04-13

데이터 유형과 의도적 제한

프로그래밍 언어들은 다양한 데이터 유형을 제공한다. 가령 자바가 제공하는 데이터 유형은 다음과 같다.

  • byte
  • short
  • int
  • long
  • float
  • double
  • boolean
  • char
  • String

* java.lang 패키지에 있는 Wrapper 클래스를 고려하면 2배가 된다.

그런데 애플리케이션 개발에 쓰는 데이터 유형 숫자를 최소화하면 생산성이 높아지고 유지보수 비용이 감소하지 않을까?

정수를 표현할 때 short나 byte는 사용하지 않고 int와 long만 사용하거나 극단적으로 long만을 사용하면 어떨까?

실수도 float 대신에 double 만을 사용하고 모든 문자열은 String으로만 표현한다든가...

* 아주 극단적으로 모든 숫자를 double로 표현할 수도 있다.


예외적인 경우를 제외한다면 이런 결정이 CPU 성능이나 메모리 양에 미치는 영향도 감내가능하지 않을까?

일단 기본적으로는 다음 데이터 유형만을 사용해서 애플리케이션 개발을 하려고 한다.

  • int
  • long
  • double
  • boolean
  • String

* 정말 int, long은 사용하지 않고 double만을 사용하면 어떨까?

이런 결정은 관계형 데이터베이스 테이블 스키마에도 선순환적 영향을 미칠 것이다.

서평 - Even Faster Web Sites

Even Faster Web Sites에 대한 서평이다.

High Performance Web Sites를 쓴 저자(Steve Souders)가 쓴 책이다. 책 절반 정도는 다른 사람들이 썼다.

High Performance Web Sites 내용은 논란의 여지없이 준수해야 하는 가이드라인이다. 그런데 Even Faster Web Sites 내용을 접했을 때 든 생각은 이런 변태들이 있나였다. 좋게 말하면 프로와 아마추어의 차이가 느껴졌다고할까...


저자가 몸담았거나(야후) 몸담고있는 회사(구글)를 생각하면 이런 극단적인 접근과 고민들이 이해된다. 작은 사이트에서 1% 트래픽 절감은 그 효과가 미미하지만 저런 빅 회사에서는 어마어마한 비용 절감일테니깐.

어찌되었든 이 책에 있는 내용들은 실감나게 다가오지 않는다. 그러나 알고 있으면 언제가 필요할 때 무기가 되어줄 것 같다. 몇 가지 내용을 정리하면 다음과 같다.

  • 초기 화면 구성에 필요하지 않는 스크립트나 스타일쉬트는 늦게 로드한다. 이를 위한 방법들을 설명한다.
  • 웹 브라우저는 스크립트를 병렬적으로 다운로드하지 않고 순차적으로 다운로드한다. 이를 병렬적으로 다운로드할 수 있는 방법들을 설명한다.
  • 여러 이유로 Gzip이 지원되지 않는 경우에(웹 브라우저는 지원하지만) 이를 지원하는 방법을 설명한다.
  • 이미지 파일을 최적화하는 방법(메타 정보 제거와 같은)을 설명한다.
  • 문서 양이 많은 경우에(Gzip 등을 고려해서) 이를 순차적으로 Flush하는 방법을 설명한다.
  • iframe과 CSS Select가 웹 사이트 성능에 미치는 영향과 이를 최적화하는 방법들을 설명한다.
  • 자바스크립트 최적화 코딩 기법이나 Comet 등도 다루는데 내용이 충실하지는 않다.

2010-04-12

자바스크립트 - 문자열 앞뒤 여백 제거

Even Faster Web Sites를 보면 문자열 앞뒤 여백을 제거하는 자바스크립트 함수를 최적화하는 방법이 나온다.

먼저 가장 간단한 방법이다. 정규 표현식으로 문자열 앞뒤 여백을 제거한다.

function trim(text) {
    return text.replace(/^\s+|\s+$/g, "");
}


다음과 같이 정규 표현식에서 g 옵션을 제거하면 성능을 개선할 수 있다.

function trim(text) {
    return text.replace(/^\s+/, "").replace(/\s+$/, "");
}


문자열 뒤에 있는 여백을 정규 표현식을 사용하지 않고 처리하면 성능을 더욱 개선할 수 있다.

function trim(text) {
    text = text.replace(/^\s+/. "");
    for (var i = text.length - 1; i >= 0; i--) {
        if (/\S/.test(text.charAt(i))) {
            text = text.substring(0, i + 1);
            break;
        }
    }
    return text;
}


어떤 방법이 어떤 방법보다 항상 올바른 것은 아니다. 마지막 방법은 성능이라는 장점을 갖지만 변태들을 위한 방법일 수 있다. 첫번째 방법은 간결하지만 성능이 중요한 부분에 사용하기에는 적합하지 않다.


Prototype은 두번째 방법(String#strip)을 사용한다.

function strip() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
}



서평 - High Performance Web Sites

High Performance Web Sites에 대한 서평이다.

서버가 아닌 웹브라우저(클라이언트)에 초점을 맞추어서 웹 사이트 성능 최적화에 사용할 수 있는 방법들을 제시한다. 그 내용이 간결하고 실질적이며 매력적이다.

책일 읽는 동안 자연스럽게 요즘 개발하고 있는 애플리케이션에 어떻게 적용해야 할까하는 숙제들이 쌓여갔다.


1. HTTP 요청을 최소화한다.(Make Fewer HTTP Requests)

자바스크립트 파일과 CSS 파일을 한 두개로 병합한다. CSS Sprites로 여러 이미지 파일도 하나로 병합할 수 있다.


2. CDN을 사용한다.(Use a Content Delivery Network)

사용자와 서버가 지역적으로 분산되어 있는 경우에 이미지, 자바스크립트, CSS 등의 정적인 파일들을 CDN에 복제하여 사용한다. 동적인 애플리케이션을 분산하는 것은 어려운 일이지만 정적인 파일들을 분산하는 것은 상대적으로 어렵지 않으며 효과도 크다.


3. Expires 해더를 설정한다.(Add an Expires Header)

이미지 뿐만 아니라 자바스크립트, CSS 등을 웹 브라우저 캐싱 기능으로 제어한다. 변경시에 이를 반영하는 효과적인 방안도 함께 강구한다.
 

4. Gzip 컴포넌트(Gzip Components)

텍스트 데이터는 모두 Gzip으로 압축한다. 서버 CPU 사용률이 증가하는 단점이 있지만 데이터 전송량을 줄이는데 가장 효과적이다.


5. 스타일쉬트는 위에 놓는다.(Put Stylesheets at the Top)

CSS 파일이 다운로드된 후 화면 구성이 된다. 따라서 화면이 점진적으로 나타나도록 하려면 CSS 파일이 내용(html)보다 먼저 다운로드되어야 한다. 표준에서도 link 태그는 head 태그에서만 사용할 수 있다.


6. 스크립트는 아래에 놓는다.(Put Scripts at the Bottom)

스크립트를 다운로드하는 동안에는 다른 파일 다운로드는 이루어지지 않는다. 따라서 화면 구성과 직접적으로 연관이 없는 스크립트는 맨 마지막에 위치시킨다.


7. CSS Expressions 사용을 지양한다.(Avoid CSS Expressions)

매력적인 기술이지만 UI 이벤트가 필터링되지 않기 때문에 매우 민감하게 반복적으로 반응한다. 결국 성능에 부정적인 영향을 야기하기 때문에 사용하지 않는다.


8. 자바스크립트와 CSS를 별도 파일로 분리한다.(Make Javascript and CSS External)

여러 화면에서 공유된다면 당연히 자바 스크립트와 CSS를 별도 파일로 분리한다.


9. DNS 찾기를 줄인다.(Reduce DNS Lookups)

도메인 이름을 최소화한다. 주요 웹 브라우저에서는 2개의 도메인을 사용할 때 최적화된 처리를 한다. 따라서 정적인 파일들은 처리하는 도메인을 별도로 분리하여 사용하는 것을 권장한다.


10. 자바스크립트를 최소화한다.(Minify Javascript)

여백을 제거하거나 로컬 변수명을 최소화해서 자바스크립트 파일 크기를 줄인다. 그 방법으로 Minification과 Obfuscation을 설명한다.


11. 리다이렉트 사용을 지양한다.(Avoid Redirects)

요청을 최소화한다는 측면에서 리다이렉트 사용을 지양한다. 리다이렉트에 대한 대안들을 설명한다.


12. 중복된 스크립트를 제거한다.(Remove Duplicate Scripts)

자바스크립트 뿐만 아니라 중복된 모든 것을 제거한다.


13. ETags를 사용하지 않는다.(Configure ETags)

클러스터링 환경에서 문제가 될 가능성이 다분하고 다른 대안이 있기 때문에 가급적 사용하지 않는다.


14. ....(Make Ajax Cacheable)

Ajax라고 크게 다르지 않다. 위에서 언급한 내용들을 그대로 준수한다.




이 책에서 제안하는 방법은 개발자보다는 시스템 엔지니어를 대상으로 한다. 따라서 적은 프로그램 수정 혹은 아파치나 IIS 등의 웹 서버 설정 변경만으로 위의 내용 대부분을 적용할 수 있을 것 같다.

2010-04-11

AJAX와 뒤로 가기 버튼

AJAX 혹은 DHTML을 사용할 때 뒤로 가기 버튼을 구현하는 방법이다.

Prototype 라이브러리
를 사용했다.


var Executer = Class.create({
  start: function() {
    new PeriodicalExecuter(function(pe) {
      if (!document.location.hash) {
        return;
      }
      var hash = document.location.hash.replace("#", "");
      if (hash == Executer.hash) {
        return;
      }
      Executer.hash = hash;
      if (hash.startsWith("menu")) {
        Menus.click(hash.substring(5));
     } else if (hash.startsWith("link")) {
        ...
     }
    }, 0.5);
  },
  execute: function(category, id) {
    window.location.href = "#" + category + "/" + id;
  }
});

사용 방법은 다음과 같다.

// onload 등을 이용해서 페이지 초기화시에 Executer 객체를 생성한 후에 start 메소드를 호출한다.

executer = new Executer();
executer.start();

뒤로 가기 기능을 제공해야 하는 경우 다음과 같이 처리한다.
<a href="#" onclick="executer.execute('menu', server/database'); return false;" >...</a>


동작 방법은 단순한다. 사용자가 뒤로 가기 기능이 필요한 행위를 할 때마다 window.location.href를 이용해서 url에 (#고유값)를 붙여준다. 이 경우에 화면이 변경되지지 않지만 웹 브라우저 경로창 URL과 히스토리 정보는 변경된다.

* IE 6에서는 안된다고 함. 이 경우에는 iframe을 사용하는 방법이 있다고 함.

그리고 백그라운드에서 주기적으로 스케줄러(여기서는 PeriodicalExecuter를 사용했는데, setInterval과 동일하다.)가 동작하면서 document.location.hash 값을 체크하여 변경이 있으면 변경 내용에 맞는 작업을 수행해준다.

* 스케줄러 호출 주기가 짧기 때문에 사용자는 거의 실시간 반응하는 것으로 느끼게 된다.

결국 사용자가 뒤로 가기 버튼을 눌러도 실제 어떤 작업이 이루어지는 것은 아니고 document.location.hash 값만 변경될 뿐이다. 물론 스케줄러가 변경 값을 확인하여 필요한 작업을 수행하게 된다.

hash 값에 따른 처리 코드는 상황에 맞게 수정해 주어야 한다. 여기서는 category를 이용해서 범주를 구분했다.



다음 내용을 참고했다.

Fixing the Back Button and Enabling Bookmarking for AJAX Apps

* 파이어폭스 3.6.3에서만 테스트를 했다.

2010-04-01

Ant 이야기

아파치 Ant를 쓴지도 8년이 넘었지만 제대로 파악하지 않고 대충 아는 지식에만 의존했기 때문에 항상 결과물에 모자람을 느껴왔다.

그러다 지금하는 일의 완성도를 높여야 한다는 당위성에 Ant에 시간 투자해서 얻은 경험 중 중요한 몇 가지를 정리해봤다.

Ant 작성에도 프로그래밍과 유사한 원칙들이 적용된다.

  • 내용 이해가 쉬워야 한다.
  • 중복된 내용이 없어서 변경하기 쉬워야 한다.

이런 원칙을 염두하고 다음 방법들을 사용했다.


이름을 명확하게


빌드 파일, 속성, 태스크 등의 이름을 길더라도 명확하게 설정한다.

예를 들어 테스크 이름을 copy로 하기보다는 copy_lib_to_web과 같이 명확하게 한다.


빌드 파일을 여러 개로 분리해서 모듈화 한다.

OOAD에서 클래스를 쪼개는 것과 동일하다. import 태스크와 antcall 태스크로 각 모듈을 효과적으로 조합할 수 있다.


라이브러리 작성

여러 빌드 모듈에서 사용하는 것들을 별도 파일에 정의한다. path 정보와 함수 역할을 하는 태스크 등이 여기에 해당된다.

라이브러리 모듈을 잘 작성하면 Ant 파일 내용을 크게 줄일 수 있다.

library.xml

<project name="library">

    <property file="build.properties" prefix="dimdol." />

    <path id="path.web.lib">
        <fileset dir="./lib">
            <include name="commons-fileupload-1.2.1.jar" />
            <include name="commons-io-1.4.jar" />
            <include name="commons-lang-2.4.jar" />
        </fileset>
    </path>

    <target name="library.compile">
        <javac srcdir="${source}"
               destdir="${target}"
               classpathref="${classpath}"
               source="${dimdol.compile.option.source}"
               target="${dimdol.compile.option.target}"
               debug="${dimdol.compile.option.debug}"
               encoding="${dimdol.compile.option.encoding}" />
    </target>

</project>

build_base.xml

<project name="build base" default="default">

    <import file="library.xml" />

    <target name="default">
        <antcall target="library.package_jar">
            <param name="source" value="${dimdol.base.src}" />
            <param name="target" value="${dimdol.base.target}" />
            <param name="classpath" value="path.web.lib" />
            <param name="jar" value="${dimdol.base.jar}" />
        </antcall>
    </target>

</project>

build_crm.xml

<project name="build crm" default="default">

   <import file="library.xml" />

   <target name="default">
       <antcall target="library.package_jar">
           <param name="source" value="${dimdol.crm.src}" />
           <param name="target" value="${dimdol.crm.target}" />
           <param name="classpath" value="path.web.lib" />
           <param name="jar" value="${dimdol.crm.jar}" />
       </antcall>
   </target>

</project>


컴파일을 담당하는 library.package_jar 태스크와 컴파일에 필요한 클래스 패스를 정의한 path.web.lib 패스를 library.xml 파일에 정의하고 build_base.xml, build_crm.xml 파일은 이 파일을 import한다.

* include가 더 유연하지만 Ant 1.8 부터 사용할 수 있다.


property는 properties 파일에 설정한다.

properties 파일에서도 ${}를 통해서 내용을 최소화한다.

build.properties

source = ./source
target = ./target
jar = ./jar

base.src = ${source}/base
base.target = ${target}/base
base.jar = ${jar}/base.jar

crm.src = ${source}/crm
crm.target = ${target}/crm
crm.jar = ${jar}/crm.jar

그런데 이 내용을 Ant 파일에서 사용할 때는 다른 많은 property와 쉽게 구분하기 위해서 prefix를 붙여 사용한다.

<property file="build.properties" prefix="dimdol." />

따라서 실제 사용할 때는 다음과 같이 한다.

<antcall target="library.package_jar">
   <param name="source" value="${dimdol.base.src}" />
   <param name="target" value="${dimdol.base.target}" />
   <param name="classpath" value="path.web.lib" />
   <param name="jar" value="${dimdol.base.jar}" />
</antcall>