DEV Community

Cover image for Svelte 기초 01 - 필수 요소들
freeseamew
freeseamew

Posted on • Updated on

Svelte 기초 01 - 필수 요소들

지금부터 프런트엔드 프레임워크 Svelte에 대해서 알아보도록 하겠습니다. 현재 프론트엔드 프레임워크로 잘 알려진 것들로는 react, vue, angular라는 제품들이 있고, 그 뒤를 이어 새롭게 떠오른 제품이 바로 이 svelte 입니다. 각각의 프레임워크들은 그에 맞는 특성있습니다. 이중에서 우리가 사용할 svelte의 가장큰 특징중 하나는

write less code

즉 다른 프레임워크들 보다 적은 코드고 결과를 만들 수 있는 간결함입니다. 사실 이럴때는 장황한 설명보다 한줄의 코드를 보는게 효율적일 때가 있습니다
코드 비교 이미지코드 비교 이미지

다음은 svelte와 함께 react, vue의 상태값을 비교한 코드입니다. 이 코드는 프런트엔드의 핵심중에 하나인 상태값을 각가의 프레임워크로 표현한 부분인데 다른 프레임워크와 달리 svelte의 경우 그냥 자바스크립트 변수선언 방법인 let을 사용한게 전부입니다.

특히 스크립트 영역에서는 프런트엔드를 다루기 위해서 특별한 문법을 배울 필요가 거의 없습니다. 그리고 마크업 영역에서는 간단하게 나마 HTML을 작성을 동적으로 만드는 몇몇 장치만 배우면 바로 사용이 가능할 정도로 심플한 구조를 가지고 있습니다.
그래서 개인적으로는 처음 frontend를 접하는 사용자들에게 적극적으로 추천하는 프레임워크 입니다.

이제부터 간단하게 svelte에 대해서 알아보도록 하겠습니다. 여기서 svelte의 모든 다루기는 힘들겠지만, 중요한 기능에 대해서 요약 설명은 드릴 수 있을 것 같습니다.

상태값(State)

첫번째로 학습할 요소는 state 즉 상태값입니다. svelte 아니 프런트엔드라는 것을 배울 때 가장 기본이 되는 중요한 개념이 바로 상태값입니다. 그래서 이 상태값 부터 알아보도록 하겠습니다.
사실 모든 프론트엔드 프레임워크의 구동방식은 비슷합니다. 자바스크립트로 state라고 하는 상태에 해당하는 어떤 변수들을 두고 이 state의 상태에 따라 html과 css로 화면을 만들어 가는 것입니다.
자 다음 코드를 보시겠습니다.

<script>
  let count = 0;

  function handleClick() {
    // 이벤트 코드
    count += 1;
  }

  </script>

  <button on:click={handleClick}>
    클릭수 { count } 
  </button>
Enter fullscreen mode Exit fullscreen mode

코드에서 let count = 0 이라고 되어 있는 부분이 상태를 나타내는 state입니다. 이 state값은 html 영역에서 { 상태값 이름 } 이런 식으로 표현할 수 있습니다.

또 함수 handleClick()은 상태값인 count에 1을 더하는 역할을 하는 메소드입니다. 이 함수 형태의 메소드를 이용해서 상태값을 변경하게 되는 것입니다. 여기서 handleClick메소드는 마크업영역에서 on:click 이벤트에 연동해 실제 마우스 클릭 이벤트를 감지해서 작동하게 됩니다.

그리고 변경된 상태값은 바로 적용되어 이 상태값을 사용하는 곳에 반영되게 됩니다.
그럼 이 코드를 실제로 작성해 보도록 하겠습니다. svelte에서는 실습을 위해 REPL이라는 환경을 제공하고 있습니다. 다음 사이트에서 코드를 입력해서 별다른 설치 없이 바로바로 결과를 확인할 수 있습니다.
참고로 이 코드는 다음 링크에서 직접 실행해 볼 수 도 있고 변화를 줘서 실행도 가능합니다.

https://svelte.dev/repl/hello-world?version=3

