- Flughafen Zurich ( 취리히 공항) -

귀차니즘으로 스캔안하고 버티던 사진들도 정리 할겸 :)

스위스 여행에 대한 얘기를 써볼까 합니다.


반농담 반 진담으로 ' 나중에 먹고 살일 없으면 스위스에서 관광 가이드 해도 돼겠다 ' 라고

주위 친구들에게 얘기 합니다 -_-*

제가 스위스에 체류한 기간이 총 3달 정도 되는것 같습니다 .


언제나 스위스에 들어 갈때 취리히 공항을 가는데..

취리히 공항에는 3 스위스 프랑 ( 정확한 기억은 가물 하네요 이정도 했던것 같습니다 ㅜ.ㅜ ) 정도 내고

활주로등 비행기 이착륙을 볼수 있는 곳이 있습니다.

활주로를 배경으로 인물 사진을 찍어도 총기류를 소지한 경비원들이 제재하는 대한민국 인천 국제 공항 과는 무척 대조적인 모습이

취리히 공항의 가장 인상적인 면모가 아니었나 생각합니다 :)



- 취리히 공항 전망대에서 -


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

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

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



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

최근에는 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 제어관련 소스 리뷰는 마치도록 하겠습니다

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

Double Click 을 하게 되면 기본적으로 실행되는 프로그램이 있습니다.

가끔 그 프로그램을 변경해야 할 경우가 있는데,

예를 들어서 저같은 경우 그림파일을 더블클릭하면 주로 Viewer 가 실행되길 원하지 Photoshop이 실행되길 원하지 않습니다.

자 그럼 살펴보도록 할까요

Mac OS X 의 경우 윈도우 보다 좀더 쉬운것 같습니다.

윈도우 같은 경우 ' 속성 ' 에서 탭을 고르고 이래저래 해야 하는데

아래 스샷에서 보이는 바와 같이 Mac OS X에서는 윈도우의 '속성'에 해당하는 Get Info ( 정보입수 ) 만 열면 바로 보입니다.



위에서 보시는것 처럼 다음으로 열기에서 프로그램을 변경하시고,

이파일 뿐만 아니라 같은 타입의 파일을 열때 항상 그 프로그램이 실행되길 원하시면

' 모두 변경 ' 아이콘을 클릭해주면 짜잔하고 변경됩니다 :)


저는 개인 일정 관리를 Google Calendar 에서 합니다.

제가 좋아하는 MP3 기기인 iPod 을 사고 나서 부터 문제가 생겼습니다.

iPod 에서 Calendar 기능을 지원하는데.. 이렇게 되면 Google Calendar 와 iPod 의 Calendar 이렇게 2개를 관리 해야 한다는건

귀차니즘의 황제인 저에게 참을 수 없는 일이었지요 ;ㅁ;

그때 주목한것이 iCal 이라는 Apple 에서 제공하는 소프트웨어와 iPod 이 연동 가능하다는것입니다.


여기서 제 첫번째 고민이 시작되었습니다.

"그럼 Google Calendar 를 버리고 iCal 로 일정관리를 해야 한다 말인가" 라는 고민입니다.

iCal 만 사용했을떄의 단점을 생각해보니

1. 다른 컴퓨터에서 사용할수 없다.
2. Google Calendar에 저장되어 있는 일정들을 옮겨야한다.

첫번째 것은 사실 iPod을 항시 소지 하기 떄문에 큰 문제는 없습니다.
하지만 귀차니즘의 황제인 저의 문제는 2번이죠 ㅎㅎ

그래서 구글링을 해보았습니다.


역시 구글에 답이 있었습니다.

CalDav 라는 기능을 통해서 Google Calendar 와 iCal의 양방향 연동이 가능합니다.
( CalDav 를 통하지 않고, Calendar 구독을 한다면 Read-Only 만 됩니다. )


주저리 주저리 말이 많지만,

iCal 에서 Preference -> Account, Account를 추가하는데.

계정은 구글 계정, 서버는 http://www.google.com/calendar/dav/YOURMAIL@DOMAIN.COM/user

이렇게 쓰면 된다고 합니다.



자 계정 추가도 완료 했고 iCal 을 볼까요 ? ㅎ


iCal 의 왼쪽 Tab 을 보면 이렇게 계정이 추가 된것을 알수 있고, 수정, 삭제 추가 다 정상 동작합니다.

마지막으로 iPod 과 iCal 만 연동 시키면 제가 원하던걸 마무리 할수 있겠군여 :)



iTunes에 들어가서 보니

