분류프로젝트 아래의 글

프로젝트의 요구사항을 고려했을 때, 효율적인 테스트 도구 시스템을 구축하기 위한 데이터베이스 설계 및 주요 로직 단계를 Java, Spring, iBatis, MariaDB 환경에 맞춰 상세히 정리해 드릴게요.


1. 데이터베이스 설계 (MariaDB)

테스트 도구의 다양한 파라미터와 계층 구조를 효율적으로 관리하기 위해 다음 테이블들을 설계할 수 있습니다.

1.1 TEST_PLAN 테이블

  • 설명: 최상위 테스트 계획 정보를 저장합니다.
  • 컬럼:

    • PLAN_ID (BIGINT PK, AUTO\_INCREMENT): 테스트 계획 ID
    • PLAN_NAME (VARCHAR(255) NOT NULL): 테스트 계획 이름
    • DESCRIPTION (TEXT): 테스트 계획 설명
    • CREATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 생성일시
    • UPDATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP ON UPDATE CURRENT\_TIMESTAMP): 수정일시

1.2 TEST_SCENARIO 테이블

  • 설명: 각 테스트 계획에 속하는 시나리오 정보를 저장합니다.
  • 컬럼:

    • SCENARIO_ID (BIGINT PK, AUTO\_INCREMENT): 테스트 시나리오 ID
    • PLAN_ID (BIGINT FK): 소속 테스트 계획 ID (TEST_PLAN.PLAN_ID 참조)
    • SCENARIO_NAME (VARCHAR(255) NOT NULL): 테스트 시나리오 이름
    • DESCRIPTION (TEXT): 테스트 시나리오 설명
    • CREATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 생성일시
    • UPDATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP ON UPDATE CURRENT\_TIMESTAMP): 수정일시

1.3 TEST_CASE 테이블

  • 설명: 각 시나리오에 속하는 테스트 케이스 정보를 저장합니다.
  • 컬럼:

    • CASE_ID (BIGINT PK, AUTO\_INCREMENT): 테스트 케이스 ID
    • SCENARIO_ID (BIGINT FK): 소속 테스트 시나리오 ID (TEST_SCENARIO.SCENARIO_ID 참조)
    • CASE_NAME (VARCHAR(255) NOT NULL): 테스트 케이스 이름
    • DESCRIPTION (TEXT): 테스트 케이스 설명
    • CREATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 생성일시
    • UPDATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP ON UPDATE CURRENT\_TIMESTAMP): 수정일시

1.4 TEST_ACTIVITY 테이블

  • 설명: 각 테스트 케이스에 속하는 개별 테스트 활동(SQL 템플릿 포함) 정보를 저장합니다.
  • 컬럼:

    • ACTIVITY_ID (BIGINT PK, AUTO\_INCREMENT): 테스트 활동 ID
    • CASE_ID (BIGINT FK): 소속 테스트 케이스 ID (TEST_CASE.CASE_ID 참조)
    • ACTIVITY_NAME (VARCHAR(255) NOT NULL): 테스트 활동 이름
    • SQL_TEMPLATE (LONGTEXT NOT NULL): 실행될 SQL 템플릿 (파라미터 포함)
    • CREATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 생성일시
    • UPDATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP ON UPDATE CURRENT\_TIMESTAMP): 수정일시

1.5 PARAMETER_DEFINITION 테이블

  • 설명: 각 계층(Plan, Scenario, Case, Activity)에서 정의된 파라미터 정보를 저장합니다.
  • 컬럼:

    • PARAM_DEF_ID (BIGINT PK, AUTO\_INCREMENT): 파라미터 정의 ID
    • REFER_ID (BIGINT NOT NULL): 참조하는 엔티티 ID (Plan ID, Scenario ID, Case ID, Activity ID)
    • REFER_TYPE (VARCHAR(20) NOT NULL): 참조하는 엔티티 타입 (PLAN, SCENARIO, CASE, ACTIVITY)
    • PARAM_NAME (VARCHAR(100) NOT NULL): 파라미터 이름 (예: testId, ADD_CONDITION_1, userId)
    • PARAM_TYPE (VARCHAR(50) NOT NULL): 파라미터 타입 (SYSTEM, CONDITIONAL, STATIC, DYNAMIC, SQL_QUERY)
    • PARAM_VALUE (TEXT): 파라미터 값 (고정값, SQL 쿼리, 동적 SQL 등. 타입에 따라 의미 다름)
    • DESCRIPTION (TEXT): 파라미터 설명
    • CREATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 생성일시
    • UPDATED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP ON UPDATE CURRENT\_TIMESTAMP): 수정일시

