005: 고급 반응성
반응형 스토어 생성 및 사용
Svelte 5에서는 $state와 함께 커스텀 반응형 스토어를 만들 수 있습니다. 이를 통해 복잡한 상태 관리 로직을 캡슐화하고 재사용할 수 있습니다.
현재 카운트: 5
컴포넌트 코드:
<script lang="ts"> function createCountStore(initialValue = 0) { let count = $state(initialValue); return { get value() { return count; }, increment: () => { count += 1; }, decrement: () => { count -= 1; }, reset: () => { count = initialValue; } }; } const counter = createCountStore(5); </script> <div class="p-4 border border-orange-500 rounded"> <p class="text-gray-200">현재 카운트: {counter.value}</p> <div class="flex space-x-2"> <button onclick={() => counter.increment()} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500" > 증가 </button> <button onclick={() => counter.decrement()} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500" > 감소 </button> <button onclick={() => counter.reset()} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500" > 리셋 </button> </div> </div>
반응성 최적화 기법
대규모 애플리케이션에서는 반응성 시스템을 효율적으로 사용하는 것이 중요합니다. 불필요한 재계산을 피하고 성능을 최적화하는 다양한 기법을 살펴보겠습니다.
총 아이템: 1000
총 값: 50955.59
필터링된 아이템: 1000
- 아이템 0 - 71.40
- 아이템 1 - 93.91
- 아이템 2 - 51.24
- 아이템 3 - 63.65
- 아이템 4 - 66.15
- 아이템 5 - 32.06
- 아이템 6 - 75.25
- 아이템 7 - 45.15
- 아이템 8 - 55.20
- 아이템 9 - 11.11
페이지 1 / 100
컴포넌트 코드:
<script lang="ts"> let items = $state(Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `아이템 ${i}`, value: Math.random() * 100 }))); let searchTerm = $state(''); let currentPage = $state(1); const itemsPerPage = 10; let itemCount = $derived(items.length); let totalValue = $derived(items.reduce((sum, item) => sum + item.value, 0)); let filteredItems = $derived( searchTerm ? items.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase())) : items ); let paginatedItems = $derived( filteredItems.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage) ); let totalPages = $derived(Math.ceil(filteredItems.length / itemsPerPage)); </script> <div class="p-4 border border-orange-500 rounded"> <input type="text" bind:value={searchTerm} placeholder="아이템 검색..." class="w-full p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> <div class="my-4 text-gray-200"> <p>총 아이템: {itemCount}</p> <p>총 값: {totalValue.toFixed(2)}</p> <p>필터링된 아이템: {filteredItems.length}</p> </div> <ul class="space-y-2"> {#each paginatedItems as item} <li class="text-gray-200"> {item.name} - {item.value.toFixed(2)} </li> {/each} </ul> <div class="flex justify-between items-center mt-4"> <button onclick={prevPage} disabled={currentPage === 1} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500 disabled:opacity-50" > 이전 </button> <span class="text-gray-200">페이지 {currentPage} / {totalPages}</span> <button onclick={nextPage} disabled={currentPage === totalPages} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500 disabled:opacity-50" > 다음 </button> </div> </div>
Push-Pull 반응성 모델
Svelte 5의 반응성 시스템은 Push와 Pull 모델을 모두 지원합니다. 각 모델의 특징과 적절한 사용 사례를 이해하는 것이 중요합니다.
카운트 (Push): 0
2배 값 (Pull): 0
짝수 여부 (Pull): 짝수
마지막 업데이트: 2:25:51 AM
컴포넌트 코드:
<script lang="ts"> let count = $state(0); let lastUpdate = $state(new Date().toLocaleTimeString()); let double = $derived(count * 2); let isEven = $derived(count % 2 === 0); function increment() { count++; lastUpdate = new Date().toLocaleTimeString(); } </script> <div class="p-4 border border-orange-500 rounded"> <div class="text-gray-200 space-y-2"> <p>카운트 (Push): {count}</p> <p>2배 값 (Pull): {double}</p> <p>짝수 여부 (Pull): {isEven ? '짝수' : '홀수'}</p> <p>마지막 업데이트: {lastUpdate}</p> </div> <button onclick={increment} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500 mt-4" > 증가 (Push) </button> </div>
의존성 추적 메커니즘
Svelte는 자동으로 반응형 의존성을 추적합니다. 이 메커니즘을 이해하면 더 효율적인 반응형 코드를 작성할 수 있습니다.
전체 이름: 길동홍
인사말: 안녕하세요, 길동홍님!
프로필: 길동홍 (30세)
컴포넌트 코드:
<script lang="ts"> let firstName = $state('홍'); let lastName = $state('길동'); let age = $state(30); let fullName = $derived(`${lastName}${firstName}`); let greeting = $derived(`안녕하세요, ${fullName}님!`); let profile = $derived(`${fullName} (${age}세)`); $effect(() => { console.log(`이름이 ${fullName}으로 변경되었습니다.`); }); $effect(() => { console.log(`나이가 ${age}세로 변경되었습니다.`); }); </script> <div class="p-4 border border-orange-500 rounded"> <div class="mb-6 space-y-4"> <div> <label for="lastName" class="block text-gray-200 mb-2">성:</label> <input id="lastName" type="text" bind:value={lastName} class="w-full p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> </div> <div> <label for="firstName" class="block text-gray-200 mb-2">이름:</label> <input id="firstName" type="text" bind:value={firstName} class="w-full p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> </div> <div> <label for="age" class="block text-gray-200 mb-2">나이:</label> <input id="age" type="number" bind:value={age} class="w-full p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> </div> </div> <div class="text-gray-200 space-y-2"> <p>전체 이름: {fullName}</p> <p>인사말: {greeting}</p> <p>프로필: {profile}</p> </div> </div>
클래스와 함께 $state 사용
Svelte 5에서는 클래스 필드에서도 $state를 사용할 수 있습니다. 이를 통해 객체 지향 프로그래밍과 반응형 프로그래밍을 결합할 수 있습니다.
컴포넌트 코드:
<script lang="ts"> class Todo { done = $state(false); text = $state(''); createdAt = $state(new Date()); constructor(text: string) { this.text = text; } toggle() { this.done = !this.done; } edit(newText: string) { this.text = newText; } } let todos = $state([ new Todo('Svelte 5 학습하기'), new Todo('Runes 시스템 이해하기'), new Todo('클래스와 상태 관리 실습하기') ]); let newTodoText = $state(''); function addTodo() { if (newTodoText.trim()) { todos = [...todos, new Todo(newTodoText)]; newTodoText = ''; } } function removeTodo(index: number) { todos = todos.filter((_, i) => i !== index); } </script> <div class="p-4 border border-orange-500 rounded"> <div class="mb-6 flex space-x-2"> <input type="text" bind:value={newTodoText} placeholder="새 할 일 입력..." class="flex-1 p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> <button onclick={addTodo} class="bg-orange-600 text-white py-1 px-3 rounded hover:bg-orange-500" > 추가 </button> </div> <ul class="space-y-4"> {#each todos as todo, i} <li class="flex items-center space-x-4 text-gray-200"> <input type="checkbox" checked={todo.done} onclick={() => todo.toggle()} class="form-checkbox h-5 w-5 text-orange-600" /> <input type="text" value={todo.text} onchange={(e: Event) => todo.edit((e.target as HTMLInputElement).value)} class="flex-1 p-2 rounded bg-zinc-700 text-gray-200 border border-zinc-600" /> <button onclick={() => removeTodo(i)} class="text-red-500 hover:text-red-400" > 삭제 </button> </li> {/each} </ul> </div>
실습 과제
고급 상태 관리 시스템 구현하기
지금까지 배운 고급 반응성 개념을 활용하여 다음 기능이 있는 상태 관리 시스템을 구현해보세요:
- 커스텀 반응형 스토어 설계
- 중첩된 객체의 효율적인 상태 관리
- 최적화된 상태 업데이트 구현
- 클래스 기반 상태 관리자 만들기
- 의존성 추적을 활용한 자동 업데이트
다음 강의 미리보기
6강: 폼 처리 및 바인딩
다음 강의에서는 Svelte의 폼 처리와 바인딩 기능을 자세히 살펴봅니다:
- 폼 요소 바인딩
- 사용자 입력 처리
- 폼 유효성 검사
- 커스텀 폼 컴포넌트
- 파일 업로드 처리