<?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: David J Song</title>
    <description>The latest articles on Forem by David J Song (@jaino_song).</description>
    <link>https://forem.com/jaino_song</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%2F3270144%2F605644e5-06d4-4235-a4cb-c3875c894fc4.jpg</url>
      <title>Forem: David J Song</title>
      <link>https://forem.com/jaino_song</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jaino_song"/>
    <language>en</language>
    <item>
      <title>Understanding "use client" in Next.js: What It Really Affects</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Mon, 03 Nov 2025 16:58:27 +0000</pubDate>
      <link>https://forem.com/jaino_song/understanding-use-client-in-nextjs-what-it-really-affects-2hhk</link>
      <guid>https://forem.com/jaino_song/understanding-use-client-in-nextjs-what-it-really-affects-2hhk</guid>
      <description>&lt;p&gt;Many developers misunderstand how "use client" works in Next.js App Router (React Server Components).&lt;/p&gt;

&lt;p&gt;It doesn’t make everything below it client-rendered. It only changes the behavior of the file itself and what it directly imports.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Common Misconception
&lt;/h2&gt;

&lt;p&gt;You might have heard:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If you use &lt;code&gt;"use client"&lt;/code&gt;, all your child components become client components.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s &lt;strong&gt;false&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Real Rule
&lt;/h2&gt;

&lt;p&gt;When you add &lt;code&gt;"use client"&lt;/code&gt; at the top of a file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ That file becomes a &lt;strong&gt;client component&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;✅ Anything it &lt;strong&gt;imports&lt;/strong&gt; becomes a client component too.
&lt;/li&gt;
&lt;li&gt;🚫 But components passed in as &lt;strong&gt;&lt;code&gt;children&lt;/code&gt; props&lt;/strong&gt; are &lt;em&gt;not affected&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Children remain whatever they were defined as (server or client).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Example: Safe Provider Pattern
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/providers.tsx&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;This Providers component is a &lt;strong&gt;client component&lt;/strong&gt;, because it needs React context, hooks, and browser logic.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;{children}&lt;/code&gt; are not converted to client components — they stay server-rendered.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. How It Works
&lt;/h2&gt;

&lt;p&gt;When a server component (like your layout) calls a client component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;layout.tsx (server)
 └── Providers.tsx ("use client")
       └── children (server or client)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React Server Components serialize the child tree as HTML and &lt;strong&gt;pass it&lt;/strong&gt; into the client boundary.&lt;/p&gt;

&lt;p&gt;The Providers component doesn’t recompile or import the children — it just receives them as already-rendered markup.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Why This Matters
&lt;/h2&gt;

&lt;p&gt;Keeping most of your UI as server components gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller JS bundles&lt;/li&gt;
&lt;li&gt;Faster page load&lt;/li&gt;
&lt;li&gt;Better SEO and streaming performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use "use client" only when you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hooks like useState, useEffect&lt;/li&gt;
&lt;li&gt;Event handlers (onClick, onChange)&lt;/li&gt;
&lt;li&gt;Access to window or browser APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now I am no more "use client"-phobic. 😂&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>NextAuth + Supabase: Handling Authentication in a Modern Full-Stack App</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Tue, 09 Sep 2025 16:47:39 +0000</pubDate>
      <link>https://forem.com/jaino_song/nextauth-supabase-handling-authentication-in-a-modern-full-stack-app-26k8</link>
      <guid>https://forem.com/jaino_song/nextauth-supabase-handling-authentication-in-a-modern-full-stack-app-26k8</guid>
      <description>&lt;p&gt;When I built ONNS (옷늘날씨), I wanted sign-in to feel effortless. Nobody wants to manage yet another password, and I definitely did not want to spend weeks reinventing authentication. That is why I combined &lt;strong&gt;NextAuth&lt;/strong&gt; with &lt;strong&gt;Supabase&lt;/strong&gt;. Together, they allowed me to focus on building the fun parts of the app while still maintaining solid security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Combo?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NextAuth&lt;/strong&gt; is the de facto standard for authentication in Next.js apps. It supports OAuth providers, session management, JWTs, and integrates seamlessly with App Router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; provides a complete Postgres backend with real-time and storage. Since user accounts and posts live in Supabase, tying authentication into the same ecosystem just made sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining them, I got the best of both worlds: flexible provider-based login through NextAuth, and user records managed in Supabase.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A user clicks &lt;strong&gt;Sign in with Google&lt;/strong&gt; (or another provider).&lt;/li&gt;
