validated(); $html = $this->previewService->render( $validated['layout'], $validated['route_params'] ?? [], $validated['url'] ?? '/', $validated['locale'] ?? app()->getLocale(), $identifier, $validated['module_id'] ?? null, $validated['plugin_id'] ?? null, $validated['seed_context'] ?? [], ); // JSON 직렬화 경계 — 미리보기 HTML 에 유효하지 않은 UTF-8 바이트가 있으면 JsonResponse 가 // "Malformed UTF-8" 로 500 을 던진다(샘플 데이터/표현식 평가 잔여로 잘린 멀티바이트 가능). // 응답 직전 한 번 정화해 직렬화를 보장한다(산출물 내용 불변 — 깨진 바이트만 제거). if (is_string($html)) { $clean = @iconv('UTF-8', 'UTF-8//IGNORE', $html); $html = $clean === false ? mb_convert_encoding($html, 'UTF-8', 'UTF-8') : $clean; } return $this->success('common.success', [ 'identifier' => $identifier, // null = meta.seo.enabled=false 또는 미렌더 → 편집기 "검색엔진 미노출" 안내. 'enabled' => $html !== null, 'html' => $html, ]); } }