Electron ve Next.js ile Üretime Hazır Masaüstü Uygulamaları Geliştirmek: Hello World'ün Ötesinde

Rind Devran Tukan
Rind Devran Tukan
Yayınlanma: •Güncelleme: •10 dakika okuma
AI generated image for Creating desktop applications with electron and next.js

Next.js App Router, web mimarisinde büyük ölçüde React Server Components (RSC) ve sunucu tarafı yürütmeye yönelen bir paradigma değişimini temsil eder. Electron ise aksine, yerel varlıkları oluşturmada ve doğrudan ana işletim sistemiyle etkileşime girmede üstün olan istemci tarafı bir masaüstü kabuğudur. Bu mimari uçurumu kapatmak için geliştiriciler, sunucu merkezli bir çerçeveyi son derece optimize edilmiş, yerel öncelikli bir varlık dağıtım sistemine dönüştürerek Next.js'in statik dışa aktarma özelliğinden (output: 'export') yararlanmalıdır.

Statik Kısıtlamayı Yönetmek

Next.js'i statik bir dışa aktarma için yapılandırdığınızda, aslında derleyiciye Node.js sunucu çalışma zamanını kaldırma talimatı vermiş olursunuz. Bu, dinamik üstbilgiler, sunucu tarafı çerezleri ve isteğe bağlı API rotaları gibi sunucu tarafı özelliklerinin kullanılamayacağı anlamına gelir. Bunun yerine, App Router sayfalarınızı, düzenlerinizi ve varlıklarınızı HTML, CSS ve JavaScript'ten oluşan statik bir dizine derler. Zorluk, bu katı derleme süresi kısıtlamaları altında çalışırken App Router'ın sunduğu zengin, modern geliştirici deneyimini korumakta yatmaktadır.

Bu iki teknolojiyi başarılı bir şekilde birleştirmek için birkaç temel mimari strateji benimsemeniz gerekir:

  • Veri Çekmeyi Ayrıştırın: Çalışma zamanı sunucu tarafı oluşturmadan (SSR), 'use client' bileşenleri içindeki istemci tarafı veri çekmeye geçiş yapın veya derleme sırasında generateStaticParams kullanarak kullanıcı arayüzü iskeletinizi önceden oluşturun.
  • Dosya Protokolü İkilemini Çözün: Varsayılan olarak Electron, yerel dosyaları file:// protokolü üzerinden yükler; bu da Next.js'in göreli yönlendirmesini ve varlık yüklemesini sıklıkla bozar. Bunu aşmak için, dışa aktarılan statik dizininizi güvenli bir şekilde sunmak üzere Electron ana sürecinizde bir özel protokol işleyici (örneğin app://) kaydedin.
  • IPC İletişimini İzole Edin: Electron'un Süreçler Arası İletişimi (IPC) kesinlikle istemci tarafındadır. Statik derleme aşamasında Next.js derleme motorunun Node.js'e özgü Electron API'lerini çözümlemeye çalışmasını önlemek için, IPC çağrılarınızı useEffect kancaları içine alın veya bunları dinamik içe aktarmalar yoluyla yükleyin.

Mimari Kazanç

Statik olarak dışa aktarılan ön ucunuz ile Electron'un Node.js özellikli ana süreci arasında kesin bir sınır çizerek, son derece ayrıştırılmış ve güvenli bir mimari elde edersiniz. Ön uç son derece hızlı, hafif ve test edilmesi kolay kalırken; ana süreç, dosya sistemi erişimi ve sistem düzeyindeki entegrasyonlar gibi ağır işleri üstlenir. Bu kurulum, en üst düzey hibriti sunar: Next.js'in modern geliştirici hızı ile Electron'un saf masaüstü gücünün birleşimi.

