Skip to content

✨ [기능추가] 동적인 스케쥴러를 구현을 위해 배치와 설정을 테스크스케쥴러로 등록 - #108

Merged
moonjun1 merged 30 commits into
devfrom
feat/kakao
Jul 21, 2025
Merged

✨ [기능추가] 동적인 스케쥴러를 구현을 위해 배치와 설정을 테스크스케쥴러로 등록#108
moonjun1 merged 30 commits into
devfrom
feat/kakao

Conversation

@daumi125

@daumi125 daumi125 commented Jul 18, 2025

Copy link
Copy Markdown
Collaborator

스케쥴러 배치, 엘라스틱서치, 핫토픽 스케쥴링 완료

Summary by CodeRabbit

  • 신규 기능

    • 사용자별 뉴스 메시지 발송 및 스케줄 관리 기능이 추가되었습니다.
    • 배치 작업(뉴스 수집, 인덱싱, 핫토픽 저장 등) 자동화 및 관리 기능이 도입되었습니다.
    • 사용자 설정 기반 스케줄링 및 설정 전체 조회 기능이 제공됩니다.
  • 개선 사항

    • API 문서화가 강화되어 Swagger에서 카카오 관련 API 정보를 확인할 수 있습니다.
    • 예외 및 오류 코드가 세분화되어, 인증 실패·뉴스 데이터 없음·스케줄러 오류 등 다양한 상황에 대한 안내가 명확해졌습니다.
    • 로그와 설명이 개선되어 시스템 동작 및 에러 추적이 용이해졌습니다.
  • 버그 수정/정리

    • 사용하지 않는 코드와 엔드포인트가 정리되었습니다.
    • 일부 메서드 및 엔드포인트 명칭이 명확하게 변경되었습니다.
  • 문서화

    • 주요 클래스와 메서드에 Javadoc이 추가되어 코드 이해도가 향상되었습니다.

daumi125 and others added 27 commits July 17, 2025 12:20
[기능추가] 동적인 스케쥴러를 구현을 위해 배치와 설정을 테스크스케쥴러로 등록
[기능추가] 배치, 엘라스틱 서치 테스크 스케쥴러 추가 구현완료
[기능추가] 테스크 스케쥴러에 배치, 핫토핏, 엘라스틱 서치 스케쥴링 완료
@coderabbitai

coderabbitai Bot commented Jul 18, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

스프링 부트 애플리케이션에 뉴스 배치 처리 및 사용자별 카카오 뉴스 메시지 스케줄링 기능이 대규모로 추가 및 개선되었습니다. 배치 스케줄러, 사용자별 스케줄러, 스케줄러 초기화, 커스텀 예외 및 에러코드, Swagger 문서화, 서비스 및 저장소 메서드 확장 등 다양한 영역에서 신규 기능 구현과 리팩토링이 이루어졌습니다.

Changes

파일/경로 요약 변경 내용 요약
.../controller/KakaoController.java Swagger/OpenAPI 어노테이션 추가, 메소드/엔드포인트명 및 Javadoc 정비, 로깅 개선, 불필요 코드 제거
.../service/KakaoMessageService.java 커스텀 예외 및 에러코드 사용, 예외 처리 구조화, 상세 Javadoc 추가, 템플릿 데이터 생성 메서드 추가, 로깅 개선
.../service/KakaoSchedulerService.java 크론 생성 메서드 개선(엔티티 기반), Javadoc 추가, 파라미터 타입 변경 및 리팩토링
.../service/SettingService.java 전체 Setting 조회 및 ID 기반 조회 메서드 2종 추가
.../JWT/JwtAuthenticationFilter.java shouldNotFilter 내부에 빈 줄 추가(로직 변화 없음)
.../Scheduler/BatchSchedulerService.java 뉴스 배치 작업 스케줄링 및 관리용 신규 서비스 클래스 추가, 예외 처리 및 상세 로깅 구현
.../Scheduler/SchedulerInitializer.java 스케줄러 초기화 및 전체 사용자 스케줄링 관리 신규 컴포넌트 추가, 테이블 존재 체크 및 예외 처리
.../Scheduler/TaskSchedulerConfig.java 커스텀 TaskScheduler 빈 제공 신규 설정 클래스 추가(스레드풀, 그레이스풀 셧다운 등)
.../Scheduler/TaskSchedulerService.java 사용자별 뉴스 발송 스케줄 관리 신규 서비스 클래스 추가, 동적 등록/취소, 예외처리, 동시성 관리
.../Repository/SettingRepository.java days 컬렉션을 조인 페치하는 findByIdWithDays(Long) 메서드 추가
.../repository/HistoryRepository.java Javadoc 주석 추가(로직/메서드 변화 없음)
.../Exception/ErrorCode.java 카카오 인증/뉴스/스케줄러 에러코드 7종 추가 및 주석 보강
.../Scheduler/SchedulerConfig.java 테스트용 스케줄러 클래스에 Javadoc 상세 설명 추가(로직 변화 없음)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SchedulerInitializer
    participant SettingService
    participant TaskSchedulerService
    participant KakaoSchedulerService
    participant KakaoMessageService

    SchedulerInitializer->>SettingService: getAllSettings()
    SettingService-->>SchedulerInitializer: List<Setting>
    loop 각 Setting
        SchedulerInitializer->>TaskSchedulerService: scheduleUser(setting)
        TaskSchedulerService->>KakaoSchedulerService: getCron(setting)
        TaskSchedulerService->>KakaoMessageService: sendKakaoMessage(refreshToken, userId)
        KakaoMessageService-->>TaskSchedulerService: (성공/실패)
    end