&lt;li&gt;NextAuth handles the OAuth handshake.&lt;/li&gt;
&lt;li&gt;On success, NextAuth calls a custom adapter that syncs the user with Supabase.&lt;/li&gt;
&lt;li&gt;The session is then available across the app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, authentication feels native, and all user-related data stays consistent in the database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NextAuth config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/auth/[...nextauth]/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NextAuth&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GoogleProvider&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/providers/google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SupabaseAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@next-auth/supabase-adapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;GoogleProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&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;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SupabaseAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPABASE_SERVICE_ROLE_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXTAUTH_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NextAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Protecting a route:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/feed/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getServerSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authOptions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../api/auth/[...nextauth]/route&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FeedPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getServerSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authOptions&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Please sign in to view the feed.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;! Here is your personalized feed.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Benefits I Saw
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: I did not need to roll my own login system. NextAuth handled the heavy lifting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: Supabase stored user records in Postgres, and I could enforce RLS (row-level security) policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: Adding more providers (GitHub, Twitter) is almost copy-paste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: Since posts, comments, and profiles already lived in Supabase, user identities aligned perfectly with the data model.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Lesson
&lt;/h2&gt;

&lt;p&gt;NextAuth and Supabase both support authentication separately. You could use Supabase Auth alone. However, by using NextAuth, I achieved tighter integration with Next.js features, such as middleware and server actions. The adapter pattern meant I did not have to pick one or the other—I could blend them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Authentication is rarely the most exciting part of a project, but it is the part that can ruin the experience if it goes wrong. By leveraging NextAuth and Supabase, ONNS achieved smooth user authentication and a maintainable setup for me.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;How do you usually handle authentication in your Next.js projects?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextauth</category>
      <category>nextjs</category>
      <category>supabase</category>
    </item>
    <item>
      <title>Why I Chose Clean Architecture for a Social Weather App</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Tue, 02 Sep 2025 12:32:02 +0000</pubDate>
      <link>https://forem.com/jaino_song/why-i-chose-clean-architecture-for-a-social-weather-app-1pcj</link>
      <guid>https://forem.com/jaino_song/why-i-chose-clean-architecture-for-a-social-weather-app-1pcj</guid>
      <description>&lt;p&gt;When I started building &lt;strong&gt;ONNS (옷늘날씨)&lt;/strong&gt;, a weather-driven outfit community app, I had one recurring thought: &lt;em&gt;“How do I stop my codebase from becoming a tangled ball of JavaScript spaghetti when I just want to tell people what to wear?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is when I decided to lean into &lt;strong&gt;Clean Architecture&lt;/strong&gt;. Not because it is trendy, but because I wanted to keep things maintainable when features inevitably pile up such as comments, filters, login, and realtime updates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Clean Architecture?
&lt;/h2&gt;

&lt;p&gt;In short, it is all about &lt;strong&gt;boundaries&lt;/strong&gt;. Just like you would not wear flip-flops in a snowstorm, you do not want your React components directly poking at your database.&lt;/p&gt;

&lt;p&gt;Clean Architecture encourages you to separate your project into layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain&lt;/strong&gt;: The pure business rules. For ONNS, that means things like &lt;code&gt;OutfitPost&lt;/code&gt;, &lt;code&gt;WeatherTag&lt;/code&gt;, and &lt;code&gt;Comment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application&lt;/strong&gt;: Use cases that orchestrate how features behave. For example, “create a new post tagged with today’s weather.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;: The part that talks to Supabase, Prisma, or any third-party service. The dirty work, kept isolated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI (App)&lt;/strong&gt;: Next.js pages, components, and hooks.&lt;/p&gt;

&lt;p&gt;This way, if tomorrow I swap Supabase for Firebase or even raw SQL queries, the core logic does not care.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example From the Repo
&lt;/h2&gt;

