Takip et

Full-Stack Finans Takip Uygulaması Geliştirme: MERN Yolculuğum (Gerçek Problemler ve Çözümleri) 💰

Her büyük projenin temelinde güçlü bir fikir yatar. Benim için bu fikir, gelirlerimi, giderlerimi ve birikimlerimi tek bir yerden, kolayca takip edebil…

Full-Stack Finans Takip Uygulaması Geliştirme: MERN Yolculuğum (Gerçek Problemler ve Çözümleri) 💰

Günümüz dünyasında kişisel finans yönetimi, bireylerin ekonomik hedeflerine ulaşmasında kritik bir rol oynamaktadır. Piyasada birçok finans takip uygulaması bulunsa da, çoğu zaman belirli özellik setleriyle sınırlı kalmakta veya kişisel ihtiyaçlara tam olarak yanıt verememektedir. Bu durum, kendi finansal verilerim üzerinde tam kontrol sahibi olma ve tamamen kişiselleştirilmiş bir deneyim yaratma arzumu tetikledi. Bu makalede, bir MERN (MongoDB, Express.js, React, Node.js) yığını kullanarak sıfırdan bir finans takip uygulaması geliştirme yolculuğumu, bu süreçte karşılaştığım gerçek problemleri ve onlara bulduğum pratik çözümleri detaylı bir şekilde ele alacağım.

1. Proje Fikri ve MERN Stack Seçimi

Her büyük projenin temelinde güçlü bir fikir yatar. Benim için bu fikir, gelirlerimi, giderlerimi ve birikimlerimi tek bir yerden, kolayca takip edebileceğim, görsel raporlarla desteklenmiş, tamamen bana özel bir platform yaratmaktı. Mevcut uygulamaların abonelik modelleri, veri gizliliği endişeleri veya arayüz kısıtlamaları beni kendi çözümümü geliştirmeye itti.

1.1. Neden Kendi Uygulamam?

Piyasada Mint, YNAB gibi güçlü finans uygulamaları mevcut. Ancak bu uygulamaların çoğu, belirli bir coğrafyaya veya bankacılık sistemine odaklanmış durumda. Ayrıca, verilerimin üçüncü taraf bir şirketin sunucularında tutulması, benim için her zaman bir soru işaretiydi. Kendi uygulamamı geliştirerek, verilerimin nerede ve nasıl saklandığı üzerinde tam kontrol sahibi olabiliyor, istediğim özellikleri ekleyip çıkarabiliyor ve tamamen kişiselleştirilmiş bir kullanıcı deneyimi sunabiliyordum. Bu, sadece bir finans takip aracı değil, aynı zamanda yazılım geliştirme becerilerimi derinleştireceğim bir öğrenme projesi olacaktı.

1.2. MERN Stack Neden İdeal Bir Seçim?

MERN stack, modern full-stack web uygulamaları geliştirmek için popüler ve güçlü bir seçimdir. Tamamen JavaScript tabanlı olması, hem frontend hem de backend geliştirme için tek bir dil bilmenin getirdiği kolaylığı sunar. Bu, geliştirme sürecini hızlandırır ve bağlam geçişlerini azaltır.

  • MongoDB: Esnek, belge tabanlı bir NoSQL veritabanı. Finansal işlemler gibi dinamik ve değişen veri yapıları için mükemmeldir. Şema esnekliği, hızlı prototipleme imkanı sunar.
  • Express.js: Node.js üzerinde çalışan minimalist ve esnek bir web uygulama çatısı. API endpoint’leri oluşturmak ve sunucu tarafı mantığını yönetmek için idealdir.
  • React: Kullanıcı arayüzleri oluşturmak için bildirimsel ve bileşen tabanlı bir JavaScript kütüphanesi. Dinamik ve etkileşimli finansal panolar oluşturmak için harikadır.
  • Node.js: Sunucu tarafı JavaScript çalışma zamanı. Ölçeklenebilir ağ uygulamaları geliştirmek için kullanılır.

