회사를 다니지 않다 보니, 삽질을 한게 얼마만인가 싶네요 ㅎ

얼마전에 아이폰 알바를 했는데, 게임을 하고, 서버에 점수를 등록해서, 1,2,3등에게 경품을 주는 광고용 어플을 개발했습니다.

여기서 문제가 마이크로 사이트를 작업한 곳에서, 한글 Encoding이 잘안된다고 얘길했었는데,

그런곳도 못해 하면서, 우리가 보겠다고 했다.
 
근데 이게 왠걸, iPhone쪽 소스에 문제가 있었나보다.

AppStore의 바이너리를 갈아치우는게 가장 좋은 방법이겠지만, 그럴만한 시간이 없었다.

완전 대박 사고! -_ㅜ .

문제는 Cocoa FrameWork의 NSString Class String 'UTF8String'이라는 녀석을 사용해서 Encoding 했는데, 그게 제대로 안되는 모양이다.

사실 다른 Encoding Method를 사용하려 했는데, TestCode가 그대로 남아 있었던 모양이다 ( 테스트시에도 제대로 안됐음 ㅜ_ㅜ )

그래서 제대로된 UTF8이 나오지 않았기에, Javascript로 UTF8Decode를 아무리 해도 제대로 안나온다 -_ㅜ.

그래서 든 첫번쨰 생각은,

UTF8String Method를 분석해서, Javasciprt로 구현하는것이다.

다행히 GNUStep에 Core Foundation, Foundation Framework 소스가 있기에, 보았는데 완전 대박.
이거 옮기는 시간이 Binary를 새로 올리는것 보다 더 오래 걸릴 기세였다.

이것도, 옵션이 안되고, 그래서 Proxy를 둬서 새로 Encoding을 해서 넘겨 주는것으로 하였다.

OSX에 Web Server를 올리고 ( 정말 감사하게도, apache2가 기본 설치 되어 있다 -_ㅜ )

bash shell 을 cgi 로 사용해서, objc 프로그램을 실행시켜, Core Foundation을 통해서 잘못된 Decode를 풀고,
새로 Encode 하게 하는 script를 짜고,

ASP 상의 XMLHTTPRequest를 통해서, syncronous method로 변환하게 했다.

일단 동작은 하는데 겁나 찜찜하다 ㅜ_ㅜ.

우선 Review Path하면 Patch 해서 App Store에 다시 등록해야 할듯 -_ㅜ

아.. 오래간만에 shell script도 짜고, 간만의 삽질 ㅎㅎ
주소창과, 즐겨찾기에 로고를 표기하는 법 !



구글에 들어 가면 좌측에 나오는거 처럼 구글 로고가 뜨는데, 갑자기 궁금해져서 한번 찾아 봤습니다 ㅎ

우선, 표기될 icon 은 16x16 pixels이어야하면, Windows ico format으로 저장되어야 합니다.
2가지 방법을 통해서, icon을 표기 할수 있습니다.

방법 1.

http://${your_url}/favicon.ico에 저장하는 방법입니다.
http://www.google.com/favicon.ico 에 로고 이미지가 들어 가 있는걸 보아하니,
구글은 이 방법을 채택하고 있네요 ㅎㅎ

방법 2.

webpage에 아래 태그를 추가하는것입니다..
<LINK REL="SHORTCUT ICON" HREF="{icon_url}">

추가로, 방법2를 이용하면 페이지별 다른 아이콘을 링크하여,
페이지별로 다른 아이콘을 보여줄수도 있는데, 가능은하지만, 별로 보기 좋아 보일것 같진 않네요 ㅎㅎ

흐음.. 내 홈피에도 추가 하고 싶긴한데, 아이콘이 없군요 ㅋㅋㅋㅋㅋ


iPad라는 녀석 떄문에, 세상이 꽤나 떠들썩합니다.

미국에서는 이미 Wifi모델이 출시되었고, 곧 3G모델이 출시합니다.

한국에서는 iPad가 아직 정식출시되지 않아서 구매대행이 성행하게 되었는데,

정부에서 구매대행은 불법 엄벌하겠다고 한것 까지 모자라, 무선기기를 인증없이 쓰는 사람들을 단속하겠다는 말까지 나돌고 있으니,

iPad라는 녀석이 세간의 관심을 확 끌어 당기고 있는 녀석임에는 분명한가 봅니다.


iPad라는 녀석의 첫 인상은 말보다 제가 좋아하는 유머로 대체 해볼까 합니다.


ㅎㅎ 그냥 커진 아이폰이잖아 ! 라는게 제 첫 인상이었습니다.

왠지 iPad 출시 발표가 있기전 아마도 아이폰 3GS가 발매가 확정된 이후로 기억합니다만,

애플 커뮤니티등에서 엄청나게 많은 iPad에 대한 루머가 발생했습니다.

저는 사실 iPad가 iPhone 처럼 철저한 기획을 통해 출시한 제품이라기보다 워낙에 루머도 많고, 스티브잡스가 테블렛 PC에 대한 꿈도 있고 해서 만들어진 제품이 아닐까 조심스레 추측합니다.


