Vue.js ์ž…๋ฌธ์„œ - ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ

Vue.js๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

MVVM ํŒจํ„ด์˜ ViewModel ๋ ˆ์ด์–ด์— ํ•ด๋‹นํ•˜๋Š” ํ™”๋ฉด๋‹จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

View Model Layer

  • ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ํ™”๋ฉด ๋‹จ์œ„๋ฅผ ์ปดํฌ๋„ŒํŠธ ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•˜๋ฉฐ, ๊ด€๋ จ API ๋ฅผ ์ง€์›ํ•˜๋Š”๋ฐ์— ๊ถ๊ทน์ ์ธ ๋ชฉ์ ์ด ์žˆ์Œ
  • Angular์—์„œ ์ง€์›ํ•˜๋Š” ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์„ ๋™์ผํ•˜๊ฒŒ ์ œ๊ณต
  • ํ•˜์ง€๋งŒ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ํ†ต์‹ ์˜ ๊ธฐ๋ณธ ๊ณจ๊ฒฉ์€ React์˜ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„(๋ถ€๋ชจ -> ์ž์‹)์„ ์‚ฌ์šฉ
  • ๋‹ค๋ฅธ ํ”„๋ŸฐํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ(Angular, React)์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ€๋ณ๊ณ  ๋น ๋ฆ„.
  • ๋ฌธ๋ฒ•์ด ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ„๊ฒฐํ•˜์—ฌ ์ดˆ๊ธฐ ํ•™์Šต ๋น„์šฉ์ด ๋‚ฎ๊ณ  ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ฒŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ

MVVM ํŒจํ„ด์ด๋ž€?

์œ„ํ‚ค์— ๋ช…์‹œ๋œ ๊ฒƒ์ฒ˜๋Ÿผ, Backend ๋กœ์ง๊ณผ Client ์˜ ๋งˆํฌ์—… & ๋ฐ์ดํ„ฐ ํ‘œํ˜„๋‹จ์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ๋กœ ์ „ํ†ต์ ์ธ MVC ํŒจํ„ด์˜ ๋ฐฉ์‹์—์„œ ๊ธฐ์ธํ•˜์˜€๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒ๊ฐํ•ด์„œ ํ™”๋ฉด ์•ž๋‹จ์˜ ํšŒ๋ฉด ๋™์ž‘ ๊ด€๋ จ ๋กœ์ง๊ณผ ๋’ท๋‹จ์˜ DB ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐ ์„œ๋ฒ„ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๊ณ , ๋’ท๋‹จ์—์„œ ๋„˜์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ Model ์— ๋‹ด์•„ View ๋กœ ๋„˜์–ด์ฃผ๋Š” ์ค‘๊ฐ„ ์ง€์ ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋˜๊ฒ ๋‹ค.

mvvm-pattern

Vue.js ์‹œ์ž‘ํ•˜๊ธฐ

๋‹ค๋ฅธ ์ฃผ์š” ํ”„๋ŸฐํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ(Angular, React)์™€ ๋น„๊ตํ–ˆ์„ ๋•Œ ๋ทฐ์œ„ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ๋ฐ”๋กœ ์‹œ์ž‘ํ•˜๊ธฐ๊ฐ€ ์ •๋ง ์‰ฝ๋‹ค๋Š” ์ ์ด๋‹ค.

<!DOCTYPE html>
<html>
  <head>
    <title>Vue.js Sample</title>
  </head>
  <body>
    <div id="app">
      {{ message }}
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      new Vue({
        el: "#app",
        data: {
          message: "Hello Vue.js!"
        }
      });
    </script>
  </body>
</html>

CDN์œผ๋กœ ์ฝ”๋“œ ๋•ก๊ฒจ์˜ค๊ณ  ๋ฐ”๋กœ Vue ์ธ์Šคํ„ด์Šค๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค. ๊ธฐ์กด์˜ ๊ตฌํ˜„๋œ ์‹œ์Šคํ…œ์— ์ ์šฉํ•˜๊ธฐ๋„ ํ›จ์”ฌ ์ˆ˜์›”ํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

Vue Instance

์ธ์Šคํ„ด์Šค๋Š” Vue.js๋กœ ํ™”๋ฉด์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•ด ๊ผญ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ํ•„์ˆ˜ ๋‹จ์œ„์ด๋‹ค.

Vue Instance ์ƒ์„ฑ์ž

Vue ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

