1 컴포넌트

source: categories/study/vue-principle/vue-principle_1.md

1.1 컴포넌트의 필요성

  • 웹팩을 써야되는 이유
  • 컴포넌트 시스템 도입
  • 같은 부분을 여러번 사용하고 싶을 때?
    • 아래와 같이 끝말잇기를 세개를 동시에 넣으면 세개가 동시에 바뀐다.
    • 데이터가 서로 공유가 되고있기 때문
    • 그래서 아래와 같이 데이터들, ref, 메소드 등을 다 나눠서 연결(바인딩)해줘야 한다.
    • 하지만 아래와 같은 방식은 별로다. 같은 코드인데 반복(중복)해서 넣어줘야되는 것이 불편하고 보기도 안좋다.


<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
  <div>{{ word }}</div>
  <form v-on:submit="onSubmitForm">
    <input type="text" ref="answer" v-model="value">
    <button type="submit">입력!</button>
  </form>
  <div>{{ result }}</div>

  <div>{{ word1 }}</div>
  <form v-on:submit="onSubmitForm1">
    <input type="text" ref="answer1" v-model="value1">
    <button type="submit">입력!</button>
  </form>
  <div>{{ result1 }}</div>

  <div>{{ word2 }}</div>
  <form v-on:submit="onSubmitForm2">
    <input type="text" ref="answer2" v-model="value2">
    <button type="submit">입력!</button>
  </form>
  <div>{{ result2 }}</div>
</div>
<script>
const app = new Vue({
  el: '#root',
  data: {
    word: '제로초',
    word1: 'Vue',
    word2: '강좌',
    result: '',
    result1: '',
    result2: '',
    value: '',
    value1: '',
    value2: '',
  },
  methods: {
    onSubmitForm(e) {
      e.preventDefault();
      if (this.word[this.word.length - 1] === this.value[0]) {
        this.result = '딩동댕';
        this.word = this.value;
        this.value = '';
        this.$refs.answer.focus();
      } else {
        this.result = '땡';
        this.value = '';
        this.$refs.answer.focus();
      }
    },
    onSubmitForm1(e) {
      e.preventDefault();
      if (this.word1[this.word1.length - 1] === this.value1[0]) {
        this.result1 = '딩동댕';
        this.word1 = this.value1;
        this.value1 = '';
        this.$refs.answer1.focus();
      } else {
        this.result1 = '땡';
        this.value1 = '';
        this.$refs.answer1.focus();
      }
    },
    onSubmitForm2(e) {
      e.preventDefault();
      if (this.word2[this.word2.length - 1] === this.value2[0]) {
        this.result2 = '딩동댕';
        this.word2 = this.value2;
        this.value2 = '';
        this.$refs.answer2.focus();
      } else {
        this.result2 = '땡';
        this.value2 = '';
        this.$refs.answer2.focus();
      }
    },
  },
})
</script>
</body>
</html>



  • 프로그래밍에서 중요한 것
    • 중복을 줄여라
    • 위 코드는 너무 중복이 많다.
    • 이렇게 반복되는 부분을 컴포넌트로 만들면된다. (여기서 컴포넌트 개념 등장)

끝말잇기 컴포넌트



<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
  <word-relay></word-relay>
  <word-relay></word-relay>
  <word-relay></word-relay>
</div>
<script>
  Vue.component('word-relay', {
    template: `
      <div>{{ word }}</div>
      <form v-on:submit="onSubmitForm">
        <input type="text" ref="answer" v-model="value">
        <button type="submit">입력!</button>
      </form>
      <div>{{ result }}</div>
    `,
    data: {
      word: '제로초',
      result: '',
      value: '',
    },
    methods: {
      onSubmitForm(e) {
        e.preventDefault();
        if (this.word[this.word.length - 1] === this.value[0]) {
          this.result = '딩동댕';
          this.word = this.value;
          this.value = '';
          this.$refs.answer.focus();
        } else {
          this.result = '땡';
          this.value = '';
          this.$refs.answer.focus();
        }
      }, 
    }
  }); 
</script>
<script>
const app = new Vue({
  el: '#root',
})
</script>
</body>
</html>


1.2 컴포넌트의 특성



<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
  <word-relay></word-relay>
  <word-relay></word-relay>
  <word-relay></word-relay>
