Skip to content

Conversation

@wjdrjs00
Copy link
Collaborator

@wjdrjs00 wjdrjs00 commented Dec 17, 2025

Related issue 🛠

Work Description ✏️

  • 시공간 퀴즈를 구현했습니다.

Screenshot 📸

  • N/A

Uncompleted Tasks 😅

  • x

Summary by CodeRabbit

새로운 기능

  • New Features
    • 시간-공간 퀴즈 기능 추가: 이미지 선택지를 포함한 새로운 퀴즈 유형 추가로 사용자 경험 확장. 진행 상황 추적, 답변 선택, 결과 피드백이 포함된 완전한 퀴즈 플로우 제공.

✏️ Tip: You can customize this high-level summary in your review settings.

@wjdrjs00 wjdrjs00 self-assigned this Dec 17, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

요약

새로운 시공간 구조(SpaceTimeQuiz) 퀴즈 기능을 구현합니다. 네비게이션 라우팅, 도메인 및 데이터 모델, 뷰모델, 화면 구성요소, UI 상태 관리를 추가하고 기존 퀴즈 카테고리 뷰모델과 응답 처리 로직을 확장합니다.

변경사항

응집 그룹 / 파일 변경 요약
네비게이션 및 기본 설정
core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt, app/src/main/kotlin/com/moa/app/main/MainActivity.kt
AppRoute에 직렬화 가능한 SpaceTimeQuiz 라우트 추가, MainActivity에서 SpaceTimeQuizScreen으로 네비게이션 바인딩 구성
도메인 모델
domain/src/main/kotlin/com/moa/app/domain/quiz/model/SpaceTimeQuiz.kt
Quiz 인터페이스를 구현하는 SpaceTimeQuiz 데이터 클래스 추가 (id, type, questionFormat, questionContent, answer, questionImageUrl, imageOptionsUrl 필드 포함), isAnswerCorrect() 메서드 구현
데이터 모델 및 응답 처리
data/src/main/kotlin/com/moa/app/data/quiz/model/response/SpaceTimeQuizResponse.kt, data/src/main/kotlin/com/moa/app/data/quiz/model/response/QuizResponse.kt
SpaceTimeQuizResponse 데이터 클래스 추가 (@Serializable, @SerialName("SPACETIME") 어노테이션 포함), toDomain() 확장 함수 추가, QuizResponse.toDomain()의 when 식에 SpaceTimeQuizResponse 처리 추가
기능 구현
feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizViewModel.kt, feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizScreen.kt, feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/model/SpaceTimeQuizUiState.kt
SpaceTimeQuizViewModel 추가 (퀴즈 로드, 답변 선택, 정답 확인, 점수 업로드 로직 구현), SpaceTimeQuizScreen 컴포저블 추가 (UI 렌더링, 다이얼로그 처리), SpaceTimeQuizUiState 데이터 클래스 추가 (로딩 상태, 에러, 퀴즈 목록, 선택된 답변, 결과 추적)
카테고리 통합
feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/QuizCategoryViewModel.kt
enabledCategories에 SPACETIME 추가, onCategoryClicked 흐름에서 SPACETIME 카테고리를 AppRoute.SpaceTimeQuiz로 네비게이션

시퀀스 다이어그램

sequenceDiagram
    participant User
    participant Screen as SpaceTimeQuizScreen
    participant ViewModel as SpaceTimeQuizViewModel
    participant UseCase as FetchQuizUseCase
    participant API as Remote API
    participant Upload as UploadQuizScoreUseCase

    User->>Screen: 화면 진입
    Screen->>ViewModel: init (loadSpaceTimeQuizzes)
    ViewModel->>UseCase: fetchQuizzes(QuizCategory.SPACETIME)
    UseCase->>API: SpaceTime 퀴즈 데이터 요청
    API-->>UseCase: SpaceTimeQuizResponse 리스트 반환
    UseCase-->>ViewModel: SpaceTimeQuiz로 변환 완료
    ViewModel->>ViewModel: uiState 업데이트 (quizzes, isLoading = false)
    Screen->>Screen: currentQuiz 렌더링

    User->>Screen: 선택지 클릭
    Screen->>ViewModel: selectAnswer(index)
    ViewModel->>ViewModel: selectedAnswerIndex = index

    User->>Screen: 계속 버튼 클릭
    Screen->>ViewModel: checkAnswer()
    ViewModel->>ViewModel: isAnswerCorrect() 계산
    ViewModel->>ViewModel: showResultDialog 표시, correctCount 증가
    Screen->>Screen: QuizResultDialog 표시

    User->>Screen: 결과 대화상자에서 진행
    Screen->>ViewModel: goToNextQuestion()
    
    alt 마지막 문제 아님
        ViewModel->>ViewModel: currentQuestionIndex 증가
        ViewModel->>ViewModel: showResultDialog = false
        Screen->>Screen: 다음 문제 렌더링
    else 마지막 문제
        ViewModel->>Upload: uploadQuizResult (QuizScore)
        Upload->>API: 최종 점수 업로드
        API-->>Upload: 성공
        Upload-->>ViewModel: 완료
        ViewModel->>ViewModel: 이전 화면으로 네비게이션
    end
