2010년 5월 24일 월요일

프로젝트가 거진 마무리 단계다..

필요한 기능은 다 구현했다..

사실상 개발시간만 따지면 20시간 정도 걸렸고

책보는 시간이 더 많았던 것 같다.

역시.. 책으로 다 읽고 지나갔던 것들은 혼자 직접 응용해서

만들어 보지 않으면 머리에서 금방 잊혀진다는 걸 깨달았다

프로페셔널 안드로이드 어플리케이션 개발은 번역은 그닥이나 수많은 예제들은

엄청난 도움이 되었으며 알짜만 골라배우는 안드로이드 프로그래밍 서적은

기초 개념과 간단한 화면을 만들때는 더 없이 좋은 책이었다.

교육받는거보다 책이 낫다 ㅡ.ㅡ;


요즘 받고 있는 안드로이드 교육이 도움이 많이 될 줄 알았는데..정말 실망스럽다.

직장인 교육은 대부분의 강사님들이 (죄송하지만) 수업 준비를 많이 안해 오시는듯..

여튼 앞으로 직장인 교육은 여러번 살펴보고 참여 해야 할듯 하고..


프로젝트는 이미지만 이제 만들어서

정식으루 배포 하면 될 정도로

완성 되었으나

안드로이드 에뮬에서만 테스트하고 돌려봐서..

정확하게 동작 할지는 모르겠다..

수정 할것도 있고

앞으로 더 넣고 싶은 기능도 많지만

조급해 하지말고.. 천천히 공부 하자

오픈은 언제 할까나..





2010년 5월 19일 수요일

Android 프로젝트 시작..

역시나.....
완성 날짜에 비해 늦어졌다..
공부만 했다 ㅡ,.ㅡ;;;

막막하기도 하고 ....
그래도 마음속으로 기획과 관련 자료들 공부는 완료 되었다고 생각한다.

어플의 이름은 Where you at

위치 확인 및 연락 서비스..


이 어플은 자기의 위치를 서비스로 서버에 송신한다.

서버에 커넥팅 하면 등록된 사용자들의 위치를

구글 맵에 오버레이해서 보여준다.

오버레이된 아이콘을 클릭하면 전화, 문자, email 작성을 할수 있다.


서버는 구글 앱엔진으로 하기로 결정했다.

우선 구글 앱엔진으로 위치들을 저장 할 서버부터 제작!



2010년 4월 11일 일요일

알짜만 골라 배우는 안드로이드 프로그래밍

 

알짜만 골라 배우는 안드로이드 프로그래밍

 

Beginning Android

 

알짜만 골라배우는 안드로이드 프로그래밍

마크머피 저/ 강철구 역

에이콘 출판사

 

 

지금 50% 정도 읽고 있는 책인데..

너무 좋다.

세세한 설명과 예제들.....

 

안드로이드라면 이 책을 강추 해야 겠다!

 

 

다  읽고 보자..

구글의 안드로이드 프로그래밍

안드로이드 프로그래밍

 김정훈 지음/ 성안당

 

대학교재로 쓰이는 듯한 책인데

 

너무너무 쉽게 잘만들어져있어서 감탄을 금 할 수가 없었다.

 

하루 3시간씩만 봐도 5일이면 다볼 책이다.

 

책은 어려우면 안된다 나는 쉽고 간단명료하고 쉽게 따라 해볼수 있는 책이 너무너무 좋다

 

대학교재가 이렇게 쉽고 간단하게 되어있으니.. 학생들은 참 좋겠다 ㅡ,ㅡ

 

다소 빠진 부분이 많아 아쉽긴 하지만

 

간단히 안드로이드를 쉽게 접해 보기엔

 

더할나위 없는 책이다.

실용주의 프로그래머 (The Pragmatic Programmer)

실용주의 프로그래머 

데이비드 토머스 / 앤드류 헌트  지음
김창준 정지호 옮김
인사이트

 

어느날 문득, 기술서적만 보고 있어서는 안되겠다는 생각이 들어서

뭔가 깨우침이나 고급 개발자로 갈 수 있는 개발패턴이나 습관 같은 것들을 깨닫고자

양장본으로 구입했다.

 

뭐랄까... 개발자로써의 마음가짐이 특히 와닿았는데

난 좀 실용주의적인 측면이 있는 것 같다 .

 

특히 왜? ㅋㅋ 라는 늘 궁금증, ㅋㅋ

 

좋은 책

