Javascript의 Array, Set, Object, Map에 대한 데이터 순회하기

자바스크립트에서 데이터 컨테이너로써의 관점에서 데이터를 순회하는 코드를 정리합니다.

먼저 Array입니다.

const arr = new Array(1, 2, 3, 4, 5)

console.log('Array Iteration 1')
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i])
}

console.log('Array Iteration 2')
arr.forEach(v => {
    console.log(v)
})

console.log('Array Iteration 3')
for (let v of arr) {
    console.log(v)
}

console.log('Array Iteration 4')
for (let i in arr) {
    console.log(arr[i])
}

다음은 Set입니다.

const set = new Set([1, 2, 3, 4, 'Hello'])

console.log('Set Iteration 1')
for (let v of set) {
    console.log(v)
}

console.log('Set Iteration 2')
for (let v of set.values()) {
    console.log(v)
}

console.log('Set Iteration 3')
set.forEach(v => {
    console.log(v)
});

다음은 Object입니다.

const obj = { a: 1, b: 2, c: 3, 9: 4, e: 'Hello' }

console.log('Object Iteration 1')
const keys = Object.keys(obj) // [ 'a', 'b', 'c', '9', 'e' ]

for (let i = 0; i < keys.length; i++) {
    const k = keys[i]
    const v = obj[k]
    console.log(k, v)
}

console.log('Object Iteration 2')
const values = Object.values(obj) // [ 1, 2, 3, 4, 'Hello' ]
for (let i = 0; i < values.length; i++) {
    const v = values[i]
    console.log(v)
}

console.log('Object Iteration 3')
const entries = Object.entries(obj) // [ ['a', 1], ['b', 2], ['c', 3], ['9', 4], ['e', 'Hello'] ]
for (let i = 0; i < entries.length; i++) {
    const k = entries[i][0]
    const v = entries[i][1]

    console.log(k, v)
}

console.log('Object Iteration 4')
for (let k in obj) {
    const v = obj[k]
    console.log(k, v)
}

끝으로 Map입니다.

const map = new Map( [ ['a', 1], ['b', 2], ['c', 3], ['9', 4], ['e', 'Hello'] ])

console.log('Map Iteration 1')
for (let [k, v] of map) {
    console.log(k, v)
}

console.log('Map Iteration 2')
for (let k of map.keys()) {
    console.log(k, map.get(k))
}

console.log('Map Iteration 3')
for (let v of map.values()) {
    console.log(v)
}

console.log('Map Iteration 4')
for (let [k, v] of map.entries()) {
    console.log(k, v)
}

console.log('Map Iteration 5')
map.forEach(function(v, k) {
    console.log(k, v)
})

사실 Map의 출현으로 더 이상 Object를 데이터 컨테이너로써 사용하는 것은 옳지 않습니다. Object는 기본적으로 생성될 때 기본 키값을 갖지만 Map은 개발자가 추가하지 않은 데이터는 갖지 않습니다. 또한 Map의 Key는 문자열 객체 이외에도 타입도 가능합니다. 또한 저장한 순서대로 값을 얻을 수 있으며 데이터의 개수도 size 함수를 통해 바로 얻을 수 있습니다. 게다가 Map은 데이터의 추가와 삭제 시 Object보다 성능이 뛰어납니다.

matter.js를 이용한 강체 시뮬레이션

matter.js 라이브러리를 이용하여 구현하고자 하는 결과는 아래와 같습니다.

먼저 HTML 코드입니다.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="style_matter.js.css">
    <script src="decomp.js"></script>
    <script src="pathseg.js"></script>
    <script src="matter.0.16.1.js"></script>
    <script src="app_matter.js.js" defer></script>
</head>
<body>
    <div></div>
</body>
</html> 

지형은 SVG 데이터를 통해 좌표를 뽑아오는데 이를 위해 matter.js 이외에도 decomp.js와 pathseg.js 라이브러리가 필요합니다. div 요소에 강체 시뮬레이션의 결과가 표시됩니다. 다음은 CSS입니다.

* {
    outline: none;
    padding: 0;
    margin: 0;
}
    
body {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100vh;
}

div 요소를 화면 중심에 놓기 위한 것이 전부입니다. 이제 js 코드를 살펴보겠습니다.

먼저 아래의 코드로 기본 코드를 작성합니다.

const engine = Matter.Engine.create();
const world = engine.world;

const render = Matter.Render.create({
    element: document.querySelector("div"),
    engine: engine,
    options: {
        width: 800,
        height: 600,
        wireframes: false,
        background: 'black'
    }
});

Matter.Render.run(render);

const runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);

다음으로 지형과 지형 속에 사각형 물체를 구성하는 코드입니다.

