Frame Activity SDK 소개
Frame Activity SDK는 외부 임베드 콘텐츠에서 발생하는 학습 이벤트(예: 학습 시작, 답안 제출 등)를 IHFB SchoolPT 플랫폼에 전달하고, 학습 데이터의 저장 및 로드를 지원하는 JavaScript 라이브러리입니다.
설치 방법
설치 방법은 다음과 같습니다:
- SDK: 다운로드 페이지에서 최신 버전의 SDK를 다운로드할 수 있습니다.
시작하기
<script type="module">
import { FrameActivitySdk } from './frame-activity-sdk.esm.js';
// SDK 인스턴스 생성
const frameActivitySdk = new FrameActivitySdk();
try {
// 현재 모드 확인
const mode = await frameActivitySdk.getMode();
// 모드에 따른 시나리오 처리
if (mode === 'Edit') {
// Edit 모드에서만 학습 활동 등록
await frameActivitySdk.registerActivities([
{
activityId: '1',
type: 'MULTIPLE_CHOICE',
choices: [
{ label: '서울', isCorrect: true },
{ label: '부산', isCorrect: false },
{ label: '대전', isCorrect: false },
],
},
{
activityId: '2',
type: 'SHORT_ANSWER',
answer: '대한민국',
},
{
activityId: '3',
type: 'INTERACTIVE',
},
{
activityId: '4',
type: 'SPEAKING',
speakingType: 'STT_ONLY',
},
{
activityId: '5',
type: 'SPEAKING',
speakingType: 'PHONICS',
},
{
activityId: '6',
type: 'SPEAKING',
speakingType: 'PRONUNCIATION',
},
]);
}
} catch (error) {
console.error('초기화 실패:', error.message);
}
</script>
공통 사항
- 모든 API는 비동기(async/await) 방식으로 동작합니다.
- SDK는 각 메서드 호출 시 자동으로 초기화됩니다. 별도의 초기화 과정이 필요하지 않습니다.
- 모든 API에서 다음과 같은 공통 에러가 발생할 수 있습니다:
- SDK 초기화 관련 에러
- 네트워크 통신 오류
- serialize 관련 오류 (함수, undefined 등 JSON으로 serialize 할 수 없는 값 포함 시)
타입 정의
학습 활동 타입
interface Choice {
label: string;
isCorrect: boolean;
}
interface MultipleChoiceActivity {
activityId: string;
type: 'MULTIPLE_CHOICE';
choices: Choice[];
}
interface ShortAnswerActivity {
activityId: string;
type: 'SHORT_ANSWER';
answer: string;
}
interface InteractiveActivity {
activityId: string;
type: 'INTERACTIVE';
}
type SpeakingType = 'STT_ONLY' | 'PHONICS' | 'PRONUNCIATION';
interface SpeakingActivity {
activityId: string;
type: 'SPEAKING';
speakingType: SpeakingType;
}
type Activity = MultipleChoiceActivity | ShortAnswerActivity | InteractiveActivity | SpeakingActivity;
제출 데이터 타입
interface MultipleChoiceSubmitData {
activityId: string;
userSelectedIndexes: number[];
}
interface ShortAnswerSubmitData {
activityId: string;
userAnswer: string;
}
interface InteractiveSubmitData {
activityId: string;
}
interface SpeakingSubmitData {
activityId: string;
}
type SubmitData =
| MultipleChoiceSubmitData
| ShortAnswerSubmitData
| InteractiveSubmitData
| SpeakingSubmitData;
저장 데이터 타입
type JsonPrimitive = string | number | boolean | null;
type JsonArray = JsonValue[];
type JsonObject = { [key: string]: JsonValue };
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
interface SaveData extends JsonObject {}
API 상세 명세
getMode()
현재 SDK의 시나리오를 조회합니다.
매개변수: 없음
반환값:
Promise<'Study' | 'TeacherClassResult' | 'TeacherStudentResult' | 'StudentResult' | 'Edit'>
발생 가능한 에러:
"모드 조회 오류"
: 현재 모드를 조회하는 데 실패한 경우"SDK 초기화 오류"
: SDK 연결 초기화에 실패한 경우
사용 예시:
try {
const mode = await frameActivitySdk.getMode();
switch (mode) {
case 'Edit':
// CMS에서 콘텐츠 등록 시 처리
break;
case 'Study':
// 수업 페이지에서 학습 진행 시 처리
break;
case 'TeacherClassResult':
// 선생님이 수업 화면에서 웹 저작물 컨텐츠의 결과를 조회 시 처리
break;
case 'TeacherStudentResult':
// 선생님이 수업 화면에서 학생 1명에 대한 웹저작물 컨텐츠의 결과를 조회 시 처리
break;
case 'StudentResult':
// 학생이 학습 화면에서 자신의 학습 결과를 조회 시 처리
break;
}
} catch (error) {
console.error('모드 조회 실패:', error.message);
}
registerActivities(activities: Activity[])
CMS에서 콘텐츠 등록 시 문제 정보를 등록합니다. Edit 모드에서만 사용해야 합니다.
매개변수:
activities
: 등록할 학습 활동 배열
반환값:
Promise<void>
발생 가능한 에러:
"유효하지 않은 데이터 형식입니다."
: 입력된 데이터가 배열이 아니거나 비어있는 경우"중복된 activityId가 존재합니다."
: 활동 데이터에 중복된 activityId가 있는 경우"유효하지 않은 활동 데이터가 포함되어 있습니다."
: 활동 데이터가 지원하는 타입의 형식에 맞지 않는 경우
사용 예시:
try {
await frameActivitySdk.registerActivities([
{
activityId: '1',
type: 'MULTIPLE_CHOICE',
choices: [
{ label: '서울', isCorrect: true },
{ label: '부산', isCorrect: false },
{ label: '대전', isCorrect: false },
],
},
{
activityId: '2',
type: 'SHORT_ANSWER',
answer: '대한민국',
},
{
activityId: '3',
type: 'INTERACTIVE',
},
{
activityId: '4',
type: 'SPEAKING',
speakingType: 'STT_ONLY',
},
{
activityId: '5',
type: 'SPEAKING',
speakingType: 'PHONICS',
},
{
activityId: '6',
type: 'SPEAKING',
speakingType: 'PRONUNCIATION',
},
]);
} catch (error) {
console.error('활동 등록 실패:', error.message);
}
submit(data: SubmitData[])
학습자의 답안을 제출합니다. Study 모드에서 사용됩니다.
매개변수:
data
: 제출할 답안 데이터 배열
반환값:
Promise<void>
발생 가능한 에러:
"유효하지 않은 답안 데이터입니다."
: 제출 데이터가 올바른 형식이 아닌 경우- 객관식: userSelectedIndexes가 숫자 배열이 아닌 경우
- 주관식: userAnswer가 문자열이 아닌 경우
- 말하기, 활동: 추가 데이터 없이 activityId만 제출
"존재하지 않는 activityId입니다."
: 등록되지 않은 활동에 대한 답안 제출 시
사용 예시:
try {
// 객관식 답안 제출
await frameActivitySdk.submit([
{
activityId: '1',
userSelectedIndexes: [0], // 첫 번째 선택지 선택
},
]);
// 주관식 답안 제출
await frameActivitySdk.submit([
{
activityId: '2',
userAnswer: '대한민국',
},
]);
// 활동, 말하기 타입 제출
await frameActivitySdk.submit([
{
activityId: '3',
},
]);
} catch (error) {
console.error('답안 제출 실패:', error.message);
}
save(key: string, data: SaveData)
현재 학습 상태를 저장합니다. 저장되는 데이터는 LearningActivity 마다 독립적으로 관리됩니다.
매개변수:
key
: 저장할 데이터의 키 (문자열)data
: 저장할 데이터 객체 (serialize 가능한 값)
반환값:
Promise<void>
발생 가능한 에러:
"유효하지 않은 저장 데이터입니다."
: 저장 데이터가 serialize 가능한 형식이 아닌 경우"직렬화할 수 없는 데이터가 포함되어 있습니다."
: 데이터에 함수, undefined 등 JSON으로 serialize 할 수 없는 값이 포함된 경우
사용 예시:
try {
await frameActivitySdk.save('user-progress', {
currentStep: 3,
progress: {
answers: ['답안1', '답안2'],
timestamp: new Date().toISOString(),
},
});
} catch (error) {
console.error('저장 실패:', error.message);
}
load(key: string)
저장된 학습 상태를 불러옵니다.
매개변수:
key
: 불러올 데이터의 키 (문자열, save 시 사용한 key와 동일한 값)
반환값:
Promise<JsonValue | null>
발생 가능한 에러:
"불러오기 오류"
: 저장된 데이터를 불러오는 데 실패한 경우"유효하지 않은 키"
: 존재하지 않는 키로 데이터를 불러오려 할 경우
사용 예시:
try {
const savedData = await frameActivitySdk.load('user-progress');
if (savedData) {
// 저장된 데이터 처리
console.log('불러온 데이터:', savedData);
}
} catch (error) {
console.error('불러오기 실패:', error.message);
}
save/load에 활용되는 데이터 저장 및 접근 구조
데이터 저장 흐름
예시) 선생님 페이지에서의 save/load 활용
선생님이 수업 화면에서 학생 1명에 대한 웹저작물 컨텐츠의 결과를 조회할 때, 다음과 같은 시나리오로 save/load를 활용할 수 있습니다:
주의사항
⚠️ 중요 안내
activityId
는 문자열 형태의 고유 식별자로, 각 문제를 특정할 수 있는 유일한 값이어야 합니다.save()
와load()
의key
는 문자열 형태이며 자유롭게 지정할 수 있습니다.save()
를 통해 저장되는 데이터는 LearningActivity 마다 독립적으로 관리됩니다. 즉, 각 문제마다 개별적인 학습 상태가 유지됩니다.- 각 모드는 다음과 같은 시나리오에서 사용됩니다:
Edit
: CMS에서 콘텐츠를 등록할 때Study
: 수업 페이지에서 학습을 진행할 때TeacherClassResult
: 선생님이 수업 화면에서 웹 저작물 컨텐츠의 전체 결과를 조회할 때TeacherStudentResult
: 선생님이 수업 화면에서 학생 1명에 대한 웹저작물 컨텐츠의 결과를 조회할 때StudentResult
: 학생이 학습 화면에서 자신의 학습 결과를 조회할 때
save/load와 플랫폼 연동 관련 중요 안내
🚨 중요
save()
와load()
를 통해 저장되는 데이터는 밀당 AIDT 플랫폼의 학습 데이터로 활용될 수 없습니다. 이는 웹 저작물 컨텐츠 내부에서만 사용되는 독립적인 저장소입니다.
save()
/load()
데이터는 플랫폼의 학습 결과나 평가 데이터와 완전히 분리되어 있습니다.- 플랫폼에서는 이 데이터를 학습 이력이나 평가 결과로 활용할 수 없습니다.
- 웹 저작물의 학습 결과를 플랫폼에 반영하려면 반드시
submit()
API를 사용해야 합니다.활용 가능한 용도:
- 학습자의 현재 진행 상태 임시 저장
- 게임 스코어나 설정값 저장
- 사용자 인터페이스 상태 관리
- 미완료된 답안의 임시 저장
예시:
// 학습 진행 상태 임시 저장 (플랫폼과 무관) await sdk.save('progress', { currentStep: 3 }); // 실제 학습 결과를 플랫폼에 제출 await sdk.submit([ { activityId: '1', userSelectedIndexes: [0], }, ]);
음성 파일 업로드
음성 파일을 업로드하고 다운로드 가능한 URL을 받습니다.
uploadFileWithBlob(params: UploadFileRequest)
매개변수:
params
:UploadFileRequest
객체interface UploadFileRequest { blob: Blob; // 업로드할 파일 데이터 fileName: string; // 파일 이름 activityId?: string; // 연결할 활동 ID (선택사항) }
반환값:
Promise<UploadFileResponse>
interface UploadFileResponse { url: string; // 다운로드 가능한 URL }
발생 가능한 에러:
"파일 업로드 오류"
: 파일 업로드 중 오류가 발생한 경우"SDK 초기화 오류"
: SDK 연결 초기화에 실패한 경우
사용 예시:
try {
const file = new Blob(['Hello, World!'], { type: 'audio/wav' });
const response = await frameActivitySdk.uploadFileWithBlob({
blob: file,
fileName: 'audio.wav',
activityId: '1',
});
console.log('업로드된 파일 URL:', response.url);
} catch (error) {
console.error('파일 업로드 실패:', error.message);
}
⚠️ 주의사항
- 아래 발화평가 기능을 사용하기 위해서는 반드시 음성 파일 업로드 기능을 사용해야 합니다.
발화평가
발화평가 기능을 통해 음성 인식(STT), 발음 평가, 파닉스 평가 등을 수행할 수 있습니다.
requestSpeechEvaluation(data: SpeechEvaluationRequestData)
발화평가를 요청합니다.
매개변수:
data
:SpeechEvaluationRequestData
객체interface SpeechEvaluationRequestData { activityId: string; speakingType: 'STT_ONLY' | 'PHONICS' | 'PRONUNCIATION'; referenceText: string; // 발화평가 기준 텍스트 url: string; // 학생이 녹음한 오디오 URL }
반환값:
Promise<string>
: 발화평가 요청 ID (sttId)
발생 가능한 에러:
"필수 파라미터가 누락되었습니다."
: 필수 매개변수가 누락된 경우"발화평가 요청 오류"
: 발화평가 요청 중 오류가 발생한 경우
사용 예시:
try {
const sttId = await frameActivitySdk.requestSpeechEvaluation({
activityId: '1',
speakingType: 'PRONUNCIATION',
referenceText: 'Hello, how are you?',
url: 'https://example.com/audio.mp3',
});
console.log('발화평가 요청 ID:', sttId);
} catch (error) {
console.error('발화평가 요청 실패:', error.message);
}
getSpeechEvaluationResult(sttId: string)
발화평가 결과를 조회합니다.
매개변수:
sttId
: 발화평가 요청 시 받은 ID
반환값:
Promise<SpeechEvaluationResult>
: 발화평가 결과는speakingType
에 따라 다음과 같은 구조를 가집니다:
- STT_ONLY (음성을 텍스트로 변환):
{
"sttId": "stt_001",
"speakingType": "STT_ONLY",
"status": "COMPLETED",
"url": "https://example.com/audio.mp3",
"sttResult": {
"result": "안녕하세요", // 인식된 텍스트
"sttRecog": null
}
}
- PHONICS (파닉스 평가):
{
"sttId": "stt_002",
"speakingType": "PHONICS",
"status": "COMPLETED",
"url": "https://example.com/audio.mp3",
"sttResult": {
"result": "cat",
"sttRecog": null,
"sttProneval": {
"sentenceLevel": {
"text": "cat",
"startTimeInSec": 0.5,
"endTimeInSec": 1.2,
"intonation": {
"data": [1, 2, 3],
"length": 3,
"max": 3,
"min": 1
},
"proficiencyScore": [
{
"name": "acoustic",
"score": 85,
"max": 100,
"min": 0
}
]
},
"wordLevel": [
{
"text": "cat",
"index": 0,
"startTimeInSec": 0.5,
"endTimeInSec": 1.2,
"proficiencyScore": [
{
"name": "acoustic",
"score": 85,
"max": 100,
"min": 0
}
],
"colorOfLetter": {
"c": "GREEN",
"a": "GREEN",
"t": "ORANGE"
}
}
],
"phoneLevel": [
[
{
"text": "c",
"ipa": "k",
"startTimeInSec": 0.5,
"endTimeInSec": 0.7,
"score": 90,
"windex": 0,
"pindex": 0
}
]
],
"phonicsComment": ["good"]
}
}
}
- PRONUNCIATION (발음 평가):
{
"sttId": "stt_003",
"speakingType": "PRONUNCIATION",
"status": "COMPLETED",
"url": "https://example.com/audio.mp3",
"sttResult": {
"result": "Hello, how are you?",
"sttRecog": null,
"sttProneval": {
"alignment": {
"sttAlignment": "hello how are you"
},
"evaluationType": "pronunciation",
"similarity": "0.85",
"threshold": "0.7",
"pronunciation": [
{
"sentenceLevel": {
"text": "Hello, how are you?",
"startTimeInSec": 0.0,
"endTimeInSec": 2.5,
"intonation": {
"data": [1, 2, 3, 2],
"length": 4,
"max": 3,
"min": 1
},
"proficiencyScore": [
{
"name": "EN_HOLISTIC",
"score": 85,
"max": 100,
"min": 0
}
]
},
"wordLevel": [
{
"text": "hello",
"index": 0,
"startTimeInSec": 0.0,
"endTimeInSec": 0.8,
"proficiencyScore": [
{
"name": "EN_SEGMENT",
"score": 90,
"max": 100,
"min": 0
}
],
"colorOfLetter": {
"h": "GREEN",
"e": "GREEN",
"l": "GREEN",
"l": "GREEN",
"o": "GREEN"
}
}
]
}
]
}
}
}
주요 필드 설명:
status
: 평가 상태 ('COMPLETED'
또는'FAIL'
)proficiencyScore
: 각종 평가 점수acoustic
: 음향학적 점수EN_HOLISTIC
: 전반적인 영어 발음 점수EN_SEGMENT
: 개별 음소 발음 점수EN_INTONATION
: 억양 점수EN_RATE
: 발화 속도 점수EN_PITCH
: 음높이 점수
colorOfLetter
: 각 글자별 발음 정확도'GREEN'
: 정확한 발음'ORANGE'
: 부분적으로 정확한 발음'RED'
: 부정확한 발음
발생 가능한 에러:
"유효하지 않은 sttId입니다."
: 잘못된 sttId가 제공된 경우"발화평가 결과 조회 오류"
: 결과 조회 중 오류가 발생한 경우
사용 예시:
try {
const result = await frameActivitySdk.getSpeechEvaluationResult(sttId);
if (result.status === 'COMPLETED') {
switch (result.speakingType) {
case 'STT_ONLY':
console.log('인식된 텍스트:', result.sttResult?.result);
break;
case 'PHONICS':
console.log('파닉스 평가 결과:', result.sttResult?.sttProneval);
break;
case 'PRONUNCIATION':
console.log('발음 평가 결과:', result.sttResult?.sttProneval);
break;
}
} else {
console.log('평가 실패');
}
} catch (error) {
console.error('결과 조회 실패:', error.message);
}
setHeight(height: number)
iframe의 높이를 픽셀 단위로 설정합니다. 기본값은 16:9 비율이며, height 값이 0일 경우 기본 비율(16:9)로 복구됩니다.
매개변수:
height
: 높이 값 (픽셀 단위, 0 이상의 값)
반환값:
Promise<boolean>
: 설정 성공 여부
발생 가능한 에러:
"높이는 0 이상의 값이여야 합니다."
: 음수 값이 제공된 경우"높이 설정 오류"
: 높이 설정 중 오류가 발생한 경우
사용 예시:
try {
// 높이를 500px로 설정
await frameActivitySdk.setHeight(500);
// 기본 16:9 비율로 복구
await frameActivitySdk.setHeight(0);
} catch (error) {
console.error('높이 설정 실패:', error.message);
}
⚠️ 주의사항
- height 값은 0 이상의 값이어야 합니다.
- 전달하는 height 값이 0인 경우 기본 16:9 비율로 복구됩니다.
- iframe의 width는 100%로 고정되어 있어 width 값은 변경할 수 없습니다.
- 세로로 긴 컨텐츠의 경우 height 값을 증가시켜 컨텐츠가 잘리지 않도록 조정할 수 있습니다.