코딩/React

ElectronJS 일렉트론 강좌 1편 SQLite3 sql.js

드리프트 2021. 9. 18. 12:36
728x170

 

안녕하세요?

 

오늘은 Electron 강좌를 시작해 볼까 합니다.

 

https://www.electronjs.org/

 

ElectronJS는 웹상의 기술인 HTML, CSS, Javascript로 데스크톱 애플리케이션을 만들 수 있는 NodeJS 패키지인데요.

 

HTML, CSS, Javascript로 작동되다 보니까 크로스 플랫폼 앱 제작에 있어 가장 좋습니다.

 

윈도, 맥, 리눅스 등 크로스 플랫폼 앱을 제작하려고 할 때 가장 많이 쓰이는 프레임웍이 Qt인데요.

 

Qt로 GUI 만들다 보면 뭔가 어렵습니다. C++로 Class를 사용하다 보니 조금 어지러운데요.

 

그래서 최근에는 NodeJS를 이용한 GUI앱이 많이 나오고 있습니다.

 

초창기에는 NodeJS 진영에 Nw.js라는 패키지가 나왔는데, Node와 WebKit을 결합하여 HTML, CSS, Javascript로 데스크톱 앱을 만드는 패키지였습니다.

 

Nw.js 가 초창기에는 잘 나가다가 Github에서 Atom editor (편집기)를 내놓으면서 같이 나온 게 ElectronJS입니다.

 

ElectronJS는 크로미엄(Chromium) 기반에다 NodeJS를 결합한 형태인데요.

 

전 세계에서 가장 많이 쓰이는 웹브라우저 엔진이 바로 크로미엄(Chromium)입니다.

 

최근에는 윈도 10의 Edge 브라우저도 크로미엄(Chromium) 기반으로 새로 만들었고, 아주 빠른 속도로 데스크톱 웹 브라우저 점유율을 끌어올리고 있습니다.

 

이렇듯 오픈소스 진영에서 아주 큰 점유율을 가지고 있는 크로미엄(chromium)입니다.

 

ElectronJS의 작동 방식을 간략하게나마 이해하고 넘어가는 것이 이번 강좌에 도움이 된다고 생각합니다.

 

ElectronJS는 백엔드에서는 NodeJS를 이용해서 크로미엄(chromium) 창을 띄웁니다.

 

크로미엄(chromium) 창을 띄울 때 우리가 만든 웹페이지를 지정해서 로드시키는 거죠.

 

한마디로 말해서 NodeJS로 웹브라우저를 띄운다고 생각하시면 됩니다.

 

우리가 만든 웹페이지는 ReactJS로 만들 수도 있고 VueJS로 만들 수도 있습니다.

 

일반 웹 프레임웍은 다 적용되다 보니 데스크톱 앱에서 GUI를 너무 쉽게 만들 수 있고 또, 너무 다양한 모습의 화면도 제작할 수 있어 그 사용가치가 나날이 상승하고 있는 실정입니다.

 

그리고 일반적인 연산은 크로미엄(Chromium) 브라우 저상에서 자바스크립트로 진행할 수 있어 딱히 백엔드 작업이 필요가 없는데요.

 

그래도 데스크톱 앱이면 디스크에서 파일 읽기 및 저장, SQL 데이터베이스 읽기 등 일반적으로 Qt 같은 하드 한 프레임웍에서만 작동하는 수많은 작업이 NodeJS를 이용하면 ElectronJS에서도 가능합니다.

 

그래서 오늘은 단순하게 ElectronJS를 띄워서 ReactJS 웹앱을 올리는 단순한 작업 말고 ElectronJS의 백엔드 쪽을 좀 더 깊게 공부해 보겠습니다.

 

 

1. SQLite3

 

필자의 컴퓨터에는 Kodi라는 멋진 멀티미디어 플레이어가 있는데, 예전에는 XBMC(Xbox Media Center)였습니다.

 

초창기 Xbox를 개조하면 멋진 홈 미디어 플레이어가 됐었는데 바로 XBMC가 그 역할을 했었고, 이제는 Kodi라는 이름으로 개명해서 홈 멀티미디어 구축에 가장 유명한 프로그램이 되었습니다.

 

