diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e218d7a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,272 @@ +name: CD - Deploy to ECS + +on: + push: + branches: [ release ] + workflow_dispatch: + +env: + ECS_REGION: ap-northeast-2 + ECR_PUBLIC_REGION: us-east-1 + + ECS_CLUSTER: monew-cluster-1 + ECR_REPOSITORY: public.ecr.aws/s7e2q2h9/monew + + PUBLIC_SUBNETS_CSV: subnet-00530329356c03add,subnet-008505c5a32cd0093,subnet-08601cd2c0aa46873,subnet-03f28f954846ad79d + ECS_SERVICE_SG: sg-0fbe8feeddc4e8195,sg-08a99e539bed73ed3 + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + strategy: + matrix: + service: + - name: api + dockerfile: Dockerfile.api + tag: monew-api + task_definition: monew-api-task + ecs_service: monew-api-service + container: monew-api-app + port: 8080 + target_group_arn: arn:aws:elasticloadbalancing:ap-northeast-2:381437600029:targetgroup/monew-api-target/188870153e8974df + - name: batch + dockerfile: Dockerfile.batch + tag: monew-batch + task_definition: monew-batch-task + ecs_service: monew-batch-service + container: monew-batch-app + port: 8081 + target_group_arn: arn:aws:elasticloadbalancing:ap-northeast-2:381437600029:targetgroup/monew-batch-target/8c8fd5dc5924e8f6 + - name: monitor + dockerfile: Dockerfile.monitor + tag: monew-monitor + task_definition: monew-monitor-task + ecs_service: monew-monitor-service + container: monew-monitor-app + port: 8082 + target_group_arn: arn:aws:elasticloadbalancing:ap-northeast-2:381437600029:targetgroup/monew-monitor-target/f2041a48b08d9ef9 + - name: prometheus + dockerfile: Dockerfile.prom + tag: prometheus + task_definition: monew-prometheus-task + ecs_service: monew-prometheus-service + container: monew-prometheus-app + port: 9090 + target_group_arn: arn:aws:elasticloadbalancing:ap-northeast-2:381437600029:targetgroup/monew-prometheus-target/01ce4f2072a65a1f + - name: grafana + dockerfile: Dockerfile.grafana + tag: grafana + task_definition: monew-grafana-task + ecs_service: monew-grafana-service + container: monew-grafana-app + port: 3000 + target_group_arn: arn:aws:elasticloadbalancing:ap-northeast-2:381437600029:targetgroup/monew-grafana-target/aece83e522ce15b5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # ===== Public ECR (us-east-1) ===== + - name: Configure AWS credentials for Public ECR + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ${{ env.ECR_PUBLIC_REGION }} + + - name: Debug AWS caller identity (ECR creds) + run: aws sts get-caller-identity --region ${{ env.ECR_PUBLIC_REGION }} + + - name: Login to Public ECR + run: | + aws ecr-public get-login-password --region ${{ env.ECR_PUBLIC_REGION }} | \ + docker login --username AWS --password-stdin public.ecr.aws + + - name: Build and push ${{ matrix.service.name }} image + run: | + set -euo pipefail + docker build -f ${{ matrix.service.dockerfile }} \ + -t ${{ env.ECR_REPOSITORY }}:${{ matrix.service.tag }}-${{ github.sha }} \ + -t ${{ env.ECR_REPOSITORY }}:${{ matrix.service.tag }}-latest . + docker push ${{ env.ECR_REPOSITORY }}:${{ matrix.service.tag }}-${{ github.sha }} + docker push ${{ env.ECR_REPOSITORY }}:${{ matrix.service.tag }}-latest + + # ===== ECS (ap-northeast-2) ===== + - name: Configure AWS credentials for ECS + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ${{ env.ECS_REGION }} + + - name: Debug AWS caller identity (ECS creds) + run: aws sts get-caller-identity --region ${{ env.ECS_REGION }} + + - name: Dry-run DescribeTaskDefinition (debug) + run: | + set -euo pipefail + echo "Family: ${{ matrix.service.task_definition }} Region: ${{ env.ECS_REGION }}" + aws ecs describe-task-definition \ + --task-definition ${{ matrix.service.task_definition }} \ + --region ${{ env.ECS_REGION }} \ + --query 'taskDefinition.taskDefinitionArn' --output text + + - name: Update task definition (swap image) + run: | + set -euo pipefail + FAMILY="${{ matrix.service.task_definition }}" + REGION="${{ env.ECS_REGION }}" + CNAME="${{ matrix.service.container }}" + IMAGE="${{ env.ECR_REPOSITORY }}:${{ matrix.service.tag }}-latest" + + TD=$(aws ecs describe-task-definition \ + --task-definition "$FAMILY" \ + --region "$REGION" \ + --query 'taskDefinition' --output json) + + echo "== Before =="; echo "$TD" | jq '.containerDefinitions[] | {name,image}' + + NEW_TD=$(echo "$TD" | jq \ + --arg IMAGE "$IMAGE" --arg CNAME "$CNAME" ' + .containerDefinitions |= + ( map( if .name == $CNAME then (.image = $IMAGE) else . end ) ) + | del(.taskDefinitionArn, .revision, .status, .requiresAttributes, + .compatibilities, .registeredAt, .registeredBy)') + + echo "== After (preview) =="; echo "$NEW_TD" | jq '.containerDefinitions[] | {name,image}' + + NEW_TASK_ARN=$(aws ecs register-task-definition \ + --cli-input-json "$NEW_TD" \ + --region "$REGION" \ + --query 'taskDefinition.taskDefinitionArn' --output text) + + echo "NEW_TASK_ARN=$NEW_TASK_ARN" >> $GITHUB_ENV + echo "Registered: $NEW_TASK_ARN" + + - name: Create or Update ECS service (auto) + run: | + set -euo pipefail + CLUSTER="${{ env.ECS_CLUSTER }}" + SERVICE="${{ matrix.service.ecs_service }}" + REGION="${{ env.ECS_REGION }}" + TG_ARN="${{ matrix.service.target_group_arn }}" + CONTAINER="${{ matrix.service.container }}" + PORT="${{ matrix.service.port }}" + TASK_DEF="${{ env.NEW_TASK_ARN }}" + + # --- Network (CLI shorthand: 배열 안전 전달) --- + IFS=',' read -r -a SUBNETS_ARR <<< "${{ env.PUBLIC_SUBNETS_CSV }}" + SUBNETS_SH=$(printf '%s,' "${SUBNETS_ARR[@]}"); SUBNETS_SH="[${SUBNETS_SH%,}]" + + IFS=',' read -r -a SGS_ARR <<< "${{ env.ECS_SERVICE_SG }}" + SGS_SH=$(printf '%s,' "${SGS_ARR[@]}"); SGS_SH="[${SGS_SH%,}]" + + NET_SH="awsvpcConfiguration={subnets=${SUBNETS_SH},securityGroups=${SGS_SH},assignPublicIp=ENABLED}" + echo "Network(shorthand): $NET_SH" + + LB_JSON=$(jq -nc \ + --arg tg "$TG_ARN" --arg cn "$CONTAINER" --argjson cp "$PORT" \ + '[{targetGroupArn:$tg, containerName:$cn, containerPort:$cp}]') + + DESC=$(aws ecs describe-services --cluster "$CLUSTER" --services "$SERVICE" --region "$REGION" --output json || true) + FAIL_LEN=$(echo "$DESC" | jq -r '.failures | length // 0') + SVC_LEN=$(echo "$DESC" | jq -r '.services | length // 0') + STATUS=$(echo "$DESC" | jq -r '.services[0].status // empty') + + # 배포 정책: Prometheus만 겹침 금지(0/100), 나머지는 기본(100/200) + if [ "$SERVICE" = "monew-prometheus-service" ]; then + DEPLOY_CONF='maximumPercent=100,minimumHealthyPercent=0' + else + DEPLOY_CONF='maximumPercent=200,minimumHealthyPercent=100' + fi + + if [ "$FAIL_LEN" != "0" ] || [ "$SVC_LEN" = "0" ]; then + echo "🟢 Service not found → create-service" + aws ecs create-service \ + --cluster "$CLUSTER" \ + --service-name "$SERVICE" \ + --task-definition "$TASK_DEF" \ + --desired-count 1 \ + --launch-type FARGATE \ + --platform-version LATEST \ + --deployment-configuration "$DEPLOY_CONF" \ + --deployment-controller "type=ECS" \ + --enable-execute-command \ + --network-configuration "$NET_SH" \ + --load-balancers "$LB_JSON" \ + --region "$REGION" + else + if [ "$STATUS" != "ACTIVE" ]; then + echo "🟠 Service exists but status=$STATUS → delete & recreate" + aws ecs delete-service --cluster "$CLUSTER" --service "$SERVICE" --force --region "$REGION" || true + for i in {1..30}; do + sleep 10 + D=$(aws ecs describe-services --cluster "$CLUSTER" --services "$SERVICE" --region "$REGION" --output json || true) + FL=$(echo "$D" | jq -r '.failures | length // 0') + SL=$(echo "$D" | jq -r '.services | length // 0') + [ "$FL" != "0" ] || [ "$SL" = "0" ] && break + echo "Waiting deletion..." + done + aws ecs create-service \ + --cluster "$CLUSTER" \ + --service-name "$SERVICE" \ + --task-definition "$TASK_DEF" \ + --desired-count 1 \ + --launch-type FARGATE \ + --platform-version LATEST \ + --deployment-configuration "$DEPLOY_CONF" \ + --deployment-controller "type=ECS" \ + --enable-execute-command \ + --network-configuration "$NET_SH" \ + --load-balancers "$LB_JSON" \ + --region "$REGION" + else + echo "🔵 Service ACTIVE" + if [ "$SERVICE" = "monew-prometheus-service" ]; then + echo "🧯 Prometheus: scale down to 0 first (avoid TSDB lock)" + aws ecs update-service \ + --cluster "$CLUSTER" \ + --service "$SERVICE" \ + --desired-count 0 \ + --region "$REGION" + + # 모든 태스크 정지 대기 + for i in {1..60}; do + TASKS=$(aws ecs list-tasks --cluster "$CLUSTER" --service-name "$SERVICE" --region "$REGION" --query 'taskArns' --output json) + if [ "$TASKS" = "[]" ]; then + echo "All prometheus tasks stopped." + break + fi + echo "Waiting prometheus tasks to stop..." + sleep 5 + done + + echo "🔁 Update task def & scale up to 1" + aws ecs update-service \ + --cluster "$CLUSTER" \ + --service "$SERVICE" \ + --task-definition "$TASK_DEF" \ + --desired-count 1 \ + --force-new-deployment \ + --region "$REGION" + else + echo "🔁 Regular rolling update" + aws ecs update-service \ + --cluster "$CLUSTER" \ + --service "$SERVICE" \ + --task-definition "$TASK_DEF" \ + --desired-count 1 \ + --force-new-deployment \ + --region "$REGION" + fi + fi + fi + + - name: Wait for service stability + run: | + set -euo pipefail + aws ecs wait services-stable \ + --cluster ${{ env.ECS_CLUSTER }} \ + --services ${{ matrix.service.ecs_service }} \ + --region ${{ env.ECS_REGION }} + echo "🚀 ${{ matrix.service.name }} deployment completed!" diff --git a/Dockerfile.grafana b/Dockerfile.grafana new file mode 100644 index 0000000..0671b26 --- /dev/null +++ b/Dockerfile.grafana @@ -0,0 +1,8 @@ +FROM grafana/grafana:latest + +# 환경변수 설정 +ENV GF_SECURITY_ADMIN_USER=admin \ + GF_SECURITY_ADMIN_PASSWORD=admin + +# 포트 노출 +EXPOSE 3000 \ No newline at end of file diff --git a/Dockerfile.prom b/Dockerfile.prom new file mode 100644 index 0000000..3b501d4 --- /dev/null +++ b/Dockerfile.prom @@ -0,0 +1,18 @@ +FROM prom/prometheus:latest + +# 설정 파일 +COPY monew-monitor/src/main/resources/prometheus.yml /etc/prometheus/prometheus.yml + +EXPOSE 9090 + +# 핵심: TSDB 경로를 /prometheus 로! +# (ECS 태스크에서 /prometheus를 EFS로 마운트했으므로 여기에 저장됨) +CMD [ \ + "--config.file=/etc/prometheus/prometheus.yml", \ + "--storage.tsdb.path=/prometheus", \ + "--storage.tsdb.retention.time=30d", \ + "--storage.tsdb.retention.size=10GB", \ + "--web.enable-admin-api", \ + "--web.console.libraries=/usr/share/prometheus/console_libraries", \ + "--web.console.templates=/usr/share/prometheus/consoles" \ +] \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.yml similarity index 96% rename from docker-compose.prod.yml rename to docker-compose.yml index bfb0964..70ddb83 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.yml @@ -136,10 +136,11 @@ services: # Prometheus (Metrics Collector) prometheus: - image: prom/prometheus:latest + build: + context: . + dockerfile: Dockerfile.prom + image: monew-prometheus:-latest container_name: monew-prometheus - volumes: - - ./monew-monitor/src/main/resources/prometheus.yml:/etc/prometheus/prometheus.yml:ro ports: - "9090:9090" networks: @@ -148,6 +149,9 @@ services: # Grafana (Visualization) grafana: + build: + context: . + dockerfile: Dockerfile.grafana image: grafana/grafana:latest container_name: monew-grafana environment: diff --git a/monew-api/build.gradle b/monew-api/build.gradle index b84ba3a..c4f5378 100644 --- a/monew-api/build.gradle +++ b/monew-api/build.gradle @@ -19,4 +19,7 @@ dependencies { implementation 'org.springframework.security:spring-security-crypto' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9' implementation 'org.apache.commons:commons-text:1.10.0' // 유사도 계산용 + + implementation("org.springframework.boot:spring-boot-starter-actuator") + runtimeOnly("io.micrometer:micrometer-registry-prometheus") } \ No newline at end of file diff --git a/monew-api/src/main/java/com/monew/monew_api/common/exception/GlobalExceptionHandler.java b/monew-api/src/main/java/com/monew/monew_api/common/exception/GlobalExceptionHandler.java index 00b7400..5688edd 100644 --- a/monew-api/src/main/java/com/monew/monew_api/common/exception/GlobalExceptionHandler.java +++ b/monew-api/src/main/java/com/monew/monew_api/common/exception/GlobalExceptionHandler.java @@ -56,7 +56,12 @@ public ResponseEntity handleValidationExceptions(MethodArgumentNo } @ExceptionHandler(Exception.class) - public ResponseEntity handleUnexpectedException(Exception e, HttpServletRequest request) { + public ResponseEntity handleUnexpectedException(Exception e, HttpServletRequest request) throws Exception { + + if (request.getRequestURI().startsWith("/actuator")) { + throw e; + } + log.error("[서버 내부 오류] 예외 타입: {}, 메시지: {}, URI: {}", e.getClass().getSimpleName(), e.getMessage(), diff --git a/monew-api/src/main/java/com/monew/monew_api/useractivity/controller/UserActivityPerfController.java b/monew-api/src/main/java/com/monew/monew_api/useractivity/controller/UserActivityPerfController.java new file mode 100644 index 0000000..9912e2f --- /dev/null +++ b/monew-api/src/main/java/com/monew/monew_api/useractivity/controller/UserActivityPerfController.java @@ -0,0 +1,46 @@ +package com.monew.monew_api.useractivity.controller; + +import com.monew.monew_api.useractivity.dto.UserActivityDto; +import com.monew.monew_api.useractivity.service.UserActivityCacheService; +import com.monew.monew_api.useractivity.service.UserActivityService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@Slf4j +public class UserActivityPerfController { + + private final UserActivityCacheService cacheService; + private final UserActivityService userActivityService; + + @GetMapping("/api/test/user-activity/{userId}") + public UserActivityDto testUserActivity(@PathVariable String userId, + @RequestParam(defaultValue = "cache") String mode) { + // mode 값: cache | single | multi + long start = System.currentTimeMillis(); + + try { + switch (mode) { + case "cache": + return cacheService.getUserActivityWithCache(userId); + + case "single": + return userActivityService.getUserActivitySingleQuery(userId); + + case "multi": + return userActivityService.getUserActivity(userId); + + default: + throw new IllegalArgumentException("mode 파라미터는 cache | single | multi 중 하나여야 합니다."); + } + } finally { + long elapsed = System.currentTimeMillis() - start; + log.info("[PERF] mode={} took={} ms", mode, elapsed); + } + } +} diff --git a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/ArticleViewActivityDto.java b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/ArticleViewActivityDto.java index 93ee743..d1cc74f 100644 --- a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/ArticleViewActivityDto.java +++ b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/ArticleViewActivityDto.java @@ -2,14 +2,12 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.time.LocalDateTime; @Getter +@Setter @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentActivityDto.java b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentActivityDto.java index f713a95..559ec90 100644 --- a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentActivityDto.java +++ b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentActivityDto.java @@ -2,14 +2,12 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.time.LocalDateTime; @Getter +@Setter @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentLikeActivityDto.java b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentLikeActivityDto.java index 068cf1c..abf4ee9 100644 --- a/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentLikeActivityDto.java +++ b/monew-api/src/main/java/com/monew/monew_api/useractivity/dto/CommentLikeActivityDto.java @@ -2,14 +2,12 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.time.LocalDateTime; @Getter +@Setter @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/monew-api/src/main/java/com/monew/monew_api/useractivity/mapper/UserActivityRawMapper.java b/monew-api/src/main/java/com/monew/monew_api/useractivity/mapper/UserActivityRawMapper.java index 25626d8..be5d5b5 100644 --- a/monew-api/src/main/java/com/monew/monew_api/useractivity/mapper/UserActivityRawMapper.java +++ b/monew-api/src/main/java/com/monew/monew_api/useractivity/mapper/UserActivityRawMapper.java @@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.web.util.HtmlUtils; import java.util.Collections; import java.util.List; @@ -19,15 +20,12 @@ public class UserActivityRawMapper { private final ObjectMapper objectMapper; - /** - * UserActivityRaw (Record) → UserActivityDto 변환 - */ public UserActivityDto toDto(UserActivityRaw record) { if (record == null) { return null; } - return UserActivityDto.builder() + UserActivityDto dto = UserActivityDto.builder() .id(String.valueOf(record.id())) .email(record.email()) .nickname(record.nickname()) @@ -49,11 +47,13 @@ public UserActivityDto toDto(UserActivityRaw record) { new TypeReference>() {} )) .build(); + + // HTML 엔티티 디코딩 + decodeHtmlEntities(dto); + + return dto; } - /** - * JSON String → List 파싱 - */ private List parseJsonList(String json, TypeReference> typeRef) { if (json == null || json.isBlank() || "[]".equals(json.trim())) { return Collections.emptyList(); @@ -67,4 +67,45 @@ private List parseJsonList(String json, TypeReference> typeRef) { return Collections.emptyList(); } } + + /** + * HTML 엔티티 디코딩 (" → " 등) + */ + private void decodeHtmlEntities(UserActivityDto dto) { + // ArticleViews + if (dto.getArticleViews() != null) { + dto.getArticleViews().forEach(av -> { + if (av.getArticleTitle() != null) { + av.setArticleTitle(HtmlUtils.htmlUnescape(av.getArticleTitle())); + } + if (av.getArticleSummary() != null) { + av.setArticleSummary(HtmlUtils.htmlUnescape(av.getArticleSummary())); + } + }); + } + + // Comments + if (dto.getComments() != null) { + dto.getComments().forEach(c -> { + if (c.getContent() != null) { + c.setContent(HtmlUtils.htmlUnescape(c.getContent())); + } + if (c.getArticleTitle() != null) { + c.setArticleTitle(HtmlUtils.htmlUnescape(c.getArticleTitle())); + } + }); + } + + // CommentLikes + if (dto.getCommentLikes() != null) { + dto.getCommentLikes().forEach(cl -> { + if (cl.getArticleTitle() != null) { + cl.setArticleTitle(HtmlUtils.htmlUnescape(cl.getArticleTitle())); + } + if (cl.getCommentContent() != null) { + cl.setCommentContent(HtmlUtils.htmlUnescape(cl.getCommentContent())); + } + }); + } + } } \ No newline at end of file diff --git a/monew-batch/build.gradle b/monew-batch/build.gradle index 7700994..17a9110 100644 --- a/monew-batch/build.gradle +++ b/monew-batch/build.gradle @@ -26,4 +26,7 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.batch:spring-batch-test' + + implementation("org.springframework.boot:spring-boot-starter-actuator") + runtimeOnly("io.micrometer:micrometer-registry-prometheus") } \ No newline at end of file diff --git a/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBackupScheduler.java b/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBackupScheduler.java index 1dfe588..dcbcd4c 100644 --- a/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBackupScheduler.java +++ b/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBackupScheduler.java @@ -19,8 +19,8 @@ public class AricleBackupScheduler { private final AricleBackupService aricleBackupService; -// @Scheduled(cron = "0 20 4 * * *", zone = "Asia/Seoul") - @Scheduled(fixedRate = 600000) // 테스트용 + @Scheduled(cron = "0 20 4 * * *", zone = "Asia/Seoul") +// @Scheduled(fixedRate = 600000) // 테스트용 public void backupNews() { log.info("🗄 뉴스 백업 시작"); aricleBackupService.backupAllArticles(); diff --git a/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBatchScheduler.java b/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBatchScheduler.java index 1077cf3..bfc5f3a 100644 --- a/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBatchScheduler.java +++ b/monew-batch/src/main/java/com/monew/monew_batch/article/scheduler/AricleBatchScheduler.java @@ -35,8 +35,8 @@ public AricleBatchScheduler( this.yonhapRssJob = yonhapRssJob; } -// @Scheduled(cron = "0 0 * * * *", zone = "Asia/Seoul") - @Scheduled(fixedRate = 600000) // 테스트용 + @Scheduled(cron = "0 0 * * * *", zone = "Asia/Seoul") +// @Scheduled(fixedRate = 600000) // 테스트용 public void runJob() throws Exception { log.info("🕒 [Batch Scheduler] 뉴스 수집 Job 실행"); diff --git a/monew-batch/src/main/resources/application-prod.yml b/monew-batch/src/main/resources/application-prod.yml index e39c3ae..f51b6c1 100644 --- a/monew-batch/src/main/resources/application-prod.yml +++ b/monew-batch/src/main/resources/application-prod.yml @@ -66,4 +66,4 @@ aws: monew: api: - url: ${MONEW_API_URL} # 배포 후 추가 필요 \ No newline at end of file + url: ${MONEW_API_URL:http://monew-alb-721921608.ap-northeast-2.elb.amazonaws.com} \ No newline at end of file