Loading

코드 리뷰 난이도

🎯 4 (복잡함) | ⏱️ ~45분

검토 시 주의 사항

  • SpaceTimeQuizViewModel의 상태 관리: loadSpaceTimeQuizzes에서 최소 로딩 지연, 필터링 로직, 타이밍 및 에러 처리 확인 필요
  • selectAnswer 및 checkAnswer 흐름: 상태 가드 조건(로딩 중, 다이얼로그 표시 시 선택 불가), 정답 검증 로직, 지연된 goToNextQuestion 스케줄링 확인
  • uploadQuizScoreUseCase 통합: QuizScore 객체 구성, 성공/실패 처리, 네비게이션 전환 로직 검토
  • UI 상태 계산 속성: currentQuiz, totalSteps, currentStep, isContinueButtonEnabled의 올바른 계산 확인
  • SpaceTimeQuizResponse.toDomain() 매핑: imageOptionsUrl의 ImmutableList 변환, 필드 직렬화 이름(@SerialName) 정확성 확인
  • existing QuizResponse.toDomain() when 식: SpaceTimeQuizResponse 처리 추가 시 다른 분기와의 일관성 확인

관련 PR

🐰 시간과 공간을 넘나드는 새로운 문제들,
선택지 중에서 손가락이 춤을 추네요!
정답 맞출 때마다 점수 올라오고,
결과 대화에서 환호성 터지니까요.
영리한 토끼도 감탄하는 퀴즈여행! 🚀✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 시공간 퀴즈 구현이라는 메인 변경사항을 명확하게 설명하고 있습니다.
Description check ✅ Passed PR 설명이 제공된 템플릿의 모든 필수 섹션(Related issue, Work Description, Screenshot, Uncompleted Tasks)을 포함하고 있습니다.
Linked Issues check ✅ Passed 코드 변경사항이 링크된 이슈 #50의 모든 요구사항을 충족합니다: 시공간 퀴즈 UI 구현(SpaceTimeQuizScreen), 비즈니스 로직 및 API 연동(SpaceTimeQuizViewModel, 데이터 모델, 라우팅).
Out of Scope Changes check ✅ Passed 모든 변경사항이 시공간 퀴즈 기능 구현과 관련된 범위 내의 변경입니다. 범위를 벗어난 변경사항이 없습니다.
✨ 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 feature/50-spacetime-quiz

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.

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: 2

🧹 Nitpick comments (2)
feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizScreen.kt (1)

106-111: 이미지 로딩 에러 처리를 고려해보세요.

AsyncImage 컴포넌트에 placeholder는 있지만 error 파라미터가 설정되지 않았습니다. 네트워크 오류나 잘못된 URL 시 사용자 경험 향상을 위해 에러 이미지를 추가하는 것을 권장합니다.

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

 AsyncImage(
     model = question.questionImageUrl,
     placeholder = painterResource(R.drawable.img_default_card_2),
+    error = painterResource(R.drawable.img_default_card_2),
     contentDescription = null,
     modifier = Modifier.fillMaxWidth()
 )

옵션 이미지에도 동일하게 적용:

 AsyncImage(
     model = option,
+    error = painterResource(R.drawable.img_default_card_2),
     contentDescription = null,
     modifier = Modifier.padding(vertical = 16.dp)
 )

Also applies to: 131-135

feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizViewModel.kt (1)

40-61: awaitAll 호출 후 중복된 await() 호출 개선 가능

awaitAll이 이미 모든 deferred를 완료시키므로 quizzesDeferred.await()를 다시 호출하는 것은 중복입니다. 결과를 직접 추출하는 것이 더 명확합니다.