필자도 Kodi라는 앱으로 컴퓨터에 저장되어 있는 각종 동영상을 관리하는데요.

 

이 Kodi라는 앱에서 사용하는 DB 기술이 바로 SQLite3입니다.

 

SQLite3는 C로 만든 오픈소스 SQL 프로그램입니다.

 

오프소스고 그 기능이 막강해 애플, 삼성 등 핸드폰에서도 사용되고 있고, 거의 모든 앱이나 프로그램에서 간단한 자체 DB 기능을 이용할 때 SQLite3가 사용되고 있습니다.

 

SQLite3는 NodeJS로 코팅된 게 있는데 바로 node-sqlite3 패키지입니다.

 

그러나 node-sqlite3는 백엔드 쪽에서만 작업할 수 있어 우리가 만들려고 하는 ElectronJS에서는 사용할 수 없습니다.

 

그래서 보통 ElectronJS에서 사용하는 SQLite3는 웹 어셈블리로 컴파일된 sql.js 패키지를 사용합니다.

 

이제 sql.js를 이용해서 Kodi가 만든 미디어 DB를 읽어서 ElectronJS에 뿌려주는 나만의 동영상 리스트 앱을 만들어 보도록 하겠습니다.

 

 

2. Sql.js 맛보기

 

https://github.com/sql-js/sql.js/

 

GitHub - sql-js/sql.js: A javascript library to run SQLite on the web.

A javascript library to run SQLite on the web. . Contribute to sql-js/sql.js development by creating an account on GitHub.

github.com

 

일단 우리가 ElectronJS 앱에서 사용할 SQLite3 관련 명령어들에 대해 알아보기 위해 테스트 앱을 만들어서 직접 DB 파일은 읽고 Query를 검색해 보겠습니다.

 

먼저, 간단한 NodeJS앱을 만들어 보겠습니다.

 

mkdir db
cd db
npm init -y
npm i sql.js xml2json-light

 

db라는 폴더를 만들고 거기에 sql.js를 설치했습니다.

 

xml2 json-light 패키지는 나중에 쓰일 패키지인데요.

 

XML 형식의 데이터를 JSON 형식으로 바꿔 주는 패키지입니다.

 

이제 Kodi 가 사용하는 DB파일은 가져와야겠네요.

 

DB 파일은 윈도의 경우 AppData파일 밑에 Kodi 폴더가 있고 그 밑에 UserData 폴더가 있습니다.

 

그 폴더에서 MyVideo라고 시작하는 확장자가. db인 파일이 있는데 이게 바로 우리가 원하는 파일입니다.

 

MyVideo 뒤에 숫자는 Kodi가 버전 업하면서 바뀌는데 크게 상관없습니다.

 

그럼 이 파일을 아까 db 폴더에 복사해 놓으십시오.

 

이제 본격적으로 sql.js를 이용해서 DB 파일은 읽어 보도록 하겠습니다.

 

sql.js는 다음과 같이 사용하시면 됩니다.

 

const fs = require("fs");
const initSqlJs = require("sql.js");

const dbFileName = "./MyVideos116.db";

initSqlJs().then(function (SQL) {
  const db = new SQL.Database(fs.readFileSync(dbFileName));
  var stmt = "select count(*) from movie_view";
  const res = db.exec(stmt);
  console.log(res);
});

 

sql.js에서 initSqlJs를 불러오고 initSqlJs로 sql.js를 초기화하면 콜백으로 SQL 객체를 리턴하는데 이 SQL 객체로 DB 작업을 하게 됩니다.

 

위 코드를 한번 실행해 볼까요?

STATEMENT에 "select count(*) from movie_view"라고 명시했습니다.

 

count(*) 명령어는 SQL에 있는 명령어인데요. 전체 데이터 개수를 리턴하는 겁니다.

 

from 뒤에 movie_view라고 되어 있는데 우리가 사용할 테이블입니다. (좀 더 엄밀히 따지면 movie 테이블이 맞는데 movie_view 란 VIEW 테이블이 우리가 원하는 정보가 모두 들어 있어 이걸로 사용할 예정입니다.)

 

그런데 데이터 개수를 리턴하라고 했는데 뭔가 이상하죠?

 

여기서 잘 보셔야 할게 바로 console.log로 출력된 형식인데요.

 

