From a0b4e0f35378f42c66ea409d189993318e4cf0b5 Mon Sep 17 00:00:00 2001 From: jihukimme Date: Mon, 8 Sep 2025 01:15:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20batch=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20domain=20=EC=99=B8=EB=B6=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=ED=95=98=EC=97=AC=20=EC=97=AD=ED=95=A0=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 배치(Batch) 작업은 특정 도메인에 종속되지 않고, 여러 도메인에 걸쳐 실행되는 애플리케이션 레벨의 관심사임 - batch 패키지를 domain 패키지 외부의 최상위 레벨로 이동하여 아키텍처의 역할을 더 명확하게 분리하고, 도메인 모델의 응집도를 높임 --- .../{domain => }/batch/job/BlogContentJobConfig.java | 6 +++--- .../batch/tasklet/ContentGenerationTasklet.java | 2 +- .../batch/tasklet/KeywordExtractionTasklet.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename apps/user-service/src/main/java/com/gltkorea/icebang/{domain => }/batch/job/BlogContentJobConfig.java (90%) rename apps/user-service/src/main/java/com/gltkorea/icebang/{domain => }/batch/tasklet/ContentGenerationTasklet.java (97%) rename apps/user-service/src/main/java/com/gltkorea/icebang/{domain => }/batch/tasklet/KeywordExtractionTasklet.java (97%) diff --git a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/job/BlogContentJobConfig.java b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/job/BlogContentJobConfig.java similarity index 90% rename from apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/job/BlogContentJobConfig.java rename to apps/user-service/src/main/java/com/gltkorea/icebang/batch/job/BlogContentJobConfig.java index 6646c9dc..61626411 100644 --- a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/job/BlogContentJobConfig.java +++ b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/job/BlogContentJobConfig.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.domain.batch.job; +package com.gltkorea.icebang.batch.job; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; @@ -9,8 +9,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import com.gltkorea.icebang.domain.batch.tasklet.ContentGenerationTasklet; -import com.gltkorea.icebang.domain.batch.tasklet.KeywordExtractionTasklet; +import com.gltkorea.icebang.batch.tasklet.ContentGenerationTasklet; +import com.gltkorea.icebang.batch.tasklet.KeywordExtractionTasklet; import lombok.RequiredArgsConstructor; diff --git a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/ContentGenerationTasklet.java b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/ContentGenerationTasklet.java similarity index 97% rename from apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/ContentGenerationTasklet.java rename to apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/ContentGenerationTasklet.java index c445cc21..5cc8918a 100644 --- a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/ContentGenerationTasklet.java +++ b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/ContentGenerationTasklet.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.domain.batch.tasklet; +package com.gltkorea.icebang.batch.tasklet; import java.util.List; diff --git a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/KeywordExtractionTasklet.java b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/KeywordExtractionTasklet.java similarity index 97% rename from apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/KeywordExtractionTasklet.java rename to apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/KeywordExtractionTasklet.java index 4dc544b9..520403b3 100644 --- a/apps/user-service/src/main/java/com/gltkorea/icebang/domain/batch/tasklet/KeywordExtractionTasklet.java +++ b/apps/user-service/src/main/java/com/gltkorea/icebang/batch/tasklet/KeywordExtractionTasklet.java @@ -1,4 +1,4 @@ -package com.gltkorea.icebang.domain.batch.tasklet; +package com.gltkorea.icebang.batch.tasklet; import java.util.List; From 812c366aa59de90142d84fa1b5bb256aa8ae7bb6 Mon Sep 17 00:00:00 2001 From: jihukimme Date: Mon, 8 Sep 2025 12:53:12 +0900 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20=EB=B0=B0=EC=B9=98/=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=ED=94=8C=EB=A1=9C=20=EA=B4=80=EB=A0=A8=20DB=20?= =?UTF-8?q?=EC=8A=A4=ED=82=A4=EB=A7=88=20=EC=B4=88=EA=B8=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/application-test-e2e.yml | 6 + .../main/resources/application-test-unit.yml | 4 +- .../mybatis/mapper/ScheduleMapper.xml | 4 +- .../src/main/resources/sql/schema.sql | 145 +++++++++++++++++- .../icebang/support/E2eTestSupportTest.java | 2 - docker/local/docker-compose.yml | 24 +-- 6 files changed, 167 insertions(+), 18 deletions(-) diff --git a/apps/user-service/src/main/resources/application-test-e2e.yml b/apps/user-service/src/main/resources/application-test-e2e.yml index e41568fb..7703f4a3 100644 --- a/apps/user-service/src/main/resources/application-test-e2e.yml +++ b/apps/user-service/src/main/resources/application-test-e2e.yml @@ -3,6 +3,12 @@ spring: activate: on-profile: test-e2e + sql: + init: + mode: always + schema-locations: classpath:sql/schema.sql + encoding: UTF-8 + mybatis: mapper-locations: classpath:mybatis/mapper/**/*.xml type-aliases-package: com.gltkorea.icebang.dto diff --git a/apps/user-service/src/main/resources/application-test-unit.yml b/apps/user-service/src/main/resources/application-test-unit.yml index df3f9cba..fec65f43 100644 --- a/apps/user-service/src/main/resources/application-test-unit.yml +++ b/apps/user-service/src/main/resources/application-test-unit.yml @@ -36,7 +36,9 @@ spring: # SQL 스크립트 초기화 설정 sql: init: - mode: embedded + mode: always + schema-locations: classpath:sql/schema.sql + encoding: UTF-8 mybatis: mapper-locations: classpath:mybatis/mapper/**/*.xml diff --git a/apps/user-service/src/main/resources/mybatis/mapper/ScheduleMapper.xml b/apps/user-service/src/main/resources/mybatis/mapper/ScheduleMapper.xml index f85de8b5..4a40fe49 100644 --- a/apps/user-service/src/main/resources/mybatis/mapper/ScheduleMapper.xml +++ b/apps/user-service/src/main/resources/mybatis/mapper/ScheduleMapper.xml @@ -4,12 +4,12 @@ diff --git a/apps/user-service/src/main/resources/sql/schema.sql b/apps/user-service/src/main/resources/sql/schema.sql index e36805fc..e2a9a917 100644 --- a/apps/user-service/src/main/resources/sql/schema.sql +++ b/apps/user-service/src/main/resources/sql/schema.sql @@ -110,4 +110,147 @@ CREATE INDEX IF NOT EXISTS CREATE INDEX IF NOT EXISTS `idx_permissions_resource` ON `permissions` (`resource`); CREATE INDEX IF NOT EXISTS - `idx_permissions_active` ON `permissions` (`is_active`); \ No newline at end of file + `idx_permissions_active` ON `permissions` (`is_active`); + + + +CREATE TABLE IF NOT EXISTS `workflows` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL UNIQUE, + `description` text NULL, + `is_enabled` boolean DEFAULT TRUE, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `created_by` bigint unsigned NULL, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `updated_by` bigint unsigned NULL, + PRIMARY KEY (`id`) + ); + +CREATE TABLE IF NOT EXISTS `schedules` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `workflow_id` bigint unsigned NOT NULL, + `cron_expression` varchar(50) NULL, + `parameters` json NULL, + `is_active` boolean DEFAULT TRUE, + `last_run_status` varchar(20) NULL, + `last_run_at` timestamp NULL, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `created_by` bigint unsigned NULL, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `updated_by` bigint unsigned NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_schedules_to_workflows` FOREIGN KEY (`workflow_id`) REFERENCES `workflows` (`id`) + ); + +CREATE TABLE IF NOT EXISTS `jobs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL UNIQUE, + `description` text NULL, + `is_enabled` boolean DEFAULT TRUE, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `created_by` bigint unsigned NULL, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `updated_by` bigint unsigned NULL, + PRIMARY KEY (`id`) + ); + +CREATE TABLE IF NOT EXISTS `tasks` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL UNIQUE, + `type` varchar(50) NULL, + `parameters` json NULL, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) + ); + +CREATE TABLE IF NOT EXISTS `workflow_jobs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `workflow_id` bigint unsigned NOT NULL, + `job_id` bigint unsigned NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_workflow_jobs_to_workflows` FOREIGN KEY (`workflow_id`) REFERENCES `workflows` (`id`), + CONSTRAINT `fk_workflow_jobs_to_jobs` FOREIGN KEY (`job_id`) REFERENCES `jobs` (`id`), + UNIQUE KEY `uk_workflow_job` (`workflow_id`, `job_id`) + ); + +CREATE TABLE IF NOT EXISTS `job_tasks` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `job_id` bigint unsigned NOT NULL, + `task_id` bigint unsigned NOT NULL, + `execution_order` int NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_job_tasks_to_jobs` FOREIGN KEY (`job_id`) REFERENCES `jobs` (`id`), + CONSTRAINT `fk_job_tasks_to_tasks` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`), + UNIQUE KEY `uk_job_task` (`job_id`, `task_id`) + ); + +CREATE TABLE IF NOT EXISTS `execution_logs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `execution_type` varchar(20) NULL COMMENT 'task, schedule, job, workflow', + `source_id` bigint unsigned NULL COMMENT '모든 데이터에 대한 ID ex: job_id, schedule_id, task_id, ...', + `log_level` varchar(20) NULL, + `executed_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `log_message` text NULL, + `trace_id` char(36) NULL, + `config_snapshot` json NULL, + PRIMARY KEY (`id`), + INDEX `idx_source_id_type` (`source_id`, `execution_type`) + ); + +CREATE TABLE IF NOT EXISTS `task_io_data` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `trace_id` char(36) NULL, + `io_type` varchar(10) NULL COMMENT 'INPUT, OUTPUT', + `name` varchar(100) NULL, + `data_type` varchar(50) NULL, + `data_value` json NULL, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + INDEX `idx_trace_id` (`trace_id`) + ); + +CREATE TABLE IF NOT EXISTS `configs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `target_type` varchar(50) NULL COMMENT 'user, job, workflow', + `target_id` bigint unsigned NULL, + `version` int NULL, + `json` json NULL, + `is_active` boolean DEFAULT TRUE, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `created_by` bigint unsigned NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_config_target` (`target_type`, `target_id`) + ); + +CREATE TABLE IF NOT EXISTS `categories` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) NULL, + `description` text NULL, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) + ); + +CREATE TABLE IF NOT EXISTS `user_configs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint unsigned NOT NULL, + `type` varchar(50) NULL, + `name` varchar(100) NULL, + `json` json NULL, + `is_active` boolean DEFAULT TRUE, + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) + ); + +-- 인덱스 추가 (성능 최적화) +CREATE INDEX IF NOT EXISTS `idx_schedules_workflow` ON `schedules` (`workflow_id`); +CREATE INDEX IF NOT EXISTS `idx_jobs_enabled` ON `jobs` (`is_enabled`); +CREATE INDEX IF NOT EXISTS `idx_tasks_type` ON `tasks` (`type`); +CREATE INDEX IF NOT EXISTS `idx_workflows_enabled` ON `workflows` (`is_enabled`); +CREATE UNIQUE INDEX IF NOT EXISTS `uk_schedules_workflow` ON `schedules` (`workflow_id`); +CREATE UNIQUE INDEX IF NOT EXISTS `uk_job_name` ON `jobs` (`name`); +CREATE UNIQUE INDEX IF NOT EXISTS `uk_task_name` ON `tasks` (`name`); +CREATE UNIQUE INDEX IF NOT EXISTS `uk_workflows_name` ON `workflows` (`name`); +CREATE INDEX IF NOT EXISTS `idx_user_configs_user` ON `user_configs` (`user_id`); \ No newline at end of file diff --git a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java b/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java index 7fd2deff..bad5a2ba 100644 --- a/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java +++ b/apps/user-service/src/test/java/com/gltkorea/icebang/support/E2eTestSupportTest.java @@ -3,9 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; -import org.springframework.test.context.jdbc.Sql; -@Sql(scripts = "classpath:sql/schema.sql") class E2eTestSupportTest extends E2eTestSupport { @Test diff --git a/docker/local/docker-compose.yml b/docker/local/docker-compose.yml index b02dd2eb..67f7ecbf 100644 --- a/docker/local/docker-compose.yml +++ b/docker/local/docker-compose.yml @@ -33,18 +33,18 @@ services: depends_on: - mariadb - pre-processing-service: - build: - context: ../../apps/pre-processing-service # 프로젝트 루트 (Dockerfile이 루트에 없으면 맞게 조정) - dockerfile: Dockerfile # Dockerfile 경로 (루트에 없다면 상대경로로 수정) - image: pre-processing-service:latest - container_name: pre-processing-service - restart: always - ports: - - "8000:8000" - env_file: - - ../../apps/pre-processing-service/.env # 공통 - - ../../apps/pre-processing-service/dev.env # 개발 +# pre-processing-service: +# build: +# context: ../../apps/pre-processing-service # 프로젝트 루트 (Dockerfile이 루트에 없으면 맞게 조정) +# dockerfile: Dockerfile # Dockerfile 경로 (루트에 없다면 상대경로로 수정) +# image: pre-processing-service:latest +# container_name: pre-processing-service +# restart: always +# ports: +# - "8000:8000" +# env_file: +# - ../../apps/pre-processing-service/.env # 공통 +# - ../../apps/pre-processing-service/dev.env # 개발 volumes: mariadb_data: \ No newline at end of file