PhoneGap2012. 6. 26. 13:44

이제는 유지보수에만 전념하던 그래서 약간은 지루했던(잘 알지도 못하면서;) 안드로이드 개발에 또 하나의 도전 과제가 생겼다. 나태에 젖어 흐느적 거리고 있던 내게 팀장님이 재미있는 미끼를 던져주었다. 이름 하여 PhoneGap 하고픈 말은 나중에 보중하고 일단 개발자라면 늘 친숙한 Hello world 부터 찍어보자

개발자는 두 가지 부류가 있다.
설계를 머리에 이고 키보드 부터 두드리는자,
그리고 UML부터 그려가며 말과 글과 그림그리며 오감으로 개발을 시작하는 자.
다들 안다. 후자가 고수의 길이며 정도라고..
하지만 난 늘 전자의 유혹에 무릎을 꿇는다..
후자 스타일이라면 먼저 PhoneGap 이 무언지 부터 살펴보자
http://phonegap.com/

시작하기
http://phonegap.com/start#android

1.요구사항
- 이클립스 3.4 이상 필요하단다 찾아보니 Ganymede 이상이면 될듯하다. 아직도 난 정식명칭이 궁금 가니메데? 개니메드? 가나이메드? ㅋ
- 이클립스를 사용하지 않는 이 듀토리얼의 터미널 버전 - 이건 무슨말인지 모르겠다;

2. SDK 설치 + PhoneGap
- 이클립스 받아 설치
- 안드로이드 SDK 받아 설치
- ADT 까지 받아 설치, 여기까지는 기존에 안드로이드 개발을 했던 분이라면 이미 준비되어 있을거라 생각된다.
- PhoneGap 최신 버전을 받아서 압축을 풉니다. 이 압축 푼 내용을 안드로이드 디렉토리와 함께 작업할 껍니다. 나는 다른 라이브러리 처럼 플러인이나 라이브러리 path를 잡아줘야 되는 줄 알았다.
하지만 내용을 보면 알겠지만 jar, js, htm 이 들어 있어 안드로이드 소스에 직접 복사해서 넣는 방법을 쓰며 배포시 패키지 만들때 포함되어 릴리즈 되는 것 같다.

3. 새 프로젝트 만들기
- New>Android Project 로 실제 Helloworld 안드로이드 프로젝트를 만든다.
- 버전선택을 맘대로 정한다. 테스트로 2.2
- 앱네임과 패키지 명, 메인 액티비티 명을 이름 짓는다.
- 최소 sdk 버전은 써도 안써도 좋다. 2.2로 정했다면 2.2의 api 레벨인 8로 테스트로 세팅한다.
여기까지는 기존에 안드로이드 개발해보신 분이라면 무난히 수월하게 따라 왔으리라고 본다.
이 후에 설정 작업이 재미있다.
- 여러분이 만든 Helloworld 프로젝트 루트 디렉토리에 다음과 같이 /libs  디렉토리를 만들고 기존에 있는 /assets 디렉토리 밑에 /www 디렉토리를 새로 만든다. 그러면 /libs , /assets/www  두 개의 디렉터리가 만들어진다.
- 아까 다운로드 받아 압축 풀어놨던 파일들 중에 android 디렉토리 안에 phonegap.js 파일을 /assets/www 에 복사해 붙여 넣는다.
- phonegap.jar 파일을 /libs 디렉토리 안에 복사하기 해서 붙여 넣는다.
- 마지막으로 이번엔 xml 디렉토리 전체를 복사하여 /res 디렉토리에 붙여넣는다.
- 이제 만들었던 메인 액티비티 소스를 고쳐보자. 
- 고치기에 앞서서 추가했던 jar를 프로젝트 path에 설정해주자. 프로젝트명 오른쪽 마우스 클릭해 Properties를 선택하여 java build path를 살펴보면 Libraries 탭을 설정해서 Add JARs를 선택해 현재 프로젝트에 속해있는 phonegap.jar 파일을 선택해 포함시킨후 확인 저장한다.
- 다음으로 소스를 고친다 이미 기본으로 생성된 소스를 다음과 같이 수정한다.
- Activity 로 extends된 클래스를 DroidGap으로 바꾼다.
- 뷰를 그려주는 역할을 하는 setContentView() 대신 super.loadUrl("file://android_asset/www/index.htm"); 으로 설정한다. 해당 index.htm 은 조금 있다 만들겠다. file:///android_asset 은 루트의 기본 디렉토리인 /assets 를 말합니다.
- ctrl + o 를 눌러서 import com.phonegarp.*; 이 추가되고 import android.app.Activity;는 제거됩니다.
- 그리고 환경설정파일인 AndroidManifest.xml 파일에 버전정보 다음으로 아래 내용을 추가합니다. 아래는 제 소스 샘플입니다.

<?xml version="1.0" encoding="utf-8"?>
 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.binsolb.phonegap.helloworld"
      android:versionCode="1"
 
      android:versionName="1.0">
 
  <supports-screens
android:largeScreens="true"
 
android:normalScreens="true"
 
android:smallScreens="true"
 
android:resizeable="true"
 
android:anyDensity="true"
 
/>
<uses-permission android:name="android.permission.CAMERA" />
 
<uses-permission android:name="android.permission.VIBRATE" />
 
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
 
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
 
<uses-permission android:name="android.permission.INTERNET" />
 
<uses-permission android:name="android.permission.RECEIVE_SMS" />
 
<uses-permission android:name="android.permission.RECORD_AUDIO" />
 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