여기서 알 수 있듯이 마크업영역에서는 몇가지 간단한 장치를 사용하긴 했지만 스크립트의 경우 100%자바스크립트로 작성한 것을 볼 수 있습니다. 참고로 상태값의 경우 기본이 되는 변수는 물론 배열, 객체 객체의 배열 형태가 되도 상관없습니다. 자바스크립트에서 사용할 수 있는 대부분의 데이터형태를 사용할 수 있다고 생각하시면 됩니다.

Reactivity

이번에는 Svelte의 핵심기능중에 하나인 Reactivity 즉 반응성에 대해서 알아보겠습니다. svelte에서는 $: 기호를 이용해서 reacivity적인 기능들을 쉽게 구현할 수 있습니다. 몇안되는 추가 문법이라고 생각하시면 됩니다.

그럼 이 반응성기호를 이용해 코드를 조금 더 확장해 보겠습니다. 이 반응성기호($:)는 svelte의 특징 적인 코드중에 하나로 선언형 패턴의 코드입니다. 이것은 일종의 센서 같은 역할을 한다고 생각하시면 되겠습니다.
그럼 코드를 자세히 살펴보겠습니다.

<script>
  let count = 0
  $: doubled = count * 2

  $: if(count >= 10) {
    alert('카운트가 10을 넘었습니다. ')
    count = 9
  }

  $: {
    console.log( count )
    console.log( doubled )
  }

  function handleClick() {
    // 이벤트 코드
    count += 1
  }

</script>

<button on:click={handleClick}>
  클릭수 { count } {count === 1 ? 'time' : 'times'}
</button>

<p>{count} 두배는 {doubled} <p/>
Enter fullscreen mode Exit fullscreen mode

우선 가장 기본이 되는 doubled부터 보겠습니다. 일반적인 상태값이 let으로 선언되는 것과 달리 doubled는 반응성 기호로 선언되었습니다. 그래서 doubled는 count가 변경될 때마다 이를 감지하고 해당 count에 곱하기 2를 한 값이 됩니다.
반응성 기호($:)는 변수 뿐만 아니라 대괄호{} 로 감싸서 특정 영역에 영향을 줄 수도 있습니다. 예제의 경우 반응성 기호 안의 console.log들은 값이 변경되면 log를 나타나게 됩니다.

또 심지어 조건문과 사용해도 됩니다. 예제에서는 count가 10보다 같거나 클 경우 alert을 나타내고 count값을 9로 변경시키게 되는 것을 알 수 있습니다.
이것은 마치 화제 경보기에 열이 감지되면 스프링클러에서 물이 발사되는 것과 비슷한 작용입니다. 일반적인 프로그램에서의 호출과 달리 선언만 하면 그에 맞는 환경에 따라 작동하는 것입니다.

그리고 마크업 영역의 경우 기본적으로 상태값과 연계되어 상태값의 변화에 따라 돔이나 내용들이 보여지게 됩니다. 예제의 경우 count와 doubled는 변경이 되면 해당 변경된 값이 표현되었고 count의 상태에 따라 time, times가 표시되는 것입니다.

자 그럼 코드를 실행해 보겠습니다.

버튼을 클릭해 보면

  1. doubled는 count의 값의 2배값을 표시해 주고,
  2. console에 log에 변경된 count와 doubled가 표시되며,
  3. count 10보다 크거나 같으면 alert를 발생합니다.
  4. 또 마크업영역에서는 count가 1과 같으면 time 1이 아닐경우 times라고 글을 표시해주기도 합니다.

자 이처럼 반응성 기호로 변수나 영역을 만들고 선언을 하면 대상이 되는 상태값의 변화를 감지해 필요한 행동을 할 수 있게 만들 수 있습니다. 따라서 반응성 기호를 잘 활용하면 복잡한 기능들의 연계를 쉽게 구현할 수 있게 되는 것입니다.

예제에서 볼 수 있듯이 우리는 count라는 상태값을 변경시키는 액션 만을 취하지만,
count가 변경되면 count를 바라보고 있는 다양한 곳에서 그에맞는 일들을 자동으로 처리됩니다.이런식으로 직접적인 call없이 자동으로 변화를 감지하고 대응되도록 하는 패턴을 선언형 프로그램이라고도 합니다.

