Vue.js - Component(컴포넌트) 1

이미지 출처(https://vuejs.org/images/logo.png)

컴포넌트라 함은 웹 어플리케이션을 구성하는 하나하나의 부품입니다. Vue.js에서는 이 컴포넌트를 가장 강력한 기능 중 하나라고 소개하고 있습니다. 컴포넌트란 무엇인가 이런 자세한 설명은 생략하고 Vue.js에서 컴포넌트를 사용하는 방법을 바로 알아보도록 하겠습니다.

컴포넌트 등록

1
2
3
4
<div id="app">
<test-component></test-component>
<test-component></test-component>
</div>
1
2
3
4
5
6
7
// 전역 컴포넌트 생성
Vue.component('test-component', {
template: '<p>테스트용 컴포넌트 생성</p>'
});
new Vue({
el: '#app'
});

See the Pen NveaEd by keun hyeok (@small) on CodePen.

Vue.component를 사용하여 전역 컴포넌트를 등록하면 Vue 인스턴스 내부 어디서든 해당 컴포넌트를 사용할 수 있게 됩니다. 이때 주의할 점은 컴포넌트가 사용되는 영역의 Vue 인스턴스화를 하기 전 컴포넌트를 등록해야 합니다. 이 예제에서는 test-component를 만들었고 특정 문장을 나타내는 기능을 하고 있습니다. 해당 컴포넌트를 2번 사용하였기에 일치하는 특정 문장이 2번 랜더링 되게 되었습니다.

컴포넌트 등록(지역)

1
2
3
4
5
6
<div id="app">
<test-component></test-component>
</div>
<div id="ohterApp">
<test-component></test-component>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
// 전역 컴포넌트 생성
var testComponent = {
template: '<p>테스트용 컴포넌트 지역 생성</p>'
}
new Vue({
el: '#app',
components: {
'test-component': testComponent
}
});
new Vue({
el: '#otherApp'
})

See the Pen XaGZRN by keun hyeok (@small) on CodePen.

components속성을 사용하여 컴포넌트를 해당 지역에서만 사용 가능하도록 등록할 수 있습니다. 예제는 test-component를 감싸고 있는 각각의 div에 한 번씩 사용하였지만 id가 app인 div내부에서만 test-component를 랜더링 하고 있는 것을 보여주고 있습니다.

컴포넌트 데이터 바인딩, 메서드 사용하기

1
2
3
4
5
<div id="app">
<test-component></test-component>
<test-component></test-component>
<test-component></test-component>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Vue.component('test-component', {
template: '<button @click="increment">{{count}}</button>',
data: function() {
return {
count: 0
}
},
methods: {
increment: function () {
this.count += 1
}
}
});

var app = new Vue({
el: '#app'
});

See the Pen eEXVON by keun hyeok (@small) on CodePen.

컴포넌트의 데이터는 data에서 관리합니다. 여기서 중요한 점은 이 data 속성은 이전처럼 객체가 아닌 함수여야 한다는 것입니다. 이는 컴포넌트마다 각각 자신의 상태를 가질 수 있게 하기 위함이라고 합니다. 이제 버튼을 클릭하면 increment 메서드가 실행되며 해당 버튼의 count 상태를 업데이트합니다.

Props

Vue.js 포스팅을 시작할 때 React 와 비슷한 느낌을 받았다는 말을 언급한 적이 있었습니다. 그 이유 중 큰 비중을 차지한 것이 Vue.js에서도 Props를 이용하여 하위 컴포넌트로 데이터를 전달해주기 때문입니다. Props를 사용하는 방법을 알아보도록 합니다.

1
2
3
4
5
6
<div id="app">
<test-component id="abk1341madgamlvba" :age="30"></test-component>
<p>
콘솔창을 열어 경고를 확인해봅시다.
</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Vue.component('test-component', {
props: {
id: {
type: String,
required: true
},
name: {
type: String,
default: 'noname'
},
age: {
type: Number,
validator: function(v) {
return v >= 50;
}
}
},
template: '<p>name: {{name}}, age: {{age}}</p>'
});
new Vue({
el: '#app'
});

See the Pen EvMLYJ by keun hyeok (@small) on CodePen.

props 프로퍼티로 전달받을 것으로 예상되는 데이터를 정의합니다. 타입 체크는 필수는 아니지만 원활한 개발을 위해 사용하시기를 권장합니다. 그리고 props를 받아오는 로직은 상위 컴포넌트에서 넘겨줍니다.

예제를 살펴봅시다.
type은 instanceof를 사용하여 클래스 타입을 체크합니다. [String, Number]처럼 배열을 사용하면 여러 가지 타입을 설정도 가능합니다. required는 필수 값 여부를 설정하고 name props의 default는 값이 들어오지 않을 경우 기본으로 사용되는 값을 의미합니다. 예제의 결과로써 noname이 표현된 이유이기도 합니다.
validator는 age 값을 받아와 특정 조건을 만족하는지 안 하는지 체크하고 false를 반환한다면 경고를 나타내 줍니다. 예제에서는 30이라는 값이 들어왔지만 validator의 조건을 만족하지 못하여 콘솔에 경고를 표시해주고 있습니다.

ps - 마크업 부분을 살펴보면 :age (v-bind:age)를 사용한 것 을 볼 수 있습니다. 이는 값이 String 형태로 넘어가는 것을 방지해준 것입니다.

Vue.js - Directive(지시자) 2/2

이미지 출처(https://vuejs.org/images/logo.png)

이번 2장에서 아직 다루지 않은 디렉티브들을 살펴볼 예정입니다. ( 요즘 프로그래밍 외 적으로 하고 있는 게 많아서 포스팅이 순위에서 많이 밀리네요 ㅠ )

이벤트 바인딩 v-on

1
2
3
4
5
6
7
<div id="app">
<button v-on:click.once="myClickEvent">이벤트 바인딩 1( once )</button><br/><br/>
<a href="/달나라" v-on:click.prevent="myClickEvent">이벤트 바인딩 2( prevent )</a>
<div class="mouse-area" v-on:mouseEnter="areaEnter" v-on:mouseLeave="areaLeave">
{{ areaState }}
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var app = new Vue({
el: '#app',
data: {
areaState: '마우스를 올려줘요!'
},
methods: {
myClickEvent: function() {
alert('테스트 이벤트입니다!');
},
areaEnter: function(e) {
this.areaState = '들어왔다!';
},
areaLeave: function(e) {
this.areaState = '나갔다!';
}
}
})

See the Pen zzyNma by keun hyeok (@small) on CodePen.

v-on 디렉티브를 사용하여서 우리가 methods에 등록한 메서드들을 특정 요소(element)의 이벤트로 연결 시켜줍니다. 사용 방법을 살펴보면 이벤트를 연결시킬 요소에 v-on:event="function" 형태로 등록을 하게 됩니다.

2번 라인의 버튼을 살펴보면 click 이벤트로 Vue 인스턴스에 등록된 myClickEvent를 연결 시킨 모습을 볼 수 있습니다. 이제 버튼을 클릭하면 alert 경고창을 볼 수 있게 되었지요. 여기서 잘 살펴보면 위 언급한 형태와 조금 상이한 부분을 찾을 수 있는데 바로 .once 라는 수식어가 추가 된 점입니다. 이 수식어는 이벤트를 1번만 처리되도록 해주는 기능을 가지고 있기에 이 버튼은 alert 경고창을 1번만 보여주고 더 이상 동작을 하지 않을 것입니다.

다음으로 3번 라인의 .prevent는 e.preventDefault() 처럼 기본 이벤트를 막아주기 때문에 /달나라 링크로 이동하지 않습니다. 이러한 수식어는 여러가지가 존재하고 있으니 한번쯤 공식 api를 살펴보시길 바랍니다

약어 사용

1
2
3
4
5
6
7
<div id="app">
<button @click.once="myClickEvent">이벤트 바인딩 1( once )</button><br/><br/>
<a href="/달나라" @click.prevent="myClickEvent">이벤트 바인딩 2( prevent )</a>
<div class="mouse-area" @mouseEnter="areaEnter" @mouseLeave="areaLeave">
{{ areaState }}
</div>
</div>

@를 사용하면 계속 v-on 을 타이핑 할 필요가 없어집니다. 저는 약어 사용이 더 편하기때문에 이 후 예제에서는 약어로 사용하겠습니다.

속성 바인딩 v-bind

1
2
3
4
5
6
7
<div id="app">
<img v-bind:src="myImage">
<p v-bind:class="[italicFont, { red : isRed }]">
흐으음~
</p>
<button @click="()=>{this.isRed = !this.isRed}">색칠!</button>
</div>
1
2
3
4
5
6
7
8
var app = new Vue({
el: '#app',
data: {
myImage: 'http://placehold.it/120x120?text=image',
italicFont: 'italic',
isRed: false
}
});

See the Pen ZyVaVv by keun hyeok (@small) on CodePen.

v-bind 디렉티브는 html 요소들의 속성들을 동적으로 바인딩을 해주는 기능입니다.

예제를 살펴보면 myImage라는 이미지 경로의 string 값을 src 속성으로 바인딩 해주었습니다.
다음으로 p 태그를 살펴보면 Array와 Object 를 사용하여서 동적으로 클래스를 바인딩 해주고 있습니다.배열의 각 값들을 해당 html 요소의 클래스로 부여합니다. 배열이 아닌 객체 형태는 { className: Boolean } 형태를 취하며 값인 Boolean이 true를 가진다면 해당 className을 html 요소에 부여합니다.
예제에서의 Boolean영역에 위치한 isRed는 처음에는 false여서 red라는 클래스를 주지 않았지만 아래의 버튼으로 isRed값을 true로 만들면 red 라는 클래스가 추가되는 것을 확인할 수 있습니다.

약어 사용

1
2
3
4
5
6
7
<div id="app">
<img :src="myImage">
<p :class="[italicFont, { red : isRed }]">
흐으음~
</p>
<button @click="()=>{this.isRed = !this.isRed}">색칠!</button>
</div>

v-bind도 v-on과 마찬가지로 : 라는 약어로 사용할 수 있습니다.

인풋 데이터 바인딩 v-model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="app">
<section>
<h1>textarea or input value</h1>
<textarea type="text" v-model="msg" placeholder="줄바꿈은 enter"></textarea>
<h2>입력값 :</h2>
<p>{{msg}}</p>
</section>
<section>
<h1>checkbox or radio</h1>
<input id="check1" type="checkbox" v-model="items" value="아이템1"/>
<label for="check1">아이템1</label><br/>
<input id="check2" type="checkbox" v-model="items" value="아이템2"/>
<label for="check2">아이템2</label><br/>
<input id="check3" type="checkbox" v-model="items" value="아이템3"/>
<label for="check3">아이템3</label><br/>
<input id="check4" type="checkbox" v-model="items" value="아이템4"/>
<label for="check4">아이템4</label><br/>
<input id="check5" type="checkbox" v-model="items" value="아이템5"/>
<label for="check5">아이템5</label>
<h2>체크 아이템 : </h2>
{{items}}
</section>
<section>
<h1>select</h1>
<select v-model="selectItem">
<option value="셀렉트a">A</option>
<option value="셀렉트b">B</option>
<option value="셀렉트c">C</option>
</select>
<h2>선택 값 : </h2>
{{selectItem}}
</section>
<button id="getData" @click="getCurrentData">get current data</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var app = new Vue({
el: '#app',
data: {
msg: '',
items: [], // 단일의 checkbox의 경우는 array가 아닌 boolean 값을 사용합니다.
selectItem: '샐렉트a'
},
methods: {
getCurrentData: function() {
alert(`
msg: ${this.msg}
items: ${this.items}
selectItem: ${this.selectItem} `);
}
}
});

See the Pen XgoZjb by keun hyeok (@small) on CodePen.

이전까지 살펴 보았던 예제들은 모두, vue 인스턴스가 가지고 있는 data를 단방향으로 바인딩을 해주었다면 이제부터는 v-model을 사용하여 data를 양방향으로 바인딩 할 수 있도록 해줍니다.

만약 잘 이해가 가지 않는다면 예제의 값들을 변경 해보고 우측 하단의 버튼을 클릭해봅시다. 이전까지의 예제는 특정 이벤트를 발생시켜 data를 변경해왔지만 view의 form elements 들을 이용하여 data들을 변경할 수 있는 사실을 알 수 있습니다.

v-pre, v-cloak, v-once

See the Pen zzQzOV by keun hyeok (@small) on CodePen.

  • v-pre는 {{}}인 머스타치 구문을 랜더링 하지 않고 보여주기 위한 디렉티브 입니다. (html의 pre태그 혹은 css의 white-spce: pre | pre-line 을 생각하면 될 것 같습니다.)
  • v-once는 데이터를 단 1번만 랜더링을 해주는 디렉티브입니다. 예제의 증가버튼을 눌러도 초기 num은 v-once로 설정되었기 때문에 값이 다시 바인딩되지 않습니다. 이는 업데이트 성능을 최적화 하는데 사용한다고 합니다.
  • v-cloak은 Vue인스턴스가 컴파일이 완료될때 까지 elements 들에 남아있다가, 컴파일이 완료되면 사라집니다. 컴파일이 완료되지 않은 상태에서는 머스타치 구문이 그대로 노출되기때문에 완료될때 까지 숨겨줄 필요성이 있습니다. 단 아래와 같은 css 구문이 필요합니다.
    1
    2
    3
    [v-cloak] {
    display: none;
    }

지금까지 Vue.js의 디렉티브들을 살펴보았습니다.

Vue.js - Directive(지시자) 1/2

이미지 출처(https://vuejs.org/images/logo.png)

디렉티브란 Vue.js 에서 사용되는 특별한 속성 입니다. Angular1 을 사용해보셨던 분들은 익숙할 것 입니다. Angular에서 ng- 를 사용한다면 Vue 에서는 v- 라는 접두사를 사용합니다. 이번 포스팅에서는 Vue.js 에서 기본적으로 제공하는 디렉티브 몇가지를 알아보려고 합니다.

v-text와 v-html

1
2
3
4
5
<div id="app">
{{text}}
<p v-text="text"></p> <!-- {{text}} 와 동일 -->
<p v-html="htmlText"></p>
</div>
1
2
3
4
5
6
7
var app = new Vue({
el: '#app',
data: {
text: '안녕안녕~',
htmlText: '<span style="color: red">하이하이~</span> !!'
}
});

See the Pen KqmeoZ by keun hyeok (@small) on CodePen.

  • v-text: {{ }} 와 같은 기능을 합니다. 해당 엘리먼트의 값이 data의 text 값으로 설정 된 것을 볼 수 있습니다.

  • v-html: html 코드를 랜더링 할 경우 사용합니다. v-text나 머스타치 구문을 사용할 경우 해당 string값 그대로 표시됩니다.

v-show와 v-if

1
2
3
4
5
6
7
8
9
10
11
<div id="app">
<section>
<h1>v-show</h1>
<span v-show="isShow">{{text}}</span>
</section>
<section>
<h1>v-if</h1>
<span v-if="isShow">{{text}}</span>
</section>
<button @click="toggleText">토글</button> <!-- 이벤트 바인딩 -->
</div>
1
2
3
4
5
6
7
8
9
10
11
12
var app = new Vue({
el: '#app',
data: {
isShow: false,
text: '보일까 말까!'
},
methods: {
toggleText: function() {
this.isShow = !this.isShow;
}
}
});

v-show와 v-if 모두 화면에 표시하는지 안하는지에 대한 지시자 입니다.

  • v-show: css의 display 속성에 의하여 컨트롤 됩니다. 값이 false여도 랜더링은 되지만 display: none 처리가 되어있는 것 입니다.

  • v-if: 해당 엘리먼트의 랜더링 여부가 판단됩니다. 값이 false면 랜더링이 되지 않습니다. true라면 랜더링이 될 것이며 true에서 false로 변경될 때는 해당 엘리먼트가 삭제됩니다.

아래 이미지를 보면 v-if는 해당 엘리먼트가 랜더링 되지 않았고, v-show 경우 display: none 처리가 되어있는 것을 보실 수 있습니다.

v-if

See the Pen mwmzRd by keun hyeok (@small) on CodePen.

v-if의 else

if문이 있으니 else 와 else if도 자연스럽게 따라오게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<p v-if="state === 0">
if 입니다.
</p>
<p v-else-if="state === 1">
else if 입니다.
</p>
<p v-else>
else 입니다.
</p>

<button @click="changeState">토글</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var app = new Vue({
el: '#app',
data: {
state: 0
},
methods: {
changeState: function() {
// 0 ~ 2 랜덤 부여
var state = parseInt(Math.random() * 3, 10);
alert(state);
this.state = state;
}
}
});

See the Pen MomzyZ by keun hyeok (@small) on CodePen.

바로 v-else-if와 v-else는 바로 이전 형제엘리먼트가 v-if 일 경우 사용한다는 점만 기억해두시면 됩니다. ( 우리가 아는 if와 같습니다! )

v-for 디렉티브

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div id="app">
<h1>v-for example</h1>
<div class="array-area">
<h2>array</h2>
<p v-for="(item, index) in arrayItem">{{index}} : {{item}}</p>
</div>
<div class="obj-area">
<h2>object</h2>
<dl>
<template v-for="(item, key, index) in objItem">
<dt>{{key}}</dt>
<dd>{{item}}</dd>
</template>
</dl>
</div>
<div class="num-area">
<h2>number</h2>
<p v-for="n in 7">{{n}}</p>
</div>
<div class="string-area">
<h2>string</h2>
<p v-for="s in text">{{s}}</p>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
var app = new Vue({
el: '#app',
data: {
arrayItem: ['arr1', 'arr2', 'arr3', 'arr4'],
objItem: {
name: 'hyeok',
age: 22,
job: 'front end developer',
birthday: '0927'
},
text: '안녕해요!'
}
});

See the Pen gRRgzX by keun hyeok (@small) on CodePen.

v-for디렉티브는 굉장히 자주 사용되는 디렉티브 중 하나 입니다. 이 디렉티브는 기본적으로 alias in(of) expression 문법을 사용하며 expression자리에는 뷰모델의 데이터 들 중 Array, Object, Number, String 형태를 가진 값들이 올 수 있습니다.

  • Array: 배열을 순환하며 alias 에 해당 인덱스의 값을 부여합니다. (alias, index) in expression 형태로 변경하여 인덱스 값 또한 넘겨받을 수 있습니다.

  • Object: 배열과 마찬가지로 객체를 순환합니다. 순환 순서는 Object.keys()로 나열된 키의 순서로 결정됩니다. (alias, key, index) in expression 형태로 사용이 가능합니다.

  • Number: expression의 값 만큼 순환하며 그 값은 1부터 시작합니다.

  • String: 문자열을 한자 한자 쪼개 한 단어씩 순환하며 표기합니다.

참고

Vue.js - LifeCycle

이미지 출처(https://vuejs.org/images/logo.png)

라이프사이클이란 Vue 생성자의 호출 시점부터 mount 되기까지, 나아가 destroy 되기까지의 특정 시점들에 실행되는 메서드들 입니다. 아래 그림의 붉은 테두리를 가진 기호들이 Vue.js의 라이프사이클의 각 실행 시점입니다. (Vue.js가 어떤 흐름으로 동작하는지도 잘 나타나 있어서 많은 도움이 됩니다.)

life-cyle
출처 - Vue.js 공식문서

위에서부터 아래로 쭉 실행됩니다. 흐름자체는 그림만으로도 충분히 이해가 가능할 것 이라고 생각합니다. 그러면 이제 각 라이프사이클 메서드들이 어떤 특징을 가지고 있는지 예제를 통하여 살펴봅시다.

예제

  • html
1
2
3
4
5
6
7
<div id="app">
{{text}}
<br/>
<button @click="changeText">변경!!</button>
<br/>
<button @click="destroyApp">파괴!?</button>
</div>

@click이 지난 예제에 있던 v-on의 약자입니다. (이벤트를 등록합니다.)

  • script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
var app = new Vue({
el: '#app',
data: {
text: '안녕하세요~'
},
methods: {
changeText: function() {
this.text = '반갑습니다!'
},
destroyApp: function() {
console.group('--- destroyApp method ---');
console.log(this === app);
console.log(this.$destroy());
console.groupEnd();
}
},
beforeCreate: function() {
// 아직 데이터 및 이벤트 정의 안됨
console.group('--- beforeCreate ---');
console.log('text : ', this.text); // undefined
console.groupEnd();
},
created: function() {
// 데이터와 이벤트는 접근 가능하지만, 아직 마운트가 안되서 $el 접근 불가
console.group('--- created ---');
console.log('text : ', this.text); // 안녕하세요~
console.log('element :', this.$el); // undefined
console.groupEnd();
},
mounted: function() {
// el에 접근이 가능합니다. 보통 여기서 초기 데이터를 불러오곤 합니다.
// beforeMount 에서는 el 접근 불가합니다.
console.group('--- mounted ---');
console.log('element : ↓');
console.log(this.$el); // object HTMLDivElement
console.groupEnd();
},
beforeUpdate: function() {
// data가 변경된 후 DOM 랜더링 직전에 실행 됩니다. 그로 인해 변경될 text data를 가지고 있는 것 입니다.
console.group('--- beforeUpdate ---');
console.log('before update : ', this.text); // 반갑습니다!
console.groupEnd();
},
updated: function() {
// DOM 랜더링이 끝난 후 실행됩니다. DOM 변경이 완료 된 후 특정 작업을 실행해야할 경우 이곳에..
console.group('--- updated ---');
console.log('updated : ', this.text); // 반갑습니다!
console.groupEnd()
},
destroyed: function() {
console.group('--- destroyed ---');
console.log('watcher : ', this._watcher.active);
this.text = '변해라 ㅠ 안변한다 ㅠ';
console.groupEnd();
}
});

See the Pen awmZRa by keun hyeok (@small) on CodePen.

아마 codepn을 embed해서 현재 블로그 페이지의 콘솔에서 제가 작성한 예제의 로그들이 나타날 것 입니다.(혹시 안나온다면 예제 복사하셔서 보시면 됩니다.. ㅠ)

console
예제 실행 -> 변경 버튼 클릭 -> 파괴 버튼 클릭을 한 모습입니다.

  • beforeCreate : 해당 영역에서는 data의 text를 접근할 수 없었습니다. 해당 시점에서는 아직 어떠한 설정이 되지 않았기 때문에 data는 물론 methods에도 접근할 수 없는 상태입니다.

  • created : 이벤트 및 데이터 설정이 완료되었습니다. 하지만 아직 템플릿이나 DOM이 마운트가 되어있지 않습니다. this.data는 제대로 가져오는 반면 this.$el 을 undefined로 반환해주고 있습니다.

  • beforeMount : 마운트 바로 이전 시점이며 render라는 메서드가 호출되는 시점입니다. 역시 this.$el 은 아직 접근 불가능합니다. 아마 beforeMount는 사용할 일이 많지 않을 것 입니다. (redenr 메서드는 추후에 다뤄보도록 하겠습니다.)

  • mounted : 마운트가 완료된 시점이며, this.$el 에 접근이 가능해집니다. 보통 이 부분에서 ajax를 호출하여 데이터를 불러옵니다.

  • beforeUpdate : changeText 메서드를 실행시켜 text data를 변경 후 실행됩니다. 데이터는 변경되어있는 상태이며 DOM 랜더링만 되지 않은 상태이기 떄문에 this.text가 랜더링될 예정인 반갑습니다! 를 반환해 줍니다.

  • updated : beforeUpdate 후 DOM 랜더링 까지 완료 된 후 실행되는 시점입니다.

  • beforeDestroy : 파기되기 직전의 상태입니다. 해당 인스턴스는 이 시점까지 완벽한(?) 동작을 수행합니다.

  • destroyed : 모든 기능이 파기 된 후 호출됩니다. 모든 이벤트가 제거되어있으며 하위에 존재하는 인스턴스 모두 삭제 됩니다. this._watcher.active가 false를 반환하며 더이상 감시하지 않는다는 상태를 알려줍니다.

ps - 그림에는 없지만 Vue.js 2.2.0 부터 <keep-alive> 라는 기본 내장 컴포넌트에서 activateddeactivated라는 라이프 사이클이 동작한다고 합니다만 이번 포스팅에서는 다루지 않겠습니다.

Vue.js - 뷰 모델 생성과 데이터 바인딩

이미지 출처(https://vuejs.org/images/logo.png)

뷰 모델 생성

ViewModel을 생성하는 방법은 간단합니다. 전역에 부여되는 Vue 생성자로 생성한 인스턴스가 ViewModel이 됩니다. 인스턴스를 생성할 때 여러 옵션을 넘겨 줄 수 있는데 이번 예제로 어떠한 옵션들이 있는지 살펴보고자 합니다.

사전 준비 작업은 진행하지 않겠습니다. (파일에 cdn 으로 스크립트 한 줄 추가 해주세요.)

  • html
1
2
3
4
5
6
7
8
<div id="app"> <!-- el 부분 -->
{{text}} <!-- data binding은 머스타치 템플릿 구문 사용 -->
<test-component></test-component>
{{otherText}} <!-- computed 되고 있는 데이터 -->
 
<!-- 메서드의 changeText를 v-on디렉티브를 이용하여 바인드, @click="changeText"로 축약 가능 -->
<button v-on:click="changeText">버튼!</button>
</div>
  • script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var app = new Vue({
el: '#app', // 마운트 할 DOM 엘리먼트
data: {
text: 'My Text',
componentText: 'Hi ??'
},
methods: { // 기능 메서드 그룹
changeText: function() {
// this가 app 인스턴스로 사용되기 위해 화살표 함수를 사용하면 안됩니다.
this.text = 'My Changed Text'
}
},
computed: {
// 의존하고 있는 반응형 속성(data 객체)이 변경될 때 마다 다시 초기화 됩니다.
otherText: function() {
// 마찬가지로 화살표 함수를 사용하면 안됩니다.
return this.text + ', yeah!!' // this.text 의존
}
},
components: { // component 그룹, #app 에서만 사용 가능한 child component
'test-component': {
// this.$parent로 부모 인스턴스에 접근 합니다.
template: '<p><i>{{this.$parent.componentText}}</i></p>'
}
}
});
  • 결과 화면

See the Pen WOrqLM by keun hyeok (@small) on CodePen.

주석으로 간단하게나마 설명을 달아두었습니다만 한번 더 천천히 살펴보겠습니다.

  • el: 해당 인스턴스를 마운트 할 DOM을 선택합니다. 이는 new를 사용하여 인스턴스를 생성할 경우에만 사용됩니다. (다른 경우는 뒤에서)

  • data: 해당 인스턴스의 데이터 객체입니다. 기본 객체 형태여야만 하며 인스턴스화 하면서 이 속성들을 반응형으로 만들어줍니다. 반응형이라는 시스템은 Vue.js에서 굉장히 중요한 요소입니다. 현재는 해당 속성이 변경되면 내부에서 이를 감지하여 다시 화면에 랜더링 해준다는 정도만 기억 해둡시다. 콘솔창을 열어서 app.text = 'some text';라고 입력해 봅시다. 어떤 의미인지 이해 되실거라고 생각됩니다. 자세한 내용은 뒤에서 따로 정리를 해보도록 할 예정입니다.

  • methods: 메서드들을 정의하는 객체 입니다. 인스턴스를 통해 직접 접근하거나( 콘솔에서 app.changeText(); ) 혹은 v-on이라는 디렉티브를 이용하여 사용할 수 있습니다.

  • computed: computed의 속성들은 data의 속성들에 의존합니다. data의 속성이 바뀔 때 마다 이를 의존하고 있는 속성은 계속 업데이트 될 것 입니다. 버튼을 클릭하여 text 라는 데이터를 변경하면 이를 의존하고 있는 otherText도 같이 변경되는 것을 볼 수 있습니다.

  • components: 해당 인스턴스에서 사용할 수 있는 컴포넌트를 정의 합니다. el속성의 영역에서만 사용 가능합니다. (Vue.component()를 사용하여 전역으로도 컴포넌트 생성이 가능합니다.)

자주 사용되는 옵션들 중 몇가지를 살펴 보았습니다. 설명을 진행하다보니 뒤에서 라는 말 혹은 예제에 언급되지 않은 문법 등이 설명 중간중간에 섞여 있습니다. 현재 모든 것을 다 설명하기에는 너무나도 큰 영역이기 때문에 현재는 이 정도의 이해만을 목표로 해도 괜찮습니다. Vue.js가 어떤 흐름으로 동작하는지, 어떠한 특성을 가지고 있는지 단편적으로 살펴 보기에는 충분할 것이라 생각 합니다. 다음에는 Vue 인스턴스의 라이프사이클에 대해서 잠깐 알아보도록 하겠습니다.

Vue.js - 시작하기

이미지 출처(https://vuejs.org/images/logo.png)

시작하기 앞서

지난 주말 여기 저기서 들려오고 있으며 엄청난 성장세를 보여주고 있는 Vue.js의 공식 문서를 한번 읽어 보았습니다. 한글화가 굉장히 잘 되어있어서 금방금방 슝슝 스크롤을 내리면서 읽을 수 있더군요.(커뮤니티분들께 감사) 처음 문법을 보면서 느낀점은 마치 angular1 과 react를 합친 듯 한 기분이 들었습니다. 또 ‘러닝커브가 낮고 굉장히 효율적이다.’ 라는 소리가 괜히 나오는게 아닌것 같았습니다. 문서를 한번 읽어보니 어떤 흐름으로 흘러가는지 대충 파악이 가능했습니다. 사용 방법도 매우 간단하여 jquery처럼 문서에 스크립트 한줄 추가(이 방법이 best practice 라는건 아닙니다!)하거나 vue-cli와 같은 제너레이터를 이용하여 쉽게 webpack 혹은 browserify와 함께 사용할 수 있습니다. 진행하는 프로젝트 중 spring 위에서 프레임워크 없이 jquery를 기반으로 사용하는 것이 있는데 Vue.js라면 쉽게 특정 부분에서 사용할 수 있겠다 싶어서 바로 적용해 보았습니다. 결과부터 말하자면 아직 진행중이지만 꽤 괜찮은 것(기존 코드가 너무…) 같더군요. 물론 아직 지식이 부족해서 많은 삽질이 있었지만 나름 만족스럽게 진행되어 가고 있는 듯 합니다. 이 작업을 계기로 장기적으로 프로젝트에 조금씩 도입해가며 기초부터 한번 포스팅을 해보고자 해서 쓰게 되었습니다.

소개

Vue.js는 이름에서 예상할 수 있듯 view작업에 초점을 맞춘 프레임워크입니다. 그래서인지 React와 많은 공통점을 가지고 있으며, 디렉티브(지시자)라는 문법이 등장하는데 이는 angular1에서 영감을 받아 상당 부분 개선되었다고 합니다. 이곳에서 다른 프레임워크와 Vue.js가 어떻게 다른지 어떠한 부분이 효율적인지에 대해서 자세히 설명되어 있으니 관심이 있으신 분들은 한번 읽어보시길 바랍니다.

호환

공식적으로 es5를 지원하는 브라우저에서 사용가능하다고 합니다. es6 문법을 사용한다면 babel 컴파일을 거쳐야합니다.

설치

  • npm
1
npm i vue
  • cdn
1
<script src="https://unpkg.com/vue"></script>

프로젝트에 위 스크립트 한줄을 추가 함으로써 Vue.js를 사용할 수 있습니다. (.Vue 확장자를 쓰면 모듈식으로 사용도 가능하지만 예제는 이런 방식으로 하지 않겠습니다.)
ps - create-react-app과 같은 vue-cli라는 어플리케이션을 구성해주는 도구도 있습니다. 위에서 언급 하였듯, webpack or browserify의 설정까지 해주어서 편리합니다.

간단한 어플리케이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<div id="app">
{{text}}
</div>
<script src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
text: 'test 입니다.'
}
});
// 콘솔에서 한번...
// app.text = '바뀌어라!!';
</script>
</body>
</html>

Result

See the Pen OgMRyZ by keun hyeok (@small) on CodePen.

Vue.js는 MVVM 패턴에 영감을 받았다고 합니다. 위 예제에서의 Vue 생성자는 MVVM(Model-View-ViewModel)중 VM(ViewModel)을 생성합니다. 이때 인자로 들어가는 객체를 통하여 여러 옵션을 지정해 줄 수 있습니다. 현재 예제에서는 정의한 data가 템플릿에서 {{property}} 와 같은 형태로 바인딩 된다는 것 입니다. 다른 옵션들에 대해서는 차차 알아가 보도록 하겠습니다.

맺음

아직 저도 공부를 하는 입장인지라(만만히 보고 도입했다가 삽질 또 삽질…) 공식 문서를 기반으로 한 예제 및 느낀 점을 기록하는 용도로서의 포스팅이 될 것 같습니다.

참고

Vue.js 한글 문서

Node.js(express) - express-session 로그인(3/3)

패스워드와 같은 중요한 정보는 누군가에게 알려져서는 안되는 정보입니다. 만약 이 정보가 털리더라도 복호화를 할 수 없는 단방향 암호화를 사용하는것이 일반적이라고 할 수 있습니다. 그래서 2장까지 진행하였던 로그인 예제의 패스워드를 암호화 하여 저장하는 방법을 알아보려고 합니다. 사용되고 있는 여러 암호화 알고리즘이 있지만 저는 bcrypt를 사용하도록 하겠습니다. pbkdf2와 더불어 많이 쓰이는 방법 중 하나입니다.

가입을 진행할때 여태까지는 사용자가 입력한 값을 그대로 저장하였지만 암호화를 진행하고 생성된 Digest(암호화된 값)을 저장하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.post('/join', (req, res) => {
const body = req.body;
if( !findUser(body.user_id, body.user_pwd) ) {
// 아이디도 중복안되게 분기 해야는데 예제이므로..
const salt = bcrypt.genSaltSync(10); // salt값 생성, 10이 default
const hash = bcrypt.hashSync(body.user_pwd, salt); // Digest
users.push({
user_id: body.user_id,
user_pwd: hash,
user_nickname: body.user_nickname
});
res.redirect('/login');
} else {
res.send('이미 존재함');
}
});

5, 6번 라인이 암호화를 진행하는 작업이다. salt라는 것 은 비밀번호를 암호화 하기 전에 랜덤한 값을 더하여 결과 값을 무작위로 만들어줍니다. 동일한 패스워드를 입력하더라도 생성된 값은 항상 다르게 되는것 이지요. 인자로 숫자가 들어가있는데 2^n만큼 해싱작업을 진행합니다.(자세한 내용은 범위가 작지 않은것 같으므로 생략하겠습니다.) 이제 생성된 값을 users에 저장합니다. 이제 비밀번호는 더 이상 우리가 입력한 값을 그대로 비교하는 것으로는 인증이 되지 않습니다. find하는 과정을 수정해야 합니다.

1
2
3
4
5
6
7
8
const findUser = (user_id, user_pwd) => {
// id와 password가 일치하는 유저 찾는 함수, 없으면 undefined 반환
return users.find( v => (v.user_id === user_id && bcrypt.compareSync(user_pwd, v.user_pwd) ) );
}
const findUserIndex = (user_id, user_pwd) => {
// 일치하는 유저의 index값(유니크) 반환
return users.findIndex( v => (v.user_id === user_id && bcrypt.compareSync(user_pwd, v.user_pwd)) );
}

사용자가 입력한 비밀번호를 compareSync를 사용하여 저장되어있는 비밀번호와 비교합니다. 일치하면 true를 반환해줄 것 입니다. 이제 암호화와 관련된 모든 작업을 마쳤습니다.

맺음

저도 처음 공부해본 내용이라 혹 개념적으로 틀린 내용이 있다면 알려주시면 감사합니다 ㅠ

참고

Node.js(express) - express-session 로그인(2/3)

2장에서는 로그인 및 가입에 관한 로직을 작성하겠습니다. 우선 로그인 form이 있는 login.ejs 파일을 만들겠습니다.

  • login.ejs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/login" method="post">
아이디 : <input type="text" name="user_id"><br/>
비밀번호 :<input type="password" name="user_pwd"><br/>
<input type="submit">
</form>
</body>
</html>

/login 엔드포인트로 post요청을 보내는 form입니다. 여기서 입력한 아이디와 패스워드로 가입되어있는 회원이라면 정상적으로 로그인 처리와 세션을 등록해주는 기능을 할 것 입니다.

  • app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/login', (req, res) => {
res.render('login'); // login.ejs 랜더링
});
app.post('/login', (req, res) => {
const body = req.body; // body-parser 사용

if( findUser( body.user_id, body.user_pwd ) ) {
// 해당유저가 존재한다면
req.session.user_uid = findUserIndex( body.user_id, body.user_pwd ); //유니크한 값 유저 색인 값 저장
res.redirect('/');
} else {
res.send('유효하지 않습니다.');
}
});

매우 직관적인 코드여서 이해하기 어렵지 않을 것 입니다.(아마도..?)
해당 유저가 존재한다면 해당 유저의 유니크한 값(여기서는 인덱스)을 세션의 user_uid에 저장합니다. 로그인을 하였으면 로그아웃에 관한 기능이 필요한 것은 당연합니다.

  • app.js
1
2
3
4
app.get('/logout', (req, res) => {
delete req.session.user_uid;
res.redirect('/');
});

로그아웃 또한 매우 간단합니다. 세션에 저장되어있는 user_uid 프로퍼티를 삭제해주기만 하면 됩니다. 이제 로그인 로그아웃이 구현되었습니다. 이쯤에서 app.js를 실행 하고 테스트를 한번 해보도록 합시다.

after login

users 배열에 hyeok이라는 user_id를 가진 정보로 로그인을 해보았습니다. 정상적으로 닉네임이 표시되는 것을 확인할 수 있습니다. 만료기간을 따로 지정하지 않았기때문에 브라우저를 완전히 종료하지 않는 이상 이 세션은 그대로 유지될 것 입니다. 다음은 회원을 추가(가입)하는 기능을 해보겠습니다. 예상하셨겠지만 그냥 users 배열에 새로운 유저정보 객체를 push해주면 됩니다.

  • join.ejs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/join" method="post">
아이디 : <input type="text" name="user_id"><br/>
비밀번호 : <input type="password" name="user_pwd"><br/>
닉네임 : <input type="text" name="user_nickname"><br/>
<input type="submit">
</form>
</body>
</html>
  • app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.get('/join', (req, res) => {
res.render('join');
});
app.post('/join', (req, res) => {
const body = req.body;
if( !findUser(body.user_id, body.user_pwd) ) {
// 아이디도 중복안되게 분기 해야는데 예제이므로..
users.push({
user_id: body.user_id,
user_pwd: body.user_pwd,
user_nickname: body.user_nickname
});
res.redirect('/login');
} else {
res.send('이미 존재함');
}
});

findUser를 통해 유저가 없다고 판단되면 users배열에 새로운 객체를 추가해줍니다. 이제 가입부터 로그인, 로그아웃 모든 기능이 구현되었습니다. 3장에서는 비밀번호를 암호화 하여 저장하는 방법을 알아보겠습니다. 사용할 암호화 방식은 bcrypt 알고리즘입니다.

Node.js(express) - express-session 로그인(1/3)

세션은 쿠키와 마찬가지로 웹 사이트로 부터 저장되는 데이터이며 조금 더 발전(?)한 기능입니다. 세션은 쿠키와는 다르게 사용자의 컴퓨터가 아닌 서버에 저장이 되며, 저장된 데이터는 세션을 생성하면서 사용자의 쿠키에 저장된 식별자 정보를 통해 접근할 수 있습니다. 누군가에게 알려져서는 안될 정보들은 보다 안전한 세션에 저장해두어야 합니다. express-session이라는 모듈을 사용하여 사용자의 로그인 인증정보를 세션에 저장하는 방법을 알아보겠습니다.

Directory

  • project
    • views
      • index.ejs
      • join.ejs
      • login.ejs
    • app.js

설치

1
2
3
4
5
npm init
.
.
.
npm i --save express ejs express-session body-parser bcrypt-nodejs

회원을 추가하는 부분도 포함되었기 때문에 body-parser도 사용하였고, bcrypt-nodejs는 3장에서 살펴볼 암호화와 관련된 모듈입니다.

예제

세션 미들웨어 등록

  • app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const session = require('express-session');
const bcrypt = require('bcrypt-nodejs'); // 3장에서 사용할 암호화 모듈
 
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
 
app.use(bodyParser.urlencoded({extended: false}));
app.use(session({
secret: 'ambc@!vsmkv#!&*!#EDNAnsv#!$()_*#@',
resave: false,
saveUninitialized: true
}));
 
app.listen(3000);

우선 세션 미들웨어 등록하는 방법부터 알아보겠습니다. 10번 라인을 보시면 express-session모듈에서 가져온 미들웨어를 등록하는 부분이 있습니다. 이 부분을 등록하면 요청시 마다 req.session으로 세션 객체에 접근할 수 있게 됩니다. 옵션으로 들어간 값을 한번 살펴보겠습니다.

  • secret: secret은 필수로 들어가야하는 옵션입니다. 세션은 기본적으로 식별자를 쿠키에 저장하게 되는데 그 저장되는 데이터를 암호화 하기위해 필요한 옵션입니다.

  • resave: 요청이 왔을때 세션을 수정하지 않더라도 세션을 다시 저장소에 다시 저장되도록 합니다. 2개 이상의 병렬요청이 왔을 경우 원치 않은 저장이 이루어질 수 있으니 유의 해야합니다. (false 권장)

  • saveUninitialized: 초기화 되지 않은 세션을 강제로 저장합니다. 이는 모든 방문자들에게 고유한 식별 값을 주는 것과 같습니다.

이제 라우팅 처리를 하기 전 회원과 관련된 몇몇 기능을 미리 만들어 두겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app.js 내부에 선언합니다.
const users = [
{
user_id: 'hyeok',
user_nickname: '혁',
user_pwd: '123456'
},
{
user_id: 'hyc7575',
user_nickname: '에이치',
user_pwd: '1q2w3e4r'
}
]
const findUser = (user_id, user_pwd) => {
// id와 password가 일치하는 유저 찾는 함수, 없으면 undefined 반환
return users.find( v => (v.user_id === user_id && v.user_pwd === user_pwd) );
}
const findUserIndex = (user_id, user_pwd) => {
// 일치하는 유저의 index값(유니크) 반환
return users.findIndex( v => (v.user_id === user_id && v.user_pwd === user_pwd) );
}

users라는 회원 로그인 정보와 해당 유저를 찾는함수 그리고 해당 유저의 인덱스값(유니크한 값으로써 사용)을 구하는 함수를 생성하였습니다. 기본적으로 필요로한 기능들은 만들어졌으니 라우팅 작업을 시작합니다.

  • app.js
1
2
3
4
5
6
7
// 미들웨어 아래쪽으로 배치해주세요.
app.get('/', (req, res) => {
const sess = req.session; // 세션 객체에 접근
res.render('index', {
nickname: sess.user_uid+1 ? users[sess.user_uid]['user_nickname'] : ''
});
});
  • index.ejs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>Home</h1>
<% if( !nickname ) { %>
<p>로그인을 해주세요.</p>
<a href="/login">로그인</a>
<a href="/join">회원가입</a>
<% } else { %>
<p>안녕하세요. <%= nickname %></p>
<a href="/logout">로그아웃</a>
<% } %>
</body>
</html>

라우터에서 넘겨준 nickname값의 유무를 통해 로그인 여부를 판단하고 각각 다른 값을 랜더링 해주도록 만들었습니다.

이제 session을 사용하기위한 모든 준비가 끝났습니다. 미들웨어의 옵션으로 saveUninitialized값을 true로 설정해주었기 때문에 app.js를 실행하고 접속하면 아직 세션에 아무런 값도 설정해주지는 않았지만 쿠키에 고유한 식별값을 남겨두었을 것 입니다.
connect.sid
기본적으로 connect.sid라는 이름으로 저장됩니다. 이 쿠키값을 통해서 메모리에 저장되어있는 세션정보를 읽을 수 있는 것 입니다. 로그인 및 가입 구현은 2장에서 진행하겠습니다.

참고

express-session에서 세션 설정에 관한 많은 옵션들을 살펴보실 수 있습니다.

Javascript(잡담) - new Array(n)의 undefined

작업중인 라이브러리의 메서드 중 인자로 들어오는 array들의 중복값을 리턴해주는 것이 있습니다. 이를 튜닝하면서 생긴 뻘짓을 쓰는 글 입니다. 성능 테스트를 하기 위해서 new Array(10000000); 형태로 배열을 만들어서 테스트 하였습니다.

1
2
3
4
5
var a = new Array(1000000000);
var b = new Array(1000000000);
var c = new Array(100000000);
var d = new Array(10000000);
method(a,b,c,d); // [];

성능 자체는 만족스럽게 올라갔지만 예상했던 [undefined]가 출력되지 않음.. Array 생성자에 인자로 정수를 넣어주면 해당 길이만큼의 undefined를 가진 배열을 반환해주는 것으로 알고 있던 나는 indexOf 메서드를 사용해보았습니다.

1
2
var a = new Array(10);
a.indexOf(undefined); // -1

이상하다 생각되어서 되지도 않는 영어를 조합하며 구글링을 시작했지만 원하는 글은 찾아볼 수 없었고 심지어 indexOf를 사용하여 undefined를 검색하던 어떤 예제는 index값을 잘 반환하는 상황… 그 예제와 배열 생성하는 방법을 비교해보니

1
2
3
4
var arr1 = ['test', undefined, undefined]; // 해당 예제
var arr2 = new Array(10);
arr1.indexOf(undefined); // 1
arr2.indexOf(undefined); // -1

이 사실을 통해 예측할 수 있는 사실은 new Array(n)은 값이 할당되지 않고 length 속성만 지정해주지 않을까 라는 점이다. 콘솔에서 배열 내부를 열어보았다.
console array

예상했던대로 값이 없고 length만 지정된 배열이 들어오게 되었습니다.( 저 undefined x 10 이라는 것 때문에 여태 undefined가 할당된줄 알았습니다 ㅠ ) 저 a라는 배열에 요소를 추가해보겠습니다.
add element
0~9까지의 값이 할당되지 않았고 10번 인덱스부터 값이 할당 되었네요. javascript의 배열이 실상은 Array.prototype 을 상속받은 객체여서 그런게 아닐까 생각되네요.(근거 있는 답이 아니라 제 추측입니다.) 본론으로 돌아와서 값이 할당되지 않은 배열을 반환하기에 이 a라는 배열은 map이나 filter와 같은 메서드들은 제대로 동작을 하지 않게 될 것입니다.

1
2
3
4
5
var a = new Array(10);
a.map(function(v, i) {
console.log(v, i);
return 0;
});

10번의 콘솔과 0이 10개가 담긴 배열을 반환해줘야할 것 처럼 보이지만 값이 할당되지 않은 배열이므로 예상했던 동작은 하지 않습니다.

1
2
3
4
5
6
7
8
var a = new Array(10);
a.push('ha');
a.map(function(v, i) {
console.log(v, i);
return 0;
});
// ha 10
// [ 빈값 10개 , 0]

이렇게 값을 명시적으로 할당해주어야 ha라는 값과 10 이라는 인덱스를 보여주고, 10번 인덱스에 0이라는 값 가진 배열을 출력해주네요. 결론은 할당 되지 않았기 떄문에 성능테스트를 할 때 비어있는 배열을 반환해준 것 이였습니다! 의식의 흐름에 맡겨 막 쓴 글이여서 두서없고 막 삼천포로 빠지고 그랬던거 같아요…

 
 
ps - 불여우 콘솔에서는…
firefox console

이럴수가 ㅠ