Vue 3 + AG Grid 페이징처리

Posted by Albert 16Day 6Hour 49Min 28Sec ago [2026-01-21]

1.Infinite Row Model

전체 폴더 구조

src/
├── pages/
│   └── UserListPage.vue
│
├── components/
│   └── grid/
│       └── UserGrid.vue
│
├── composables/
│   └── useAgGridInfinite.js
│
├── stores/
│   └── userStore.js
│
├── services/
│   └── userService.js
│
└── main.js

1️⃣ services/

“API 통신만 담당” (비즈니스 로직 ❌)

services/userService.js

export async function fetchUsers({ start, limit, sortModel, filterModel }) {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      start,
      limit,
      sort: sortModel,
      filter: filterModel
    })
  })

  return response.json()
}

✔ 이 파일의 특징

  • axios / fetch 교체 쉬움
  • 테스트 가능
  • Grid / Pinia 모름

2️⃣ stores/

“Grid에서 사용할 상태 & 액션”

stores/userStore.js

import { defineStore } from 'pinia'
import { fetchUsers } from '@/services/userService'

export const useUserStore = defineStore('user', {
  state: () => ({
    totalCount: 0
  }),

  actions: {
    async loadUsers(params) {
      const result = await fetchUsers(params)
      this.totalCount = result.total
      return result.rows
    }
  }
})

✔ 여기서 하는 일

  • API 호출 위임
  • totalCount 상태 관리
  • 비즈니스 흐름 담당

3️⃣ composables/

“AG Grid Infinite 로직 캡슐화 (가장 중요 ⭐⭐⭐)”

composables/useAgGridInfinite.js

import { useUserStore } from '@/stores/userStore'

export function useAgGridInfinite() {
  const userStore = useUserStore()

  const dataSource = {
    async getRows(params) {
      try {
        const rows = await userStore.loadUsers({
          start: params.startRow,
          limit: params.endRow - params.startRow,
          sortModel: params.sortModel,
          filterModel: params.filterModel
        })

        params.successCallback(rows, userStore.totalCount)
      } catch (e) {
        params.failCallback()
      }
    }
  }

  return {
    dataSource
  }
}

왜 composables 인가?

  • Grid 재사용 가능
  • store / service 조합
  • 페이지와 Grid 분리

Infinite Row Model의 핵심은 여기 있음

4️⃣ components/

“AG Grid UI 전용 컴포넌트”

components/grid/UserGrid.vue

<template>
  <div class="ag-theme-alpine" style="height: 600px">
    <AgGridVue
      rowModelType="infinite"
      :columnDefs="columnDefs"
      :datasource="dataSource"
      :cacheBlockSize="100"
      :maxBlocksInCache="5"
      :rowHeight="40"
      @grid-ready="onGridReady"
    />
  </div>
</template>

<script setup>
import { AgGridVue } from 'ag-grid-vue3'
import { useAgGridInfinite } from '@/composables/useAgGridInfinite'

const { dataSource } = useAgGridInfinite()

const columnDefs = [
  { field: 'id', headerName: 'ID', width: 80 },
  { field: 'name', headerName: '이름' },
  { field: 'email', headerName: '이메일' }
]

const onGridReady = (params) => {
  params.api.setDatasource(dataSource)
}
</script>

✔ 역할

  • UI만 담당
  • API / store 모름
  • Infinite 설정 집중

5️⃣ pages/

“페이지 단위 조립”

pages/UserListPage.vue

<template>
  <div>
    <h2>사용자 목록</h2>
    <UserGrid />
  </div>
</template>

<script setup>
import UserGrid from '@/components/grid/UserGrid.vue'
</script>

✔ 페이지에서는

  • 비즈니스 몰라도 됨
  • Grid 컴포넌트 조립만

6️⃣ 데이터 흐름 한눈에 보기

UserListPage
   ↓
UserGrid
   ↓
useAgGridInfinite (composable)
   ↓
userStore (Pinia)
   ↓
userService (API)
   ↓
Server

7️⃣ 이 구조의 장점 (실무에서 선택되는 이유)

