학습요소를 복제해도 할 일 목록에는 없을 수 있다

LearningX의 Blueprint 동기화는 템플릿 과목의 학습요소를 자식 과목으로 복제한다. 그런데 복제한 AttendanceItem이 MongoDB에 있어도, 대시보드가 읽는 파생 데이터가 갱신되지 않으면 할 일 목록에 강의 영상이 나타나지 않는다. 원본 동기화가 끝났다는 상태와 조회 화면에 필요한 행이 준비됐다는 상태는 달랐다.

변경은 원본 복제 뒤에 기존 CourseComponent 동기화를 연결했다. 이후 그 호출을 자식 과목별로 나눴다. 화면을 다시 조회하게 만드는 것만으로는 해결할 수 없는 문제였다. 다시 읽을 조회용 데이터부터 만들어야 했다.

새로운 화면 갱신 경로 대신 기존 동기화 경로를 이었다

  1. 원본 복제

    자식 과목의 AttendanceItem과 이용기간을 동기화한다.

  2. 조회용 학습요소 동기화

    CourseComponentHelper가 원본을 xn_course_components에 반영한다.

  3. 변경 이벤트

    생성·삭제·갱신한 컴포넌트를 CourseComponentsSynchronized에 담는다.

  4. 할 일 반영

    등록된 리스너가 변경 컴포넌트로 xn_mypage_todo_items를 갱신한다.

컴포넌트 동기화는 course_id:item_type:item_id를 키로 원본과 기존 행을 대조한다. 새 행 생성뿐 아니라 사라진 항목의 삭제, 바뀐 항목의 갱신, soft delete된 행의 복구도 같은 경로가 맡는다. Blueprint 처리기에서 할 일 행을 직접 만들면 이 규칙을 다른 위치에 다시 구현해야 한다. 기존 동기화 함수를 호출하면 원본을 조회용 학습요소로 바꾸는 규칙과 변경 이벤트를 함께 사용할 수 있다.

호출에는 dashboard.MypageTodoItem.real_time === true 조건이 붙었다. 대시보드 V2가 설치되지 않은 환경에는 해당 테이블이 없을 수 있기 때문이다. 같은 설정이 이벤트 리스너 등록도 제어한다. 원본 복제는 필요한 환경에서 계속 수행하고, 선택 기능의 파생 테이블 접근은 그 기능이 활성화됐을 때만 시작한다.

다음 과목을 살리는 대신 전체 성공의 뜻이 좁아졌다

최종 변경은 자식 과목 목록 전체를 한 번에 넘기는 대신, 과목 하나를 담은 Collection으로 동기화 함수를 호출한다. 과목별 예외는 기록하고 다음 과목을 처리하도록 했다. 아래는 설정 조건 안의 반복문과 마지막 상태 저장을 단순화한 코드다.

$childCourses->each(function ($childCourseId) {
    try {
        $helper->syncCourseComponentsInCourseIds(collect([$childCourseId]));
    } catch (\Throwable $error) {
        Log::error('component sync failed', ['course_id' => $childCourseId]);
    }
});
// 과목별 파생 동기화가 일부 실패해도 이 상태 저장으로 진행한다.
$jobStatus->update(['progress' => 100, 'status' => STATUS_SUCCEEDED]);

한 과목의 실패 때문에 뒤의 모든 과목을 건너뛰는 것을 줄일 수 있다. 하지만 잡의 마지막 상태는 여전히 STATUS_SUCCEEDED, 진행률은 100이 된다. 이 성공 상태는 “모든 자식 과목의 할 일 데이터가 일치한다”는 증명이 아니다. 원본 단계가 끝난 뒤 파생 동기화를 시도했고, 일부 실패를 허용한 채 처리기를 끝냈다는 상태다.

실패한 과목을 별도 목록으로 저장해 자동 재처리하는 기능이나 여러 저장소를 묶은 트랜잭션은 이 변경에 없다. 기존 컴포넌트 도우미도 일부 행의 쓰기 실패를 기록하고 넘어갈 수 있다. 과목별 catch만으로 파생 행 전체의 성공을 판정할 수 없는 이유다.

빠진 갱신 연결을 복구한 것과 일치 보장은 구분해야 한다

일반 컴포넌트 테스트는 생성·삭제·복구 같은 도우미 동작을 검사한다. 그것을 Blueprint 진입부터 설정 분기, 과목별 오류, 마지막 잡 상태까지 한 번에 검증한 결과로 읽을 수는 없다. 특히 “한 자식 과목의 파생 갱신은 실패했지만 잡은 성공으로 끝나는 경우”는 별도의 회귀 시나리오가 필요하다.

이 변경이 메운 구간은 원본 복제와 대시보드 파생 갱신 사이의 누락된 호출이다. 운영 상태를 해석할 때는 원본 완료, 파생 갱신 시도, 모든 대상의 갱신 완료를 나눠야 한다. 출결 이벤트의 변화 판정이 불필요한 신호를 줄이는 문제라면, 여기서는 필요한 파생 갱신 신호가 시작되지 않던 경로를 연결한 것이다.