fetch("./terrain.svg")
    .then((response) => { return response.text(); })
    .then((raw) => { return (new window.DOMParser()).parseFromString(raw, "image/svg+xml"); })
    .then(function(root) {
        const paths = Array.prototype.slice.call(root.querySelectorAll('path'));
        const vertices = paths.map((path) => { return Matter.Svg.pathToVertices(path, 30); });
        const terrain = Matter.Bodies.fromVertices(400, 350, vertices, {
            isStatic: true,
            render: {
                fillStyle: '#2c3e50',
                strokeStyle: '#2c3e50',
                lineWidth: 1,
            }
        }, true);

        Matter.World.add(world, terrain);

        const bodyOptions = {
            frictionAir: 0.1, 
            friction: 0.5,
            restitution: 0.1
        };
        
        Matter.World.add(world, Matter.Composites.stack(100, 200, 40, 10, 15, 15, (x, y) => {
            if (Matter.Query.point([terrain], { x: x, y: y }).length === 0) {
                return Matter.Bodies.polygon(x, y, 4, 10, bodyOptions);
            }
        }));
    }
);

끝으로 마우스를 통해 사각형 객체를 드레그하여 옮길 수 있도록 합니다.

const mouse = Matter.Mouse.create(render.canvas),
mouseConstraint = Matter.MouseConstraint.create(engine, {
    mouse: mouse,
    constraint: {
        stiffness: 0.2,
        render: {
            visible: false
        }
    }
});

Matter.World.add(world, mouseConstraint);

matter.js는 매우 정교한 강체 시뮬레이션에는 적합하지 않으나 시각적인 면에서 다양한 물리 효과를 2차원에서 폭넓게 응용할 수 있다는 점이 매우 큰 장점입니다.

아래는 필요한 코드와 데이터 전체를 다운로드 받을 수 있는 링크입니다.

Javascript 객체(object)의 키(key)와 값(value)을 배열로 얻기

만약 다음과 같은 자바스크립트 객체가 있다고 할 때..

const obj = {
  a: 'Dip2K',
  b: 30,
  c: true,
  d: {}
};

obj 객체를 구성하는 전체 키를 배열로 얻는 코드는 다음과 같습니다.

console.log(Object.keys(obj)); 
// Array ["a", "b", "c", "d"]

다시 obj 객체를 구성하는 전체 값을 배열로 얻는 코드는 다음과 같구요.

console.log(Object.values(obj)); 
// Array ["Dip2K", 30, true, Object {  }]

배열에 대해서 각 구성 항목을 참조하는 코드는 다음과 같습니다.

for(i of Object.values(obj)) {
    console.log(i);
}

/* 
"Dip2K"
30
true
Object {  }
*/

three.js에서 기본 정육면체(BoxGeometry)에 텍스쳐 맵핑하기

three.js에서 제공하는 기본 정육면체에 대해 텍스쳐 맵핑을 하는 코드는 다음과 같습니다.

const geometry = new THREE.BoxGeometry(1, 1, 1);
const loader = new THREE.TextureLoader();
const material = new THREE.MeshBasicMaterial({
    map: loader.load("flower-5.jpg", undefined, undefined, function(err) {
        alert('Error');
    }),
});
const cube = new THREE.Mesh(geometry, material);
this.scene.add(cube);

실행하면 다음과 같은 결과를 얻을 수 있습니다.

그런데 이 THREE.BoxGeometry는 각 면에 대해 다른 텍스쳐 맵핑을 지정할 수 있습니다. 아래처럼요.

const geometry = new THREE.BoxGeometry(1, 1, 1);
const loader = new THREE.TextureLoader();
const materials = [
    new THREE.MeshBasicMaterial({ map: loader.load("flower-1.jpg") }),
    new THREE.MeshBasicMaterial({ map: loader.load("flower-2.jpg") }),
    new THREE.MeshBasicMaterial({ map: loader.load("flower-3.jpg") }),
    new THREE.MeshBasicMaterial({ map: loader.load("flower-4.jpg") }),
    new THREE.MeshBasicMaterial({ map: loader.load("flower-5.jpg") }),
    new THREE.MeshBasicMaterial({ map: loader.load("flower-6.jpg") }),
];

const cube = new THREE.Mesh(geometry, materials);
this.scene.add(cube);

결과는 다음과 같습니다.

이 글은 three.js의 전체 코드가 아닌 정육면체에 텍스쳐 맵핑에 대한 코드만을 언급하고 있습니다. 전체 코드에 대한 뼈대는 아래 글을 참고 하시기 바랍니다. 위의 코드들은 모두 _setupModel 함수의 코드입니다.

three.js start project 코드