하이버네이트 프로그래밍(HARNESSING HIBERNATE)

제임스 엘리어트 저/ 정미영 역/ 한빛미디어

 

 

이책의 영어 원서가 얼마나 좋은지는 모르겠지만

하이버네이트를 첫 공부 하고 싶다면

이책은 절대로 읽지 않았으면 좋겠다.

 

진짜 이해 안되는 번역,

책의 내용은  Ant 와 Maven  설명이 반정도 먹고 있는 듯 하다.

 

국내에선 그다지 인기 없는 하이버네이트,

더 인기 없는 Ant

알지도 못하는 Maven 까지..

 

낯섬의 연속에

이해 안되는 설명까지 보고 있자니 화가 치밀어 오른다 ㅡ.ㅡ;

 

하이버네이트3 프로그래밍

 

최범균 / 가메 출판사

 

하이버네이트의 책으로는 이만한 책이 있을까? ( 책도 없지만-_-)

 

최고의 책이다. 최범균님의 책은 날 언제나 생각하고 고민하고 공부하게 만들어준다.

 

어려우면서도 탁 이해해주는 설명과 짤막짤막한 코드들은 늘 즐겁다.!

 

예제로 시작하는 안드로이드 개발

릭 로저스 저/ 안드로이드펍 역 / 에이콘 출판사
 
 
난감한 책이다..
좋지도, 나쁘지도 않은 번역,
 
분명히 안드로이드 초보 서적인데도,
실행도 안되는 자기 프로젝트를 가지고 설명하며 어렵사리 프로젝트를 실행 시켜본다고 한들
이런 깊이도 없고 세세하지도 않은 설명들로는 (그것도 번역서를)
공부하기가 꽤나 힘들다.
 
절대 추천하지 않는 책이다.
 
그래도 나름대로의 장점이라고 한다면, 디버깅 부분은 좋다.
 

스프링 2.5 프로그래밍



최범균 / 가메 출판사

최범균님의 책은 언제나 좋다.
최범균님의 책이 거진 그렇듯 다양한 API의 소개는
언제나 찾아보는 레퍼런스식의 활용도가 높으며
전체적으로 한번 읽어 보는 것도 좋은 공부가 된다.

다양한 스프링 클래스들의 소개.. 조금은 깊이 있는 내용..

두고두고 봐야 할 책이당..

Spring 2.5 실무 프로그래밍

 
SPRING 2.5실무프로그래밍   
성윤정 / 삼양 미디어

 

성윤정씨의 책이 그렇듯, 초보들을 상대로 하는 무지막지하게 쉬운 책이긴 하나

당최 이해가 안되는 프로젝트의 구조 쓸데 없이 자잘하게 나눠둔 프로젝트 설계등은

내 실력이 미천한지 이해가 되지 않는다.

 

스프링이 어렵고 힘들게 느껴진다면 한번 쭈욱 실습 해 볼만 한 책.

여전히 두깨는 쓸데 없으며, 같은 소스의 반복으로 30%는 잡아 먹는다.

 

 

나의 첫번째 안드로이드 프로젝트

개인적인 안드로이드 공부겸

 

우리 회사를 위해 개인적인 프로젝트를 기획

 

Project Name : Where you at

 

Contents : 간단하다. 어플리케이션을 실행 시키면 등록된 사원들의 위치정보를 보여준다.

                파견을 주로 나가는 업체인 만큼 사원들의 위치정보는 서로에게 요긴하게 쓰일 듯

 

 

 

5월 15일 이전에 완성하는 것으로 한다.

 

 

2010년 3월 9일 화요일

FCKEditor 이미지 업로드 기능 구현하기

FCKEditor를 사용하는 Java 환경에서 이미지 업로드를 구현 하기 위해서는
FCKEditor Java 버전을 다운받아서 사용해야 한다.

WAS가 톰캣인 경우라면 별다른 수정없이 웹에 널리 알려진 방법대로 사용이 가능하나,
업로드 쪽을 직접 뜯어 고치고 싶다던지, 톰캣 환경이 아니라면 소스의 수정은 불가피하다.


우리가 지금 진행중인 프로젝트 역시 조금은 다른 환경에서 가동되므로,
FCKEditor 를 약간 수정 하되, FCKEditor에서 제공하는 디자인과 틀을 그대로 가져가면서
업로드 처리 부분만 제작했다.
다시 간단하게 말해서, FCKEditor 에서 요구하는 양식에 맞춰 주기만 하면 된다.