그럼 여전히 제 첫인상을 이어가고 있을까요? 만약 그렇다면 이런 글을 쓰는데 시간을 낭비하는 일따윈 없었을것입니다 ㅎ

크기가 큰 아이폰 혹은 아이팟 터치라는 생각에는 변함이 없습니다만, 크기가 커졌다는 것에 맹정이 있다고 생각합니다.



요새 이슈가 되고 있는 'Alice for iPad' 동영상을 한번 보시죠 ^^



이 녀석이 아이폰에 있다고 생각하면, 제대로 읽을수도 사용할수도 없었겠지만,

단순히 '크기가커진' 아이패드에 있기 떄문에 'Interactive' eBook이라는게 성립됩니다.

제가 아이패드의 강점으로 생각하는것중 하나가 바로 이 '인터랙티브' 입니다.

이런 'Interactive'를 통한 교육용 어플 시장이 진지하게 펼쳐 지지 않을까 생각합니다.


사실, 제가 iPad에 큰 관심을 갖게 된 계기는 전문성을 가진 어플들에 있습니다.

Photoshop for iPhone 을 생각해보도록 하죠

아이폰에서 하는 포토샵은 솔직히 말해 장난감입니다. 아무도 아이폰에 있는 포토샵으로 자신의 사진을 진지하게 리터칭하지 않습니다.

하지만 아이패드에 있다고 생각해볼까요 ?

사진을 불러드리는게 좀 짜증나게 되어 있어서 사용성은 떨어지겠지만, 사진 편집용도만 생각해본다면 꽤 쓸만할꺼라 생각됩니다.

드로잉 프로그램은 어떨까요? 아이폰용 드로잉 프로그램이 화가들의 아이디어 스케치 용도로 국한되었다면,

아이패드용 드로잉프로그램을 통해서 충분한 작품활동도 할수 있을것 같습니다.

특히나 만화작가들에게 유용할꺼라 생각합니다.


특화된 어플들이 아이폰에 있을때는 장난감같이 보였다면, 아이패드 속에 들어 감으로써 꽤 쓸만해졌다는거죠.

시장이라는것은 흘러가봐야 하는것이라 두고 봐야겠지만,

아이폰 앱스토어의 키워드가 '게임' 과 'SNS' 였다면, 아이패드에선 '특화된 어플'이라는 단어를 추가 하고 싶습니다.


제가 좋아하는 홍익대학교의 박지헌 교수님은 이런 말씀을 하셨습니다. ㅎ

"무협지에서 주인공이 무공비급을 보면서 절세의 고수로 거듭나는것처럼

우리는 세상에 널려있는 공개소스를 봐야한다"



그래서 저는 공개된 소스를 자주 분석 하는 편입니다.

최근에는 iPhone SDK에 대해 공부 하기떄문에 iPhone Sample code들을 분석하려고 노력하고 있습니다.

Apple에서 공개한 소스 인 만큼 가장 정석적인 코드라고 봐도 무방하지 않을까 합니다.  (얘들도 사람이기때문에 실수는 하겠지만 ㅎ)


iPhone SDK에서 DB 를 사용하는 법을 공부 하기 위해서 SQLiteBooks 라는 Sample Code 를결정했습니다.

프로그램 이름에도 나와 있듯이 iPhone SDK는 SQLite 를 DBMS로 사용하고 있습니다.

이번 리뷰는 DBMS 공부가 목적이기 때문에 UI part 는 제외하고 DBMS 사용 즉 sqlite3 쪽 사용에만 초점을 두고 진행하겠습니다.

목차는 아래와 같습니다 :)

1. Introduction
a. What does this App. do ?
2. Analyze
a. DB- AppDelegate
b. DB- Book Class


[1-a What does this App. do ( SQLiteBooks 는 어떤 일을 하는가 )? ]

제목에서 알수 있듯이, 책을 관리 하는 프로그램입니다.
책을 하나의 list 로 관리 하며, 하나의 책 object 는 세가지 속성 ( Title, Copyright, Author ) 을 가지게 됩니다.

직관적으로 알수 있듯이, 책 추가, 책 삭제, 책의 정보 보기, 정보 수정하기의 기능을 합니다.



[2-a DB - AppDelegate] 

ApplDelegate 는 DB 제어 용이 아니라, App. 시작과 종료등에 날라오는 Delegate 즉, Application Delegate Protocol 을 구현하는 Class 이다.
이 sample 에서는 AppDelegate에서 DB 제어에 사용되고 있다.
사실 DB 제어 하는 부분의 소스코드는 썩 맘에 들지는 않는다.
DB 접근이 Class AppDelegate 와 Class Book에서 나누어져 이루어져 있다.
DBM Class 를 구현해서 한곳으로 모았으면 어땠을까 하는 생각이 든다.

어찌되었건, Class 의 선언 부분을 보도록 하자.

// This includes the header for the SQLite library.
#import <sqlite3.h>

// Inform the compiler that the following classes are defined in the project:
@class Book, MasterViewController, DetailViewController, AddViewController, EditingViewController;

@interface AppDelegate : NSObject {
    IBOutlet UIWindow *window;
    IBOutlet UINavigationController *navigationController;
    NSMutableArray *books;
    // Opaque reference to the SQLite database.
    sqlite3 *database;
}

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) UINavigationController *navigationController;

