2012년 10월 30일 화요일

[javascript] mobile agent 쉽게 구분하기

최근에는 하나의 웹페이지를 만들면 그에 대응하는 모바일 웹페이지를 만드는 것이 필수처럼 되어버렸습니다.

이 경우 아예 도메인을 분리하는 경우도 있고, javascript에서 분기처리를 하는 경우도 있습니다. javascript에서 분기처리를 하는 경우 다음과 같이 agent를 체크하게 됩니다.

var mobileKeyWords = new Array('iPhone', 'iPod', 'BlackBerry', 'Android', 'Windows CE', 'LG', 'MOT', 'SAMSUNG', 'SonyEricsson');
for (var word in mobileKeyWords){
    if (navigator.userAgent.match(mobileKeyWords[word]) != null){
        // 모바일 작업
        break;
    }
}

하지만 agent 체크의 경우 새로운 기기가 나오거나, 새로운 모바일 브라우저를 이용하거나.. 하는 경우 무용지물이 될수있습니다.

또, 타블릿의 처리도 문제입니다. agent 속성은 mobile과 비슷한데 화면은 충분히 커서 PC화면을 보여주는게 나을 때가 있습니다.

생각해보면 모바일 agent에 대한 분기처리를 하는 이유는, 모바일 환경의 화면이 작기 때문이지.. 그 agent의 속성이 특이하기 때문이 아닌 경우가 대다수입니다.

대다수라는 표현을 사용한 이유는 모바일에서 특정 스크립트나 css가 먹히지 않는 경우가 존재하기 때문입니다. 대표적으로 mobile 환경에서는 overflow: scroll이 먹히지 않습니다.

따라서 단순히 화면이 작기 때문에 mobile에 대한 분기 처리가 필요한 경우라면 오히려 다음과 같은 코드가 쉽고, 직관적이며, 영속적일 수 있습니다.

jquery 기준으로 다음과 같은 코드가 됩니다.

if ($(window).width() < 450 || $(window).height() < 450) {
    // 모바일 작업
}

2012년 7월 28일 토요일

[java] Date classes의 사용을 자제하자.

JDK에는 기본적으로 여러 편리한 library들이 포함되어있습니다.

마치 웹 표준인 것 마냥 널리 사용되는 Java지만, 그중에는 잘 설계된 것이 있는 반면, 잘못 설계된것도 많습니다.

날짜 계열 Class들은 잘못설계되서 욕을 바가지로 먹고있는 대표적인 class들입니다.
구체적으로는 다음 class들이 해당됩니다.

java.util.Date
java.sql.Date
java.sql.Timestamp
java.util.Calendarjava.util.GregorianCalendar
java.util.TimeZone
java.util.SimpleTimeZone
java.text.DateFormatjava.text.SimpleDateFormat
java.text.DateFormatSymbols

이 class들이 욕먹는 이유는 대체적으로 다음과 같습니다.

1. Mutable하다
객체는 기본적으로 Immutable한것이 좋습니다. mutable하지 않은 경우 의도치 않게 객체의 정보가 변경되어 버그의 온상이 됩니다. 특히 multi thread환경이라면 더욱 그렇습니다.
실제로 SimpleDateFormat의 경우 static으로 박아놓고 사용해, 문제가 되는 경우가 잦은 편입니다.
SimpleDateFormat의 문서에는 이 class가 thread-safe하지 않음을 명시해놓고 있지만, 만약 이 클래스가 Immutable하다면 이런 명시도 필요없었을 겁니다.

2. 불필요한 CheckedException을 던진다.
SimpleDateFormat.parse를 사용하면 다음과 같이 ParseException을 던지고 있습니다만, 실제로 Checked Exception은 꼭 필요한 경우가 아니면 사용을 지양하는 것이 좋습니다.

try {
    Date date = new SimpleDateFormat("yyyyMMdd").parse("20120508");
    System.out.println(date);
} catch (ParseException e) {
    e.printStackTrace();
}

게다가 기껏 ParseExceptin을 던지면서도 실상 Exception을 제대로 던지고 있지도 못합니다.
날짜에 20120508이 아니라 20129931나,  222220120508과 같이 실제로 ParseException을 던져야하는 문자열을 입력해도... Exception을 뱉지않고 넙죽넙죽 Parsing해버리고 맙니다.
정말 필요없는 Checked Exception을 던지고 있는겁니다. 참고로 잘못된 입력의 경우 다음과 같은 결과를 내놓고 있습니다.