<uses-permission android:name="android.permission.READ_CONTACTS" />
 
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
<uses-permission android:name="android.permission.GET_ACCOUNTS" 
/>
 
<uses-sdk android:minSdkVersion="8" />
 
    <application android:icon="@drawable/icon" android:label="@string/app_name">
 
        <activity android:name=".HelloActivity"
                  android:label="@string/app_name"
 
                  android:configChanges="orientation|keyboardHidden">
 
            <intent-filter>
 
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
 
            </intent-filter>
 
        </activity>
 
    </application>
 
</manifest>


- 그리고 다음의 회전설정도 추가해줍니다.

4. Hellow World htm 만들기
-앞서 말씀드렸던 htm파일을 만듭니다. 위치는 /assets/www  아래에 index.htm  파일을 아래와 같이 만듭니다.

<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap</title>
<script type="text/javascript" charset="utf-8" src="phonegap-1.3.0.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>

- 스크립트 파일 명은 복사 해 넣은 버전의 파일명 그대로 쓰시면됩니다. 위는 제 샘플입니다.
5 배포합니다.
- 시뮬레이터로 배포 또는 디바이스로 배포를 합니다.
- Run as > Android Application 선택으로 실행시킵니다.

6. 결과 확인


잘나오는 군요 뿌듯합니다 이제 간단히 tv팟을 만들어봐야겠어요!


*혹시 잘 모르시는 분은 좀더 자세한 설명의 아래 페이지를 참고하세요~
http://wiki.phonegap.com/w/page/30862722/phonegap-android-eclipse-quickstart

Posted by 다오나무
SVN2012. 6. 26. 13:08

SVN을 이용해서 커밋을 하다가 보면 아래와 같은 에러 메세지가 뜨면서

커밋이 안될 때가 있다.


 svn: Commit failed (details follow):

svn: File or directory 'main.svn' is out of date; try updating
svn: resource out of date; try updating

이것은 현재 SVN 서버에 저장된 소스의 버전과 내가 Local에서 작업한 소스의 버전이 맞지 않아 발생한다.

즉, Local에서 작업한 소스 버전이 SVN서버의 버전에 있는 소스보다 과거의 소스를 받아서 계속 작업을 해왔다면

소스 버전이 맞지 않아 새로 수정한 소스 조차도 날짜가 맞지 않는 다고 나온다.


이럴 때는 기존의 Local에 있는 소스를 다 날리고, 새로게 업데이트를 받아서

수정한 부분을 Merge해서 다시 빌드한 다음에 올려야 제대로 올라간다.

'SVN' 카테고리의 다른 글

맥용 SVN 서버 설치  (0) 2012.06.26
Subversion (SVN) 명령어  (0) 2012.06.18
svn add로 관리 대상 추가/delete 로 파일 제거/ rename 으로 파일 변경  (0) 2012.06.18
SVN 명령어  (0) 2012.06.18
Posted by 다오나무
SVN2012. 6. 26. 11:37

기본적으로 Mac OS X에는 Subversion 프로그램이 있다.

1.터미널에서 SVN 서버와 클라이언트 버젼을 확인해 보자

서버 : svnserve --version
클라이언트 svn --version

 




2. 소스 저장할 위치에 대해 폴더를 생성 하고 Repository 생성한다.

svnadmin create [경로]


실행화면


다음과 같이 폴더와 파일이 생성된다.



 3. SVN 로그인 계정 등록
[Repository 폴더]/conf/passwd 파일을 수정한다.


예) 아이디 : zang / 패스워드 : 1234 

 
4. SVN 환경 설정
[Repository 폴더]/conf/svnserve.conf 파일을 수정한다.


익명 사용자 접근 막기 (anon_access = none)
인증 사용자 쓰기 권한 (auth_access = write)
인증 데이터베이스는 passwd 사용 (password-db = passwd)




5. Mac에서 SVN 서버를 자동으로 실행하기 위한 스크립트 파일 만들어야 한다.
[참고] 직접 서버를 실행하는 명령어

svnserve -d --listen-port=3690 -r [Repository경로]


스크립트 파일을 만들어 명령어를 입력한다.



스크립트 파일 저장후 파일 권한 설정해준다.

chmod 755 [스크립트 파일 경로]



6. Mac에서 부팅시 자동으로 스크립트 실행하도록 설정한다.

[시스템 환경설정]-[사용자 및 그룹]-[로그인 항목]에서 하단의 + 버튼을 눌러서 방금전에 생성한 스크립트 파일을 선택하면 된다.




7. 설치된 SVN 서버에 기존 소스를 import 하기 


디렉토리 생성

svn mkdir svn://localhost/trunk
svn mkdir svn://localhost/branches
svn mkdir svn://localhost/tags 


오류가 다음처럼 나오면 export SVN_EDITOR=vi를 처리해준다. 

svn: Could not use external editor to fetch log message; consider setting the $SVN_EDITOR environment variable or using the --message (-m) or --file (-F) options
svn: None of the environment variables SVN_EDITOR, VISUAL or EDITOR is set, and no 'editor-cmd' run-time configuration option was found


export SVN_EDITOR=vi


실행하고 나면 vi창으로 넘어감.. -> esc키 -> :q! -> 엔터 -> c 
왜 이렇게 하는건지는.. 자세히 안봐서 아직 모르겠고 그냥 vi빠져 나가고 continue(C) 눌러주면 폴더 생성


디렉토리 삭제  

svn rm svn://localhost/trunk



