2022년 4월 14일 목요일

[기록용] Html Canvas 를 활용한 시계 만들기 (2)

 canvas 그리기 기능을 이용해서 translate 와 rotation을 구현하는 예제로 구성해 보았습니다.

arc 와 line 을 구성하는 것으로 거의 대부분 구성이 완료되지만, 

초침의 화살표는 각도에 따라 화살표 끝이 rotation 되어야 하기 때문에 화살표 그리는 부분

과, 라인을 그리는 부분을 분리하여 구성해 보았습니다. 


구성한 방법은 초, 분, 시침의 rotation 각도를 가져올 때 시작하는 0도가 시침의 3시 15분 15초가 기준이 되기 때문에 0도에서 시작하기 위해 각 단위 별로 시작 값을 옮겨주는 방식을 

사용하였습니다.  

시침, 분침, 초침의 각도는 초침은 단독으로 구성할 수 있으나, 분침은 초침이 흐른만큼 

더 움직여야 하고, 시침은 분침 초침이 움직인 만큼 더 움직여야 하기 때문에 움직임을 

그에 따라 계산해 주고 있습니다. 

대략 시침을 기준으로 30도 움직임을 가질 수 있고, 분은 60분을 기준으로 30도 움직일 수 

있고, 초는 60초를 기준으로 0.5도를 움직일 수 있습니다. 해당 부분을 계산에 포함 시켰습니다.


아래는 구성하기 위한 함수 들 입니다. 


function makeClocks(ctx, cw, ch, radious) {

let cx = cw/2;

let cy = ch/2;


ctx.clearRect(0,0,cw,ch);

var innerLineWidth = 12;

var outerLineWidth = 4;

var sOptions = getDefaultOptions();

sOptions.strokeStyle = "#008080";

sOptions.lineWidth = innerLineWidth;

drawClockLayers(ctx, cx, cy, radious, sOptions);


sOptions.lineWidth = outerLineWidth;

sOptions.strokeStyle = "#000080";

drawClockLayers(ctx, cx, cy, (radious+innerLineWidth/2+outerLineWidth+1), sOptions);


var markRadious = radious-innerLineWidth/2;

var txtRadious = markRadious-8;


drawClockTextArea(ctx, cx,cy, txtRadious, markRadious, innerLineWidth);


var now = new Date();

sOptions.strokeStyle = "#008080";

sOptions.lineCap = 'butt';

sOptions.joinCap = 'butt';

sOptions.lineWidth = 2;

drawClockSecond(ctx, cx, cy, txtRadious-4, now, sOptions);

sOptions.strokeStyle = "#000080";

sOptions.lineWidth = 4;

drawClockMinute(ctx, cx, cy, txtRadious-8, now, sOptions);

sOptions.strokeStyle = "#000080";

sOptions.lineWidth = 8;

drawClockHour(ctx, cx, cy, txtRadious-12, now, sOptions);

}


function drawClockLayers(ctx,cx,cy,radious, sOptions ) {

ctx.save();

ctx.beginPath();

setCurrentOptions(ctx, sOptions);

ctx.arc(cx,cy,radious,0, Math.PI*2);

ctx.stroke();

ctx.closePath();

ctx.restore();

}


function drawClockTextArea(ctx, cx, cy, txtRadious, markRadious, innerLineWidth ) {

ctx.save();

ctx.beginPath();

ctx.font = "12px consolas";

ctx.textAlign = "center";

ctx.textBaseline = "middle";

ctx.lineCap = "butt";

ctx.lineWidth = 1;

ctx.beginPath();

for ( let i = 0 ; i < 12 ; i++ ) {

ctx.lineWidth = 1;

ctx.strokeStyle = "#000080";

let rd = i*Math.PI/6;

let tx = txtRadious*Math.cos(rd)+cx;

let ty = txtRadious*Math.sin(rd)+cy;

let numStr = ((3+i)%12);

if ( numStr == 0 ) {

numStr = 12;

}

ctx.strokeText(numStr, tx,ty);


ctx.lineWidth = 2;

ctx.strokeStyle = "#00FFFF";


let tsx = (markRadious)*Math.cos(rd)+cx;

let tex = (markRadious+innerLineWidth)*Math.cos(rd)+cx;


let tsy = (markRadious)*Math.sin(rd)+cy;

let tey = (markRadious+innerLineWidth)*Math.sin(rd)+cy;

ctx.moveTo(tsx,tsy);

ctx.lineTo(tex,tey);

ctx.stroke();

}

ctx.closePath();

ctx.restore();

}


function drawArrowPoints(ctx, tx, ty, radians,pw,ph,sOptions ) {

ctx.save();

setCurrentOptions(ctx, sOptions);

ctx.beginPath();

ctx.translate(tx,ty);

ctx.rotate(radians);

ctx.moveTo(ph/2,0);

ctx.lineTo(-ph/2,-pw/2);

ctx.lineTo(-ph/2,pw/2);

ctx.closePath();

ctx.fill();

ctx.stroke();

ctx.restore();

}



function drawClockSecond(ctx, cx, cy, radious ,now, sOptions) {

ctx.save();

setCurrentOptions(ctx, sOptions);

let s = now.getSeconds();

let rd = ((s-15)*Math.PI*6)/180;

let tx = Math.cos(rd)*radious+cx

let ty = Math.sin(rd)*radious+cy;


ctx.beginPath();

ctx.moveTo(cx,cy);

ctx.lineTo(tx,ty);

ctx.stroke();

ctx.closePath();


if ( !sOptions ) {

sOptions = getDefaultOptions();

}

sOptions.strokeStyle = "blue";

sOptions.fillStyle = "#008080";

sOptions.lineWidth = 1;

sOptions.lineCap = 'butt';

sOptions.joinCap = 'butt';

drawArrowPoints(ctx, tx,ty, rd, 6,12, sOptions);

ctx.restore();

}


