1부터 1000까지의 임의의 아라비아 숫자를 로마식 숫자로 바꿔주는 프로그램입니다.
기선님 블로그 보고 시간나면 해봐야지 하다가 며칠전에야 결심하고 점심시간에 1시간동안
코딩을 했는데 생각보다 쉽지 않았습니다. 1시간에 끝내려다가 결국 3시간 넘게 걸렸네요. -.-

우선 규칙을 파악하고 파악된 규칙을 코드로 풀어내는게 결코 쉽지 않더군요.
게다가 토비님의 15줄 언급으로 압박이 생겨서인지 짱구가 잘 안굴려진다는... ㅠㅠ

package kr.pe.javarss;
public class RomanConverter {
 String deciRomans[] = { "I", "X", "C", "M" }; // 1,10,100,1000에 대응하는 로마숫자
 String pentaRomans[] = { "V", "L", "D", "ↁ" }; // 5,50,500,5000에 대응하는 로마숫자
 public String convert(int number) {
  String roman = "";
  for (int i = 0; i < deciRomans.length && number != 0; i++, number /= 10) {
     roman = convertDigit(number % 10, i) + roman;
  }
  return roman;
 }
 // 0~9까지 숫자를 자릿수(1의 자리=0, 10의 자리=1,...)에 따라 로마숫자로 변환
 private String convertDigit(int digit, int pos) {
   if (digit < 4) return org.apache.commons.lang.StringUtils.repeat(deciRomans[pos], digit);
   if (digit == 4) return deciRomans[pos] + pentaRomans[pos];
   if (digit < 9) return pentaRomans[pos] + convertDigit(digit - 5, pos);
   if (digit == 9) return deciRomans[pos] + deciRomans[pos + 1];
   return "";
 }
}


결과는 15줄보다 조금 많은 23줄이 나왔습니다.(이클립스 소스포맷하면 몇줄 더 늘어나요 -.-;)
그래도 초록막대를 보면 기분이 좋아집니다. 즐거운 경험이었습니다. ^^


* 느낀 점
- 테스트케이스가 로직을 만들어 주지는 않는다. 테스트는 로직에 대한 확신과 힌트를 제공할 뿐,
  로직은 개발자의 머리에서 나오는 것이다.
 
- 자바언어는 별로 재미가 없다. 머릿속의 아이디어를 표현하기 위한 융통성과 지원구문이 부족하다.

Posted by 에코지오
,
Rails의 ActiveRecord::Migration과 비슷한 기능을 가진 자바쪽 데이터베이스 리팩토링 라이브러리 2개.

* LiquiBase : http://www.liquibase.org/

리퀴베이스는 데이터베이스 변경이력을 XML로 관리한다. 그러나 이클립스의 기본 Data Explorer 플러그인에
다양한 DB 리팩토링 메뉴를 추가한 플러그인을 제공하기 때문에 직접 XML 파일을 편집할 필요는 별로 없다.
DB 마이그레이션이 아니더라도 여러가지 DDL 작업을 GUI 모드로 실행할 수 있어서 좋은 거 같다.
이클립스 외에 Ant 타스크 및 Maven 플러그인도 제공한다.

리퀴베이스 이클립스 플러그인



* migrate4j : http://migrate4j.sourceforge.net/

자바코드로 변경을 기록한다. Rails의 db:migrate와 유사하지만 아직은 문서도 부족하고 여러모로
딸리는 듯한 느낌이다.

사용자 삽입 이미지


두 라이브러리 모두 애자일에서 말하는 데이터베이스 리팩토링 개념을 구현한 도구로 이해할 수 있을 거 같은데,
구체적으로 어떻게 써먹어야 할지는 아직 감이 잘 오지 않는다.

그외 읽을꺼리.
- 하이버네이트를 사용한 애자일 데이터베이스 리팩토링
- Evolutionary Database Design
- 리팩토링 데이터베이스
Posted by 에코지오
,
YAGNI에 이은 TAGRI 라... ㅎㅎ 공감이 간다.

http://www.agilemodeling.com/essays/tagri.htm
http://www.theserverside.com/blogs/thread.tss?thread_id=42984


요며칠 동안 TAGRI한 문서를 줄창 작성하고 있따...ㅠㅠ
Posted by 에코지오
,
애자일 방법론에서 S/W아키텍처는 어떤 식으로 설계하는가?