new Vue({
  // instance option properties
});

Vue ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์ด data, template, el, methods, life cycle hook ๋“ฑ์˜ ์ธ์Šคํ„ด์Šค ์˜ต์…˜ ์†์„ฑ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.

new Vue({
  // instance option properties
  template: "",
  el: "",
  methods: {}
  // ...
});

Vue Instance ๋ผ์ดํ”„์‹ธ์ดํด ์ดˆ๊ธฐํ™”

์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์•„๋ž˜์˜ ์ดˆ๊ธฐํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

  • ๋ฐ์ดํ„ฐ ๊ด€์ฐฐ
  • ํ…œํ”Œ๋ฆฟ ์ปดํŒŒ์ผ
  • DOM ์— ๊ฐ์ฒด ์—ฐ๊ฒฐ
  • ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‹œ DOM ์—…๋ฐ์ดํŠธ

์ด ์ดˆ๊ธฐํ™” ์ž‘์—… ์™ธ์—๋„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜๋„ํ•˜๋Š” ์ปค์Šคํ…€ ๋กœ์ง์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

new Vue({
  data: {
    a: 1
  },
  created: function() {
    // this ๋Š” vm ์„ ๊ฐ€๋ฆฌํ‚ด
    console.log("a is: " + this.a);
  }
});

์œ„ created ์ด์™ธ์—๋„ ๋ผ์ดํ”„์‹ธ์ดํด ๋‹จ๊ณ„์— ๋”ฐ๋ผ mounted, updated, destroyed ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ผ์ดํ”„์‹ธ์ดํด ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ๋กœ ์ปค์Šคํ…€ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ทฐ์—์„œ๋Š” ๋”ฐ๋กœ Controller๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

Vue Components

ํ™”๋ฉด์˜ ์˜์—ญ์„ ์ผ์ •ํ•œ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์–ด ์žฌํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ปดํฌ๋„ŒํŠธ

vue-js-component-intro-picture

์ปดํฌ๋„ŒํŠธ ๋“ฑ๋ก์€ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

<div id="app">
  <my-component></my-component>
</div>
new Vue({
  el: "#app",
  // ์ปดํฌ๋„ŒํŠธ ๋“ฑ๋ก ์ฝ”๋“œ
  components: {
    // '์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„': ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ
    "my-component": {
      template: "<div>A custom component!</div>"
    }
  }
});

Global or Local Component

์•„๋ž˜์˜ ์ปดํฌ๋„ŒํŠธ ๋“ฑ๋ก ๋ฐฉ์‹์€ ์ „์—ญ ์ปดํฌ๋„ŒํŠธ ๋“ฑ๋ก ๋ฐฉ์‹์ด๋‹ค.

Vue.component('my-component', {
  // ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ
  template: '',
  ...
})

์•„๋ž˜์™€ ๊ฐ™์ด ์ง€์—ญ ์ปดํฌ๋„ŒํŠธ๋กœ๋„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

var cmp = {
  // ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ
  template: '',
  ...
}

new Vue({
  components: {
    'my-cmp' : cmp
  }
})

๋ถ€๋ชจ์™€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๊ด€๊ณ„

์ปดํฌ๋„ŒํŠธ ๊ด€๊ณ„๋„์—์„œ ์ƒ-ํ•˜ ๊ด€๊ณ„์— ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ํ†ต์‹ ์€

  • ์œ„์—์„œ ์•„๋ž˜๋กœ๋Š” ๋ฐ์ดํ„ฐ(props)๋ฅผ ๋‚ด๋ฆฌ๊ณ 
  • ์•„๋ž˜์—์„œ ์œ„๋กœ๋Š” ์ด๋ฒคํŠธ๋ฅผ ์˜ฌ๋ฆฐ๋‹ค(event emit)

parent-child-components-relationship

Props

ํ”„๋กญ์Šค๋Š” ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‚ด๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ ์†์„ฑ์„ ์˜๋ฏธํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ ์ปดํฌ๋„ŒํŠธ ์ž์ฒด์˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ–๊ณ  ์žˆ์–ด ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ’์„ ๋ฐ”๋กœ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

<!-- ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ -->
<div id="app">
  <!-- ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” message๋ฅผ ์ „๋‹ฌํ•จ -->
  <child-component v-bind:propsdata="message"></child-component>
