[TSK-71] 기존 기필 카테고리 대체과목 보정 로직 수정하여 동일과목 학점 미인정 이슈 해결#310
[TSK-71] 기존 기필 카테고리 대체과목 보정 로직 수정하여 동일과목 학점 미인정 이슈 해결#310
Conversation
…uired_courses`) 검증 로직 수정
Test Results188 tests 188 ✅ 26s ⏱️ Results for commit de8ae84. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e272f14ca
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| select e.groupCode from CourseEquivalence e | ||
| where e.curiNo = :curiNo | ||
| """) | ||
| Optional<String> findGroupCodeByCuriNo(String curiNo); |
There was a problem hiding this comment.
Enforce unique curi_no when reading equivalence group
findGroupCodeByCuriNo assumes a single row, but course_equivalences is populated from a sheet that is only string-validated (no uniqueness check on curi_no), so duplicate rows for the same course code will make this query return multiple results and throw IncorrectResultSizeDataAccessException at runtime during academic-basic evaluation. This should either be made tolerant (distinct/first) or guarded by validation/constraints so one bad sheet row does not break graduation checks.
Useful? React with 👍 / 👎.
| graduationSheetTable.getString(row, "curi_no"), | ||
| graduationSheetTable.getString(row, "curi_nm"), | ||
| graduationSheetTable.getString(row, "alt_group"), | ||
| graduationSheetTable.getString(row, "group_code"), |
There was a problem hiding this comment.
Reject required-course rows missing group_code
This sync now persists group_code and the new findRequiredCourseInGroup logic depends on it, but required-courses validation still does not require that column/value; if the sheet is not updated (or cells are blank), rows are saved with null group codes and group-based matching always misses, causing equivalent/replacement academic-basic courses to be incorrectly treated as not required instead of failing fast during sync.
Useful? React with 👍 / 👎.
…urseReplacement`기반 클래스 -> `CourseEquivalence`기반 클래스)
… 판단 책임을 `CourseEquivalence`로 위임
…ement` 관련 클래스 제거
이슈
기존 기필 카테고리 졸업요건 검사 시, 동일과목 커버리지가 낮아 미인정 처리되는 문제가 있었습니다.
[기존 로직]
이수 과목이
required_courses에 존재하는지 확인하고, 없으면course_replacement기반으로 대체된 최신 과목을 조회하여 과목명 일치 여부를 판단했습니다.제가 판단한 문제 상황을 예시로 들어보겠습니다.
case1) 사용자가 지정 과목보다 이전 버전의 과목을 들었을 경우
case2) 수강편람 내에 사용자가 이수한 과거 과목에 대한 정보가 없고, 동일과목 목록에는 정의되어 있는 경우
-> 이 경우는 A'라는 과목이 현재 A라는 과목으로 대체된 과거 과목인데, 수강편람에서 누락된 게 아닐까 추측해봅니다.
혹은 대체된 년도의 수강편람에는 존재하지만, 최신 수강편람에서 누락된 경우로 보입니다.
이런 누락된 케이스가 많이 존재할 거라고 가정하고 작업을 진행해보았습니다.
결과적으로, 기존 로직은 위 두가지 케이스를 커버하지 못했습니다.
원인
기존
course_replacements는 학번 단위로 대체과목을 관리하는 구조였습니다.이 구조는 다음 문제가 있었습니다.
required_courses에 적재된 과목보다 더 이전 버전의 과목을 이수한 경우 커버하지 못합니다.즉, 학번 단위 대체과목 관리 방식 자체가 동일과목/대체과목 판별의 커버리지를 떨어뜨리는 원인이라고 판단했습니다!
작업 내용
1. 졸업요건검사 API - 동일 및 대체과목 통합 관리 엔티티(
course_equivalences) 추가[기존]
대체과목 관리 테이블(
course_replacements)에는 학번 별로 대체과목(A→B)만 관리되었습니다.그러나 동일과목도 함께 관리되어야 합니다.
[개선]
기존
course_replacements는 학번별로 대체과목을 관리했기 때문에, 특정 학번의 학생이 이전 버전의 과목을 이수하는 엣지 케이스에서 커버리지가 낮았습니다. 또한 대체과목이 추가될 때마다 학번마다 모두 추가해줘야 하는 운영 리소스 문제도 존재했습니다.따라서 학번 단위가 아닌, 과목 자체를 기준으로 동일 및 대체과목 이력을 통합 관리하는
course_equivalences엔티티를 새로 추가했습니다.course_equivalences는 모든 학번 및 학과에 공통으로 적용되는 대상으로 관리합니다.[동일과목 및 대체과목을 통합하여 관리하는 방식을 채택한 이유]
동일과목이 대체과목의 상위 개념이라고 판단했습니다.
즉, 동일과목 카테고리 안에 대체과목 카테고리가 포함되는 구조입니다.
이렇게 판단한 이유는 다음과 같습니다.
따라서 동일과목으로 통합 관리하는 것이 커버리지와 운영 효율 모두에서 유리하다고 판단했습니다.
2. 졸업요건검사 API -
group_code기반으로 동일 및 대체과목 판단하도록 방식 변경[기존] 에는 다음과 같이 deprecated된 대체과목의 이수 여부를 판단하도록 했습니다.
동일 및 대체과목 통합 관리 엔티티(
course_equivalences)를 추가함에 따라,동일 및 대체과목 판단 방식을 변경했습니다.
[개선]
required_courses및course_equivalence에group_code칼럼을 추가했습니다.group_code로 묶었습니다.required_courses에 존재하는 과목들과course_equivalence에 존재하는 과목들도 같은group_code로 매핑되어 있습니다.개선된 동일 및 대체과목 판단 -> 동일성 체크 플로우는 다음과 같습니다.
이를 통해 이수한 과목이 구버전이더라도,
group_code가 일치하면 이수한 것으로 판단합니다.3. 카테고리 별 졸업요건 기준 데이터 조회 API
이 API는 사용자의 입학년도/학과/이수구분별 졸업 기준 데이터를 반환하며, 미이수 과목도 함께 내려줍니다.
course_replacements기반으로 최신 과목으로 보정하는 곳은 두 곳이 존재했습니다.이를
course_equivalences기반으로 보정하도록 책임을 위임하는 작업을 진행했습니다.3-1. deprecated 된 과목의 최신 과목 반환 로직 변경
[기존]
카테고리별 졸업요건 기준 데이터를 구성할 때, deprecated된 지정 과목은
course_replacements기반으로 최신 과목으로 보정하여 반환했습니다.즉, 기준 데이터에 과거 과목이나 deprecated 과목이 포함되어 있더라도, 응답을 내려주기 전에
course_replacements에서 학번 기준 대체 관계를 조회하여 현재 기준의 과목명/학수번호로 치환하는 방식이었습니다.흐름으로 보면 아래와 같습니다.
이 방식은 학번 단위 대체과목 데이터에 의존하기 때문에, 변경 이력이 길거나 동일과목만 존재하는 케이스에서는 최신 과목 보정이 누락될 수 있었습니다.
[변경]
deprecated 과목의 최신 과목 반환 로직을
course_equivalences와group_code기준으로 변경했습니다.이제 deprecated된 과목을 직접 대체과목 테이블에서 최신 과목으로 치환하는 것이 아니라,
해당 과목이 속한
group_code를 기준으로 같은 그룹에 속한 현재 과목을 찾아 응답에 내려주도록 변경했습니다.변경된 흐름은 아래와 같습니다.
즉, 기존처럼 학번별 대체과목 매핑 방식이 아니라, 동일/대체과목 그룹 안에서 현재 기준 과목을 찾아 내려주는 방식으로 변경했습니다.
이를 통해 deprecated 과목 응답 보정 로직이 특정 학번의 대체과목 데이터에 종속되지 않고, 동일과목/대체과목 전체 이력을 기준으로 동작하도록 개선했습니다.
3-2. 미이수 과목 판단 로직 변경
[기존]
기존에는 사용자의 이수 과목으로부터 인정 가능한 과목 목록을 만들 때,
course_replacements기반으로 최신 과목을 보정한 뒤 미이수 여부를 판단했습니다.즉, 사용자가 실제로 이수한 과목들의 학수번호만 바로 비교하는 것이 아니라,
이수 과목명들을 기준으로
course_replacements에서 현재 과목의 대체과목들을 추가 조회하여, 그 결과를 포함한 뒤 미이수 과목을 걸러내는 방식이었습니다.흐름으로 보면 아래와 같습니다.
이 방식 역시 학번 단위 대체과목 정보에 의존하기 때문에, 미이수 판단이 부정확할 수 있었습니다.
[변경]
미이수 과목 판단도
course_equivalences기반으로 내려주도록 변경했습니다.사용자의 이수 과목 학수번호 집합을 먼저 만든 뒤,
course_equivalences에서 같은group_code에 속한 과목들의 학수번호를 모두 조회하여 인정 가능한 과목들로 포함해 미이수 과목 판단을 하도록 변경했습니다.변경된 흐름은 아래와 같습니다.
즉, 기존에는 이수 과목명을 기반으로 최신 과목만을 포함시켰다면,
이수한 과목이 속한 동일 과목 전체를 인정 가능한 과목으로 확장하는 방식으로 변경했습니다.
이를 통해 사용자가 어떤 버전의 과목을 이수했는지와 관계없이, 같은
group_code에 포함된 과목이라면 모두 동일/대체과목으로 인정할 수 있습니다.검증
case1) 지정 과목보다 더 이전 버전의 과목을 이수한 경우
해당 과목이 동일한 group_code에 속해 있으면 정상적으로 이수로 인정되는지 검증했습니다.
-> 정상적으로 학점 인정이 되는 것을 확인했습니다.
case2) 수강편람에는 없지만 동일과목 목록에는 존재하는 과거 과목을 이수한 경우
course_equivalences에 동일과목/대체과목으로 등록되어 있고 동일한group_code에 속해있다면 정상적으로 이수로 인정되는지 검증했습니다.-> 기필 영역에서 해당 과목이 인정되었고, 미이수 과목 추천 목록에도 포함되지 않은 것을 확인했습니다.
case3) 미이수 추천 과목 정상 작동하는지
미이수 과목이 정상적으로 필터링되고 추천 과목 목록이 기대한 형태로 내려오는지 검증했습니다.
-> 미이수 과목 추천에서 과거 과목 정보가 아닌, 최신 과목 정보로 반환되는 것을 확인했습니다.
고민 지점과 리뷰 포인트
제가 판단한 방식이 적절한지 의견 부탁드립니다!!
TODO (현재 구현된 방향으로 머지가 된다면)