Loading
sequenceDiagram
    participant BatchSchedulerService
    participant DBBatchService
    participant ESBatchService
    participant HotTopicService
    participant TaskSchedulerService

    BatchSchedulerService->>DBBatchService: runDbBatch()
    DBBatchService-->>BatchSchedulerService: 완료
    BatchSchedulerService->>ESBatchService: runEsBulkIndexing()
    ESBatchService-->>BatchSchedulerService: 완료
    BatchSchedulerService->>HotTopicService: collectAndSaveHotTopics()
    HotTopicService-->>BatchSchedulerService: 완료
    BatchSchedulerService->>TaskSchedulerService: registerUserSchedules()
    TaskSchedulerService-->>BatchSchedulerService: 완료
Loading

Suggested labels

feat

Suggested reviewers

  • daumi125
  • wjkim9

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3fe51 and fd749fa.

📒 Files selected for processing (3)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/ErrorCode.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java (2 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (12)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerConfig.java (2)

8-21: TaskScheduler 설정 검토 및 개선 제안

TaskScheduler 구성이 전반적으로 잘 설계되었습니다. 다음과 같은 개선사항을 제안합니다:

풀 사이즈를 설정 파일에서 관리할 수 있도록 외부화하는 것을 고려해보세요:

+@Value("${scheduler.pool.size:10}")
+private int poolSize;
+
 @Bean("customTaskScheduler")
 public TaskScheduler taskScheduler() {
     ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
-    scheduler.setPoolSize(10); // 동시 작업 수
+    scheduler.setPoolSize(poolSize); // 동시 작업 수

이렇게 하면 환경별로 다른 풀 사이즈를 설정할 수 있습니다.


10-10: 주석 개선 제안

주석의 문법을 개선하여 더 명확하게 작성할 수 있습니다.

-    //동적인 스케쥴러를 사용자로부터 받아오기 위해 taskScheduler로 스케쥴을 동적으로 받아옴.
+    // 동적 스케줄러를 구현하기 위한 TaskScheduler 빈 설정
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (2)

171-180: 에러 처리 개선 승인

DB와 Elasticsearch 간의 데이터 불일치를 우아하게 처리하는 로직으로 개선되었습니다. 예외 발생 대신 경고 로그를 남기고 처리를 계속하는 것은 시스템 안정성 향상에 도움이 됩니다.

주석 처리된 기존 코드를 정리하는 것을 고려해보세요:

-//            News newsitem = newsRepository.findById(Long.parseLong(newsDoc.getId()))
-//                    .orElseThrow(() -> new RuntimeException("뉴스가 존재하지 않습니다: " + newsDoc.getId()));
-
-            /* DB와 ES 동기화 되어있지 않을 시, 잡는 예외 */
+            // DB와 ES 동기화 되어있지 않을 시 처리
             News newsitem = newsRepository.findById(Long.parseLong(newsDoc.getId()))
                     .orElse(null);

174-174: 주석 문법 개선 제안

주석의 문법을 더 명확하게 작성할 수 있습니다.

-            /* DB와 ES 동기화 되어있지 않을 시, 잡는 예외 */
+            // DB와 ES 동기화 불일치 시 예외 처리
SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java (1)

148-155: 주석 처리된 코드 제거 권장

버전 관리 시스템을 통해 이전 코드를 확인할 수 있으므로, 주석 처리된 원본 코드는 제거하는 것이 좋습니다.

-
-        //원본 코드
-//        return path.startsWith("/api/auth/")
-//                || path.startsWith("/login/oauth2/")
-//                || path.startsWith("/oauth2/")
-//                || path.equals("/")
-//                || path.startsWith("/css/")
-//                || path.startsWith("/js/")
-//                || path.startsWith("/images/");
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java (1)

108-164: 주석 처리된 원본 코드 제거 권장

대량의 주석 처리된 코드가 가독성을 해치고 있습니다. Git 히스토리를 통해 확인 가능합니다.

주석 처리된 원본 코드를 모두 제거하시기 바랍니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (2)

15-16: 불필요한 import 제거 필요

@Component@Service 어노테이션을 모두 import했지만, 클래스에서는 @Service만 사용하고 있습니다.

-import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;

41-92: 주석처리된 핵심 기능 구현 완료 필요

스케줄러의 핵심 기능인 scheduleUser 메서드가 주석처리되어 있습니다. 동적 스케줄러 구현이 PR의 목적인 만큼, 이 기능의 구현 완료가 필요합니다.

주석처리된 코드에서 확인된 잠재적 문제점:

  1. 87라인에서 cron 변수가 정의되지 않음
  2. 예외 처리 로직이 포함되어 있어 안정성 고려됨

이 메서드를 활성화하고 완성하는 데 도움이 필요하시면 알려주세요.

SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/controller/KakaoController.java (1)

64-85: 재시도 로직 검토 필요

1차 실패 후 2차 재시도 로직이 구현되어 있지만, 재시도 간격이나 백오프 전략이 없습니다. 연속적인 재시도가 외부 API에 부하를 줄 수 있습니다.

재시도 간격 추가를 고려해보세요:

// 재시도 전 잠시 대기
Thread.sleep(1000); // 1초 대기
.github/workflows/dev-test.yml (1)

67-67: trailing spaces 제거 필요

YAMLlint에서 지적한 trailing spaces 문제를 해결해야 합니다.

-          echo "⏳ Dev 테스트 환경 준비 중..." 
+          echo "⏳ Dev 테스트 환경 준비 중..."

모든 trailing spaces를 제거하세요.

Also applies to: 73-73, 84-84, 89-89, 100-100, 106-106, 112-112, 120-120, 123-123, 128-128, 139-139, 143-143, 146-146, 149-149, 155-155, 158-158, 191-191

docker-compose.yml (1)

160-160: 파일 끝에 개행 문자 추가 필요

YAMLlint에서 지적한 대로 파일 끝에 개행 문자를 추가해야 합니다.

 networks:
   backend:
     driver: bridge
+
.github/workflows/deploy-prod.yml (1)

38-38: YAML 형식 오류 수정 필요

YAMLlint에서 지적한 trailing spaces와 파일 끝 개행 문자 누락을 해결해야 합니다.

-            git pull origin ${{ github.ref_name }} 
+            git pull origin ${{ github.ref_name }}

모든 trailing spaces를 제거하고 파일 끝에 개행 문자를 추가하세요.

Also applies to: 44-44, 48-48, 51-51, 54-54, 63-63, 67-67, 70-70, 73-73, 76-76, 79-79, 82-82, 91-91, 97-97, 100-100, 103-103, 107-107, 111-111, 119-119, 128-128, 137-137, 146-146, 155-155, 161-161, 164-164, 170-170, 177-177, 179-179, 182-182, 185-185, 189-189, 193-193, 195-195, 206-206, 212-212, 239-239

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eeb86d7 and a56b251.

⛔ Files ignored due to path filters (1)
  • SpringBoot/.DS_Store is excluded by !**/.DS_Store
📒 Files selected for processing (16)
  • .github/workflows/deploy-prod.yml (2 hunks)
  • .github/workflows/dev-test.yml (4 hunks)
  • SpringBoot/Dockerfile (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/controller/KakaoController.java (3 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java (3 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerConfig.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1 hunks)
  • SpringBoot/src/main/resources/application-test.properties (2 hunks)
  • SpringBoot/src/main/resources/application.properties (1 hunks)
  • docker-compose.yml (6 hunks)
  • nginx/nginx.conf (0 hunks)
💤 Files with no reviewable changes (1)
  • nginx/nginx.conf
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/dev-test.yml

[error] 67-67: trailing spaces

(trailing-spaces)


[error] 73-73: trailing spaces

(trailing-spaces)


[error] 84-84: trailing spaces

(trailing-spaces)


[error] 89-89: trailing spaces

(trailing-spaces)


[error] 100-100: trailing spaces

(trailing-spaces)


[error] 106-106: trailing spaces

(trailing-spaces)


[error] 112-112: trailing spaces

(trailing-spaces)


[error] 120-120: trailing spaces

(trailing-spaces)


[error] 123-123: trailing spaces

(trailing-spaces)


[error] 128-128: trailing spaces

(trailing-spaces)


[error] 139-139: trailing spaces

(trailing-spaces)


[error] 143-143: trailing spaces

(trailing-spaces)


[error] 146-146: trailing spaces

(trailing-spaces)


[error] 149-149: trailing spaces

(trailing-spaces)


[error] 155-155: trailing spaces

(trailing-spaces)


[error] 158-158: trailing spaces

(trailing-spaces)


[error] 191-191: trailing spaces

(trailing-spaces)

.github/workflows/deploy-prod.yml

[error] 38-38: trailing spaces

(trailing-spaces)


[error] 44-44: trailing spaces

(trailing-spaces)


[error] 48-48: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 54-54: trailing spaces

(trailing-spaces)


[error] 63-63: trailing spaces

(trailing-spaces)


[error] 67-67: trailing spaces

(trailing-spaces)


[error] 70-70: trailing spaces

(trailing-spaces)


[error] 73-73: trailing spaces

(trailing-spaces)


[error] 76-76: trailing spaces

(trailing-spaces)


[error] 79-79: trailing spaces

(trailing-spaces)


[error] 82-82: trailing spaces

(trailing-spaces)


[error] 91-91: trailing spaces

(trailing-spaces)


[error] 97-97: trailing spaces

(trailing-spaces)


[error] 100-100: trailing spaces

(trailing-spaces)


[error] 103-103: trailing spaces

(trailing-spaces)


[error] 107-107: trailing spaces

(trailing-spaces)


[error] 111-111: trailing spaces

(trailing-spaces)


[error] 119-119: trailing spaces

(trailing-spaces)


[error] 128-128: trailing spaces

(trailing-spaces)


[error] 137-137: trailing spaces

(trailing-spaces)


[error] 146-146: trailing spaces

(trailing-spaces)


[error] 155-155: trailing spaces

(trailing-spaces)


[error] 161-161: trailing spaces

(trailing-spaces)


[error] 164-164: trailing spaces

(trailing-spaces)


[error] 170-170: trailing spaces

(trailing-spaces)


[error] 177-177: trailing spaces

(trailing-spaces)


[error] 179-179: trailing spaces

(trailing-spaces)


[error] 182-182: trailing spaces

(trailing-spaces)


[error] 185-185: trailing spaces

(trailing-spaces)


[error] 189-189: trailing spaces

(trailing-spaces)


[error] 193-193: trailing spaces

(trailing-spaces)


[error] 195-195: trailing spaces

(trailing-spaces)


[error] 206-206: trailing spaces

(trailing-spaces)


[error] 212-212: trailing spaces

(trailing-spaces)


[error] 239-239: no new line character at the end of file

(new-line-at-end-of-file)

docker-compose.yml

[error] 160-160: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (23)
SpringBoot/src/main/resources/application-test.properties (1)

4-4: 코드 가독성 향상을 위한 주석 추가 승인

설정 파일의 가독성과 구조화를 위해 주석을 추가한 것은 좋은 개선사항입니다. 각 설정 섹션이 명확하게 구분되어 유지보수가 용이해졌습니다.

Also applies to: 7-7, 13-13, 19-19, 27-27, 53-53, 56-56, 62-62

SpringBoot/Dockerfile (1)

23-25: 타임존 설정 구현 승인

Asia/Seoul 타임존 설정이 올바르게 구현되었습니다. 다음과 같은 좋은 관행을 따르고 있습니다:

  • tzdata 패키지 설치 후 정리를 통한 이미지 크기 최적화
  • 여러 타임존 설정 방법 병행 사용으로 호환성 확보
  • 환경 변수 설정으로 애플리케이션 레벨 타임존 일관성 보장
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java (1)

25-25: 순환 의존성 없음 확인

SchedulerInitializer.java에서 BatchSchedulerService에 대한 참조는 모두 주석 처리되어 있어 현재 순환 의존성이 발생하지 않습니다.
불필요한 주석 코드는 제거하여 가독성을 개선하는 것을 권장드립니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (2)

17-17: @transactional 사용 검토 필요

@Transactional 어노테이션이 import되었지만 클래스나 메서드에서 사용되지 않습니다. 스케줄러 서비스에서 데이터베이스 트랜잭션 처리가 필요한지 검토하세요.


39-39: 스레드 안전성 잘 고려됨

ConcurrentHashMap을 사용하여 멀티스레드 환경에서의 안전성을 적절히 고려했습니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/controller/KakaoController.java (4)

12-14: Swagger 문서화 개선 완료

OpenAPI 3.0 어노테이션을 적절히 추가하여 API 문서화가 크게 개선되었습니다.


23-30: 클래스 레벨 문서화 우수

Javadoc과 @Tag 어노테이션을 통해 클래스의 목적이 명확히 문서화되었습니다.


43-50: 상세한 API 문서화 제공

@Operation 어노테이션으로 API의 동작과 응답 코드를 상세히 문서화했습니다. 개발자 경험 향상에 도움이 됩니다.


60-60: 매개변수화된 로깅으로 개선

log.info("유저 RefreshToken 확인: {}", allUsersRefreshToken) 등과 같이 매개변수화된 로깅을 사용하여 성능과 가독성이 향상되었습니다.

Also applies to: 68-68, 88-88

.github/workflows/dev-test.yml (5)

12-12: 테스트 시간 제한 설정 우수

12분 타임아웃 설정으로 무한 대기 상황을 방지했습니다.


53-60: Gradle 캐시 최적화 우수

Gradle 패키지 캐시를 추가하여 빌드 시간을 크게 단축할 수 있습니다.


64-92: 병렬 서비스 체크 로직 우수

서비스 준비 상태를 병렬로 확인하여 전체 대기 시간을 단축했습니다. 백그라운드 작업과 wait 명령어를 활용한 구현이 효율적입니다.


94-161: 동적 설정 파일 생성 우수

테스트 환경에 필요한 모든 설정을 동적으로 생성하여 환경 독립성을 확보했습니다. 특히 Asia/Seoul 타임존 설정이 일관성 있게 적용되었습니다.


171-179: 실패 시에만 테스트 리포트 업로드

if: failure() 조건을 사용하여 실패 시에만 아티팩트를 업로드하도록 최적화했습니다.

docker-compose.yml (3)

9-9: 타임존 일관성 강화 우수

모든 서비스에 TZ=Asia/Seoul 환경변수와 타임존 관련 볼륨 마운트를 추가하여 시스템 전체의 시간 동기화를 보장했습니다.

Also applies to: 31-32, 49-49, 64-65, 73-73, 77-78, 87-87, 93-94, 103-103, 109-110, 118-118, 131-132


29-29: 포트 매핑 변경 확인

nginx 제거에 따라 Spring Boot 서비스를 직접 80 포트로 노출하도록 변경했습니다. 이는 아키텍처 단순화에 도움이 됩니다.


54-54: MySQL 타임존 설정 추가

MySQL 서버의 기본 타임존을 +09:00으로 설정하여 데이터베이스 레벨에서도 타임존 일관성을 확보했습니다.

.github/workflows/deploy-prod.yml (6)

34-37: 동적 브랜치 처리 개선

${{ github.ref_name }}을 사용하여 동적으로 브랜치를 처리하도록 개선했습니다. 이는 다양한 브랜치에서의 배포를 지원합니다.


45-48: 컨테이너 정리 과정 개선

--remove-orphans --timeout 30 옵션과 docker system prune -f를 추가하여 더 철저한 컨테이너 정리를 수행합니다.


58-121: 고급 디버깅 기능 추가

배포 후 문제 진단을 위한 포괄적인 디버깅 정보 수집 로직을 추가했습니다. Docker 상태, 로그, 네트워크, 시스템 리소스 등을 체계적으로 확인합니다.


123-172: 컨테이너별 개별 테스트 우수

각 컨테이너(MySQL, Redis, Elasticsearch, Spring Boot)에 대해 개별적으로 상태를 확인하는 로직을 추가했습니다. 문제 발생 시 원인을 빠르게 파악할 수 있습니다.


174-221: 유연한 헬스체크 로직

여러 엔드포인트를 순차적으로 테스트하고, 하나라도 성공하면 배포 성공으로 간주하는 유연한 접근 방식을 적용했습니다.


223-239: 배포 요약 정보 개선

GitHub Actions 요약에 동적 브랜치 정보와 유용한 링크들을 포함하여 배포 결과를 명확히 제공합니다.

spring.security.oauth2.client.registration.kakao.client-secret=${KAKAO_CLIENT_SECRET}
spring.security.oauth2.client.registration.kakao.client-authentication-method=client_secret_post
spring.security.oauth2.client.registration.kakao.redirect-uri=http://43.201.27.98/login/oauth2/code/kakao
spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:8080/login/oauth2/code/kakao

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

OAuth2 리다이렉트 URI 변경 사항 검토 필요

리다이렉트 URI가 고정 IP 주소에서 localhost로 변경되었습니다. 이는 로컬 개발 환경에서는 정상 작동하지만, 프로덕션 환경에서는 문제가 될 수 있습니다.

다음 스크립트를 실행하여 다른 환경별 설정 파일이 있는지 확인하겠습니다:


🏁 Script executed:

#!/bin/bash
# 다른 환경별 application.properties 파일들을 찾아서 OAuth2 설정 확인
fd -e properties application | xargs grep -l "oauth2.client.registration.kakao.redirect-uri" || true

Length of output: 203


다음으로 application-test.properties에서 설정된 리다이렉트 URI를 확인합니다:


🏁 Script executed:

#!/bin/bash
grep -n "spring.security.oauth2.client.registration.kakao.redirect-uri" SpringBoot/src/main/resources/application-test.properties || true

Length of output: 239


OAuth2 리다이렉트 URI 환경별 설정 검토 및 수정 필요

현재 두 개의 프로퍼티 파일에서 서로 다른 localhost URI가 설정되어 있습니다. 프로덕션 환경으로 배포 시 실제 도메인·포트로 변경되지 않으면 OAuth2 인증 흐름이 실패할 수 있습니다.

• SpringBoot/src/main/resources/application.properties (default)
– spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:8080/login/oauth2/code/kakao

• SpringBoot/src/main/resources/application-test.properties (test)
– spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost/api/login/oauth2/code/kakao

제안

  1. application-{profile}.properties 혹은 환경 변수로 URI를 외부화하여 개발·테스트·프로덕션마다 별도로 관리
  2. 프로덕션 배포 전에 실제 리다이렉트 URI(예: https://your-domain.com/login/oauth2/code/kakao)로 설정 확인
  3. CI/CD 파이프라인이나 Spring Cloud Config 등을 활용해 환경별 프로퍼티 분리
🤖 Prompt for AI Agents
In SpringBoot/src/main/resources/application.properties at line 45, the OAuth2
redirect URI is hardcoded to a localhost address, which differs from the test
properties and will cause failures in production. Refactor the configuration to
externalize the redirect URI using environment-specific property files like
application-{profile}.properties or environment variables. Ensure that for
production, the redirect URI is set to the actual domain and port, and integrate
this setup into your CI/CD pipeline or configuration management to manage
environment-specific values properly.

Comment on lines 142 to 146
return path.startsWith("/login/oauth2/")
|| path.startsWith("/oauth2/")
|| path.equals("/")
|| path.startsWith("/css/")
|| path.startsWith("/js/")
|| path.startsWith("/images/");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

인증이 필요한 엔드포인트가 제외되었는지 확인 필요

/api/auth// 경로가 JWT 필터 제외 목록에서 제거되었습니다. 이는 보안 강화일 수 있지만, 일반적으로 인증 관련 API는 JWT 없이도 접근 가능해야 합니다.

다음 스크립트를 실행하여 /api/auth/ 엔드포인트가 인증 없이도 접근 가능한지 확인하세요:


🏁 Script executed:

#!/bin/bash
# /api/auth/ 경로를 사용하는 컨트롤러 메서드 확인
ast-grep --pattern 'class $_ {
  $$$
  @$_Mapping($$$"/api/auth/$$$"$$$)
  $_($$$) {
    $$$
  }
  $$$
}'

Length of output: 107


다음 스크립트를 실행하여 /api/auth/ 및 루트(/) 경로에 매핑된 컨트롤러 메서드가 있는지 확인하세요.


🏁 Script executed:

#!/bin/bash
# /api/auth/ 경로를 사용하는 컨트롤러 메서드 확인
rg -n '@.*Mapping.*"/api/auth' .

# 루트("/") 경로를 사용하는 컨트롤러 메서드 확인
rg -n '@.*Mapping.*"/"' .

Length of output: 184


다음 스크립트를 실행하여 /api/auth 컨트롤러 매핑과 정적 리소스(특히 루트 경로) 구성을 확인해주세요:


🏁 Script executed:

#!/bin/bash
echo "=== Index.html 위치 확인 ==="
find SpringBoot/src/main/resources -type f -name "index.html" || echo "index.html 없음"

echo "=== 정적 리소스 디렉토리 목록 확인 (resources/static) ==="
find SpringBoot/src/main/resources/static -maxdepth 1 -type f -print 2>/dev/null || echo "resources/static 디렉토리 없음"

echo "=== AuthController 클래스 및 앞뒤 10줄 ==="
rg -n 'class AuthController' -A10 SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Auth/Controller/AuthController.java

echo "=== AuthController 내 매핑 어노테이션 전체 ==="
rg -n '@.*Mapping' SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Auth/Controller/AuthController.java

Length of output: 1241


JWT 필터 예외 경로에 /api/auth 미포함으로 인한 토큰 처리 실패 우려

AuthController/api/auth에 매핑되어 있어 다음 엔드포인트들이 현재 JWT 필터의 보호 대상에 포함되어 있습니다.

  • POST /api/auth/refresh
  • POST /api/auth/logout
  • GET /api/auth/me
  • GET /api/auth/status

이로 인해 토큰 갱신·로그아웃·사용자 조회 기능이 정상 동작하지 않을 수 있으므로, JWT 필터의 shouldNotFilter 예외 목록에 적절히 추가하거나 경로별 예외 처리 로직을 세분화해야 합니다.

• 대상 파일:
SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java (라인 142–146)

• 제안 diff:

@@ -142,6 +142,7 @@
         return path.startsWith("/login/oauth2/")
                 || path.startsWith("/oauth2/")
+                || path.startsWith("/api/auth/refresh")
                 || path.startsWith("/css/")
                 || path.startsWith("/js/")
                 || path.startsWith("/images/");

필요하다면 /api/auth/logout 등 추가 경로도 함께 검토·추가 바랍니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return path.startsWith("/login/oauth2/")
|| path.startsWith("/oauth2/")
|| path.equals("/")
|| path.startsWith("/css/")
|| path.startsWith("/js/")
|| path.startsWith("/images/");
return path.startsWith("/login/oauth2/")
|| path.startsWith("/oauth2/")
|| path.startsWith("/api/auth/refresh")
|| path.startsWith("/css/")
|| path.startsWith("/js/")
|| path.startsWith("/images/");
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/JWT/JwtAuthenticationFilter.java
around lines 142 to 146, the JWT filter's shouldNotFilter method is missing the
/api/auth path in its exception list, causing token processing failures for
endpoints like /api/auth/refresh, /api/auth/logout, /api/auth/me, and
/api/auth/status. To fix this, add path.startsWith("/api/auth/") to the existing
conditions to exclude these endpoints from JWT filtering, and consider adding
more specific paths like /api/auth/logout if needed for finer control.

Comment on lines +270 to +272
public Setting getById(Long settingId) {
return settingRepository.findById(settingId).get();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Optional.get() 직접 호출은 위험합니다

Optional.get()을 체크 없이 호출하면 값이 없을 때 NoSuchElementException이 발생합니다. 다른 메서드들처럼 적절한 예외 처리가 필요합니다.

 public Setting getById(Long settingId) {
-    return settingRepository.findById(settingId).get();
+    return settingRepository.findById(settingId)
+            .orElseThrow(() -> new SettingException(ErrorCode.SETTING_NOT_FOUND));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Setting getById(Long settingId) {
return settingRepository.findById(settingId).get();
}
public Setting getById(Long settingId) {
return settingRepository.findById(settingId)
.orElseThrow(() -> new SettingException(ErrorCode.SETTING_NOT_FOUND));
}
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java
around lines 270 to 272, the method calls Optional.get() directly without
checking if a value is present, which can cause NoSuchElementException if the
setting is not found. Modify the method to handle the absence of the value
safely by using Optional.orElseThrow() with a custom exception or a suitable
alternative to provide proper exception handling instead of calling get()
directly.

Comment on lines +266 to +268
public List<Setting> getAllSettings() {
return settingRepository.findAll();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

활성 설정만 반환하도록 필터링 추가 고려

getAllSettings()는 삭제된 설정을 포함한 모든 설정을 반환합니다. 스케줄러에서 사용한다면 활성 설정만 필요할 것으로 보입니다.

 public List<Setting> getAllSettings() {
-    return settingRepository.findAll();
+    return settingRepository.findAll().stream()
+            .filter(setting -> !setting.getIsDeleted())
+            .filter(setting -> setting.getEndDate() == null || setting.getEndDate().isAfter(LocalDateTime.now()))
+            .toList();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public List<Setting> getAllSettings() {
return settingRepository.findAll();
}
public List<Setting> getAllSettings() {
return settingRepository.findAll().stream()
.filter(setting -> !setting.getIsDeleted())
.filter(setting -> setting.getEndDate() == null
|| setting.getEndDate().isAfter(LocalDateTime.now()))
.toList();
}
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java
around lines 266 to 268, the getAllSettings() method currently returns all
settings including deleted ones. Modify this method to filter and return only
active settings, for example by adding a condition to query only settings where
the active flag is true or deleted flag is false, depending on your entity
design, so that only active settings are returned.


@PostConstruct
public void scheduleNewsBatch() {
String cron = "0 17 17 * * *";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

테스트용 크론 표현식 수정 필요

현재 크론 표현식이 매일 17:17에 실행되도록 설정되어 있습니다. 주석에는 자정 실행이라고 되어 있는데 불일치합니다.

-        String cron = "0 17 17 * * *";
+        String cron = "0 0 0 * * *"; // 매일 자정
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String cron = "0 17 17 * * *";
String cron = "0 0 0 * * *"; // 매일 자정
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java
at line 36, the cron expression is set to run daily at 17:17, which conflicts
with the comment stating it should run at midnight. Update the cron expression
to "0 0 0 * * *" to schedule the task to run at midnight daily, ensuring the
code matches the comment.

Comment on lines +68 to +76
private boolean isSettingTableAvailable() {
try {
settingRepository.count();
return true;
} catch (Exception e) {
log.warn("[SchedulerInit] 테이블 확인 중 오류 발생: {}", e.getMessage());
return false;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

너무 광범위한 예외 처리

모든 예외를 잡아서 false를 반환하면 실제 설정 문제를 숨길 수 있습니다. 데이터베이스 연결 문제와 테이블 부재를 구분해야 합니다.

 private boolean isSettingTableAvailable() {
     try {
         settingRepository.count();
         return true;
+    } catch (org.springframework.dao.InvalidDataAccessResourceUsageException e) {
+        // 테이블이 없는 경우
+        log.warn("[SchedulerInit] setting 테이블이 존재하지 않습니다: {}", e.getMessage());
+        return false;
     } catch (Exception e) {
-        log.warn("[SchedulerInit] 테이블 확인 중 오류 발생: {}", e.getMessage());
+        log.error("[SchedulerInit] 데이터베이스 접근 중 예상치 못한 오류 발생: {}", e.getMessage(), e);
         return false;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private boolean isSettingTableAvailable() {
try {
settingRepository.count();
return true;
} catch (Exception e) {
log.warn("[SchedulerInit] 테이블 확인 중 오류 발생: {}", e.getMessage());
return false;
}
}
private boolean isSettingTableAvailable() {
try {
settingRepository.count();
return true;
} catch (org.springframework.dao.InvalidDataAccessResourceUsageException e) {
// 테이블이 없는 경우
log.warn("[SchedulerInit] setting 테이블이 존재하지 않습니다: {}", e.getMessage());
return false;
} catch (Exception e) {
log.error("[SchedulerInit] 데이터베이스 접근 중 예상치 못한 오류 발생: {}", e.getMessage(), e);
return false;
}
}
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java
between lines 68 and 76, the method isSettingTableAvailable() currently catches
all exceptions broadly, which can mask real configuration issues. Refine the
exception handling to specifically catch exceptions related to the table not
existing (e.g., DataAccessException or a more specific subclass) and return
false only in those cases. For other exceptions like database connection errors,
allow them to propagate or handle them separately to avoid hiding critical
problems.

[기능 추가] 사용자 셋팅값을 받아 메세지 전송 완료

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1)

66-74: 예외 처리 개선 필요

과거 리뷰 코멘트에서 지적된 바와 같이, 모든 예외를 포괄적으로 처리하면 실제 설정 문제를 숨길 수 있습니다. 테이블 부재와 데이터베이스 연결 문제를 구분하여 처리해야 합니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a56b251 and 66b7201.

📒 Files selected for processing (5)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java (3 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Repository/SettingRepository.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (4)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1)
  • Component (16-76)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java (1)
  • Service (21-71)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/service/SettingService.java (1)
  • Service (43-273)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (1)
  • Service (31-210)
🔇 Additional comments (9)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Repository/SettingRepository.java (2)

12-12: Optional import 추가 승인

새로운 메서드의 반환 타입을 위해 필요한 import가 적절히 추가되었습니다.


35-40: JPQL 쿼리 구현이 우수함

LEFT JOIN FETCH를 사용하여 days 컬렉션을 즉시 로딩하는 것은 스케줄러에서 LazyInitializationException을 방지하는 좋은 접근입니다. 쿼리 문법과 Optional 반환 타입 사용이 적절합니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1)

54-62: 사용자별 스케줄링 로직 승인

각 Setting에 대해 개별적으로 예외 처리를 하면서 스케줄링하는 로직이 적절합니다. 하나의 설정 실패가 전체 스케줄링을 중단시키지 않도록 잘 구현되었습니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (6)

31-32: 커스텀 TaskScheduler 사용 승인

@qualifier를 사용하여 커스텀 TaskScheduler를 명시적으로 주입하는 것이 적절합니다. 이를 통해 스케줄링 설정을 세밀하게 제어할 수 있습니다.


47-49: 즉시 로딩으로 LazyInitializationException 방지

findByIdWithDays() 메서드를 사용하여 days 컬렉션을 즉시 로딩하는 것은 스케줄된 작업에서 발생할 수 있는 LazyInitializationException을 효과적으로 방지합니다.


54-57: 중복 스케줄 방지 로직 우수

기존 스케줄이 있는 경우 취소 후 새로 등록하는 로직이 적절합니다. 이를 통해 중복 스케줄링을 방지할 수 있습니다.


40-40: 스레드 안전한 작업 추적 구현 우수

ConcurrentHashMap을 사용하여 스케줄된 작업을 추적하고 관리하는 것이 멀티스레드 환경에서 안전합니다.

Also applies to: 94-95


101-109: 스케줄 취소 메커니즘 적절

사용자와 설정 ID를 기반으로 특정 스케줄을 취소하는 메커니즘이 잘 구현되었습니다. 적절한 로깅도 포함되어 있습니다.


73-90: 스케줄된 작업의 예외 처리 우수

스케줄된 작업 내에서 설정 존재 여부를 확인하고 예외를 적절히 처리하는 로직이 견고합니다. 하나의 작업 실패가 전체 스케줄러에 영향을 주지 않도록 잘 구현되었습니다.

.map(Auth::getKakaoRefreshToken))
.orElseThrow(() -> new IllegalArgumentException("유저 또는 리프레시 토큰이 존재하지 않습니다."));

log.info("[Scheduler] 토큰 확인 완료 - {}", refreshAccessToken);

ghost Jul 19, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

보안 위험: 리프레시 토큰 로깅

리프레시 토큰을 전체 로그에 출력하는 것은 보안 위험을 초래할 수 있습니다. 토큰의 일부만 마스킹하여 로깅하는 것을 권장합니다.

-        log.info("[Scheduler] 토큰 확인 완료 - {}", refreshAccessToken);
+        log.info("[Scheduler] 토큰 확인 완료 - {}****", refreshAccessToken.substring(0, Math.min(8, refreshAccessToken.length())));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log.info("[Scheduler] 토큰 확인 완료 - {}", refreshAccessToken);
log.info("[Scheduler] 토큰 확인 완료 - {}****",
refreshAccessToken.substring(0, Math.min(8, refreshAccessToken.length())));
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java
at line 66, the code logs the full refresh token which poses a security risk.
Modify the log statement to mask most of the token, for example by showing only
the first few and last few characters with the middle replaced by asterisks or
dots, to prevent exposing sensitive information while still allowing some
traceability.

[기능추가] 문서화 주석 달아둠

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (1)

172-173: 논리 오류: 히스토리 저장 성공 시 null 반환

현재 로직은 히스토리가 성공적으로 저장되면 null을 반환하고 있습니다. 이는 의도와 반대인 것으로 보입니다:

-        if (saveHistory(newsList, settings)) return null;
+        saveHistory(newsList, settings);
         return newsList;

또는 저장 실패 시 예외를 발생시키는 방식을 고려해보세요.

♻️ Duplicate comments (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1)

97-97: 보안 위험: 리프레시 토큰 전체 로깅

리프레시 토큰을 전체 로그에 출력하는 것은 보안상 위험합니다. 토큰의 일부만 마스킹하여 로깅하는 것을 권장합니다.

-        log.info("[Scheduler] 토큰 확인 완료 - {}", refreshAccessToken);
+        log.info("[Scheduler] 토큰 확인 완료 - {}...", refreshAccessToken.substring(0, Math.min(8, refreshAccessToken.length())));
🧹 Nitpick comments (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1)

85-130: 스케줄 등록의 원자성 개선 권장

기존 스케줄을 취소한 후 새 스케줄 등록 과정에서 예외가 발생하면 사용자의 스케줄이 완전히 사라질 수 있습니다. 더 안전한 방식을 고려해보세요:

-        // 이미 등록된 경우 기존 스케줄 취소
-        if (scheduledTasks.containsKey(taskKey)) {
-            cancelUser(userId, settingId);
-        }
+        // 기존 스케줄은 나중에 취소하도록 보관
+        ScheduledFuture<?> existingTask = scheduledTasks.get(taskKey);
 
         // ... 새 스케줄 생성 로직 ...
 
         CronTrigger trigger = new CronTrigger(cron);
         ScheduledFuture<?> future = taskScheduler.schedule(task, trigger);
-        scheduledTasks.put(taskKey, future);
+        
+        // 새 스케줄 등록 성공 시에만 기존 스케줄 취소
+        scheduledTasks.put(taskKey, future);
+        if (existingTask != null) {
+            existingTask.cancel(false);
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66b7201 and 9f3fe51.

📒 Files selected for processing (11)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/controller/KakaoController.java (3 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/repository/HistoryRepository.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (7 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Repository/SettingRepository.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/ErrorCode.java (2 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerConfig.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerConfig.java (1 hunks)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/repository/HistoryRepository.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerConfig.java
🚧 Files skipped from review as they are similar to previous changes (6)
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Mypage/Repository/SettingRepository.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerConfig.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/BatchSchedulerService.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/SchedulerInitializer.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoSchedulerService.java
  • SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/controller/KakaoController.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (1)
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/entity/History.java (1)
  • Entity (13-44)
🔇 Additional comments (5)
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java (1)

138-158: 스케줄 취소 및 키 생성 로직 적절함

스케줄 취소와 키 생성 메서드가 잘 구현되어 있습니다. 사용자별, 설정별로 고유한 키를 생성하여 중복 스케줄을 방지하는 구조가 좋습니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Global/Exception/ErrorCode.java (1)

42-64: 에러 코드 추가 적절함

새로 추가된 카카오 및 스케줄러 관련 에러 코드들이 기존 규칙을 잘 따르고 있으며, 명확한 메시지와 적절한 HTTP 상태 코드를 가지고 있습니다.

SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java (3)

76-86: 예외 처리 개선 확인

RuntimeException 대신 KakaoException을 사용하여 일관된 예외 처리를 구현한 것이 좋습니다.


183-196: 템플릿 데이터 생성 메서드 추가 적절함

뉴스 리스트를 카카오 템플릿용 데이터로 변환하는 헬퍼 메서드가 잘 구현되었습니다. 최대 5개 제한도 적절합니다.


219-224: DB/ES 동기화 문제 처리 개선

ElasticSearch와 DB 간 동기화 문제를 우아하게 처리하도록 개선되었습니다. 예외를 발생시키는 대신 경고 로그를 남기고 계속 진행하는 방식이 적절합니다.

if (newsList == null) return false;

//뉴스가 없을 때 반환할 값을 고민 해볼 것.
if (newsList == null) new KakaoException(ErrorCode.NO_NEWS_DATA);;

ghost Jul 20, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

문법 오류: 예외가 throw되지 않음

예외 객체를 생성하지만 throw하지 않고 있으며, 세미콜론이 중복되어 있습니다:

-            if (newsList == null) new KakaoException(ErrorCode.NO_NEWS_DATA);;
+            if (newsList == null) throw new KakaoException(ErrorCode.NO_NEWS_DATA);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (newsList == null) new KakaoException(ErrorCode.NO_NEWS_DATA);;
if (newsList == null) throw new KakaoException(ErrorCode.NO_NEWS_DATA);
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Domain/Kakao/service/KakaoMessageService.java
at line 102, the code creates a new KakaoException but does not throw it, and
there is an extra semicolon. Fix this by adding the 'throw' keyword before the
new KakaoException to properly throw the exception, and remove the redundant
semicolon.

Comment on lines +119 to +122
} catch (Exception e) {
log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 예외 발생: {}", userId, settingId, e.getMessage(), e);
throw new KakaoException(ErrorCode.MESSAGE_SEND_FAILED);
}

ghost Jul 20, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

예외 처리 개선 필요

일반적인 Exception을 잡는 것보다 구체적인 예외를 처리하는 것이 좋습니다. 또한 원본 예외를 보존해야 합니다:

-        } catch (Exception e) {
-            log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 예외 발생: {}", userId, settingId, e.getMessage(), e);
-            throw new KakaoException(ErrorCode.MESSAGE_SEND_FAILED);
+        } catch (KakaoException e) {
+            log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 카카오 예외 발생: {}", userId, settingId, e.getMessage(), e);
+            throw e; // 원본 예외 재발생
+        } catch (Exception e) {
+            log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 예외 발생: {}", userId, settingId, e.getMessage(), e);
+            throw new KakaoException(ErrorCode.MESSAGE_SEND_FAILED, e); // 원본 예외를 cause로 포함
        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (Exception e) {
log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 예외 발생: {}", userId, settingId, e.getMessage(), e);
throw new KakaoException(ErrorCode.MESSAGE_SEND_FAILED);
}
} catch (KakaoException e) {
log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 카카오 예외 발생: {}", userId, settingId, e.getMessage(), e);
throw e; // 원본 예외 재발생
} catch (Exception e) {
log.error("[Scheduler] 유저 {} / setting {} 메시지 발송 중 예외 발생: {}", userId, settingId, e.getMessage(), e);
throw new KakaoException(ErrorCode.MESSAGE_SEND_FAILED, e); // 원본 예외를 cause로 포함
}
🤖 Prompt for AI Agents
In
SpringBoot/src/main/java/Baemin/News_Deliver/Global/Scheduler/TaskSchedulerService.java
around lines 119 to 122, replace the generic Exception catch block with more
specific exceptions relevant to the message sending process. Also, when throwing
the KakaoException, pass the caught exception as the cause to preserve the
original exception details for better debugging and error tracing.

@moonjun1
moonjun1 merged commit 5767ce1 into dev Jul 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants