<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>dev-burnern.dev Blog</title>
        <link>https://dev-burnern.dev</link>
        <description>dev-burnern.dev Blog</description>
        <lastBuildDate>Sun, 26 Apr 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>ko</language>
        <copyright>Copyright © 2026 dev-burnern.dev</copyright>
        <item>
            <title><![CDATA[기술블로그 배포 전에 자동으로 검증하는 것들]]></title>
            <link>https://dev-burnern.dev/platform/blog-deployment-validation</link>
            <guid>https://dev-burnern.dev/platform/blog-deployment-validation</guid>
            <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[개인 기술블로그를 안정적으로 배포하기 위해 sync, test, audit, build를 GitHub Actions와 npm check 명령으로 검증하는 운영 기준을 정리합니다.]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="핵심-요약">핵심 요약<a href="https://dev-burnern.dev/platform/blog-deployment-validation#%ED%95%B5%EC%8B%AC-%EC%9A%94%EC%95%BD" class="hash-link" aria-label="핵심 요약에 대한 직접 링크" title="핵심 요약에 대한 직접 링크" translate="no">​</a></h2>
<p>개인 기술블로그라도 배포 전 검증 기준이 있어야 한다. 글 하나가 깨진 링크나 잘못된 frontmatter를 포함하면 검색 인덱스, RSS, sitemap, 빌드 결과까지 함께 흔들릴 수 있기 때문이다.</p>
<p>이 저장소의 기준 명령은 <code>npm run check</code>다. strict sync, 테스트, audit, build를 한 번에 실행해서 콘텐츠 파이프라인과 Docusaurus 빌드가 같이 통과하는지 확인한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="문제-상황">문제 상황<a href="https://dev-burnern.dev/platform/blog-deployment-validation#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9" class="hash-link" aria-label="문제 상황에 대한 직접 링크" title="문제 상황에 대한 직접 링크" translate="no">​</a></h2>
<p>블로그 배포 실패는 코드 변경에서만 생기지 않는다. Markdown 문서 하나에 잘못된 wiki link가 남아 있어도 공개 페이지의 연결성이 깨진다. 같은 slug를 가진 글이 두 개 생기면 canonical URL과 sitemap 신뢰도가 떨어진다.</p>
<p>반복 검증을 사람이 기억에 의존해서 실행하는 것도 위험하다. 글을 빠르게 수정한 뒤 바로 배포하면 draft 글이 검색 인덱스에 들어가거나, description이 비어 있는 글이 sitemap에 포함될 수 있다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="설계-방식">설계 방식<a href="https://dev-burnern.dev/platform/blog-deployment-validation#%EC%84%A4%EA%B3%84-%EB%B0%A9%EC%8B%9D" class="hash-link" aria-label="설계 방식에 대한 직접 링크" title="설계 방식에 대한 직접 링크" translate="no">​</a></h2>
<p>검증은 네 단계로 나눴다.</p>
<p>첫 번째는 <code>npm run sync:strict</code>다. 이 단계는 Obsidian Markdown을 Docusaurus 글로 정규화하면서 slug 충돌, broken wiki link, 처리 실패, description 부족, tags 누락, category 누락을 검사한다.</p>
<p>두 번째는 <code>npm test</code>다. sync 로직의 순수 함수와 파이프라인 흐름을 Vitest로 검증한다. 이 테스트는 실제 <code>posts/</code>를 망가뜨리지 않도록 임시 디렉터리를 사용한다.</p>
<p>세 번째는 <code>npm run audit</code>다. 전체 의존성 audit을 실행해 보안 취약점을 확인한다. 운영 상황에서 배포 산출물 기준만 보고 싶을 때는 <code>npm run audit:prod</code>를 별도로 사용할 수 있다.</p>
<p>네 번째는 <code>npm run build</code>다. Docusaurus가 RSS, Atom, sitemap, 검색 인덱스, 정적 HTML을 모두 생성할 수 있는지 확인한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="검증-방법">검증 방법<a href="https://dev-burnern.dev/platform/blog-deployment-validation#%EA%B2%80%EC%A6%9D-%EB%B0%A9%EB%B2%95" class="hash-link" aria-label="검증 방법에 대한 직접 링크" title="검증 방법에 대한 직접 링크" translate="no">​</a></h2>
<p>로컬에서는 다음 순서로 확인한다.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> ci</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run sync:strict</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token builtin class-name">test</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run audit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run build</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run check</span><br></div></code></pre></div></div>
<p>GitHub Actions에서는 같은 기준을 더 단순하게 적용한다. 의존성 설치 후 <code>npm run check</code>만 실행하게 해서 로컬과 CI의 기준을 맞춘다.</p>
<p>빌드 결과는 <code>build/sitemap.xml</code>, <code>build/robots.txt</code>, <code>build/llms.txt</code>, 글 HTML의 canonical/OG/Twitter/JSON-LD를 함께 확인한다. 이 검증은 검색 엔진과 AI 검색이 사이트 구조를 올바르게 읽는지 확인하기 위한 최소 기준이다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="개선-방향">개선 방향<a href="https://dev-burnern.dev/platform/blog-deployment-validation#%EA%B0%9C%EC%84%A0-%EB%B0%A9%ED%96%A5" class="hash-link" aria-label="개선 방향에 대한 직접 링크" title="개선 방향에 대한 직접 링크" translate="no">​</a></h2>
<p>앞으로는 검증 결과를 더 읽기 좋은 리포트로 만들 수 있다. 예를 들어 broken wiki link, slug 충돌, description 품질 문제를 Markdown 리포트로 남기면 PR에서 바로 확인할 수 있다.</p>
<p>또한 Lighthouse나 HTML validator를 CI에 추가하면 성능과 접근성까지 같은 흐름에서 점검할 수 있다. 다만 개인 블로그에서는 우선 빌드 안정성과 콘텐츠 메타데이터 신뢰도를 먼저 고정하는 것이 효과가 크다.</p>]]></content:encoded>
            <category>platform</category>
            <category>ci</category>
            <category>github-actions</category>
            <category>docusaurus</category>
            <category>quality-gate</category>
        </item>
        <item>
            <title><![CDATA[sync-posts는 Obsidian Markdown을 어떻게 Docusaurus 글로 바꾸는가]]></title>
            <link>https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown</link>
            <guid>https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown</guid>
            <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Obsidian에서 작성한 Markdown을 Docusaurus 블로그 글로 변환하기 위해 sync-posts가 frontmatter, slug, callout, wiki link를 처리하는 방식을 설명합니다.]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="핵심-요약">핵심 요약<a href="https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown#%ED%95%B5%EC%8B%AC-%EC%9A%94%EC%95%BD" class="hash-link" aria-label="핵심 요약에 대한 직접 링크" title="핵심 요약에 대한 직접 링크" translate="no">​</a></h2>
