[Vue.js] Event 처리

Vue.js 2018.12.17 21:18

Vue.js Event 처리

동적인 화면을 구성할때 다이나믹한 UI는 HTML 요소에서 발행하는 이벤츠 처리를 통해서 구현되는경우가 많다. HTML 문서에서 발생하는 이벤트는 다양하다 키보드를 누를 때 발생하는 keyup, keypress, keydown 이벤트 마우스 클릭할 때 발생하는 click, doubleclick 이벤트, 마우스를 움직일 때 발생하는 mousemove 이벤트 등이 있다. 




1. 인라인 이벤트 처리

Vue.js에서는 v-on 디렉티브를 이용해서 처리할 수있다. 가장 많이 쓰이는 Click 이벤트 처리를 예제로 만들어 보았다.

<html>

<head>
<meta charset="utf-8">
<title>hello vue.js</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/
3.3.7/css/bootstrap.min.css">
</head>
<style>
.layout1 {
margin: 30px 30px 30px 30px;
}
</style>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<body>
<div id="example" class="container layout1">
<p>
<input tpye="text" v-model="amount" class="form-control" /></p>
</p>
<p>
<button id="deposit" v-on:click="balance += parseInt(amount)"
class="btn btn-primary">예금</button>
<button id="deposit" v-on:click="balance -= parseInt(amount)"
class="btn btn-primary">인출</button>
</p>
<h3>계좌 잔고 : {{balance}}</h3>
</div>
<script tpye="text/javascript">
var vm = new Vue({
el: '#example',
data: {
balance: 0,
amount: 0
}
})
</script>
</body>
</html>

예제는 간단하지만 디자인을 입히기 위해 bootstrap을 사용했다. amount 데이터 속성을 v-model로 양방향 바인딩하였고 사용자가 입력한 값은 amount에 즉시 반영된다. v-on:click 디렉티브를 이용해 클릭 이벤트 처리를 숭행한다. 예금 버튼을 클릭할때는 balance에 amount를 누적하고 인출 버튼을 클릭할 때는 amount를 balance에서 차감한다.


v-on 디렉티브는 @로 줄여 쓸수도 있다. v-on:click 대신에 @click으로 변경해도 정상적으로 동작한다.


v-on:click="balance += parseInt(amount)"


위의 코드는 실행 코드를 직접 연결하고 있다. 이와 같은 코드는 약간의 문제를 안고있는데 예를 들어 예금하거나 인출하려는 금액은 마이너스 값을 허용하지 않고, 인출 금액은 계좌 잔고보다 많아서는 안된다면 이 처리를 위한 코드를 인라인 이벤트에 모두 작성하기 어렵다. 그렇기 때문에 현실적인 이벤츠 처리 방법이라 하기 힘들다.




2. 이벤트 핸들러 메서드

앞의 예제에 이어 Vue 인스턴스에 등록한 메서드를 이벤트 처리 함수로 연결해보겠다. 

<html>

<head>
<meta charset="utf-8">
<title>hello vue.js</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/
3.3.7/css/bootstrap.min.css">
</head>
<style>
.layout1 {
margin: 30px 30px 30px 30px;
}
</style>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>

<body>
<div id="example" class="container layout1">
<p>
<input tpye="text" v-model="amount" class="form-control" /></p>
</p>
<p>
<button id="deposit" v-on:click="deposit" class="btn btn-primary">예금</button>
<button id="deposit" v-on:click="withdraw" class="btn btn-primary">인출</button>
</p>
<h3>계좌 잔고 : {{balance}}</h3>
</div>
<script tpye="text/javascript">
var vm = new Vue({
el: '#example',
data: {
balance: 0,
amount: 0
},
methods: {
deposit: function(e) {
var amt = parseInt(this.amount);
if (amt <= 0) {
alert("0보다 큰 값을 예금해야한다. ")
} else {
this.balance += amt;
}
},
withdraw: function(e) {
var amt = parseInt(this.amount);
if (amt <= 0) {
alert("0보다 큰 값을 인출할 수 있다. ")
} else if (amt > this.balance) {
alert("잔고보다 많은 금액을 인출할 수 있다. ")
} else {
this.balance += amt;
}
}
}
})
</script>
</body>

</html>


deposit, withdraw 메서드를 작성하였고 메서드 내부에는 앞서 이야기했던 금액과 계좌 잔고에 따른 유효성 검사 기능을 코드로 작성했다. 


이와 같이 복잡한 기능은 메서드를 미리 작성해두고 v-on 디렉티브로 참조해서 이벤트 처리를 수행한다.





3. 이벤트 객체