&lt;p&gt;Here is a simplified example. Let us say I want to create a new post tagged with today’s weather.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain entity:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// backend/domain/entities/outfitPost.entity.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;feelsLike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;season&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SPRING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUMMER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FALL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WINTER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&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;p&gt;&lt;strong&gt;Application use case:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// backend/application/usecases/createPost.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OutfitPostRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../repositories/OutfitPostRepository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../domain/entities/outfitPost.entity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreatePost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OutfitPostRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OutfitPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createdAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OutfitPost&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&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;p&gt;&lt;strong&gt;Infrastructure (Supabase adapter):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// backend/infrastructure/repositories/SupabaseOutfitPostRepository.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../domain/entities/outfitPost.entity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OutfitPostRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../application/repositories/OutfitPostRepository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../lib/supabase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseOutfitPostRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OutfitPostRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OutfitPost&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;single&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="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;OutfitPost&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;p&gt;&lt;strong&gt;UI layer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/components/NewPostForm.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CreatePost&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/backend/application/usecases/createPost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SupabaseOutfitPostRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/backend/infrastructure/repositories/SupabaseOutfitPostRepository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NewPostForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repo&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;SupabaseOutfitPostRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usecase&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;CreatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;usecase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;feelsLike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;season&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WINTER&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;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* input fields for image, tags, etc. */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Post&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;Notice how the &lt;code&gt;NewPostForm&lt;/code&gt; does not know or care whether the data is going to Supabase, PostgreSQL, or even a JSON file on my desktop. That is the power of boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Bought Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: New features such as filtering outfits by season meant adding new use cases, not rewriting everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: It could replace Supabase with another backend in theory without touching the UI or domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clarity&lt;/strong&gt;: When I opened the repo after two weeks, I did not feel like a stranger in my own house.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Little Wit to End
&lt;/h2&gt;

&lt;p&gt;Just like you would not layer a winter coat under your t-shirt, you should not jam your database calls inside your React components. Clean Architecture keeps your code dressed appropriately for any season.&lt;/p&gt;

&lt;p&gt;And in ONNS, that is literally the point.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have thoughts on Clean Architecture, or want to roast my choice of Supabase? Drop a comment!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>ORMs and Prisma for Web Devs</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Tue, 22 Jul 2025 08:46:03 +0000</pubDate>
      <link>https://forem.com/jaino_song/orms-and-prisma-for-web-devs-2ejb</link>
      <guid>https://forem.com/jaino_song/orms-and-prisma-for-web-devs-2ejb</guid>
      <description>&lt;h2&gt;
  
  
  ORMs and Prisma
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Object-Relational Mapping (ORM) is a programming technique that creates a bridge between object-oriented code and relational databases by mapping classes to tables, allowing developers to work with database records as native objects rather than writing raw SQL queries directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For web developers, Prisma is one of the choices out there for ORM. Prisma is an open-source, next-generation ORM for Node.js and TypeScript that uses a declarative schema file to generate a fully type-safe client, manage schema migrations, and provide a visual data browser via Prisma Studio.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is ORM
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Object-Relational Mapping (ORM) automates the transfer of data between an application's in-memory objects and a relational database by using metadata to describe how classes map to tables and properties map to columns. With ORM, developers manipulate data through familiar object methods, rather than embedding SQL strings in code, effectively creating a "virtual object database" within the programming language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducing Prisma
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prisma is a modern ORM designed specifically for TypeScript and JavaScript backends. It centers around a single &lt;code&gt;schema.prisma&lt;/code&gt; file that serves as the source of truth for both your database schema and application models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prisma's Core Components
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Prisma Client: An auto-generated, fully type-safe query builder that provides intuitive CRUD methods and rich filtering APIs&lt;br&gt;
Prisma Migrate: A declarative migration tool that infers schema changes, generates SQL migration scripts, and applies them safely in development and production.&lt;br&gt;
Prisma Studio: A GUI for visually browsing and editing your database records locally, streamlining data inspection without writing SQL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Type Safety of Prisma
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prisma generates TypeScript types based on your schema, ensuring that all database operations are checked at compile time and reducing runtime errors. Its strong emphasis on type safety and end-to-end type inference helps catch mistakes early, enhancing both code quality and developer productivity.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>prisma</category>
      <category>orm</category>
    </item>
    <item>
      <title>Why Next.js?</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Tue, 24 Jun 2025 08:36:49 +0000</pubDate>
      <link>https://forem.com/jaino_song/why-nextjs-2n4</link>
      <guid>https://forem.com/jaino_song/why-nextjs-2n4</guid>
      <description>&lt;h2&gt;
  
  
  How Next.js Got Big
&lt;/h2&gt;

&lt;p&gt;Next.js has rapidly become the go-to framework for building high-performance, SEO-friendly, React-based applications since its launch in 2016. Let me explain why and how it got so big in the web development scene now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexible Rendering Strategies: SSR, SSG, ISR
&lt;/h2&gt;

