검색

레이블이 vuetify3인 게시물을 표시합니다. 모든 게시물 표시
레이블이 vuetify3인 게시물을 표시합니다. 모든 게시물 표시

2024년 10월 29일

코딩 - Vue3 : AG-Grid 커뮤니티 버전 기반의 페이징 그리드 컴포넌트 만들기

 AG Grid Community 버전은 매우 인기 있는 오픈소스 그리드 컴포넌트로, 다양한 기능을 제공하지만 상업적인 용도로 사용할 때는 프로 버전으로의 업그레이드가 필요할 수도 있다. 커뮤니티 버전의 이점과 한계를 살펴보면 다음과 같다.

AG Grid Community  버전의 이점

  1. 무료 사용: 커뮤니티 버전은 오픈소스로 제공되므로 무료로 사용할 수 있다. 중소규모의 프로젝트나 상업적 용도가 아닌 경우에 적합.
  2. 강력한 성능:  AG Grid 는 많은 양의 데이터를 효율적으로 처리할 수 있는 그리드 컴포넌트로 기본적인 CRUD 작업을 포함하여 빠른 렌더링 성능을 지원.
  3. 광범위한 기능:
    • 기본적인 정렬, 필터링, 페이징, 그룹핑, 편집 등 그리드에서 필요한 대부분의 기능을 포함
    • 다양한 컬럼 레이아웃과 사용자 정의 셀 렌더러 등 유연한 데이터 표현이 지원
  4. 활발한 커뮤니티: AG Grid는 대규모 사용자 커뮤니티를 보유하고 있어 문제 발생 시 도움을 얻기 용의.
  5. 다양한 프레임워크 지원: React, Angular, Vue, 순수 자바스크립트 등 여러 프레임워크에서 사용할 수 있는 유연함

AG Grid Community 버전의 한계

  1. 고급 기능 제한: 커뮤니티 버전에는 고급 기능이 포함되어 있지 않음. 특히 아래 기능들이 프로 버전에만 포함.
    • 서버 사이드 모델(Server-Side Model): 대규모 데이터 집합에서 서버와 연동하여 그리드 데이터를 관리하는 기능.
    • 익스포트 기능: CSV, Excel 등의 형식으로 데이터를 내보내는 기능이 제한.
    • Tree Data 구조: 트리 구조의 데이터를 처리할 수 있는 고급 데이터 표현 방식 미지원.
    • 엔터프라이즈 레벨 필터링: 고급 필터링 기능은 프로 버전에만 포함.
  2. 기술 지원 부족: 커뮤니티 버전에서는 공식적인 기술 지원을 받을 수 없음. 문제 해결 시 커뮤니티에 의존해야 함.
  3. 사용 제한: 상업적 프로젝트에서 고급 기능이 필요하거나 성능 최적화 및 유지보수가 중요한 경우, 커뮤니티 버전만으로는 한계가 있을 수 있음.
AG Grid Community 버전은 무료로 사용할 수 있고, 기본적인 그리드 기능을 모두 제공하기 때문에 많은 프로젝트에서 유용하다. 그러나 고급 기능이나 대규모 데이터 처리가 필요한 경우 프로 버전으로 업그레이드하는 것이 필요하다. 

유료 버전에서는 rowModelType을 "serverSide" 로 설정하고 그리드가 준비되면 getRows 함수 내에서 서버 요청을 수행하고 결과를 그리드에 전달하는 데이터소스를 api.setServerSideDatasource() 함수를 사용하여 설정하는 방식으로 쉽게 구현할 수 있다.

AG Grid 커뮤니티 버전에서 서버 페이징은 (무한 스크롤을 구현하는 방법) rowModelType 값으로  "infinite" 을 사용하여 비슷한 방법으로 구현할 수 있다. 
  1. rowModelType을 "infinite"로 설정하여 그리드가 데이터를 한 번에 모두 로드하지 않고, 필요한 만큼 서버에서 요청하도록 한다.
  2. getRows 함수는 그리드가 추가 데이터를 요청할 때마다 서버에 데이터를 가져오는 역할을 한다.
  3. dataSource는그리드가 준비될 때 설정하고, 그리드가 데이터를 필요로 할 때마다 자동으로 서버에 데이터를 요청한다. ( 그리드 준비 여부는 onGridReady 이벤트를 사용)