Bu bileşenlerin bir araya gelmesi, hızlı, ölçeklenebilir ve bakımı kolay bir finans takip uygulaması oluşturmak için güçlü bir temel sağlar.

1.3. Projenin Temel Özellikleri

Uygulamamın temel özellikleri şunları içerecekti:

  • Gelir ve Gider Takibi: Kullanıcıların tüm finansal işlemlerini (gelir, gider, transfer) kolayca kaydetmesi.
  • Kategori Yönetimi: İşlemleri kategorize etme (yemek, ulaşım, faturalar vb.) ve özel kategoriler oluşturma.
  • Bütçeleme: Belirli kategoriler veya dönemler için bütçeler belirleme ve harcamaları takip etme.
  • Görsel Raporlar: Pasta grafikleri, çubuk grafikler ve çizgi grafikler aracılığıyla finansal durumu görselleştirme.
  • Kullanıcı Kimlik Doğrulama: Güvenli kayıt ve giriş sistemi.

2. Backend Geliştirme: API ve Veritabanı Yönetimi

Uygulamanın beyni olan backend, verilerin güvenli bir şekilde depolanmasını, işlenmesini ve frontend’e sunulmasını sağlar. Express.js ve MongoDB ile bu katmanı inşa ettim.

2.1. Veritabanı Modelleri ve İlişkiler

MongoDB, şemasız bir veritabanı olmasına rağmen, Mongoose gibi bir ODM (Object Data Modeling) kütüphanesi kullanarak veri yapılarımızı tanımlamak, kod okunabilirliğini ve veri tutarlılığını artırır. Başlangıçta basit bir yapı düşündüm, ancak zamanla kullanıcı, işlem ve kategori modellerinin ilişkileri daha karmaşık hale geldi.

Problem: Veri tutarlılığı ve ilişkisel veri modellemesi eksikliği. MongoDB şemasız olduğu için, hatalı veri girişleri veya eksik alanlar oluşabiliyordu.

Çözüm: Mongoose ile güçlü şemalar tanımlamak ve her alan için tip, zorunluluk, varsayılan değerler gibi kısıtlamalar eklemek. Örneğin, bir işlem modeli şöyle tanımlanabilir:


const mongoose = require('mongoose');

const transactionSchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    type: {
        type: String,
        enum: ['income', 'expense', 'transfer'],
        required: true
    },
    amount: {
        type: Number,
        required: true,
        min: 0.01
    },
    category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Category',
        required: true
    },
    description: {
        type: String,
        trim: true
    },
    date: {
        type: Date,
        default: Date.now
    }
}, { timestamps: true });

module.exports = mongoose.model('Transaction', transactionSchema);

Bu şema, her işlemin bir kullanıcıya, türe, miktara ve kategoriye sahip olmasını zorunlu kılar, böylece veri tutarlılığını artırır.

2.2. RESTful API Tasarımı

Express.js ile RESTful API endpoint'leri oluşturmak oldukça basittir. Her kaynak (kullanıcı, işlem, kategori) için CRUD (Create, Read, Update, Delete) operasyonlarını destekleyen endpoint'ler tanımladım.

Problem: API endpoint'lerinin karmaşıklaşması ve veri doğrulama eksikliği.

Çözüm: Endpoint'leri mantıksal olarak gruplandırmak (örn. /api/transactions, /api/categories). Gelen verileri doğrulamak için express-validator veya Joi gibi kütüphaneler kullanmak. Bu, hem güvenlik hem de veri kalitesi açısından önemlidir.


// Örnek bir işlem ekleme endpoint'i
const express = require('express');
const router = express.Router();
const { body, validationResult } = require('express-validator');
const auth = require('../middleware/auth'); // Kimlik doğrulama middleware'i
const Transaction = require('../models/Transaction');

router.post(
    '/',
    [
        auth, // Kullanıcının oturum açmış olduğunu doğrula
        body('type').isIn(['income', 'expense', 'transfer']).withMessage('Geçersiz işlem tipi.'),
        body('amount').isFloat({ gt: 0 }).withMessage('Miktar pozitif bir sayı olmalıdır.'),
        body('category').isMongoId().withMessage('Geçersiz kategori ID\'si.')
    ],
    async (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }

        try {
            const newTransaction = new Transaction({
                userId: req.user.id,
                ...req.body
            });
            const transaction = await newTransaction.save();
            res.status(201).json(transaction);
        } catch (err) {
            console.error(err.message);
            res.status(500).send('Sunucu Hatası');
        }
    }
);