function drawClockMinute(ctx, cx, cy, radious , now, sOptions) {

ctx.save();


setCurrentOptions(ctx, sOptions);

let s = now.getSeconds();


let m = now.getMinutes();

let rd = ((m-15)*6+s/10)*Math.PI/180;

let tx = Math.cos(rd)*radious+cx

let ty = Math.sin(rd)*radious+cy;



ctx.beginPath();

ctx.moveTo(cx,cy);

ctx.lineTo(tx,ty);

ctx.stroke();

ctx.closePath();


ctx.restore();

}


function drawClockHour(ctx, cx, cy, radious , now, sOptions) {

ctx.save();


setCurrentOptions(ctx, sOptions);

let s = now.getSeconds();


let m = now.getMinutes();

let h = now.getHours();

let rd = (((h-3))*30+m/2+s/120)*Math.PI/180;

let tx = Math.cos(rd)*radious+cx

let ty = Math.sin(rd)*radious+cy;



ctx.beginPath();

ctx.moveTo(cx,cy);

ctx.lineTo(tx,ty);

ctx.stroke();

ctx.closePath();


ctx.lineWidth = 2;

ctx.fillStyle = "#00FFFF";

ctx.strokeStyle = "#0000FF";


ctx.beginPath();

ctx.arc(cx,cy,6,0,Math.PI*2);

ctx.fill();


ctx.closePath();


ctx.beginPath();


ctx.arc(cx,cy,6,0,Math.PI*2);

ctx.stroke();

ctx.closePath();


ctx.restore();

}



아래는 해당 소스의 실행 결과 입니다. 













2022년 4월 12일 화요일

[기록용] html canvas 를 활용한 간단한 그리기 (1)

 html 5 에서 제공하는 canvas 는 일반 application 에서 구현이 가능한 영역까지 작업이 가능

한 것이 특징 인 것 같습니다.

web을 기반한 업무 시스템에서 아주 많이 사용된다고는 할 수 없지만, 일반적인 도형 그리기, 

이미지 관련 작업, 경우에 따라서는 webgl 을 포함한 3d 작업등에 사용될 수 있는 기술 입니다.


아주 간단한 도형그리기 기초 함수를 구성해 보았습니다. 

Mouse Event로 사용자의 Action 을 받아 들여 canvas 를 도화지처럼 사용할 수 있는 

기초 함수 들 입니다. 


- Canvas 에서 사용할 꾸미기 옵션에 관련한 간단한 예시 입니다. 

   해당 내용이 추가 되더라도 이곳에서 더 추가후 진행할 수 있도록 함수화 하여 구성 

   ie 에서는 Object.assign 이 사용되지 못하기 때문에 json 객체는 직접 복제하여야 합니다.

  // Object.assign({}, source); 

// IE NOT WORKING ....

function getDefaultOptions() {

var obj = {

strokeStyle : "#000000",

fillStyle : "#000000",

lineWidth : 2

};

return obj;

}

function setCurrentOptions(ctx, sOptions) {

if ( !ctx ) {

return;

}

if ( !sOptions ) {

return;

}


ctx.strokeStyle = sOptions.strokeStyle;

ctx.lineWidth = sOptions.lineWidth;

ctx.fillStyle = sOptions.fillStyle;

}

- Line 은 다음과 같은 함수를 활용해 보았습니다. 
  Polyline 을 그리는 함수는 직접 쓰기 그리기 및 도형을 그리는 영역에서도 사용할 수 
  있습니다. 

function drawLine(ctx, sx, sy, ex, ey, sOptions) {
if ( !ctx ) 
return;

ctx.save();
setCurrentOptions(ctx, sOptions);
ctx.beginPath();
ctx.moveTo(sx,sy);
ctx.lineTo(ex,ey);
ctx.stroke();
ctx.closePath();
ctx.restore();
}

function makePolylines(ctx, sx, sy, ex, ey, points, sOptions, isClosed, isFilled ) {
if ( !ctx ) 
return;

ctx.save();
setCurrentOptions(ctx, sOptions);
ctx.beginPath();
ctx.moveTo(sx,sy);
if ( points ) {
for ( let i = 0, iSize= points.length; i < iSize; i++ ) {
ctx.lineTo(points[i][0],points[i][1]);
}
}
ctx.lineTo(ex,ey);
if ( isClosed || isFilled ) {
ctx.closePath();
if ( isFilled ) {
ctx.fill();
} else {
ctx.stroke();
}
} else {
ctx.stroke();
ctx.closePath();
}
ctx.restore();
}