또한, filterIsInstance 후 빈 리스트인 경우에 대한 처리가 없습니다. 퀴즈가 없는 경우 사용자에게 적절한 피드백이 필요할 수 있습니다.

     private fun loadSpaceTimeQuizzes() {
         viewModelScope.launch {
             val minLoadingTime = async { delay(2000L) }
             val quizzesDeferred = async { fetchQuizUseCase(QuizCategory.SPACETIME) }
-            awaitAll(minLoadingTime, quizzesDeferred)
-            quizzesDeferred.await().fold(
+            minLoadingTime.await()
+            quizzesDeferred.await().fold(
                 onSuccess = { quizzes ->
                     val spaceTimeQuizzes = quizzes.filterIsInstance<SpaceTimeQuiz>()
+                    if (spaceTimeQuizzes.isEmpty()) {
+                        Timber.w("No SpaceTime quizzes available")
+                    }
                     _uiState.update {
                         it.copy(
                             isLoading = false,
                             quizzes = spaceTimeQuizzes.toImmutableList()
                         )
                     }
                 },
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68fb036 and 7aa3ee8.

📒 Files selected for processing (9)
  • app/src/main/kotlin/com/moa/app/main/MainActivity.kt (2 hunks)
  • core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1 hunks)
  • data/src/main/kotlin/com/moa/app/data/quiz/model/response/QuizResponse.kt (1 hunks)
  • data/src/main/kotlin/com/moa/app/data/quiz/model/response/SpaceTimeQuizResponse.kt (1 hunks)
  • domain/src/main/kotlin/com/moa/app/domain/quiz/model/SpaceTimeQuiz.kt (1 hunks)
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/QuizCategoryViewModel.kt (1 hunks)
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizScreen.kt (1 hunks)
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizViewModel.kt (1 hunks)
  • feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/model/SpaceTimeQuizUiState.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run Unit Tests
🔇 Additional comments (15)
feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/model/SpaceTimeQuizUiState.kt (1)

9-47: LGTM! 잘 설계된 UI State입니다.

불변 컬렉션을 사용하고 computed properties로 파생 상태를 계산하는 구조가 깔끔합니다. isContinueButtonEnabled 로직도 적절하게 구현되었습니다.

data/src/main/kotlin/com/moa/app/data/quiz/model/response/QuizResponse.kt (1)

24-24: LGTM! 기존 패턴을 잘 따릅니다.

SpaceTimeQuizResponse 분기가 다른 퀴즈 타입들과 일관성 있게 추가되었습니다.

feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/QuizCategoryViewModel.kt (2)

36-43: LGTM! 카테고리 활성화가 적절합니다.

SPACETIME 카테고리가 다른 활성화된 카테고리들과 함께 추가되었습니다.


60-60: LGTM! 네비게이션 로직이 일관성 있습니다.

SpaceTimeQuiz로의 네비게이션이 다른 퀴즈 타입들과 동일한 패턴으로 구현되었습니다.

core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1)

46-47: LGTM! 라우트가 올바르게 정의되었습니다.

SpaceTimeQuiz 라우트가 다른 라우트들과 일관된 방식으로 추가되었습니다.

app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)

36-36: LGTM! 네비게이션 와이어링이 적절합니다.

SpaceTimeQuizScreen이 다른 퀴즈 화면들과 동일한 방식으로 네비게이션 그래프에 등록되었습니다.

Also applies to: 84-84

data/src/main/kotlin/com/moa/app/data/quiz/model/response/SpaceTimeQuizResponse.kt (2)

10-19: LGTM! Response 모델이 적절히 정의되었습니다.

직렬화 어노테이션과 필드 정의가 올바르며, QuizResponse를 적절히 상속합니다.


21-31: LGTM! 도메인 매핑이 깔끔합니다.

toDomain() 함수가 모든 필드를 적절히 매핑하며, toPersistentList()로 불변 컬렉션을 보장합니다.

feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizScreen.kt (2)

36-71: LGTM! 화면 구조가 잘 설계되었습니다.

로딩, 콘텐츠, 결과 다이얼로그, 종료 확인 다이얼로그의 조건부 렌더링이 명확하게 구현되었습니다. BackHandler로 뒤로가기도 적절히 처리됩니다.


115-138: LGTM! 버튼 상태 로직이 정확합니다.

선택된 인덱스에 따라 버튼 상태(DEFAULT, SELECTED, UNSELECTED)가 동적으로 결정되는 로직이 직관적이고 정확합니다.

domain/src/main/kotlin/com/moa/app/domain/quiz/model/SpaceTimeQuiz.kt (1)

14-17: 답변 형식의 일관성 확인 필요

SpaceTimeQuiz와 다른 퀴즈 타입의 답변 형식이 불일치합니다. SpaceTimeQuiz.isAnswerCorrect는 선택된 인덱스를 1-based로 변환하여 문자열 비교하는 반면, LinguisticQuizPersistenceQuiz는 실제 답안 옵션 텍스트와 직접 비교합니다.

API가 SpaceTimeQuiz에 대해 답변을 "1", "2", "3" 형식으로 반환하는지, 다른 퀴즈 타입과 일관되게 처리되는지 확인하고, 필요시 문서화해주세요.

feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/spacetime/SpaceTimeQuizViewModel.kt (4)

26-38: LGTM!

Hilt DI 구성과 StateFlow 패턴을 사용한 상태 관리가 적절합니다. Init 블록에서 퀴즈 로딩을 트리거하는 것도 표준적인 ViewModel 패턴입니다.


63-68: LGTM!

로딩 중이거나 결과 다이얼로그가 표시되는 동안 답변 선택을 방지하는 가드 로직이 적절합니다.


90-106: LGTM!

다음 문제로 진행하거나 마지막 문제 후 결과를 업로드하는 로직이 명확하게 구현되어 있습니다.


128-137: LGTM!

종료 다이얼로그 표시/숨김 및 네비게이션 로직이 간결하고 명확합니다.

@wjdrjs00 wjdrjs00 merged commit fdd8e88 into develop Dec 17, 2025
4 checks passed
@wjdrjs00 wjdrjs00 deleted the feature/50-spacetime-quiz branch December 17, 2025 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 시공간 구조 퀴즈를 구현합니다.

2 participants