</div>
// ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ
Vue.component("child-component", {
  // ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์˜ data ์†์„ฑ์ธ message๋ฅผ propsdata๋ผ๋Š” ์†์„ฑ์œผ๋กœ ๋„˜๊ฒจ๋ฐ›์Œ
  props: ["propsdata"],
  template: '<p>{{ propsdata }}</p>'
});

// ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ
var app = new Vue({
  el: "#app",
  data: {
    message: "Hello Vue! from Parent Component"
  }
});

์ฃผ์˜ํ•  ์ : props ๋ณ€์ˆ˜ ๋ช…์„ ์นด๋ฉœ ๊ธฐ๋ฒ•(aBow)์œผ๋กœ ์ •์˜ํ•˜๋ฉด html ํƒœ๊ทธ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ผ€๋ฐฅ ๊ธฐ๋ฒ•(-)์œผ๋กœ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜๋Š” ๋งŒ์•ฝ ํ”„๋กญ์Šค ์†์„ฑ ๋ช…์„ ์นด๋ฉœ ๊ธฐ๋ฒ•์ธ passedData๋กœ ์„ ์–ธํ–ˆ์„ ๋•Œ์˜ ์ฃผ์˜ ๋ฉ”์‹œ์ง€

props-parsing-rules-between-components

๊ฐ™์€ ๋ ˆ๋ฒจ์˜ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ํ†ต์‹ 

๋™์ผํ•œ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ง„ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค ๊ฐ„์˜ ํ†ต์‹ ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์•ผ ํ•œ๋‹ค.

  • Child(ํ•˜์œ„) -> Parent(์ƒ์œ„) -> Children(ํ•˜์œ„ 2๊ฐœ)

์ฐธ๊ณ  : ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์ง์ ‘์ ์ธ ํ†ต์‹ ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋„๋ก ๋˜์–ด ์žˆ๋Š”๊ฒŒ Vue ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

Event Bus

์ƒ์œ„ - ํ•˜์œ„ ๊ด€๊ณ„๊ฐ€ ์•„๋‹Œ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ํ†ต์‹ ์„ ์œ„ํ•ด Event Bus๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Event Bus๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ๋ทฐ ์ธ์Šคํ„ด์Šค๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑํ•œ๋‹ค.

// ํ™”๋ฉด ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ์ธ์Šคํ„ด์Šค์™€ ๋‹ค๋ฅธ ๋ณ„๋„์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ™œ์šฉ
var eventBus = new Vue();

new Vue({
  // ...
});

์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ปดํฌ๋„ŒํŠธ์—์„œ $emit() ํ˜ธ์ถœ

eventBus.$emit("refresh", 10);

์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ปดํฌ๋„ŒํŠธ์—์„œ $on() ์ด๋ฒคํŠธ ์ˆ˜์‹ 

// ์ด๋ฒคํŠธ ๋ฒ„์Šค ์ด๋ฒคํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ•จ์ˆ˜์—์„œ ์ˆ˜์‹ 
new Vue({
  created: function() {
    eventBus.$on("refresh", function(data) {
      console.log(data); // 10
    });
  }
});

๋งŒ์•ฝ, eventBus์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์•ˆ์—์„œ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ ค๋ฉด vm ์‚ฌ์šฉ

new Vue({
  methods: {
    callAnyMethod() {
      // ...
    }
  },
  created() {
    var vm = this;
    eventBus.$on("refresh", function(data) {
      console.log(this); // ์—ฌ๊ธฐ์„œ์˜ this๋Š” ์ด๋ฒคํŠธ ๋ฒ„์Šค์šฉ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ด
      vm.callAnyMethod(); // vm์€ ํ˜„์žฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ด
    });
  }
});

Vue Routers

๋ทฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์‹ฑ๊ธ€ ํŽ˜์ด์ง€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ œ์ž‘ํ•  ๋•Œ ์œ ์šฉํ•œ ๋ผ์šฐํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ. ๋ทฐ ์ฝ”์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ๊ณต์‹ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ง€์›๋˜๊ณ  ์žˆ๋‹ค.

์„ค์น˜๋Š” NPM๊ณผ CDN ๋ฐฉ์‹ ๋ชจ๋‘ ์ง€์›ํ•œ๋‹ค.

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
npm install vue-router --save

๋ผ์šฐํ„ฐ ํŠน์„ฑ

Vue ๋ผ์šฐํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ '๋ฃจํŠธ URL'/#/'๋ผ์šฐํ„ฐ ์ด๋ฆ„'์˜ ๊ตฌ์กฐ๋กœ ๋˜์–ด ์žˆ๋‹ค.

