2025年5月

요구 사항 기반 테스트 관리 소프트웨어 Visure의 기능 및 관련 자료 정리 해주세요

요구 사항 기반 테스트 관리 소프트웨어인 Visure Requirements ALM은 복잡한 시스템과 소프트웨어 개발 프로젝트를 위한 요구사항 관리(Requirements Management), 테스트 관리(Test Management), 리스크 관리(Risk Management) 및 추적성(Traceability)을 제공하는 통합 Application Lifecycle Management(ALM) 플랫폼입니다.

monitorStore.ts

import { makeAutoObservable } from "mobx";

export type MonitoringCycle = 'DAILY' | 'WEEKLY' | 'MONTHLY';

export class MonitorStore {
cycle: MonitoringCycle = 'DAILY';
startTime: string = '';
dayOfWeek: string = 'MON';
dayOfMonth: number = 1;

constructor() {

makeAutoObservable(this);

}

setCycle = (cycle: MonitoringCycle) => {

this.cycle = cycle;

};

setStartTime = (time: string) => {

this.startTime = time;

};

setDayOfWeek = (day: string) => {

this.dayOfWeek = day;

};

setDayOfMonth = (day: number) => {

this.dayOfMonth = day;

};
}

export const monitorStore = new MonitorStore();
Monitor.tsx
tsx
복사
편집
import React from 'react';
import { observer } from 'mobx-react-lite';
import { monitorStore } from './monitorStore';

export const Monitor = observer(() => {
const store = monitorStore;

const handleCycleChange = (e: React.ChangeEvent) => {

store.setCycle(e.target.value as any);

};

const handleStartTimeChange = (e: React.ChangeEvent) => {

store.setStartTime(e.target.value);

};

const handleDayOfWeekChange = (e: React.ChangeEvent) => {

store.setDayOfWeek(e.target.value);

};

const handleDayOfMonthChange = (e: React.ChangeEvent) => {

store.setDayOfMonth(Number(e.target.value));

};

const renderDayOfWeekOptions = () => {

const days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'];
return (
  <div>
    <label>요일 선택: </label>
    {days.map(day => (
      <label key={day} style={{ marginRight: 10 }}>
        <input
          type="radio"
          name="dayOfWeek"
          value={day}
          checked={store.dayOfWeek === day}
          onChange={handleDayOfWeekChange}
        />
        {day}
      </label>
    ))}
  </div>
);

};

const renderDayOfMonthOptions = () => {

return (
  <div>
    <label>날짜 선택: </label>
    <select value={store.dayOfMonth} onChange={handleDayOfMonthChange}>
      {Array.from({ length: 31 }, (_, i) => i + 1).map(day => (
        <option key={day} value={day}>
          {day}일
        </option>
      ))}
    </select>
  </div>
);

};

return (

<div style={{ padding: 20, fontFamily: 'Arial' }}>
  <h3>모니터링 주기 설정</h3>

  <div style={{ marginBottom: 10 }}>
    <label>주기 선택: </label>
    {(['DAILY', 'WEEKLY', 'MONTHLY'] as const).map((type) => (
      <label key={type} style={{ marginRight: 10 }}>
        <input
          type="radio"
          name="cycle"
          value={type}
          checked={store.cycle === type}
          onChange={handleCycleChange}
        />
        {type}
      </label>
    ))}
  </div>

  <div style={{ marginBottom: 10 }}>
    <label>Start Time: </label>
    <input
      type="text"
      value={store.startTime}
      onChange={handleStartTimeChange}
      placeholder="HH:mm"
    />
  </div>

  {store.cycle === 'WEEKLY' && renderDayOfWeekOptions()}
  {store.cycle === 'MONTHLY' && renderDayOfMonthOptions()}
</div>

);
});
index.tsx
tsx
복사
편집
import React from 'react';
import ReactDOM from 'react-dom';
import { Monitor } from './Monitor';

ReactDOM.render(
<React.StrictMode>

<Monitor />

</React.StrictMode>,
document.getElementById('root')
);