function drawBezierLine(ctx, sx, sy, ex, ey, points, sOptions ) {
if ( !ctx ) 
return;

ctx.save();
setCurrentOptions(ctx, sOptions);
ctx.beginPath();
ctx.moveTo(sx,sy);
let mx01 = sx;
let my01 = sy;
let mx02 = ex;
let my02 = ey;
if ( points ) {
if ( points.length == 1 ) {
mx01 = points[0][0];
my01 = points[0][1];
if ( points.length >= 2 ) {
mx01 = points[0][0];
my01 = points[0][1];
mx02 = points[1][0];
my02 = points[1][1];
}
}
ctx.bezierCurveTo(mx01,my01, mx02, my02, ex,ey);
ctx.stroke();
ctx.closePath();
ctx.restore();
}

- 도형은 더 구성할 수도 있지만, 대략 사각형, 원, 타원 등을 그리도록 구성해 보았습니다.
  사각형이나, 타원은 위 polyline 으로 구성해 보았습니다. 
  ie 가 아닌 경우 타원을 그리는 함수가 지원되지만, 구현이 간단하기 때문에 구성해 
  보았습니다.  rotate 등의 각은 모두 radian 을 사용합니다. 

function makeRectangle(ctx, sx, sy, ex, ey, sOptions, isFilled ) {

if ( !ctx ) 
return;

let points = [];
points.push([sx,ey]);
points.push([ex,ey]);

makePolylines(ctx, sx,sy, ex,sy, points, sOptions, true, isFilled );
}

function makeEllipse(ctx, sx, sy, ex, ey, sOptions, isFilled ) {
if ( !ctx ) 
return;

let rx = Math.abs(sx-ex);
let ry = Math.abs(sy-ey);
let ssx = sx;
let ssy = sy;
let points = [];
for ( let i = 0; i < 360; i++ ) {
let rv = (Math.PI*i/180);
if ( i == 0 ) {
ssx = Math.sin(rv)*rx + sx;
ssy = Math.cos(rv)*ry + sy;
} else if ( i == 359 ) {
ex = Math.sin(rv)*rx + sx;
ey = Math.cos(rv)*ry + sy;
} else {
points.push([Math.sin(rv)*rx+sx, Math.cos(rv)*ry+sy]);
}
}

makePolylines(ctx, ssx,ssy, ex,ey, points, sOptions, true, isFilled );
}


function makeArc(ctx, sx, sy, ex, ey, sOptions , isFilled) {
if ( !ctx ) 
return;

let radious = Math.sqrt( Math.pow((sx-ex),2)+Math.pow((sy-ey),2));
alert ( radious );

ctx.save();
setCurrentOptions(ctx, sOptions);
ctx.beginPath();
ctx.arc(sx, sy, radious, 0, Math.PI*2);
ctx.closePath();
if ( isFilled ) {
ctx.fill();
} else {
ctx.stroke();
}
ctx.restore();
}

위 함수로 간단히 그려본 결과 입니다. 
Mouse Event를 활용 하면 간단한 그림 그리기 도구를 만들 수 있습니다. 
                
                function drawTest() {
......
var ctx = canvasObjects.display[1];
var opt = getDefaultOptions();
opt.strokeStyle = "red";
opt.lineWidth = 10;
drawBezierLine(ctx, 100, 100, 300, 300, [[300,100],[100,300]], opt) ;
opt.strokeStyle = "blue";
opt.lineWidth = 3;
makeRectangle(ctx, 100, 100, 300, 300, opt, false);
makeEllipse(ctx, 400,300, 450, 200, opt, false);
opt.strokeStyle = "cyan";
makeArc(ctx, 400, 300, 450, 200, opt, false);
            }








      

2022년 4월 11일 월요일

[기록용] 추세 데이터를 구성하는 방법

선형회귀로 구성된 1차 추세선은 f(x) = ax + b 라는 일차식으로 구성하게 됩니다. 

추세를 구하는 공식은 여러 방식으로 구하는 법을 찾을 수 있습니다. " Head First Statistics " 의 책에서는 간단한 공식을 제공하고 있는데 x와 y의 평균을 구해서

분자에는 (x - xAvg)^2 의 합을 구한값을 사용하고,

분모에는 (x - xAvg ) * (y-yAvg) 의 곱을 구해서 ax 의 a 를 구하게 됩니다.

y = ax + b 이기 때문에 b = y - ax 의 값이니, b = yAvg - a*xAvg 를 통해서

값을 확인해 볼 수 있습니다.

예를 들어 x = [1,2,3,4,5,6] 이고, y = [104, 117, 131, 145, 160, 171] 의 값을 가지고 있다고 가정하면, x의 평균은 3.5 이고, y의 평균은 138.0 입니다.

분모의 x 차의 제곱의 값은 17.5 가 되고 x차와 y차의 곱의 누적합은 239.0 이 됩니다. 결국 239.0/17.5 가 위의 a 값인 기울기가 되고, b = 138.0 - (13.6571 * 3.5 ) b = 90.2 의 값을 구하게 됩니다. 결국 위 예시된 값은 y = 13.6571428 *x + 90.2 의 식으로 표현될 수 있습니다.


만약 데이터의 각 단위가 제각각일 때 이들을 모두 하나의 수치 범위로 일종의 정규화를 하고자 한다면 어떻게 할 수 있을 지 잠시 생각해 보았습니다.


가령 어떤 주식은 1000에서 시작하고 어떤 주식은 15000 에서 시작하고 각 시작이

다른 항목을 가급적 유사한 범위의 기울기로 만들려면 시작점의 데이터가 절편(intercept) 이 가급적 0의 값을 지닐 수 있도록 구성하면, 다른 범위의 데이터라도

기울기 로만 비교할 수 있지 않을까 하는 생각을 해 보았습니다.

전체를 수치화 하여 비교해 보려면 각 단위 변화량을 유사하게 묶어둘 필요성이 있어서

입니다.

5일간의 변동이 1000, 1005, 1010, 1015, 1020 으로 변화하였다면,

x의 변화를 0, 1, 2, 3, 4 로 두고,y의 변화를 (1000/1000 -1)*100, (1005/1000-1)*100 ,,, 등으로 y 값을 조정 하면 x 가 0일 때 y가 0으로 구성하게

될 것이므로 아주 변화가 많지 않은 경우 절편의 값은 최소화 하여 구성 할 수 있을 것 같습니다.

이런 방식으로 데이터를 수치화 하여 일괄 비교를 가능하게 만들어 볼 수 있을 것 같습니다.





2022년 4월 8일 금요일

[기록용] 네이버 증권 등에서 받아온 xml 데이터 파싱 ( 주식1)

 Naver 에서 알려져 있는 회사 이름을 검색하면, 쉽게 해당 회사의 주식 정보를 확인할 수 있습니다.  (  Naver 로 검색 )

현재 시점의 사이트는 https://finance.naver.com/item/main.naver?code=035420 으로 간단한 

정보를 확인할 수 있습니다.  ( 향후 어느 시점에 해당 URL은 변경될 수 있습니다. )

해당 메뉴에서 Chart 를 선택하면 다음과 같은 URL 을 확인할 수 있습니다. 

https://finance.naver.com/item/fchart.naver?code=035420

이 화면에 나오는 데이터를 구성하기 위해서 통신한 결과를 확인하여 데이터를 받으면 

주식 데이터를 확인 할 수 있습니다. 


데이터 중에는 

 [['날짜', '시가', '고가', '저가', '종가', '거래량', '외국인소진율'],

["20171120", 161827, 162027, 159425, 160427, 85893, 59.33],

의 배열 처럼 보이는 데이터도 있고, 아래와 같이 xml 로 보이는 데이터도 있습니다. 

<?xml version="1.0" encoding="EUC-KR" ?>

<protocol>

<notice symbol="035420" count="61">

                </notice>

</protocol>


데이터를 크롤링하는 방법은 여러 언어에서 많이 있지만, java 에서는 jsoup 을 활용하면 

어렵지 않게 가져올 수 있습니다.  해당 사이트에 부하를 DDOS 처럼 주지 않게 하려면 sleep 등을 활용하여 가져오는 방식을 조금 천천히 하는 것도 좋을 것 같습니다. 

일단 위와 같은 xml 파일을 가져왔다면, 해당 파일을 파싱하여 데이터를 확인할 수 있을 것 같습니다.


<protocol>

    <chartdata symbol="005930" name="삼성전자" count="100" timeframe="day" 

        precision="0" originaltime="19900103">

        <item data="20211111|69900| |70000|69600|69900|11489122"/>

        ...

    </chartdata>

</protocol>


위 xml 을 파싱하는 초 간단 코드 입니다. 


import java.io.File;

import java.io.IOException;


import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;


import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;


/**

 * @author kskim

 *

 */

public class NhnOHLCXmlHandler extends DefaultHandler {

private boolean isOnlyHeader = false;


@Override

public void startDocument() {

}

@Override

public void endDocument() {

//System.out.println ( "END : " + hDto.getStockName() + " , " + hDto.getPriceDTOSize());

}

@Override

public void startElement(String uri, String localName,

            String qName, Attributes attributes) {

if ( isOnlyHeader ) {

// if ( hDto != null )

// return;

}

if ( qName.equals("chartdata")) {

int cLen = attributes.getLength();

for ( int i = 0; i < cLen; i++ ) {

System.out.println ( i + " , " + attributes.getValue(i) + " , " + attributes.getQName(i));

}

String stockID = attributes.getValue("symbol");

String stockName = attributes.getValue("name");

int dayCount = Integer.parseInt(attributes.getValue("count"));

String nhnTimeFrame = attributes.getValue("timeframe");

int nhnPresision = Integer.parseInt(attributes.getValue("precision"));

String nhnOriginTime = attributes.getValue("origintime");

} else if ( qName.contentEquals("item")) {

String str = attributes.getValue(0);

if ( str != null && str.trim().length() > 0 ) {

str = str.replaceAll("[\\h]", "");

String[] strArray = str.split("\\|");

int cLen = strArray.length;

if ( cLen == 6  ) {

String dayID = strArray[0];

float openPrice = Float.parseFloat(strArray[1]);

float highPrice = Float.parseFloat(strArray[2]);

float lowPrice = Float.parseFloat(strArray[3]);

float closePrice = Float.parseFloat(strArray[4]);

long volumn = Long.parseLong(strArray[5]);

System.out.println ( dayID + " : " + closePrice);

}

}

}

}

@Override

public void endElement(String uri, String localName, String qName) {   

        //System.out.println ( "endElement : " +  uri + " , " + localName + " , " + qName );

    }

public void characters (char ch[], int start, int length) {

//System.out.println ( " characters : " + new String(ch,start,length));

}


/**

* @param args

*/

public static void main(String[] args) {

String baseDir = "위 형식이 담긴 파일.xml";

SAXParserFactory sf = SAXParserFactory.newDefaultInstance();

try {

File f = new File(baseDir);

SAXParser sp = sf.newSAXParser();

NhnOHLCXmlHandler qph = new NhnOHLCXmlHandler();

try {

sp.parse(f, qph);

} catch (IOException e) {

e.printStackTrace();

}

} catch (ParserConfigurationException e) {

e.printStackTrace();

} catch (SAXException e) {

e.printStackTrace();

}

}

}


개인적으로는 주식에 문외한 입니다.  금융이나, 주식 분석의 영역은 거의 모르는 분야이지만,  개발의 관점에서 데이터 수집, 파싱, 추세 , 분석의 데이터로는 관심이 많아서 조금씩 모듈을 구성해 보고 그 과정을 기록하고자 합니다. 


[기록용] Maria db Windows 설치 및 간단 설정

 https://mariadb.com/downloads/ Site 에서 windows 용도로 선택하면 

msi 형식의 설치 파일을 다운로드 할 수 있습니다. CentOS 등 전체 파일을 다운로드 하는 것이

아닌 windows install programs을 다운 받아 설치하게 됩니다. 

진행 과정을 따라 가면 특별한 문제 없이 설치 되는데, 다국어를 고려 한다면 utf-8 을 

선택한 후 설치하는 게 좋을 것 같습니다.  

windows installer 를 통해 설치 할 경우 데이터 베이스 연결 tool 로 HeidiSQL 이 

같이 설치될 텐데 해당 툴을 활용하여 접속을 시도해 보면 좋을 것 같습니다. 


설치할 때 아마도 root, password 를 설정 하였을 테니, 접속을 연결한 후 

연결된 상태에서 다음과 같이 확인 및 구성합니다. 

use mysql; 

- mysql database 선택.

show tables; 

 - mysql database 에 포함된 table 조회

show grants;

 - 현재 접속환경에서의 권한확인

select user(), current_user() ;

 - 현재 접속 사용자 확인

create database my_database; ( 임의의 이름 )

  - 구성하고자 하는 데이터 베이스 이름 ( 임의 지정 )

create user 'user_name'@'localhost' identifieyd by 'user_password' ;

 - 사용자 생성 및 password 구성 'user_name'@'%' 로 구성하면 원격지에서 접근가능.

grant all privileges on my_database.* to 'user_name'@'localhost' 

  - 구성한 테이블 권한 부여 ( localhost 에서만 접근 가능 )

FLUSH PRIVILEGES;

  - 권한 테이블 정보를 메모리 reload


 







2022년 4월 5일 화요일

[기록용] Webpack Babel 을 활용한 ie 11 사용 구성 ( 1 )

webpack 과 babel 을 활용하여 ie 11 에서 사용 할 수 있는 script 를 간단히 구성해 보겠습니다.  ( windows 기준 )

가장 먼저 window command 창을 뛰웁니다. 

mkdir 로 자신이 구성할 directory  를 구성합니다. 

이후 아래와 같은 내용을 순서대로 구성합니다. 


npm init -y

- 특정 directroy 에 node를 활용하여 프로젝트를 수행 할 수 있는 구조를 만듭니다.



https://webpack.js.org/guides/getting-started  사이트의 초 간단 예제를 약간 변형합니다.


webpack 관련 모듈을 개발용으로 설치합니다. 

npm i -D webpack webpack-cli 혹은 npm install webpack webpack-cli --save-dev 라고 

입력합니다. ( 둘은 같은 역활을 수행합니다. )


source directory 를 구성한 후 ( mkdir src ) 

해당 directory 에 다음과 같은 2개의 파일을 생성합니다. 

- /src/fnInit.js

import fn from "./fnClick.js";

const initPageButton = () => {
    var domObj = document.createElement("button");
    domObj.innerHTML = "Click Me";
    domObj.onclick = fn;
    return domObj;
};

document.body.appendChild(initPageButton());

- /src/fnClick.js

export default function fnClick() { alert( "TEST CLICKED") ; }


root directory 에 webpack.config.js 파일을  다음과 같이 생성합니다.

-/webpack.config.js

const path = require('path');

module.exports = {
    mode : 'development',
    entry : ['./src/fnInit.js'],
    output : {
        filename:'main.js',
        path:path.resolve(__dirname,'dist'),
    },
}

/package.json 

{
  "name": "webpack_babel",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.71.0",
    "webpack-cli": "^4.9.2"
  }
}

build : webpack 을 기록하여 npm run build 를 수행합니다. 


결과를 확인하면 chrome 에서는 정상적으로 index.html 이 로딩되지만, ie 에서는 

페이지가 나타나지 않습니다.  이것을 해결하기 위해서 babel 을 설정 합니다. 


babel site 는 https://babeljs.io/docs/en/usage 에서 사용법을 확인 할 수 있습니다. 

npm install --save-dev @babel/core @babel/cli @babel/preset-env

npm i @babel/runtime @babel/plugin-transform-runtime -D

npm install --save-dev @babel/runtime-corejs3

npm i -D babel-loader

npm i -D core-js@3


위 내용으로 babel 관련한 모듈을 설치합니다.  다음은 babel.config.json 입니다.

/babel.config.json 

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1",
          "ie":"11"
        },
        "useBuiltIns": "entry",
        "corejs": 3,
        "debug":true
      }
    ]
  ],
  "plugins": [["@babel/plugin-transform-runtime",{"corejs":3}]]
}