현실세계로 비유하자면 일반적인 프로그램방식이 물을 수동으로 틀었다 잠그는 샤워기와 비슷하다면 이 선언형 방식의 경우 불이나면 자동으로 물을 뿌려주는 화재경보기와 비슷하다고 할 수 있습니다.

프런트엔드를 개발하다 보면 이런 반응성적인 특성이 필요할 때가 많습니다. 메일 시스템을 만든다고 가정해보면 새로운 메일 시스템이 들어오면 화면에 알람을 나타내고, 메일의 개수를 표시하는 각종 카테고리에 수를 더해서 표시해야 하며 새로운 메일을 목록에 추가해서 보여주어야 할 것입니다. 이런상황에서 선언형 처리를 할 수 있다면 기존의 방법보다 훨씬 편하게 개발을 할 수 있을 것입니다.

component

svelte를 포함한 대부분의 프론트앤드 프레임워크는 SPA(single page application)의 구조를 가집니다. 하나의 페이지에 필요에 따라 다양한 컴포넌트를 배치해서 사용하는 구조가 SPA입니다.

이런 SPA구조의 앱을 만들 때 페이지가 하나라고 해서 하나의 페이지에 모든 내용을 넣는 것은 효율적이지 못합니다. 그래서 component라는 작은 블록을 만들고 그 블록을 조합해 가면서 화면을 구성하는 방식을 가집니다.

일반적인 블로그나 사이트의 경우로 보자면 상단의 Header, 사이드의 aside, 내용의 content, 하단의 footer로 구성됩니다. 그리고 contet에는 개별 post가 독립된 compoent로 구성할 수 있을 것입니다.

하나의 컴포넌트에는 javascript, html, css가 포함될 수 있고, 재사용도 가능합니다. 이런 컴포넌트는 필요에 따라 만들어지 부품으로 현실세계의 레고블록과 비슷하다고도 볼 수 있습니다.

컴포넌트 구조 예시

또 이렇게 컴포넌트가 모여서 하나의 앱이 되는 형태이므로 완성된 앱은 구조상 트리구조를 가집니다. App.svelte라는 제일 상위 컴포넌트가 있고, 거기에서 가지가 뻗어 나가듯 다양한 컴포넌트가 위치하게 되는 구조입니다.

그럼 실제 코드를 작성해서 svelte에서 컴포넌트를 만들고 사용하는 방법을 알아보겠습니다. 만들어진 컴포넌트는 자바스크립트 import를 이용해 가져올 수 있습니다 . 이때 불러온 컴포넌트는 대문자로 시작하게 이름을 작성해야 합니다. 그리고 배치의 경우는 일반적인 태그처럼 배치하여 사용하면 됩니다.

app.svelte

<script>
  import Header from './header.svelte';
  import Content from './content.svelte';
  import Footer from './footer.svelte';
</script>

<Header />
<Content />
<Content />
<Footer />
Enter fullscreen mode Exit fullscreen mode

header.svelte

<h1>Header Compoent</h1>
Enter fullscreen mode Exit fullscreen mode

content.svelte

<p>Content Component</p>
Enter fullscreen mode Exit fullscreen mode

footer.svelte

<h2>Footer Component</h2>
Enter fullscreen mode Exit fullscreen mode

이렇게 배치된 컴포넌트들은 서로 통신을 하면서 필요한 데이터를 교환합니다. 다른 컴포넌트에서 상태값을 전달하는 가장 기본적인 전달방법은 props입니다. 이 props의 경우 상위 컴포넌트에서 하위컴포넌트로 상태값을 전달하는 것으로 단방향 바인딩의 성격을 가집니다.

