아이폰 APP 알림 인증서 관련

아이폰 APP의 특징중 하나는 알리미다.  앱이 종료, 혹은 슬립상태일 때도 사용자에게 노티를 해 줌으로써 사용성을 향상 시킬 수 있다.

APP의 알림을 보내기 위해서는 APP 별로 인증서가 필요하다. 이 인증서는 APP 별로 발급이 된다.

그럼 APP을 만들면 하나의 인증서만 있으면 되는가? 아니다.

APP 개발자가 아닌 내가 알기로는 인증서는 하나의 APP 관련해서 총 3개의 인증서를 발급 받을 수 있다.

1. 개발용: 말 그대로 개발용이다. 이 것은 애플사의 APNS를 보낼 때도 개발용 APNS에 전송해야 성공한다.

2. 인하우스용: 개발용은 아니지만 실제 배포하려는 앱의 베타 테스트를 위해 사용하는 버전. 실 APNS에 전송한다.

3. APP용: 모든 테스트가 마무리 된 후에 사용자에게 배포할 APP을 위한 버전. 이 버전이 우리가 APP을 다운받고 알림을 받는데 사용되는 것이다.

 

JRebel

웹 애플리케이션 개발을 하는 나는 자바 소스나 프로퍼티성 파일이 변경이 될 경우에는 WAS로 사용하는 톰캣 서버를 리스타트 해야했다. 혹은 reloadable=”true”로 설정을 해서 톰캣이 변경사항을 인지하고 서버를 재시작하는 방법을 사용하곤 했다.

그런데, JRebel은 서버 재시작의 번거로움 없이 변경된 리소스를 인지하고 즉시 클래스 로더에 변경 정보를 반영해서 사용가능하게 해준다. 물론 변경하자마자 바로는 아니지만, 대에충 1~2초 정도면 반영이 되는것 같다.

내가 하는 프로젝트는 그리 무거운 프로젝트가 아닌지라 톰캣의 재기동이 그리 올래걸리진 않지만, 그래도 10 여초 정도가 필요했다.

이를 기준으로 절약된 시간을 계산하면 약 80% 정도가 줄었고, 만약 프로젝트가 무거워지고 의존하는 라이브러리가 증가하게 되면 톰캣의 재시작은 더 오래 걸리게 되고 절약율은 더 커질 것이다.

더 중요한 것은, 서버 재기동 시간동안 집중력을 잃어버릴 수 있는 사잇 시간이 생기는데, 이 시간이 없어진다는 것이 업무효율, 집중력을 높일 수 있다.

이제까지 참 바보같이 일했다는 생각이…

감사합니다.

http://zeroturnaround.com/jrebel/current/

 

스프링 배치 구현체 FlatFileItemReader

스프링 프레임웍은 공통적인 문제의 해결 방법을 풀어주는 방법을 제공해준다. 그래서 개발자는 이러한 공통적인 문제가 아닌 비지니스 로직에만 집중할 수 있다.  그리고 비지니스 로직에서 유용할 수 있는 구현체를 제공하기도 한다. 이는 스프링 배치 프로젝트에서도 의외는 아니었다.

스프링 배치 프로젝트는 1.x 에서 2.x 로 버전 업 되면서 많은 것이 변했는데, 그런 변화를 document에 다 언급하지 못한 상태라 공부하려는 사람에게 혼동을 줄 수 있다. 이 포스트에서는 2.x 버전에서 흔히 필요할 수 있는 스프링 배치 구현체를 사용하는 방법을 얘기하려 한다. 그 중에서 FlatFileItemReader 먼저…

1. FlatFileItemReader (org.springframework.batch.item.file.FlatFileItemReader)

클래스 이름에서 알 수 있듯이 XML이나  json 형태의 데이터가 아닌 보통의 파일에서 데이터를 읽는 구현체이다. 파일에서 데이터를 읽어오는 방법은 구분자를 이용하거나 데이터의 길이를 고정해서 읽어오는 방법이 있다. 여기서는 구분자를 이용하는 방법을 설명한다.

