[VueJS] 재사용을 해보자

eunniverse·2024년 5월 26일
0

글 쓰게된 계기

개발을 하던 중 재사용을 가능케하는 여러 요소들을 보며 어떤 점이 다르고 내가 아는 것 외에도 어떤 요소들이 있을지 궁금해져서 글을 쓰게 되었다!

재사용??

재사용이란 공통으로 사용될 수 있는 로직을 한번만 구현하여 이곳 저곳에서 쓰이게끔 하는 것을 의미한다. 그렇다면 어떤 요소들이 있을까??

1.Mixin

정의

여러 컴포넌트 간에 공통으로 사용되고 있는 로직, 기능들 재사용하는 방법이다.

예시

Mixin의 다양한 예시를 코드를 통해 살펴보자!

let mixin = {
	created() {
      console.log('mixin created!');
    },
  	method: {
      hello() {
        console.log('mixin hello!');
      }
    }
}

let Component = Vue.extend({
  mixin: [mixin]
});

let comp1 = new Component();

위와 같을 때 예상되는 결과는 ?? 바로 created 메서드가 실행되어 mixin created! 가 콘솔에 찍힌다. 이게 바로 Mixin 모든 옵션이 컴포넌트 고유 옵션에 혼합되는 예시이다.
(그러면 comp1.hello() 를 하면 당연히 mixin hello! 가 콘솔에 찍히게 된다!!!)

그렇다면! mixin과 component에 중첩되는 옵션이 있다면 어떻게 나올까?