module.exports = router;

2.3. Kimlik Doğrulama ve Yetkilendirme

Kullanıcıların kendi verilerine erişmesini sağlamak ve başkalarının verilerine erişmesini engellemek için güçlü bir kimlik doğrulama ve yetkilendirme sistemi şarttı.

Problem: Güvenli kullanıcı oturum yönetimi ve yetkisiz erişim.

Çözüm: JSON Web Tokens (JWT) kullanmak. Kullanıcı giriş yaptığında, sunucu bir JWT oluşturur ve istemciye gönderir. İstemci, sonraki tüm isteklerde bu token'ı gönderir ve sunucu, token'ı doğrulayarak kullanıcının kimliğini ve yetkisini kontrol eder. Şifreleri güvenli bir şekilde saklamak için bcrypt.js kullanarak hash'leme yaptım.


// auth middleware örneği
const jwt = require('jsonwebtoken');
const config = require('config');

module.exports = function(req, res, next) {
    // Token'ı header'dan al
    const token = req.header('x-auth-token');

    // Token yoksa hata döndür
    if (!token) {
        return res.status(401).json({ msg: 'Yetkilendirme reddedildi, token yok' });
    }

    // Token'ı doğrula
    try {
        const decoded = jwt.verify(token, config.get('jwtSecret'));
        req.user = decoded.user;
        next();
    } catch (err) {
        res.status(401).json({ msg: 'Token geçerli değil' });
    }
};

3. Frontend Geliştirme: Kullanıcı Deneyimi ve Veri Görselleştirme

React ile kullanıcı dostu ve etkileşimli bir arayüz oluşturmak, uygulamanın benimsenmesi açısından kritikti. Veri görselleştirme, finansal durumu anlamayı kolaylaştırdı.

3.1. React Bileşen Mimarisi

Uygulamayı modüler ve yeniden kullanılabilir bileşenlere ayırmak, geliştirme sürecini düzenli tutar ve bakımı kolaylaştırır. Ana bileşenler: Dashboard, TransactionForm, TransactionList, CategoryManager, BudgetTracker, ChartComponents.

Problem: Bileşenler arası veri paylaşımı ve state yönetimi karmaşıklığı.

Çözüm: Başlangıçta prop drilling ile uğraşırken, daha sonra React Context API'ı veya Redux gibi bir state yönetim kütüphanesi kullanmaya karar verdim. Context API, küçük ve orta ölçekli uygulamalar için yeterli esnekliği ve basitliği sunar.


// Örnek bir React bileşeni: TransactionList
import React, { useContext, useEffect } from 'react';
import { GlobalContext } from '../context/GlobalState';
import TransactionItem from './TransactionItem';