// Makes the main array of book objects available to other objects in the application.
@property (nonatomic, retain) NSMutableArray *books;

// Removes a book from the array of books, and also deletes it from the database. There is no undo.
- (IBAction)removeBook:(Book *)book;
// Creates a new book object with default data.
- (void)addBook:(Book *)book;

@end

먼저 Attributes 들을 보면
IB에서 생성된, window 와 navigationController,
Data 관리를 위한 books, database 가 있습니다.

UI 와 관련된 부분은 관심사가 아니기 때문에 Data 와 관련된 부분만 살펴 보도록 하겠습니다..

books 는 DB 에서 load 된 book list 들을 가지고 있는것이고,
database attribute 는 DB 접근을 위한 Object 입니다..
SQLite 는 많이들 사용하는 Mysql  과 달리, Server-Client 구조가 아니라,
하나의 파일을 open-read-write-close 하는 구조입니다..
sqlite 파일을 sqlite3_open 을 통해서 open 하고, query 를 통해서 read/write 하고 , sqlite3_close 를 통해서 close 합니다.
sqlite3 * 는 sqlite3_open 의 return type 이고, sqlite3_open 이외의 대부분의 api 가 sqilte3 *를 아규먼트로 요구 합니다.

더 자세한 정보는 sqlite3 홈페이지 에서 확인하시기 바랍니다.

자그럼 AppDelegate에서 구현하고 있는 Method 들에 대해서 살펴 보도록 하겠습니다.

@interface AppDelegate
// Removes a book from the array of books, and also deletes it from the database. There is no undo.
- (IBAction)removeBook:(Book *)book;
// Creates a new book object with default data.
- (void)addBook:(Book *)book;
@endif

// Private interface for AppDelegate - internal only methods.
@interface AppDelegate (Private)
- (void)createEditableCopyOfDatabaseIfNeeded;
- (void)initializeDatabase;
@end

// Implementation of Application Delegate Protocol
- (void)applicationDidFinishLaunching:(UIApplication *)application
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
- (void)dealloc
- (void)applicationWillTerminate:(UIApplication *)application


여러가지가 있지만 우선 Application Delegate Protocol 을 구현하고 있는 메소드들 부터 살펴보도록 하겠습니다.
우선 applicationDidFinishiLaunching 메소드를 살펴 보도록하겠습니다.
applicationDidFinishiLaunching Delegate 는 다들 잘아시겠지만.
Application 이 실행된후 최초로 불러지는 Delegate 입니다.
최초로 불러 지는것인 만큼 주로 초기화가 필요 한 코드들을 실행하게 됩니다.
이 Application 에서는 MainView를 윈도우에 할당하고, DBMS 샘플 소스 인만큼 DB 관련 초기화 코드인
createEditableCopyOfDatabaseIfNeeded 와 initializeDatabase 를 불러 줍니다.

소스는 아래와 같습니다.

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // The application ships with a default database in its bundle. If anything in the application
    // bundle is altered, the code sign will fail. We want the database to be editable by users,
    // so we need to create a copy of it in the application's Documents directory.    
    [self createEditableCopyOfDatabaseIfNeeded];
    // Call internal method to initialize database connection
    [self initializeDatabase];
    // Add the navigation controller's view to the window
    [window addSubview:navigationController.view];
    [window makeKeyAndVisible];
}


다른 Delegate Protocol 들은 메모리 해제 등의 상투적인 일을 하고, 이번 소스코드 분석이 AppDelegate분석에 초점을 맞춘것이 아니기 때무에 넘어 가도록 하겠습니다


그럼 createEditableCopyOfDatabaseIfNeeded 와 initializeDatabase Method를 살펴 보도록하겠습니다.

우선 createEditableCopyOfDatabaseIfNeeded를 보도록하겠습니다.
이름에서 보다 시피 EditableDatabase를 생성하는 함수 입니다.
Project를 살펴보시면, Resource->bookdb.sql 이라는 파일로 DB 파일이 존재 하는걸 알수 있습니다.
그런데 왜 Editable 한 DB 가 필요 하냐고 생각하실수 있습니다.
" 그럼 Bundle로 들어간 DB 는 Read-Only 란 말이냐? " 라고 생각하실수 있는데.
네 Read-Only 입니다. Application Executable File 과 번들들은 iPhone의 Sandbox 라는 Read-Only 영역에 들어 가있습니다.
Application 에서 파일 생성및 수정등의 Write 작업이 필요 한 경우에는 $(APP_HOME) Directory 밑에서 이루어져야 합니다.
즉 DB를 수정하기 위해서는 $(APP_HOME) 로 옮기는 작업이 필요 합니다.
이작업은 App.이 최초에 실행될때 한번만 이루어지게 됩니다.

자 그럼 소스를 보도록 하겠습니다.