사용방법은 컴포넌트에 대괄호 안에 상태값의 이름을 작성하는 것으로 값을 넘길 수 있고받는 측에서는 export let을 이용해 값을 받을 수 있습니다. 이 props의 경우 말한 것처럼 단방향적인 성격을 가집니다. 그래서 만약 하위 컴포넌트에서 상위 컴포넌트의 상태값(state)를 변경할 경우 상위 컴포넌트에서 변경과 관련된 메소드를 만들고 이 메소드를 상태값과 같은 방법으로 전달하여 사용해야 합니다. 이말은 하위 컴포넌트에서 전달받은 상태값을 그냥 변경해 버릴 경우 상위 컴포넌트의 상태값은 변경되지 않는 다는 의미입니다. 그래서 props를 이용한 상태값의 제어의 경우 꼭 상태값이 위치한 상위컴포넌트에서 이처럼 메소드를 만들고 그 메소드를 전달해서 제어를 해야 합니다.

app.svelte

<script>
  import BtnClick from './btnClick.svelte';
  let count = 0;

  function handleClick() {
    // 이벤트 코드
    count += 1;
  }

</script>

<BtnClick {count} {handleClick} />
Enter fullscreen mode Exit fullscreen mode

btnClick.svelte

<script>
  export let count;
  export let handleClick;
</script>

<button on:click={handleClick}>
  클릭수 { count } 
</button>
Enter fullscreen mode Exit fullscreen mode

그럼 조금전 만든 예제의 코드를 분리해서 기능에 따라 컴포넌트로 만들어 보도록 하겠습니다.

example-app
└── App.svelte
    ├── countComp.svelte
    └── btnComp.svelte
          └── labelComp.svelte 
Enter fullscreen mode Exit fullscreen mode

우선 제일 상위 컴포넌트인 App.svelte에서 기본 상태값 count를 기본값 0으로 작성하고 반응성기호를 이용해 doubled를 만들고 count *2를 해주겠습니다. 여기에 역시 반응성기호로 count가 10보다 커질경우 alert을 나타내는 코드 그리고 count값을 변경하는 handleClick메소드를 작성하겠습니다. 그리고 CountComp와 btnComp 컴포넌트를 import가져오고 props를 이용해 btnComp에는 count와handleCliek를 전달하고 CountComp 컴포넌트에는 count와 doubled를 props로 전달하겠습니다

App.svelte

<script>
  import CountComp from './countComp.svelte'
  import BtnComp from './btnComp.svelte';


  let count = 0
  $: doubled = count * 2

  $: if(count >= 10) {
    alert('카운트가 10을 넘었습니다. ');
    count = 9;
  }

  function handleClick() {
    // 이벤트 코드
    count += 1;
  }
</script>

<BtnComp {count} {handleClick} />
<CountComp {count} {doubled} />
Enter fullscreen mode Exit fullscreen mode

다음으로 countComp 컴포넌트를 작성하겠습니다. export let으로 count와 doubled를 전달받고 이번에도 반응성 기호로 console.log를 작성해서 count와 doubled의 변화가 감지되면 로그를 기록하게 하겠습니다.
또 마크업영역에서는 count와 doubled의 값이 나타나게도 해주겠습니다.

countComp.svelte

<script>
  export let count;
  export let doubled;

  $: {
    console.log( count )
    console.log( doubled )
  }

</script>

<p>{count} 두배는 {doubled} <p/>
Enter fullscreen mode Exit fullscreen mode

btnComp컴포넌트도 작성하겠습니다. export let을 이용해서 count, handleClick를 전달받고 또 다른 하위 컴포넌트로 labelComp 컴포넌트를 불러와 주겠습니다.

마크업 영역에서 onclick이벤트에 handleClick를 연동하고 버튼의 내용에 count와 함께 labelComp컴포넌트를 배치하겠습니다.
그리고 labelComp 컴포넌트에 다시 count를 전달하겠습니다.

btnComp.svelte

<script>
  export let count;
  export let handleClick;
  import LabelComp from './labelComp.svelte';

</script>

<button on:click={handleClick}>
  클릭수 { count } <LabelComp {count} />
</button>
Enter fullscreen mode Exit fullscreen mode

마지막으로 labelComp컴포넌트에는 count를 전달받고 count의 값이 1보다 작거나 같으면 time을 아닐경우는 times가 표시되도록 해주겠습니다.

labelComp.svelte

<script>
  export let count
</script>

{count <= 1 ? 'time' : 'times'}
Enter fullscreen mode Exit fullscreen mode