프로젝트 Import 

  svn import --no-auto-props --no-ignore -m "test프로젝트 추가" /Volumes/data/workspace/test svn://localhost/trunk/test


--no-auto-props --no-ignore 옵션을 주면 바이너리 파일을 추가 할수 있음.
-m "메시지" SVN 기록 메시지 

 

프로젝트 export

  svn export svn://localhost/trunk/test

Posted by 다오나무
PhoneGap2012. 6. 26. 10:43

최근엔 "하이브리드 앱"이라는 용어가 마케팅으로 사용될 정도로 PhoneGap에 대한 관심이 많습니다. 하지만 아래와 같은 장점을 가지고 있지만 단점도 가지고 있습니다.


장점

- 기존 Javascript 코드를 부분적으로 재사용 가능

- 하나의 웹 코드 (HTML, CSS, Javascript)를 작성하면 부분적으로 여러 플랫폼(iOS, Android, Window Phone, Black berry, webOS, smybian, bada) 재사용 가능 


단점

- 웹베이스로 코드가 동작 되기 떄문에 비교적(Native App과 비교하면) 느리다.

- PhoneGap API들은 가장 기본이 되는 것만 제공해 준다. (PhoneGap API 참고 : http://docs.phonegap.com/en/1.7.0/index.html)

- Native 고유의 기능을 사용해야만 구현 할 수 있는 기능들이 있다 (예) Android의 Background Service


이러한 장.단점들을 가지고 있지만 단점들을 보면 정말 부족해 보입니다. 이 단점을 어느 정도 극복 하기 위한 방법이 각 플랫폼 별로 Plugin을 개발해서 사용하는 것 입니다.


PhoneGap Plugin

Plugin을 만든다는 것은 Native 코드를 이용해서 필요한 기능들 만들어서 사용한다는 것 입니다. iOS의 경우에는 Objective-C로 Android는 Java로 기존 Native에서 동작하는 코드를 PhoneGap Plugin 형식에 맞게 제공하여 PhoneGap을 이용한 앱에서 이를 사용할 수 있도록 합니다.


무엇을 할 수 있는가?

많은 연산을 필요로 하는 작업은 Native에서 처리 (더 빠르다?)

- PhoneGap API에 없는 기능들을 추가해서 사용 (Android의 Background service, 바이너리 이미지를 PhotoLibrary에 저장)

- Native 언어적으로 복잡한 비지니스 로직을 처리 하는 방법이 있을 경우 플러그인으로 만들어서 사용




개발 방법

PhoneGap Plugin은 Javascript코드와 Native코드 쌍으로 이루어져 있습니다. 여러 플랫폼에 대응하는 Plugin기능을 만드려면 플랫폼 마다 구현해 줘야 합니다. Javascript는 기본 인터페이스를 만들어 놓고 세부적인 내용은 플랫폼 별로 다르게 작성 되야 합니다.


PhoneGap 1.5 부터 Apache Cordova로 이름이 바뀌면서 덩달아 Namespace도 모두 바뀌었습니다. 아래 내용은 PhoneGap 1.5 이후 버전에 맞게 작성 되었습니다.


iOS (wiki : http://goo.gl/fNC4c)

예제 코드는 Javascript에서 Plugin으로 Hellow World 문자열을 전달하여 전달된 문자열이 Hellow World가 맞다면 성공, 아니면 실패를 반환하는 코드 입니다. 원본 코드는 위의 wiki에서 확인 가능 합니다.


1.  사용할 Objective-C 코드 작성

(코드 출처 :  http://goo.gl/fNC4c)

#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
   
 
// CDVPlugin을 상속
@interface MyClass : CDVPlugin {
      
      // 호출된 Javascript를 참조 하는 ID
      NSString* callbackID; 
 }
 
@property (nonatomic, copy) NSString* callbackID;
 
// Instance Method 
- (void) print:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
  
@end


#import "MyClass.h"
       
 @implementation MyClass
 
 @synthesize callbackID;
 
 -(void)print:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options 
 {
           // arguments에 제일 마지막에 있는 값이 callbackID이다.
           self.callbackID = [arguments pop];
 
           // Javascript로 부터 전달된 값을 저장
           NSString *stringObtainedFromJavascript = [arguments objectAtIndex:0];                
 
           // Javsacript로 확인된 메세지를 보내기 위해 Objective-C에서 값 생성
           NSMutableString *stringToReturn = [NSMutableString stringWithString: @"StringReceived:"];
 
           // 전달된 값과 생성된 값을 합침
           [stringToReturn appendString: stringObtainedFromJavascript];
            
           // Javascript에 전달할 결과 값을 생성 CDVPluginResult 로 생성 해야만 한다. 전달하는 문자열은 UTF-8로 인코딩
           CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
                  messageAsString: [stringToReturn stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
 
           // Javascript로 부터 전달된 값이 HellowWorld이면 성공 Callback 실행, 실패면 실패 Callback 실행
           if ([stringObtainedFromJavascript isEqualToString:@"HelloWorld"] == YES)
           {
                  // Call the javascript success function
                  [self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
           else
           {   
                  // Call the javascript error function
                  [self writeJavascript: [pluginResult toErrorCallbackString:self.callbackID]];
           }
  }
  @end


2. 위에서 작성된 Plugin 코드를 실행할 Javascript 클래스 생성 및 실행 예제


var MyPlugin = {
     nativeFunction: function(types, success, fail) {
          return Cordova.exec(success, fail, "MyPlugin""print", types);
     }
};
 
MyPlugin.nativeFunction(
      ["HelloWorld"],
      function(result) {
           alert("Success: \r\n"+result);     
      },
      function(error) {
           alert("Error: \r\n"+error);     
      }
); 



3. 위의 코드들이 제대로 동작하게 할 설정  


Cordova.plist



<plist version="1.0">

     <dict>
          ...
          <key>Plugins</key>
          <dict>
               ...

               <key>MyPlugin</key>

               <string>MyClass</string>
               ...
          </dict>
          ...
     </dict>
</plist>





다른 플랫폼에 대한 Plugin 개발 방법은 여기서(http://goo.gl/O6XHI) 확인 가능 합니다


결론


PhoneGap은 모바일 개발 인력이 부족한 환경에서 빠른 시간 안에 더 많은 플랫폼을 대응할 수 있다는 장점을 가지고 있습니다. 하지만 이러한 생산성은 모든 플랫폼에 적용되는 것이 아닙니다. 지극히 한정적인 서비스에서 그리고 웹기반으로 서비스를 구축했을때 장점을 가진 환경에서만 위의 장점을 누릴수 있습니다. 많은 서비스들이 하이브리드 앱으로 서비스를 시작했다가 결국에는 Native 로 다시 만드는 사례가 많아 지고 있는 이유는, 하이브리드 앱 개발이나 HTML5 라는 용어가 단순히 유행 처럼 서비스에 적용하는 사례들이 많아 지고 있기 때문이 아닐까 생각해 봅니다.


저도 PhoneGap에 대해 공부 하고 있는 상태지만, 서비스에 해당 기술을 도입함에 앞서서 앞으로 만드려고 하는 서비스가 퍼포먼스를 중시하는 서비스 인지, 아니면 특별한 기능이 있는데 이를 하이브리드 앱으로 개발해도 가능 할지에 대해 반드시 확인하고 적용해 보는게 좋습니다. 

제대로된 선행 조사와 서비스에 대한 이해가 있는 상태에서 PhoneGap을 선택했을때는 더할나위 없이 빠른 퍼포먼스를 낼 수도 있는 플랫폼이지만 그게 아니라면 빠른 생산성은 커녕 유지 보수 하는데 더 많은 시간과 인력이 필요하게 될거라 생각 합니다.


PhoneGap의 모든것이라는 제목으로 3개의 포스팅을 하였는데요. 모든것을 다룬것 같진 않네요. 처음 시작하기에 앞서 한번 훑어 보면 좋은 글이라고 봐주셨으면 합니다.


제가 언급한 내용중 잘못된 내용이 있으면 바로 잡아 주세요.

Posted by 다오나무
PhoneGap2012. 6. 26. 10:39



이번 문서에서는 PhoneGap를 셋팅하고 (iOS) PhoneGap 프로젝트에서 javascript 초기화에 대해 다룬다.


Getting Start PhoneGap



PhoneGap을 시작하는 사람들을 모두 들어가 봤을만한 공식 사이트다. 아주 친절하게도 http://phonegap.com/start 이 주소로 들어 가면 플랫폼 별로 초기 셋팅을 어떻게 하는지에 대해 순서대로 나와 있다. 



주의 해야할 사항은 xcode 를 이용하여 iOS 플랫폼에 셋팅을 하는 경우라면 초기에 PhoneGap을 설치하고 Cordova Project로 생성을 한 뒤에 위와 같은 폴더 구조가 나오는데 바로 컴파일을 하면 제대로된 화면이 안나오고 아래와 같은 메세지를 볼수 있다.



PhoneGap에서 사용되는 javascript 와 html, css파일들은 프로젝트 하위의 www 폴더에 생성되는데 xcode에서 이를 인식하지 못하여 발생하는 문제이다. xcode에 플러그인 형식으로 설치 하는 거라 플러그인에서 생성 시킨 폴더(www)를 자동으로 프로젝트에 추가 해주지 못해서 그런것 같다. 위의 문제는 프로젝트를 한번 컴파일 한뒤에 프로젝트를 저장한 폴더에 이동해 보면 www폴더가 생성되어 있는걸 볼수 있는데 아래와 같이 프로젝트에 추가해 주면 제대로 동작 하게 된다.


www폴더를 드래그 해서 위의 phonegap_sample 프로젝트에 드랍해 주면 프로젝트에서 www를 인식 하게 된다. 





www/index.html 



<script type=
"text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
<script type="text/javascript">
 
function onBodyLoad() {
 
    document.addEventListener("deviceready", onDeviceReady, false);
}
 
function onDeviceReady()
{
    // Device Ready!
}
 
  
</script>
<body onload="onBodyLoad()">
</body>


www폴더를 보면 PhoneGap을 동작하는데 필요한 cordova.js와 index.html 이 있다. cordova.js를 PhoneGap을 초기화 하고 디바이스와 통신하는데 필요한 준비 및 API로 구성되어 있다. 한번 훑어 보는것도 좋을듯 하다.


위의 코드는 index.html에서 기본적인 source만 뽑아낸 것이다. 어플리케이션이 실행 되면 처음으로 시작되는 곳이 index.html 이다. 모든 초기화는 여기서 이루어 진다. 일반 웹 페이지와 다른 것은 "deviceready" 이벤트를 통해 PhoneGap이 동작하는 기기(플랫폼)가 준비가 되었는지 확인하는 과정이 필요하다. 위의 코드에서 알수 있듯이 onDeviceReady가 호출 된 후에에 PhoneGap API 등을 사용할 수 있다. 그 전에 호출을 해봤자 실행 되지 않는다.



2개의 문서로 마무리를 지으려고 했는데, 문서를 작성하면서 생각이 덧붙여 지면서 분량이 더 많아 졌다. 다음 문서에서는 iOS와 Android 플랫폼에서 동작하는 Plugin 개발하는 방법을 다루겠다.

Posted by 다오나무
PhoneGap2012. 6. 26. 09:37


PhoneGap

PhoneGap의 기본컨셉은 대부분의 모바일 플랫폼이 WebView를 가지고 있기 때문에 기본적인 기능들은 HTML과 CSS, Javascript로 만들어서 WebView에서 동작하게 하고 Device(Native)의 도움이 필요한 영역은 PhoneGap framework가 도움을 줘서 설치되어 동작하는 어플리케이션을 만들수 있는 방법을 제공 하는 것이다.





Apache Cordova

Adobe가 PhoneGap을 인수한 뒤에 PhoneGap을 Apache재단에 기부하였다. 그래서 Opensource가 되었고, PhoneGap 1.5 (Cordova) 버전부터 클래스 이름이 죄다 바뀌어 버렸다.


PhoneGap에서 제공하는 것들

WebView만으로는 구현이 어렵거나, Native의 도움이 필요한 기본 기능들은 PhoneGap API을 통해 사용 가능하다. 

지원하는 API 는 다음과 같다.

  • 각각의 기능별로 예제 코드와 함께 정리가 잘되어 있어서 쉽게 적응할 수 있다.
  • 각 API별로 지원하는 플랫폼이 명시 되어 있다. (iOS와 Android는 대부분 지원)


Accelerometer : Tap into the device's motion sensor.

Camera : Capture a photo using the device's camera.

Capture : Capture media files using device's media capture applications.

Compass : Obtain the direction that the device is pointing.

Connection : Quickly check the network state, and cellular network information.

Contacts : Work with the devices contact database.

Device : Gather device specific information.

Events : Hook into native events through JavaScript.

File : Hook into native file system through JavaScript.

Geolocation : Make your application location aware.

Media : Record and play back audio files.

Notification : Visual, audible, and tactile device notifications.

Storage : Hook into the devices native storage options.



PhoneGap의 동작 원리

PhoneGap을 이용하여 만든 어플리케이션들도 결국에는 Native 어플리케이션으로 컴파일되어 설치 된다. Adobe AIR과 같이 코드가 바이너리로 설치 파일에 포함되는 형식이 아니라, Native 어플리케이션 위에서 PhoneGap이 동작하고 이 PhoneGap을 통해 Javascript로 만든 어플리케이션과 통신하는 과정을 통해 실행 된다. 하지만 Javascript는 Native와 다른 환경에서(WebView) 동작한다. JavaScript는 네이티브 코드와 데이터를 공유할 수 없다. 그래서 PhoneGap에서는 별도의 통신 방법을 만들어 웹뷰에서 네이티브 코드와 데이터를 교환한다. 각각의 플랫폼(iOS, Android...) 마다 WebView와 Native와 서로 통신하는 방법이 다르다. 이렇게 통신하는 방법이 다르기 때문에 PhoneGap에서 이를 지원한다. 이러한 방법이 PhoneGap이 동작하는 원리이다.

PhoneGap을 Native에서 동작하는 코드들과 (각각 플랫폼 마다 별개) cordova.js 과의 통신을 통해 동작한다. (Plugin이 있다면 Plugin을 제어 하는 코드들도 함께 동작) 

각각의 플랫폼마다 WebView와의 통신 방법이 다르므로 모두 언급할 수 없기에 대표적으로 iOS와 Android를 설명하면 다음과 같다. 더 자세한 내용은여기를 참고하라.


IOS


Custom Scheme 이란? (예제)

tel:01012341234
kakaolink://sendurl?msg=[message]&url=[url]&appid=[appid]&appver=[appver]
gap://ready



iOS에서는 WebView에서 Native로 명령을 전달하고 받기 위한 방법으로 Custom Scheme를 사용한다. 아래와 같은 형식으로 WebView에서 호출하면 Native에서는 클래스(class), 함수(command) 그리고 함수에 전달될 인자(arguments) 를 통해 Native Code를 실행 한다.


yourscheme://<class>.<command>/[<arguments>][?<dictionary>]


반대로 Native Code에서 WebView로 명령은 iOS SDK의 stringByEvaluatingJavaScriptFromString를 이용한다.



NSString* status = @
"Native To WebView!";
[webView stringByEvaluatingJavaScriptFromString:status];


Android

WebView에 있는 addJavascriptInterface를 통해 Javascript가 호출할 수 있는 코드정보를 미리 등록해서 이를 이용해서 WebView에서 Native코드를 실행한다.

등록


webView.addJavascriptInterface(new MyNativeAPI(), "MyNativeAPI");
Class MyNativeAPI {
 
    public String whereAmI() {
        return "I am in Native";
    }
}


호출


MyNativeAPI.whereAmI();


반대로 Native Code에서 WebView로 명령은 WebView클래스의 loadUrl() 을 이용한다.


webView.loadUrl("javascript:alert('I am in WebView')");


PhoneGap을 사용하면 위에서 언급한 제공하는 API 만으로 어플리케이션을 구현하는데 부족함이 많다. 그래서 PhoneGap에서는 이 부족한 기능을 특정 플랫폼에 맞게 PhoneGap Plugin을 개발하여 사용할수 있다. (Adobe AIR의 Native Extention 이라 생각하면 되겠다) PhoneGap의 근본 취지와는 다르게 Plugin은 특정 플랫폼에 맞춰서 따로 따로 개발해 줘야한다. 2번째 문서에서는 간단히 PhoneGap Plugin을 개발하는 방법에 대해 다룰 것이다.

Posted by 다오나무
MYSQL2012. 6. 22. 21:40

MYSQL 을 백업하는 방법은 2가지가 있습니다.

 

1.  파일 복사 하기 

- data 디렉토리의 파일을 별도의 장소에 복사 놓는 방법입니다.  

 

 ① mysql 의 데이타가 저장되는 장소를 알아야 됩니다.  

      windows에 mysql이 설치된 경우 mysql 이 설치된 폴더의 my.ini 에서 저장된 곳을 알아 낼 수 있습니다. 
      메모장을 이용해서 my.ini를 열어 보면

      #Path to the database root
       datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.1/Data/"

      

      datadir 이 mysql DB 가 저장되어 있는 위치 입니다.  

 

 ②  위의 경우 datadir의 위치의 해당 데이타베이스의 폴더를 다른 곳에 복사해서 저장해 놓으면 됩니다.

 

  ※  datadir폴더에 보면 여러 파일들이 있는데 그 용도는 다음과 같습니다. 

       *.frm - 테이블 구조 *.MYD - 데이타 *.MYI - index

  ※ 운영체제별 mysql DB 저장되는 곳의 위치는 아래 링크를 참고하세요.  

       http://www.mkyong.com/mysql/where-does-mysql-stored-the-data-in-my-harddisk/ 

 

  ※  InnoDB의 경우 조금 더 복잡한 과정을 거쳐야 한다고 합니다.  더 알게 되면 포스팅 하겠습니다.

       http://dev.mysql.com/doc/refman/5.0/en/innodb-backup.html 

     http://feedtome.springnote.com/pages/546778

      http://dev.mysql.com/doc/refman/5.0/en/mysqldump.html#option_mysqldump_databases


2. mysqldump를 이용해 테이블과 데이타를 쿼리문으로 백업하기

 

① 서버의 전체 DB 백업 하기

 mysqldump -u계정 -p비밀번호 -A > 모든DB.sql

(예) mysqldump -uroot -ppassword -A > All_database.sql


 특정 DB 백업 하기 

     mysqldump -u계정 -p비밀번호 특정DB명 > 저장할파일명.sql

    (예) mysqldump -uroot -ppassword northwind > northwind.sql

 

 특정 DB 의 특정 Table 만 백업

     mysqldump -u계정 -p비밀번호 특정DB명 특정Table명  > 저장할파일명.sql

     (예) mysqldump -uroot -ppassword northwind orders > northwind_orders.sql
 

④ 특정 DB 의 스키마만 백업하기

     mysqldump -u계정 -p비밀번호 -d 특정DB명   > 저장할파일명.sql

    (예) mysqldump -uroot -ppassword -d northwind > northwind.sql

 

⑤  InnoDB에서 트리거 , 프로시져, 함수 포함하여 백업하기

   - 트리거는 default값으로 백업이 실행되나 저장 프로시져는 백업되지 않는다. 

   - 저장 프로시져가 백업되게 하기 위해서는 옵션에  --routines 을 넣어줘야 한다.

     mysqldump -u계정 -p비밀번호 --routines 특정DB명 > 함수프로시져트리거.sql

     (예) mysqldump -uroot -ppassword --routines  northwind > northwind.sql

 

     ※ 트리거 , 프로시져, 함수 만 백업하기 - 쿼리문만  

       mysqldump -u계정 -p비밀번호 --routines  --no-create-info --no-data --no-create-db --skip-opt

       특정DB명 > 함수프로시져트리거.sql 


    (예) mysqldump -uroot -ppassword --routines --no-create-info --no-data --no-create-db --skip-opt 

           northwind > northwind_only_sp_trigger_function.sql

           이렇게 트리커 프로시져 함수만 백업한 경우는 반드시 Data와 테이블 스키마를 별도로 백업 받아 줘야 합니다.  

[출처] mysql 백업하기|작성자 불가사리

Posted by 다오나무
영삼이의 IT정보2012. 6. 22. 11:18

<p>sqlite3 database;
 
//sqlite3 오픈및 생성
if (sqlite3_open([[self dataFilePath] UTF8String], &database) != SQLITE_OK) {
    sqlite3_close(database);
    NSAssert(0, @"Failed to open database");
}
 
else {
    char *errorMsg;
    NSString *createSQL1 = @"CREATE TABLE IF NOT EXISTS LOGS (ID INTEGER PRIMARY KEY
               AUTOINCREMENT, PHONENUMBER_DATA TEXT, NOW_DATE DATE, CALL_TIME TEXT);";
    if (sqlite3_exec (database, [createSQL1 UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
        sqlite3_close(database);
        NSLog(@"Error creating table: %s", errorMsg);
    }
    NSString *createSQL2 = @"CREATE TABLE IF NOT EXISTS ADDRESS (ID INTEGER PRIMARY KEY
               AUTOINCREMENT, NAME_DATA TEXT, PHONENUMBER_DATA TEXT, MEMO_DATA TEXT);";
    if (sqlite3_exec (database, [createSQL2 UTF8String], NULL, NULL, &errorMsg) != SQLITE_OK) {
        sqlite3_close(database);
        NSLog(@"Error creating table: %s", errorMsg);
    }
}
/* 여기서 생략된 -(NSString *)dataFilePath는 sqlite3파일이 있는 주소값을 리턴해주는 함수 입니다.
   지금 이 소스는 제가 실제로 썼던 소스라 두개의 테이블을 만들게 되어 있어요.
   ID integer primary key autoincrement는 자동으로 생성 되며 키값이 되구요, 그외 나머지는 Text,
   Date, Integer 등등 타입을 설정 할수 있어요 */
 
// 값을 받아 오기.
NSMutableArray *mutableLogData = [NSMutableArray array];
NSString *query = @"SELECT ID ,PHONENUMBER_DATA, NOW_DATE, CALL_TIME FROM LOGS ORDER
                    BY ID DESC";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) {
    while (sqlite3_step(statement) == SQLITE_ROW) {
        int idData = sqlite3_column_int(statement, 0);
        char *numberData = (char *)sqlite3_column_text(statement, 1);
        double dateData = sqlite3_column_double(statement, 2);
        char *callData = (char *)sqlite3_column_text(statement, 3);
        if (numberData != nil && dateData != 0 && idData != 0){
            NSNumber *idValue = [NSNumber numberWithInt:idData];
            NSString *numberValue = [[NSString alloc] initWithUTF8String:numberData];
            NSDate *dateValue = [NSDate dateWithTimeIntervalSince1970:dateData];
            NSString *callTimeValue = [[NSString alloc] initWithUTF8String:callData];
            NSArray *dataArray = [NSArray arrayWithObjects:idValue,
                                                           numberValue,
                                                           dateValue,
                                                           callTimeValue, nil];
            [mutableLogData addObject:dataArray];
            [numberValue release];
            [callTimeValue release];
        }
    }
}
sqlite3_finalize(statement);
/* logs라는 테이블의 id, phonenumber_data, now_date, call_time을 id의 역순으로 가져 오는
    부분 입니다.
    아주아주 쉬운 코드니 바로 아실듯 해요 ㅠ_ㅠ
    여기서는 제가 모든줄을 받아 오기 때문에 while문을 돌려 Array에 넣어주고 있어요 */
 
// 특정 줄의 삭제
 
char *errorMsg;
char *delete = "DELETE FROM ADDRESS WHERE ID = ?";
sqlite3_stmt *stmt;
if( sqlite3_prepare_v2(database, delete, -1, &stmt, nil) == SQLITE_OK) {
    sqlite3_bind_int(stmt, 1, [idData intValue]);
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
    NSLog(@"Error deleting table: %s", errorMsg);
}
sqlite3_finalize(stmt);
 
/* id값을 받아 그것과 일치하는 address테이블의 줄을 삭제 해주는 코드 입니다.
    id는 당연 integer값이니 int형으로 넣구요. */
 
 
// 테이블에 값 입력하기
char *errorMsg;
char *update = "INSERT INTO LOGS (PHONENUMBER_DATA, NOW_DATE, CALL_TIME) VALUES (?,?,?);";
if (sqlite3_open([[self dataFilePath] UTF8String], &database) != SQLITE_OK) {
    sqlite3_close(database);
    NSAssert(0, @"Failed to open database");
}
sqlite3_stmt *stmt;
if( sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {
    sqlite3_bind_text(stmt, 1, [callNum UTF8String], -1, NULL);
    sqlite3_bind_double(stmt, 2, [nowDate timeIntervalSince1970]);
    sqlite3_bind_text(stmt, 3, [callTime UTF8String], -1, NULL);
};
if( sqlite3_step(stmt) != SQLITE_DONE)
    NSLog(@"Error updating table: %s", errorMsg);
sqlite3_finalize(stmt);
 
/* logs 테이블에 값을 넣는 코드 입니다.
    넣으려고 하는 값은 ?를 이용하여 따로 넣어 줄수 있어요.
    조금 아래를 보시면 bind를 이용해 값을 넣는 부분이 있어요 */
 
// 원래 있던 값 수정하기
char *errorMsg;
char *update =
     "UPDATE ADDRESS SET NAME_DATA=? , PHONENUMBER_DATA=? , MEMO_DATA=? WHERE ID=?;";
sqlite3_stmt *stmt;
if( sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {
    sqlite3_bind_text(stmt, 1, [name UTF8String], -1, NULL);
    sqlite3_bind_text(stmt, 2, [phoneNum UTF8String], -1, NULL);
    sqlite3_bind_text(stmt, 3, [memo UTF8String], -1, NULL);
    sqlite3_bind_int(stmt, 4, idNum);
}
if( sqlite3_step(stmt) != SQLITE_DONE)
NSLog(@"Error updating table: %s", errorMsg);
sqlite3_finalize(stmt);
 
/* address테이블의 id를 같은 값을 찾아 <br><span id="callbacknestceruleanbtistorycom68666" style="width:1px; height:1px; float:right"><embed allowscriptaccess="always" id="bootstrapperceruleanbtistorycom68666" src="http://ceruleanb.tistory.com/plugin/CallBack_bootstrapperSrc?nil_profile=tistory&nil_type=copied_post" width="1" height="1" wmode="transparent" type="application/x-shockwave-flash" enablecontextmenu="false" flashvars="&callbackId=ceruleanbtistorycom68666&host=http://ceruleanb.tistory.com&embedCodeSrc=http%3A%2F%2Fceruleanb.tistory.com%2Fplugin%2FCallBack_bootstrapper%3F%26src%3Dhttp%3A%2F%2Fs1.daumcdn.net%2Fcfs.tistory%2Fv%2F0%2Fblog%2Fplugins%2FCallBack%2Fcallback%26id%3D6%26callbackId%3Dceruleanbtistorycom68666%26destDocId%3Dcallbacknestceruleanbtistorycom68666%26host%3Dhttp%3A%2F%2Fceruleanb.tistory.com%26float%3Dleft" swliveconnect="true"></span>
   그 줄의 name_data, phonenumber_data, memo_data를 바꾸는 코드입니다.
   위랑 같이 ?를 이용해 따로 넣으실수 있어요 */
</p>

Posted by 다오나무
iOS2012. 6. 21. 18:32

@interface TESTAppDelegate : NSObject <UIApplicationDelegate> {

 

 

NSString *DBNAME;

NSString *DBPATH;

 

}


-- m파일 

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectoryNSUserDomainMaskYES);

    NSString *documentsDirectory = [paths objectAtIndex:0];    

    NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent@"Store.sqlite"];

self.DBPATH = writableDBPath;

NSLog(@"writableDBPath is %@", writableDBPath);

    

    BOOL dbexits = [fileManager fileExistsAtPath:writableDBPath];

    if (!dbexits) 

    {

NSLog(@"데이터베이스 카피");

        // 데이터베이스가 존재하지 않으면어플리케이션 Resource아래에서 복사를 한다

        NSString *defaultDBPath = [[[NSBundle mainBundleresourcePathstringByAppendingPathComponent:@"Store.sqlite"];

        NSError *error;

        BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];

    }    

   else {

NSLog(@"이미 패스가 잇음");

    }

아 별짓다했네... 3시간동안 강제로 Path따서 햇지만 읽히는건 읽혀지지만... insert가 안되서...
완전 제일 중요한거!!! 앱/Documents <--요놈안에있는놈만 수정이 가능하다!!

Posted by 다오나무
영삼이의 IT정보2012. 6. 21. 10:23


위와 같은 테이블 뷰를 구성할 때 체크박스를 선택하는 기능을 구현해야 한다. 선택된 값을 저장하는 시점도 필요한데 언제가 되야 할지 처음엔 다소 어렵게 느껴질 수 있다. 하지만 tableView:cellForRowAtIndexPath: 델리게이트 메서드를 이용해서 간단히 구현 할 수 있다.



우선 다음 코드를 보자 일반적인  tableView:cellForRowAtIndexPath: 구현이다.


1. 재사용을 위해 cellIdentifier을 선언한다. static NSString *cellIdentifier = @"CELL";


2. 만약 처음 cell을 생성해야 하는 경우는 if (cell == nil) 블럭 안에서 UITableViewCell이 생성 될 것이다. 나는 보통 셀이 생성될때 공통적인 속성을 설정한다. cell.selectionStyle을 설정한 것처럼 말이다.


3. 그 외 변동되는 부분은 아래의 코드에서 보듯이 cell의 객체가 nil이 아닐때 처리한다.


static NSString *cellIdentifier = @"CELL";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];


if (cell == nil) {

    cell = [[[UITableViewCell allocinitWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];

    cell.selectionStyle = UITableViewCellSelectionStyleGray;

}


if ([[self.item objectForKey:@"select"intValue] == indexPath.row) {

    cell.accessoryType = UITableViewCellAccessoryCheckmark;

    self.prevSelectedIndexPath = indexPath;

else {

    cell.accessoryType = UITableViewCellAccessoryNone;

}


cell.textLabel.text = [[self.item objectForKey:@"value"objectAtIndex:indexPath.row];


return cell;



위의 코드대로라면 select row의 값만 UITableViewCellAccessoryCheckmark 가 표시될 것이다.

이제 tableView:didSelectRowAtIndexPath:를 구현하면 되는데 선택되 었을때 다음 코드와 같이 하면 선택된 셀이 변경될 것이다. 


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    [self.item setValue:[NSNumber numberWithInt:indexPath.rowforKey:@"select"];

    [tableView reloadData];

}



하지만 문제점이 있는데 바로 셀이 선택되는 순간 선택된 애니메이션이 보여지기도 전에 체크만되고 테이블 뷰가 리로드 될 것이다. 그래서 다음 코드를 적용하면 올바르게 동작할 것이다.  


1. 미리 저장된 이전에 선택된 셀의 IndexPath와 선택된 셀의 IndexPath를 비교해 동일하다면 return한다.


2. 만약 같지 않다면 reloadRowsAtIndexPath:withAnimation: 메서드를 사용해서 이전에 선택된 셀 및 선택된 셀을 리로드한다. 


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    NSComparisonResult result = [self.prevSelectedIndexPath compare:indexPath];

    if (result == NSOrderedSamereturn;

    

    //셀렉트 저장

    [self.item setValue:[NSNumber numberWithInt:indexPath.rowforKey:@"select"];

    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:self.prevSelectedIndexPath, indexPath, nil]withRowAnimation:UITableViewRowAnimationFade];

}

'영삼이의 IT정보' 카테고리의 다른 글

Remove @Override annotation  (0) 2012.06.26
ios에서의 sqlite3 사용법  (0) 2012.06.22
iOS 5에서 UIAlertView에 추가된 것  (0) 2012.06.20
문자열 (NSString) 비교하기  (0) 2012.06.20
날짜/시각 구하기  (0) 2012.06.19
Posted by 다오나무