본문 바로가기

프로그래밍/아이폰 프로그래밍

22. SQLite 사용하기




이번 시간에는 SQLite를 사용해서 DB 설계 및 데이터 저장과 불러오기 작업을 해보겠습니다.

우선 프로젝트 생성을 해보겠습니다.
기본적으로 테이블 뷰가 들어있는 Navigation-based Application을 사용하도록 하겠습니다.



SQLite 데이터베이스 작업은 터미널에서 하게 되는데, 오류 없이 입력하기가 쉽지 않으므로 텍스트 편집기에서 명령을 입력한 뒤에 복사하고 붙여넣는 방법을 사용하겠습니다.


응용 프로그램에 들어있는 텍스트 편집기에서 다음과 같이 입력합니다.

CREATE TABLE tblMemoPad (

MP_Index INTEGER PRIMARY KEY autoincrement,

MP_Title TEXT,

MP_Content TEXT,

MP_Date TEXT

);


INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date)

VALUES('Sample Data 1','Content 1','2010-01-01 00:00:00');


INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date)

VALUES('Sample Data 2','Content 2','2010-01-02 00:00:00');


INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date)

VALUES('Sample Data 3','Content 3','2010-01-03 00:00:00');


INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date)

VALUES('Sample Data 4','Content 4','2010-01-04 00:00:00');


INSERT INTO tblMemoPad(MP_Title, MP_Content, MP_Date)

VALUES('Sample Data 5','Content 5','2010-01-05 00:00:00');






이제 터미널 프로그램을 실행합니다.

터미널 프로그램은 응용프로그램의 유틸리티 폴더안에서 찾을 수 있습니다.




터미널을 실행시키고 프로젝트가 들어있는 폴더로 접근합니다.

"cd /Users/massv/Documents/MemoPad/"

(각 유저들마다 경로가 다르니 프로젝트 폴더의 정보입수를 보고 해당경로를 적어 넣으세요.)


그리고 "ls" 명령어를 실행하여 폴더에 들어있는 파일 및 폴더 보기를 합니다.


이제, "sqlite3 MomoPad.db" 명령어를 입력하여 SQLite를 실행합니다.



아까 텍스트 편집기에 입력 해 놓은 것을 복사-붙여넣기 합니다.





데이터가 잘 들어가있는지 확인하기 위해서 다음과 같이 쿼리를 사용합니다.

"select * from tblMemoPad;"


데이터가 나오면 DB가 잘 만들어진 것입니다!!












이제 DB에 입력된 데이터를 불러오는 작업을 해 보겠습니다.



SQLite를 사용하기 위해서는 라이브러리를 추가해야 합니다.


Frameworks를 우클릭 후, Add -> Existing Frameworks 를 선택. 

위의 사진에 보이는 libsqlite3.0.dylib 를 선택합니다.




그리고, 아까 프로젝트 폴더안에 생성된 db파일을 리소스 폴더 안에 끌어다 놓습니다.






이제 데이터 베이스에서 읽어온 데이터를 담을 수 있는 오브젝트를 만들어 보겠습니다.

위와 같이 파일을 새로 만듭니다.




이름은 위와 같이 하겠습니다.









MemoData.h




#import <Foundation/Foundation.h>



@interface MemoData : NSObject {


NSInteger mIndex;

NSString *mTitle;

NSString *mContent;

NSString *mDate;

}


@property (nonatomic, assign) NSInteger mIndex;

@property (nonatomic, retain) NSString *mTitle;

@property (nonatomic, retain) NSString *mContent;

@property (nonatomic, retain) NSString *mDate;


- (id)initWithData:(NSInteger)pIndex Title:(NSString*)pTitle Content:(NSString*)pContent Date:(NSString*)pDate;


@end





MemoData.m





#import "MemoData.h"



@implementation MemoData


@synthesize mIndex, mTitle, mContent, mDate;


- (id)initWithData:(NSInteger)pIndex Title:(NSString *)pTitle Content:(NSString *)pContent Date:(NSString *)pDate {

self.mIndex = pIndex;

self.mTitle = pTitle;

self.mContent = pContent;

self.mDate = pDate;

return self;

}


- (void)dealloc {

[super dealloc];

}


@end








이제 데이터를 가져와 보겠습니다. 

가장 먼저 실행되는 곳은 MemoPadAppDelegate.m파일의 application didFinishLaunchingWithOptions: 입니다.


이곳에서 데이터베이스 초기화를 하겠습니다.







MemoPadAppDelegate.h



#import <UIKit/UIKit.h>

#import <sqlite3.h> // SQLite 사용하기 위한 헤더 파일을 임포트


@interface MemoPadAppDelegate : NSObject <UIApplicationDelegate> {

    

    UIWindow *window;

    UINavigationController *navigationController;

NSString *DBName; // 데이터 베이스 이름

NSString *DBPath; // 데이터 베이스 경로

NSMutableArray *DBData; // 데이터 베이스에서 읽은 데이터를 가져온다.

BOOL isFirstTimeAccess; // 데이터 베이스에 처음으로 접속하는지 여부

}


@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;


@property (nonatomic, retain) NSString *DBName;

@property (nonatomic, retain) NSString *DBPath;

@property (nonatomic, retain) NSMutableArray *DBData;

@property (nonatomic, assign) BOOL isFirstTimeAccess;


- (void)checkAndCreateDatabase; // 파일이 있는지 체크하고 없으면 생성하는 메소드