이렇게 전달된 props는 결국 App에서 변경이 되고 변경이 되면 다시 하위 컴포넌트로 전달되는 구조입니다. 이미 설명 한 것처럼 이런 방식을 단방향 바인딩(one way binding) 이라고도 합니다. 이것은 react, vue 같은 frontend framework에서도 널리 사용되는 통신 패턴입니다. 핵심은 상위 컴포넌트에서 하위 컴포넌트로만 변화를 전달할 수 있다는 것을 기억하시기 바랍니다.

이 props를 이용하면 상하 관계의 state의 조작은 용이하지만, 수평관계이거나 혹은 컴포넌트끼리의 거리가 아주 먼 경우도 발생합니다. 이때는 context, dipatcher 그리고 store라는 것을 사용해 통신을 할 수 있습니다. 참고로 이번 강좌에서는 이 전역 컴포넌트 통신에 store를 적극적으로 사용할 것입니다.

html 돔 제어

이번에는 html영역을 효율적으로 만들어 주는 기능들을 알아보겠습니다.

위에서도 설명했지만, frontend는 결국 상태값에 따라 화면을 그려주는 역할이 메인입니다. 그래서 svelte에서는 그 화면 득 html 돔을 효율적으로 제어하게 도와주는 기능을 제공합니다.

  1. 첫번째는 논리블록({#if..}) 입니다.

논리블록은 일반적인 프로그램의 if와 같습니다. 단지 script영역에서 사용하는 것이 아니고, html영역 즉 마크업영역에서 상태값(state)에 따라 분기를 나누고 표현하는 기능을 가집니다.

그럼 예제를 보도록 하겠습니다. 이번 예제의 경우 auth.loggedIn의 상태에 따라 메시지가 달라지는 기능을 수행합니다. 이런식으로 상태값에 따라서 돔을 제어해야할 때 사용하면 좋은 기능이 svelte의 논리블록입니다.

<script>
let auth = {
  loggedIn: false
}

const handleLogin = () => auth.loggedIn = true;
const handleLogout = () =>  auth.loggedIn = false;

</script>

<button on:click={handleLogin} >Log In</button>
<button on:click={handleLogout} >log Out</button>

{#if auth.loggedIn === true}
  <p> 로그인 중입니다. </p>
{:else }
  <p> 로그아웃 중입니다. </p>
{/if}
Enter fullscreen mode Exit fullscreen mode
  1. 두번째로 반복블록({#each...}) 입니다.

일반적인 프로그램의 for같은 반복문의 기능을 html영역에서 실행시켜 주는 역할을 합니다. 예제를 통해 알아보도록 하겠습니다. 예제를 보면 todos라는 객체의 배열로 된 상태값이 있습니다. 우리는 이 객체의 내용을 마크업 영역에 출력해보겠습니다.

<script>
  let todos = [
    {
      id:0,
      content: '첫 번째 할일',
      done: false
    },
    {
      id:1,
      content: '두 번째 할일',
      done: false
    },
    {
      id:2,
      content: '세 번째 할일',
      done: true
    },
    {
      id:3,
      content: '네 번째 할일',
      done: false
    }
  ]
</script>

<ul>
    {#each todos as todo}
      <li>
        <span>{todo.id}</span>
        <span>{todo.content}</span>
      </li>
    {/each}
</ul>
Enter fullscreen mode Exit fullscreen mode

이 반복블록을 이용하면 이런 배열 형태의 상태값을 쉽게 연속해서 출력할 수 있습니다 상품리스트나, sns의 글목록등을 나타날때 아주 편리한 기능이라고 생각하시면 됩니다.

이것으로 Svelte를 사용하는데 필요한 기본적인 기능들을 모두 알아봤습니다. 다음시간에는 svelte의 또다른 핵심기능 중에 하나로 컴포넌트간 전역통신에 사용하는 store에 대해서 알아보도록 하겠습니다.


📺 유튜브 채널: https://www.youtube.com/channel/UC3cJspjF4TRTyD_RS0azeaw

✍️ 블로그1: https://dev.to/freeseamew

🖍 블로그2: https://medium.com/@freeseamew

📖 출판 도서

🖥 온강라강 강좌

Top comments (0)