Skip to content

version 2.16.0#317

Merged
rlagkswn00 merged 18 commits intomainfrom
develop
Oct 8, 2025
Merged

version 2.16.0#317
rlagkswn00 merged 18 commits intomainfrom
develop

Conversation

@rlagkswn00
Copy link
Member

@rlagkswn00 rlagkswn00 commented Oct 8, 2025

Summary by CodeRabbit

  • New Features

    • 학사일정 알림 자동 발송(매일 09:00) 도입 및 전용 토픽 구독 기본 활성화/설정 지원
    • 관리자용 전체 사용자 구독 재설정 API 추가
    • 학과 목록에 ‘매체연기학과’ 추가
    • 학과 공지 응답에 대학원 공지 지원 여부(graduateSupported) 표시
  • Improvements

    • 학사일정 카테고리 표준화 및 휴일 일정 제외 처리
    • 학사일정 주간(월요일 05:00) 업데이트로 스케줄 조정
    • 오늘의 학사일정 조회 및 토픽 알림 발송 정확도 향상
    • 일부 학과의 대학원 공지 수집 기능을 기능플래그로 제어

jiyun921 and others added 17 commits September 12, 2025 15:26
* DepartmentName에 COMPUTER_GRADUATE 추가

* ComputerScienceGraduateDept 구현

* [feat]: urlPrefix 구현

* [feat]: urlPrefix 적용

* [test]: ComputerScienceGraduateDeptTest 구현

* [refactor]: GraduateDeptInfoTest로 이동

* [feat]: isGrad 필드 추가

* [feat]: isGrad 적용

* [feat]: is_grad 필드 추가 flyway 적용

* [refactor]: getUrlPrefix -> getHostPrefix로 수정

* [refactor]: urlPrefix 삭제

* [feat]: noticeGraudationInfo 추가

* [refactor]: ComputerScienceGraduateDept 삭제

* [refactor]: isGrad 적용

* [refactor]: GraduateDeptInfoTest 개행 추가, TestFileLoader 적용

* [refactor]: isGrad -> graduate로 필드명 수정

* [refactor]: graduate 필드 Null 허용으로 수정

* [refactor]: getter 필드 위에 지정

* [refactor]: setter -> 비즈니스 함수로 수정

* [refactor]: 대학원/학사 DepartmentNoticeUpdater 분리

* [refactor]: 개행처리

* [refactor]: DepartmentNoticeScrapResult 삭제

* [refactor]: LatestPageNoticeApiClient 분리

* [refactor]: creatUrl/scrapHtml 분리

* [refactor]: graduate로 필드 수정

* [refactor]: latestPageGraduateNoticeApiClient 사용

* [refactor]: graduate -> graduated로 이름 수

* [refactor]: 일반 notice는 graduated 필드에 null로 저장

* [refactor]: getGraduated() 오타 수정

* [refactor]: deptInfo.isSupportGraduateScrap() 구현 -  대학원 과정을 지원 안하는 DeptInfo 방어 로직

* [refactor]: ? 개수 수정

* [refactor]: 일반 공지 graduated null로 반환 테스트 추가

* [refactor]: 컨트롤러 graduated 파라미터 required=false로 설정

* [refactor]: 공통 로직 getGraduateDeptInfoList 메서드 추출

* [feat]: noticeGraduationInfo 추가

* [feat]: Notice 관련 테스트에 graduated 적용

* [fix]: noticeGraduationInfo 오타 수정
* feat(AuthConfig): CORS 설정 추가

* feat(AuthConfig): CORS 설정 추가

* feat(Report): reporterId로 필드명 변경

* feat(AdminQueryService): 조회시 Page구현체 사용하도록 변경
* feat: 학사일정 알림 ON/OFF 컬럼 추가 Flyway 쿼리문 작성

* feat: 유저 엔티티에 학사일정 알림 ON/OFF 필드 추가

* feat: 학사일정 알림 FeatureFlag 추가

* fix: 불피요 Getter 삭제

* fix: 학사일정 알림 ON/OFF 컬럼 추가 시 불필요 쿼리문 제거
* feat: 학사일정 알림 ON/OFF 컬럼 추가 Flyway 쿼리문 작성

* feat: 유저 엔티티에 학사일정 알림 ON/OFF 필드 추가

* feat: 학사일정 알림 FeatureFlag 추가

* fix: 불피요 Getter 삭제

* fix: 학사일정 알림 ON/OFF 컬럼 추가 시 불필요 쿼리문 제거

* feat: 학사일정 알림 토글 API 엔드포인트 및 Response Dto추가

* feat: 학사일정 알림 성공 메시지 추가

* feat: 학사일정 알림 토글 UseCase 추가

* feat: 학사일정 알림 토글 서비스 로직 추가

* feat: 학사일정 토글 도메인 테스트 추가

* feat: 학사일정 토글 인수 테스트 추가

* fix: FeatureFlagConfig Remote 설정

* fix: toggleAcademicEventNotification 메서드명 변경 -> updateUserAcademicEventNotification

* fix: 불필요 반환 객체 제거(result, response)

* fix: 학사일정 알림 변경 요청 시 설정값 받도록 수정

* fix: 학사일정 알림 설정 도메인 로직수정

* fix: 유저 인수테스트 및 테스트 수정

