Takip et

MERN Stack Öğreniminde 145. Gün: Profesyonel Seviyeye Geçiş Rehberi

MERN Stack yolculuğunuzun 145. gününde, basit CRUD projelerini geride bırakıp gerçek dünya uygulamalarının performans ve güvenlik sorunlarını çözmeye hazır mısınız?

MERN Stack Öğreniminde 145. Gün: Profesyonel Seviyeye Geçiş Rehberi

MERN Stack yolculuğunuzun 145. gününde, basit CRUD projelerini geride bırakıp gerçek dünya uygulamalarının performans ve güvenlik sorunlarını çözmeye hazır mısınız? Birçok geliştirici ilk birkaç ayda MongoDB, Express, React ve Node.js teknolojilerinin temel prensiplerini kolayca öğrenir. Ancak projeleri canlıya alırken ve ölçeklendirirken ciddi mimari engellerle karşılaşırlar. Bu rehberde, 145 günlük birikiminizi profesyonel bir seviyeye taşımak için odaklanmanız gereken ileri düzey teknikleri, optimizasyon yöntemlerini ve gerçek dünya senaryolarını ele alacağız.

Web geliştirme dünyasında rekabetçi kalabilmek için sadece kod yazmak yetmez. Yazdığınız kodun güvenli, hızlı ve sürdürülebilir olması gerekir. Bu nedenle, makalemiz boyunca teorik bilgileri pratik kod örnekleriyle birleştireceğiz. Hazırsanız, MERN Stack (MongoDB, Express, React, Node.js) ekosisteminin derinliklerine inelim.

145. Günde Neredeyiz? Temel CRUD Yapılarından Kurumsal Mimariye Geçiş

Öncelikle, buraya kadar gösterdiğiniz kararlılık için kendinizi tebrik etmelisiniz. Muhtemelen şimdiye kadar onlarca Todo uygulaması, basit blog siteleri veya hava durumu uygulamaları geliştirdiniz. Bu süreçte Library (kütüphane) olarak React’ı, Framework (yazılım çerçevesi) mantığında ise Express’i deneyimlediniz. Ancak kurumsal projelerde işler sadece veri ekleyip silmekten çok daha karmaşıktır.

Bu aşamada karşımıza çıkan en büyük problem, kod tabanının (codebase) büyümesiyle birlikte yönetilebilirliğin zorlaşmasıdır. Spagetti kod yapısından kurtulmak için MVC (Model-View-Controller) veya Clean Architecture (Temiz Mimari) gibi desenleri benimsemeniz gerekir. Bununla birlikte, asenkron işlemleri yönetmek ve hata kontrolü (error handling) mekanizmalarını merkezi hale getirmek projenizin sağlığı için hayati önem taşır.

Gelişmiş bir MERN uygulamasında, backend (arka uç) klasör yapınızın şu şekilde organize edilmesi sürdürülebilirliği artıracaktır:

  • config/: Veritabanı ve çevre değişkenleri yapılandırmaları.
  • controllers/: İstekleri işleyen ve yanıt dönen iş mantığı katmanı.
  • middlewares/: Kimlik doğrulama ve hata yakalama gibi ara yazılımlar.
  • models/: Veritabanı şemaları (Mongoose şemaları).
  • routes/: API uç noktalarının (endpoint) tanımlandığı katman.
  • utils/: Yardımcı fonksiyonlar ve servisler.

Bu yapı sayesinde kodunuzu modüler hale getirirsiniz. Böylece projenize yeni bir geliştirici dahil olduğunda, hangi kodun nerede olduğunu kolayca bulabilir.

Güvenliği Üst Seviyeye Taşımak: JWT Access ve Refresh Token Yapısı Nasıl Kurulur?

İlk dönemlerde kimlik doğrulama (authentication) işlemleri için sadece basit JWT (JSON Web Token) kullanmış olabilirsiniz. Fakat tek başına kullanılan ve süresi uzun olan Access Token (erişim belirteci) yapıları büyük bir güvenlik açığı oluşturur. Token çalındığı anda, kötü niyetli kişiler sisteme sınırsız erişim sağlayabilir. Bu sorunu çözmek için Access Token ve Refresh Token (yenileme belirteci) mekanizmasını birlikte kurmalıyız.

Sistem şu şekilde çalışır: Access Token kısa ömürlüdür (örneğin 15 dakika) ve bellek üzerinde (memory) tutulur. Refresh Token ise uzun ömürlüdür (örneğin 7 gün) ve HTTP-Only Cookie (yalnızca HTTP üzerinden erişilebilen çerez) olarak tarayıcıda saklanır. Access Token süresi dolduğunda, arka planda sessizce yeni bir Access Token talep edilir. Bu sayede kullanıcı deneyimi kesintiye uğramadan maksimum güvenlik sağlanır.

