기본 콘텐츠로 건너뛰기

2015의 게시물 표시

Spring Boot + Spring Batch 그리고 CommandLineRunner Unit 테스트 하기

커멘드라인에서 실행되는 배치 어프리케이션을 작성할때 겪었던 2가지 문제 정리

첫번째, 커멘드라인에서 실행하는 배치프로그램을 만들때 spring.batch.job.enabled 옵션을 꺼두지 않아 Job이 두번 실행되는 문제

http://stackoverflow.com/questions/23447948/how-spring-boot-run-batch-jobs
http://stackoverflow.com/questions/22318907/how-to-stop-spring-batch-scheduled-jobs-from-running-at-first-time-when-executin

spring.batch.job.enabled: false

두번째, 부트기반 커멘드라인 어프리케이션은 UnitTest를 작성하기가 불편하기 때문에 CommandLineRunner 인터페이스를 사용했다. 이 CommandLineRunner는 스프링 빈이 모두 생성 후 호출되어 Unit Tese 작성이 수월하다.

문제는 Unit Test를 설정에 @SpringApplicationConfiguration 을 사용하면 CommandLineRunner가 바로 실행 되어 버린다. CommandLineRunner가 여러개일 경우나 인자를 넘겨야 할 때는 문제가 된다. @SpringApplicationConfiguration은 단순히 SpringApplicationConfiguration Loader를 @ContextConfiguration로 사용하는 어노테이션이다. ( 코드 )

...@RunWith(SpringJUnit4ClassRunner.class)@SpringApplicationConfiguration(classes = App.class)publicclassAppTest{@Testpublicvoid test1(){...}}...
그래서 @ContextConfiguration 에 class를 지정하면 CommandLineRunner가 바로 실행되지 않게 할 수 있다.