const TransactionList = () => {
    const { transactions, getTransactions } = useContext(GlobalContext);

    useEffect(() => {
        getTransactions();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    return (
        

Geçmiş İşlemler

    {transactions.map(transaction => ( ))}
); }; export default TransactionList;

3.2. Veri Çekme ve Yönetimi

Frontend'in backend ile iletişim kurması için HTTP istekleri yapması gerekir. Axios, bu amaçla yaygın olarak kullanılan Promise tabanlı bir HTTP istemcisidir.

Problem: Asenkron veri çekme işlemlerinde yükleme durumları, hatalar ve verinin güncellenmesi.

Çözüm: Axios kullanarak API çağrılarını yönetmek. Her çağrı için try-catch blokları ile hata yakalamak ve kullanıcıya geri bildirim sağlamak. Yükleme durumlarını (loading state) yönetmek, kullanıcı deneyimini iyileştirir.


// GlobalState.js içinde bir örnek:
import React, { createContext, useReducer } from 'react';
import axios from 'axios';

// Initial state
const initialState = {
    transactions: [],
    error: null,
    loading: true
};

// Reducer
export const AppReducer = (state, action) => {
    switch (action.type) {
        case 'GET_TRANSACTIONS':
            return {
                ...state,
                loading: false,
                transactions: action.payload
            };
        case 'TRANSACTION_ERROR':
            return {
                ...state,
                error: action.payload
            };
        // ... diğer action'lar
        default:
            return state;
    }
};

// Create context
export const GlobalContext = createContext(initialState);

// Provider component
export const GlobalProvider = ({ children }) => {
    const [state, dispatch] = useReducer(AppReducer, initialState);

    // Actions
    async function getTransactions() {
        try {
            const res = await axios.get('/api/transactions');
            dispatch({
                type: 'GET_TRANSACTIONS',
                payload: res.data.data
            });
        } catch (err) {
            dispatch({
                type: 'TRANSACTION_ERROR',
                payload: err.response.data.error
            });
        }
    }
    // ... diğer action'lar

    return (
        
            {children}
        
    );
};

3.3. Görselleştirme ve Grafik Kütüphaneleri

Finansal verilerin görselleştirilmesi, kullanıcıların harcama alışkanlıklarını ve finansal durumlarını hızlıca anlamalarını sağlar. Pasta grafikleri, çubuk grafikler ve çizgi grafikler bu amaçla kullanışlıdır.

Problem: Verileri anlamlı grafiklere dönüştürmek ve arayüze entegre etmek.

Çözüm: Chart.js veya Recharts gibi popüler grafik kütüphanelerini kullanmak. Ben, basitliği ve esnekliği nedeniyle Chart.js'i tercih ettim. İşlem verilerini gruplandırarak (örn. kategoriye göre harcamalar, aya göre gelir/gider) grafiklere uygun formatta hazırladım.


// Örnek bir pasta grafiği bileşeni (Chart.js ile)
import React, { useEffect, useRef } from 'react';
import Chart from 'chart.js/auto'; // Chart.js v3 ve sonrası için 'auto'

const ExpenseByCategoryChart = ({ transactions }) => {
    const chartRef = useRef(null);
    const chartInstance = useRef(null); // Chart örneğini saklamak için

    useEffect(() => {
        if (chartInstance.current) {
            chartInstance.current.destroy(); // Önceki grafiği yok et
        }

        const ctx = chartRef.current.getContext('2d');

        // İşlemleri kategoriye göre gruplandır
        const categoryExpenses = transactions.reduce((acc, transaction) => {
            if (transaction.type === 'expense') {
                const categoryName = transaction.category.name || 'Diğer'; // Kategori adını varsayalım
                acc[categoryName] = (acc[categoryName] || 0) + transaction.amount;
            }
            return acc;
        }, {});

        const labels = Object.keys(categoryExpenses);
        const data = Object.values(categoryExpenses);

        chartInstance.current = new Chart(ctx, {
            type: 'pie',
            data: {
                labels: labels,
                datasets: [{
                    data: data,
                    backgroundColor: [
                        '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'
                    ],
                    hoverOffset: 4
                }]
            },
            options: {
                responsive: true,
                plugins: {
                    legend: {
                        position: 'top',
                    },
                    title: {
                        display: true,
                        text: 'Kategoriye Göre Harcamalar'
                    }
                }
            }
        });

        return () => {
            if (chartInstance.current) {
                chartInstance.current.destroy(); // Bileşen kaldırıldığında grafiği yok et
            }
        };
    }, [transactions]);

    return ;
};

export default ExpenseByCategoryChart;

4. Gerçek Hayat Problemleri ve Çözümleri

Geliştirme süreci boyunca karşılaştığım bazı önemli zorluklar ve onlara bulduğum çözümler:

4.1. Veri Tutarlılığı ve Doğrulama Zorlukları

Problem: Kullanıcıların hatalı veya eksik veri girmesi, backend'de veri tabanına yanlış verilerin kaydedilmesi. Örneğin, negatif bir miktar girilmesi veya geçersiz bir kategori seçilmesi.

Çözüm: Hem frontend hem de backend'de katı doğrulama kuralları uygulamak. Frontend'de form doğrulama ile kullanıcıya anında geri bildirim sağlamak. Backend'de ise express-validator veya Mongoose şema doğrulama ile veritabanına kaydedilmeden önce verileri kontrol etmek. Bu, uygulamanın genel güvenilirliğini artırır.

4.2. Asenkron İşlemler ve Hata Yönetimi

Problem: JavaScript'in asenkron doğası nedeniyle API çağrılarında veya veritabanı işlemlerinde oluşan hataları yönetmek karmaşık olabilir. Kullanıcı arayüzünde takılmalar veya belirsiz hata mesajları oluşabilir.

Çözüm: Modern JavaScript'in async/await sözdizimini kullanarak asenkron kodları daha okunabilir hale getirmek. Her asenkron çağrıyı try-catch blokları içine almak ve hataları uygun HTTP durum kodlarıyla (örn. 400, 401, 500) döndürmek. Frontend'de, bu hataları yakalayıp kullanıcıya anlaşılır mesajlarla sunmak.

4.3. Performans Engelleri

Problem: Zamanla kullanıcıların işlem sayısı arttıkça, tüm verileri tek seferde çekmek veya işlemek performansı düşürebilir. Özellikle raporlama ekranlarında bu durum belirginleşir.

Çözüm:

  • Pagination (Sayfalama): Büyük veri setlerini küçük parçalar halinde çekmek. Örneğin, işlem listesini 10'arlı veya 20'şerli sayfalara ayırmak.
  • Lazy Loading: Sadece ihtiyaç duyulan bileşenleri veya verileri yüklemek.
  • Veritabanı İndeksleme: MongoDB'de sık sorgulanan alanlara indeksler eklemek (örn. userId, date) sorgu sürelerini önemli ölçüde azaltır.
  • Backend'de Veri Agregasyonu: Raporlar için gerekli olan özet verileri (örn. aylık toplam harcamalar) doğrudan veritabanı seviyesinde hesaplayıp göndermek, frontend'deki işlem yükünü azaltır.

4.4. Kullanıcı Arayüzü Duyarlılığı (Responsive Design)

Problem: Uygulamanın farklı ekran boyutlarında (mobil, tablet, masaüstü) düzgün görünmemesi.

Çözüm: CSS framework'leri (Tailwind CSS, Material-UI veya Bootstrap) kullanarak responsive tasarım prensiplerini uygulamak. Flexbox ve CSS Grid gibi modern CSS özelliklerinden faydalanmak. Mobil öncelikli tasarım yaklaşımı benimsemek, küçük ekranlardan başlayarak daha büyük ekranlara doğru tasarımları genişletmek.

5. Güvenlik ve Performans Optimizasyonu

Uygulamanın uzun ömürlü ve güvenilir olması için güvenlik ve performans sürekli göz önünde bulundurulması gereken konulardır.

5.1. Güvenlik Önlemleri

Finansal veriler hassas olduğu için güvenlik en yüksek önceliklerden biridir.

  • Şifre Hashleme: Kullanıcı şifrelerini asla düz metin olarak saklamamak. bcrypt.js kullanarak şifreleri güvenli bir şekilde hash'lemek.
  • JWT Güvenliği: JWT'leri HTTP Only çerezlerinde saklamak (XSS saldırılarına karşı), kısa ömürlü token'lar kullanmak ve refresh token mekanizması eklemek.
  • Ortam Değişkenleri: Hassas bilgileri (veritabanı bağlantı dizeleri, JWT sırları) doğrudan koda yazmak yerine ortam değişkenlerinde (.env dosyası) saklamak.
  • CORS Politikaları: Yalnızca izin verilen alan adlarından gelen isteklere yanıt vermek için CORS (Cross-Origin Resource Sharing) yapılandırması.
  • Veri Şifreleme: Hassas finansal verilerin (örn. banka hesap numaraları, eğer saklanıyorsa) veritabanında şifrelenerek saklanması.

5.2. Performans İyileştirmeleri

Uygulamanın hızlı ve akıcı çalışması kullanıcı memnuniyeti için esastır.

  • Veritabanı İndeksleme: Daha önce de belirtildiği gibi, sorgu performansını artırmak için MongoDB indekslerini doğru kullanmak.
  • Önbellekleme (Caching): Sık erişilen ancak nadiren değişen verileri (örn. kategori listeleri) sunucu tarafında veya istemci tarafında önbelleğe almak. Redis gibi bir önbellek sunucusu kullanılabilir.
  • Gereksiz Yeniden Render'ları Engelleme: React'ta React.memo, useCallback ve useMemo hook'larını kullanarak bileşenlerin gereksiz yere yeniden render edilmesini engellemek.
  • Resim Optimizasyonu: Eğer varsa, resimleri optimize etmek ve uygun formatlarda (WebP) sunmak.
  • Kod Bölme (Code Splitting): Frontend uygulamasının başlangıç yükleme süresini azaltmak için kodu daha küçük parçalara bölmek ve sadece ihtiyaç duyulduğunda yüklemek.

5.3. Hata Kaydı ve İzleme

Problem: Canlıya alınan uygulamada oluşan hataları tespit etmek ve gidermek.

Çözüm: Sunucu tarafında Winston gibi bir loglama kütüphanesi kullanarak hataları, uyarıları ve bilgi mesajlarını kaydetmek. Frontend'de ise Sentry gibi hata izleme araçlarını entegre etmek, kullanıcı tarafında oluşan hataları gerçek zamanlı olarak takip etmeyi sağlar.

6. Dağıtım (Deployment) ve Sonraki Adımlar

Uygulamayı geliştirmek kadar, onu canlıya almak ve sürdürmek de önemlidir.

6.1. Uygulamayı Canlıya Alma

Problem: Backend ve frontend'i ayrı ayrı veya birlikte nasıl barındıracağımı seçmek.

Çözüm: Birçok seçenek mevcut:

  • Heroku: Hızlı prototipleme ve küçük projeler için ideal, ancak ücretsiz katmanı artık yok.
  • Vercel (Frontend) ve Render/Railway (Backend/DB): Modern ve kullanımı kolay platformlar. Frontend için Vercel, serverless fonksiyonlar ve hızlı dağıtım sunar. Backend için Render veya Railway, veritabanı ve Node.js uygulamaları için uygun.
  • AWS EC2/DigitalOcean Droplet: Daha fazla kontrol ve özelleştirme isteyenler için sanal sunucular. Manuel kurulum gerektirir.

Ben, frontend için Vercel'i, backend ve MongoDB için ise Render'ı tercih ettim. Bu platformlar, CI/CD (Sürekli Entegrasyon/Sürekli Dağıtım) süreçlerini otomatikleştirmeyi kolaylaştırır, bu da her kod değişikliğinde uygulamanın otomatik olarak dağıtılmasını sağlar.

6.2. Sürekli Geliştirme ve Bakım

Bir yazılım projesi asla "tamamlanmış" sayılmaz. Sürekli geliştirme ve bakım, uygulamanın güncel, güvenli ve kullanışlı kalmasını sağlar.

  • Yeni Özellikler: Kullanıcı geri bildirimlerine veya kendi ihtiyaçlarıma göre yeni özellikler eklemek (örn. banka entegrasyonu, fatura hatırlatıcıları).
  • Bug Düzeltmeleri: Ortaya çıkan hataları düzenli olarak düzeltmek.
  • Bağımlılık Güncellemeleri: Projedeki tüm kütüphaneleri ve framework'leri güncel tutmak, güvenlik açıklarını kapatmak ve yeni özelliklerden faydalanmak.

6.3. Gelecek Potansiyeli

Bu finans takip uygulaması, basit bir başlangıç noktasıydı ancak gelecekte birçok potansiyel geliştirme alanı sunuyor:

  • Banka Entegrasyonları: Open Banking API'ları veya üçüncü taraf entegrasyon servisleri (örn. Plaid) arac
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

Gönder

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.
Mobil sürümden çık