다시 webpack.config.js 파일을 아래와 같이 수정합니다. 

/webpack.config.js 

const path = require('path');

module.exports = {
    mode : 'development',
    entry : ['./src/fnInit.js'],
    output : {
        filename:'main.js',
        path:path.resolve(__dirname,'dist'),
    },
    target:['web','es5'],
    module: {
        rules : [
            {
                test : /\.jsx?$/,
                exclude : /node_module/,
                use: {
                    loader: 'babel-loader',
                    options : {
                        rootMode: "upward",
                    }
                }
            }
        ]
    },
}


target 으로 es5 형식으로 변환하고, babel의 변환은 babel.config.json 을 따르도록 

구성합니다.  Babel 에서 제공하는 문서에는 rootMode upward 의 경우 각 package 

단위로 하위의 디렉토리를 따라가면서 각기 다른 설정이 가능하다고 기재되어 있습니다. 

(  https://babeljs.io/docs/en/config-files 문서 참조 ) 

babel.config.json 파일은 babel 이 설정을 찾는 기본 파일 이름 입니다. 

babel 에서 제공하는 문서에는 다음과 같이 기재되어 있습니다.

rootMode

Type: "root" | "upward" | "upward-optional"
Default: "root"

Note: babel.config.json is supported from Babel 7.8.0. In older Babel 7 versions, only babel.config.js is supported.

rootMode 에 따라 약간의 차이가 있는데 root 의 경우 configFile 을 지정할 수 있지만,

upward 의 경우 package.json 이 있는 위치에 babel 의 기본 config file 이 있어야 합니다.


/src/fnClick.js

const fnClick = () => { alert( "TEST CLICKED") ; };
export default fnClick;


화살표 함수로 변경 후 npm run build 로 구성하여도 ie 11 에서 동작합니다.











2022년 4월 3일 일요일

[기록용] ( ) 등의 brace 가 포함된 문자열 논리식으로 분해하여 참, 거짓으로 가져오기 위한 class

 앞서 했던 모든 class를 활용하여 구문을 각 block 영역으로 재 구성하고 각각을 계산하여 

최종적인 참 거짓을 가져오기 위한 class 입니다. 


public class BraceSyntaxBaseDTO extends SyntaxBaseDTO {

private Map<String,SyntaxBaseDTO> braceDtoMap

= new LinkedHashMap<String,SyntaxBaseDTO>();;

public BraceSyntaxBaseDTO(String syntax) {

super(syntax);

initResource();

}

private void initResource() {

if ( CommonBaseUtility.isEmptyValue(syntax) ) {

return;

}

parseCurrentSyntaxValue();

}

@Override

public boolean parseCurrentSyntaxValue() {

// TODO Auto-generated method stub

this.setMainType(MAIN_TYPE.BRACE_TYPE);

if (CommonBaseUtility.isEmptyValue(syntax)) {

return false;

}

if ( isConstValue() ) {

return true;

}

specialKeySet = CommonParserUtility.parseSpecialKeyValues(syntax);

variableMap = CommonParserUtility.parseVariableValues(syntax);

if ( CommonParserUtility.hasBrasceValue(syntax) ) {

Map<String,String> braceMap = CommonParserUtility.parseStringBraceBlocks(syntax);

for ( String keyStr : braceMap.keySet() ) {

String v = braceMap.get(keyStr);

if ( CommonParserUtility.hasLogicalValue(v ) ) {

LogicSyntaxBaseDTO cDto = new LogicSyntaxBaseDTO(v);

braceDtoMap.put(keyStr, cDto);

} else if ( CommonParserUtility.hasCompareValue(v)) {

CompareSyntaxBaseDTO cDto = new CompareSyntaxBaseDTO(v);

braceDtoMap.put(keyStr, cDto);

} else if ( CommonParserUtility.hasCalculationValue(v)) {

CalculationSyntaxBaseDTO cDto = new CalculationSyntaxBaseDTO(v);

braceDtoMap.put(keyStr, cDto);

}

}

} else {

if ( CommonParserUtility.hasLogicalValue(syntax) ) {

braceDtoMap.put("FINAL", new LogicSyntaxBaseDTO(syntax));

} else if ( CommonParserUtility.hasCompareValue(syntax)) {

braceDtoMap.put("FINAL", new CompareSyntaxBaseDTO(syntax));

} else if ( CommonParserUtility.hasCalculationValue(syntax)) {

braceDtoMap.put("FINAL", new CalculationSyntaxBaseDTO(syntax));

}

}

return true;

}

@Override

public String getValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

return null;

}


@Override

public boolean getLogicalValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

if ( this.braceDtoMap == null || braceDtoMap.isEmpty() )

return false;

String finalKey = null;

for ( String s : braceDtoMap.keySet()) {

SyntaxBaseDTO dto = braceDtoMap.get(s);

if ( dto.getMainType() == MAIN_TYPE.CALCULATION_TYPE ) {

String v = dto.getValue(transMap);

transMap.put(s, v);

} else if ( dto.getMainType() == MAIN_TYPE.COMPARE_TYPE ) {

boolean v = dto.getLogicalValue(transMap);

transMap.put(s, v+"");

} else if ( dto.getMainType() == MAIN_TYPE.LOGIC_TYPE ) {

boolean v = dto.getLogicalValue(transMap);

transMap.put(s, v+"");

}

finalKey = s;

}

return braceDtoMap.get(finalKey).getLogicalValue(transMap);

}


