Cocos Creator 세션 리플레이(실험적)¶
이 문서는 Cocos Creator Session Replay 초기화, Camera 선택, 성능 파라미터, 터치 프라이버시 및 노드 프라이버시 규칙을 설명합니다.
실험적 기능
Cocos Creator 세션 리플레이는 현재 실험적 기능이며, API, 플랫폼 호환성 및 리플레이 효과는 이후 버전에서 변경될 수 있습니다. 프로덕션에 적용하기 전에 먼저 테스트 환경에서 프라이버시, 성능, 리플레이 완전성을 평가하는 것이 좋습니다.
사전 조건¶
Session Replay의 요구 사항:
- Android 또는 iOS 네이티브 빌드;
- RUM 초기화 완료;
- 현재 유효한 RUM View 존재;
- 현재 SDK 버전 조합의 Cocos SDK 및 네이티브 의존성 사용.
유효한 RUM Context가 없으면 SDK는 현재 프레임을 건너뛰며 RUM과 별개로 리플레이 데이터를 생성하지 않습니다. autoTrack.scenes를 활성화하거나 프레임 캡처 전에 guanceSdk.rum.startView()를 직접 호출하는 것이 좋습니다.
독립형 Replay 패키지 설치¶
Cocos SDK 0.1.0-alpha.6부터 세션 리플레이는 @cloudcare/cocos-session-replay로 별도 제공됩니다. Cocos 프로젝트 루트 디렉터리에 정확히 동일한 버전의 두 패키지를 설치하세요:
npm install @cloudcare/cocos-sdk@0.1.0-alpha.6 @cloudcare/cocos-session-replay@0.1.0-alpha.6
npx --no-install guance-cocos install --project . --replay
Creator를 다시 열고 네이티브 프로젝트를 생성 및 컴파일하세요. CocoaPods를 사용하는 iOS 프로젝트는 pod install도 실행해야 합니다. SPM 구성 및 스위치 유지 규칙은 앱 연동을 참조하세요.
기본 패키지에는 Replay API, 프레임 캡처 구현 또는 Replay 네이티브 의존성이 포함되어 있지 않습니다. 아래 예시는 결합된 인스턴스를 observability.ts에서 내보낸 guanceSdk로 저장하며, 다른 모듈은 이 인스턴스를 재사용합니다.
초기화¶
설명
이 페이지의 코드 예시에서 ...은 sdk 기본 구성(예: datakitUrl)이 생략되었음을 의미합니다. 먼저 SDK 초기화를 참조하여 공통 구성을 완료하세요. 이 페이지에서는 Session Replay 관련 구성만 설명합니다.
import { guanceSdk as baseSdk } from '@cloudcare/cocos-sdk/creator3';
import { withSessionReplay } from '@cloudcare/cocos-session-replay/creator3';
export const guanceSdk = withSessionReplay(baseSdk);
guanceSdk.start({
...,
rum: {
androidAppId: 'android-rum-app-id',
iosAppId: 'ios-rum-app-id',
},
replay: {
sampleRate: 1,
sessionOnErrorSampleRate: 0,
captureFps: 2,
maxImageDimension: 720,
imagePolicy: {
quality: 'medium',
},
touchPrivacy: 'show',
},
autoTrack: {
scenes: true,
},
});
Creator 2는 두 패키지의 임포트 경로를 모두 /creator2로 변경하세요. 결합은 SDK 초기화 또는 Hybrid attach() 이전에 이루어져야 합니다. 이후의 .replay, start({ replay }), attach({ replay })는 모두 결합된 인스턴스에서 호출됩니다.
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
sampleRate |
number |
아니요 | Session Replay 세션 샘플링 비율, 범위 0–1 |
sessionOnErrorSampleRate |
number |
아니요 | 오류 세션 보충 샘플링 비율, 범위 0–1 |
captureFps |
number |
아니요 | 초당 캡처 프레임 수, 정수 1–5만 허용, 기본값 1 |
maxImageDimension |
number |
아니요 | 캡처 프레임의 최장 변 픽셀, 범위 1–2048, 기본값 720 |
imagePolicy |
FTReplayImagePolicy |
아니요 | 이미지 인코딩, 단일 프레임 크기 및 분당 트래픽 정책. 명시적으로 전달하면 이미지 트래픽 제어가 활성화됩니다. |
touchPrivacy |
show / hide |
아니요 | 터치 데이터 프라이버시 수준. show는 터치를 누른 위치와 뗀 위치를 기록하고, hide는 터치 위치를 기록하지 않습니다. 기본값은 hide입니다. |
잘못된 샘플링 비율, FPS, 이미지 크기 또는 트래픽 정책은 TypeScript 레이어에서 TypeError 또는 RangeError를 발생시킵니다.
이미지 트래픽 정책¶
imagePolicy는 녹화된 세션에서 생성되는 이미지 Resource 트래픽을 제한하는 데 사용됩니다. 품질 등급만 구성하면 해당 프리셋을 사용할 수 있습니다:
| 등급 | 기본 최장 변 | 인코딩 품질 | 일반 단일 프레임 상한 | 이미지 Resource 60초 롤링 예산 |
|---|---|---|---|---|
low |
480 px | 0.35 | 20 KiB | 0.6 MiB |
medium |
720 px | 0.45 | 40 KiB | 1.5 MiB |
high |
960 px | 0.60 | 80 KiB | 4 MiB |
imagePolicy는 다음 필드를 지원합니다:
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
quality |
low / medium / high |
medium |
품질 프리셋. captureFps는 변경하지 않습니다. |
maxFrameBytes |
number |
현재 등급 프리셋 | 일반 이미지 Resource의 최대 인코딩 크기, 범위 1 KiB–1 MiB |
maxBytesPerMinute |
number |
현재 등급 프리셋 | 이미지 Resource의 60초 롤링 예산, 범위 16 KiB–64 MiB, maxFrameBytes보다 작을 수 없습니다. |
adaptiveCapture |
boolean |
true |
예산 사용량에 따라 유효 이미지 출력 빈도, 품질 및 크기를 적응형으로 낮출지 여부 |
명시적으로 설정한 maxImageDimension, maxFrameBytes, maxBytesPerMinute는 등급 프리셋을 덮어씁니다. captureFps는 항상 별도로 구성됩니다. 2 fps 이상으로 높이면 예산 컨트롤러가 실제 출력을 제한하므로 트래픽이 명목 FPS에 따라 지속적으로 선형 증가하지 않습니다.
adaptiveCapture를 활성화하면 SDK는 최근 60초 동안 수락된 이미지 크기에 따라 캡처를 조정합니다:
| 예산 사용량 | 동작 |
|---|---|
| 75% 미만 | 구성된 captureFps, 품질 및 크기로 캡처 |
| 75% 도달 | 유효 이미지 출력 빈도가 약 0.5 fps로 감소 |
| 90% 도달 | 빈도 감소에 더해 인코딩 품질과 이미지 크기를 추가로 낮춤 |
| 100% 도달 | 롤링 창이 예산을 해제할 때까지 이미지 readback을 일시 중지. 쓰기 대기 중인 터치 기록은 계속 저장됩니다. |
새 View 또는 가로/세로 화면 전환의 첫 프레임은 최대 100 KiB의 별도 버스트 할당량을 사용할 수 있어 페이지 전환 후 가능한 한 빨리 전체 화면이 표시됩니다. 품질이 자주 흔들리는 것을 방지하기 위해 예산이 감소한 후에는 낮은 복원 임계값을 사용하여 점진적으로 정상 캡처 상태로 돌아갑니다.
iOS는 JPEG, Android는 WebP 인코딩을 사용하며, 실제 인코딩 크기를 기준으로 단일 프레임 제한과 분 예산을 계산합니다. 인코딩 결과가 maxFrameBytes를 초과하면 먼저 품질을 낮춘 다음 크기를 줄이며, 그래도 초과하면 Resource에 기록하지 않습니다.
Camera 선택¶
기본적으로 현재 씬에서 찾은 첫 번째 Camera를 사용합니다. 다중 Camera 프로젝트는 리플레이할 Camera를 명시적으로 지정해야 합니다:
import { guanceSdk } from './observability';
export function selectReplayCamera(camera: unknown): void {
guanceSdk.setReplayCamera(camera);
}
setReplayCamera()는 호출할 때마다 Camera 하나만 저장합니다. 여러 번 호출하면 마지막에 전달된 Camera가 이전 설정을 덮어쓰며, SDK는 여러 Camera를 동시에 캡처하거나 합성하지 않습니다. 호출 시점에 이전 프레임이 캡처 중이면 새 Camera는 다음 캡처부터 적용됩니다.
기본 Camera를 전환한 후에는 setReplayCamera()를 다시 호출해야 합니다. 씬에 사용 가능한 Camera가 없으면 현재 프레임은 건너뜁니다.
터치 프라이버시¶
touchPrivacy는 Session Replay가 터치 위치를 기록할지 여부를 제어합니다:
| 모드 | 동작 |
|---|---|
show |
Replay에 터치를 누른 위치와 뗀 위치를 기록합니다 |
hide |
터치 위치를 기록하지 않습니다. 기본값입니다 |
터치 캡처는 Session Replay에 속하며 autoTrack.actions와 독립적입니다. autoTrack.actions를 활성화하지 않아도 touchPrivacy: 'show'가 Replay 터치를 기록합니다. 반대로 Action 자동 캡처를 활성화하고 touchPrivacy: 'hide'를 유지하면 Replay에 터치 위치가 표시되지 않습니다. 전후 두 프레임의 화면에 변화가 없더라도 쓰기 대기 중인 터치 동작은 Replay에 별도로 저장됩니다.
현재 Cocos API는 전역 터치 프라이버시 설정만 지원하며 노드별 터치 프라이버시 재정의는 지원하지 않습니다. guanceSdk.replay.setPrivacy(node, 'hide')는 노드 화면만 숨기며 해당 위치의 터치 기록은 숨기지 않습니다. 민감한 페이지에서는 Session Replay 시작 시 touchPrivacy: 'hide'를 사용해야 합니다.
터치 위치 프라이버시
터치 좌표는 사용자가 민감한 페이지에서 조작한 위치를 노출할 수 있습니다. 프라이버시 평가를 완료하고 필요한 승인을 받은 경우에만 show로 설정해야 합니다. 그렇지 않으면 기본값인 hide를 유지하세요.
노드 프라이버시¶
모든 EditBox 노드는 기본적으로 mask를 사용합니다. 지정된 노드에 규칙을 설정할 수도 있습니다:
guanceSdk.replay.setPrivacy(accountNode, 'mask');
guanceSdk.replay.setPrivacy(secretPanelNode, 'hide');
guanceSdk.replay.setPrivacy(publicNode, 'unmask');
| 모드 | 동작 |
|---|---|
mask |
마스크 색상으로 노드 사각형 영역을 덮습니다 |
hide |
단색으로 노드 사각형 영역을 숨깁니다 |
unmask |
해당 노드의 사용자 정의 규칙을 삭제합니다 |
unmask는 사용자 정의 규칙만 삭제합니다. 노드가 여전히 EditBox이면 기본 마스킹이 계속 적용됩니다.
프라이버시 영역은 노드의 월드 좌표 사각형을 기준으로 계산됩니다. 사용자 정의 렌더링, 파티클, Shader, RenderTexture 또는 노드 경계 상자를 벗어나는 시각적 콘텐츠는 프라이버시 영역이 자동으로 파생되지 않으므로 연동 테스트에서 씬별로 확인해야 합니다.
페이지별 마스크 관리¶
setPrivacy()의 규칙은 전달된 노드 인스턴스에 바인딩되며 페이지 이름이나 RUM View에 따라 자동으로 전환되지 않습니다. 페이지마다 다른 내용을 마스킹해야 하는 경우 비즈니스 페이지의 진입·이탈 로직에서 각 노드의 규칙을 관리해야 합니다.
아래는 같은 씬에서 '진단 페이지'와 '조작 페이지'를 전환하는 예시입니다. diagnosticsPage, motionPage는 두 페이지의 루트 노드이고, privateTokenNode는 진단 페이지에서 마스킹해야 하는 일반 노드입니다. SDK 초기화 또는 Hybrid attach() 완료 후 페이지를 전환할 때 해당 함수를 호출하세요:
function showDiagnostics() {
// 진입할 때마다 다시 설정하여 민감한 노드가 표시되기 전에 마스크 규칙이 있도록 합니다.
guanceSdk.replay.setPrivacy(privateTokenNode, 'mask');
motionPage.active = false;
diagnosticsPage.active = true;
}
function showMotion() {
// 먼저 민감한 페이지를 숨긴 후 해당 페이지 노드의 사용자 정의 규칙을 제거합니다.
diagnosticsPage.active = false;
guanceSdk.replay.setPrivacy(privateTokenNode, 'unmask');
motionPage.active = true;
}
조작 페이지에도 민감한 노드가 있다면 표시 전에 해당 노드 각각에 mask 또는 hide를 설정하고, 이탈할 때 해당 규칙을 제거해야 합니다. 페이지에 사용자 정의 마스크 노드가 여러 개 포함된 경우 하나씩 관리해야 합니다.
페이지 수명 주기에서 다음 사항도 유의해야 합니다:
- 페이지 또는 부모 노드를
active = false로만 설정하면 등록된 사용자 정의 규칙이 삭제되지 않습니다. 페이지를 숨기는 것으로unmask호출을 대신할 수 없으며, 그렇게 하면 이전 페이지의 마스크 영역이 이후 화면에 영향을 줄 수 있습니다. - 페이지를 떠나거나 노드가 파괴되기 전에 해당 페이지에 등록된 사용자 정의 규칙을 제거하세요. 로직은 프로젝트의 페이지 관리자 또는 컴포넌트의 활성화·비활성화·파괴 콜백에 넣을 수 있습니다.
- 페이지에 다시 진입할 때 규칙을 다시 설정하세요. 페이지가 파괴된 후 재생성된 경우 새로 생성된 노드 인스턴스를 전달해야 합니다.
- RUM View 이름을 전환해도 노드 규칙이 자동으로 설정되거나 제거되지 않습니다. Hybrid
enterCocos()/leaveCocos()는 캡처 소속을 관리하며, 페이지는 여전히 노드 마스크를 직접 관리해야 합니다. unmask는 지정된 노드의 사용자 정의 규칙만 삭제하며, 전역 프라이버시 보호를 끄거나EditBox기본 마스킹을 해제하지 않습니다.
이러한 규칙은 Cocos 캡처 프레임의 노드 화면에 영향을 줍니다. Hybrid 네이티브 페이지는 여전히 호스트 Native SDK의 Session Replay 프라이버시 구성을 사용하며, 터치 위치는 touchPrivacy가 별도로 제어합니다.
검증 시 '페이지 진입 → 다른 페이지로 전환 → 다시 진입'을 포함하여 Replay에서 민감한 콘텐츠가 계속 마스킹되고 다른 페이지에 마스크가 남아 있지 않은지 확인하세요.
더 많은 프라이버시 권장 사항은 데이터 및 프라이버시를 참조하세요.
런타임 동작¶
- Cocos
RenderTexture를 사용하여 RGBA 화면을 캡처합니다. imagePolicy를 구성하지 않으면 기존 이미지 경로가 유지됩니다. 명시적으로 구성하면 Android는 WebP V2, iOS는 JPEG V2를 사용하며 실제 인코딩 크기로 계상합니다.- 프라이버시 마스킹은 이미지 압축 전에 적용됩니다.
- 내용이 완전히 동일하거나 거의 정지된 프레임은 건너뜁니다. View, 화면 크기 또는 프라이버시 규칙이 변경되면 변경 프레임이 강제로 생성됩니다.
- 이미지가 중복 제거, 예산 또는 인코딩 크기로 인해 건너뛰어져도 쓰기 대기 중인 터치 기록은 별도로 저장됩니다.
- 이전 프레임이 아직 처리 중이면 새 프레임이 동시에 처리되지 않습니다.
- 단일 프레임 캡처 또는 인코딩 실패는 현재 프레임만 폐기하며 이후 Session Replay 캡처를 중단하지 않습니다.
- 임시 이미지는 Native SDK에 기록된 후 앱 임시 디렉터리에서 삭제됩니다.
트래픽 추정¶
트래픽 예산은 이미지 Resource만 집계하며 Replay Segment, 터치 기록 및 업로드 프로토콜 오버헤드는 포함하지 않습니다. 연속 전투, 카메라 이동, 파티클 등 고역동 화면은 일반적으로 등급 상한에 더 가깝습니다. 메뉴, 정적 배경 및 소량의 UI 애니메이션은 완전 동일 프레임 및 근사 정지 감지의 영향을 받으므로 실제 사용량이 일반적으로 더 낮습니다.
아래 표는 V2 인코딩 및 해당 등급의 기본 구성을 기준으로 단일 안정적인 View에서 추정한 값입니다. 용량 계획에 사용되며 실측 데이터나 네트워크 요금 청구 보장은 아닙니다. maxImageDimension, maxFrameBytes 또는 maxBytesPerMinute를 명시적으로 변경하면 범위와 상한이 함께 달라집니다.
| 등급 | 캐주얼 씬 이미지 트래픽 추정 | 고역동 씬 이미지 Resource 상한 | 낮은 터치 밀도에서의 고역동 총량 참고 |
|---|---|---|---|
low |
0.1–0.4 MiB/분 | 0.6 MiB/분 | 약 0.7–0.9 MiB/분 |
medium |
0.2–0.8 MiB/분 | 1.5 MiB/분 | 약 1.6–1.8 MiB/분 |
high |
0.4–1.6 MiB/분 | 4 MiB/분 | 약 4.1–4.4 MiB/분 |
캐주얼 씬 범위는 화면에 지속적으로 약간의 변화가 있다고 가정합니다. 화면이 완전히 정지된 경우 중복 제거 후 트래픽이 더 낮을 수 있습니다. 고역동 이미지 값은 60초 롤링 예산 상한이며 고정 소비량이 아닙니다. 총량 참고치는 이미지 Resource에 낮은 터치 밀도에서의 Replay Segment, 터치 메타데이터 및 애플리케이션 계층 업로드 오버헤드를 더해 추정한 것으로, 프로덕션 네트워크의 TLS, TCP/IP, 셀룰러 또는 Wi-Fi 링크 오버헤드는 포함하지 않습니다.
Android V2는 WebP, iOS V2는 JPEG를 사용합니다. 이미지 콘텐츠, 압축기의 플랫폼 차이, 실제 변경 프레임 수, View 또는 방향 전환 횟수, 터치 밀도, 업로드 재시도는 모두 최종 트래픽에 영향을 줍니다. 출시 전에 대상 플랫폼과 실제 비즈니스 시나리오에서 측정하세요.
단일 녹화 세션의 이미지 트래픽은 다음 방식으로 추정할 수 있습니다:
예를 들어 captureFps: 2, quality: 'medium' 설정에서 고역동 화면은 예산이 75%에 도달하면 자동으로 캡처 빈도가 낮아지고, 90% 이후에는 품질과 크기가 낮아집니다. 이미지 Resource는 2 × 60 × 40 KiB로 분당 약 4.7 MiB까지 계속 증가하는 것이 아니라 1.5 MiB 60초 롤링 예산에 의해 제한됩니다. 새 View 또는 방향 전환 시 첫 프레임은 여전히 최대 100 KiB의 별도 버스트 할당량을 사용할 수 있으므로 해당 창에서는 일반 이미지 예산보다 일시적으로 높을 수 있습니다.
예산은 녹화된 각 세션을 제어하며 세션 샘플링을 대신할 수 없습니다. 프로덕션 환경에서는 먼저 일반 세션의 sampleRate를 0.01–0.05로 설정한 다음 트러블슈팅 요구에 따라 sessionOnErrorSampleRate를 구성할 수 있습니다. 진단 환경에서는 임시로 100% 샘플링을 사용할 수 있습니다. 전체 이미지 트래픽은 다음 방식으로 추정할 수 있으며, 오류 세션 추가 샘플링, Segment 및 네트워크 프로토콜 오버헤드는 별도로 포함해야 합니다:
통합 패키지에서 마이그레이션¶
0.1.0-alpha.5 또는 이전 버전에서 업그레이드하는 경우:
- 기본 패키지를
0.1.0-alpha.6으로 업그레이드하고 동일 버전의 Replay 패키지를 설치한 후 인스톨러--replay를 다시 실행하세요. - 첫
start()/attach()전에withSessionReplay(baseSdk)를 호출하여 비즈니스 코드가 결합된 인스턴스를 재사용하도록 하세요. - 기존에 별도로 내보냈던
setReplayCamera(camera)를guanceSdk.setReplayCamera(camera)로 변경하세요. FTSessionReplayConfig,FTHybridSessionReplayConfig,FTReplayImagePolicy등 Replay 타입의 임포트를@cloudcare/cocos-session-replay/creator2또는/creator3로 이동하세요. Replay를 포함하는 전체 구성은 각각FTCocosReplayConfig와FTCocosHybridReplayConfig를 사용합니다.- 네이티브 프로젝트를 다시 생성하고 컴파일하세요. JavaScript만 교체하면 새 Replay Bridge가 설치되지 않습니다.
Replay를 더 이상 사용하지 않으면 결합 및 Replay 구성을 제거하고 인스톨러 --no-replay를 실행한 후 다시 빌드하세요. 이 스위치는 인스톨러가 관리하는 네이티브 통합을 제거하는 데 사용됩니다. 런타임에 프레임 캡처를 일시 중지하려면 아래의 replay.stop()을 사용하고, Hybrid 시나리오에서는 leaveCocos()를 사용합니다.
시작 및 중지¶
Replay 패키지를 설치하고 결합했다는 전제하에,guanceSdk.start()에 replay를 전달하지 않았다면, RUM 초기화 및 View 시작 후에도 별도로 활성화할 수 있습니다:
guanceSdk.replay.start({
sampleRate: 1,
captureFps: 1,
maxImageDimension: 720,
touchPrivacy: 'show',
});
프레임 캡처 중지:
guanceSdk.shutdown()도 프레임 캡처를 중지합니다. guanceSdk.start({ replay: ... })와 guanceSdk.replay.start()를 동시에 사용하여 중복 시작하지 마세요.
네이티브 호스트 Hybrid 모드에서는 위의 시작/중지 인터페이스를 사용하지 않습니다. Session Replay는 네이티브 호스트가 네이티브 recorder 모드로 초기화하며, Cocos는 enterCocos()와 leaveCocos() 사이에 자동으로 외부 캡처 소스로 전환합니다. 자세한 내용은 네이티브 호스트 Hybrid 연동을 참조하세요.
성능 권장 사항¶
captureFps: 1,imagePolicy: { quality: 'medium' }부터 검증을 시작하고, 더 부드러운 화면이 필요할 때2 fps로 높이세요.- 리플레이가 필요한 비즈니스 환경에서만 샘플링을 활성화하세요.
- 저사양 디바이스에서 GPU, 메모리 또는 디스크 부담이 발생하면 먼저
low로 변경한 다음 최장 변 크기와 샘플링 비율을 낮추세요. - 가로/세로 화면 전환, 다중 Camera, 복잡한 UI, 저프레임 레이트 시나리오에서 이미지 방향과 마스크 위치를 검증하세요.