728x170
해당 글은 Expo-cli 프로젝트(React-native) 내에 데이터베이스로 사용되는 Sqlite에 대한 이해를 하고 환경설정을 위한 연결하기 위한 expo-sqlite 라이브러리를 적용하는 가이드입니다.
💡 필자는 해당 개발환경을 아래와 같이 구성하였습니다.
{
"dependencies": {
"react-native": "0.64.3",
"expo": "^44.0.6",
"expo-sqlite": "~10.1.0",
"react": "17.0.1"
},
"devDependencies": {
"typescript": "~4.3.5"
}
}
1) expo-sqlite 설명
1. expo-sqlite 정의
💡 SQLite는 설치나 설정이 필요하지 않은 내장형 파일 기반의 관계형 데이터베이스(RDBMS)입니다. 외부에 데이터베이스를 두지 않고 모든 휴대폰 및 대부분의 컴퓨터에 내장되어 있어서 로컬/클라이언트 저장소를 위한 임베디드 DB입니다. 결론적으로 위 정의를 기반으로 expo-sqlite는 expo에서 Sqlite를 사용하기 편하게 제공해주는 라이브러리입니다.
- 해당 데이터베이스는 앱을 다시 시작해도 값이 유지가 됩니다.
- IoT 및 임베디드 장치에 적합한 데이터베이스입니다.
2. expo-sqlite 데이터 자료형
데이터 자료형 | 설명 | 예 |
NULL | 값이 정의되지 않거나 존재하지 않는 값을 저장합니다. | NULL |
INTEGER | 양수(+), 음수(-)와 같이 부호를 가지는 '정수 값'으로 저장이 되며 1, 2, 4, 6, 8 바이트와 같은 가변의 크기를 가질 수 있다. | -1234, 1234, 0 |
REAL | 양수(+), 음수(-)와 같이 부호를 가지는 '실수 값'으로 저장이 되며 실수 또는 8바이트 부동 소수점 숫자로 저장이 됩니다. | 0, 3.14, 3.22331, 1.0 |
TEXT | '문자 값'을 저장하는데 사용이 되며 기본적으로 ‘UTF-8’을 기준으로 저장이 되며, 해당 크기는 제한이 없으며 데이터베이스 인코딩을 사용하여 저장된 텍스트 문자열. UTF-8, UTF-16BE, UTF-16LE를 지원한다. | "문자열", "한" |
BLOB | 바이너리 타입의 파일을 저장한다. 이미지, 비디오, mp3멀티미디어 파일을 저장한다. | x'0101', x'0fac', x'0F0FAC', x'01234532' |
기타 데이터 자료형에 대한 저장 방법
- Boolean : 정수로 false(0), true(1)의 값으로 저장이 된다 (=INTEGER)
- Date, Time: 텍스트의 'YYYY-MM-DD hh:mm:ss'형태로 저장이 된다(=TEXT)
2) expo-sqlite 환경설정
1. expo-sqlite 라이브러리를 설치합니다.
💡 expo-sqlite 라이브러리를 설치합니다.
- expo에서 sqlite를 사용할 수 있도록 하는 ‘expo-sqlite’ 라이브러리를 설치합니다.
# expo-sqlite install
$ expo install expo-sqlite
# yarn install
$ yarn add expo-sqlite
# or
# npm install
$ npm install expo-sqlite
2. [참고] 데이터베이스 파일을 열 수 있도록 환경설정을 합니다.
💡 데이터베이스 파일을 열 수 있도록 환경설정을 합니다.
- 해당 경우는 사용자가 가지고 있는 .db 파일이 있는 경우, 불러와서 이를 수행하는 방식을 의미합니다.
- 해당 방법을 적용하지 않았다면, 내부 로컬 데이터베이스에 .db 파일을 생성하고 관리하는 방식입니다.
💡 아래의 경우 metro에서 .db 확장자의 파일에 대한 접근을 허용하도록 환경설정을 해주는 과정입니다.
const { getDefaultConfig } = require('@expo/metro-config');
module.exports = (async () => {
const defaultConfig = await getDefaultConfig();
const { assetExts } = defaultConfig.resolver;
return {
resolver: {
// Add bin to assetExts
assetExts: [...assetExts, 'db'],
},
};
})();
3. SQLite 데이터베이스 연결을 합니다.
💡 [꿀팁] expo-sqlit는 'Web' 플랫폼을 제외하고 모두 연결이 가능합니다.
[출처] expo-sqlite 공식 사이트(https://docs.expo.dev/versions/latest/sdk/sqlite/)
💡 디바이스에 저장할 데이터베이스로 설정을 합니다. 최종 디바이스에 저장될 환경설정이 완료되면 함수 내에서 연결된 db를 리턴하도록 구성하였습니다.
import * as SQLite from 'expo-sqlite';
import { Platform } from 'react-native';
/**
* 데이터베이스의 인스턴스를 생성합니다.
*/
class CommonTableInstance {
private DATABASE_NAME = "text.db"
createDBInstance = async (): Promise<SQLite.SQLiteDatabase | { transaction: () => { executeSql: () => void } }> => {
// [STEP1] 'Web' 플랫폼 인 경우 아무것도 수행하지 않습니다.
if (Platform.OS === "web") {
return {
transaction: () => ({
executeSql: () => { },
}),
};
}
// [STEP2] 데이터베이스가 존재하지 않으면 생성하고, 존재하면 이를 불러옵니다.
const dbInstance = SQLite.openDatabase(this.DATABASE_NAME);
// [STEP3] 외래키 제약조건을 활성화합니다. 기본적으로 SQLite에서는 외래 키 제약 조건이 비활성화되어 있습니다.
await new Promise<void>((resolve) => dbInstance.exec([{ sql: 'PRAGMA foreign_keys = ON;', args: [] }], false, () => resolve()));
return dbInstance;
}
}
export default new CommonTableInstance();
4. 구성한 데이터베이스를 기반으로 테이블을 생성 혹은 연결합니다.
💡 구성한 데이터베이스를 기반으로 테이블을 생성 혹은 연결합니다.
- 위에서 생성한 데이터베이스 인스턴스를 활용합니다.
import { initDatabaseConfig } from '../configs/SqliteConfig';
import { UserType, UserResolvers } from '../types/common/UserType';
const TABLE_USER = 'tb_user';
/**
* [init] 연결한 데이터 베이스 정보를 가져온다.
*/
const DBInstance = initDatabaseConfig();
5. 테이블 생성, 데이터 등록, 조회
💡 테이블 생성, 데이터 등록, 조회
- 각각 테이블 생성, 데이터 등록, 조회를 수행합니다.
- createTbUser(): TB_USER라는 이름의 테이블을 생성합니다. 이 테이블은 userSq(자동 증가 기본키), userId, userNm 컬럼을 가집니다.
- insertUser(): TB_USER 테이블에 새로운 사용자 정보를 추가합니다. userId와 userNm을 매개변수로 받아 테이블에 삽입합니다.
- selectUserList(): TB_USER 테이블의 모든 데이터를 조회합니다. 결과는 InnerDatabaseType.tbUserDto[] 타입의 배열로 반환됩니다. - dropTable(): TB_USER 테이블을 삭제합니다.
import * as SQLite from 'expo-sqlite';
import CommonTableInstance from "./CommonTableInstance";
import { InnerDatabaseType, TABLE_NAME } from "types/common/InnerDatabaseType";
class TbUserModules {
private TABLE_NM = "TB_USER"
/**
* TB_USER 테이블을 생성합니다.
* @returns
*/
createTbUser = async (): Promise<void> => {
const query: string =
`CREATE TABLE IF NOT EXISTS ${this.TABLE_NM}
(
userSq INTEGER PRIMARY KEY AUTOINCREMENT,
userId TEXT,
userNm TEXT
)`;
return await new Promise(async (resolve, reject) => {
(await CommonTableInstance.createDBInstance())
.transaction(
// Successs Callback
(tx: SQLite.SQLTransaction) => {
console.log(`[+] ${this.TABLE_NM} 테이블이 생성되었습니다.`)
tx.executeSql(query);
},
// Error Callback
(error: SQLite.SQLError) => {
reject(`Fail to create a todo table :: ${error}`);
},
() => {
resolve();
}
);
});
}
/**
* TB_USER 테이블 데이터를 추가합니다.
* @param userInfo
* @returns
*/
insertUser = async (userInfo: InnerDatabaseType.tbUserDto): Promise<void> => {
const query: string = `INSERT INTO ${this.TABLE_NM} (userId, userNm) VALUES (?, ?)`;
return await new Promise(async (resolve, reject) => {
(await CommonTableInstance.createDBInstance())
.transaction(
(tx: SQLite.SQLTransaction) => {
tx.executeSql(
query,
[userInfo.userId, userInfo.userNm],
(_, result) => {
console.log(`[+] 데이터가 성공적으로 삽입되었습니다. Insert ID: ${result.insertId}`);
resolve();
},
(_, error) => {
console.error(`[-] 데이터 삽입 실패: ${error}`);
reject(error);
return false;
}
);
},
(error: SQLite.SQLError) => {
reject(`데이터 삽입 트랜잭션 실패: ${error}`);
}
);
});
}
/**
* TB_USER 테이블의 모든 데이터를 조회합니다.
* @returns
*/
selectUserList = async (): Promise<InnerDatabaseType.tbUserDto[]> => {
const query: string = `SELECT * FROM ${this.TABLE_NM}`;
return await new Promise(async (resolve, reject) => {
(await CommonTableInstance.createDBInstance())
.transaction(
(tx: SQLite.SQLTransaction) => {
tx.executeSql(
query,
[],
(_, result) => {
const users: InnerDatabaseType.tbUserDto[] = [];
for (let i = 0; i < result.rows.length; i++) {
users.push(result.rows.item(i));
}
console.log(`[+] ${users.length}개의 데이터를 조회했습니다.`);
resolve(users);
},
(_, error) => {
console.error(`[-] 데이터 조회 실패: ${error}`);
reject(error);
return false;
}
);
},
(error: SQLite.SQLError) => {
reject(`데이터 조회 트랜잭션 실패: ${error}`);
}
);
});
}
}
export default new TbUserModules();
6. 구성한 함수를 호출합니다.
💡 구성한 함수를 호출합니다.
- App.tsx 파일이 실행될 때, useEffect()를 통해서 해당 테이블을 호출합니다.
- 테이블 생성 > 데이터 등록 > 데이터 조회 순으로 모듈에서 정의된 내용을 기반으로 수행이 됩니다.
import TbUserModules from "modules/sqlite/TbUserModules";
import { useEffect } from "react"
const App = () => {
useEffect(() => {
sqliteHandler.createTable();
sqliteHandler.insertRowTable();
sqliteHandler.selectTable();
}, [])
const sqliteHandler = (() => {
return {
/**
* 테이블을 생성합니다.
*/
createTable: async () => {
console.log("[+] 테이블을 생성합니다.")
const dbInstance = await TbUserModules.createTbUser();
console.log(dbInstance)
},
/**
* 테이블 데이터를 등록합니다.
*/
insertRowTable: async () => {
const userInfo: InnerDatabaseType.tbUserDto = {
userId: "user1234",
userNm: "홍길동"
}
try {
await TbUserModules.insertUser(userInfo);
console.log('데이터가 성공적으로 삽입되었습니다.');
} catch (error) {
console.error('데이터 삽입 중 오류 발생:', error);
}
},
/**
* 테이블 데이터를 조회합니다.
*/
selectTable: async () => {
const result = await TbUserModules.selectUserList();
console.log("조회된 데이터 :: ", result)
},
}
})()
}
export default App;
3) 결과 확인
💡 결과 확인
- 테이블 생성 > 데이터 삽입 > 테이블 조회 처리를 수행하였습니다.
💡 [참고] 구체적인 활용방법은 다음 글에서 이어집니다.
오늘도 감사합니다😀
그리드형
'React & React Native > 라이브러리 활용' 카테고리의 다른 글
[RN] React Native 자이로센서를 활용한 자이로스코프, 디바이스 모션 이해하기: expo-sensors (0) | 2022.05.15 |
---|---|
[RN] React Native Tensorflow.js 메모리 관리 이해하기 (0) | 2022.04.10 |
[RN] React Native 앱 상태 관리 이해 및 구성 방법: AppState (0) | 2022.03.27 |
[RN] React Native Tensorflow의 blazeface 모델을 이용한 얼굴 감지 (6) | 2022.03.09 |
[React] React-konva 이해하기-1 (구조편) (1) | 2022.01.29 |