가장 바깥쪽에 []가 있는 걸로 봐서 Array 배열을 리턴하는 걸로 보입니다.

 

그리고 [] 안쪽에 {}가 있는 걸로 봐서는 JSON 객체를 리턴하는 걸로 보이네요.

 

그러면 우리가 console.log 해야 할 res값은 뭐가 될까요?

 

가장 바깥쪽이 배열이니까 res [0]을 사용해야 하고 그다음 객체니까 원하는 values를 참조해야 합니다.

 

그래서 res [0]. values라고 console.log 하면 원하는 숫자가 나오게 됩니다.

  console.log(res[0].values);

 

총 707개가 있는데 출력 결과물이 또 배열이네요.

 

[ [ 707 ] ]가 있는 걸로 봐서는 배열이 두 개가 겹쳐 있네요.

 

console.log(res[0].values[0][0]);

 

이제 우리가 원하는 타입의 데이터가 나왔습니다.

 

총 707개가 있네요.

 

그럼 전체적인 movie_view 테이블(뷰)의 구조를 보면서 어떤 원하는 데이터를 가져올지 생각해 볼까요?

 

table의 schema를 보려면 sqlite3 명령어가 필요한데

sqlite3는 맥에서는 brew, 리눅스에서는 apt-get으로 쉽게 설치할 수 있습니다.

 

윈도에서는 sqlite3.exe 파일을 직접 홈페이지에서 찾아서 설치해야겠죠.

 

그래서 보통 개발자들은 리눅스 환경이나 맥 환경을 선호하게 됩니다.

 

필요한 라이브러리나 패키지를 쉽게 설치할 수 있기 때문이죠.

 

sqlite3.exe가 없다면 다른 방법으로 알아볼까요?

 

const fs = require("fs");
const initSqlJs = require("sql.js");

// const dbFileName = require('./dbconfig');
const dbFileName = "./MyVideos116.db";

initSqlJs().then(function (SQL) {
  const db = new SQL.Database(fs.readFileSync(dbFileName));
  var stmt = "select * from movie_view limit 1";
  const res = db.exec(stmt);
  console.log(res);
});

바로 select * 값을 이용하는 겁니다. 그리고 마지막에 limit 1을 두어 한 개만 불러오라고 지정하는 거죠.

 

결과값을 볼까요?

 

columns 값에 우리가 원하는 테이블이 나와 있습니다.

 

그럼 values 값을 살펴볼까요?

 

  console.log(res[0].values);

드디어 데이터가 나왔습니다.

 

그럼 우리가 movie_view에서 골라야 할 거는 뭐가 될까요?

 

시간 관계상 제가 선별해 보겠습니다.

 

idMovie, c00, c01, c03, c08, c16, c19, c20, premiered, strPath,rating, uniqueid_value

일단 우리가 만들 앱에서는 위와 같은 항목만 사용할 예정입니다.

 

그럼 코드를 변경해 볼까요?

 

const fs = require("fs");
const initSqlJs = require("sql.js");

const dbFileName = "./MyVideos116.db";

initSqlJs().then(function (SQL) {
  const db = new SQL.Database(fs.readFileSync(dbFileName));
  var stmt = `select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath,rating from movie_view order by idMovie desc limit 1`;
  // var stmt = "select * from movie_view limit 1";
  const res = db.exec(stmt);
  console.log(res[0].values);
});

STATEMENT 인 stmt에 select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath, rating from movie_view order by idMovie desc limit 1; 이렇게 적었습니다.

 

마지막에 보시면 order by idMovie desc라고 했으니까 idMovie가 1번부터 불러오겠죠!

 

만약 order by idMovie asc라고 하면 가장 큰 숫자부터 불러오게 됩니다.

 

limit 1 은 1개만 불러오라는 뜻입니다.

 

이제 결과값을 보시면 c00 이 제목이 되겠네요.

 

c01은 줄거리, c03은 간략한 설명 정도,  c08이 포스터 이미지, c19가 youtube 링크, c20이 fanart(팬아트), premiered는 극장 개봉일, strPath는 하드디스크 경로, rating은 IMDB 평점이 되겠습니다.

 

이제 우리가 원하는 정보는 다 확보했네요.

 