1.6 TEST_EXECUTION_RESULT 테이블

  • 설명: 테스트 실행 결과 및 실행된 SQL, 최종 파라미터 값을 저장합니다.
  • 컬럼:

    • EXEC_ID (BIGINT PK, AUTO\_INCREMENT): 실행 결과 ID
    • ACTIVITY_ID (BIGINT FK): 실행된 테스트 활동 ID (TEST_ACTIVITY.ACTIVITY_ID 참조)
    • EXECUTED_SQL (LONGTEXT NOT NULL): 최종 실행된 SQL 문장
    • PARAMETER_VALUES (JSON): 실행 시 사용된 최종 파라미터 값들 (JSON 형태로 저장하여 유연성 확보)
    • EXECUTION_STATUS (VARCHAR(50) NOT NULL): 실행 상태 (SUCCESS, FAILED 등)
    • RESULT_DATA (LONGTEXT): SQL 실행 결과 데이터 (필요시 저장)
    • ERROR_MESSAGE (TEXT): 에러 발생 시 메시지
    • EXECUTED_AT (TIMESTAMP DEFAULT CURRENT\_TIMESTAMP): 실행일시

2. 주요 로직 단계별 정리 (Java, Spring, iBatis)

Spring Boot와 MyBatis(iBatis의 후속 프레임워크)를 기반으로 주요 로직 단계를 설명합니다.

2.1. 테스트 계획 수립 및 파라미터 정의 (SQL 템플릿 및 파라미터 정의)

개요:
사용자가 웹 UI를 통해 테스트 계획, 시나리오, 케이스, 액티비티를 정의하고, 각 단계에서 필요한 파라미터들을 설정합니다. 이 정보들은 위에서 설계한 DB 테이블에 저장됩니다.

주요 컴포넌트:

  • Spring REST Controller: 클라이언트의 요청을 받아 테스트 계획, 시나리오, 케이스, 액티비티 및 파라미터 정의를 처리합니다.
  • Service Layer: 비즈니스 로직을 담당하며, DTO를 통해 데이터를 주고받습니다.
  • MyBatis Mapper: DB와 상호작용하여 데이터를 저장하고 조회합니다.

저장 예시 (MyBatis Mapper XML):

