Kotlin Ktor과 React 같이 사용하기

|

React 다운로드

먼저 React를 사용하기 위해서 react.jsreact-dom.js 파일을 다운로드합니다.

여기에서 다운로드 가능하며, 저는 min 버전으로 다운로드했습니다.


react.html

<html>
<head>
    <script src="/static/web/react/react.js"></script>
    <script src="/static/web/react/react-dom.js"></script>
</head>

<body>
<div id="content">hello, react</div>
<script type="module">
    import { init } from '/static/web/react_test.js';

    init();
</script>

</body>
</html>


react_test.js

export function init() {
    console.log("init()");

    var h1=React.createElement('h1', null, 'Hello, React')
    ReactDOM.render(
        h1,
        document.getElementById('content')
    )
}

HTML5 var 보다는 let을 사용해라

|

변수 선언

ES5 까지는 변수 선언을 위해 var 키워드를 사용했었는데, ES6 부터 constlet이 추가되었습니다.

const는 보이는대로 상수값 선언이기 때문에 깔끔합니다. 가장 많이 사용되어야 할 상수 선언 키워드입니다.

var의 경우 어휘적 유효 범위(Lexical scope)를 가지는데, let은 블럭 유효 범위(Block scope)를 가집니다. 해당 블럭까지만 유효한 변수입니다. 우리가 다른 언어에서 흔히 볼 수 있는 로컬 변수(Local variable)와 같다고 볼 수 있습니다.


var 의 문제점

var 키워드를 사용하면 다음과 같은 버그가 발생할 수 있습니다.

function init() {
    var a = 1;

    if(true) {
        var a = 2;

        console.log("a: " + a);
    }

    console.log("a: " + a);
}

위 코드의 실행 결과는 다음과 같습니다.

2
2

블럭 안에서 var a = 2;를 선언했고, 블럭이 끝났음에도 이전 블럭에 있는 a 변수 값을 바꿔버렸습니다. 이러한 특성은 잠재적 오류를 발생시키기 쉽습니다.

let으로 바꾸면 다음과 같습니다.

function init() {
    let a = 1;

    if(true) {
        let a = 2;

        console.log("a: " + a);
    }

    console.log("a: " + a);
}
2
1


또한 constletvar에 비해 가지는 장점은 또 있습니다. constlet은 같은 이름의 변수를 다시 선언할 수 없습니다. var은 같은 Scope 내에서 같은 이름의 변수를 여러 번 선언할 수 있습니다.

따라서 constlet은 미연에 실수를 방지해주기 때문에 var 보다는 let을 사용하는 것이 좋습니다.

HTML5 ES6 클래스 사용하기

|

ES6 클래스

ES6(ECMAScript 6, ES2015)에 클래스를 사용하는 구문이 추가되었습니다. 기존에도 클래스를 사용할 수는 있었지만, 다음과 같이 function을 이용하거나 클래스 표현식을 이용해서 사용했었습니다.

기존 클래스 선언 방법

function Person(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
}

Person.prototype.id = function() {
    return id;
}

Person.prototype.name = function() {
    return "[name: " + this.name + 
        ", email: " + this.email + "]";
}


ES6 에서의 클래스 선언 방법

class Person {
    constructor(id, name, email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    id() {
        return id;
    }

    name() {
        return name;
    }

    info() {
        return "[name: " + this.name +
            ", email: " + this.email + "]";
    }
}

훨씬 더 Java의 문법과 비슷해졌습니다. 커다란 중괄호로 묶이기 때문에 기존 클래스 선언보다 가독성도 더 좋아졌습니다.


함수 선언문과 클래스 선언문의 차이

  • 클래스 선언문은 자바스크립트 엔진이 미리 읽어들이지 않음. 따라서 생성자를 사용하기 전에 클래스 선언문이 먼저 호출되어야 함
  • 클래스 선언문은 한 번만 작성되어져야 함


클래스 Property 접근자 작성하기

class Person {
    constructor(name) {
        this._name = name;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        this._name = value;
    }
}

위에서 name() 메소드에 get, set 키워드를 사용했습니다. 따라서 다음과 같이 사용할 수 있습니다.

const p = new Person("snowdeer", "Seo", "snowdeer0314@gmail.com");
p.name = "Won";
console.log(p.info());

위에서 get 이나 set 키워드 함수 내부를 보면 this._name 변수를 사용하고 있는 것이 보입니다. 자바스크립트에서 게터나 세터와 같은 이름의 속성을 만들 수 없기 때문에 별도의 변수를 추가해서 이를 해결한 방법이며, 자바스크립트에서는 private 속성을 지원하지 않아서 prefix_을 추가해서 형식적으로 표현만 했습니다.


static 메소드 작성하기

export default class Person {
    constructor(name) {
        this._name = name;;

        Person.count++;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        this._name = value;
    }