Electron ve Next.js ile masaüstü uygulamaları geliştirmek, web tabanlı kullanıcı arayüzü ile yerel işletim sistemi yeteneklerinin en iyi yönlerini bir araya getirir. Ancak bu birleşim, kritik bir mimari zorluğu da beraberinde getirir: Electron'un ayrıcalıklı Ana (Main) süreci ile Next.js'in yalıtılmış (sandboxed) İşleyici (Renderer) süreci arasındaki köprüyü kurmak. Ham IPC yeteneklerini doğrudan ön yüzünüze açmak, uygulamanızın güvenilmeyen içerikleri veya harici API'leri işlemesi durumunda Uzaktan Kod Çalıştırma (RCE) açıklarına davetiye çıkaran feci bir güvenlik açığıdır.

Bağlam Köprüsü (Context Bridge): Güvenli Bir Hava Kilidi

Güvenli bir iletişim kanalı kurmak için, Electron'un preload script özelliğini contextIsolation ile birlikte kullanmalıyız. Preload betiği, Node.js API'lerine erişimi olan ayrıcalıklı bir bağlamda çalışan, ancak Next.js window nesnesine yalnızca kesin olarak tanımlanmış, temizlenmiş bir geçit sunan bir hava kilidi görevi görür.

  • Yalıtımı Zorunlu Kılın: İşleyiciyi kilitlemek için BrowserWindow yapılandırmanızda her zaman contextIsolation: true ve nodeIntegration: false ayarlarını yapın.
  • Belirgin Geçitler: Genel bir geçiş sağlamak yerine, preload betiğiniz içinde contextBridge.revealInMainWorld kullanarak katı bir API tasarlayın.
  • Sıkı Kanal Beyaz Listesi (Whitelisting): Rastgele mesaj iletimini önlemek için preload betiğinizde izin verilen IPC kanal adlarını doğrudan kodlayın (hardcode).

Yerel Olayları Next.js Durumuna (State) Bağlama

Güvenli köprünüz kurulduktan sonraki adım, eşzamansız, itme tabanlı (push-based) IPC olaylarını Next.js'in reaktif durum yönetimine (state management) eşlemektir. Bunu yönetmenin en zarif yolu, dışa açtığınız window API'lerini özel React kancaları (hooks) ile sarmalamaktır.

Örneğin, useNativeNotification gibi özel bir hook oluşturmak, bileşen yüklendiğinde (mount) bir IPC dinleyicisi kaydetmenize, Ana süreç bir sistem olayı yayınladığında yerel bir React state değişkenini güncellemenize ve bileşen kaldırıldığında (unmount) dinleyiciyi düzgün bir şekilde temizlemenize olanak tanır. Bu, bellek sızıntılarını önler ve kullanıcı arayüzü durumunuzun temel işletim sistemiyle mükemmel bir uyum içinde kalmasını sağlar. IPC köprüsünü reaktif bir akış olarak ele alarak, Next.js bileşenleriniz; dosya sistemi değişiklikleri, menü çubuğu tıklamaları veya donanım olayları gibi yerel tetikleyicilere, React'in deklaratif paradigmasını bozmadan sorunsuz bir şekilde yanıt verebilir.

Hibrit bir uygulama geliştirmek, temelde birbirinden farklı iki ortamı uyumlu hale getirmeyi gerektirir: Next.js web çalışma zamanı ve Electron Node.js kapsayıcısı. Sorunsuz bir geliştirici deneyimi (DX) elde etmek, arayüz (UI) değişikliklerinin Sıcak Modül Değişimi (HMR) aracılığıyla anında işlendiği, ana Electron sürecindeki değişikliklerin ise durum kaybı yaşanmadan akıllı uygulama yeniden başlatmalarını tetiklediği bir iş akışı yapılandırmak anlamına gelir.

Çift Süreçli Geliştirme Stratejisi

Gerçek bir anında yeniden yükleme (hot reloading) deneyimi sunmak için, geliştirme sırasında işleyiciyi (renderer) ana süreçten ayırmalısınız. Next.js geliştirme sunucusunu yerel bir bağlantı noktasında (örneğin localhost:3000) çalıştırarak onun yerleşik Hızlı Yenileme (Fast Refresh) özelliğinden yararlanın. Eş zamanlı olarak, Electron ana sürecinizi statik bir dosya yerine bu yerel URL'yi yükleyecek şekilde yapılandırın. IPC ana dinleyicileri veya sistem menüsü yapılandırmaları gibi yerel taraftaki değişiklikleri yönetmek için, electronmon veya electron-reload gibi bir yardımcı araç entegre edin. Bu sayede Chromium penceresinde UI güncellemeleri anında gerçekleşirken, yerel Node.js kodunuzdaki değişiklikler Electron sarmalayıcısının kendisini hızla yeniden başlatmasını tetikler.

