Svelte 반응성 시스템

Svelte 반응성 이해하기

assignments

Svelte는 “이벤트에 대한 응답”을 통해 DOM을 애플리케이션의 상태와 동기화시킬 수 있는 강력한 반응성 시스템을 제공하는데, 특정 요소에 이벤트 할당이 필요한 경우 이벤트 핸들러를 할당해 사용할 수 있습니다.

click 이벤트 연결
<script>
    let count = 0;

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

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

위 코드의 incrementCount 함수 내에서는 count 값을 변경하기만 하면 되는데, 나머지 DOM을 업데이트하는 작업은 Svelte의 내부 코드가 처리해주기 때문입니다.

declarations

Svelte는 컴포넌트의 상태가 변경되면 DOM을 자동으로 업데이트하는데, 컴포넌트 상태의 일부는 다른 부분에서 계산된 후 변경될 때마다 다시 계산되어야 하는 경우가 있습니다. 이런 경우에는 선언을 통해 계산된 반응 값을 참조할 수 있습니다.

let count = 0;
$: doubled = count * 2;

위 코드의 $: doubled는 Svelte에서 ‘참조된 값이 변경될 때마다 이 코드를 다시 실행’하도록 해석되는 유효한 Javascript 코드로, 마크업에서 해당 값을 참조할 때는 선언된 이름인 doubled를 사용합니다.

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

물론 $: doubled 대신 마크업에 직접 {count * 2}과 같은 표현식을 써도 되지만, 자주 참조되는 값이라면 미리 계산된 반응 값을 선언해 두고 이 반응 값을 사용하면 더 유용하고 편리하게 사용할 수 있습니다.

선언을 이용한 이벤트 연결
<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>

statements

$:은 반응 값을 선언하는 것 뿐만 아니라, 반응적으로 임의의 명령문을 실행하는 문장을 선언할 수도 있는데, 다음의 코드는 count 값이 변경될 때마다 로그를 출력하는 명령 문장을 선언한 예제입니다.

명령 문장 선언
$: console.log('the count is ' + count);

또 다음과 같이 중괄호{}를 사용해 블록으로 지정하거나, if 문으로 그룹화를 시킬 수도 있습니다.

블록 그룹으로 선언한 명령 문장
$: {
    console.log('the count is ' + count);
    alert('I SAID THE COUNT IS ' + count);
}
if 조건문을 선언한 명령 문장
$: if (count >= 10) {
    alert('count is dangerously high!');
    count = 9;
}

다음의 코드는 명령 문장을 사용한 예제의 전체 코드입니다.

명령문장 사용 예제
<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의 반응성은 할당에 의해 트리거되기 때문에 push 및 splice와 같은 배열 메서드를 사용하는 경우에는 자동으로 업데이트가 되지 않습니다.

자동 업데이트가 되지 않는 비할당 방식
function addNumber() {
    numbers.push(numbers.length + 1);
    numbers = numbers;
}

위 코드와 같은 경우에는 push 메서드로 배열을 업데이트하더라도 할당이 이루어지지 않기 때문에 자동 업데이트가 실행되지 않는데, 중복되는 할당을 추가하여 자동 업데이트를 강제로 실행시킬 수도 있지만 더 좋은 방법은 다음과 같이 배열의 스프레드 연산자를 이용하는 겁니다.

스프레드 연산자를 이용한 배열 할당
function addNumber() {
    numbers = [...numbers, numbers.length + 1];
}

물론 pop, shift, unshift, splice 메서드도 위와 유사한 패턴으로 대체할 수 있는데, obj.foo += 1 또는 array[i] = x와 같이 배열과 객체의 속성에 대한 할당도 값 자체에 대한 할당과 동일한 방식으로 작동합니다.

요소에 대한 할당
function addNumber() {
    numbers[numbers.length] = numbers.length + 1;
}

여기서, 요소에 대한 할당을 하는 경우 업데이트된 변수의 이름은 할당의 왼쪽에 나타나야 하는데, 다음과 같이 foo라는 변수에 객체의 요소를 할당한 후 해당 변수의 객체를 통해 업데이트를 하면 Svelte의 반응성은 작동하지 않습니다.

반응성이 동작하지 않는 재할당 코드
const foo = obj.foo;
foo.bar = 'baz';

위와 같은 경우에는 obj.foo.bar에서 반응성이 트리거되지 않기 때문에, obj = obj 처럼 재할당을 해주는 등의 추가적인 작업이 필요합니다.

스프레드 연산자를 이용한 자동 업데이트 구현
<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>

답글 남기기