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
총 값: 49798.62
필터링된 아이템: 1000
- 아이템 0 - 12.48
- 아이템 1 - 48.43
- 아이템 2 - 83.98
- 아이템 3 - 21.83
- 아이템 4 - 64.32
- 아이템 5 - 94.43
- 아이템 6 - 50.50
- 아이템 7 - 56.46
- 아이템 8 - 86.05
- 아이템 9 - 88.79
페이지 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): 짝수
마지막 업데이트: 1:58:23 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의 폼 처리와 바인딩 기능을 자세히 살펴봅니다:
- 폼 요소 바인딩
- 사용자 입력 처리
- 폼 유효성 검사
- 커스텀 폼 컴포넌트
- 파일 업로드 처리