fix: 매니페스트 로드 실패 시 로컬 자산으로 안전하게 대체되지 않는 버그 수정

- normalizeMediaManifest에서 빈 데이터 수신 시 DEFAULT_MEDIA_MANIFEST를 활용하도록 수정
- 매니페스트 로드 실패 시 상세 정보(URL, 상태 코드)를 에러 메시지에 포함
- useMediaCatalog에서 fetch 에러 발생 시 명시적으로 로컬 기반 매니페스트를 적용하도록 보강
This commit is contained in:
2026-03-11 15:16:00 +09:00
parent 35f1dfb92d
commit 972be117cb
3 changed files with 35 additions and 18 deletions

View File

@@ -36,6 +36,7 @@ export const mediaManifestApi = {
return DEFAULT_MEDIA_MANIFEST;
}
try {
const response = await fetch(MEDIA_MANIFEST_URL, {
method: 'GET',
cache: MEDIA_MANIFEST_FETCH_CACHE,
@@ -43,10 +44,18 @@ export const mediaManifestApi = {
});
if (!response.ok) {
throw new Error(copy.media.manifestLoadFailed);
throw new Error(`${copy.media.manifestLoadFailed} (${response.status} at ${MEDIA_MANIFEST_URL})`);
}
const payload = (await response.json()) as Partial<MediaManifest>;
return normalizeMediaManifest(payload);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
throw error;
}
throw new Error(
`${copy.media.manifestLoadFailed}: ${error instanceof Error ? error.message : String(error)} (URL: ${MEDIA_MANIFEST_URL})`
);
}
},
};

View File

@@ -99,18 +99,18 @@ const mergeSoundAssets = (manifest: MediaManifest) => {
};
export const normalizeMediaManifest = (manifest: Partial<MediaManifest> | null | undefined): MediaManifest => {
const mergedManifest: MediaManifest = {
const baseManifest: MediaManifest = {
version: manifest?.version ?? DEFAULT_MEDIA_MANIFEST.version,
updatedAt: manifest?.updatedAt ?? DEFAULT_MEDIA_MANIFEST.updatedAt,
cdnBaseUrl: manifest?.cdnBaseUrl ?? DEFAULT_MEDIA_MANIFEST.cdnBaseUrl,
scenes: manifest?.scenes ?? [],
sounds: manifest?.sounds ?? [],
scenes: manifest?.scenes ?? DEFAULT_MEDIA_MANIFEST.scenes,
sounds: manifest?.sounds ?? DEFAULT_MEDIA_MANIFEST.sounds,
};
return {
...mergedManifest,
scenes: mergeSceneAssets(mergedManifest),
sounds: mergeSoundAssets(mergedManifest),
...baseManifest,
scenes: mergeSceneAssets(baseManifest),
sounds: mergeSoundAssets(baseManifest),
};
};

View File

@@ -40,10 +40,18 @@ const readMediaManifest = async (signal?: AbortSignal): Promise<MediaCatalogLoad
};
})
.catch((error) => {
const nextError = error instanceof Error ? error.message : null;
// Only return abort errors up the chain if we're not using fallback
if (error instanceof Error && error.name === 'AbortError') {
throw error;
}
const nextError = error instanceof Error ? error.message : String(error);
// Explicitly use normalize with null to get a pure fallback manifest
const fallbackManifest = normalizeMediaManifest(null);
return {
manifest: manifestCache,
manifest: fallbackManifest,
error: nextError,
usedFallbackManifest: true,
};