Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?

Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?
Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projenin sorunsuz çalışması için sisteminizde Node.js'in en güncel LTS (Long Term Support) sürümünün yüklü olması gerekmektedir. 2026 yılı standartları gereği, modül yapısı olarak ES Modules (ESM) tercih edeceğiz.

  • Node.js (v22.x veya üzeri)
  • npm (Node Package Manager)
  • Temel düzeyde Express.js bilgisi
  • WebSocket protokolü mantığına aşinalık

Çalışma dizininizi oluşturun ve gerekli paketleri yüklemek için terminalinizde şu komutları çalıştırın:

mkdir canli-duzenleyici
cd canli-duzenleyici
npm init -y
npm install express socket.io

Bu komutlar, projenin temel bağımlılıklarını yönetmek için bir package.json dosyası oluşturacak ve gerekli kütüphaneleri projenize dahil edecektir.

Sunucu Tarafını Kurma ve Socket.io Entegrasyonu

Sunucu tarafında Express kullanarak bir HTTP sunucusu başlatacağız ve bu sunucuyu Socket.io ile sarmalayacağız. Socket.io, WebSocket protokolü üzerinden çift yönlü ve olay tabanlı iletişim sağlar.

import express from 'express';
import { createServer } from 'http';
import { Server } from 'socket.io';

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer);

io.on('connection', (socket) => {
    console.log('Bir kullanıcı bağlandı:', socket.id);

    socket.on('metin-degisti', (yeniMetin) => {
        // Gelen veriyi diğer tüm istemcilere yayınla
        socket.broadcast.emit('guncelleme-al', yeniMetin);
    });
});

httpServer.listen(3000, () => {
    console.log('Sunucu 3000 portunda çalışıyor.');
});

Bu kodda, socket.broadcast.emit metodu, metni değiştiren kullanıcı dışındaki tüm bağlı istemcilere güncel veriyi gönderir. Böylece herkes aynı anda aynı içeriği görebilir.

İstemci Tarafı: Arayüz ve Socket İletişimi

İstemci tarafında, kullanıcının metin girişi yapabileceği bir textarea alanı oluşturacağız. Kullanıcı her tuşa bastığında, bu değişiklik sunucuya iletilecek ve diğer kullanıcılara yansıtılacaktır.




  const socket = io();
  const editor = document.getElementById('editor');

  editor.addEventListener('input', (e) => {
    socket.emit('metin-degisti', e.target.value);
  });

  socket.on('guncelleme-al', (metin) => {
    editor.value = metin;
  });

Burada dikkat edilmesi gereken nokta, input olayının tetiklenmesiyle verinin sürekli akış halinde olmasıdır. Küçük projelerde bu yöntem iş görse de, büyük metin bloklarında performans optimizasyonu gerekebilir.

Veri Senkronizasyonu ve Çakışma Yönetimi

Canlı düzenleyicilerde "Race Condition" (yarış durumu) kaçınılmazdır. İki kullanıcı aynı anda aynı satırı değiştirmeye çalışırsa ne olur? Profesyonel uygulamalarda "Operational Transformation" (OT) veya "Conflict-free Replicated Data Types" (CRDT) kullanılır.

Kritik Güvenlik Uyarısı: Socket.io üzerinden gelen veriler her zaman doğrulanmalıdır. Kullanıcıdan gelen metni doğrudan veritabanına veya DOM'a basmadan önce mutlaka XSS (Cross-Site Scripting) saldırılarına karşı sanitize (temizleme) edin.

Basit bir çakışma yönetimi için, imleç pozisyonunu korumak adına metni tamamen değiştirmek yerine sadece değişen karakteri göndermek daha verimli bir yaklaşımdır.

Performans ve Ölçeklenebilirlik Karşılaştırması

Gerçek zamanlı düzenleme uygulamalarında kullanılan yöntemlerin karşılaştırması aşağıdadır:

Yöntem Avantajı Dezavantajı
Basit Broadcast Kolay kurulum Çakışma yönetimi yok
Operational Transformation Güçlü tutarlılık Karmaşık algoritma
CRDT Dağıtık sistem uyumu Yüksek bellek kullanımı

Üretim Ortamı İçin İpuçları

Uygulamanızı canlıya alırken dikkat etmeniz gereken en önemli husus, Socket.io'nun "sticky session" gereksinimidir. Eğer birden fazla sunucu (load balancer) kullanıyorsanız, istemcinin aynı sunucuya bağlı kalması için Redis adaptörü kullanmanız şarttır.

import { createClient } from 'redis';
import { createAdapter } from '@socket.io/redis-adapter';

const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();

await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));

Bu yapılandırma, uygulamanızın yatayda ölçeklenmesini sağlar ve farklı sunuculara bağlı kullanıcıların birbirini görmesini mümkün kılar.

Sıkça Sorulan Sorular

Socket.io yerine neden WebSockets kullanmıyorum?

Socket.io, bağlantı kopmalarında otomatik yeniden bağlanma, fallback (yedekleme) mekanizmaları ve oda (room) desteği gibi gelişmiş özellikler sunduğu için geliştirme sürecini hızlandırır.

Uygulama neden çok yavaş çalışıyor?

Her tuş vuruşunda tüm metni göndermek büyük verilerde performans kaybına yol açar. "Debounce" tekniğini kullanarak gönderim sıklığını azaltmayı deneyin.

