프론트엔드 프레임워크를 공부할 때 가장 기본이 되는 Todo앱 만들기를 통해 이제까지 정리했던 사항들을 되짚어보자.
전체적인 구조는 다음과 같다. App.svelte에서 input을 통해 todos 리스트에 항목을 추가 받고 해당 todos 리스트를 Props로 Todo.svelte에 제공하여 아래에 순서대로 출력하도록 한다. 이 때 각 항목들은 id를 가지며 수정, 삭제 기능을 가지고 있다.
입력할 input, button 생성
사용자로부터 새로운 todo를 입력받을 input과 button을 생성하며 이 값을 저장할 변수와 그것을 처리할 함수를 추가한다.
<script>
import Todo from './Todo.svelte'
let title = ''
let todos = []
let id = 0
function createTodo() {
if (!title.trim()) {
title = ''
return
}
todos.push({
id,
title
})
todos = todos // 스벨트는 할당을 통해 변경을 감지!! 많이 쓰임
title = ''
id += 1
}
</script>
<input bind:value={title}
on:keydown={(e) => {e.key === 'Enter' && createTodo()}}
type="text" />
<button on:click={createTodo}>
Create Todo
</button>
{#each $todos as todo}
<Todo {todos} {todo}/>
{/each}
이후에 삭제 버튼을 통해 리스트의 요소를 삭제해야 하므로 todos 리스트에 들어가는 값은 객체이며 id와 title을 field로 갖는다. input에 입력된 값은 title에 할당되며 Create Todo 버튼을 누르면 고유한 id값과 함께 todos 리스트에 추가된다. todos 리스트 내부의 값은 Todo 컴포넌트에 Props로 제공되어 리스트로 출력되게 된다.
여기서 중요한 것은 svelte는 값을 할당을 통해 변경을 감지하기 때문에 todos 리스트에 push를 한 뒤에 todos에 todos 자신을 할당해주어야 한다는 점이다. todos에 push 함수를 사용하면 값이 할당되는 것이 아니냐고 할 수 있는데 push 함수를 사용하면 새 값이 할당되는 것으로 취급되지 않고 그저 리스트에 요소가 하나 추가될 뿐이다.
그 외에 input 태그에서 enter를 누르면 바로 createTodo가 실행될 수 있도록 on:keydown 함수를 바인딩 해주었다. 이 때 사용한 람다식을 보면 약간 생소하게 느낄 수도 있는데 보통은 if 문이나 삼항 연산자를 사용하지만 위처럼 &&를 통해 간략히 작성할 수도 있다. 아래의 세가지 식은 전부 같은 동작을 의미한다.
if (e.key === 'Enter') { createTodo() }
e.key === 'Enter' ? createTodo() : undefined
e.key === 'Enter' && createTodo()
하위 컴포넌트 Todo.svelte 작성
이제 App.svelte에서 사용할 Todo.svelte를 작성한다. 여기서는 수정 모드 / 일반 모드를 구분할 isEdit 변수를 사용하며 수정 모드를 껐다 키는 함수와 todo 업데이트, 제거 함수를 구현한다.
<script>
export let todos
export let todo
let isEdit = false
let title = ''
function onEdit() {
isEdit = true
title = todo.title
}
function offEdit() {
isEdit = false
}
function updateTodo() {
todo.title = title
offEdit()
}
function deleteTodo() {
todos = todos.filter(t => t.id !== todo.id)
}
</script>
{#if isEdit}
<div>
<input bind:value={title}
on:keydown={(e) => {e.key === 'Enter' && updateTodo()}}
type="text" />
<button on:click={updateTodo}>
OK
</button>
<button on:click={offEdit}>
Cancel
</button>
</div>
{:else}
<div>
{todo.title}
<button on:click={onEdit}>
Edit
</button>
<button on:click={deleteTodo}>
Delete
</button>
</div>
{/if}
앞에서 정리했던 if 문을 사용하여 isEdit을 통해 수정 / 일반 모드를 구분하여 화면에 표시되는 DOM을 다르게 표시하였다. deleteTodo를 실행하게 되면 filter 함수를 통해 부모로부터 받은 todos 변수에서 해당 todo값을 제거하게 되는데 이 때 todos를 수정하면 해당 변경사항이 반영되지 않는다.
그 이유는 상위 컴포넌트(App)에서 todos를 바인딩 할 때 단방향 바인딩(todos={todos})을 해주었기 때문이며 이것은 bind:todos={todos} 로 값 바인딩을 수정하여 해결할 수 있다. 하지만 여기서는 이전에 배운 것들을 활용하는 단계이기 때문에 App.svelte에서 todos를 일반 배열이 아닌 writable 객체로 수정하기로 한다.
Writable 객체 사용하기
Todo.svelte에서 props로 받은 todos를 반응성을 유지한 채 수정하기 위해서 App.svelte에서 writable 객체를 import하고 todos객체를 writable 객체로 감싸서 초기화 해준다. 이 때 wriatble 객체에 직접 값을 할당하기 위해서는 변수명 앞에 $를 붙이는 것을 잊지 말자!
<script>
import { writable } from 'svelte/store'
import Todo from './Todo.svelte'
let title = ''
let todos = writable([])
let id = 0
function createTodo() {
if (!title.trim()) {
title = ''
return
}
$todos.push({
id,
title
})
$todos = $todos // 스벨트는 할당을 통해 변경을 감지!! 많이 쓰임
title = ''
id += 1
}
</script>
<input bind:value={title}
on:keydown={(e) => {e.key === 'Enter' && createTodo()}}
type="text" />
<button on:click={createTodo}>
Create Todo
</button>
{#each $todos as todo}
<Todo {todos} {todo}/>
{/each}
여기서 주의할 점은 App.svelte 파일 내부의 모든 todos 변수 앞에 $를 붙여서 값을 제대로 할당할 수 있게 했지만 Todo 컴포넌트로 넘겨주는 props로써의 todos에는 $를 붙여주지 않았다는 점이다. 그 이유는 todos를 writable객체로 만든 것은 하위 컴포넌트에서도 반응성을 유지한 채 수정할 수 있는 변수로 만들기 위함인데 props로 넘겨줄 때 $를 붙이게 되면 writable 객체로써 넘어가는 것이 아니라 그냥 배열로 넘어가는것이기 때문에 이전과 같이 반응성을 갖지 못하기 때문이다.
이렇게 writable 객체로 받은 todos를 Todo.svelte에서 다음과 같이 수정하면 우리가 기대한 대로 Delete 버튼을 누르면 해당 Todo가 리스트에서 삭제되는 것을 볼 수 있다.
<script>
...
function deleteTodo() {
$todos = $todos.filter(t => t.id !== todo.id)
}
...
</script>
정리
이번에는 Svelte 기본강의 마지막으로 간단한 Todo앱을 만들며 이제까지 살펴보았던 기본 문법들을 복습하였다. 기존 내용들과 특별히 다를 것은 없지만 이번 예제를 통해 Vue, React와 다른 큰 차별점을 느꼈다.
기존 Vue, React에서는 상태관리 도구(Vuex, Redux)를 사용하지 않고 하위 컴포넌트에서 상위 컴포넌트의 변수를 수정하려면 해당 변수를 수정하는 함수를 부모에서 자식으로 Props를 넘겨주었어야 했다. 하지만 Svelte에서는 그런 거추장한 과정 없이 자식 컴포넌트에서 부모 컴포넌트의 변수를 수정할 수 있다는 점이 편리하게 다가왔다.
물론 이러한 차이점이 어떤 경우에서는 단점으로 작용할 수도 있겠으나 대부분은 상태관리 도구를 사용하고 일부분만 하위 컴포넌트에서 부모 컴포넌트의 변수를 변경해야 할 일이 있다면 다른 프론트엔드 프레임워크보다 간편하게 처리를 할 수 있을 것 같다.
아직 기본 예제만 살펴보았지만 배우다보니 Vue, React와 개념상 큰 차이점은 없는 것 같아서 기존에 다른 프레임워크를 사용해보았던 사람이라면 쉽게 사용할 수 있을 것 같다. 앞으로 Svelte Core API 강의를 들으면서 사이드 프로젝트를 진행할 수 있을 만큼의 지식을 쌓고 이 블로그에 정리해 보려고 한다.
'Svelte' 카테고리의 다른 글
Svelte 기초 문법 (0) | 2021.08.16 |
---|---|
Svelte란?? (0) | 2021.08.13 |
댓글