[SQLite: 1편]react-native-sqlite-storage 사용하기

Felix, JooHwan Yeon·2022년 1월 20일
1
post-thumbnail

☕️ 참고자료

  1. react-native-sqlite-storage 공식문서
  2. 참고자료1
  3. 참고자료2
  4. 공식문서 예제

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()"
        }
      }
    }
    ...
  }
  ...
};

sqlite DB 준비하기

iOS

iOS에서는 준비된 DB를 Xcode를 통해 추가해줘야 한다.

  1. 'www' folder를 ios폴더 내에 생성하기
    • 폴더의 다른 네이밍은 동작되지 않는다고 하니 꼭 이름을 맞춰야 한다.
  2. 'www' folder에 미리 준비한 DB를 추가하기
    • 이때 DB의 이름은 앱에서 불러올 때 사용하게 된다.
  3. Xcode로 workspace를 실행한다.
  4. 프로젝트 이름으로 된 폴더를 우클릭해서 Add Files to [project name]를 선택한다.
  5. 여기서 www 폴더를 추가하는데 Create folder references를 추가한다.
안드로이드
  1. 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')
  1. android/app/build.gradle 파일을 열고, dependencies 부분에 코드 추가하기
implementation project(':react-native-sqlite-storage')
  1. 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;
}
  1. android/app/src/main/assets/www 폴더를 생성하고 DB파일을 저장합니다.

SQLite 사용

DB 불러오기

미리 저장된 DB를 불러오기 위해서는 SQLite.openDatabase() 메소드를 사용한다.
이 때, 3가지 파라미터를 받는데
1. db config 객체

  • name: 불러올 db의 정확한 파일이름
  • location: db가 담겨있는 위치인데, www 폴더에 담았을 때는 default를 전달한다.
  • createFromLocation: db가 www에 담겨있을 때는 1을 전달한다.
  1. success callback
    • 불러오기에 성공했을 때 실행되는 콜백함수
    • 불러온 DB 객체를 파라미터로 전달받는다.
  2. error callback
    • 불러오기에 실패했을 때 실행되는 콜백함수
    • 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를 불러온다.
  • 꼭 App 컴포넌트일 필요가 없고 내장 db가 필요한 컴포넌트에서 불러오면 된다.
  • db 불러오기가 성공하거나 실패했을 때 로그를 출력한다.

DB 접근하기

내장 db에 접근하기 위해서는 sql 코드를 db로 보내줘야 하는데 이를 transaction이라고 표현한다.
블록체인에서 나오는 그 transaction이네..

  1. 불러온 db 객체에 transaction() 메소드를 사용해서 sql 코드를 전달할 수 있다.
    • 파라미터는 콜백함수를 받고, 콜백함수는 tx 객체를 전달받는다.
db.transaction((tx) => {
    //something...
})
  1. 실질적으로 sql 코드를 실행하는 것은 tx.executeSql()이며, 파라미터로
    • sql 코드(string) required
    • 어떤 배열(array) (불러오는 데이터 개수를 의미하는걸까)
    • success callback
    • error callback
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 콜백함수를 자세히 살펴보면, txresults 객체를 전달한다.
  • 이 때 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>
    ))
}
profile
🚀 세상과 인간이 궁금한 사상가, 그 속에서 가치를 찾는 공학자이자 사업가

0개의 댓글