<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: JongHwa</title>
    <description>The latest articles on Forem by JongHwa (@jonghwayoo3_dev).</description>
    <link>https://forem.com/jonghwayoo3_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3646210%2F63e876d7-853b-4ac9-9058-06bea8d8d99c.jpg</url>
      <title>Forem: JongHwa</title>
      <link>https://forem.com/jonghwayoo3_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jonghwayoo3_dev"/>
    <language>en</language>
    <item>
      <title>[CS] Operating System</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 12 Feb 2026 01:22:38 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/cs-operating-system-2cml</link>
      <guid>https://forem.com/jonghwayoo3_dev/cs-operating-system-2cml</guid>
      <description>&lt;h2&gt;
  
  
  1.Process is an 'Independent House', Thread is a 'Roommate'
&lt;/h2&gt;

&lt;p&gt;To understand how computer programs work, we can imagine them as houses.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏠 Process = "An Independent House"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; A basic unit of work that gets resources from the Operating System (OS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Features:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Independence:&lt;/strong&gt; It is completely separate from other houses. If the house next door catches fire, your house is safe!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; Building a house (creating a process) is heavy work. It takes a lot of land (RAM) and time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧔 Thread = "Roommates in a House"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; A unit of execution that actually does the work inside a process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Features:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sharing:&lt;/strong&gt; Roommates share the living room (Heap memory) and the fridge. Communication is very fast because they live together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight:&lt;/strong&gt; Adding one person is much easier and faster than building a whole new house.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk:&lt;/strong&gt; If one roommate starts a fire (a critical error), the whole house (the process) burns down.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example:&lt;/strong&gt; This is why if your Chrome browser crashes, your KakaoTalk app still works fine. They are in different houses!&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpnbfh02h2gws9gw12yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpnbfh02h2gws9gw12yg.png" alt=" " width="733" height="527"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How to Stop Fighting (Synchronization)
&lt;/h2&gt;

&lt;p&gt;When roommates share one fridge (Heap), they might fight. Here are two ways to keep the peace.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔒 1. Synchronized (Pessimistic Lock)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"The world is dangerous. I will lock the door while I use the fridge!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100% Safe:&lt;/strong&gt; Since the door is locked, the data never gets messy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good for complex tasks:&lt;/strong&gt; It is safe when you have many steps to do, like taking food out, cooking, and putting it back.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Cons:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deadlock Risk:&lt;/strong&gt; Roommate A has the bathroom key, and B has the kitchen key. They both wait for each other forever, and the whole house stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow Speed:&lt;/strong&gt; If the line is long, other roommates must wait and do nothing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚛️ 2. Atomic / CAS (Optimistic Lock)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"I don't think anyone touched it while I was gone. Oh, they did? Then I'll try again!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Very Fast:&lt;/strong&gt; There is no locking or unlocking. It is great for simple tasks like adding numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Waiting:&lt;/strong&gt; Roommates don't stand in line. If they fail, they just try again immediately.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Cons:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infinite Loop:&lt;/strong&gt; If 100 people try to open the fridge at the same time, they might keep failing and trying again forever. This makes the CPU very busy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ABA Problem:&lt;/strong&gt; If a value changes from 100 to 200 and back to 100 while you are away, the system thinks "Oh, nothing changed!" even though it did.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzkhtazbter3rhii2hw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzkhtazbter3rhii2hw4.png" alt=" " width="605" height="423"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>[System Design] Scaling for User Growth</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Sun, 01 Feb 2026 18:57:40 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/system-design-scaling-for-user-growth-ljg</link>
      <guid>https://forem.com/jonghwayoo3_dev/system-design-scaling-for-user-growth-ljg</guid>
      <description>&lt;h1&gt;
  
  
  Scaling from 1 to Millions: System Design Fundamentals
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;English Summary:&lt;/strong&gt; In this post, I explore the fundamental concepts of system design, from a single server setup to a distributed architecture handling millions of users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;저처럼 시스템 설계에 막 입문하신 분들에게 도움이 되길 바라며, 공부한 내용을 직접 그린 도표와 함께 정리해 보았습니다.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. 단일 서버 (Single Server Setup)
&lt;/h3&gt;

&lt;p&gt;모든 위대한 시스템은 단 한 대의 서버에서 시작됩니다. 웹 앱, DB, 캐시가 모두 한 곳에 모여 있는 구조죠.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqgtwpdcr98swzemqrbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqgtwpdcr98swzemqrbf.png" alt="1-1 기본 시스템" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;특징:&lt;/strong&gt; 구성이 단순하지만, 서버 하나가 죽으면 서비스 전체가 중단되는 위험(SPOF)이 있습니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. 데이터베이스 분리 (Separating the Database)
&lt;/h3&gt;

&lt;p&gt;사용자가 늘어나면 웹 트래픽 처리용 서버와 데이터 보관용 DB 서버를 분리해야 합니다.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8whol8o1rxdzoua3qth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq8whol8o1rxdzoua3qth.png" alt=" " width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NoSQL이 필요한 경우:&lt;/strong&gt; 아주 낮은 응답 지연시간이 필요하거나, 데이터 양이 폭발적으로 많을 때 비-관계형 DB가 좋은 대안이 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. 수직적 vs 수평적 확장 (Vertical vs Horizontal Scaling)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;수직적 확장(Scale-up):&lt;/strong&gt; 서버의 CPU나 메모리를 높이는 것. 한계가 명확하고 장애 대응이 어렵습니다.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;수평적 확장(Scale-out):&lt;/strong&gt; 서버 대수를 늘리는 것. 대규모 트래픽 처리를 위한 현대 시스템의 핵심입니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. 부하 분산: 로드밸런서 (Load Balancer)
&lt;/h3&gt;

&lt;p&gt;서버를 여러 대 두었다면, 트래픽을 골고루 나눠주는 '교통경찰'이 필요합니다.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xmx4cfcb34vhfeenece.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xmx4cfcb34vhfeenece.png" alt=" " width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;역할:&lt;/strong&gt; 특정 서버에 부하가 몰리는 것을 방지하고, 한 서버가 죽어도 다른 서버로 유저를 안내합니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. 데이터 안정성: DB 다중화 (DB Replication)
&lt;/h3&gt;

&lt;p&gt;DB도 여러 대를 두어 '주(Master)-부(Slave)' 구조로 관리합니다.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e9r71d8l3ph1aio6b7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7e9r71d8l3ph1aio6b7n.png" alt=" " width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;장점:&lt;/strong&gt; 성능 향상은 물론, 자연재해 등으로 서버가 파괴되어도 데이터를 안전하게 보존할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6. 더 빠른 응답을 위한 캐시 &amp;amp; CDN
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; 자주 찾는 데이터를 메모리에 저장해 DB 부하를 줄입니다.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN:&lt;/strong&gt; 이미지나 영상 같은 정적 콘텐츠를 유저와 가까운 곳에서 빠르게 전달합니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivvtv61o7ajg5cvd67uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivvtv61o7ajg5cvd67uy.png" alt=" " width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  7. 무상태(Stateless) 웹 계층
&lt;/h3&gt;

&lt;p&gt;웹 서버를 수평적으로 확장하려면 상태 정보를 서버에서 제거해야 합니다. 세션 데이터를 DB나 Redis 같은 공유 저장소에 보관하여 어떤 웹 서버든 요청을 처리할 수 있게 만듭니다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgx3glc1imcb8559kiux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgx3glc1imcb8559kiux.png" alt=" " width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  8. 데이터 센터 (Data Center)
&lt;/h3&gt;

&lt;p&gt;지리적으로 떨어진 여러 곳에 데이터 센터를 구축하여 가용성을 높이고 유저에게 더 빠른 응답을 제공합니다. (지리적 라우팅)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9esgpjagu2rutdmddka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9esgpjagu2rutdmddka.png" alt=" " width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  9. 메시지 큐 (Message Queue)
&lt;/h3&gt;

&lt;p&gt;메시지 큐는 서비스 간의 결합도를 낮추고, 비동기 처리를 가능하게 하는 핵심 컴포넌트입니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;비동기 처리:&lt;/strong&gt; 시간이 오래 걸리는 작업(예: 이메일 발송, 이미지 프로세싱)을 큐에 던져두고, 웹 서버는 즉시 응답을 반환할 수 있습니다.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;서버 간 결합도 완화(Decoupling):&lt;/strong&gt; 생산자(Producer)는 메시지를 발행하고, 소비자(Consumer)는 이를 받아 처리하므로 두 서비스가 서로를 직접 몰라도 작동합니다.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;안정성:&lt;/strong&gt; 트래픽이 갑자기 몰려도 메시지 큐가 버퍼 역할을 해주어 소비자 서버가 죽지 않고 차례대로 일을 처리할 수 있게 돕습니다.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  10. 로그, 메트릭 그리고 자동화
&lt;/h3&gt;

&lt;p&gt;시스템이 커지면 모니터링은 필수입니다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;로그:&lt;/strong&gt; 오류 추적&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;메트릭:&lt;/strong&gt; 시스템 성능 수치화&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;자동화:&lt;/strong&gt; CI/CD를 통한 안정적인 배포&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  11. 데이터베이스의 규모 확장 (Sharding)
&lt;/h3&gt;

&lt;p&gt;DB의 부하를 나누기 위해 &lt;strong&gt;샤딩(Sharding)&lt;/strong&gt;을 도입합니다. 데이터를 여러 서버에 분산 저장하여 개별 DB의 크기를 줄이는 방식입니다.&lt;/p&gt;




&lt;h3&gt;
  
  
  12. 백만 사용자, 그리고 그 이상
&lt;/h3&gt;

&lt;p&gt;결국 대규모 시스템 설계는 반복적인 개선 과정입니다. 무상태 계층, 다중화, 캐시, 그리고 샤딩까지 도입된다면 백만 명 이상의 사용자도 거뜬히 감당할 수 있는 구조가 됩니다.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32tz882etuy6kwav2okk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32tz882etuy6kwav2okk.png" alt=" " width="790" height="690"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>architecture</category>
      <category>backend</category>
      <category>beginners</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Part 4: Advanced Strategies for High Traffic</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 29 Jan 2026 18:18:24 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/extra-advanced-strategies-for-high-traffic-585m</link>
      <guid>https://forem.com/jonghwayoo3_dev/extra-advanced-strategies-for-high-traffic-585m</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In the previous parts, we built the foundation. But in a real production environment, we face tougher challenges: &lt;strong&gt;System crashes, Data loss, and Database bottlenecks.&lt;/strong&gt; Here is how we solve them.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Guaranteeing Atomicity with Lua Scripts
&lt;/h3&gt;

&lt;p&gt;When checking stock and issuing a coupon, we must ensure these two steps happen as &lt;strong&gt;one single unit&lt;/strong&gt;. If another request sneaks in between, we might over-issue coupons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;br&gt;
We use a &lt;strong&gt;Redis Lua Script&lt;/strong&gt;. Redis executes the script in a single thread, meaning no other operation can interrupt it. It’s the perfect way to prevent "Race Conditions."&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Database Protection: Throttling &amp;amp; Batching
&lt;/h3&gt;

&lt;p&gt;Even if Redis is fast, the Database (MySQL) can be a bottleneck. If we send 10,000 "Insert" queries at once, the DB connection pool will be exhausted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Message Queue (Kafka/RabbitMQ)&lt;/strong&gt;: We send the results to a queue first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worker Throttling&lt;/strong&gt;: A background worker pulls data from the queue at a controlled speed (e.g., 200 records/sec).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch Insert&lt;/strong&gt;: Instead of 1,000 separate queries, we group them into one single &lt;code&gt;INSERT&lt;/code&gt; query to reduce IO overhead.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Handling Failures: Eventual Consistency
&lt;/h3&gt;

&lt;p&gt;What happens if the Redis operation succeeds, but the Database save fails? This is a &lt;strong&gt;Data Integrity&lt;/strong&gt; issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strategy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry with Back-off&lt;/strong&gt;: The system automatically retries the save operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead Letter Queue (DLQ)&lt;/strong&gt;: If it fails after 3-5 tries, the data is moved to a DLQ. Engineers can then check the logs and manually fix the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Goal&lt;/strong&gt;: We accept that data might be delayed for a few seconds, but we ensure it is &lt;strong&gt;eventually consistent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Building a high-performance system is a balance between &lt;strong&gt;Speed&lt;/strong&gt; and &lt;strong&gt;Safety&lt;/strong&gt;. By using Redis for speed and Message Queues for safety, we can build a backend that never crashes.&lt;/p&gt;




</description>
    </item>
    <item>
      <title>Part 3: Final Architecture &amp; Lessons Learned</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 29 Jan 2026 18:08:23 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/part-4-final-architecture-lessons-learned-on4</link>
      <guid>https://forem.com/jonghwayoo3_dev/part-4-final-architecture-lessons-learned-on4</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Opener Market&lt;/strong&gt; project is finally coming to an end. In this final post, I will review the overall system architecture and share the key lessons learned from building a high-concurrency e-commerce platform.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Big Picture: System Architecture
&lt;/h3&gt;

&lt;p&gt;Our system is divided into three main domains, each solving a specific high-traffic problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Coupon Domain&lt;/strong&gt;: Uses &lt;strong&gt;Redis Lua Scripts&lt;/strong&gt; for atomic stock control and a &lt;strong&gt;Write-Back&lt;/strong&gt; pattern to ensure data consistency without slowing down the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Domain&lt;/strong&gt;: Implements &lt;strong&gt;Database Locking&lt;/strong&gt; (Pessimistic/Optimistic) to prevent over-selling and ensure transaction integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search Domain&lt;/strong&gt;: Powered by &lt;strong&gt;QueryDSL&lt;/strong&gt; for type-safe dynamic queries and optimized with composite indexes for sub-second latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3316pcqx2cl3zibmpqu5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3316pcqx2cl3zibmpqu5.png" alt=" " width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Performance Milestones
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search Latency&lt;/strong&gt;: Reduced from &lt;strong&gt;1.8s&lt;/strong&gt; to &lt;strong&gt;0.5s&lt;/strong&gt; (72% improvement).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput&lt;/strong&gt;: Successfully handled high-concurrency coupon issuance using Redis memory instead of DB locks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Guaranteed atomic transactions across Balance, Stock, and Points.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Key Lessons
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory-First Strategy&lt;/strong&gt;: For high-traffic events, the Database should be the last resort. Redis is essential for protecting the core system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Processing&lt;/strong&gt;: Decoupling the user response from heavy tasks (like DB persistence) is the secret to a smooth UX.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Integrity vs. Speed&lt;/strong&gt;: In e-commerce, speed is important, but accuracy is everything. Always use &lt;code&gt;@Transactional&lt;/code&gt; and proper locking strategies.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Building &lt;strong&gt;Opener Market&lt;/strong&gt; was a deep dive into the world of scalable backend systems. This project taught me how to balance complex requirements with performance optimization.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Part 2: Optimizing Search Performance"</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 29 Jan 2026 18:02:14 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/part-2-optimizing-search-performance-22h5</link>
      <guid>https://forem.com/jonghwayoo3_dev/part-2-optimizing-search-performance-22h5</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;As data grows, search speed becomes a major bottleneck. In this post, I will share how I optimized product search from 1.8s to 0.5s using &lt;strong&gt;QueryDSL&lt;/strong&gt; and &lt;strong&gt;Database Indexing&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Challenge: Dynamic Queries
&lt;/h3&gt;

&lt;p&gt;Users want to filter products by category, price, and rating, while sorting by sales or newest arrival. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem&lt;/strong&gt;: Writing raw SQL for every combination is impossible to maintain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Goal&lt;/strong&gt;: Build a type-safe, dynamic search engine that stays fast even with 1M+ records.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Implementation: QueryDSL
&lt;/h3&gt;

&lt;p&gt;I used &lt;strong&gt;QueryDSL&lt;/strong&gt; to handle complex filtering logic without losing type safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;searchItems&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ItemSearchCondition&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;queryFactory&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;nameContains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;categoryEq&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategoryName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getSortOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSortType&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOffset&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPageSize&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Performance Tuning: Indexing
&lt;/h3&gt;

&lt;p&gt;Even with good code, a query is slow if it scans the entire table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem&lt;/strong&gt;: Sorting 1 million rows took over &lt;strong&gt;1.8 seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solution&lt;/strong&gt;: I analyzed the execution plan and applied &lt;strong&gt;Composite Indexes&lt;/strong&gt; on frequently used columns (e.g., &lt;code&gt;Category + SalesVolume&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result&lt;/strong&gt;: Latency dropped to &lt;strong&gt;under 0.5s&lt;/strong&gt; (72% improvement).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Advanced: N+1 Problem
&lt;/h3&gt;

&lt;p&gt;When fetching items with their categories, Hibernate often executes too many queries. I solved this by using &lt;code&gt;fetchJoin()&lt;/code&gt;, reducing the database load significantly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Optimizing search is not just about writing queries; it's about understanding how the database reads data. In the next part, we will dive into high-concurrency coupon systems.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>java</category>
      <category>performance</category>
    </item>
    <item>
      <title>Part 1: Reliable Payment &amp; Transaction</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 29 Jan 2026 17:58:45 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/part-1-reliable-payment-transaction-1njd</link>
      <guid>https://forem.com/jonghwayoo3_dev/part-1-reliable-payment-transaction-1njd</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In e-commerce, the checkout process must be &lt;strong&gt;atomic&lt;/strong&gt;. If one step fails, everything must roll back. Here is how I built a reliable payment system using &lt;strong&gt;Spring Boot&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Core Workflow
&lt;/h3&gt;

&lt;p&gt;When a user buys an item, three things must happen as a single unit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Balance&lt;/strong&gt;: Withdraw money from the wallet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stock&lt;/strong&gt;: Decrease quantity. If 0, set status to &lt;code&gt;SOLD_OUT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Points&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spend&lt;/strong&gt;: Users decide the exact amount (Min 1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Earn&lt;/strong&gt;: Get 2.5% back from the final payment.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Implementation: &lt;code&gt;@Transactional&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I used Spring's transaction management to ensure &lt;strong&gt;Data Integrity&lt;/strong&gt;. If the stock reduction fails, the money withdrawal is automatically canceled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Validation (Check Sold Out)&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Point Usage &amp;amp; Balance Deduction&lt;/span&gt;
    &lt;span class="c1"&gt;// 3. Stock Reduction (Atomic)&lt;/span&gt;
    &lt;span class="c1"&gt;// 4. Point Accrual (2.5% Reward)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Solving the "Logical Gap"
&lt;/h3&gt;

&lt;p&gt;In Step 1, simple subtraction was enough. But in a real system, Concurrency is the enemy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Problem: If two users buy the last item at the same time, we might get "Negative Stock."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Solution: I researched Database Locking to ensure only one process can update the stock at a time, preventing over-selling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Failure Recovery
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Validation First: Always check stock status before touching the balance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rollback: If an exception occurs during point accrual, the entire transaction is reverted by the @Transactional manager. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Data consistency is more important than speed in payments. Using @Transactional and strict validation is the first step toward a professional e-commerce backend.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>java</category>
      <category>springboot</category>
    </item>
    <item>
      <title>[Simple SNS Project] Step 4. Search Implementation &amp; Logging Strategy</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Sat, 10 Jan 2026 20:11:18 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-4-search-implementation-logging-strategy-1b0d</link>
      <guid>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-4-search-implementation-logging-strategy-1b0d</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In Step 3, I connected users via the Follow system. Now, in Step 4, I implemented a Search Feature to let users find content.&lt;br&gt;
More importantly, I established a Logging Strategy. I'm not just printing logs to the console; I'm saving user search patterns to a file to analyze trends later.&lt;/p&gt;


&lt;h3&gt;
  
  
  1. Search Implementation: LIKE Query
&lt;/h3&gt;

&lt;p&gt;For the search functionality, I used the standard SQL LIKE operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// FeedRepository.java&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Feed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findByContentContainingOrderByCreatedAtDesc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;How it works: It translates to SELECT * FROM feed WHERE content LIKE '%keyword%'.&lt;/li&gt;
&lt;li&gt;Performance Note: Starting a query with a wildcard (%) prevents the database from using standard B-Tree indexes (Full Table Scan).&lt;/li&gt;
&lt;li&gt;Future Improvement: For a production-level application with millions of rows, I would switch to Full-Text Search (MySQL) or a dedicated search engine like Elasticsearch. But for this project scope, LIKE is sufficient.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Logging Strategy: Why File Logging?
&lt;/h3&gt;

&lt;p&gt;Console logs disappear when the server restarts. To analyze user behavior (e.g., "What are people searching for?"), logs must be persisted.&lt;/p&gt;

&lt;p&gt;I configured logback in application.yml to write logs to a specific file (logs/simple-sns.log).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logs/simple-sns.log&lt;/span&gt;
  &lt;span class="na"&gt;logback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rollingpolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;max-history&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="c1"&gt;# Keep logs for 7 days&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the Service layer, I added a structured log message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[SEARCH] keyword: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specific format [SEARCH] acts as a tag that will make parsing easier in Step 6 (Statistics).&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Conclusion
&lt;/h3&gt;

&lt;p&gt;This step was about preparing for the future. By logging user actions, I am laying the groundwork for data-driven features like "Trending Keywords".&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>[Simple SNS Project] Step 3. Building the Follow System &amp; Timeline (N:M Relationship)</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Fri, 09 Jan 2026 21:28:20 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-3-building-the-follow-system-timeline-nm-relationship-2ke1</link>
      <guid>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-3-building-the-follow-system-timeline-nm-relationship-2ke1</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In Step 2, I built a personal feed. Now, in Step 3, I transformed the application into a real social network by implementing the Follow System.&lt;br&gt;
The biggest challenge was modeling the relationship between users and efficiently retrieving the Timeline (a mix of my posts and my friends' posts).&lt;/p&gt;


&lt;h3&gt;
  
  
  1. Database Modeling: The N:M Relationship
&lt;/h3&gt;

&lt;p&gt;A user can follow many people, and can be followed by many. This is a classic Many-to-Many (N:M) relationship.&lt;br&gt;
To handle this in a Relational Database (MySQL), I introduced a mapping table called Follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"follows"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;uniqueConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@UniqueConstraint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"follower_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"followee_id"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Prevent duplicate follows&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Follow&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;follower&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Who follows&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;followee&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Who is followed&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also added a UniqueConstraint to ensure data integrity at the database level, preventing a user from following the same person twice.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The Timeline Query: Pull Model
&lt;/h3&gt;

&lt;p&gt;How do we show the timeline?&lt;br&gt;
I adopted the Pull Model (Fan-out on Read) strategy for this stage. When a user requests their timeline, the system dynamically queries the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Logic:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Find all users that I follow.&lt;/li&gt;
&lt;li&gt;Extract their IDs.&lt;/li&gt;
&lt;li&gt;Query the Feed table using an IN clause.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// FeedRepository.java&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Feed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllByUserInOrderByCreatedAtDesc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this approach?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pros: Simple to implement and data consistency is guaranteed.&lt;/li&gt;
&lt;li&gt;Cons: As the number of followings grows, the IN query can become slow.&lt;/li&gt;
&lt;li&gt;Future Improvement: For a large-scale system, I would consider the Push Model (Fan-out on Write), where feeds are pre-distributed to followers' timelines upon creation. But for this project, the Pull Model is sufficient and efficient enough.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Frontend Integration
&lt;/h3&gt;

&lt;p&gt;I updated the React frontend to allow users to input a User ID and follow them. The timeline now seamlessly mixes my posts with my friends' posts, sorted by time.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Conclusion
&lt;/h3&gt;

&lt;p&gt;Handling relationships is the core of backend development. Through this step, I learned how to map entities using JPA and how to construct complex queries using Spring Data JPA's method naming conventions.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>java</category>
      <category>mysql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>[Simple SNS Project] Step 2. Optimizing Feed Performance: DB Indexing &amp; Rate Limiting</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Mon, 29 Dec 2025 22:59:44 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/step-2-optimizing-feed-performance-db-indexing-rate-limiting-3a6f</link>
      <guid>https://forem.com/jonghwayoo3_dev/step-2-optimizing-feed-performance-db-indexing-rate-limiting-3a6f</guid>
      <description>&lt;p&gt;Following the user registration feature in Step 1, I moved on to the core feature of any SNS: &lt;strong&gt;The Feed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, simply storing and retrieving data isn't enough. I had to consider &lt;strong&gt;performance&lt;/strong&gt; (how fast can we retrieve feeds?) and &lt;strong&gt;stability&lt;/strong&gt; (how do we prevent abuse?).&lt;/p&gt;

&lt;p&gt;In this post, I will share how I optimized database queries using &lt;strong&gt;Composite Indexes&lt;/strong&gt; and implemented a custom &lt;strong&gt;Rate Limiter&lt;/strong&gt; in Spring Boot.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Database Indexing Strategy
&lt;/h3&gt;

&lt;p&gt;The most common query for a feed is: &lt;em&gt;"Show me the latest posts written by User A."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Without an index, the database performs a &lt;strong&gt;Full Table Scan&lt;/strong&gt;, checking every single row. To optimize this, I applied a &lt;strong&gt;Composite Index&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"feeds"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Index&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"idx_user_created_at"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columnList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_id, created_at"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Feed&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why user_id?&lt;/strong&gt; To filter posts by the specific user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why created_at?&lt;/strong&gt; To sort them in descending order (latest first).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The database can now jump directly to the user's data and read it sequentially, significantly reducing query time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Protecting the Server: Rate Limiting
&lt;/h3&gt;

&lt;p&gt;One of the requirements was to limit users to &lt;strong&gt;5 requests per minute&lt;/strong&gt; to prevent spamming or DDoS-like behavior.&lt;/p&gt;

&lt;p&gt;Instead of using heavy external tools like Redis immediately, I implemented a lightweight &lt;strong&gt;In-Memory Rate Limiter&lt;/strong&gt; using Java's ConcurrentHashMap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; A Map stores the request count for each user ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; Before processing a request, check if the count exceeds 5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reset:&lt;/strong&gt; A @Scheduled task clears the map every minute.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RateLimiter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requestCounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConcurrentHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestCounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrDefault&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Block request&lt;/span&gt;
        &lt;span class="n"&gt;requestCounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0 * * * * *"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Reset every minute&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;resetCounts&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;requestCounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Frontend Integration (React)
&lt;/h3&gt;

&lt;p&gt;On the frontend, I handled the 429 Too Many Requests error gracefully. If the server rejects a request due to the rate limit, the user sees a friendly alert message instead of a crash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are posting too fast! Please wait a moment. 🛑&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Conclusion
&lt;/h3&gt;

&lt;p&gt;In this step, I learned that &lt;strong&gt;backend development is not just about logic, but about resource management.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Indexes&lt;/strong&gt; save CPU and I/O.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiters&lt;/strong&gt; save the server from being overwhelmed.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>springboot</category>
      <category>performance</category>
    </item>
    <item>
      <title>[Simple SNS Project] Step 1. User Registration &amp; Handling Forbidden Words with Spring Boot</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Mon, 29 Dec 2025 22:52:41 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-1-user-registration-handling-forbidden-words-with-spring-boot-5g4b</link>
      <guid>https://forem.com/jonghwayoo3_dev/simple-sns-project-step-1-user-registration-handling-forbidden-words-with-spring-boot-5g4b</guid>
      <description>&lt;p&gt;I have started a new side project called &lt;strong&gt;"Very Simple SNS"&lt;/strong&gt; to strengthen my full-stack development skills using &lt;strong&gt;Spring Boot&lt;/strong&gt; and &lt;strong&gt;React&lt;/strong&gt;. The goal is not just to write code, but to understand the "Why" behind every architectural decision, such as database indexing, traffic handling, and system design.&lt;/p&gt;

&lt;p&gt;In this first step, I focused on setting up the environment and implementing the &lt;strong&gt;User Registration&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Spring Boot 3.x, Spring Data JPA, MySQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React (Vite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration:&lt;/strong&gt; Git (Monorepo structure)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  1. The Core Logic: Forbidden Word Filter
&lt;/h3&gt;

&lt;p&gt;One of the key requirements for Step 1 was to &lt;strong&gt;prevent users from using specific keywords&lt;/strong&gt; (e.g., "admin", "operator") in their nicknames to avoid confusion.&lt;/p&gt;

&lt;p&gt;I faced a design decision: &lt;strong&gt;Where should I put this validation logic?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inside the Entity?&lt;/strong&gt; (User.java)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inside the Service Layer?&lt;/strong&gt; (UserService.java)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;My Decision: The Service Layer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Entities should focus on their own internal integrity (e.g., length check, null check). Checking against a list of "forbidden words" is a business policy that might change over time or require external data sources (like a DB table or Redis). Therefore, it belongs in the Service layer to keep the Entity pure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// In a real-world scenario, this would be fetched from a DB or Cache&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;FORBIDDEN_WORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"operator"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="nf"&gt;signup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SignupRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validation Logic&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containsForbiddenWord&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNickname&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This nickname is not allowed."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// ... saving user logic&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;containsForbiddenWord&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;nickname&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;FORBIDDEN_WORDS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;anyMatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;nickname:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. What I Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo Setup:&lt;/strong&gt; Managing both Backend and Frontend in a single Git repository makes it easier to track changes for a specific feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; Distinguishing between Domain logic (Entity) and Business logic (Service) is crucial for maintainability.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Stop Asking for Data! Master the "Tell, Don't Ask" Principle</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Fri, 19 Dec 2025 00:57:48 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/stop-asking-for-data-master-the-tell-dont-ask-principle-373g</link>
      <guid>https://forem.com/jonghwayoo3_dev/stop-asking-for-data-master-the-tell-dont-ask-principle-373g</guid>
      <description>&lt;p&gt;One of the most common mistakes in Object-Oriented Programming (OOP) is treating objects like simple data structures. We often pull data out of an object, perform some logic, and then put the result back in.&lt;br&gt;
This violates the "Tell, Don't Ask" (TDA) principle.&lt;/p&gt;

&lt;p&gt;In this post, I will explain what TDA is and how it improves your code using a simple Shop and Account example.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is "Tell, Don't Ask"?
&lt;/h2&gt;

&lt;p&gt;The principle is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't Ask:&lt;/strong&gt; Don't ask an object for its data to perform logic on it yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tell:&lt;/strong&gt; Tell the object what to do, and let it handle its own state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you follow TDA, you improve Encapsulation and Cohesion.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem: Procedural Code in OOP ❌
&lt;/h2&gt;

&lt;p&gt;Let's look at a common anti-pattern. Here, the Shop class is managing the logic that belongs to the Account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mateandgit.programming.chapter1_2.problem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Shop&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sell&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ❌ BAD: Asking for data (getMoney)&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;mileage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMoney&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// ❌ BAD: The Shop is deciding if the account has enough money&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mileage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ❌ BAD: Modifying the account's state externally&lt;/span&gt;
            &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMoney&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mileage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" purchased."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Insufficient balance."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why is this bad?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Violation of Encapsulation:&lt;/strong&gt; The Shop knows too much about how the Account works (it knows it has "money" and knows how to calculate the balance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic Duplication:&lt;/strong&gt; If another class (e.g., SubscriptionService) needs to deduct money, you have to duplicate this &lt;code&gt;if (balance &amp;gt;= price)&lt;/code&gt; logic there too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard to Maintain:&lt;/strong&gt; If the validation rule changes (e.g., "minimum balance must remain 100"), you have to fix it in every class that accesses Account.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Solution: Tell the Object What to Do ✅
&lt;/h2&gt;

&lt;p&gt;Refactoring this to follow TDA means moving the logic into the Account class. The Shop should just command the Account to pay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mateandgit.programming.chapter1_2.solution&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Shop&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sell&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ✅ GOOD: Just asking a question (canAfford)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;canAfford&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

            &lt;span class="c1"&gt;// ✅ GOOD: Telling the object to do something (withdraw)&lt;/span&gt;
            &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" purchased."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Insufficient balance."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Inside the Account class, you would have methods like canAfford() and withdraw() that encapsulate the logic.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is this better?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better Encapsulation:&lt;/strong&gt; The Shop doesn't know how the withdrawal happens. It just sends a message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Logic:&lt;/strong&gt; The validation rules (canAfford) are centralized in the Account class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability:&lt;/strong&gt; The code reads like a sentence: "If account can afford, withdraw."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Object-Oriented Programming is about sending messages, not manipulating data.&lt;br&gt;
If you find yourself writing &lt;code&gt;getSomething()&lt;/code&gt;, changing it, and calling &lt;code&gt;setSomething()&lt;/code&gt;, stop and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Should this logic belong to the object I'm getting data from?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apply the Tell, Don't Ask principle to keep your objects independent and your logic clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't:&lt;/strong&gt; get data -&amp;gt; calculate -&amp;gt; set data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do:&lt;/strong&gt; Create a method in the object and tell it to perform the action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; High cohesion, low coupling, and better encapsulation.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>design</category>
      <category>programming</category>
    </item>
    <item>
      <title>My Service is Slow! Where Do I Start? (Performance Tuning Basics)</title>
      <dc:creator>JongHwa</dc:creator>
      <pubDate>Thu, 18 Dec 2025 22:05:50 +0000</pubDate>
      <link>https://forem.com/jonghwayoo3_dev/my-service-is-slow-where-do-i-start-performance-tuning-basics-1h1m</link>
      <guid>https://forem.com/jonghwayoo3_dev/my-service-is-slow-where-do-i-start-performance-tuning-basics-1h1m</guid>
      <description>&lt;p&gt;We've all been there. You deploy your application, and everything seems fine until traffic spikes. Suddenly, the API response takes 5 seconds, and users are complaining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Where do I even start looking?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, I will summarize the core concepts of server performance tuning, focusing on metrics, scaling, and the critical database connection pool.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Throughput vs. Latency (Response Time)
&lt;/h2&gt;

&lt;p&gt;Before fixing anything, we need to know what we are measuring. There are two main metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency (Response Time):&lt;/strong&gt; How long does it take to process &lt;strong&gt;one&lt;/strong&gt; request? (e.g., "The API took 200ms to return data.")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput:&lt;/strong&gt; How many requests can the system handle &lt;strong&gt;per second&lt;/strong&gt;? (e.g., "The server handles 1,000 TPS/RPS.")&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Key Insight:&lt;/strong&gt; Improving Latency usually improves Throughput, but not always. You must decide which metric is more important for your specific service goal.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Finding the Bottleneck
&lt;/h2&gt;

&lt;p&gt;A system is only as fast as its slowest component. This is called the &lt;strong&gt;Bottleneck&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common bottlenecks include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU:&lt;/strong&gt; Heavy calculations or infinite loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; Memory leaks or frequent Garbage Collection (GC).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I/O (Input/Output):&lt;/strong&gt; Slow database queries, network calls to external APIs, or disk read/write.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Bad Practice:&lt;/strong&gt; Guessing. "Maybe we need a bigger server?"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Best Practice:&lt;/strong&gt; Monitoring. Use tools (like APM, Prometheus, or Grafana) to identify exactly &lt;strong&gt;where&lt;/strong&gt; the request is getting stuck.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Scaling: Vertical vs. Horizontal
&lt;/h2&gt;

&lt;p&gt;Once you find the bottleneck, you generally have two ways to expand capacity:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Vertical Scaling (Scale Up)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is it?&lt;/strong&gt; Upgrading the server hardware (e.g., increasing RAM from 8GB to 32GB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Simple configuration. No code changes required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Expensive and has a physical limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Horizontal Scaling (Scale Out)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is it?&lt;/strong&gt; Adding &lt;strong&gt;more&lt;/strong&gt; servers (nodes) to share the load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Infinite scalability. High availability (if one server dies, others survive).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Complexity. You need a Load Balancer and need to handle distributed data consistency.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The Silent Killer: DB Connection Pool
&lt;/h2&gt;

&lt;p&gt;In many web applications (especially Spring Boot), the bottleneck is often the &lt;strong&gt;Database Connection Pool&lt;/strong&gt;. Establishing a connection to a database is expensive. Therefore, we use a "Pool" to reuse connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Connection Pool Size&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too Small:&lt;/strong&gt; Threads have to wait in a queue for a connection to open. (&lt;strong&gt;High Latency&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too Large:&lt;/strong&gt; Too many connections overwhelm the CPU with "Context Switching." (&lt;strong&gt;Low Throughput&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Connection Wait Time (Timeout)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If the pool is full, how long should a request wait before throwing an error? If set too long, the user waits 30 seconds just to see an error. It is better to &lt;strong&gt;fail fast&lt;/strong&gt; so the system can recover.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Server Caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The fastest query is the one you never make.&lt;/strong&gt; If your service reads data that doesn't change often, use &lt;strong&gt;Caching&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Cache (Ehcache, Caffeine):&lt;/strong&gt; Extremely fast, stored in the application's RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Cache (Redis):&lt;/strong&gt; Shared across multiple servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By caching frequently accessed data, you can drastically reduce the load on your database and improve Latency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Performance tuning isn't magic. It starts with understanding the difference between &lt;strong&gt;Throughput&lt;/strong&gt; and &lt;strong&gt;Latency&lt;/strong&gt;, identifying the &lt;strong&gt;Bottleneck&lt;/strong&gt;, and then optimizing resources like the &lt;strong&gt;DB Connection Pool&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Checklist for slow services:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Are my queries slow?&lt;/strong&gt; (Check DB Indexes)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Is my Connection Pool size appropriate?&lt;/strong&gt; (Check HikariCP settings)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Can I cache this data?&lt;/strong&gt; (Use Local or Global Cache)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Do I need to Scale Up or Scale Out?&lt;/strong&gt; (Vertical vs Horizontal Scaling)&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>beginners</category>
      <category>performance</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
