Svelte로 만드는 TODO-App (3), Item 순서 변경하기


2019.11.29svelte

svelte

이번 포스트에서는 Item의 순서를 변경할 수 있도록 구현하겠습니다.

1. Item 순서 변경 구현하기

Item의 순서를 변경하여서 Todo-App을 사용하기 좋게 만들어 보겠습니다. 순서를 변경하는 UI/UX는 심플하게 드래그해서 변경할 수 있게 만들 것입니다.

event를 이용하여 구현할 수도 있겠지만, 해본 적도 없고 하니 이미 만들어진 라이브러리를 이용하여 적용해보겠습니다.

1.1 Sortablejs 라이브러리 적용하기

정렬을 구현하는 여러 라이브러리 중 Sortablejs를 이용하겠습니다.

npm을 이용해서 설치하겠습니다.

$ npm install sortablejs

이제 각 Board의 리스트에 적용해 보겠습니다. Item들을 감싸는 div를 만들어줍니다. 또 list라는 변수를 선언해주고 만들어 준 div에 바인딩 시켜주겠습니다.

svelte로 부터 onMount함수를 import하고, 그 안에 다음과 같이 구현해주세요. list변수에 div Element가 바인딩 되면 Sortable 라이브러리를 적용시키도록 합니다.

<!-- Board.svelte -->
<script>
  import { onMount } from 'svelte'
  import Sortable from 'sortablejs'
  
  let list = null
  onMount(() => {
    if(list) {
      new Sortable(list)
    }
  })
</script>
<div id={board.id} class="item-list" bind:this={list}>
{#each _items as item (item.id)}
  <Item
    {item}
    on:update={handleUpdateItem}
    on:remove={handleRemoveItem}
  />
{/each}
</div>

이제 브라우저로 가서 잘 적용되었는지 확인해 봅니다. Item을 드래그해서 이동할 수 있게 되었고, 순서도 변경이 됩니다.

하지만 DOM Element만 움직이는 것 일뿐, 아직 실제 Store에 있는 데이터의 순서가 변경되는 것은 아닙니다.

1.2 Sortablejs를 이용하여 순서 변경 구현하기

Sortablejs를 적용하여서 DOM Element를 드래그로 움직일 수 있게 했습니다. 드래그로 순서가 변경될 때, Store에 있는 실제 데이터가 변경되도록, 이벤트 핸들러를 연결시켜주면 됩니다.

먼저 그 전에 Store에 있는 데이터에서 Itemid 값을 약간 수정해주고, Item 컴포넌트에서 드래그 핸들을 만들어주도록 하겠습니다.

// stores/index.js
const _boards = [
  { id: uuid(), title: 'Planning' },
  { id: uuid(), title: 'In Progress' },
  { id: uuid(), title: 'All Done' },
]

const _items = [
  { id: uuid(), boardId: _boards[0].id, title: '자료 조사하기', done: false },
  { id: uuid(), boardId: _boards[0].id, title: '교양책 읽기', done: false },
  { id: uuid(), boardId: _boards[1].id, title: '분리수거하기', done: false },
  { id: uuid(), boardId: _boards[1].id, title: '가계부 정리하기', done: false },
  { id: uuid(), boardId: _boards[2].id, title: '버그 수정하기', done: false },
]

const createItems = () => {
  const { subscribe, reset, update: _update, set } = items
  // ...생략...
  return { subscribe, reset, set, add, remove, update }
}

BoardItemid를 이용해서 비교할 때, 문자와 숫자가 섞이면 비교하기 번거로우니 모두 문자로 수정하겠습니다. 그리고 itemsset함수를 넣어줘서 순서가 변경되면 적용되는 부분에 사용하도록 하겠습니다.

다음은 Item컴포넌트를 수정해줍니다.

<!-- Item.svelte -->
<div
  id={item.id}
  class="item card p-1 mb-2 shadow-sm"
  class:shadow={hovering}
  on:mouseenter={() => hovering = true}
  on:mouseleave={() => hovering = false}
>
  <span class="drag-handle">=</span>
  {#if isEditable}
  <input bind:value={item.title} />
  {:else}
  <span class:item-done={item.done} on:click={handleToggleDone}>
    {item.title}
  </span>
  {/if}
  <div class="item-action">
    <button class="item-btn" on:click={handleToggleEditable}>E</button>
    <button class="item-btn" on:click={handleRemoveItem}>-</button>
  </div>
</div>

Item의 가장 상위 div 태그에 id속성을 넣어주었고, 드래그를 위한 핸들 아이콘을 하나 만들어 주었습니다.

이제 순서가 변경될 수 있도록 Sortable에 옵션과 이벤트를 등록합니다.

<!-- Board.svelte -->
<script>
  const handleSortItem = e => {
    const target = $items.find(item => item.id === e.item.id)
    const allItems = $items.filter(item => item.id !== e.item.id)
    const _items = allItems.filter(item => item.boardId === e.to.id)
    target.boardId = e.to.id
    _items.splice(e.newIndex, 0, target)

    const newItems = allItems
      .filter(item => item.boardId !== e.to.id)
      .concat(_items)
    items.set(newItems)
  }

  let list = null
  onMount(() => {
    if (list) {
      new Sortable(list, {
        group: 'board',
        handle: '.drag-handle',
        animation: 300,
        onEnd: handleSortItem,
      })
    }
  })
</script>

onMount함수에서 Sortable 생성자에 옵션을 추가해줍니다.

  • group옵션은 다른 Board의 리스트로 드래그하여 움직일 수 있게 해줍니다.
  • handle옵션은 Item전체를 클릭하여 드래그하지 않고, handle에 주입된 클래스이름을 가진 Element를 클릭하여 드래그할 수 있게 해줍니다.
  • animation옵션은 드래그로 Item의 순서가 변경될 때, 약간의 애니매이션을 사용합니다.
  • onEnd옵션은 드래그가 끝났을 때, 이벤트 핸들러로 콜백 함수를 호출합니다. 실제 Store의 데이터를 변경하기 위해 사용됩니다.

onEnd옵션의 이벤트 핸들러로 위에서 선언한 handleSortItem함수를 호출합니다. 해당 함수에서는 실제 데이터의 순서가 변경되도록 구현합니다.

이제 브라우저로 가서 확인해보겠습니다.

svelte drag item

잘 동작하는지 확인하고 싶으면, 순서 변경 후 items를 콘솔로 출력해보시면 됩니다.


마무리

여기까지 Svelte로 만드는 Todo-App을 만들어 보았습니다. Svelte를 공부하기 위해서 간단히 튜토리얼이나 공식문서를 훑어보았고, 어떻게 동작하는지 실습하는 형식으로 공부하기 위해 이 시리즈의 포스트를 작성하며 공부해 보았습니다.

이제 기초적인 부분을 시작했고, 전반적으로 Svelte로 만든 앱이 어떻게 동작하는지 조금 알았으니, 공식 문서를 꼼꼼히 읽어보며 공부하면 더 도움이 될 것이고, 이해하기도 쉬울 것입니다.

전체 프로젝트 코드는 Github에서 보실 수 있습니다.

javascriptfrontendsvelte