Svelte 반응성(Reactivity)

Shy·2024년 10월 10일

Svelte

목록 보기
9/12

반응성

반응성은 데이터가 변경되었을 때 UI가 자동으로 업데이트되는 방식으로 동작한다. Svelte는 코드 작성 방식에 따라 데이터의 변화를 자동으로 감지하고, 이 변화에 맞게 DOM을 효율적으로 다시 렌더링한다.

Svelte의 반응성은 매우 직관적이며, 개발자가 별도의 복잡한 설정을 할 필요 없이 쉽게 사용할 수 있다.

기본적인 반응성

Svelte에서는 변수의 값이 변경되면 자동으로 UI가 업데이트된다. 일반적인 변수 선언과 값 변경을 통해서도 Svelte는 해당 값을 추적하고, 값이 변경되면 UI가 다시 렌더링된다.

<!-- 기본적인 반응성 -->
<script>
  let count = 0;

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

<button on:click={increment}>
  Increment
</button>

<p>Count is: {count}</p>

count변수는 +=1로 값이 변경될 때마다, 자동으로 DOM에 반영이 된다. count가 변경될 때마다 <p>태그 내의 값도 자동으로 업데이트된다.

반응형 할당($:)

반응형 할당은 변수 값을 다른 변수에 반응적으로 할당하는 방식이다. 이는 특정 값이 변경될 때 그에 따라 또 다른 변수가 자동으로 업데이트되도록 할 때 사용한다. 반응형 할당은 반응형 선언의 일종이며, 동일한 : 기호를 사용하여 작동한다.

<script>
  let count = 0;

  $: double = count * 2; // count가 변경될 때마다 double이 자동으로 갱신
</script>

<button on:click={() => count += 1}>Increment</button>
<p>Double is: {double}</p>

$: double = count * 2구문은 count값이 변경될 때마다 자동으로 double값을 다시 계산한다.

$: 반응형 선언

반응형 선언은 : 기호를 사용하여 특정 코드 블록이나 변수 할당을 반응형으로 만들어준다. 어떤 변수가 변경되었을 때 특정 코드를 다시 실행하거나 변수 값을 자동으로 업데이트할 수 있다.

<script>
  let count = 0;
  
  // 반응형 할당: count가 변하면 double도 자동으로 갱신
  $: double = count * 2;
</script>

<button on:click={() => count += 1}>
  Increment
</button>

<p>Double is: {double}</p>

아래처럼 반응형 할당을 블록 형태로도 사용할 수 있다. 이는 여러 변수나 표현식을 사용할 때 유용하다.

<script>
  let a = 1;
  let b = 2;

  $: {
    console.log(`a: ${a}, b: ${b}`);
  }
</script>

<button on:click={() => a += 1}>Increment A</button>
<button on:click={() => b += 1}>Increment B</button>

이 코드는 ab가 변경될 때마다 반응형 블록 안의 코드가 다시 실행되어 콘솔에 로그가 출력된다.

$:와 반응형 스토어

스토어는 반응형 데이터를 관리하는 또 다른 방법이다. 스토어는 Svelte 컴포넌트 간에 데이터를 공유하면서 반응형 속성을 유지한다.

<script>
  import { writable } from 'svelte/store';

  let count = writable(0);
</script>

<button on:click={() => count.update(n => n + 1)}>
  Increment
</button>

<p>Count is {$count}</p>
  • writable스토어는 count변수를 반응형으로 관리한다.
  • 스토어에 저장된 값이 변경되면 ${count}구문을 통해 UI가 자동으로 업데이트된다.

Svelte의 반응성 차별점

Svelte의 반응성은 컴파일 단계에서 처리된다. 다른 프레임워크에서는 보통 가상 DOM을 사용해 변경 사항을 감지하고 비교하지만, Svelte는 컴파일 시점에 코드에서 데이터의 변화가 발생할 부분을 미리 파악하고, 그 변롸가 발생할 때만 필요한 부분을 다시 렌더링한다. 이 방식 덕분에 Svelte는 매우 빠르고 가볍다.

비동기 데이터와 반응성

Svelte에서 비동기 작업의 결과도 반응형으로 처리할 수 있다. 예를 들어, await 구문을 사용하여 비동기 함수의 결과가 도착하면 자동으로 화면에 반영될 수 있다.

