Skip to content

[FEAT] MIDI 이벤트 저장 API 구현 - #72

Merged
on1yoneprivate merged 17 commits into
developfrom
feat/#20-midi-event-save
Jul 31, 2026
Merged

[FEAT] MIDI 이벤트 저장 API 구현#72
on1yoneprivate merged 17 commits into
developfrom
feat/#20-midi-event-save

Conversation

@on1yoneprivate

@on1yoneprivate on1yoneprivate commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📍 개요

연주 종료 후 MIDI 이벤트를 저장하는 API를 구현

⛓️‍💥 관련 이슈


🛠️ 작업 내용

  • MIDI 이벤트 저장 API 추가
  • MIDI 이벤트 저장 서비스 로직 구현
  • 연주 소유자 검증 로직 추가
  • MIDI 이벤트 관련 예외 코드 분리 및 적용
    • 기존 PlayingErrorStatus에서 공동으로 관리하던 코드를 MidiEventErrorStatus로 분리
  • 요청 DTO 유효성 검증(Validation)
  • Service / Controller 테스트 코드 작성

🔥 리뷰 요청 사항

리뷰어가 중점적으로 확인해주었으면 하는 내용을 작성해주세요.

  • MIDI 이벤트 저장 및 연주 완료 처리 흐름이 적절한지
  • 예외 코드 분리 및 권한 검증(validateOwner) 방식이 적절한지

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 코드 및 import를 제거했습니다.
  • 예외 처리를 적용했습니다.
  • 테스트를 완료했습니다.
  • 관련 Issue를 연결했습니다.

📎 참고 사항

  • MVP에서는 사용자가 [분석하기] 버튼을 클릭할 때, 연주 MIDI 데이터를 한 번에 저장합니다.
    • 분석 담당자(@ownue)분께서는 해당 저장 시점이 적절한지 함께 검토 부탁드립니다.
  • MIDI 이벤트 저장 시 연주 상태를 COMPLETED로 변경합니다.
  • 10분을 초과하는 MIDI 이벤트는 저장 대상에서 제외됩니다.

Summary by CodeRabbit

  • 새로운 기능
    • 연주 세션에 MIDI 이벤트를 저장하는 API(POST /api/playings/{playingId}/midi-events)를 추가했습니다.
    • 요청의 이벤트 목록/각 값에 대해 유효성 검증을 적용하고, 저장 결과로 세션 ID와 저장된 이벤트 수를 제공합니다.
    • 연주 완료 요청은 1분 1회로 제한합니다.
  • 오류 처리 개선
    • 연주 진행 여부, 소유권, 세션 존재 및 MIDI 값/순서 관련 오류를 더 일관된 코드와 메시지로 안내합니다.
  • 테스트
    • 저장 API의 성공·실패 시나리오 및 입력 검증 케이스를 포괄적으로 추가/갱신했습니다.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@on1yoneprivate, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c651b766-37a5-401e-913d-8683b385771a

📥 Commits

Reviewing files that changed from the base of the PR and between e94371c and f22c795.

📒 Files selected for processing (1)
  • src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java
📝 Walkthrough

Walkthrough

인증된 사용자가 연주 세션에 MIDI 이벤트를 일괄 저장하는 POST API가 추가되었습니다. 요청 검증, MIDI 정규화·저장, 소유자·상태·빈도 검증, 오류 코드 및 관련 테스트가 함께 변경되었습니다.

Changes

MIDI 이벤트 저장 기능

Layer / File(s) Summary
요청·응답 및 오류 계약
src/main/java/com/mr/domain/playing/dto/..., src/main/java/com/mr/domain/playing/constant/..., src/main/java/com/mr/domain/playing/exception/...
MIDI 이벤트 필드 검증, 최대 100,000개 제한, 저장 결과 DTO와 MIDI·연주 오류 상태가 정의되었습니다.
MIDI 도메인 검증 및 정규화
src/main/java/com/mr/domain/playing/entity/..., src/test/java/com/mr/domain/playing/entity/PlayingTest.java
MIDI 이벤트가 허용 시간 범위로 필터링·정렬되고, null·음수·중복·개수·상태 조건을 검증한 뒤 연주 완료 정보와 함께 저장됩니다.
서비스 조회·저장 흐름
src/main/java/com/mr/domain/playing/repository/PlayingRepository.java, src/main/java/com/mr/domain/playing/service/PlayingService.java, src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java
삭제되지 않은 연주 조회, 소유자 확인, 최근 1분 내 완료 저장 여부 확인, MIDI 변환·저장 및 응답 반환 흐름이 구현되었습니다.
API 연결 및 요청 검증 테스트
src/main/java/com/mr/domain/playing/controller/PlayingController.java, src/test/java/com/mr/domain/playing/controller/PlayingControllerTest.java, src/test/java/com/mr/domain/playing/dto/req/MidiEventSaveRequestTest.java
POST 엔드포인트가 인증 사용자와 검증된 요청을 서비스에 전달하며, 성공 응답과 다양한 입력 오류가 검증됩니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PlayingController
  participant PlayingService
  participant PlayingRepository
  participant Playing
  Client->>PlayingController: POST /api/playings/{playingId}/midi-events
  PlayingController->>PlayingService: saveMidiEvents(userId, playingId, request)
  PlayingService->>PlayingRepository: 조회 및 최근 완료 여부 확인
  PlayingRepository-->>PlayingService: Playing 또는 저장 이력 여부
  PlayingService->>Playing: 소유자 검증 및 MIDI 완료 처리
  Playing-->>PlayingService: saved MIDI data
  PlayingService-->>PlayingController: MidiEventSaveResponse
  PlayingController-->>Client: ApiResponse
