jackcoke-blog
jackcoke-blog
jack and coke, please
12 posts
Don't wanna be here? Send us removal request.
jackcoke-blog · 13 years ago
Text
Tomcat6에서 Error listenerStart 로그 확인
spring  프레임워크를 쓰는 프로그램을 tomcat6에서 돌리는데 프로그램을 수정하고나서 tomcat을 재시작했는데 tomcat은 시작했는데 프로그램이 올라가지 않았다.
catalina.out 파일을 열여서 로그를 확인하니 listener 시작하면서 에러가 나면서 프로그램이 시작되지 않았다.
2012. 3. 21 오후 10:26:45 org.apache.catalina.core.StandardContext start 심각: Error listenerStart 2012. 3. 21 오후 10:26:45 org.apache.catalina.core.StandardContext start 심각: Context [] startup failed due to previous errors
그런데 더 자세한 로그가 없어서 정확히 무슨 이유로 확인할 수가 없었다. 구글링을 하고 알아냈다.
[context_path]/WEB-INF/classes/logging.properties 파일을 만들고 아래의 내용을 저장한다.
org.apache.catalina.core.ContainerBase.[Catalina].level = ERROR org.apache.catalina.core.ContainerBase.[Catalina].handlers = java.util.logging.ConsoleHandler
그 후에 tomcat을 재시작하면 catalina.out 파일에 listener가 시작하면서 에러의 stacktrace를 볼 수 있다.
0 notes
jackcoke-blog · 13 years ago
Text
<구글 개발자가 들려주는 HTML5 활용>을 읽고 HTML5 정리
HTML5는 전혀 새로운 건 아니고 기존 버전의 연장 선상이다. 새로 추가된 기능은 캔버스, 비디오, 위치정보, 로컬 저장소, 오프라인 웹 등이다. 글을 쓰면서 '주요 기능'이라고 했다가 '새로 추가된 기능'으로 바꿨다. 말 그대로 새로 추가됐을 뿐 꼭 써야 하거나 기능이 좋다는 건 아니다.
예로 캔버스는 게임 말고 어디다 쓸지 모르겠다. 오이카키가 html5 버전으로 나오려나?
비디오는 웹브라우저마다 코덱 지원이 달라서 실제로 사용하기가 복잡해 보인다. 책에서 처음 소개는 <video src="..." /> 식으로 한 줄로 나오다가 웹브라우저 마다의 지원하는 코덱의 차이와 비디오 태그를 지원하지 않는 웹브라우저를 위해서 플래쉬 플레이어를 embed 태그로 넣으면서 끝난다. 1줄짜리 코드가 최종코드에는 16줄짜리 코드가 되었다. 원본 비디오 파일을 3가지 코덱으로 인코딩하는 과정도 거쳐야 한다. 이럴 거면 유튜브에 올리고 embed 시키는 게 낫지 않을까.
이 책을 쓸 시점에 아이패드에는 첫 번째로 적어 놓은 비디오만 감지하는 버그가 있었다네. 아이페드에서도 HTML5 비디오를 제대로 재생시키려면 MP4 파일을 가장 먼저 적어야하고 다른 비디오 포맷은 나중에 적어야 한다는 것을 의미하지. 아...
마지막의 '아...'의 줄임표에는 탄식 섞인 욕이 감춰졌을 거 같다.
위치정보, 로컬 저장소, 오프라인 웹 기능은 모바일 어플을 따로 만들지 않고, 모바일 웹으로 대체 가능할 거 같다. 정보 위주의 앱이나 인터넷 기능이 별로 안 쓰이는 앱(예: todo list, note)등은 충분할 거 같다. 웹브라우저의 지원 여부도 PC 환경보다 단순하고, 앱의 모바일 기기 호환성에 비할 바가 아니겠지.
0 notes
jackcoke-blog · 14 years ago
Text
<스케일러블 웹사이트 구축>
 회사에서 진행한 프로젝트 중에 합격 여부를 알려주는 기능이 포함된 사이트가 있었다. 합격 발표 기간이 되었을 때 사용자가 몰려서 웹서버에 접속이 안 되는 일이 발생했다. 문제는 apache httpd의 MPM(Multi-Processing Module)을 prefork에서 worker로 바꾼 거였다. worerk로 작동할 때에는 동시 접속자가 많아지면 처리를 못 하고 에러 페이지(지금은 몇 번 에러였는지 기억이 안 난다)만 보여줬다. 다시 prefork로 돌려서 해결했는데 문제의 원인을 찾으면서 iBatis 캐쉬, mysql 모니터링에 대해 좀 더 깊게 알게 됐다.
 그리고 이번에 사용자가 많을 수 있는 사이트를 만들면서 성능을 염두에 둬야 했고, 관련 책을 찾아서 공부하기로 했다. 그래서 산 책이 <스케일러블 웹사이트 구축>이다. 다 읽고 난 뒤의 느낌은 읽기를 정말 잘했다는 것과 더 일찍 읽었으면 삽질하며 얻은 경험들은 쉽게 얻을 수 있었을 텐데 라는 안타까움이었다. 작가가 flickr 개발자라서 경험에서 나온 글들이 읽기 재미있었는데 통신회사들이 (표준을 안 지키고 제멋대로)  메일을 보내는 방식과 처리하는 노하우에 대한 부분은 읽으면서 동병상련을 느꼈달까.
 개인적으로 컴퓨터 서적들이 이론적이거나 너무나 기초적인 예제들만 있어서 잘 읽지 않는 편인데 이 책은 실제 경험들이 있어서 내 취향에 딱 맞었다. mysql의 index나 table engine의 종류에 대한 부분은 바로 적용할 정도로 실용적이었다. top, iostat의 결과에 대한 설명도 유용했다.
 꼭 큰 사이트를 개발하는 개발자가 아니어도 신입 개발자들에게도 유용할 장들이 있다. 유니코드, SQL injection, HTTP protocol에 대한 부분은 웹 개발자라면 꼭 알아야 한다고 생각한다. telnet으로 80번 포트에 접속해서 파일 업로드는 못 해도 페이지는 읽어올 수 있어야 하지 않을까.
 소스코드가 너무 많지도 적지도 않고, (그놈의!) hello world, 피보나치도 없어서 오래간만에 재미있게 컴퓨터책을 읽었다.