왜 애자일 방법론에서는 S/W아키텍처 설계, 데이터베이스 모델링에 대해 얘기하지 않는가?

관련된 한글 자료가 별로 없어서 실망하다가, 그나마 하나 괜찮은 기사를 건졌다.

애자일(Agile) 프로세스 및 모델링을 사용한 엔터프라이즈 애플리케이션 구축

[agile java development with spring, hibernate and eclipse] 책을 참고해서 작성했다고
하는데, 뜬구름 잡는 얘기가 아니라 실제로 프로젝트를 애자일 기법으로 개발해나가는
예제를 볼 수 있는 좋은 자료가 아닌가싶다. 다만, 퍼시스턴스 티어 구현을 위해 하이버네이트를
사용하기 때문에 그런건지는 잘 모르겠지만 DB설계를 어떤 식으로 풀어내는지에 대한
얘기가 거의 없는 것이 쬐끔 아쉽다.


관련 사이트.
http://www.agilemodeling.com/ 애자일 모델링 이론
http://www.agiledata.org/ 애자일 데이터베이스 설계
http://www.agilearchitect.org/ 애자일 아키텍처 설계
http://visualpatterns.com/ 애자일 모델링/아키텍처
http://www.agilealliance.com/articles 애자일 아티클 모음

http://remark.wordpress.com/2008/02/19/agile-architecture/
http://c2.com/cgi/wiki?CanAnArchitectureEmerge
http://www.agilejournal.com/blogs/agile-junction/agile-architecture.html

Posted by 에코지오
,

String noHTMLString = htmlString.replaceAll("\\<.*?\\>", "");


String regex1 = "\\<.*?\\>";
String regex2 = "<(/)?([a-zA-Z]*)(\\s[a-zA-Z]*=[^>]*)?(\\s)*(/)?>";
String html = "가<aaa>나</bbb>다<ccc/>라<한글>마<ddd >바< eee >사< img src=\"한글\" >아\n";
System.out.println(html.replaceAll(regex1, ""));
System.out.println(html.replaceAll(regex2, ""));

==== 실행결과 ======
가나다라마바사아
가나다라<한글>마바< eee >사< img src="한글" >아


참고 사이트
http://snippets.dzone.com/posts/show/4018 
http://javacan.madvirus.net/main/content/read.tle?contentId=122 (최범균님)

http://forum.java.sun.com/thread.jspa?threadID=683818&messageID=3983002
http://www.rgagnon.com/javadetails/java-0424.html
http://java-source.net/open-source/html-parsers
Posted by 에코지오
,
* agimatec-validation  http://code.google.com/p/agimatec-validation/

JSR 303: bean-validation 스펙을 구현한 라이브러리. 아래는 agimatec-validation 사용예제.

javax.validation.Validator customerValidator = new ClassValidator(Customer.class);
Set<InvalidConstraint<Customer>> violations = customerValidator.validate(customer);
public class Customer{
    @NotEmpty(groups = "last")
    private String firstName;
    @NotEmpty(groups = "first")
    private String lastName;
    @Length(max = 30, groups = "last")
    private String company;
    @Valid
    private List<Address> addresses;
   
    ....
}

그 밖에 다음 글도 참고할 것.

- Bean Validation Sneak Peek http://in.relation.to/Bloggers/BeanValidationSneakPeekPartI
- 기선님 Spring MVC Validation 정리(스프링 validator와 valang)
- 기선님 Really easy field validation 사용하기(이건 css를 통해 브라우저단에서 입력값을 검증하는 것)
- 스트러츠2에서 어노테이션방식 입력값검증 http://struts.apache.org/2.x/docs/validation-annotation.html
- 하이버네이트 Validator http://www.hibernate.org/hib_docs/validator/reference/en/html/validator-defineconstraints.html
- springmodules https://springmodules.dev.java.net/docs/reference/0.8/html/validation.html#d0e9699
Posted by 에코지오
,
- this.getClass().getResource();

- this.getClassLoader().getResource();

- Thread.currentThread().getContextClassLoader().getResource();

- MyClass.class.getResource();

위 4가지의 차이점은 아래 링크 참조.