<p><code>scripts/sync-posts.mjs</code>는 Obsidian에서 작성한 Markdown을 Docusaurus가 안정적으로 빌드할 수 있는 블로그 글로 정규화한다. 핵심은 글쓴이가 매번 메타데이터와 링크 문법을 손으로 맞추지 않아도 되도록, 파일 경로와 본문을 기준으로 필요한 정보를 자동 보정하는 것이다.</p>
<p>이 스크립트는 글 작성 도구와 배포 도구 사이의 번역 계층이다. Obsidian의 빠른 기록 경험은 유지하고, 공개 블로그에는 Docusaurus가 이해할 수 있는 구조만 남긴다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="문제-상황">문제 상황<a href="https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9" class="hash-link" aria-label="문제 상황에 대한 직접 링크" title="문제 상황에 대한 직접 링크" translate="no">​</a></h2>
<p>Obsidian은 개발 기록을 빠르게 남기기 좋지만, 그 문법이 정적 블로그에 그대로 맞지는 않는다. <code>[[문서]]</code> 형태의 wiki link는 Docusaurus 라우트가 아니고, <code>&gt; [!note]</code> 같은 callout은 Docusaurus admonition과 다르다.</p>
<p>frontmatter도 반복 비용이 있다. 글이 늘어나면 <code>category</code>, <code>tags</code>, <code>slug</code>, <code>authors</code>, <code>draft</code>를 매번 같은 기준으로 채워야 한다. 이 작업을 사람이 직접 하면 누락과 불일치가 생기기 쉽다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="구현-방식">구현 방식<a href="https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown#%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D" class="hash-link" aria-label="구현 방식에 대한 직접 링크" title="구현 방식에 대한 직접 링크" translate="no">​</a></h2>
<p>sync 단계는 먼저 <code>posts/</code> 아래의 Markdown과 MDX 파일을 순회한다. Excalidraw, 내부 문서, 숨김 폴더처럼 공개 블로그에 올리지 않을 파일은 제외한다.</p>
<p>각 글은 <code>gray-matter</code>로 frontmatter와 본문을 분리한 뒤 정규화한다. <code>title</code>이 없으면 첫 번째 H1에서 가져오고, <code>date</code>가 없으면 안전한 기본값을 넣는다. <code>category</code>와 <code>tags</code>는 파일 경로에서 생성하며, <code>slug</code>는 직접 지정하지 않았을 때 경로 기반으로 만든다.</p>
<p>본문 변환은 코드 블록을 먼저 보호한 뒤 수행한다. 코드 블록 안의 <code>[[...]]</code>나 callout 예제가 실제 링크로 바뀌면 안 되기 때문이다. 이후 Obsidian callout을 Docusaurus admonition으로 바꾸고, wiki link는 실제 대상 파일을 찾아 블로그 permalink로 변환한다.</p>
<p>마지막으로 공개 글만 모아 <code>static/posts.json</code>을 만든다. 이 파일은 검색 페이지와 시리즈 내비게이션에서 사용한다. <code>draft: true</code> 글은 작성 중인 상태로 보고 검색 인덱스와 AI 검색 문맥에서 제외한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="검증-방법">검증 방법<a href="https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown#%EA%B2%80%EC%A6%9D-%EB%B0%A9%EB%B2%95" class="hash-link" aria-label="검증 방법에 대한 직접 링크" title="검증 방법에 대한 직접 링크" translate="no">​</a></h2>
<p>핵심 변환 로직은 Vitest로 검증한다. 숫자 접두어 제거, tag 정규화, slug 정규화, 경로 기반 category/tag 추출, callout 변환, wiki link 변환, frontmatter 기본값 보정이 테스트 대상이다.</p>
<p>로컬에서는 <code>npm run sync</code>로 경고 모드 동기화를 실행한다. CI와 배포 전에는 <code>npm run sync:strict</code>를 사용한다. strict 모드는 slug 충돌, broken wiki link, 처리 실패, 메타데이터 품질 문제를 실패로 보고 빌드를 막는다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="개선-방향">개선 방향<a href="https://dev-burnern.dev/platform/how-sync-posts-converts-obsidian-markdown#%EA%B0%9C%EC%84%A0-%EB%B0%A9%ED%96%A5" class="hash-link" aria-label="개�선 방향에 대한 직접 링크" title="개선 방향에 대한 직접 링크" translate="no">​</a></h2>
<p>다음 단계는 변환 결과를 더 관찰 가능하게 만드는 것이다. 예를 들어 broken wiki link 리포트를 별도 파일로 남기고, 글별 변환 전후 diff를 요약하면 배포 전에 문제를 더 빨리 찾을 수 있다.</p>
<p>또한 slug 충돌은 현재 strict 실패 조건이지만, 앞으로는 충돌 후보를 더 친절하게 제안할 수 있다. 파일 경로 기반 slug를 유지하되, 같은 이름의 글이 늘어날 때 어떤 경로에서 충돌했는지 명확히 보여주는 방향이 좋다.</p>]]></content:encoded>
            <category>platform</category>
            <category>obsidian</category>
            <category>docusaurus</category>
            <category>markdown</category>
            <category>automation</category>
        </item>
        <item>
            <title><![CDATA[Obsidian 기반 기술블로그 파이프라인을 만든 이유]]></title>
            <link>https://dev-burnern.dev/platform/obsidian-blog-pipeline</link>
            <guid>https://dev-burnern.dev/platform/obsidian-blog-pipeline</guid>
            <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Obsidian에서 작성한 개발 기록을 Docusaurus 기술블로그로 배포하기 위해 콘텐츠 파이프라인을 설계한 이유와 운영 방식을 정리합니다.]]></description>
            <content:encoded><![CDATA[<p>개발 기록은 작성할 때보다 다시 찾아 쓰고 공유할 때 가치가 커진다. 문제는 개인 지식 관리 도구와 공개 블로그가 요구하는 형식이 다르다는 점이다. Obsidian은 빠르게 연결하고 메모하기 좋지만, 그 문법을 그대로 정적 블로그에 올리면 링크, 콜아웃, 메타데이터가 깨지기 쉽다.</p>
<p>이 블로그는 그 간극을 줄이기 위해 만들었다. Obsidian에서는 작성 경험을 유지하고, 배포 단계에서는 Docusaurus가 안정적으로 빌드할 수 있는 문서로 정규화한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="문제-상황">문제 상황<a href="https://dev-burnern.dev/platform/obsidian-blog-pipeline#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9" class="hash-link" aria-label="문제 상황에 대한 직접 링크" title="문제 상황에 대한 직접 링크" translate="no">​</a></h2>
<p>처음에는 글마다 <code>title</code>, <code>date</code>, <code>slug</code>, <code>tags</code>를 직접 관리했다. 글 수가 적을 때는 큰 문제가 아니었지만, 카테고리가 늘어나면서 반복 작업이 많아졌다.</p>
<ul>
<li class="">파일 경로와 블로그 카테고리가 따로 관리되었다.</li>
<li class="">Obsidian의 <code>[[위키링크]]</code>가 공개 블로그에서 의미 없는 텍스트가 되었다.</li>
<li class=""><code>&gt; [!note]</code> 같은 콜아웃을 Docusaurus admonition으로 매번 고쳐야 했다.</li>
<li class="">작성 중인 글을 배포 전에 제외하는 기준이 흔들렸다.</li>
<li class="">검색 페이지를 위해 공개 글 목록을 다시 가공해야 했다.</li>
</ul>
<p>반복되는 작업을 사람이 기억하는 방식으로 해결하면 언젠가 실수한다. 그래서 글 작성 규칙을 코드로 옮겼다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="왜-docusaurus를-선택했는가">왜 Docusaurus를 선택했는가<a href="https://dev-burnern.dev/platform/obsidian-blog-pipeline#%EC%99%9C-docusaurus%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%96%88%EB%8A%94%EA%B0%80" class="hash-link" aria-label="왜 Docusaurus를 선택했는가에 대한 직접 링크" title="왜 Docusaurus를 선택했는가에 대한 직접 링크" translate="no">​</a></h2>
<p>Docusaurus는 기술 문서에 필요한 기능을 기본값에 가깝게 제공한다. Markdown/MDX 기반 작성 흐름, 사이드바와 라우팅, RSS/Atom, sitemap, prism 코드 하이라이트, 플러그인 생태계가 개인 기술블로그에 잘 맞았다.</p>
<p>특히 Mermaid와 KaTeX를 함께 사용할 수 있어 아키텍처 다이어그램과 수식을 글 안에서 자연스럽게 다룰 수 있다. 정적 사이트로 배포되므로 GitHub Pages와도 잘 맞는다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sync-posts가-해결하는-문제">sync-posts가 해결하는 문제<a href="https://dev-burnern.dev/platform/obsidian-blog-pipeline#sync-posts%EA%B0%80-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C" class="hash-link" aria-label="sync-posts가 해결하는 문제에 대한 직접 링크" title="sync-posts가 해결하는 문제에 대한 직접 링크" translate="no">​</a></h2>
<p><code>scripts/sync-posts.mjs</code>는 Obsidian에서 작성한 Markdown을 Docusaurus 블로그 글로 정규화한다.</p>
<ol>
<li class="">누락된 frontmatter를 보정한다.</li>
<li class="">파일 경로에서 category와 tag를 파생한다.</li>
<li class="">slug가 없으면 경로 기반 slug를 생성한다.</li>
<li class="">Obsidian callout을 Docusaurus admonition으로 변환한다.</li>
<li class="">wiki link를 실제 블로그 링크로 변환한다.</li>
<li class=""><code>draft: true</code> 글은 검색 인덱스에서 제외한다.</li>
<li class="">공개 글을 모아 <code>static/posts.json</code>을 생성한다.</li>
</ol>
<p>이 방식 덕분에 작성자는 문서 내용에 집중하고, 배포 전 형식 정리는 스크립트가 담당한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="운영-방식">운영 방식<a href="https://dev-burnern.dev/platform/obsidian-blog-pipeline#%EC%9A%B4%EC%98%81-%EB%B0%A9%EC%8B%9D" class="hash-link" aria-label="운영 방식에 대한 직접 링크" title="운영 방식에 대한 직접 링크" translate="no">​</a></h2>
<p>파일 경로는 정보 구조의 기준이다. 예를 들어 <code>posts/platform/obsidian-blog-pipeline.md</code>는 <code>platform</code> 카테고리와 태그를 자동으로 얻는다. 글이 많아져도 폴더 구조만 보면 어떤 주제를 다루는지 알 수 있다.</p>
<p>slug는 직접 지정할 수 있지만, 지정하지 않아도 경로 기반으로 생성된다. 공개 URL을 직접 제어해야 하는 글만 slug를 명시하고, 일반 글은 파일 경로를 따른다.</p>
<p>draft는 공개 여부를 나누는 운영 스위치다. <code>draft: true</code>인 글은 Docusaurus 빌드 대상에는 남아 있을 수 있지만, 블로그 검색 인덱스와 공개 목록에서 제외하는 기준으로 사용한다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="앞으로의-개선-방향">앞으로의 개선 방향<a href="https://dev-burnern.dev/platform/obsidian-blog-pipeline#%EC%95%9E%EC%9C%BC%EB%A1%9C%EC%9D%98-%EA%B0%9C%EC%84%A0-%EB%B0%A9%ED%96%A5" class="hash-link" aria-label="앞으로의 개선 방향에 대한 직접 링크" title="앞으로의 개선 방향에 대한 직접 링크" translate="no">​</a></h2>
<p>다음 단계는 파이프라인을 더 신뢰할 수 있게 만드는 것이다. slug 충돌을 더 강하게 감지하고, 깨진 wiki link를 리포트로 남기며, 글 품질 체크를 CI에 붙일 수 있다. 최종 목표는 글을 작성하는 순간부터 배포 가능한 기술 문서가 되는 흐름이다.</p>]]></content:encoded>
            <category>platform</category>
            <category>obsidian</category>
            <category>docusaurus</category>
            <category>automation</category>
            <category>knowledge-management</category>
        </item>
        <item>
            <title><![CDATA[개인 기술블로그에서 SEO와 GEO를 함께 설계하는 방법]]></title>
            <link>https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog</link>
            <guid>https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog</guid>
            <pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[개인 기술블로그가 검색 엔진과 AI 검색 모두에서 의도를 이해받도록 canonical, sitemap, robots, JSON-LD, llms.txt를 함께 설계하는 방식을 설명합니다.]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="핵심-요약">핵심 요약<a href="https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog#%ED%95%B5%EC%8B%AC-%EC%9A%94%EC%95%BD" class="hash-link" aria-label="핵심 요약에 대한 직접 링크" title="핵심 요약에 대한 직접 링크" translate="no">​</a></h2>