// Creates a writable copy of the bundled default database in the application Documents directory.
- (void)createEditableCopyOfDatabaseIfNeeded {
    // First, test for existence.
    BOOL success;
    // NSFileManager 는 iPhone SDK 에서 File I/O 를 담당하는 Class 입니다.
    // 이 Class 의 object 를 통해 Editable한 DB 의 존재 유무를 확인하거나 Copy할수 있습니다.
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"bookdb.sql"];
    //NSFileManager를 통해 Writable DB 의 존재 유무를 확인합니다.
    success = [fileManager fileExistsAtPath:writableDBPath];
    // Writable한 DB 가 존재 할때는 아무작업도 하지 않습니다.
    if (success) return;
    // The writable database does not exist, so copy the default to the appropriate location.
    NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"bookdb.sql"];
   //NSFileManager를 통해 Copy 하는 코드 입니다
    success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
    if (!success) {
        NSAssert1(0, @"Failed to create writable database file with message '%@'.", [error localizedDescription]);
    }
}


자 그럼 initializeDatabase Method 를 살펴보도록하겠습니다.

initializeDabase를 이름만 보고 판단했을때는, DB object를 초기화 하는구나 싶습니다.
하지만 이게 다가 아닙니다 =ㅁ=.
DB object 를 초기화 하고, DB 로 부터 UI display에 필요한 최소한의 정보들을 Load하는 일을 함께 합니다.
이런 명명법은 딱히 좋아 보이지 않습니다.

그럼 소스를 보면서 살펴보도록 하겠습니다.

// Open the database connection and retrieve minimal information for all objects.
- (void)initializeDatabase {
    // BookList 를 관리할 NSMutableArray Object인 bookArray를 생성하고, AppDelegate Object에 링크 시킵니다.
    NSMutableArray *bookArray = [[NSMutableArray alloc] init];
    self.books = bookArray;
    [bookArray release];
    // The database is stored in the application bundle.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"bookdb.sql"];
    // sqlite3_open 을 통해 DB 를 오픈 합니다.
    // 실패한 경우의 코드를 봐도 알수 있듯이, 실패 하더라도 꼭 sqlite3_close를 불러줘야,
    // Memory Leak이 발생하지 않습니다.
    // Open the database. The database was prepared outside the application.
    if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
        // Get the primary key for all books.
        const char *sql = "SELECT pk FROM book";
        sqlite3_stmt *statement;
        //sqlite3 DB 파일에 쿼리를 던져주고, 처리 하기 위해서 sqlite3_stmt 라는 구조체를 사용합니다.
        //query 를 바인딩하고, sqlite3_step 등의 api 를 통해서 행을 처리하게 됩니다.
       // statement 를 이용해서 작업이 끝난 후에는 sqlite3_finalize를 불러줘서 Memory를 해제 해야합니다.
        // Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
        // The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.       
        if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
            // We "step" through the results - once for each row.
            while (sqlite3_step(statement) == SQLITE_ROW) {
                // The second parameter indicates the column index into the result set.
                int primaryKey = sqlite3_column_int(statement, 0);
                // We avoid the alloc-init-autorelease pattern here because we are in a tight loop and
                // autorelease is slightly more expensive than release. This design choice has nothing to do with
                // actual memory management - at the end of this block of code, all the book objects allocated
                // here will be in memory regardless of whether we use autorelease or release, because they are
                // retained by the books array.
                Book *book = [[Book alloc] initWithPrimaryKey:primaryKey database:database];
                [books addObject:book];
                [book release];
            }
        }
        // "Finalize" the statement - releases the resources associated with the statement.
        sqlite3_finalize(statement);
    } else {
        // Even though the open failed, call close to properly clean up resources.
        sqlite3_close(database);
        NSAssert1(0, @"Failed to open database with message '%s'.", sqlite3_errmsg(database));
        // Additional error handling, as appropriate...
    }
}


자 DB 초기화 하는 부분까지 살펴 봤고, 이제 Public Method 인 addBook 과 removeBook 에 대해서 살펴보도록하겠습니다.
addBook 은 Book object 를 받아서 class Book 의 method insertIntoDataBase를 통해 DB에 추가 해주고,
Book List 를 관리하는 bookArray에 추가를 해주는것으로 끝이 납니다.
간단함으로 소스는 살펴보지 않도록 하겠습니다.

removeBook 의 경우 IBAction 즉 IB 를 통해 정의된 Method 입니다.
IB 를 열어서 살펴보면 아시겠지만 Connection 은 이루어 져 있지 않습니다.


addBook 함수 처럼 왜 그냥 선언하지 않고 IB 를 통해서 정의 했는지는 잘 모르겠습니다.
혹시 아시는 분은 좀 알려주시기 바랍니다 :)
removeBook은 addBook과 반대의 일을 합니다. DB 로부터 삭제, bookArray로 부터 제거.
addBook 과 마찬가지로 코드가 간단하기 떄문에 소스를 샆펴보지 않겠씁니다.

자 이것으로 AppDelegate Class 의 구현을 모두 살펴 보았습니다 :)

[2-b DB- Class Book]

자 그럼 Book Class 에 대해서 살펴 보도록 하겠습니다.

Book Class 의 선언은 다음과 같습니다.

@interface Book : NSObject {
    // Opaque reference to the underlying database.
    sqlite3 *database;
    // Primary key in the database.
    NSInteger primaryKey;
    // Attributes.
    NSString *title;
    NSDate *copyright;
    NSString *author;
    // Internal state variables. Hydrated tracks whether attribute data is in the object or the database.
    BOOL hydrated;
    // Dirty tracks whether there are in-memory changes to data which have no been written to the database.
    BOOL dirty;
    NSData *data;
}

