'2008/12'에 해당되는 글 12건
- 2008/12/31 올해의 스프링 기술 top5, 내년 유망 스프링 기술 top5 (javajigi 편) (1)
- 2008/12/30 올해의 스프링 기술 top5, 내년 유망 스프링 기술 top5 (안영회 편) (4)
- 2008/12/30 [Spring batch] 스프링배치 연재(15) 하이버네이트 활용과 여러파일 읽기
- 2008/12/29 올해의 스프링 기술 top5, 내년 유망 스프링 기술 top5 (benelog 편)
- 2008/12/27 [Spring batch] 스프링배치 연재(14) 드라이빙 쿼리와 iBatis의 활용
- 2008/12/19 WOC 2008(Winter of Code) 행사 공지 (1)
- 2008/12/16 [Spring batch] 스프링배치 연재(13) 스프링배치의 형제들
- 2008/12/11 [Spring batch] 스프링배치 연재(12) 이벤트 처리, 유효성 검사, 변환, 기존 클래스 활용
- 2008/12/10 Spring One America 2008 참석기 링크 모음
- 2008/12/10 [Spring batch] 스프링배치 연재(11) 재시작과 재시도
안녕하세요. 박재성입니다.
Spring 프레임워크와 관련해서는 두 분이 너무 잘 정리해주셔서 자바 개발자라면 관심을 가져야 될 것으로 생각되는 부분을 정리해봤습니다. Spring 프레임워크와 관련되지 않아서 활용하기 힘들다면 그렇게 하셔도 괜찮습니다. 2008년과 2009년으로 나누지 않고 최근의 기술 흐름에 대해서 QCon에서 느꼈던 부분을 정리했습니다. 이런 글을 적는다는게 생각보다 쉽지 않네요.
1. Domain Driven Design(DDD)
2004년에 Eric Evans가 Domain Driven Design 책을 출간했지만 아직 적용한 곳이 많지 않은 상태이다. 그러나 점점 더 많은 활용사례가 나타나면서 DDD에 대한 중요성이 부각될 것으로 생각한다. 이미 자바 뿐만 아니라 C#에서도 활용사례가 나타나고 있다. Spring Batch가 DDD 사상으로 개발되었다는 글(http://java-chimaera.blogspot.com/2008/11/spring-batch-domain-driven-design-in.html)도 볼 수 있으며, 2009년의 Spring 로드맵에는 DDD 기반의 소스 생성기인 ROO가 포함되어 있는 것도 주목할 만하다. 앞으로 DDD 기반으로 개발된 애플리케이션이 점점 더 증가할 것으로 예상된다.
2. Domain Specific Language(DSL)
지금까지 특정 Context에 맞는 구문들이 많들어져 사용되어 왔다. 예를 들어 SQL, CSS, Junit, Ant등과 같이 특정 Context에 맞는 형태로 구문을 정의하고 사용되어 왔는데 최근에 이를 통틀어 DSL로 지칭하고 있다. RSpect, JBehave와 같은 DSL이 등장하면서 DSL에 대한 관심도가 더욱 높아지고 있다. 업무 분석가와 고객이 좀 더 쉽게 이해할 수 있는 언어로 테스트 시나리오를 작성하고 이를 실제 애플리케이션 테스트에 적용할 수 있다면 더 없이 좋을 듯하다. 국내 현실에서는 좀 더 많은 시간이 필요하겠지만 외국은 이미 움직임이 시작되고 있다.
3. Ployglot와 Poly-Paradigm
최근의 경향은 하나의 애플리케이션을 개발하기 위하여 여러 개의 언어를 같이 사용하며(Ployglot), 여러 개의 패러다임(Poly Paradigm)을 같이 사용하는 방향으로 움직이고 있다. 예를 들어 Adobe Lightroom은 C++ 과 Lua, Google Android는 Linux + libraris(C)+ Java로 개발되어 있다. 또한 애플리케이션의 요구사항따라 Object Language와 Functional Language를 혼용해서 사용하는 사례들이 나타나고 있다. 자바 개발자에게 Grails와 같은 프레임워크가 널리 보급된다면 이와 같은 방식의 접근 방법에 거부감을 쉽게 없앨 수 있을 것으로 기대한다.
4. 애자일 프로세스의 성장
해외에서는 이미 애자일 프로세스가 많이 보급되었으며, 다양한 적용 사례가 컨퍼런스를 통하여 공유되고 있다. 애자일 프로세스에 맞는 아키텍트의 역할까지 논하고 있는 상황이다. 아직 국내에서는 많은 프로젝트에서 적용하지 않고 있는 상황이지만 점진적으로 성장할 것으로 판단된다. 애자일 프로세스의 실천 방법 중에 일부분이라도 적용하는 프로젝트가 많아질 것으로 예상한다. 그 중에서 잦은 통합을 위하여 지속적 통합툴의 활용과 테스트에 대한 중요성이 높아질 것으로 예상한다.
5. Grails와 ORM
안영회씨와 정상혁씨도 언급했지만 자바 개발자라면 앞으로 Grails의 성장에 관심을 가져야 될 것으로 생각한다. 국내에서는 Hibernate 프레임워크에 대한 관심도 가 낮은데 Grails의 성장으로 인해 자연스럽게 Hibernate ORM 프레임워크에 대한 관심도도 높아질 것으로 생각한다. ORM 프레임워크에 대한 학습 비용이 높지만 Grails를 좀 더 잘 사용하고자하는 개발자들을 중심으로 Hibernate가 보급될 것으로 생각한다. 또한 Groovy에 대한 관심도가 높아지면서 Script Language와 DSL에 대한 관심도도 높아질 것으로 예상해본다.
6. 자바진영에서 Spring 프레임워크의 영향력 확대
Spring 프레임워크는 급격하게 성장하고 있으며, 자바 진영에서 그 영향력이 점점 더 커지고 있다. 하지만 Spring 프레임워크에 대한 회의감을 표시하는 개발자들도 등장하고 있다. Spring 프레임워크의 도입에 대한 찬반 논쟁이 지속적으로 발생할 것으로 생각한다. 또한 Spring 프레임워크에서 Spring Security와 Spring Batch와 같은 서브 프로젝트의 국내 적용 사례가 점점 더 증가할 것으로 판단된다. 점점 더 비대해져가는 Spring 프레임워크를 이해하기 위하여 Spring 프레임워크가 가지는 핵심 사상과 방향을 전파하려는 노력도 지속될 것으로 생각한다.
올해의 스프링 기술 top5
1. Annotation 기반 설정(since Spring 2.5)
지난 Spring One Americas에서는 "설정은 XML 에서"라는 오랜 고정관념을 깨려는 듯 애노테이션 사용을 적극 장려하는 분위기였다. 설정 기반으로 애노테이션 기반으로 옮기는데 있어 오랫동안 J2EE/JEE 에서는 설정을 XML(EJB의 DD, Struts 등등)에서 했다는 점이 관성으로 작용한다. 그리고, 아직은 IDE 지원 역시 XML이 더욱 우위에 있다. 반면, Spring 2.5 부터 등장한 애노테이션 기반 설정은 light-weight 설정(?)을 가능하게 한다. Spring 3.0 에서 Java Config가 Core로 포함되면 애노테이션과 함께 시너지를 내는 모습을 볼 수 있지 않을까. 설정은 XML 영역이었던 시절이 현재형에서 과거형으로 바뀌어 가는 시점이다.1
2. Spring dm Server
엔터프라이즈 시장에서 아직 OSGi 애플리케이션은 실험적인 단계로 볼 수 있다. Spring dm Server은 실험적인 단게에서 실용적인 단계로 견인하는 큰 축을 차지하고 있다. Spring dm Server가 없었더라도 엔터프라이즈 영역에서 OSGi의 발전이 이렇게 빨랐을까? OSGi에서도 POJO 프로그래밍을 고수할 수 있었을까? dm Server의 순위를 놓고 고민했는데 당장은 가시적인 효과가 나타나지 않았더라도 영향력에 있어서는 첫 손가락으로 꼽하도 과하지 않다.
3. Spring Batch
Spring Batch는 새로운 프로젝트이긴 하지만, Accenture의 오랜 노하우가 Spring의 옷을 입고 등장한 것으로 봐야 할 듯 하다. 몇 주전 필자의 소속회사에서 성공리에 국내 대형 금융사에 성공적으로 배치 개선 프로젝트를 끝냈다. 실험적인 것이 아니라 이미 운영 중인 애플리케이션에 도입했다는 점에서 국내 환경, 그것도 대용량 금융환경 적용 가능성을 검증한 것이라 볼 수 있다. 작년 말에는 소문만 무성했는데 한 해 만에 릴리즈하고, 사이트 적용이 가능해졌다니 놀랍도록 빠른 발전이다. 마침 KSUG에서 Spring Batch에 대한 benelog님의 시리즈를 연재 하고 있다.
4. Spring Web Flow 2.0
SWF가 2.0을 출시하면서 Spring Web Flow는 선택적 웹 기술에서 MVC의 중심으로 위상이 바뀐 듯 하다. Spring Faces나 Spring JavaScript 등의 웹 기반 프로젝트 모두는 SWF를 중심으로 MVC의 확장 기술로 위치하고 있다. AJAX 나 RIA 기술 등장으로 비롯한 다양한 웹 화면 요구가 SWF를 축으로 Spring에서도 구현되고 있다. SWF를 유망 기술로 꼽아야 하나 고민이지만, 2.0 릴리즈 이후 기반을 닦았다는 점에서 올해의 기술로 꼽았다.
5. Spring Integration
올해 처음으로 선을 보인 Spring Integration이 빠른 속도로 발전하고 있다. 아직은 incubator 수준이긴 하지만 Spring Integration Adapters 까지 생겨나 Spring Integration의 발전 가능성을 높여주고 있다. 아직 기능에 있어서는 부족한 모습이지만, SpringSource의 다양한 기술을 고려하면 향후 어떤 시너지를 창출할지 기대가 된다.
내년 유망 스프링 기술 top5
1. Spring Twin Server (Spring tc Server + Spring dm Server)2
tc Server는 아직 데모밖에 보지 못한 상태지만, 파급효과에 대한 기대감에 있어서는 타의 추종을 불허한다. 과거 문제가 있어도 반쪽 EJB(?)를 그대로 쓰던 상황을 Spring이 타개했듯, 반쪽 WAS(?)를 쓰는 상황을 tc Server가 개선해주기를 바란다. 그리고, 올해의 top5에 선정했지만 dm Server 역시 내년에는 더욱 발전하여 tc Server/dm Server가 Application Server 시장에도 일대 혁신을 가져오길 기대한다.
2. Grails
Spring One Americas 2008에 참석하기 전까지는 SpringSource의 G2One 인수가 얼마나 큰 의미를 지니는지 알지 못했다. Grails는 Spring/Hibernate의 기반을 똑같이 활용하면서 완전히 새로운 API 스타일을 추가한 격이다. DSL 개발 등을 통해 개발 과정을 고도화 한다면, 엔터프라이즈 환경에서도 요구사항 반영 주기/시스템 개선 주기를 혁신적으로 개선할 수 있을 가능성을 보여주고 있다. 개인적으로 내년도 가장 흥미롭게 연구해볼 주제 가운데 하나다.
3. Spring 3.0
2.5, 3.0 대신에 3.0, 3.5 라고 하는 것이 어땠을까 할만큼 완전히 새로운 면모보다는 2.5의 일대 개선처럼 보인다. 이러한 면에서 Spring 커뮤니티의 지속적인 개선을 더욱 높이사게 된다. 물론 영원하지야 않겠지만, 내년 이맘때에도 지금처럼 Spring의 꾸준한 발전을 볼 수 있기를 기대한다.
4. Spring Security 2.5
benelog님 말처럼 가장 복잡한 영역 가운데 하나인 보안 프로그래밍 쪽에 단비와 같은 프레임워크가 Spring Security 이다. 엔터프라이즈 프로그래밍에 국한해서 보면, 확장성에 있어서는 거의 독보적인 솔루션이 아닌가 싶다. Acegi 1.5 에서 Spring Security 2.0 개선할 때는 Spring 2.0의 스키마 기반 설정을 대폭 보강하더니 2.5에서는 Spring 3.0과 궤를 같이 하여 애노테이션 및 EL 활용에 대한 밀착 연계를 제공한다. 출시 시점도 Spring 3.0에 맞춰져 있다.
5. Consolidation 솔루션
다소 무리한 분류라 할 수 있지만, terracotta 류의 클러스터 솔루션과 플랫폼 가상화 솔루션 등을 묶어서 consolidation3 솔루션으로 분류했다. 기존에 Spring의 영역은 "개발"에 국한했었지만, 캐시에서 클러스터 솔루션으로 변모한 Spring 유관제품이 등장하더니 VMWare와 손잡고 새 제품 출시를 준비중이다. Spring Portfolio의Consolidation 시장 진출은 "Ubiquitous Spring" 현상을 강화하는 전기가 될 것이다.
- 참고로 Spring 2.5 릴리즈는 작년 말이다. [본문으로]
- 둘은 다른 시장을 대상으로 하면서도 같은 목적과 기반을 갖고 있어 Twin 서버라고 지칭했지만, 필자 임의로 붙이 이름이다. :) [본문으로]
- IFRS 회계 국경이 사라진다 에서 '연결과 통합'이라고 번역했다. [본문으로]
하이버네이트의 활용
HibernateCursorItemReader 를 사용하면 하이버네이트를 사용하면서도, 지난 연재에서 소개했던 JdbcCurItemReader와 유사한 커서방식의 조회가 가능하다. 설정의 예는 리스트 8에 나와있다.
<bean id="hibernateItemReader" class="org.springframework.batch.item.database.HibernateCursorItemReader">
<property name="useStatelessSession" value="true" />
<property name="queryString" value="from player" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
리스트 8 : 하이버네이트를 이용한 조회
sessionFactory 속성은 스프링에서 기본적으로 제공하는 LocalSessionFactoryBean 를 이용해서 설정한다. useStatelessSession 속성에 따라서 상태를 가지거나 안 가지는 Session이 생성된다. useStatelessSession과 queryString이 하이버네이트의 코드와 어떻게 연결되는지는 리스트9에 있는 Query를 생성하는 부분을 보면 알 수 있다. StatelessSession이라면 하이버네이트에서 제공하는 Cache 와 Dirty체크 기능을 쓰지 않게 된다.
private Query createQuery() {
if (useStatelessSession) {
statelessSession = sessionFactory.openStatelessSession();
return statelessSession.createQuery(queryString);
} else {
statefulSession = sessionFactory.openSession();
return statefulSession.createQuery(queryString);
}
}
리스트 9 : 하이버테이트와 연결되는 부분
하이버네이트를 이용해서 DB에 자료를 저장하고 싶을 때도 HibernateAwareItemWriter라는 클래스도 도움을 줄 수 있다. 리스트9에 나와있듯이, 이 클래스에도 마찬가지로 sessionFactory를 속성으로 지정하고, 다른 하이버네이트를 쓰는 ItemWriter를 delegate에 연결시킨다.
<bean id="hibernateItemWriter"
class="org.springframework.batch.item.database.HibernateAwareItemWriter">
<property name="sessionFactory" ref="sessionFactory" />
<property name="delegate" ref="playerWriter" />
</bean>
리스트 10 : 하이버네이트를 인식하는 ItemWriter
HibernateAwareItemWriter는 트랜잭션의 한 묶음이 한꺼번에 commit 되었을 때 실패가 일어난다면, 어떤 item이 실패인지 알아보기 위해 실패한 묶음을 다시 한건 한건 쓰기 시도를 해준다. Jdbc의 batchUpdate를 이용하는 ItemWriter인 BatchSqlUpdateItemWriter의 경우에도 마찬가지의 흐름을 가진다.
여러 파일 읽기
앞 선 연재에서 소개한 FlatFileItemReader 같은 파일읽기 클래스들은 읽을 파일을 resource속성을 통해서 지정한다. 실무에서는 읽어서 처리할 파일이 하나가 아니고 특정 디렉토리에 있는 파일 전체나 아니면 특정 확장자, 이름패턴을 가지고 있는 경우가 대부분일 것이다. MultiResourceItemReader클래스는 와일드 카드등을 이용해서 여러 파일들을 한꺼번에 지정할 수 있다. 리스트 11과 같이 resources 속성으로 파일들을 읽을 패턴을 지정하고, delegate속성에 그 resource를 읽을 itemReader를 연결시켜 준다.
<bean id="multiResourceReader" class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="classpath:data/multiResourceJob/input/file-*.txt" />
<property name="delegate" ref="flatFileItemReader" />
</bean>
리스트 11 : 여러파일 읽기
2008 올해의 스프링기술 Top 5
5. Spring Security 권한,인증,보안 등의 모듈의 설계는 어느 시스템이나 필수적이고, 많은 고려 사항이 들어가야 하는 부분입니다. 그래서, 경험이 있는 개발자라면 이 것이 원래 복잡한 영역이라는 것에 동의할 것입니다. 이 권한 등의 관련 모듈은 유연하면서 충분한 기능을 담도록 만들기가 쉽지 않고, 매번 새로운 시스템마다 반복적으로 설계, 개발되는 경우도 많습니다. 그렇기에 이 영역에서 Spring Security의 존재가 가치있게 느껴집니다. Spring이 간단한 개발을 괜히 복잡하게 만드는 것이 아니고, 필수적으로 복잡한 요구사항이 충족되어야 하는 영역을 이미 만들어진 모듈을 쓰면서도 더 유연한 구조를 제공을 한다는 것을 Spring Security를 통해서 보여주고, 공감을 이끌어낼 수 있다고 생각합니다. 그런 의미와 2008년 들어 더욱 빨라지고 있는 것으로 보이는 발전속도, 적용 사례의 증가가 돋보이기에 2008년의 Spring 기술중 하나로 선정했습니다.
4. SpringSourceTool Suite
SpringSourceTool Suite는 스프링이 이제 개발자의 더 가까운 곳에서 더 자세히 개발을 도와주는 역할을 주겠다는 의미로 다가옵니다. 그리고 스프링의 상용제품 출시를 본격적으로 알리는 신호탄으로, 앞으로의 스프링소스의 사업모델의 측면에서 보아도 이 제품이 중요한 분기점의 의미가 되지 않나하는 생각이 듭니다.
3. Spring batch
KSUG 블로그에 오시는 분들은 잘 아시다시피, 제가 올해 가장 큰 관심을 가졌던 배치 프레임웍입니다. Accenture의 현장경험이 잘 녹아들어가고 Spring기반의 기술이 이를 뒷받침해서 본격적으로 Java 배치프레임웍의 논의를 불러일으킨 기술이라고 생각합니다. 배치처리에 어떤 기능들이 필요할 지를 보다 고민하는 계기가 되고, 경험있는 개발자의 처리 방식을 참고하고 그대로 사용할 수도 있다는 것에서 스프링배치는 큰 의미가 있습니다. 아직 젊은 프로젝트라서 1.X의 버전에서는 분산처리 같은, 성숙한 유사 모듈에 있는 기능들이 빠진 것들도 보이지만, 내년 2.0 발표 등으로 보다 강력한 프레임웍으로 발전해 나갈 것으로 예상됩니다.
2. Spring 2.5
Spring 2.5가 발표된 것은 작년이였지만, 본격적으로 주변에서 적용되는 것을 본 것은 올해라고 느껴집니다. 개발자들이 하위 호환성에 대한 걱정을 크게 하지 않으면서 기존 모듈의 의존성을 Spring 2.0에서 2.5로 업그레이드하는 사례를 여러번 봤는데, 이런 현상은 Spring에 대한 신뢰성이 깊어졌으면서도 그 발전 속도에 개발자들이 적응을 하게 된 것 같다는 의미부여를 하고 싶습니다.
1. Spring DM Server
이 기술의 발표 소식을 듣는 순간 과연 스프링의 영역이 어디까지 뻗어나갈까 하는 감탄이 들었습니다. 그리고 앞으로 퍼져나갈 OSGi 세상에서도 Spring이 단순 프레임웍 이상의 큰 역할을 할 것이라는 확신을 심어준 사건이였죠.
2009 유망 스프링 기술 Top
5. Spring Web Flow
javascript, JSF 지원기능 등 더욱 확장된 영역으로, 이제는 앞으로는 기본 웹개발은 SpringMVC, 보다 많은 기능이 필요한 웹개발은 Spring Web Flow이 맡게 되지 않을까하는 예감이 듭니다.
4. Spring Integration
현재 1.0 마일스톤 버전이 발표되어 있습니다. 1.0 최종버전이 나오면 본격적으로 Spring Integration을 적용해보고 싶어하는 개발자가 많을 것 같습니다. 데이터 연계(Integration) 모듈도 어느 시스템에나 있는 것이고, Spring Integration은 다양한 종류의 자원들을 동일한 방식으로 연결시켜주는 기능을 제공하므로, 많은 기대를 가지게 합니다.
3. Spring DM
아마도 내년이 Spring DM의 본격적인 대중화 시기가 되었으면 합니다. 대부분의 개발자들에게 낯선 OSGi 세계에서도 스프링이 든든한 길잡이가 되어줄 것으로 믿습니다.
2. Grails
이미 JVM위에서 돌고 있는 스크립트 언어가 200여가지라고 하죠. (http://www.is-research.de/
1 .Spring 3.0
드라이빙 쿼리 방식의 조회
앞선 연재에서 JdbcCursorItemReader를 이용한 Cursor방식의 조회에 대해서 소개했었다. 전체 데이터를 한꺼번에 메모리에 올리지 않기 때문에 대용량을 처리할 수 있는 방식이지만, DB2와 같이 비관적인(pessimistic) 락을 주로 사용하는 DB에서는 주의해서 사용해야 한다. Pessimistic lock은 레코드가 변경되지 않을 때도 락이 걸릴 수 있는데, Cursor를 사용하는 처리방식이 시간이 오래 걸리는 경우라면 오랫동안 lock을 유지할 수 있는 위험성이 생긴다. 이 것이 염려되는 상황이라면 한꺼번에 키 값만을 먼저 읽어서 메모리에 올린 후 한번에 조회하는 드라이빙 쿼리방식을 검토해 보아야 한다. 한 편으로는 이 방식은 전체 건수+1 만큼의 쿼리를 DB에 요청하게 되어 있으므로, Cursor 방식에 비해 전체 처리시간은 확연히 길어지게 된다. 사용하는 DB와 어플리케이션의 특성을 감안하고, 필요하다면 DBA나 같은 DB를 사용하는 다른 개발자와 충분한 상의를 해보도록 하자. 그리고, 전체 조회건수가 엄청나게 많아서 키값만을 메모리에 한번에 올리는 것조차 불가능한 상황이 올 수 있는지도 검토해보아야 할 것이다.
스프링배치에서는 DrivingQueryItemReader 클래스로 드라이빙 쿼리방식을 사용할 할 수 있다. 이 클래스의 keyCollector라는 속성이 이름 그대로 아이템의 키값을 먼저 읽어오는 역할을 한다. 이 속성은 리스트1과 같은 인터페이스로 선언되어 있다.
public interface KeyCollector {
List retrieveKeys(ExecutionContext executionContext);
void updateContext(Object key, ExecutionContext executionContext);
}
리스트 1 : KeyCollector 인터페이스
KeyCollector 의 retrieveKeys메소드에서는 키값을 읽어와서 List형태로 반환하는 역할을 하고, updateContext 메소드에서는 작업이 중간에 실패하면 재시도를 위한 정보를 저장하는 작업을 수행하게 된다. 이런 처리를 위해 구현 클래스인 SingleColumnJdbcKeyCollector클래스에서는 재시도 시 실행시키는 쿼리를 담기 위한 restartSql 속성도 가지고 있다.
DB에 player 라는 테이블이 있고, Id라는 컬럼이 이 테이블의 주키값이라면 리스트2와 같이 드라이빙 쿼리를 이용하는 설정을 할 수 있다.
<bean id="playerReader"
class="imaso.springbatch.PlayerReader">
<property name="keyCollector">
<bean class="org.springframework.batch.item.database.support.SingleColumnJdbcKeyCollector">
<property name="sql">
<value> SELECT id FROM player ORDER BY id
</value>
</property>
<property name="restartSql">
<value> SELECT id FROM player WHERE id > ? ORDER BY id
</value>
</property><property name="keyMapper" ref="playerMapper">
<property name="jdbcTemplate" ref="jdbcTemplate">
</bean>
</property>
</bean>
리스트 2 : 드라이빙 쿼리 방식의 조회 설정
첫째 줄의 playerReader빈의 클래스로 지정된 PlayerReader 클래스는 리스트3과 같이 DrivingQueryItemReader를 상속한 클래스의 read메소드 안에서 상세조회 기능을 만들어 준다.
public class PlayerReader extends DrivingQueryItemReader {
public Object read(){
Object item = super.read();
//읽어온 item의 상세조회를 하기 위한 작업을 수행
}
리스트 3 : 드라이빙 쿼리를 바탕으로 상세조회를 하는 클래스
jdbcTemplate속성은 DataSource가 그 내부에 연결되어 있는 JdbcTemplate 클래스 설정 bean을 지정한다. keyMapper는 DB에서 조회된 키값인id를 Player테이블을 위한 도메인 오브젝트로 매핑해주기 위한 클래스로 스프링의 RowMapper 인터페이스를 구현하면 된다.
IBatis의 활용
IbatisDrivingQueryItemReader클래스는 item의 상세 조회를 iBatis를 통해서 할 수 있게 한다. sql쿼리를 직접 지정하지 않고 iBatis의 sql설정파일에 있는 쿼리의 id를 넘기는 것이다. 앞에선 본 playerReader 예제를 iBatis를 활용한 클래스로 재구성하면 리스트4와 같다.
<bean id="playerReader" class="imaso.springbatch.PlayerReader">
<property name="keyCollector" ref=”playerKeyCollector”>
<property name="detailsQueryId" value=" selectPlayerDetail "/>
<property name="sqlMapClient" value=" sqlMapClient "/>
</bean>
리스트 4 : IBatis를 이용한 상세조회
detailsQueryId에 들어가는 속성이 sqlMap 설정 파일에 들어가 있는 쿼리의 id이고, sqlMapClient 는 iBatis에서 쓰는 DB접근을 위한 클래스이다. 도메인 오브젝트와의 매핑 등 쿼리실행에 대한 것들은 iBatis의 설정파일 내에서 지정된다.
웹Application에서 스프링과 iBatis를 연결시킬 때와 마찬가지로, sqlMapClient와 Datasource의 설정은 리스트5와 같이 SqlMapClientFactoryBean을 활용하면 된다.
<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:datasource/sql-map-sample.xml" />
<property name="dataSource" ref="sampleDataSource"/>
</bean><bean id="sampleDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${batch.jdbc.driver}" />
<property name="username" value="${batch.jdbc.user}" />
<property name="password" value="${batch.jdbc.password}" />
</bean>
리스트 5 : iBatis 관련 스프링 설정
${batch.jdbc.driver}처럼 변수로 지정된 부분은 스프링의 PropertyPlaceholderConfigurer클래스를 이용해서 다른 프로퍼티 파일에 지정된 것을 가지고 오거나 Maven의 resource filtering 기능을 이용해서 빌드 시에 개발,운영 등 각 환경에 맞는 값이 엮여지도록 할 수 있다.
KeyCollector를 사용할 때도, IBatisKeyCollector를 통해서 iBatis의 설정파일 속에 있는 쿼리를 가지고 올 수 있다. drivingQueryId라는 속성에 키값만을 조회할 쿼리의 id를 지정해주면 된다.
<bean id=" playerKeyCollector " class="org.springframework.batch.item.database.support.IbatisKeyCollector">
<property name="drivingQueryId" value="selectAllPlayerId" />
<property name="sqlMapClient" ref=" sqlMapClient " />
</bean>
리스트 6 : iBatis를 이용한 드라이빙 쿼리
마찬가지로 DB에 데이터를 입력할 때에도 유사하게 iBatis를 쓸 수 있다. 필자가 실무에서 활용하기 위해 만들어본 SimpleIbatisItemWriter 라는 클래스가 리스트7에 나와있다.
public class SimpleIbatisItemWriter extends AbstractItemWriter{
private String writingQueryId;
private SqlMapClientTemplate sqlMapClientTemplate;
public void write(Object item) throws Exception {sqlMapClientTemplate.insert(writingQueryId,item);
}
public void setSqlMapClient(SqlMapClient sqlMapClient) {
sqlMapClientTemplate = new SqlMapClientTemplate(sqlMapClient);
}
//writingQueryId setter 메소드 생략
}
리스트 7 : iBatis를 활용한 DB입력
IBatisKeyCollector와 유사하지만writingQueryId에 insert문이 들어가 있는 쿼리의 id를 지정해 준다는 것만 차이가 있다.
다른 스프링 프로젝트들
지금 시점에서 스프링이 커버하는 넓은 영역들은 '프레임웍'이라는 말만으로는 다 표현하기가 벅차다고 느껴진다. SpringWebFlow, SpringSecurity, SpringDM 등이 포함된 스프링 포트폴리오들의 진용을 보면, 안 건드리는 영역이 어떤 것이 있을까하는 생각이 든다. 이 중 스프링배치와 연관성을 맺고 있는 프로젝트는 스프링 인테그레이션(Spring-Integration), 스프링웹서비스(SpringWebService), 스프링 어플리케이션 플랫폼(Spring Application Flatform)이 있다.
스프링 인테그레이션은 기업환경의
데이터 연계 작업을 돕는 프레임웍이다. 복잡한 연계방식을 추상화 시켜서 비동기 메시지 기반으로 구성할 수 있게 한다. JMS,
RMI, File, Http, Ftp, Web service 등의 연결방식이 같은 인터페이스로 추상화되어 제공되는 것이다.
배치처리도 데이터 연계를 위한 목적으로 많이 쓰이기 때문에 당연히 스프링배치와 스프링 인터그레이션의 영역은 겹치는 곳이 있다.
아직 정식판에 포함되지는 않았지만 스프링배치와 스프링 인테그레이션을 결합한 프로토타입 코드도 나와있다. 1앞으로 버전업이 계속 될수록 두 프레임웍은 더욱 긴밀한 사이를 유지할 것으로 보인다. 스프링배치
2.0에서는 스프링 인테그레이션을 이용해서 원격지에서 Item을 쓸 수 있는 Remote Chunking이라는 기술을 지원할
예정이다.2
스프링 웹서비스는 SOAP을 사용하는 Web servcie
를 쉽게 만들 수 있는 프레임웍이다. 다른 웹서비스 구현 프레임웍에 비해서도 짧은 코드로 XML 웹서비스를 쓰는 어플리케이션을
구성할 수 있고, Spring MVC를 아는 사람에게는 더욱 친숙하게 느껴질 수 있는 구조로 되어 있다. 스프링배치에서
XML파일은 스프링웹서비스에 포함되어 있는 Spring OXM(Object-XML-Mapping) 을 이용해 처리하는 것이
가능해서 보다 많은 유연하고 많은 라이브러리들을 사용할 수 있다. 스프링 3.0에서는 Spring OXM이 보다 상위모듈에
포함될 수도 있다고 한다. 그리고 스프링 인테그레이션에서도 Web service 연결부분은 스프링 웹서비스에 위임하고 있다.
OSGi 기반의 환경을 제공하는 스프링 어플리케이션 플랫폼에서도 스프링배치가 OSGi 번들로 실행될 수 있다.3 향후 버전에서도 더욱 배치에 특화된 기능들이 추가될 것이라고 한다.
한편, 스프링배치의 Retry, Repeat 개념들도 보다 일반화 되어 스프링3.0에서는 코어 모듈로 들어갈 수 있다는 소문도 들린다. 이렇듯 스프링 포트폴리오들은 서로 영향을 주고 받으며 빠른 속도로 발전하고 있고, 이는 개발자들에게는 보다 풍족함을 느끼게 해 줄 것으로 기대된다. 앞으로 2~3년 후에 스프링과 그로 인한 자바 세계가 어떻게 변해있을지 생각하는 일은 버거운 일이지만 즐거운 상상이다.
ETL 툴
스프링배치조차도 ETL(Extract, Transform, Load)툴로 분류되기도 하지만, Java 세계에도 이전부터 오픈소스 ETL툴이 있어 왔다.
Kettle(http://kettle.pentaho.org/)은 이중 가장 오랜역사와 많은 사용자를 가지고 있는 툴로, Pentaho 라는 회사에서 Pentaho Data Integration이라는 이름으로도 배포된다. 별도의 GUI툴을 제공하고, Job의 설정 내용을 XML이나 DB로 저장할 수 있다. 특히 DB를 Job의 내용을 담는 저장소로 활용하면, 별도의 프로그램 배포작업이 필요없고, 여러명사이에 공동작업에 유리한 점이 있다.
Talend Open
Studio(http://www.talend.com/ )은 Jasper사에서도 Jasper ETL이라는 이름으로 제공되고 있다.
Eclipse plug-in 기반의 툴이 제공되고, 최종 산출물로는 Java나 Perl의 코드가 생성된다.
두 제품이 Java 오픈소스 ETL툴의 양대산맥이라고 할 수 있고, Open소스 기반이지만 일부 유료기능이나 교육, 컨설팅
등으로 수익을 얻는 사업모델을 가지고 있다. 그 툴다 리포팅툴 등을 포함한 Business Intelligence 패키지의
일부로 제공된다는 것도 공통점이다. Kettle이 무료 기능의 폭이 더 넓은 편이고 Talend Open Studio는 국내에도
제휴된 기술 지원 업체가 있다는 장점이 있다. 스프링배치에서 다루고 있는 파일 형식들(XML,Flat file)과
프로토콜(FTP, HTTP)가 대부분 지원되며, 여러대의 컴퓨터에서 하나의 작업을 병렬실행하는 것도 가능하다.
스프링배치와 이런 툴들은 어떤 장단점이 있을까? Java와 스프링개발에 익숙한 개발자가 많고, 정교한 흐름처리, 확장성이 필요하다면 스프링배치가 유리할 것이고, 시스템에서 필요한 요구사항들이 ETL툴이 제공하고 있는 기능들에 잘 맞아떨어지는 경우에 GUI로 된 편한 작업환경을 누리고 싶다면 Kettle이나 Talend Open Studio 같은 툴이 나을 것이다.
- 정상혁 (http://benelog.egloos.com)
이벤트 처리
스프링배치에서는 Step 실행, 읽기, 쓰기, 트랜잭션의 시작과 끝이나 에러, 건너뛰기 등 다양한 시점에 실행될 수 있는 EventListner들이 제공되고 있다. 이를 이용하면 핵심처리 로직과 분리되어 유연한 흐름제어를 할 수 있게 된다.
Step내에서는 StepListener라는 인터페이스가 있고, 이를 상속한 StepExecutionListener 등의 인터페이스가 존재한다. StepListener자체는 아무런 메소드도 없이 marker interface 의 역할만 한다고 볼 수 있다. 리스트 5부터 9까지가 이를 인터페이스들의 모양을 보여준다. 메소드명만으로도 어느 시점이 각 메소드들이 호출되는지 알 수 있을 것이다.
public interface StepExecutionListener extends StepListener {
void beforeStep(StepExecution stepExecution);
ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e);
ExitStatus afterStep(StepExecution stepExecution);
}
리스트 7 : StepExcutionListener인터페이스
public interface ChunkListener extends StepListener {
void beforeChunk();
void afterChunk();
}
리스트 8 : ChunkListner 인터페이스
public interface ItemReadListener extends StepListener {
void beforeRead();
void afterRead(Object item);
void onReadError(Exception ex);
}
리스트 9 : ItemReadListener 인터페이스
public interface ItemWriteListener extends StepListener {
void beforeWrite(Object item);
void afterWrite(Object item);
void onWriteError(Exception ex, Object item);
}
리스트 10 : ItemWriteListener 인터페이스
public interface SkipListener extends StepListener {
void onSkipInRead(Throwable t);
void onSkipInWrite(Object item, Throwable t);
}
리스트 11 : SkipListener 인터페이스
Listener의 등록은 Step선언 시에 SimpleStepFactoryBean의 listeners속성을 통해서 할 수 있다. 속성명만 보아도 알 수 있듯이 Listeners는 StepListener 클래스의 배열로 이루어져 있으므로, 역시나 여러 개를 설정할 수 있다. 그리고 ItemReader나 ItemWriter로 설정되는 클래스가 동시에 StepExecutionListener를 구현하고 있다면 SimpleStepFactoryBean에서 알아서 Listener로 등록해주므로 따로 listeners에 명시해 줄 필요는 없다.
StepExecutionListener에서는 Step실행의 선후 조건을 조사해서 Job을 실행을 멈추는 것도 가능하다. stepExecution을 참조할 수 있는 범위 내에서 아래 같이 terminatedOnly속성을 true로 해주면 Job을 멈출 수 있다.
public class CustomItemWriter extends ItemListenerSupport implements StepListener {
private StepExecution stepExecution;
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
public void afterRead(Object item) {
if (isPoisonPill(item)) {
stepExecution.setTerminateOnly(true);
}
}
}
리스트 12 : Job 멈추기 (스프링배치 레퍼런스 문서 중에서)
이와 같은 선언이 되었을 때 기본적인 정의된 동작은 JobInterruptedException을 발행시키는 것이다. 이를 바꾸고 싶다면 StepInterruptionPolicy 인터페이스를 구현한 클래스를 만들고 ItemOrientedStep에 setInterruptionPolicy 메소드로 그것을 넣어주면 된다.
이벤트의 활용예로 배치처리에서는 흔하게 파일에서 읽어온 내용을 DB에 입력할 때,DB의 primary key나 foreign key constraint 때문에 Exception이 떨어지는 것을 처리해야 할 경우가 있다. 그런 Exception은 전체 배치처리를 중단시키지 않고 해당 건만 따로 정해진 디렉토리에 파일만 쓰는 것으로 처리정책을 정했다고 가정해 보자. 일반적인 프로그램에서는 try 절을 이용해서 Exception을 잡고 catch 절 내에서 에러가 난 건을 파일로 써주는 모듈을 호출해야 할 것이다. 스프링배치를 사용한다면 리스트 2처럼 SkipLimitStepFactoryBean 을 이용한 Step선언을 사용하고 그 속성인 skippableExceptionClasses으로 DataIntegrityViolationException같이 그 상황에서 예상되는 Exception을 지정해 주면 된다. 그리고 리스트 9의 SkipListener를 구현해서 onSkipInWrite메소드 안에서 Skip한 건을 파일로 쓰는 모듈을 호출해주면 되는 것이다.
원하는 Listener를 구현할 때 그 중 일부 메소드만 구현하고 싶을 때는 StepListnerSupport, ItemListenerSupport와 같은 텅빈 구현체를 상속하면 약간의 편의를 얻을 수 있다.
유효성 검사
스프링배치에서는 ItemReader의 일종인 ValidatingItemReader를 통해 validation 기능을 제공한다. 리스트 13과 같이 선언을 할 수 있다.
<bean class="org.springframework.batch.item.validator.ValidatingItemReader">
<property name="itemReader">
<bean class="org.springframework.batch.sample.item.reader.OrderItemReader" />
</property>
<property name="validator" ref="validator" />
</bean>
리스트 13 : ValidatingItemReader설정
다른 ItemReader가 내부의 멤버객체로 구성되어 가지고 있으면서, 그 내부 ItemReader에서 읽어온 item을 validator객체로 검사하는 구조이다. 리스트 14에 나와 있는 내부의 read메서드를 보면 쉽게 그 과정이 눈에 들어올 것이다.
public Object read() throws Exception {
Object input = super.read();
if(input != null){
validator.validate(input);
}
return input;
}
리스트 14 : ValidatingItemReader의 read메서드
ValidatingItemReader가 가진 validator 속성의 인터페이스는 org.springframework.batch.item.validator.Validator이고, 다음과 같다.
public interface Validator {
void validate(Object value) throws ValidationException;
}
리스트 15 : 스프링배치의 Validator 인터페이스
굳이 패키지명을 길게 소개한 이유는, 스프링을 잘 아시는 분들이 오히려 이 인터페이스 이름만으로는 더 헷갈릴 수 있기 때문인데, 스프링에는 org.springframework.validation.Validator 라는 인터페이스도 있다.
public interface Validator {
public boolean supports(Class class1);
public void validate(Object obj, Errors errors);
}
리스트 16 : 스프링 validation 패키지의 Validator 클래스
더욱 혼동이 올 수 있는 것은 스프링배치의 Validator 인터페이스의 구현체인 SpringValidator라는 클래스가 있고, 그 클래스에는 또 validator라는 속성이 있는데, 이 validator는 스프링 validation패키지안의 Validator라는 것이다. 즉, 스프링배치의 Validation기능은 원래 스프링 validation패키지 에서 제공하는 Validator에게 그 처리를 위임할 수 있다는 것이다. 스프링 validation패키지의 ValangValidator클래스를 이용하여 유효성 검사기준을 설정한 예는 리스트 19와 같다.
<bean id="validator"
class="org.springframework.batch.item.validator.SpringValidator">
<property name="validator">
<bean
class="org.springmodules.validation.valang.ValangValidator">
<property name="valang">
<value> <![CDATA[
{ playerId : length(?) < 13 : 'playerId too long' : 'playerId' : 12}
]]></value>
</property>
</bean>
</property>
</bean>
리스트 17 : ValangValidator이용한 설정
Validator는 간단한 인터페이스이므로 사용자가 직접 쉽게 구현할 수도 있다.
아이템 변환하기
입력항목과 쓰기 항목의 속성들이 많이 다르고, 그 사이에 다소 복잡한 매핑규칙이 있다면 중간에 클래스를 따로 만들어서 Java로 코딩을 해주는 것이 더 편한 경우도 있을 것이다. 이렇게 ItemReader에서 읽은 item을 다른 객체로 매핑을 해야하는 경우 ItemWriter 중의 하나인 ItemTransformerItemWriter를 사용할 수 있다.
이 클래스도 다른 ItemWriter를 delegate라는 속성으로 지니고 있고, 변환작업을 하는 구성요소를 itemTransformer라는 속성으로 가지고 있다. 그 인터페이스는 리스트18과 같이 간단하다.
public interface ItemTransformer {
Object transform(Object item) throws Exception;
}
리스트 18 : ItemTransformer 인터페이스
ItemTransformerItemWriter에서의 write메서드의 호출과정은 먼저 itemTransformer를 통해 item객체를 변환시키고 delegate속성으로 지정된 다른 itemWriter 로 그 값을 쓰게 하는 것이다.
유사하게 CompositeItemTransformer 클래스도 있는데, 이 클래스에서는 setItemTransformers라는 메소드를 호출해서 itemTransformer들을 List로 여러 개 지정해서 연쇄적으로 호출할 수 있는 구조를 지원한다. 리스트 19에서 확인할 수 있듯이 itemTransformers에 들어간 순서대로 변환을 수행하는 것이다.
public Object transform(Object item) throws Exception {
Object result = item;
for (Iterator iterator = itemTransformers.listIterator(); iterator.hasNext();) {
result = ((ItemTransformer)iterator.next()).transform(result);
}
return result;
}
리스트 19 : CompositeItemTransformer 의 transform메소드
주의할점은 ItemWriter를 구현한 클래스가 동시에 ItemStream이나 StepExecutionListener를 구현했다면, 따로 streams속성이나 listeners속성에 등록해줄 필요가 없지만, ItemTransformerItemWriter안으로 한번 감싸져서 들어간다면 자동등록되지 않는다는 것이다. 예를 들어 Xml파일을 쓰는StaxEventItemWriter를 바로 ItemWriter로 쓸 때는 이 클래스가 InputStream을 구현한 클래스인데도 SimpleStepFactoryBean의 streams속성에 명시해줄 필요가 없지만, ItemTransformerItemWriter안의 delegate속성으로 들어갈 때는, 이를 Step설정에서도 streams에 따로 등록을 해줘야 한다.
기존 클래스 활용하기
개발하고 있는 시스템에서 기존에 작성된 클래스 중 ItemReader나 ItemWriter의 역할을 할 수 있는 것이 있다면, 추가로 래핑을 해주는 클래스를 작성을 할 필요 없이 설정만으로 활용이 가능하다. 즉, ItemReader 인터페이스를 구현하고 있는 클래스가 아니더라도, 메소드 호출시 하나씩 읽어온 Object를 반환하고 더 읽을 것이 없으면 null을 반환하는 ItemReader의 동작방식만 만족한다면 , 바로 쓸 수 있다는 것이다. ItemReader의 경우 ItemReaderAdapter를 이용해서 targetObject 속성에서 실행할 클래스의 bean선언을 참조하고, tagetMethod속성에서 ItemReader.read와 같은 역할을 하는 메소드를 적어준다.
<bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter">
<property name="targetObject" ref="fooService" />
<property name="targetMethod" value="generateFoo" />
</bean>
<bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
리스트 20 : ItemReaderAdapter기존 클래스 연결
기존 클래스를 ItemWriter로 활용할 때는 bean선언의 class부분에 org.springframework.batch.item.adapter.ItemWriterAdapter 을 지정하고 마찬가지의 방식으로 targetObject와 targetMethod를 지정하면 된다.
- 정상혁 ( http://benelog.egloos.com )
토비님
S1A 2008 둘째날 SpringSecurity 2.5
S1A 2008 셋째날 - Spring JavaConfig
백기선님
S1A 첫 날 - 캐나다 개발자 셋과 미국 내에서 혼자온 한 분
S1A - SpringSource dm Server Introduction by 뢉 해럽
S1A - Advanced SpringSource DM Server by 뢉 해럽
S1A - Building Large-Scale, Modular Software - 뢉 해럽
S1A - OSGi best practive by 코스틴 리우
로드 존슨과 친구들이 싸인한 스프링 책이 나에게 주는 의미
S1A - Spring Dynamic Modules Updates : Costin Leau
안영회님
Adrian Coyler 기조 연설 (SpringOneAmerica2008)
Spring One Americas 2008 리뷰 - 1. SpringSource tc Server
Spring One Americas 2008 리뷰 - 2. Rod Johnson의 기조연설(keynote)
Spring One Americas 2008 리뷰 - 3. 외국 블로거들의 하나
허광남님
장진달님
2008 SpringOne America 컨퍼런스 후기
Job의 재시작 관련 속성
Job의 인터페이스를 보면 isRestartable 이라는 메소드가 선언되어 있는 것을 볼 수 있다. 스프링배치가 제공하는 기본적인 구현클래스인 SimpleJob에서는 setRestartble (boolean)메소드를 통해서 isRestartable에서 반환할 값을 boolean값으로 선언해 줄 수 있다. 이 값은 같은 JobRepository안에 JobInstace가 존재할 시 한 번 더 실행하는 것을 허용할지 여부를 결정하는 속성이다. 명시적으로 설정이 없을 경우의 디폴트값은 false이다.
Job은 JobLaucher에 의해 실행되고, JobLauncher 안에서는 JobRepository를 통해서 JobExecution을 생성한다. 이 때 호출되는 JobRepository안의 createJobExecution메소드 내부에서는 이미 같은 JobInstance가 있을 때 현재 실행하고자 하는 Job이 restartable하지 않다면 JobRestartException을 발생하게 한다. 앞의 연재에 나왔던 각 구성요소들의 개념을 다시 떠올려보면 JobInstance는 논리적인 Job 실행이고 JobExecution은 물리적인 한번의 Job의 실행 시도임을 기억할 수 있을 것이다.
리스트1에서 SimpleJobRepository의 createJobExecution의 내부코드를 보면 이 과정을 직접 확인할 수 있다.
JobInstance jobInstance = jobInstanceDao.getJobInstance(job, jobParameters);
……….
if(jobInstance != null) {
if(!job.isRestartable())
throw new JobRestartException("JobInstance already exists and is not restartable");
리스트 1 : SimpleJobRepository의 createJobExecution메소드 내부중 일부
첫째 줄에서 보이듯이 같은 JobInstance가 존재하는지를 찾는 것은 JobInstanceDao 에 의해 이루어 진다. JobInstanceDao 의 구현체들인 MapJobInstanceDao, JdbcJobInstanceDao에서 둘 다 Job의 이름과 JobParameter의 값들로 JobInstance의 동일성을 식별하게 되어 있다. DB테이블에 Meta데이터를 관리하는 JdbcJobInstanceDao에서는 JOB_NAME컬럼과 JOB_KEY 컬럼값으로 JobInstance를 인식하게 되어 있고, JOB_KEY 컬럼에 들어가는 값은 JobParameter로 넘어온 값들의 조합으로 이루어 진다. 같은 JobParameter를 가지는 Job을 반복해서 실행하고 Job의 restable이 false라면 JobRestartException을 받을 수 있음을 유의해서 이를 의도한 곳에 사용할 수 있어야 할 것이다.
JobIntanace의 논리적인 개념에 일치하게 JobParameter를 선택하는 것도 중요하다. 매일 한번 실행되는 배치잡이고, 오늘날짜로 입력된 데이터를 처리하는 배치잡이라면 무난하게 JobParameter에 오늘날짜를 넣을 수 있다. Job실행의 핵심 파라미터는 JobParameter에 포함시켜야지 약간의 코딩편의를 위해 DB쿼리에서 바로 현재 날짜를 얻어오는 방식은 피해야 할 것이다.
Step의 재시작관련 속성
Step의 인터페이스에서는 startLimit와 allowStartIfComplete 속성의 getter 메소드가 있고, ItemOrientedStep와 TaskletStep같은 구현클래스들에서 setter메소드를 통해 getter에서 반환될 값을 설정한다.
startLimit의 디폴트값은 Integer.MAX_VALUE로, 아무 설정이 없으면 현실적으로 startLimit속성의 제한에 안 걸린다고 보면 된다. Step이 실행될 때 startLimit값이 지금 실행하고 있는 JobInstance내부에서 Step이 실행된 횟수보다 크지 않으면 StartLimitExceededException을 발생시킨다. 즉 startLimit가 2로 지정되어 있으면 3번째 실행에서 Exception을 내는 것이다.
allowStartIfComplete 속성은 그 Step을 포함하는 JobInstance가 재시도 중일 때, 앞 선 시도에서 성공한 Step이라도 다시 실행을 시킬 것인지를 지정하는 속성이다. 이 것이 true로 되어 있다면 Job을 재시도 시에도 이 Step을 건너뛰지 않는 것이다. 디폴트값은 false이므로 별다른 설정이 없다면 같은 JobInstance내에서 한번 성공한 StepExecution이 있다면 그 Step은 반복해서 실행되지 않는다.
건너뛰거나 재시도할 Exception 지정하기
배치처리 내에서 항상 모든 Exception이 전체 Job을 멈추는 것이 바람직하지 않은 경우도 많다. 예를 들어, 건건이 독립적인 데이터를 플랫파일을 읽어서 데이터베이스에 입력할 때, 파싱 중 에러가 난 플랫파일은 처리를 건너 뛰고 따로 그 기록을 남긴 후에 그 다음 파일을 파싱해서 한 건이라도 더 처리하는 것이 나은 업무상황도 있을 것이다.
SkipLimitStepFactoryBean을 이용한다면 복잡한 try catch 문 없이 설정만으로 Exception의 처리정책을 간단하게 지정할 수 있다. 가령 플랫파일 파싱 중에 생길수 있는 Exception인 FlatFileParseException은 10번까지는 그냥 넘어가도록 하려 리스트 2처럼 Step을 설정하면 된다.
<bean id="skipSample" class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
<property name="skipLimit" value="10" />
<property name="itemReader" ref="flatFileItemReader" />
<property name="itemWriter" ref="itemWriter" />
<property name="skippableExceptionClasses"
value="org.springframework.batch.item.file.FlatFileParseException">
</property>
</bean>
리스트 2 : 건너뛸 Exception지정 (출처 : 스프링배치 레퍼런스 매뉴얼)
skippableExceptionClasses속성은 java.lang.Class 클래스의 배열로 선언되어 있으므로 <list>와 <value> 태그를 이용해서 여러 개를 지정할 수도 있다. skipLimit속성으로 허용할 수 있는 skip의 한도를 정한다. 디폴트 값이 0이므로 skipLimit를 명시적으로 지정하지 않으면 skip관련 속성들의 지정이 의미가 없어진다.
건너뛰어야 될 Exception을 명시적으로 지정하지 않고, 반드시 현재 Step의 실행을 중단시켜야 할 Exception을 선언하는 방식도 가능하다. 리스트 3처럼 SkipLimitStepFactoryBean의 fatalExceptionClasses속성을 통해서 그것이 가능하다. SkippableExceptionClasses을 java.lang.Exception으로 설정해서 모든 Exception을 다 skip하지만 fatalExceptionClasses에 FileNotFoundException이 지정되어 있기 때문에 파일이 없는 경우에는 해당 Step이 실패하게 되는 것이다.
<property name="skippableExceptionClasses" value="java.lang.Exception">
<property name="fatalExceptionClasses" value="java.io.FileNotFoundException">
리스트 3 : 반드시 Step을 실패시킬 EXCEPTION을 명시적으로 지정
한편, 일시적인 이유로 잠시 발생했을 가능성이 큰 Exception에 대해서는 현재 Step 내에서 몇 차례 더 시도를 해보는 것이 효율적일 수 있다. SkipLimitStepFactoryBean의 retryableExceptionClasses속성과 retryLimit속성으로 재시도할 Exception들을 지정할 수있다. 리스트4는 DeadlockLoserDataAccessException에 대해서 3번까지 재시도를 설정하는 예이다.
<property name="retryableExceptionClasses" value="org.springframework.dao.DeadlockLoserDataAccessException" />
<property name="retryLimit" value="3" />
리스트 4 : 재시도할 Exception 지정
그리고, 스프링의 TransactionAttribute 지정 기능을 이용해서 RollBack을 하지 않을 Exception을 지정할 수도 있다. ItemOrientedStep에 바로 transactionAttribute속성이 들어갈 수 있고, SkipLimitStepFactoryBean이나 SimpleStepFactoryBean에서 이를 선언하는 것이 가능하다.
<property name="transactionAttribute" value="+org.springframework.batch.item.validator.ValidationException" />
리스트 5 : 트랜잭션 속성 지정
더하기표시(+)뒤에 있는 Exception은 발생시에도 RollBack을 시키지 않도록 지정된 것이다.
재시도 정책
재시도 관련 정책들은 RetryPolicy라는 인터페이스를 구현한 클래스로 지정하는데, 위에 나왔던SkipLimitStepFactoryBean에서는setRetryPolicy설정을 통해 이를 직접 설정하는 것도 가능하다. 가령 30초의 제한시간이 넘지 않았을 때만 재시도를 허용하는 정책을 설정한다고 하면 아래와 같이 가능하다.
<bean id=”sampleStep" class="org.springframework.batch.core.step.item.SkipLimitStepFactoryBean">
<property name="retryPolicy" ref=”timeoutRetryPolicy”/>
</bean>
<bean id=”timeoutRetryPolicy” class=” org.springframework.batch.retry.policy.TimeoutRetryPolicy”>
<property name="timeout" value=”30000L”/>
</bean>
리스트 6 : 시간 제한의 재시도 정책 설정
단순한 설정을 보여주기 위해서 TimeOutRetryPolicy만 설정을 보여주였지만, 실제적용에서는 여러 개의 retryPolicy가 복잡적으로 쓰이는 경우가 많을 것이다. org.springframework.batch.retry.policy패키지안의 RetryPolicy를 구현한 클래스들은 아래와 같이 정리할 수 있다.
- AlwaysRetryPolicy : 항상 재시도를 허용함
- NeverRetryPolicy : 첫시도만을 허용하고 재시도는 없음.
- ExceptionClassifierRetryPolicy : Exception에 따라서 다른 내부적으로 다른 RetryPolicy를 적용시켜 줌.
- TimeoutRetryPolicy : 한도시간 이내에서만 재시도를 허용
- SimpleRetryPolicy : 재시도가능한 Exception, 아닌 Exception을 지정해서 지정된 횟수 내에서만 재시도를 허용하는 정책
- CompositeRetryPolicy : 여러 개의 RetryPolicy들을 연결해서 호출하여 사용함.
- RecoveryCallbackRetryPolicy : 앞선 시도에서의 정보를 가지고 있는 재시도 정책 (Stateful함)
표 1 : 재시도 정책 클래스들
앞에서 나온 리스트3,4의 예제처럼 재시도하거나 꼭 멈추어할 Exception을 지정하는 설정을 하면 SkipLimitStepFactoryBean의 내부에서 ExceptionClassifierRetryPolicy안에 SimpleRetryPolicy 를 엮어서 넣어주는 알아서 작업을 해주게 되어있다.
- 정상혁( http://benelog.egloos.com )

Prev



Rss Feed