개발 단계를 구체적으로 설명하면 아래와 같다.

설명 코드 예시
1 프로젝트에 AG Grid 설치 npm install --save ag-grid-community ag-grid-vue
2 컬럼 정의 및 그리드 옵션 설정 columnDefsgridOptions<script setup lang="ts">에서 정의하고 ref로 선언
3 Infinite Row Model 활성화 gridOptions에서 rowModelType'infinite'로 설정하고, paginationPageSizecacheBlockSize를 정의
4 onGridReady 함수에서 서버 데이터 소스 설정 onGridReady에서 getRows 함수가 포함된 dataSource를 설정
5 getRows 함수에서 서버 데이터 요청 params.startRow, params.endRow를 기반으로 페이지와 페이지 크기를 서버에 요청
6 서버 응답 처리 서버의 데이터 응답을 받아 params.successCallback(data.rows, data.totalCount)을 호출하여 그리드에 전달
7 Vue 컴포넌트 마운트 시 그리드 참조 설정 onMounted에서 gridRef를 통해 그리드 API에 접근하여 초기 설정 완료


매번 서버 페이징이 필요한 경우 위와 같이 코딩하는 것은 여간 불편한 것이 아니다. 이런 이유에서 추상 형태의 DataSource 을 정의하고 공통으로 적용할 설정을 미리 정의하고 필요한 값만 전달하여 페이징 그리드를 구현하는 컴포넌트를 만들어 보았다.


아래는 PageableGridContent.vue 에서 사용하는 서버에서 데이터를 가져오는 PageableDataSource 에 대한 추상 AbstractPageDataSource 이다. 이를 통하여 좀더 쉽게 서버에서 데이터를 가져오는 데이터소스 구현을 가능하게 한다. ( 구현체에서는 규칙을 벗어 나지 않는 경우라면 서버 엔트포인트 값을 리턴하는 getPatchUrl () 만 구현하면 된다. 서버 프로그램은 스프링의 Pageable  형식으로 데이터를 리턴하는 것을 가정 하여 데이터소스 추상을 구현 했다.


위의 컴포넌트를 사용하여 1) 컴포넌트 임포트 2)  그리드 컬럼 정의 3) 태그를 사용하여 컴포넌트를 정의하는 것으로 그리드를 간단하게 구현 할 수 있다. 또한 컴포넌트에 직접 제어가능한 함수를 사용하여 새로고침, 선택된 데이터 접근, 필터 초기화 등을 제어할 수 있다. 그리드 컬럼에서 단수 데이터 출력이 아닌 버튼과 또는 이미지 출력 등의 작업이 필요한 경우는 사용자 정의 더러를 정의하고 버튼 클릭등의 이벤트는 이벤트 리스트너를 통하여 구현하였다. 


다음은 위의 AbstractPageDataSource 활용하여 구현한 예이다. 아래는 목록을 조회하는 기능과 추가로 여러 함수를 포함하고 있다.

구현 클래스는 추가 기능이 필요하면 PageableDataSource 을 포함하여 새로운 타입을 정의하고 이를 구현하는 클래스를 AbstractPageableDataSource 상속하여 만든다. 구현 클래스는 getFetchUrl 함수를 구현하는 것으로 완성되며 this 이슈를 해결하기 위하여 반인딩을 사용하여 스토어를 생성하게 한다.
  1. 필요한 새로운 PageableDataSource 을 포함하는 type 생성
  2. getFetchUrl 을 포함 새로운 type 을 구현하는 클래스 생성 
  3. 구현 클래스를 사용하여 스토어 생성

import { RoleModel } from "@/types/studio/RoleModel";
import {
API_HEADERS,
authHeader
} from "@/util/helpers";
import axios from "axios";
import { defineStore } from "pinia";