// Finalize (delete) all of the SQLite compiled queries.
+ (void)finalizeStatements;

// Creates the object with primary key and title is brought into memory.
- (id)initWithPrimaryKey:(NSInteger)pk database:(sqlite3 *)db;
// Inserts the book into the database and stores its primary key.
- (void)insertIntoDatabase:(sqlite3 *)database;

// Brings the rest of the object data into memory. If already in memory, no action is taken (harmless no-op).
- (void)hydrate;
// Flushes all but the primary key and title out to the database.
- (void)dehydrate;
// Remove the book complete from the database. In memory deletion to follow...
- (void)deleteFromDatabase;
@end

Attribute 의 다른 부분들은 직관적으로 아실꺼라 생각합니다. 눈여겨봐야 할것은
hydrated , dirty 가 아닌가 생각합니다.

Mobile 환경에서의 부족한 메모리를 효율적으로 상용하기 위해 Book Class 의 경우 Cache Concept을 사용하였습니다.
기본적으로 Display 하기에는 title 만 있으면 됩니다.
그래서 Class Appdegate 의 메소드 initializeDatabase에서 살펴 봤듯이 최초에 key와 title만 가져옵니다.
이때 hydrated = NO 로 세팅하고 실제 copyright 등의 data 가 필요 할때 DB 로 부터 가져와서 채워주게 됩니다.
이때 hydrated = YES 로 표기 하게 되는것이죠.
그리고 Application Delegate Protocol 중 하나인 applicationDidReceiveMemoryWarning:(UIApplication *)application 이 불러 졌을때
hydrated 된 object 들을 dehydrate 해줌으로써 메모리 효율을 늘려주는식의 구현이 되어 있습니다.

그리고 dirty 필드는 edit 가 일어 났을때 ( 이때는 항상 hydrated=YES 임) 바로 DB 에 써주는것은 성능에 좋지 않기때문에
기왕 Memory에 관리 하고 있는 book object 에만 기록하고 DB 에는 나중에 쓰는 delayed-Write를 구현하기 위한 attribute입니다.

applicationDidReceiveMemoryWarning 프로토콜이 불러졌을때 혹은 App.을 종료할때 Write 해줌으로써 프로그램 성능을 높이는 형태입니다.

자 그럼 Method 들에대해서 살펴 봐야 하는데 그전에 Class Book 에서 sqlite3_statement 를 관리 하는 구조를 살펴 보도록하겠습니다.

book.m 파일 상단에 보면 아래와 같은 코드를 볼수 있습니다.

static sqlite3_stmt *insert_statement = nil;
static sqlite3_stmt *init_statement = nil;
static sqlite3_stmt *delete_statement = nil;
static sqlite3_stmt *hydrate_statement = nil;
static sqlite3_stmt *dehydrate_statement = nil;

이 코드는 book object 별로 db 제어를 해야 하는데, object 별로 statement 를 관리하면, 같은 statement 가 여러번 할당되어
낭비를 막고자 작성된 코드 입니다.

사견을 덧붙히자면, DB 제어를 한곳으로 모았다면 사실 이런 귀찮은 방법을 써가면서 메모리 효율을 생각할 필요까진 없지 않았을까 생각합니다.

여튼 최초에 nil 로 되어 있다가 최초에 statement 가 할당될때, 초기화 하고 ( 사용하지 않을수도 있기 때문에 )
프로그램 종료시에 해제 해주게 됩니다.

자 그럼 Method들을 살펴 보도록 하겠습니다.

- (id)initWithPrimaryKey:(NSInteger)pk database:(sqlite3 *)db

당연히 book object 를 initiailize 하는 함수 입니다 ㅎ

그럼 코드를 살펴 보도록 하겠습니다

        if (init_statement == nil) {
            // 앞서 설명 드린대로 init_statement는 global variable 이고, 최초에 사용할때 이를 할당하게 됩니다.
            // query 의 마지막 부분 pk=? 를 보시면 pk 는 primary key field 이고 value가 ? 인것을 알수 있습니다.
            // '?' 는 printf 같은 함수를 쓸대 '%s'등 과 같다고 보시면 될듯합니다 값은 sqlite3_bind_xx 함수로 채우게 됩니다.
// Note the '?' at the end of the query. This is a parameter which can be replaced by a bound variable.
            // This is a great way to optimize because frequently used queries can be compiled once, then with each
            // use new variable values can be bound to placeholders.
            const char *sql = "SELECT title FROM book WHERE pk=?";
            if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
                NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
            }
        }
        // For this query, we bind the primary key to the first (and only) placeholder in the statement.
        // Note that the parameters are numbered from 1, not from 0.
        // query 의 ? 부분을 채워주는 부분
        sqlite3_bind_int(init_statement, 1, primaryKey);
        if (sqlite3_step(init_statement) == SQLITE_ROW) {
            self.title = [NSString stringWithUTF8String:(char *)sqlite3_column_text(init_statement, 0)];
        } else {
            self.title = @"No title";
        }
        // 재사용을 위해 reset 을 해주는 아마도 bind를 해제 하는것 같습니다.
        // Reset the statement for future reuse.
        sqlite3_reset(init_statement);