[xml]<bean id="fileReader" class="org.springframework.batch.item.file.FlatFileItemReader"
p:resource="classpath:/file/people.txt" p:encoding="UTF-8">
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="name,gender,age"/>
</bean>
</property>
<property name="fieldSetMapper">
<bean class="example.file.PersonMapper"/>
</property>
</bean>
</property>
</bean>
[/xml]

위의 설정을 보면 읽을 파일의 위치와 인코딩 정보를 bean의 속성값으로 설정해줬다. 또 다른 설정으로 lineMapper 라는 것이 있는데, 이것은 파일에서 한줄을 읽어와 도메인 객체로 이용하기 위해 사용된다.

lineMapper 안에 보면 lineTokenizer와 fieldSetMapper라는 것이 있다. lineTokenizer는 이름을 보면 자바 개발자라면 쉽게 유추가 가능할 것이다. 한줄을 읽어와 특정 구분자를 이용해 정보를 구분하는데, 특별히 구분자를 지정하지 않으면 기본적으로 콤마(‘,’ comma)를 구분자로 사용한다. names라는 프로퍼티의 값을 설정하는데, 이는 lineTokenizer가 구분자를 사용해 읽은 정보를 얻어오기 위해 사용된다.

예를 들면, 파일에 다음과 같이 데이터가 쓰여지 있다고 가정하자.

박지성,male,31

그럼 이 파일을 읽었을 때 데이터에 접근하는 방법은 두 가지다. 하나는 배열을 사용하듯이 인덱스를 사용하는 방법이다. 이미 머릿속에 예상한대로 기본 구분자(comma)로 구분되는 첫 정보가 0으로 시작하는 배열처럼 접근이 가능하다. 다른 하나는 위의 설정에서 names 속성에 정의한 이름으로 접근하는 방법이다. 구분자로 구분되는 벙보 순서대로 name,gender,age라는 이름으로 접근이 가능하다.

이제 이 정보를 fieldSetMapper에서 접근해서 배치에서 사용할 도메인 객체를 생성해서 돌려준다. 물론 돌려주는 객체는 ItemProcess를 구현한 객체에서 입맛대로 처리해주면 된다. 여기서 사용한 fieldSetMapper는 다음과 같이 구현되어 있다. (여기서는 인덱스 번호로 접근하지 않고 xml에서 설정한 name,gender,age를 이용하도록 했다. 인덱스 번호는 따로 설정하지 않아도 그냥 사용이 가능하다.)

[java]public class PersonMapper implements FieldSetMapper<Person> {
@Override
public Person mapFieldSet(FieldSet fieldSet) throws BindException {
Person person = new Person();
int idx = 0;
person.setName(fieldSet.readString("name"));
person.setGender(fieldSet.readString("gender"));
person.setAge(fieldSet.readInt("age"));
return person;
}
}[/java]

Person이란 객체는 name,gender,age를 저장하는 단순한 클래스이다.

생각보다 설명할게 많아서 글이 길어졌는데, 기본적으로 필요로할만한 기능은 이미 스프링 배치에서 제공하는 것들이 있으니 ‘있지 않을까?’ 라는 생각이 들면 조금 조사하는데 시간을 들일 필요는 있을 것 같다. 아니면 만드련 되고~

다음에는 FileWriter 구현체를 이용해서 XML 타입, formatted 타입의 결과 파일을 생성하는 방법에 대해서 다루도록 하겠다.

fallback mechanism 라이브러리

지금 다니고 있는 회사에서는 퀵리뷰라는것을 진행하고 있다. 유용한 아티클등을 읽고 포인트만 찝어내서 공유하는 작업이다. 말이 ‘퀵’리뷰지 더 이상 퀵하지 않다. 왜냐하면… 자세한 이유는 생략한다.