이벤트를 처리하는 메서드는 첫 번째 파라미터로 이벤트 객체를 전달받는다. deposit 함수를 보면 function(e) {...}와 같이 이벤트 객체를 전달받고 있고 이 이벤트 객체를 통해서 이용할 수 있는 정보가 많다.


Vue.js의 이벤트 객체는 W3C 표준 HTML DOM Event 모델을 그대로 따르면서 추가적인 속성을 제공한다. 그렇기 때문에 기존의 순수 자바스크립트에서 사용하던 이벤트 객체의 정보를  거의 대부분 그대로 이용할 수있다. 


이벤트 객체의 주요 공통 속성

속성명 

설명 

 target

 이벤트가 발생한 HTML 요소를 리턴함 

 currentTarget

 이벤트 리스너가 이벤트를 발생시키는 HTML 요소를 리턴함

 path

 배열값. 이벤트 발생 HTML 요소로부터 documnet, window 객체로까지 거슬러 올라가는 경로를 나타냄

 bubbles

현재의 이벤트가 버블링을 일으키는 이벤트인지 여부를 리턴함 

 cancelable

기본 이벤트를 발지할 수 잇는지 여부를 리턴함 

 defaultPrevented

기본 이벤트가 발지되었는지 여부를 나타냄 

 eventPhase

이벤트 흐름의 단계를 나타냄

1 : 포착

2 : 이벤트 발생

3 : 버블링 

 srcElement

 IE에서 사용되던 속성으로 target과 동일한 속성



키보드 이벤트 관련 속성

 속성명

설명 

 alrtKey

 ALT 키가 눌러졌는지 여부를 나타냄(true/false)

 shiftKey 

 Shift 키가 눌러졌는지 여부를 나타냄(true/false) 

 ctrlKey

 CTRL 키가 눌러졌는지 여부를 나타냄(true/false) 

 metakey

 메타키가 눌러져있는지 여부를 나타냄 window key or command key 

 key

 이벤트에 의해 나타나는 키의 값을 리턴함 대소문자 구분함 

 code

 이벤트를 발생시킨 키의 코득밧을 리턴함 

 keyCode

 이벤트를 발생시킨 키보드의 고유 키보드

 charCode

 keypress 이벤트가 발생될 때 Unicode 캐릭터 코드를 리턴함 

 location

 디바이스에서의 키 위칫값, 일반 키보드는 이 밧이 모두 0 


이벤트 객체 주요 메서드

메서드 명 

설명 

 preventDefault()

 기본 이벤트의 자동 실행을 중지시킴 

 stopPropagation() 

 이벤트의 전파를 막음 





4. 기본 이벤트


몇몇 HTML 요소는 개발자가 이벤트를 연결하지 않았음에도 뭔가 실해되는 기능을 가지고있는 것들이 있따. 예를 들어 <a> 요소는 클릭 이벤트 처리를 하지 않았음에도 클릭하면 href 특성에 정의된 경로로 화면을 이동시킨다. HTML 문서나 요소에 어떤 기능을 실행하도록 이미 정의되어 있는 이벤트를 기본 이벤트라고 부른다.

ex) <a>, <form>, <input>

이와 같이 평상시에 당연하다고 여기는 것 중 으이외로 많은 부분이 기본 이벤트로 처리되고 있다. 하지만 미리 정의 된 기능을 실행할 수 있기 때문에 편리하지만 걸림돌이 되기도 한다. 따라서 기본 이벤트 실행을 중지시킬 수 있는 방법을 알아 둘 필요가 있다.

<head>
<meta charset="utf-8">
<title>hello vue.js</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap
/3.3.7/css/bootstrap.min.css">
</head>
<style>
html,
body {
margin: 0;
padding: 0;
}
#example {
height: 98vh;
min-height: 100%;
padding: 5px;
}
</style>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>

<body>
<div id="example" v-on:contextmenu="ctxStop">
<a href="https://facebook.com" @click="confirmFB">페이스북</a>
</div>
<script tpye="text/javascript">
var vm = new Vue({
el: '#example',
methods: {
ctxStop: function(e) {
e.preventDefault();
},
confirmFB: function(e) {
if (!confirm("페이스북으로 이동할까요?")) {
e.preventDefault();
}
}
}
})
</script>
</body>

내부에서는 기본 이벤트 실행을 막기 위해서 이벤트 객체가 제공하는 preventDefault()메서드를 호출한다. confirmFB 메서드는 사용자에게 확인을 받기 위해서 confirm() 함수를 사용한다. 사용자가 확인 버튼이 아닌 취소 버튼을 클릭하면 preventDefault()메서드가 호출된어 기본 이벤트의 실행을 중지시킨다.