Aşağıdaki kod örneğinde, Express.js üzerinde bu yapının nasıl kurulacağını görebilirsiniz:

const jwt = require('jsonwebtoken');

// Access Token Oluşturma
const generateAccessToken = (user) => {
  return jwt.sign({ id: user._id, role: user.role }, process.env.ACCESS_TOKEN_SECRET, {
    expiresIn: '15m'
  });
};

// Refresh Token Oluşturma
const generateRefreshToken = (user) => {
  return jwt.sign({ id: user._id }, process.env.REFRESH_TOKEN_SECRET, {
    expiresIn: '7d'
  });
};

// Refresh Token Kontrolü ve Yeni Access Token Üretimi
const refreshAccessToken = async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  
  if (!refreshToken) {
    return res.status(401).json({ message: "Erişim yetkiniz yok, token bulunamadı." });
  }

  try {
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
    const newAccessToken = generateAccessToken({ _id: decoded.id, role: decoded.role });
    
    res.status(200).json({ accessToken: newAccessToken });
  } catch (error) {
    res.status(403).json({ message: "Geçersiz veya süresi dolmuş refresh token." });
  }
};

Bu yöntemle, kullanıcının hassas verilerini koruma altına alırken, Cross-Site Scripting (XSS – Siteler Arası Betik Çalıştırma) saldırılarına karşı da güçlü bir savunma hattı oluşturmuş olursunuz. Refresh token’ları veritabanında saklayarak, istediğiniz zaman bir kullanıcının tüm oturumlarını sonlandırma (revoke) yetkisine de sahip olabilirsiniz.

State Management (Durum Yönetimi) Savaşları: Redux Toolkit mi, Zustand mı?

React tarafında uygulama büyüdükçe bileşenler arası veri aktarımı (prop drilling) tam bir kabusa dönüşebilir. 145. güne geldiğinizde, muhtemelen Redux Toolkit ile tanışmışsınızdır. Redux Toolkit, React ekosistemindeki en güçlü ve yaygın State Management (durum yönetimi) kütüphanesidir. Ancak son yıllarda Zustand gibi daha hafif ve minimalist alternatifler de popülerlik kazanmıştır.

Hangi kütüphaneyi seçeceğiniz projenizin ölçeğine ve ekibinizin yapısına bağlıdır. Aşağıdaki tabloda bu iki popüler kütüphanenin temel farklarını inceleyelim:

Özellik Redux Toolkit Zustand
Öğrenme Eğrisi Orta / Zor Çok Kolay
Boilerplate (Şablon Kod) Fazla Çok Az
Paket Boyutu Büyük Çok Küçük (~1.5KB)
Performans Yüksek (Seçiciler ile) Yüksek (Doğal olarak hızlı)
Geliştirici Araçları Mükemmel (Redux DevTools) Destekliyor (Middleware ile)

Büyük ölçekli, çok sayıda geliştiricinin çalıştığı ve katı kurallara ihtiyaç duyulan kurumsal projelerde Redux Toolkit hala endüstri standardıdır. Ancak daha hızlı prototip üretmek istediğiniz, orta ölçekli veya kişisel projelerde Zustand kullanarak kod kalabalığından kurtulabilirsiniz. Zustand ile durum yönetimi kurmak sadece birkaç satır kod sürer:

import create from 'zustand';

const useStore = create((set) => ({
  user: null,
  setUser: (userData) => set({ user: userData }),
  logout: () => set({ user: null }),
}));

export default useStore;

Gördüğünüz gibi, hiçbir karmaşık aksiyon (action) veya indirgeyici (reducer) tanımlamadan doğrudan durumunuzu oluşturup bileşenlerinizde kullanmaya başlayabilirsiniz. Bu durum, geliştirme sürecinizi inanılmaz derecede hızlandırır.

MongoDB Performans Optimizasyonu: Indexing (Endeksleme) ve Aggregation (Kümeleme) Pipelines

Veritabanınızda sadece birkaç yüz kayıt varken yazdığınız sorguların hızı milisaniyeler seviyesindedir. Ancak veritabanınız büyüyüp milyonlarca satıra ulaştığında, optimize edilmemiş sorgular sunucunuzun kilitlenmesine neden olur. MongoDB’de performans optimizasyonunun kalbi Indexing (endeksleme) işlemidir.

Eğer kullanıcılarınızı sürekli e-posta adreslerine göre sorguluyorsanız ve bu alanda bir indeks tanımlamadıysanız, MongoDB her sorguda tüm koleksiyonu baştan sona tarar (Collection Scan). Bu sorunu çözmek için ilgili alana indeks eklemeliyiz. Mongoose üzerinde indeks tanımlamak oldukça basittir:

const userSchema = new mongoose.Schema({
  username: { type: String, required: true },
  email: { type: String, required: true, unique: true, index: true },
  createdAt: { type: Date, default: Date.now }
});

Bunun yanı sıra, karmaşık raporlama ve veri analizleri için Aggregation Pipelines (veri toplama boru hatları) kullanmalıyız. Aggregation, verileri veritabanı seviyesinde filtreleyip, gruplayıp, işleyerek Node.js sunucumuza sadece nihai sonucu göndermemizi sağlar. Bu sayede ağ trafiğini ve bellek kullanımını minimize ederiz.

Örneğin, bir e-ticaret uygulamasında kategorilere göre toplam satış miktarını hesaplayan bir aggregation sorgusu şu şekilde yazılabilir:

const getCategoryStats = async () => {
    return await Order.aggregate([
      { $match: { status: "completed" } }, // Sadece tamamlanan siparişleri filtrele
      { $unwind: "$products" }, // Ürünler dizisini tekil belgelere ayır
      { 
        $group: { 
          _id: "$products.category", 
          totalSales: { $sum: "$products.price" },
          totalQuantity: { $sum: 1 }
        } 
      },
      { $sort: { totalSales: -1 } } // En yüksek satıştan en düşüğe sırala
    ]);
};

Bu sorgu, milyonlarca sipariş verisini Node.js tarafına çekip Array.reduce() ile işlemek yerine, işlemi doğrudan MongoDB’nin optimize edilmiş motorunda gerçekleştirir ve bize saniyeler içinde sonucu döner.

Node.js ve Express Performansını Artırma: Caching (Önbelleğe Alma) ve Redis Entegrasyonu

Veritabanı sorgularını optimize etsek bile, bazı veriler çok sık değişmez ama çok sık talep edilir. Örneğin, bir e-ticaret sitesinin ana sayfasındaki kategoriler listesi veya en çok satan ürünler her saniye değişmez. Bu tür verileri her istekte veritabanından çekmek yerine Caching (önbelleğe alma) mekanizmasıyla bellekte tutmalıyız.

Redis, bu işlem için biçilmiş kaftandır. Bellek içi (in-memory) çalışan bir anahtar-değer (key-value) veritabanı olan Redis, mikro saniyeler seviyesinde okuma hızı sunar. Express.js projenizde Redis kullanarak veritabanı yükünüzü %80’e varan oranda azaltabilirsiniz.

Aşağıda, basit bir Redis önbellekleme ara yazılımı (middleware) örneği yer almaktadır:

const redis = require('redis');
const client = redis.createClient();

const cacheMiddleware = async (req, res, next) => {
  const { category } = req.query;
  const cacheKey = products:${category};

  try {
    const cachedData = await client.get(cacheKey);
    if (cachedData) {
      return res.status(200).json(JSON.parse(cachedData));
    }
    // Önbellekte yoksa bir sonraki fonksiyona geç (veritabanından çekecek)
    req.cacheKey = cacheKey;
    next();
  } catch (err) {
    next(); // Redis hatası durumunda uygulamayı durdurma, DB'den devam et
  }
};

Veritabanından veriyi çektikten sonra client.setEx(cacheKey, 3600, JSON.stringify(data)) komutuyla veriyi 1 saatliğine Redis’e kaydedebilirsiniz. Böylece sonraki istekler doğrudan bellekten karşılanacaktır.

Gerçek Dünya Senaryosu: Türkiye’deki Bir E-Ticaret Girişiminin Ölçeklenme Hikayesi

Gelin, öğrendiklerimizi somutlaştırmak için yerel bir vaka analizine (case study) göz atalım. Türkiye pazarında faaliyet gösteren ve MERN Stack kullanan “ModaTrend” adında bir giyim girişimi düşünelim. Şirket, büyük bir indirim dönemi olan “Efsane Cuma” kampanyasında saniyede binlerce istek almaya başladı. Ancak sistemleri bu yükü kaldıramadı ve çöktü.

Yapılan incelemelerde şu sorunlar tespit edildi:

  1. Kullanıcılar her ana sayfaya girdiğinde MongoDB’deki 50.000 ürün arasından rastgele sorgular yapılıyordu ve bu sorgularda indeks kullanılmıyordu.
  2. React tarafında, sepet güncellendiğinde tüm sayfa gereksiz yere yeniden işleniyordu (re-render) ve bu durum tarayıcıları kilitliyordu.
  3. Görseller optimize edilmeden doğrudan orijinal boyutlarıyla yüklendiği için bant genişliği (bandwidth) limitleri aşılmıştı.

