spring-batch

spring-batchspring의 sub project로서 ETL관련 수많은 API를 제공하고 있다.

장비에서 발생하는 데이터 파일(CSV포맷)을 파싱하여, DB 적재 프로그래밍을 수행하면서 spring-batch를 이용하였다.

이를 수행 하면서, 겪었던 일을 정리한다.

주요 개발 항목

spring-batch에서 제공하는 API 중 FlatFileItemReader를 이용해 CSV파일을 읽어 드렸고, 데이터 적재는 ItemWriter를 직접 구현하였다.

spring-batchJob 관리를 JobRepository를 통해서 하는데, 내 업무 환경에서는 단타성으로 많은 파일 처리를 해야 해서, MapJobRepositoryFactoryBean을 이용하였다. 주의 점은 clear() 메소드를 호출해야 한다는 점이다. 않그러면, OOME 가 발생한다.

중요한 부분은 ItemReader를 구현하는 것이었는데, 이 부분은 AggregateItemReader를 참고하여 개발 하였다.

CSV 파일 포맷 및 처리 전략

주요 정보는 제거하였다. 예제이므로 데이터 포맷 역시 간략화 하였다. 아래는 파일 데이터 이다.

HEADER_BEGIN
MACHINE_TYPE:HELLO
MACHINE_ID:HELLO001
FILE_CREATED_TIME:2016/11/11 11:11:11
HEADER_END

MATERIAL_DATA_BEGIN
MATERIAL_ID,RECIPE_ID,ITEM1,ITEM2,ITEM3
SPRING_BATCH_MULTILINE_000001,SPRING_BATCH,,,
MATERIAL_DATA_END

MACHINE_MATERIAL_DATA_BEGIN
MACHINE_ID,MATERIAL_ID,START_TIME,END_TIME,ITEM1,ITEM2,ITEM3
HELLO001,SPRING_BATCH_MULTILINE_000001,2016/11/11 11:00:00,2016/06/02 11:11:10,,,
MACHINE_MATERIAL_DATA_END

DEFECT_DATA_BEGIN
MATERIAL_ID,X,Y,JUDGE
SPRING_BATCH_MULTILINE_000001,-735.329,914.988,G
SPRING_BATCH_MULTILINE_000001,-735.329,914.988,G
SPRING_BATCH_MULTILINE_000001,-735.329,914.988,G
SPRING_BATCH_MULTILINE_000001,-735.329,914.988,G
...
DEFECT_DATA_END

데이터는 XXX_BEGIN, XXX_END내에 존재 하고 있다. 각 컨텐츠는 CRLF로 구분 되어 있다. 각 컨텐츠에는 컨텐츠에 대한 헤더 정보가 있고, 그 하단에 정보가 있다. DEFECT_DATA 같은 경우는 수십만건이 있다.

특히, DEFECT data 경우는 상위에 있는, 자재, 장비 정보와 함께 Dataabase에 관리 되어야 한다. 이 처리를 위해 ItemWriter에서, StepExecution 객체 내부 ExecutionContext 에 자재, 장비 정보를 관리 하였다.

BEGIN, END로 감싸져 있는 정보 처리를 위해 Aggretation 하기 위한 Holder를 사용하였고, OOME 방지를 위해, 1000건씩 처리 하였다. Job 관련 spring-batch 설정 정보를 보면 commit interval이 1로 설정 되어 있다. 즉, BEGIN, END 쌍으로 처리를 하겠다는 것이고, 내부 데이터가 많을 경우를 대비하여, 1000건씩 처리를 하겠다는 의도 이다.

예제 환경

OS : Linux JVM : JAVA 1.8 Spring : spring-boot (1.4.2)

소스

linux(ubuntu 14.04) h2database 설치

h2database 는 엄청나게 좋은 db이다. Compatibility Mode(호환성 모드) 지원으로, Oracle, MsSql, MySql 등등으로도 사용 가능한 엄청난 db 이다.

요즘 linux mint를 사용하는 재미에 푹 빠져서, h2database 실행 icon을 만들고 싶었다.

이 방법은 oracle sql developer 설치 방법을 응용한 것이다.

방법

  1. h2 jar 파일을 다운로드 받은 후 아래와 같이 이동
mkdir /opt/h2database
mv h2-1.4.193.jar /opt/h2database
  1. h2database 실행 스크립트 작성
cd /usr/local/bin
vi h2database
  1. h2database 실행 스크립트 내용
#!/bin/bash
java -cp "/opt/h2database/h2-1.4.193.jar:$H2DRIVERS:$CLASSPATH" org.h2.tools.Console "$@"
  1. h2database 실행 가능하도록 변경
chmod 755 /usr/local/bin/h2database
  1. desktop 파일 생성
cd /usr/share/applications
vi h2database.desktop
  1. desktop 파일 내용
[Desktop Entry]
Name=h2database
Exec=/usr/local/bin/h2database
Terminal=false
StartupNotify=true
Icon=/opt/h2database/favicon.ico
Type=Application

들어가며