/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

String str = "(a+b > 29.9 && ( c < 10 or d >= 10) ) == false";

System.out.println ( String.format("Original Syntax : [%s]", str));

System.out.println ( String.format("Each varialbe value is assigned - [%s]", 10));

BraceSyntaxBaseDTO dto = new BraceSyntaxBaseDTO(str);

Map<String,Map<String,String>> variableMap = CommonParserUtility.parseVariableValues(str);

Map<String,String> transMap = new HashMap<String,String>();

for ( String s : variableMap.keySet()) {

transMap.put(s,"10");

}

System.out.println ( "FINAL RESULT : " + dto.getLogicalValue(transMap));

}

}

Main Block 에 보면 (a+b > 29.9 && ( c < 10 or d >= 10) ) == false 의 문자열이 있고, 

각 변수에 10을 할당하는 방법을 사용하였습니다. 

결국 (10+10 > 29.9 && (10 < 10 or 10 >= 10)) == false 의 구문으로 구성되고, 

( false && (true)) == false => false == false 가 되어 최종 결과는 true 입니다.

 




[기록용] Syntax 구문에서 and, or , &&, || 등의 항목을 분해하여 논리 식을 구성하기 위한 class

 앞서 구성되었던 CalucationSyntaxBaseDTO, CompareSyntaxBaseDTO 등의 class를 