아래는 주어진 사양을 바탕으로 설계된 Monitoring 수행 Java 배치 프로그램의 기본 구조와 함께, 관련 MariaDB 테이블 생성 SQL을 제공합니다. 이 프로그램은 모니터링 항목에 따라 주기적으로 SQL을 실행하고, 그 결과를 임계치 기준으로 평가하여 결과를 기록합니다.


🔧 1. MariaDB 테이블 생성 SQL

-- 모니터링 항목 테이블
CREATE TABLE monitoring_item (
    item_id            BIGINT AUTO_INCREMENT PRIMARY KEY,
    system             VARCHAR(100),
    module             VARCHAR(100),
    testcase_id        VARCHAR(100),
    testcase_name      VARCHAR(200),
    activity_no        VARCHAR(100),
    monitoring_name    VARCHAR(200),
    description        TEXT,
    action_guide       TEXT,
    use_yn             CHAR(1) DEFAULT 'Y',
    monitoring_cycle   VARCHAR(10), -- DAILY, WEEKLY, MONTHLY
    cycle_week         VARCHAR(10), -- 월: MON, 화: TUE ...
    cycle_day          INT, -- 1~31 (Monthly)
    start_time         VARCHAR(4), -- HHMM 형식
    time_interval      INT, -- 분 단위
    monitoring_from    DATE,
    monitoring_to      DATE,
    exclude_from       DATE,
    exclude_to         DATE,
    sql_text           TEXT,
    green_operator     VARCHAR(2), -- <, <=, =, >=, >
    green_value        DOUBLE,
    red_operator       VARCHAR(2),
    red_value          DOUBLE,
    created_at         TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 모니터링 실행 결과 테이블
CREATE TABLE monitoring_result (
    result_id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    item_id            BIGINT,
    run_date           DATE,
    run_time           TIME,
    run_rank           INT,
    status             VARCHAR(20), -- 대기, 실행중, 성공, 실패
    result_value       DOUBLE,
    result_level       VARCHAR(10), -- GREEN, RED, NONE
    result_message     TEXT,
    created_at         TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (item_id) REFERENCES monitoring_item(item_id)
);

🚀 2. Java 배치 프로그램 개요

기술 스택

  • OpenJDK 11
  • Spring Boot (or Spring Batch if 원하는 경우)
  • iBatis 또는 MyBatis
  • JDBC

🔁 JobSchedulerService.java

@Service
public class JobSchedulerService {
    @Autowired
    private MonitoringRepository monitoringRepository;

    public void runBatchJob() {
        LocalDate today = LocalDate.now();
        LocalTime now = LocalTime.now();
        List<MonitoringItem> items = monitoringRepository.findExecutableItems(today, now);

        for (MonitoringItem item : items) {
            if (!isInExcludePeriod(item)) {
                MonitoringResult result = executeMonitoring(item, now);
                monitoringRepository.saveResult(result);
            }
        }
    }

    private boolean isInExcludePeriod(MonitoringItem item) {
        LocalDate now = LocalDate.now();
        return (item.getExcludeFrom() != null && item.getExcludeTo() != null)
            && !now.isBefore(item.getExcludeFrom()) && !now.isAfter(item.getExcludeTo());
    }

    private MonitoringResult executeMonitoring(MonitoringItem item, LocalTime now) {
        MonitoringResult result = new MonitoringResult();
        result.setItemId(item.getId());
        result.setRunDate(LocalDate.now());
        result.setRunTime(now);
        result.setStatus("실행중");

        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(item.getSqlText())) {

            double value = 0.0;
            if (rs.next()) {
                value = rs.getDouble(1);
            }

            result.setResultValue(value);
            result.setResultLevel(evaluateLevel(value, item));
            result.setStatus("성공");
            result.setResultMessage("정상 수행");

        } catch (Exception e) {
            result.setStatus("실패");
            result.setResultMessage(e.getMessage());
        }

        return result;
    }

    private String evaluateLevel(double value, MonitoringItem item) {
        if (compare(value, item.getRedOperator(), item.getRedValue())) {
            return "RED";
        } else if (compare(value, item.getGreenOperator(), item.getGreenValue())) {
            return "GREEN";
        }
        return "NONE";
    }

    private boolean compare(double actual, String operator, double threshold) {
        return switch (operator) {
            case ">" -> actual > threshold;
            case ">=" -> actual >= threshold;
            case "<" -> actual < threshold;
            case "<=" -> actual <= threshold;
            case "=" -> actual == threshold;
            default -> false;
        };
    }
}