지난 몇 주간 Spring Boot, SmartAdmin(AngularJS1 version) 환경에서 SVG 기술을 이용한 화면 개발을 경험 해 보았다. 총 기간은 약 3주가 조금 안되는 기간(개인적으로 아주 긴 기간)이었고, 실제 개발을 한 기간은 대략 2주 정도 였다. 그동안 만들어 낸 결과물과 학습량은 아주 초라 하다. 허나, 앞으로 이런 경험은 할 수 없을 거 같고, 경험한 내용 정도는 기록해 둬야 할 거 같아, 몇 자 적는다. 개발툴은 eclipse 이고, 빌드 툴은 maven 이다.

Web Back-end

Spring Boot

spring framework를 사용 한다면, spring boot 기반에서 application 개발을 진행 하는 것이 요즘 대세인가 보다. spring boot의 모든 것을 살펴 볼 이유는 없고, 내가 사용했던 범위 만큼만 기술 한다. spring boot을 사용하여 web 개발을 하기 위해서는 pom.xml에 아래와 같이 기술 한다.

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

spring boot을 효과적으로 잘 이해하기 위해서는 본인이 사용하는 build tool의 개념과 원리를 잘 알아 볼 필요가 있다. 뭔 말이냐 하면, parent로 지정한 artifact에 기술된 pom.xml 내용을 적어도 한 번 쯤은 확인 해야 한 다는 말이다. parent 쪽 pom.xml 파일을 확인 하였다면, 아래와 같이 properties를 왜 재 정의 했는지 이해할 수 있을 것이다.

	<properties>
		<java.version>1.8</java.version>
		<start-class>xxx.AppMain</start-class>
		<spring.version>4.3.1.RELEASE</spring.version>
		<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
	</properties>

spring-boot-starter-xxx

이 밖에 본인이 사용해야 할 기술 목록은 여기서 찾아, dependency에 추가 해 주면 된다.

spring devtools

spring-boot-devtools 라는 artifact가 있다. 그리고 chrome 확장 프로그램에 LiveReload라는 툴도 있다. 이를 같이 사용하면 편안하게 개발 할 수 있다.

동영상을 참조 해 보자.

spring boot 구동 원리

호수를 유유히 떠다니는 백조가 우아해 보이는 이유는, 백조의 물갈퀴 발이 물 속에서 쉴새 없이 움직이고 있기 때문이다.

spring boot에 있는 sample을 돌려 보면, 내가 infra(db 관련 bean 같은 거…)성 bean을 spring에 등록 하지도 않았는데도, bean이 생성되어 DI를 받을 수 있고, 정말 나의 business logic만 구현 해도 될 것같은 착각이 든다. 그것도 빠른 시간내에 말이다.

maven dependencies 목록을 보면, spring-boot-autoconfigure-xxx.jar 파일이 있고, 내부에 META-INF/spring.factories 파일이 있다. 이 파일에는 XXXAutoConfiguration 클래스 이름이 적혀 있고, 이것들을 읽어 들여, @Conditional 로직에 맞추어 빈을 등록 하는 것이다.

WebMvcAutoConfiguration

web dev env구축은 선행 팀에서 이미 구축 해 놓은 상황 이었고, 나는 그 속에 들어가서 개발을 진행 하고 있었다.

나는 jQuery만 사용할 줄 안다. 그것도 내가 필요한 만큼만. modern javascript 개발 패턴은 아는게 없다.

하루는 개발을 하다가 html or js 파일을 수정하고, 전통적 방법으로 수정된 사항을 확인코자 chrome browser를 refresh 하였다. 반영이 안되었다. 몇 번을 refresh 해도 반영이 되지 않았다. chrome 설정에서 인터넷기록삭제를 한 후 다시 refresh 해도 안되었다. 그렇게 몇 분을 시도하고 고민하고 스트레스는 점점 쌓여만 갔다.

뭐가 문제였을까? 나는 eclipse로 개발 할 때, build automatically 옵션을 비활성화 한다. 이게 문제 였다. web assets는 src/main/resources 내에 있었다. 어? 이게 여기 왜 들어가 있지? 어랏? 브라우져에 화면이 나오고 동작은 하네? 뭐지?

직접 내부 코드를 살펴 보게 된 동기는 이거였다. WebMvcAutoConfigurationAdapter 코드를 살펴 보다 보면, addViewControllers 메소드가 있고, 이 것의 역할은 ParameterizableViewController를 등록하는 것이었다. 이때 AngularJS 1 개념도 나름 이해하게 되었다.

Web Front-end

Sublime Text

awesome

NodeJS, npm

web front-end 개발을 위해서 반드시 설치해야 할 시스템인 것 같다.

yeoman

영국 황실 근위병이라고… 그래서 파비콘 이미지가… 이건 사용하지 않았다. 검색을 통해 알게 되었다.

bower

maven dependency 등록이라고 생각 하자. 이건 사용하지 않았다. SmartAdmin에서 이미 의존관계 설정이 되어 있다. 역으로, 이게 문제다. version up을 할 수 없다.

gulp

maven life cycle 관리, plug-in 등록 및 action 설정 이라고 생각 하자.