사용하여 각 구분의 and or 의 항목을 연결하여 최종 참, 거짓으로 가져오기 위한 calss 


public class LogicSyntaxBaseDTO extends SyntaxBaseDTO {

private SyntaxBaseDTO leftDTO = null;

private SyntaxBaseDTO rightDTO = null;

private int markType = -1;

public LogicSyntaxBaseDTO(String syntax) {

super(syntax);

initResource();

}

private void initResource() {

parseCurrentSyntaxValue();

}



@Override

public boolean parseCurrentSyntaxValue() {

// TODO Auto-generated method stub

this.setMainType(MAIN_TYPE.LOGIC_TYPE);

if (CommonBaseUtility.isEmptyValue(syntax)) {

return false;

}

if ( isConstValue() ) {

return true;

}

specialKeySet = CommonParserUtility.parseSpecialKeyValues(syntax);

variableMap = CommonParserUtility.parseVariableValues(syntax);

if ( CommonParserUtility.hasLogicalValue(syntax) ) {

List<Map<String,String>> logicMapList = CommonParserUtility.parseStringLogicalBlocks(syntax);

String rightStr = logicMapList.get(0).get("RIGHT");

String markStr = logicMapList.get(0).get("MARK");

String leftStr = logicMapList.get(0).get("LEFT");

if ( !CommonBaseUtility.isEmptyValue(markStr)) {

markStr = markStr.trim().toUpperCase();

if ( markStr.equals("AND") || markStr.equals("&&")) {

markType = 1;

} else {

markType = 2;

}

}

if ( CommonParserUtility.hasCompareValue(rightStr) ) {

rightDTO = new CompareSyntaxBaseDTO(rightStr);

} else {

rightDTO = new CalculationSyntaxBaseDTO(rightStr);

}

if ( logicMapList != null && logicMapList.size() > 1 ) {

leftDTO = new LogicSyntaxBaseDTO(logicMapList.get(0).get("LEFT"));

} else {

if ( CommonParserUtility.hasCompareValue(leftStr) ) {

leftDTO = new CompareSyntaxBaseDTO(leftStr);

} else {

leftDTO = new CalculationSyntaxBaseDTO(leftStr);

}

}

} else {

if ( CommonParserUtility.hasLogicalValue(syntax) ) {

} else if ( CommonParserUtility.hasCompareValue(syntax)) {

} else if ( CommonParserUtility.hasCalculationValue(syntax)) {

}

}

return true;


}


@Override

public String getValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

return null;

}