example.com/#/user

์—ฌ๊ธฐ์„œ โ€˜#โ€™ ๊ฐ’์„ ์ œ์™ธํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด mode ์†์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

new VueRouter({
  mode: "history"
});

Nested Routers

๋ผ์šฐํ„ฐ๋กœ ํ™”๋ฉด์„ ์ด๋™ํ•  ๋•Œ ๋„ค์Šคํ‹ฐ๋“œ ๋ผ์šฐํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง€์ •๋œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ ์ปดํฌ๋„ŒํŠธ์˜ ๊ตฌ์กฐ๋Š” ๊ฐ€์žฅ ํฐ ์ƒ์œ„์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•˜์œ„์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” Parent - Child ํ˜•ํƒœ์™€ ๊ฐ™๋‹ค.

<!-- localhost:5000 -->
<div id="app">
  <router-view></router-view>
</div>

<!-- localhost:5000/home -->
<div>
  <p>Main Component rendered</p>
  <app-header></app-header>
</div>
// 'localhost:5000/home'์— ์ ‘๊ทผํ•˜๋ฉด Main๊ณผ Header ์ปดํฌ๋„ŒํŠธ ๋‘˜๋‹ค ํ‘œ์‹œ๋œ๋‹ค.
{
  path : '/home',
  component: Main,
  children: [
    {
      path: '/',
      component: AppHeader
    },
    {
      path: '/list',
      component: List
    },
  ]
}

Named Views

ํŠน์ • URL๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™์‹œ์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

<div id="app">
  <router-view name="appHeader"></router-view>
  <router-view></router-view>
  <router-view name="appFooter"></router-view>
</div>
{
  path : '/home',
  // Named Router
  components: {
    appHeader: AppHeader,
    default: Body,
    appFooter: AppFooter
  }
},

Nested Router vs Named Views

  • ํŠน์ • URL์— ์ง€์ •๋œ 1๊ฐœ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์„ Nested Router
  • ํŠน์ • URL์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์˜์—ญ ๋ณ„๋กœ ์ง€์ •ํ•˜์—ฌ ๋ Œ๋”๋ง ํ•˜๋Š” ๊ฒƒ์„ Named View

View Model Layer

Axios

Vue์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” HTTP ํ†ต์‹  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. CDN๊ณผ NPM ์„ค์น˜ ๋ฐฉ์‹์„ ๋ชจ๋‘ ์ง€์›ํ•˜๋ฉฐ ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹์€ ์†์„ฑ๊ณผ API๊ฐ€ ๋งŽ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค๋„ Promise ๊ธฐ๋ฐ˜์ด๋ผ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๊ธฐ ์šฉ์ดํ•˜๋‹ค.

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
npm install axios

์„ค์น˜ ํ›„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

methods: {
  fetchData: function() {
    axios.get('URL ์ฃผ์†Œ');
  }
}

Vue Template

ํ…œํ”Œ๋ฆฟ์ด๋ž€ ๋ทฐ๋กœ ํ™”๋ฉด์„ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์ œ๊ณต๋˜๋Š” ๋ฌธ๋ฒ•์ด๋‹ค. ๋ทฐ ์ธ์Šคํ„ด์Šค์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ํ™”๋ฉด์˜ ์กฐ์ž‘์„ ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š” ๋””๋ ‰ํ‹ฐ๋ธŒ๋กœ ๋‚˜๋‰œ๋‹ค.

Data Binding

์ฝง์ˆ˜์—ผ ๋ฌธ๋ฒ•์ธ โ€œ{{ }}โ€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค์˜ data, computed, props ์†์„ฑ์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ„๋‹จํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ‘œํ˜„์‹๋„ ํ™”๋ฉด์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

<div>{{ str }}</div>
<div>{{ number + 1 }}</div>
<div>{{ message.split('').reverse().join('') }}</div>

Directive

HTML ํƒœ๊ทธ์˜ ์†์„ฑ์— v- ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™์€ ํŠน๋ณ„ํ•œ ์†์„ฑ์œผ๋กœ ํ™”๋ฉด์˜ DOM ์กฐ์ž‘์„ ์‰ฝ๊ฒŒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ๋ฒ•๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