- (void)insertIntoDatabase:(sqlite3 *)db
- (void)deleteFromDatabase

 이 Method 들은 init함수와 마찬가지로 DB 제어 를 통해서 read/write 하는 부분들이기때문에 initWithPrimaryKey 와 모습이 거의 비슷합니
다. 그러므로 생략하고 넘어 가도록 하겠습니다.

- (void)hydrate
- (void)dehydrate

이 Method 들 역시 앞서 설명드린, Cache 컨셉과 , Dirty Filed 컨셉을 위해 DB 와 Book Object 연동을 다룬 Method이기때문에
앞의 설명을 숙지하고 코드를 보시면 충분히 이해하실수 있따고 생각하여 넘어가도록 하겠습니다.

이것으로 SQLibteBooks DB 제어관련 소스 리뷰는 마치도록 하겠습니다

장문의 글 읽어주셔서 감사합니다 :)


프로그램을 개발하다 보면, 특정 Class 의 선언부 혹은 특정 함수의 구현부분을 봐야할 필요가 있습니다

vim 에서 개발할때는 ctag 라는 프로그램을 통해서 tag를 생성하고, vim 에서 그 태그를 사용하여

정의부분 혹은 구현부분을 탐색할수 있었습니다.

Xcode 에서도 그런것이 가능할까요?

당연히 가능합니다 :) 가능하지 않았다면 이글을 쓰지도 않았겠죠 ㅎㅎ

클래스 이름 혹은 함수 이름을 마우스로 드래그 하여 선택을 한후

Control + Click ( 혹은 Right-Click)을 하면 옵션 Popup 이 뜨는데 그중 'Jump To Definition'이 바로 그기능을 하는 녀석입니다 ㅎ

참 쉽죠 ? :)

 
Keyboard
Keyboard / cc by i_yuda



저는 이름만 들어 도 즐거운 해피해킹 키보드를 2년째 사용하고 있습니다.

제가 살때만해도 20만원 초반대 였던것 같은데..

미친 환율 덕에 40만원에 육박한다는 소문을 들은적 있는것 같네요 ㅜ.ㅜ


회사 내의 다른 개발자 분들이 간혹 키보드에 너무 큰돈들이는거 아니냐고 그러시는데..

우리 밥줄인데.. 이정도는 투자 해야죠 ㅎ

물론 저렴한건 아니지만 술 몇번 혹은 한번 안먹으면 구입할 수 있으니 꼭 비싼것만은 아니라고 생각합니다.
( 제가 구매 했을떄 기준으로 ㅎㅎ )


저는 임베디드 리눅스 위에서 STB Application ( C Language )를 개발하면서 밥 벌어 먹고 살고 있습니다.


우리 회사에서는 Eclipse 등의 IDE 툴 대신 고전적인 vim + Makefile 시스템을 이용하고 있습니다.

누군가 'vim' 을 제대로 습득하고 나면.. 어떤 Editor 혹은 IDE 보다 편하다고 하는데.. 맞는 말인것 같습니다.

vim 을 editor 로 사용하면 키보드에서 손을 떌일이 없습니다..

Web 서핑이나 이메일을 사용할때를 제외하고는 거의 키보드에 손을 떌일이 없습니다.


저같이 키보드가 마우스 보다 더 편하고, 키보드에서 마우스로 손을 옮기는것조차 귀찮아 하는 사람에게는 더할나이 없이 좋은 툴이죠.

여기서 키보드를 HHKB 로 교체 하게 되면 키보드위에서 움직임도 최소화가 됩니다.

Function Key 들이나 기타 키들이 Fn 키와의 조합으로 되어 있고, 모든키가 손목을 움직이지 않고 사용이 가능합니다.

(여담이지만 19인치 모니터 2개를 듀얼로 쓰다가 머리를 움직이는게 싫어서 24인치를 구입했습니다 ㅎㅎ )


단점이라면 PageUp/PageDown, Home 키를 사용하는게 직관적이지 않다는것과, 방향키마져도 Fn키와의 조합을 사용해야 하기 때문에
매우 불편하다는 점입니다.
사실 생각해보면 방향키가 있는 HHKB lite 의 경우 방향키를 위해 손목을 움직여야 할것 같으니, 또 귀찮아 할지도 모르겠근여ㅎㅎ


저처럼 vim 을 주력 EDITOR로 삼는 분들께는 꼭 추천하고 싶은 키보드라고 생각합니다.



여담으로 이글을 읽으시면 이자식 완전 게으름 뱅이구나 라고 생각하실지도 모르겠는데 ;ㅁ;,
전 개발자는 좀 게을름을 가지고 있어야 한다고 생각합니다 ㅎㅎ
여기서 게으르다는건 무조건 일은 안한다가 아니라 같은 일은 하더라도 얼마나 효율적으로 할수 있는가를 고민하는 자세라고 생각합니다.
제가 너무 부지런하면 비효율적이고 불편하지만 그냥 묵묵히 쓰지 않았을까요 ?
게으르다는건 효율을 추구한다는것과 동의어가 아닐까 하고 우겨봅니다 ㅋㅋㅋ