&lt;p&gt;Next.js stands out by offering multiple rendering modes in one framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-Side-Rendering (SSR): Fresh HTML is generated on each request, ideal for dynamic content or personalized pages.&lt;/li&gt;
&lt;li&gt;Static Site Generation (SSG): Pages are pre-built at build time, perfect for blogs or documentation sites, and served fast via CDNs.&lt;/li&gt;
&lt;li&gt;Incremental Static Regeneration (ISR): Combines the best of SSR and SSG. Static pages refresh in the background without complete rebuilds.
These capabilities give developers control over performance, scalability, and content freshness per page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SEO &amp;amp; Performance Gains
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SEO-ready: SSR/SSG ensures search engines crawl full-rendered HTML, improving indexability.&lt;/li&gt;
&lt;li&gt;Core Web Vitals: Features like image optimization, code-splitting, and Turbopack boost metrics like LCP, FCP, TTI.&lt;/li&gt;
&lt;li&gt;In real-world cases, many teams meet Google's Core Web Vitals thresholds on first deployment (89%) with Next.js VS 52% with other frameworks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Built-In Image Optimization
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;image&lt;/code&gt; component, Next.js delivers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On-demand, responsive image&lt;/li&gt;
&lt;li&gt;Lazy Loading&lt;/li&gt;
&lt;li&gt;Modern formats like WebP&lt;/li&gt;
&lt;li&gt;CLS prevention&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Developer Experience &amp;amp; Productivity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Zero-config setup: File-based routing, type support, hot module reloading work out of the box.&lt;/li&gt;
&lt;li&gt;Full-stack in a single repo: API routes, SSR/SSG, and middleware all coexist. No need for separate backend codebases.&lt;/li&gt;
&lt;li&gt;Turbopack: Next-gen build tool (Rust-based) that dramatically accelerates builds, faster than Webpack and Vite.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Go For It
&lt;/h2&gt;

&lt;p&gt;Next.js isn't just another React boilerplate. It is a full-featured, versatile application framework. Whether you are building a blog, e-commerce platform, marketing microsite, or full-stack application, Next.js delivers a powerful foundation, so you can focus on delivering value instead of wiring up infrastructure.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>The Battle Between Interface and Type in TypeScript</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Mon, 23 Jun 2025 08:16:30 +0000</pubDate>
      <link>https://forem.com/jaino_song/the-battle-between-interface-and-type-in-typescript-noo</link>
      <guid>https://forem.com/jaino_song/the-battle-between-interface-and-type-in-typescript-noo</guid>
      <description>&lt;h2&gt;
  
  
  Where the Fight Begins
&lt;/h2&gt;

&lt;p&gt;TypeScript's &lt;code&gt;interface&lt;/code&gt; and &lt;code&gt;type&lt;/code&gt; aliases overlap for defining object shapes. Their key distinction is that interfaces support declaration merging while type aliases offer richer expressiveness for unions, intersections, mapped, and conditional types. &lt;/p&gt;

&lt;p&gt;In modern practice, many teams default to &lt;code&gt;type&lt;/code&gt; for its flexibility, using &lt;code&gt;interface&lt;/code&gt; only when they need merging or classic OOP-style contracts. Performance between the two is now virtually identical, so clarity and project conventions determine the choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Differences
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Declaration Merging: Only interfaces can be reopened and extended across multiple declarations, while types cannot.&lt;/li&gt;
&lt;li&gt;Expressiveness: Types can define primitives, unions, intersections, mapped types, and more, while interfaces are limited to object/function signatures.&lt;/li&gt;
&lt;li&gt;Compiler Impact: There is no meaningful compile-time speed difference in current TypeScript versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Industry Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Default to types: Many codebases adopt &lt;code&gt;type&lt;/code&gt; as the standard for general type definitions and advanced type manipulations.&lt;/li&gt;
&lt;li&gt;Reserve interfaces: Use when you specifically need &lt;code&gt;extends&lt;/code&gt;, &lt;code&gt;implements&lt;/code&gt;, or declaration merging for library augmentation or OOP contracts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick one by convention: Enforce your team's preferences via lint rules to ensure consistency.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;type&lt;/code&gt; for flexibility: Leverage it for complex scenarios where unions, conditional types, or utility types are needed.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;interface&lt;/code&gt; for merging: Opt for it when you need to augment or merge declarations, or when modeling class-like contracts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Now You Know Which to Use
&lt;/h2&gt;