contextmenu 이벤트가 발생할 때 호출하는 ctxStop 메서드는 무조건 preventDefault() 메서드를 호출한다. 이로서 내장 컨텍스트 메뉴는 나타나지 않게 된다. 최근에 이 기본 이벤트의 실행을 막는 주된 이유는 브라우져 화면에서 오른쪽 마우스버튼을 클릭할 때 내장 컨텍스트 메뉴 대신 개발자가 직접 작성한 메뉴를 나타내기 위한 경우가 많다. 




5. 이벤트 전파와 버블링


HTML 문서의 이벤트 처리는 3단계를 거친다. 1단계는 무선 내의 요소에서 이벤트가 발생했을 때 HTML 문서의 밖에서부터 이벤트를 발생시킨 HTML 요소까지 포착해 들어가는 이벤트 포착단계 이다.

2단계는 이벤트를 발생시킨 요소에 다다르면 요소의 이벤트에 연결된 함수를 직접 호출시키는 이벤트 발생 단계이다. 마지막 3단계는 이벤트가 발생한 요소로부터 상위 요소로 거슬러 올라가면서 동일한 이벤트를 호출시키는 버블링 단계이다. 일반적으로 2단계, 3단계에서 연결된 이벤트 함수가 호출 된다.


(요런 느낌?)



<style>
#outer {
width: 200px;
height: 200px;
border: solid 2px black;
background-color: aqua;
position: absolute;
top: 100px;
left: 50px;
padding: 10px 10px 10px 10px
}
#inner {
width: 100px;
height: 100px;
border: solid 2px black;
background-color: yellow;
}
</style>
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>

<body>
<div id="example">
<div id="outer" @click="outerClick">
<div id="inner" @click="innerClick"></div>
</div>
</div>
<script tpye="text/javascript">
var vm = new Vue({
el: '#example',
methods: {
outerClick: function(e) {
console.log("### OUTER CLICK")
console.log("Event Phase : ", e.eventPhase)
console.log("Current Target : ", e.currentTarget)
console.log("Target : ", e.target)
},
innerClick: function(e) {
console.log("### INNER CLICK")
console.log("Event Phase : ", e.eventPhase)
console.log("Current Target : ", e.currentTarget)
console.log("Target : ", e.target)
}
}
})
</script>
</body>



위의 예제를 실행후 개발자도구에서 #inner 요소를 클릭(노란색 박스)하면 outer click 이벤트가 같이 실행된 것을 알 수 있다. 일반적으로는 이벤트 버블링은 막아야 할 작업니다. #inner를 클릭했을 때 상위요소로의 이벤트 전파(Propagation)를 막아야 한다. 이를 위해 이벤트 객체의 stopPropagation() 메서드를 호출한다. 

outerClick: function(e) {
console.log("### OUTER CLICK")
console.log("Event Phase : ", e.eventPhase)
console.log("Current Target : ", e.currentTarget)
console.log("Target : ", e.target)
e.stopPropagation();
},

위의  e.stopPropagation(); 이 소스를 넣으면 더 이상 버블링이 일어나지 않는 것을 확인 할 수 있다. 이렇게 수정 할 수도 있지만 이 부분도 이벤트 수식어로 대체할 수 있다.


1) .stop : 이벤트 전파를 중단

2) .capture : CAPTUREING_PHASE 단계에서만 이벤트가 발생

3) .self : RAISING_PHASE 단게일 때만 이벤트 발생



<div id="example">
<div id="outer" @click.stop="outerClick">
<div id="inner" @click.stop="innerClick"></div>
</div>
</div>






6. 이벤트 수식어

.prevent, .stop, .self 와 같은 이벤트 수식어를 살펴보았지만 이 밖에도 다양한 이벤트 수식어가 제공된다.


1) once 수식어

once 수식어는 한 번만 이벤트를 발생시킨다. 

ex) v-on:click.once


2) 키코드 수식어

키보드 관련 이벤트를 처리할 때 사용할 수 있는 수식어이다. 키보드의 키를 누를 때 고유의 키코드 값을 가질 때만 이벤트를 발생 시킬 수있다. 

'Vue.js' 카테고리의 다른 글

[Vue.js] Vue CLI(GUI)  (0) 2018.12.18
[Vue.js] Component  (0) 2018.12.18
[Vue.js] Event 처리  (0) 2018.12.17
[Vue.js] Vue instance (뷰 인스턴스)  (0) 2018.12.16
[Vue.js] computed Property (계산형 속성)  (0) 2018.12.15
[Vue.js] 기본 디렉티브  (0) 2018.12.15
Posted by 루우지