| 기준 | API | 저사양 조건 | 결합 방식 |
|---|---|---|---|
| CPU 코어 수 | navigator.hardwareConcurrency | ≤ 4코어 | 셋 중 하나라도 해당하면 저사양 |
| 메모리 | navigator.deviceMemory | ≤ 4 GB | |
| GPU | WEBGL_debug_renderer_info | Mali-T830 / T720 / 400 |
| 기준 | API / 계산 | 저사양 조건 |
|---|---|---|
| 물리 코어 수 | navigator.hardwareConcurrency | ≤ 2코어 (즉시 저사양) |
| 논리 코어 | hardwareConcurrency ≤ 3 | 무조건 저사양 |
| iPad 5세대 판정 | 코어 = 4 + iOS ≤ 16 + 해상도 2048×1536 | 저사양 |
| 복합 판정 (코어 = 4) | JS 벤치마크 < 60 FPS 또는 (iOS ≤ 16 + 구형 해상도) | 저사양 |
initLowPerformanceDevice() 를 리소스 캐싱 시작 전에 호출
결과는 모듈 레벨 캐시에 저장 — 이후 동기 호출(isLowPerformanceDevice())은 캐시 반환
MediaCapabilities.decodingInfo() 로 MP4 H.264 720p 디코딩 실측
type: "file", contentType: "video/mp4; codecs=avc1.42E01E", 1280×720 @ 30fps, 1 Mbps
result.smooth === false 이면 저사양으로 분류
API 미지원 브라우저이거나 예외 발생 시 → 고사양으로 폴백
type: "webrtc" + video/VP8 프로브를 사용했으나,
실제 서빙되는 리소스(MP4/H.264)와 다른 코덱 경로를 측정해 오탐 가능성이 있었습니다.
type: "file" + video/mp4; codecs=avc1.42E01E으로 변경하여 실제 재생 경로와 일치시켰습니다.
| 기기 유형 | 저사양 판정 | 비디오 리소스 | 조건 |
|---|---|---|---|
| Android / iOS (저사양) | true | 480p | s3KeyProcessed480p 존재 시 |
| Android / iOS (고사양) | false | 720p | s3KeyProcessed 또는 원본 |
| 데스크탑 / 노트북 (저사양) | true | 480p | smooth=false + s3KeyProcessed480p 존재 |
| 데스크탑 / 노트북 (고사양) | false | 720p | smooth=true |
// apps/web/lib/utils.ts async function _checkDesktopDecodingCapability(): Promise<boolean> { if (!("mediaCapabilities" in navigator)) return false; try { const result = await navigator.mediaCapabilities.decodingInfo({ type: "file", video: { contentType: "video/mp4; codecs=avc1.42E01E", width: 1280, height: 720, bitrate: 1_000_000, framerate: 30, }, }); return !result.smooth; // smooth=false → 저사양 } catch { return false; // 예외 → 고사양 폴백 } } // 동기 호출용 (JSX 인라인, 캐시 히트 후) export function isLowPerformanceDevice(): boolean { if (_lowPerfCache !== null) return _lowPerfCache; return _computeSyncLowPerf(); } // 리소스 캐싱 전 1회 호출 — 결과를 모듈 캐시에 저장 export async function initLowPerformanceDevice(): Promise<boolean> { if (_lowPerfCache !== null) return _lowPerfCache; if (!_lowPerfPromise) { _lowPerfPromise = (async () => { const syncResult = _computeSyncLowPerf(); const isDesktop = !/Android|iPad|iPhone|iPod/i.test(navigator.userAgent) && !(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1); const result = syncResult || (isDesktop && await _checkDesktopDecodingCapability()); _lowPerfCache = result; return result; })(); } return _lowPerfPromise; }