2025年6月

✅ 목표

  • Windows 10/11 환경에서 Remotion 프로젝트 생성
  • 이미지 여러 장으로 동영상 자동 생성 테스트
  • 영상 렌더링 및 결과물 확인

🛠️ 사전 준비사항

항목설명
Node.js최소 v16 이상 (권장: v18) 설치 필요
npm 또는 yarn패키지 매니저 사용 가능하도록 설치
VS Code코드 편집기 (선택)
FFmpegRemotion이 내부적으로 사용하므로 설치 필수

🔧 Step-by-Step 설치 및 실행 가이드


1. Node.js 설치

먼저 Node.js 공식 사이트에서 LTS 버전(예: 18.x)을 다운로드 후 설치하세요.

설치 완료 후 명령 프롬프트(cmd 또는 PowerShell)에서 확인:

node -v
npm -v

2. FFmpeg 설치

Remotion은 내부적으로 FFmpeg를 사용하므로 반드시 설치해야 합니다.

방법:

  1. https://www.gyan.dev/ffmpeg/builds/ 접속
  2. ffmpeg-release-full.7z 파일 다운로드
  3. 압축 해제 후, bin 폴더의 경로를 시스템 환경 변수 PATH에 추가

예시 경로: C:\ffmpeg\bin

확인:

ffmpeg -version

출력이 나온다면 정상 설치된 것입니다.


3. Remotion 프로젝트 생성

PowerShell 또는 cmd에서 아래 명령어로 Remotion 프로젝트를 생성합니다.

npx create-video my-video-project
위 명령어는 create-video CLI를 통해 Remotion 템플릿 프로젝트를 생성합니다.

생성된 디렉토리로 이동:

cd my-video-project

4. 프로젝트 구조 살펴보기

my-video-project/
├── public/            # 정적 파일 (이미지, 음악 등)
├── src/
│   ├── index.tsx      # 컴포넌트 정의
│   └── Root.tsx
├── package.json
└── remotion.config.ts

5. 이미지를 영상으로 만드는 예제 작성

a. public/ 폴더에 이미지 넣기

예시 이미지: image1.jpg, image2.jpg, image3.jpg 등을 public/ 폴더에 복사합니다.

b. src/index.tsx 수정

import { AbsoluteFill, Img, Sequence, useVideoConfig } from "remotion";
import React from "react";

const images = [
  "/image1.jpg",
  "/image2.jpg",
  "/image3.jpg"
];

export const MyVideo = () => {
  const { width, height } = useVideoConfig();

  return (
    <AbsoluteFill>
      {images.map((src, i) => (
        <Sequence key={src} from={i * 30} durationInFrames={30}>
          <AbsoluteFill>
            <Img src={src} style={{ width, height, objectFit: "cover" }} />
          </AbsoluteFill>
        </Sequence>
      ))}
    </AbsoluteFill>
  );
};
  • 각 이미지는 30프레임(=1초 @ 30fps) 동안 표시됩니다.
  • objectFit="cover"로 비율 유지하면서 꽉 채워서 보여줍니다.

6. 비디오 설정 변경 (옵션)

remotion.config.ts에서 기본 설정을 조정할 수 있습니다:

import { Config } from "remotion";

Config.Rendering.setImageFormat("jpeg");
Config.Output.setLocation("out/video.mp4");
Config.Puppeteer.setLaunchPuppeteerSettings({
  headless: true,
});

7. 영상 미리보기 (개발 서버 실행)

npm run dev

브라우저에서 http://localhost:3000 접속하면 영상 미리보기가 됩니다.


8. 실제 동영상 렌더링

npm run build

또는 특정 출력 옵션 지정:

npx remotion render MyVideo out/video.mp4 --props='{}'

렌더링 완료되면 out/ 폴더에 video.mp4가 생성됩니다.


🎬 결과 확인

out/video.mp4를 영상 플레이어에서 열어보세요!


📦 추가 기능 확장 아이디어

  • 트랜지션 효과 추가 (crossfade)
  • 배경 음악 삽입 (<Audio src="/music.mp3" />)
  • 텍스트 오버레이
  • SNS 맞춤 비율 (9:16 등)

📚 참고 문서


🙋‍♂️ 도움말 요청 시 필요한 정보

필요하시면 아래 내용도 알려주세요:

  • 어떤 형식의 영상을 만들고 싶으신가요? (예: 인스타용 9:16 슬라이드쇼)
  • 음악도 포함하고 싶으신가요?
  • 몇 장의 이미지를 처리하고 싶으신가요?

그에 따라 더 구체적인 예제 코드도 제공해 드릴 수 있어요 😊


준비가 되셨다면, 지금 바로 Remotion으로 첫 번째 영상을 만들어 보세요!
궁금한 점 있으시면 언제든지 물어보세요 💪

테스트 계층 구조를 위한 AG-Grid 구현 예제
react 17.2.4, typescript 4.3.5, ag-grid 26.1.0, openjdk 17, spring, ibatis, mariadb 기술 스택을 기반으로 테스트 계층 구조(테스트계획-테스트시나리오-테스트케이스-테스트액티비티)를 하나의 AG-Grid에 표시하고 표시/숨김 기능 있고 그리드에서도 +,- 항목이 있어서 표시/숨김 기능이 있도록 소스 생성해주세요

"테스트계층 구조(테스트계획 → 시나리오 → 케이스 → 액티비티)"를 하나의 AG-Grid에 표시 + 표시/숨김 토글 + +, -로 트리 확장/축소 가능한 예제

프로젝트의 요구사항을 고려했을 때, 효율적인 테스트 도구 시스템을 구축하기 위한 데이터베이스 설계 및 주요 로직 단계를 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 개발이 중요합니다.

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