6. 이벤트 처리

2020. 9. 8. 15:57재주껏 하는 Front-End/Svelte (준비중)

반응형

이번 글에서는 Svelte의 이벤트 처리에 대해 알아보자.

 

이벤트 등록

 

Svelte는 모든 구성 요소에 이벤트 설정이 가능하며, 아래의 방법으로 이벤트 리스너를 등록할 수 있다.

 

<요소 on:이벤트 이름={이벤트 리스너} />
<요소 on:이벤트 이름={이벤트 변수 => 이벤트 리스너 동작} />

 

간단한 사용 예제를 살펴보자. 하나의 <div> 태그를 추가하고 <div> 태그 내에서 마우스를 움직이면 마우스 커서 좌표를 출력하는 코드이다.

 

<script>
	let m = { x: 0, y: 0 };

	function handleMousemove(event) {
		m.x = event.clientX;
		m.y = event.clientY;
	}
</script>

<style>
	div { width: 100%; height: 100%; }
</style>

<div on:mousemove={handleMousemove}>
	The mouse position is {m.x} x {m.y}
</div>

 

실행 결과는 아래와 같다. 어렵지 않게 마우스 이벤트와 이벤트 리스너를 등록하여 원하는 기능을 구현할 수 있다.

 

 

간단한 기능이라면 아래와 같이 이벤트 핸들러를 ES6의 화살표 함수를 통하여 구현할 수도 있다.

 

<script>
	let m = { x: 0, y: 0 };

	function handleMousemove(event) {
		m.x = event.clientX;
		m.y = event.clientY;
	}
</script>

<style>
	div { width: 100%; height: 100%; }
</style>

<div on:mousemove={e=> m = {x: e.clientX, y: e.clientY}}>
	The mouse position is {m.x} x {m.y}
</div>

 

실행 결과는 위와 동일하다.

 

이벤트 한정자

 

Svelte는 이벤트 한정자를 통하여 특정 요소의 기본 동작이나 부모 요소의 이벤트 대응에 관련된 동작들을 제어할 수 있다. 아래는 Svelte에서 지원하는 이벤트 한정자 목록이다.

 

한정자 설명
preventDefault 현재 이벤트의 기본 동작을 차단한다.
stopPropagation 현재 이벤트가 상위로 전파되지 않도록 차단한다.
passive 터치/휠 이벤트에서 스크롤 성능을 향상시킨다. (Svelte가 알아서 처리한다.)
capture 이벤트 버블링 단계가 아닌 캡처 단계에서 핸들러를 실행한다.
once 이벤트가 단 한번만 핸들러를 실행한다.
self 이벤트를 연결한 요소와 event.target이 같을때만 핸들러를 실행한다.

 

아래는 이벤트 한정자를 사용하는 기본 방법이다.

 

<요소 on:이벤트|이벤트 한정자 1|이벤트 한정자 2|...={이벤트 핸들러} />

 

아래의 예제는 단 한 번만 클릭 이벤트가 발생하는 아주 간단한 예제 코드다. 코드를 실행하면 클릭 이벤트가 단 한 번만 발생하는 것을 확인할 수 있다.

 

<script>
	function handleClick() {
		alert('clicked')
	}
</script>

<button on:click|once={handleClick}>
	Click me
</button>

 

컴포넌트 이벤트

 

Svelte는 하위 컴포넌트에 데이터를 전달하기 위해 Props를 사용한다. 일반적으로 웹 애플리케이션의 데이터 흐름도는 상위에서 하위로 방향성을 가지고 있다. 하지만 하위의 컴포넌트가 상위에게 특정 값을 전달하거나 작업을 요청해야 하는 경우가 있다.

 

하위 컴포넌트에서 상위 컴포넌트로 값을 전달하기 위해서는 양방향 바인딩을 사용하거나 상위 컴포넌트에 이벤트를 연결하고 하위 컴포넌트에서 발생시키는 방법으로 해결할 수 있다. 기본적인 사용 방법은 아래와 같다.

 

1. 상위 컴포넌트에서

<script>
  function 상위 컴포넌트 이벤트 핸들러 (event) {
    event.detail로 하위 컴포넌트가 전달한 값 확인
  }
</script>

<요소 on:이벤트 이름={상위 컴포넌트 이벤트 핸들러} />
2. 하위 컴포넌트에서

<script>
  import { createEventDispatcher } from 'svelte'

  const dispatcher = createEventDispatcher ()

  function 하위 컴포넌트 이벤트 핸들러 () {
    dispatcher('상위 컴포넌트 이벤트 이름', 전달할 값)
  }
</script>

<요소 on:이벤트 이름={하위 컴포넌트 이벤트 핸들러} />

 

꽤 복잡해 보인다. 예제 코드에서 한번 확인해보자.

 

// Outer.svelte


<script>
	import Inner from './Inner.svelte';

	function handleMessage(event) {
		alert(event.detail.text);
	}
</script>

<Inner on:message={handleMessage}/>


// Inner.svelte


<script>
	// setup code goes here
	import { createEventDispatcher } from 'svelte'
	
	const dispatch = createEventDispatcher()

	function sayHello() {
		dispatch('message', {
			text: 'hello!!'
		})
	}
</script>

<button on:click={sayHello}>
	Click to say hello
</button>

 

상위 컴포넌트에 하위 컴포넌트 Inner를 추가하고 message라는 이벤트를 연결한다. 하위 컴포넌트 Inner에는 버튼 요소가 있고, 버튼을 클릭 시 eventDispatcher에 의해 상위 컴포넌트에 message라는 이벤트를 요청한다. 하위 컴포넌트에서 전달한 값은 상위 컴포넌트의 이벤트 핸들러 파라미터인 event.detail에서 확인할 수 있다. 이벤트 순서를 정리하면 아래와 같다.

 

