이 글에서는 C/C++ 프로그램에서 SQLite를 사용하는 방법을 단계별로 알아봅니다. 라이브러리 설치부터 데이터베이스 연결, 테이블 생성, 데이터 삽입과 조회까지 실제 예제 코드와 함께 살펴보겠습니다.
설치
C/C++ 프로그램에서 SQLite를 사용하기 전에 먼저 개발 환경에 SQLite 라이브러리가 설치되어 있는지 확인해야 합니다. 설치 과정에 대한 자세한 내용은 SQLite 설치 관련 문서를 참고하시기 바랍니다. 리눅스 환경에서는 대부분의 배포판에서 sqlite3 패키지와 개발용 헤더 파일(libsqlite3-dev 등)을 패키지 관리자를 통해 간편하게 설치할 수 있습니다.
주요 C/C++ 인터페이스 API
다음은 C/C++ 프로그램에서 SQLite 데이터베이스를 다룰 때 가장 많이 사용되는 핵심 인터페이스 함수들입니다. 더 복잡하고 고급화된 애플리케이션을 개발하려면 SQLite 공식 문서를 참조하는 것이 좋습니다.
| 번호 | API 및 설명 |
|---|---|
| 1 | sqlite3_open(const char *filename, sqlite3 **ppDb)SQLite 데이터베이스 파일에 대한 연결을 열고, 다른 SQLite 함수들이 사용할 수 있는 데이터베이스 연결 객체를 반환합니다. filename 인자가 NULL이거나 ':memory:'인 경우, sqlite3_open()은 세션이 유지되는 동안에만 존재하는 RAM 기반의 인메모리 데이터베이스를 생성합니다. 파일명이 NULL이 아니라면 해당 이름의 데이터베이스 파일을 열려고 시도하며, 같은 이름의 파일이 존재하지 않으면 새로운 데이터베이스 파일을 생성합니다. |
| 2 | sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)SQL 명령을 빠르고 간편하게 실행할 수 있는 함수입니다. sql 인자에는 여러 개의 SQL 명령을 포함시킬 수도 있습니다. 첫 번째 인자인 sqlite3는 열려 있는 데이터베이스 객체이며, sqlite_callback은 콜백 함수로 data가 그 첫 번째 인자로 전달됩니다. errmsg는 함수 실행 중 발생한 오류 메시지를 담아 반환하는 용도로 사용됩니다. sqlite3_exec()는 sql 인자로 전달된 모든 명령을 문자열 끝에 도달하거나 오류가 발생할 때까지 순서대로 파싱하고 실행합니다. |
| 3 | sqlite3_close(sqlite3*)sqlite3_open()으로 열었던 데이터베이스 연결을 닫는 함수입니다. 연결을 종료하기 전에 해당 연결과 관련된 모든 준비된(prepared) 구문은 반드시 마무리(finalize)되어야 합니다. 마무리되지 않은 쿼리가 남아 있다면 sqlite3_close()는 SQLITE_BUSY를 반환하며 "Unable to close due to unfinalized statements"라는 오류 메시지를 출력합니다. |
데이터베이스 연결하기
다음 C 코드는 기존에 존재하는 데이터베이스에 연결하는 방법을 보여줍니다. 만약 해당 데이터베이스가 존재하지 않으면 새로 생성된 후 데이터베이스 객체가 반환됩니다.
예제 코드
#include <stdio.h>
#include <sqlite3.h>
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_close(db);
}실행 결과
$gcc test.c -l sqlite3 $./a.out Opened database successfully
테이블 생성하기
다음 C 코드는 앞서 생성한 데이터베이스 안에 테이블을 만드는 방법을 보여줍니다.
예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for(i = 0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
/* 데이터베이스 열기 */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stdout, "Opened database successfully\n");
}
/* SQL문 작성 */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
/* SQL문 실행 */
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Table created successfully\n");
}
sqlite3_close(db);
return 0;
}실행 결과 (데이터베이스 파일 상태 확인):
-rwxr-xr-x. 1 root root 9567 May 8 02:31 a.out -rw-r--r--. 1 root root 1207 May 8 02:31 test.c -rw-r--r--. 1 root root 3072 May 8 02:31 test.db
위 결과에서 test.db 파일이 실제로 생성된 것을 확인할 수 있습니다.
INSERT 연산 (데이터 삽입)
다음 C 코드는 앞서 만든 COMPANY 테이블에 레코드를 삽입하는 방법을 보여줍니다.
예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
int i;
for(i = 0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
/* 데이터베이스 열기 */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* SQL문 작성 */
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " \
"VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " \
"VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); " \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
"VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );" \
"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
/* SQL문 실행 */
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Records created successfully\n");
}
sqlite3_close(db);
return 0;
}실행 결과
Opened database successfully Records created successfully
SELECT 연산 (데이터 조회)
실제 레코드를 조회하는 예제로 넘어가기 전에, 예제에서 사용 중인 콜백 함수에 대해 자세히 살펴보겠습니다. 이 콜백은 SELECT 문의 결과를 얻어오는 방법을 제공하며, 다음과 같이 선언됩니다.
typedef int (*sqlite3_callback)(
void*, /* sqlite3_exec()의 네 번째 인자로 전달된 데이터 */
int, /* 행(row)의 컬럼 개수 */
char**, /* 행의 각 필드 값을 나타내는 문자열 배열 */
char** /* 컬럼 이름을 나타내는 문자열 배열 */
);위 콜백이 sqlite_exec()의 세 번째 인자로 전달되면, SQLite는 sql 인자 내에서 실행되는 각 SELECT 문이 처리하는 레코드마다 이 콜백 함수를 호출합니다.
다음 C 코드는 앞서 만든 COMPANY 테이블에서 레코드를 가져와 화면에 출력하는 방법을 보여줍니다.
예제 코드
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *data, int argc, char **argv, char **azColName) {
int i;
fprintf(stderr, "%s: ", (const char*)data);
for(i = 0; i<argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char* argv[]) {
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
/* 데이터베이스 열기 */
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
/* SQL문 작성 */
sql = "SELECT * from COMPANY";
/* SQL문 실행 */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Operation done successfully\n");
}
sqlite3_close(db);
return 0;
}실행 결과
Opened database successfully Callback function called: ID = 1 NAME = Paul AGE = 32 ADDRESS = California SALARY = 20000.0 Callback function called: ID = 2 NAME = Allen AGE = 25 ADDRESS = Texas SALARY = 15000.0 Callback function called: ID = 3 NAME = Teddy AGE = 23 ADDRESS = Norway SALARY = 20000.0 Callback function called: ID = 4 NAME = Mark AGE = 25 ADDRESS = Rich-Mond SALARY = 65000.0 Operation done successfully
출력 결과에서 알 수 있듯이, SELECT 문이 처리하는 각 레코드마다 콜백 함수가 한 번씩 호출되며, 컬럼 이름과 값이 함께 출력됩니다. 이처럼 sqlite3_open(), sqlite3_exec(), sqlite3_close() 세 가지 핵심 함수만 익혀도 C/C++에서 SQLite 데이터베이스의 기본적인 CRUD 작업을 충분히 수행할 수 있습니다.