서버측 파일 업로드 프로그램이야 알아서 간단하게 개발 가능할 테지만
이를 FCKEditor 이미지 업로드 폼에서 작동 되게 하는건 조금의 설정이 필요!.

fckeditor 폴더의 editor -> dialog 폴더와 그아래 fck_image 폴더에 우리가 찾는 이미지 업로드 다이얼로그 html 폼과 js 파일이 존재 한다. (이는 FCKEditor 2.6.6 기준이다)

fck_image.js 파일을 열고
window.onload 이벤트에서 아래와 같은 부분을 찾아서 직접 만든 업로드 처리 서블릿을 명시한다.

    // Set the actual uploader URL.
    if ( FCKConfig.ImageUpload )
        GetE('frmUpload').action = "니업로드처리서블릿주소";

    dialog.SetAutoSize( true ) ;


여기 주소만 수정해줘도 거의 모든 설정은 끝난 것과 마찬가지
(이부분에서 설정이 중요하다. html 파일의 upload form 영역의 action 주소는 공백으로 놔둘것)

아래로 더 살펴보면 아래와 같은 두가지 메소드를 찾을 수 있다.


function OnUploadCompleted( errorNumber, fileUrl, fileName, customMsg )
{
    // Remove animation
    window.parent.Throbber.Hide() ;
    GetE( 'divUpload' ).style.display  = '' ;

    switch ( errorNumber )
    {
        case 0 :    // No errors
            alert( 'Your file has been successfully uploaded' ) ;
            break ;
        case 1 :    // Custom error
            alert( customMsg ) ;
            return ;
        case 101 :    // Custom warning
            alert( customMsg ) ;
            break ;
        case 201 :
            alert( 'A file with the same name is already available. The uploaded file has been renamed to "' + fileName + '"' ) ;
            break ;
        case 202 :
            alert( 'Invalid file type' ) ;
            return ;
        case 203 :
            alert( "Security error. You probably don't have enough permissions to upload. Please check your server." ) ;
            return ;
        case 500 :
            alert( 'The connector is disabled' ) ;
            break ;
        default :
            alert( 'Error on file upload. Error number: ' + errorNumber ) ;
            return ;
    }

    sActualBrowser = '' ;
    SetUrl( fileUrl ) ;
    GetE('frmUpload').reset() ;
}

var oUploadAllowedExtRegex    = new RegExp( FCKConfig.ImageUploadAllowedExtensions, 'i' ) ;
var oUploadDeniedExtRegex    = new RegExp( FCKConfig.ImageUploadDeniedExtensions, 'i' ) ;

function CheckUpload()
{
    var sFile = GetE('txtUploadFile').value ;
    if ( sFile.length == 0 )
    {
        alert( 'Please select a file to upload' ) ;
        return false ;
    }

    if ( ( FCKConfig.ImageUploadAllowedExtensions.length > 0 && !oUploadAllowedExtRegex.test( sFile ) ) ||
        ( FCKConfig.ImageUploadDeniedExtensions.length > 0 && oUploadDeniedExtRegex.test( sFile ) ) )
    {
        OnUploadCompleted( 202 ) ;
        return false ;
    }

    // Show animation
    window.parent.Throbber.Show( 100 ) ;
    GetE( 'divUpload' ).style.display  = 'none' ;

    return true ;
}


메소드 명만 봐도 아! 우리는 저것이 뭘 하는 이벤트겠구나 하는 감을 잡을 수 있다.
폼이 전송 되기전 onSubmit 이벤트 처리 메소드이며 하나는 전송이 완료되어 호출 되는 callback 메소드이다.

이 두가지 메소드는 뜯어 고칠 일이 전혀 없다.

파일 업로드를 처리하는 서블릿에서 파일 전송이 끝났다면
직접 OnUploadCompleted 메소드만 호출 해 주면 끝난다.

