Next.js ve MongoDB ile Flashcard Uygulaması Geliştirin

Next.js ve MongoDB ile Flashcard Uygulaması Geliştirin

Next.js ve MongoDB ile Flashcard Uygulaması Geliştirin

Sınav öncesi gece geç saatlere kadar çalışıp her şeyi ezberlemeye çalıştıysanız, ne kadar zor olduğunu bilirsiniz. Flashcard'lar (kart çalışma yöntemi) en etkili çalışma araçlarından biridir. Çünkü **aktif hatırlama** prensibini kullanırlar — beyninizi bilgiyi pasif bir şekilde tekrar okumak yerine, aktif olarak çekmeye zorlarlar.

Dijital bir flashcard uygulaması geliştirmek, modern web geliştirmeyi eğitim psikolojisiyle birleştirir. Bu rehberde, Next.js (frontend ve backend için) ve MongoDB (veri depolama için) kullanarak tam yığın (full-stack) bir flashcard uygulaması nasıl oluşturulur, adım adım anlatacağım.

Neden Flashcard Uygulaması Geliştirelim?

Dijital flashcard'lar, fiziksel kartlara göre birkaç avantaj sunar:

- **Taşınabilirlik**: İnternet varsa dükkanlarınız her yerden erişilebilir
- **Aralıklı tekrar (spaced repetition)**: Algoritmalar tekrar zamanlamasını optimize edebilir
- **Medya desteği**: Görseller, ses dosyaları ve formatlama
- **İlerleme takibi**: Öğrenme performansınız hakkında analitik veriler

Gerçek Hayat Kullanım Senaryoları

Koda dalmadan önce, uygulamamızın parladığı bu senaryoları düşünün:

1. **Tıp Öğrencileri**: Görsellerle detaylı anatomi flashcard'ları oluşturmak
2. **Dil Öğrenenler**: Telaffuz ses dosyalarıyla kelime hazinesi dükkanları hazırlamak
3. **Profesyonel Sertifikasyon**: Karmaşık teknik kavramları sistematik olarak organize etmek

Teknoloji Yığını (Tech Stack) Özeti

Kullanacağımız araçlar:

- **Next.js 14** (Sunucu tarafı renderlama özelliğine sahip React framework'ü)
- **MongoDB Atlas** (Bulut veritabanı)
- **Mongoose** (MongoDB nesne modelleme aracı)
- **Tailwind CSS** (Styling için)

Neden bu yığın? Next.js hem frontend'i hem de API route'larını yönetir, bu da deployment'ı basitleştirir. MongoDB'nin esnek şeması, değişen flashcard yapıları için uygundur.

Projeyi Kurma

Önce yeni bir Next.js projesi oluşturalım:

```bash
npx create-next-app@latest flashcard-app
cd flashcard-app
npm install mongoose mongodb
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

`tailwind.config.js` dosyasını yapılandıralım:

```javascript
/** @type {import('tailwindcss').tailwind.config} */
module.exports = {
content: [
"./app/**/*.{js,jsx,ts,jsx}",
"./pages/**/*.{js,jsx,ts,jsx}",
"./components/**/*.{js,jsx,ts,jsx}",
],
theme: {
extend: {},
},
plugins: [],
}
```

`app/globals.css` dosyasına ekleyin:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Veritabanı Bağlantısı

`lib/mongodb.js` dosyasını oluşturun:

```javascript
import { MongoClient } from 'mongodb'

const uri = process.env.MONGODB_URI
const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
}

let client
let clientPromise

if (!process.env.MONGODB_URI) {
throw new Error('Lütfen MongoDB URI\'nizi .env.local dosyasına ekleyin')
}

if (process.env.NODE_ENV === 'development') {
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
client = new MongoClient(uri, options)
clientPromise = client.connect()
}

export default clientPromise
```

Flashcard Modeli Oluşturma

`models/Flashcard.js` dosyasında:

```javascript
import mongoose from 'mongoose'

const flashcardSchema = new mongoose.Schema({
question: {
type: String,
required: true,
},
answer: {
type: String,
required: true,
},
deck: {
type: String,
required: true,
},
tags: [String],
createdAt: {
type: Date,
default: Date.now,
},
})

export default mongoose.models.Flashcard || mongoose.model('Flashcard', flashcardSchema)
```

API Route'ları

Next.js API route'ları backend mantığımızı yönetir. `pages/api/flashcards/index.js` oluşturun:

```javascript
import dbConnect from '../../../lib/dbConnect'
import Flashcard from '../../../models/Flashcard'

export default async function handler(req, res) {
const { method } = req

await dbConnect()

switch (method) {
case 'GET':
try {
const flashcards = await Flashcard.find({})
res.status(200).json(flashcards)
} catch (error) {
res.status(400).json({ success: false })
}
break
case 'POST':
try {
const flashcard = await Flashcard.create(req.body)
res.status(201).json(flashcard)
} catch (error) {
res.status(400).json({ success: false })
}
break
default:
res.status(400).json({ success: false })
break
}
}
```

Frontend Bileşenleri

Form bileşeni `components/FlashcardForm.js`:

```javascript
import { useState } from 'react'

export default function FlashcardForm({ onSubmit }) {
const [question, setQuestion] = useState('')
const [answer, setAnswer] = useState('')
const [deck, setDeck] = useState('')

const handleSubmit = async (e) => {
e.preventDefault()
const response = await fetch('/api/flashcards', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question, answer, deck })
})

if (response.ok) {
const newFlashcard = await response.json()
onSubmit(newFlashcard)
setQuestion('')
setAnswer('')
setDeck('')
}
}