1. Trac 을 통해서 새 프로젝트 생성하기

trac-admin [projectpath] initenv



2. Admin 계정 설정하기

milestone이나 component 등을 설정하기 위해서는 admin 권한이 필요하다.
TRAC에 접속해서 wiki 페이지를 잘 찾아 보면 추가하는 방법이 나와있다.
(http://localhost:8000/my-project/wiki/TracAdmin 문서에 설명이 되어있다.)

방법은 아래 구문과 같다.
trac-admin <my-project pth> add <id> <권한> [<권한>]

예는 아래와 같다.
trac-admin c:\projects\trac\my-project permission add alones TRAC_ADMIN

※ 추가 가능한 권한은 아래와 같은 명령으로 알 수 있다.
trac-admin c:\projects\trac\my-project permission list alones


3. Web에서 Admin 활성화 시키기

{$TRAC_HOME}/conf/trac.ini 파일을 수정해야한다

[components] 옵션에

webadmin.* = enabled 를 추가 해주고 접속 하면 된다 :)

[components] 옵션이라는 항목이 없으면 직접 추가해주면 된다 :)


3. Trac Backup

$ cd /var/lib/trac
$ ls
angelos production test
$ sudo trac-admin angelos hotcopy /media/외장\ 디스크/백업/trac/angelos
Hotcopying /var/lib/trac/angelos to /media/외장 디스크/백업/trac/angelos ...
Hotcopy done.
$ sudo trac-admin production hotcopy /media/외장\ 디스크/백업/trac/production
Hotcopying /var/lib/trac/production to /media/외장 디스크/백업/trac/production ...
Hotcopy done.


본자료는 iphonedevcentral.org에 공개되어있는 Tutorial Video에 의거하여 작성되었음을 알려드립니다.
Video Link : http://iphonedevcentral.org/tutorials.php?page=ViewTutorial&id=49&uid=27391603


리눅스 환경에서 C Programming 을 하면서 밥 벌어 먹고 살다 보니, vim 과 Makefile System을 이용한 CUI 환경에 너무 익숙해져 있어, Xcode의 GUI 환경은 무척이나 불편하고 ㅎ, 생소한점이 많다.

거기다가 언어도 한번도 해본적 없는 Obj-C 라니 orz. 이래저래 공부해야 할것이 태산이다.

이랬거나 저랬거나 언제나 그래왔듯이 새로운 언어와 플랫폼을 공부할때는 역시나 Hello World 를 짜면서 이래저래 살펴 보는게 좋은것 같다.

역시나 Hello World 프로그램 짜는게 가장 자료가 많은것 같다 ㅋㅋㅋ

You Tube 에도 강좌가 올라와 있고 ( 저해상도의 압박으로 보기가 많이 힘들다 )

이런저런 카페에도 글이 많다.

그중에서 가장 괜찮은게 위에 링크 걸려 있는 자료가 아닌가 싶다.

Hello World 프로그램 관련 자료는 꽤나 오래전에 작성된것이 많아 현재 Xcode 버전과도 맞지 않는 점이 많다.

내가 내 블로그에 이런 글을 쓰고는 있지만, 사실 내 글과 링크된 동영상 둘중 하나만 봐야만 한다면 난 당연히 동영상을 볼것임으로 읽는 분들은 꼭 동영상을 한번 보시길 적극 추천한다.

지금 만들어 볼 프로그램은 Button을 클릭하면 Hello World 메세지가 나타나는 프로그램이다.

자 그럼 시작해보자

1. Project 생성하기

사용자 삽입 이미지

위의 이미지에 올라가 있는 것처럼 File->New Project 클릭혹은 단축기를 이용하여, 프로젝트 생성을 시작할수 있다.

사용자 삽입 이미지


New Project 를 실행하면 Iphone 용 Project 혹은 Mac OS X 용 Project 를 선택 할수 있는데,
Iphone 용 Project 중 Window-Based App. 를 선택하면 된다.

하단에 설명이 나와 있는것처럼 Window-based App.는 어떠한 형태의 App.도 될수 있는 가장 원시적인 형태의 App. 라고 볼수 있다. 그외의 타입은 Window-based App. 에서 목적에 따라 기초적인 코드가 자동으로 작성되는 Project라고 생각하면 되겠다.

사용자 삽입 이미지

Project 이름을 결정하고 ( 당연히 Hello World ) 로 하고, 'Save'버튼을 누르면 Project 생성이 완료된다.

2. Interface Builder를 이용한 Inteface 설계및 Class 생성하기

사용자 삽입 이미지

Project 생성이 완료 되고 나면 Interface Builder(이하 IB) 를 통해서 UI 를 디자인 하고 해당 Class 를 등록해야한다.

현재 Project의 UI design 을 위해 IB 를 실행시키기 위해선 .xib 파일을 더블 클릭 하면 된다.

좌측 Overview 칼럼에 Project의 하부 그룹들중에 Resources 을 열어 보면 MainWindow.xib 를 찾을 수 있다.

사용자 삽입 이미지

위의 그림은 IB 를 실행 시킨 모습이다. 최초에 IB 를 실행하면 우측에 있는 윈도우가 없다 ( 나는 없었다 ㅜ.ㅜ )

