Skip to content

Ketika Rating Dimatikan, Menyembunyikan Badge Saja Tidak Cukup

Adityo Guni Waluyo

Mematikan fitur rating lewat flag ternyata tidak cukup kalau badge kartu dan markup aggregateRating masih tayang. Catatan saya merapikan semua permukaannya.

Ringkasan

Gue matiin fitur rating di detail tempat wisata tapi kaget kartu di beranda masih nongolin bintang bahkan JSON-LD-nya masih kebawa. Ternyata fetch detail udah dimatiin tapi kartu sama generator schema masih baca data rating lama. Akhirnya semua dibikin ngecek flag isRatingEnabled dulu biar visual sama markup mati bareng.

Saya baru saja mematikan fitur rating di dashboard admin proyek WisataKota. Halaman detail tempat wisata langsung bersih, bagian ulasan hilang tanpa sisa. Tapi pas saya buka halaman beranda, mata saya langsung tertuju pada sesuatu yang aneh. Kartu tempat wisata masih memamerkan ikon bintang 4.5.

Awalnya saya kira itu cuma masalah cache browser. Saya hard refresh, bahkan coba buka di mode incognito. Hasilnya tetap sama. Bintang-bintang itu masih ada di kartu listing. Yang lebih parah, saat saya klik kanan dan pilih View Page Source, markup aggregateRating masih tercetak rapi di dalam blok JSON-LD.

Dugaan pertama saya salah. Saya mengira commit sebelumnya yang sudah memotong fetch data ulasan di halaman detail sudah cukup untuk mematikan fitur ini secara global. Ternyata, logika gating tersebut belum merambat ke komponen kartu, daftar, peta, dan yang paling bahaya kalau kelewat, generator structured data.

Jejak yang Tertinggal di Kartu dan JSON-LD

Snippet ulasan pada dasarnya adalah rata-rata dari gabungan skor rating yang diberikan banyak pengulas [1]. Untuk menampilkan rich snippet yang valid, sistem wajib menyediakan rata-rata rating tersebut [1]. Masalah muncul ketika fitur dimatikan di tingkat bisnis, tapi lapisan presentasi dan lapisan data tidak sinkron.

Di proyek WisataKota, entry sebelumnya sudah berhasil membatasi pengambilan data ulasan di halaman detail. Fetch request ke backend memang sudah dihentikan. Tapi komponen kartu di halaman lain ternyata masih membaca data rating yang tersisa di objek properti utama, bukan dari endpoint ulasan yang terpisah.

Akibatnya, meskipun tidak ada ulasan baru yang bisa dibaca, kartu tempat wisata tetap menampilkan rating lama. Lebih buruk lagi, fungsi yang menghasilkan schema markup untuk SEO masih berjalan tanpa hambatan. Ia terus menyuntikkan data AggregateRating ke dalam HTML, padahal secara visual fitur itu sudah tidak ada. Tipe itu sendiri didefinisikan sebagai rata-rata rating dari banyak rating atau ulasan [2].

Mesin pencari tidak membaca layar monitor. Mereka membaca markup HTML dan JSON-LD yang disajikan. Kalau antarmuka tidak menampilkan rating sama sekali, tetapi markup terstruktur masih mengklaim adanya rating tinggi, ini menciptakan diskrepansi.

Kebijakan spam pencarian secara eksplisit mencakup structured data yang menyesatkan atau disembunyikan [3]. Mesin pencari menganggap ini sebagai upaya menipu pengguna atau memanipulasi peringkat. Saya tidak punya angka pasti soal dampak peringkatnya, dan memang tidak ada angka publik untuk kasus seperti ini, tapi risiko terkena penalti manual atau penurunan visibilitas organik sangat nyata.

Konsistensi antara apa yang dilihat manusia dan apa yang dibaca crawler adalah harga mati.

Flag Mati, Semua Permukaan Ikut Mati

Solusinya bukan sekadar menambahkan display: none di CSS. Ketika sebuah flag mematikan sebuah fitur, setiap permukaan yang mengiklankan fitur tersebut harus ikut mati. Ini termasuk badge visual dan data JSON-LD yang tidak terlihat. Di React, rendering kondisional semacam ini memang idiom bawaan, pakai sintaks if, &&, atau ternary [4].

Saya memperbarui logika kondisional di komponen kartu. Alih-alih hanya mengecek ketersediaan data rating, komponen sekarang wajib mengecek status flag rating_enabled terlebih dahulu.

// /src/components/ListingCard.tsx
interface CardProps {
  place: PlaceData;
  isRatingEnabled: boolean;
}

export function ListingCard({ place, isRatingEnabled }: CardProps) {
  return (
    <div className="card">
      <h3>{place.name}</h3>
      {isRatingEnabled && place.rating && (
        <div className="rating-badge">
          &#9733; {place.rating.average}
        </div>
      )}
    </div>
  );
}

Perubahan yang sama wajib diterapkan pada generator structured data. Fungsi ini tidak boleh lagi mengasumsikan bahwa kalau data rating ada di objek, maka data itu boleh dipublikasikan ke schema.

// /src/lib/seo/generateStructuredData.ts
export function generatePlaceSchema(place: PlaceData, isRatingEnabled: boolean) {
  const baseSchema = {
    "@context": "https://schema.org",
    "@type": "TouristAttraction",
    "name": place.name,
  };

  if (isRatingEnabled && place.rating) {
    return {
      ...baseSchema,
      "aggregateRating": {
        "@type": "AggregateRating",
        "ratingValue": place.rating.average,
        "reviewCount": place.rating.count
      }
    };
  }

  return baseSchema;
}

Dengan pendekatan ini, flag isRatingEnabled menjadi satu-satunya gerbang yang mengontrol tampilan visual maupun output data terstruktur.

Verifikasinya juga bukan tebakan. Saya buka halaman yang seharusnya bersih, view source, lalu cari string aggregateRating. Kalau masih muncul, berarti ada generator yang belum ke-gating. Terus saya cek halaman beranda, daftar kategori, dan peta satu per satu. Tidak ada kartu yang boleh menampilkan elemen rating.

Keputusan yang saya pegang sekarang: setiap kali mematikan sebuah fitur, hapus jejaknya sebagai satu paket utuh. Badge, fetch, dan JSON-LD mati bareng. Jangan biarkan sisa fitur yang sudah mati terus hidup sebagai hantu di kode produksi.

Sumber

1. Google Search Central, review snippet guidelines, review-snippet docs
2. Schema.org, AggregateRating type, AggregateRating
3. Google Search Central, spam policies, spam policies
4. Artikel internal, review-mati-tetap-404
5. React docs, conditional rendering, SebelumnyaMatikan Fetch, Bukan Sekadar Sembunyikan UI

Artikel terkait