4. 반응형 문법

2020. 9. 2. 19:35재주껏 하는 Front-End/Svelte (준비중)

반응형

이번 글에서는 Svelte의 반응형 문법에 대해 알아보도록 하겠다. Svelte에서 반응형이란 데이터 모델의 상태가 변경되면 이를 인지하여 DOM에서 변경될 부분을 찾아 반영하는 것을 말한다. 이벤트 리스너 등록부터 알아보자.

 

참고 - 이해를 돕기 위해 데이터 모델이 아닌 변수라는 단어로 설명하였다.

 

이벤트 연결

 

이벤트 연결은 특정 컴포넌트 또는 Form이 지원하는 이벤트에 대해 핸들러 함수를 등록하는 것으로, 해당 이벤트가 발생했을 때의 작업을 정의하는 작업이다. 기본 사용 방법은 아래와 같다.

 

<script>
  let count = 0;

  function 핸들러 함수 () {
    count++;
  }
</script>

<HTML 폼 또는 컴포넌트 on:이벤트 이름={핸들러 함수}/>

참고 - 핸들러 함수의 이름이 이벤트의 이름과 같다면 아래와 같이 사용 가능 (Ex. Click 이벤트인 경우)

<button click> ...

 

VueJS에서는 @이벤트 이름="이벤트 핸들러 함수" 형태로 사용하는데, Svelte도 비슷한 형태로 핸들러를 등록할 수 있다.

 