let eunniverseMixin = {
  	data() {
    	return {
          name: 'eunniverse',
          gender: 'female'
    },
	created() {
      console.log('eunniverse created!');
    },
  	method: {
      hello() {
        console.log('eunniverse hello!');
      }
    }
}

let newEunniverse = new Vue({
  mixin: [eunniverseMixin],
  data() {
    return {
      name: 'notEunniverse',
      age: 27
    }
  },
  created() {
    console.log(this.$data)
  },
  method: {
    hello() {
      console.log('new eunniverse hello!');
    }
  }
});

newEunniverse.hello();

위의 코드같은 경우, 선언한 data 중 name, gender이 같고, method 중 hello 가 중복된다. 그러면 어떻게 나올까??
결과는 바로 { name: 'notEunniverse', age: 27, gender: 'female'} 가 찍힌 후, new eunniverse hello! 가 콘솔에 찍힌다.
왜냐하면 중복되는 옵션이 있을 경우 컴포넌트의 옵션이 우선순위를 가지기 때문이다.

특징

  1. Mixin에서 정의할 수 있는 재사용 로직은 data, methods, created, mounted 등 컴포넌트의 옵션이다.
  2. 컴포넌트에 Mixin을 사용하면 해당 Mixin 모든 옵션이 컴포넌트 고유 옵션에 혼합된다.
    (위의 예시에서 볼 수 있듯이!)
  3. 주로 Vue 내부에서 공통적으로 쓰이는 로직을 재활용하기에 편리하며, 다중 상속이 가능하다.
    ([ ] 안에 나열하면 된다!)

2. Plugin

(...!) 음, Plugin은 통상적으로 사용하는 전역 method 인데, 어찌됐든 전역으로 등록해 import하지 않고 사용할 수 있으니까 재사용과 어느정도 가깝다고 생각해서 정리하게 되었다!

정의

자주 사용될만한 속성, 함수, 라이브러리를 설치하여 사용을 하는 속성(?) 혹은 기능(?)

예시

// ===================== plugin 구현하기 =====================
// myPlugin.js
export default {
  // install 메서드 구현 필요
  install(Vue, options) {
  	Vue.myMethod = () => {
    	// 로직 생성....
    }
  }

  
 //===================== plugin 사용하기 =====================
 // ex) main.js
 // myPlugin.install(Vue) 를 호출
 Vue.use(myPlugin);

// Browserify 또는 Webpack을 통해 CommonJS를 사용할 때
var VueRouter = require('vue-router')
Vue.use(VueRouter)

위의 예시처럼 plugin 을 구현할 때는 install 메서드 노출이 필요하다. 그리고 Vue.use(${plugin명})으로 사용을 해주면 컴포넌트에서 매번 import를 해주지 않아도 사용할 수 있다.

특징

  1. plugin으로 선언된 파일은 Vue 파일 내부에서 this로 접근이 가능하다.
  2. Vue.use는 자동으로 같은 플러그인을 두 번 이상 사용하지 못하기 때문에 같은 플러그인에서 여러번 호출하면 플러그인이 한 번만 설치한다.

3. HOC (High-Order Component)

정의

컴포넌트 로직을 재사용하기 위한 React의 고급 기술로서 React의 컴포넌트적 성격에서 나타나는 패턴이다. 즉 컴포넌트가 컴포넌트를 새롭게 리턴해주는 것이다.

예시

<!-- books.vue -->
<template>
  <div>{{ data }}</div>
</template>

<script>
import axios from 'axios';
  
export default {
  name: "books",
  data() {
    return {
      data: null
    }
  },
  // ======== 공통 로직 =========
  created() {
    axios.get("https://localhost:8080/books").then((res) => {
    	this.data = res.data;
    });
  },
</script>

<!-- myBooks.vue -->
<template>
  <div>{{ data }}</div>
</template>

<script>
import axios from 'axios';
  
export default {
  name: "myBooks",
  data() {
    return {
      data: null,
    }
  },
  // ======== 공통 로직 =========
  created() {
    axios.get("https://localhost:8080/books?id=1").then((res) => {
    	this.data = res.data;
    });
  },
};
</script>

위와 같이 공통 로직이 있을 때, 사용할 수 있는데 HOC 이다. HOC로 사용하려면 다음의 절차가 필요하다!

HOC로 만들기

  1. JS파일을 생성해 with 으로 시작하는 함수를 만든다.
    : HOC를 만들 때 with 이라는 접두어를 붙인다.
/* WithRequest.js */
import Vue from 'vue';
import axios from 'axios';

const WithRequest = (url) => (component) => {
  return Vue.component("WithRequest", {
    data() {
      return {
        data: null,
      };
    },
    created() {
    	axios.get(url).then((res) => {
    		this.data = res.data;
    	});
    },
    render(createElement) {
    	return createElement(component, {
          props: {
            data: this.data,
          },
        });
    },
  });
};

export { WithRequest };
  1. 컴포넌트에서 import 하여 사용한다.
<!-- App.vue -->
<template>
  <books />
  <my-books />
</template>

<script>
import books from '@/components/books.vue';
import myBooks from '@/components/myBooks.vue';
import { WithRequest } from '@/WithRequest';

export default {
  name: "App",
  components: {
    // books(기존 컴포넌트) : withRequest('url로 파라미터 넘기기')('새로운 컴포넌트') 
    // url을 파라미터로 넘기고 component를 받아 새로운 component를 리턴하는 구조
    "books": WithRequest('https://localhost:8080/books')(books),
    "my-books": WithRequest('https://localhost:8080/books?id=1')(myBooks),
  },
};
</script>
  1. 기존 컴포넌트 수정한다.
<!-- books.vue -->
<template>
  <div>{{ data }}</div>
</template>

<script>
import axios from 'axios';
  
export default {
  name: "books",
  props: {
    data: Object
  },
</script>

<!-- myBooks.vue -->
<template>
  <div>{{ data }}</div>
</template>

<script>
import axios from 'axios';
  
export default {
  name: "myBooks",
  props: {
    data: Object
  }
};
</script>
  • 위의 코드와 첫번째 코드를 비교하면 HOC를 적용했을 때 코드가 훨씬 간결해졌다.

생각해보자!

  1. Vue 에서는 자식이 발생시키는 이벤트와 props를 처리할 수 있는데, 이는 HOC로 어떻게 처리하면 될까?
    정답은!!! 기존 컴포넌트를 사용하는 것처럼 HOC 컴포넌트에 전달하면 된다.
import Vue from 'vue'

export function withComponent(components) {
  return Vue.extend({
    name: 'withComponent',
    // vue 에서 jsx 컴포넌트를 렌터링하는 방법이 `render` 옵션 API이다!
    render(h) {
      return h(components, {
        attrs: this.$attrs || {}, // attrs 전달
        props: this.$props || {}, // props 전달
        on: this.$listeners // 이벤트 전달
        scopedSlots: this.$scopedSlots // slot 전달
      })
    }
  })
}

참고)) 하위 컴포넌트에 slot 또한 전달할 수 있다!!

Mixin 과 비교하면?

Mixin은 서로 다른 컴포넌트에 공통적인 기능을 제공할 수 있다는 장점이 있다. 하지만 Mixin이 복잡해지면 컴포넌트와 Mixin은 병합되기 때문에 컴포넌트의 복잡도 또한 올라간다. 이런 점과 비교하면 HOC는 컴포넌트 관계를 유지하며 컴포넌트를 한번 감싸는 형태로 사용되므로 기존 컴포넌트에 주는 영향도가 적다. 그리고 HOC 활용한 컴포넌트는 일반적인 컴포넌트이므로 props 활용, 옵션 비활성화 등의 유연한 처리가 가능하다.
한마디로 Mixin은 컴포넌트와의 병합, HOC는 컴포넌트의 관계 유지가 차이점이다!!

특징

  1. 컴포넌트 자체를 재사용 함으로써 템플릿까지 포함해 캡슐화한다.
  2. HOC가 많아질수록 컴포넌트 레벨이 깊어지면서 컴포넌트간 통신에 있어 불안정해질 수 있다.

4. Composition API

정의

옵션을 선언하는 대신 import한 함수를 사용하여 Vue 컴포넌트를 작성할 수 있는 API 세트이다.

예시

import { reactive } from 'vue'

const algorithm: Book = {
  code: 'AAAAAA',
  createDate: '2024-05-27',
  // ... 더많은 속성들
}

// 공통되는 함수를 hook 형태로 작성
export function borrowBook() {
  const book = reactive(algorithm)
  
  // 로직....
  
  return { book }
}
<template>
  <div>{{ book.name }}</div>
</template>

<script lang="ts">
import { borrowBook } from '../hooks/books'

export default {
  setup() {
    // hook 가져와서 사용하기
    const book = borrowBook()

    return book
  }
}
</script>

특징

  1. 데이터 그룹핑에 있어 매우 용이하고 데이터 흐름을 파악하고 유지보수가 매우 편해진다.
  2. 공통되는 함수는 hook 형태로 작성하여 제공하면 mixin 기능을 사용할 필요가 없다.
  3. 자바스크립트(혹은 타입스크립트) 파일에서 함수를 제공하는 것이기 때문에 타입 지원 효과를 볼 수 있으며 비슷한 관심사의 로직을 한 곳에 몰아 관리 용이성도 챙길 수 있다.
  4. 유틸 함수를 import export가 재사용할 수 있고 좀 더 데이터 추적 및 사용이 쉬워졌다.
profile
능력이 없는 것을 두려워 말고, 끈기 없는 것을 두려워하라

0개의 댓글