어찌됐든 퀵리뷰를 했던 내용이 개발자 블로그에 올라갔다. 사실 저 글을 올린지 한달이 넘었다. 그래서 안 올라가나보다 했는데, 우여곡절(?) 끝에 올라갔다. 내가 올린 퀵리뷰의 포인트는 간단하다.

‘fallback mechanism 라이브러리’다. 사실 원문에서 언급한 내용은 퀵리뷰 하단에 요약으로 짧게 얘기했고 내가 하고 싶은 얘기를 했다.

fallback mechanism이란 웹앱을 만들 경우 브라우저에 따라 지원하는 기능이 다르기 때문에 의도한 기능이 지원/렌더링 되지 않을경우에 이를 대비해서 유사하게 동작하도록 만드는 방법을 의미한다.

이 글을 쓰고, 그 즈음 번역되서 출간된 ‘HTML5 & CSS’를 보았다. 사놓고 읽어보진 않았지만 여러 후기에서 본것은 fallback mechanism의 충실한 소개에 대한 감탄이었다.

다행히 나만 이런 생각을 하는게 아니구나라는 안도를 하게 됐다. 휴~~~

퀵리뷰 보기

스프링 배치에서 기본 Job 흐름도

최근에 개인적으로 하는 스터디에서 스프링 배치에 대해 발표할 일이 있었다. 생각지도 못한 발표라서 급하게 준비하느라 깊이는 얇다. 하지만 그 중에서 기본 job 흐름도를 공유하려 한다.

스프링 배치 홈페이지에 보면 10여년간의 배치성 job을 분석했을 때 다음과 같은 표본(stereotype)이면 웬만한건 다 커버할 수 있다고 한다.

위의 그림에서 나오는 ItemReader, ItemWriter, ItemProcessor의 흐름을 보면 다음과 같다.



알아보기 어려울 수도 있겠지만 그림에 나온 각각의 함수(괄호가 여닫힌 것)는 각각의 interface의 정의된 함수다. 오른쪽의 약간 희끄무릿한 것은 ItemProcessListener이다. 구현을 안해줘도 괜찮지만 구현할 경우 위와같은 흐름을 보여준다.

혹시라도 위의 그림이 안 맞거나 이해가 안되면 댓글 남겨주시기 바랍니다.

스프링 배치(Spring Batch)란?

스프링 배치(Spring Batch) 프레임웍에 대해서 공부하기 전에 먼저 그 정의부터 알아야할 필요가 있다.  Spring은 개발자라면 사용해보지는 않았더라도 들어는 봤을거라 생각해서 생략.

개인적으로 이름이나 타이틀은 그 본질적인 의미를 가장 잘 함축적으로 표현할것이라 생각해서 단어의 뜻을 찾아보았다. Batch로 검색하니  위키피디아에 많은 링크가 등장했다. 그 중에서 가장 가깝다고 생각하는 것을 하나 꼽았다.

Batch Processing

Batch processing is execution of a series of programs (“jobs“) on a computer without manual intervention.

Batch jobs are set up so they can be run to completion without manual intervention, so all input data is preselected through scripts or command-line parameters. This is in contrast to “online” or interactive programs which prompt the user for such input. A program takes a set of data files as input, processes the data, and produces a set of output data files. This operating environment is termed as “batch processing” because the input data are collected into batches of files and are processed in batches by the program.

간단히 요약하면  배치란 누구의 간섭, 참여 없이 일련의 작업(데이터 입력, 처리, 출력)이 동작하는 것을 말한다. 이와 반대되는 단어는 ‘online’과 ‘interactive programs’가 될 수 있겠다.

거의 모든 애플리케이션에서 배치성 작업은 필요하다. 5분단위로 교통소통 정보를 업데이트하 하거나, 온라인 비디오샵 오늘의 가입자 통계부터 대형 은행의 수천만개 이상의 계좌의 이자를 계산하는 작업을 배치로 처리할 수 있다. 이 작업은 사용자에게 제공하는 서비스에 직접적으로 영향을 미칠 수 있으며 운영에 사용될 수도 있다.