<script>
  let data;

  async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    data = await response.json();
  }

  fetchData();
</script>

{#if data}
  <p>{data.someValue}</p>
{:else}
  <p>Loading...</p>
{/if}

비동기 데이터가 도착하면 data 변수가 업데이트되고, 이에 따라 화면이 다시 렌더링된다.

반응성의 주의할 점

변수의 직접 할당만 반응성에 영향을 준다. 즉, 배열이나 객체의 항목을 직접 수정하지 않고, 새로운 배열이나 객체로 할당해야 반응형이 작동한다.

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

  function addNumber() {
    numbers = [...numbers, 4]; // 배열을 직접 수정하지 않고, 새로운 배열로 할당
  }
</script>

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

<ul>
  {#each numbers as number}
    <li>{number}</li>
  {/each}
</ul>

이벤트 수식어

이벤트 수식어(Modifiers)는 이벤트 핸들러에 추가하여 특정 동작을 수정하거나 보완할 수 있는 기능이다.

이벤트 수식어는 보통 이벤트 리스너의 기본 동작을 변경하거나, 추가적인 기능을 손쉽게 적용할 때 유용하다. 이벤트 수식어는 이벤트와 함께 콜론(:)을 사용하여 지정된다.

preventDefault

preventDefault수식어는 이벤트의 기본 동작을 막는역할을 한다.

예를 들어, 폼 제출 시 기본적으로 페이지가 리프레시되는 동작을 막을 수 있다.

<a href="https://example.com" on:click|preventDefault>
  Click me (but it won’t navigate)
</a>

이 수식어는 자바스크립트의 event.preventDefault()를 호출하는 것과 동일하다.

폼 이벤트 preventDefault

<script>
  let formData = {
    name: '',
    email: '',
  };

  function handleSubmit(event) {
    event.preventDefault(); // 폼 전송 방지
    console.log('Form submitted:', formData);

    // 클라이언트 측 유효성 검사 (예시)
    if (!formData.name || !formData.email) {
      alert('Please fill out all fields.');
      return;
    }

    // 여기서 AJAX 요청을 보낼 수 있다 (예: fetch API)
    console.log('Sending data to the server...', formData);
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <label>
    Name:
    <input type="text" bind:value={formData.name} />
  </label>
  <br />
  <label>
    Email:
    <input type="email" bind:value={formData.email} />
  </label>
  <br />
  <button type="submit">Submit</button>
</form>
  • on:submit|preventDefault: 폼 제출 시 기본 동작(새로고침)을 막는다.
  • handleSubmit 함수: 폼 제출을 처리하고 유효성 검사를 수행한다.
  • 클라이언트 측 유효성 검사: 폼 데이터가 비어 있는 경우 경고를 표시한다.

stopProgagation

stopProgagation수식어는 이벤트가 상위 요소로 전파되는 것을 방지한다. 이벤트 버블링이 일어나지 않도록하여, 하위 요소의 이벤트가 상위 요소에서 감지되지 않게 할 수 있다.

<!-- 이벤트 전파 막기 -->
<div on:click={() => alert('Parent div clicked!')}>
  <button on:click|stopPropagation={() => alert('Button clicked!')}>
    Click me
  </button>
</div>

on:click|stopProgagation은 버튼 클릭 시 이벤트가 상위 요소인 <div>로 전파되지 않도록 한다.

capture

capture수식어는 이벤트 전파 방식인 캡처링(capturing)을 사용하도록 변경한다. 기본적으로 이벤트는 버블링 단계(하위 -> 상위)에서 처리되지만, capture수식어를 사용하면 캡처링 단계(상위 -> 하위)에서 이벤트가 처리된다.

<div on:click|capture={() => alert('Parent div clicked!')}>
  <button on:click={() => alert('Button clicked!')}>
    Click me
  </button>
</div>

on:click|capture는 상위 요소가 먼저 이벤트를 처리하도록 하여, 버튼 클릭 시 Parent div clicked!가 먼저 실행되고, 그 후에 Button clicked!이 실행된다.

once

once수식어는 이벤트가 한 번만실행되도록 한다. 이벤트가 한 번 발생한 후에는 해당 리스너가 제거된다.

<button on:click|once={() => alert('Clicked!')}>
  Click me (only works once)
</button>

self

self수식어는 이벤트가 해당 요소에서 직접 발생했을 때만 실행되도록 한다. 즉, 하위 요소에서 발생한 이벤트는 무시하고, 해당 요소에서 발생한 이벤트에만 반응하게 한다.

<div on:click|self={() => alert('Div clicked!')}>
  <button>Click me (does not trigger parent div)</button>
</div>
  • on:click|selfdiv가 직접 클릭되었을 때만 실행된다.
  • 버튼을 클릭하면 이벤트는 div로 전파되지만, self 수식어가 적용되었기 때문에 Div clicked!는 호출되지 않는다.

passive

passive수식어는 이벤트 핸들러가 기본 동작을 막지 않도록 설정하는 수식어이다. 예를 들어, 스크롤 이벤트 같은 경우 성능 최적화를 위해 passive수식어를 사용하여 기본 동작을 빠르게 처리할 수 있다.

<div on:wheel|passive={() => console.log('Scrolling...')}>
  Scroll me!
</div>
  • on:wheel|passive는 스크롤 이벤트의 기본 동작(스크롤)을 방해하지 않고 로그를 출력한다.
  • passiveevent.preventDefault()를 무시하며, 성능 향상에 도움이 된다.

수식어 결합

여러 수식어를 하나의 이벤트 핸들러에 결합하여 사용할 수 있다. 수식어 |로 구분하여 결합할 수 있다.

<!-- 여러 수식어 결합 -->
<button on:click|preventDefault|stopPropagation|once={() => alert('Clicked!')}>
  Click me
</button>
  • 이 버튼은 클릭 시 기본 동작을 막고(preventDefault), 이벤트가 전파되지 않으며(stopProgagation), 한 번만 이벤트가 실행된다(once).

인라인 함수

인라인 함수는 이벤트 핸들러에서 자주 사용되는 패턴이다.

인라인 함수는 이벤트 핸들러나 특정 조건에서 함수 실행을 즉석에서 정의하는 방식이다. 그러나 인라인 함수를 남용하면 성능 문제가 발생할 수 있다. 특히 함수가 매번 재생성되면서 불필요한 메모리 사용을 초래할 수 있다.

<script>
  let count = 0;
</script>

<button on:click={() => count += 1}>
  Increment
</button>

<p>{count}</p>

인라인 함수의 단점

인라인 함수는 간단한 로직을 처리하는 경우엔 문제가 없지만, 복잡한 로직이 포함되거나 많은 렌더링이 필요한 컴포넌트에서는 성능 저하를 유발할 수 있다. 그 이유는 Svelte는 매 렌더링 시마다 인라인 함수를 다시 생성하기 때문이다.

<!-- 비효울적인 인라인 함수 -->
<script>
  let count = 0;

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

<!-- 매번 새로운 인라인 함수가 생성됨 -->
<button on:click={() => increment()}>
  Increment
</button>
  • 버튼을 클릭할 때마다 함수가 매번 다시 생성된다.
  • 이 방식은 코드가 간단한 경우에는 큰 문제가 없지만, 컴포넌트가 자주 없데이트되거나 복잡한 로직이 포함될 경우 성능이 저하될 수 있다.

성능 최적화를 위한 외부 함수 사용

성능을 최적화하려면 인라인 함수 대신 함수를 외부로 분리하여 매번 함수가 재생성되지 않도록 할 수 있다.

<script>
  let count = 0;

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

<!-- 외부 함수 사용 -->
<button on:click={increment}>
  Increment
</button>
  • 외부로 분리된 함수는 매 렌더링마다 새롭게 생성되지 않고, 동일한 함수 참조를 사용하므로 성능이 최적화된다.
  • 이렇게 함으로써 불필요한 함수 생성과 메모리 사용을 줄일 수 있다.

인라인 함수와 bind

Svelte에서는 bind를 사용하여 간단한 데이터 바인딩을 할 수 있는데, bind는 인라인 함수의 대안이 될 수 있다.

<script>
  let value = '';
</script>

<input bind:value={value} />

<p>{value}</p>
  • bind:value는 인라인 함수를 사용하지 않고도 값이 변경될 때마다 value가 자동으로 업데이트된다.
  • 이를 통해 코드가 간결해지고 성능도 최적화된다.
profile
신입사원...

0개의 댓글