import type {
PageableDataSource
} from "@/types/studio/index";
import { AbstractPageDataSource } from "../AbstractPageDataSource";

// getById,getByName,loadById,saveOrUpdate 추가을 추가하여 IPageableRoleDataSource를 정의
type IPageableRoleDataSource = PageableDataSource & {
getById: (roleId: number) => RoleModel;
getByName: (name: string) => RoleModel;
loadById: (roleId: number) => Promise<RoleModel>;
saveOrUpdate: (item: RoleModel) => Promise<void>;
};
const baseUrl = `${import.meta.env.VITE_API_URL}/data/secure/mgmt/security/roles:find`;

class PageableRoleDataSource
extends AbstractPageDataSource
implements IPageableRoleDataSource
{
getById(roleId: number) {
return this.dataItems.value.find(
(item: RoleModel) => item.roleId === roleId
);
}

getByName(name: string) {
return this.dataItems.value.find((item: RoleModel) => item.name === name);
}

async loadById(roleId: number) {
const headers = { ...API_HEADERS, ...authHeader() };
const category = await axios
.get(
`${import.meta.env.VITE_API_URL}/data/secure/mgmt/security/roles/${roleId}`,
{ headers: headers }
)
.then((response) => {
const data = response.data;
return data;
})
.catch((err) => {
this.alertStore.error(err);
});

return category;
}

async saveOrUpdate(item: RoleModel) {
const headers = { ...API_HEADERS, ...authHeader() };
await axios
.post(
`${import.meta.env.VITE_API_URL}/data/secure/mgmt/security/roles/${item.roleId}`,
JSON.stringify(item),
{ headers: headers }
)
.then((response) => {
const dataItems = response.data;
})
.catch((err) => {
this.alertStore.error(err);
});
}

// API 엔드포인트 URL을 제공. 이 클래스만 구현하면 그리드는 정상 동작한다.
getFetchUrl(): string {
return baseUrl;
}
}

// 필수로 표기된 부분은 반드시 동일하게 리턴해야 한다.
export const usePageableRolesStore = defineStore("mgmt-pageable-roles-store", () => {
const dataSource = new PageableRoleDataSource();
return {
isLoaded: dataSource.isLoaded, // 필수
dataItems: dataSource.dataItems, // 필수
total: dataSource.total, // 필수
pageSize: dataSource.pageSize,// 필수
getById: dataSource.getById.bind(dataSource),
getByName: dataSource.getByName.bind(dataSource),
loadById: dataSource.loadById.bind(dataSource),
saveOrUpdate: dataSource.saveOrUpdate.bind(dataSource),
setPage: dataSource.setPage.bind(dataSource),// 필수
setSort: dataSource.setSort.bind(dataSource),// 필수
setFilter: dataSource.setFilter.bind(dataSource),// 필수
fetch: dataSource.fetch.bind(dataSource), // 필수
};
});

예제 프로그램 실행 화면은 아래와 같다.


2023년 11월 20일

코딩 - Spring 기반 웹 프로그램에서 Actuator 기반 모니터링 구현하기

1. Spring Boot Actuator 활성화

Spring Boot 는 애플리케이션을 운영환경으로 전환할 때 애플리케이션을 모니터링하고 관리하는 데 도움이 되는 여러 가지 추가 기능이 포함되어 있다. 이들 기능은 Spring Boot Actuator 모듈에 대한 의존성을 추가하여 활성화 할 수 있다. 의존성은 spring-boot-starter-actuator 스타터를 사용한다.

① 의존성 추가
spring-boot-starter-actuator 의존성을 프로젝트의 pom.xml 또는 build.gradle 파일에 추가한다. 이렇게 하면 Spring Actuator 관련 기능을 프로젝트에 포함시킬 수 있다.