iCal 항목에 Google계정이 추가 된것을 알수 있습니다.

항목에 체크 하고, ' 동기화 ' 해주면 비로서 Google Calendar & iCal & iPod 의 동기화가 완료 됩니다.

하기 전에는 귀찮았지만, 역시 하고 나니 뿌듯합니다 :)



프로그램을 개발하다 보면, 특정 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로 삼는 분들께는 꼭 추천하고 싶은 키보드라고 생각합니다.



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











파이어 폭스 를 활성화 시키고

사과 아이콘을 클릭하면

'시스템 종료' 등이 두개가 나온다..

다른 소프트웨어는 괜찮은걸 보면  Firefox 문제 일수도 있는것 같고, ㅎㅎㅎ

여튼 짜증난다기 보다 왠지 반갑다 , 애플 개발자 혹은 Firefox 개발자도 사람이라는 증거 ㅎㅎㅎ
회사에서 원래 Linux PC 를 데스크탑으로 사용하다가

Macbook 을 사고 나서 부터는 Mac을 주로 사용하기로 마음먹었다 ㅎㅎ

우선 Linux PC 에 저장되어 있는 메일을 가져와야 하는데..

thunderbird 는 왜 그렇게 하는지 모르겠지만.. 배포판 ( Ubunu, Fedora, OS x ) 마다 메일이 저장되는 장소 혹은 구조가 좀 다르다.




계정 설정에 Local Folder 부분을 클릭해서 보면 저장되어 있는 장소가 어디 인지 알수 있다.

OS 별 기본 디렉토리

Mac OS X : /$HOME/Library/Thunderbird

Ubuntu : /$HOME/.mozilla_thunderbird

Fedora : /$HOME/.thunderbird


우선 리눅스 피시의 저장장소를 열어 보면 아래와 같은 파일과 폴더가 있는것을 볼수 있다.



우선 쉽게 얘기하면, XXXXX.default 파일과 profiles.ini 만 압축해서 Max OS X 로 옮겨주기만 하면된다.

그런데 Max OS X 의 폴더를 보면 Folder 구조가 좀 다르다


profile.ini 는 존재 하지만 XXXX.default 폴더가 없다.

Max OS X 는 각 profile에 해당하는 폴더를 Profiles에 저장하도록 구성되어있기 때문이다.

사실 Profile 에 해당하는 폴더에 대한 Path는 profiles.ini 에 저장되기때문에

Profiles 에 넣지 않고 profiles.ini 와 같은 폴더에 둔다면 따로 해줄일이 없지만.

Profiles 안에 넣기 위해서는 profiles.ini 의 path 부분을 바꿔 줘야 한다.


profiles.ini 를 수정하지 않고 그냥 Profiles 폴더에 넣게 되면 Thunderbird 가 실행되지 않으니

이점을 유의 해야 한다.






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.



Mac OS X 는 Unix-like System 중의 하나인 BSD 를 기반으로 한 운영 체재다.

아무래도 임베디드 리눅스 플랫폼위에서 프로그래밍 하는것을 밥먹고 살다 보니 이런 기반 자체가 친숙하게 다가온다.

자 그럼 Unix-like System 에서 빠질수 없는 쉘을 한번 살펴 보자.


쉘을 써보려면 당연히 Terminal 이 필요 하다.

Terminal 은 위의 스샷과 같이 Application-> Utility 에 존재 한다

> export
declare -x SHELL='/bin/bash'

위에서 보는 바와 같이 Mac OS X 의 default Shell은 Fedora나 Ubuntu 와 마찬가지로  Bash Shell 이다.

Bash shell 은 bashrc 라는 파일을 이용해서 Customizing 할 수 있다.

/etc/bashrc 라는 파일이 모든 유저에게 적용되는 bashrc 파일이고

Ubuntu 나 Fedora 에서는 $HOME/.bashrc 파일을 생성 ( 대부분 이미 생성되어 있다 ) 하여 유저별 Customizing 이 가능하다.

내가 이글을 쓰고 있다는 얘기는 $HOME/.bashrc 가 동작하지 않는 다는 얘기다.

.bashrc 를 불러 주는 건 .bash_profile 이다..

그래서 .bash_profile 을 찾아 보았는데 , 이것도 없다 ㅎㅎ

# .bash_profile

if [ -f ~/.bashrc]; then
        . ~/.bashrc
fi


위와 같이 .bash_profile 을 생성해주면

이제 Terminal 을 실행 실킬때 bashrc 를 실행 해주게 된다. :)
 

+ Recent posts