[{"content":" Java geliştirme konusunda güçlü bir altyapıya sahip bir Yazılım Mühendisiyim. Programlamaya lisede başladım ve şu anda Turkish Airlines Technology’de çalışıyorum.\nYetkinliklerim arasında Spring Boot, MongoDB, PostgreSQL, Kafka, Redis ve gerçek zamanlı iletişim için WebSocket yer alıyor.\nProgramlama, elektronik, robotik ve havacılıkla ilgileniyorum.\nBaşkalarıyla birlikte çalışmaktan, yeni zorlukların altına girmekten ve gerçekten değer taşıyan projelere anlamlı katkılar sunmaktan keyif alıyorum.\nCV'yi indir PDF · 94 KB · İngilizce Deneyim Turkish Airlines Technology — Java Yazılım Mühendisi (Experilabs üzerinden danışman) Ağu 2024 – devam ediyor\n10.000\u0026rsquo;den fazla kabin ve kokpit ekibinin kullandığı uygulamalar geliştirdim: doküman atama uygulaması, uçuş destinasyon bilgi sistemi ve Mobile Cabin Assistant Legacy backend servislerinde performans iyileştirmeleri yaparak yanıt sürelerini yaklaşık %20 düşürdüm Fortify ve TruffleHog ile kritik güvenlik açıklarını kapatıp deployment pipeline\u0026rsquo;larını temiz tuttum Java, Spring Boot, OpenShift, S3 object storage, Kafka Yapı Kredi — Java Yazılım Mühendisi (Metasis üzerinden danışman) May 2023 – Ağu 2024\nSpring Boot ve React ile yürütülen monolitten mikroservise geçiş projesine katkı verdim Geliştirme akışını hızlandırmak için kurum içi REST/SOAP API mock\u0026rsquo;lama aracı ve özel bir IntelliJ eklentisi yazdım JUnit, WireMock ve RestAssured ile %90 birim ve entegrasyon testi kapsamına ulaştım Yaşam döngüsü yönetiminde SAP PowerDesigner, Jira, Confluence ve Bitbucket kullandım Huawei — Yazılım Mühendisliği Stajyeri Ara 2022 – Mar 2023\nSpring Boot, MongoDB, PostgreSQL, Kafka ve Rancher ile mikroservis geliştirme IKnow Technology — Yazılım Mühendisliği Stajyeri Haz 2022 – Tem 2022\nSpring Cloud Gateway, Feign Client ve Eureka Server ile service discovery ve API gateway desenlerini gösteren dağıtık bir mikroservis uygulaması geliştirdim Bulut MD Software — Yazılım Mühendisliği Stajyeri Haz 2020 – Mar 2021\nSpring Boot ve React ile full-stack projelere (e-ticaret, yedek parça) katkı verdim Eğitim Trakya Üniversitesi — Bilgisayar Mühendisliği Yüksek Lisans 2023 – 2026 Tez: Derin Öğrenme Teknikleri ile Akıllı Telefonlarda Üretim Hatalarının Tespiti (Samsung Ar-Ge ile)\nTrakya Üniversitesi — Bilgisayar Mühendisliği Lisans 2019 – 2023 Not ortalaması 3.56 / 4.00 — bölüm ikincisi\nKullandığım araçlar Diller Java, JavaScript, Go Framework\u0026rsquo;ler Spring Boot, React, Hibernate Veritabanları PostgreSQL, MongoDB, Redis Mesaj kuyrukları Kafka, RabbitMQ Cloud \u0026amp; DevOps Linux, Docker, OpenShift, AWS Depolama \u0026amp; ağ S3 object storage, Nginx, Traefik Test JUnit, WireMock, Postman/Newman Protokoller REST, WebSockets, MQTT, WebRTC Araçlar Git, Jira, Fortify, SonarQube, Grafana, Copilot, Arduino Topluluk FolksDEV — Eyl 2021 – Ara 2023 Eğitim içerikleri hazırladım, Medium\u0026rsquo;da yazılar yayımladım ve ekip etkinliklerinde yer aldım.\nDiller Türkçe (ana dil) · İngilizce (profesyonel çalışma seviyesi)\nİletişim Bana ulaşmanın en güvenilir yolu e-posta: ucar.gurkan@hotmail.com. Ayrıca GitHub, LinkedIn ve Medium üzerinden de bulabilirsiniz.\n","permalink":"https://gurkanucar.com/tr/about/","summary":"\u003cimg class=\"about-portrait\" src=\"/images/gurkan-ucar.png\" width=\"300\" height=\"300\" alt=\"Gürkan UÇAR\"\u003e\n\u003cp\u003eJava geliştirme konusunda güçlü bir altyapıya sahip bir Yazılım Mühendisiyim. Programlamaya lisede başladım ve şu anda \u003cstrong\u003eTurkish Airlines Technology\u003c/strong\u003e’de çalışıyorum.\u003c/p\u003e\n\u003cp\u003eYetkinliklerim arasında Spring Boot, MongoDB, PostgreSQL, Kafka, Redis ve gerçek zamanlı iletişim için WebSocket yer alıyor.\u003c/p\u003e","title":"Hakkımda"},{"content":"Nginx, temel olarak açık kaynaklı ve yüksek performanslı bir web sunucusudur. Ancak işlevi yalnızca bununla sınırlı değildir; aynı zamanda bir reverse proxy olarak da görev yapar. Reverse proxy, istemciden gelen talepleri alarak bunları arka plandaki sunuculara yönlendiren bir katmandır. Bu sayede istemci, doğrudan uygulama sunucusuyla değil, Nginx aracılığıyla iletişime geçer.\nNginx, özellikle bir domain’i belirli bir IP adresiyle eşleştirip SSL üzerinden güvenli bağlantılar kurmak istediğimizde sıkça tercih edilir. Bununla birlikte; yük dengeleme (load balancing), önbellekleme (caching), statik dosya sunumu ve bazı e-posta servisleri için proxy desteği gibi birçok gelişmiş özelliği de destekler.\nNOT: production ortamlarda ip adresi ve portlardan ziyade domain ve subdomain adresleri kullanılır. Nginx, bu domain/subdomainlere gelen istekleri sunucu içerisinde ilgili porta yönlendirmede büyük önem taşır.\nKurulum\nAşağıdaki komutları linux ortamda çalıştırarak NGINX kurabilirsiniz.\nsudo apt update \u0026amp;\u0026amp; sudo apt upgrade -y # install nginx sudo apt install nginx -y # run nginx sudo systemctl start nginx # auto start nginx on startup sudo systemctl enable nginx # check status sudo systemctl status nginx ## Util commands # check configuration file syntax is correct sudo nginx -t # restart nginx sudo systemctl restart nginx # or both same line nginx -t \u0026amp;\u0026amp; systemctl reload nginx # check error logs sudo tail -f /var/log/nginx/error.log Nginx Konfigurasyon\nNginx, çoğunlukla /etc/nginx dizini altında konumlanır. Bu dizine gittiğimizde:\nroot@localhost:/etc/nginx# ll total 76 drwxr-xr-x 8 root root 4096 Mar 16 17:14 . drwxr-xr-x 95 root root 4096 Mar 16 09:44 .. drwxr-xr-x 2 root root 4096 Mar 14 2023 conf.d -rw-r--r-- 1 root root 1125 Mar 14 2023 fastcgi.conf -rw-r--r-- 1 root root 1055 Mar 14 2023 fastcgi_params -rw-r--r-- 1 root root 2837 Mar 14 2023 koi-utf -rw-r--r-- 1 root root 2223 Mar 14 2023 koi-win -rw-r--r-- 1 root root 4338 Mar 14 2023 mime.types drwxr-xr-x 2 root root 4096 Mar 14 2023 modules-available drwxr-xr-x 2 root root 4096 Mar 14 2023 modules-enabled -rw-r--r-- 1 root root 1762 Mar 16 17:14 nginx.conf -rw-r--r-- 1 root root 180 Mar 14 2023 proxy_params -rw-r--r-- 1 root root 636 Mar 14 2023 scgi_params drwxr-xr-x 2 root root 4096 Mar 16 17:11 sites-available drwxr-xr-x 2 root root 4096 Apr 16 15:07 sites-enabled drwxr-xr-x 2 root root 4096 Mar 16 09:32 snippets -rw-r--r-- 1 root root 664 Mar 14 2023 uwsgi_params -rw-r--r-- 1 root root 3071 Mar 14 2023 win-utf şeklinde bir çok dosya va dizin ile karşılaşırız. Burada bizi ilgilendiren en önemli kısımlar nginx.conf dosyası, sites-enabled ve sites-available dizinleridir.\n📄nginx.conf Nedir?\nnginx.conf, Nginx’in ana yapılandırma dosyasıdır. Nginx’in nasıl çalışacağını tanımlar. İçerisinde; genel ayarlar, modül yüklemeleri, log ayarları, include edilen diğer config dosyaları vs. bulunur. Bu dosya olmadan Nginx düzgün çalışmaz. Ayrıca içerisinde, aktif olan configleri include eden bir satır da bulunur.\ninclude /etc/nginx/sites-enabled/*; sites-available Nedir?\nBu dizin, tanımlı tüm sanal host (virtual host) konfigürasyonlarını içerir. Buraya her domain/site için bir config dosyası ekleriz. Ama buraya koymak tek başına aktif etmek anlamına gelmez. Yani “hazır ama aktif değil” gibi düşünebiliriz. Çok doğru bir yaklaşım olmasa da, tek tek domaine özel dosya oluşturmak yerine, direkt olarak sites-available içerisinde bulunan default isimli dosyaya da configler eklenebilir.\nroot@localhost:/etc/nginx/sites-available# ll -rw-r--r-- 1 root root 1964 Apr 16 14:12 default sites-enabledNedir?\nBu dizin, aktif olan config dosyalarının sembolik linklerini içerir. Burada doğrudan dosya tutmayız, sadece sites-available içindekilerin linki olur. Nginx, sadece sites-enabled/ içindekileri yükler.\nŞimdi ise örnek bir domain oluşturup, nginx ile hello world serve etmeye çalışalım:\n# kendi domainime ait bir subdomain configurasyon dosyası oluşturuyorum sudo nano /etc/nginx/sites-available/subdomain.gurkanucar.com Nginx\u0026rsquo;in, DSL (Domain-Specific Language) gibi kendine özgü bir konfigurasyon söz dizimi (syntax) vardır. Direkt olarak Nginx tarafından parse edilir. İçerisine aşağıdaki örnek konfigurasyonu ekleyelim:\nserver { listen 80; server_name subdomain.gurkanucar.com; root /var/www/subdomain.gurkanucar.com; index index.html; location / { try_files $uri $uri/ =404; } } Oluşturduğumuz konfigürasyon dosyasını linkleyip aktif edelim:\n# bu configurasyon dosyasini sites-enabled ile linkleyelim sudo ln -s /etc/nginx/sites-available/subdomain.gurkanucar.com /etc/nginx/sites-enabled/ # nginx i önce test edip sonra restart edelim sudo nginx -t sudo systemctl reload nginx Domain adımız için, web root dizini oluşturalım. Sonrasında ise içerisine Hello World! yazan bir html dosyası ekleyelim:\n# create directory sudo mkdir -p /var/www/subdomain.gurkanucar.com # create html file echo \u0026#39;\u0026lt;h1\u0026gt;Hello, World!\u0026lt;/h1\u0026gt;\u0026#39; | sudo tee /var/www/subdomain.gurkanucar.com/index.html Sonrasında ise kendi DNS sağlayıcımıza gidip, subdomain ve sunucu ip adresimizi ekleyelim:\nsubdomain.gurkanucar.com\nYukarıda görmüş olduğunuz gibi subdomain ile, oluşturduğumuz Hello World! örneğine erişebildik. Fakat bir sorun var. URL in yanında not secure uyarısını görmektesiniz. Bunu düzeltebilmek için ssl eklememiz gerekmekte.\nSSL Sertifikası Ekleme\nSSL eklemek için aşağıdaki komutları çalıştıralım ve certbot yükleyelim.\nsudo apt install certbot python3-certbot-nginx -y # enter your email address etc and accept terms sudo certbot --nginx -d subdomain.gurkanucar.com Konfigurasyon dosyamıza aşağıdaki komutla girip tekrardan göz attığımızda SSL ile alakalı bazı alanları otomatik olarak geldiğini göreceksiniz:\nsudo nano /etc/nginx/sites-available/subdomain.gurkanucar.com # /etc/nginx/sites-available/subdomain.gurkanucar.com server { server_name subdomain.gurkanucar.com; root /var/www/subdomain.gurkanucar.com; index index.html; location / { try_files $uri $uri/ =404; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/subdomain.gurkanucar.com/fullchain.pem; # m\u0026gt; ssl_certificate_key /etc/letsencrypt/live/subdomain.gurkanucar.com/privkey.pem; #\u0026gt; include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = subdomain.gurkanucar.com) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name subdomain.gurkanucar.com; return 404; # managed by Certbot } Ve tekrardan sitemize girdigimizde ise SSL sertifikasinin çalıştığını göreceğiz:\nEk olarak aşağıdaki 2 konfigürasyonun da eklenmesi güveliği artıracaktır:\n# SSL Protocols ssl_protocols TLSv1.2 TLSv1.3; # HSTS (HTTP Strict Transport Security) activate add_header Strict-Transport-Security \u0026#34;max-age=31536000; includeSubDomains; preload\u0026#34; always; TLS 1.3 en guncel sifreleme protokollerinden biridir. HSTS ise bir kez https kullanildiysa her zaman https uzerinden haberlesilmesi gerekliligini saglar\nDiğer Türdeki dosyaların serve edilmesi\nDiğer türdeki (json, image, zip etc.) dosyaları serve edebilmek için aşağıdaki şekilde konfigüre edelim:\nserver { server_name subdomain.gurkanucar.com; root /var/www/subdomain.gurkanucar.com; index index.html; # Static files location / { try_files $uri $uri/ =404; } # JSON files location ~* \\.json$ { add_header Content-Type application/json; try_files $uri =404; } # Images (JPEG, PNG, etc.) location ~* \\.(jpg|jpeg|png|gif|svg|webp)$ { add_header Content-Type image/jpeg; try_files $uri =404; } # ZIP files location ~* \\.zip$ { add_header Content-Type application/zip; try_files $uri =404; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/subdomain.gurkanucar.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/subdomain.gurkanucar.com/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot # SSL Protokolü ve Ciphers ssl_protocols TLSv1.2 TLSv1.3; # HSTS (HTTP Strict Transport Security) aktif etme add_header Strict-Transport-Security \u0026#34;max-age=31536000; includeSubDomains; preload\u0026#34; always; } server { if ($host = subdomain.gurkanucar.com) { return 301 https://$host$request_uri; } listen 80; server_name subdomain.gurkanucar.com; return 404; } root@localhost:/var/www/subdomain.gurkanucar.com# ll total 608 drwxr-xr-x 2 root root 4096 Apr 16 22:29 . drwxr-xr-x 5 root root 4096 Apr 16 21:55 .. -rw-r--r-- 1 root root 1182 Apr 16 22:25 data.json -rw-r--r-- 1 root root 23 Apr 16 21:56 index.html -rw-r--r-- 1 root root 605766 Apr 16 22:29 istanbul.jpg https://subdomain.gurkanucar.com/data.json | https://subdomain.gurkanucar.com/istanbul.jpg\nVeya ozel bir konumdaki dosyayi da asagidaki sekilde serve edebiliriz:\n# Custom file location = /configs/app.json { alias /opt/configs/app.json; add_header Content-Type application/json; # Cors config - optional add_header \u0026#39;Access-Control-Allow-Origin\u0026#39; \u0026#39;*\u0026#39; always; add_header \u0026#39;Access-Control-Allow-Methods\u0026#39; \u0026#39;GET, OPTIONS\u0026#39; always; add_header \u0026#39;Access-Control-Allow-Headers\u0026#39; \u0026#39;DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization\u0026#39; always; # cache - optional expires 5m; add_header Cache-Control \u0026#34;public\u0026#34;; } https://subdomain.gurkanucar.com/configs/app.json\nEğer 403 hatası alırsanız, dosya ve klasörlerin read yetkisini kontrol edin. Buna rağmen hata almaya devam ediyorsanız çok yüksek ihtimal ile SELinux enable olduğundan dolayı olabilir.\nNodeJS web sunucu uygulamasina yonlendirme\nSimdi ise basit bir node js uygulaması oluşturalım ve gelen istekleri oraya yönlendirelim.\nconst express = require(\u0026#39;express\u0026#39;); const app = express(); const port = 3000; // Fake response app.get(\u0026#39;/api\u0026#39;, (req, res) =\u0026gt; { res.json({ message: \u0026#34;Hello, World!\u0026#34;, data: { id: 1, name: \u0026#34;John Doe\u0026#34;, email: \u0026#34;john.doe@example.com\u0026#34; } }); }); // Start server app.listen(port, () =\u0026gt; { console.log(`Server running at http://localhost:${port}`); }); subdomain.gurkanucar.com config dosyasindaki server bloklari arasina aşağıdaki kodu ekleyelim:\nserver { server_name subdomain.gurkanucar.com; root /var/www/subdomain.gurkanucar.com; index index.html; .............. # Proxy requests to Node.js app location /api/ { # Note the trailing slash! proxy_pass http://localhost:3000/; # Node.js uygulamanın çalıştığı port proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ............... Ardindan nginx restart edelim\nnginx -t \u0026amp;\u0026amp; sudo systemctl reload nginx Görmüş olduğunuz üzere https://subdomain.gurkanucar.com/api adresi uzerinden servisimize erişebildik.\nPeki websocket de eklemek istersek ne yapmalıyız?\nWebsocket eklemek için Nginx tarafında ufak bir güncelleme yapmak gerekecek. Once uygulamaya websocket ekleyelim:\n// app.js - Express with WebSocket implementation const express = require(\u0026#39;express\u0026#39;); const http = require(\u0026#39;http\u0026#39;); const WebSocket = require(\u0026#39;ws\u0026#39;); const app = express(); const server = http.createServer(app); const wss = new WebSocket.Server({ server, path: \u0026#39;/api/ws\u0026#39; // WebSocket endpoint under the /api path }); const port = 3000; // Express REST API endpoint app.get(\u0026#39;/api\u0026#39;, (req, res) =\u0026gt; { res.json({ message: \u0026#34;Hello, World!\u0026#34;, data: { id: 1, name: \u0026#34;John Doe\u0026#34;, email: \u0026#34;john.doe@example.com\u0026#34; } }); }); // WebSocket connection handling wss.on(\u0026#39;connection\u0026#39;, (ws) =\u0026gt; { console.log(\u0026#39;Client connected\u0026#39;); // Send welcome message ws.send(JSON.stringify({ type: \u0026#39;welcome\u0026#39;, message: \u0026#39;WebSocket connection established\u0026#39; })); // Handle messages from client ws.on(\u0026#39;message\u0026#39;, (message) =\u0026gt; { console.log(\u0026#39;Received:\u0026#39;, message.toString()); // Echo the message back ws.send(JSON.stringify({ type: \u0026#39;echo\u0026#39;, message: message.toString() })); }); // Handle client disconnection ws.on(\u0026#39;close\u0026#39;, () =\u0026gt; { console.log(\u0026#39;Client disconnected\u0026#39;); }); }); // Start server server.listen(port, () =\u0026gt; { console.log(`Server running at http://localhost:${port}`); console.log(`WebSocket server running at ws://localhost:${port}/api/ws`); }); Ardindan nginx konfigurasyonunu güncelleyelim:\n# Combined API and WebSocket configuration location /api/ { # Note the trailing slash! proxy_pass http://localhost:3000/; # Node.js application port # Enable WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; # Timeout for WebSocket connections (24h) } wss://subdomain.gurkanucar.com/api/ws\nGörmüş olduğunuz üzere websocket de başarılı bir şekilde çalıştı.\nLoad Balancing Nginx in en önemli özelliklerinden bir diğeri ise load balancing yapabilmesi. Load balancing, gelen istekleri uygulama node larına dağtımak ve performansı artırmak anlamına gelmektedir. upstream kullanarak nginx de load balancing yapabiliriz.\nUpstream tanimlamasini, server bloğunun üzerinde gerçekleştirmeliyiz. Ardindan az önce tanımladığımız 3000 portlu proxy pass\u0026rsquo;ı (proxy_pass http://localhost:3000;) upstream ile degistirmeliyiz.\n# load balancing upstream backend_servers { server localhost:3000; server localhost:3001; # You can add more servers here if needed } server { server_name subdomain.gurkanucar.com; .......... # Proxy WebSocket and API requests location /api/ { proxy_pass http://backend_servers/; # Load balancing ........... } Şimdi ise bazen 3000 bazen 3001 portuna yönlendirmekte:\nLoad Balancing Basic authentication her ne kadar cogu zaman web uygulama sunucusunda eklense de, nginx ile de ekleyebilmek mumkun. Ornegin bazi docker servislerimizi dis dunyaya acarken onune basic authentication eklemek isteyebiliriz.\nBasic authentication icin htpasswd ile username ve password olusturmak gerek. Asagidaki sh dosyasi ile bunu kolayca halledebilirsiniz.\n#!/bin/bash if ! command -v htpasswd \u0026amp;\u0026gt; /dev/null; then echo \u0026#34;Installing apache2-utils to use htpasswd...\u0026#34; apt-get update apt-get install -y apache2-utils fi # Set username and password USERNAME=\u0026#34;user\u0026#34; PASSWORD=\u0026#34;secure_password\u0026#34; echo \u0026#34;Creating password file for user: $USERNAME\u0026#34; htpasswd -cb /etc/nginx/.htpasswd $USERNAME $PASSWORD chown www-data:www-data /etc/nginx/.htpasswd chmod 600 /etc/nginx/.htpasswd echo \u0026#34;Basic authentication setup complete!\u0026#34; echo \u0026#34;Username: $USERNAME\u0026#34; echo \u0026#34;Password: $PASSWORD (Please change this in production!)\u0026#34; echo \u0026#34;Password file location: /etc/nginx/.htpasswd\u0026#34; echo \u0026#34;\u0026#34; echo \u0026#34;To manually create/update users, use:\u0026#34; echo \u0026#34; htpasswd -c /etc/nginx/.htpasswd username # Create new file\u0026#34; echo \u0026#34; htpasswd /etc/nginx/.htpasswd username # Add/update user\u0026#34; Ardindan basic authentication eklemek istedigimiz servise:\n# Basic Authentication auth_basic \u0026#34;Restricted Access\u0026#34;; auth_basic_user_file /etc/nginx/.htpasswd; satirlarini ekleyelim. Ornegin:\nlocation /api/ { # Basic Authentication auth_basic \u0026#34;Restricted Access\u0026#34;; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://backend_servers/; # Load balancing # Enable WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; # Timeout for WebSocket connections (24h) } 401 hatasi\ndogru username ve password girildiginde\nnginx performans onerileri / performance tunning ","permalink":"https://gurkanucar.com/tr/posts/nginx-nedir-neden-kullanilir/","summary":"\u003cp\u003eNginx, temel olarak açık kaynaklı ve yüksek performanslı bir web sunucusudur. Ancak işlevi yalnızca bununla sınırlı değildir; aynı zamanda bir \u003cstrong\u003ereverse proxy\u003c/strong\u003e olarak da görev yapar. Reverse proxy, istemciden gelen talepleri alarak bunları arka plandaki sunuculara yönlendiren bir katmandır. Bu sayede istemci, doğrudan uygulama sunucusuyla değil, Nginx aracılığıyla iletişime geçer.\u003c/p\u003e","title":"Nginx Nedir? Neden Kullanılır?"},{"content":"\nCache, en basit haliyle veriyi önbellekte saklayan ve tekrar ihtiyaç duyulduğunda, ek bir işlem gerektirmeden hızlıca bu önbellekten getiren bir mekanizmadır. Normal şartlarda bir veriye erişmek istediğimizde doğrudan veritabanına sorgu atarız. Ancak bu, özellikle yoğun erişimlerde, veritabanına sürekli sorgu atılmasına ve dolayısıyla performansın düşmesine neden olur.\nÇok konuşma kodu ver diyenler için: github\nÖrneğin, uygulama ilk açıldığında çekilmesi gereken konfigürasyon verilerini sağlayan bir servisinizi düşünün. Bu servise çok sayıda kullanıcı aynı anda eriştiğinde veritabanına olan yük artar ve sistem performansı olumsuz etkilenir. Ancak bu tür, sık değişmeyen veriler için cache kullanmak, hem veritabanı yükünü azaltır hem de uygulama performansını ciddi ölçüde artırır.\nCache sistemleri kullanım senaryolarına göre farklı şekillerde uygulanabilir. En yaygın cache türleri şunlardır:\nIn-Memory Cache Disk Cache Browser Cache Database Cache Distributed Cache Spring Boot içerisinde cache işlemleri için farklı kütüphaneler kullanılabilir. En yaygın olanlar şunlardır:\nGuava Caffeine Redis Bu yazıda, basit ve hafif bir çözüm olan Caffeine Cache’i ele alacağız.\nUfak bir not: Burada kullanılan yöntem, birden fazla instance şeklinde çalışan uygulamalarda veri tutarsızlığına sebep olabilir. Daha tutarlı bir cache mekanizması için redis ve türevleri gibi distributed cache kullanılması daha doğru olacaktır.\nEğer herhangi bir ek kütüphane tanımlamazsanız, Spring Boot varsayılan olarak ConcurrentHashMap kullanır. Ancak bu yapının TTL (Time To Live) gibi gelişmiş yapılandırma seçenekleri yoktur. Bu yüzden, TTL ve maksimum boyut gibi gelişmiş özellikler sunan Caffeine cache tercih edilmiştir.\nİşe gerekli depencyleri eklemekle başlayalım:\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-cache\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.github.ben-manes.caffeine\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;caffeine\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; Cache i aktifleştirmek için @EnableCaching anotasyonu kullanmalıyız:\n@EnableCaching @SpringBootApplication public class SpringBootStarterTemplateApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStarterTemplateApplication.class, args); } } Cache\u0026rsquo;e ait logları daha iyi görebilmek adına application.properties dosyamıza aşağıdaki configurasyonu ekleyelim:\nlogging.level.org.springframework.cache=TRACE Cache işlemleri için, metodlarin uzerine ekleyecegimiz bazı anotasyonlar vardır. Bunlar:\n@Cacheable: Metodun sonuçlarını önbelleğe alır, aynı parametrelerle tekrar çağrıldığında metodu çalıştırmadan önbellekten sonuç döndürür.\n@CachePut: Metodu her zaman çalıştırır ve sonucu önbelleğe kaydeder/günceller.\nNot: @Cacheable\u0026lsquo;dan farkı metodu atlamamasıdır, her zaman çalışır. @CacheEvict: Önbellekteki verileri siler.\nNot: allEntries parametresi tüm önbellek verilerini siler Tüm Anotasyonlarda Ortak Parametreler\nvalue/cacheNames: Önbellek adları key: Önbellek anahtarı için kullanılacak SpEL ifadesi keyGenerator: Özel anahtar üreteç sınıfı cacheManager: Kullanılacak önbellek yöneticisi cacheResolver: Önbellek çözümleyici condition: Önbellekleme koşulu unless: Önbellekleme yapılmayacak koşul Aşağıda örnek kullanımını görebilirsiniz:\n@Slf4j @Service @RequiredArgsConstructor public class ProductService { private final ProductRepository productRepository; // Caches the result; skips DB call if data is in cache // key also can be like concat of =\u0026gt; key = \u0026#34;\u0026#39;entity-\u0026#39; + #id\u0026#34; @Cacheable(value = \u0026#34;products\u0026#34;, key = \u0026#34;#id\u0026#34;) public Product getProductById(Long id) { log.info(\u0026#34;Fetching from DB...\u0026#34;); return productRepository.findById(id).orElse(null); } // Always updates the DB and cache @CachePut(value = \u0026#34;products\u0026#34;, key = \u0026#34;#product.id\u0026#34;) public Product updateProduct(Product product) { log.info(\u0026#34;Updating product and cache...\u0026#34;); return productRepository.save(product); } // Deletes the product from DB and evicts the cache entry @CacheEvict(value = \u0026#34;products\u0026#34;, key = \u0026#34;#id\u0026#34;) public void deleteProduct(Long id) { log.info(\u0026#34;Deleting from DB and cache...\u0026#34;); productRepository.deleteById(id); } // clears entire cache @CacheEvict(value = \u0026#34;products\u0026#34;, allEntries = true) public void clearAllCache() { log.info(\u0026#34;All cache entries cleared.\u0026#34;); } } Not: Eğer aynı servis içerisinde cache kullanan bir methodu çağırmanız gerekirse, direkt olarak metodu çağırdığınızda cache\u0026rsquo;in çalışmadığını göreceksiniz. Bu sorunu çözmek için aşağıdaki yöntemi kullanabilirsiniz:\nimport org.springframework.context.ApplicationContext; ... public class MyClass { private final ApplicationContext applicationContext; private MyClass self() { return applicationContext.getBean(MyClass.class); } public void doSomethingWithCachedInSameClass(Long id) { // Use self-invocation to ensure @Cacheable is applied var existing = self().getById(id).orElseThrow( () -\u0026gt; new EntityNotFoundException(\u0026#34;Entity not found\u0026#34;)); log.info(existing.toString()); } @Cacheable(key = \u0026#34;#id\u0026#34;) public Optional\u0026lt;MyEntity\u0026gt; getById(Long id) { return myEntityRepo.findById(id); } Peki production ortaminda direkt bu halde mi kullanılıyor? Çoğunlukla hayır. Bunu daha yönetilebilir bir şekilde kullanmak gerek. Bu sepeble öncelikle bir CacheNames sınıfı oluşturalım ve kullanacağımız cache isimlerini ekleyelim:\npublic class CacheNames { public static final String PRODUCTS = \u0026#34;products\u0026#34;; public static final String CACHE_USER_PREFERENCES = \u0026#34;user_preferences\u0026#34;; public static final String CACHE_USER_SESSIONS = \u0026#34;user_sessions\u0026#34;; public static final String CACHE_TEMPORARY_TOKENS = \u0026#34;temporary_tokens\u0026#34;; public static List\u0026lt;String\u0026gt; getAllCacheNames() { return Arrays.asList(PRODUCTS, CACHE_USER_PREFERENCES, CACHE_USER_SESSIONS, CACHE_TEMPORARY_TOKENS); } } Ardından bir cache managerı sınıfı oluşturalım:\ncache.short.ttl=60 cache.medium.ttl=120 cache.default.max-size=1000 //@EnableCaching @Configuration public class CaffeineCacheConfig { // Cache Manager Bean Names public static final String CACHE_MANAGER_SHORT_LIVED = \u0026#34;shortLivedCacheManager\u0026#34;; public static final String CACHE_MANAGER_MEDIUM_LIVED = \u0026#34;mediumLivedCacheManager\u0026#34;; @Primary @Bean(CACHE_MANAGER_SHORT_LIVED) public CacheManager shortLivedCacheManager( @Value(\u0026#34;${cache.short.ttl:60}\u0026#34;) int shortTtlSeconds, @Value(\u0026#34;${cache.default.max-size:1000}\u0026#34;) int defaultMaxSize ) { return buildCacheManager(shortTtlSeconds, defaultMaxSize); } @Bean(CACHE_MANAGER_MEDIUM_LIVED) public CacheManager mediumLivedCacheManager( @Value(\u0026#34;${cache.medium.ttl:120}\u0026#34;) int mediumTtlSeconds, @Value(\u0026#34;${cache.default.max-size:1000}\u0026#34;) int defaultMaxSize ) { return buildCacheManager(mediumTtlSeconds, defaultMaxSize); } private CacheManager buildCacheManager(int ttlSeconds, int maxSize) { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCacheNames(CacheNames.getAllCacheNames()); cacheManager.setCaffeine( Caffeine.newBuilder() .expireAfterWrite(ttlSeconds, TimeUnit.SECONDS) .maximumSize(maxSize) ); return cacheManager; } } Burada iki farklı manager oluşturduk. Birisi uzun süren cache işlemeri diğeri ise kısa sürenler için. Ek olarak @Primary anotasyonu ile de, cache manager verilmeme durumunda default olarak kısa süreli cache manager ın seçilmesini ayarladık.\nAşağıda, cache in kullanıldığı daha kapsamlı bir örneği bulabilirsiniz:\n@Service @Slf4j public class CacheDemoService { // Simulate database private final Map\u0026lt;String, Object\u0026gt; mockDatabase = new HashMap\u0026lt;\u0026gt;(); /** * Basic caching example using short-lived cache (default) */ @Cacheable(value = CacheNames.CACHE_PRODUCTS, key = \u0026#34;#id\u0026#34;) public String getProductData(String id) { log.info(\u0026#34;Cache MISS for product ID: {} - This would be a database call\u0026#34;, id); simulateSlowOperation(); return \u0026#34;Product data for \u0026#34; + id + \u0026#34; at \u0026#34; + System.currentTimeMillis(); } /** * Caching example with medium-lived cache manager */ @Cacheable( value = CacheNames.CACHE_USER_PREFERENCES, key = \u0026#34;#userId\u0026#34;, cacheManager = CaffeineCacheConfig.CACHE_MANAGER_MEDIUM_LIVED ) public String getUserPreferences(String userId) { log.info(\u0026#34;Cache MISS for user preferences, userId: {} - This would be a database call\u0026#34;, userId); simulateSlowOperation(); return \u0026#34;User preferences for \u0026#34; + userId + \u0026#34; at \u0026#34; + System.currentTimeMillis(); } /** * Cache update example using CachePut */ @CachePut(value = CacheNames.CACHE_USER_SESSIONS, key = \u0026#34;#sessionId\u0026#34;) public String updateSession(String sessionId, String data) { log.info(\u0026#34;Updating session data for sessionId: {}\u0026#34;, sessionId); String sessionData = data + \u0026#34; (updated at \u0026#34; + System.currentTimeMillis() + \u0026#34;)\u0026#34;; mockDatabase.put(sessionId, sessionData); return sessionData; } /** * Retrieve session data with caching */ @Cacheable(value = CacheNames.CACHE_USER_SESSIONS, key = \u0026#34;#sessionId\u0026#34;) public String getSessionData(String sessionId) { log.info(\u0026#34;Cache MISS for session data, sessionId: {} - This would be a database call\u0026#34;, sessionId); simulateSlowOperation(); return mockDatabase.getOrDefault(sessionId, \u0026#34;No session data found\u0026#34;).toString(); } /** * Cache eviction example - remove specific entry */ @CacheEvict(value = CacheNames.CACHE_PRODUCTS, key = \u0026#34;#id\u0026#34;) public void invalidateProductCache(String id) { log.info(\u0026#34;Invalidating cache for product ID: {}\u0026#34;, id); } /** * Multiple cache operations example */ @Caching(evict = { @CacheEvict(value = CacheNames.CACHE_USER_PREFERENCES, key = \u0026#34;#userId\u0026#34;), @CacheEvict(value = CacheNames.CACHE_USER_SESSIONS, key = \u0026#34;#sessionId\u0026#34;) }) public void logoutUser(String userId, String sessionId) { log.info(\u0026#34;User logout - clearing user-related caches for userId: {}, sessionId: {}\u0026#34;, userId, sessionId); // Perform actual logout operations } /** * Clear all entries from a cache */ @CacheEvict(value = CacheNames.CACHE_TEMPORARY_TOKENS, allEntries = true) public void clearAllTokens() { log.info(\u0026#34;Clearing all temporary token cache entries\u0026#34;); } /** * Clear all entries from multiple caches */ @Caching(evict = { @CacheEvict(value = CacheNames.CACHE_PRODUCTS, allEntries = true), @CacheEvict(value = CacheNames.CACHE_USER_PREFERENCES, allEntries = true), @CacheEvict(value = CacheNames.CACHE_USER_SESSIONS, allEntries = true), @CacheEvict(value = CacheNames.CACHE_TEMPORARY_TOKENS, allEntries = true) }) public void clearAllCaches() { log.info(\u0026#34;Clearing ALL cache entries from all caches\u0026#34;); } /** * Conditional caching example */ @Cacheable( value = CacheNames.CACHE_TEMPORARY_TOKENS, key = \u0026#34;#token\u0026#34;, condition = \u0026#34;#token.length() \u0026gt; 10\u0026#34; ) public String validateToken(String token) { log.info(\u0026#34;Cache MISS for token validation, token: {} - This would be a database call\u0026#34;, token); simulateSlowOperation(); return \u0026#34;Token \u0026#34; + token + \u0026#34; is \u0026#34; + (token.length() \u0026gt; 15 ? \u0026#34;valid\u0026#34; : \u0026#34;invalid\u0026#34;); } /** * Generate a random token for testing */ public String generateToken() { String token = UUID.randomUUID().toString(); log.info(\u0026#34;Generated new token: {}\u0026#34;, token); return token; } /** * Helper method to simulate slow database operations */ private void simulateSlowOperation() { try { // Simulate database latency Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } https://www.spiceworks.com/tech/tech-101/articles/what-is-cache\nhttps://docs.spring.io/spring-boot/reference/io/caching.html#io.caching.provider.simple\n","permalink":"https://gurkanucar.com/tr/posts/spring-boot-in-memory-cache-caffeine/","summary":"\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/posts/spring-boot-in-memory-cache-caffeine/image-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003eCache, en basit haliyle veriyi önbellekte saklayan ve tekrar ihtiyaç duyulduğunda, ek bir işlem gerektirmeden hızlıca bu önbellekten getiren bir mekanizmadır. Normal şartlarda bir veriye erişmek istediğimizde doğrudan veritabanına sorgu atarız. Ancak bu, özellikle yoğun erişimlerde, veritabanına sürekli sorgu atılmasına ve dolayısıyla performansın düşmesine neden olur.\u003c/p\u003e","title":"Spring Boot In Memory Cache - Caffeine"},{"content":"\nZaman zaman birileriyle sohbet ederken “Nasıl geliştirici olunur?”, “Zaten geliştiriciyim ama kendimi nasıl daha iyi bir noktaya taşırım?” gibi sorular geliyor. Bu yazıda doğrudan bu sorulara cevap vermektense, kendi yolculuğumu, yaptığım hataları, doğruları ve yanlışları paylaşmak istiyorum. Anlatacaklarım bu konu hakkında fikir edinmenize yardımcı olacaktır diye umuyorum.\nUfak bir not: Burada paylaştığım şeyler kesinlikle bir tavsiye değil. Sadece kendi deneyimlerimi ve düşüncelerimi anlatıyorum. Lütfen söylediklerimi mantık süzgecinden geçirip, sadece sana uygun olanları ciddiye al. Bir şekilde hayatını olumsuz etkilemek beni gerçekten üzer.\nAsıl amacım yazılımı öğrenmek değil, yazılım ile sorun çözebilmekti\nÖncelikle yazılıma başlama amacım, yazılımcı olmak değil de aklımdaki bir sorunu bilgisayar yardımı ile çözüp hayata geçirebilmekti. Yazılım; bunu yapabilmem için sadece bir araç görevi görüyordu. Örnek olarak Visual Basic 6.0 ile yaptığım ilk uygulamalardan birisi, fizik dersindeki formülleri hesaplamaya yarıyordu.\nDaha sonra öğrendiğim Arduino ve C# ile geliştirdiğim uygulamalar da, uzaktan kumandalı aracı bilgisayardan kontrol etmeye yarıyordu.\nDolayısıyla, yazılım öğrenmedeki motivasyonumuz problem çözebilmek üzerine olmalı. Aksi takdirde yazılımı sırf öğreneyim, para kazanayım kafasıyla öğrendiğinizde hem ilerlemek çok sancılı oluyor hem de gerekli özeni göstermediğinizden süreç uzuyor.\nKendi günlük hayatınızda kullanabileceğiniz basit yazılım projeleri:\nUrl Kısaltıcı Doğumgünü, özelgün vs hatırlatıcı Komik paylaşımları, capsleri tutabileceğiniz bir veritabanı Şifrelerinizi saklayabileceğiniz bir platform Her şeyden azar azar ama hiçbirinden tam değil\nBu süreçte yaptığım yanlışlardan ilki, birşeyi tam öğrenmeden yeni bir dile geçmekti. Örneğin Visual Basic de for-while döngüsü, if-else\u0026rsquo;i vs öğrenir hemen yeni bir dile geçerdim. Sonra da yine başka bir dile. OOP, class falan hak getire. Daha da vahim olanı, ben if-else, for-while döngüsü yazabildiğim her dili bildiğimi kabul ederdim. Frameworkler vs hakkında en ufak bir fikrim yoktu. En fazla yapabildiğim; txt, SQLite veya AccessDB ye veri yazıp okumaktı.\nŞu an geriye dönü baktığımda önce X dilinin her şeyini, ayrıntılarını öğrenir daha sonra başka dillere/platformlara geçerdim. Bir alanda uzman olmak, diğer alanlarda da nasıl ilerlemeniz gerektiği hakkında hem size fikir vermekte hem de daha kolay platform değiştirmenize olanak sağlamakta.\nYazılım araba sürmek gibidir. Hangi marka olursa olsun direksiyon, gaz, fren hep vardır. Sadece yerleri, konforu ya da tasarımı değişir.\nFrontend/mobil alanında kariyerlerinde ilerleyen arkadaşların bunu nispeten gözlemlemesi zor olsa da, backend geliştiren arkadaşlar bir süre sonra şunu farkedecekler:\nSiz java da yazsanız, node js de yazsanız, python da yazsanız istisnasız hepsinde:\nCRUD (create, read, update, delete) işlemleri var. Zaten yazılımın büyük çoğunluğu business rules (iş kuralı) a göre değişen crud operasyonları. Bunu da öğrenmek için yapılabilecek en mantıklı şey içerisinde 1-1, 1-n, n-m tablo ilişkileri bulunan bir proje yapmak. Controller (route, api), istekleri karşılayan ana yapı. GET, POST, PUT, DELETE methodlarının kod syntaxı olarak yazılışı farklı sadece. Hepsinde path variable, request parameter vs ortak olarak var. Api validations yine tüm dillerde mevcut. Bazılarında şema objesi (node js express) üzerinden oluyor, bazılarında anotasyon (java) ile. Loglama, Swagger/OpenAPI (koddaki yazdığınız apileri tarayıp bulan ve deneme yapmanızı kolaylaştıran dokuman aracı) hepsinde mevcut. Exception handling de yine tüm platformlarda ortak, olması gereken bir yapı. Authentication-Authorization (Kimliklendirme ve Rol Yetkisi), JWT token artık defacto olmuş ve projelerin yüzde 90\u0026rsquo;ı bu standartı kullanıyor. Ek olarak OAUTH2 gibi standartları kullananlar da oldukça yaygınlaştı. Neredeyse tüm diller/platformlar için kütüphanesi var. Buradan çıkartacağımız sonuç; bir dil için yukarıdaki özelliklere sahip full+full paket bir proje yaptığınızda herhangi bir X platformuna geçip aynı şeyleri orada da uygulamanız çok basit.\nÖğrenciyken paranın miktarını önemsemek\nGeriye dönüp baktığımda gözlemlediğim bir diğer hatam; paraya, deneyim kazanmaktan daha fazla öncelik vermekti. Üniversite 1. sınıfta pandeminin de araya girip okulların uzaktana dönmesi sebebiyle evde iyice canım sıkılmaya başlamıştı. Yine yukarıda belirttiğim döngüdeki gibi azıcık ondan azıcık şundan, for döngüsü, if-else falan yazar sonra da yeni dile geçerdim. Bu da bana her şeyi biliyormuşum ve işe hazırmışım gibi bir özgüven vermişti. Üniversiteden hocam ile tanıştıktan sonra kendisine staj yapabileceğim bir yer arayışında olduğumu söylemiştim. O da sağolsun tanıdığı tecrübeli ve startup sahibi olan bir abi ile tanışmama ve sonrasında staja başlamama vesile olmuştu.\nStaja başladığımda düşük bir ücretle başlamıştım ve çevremden bazı staja başlayan arkadaşlardan da benden yüksek farklı rakamlar duyuyordum. Bu motivasyonumu çok düşürüyordu. Halbuki çok şey öğreniyordum bir de üstüne para alıyordum fakat bunu o zaman için akıl edemiyordum. Aslında işletme açısından düşünecek olursak da ortada haksız bir durum yoktu. Şu an kendimi sorguluyorum ve diyorum ki acaba kendi iş yerim olsa, yalnızca ufak bir kaç bişey bilen bir üniversite 1. sınıf öğrencisini alsam ne kadar para verirdim diye. Bence öğrenci/stajyer iken düşük ücretlere morali bozulan arkadaşlar kendilerini işletmenin sahibi yerine koymalı ve tekrar düşünmeli. Para yerine edindiği tecrübeden motive olmalı. Fakat mezun olduktan ve 1,2 sene tecrübe kazandıktan sonra kendinizi elbetteki piyasanın altında konumlandırmamalı ve hakkınızı aramalsınız. Çünkü o işi artık profesyonel olarak icra ediyorsunuz.\nStaj yapmaya başladığım 2021 senesinden günümüze net maaşlarım - 2023 ilk maaşım (gerçek rakamlar arasında ufak farklar mevcuttur - son maaşlarımı içermemektedir)\nİşverenlerin gözünde topluluklarda rol almak çok da önemli olmayabilir\nÜniversitede okurken bazı topluluklarda yer aldım, eğitim verdim. Bunlardan üniversiteye ait topluluklar da vardı, üniversite dışından olan da. Aralarında en çok verim ardığım şüphesiz ki üniversite dışından olan, Hollanda ve Almanyadaki Türk yazılımcı abilerin kurmuş olduğu FolksDEV topluluğu idi.\nTopluluklardan bazı arkadaşlar, üniversiteden mezun olup iş hayatına girdğinde konuşmacı olarak etkinliklere katılırlardı. Çoğunun söylediği, toplulukta idari görev yapmanın iş hayatında pek bir karşılığı olmadığıydı. Burada geriye dönüp baktığımda mantıklı bulduğum konu; sırf topluluğa girmiş olmak için girmek yerine o toplulukta kendi mesleki gelişimimi artıracak roller almak ve o topluluğu network aracı olarak kullanmak oldu**.** Tavsiyem, bu topluluklarda boşa vakit harcamayın. Eğer size bir şey katmıyorsa boşuna idari roller almayın, teknik roller üstlenin, mesleki anlamda kendinizi projeler/staj yaparak geliştirin veya okul dışında sektörden insanların oluşturduğu topluluklara katılıp gerçek network edinin.\nÖnerebileceğim bazı topluluklar: FolksDEV, Turkey Java Community, Devnot, Java Users Group\nHiç bir zaman, çok çalıştım ve bunun kesinlikle karşılığı olacak diye bir yanılgıya düşmedim\nLinkedin\u0026rsquo;de bazı serzeniş postları görüyorum. Adam paylaşıyor, youtube daki tutorial dan bakarak html-css-js website tasarlamış ve diyor ki \u0026ldquo;Aylardır proje yapıyorum hala iş yok\u0026rdquo;. Başka birisi; react ile inmemory çalışacak, api bile çağırmayan proje yapmış yine aynı serzenişi söylüyor. Kendi fikrimce buradaki en büyük yanılgı, sektörün ihtiyacı olmayan bir şeyi öğrenmeye vakit ayırıp karşılık aramak. Eğer millet react ile next js ile api\u0026rsquo;ye istek atıp işlemler yapıyor, login logout jwt mekanizması ekliyor, next js, supabase vs ile uçup kaçarken; sen gidip de html-css-js ile 6 aydır proje yapıyorum iş yok dersen sana \u0026ldquo;\u0026hellip;.. git\u0026rdquo; derler.\nBurada kilit konu sektörden arkadaşlar edinip veya üniversiteden hocalara danışıp hangi teknolojinin sektörde tercih edildiğini öğrenmek. Ek olarak hocalara danışma meselesinde de dikkat etmek gerek. Çünkü akademi ile gerçek hayattaki durumlar çoğu zaman birbiri ile örtüşmüyor. Aksi takdirde halen visual basic yazan bir hoca sektörde java kullanılmıyor diyebilir ve hayatınızı yanlış etkileyebilir.\nSonuç olarak çalışmanın her zaman karşılığını alacağını beklemek yerine yaptığımız çalışma doğru mu diye sorgulayıp ilerlemek daha güzel bir yöntem olabilir.\nYetiştirilmek üzere iş arıyorum\nBir diğer mantıksız bulduğum olay Linkedin\u0026rsquo;de \u0026ldquo;Selam X üniversitesi mezunuyum, Y alanında yetiştirilmek üzere iş/staj arıyorum.\u0026rdquo; diyerek yapılan paylaşımlar.\nSevgili arkadaşım, kendini bir işveren yerine koy ve birinin sana gelip \u0026ldquo;abi/abla ben sana muhtacım, yetiştirilmek üzere iş arıyorum ama bana para da vermelisin, üstüne ben işi öğrendikten 1 sene sonra maaşı beğenmeyip seni bırakıp gideceğim ve tecrübe ile kazandığım para da yanıma kar kalacak\u0026rdquo; dese, nasıl cevap verirdiniz? Adama sorarlar 4 sene boyunca okulda ne yaptın diye. Böyle bir durumdaki arkadaşın iş bulma olasılığı çok düşük.\nAlmadan vermek, Allah\u0026rsquo;a mahsustur\n(bir işveren atasözü)\nBurada kendimce yapılabilecek en mantıklı hareket öncelikle bir çok proje geliştirip işverenin karşısına dolu dolu çıkabilmek. Bir şeyler yapabildiğinizi göstermek. Elbetteki işveren yeni mezundan çok bir şey bekleyemez ama hiç bir şey bilmeyen bir adama da para vermek sizce ne kadar doğru?\nİlk işe/staja girmeyi kolaylaştırmak için alınabilecek aksiyonlar:\nİş/Staj deneyimi olmasa bile sürekli projeler yapıp github da paylaşmak. Eğer proje geliştirme konusunda eksiğiniz var ise youtube dan tutorial izleyip aynılarını yapmak. Eğer biraz kendinizi geliştirdiyseniz X Clone, Y Clone gibi projeler yapıp bunlara kendinizden bir şeyler katmak. Üniversite boş diyenlere aldırmayıp; üniversitedeki arkadaşlarınızın, üst dönemlerin, topluluktan tanıştığınız kişilerin veya hocalarınızın networkü aracılığı ile iş aramak. İlk işleriniz için maaş beklentinizi düşük tutmak. Bu demek değil ki ileride de maaşım düşük olacak. Çoğunluğun 35-40 bin istedigi yerde 25-30 civari isterseniz işe girme olasılığınız artabilir. Üniversite birinci sınıftan itibaren staja başlayıp deneyim kazanmak. Eğer Linkedin\u0026rsquo;den iş arayışınızı paylaşacaksanız kesinlikle ilgilenene cv mi iletirim falan yazmayın. Direkt cv nizi, bilgilerinizi paylaşıma ekleyin. İnsan kaynaklarının size ulaşmasını kolaylaştırın. İnsan kaynakları ve müdür poziyonundaki kişileri ağınıza ekleyin. Asla \u0026ldquo;yetiştirilmek üzere\u0026rdquo; ifadesini kullanıp kendinizi işverene karşı güçsüz göstermeyin. İnternetten mülakat sorularına çalışmanız mülakatın iyi geçme olasılığını artırır. Ek olarak her mülakattan çıktıktan sonra bilmediğiniz konuları, soruları vs not alın. Direkt firmalarin bünyesine girmektense dış kaynak olarak işe başlayıp zaman içerisinde firmaya geçmeyi deneyin. Burada önerebileceğim daha önce çalıştığım iki firma var. Biri metasis diğeri experilabs. Çalışmadığım fakat adını sıkça duyduğum yerler ise: RDC, OBSS, Infonal, Mirsis. (not: reklam degildir, benim iyi deneyim yasamam sizin de iyi deneyim yasayacaginizi garanti etmez.) Medium da öğrendiğiniz bilgileri makale şeklinde paylaşın. Yaptığınız projeleri, yazdığınız makaleleri Linkedin\u0026rsquo;de paylaşın. Kurumsalda değil, startup da işe başlamak\nGeriye dönüp baktığımda yaptığım en mantıklı hareket neydi diye soracak olursanız, muhtemelen bu derdim. İlk işinize startup da başladığınızda; production ortamının database şifresi de dahil olmak üzere bir çok konuda bilgi sahibi olur, bir çok farklı alanda çalışmalar yaparsınız. Bu sayede büyük resmi görebilme imkanınız da olur.\nEğer ilk işinize kurumsalda başlarsanız muhtemelen çok büyük bir organizasyonun ufak bir parçası olacaksınız. Gidip de X, Y, Z ihtiyacınız olduğunda bunu siz değil bunun için özel olarak ayrılan ekipler yapacak ve siz arkada ne olup bittiğinin farkına bile varmayacaksınız.\nölümü gösterip sıtmaya razı etmek (!)\nStartup da ilk işe başlamanın bir diğer artısı da; yoğun tempoya alışıp kurumsal firmaya geçtiğinizde rahatlama yaşayacak olmanız. Diyeceksiniz ki adamlar ne basit tasklara kaç günlük eforlar veriyor. O taskları havada karada yapacaksınız. Farkınızı emin olun göstereceksiniz. Bir de olduğunuz yerden çok yüksek ihtimalle memnun olup bir daha startup a geçmeyi falan düşünmeyeceksiniz. Ama ilk defa kurumsalda işe başlayan arkadaşlar muhtemelen sahip oldukları nimetin farkında olmadığından mutsuz olacaklar. Belki de daldan dala ufak paralar için atlayacaklar.\nBunları her geliştirici alanından bağımsız olarak temel bir şekilde bilmeli\nAşağıda yazacağım konulara bu yazıda değinmeyeceğim. Alttaki konuların gerek mobil, gerekse backend, frontend vs herkesin bilmesi gerektiğini düşünüyorum.\nTemel Network bilgisi, IP, Domain, DNS, HTTP, HTTPS, SSL Backend, frontend vs kavramlar MySQL veya SQLite 1-1, 1-n, n-m ilişkilerin olduğu çok basit backend crud uygulaması (ister java ister python veya node js) html + css (div ortalayabilecek kadar) + js (api ye istek atacak kadar) react, angular, vue gibi frameworkleri kullanarak backend api ye istek atan basit bir frontend JWT token, hem server tarafında hem de client tarafında implementasyonu RestAPI NGINX Docker Postman kullanımı Linux işletim sistemi temel komutları Git Versiyon sistemi readme.md dokumentasyon, markdown formatı Sektördeki son gelişmelerden haberdar olmak\nBunu yapabilmek için aşağıdaki yöntemleri tercih edebilirsiniz:\nO dil/platform hakkında özelleşmiş toplulukları ve topluluktaki kişileri LinkedIn, Twitter gibi mecralardan takip etmek LinkedIn\u0026rsquo;de ilgili gruplara katılmak Youtube da paylaşım yapan o platform ile alakalı kanalları takip etmek Java özelinde tavsiye edebileceğim youtube kanalları, web siteler, kişiler, topluluklar vs.\nhttps://javagyansite.com\nhttps://www.vinsguru.com\nhttps://javarevisited.blogspot.com\nhttps://www.java67.com\nRobert C. Martin\nMartin Fowler\nVenkat Subraminam\nLemi Orhan (https://speakerdeck.com/lemiorhan)\nDevoxx (https://www.youtube.com/@DevoxxForever)\nÇağkan kantarcı (https://www.youtube.com/@cagkankantarci)\nFolksDEV (https://www.youtube.com/@FolksDev)\nTurkey Java Community (https://www.youtube.com/@TurkiyeJavaCommunity)\nJava Users Group (https://www.youtube.com/@IstanbulJavaUserGroup)\nBouali Ali (https://www.youtube.com/@BoualiAli)\nDaily Code Buffer (https://www.youtube.com/@DailyCodeBuffer)\nAmigos Code (https://www.youtube.com/@amigoscode)\nDan Vega (https://www.youtube.com/@DanVega)\nSiva Labs (https://www.youtube.com/@sivalabs)\nProgramming Techie (https://www.youtube.com/@ProgrammingTechie)\n","permalink":"https://gurkanucar.com/tr/posts/java-gelistirici-olmak-icin-izledigim-yol/","summary":"\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/posts/java-gelistirici-olmak-icin-izledigim-yol/AGV_vUfbA-ajbOkPXOJEg00ji6URu6DoAhE1NIWRQ1-oTFmw-33bf788f.png\"\u003e\u003c/p\u003e\n\u003cp\u003eZaman zaman birileriyle sohbet ederken “Nasıl geliştirici olunur?”, “Zaten geliştiriciyim ama kendimi nasıl daha iyi bir noktaya taşırım?” gibi sorular geliyor. Bu yazıda doğrudan bu sorulara cevap vermektense, kendi yolculuğumu, yaptığım hataları, doğruları ve yanlışları paylaşmak istiyorum. Anlatacaklarım bu konu hakkında fikir edinmenize yardımcı olacaktır diye umuyorum.\u003c/p\u003e","title":"Geliştirici olmak için izlediğim yol"}]