근데 c08, c20이 각각 포스터 링크와 팬아트 링크인데 이게 좀 복잡하게 자료 값이 나오고 있습니다.

 

바로 XML 타입인데요.

 

이 XML 타입을 JSON 형태로 바꿔야 하는데 이때 필요한 패키지 중에 가장 가볍고 좋은 게 바로 xml2 json-light입니다.

 

이제, 본격적으로 가장 어려운 c08과 c20의 XML 타입의 자료에서 첫 번째 값만 가져오는 코드를 만들어 보겠습니다.

 

 

XML 타입이라 복잡한데요.

 

잘 보시면 thumb에 preview 항목에 에 있는 링크와 그다음 <thumb></thumb> 사이에 있는 링크를 보시면 한 개는 w500 즉, width 가 500인 이미지이고 두 번째는 original 이미지입니다.

 

우리가 필요한 건 width 가 500인 게 좋겠죠. 웹 트래픽을 위해서 작은 게 좋을 거 같습니다.

 

그럼 본격적으로 XML에서 데이터를 추출해 보겠습니다.

 

일단 sql.js가 리턴하는 데이터를 JSON 형태로 되어 있는 Array형태로 받는 게 편합니다.

 

왜냐하면 나중에 Array.map 함수를 쓸 수 있기 때문입니다.

 

그래서 일단 _rowsFromSqlDataArray라는 함수를 만들어 보겠습니다.

 

let _rowsFromSqlDataArray = function (object) {
  let data = [];
  let i = 0;
  let j = 0;
  for (let valueArray of object.values) {
    data[i] = {};
    j = 0;
    for (let column of object.columns) {
      Object.assign(data[i], { [column]: valueArray[j] });
      j++;
    }
    i++;
  }
  return data;
};

이 함수는 객체를 받으면 그 객체를 JSON 형식의 배열로 되돌려 주는 함수입니다.

 

이 함수는 StackOverflow에서 얻었습니다.

 

그럼 전체 코드를 보실까요?

 

const fs = require("fs");
const initSqlJs = require("sql.js");

const dbFileName = "./MyVideos116.db";

let _rowsFromSqlDataArray = function (object) {
  let data = [];
  let i = 0;
  let j = 0;
  for (let valueArray of object.values) {
    data[i] = {};
    j = 0;
    for (let column of object.columns) {
      Object.assign(data[i], { [column]: valueArray[j] });
      j++;
    }
    i++;
  }
  return data;
};

initSqlJs().then(function (SQL) {
  const db = new SQL.Database(fs.readFileSync(dbFileName));
  // var stmt = `select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath, rating from movie_view order by idMovie desc limit 1`;
  var stmt = `select c08 from movie_view order by idMovie desc limit 1`;
  // var stmt = "select * from movie_view limit 1";
  var res = db.exec(stmt);
  // console.log(res[0].values);
  if (res.length === 0) console.error("No Data Found!");
  else {
    res = _rowsFromSqlDataArray(res[0]);
    console.log(res);
    db.close();
  }
});

 

실행해 보겠습니다.

 

우리가 원하는 JSON 형식의 데이터에 배열 형식으로 리턴되는 걸 볼 수 있습니다.

 

그럼 본격적으로 위에서 볼 수 있듯이 JSON 형태인 c08의 값인 XML을 파싱 해서 한 개만 가져오도록 하겠습니다.

 

일단 여기에 필요한 함수인 sqlFilterXml2 Json 함수를 만들어 보겠습니다.

 

const sqlFilterXml2Json = (movies) => {
  // movies.c08, c20 is the type of xml
  // movies is array
  // if movies is object, below is error
  const parser = require("xml2json-light");

  movies.forEach((row) => {
    if (row.c08 !== "") {
      var poster = parser.xml2json(row.c08);
      // console.log(poster);
      var poster_temp = [];

      if (Array.isArray(poster.thumb)) {
        poster.thumb.map((i) => {
          if (i.aspect === "poster") {
            poster_temp.push(i);
          } else if (i.aspect === undefined) {
            poster_temp.push(i);
          }
          // console.log(poster_temp);
        });
      } else {
        poster_temp.push(poster.thumb);
      }

      row.c08 = poster_temp[0].preview;
    }

    if (row.c20 !== "") {
      var fanart = parser.xml2json(row.c20);
      // console.log(fanart);
      var fanart_temp = [];

      if (Array.isArray(fanart.fanart.thumb)) {
        fanart.fanart.thumb.map((i) => {
          if (i.aspect === "fanart") {
            fanart_temp.push(i);
          } else if (i.aspect === undefined) {
            fanart_temp.push(i);
          }
        });
      } else {
        fanart_temp.push(fanart.fanart.thumb);
      }

      // console.log(fanart_temp);
      row.c20 = fanart_temp[0].preview;
    }
  });

  if (Object.keys(movies).length === 0) {
    console.log("No data found");
  } else {
    // console.log('return movies');
    return movies;
  }
};