Üretim Sürümü (Production Build) Dönüşümü

Yerel bir geliştirme sunucusu geliştirme aşaması için ideal olsa da, üretim aşaması tamamen farklı bir mimari yaklaşım gerektirir. Electron, çalışan bir Next.js Node sunucusuyla birlikte dağıtılamaz; statik varlıkları doğrudan dosya sisteminden yüklemelidir. Bu geçişi sorunsuz bir şekilde yapılandırmak için şu temel entegrasyon stratejilerine öncelik verin:

  • Statik Çıktılar (Static Exports): Next.js'i, next.config.js dosyanızda output: 'export' olacak şekilde yapılandırın. Bu, React bileşenlerinizi, kancalarınızı (hooks) ve sayfalarınızı dışa aktarma dizini içinde statik HTML, CSS ve JS varlıkları halinde derler.
  • Varlık Yolu Çözümleme (Asset Path Resolution): Electron, üretim ortamında dosyaları file:// protokolü üzerinden yüklediğinden, standart mutlak yollar çalışmayacaktır. Chromium işleyicisinin betiklerinizi bulabilmesi için Next.js yapılandırmanızın göreli varlık önekleri (assetPrefix: './' veya özel yükleyici mantığı) kullandığından emin olun.
  • Birleşik Orkestrasyon (Unified Orchestration): Bu adımları birbirine bağlamak için kırılgan, özel bash betikleri yazmaktan kaçının. nextron gibi özel şablonlardan (boilerplates) yararlanın veya derleme sırasını koordine etmek için Turborepo gibi bir monorepo görev yöneticisi yapılandırın: önce Next.js statik çıktısını derleyin ve ardından electron-builder kullanarak Electron uygulamasını paketleyin.

Geliştirme döngünüz ile üretim derleyiciniz arasında bu net sınırı belirleyerek, manuel olarak yeniden derleme yapmanın getirdiği zorlukları ortadan kaldırır ve masaüstü uygulamanızın hafif, öngörülebilir ve bakımı kolay kalmasını sağlarsınız.

Bir masaüstü uygulamasını dağıtmak mücadelenin yalnızca yarısıdır; binlerce parçalı kullanıcı ortamında uygulamanın bakımını yapmak, asıl mühendisliğin başladığı yerdir. Electron'u Next.js ile birleştirirken, sağlam ve sorunsuz bir güncelleme yaşam döngüsü uygulamak kritik önem taşır. İşte bu noktada electron-updater vazgeçilmez hale gelir. Karmaşık ve özel bir sürüm sunucusu gerektiren Electron'un yerleşik API'sinin aksine, electron-updater; Amazon S3, DigitalOcean Spaces veya GitHub Releases gibi uygun maliyetli statik barındırma ortamlarından doğrudan güncellemeyi destekler.

Next.js Durum İkilemini Çözmek

Geleneksel bir web ortamında dağıtım, kullanıcının sayfayı bir sonraki yenilemesinde yeni sürümü alması anlamına gelir. Electron ile paketlenmiş bir Next.js uygulamasında, güncellemeyi uygulamak için ani bir yeniden başlatmayı zorunlu kılmak; yerel SQLite veritabanlarını bozabilir, aktif arka plan çalışanlarını kesintiye uğratabilir veya bellek üzerindeki kritik React durumunu yok edebilir. Canlı ortamda bunu sorunsuz bir şekilde yönetmek için, sessiz ve zorunlu kurulumlar yerine "indir ve sor" modelini benimsemelisiniz.

  • Sessizce kontrol edin: Güncelleyiciyi, uygulama açılışında ve tamamen arka planda çalışacak şekilde her dört saatte bir gibi sakin bir zaman aralığında güncellemeleri kontrol edecek şekilde yapılandırın.
  • Süreçler arası boşluğu köprüleyin: Güncelleme ilerleme olaylarını ana süreçten (main process) Next.js derleyicinize (renderer) aktarmak için Electron'un Süreçler Arası İletişimini (IPC) kullanın; bu, yerel görünümlü bir indirme ilerleme çubuğu görüntülemenizi sağlar.
  • Yeniden başlatmayı erteleyin: Kullanıcılara kendi kolaylıklarına göre "Güncellemek için Yeniden Başlat" seçeneğine tıklama özerkliği verin veya güncelleme kurulumunu yalnızca uygulama normal şekilde kapatıldığında otomatik olarak tetiklenecek şekilde zamanlayın.