Mühendislik ekibi, 145 günlük bir geliştiricinin sahip olması gereken vizyonla şu çözümleri uyguladı:

Öncelikle, MongoDB tarafında ürün arama sorguları için bileşik indeksler (compound indexes) oluşturuldu. Ardından, en çok ziyaret edilen kategori sayfaları için Redis önbellekleme sistemi devreye alındı. React tarafında ise gereksiz render işlemlerini engellemek amacıyla React.memo, useMemo ve useCallback kancaları (hooks) kullanıldı. Görseller için ise AWS S3 ve Cloudflare CDN (İçerik Dağıtım Ağı) entegrasyonu yapılarak yükleme süreleri 4 saniyeden 0.8 saniyeye düşürüldü.

Sonuç olarak, ModaTrend bir sonraki indirim dönemini sıfır kesinti ve %30 daha az sunucu maliyetiyle tamamlamayı başardı. Bu vaka analizi bize, doğru mimari kararların şirketlerin kaderini nasıl etkilediğini açıkça göstermektedir.

Canlıya Alış (Deployment) Süreçleri: PM2, Nginx ve Docker ile Üretime Hazırlık

Uygulamanızı yerel bilgisayarınızda (localhost) çalıştırmak kolaydır. Ancak onu gerçek kullanıcıların erişebileceği güvenli ve kesintisiz bir sunucu ortamına taşımak tamamen farklı bir uzmanlık gerektirir. Üretim ortamında (production environment) Node.js uygulamanızın arka planda sürekli çalışmasını sağlamak için PM2 gibi bir Process Manager (süreç yöneticisi) kullanmalısınız.

PM2, uygulamanız çöktüğünde onu otomatik olarak yeniden başlatır ve sunucunun tüm işlemci çekirdeklerini (cluster mode) kullanmanıza olanak tanır. Sunucu önünde ise gelen istekleri yönlendirmek, SSL sertifikalarını yönetmek ve statik dosyaları hızlıca sunmak için Nginx’i Reverse Proxy (ters vekil sunucu) olarak konumlandırmalısınız.

Eğer uygulamanızın farklı sunucularda da her zaman aynı şekilde çalışmasını istiyorsanız, Docker kullanarak konteynerleştirme (containerization) yapmalısınız. Docker, “benim bilgisayarımda çalışıyordu ama sunucuda çalışmıyor” sorununu tamamen ortadan kaldırır.

MERN projenizin backend kısmı için hazırlayabileceğiniz örnek bir Dockerfile şu şekildedir:

FROM node:18-alpine

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install --only=production

COPY . .

EXPOSE 5000

CMD ["node", "server.js"]

Bu Dockerfile, uygulamanız için hafif bir Linux imajı oluşturur, sadece üretim bağımlılıklarını kurar ve uygulamanızı güvenli bir şekilde çalıştırır. Bu sayede projenizi AWS, DigitalOcean veya Heroku gibi bulut sağlayıcılara saniyeler içinde sorunsuz bir şekilde dağıtabilirsiniz.

Sıkça Sorulan Sorular (FAQ)

MERN Stack projelerinde SEO sorununu nasıl çözerim?

React varsayılan olarak bir Single Page Application (SPA – Tek Sayfa Uygulaması) olduğu için istemci tarafında render (client-side rendering) yapar. Bu durum arama motoru botlarının sitenizi taramasını zorlaştırır. Çözüm olarak, Next.js framework’üne geçiş yaparak Server-Side Rendering (SSR – Sunucu Taraflı İşleme) kullanabilir veya React Helmet ve prerendering servislerinden yararlanabilirsiniz.

MongoDB şifrelerimi ve API anahtarlarımı nasıl korumalıyım?

Kesinlikle hassas bilgilerinizi kodunuzun içine doğrudan yazmayın ve GitHub gibi platformlara yüklemeyin. Bunun yerine projenizin kök dizininde bir .env dosyası oluşturup bilgileri burada saklayın ve bu dosyayı .gitignore dosyasına ekleyin. Node.js tarafında bu verilere ulaşmak için dotenv kütüphanesini kullanabilirsiniz.

MERN Stack öğrenirken SQL veritabanlarını da öğrenmeli miyim?

Evet, kesinlikle öğrenmelisiniz. MongoDB harika bir NoSQL veritabanıdır ancak ilişkisel veri modelleri (örneğin finansal sistemler, karmaşık kullanıcı ilişkileri) için PostgreSQL veya MySQL gibi SQL veritabanları çok daha uygundur. Profesyonel bir MERN geliştiricisi, projenin ihtiyacına göre doğru veritabanı teknolojisini seçebilmelidir.

#MERNStack #WebGeliştirme #Yazılım #ReactJS #NodeJS

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.