2015년 12월 21일 월요일

웹페이지에 눈내리게 하자

크리스마스를 맞아서 운영중인 웹페이지에 눈내리는 효과를 내보고 싶어졌다.

1.
js snow effect로 구글링 해서 첫번째 링크가 10 Top jQuery Snow Falling effect Plugin로 나와서 바로 하나씩 클릭해봤다. 특히, 내 웹페이지가 모바일이 기본이어서 스마트폰을 꺼내서 모바일로 하나씩 접속해봤다.

2.
효과가 너무 심심하거나. 눈이 눈같지 않은 플러그인은 제외
효과가 너무 강하거나 스마트폰에서 버벅이는 플러그인도 제외
최종 선택한 플러그인은 바로

3.
플러그인의 웹페이지에는 문서가 딱히 눈에 띄지 않았는데, 소스를 내려 받아보니 js안에 설명이 자세히 있었다.

flakeCount,
flakeColor,
flakeIndex,
minSize,
maxSize,
minSpeed,
maxSpeed,
round, true or false, makes the snowflakes rounded if the browser supports it.
shadow true or false, gives the snowflakes a shadow if the browser supports it.

내 웹페이지가 흰색 배경이어서 flakeColor만 적당히 눈에 띄게 #dddddd 정도로 주고 실행시키니까 그럴듯 했다.

4.
눈내리는 효과를 시작하는 버튼명은 Let it snow. 마음 같아서는 let it snow 노래도 넣고 싶지만, 음원 저작권을 신경쓰고 싶지 않아서 효과만 넣었다. 대신 버튼은 붉은색으로...

2015년 10월 6일 화요일

Handlebarjs로 생성하는 HTML을 서버에서 생성하기

underscorehandlebarjs로 생성하는 HTML조각을 서버에서 생성해야 하는 경우가 생겼다. 현재 서버에서 정형화된 데이터를 JSON으로 받아서 javascript에서 underscore로 자료구조를 변경한 뒤에, handlebarjs로 템플릿을 씌워서 jquery로 DOM에 붙이는 작업이 있는데, 이를 클라이언트에서 변경하지 못하도록 서버에서 생성해야 하는 것이다. 결국에는 서버에서 모든 HTML을 생성하여 PDF로 바로 생성하는데 그 목적이 있다.

1.
javascript 엔진을 java에서 구현한 게 있으니 그걸 사용해서 해보자. java 1.7까지는 rhino가 있고, 1.8부터는 nashorn이 있다. 현재 프로젝트가 1.7을 사용 중이어서 rhino에 underscore와 handlebajs를 load해서 실행시키는 것 까지는 성공했다.

        Context cx = Context.enter();
        Global global = new Global(cx);
        cx.evaluateString(global, "print('Hello World!')", "helloWorld.js", 1, null);
        cx.evaluateString(global, "print('Hello World!')", null, 1, null);
        cx.evaluateString(global, "function a() { print('this is a') }", null, 1, null);
        cx.evaluateString(global, "a();a();", null, 1, null);
        cx.evaluateString(global, "load('underscore.js')", null, 1, null);
        cx.evaluateString(global, "load('handlebars-v4.0.1.js')", null, 1, null);
        cx.evaluateString(global, "print(_.now())", null, 1, null);
        cx.evaluateString(global, "print(Handlebars.Utils.isFunction(_.now))", null, 1, null);
        Context.exit();

2.
그런데 jquery를 안된다. rhino 내부에 window 객체가 없어서 에러가 난다. 구글링을 해봐도 전부 2008년도 즈음의 웹페이지들만 검색이 되고, 대부분 안된다는 내용이다.

3.
1번에서 사용한 javascript context에 java의 VO객체 유형의 데이터를 넣고 underscore로 데이터를 조작한 다음, 바로 handlebarjs에 넣어서 템플릿을 만들고 그걸 java로 다시 받아와서 이어 붙이는 형태를 해볼까 생각해봤다.

4.
그러던 와중에 handlebarjs의 java 버전이 있는 것을 찾아 냈다. 블로그는 여기. 예제를 따라하니 금방된다.

Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hello {{this}}!");
System.out.println(template.apply("Handlebars.java"));
template = handlebars.compile("mytemplate");
System.out.println(template.apply("Handlebars.java"));

5.
jsp에 <script> 태그로 선언한 템플릿을 hbs 파일로 나누거나, 하나의 파일을 읽어서 나누거나 해서 서버에서 충분히 HTML코드를 만들 수 있게 되었따.

6.
underscore로 정형화된 데이터를 재구성하는 것은 DB조회시부터 원하는 모양으로 조회하도록 변경할 예정

7.
템플릿을 미리 컴파일해놓고 쓸 수 있게 최적화하는 것도 이번에 고려할 예정

2015년 9월 14일 월요일

javascript로 csv, tsv 파일 저장하기

브라우저에서 javascript로 csv, tsv 형식의 파일을 로컬로 저장해야한다.

1.
javascript object를 csv, tsv 형식의 문자열로 변환해주는 라이브러리, PapaParse

2.
브라우저에서 파일로 저장하는 라이브러리, FileSaver.js