http://www.javaworld.com/javaworld/javaqa/2002-11/02-qa-1122-resources.html
http://www.jroller.com/coreteam/entry/loading_configuration_files_under_web


그럼 스프링 사용환경에서는 보통 어떻게 로딩하지?

일단 링크 걸어두고 읽어봐야겠다.

http://static.springframework.org/spring/docs/2.5.x/reference/resources.html
http://www.carbonfive.com/community/archives/2007/05/using_classpath.html


Posted by 에코지오
,

1. Selenium IDE : http://selenium-ide.openqa.org/
- 불여우 플러그인
- GUI 모드로 UI를 테스트


2. Watij - Web Application Testing in Java : http://watij.com/
- 사용자 UI 액션을 코딩하여 테스트

3. Canoo Webtest : http://webtest.canoo.com/
- ant XML 형식으로 테스트케이스 작성


* 참고 글
자바지기님 블로그
http://javajigi.tistory.com/tag/테스트%20자동화

개발 고수를 향한 날개짓, 프로그램 테스팅
http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=30542

Posted by 에코지오
,
지금까지 트랜잭션 안에 있기만하면 준영속 상태의 객체에서 늦은 로딩이 가능한 줄 알았는데, 그게 아니었다.

늦은 로딩은 영속 상태에서만 즉, 객체가 세션과 연결된 상태에서만 적용된다.

이런 기초적인 것도 모르고 하이버네이트로 코딩을 한다는 것이 부끄럽다.

하이버네이트 객체 생명주기


그럼 준영속 상태의 객체를 파라미터로 받는 메소드(A)에서는 어떻게 늦은 로딩으로 설정된 연관 데이터를 갖고와야할까?

1. 준영속 객체를 session.update(), saveOrUpdate()를 이용하여 영속 상태로 만든 후에 늦은 로딩하는 방법.
- 이 방법은 필요없는 UPDATE 또는 SELECT 쿼리가 추가적으로 실행되는 문제점이 있다.

2. 준영속 객체의 키를 이용하여 session.get()을 통해 영속 객체를 로딩한 후 늦은 로딩하는 방법.
- 이것도 SELECT 쿼리가 추가적으로 실행이 된다.

3. 처음부터 연관데이터를 미리 로딩한 뒤 메소드에 넘겨주는 방법.
- 영속 객체를 메소드 A에 넘기기 전에 Query에서 fetch 키워드를 쓰거나 또는 Hibernate.initialize()를 이용하여 연관 데이터를 미리 객체에 담아둔다. 만약 메소드 A가 시간이 좀 걸리는 작업이고 연관 데이터가 수시로 변한다면 연관 데이터의 싱크가 문제가 될수 있다.

아마 대부분의 경우 별거 아닐텐데 내 경우에는 3번은 불가하고, 1,2번 중에서 골라야 한다. 음...
Posted by 에코지오
,

아래 글의 트랜잭션 처리와 관련하여 내가 겪었던 상황을 살펴볼까한다.

- 삽질했던 상황 -

DB로부터 목록을 조회한 후 목록안의 개별 아이템에 대한 작업을 수행한 뒤
개별 아이템별로 작업결과를 DB에 반영한다.

로직은 별로 복잡하지 않다. 근데 문제는 이거 2가지다.

- 전체 작업이 아니라 개별 작업결과를 커밋하길 원한다.
- 개별 작업은 1~10초 정도 시간이 걸리는 네트워크 작업이다.

처음에 아래처럼 코드를 작성했다.

public void 전체작업() {
  전체 목록 조회;
  for (아이템 : 전체목록) {
      try {
          아이템작업(아이템);
      } catch(에러) {
         //로깅
      }
  }
}

public void 아이템작업(아이템) throws 네트워크에러, DB에러 {
    네트워크작업(아이템);
    작업결과처리(아이템);
}

@Transactional
private void 작업결과처리(아이템) {
     // 아이템 가공
     dao.save(아이템);
}

나름 트랜잭션 스코프도 최소화하고 독립적으로 개별 아이템작업을 처리하기 위해 신경썼는데,

무지막지하게 삽질을 해댔으니... RTFM이 떠오른다.

이제 위 코드에서 왜 트랜잭션이 작동하지 않는지 이유를 알았으니 해결방안을 찾아야겠다.

Posted by 에코지오
,