@Override

public boolean getLogicalValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

if ( this.markType == 2 ) {

if ( rightDTO.getLogicalValue(transMap) ) {

return true;

}

if ( leftDTO == null ) {

System.out.println ( "Left class is null!!! Syntax is " + this.getSyntax() );

return false;

}

return leftDTO.getLogicalValue(transMap);

} else {

if ( !rightDTO.getLogicalValue(transMap)) {

return false;

}

return leftDTO.getLogicalValue(transMap);

}

}

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

System.out.println ( false && false && false || true );


}


}


원칙적으로 모든 구문에는 ( ) 의 항목이 포함되어 있지 않아야 합니다. 

[기록용] 등호 부등호 기호에 따른 비교 구문을 구성하기 위한 class

 양 변으로 구성된 값을 비교하기 하여 그 결과를 참, 거짓으로 구성하기 위한 class 입니다. 

양 변의 값은 대부분 앞서 구성한 계산 class 를 활용하여 구성하게 됩니다. 


public class CompareSyntaxBaseDTO extends SyntaxBaseDTO {

private SyntaxBaseDTO leftDTO = null;

private SyntaxBaseDTO rightDTO = null;

private int markType = -1;

public CompareSyntaxBaseDTO(String syntax) {

super(syntax);

initResource();

}

private void initResource() {

parseCurrentSyntaxValue();

}



@Override

public boolean parseCurrentSyntaxValue() {

// TODO Auto-generated method stub

this.setMainType(MAIN_TYPE.COMPARE_TYPE);

if ( isConstValue() ) {

return true;

}

if ( CommonBaseUtility.isEmptyValue(syntax)) {

return false;

}


specialKeySet = CommonParserUtility.parseSpecialKeyValues(syntax);

variableMap = CommonParserUtility.parseVariableValues(syntax);


if ( CommonParserUtility.hasCompareValue(syntax)) {

Map<String,String> parsedMap = CommonParserUtility.parseStringCompareBlocks(syntax);

String rightStr = parsedMap.get("RIGHT");

String markStr = parsedMap.get("MARK");

String leftStr = parsedMap.get("LEFT");

if ( !CommonBaseUtility.isEmptyValue(markStr)) {

markStr = markStr.trim();

switch ( markStr ) {

case ">" :

markType = 1;

break;

case ">=" :

markType = 2;

break;

case "<" :

markType = 3;

break;

case "<=" :

markType = 4;

break;

case "==" :

markType = 5;

break;

case "!=" :

markType = 6;

break;

default :

markType = -1;

break;

}

}

if ( CommonParserUtility.hasCompareValue(rightStr) ) {

rightDTO = new CompareSyntaxBaseDTO(rightStr);

} else {

rightDTO = new CalculationSyntaxBaseDTO(rightStr);

}

if ( CommonParserUtility.hasCompareValue(leftStr)) {

leftDTO = new CompareSyntaxBaseDTO(leftStr);

} else {

leftDTO = new CalculationSyntaxBaseDTO(leftStr);

}

}

return true;

}


@Override

public String getValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

return null;

}


@Override

public boolean getLogicalValue(Map<String, String> transMap) {

// TODO Auto-generated method stub

if ( leftDTO == null || rightDTO == null || markType == -1 )

return false;

String lv = leftDTO.getValue(transMap);

if ( leftDTO.getMainType() == MAIN_TYPE.COMPARE_TYPE ) {

lv = leftDTO.getLogicalValue(transMap)+"";

}

String rv = rightDTO.getValue(transMap);

if ( rightDTO.getMainType() == MAIN_TYPE.COMPARE_TYPE ) {

rv = rightDTO.getLogicalValue(transMap)+"";

}

boolean result = false;

if ( CommonBaseUtility.isNumericValue(lv) 

&& CommonBaseUtility.isNumericValue(rv) ) {

float left = Float.parseFloat(lv);

float right = Float.parseFloat(rv);


switch(markType) {

case 1 :

result = left > right;

break;

case 2 :

result = left >= right;

break;

case 3 :

result = (left < right);

break;

case 4 :

result = (left <= right);

break;

case 5 :

result = (left == right );

break;

case 6 :

result = (left != right );

break;

default :

result = false;

} else if ( !CommonBaseUtility.isEmptyValue(lv) 

&& !CommonBaseUtility.isEmptyValue(rv) ) {

switch(markType) {

case 5 :

result = (lv.equals(rv) );

break;

case 6 :

result = (!lv.equals(rv));

break;

default :

result = false;

}

return result;

}


/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

}

}


