본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Node.js/Electron

일렉트론 시작 - 2 (Electron study, 기초 사용법)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2019. 12. 23.

 

데이터베이스에 접속하는 방법과 그리고 네이티브js파일에 접근하는 방법에 대해서 살펴 보려고 합니다.

네이티브js라는 것은 일렉트론을 실행해주는, 백그라운드에서 동작하는 js파일을 의미 합니다.(main.js같은)

제이쿼리, 부트스트랩같은 화면을 구성하는데 사용하는 js파일이 아니라, 데이터베이스에 연결해주거나 각종 설정과 같은 내용을 실행해주는 파일이라고 볼 수 있습니다.

 

왜 접근하려고 하냐면..

사용자가 html에서 데이터베이스에 등록, 수정, 삭제를 하려고 하면 해당 명령을 웹 -> 네이티브js로 전달을 해 주어야 하기 때문 입니다.

 

결국 일렉트론은 2가지 Javascript 파일로 이루어 져 있다고 볼 수 있습니다.

1. html에서 사용하는 화면 구성용 Javascript 파일(*.js)

1. 어플리케이션 자체를 움직이는 Javascript 파일(*.js)

그래서 개발하다보면, 가끔 햇갈릴때가 많습니다..ㅠ

 

일단..마리아, 몽고db등 이러한 데이터 베이스는 외부 서버를 두고 있어야 하므로 간단하게 데이터베이스를 적용 시켜 보기 위해서 sqlite를 붙여 보기로 하였습니다.

프로젝트 구성 및 내용은 앞전 시간에 것을 사용하였습니다.

다만 html 파일은 html 디렉토리를 만들어 주어 분리 하였습니다.

 

npm install --save sqlite3

쭉쭉 설치가 잘 됩니다.

 

만약 비주얼 스튜디오 코드를 사용중이라면 sqlite와 관련된 플러그인을 설치하여주면 나중에 개발하는 데 편리 합니다.

아무튼, db라는 폴더를 만들어주고 내부에다 my.db라는 비어있는 파일을 만들어 주었습니다.

요렇게~

 

자..그리고 sqlite와 관련된 코드를 main.js 한번 붙여서 동작시켜 보았습니다.

동작 명령어는 스크립트에 미리 등록 한 npm start 입니다.

* sqlite와 관련된 소스코드 (단순하게 커넥션을 붙은 후 종료하는 코드 입니다.)

    //아래 ./db/my.db의 경로를 데이터베이스 절대 경로를 써 주셔도 됩니다.
    // my.db가 위치하고있는 경로가 만약 d드라이브에 db라는 폴더에 있다면 D:/db/my.db
    let db = new sqlite3.Database('./db/my.db', sqlite3.OPEN_READWRITE, (err) => { 
    if (err) {
        console.error(err.message);
    } else {
        console.log('Connected to the mydb database.');
    }
    });
    db.close((err) => {
    if (err) {
        return console.error(err.message);
    }
        console.log('Close the database connection.');
    });    

 

동작시켰더니 아래사진처럼 오류가 발생 하였습니다.

음..시키는데로 한 것 뿐인데..

 

역시나 뭐 한번에 되는게 없습니다..ㅠ

해당 오류는 일렉트론이 동작을 하면서 네이티브관련된 소스코드를 못 물고(?) 동작했다는 내용입니다. (맞나..?)

아무튼 이를 해결하는 방법으로는 일렉트론 명령어에서 install-app-deps를 실행하면 된다고 합니다.

아래 사진처럼 해당 명령어를 스크립트에 추가하고 명령어를 실행하여 봅니다.

npm run postinstall

요렇게 명령어를 package.json에 추가하여 줍니다.

 

명령어를 실행하고 나면 문제없이 잘 동작 합니다.

소스코드를 조금 수정 하여 보았습니다.

데이터베이스에 접속하는 스크립트를 script.js 파일로 분리 하였습니다.

const sqlite3 = require('sqlite3').verbose();
    
module.exports.connector = function(){
    let db = new sqlite3.Database('./db/my.db', sqlite3.OPEN_READWRITE, (err) => {
    if (err) {
        console.error(err.message);
    } else {
        console.log('Connected to the mydb database.');
    }
    });
    db.close((err) => {
    if (err) {
        return console.error(err.message);
    }
        console.log('Close the database connection.');
    });        
};
module.exports.console = function (){  //그냥 테스트용
    console.log(1234);
}

 

요렇게 한뒤에 웹페이에 해당 코드를 추가 하였습니다.

웹페이지에 해당 코드를 붙여넣으면 웹에서 네이티브js로 명령을 전달 할 수 있습니다.

<script>
    const scripts = require('./script.js');  //html 파일이 있는 경로에 위치, 
    const {ipcRenderer} = require('electron');
    
    scripts.connector();
</script>

여기까지 프로젝트 구조는 아래 사진처럼 구성 하였습니다.

 

위 소스코드가 네이티브js 파일에 대해서 html이 명령을 전달하는 방법 입니다.

그런데 해당 방법을 적용하면 사진처럼 문제가 있다고 경고내용이 콘솔로 출력 됩니다.

해당 내용은..저런식으로 네이티브js를 가져다 쓰는건 곧 종료할 예정이다..라는 내용입니다..ㅠ

 

구글링 하면 저런식으로 네이티브js파일을 가져가 사용하던데...아이고..