return (



value={question}
onChange={(e) => setQuestion(e.target.value)}
required
className="w-full p-2 border rounded"
/>



value={answer}
onChange={(e) => setAnswer(e.target.value)}
required
className="w-full p-2 border rounded"
/>



type="text"
value={deck}
onChange={(e) => setDeck(e.target.value)}
required
className="w-full p-2 border rounded"
/>



)
}
```

Çalışma Arayüzü (Study Mode)

Aktif hatırlama pratiği için `components/StudyMode.js`:

```javascript
import { useState, useEffect } from 'react'

export default function StudyMode({ flashcards }) {
const [currentIndex, setCurrentIndex] = useState(0)
const [showAnswer, setShowAnswer] = useState(false)
const [shuffledCards, setShuffledCards] = useState([])

useEffect(() => {
setShuffledCards([...flashcards].sort(() => Math.random() - 0.5))
}, [flashcards])

const nextCard = () => {
setShowAnswer(false)
if (currentIndex < shuffledCards.length - 1) {
setCurrentIndex(currentIndex + 1)
}
}

const prevCard = () => {
setShowAnswer(false)
if (currentIndex > 0) {
setCurrentIndex(currentIndex - 1)
}
}

if (!shuffledCards.length) return

Kullanılabilir flashcard yok



const card = shuffledCards[currentIndex]

return (


Soru:


{card.question}




{showAnswer && (

Cevap:


{card.answer}



)}





{currentIndex > 0 && (

)}
{currentIndex < shuffledCards.length - 1 && (

)}




{currentIndex + 1} / {shuffledCards.length}



)
}
```

Ana Uygulama Sayfası

`app/page.js` oluşturun:

```javascript
import { useState, useEffect } from 'react'
import FlashcardForm from '../components/FlashcardForm'
import StudyMode from '../components/StudyMode'

export default function Home() {
const [flashcards, setFlashcards] = useState([])
const [activeView, setActiveView] = useState('create')
const [selectedDeck, setSelectedDeck] = useState('')

useEffect(() => {
fetchFlashcards()
}, [])

const fetchFlashcards = async () => {
const response = await fetch('/api/flashcards')
const data = await response.json()
setFlashcards(data)
}

const addFlashcard = (newFlashcard) => {
setFlashcards([newFlashcard, ...flashcards])
}

const filteredCards = selectedDeck
? flashcards.filter(card => card.deck === selectedDeck)
: flashcards

const decks = [...new Set(flashcards.map(card => card.deck))]

return (


Flashcard Çalışma Uygulaması




onClick={() => setActiveView('create')}
className={`px-4 py-2 mr-2 rounded ${
activeView === 'create' ? 'bg-blue-500 text-white' : 'bg-white'
}`}
>
Flashcard Oluştur

onClick={() => setActiveView('study')}
className={`px-4 py-2 rounded ${
activeView === 'study' ? 'bg-blue-500 text-white' : 'bg-white'
}`}
disabled={flashcards.length === 0}
>
Çalışma Modu



{activeView === 'create' && (



)}

{activeView === 'study' && (


value={selectedDeck}
onChange={(e) => setSelectedDeck(e.target.value)}
className="w-full p-2 border rounded"
>

{decks.map(deck => (

))}


)}

{activeView === 'study' && }


Dükkanlarınız


{decks.length === 0 ? (

Henüz dükkan yok. Yukarıdan ilk flashcard'ınızı oluşturun!


) : (

    {decks.map(deck => (

  • {deck} ({flashcards.filter(c => c.deck === deck).length} kart)

  • ))}

)}



)
}
```

Deployment İçin Düşünülmesi Gerekenler

Production ortamı için şu geliştirmeleri düşünebilirsiniz:

1. **Kimlik Doğrulama**: NextAuth.js ile kullanıcı hesapları ekleyin
2. **Aralıklı Tekrar (Spaced Repetition)**: SM-2 gibi algoritmaları implement edin
3. **Zengin Medya**: Flashcard'lara görsel ve ses desteği ekleyin
4. **Çevrimdışı Destek**: PWA yetenekleri için service worker kullanın

Vercel'e (Next.js için optimize edilmiş) veya benzer platformlara deploy edin. MongoDB Atlas kümesi kurun ve environment variable'ları yapılandırın.

Sonuç

Bu flashcard uygulaması, temel kavramları gösterirken aynı zamanda pratik bir çözüm sunar. Artık ihtiyaçlarınıza göre genişletilebilecek bir temelınız var. Dil öğreniyor olun, sınava hazırlanıyor olun ya da profesyonel materyal ezberliyor olun — dijital flashcard'lar etkili ve özelleştirilebilir bir çözüm sağlar.

Basitten başlayın, sık sık iyileştirin ve temel kullanıcı deneyimine odaklanın. Öğrenme yolculuğunuzda başarılar dilerim!

SSS

S: MongoDB deneyimine ihtiyacım var mı?
C: Temel bilgi yardımcı olur ama zorunlu değil. JSON yapısını anladığınızda MongoDB'nin belge modeli çok sezgisel hale geliyor.

S: Flashcard'lara resim ekleyebilir miyim?
C: Tabii! Şemayı resim URL'leriyle genişletin ve form bileşenlerini dosya yüklemeyi destekleyecek şekilde değiştirin.

S: Aralıklı tekrar (spaced repetition) nasıl implement edilir?
C: Zaman damgalı tekrar geçmişi saklayın, sonra hatırlama performansına göre optimal aralıkları hesaplayın. `supermemo` gibi kütüphaneler algoritmalar konusunda yardımcı olabilir.

S: Bu mobil cihazlar için uygun mu?
C: Tailwind gibi responsive CSS framework'leriyle evet. Yerel uygulamalara yakın deneyim için PWA özellikleri eklemeyi düşünün.

Yorumlar (0)

Henüz yorum yapılmamış. İlk yorumu siz yapın!

Yorum Yap