ex: 업로드를 처리하는 서블릿에서 스크립트 메소드 호출
    if(!item.isFormField() && item.getSize()>0) {
                       
                        String fieldName = item.getFieldName();
                        String fileName = item.getName();
                        String contentType = item.getContentType();
                        boolean isInMemory = item.isInMemory();
                        long sizeInBytes = item.getSize();
                       
                        File uploadedFile = new File(savePath,fileName);
                        try {
                            item.write(uploadedFile);
                            item.delete();
                           
                            String retVal="0";
                            String newName="";
                            String fileUrl= "http://"+request.getServletRequest().getServerName()+":"+request.getServletRequest().getServerPort()+"/common/uploads/"+fileName;
                            String errorMessage="";
                       
                            HttpServletResponse res = request.getServletResponse(true);
                            PrintWriter out = res.getWriter();
                            out.println("<script type=\"text/javascript\">");
                            out.println(" window.parent.OnUploadCompleted("+retVal+",'"+fileUrl+"','"+newName+"','"+errorMessage+"');");
                            out.println("</script>");
                            out.flush();
                            out.close();



끝...

혹시라도 위의 작업을 완료하고 이미지를 업로드 하는데 만약,
액세스 거부 에러 또는 권한 없다는 스크립트 에러가 나면 본문과 이 서블릿에서
document.domain 을 서로 맞춰주면 된다.


2010년 3월 8일 월요일

IE8에서 FCKEditor 액세스 거부 (Access Denied) 문제 해결하기


중요한 사항이라 기록 해 둠.
(전혀 안쓰는 블로그지만 가끔씩 생각나면 여기다. 쓰게 되는군아!!)


FCKEditor 2.x 버전에서 (혹은 그 이하도 마찬가지일지도 모름) IE8로 접근 할 시에
액세스가 거부되었습니다 라는 스크립트 에러 메세지로 글쓰기 영역이 생성되지 않는 문제가 있다.

해당 문제는 IE8 에서 일어나며 원인은 몇가지 있으나 우리에겐 원인이 중요 한 것이 아니라
해결책과 대안이 늘 중요하다 ㅡ.ㅡ


원인과 또 다른 해결 방법:
http://blog.naver.com/webdzang?Redirect=Log&logNo=50047871069


하지만 위 링크의 해결 방법은 우리의 상황에 맞지 않으며
직접적인 원인을 수정해야만 했다.
따라서 구글신에게 문의 해 본 결과, 아래와 같은 해결책을 찾았다.

소스에 IE8 일때의 경우를 추가:

 http://cksource.com/forums/viewtopic.php?f=6&t=13378&p=36503

 
이에 따라 수정 하려면 몇가지 고통이 필요 한데,
Compressed 된 fckeditorcode_ie.js 파일을 그대로 눈알빠지게 봐가며, 변수명 비교해가며, 수정 할 것인가
아니면 source 폴더의 스크립트 파일들을 수정하여 재 압축 할 것인가 라는 선택

선자는 고통이고 후자는 좀 귀찮다..
귀찮은건 고통보다 싫기에,  선자를 택하여 수정 했다.


fckeditorcode_ie.js 파일을 연다. (FCKEditor 2.6.6 버전 기준)

var FCKBrowserInfo={IsIE:/*@cc_on!@*/false,IsIE7:/*@cc_on!@*/false&&(parseInt(s.match(/msie (\d+)/)[1],10)>=7),IsIE6:/*@cc_on!@*/false&&(parseInt(s.match(/msie (\d+)/)[1],10)>=6),IsSafari:s.Contains(' applewebkit/'),IsOpera:!!window.opera,IsAIR:s.Contains(' adobeair/'),IsMac:s.Contains('macintosh')};

부분을 찾아

IsIE8: /*@cc_on!@*/false && ( parseInt( s.match( /msie (\d+)/ )[1],10)>=8) 를 추가한다.

var FCKBrowserInfo={IsIE:/*@cc_on!@*/false,IsIE8: /*@cc_on!@*/false && ( parseInt( s.match( /msie (\d+)/ )[1],10)>=8),IsIE7:/*@cc_on!@*/false&&(parseInt(s.match(/msie (\d+)/)[1],10)>=7),IsIE6:/*@cc_on!@*/false&&(parseInt(s.match(/msie (\d+)/)[1],10)>=6),IsSafari:s.Contains(' applewebkit/'),IsOpera:!!window.opera,IsAIR:s.Contains(' adobeair/'),IsMac:s.Contains('macintosh')};


fckeditor.html 파일을 열어 아래 부분을


// Save a reference to the default domain.
var FCK_ORIGINAL_DOMAIN ;

// Automatically detect the correct document.domain (#123).
(function()
{
    var d = FCK_ORIGINAL_DOMAIN = document.domain ;

    while ( true )
    {
        // Test if we can access a parent property.
        try
        {
            var test = window.parent.document.domain ;
            break ;
        }
        catch( e ) {}

        // Remove a domain part: www.mytest.example.com => mytest.example.com => example.com ...
        d = d.replace( /.*?(?:\.|$)/, '' ) ;

        if ( d.length == 0 )
            break ;        // It was not able to detect the domain.

        try
        {
            document.domain = d ;
        }
        catch (e)
        {
            break ;
        }
    }
})() ;

// Save a reference to the detected runtime domain.
var FCK_RUNTIME_DOMAIN = document.domain ;

var FCK_IS_CUSTOM_DOMAIN = ( FCK_ORIGINAL_DOMAIN != FCK_RUNTIME_DOMAIN ) ;

아래와 같이 바꾼다

var IsIE8 = /*@cc_on!@*/false && ( parseInt( navigator.userAgent.toLowerCase().match( /msie (\d+)/ )[1], 10 ) >= 8 );
// if IE8, setup a bunch of popups that use a domain we can touch....
var GLOBAL_POPUP_BUCKET_FCK_IE8 = [];
if(IsIE8){
      for(i=0; i<20; i++){
         GLOBAL_POPUP_BUCKET_FCK_IE8.push(window.createPopup());
      }
}


// Save a reference to the default domain.
var FCK_ORIGINAL_DOMAIN ;
var FCK_RUNTIME_DOMAIN ;
// Automatically detect the correct document.domain (#123).
(function()
{
   var d = FCK_ORIGINAL_DOMAIN = FCK_RUNTIME_DOMAIN = document.domain ;

   while ( true )
   {
      // Test if we can access a parent property.
      try
      {
         var test = window.parent.document.domain ;
         break ;
      }
      catch( e ) {}

      // Remove a domain part: www.mytest.example.com => mytest.example.com => example.com ...
      d = d.replace( /.*?(?:\.|$)/, '' ) ;

      if ( d.length == 0 )
         break ;      // It was not able to detect the domain.

      try
      {
         // Before setting document.domain, set it for all the popups we've created.  I hope this fucking works....
         for(i=0; i < GLOBAL_POPUP_BUCKET_FCK_IE8.length; i++){
            GLOBAL_POPUP_BUCKET_FCK_IE8[i].document.domain = d;
         }
        
         document.domain = d ;
      }
      catch (e)
      {
         break ;
      }
   }
})() ;

var FCK_RUNTIME_DOMAIN = document.domain ;

var FCK_IS_CUSTOM_DOMAIN = ( FCK_ORIGINAL_DOMAIN != FCK_RUNTIME_DOMAIN ) ;



다시 fckeditorcode_ie.js 를 열어 아래 부분에

var B; if (FCKBrowserInfo.IsIE){this._Popup=this._Window.createPopup();var C=this._Window.document;if (FCK_IS_CUSTOM_DOMAIN&&!FCKBrowserInfo.IsIE7){C.domain=FCK_ORIGINAL_DOMAIN;document.domain=FCK_ORIGINAL_DOMAIN;};B=this.Document=this._Popup.document;if (FCK_IS_CUSTOM_DOMAIN){B.domain=FCK_RUNTIME_DOMAIN;C.domain=FCK_RUNTIME_DOMAIN;document.domain=FCK_RUNTIME_DOMAIN;};FCK.IECleanup.AddItem(this,FCKPanel_Cleanup);}else{var D=this._IFrame=this._Window.document.createElement('iframe');

빨간색으로 표시된 브라우저 처리 부분을 추가해준다

var B;
if ( FCKBrowserInfo.IsIE8 )
    {
        this._Popup = GLOBAL_POPUP_BUCKET_FCK_IE8.pop();

         B = this.Document = this._Popup.document ;

         FCK.IECleanup.AddItem( this, FCKPanel_Cleanup ) ;
    }
    else if (FCKBrowserInfo.IsIE){this._Popup=this._Window.createPopup();var C=this._Window.document;if (FCK_IS_CUSTOM_DOMAIN&&!FCKBrowserInfo.IsIE7){C.domain=FCK_ORIGINAL_DOMAIN;document.domain=FCK_ORIGINAL_DOMAIN;};B=this.Document=this._Popup.document;if (FCK_IS_CUSTOM_DOMAIN){B.domain=FCK_RUNTIME_DOMAIN;C.domain=FCK_RUNTIME_DOMAIN;document.domain=FCK_RUNTIME_DOMAIN;};FCK.IECleanup.AddItem(this,FCKPanel_Cleanup);}else{var D=this._IFrame=this._Window.document.createElement('iframe');




끝 ㅡ.ㅡ;