SmartAdmin

이거 만든 사람 고생 좀 했겠다.

javascript

할 말이 없다. 어렵다. 그래도 신선했다. 일급객체가 function 이다.

websocket, stomp

Promise, $q

약속은 지킬수도 있고 안 지킬수도 있다. 이 상황은 모두 비동기 상황이다. 검색을 하면, 이미 많은 사람들이 정리해 두었다.

Conclusion

웹 기반 기술이 이렇게 다양 한지, 알고는 있었지만, 다 남들 이야기 인 줄 알고 관심조차 없던 나에게 지난 시간은 감사하고, 소중 했다. 기회가 언제 다시 주어질 지는 모르겠지만, 기회가 주어진다면 javascript를 무자비 할 정도로 파 보고 싶다.

도메인 모델

DDD START책을 읽고, 생각을 정리로 남긴다.

도메인?

소프트웨어로 해결 하기 위한 문제 영역.

도메인 모델?

도메인 모델은 상황에 따라 해석이 나뉜다.

분석, 설계 시점?에서는 도메인의 개념적 표현 이다. 이 개념적 표현은 UML 혹은 표현 기법에 구애 받지 않은채 모든 방법을 동원하여 표현이 실체화 되어야 한다. 모든 이해 당사자들은 실체화 된 것을 공유하고, 관리를 해야 한다.

구현 시점에서는 당연히 구현 기술을 이용하여, 앞서 도출된 개념적 도메인 모델을 구현하게 된다. Layered Architecture에는 도메인 layer가 존재 하고, 바로 이 layer에서 사용되는 모든 객체 모델을 도메인 모델이라고 부른다.

도메인 모델 도출

도출을 한다는 표현은, 개념 도메인 모델을 어떠한 구현 기술을 이용하여 구현하는 과정이라고 생각한다. 이러한 과정 중 개념에 대한 변경 혹은 새로운 개념이 생성 된다면, 다시 개념 모델을 재정의 하고, 구현 도메인 모델에 적용해야 겠다.

이 행위를 하기 위해, 가장 중요한 것은 어떤 것들이 있을까?

첫째, 도메인 자체에 대한 이해와 이해를 바탕으로 한 올바른 해석

둘째, 개념 도메인 모델을 객체지향 기술로 구현을 한다면, 객체지향 기술에 대한 이해

셋째, 앞의 두가지를 아우를 수 있는 패턴에 대한 이해와 기타 장치

사견

세상은 그리 녹녹치 않다. 결국 돈이라는 아주 큰 이유로 여러 제약 사항이 발생한다. 나는 엔지니어다. 할 일이 많다. 부족하면 채우면 된다. 보다 더 근본적인 문제 해결 능력을 키워야 한다. 기술의 경우, 그 문제를 해결하기 위한 수단으로 학습하고, 그 정도로 충분하다.(기술에 압도 된다거나 오용은 금물!!!)

TDD, CI 와 같은 도구 기법이 생긴 이유를 생각해 보자.

구현부에서 가장 중요한 것은 객체지향 자체에 대한 이해와 객체지향 구현 기술 혹은 패턴이다.

Custem Id Generator

최근 프로젝트에서 database table의 primary key 생성 전략을 TimeStamp(yyyyMMddHHmmss) + sequence 조합으로 사용하였다.

이를 jpa(hibernate 구현체)에서 사용하기 위해 어떤 방식으로 처리 해야 할 까?

우선 custom id generator를 구현하기 위해서는, jpa 구현체에서 제공하는 비표준 jpa 기술을 이용하여야 한다. hibernate에서는 IdentifierGenerator interface를 제공한다. (다른 jpa 구현체는 확인 하지 않았다.)

public interface IdentifierGenerator {
	/**
	 * The configuration parameter holding the entity name
	 */
	public static final String ENTITY_NAME = "entity_name";

	/**
	 * The configuration parameter holding the JPA entity name
	 */
	public static final String JPA_ENTITY_NAME = "jpa_entity_name";

	/**
	 * Generate a new identifier.
	 *
	 * @param session The session from which the request originates
	 * @param object the entity or collection (idbag) for which the id is being generated
	 *
	 * @return a new identifier
	 *
	 * @throws HibernateException Indicates trouble generating the identifier
	 */
	public Serializable generate(SessionImplementor session, Object object) throws HibernateException;
}

위와 같이 generate를 overriding한다. generate의 object argument는 entity 혹은 collection으로 명시 되어 있다. 만약, method에서 object argument를 활용한다면, 이에 대한 코드를 주의하여 작성 하도록 해야겠다.

마지막으로, entity에 custom id 생성 전략을 알려줘야 한다. 예제는 아래와 같다.

	@Id
	@org.hibernate.annotations.GenericGenerator(name="orders_seq_id", strategy="ddd.support.OrderIdGenerator") // hibernate annotation
	@GeneratedValue(generator="orders_seq_id")
	@Column(length=2, precision=20)
	private BigDecimal id;

샘플코드

  1. Entity
  2. Custom Id Generator