Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b3cea94
test: 업push 테스트
skykim5538 Sep 4, 2025
fb485cd
git commit -m "feat: RDS 연결 테스트 코드 및 설정 추가
skykim5538 Sep 4, 2025
9dc5c1c
feat: 파이프라인 엔진 기본 구조 구축 (진행중)
skykim5538 Sep 4, 2025
76f15a9
dev 브랜치를 feature/scheduler에 병합
skykim5538 Sep 5, 2025
3f8ee14
feat: 파이프라인 뼈대 구현
skykim5538 Sep 5, 2025
268da0c
refactor: 전체 파이프라인 서비스 클래스 구조 개편
skykim5538 Sep 9, 2025
29441fc
Merge branch 'dev' into feature/scheduler
skykim5538 Sep 9, 2025
9c0dfb2
refactor : 스케줄러 파이프라인 테스트 환경 구성 및 todo 추가
skykim5538 Sep 10, 2025
2cec318
refactor : 스케줄러 파이프라인 테스트 환경 구성 및 todo 추가
skykim5538 Sep 10, 2025
ac67051
Merge branch 'dev' into feature/scheduler
skykim5538 Sep 10, 2025
57d2ee8
Merge remote-tracking branch 'origin/feature/common' into feature/sch…
skykim5538 Sep 10, 2025
5469d12
chore: feature/common 브랜치의 최신 변경사항 병합
skykim5538 Sep 10, 2025
92c4194
chore: scheduler, orchestration 디렉토리 위치 변경
skykim5538 Sep 10, 2025
9617299
feat: ScheduleEngineController 엔드포인트 추가
skykim5538 Sep 10, 2025
675ddb2
Merge branch 'dev' into feature/scheduler
ronshalee Sep 10, 2025
a67d728
Merge branch 'feature/scheduler' of https://github.com/Kernel180-BE12…
ronshalee Sep 10, 2025
a9232f0
Merge branch 'dev' of https://github.com/Kernel180-BE12/Final-7team-B…
skykim5538 Sep 11, 2025
ecd8928
feat: 응답 객체 추가 및 테스트
skykim5538 Sep 11, 2025
049a0bf
feat: 상태조회 테스트 코드 추가
skykim5538 Sep 11, 2025
d4c5d3c
Merge branch 'dev' of https://github.com/Kernel180-BE12/Final-7team-B…
skykim5538 Sep 11, 2025
073920e
Merge branch 'dev' into feature/scheduler
skykim5538 Sep 11, 2025
4a23ae4
feat: 표준화된 에러 처리를 위한 ErrorCode enum 추가
JungSoonIn Sep 11, 2025
20a5cab
feat: 비즈니스 로직 예외 처리를 위한 BusinessException 추가
JungSoonIn Sep 11, 2025
b30401e
feat: 표준화된 에러 응답을 위한 ErrorResponseDTO 추가
JungSoonIn Sep 11, 2025
f5d46f7
feat: 전역 예외 처리를 위한 GlobalExceptionHandler 추가
JungSoonIn Sep 11, 2025
f53f7ae
test: 예외 처리 컴포넌트 단위 테스트 추가
JungSoonIn Sep 11, 2025
d75dc68
chore: spotlessApply
JungSoonIn Sep 11, 2025
60e0c17
Merge pull request #117 from Kernel180-BE12/feature/global-exception-…
JungSoonIn Sep 11, 2025
1c04fb2
feat: 스케줄러 기능 추가
skykim5538 Sep 14, 2025
41c0740
Merge branch 'feature/scheduler' into feature/board-ScheduleEngine-me…
ronshalee Sep 14, 2025
9b414d0
Merge branch 'feature/scheduler' into feature/board-ScheduleEngine-me…
ronshalee Sep 14, 2025
3fcfaaa
feat: 스케줄엔진BE-FE 통합 테스트
ronshalee Sep 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions springboot/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ dependencies {

// Oracle JDBC 드라이버
implementation 'com.oracle.database.jdbc:ojdbc8:21.1.0.0'

// MyBatis
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'

// dotenv-java for .env file support
implementation 'io.github.cdimascio:dotenv-java:3.0.0'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.softlabs.aicontents.common.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


// 공통 응답 DTO
// FE -> BE(DTO) 대시보드 중 "스케줄 관리" 카드
// 단순 객체 전달용
// 용도 및 의미만 맞추고 XML에는 alias 적용

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "ScheduleTasksRequestDTO - 스케줄 작업 요청 객체")
public class ScheduleTasksRequestDTO {


@Schema(description = "스케줄러 명칭", example = "Untitled Schedule", requiredMode = Schema.RequiredMode.REQUIRED)
private String taskName;

@Schema(description = "크론 표현식", example = "0 8 * * *")
private String cronExpression ;

@Schema(description = "실행 주기", example="08:00")
private String executionCycle; // "매일 실행/ 주간 실행/ 월간 실행"

@Schema(description ="실행 시간", example="08:00")
private String executionTime ; // "HH:MM" 자동 실행 시간

@Schema(description ="키워드 추출 개수" , example="50")
private int keywordCount ; // 추출 키워드 수량

@Schema(description ="블로그 발행 개수", example="1")
private int publishCount; // 블로그 발행 수량

@Schema(description ="AI 모델명", example="OpenAI GPT-4")
private String aiModel ; // AI 모델명 (예: "OpenAI GPT-4")


}

Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.softlabs.aicontents.common.dto.response;