3.
csv, tsv 형식의 정의와 각각의 media type, csv, tsv

4.
UTF-8 인코딩으로 만들고, 엑셀에서 잘 읽을 수 있게 UTF-8의 BOM추가

5.
완성된 javascript fucntion

var downloadAsCsv = function(data, filename) {
saveAs(new Blob(["\uFEFF" + Papa.unparse(data)], {type: "text/csv;charset=utf-8"}), filename);
};

var downloadAsTsv = function(data, filename) {
saveAs(new Blob(["\uFEFF" + Papa.unparse(data, {delimiter: "\t"})], {type: "text/tab-separated-values;charset=utf-8"}), filename);
};


2015년 7월 14일 화요일

디즈니 후짝짝에 딸래미 사진 넣기

내일 만 4세 생일을 맞는 5살 딸래미가 있다. 평소에 디즈니 주니어 티비를 즐겨 보는 편인데, 생일파티 후짝짝을 보면서 본인도 거기에 나올거라는 굳은 믿음을 가지고 있었다. 한달에 30명을 뽑기 때문에 되기도 어려울 것 같은데, 신청기간마저 지나버렸다. 이걸 잘 설명해주니까 바로 울음을 터트린다.

예전에 동영상 편집을 몇번 해봤던 기억을 가지고, 실제로 할 수 있을지 가늠해보지 않고서 "아빠가 만들어줄게~!"를 질러버렸다. 울음을 그치고 달래긴 했는데, 이제 이걸 어떻게 만들지란 고민이 시작됐다.

1.
일단 원본을 확보했다. 디즈니 주니어 홈페이지에서도 영상을 유투브에 올려놨다. 후짝짝으로 검색하면 유투브에 월별로 영상이 나온다. 유투브 다운로더 프로그램를 이용해서 원본을 고화질(1280x720)로 다운로드받을 수 있다. 

2.
동영상 편집 프로그램이 필요해서 무료 프로그램을 찾아보니, Lightworks란 프로그램이 좋다고 하여 설치했다. 그런데 프로그램 사용이 직관적이지 않고, 관련된 강좌나 매뉴얼이 (프리미어 프로에 비해) 부실해서 원하는 작업을 빨리빨리 하기가 어려웠다.

3.
예전에 써본 프리미어 프로를 알아보니 요즘에는 프리미어 프로 CC라고 해서 월별로 구매해서 사용이 가능했다. 난 한번만 쓸꺼니까 30일 시험판을 설치했다. 설치도 클라우드 어쩌구를 설치해서 그 안에서 로그인도 하고 설치도 하는 구조이다. 그리고 구매로도 이어지게 해놨다.설치 중간에 .net framework과 window media player 설치가 필요했다.

4.
미리 다운로드한 원본(mp4)을 불러와서 필요한 부분만 자르고 다시 내보내는 것은 따로 강좌를 찾아보지 않아도 이것저것 눌러보면서 작업이 가능했다. 바로 내보내기도 되고, 대기열에 넣어서 백그라운드로 작업할 수도 있었다.

5.
1분짜리 하나의 영상에서 10명의 아이들의 생일을 축하해준다. 처음에는 10명 모두를 딸래미로 바꾸려고 했는데, 작업시간에 한계가 있었다. 그래서 딸래미가 좋아하는 캐릭터가 같이 나오고, 사진의 모양이 작업하기 쉬운 동그라미인 장면만 작업대상으로 정했다. 물음표표시에 아이 얼굴이 나오고, 모자이크된 부분에 이름과 생일이 표시된다.


6.
영상에 넣을 사진을 고르고, 온라인 포토샵 사이트를 이용해서 동그라미 부분을 제외하고 투명하게 만들었다. 영억선택도구 오른쪽에 선택모양을 원으로 바꾸고, 가로세로비율이 바뀌지 않도록 하면 이쁘게 동그라미 선택을 할 수 있다. 복사하고 새 이미지를 만들때 배경 투명 옵션으로 만들고 붙여넣으면 된다.

7.
편집된 사진을 동영상 중간에 넣는 작업은 유투브 강좌를 보고 쉽게 할 수 있었다. 그리고 좀더 자연스런 움직임을 위해서, 시간(프레임)에 따라서 위치와 크기가 바뀌어야 했다. 이것도 유투브 강좌가 도움이 됐다. 타임라인의 프레임을 하나씩 움직이면서 사진의 Position과 Scale을 변경해서 배경 움직임에 맞추도록 조정했다.

8.
딸래미 이름과 생일날짜는 픽픽에서 비슷하게 만들었다. 원본 영상에서 사용한 색을 뽑아와서 최대한 비슷하게 만들었는데, 더 비슷한 글꼴을 찾으면 똑같이 만들 수도 있을 듯 하다. 이름과 생일도 사진과 동일하게 동영상에 넣고 프레임마다 위치를 바꿔주었다.

다 만들고 나서 가족에게 보여주니 반응이 좋다. USB에 담아서 큰 TV화면으로 보여주니 딸래미도 좋아한다. 대만족!

2015년 7월 7일 화요일

더 지니어스 시즌4 2화 후기

