반응형
해당 글에서는 React Native에서 expo-sqlite를 이용하는 활용 방법 및 데이터베이스 데이터를 GUI 툴을 이용하여 확인하는 방법에 대해 알아봅니다
💡 [참고] 이전에 작성한 글을 참고하시면 도움이 됩니다.
1) expo-sqlite
💡 expo-sqlite
- 설치나 설정이 필요하지 않은 내장형 파일 기반의 관계형 데이터베이스(RDBMS)입니다. 외부에 데이터베이스를 두지 않고 모든 휴대폰 및 대부분의 컴퓨터에 내장되어 있어서 로컬/클라이언트 저장소를 위한 임베디드 DB입니다.
- 결론적으로 위 정의를 기반으로 expo-sqlite는 expo에서 Sqlite를 사용하기 편하게 제공해 주는 라이브러리입니다.
1. SQLite 데이터 타입
💡 SQLite 데이터 타입
- SQLite에서 제공하는 5가지 데이터 타입입니다.
데이터 타입 | 설명 | 예시 |
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' |
[ 더 알아보기 ]
💡 그러면 SQLite에서는 DATE 타입은 사용이 불가능한 것인가?
- SQLite에서는 DATE 타입을 직접적으로 지원하지 않습니다. 대신, TEXT 타입으로 저장하고 내장 함수인 datetime()을 활용하여서 날짜 데이터를 관리할 수 있습니다.
2. react-native-cli 환경에서 expo-sqlite를 사용할 수 있을까?
💡 react-native-cli 환경에서 expo-sqlite를 사용할 수 있을까?
- react-native-cli를 사용하는 프로젝트에서는 ‘install-expo-modules’을 통해서 사용이 가능합니다. 그러나 이는, expo 모듈 자체를 설치하는 것이기에 함께 expo 모듈을 사용 중이라면 이를 사용하는 것이 권장이 됩니다.
- expo 모듈을 사용하지 않는 react-native-cli 환경이라면 react-native-sqlite-storage를 사용하기를 권장합니다.
💡 [참고] react-native-cli 환경에서 expo 모듈 사용 방법
💡 [참고] react-native-sqlite-storage 라이브러리
2) expo-sqlite 활용하기
1. 라이브러리 추가
# expo를 활용하여 설치
$ npx expo install expo-sqlite
# expo-sqlite 추가
$ npm install expo-sqlite
# or
$ yarn add expo-sqlite
2. 공통 db 인스턴스를 생성합니다 : SqliteConfig
💡 공통 db 인스턴스를 생성합니다 : SqliteConfig
- 공통으로 사용되는 데이터베이스 인스턴스 생성과 테이블 실행 구문에 대해서 관리합니다.
1. createDBInstance()
- 데이터베이스에 연결하고 인스턴스를 생성합니다.
- SQLite.openDatabase() 메서드를 통해 지정한 "데이터베이스.db"에 대해서 존재하면 연결하고, 존재하지 않으면 새로 생성합니다.
2. enableForeignKeys()
- 외래키 제약조건을 활성화합니다.
- 왜래 키 제약 조건이 활성화되면, 테이블 간의 관계를 유지하고 데이터 무결성을 보장하는 데 도움이 됩니다.
3. executeQuery()
- 파라미터로 쿼리(query)와 [optinal] 파라미터 값(params)을 전달받아서 이를 수행하는 역할을 하는 함수입니다.
- db query를 실행에 성공 경우 resolve로 실행되며, 실패하는 경우 reject로 실행이 됩니다.
import * as SQLite from 'expo-sqlite';
import { Platform } from 'react-native';
/**
* 공통 데이터베이스 인스턴스와 테이블 실행 구문을 관리합니다.
*/
class SqliteConfig {
private DATABASE_NAME = "test.db";
/**
* 데이터베이스 인스턴스를 생성합니다.
* @returns {Promise<SQLite.SQLiteDatabase | { transaction: () => { executeSql: () => void } }>}
*/
async createDBInstance(): Promise<SQLite.SQLiteDatabase | { transaction: () => { executeSql: () => void } }> {
if (Platform.OS === "web") {
return {
transaction: () => ({
executeSql: () => { },
}),
};
}
const dbInstance = SQLite.openDatabase(this.DATABASE_NAME);
if (!dbInstance) {
throw new Error("데이터베이스 인스턴스를 생성할 수 없습니다.")
}
await this.enableForeignKeys(dbInstance); // 왜래키 제약조건을 활성화하여 인스턴스를 최종 구성합니다.
return dbInstance;
}
/**
* 왜래키 제약조건을 활성화 합니다.
* - 왜래 키 제약 조건이 활성화되면, 테이블 간의 관계를 유지하고 데이터 무결성을 보장하는 데 도움이 됩니다.
* @param {SQLite.SQLiteDatabase} dbInstance
* @returns {Promise<void> }
*/
private async enableForeignKeys(dbInstance: SQLite.SQLiteDatabase): Promise<void> {
return new Promise((resolve, reject) => {
dbInstance.exec([{ sql: 'PRAGMA foreign_keys = ON;', args: [] }], false, (error) => {
if (error) reject(error);
else resolve();
});
});
}
/**
* 데이터베이스 명령어를 전달받은 파라미터 SQL을 기반으로 수행합니다
* - params는 Optional하게 존재 할 수도 있고 존재하지 않을 수도 있습니다.
* @param {string} query
* @param {any[]} params
* @returns
*/
async executeQuery(query: string, params?: any[]): Promise<any> {
if (params && params.length === 0) params = [];
try {
const dbInstance = await this.createDBInstance();
const execute = new Promise(async (resolve, reject) => {
dbInstance.transaction(tx => {
tx.executeSql(
query,
params,
(_, result) => {
console.log("[+] SQL 실행 성공 :: ", result)
resolve(result.rows._array)
},
(_, error) => {
reject(`SQL 실행 실패: ${error}`);
return false;
}
);
});
});
return execute;
} catch (error) {
throw new Error(`쿼리 실행중에 오류가 발생하였습니다 :: ${error}`,)
}
}
}
export default new SqliteConfig();
3. SQLite DDL 활용하기
💡SQLite DDL 활용하기
- 해당 DDL은 테이블을 생성하고 조회하며, 모든 컬럼들에 대해 확인합니다. 또한 데이터를 비우거나 혹은 삭제하는 DDL로 구성이 되어 있습니다.
1. createTable()
- TB_USER 테이블을 생성합니다.
- CREATE 중복 오류가 발생할 수 있기에 존재하지 않을 때(IF NOT EXIST)를 적용하여 테이블을 생성합니다.
2. showAllTables()
- 데이터베이스 내의 모든 테이블에 대해서 조회합니다.
3. showTableStructure()
- TB_USER 테이블 내의 컬럼 구조를 확인합니다.
4. truncateTable()
- TB_USER 테이블을 데이터를 초기화 & 시퀀스 초기화 작업을 수행합니다.
5. dropTable()
- TB_USER 테이블을 삭제합니다.
import SqliteConfig from './SqliteConfig';
class TbUserModules {
private TABLE_NM = "TB_USER"
/**
* ====================================================================================================================================================
* ======================================================================== [TABLE DDL] ===============================================================
* ====================================================================================================================================================
*/
/**
* TB_USER 테이블을 생성합니다.
* @returns {Promise<any>}
*/
createTable = async (): Promise<any> => {
const query = `
CREATE TABLE IF NOT EXISTS ${this.TABLE_NM}
(
userSq INTEGER PRIMARY KEY AUTOINCREMENT,
userId TEXT,
userNm TEXT
);
`;
const execute = await SqliteConfig.executeQuery(query);
console.log(`[+] ${this.TABLE_NM} 테이블이 생성되었습니다.`, execute)
return execute
};
/**
* 데이터베이스 내의 모든 테이블에 대해서 조회합니다.
* @returns {Promise<any>}
*/
showAllTables = async (): Promise<any> => {
const query = `
SELECT name
FROM sqlite_master
WHERE type='table';
`;
const execute = await SqliteConfig.executeQuery(query)
try {
console.log(`[+] 데이터베이스 내에 모든 테이블을 조회합니다.`, execute)
return execute;
} catch (error) {
console.error(`데이터베이스 작업 실패: ${error}`);
}
}
/**
* TB_USER 테이블내의 컬럼 구조를 확인합니다.
* @returns {Promise<any>}
*/
showTableStructure = async (): Promise<any> => {
const query = `PRAGMA table_info(${this.TABLE_NM});`
try {
const execute = await SqliteConfig.executeQuery(query)
console.log(`[+] TB_USER 테이블 내의 구조를 조회합니다.`, execute)
return execute
} catch (error) {
console.error(`테이블 구조 조회 실패: ${error}`);
}
};
/**
* TB_USER 테이블을 데이터를 초기화 & 시퀀스 초기화 작업을 수행합니다.
* - 데이터를 비우는 TRUNCATE가 존재하지 않기에 테이블 데이터를 지우고, 시퀀스를 비우는 방법으로 처리합니다.
* @returns {Promise<any>}
*/
truncateTable = async (): Promise<any> => {
const deleteQuery = `DELETE FROM ${this.TABLE_NM};`
const resetSequenceQuery = `DELETE FROM sqlite_sequence WHERE name='${this.TABLE_NM}';`
const execute = await SqliteConfig.executeQuery(deleteQuery);
const execute2 = await SqliteConfig.executeQuery(resetSequenceQuery);
console.log(`[+] 데이터베이스 내에 모든 데이터를 초기화합니다.`, execute2)
return execute2;
}
/**
* TB_USER 테이블을 삭제합니다.
* @returns {Promise<any>}
*/
dropTable = async (): Promise<any> => {
const query = `DROP TABLE IF EXISTS ${this.TABLE_NM}`;
const execute = await SqliteConfig.executeQuery(query)
console.log(`[+] ${this.TABLE_NM} 테이블이 삭제되었습니다.`)
return execute;
}
/**
* ====================================================================================================================================================
* ======================================================================== [END TABLE DDL] ===============================================================
* ====================================================================================================================================================
*/
}
export default new TbUserModules();
3.1. 결과 확인: 테이블을 생성하고 데이터베이스 내의 모든 테이블을 조회합니다.
💡 결과 확인 : 테이블을 생성하고 데이터베이스 내의 모든 테이블을 조회합니다.
- 위에서 구성한 함수를 호출하여 수행이 잘됨을 확인하였습니다.
TbUserModules.createTable(); // 테이블 생성합니다
TbUserModules.showAllTables(); // 테이블 모두 조회합니다.
3.2. 결과 확인 : 테이블 내의 컬럼들을 조회합니다.
💡 결과 확인 : 테이블 내의 컬럼들을 조회합니다.
- 생성한 TB_USER 테이블 내의 컬럼들을 조회합니다.
TbUserModules.showTableStructure(); // TB_USER 테이블 조회합니다.
[
{
"cid": 0,
"dflt_value": null,
"name": "userSq",
"notnull": 0,
"pk": 1,
"type": "INTEGER"
},
{
"cid": 1,
"dflt_value": null,
"name": "userId",
"notnull": 0,
"pk": 0,
"type": "TEXT"
},
{
"cid": 2,
"dflt_value": null,
"name": "userNm",
"notnull": 0,
"pk": 0,
"type": "TEXT"
}
]
3.3. 테이블을 삭제하고 데이터베이스 내의 모든 테이블을 조회합니다.
💡 테이블을 삭제하고 데이터베이스 내의 모든 테이블을 조회합니다.
- 테이블을 삭제하고 데이터베이스 내의 모든 테이블을 조회하여 잘 삭제되었음을 확인하였습니다.
TbUserModules.dropTable(); // 테이블을 삭제합니다.
TbUserModules.showAllTables(); // 테이블 모두 조회합니다.
4. SQLite DML을 구성합니다.
💡SQLite DML을 구성합니다
- 모든 메서드는 비동기적으로 작동하며, SQLiteConfig.executeQuery를 사용하여 데이터베이스 작업을 수행합니다.
- 각 작업 후에는 콘솔에 로그를 출력하여 작업 결과를 확인할 수 있습니다.
1. insertUser
- 이 메서드는 TB_USER 테이블에 새로운 사용자 정보를 삽입합니다. userDto 객체를 매개변수로 받아 userId와 userNm을 테이블에 추가합니다.
2. updateUser
- 이 메서드는 기존 사용자의 정보를 업데이트합니다. userDto 객체를 받아 특정 userId를 가진 사용자의 userNm을 변경합니다.
3. deleteUser
- 이 메서드는 특정 userId를 가진 사용자를 테이블에서 삭제합니다. userId를 매개변수로 받아 해당 사용자를 제거합니다.
4. selectAllUserList
- 이 메서드는 TB_USER 테이블의 모든 데이터를 조회합니다. 별도의 매개변수 없이 테이블의 전체 내용을 반환합니다
/**
* ====================================================================================================================================================
* ======================================================================== [TABLE DML] ===============================================================
* ====================================================================================================================================================
*/
/**
* TB_USER 테이블을 row를 등록합니다.
* @param userDto
* @returns {Promise<any>}
*/
insertUser = async (userDto): Promise<any> => {
try {
const query: string = `
INSERT INTO ${this.TABLE_NM} (userId, userNm)
VALUES (?, ?);`
console.log("[+] query :: ", query)
const execute = await SqliteConfig.executeQuery(
query,
[
userDto.userId,
userDto.userNm
]
)
console.log("[+] 데이터를 등록하였습니다 ", execute)
return execute
} catch (error) {
console.error("[-] 예외 발생:", error)
throw error
}
}
/**
* TB_USER 테이블의 데이터를 업데이트합니다.
* @param userDto
* @returns {Promise<any>}
*/
updateUser = async (userDto): Promise<any> => {
try {
const query: string = `
UPDATE ${this.TABLE_NM}
SET userNm = ?
WHERE userId = ?;`
console.log("[+] query :: ", query)
const execute = await SqliteConfig.executeQuery(
query,
[
userDto.userNm,
userDto.userId
]
)
console.log("[+] 데이터를 업데이트하였습니다 ", execute)
return execute
} catch (error) {
console.error("[-] 예외 발생:", error)
throw error
}
}
/**
* TB_USER 테이블의 데이터를 삭제합니다.
* @param userId
* @returns {Promise<any>}
*/
deleteUser = async (userId: string): Promise<any> => {
try {
const query: string = `
DELETE FROM ${this.TABLE_NM}
WHERE userId = ?;`
console.log("[+] query :: ", query)
const execute = await SqliteConfig.executeQuery(
query,
[userId]
)
console.log("[+] 데이터를 삭제하였습니다 ", execute)
return execute
} catch (error) {
console.error("[-] 예외 발생:", error)
throw error
}
}
/**
* TB_USER 테이블의 모든 데이터를 조회합니다.
* @returns
*/
selectAllUserList = async (): Promise<any> => {
const query: string = `
SELECT *
FROM ${this.TABLE_NM}`;
const execute = await SqliteConfig.executeQuery(query);
console.log(`[+] TB_USER 테이블 데이터를 조회합니다.`, execute)
return execute;
}
4.1. 데이터 등록
💡 데이터 등록
- 구성한 TbUserModules.selectAllUserList() 메서드를 이용하여 TB_USER 테이블 내의 데이터를 INSERT 합니다.
TbUserModules.insertUser({userId: "adjh54", userNm: "lee"}); // 테이블 데이터를 등록합니다.
TbUserModules.insertUser({userId: "ckask123", userNm: "park"}); // 테이블 데이터를 등록합니다.
4.2. 데이터 조회
💡 데이터 조회
- 구성한 TbUserModules.selectAllUserList() 메서드를 이용하여서 TB_USER 테이블의 모든 데이터를 조회합니다.
TbUserModules.selectAllUserList(); // 사용자의 모든 데이터를 조회합니다.
[
{
"userId": "adjh54",
"userNm": "lee",
"userSq": 1
},
{
"userId": "ckask123",
"userNm": "park",
"userSq": 2
},
{
"userId": "ckask123",
"userNm": "park",
"userSq": 3
}
]
4.3. 데이터 수정
💡 데이터 수정
- 구성한 TbUserModules.updateUser() 메서드를 이용하여서 TB_USER 테이블의 특정 데이터를 수정합니다.
- 수정한 데이터를 확인하기 위해 TB_USER 테이블을 조회합니다.
TbUserModules.updateUser({ userNm: "이", userSq: 1 })
TbUserModules.selectAllUserList(); // 사용자의 모든 데이터를 조회합니다.
4.4. 데이터 삭제
💡 데이터 삭제
- 구성한 TbUserModules.deleteUser() 메서드를 이용하여서 TB_USER 테이블의 특정 데이터를 삭제합니다
- 삭제된 데이터를 확인하기 위해 TB_USER 테이블을 조회합니다.
TbUserModules.deleteUser({userSq: 2}); // 사용자 시퀀스 기반 데이터를 삭제합니다.
3) [Android] .db 파일 데이터 확인방법 : Android Studio
1. 프로젝트 > android 폴더를 열어줍니다.
2. 메뉴 중 View > Tool Windows > Device Explorer를 선택합니다.
3. 폴더 중 "/"> data > data 폴더로 이동합니다.
4. 프로젝트 패키지 > files > SQLite > .db 파일을 확인할 수 있습니다.
5. 데이터를 확인하기 위해 .db 파일을 Save As… 로 저장을 합니다.
4) SQlite 데이터 확인방법 : db-browser-for-sqlite
1. GUI 프로그램 설치
# db-browser-for-sqlite를 설치합니다.
$ brew install db-browser-for-sqlite
2. DB Browser for SQLite를 실행하고 Open Database 버튼을 눌러서 .db 파일을 열어줍니다.
3. 아래와 같은 형태로 데이터베이스 내의 테이블 구성을 확인할 수 있습니다.
4. Browse Data 탭을 이동하면 테이블 형태로 확인이 가능합니다.
오늘도 감사합니다😀
반응형