EduClass Project

[Project] 학생 시험 채점 기능 개선 - DTO 활용으로 확장성 높이기

sagecode 2025. 3. 7. 16:13

학생이 제출한 답안을 채점하는 기존 방식의 문제점

    // 학생 시험 채점
    @Transactional
    public void markStudentTest(Long id, StudentTestMarkRequest request) {
        StudentTest studentTest = getStudentTestById(id);
        ProblemSet problemSet = studentTest.getProblemSet();
        int score = 0;

        List<String> answerSet = new ArrayList<>(); // 해답
        problemSet.getProblems().forEach(problem -> answerSet.add(problem.getAnswer()));

        List<String> studentAnswerSet = request.getStudentAnswers();
        for (int i = 0; i < studentAnswerSet.size(); i++) {
            if (studentAnswerSet.get(i).equals(answerSet.get(i))) {
                score++;
            }
        }
        studentTest.setScore(score);
        studentTest.setCompleted(Completed.Y);

        studentTestRepository.save(studentTest);
    }

 

StudentTestService의 학생시험 채점 method이다.

학생이 시험을 보았을 때, 정답을 적은 request를 StudentTestMarkRequest로 만들어 파라미터로 넘겼다.

 

@Getter
public class StudentTestMarkRequest {
    private Long studentId;
    private Long StudentTestId;
    private List<String> studentAnswers;
}

 

기존 채점 방식은 학생이 제출한 정답을 단순 문자열 리스트(List<String> studentAnswers)로 받아, 문제별 정답과 순서대로 비교하는 구조였다. 기본적인 비교에는 문제가 없지만, 다음과 같은 한계점이 존재했다.

 

  1. 문제 ID와 유형(type) 정보 부족
    • List<String>으로만 처리했을 때, 어떤 문제가 어떤 유형인지 정답은 어떤 형태인지 알기 힘들다.
    • 객관식, 주관식, 서술형 문제를 채점할 때, 같은 방식으로 해야한다.
  2. 정렬 방식의 의존성
    • 만약 문제가 무작위로 출제되거나, 문제의 순서가 바뀌는 경우, 채점이 올바르게 이루어지지 않을 위험이 있다.
    • 문제별 고유 식별자인 problemId가 없기 때문에, 같은 문제가 두 번 출제될 경우에도 구분이 어렵다.
  3. 객체 지향적 설계 부족으로 확장성 저하
    • 단순한 List<String> 방식은 추가 기능(예: 서술형 문제 자동 채점, 부분 점수 부여 등)을 도입할 때, 기존 로직을 대폭 수정해야 하는 문제가 발생한다.
    • 새로운 채점 방식이 추가될 때마다 기존 로직을 변경해야 하므로 유지보수가 어렵다.

 

DTO를 활용하여 개선

위의 문제점을 해결하기 위해, 학생이 제출한 답안을 단순 List<String>이 아닌 StudentTestAnswerDTO 객체로 관리하는 방식으로 변경한다.

@Getter
public class StudentTestMarkRequest {
    private Long studentId;
    private Long StudentTestId;
    private List<StudentTestAnswerDTO> studentAnswers;
}
@Getter
@Setter
@NoArgsConstructor
public class StudentTestAnswerDTO {
    private Long problemId;
    private String answer;
    private ProblemType problemType;
}

이와 같이 문제 ID, 유형, 답안을 포함하는 객체(StudentTestAnswerDto)를 도입하면 다음과 같은 이점이 있다.

  1. 문제가 랜덤으로 섞이더라도 문제별 고유 ID(problemId)를 기반으로 정확한 정답 매칭 가능
  2. 문제 유형(객관식, 주관식, 서술형)에 따라 다른 채점 방식을 적용 가능