-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add patch quiz progress #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,13 +4,21 @@ | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public class QuizDto { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public record GradeRequest(@NotBlank | ||||||||||||||||||||||||||||||||||
| String answer) { | ||||||||||||||||||||||||||||||||||
| public record GradeRequest(@NotBlank String answer) { | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public record GradeResponse( | ||||||||||||||||||||||||||||||||||
| boolean correct, | ||||||||||||||||||||||||||||||||||
| double score, | ||||||||||||||||||||||||||||||||||
| String correctAnswer) { | ||||||||||||||||||||||||||||||||||
| boolean correct, | ||||||||||||||||||||||||||||||||||
| double score, | ||||||||||||||||||||||||||||||||||
| String correctAnswer) { | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public record SyncProgressRequest( | ||||||||||||||||||||||||||||||||||
| Long lastQuizId, | ||||||||||||||||||||||||||||||||||
| Integer totalQuestions, | ||||||||||||||||||||||||||||||||||
| Integer success, | ||||||||||||||||||||||||||||||||||
| Integer failure, | ||||||||||||||||||||||||||||||||||
| Integer solveTime, | ||||||||||||||||||||||||||||||||||
| boolean isComplete) { | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| import com.blaybus.backend.domain.quiz.QuizUserProgress; | ||
| import com.blaybus.backend.domain.scene.SceneInformation; | ||
| import com.blaybus.backend.domain.user.User; | ||
| import com.blaybus.backend.dto.QuizDto; | ||
| import com.blaybus.backend.dto.QuizResponse; | ||
| import com.blaybus.backend.exception.BusinessException; | ||
| import com.blaybus.backend.exception.CommonErrorCode; | ||
|
|
@@ -52,6 +53,26 @@ public QuizResponse getSceneQuizzes(Long sceneId, User user) { | |
| return mapToResponse(sceneId, progress, quizzes); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void syncProgress(Long sceneId, QuizDto.SyncProgressRequest request, User user) { | ||
| QuizUserProgress progress = progressRepository.findByUserIdAndSceneId(user.getId(), sceneId) | ||
| .orElseThrow(() -> new BusinessException(CommonErrorCode.QUIZ_PROGRESS_NOT_FOUND)); | ||
|
|
||
| QuizUserProgress updated = QuizUserProgress.builder() | ||
| .id(progress.getId()) | ||
| .user(progress.getUser()) | ||
| .scene(progress.getScene()) | ||
| .lastQuizId(request.lastQuizId()) | ||
| .totalQuestions(request.totalQuestions()) | ||
| .success(request.success()) | ||
| .failure(request.failure()) | ||
| .solveTime(request.solveTime()) | ||
| .isComplete(request.isComplete()) | ||
| .build(); | ||
|
|
||
| progressRepository.save(updated); | ||
| } | ||
|
Comment on lines
+57
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPA 엔티티를 업데이트하는 현재 방식은 비효율적이며, 더 나은 대안이 있습니다. 현재 방식의 문제점 (Cons):
개선 방안 (Pros): 대안 1: 엔티티에 업데이트 메소드 추가 (권장) // QuizUserProgress.java
public void syncProgress(QuizDto.SyncProgressRequest request) {
this.lastQuizId = request.lastQuizId();
this.totalQuestions = request.totalQuestions();
this.success = request.success();
this.failure = request.failure();
this.solveTime = request.solveTime();
this.isComplete = request.isComplete();
}그 후 // QuizService.java
@Transactional
public void syncProgress(Long sceneId, QuizDto.SyncProgressRequest request, User user) {
QuizUserProgress progress = progressRepository.findByUserIdAndSceneId(user.getId(), sceneId)
.orElseThrow(() -> new BusinessException(CommonErrorCode.QUIZ_PROGRESS_NOT_FOUND));
progress.syncProgress(request);
// progressRepository.save() 호출이 더 이상 필요하지 않습니다.
}대안 2: Setter 사용 |
||
|
|
||
| private QuizResponse mapToResponse(Long sceneId, QuizUserProgress progress, List<Quiz> quizzes) { | ||
| QuizResponse.UserProgressDto progressDto = new QuizResponse.UserProgressDto( | ||
| progress.getId(), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| package com.blaybus.backend.service; | ||
|
|
||
| import static org.assertj.core.api.Assertions.*; | ||
| import static org.mockito.BDDMockito.*; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.extension.ExtendWith; | ||
| import org.mockito.InjectMocks; | ||
| import org.mockito.Mock; | ||
| import org.mockito.junit.jupiter.MockitoExtension; | ||
|
|
||
| import com.blaybus.backend.domain.quiz.QuizUserProgress; | ||
| import com.blaybus.backend.domain.scene.SceneInformation; | ||
| import com.blaybus.backend.domain.user.User; | ||
| import com.blaybus.backend.dto.QuizDto; | ||
| import com.blaybus.backend.exception.BusinessException; | ||
| import com.blaybus.backend.exception.CommonErrorCode; | ||
| import com.blaybus.backend.repository.QuizUserProgressRepository; | ||
|
|
||
| @ExtendWith(MockitoExtension.class) | ||
| class QuizServiceTest { | ||
|
|
||
| @Mock | ||
| private QuizUserProgressRepository progressRepository; | ||
|
|
||
| @InjectMocks | ||
| private QuizService quizService; | ||
|
|
||
| @Test | ||
| @DisplayName("퀴즈 진행 상황을 정상적으로 동기화한다.") | ||
| void syncProgressSuccess() { | ||
| // given | ||
| Long sceneId = 1L; | ||
| User user = mock(User.class); | ||
| given(user.getId()).willReturn(1L); | ||
| SceneInformation scene = SceneInformation.builder().id(sceneId).build(); | ||
| QuizUserProgress progress = QuizUserProgress.builder() | ||
| .id(1L) | ||
| .user(user) | ||
| .scene(scene) | ||
| .totalQuestions(5) | ||
| .success(2) | ||
| .failure(1) | ||
| .solveTime(100) | ||
| .isComplete(false) | ||
| .build(); | ||
|
|
||
| QuizDto.SyncProgressRequest request = new QuizDto.SyncProgressRequest( | ||
| 3L, 5, 4, 1, 250, true); | ||
|
|
||
| given(progressRepository.findByUserIdAndSceneId(user.getId(), sceneId)) | ||
| .willReturn(Optional.of(progress)); | ||
|
|
||
| // when | ||
| quizService.syncProgress(sceneId, request, user); | ||
|
|
||
| // then | ||
| then(progressRepository).should().save(argThat(updated -> updated.getLastQuizId().equals(3L) && | ||
| updated.getSuccess().equals(4) && | ||
| updated.getSolveTime().equals(250) && | ||
| updated.isComplete())); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("진행 상황이 존재하지 않으면 예외를 던진다.") | ||
| void syncProgressNotFound() { | ||
| // given | ||
| Long sceneId = 1L; | ||
| User user = mock(User.class); | ||
| given(user.getId()).willReturn(1L); | ||
| QuizDto.SyncProgressRequest request = new QuizDto.SyncProgressRequest( | ||
| 3L, 5, 4, 1, 250, true); | ||
|
|
||
| given(progressRepository.findByUserIdAndSceneId(user.getId(), sceneId)) | ||
| .willReturn(Optional.empty()); | ||
|
|
||
| // when & then | ||
| assertThatThrownBy(() -> quizService.syncProgress(sceneId, request, user)) | ||
| .isInstanceOf(BusinessException.class) | ||
| .hasFieldOrPropertyWithValue("errorCode", CommonErrorCode.QUIZ_PROGRESS_NOT_FOUND); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
컨트롤러 내 여러 메소드에서 사용자 정보를 조회하는 로직이 중복되고 있습니다.
이러한 중복은 코드 유지보수성을 저하시킬 수 있습니다.
개선 방안:
HandlerMethodArgumentResolver를 구현하여 사용자 조회 로직을 중앙에서 관리하고, 컨트롤러 메소드에서는@AuthUser User user와 같이 어노테이션을 통해 바로User객체를 주입받는 방식을 고려해볼 수 있습니다. 이렇게 하면 코드 중복을 제거하고 컨트롤러를 더 깔끔하게 유지할 수 있습니다. 이는 장기적인 관점에서 프로젝트의 유지보수성을 높이는 데 도움이 될 것입니다.References