Skip to content

[FEAT] 학습 조회 API 구현 (1/3) - 학습 주제 전체보기 · 실습 데이터 조회#50

Merged
p1001q merged 7 commits into
developfrom
feat/#40-learning-api
Jul 25, 2026
Merged

[FEAT] 학습 조회 API 구현 (1/3) - 학습 주제 전체보기 · 실습 데이터 조회#50
p1001q merged 7 commits into
developfrom
feat/#40-learning-api

Conversation

@p1001q

@p1001q p1001q commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

📍 개요

학습 화면 조회 API 6개 중 2개(학습 주제 전체보기, 단계별 연습 실행 정보 조회) 구현

⚠️ 이슈 #40(학습 조회 API 6개)은 작업 단위를 위해 PR 3개로 나눠 진행합니다. 이번이 1번째 PR이고, 나머지 4개 API(학습 홈, 실전 반주법 전체보기, 커리큘럼 조회, 단계별 상세 조회)는 후속 PR 2개로 이어서 올릴 예정입니다.

⛓️‍💥 관련 이슈


🛠️ 작업 내용

  • LearningRepository/PlayingExampleRepository에 조회 메서드 추가,
    LearningErrorStatus에 에러코드 2개(LEARNING_400_02, LEARNING_404_03) 추가
  • GET /api/learnings/theory (학습 주제 전체보기, difficulty 필수) 구현
  • GET /api/learnings/{learningId}/steps/{learningStepId}/practice-data (단계별 연습 실행 정보 조회) 구현
  • 두 API 모두 is_active=false인 학습을 존재하지 않는 학습과 동일하게 404 처리
  • 기존 학습 API(saveLearningResult, getLearningProgress) 포함해
    컨트롤러 전체에 Swagger @Operation/@Tag 문서 추가
  • 컨트롤러 테스트 7개 추가(MockMvc standalone + Mockito)

🔥 리뷰 요청 사항

  • difficulty 쿼리 파라미터를 @RequestParam(required=false)로 받고 서비스에서 직접 검증하는 방식(누락 시 MissingServletRequestParameterException으로 500 떨어지는 걸 피하려는 의도)이 적절한지
  • practice-data API는 명세상 유저 존재 확인(USER_404_01)이 없어서 안 넣었는데, 이 판단이 맞는지
  • LearningStep이 다른 learningId에 속한 경우 기존 validateBelongsTo()(400)를 재사용하지 않고 GET API에서만 LEARNING_STEP_NOT_FOUND(404)로 별도 처리한 것(POST 저장 API와 의도적으로 다른 시맨틱)

✅ 체크리스트

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

📎 참고 사항

  • 이슈 #40의 나머지 API(1-1 학습 홈, 1-3 실전 반주법 전체보기, 1-4 커리큘럼 조회, 1-5 단계별 상세 조회)는
    진행률 계산 로직을 공유하는 것들끼리 묶어서 후속 PR 2개로 이어집니다.

Summary by CodeRabbit

  • 새로운 기능

    • 학습 단계별 연습 실행 정보(템포, 조성, MIDI 데이터)를 조회할 수 있습니다.
    • 난이도별 이론 학습 주제 목록을 확인할 수 있습니다.
    • 학습 결과 저장 및 진행률 조회 API 문서가 개선되었습니다.
  • 버그 수정

    • 존재하지 않는 학습 단계·연습 정보와 잘못된 난이도 입력에 대해 명확한 오류가 제공됩니다.
    • 활성 학습이 제목순으로 정렬되어 표시됩니다.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 36 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: ca1c718a-16df-4345-9eaf-13cb7163fba7

📥 Commits

Reviewing files that changed from the base of the PR and between 8eb86c7 and d7e2923.

📒 Files selected for processing (4)
  • src/main/java/com/mr/domain/learning/dto/res/LearningPracticeDataResponseDTO.java
  • src/main/java/com/mr/domain/learning/service/LearningService.java
  • src/test/java/com/mr/domain/learning/controller/LearningControllerTest.java
  • src/test/java/com/mr/domain/learning/service/LearningServiceTest.java
📝 Walkthrough

Walkthrough

학습 단계별 연습 실행 정보와 난이도별 이론 목록 조회 API가 추가되었습니다. 응답 DTO, 저장소 조회 메서드, 입력·존재성 검증, 오류 상태, 컨트롤러 테스트와 OpenAPI 문서가 함께 반영되었습니다.

Changes

학습 조회 기능