Date date = new SimpleDateFormat("yyyyMMdd").pare("20109931");
    -> Sat Mar 31 00:00:00 KST 2018

Date date = new SimpleDateFormat("yyyyMMdd").pare("222220120508");
    -> Sun Jul 08 00:00:00 KST 2553 

3. 날짜 계산이 복잡하다.
add정도의 method는 지원하고 있습니다만, 이는 가장 단순한 형태만 지원하는 것으로.. 실제 날짜 계산에 들어가면 적절한 메소드가 없어서 생으로 계산하게 되는 경우가 많습니다. Date나 Calendar를 이용해 날짜 계산을 해보신 분들이라면 느끼셨을 불편함으로.. 여기서는 딱히 예를 들지 않겠습니다.

4. 월이 0부터 시작한다.
new Date().getMonth() 메소드로 월을 구하면 꼭 +1을 해줘야하는 불편함이 있습니다.
1월인 경우 0이나오고 2월인 경우 1이 나오기 때문에, 숙련된 프로그래머라도 종종 실수하게 되는 부분입니다.
처음에는 외국에서는 Jan, Feb와 같이 영문 표기를 즐겨 사용하니 그렇게 했나보다 헀는데.. 외국 포럼에도 Month가 0부터 시작하는 사실을 모르고 버그냐고 물어보고.. 그런 게시물들이 있더군요.

5. TimeZone 변경에 따른 잘못된 시간표기
특정한 날짜를 다음과 같은 코드로 표기하면 다음과 같이 오차가 나오는 경우가 발생합니다.

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse("1988-05-08 00:00:00"));

Expected                => Actual
1904-12-01 00:00:00 => 1904-12-01 00:30:00
1932-01-01 00:00:00 => 1932-01-01 00:30:00
1960-05-15 00:00:00 => 1960-05-15 01:00:00
1961-08-10 00:00:00 => 1961-08-10 00:30:00
1968-10-01 00:00:00 => 1968-10-01 00:30:00
1987-05-10 00:00:00 => 1987-05-10 01:00:00
1988-05-08 00:00:00 => 1988-05-08 01:00:00

말하자면 1904년 12월 1일 00시 00분 00초를 파싱했더니 1904년 12월 1일 00시 30분이 나오는 겁니다. 이는 우리나라가 과거 몇번 우리나라 고유의 TimeZone을 사용했다가 일본것으로 바꾸고 하는 과정이 있었는데.. 이때 생긴 오차 때문인 것으로 알고있습니다.

그렇다고 해서, 이렇게 제멋대로 시간을 바꿔버리면... 사용하는 입장에서는 곤혹스럽기 마련입니다. 이런 경우 차라리 명확하게 Exception을 던지거나 하는 것이 낫죠.

6. 그외..
그외에도 외국에서는 서머타임이나 이런 부분 때문에 혼동스러운 부분이 많은 모양입니다.
Deplicated된 메소드도 유난히 많은데... 처음 설계자체가 좋지않은 것을 알 수 있습니다.
만약 추가적인 문제를 알고있으시다면 댓글로 달아주시면 감사하겠습니다. 본문에 추가하도록 하겠습니다.


