Activity 시스템 (템플릿·step·jumpConfig·auto-finish) — 코드레벨 동작 흐름 P1코드레벨

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

Activity 시스템 (템플릿·step·jumpConfig·auto-fi…: 입력: 개요 · 범위, 주요 처리 단계: 예비 콘텐츠(isSpare) 스킵 spare-content-utils.ts…, 결과: 파일 · 라인 레퍼런스 흐름
동작 흐름 요약
  1. 입력: 개요 · 범위
  2. 주요 처리 단계: 예비 콘텐츠(isSpare) 스킵 spare-content-utils.ts…
  3. 결과: 파일 · 라인 레퍼런스
작성일: 2026-06-14 대상: 개발자 — 아동 대화 활동 구성·전환 파악 핵심 파일: types/db/activity.types.ts, lib/{activity-utils,spare-content-utils,lesson-utils}.ts, hooks/use-session-auto-finish.ts
💬 대화로 먼저 이해하기 — "연극 대본·무대감독 비유" (비개발자·처음 읽는 사람용)
Q수업 하나는 안에서 어떻게 짜여 있나요?
A연극 공연과 대본을 떠올리면 돼요. 수업이 공연, 활동(Activity)이 막, 스텝(Step)이 장이에요. 각각 priority라는 순서 번호가 붙어 그 차례대로 진행되고, 공연마다 대본 원본(회기 템플릿)을 복사해서 씁니다.
Q한 활동이 끝나면 다음 활동은 어떻게 정해지나요?
A대본 끝에 적힌 "다음 장면 지시"(jumpConfig)를 따라요. 기본(default)은 정해진 다음 막으로 가고, 조건부(conditional)는 아동의 말에 특정 키워드가 전부 들어 있으면 그 갈래의 막으로 건너뜁니다. 지시가 없으면 그냥 번호 순서대로 가요.
Q"자동으로 넘어간다"(auto-finish)는 건 누가 신호를 주는 건가요?
A무대감독이 약속된 대사를 듣고 큐를 주는 것과 같아요. 핑퐁이(AI)가 종료 문구를 말하면 바로 다음으로 넘어가거나(after-pingpong-speech), 아동의 대답을 기다렸다가 넘어가요(after-child-speech, 무응답이면 5초 뒤). 호스트가 타이핑 중이면 큐를 잠시 보류합니다.
Q수정할 때 조심할 곳은요?
A자동 전환 스위치가 두 개(세션 전역 + 스텝별)라 둘 다 켜져야 하고, 활동 이동이 인덱스 기반이라 순서 변경·삭제 시 어긋날 수 있어요. 자세한 건 하단 "함정 · 주의"와 spare-content-utils.ts·use-session-auto-finish.ts를 보세요.

개요 · 범위

한 수업은 여러 Activity(활동)로 구성되고, 각 활동은 여러 Step(스텝)을 가진다. 활동/스텝은 priority로 순서가 정해지며, 활동 종료 시 jumpConfig로 다음 활동을 정한다(기본/조건부). 스텝의 autoFinish 설정으로 발화 종료/특정 문구 감지 시 자동 전환된다.

구성: 데이터 모델(activity.types) + 우선순위 유틸(activity-utils) + 활동 이동·예비 스킵(spare-content-utils) + 템플릿 복사(lesson-utils) + 자동 종료(use-session-auto-finish, 세션 매니저 #1과 연동).

데이터 모델 activity.types.ts

Activity

Step

필드의미
typeprompt | hybrid | resource
actionType (hybrid)talk | resource
resTypescreen-share | embedded (+ resName)
startMent스텝 시작 멘트(세션 매니저 startMent로 전달)
avatarInitialState아바타 초기 상태(talking/idle 등)
autoFinishEnabled / autoFinishType / autoFinishPhrases자동 종료 설정(아래)

우선순위 기반 시퀀싱 activity-utils.ts

활동 이동 (jumpConfig) spare-content-utils.ts: findNextActivityByJumpConfig L167

// default 모드: 고정 다음 활동
if (jumpMode === "default")  → defaultNextActivityIndex 반환(범위 검증)

// conditional 모드: 키워드 매칭으로 분기
if (jumpMode === "conditional" && matchedConditionKeywords)
  for cj of conditionalJumps:
    normalize 후 cj.keywords 전부가 입력에 포함되면(every)
      → cj.targetActivityIndex 반환

예비 콘텐츠(isSpare) 스킵 spare-content-utils.ts: findNextStepSkippingSpare

isSpare 활동은 일반 순차 진행에서 건너뛰는 예비물이다. findNextStepSkippingSpare가 다음 후보를 찾을 때 spare를 스킵하며, 후보 소진 시 null 반환. (jumpConfig로 명시적으로 지목되면 사용 가능)

회기 템플릿 → 활동 복사 lesson-utils.ts L52–73

활동은 회기 템플릿에서 복사 생성된다. 복사 시 템플릿의 jumpConfig를 활동 인덱스에 맞게 재매핑한다(default의 defaultNextActivityIndex, conditional의 각 targetActivityIndex). 잘못된 인덱스는 필터링.

자동 종료 (auto-finish) hooks/use-session-auto-finish.ts

스텝의 autoFinishPhrases로 종료 문구를 감지하면 자동으로 다음으로 넘어간다. 세션 매니저(#1)의 전사 이벤트가 이 훅을 구동한다. autoTransitionEnabled(enabled) + config.enabled 둘 다 켜져야 동작.

문구 매칭 checkPhrase L49

// 각 condition은 콤마 구분 단어들. normalize(따옴표/공백 제거) 후
matchedCount = words.filter(w => text.includes(w)).length
threshold = words.length >= 3 ? 2 : words.length   // 3단어↑면 2개만 맞아도 OK
matchedCount >= threshold  →  매칭

두 가지 타입

type동작
after-pingpong-speech핑퐁이 발화 전사에서 문구 매칭(handlePingpongSpeech) → autoFinishSilence=true(입력 차단) → 1초 후 onAutoFinish
after-child-speech핑퐁이가 문구 발화 후 아동 응답을 기다림. 아동 발화 종료(handleChildSpeechEnd) 시 즉시 종료, 무응답이면 5초 타임아웃 종료. 호스트 타이핑 중이면 타이머 보류(handleHostTypingStatusChange)

onAutoFinish(matchedCondition, triggerText)는 매칭된 조건/문구를 함께 넘겨 jumpConfig conditional 분기의 키워드로 쓰일 수 있다. (세션 매니저 #1의 handleAutoFinish가 after-child-speech 시 cancelResponse 후 콜백)

함정 · 주의

파일 · 라인 레퍼런스

파일/심볼역할
types/db/activity.types.tsActivity/Step/jumpConfig 모델
lib/activity-utils.ts우선순위·기본 스텝·검증
lib/spare-content-utils.ts (findNextActivityByJumpConfig L167)활동 이동·예비 스킵
lib/lesson-utils.ts (L52–73)템플릿→활동 jumpConfig 재매핑
hooks/use-session-auto-finish.ts자동 종료(문구 매칭·타이머)
hooks/use-activity-templates.ts템플릿 CRUD

관련 문서