Conversation
#673) * main으로 병합 (#670) * feat(enroll): 수강 시작 시 AI 스케줄러 즉시 생성 배선 (AFTER_COMMIT @async) - EnrollmentStartedEvent 발행 → ScheduleGenerationListener가 커밋 후 Python /generate-for-member 호출 - ScheduleGenerationPort/AiAdapter, schedulerAiExecutor 전용 풀, schedule.ai.* 설정 * fix(enroll): schedulerAiExecutor 포화 시 CallerRunsPolicy→비차단 폐기+경고 로그 (CodeRabbit) 풀 포화 시 즉시 스케줄 생성이 수강 요청 스레드에서 동기 실행(최대 60초)되어 수강 API를 지연/차단하는 문제 해소. 폐기 건은 주간 배치(weekly_reflow)가 백업. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(community): 게시글 수정 시 신규 첨부 유지·기존 파일만 지정 삭제 (#666) * fix(community): 게시글 수정 시 신규 첨부 유지·기존 파일만 지정 삭제 PATCH /api/posts/{id} 로 이미지를 재첨부하면 응답은 200이지만 신규/기존 이미지가 모두 사라지는 버그 수정. 원인: PostCommandService.update() 가 신규 파일 업로드 후 deleteByPostId(postId) 로 post_id 전체 row 를 삭제. 신규 PostFile 은 기존 파일과 동일한 post_id 라 auto-flush 로 반영된 신규 row 까지 함께 삭제됨. 수정: - 신규 업로드 전에 기존 파일 스냅샷(oldFiles) 확보 - 삭제 단계에서 deleteByPostId 대신 deleteByIdIn(oldFiles id) 로 기존 파일만 지정 삭제, 신규 row 는 보존 - PostFileRepository 3계층에 deleteByIdIn(List<Long>) 추가 (파생 쿼리) - 회귀 테스트 2건 추가: 재첨부 시 신규 보존·기존만 삭제 / 미첨부 시 기존만 제거 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(community): 게시글 수정 중 업로드 실패 시 기존 파일 보존 회귀 테스트 추가 CodeRabbit 리뷰 반영 — 신규 업로드 도중 예외가 발생하면 기존 파일이 S3·DB 어디서도 삭제되지 않고, 이미 업로드된 신규 파일만 롤백 정리되는지 검증. 삭제 단계 순서가 다시 바뀌어 실패 요청에서 기존 파일을 잃는 회귀 방지. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(community): 게시글 수정 동시성 락 + 첨부 S3 삭제 커밋 후 분리 CodeRabbit 리뷰 반영 (2건). #1 동시 PATCH 파일 개수 race (비관적 락): - 같은 게시글에 PATCH가 동시에 들어오면 양쪽 다 oldFiles가 비어 2장씩 저장돼 2장 초과가 잔존할 수 있었음. - update()의 게시글 조회를 findByIdForUpdate(PESSIMISTIC_WRITE)로 변경해 같은 게시글 수정 요청을 직렬화. 뒤 요청은 앞 요청 커밋 후 최신 상태를 읽음. - PostRepository 포트·어댑터·Spring Data(@lock+@query, study 도메인과 동일 패턴)에 findByIdForUpdate 추가. #2 S3 삭제 트랜잭션 정합성 (AFTER_COMMIT 분리): - 트랜잭션 내 S3 삭제는 이후 커밋 실패 시 S3/DB가 어긋남. - 기존 파일 S3 삭제를 PostFilesDeletedEvent 발행 → PostFileCleanupListener가 AFTER_COMMIT에 처리하도록 분리(코드베이스 기존 이벤트 리스너 패턴과 일치). DB row 삭제는 트랜잭션 내 유지, S3는 커밋 후에만 삭제. 롤백 시 S3 미삭제. update()·delete() 두 경로 모두 적용. 테스트: - 기존 회귀 테스트를 이벤트 발행 검증으로 갱신(+ findByIdForUpdate 스텁). - PostFileCleanupListenerTest 추가: 전체 URL 삭제 / 일부 실패 시 예외 미전파·계속. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(community): 비관적 락 조회를 파생 쿼리로 전환 (semgrep QUERY_002 해소) 신규 @query가 semgrep QUERY_002(신규 @query 추가 금지) 게이트에 차단됨. findByIdForUpdate의 @query+@lock을 파생 쿼리 메서드 findWithLockById + @lock 조합으로 교체 — @query 없이 동일하게 SELECT ... FOR UPDATE를 건다. ("WithLock"은 find와 By 사이의 무시되는 서술어라 파생 파싱 대상은 Id 뿐.) 도메인 포트(findByIdForUpdate) 시그니처는 그대로 유지, 어댑터 위임만 변경. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(study_timer): 순공시간 집계에 세션 전체 누적 반영 (tail delta 버그) (#669) 종료 시 daily_study_stats·랭킹에 '마지막 heartbeat 이후 증가분(tail)'만 반영되어 세션당 ≈1초만 집계되던 버그 수정. endedSession의 전체 누적을 집계에 반영하도록 변경하고 tail delta 계산 로직을 제거한다. - EndStudyTimerSessionService: deltaStudySeconds(tail) → 전체 누적(accumulatedStudySeconds) - calculateDeltaStudySeconds 제거 - 회귀 테스트 추가(실측 버그 session 68 재현), 기존 테스트를 전체누적 기준으로 정정 Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: jongjunn <jongjunny2001@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(migration): experiment_exposure/experiment_shadow_decision 테이블 추가 Python-Server 스케줄러의 A/B 실험 관측 로그가 이 테이블 없이도 fail-soft로 500은 안 났지만(에러 1146/1054 스킵), 로그가 전혀 안 쌓여 실험 분석이 불가능했다. 정식 집계를 위해 Flyway 마이그레이션으로 생성. * fix: 마이그레이션 IF NOT EXISTS 제거 (CodeRabbit 리뷰 반영) 최근 마이그레이션 컨벤션(V3.4.x, V3.5.12/14/15)에 맞춰 신규 테이블 생성 시 IF NOT EXISTS 제거 — 스키마 드리프트를 조용히 가리지 않도록. --------- Co-authored-by: 유강현 <masan8259@gmail.com> Co-authored-by: dlxodus02 <13579lty0907@gmail.com> Co-authored-by: Yoonjongho1122 <k6k2dao112233@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Gate 1 · 결정론 규칙16 tests 16 ✅ 1s ⏱️ Results for commit 38dfb02. |
Gate 2 · 결정론 판정88 tests 82 ✅ 0s ⏱️ Results for commit 38dfb02. |
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit 38dfb02. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main/resources/db/migration/V3.6.1__create_experiment_tables.sql`:
- Line 27: Update the schedule_would_change column definition in the V3.6.1
experiment-table migration to enforce that only 0 or 1 values are accepted,
using a MySQL 8.0/InnoDB-compatible CHECK constraint while preserving its NOT
NULL DEFAULT 0 behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 67fd06a5-859f-4c04-a352-4b711465c478
📒 Files selected for processing (1)
src/main/resources/db/migration/V3.6.1__create_experiment_tables.sql
| variant VARCHAR(255) NOT NULL, | ||
| extension_delta INT NULL, | ||
| weekly_minutes_delta INT NULL, | ||
| schedule_would_change TINYINT(1) NOT NULL DEFAULT 0, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file excerpt =="
sed -n '1,80p' src/main/resources/db/migration/V3.6.1__create_experiment_tables.sql 2>/dev/null || true
echo
echo "== mysql dependency/version hints =="
rg -n "mysql|MariaDB|jdbc:mysql|flyway" -S --iglob '*.{gradle,gradle.kts,pom.xml,docker-compose*.yml,docker-compose*.yaml,sql,md}' . | head -200
echo
echo "== schedule_would_change usages =="
rg -n "schedule_would_change" -S . | head -200Repository: Hard-Click/Hard-Click-BackEnd
Length of output: 3043
schedule_would_change에 0/1 CHECK 제약을 추가하세요.
TINYINT(1)은 불리언 타입과 같지 않아 2, -1 같은 값도 저장 가능합니다. shadow 결과 집계 컬럼이므로 잘못된 값 유입이 데이터 정합성을 훼손할 수 있고, 컬럼은 아직 생성 중인 migration 단계라 제약 추가 비용이 낮습니다. MySQL 8.0 이상/InnoDB 환경에서는 CHECK 제약으로 제어를 붙이세요.
권장 수정
schedule_would_change TINYINT(1) NOT NULL DEFAULT 0,
+ CONSTRAINT chk_shadow_schedule_would_change
+ CHECK (schedule_would_change IN (0, 1)),
detail JSON NOT NULL,📝 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.
| schedule_would_change TINYINT(1) NOT NULL DEFAULT 0, | |
| schedule_would_change TINYINT(1) NOT NULL DEFAULT 0, | |
| CONSTRAINT chk_shadow_schedule_would_change | |
| CHECK (schedule_would_change IN (0, 1)), | |
| detail JSON NOT NULL, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/db/migration/V3.6.1__create_experiment_tables.sql` at line
27, Update the schedule_would_change column definition in the V3.6.1
experiment-table migration to enforce that only 0 or 1 values are accepted,
using a MySQL 8.0/InnoDB-compatible CHECK constraint while preserving its NOT
NULL DEFAULT 0 behavior.
…가 (#673)
main으로 병합 (main으로 병합 #670)
feat(enroll): 수강 시작 시 AI 스케줄러 즉시 생성 배선 (AFTER_COMMIT @async)
풀 포화 시 즉시 스케줄 생성이 수강 요청 스레드에서 동기 실행(최대 60초)되어
수강 API를 지연/차단하는 문제 해소. 폐기 건은 주간 배치(weekly_reflow)가 백업.
fix(community): 게시글 수정 시 신규 첨부 유지·기존 파일만 지정 삭제 (fix(community): 게시글 수정 시 신규 첨부 유지·기존 파일만 지정 삭제 #666)
fix(community): 게시글 수정 시 신규 첨부 유지·기존 파일만 지정 삭제
PATCH /api/posts/{id} 로 이미지를 재첨부하면 응답은 200이지만 신규/기존 이미지가 모두 사라지는 버그 수정.
원인: PostCommandService.update() 가 신규 파일 업로드 후
deleteByPostId(postId) 로 post_id 전체 row 를 삭제. 신규 PostFile 은 기존 파일과 동일한 post_id 라 auto-flush 로 반영된 신규 row 까지 함께 삭제됨.
수정:
CodeRabbit 리뷰 반영 — 신규 업로드 도중 예외가 발생하면 기존 파일이
S3·DB 어디서도 삭제되지 않고, 이미 업로드된 신규 파일만 롤백 정리되는지
검증. 삭제 단계 순서가 다시 바뀌어 실패 요청에서 기존 파일을 잃는 회귀 방지.
CodeRabbit 리뷰 반영 (2건).
#1 동시 PATCH 파일 개수 race (비관적 락):
#2 S3 삭제 트랜잭션 정합성 (AFTER_COMMIT 분리):
테스트:
신규 @query가 semgrep QUERY_002(신규 @query 추가 금지) 게이트에 차단됨. findByIdForUpdate의 @query+@lock을 파생 쿼리 메서드 findWithLockById + @lock 조합으로 교체 — @query 없이 동일하게 SELECT ... FOR UPDATE를 건다. ("WithLock"은 find와 By 사이의 무시되는 서술어라 파생 파싱 대상은 Id 뿐.) 도메인 포트(findByIdForUpdate) 시그니처는 그대로 유지, 어댑터 위임만 변경.
종료 시 daily_study_stats·랭킹에 '마지막 heartbeat 이후 증가분(tail)'만 반영되어 세션당 ≈1초만 집계되던 버그 수정. endedSession의 전체 누적을
집계에 반영하도록 변경하고 tail delta 계산 로직을 제거한다.
Python-Server 스케줄러의 A/B 실험 관측 로그가 이 테이블 없이도 fail-soft로 500은 안 났지만(에러 1146/1054 스킵), 로그가 전혀 안 쌓여 실험 분석이
불가능했다. 정식 집계를 위해 Flyway 마이그레이션으로 생성.
최근 마이그레이션 컨벤션(V3.4.x, V3.5.12/14/15)에 맞춰 신규 테이블
생성 시 IF NOT EXISTS 제거 — 스키마 드리프트를 조용히 가리지 않도록.
📌 연관 이슈
📝 작업 내용
🖥️ 프론트엔드 연동 가이드 (API 명세)
1. 주요 엔드포인트
[GET/POST/PUT/DELETE]/api/...: (API 설명)2. 요청 파라미터 (Request)
3. 정상 응답 예시 (200 OK)
응답 JSON 보기 (클릭)
{ // 정상 응답 JSON 복사 붙여넣기 }4.⚠️ 프론트엔드 참고 및 주의사항
type=COURSE일 때courseId누락 시 HTTP 400 발생)isRead필드는 아직 구현 전이라 항상false를 반환합니다)🚨 주요 에러 코드 및 예외 (Exceptions)
에러 코드 (예: N004): (발생 조건 및 설명)에러 코드 (예: C003): (발생 조건 및 설명)💡 백엔드 리뷰 포인트 (Backend Review)
✅ 체크리스트
Summary by CodeRabbit