<p>SEO는 검색 엔진이 페이지를 정확히 수집하고 평가하게 만드는 작업이고, GEO는 생성형 AI 검색이 사이트의 목적과 신뢰 신호를 해석하기 쉽게 만드는 작업이다. 개인 기술블로그도 두 기준을 함께 설계해야 한다.</p>
<p>이 저장소에서는 <code>dev-burnern.dev</code>를 canonical 도메인으로 통일하고, sitemap과 robots, structured data, <code>llms.txt</code>를 같은 기준으로 맞춘다. 목표는 “개발 지식 관리와 배포 자동화 시스템”이라는 사이트의 성격을 사람과 검색 시스템 모두가 이해하게 만드는 것이다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="문제-상황">문제 상황<a href="https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog#%EB%AC%B8%EC%A0%9C-%EC%83%81%ED%99%A9" class="hash-link" aria-label="문제 상황에 대한 직접 링크" title="문제 상황에 대한 직접 링크" translate="no">​</a></h2>
<p>도메인과 사이트명이 섞이면 검색 신호가 분산된다. GitHub Pages 기본 주소, 저장소명, 서비스명이 모두 노출되면 어떤 URL이 대표 주소인지 불명확해진다.</p>
<p>또한 검색 페이지나 404 페이지가 sitemap에 포함되면 품질 신호가 약해질 수 있다. 이런 페이지는 탐색에는 필요하지만 검색 결과에 노출될 대표 콘텐츠는 아니다.</p>
<p>AI 검색 관점에서도 문제가 있다. 글이 여러 개 있어도 사이트 목적, 작성자 신뢰 신호, 핵심 글 묶음이 명확하지 않으면 “무엇을 하는 프로젝트인가”를 요약하기 어렵다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="설계-방식">설계 방식<a href="https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog#%EC%84%A4%EA%B3%84-%EB%B0%A9%EC%8B%9D" class="hash-link" aria-label="설계 방식에 대한 직접 링크" title="설계 방식에 대한 직접 링크" translate="no">​</a></h2>
<p>대표 도메인은 <code>https://dev-burnern.dev</code>로 고정한다. <code>docusaurus.config.ts</code>의 <code>url</code>, <code>robots.txt</code>의 sitemap URL, GitHub Pages custom domain인 <code>static/CNAME</code>을 같은 값으로 맞춘다. GitHub Pages 기본 주소는 README에서 호스팅 주소로만 설명한다.</p>
<p>sitemap에서는 <code>/404</code>, <code>/search</code>, <code>/search-page</code>를 제외한다. 검색 페이지와 404 페이지에는 <code>noindex,follow</code>를 넣어 링크 흐름은 유지하되 검색 결과 대표 문서로는 노출하지 않게 한다.</p>
<p>홈에는 <code>WebSite</code>, <code>Person</code>, <code>Organization</code> JSON-LD를 추가한다. 글 상세 페이지에는 <code>BlogPosting</code>과 <code>BreadcrumbList</code>를 제공하고, 글의 tags를 <code>keywords</code>로 연결한다. 작성자에는 GitHub, Instagram, LinkedIn <code>sameAs</code>를 넣어 신뢰 신호를 정리한다.</p>
<p>GEO를 위해 <code>llms.txt</code>도 생성한다. 이 파일은 사이트 목적, 주요 URL, 콘텐츠 파이프라인 설명, 핵심 글 목록, 작성자 신뢰 신호를 짧고 구조적으로 제공한다. 검색 엔진용 sitemap과 AI 검색용 요약 문맥을 별도로 제공하는 셈이다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="검증-방법">검증 방법<a href="https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog#%EA%B2%80%EC%A6%9D-%EB%B0%A9%EB%B2%95" class="hash-link" aria-label="검증 방법에 대한 직접 링크" title="검증 방법에 대한 직접 링크" translate="no">​</a></h2>
<p>빌드 후에는 <code>build/sitemap.xml</code>에 <code>/404</code>, <code>/search</code>, <code>/search-page</code>가 없는지 확인한다. <code>build/robots.txt</code>가 <code>https://dev-burnern.dev/sitemap.xml</code>을 가리키는지도 확인한다.</p>
<p>홈 HTML에서는 title과 description, OG, Twitter metadata, WebSite/Person/Organization JSON-LD를 확인한다. 글 HTML에서는 canonical, OG, Twitter metadata, BlogPosting JSON-LD, BreadcrumbList가 함께 존재하는지 본다.</p>
<p><code>build/llms.txt</code>는 생성되어야 하며, 핵심 시리즈 글과 작성자 링크를 포함해야 한다. 이 파일은 sync 결과물이므로 <code>npm run sync:strict</code>와 <code>npm run build</code>를 통과하면 같이 검증된다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="개선-방향">개선 방향<a href="https://dev-burnern.dev/platform/seo-geo-for-personal-tech-blog#%EA%B0%9C%EC%84%A0-%EB%B0%A9%ED%96%A5" class="hash-link" aria-label="개선 방향에 대한 직접 링크" title="개선 방향에 대한 직접 링크" translate="no">​</a></h2>
<p>앞으로는 글별 JSON-LD를 더 풍부하게 만들 수 있다. 예를 들어 시리즈 글에는 <code>isPartOf</code>나 <code>position</code> 정보를 추가해 글 묶음을 더 명확히 표현할 수 있다.</p>
<p>또한 OG 이미지를 글별로 자동 생성하면 공유 품질이 좋아진다. 다만 현재 단계에서는 canonical 도메인, sitemap, robots, JSON-LD, <code>llms.txt</code>가 일관된 것이 먼저다. 이 기반이 있어야 검색 엔진과 AI 검색이 같은 신호를 읽는다.</p>]]></content:encoded>
            <category>platform</category>
            <category>seo</category>
            <category>geo</category>
            <category>structured-data</category>
            <category>docusaurus</category>
        </item>
        <item>
            <title><![CDATA[Docker Compose로 개발환경 구축하기]]></title>
            <link>https://dev-burnern.dev/docker/compose-guide</link>
            <guid>https://dev-burnern.dev/docker/compose-guide</guid>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Docker Compose를 활용하여 로컬 개발환경을 효율적으로 구축하는 방법을 알아봅니다.]]></description>
            <content:encoded><![CDATA[<p>Docker Compose는 여러 컨테이너를 정의하고 실행할 수 있는 도구입니다. 이 글에서는 실제 프로젝트에서 Docker Compose를 활용하는 방법을 알아보겠습니다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="왜-docker-compose인가">왜 Docker Compose인가?<a href="https://dev-burnern.dev/docker/compose-guide#%EC%99%9C-docker-compose%EC%9D%B8%EA%B0%80" class="hash-link" aria-label="왜 Docker Compose인가?에 대한 직접 링크" title="왜 Docker Compose인가?에 대한 직접 링크" translate="no">​</a></h2>
<p>개발 환경을 구축할 때 흔히 겪는 문제들이 있습니다:</p>
<ul>
<li class="">팀원마다 다른 환경 설정</li>
<li class="">"내 컴퓨터에서는 되는데..." 문제</li>
<li class="">복잡한 의존성 관리</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>핵심 포인트</div><div class="admonitionContent_BuS1"><p>Docker Compose를 사용하면 <code>docker-compose up</code> 한 줄로 전체 개발환경을 실행할 수 있습니다.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="기본-구성">기본 구성<a href="https://dev-burnern.dev/docker/compose-guide#%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%84%B1" class="hash-link" aria-label="기본 구성에 대한 직접 링크" title="기본 구성에 대한 직접 링크" translate="no">​</a></h2>
<p>다음은 웹 애플리케이션을 위한 기본 <code>docker-compose.yml</code> 예시입니다:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3.8'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">web</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">build</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3000:3000'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> .</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /app/node_modules</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> NODE_ENV=development</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">depends_on</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> db</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> redis</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">db</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> postgres</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">15</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_USER</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dev</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dev123</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_DB</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> myapp</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> postgres_data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/postgresql/data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'5432:5432'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">redis</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">7</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">alpine</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'6379:6379'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  postgres_data</span><span class="token punctuation" style="color:#393A34">:</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="수학적-개념">수학적 개념<a href="https://dev-burnern.dev/docker/compose-guide#%EC%88%98%ED%95%99%EC%A0%81-%EA%B0%9C%EB%85%90" class="hash-link" aria-label="수학적 개념에 대한 직접 링크" title="수학적 개념에 대한 직접 링크" translate="no">​</a></h2>
<p>컨테이너 리소스 할당을 계산할 때 다음 공식을 사용할 수 있습니다:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Total&nbsp;Memory</mtext><mo>=</mo><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><msub><mi>M</mi><mi>i</mi></msub><mo>×</mo><msub><mi>R</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\text{Total Memory} = \sum_{i=1}^{n} M_i \times R_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord text"><span class="mord">Total&nbsp;Memory</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.9291em;vertical-align:-1.2777em"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em"><span style="top:-1.8723em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em"><span class="pstrut" style="height:3.05em"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em"><span class="pstrut" style="height:3.05em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10903em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></span>
<p>여기서 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>M</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">M_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.10903em">M</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.109em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>는 각 컨테이너의 메모리, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">R_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span>는 레플리카 수입니다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="아키텍처-다이어그램">아키텍처 다이어그램<a href="https://dev-burnern.dev/docker/compose-guide#%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8" class="hash-link" aria-label="아키텍처 다이어그램에 대한 직접 링크" title="아키텍처 다이어그램에 대한 직접 링크" translate="no">​</a></h2>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="환경별-구성">환경별 구성<a href="https://dev-burnern.dev/docker/compose-guide#%ED%99%98%EA%B2%BD%EB%B3%84-%EA%B5%AC%EC%84%B1" class="hash-link" aria-label="환경별 구성에 대한 직접 링크" title="환경별 구성에 대한 직접 링크" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>주의사항</div><div class="admonitionContent_BuS1"><p>프로덕션 환경에서는 반드시 별도의 <code>docker-compose.prod.yml</code>을 사용하세요.
시크릿과 민감한 정보는 환경변수로 관리해야 합니다.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="개발-환경">개발 환경<a href="https://dev-burnern.dev/docker/compose-guide#%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD" class="hash-link" aria-label="개발 환경에 대한 직접 링크" title="개발 환경에 대한 직접 링크" translate="no">​</a></h3>
<p>개발 환경에서는 hot-reload와 디버깅을 위한 설정이 필요합니다:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">web</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">build</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">context</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> development</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> .</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/app</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">delegated</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> npm run dev</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="프로덕션-환경">프로덕션 환경<a href="https://dev-burnern.dev/docker/compose-guide#%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%ED%99%98%EA%B2%BD" class="hash-link" aria-label="프로덕션 환경에 대한 직접 링크" title="프로덕션 환경에 대한 직접 링크" translate="no">​</a></h3>
<p>프로덕션에서는 최적화된 이미지를 사용합니다:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">web</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">build</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">context</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> production</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> always</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">deploy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="관련-문서">관련 문서<a href="https://dev-burnern.dev/docker/compose-guide#%EA%B4%80%EB%A0%A8-%EB%AC%B8%EC%84%9C" class="hash-link" aria-label="관련 문서에 대한 직접 링크" title="관련 문서에 대한 직접 링크" translate="no">​</a></h2>
<p>더 자세한 내용은 <a class="" href="https://dev-burnern.dev/react/state-management">React 상태관리</a> 문서를 참고하세요.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="마무리">마무리<a href="https://dev-burnern.dev/docker/compose-guide#%EB%A7%88%EB%AC%B4%EB%A6%AC" class="hash-link" aria-label="마무리에 대한 직접 링크" title="마무리에 대한 직접 링크" translate="no">​</a></h2>
<p>Docker Compose를 활용하면:</p>
<ol>
<li class="">일관된 개발 환경 구축</li>
<li class="">빠른 온보딩</li>
<li class="">쉬운 테스트 환경 구성</li>
</ol>
<p>이 모든 것이 가능해집니다.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>다음 단계</div><div class="admonitionContent_BuS1"><p>다음 글에서는 Kubernetes로의 마이그레이션을 다룰 예정입니다.</p></div></div>]]></content:encoded>
            <category>docker</category>
            <category>devops</category>
            <category>container</category>
        </item>
        <item>
            <title><![CDATA[React 상태관리 완벽 가이드]]></title>
            <link>https://dev-burnern.dev/react/state-management</link>
            <guid>https://dev-burnern.dev/react/state-management</guid>
            <pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[React 애플리케이션에서 상태를 효과적으로 관리하는 다양한 방법을 비교 분석합니다.]]></description>
            <content:encoded><![CDATA[<p>React 애플리케이션이 커지면서 상태 관리는 필수적인 고려사항이 됩니다. 이 글에서는 다양한 상태 관리 방법을 비교해봅니다.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="상태-관리가-필요한-이유">상태 관리가 필요한 이유<a href="https://dev-burnern.dev/react/state-management#%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0" class="hash-link" aria-label="상태 관리가 필요한 이유에 대한 직접 링크" title="상태 관리가 필요한 이유에 대한 직접 링크" translate="no">​</a></h2>
<p>React의 단방향 데이터 흐름은 예측 가능한 코드를 만들지만, 컴포넌트가 깊어지면 <strong>Props Drilling</strong> 문제가 발생합니다.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Props Drilling이란?</div><div class="admonitionContent_BuS1"><p>부모에서 자식으로 props를 여러 단계에 걸쳐 전달해야 하는 상황을 말합니다.
중간 컴포넌트들이 실제로 사용하지 않는 props를 전달만 하는 것은 비효율적입니다.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="상태-관리-옵션-비교">상태 관리 옵션 비교<a href="https://dev-burnern.dev/react/state-management#%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EC%98%B5%EC%85%98-%EB%B9%84%EA%B5%90" class="hash-link" aria-label="상태 관리 옵션 비교에 대한 직접 링크" title="상태 관리 옵션 비교에 대한 직접 링크" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-react-context">1. React Context<a href="https://dev-burnern.dev/react/state-management#1-react-context" class="hash-link" aria-label="1. React Context에 대한 직접 링크" title="1. React Context에 대한 직접 링크" translate="no">​</a></h3>
<p>React 내장 기능으로 별도 라이브러리 없이 사용 가능합니다.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> createContext</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> useContext</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> useState </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">ThemeContextType</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  theme</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'light'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dark'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">toggleTheme</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">ThemeContext</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">createContext</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ThemeContextType </span><span class="token generic-function generic class-name operator" style="color:#393A34">|</span><span class="token generic-function generic class-name"> </span><span class="token generic-function generic class-name keyword" style="color:#00009f">null</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ThemeProvider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> children </span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> children</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">ReactNode</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">theme</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setTheme</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">useState</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name string" style="color:#e3116c">'light'</span><span class="token generic-function generic class-name"> </span><span class="token generic-function generic class-name operator" style="color:#393A34">|</span><span class="token generic-function generic class-name"> </span><span class="token generic-function generic class-name string" style="color:#e3116c">'dark'</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'light'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">toggleTheme</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">setTheme</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prev</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prev </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'light'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dark'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'light'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">ThemeContext.Provider</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">value</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> theme</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> toggleTheme </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">children</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">ThemeContext.Provider</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">useTheme</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">ThemeContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">context</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'useTheme must be used within ThemeProvider'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-zustand">2. Zustand<a href="https://dev-burnern.dev/react/state-management#2-zustand" class="hash-link" aria-label="2. Zustand에 대한 직접 링크" title="2. Zustand에 대한 직접 링크" translate="no">​</a></h3>
<p>가볍고 간단한 상태 관리 라이브러리입니다.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> create </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'zustand'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">CounterState</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">increment</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">decrement</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> useCounter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">create</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">CounterState</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">set</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">increment</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">count</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">decrement</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">count</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-jotai">3. Jotai<a href="https://dev-burnern.dev/react/state-management#3-jotai" class="hash-link" aria-label="3. Jotai에 대한 직접 링크" title="3. Jotai에 대한 직접 링크" translate="no">​</a></h3>
<p>원자적(atomic) 상태 관리를 제공합니다.</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> atom</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> useAtom </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'jotai'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> countAtom </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">atom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> doubledCountAtom </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">atom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">countAtom</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useAtom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">countAtom</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">doubled</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useAtom</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">doubledCountAtom</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Count: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Doubled: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">doubled</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">p</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setCount</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">c</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> c </span><span class="token tag script language-javascript operator" style="color:#393A34">+</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">1</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">+</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="성능-비교">성능 비교<a href="https://dev-burnern.dev/react/state-management#%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90" class="hash-link" aria-label="성능 비교에 대한 직접 링크" title="성능 비교에 대한 직접 링크" translate="no">​</a></h2>
<p>각 라이브러리의 번들 크기를 비교하면:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Bundle&nbsp;Impact</mtext><mo>=</mo><mfrac><mtext>Library&nbsp;Size</mtext><mtext>Total&nbsp;Bundle</mtext></mfrac><mo>×</mo><mn>100</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">\text{Bundle Impact} = \frac{\text{Library Size}}{\text{Total Bundle}} \times 100\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em"></span><span class="mord text"><span class="mord">Bundle&nbsp;Impact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:2.0574em;vertical-align:-0.686em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">Total&nbsp;Bundle</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord text"><span class="mord">Library&nbsp;Size</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em"></span><span class="mord">100%</span></span></span></span></span>
<table><thead><tr><th>라이브러리</th><th>크기 (gzip)</th><th>특징</th></tr></thead><tbody><tr><td>Context</td><td>0 KB</td><td>내장</td></tr><tr><td>Zustand</td><td>~1.5 KB</td><td>간결함</td></tr><tr><td>Jotai</td><td>~3 KB</td><td>원자적</td></tr><tr><td>Redux TK</td><td>~11 KB</td><td>완전함</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="선택-가이드">선택 가이드<a href="https://dev-burnern.dev/react/state-management#%EC%84%A0%ED%83%9D-%EA%B0%80%EC%9D%B4%EB%93%9C" class="hash-link" aria-label="선택 가이드에 대한 직접 링크" title="선택 가이드에 대한 직접 링크" translate="no">​</a></h2>
<!-- -->
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>성능 주의</div><div class="admonitionContent_BuS1"><p>Context API는 값이 변경되면 모든 Consumer가 리렌더링됩니다.
성능이 중요한 경우 상태를 분리하거나 다른 솔루션을 고려하세요.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="실제-적용-예시">실제 적용 예시<a href="https://dev-burnern.dev/react/state-management#%EC%8B%A4%EC%A0%9C-%EC%A0%81%EC%9A%A9-%EC%98%88%EC%8B%9C" class="hash-link" aria-label="실제 적용 예시에 대한 직접 링크" title="실제 적용 예시에 대한 직접 링크" translate="no">​</a></h2>
<p>다음은 <a class="" href="https://dev-burnern.dev/docker/compose-guide">Docker Compose로 개발환경 구축하기</a> 환경에서 React 앱을 개발할 때의 상태 관리 패턴입니다.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="서버-상태-vs-클라이언트-상태">서버 상태 vs 클라이언트 상태<a href="https://dev-burnern.dev/react/state-management#%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C-vs-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%83%81%ED%83%9C" class="hash-link" aria-label="서버 상태 vs 클라이언트 상태에 대한 직접 링크" title="서버 상태 vs 클라이언트 상태에 대한 직접 링크" translate="no">​</a></h3>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 서버 상태: React Query 사용</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isLoading </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  queryKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'users'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  queryFn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> fetchUsers</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 클라이언트 상태: Zustand 사용</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> filter</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setFilter </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useFilterStore</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="결론">결론<a href="https://dev-burnern.dev/react/state-management#%EA%B2%B0%EB%A1%A0" class="hash-link" aria-label="결론에 대한 직접 링크" title="결론에 대한 직접 링크" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>추천 조합</div><div class="admonitionContent_BuS1"><ul>
<li class=""><strong>서버 상태</strong>: TanStack Query (React Query)</li>
<li class=""><strong>클라이언트 상태</strong>: Zustand 또는 Jotai</li>
<li class=""><strong>폼 상태</strong>: React Hook Form</li>
</ul></div></div>
<p>프로젝트 규모와 팀의 익숙함에 따라 적절한 도구를 선택하세요.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="참고-자료">참고 자료<a href="https://dev-burnern.dev/react/state-management#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C" class="hash-link" aria-label="참고 자료에 대한 직접 링크" title="참고 자료에 대한 직접 링크" translate="no">​</a></h2>
<ul>
<li class="">React 공식 문서</li>
<li class="">Zustand GitHub</li>
<li class="">Jotai 문서</li>
</ul>]]></content:encoded>
            <category>react</category>
            <category>state-management</category>
        </item>
    </channel>
</rss>