usePagination - composition api (vue2)

2022-08-19

이슈 1. 반복 코드 리팩토링

Pagination 컴포넌트를 사용하는 곳에서 공통으로 반복해서 사용하는 코드를 composition api 를 사용하여 개선해보았습니다.

기존 공통 코드

export default Vue.extend({
  data: () => ({
    pageOptions: {
      page: 1,
      size: 10,
      totalElements: 0,
      totalPages: 0,
      first: false,
      last: false,
    },
  }),
  ...
  methods: {
    async getNotice() {
      try {
        const response = await notice_api.getNotice(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.pageOptions = {
            page: this.pageOptions.page,
            size: data.size,
            totalElements: data.totalElements,
            totalPages: data.totalPages,
            first: data.first,
            last: data.last,
          };

개선 코드

hooks/usePagination.ts

공통으로 사용할 data와 function을 관리

import { reactive } from "@vue/composition-api";

export interface PageType {
  page: number;
  size: number;
  totalElements?: number;
  totalPages: number;
  first: boolean;
  last: boolean;
}

interface ArgType {
  size: number;
}
interface ReturnType {
  pageOptions: PageType;
  setPageOptions: (page: number, response: PageType) => void;
}

// 한번에 보여줄 리스트 갯수(size)를 argument로 전달하고, 기본값은 10. optional로 생략가능 
export const usePagination = (arg?: ArgType): ReturnType => {
  const pageOptions = reactive<PageType>({
    page: 1,
    size: arg?.size || 10,
    totalElements: 0,
    totalPages: 0,
    first: false,
    last: false,
  });
  const setPageOptions = (page: number, response: PageType) => {
    pageOptions.page = page;
    pageOptions.size = response.size;
    pageOptions.totalElements = response.totalElements;
    pageOptions.totalPages = response.totalPages;
    pageOptions.first = response.first;
    pageOptions.last = response.last;
  };

  return { pageOptions, setPageOptions };
};

사용 예

import { usePagination } from "@/hooks/usePagination"; 

export default Vue.extend({ // 이슈 2. 에서 수정!
  setup() {
    const { pageOptions, setPageOptions } = usePagination();
    return { pageOptions, setPageOptions };
  },
  ...
  methods: {
    async getNotice() {
      try {
        const response = await notice_api.getNotice(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.setPageOptions(this.pageOptions.page, data); 
  

한번에 보여줄 리스트 갯수(size) 조절시 예

const { pageOptions, setPageOptions } = usePagination({size: 5});

이슈 2. typescript: property does not exist on type 'object & record<never, any>

  • setup() 에서 return한 데이터를 <template> 코드 내에서 사용시 기능은 모두 동작하였으나, 위와 같은 빨간줄 typescript 경고가 생겼습니다.

  • 테스트해보니 chrome vue extention 에서도 data로 잘 들어오고 있고, build 에러도 뜨지 않았지만, 찾아보니 아래와 같이 수정을 하면 typescript 경고가 발생하지 않았습니다.

참고 자료

해결 코드

<template>
  ... 
  // 이제 빨간 경고 줄이 생기지 않습니다. 
  <Pagination :pageOptions="pageOptions" @click="onClickPage" />
</template>

<script lang="ts">
import { defineComponent } from "@vue/composition-api";

export default defineComponent({
  setup() {
    const { pageOptions, setPageOptions } = usePagination();
    return { pageOptions, setPageOptions };
  },

Pagination 2개 이상 사용시 코드 비교

composition api 사용 전

data: () => ({
    pageOptionsHistory: {
      page: 1,
      size: 5,
      totalElements: 0,
      totalPages: 0,
      first: false,
      last: false,
    },
    pageOptionsGroup: {
      page: 1,
      size: 5,
      totalElements: 0,
      totalPages: 0,
      first: false,
      last: false,
    },
  }),
...
  methods: {
    async getHistory() {
      try {
        const response = await notice_api.getHistory(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.pageOptionsHistory = {
            page: this.pageOptionsHistory.page,
            size: data.size,
            totalElements: data.totalElements,
            totalPages: data.totalPages,
            first: data.first,
            last: data.last,
          };
        ...
    async getGroup() {
      try {
        const response = await notice_api.getGroup(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.pageOptionsGroup = {
            page: this.pageOptionsGroup.page,
            size: data.size,
            totalElements: data.totalElements,
            totalPages: data.totalPages,
            first: data.first,
            last: data.last,
          };

composition api 사용 후

setup() {
    const {
      pageOptions: pageOptionsHistory,
      setPageOptions: setPageOptionsHistory,
    } = usePagination({ size: 5 });

    const {
      pageOptions: pageOptionsGroup,
      setPageOptions: setPageOptionsGroup,
    } = usePagination({ size: 5 });
    return {
      pageOptionsHistory,
      setPageOptionsHistory,
      pageOptionsGroup,
      setPageOptionsGroup,
    };
  },
...
  methods: {
    async getHistory() {
      try {
        const response = await notice_api.getHistory(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.setPageOptionsHistory(this.pageOptionsHistory.page, data);
        ...
    async getGroup() {
      try {
        const response = await notice_api.getGroup(config);
        if (response.status === 200) {
          const data = response.data.items;
          this.setPageOptionsGroup(this.pageOptionsGroup.page, data);

이렇게 코드의 재사용성을 높여 반복 코드를 줄일 수 있습니다.

Last updated