ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Vue 입문 - todolist 만들어보기 1 - 시작하기
    Study/개발 2021. 6. 7. 00:32

    Vue 홈페이지 스크린샷

    들어가며

    이번에 회사에서 맡은 프로젝트가 Vue를 사용하기 때문에 Vue를 시작하게 되었다.
    Vue는 js 프레임워크 중 하나로, 쉽게 FE 개발을 할 수 있다. 리액트를 해봤다면 더 쉬울 것이다. Vue 학습을 위해 todolist를 만들며 연습해본 과정을 정리해봤다.

    Vue 시작하기

    Hello World!

    가장 간단한 형태의 Vue app은 다음과 같이 만들 수 있다. <script>태그로 cdn을 사용해서 Vue를 사용할 수 있다. Vue라는 생성자 함수가 전역에 등록되어 이를 사용할 수 있게 된다.

    Vue 함수

    지정해준 옵션(data와 메서드 등)은 _init을 통해 Vue 인스턴스(this)에 등록한다. 그러므로, 인스턴스가 생성된 후인 created 이후단계부터 프로퍼티에 접근할 수 있다.

    <!-- index.html -->
    <html>
      <head>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
          <span> {{ message }} </span>
        </div>
        <script src="index.js"></script>
      </body>
    </html>
    // index.js
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello World!'
      }
    })

    index.js를 보면 app이라는 새로운 Vue인스턴스를 생성한다. Vue 생성자함수는 옵션 객체를 인자로 호출되고 있다. 옵션 객체의 내부에는 eldata가 있다.

    el은 css selector를 값으로 가진다. 해당 값을 document.querySelector처럼 검색하여 Vue를 마운트할 element를 찾는다. 위 예시에서는 <div id='app'>부분이 여기에 해당한다. 이 element에는 이제 Vue 템플릿 문법이 적용되어, mustache 구문 {{ ... }}으로 Vue 인스턴스에 등록된 식별자를 사용할 수 있다.

    hello world

    위 html을 띄우면 다음과 같은 화면을 볼 수 있다. {{ message }} 였던 <span> 태그의 내부 textContent가 자동으로 Vue인스턴스의 data 속성으로 등록된 message값으로 바뀐 것을 알 수 있다.

    이런 방식으로 작동하기 때문에 <span>부분을 <div id='app'> 밖으로 빼면 {{ message }}가 치환되지 않고 그대로 표시되는 것을 볼 수 있다.

    나중에 알아보겠지만, data 외에도 method 등을 등록해 사용할 수 있다. Vue를 써봤던 사람이라면 흔히 쓰던 컴포넌트 방식이랑 좀 달라서 어색할 수 있지만, 가장 기본적인 Vue 앱은 이렇게 동작한다.

    기본적인 todolist 만들기

    위 방식으로 간단한 todolist를 작성해 보기로했다. 복잡한 기능은 제외하고 기본 기능만 구현한다. 우선, 할일들을 아래와 같이 목록으로 나타낼 수 있어야 한다.

    • 할일 1
    • 할일 2
    • 할일 3

    Vue 적용해보기

    우선 마크업을 작성해본다. 위 html에서 app 내부에 리스트를 만들어준다. 우선 할일은 하나만 작성해본다.

    ...
    <div id="app">
      <ul>
        <li>할일 1</li>
      </ul>
    </div>
    ...

    "할일 1"으로 하드코딩되어있는 부분이 원래의 투두리스트라면 우리가 원하는 데이터가 들어가는 부분이다. 즉, 변경될 수 있는 부분이다. 위에서 이런 부분을 Vue의 data를 이용해서 표현하는 것을 보았다. 그 방식을 다시 적용해본다.

    ...
    <div id="app">
      <ul>
        <li>{{ todo }}</li>
      </ul>
    </div>
    ...
    // index.js
    var app = new Vue({
      el: '#app',
      data: {
        todo: '할일 1'
      }
    })

    메서드 추가하기

    데이터는 추가해봤지만, 아직 데이터를 변경해보지는 못했다. Vue를 생성할 때 만든 데이터가 그대로 유지된다. 이번에는 클릭 시 완료처리를 할 수 있도록 메서드를 추가해보자.

    완료처리된 항목은 취소선을 표시한다. 그러려면 class에 inactive를 줘서 css로 처리해줄 수 있다. 즉, 클릭하면 inactive라는 클래스가 토글되도록 만들어보자.

    <li class="inactive">할일 1</li>

    위와 같이 만들어져야하는데, 이제 "할일 1" 뿐만 아니라 class="inactive"도 우리가 Vue를 통해 제어해야하는 부분이 되었다. 그럼 클래스 명을 todo-class라는 식별자로 만들어보자.

    class는 속성이므로, Vue에서 컨트롤 하려면 v-bind라는 기능을 통해 Vue의 식별자들과 binding해주어야한다. 다음과 같이 나타낼 수 있다.

    ...
    <div id="app">
      <ul>
        <li v-bind:class="todoClass">{{ todo }}</li>
      </ul>
    </div>
    ...
    // index.js
    var app = new Vue({
      el: '#app',
      data: {
        todo: '할일 1',
        todoClass: 'inactive', 
      }
    })

    추가된 클래스


    크롬 개발자 도구를 보면 v-bind를 사용해서 클래스에 inactive가 추가된 것을 알 수 있다. 하지만 할일의 초기 값이 비활성인 것은 적절하지 않다. 초기값은 활성 상태고, 클릭할 때 비활성/활성이 토글되어야한다. 이게 가능하도록 메서드를 만들어서 데이터를 조작해보자.

    // index.js
    var app = new Vue({
      el: '#app',
      data: {
        todo: '할일 1',
        todoClass: null, 
      },
      methods: {
        onClickTodo() {
          this.todoClass = this.todoClass ? null : 'inactive';
        }
      }
    })

    메서드는 옵션 객체의 methods에 추가된다. 이렇게 만든 메서드를 템플릿에 등록하려면 v-on을 사용해야한다. 기존 html의 onClick 등 이벤트 핸들러와 유사한 방식으로 사용한다. 다음 예시와 같다.

    <li v-bind:class="todoClass" v-on:click="onClickTodo">{{ todo }}</li>

    click자리에 다른 이벤트를 원한다면 다른 이벤트를 사용하면 된다. 이제 클릭하면 class가 inactive가 되고, 다시 클릭하면 클래스가 없어지는 것을 볼 수 있다.

    스타일 변화를 위해 css만 추가해주면 제대로 작동한다.

    /* style.css */
    .inactive {
      text-decoration: line-through;
    }
    <html>
      <head>
        <link rel="stylesheet" href="style.css"/>
        ...

    취소선1

    투두 아이템 추가

    지금까지 아이템 하나를 만들었다. 그럼 여러개를 추가해볼 수 있다. 근데 만약 똑같은 메서드와 데이터를 이용한 상태로 템플릿만 복사-붙여넣기 한다면 다음과 같이 모든 데이터가 똑같아 진다.

    각각 데이터를 data1, data2, data3, ... 으로 만들고 그 마다의 메서드를 메서드1, 메서드2, 메서드3, ... 으로 일단 만들 수는 있지만, 그렇게 되면 데이터는 계속 세개만 다룰 수 있고 추가하거나 줄일 수가 없어서 좋은 방식이 아니다.

    // 안좋은 예
    
      data: () => ({
        todo1: '할일 1',
        todoClass1: null, 
        todo2: '할일 1',
        todoClass2: null, 
      }),
      methods: {
        onClickTodo1() {
          this.todoClass = this.todoClass ? null : 'inactive';
        }
        onClickTodo2() {
          this.todoClass = this.todoClass ? null : 'inactive';
        }
      }

    더 좋은 방식은 코드의 재사용이다. todoitem을 분리해서 재사용할 수 있는 코드로 만들고, data와 method는 각각이 들고 있도록 todolist와 todoitem을 분리 하는 것이다. 다음 예시를 보는 것이 더 쉽다.

    Vue.component('todo-item', {
      template: `
        <li v-bind:class="todoClass" v-on:click="onClickTodo">{{ todo }}</li>
      `,
      data: () => ({
        todo: '할일 1',
        todoClass: null, 
      }),
      methods: {
        onClickTodo() {
          this.todoClass = this.todoClass ? null : 'inactive';
        }
      }
    })
    
    var app = new Vue({
      el: '#app',
    })
    <div id="app">
      <ul>
        <todo-item></todo-item>
        <todo-item></todo-item>
        <todo-item></todo-item>
      </ul>
    </div>

    Vue.component를 사용해서 기존에 Vue 생성자의 옵션 객체로 있던 data와 methods를 분리해냈다. Vue.component는 전역 뷰 컴포넌트를 생성하는 함수로, 이처럼 코드를 분리할 수 있게 해준다. 첫번째 인자에는 html에서 사용할 이름이 들어가고, 두번째 인자에는 옵션이 들어간다.

    옵션의 template은 해당 컴포넌트를 html에 사용했을 때 어떤 엘리먼트로 대체할것인지를 나타내고, data와 method는 Vue 생성자를 쓸 때와 같지만, 인스턴스별로 독립된 스코프를 갖게 된다.

    할일분리

    Props 사용하기

    지금은 세 todo-item 모두 "할일 1" 이라는 내용이 표시된다. 이 부분을 개선해보자. todo-item에 데이터를 전달하여 표시될 텍스트로 설정되도록 하고싶다.

    <div id="app">

    위에서 "app"을 div의 id에 전달하듯이 todo-item도 attribute를 전달하는 방식으로 사용할 수 있다. props를 사용하면된다.

    Vue.component('todo-item', {
      props: ['todo'],
      template: `
        <li v-bind:class="todoClass" v-on:click="onClickTodo">{{ todo }}</li>
      `,
      data: () => ({
        todoClass: null, 
      }),
      methods: {
        onClickTodo() {
          this.todoClass = this.todoClass ? null : 'inactive';
        }
      }
    })
    
    const todos = ['할일 1', '할일 2', '할일 3'];
    
    const app = new Vue({
      el: '#app',
    })
    <div id="app">
      <ul>
        <todo-item v-bind:todo="'할일 1'"></todo-item>
        <todo-item v-bind:todo="'할일 2'"></todo-item>
        <todo-item v-bind:todo="'할일 3'"></todo-item>
      </ul>
    </div>

    위 예시는 todo-item 컴포넌트의 props로 todo라는 인자를 받을 수 있다. 이 때 주의할점은 v-bind:todo에 값을 넘길 때 따옴표 안의 값이 js구문으로 해석되므로 문자열을 넣으려면 "'할일 1'"과 같이 작성해줘야한다. '할일 1'로 작성하면 todo라는 prop할일1이라는 변수의 값을 할당하겠다는 뜻이 된다.

    반복문 사용하기

    컴포넌트 형태로 만들었지만, 중복을 다 없애진 못했다. 바뀌는 부분은 todo에 들어가는 값 뿐이기 때문에 반복문으로 처리하면 쉬울 듯 보인다.

    vue에서 반복문은 v-for를 사용한다.

      <ul>
        <todo-item 
          v-for="todo in todos"
          v-bind:todo="todo">
        </todo-item>
      </ul>

    위와 같이 사용하는데, v-for의 값으로 "todo in todos"라고 적어준 것을 볼 수 있다. todos라는 배열에 있는 원소 하나하나를 순회하는데, 그 원소를 참조할 때 todo라는 이름을 사용하겠다는 뜻이다. 그럼 todos가 정의되어 있어야한다. app에 데이터로 이를 정의해준다.

    const app = new Vue({
      el: '#app',
      data: () => ({
        todos: ['할일 1', '할일 2', '할일 3']
      })
    })

    처음에 비해 코드가 훨씬 간결해진 것을 볼 수 있다.

    완성

    Vue의 가장 기본기능에 대해 만들어봤다. 사실 실제로는 이런식으로 잘 쓰지 않는다.
    다음에는 todolist를 더 발전시켜 보겠다. script태그가 아닌 npm을 통해 vue를 설치하고, webpack을 사용해 모듈 방식으로 발전시켜볼 예정이다.

    출처

    728x90
Designed by Tistory.