Spring Boot Actuator가 제공하는 주요 기능은 다음과 같다:
  • 엔드포인트(Endpoints): Spring Boot Actuator는 다양한 HTTP 엔드포인트를 제공하여 애플리케이션의 상태 정보를 노출한다. 예를 들어, /actuator/health, /actuator/info, /actuator/metrics, /actuator/beans, /actuator/env 등의 엔드포인트를 통해 건강 상태, 애플리케이션 정보, 메트릭 데이터, 빈 정보, 환경 속성 정보 등을 확인할 수 있다.
  • 메트릭 수집: Spring Boot Actuator는 애플리케이션의 성능과 동작에 관한 다양한 메트릭 데이터를 수집하고 노출한다. 이러한 메트릭 데이터는 모니터링 도구나 시각화 도구와 연동하여 성능 향상 및 문제 해결에 도움을 줄 수 있다.
  • 응용 프로그램 정보: /actuator/info 엔드포인트를 통해 사용자 정의 애플리케이션 정보를 노출.
  • 애플리케이션의 건강 검사: /actuator/health 엔드포인트를 사용하여 애플리케이션의 건강 상태를 확인하고 이에 따른 조치를 취할 수 있다.
  • 환경 속성 관리: /actuator/env 엔드포인트를 통해 애플리케이션 환경 속성을 확인하고 조작할 수 있다.

 ❉ HTTP 엔드포인트는 일반적으로 URL 형식을 가지며, 웹 주소의 일부다. 예를 들어, "https://example.com/api/data"와 같은 형태가 될 수 있다.

2. 설정 및 보안 구성

Spring Boot Actuator 사용을 위해서 의존성을 추가하는 것으로는 충분하지 않으며  몇 가지 설정과 보안을 추가적으로 구성해야 한다.

② 구성 파일 설정
Spring Actuator 의 기능 중 일부는 구성 파일(application.properties 또는 application.yml)에서 활성화하거나 구성해야 한다. 예를 들어, management.endpoints.web.exposure.include 속성을 사용하여 공개할 엔드포인트를 구성하거나, 엔드포인트의 경로를 사용자 정의할 수 있다.

③ 보안 설정
Spring Actuator 엔드포인트에 접근하는 데 보안을 구성해야 할 수 있습니다. 기본적으로 모든 엔드포인트는 보안이 적용되지 않았으며, 이를 구성하여 액세스 제한, 인증 또는 권한 부여를 설정할 수 있다.

④ 엔드포인트 사용
Spring Actuator의 엔드포인트를 사용하려면 브라우저 또는 HTTP 클라이언트를 통해 엔드포인트에 요청을 보낼 수 있다. 엔드포인트는 /actuator 경로 아래에 위치하며, 예를 들어 /actuator/health 엔드포인트는 애플리케이션의 건강 상태 정보를 제공한다. 다음은 health,beans,metrics 기능들을 웹 서비스 엔트포인트로 사용하고 jmx 는 사용하지 않는 경우 설정이다.


3. 모니터링 구현하기

모니터링은 Vue 기술을 사용하여 구현했다. 모니터링 포트는 별도로 지정하는 것이 보안 및 모니터링 행위가 주는 영향을 최소화하기위하여 좋을 것 같다.

◼︎ 환경

  • Computing Power
    • Model : MacBook Pro (14-inch, 2021)
    • CPU : Apple M1 Pro
    • MENORY : 16GB
    • DISK : 512 GB SSD
    • OS : macOS 13.2.4 (22F66)
  • TOOLS : Visual Studio Code, Java 11, Gradle, Docker
  • Version Control : GitHub
  • Back-End
    • Programming Language : Java
    • Framework : Spring Boot 2.7.12
    • DBMS : MySql 8.0.33 
  • Front-End
    • Vue3 , Vuetify3, pinia, axios, apexcharts
웹 프로그램과 모니터링 포트를 같이 사용하는 경우
<웹 프로그램과 모니터링 포트를 같이 사용하는 경우>

웹과 모니터링 포트를 같이 사용하는 경우 모니터링 API 호출로 인하여 http.server.requests 값이 상승하는 것을 확인할 수 있었다.

<웹 프로그램과 모니터링 포트를 다르게 사용하는 경우>


관련 코드는 아래와 같다.