항목이유대용량 성능Infinite Row Model유지보수책임 분리재사용성composable 중심테스트service 분리확장성다른 Grid 쉽게 추가

실무 꿀팁 (중요)

✔ Infinite Grid는 반드시

rowHeight: 40
animateRows: false

❌ 절대 하지 말 것

rowData = hugeArray

✅ 최종 정리 한 줄

AG Grid Infinite Row Model의 정석 구조는

composables에서 datasource, stores에서 상태, services에서 API다




2. Client-Side Row Model + pagination

전체 폴더 구조

src/
├── pages/
│   └── UserListPage.vue
│
├── components/
│   └── grid/
│       └── UserGridClient.vue
│
├── composables/
│   └── useAgGridClient.js
│
├── stores/
│   └── userStore.js
│
├── services/
│   └── userService.js
│
└── main.js

1️⃣ services/

서버에서 “전체 데이터”를 한 번에 가져옴

services/userService.js

export async function fetchAllUsers() {
  const response = await fetch('/api/users/all')
  return response.json()
}

서버 응답 예시

[
  { "id": 1, "name": "Alice", "email": "a@test.com" },
  { "id": 2, "name": "Bob", "email": "b@test.com" }
]

2️⃣ stores/

전체 데이터를 메모리에 보관

stores/userStore.js

import { defineStore } from 'pinia'
import { fetchAllUsers } from '@/services/userService'

export const useUserStore = defineStore('user', {
  state: () => ({
    users: []
  }),

  actions: {
    async loadAllUsers() {
      this.users = await fetchAllUsers()
    }
  }
})

✔ Client-Side 방식의 핵심

store에 전체 데이터 보관

3️⃣ composables/

AG Grid 옵션 & pagination 설정 담당

composables/useAgGridClient.js

export function useAgGridClient() {
  const columnDefs = [
    { field: 'id', headerName: 'ID', width: 80 },
    { field: 'name', headerName: '이름' },
    { field: 'email', headerName: '이메일' }
  ]

  const gridOptions = {
    pagination: true,
    paginationPageSize: 20,
    rowHeight: 40,
    animateRows: false
  }

  return {
    columnDefs,
    gridOptions
  }
}

✔ 여기서는

  • pagination 활성화
  • page size 설정

4️⃣ components/

Grid UI 전용 컴포넌트

components/grid/UserGridClient.vue

<template>
  <div class="ag-theme-alpine" style="height: 600px">
    <AgGridVue
      :rowData="rowData"
      :columnDefs="columnDefs"
      :gridOptions="gridOptions"
    />
  </div>
</template>

<script setup>
import { AgGridVue } from 'ag-grid-vue3'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/userStore'
import { useAgGridClient } from '@/composables/useAgGridClient'

const userStore = useUserStore()
const { users: rowData } = storeToRefs(userStore)

const { columnDefs, gridOptions } = useAgGridClient()
</script>

✔ 특징

  • rowModelType ❌ (기본값 = clientSide)
  • pagination 버튼 자동 생성

5️⃣ pages/

페이지 진입 시 전체 데이터 로딩

pages/UserListPage.vue

<template>
  <div>
    <h2>사용자 목록 (Client-Side Pagination)</h2>
    <UserGridClient />
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import { useUserStore } from '@/stores/userStore'
import UserGridClient from '@/components/grid/UserGridClient.vue'

const userStore = useUserStore()

onMounted(() => {
  userStore.loadAllUsers()
})
</script>

6️⃣ 데이터 흐름

UserListPage (onMounted)
   ↓
userStore.loadAllUsers()
   ↓
userService.fetchAllUsers()
   ↓
store.users (전체 데이터)
   ↓
AG Grid (pagination 처리)

7️⃣ 화면에서 자동으로 생기는 것들

AG Grid가 자동 제공

  • ⬅ 이전 / 다음 페이지
  • 페이지 번호
  • 페이지당 row 수
[ < ] 1 2 3 4 [ > ]






LIST

Copyright © 2014 visionboy.me All Right Reserved.