three.js 웹을 Electron을 이용해 어플리케이션으로 만들기 (1/3)

웹은 웹브라우저를 통해 URL만 알면 어디서든 사용할 수 있는 접근성 ‘갑’ 기술이다. 그러나 이 웹은 치명적인 단점이 있는데 바로 인터넷이 되는 환경이여야 하고 또 기본적으로 로컬 리소스에 자유롭게 접근할 수 없다는 점이다. (물론 웹에서도 사용자의 인터렉션을 통해 로컬 리소스을 접근할 수 있기는 하다.)

아래의 동영을 보자. three.js를 이용해 간단한 3D 뷰 페이지를 만들었고 내 PC에 저장된 모델 파일을 열어 표시한다. 분명 웹으로 만들었지만 이 프로그램의 타이틀을 보면 크롬등과 같은 프로그램이 아닌 독자적인 프로그램이라는 것을 알 수 있다. 이 프로그램은 원래 웹이였던 아이를 Electron이라는 기술을 이용해 인터넷 없이도 실행할 수 있는 단독 실행 파일로 만들어 낸 결과이다.

위의 결과를 만들기 위한 방법을 유튜브 영상으로 만들어 올릴 예정인데, 그전에 블로그에 정리하기 위한 목적으로 이 글을 작성한다.

먼저 간단한 웹페이지를 만들어야 한다. 나는 다음 git 명령을 통해 이미 작성된 프로젝트를 가져왔다.

git clone https://github.com/GISDEVCODE/threejs-with-javascript-starter.git .

패키지를 설치하고 실행까지해보면 다음과 같은 결과를 볼 수 있다.

이 실행 결과를 배포 버전으로 만들어야 한다. Electron은 웹 페이지에 대해 배포 버전을 대상으로 하기 때문이다. 이 프로젝트의 경우 배포 명령은 다음과 같다.

npm run build

위의 명령을 실행하기에 앞서 아래 글을 참고해서 손이 덜 가도록 만드는 것을 추천한다.

Vite로 개발된 웹앱 배포 시 주의점

배포 버전을 만들면 dist라는 폴더에 그 결과가 생성되고 이 dist 폴더의 결과를 Electron으로 단독 실행이 가능한 프로그램으로 만들 수 있다. 이를 위해 다음 명령으로 필요한 패키지를 설치한다.

npm i electron --save-dev

Electron에만 관련된 소스코드를 따로 관리하기 위해 프로젝트에서 electron 폴더를 만들고 electron-run.cjs와 preload.js 파일을 만든다. 확장자가 각각 cjs와 js라는 점에 유의하자. electron-run.cjs의 코드를 다음처럼 입력한다.

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

let win = null;
const createWindow = () => {
  win = new BrowserWindow({
    width: 1024,
    height: 768,
    webPreferences: {
      nodeIntegration: true,
      preload: path.resolve("electron/preload.js")
    }
  });

  win.loadFile(`${path.join(__dirname, '../dist/index.html')}`);
};

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

package.json에 위의 electron-run.cjs를 참조를 다음처럼 해준다.

{
  "main": "./electron/electron-run.cjs",
  ...

  "scripts": {
    ...
    "electron": "electron ."
  },

  ...
}

이제 기본적인 것은 모두 끝났다. 다음 명령을 통해 우리의 웹이 크롬과 같은 브라우저 없이도 단독 실행 가능한 어플리케이션으로 탄생시키기 위해 다음 명령을 실행한다.

npm run electron

원(Circle)을 그리라 했을 때, 나는 남들과 다른 원을 그리고 싶었다.

사용자가 원(Circle)을 그려달라고 했다. 다음처럼 그려주면 완벽하겠다.. 싶다.

하지만 나는 칼로 댄듯 그린 정확한 원 대신에 변곡을 넣어 다음과 같은 원을 먼저 그려주고 싶다.

대부분의 사용자는 원 모서리가 왜 이렇게 매끄럽지 못하냐고 하겠지. 그럼 난 그냥 넣었던 변곡에 대한 잡음을 빼면 그만이다. 내가 얻은건 사용자의 만족 뿐만 아니라 새로운 시도와 도전이다.