Olmazsa Olmaz: Kod İmzalama ve CI/CD Entegrasyonu

Kod imzalamaya değinmeden gerçek dünyadaki otomatik güncellemelerden bahsedemezsiniz. Geçerli bir Apple Developer ID sertifikası (macOS noter onayı dahil) ve bir Windows EV sertifikası olmadan, modern işletim sistemleri güncellemelerinizi güvenilmeyen yazılım olarak agresif bir şekilde engelleyecektir. macOS'ta electron-updater imzasız güncellemeleri uygulayamaz ve arka planda sessizce duraklatılır. Her canlı sürümün tek bir birleşik, müdahalesiz iş akışında imzalanması, noter onayından geçirilmesi ve güncelleme deponuzda yayınlanması için bu imzalama sürecini tamamen CI/CD boru hattınızda otomatikleştirin.

Electron ve Next.js ile oluşturulmuş bir masaüstü uygulamasını dağıtmak size web düzeyinde geliştirme hızı kazandırır, ancak aynı zamanda sizi benzersiz bir çift tehdit ortamıyla karşı karşıya bırakır: Siteler Arası Betik Çalıştırma (XSS) gibi web güvenlik açıkları ve işletim sistemi düzeyindeki güvenlik tehditleri. Kullanıcılarınızı ve marka itibarınızı korumak için, kod imzalama ve uygulama korumalı alanı işlemlerini sonradan akla gelen yapılacaklar listeleri olarak değil, tartışılamaz üretim gereksinimleri olarak ele almalısınız.

Kod İmzalama ile Güven Oluşturma

İşletim sistemleri, imzalanmamış yürütülebilir dosyalara karşı doğal olarak şüphecidir. Kod imzalama olmadan, Windows SmartScreen ve macOS Gatekeeper uygulamanızı güvenilir olmayan kötü amaçlı yazılım olarak işaretleyecek ve dönüşüm oranlarınızı yerle bir edecektir. Kod imzalama, ikili dosyaların doğrulanmış bir geliştiriciden geldiğini ve aktarım sırasında değiştirilmediğini kanıtlayan dijital bir kriptografik mühür görevi görür.

  • macOS için: Uygulamanızı bir Apple Developer ID sertifikası ile imzalamalı ve Apple'ın noterlik hizmetine göndermelisiniz. Noter onaylama ve iliştirme sürecini otomatikleştirmek için bunu doğrudan electron-builder iş hattınıza entegre edin.
  • Windows için: Bir Genişletilmiş Doğrulama (EV) Kod İmzalama Sertifikasına yatırım yapın. Standart sertifikaların aksine, EV sertifikaları SmartScreen nezdinde anında itibar sağlayarak ilk günden itibaren uyarı ekranlarını devre dışı bırakır.

Korumalı Alan ile Çalışma Zamanını İzole Etme

Bir saldırgan Next.js ön ucunuzdaki bir güvenlik açığından yararlanmayı başarsa bile, güçlü bir korumalı alan onların temel işletim sistemini ele geçirmesini engeller. İzolasyonu hem uygulama düzeyinde hem de işletim sistemi düzeyinde zorunlu kılmalısınız.