...@RunWith(SpringJUnit…

Lombok은 어떻게 동작되나? 간단정리

lombok은 Annotation Processing과 Instrumentation을 사용한다. Instrumentation은 이클립스는 별도 컴파일러가 있기 때문에 이클립스 종속적인 bytecode 처리에 사용되고, AnnotationProcessor에서는 Annotation별로 코드를 생성하는데 사용된다.
Annotation별 코드 생성이란, 예를 들어 @Getter를 선언하면 실제 getter가 생성된다. @Getter String name;
public String getName(){returnthis.name;}
간단히 lombok구현은 AnnotationProcessor를 이용한 컴파일 시점 코드 생성으로 요약 할 수 있다. AnnotationProcessor는 Java Compiler와 관련이 있다. 잠시 정리해 보자 http://openjdk.java.net/groups/compiler/doc/package-overview
javac는 크게 두가지 부분으로 나눈다. 하나는 ".java"을 ".class"로 바꾸는 컴파일러이고 다른 하나는 컴파일러를 지원하는 환경이다.
컴파일러 환경은 세가지 API 패키지를 제공하고
Language Model API ( javax.lang.model package ) Annotation Processing API ( javax.annotation.processing package ) Compiler Tree API( com.sun.source package ) 컴파일러는 Java Compiler API 패키지를 제공한다.
Java Compiler API ( javax.tools package ) 
javac는 세가지 방식으로 호출 할 수 있다.
com.sun.tools.javac.main.Main: 커멘드라인에서 직접 호출 java Compiler API: javax.tools 패키지 이용 Compiler Tree API: javax.tools.CompilationTask를 com…

GitFlow

깃 기반에서 팀 프로젝트 운용이나 방법론을 개인적으로 정리하는 글입니다. #오해금지 #딴지금지 #지적환영

자동화된 테스트가 부족하고 우리 회사같이 금요일에는 배포가 없다든지 그리고 코드리뷰가 끝나길 기다려야 하는 등등 기다림이 있는 상황에는 Ticket 별로 Feature 브랜치를 생성해서 개발하는 GitFlow 브랜치 모델이 적합하다.

프로젝트 시작은 develop 브랜치에서 하고 Feature가 추가 될 때 Ticket (이슈) 별로 Feature 브랜치를 생성하고 Integration 브랜치를 두어 Feature를 머지하는 방식은 다른 브랜치 모델과 유사하다. 물론 Feature 브랜치가 시작되는 브랜치가 develop 인 것도 같다.

버그를 수정할 때, Waterfall 방식으로 개발하는 팀에서는 기간을 정해 QA 주관하에 진행 버그를 수정하게 되고, 애자일 방식을 가진 팀은 개발자가 Ticket 별로 여러 브랜치를 생성/개발을 하고 테스트는 다른 사람들이 진행한다. 그리고 두 가지 개발방식 모두 release-1.0과 같은 Integration 브랜치를 생성하고 QA를 한다. 어떻게 보면 당연하지만, Release 브랜치에는 Feature Freeze - https://en.wikipedia.org/wiki/Freeze_(software_engineering) 라는 개념이 필요하다. 즉 Release 브랜치에는 버그 수정은 하되 더는 기능추가를 하지는 않는다.

그리고 Feature Freezing 중 버그는 Release 브랜치에 적용되고 다시 develop에도 머지된다. 이렇게 Release 브랜치에서 QA를 계속 진행하며 모든 버그가 수정되면 배포준비 생태의 Release 브랜치가 하나 완성된다. 일반적으로 QA가 끝나고 Sign Off가 된 상태를 말한다. 그러면 master에 v1.0과 같은 버전을 하나 Tagging 하고 배포를 한다.

이렇게 끝나면 좋은데 보통 리얼에 배포되면 HotFix가 생기게 마련이다. 버전이 Tagging 되…

Branch Per Feature

깃 기반에서 팀 프로젝트 운용이나 방법론을 개인적으로 정리하는 글입니다. #오해금지 #딴지금지 #지적환영

Branch Per Feature는 브랜치 하나로 쓰는 전략은 아무래도 제약이 많아서 Feature 브랜치와 Integration 브랜치를 활용하는 방법에 대한 내용이다. Feature 브랜치와 Integration 브랜치는 하는 일은 같은데, 단지 어떤 커밋들을 대상으로 하느냐의 차이가 있다.

모든 작업은 Feature 브랜치에서 이루어지고 Integration 브랜치를 통해 다른 개발자 커밋을 반영하는 등 최신상태를 맞춘다. 릴리즈 할 때는 Integration 브랜치를 생성하고 빌드에 포함할 Feature만 골라 머지하고 릴리즈를 한다. 즉 마지막 빌드이후 완료된 모든 작업을 포함할 필요가 없다.

이렇게 Feature 브랜치와 Integration 브랜치를 활용하면 특정 코드가 배포되는 것을 막을 수도 있고 배포가 준비된 상태를 유지 할 수도 있다.

이런 Feature별 브랜치 모델에 대해 좀 더 자세한 내용은 ( http://dymitruk.com/blog/2012/02/05/branch-per-feature ) 링크에 설명되어있고, 조금 더 초기 모델이 GitHub Flow 다. ( http://scottchacon.com/2011/08/31/github-flow.html )

GitHub Flow 브랜치 모델에서 master는 항상 배포가능한 브랜치다. 개발자는 Feature 브랜치를 만들고 개발하고 master 브랜치에서 코드를 가져와 최신상태를 유지한다. 작업이 완료되거나 다른 개발자와 협업 또는 도움이 필요하면 Pull Request를 master 브랜치로 날리고 이슈트래커에서 개발한 기능에 관해 이야기를 나눈다.

이런 GitHub Flow와 Dymitruk 모델과 차이점은 단지 어떻게 배포가 일어나는가의 차이다. 즉 Dymitruk 모델은 어떤 Feature를 선택해서 빌드하느냐이고, GitHub Flow는 Pull Request가 수락되면…

Mainline Branch Development

깃 기반에서 팀 프로젝트 운용이나 방법론을 개인적으로 정리하는 글입니다. #오해금지 #딴지금지 #지적환영

메인라인 브랜치 모델은 브랜치 전략중 가장 쉬운 방법이다. 작업 하는 브랜치가 몇개 안되기 때문인데,, 개발자들은 하나의 중앙 브랜치에 계속 커밋을 한다. ( 이를테면 마스터 브랜치? ) 문제는 항상 배포준비가 된 상태의 브랜치를 유지해야 하는 어려움이 있다. 정확히는 마스터 브랜치 하나만 두고 개발을 하는 방식이라 개발자는 로컬 브랜치나 "git stash"를 사용해 리뷰나 빌드에 대응하게 된다.

그리고 이 모델은 프로젝트가 커지고 개발자별로 또는 스프린트별로 브랜치 많아 지면 머지비용이 커진다. 가령 개발자들이 각자 자신의 브랜치에서 작업하다가 각자 개발을 마치고 다른 사람 개발 내용을 합친다고 생각하면 쉽다. 코드충돌이 없으면 쉽지 않나? 라고 생각 할 수 있지만, 코드충돌이 없다 하더라도 사용하는 라이브러리가 변경되었거나 이기종 플랫폼간 메세지 통신을 할 때 메세지 포멧이 바뀐 상황이라면 머지비용은 적지 않다.

그래서 이 Mainline Model은 Continuous Integration 과 Continuous Delivery 패러다임에 적합한 모델이다. 그리고 개발에 대한 규칙이나 규범이 아주 잘 정비되고 개발자들이 잘 훈련되었다면 큰팀으로 확장 하기가 쉽다 - 잘 정비된 프로세스와 잘 훈련된의 뜻을 좀더 정리해 보면 - 예를들어서, 새로운 기능이 추가되는데 이 기능은 구현에 시간도 오래 걸리고 영향범위도 넓은 기능이라고 가정해 보자. 이 "기능을 잘게 쪼개고 작은 커밋으로 추가할 수 있는 프로세스가 있고 이 프로세스위에서 개발 할 수 있는 개발자들" 이라는 뜻이 되겠다.

그리고 Feature Toggle을 이용해 완료되지 않은 작업은 설정으로 제어할 수 있어야 한다.

결과적으로, 항상 배포준비가된 코드를 유지한다는 점 그리고 Feature Toggle 이 사용된다는 점을 고려해야 한다. ( Feature Tog…

Ansible 요약

기본
설정은 INI 포멧을 사용
ANSIBLE_CONFIG ./ansible_config~/.ansible.cfg/etc/ansible/ansible.cfg순서로 설정을 찾는다 ANSIBLE_ 로 시작하는 환경변수는 설정 파라미터로 사용할 수 있다.
export ANSIBLE_SUDO_USER=root
설정관리 툴셋
Puppet: manifestChef: cookbookAnsible: playbookplaybook은 yml 사용yml 자료: https://www.digitalocean.com/community/tutorials/how-to-create-ansible-playbooks-to-automate-system-configuration-on-ubuntuChef나 Puppet은 Ruby를 사용facts 라는 것은 시스템 환경변수를 말한다
- name: “Show how debug works” debug: {{ ansible_distreibution }}

Inventory
INI 포멧의 file 이다 -i 또는 --inventory-file ansible.cfg에 host_file이 기본 설정 group을 정할 수 있다[webservers] 192.168.0.1 192.168.0.2
group of group
[webservers] 192.168.0.[1:100] [dbservers] 192.168.0.[1:100] [restartable] webservers webservers

regexp

[webservers] 192.168.0.[1:100]
external variable
192.168.1.2 ansible_ssh_private_key_file=~/.ssh/aaa.pub
command module
쉘변수나 오퍼레이터 <, >, |, & 등은 안됨 C에서 fork 와 유사
- name: Reboot command: /sbin/shutdown -r now sudo: yes
raw module
컴퓨터 머신에 적용하거나 SSH에서 …

카우치베이스 요약1

⌘ 카우치베이스 서버 클러스터는 1개 부터 1024개 까지 노드로 구성 될 수 있다.⌘ 노드 하나가 하나의 카우치베이스 인스턴스.⌘ 데이터는 클러스터내 노드들에서 파티션되고 분산된다.⌘ 카우치베이스 서버는 두개의 주요 컴포넌트가 있다.클러스터 매니저 * 클러스터내 노드 설정 * 노드간 데이터 리발란싱 * 페일오버 후 데이터 복제 핸들링 * 통계자료 수집 * 로깅 * 클라이언트가 어디서 데이터를 찾아야 하는지 알려줄 수 있게 클러스터 맵(Cluster map)을 업데이트하며 관리한다. * 어드민 API를 노출하고 있고 웹 매니징 콘솔도 있다. * 클러스터 매니저는 distributed, concurrent 한 처리에 적합 하도록 Erlang/OTP로 만들어졌다. 데이터 매니저 * 데이터 저장소와 검색에대한 관리 * 메모리 케시 레이어, Disk Persistence Mechanism, 쿼리 엔진을 포함하고 있다 * 카우치베이스 클라이언트는 카우치베이스 매니저가 제공하는 클라이언트 맵을 사용한다. 이 맵을 통해 필요한 데이터를 가진 노드를 찾고 그 노드와 통신한다. ⌘ 데이터 스토리지카우치베이스는 데이터를 버킷에 관리한다. * 리소스와 연관된 로지컬한 그룹이다. * 오라클의 스키마 정도로 생각하면 된다. 두가지의 버킷종류를 제공한다. * 카우치베이스 * 멤케시 멤케시 버킷은 * 1MB 크기의 메모리에 바이너리로 데이터를 저장한다. * 데이터를 디스크에 저장하지 않는다. * Redundancy 하려고 노드에 데이터를 복제 하지 않는다. 카우치베이스 버킷은 * JSON Document, Primitive data 타입이나 Binary blob 형태로 20MB 까지 데이터를 저장 할 수 있다. * 데이터는 메모리에 캐시되고 디스크에 저장된다. * 데이터는 부하분산을 위해 클러스터 내에서 노드간에 동적으로 Reb…

깃헙에서 릴리즈는 어떻게 하나?

간단,, git 명령어로 tag 생성

$ git tag 0.1
$ git push origin 0.1

또는
https://github.com/{id}/{repository}/releases (저장소 메인에서 release 클릭해서 이동)"Create a new release" 또는 "Draft a new release"https://github.com/{id}/{repository}/releases/tag/{tag_name} 에서 태그별 확인 가능
역시 간단,, git 명령어로 tag 삭제

$ git tag -d 0.1
$ git push origin :0.1

또는
https://github.com/{id}/{repository}/releases/tag/{tag_name} 에서 Delete 버튼 참고
https://help.github.com/articles/creating-releases/http://stackoverflow.com/questions/18216991/create-a-tag-in-github-repository

AngularJS 웹사이트는 어떻게 생성되나?

⌘ 웹사이트 소스코드는 angular.js가 빌드 될 때 생성/angular.js/blob/master/Gruntfile.js#L310docs라는 Grunt Task가 정의 /angular.js/blob/master/lib/grunt/plugins.js#L37-L42⌘ 결과 확인쉘에서 명령어를 실행grunt package webserver 브라우저에서 URL 접근http://0.0.0.0:8000/build/docs angularjs.org와 동일한 화면이 보임⌘ docs 빌드는 glup 사용⌘ 문서생성은 dgeni 사용/angular.js/blob/master/docs/gulpfile.js#L5/angular.js/blob/master/docs/gulpfile.js#L51-L57⌘ bootstrap CSS와 AngularJS로 만들어짐/angular.js/blob/master/docs/docs.config.js#L80-L98/angular.js/blob/master/docs/docs.config.js#L99-L105⌘ Dgeni⌘ Dgeni는 document processor들을 파이프라인으로 연동한 툴, 즉 여러 document processor 묶음이다. Technology agnostic - 서로 다른 문제들을 해결 위해 여러 다른 도구들을 사용한다는 관점http://en.wikipedia.org/wiki/Document_processor/dgeni#architecture/dgeni-example/blob/master/README.md#why-should-i-use-dgeni⌘ 기본 엔진은 구현은 간단?! 프로세서들이 Promise를 통해 Chaining 됨/dgeni/blob/master/lib/doc-processor.js#L108-L134deni-packages가 있다. 문서를 생성하기 위한 패키지들요기에 AngularJS용 ngdoc-package 패키지가 있다⌘ 정작 Dgeni 자체가 문서화가 잘 되어 있지 않다며 example을 제공/…

AngularJS는 어떻게 빌드하나?

웹사이트 소스코드는 https://github.com/angular/angularjs.org
소스코드는 https://github.com/angular/angular.js
대충 보면 두 개가 헷갈림
⌘ 빌드는 Grunt 기반, 빌드는 쉽다 grunt package
⌘ 별도의 모듈 관리 시스템 사용하지 않음angularFiles.js에 angular와 karma 두 가지에 관련된 파일 리스트들을 관리요렇게 사용 /angular.js/blob/master/Gruntfile.js#L114/angular.js/blob/master/Gruntfile.js#L160
⌘ 버전에 필요한 정보는 Git 명령어로 추출예: getCodeName예: getBuild예: getTaggedVersion예: getPreviousVersions
semver 사용getSnapshotVersion
⌘ plugins.js에 Task들 정의 ( 그닥,, 분리할 이유는,, )/angular.js/blob/master/Gruntfile.js#L10/angular.js/blob/master/lib/grunt/plugins.js
⌘ Travis CI 에서는 unit 또는 e2e 테스트만 돌림/angular.js/blob/master/Gruntfile.js#L25/angular.js/blob/master/.travis.yml#L34/angular.js/blob/master/scripts/travis/build.sh
⌘ Grunt에 등록된 Task는 test류, minify, package, ci-checks등이 있음ci-checks Task는 ddescribe-iit, merge-conflict, jshint, jscs 4가지를 체크ddescribe-iit 파일내용중에 ddescribe(?) 나 iit(?)가 있는지 검사/grunt-ddescribe-iit/blob/master/tasks/lib/check-file.js#L1-L16merge-conflict 파일내용에 머지되지 않은 파일이…

카우치베이스 요약3 - View

⌘ 카우치베이스 뷰Key 기반 동작은 데이터 접근에 효율적이고 성능도 좋지만 복잡한 동작엔 제한적이다. Document 기반에서는 컨텐트를 효과적으로 쿼리 할수있다.ViewN1QL 두가지 방법을 제공한다.뷰는 인덱스를 생성한다.뷰는 자바스크립트로 작성된 MapReduce function 이다.뷰는 Design Documents에 하나 이상 저장될수 있다.
⌘ 뷰 생성하기뷰를 큰 데이터셋으로 생성하면 클러스터 성능에 영향이 있을 수 있기 때문에 Development View를 생성해서 테스트 할 수 있다. Development View는 Development Design Document 에 생성되고 Production View 로 Publish 할 수 있다.뷰의 생성과 수정은 Admin Console에서도 가능하고 Code 에서도 가능하다.
⌘ 뷰에 쿼리하기Key로 쿼리하기 view.query({limit: 10, key: username}, function (error, results) { ... }); 범위로 쿼리하기view.query({startkey: startDate, endkey: endDate}, function (error, results){ ... }); 정렬 순서
NullFalse True Number Text ( 대소문자 가림, 소문자 우선, UTF-8 순서 )Arrays ( 저장된 값으로 동일하면 인덱스 순 )Object ( 키 알파벳순 )view.query({startkey: endDate, endkey: startDate, descending: true}, function (error, results){ ... }); 페이징skip 파라메터와 limit 파라메터의 조합으로 사용
view.query({limit: 10, skip: 10, key: username}, function (error, results) { ... }); firstPage 메소를 이용해 Paginator 조합으로 사용
view.fi…

카우치베이스 요약2

⌘ 카우치베이스는 Key-Value Store인 동시에 Document 데이터베이스다.
⌘ Document를 저장할때는 1차적으로 메모리에 캐시되고 경우에 따라 디스크에 저장된다. Eventually Persistent Model

⌘ Optimistic Concurrency와 Pessimistic Concurrency를 제공한다. Optimistic Concurrency
 -> 저장시점에 변경이 있으면 업데이트가 실패 (check-and-set)

Perssimistic Concurrency
-> 리소스에 접근 자체를 막는다. (get-and-lock)

⌘ Item을 저장할때 기본 TTL은 0이다. 즉, Document는 Nerver Expired.
⌘ 카우치베이스는 JSON Document의 일부를 업데이트 하지 않고 전체를 새 버전으로 바꾼다.
⌘ set 메소드는 create new item and override를 의미한다. 논리적으로 upsert ( insert-or-update ) 이다.

⌘ add 메소드는 set과 유사하지만 동일 키가 있으면 에러난다.
⌘ get-and-touch는 일시적인 값을 다룰때 유용하다. 가령 특정기간 동안 임시 저장을 할 수있다. 값을 읽을때 마다 expire time을 연장할 수 있다.

 -> client.get(KEY, {expiry: 3600, callback});

⌘ CAS (check-and-set) Get-with-CAS: 값을 가져온 후 업데이트 가능 여부를 체크
-> connection.get(KEY, function(err, result) { ... update(result); }
Check-and-Set: 값을 업데이트 할 때 체크
-> connection.set(KEY, DOC, {cas: cas}, function(err, result) { ... if(!err) /success/ } );

⌘ lock connection.lock(KEY,{ lockTime: 5 }, func…