React Native 앱이 커지다보면, 내부에서 key-value 쌍으로 이루어진 AsyncStorage 외에 관계형 데이터베이스를 사용해야 하는 경우가 있는거 같다. 그런 경우가 어떤 경우인지는 정확하게 모르겠다..
그래서 이번 시리즈에서는 React Native 앱 내부에서 sqlite를 쓸 수 있도록 해주는 react-native-sqlite-storage
를 구현해보자 한다.
npm install --save react-native-sqlite-storage
다음과 같이 라이브러리를 설치해준 후, iOS 세팅을 위해 pod install을 해준다.
cd ios && pod install && cd ..
안드로이드의 경우, 추가적인 세팅이 없어도 사용이 가능하지만 react-native-sqlite-storage 라이브러리와 SQLite를 번들해서 사용하기 위해서는 설정이 필요하다. (이는 FTS5를 지원한다.)
react-native.config.js
파일에 아래 코드를 추가한다.
module.exports = {
...,
dependencies: {
...,
"react-native-sqlite-storage": {
platforms: {
android: {
sourceDir:
"../node_modules/react-native-sqlite-storage/platforms/android-native",
packageImportPath: "import io.liteglue.SQLitePluginPackage;",
packageInstance: "new SQLitePluginPackage()"
}
}
}
...
}
...
};
iOS에서는 준비된 DB를 Xcode를 통해 추가해줘야 한다.
Add Files to [project name]
를 선택한다.www
폴더를 추가하는데 Create folder references를 추가한다.android/settings.gradle
파일에 코드 추가하기include ':react-native-sqlite-storage'
project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/platforms/android')
android/app/build.gradle
파일을 열고, dependencies 부분에 코드 추가하기implementation project(':react-native-sqlite-storage')
MainApplication.java
에서 getPackages()
메소드 내부에 코드 추가하기Native module SQLite tried to override SQLitePlugin.check the getPackage() method
이런 에러가 발생한다.//import 부분에 추가하기
import org.pgsqlite.SQLitePluginPackage;
...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new SQLitePluginPackage());
return packages;
}
android/app/src/main/assets/www
폴더를 생성하고 DB파일을 저장합니다.미리 저장된 DB를 불러오기 위해서는 SQLite.openDatabase()
메소드를 사용한다.
이 때, 3가지 파라미터를 받는데
1. db config 객체
name
: 불러올 db의 정확한 파일이름location
: db가 담겨있는 위치인데, www
폴더에 담았을 때는 default
를 전달한다.createFromLocation
: db가 www
에 담겨있을 때는 1
을 전달한다.DB
객체를 파라미터로 전달받는다.error
를 파라미터로 전달받는다.import SQLite from 'react-native-sqlite-storage';
let db;
const App = () => {
useEffect(() => {
db = SQLite.openDatabase(
{
name: 'TestDB.db',
location: 'default',
createFromLocation: 1,
},
(DB) => {
console.log('불러오기 성공');
},
(error) => {
console.log('에러발생: ', error);
});
}, [])
...
}
useEffect
hook을 사용하여, App 컴포넌트가 마운트될 때 db를 불러온다. 내장 db에 접근하기 위해서는 sql 코드를 db로 보내줘야 하는데 이를 transaction
이라고 표현한다.
블록체인에서 나오는 그 transaction이네..
transaction()
메소드를 사용해서 sql 코드를 전달할 수 있다.tx
객체를 전달받는다.db.transaction((tx) => {
//something...
})
tx.executeSql()
이며, 파라미터로db.transaction((tx) => {
tx.executeSql(`SELECT * FROM test;`, [], (tx, results) => {
const rows = results.rows;
let users = [];
for (let i=0; i<rows.length; i++) {
console.log(rows.item(i));
}
})
})
다음과 같이 db의 데이터들을 성공적으로 가져오는 것을 확인할 수 있다.
tx.executeSql()
메소드의 success 콜백함수를 자세히 살펴보면, tx
와 results
객체를 전달한다.results
에는 rows
객체가 있고, rows.item()
메소드가 실질적으로 테이블의 데이터 한줄씩을 불러오는 것이다.불러온 db의 데이터들을 컴포넌트에 렌더링해보도록 할 것이다.
db에서 불러온 데이터를 저장할 users
state을 생성하고, 여기에 rows.item()
을 하나씩 넣을 것이다.
const [users, setUsers] = useState([]);
...
db.transaction((tx) => {
tx.executeSql(`SELECT * FROM test;`, [], (tx, results) => {
const rows = results.rows;
let users = [];
for (let i=0; i<rows.length; i++) {
console.log(rows.item(i));
users.push({
...rows.item(i);
});
}
setUsers(users);
});
});
이렇게 users
state과 연결된 UI를 구성하기만 하면 된다.
{
users.map((item) => (
<Text key={item.id}>이름: {item.name}, 나이: {item.age}</Text>
))
}