스프링 배치는 특정 도메인에 사용되기위해 만들어진 것이 아닌 범용적으로 사용되기위해 만들어졌다. 1개의 입력 데이터 처리에 1분이 걸려도 관계없거나 혹은 0.01초가 걸려도 문제가 될 수 있는 다양한 배치를 다 고려해서 디자인되었다. 그래서 생각보다 복잡하다. 간단한 작업을 배치로 처리하려는 개발자에게는 배보다 배꼽이 더 큰 비용이 들 수 있다. 그럼에도 불구하고 스프링 배치를 공부해야 하는 이유는 다음과 같다.

  • 컨설팅 회사인 엑센츄어(Accenture)의 참여로 현장의 경험을 기반으로 설계된 프레임웍
  • 명확한 책임의 분리를 통해 SRP의 이점을 볼 수 있음. 확장에 용이
  • Job Repository를 이용해 배치 작업 정보를 persistent 하게 남겨서 유지보수, 관리에 용이
  • (스프링 프로젝트는 늘 그렇듯이) 이쁘게 만들어진 추상화로 특정 기술에 종속적이지 않음

엔터프라이즈급 배치작업을 작성해본 경험이 없는 나로서는 이 정도가 표면적으로 느껴지는 스프랭 배치의 사용이유이다. 더 경험하다 보면 지금은 안 보이는 것도 보일것이다. 지금은 여기까지, 그때 다시 포스팅을 해야겠다.

나가수를 보고

한 때 무도빠였던 내가 식상함을 느끼고 예능을 멀리한지 꽤 됐다. 무도빠였던 시절에는 DMB 기기까지 준비해서 녹화방송인 프로그램을 정규 방송 시간에 챙겨보곤 했었지만, 이제는 집에 있을 경우에도 보지 않게됐다. 장기프로젝트, 감동프로젝트등 다양한 시도를 하긴 했지만 이내 식상함을 느껴버렸기 때문인듯 하다.

이런 내가 최근에 나가수(‘나는 가수다’)를 챙겨보고 있다. 우후죽순 생겨나서 TV 가요 프로그램을 독식하고 있는 아이돌 그룹의 가수들이 아니라 흔히 사람들이 인정하는 가창력 있는 가수들이 나와 더욱 더 노력하는 모습, 그리고 그 결과물이 나의 마음을 감동시켰기 때문이다.

오늘도 7명의 가수들이 나와서 다양성이 극대화된 무대를 준비해서 또 한번 감동을 받고 만족했다. 헌데 방송이 끝나갈 무렵, JK 김동욱이 자진 하차를 한다고 한다. 그를 잘 알지 못하는 청중으로서 새로운 가수를 발견하고 그의 실력에 감탄하고 있었는데 참 안타까운 일이 아닐 수 없다. 물론 그는 무대에서 만족스런 공연을 선보이지 못해 한번 노래를 중단하고 다시 시작한다. 명백한 그의 실수이다. 가수 본인 뿐만 아니라 청중들의 몰입도가 끊겼음에는 틀림이 없다. 그래도 말이 많았던 제작진이 현명하게 대처해서 청중 평가단이 표를 찍기전에 그에대한 사실을 인지시키고 평가에도 참조하라는 전달이 있었다. 그 결과 그는 2등을 하고 이번에 살아남게 돼었다. 하지만 한번의 실수로 그는 자진 하차를 결정했다고 한다.

이번의 재녹화는 일전의 재도전과는 의미가 많이 다르다. 재도전은 청중평가단의 결과를 무시하는 처사였음이 분명했고 제작진과 가수의 독단적인 협의 결과로 이루어진 거였고 재녹화는 청중평가단의 평가가 내려지기 전이었고 가수들의 모든 공연 내용(실수 포함)을 평가한 내용에서 JK 김동욱은 살아남았던 것이다.