2화는 호러레이스였고, 게임은 아무 맘에 들었지만, 편집은 아쉬운 점이 있었다.

1. anti-연합게임 호러레이스
메인매치 게임이야기를 안할수가 없다. 시즌4의 문구(You can't win again in the same way.)에 아주 걸맞는 게임인 것 같다. 그동안의 지니어스 게임은 다수의 연합이 만들어지면 그들에게 상당히 유리하게 게임을 풀어갈 수 있는 경우가 많았다. 그런데 호러레이스는 플레이어가 지지하는 캐릭터를 제외한 캐릭터코인들을 모아서 레이스를 하기 때문에, 연합을 해서 특정 캐릭터를 몰아서 지자하면 정작 레이스를 할 캐릭터코인은 부족한 현상이 발생한다. 그리고 뱀파이어로 그것이 실제로 일어났다. 연합을 하면 불리해지는 구조를 게임규칙으로 녹아낸 제작진에 박수를 보낸다.

2. 불친절한 편집
플레이어마다 어떤 캐릭터를 지지하고 있고, 연합이 어떻게 구성되어있는지가 전략을 짜는데에 핵심이 된다. 이를 전체적인 관점에서 한눈에 보지 않으면 이제 남은 코인이 몇개이고, 연합의 문제점이 무엇인지 잘 보이지 않는다. 편집 중간중간에 모든 플레이어의 지지하는 캐릭터를 정리해주는 화면을 연합 상황과 함께 더 자주 보여주면 어땠을까하는 아쉬움이 남는다. 장동민과 홍진호가 각자 자신의 연합의 문제점을 파악하는 시점에, 시청자들은 따로 적어놓지 않았다면 그냥 그려려니 하고 방송을 보기만 하게 된다.  그리고 라운드마다 플레이어가 코인을 뽑으면 대부분의 플레이어들은 뽑히지 않은 코인의 갯수를 계산하면서 플레이할 것이다. 이런 것들도 편집화면으로 잠깐 잠깐 정리해서 보여주면 더 친절한 편집이 되지 않을까.
게다가 예고편에서 홍진호 컷을 보여주면서 시청자를 낚았다.

3. 장동민
메인매치와 데스매치 모두 홍진호를 발라버린 장동민으로 정리할 수 있을 것 같다. 연합구성은 이상민과 김경란이 주도적으로 만들었지만, 실질적인 전략구성이나 실행은 장동민이 진행시켰고, 다수연합전략의 문제점도 먼저 파악을 해서 이상민을 우승시키는데에 가장 큰 역할을 했다고 생각한다. 이상민에게 생명의 징표를 받을만 한데, 받지 못했다. 그래도 연합에 들어오면서 부터 전부다 책임지지 못한다는 것을 알고 있었기에, 이해하는 듯 하다.

4. 홍진호
홍진호는 본인이 세운 전략에도 구멍이 있었다. 가넷매치인 만큼, 현재 연합 플레이어들이 보유한 가넷수를 고려하여 지지 캐릭터를 결정했어야함에도 불구하고 딱히 별다른 기준 없이 캐릭터를 선정하여 막판에 멘붕에 빠지게 되는 오류를 범하였다. 그리고 초반에 생각해낸 연속 코인포기 전략을 왜 첫 라운드부터 사용하지 않았는지가 의문이다. 3라운드만에 게임이 끝날 것이라고 예측을 못하고, 마지막 4개 라운드에서 사용하고자 했었지 않았나란 추측만 해본다. 중반 이후, 전략을 탈락자들을 위해 수정했다. 그러나 너무 포기를 빨리한 것이 아닌가를 생각이 든다. 이준석의 말처럼 미라가 1등을 못 하게, 그리고 전진을 못하게 전략을 짜서 (연속 코인포기 등으로) 게임을 장기전으로 가져가게끔 했으면 멋진 역전승을 만들 수도 있지 않았을까 싶다. 데스매치에서도 기존의 본인의 전략윷놀이 전략이 노출되어 있다는 것을 간과하고 같은 방식으로 플레이를 해서 완패를 당했다.

5. 임요환
시즌2에서 새로운 게임에 대한 적응력이 매우 낮은 모습을 보여주었었다. 다른 사람의 플레이를 볼 수 있었던 흑과 백을 제외하고는 모든 게임에서 평범하거나 모자란 플레이를 했었다. 시즌1,2,3의 주요 플레이어들이 모여 있는 시즌4에서 2화 탈락은 쉽게 예상되는 수순이라고 생각한다.


2015년 6월 29일 월요일

더 지니어스 시즌4 1화 후기

몇 안되는 챙겨보는 예능 중의 하나인 더 지니어스가 시즌4를 시작했다. 1화를 다시보기로 보고 후기 몇개 챙겨보고 나도 후기를 올려 보기로 했다.

1. 본인의 플레이를 하지 못하는 김경훈
게임 초반인지, 게임 전의 인터뷰인지는 확실치 않지만, 분명 스스로 진실된 플레이를 하겠다고 이야기를 했었다. 그런데 게임 초반부터 양다리를 걸치는 행동을 하고, 결국에는 양쪽 모두의 눈치를 보다가 급한 마음에 이상민의 사형수 카드를 얻게(?)되는 행동을 한다. 결과론으로 이야기를 하지 안하도, 이런 플레이는 같은 연합의 플레이어들에게 예상하지 못하는 결과를 만들게 되고, 곧 본인의 신뢰도 하락으로 이어진다. 스스로의 판단을 하지 못하고 주변에서(특히 강한 카리스마를 가진 이로부터) 이렇게 해라 저렇게 해라는 요청을 받으면 그때그때의 상황을 모면하기 위해 반응하는 성향인 듯 하다.

2. 배신했지만 이해를 얻고 실리를 챙긴 이상민
반대로 같은 배신을 했지만, 욕을 먹으면서도 욕하는 사람이 이해가 되는 행동을 한 건 이상민이다. 얼떨결에 본인의 사형수 카드를 가져가면서 보다 더 많은 욕을 먹고 있는 김경훈이 불쌍한 나머지 마지막에 그를 구원해준 듯 하다. 그러면서 메인매치 우승과 가넷2개라는 실리를 취했고, 생명의 징표를 오현민에게 주면서 다음 라운드를 위한 디딤돌도 박아놨다. 게다가 사형수 카드를 직접 교환하여 전달한 최정문은 데스매치에서 제외되어 심리적으로도 미안한 마음은 좀 덜 부담하게 되었다.

3. 멘트는 맞지만 행동은 틀린 이준석
이준석은 "다수 연합이 너무 꼴보기 싫다"는 명언(?)을 남기면서 돌발 행동을 보여 주었다. 그의 생각에는 나도 동의한다. 그러나 꼴보기 싫어하는 다수 연합의 힘으로 승점 4점을 챙긴 상황에서 공용 카드를 들고 교환장소에서 빠져나간 것은 옳지 않은 행동으로 판단한다. 다수 연합을 깨고 싶었다면, 다수 연합이 그들의 작전대로 각자 승점 4점을 챙기도록 흘러가게 해두고, 본인은 임요환, 김경훈과 다른 작전을 더 수행해서 더 높은 점수를 챙기는 모습을 보여주어야 했다.

4. 계산기 오현민
승점이 공개되지 않는 상황에서 많은 인원이 같은 승점으로 공동 우승하도록 만들어야한다는 것이 그에게는 큰 부담감이었을 것이다. 그나다 공용 카드를 만들어서 공유한다는 생각(내 기억에는 오현민이 생각해낸 듯 한데 확실치 않다)으로 그나마 계산이 좀 편해졌을 듯. 초반부터 이상민에게 사형수 카드라는 고급 정보를 듣게 되면서 이상민이 부여한 전략/계산 역할을 더 충실히 하고자 했던 것이 아닐까 추측해본다. 너무 많은 사람들의 승점을 계산하느라 더 좋은 전략을 보여주지 못한 것이 아쉬운 점

5. 김유현, 임요환
이번 게임처럼 뛰어난 언변으로 사람들을 설득해서 연합을 만들어내는 것이 키포인트인 경우에는 눈에 띄는 플레이를 보여주지 못 하는 유형이다. 포커 플레이처럼 개인플레이가 가능한 게임에서만 그 능력을 보여줄 수 있을 듯 하다. 사족을 달면, 그런 면에서 김유현보다는 오현민이, 임요환보다는 홍진호가 낫다고 생각한다. 그리고 마지막에 이상민이 다른 카드로 공식 교환하는 것을 포착한 김유현이 방송에 나오는데, 내 의견은 "그래서 뭐?" 달라지는 것은 아무것도 없다.

6. 장동민, 홍진호, 최연승, 김경란, 임경선
이상민의 활약을 지켜만 볼 뿐...

7. 최정문
마지막 기지로 사형수 카드를 유정현에게 넘기는 장면이 없었다면 기억될 플레이가 없었다.

8. 유정현
가만히 있다가 얼떨결에 데스매치를 하고, 실력 부족으로 떨어졌다. 그나마 데스매치 상대로 이준석을 선택한 것이 이해가 되고, 기억에 남는 플레이였다.

아무래도 플레이어 중심으로 후기를 남기기 된다. 게임 룰에 대한 아쉬움이나, 내가 플레이어였다면 어떻게 했을까라는 생각도 드는데, 이건 다른 포스트에 남겨야 겠다.

2015년 6월 27일 토요일

나만의 개발환경

내가 좋아하는 개발환경 중에서 이클립스와 관련된 내용

1. 버전
별다른 특이사항이 없으면 최신 버전을 이용한다. 현재는 luna. 링크

2. Package
Eclipse IDE for Java EE Developers로 받아야 Web과 관련된 개발을 편하게 할 수 있다. 일반 Package를 받으면 HTML Editor도 없어서 html파일을 더블 클릭으로 열면 이클립스에 내장된 브라우저로 열린다 -.- 여기서 패키지별 기능 비교가 가능하다.

3. Workspace
root에 dev와 같은 특정 이름으로 디렉토리를 만들어서 그 안에 이클립스가 설치되는 폴더와 workplace 폴더를 만든다. 포맷을 대비하여 d 드라이브를 선호한다. root에 만드는 디렉토리는 이클립스를 설치하는 의도가 드러나도록 이름을 짓는다. 일부러 과거 버전의 이클립스를 쓰는 경우에는 kepler, indigo와 같은 이름으로 만들거나, 특정 프로젝트를 위한 개발환경이라면 해당 프로젝트 이름으로 만드는 식이다. root에 만드는 이유는 소스파일들의 전체 경로가 너무 길어지지 않게 하기 위해서 이다.

4. Perspective
기본적으로 Java Perspective를 사용하지만, 아래처럼 View 구성을 약간씩 변경한다. 

- 왼쪽 View에 JUnit View를 추가한다. JUnit을 돌리는 시점이라면 테스트 대상이 되는 Java 파일에만 집중하게 되므로 전체 트리 구조는 볼 필요 없기 때문이다.

- Server, Ant View를 왼쪽 아래에 넣는다. 둘다 위아래로 긴 목록이 나오지 않는 스타일의 View이지만, 자주 접근하게 되므로 왼쪽 아래에 필요한 만큼만 보이게 줄여서 넣는다.

- 오른쪽 View를 최소화한다. 기본 설정에 Task List와 Outline이 있는데 둘다 항상 보고 싶은 정보는 아니고, 특정 시점에 뭔가 있는지 궁금해지는 View이다. Task List는 Mylyn을 쓰지 않으면 제거하고, Tasks를 추가하여 넣는다.

- 아래쪽 View도 필요한 것만 남기고 제거한다. Problems, Console, Progress, Search만 남겨놓는다. Javadoc은 필요할때 마우스 오버해서 보는 편이다.

여기 까지 설정하면 아래 처럼 된다.


5. Colors
밝은 계열의 테마를 선호한다. 그래서 이클립스의 기본 설정색을 변경하지 않고 그냥 사용한다. 오히려 cmd.exe나 putty 사용할 때에 기본 설정인 검정색 바탕에 회색 글씨를 희색 바탕의 검정색 글씨로 변경해서 사용하는 편이다.

6. Fonts
글씨체는 무조건 나눔고딕코딩으로 변경한다. 개발하기에 이보다 좋은 폰트는 못 찾은 것 같다. 이 주제에 대해서 나름의 관심이 있어서 많은 Font를 써봤지만, 한글까지 정확하고 이쁘게 표현하는 Font는 나눔고딕코딩이 유일한 듯 한다. 그리고 큰 글씨를 선호하기 때문에 기본 크기도 14로 올려서 사용한다.

7. Keys
단축키도 몇가지를 추가한다. File Search에 Ctrl + Shift + F 를 지정해서 패키지 익스플로러에서 바로 Selected 범위로 파일내용을 찾기에 편하게 만든다. Copy Qualified Name에 Ctrl + Shift + C 를 지정해서 전체 경로를 폼함한 파일명이나 패키지명을 포함한 클래스명을 손휩게 복사할 수 있게 만든다.

... 더 있지만 블로그가 너무 길어지므로 여기까지...


JSON 문자열의 데이터를 HTML escape 할때 주의점

브라우저에서 AJAX로 JSON을 요청한 경우, 서버에서 JSON 문자열을 리턴해주는데, XSS 방지를 위해 특수문자 6개( & < > " ' / )는 HTML 엔티티로 변환해서 넘겨준다.

return input.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\\\\\"", "&quot;")
.replaceAll("'", "&#x27;")
.replaceAll("/", "&#x2F;");

그런데 "의 경우에는 " 자체가 JSON에서 사용하는 특수문자이기 때문에 \"와 같이 표현되도록 JSON이 생성된다. 그래서 "를 &quot;로 변경하면 실제로는 \" 가 \&quot;가 되므로 escape 문자인 \가 데이터인 것처럼 변경된다. 그래서 \"를 &quot;로 변경해야 한다.

그런데 값이 \일때는 또다른 문제가 발생한다.

name이라는 항목의 실제값이 \ 라고 가정하면,
JSON인코딩 문자열은 {"name":"\\"} 가 된다.
이게 위 XSS 치환 로직을 거치면 {"name":"\&quot;}가 되면서 JSON 규칙에서 어긋나게된다.

JSON spec에 따르면 값(value)는 쌍따옴표(")로 둘러싸여져 있고, 값 바로뒤에는 특수문자 } , ]만 나올 수 있다. 쌍따옴표(")가 값(value)이 아닌 이름(name)일 수 도 있으므로 : 도 나올 수 있다. 그래서 정규식에 negative lookahead를 적용해서 \\\"(?!,|]|:|})를 &quot;로 변경하도록 적용하니 값이 \일때도 정상적으로 처리된다. 그리고 Java String의 escape 문자도 적용되어야 하니 Java String에 넣을려면 \\\\\"(?!,|]|:|}) 가 되어야 한다.
실제 데이터가 ":" 일 때는 \":\" 처럼 데이터가 생성된다. 그래서 위에 취소선을 그은 방법대로 하면 , } ] : 앞에 나타나는 "는 치환되지 않는다. 그래서 negative lookbehind를 적용해서 짝수개의 \\\\가 발생하고 바로 나타나는 \"에 대해서 치환하도록 정규식을 변경했더니 정상적으로 치환이 된다.
특정 패턴이 0번 혹은 짝수번 나타나는지를 확인하기 위해 negative lookbehind와 짝수번 패턴의 *를 사용했다. 참고 \\가 안나오고 \\\\가 0번이상 발생하는 패턴. 만들어진 정규식은 (?<!\\)(\\\\)*
Java String의 replaceAll 사용시 짝수번패턴은 치환하지 않고 그대로 유지하기 위해 치환할 부분을 제외한 전체부분은 그룹으로 묶고 치환해서 들어갈 문자열에 $1으로 지정. 참고


return input.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("((?<!\\\\)(\\\\\\\\)*)(\\\\\\\")", "$1&quot;")
.replaceAll("'", "&#x27;")
.replaceAll("/", "&#x2F;");

2015년 5월 29일 금요일

흥분하지 않는 이유

최근 회사에서 주변 사람들이 나에게 이런 말을 몇번 들었다. 흥분하는 모습을 한 번 보고싶다고, 기분에 up and down이 별로 없지 않냐고.

집에서도 와이프한테 한번인가 들었던 말이어서 딱히 놀라진 않았다. 그리고 그런 내 성격이 나름 인생살이에서 좋은 점이라고 생각하고 있었다. 그래서 그런 모습으로 나를 보는 사람이 있다는 것도 기분이 괜찮았다.

나는 왜 그런 성격을 가지게 됐을까? 당연한 말이지만 처음부터 그렇진 않았다.  회사에서 개발을 하고 여러 사람들과 같이 업무를 하다보니, 나만의 기준이 생기고 그 기준에 따라서 행동하다보니 성격으로 굳어진 듯 하다.

아래 3가지는 내가 진리라고 믿고, 따를 만한 가치가 있다고 여기는 기준이다. 즉, 인생에 대한 가치관이고 삶에 대한 행동양식이다.

1. 모든 사건에는 그럴만한 이유가 있다고 믿는다.

2. 내가 바꿀 수 없는 것은 고민하지 않는다.

3. 객관적인 사실을 확인하고 결정한다.

이라한 것들이 흥분하지 않는, 기분의 동요가 적은 나를 만든다고 생각한다.

위에 언급한 번호 하나하나에 대해서 다른 블로그에서 자세하게 쓰겠다. 안그러면 블로그가 너무 길어질 것 같다.

2015년 5월 25일 월요일

delacourt at a glance 만들면서...

최근에 취미 코딩으로 회사 지하식당의 메뉴를 좀 더 보기 좋게 제공하는 사이트를 만들었다. 이걸 만들면서 경험한 내용을 블로그로 남겨놓는다. 아마도 내용이 제법 길어질 수 있을므로 여러개의 블로그로 올릴 것 같다.

1.
처음에는 생각만 했었다. 지하1층 메뉴와 지하2층 메뉴를 한 페이지에서 확인 할 수 없고, 한번씩 링크를 눌러서 2개의 페이지에서 메뉴를 확인한다는게 좀 불편했었다. 아~ 좀 불편하네~ 좀 길어져도 한 페이지에 해 놓으면 페이지 전환없이 편하게 볼텐데...

2.
전혀 상관없는 계기로 웹페이지를 긁어서 원하는 데이터를 뽑아내고 원하는 형식으로 표시하는 작업을 2번 정도 진행했다. 첫번째는 로컬시스템의 html파일에서 JQuery의 ajax를 이용해서 작업했고, 두번째는 nodejs로 만든 서버를 AWS에 띄워서 request로 호출하고, 결과 html 문자열은 cheerio로 파싱해서 작업했다. 더 상세한 내용은 다른 블로그에서....

3.
비슷한 작업을 2번정도 연습하고, 그 와중에 AWS에 서버도 셋팅을 하고 나니, 식당 메뉴를 보기 좋게 만드는 작업을 해보고 싶어졌다.

4.
소스는 GitHub에 올렸다. 서버는 AWS를 쓰기로 했다. 자바스크립트로 nodejs로 구현해서 별도 웹서버는 필요없었다. 도메인은 생각하지 않고 시작했는데, 결국 여기에서 kr.pe의 서브도메인을 무료로 받았다.

5.
단순히 서버를 하나 띄워서 사용자의 요청이 들어올 때마다 공식홈페이지에서 데이터를 가져와서 뿌리기로 했다. 접속주소도 루트로 들어올때만 준비한 페이지를 보여주게 하고 그 외의 주소는 아무 표시없이 무시했다.

2개 페이지의 결과를 합쳐서 응답해야 해서, async라는 모듈을 사용했다. html template을 쉽게 만들기 위해 swig도 썼다. 위에서도 언급했지만, html 파싱은 cheerio를 썼다.

6.
하루 정도 서비스를 해보니까 하루에 몇명이나 들어올까가 궁금해졌다. 좀 찾아보니 구글 어날리틱스가 있어서 출근길에 붙였다. 하루종일 통계가 안나와서 답답했는데 하루가 지나니 자 나온다.

7.
빌드를 자동화해야 할 것 같아서 travis를 붙이긴 했는데, travis에서 실제로 AWS 서버에 배포하는 것 까지 붙이진 않았다. 대신 AWS 콘솔에서 사용할 작은 shell 프로그램만 하나 만들었다. nodejs 프로세스를 죽이고(kill), git pull 한다음 nohup으로 실행하는 것 까지 한다.

8.
공식홈페이지에서 제공하던 기능은 다 포함했기에 GitHub에서 1.0.0으로 릴리즈를 했다. 현재 버전이기도 하다. 현재 버전에 포함되지 않은 기능들은 GitHub의 Issue로 등록했다. 앞으로는 Issue 기반으로 작업할 생각이다.

여기까지가 큰 흐름이고, 자세한 내용은 번호마다 다른 블로그에서 더 자세하게 다루겠다.

2015년 5월 21일 목요일

java feature toggle

프로그램이 가지고 있는 특정 기능을 상황에 따라서 켜고 끄는 기능이 필요하다. 구글링을 조금 해보니 이런 걸 feature toggle이라고 하는 듯 하다. 지금 개발중인 시스템에서 이런 기능이 자주 필요해졌고, 이번에도 나 혼자 이런게 필요한 건 아니겠지라는 생각에 구글링을 시작.

1.
feature toggle의 구글링 첫번째 결과는 마틴 파울러의 블로그이다. 글 중반에 feature toggle의 유형을 다시 2가지로 나눈다. release toggle과 business toggle이다.

release toggle은 새로 개발된 기능을 일부 적용하고 손쉽게 되돌릴 수 있게 릴리즈하는 장치이다. 이건 내가 찾던 것은 아니다. 새로운 기능이 안정적으로 안착되면 toggle을 지우라고 권장하고 있다. 이게 시간이 지나면 Technical dept이 된단다.

business toggle이 내가 필요한 유형이라고 할 수 있겠다. 프로그램 runtime 시점에 특정 기능을 적용하거나 적용된 기능을 제외시키는 기능이고, 이게 관리자 역할의 사용자에게 권한이 부여되서 직접 시스템의 기능을 제어할 수 있어야 한다. (오피스 프로그램의 옵션처럼)

2.
feature toggle java로 검색해봤다. 이 기능을 쉽게 구현하게 도와주는 프레임웍들을 모아놓은 페이지가 있다. 4개의 프레임웍에 대한 링크가 존재하고, 구글링 검색결과와도 대략 일치한다. 이것들만 검토해보고 1개를 선택하거나, 직접 구현하는 경우에 대비해서 insight를 얻을 수 있을 것 같다.

3. Togglz
http://www.togglz.org/ 구글링 검색에서도 처음으로 링크되어 있고, 문서화도 잘 되어있고, 기능도 많다. 2014년 12월이 마지막 릴리즈이니까 죽은 상태도 아닌 듯. ENUM을 활용하고, enable/disable 개념과 active/inactive 개념을 가지고 있다. enable이어도 user나 time등의 기준에 의해서 inactive한 상태도 제어가 가능하다. admin기능을 자체적으로 web view와 함께 제공한다. feature들을 memory/file/DB에서 관리가 가능한데, DB는 테이블을 알아서 만들어서 사용하는 듯.

4. FF4J
http://ff4j.org/ 최근 한달전까지 커밋이 있었다. PDF로 된 문서는 잘 작성되어 있는데, 막판에 DB쪽이 나오면서 미작성된 부분이 있다. FeatureStore를 직접 구현하면 DB로도 가능할 듯. feature을 String으로 지칭한다. 설정없이 초기화하여 동적으로 feature를 추가할 수 있다.

5. Fitchy
https://github.com/akomtur/fitchy 프러퍼티 파일 기반이고 특정 interface를 구현한 클래스에 toggle이 필요한 메서드에 annotation을 달아서 특정값 또는 null을 리턴하도록 하는 구성이다. 리턴이 boolean이 아니고 Object가 가능하다는 게 특징인 듯. 마지막 커밋이 2년전인데, 실제 소스는 3년 전인 듯.

6. Flip
https://github.com/tacitknowledge/flip 3년전이 마지막 커밋이고 문서화도 부족함.

7.
기존 프레임웍들을 보면 xml이나 DB에서 feature 기본값을 읽어서 ENUM이나 Proxy Interface로 On/Off를 체크하는 방식을 모두 사용하고 있다. 그리고 Web Page를 통해서 그 값들을 변경가능하게 만들었다. 현재 개발중인 시스템에 구현한 내용도 동일하다. 굳이 추가적인 프레임웍을 붙여서 구현할 필요는 없을 듯.

8.
feature가 추가되면
-> DB에 행 추가
-> 관련VO에 member field, getter/setter 추가
-> Util의 메서드 추가
-> js object에 property 추가
이런 식으로 진행이 될 것 같다.


2015년 5월 11일 월요일

솔루션의 git branch 전략

지금 진행하고 있는 솔루션에서 갑작스럽게 git을 적용하다보니 branch를 어떻게 운영해야 할지에 대해서 막막했는데, 여러 자료들을 보면서 나름의 branch 전략을 세우려고 한다.

당연히 누군가는 이런 고민을 했겠지라는 생각으로 구글링을 시작.

1.
A successful Git branching model 번역 이게 제일 유명한 포스팅인 듯 하다. 특별한 이름은 없고 대부분 "성공적인 모델"이라고 지칭하는 듯. 길지 않은 글이지만, 한글로 짧게 요약한 글도 있다. 이게 더 잘 요약한 것 같다. 이 전략을 기반으로 gitflow라는 것도 누가 만들었다. 그리고 적용후기 (한글)

2.
branch 전략은 아니지만 분산 저장소들을 어떻게 관리할지에 대해서 그림으로 설명한 글도 있다. 같은 블로그에서 비공개 소규모 팀 운영을 위한 예제 시나리오도 읽어볼 만 하다.

이제 회사업무에 적용할 생각을 해보면...

3.
사내 ALM시스템을 사용하면 자연스럽게 1번에서 언급한 "성공적인 모델"을 사용하게 된다. master, develpoment, release-*, feature-* 까지는 그대로 사용하고, hotfix-*만 bugfix-*라는 prefix를 사용하게 된다. 아마도 master에서만 가져오는게 아니라 development나 release-*에서도 가져올 수 있는 구조이므로 일부러 이름을 다르게 잡은 것 같다.

4.
우리 솔루션만의 문제가 더 있다. 솔루션이 고객사 시스템에 설치되는 구축형이다 보니 고객사마다 릴리즈된 버전을 관리할 필요가 있다. 아직 버전에 대한 경험이 없어서 특정 시점의 소스형상으로 설치가 된다. 그 시점의 소스형상을 "고객사A" branch로 만든다. 고객사가늘어날 때마다 만들어지는 branch인데, 고객사가 많아지기 전에 고객사에 "특정 시점의 소스형상"이 아니라 "특정 버전의 소스형상"을 릴리즈하는 형태가 만들어져야 할 것 같다.

5.
팀내의 퍼블리셔가 만드는 이미지 파일과 HTML소스는 "publish" branch에서 관리된다. 언듯 생각하면 퍼블리셔에게 퍼블리싱하는 대상 기능에 맞는 feature-*에 commit하라고 하면 될 것 같지만, 그렇게 하지 못 하는 이유가 있다. 이미지 파일의 경우 여러 이미지를 합쳐서 하나의 이미지로 만들고 잘라서 쓰는 경우가 있고, css 파일도 하나의 파일에서 모든 css를 관리하고 있기 때문이다. 즉, branch를 나눠서 얻는 이점보다 merge할때 생기는 conflict를 해결하는 게 더 힘들어질 것으로 예상했다. 게다가 퍼블리셔 입장에서도 여기 저기 feature-*를 변경하면서 개발하는 것은 너무 번거롭다.

6.
결국 지속적으로 관리되어야 하는 branch가 4개가 된다. master, "고객사A", developement, publish. 
"고객사A" branch는 버전관리를 잘 해서 특정 버전 branch가 되도록 유도하여 결국에는 branch자체를 없에야 겠다. publish는 그냥 두고 진행한다. 퍼블리싱 대상 소스만를 위한 devleopment처럼 개념을 잡고 수시로 development쪽으로 merge를 수행하도록 해야 겠다.

7.
ALM에 bug가 등록되면 hotfix인지 여부를 제품책임자와 개발리더가 판단하여 branch를 hotfix면 master에서, 그렇지 않으면 development에서 가져오도록 해야 겠다. hotfix인 경우, 사내 ALM시스템에서 새로운 patch version을 생성해서 릴리즈 절차를 진행할 수 있도록 해야겠다. (그런 의도의 기능이라면) patch version의 bug에서 branch를 생성할 때 기본적으로 master에서 가져오는지 확인해야 한다. [TODO]

8.
6번과 7번처럼 정리가 되면 한번 사용한 branch들은 "성공적인 모델"에서 언급한 것 처럼 merge된 직후에 삭제되어도 된다. merge request를 개발리더가 accept한 직후에 삭제해버리면 될 듯. git remote repo에서 삭제한 branch를 이클립스 eGit에서 pull할때 자동으로 삭제되도록 개발자로컬환경 셋팅이 필요하다. 그래야 없어진 branch에서 개발하는 실수를 막을 수 있을 듯 하다. 이렇게 eGit의 버전을 올리고 prune 옵션을 설정해서 해결하면 된다.

3줄정리
- branch 전략은 "성공적인 모델"을 따른다.
- "고객사A" branch는 특정 version의 branch로 대체한다.
- publish는 상시 유지하고 수시로 development에 merge한다.

회사업무 중 문제해결 시나리오 정리 시작~

회사업무를 진행하면서 만나게 되는 문제들을 블로그로 정리하려고 한다. 어떤 문제들이 있는지, 어떤 과정을 거쳐서 해결책을 찾아내는지, 최종 선택은 무엇이고 그 근거는 무엇인지를 정리할 예정.