이 class 가 구성될 때 and or 및 ( ) 등의 brace 가 없는 경우를 가정하였습니다. 
해당 부분이 필요하면 구성되는 하위 항목이 달라져야 합니다. 



[기록용] 문자열로 간단한 사칙연산 계산을 담당하는 class

 앞서 구성한 SyntaxBaseDTO 클래스를 상속 받은 class 를 구성 합니다. 

간단한 연산 (+, -, *, / ) 만을 대상으로 합니다. 


public class CalculationSyntaxBaseDTO extends SyntaxBaseDTO {

public CalculationSyntaxBaseDTO(String syntax) {

super(syntax);

initResource();

}

private void initResource() {

parseCurrentSyntaxValue();

}

@Override

public boolean parseCurrentSyntaxValue() {

// TODO Auto-generated method stub

this.setMainType(MAIN_TYPE.CALCULATION_TYPE);

if (CommonBaseUtility.isEmptyValue(syntax)) {

return false;

}

if ( isConstValue() ) {

return true;

}

specialKeySet = CommonParserUtility.parseSpecialKeyValues(syntax);

variableMap = CommonParserUtility.parseVariableValues(syntax);

return true;

}

public String getValue(Map<String,String> transMap) {

if ( isConstValue() ) {

return syntax;

}

String trStr = syntax;

if ( transMap != null ) {

for ( String s : transMap.keySet()) {

if ( specialKeySet.contains(s) || variableMap.containsKey(s)) {

trStr = trStr.replaceAll("\\b" + s + "\\b", transMap.get(s));

}

if ( s.equals(trStr.trim())) {

trStr = transMap.get(s);

break;

}

}

}

if ( "true".equalsIgnoreCase(trStr.trim())) {

return trStr.trim();

} else if ( "false".equalsIgnoreCase(trStr.trim())) {

return trStr.trim();

}

return CommonParserUtility.parseStringBasicCalulation(trStr)+"";

}

public boolean getLogicalValue(Map<String,String> transMap) {

double d = Double.NaN;

if ( isConstValue() ) {

d = Double.parseDouble(syntax);

if ( Double.isNaN(d) || d == 0.0 )

return false;

else 

return true;

}

String trStr = syntax;

if ( transMap != null ) {

for ( String s : transMap.keySet()) {

trStr = trStr.replaceAll("\\b" + s + "\\b", transMap.get(s));

if ( s.equals(trStr.trim())) {

trStr = transMap.get(s);

break;

}

}

}

if ( trStr != null ) 

trStr = trStr.trim();

if ( trStr.equalsIgnoreCase("true")) {

return true;

if ( trStr.equalsIgnoreCase("false")) {

return false;

}

d = CommonParserUtility.parseStringBasicCalulation(trStr);

if ( Double.isNaN(d) || d == 0.0 )

return false;

else 

return true;

}


/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub


}


}


정말 간단한 계산만 수행 합니다. 

[기록용] 문자열 기준으로 데이터를 가져오기 위한 기초 클래스

 앞서 구성한 함수를 활용하여 계산 , 논리적인 판별을 하기 위한 모듈을 구성하기 위해서

가장 기본적인 내용을 담은 class를 구성 합니다. 

interface 구조도 가능하나, 해당 class 가 하는 일이 있기 때문에 abstract class로 구성합니다.


public abstract class SyntaxBaseDTO {

protected String syntax = null;

private MAIN_TYPE mainType = null;

private int subType = -1;

private boolean isConst = false;

protected Set<String> specialKeySet = null;

protected Map<String,Map<String,String>> variableMap = null;

public enum MAIN_TYPE {

BRACE_TYPE(1),

LOGIC_TYPE(2),

COMPARE_TYPE(3),

CALCULATION_TYPE(4);

private int value = -1;

private MAIN_TYPE(int value) {

this.value = value;

}

public int getValue() {

return this.value;

}

public SyntaxBaseDTO() {

}

public SyntaxBaseDTO( String syntax ) {

this.syntax = syntax;

initResource();

}

private void initResource() {

if ( CommonBaseUtility.isEmptyValue(syntax) ) {

return;

}

if (syntax.toLowerCase().matches("true|false")) {

isConst = true;

mainType = MAIN_TYPE.COMPARE_TYPE;

}

isConst = CommonBaseUtility.isNumericValue(syntax);

mainType = MAIN_TYPE.CALCULATION_TYPE;

}

public boolean isConstValue() {

return isConst;

}

public abstract boolean parseCurrentSyntaxValue();

public abstract String getValue(Map<String,String> transMap);

public abstract boolean getLogicalValue(Map<String,String> transMap);


/**

* @return the syntax

*/

public String getSyntax() {

return syntax;

}


/**

* @param syntax the syntax to set

*/

public void setSyntax(String syntax) {

this.syntax = syntax;

}


/**

* @return the mainType

*/

public MAIN_TYPE getMainType() {

return mainType;

}


/**

* @param mainType the mainType to set

*/

public void setMainType(MAIN_TYPE mainType) {

this.mainType = mainType;

}


/**

* @return the isConst

*/

public boolean isConst() {

return isConst;

}


/**

* @param isConst the isConst to set

*/

public void setConst(boolean isConst) {

this.isConst = isConst;

}


/**

* @return the subType

*/

public int getSubType() {

return subType;

}


/**

* @param subType the subType to set

*/

public void setSubType(int subType) {

this.subType = subType;

}

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub


}

}


Exception 처리 등 구성해야 할 부분이 많이 있지만, 기초 class 로 향후 참조용으로
기록합니다.