İçeriğe geç
JavaScript

JavaScript Web Storage: localStorage ve sessionStorage

Tarık Tunç
JavaScript Web Storage: localStorage ve sessionStorage

JavaScript Web Storage: localStorage ve sessionStorage

Web Storage API, tarayıcıda anahtar-değer çiftlerini saklamak için basit ve senkron bir mekanizma sunar. localStorage kalıcı, sessionStorage ise oturum bazlı depolama sağlar. Cookie'lere göre daha büyük kapasite ve daha kolay kullanım sunan Web Storage, kullanıcı tercihleri, form verileri ve uygulama durumu saklamak için yaygın olarak kullanılır.

1. localStorage ve sessionStorage Temelleri

Her iki depolama alanı da aynı API'yi paylaşır, ancak yaşam süreleri farklıdır.

// localStorage — kalıcı (tarayıcı kapatılsa bile kalır)
localStorage.setItem("tema", "koyu");
localStorage.setItem("dil", "tr");

const tema = localStorage.getItem("tema"); // "koyu"
const bilinmeyen = localStorage.getItem("yok"); // null

localStorage.removeItem("dil");
localStorage.clear(); // Tümünü sil

// sessionStorage — oturum bazlı (sekme kapatılınca silinir)
sessionStorage.setItem("sepet", JSON.stringify(["Laptop", "Mouse"]));
const sepet = JSON.parse(sessionStorage.getItem("sepet"));

// Tüm anahtarları listeleme
for (let i = 0; i < localStorage.length; i++) {
  const anahtar = localStorage.key(i);
  const deger = localStorage.getItem(anahtar);
  console.log(`${anahtar}: ${deger}`);
}

// Object.keys alternatifi
const tumAnahtarlar = Object.keys(localStorage);
console.log(tumAnahtarlar);

2. JSON Veri Yönetimi

Web Storage yalnızca string saklar. Karmaşık veri tipleri için JSON serileştirme kullanılır.

// Güvenli Storage wrapper
class StorageManager {
  #storage;
  #onEk;

  constructor(storage = localStorage,) {
    this.#storage = storage;
    this.#onEk = onEk;
  }