이 함수는 c08과 c20에서 XML 데이터를 추출해서 map으로 돌린 다음 첫 번째 값만 가져오는 루틴입니다.

 

우리가 필요한 거 포스터 한 개랑 팬아트 한 개만 있으면 되거든요.

 

위 코드는 제가 직접 만들었습니다.

 

중간중간 console.log 주석 처리한 부분을 주석처리를 없애면서 보시면 이해하시는데 도움이 되실 겁니다.

 

자 그러면 최종 단계인 우리가 원하는 데이터를 뽑아 보도록 하겠습니다.

 

const fs = require("fs");
const initSqlJs = require("sql.js");

const dbFileName = "./MyVideos116.db";

let _rowsFromSqlDataArray = function (object) {
  let data = [];
  let i = 0;
  let j = 0;
  for (let valueArray of object.values) {
    data[i] = {};
    j = 0;
    for (let column of object.columns) {
      Object.assign(data[i], { [column]: valueArray[j] });
      j++;
    }
    i++;
  }
  return data;
};

const sqlFilterXml2Json = (movies) => {
  // movies.c08, c20 is the type of xml
  // movies is array
  // if movies is object, below is error
  const parser = require("xml2json-light");

  movies.forEach((row) => {
    if (row.c08 !== "") {
      var poster = parser.xml2json(row.c08);
      // console.log(poster);
      var poster_temp = [];

      if (Array.isArray(poster.thumb)) {
        poster.thumb.map((i) => {
          if (i.aspect === "poster") {
            poster_temp.push(i);
          } else if (i.aspect === undefined) {
            poster_temp.push(i);
          }
          // console.log(poster_temp);
        });
      } else {
        poster_temp.push(poster.thumb);
      }

      row.c08 = poster_temp[0].preview;
    }

    if (row.c20 !== "") {
      var fanart = parser.xml2json(row.c20);
      // console.log(fanart);
      var fanart_temp = [];

      if (Array.isArray(fanart.fanart.thumb)) {
        fanart.fanart.thumb.map((i) => {
          if (i.aspect === "fanart") {
            fanart_temp.push(i);
          } else if (i.aspect === undefined) {
            fanart_temp.push(i);
          }
        });
      } else {
        fanart_temp.push(fanart.fanart.thumb);
      }

      // console.log(fanart_temp);
      row.c20 = fanart_temp[0].preview;
    }
  });

  if (Object.keys(movies).length === 0) {
    console.log("No data found");
  } else {
    // console.log('return movies');
    return movies;
  }
};

initSqlJs().then(function (SQL) {
  const db = new SQL.Database(fs.readFileSync(dbFileName));
  var stmt = `select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath, rating from movie_view order by idMovie desc limit 1`;
  // var stmt = "select * from movie_view limit 1";
  var res = db.exec(stmt);
  // console.log(res[0].values);
  if (res.length === 0) console.error("No Data Found!");
  else {
    res = _rowsFromSqlDataArray(res[0]);
    res = sqlFilterXml2Json(res);
    console.log(res);
    db.close();
  }
});

결과물을 보실까요?

 

아주 잘 정리된 JSON객체의 배열을 리턴하는 모습을 볼 수 있습니다.

 

그럼 limit 2라고 코드를 수정해볼까요?

보시면 전체 리턴되는 게 배열입니다.

 

그 배열 안에 JSON 객체 2개가 있는 걸 볼 수 있죠.