* fix: @NotNull javax -> jakarta 임포트 수정
* version 2.15.3 (#304)

* Feat: 컴퓨터공학부 대학원 공지 Scrap 추가  (#291)

* DepartmentName에 COMPUTER_GRADUATE 추가

* ComputerScienceGraduateDept 구현

* [feat]: urlPrefix 구현

* [feat]: urlPrefix 적용

* [test]: ComputerScienceGraduateDeptTest 구현

* [refactor]: GraduateDeptInfoTest로 이동

* [feat]: isGrad 필드 추가

* [feat]: isGrad 적용

* [feat]: is_grad 필드 추가 flyway 적용

* [refactor]: getUrlPrefix -> getHostPrefix로 수정

* [refactor]: urlPrefix 삭제

* [feat]: noticeGraudationInfo 추가

* [refactor]: ComputerScienceGraduateDept 삭제

* [refactor]: isGrad 적용

* [refactor]: GraduateDeptInfoTest 개행 추가, TestFileLoader 적용

* [refactor]: isGrad -> graduate로 필드명 수정

* [refactor]: graduate 필드 Null 허용으로 수정

* [refactor]: getter 필드 위에 지정

* [refactor]: setter -> 비즈니스 함수로 수정

* [refactor]: 대학원/학사 DepartmentNoticeUpdater 분리

* [refactor]: 개행처리

* [refactor]: DepartmentNoticeScrapResult 삭제

* [refactor]: LatestPageNoticeApiClient 분리

* [refactor]: creatUrl/scrapHtml 분리

* [refactor]: graduate로 필드 수정

* [refactor]: latestPageGraduateNoticeApiClient 사용

* [refactor]: graduate -> graduated로 이름 수

* [refactor]: 일반 notice는 graduated 필드에 null로 저장

* [refactor]: getGraduated() 오타 수정

* [refactor]: deptInfo.isSupportGraduateScrap() 구현 -  대학원 과정을 지원 안하는 DeptInfo 방어 로직

* [refactor]: ? 개수 수정

* [refactor]: 일반 공지 graduated null로 반환 테스트 추가

* [refactor]: 컨트롤러 graduated 파라미터 required=false로 설정

* [refactor]: 공통 로직 getGraduateDeptInfoList 메서드 추출

* [feat]: noticeGraduationInfo 추가

* [feat]: Notice 관련 테스트에 graduated 적용

* [fix]: noticeGraduationInfo 오타 수정

* Feat/cors setting

* feat(AuthConfig): CORS 설정 추가

* feat(AuthConfig): CORS 설정 추가

* feat(Report): reporterId로 필드명 변경

* feat(AdminQueryService): 조회시 Page구현체 사용하도록 변경

* Feat: 유저 학사일정 알림 설정 관련 기본 설정 추가 (#302)

* feat: 학사일정 알림 ON/OFF 컬럼 추가 Flyway 쿼리문 작성

* feat: 유저 엔티티에 학사일정 알림 ON/OFF 필드 추가

* feat: 학사일정 알림 FeatureFlag 추가

* fix: 불피요 Getter 삭제

* fix: 학사일정 알림 ON/OFF 컬럼 추가 시 불필요 쿼리문 제거

* Feat: 학사일정 알림 설정 API 추가 (#303)

* feat: 학사일정 알림 ON/OFF 컬럼 추가 Flyway 쿼리문 작성

* feat: 유저 엔티티에 학사일정 알림 ON/OFF 필드 추가

* feat: 학사일정 알림 FeatureFlag 추가

* fix: 불피요 Getter 삭제

* fix: 학사일정 알림 ON/OFF 컬럼 추가 시 불필요 쿼리문 제거

* feat: 학사일정 알림 토글 API 엔드포인트 및 Response Dto추가

* feat: 학사일정 알림 성공 메시지 추가

* feat: 학사일정 알림 토글 UseCase 추가

* feat: 학사일정 알림 토글 서비스 로직 추가

* feat: 학사일정 토글 도메인 테스트 추가

* feat: 학사일정 토글 인수 테스트 추가

* fix: FeatureFlagConfig Remote 설정

* fix: toggleAcademicEventNotification 메서드명 변경 -> updateUserAcademicEventNotification

* fix: 불필요 반환 객체 제거(result, response)

* fix: 학사일정 알림 변경 요청 시 설정값 받도록 수정

* fix: 학사일정 알림 설정 도메인 로직수정

* fix: 유저 인수테스트 및 테스트 수정

* fix: @NotNull javax -> jakarta 임포트 수정

* fix: 교직원 스크랩 DTO변환간 중복 Key 문제 해결

* fix: 불필요 로깅 삭제

* fix: 불필요 주석 삭제

---------

Co-authored-by: 양지윤 <[email protected]>
Co-authored-by: Jiwoo Kim <[email protected]>

* fix: 학사일정 스크랩 주기 원복

* feat: 최초 사용자 토큰 저장 시 학사일정 알림 구독하도록 수정

* feat: 학상일정 알림 설정 시 FCM 구독/구독해제 설정 추가

* feat: 학사일정 알림 토픽 추가

* feat: 학사일정 알림 스케쥴 로직 추가

* feat: 금일 학사일정 DB 조회 로직 구현

* feat: 학사일정 알림 전송 비지니스 로직 추가

* feat: 학사일정 알림 전송 관련 도메인 로직 추가

* feat: FeatureFlagsSupport 객체 추가

* test: IntegrationTestSupport 통합 테스트 수행 전 FeatureFlag 초기화 추가

* feat: FeatureFlagsSupport 객체 추가로 인한 불필요 Mocking 제거

* feat: User 도메인 테스트 추가

* test: AcademicEventNotificationServiceTest 추가

* test: AcademicEventNotificationSchedulerTest 추가

* fix: 메세지 리터럴 변수명 불일치 수정

* fix: 잘못된 try Catch문 삭제

* fix: Refrence Type으로 수정

* fix: Boolean 비교 equals 적용

이걸 까먹네 바본가

* fix: 기본 토픽 구독하는 메서드명 수정

* fix: 학사일정 알림 메시지 포맷 변경

* fix: 학사일정 알림 발송 간 에러 발생시 로직 수정

---------

Co-authored-by: 양지윤 <[email protected]>
Co-authored-by: Jiwoo Kim <[email protected]>
* [feat]: 학과별(학사) 전체 공지 업데이트 테스트 구현

* [refactor]: 변수 이름 graduated로 수정

* [fix]: latestPageGraduateNoticeApiClient 주입 누락 수정

* [feat]: 학과 목록 조회 - graduateSupported 추가

* [refactor]: 학과 목록 조회 - DeptInfo 주입 방식으로 수정

* [feat]: 매체연기학과 추가 구현

* [refactor]: import 수정

* [fix]: 학과 개수 변경 반영
* feat: OCI CI/CD 워크플로우

* fix: plain 파일 제외 하도록 수정
* feat: 학사일정 카테고리 추가

* fix: 학사일정 카테고리 이넘화로 인한 수정

* feat: 학사일정 카테고리 분류 클래스 추가

* feat: 학사일정 알림 분류기 추가

* feat: 학사일정 제목 정규화 클래스 추가

* feat: AcademicEventConverter 내 알림 및 카테고리 분류 추가

* fix: 학사일정 카테고리 추가로 인한 테스트 변경

* fix: 학사일정 카테고리 추가로 인한 테스트 변경

* fix: 학사일정 스크랩 간 공휴일 제거로 인한 검증 횟수 변경

* test: AcademicEventCategorizerTest 추가

* test: AcademicEventNotificationClassifierTest 추가

* test: AcademicEventSummaryNormalizerTest 추가

* test: AcademicEventConverterTest 카테고리, 알림 구분 테스트 추가

* feat: 학사일정 스크랩 주기 변경(월1회 -> 주1회)

* fix: 학사일정 업데이트 스케줄링 간 이벤트 건수 로그레벨 변경

* fix: 소나큐브 대응(Private Constructor)

* fix: 코드래빗 대응(DisplayName 변경)

* fix: 소나큐브 대응(ReDOS문제)

* fix: 소나큐브 대응(ReDOS문제)

* fix: 학사일정 카테고리 이름 공백 제거

* fix: 학사일정 엔티티 변환 시 Optional 반환하도록 수정

* fix: 푸시알림 메시지 생성 시 data 추가

* fix: 로그 오타 수정

* feat: Normalizer 패턴 변경(글자수 제한, Possessive Quantifiers)

* fix: AcademicEventCategorizer private 생성자 추가
* feat: User.getFcmToken getter 추가

* feat: 사용자 모든 구독 정보 가져오는 Repository Method 추가

* remove: 불필요 메서드 삭제

* remove: 모든 사용자 토픽 구독 처리 서비스 로직 추가

* remove: 모든 사용자 토픽 구독 처리 API추가

* remove: 모든 사용자 토픽 구독 처리 인수테스트 추가

* fix: 재구독 시 트랜잭션 어노테이션 제거

* fix: 재구독 성공/실패 개수 로깅

* feat: 학사일정 카테고리 컬럼 사이즈 30으로 변경
* fix: 학사일정 알림 스케쥴 시간 변경(매일 아침 9시)

* fix: 학사일정 업데이트 시간 수정(매주 월요일 오전 5시)
* fix: 대학원 공지사항 스크랩 피처플래그 추가

* feat: 대학원 공지사항 스크랩 피처플래그 적용
@rlagkswn00 rlagkswn00 self-assigned this Oct 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 8, 2025

Walkthrough

  • Dev CI workflow renamed and reworked to build with Gradle and deploy JAR to OCI via SCP/SSH.
  • Admin API replaces GET subscribe-all with POST resubscribe-all; service/ports refactored to batch topic subscriptions using user subscriptions.
  • Academic events: introduce enum category, converter normalization/categorization, notification service/scheduler, queries for today’s events.
  • Notice/Dept: DeptInfo-based DTO/service, new department MEDIA_ACTING, many dept scrapers accept graduate notice client.
  • User: new query for users with subscriptions, academic event topic default and toggle.
  • DB migration increases academic_event.category size.

Changes

Cohort / File(s) Summary
CI: Develop deploy to OCI
.github/workflows/dev.yml
Rename job, add checkout/JDK17/decrypt, Gradle build, verify artifacts, SCP to OCI, SSH restart; remove Heroku.
Admin: Resubscribe all users to topics
.../admin/adapter/in/web/AdminCommandApiV2.java, .../admin/application/port/in/AdminCommandUseCase.java, .../admin/application/service/AdminCommandService.java, .../message/application/service/FirebaseSubscribeService.java, .../admin/application/port/out/AdminUserFeedbackPort.java, src/test/java/.../acceptance/AdminAcceptanceTest.java, src/test/java/.../acceptance/AdminStep.java, .../common/dto/ResponseCodeAndMessages.java
Replace GET with POST endpoint returning BaseResponse; use new use-case method; service refactor to batch subscribe by user subscriptions via FirebaseSubscribePort/UserQueryPort; add ACADEMIC_EVENT_TOPIC const; remove unused feedback token port; add tests and new response code.
User: queries and academic-event topic handling
.../user/adapter/out/persistence/UserPersistenceAdapter.java, .../user/adapter/out/persistence/UserQueryRepository*.java, .../user/application/port/out/UserQueryPort.java, .../user/application/service/UserCommandService.java, .../user/application/service/UserQueryService.java, .../user/domain/User.java, src/test/java/.../user/domain/UserTest.java
Add findAllWithSubscriptions; subscribe/unsubscribe academic-event topic on toggle; default subscribe on new user; expose fcmToken getter and isAcademicEventNotificationEnabled; tests updated/added.
Auth filter: default topic subscriptions
.../auth/interceptor/UserRegisterNonChainingFilter.java
Extract helpers to subscribe default topics; include academic-event topic with dev suffix handling.
Academic event: domain and persistence
.../calendar/domain/AcademicEvent.java, .../calendar/domain/AcademicEventCategory.java, .../calendar/adapter/out/persistence/AcademicEventJdbcRepository.java, .../calendar/adapter/out/persistence/AcademicEventPersistenceAdapter.java, .../calendar/adapter/out/persistence/AcademicEventQueryRepository*.java, .../calendar/application/port/out/AcademicEventQueryPort.java, .../calendar/application/port/out/dto/AcademicEventReadModel.java
Switch category to enum with @Enumerated; add enum; JDBC stores enum name; add findTodayEvents in port/adapter/repo; projection uses stringValue; ReadModel gains date-related helpers.
Academic event: conversion, categorization, notification
.../worker/update/calendar/AcademicEventConverter.java, .../worker/update/calendar/AcademicEventCategorizer.java, .../worker/update/calendar/AcademicEventNotificationClassifier.java, .../worker/update/calendar/AcademicEventSummaryNormalizer.java, .../calendar/application/port/in/AcademicEventNotificationUseCase.java, .../calendar/application/service/AcademicEventNotificationService.java, .../worker/notification/AcademicEventNotificationScheduler.java, .../worker/update/calendar/AcademicEventDbSynchronizer.java, .../worker/update/calendar/AcademicEventUpdater.java
Converter now returns Optional, normalizes summary, filters holidays, categorizes, and classifies notify; add categorizer, classifier, normalizer; add use-case, service sending FCM per today’s events; add daily 09:00 scheduler gated by feature flag; adjust logs; updater cron weekly Monday 05:00.
DB migration
src/main/resources/db/migration/V251008__Alter_academic_event_category_column_size.sql
ALTER academic_event.category VARCHAR length from 20 to 30.
Feature flags
.../common/featureflag/KuringFeatures.java, .../worker/update/notice/DepartmentGraduationNoticeUpdater.java, src/test/java/.../support/FeatureFlagsSupport.java, src/test/java/.../support/IntegrationTestSupport.java
Add UPDATE_DEPARTMENT_GRADUATION_NOTICE; gate graduation updater and skip REAL_ESTATE on full update; add test support to set/refresh remote flags; initialize in integration tests.
Notice/Dept DTOs and service
.../notice/adapter/in/web/dto/NoticeDepartmentNameResponse.java, .../notice/application/port/in/dto/NoticeDepartmentNameResult.java, .../notice/application/service/NoticeQueryService.java
Move to DeptInfo-based flow; add graduateSupported field through DTOs; update filtering/mapping.
Departments: enum and new dept
.../notice/domain/DepartmentName.java, .../worker/scrap/deptinfo/art_design/MediaActingDept.java
Add MEDIA_ACTING enum and new department scraper class with IDs/config. Update EDUCATION korName.
Dept scrapers: graduate client wiring
.../worker/scrap/deptinfo/**/{*.java} (many files)
Add LatestPageGraduateNoticeApiClient to constructors; assign to field across multiple department classes.
Tests: academic events
src/test/java/.../calendar/**
Add tests for notification service, scheduler, categorizer, converter (Optional/holiday), summary normalizer, domain enum updates, synchronizer logs; adjust counts/summaries; remove some feature flag mocks.
Tests: notice/department updaters
src/test/java/.../worker/update/notice/DepartmentNoticeUpdaterTest.java
Add undergraduate updateAll test and assertions.
Tests: acceptance/category
src/test/java/.../acceptance/CategoryAcceptanceTest.java
Update expected dept count from 62 to 63; explicit imports.
Test data config
src/test/java/.../support/DatabaseConfigurator.java
Use AcademicEventCategory enum values in seed data.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Admin
  participant API as AdminCommandApiV2
  participant App as AdminCommandService
  participant U as UserQueryPort
  participant F as FirebaseSubscribePort

  Admin->>API: POST /api/v2/admin/users/subscriptions/all
  API->>App: resubscribeAllUsersToTopics()
  App->>U: findAllWithSubscriptions()
  U-->>App: List<User>
  loop per topic (batched)
    App->>F: subscribeToTopic(tokens[<=500], topic)
    F-->>App: result
  end
  App-->>API: void
  API-->>Admin: 200 BaseResponse("성공적으로 재설정")
Loading
sequenceDiagram
  autonumber
  participant Sched as AcademicEventNotificationScheduler
  participant Use as AcademicEventNotificationUseCase
  participant Q as AcademicEventQueryPort
  participant Msg as FirebaseMessagingPort

  Sched->>Sched: 09:00 cron (if feature ON)
  Sched->>Use: sendTodayAcademicEventNotifications()
  Use->>Q: findTodayEvents(LocalDate.now)
  Q-->>Use: List<AcademicEventReadModel>
  alt no events
    Use-->>Sched: return
  else events present
    loop per event
      Use->>Msg: send(topic=academicEvent[dev], title/body)
      Msg-->>Use: messageId or error
    end
  end
  Use-->>Sched: done
Loading

Possibly related PRs

Poem

(\_/) 빌드가 출발해, OCI로 쓩—
( •ᴗ•)و 사용자 구독 재정렬, 딱!
학사 일정은 정제·분류·알림 뿅—
부서도 한 칸 늘어, 63 꽉!
아침 아홉 시, 토픽에 딩동—
깡총깡총, 로그엔 성공!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning 제목 “version 2.16.0”은 단순히 버전 번호만 표시하고 있어 이번 PR의 주요 변경 사항이나 기능 추가를 알 수 없으며, 변경 내용의 핵심을 요약하지 못하고 있습니다. PR의 가장 중요한 변경 사항을 간결하게 요약하는 제목으로 수정하세요. 예를 들어 “학사 행사 알림 기능 추가 및 전체 사용자 토픽 재구독 API 도입”과 같이 주요 기능 또는 변경 포인트를 반영한 제목을 사용하면 좋습니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.70% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rlagkswn00 rlagkswn00 added the 🚀 Deploy Production deploy production server label Oct 8, 2025
@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 8, 2025

@github-actions
Copy link

github-actions bot commented Oct 8, 2025

Unit Test Results

  74 files  +    5    74 suites  +5   1m 28s ⏱️ +6s
531 tests +136  524 ✔️ +135  7 💤 +1  0 ±0 
534 runs  +136  527 ✔️ +135  7 💤 +1  0 ±0 

Results for commit beb2476. ± Comparison against base commit 62b7653.

@rlagkswn00 rlagkswn00 merged commit 56a8fd4 into main Oct 8, 2025
6 of 7 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (10)
src/test/java/com/kustacks/kuring/support/FeatureFlagsSupport.java (3)

22-22: 주석을 명확하게 개선하세요.

한글 주석 "refresh를 꼭 해주어야 한다..."는 불완전한 설명입니다. 왜 refresh가 필요한지, 어떤 상황에서 문제가 발생할 수 있는지 명확하게 문서화하거나 제거하는 것이 좋습니다.

다음과 같이 개선할 수 있습니다:

-    //refresh를 꼭 해주어야 한다...
+    /**
+     * 피처 플래그 값을 Map 형식으로 설정합니다.
+     * 변경 후 RemoteFeatureFlags를 refresh하여 즉시 반영합니다.
+     */
     public void setMapProperty(String key, boolean value) {

27-33: 예외 처리 또는 로깅 추가를 고려하세요.

remotePropertyResolverMockKuringPropertyRestClient가 아닌 경우 메서드가 아무 동작도 하지 않고 조용히 실패합니다. 이는 테스트 설정 오류를 숨길 수 있습니다.

테스트 환경이 올바르게 구성되지 않았을 때 명확한 피드백을 제공하기 위해 else 절을 추가하는 것을 권장합니다.

다음과 같이 개선할 수 있습니다:

     public void setProperty(String key, Object value) {
         if (remotePropertyResolver instanceof MockKuringPropertyRestClient mockClient) {
             mockClient.setProperty(key, value);
             remoteFeatureFlags.refresh();
-
+        } else {
+            throw new IllegalStateException(
+                "FeatureFlagsSupport는 테스트 환경에서만 사용 가능합니다. " +
+                "RemotePropertyResolver가 MockKuringPropertyRestClient 타입이어야 합니다."
+            );
         }
     }

35-40: 예외 처리 또는 로깅 추가를 고려하세요.

resetProperties 메서드도 setProperty와 동일한 문제가 있습니다. remotePropertyResolver가 예상한 타입이 아닌 경우 조용히 실패하여 테스트 설정 오류를 감춥니다.

다음과 같이 개선할 수 있습니다:

     public void resetProperties() {
         if (remotePropertyResolver instanceof MockKuringPropertyRestClient mockClient) {
             mockClient.resetProperties();
             remoteFeatureFlags.refresh();
+        } else {
+            throw new IllegalStateException(
+                "FeatureFlagsSupport는 테스트 환경에서만 사용 가능합니다. " +
+                "RemotePropertyResolver가 MockKuringPropertyRestClient 타입이어야 합니다."
+            );
         }
     }
src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java (1)

85-97: 테스트 코드 리팩토링을 고려해보세요.

기존 department_scrap_async_test()와 거의 동일한 설정 코드가 반복됩니다. 테스트 가독성과 유지보수성 향상을 위해 다음을 고려해보세요:

  • 공통 설정을 @BeforeEach 메서드로 추출
  • 또는 파라미터화된 테스트(@ParameterizedTest)를 사용하여 메서드와 예상 카운트를 파라미터로 전달

예시:

@ParameterizedTest
@CsvSource({
    "update, 3780",
    "updateAll, 3720"
})
void department_notice_update_test(String methodName, long expectedCount) throws InterruptedException {
    // 공통 테스트 로직
}
src/main/java/com/kustacks/kuring/calendar/application/port/in/AcademicEventNotificationUseCase.java (1)

1-6: LGTM! 인터페이스 설계가 단순하고 명확합니다

Use Case 인터페이스가 단일 책임 원칙을 잘 따르고 있습니다. 선택적으로 JavaDoc을 추가하면 더 좋을 것 같습니다:

/**
 * 오늘의 학사일정에 대한 알림을 전송하는 Use Case
 */
public interface AcademicEventNotificationUseCase {
    
    /**
     * 오늘 예정된 학사일정 중 알림이 활성화된 이벤트들에 대해
     * 구독 중인 사용자들에게 알림을 전송합니다.
     */
    void sendTodayAcademicEventNotifications();
}
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1)

35-55: LGTM! 카테고리 분류 로직이 명확합니다

null 처리와 우선순위 기반 매칭이 잘 구현되어 있습니다. 단, 간단한 substring 매칭을 사용하므로 false positive가 발생할 수 있습니다.

예를 들어:

  • "수강신청 안내" → REGISTRATION_COURSE_GRADE ✓
  • "수강신청안내자료" → REGISTRATION_COURSE_GRADE ✓
  • "비수강신청기간" → REGISTRATION_COURSE_GRADE (의도하지 않은 매칭 가능)

필요하다면 더 정확한 매칭을 위해 정규식이나 단어 경계 검사를 고려할 수 있습니다.

src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1)

13-35: 키워드 목록이 AcademicEventCategorizer와 중복됩니다

ACTION_REQUIRED_KEYWORDS 목록이 AcademicEventCategorizerREGISTRATION_COURSE_GRADE 카테고리 키워드와 상당 부분 중복됩니다.

두 클래스 간 관계를 고려해보세요:

  • 옵션 1: 공통 키워드 레지스트리를 별도 클래스로 분리
  • 옵션 2: Categorizer의 특정 카테고리들을 "액션 필요" 키워드로 재사용
  • 옵션 3: 현재대로 유지하되 주석으로 두 클래스의 목적 차이를 명확히 설명

현재 구조:

  • AcademicEventCategorizer: 이벤트를 카테고리로 분류 (분류 목적)
  • AcademicEventNotificationClassifier: 알림 발송 여부 결정 (액션 목적)

목적이 다르므로 현재대로 유지해도 되지만, 키워드 업데이트 시 두 곳을 모두 수정해야 하는 유지보수 부담이 있습니다.

src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java (1)

117-172: 토픽 구독 배치 크기 조정 및 에러 처리 개선 제안

  • Firebase 공식 최대 배치 크기는 1,000개입니다. 필요 시 500에서 1,000으로 늘려보세요.
  • 배치 단위 예외 처리 시 실패한 개별 토큰 추적이 어려우므로, 개별 토큰 수준 로깅 추가를 고려하세요.
  • user.isAcademicEventNotificationEnabled()User 엔티티에 올바르게 정의되어 있는지 확인해주세요.
src/main/java/com/kustacks/kuring/calendar/application/port/out/dto/AcademicEventReadModel.java (1)

36-50: 날짜 관련 헬퍼 메서드 구현 확인

추가된 날짜 비교 메서드들의 로직이 올바르게 구현되어 있습니다. 다만 isRelatedToDate 메서드는 시작일 또는 종료일이 정확히 일치하는 경우만 true를 반환하므로, 여러 날에 걸친 일정의 경우 중간 날짜들은 제외됩니다.

현재 AcademicEventQueryRepositoryImpl.findTodayEvents에서 DB 레벨 BETWEEN 쿼리로 필터링하고 있으므로 의도된 동작으로 보이나, 메서드 이름이나 JavaDoc으로 이 동작을 명확히 하면 좋겠습니다.

src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java (1)

76-82: 불필요한 StringBuilder 사용

단순 문자열 결합에 StringBuilder를 사용하는 것은 과도합니다. 가독성을 위해 단순 결합 연산자나 String.format을 사용하는 것이 더 명확합니다.

다음과 같이 단순화할 수 있습니다:

     private String createTitle(AcademicEventReadModel event) {
-        return new StringBuilder()
-                .append("[")
-                .append(event.summary())
-                .append("]")
-                .toString();
+        return "[" + event.summary() + "]";
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62b7653 and beb2476.

📒 Files selected for processing (91)
  • .github/workflows/dev.yml (1 hunks)
  • src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java (1 hunks)
  • src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java (1 hunks)
  • src/main/java/com/kustacks/kuring/admin/application/port/out/AdminUserFeedbackPort.java (0 hunks)
  • src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java (4 hunks)
  • src/main/java/com/kustacks/kuring/auth/interceptor/UserRegisterNonChainingFilter.java (2 hunks)
  • src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventJdbcRepository.java (2 hunks)
  • src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventPersistenceAdapter.java (1 hunks)
  • src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepository.java (1 hunks)
  • src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepositoryImpl.java (3 hunks)
  • src/main/java/com/kustacks/kuring/calendar/application/port/in/AcademicEventNotificationUseCase.java (1 hunks)
  • src/main/java/com/kustacks/kuring/calendar/application/port/out/AcademicEventQueryPort.java (1 hunks)
  • src/main/java/com/kustacks/kuring/calendar/application/port/out/dto/AcademicEventReadModel.java (2 hunks)
  • src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java (1 hunks)
  • src/main/java/com/kustacks/kuring/calendar/domain/AcademicEvent.java (3 hunks)
  • src/main/java/com/kustacks/kuring/calendar/domain/AcademicEventCategory.java (1 hunks)
  • src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java (1 hunks)
  • src/main/java/com/kustacks/kuring/common/featureflag/KuringFeatures.java (1 hunks)
  • src/main/java/com/kustacks/kuring/message/application/service/FirebaseSubscribeService.java (1 hunks)
  • src/main/java/com/kustacks/kuring/notice/adapter/in/web/dto/NoticeDepartmentNameResponse.java (1 hunks)
  • src/main/java/com/kustacks/kuring/notice/application/port/in/dto/NoticeDepartmentNameResult.java (1 hunks)
  • src/main/java/com/kustacks/kuring/notice/application/service/NoticeQueryService.java (4 hunks)
  • src/main/java/com/kustacks/kuring/notice/domain/DepartmentName.java (1 hunks)
  • src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserPersistenceAdapter.java (1 hunks)
  • src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserQueryRepository.java (1 hunks)
  • src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserQueryRepositoryImpl.java (1 hunks)
  • src/main/java/com/kustacks/kuring/user/application/port/out/UserQueryPort.java (1 hunks)
  • src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java (3 hunks)
  • src/main/java/com/kustacks/kuring/user/application/service/UserQueryService.java (3 hunks)
  • src/main/java/com/kustacks/kuring/user/domain/User.java (2 hunks)
  • src/main/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationScheduler.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/architecture/ArchitectureDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/IndustrialDesignDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/LivingDesignDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MediaActingDept.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MovingImageFilmDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/EducationalTechnologyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/EnglishEducationDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/PhysicalEducationDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/BiologicalDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ChemicalDivisionDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/CivilEnvironmentDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ComputerScienceDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ElectricalElectronicsDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/IndustrialDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/BioMedicalScienceDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/CosmeticsDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/EnergyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/ChineseDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/CultureContentDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/EnglishDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/GeologyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/HistoryDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/KoreanDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/MediaCommunicationDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/PhilosophyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/real_estate/RealEstateDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/AnimalScienceTechnologyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/BiologicalSciencesDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/FoodMarketingSafetyDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/science/MathematicsDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/science/PhysicsDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/InternationalTradeDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/PoliticalScienceDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/PublicAdministrationDept.java (3 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverter.java (4 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventDbSynchronizer.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventUpdater.java (1 hunks)
  • src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentGraduationNoticeUpdater.java (3 hunks)
  • src/main/resources/db/migration/V251008__Alter_academic_event_category_column_size.sql (1 hunks)
  • src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java (2 hunks)
  • src/test/java/com/kustacks/kuring/acceptance/AdminStep.java (1 hunks)
  • src/test/java/com/kustacks/kuring/acceptance/CategoryAcceptanceTest.java (2 hunks)
  • src/test/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationServiceTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/calendar/domain/AcademicEventTest.java (4 hunks)
  • src/test/java/com/kustacks/kuring/support/DatabaseConfigurator.java (3 hunks)
  • src/test/java/com/kustacks/kuring/support/FeatureFlagsSupport.java (1 hunks)
  • src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java (2 hunks)
  • src/test/java/com/kustacks/kuring/user/domain/UserTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizerTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverterTest.java (3 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventDbSynchronizerTest.java (3 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifierTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizerTest.java (1 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventUpdaterTest.java (4 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java (2 hunks)
  • src/test/java/com/kustacks/kuring/worker/update/user/UserUpdaterTest.java (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/com/kustacks/kuring/admin/application/port/out/AdminUserFeedbackPort.java
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-23T19:59:11.021Z
Learnt from: rlagkswn00
PR: ku-ring/ku-ring-backend-web#305
File: src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java:393-397
Timestamp: 2025-09-23T19:59:11.021Z
Learning: User 도메인 클래스에서 academicEventNotificationEnabled 필드는 선언부에서 = Boolean.TRUE로 기본값이 설정되어 있어, 신규 사용자 생성 시 자동으로 true 값을 가집니다.

Applied to files:

  • src/test/java/com/kustacks/kuring/user/domain/UserTest.java
  • src/main/java/com/kustacks/kuring/user/domain/User.java
  • src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java
📚 Learning: 2025-09-23T19:59:11.021Z
Learnt from: rlagkswn00
PR: ku-ring/ku-ring-backend-web#305
File: src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java:393-397
Timestamp: 2025-09-23T19:59:11.021Z
Learning: User 도메인 클래스에서 academicEventNotificationEnabled 필드는 Boolean 타입으로 선언되어 있으며, 생성자에서 명시적으로 초기화하지 않아 기본값이 null입니다. isAcademicEventNotificationEnabled() 메서드는 Boolean.TRUE.equals()를 사용하여 null일 때 false를 반환합니다.

Applied to files:

  • src/test/java/com/kustacks/kuring/user/domain/UserTest.java
  • src/main/java/com/kustacks/kuring/user/domain/User.java
📚 Learning: 2025-09-23T19:44:49.591Z
Learnt from: rlagkswn00
PR: ku-ring/ku-ring-backend-web#305
File: src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java:46-58
Timestamp: 2025-09-23T19:44:49.591Z
Learning: IntegrationTestSupport 클래스는 BeforeEach setUp() 메서드에서 featureFlagsSupport.resetProperties()를 호출하여 테스트 간 피처 플래그 상태를 자동으로 초기화한다.

Applied to files:

  • src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java
  • src/test/java/com/kustacks/kuring/support/FeatureFlagsSupport.java
📚 Learning: 2025-09-23T19:44:49.591Z
Learnt from: rlagkswn00
PR: ku-ring/ku-ring-backend-web#305
File: src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java:46-58
Timestamp: 2025-09-23T19:44:49.591Z
Learning: IntegrationTestSupport 클래스는 BeforeEach setUp() 메서드에서 featureFlagsSupport.resetProperties()를 호출하여 테스트 간 피처 플래그 상태를 자동으로 초기화한다. 따라서 통합 테스트에서는 피처 플래그 리셋을 별도로 구현할 필요가 없다.

Applied to files:

  • src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java
  • src/test/java/com/kustacks/kuring/support/FeatureFlagsSupport.java
🧬 Code graph analysis (15)
src/test/java/com/kustacks/kuring/acceptance/CategoryAcceptanceTest.java (1)
src/test/java/com/kustacks/kuring/acceptance/CategoryStep.java (1)
  • CategoryStep (14-82)
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MediaActingDept.java (3)
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/IndustrialDesignDept.java (1)
  • RegisterDepartmentMap (15-36)
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/LivingDesignDept.java (1)
  • RegisterDepartmentMap (16-37)
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MovingImageFilmDept.java (1)
  • RegisterDepartmentMap (15-36)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverter.java (4)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventDbSynchronizer.java (1)
  • Slf4j (18-87)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1)
  • NoArgsConstructor (13-61)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1)
  • NoArgsConstructor (9-73)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (1)
  • NoArgsConstructor (7-97)
src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java (1)
src/test/java/com/kustacks/kuring/acceptance/AdminStep.java (1)
  • AdminStep (13-140)
src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java (3)
src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepositoryImpl.java (1)
  • RequiredArgsConstructor (17-96)
src/main/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationScheduler.java (1)
  • Slf4j (11-32)
src/main/java/com/kustacks/kuring/message/application/service/FirebaseSubscribeService.java (1)
  • Slf4j (20-65)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverterTest.java (3)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizerTest.java (1)
  • DisplayName (11-83)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifierTest.java (1)
  • DisplayName (12-193)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizerTest.java (1)
  • DisplayName (9-94)
src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java (1)
src/test/java/com/kustacks/kuring/calendar/domain/AcademicEventTest.java (1)
  • DisplayName (14-114)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (2)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1)
  • NoArgsConstructor (13-61)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1)
  • NoArgsConstructor (9-73)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizerTest.java (1)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifierTest.java (1)
  • DisplayName (12-193)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (2)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1)
  • NoArgsConstructor (13-61)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (1)
  • NoArgsConstructor (7-97)
src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepositoryImpl.java (1)
src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java (1)
  • Transactional (28-180)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (2)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1)
  • NoArgsConstructor (9-73)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (1)
  • NoArgsConstructor (7-97)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifierTest.java (3)
src/test/java/com/kustacks/kuring/calendar/domain/AcademicEventTest.java (1)
  • DisplayName (14-114)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizerTest.java (1)
  • DisplayName (11-83)
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizerTest.java (1)
  • DisplayName (9-94)
src/test/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationServiceTest.java (1)
src/test/java/com/kustacks/kuring/calendar/domain/AcademicEventTest.java (1)
  • DisplayName (14-114)
src/main/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationScheduler.java (2)
src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepositoryImpl.java (1)
  • RequiredArgsConstructor (17-96)
src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java (1)
  • Slf4j (20-104)
🪛 actionlint (1.7.7)
.github/workflows/dev.yml

15-15: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


19-19: the runner of "actions/setup-java@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🔇 Additional comments (111)
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventDbSynchronizer.java (1)

43-43: 로깅 레벨 변경이 적절합니다.

디버그에서 인포 레벨로의 변경은 학사일정 동기화 결과에 대한 운영 가시성을 향상시킵니다. 동기화 시작 로그(28번 줄)와 일관성을 유지하며, 프로덕션 환경에서 모니터링에 유용한 정보입니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/PhilosophyDept.java (3)

4-4: LGTM! 임포트 추가가 적절합니다.

새로운 LatestPageGraduateNoticeApiClient 의존성을 사용하기 위한 임포트가 올바르게 추가되었습니다.


18-23: LGTM! 생성자 시그니처 확장이 적절합니다.

LatestPageGraduateNoticeApiClient 파라미터가 생성자에 올바르게 추가되었으며, 의존성 주입 패턴을 따르고 있습니다. AI 요약에 따르면 이 변경은 여러 학과 클래스에 걸쳐 일관되게 적용되고 있습니다.


34-34: LGTM! 필드 할당이 적절합니다.

latestPageGraduateNoticeApiClient 필드에 생성자 파라미터가 올바르게 할당되었습니다. 다른 필드 할당과 일관된 패턴을 따르고 있습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/EnergyDept.java (1)

4-4: LGTM! 대학원 공지 클라이언트 의존성 추가가 올바릅니다.

생성자에 LatestPageGraduateNoticeApiClient 파라미터를 추가하고 필드에 할당하는 변경사항이 명확하고 일관된 패턴을 따르고 있습니다. 의존성 주입 방식이 적절하며 다른 필드 할당과 동일한 패턴을 사용하고 있습니다.

Also applies to: 21-22, 34-34

src/main/java/com/kustacks/kuring/notice/domain/DepartmentName.java (2)

80-80: 새로운 학과 추가가 적절합니다.

MEDIA_ACTING enum 상수가 기존 패턴을 따라 올바르게 추가되었습니다.


88-88: 한글 명칭 수정이 적절합니다.

EDUCATION의 한글 명칭이 "교육학과"에서 "교직과"로 정확하게 수정되었습니다.

src/main/java/com/kustacks/kuring/notice/adapter/in/web/dto/NoticeDepartmentNameResponse.java (1)

5-18: 대학원 지원 여부 필드 추가가 적절합니다.

graduateSupported 필드가 record에 올바르게 추가되었고, from() 메서드에서도 적절히 매핑되고 있습니다.

src/main/java/com/kustacks/kuring/notice/application/port/in/dto/NoticeDepartmentNameResult.java (1)

6-15: DeptInfo 기반 팩토리 메서드로의 리팩토링이 적절합니다.

from() 메서드가 DeptInfo를 파라미터로 받도록 변경되었고, graduateSupported 필드가 추가되어 대학원 공지 지원 여부를 전달할 수 있게 되었습니다. 구현이 올바르게 되어 있습니다.

src/main/java/com/kustacks/kuring/notice/application/service/NoticeQueryService.java (2)

52-65: DeptInfo 기반 구조로의 전환이 적절합니다.

서비스가 List<DepartmentName>에서 List<DeptInfo>로 변경되어 학과 정보와 대학원 지원 여부를 함께 관리할 수 있게 되었습니다. 생성자와 필드가 일관성 있게 업데이트되었습니다.


196-201: 필터링 로직이 올바르게 리팩토링되었습니다.

convertDepartmentNameDtos() 메서드가 DeptInfo를 처리하도록 업데이트되었고, dept.getDepartmentName()을 통해 올바르게 학과명에 접근하여 필터링하고 있습니다.

src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java (1)

44-45: LGTM! 피처 플래그 초기화가 올바르게 구현되었습니다.

FeatureFlagsSupport를 주입하고 setUp() 메서드에서 resetProperties()를 호출하여 테스트 간 피처 플래그 상태를 자동으로 초기화하는 패턴이 올바르게 적용되었습니다. Based on learnings

Also applies to: 59-59

src/test/java/com/kustacks/kuring/worker/update/user/UserUpdaterTest.java (1)

70-70: LGTM! 피처 플래그 설정 방식이 개선되었습니다.

Mockito 기반 모킹 대신 FeatureFlagsSupport를 사용하여 피처 플래그를 직접 제어하는 방식으로 변경되어, 테스트 코드가 더 명확하고 관리하기 쉬워졌습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/science/PhysicsDept.java (1)

4-4: 의존성 주입 승인 및 필드 선언 검증 완료
LatestPageGraduateNoticeApiClient가 DeptInfo.java의 protected 필드로 선언되어 있으며, 생성자 인자와 필드 할당이 일치합니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/real_estate/RealEstateDept.java (1)

4-4: LGTM! 생성자 매개변수 추가가 올바르게 적용되었습니다.

Import, 생성자 파라미터, 필드 할당이 정확히 추가되었음을 확인했습니다.
다만 다음 사항은 직접 검증해주세요:

  1. 부모 클래스 RealEstateCollegelatestPageGraduateNoticeApiClient 필드가 선언되어 있는지
  2. RealEstateDept 인스턴스화 시 새로운 4번째 매개변수가 전달되도록 모든 호출부가 업데이트되었는지
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/KoreanDept.java (1)

4-4: LGTM! 생성자에 추가된 의존성 주입이 정상 동작합니다.
LatestPageGraduateNoticeApiClient 필드는 DeptInfo에 선언되어 있어 상속을 통해 올바르게 주입됩니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/FoodMarketingSafetyDept.java (2)

18-23: 생성자 파라미터 추가가 적절합니다.

대학원 공지사항 기능을 위한 LatestPageGraduateNoticeApiClient 파라미터가 올바르게 추가되었습니다. 파라미터 순서와 네이밍이 적절합니다.


34-34: 필드 선언 확인 완료
latestPageGraduateNoticeApiClient 필드는 DeptInfo.java 27행에 protected로 선언되어 있어 모든 자식 클래스에서 안전하게 할당됩니다. 별도 조치 불필요합니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ElectricalElectronicsDept.java (2)

4-4: 새로운 의존성 추가가 적절합니다.

대학원 공지사항 처리를 위한 전용 클라이언트를 도입하는 것은 관심사의 분리 측면에서 좋은 개선입니다.


18-23: 생성자 시그니처 변경이 올바릅니다.

새로운 파라미터가 일관된 스타일로 추가되었습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/HistoryDept.java (3)

4-4: LGTM!

대학원 공지 클라이언트 import가 올바르게 추가되었습니다.


34-34: LGTM!

필드 할당이 올바른 위치에서 수행되고 있으며, 기존 필드 초기화 패턴과 일관성이 있습니다.


18-23: latestPageGraduateNoticeApiClient 사용 확인됨
DeptInfo 클래스의 scrapGraduateLatestPageHtmlscrapGraduateAllPageHtml에서 실제로 사용되고 있습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/BiologicalDept.java (2)

4-4: LGTM!

대학원 공지 API 클라이언트 import가 올바르게 추가되었습니다.


34-34: LatestPageGraduateNoticeApiClient 빈 등록 확인 스프링 컨텍스트에 LatestPageGraduateNoticeApiClient가 @Component/@service 또는 @bean으로 등록되어 있어야 BiologicalDept 빈 생성 시 DI 에러가 발생하지 않습니다. 등록 여부를 확인하세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ChemicalDivisionDept.java (2)

4-4: 생성자 시그니처 변경 확인 필요

ChemicalDivisionDept 생성자에 LatestPageGraduateNoticeApiClient 파라미터가 추가되었습니다. 모든 인스턴스 생성 지점이 새로운 시그니처로 업데이트되었는지 수동으로 검토해 주세요.


34-34: 필드 상속 구조 확인

latestPageGraduateNoticeApiClientsrc/main/java/com/kustacks/kuring/worker/scrap/deptinfo/DeptInfo.java:27에서 protected로 선언되어 있어, EngineeringCollege 및 하위 클래스에서 정상 상속·사용 가능합니다. 별도 조치 불필요합니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/BiologicalSciencesDept.java (3)

4-4: 변경사항 확인됨.

졸업 공지 API 클라이언트를 위한 import가 올바르게 추가되었습니다.


19-24: 의존성 주입 패턴이 올바르게 적용됨.

생성자에 LatestPageGraduateNoticeApiClient 파라미터가 적절하게 추가되었습니다.


28-36: 필드 선언 검증 완료: DeptInfo(부모 클래스)에 latestPageGraduateNoticeApiClient가 선언되어 있어 해당 할당이 유효합니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ComputerScienceDept.java (2)

18-23: 생성자 파라미터 추가가 올바르게 적용되었습니다.

LatestPageGraduateNoticeApiClient가 의존성 주입 패턴에 따라 적절히 추가되었습니다. 다른 학과 클래스들과 일관된 패턴을 따르고 있어 좋습니다.


34-34: latestPageGraduateNoticeApiClient 상속 필드 확인됨
필드가 조상 클래스 DeptInfo(src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/DeptInfo.java:27)에 protected LatestPageGraduateNoticeApiClient latestPageGraduateNoticeApiClient;로 선언되어 있으며, request()/requestAll()에서 사용됩니다. 서브클래트에서 해당 필드를 할당하는 방식은 적절합니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/sanghuo_biology/AnimalScienceTechnologyDept.java (3)

4-4: LGTM!

새로운 LatestPageGraduateNoticeApiClient import가 적절하게 추가되었습니다.


34-34: 필드 초기화 및 사용 검증 완료

latestPageGraduateNoticeApiClient가 부모 클래스인 DeptInfoscrapGraduateLatestPageHtmlscrapGraduateAllPageHtml 메서드에서 실제로 사용됩니다.


18-23: 생성자 파라미터 추가 및 필드 선언 확인
LatestPageGraduateNoticeApiClient 파라미터가 기존 패턴대로 추가되었고, DeptInfo 클래스에 해당 필드(latestPageGraduateNoticeApiClient)가 이미 선언되어 있어 문제 없습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/ChineseDept.java (2)

29-34: 필드 초기화 순서 일관성 확인됨

다른 학과 스크래퍼(IndustrialDesignDept, ArchitectureDept)와 동일한 순서로 초기화되고 있어 의도된 동작입니다.


18-23: LatestPageGraduateNoticeApiClient 의존성 추가가 올바르게 선언·할당되고 사용되는 것을 확인했습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MovingImageFilmDept.java (1)

4-4: 스크립트 실행 결과 MovingImageFilmDept 생성자 호출을 찾지 못했습니다. Spring Bean 등록(@bean)이나 팩토리 메서드 등에서 해당 클래스가 인스턴스화되는 부분을 수동으로 확인해주세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/IndustrialDept.java (1)

4-4: LGTM! 의존성 주입이 올바르게 구현되었습니다.

LatestPageGraduateNoticeApiClient가 생성자를 통해 주입되고 필드에 할당된 점 확인했습니다. 직접 new IndustrialDept(...) 호출 지점이 없는 관계로, 스프링 컨테이너를 통한 빈 등록 및 의존성 주입 설정을 수동으로 검증해주세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/CosmeticsDept.java (1)

4-4: 생성자 호출 반영 여부 확인
CosmeticsDept를 수동 인스턴스화하거나 Bean으로 등록한 모든 지점에서 새 매개변수가 전달되었는지 확인하세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ku_integrated_science/BioMedicalScienceDept.java (1)

4-4: LGTM! 의존성 주입이 올바르게 구현되었습니다.
BioMedicalScienceDept는 직접 생성하는 코드가 없으므로, 스프링 컨테이너에서 빈이 정상 등록되어 LatestPageGraduateNoticeApiClient가 주입되는지 확인해 주세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/PublicAdministrationDept.java (1)

4-4: LGTM! Spring 빈으로 자동 관리되므로 수동 new 호출은 없습니다.

LatestPageGraduateNoticeApiClient가 생성자를 통해 주입되어 필드에 올바르게 할당되었습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/science/MathematicsDept.java (1)

4-4: LGTM! 의존성 주입이 올바르게 구현되었습니다.

  • LatestPageGraduateNoticeApiClient가 생성자를 통해 주입되고 필드에 올바르게 할당되었습니다.
  • MathematicsDept@RegisterDepartmentMap으로 스프링 빈으로 관리되며 수동 new 호출이 없어도 스프링이 생성자를 통해 필요한 의존성을 자동으로 주입합니다.
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/IndustrialDesignDept.java (1)

4-4: IndustrialDesignDept 인스턴스화 로직 검증 필요
@RegisterDepartmentMap 처리 로직 또는 빈 등록 설정이 새 생성자 시그니처(4번째 LatestPageGraduateNoticeApiClient 파라미터 포함)로 IndustrialDesignDept를 올바르게 생성하는지 확인하세요.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/PhysicalEducationDept.java (1)

4-4: LGTM — 직접 인스턴스화 검증 불필요
PhysicalEducationDept는 @RegisterDepartmentMap으로 스캔되어 Spring 빈으로 등록되므로 new PhysicalEducationDept(...) 호출이 없으며, 의존성 주입은 올바르게 구현되었습니다.

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/LivingDesignDept.java (1)

4-4: LGTM! 생성자 시그니처 변경 확인 필요

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다. 이 변경은 여러 학과 스크래퍼 클래스에 걸친 체계적인 리팩토링의 일부입니다.

생성자 시그니처가 변경되었으므로 모든 호출 지점에서 새 매개변수를 전달하도록 업데이트되었는지 확인해 주세요.

Also applies to: 22-23, 35-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/CultureContentDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/CivilEnvironmentDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/MediaCommunicationDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/InternationalTradeDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/EnglishEducationDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/architecture/ArchitectureDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/GeologyDept.java (1)

4-4: LGTM!

LatestPageGraduateNoticeApiClient 의존성이 올바르게 추가되었습니다.

Also applies to: 21-23, 34-35

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/liberal_art/EnglishDept.java (1)

4-4: 변경 사항이 올바릅니다.

LatestPageGraduateNoticeApiClient 추가가 다른 학과들과 일관된 패턴을 따르고 있으며, 졸업 공지사항 스크래핑 기능을 위한 적절한 의존성 주입입니다.

Also applies to: 21-22, 34-34

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/social_science/PoliticalScienceDept.java (1)

4-4: 변경 사항이 올바릅니다.

LatestPageGraduateNoticeApiClient 추가가 PR 전반의 일관된 패턴을 따르고 있으며, 구현이 정확합니다.

Also applies to: 21-22, 34-34

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/education/EducationalTechnologyDept.java (1)

4-4: 변경 사항이 올바릅니다.

졸업 공지사항 API 클라이언트 통합이 다른 학과들과 동일한 패턴으로 구현되었습니다.

Also applies to: 21-22, 34-34

src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/art_design/MediaActingDept.java (1)

17-31: MediaActingDept 생성자에 LatestPageGraduateNoticeApiClient, LatestPageGraduateNoticeHtmlParser 파라미터 및 noticeGraduationInfo 초기화가 누락되었습니다. 미디어연기학과에 졸업 공지사항이 없는 것이 확실한지 확인하고, 필요 시 동일 로직을 추가해주세요.

src/main/java/com/kustacks/kuring/user/domain/User.java (2)

38-40: fcmToken 필드에 대한 public getter 추가 확인

fcmToken 필드에 @Getter(AccessLevel.PUBLIC) 어노테이션이 추가되었습니다. 이전에 이 필드에 대한 public getter가 없었다면 적절한 변경입니다.


186-188: LGTM! null-safe 패턴 구현

Boolean.TRUE.equals() 패턴을 사용하여 null 값을 안전하게 처리하고 있습니다. academicEventNotificationEnabled 필드가 null인 경우 false를 반환하는 안전한 구현입니다.

src/test/java/com/kustacks/kuring/user/domain/UserTest.java (1)

210-230: LGTM! 상태 전환 테스트가 완벽합니다

새로운 isAcademicEventNotificationEnabled() 메서드에 대한 테스트가 다음을 잘 검증하고 있습니다:

  • 기본값이 true인지 확인
  • false로 설정 후 상태 확인
  • true로 재설정 후 상태 확인
src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventUpdaterTest.java (2)

69-71: LGTM! 공휴일 필터링이 반영된 테스트 업데이트

테스트 기댓값이 19건의 공휴일을 제외한 184건으로 적절하게 업데이트되었습니다. 이는 AI 요약에서 언급된 공휴일 필터링 로직과 일치합니다.


89-89: LGTM! 이벤트 요약 정규화 반영

이벤트 요약에서 시간 정보가 제거된 것이 테스트에 반영되었습니다:

  • 변경 전: "폐강교과목 공지(1차)(9:00~)"
  • 변경 후: "폐강교과목 공지(1차)"

이는 AcademicEventSummaryNormalizer의 정규화 로직과 일관성 있는 변경입니다.

Also applies to: 131-131

src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifier.java (1)

37-72: LGTM! 알림 결정 로직이 명확합니다

OR 로직이 적절하게 구현되어 있습니다:

  1. OPAQUE 이벤트는 항상 알림 발송
  2. TRANSPARENT 이벤트는 액션 필요 키워드가 있을 때만 알림 발송

null 처리도 안전하게 되어 있어 런타임 오류 가능성이 없습니다.

src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventNotificationClassifierTest.java (1)

1-193: LGTM! 테스트 커버리지가 매우 우수합니다

AcademicEventNotificationClassifier에 대한 테스트가 다음을 잘 검증하고 있습니다:

기본 동작:

  • OPAQUE는 항상 알림 활성화
  • TRANSPARENT는 키워드 기반 결정

엣지 케이스:

  • null/빈 summary 처리
  • null Transparent 처리
  • 복합 키워드가 포함된 긴 summary

파라미터화 테스트:

  • 다양한 액션 키워드 검증
  • 복합 시나리오 검증

테스트 설계가 매우 체계적이고 유지보수하기 좋은 구조입니다.

src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizer.java (1)

16-33: 다음 쿼리를 실제 DB 환경에서 실행하여 각 카테고리별 이벤트 수 집계 결과를 공유해 주세요.

SELECT category, COUNT(*) 
FROM academic_event 
GROUP BY category;
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventUpdater.java (1)

27-28: 업데이트 주기 변경 확인 필요

학사일정 업데이트 주기가 매일 새벽 5시→매주 월요일 새벽 5시로 변경되었습니다. 데이터 신선도 영향 여부 확인:

  • 주 1회 업데이트가 충분한지
  • 사용자 공지 필요 여부
  • 긴급 변경 시 수동 처리 절차 준비 여부
src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverter.java (2)

23-24: 공휴일 필터링 정규식이 적절합니다.

[^\\r\\n]*공휴일[^\\r\\n]* 패턴은 줄바꿈 내에서 "공휴일" 키워드를 포함한 이벤트를 효과적으로 필터링합니다. 주석에 명시된 대로 ReDoS 공격을 방지하는 안전한 패턴입니다.


34-64: Optional 반환 타입으로 안전성이 향상되었습니다.

Optional<AcademicEvent> 반환 타입을 사용하여 변환 실패 케이스를 명시적으로 처리하는 것은 좋은 설계입니다. 공휴일 필터링과 예외 처리가 적절하게 구현되어 있습니다.

src/test/java/com/kustacks/kuring/acceptance/CategoryAcceptanceTest.java (2)

15-22: 명시적 static import로 개선되었습니다.

와일드카드 import 대신 명시적으로 사용하는 메서드만 import하는 것은 코드 가독성과 유지보수성을 높이는 좋은 관행입니다.


63-63: 학과 수 63개 업데이트 및 MEDIA_ACTING enum 정상 반영 확인 지원 학과가 63개로 증가했으며, DepartmentName enum과 스크랩 로직에 MEDIA_ACTING이 올바르게 추가되었습니다.

src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventDbSynchronizerTest.java (1)

6-6: 테스트 데이터가 enum 기반 카테고리로 올바르게 업데이트되었습니다.

문자열 "기본" 대신 AcademicEventCategory.ETC enum 값을 사용하도록 테스트 데이터를 업데이트한 것은 도메인 모델의 변경사항과 일치합니다.

Also applies to: 88-88, 153-153

src/main/java/com/kustacks/kuring/user/application/port/out/UserQueryPort.java (1)

15-15: 구독 정보를 포함한 전체 사용자 조회 메서드가 추가되었습니다.

새로운 findAllWithSubscriptions() 메서드는 모든 사용자와 구독 정보를 한 번에 조회합니다.

대용량 데이터 처리 고려사항: 사용자 수가 많은 경우 메모리 부담이 클 수 있습니다. 프로덕션 환경의 사용자 규모가 크다면 페이지네이션 또는 스트리밍 방식을 고려해보세요.

현재 프로덕션 환경의 사용자 수 규모가 이 방식에 적합한지 검토해주세요. AdminCommandService의 주석(line 122-123)에서 5000명을 예시로 들고 있는데, 실제 사용자 수가 이보다 훨씬 많다면 메모리 이슈가 발생할 수 있습니다.

src/main/java/com/kustacks/kuring/message/application/service/FirebaseSubscribeService.java (1)

26-26: 학사일정 알림을 위한 토픽 상수가 추가되었습니다.

ACADEMIC_EVENT_TOPIC 상수는 학사일정 알림 기능을 위한 토픽 이름을 정의합니다. 기존 ALL_DEVICE_SUBSCRIBED_TOPIC과 일관된 네이밍 패턴을 따릅니다.

src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventJdbcRepository.java (1)

38-38: 카테고리 enum을 이름으로 저장하도록 일관되게 업데이트되었습니다.

academicEvent.getCategory().name()을 사용하여 enum 값을 문자열로 저장하는 방식은 적절합니다. INSERT와 UPDATE 경로 모두에서 일관되게 적용되었으며, VARCHAR(30) 마이그레이션과도 정확히 일치합니다.

Also applies to: 71-71

src/main/resources/db/migration/V251008__Alter_academic_event_category_column_size.sql (1)

1-2: 스크립트가 결과를 반환하지 않았습니다. AcademicEventCategory enum 파일을 열어 모든 상수명이 VARCHAR(30)로 확장된 컬럼 크기(30자) 이내인지 직접 검증해주세요.

src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java (1)

32-32: 관리자 구독 재설정 성공 메시지가 적절하게 추가되었습니다.

새로운 응답 코드가 기존 Admin 섹션의 패턴을 따르고 있으며, 메시지도 명확합니다.

src/main/java/com/kustacks/kuring/calendar/application/port/out/AcademicEventQueryPort.java (1)

26-32: 오늘의 학사일정 조회 메서드가 잘 추가되었습니다.

메서드 시그니처와 Javadoc이 명확하며, 알림 가능 여부와 날짜 조건을 잘 설명하고 있습니다.

src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserQueryRepositoryImpl.java (1)

48-56: 구독 정보를 포함한 사용자 조회 메서드가 올바르게 구현되었습니다.

left join fetch를 사용하여 N+1 문제를 방지하고, distinct()로 중복을 제거하는 것이 적절합니다.

src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepository.java (1)

17-17: 리포지토리 인터페이스에 오늘의 일정 조회 메서드가 적절하게 추가되었습니다.

메서드 시그니처가 포트 인터페이스와 일치하며, 기존 패턴을 따릅니다.

src/main/java/com/kustacks/kuring/common/featureflag/KuringFeatures.java (1)

7-7: 학과 졸업 공지 업데이트를 위한 기능 플래그가 적절하게 추가되었습니다.

기존 기능 플래그들과 동일한 패턴을 따르고 있습니다.

src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventPersistenceAdapter.java (1)

70-73: 오늘의 일정 조회 메서드가 어댑터에 올바르게 구현되었습니다.

리포지토리로의 단순 위임 패턴이 기존 쿼리 메서드들과 일관성 있게 적용되었습니다.

src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java (1)

20-20: 메서드명이 더 명확하게 변경되었습니다.

resubscribeAllUsersToTopics()는 기존의 subscribeAllUserSameTopic()보다 의도를 더 잘 표현합니다.

src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserQueryRepository.java (1)

16-16: 사용자 구독 정보 조회 메서드가 인터페이스에 적절하게 추가되었습니다.

구현체와 일치하는 메서드 시그니처가 선언되었습니다.

src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentGraduationNoticeUpdater.java (2)

44-60: 기능 플래그 구현이 올바릅니다.

대학원 공지 업데이트를 기능 플래그로 보호하는 것은 좋은 접근입니다. 플래그가 비활성화되면 전체 업데이트 로직을 건너뛰므로 안전한 배포가 가능합니다.


117-117: 매개변수 이름 일관성 개선.

graduate에서 graduated로 변경하여 과거분사형으로 명확하게 표현했습니다. 불린 상태를 나타내는 데 더 적합합니다.

src/test/java/com/kustacks/kuring/calendar/domain/AcademicEventTest.java (1)

20-20: 테스트가 enum 기반 카테고리로 올바르게 업데이트되었습니다.

String에서 AcademicEventCategory enum으로 전환하여 타입 안정성이 향상되었습니다.

src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventCategorizerTest.java (1)

1-82: 카테고리 분류 테스트가 포괄적입니다.

파라미터화된 테스트를 사용하여 여러 키워드 그룹과 엣지 케이스(null, 빈 문자열, 알 수 없는 입력)를 효과적으로 검증하고 있습니다.

src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventConverterTest.java (2)

58-64: Optional 기반 API로 올바르게 전환되었습니다.

orElse(null)을 사용하여 Optional 결과를 언래핑하고 있으며, 이는 테스트 단언에 적합합니다.


82-115: 공휴일 필터링 테스트가 잘 작성되었습니다.

파라미터화된 테스트를 사용하여 다양한 공휴일 패턴이 변환에서 제외되는지 확인하고 있습니다.

src/test/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationServiceTest.java (1)

1-102: 알림 서비스 테스트가 잘 구성되었습니다.

Mock 객체를 사용하여 이벤트가 없을 때, 단일 이벤트, 여러 이벤트의 세 가지 주요 시나리오를 테스트하고 있습니다. Firebase 메시징 호출이 올바르게 검증되고 있습니다.

src/main/java/com/kustacks/kuring/auth/interceptor/UserRegisterNonChainingFilter.java (1)

63-81: 헬퍼 메서드 추출로 가독성이 향상되었습니다.

구독 로직을 subscribeDefaultTopics, subscribeTopic, makeSubscribeCommand 메서드로 분리하여 코드가 더 명확해졌습니다. 또한 이제 사용자 등록 시 학사 일정 토픽(ACADEMIC_EVENT_TOPIC)을 기본 구독에 포함합니다.

src/main/java/com/kustacks/kuring/user/application/service/UserQueryService.java (1)

111-111: 새 사용자에 대한 학사 일정 토픽 구독이 추가되었습니다.

기존 사용자가 없을 때 생성되는 새 사용자가 ACADEMIC_EVENT_TOPIC에 자동으로 구독되도록 하여 등록 플로우와 일관성을 유지합니다.

src/test/java/com/kustacks/kuring/support/DatabaseConfigurator.java (1)

298-313: 테스트 데이터가 enum 기반 카테고리로 올바르게 마이그레이션되었습니다.

AcademicEventCategory.valueOf()를 사용하여 문자열 데이터를 enum으로 변환하고 있으며, 모든 카테고리 값이 유효한 enum 상수(ACADEMIC_DEGREE, REGISTRATION_COURSE_GRADE, ACADEMIC_OPERATION_EVENT, ETC)입니다.

src/test/java/com/kustacks/kuring/acceptance/AdminStep.java (1)

122-139: LGTM!

새로운 관리자 재구독 API를 위한 테스트 헬퍼 메서드가 기존 패턴과 일관성 있게 잘 구현되었습니다.

src/main/java/com/kustacks/kuring/user/adapter/out/persistence/UserPersistenceAdapter.java (1)

65-68: LGTM!

토큰만 조회하던 기존 방식에서 구독 정보를 포함한 전체 사용자 엔티티를 조회하는 방식으로 변경되어, 관리자의 일괄 재구독 기능을 지원하기에 적합합니다.

src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java (2)

21-31: LGTM!

와일드카드 임포트에서 명시적 임포트로 변경하여 코드 가독성이 개선되었습니다.


369-380: LGTM!

새로운 관리자 재구독 API에 대한 인수 테스트가 기존 테스트 패턴과 일관성 있게 잘 작성되었습니다.

src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java (2)

378-388: LGTM!

학사일정 알림 설정에 따라 토픽을 구독하거나 구독 해제하는 로직이 명확하게 구현되었습니다.


393-396: LGTM!

신규 사용자 생성 시 ACADEMIC_EVENT_TOPIC을 자동 구독하는 로직이 추가되었습니다. User 도메인의 academicEventNotificationEnabled 필드가 기본값 true로 설정되어 있어 이 흐름과 일관성이 있습니다.

Based on learnings

src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java (1)

137-144: LGTM!

사용자 토픽 재구독 엔드포인트가 RESTful하게 잘 구현되었습니다. GET에서 POST로 변경하고, 명확한 API 문서화와 응답 타입을 제공합니다.

src/main/java/com/kustacks/kuring/calendar/adapter/out/persistence/AcademicEventQueryRepositoryImpl.java (2)

60-60: LGTM!

카테고리를 문자열 값으로 프로젝션하도록 변경되어 DTO 구조가 단순해졌습니다.


78-95: 쿼리 로직이 요구사항과 일치하는지 확인하세요.

현재 쿼리는 오늘 시작하거나 오늘 종료하는 일정을 조회합니다(OR 조건). 테스트 코드를 보면 이것이 의도된 동작으로 보이지만, 일반적인 "오늘의 일정" 조회는 오늘 날짜와 겹치는 모든 일정(오늘 진행 중인 일정 포함)을 의미할 수 있습니다.

현재 로직:

  • 오늘 시작하는 일정 ✓
  • 오늘 종료하는 일정 ✓
  • 어제 시작해서 내일 종료하는 일정(오늘 진행 중) ✗

비즈니스 요구사항이 "오늘 시작하거나 종료하는 일정"이 맞다면 현재 로직이 적절하지만, "오늘 진행 중인 모든 일정"이라면 로직을 다음과 같이 변경해야 합니다:

.where(
    academicEvent.notifyEnabled.isTrue(),
    academicEvent.startTime.loe(date.atTime(23, 59, 59, 999_999_999))
        .and(academicEvent.endTime.goe(date.atStartOfDay()))
)

테스트 코드(AcademicEventNotificationSchedulerTest)를 확인한 결과 현재 로직이 의도된 것으로 보이지만, 실제 요구사항과 일치하는지 확인해 주세요.

src/test/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationSchedulerTest.java (1)

1-180: LGTM!

학사일정 알림 스케줄러에 대한 통합 테스트가 다양한 시나리오를 잘 커버하고 있습니다:

  • Feature flag 검증
  • 일정 없음/있음
  • 오늘 시작/종료/진행중 일정
  • 여러 일정 처리

테스트 구조와 검증 로직이 명확합니다.

src/test/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizerTest.java (1)

1-94: LGTM!

학사일정 제목 정규화 로직에 대한 단위 테스트가 매개변수화된 테스트를 활용하여 다양한 케이스를 효율적으로 검증하고 있습니다. 테스트 케이스가 명확하고 포괄적입니다.

src/main/java/com/kustacks/kuring/calendar/domain/AcademicEventCategory.java (1)

1-19: LGTM!

학사일정 카테고리를 enum으로 정의한 구조가 깔끔합니다. 각 카테고리별 한글 표시명도 적절하게 설정되어 있습니다.

src/main/java/com/kustacks/kuring/worker/update/calendar/AcademicEventSummaryNormalizer.java (2)

59-71: 괄호 내용 필터링 로직 개선 가능

Line 60-70의 removeDateTimeBrackets 메서드에서 replaceAll과 람다를 사용한 조건부 치환이 잘 구현되어 있습니다. 차수 정보 (3차) 같은 비시간 정보는 보존하고 날짜/시간 정보만 제거하는 로직이 명확합니다.


10-27: 정규표현식 ReDoS 검증 완료
모든 패턴이 negated 캐릭터 클래스 및 제한된 수량자 사용으로 백트래킹 폭주 없이 안전합니다.

src/main/java/com/kustacks/kuring/calendar/domain/AcademicEvent.java (2)

39-41: 카테고리 필드의 enum 타입 변경 확인

String에서 AcademicEventCategory enum으로의 타입 변경이 적절하게 이루어졌습니다. @Enumerated(EnumType.STRING)@Column(length = 30) 설정도 올바릅니다. 가장 긴 enum 이름인 ACADEMIC_OPERATION_EVENT(25자)가 length 제약 내에 있습니다.


74-96: 정적 팩토리 메서드 시그니처 업데이트 확인

category 파라미터 타입이 String에서 AcademicEventCategory로 변경되고, 적절한 null 검증도 포함되어 있습니다. 빌더 패턴을 통한 객체 생성 로직도 일관성 있게 업데이트되었습니다.

src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java (1)

32-48: 알림 발송 로직 구현 확인

오늘의 학사일정을 조회하고 각 일정별로 FCM 알림을 발송하는 로직이 잘 구현되어 있습니다. 성공/실패 카운팅과 적절한 로깅도 포함되어 있습니다.

src/main/java/com/kustacks/kuring/worker/notification/AcademicEventNotificationScheduler.java (1)

19-31: 예외 스택 트레이스 로깅 누락

Line 28의 log.error() 호출에서 예외 객체가 포함되지 않아 디버깅 시 스택 트레이스를 확인할 수 없습니다.

다음과 같이 수정하여 예외 정보를 포함하세요:

             } catch (Exception e) {
-                log.error("학사일정 알림 발송 간 오류가 발생했습니다.", e);
+                log.error("학사일정 알림 발송 중 오류가 발생했습니다.", e);
             }

참고: 현재 코드도 e를 전달하고 있으나, 로그 메시지에 오타가 있습니다 ("발송 간" → "발송 중").

Likely an incorrect or invalid review comment.

Comment on lines +15 to +22
uses: actions/checkout@v3

# (2) JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

GitHub Actions 최신 런타임에 맞게 액션 버전 업데이트 필요
actions/checkout@v3actions/setup-java@v3는 Node 16 런타임에 의존하는 구버전이라 현재 GitHub 호스티드 러너에서 실행 즉시 실패합니다(actionlint에서도 동일하게 지적됨). 배포 파이프라인이 막히지 않도록 v4로 올려 주세요.

-      - name: Checkout repository
-        uses: actions/checkout@v3
+      - name: Checkout repository
+        uses: actions/checkout@v4
...
-      - name: Set up JDK 17
-        uses: actions/setup-java@v3
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
📝 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
uses: actions/checkout@v3
# (2) JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Checkout repository
uses: actions/checkout@v4
# (2) JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
🧰 Tools
🪛 actionlint (1.7.7)

15-15: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


19-19: the runner of "actions/setup-java@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In .github/workflows/dev.yml around lines 15 to 22, the workflow uses
actions/checkout@v3 and actions/setup-java@v3 which depend on an older Node 16
runtime and fail on current GitHub hosted runners; update both action references
to their v4 releases (e.g., actions/checkout@v4 and actions/setup-java@v4) so
they use the latest runtime compatibility, then commit the updated workflow
file.

Comment on lines +59 to +74
private boolean sendNotificationForEvent(AcademicEventReadModel event) {
try {
String title = createTitle(event);
String body = createBody(event);
sendNotificationMessage(title, body);

log.info("학사일정(ID = {}) 알림 전송에 성공했습니다.", event.id());
return true;
} catch (FirebaseMessagingException e) {
log.error("학사일정(ID = {}) FCM 전송에 실패했습니다.", event.id());
return false;
} catch (Exception e) {
log.error("학사일정(ID = {})을 FCM에 보내는 중 알 수 없는 오류가 발생했습니다.", event.id());
return false;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

예외 로깅 개선 필요

Lines 68, 71의 log.error() 호출에서 예외 객체가 인자로 전달되지 않아 스택 트레이스가 로그에 기록되지 않습니다. 디버깅 시 문제 원인 파악이 어려울 수 있습니다.

다음과 같이 수정하세요:

         } catch (FirebaseMessagingException e) {
-            log.error("학사일정(ID = {}) FCM 전송에 실패했습니다.", event.id());
+            log.error("학사일정(ID = {}) FCM 전송에 실패했습니다.", event.id(), e);
             return false;
         } catch (Exception e) {
-            log.error("학사일정(ID = {})을 FCM에 보내는 중 알 수 없는 오류가 발생했습니다.", event.id());
+            log.error("학사일정(ID = {})을 FCM에 보내는 중 알 수 없는 오류가 발생했습니다.", event.id(), 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 sendNotificationForEvent(AcademicEventReadModel event) {
try {
String title = createTitle(event);
String body = createBody(event);
sendNotificationMessage(title, body);
log.info("학사일정(ID = {}) 알림 전송에 성공했습니다.", event.id());
return true;
} catch (FirebaseMessagingException e) {
log.error("학사일정(ID = {}) FCM 전송에 실패했습니다.", event.id());
return false;
} catch (Exception e) {
log.error("학사일정(ID = {})을 FCM에 보내는 중 알 수 없는 오류가 발생했습니다.", event.id());
return false;
}
}
private boolean sendNotificationForEvent(AcademicEventReadModel event) {
try {
String title = createTitle(event);
String body = createBody(event);
sendNotificationMessage(title, body);
log.info("학사일정(ID = {}) 알림 전송에 성공했습니다.", event.id());
return true;
} catch (FirebaseMessagingException e) {
log.error("학사일정(ID = {}) FCM 전송에 실패했습니다.", event.id(), e);
return false;
} catch (Exception e) {
log.error("학사일정(ID = {})을 FCM에 보내는 중 알 수 없는 오류가 발생했습니다.", event.id(), e);
return false;
}
}
🤖 Prompt for AI Agents
In
src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java
around lines 59–74, the log.error calls in the FirebaseMessagingException and
generic Exception catch blocks do not include the caught exception, so the stack
trace is not recorded; update both catch blocks to pass the caught exception as
the last argument to log.error (e.g., keep the same formatted message and
event.id() parameter(s), then add the exception variable so the logger records
the stack trace).

Comment on lines 88 to 100
public void updateAcademicEventNotification(UserAcademicEventNotificationCommand command) {
User user = findUserByToken(command.userToken());
user.updateAcademicNotificationEnabled(command.enabled());
Boolean wasEnabled = user.getAcademicEventNotificationEnabled();

// 설정이 변경된 경우에만 토픽 구독/해제 처리
if (wasEnabled.equals(command.enabled())) {
editAcademicEventNotificationEnabled(
command.userToken(),
command.enabled()
);
user.updateAcademicNotificationEnabled(command.enabled());
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

조건문 로직 오류를 수정하세요.

Line 93의 조건이 반대입니다. 현재는 설정이 동일할 때 토픽 구독/해제를 수행하지만, 설정이 변경되었을 때 수행해야 합니다.

다음 diff를 적용하여 수정하세요:

-        if (wasEnabled.equals(command.enabled())) {
+        if (!wasEnabled.equals(command.enabled())) {
             editAcademicEventNotificationEnabled(
                     command.userToken(),
                     command.enabled()
📝 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 void updateAcademicEventNotification(UserAcademicEventNotificationCommand command) {
User user = findUserByToken(command.userToken());
user.updateAcademicNotificationEnabled(command.enabled());
Boolean wasEnabled = user.getAcademicEventNotificationEnabled();
// 설정이 변경된 경우에만 토픽 구독/해제 처리
if (wasEnabled.equals(command.enabled())) {
editAcademicEventNotificationEnabled(
command.userToken(),
command.enabled()
);
user.updateAcademicNotificationEnabled(command.enabled());
}
}
public void updateAcademicEventNotification(UserAcademicEventNotificationCommand command) {
User user = findUserByToken(command.userToken());
Boolean wasEnabled = user.getAcademicEventNotificationEnabled();
// 설정이 변경된 경우에만 토픽 구독/해제 처리
if (!wasEnabled.equals(command.enabled())) {
editAcademicEventNotificationEnabled(
command.userToken(),
command.enabled()
);
user.updateAcademicNotificationEnabled(command.enabled());
}
}
🤖 Prompt for AI Agents
In
src/main/java/com/kustacks/kuring/user/application/service/UserCommandService.java
around lines 88–100 the if condition is inverted: it currently runs the topic
subscribe/unsubscribe logic when the old setting equals the new setting, but it
should run only when the setting has changed; change the condition to check for
inequality (i.e., when !wasEnabled.equals(command.enabled()) or otherwise
compare for difference) so editAcademicEventNotificationEnabled(...) and
user.updateAcademicNotificationEnabled(...) are executed only when the enabled
value differs.

Comment on lines 18 to 23
public BiologicalDept(
LatestPageNoticeApiClient latestPageNoticeApiClient,
LatestPageNoticeHtmlParser latestPageNoticeHtmlParser,
LatestPageNoticeProperties latestPageNoticeProperties
LatestPageNoticeProperties latestPageNoticeProperties,
LatestPageGraduateNoticeApiClient latestPageGraduateNoticeApiClient
) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

생성자 파라미터가 올바르게 확장되었습니다.

새로운 latestPageGraduateNoticeApiClient 파라미터가 일관된 패턴으로 추가되었습니다. 다만 부모 클래스 EngineeringCollege에 해당 필드가 선언되어 있는지 확인이 필요합니다.

다음 스크립트로 부모 클래스의 필드 선언을 확인하세요:


🏁 Script executed:

#!/bin/bash
# 부모 클래스 EngineeringCollege에서 latestPageGraduateNoticeApiClient 필드 선언 확인
ast-grep --pattern $'class EngineeringCollege {
  $$$
  latestPageGraduateNoticeApiClient
  $$$
}'

Length of output: 0


아래 스크립트로 EngineeringCollege.java 파일 경로를 찾고, 해당 파일에서 LatestPageGraduateNoticeApiClient 필드 선언을 확인해 주세요.


🏁 Script executed:

#!/bin/bash
set -e
file=$(fd EngineeringCollege.java -t f | head -n1)
echo "Inspecting: $file"
grep -nE 'class EngineeringCollege\b' "$file"
grep -n 'LatestPageGraduateNoticeApiClient' "$file" || echo "No field declaration found"

Length of output: 183


EngineeringCollege에 누락된 필드 선언 및 초기화 로직 추가 필요
LatestPageGraduateNoticeApiClient latestPageGraduateNoticeApiClient 필드가 EngineeringCollege에 선언되어 있지 않아 컴파일 오류가 발생합니다. 아래를 반영하세요:

  • EngineeringCollege 클래스에
    private final LatestPageGraduateNoticeApiClient latestPageGraduateNoticeApiClient;
    선언
  • 생성자 파라미터로 전달된 latestPageGraduateNoticeApiClient를 해당 필드에 할당
    File: src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/EngineeringCollege.java
🤖 Prompt for AI Agents
In
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/EngineeringCollege.java
around lines 18 to 23, the class is missing the declaration and initialization
of LatestPageGraduateNoticeApiClient which causes a compile error; add a private
final LatestPageGraduateNoticeApiClient latestPageGraduateNoticeApiClient; field
to the class and assign the constructor parameter
latestPageGraduateNoticeApiClient to this field inside the constructor so the
passed dependency is stored for use.

this.noticeScrapInfo = new NoticeScrapInfo(ELEC_ELEC.getHostPrefix(), 407);
this.departmentName = ELEC_ELEC;
this.noticeGraduationInfo = new NoticeScrapInfo(ELEC_ELEC.getHostPrefix(), 767);
this.latestPageGraduateNoticeApiClient = latestPageGraduateNoticeApiClient;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

부모 클래스의 필드 선언을 확인하세요.

latestPageGraduateNoticeApiClient 필드에 값을 할당하고 있는데, 이 클래스에는 필드 선언이 보이지 않습니다. 부모 클래스 EngineeringCollege에 이 필드가 선언되어 있는지 확인이 필요합니다.

다음 스크립트로 부모 클래스의 필드 선언을 확인하세요:


🏁 Script executed:

#!/bin/bash
# 부모 클래스 EngineeringCollege에서 latestPageGraduateNoticeApiClient 필드 선언 확인

# EngineeringCollege 클래스 파일 찾기 및 필드 선언 확인
rg -n --type java -A 2 -B 2 'latestPageGraduateNoticeApiClient' --glob '**/EngineeringCollege.java'

# 또는 ast-grep으로 필드 선언 패턴 매칭
ast-grep --pattern 'class EngineeringCollege {
  $$$
  LatestPageGraduateNoticeApiClient $FIELD;
  $$$
}'

Length of output: 0


다음 스크립트를 실행하여 EngineeringCollege.java 파일 위치와 필드 선언 여부를 직접 확인하세요:


🏁 Script executed:

#!/bin/bash
# 1. EngineeringCollege.java 파일 경로 확인
fd --extension java 'EngineeringCollege.java'

# 2. 클래스 선언부 및 필드 목록 출력
for file in $(fd --extension java 'EngineeringCollege.java'); do
  echo "=== $file ==="
  sed -n '1,200p' "$file"
  echo
  rg -n 'PageGraduateNoticeApiClient' "$file"
done

Length of output: 462


latestPageGraduateNoticeApiClient 필드 선언 추가 필요
ElectricalElectronicsDept.java 34행에서 할당하는 latestPageGraduateNoticeApiClient 필드가 EngineeringCollegeDeptInfo 어디에도 선언되어 있지 않아 컴파일 오류가 발생합니다. 적절한 클래스에 필드 선언을 추가하세요.

🤖 Prompt for AI Agents
In
src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/engineering/ElectricalElectronicsDept.java
around line 34, the constructor assigns latestPageGraduateNoticeApiClient but
the field is not declared, causing a compile error; add a private final field
declaration of the correct type (matching the
injected/latestPageGraduateNoticeApiClient parameter type) to the class (or to
an appropriate superclass if intended), initialize it via the constructor
assignment, and ensure imports and visibility (private final) are consistent
with other similar fields in the class.

Comment on lines +83 to +97
@DisplayName("학과별(학사) 전체 공지 업데이트 테스트")
@Test
void updateAll_undergraduate_test() throws InterruptedException {
// given
doReturn(createDepartmentNoticesFixture()).when(scrapperTemplate).scrap(any(), any());
doNothing().when(firebaseService).sendNotifications(anyList());

// when
departmentNoticeUpdater.updateAll();
noticeUpdaterThreadTaskExecutor.getThreadPoolExecutor().awaitTermination(2, TimeUnit.SECONDS);

// then
Long count = noticeQueryPort.count();
assertThat(count).isEqualTo(3720);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

테스트 타임아웃 및 예상 카운트 검증이 필요합니다.

다음 사항들을 확인해주세요:

  1. 하드코딩된 타임아웃: 2초 타임아웃이 모든 환경에서 충분한지 확인이 필요합니다. CI/CD 환경이나 부하 상황에서 테스트가 불안정해질 수 있습니다.

  2. 예상 카운트 차이: update() 메서드는 3780개, updateAll() 메서드는 3720개를 예상합니다. 60개 차이(정확히 fixture 1개 분량)가 발생하는 이유가 명확하지 않습니다. 두 메서드의 동작 차이를 확인하고, 의도된 차이라면 주석으로 설명을 추가해주세요.

  3. @disabled 어노테이션: 테스트 클래스 전체가 비활성화되어 있어 이 테스트가 실행되지 않습니다. 활성화 계획이 있는지 확인해주세요.

다음 스크립트로 update()updateAll() 메서드의 동작 차이를 확인할 수 있습니다:


🏁 Script executed:

#!/bin/bash
# Description: update()와 updateAll() 메서드 구현 비교

# update() 메서드 구현 확인
echo "=== update() 메서드 구현 ==="
ast-grep --pattern $'class DepartmentNoticeUpdater {
  $$$
  update($$$) {
    $$$
  }
  $$$
}'

# updateAll() 메서드 구현 확인
echo -e "\n=== updateAll() 메서드 구현 ==="
ast-grep --pattern $'class DepartmentNoticeUpdater {
  $$$
  updateAll($$$) {
    $$$
  }
  $$$
}'

Length of output: 51


🏁 Script executed:

#!/bin/bash
# 1. DepartmentNoticeUpdater.java 파일 경로 찾기
echo "=== DepartmentNoticeUpdater.java 파일 경로 ==="
fd DepartmentNoticeUpdater.java

# 2. 클래스 선언 및 메서드 검색
FILE=$(fd DepartmentNoticeUpdater.java | head -n1)
echo -e "\n=== 클래스 및 메서드 내용 (${FILE}) ==="
rg -n 'class DepartmentNoticeUpdater' -C3 "$FILE"
rg -nP 'void\s+update\(' -C3 --type=java "$FILE"
rg -nP 'void\s+updateAll\(' -C3 --type=java "$FILE"

Length of output: 1077


🏁 Script executed:

#!/bin/bash
FILE=src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdater.java
echo "=== update() body ==="
rg -nP 'public void update\(\)' -A30 "$FILE"
echo -e "\n=== updateAll() body ==="
rg -nP 'public void updateAll\(\)' -A30 "$FILE"

Length of output: 3044


테스트 타임아웃 및 매직 넘버 리팩터링 필요

  • 2초 하드코딩된 awaitTermination(2, TimeUnit.SECONDS) → 상수로 추출하거나 Awaitility 등 안정적인 대기 방식으로 교체하세요.
  • 예상 카운트 3720REAL_ESTATE 부서를 제외한 결과입니다. 테스트에 해당 skip 로직 근거를 주석으로 명시해주세요.
  • 클래스 전체에 적용된 @Disabled 어노테이션의 비활성화 이유 및 활성화 계획을 문서화하거나 제거하세요.
🤖 Prompt for AI Agents
In
src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java
around lines 83 to 97, replace the hardcoded awaitTermination(2,
TimeUnit.SECONDS) by using a named constant (e.g. TEST_AWAIT_TIMEOUT_MS) or a
robust waiting utility such as Awaitility to avoid flaky timing; add an inline
comment above the assertThat(count).isEqualTo(3720) explaining that 3720
excludes the REAL_ESTATE department and reference the skip logic in the
production code; and update the class-level @Disabled usage by either removing
it if the test can be reliably enabled or adding a Javadoc comment describing
why the class is disabled and the plan/timeline to re-enable it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Deploy Production deploy production server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants