클래스 관리/스케줄링 — 코드레벨 동작 흐름 P2코드레벨

마지막 업데이트 2026-08-04

작성일: 2026-06-14 갱신일: 2026-08-04 (조회 필터 · 예정 수업 변경 · 감사 헤더) 대상: 개발자 — 수업/그룹/스케줄 관리 파악 핵심 파일: app/main/class-mgmt, api/class-mgmt/*, lib/class-group-utils.ts
💬 대화로 먼저 이해하기 — "학원 교무실 비유" (비개발자·처음 읽는 사람용)
Q이 영역은 무엇을 관리하나요?
A학원 교무실 업무예요. 치료사-아동 배정, 수업(class) 생성, 시간대별 그룹(반) 편성, 예약 수업 조회까지 — 수업이 시작되기 전의 행정 처리를 담당하는 관리자 영역입니다.
Q학생 명부 같은 실제 데이터는 이 서버에 있나요?
A대부분 아니에요. 원장 장부(실제 데이터·비즈니스 로직)는 본사(외부 ppi-api)에 있고, 여기 API는 창구 직원(BFF 프록시)처럼 서류를 전달하고 회신을 받아 올 뿐이에요. 그래서 로직 수정이 필요하면 ppi-api 쪽일 수 있습니다.
Q그룹 편성이 왜 특히 중요하죠?
A교무실에서 정한 반 배정표(groupId)가 교실 배정(SFU room)과 관찰 카메라 화면 구성(모니터 카드뷰·그룹 모니터링)까지 그대로 전파되기 때문이에요. 여기서 바꾸면 멀리 떨어진 화면들이 함께 영향을 받아요.
Q코드를 볼 때 조심할 부분은요?
A수업 길이가 +20분으로 하드코딩되어 있고, scheduled-lessons 응답 구조는 모니터 대시보드가 그대로 소비해요. 시간대·그룹 변환의 중심 파일은 lib/class-group-utils.ts이고, 나머지는 아래 "함정 · 주의" 섹션에 정리돼 있습니다.

개요 · 범위

치료사-아동 배정, 수업(class) 생성/관리, 그룹(시간대별 다중 세션) 편성, 예약 수업(scheduled-lessons) 조회를 다루는 관리자 영역. 대부분 Next API가 BFF로 외부 ppi-api를 프록시하고, 클라이언트는 시간대/그룹 구조로 변환해 표시한다.

페이지 · API 맵

영역경로
클래스 관리/main/class-mgmt
스케줄/main/schedule
커리큘럼(학습플랜)/main/curriculum
수업 CRUDapi/class-mgmt/classes, classes/[childId]/[date](+status/videos), batch-delete, counts
그룹groups, groups/[groupId]/{assign,unassign,members,check-unassign}, auto-assign
아동children/[childId](+class-type, auth/convert·expire), children/search
배정 현황assignment-matrix, assignment-drilldown, members/available
예약 수업api/scheduled-lessons (모니터 대시보드 #13가 소비)

시간대·그룹 변환 class-group-utils.ts: groupClassesByTimeslot L19

평면적인 class 목록을 시간대(timeslot) → 그룹/1:1 2단 구조로 변환해 UI에 표시한다.

TimeslotSection { timeslot, endTime(+20분), groups[], individualClasses[] }
각 class를 time으로 묶고, groupId 있으면 그룹 섹션·없으면 1:1로 분류.
그룹 = 모니터 카드뷰 단위: 여기서 만든 groupId가 SFU room의 groupId(JOIN_ROOM #2), 모니터 그룹 모니터링(#20)과 연결된다.

그룹 운영 · class-type

2026-07~08 변경 — 조회 필터 · 예정 수업 변경 · 감사 헤더

이 영역은 7월 말~8월 초에 운영자 조회/편집 UX 중심으로 5건이 들어왔다. BFF 프록시 구조 자체는 그대로다.

오늘 수업 — 날짜 필터 + 예정 수업 변경 today-class-list.tsx · #939

회귀 테스트가 걸려 있다: onDateTimeChange는 모달 내부 useEffect dependency이므로 handleModifyDateTimeChangeuseCallback([fetchAvailableMembers])로 고정해야 한다. 인라인 함수로 되돌리면 모달이 매 렌더마다 재조회하며 무한 루프에 가까워진다. today-class-list.regression.test.ts가 소스 문자열로 이 형태를 검사한다 (pnpm exec tsx apps/web/components/sections/today-class-list.regression.test.ts).

중단 관리 — 전체/날짜 필터 + 컬럼 재구성 stopped-class-list.tsx

아동 선택 검색 — 동명이인 구분 child-search-input.tsx · lib/child-display.ts · #933

getChildDisplayName(child, candidates) — 후보 목록에 같은 이름이 2명 이상일 때만 이름 (생년월일)로 표기하고, 유일하면 이름만 쓴다. 생년월일이 없으면 이름만.

오늘 수업·중단 관리·전체 수업·수동 바우처 모달이 이 입력을 공유하므로, 표시 규칙 변경은 4개 화면에 동시 반영된다.

수업 일괄 변경 감사 헤더 api/class-mgmt/groups/audit-headers.ts

buildGroupUnassignAuditHeaders(request) → ppi-api로 전달되는 헤더 3종
X-PPI-Bulk-Operation-ID(UUID 형식일 때만 통과) · X-PPI-UI-Surface: today_class_list · X-PPI-UI-Action: bulk_auto_to_individual | single_group_unassign

고정 시간표 저장 충돌 메시지 lib/fix-rule-save-result.ts · fix-rule-conflict-modal.tsx

함정 · 주의

파일 · 라인 레퍼런스

파일역할
app/main/class-mgmt/page.tsx, schedule, curriculum관리 페이지
lib/class-group-utils.ts시간대·그룹 변환
api/class-mgmt/{classes,groups,children,assignment-*}/...BFF 프록시
api/scheduled-lessons/route.ts예약 수업(모니터 소비)
components/sections/today-class-list.tsx (+regression.test.ts)오늘 수업 목록 · 날짜 필터 · 예정 수업 변경
components/sections/stopped-class-list.tsx중단 관리 목록 · 전체/날짜 필터
components/ui/child-search-input.tsx, lib/child-display.ts아동 검색 · 동명이인 생년월일 표기
api/class-mgmt/groups/audit-headers.ts, lib/bulk-group-convert.ts일괄 변경 감사 헤더
lib/fix-rule-save-result.ts, types/db/member-fix-rule.types.ts고정 시간표 저장 결과·충돌 메시지

관련 문서