Loading

Possibly related PRs

  • Musereview/BE#15: Playing MIDI 검증 및 오류 코드 변경과 직접 연결됩니다.
  • Musereview/BE#23: MidiEventDataPlaying 저장 구조를 확장합니다.

Poem

이벤트가 줄을 서고
연주 세션 문이 열리네
검증을 통과한 음표들이
저장소로 춤춰 가네 🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 MIDI 이벤트 저장 API 구현이라는 핵심 변경을 정확히 반영해 간결하고 명확합니다.
Linked Issues check ✅ Passed API, 소유권·IN_PROGRESS 검증, 입력 검증, 응답, 전용 예외 처리가 모두 반영되어 이슈 요구사항을 충족합니다.
Out of Scope Changes check ✅ Passed 추가된 변경은 기능 구현에 직접 연결되어 보이며 별도 범위 이탈로 판단되는 내용은 없습니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#20-midi-event-save

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/java/com/mr/domain/playing/dto/req/MidiEventSaveRequest.java`:
- Around line 13-14: Update the events field in MidiEventSaveRequest to enforce
the 100,000-item maximum during request validation, preferably by reusing the
shared Playing limit constant instead of duplicating it. Ensure validation
occurs before PlayingService converts entries to MidiEventData; configure a
request-body size limit as well if the existing application configuration
provides the appropriate setting.

In `@src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java`:
- Around line 17-19: Update PlayingErrorStatus so the existing
MISSING_PLAYING_MODE, MISSING_PLAYING_STATUS, and UNSUPPORTED_PLAYING_MODE
entries retain their prior PLAYING_400_11 through PLAYING_400_13 codes. Add
MIDI-specific errors in a separate code range without renumbering these
established non-MIDI codes.
🪄 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: CHILL

Plan: Pro Plus

Run ID: afc90716-482a-4af6-bc9e-1297126b5216

📥 Commits

Reviewing files that changed from the base of the PR and between a3e0878 and 6f72d48.

📒 Files selected for processing (10)
  • src/main/java/com/mr/domain/playing/controller/PlayingController.java
  • src/main/java/com/mr/domain/playing/dto/req/MidiEventSaveRequest.java
  • src/main/java/com/mr/domain/playing/dto/res/MidiEventSaveResponse.java
  • src/main/java/com/mr/domain/playing/entity/MidiEventData.java
  • src/main/java/com/mr/domain/playing/entity/Playing.java
  • src/main/java/com/mr/domain/playing/exception/MidiEventErrorStatus.java
  • src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java
  • src/main/java/com/mr/domain/playing/service/PlayingService.java
  • src/test/java/com/mr/domain/playing/controller/PlayingControllerTest.java
  • src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java

Comment thread src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java Outdated

@ownue ownue left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

코멘트 확인 부탁드립니동

Integer velocity,

@NotNull(message = "MIDI 이벤트 발생 시간은 필수입니다.")
@Min(value = 0, message = "MIDI 이벤트 발생 시간은 0 이상이어야 합니다.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR 참고 사항에는 10분을 초과하는 MIDI 이벤트는 저장 대상에서 제외한다고 되어 있는데, 현재 DTO와 서비스 로직에서는 timestampMs >= 0만 검증하고 전체 이벤트를 그대로 저장하는 것으로 보입니다! timestampMs > 600_000인 이벤트를 필터링하려는 의도인지, 요청 자체를 예외 처리하려는 의도인지 명확히 해야 할 것 같습니다...🥹🥹

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

최대 timestampMs는 요청 시점에 알 수 있는 고정값이 아니라 실제 연주 종료 시각을 기준으로 동적으로 결정되므로, DTO에서는 상한 검증을 두지 않았어요! (음수 값만 검증)
→ Playing.completeWithMidiData()에서 실제 연주 시간 + 500ms의 허용 오차를 적용하고, 최대 10분(600_000ms)까지만 저장하도록 필터링하는 비즈니스 로직으로 처리

다만 연주 시간이 정확히 10분에 도달한 경우에 최대 10분 제한으로 인해 추가 500ms의 허용 오차가 적용되지 않는데, 해당 동작이 적절한지에 대해서는 팀원분들 의견도 궁금합니다ㅎㅎ

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

도메인에 대한 이해가 많이 부족하지만.... 저는 지금은 괜찮다구 생각해요!!
추후 QA를 거쳐 디벨롭하면 될 것 같습니동

Comment thread src/main/java/com/mr/domain/playing/service/PlayingService.java
Comment thread src/main/java/com/mr/domain/playing/entity/Playing.java Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

지금 요 테스트는 completeWithMidiData()가 호출됐는지만 확인하고 있어, 실제 MIDI 저장/정렬과 IN_PROGRESS → COMPLETED 상태 전이는 테스트되지 않는 것 같은데 의도하신 건지 궁금합니다!

의도가 아니라면, 이번 기능의 핵심이 Playing.completeWithMidiData()에 있으므로 실제 Playing 객체를 생성한 도메인 테스트에서 상태 변경, 중복 sequence, 빈 목록, 이벤트 개수 제한 등을 검증하는 테스트도 추가로 진행하면 좋을 것 같습니다!

Comment thread src/main/java/com/mr/domain/playing/exception/MidiEventErrorStatus.java Outdated

@kimyw1018 kimyw1018 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

고생하셨습니다! 리뷰 확인해주세욥

public record MidiEventSaveRequest (
@NotEmpty(message = "MIDI 이벤트 목록은 필수입니다")
@Size(max = 100_000, message = "MIDI 이벤트 개수가 허용 범위를 초과했습니다.")
List<@Valid MidiEventRequest> events

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

리스트 내부 @Valid 어노테이션이 제대로 작동안될 수도 있어서 @Valid List events 이런식으로 선언하면 어떨까요

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

관련 문서 확인해 보니, Bean Validation 2.0부터 List<@Valid MidiEventRequest>처럼 컨테이너의 타입 인자에 @Valid를 적용하는 방식 권장 + @Valid List 방식도 기존 방식으로 계속 지원되고 있다고 나와서, 우선은 현재 작성한 List<@Valid MidiEventRequest> 형태를 유지해도 괜찮을 것 같다는 개인적인 의견입니다..!
다만 말씀해 주신 것처럼 실제 프로젝트 환경에서 리스트 내부 DTO의 검증이 정상적으로 동작하는지는 테스트 코드 추가해서 확인해 볼게요!

참고 문서: Jakarta Bean Validation

@p1001q p1001q left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

제가 남긴 리뷰들은 리팩토링 때 고려하셔도 될 거 같습니다! 수고하셨어요!

Comment thread src/main/java/com/mr/domain/playing/service/PlayingService.java Outdated
Comment thread src/main/java/com/mr/domain/playing/dto/req/MidiEventSaveRequest.java Outdated
@p1001q p1001q mentioned this pull request Jul 30, 2026
6 tasks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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/java/com/mr/domain/playing/exception/PlayingErrorStatus.java`:
- Around line 21-25: Update the PLAYING_ACCESS_DENIED entry in
PlayingErrorStatus so its code string uses the 403 HTTP-status prefix while
preserving its FORBIDDEN status and access-denied message.
🪄 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: CHILL

Plan: Pro Plus

Run ID: cba43fdd-4c19-43d1-be4f-9ea3a2500a45

📥 Commits

Reviewing files that changed from the base of the PR and between 6f72d48 and e94371c.

📒 Files selected for processing (11)
  • src/main/java/com/mr/domain/playing/constant/MidiEventConstants.java
  • src/main/java/com/mr/domain/playing/dto/req/MidiEventSaveRequest.java
  • src/main/java/com/mr/domain/playing/entity/Playing.java
  • src/main/java/com/mr/domain/playing/exception/MidiEventErrorStatus.java
  • src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java
  • src/main/java/com/mr/domain/playing/repository/PlayingRepository.java
  • src/main/java/com/mr/domain/playing/service/PlayingService.java
  • src/test/java/com/mr/domain/playing/controller/PlayingControllerTest.java
  • src/test/java/com/mr/domain/playing/dto/req/MidiEventSaveRequestTest.java
  • src/test/java/com/mr/domain/playing/entity/PlayingTest.java
  • src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/mr/domain/playing/service/PlayingServiceTest.java

Comment thread src/main/java/com/mr/domain/playing/exception/PlayingErrorStatus.java Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Feature - MIDI 이벤트 저장 API 구현

4 participants