const findAll = (stmt, callback) => {
  SQL().then((SQL) => {
    SQL.dbOpen = function (databaseFileName) {
      try {
        return new SQL.Database(fs.readFileSync(databaseFileName));
      } catch (error) {
        console.log("Can't open database file.", error.message);
        return null;
      }
    };

    let db = SQL.dbOpen(dbFileName);
    var res = db.exec(stmt);
    // console.log(res.legth);
    if (res.length === 0) callback([{ error: "No Data found!" }]);
    else {
      res = _rowsFromSqlDataArray(res[0]);
      res = sqlFilterXml2Json(res);
      callback(res);
      db.close();
    }
  });
};

이렇게 하면 React에서 map으로 배열을 돌리면서 화면에 뿌려주기 쉽습니다.

 

이제 이 코드를 함수 형태로 만들어 볼까요?

 

ElectronJS에서 우리가 원하는 Query를 이용해서 영화를 검색할 때 필요하겠죠.

 

 

3. 백엔드 Sqlite3 코드 작성하기

 

ElectronJS에서 간단히 함수 호출로 우리가 원하는 데이터를 리턴하는 함수를 만들어 보겠습니다.

 

먼저 findAll 이란 함수입니다.

 

const findAll = (stmt, callback) => {
  initSqlJs().then((SQL) => {
    SQL.dbOpen = function (databaseFileName) {
      try {
        return new SQL.Database(fs.readFileSync(databaseFileName));
      } catch (error) {
        console.log("Can't open database file.", error.message);
        return null;
      }
    };

    let db = SQL.dbOpen(dbFileName);
    var res = db.exec(stmt);
    // console.log(res.legth);
    if (res.length === 0) callback([{ error: "No Data found!" }]);
    else {
      res = _rowsFromSqlDataArray(res[0]);
      res = sqlFilterXml2Json(res);
      callback(res);
      db.close();
    }
  });
};

 

findAll이란 함수는 STATEMENT를 stmt로 전달하고 그 결과를 callback함수로 리턴하게 됩니다.

 

그럼 이 함수를 이용하는 getData 함수를 만들어 볼까요?

 

const getData = (query) => {
  // var stmt = `select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath, rating from movie_view where c00 like '%${query}%' order by idMovie desc`;
  // console.log(query);
  return new Promise((resolve, reject) => {
    findAll(query, (res, err) => {
      if (err) reject(err);
      else resolve(res);
    });
  });
};

 

이 함수가 우리가 ElectronJS에서 백엔드로 호출할 함수입니다.

 

query에 우리가 원하는 STATEMENT를 넣으면 그 값을 Promise로 리턴하는 함수입니다.

 

참고로 포스터와 팬아트 부분만 빼고 데이터를 불러오는 함수도 만들어 두는 게 좋을 듯합니다.

 

실행 속도 측면에서 단순하게 동영상 이름만 불러오는 게 좀 더 빠르겠죠.

 

그래서 findAll2란 함수를 만들었습니다.

 

const findAll2 = (stmt, callback) => {
  initSqlJs().then((SQL) => {
    SQL.dbOpen = function (databaseFileName) {
      try {
        return new SQL.Database(fs.readFileSync(databaseFileName));
      } catch (error) {
        console.log("Can't open database file.", error.message);
        return null;
      }
    };

    let db = SQL.dbOpen(dbFileName);
    var res = db.exec(stmt);
    // console.log(res.legth);
    if (res.length === 0) callback([{ error: "No Data found!" }]);
    else {
      res = _rowsFromSqlDataArray(res[0]);
      callback(res);
      db.close();
    }
  });
};

기본적으로 findAll 함수랑 같은데요.

 

res = sqlFilterXml2 Json(res);  이 부분만 빠졌습니다. 이 부분에서 포스터와 팬아트를 불러오거든요.

 

그럼 getData2를 만들어 보겠습니다.

 

/*
 * getData2 : query without poster, thumbnail (no sqlFilterXml2Json function)
 */
const getData2 = (query) => {
  return new Promise((resolve, reject) => {
    findAll2(query, (res, err) => {
      if (err) reject(err);
      else resolve(res);
    });
  });
};

이제 다 만들었습니다.

 

그럼 getData와 getData2를 export만 하면 되겠네요.

 

module.exports = { getData, getData2 };

 

 

전체 코드는 아래와 같습니다.

 