하지만 그는 자진하차했다. 왜일까? 오늘 나가수를 보기전에 무대를 평가했던 청중에 의해서 옥주현 및 JK 김동욱이 재녹화를 했다는 사실이 알려졌고 공연을 보지 못한 네티즌들이 다양한 추리를 꺼내놨다. 그리고 이미 재도전이라는 과정을 한번 겪었던 네티즌들은 더욱 과격한 음모론 그리고 까대기를 시작했던것 같다. 이는 JK 김동욱이 하차를 결정하는데 한 몫을 했음이 틀림 없을 것이다.

누군가를 파멸로, 극단적인 선택으로 몰고가기도 하는 대한민국의 네티즌 한명으로서 이러한 사태를 보니 가슴이 아프다. 때론 신상털기라는 네티즌들의 액션에 통쾌함을 느끼기도 하지만(물론 대상이 극악무도, 패륜 등의 인간으로서 해서는 안될 행동을 했던 대상들) 막무가내식의 내가 좋아하는 사람이 아니라서, 내가 좋아하는 행동을 하지 않아서… 등의 이유로 이러한 행동을 일말의 가책없이 하는 행동들… 이러한 네티즌 문화, 행동은 분명 더 성숙해질 필요가 있다.

나를 포함해 네티즌들은 좀 더 이성적으로 사고하고 소신있는 의견을 표출할 필요가 있다.

공부하고 소통하자!

tomcat에서 한글 인코딩 문제 처리 경험 공유

tomcat 6.x 버전을 사용해서 서비스를 진행하고 있다. 인코딩 설정은 URIEncoding=”UTF-8″ 과 request.setCharacterEncoding()으로 설정해서 사용하고 있었다. 이 설정에 대한 내용은 이전에 포스트한 tomcat에서 GET 형태의 파리미터 인코딩 설정에서 설명이 되어있다.

위의 설정으로 한글을 문제없이 쓰고 있다가 갑자기 내가 담당하는 서비스(이제부터 편의상 A)와 B라는 서비스와 연동할 일이 생겼다. 연동 시나리오는 다음과 같다.

시나리오
1. A에서 B로 특정 URL로 페이지 이동을 하면서 콜백URL을 전달한다.
2. B에서 작업을 수행한 후 콜백URL로 리턴하면서 결과 메시지를 GET 형태의 파라미터로 돌려준다.

여기서 문제가 발생했다. 시나리오 2에서 GET 형태의 파라미터를 EUC-KR로 인코딩 해서 넘겨주고 있었다. A는 인코딩이 UTF-8으로 설정되어 있기 때문에 tomcat에서는 EUC-KR로 인코딩된 값을 서버에서 설정한 UTF-8으로 디코딩 하기 때문에 문제가 발생한다.

인코딩-디코딩 횟수는 문제가 되지 않는다. 인코딩한 캐릭터셋으로 디코딩만 짝을 맞춰서 해주면 된다. 하지만 단 한번이라도 인코딩한 캐릭터셋의 값과 다른 값으로 디코딩을 시도하면 더이상 복구할 수 없는 이상한 값으로 나타난다.

tomcat 인코딩 설정이 URIEncoding=”UTF-8″만 되어있으면 위와같은 문제는 해결할 수 없다. 왜냐면 무조건 UTF-8으로 디코딩하려 하기 때문이다. 이 문제에 대해 다음 방법으로 해결했다.

해결
1. useBodyEncodingForURI=”true” 설정 (이 설정은 URIEncoding=”UTF-8″ 보다 우선해서 적용된다.)
2. org.springframework.web.filter.CharacterEncodingFilter을 확장에서 EUC-KR로 인코딩이 필요한 URI에 대해 따로 처리하도록 한다.