İlk olarak, Electron'un dahili işlem korumalı alanından yararlanın. BrowserWindow yapılandırmalarınızda sandbox: true seçeneğinin açıkça etkinleştirildiğinden emin olun. Next.js kullandığınız için bunu statik bir dışa aktarma (output: 'export') için yapılandırın. Bu, işleyici (renderer) içinde yerel bir Node.js sunucusuna olan ihtiyacı ortadan kaldırarak nodeIntegration özelliğini ve bağlam izolasyonu atlamalarını güvenli bir şekilde devre dışı bırakmanıza olanak tanır. Tüm yerel işletim sistemi işlemleri, bağlamı izole edilmiş IPC kanalları aracılığıyla kesinlikle güvenli bir ana işleme yönlendirilmelidir.

İkinci olarak, işletim sistemi düzeyinde korumalı alan uygulayın. Mac App Store aracılığıyla dağıtım yapıyorsanız, yetkilendirmelerinizde macOS Uygulama Korumalı Alanını etkinleştirmelisiniz. Bu, kullanıcı izin iletişim kutuları aracılığıyla açıkça verilmediği sürece uygulamanızın dosya sistemine, ağ kaynaklarına ve donanıma erişimini sınırlar.

Electron ve Next.js ile bir masaüstü uygulaması geliştirmek inanılmaz bir geliştirici hızı sunar, ancak sistem kaynakları üzerinde çift yönlü bir yük oluşturur: hem bir Chromium örneğini hem de bir Node.js çalışma zamanını çalıştırmak. Bilinçli bir optimizasyon olmadan, uygulamanız hızla bellek tüketen ve yavaş bir hale gelebilir. Yerel bir uygulama hissi veren bir deneyim sunmak için, bu iki çerçevenin birbiriyle nasıl etkileşime girdiğini optimize etmelisiniz.

Statik HTML Dışa Aktarımlarını Benimseyin

Canlı bir Next.js Node sunucusunu canlıdaki bir Electron uygulaması içinde çalıştırmak, performansı ciddi şekilde düşüren yaygın bir anti-paterndir. Bunun yerine, yapılandırmanızda output: 'export' ayarını yaparak Next.js'i statik bir dışa aktarım için yapılandırın. Bu, uygulamanızı statik HTML, CSS ve JavaScript varlıklarına derler. Electron daha sonra özel protokoller kullanarak bu varlıkları doğrudan yerel dosya sisteminden yükleyebilir, böylece dahili bir HTTP sunucusunun getirdiği yükü tamamen devre dışı bırakır ve ilk başlangıç sürelerini büyük ölçüde azaltır.

IPC Tıkanıklıklarını En Aza İndirin

Electron'un ana işlemi (Node.js) ile işleyici işlemi (Next.js kullanıcı arayüzü) arasındaki köprü, kullanıcı arayüzünde sıkça takılmalara neden olur. Süreçler Arası İletişim (IPC), verilerin JSON olarak serileştirilmesini ve tersi işlemden geçirilmesini gerektirir. Tepki veren bir kullanıcı arayüzü sağlamak için, IPC kanalı üzerinden asla büyük veri kümeleri veya yüksek frekanslı güncellemeler göndermeyin. Bunun yerine, ağır veri işleme görevlerini işleyicideki web worker'lara aktarın veya işlenen verileri her iki işlemin de bağımsız olarak erişebileceği yerel bir SQLite veritabanına yazın.

Hidrasyon ve Tembel Yüklemeyi (Lazy Loading) Optimize Edin

Next.js uygulamaları başlangıçta, React önyükleme yaparken kullanıcı arayüzünün görünür olduğu ancak etkileşime girilemediği "hidrasyon gecikmesi" sorunu yaşayabilir. Masaüstü ortamında bunu hafifletmek için:

  • Grafikler, karmaşık formlar veya ayarlar paneli gibi ağır bileşenlerin yalnızca ihtiyaç duyulduğunda yüklenmesi için dinamik içe aktarımlar kullanın.
  • Gerekli olmayan Node.js modülü içe aktarımlarını açıkça çağrılana kadar erteleyerek ana işlemi hafif tutun, böylece başlangıç gecikmelerini önleyin.
  • İşleme iş parçacığını kullanıcı etkileşimleri için boşta tutmak amacıyla ağır JavaScript animasyon kitaplıkları yerine CSS geçişlerinden yararlanın.