<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>My Blog</title>
        <link>https://krblog-xon.pages.dev</link>
        <description>새로운 블로그에 오신 것을 환영합니다. (설정에서 이 문구를 수정하세요.)</description>
        <language>ko</language>
        <atom:link href="https://krblog-xon.pages.dev/rss.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Sat, 30 May 2026 16:12:46 GMT</lastBuildDate>
        <item>
            <title><![CDATA[test]]></title>
            <link>https://krblog-xon.pages.dev/html-test/html-test</link>
            <guid isPermaLink="true">https://krblog-xon.pages.dev/html-test/html-test</guid>
            <pubDate>Sat, 30 May 2026 23:16:19 GMT</pubDate>
            <description><![CDATA[html 혼용 가능한지 테스트 하는 포스트]]></description>
            <content:encoded><![CDATA[<p>잘 되냐?</p>]]></content:encoded>
            <category>테스트</category>
        </item>
        <item>
            <title><![CDATA[커스텀 JS 예제들]]></title>
            <link>https://krblog-xon.pages.dev/sample/custom-js-sample</link>
            <guid isPermaLink="true">https://krblog-xon.pages.dev/sample/custom-js-sample</guid>
            <pubDate>Sat, 30 May 2026 22:49:34 GMT</pubDate>
            <description><![CDATA[커스텀 js 예제들]]></description>
            <content:encoded><![CDATA[<h1>🎨 커스텀 JS 동적 배경 라이브러리 (최종 최적화 버전)</h1>
<p>이 버전은 <strong>Canvas ID 불일치 해결</strong> 및 <strong>밝은 테마에서의 가시성 확보</strong>가 완료된 최종 버전입니다.</p>
<hr>
<h2>1. ✨ Twinkling Starfield (반짝이는 밤하늘)</h2>
<p><em>배경을 어두운 남색으로 자동 설정하여 별이 잘 보이게 합니다.</em></p>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    let stars = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 150; i++) {
        stars.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            size: Math.random() * 1.5,
            opacity: Math.random(),
            speed: Math.random() * 0.01 + 0.005
        });
    }

    function animate() {
        // 어두운 배경색 강제 지정 (별 가시성 확보)
        ctx.fillStyle = &#39;#0f172a&#39;; 
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        stars.forEach(s =&gt; {
            s.opacity += s.speed;
            if (s.opacity &gt; 1 || s.opacity &lt; 0) s.speed *= -1;
            ctx.fillStyle = `rgba(255, 255, 255, ${Math.max(0, s.opacity)})`;
            ctx.beginPath();
            ctx.arc(s.x, s.y, s.size, 0, Math.PI * 2);
            ctx.fill();
        });
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>2. 🌊 Smooth Wave Motion (부드러운 물결)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    let tick = 0;

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    function drawWave(color, speed, height, offset) {
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.moveTo(0, canvas.height);
        for (let x = 0; x &lt;= canvas.width; x++) {
            const y = Math.sin(x * 0.01 + tick * speed + offset) * height + (canvas.height / 2);
            ctx.lineTo(x, y);
        }
        ctx.lineTo(canvas.width, canvas.height);
        ctx.fill();
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        tick++;
        drawWave(&#39;rgba(59, 130, 246, 0.15)&#39;, 0.02, 20, 0);
        drawWave(&#39;rgba(96, 165, 250, 0.1)&#39;, 0.015, 30, Math.PI);
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>3. 🕸️ Particle Network (지적인 네트워크)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const particles = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 50; i++) {
        particles.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            vx: (Math.random() - 0.5) * 0.5,
            vy: (Math.random() - 0.5) * 0.5
        });
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        particles.forEach((p, i) =&gt; {
            p.x += p.vx; p.y += p.vy;
            if(p.x &lt; 0 || p.x &gt; canvas.width) p.vx *= -1;
            if(p.y &lt; 0 || p.y &gt; canvas.height) p.vy *= -1;
            
            ctx.fillStyle = &#39;rgba(148, 163, 184, 0.4)&#39;;
            ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, Math.PI*2); ctx.fill();

            for(let j = i + 1; j &lt; particles.length; j++) {
                const p2 = particles[j];
                const dist = Math.hypot(p.x - p2.x, p.y - p2.y);
                if(dist &lt; 100) {
                    ctx.strokeStyle = `rgba(148, 163, 184, ${1 - dist/100})`;
                    ctx.lineWidth = 0.5;
                    ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(p2.x, p2.y); ctx.stroke();
                }
            }
        });
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>4. 🧬 Matrix Rain (디지털 비)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const chars = &quot;01&quot;.split(&quot;&quot;);
    const fontSize = 14;
    let columns;
    let drops = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        columns = Math.floor(canvas.width / fontSize);
        drops = Array.from({length: columns}, () =&gt; Math.random() * -50);
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    function animate() {
        ctx.fillStyle = &quot;rgba(0, 0, 0, 0.1)&quot;;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = &quot;#0F0&quot;;
        ctx.font = fontSize + &quot;px monospace&quot;;

        for (let i = 0; i &lt; drops.length; i++) {
            const text = chars[Math.floor(Math.random() * chars.length)];
            ctx.fillText(text, i * fontSize, drops[i] * fontSize);
            if (drops[i] * fontSize &gt; canvas.height &amp;&amp; Math.random() &gt; 0.975) drops[i] = 0;
            drops[i]++;
        }
        setTimeout(() =&gt; requestAnimationFrame(animate), 33);
    }
    animate();
})();
</code></pre>
<hr>
<h2>5. ❄️ Gentle Snowfall (겨울 눈)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const snowflakes = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 100; i++) {
        snowflakes.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            r: Math.random() * 3 + 1,
            d: Math.random() * 1,
            v: Math.random() * 0.8 + 0.4
        });
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = &quot;rgba(255, 255, 255, 0.8)&quot;;
        ctx.beginPath();
        snowflakes.forEach(f =&gt; {
            ctx.moveTo(f.x, f.y);
            ctx.arc(f.x, f.y, f.r, 0, Math.PI * 2, true);
            f.y += f.v;
            f.x += Math.sin(f.d += 0.01);
            if(f.y &gt; canvas.height) {
                f.y = -10;
                f.x = Math.random() * canvas.width;
            }
        });
        ctx.fill();
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>6. 🏮 Floating Bubbles (비눗방울)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const bubbles = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 30; i++) {
        bubbles.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            r: Math.random() * 15 + 5,
            s: Math.random() * 0.5 + 0.2
        });
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        bubbles.forEach(b =&gt; {
            b.y -= b.s;
            if(b.y &lt; -b.r) {
                b.y = canvas.height + b.r;
                b.x = Math.random() * canvas.width;
            }
            ctx.beginPath();
            ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
            ctx.strokeStyle = &quot;rgba(100, 100, 100, 0.2)&quot;;
            ctx.stroke();
        });
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>7. 🌈 Gradient Flow (유동적 그라데이션)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    let hue = Math.random() * 360;

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    function animate() {
        hue = (hue + 0.1) % 360;
        const grad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
        grad.addColorStop(0, `hsl(${hue}, 50%, 90%)`);
        grad.addColorStop(1, `hsl(${(hue + 60) % 360}, 50%, 80%)`);
        ctx.fillStyle = grad;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>8. 📐 Geometric Float (떠다니는 도형)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const shapes = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 15; i++) {
        shapes.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            size: Math.random() * 40 + 20,
            angle: Math.random() * Math.PI * 2,
            rot: Math.random() * 0.02,
            v: Math.random() * 0.5
        });
    }

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        shapes.forEach(s =&gt; {
            s.angle += s.rot;
            s.y -= s.v;
            if(s.y &lt; -s.size) {
                s.y = canvas.height + s.size;
                s.x = Math.random() * canvas.width;
            }
            
            ctx.save();
            ctx.translate(s.x, s.y);
            ctx.rotate(s.angle);
            ctx.strokeStyle = &quot;rgba(100, 100, 100, 0.1)&quot;;
            ctx.strokeRect(-s.size/2, -s.size/2, s.size, s.size);
            ctx.restore();
        });
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>9. ⚡ Aurora Borealis (오로라)</h2>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    let time = 0;

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        time += 0.005;
        for(let i = 0; i &lt; 3; i++) {
            const y = 30 + i * 15;
            const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
            grad.addColorStop(0, `rgba(52, 211, 153, ${0.3 - i*0.1})`);
            grad.addColorStop(1, &#39;transparent&#39;);
            ctx.fillStyle = grad;
            ctx.beginPath();
            ctx.moveTo(0, canvas.height);
            for(let x = 0; x &lt;= canvas.width; x += 10) {
                const noise = Math.sin(x * 0.005 + time + i) * 50;
                ctx.lineTo(x, y + noise);
            }
            ctx.lineTo(canvas.width, canvas.height);
            ctx.fill();
        }
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
<hr>
<h2>10. 🌑 Bokeh Background (컬러 보케 조명)</h2>
<p><em>색상을 입혀 밝은 배경에서도 잘 보이게 수정되었습니다.</em></p>
<pre><code class="language-javascript">(function() {
    const canvas = document.getElementById(&#39;bg-canvas&#39;);
    if (!canvas) return;
    const ctx = canvas.getContext(&#39;2d&#39;);
    const circles = [];

    function resize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    window.addEventListener(&#39;resize&#39;, resize);
    resize();

    for(let i = 0; i &lt; 25; i++) {
        circles.push({
            x: Math.random() * canvas.width,
            y: Math.random() * canvas.height,
            r: Math.random() * 100 + 50,
            v: Math.random() * 0.2 + 0.1,
            color: `hsla(${Math.random() * 360}, 70%, 70%, 0.3)`
        });
    }

    function animate() {
        // 배경을 살짝 어둡게 처리하여 보케 입자가 보이게 함
        ctx.fillStyle = &#39;rgba(15, 23, 42, 0.05)&#39;;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        
        ctx.globalCompositeOperation = &quot;lighter&quot;;
        circles.forEach(c =&gt; {
            c.y -= c.v;
            if(c.y &lt; -c.r) {
                c.y = canvas.height + c.r;
                c.x = Math.random() * canvas.width;
            }
            const g = ctx.createRadialGradient(c.x, c.y, 0, c.x, c.y, c.r);
            g.addColorStop(0, c.color);
            g.addColorStop(1, &quot;transparent&quot;);
            ctx.fillStyle = g;
            ctx.beginPath(); ctx.arc(c.x, c.y, c.r, 0, Math.PI*2); ctx.fill();
        });
        requestAnimationFrame(animate);
    }
    animate();
})();
</code></pre>
]]></content:encoded>
            <category>예시</category>
        </item>
        <item>
            <title><![CDATA[⚡ SveltekitBlog: Full-Edge Serverless Multi-Tenant Blog Engin 홍보용]]></title>
            <link>https://krblog-xon.pages.dev/general/SveltekitBlog</link>
            <guid isPermaLink="true">https://krblog-xon.pages.dev/general/SveltekitBlog</guid>
            <pubDate>Sat, 30 May 2026 22:46:30 GMT</pubDate>
            <description><![CDATA[스펙 요약 및 소개]]></description>
            <content:encoded><![CDATA[<h1>⚡ SveltekitBlog: Full-Edge Serverless Multi-Tenant Blog Engine</h1>
<p>SveltekitBlog는 최첨단 <strong>Svelte 5 Runes</strong>, <strong>Better-Auth</strong>, <strong>Cloudflare D1 &amp; Pages</strong>, <strong>Drizzle ORM</strong>을 결합한 차세대 <strong>Full-Edge Serverless 하이브리드 블로그 엔진</strong>입니다.</p>
<p>정적 빌드 방식에서 나타나기 쉬운 빌드/배포 지연을 최소화하고, 클라이언트 사이드 API 페칭 방식의 레이아웃 깜빡임(Flicker) 현상을 세련되게 해결했습니다. <strong>소스 코드의 재빌드 및 재배포 없이 관리자 설정 즉시 독자 화면에 0ms 급으로 실시간 동기화</strong>되는 최정상급 렌더링 퍼포먼스를 경험해 보세요.</p>
<hr>
<h2>🚀 Key Badges &amp; Tech Stack</h2>
<p align="center">
  <img src="https://img.shields.io/badge/Svelte-5.45%2B-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" alt="Svelte 5" />
  <img src="https://img.shields.io/badge/SvelteKit-2.49%2B-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" alt="SvelteKit 2" />
  <img src="https://img.shields.io/badge/Cloudflare_Pages-F38020?style=for-the-badge&logo=cloudflare&logoColor=white" alt="Cloudflare Pages" />
  <img src="https://img.shields.io/badge/Cloudflare_D1-F38020?style=for-the-badge&logo=sqlite&logoColor=white" alt="Cloudflare D1" />
  <img src="https://img.shields.io/badge/Drizzle_ORM-C5F74F?style=for-the-badge&logo=drizzle&logoColor=black" alt="Drizzle ORM" />
  <img src="https://img.shields.io/badge/Better_Auth-000000?style=for-the-badge&logo=auth0&logoColor=white" alt="Better Auth" />
</p><table>
<thead>
<tr>
<th align="left">스택 카테고리</th>
<th align="left">핵심 적용 기술 (Core Stack)</th>
<th align="left">공학적 기여 및 역할 (Role)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Client / Frontend</strong></td>
<td align="left"><strong>Svelte 5</strong></td>
<td align="left">Fine-grained Runes 컴파일 타임 반응성 기반 초경량 DOM 제어 &amp; 60FPS 테마 엔진</td>
</tr>
<tr>
<td align="left"><strong>Meta-Framework</strong></td>
<td align="left"><strong>SvelteKit</strong></td>
<td align="left"><strong>풀스택 아키텍처</strong>: SSR 하이브리드 로드, Server Load Functions, Form Actions 및 백엔드 REST API 라우팅 통합 제어</td>
</tr>
<tr>
<td align="left"><strong>Edge Platform</strong></td>
<td align="left"><strong>Cloudflare Pages (Workers)</strong></td>
<td align="left">전 세계 300개 Anycast 리전 분산 V8 Isolate 기반 Cold Start 0ms 서버리스 구동 환경</td>
</tr>
<tr>
<td align="left"><strong>Database</strong></td>
<td align="left"><strong>Cloudflare D1 + Drizzle ORM</strong></td>
<td align="left">에지 인접 전역 분산 관계형 SQLite 및 Drizzle 기반 정적 타입 안전 쿼리 매핑</td>
</tr>
<tr>
<td align="left"><strong>Auth</strong></td>
<td align="left"><strong>Better-Auth (Web Crypto PBKDF2)</strong></td>
<td align="left">NodeJS native 모듈 의존성 없는 Web Crypto API 하드웨어 가속 에지 인증 프로세서</td>
</tr>
<tr>
<td align="left"><strong>Storage</strong></td>
<td align="left"><strong>Multi-Storage Adapter Factory</strong></td>
<td align="left">KV(기본), R2, Supabase, ImageKit을 단일 인터페이스로 추상화한 동적 스위칭 스토리지 팩토리</td>
</tr>
</tbody></table>
<hr>
<h2>🏆 성능 지표 및 비즈니스 특장점 (Core Strengths &amp; Metrics)</h2>
<p>SveltekitBlog는 단순한 토이 프로젝트 수준이 아닌, 실제 상용 대형 블로그 및 퍼블리싱 매체 수준의 압도적인 엔지니어링 지표와 비즈니스적 강점을 자랑합니다.</p>
<h3>💰 1. 실질 운영 비용 0원 (Only Domain Fee)</h3>
<ul>
<li><strong>무제한 무료 인프라</strong>: Cloudflare Pages, Workers, D1 Database, KV 스토리지의 파격적인 무료 티어 범위 내에서 기동되므로, <strong>매월 청구되는 웹 인프라 호스팅 비용이 0원</strong>에 수렴합니다.</li>
<li><strong>지출 비용</strong>: 오직 본인이 소유할 개인 도메인(Domain) 등록 비용 외에는 어떠한 유지보수 비용도 들지 않습니다.</li>
</ul>
<h3>🎨 2. 한계 없는 디자인 자유도 &amp; 레이아웃 포터빌리티 (Theme Portability)</h3>
<ul>
<li><strong>드래그 앤 드롭 테마 빌더</strong>: 테마 에디터에서 헤더, 메뉴 여백, 픽셀 두께, 로고 정렬(수평/수직)을 완벽하게 맞춤 조절합니다.</li>
<li><strong>디자인 JSON 공유</strong>: 개인정보가 완벽히 소거된 <strong>디자인 JSON 설정 파일 내려받기/올려받기</strong> 기능이 기본 탑재되어 있어, 유저들끼리 클릭 한 번으로 본인이 정교하게 가꾼 테마 레이아웃 디자인을 서로 주고받아 장착할 수 있습니다.</li>
</ul>
<h3>💾 3. 완전무결한 데이터 주권 및 백업/복원 자유 (Full Data Sovereignty)</h3>
<ul>
<li>플랫폼 정책 변화에 구애받지 않고, 자신이 생산한 창작물과 독자 교류 데이터를 완벽하게 소유하고 자유롭게 백업 및 복원할 수 있습니다.</li>
<li>작성한 모든 마크다운/HTML 포스트 본문, 유저 댓글, 방명록 정보와 Cloudflare D1 전역 분산 SQLite 데이터베이스 파일은 <strong>100% 블로그 주인의 온전한 디바이스 하에 보관 및 영구 소유</strong>합니다.</li>
</ul>
<h3>⚡ 4. 경이적인 Lighthouse 성능 지표 (All 100% Desktop)</h3>
<p>V8 Isolate 런타임과 Svelte 5 컴파일 타임 최적화를 통해 구글 크롬 Lighthouse 웹 표준성 검사 시 세계 정상급 최고점을 획득합니다:</p>
<ul>
<li><strong>데스크톱 (Desktop)</strong>: <strong>성능 100점 / 접근성 100점 / 권장사항 100점 / SEO 100점</strong> (전 분야 100점 퍼펙트!)</li>
<li><strong>모바일 (Mobile)</strong>: <strong>성능 94~96점</strong> 획득, 그리고 접근성/권장사항/SEO는 <strong>올 100점</strong> 달성.</li>
</ul>
<h3>📈 5. 실무용 광고/트래커 탑재 시 우수한 성능 지표 유지 (AdSense &amp; GA4)</h3>
<ul>
<li>일반적으로 마케팅 및 통계 수집용 외부 스크립트(Google AdSense, Google Analytics 4, 네이버 서치어드바이저 등)들을 다수 연동하는 경우, 모바일 기기에서의 성능 점수는 급격히 하락하는 경향이 있습니다.</li>
<li>SveltekitBlog는 Svelte 5의 극도로 가볍고 영리한 풋프린트와 비동기 렌더링 흐름을 바탕으로, <strong>이러한 실무용 외부 스크립트들을 모두 구동하더라도 모바일 환경에서 성능 점수 80점대 이상을 안정적으로 기록 및 유지</strong>합니다.</li>
</ul>
<hr>
<h2>✨ Core Features &amp; Engineering Innovations</h2>
<h3>⚡ 1. 무재배포 실시간 디자인 동기화 (Zero-Rebuild Live Sync)</h3>
<ul>
<li><strong>No Build, No Deploy</strong>: 에디터에서 로고 높이, 정렬, 색상 등 테마를 수정하고 저장하는 즉시 CI/CD 배포 파이프라인 가동 없이 독자 화면에 동기화됩니다.</li>
<li><strong>Dynamic Edge SSR</strong>: 독자가 접속하는 찰나 에지 노드에서 D1 데이터베이스(<code>BLOG_DB</code>)를 실시간 병렬 쿼리하여 완성된 스타일시트 HTML을 즉시 배출합니다.</li>
<li><strong>No-Flicker CSS Injection</strong>: CSS 커스텀 변수(<code>--logo-margin</code> 등)를 HTML 루트에 직접 인라인 SSR 주입하여, 자바스크립트 구동 전 레이아웃 깜빡임이나 레이아웃 이동(CLS) 현상을 완전히 배제했습니다.</li>
</ul>
<h3>🎨 2. Svelte 룬 &amp; CSS Repainting 기반 60FPS 테마 프리뷰</h3>
<ul>
<li>React 진영의 전체 컴포넌트 트리 파괴/재생성(Re-render) 병목을 탈피했습니다.</li>
<li>Svelte 5 <code>$state</code> 반응성 상태와 CSS 변수 재매핑(Repaint) 기술을 결합하여, 사용자가 어드민 패널에서 여백 픽셀이나 테마 색상 조절 시 <strong>렌더링 끊김(Lag)이 전혀 없는 완벽한 GPU 가속 60FPS 테마 프리뷰</strong>를 실현합니다.</li>
</ul>
<h3>🖼️ 3. 웹 검색기 스타일 비주얼 미디어 매니저 &amp; REST 어댑터</h3>
<ul>
<li><strong>Zero-SDK 경량 아키텍처</strong>: 무거운 클라우드 공식 SDK 라이브러리를 전면 배제하고, Web Fetch API 표준만을 활용해 각 스토리지의 REST API 규격과 다이렉트로 결합한 초경량 스토리지를 완성했습니다.</li>
<li><strong>비주얼 미리보기 (Thumbnail Grid)</strong>: KV 모드의 텍스트 지향 제한에서 벗어나, R2 / Supabase / ImageKit 활성화 시 마치 <strong>고성능 웹 이미지 검색기</strong>처럼 전체 에셋을 풍부한 썸네일 그리드로 브라우징 및 폴더 탐색할 수 있습니다.</li>
<li><strong>원클릭 원격 물리 삭제</strong>: 대시보드에서 쓰지 않는 에셋을 선택해 삭제하면 <code>adapter.delete(keys)</code> 함수가 비동기 구동되어 <strong>원격지 스토리지의 물리 파일 자체를 즉각 영구 삭제</strong>함으로써 불필요한 클라우드 부하와 유지 요금을 방지합니다.</li>
</ul>
<h3>🛰️ 4. 스크롤 스레드 부하 0%의 IntersectionObserver 센서</h3>
<ul>
<li>윈도우 스크롤 이벤트를 매번 리스닝하여 메인 스레드를 선점하고 버벅임을 유발하는 전통적인 방식에서 과감히 탈피했습니다.</li>
<li>헤더 하단부에 보이지 않는 <strong>1px 크기의 투명 센서 엘리먼트</strong>를 숨겨두고 브라우저 자체 엔진이 스레드 백그라운드에서 구동하는 <strong><code>IntersectionObserver</code></strong> 센서를 맵핑하여 스크롤 점유율 0%의 압도적으로 스무스한 모바일 스크롤 경험을 제공합니다.</li>
</ul>
<h3>🌐 5. 무한 확장형 단일 진실원(SSOT) 다국어 i18n 시스템 &amp; 멀티 랭귀지 배포</h3>
<ul>
<li><strong>3개 국어 기본 탑재 및 무한 확장</strong>: 한국어, 영어, 일본어(<code>ko</code>, <code>en</code>, <code>ja</code>)를 즉시 사용할 수 있도록 기본 탑재하고 있습니다. 이에 그치지 않고 새로운 언어가 필요한 경우, 사전에 언어 키를 추가하고 해당 번역 텍스트만 채워 넣는 방식으로 무한히 쉽게 확장할 수 있는 개방 구조를 가집니다.</li>
<li><strong>1개 포스트의 다국어 동시 발행 (Multi-language Publishing)</strong>: 동일한 포스트를 서로 다른 언어 번역 버전으로 동시에 연동 발행할 수 있습니다. 데이터베이스 수준에서 <code>translationGroupId</code> 관계를 연결해 한 묶음으로 엮기 때문에, 독자는 블로그 화면 상단에서 클릭 한 번으로 영문판, 일문판 등을 실시간 스위칭하며 즉각적으로 감상할 수 있습니다.</li>
<li><strong>강건한 폴백(Fallback) 엔진</strong>: 타입 안전성이 검증된 번역 사전을 모노레포 패키지 레벨(<code>packages/shared/src/i18n</code>)에서 공유하여 중복 코드를 완벽 배제하며, 특정 언어 번역이 누락되었을 경우 기본 언어로 매끄럽게 대체 노출되는 완성도 높은 폴백 시스템을 가집니다.</li>
</ul>
<hr>
<h2>📐 Overall Architecture Flow</h2>
<pre><code class="language-mermaid">sequenceDiagram
    actor Client as 웹 브라우저 클라이언트
    participant Anycast as CF Anycast Edge CDN
    participant Pages as CF Pages (SvelteKit 에지 런타임)
    participant D1 as Cloudflare D1 (BLOG_DB)
    participant Storage as Cloudflare R2 / Supabase / ImageKit

    Client-&gt;&gt;Anycast: 1. 블로그 URL 접속 요청
    Anycast-&gt;&gt;Pages: 2. 최저 핑 리전 에지 Worker 기동 (Cold Start 0ms)
    Pages-&gt;&gt;D1: 3. db.getSettings() 실시간 디자인 설정 조회 (0ms~5ms)
    D1--&gt;&gt;Pages: 4. SQL 쿼리 결과 반환
    Pages-&gt;&gt;Storage: 5. 이미지 프록시 레이어 가동 및 에셋 캐시 체크
    Storage--&gt;&gt;Pages: 6. 이미지 바이너리 버퍼 반환 (CDN 캐시 수혈)
    Pages--&gt;&gt;Client: 7. CSS 변수가 SSR 인라인 탑재된 최적의 HTML 응답 반환
</code></pre>
<hr>
<h2>🛠️ Quick Start &amp; Deployment</h2>
<p>본 프로젝트는 의존성 복잡도를 원천 배제하기 위해 npm workspaces 모노레포 구조로 단정하게 엮여 있습니다.</p>
<blockquote>
<p>[!IMPORTANT]<br><strong>로컬 및 클라우드 배포 통합 한방 셋업 (<code>npm run setup</code>)</strong></p>
<ul>
<li><strong>인프라 자동화 끝판왕</strong>: Cloudflare D1(데이터베이스) 및 KV Namespace 자원 생성, <code>wrangler.json</code> 바인딩 자동 업데이트, 스키마 마이그레이션 및 시드 데이터 이식, 에지 서버 환경변수(Secrets) 동기화, 전체 워크스페이스 빌드 및 Cloudflare Pages 최종 프로덕션 라이브 서버 배포에 이르기까지 <strong>모든 로컬 및 클라우드 구축 프로세스를 단 하나의 스크립트로 완전 종결</strong>합니다!</li>
<li><strong>구동 OS 권장</strong>: 로컬 에뮬레이션은 Wrangler 가상화가 안정적으로 동작하는 <strong>리눅스(Linux) 환경</strong>을 권장합니다.</li>
</ul>
</blockquote>
<h3>1. 의존성 셋업 및 취약점 자동 패치</h3>
<pre><code class="language-bash"># 전체 워크스페이스 의존성 설치 및 로컬 심볼릭 링크 자동 생성
npm install

# 의존성 패키지의 취약점 정밀 보안 검수 및 자동 교정 패치 (필수)
npm audit fix
</code></pre>
<h3>2. 원클릭 로컬 &amp; 클라우드 인프라 한방 구축 (One-Click Setup &amp; Deploy)</h3>
<p>프로덕션 배포 파이프라인과 로컬 DB 셋업은 극단적으로 간소화되어 있습니다. 아래 단 한 줄의 스크립트만 실행하면, 최초 1회 wrangler 연결 승인( wrangler login ) 후 <strong>안내창에 맞춰 엔터(Enter) 몇 번 누르는 것만으로 데이터베이스 구축부터 최종 프로덕션 Pages 라이브 서버 배포까지 원스톱으로 고속 완성</strong>됩니다!</p>
<pre><code class="language-bash"># 클라우드 D1/KV 리소스 생성 + DB 마이그레이션 + 환경변수 동기화 + 빌드 + 라이브 서버 최종 배포까지 한방 실행!
# (실행 후 승인 및 설정 안내에 따라 엔터만 입력해 주면 클라우드 전역 롤아웃이 끝납니다.)
npm run setup
</code></pre>
<h3>3. 로컬 독립 개발 서버 실행 (인프라 구축 완료 후)</h3>
<p>배포 및 설정이 끝난 후 아래 명령어를 호출하여 독립 개발 서버를 띄울 수 있습니다. 이 과정에서 <strong>Wrangler 가상 샌드박스가 이전 <code>setup</code> 단계에서 기입된 <code>wrangler.json</code> 설정 파일의 리소스 바인딩 정보(D1 DB UUID 등)를 자동으로 다시 한번 파악하여, 개발 환경에 최적화된 로컬 환경변수 정보로 자동 등록/매핑</strong>하는 과정을 수행하게 됩니다.</p>
<pre><code class="language-bash"># [환경변수 자동 매핑 및 기동] 1. 블로그 독자 사이트 로컬 개발 기동 (apps/blog)
npm run dev:blog

# [환경변수 자동 매핑 및 기동] 2. 어드민 테마 에디터 로컬 개발 기동 (apps/admin)
npm run dev:admin
</code></pre>
<hr>
<h2>🏁 Architecture Significance</h2>
<p>SveltekitBlog는 무겁고 물리 IDC 리전에 고정된 전통적인 백엔드 아키텍처를 탈피하고, 전 세계 300여 개 Anycast CDN 리전 어디서나 <strong>0ms에 가까운 속도로 코드가 깨어나고, 에지 데이터베이스에서 직접 디자인을 로드하여 독자에게 빛의 속도로 서빙</strong>되는 서버리스 거버넌스의 가장 앞선 모범을 보여줍니다.</p>
]]></content:encoded>
            <category>일반</category>
            <enclosure url="https://img.shields.io/badge/Svelte-5.45%2B-FF3E00?style=for-the-badge&logo=svelte&logoColor=white" length="0" type="image/jpeg"/>
        </item>
    </channel>
</rss>