<insert id="insertTestPlan" useGeneratedKeys="true" keyProperty="planId">
    INSERT INTO TEST_PLAN (PLAN_NAME, DESCRIPTION)
    VALUES (#{planName}, #{description})
</insert>

<insert id="insertParameterDefinition">
    INSERT INTO PARAMETER_DEFINITION (REFER_ID, REFER_TYPE, PARAM_NAME, PARAM_TYPE, PARAM_VALUE, DESCRIPTION)
    VALUES (#{referId}, #{referType}, #{paramName}, #{paramType}, #{paramValue}, #{description})
</insert>

로직 흐름:

  1. 사용자가 UI를 통해 테스트 계획 생성 및 이름, 설명 입력.
  2. TestPlanController가 요청을 받아 TestPlanService 호출.
  3. TestPlanServiceTestPlanMapper를 통해 TEST_PLAN 테이블에 데이터 삽입.
  4. 유사한 방식으로 TEST_SCENARIO, TEST_CASE, TEST_ACTIVITY 및 각 단계의 PARAMETER_DEFINITION 데이터를 저장. 특히 SQL_TEMPLATETEST_ACTIVITY 테이블에 그대로 저장됩니다.

2.2. 테스트 실행 데이터 생성: 계획 기반 실행 가능한 테스트 세트 생성

개요:
사용자가 특정 테스트 계획을 선택하여 실행을 요청하면, 해당 계획에 속한 모든 시나리오, 케이스, 활동들을 조회하여 실행 가능한 테스트 세트를 구성합니다. 이 단계에서는 실제 SQL 실행은 하지 않고, 실행 준비를 위한 데이터를 로드합니다.

주요 컴포넌트:

  • Spring REST Controller: 테스트 실행 요청을 받습니다.
  • Service Layer: 실행할 테스트 세트를 구성하는 핵심 로직을 포함합니다.
  • MyBatis Mapper: 계층별 정보와 파라미터 정의를 조회합니다.

조회 예시 (MyBatis Mapper XML):

<resultMap id="activityWithParams" type="com.example.testtool.model.TestActivity">
    <id property="activityId" column="ACTIVITY_ID"/>
    <result property="activityName" column="ACTIVITY_NAME"/>
    <result property="sqlTemplate" column="SQL_TEMPLATE"/>
    <collection property="parameters" ofType="com.example.testtool.model.ParameterDefinition">
        <id property="paramDefId" column="PARAM_DEF_ID"/>
        <result property="paramName" column="PARAM_NAME"/>
        <result property="paramType" column="PARAM_TYPE"/>
        <result property="paramValue" column="PARAM_VALUE"/>
        </collection>
</resultMap>

<select id="getActivitiesWithParametersByPlanId" resultMap="activityWithParams">
    SELECT
        ta.ACTIVITY_ID, ta.ACTIVITY_NAME, ta.SQL_TEMPLATE,
        pd.PARAM_DEF_ID, pd.PARAM_NAME, pd.PARAM_TYPE, pd.PARAM_VALUE
    FROM TEST_ACTIVITY ta
    JOIN TEST_CASE tc ON ta.CASE_ID = tc.CASE_ID
    JOIN TEST_SCENARIO ts ON tc.SCENARIO_ID = ts.SCENARIO_ID
    JOIN TEST_PLAN tp ON ts.PLAN_ID = tp.PLAN_ID
    LEFT JOIN PARAMETER_DEFINITION pd ON (pd.REFER_ID = ta.ACTIVITY_ID AND pd.REFER_TYPE = 'ACTIVITY')
                                     OR (pd.REFER_ID = tc.CASE_ID AND pd.REFER_TYPE = 'CASE')
                                     OR (pd.REFER_ID = ts.SCENARIO_ID AND pd.REFER_TYPE = 'SCENARIO')
                                     OR (pd.REFER_ID = tp.PLAN_ID AND pd.REFER_TYPE = 'PLAN')
    WHERE tp.PLAN_ID = #{planId}
    ORDER BY ts.SCENARIO_ID, tc.CASE_ID, ta.ACTIVITY_ID, pd.REFER_TYPE DESC, pd.PARAM_NAME
</select>

로직 흐름:

  1. 사용자가 특정 PLAN_ID로 테스트 실행 요청.
  2. TestExecutionServicegetActivitiesWithParametersByPlanId 같은 매퍼를 호출하여 해당 계획에 속한 모든 Activity와 각 계층(Plan, Scenario, Case, Activity)에 정의된 모든 파라미터를 조회합니다.
  3. 조회된 데이터를 바탕으로 ExecutionJob 또는 TestExecution 객체 리스트를 생성합니다. 각 ExecutionJob은 하나의 TestActivity와 해당 Activity에 적용될 가능성이 있는 모든 상위/자신 계층의 파라미터 정보를 포함합니다.

2.3. 파라미터 바인딩: 계층적 파라미터 우선순위 적용

개요:
이 단계는 가장 중요하며, 조회된 SQL 템플릿에 최종적으로 적용될 파라미터 값을 결정합니다. 우선순위는 Test Activity (Level 1) \> Test Case (Level 2) \> Test Scenario (Level 3) \> Test Plan (Level 4) 순으로 적용됩니다.

주요 컴포넌트:

  • ParameterResolver Service/Utility: 파라미터 우선순위를 처리하고 최종 파라미터 맵을 생성합니다.
  • JdbcTemplate (또는 MyBatis의 동적 SQL 기능): SQL 템플릿에 파라미터를 바인딩합니다.

로직 흐름 (ParameterResolver의 역할):

public Map<String, Object> resolveParameters(Long planId, Long scenarioId, Long caseId, Long activityId) {
    Map<String, Object> finalParameters = new HashMap<>();

    // 1. Level 4: Test Plan 파라미터 로드 및 적용
    loadAndApplyParameters(finalParameters, planId, "PLAN");

    // 2. Level 3: Test Scenario 파라미터 로드 및 적용 (동일 이름 시 덮어쓰기)
    loadAndApplyParameters(finalParameters, scenarioId, "SCENARIO");

    // 3. Level 2: Test Case 파라미터 로드 및 적용 (동일 이름 시 덮어쓰기)
    loadAndApplyParameters(finalParameters, caseId, "CASE");

    // 4. Level 1: Test Activity 파라미터 로드 및 적용 (동일 이름 시 덮어쓰기)
    loadAndApplyParameters(finalParameters, activityId, "ACTIVITY");

    // 5. 시스템 파라미터 추가 (항상 최신값으로 적용)
    finalParameters.put("testId", "TEST_" + System.currentTimeMillis());
    finalParameters.put("scenarioId", "SCENARIO_" + System.currentTimeMillis());

    // 6. 런타임 계산 파라미터 (Dynamic Parameters) 처리
    //    PARAM_TYPE이 'DYNAMIC'인 파라미터는 PARAM_VALUE에 정의된 SQL을 실행하여 결과값을 finalParameters에 추가
    processDynamicParameters(finalParameters);

    // 7. SQL 쿼리 결과값 파라미터 (SQL Query Parameters) 처리
    //    PARAM_TYPE이 'SQL_QUERY'인 파라미터는 PARAM_VALUE에 정의된 SQL을 실행하여 결과값을 finalParameters에 추가
    //    이는 SQL 템플릿 치환 이전에 처리되어야 함.
    processSqlQueryParameters(finalParameters);

    return finalParameters;
}

private void loadAndApplyParameters(Map<String, Object> params, Long referId, String referType) {
    if (referId == null) return;
    List<ParameterDefinition> definitions = parameterDefinitionMapper.findByReferIdAndType(referId, referType);
    for (ParameterDefinition def : definitions) {
        // SYSTEM, CONDITIONAL, STATIC 파라미터는 여기서 직접 적용
        if (def.getParamType().equals("SYSTEM") || def.getParamType().equals("STATIC") || def.getParamType().equals("CONDITIONAL")) {
            params.put(def.getParamName(), def.getParamValue());
        }
        // DYNAMIC, SQL_QUERY 타입은 나중에 별도 처리
    }
}

private void processDynamicParameters(Map<String, Object> params) {
    // PARAMETER_DEFINITION 테이블에서 'DYNAMIC' 타입의 파라미터를 찾아
    // PARAM_VALUE에 있는 SQL을 실행하여 결과값을 params에 추가
    // 이 과정은 DataSource 및 JdbcTemplate 필요
    // 예: select sysdate from dual
}

private void processSqlQueryParameters(Map<String, Object> params) {
    // PARAMETER_DEFINITION 테이블에서 'SQL_QUERY' 타입의 파라미터를 찾아
    // PARAM_VALUE에 있는 SQL을 실행하여 결과값을 params에 추가
    // 이 결과는 해당 파라미터가 사용될 SQL 템플릿에 직접 치환될 예정
}

파라미터 처리 순서 (최종 SQL 생성 전):

  1. 시스템 파라미터 치환 ($paramName): $testId, $scenarioId 등 시스템에서 사전에 정의된 파라미터들을 ParameterResolver에서 생성된 최종 파라미터 맵의 값으로 치환합니다.
  2. SQL 쿼리 결과값 파라미터 (SQL Query Parameters) 처리: SQL Query Parameters 타입의 파라미터(PARAM_TYPE = 'SQL_QUERY')는 PARAM_VALUE에 정의된 SQL을 직접 실행하여 그 결과값으로 SQL 템플릿 내의 해당 파라미터 이름을 대체합니다. 이는 SQL 문맥에 따라 다르게 처리될 수 있으므로, SQL 템플릿 치환 전에 미리 처리되어야 합니다.
  3. 조건부 SQL 블록 처리 (#{conditionalSql}): #{ADD_CONDITION_1}과 같이 조건부로 추가되는 SQL 블록을 처리합니다. ParameterResolver에서 결정된 최종 파라미터 맵에 ADD_CONDITION_1이라는 키가 존재하고 값이 비어있지 않다면 해당 값을 SQL에 삽입합니다. 값이 없거나 비어있으면 해당 블록을 제거합니다.
  4. Add-On 파라미터 처리 (WHERE 절 추가): 이 부분은 CONDITIONAL 파라미터와 유사하게 처리될 수 있습니다. PARAM_TYPE = 'CONDITIONAL'로 정의된 파라미터들이 finalParameters 맵에 존재하고 그 값이 유효하다면, SQL 템플릿의 WHERE 1=1 뒤에 해당 조건절을 추가합니다.

2.4. SQL 실행: 동적 SQL 생성 및 데이터베이스 실행

개요:
파라미터 바인딩이 완료된 최종 SQL을 MariaDB에 실행하고 그 결과를 저장합니다.

주요 컴포넌트:

  • Spring JdbcTemplate: 동적으로 생성된 SQL을 실행하기에 적합합니다.
  • MyBatis Dynamic SQL: XML 또는 어노테이션 기반으로 동적 SQL을 생성하는 데 사용할 수 있습니다. 여기서는 JdbcTemplate 사용이 더 유연합니다.
  • Transaction Management: SQL 실행 시 트랜잭션 관리가 필요합니다.

로직 흐름:

@Service
public class SqlExecutorService {

    private final JdbcTemplate jdbcTemplate;
    private final TestExecutionResultMapper resultMapper;

    public SqlExecutorService(JdbcTemplate jdbcTemplate, TestExecutionResultMapper resultMapper) {
        this.jdbcTemplate = jdbcTemplate;
        this.resultMapper = resultMapper;
    }

    @Transactional
    public void executeSqlAndSaveResult(Long activityId, String processedSql, Map<String, Object> finalParameters) {
        String executionStatus = "FAILED";
        String errorMessage = null;
        String resultData = null;

        try {
            // SELECT 문인 경우 쿼리 실행 및 결과 저장
            if (processedSql.trim().toUpperCase().startsWith("SELECT")) {
                List<Map<String, Object>> rows = jdbcTemplate.queryForList(processedSql);
                resultData = new ObjectMapper().writeValueAsString(rows); // JSON 형태로 저장
            }
            // INSERT, UPDATE, DELETE 등 DML 문인 경우 업데이트 갯수 저장
            else {
                int affectedRows = jdbcTemplate.update(processedSql);
                resultData = "Affected Rows: " + affectedRows;
            }
            executionStatus = "SUCCESS";
        } catch (Exception e) {
            errorMessage = e.getMessage();
            // 에러 로깅
        } finally {
            // 실행 결과 저장
            TestExecutionResult result = new TestExecutionResult();
            result.setActivityId(activityId);
            result.setExecutedSql(processedSql);
            result.setParameterValues(new ObjectMapper().writeValueAsString(finalParameters)); // JSON 문자열로 변환
            result.setExecutionStatus(executionStatus);
            result.setResultData(resultData);
            result.setErrorMessage(errorMessage);
            resultMapper.insertTestExecutionResult(result);
        }
    }
}

2.5. 결과 검증: SQL 수행 결과 저장

개요:
SQL 실행 후 결과를 TEST_EXECUTION_RESULT 테이블에 저장합니다. 이 결과는 나중에 테스트 리포트 생성 등에 활용될 수 있습니다.

주요 컴포넌트:

  • MyBatis Mapper: TEST_EXECUTION_RESULT 테이블에 데이터를 삽입합니다.

저장 예시 (MyBatis Mapper XML):

<insert id="insertTestExecutionResult" useGeneratedKeys="true" keyProperty="execId">
    INSERT INTO TEST_EXECUTION_RESULT (ACTIVITY_ID, EXECUTED_SQL, PARAMETER_VALUES, EXECUTION_STATUS, RESULT_DATA, ERROR_MESSAGE)
    VALUES (#{activityId}, #{executedSql}, #{parameterValues}, #{executionStatus}, #{resultData}, #{errorMessage})
</insert>

3. 구현 시 고려사항 및 추가 제안

  • 보안: 동적 SQL 실행은 SQL Injection에 매우 취약합니다. 사용자 입력 값은 절대로 직접 SQL에 삽입하지 않도록 철저히 검증하고, 가능하다면 PreparedStatement를 통해 바인딩해야 합니다. SQL_QUERY 타입의 파라미터나 동적 SQL을 사용할 경우, 최대한 제한적인 권한의 DB 계정을 사용하고, 입력값에 대한 화이트리스트 방식의 검증을 강력히 적용해야 합니다.
  • 런타임 계산 파라미터 (Dynamic Parameters) 구현: PARAM_VALUE에 저장된 SQL을 별도의 JdbcTemplate으로 실행하여 결과값을 받아와야 합니다. 이때, 해당 SQL 자체도 또 다른 파라미터를 포함할 수 있는지 등 복잡도를 고려해야 합니다.
  • SQL 쿼리 결과값 파라미터 (SQL Query Parameters) 구현: PARAM_VALUE에 정의된 SQL을 실행하고, 그 결과값(단일 값으로 가정)으로 SQL 템플릿 내의 특정 ${paramName}을 대체해야 합니다. 이는 문자열 치환 방식으로 구현될 수 있습니다.
  • 트랜잭션 관리: 각 테스트 활동의 SQL 실행은 독립적인 트랜잭션으로 관리하거나, 전체 테스트 계획 실행을 하나의 큰 트랜잭션으로 묶을지 전략을 수립해야 합니다.
  • 비동기 처리: 대량의 테스트 케이스를 실행할 경우, 각 TestActivity의 SQL 실행을 비동기로 처리하여 시스템 응답성을 높일 수 있습니다 (예: Spring @Async, CompletableFuture).
  • 에러 핸들링: SQL 실행 중 발생하는 다양한 예외 상황에 대한 견고한 에러 핸들링 로직이 필요합니다.
  • 결과 리포팅: 저장된 TEST_EXECUTION_RESULT 데이터를 기반으로 테스트 실행 보고서를 생성하는 기능이 추가되어야 합니다.
  • UI/UX: 사용자가 파라미터를 쉽게 정의하고, SQL 템플릿을 작성하며, 실행 결과를 직관적으로 확인할 수 있는 웹 UI 개발이 중요합니다.

이 설계와 로직 흐름은 제시된 요구사항을 충족하며, 안정적이고 확장 가능한 테스트 도구 시스템을 구축하는 데 도움이 될 것입니다.

React, Typescript의 클래스 컴퍼넌트 환경에서 monitoringType 변수가 T 일 경우 'Activity Name'를 리턴하고 S 일 경우
'Monitoring Name'를 리턴하는 예제 만들어 주세요.

import React, { Component } from 'react';

type MonitoringType = 'T' | 'S';

interface Props {
monitoringType: MonitoringType;
}

class MonitoringLabel extends Component {
getLabel = (): string => {

const { monitoringType } = this.props;

switch (monitoringType) {
  case 'T':
    return 'Activity Name';
  case 'S':
    return 'Monitoring Name';
  default:
    return 'Unknown';
}

};

render() {

return (
  <div>
    <label>{this.getLabel()}</label>
  </div>
);

}
}

export default MonitoringLabel;

switch 문장 말고 간단하게 T이면 Activity Name 나머지는 Monitoring Name 으로 간략하게 수정해주세요

import React, { Component } from 'react';

type MonitoringType = 'T' | 'S';

interface Props {
monitoringType: MonitoringType;
}

class MonitoringLabel extends Component {
getLabel = (): string => {

return this.props.monitoringType === 'T' ? 'Activity Name' : 'Monitoring Name';

};

render() {

return (
  <div>
    <label>{this.getLabel()}</label>
  </div>
);

}
}

export default MonitoringLabel;

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

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

아래는 주어진 사양을 바탕으로 설계된 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 파일도 도와드릴 수 있습니다. 추가 요청 주시면 이어서 작성해드릴게요!

시스템 문제를 체크하기 위해서 DB에 SQL과 기준, 체크 스케줄을 등록하고 배치를 수행하여 문제가 발생하였을 떄 담당자에게 메일을 보내는 기능을 개발하려고 합니다. Java 기반으로 유사한 오픈 소스 프로젝트 찾아 주세요.

Quartz: 가장 널리 사용되는 Java 스케줄러 라이브러리 중 하나입니다. Cron 표현식을 사용하여 정교한 스케줄링이 가능하며, Job 및 Trigger 개념을 통해 실행할 작업을 정의하고 관리할 수 있습니다. SQL 실행, 결과 검증, 메일 발송 로직을 Job으로 구현하여 스케줄링할 수 있습니다.

https://camel.apache.org/

OpenNMS
https://github.com/OpenNMS/opennms

✅ 2. JobRunr
https://www.jobrunr.io/en/
특징:
백그라운드 작업 처리: Java 8 람다 표현식을 사용하여 백그라운드 작업을 쉽게 정의하고 실행할 수 있습니다.
스케줄링 기능: enqueue, schedule, scheduleRecurrently 메서드를 통해 작업을 즉시 실행하거나 예약할 수 있습니다.
대시보드 제공: 내장된 대시보드를 통해 작업 상태, 진행 상황, 실패 내역 등을 실시간으로 모니터링할 수 있습니다.
다양한 저장소 지원: 관계형 데이터베이스(RDBMS), MongoDB, Redis 등 다양한 저장소와 통합할 수 있습니다.
적합한 경우:
정기적인 데이터베이스 상태 점검 작업을 예약하고, 결과에 따라 이메일 알림을 보내는 시스템을 구축할 때.
작업의 상태를 시각적으로 모니터링하고 관리하고자 할 때.

  1. Dolphin Scheduler (Apache Incubator, 中国 개발팀 참여)
    https://github.com/apache/dolphinscheduler
    기능: 분산 스케줄링 시스템으로, SQL 작업을 정의하고 주기적으로 실행할 수 있으며, 실패 시 이메일/알림 발송 가능
    특징:
    웹 UI를 통해 작업 흐름(Workflow)을 정의하고 스케줄링
    MySQL, PostgreSQL 등 다양한 DB 지원
    중국 개발팀이 주도한 프로젝트로 중국어 문서 풍부
  1. Alibaba Canal (Alibaba 오픈소스)
    https://github.com/apache/dolphinscheduler
    기능: MySQL Binlog 기반의 데이터 동기화/모니터링 도구
    유사점: DB 변경 사항을 감지하고 알림 가능 (확장 개발 필요)
    참고: 주로 CDC(Change Data Capture)용이지만, 커스텀 체크 로직 추가 가능
  2. HertzBeat (中国 Dromara 커뮤니티)
    https://github.com/apache/hertzbeat
    기능: 오픈소스 실시간 모니터링 시스템으로 DB, API, 서비스 건강 상태 체크
    특징:
    지원 DB: MySQL, Oracle, PostgreSQL 등
    임계값 초과 시 이메일/웹훅 알림
    중국 개발자 중심의 활발한 커뮤니티

https://github.com/dromara/Jpom

  1. Ward (간단한 모니터링 툴)
    기능: 서버 및 DB 상태 모니터링 대시보드

참고: 알림 기능은 제한적이지만, 오픈소스라 커스터마이징 가능

  1. Jpom (Dromara)
    웹 사이
    https://github.com/dromara/Jpom
    기능: Java 프로젝트 운영 관리 도구로, 배치 및 모니터링 기능 포함

유사점: 스크립트 실행 및 알림 기능 지원

SQLeonardo

Java 기반 SQL 모니터링 및 관리 도구
스케줄링 기능과 알림 통합 가능
https://sourceforge.net/projects/sqleonardo/