diff --git a/apps/user-service/src/main/java/site/icebang/domain/schedule/service/QuartzScheduleService.java b/apps/user-service/src/main/java/site/icebang/domain/schedule/service/QuartzScheduleService.java index d8348e7e..667637b1 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/schedule/service/QuartzScheduleService.java +++ b/apps/user-service/src/main/java/site/icebang/domain/schedule/service/QuartzScheduleService.java @@ -7,12 +7,39 @@ import site.icebang.domain.schedule.model.Schedule; import site.icebang.domain.workflow.scheduler.WorkflowTriggerJob; +/** + * Spring Quartz 스케줄러의 Job과 Trigger를 동적으로 관리하는 서비스 클래스입니다. + * + *

이 서비스는 데이터베이스에 정의된 {@code Schedule} 정보를 바탕으로, + * Quartz 엔진에 실제 실행 가능한 작업을 등록, 수정, 삭제하는 역할을 담당합니다. + * + *

주요 기능:

+ * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Slf4j @Service @RequiredArgsConstructor public class QuartzScheduleService { + + /** Quartz 스케줄러의 메인 인스턴스 */ private final Scheduler scheduler; + /** + * DB에 정의된 Schedule 객체를 기반으로 Quartz에 스케줄을 등록하거나 업데이트합니다. + * + *

지정된 워크플로우 ID에 해당하는 Job이 이미 존재할 경우, 기존 Job과 Trigger를 삭제하고 + * 새로운 정보로 다시 생성하여 스케줄을 업데이트합니다. {@code JobDataMap}을 통해 + * 실행될 Job에게 어떤 워크플로우를 실행해야 하는지 ID를 전달합니다. + * + * @param schedule Quartz에 등록할 스케줄 정보를 담은 도메인 모델 객체 + * @since v0.1.0 + */ public void addOrUpdateSchedule(Schedule schedule) { try { JobKey jobKey = JobKey.jobKey("workflow-" + schedule.getWorkflowId()); @@ -40,6 +67,12 @@ public void addOrUpdateSchedule(Schedule schedule) { } } + /** + * 지정된 워크플로우 ID와 연결된 Quartz 스케줄을 삭제합니다. + * + * @param workflowId 삭제할 스케줄에 연결된 워크플로우의 ID + * @since v0.1.0 + */ public void deleteSchedule(Long workflowId) { try { JobKey jobKey = JobKey.jobKey("workflow-" + workflowId); diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/TaskRunner.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/TaskRunner.java index f8ad27c8..72c9f078 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/TaskRunner.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/TaskRunner.java @@ -5,19 +5,53 @@ import site.icebang.domain.workflow.model.Task; import site.icebang.domain.workflow.model.TaskRun; -/** 워크플로우의 개별 Task를 실행하는 모든 Runner가 구현해야 할 인터페이스 */ +/** + * 워크플로우 내 개별 Task의 실행을 담당하는 모든 Runner 객체가 구현해야 할 공통 인터페이스입니다. + * + *

이 인터페이스는 전략 패턴(Strategy Pattern)의 '전략(Strategy)' 역할을 수행합니다. {@code WorkflowExecutionService}는 + * 이 인터페이스에 의존하여, Task의 타입('FastAPI' 등)에 따라 적절한 Runner 구현체를 선택하고 실행 로직을 위임합니다. + * + *

주요 구성 요소:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ public interface TaskRunner { - /** Task 실행 결과를 담는 Record. status: SUCCESS 또는 FAILED message: 실행 결과 또는 에러 메시지 */ + /** + * Task 실행 결과를 담는 불변(Immutable) 데이터 객체(Record)입니다. + * + *

실행의 성공/실패 여부(status)와 결과 메시지(message)를 표준화된 방식으로 반환합니다. + * + * @param status 실행 상태 ("SUCCESS" 또는 "FAILED") + * @param message 실행 결과 (성공 시 응답 Body, 실패 시 에러 메시지) + * @since v0.1.0 + */ record TaskExecutionResult(String status, String message) { public static TaskExecutionResult success(String message) { return new TaskExecutionResult("SUCCESS", message); } + /** + * 실패 결과를 생성하는 정적 팩토리 메소드입니다. + * + * @param message 실패 원인 메시지 + * @return status가 "FAILED"로 설정된 결과 객체 + */ public static TaskExecutionResult failure(String message) { return new TaskExecutionResult("FAILED", message); } + /** + * 해당 결과가 실패했는지 여부를 반환합니다. + * + * @return 실패했다면 true, 아니면 false + */ public boolean isFailure() { return "FAILED".equals(this.status); } @@ -26,10 +60,11 @@ public boolean isFailure() { /** * 특정 Task를 실행합니다. * - * @param task 실행할 Task의 정적 정의 - * @param taskRun 현재 실행에 대한 기록 객체 - * @param requestBody 동적으로 생성된 요청 데이터 - * @return Task 실행 결과 + * @param task 실행할 Task의 정적 정의 (이름, 타입, 파라미터 등) + * @param taskRun 현재 실행에 대한 DB 기록 객체 (ID 추적 등에 사용) + * @param requestBody {@code TaskBodyBuilder}에 의해 동적으로 생성된 최종 요청 Body + * @return Task 실행 결과를 담은 {@code TaskExecutionResult} 객체 + * @since v0.1.0 */ TaskExecutionResult execute(Task task, TaskRun taskRun, ObjectNode requestBody); } diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/FastApiTaskRunner.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/FastApiTaskRunner.java index 136a9d93..95a0e89c 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/FastApiTaskRunner.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/runner/fastapi/FastApiTaskRunner.java @@ -13,12 +13,43 @@ import site.icebang.domain.workflow.runner.TaskRunner; import site.icebang.external.fastapi.adapter.FastApiAdapter; +/** + * FastAPI 서버와 통신하는 Task를 실행하는 구체적인 Runner 구현체입니다. + * + *

이 클래스는 {@code TaskRunner} 인터페이스를 구현하며, Task의 타입이 'FastAPI'일 때 선택됩니다. 실제 HTTP 통신은 {@code + * FastApiAdapter}에 위임하고, 이 클래스는 워크플로우의 {@code Task} 객체를 {@code FastApiAdapter}가 이해할 수 있는 호출 형식으로 + * 변환하는 **어댑터(Adapter)** 역할을 수행합니다. + * + *

주요 기능:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Component("fastapiTaskRunner") @RequiredArgsConstructor public class FastApiTaskRunner implements TaskRunner { + /** FastAPI 서버와의 통신을 전담하는 어댑터 */ private final FastApiAdapter fastApiAdapter; + /** + * FastAPI 타입의 Task를 실행합니다. + * + *

Task의 파라미터에서 엔드포인트와 HTTP 메소드를 추출하고, {@code WorkflowExecutionService}로부터 전달받은 동적 Request + * Body를 사용하여 {@code FastApiAdapter}를 호출합니다. + * + * @param task 실행할 Task의 정적 정의 + * @param taskRun 현재 실행에 대한 기록 객체 + * @param requestBody {@code TaskBodyBuilder}에 의해 동적으로 생성된 최종 요청 Body + * @return {@code FastApiAdapter}의 호출 결과를 담은 {@code TaskExecutionResult} 객체 + * @since v0.1.0 + */ @Override public TaskExecutionResult execute(Task task, TaskRun taskRun, ObjectNode requestBody) { JsonNode params = task.getParameters(); diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/scheduler/WorkflowTriggerJob.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/scheduler/WorkflowTriggerJob.java index 196c1fa0..a3076d1f 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/scheduler/WorkflowTriggerJob.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/scheduler/WorkflowTriggerJob.java @@ -9,12 +9,38 @@ import site.icebang.domain.workflow.service.WorkflowExecutionService; +/** + * Spring Quartz 스케줄러에 의해 실행되는 실제 작업(Job) 클래스입니다. + * + *

이 클래스는 Quartz의 스케줄링 세계와 애플리케이션의 비즈니스 로직을 연결하는 **브릿지(Bridge)** 역할을 수행합니다. Quartz의 Trigger가 정해진 + * 시간에 발동하면, Quartz 엔진은 이 Job을 인스턴스화하고 {@code executeInternal} 메소드를 호출합니다. + * + *

주요 기능:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Slf4j @Component @RequiredArgsConstructor public class WorkflowTriggerJob extends QuartzJobBean { private final WorkflowExecutionService workflowExecutionService; + /** + * Quartz 스케줄러에 의해 트리거가 발동될 때 호출되는 메인 실행 메소드입니다. + * + *

이 메소드는 실행 컨텍스트({@code JobExecutionContext})에서 {@code JobDataMap}을 통해 스케줄 등록 시점에 저장된 + * 'workflowId'를 추출합니다. 그 후, 해당 ID를 파라미터로 하여 {@code WorkflowExecutionService}의 {@code + * executeWorkflow} 메소드를 호출하여 실제 비즈니스 로직의 실행을 시작합니다. + * + * @param context Quartz가 제공하는 현재 실행에 대한 런타임 정보. JobDetail과 Trigger 정보를 포함합니다. + * @since v0.1.0 + */ @Override protected void executeInternal(JobExecutionContext context) { Long workflowId = context.getJobDetail().getJobDataMap().getLong("workflowId"); diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/TaskExecutionService.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/TaskExecutionService.java index 80cf44a3..29f28d98 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/TaskExecutionService.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/TaskExecutionService.java @@ -13,20 +13,50 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import site.icebang.domain.workflow.model.Task; import site.icebang.domain.workflow.model.TaskRun; import site.icebang.domain.workflow.runner.TaskRunner; -@Slf4j +/** + * 워크플로우 내 개별 Task의 실행과 재시도 정책을 전담하는 서비스입니다. + * + *

이 클래스는 {@code WorkflowExecutionService}로부터 Task 실행 책임을 위임받습니다. Spring AOP의 '자기 + * 호출(Self-invocation)' 문제를 회피하고, 재시도 로직을 비즈니스 흐름과 분리하기 위해 별도의 서비스로 구현되었습니다. + * + *

주요 기능:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Service @RequiredArgsConstructor -public class TaskExecutionService { // 📌 클래스 이름 변경 +public class TaskExecutionService { + /** 워크플로우 실행 이력 전용 로거 */ private static final Logger workflowLogger = LoggerFactory.getLogger("WORKFLOW_HISTORY"); + private final Map taskRunners; - /** RestClientException 발생 시, 5초 간격으로 최대 3번 재시도합니다. */ + /** + * 지정된 Task를 재시도 정책을 적용하여 실행합니다. + * + *

HTTP 통신 오류 등 {@code RestClientException} 발생 시, 5초의 고정된 간격({@code Backoff})으로 최대 3회({@code + * maxAttempts})까지 실행을 재시도합니다. 지원하지 않는 Task 타입의 경우 재시도 없이 즉시 {@code IllegalArgumentException}을 + * 발생시킵니다. + * + * @param task 실행할 Task의 도메인 모델 + * @param taskRun 현재 실행에 대한 기록 객체 + * @param requestBody 동적으로 생성된 요청 Body + * @return Task 실행 결과 + * @throws IllegalArgumentException 지원하지 않는 Task 타입일 경우 + * @since v0.1.0 + */ @Retryable( value = {RestClientException.class}, maxAttempts = 3, @@ -45,7 +75,19 @@ public TaskRunner.TaskExecutionResult executeWithRetry( return runner.execute(task, taskRun, requestBody); } - /** 모든 재시도가 실패했을 때 마지막으로 호출될 복구 메소드입니다. */ + /** + * {@code @Retryable} 재시도가 모두 실패했을 때 호출되는 복구 메소드입니다. + * + *

이 메소드는 {@code executeWithRetry} 메소드와 동일한 파라미터 시그니처를 가지며, 발생한 예외를 첫 번째 파라미터로 추가로 받습니다. 최종 실패 + * 상태를 기록하고 실패 결과를 반환하는 역할을 합니다. + * + * @param e 재시도를 유발한 마지막 예외 객체 + * @param task 실패한 Task의 도메인 모델 + * @param taskRun 실패한 실행의 기록 객체 + * @param requestBody 실패 당시 사용된 요청 Body + * @return 최종 실패를 나타내는 Task 실행 결과 + * @since v0.1.0 + */ @Recover public TaskRunner.TaskExecutionResult recover( RestClientException e, Task task, TaskRun taskRun, ObjectNode requestBody) { diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowHistoryService.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowHistoryService.java index d04e238f..17887630 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowHistoryService.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowHistoryService.java @@ -10,19 +10,44 @@ import site.icebang.common.dto.PageParams; import site.icebang.common.dto.PageResult; import site.icebang.common.service.PageableService; -import site.icebang.domain.workflow.dto.*; +import site.icebang.domain.workflow.dto.JobRunDto; +import site.icebang.domain.workflow.dto.TaskRunDto; +import site.icebang.domain.workflow.dto.WorkflowHistoryDTO; +import site.icebang.domain.workflow.dto.WorkflowRunDetailResponse; +import site.icebang.domain.workflow.dto.WorkflowRunDto; +import site.icebang.domain.workflow.dto.WorkflowRunLogsResponse; import site.icebang.domain.workflow.mapper.WorkflowHistoryMapper; +/** + * 워크플로우 실행 이력(History) 조회 관련 비즈니스 로직을 처리하는 서비스 클래스입니다. + * + *

이 서비스는 워크플로우 실행 목록의 페이징 처리, 특정 실행 건의 상세 정보 조회 등 읽기 전용(Read-Only) 기능에 집중합니다. + * + *

주요 기능:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Service @RequiredArgsConstructor public class WorkflowHistoryService implements PageableService { private final WorkflowHistoryMapper workflowHistoryMapper; /** - * 워크플로우 런 조회 + * 워크플로우 실행 이력 목록을 페이징 처리하여 조회합니다. * - * @param pageParams pageParams - * @return PageResult + *

이 메소드는 {@code PageableService} 인터페이스를 구현하며, {@code PageResult} 유틸리티를 사용하여 전체 카운트 쿼리와 목록 조회 + * 쿼리를 실행하고 페이징 결과를 생성합니다. + * + * @param pageParams 페이징 처리에 필요한 파라미터 (페이지 번호, 페이지 크기 등) + * @return 페이징 처리된 워크플로우 실행 이력 목록 + * @see PageResult + * @since v0.1.0 */ @Override @Transactional(readOnly = true) @@ -35,10 +60,14 @@ public PageResult getPagedResult(PageParams pageParams) { } /** - * 워크플로우 실행 상세 조회 + * 특정 워크플로우 실행 건의 상세 정보를 조회합니다. + * + *

지정된 실행 ID(`runId`)에 해당하는 워크플로우 실행 정보와, 그에 속한 모든 Job 실행 정보, 그리고 각 Job에 속한 모든 Task 실행 정보를 + * 계층적으로 조회하여 반환합니다. * - * @param runId workflow_run.id - * @return WorkflowRunDetailResponse + * @param runId 조회할 워크플로우 실행의 ID (`workflow_run.id`) + * @return 워크플로우, Job, Task 실행 정보를 포함하는 상세 응답 객체 + * @since v0.1.0 */ @Transactional(readOnly = true) public WorkflowRunDetailResponse getWorkflowRunDetail(Long runId) { @@ -69,10 +98,11 @@ public WorkflowRunDetailResponse getWorkflowRunDetail(Long runId) { } /** - * 워크플로우 실행 로그 조회 + * 특정 워크플로우 실행과 관련된 모든 로그를 조회합니다. * - * @param runId workflow_run.id - * @return WorkflowRunLogsResponse + * @param runId 조회할 워크플로우 실행의 ID (`workflow_run.id`) + * @return 워크플로우 실행 로그 응답 객체 + * @since v0.1.0 */ public WorkflowRunLogsResponse getWorkflowRunLogs(Long runId) { // TODO: 구현 예정 @@ -80,10 +110,11 @@ public WorkflowRunLogsResponse getWorkflowRunLogs(Long runId) { } /** - * TraceId로 워크플로우 실행 조회 + * Trace ID를 사용하여 특정 워크플로우 실행 정보를 조회합니다. * - * @param traceId workflow_run.trace_id - * @return WorkflowRunDetailResponse + * @param traceId 조회할 워크플로우 실행의 Trace ID (`workflow_run.trace_id`) + * @return 워크플로우 실행 상세 응답 객체 + * @since v0.1.0 */ public WorkflowRunDetailResponse getWorkflowRunByTraceId(String traceId) { // TODO: 구현 예정 diff --git a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowService.java b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowService.java index b994c82e..e8c857f3 100644 --- a/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowService.java +++ b/apps/user-service/src/main/java/site/icebang/domain/workflow/service/WorkflowService.java @@ -17,12 +17,38 @@ import site.icebang.domain.workflow.dto.WorkflowDetailCardDto; import site.icebang.domain.workflow.mapper.WorkflowMapper; +/** + * 워크플로우의 '정의'와 관련된 비즈니스 로직을 처리하는 서비스 클래스입니다. + * + *

이 서비스는 워크플로우의 실행(Execution)이 아닌, 생성된 워크플로우의 구조를 조회하는 기능에 집중합니다. + * + *

주요 기능:

+ * + * + * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Service @RequiredArgsConstructor public class WorkflowService implements PageableService { private final WorkflowMapper workflowMapper; + /** + * 워크플로우 목록을 페이징 처리하여 조회합니다. + * + *

이 메소드는 {@code PageableService} 인터페이스를 구현하며, {@code PageResult} 유틸리티를 사용하여 전체 카운트 쿼리와 목록 조회 + * 쿼리를 실행하고 페이징 결과를 생성합니다. + * + * @param pageParams 페이징 처리에 필요한 파라미터 (페이지 번호, 페이지 크기 등) + * @return 페이징 처리된 워크플로우 카드 목록 + * @see PageResult + * @since v0.1.0 + */ @Override @Transactional(readOnly = true) public PageResult getPagedResult(PageParams pageParams) { @@ -32,6 +58,16 @@ public PageResult getPagedResult(PageParams pageParams) { () -> workflowMapper.selectWorkflowCount(pageParams)); } + /** + * 특정 워크플로우의 상세 구조를 조회합니다. + * + *

지정된 워크플로우 ID에 해당하는 기본 정보, 연결된 스케줄 목록, 그리고 Job과 Task의 계층 구조를 모두 조회하여 하나의 DTO로 조합하여 반환합니다. + * + * @param workflowId 조회할 워크플로우의 ID + * @return 워크플로우의 전체 구조를 담은 상세 DTO + * @throws IllegalArgumentException 주어진 ID에 해당하는 워크플로우가 존재하지 않을 경우 + * @since v0.1.0 + */ @Transactional(readOnly = true) public WorkflowDetailCardDto getWorkflowDetail(BigInteger workflowId) { diff --git a/apps/user-service/src/main/java/site/icebang/external/fastapi/adapter/FastApiAdapter.java b/apps/user-service/src/main/java/site/icebang/external/fastapi/adapter/FastApiAdapter.java index 2a5bd001..3ad1466b 100644 --- a/apps/user-service/src/main/java/site/icebang/external/fastapi/adapter/FastApiAdapter.java +++ b/apps/user-service/src/main/java/site/icebang/external/fastapi/adapter/FastApiAdapter.java @@ -10,6 +10,24 @@ import site.icebang.global.config.properties.FastApiProperties; +/** + * 외부 FastAPI 서버와의 모든 HTTP 통신을 전담하는 어댑터 클래스입니다. + * + *

이 클래스는 내부 시스템의 다른 부분들이 외부 시스템의 상세한 통신 방법을 알 필요가 없도록 HTTP 요청/응답 로직을 캡슐화합니다. {@code + * RestTemplate}을 사용하여 실제 통신을 수행하며, 모든 FastAPI 요청은 이 클래스의 {@code call} 메소드를 통해 이루어져야 합니다. + * + *

사용 예제:

+ * + *
{@code
+ * @Autowired
+ * private FastApiAdapter fastApiAdapter;
+ *
+ * String response = fastApiAdapter.call("/keywords/search", HttpMethod.POST, "{\"tag\":\"naver\"}");
+ * }
+ * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Slf4j @Component @RequiredArgsConstructor @@ -18,7 +36,19 @@ public class FastApiAdapter { private final RestTemplate restTemplate; private final FastApiProperties properties; - // 📌 Task나 context에 대한 의존성이 완전히 사라짐 + /** + * FastAPI 서버에 API 요청을 보내는 범용 메소드입니다. + * + *

지정된 엔드포인트, HTTP 메소드, 요청 Body를 사용하여 외부 API를 호출합니다. 통신 성공 시 응답 Body를 문자열로 반환하고, 실패 시 에러 로그를 + * 남기고 null을 반환합니다. + * + * @param endpoint 호출할 엔드포인트 경로 (예: "/keywords/search") + * @param method 사용할 HTTP 메소드 (예: HttpMethod.POST) + * @param requestBody 요청에 담을 JSON 문자열 + * @return 성공 시 API 응답 Body 문자열, 실패 시 null + * @see RestTemplate + * @since v0.1.0 + */ public String call(String endpoint, HttpMethod method, String requestBody) { String fullUrl = properties.getUrl() + endpoint; HttpHeaders headers = new HttpHeaders(); diff --git a/apps/user-service/src/main/java/site/icebang/global/config/QuartzSchedulerInitializer.java b/apps/user-service/src/main/java/site/icebang/global/config/QuartzSchedulerInitializer.java index 233f5834..bdca3015 100644 --- a/apps/user-service/src/main/java/site/icebang/global/config/QuartzSchedulerInitializer.java +++ b/apps/user-service/src/main/java/site/icebang/global/config/QuartzSchedulerInitializer.java @@ -9,6 +9,22 @@ import site.icebang.domain.schedule.service.QuartzScheduleService; import java.util.List; +/** + * 애플리케이션 시작 시 데이터베이스에 저장된 스케줄을 Quartz 스케줄러에 동적으로 등록하는 초기화 클래스입니다. + * + *

이 클래스는 {@code CommandLineRunner}를 구현하여, Spring Boot 애플리케이션이 완전히 + * 로드된 후 단 한 번 실행됩니다. 데이터베이스의 {@code schedule} 테이블을 'Source of Truth'로 삼아, + * 활성화된 모든 스케줄을 읽어와 Quartz 엔진에 동기화하는 매우 중요한 역할을 수행합니다. + * + *

주요 기능:

+ *
    + *
  • 애플리케이션 시작 시점에 DB의 활성 스케줄 조회
  • + *
  • 조회된 스케줄을 {@code QuartzScheduleService}를 통해 Quartz 엔진에 등록
  • + *
+ * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Slf4j @Component @RequiredArgsConstructor @@ -17,6 +33,15 @@ public class QuartzSchedulerInitializer implements CommandLineRunner { private final ScheduleMapper scheduleMapper; private final QuartzScheduleService quartzScheduleService; + /** + * Spring Boot 애플리케이션 시작 시 호출되는 메인 실행 메소드입니다. + * + *

데이터베이스에서 활성화된 모든 스케줄을 조회하고, 각 스케줄을 + * {@code QuartzScheduleService}를 통해 Quartz 스케줄러에 등록합니다. + * + * @param args 애플리케이션 실행 시 전달되는 인자 + * @since v0.1.0 + */ @Override public void run(String... args) { log.info("Quartz 스케줄러 초기화 시작: DB 스케줄을 등록합니다."); diff --git a/apps/user-service/src/main/java/site/icebang/global/config/WebConfig.java b/apps/user-service/src/main/java/site/icebang/global/config/WebConfig.java index 43cfd8b1..7029b7d9 100644 --- a/apps/user-service/src/main/java/site/icebang/global/config/WebConfig.java +++ b/apps/user-service/src/main/java/site/icebang/global/config/WebConfig.java @@ -8,9 +8,37 @@ import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; +/** + * 애플리케이션의 웹 관련 설정을 담당하는 Java 기반 설정 클래스입니다. + * + *

이 클래스는 애플리케이션 전역에서 사용될 웹 관련 빈(Bean)들을 생성하고 구성합니다. 현재는 외부 API 통신을 위한 {@code RestTemplate} 빈을 + * 중앙에서 관리하는 역할을 합니다. + * + *

주요 기능:

+ * + *
    + *
  • 커넥션 및 읽기 타임아웃이 설정된 RestTemplate 빈 생성 + *
+ * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Configuration public class WebConfig { + /** + * 외부 API 통신을 위한 RestTemplate 빈을 생성하여 스프링 컨테이너에 등록합니다. + * + *

기본 {@code RestTemplateBuilder}를 사용하되, 커넥션 및 읽기 타임아웃을 각각 30초로 명시적으로 설정하기 위해 {@code + * SimpleClientHttpRequestFactory}를 구성하여 주입합니다. 이렇게 생성된 RestTemplate 빈은 애플리케이션의 다른 컴포넌트에서 주입받아 외부 + * 시스템과의 HTTP 통신에 사용됩니다. + * + * @param builder Spring Boot가 자동으로 구성해주는 RestTemplateBuilder 객체 + * @return 타임아웃이 설정된 RestTemplate 인스턴스 + * @see RestTemplate + * @see RestTemplateBuilder + * @since v0.1.0 + */ @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { // 1. SimpleClientHttpRequestFactory 객체를 직접 생성 diff --git a/apps/user-service/src/main/java/site/icebang/global/config/mybatis/typehandler/JsonNodeTypeHandler.java b/apps/user-service/src/main/java/site/icebang/global/config/mybatis/typehandler/JsonNodeTypeHandler.java index 4079c9f3..3def3d9d 100644 --- a/apps/user-service/src/main/java/site/icebang/global/config/mybatis/typehandler/JsonNodeTypeHandler.java +++ b/apps/user-service/src/main/java/site/icebang/global/config/mybatis/typehandler/JsonNodeTypeHandler.java @@ -13,11 +13,42 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +/** + * MyBatis에서 Jackson 라이브러리의 {@code JsonNode} 타입을 데이터베이스의 문자열 타입(예: VARCHAR, JSON)과 매핑하기 위한 커스텀 타입 + * 핸들러입니다. + * + *

이 핸들러를 통해, 애플리케이션에서는 JSON 데이터를 편리하게 {@code JsonNode} 객체로 다루고, 데이터베이스에는 해당 객체를 JSON 문자열 형태로 + * 저장하거나 읽어올 수 있습니다. + * + *

MyBatis XML 매퍼에서의 사용 예제:

+ * + *
{@code
+ * 
+ * 
+ * 
+ * }
+ * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @MappedTypes(JsonNode.class) public class JsonNodeTypeHandler extends BaseTypeHandler { private static final ObjectMapper objectMapper = new ObjectMapper(); + /** + * {@code JsonNode} 파라미터를 DB에 저장하기 위해 JSON 문자열로 변환하여 PreparedStatement에 설정합니다. + * + * @param ps PreparedStatement 객체 + * @param i 파라미터 인덱스 + * @param parameter 변환할 JsonNode 객체 + * @param jdbcType JDBC 타입 + * @throws SQLException JSON 직렬화 실패 시 + */ @Override public void setNonNullParameter( PreparedStatement ps, int i, JsonNode parameter, JdbcType jdbcType) throws SQLException { @@ -28,21 +59,52 @@ public void setNonNullParameter( } } + /** + * ResultSet에서 컬럼 이름으로 문자열을 가져와 {@code JsonNode} 객체로 파싱합니다. + * + * @param rs ResultSet 객체 + * @param columnName 컬럼 이름 + * @return 파싱된 JsonNode 객체, 원본이 null이면 null + * @throws SQLException JSON 파싱 실패 시 + */ @Override public JsonNode getNullableResult(ResultSet rs, String columnName) throws SQLException { return parseJson(rs.getString(columnName)); } + /** + * ResultSet에서 컬럼 인덱스로 문자열을 가져와 {@code JsonNode} 객체로 파싱합니다. + * + * @param rs ResultSet 객체 + * @param columnIndex 컬럼 인덱스 + * @return 파싱된 JsonNode 객체, 원본이 null이면 null + * @throws SQLException JSON 파싱 실패 시 + */ @Override public JsonNode getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parseJson(rs.getString(columnIndex)); } + /** + * CallableStatement에서 컬럼 인덱스로 문자열을 가져와 {@code JsonNode} 객체로 파싱합니다. + * + * @param cs CallableStatement 객체 + * @param columnIndex 컬럼 인덱스 + * @return 파싱된 JsonNode 객체, 원본이 null이면 null + * @throws SQLException JSON 파싱 실패 시 + */ @Override public JsonNode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parseJson(cs.getString(columnIndex)); } + /** + * JSON 문자열을 {@code JsonNode} 객체로 변환하는 private 헬퍼 메소드입니다. + * + * @param json 파싱할 JSON 문자열 + * @return 파싱된 JsonNode 객체 + * @throws SQLException JSON 문자열이 유효하지 않을 경우 + */ private JsonNode parseJson(String json) throws SQLException { if (json == null) { return null; diff --git a/apps/user-service/src/main/java/site/icebang/global/config/properties/FastApiProperties.java b/apps/user-service/src/main/java/site/icebang/global/config/properties/FastApiProperties.java index 24fa309d..f35d1ee6 100644 --- a/apps/user-service/src/main/java/site/icebang/global/config/properties/FastApiProperties.java +++ b/apps/user-service/src/main/java/site/icebang/global/config/properties/FastApiProperties.java @@ -8,7 +8,31 @@ import lombok.Getter; import lombok.Setter; -/** FastAPI 연동을 위한 설정값을 application.yml에서 바인딩하는 클래스 */ +/** + * FastAPI 서버 연동을 위한 설정값을 application.yml에서 타입-세이프(Type-safe)하게 바인딩하는 클래스입니다. + * + *

이 클래스는 {@code @ConfigurationProperties}를 통해 'api.fastapi' 경로의 설정값을 자동으로 주입받습니다. + * {@code @Validated}와 {@code @NotBlank}를 사용하여, 필수 설정값(url)이 누락될 경우 애플리케이션 시작 시점에 즉시 에러를 발생시켜 설정 오류를 + * 방지합니다. + * + *

사용 예제:

+ * + *
{@code
+ * @Component
+ * @RequiredArgsConstructor
+ * public class FastApiAdapter {
+ * private final FastApiProperties properties;
+ *
+ * public void someMethod() {
+ * String baseUrl = properties.getUrl(); // 설정된 URL 사용
+ * // ...
+ * }
+ * }
+ * }
+ * + * @author jihu0210@naver.com + * @since v0.1.0 + */ @Getter @Setter @Component // Component로 등록하여 Spring이 Bean으로 관리하도록 함 @@ -16,10 +40,19 @@ @Validated // 아래의 유효성 검사 어노테이션을 활성화 public class FastApiProperties { - /** FastAPI 서버의 기본 URL */ + /** + * FastAPI 서버의 기본 URL 주소입니다. + * + *

{@code @NotBlank} 어노테이션이 적용되어 있어, application.yml 파일에 반드시 값이 존재해야 합니다. (예: + * "http://host.docker.internal:8000") + */ @NotBlank // 값이 비어있을 수 없음을 검증 private String url; - /** API 호출 시 적용될 타임아웃 (밀리초 단위) */ + /** + * API 호출 시 적용될 타임아웃 시간 (밀리초 단위)입니다. + * + *

별도로 설정하지 않을 경우 기본값으로 5000ms (5초)가 적용됩니다. + */ private int timeout = 5000; // 기본값 5초 설정 }