작동되는 코드를 빨리 확보할수록 디자인의 문제점을 빨리 파악할 수 있으며 완전히 새로 다시 작업을 하게 되더라도 낭비하는 시간이 적어진다. - p 6
대규모 웹 애플리케이션을 개발할 때 성공적인 팀들에게서 꾸준하게 발겨되는 세 가지 규칙이 있다. <중략>
소스 컨트롤 사용
한 방 빌드
버그 관리 -p 36
미���한 관찰자에게는 CPU가 모든 애플리케이션의 주 병목 지점으로 비춰질 것이다. 하지만 실무에서는 CPU가 문제가 되는 경우는 거의 없다. -p 207
경험적 법칙에 의하면 필요 이상으로 많은 통계 수치를 추적하는 편이 좋다.  여분의 통계치를 무시하는 것은 쉽지만 필요한 통계 수치가 없이 감시하고 추적하는 것은 힘들다. -p 349
책 링크(yes24)
0 notes
jackcoke-blog · 14 years ago
Text
webtob + jeus에서 DWR 사용
최근에 하는 모바일웹 프로젝트에서 DWR를 사용했다. 개발자들 컴퓨터에서는 작동하는 기능이 서버에서 400에러를 내면서 작동하지 않았다. 문제를 찾으려고 크롬의 자바스크립트 콘솔을 열어두고 보니 DWR javascript를 호출하면 DWR 서블릿에 접속하게 되어 있는데 이 서블릿을 호출하는 URL이 .dwr로 끝났다. 빙고! 서버는 webtob(http server)와 jeus(WAS server)를 쓰고 있고, webtob에서 jeus로 넘기는 부분에서 확장자를 기준으로 넘기게 돼 있다. jsp와 우리가 만든 서블릿(Spring MVC)에서 쓰는 m은 jeus로 넘기게 설정이 되어 있었지만 dwr 확장자는 webtob에서 처리하려다가 에러가 나는 상황이었다. tmax(webtob, jeus의 제작사) 담당자에게 dwr 확장자도 jeus로 넘기게끔 해달라고 요청하는 힘들고 언제 끝날지 모르는 방법 대신에 DWR 소스를 고쳐보기로 했다. javascript에서 DWR 서블릿을 호출할 때 확장자를 dwr 대신 jsp나 m으로 바꿀 수만 있다면 문제가 쉽게 해결될 거 같았다. 소스를 열어보니 다행히 engine.js 파일만 수정해서 가능했다. DWR jar 파일을 풀어서 engine.js에서 .dwr로 된 부분을 .m으로 고쳤더니 서버에서도 이제 제대로 작동이 되었다. 글을 읽으면서 tmax에 요청하면 굳이 DWR 소스를 열어볼 필요가 없다고 생각하는 사람들이 있을지도 모르겠다. 그러나 tmax의 제품을 쓰는 프로젝트를 몇 번 한 경험에 의하면 요청 사항이 제대로 처리되기까지는 초보로 보이는 담당자가 몇 번의 실수를 한 다음에야 다른 담당자에 의해서 처리된다. 그러니 속 편하게 DWR를 직접 고친 것이다. 만약 Weblogic이나 Tomcat이 아니라 tmax의 제품을 프로젝트에 써야 한다면 서버 세팅 일정에 (최소!) 일주일은 더 추가하기를 추천한다.
0 notes
jackcoke-blog · 14 years ago
Text
안드로이드 마켓 경험기
필요한 어플리케이션을 만들고, 마켓에 올려서 다른 이들이 쓰는 것과 그리고 그들의 평가를 받는 건 재미있는 경험이다.
두 달 전쯤 한 어플리케이션을 만들기 시작했고, 그로부터 한 달 뒤에 마켓에 올렸다. 그리고 또 한 달이 지난 지금, 그간에 있었던 일들을 정리해보겠다.
2010년 겨울 - 이즈음 받기 싫은 전화가 많았다. 그래서 휴대폰 주소록에 저장되어 있지 않은 전화는 자동으로 차단하는 기능이 있으면 좋지 않을까 생각했다.
1월 1일 - 새해 계획을 세우면서 작년에 못한 안드로이드 마켓에 어플리케이션 올리기를 해보기로 했고, 1월 안에는 올리기로 결심했다.
1월 5일 - 작년에 생각하던 어플리케이션의 기본 기능을 하루 만에 완성했다. 워낙 간단해서 가능한 일이었다. 우선 전화가 오는 걸 감지하고, 걸려온 번호가 주소록에 있는지 확인하고, 없으면 전화를 끊기만 하면 됐다. 이름은 BAA로 정했다. 영화 <소셜네트워크>의 끝 부분에 변호사가 주인공에게 하는 대사("You're not an asshole, Mark. You're just trying so hard to be one.")를 듣고 정한 것이다. BAA의 약자는 “Being an asshole”이다. (요즘도 많이 다르진 않지만) 그즈음의 내 상태였고 만든 어플리케이션은 그런 상태에서 필요한 기능이었다.
개발자를 위해서 각 작동에 쓰인 방법들은 간략하게 설명하겠다.
전화가 걸려오는 걸 감지하기 위해서는 android.content.BroadcastReceiver 를 상속하고 onReveive를 구현하면 된다. 그렇게 만든 클래스를 AndroidManifest.xml 파일에 receiver로 추가한다. (receiver는 application 안으로 넣는다.)
<receiver android:name=".CallReceiver"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE" /> </intent-filter> </receiver>
전화를 끊는 방법은 내부 API를 사용해야 했지만 특별히 어려운 건 아니었다. com.android.internal.telephony.ITelephony 인터페이스의 endCall() 메소드를 이용하면 됐다. ITelephony 인터페이스를 얻는 코드는 아래와 같은 식으로 한다.
ITelephony 인터페이스를 얻는 코드는 아래와 같은 식으로 한다. TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); Class c = Class.forName(tm.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); ITelephony telephonyService = (ITelephony) m.invoke(tm);
이 방법들을 조합해서 기본 기능을 만들었고, eclipse adt를 이용해서 에뮬레이터에 전화를 걸어보며 테스트했다.
그리고 이날 마켓에 어플리케이션을 올릴 수 있게 개발자 등록을 했다. 개발자 등록은 이름이나 이메일 같은 정보를 입력하고, 구글 체크아웃을 이용해서 25달러를 지불하는 과정을 거친다. 체크카드로도 가능한데 은행에 전화해서 국외에서도 체크카드가 사용 가능하도록 별도의 요청을 ��야 했다. 체크아웃에서 지불정보를 입력할 때 주소도 요청하는데 한글을 그대로 입력해도 가능하다. 사실 뭔가를 배송받는 건 아니니까. 그래서 결제가 끝나고 바로 마켓에 어플리케이션을 올릴 수 있다. 하지만 아직 여러 가지로 준비가 안 되었기 때문에 바로 올리지는 않았다.
1월 11일 - 오후에 카드 결제 내역이 문자로 왔고 실제로 통장에서 돈이 나갔다. 28,972원이었다.
이때쯤에는 차단 기능을 켜고 끌 수 있는 설정 화면도 추가했고, 차단 시간 기능도 구현하고 있었다. 간단한 기능이었지만 마켓에 올려진 비슷한 프로그램들에서는 없는 기능이었다. 낮에는 회사 일로 모르는 번호로 오는 경우에도 받아야 했지만 퇴근 후에는 그러지 않아도 됐기에 개인적으로 필요한 기능이었다.
그리고 회사 동료들에게 만들고 있는 어플리케이션을 소개했지만 기능의 무자비함(세상에, 저장 안 된 전화를 다 끊다니!)에 놀라고 누가 그런 기능을 쓰겠냐고 말했다. 하지만 분명 누군가는 쓸 거 같았고, 이 나라에는 없어도 세계적으로 보면 그래도 몇 명은 있지 않겠느냐는 생각이었다. 그래서 다국어 기능으로 영어와 한국어를 지원하기로 했다.
1월 31일 - 쓰고 있는 옵티머스Q 이외에도 다른 폰들에서도 테스트했다. 그렇지만 영어 번역과 아이콘 제작 때문에 마켓에 올리는 걸 차일피일 지내다가 1월 1일에 세운 계획을 지키려고 부랴부랴 급하게 마무리 지었다. 아이콘은 회사 디자이너에게 부탁해서 급조했고, 영어는 대충대충 적었다. 마켓에 올릴 때 배포 지역을 선택할 수 있는데 애초 생각대로 한국만 하지 않고 전세계로 했다.
3월 1일 - 마켓에 올린 지 한 달이 지났다. 개발자는 마켓 개발자 사이트에서 마켓에 올린 어플리케이션의 다운로드 수나 에러를 볼 수 있다.
Tumblr media
그림에서 보듯이 한 달간 495건의 다운로드가 있었고, 178명이 사용 중이다. 에러의 stack traces를 볼 수 있는데 오류가 났을 때 디버깅에 도움이 된다. BAA의 경우는 걸려온 번호가 null일 수도 있어서 주소록 번호와 비교할 때 에러가 났다.
Tumblr media
사용자들의 평가도 볼 수 있는데 가장 최근에 올라온 러시아 사람의 평가가 내가 예상했던 반응이라 좋았다. 전 세계적으로 봤을 때 분명히 쓰는 사람이 있을 거라는 점과 차단 시간 기능이 유용할 거라는 점을 둘 다 만족했다. (물론 러시아어를 할 줄 모르고 구글 번역 사이트를 이용했다. 처음에는 저 말이 러시아어인줄도 몰랐다.)
Tumblr media
그림에는 안 나왔지만 처음 평가는 아이콘을 수정해다라는 거였는데 디자이너에게 보여줬더니 창피하다며 다시 만들어주겠다고 약속했다. ;)
이렇게 해서 오늘까지의 일어난 일을 생각나는 대로 적었다. 이 기록이 시작하는 안드로이드 개발자에게 조금이라도 도움이 되면 좋겠다.
BAA market link
0 notes
jackcoke-blog · 14 years ago
Text
Internal Storage - 파일 저장하기
지난번 글에서 Shared Preferences를 이용해서 key-value 환경설정을 저장하는 법을 알아봤다. 이번에는 안드로이드의 어플리케이션 데이터를 저장하는 방법 중에 파일을 저장하는 방법을 알아봤다.
Internal Storage는 기본적으로 다은 어플리케이션에서는 접근할 수 없는 공간에 파일을 저장하고 읽을 수 있게 한다. 
openFileOutput(), openFileInput() 메소드로 각각 파일을 쓰고, 읽을 수 있는 Stream을 리턴한다.
예를 들어서 인터넷에서 파일을 다운로드해서 저장하려면 아래처럼 한다.
url = new URL("http://..."); HttpURLConnection conn= (HttpURLConnection) url.openConnection(); conn.connect(); InputStream is = conn.getInputStream(); byte[] data = readBytes(is); //readBytes는 적당히 구현 ;) FileOutputStream fos = openFileOutput("filename", Context.MODE_PRIVATE); fos.write(data); fos.close(); conn.disconnect();
참고로 안드로이드에서 어플리케이션 데이터를 저장하는 방법은 여러가지가 있는데 자세한 내용은 안드로이드 개발자 사이트의 Data Storage 페이지를 참고한다.
0 notes
jackcoke-blog · 14 years ago
Text
jpeg dpi 변경
jpeg 인코더 파라미터에 X,YDensity를 세팅하면 된다.
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img); param.setDensityUnit(JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH); param.setXDensity(96); param.setYDensity(96);
쉽다. 그런데 쓸모가 있을까 싶네.
0 notes
jackcoke-blog · 14 years ago
Text
tiff 포멧 jpeg로 변환
tiff 디코더로 읽고, jpeg 인코더로 쓰면 되지.
private static void tiffToJpg(String tifFileName, String jpgFileName) throws IOException { SeekableStream s = new FileSeekableStream(new File(tifFileName)); ImageDecoder dec = ImageCodec.createImageDecoder("tiff", s, null); RenderedImage op = dec.decodeAsRenderedImage(0); BufferedImage bi = convertRenderedImage(op); FileOutputStream fos = new FileOutputStream(jpgFileName); JPEGImageEncoder jpeg = JPEGCodec.createJPEGEncoder(fos); JPEGEncodeParam param = jpeg.getDefaultJPEGEncodeParam(bi); param.setQuality(1f, true); jpeg.setJPEGEncodeParam(param); jpeg.encode(bi); fos.close(); s.close(); }
디코더로 읽은 데이터가 RenderedImage인데 인코더 파라미터를 지정하려면 BufferedImage로 바꿔야 한다. 바꾸는 함수는 구글링으로 여기서 찾았다.
0 notes
jackcoke-blog · 14 years ago
Text
웹프로그램 에러 검사 항목 - 실제 예제 iBatis
iBatis(MyBatis)를 사용하고 Pool을 사용하는 경우에 최대 연결 수와 최대 대기 연결 수를 지정했는지 확인한다. SqlMapConfig 파일에서 <dataSource> 항목에 Pool.MaximumActiveConnections, Pool.MaximumIdleConnections 속성이 있어야 한다.
기본 Pool.MaximumActiveConnections의 수는 10인데 연결이 많고, (쿼리나 네트워크의 영향으로) 연결 시간이 오래 걸릴 때에는 이 수를 늘려준다.
<property name="Pool.MaximumActiveConnections" value="100" /> <property name="Pool.MaximumIdleConnections" value="50" />
DB를 해당 프로그램에서만 사용한다면 연결 수를 DB의 최대 연결 수와 근접하게 해도 된다. ('근접하게'라고 한 이유는 DB 자체에서 쓰는 연결이나 DB Tool 연결도 있기 때문이다.)
Oracle에서 최대 연결 수를 보려면 ORACLE_HOME/dbs/init<sid>.ora 파일 내용에서 processes 값을 확인한다.
1 note · View note
jackcoke-blog · 15 years ago
Text
웹프로그램 에러 검사 항목
웹프로그램이 에러 페이지같이 겉으로 드러난 프로그램 에러가 아닌 다른 이유로 접속이 불량하거나 아예 안 되는 경우 아래의 항목들을 확인해본다.
방화벽 외부에서 프로그램이 사용하는 포트가 열려 있는지 확인한다.
log file WAS, Tomcat, 프로그램(log4 등) 로그 파일을 확인한다. 로그 파일에 적힌 메시지를 보는 것도 중요하고, 로그 파일 자체도 중요하다. 너무 많은 로그가 쌓이고 있는 건 아닌지, 날짜별, 용량 제한 등으로 로그파일을 나누고 있는지 확인한다.
디스크 사용량 디스크 용량이 가득 차 있는지 확인한다.
메모리 사용량 시스템의 메모리 사용량이 너무 많은지, 적은지 확인한다. 너무 많은 경우 프로그램에서 메모리가 새는 곳은 없는지 확인한다. 너무 적은 경우 프로그램에 메모리 최대사용량이 적게 제한되어 있는지 확인한다.
DB Connection 정적인 컨텐츠(DB 접근이 없는 사이트맵, 이미지)는 접속이 원활하고, 동적인 페이지(DB에 접근하는 게시판 등)는 접속이 느리거나 안 된다면 프로그램에서 DB에 연결했다가 처리를 완료하고 연결을 끊지 않는지 확인한다.
0 notes
jackcoke-blog · 15 years ago
Text
환경설정 읽기/쓰기
//쓰기 SharedPreferences sp = getSharedPreferences("myPref", Activity.MODE_PRIVATE); SharedPreferences.Editor spe = sp.edit(); spe.putBoolean("booleanVar", true); spe.commit(); //읽기 SharedPreferences sp = context.getSharedPreferences("myPref", Activity.MODE_PRIVATE); boolean enable = sp.getBoolean("booleanVar", false);
앱에서 간단한 정보(환경설정 정보 등)을 저장할 때 SharedPreferences를 쓴다. put[Type], get[Type] 메소드가 있다. Boolean 외에 Float, Int, Long, String이 있다.
쓸 때는 put[Type]()을 하고 commit()을 호출해야 저장한다.
읽을 때는 get[Type]()의 두 번째 인자로 값이 없을 때 리턴할 기본 값을 지정한다.
0 notes
jackcoke-blog · 15 years ago
Text
벨/진동 모드 변경
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); int ringerMode = am.getRingerMode(); //모드 가져오기 am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); //모드 변경하기
AudioManager를 가져와서 get,setRingerMode를 사용하면 된다. 모드 파라미터 값은 RINGER_MODE_NORMAL, RINGER_MODE_SILENT, RINGER_MODE_VIBRATE 이다.
0 notes