&lt;p&gt;There is no absolute better choice. Choosing between &lt;code&gt;interface&lt;/code&gt; and &lt;code&gt;type&lt;/code&gt; should be guided by your project's style and needs. Choose &lt;code&gt;type&lt;/code&gt; for its versatility, or &lt;code&gt;interface&lt;/code&gt; when you require its unique merging capabilities or OOP semantics.&lt;/p&gt;

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>Understanding Multitasking, Processes, and Threads</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Fri, 20 Jun 2025 08:32:26 +0000</pubDate>
      <link>https://forem.com/jaino_song/understanding-multitasking-processes-and-threads-4jgi</link>
      <guid>https://forem.com/jaino_song/understanding-multitasking-processes-and-threads-4jgi</guid>
      <description>&lt;h2&gt;
  
  
  Multitasking is Not as Magical as You Might Think
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Modern computers stream music, download files, and browse the internet simultaneously with a single CPU on the board. This seemingly magical ability is called multitasking, a cornerstone of modern operating systems. It may make computers seem as if they are magical beings, but it is not so different from how we humans multitask.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Magic Behind the Scenes: Time-Sharing and Context Switching
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;With a single CPU, true simultaneous execution of multiple tasks is not possible. Instead, the operating system employs a clever technique called time-sharing, which lets the CPU switch between tasks rapidly. This happens so instantly that tasks are running concurrently.&lt;/li&gt;
&lt;li&gt;When switching between tasks, the operating system saves the current progress and state of the active task and moves on to the next task by loading the saved progress and state of the next task. This is called context switching. This allows computers to pick up exactly where they left off on each task, ensuring a smooth transition and continuous progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Foundation of Multitasking: Processes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To define what these 'tasks' are, we must discuss processes and threads. When a program runs, it is a process. When an application launches, the OS creates a process and allocates the necessary resources.&lt;/li&gt;
&lt;li&gt;A key characteristic of a process is its isolation. Each process is granted its own independent memory space, a dedicated share of CPU time, and other system resources. This isolation is crucial for stability because it won't shut down the entire system in case of a process crash. The OS acts as a protector that keeps processes from accessing others' memory, creating a secure and reliable multitasking environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More and More: Threads
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To further enhance efficiency, a process can be broken down into smaller units of execution called threads. If a process is a program in action, a thread is a distinct execution path within that program. A single process can contain multiple threads, all working in parallel to accomplish different parts of the main task.&lt;/li&gt;
&lt;li&gt;Unlike processes, which are isolated from one another, threads within the same process share the resources allocated to that process. This shared environment allows for efficient communication and data exchange between threads. However, each thread maintains its unique stack, registers, and program counter to prevent collision.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>multitasking</category>
      <category>programming</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Frontend Architectures</title>
      <dc:creator>David J Song</dc:creator>
      <pubDate>Wed, 18 Jun 2025 07:04:10 +0000</pubDate>
      <link>https://forem.com/jaino_song/frontend-architectures-2lli</link>
      <guid>https://forem.com/jaino_song/frontend-architectures-2lli</guid>
      <description>&lt;h2&gt;
  
  
  Server-Side-Rendering
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Instead of sending a bare HTML shell and letting JavaScript build the page in the browser, server-side rendering (SSR) uses JavaScript on the server to generate fully finished HTML (including actual content), then sends that complete HTML to the client. The browser can display it immediately without waiting for client-side rendering.&lt;/li&gt;
&lt;li&gt;Server-side rendering provides better visibility to search engine bots, resulting in good SEO optimization, whereas Client-Side Rendering shows weakness.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MVC Architecture
&lt;/h2&gt;

&lt;h4&gt;
  
  
  M(odel) V(iew) C(ontroller)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;MVC is a software architecture design that divides the UI logic into three layers. This provides a better maintenance and test-driven development environment.

&lt;ul&gt;
&lt;li&gt;Model: Data and business logic&lt;/li&gt;
&lt;li&gt;View: UI&lt;/li&gt;
&lt;li&gt;Controller: Applying User Inputs to Model and View&lt;/li&gt;
&lt;li&gt;Data flow: Often bidirectional&lt;/li&gt;
&lt;li&gt;Environment: Works on both client and server, supported by frameworks like Rails and AngularJS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Flux
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Designed for unidirectional data flow in React apps.

&lt;ul&gt;
&lt;li&gt;Action: User or system event&lt;/li&gt;
&lt;li&gt;Dispatcher: Central hub to send actions&lt;/li&gt;
&lt;li&gt;Store: Holds state and logic&lt;/li&gt;
&lt;li&gt;View: React components subscribe to stores&lt;/li&gt;
&lt;li&gt;Data flow: Strictly unidirectional&lt;/li&gt;
&lt;li&gt;Store: Supports multiple stores, logic resides in stores, not controllers&lt;/li&gt;
&lt;li&gt;Adaptations: Inspired libraries like Redux, offering a single store and centralized state management.&lt;/li&gt;
&lt;li&gt;Environment: Primarily for complex client-side apps (React, Vue, Angular) and not typically used on server-side&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>webdev</category>
      <category>ssr</category>
    </item>
  </channel>
</rss>