// 공통 응답 DTO
public record ApiResponseDTO<T>(boolean success, T data, String message) {
public record ApiResponseDTO<T>(
boolean success,
T data,
String message)

{
// 성공 응답 생성
public static <T> ApiResponseDTO<T> success(T data) {
return new ApiResponseDTO<>(true, data, null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.softlabs.aicontents.common.dto.response;

import com.softlabs.aicontents.common.enums.ErrorCode;
import com.softlabs.aicontents.common.util.TraceIdUtil;
import java.time.LocalDateTime;

public record ErrorResponseDTO(
LocalDateTime timestamp, String code, String message, String traceId, String path, int status) {

public static ErrorResponseDTO of(ErrorCode errorCode, String path) {
return new ErrorResponseDTO(
LocalDateTime.now(),
errorCode.getCode(),
errorCode.getMessage(),
TraceIdUtil.getOrCreateTraceId(),
path,
errorCode.getStatus());
}

public static ErrorResponseDTO of(ErrorCode errorCode, String path, String customMessage) {
return new ErrorResponseDTO(
LocalDateTime.now(),
errorCode.getCode(),
customMessage,
TraceIdUtil.getOrCreateTraceId(),
path,
errorCode.getStatus());
}

public static ErrorResponseDTO ofWithTraceId(ErrorCode errorCode, String path, String traceId) {
return new ErrorResponseDTO(
LocalDateTime.now(),
errorCode.getCode(),
errorCode.getMessage(),
traceId,
path,
errorCode.getStatus());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.softlabs.aicontents.common.dto.response;

import lombok.Data;

@Data
public class ScheduleTasksResponseDTO {

private int taskId;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.softlabs.aicontents.common.enums;

import org.springframework.http.HttpStatus;

public enum ErrorCode {

// 400번대 클라이언트 에러
BAD_REQUEST(HttpStatus.BAD_REQUEST, "E001", "잘못된 요청입니다."),
INVALID_INPUT(HttpStatus.BAD_REQUEST, "E002", "입력값이 올바르지 않습니다."),
MISSING_REQUIRED_FIELD(HttpStatus.BAD_REQUEST, "E003", "필수 필드가 누락되었습니다."),
INVALID_FORMAT(HttpStatus.BAD_REQUEST, "E004", "올바르지 않은 형식입니다."),

UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "E101", "인증이 필요합니다."),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "E102", "유효하지 않은 토큰입니다."),
TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "E103", "토큰이 만료되었습니다."),

FORBIDDEN(HttpStatus.FORBIDDEN, "E201", "접근 권한이 없습니다."),
INSUFFICIENT_PERMISSIONS(HttpStatus.FORBIDDEN, "E202", "권한이 부족합니다."),

NOT_FOUND(HttpStatus.NOT_FOUND, "E301", "요청한 리소스를 찾을 수 없습니다."),
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "E302", "사용자를 찾을 수 없습니다."),
DATA_NOT_FOUND(HttpStatus.NOT_FOUND, "E303", "데이터를 찾을 수 없습니다."),

METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "E401", "허용되지 않은 HTTP 메서드입니다."),

CONFLICT(HttpStatus.CONFLICT, "E501", "데이터 충돌이 발생했습니다."),
DUPLICATE_RESOURCE(HttpStatus.CONFLICT, "E502", "이미 존재하는 리소스입니다."),

// 500번대 서버 에러
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E901", "내부 서버 오류가 발생했습니다."),
DATABASE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E902", "데이터베이스 오류가 발생했습니다."),
EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E903", "외부 API 호출 중 오류가 발생했습니다."),
SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "E904", "서비스를 사용할 수 없습니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;

ErrorCode(HttpStatus httpStatus, String code, String message) {
this.httpStatus = httpStatus;
this.code = code;
this.message = message;
}

public HttpStatus getHttpStatus() {
return httpStatus;
}

public String getCode() {
return code;
}

public String getMessage() {
return message;
}

public int getStatus() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.softlabs.aicontents.common.exception;

import com.softlabs.aicontents.common.enums.ErrorCode;

public class BusinessException extends RuntimeException {

private final ErrorCode errorCode;
private final String customMessage;

// 기본 생성자 (ErrorCode만 사용)
// 사용 예시 throw new BusinessException(ErrorCode.USER_NOT_FOUND);
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
this.customMessage = null;
}

// 커스텀 메시지 사용 가능
// 사용 예시 throw new BusinessException(ErrorCode.INVALID_INPUT, "이메일 형식이 잘못되었습니다.");
public BusinessException(ErrorCode errorCode, String customMessage) {
super(customMessage);
this.errorCode = errorCode;
this.customMessage = customMessage;
}

// 원인 예외(cause) 전달 가능
// 사용 예시
// try {
// externalApi.call();
// } catch (IOException e) {
// throw new BusinessException(ErrorCode.EXTERNAL_API_ERROR, e);
// }
public BusinessException(ErrorCode errorCode, Throwable cause) {
super(errorCode.getMessage(), cause);
this.errorCode = errorCode;
this.customMessage = null;
}

// 커스텀 메시지 + 원인 예외 둘 다 사용
// 사용 예시
// try {
// paymentGateway.call();
// } catch (Exception e) {
// throw new BusinessException(
// ErrorCode.EXTERNAL_API_ERROR,
// "결제 서비스 호출 중 오류 발생",
// e
// );
// }
public BusinessException(ErrorCode errorCode, String customMessage, Throwable cause) {
super(customMessage, cause);
this.errorCode = errorCode;
this.customMessage = customMessage;
}

public ErrorCode getErrorCode() {
return errorCode;
}

public String getCustomMessage() {
return customMessage;
}

public String getEffectiveMessage() {
return customMessage != null ? customMessage : errorCode.getMessage();
}
}
Loading