GLSL, 무지개 색 표현

수평으로 7개의 무지개 색을 표현하는 프레그먼트 쉐어디 코드입니다. 배열 자료형을 사용하고 있는 예제 코드입니다.

uniform vec2 uResolution;
uniform float uTime;
uniform vec2 uMouse;

vec3 rainbow(float t) {
    vec3 colors[7];

    colors[0] = vec3(1.0, 0.0, 0.0); // 빨강
    colors[1] = vec3(1.0, 0.5, 0.0); // 주황
    colors[2] = vec3(1.0, 1.0, 0.0); // 노랑
    colors[3] = vec3(0.0, 1.0, 0.0); // 초록
    colors[4] = vec3(0.0, 0.0, 1.0); // 파랑
    colors[5] = vec3(0.29, 0.0, 0.51); // 남색
    colors[6] = vec3(0.56, 0.0, 1.0); // 보라
    
    int index = int(t * 7.0);
    return colors[index];
}

void main() {
    vec2 st = gl_FragCoord.xy/uResolution.xy;
    vec3 color = rainbow(st.x);
        
    gl_FragColor = vec4(color,1.0);
}

결과는 다음과 같습니다.

PostgreSQL JSON

원문출처 : http://www.postgresqltutorial.com/postgresql-json/

이 튜토리얼은 PostgreSQL의 JSON 데이터 타입을 어떻게 처리하는지 설명한다. 추가적으로 JSON 데이터를 처리하기 위한 가장 기본적인 연산자와 함수에 대해서도 설명한다.

JSON은 JavaScript Object Notation이다. JSON으로 다양한 데이터를 표현할 수 있지만 특히 키-값으로 구성된 쉽게 해석할 수 있는 텍스트 형식이다. JSON의 주요 사용처는 서버와 웹 어플리케이션 사이의 데이터 교환 및 전송이다. 바이너리 형식과 다르게 JSON은 사람이 이해하고 읽을 수 있는 텍스트 형식이다.

PostgreSQL은 9.2버전 이후부터 JSON 데이터 타입을 지원하며 JSON 데이터를 조작하기 위한 다양한 함수와 연산자를 제공한다.

JSON 데이터에 대한 이해를 위해 테이블을 생성하는 것부터 시작해 보자.

CREATE TABLE orders (
    ID serial NOT NULL PRIMARY KEY,
    info json NOT NULL
);

orders 테이블은 2개의 컬럼으로 구성 되어져 있는데, id는 순서를 나타내는 주 키이며 info는 JSON 형식으로 데이터가 저장될 컬럼이다.

JSON 데이터 INSERT

JSON 컬럼에 데이터를 입력하기 위해서 올바른 JSON 포맷의 데이터가 필요하다. 다음 INSERT 구문은 orders 테이블에 새로운 레코드를 추가한다.

INSERT INTO orders (info)
VALUES
(
    '{ "customer": "John Doe", "items": {"product": "Beer","qty": 6}}'
);

위의 데이터는 6병의 맥주를 가진 John Doe를 의미한다.

마찬가지로 여러 개의 레코드를 추가해 보자.

INSERT INTO orders (info)
VALUES
(
    '{ "customer": "Lily Bush", "items": {"product": "Diaper","qty": 24}}'
),
(
    '{ "customer": "Josh William", "items": {"product": "Toy Car","qty": 1}}'
),
(
    '{ "customer": "Mary Clark", "items": {"product": "Toy Train","qty": 2}}'
);

JSON 데이터 쿼리

JSON 데이터를 쿼리하기 위해서 일반적인 다른 데이터 타입을 쿼리 하듯이 SELECT 구문을 사용한다.

SELECT
 info
FROM
 orders;

PostgreSQL은 그 결과를 JSON 형태의 집합으로 반환한다.

PostgreSQL은 JSON 데이터에 대한 쿼리를 돕기 위해 ->과 ->> 연산자를 제공한다.


  • “->” 연산자는 키로 JSON 데이터 필드를 반환한다.

  • “->>” 연산자는 텍스트로 JSON 데이터 필드를 반환한다.