네이티브js를 호출하려면 해당 스크립트 파일을 직접 import (require) 하지 말고 이벤트를 주고받는 형식으로 바꾸라고 나와 있습니다.

결론은 위 방식은 지금은 동작하지만 결국 나중에는 못 써먹는다는 뜻 입니다.

 

위 코드를 이벤트로 주고받는 방식으로 바꾸려면 main.js 에서 이벤트를 받아서 명령을 수행 한 이후에 다시 이벤트를 전달 해 주는 방식으로 바꾸어야하며,

웹에서는 사용 할 라이브러리를 직접 import(require) 하지 말고 main.js에 이벤트를 전달하는 방식으로 수정하면 됩니다.

 

아..말이 어렵습니다..

아래 코드는 보는게 이해하시는데 나을 것 같습니다.

main.js파일 내용입니다.

const { app, BrowserWindow } = require('electron');

function createWindow () {  // 브라우저 창을 생성
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true  //require같은 기능 사용 가능하도록
    }
  });
  //브라우저창이 읽어 올 파일 위치
  win.loadFile('./html/index.html');
  //win.setMenu(null); //top 메뉴 세팅

  
}

app.on('ready', createWindow);



const sripts = require('./script.js');  //데이터베이스 접속과 관련된 라이브러리 위치.
const { ipcMain } = require('electron')  //이벤트 송/수신 관련 라이브러리

ipcMain.on('dbconn', (event, arg) => {  //dbconn요청이 들어오면,
  console.log(arg) // html로 받은 파라미터 
  sripts.connector();
  event.reply('reply', 'db-con-ok');  //결과 완료 후 보낸다.
});


 

다음은 요청하는 html 파일의 내용 입니다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> hello </title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>

<body>
    <a href='other.html'>next</a>
</body>
</html>
<script>

const { ipcRenderer } = require('electron');

ipcRenderer.send('dbconn', 'go go db');  //네이티브js에게 요청한다.

ipcRenderer.on('reply', (event, arg) => {   //네이티브js로부터 받는 녀석
  console.log(arg) 
})

</script>



 

요렇게 수정후에 실행하면 경고내용 없이 잘 동작합니다.

경고가 사라지고 원하는 콘솔 내용이 정상적으로 출력 됩니다.

 

여기서 핵심은 native js에 존재하는 내용을 직접 호출하면 안된다는 것 입니다.

 

ELECTRON_STUDY.zip
0.03MB

* 해당첨부파일에는 node_modules가 없습니다. npm install을 실행하셔서 받으면 테스트 해 볼 수 있습니다.

* 바이러스, 악성코드 절.대.없.습.니.다 ^-^

 

 

 

─────────────────────────────────────────────────

─────────────────────────────────────────────────

* 추가 : 노트북님의 의견을 토대로 만든 preload 방식의 네이티브js 호출 방법

* 매우 간단하게 하였습니다. ^-^

 

네이티브 js를 호출하는 방법중 html에서 electron 모듈을 호출하지 않고 사용하는 방법이 있었습니다.

위 방식이 보안에 취약하다고 하는 의견을 따라서 html에서 require를 통해 호출하지 않고 사용하는 방법 입니다.

먼저 세팅을 바꾸어야 합니다.

main.js에서 preload 방식을 사용한다고 선언하여야 합니다.

const { app, BrowserWindow } = require('electron');
const path = require("path");

function createWindow () {  // 브라우저 창을 생성
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true, 
      enableRemoteModule: false, 
      preload: path.join(__dirname, "preload.js")  //요 부분!!!!
    }
  })

  //브라우저창이 읽어 올 파일 위치
  win.loadFile('./index.html')
}

app.on('ready', createWindow);

 

path 모듈을 사용한 것은 해당 경로를 일반 상대경로로 써주면(예: ./preload.js 이런식) 파일을 찾지 못하는 오류가 발생 하기 때문 입니다.

절대경로를 써 주던가 아니면 path모듈을 사용해서 경로와 관련된 오류를 만나지 않도록 해 주어야 합니다.

 

다음 파일은 preload.js 입니다.

const { contextBridge } = require("electron");

contextBridge.exposeInMainWorld(
    "api", {  //이름은 api입니닷
        request : (channel, data) => {
            console.log(channel, data);//html에서 넘기는 값, channel로 구분하면 됩니다.
            data.calback({result:'성공'});
        }
    }
);

 

html에서 사용할 이름은 api이고 함수명칭은 request 입니다.

요렇게만 봐서는 이해가 잘 되지 않으므로 바로 index.html파일을 확인하여 보겠습니다.

<!doctype html>
<html lang="en-US">
<head>
    <meta charset="utf-8"/>
    <title>제목</title>
</head>
<body>
    <script>

        window.api.request("넘긴값", {value:1234, key:'key', calback :(result)=>{
            console.log('요청 후 결과 값 : ',result);
        }});

    </script>
</body>
</html>

 

preload.js와 index.html을 서로 비교하여 보면 그리 어렵지 않는 코드임을 알 수 있습니다.

파라미터로 각종 값을 넘기고 콜백함수를 통해서 결과를 나타내고 있습니다.

위 내용을 실행하여보면..

요렇게 출력 되네요!

 

위 방식으로 하면 electron 모듈이 html 코드에서 나타나는 일이 없으므로 보안에 좀 더 좋다고 하는 것 같습니다.

내부 파일 구조 모습

 

간단하게 테스트 하려면 아래 파일을 받아서 실행하여 보세요.

프리로드 방식.zip
0.01MB

 

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글