처음부터 B서비스에서 인코딩 값을 설정 가능하도록 해주는 것이 맞는거 같은데, 그쪽에서는 절대 안된다고 한다. 확장성에 대한 고려없이 개발이 되어서일까?

tomcat에서 GET 형태의 파리미터 인코딩 설정

톰캣 버전: 6.0

톰캣은 GET 형식의 파라미터를 인코딩하는 방법을 설정에서 제어할 수 있습니다.
가장 많이 사용되는것이 Connector 엘리먼트에 URIEncoding 값 입니다.

<Connector port=”80″ protocol=”HTTP/1.1″
maxThreads=”150″ connectionTimeout=”20000″
redirectPort=”8443″ URIEncoding=”UTF-8″ />

위와 같이 설정되어 있으면 모든 GET 형태의 파라미터를 tomcat안의 서블릿에 던져줄 때 자동으로 URIEncoding에 설정한 케릭터셋으로 URLDecoding을 합니다.

그리고 Connector 엘리먼트에 useBodyEncodingForURI 값이 있습니다.

위와 같이 useBodyEncodingForURI=”true”로 되어 있다면 request.setCharacterEncoding()으로 설정한 케릭터셋으로 인코딩을 하게 됩니다. 만약 해당 리퀘스트에서 request.setCharacterEncoding()을 설정하지 않았다면 URIEncoding에서 설정한 값을 이용합니다.

request.setCharacterEncoding()은 인코딩을 위해 사용하는 org.springframework.web.filter.CharacterEncodingFilter에서 사용하고 있는 방식이다. CharacterEncodingFilter를 사용하면서 url-pattern 을 /*로 한 경우라면 모든 리퀘스트에 대해서 request.setCharacterEncoding()를 사용하고 있는 셈이니, URIEncoding으로 설정된 값은 사용될 경우가 없어진다.

정리하면
1. useBodyEncodingForURI=”false”이거나 설정되지 않고 URIEncodig이 설정되어 있으면 GET 형태의 파라미터는 항상 URIEncoding 값으로 인코딩을 한다.
2. useBodyEncodingForURI=”true” 이면 request.setCharacterEncoding()로 설정한 값으로 인코딩한다. request.setCharacterEncoding()으로 값을 설정하지 않은 경우면 URIEncoding 값으로 인코딩 한다.

자바스크립트로 event trigger 하기

자바스크립트에서 어떤 동작을 조작하기 위해서 event발생하면 이를 제어하는 handler를 등록한다.

이러한 handler는 이벤트가 발생할 경우 동작을 하는데, 자바스크립트에서 인위적으로 이벤트를 생성해서 이를 트리거 시킬 수 있는 방법이 있다. 원문은 다음을 참조하자.
[js]
function fireEvent(element,event){
if (document.createEventObject){
// dispatch for IE
var evt = document.createEventObject();
return element.fireEvent(‘on’+event,evt)
}
else{
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true ); // event type,bubbling,cancelable
return !element.dispatchEvent(evt);
}
}
[/js]
위와 같은 방식으로 원하는 엘리먼트에 원하는 이벤트를 트리거할 수 있다. 원문에서는 특정 엘리먼트의 이벤트 핸들러 실행이 잘 안될경우의 해결 방식으로 이용하고 있다. 하지만 나와 같은 경우에는 특정 사이트의 예약 시스템을 위한 매크로로 사용했다. 이벤트 핸들러의 파라미터가 단순 이벤트 정보가 아니고 동적으로 결정되는 값일 경우, 해당 파라미터의 올바른 값을 유추하기 어렵다. 이 경우 그냥 해당 이벤트를 발생하게 해서 처리하는 것으로 해결봤다.

공공기관의 예약 시스템은 취약점이 많다. 아침 9시에 예약 시스템이 오픈하는데 9시 1분이면 모든 예약 완료. 이건 나 말고도 무언가 매크로성 프로그램을 돌리고 있다는 것. 흠…