Veriler veritabanına nasıl kaydedilmeli?

Canlı düzenleme sırasında her değişikliği veritabanına yazmak yerine, belirli aralıklarla (örneğin 5 saniyede bir) veya kullanıcı "Kaydet" butonuna bastığında veriyi kalıcı hale getirmelisiniz.

XSS saldırılarını nasıl önlerim?

İstemciden gelen veriyi sunucu tarafında dompurify gibi bir kütüphane ile temizleyerek güvenli hale getirebilirsiniz.

Mobil cihazlarda sorun yaşar mıyım?

Mobil tarayıcılar arka planda WebSocket bağlantılarını askıya alabilir. Bu durumu yönetmek için bağlantı durumunu takip eden bir "heartbeat" mekanizması kurmanız önerilir.

Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamında kullanmadan önce mutlaka güvenlik denetimlerinden geçirin ve güncel kütüphane sürümlerini takip edin.

İleri Seviye Optimizasyon: Operasyonel Dönüşüm (OT) Mantığı

Canlı düzenleme uygulamalarında en büyük zorluk, iki kullanıcının aynı anda aynı karakteri değiştirmesi durumunda ortaya çıkan "çakışma" sorunudur. Basit bir socket.emit yapısı, yüksek trafikli anlarda verilerin birbirinin üzerine yazılmasına neden olabilir. Bu noktada Operational Transformation (OT) mantığını devreye almak, uygulamanızı profesyonel seviyeye taşır.

OT, bir işlemin (ekleme, silme, biçimlendirme) diğer işlemlerle nasıl birleştirileceğini belirleyen bir algoritmadır. Aşağıdaki örnekte, bir karakter ekleme işleminin sunucu tarafında nasıl bir sıra numarası (revision) ile işlendiğini görebilirsiniz:

// Sunucu tarafında basit bir operasyon sırası yönetimi
let revision = 0;
const history = [];

socket.on('edit', (data) => {
  if (data.revision === revision) {
    revision++;
    history.push(data);
    socket.broadcast.emit('update', { ...data, revision });
  } else {
    // Çakışma durumu: İstemciye düzeltme gönder
    socket.emit('rebase', { currentRevision: revision, history });
  }
});

Uygulama İçin Otomatik Test Stratejileri

Gerçek zamanlı uygulamalarda "race condition" (yarış durumu) hatalarını manuel test etmek imkansızdır. Bu nedenle, Socket.io bağlantılarını simüle eden test senaryoları yazmalısınız. Jest ve Socket.io-client kütüphanelerini kullanarak, birden fazla istemcinin aynı anda veri gönderdiği senaryoları şu şekilde test edebilirsiniz:

const io = require('socket.io-client');

test('İki istemci aynı anda veri gönderdiğinde sunucu sırayı korumalıdır', (done) => {
  const client1 = io('http://localhost:3000');
  const client2 = io('http://localhost:3000');

  client1.emit('edit', { char: 'A', pos: 0 });
  client2.emit('edit', { char: 'B', pos: 1 });

  client1.on('update', (data) => {
    expect(data.revision).toBeDefined();
    client1.disconnect();
    client2.disconnect();
    done();
  });
});

Performans İzleme ve Loglama

Canlı düzenleyicilerde gecikme (latency) doğrudan kullanıcı deneyimini etkiler. Sunucunuzun yük altında nasıl tepki verdiğini anlamak için socket.io-adapter kullanarak Redis üzerinden metrik toplayabilirsiniz. Ayrıca, her bir bağlantının ne kadar sürede yanıt aldığını ölçmek için şu basit middleware yapısını kullanabilirsiniz:

io.use((socket, next) => {
  socket.onAny((event, ...args) => {
    console.log(`[LOG] ${new Date().toISOString()} - Olay: ${event}`);
  });
  next();
});

Deployment: Nginx ile WebSocket Proxy Yapılandırması

Uygulamanızı canlıya alırken, Socket.io'nun düzgün çalışması için Nginx üzerinde "Upgrade" başlıklarını yapılandırmanız şarttır. Aksi takdirde bağlantınız sürekli kopabilir veya HTTP uzun sorgulama (polling) modunda kalarak performansı düşürebilir.

Önemli: Nginx yapılandırmanızda proxy_set_header Upgrade $http_upgrade; ve proxy_set_header Connection "upgrade"; satırlarının olduğundan emin olun.

Sonuç

Node.js ve Socket.io kullanarak paylaşımlı bir canlı düzenleyici oluşturmak, gerçek zamanlı sistemlerin temelini anlamak için mükemmel bir projedir. Bu uygulamayı geliştirdikten sonra bir sonraki adım olarak, kullanıcıların imleçlerini diğer kullanıcılara göstermek (Presence API) veya metin geçmişini tutmak için bir veritabanı entegrasyonu ekleyebilirsiniz.

Kod yazarken her zaman modüler yapıyı korumaya ve hata yönetimine önem verin. Başarılar dilerim.

Bu yazıya tepkinizi paylaşın:
Kerem Tekin

Teknoloji ve yazılım kullanımı üzerine pratik kılavuzlar hazırlıyorum. Karmaşık dijital araçları, herkesin hızlıca öğrenebileceği sade rehberler haline getirmekte uzmanım.

Yorumlar (0)

Yorum Yaz