<!-- seen์˜ ์ง„์œ„ ๊ฐ’์— ๋”ฐ๋ผ p ํƒœ๊ทธ๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ ๋˜๋Š” ๋ฏธํ‘œ์‹œ -->
<p v-if="seen">Now you see me</p>
<!-- ํ™”๋ฉด์— a ํƒœ๊ทธ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์‹œ์ ์— ๋ทฐ ์ธ์Šคํ„ด์Šค์˜ url ๊ฐ’์„ href์— ๋Œ€์ž… -->
<a v-bind:href="url"></a>
<!-- ๋ฒ„ํŠผ์— ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ doSomething์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ -->
<button v-on:click="doSomething"></button>

Filters

ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ํ…์ŠคํŠธ์˜ ํ˜•์‹์„ ํŽธํ•˜๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ๊ณ ์•ˆ๋œ ๊ธฐ๋Šฅ์ด๋ฉฐ | ์„ ์ด์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<!-- message ๊ฐ’์— capitalize ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์—ฌ ์ฒซ ๊ธ€์ž๋ฅผ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€๊ฒฝ -->
{{ message | capitalize }}
new Vue({
  filters: {
    capitalize: function(value) {
      if (!value) return "";
      value = value.toString();
      return value.charAt(0).toUpperCase() + value.slice(1);
    }
  }
});

Single File Component

ํŠน์ • ํ™”๋ฉด ์˜์—ญ์˜ HTML, CSS, JS ์ฝ”๋“œ๋ฅผ ํ•œ ํŒŒ์ผ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•. ํŒŒ์ผ ํ™•์žฅ์ž๋Š” vue์ด๋ฉฐ HTML ํŒŒ์ผ์—์„œ ๋ทฐ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ–ˆ์„ ๋•Œ์˜ ํ•œ๊ณ„์ ์„ ๊ทน๋ณตํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ๋„ ํ•˜๋‹ค. ํ•œ๊ณ„์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ๊ณ ์œ ์˜ ์ด๋ฆ„์„ ๋ถ™์—ฌ์•ผ ํ•จ
  2. js ํŒŒ์ผ์—์„œ template ์•ˆ์˜ html ์˜ ๋ฌธ๋ฒ• ๊ฐ•์กฐ๊ฐ€ ๋˜์ง€ ์•Š์Œ
  3. js ํŒŒ์ผ์ƒ์—์„œ css ์Šคํƒ€์ผ๋ง ์ž‘์—…์ด ๊ฑฐ์˜ ๋ถˆ๊ฐ€
  4. ES5 ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ณ„์† ์•ฑ์„ ์ž‘์„ฑํ•  ๊ฒฝ์šฐ Babel ๋นŒ๋“œ๊ฐ€ ์ง€์›๋˜์ง€ ์•Š์Œ

์‹ฑ๊ธ€ ํŒŒ์ผ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐœ๋ฐœํ•˜๋ ค๋ฉด Webpack๊ณผ ๊ฐ™์€ ๋ฒˆ๋“ค๋ง ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์‹ฑ๊ธ€ ํŒŒ์ผ ์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋ณธ ๊ณจ๊ฒฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

<template>
  <!-- HTML -->
</template>

<script>
  // Javascript
</script>

<style>
  /* CSS */
</style>

Vue Loader

์‹ฑ๊ธ€ ํŒŒ์ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์›นํŒฉ ๋กœ๋”. ๋ทฐ ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ๋‹ค.

  1. ES6 ์ง€์›
  2. <style> ๊ณผ <template> ์— ๋Œ€ํ•œ ๊ฐ๊ฐ์˜ ์›นํŒฉ ๋กœ๋” ์ง€์›. ex) sass, jade
  3. ๊ฐ .vue ์ปดํฌ๋„ŒํŠธ์˜ ์Šค์ฝ”ํ”„๋กœ ์ขํžŒ css ์Šคํƒ€์ผ๋ง ์ง€์›
  4. ์›นํŒฉ์˜ ๋ชจ๋“ˆ ๋ฒˆ๋“ค๋ง์— ๋Œ€ํ•œ ์ง€์›๊ณผ ์˜์กด์„ฑ ๊ด€๋ฆฌ๊ฐ€ ์ œ๊ณต
  5. ๊ฐœ๋ฐœ ์‹œ Hot Module Replacement(HMR) ์ง€์›

Vue CLI