  #anahtarOlustur(anahtar) {
    return `${this.#onEk}${anahtar}`;
  }

  kaydet(anahtar, deger, sureSaniye = null) {
    const veri = {
      deger,
      olusturma: Date.now(),
      sonlanma: sureSaniye ? Date.now() + sureSaniye * 1000 : null
    };

    try {
      this.#storage.setItem(
        this.#anahtarOlustur(anahtar),
        JSON.stringify(veri)
      );
      return true;
    } catch (hata) {
      if (hata.name === "QuotaExceededError") {
        console.warn("Depolama alanı dolu!");
        this.eskiVerileriTemizle();
        return false;
      }
      throw hata;
    }
  }

  getir(anahtar, varsayilan = null) {
    const ham = this.#storage.getItem(this.#anahtarOlustur(anahtar));
    if (!ham) return varsayilan;

    try {
      const veri = JSON.parse(ham);

      // Süre kontrolü
      if (veri.sonlanma && Date.now() > veri.sonlanma) {
        this.sil(anahtar);
        return varsayilan;
      }

      return veri.deger;
    } catch {
      return varsayilan;
    }
  }

  sil(anahtar) {
    this.#storage.removeItem(this.#anahtarOlustur(anahtar));
  }

  eskiVerileriTemizle() {
    const anahtarlar = Object.keys(this.#storage)
      .filter(k => k.startsWith(this.#onEk));

    for (const anahtar of anahtarlar) {
      try {
        const veri = JSON.parse(this.#storage.getItem(anahtar));
        if (veri.sonlanma && Date.now() > veri.sonlanma) {
          this.#storage.removeItem(anahtar);
        }
      } catch {
        this.#storage.removeItem(anahtar);
      }
    }
  }
}

// Kullanım
const storage = new StorageManager(localStorage, "blog_");

storage.kaydet("kullanici", { ad: "Tarık", rol: "admin" });
storage.kaydet("geciciToken", "abc123", 3600); // 1 saat geçerli

const kullanici = storage.getir("kullanici");
console.log(kullanici); // { ad: "Tarık", rol: "admin" }
İpucu: Web Storage senkron çalışır ve ana thread'i bloke eder. Büyük veri setleri için IndexedDB veya Cache API gibi asenkron alternatifleri tercih edin. localStorage kapasitesi genellikle 5-10 MB arasındadır.

3. Storage Event ile Sekmeler Arası İletişim

storage olayı, aynı origin'deki farklı sekmeler arasında iletişim kurmak için kullanılabilir.

// Diğer sekmelerdeki değişiklikleri dinleme
window.addEventListener("storage", (event) => {
  console.log("Depolama değişti:", {
    anahtar: event.key,
    eskiDeger: event.oldValue,
    yeniDeger: event.newValue,
    url: event.url,
    storage: event.storageArea
  });

  // Oturum durumu değişikliği
  if (event.key === "oturum_durumu") {
    if (event.newValue === "cikis") {
      // Diğer sekmede çıkış yapıldı, bu sekmeyi de güncelle
      window.location.href = "/giris";
    }
  }

  // Tema değişikliği
  if (event.key === "tema") {
    document.documentElement.dataset.tema = event.newValue;
  }
});

// Sekmeler arası mesajlaşma (BroadcastChannel alternatifi)
function mesajGonder(kanal, veri) {
  const mesaj = JSON.stringify({ kanal, veri, zaman: Date.now() });
  localStorage.setItem("_mesaj", mesaj);
  localStorage.removeItem("_mesaj"); // Tetikle ve temizle
}

window.addEventListener("storage", (e) => {
  if (e.key === "_mesaj" && e.newValue) {
    const { kanal, veri } = JSON.parse(e.newValue);
    console.log(`[${kanal}] Mesaj alındı:`, veri);
  }
});

4. Güvenlik ve En İyi Pratikler

// ❌ YANLIŞ: Hassas veri saklama
localStorage.setItem("token", "gizli-jwt-token"); // XSS ile çalınabilir
localStorage.setItem("sifre", "12345");            // Asla!

// ✅ DOĞRU: Hassas veri için httpOnly cookie kullanın

// Veri şifreleme (basit obfuscation, gerçek güvenlik değil)
function basitSifrele(metin, anahtar) {
  return btoa(
    metin.split("").map((c, i) =>
      String.fromCharCode(c.charCodeAt(0) ^ anahtar.charCodeAt(i % anahtar.length))
    ).join("")
  );
}

// Depolama boyutu hesaplama
function storageBoyutu(storage = localStorage) {
  let toplam = 0;
  for (const anahtar of Object.keys(storage)) {
    toplam += anahtar.length + storage.getItem(anahtar).length;
  }
  return {
    byte: toplam * 2, // UTF-16
    kb: ((toplam * 2) / 1024).toFixed(2),
    mb: ((toplam * 2) / (1024 * 1024)).toFixed(4)
  };
}

console.log(storageBoyutu());
// { byte: 2048, kb: "2.00", mb: "0.0020" }

// Feature detection
function storageKullanilabilir(tip = "localStorage") {
  try {
    const storage = window[tip];
    const test = "__storage_test__";
    storage.setItem(test, test);
    storage.removeItem(test);
    return true;
  } catch {
    return false;
  }
}

if (!storageKullanilabilir()) {
  console.warn("localStorage kullanılamıyor, fallback mekanizması aktif");
}
Uyarı: localStorage'da JWT token, parola veya kredi kartı bilgisi gibi hassas verileri saklamamalısınız. XSS saldırısı ile bu veriler kolayca çalınabilir. Hassas veriler için httpOnly, Secure ve SameSite flag'li cookie'ler kullanın.
Bilgi: Gizli/özel tarayıcı modunda bazı tarayıcılar localStorage'ı sınırlar veya oturum sonunda temizler. Bu yüzden localStorage erişimini her zaman try/catch ile sarmalayın.

Sonuç

Web Storage API, istemci tarafında basit veri depolama için etkili bir araçtır. localStorage ile kalıcı tercihler, sessionStorage ile oturum bazlı veriler, storage event ile sekmeler arası iletişim ve güvenli wrapper'lar ile veri yönetimi modern web uygulamalarının temel ihtiyaçlarını karşılar. Hassas veriler için alternatif mekanizmalar kullanmayı ve kapasite sınırlarını göz önünde bulundurmayı unutmayın.

Kaynaklar

İlgili Yazılar