🕐 실행 조건 쿼리 (MyBatis XML 또는 Mapper)

<!-- 실행 대상 항목 조회 -->
<select id="findExecutableItems" resultType="MonitoringItem">
SELECT *
FROM monitoring_item
WHERE use_yn = 'Y'
  AND CURDATE() BETWEEN monitoring_from AND monitoring_to
  AND (monitoring_cycle = 'DAILY'
       OR (monitoring_cycle = 'WEEKLY' AND cycle_week = #{dayOfWeek})
       OR (monitoring_cycle = 'MONTHLY' AND cycle_day = #{dayOfMonth}))
  AND #{nowTime} >= start_time
  AND MOD(TIMESTAMPDIFF(MINUTE, STR_TO_DATE(start_time, '%H%i'), #{nowTime}), time_interval) = 0
</select>

🗓️ 3. 배치 스케줄링 (Spring 기반)

@Component
public class BatchScheduler {

    @Autowired
    private JobSchedulerService jobSchedulerService;

    // 매 1분마다 실행
    @Scheduled(cron = "0 */1 * * * *")
    public void scheduleBatch() {
        jobSchedulerService.runBatchJob();
    }
}

✅ 요약

구성 요소내용
테이블monitoring_item, monitoring_result
주기DAILY / WEEKLY / MONTHLY (시간 및 분 기준 제어)
제외 기간exclude_from, exclude_to 반영
결과 평가Green / Red 기준값 비교 연산 지원
배치 실행매 1분마다 수행, 자정에는 monitoring_item 기준으로 판단

필요 시 다음 항목도 확장 가능합니다:

  • Slack 또는 이메일 알림 연동
  • 모니터링 실패 자동 재시도
  • 실행 이력 별 통계

필요하다면 이 구조에 맞는 React 관리자 화면 예시 UI, 또는 iBatis Mapper XML 파일도 도와드릴 수 있습니다. 추가 요청 주시면 이어서 작성해드릴게요!

info No lockfile found. error Running this command will add the dependency to the workspace root rather than the workspace itself, which might not be what you want - if you really meant it, make it explicit by running this command again with the -W flag (or --ignore-workspace-root-check). info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.

오류 해결 방법
yarn add react@17.0.2 -W
or
yarn add react@17.0.2 --ignore-workspace-root-check

import React, { Component } from 'react';
import { AgGridReact } from 'ag-grid-react';
import { ColDef } from 'ag-grid-community';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

interface RowData {
id: number;
name: string;
chkResult: 'N' | 'W' | 'E';
}

interface State {
columnDefs: ColDef[];
rowData: RowData[];
}

export class SignalGrid extends Component<{}, State> {
constructor(props: {}) {

super(props);

this.state = {
  columnDefs: [
    { field: 'id', headerName: 'ID', width: 80 },
    { field: 'name', headerName: 'Name', width: 150 },
    {
      field: 'chkResult',
      headerName: 'Result',
      cellRenderer: this.signalRenderer,
      width: 150,
    },
  ],
  rowData: [
    { id: 1, name: 'Item A', chkResult: 'N' },
    { id: 2, name: 'Item B', chkResult: 'W' },
    { id: 3, name: 'Item C', chkResult: 'E' },
  ],
};

}

// 셀 렌더러 함수
signalRenderer(params: any) {

const value = params.value;
let color = 'gray';

if (value === 'W') color = 'gold';
else if (value === 'E') color = 'red';

return (
  `<span style="display: flex; align-items: center;">
    <span style="
      display: inline-block;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: ${color};
      margin-right: 6px;
    "></span>
    ${value}
  </span>`
);

}

render() {

return (
  <div
    className="ag-theme-alpine"
    style={{ height: 300, width: 400 }}
  >
    <AgGridReact
      columnDefs={this.state.columnDefs}
      rowData={this.state.rowData}
    />
  </div>
);

}
}