다음 쿼리는 JSON 형식으로 모든 고객 정보를 얻기 위해 -> 연산자를 사용한다.

SELECT
    info -> 'customer' AS customer
FROM
    orders;

그리고 ->> 연산자를 사용하는 다음의 쿼리문은 텍스트 형식으로 모든 고객 정보를 반환한다.

SELECT
    info ->> 'customer' AS customer
FROM
    orders;

-> 연산자가 JSON 객체를 반환하므로, 연관된 노드를 반환시키기 위해 ->> 연산자와 함께 연결 호출이 가능하다. 예를들어 아래의 구문은 판매된 모든 제품을 반환한다.

SELECT
    info -> 'items' ->> 'product' as product
FROM
    orders
ORDER BY
    product;

먼저 info->’items’는 JSON 객체로써 항목을 반환한다. 그런 뒤 info->’items’->>’product’는 텍스트 형식으로 모든 product를 반환한다.

WHERE 구문에서 JSON 연산자 사용

조건 검색을 지정하기 위해 WHERE 구문에서 JSON 연산자를 사용할 수 있다. 예를들어 Diaper를 가져온 사람을 얻고자 한다면 다음 쿼리를 사용한다.

SELECT
    info ->> 'customer' AS customer
FROM
    orders
WHERE
    info -> 'items' ->> 'product' = 'Diaper'

또 다른 예로써, 다음의 쿼리는 2개의 product를 가지고 있는 레코드를 조회한다.

SELECT
    info ->> 'customer' AS customer,
    info -> 'items' ->> 'product' AS product
FROM
    orders
WHERE
    CAST( info -> 'items' ->> 'qty' AS INTEGER ) = 2

위의 쿼리문에서 주목해야 할 부분은 qty 필드를 숫자 2와 비교하기 위해 INTEGER로 형변환(Casting)했다는 것이다.

JSON 데이터에 집계 함수 적용하기

MIN, MAX, AVERAGE, SUM 등과 같은 집계 함수를 JSON 데이터에 적용할 수 있다. 예를 들어 다음 구문은 최소 개수, 최대 개수, 평균 개수와 전체 판매된 상품의 개수에 대한 쿼리문이다.

SELECT
    MIN (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    MAX (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    SUM (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    AVG (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
     )
FROM orders

PostgreSQL의 JSON 함수

PostgreSQL은 JSON 데이터를 처리하는데 도움이 되는 함수들을 제공한다.

json_each 함수

json_each 함수는 키-값의 집합으로 JSON 객체를 확장시킨다. 다음 구문을 보자.

SELECT
    json_each (info)
FROM
    orders;

만약 문자형으로 키-값의 집합을 얻고자 한다면, json_each_text 함수를 사용하면 된다.

json_object_keys 함수

JSON 객체의 키 집합을 얻고자 한다면, json_object_keys 함수를 사용한다. 다음의 쿼리는 info 컬럼 안의 items 객체의 모든 키를 반환한다.

SELECT
    json_object_keys (info->'items')
FROM
    orders;

json_typeof 함수

json_typeof 함수는 문자열로 JSON 값의 타입을 반환하며, 반환값은 number, boolean, null, object, array 그리고 string이다.

다음 쿼리문은 항목의 데이터 타입을 반환한다.

SELECT
    json_typeof (info->'items')
FROM
    orders;

다음의 쿼리문은 중첩되어진 JSON 객체의 qty 필드의 데이터 타입을 반환한다.

SELECT
    json_typeof (info->'items'->'qty')
FROM
    orders;

JSON 함수에 대해 좀더 깊이 알고자 한다면, PostgreSQL JSON functions을 참고하기 바랍니다.

이 튜토리얼에서는, PostgreSQL JSON 데이터 타입에 대해 살펴보았다. 또한 JSON 데이터를 좀더 효율적으로 처리하기 위해서 가장 많이 활용되는 JSON 연산자와 함수에 대해서도 살펴보았다.