Tools -> Identity Inspector 를 클릭하면 짠하고 나타날것이다 :)

제일 먼저 해야 할일은 윈도우에 View 를 할당하는 고, View를 컨트롤 하게 될 클래스를 설정하는것이다.

사용자 삽입 이미지

Library Window 를 살펴 보면 View 라는 컴포넌트가 있다. View를 드래그 하여 Window 창에 놓자.

드래그를 하면 최초그림과 달리 Window가 파랗게 변했음을 알수 있다.

파란색으로 색칠된 영역만큼 방금 할당한 View가 보여지게 된다. 영역의 크기는 변경가능하다.

이제 View를 넣었으면 Button 과 Hello World 를 표기할 Label ( Textbox 정도로 생각하면 되겠다 ) 을

생성할차례다


사용자 삽입 이미지

라이브러리 윈도우를 찾아오면 Round Rect Button 과 Label Component 가 있다. 각 컴포넌트를 View에 적당한 위치에 넣고 크기를 알맞게 적용 하면된다.

라벨과 버튼을 각각 더블클릭하면, 초기 텍스트를 변경할수 있다.

자 이제 Button 과 Label Component 를 처리 할 Class를 정의해야한다.

Window 창에서 UIView영역을 클릭하고 Identity 창 중 네번째 Tab(i) 를 클릭해보면

클래스 이름및 속성을 설정 하는 부분이 나온다.

사용자 삽입 이미지

Class 이름을 정의하고, Class Action ( Method 라고 생각하면 되겠다 ) 에 Button Event를 처리할
Action을 추가 하고, Class Outlets (Member Var 이라고 생각하면 되겠다 ) 에 Label과 연결된
Outlet을 추가 하자.

이때, 선언된 Outlet은 Label과 연결될것이기 떄문에 Type에 UILabel 이라고 써줘야 한다.

자 이제 UI Component(Label, Button)과 Class 를 연결할일만 남았다.

Identity Window 의 두번째 Tab (->) 으로 이동하자

사용자 삽입 이미지

위의 그림과 같이 Outlet 혹은 Action 을 Window창에 보이는 컴포넌트와 시각적으로 연결 시킴으로써, Class 와 Component를 연결시킬수 있다.
Outlet : mainText -> Label, Action : showText -> Button
이렇게 연결 시키면 된다.
showText를 연결시킬때는 어떤 이벤트에 대해서 연결한것인지 묻는데
'Touch Up inside'를 선택하도록 하자.

자 드디어 IB 에서 할일은 거의 끝났다.

마지막으로 File->Write Class File 을 선택하여 Xcode에 생성되어있는 Project 와 연결 시키면 된다.

'Write Class File' 을 선택하면, 저장여부를 묻고 Xcode에 열려 있는 Project에 추가 하겠냐는
창이 나오는데, 저장한후 HelloWorld Project에 추가 하면 된다.

자 이제 IB 를 종료 하고 Xcode로 돌아가자.
 
3. 코드 작성하기

사용자 삽입 이미지

Xcode로 돌아 가면, 아까 IB 에서 생성한 Class 가 Products 그룹에 있는걸 알수 있다.
우리가 구현하는 프로그램은 Button 과 Label 만 사용하는것이고, Class 와 Component가 연결된 상태이기 때문에 MainView Class만 수정하면 된다.

.m 과 .h 파일을 Classes 에 옮긴후 ( Class 니까:) ) , 수정하도록 하자

MainView.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface MainView : UIView /* Specify a superclass (eg: NSObject or NSView) */ {
    IBOutlet UILabel *mainText;
}

@property (nonatomic, retain) UILabel *mainText;

- (IBAction)showText;
@end


MainView.m

#import "MainView.h"

@implementation MainView

@synthesize mainText;

- (IBAction)showText {
  mainText.text = @"Hello World"; 
}
@end


빨간색 으로 처리된 부분이 새로 추가된 코드 부분이다.

생성된 MainView Class 에는 Super Class 선언이 빠져 있음으로 Super Class선언을 해주어야 한다.
MainView Class는 View Component 에 연결된 Class 이고, View Component는 UIView Class를
가져야 함으로 Super Class를 UIView 로 설정하도록 한다.

Property 와 Synthesize 부분은 아이님의 블로그에 자세하게 설명 되어 있으니, 참고 하길 바란다.

자 이제 모든 작업이 끝나고 기쁘게 실행 해보면 된다.


4. 실행하기


사용자 삽입 이미지

Xcode 창 상단에 Build and Go 를 선택하면 Build 한후 실행을 할수 있다.

그전에 Target이 iPhone Simulator로 설정 되어 있는지 확인해야 한다.

아래 화면은 Hello World 가 실행 된 모습이다.

사용자 삽입 이미지



어떤 프로그래밍 공부건 Hello World 를 짜보는것으로 그것을 할줄 안다고 자신있게 말할순 없다.
하지만 Hello World 프로그램을 짜보면 많은것을 알수 있다.

방금 해본 프로그램에서도 아주 기초적이지만, IB 를 사용하는법과,
Objective-C 에서의 Class 를 사용하는 법을 훓어 볼수 있었지 않나 생각한다.



+ Recent posts