[Release] 4.0.0 변경사항 배포 - #223
Merged
Merged
Conversation
- .claude/settings.local.json: 머신별 권한 설정이므로 공유 제외
- CLAUDE.md: 운영 제약·빌드 명령·구조·작업 파이프라인 문서화 - .claude/agents/: logic-reviewer, api-contract-checker, code-tracer 등 전문 에이전트 정의 - .claude/commands/: feat, check, plan, test 등 슬래시 커맨드 정의 - .claude/hooks/db_safety_check.py: DROP·TRUNCATE·WHERE 없는 DELETE 차단 훅
- build.config.toml, review.config.toml: 빌드·리뷰 모드 설정 - skills/: feat, plan, check, test, perf 등 Codex 스킬 정의 - scripts/codex-notify-macos.sh: macOS 알림 스크립트
- 스키마 초기화 명령(flyway의 clean 서브커맨드) 감지 추가 - JPA ddl-auto 위험 모드(create/create-drop) 감지 추가 - 볼륨 삭제 플래그(-v) 포함한 compose down 차단 - 프로덕션 DB 직접 접속 명령 차단 - WHERE 절 없는 UPDATE 감지 추가
- 사용자 영향, API 호환성, DB migration 체크리스트 추가 - 인증/권한, 검증 결과, 배포 리스크, 롤백 방법 섹션 추가
- CLAUDE.md: 자유 작업 범위를 읽기·로컬 검증으로 한정, git 작업은 명시 요청 시에만 수행 - CLAUDE.md: 인프라·외부 발송·되돌리기 어려운 작업 사전 확인 절차 명확화 - .gitignore: /AGENTS.md 항목 제거 - .codex/USAGE.md: migrate, pr 스킬 추가 및 문서화 - .codex/skills/check/SKILL.md: description 영문화, 불필요 공백 제거
- .claude/hooks/__pycache__/: 바이트코드 캐시는 로컬 생성물이므로 제외
- AGENTS.md: AI 에이전트 작업 시 따를 공통 규칙 정의 - 사용자·서비스 맥락, 산출물 기준 등 포함
- .claude/commands/pr.md: 기준 커밋~현재 변경사항으로 PR 초안 작성 커맨드 - .codex/skills/pr/: Codex pr 스킬 정의 - .codex/skills/migrate/: Flyway 마이그레이션 파일 작성 및 안전성 검토 스킬
- .codex/scripts/agent-config-lint.sh: 에이전트 설정 파일 유효성 검사 스크립트 - .github/workflows/agent-config-lint.yml: PR 시 에이전트 설정 변경 자동 검사 워크플로
[Chore] 에이전트 작업 환경 정비 - Claude Code/Codex 설정 추가 DB 안전성 훅 및 lint CI 추가
[Chore] 에이전트 PR/이슈 명령 자동화 개선
- 허용 이모지 키 목록과 검증기, 에러 케이스 정의 - 복습노트 소감 컬럼·학습 캘린더 감정 테이블 추가 마이그레이션(V7) 추가
- 학습 기록이 있는 날짜에 한해 감정 이모지(mood) 등록/수정 PATCH API 추가 - 캘린더 응답에 moodEmojiKey 포함, 컨트롤러/서비스 테스트 보강
- 복습 완료 요청에 moodEmojiKey 옵션 추가, 최신 복습노트에 저장 및 응답 노출 - 허용되지 않은 이모지 키 검증 테스트 추가
- 피드/공유문제 반응의 고정 이모지 화이트리스트를 공통 CustomEmojiValidator로 대체 - 반응 emoji 컬럼 길이를 80자로 확장하는 마이그레이션(V9) 추가
- ATTENDANCE 등 신규 측정 지표와 챌린지 기간(DAILY/WEEKLY/MONTHLY) 컬럼 추가(V8) - 기존 WEEKLY_PROBLEM_COUNT 등은 역직렬화 호환을 위해 @deprecated 유지, 챌린지 생성 권한을 방장에서 멤버로 완화 - 방 목록/상세에 멤버별·전체 오늘 복습 횟수·인원 통계 노출, 멤버 수 일괄 조회 쿼리 추가 - 통합 테스트의 반응 이모지 표기를 커스텀 이모지 키로 갱신
- @PathVariable/@RequestParam에 name 속성을 명시해 파라미터 이름 추론 실패 위험 제거
- 방장이 멀티파트 이미지를 업로드해 스터디룸 썸네일을 변경하는 PATCH 엔드포인트 추가 - S3 업로드 후 기존 썸네일은 비동기 삭제 큐로 정리, 응답으로 새 썸네일 URL 노출 - 정상 업로드/교체 및 비방장 접근 차단 검증 통합 테스트 추가
- 공유 문제 삭제 전 해당 문제에 달린 반응을 함께 삭제하는 리포지토리 메서드 추가 - 외래키 제약 위반 없이 공유 문제를 삭제할 수 있도록 서비스 로직 보강
- DAILY/WEEKLY/MONTHLY 주기 챌린지의 진행 상황을 전체 기간이 아닌 현재 구간 기준으로 집계하도록 변경 - 주기형 챌린지는 그룹·개인 목표 달성만으로 조기 완료 처리되지 않고 종료 시점까지 진행 상태 유지 - 집계 구간 산출 로직 검증 단위 테스트 추가
- V14: study_room_feed 테이블에서 event_type='SESSION_STARTED' 행 전체 삭제 - 공부중 세션 기능 폐기 후 enum에서 SESSION_STARTED 제거로 발생하는 500 오류 해소
- isAllowedImageType, hasAllowedImageSignature에 image/jpg 케이스 추가 - 모바일(Flutter)에서 비표준 MIME 타입 image/jpg로 전송 시 400 오류 해소
image_cropper 등 모바일 라이브러리가 MIME 타입을 application/octet-stream으로 전송하는 경우 extension 기반으로 실제 이미지 타입을 추론하도록 수정. magic bytes 검증은 그대로 유지하여 보안 수준은 동일하게 유지됨. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[Fix] 스터디룸 QA 수정 - StudySession 제거 및 썸네일 업로드 개선
- 4xx 상태코드의 ApplicationException은 Sentry 전송에서 제외 (유효하지 않은 토큰, 파일/문제 없음, 태그 길이 초과 등 사용자 오류는 제외) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- analyzeProblemSync 트랜잭션을 데이터 조회/저장으로 분리 (OpenAI 호출 중 DB 커넥션 장기 점유 방지 → 동시 업데이트 충돌 감소) - RabbitMQ 분석 consumer 동시성 1-3 → 1-2 감소 (Deadlock 발생 가능성 추가 완화) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- phone-duo gap 12px → 24px (노트북 기준 스크린샷 간격 개선) - 스터디룸 피처 섹션 추가: 스터디룸 개설 · 챌린지 · 학습 피드 · 주간 리포트 설명 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HibernatePropertiesCustomizer로 batch_size=50, order_inserts/updates=true 적용 - saveAll 호출 시 INSERT/UPDATE 묶음 처리로 DB 라운드트립 감소 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StudyRoomChallengeService.checkAndNotifyChallengeCompletionForUser 추가 (사용자가 속한 룸의 IN_PROGRESS 챌린지를 일괄 조회 후 완료 여부 즉시 확인) - StudyRoomFeedService.handleActivity에서 활동 발생 시 챌린지 완료 체크 호출 (문제 등록·복습 완료·문제 공유 시점에 즉시 알림 발송 가능) - REQUIRES_NEW 독립 트랜잭션으로 피드 생성 트랜잭션과 격리 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ProblemAnalysisDbService 신규 빈으로 fetchAnalysisPreparation, saveAnalysisSuccess 분리 → analyzeProblemSync에서 직접 self-call 대신 별도 빈 호출로 AOP 프록시 정상 적용 - analyzeProblemSync에 @transactional(NOT_SUPPORTED) 추가 → OpenAI 호출 중 DB 커넥션 미점유 설계 의도 실제 동작 - saveAnalysisSuccess에 COMPLETED 상태 중복 체크 추가 → 동시 처리 시 이미 완료된 분석 덮어쓰기 방지 - PROBLEM_ANALYSIS_NOT_FOUND 예외를 Consumer와 Service 양쪽에서 graceful skip 처리 → 문제 삭제 후 Phase 3 실행 시 불필요한 DLQ 처리 방지 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- V15 마이그레이션: study_room_challenge 테이블에 completed_at 컬럼 추가 - StudyRoomChallengeRepository에 tryTransitionFromInProgress 추가 → UPDATE WHERE status = 'IN_PROGRESS' 원자적 실행으로 단 하나의 스레드만 1 반환 - StudyRoomChallengeService.toResponse에서 COMPLETED 전이 시 원자적 쿼리 사용 → 동시 트랜잭션이 같은 챌린지를 동시에 완료 처리해도 FCM은 1회만 발송 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/fileUpload/presigned-urls?count=N&contentType=image/jpeg - 앱이 백엔드 경유 없이 S3에 직접 PUT 업로드 가능 - presignedUrl(10분 유효) + fileUrl(업로드 후 백엔드에 전달할 최종 URL) 반환 - count 최대 20 제한 - 기존 multipart 업로드 엔드포인트는 그대로 유지 (구 버전 앱 호환) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PATCH /api/study-room/{roomId}/thumbnail-url
OnO S3 URL 검증 후 썸네일 교체, 기존 이미지 비동기 삭제
- POST /api/problems/{problemId}/imageData/urls
기존 이미지 유지하며 새 이미지 URL 추가, AI 분석 재트리거
- POST /api/problem-solves/{problemSolveId}/image-urls
복습 기록에 이미지 URL 추가 (순서 이어받기)
- FileUploadService.validateS3Url(): OnO 버킷 URL 검증 공통 헬퍼 추가
- 기존 multipart API 전부 유지 (구버전 앱 호환)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ChallengeMemberProgressResponse에 profileImageUrl 필드 추가 - WeeklyReportResponse에 topMemberProfileImageUrl, longestStreakProfileImageUrl 추가 V16 마이그레이션으로 study_room_weekly_report 테이블에 컬럼 추가 리포트 생성 시 User.getProfileImageUrl() 값 저장 - PATCH /api/users/me/profile-image-url 추가 Presigned URL로 S3에 직접 업로드 후 URL만 전달하는 방식 지원 기존 multipart API 유지 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserService에 touchAndFindUser() 메서드 추가 (단일 트랜잭션) - UserController에서 두 번 호출하던 방식 제거
- DiscordWebhookQueue / DLQ / 바인딩 Bean 추가 (TTL 5분) - DiscordWebhookProducer, DiscordWebhookConsumer 구현 - DiscordWebhookNotificationService의 RestTemplate 직접 호출 제거
- DLQ 도달(최대 재시도 소진) 시 ProblemAnalysisFailureService.markFailed() 호출 - PROCESSING 상태 고착 방지
- FolderThumbnailResponseDto에 problemCount 추가 - FolderRepository에 countProblemsByFolderIds() 벌크 쿼리 추가 - FolderService에 toFolderThumbnailDtos(), findProblemCountsByFolderIds() 헬퍼 추가 - 관련 Repository / Service / 통합 테스트 보완
- StudyRoomMemberSummary record 추가 (userId, profileImageUrl, practicedToday) - StudyRoomListResponse에 members 필드 추가 - 멤버카운트 별도 쿼리 제거, 멤버 목록 단건 조회로 통합
- [Fix] FCM RabbitMQ enqueue를 TransactionSynchronization.afterCommit으로 이동 — REQUIRES_NEW 롤백 시 중복 발송 방지 - [Fix] AddProblemImageUrlsRequest.imageDataList, ImageUrlsRequest.imageUrls에 @NotNull 추가 및 컨트롤러 @validated 적용 — null 전송 시 NPE → 500 방지 - [Perf] FolderService.findAllUserFolders 2N+1 쿼리 → 배치 쿼리 전환 (findProblemIdsByFolderIds 신규 추가) - [Perf] StudyRoomChallengeService.checkAndNotifyChallengeCompletionForUser에 stats 캐시 도입 — 동일 range 챌린지의 중복 DB 쿼리 제거 - [Feat] Presigned URL 발급 엔드포인트에 일일 200회 Rate Limit 적용 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[Feat] 3.6.1 — S3 Presigned URL, 스터디룸 챌린지 알림, 폴더·챌린지 개선 및 다수 버그 수정
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 작업 내용
DAILY/WEEKLY/MONTHLY기간 기준 집계를 지원합니다.periodDays기반 임의 주기 챌린지를 지원합니다.CustomEmojiValidator와 에러 케이스를 추가했습니다.image/jpg,application/octet-stream등 업로드 MIME 허용 범위를 보강했습니다.problemCount를 추가했습니다.PROCESSING상태 고착을 방지했습니다.getUserInfo의 사용자 조회와 last active 갱신 트랜잭션을 통합했습니다.ApplicationException노이즈 필터링을 추가했습니다.refresh_token컬럼 인덱스를 추가했습니다.👤 사용자 영향
🔌 API 호환성
요청/응답 DTO 변경 없음
기존 Flutter 앱 버전과 호환 확인
breaking change 있음
신규 API가 다수 추가되었습니다.
/api/study-room,/api/study-rooms/api/study-room/{roomId}/feed/api/study-room/{roomId}/challenges/api/study-room/{roomId}/shared-problems,/api/study-rooms/{roomId}/shared-problems/api/study-room/{roomId}/weekly-reports/api/fileUpload/presigned-urls/api/learning-calendar/mood/api/users/me/profile-image,/api/users/me/profile-image-url/api/problems/{problemId}/imageData/urls/api/problem-solves/{problemSolveId}/image-urls기존 API 응답에 필드가 추가되었습니다.
profileImageUrlmoodEmojiKeylastSessionMoodEmojiKeyproblemCount기존 계약에 대한 주의사항
409 ALREADY_SHARED_PROBLEM으로 응답합니다.🗄️ DB migration
없음
있음: migration 파일과 기존 데이터 호환성 확인
추가된 migration
V6__add_study_room_schema.sqlV7__add_custom_emoji_fields.sqlV8__add_study_room_challenge_period.sqlV9__widen_study_room_reaction_emoji.sqlV10__add_shared_problem_comment.sqlV11__add_comment_reactions_and_user_profile_image.sqlV12__add_challenge_period_days.sqlV13__drop_study_session.sqlV14__delete_session_started_feeds.sqlV15__add_challenge_completed_at.sqlV16__add_weekly_report_profile_image_urls.sqlV17__add_refresh_token_index.sql주요 schema 영향
practice_note.last_session_mood_emoji_key,user.profile_image_url,study_room_challenge.period,study_room_challenge.period_days,study_room_challenge.completed_at, 주간 리포트 프로필 이미지 컬럼이 추가됩니다.VARCHAR(80)으로 확장됩니다.study_session테이블은 제거되고, 기존SESSION_STARTED피드는 삭제됩니다.refresh_token.refresh_token(255)인덱스가 추가됩니다.배포 전 확인 필요
study_session테이블 또는SESSION_STARTED피드가 의미 있는 데이터로 사용 중인지 확인해야 합니다.refresh_token인덱스 생성 시간이 운영 DB 크기에서 허용 가능한지 확인해야 합니다.🔐 인증/권한
영향 없음
사용자별 데이터 소유권 검증 확인
관리자/특수 권한 영향 확인
스터디룸 API는 멤버 여부, 방장 권한, 작성자 권한을 기준으로 접근을 제한합니다.
스터디룸 이름/썸네일 수정, 삭제, 멤버 강퇴 등은 방장 권한 검증이 필요합니다.
공유 문제 댓글 수정/삭제, 공유 문제 삭제, 반응 등록은 멤버십과 작성자/방장 정책을 기준으로 동작합니다.
사용자 프로필 이미지 변경/삭제는 본인 계정 기준으로 처리됩니다.
✅ 검증 결과
git log --oneline --no-merges b642e7c6..develop로 기준 커밋 이후 커밋 범위를 확인했습니다.git diff --stat b642e7c6..develop기준158 files changed, 8959 insertions(+), 178 deletions(-)변경을 확인했습니다.git diff --name-status b642e7c6..develop로 변경 파일 범위를 확인했습니다../gradlew testproblemCount관련 테스트🚀 배포 리스크
V13__drop_study_session.sql,V14__delete_session_started_feeds.sql는 데이터 삭제 성격이 있으므로 운영 데이터 사용 여부를 반드시 확인해야 합니다.refresh_token인덱스 생성은 DB 크기에 따라 배포 중 lock 또는 지연 가능성이 있습니다.↩️ 롤백/대응 방법
study_session테이블 및SESSION_STARTED피드 삭제 전 데이터 사용 여부를 확인합니다.스크린샷 (선택)