const fs = require("fs");
const initSqlJs = require("sql.js");

const dbFileName = "./MyVideos116.db";

let _rowsFromSqlDataArray = function (object) {
  let data = [];
  let i = 0;
  let j = 0;
  for (let valueArray of object.values) {
    data[i] = {};
    j = 0;
    for (let column of object.columns) {
      Object.assign(data[i], { [column]: valueArray[j] });
      j++;
    }
    i++;
  }
  return data;
};

const sqlFilterXml2Json = (movies) => {
  // movies.c08, c20 is the type of xml
  // movies is array
  // if movies is object, below is error
  const parser = require("xml2json-light");

  movies.forEach((row) => {
    if (row.c08 !== "") {
      var poster = parser.xml2json(row.c08);
      // console.log(poster);
      var poster_temp = [];

      if (Array.isArray(poster.thumb)) {
        poster.thumb.map((i) => {
          if (i.aspect === "poster") {
            poster_temp.push(i);
          } else if (i.aspect === undefined) {
            poster_temp.push(i);
          }
          // console.log(poster_temp);
        });
      } else {
        poster_temp.push(poster.thumb);
      }

      row.c08 = poster_temp[0].preview;
    }

    if (row.c20 !== "") {
      var fanart = parser.xml2json(row.c20);
      // console.log(fanart);
      var fanart_temp = [];

      if (Array.isArray(fanart.fanart.thumb)) {
        fanart.fanart.thumb.map((i) => {
          if (i.aspect === "fanart") {
            fanart_temp.push(i);
          } else if (i.aspect === undefined) {
            fanart_temp.push(i);
          }
        });
      } else {
        fanart_temp.push(fanart.fanart.thumb);
      }

      // console.log(fanart_temp);
      row.c20 = fanart_temp[0].preview;
    }
  });

  if (Object.keys(movies).length === 0) {
    console.log("No data found");
  } else {
    // console.log('return movies');
    return movies;
  }
};

const findAll = (stmt, callback) => {
  initSqlJs().then((SQL) => {
    SQL.dbOpen = function (databaseFileName) {
      try {
        return new SQL.Database(fs.readFileSync(databaseFileName));
      } catch (error) {
        console.log("Can't open database file.", error.message);
        return null;
      }
    };

    let db = SQL.dbOpen(dbFileName);
    var res = db.exec(stmt);
    // console.log(res.legth);
    if (res.length === 0) callback([{ error: "No Data found!" }]);
    else {
      res = _rowsFromSqlDataArray(res[0]);
      res = sqlFilterXml2Json(res);
      callback(res);
      db.close();
    }
  });
};

const findAll2 = (stmt, callback) => {
  initSqlJs().then((SQL) => {
    SQL.dbOpen = function (databaseFileName) {
      try {
        return new SQL.Database(fs.readFileSync(databaseFileName));
      } catch (error) {
        console.log("Can't open database file.", error.message);
        return null;
      }
    };

    let db = SQL.dbOpen(dbFileName);
    var res = db.exec(stmt);
    // console.log(res.legth);
    if (res.length === 0) callback([{ error: "No Data found!" }]);
    else {
      res = _rowsFromSqlDataArray(res[0]);
      callback(res);
      db.close();
    }
  });
};

const getData = (query) => {
  // var stmt = `select idMovie, c00, c01, c03, c08, c19, c20, premiered, strPath,rating, uniqueid_value from movie_view where c00 like '%${query}%' order by idMovie desc`;
  // console.log(query);
  return new Promise((resolve, reject) => {
    findAll(query, (res, err) => {
      if (err) reject(err);
      else resolve(res);
    });
  });
};

/*
 * getData2 : query without poster, thumbnail (no sqlFilterXml2Json function)
 */
const getData2 = (query) => {
  return new Promise((resolve, reject) => {
    findAll2(query, (res, err) => {
      if (err) reject(err);
      else resolve(res);
    });
  });
};

module.exports = { getData, getData2 };

 

이상 ElectronJS 앱 만들기 중 첫 번째 부분이 SQLite3 부분에 대해 살펴보았습니다.

 

다음 편에서는 본격적으로 ElectronJS와 우리가 만든 백엔드 함수의 연결방식에 대해 알아보겠습니다.

 

그리드형