- (void)readMemoFromDatabase; // 데이터베이스에서 데이터를 읽어오는 메소드


@end







MemoPadAppDelegate.m




#import "MemoPadAppDelegate.h"

#import "RootViewController.h"

#import "MemoData.h"


@implementation MemoPadAppDelegate


@synthesize window;

@synthesize navigationController;


@synthesize DBName, DBPath, DBData, isFirstTimeAccess;



#pragma mark -

#pragma mark Application lifecycle


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    

    // Override point for customization after application launch.

    

// 최초 엑세스 인가?

self.isFirstTimeAccess = YES;

// 앱의 파일 시스템 경로를 구한다.

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDir = [documentPaths objectAtIndex:0];

// 데인터베이스 이름과 경로를 저장한다.

self.DBName = @"MemoPad.db";

self.DBPath = [documentsDir stringByAppendingPathComponent:self.DBName];

[self checkAndCreateDatabase];

//[self readMemoFromDatabase];

    // Add the navigation controller's view to the window and display.

    [self.window addSubview:navigationController.view];

    [self.window makeKeyAndVisible];


    return YES;

}


- (void)checkAndCreateDatabase {

NSFileManager *fileManager = [NSFileManager defaultManager];

// 해당 경로에 데이터 베이스 파일이 존재하는지 확인한다.

if ([fileManager fileExistsAtPath:self.DBPath]) {

return;

}

// 데이터 베이스 파일이 없다면 번들(앱에 포함된 리소스)에서 데이터 베이스 파일을 복사한다.

else {

NSString *databasePathFromApp = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:self.DBName];

[fileManager copyItemAtPath:databasePathFromApp toPath:self.DBPath error:nil];

[fileManager release];

}

}


- (void)readMemoFromDatabase {

sqlite3 *database;

// 데이터를 담을 오브젝트인 DBData 초기화한다.

// 데이터베이스에 처음 엑세스하는 것이라면 alloc으로 메모리를 할당한다.

if (self.isFirstTimeAccess == YES) {

self.DBData = [[NSMutableArray alloc] init];

self.isFirstTimeAccess == NO;

}

// 처음 엑세스하는 것이 아니면 removeAllObject DBData 초기화 한다.

else {

[self.DBData removeAllObjects];

}

// 데이터 베이스 파일을 연다.

if (sqlite3_open([self.DBPath UTF8String], &database) == SQLITE_OK) {

// SQL명령을 실행한다.

const char *sqlStatement = "SELECT * FROM tblMemoPad ORDER BY MP_Index DESC";

sqlite3_stmt *compiledStatement;


if (sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {

// SQL 명령어를 실행하여 얻은 결과를 하나씩 읽는다.

while (sqlite3_step(compiledStatement) == SQLITE_ROW) {

NSInteger aIndex = sqlite3_column_int(compiledStatement, 0);

NSString *aTitle = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];

NSString *aContent = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];

NSString *aDate = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];

   

  // 읽어온 데이터를 사용하여 MemoData 오브젝트를 생성한다.

  MemoData *md = [[MemoData alloc] initWithData:aIndex Title:aTitle Content:aContent Date:aDate];

  NSLog(@"%d / %@ / %@ / %@", aIndex, aTitle, aContent, aDate);

  // MemoData 오브젝트를 DBData 배열에 추가한다.

  [self.DBData addObject:md];

  [md release];

}

}

else {

printf("could not prepare statement: %s\n", sqlite3_errmsg(database));

}

// SQL 명령을 종료한다.

sqlite3_finalize(compiledStatement);

}

// 데이터베이스를 닫는다.

sqlite3_close(database);

}






이제 데이터를 테이블 뷰에 뿌려주기 위해서 작업해보겠습니다.






RootViewController.m


#import "RootViewController.h"

// 다음 헤더파일을 임포트 합니다.

#import "MemoPadAppDelegate.h"

#import "MemoData.h"


@implementation RootViewController



#pragma mark -

#pragma mark View lifecycle



- (void)viewDidLoad {

    [super viewDidLoad];


// MemoPadAppDelegate 불러옵니다

MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

// 데이터베이스 데이터를 읽어옵니다.

[appDelegate readMemoFromDatabase];

// 테이블의 제목을 설정합니다.

self.title = @"MemoPad";

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

}



#pragma mark -

#pragma mark Table view data source


// Customize the number of sections in the table view.

// 테이블 뷰의 섹션(그룹) 수를 지정한다. 1 지정하면 단순한 하나의 리스트가 된다.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return 1;

}



// Customize the number of rows in the table view.

// 테이블 뷰의 (row) 수를 지정한다. 데이터베이스에서 읽어온 메모의 수를 행의 수로 지정한다.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

// App delegate DBData 있는 메모의 수를 얻어낸다.

NSInteger rowCount = [[appDelegate DBData] count];

return rowCount;

}



// Customize the appearance of table view cells.

// 테이블 뷰의 행에 들어갈 셀의 내용을 얻어낸다.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    

    static NSString *CellIdentifier = @"Cell";

    

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

    }

    

// Configure the cell.

// App delegate 얻는다.

MemoPadAppDelegate *appDelegate = (MemoPadAppDelegate *)[[UIApplication sharedApplication] delegate];

// DBData에서 indexPath.row 번째 MemoData 오브젝트를 가져온다.

MemoData *mData = [[appDelegate DBData] objectAtIndex:indexPath.row];

// MemoData mTitle textLable text 속성에 할당한다.

cell.textLabel.text = mData.mTitle;


    return cell;

}