대략.. 위와 같은 이유로 Java의 Date Classes들은 사용을 지양하는 것이 좋습니다. 대체품을 사용하는 것이 좋은데, 가장 추천할만한 것으로는 JodaTime(http://joda-time.sourceforge.net/)이 있습니다.
쪼다타임으로 읽어야할지 요다타임으로 읽어야할지 약간 망설여지는 이름입니다만...

날짜 표기 방식으로 국제 표준인 ISO8601을 사용하고 있고 JSR310에도 영향을 끼친 것으로 알고있습니다. JSR310이 아직 나오지 않은 지금으로서는 거의 유일한 대안이라고 보여집니다.

[eclipse] eclipse juno에서 sts 사용하기

오랜만에서 이것저것 개발관련 글들을 보다보니, eclipse juno가 나와있었네요.
6월 27일에 나온 모양인데, 한달이나 지나 알게됐습니다. -_-

새로 설치하고 나서 STS를 설치하려고 보니, 아직 juno용 STS가 나오지 않은 모양입니다.

아직 개발중인 모양으로, 다음 페이지를 참고하시면 미리 사용해볼 수 있습니다.

[java] interface의 필요성

Interface는 Java 초심자 입장에서는 참으로 이해하기 힘든 존재입니다.

이미 Class가 있는데 굳이 Interface라는 문법이 필요한가도 그렇고, 이 Interface가 실질적으로 로직은 기술할 수 없는 반쪽짜리 class라는 점이 더욱 그렇습니다.

이 Interface를 이해하기 위해서 USB를 예로 들어보겠습니다.

모든 컴퓨터 마다 몇개씩 달려있는 USB단자는 매우 편리합니다. 어떠한 주변기기든 USB에 꼽기만 하면 그걸로 O.K. 입니다.

우리는 아무 생각없이 사용하고 있지만, USB라는 표준이 없었을때 얼마나 불편한지를 생각해보면 이 USB라는 표준하나로 얼마나 많은 사람들이 편리함을 얻고있는지 알수있을 겁니다.

마우스, 키보드, 그외 각종 주변기기가 종류별로, 제조사 별로 모두 제각각의 단자를 가지고 있다면 기껏산 마우스가 자신의 컴퓨터에 맞지 않거나 하는 문제가 빈번히 발생할 겁니다. 또 이 문제를 해결하기 위해서 우리는 수많은 종류의 아답터를 구비해 놓고있어야할겁니다.

Interface는 USB표준과 같은 역할을 하는 것으로, 만약 여러분이 작성한 class가 파라미터를 Interface로 받도록 구현해놓았다면, 어떤 클래스든지 여러분이 만든 class의 기능을 이용하기 위해서 그 Interface만 구현하면 될겁니다.

이는 모든 마우스, 키보드 그외 컴퓨터 주변기기 제조사들이 USB표준대로만 물건을 만들어 팔면 별 고민없이 여러분의 컴퓨터에서 사용할수있는 상황과 같습니다.

실제 코드상의 예를 들자면 Java의 Comparable이 좋은 예가 될겁니다.
http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html

정렬을 원하는 class라면 그 class가 무엇이든지 Comparable을 구현한 후, Collections.sort에 집어넣으면 정렬이됩니다.

만약 Java의 정렬을 구현한 개발자가 이와같은 설계대신 Integer, String, Date와 같은 기본적인 class의 정렬만 따로 구현해놨다면, 우리는 정렬이 필요할 때마다 직접 정렬 로직을 구현하거나, 정렬을 도와주는 helper class등을 따로 이용해야했을 겁니다.

게다가 Class의 정렬규칙이 변경되기라도 한다면 그 정렬 로직자체의 수정이 불가피했을 겁니다.

하지만 Java에서는 정렬이라는 행위를 추상화하고 Interface를 이용함으로서 이러한 불편함을 최소화했습니다.


우리가 Class를 설계할때도 항상 이러한 부분을 고려해야합니다. 자신이 만들고자 하는 기능을 최대한 작은 부분 부분으로 나누고 추상화해서 범용적으로 사용될 수 있는지 생각해 봐야합니다.

범용적으로 사용될만한 기능이 있다면, '이 기능을 사용하고 싶으면 이 Interface만 구현해.'
라고 설계합니다.

추후 로직의 재사용뿐만 아니라 유지보수시에도 상당히 편리해집니다.




2012년 5월 20일 일요일

[java] object generator library는 immutable class를 만들수없다.

Effective Java에서는 별다른 이유가 없다면 class 설계시 Immutable class로 만들것을 권하고 있습니다.

원하지 않는 정보수정과 멀티 쓰레딩에 대해 고민할 거리가 없어지니, 매우 좋은 조언이라고 생각합니다.

하지만 우리가 자주 사용하는 library들 중에 원천적으로 이러한 원칙을 위배하고 있는 것들이 있습니다.
mybatis, hibernate, jaxb, jackson, gson...
등이 그렇습니다. 이러한 library들은 애초에 Immutable한 객체를 생성해낼 수 없습니다.

위 library들은 class가 아닌 무언가를 참고해서 객체를 자동으로 생성해준다는 준다는 공통점이 있습니다.
mybatis와 hibernate는 RDB를 참고해서 객체를 생성해주며, jaxb는 XML을 참고해 객체를 생성합니다. jackson과 gson은 json을 참고해 객체를 자동 생성합니다.

위 library들이 Immutable한 객체를 만들수 없는 이유는 그 생성과정 자체에 있습니다.
대부분의 경우 다음과 같은 방식으로 객체를 자동으로 생성합니다.
1. reflection을 이용해 기본 생성자로 객체를 생성
2. reflection을 이용해 setter를 호출하거나 field에 직접 주입하는 방식으로 객체에 정보를 삽입
3. 사용자에게 반환

Immutable이라하면 객체 초기화시에 모든 정보가 설정되고 그 이후로는 정보를 바꿀 수 없는 객체를 의미합니다.

하지만 위 과정을 보면 1번에서 객체를 만들고 그 이후에 2번 과정을 통해서 객체에 정보를 주입합니다. 즉, 만든 이후에 정보에 변경이 있게되는 것이고.. 이러한 과정 자체가 Immutable이라는 개념과 충돌하게 됩니다.

그럼 왜 객체를 자동으로 생성해주는..(제목에서는 object generator라고 지칭했습니다.)
library들은 중요 개념인 Immutable을 버린걸까요...

이는 library들의 설계상 오류나 한계라기 보다는 Java라는 언어의 한계라고 보여집니다.
생성시 모든 정보를 객체에 넣자면 생성자를 사용해야할텐데, reflection의 생성자 콘트롤 관련 메소드들을 보면...
생성자의 어떤 파라미터에 어떤 값을 넣어야하는지 충분한 정보들을 얻을 수 없음을 알 수 있습니다. 하고싶어도 못하는 형편인것이죠.

즉, 만약 여러분이 진행하는 Project의 모든 Statefull 객체를 Immutable로 만들고자 한다면, 이 시도는 즉시 난관에 부딫히게 될겁니다.
사용하게 될 주요 library들이 모두 Immutable이라는 개념을 포기하고 있으니까요.

만약 이러한 library를 충분히 이용하면서, Immutable이라는 목표 또한 달성하고자 한다면, 중간에 합의점을 찾아야 합니다.
모든 객체를 자동으로 생성해주는 hibernate보다는, 수동으로 객체를 생성할 수 있는 jdbc template를 사용하는 것이 나을지도 모릅니다.
json을 바로 객체로 변환하지 않고, Map으로 변환한 이후에 수동으로 그 Map의 값들을 객체에 set해주는 것이 정답일수도 있습니다.

그나마 mybatis의 경우에는 constructor의 몇번째 index에 어떤 파라미터를 넣을 것인지를 XML에 지정할 수 있어 이 문제를 피해갈수있습니다.

물론.. 이와같은 경우, 자동화된 객체 생성 방법이 아니라 수동으로 일일이 값을 Mapping 시켜주는 것에 불과하다는 근본적인 문제가 여전히 있습니다.

보통, Immutable을 포기하고 setter getter가 무분별하게 쭈욱 달린 객체를 사용함으로서 편리함을 택하고 별 문제없이 프로젝트가 진행되기 마련입니다. 하지만, object generator library를 사용할 때는 최소한 이러한 문제 의식을 갖고 사용하면 좋을겁니다.


[eclipse] javascript validation check 제외 하기

오픈소스 javascript를 가져와 사용하는 경우 다음과 같이 validation check가 쭉 뜨는 경우가 많습니다.
javascript의 경우 문법에 관대한 편이다보니 세미콜론등을 빠트려도 정상적으로 동작하거니와... 아무래도 용량을 줄이기 위해 일부러 빼고 배포하는 경우가 많은것 같습니다.



실제 개발에 지장이 있는 것은 아니지만 찝찝한 일이 아닐 수 없죠.

이런 경우 다음과 같이 특정 directory를 validation check에서 제외함으로서 해결이 가능합니다.

위 예의 경우 문제가 되는 bootstrap.js와 bootstrap.min.js는 resources/bootstrap directory에 존재하므로, 이 directory를 javascript validation check에서 제외해보겠습니다.

1. Project 우클릭 - Properties - JavaScript - Include Path - Source 로 이동한다.
2. Exclude를 선택후 Edit 버튼을 누른다.
3. 팝업이 뜨면 Exclusion patterns의 Add 버튼을 눌러 validaion check에서 제거할 목록을 구성한다.
4. validation check에 입력하는 문자열은 Ant 타입의 표현식을 이용한다.

제 경우 다음과 같이 입력했습니다.
src/main/webapp/resources/bootstrap/

다음과 같이 추가가되면 완료된 것으로, 더이상 오류가 나지않음을 알 수 있습니다.


이는 실제로는 Eclipse로 하여금 지정한 Path의 js를 Javascript로 인식하지 못하게 한 것인데, 외부 library이고 직접 수정할 Javascript가 아니라면 이 방법이 유용할겁니다.


Include 설정도 있으니, 적절히 사용하면 됩니다.