Layer / File(s) Summary
조회 계약과 저장소 정의
src/main/java/com/mr/domain/learning/dto/res/*, src/main/java/com/mr/domain/learning/exception/LearningErrorStatus.java, src/main/java/com/mr/domain/learning/repository/*
연습 데이터와 이론 목록 응답 DTO가 추가되고, 활성 이론 학습 및 단계별 PlayingExample 조회 메서드와 관련 오류 상태가 정의되었습니다.
학습 조회 서비스 구현
src/main/java/com/mr/domain/learning/service/LearningService.java
학습·단계 소속·사용자·난이도를 검증한 뒤 연습 데이터와 THEORY 카테고리의 활성 학습을 제목순으로 조회합니다.
조회 엔드포인트와 검증
src/main/java/com/mr/domain/learning/controller/LearningController.java, src/test/java/com/mr/domain/learning/controller/LearningControllerTest.java
두 GET 엔드포인트가 추가되고 기존 API 문서가 보강되었습니다. 성공 응답과 학습·단계·연습 데이터·난이도·사용자 오류 응답을 테스트합니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant LearningController
  participant LearningService
  participant Repository
  Client->>LearningController: 조회 요청
  LearningController->>LearningService: learningId, stepId, difficulty 전달
  LearningService->>Repository: 검증 및 조건부 학습 데이터 조회
  Repository-->>LearningService: 엔티티 또는 PlayingExample 반환
  LearningService-->>LearningController: 응답 DTO 반환
  LearningController-->>Client: ApiResponse 반환
Loading

Possibly related PRs

  • Musereview/BE#24: PlayingExample, LearningStep, LearningDifficulty 등 이번 조회 기능이 사용하는 학습 도메인 구조와 직접 연결됩니다.
  • Musereview/BE#31: 학습·단계 불일치 검증과 LearningErrorStatus 확장 흐름이 연관됩니다.
  • Musereview/BE#46: LearningController, LearningService, 학습 오류 코드 변경을 함께 다룹니다.

Poem

단계마다 리듬이 톡,
이론 목록은 난이도별로 쏙.
잘못된 요청엔 오류 표지판,
DTO가 데이터를 단정히 포장.
API 문서도 함께 노래하네 🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% 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 학습 주제 전체보기와 실습 데이터 조회라는 이번 PR의 핵심 변경을 정확히 담고 있어 제목이 적절합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#40-learning-api

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.

public record PracticeDataResultDTO(
Integer bpm,
String keySignature,
String midiData

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.

P2: midiData가 DB에서는 JSON으로 저장되지만 응답 DTO에서는 String

API 응답에서 JSON 객체가 아닌 이스케이프된 문자열로 반환될 우려가 있습니다!
프론트에서 MIDI 데이터를 객체나 배열로 바로 사용해야 하는 명세라면 JsonNode 또는 별도의 MIDI 응답 DTO로 반환하는 방향을 고려하면 좋을 것 같아요.
현재처럼 문자열 반환이 의도된 것인지 확인 부탁드립니다!

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.

앗 맞아요 DB엔 JSON으로 저장되는데 응답 DTO에서 String 그대로 내려가면서 이스케이프된 문자열로 나가고 있었네요. @JsonRawValue 붙여서 실제 JSON 객체로 나가도록 수정했습니다. 확인 감사합니다!!

throw new GeneralException(LearningErrorStatus.INVALID_DIFFICULTY);
}
try {
return LearningDifficulty.valueOf(difficulty);

@on1yoneprivate on1yoneprivate Jul 24, 2026

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.

P2: 소문자나 앞뒤 공백이 포함된 값도 모두 400으로 처리될 우려

항상 대문자 enum 값을 전달하도록 약속된 경우에는 괜찮지만, 입력 유연성을 고려한다면 trim()toUpperCase()로 정규화한 뒤 변환하는 방법도 고려해 보면 좋을 것 같습니다~

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.

클라이언트에서 직접 입력하는 값이 아니라, 현재는 큰 문제가 없어 보이긴 하지만, 정규화 후 변환을 고려해도 좋을 것 같아 의견 남깁니다.

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.

말씀하신 대로 trim()+toUpperCase() 넣었습니다.
겸사겸사 toUpperCase()에 로케일 안 주면 터키어 로케일 같은 데서 i가 İ로 바뀌는 이슈가 있어서
Locale.ROOT도 같이 넣어뒀어요. 좋은 의견 감사합니다!

@on1yoneprivate on1yoneprivate 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.

로직상 크게 문제되는 부분은 없어 승인했습니다!

코멘트로 남긴 부분들은 필수 수정 사항이라기보다는, 추후 유지보수성과 확장성을 고려한 제안 사항입니다. 편하실 때 검토 부탁드립니다~

@rkdehdrbs7885-oss rkdehdrbs7885-oss 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.

작업 고생 많으셨습니다! 서비스 로직에 필요했던 부분들이 잘 채워진 것 같습니다. LearningRepository 쪽 조회 메서드는 조건이 많아서 나중에 리팩토링할 때 복합 인덱스 걸어두는 것도 같이 고려해 보면 좋을 것 같습니다!

@p1001q
p1001q merged commit a5ff3a9 into develop Jul 25, 2026
2 checks passed
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.

3 participants