๋ทฐ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ช…๋ น์–ด ๋„๊ตฌ์ด๋‹ค. ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋กœ CLI๋ฅผ ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์— ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  CLI๋ฅผ ์„ค์น˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Node.js LTS ๋ฒ„์ „์ด ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

npm install -g @vue/cli

CLI๊ฐ€ ์„ค์น˜๋˜๊ณ  ๋‚˜๋ฉด ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

vue create ํ”„๋กœ์ ํŠธ ์ด๋ฆ„

๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋‚˜๋ฉด Preset์„ ์„ ํƒํ•˜๋ผ๊ณ  ๋‚˜์˜ค๋Š”๋ฐ Default๋ฅผ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค. ํ”„๋กœ์ ํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ์ฝ˜์†”์— ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์•ˆ๋‚ด๋œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

cd ํ”„๋กœ์ ํŠธ ํด๋” ์ด๋ฆ„
npm run serve

Virtual DOM in Vue.js

๋ฆฌ์•กํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ทฐ๋„ Virtual DOM์„ ์‚ฌ์šฉํ•œ๋‹ค. Virtual DOM์€ ํ™”๋ฉด์„ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ฐ˜ ๊ธฐ์ˆ ์ด๋‹ค. ํ™”๋ฉด์˜ DOM์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” ๋“ฑ์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚  ๋•Œ ๋งˆ๋‹ค ํ™”๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋กœ DOM์˜ ๋ชจ์–‘์„ ์žก์•„ ๋†“๊ณ  ํ™”๋ฉด์˜ ๋ Œ๋”๋ง ํšŸ์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ ๋ถ€ํ•˜๋ฅผ ์ค„์ธ๋‹ค.

์ฐธ๊ณ  ์„œ์ 

๊ฐ ์ฃผ์ œ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ๋” ์ž์„ธํžˆ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฑ…์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค :)

Do it! Vue.js ์ž…๋ฌธ

๊ธ€๋ณด๋‹ค ๋” ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๋Š” Vue.js ์˜จ๋ผ์ธ ๊ฐ•์ขŒ

์ข€ ๋” ์นœ์ ˆํ•˜๊ณ  ์ƒ์„ธํ•œ ์„ค๋ช…์„ ์›ํ•˜์‹ ๋‹ค๋ฉด ์•„๋ž˜ ๊ฐ•์ขŒ๋ฅผ ์ด์šฉํ•ด๋ณด์‹œ๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š” ๐Ÿ˜„

์ธํ”„๋Ÿฐ ์˜จ๋ผ์ธ ๊ฐ•์˜ : Vue 3 ์‹œ์ž‘ํ•˜๊ธฐ / Vue.js ์‹œ์ž‘ํ•˜๊ธฐ(Vue 2) / Vue.js ์ค‘๊ธ‰
์ธํ”„๋Ÿฐ ์˜จ๋ผ์ธ ๊ฐ•์˜ : Vue.js ์™„๋ฒฝ ๊ฐ€์ด๋“œ / Vue.js ๋์žฅ๋‚ด๊ธฐ / ํ”„๋ŸฐํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์›นํŒฉ
์ธํ”„๋Ÿฐ ์˜จ๋ผ์ธ ๊ฐ•์˜ : ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ž…๋ฌธ / ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋กœ ๋ฐฐ์šฐ๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ / Vue.js + TypeScript ์™„๋ฒฝ ๊ฐ€์ด๋“œ

์ดํ•ด๊ฐ€ ์ž˜ ์•ˆ๋˜์‹œ๋‚˜์š”? ๋ฐฉ์†ก์—์„œ ์ง์ ‘ ๋ฌผ์–ด๋ณด์„ธ์š” :)

๋งค์ฃผ ํ† ์š”์ผ ์˜คํ›„ 3์‹œ 30๋ถ„์— ์œ ํŠœ๋ธŒ ๋ผ์ด๋ธŒ ๋ฐฉ์†ก์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ŸฐํŠธ์—”๋“œ ๊ฐœ๋ฐœ ๊ด€๋ จํ•ด์„œ ์•„๋ฌด๊ฑฐ๋‚˜ ์—ฌ์ญค๋ณด์‹ค ์ˆ˜ ์žˆ์œผ์„ธ์š” :)

ํ”„๋ŸฐํŠธ์—”๋“œ ๊ฐœ๋ฐœ ์ƒ๋‹ด์†Œ ๋ฐ”๋กœ๊ฐ€๊ธฐ