1. 하위 컴포넌트 Inner에서 message 이벤트 요청
2. 상위 컴포넌트 Outer에서 message 이벤트 청취 중 하위 컴포넌트가 요청한 이벤트 확인
3. 상위 컴포넌트 Outer 핸들러의 파라미터로 하위 컴포넌트 Inner가 전달한 {text: 'hello!!'} 객체를 전달 받음.
4. Alert으로 전달받은 데이터 출력

 

실행 결과는 아래와 같다.

 

 

컴포넌트 이벤트 전달

 

하위 요소에서 발생한 이벤트가 상위 요소로 타고 올라가는 특징을 이벤트 버블링이라고 한다. 이벤트 버블링으로 인해 하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트로 전달할 때 개발자가 일일이 이벤트를 연결하고 발생시킬 필요가 없다. 하지만 이벤트 버블링은 DOM 이벤트에만 해당된다. Svelte 컴포넌트 이벤트는 이벤트 버블링이 지원되지 않기 때문에 개발자가 일일이 이벤트를 연결하고 발생시켜야 한다.

 

위에서 본 것과 같이 상위 컴포넌트로 전달하는 dispatcher를 전달하는 길목에 위치한 컴포넌트마다 설치하면 최하위 컴포넌트에서 발생한 이벤트를 상위 컴포넌트로 전달할 수 있다. 예제 코드를 살펴보자.

 

// App.svelte (최상위 컴포넌트)

<script>
	import Outer from './Outer.svelte';

	function handleMessage(event) {
		alert(event.detail.text);
	}
</script>

<Outer on:message={handleMessage}/>


// Outer.svelte (중간 컴포넌트)


<script>
	import Inner from './Inner.svelte';
	import { createEventDispatcher } from 'svelte'
	
	const dispatcher = createEventDispatcher()
	
	function forward (event) {
		dispatcher('message', event.detail)
	}
</script>

<Inner on:message={forward}/>


// Inner.svelte (최하위 컴포넌트)


<script>
	import { createEventDispatcher } from 'svelte';

	const dispatch = createEventDispatcher();

	function sayHello() {
		dispatch('message', {
			text: 'Hello!'
		});
	}
</script>

<button on:click={sayHello}>
	Click to say hello
</button>

 

예제 코드를 실행하면 최하위 컴포넌트인 Inner에서 전달한 값이 최상위 컴포넌트 이벤트 핸들러에서 정상적으로 전달되었음을 확인할 수 있다.

 

 

하지만 매번 이렇게 이벤트를 끌어올려야 된다고 생각하니 너무 불편하다. 다행히도 Svelte에서는 다중 컴포넌트 구조로 되어있을 때, 이벤트 전달에 대한 간단한 문법을 제공한다. 위에서 Outer.svelte 코드를 아래와 같이 변경한다.

 

<script>
	import Inner from './Inner.svelte';
</script>

<Inner on:message/>

 

실행해보면 값이 제대로 전달되었음을 알 수 있다. 핸들러가 지정되지 않은 이벤트의 경우 Svelte는 요청한 이벤트에 해당되는 모든 요소에 값을 전달한다. 위의 코드는 상위 컴포넌트에 message라는 이름의 이벤트를 청취하는 모든 요소에 값을 전달한다는 의미이다. 위와 같은 문법을 통해 가독성 높은 코드를 작성할 수 있다.

 

DOM 이벤트 전달

 

위에서 본 것과 마찬가지로 DOM 이벤트도 간단하게 전달할 수 있다. 하위 컴포넌트의 버튼 클릭 이벤트에 대한 동작 구현을 상위 컴포넌트에서 한다고 가정해보자.

 

// App.svelte (상위 컴포넌트)


<script>
	import CustomButton from './CustomButton.svelte';

	function handleClick() {
		alert('clicked');
	}
</script>

<CustomButton on:click={handleClick}/>


// CustomButton.svelte (하위 컴포넌트)


<style>
	button {
		height: 4rem;
		width: 8rem;
		background-color: #aaa;
		border-color: #f1c40f;
		color: #f1c40f;
		font-size: 1.25rem;
		background-image: linear-gradient(45deg, #f1c40f 50%, transparent 50%);
		background-position: 100%;
		background-size: 400%;
		transition: background 300ms ease-in-out;
	}
	button:hover {
		background-position: 0;
		color: #aaa;
	}
</style>

<button on:click>
	Click me
</button>

 

위와 같이 하위 컴포넌트의 이벤트 핸들러를 따로 지정하지 않아도 상위 컴포넌트에서 click 이벤트를 처리할 수 있다. 단, 상위 컴포넌트의 청취 이벤트 이름이 하위 컴포넌트에서 전달하는 click과 동일해야 한다. 너무 당연한 건가?

 

이렇게 해서 Svelte의 이벤트 처리에 대해 알아보았다. 다음 글에서는 가장 활용을 많이 할 것으로 예상되는 바인딩에 대해서 알아보도록 하겠다. 오늘은 여기까지 ~

반응형

'재주껏 하는 Front-End > Svelte (준비중)' 카테고리의 다른 글

8. 컴포넌트 생명 주기  (0) 2020.09.22
7. 바인딩  (0) 2020.09.14
5. 논리 문법 / Props  (0) 2020.09.04
4. 반응형 문법  (0) 2020.09.02
3. 기본 문법  (0) 2020.09.01