<script>
	let count = 0;

	function handleClick() {
		count++;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

 

실행 결과는 아래와 같다.

 

 

선언 ($ 문법 4달라)

 

구성 요소 상태의 일부분을 다른 부분에서 계산하거나 변경될 때마다 같이 갱신해줘야 하는 작업들이 필요한 경우가 있다. Svelte는 $를 이용하여 이 작업들을 수행한다. 기본 사용 방법은 아래와 같다.

 

$: 계산된 값을 저장하는 변수 = 새롭게 계산해야 할 식

$: {
  중괄호 영역에 포함된 변수의 상태가 변경될 때 실행될 식 또는 문법
}

$: 조건 {
  중괄호 영역에 포함된 변수의 상태가 변경될 때 실행될 식 또는 문법
}

$: 감시할 변수, 자바스크립트 식

 

각 사용 방법에 대한 예제를 살펴보자. 먼저 상태가 변경되면 새로운 변수에 값을 계산하여 할당하는 예제다.

 

<script>
	let count = 0;
	$: doubled = count * 2;

	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<p>{count} doubled is {doubled}</p>

 

실행 결과는 아래와 같다. VueJS의 Computed 문법과 동일하다는 것을 알 수 있다.

 

 

다음은 중괄호 영역에 포함된 변수의 상태가 업데이트될 때, 중괄호 영역의 문을 실행하는 예제다.

 

<script>
	let count = 0;

	$: {
	  console.log(`the count is ${count}`);
	}
	
	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

 

실행 결과는 아래와 같다. VueJS의 Watch와 비슷하다는 것을 알 수 있다.

 

차이점이라면 VueJS의 Watch는 지정된 특정 변수 하나만 바라보고 상태를 변경하는데, Svelte는 구문 내의 모든 변수의 상태를 체크한다는 것이다. 아래와 같이 코드를 변경해도 $의 중괄호 내부의 코드가 실행됨을 알 수 있다.

 

<script>
	let count = 0;
	let count2 = 2;

	$: {
	  console.log(`the count is ${count}`);
		console.log(`the count is ${count2}`);
	}
	
	function handleClick() {
		count += 1;
	}
	
	function handleClick2() {
		count2 += 2;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<button on:click={handleClick2}>
	Clicked {count2} {count2 === 1 ? 'time' : 'times'}
</button>

 

실행 결과는 아래와 같다. 선언 문법의 경우 우려스러운 부분이 있는데 특정 변수만 감시하고 업데이트하고 싶을 때, 어떤 변수의 상태가 변경되었는지 구분을 할 수 없기 때문에 불필요한 코드들이 추가되고 또 실행된다는 점은 분명히 차후 버전에서 개선되어야 할 점으로 생각된다.

 

 

VueJS로 치면 Computed와 Watch가 합쳐진 기능이 $인데, 분명히 Computed와 Watch의 기능과 사용 용도가 다르기 때문에 차후 버전에서는 나눠지길 기대해본다.

 

참고 - $: 감시할 변수, 자바스크립트식의 형태로 사용하면 특정 변수만 감시할 수 있다. 하지만, 자바스크립트 식만 허용되므로 사용에 제약 사항이 많은 것은 마찬가지다. 해외에서는 논리 조건문으로 이 부분을 해결하는 상황이 벌어진다.

 

마지막으로 논리 조건을 넣은 사용 방법에 대해 알아보자. 아래와 같이 $ 오른쪽에 if로 조건을 추가하여 조건에 따라 내부 자바스크립트 코드가 실행될 수 있도록 할 수 있다.

 

<script>
	let count = 0;

	$: if (count >= 10) {
		alert(`count is dangerously high!`);
		count = 9;
	}

	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

 

배열의 상태 변경

 

Svelte의 배열과 객체는 다른 UI 프레임워크와 달리 상태를 변경하는 방법이 다르다. Svelte는 객체의 할당에 의해 업데이트 트리거가 동작하기 때문이다. 기본적인 사용 방법은 아래와 같다.

 

배열 = 새로운 배열로 할당 (array.map, array.filter, array.reduce, array.concat, array.slice, [...] 사용 가능)

주의 사항

1. 원본이 바뀌지 않는 배열 메서드를 사용하여 상태를 업데이트한다.
2. 원본이 변경되는 메서드를 사용하면 Svelte에서는 이를 인식하지 못한다. (Ex. array.push(1)은 Svelte에서 동작하지 않음)

 

예제 코드를 살펴보자. Click 이벤트 핸들러인 addNumber 내부에 numbers에 [...]으로 배열을 새로 생성해서 할당하는 것을 볼 수 있다. 다른 UI 프레임워크에서는 array.push와 같이 원본이 변경되는 메서드를 사용해도 상태가 변경되었지만 Svelte는 새로운 배열을 할당해야만 한다.

 

<script>
	let numbers = [1, 2, 3, 4];

	function addNumber() {
		numbers = [...numbers, numbers.length + 1];
	}

	$: sum = numbers.reduce((t, n) => t + n, 0);
</script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>
	Add a number
</button>

 

실행 결과는 아래와 같다.

 

 

객체 상태 변경

 

객체를 새로운 변수에 할당하는 경우 업데이트가 일어나지 않을 수 있다. 아래의 예제 코드를 살펴보자. obj 객체를 할당한 copyAuthor 객체가 있고, Change Name 버튼을 클릭하면 copyAuthor 객체의 name 멤버 값이 "Change name ..."으로 변경되는 예제이다.

 

<script>
	let obj = {
		author: {
			name: 'kim1124'
		}
	};
	
	let copyAuthor = obj
	
	let isShow = false

	function showAuthorName() {
		isShow = !isShow
	}
	
	function changeAuthorName () {
		copyAuthor.author.name = 'Change name ...'
	}
</script>

{#if isShow}
<p>Author name is {obj.author.name}</p>
{/if}

<button on:click={showAuthorName}>
	{isShow ? 'Hide name' : 'Show name'}
</button>

<button on:click={changeAuthorName}>
	Change Name
</button>

 

다른 UI 프레임워크라면 Show name 버튼 클릭 시 출력되는 문자열은 "Change name..."이어야 하지만 Svelte는 새로 할당한 객체는 완전히 별개로 인식한다. 따라서 참조에 의한 업데이트가 일어나지 않으므로 출력되는 값은 변경되지 않는다. 실행 결과는 아래와 같다.

 

 

Svelte 반응형 문법 주의사항

 

Svelte에서 상태 값에 대한 업데이트의 조건으로 새로운 값의 할당으로 정의하고 있다. 따라서 배열에서는 변수에 계산된 값을 할당할 때 원본 값을 변경하는 것이 아닌 변경된 새로운 배열을 할당한 것이다.

 

객체의 경우에도 동일한데 객체 A == B라고 하더라도 Svelte에서는 객체 A의 업데이트를 따로 수행하고 B의 업데이트를 따로 수행한다. 마치 값에 의한 복사처럼 변수의 상태에 대해 DOM 업데이트를 수행하기 때문에 주의할 필요가 있다.

 

다음 글에서는 논리 문법과 Props에 대해 알아보겠다. 오늘은 여기까지~

반응형