</div>
<script>
  // 아래 컴포넌트 형식은 전역 컴포넌트이다. 반대로 지역 컴포넌트도 있다.
  Vue.component('word-relay', {
    template: `
      <div>
        <div>{{ word }}</div>
          <form v-on:submit="onSubmitForm">
            <input type="text" ref="answer" v-model="value">
            <button type="submit">입력!</button>
          </form>
        <div>{{ result }}</div>
      </div>
    `,
    data() {
      return {
        word: '제로초',
        result: '',
        value: '',
      }
    },
    methods: {
      onSubmitForm(e) {
        e.preventDefault();
        if (this.word[this.word.length - 1] === this.value[0]) {
          this.result = '딩동댕';
          this.word = this.value;
          this.value = '';
          this.$refs.answer.focus();
        } else {
          this.result = '땡';
          this.value = '';
          this.$refs.answer.focus();
        }
      }, 
    }
  }); 
</script>
<script>
const app = new Vue({
  el: '#root',
})
</script>
</body>
</html>


  • 위 코드를 그대로 실행하면 에러 발생
    • "data" option should be a function that returns a per-instance value in component definitions.
    • data 를 객체 형식이 아닌 컴포넌트에선 함수 형식으로 만들어야 한다.
    • 같은 data 를 참조하지 않고 각각 새로운 객체를 참조하도록 새로운 객체를 만들도록 함수 형식으로 작성해야된다.
  • 위 에러 해결한 후 발생하는 에러
    • Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
    • 위에 component 를 보면 루트 수준에 형제 태그가 3개가 있다. 이 루트 수준의 태그는 반드시 1개만 있어야된다.
    • 이점이 마음에 안드시는 분들도 있을 것이다. 쓸데없는 div 태그 하나가 더 들어갔기 때문이다.
    • 이러한 자잘한 점들 때문에 웹팩을 이용하게된다. 웹팩을 이용하면 이런한 문제를 해결할 수 있다.
  • component 는 같은 것을 렌더링하지만, 중복을 제거할 수 있다.
  • data 들은 각 component 별로 따로따로 존재한다.
  • component 는 vue instance 보다는 위에 위치하여야하고 스크립트는 div#root 보다는 아래에 위치해야 한다.

  • component 를 만든다고 해서 서버에 부담을 줄여주거나 그런건 아니다.
    • 어차피 처음에 모든 컴포넌트를 서버에서 내려주기 때문.

1.3 props 와 웹팩의 필요성



<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
  <word-relay start-word="제로초"></word-relay>
  <word-relay start-word="초밥"></word-relay>
  <word-relay start-word="바보"></word-relay>
</div>
<script>
  // 아래 컴포넌트 형식은 전역 컴포넌트이다. 반대로 지역 컴포넌트도 있다.
  Vue.component('wordRelay', {
    template: `
      <div>
        <div>{{ word }}</div>
          <form v-on:submit="onSubmitForm">
            <input type="text" ref="answer" v-model="value">
            <button type="submit">입력!</button>
          </form>
        <div>{{ result }}</div>
      </div>
    `,
    props: ['startWord'],
    data() {
      return {
        word: this.startWord,
        result: '',
        value: '',
      }
    },
    methods: {
      onSubmitForm(e) {
        e.preventDefault();
        if (this.word[this.word.length - 1] === this.value[0]) {
          this.result = '딩동댕';
          this.word = this.value;
          this.value = '';
          this.$refs.answer.focus();
        } else {
          this.result = '땡';
          this.value = '';
          this.$refs.answer.focus();
        }
      }, 
    }
  }); 
</script>
<script>
const app = new Vue({
  el: '#root',
})
</script>
</body>
</html>


  • component 가 많아지다보면 script 파일을 여러개 만들 것이다.
  • 그리고 각 파일을 script 태그로 불러올 것이다.
    • 이때 불편함이 생긴다.
    • 불러온 script 순서도 일치해야하고 하나 수정하면 다른 파일에 영향을 미치는지 아닌지도 검사해야되고..
    • 이러한 문제들을 해겨하기 위해 CommonJS, RequireJS 같은 시스템을 브라우저에서 많이 쓰다가
    • 웹팩이 나오면서 스크립트들을 아예 하나로 합쳐주는 역할을 해주게되었다.
    • 스크립트 파일이 여러개 있을 때의 복잡함을 웹팩이 많이 해소해준다.