    static count() {
        return Person.count;
    }
}

Person.count = 0;

사용법은 다음과 같습니다.

function test() {
    const p1 = new Person("snowdeer", "Seo");
    console.log(Person.count);

    const p2 = new Person("ran", "Lee");
    const p3 = new Person("yang", "Yang");

    console.log(Person.count);
    console.log(p3.count);
}

실행 결과는 다음과 같습니다.

1
3
undefined


클래스 상속

class Person {
    constructor(name) {
        this._name = name;;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        this._name = value;
    }
}

class Student extends Person {
    hello() {
        console.log("Hello, My name is " + super.name + ". ");
    }
}

HTML5 Canvas에 사각형 그리고 Drag & Drop 구현하기

|

Canvas에 사각형 그리고 Drag & Drop 구현하기

var canvas;
var ctx;

var screenRect;

var rectX, rectY;
var WIDTH = 100, HEIGHT = 50;
var prevMouseX, prevMouseY;
var isMouseDown = false;
var isRectSelected = false;

function init() {
    console.log("init()");
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');

    screenRect = canvas.getBoundingClientRect();

    rectX = canvas.width / 2;
    rectY = canvas.height / 2;

    addKeyEventListener();

    animate();
}

function addKeyEventListener() {

    canvas.onmousedown = function(e) {
        var x = event.clientX - screenRect.left;
        var y = event.clientY - screenRect.top;

        console.log("MouseDown : (" + x + ", " + y + ")");

        prevMouseX = x;
        prevMouseY = y;

        isMouseDown = true;

        if(isRectClicked(x, y)) {
            isRectSelected = true;
        }
        else {
            isRectSelected = false;
        }
    }

    canvas.onmousemove = function(e) {
        if(isMouseDown && isRectSelected) {
            var x = event.clientX - screenRect.left;
            var y = event.clientY - screenRect.top;

            var dx = x - prevMouseX;
            var dy = y - prevMouseY;

            rectX += dx;
            rectY += dy;

            prevMouseX = x;
            prevMouseY = y;
        }     
    }

    canvas.onmouseup = function(e) {
        var x = event.clientX - screenRect.left;
        var y = event.clientY - screenRect.top;

        console.log("MouseUp : (" + x + ", " + y + ")");

        isMouseDown = false;
        isRectSelected = false;
    }

    canvas.onmouseout = function(e) {
        isMouseDown = false;
    }
}

function animate() {
    drawCanvasBackground();
    drawRect();

    requestAnimationFrame(function() {
        animate();
    });
}

function drawCanvasBackground() {
    ctx.fillStyle = 'white';
    ctx.strokeStyle = 'black';

    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    ctx.rect(0, 0, canvas.width, canvas.height);
    ctx.stroke();
}

function drawRect() {
    ctx.strokeStyle = 'black';

    ctx.beginPath();
    ctx.rect(rectX - WIDTH / 2, rectY - HEIGHT / 2, WIDTH, HEIGHT);
    ctx.stroke();
}

function isRectClicked(x, y) {
    if ((x >= (rectX - WIDTH / 2)) && 
        (x <= (rectX + WIDTH / 2)) && 
        (y >= (rectY - HEIGHT / 2)) && 
        (y <= (rectY + HEIGHT / 2))) {
        return true;
    }

    return false;

}

HTML5 ES6 module (import/export) 사용하기

|

ES6 module

ES6(ECMAScript 6, ES2015)부터 module이라는 기능이 생겼습니다.

<script type="module" src="hello.mjs"></script>

기존에 자바스크립트를 불러오는 코드에 type="module" 옵션을 추가해주면 모듈을 불러올 수 있습니다. 쉽게 구분하고 사용할 수 있도록 mjs 확장자를 사용하는 것을 추천하고 있습니다.



모듈의 특징

모듈은 기본적으로 자바스크립트의 특징을 모두 갖고 있습니다. 그리고 추가적으로 다음과 같은 특징이 있습니다.

  • import, export를 사용할 수 있음
  • 모듈 바깥쪽에 선언한 변수들은 전역(Globas scope)가 아닌 Module scope로 선언됨
  • 기본적으로 Strict mode로 동작
  • 같은 모듈을 다른 모듈에서 여러 번 불러도, 모둘 내부 코드는 단 한 번만 실행됨


사용법

모듈 외부에서 사용할 수 있도록 공개된 변수나 함수 앞에 export 키워드를 붙이면 되고, 다른 모듈에서는 import 키워드를 이용해서 불러올 수 있습니다.

export 사용 예제

export const arrs = [10, 20, 30, 40];

export function getName() {
    // ...
}

또는 기본 문법으로 구현된 모듈 뒷 부분에 다음 라인을 추가하면 됩니다.

export { arrs, getName };

아래 예제처럼 alias를 지정할 수도 있습니다.

export { arrs, getName as name };


import 사용 예제

import { arrs, getName } from './sample_module.js';

import arrs from './sample_module.js';

import getName as name from './sample_module.js';

import * as name from './sample_module.js';


예제

module.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Module Test</title>
</head>
<body>
    Hello

    <script type="module">
        import { init } from '/static/js/test.js';

        window.onload = function() {
            console.log("window.onload()");
            init();
        }
    </script>
</body>
</html>


sample_module.js

export const arrs = [10, 20, 30, 40];

const name = "snowdeer";

export function getName() {
    return name;
}


test.js

import { arrs, getName } from './sample_module.js';

console.log(arrs);
printName();

function printName() {
    console.log(getName())
}

export function init() {
    console.log("init()");
    printName();
}