<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Korobopolly's Dev Blog</title><link>https://korobopolly.github.io/</link><description>Recent content on Korobopolly's Dev Blog</description><generator>Hugo</generator><language>ko</language><lastBuildDate>Mon, 16 Feb 2026 14:00:00 +0900</lastBuildDate><atom:link href="https://korobopolly.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Hugo 시작하기 - 설치, 사이트 생성, GitHub Pages 배포</title><link>https://korobopolly.github.io/posts/hugo-getting-started/</link><pubDate>Mon, 16 Feb 2026 14:00:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/hugo-getting-started/</guid><description>&lt;h2 id="hugo란"&gt;Hugo란?&lt;/h2&gt;
&lt;p&gt;Hugo는 Go로 작성된 정적 사이트 생성기(Static Site Generator)입니다. 마크다운으로 글을 쓰면 HTML로 변환해주며, 빌드 속도가 매우 빠릅니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;수천 개의 페이지도 수 초 이내에 빌드&lt;/li&gt;
&lt;li&gt;마크다운 기반 콘텐츠 관리&lt;/li&gt;
&lt;li&gt;테마와 레이아웃 커스터마이징 자유도 높음&lt;/li&gt;
&lt;li&gt;GitHub Pages, Netlify, Vercel 등 다양한 플랫폼에 배포 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1-설치"&gt;1. 설치&lt;/h2&gt;
&lt;h3 id="windows"&gt;Windows&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# winget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;winget install Hugo.Hugo.Extended
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# chocolatey&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;choco install hugo-extended
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="macos"&gt;macOS&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="linux-ubuntudebian"&gt;Linux (Ubuntu/Debian)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# snap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo snap install hugo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# apt (버전이 오래될 수 있음)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="linux-직접-설치"&gt;Linux (직접 설치)&lt;/h3&gt;
&lt;p&gt;최신 버전이 필요하면 GitHub 릴리즈에서 직접 다운로드합니다.&lt;/p&gt;</description></item><item><title>Git 브랜치 전략 - Git Flow, GitHub Flow, Rebase, Cherry-pick</title><link>https://korobopolly.github.io/posts/git-branch-strategy/</link><pubDate>Mon, 16 Feb 2026 13:25:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/git-branch-strategy/</guid><description>&lt;h2 id="1-브랜치란"&gt;1. 브랜치란?&lt;/h2&gt;
&lt;h3 id="브랜치의-개념"&gt;브랜치의 개념&lt;/h3&gt;
&lt;p&gt;브랜치(Branch)는 독립적인 작업 공간을 만들어주는 Git의 핵심 기능입니다. 마치 평행세계처럼 원본 코드에 영향을 주지 않고 새로운 기능을 개발할 수 있습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 현재 브랜치 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 모든 브랜치 확인 (원격 포함)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git branch -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="head-포인터"&gt;HEAD 포인터&lt;/h3&gt;
&lt;p&gt;HEAD는 현재 작업 중인 브랜치를 가리키는 포인터입니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# HEAD가 가리키는 위치 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat .git/HEAD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 출력: ref: refs/heads/main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 커밋으로 HEAD 이동 (detached HEAD 상태)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout &amp;lt;commit-hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;브랜치 구조 시각화:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>암호화 기초 - AES, RSA, 디지털 서명, 패스워드 해싱, 하이브리드 암호화</title><link>https://korobopolly.github.io/posts/security-encryption-basics/</link><pubDate>Mon, 16 Feb 2026 13:25:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/security-encryption-basics/</guid><description>&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;현대 소프트웨어 시스템에서 데이터 보안은 선택이 아닌 필수입니다. 사용자 비밀번호, 개인정보, 금융 데이터 등 민감한 정보를 안전하게 보호하려면 암호화 기술을 올바르게 이해하고 적용해야 합니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 대칭키 암호화(AES), 비대칭키 암호화(RSA), 디지털 서명의 원리와 Java 구현을 실전 예제와 함께 다룹니다.&lt;/p&gt;
&lt;h2 id="암호화가-필요한-이유"&gt;암호화가 필요한 이유&lt;/h2&gt;
&lt;h3 id="보호해야-할-데이터"&gt;보호해야 할 데이터&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;저장 데이터(Data at Rest)&lt;/strong&gt;: 데이터베이스의 비밀번호, 개인정보&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;전송 데이터(Data in Transit)&lt;/strong&gt;: HTTPS 통신, API 요청/응답&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;처리 데이터(Data in Use)&lt;/strong&gt;: 메모리 상의 민감 정보&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="암호화-없이-발생하는-문제"&gt;암호화 없이 발생하는 문제&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 위험한 예: 평문 저장&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;String password &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;user1234&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db.&lt;span style="color:#89b4fa"&gt;save&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;INSERT INTO users (password) VALUES (&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; password &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;&amp;#39;)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// DB 유출 시 모든 비밀번호 노출&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 위험한 예: 평문 전송&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpClient.&lt;span style="color:#89b4fa"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;http://api.example.com/user?apiKey=secret123&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 중간자 공격(MITM)으로 API 키 탈취 가능&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="암호화-적용-후"&gt;암호화 적용 후&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 안전한 예: 암호화 저장&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;String encrypted &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; AESUtil.&lt;span style="color:#89b4fa"&gt;encrypt&lt;/span&gt;(password, secretKey);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db.&lt;span style="color:#89b4fa"&gt;save&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;INSERT INTO users (password) VALUES (&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; encrypted &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;&amp;#39;)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// DB 유출되어도 암호화된 상태&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 안전한 예: HTTPS 사용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HttpClient.&lt;span style="color:#89b4fa"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;https://api.example.com/user?apiKey=secret123&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// TLS/SSL로 암호화된 통신&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="대칭키-암호화-aes"&gt;대칭키 암호화 (AES)&lt;/h2&gt;
&lt;p&gt;같은 키로 암호화와 복호화를 수행하는 방식입니다. 빠르고 효율적이지만 키 공유 문제가 있습니다.&lt;/p&gt;</description></item><item><title>Git 핵심 개념 - 커밋, 되돌리기, Stash, 태그, 원격 저장소</title><link>https://korobopolly.github.io/posts/git-basics/</link><pubDate>Mon, 16 Feb 2026 13:24:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/git-basics/</guid><description>&lt;h2 id="git이란"&gt;Git이란?&lt;/h2&gt;
&lt;p&gt;Git은 &lt;strong&gt;분산 버전 관리 시스템&lt;/strong&gt;(Distributed Version Control System)으로, 파일의 변경 이력을 추적하고 여러 개발자가 협업할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;h3 id="svn과의-주요-차이점"&gt;SVN과의 주요 차이점&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;특징&lt;/th&gt;
 &lt;th&gt;Git (분산)&lt;/th&gt;
 &lt;th&gt;SVN (중앙집중)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;저장소 위치&lt;/td&gt;
 &lt;td&gt;로컬에 전체 이력 저장&lt;/td&gt;
 &lt;td&gt;중앙 서버에만 이력 저장&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;오프라인 작업&lt;/td&gt;
 &lt;td&gt;가능 (커밋, 브랜치 등)&lt;/td&gt;
 &lt;td&gt;제한적&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;속도&lt;/td&gt;
 &lt;td&gt;빠름 (로컬 작업)&lt;/td&gt;
 &lt;td&gt;느림 (서버 통신 필요)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;브랜치 생성&lt;/td&gt;
 &lt;td&gt;빠르고 가벼움&lt;/td&gt;
 &lt;td&gt;무겁고 비용이 큼&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="git-핵심-개념-3가지-영역"&gt;Git 핵심 개념: 3가지 영역&lt;/h2&gt;
&lt;p&gt;Git은 파일을 3가지 상태로 관리합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Working Directory → Staging Area → Repository
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (작업 디렉토리) (스테이징 영역) (저장소)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ ↓ ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; git add git commit git push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Working Directory&lt;/strong&gt;: 실제 파일을 수정하는 공간&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Staging Area&lt;/strong&gt; (Index): 커밋할 변경사항을 준비하는 공간&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt;: 커밋된 스냅샷이 저장되는 공간&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="기본-명령어"&gt;기본 명령어&lt;/h2&gt;
&lt;h3 id="저장소-초기화-및-복제"&gt;저장소 초기화 및 복제&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 새 Git 저장소 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 원격 저장소 복제&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/username/repository.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 브랜치만 복제&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone -b develop https://github.com/username/repository.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="변경사항-추적-및-커밋"&gt;변경사항 추적 및 커밋&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 파일 상태 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git status
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 파일을 Staging Area에 추가&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add file.txt &lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 파일&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add . &lt;span style="color:#6c7086;font-style:italic"&gt;# 현재 디렉토리 모든 변경사항&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add -p &lt;span style="color:#6c7086;font-style:italic"&gt;# 대화형 모드 (부분 커밋 가능)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -m &lt;span style="color:#a6e3a1"&gt;&amp;#34;커밋 메시지&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# add + commit 한 번에 (추적 중인 파일만)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -am &lt;span style="color:#a6e3a1"&gt;&amp;#34;커밋 메시지&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋 메시지 수정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit --amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="변경사항-확인"&gt;변경사항 확인&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# Working Directory vs Staging Area&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git diff
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# Staging Area vs Repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git diff --staged
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 파일의 변경사항&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git diff file.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 두 커밋 간 차이&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git diff commit1 commit2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="커밋-히스토리-관리"&gt;커밋 히스토리 관리&lt;/h2&gt;
&lt;h3 id="로그-확인"&gt;로그 확인&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 기본 로그&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 한 줄로 보기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log --oneline
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 그래프로 보기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log --graph --oneline --all
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 최근 3개 커밋만&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log -3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 날짜 범위&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log --since&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;2 weeks ago&amp;#34;&lt;/span&gt; --until&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;yesterday&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 작성자&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log --author&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;홍길동&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 파일별 로그&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log -- file.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋 메시지 검색&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log --grep&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;&amp;#34;버그 수정&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="특정-커밋-상세-보기"&gt;특정 커밋 상세 보기&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋 상세 내용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git show commit-hash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 파일의 특정 커밋&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git show commit-hash:path/to/file.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 각 줄을 마지막으로 수정한 사람 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git blame file.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 줄 범위만&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git blame -L 10,20 file.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="되돌리기"&gt;되돌리기&lt;/h2&gt;
&lt;h3 id="reset-vs-revert-vs-checkout"&gt;reset vs revert vs checkout&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# reset: 커밋 히스토리 변경 (로컬 작업에만 사용)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset --soft HEAD~1 &lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋만 취소 (Staging Area 유지)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset --mixed HEAD~1 &lt;span style="color:#6c7086;font-style:italic"&gt;# 커밋 + add 취소 (기본값)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset --hard HEAD~1 &lt;span style="color:#6c7086;font-style:italic"&gt;# 모든 변경사항 삭제 (위험!)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# revert: 새로운 커밋으로 되돌림 (안전, 공유된 브랜치에 사용)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git revert commit-hash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# checkout: 특정 커밋 상태로 이동 (읽기 전용)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout commit-hash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# restore: 파일 되돌리기 (Git 2.23+)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore file.txt &lt;span style="color:#6c7086;font-style:italic"&gt;# Working Directory에서 되돌림&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore --staged file.txt &lt;span style="color:#6c7086;font-style:italic"&gt;# Staging Area에서 되돌림&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="reset-옵션-비교"&gt;reset 옵션 비교&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;옵션&lt;/th&gt;
 &lt;th&gt;HEAD 이동&lt;/th&gt;
 &lt;th&gt;Staging Area&lt;/th&gt;
 &lt;th&gt;Working Directory&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;--soft&lt;/td&gt;
 &lt;td&gt;O&lt;/td&gt;
 &lt;td&gt;유지&lt;/td&gt;
 &lt;td&gt;유지&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;--mixed&lt;/td&gt;
 &lt;td&gt;O&lt;/td&gt;
 &lt;td&gt;초기화&lt;/td&gt;
 &lt;td&gt;유지&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;--hard&lt;/td&gt;
 &lt;td&gt;O&lt;/td&gt;
 &lt;td&gt;초기화&lt;/td&gt;
 &lt;td&gt;초기화&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="실수-복구"&gt;실수 복구&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 실수로 reset --hard 했을 때&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reflog &lt;span style="color:#6c7086;font-style:italic"&gt;# 모든 HEAD 이동 이력 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset --hard HEAD@&lt;span style="color:#89dceb;font-weight:bold"&gt;{&lt;/span&gt;2&lt;span style="color:#89dceb;font-weight:bold"&gt;}&lt;/span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;# 특정 시점으로 복구&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 삭제된 브랜치 복구&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reflog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -b recovered-branch HEAD@&lt;span style="color:#89dceb;font-weight:bold"&gt;{&lt;/span&gt;3&lt;span style="color:#89dceb;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="gitignore-작성법"&gt;.gitignore 작성법&lt;/h2&gt;
&lt;h3 id="기본-문법"&gt;기본 문법&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 주석
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 특정 파일
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;secret.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 특정 디렉토리
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logs/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 와일드카드
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*.log # 모든 .log 파일
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*.log.* # .log.로 시작하는 파일
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 예외 처리
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;*.log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;!important.log # important.log는 추적
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 특정 디렉토리의 파일만
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/config.json # 루트의 config.json만
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;**/temp/ # 모든 경로의 temp 디렉토리
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="언어별-예시"&gt;언어별 예시&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Node.js 프로젝트&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>JWT 인증 시스템 - Spring Security 통합, Refresh Token, 보안 설정</title><link>https://korobopolly.github.io/posts/security-jwt-authentication/</link><pubDate>Mon, 16 Feb 2026 13:24:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/security-jwt-authentication/</guid><description>&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;웹 애플리케이션에서 사용자 인증은 가장 기본적이면서도 중요한 기능입니다. 전통적인 세션 기반 인증 방식은 서버에 상태를 저장하기 때문에 확장성과 분산 환경에서 한계가 있습니다. JWT(JSON Web Token)는 이러한 문제를 해결하는 토큰 기반 인증 방식으로, 최근 REST API와 마이크로서비스 환경에서 널리 사용되고 있습니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 JWT의 원리부터 Spring Boot와 프론트엔드에서의 실전 구현까지 다룹니다.&lt;/p&gt;
&lt;h2 id="세션-기반-vs-토큰-기반-인증"&gt;세션 기반 vs 토큰 기반 인증&lt;/h2&gt;
&lt;h3 id="세션-기반-인증"&gt;세션 기반 인증&lt;/h3&gt;
&lt;p&gt;전통적인 방식으로, 서버가 세션 정보를 메모리나 DB에 저장합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. 사용자 로그인 → 서버가 세션 ID 생성 → 메모리/DB 저장
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. 클라이언트에 세션 ID 쿠키 전송
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. 이후 요청마다 쿠키로 세션 ID 전송 → 서버가 세션 저장소 조회
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>GitHub Actions CI/CD - Spring Boot, React, Docker 빌드와 배포</title><link>https://korobopolly.github.io/posts/docker-github-actions-cicd/</link><pubDate>Mon, 16 Feb 2026 13:23:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/docker-github-actions-cicd/</guid><description>&lt;p&gt;GitHub Actions를 활용하면 코드 푸시부터 배포까지 전 과정을 자동화할 수 있습니다. 이번 포스트에서는 실전에서 바로 사용할 수 있는 CI/CD 파이프라인 구축 방법을 다룹니다.&lt;/p&gt;
&lt;h2 id="1-cicd-개념-이해하기"&gt;1. CI/CD 개념 이해하기&lt;/h2&gt;
&lt;h3 id="ci-continuous-integration-지속적-통합"&gt;CI (Continuous Integration, 지속적 통합)&lt;/h3&gt;
&lt;p&gt;개발자가 코드를 커밋할 때마다 자동으로 빌드와 테스트를 실행하는 프로세스입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;주요 목적:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드 통합 과정에서 발생하는 버그를 조기 발견&lt;/li&gt;
&lt;li&gt;빌드 실패나 테스트 실패를 즉시 파악&lt;/li&gt;
&lt;li&gt;코드 품질 유지 및 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cd-continuous-deploymentdelivery-지속적-배포"&gt;CD (Continuous Deployment/Delivery, 지속적 배포)&lt;/h3&gt;
&lt;p&gt;테스트를 통과한 코드를 자동으로 프로덕션 환경에 배포하는 프로세스입니다.&lt;/p&gt;</description></item><item><title>Docker 시작하기 - Dockerfile, Compose, 멀티스테이지 빌드</title><link>https://korobopolly.github.io/posts/docker-basics/</link><pubDate>Mon, 16 Feb 2026 13:22:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/docker-basics/</guid><description>&lt;h2 id="1-docker란---컨테이너-vs-vm"&gt;1. Docker란? - 컨테이너 vs VM&lt;/h2&gt;
&lt;h3 id="docker가-등장한-이유"&gt;Docker가 등장한 이유&lt;/h3&gt;
&lt;p&gt;&amp;quot;내 컴퓨터에선 잘 되는데요?&amp;quot; - 개발자라면 한 번쯤 들어본 말입니다. 개발 환경과 운영 환경의 차이로 인한 문제를 해결하기 위해 Docker가 등장했습니다.&lt;/p&gt;
&lt;h3 id="컨테이너-vs-가상머신-vm"&gt;컨테이너 vs 가상머신 (VM)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[ VM 구조 ] [ Container 구조 ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;┌──────────────┐ ┌──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ App A │ │ App A │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├──────────────┤ ├──────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Guest OS │ │ Libraries │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├──────────────┤ ├──────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Hypervisor │ │ Docker Engine│
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├──────────────┤ ├──────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Host OS │ │ Host OS │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└──────────────┘ └──────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;가상머신 (VM)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>소켓 프로그래밍 - Java TCP 통신, 프로토콜 설계, 스레드 분리</title><link>https://korobopolly.github.io/posts/network-socket-programming/</link><pubDate>Mon, 16 Feb 2026 13:22:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/network-socket-programming/</guid><description>&lt;h2 id="네트워크-기초"&gt;네트워크 기초&lt;/h2&gt;
&lt;p&gt;네트워크 프로그래밍에서 가장 중요한 선택 중 하나는 전송 프로토콜입니다. TCP와 UDP의 차이를 이해하는 것이 첫 번째 단계입니다.&lt;/p&gt;
&lt;h3 id="tcp-vs-udp"&gt;TCP vs UDP&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;TCP (Transmission Control Protocol)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;연결 지향 프로토콜: 3-way handshake로 연결 수립&lt;/li&gt;
&lt;li&gt;신뢰성 보장: 패킷 순서 보장, 재전송 메커니즘&lt;/li&gt;
&lt;li&gt;흐름 제어와 혼잡 제어&lt;/li&gt;
&lt;li&gt;용도: HTTP, 파일 전송, 데이터베이스 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;UDP (User Datagram Protocol)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;비연결 프로토콜: 연결 수립 없이 즉시 전송&lt;/li&gt;
&lt;li&gt;신뢰성 미보장: 패킷 손실 가능, 순서 보장 없음&lt;/li&gt;
&lt;li&gt;낮은 오버헤드, 빠른 전송 속도&lt;/li&gt;
&lt;li&gt;용도: DNS, 스트리밍, 온라인 게임&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 포스트에서는 신뢰성 있는 통신이 필요한 대부분의 애플리케이션에 적합한 TCP를 중심으로 설명합니다.&lt;/p&gt;</description></item><item><title>DB 인덱스와 쿼리 최적화 - EXPLAIN, N+1 문제, 트랜잭션 격리 수준</title><link>https://korobopolly.github.io/posts/database-index-optimization/</link><pubDate>Mon, 16 Feb 2026 13:21:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/database-index-optimization/</guid><description>&lt;p&gt;데이터베이스의 성능을 좌우하는 가장 중요한 요소 중 하나가 바로 &lt;strong&gt;인덱스&lt;/strong&gt;입니다. 이 글에서는 인덱스의 동작 원리부터 실전 최적화 기법까지 단계별로 알아봅니다.&lt;/p&gt;
&lt;h2 id="1-인덱스란"&gt;1. 인덱스란?&lt;/h2&gt;
&lt;p&gt;인덱스는 데이터베이스 테이블의 &lt;strong&gt;검색 속도를 높이기 위한 자료구조&lt;/strong&gt;입니다. 책의 색인(index)처럼 특정 데이터를 빠르게 찾을 수 있도록 도와줍니다.&lt;/p&gt;
&lt;h3 id="b-tree-구조"&gt;B-Tree 구조&lt;/h3&gt;
&lt;p&gt;대부분의 RDBMS는 B-Tree(Balanced Tree) 구조를 사용합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [50]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [25] [75]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / \ / \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[10] [30] [60] [90]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;B-Tree의 특징:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;균형 잡힌 트리 구조로 탐색 시간이 O(log N)&lt;/li&gt;
&lt;li&gt;루트 노드에서 시작해 리프 노드까지 탐색&lt;/li&gt;
&lt;li&gt;데이터가 정렬된 상태로 유지됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;왜 빠른가?&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>React E2E 테스트 - Playwright 설정, Locator, 네트워크 모킹, CI 통합</title><link>https://korobopolly.github.io/posts/react-e2e-testing/</link><pubDate>Mon, 16 Feb 2026 13:21:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/react-e2e-testing/</guid><description>&lt;p&gt;E2E 테스트는 실제 사용자 관점에서 애플리케이션을 검증합니다. 이번 글에서는 Playwright를 활용한 React 애플리케이션 E2E 테스트 작성법을 알아봅니다.&lt;/p&gt;
&lt;h2 id="e2e-테스트란"&gt;E2E 테스트란&lt;/h2&gt;
&lt;h3 id="테스트-피라미드"&gt;테스트 피라미드&lt;/h3&gt;
&lt;p&gt;소프트웨어 테스트는 세 가지 레벨로 나뉩니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / \ E2E 테스트 (적음, 느림, 비용 높음)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /____\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / \ 통합 테스트 (중간)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /________\
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / \ 단위 테스트 (많음, 빠름, 비용 낮음)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /____________\
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;단위 테스트 (Unit Test):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;개별 함수, 컴포넌트 테스트&lt;/li&gt;
&lt;li&gt;빠르고 격리된 환경&lt;/li&gt;
&lt;li&gt;예: &lt;code&gt;sum(1, 2) === 3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;통합 테스트 (Integration Test):&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>React 상태 관리 - Context API, Provider 패턴, Redux vs Zustand 비교</title><link>https://korobopolly.github.io/posts/react-state-management/</link><pubDate>Mon, 16 Feb 2026 13:20:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/react-state-management/</guid><description>&lt;p&gt;Props drilling 문제를 겪어보신 적 있나요? 컴포넌트가 깊어질수록 상태를 여러 단계에 걸쳐 전달해야 하는 문제가 발생합니다. 이번 글에서는 전역 상태 관리 아키텍처 패턴과 실전 Context 조합 전략, 그리고 프로젝트 규모에 맞는 상태 관리 솔루션 선택 방법을 알아봅니다.&lt;/p&gt;
&lt;h2 id="상태-관리의-필요성"&gt;상태 관리의 필요성&lt;/h2&gt;
&lt;h3 id="props-drilling-문제"&gt;Props Drilling 문제&lt;/h3&gt;
&lt;p&gt;중간 컴포넌트들이 실제로 사용하지 않는 props를 단순히 아래로 전달만 하는 상황입니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-jsx" data-lang="jsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// Props Drilling 예시
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; App() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [user, setUser] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState(&lt;span style="color:#fab387"&gt;null&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;Layout&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;user&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{user} &lt;span style="color:#89b4fa"&gt;setUser&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{setUser} /&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; Layout({ user, setUser }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;Header&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;user&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{user} &lt;span style="color:#89b4fa"&gt;setUser&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{setUser} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;Main&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;user&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{user} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#cba6f7"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; Header({ user, setUser }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;UserMenu&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;user&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{user} &lt;span style="color:#89b4fa"&gt;setUser&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{setUser} /&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; UserMenu({ user, setUser }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 실제로 사용하는 곳은 여기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; user &lt;span style="color:#89dceb;font-weight:bold"&gt;?&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;onClick&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{() =&amp;gt; setUser(&lt;span style="color:#fab387"&gt;null&lt;/span&gt;)}&amp;gt;로그아웃&amp;lt;/&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) &lt;span style="color:#89dceb;font-weight:bold"&gt;:&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;onClick&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{() =&amp;gt; setUser({ name&lt;span style="color:#89dceb;font-weight:bold"&gt;:&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#39;홍길동&amp;#39;&lt;/span&gt; })}&amp;gt;로그인&amp;lt;/&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;중간 컴포넌트들은 &lt;code&gt;user&lt;/code&gt;와 &lt;code&gt;setUser&lt;/code&gt;를 직접 사용하지 않지만 전달만 하고 있습니다. 이것이 Props Drilling 문제입니다.&lt;/p&gt;</description></item><item><title>Spring Boot WebSocket - STOMP 프로토콜과 실시간 통신 구현</title><link>https://korobopolly.github.io/posts/network-websocket-stomp/</link><pubDate>Mon, 16 Feb 2026 13:20:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/network-websocket-stomp/</guid><description>&lt;h2 id="http-폴링-vs-websocket"&gt;HTTP 폴링 vs WebSocket&lt;/h2&gt;
&lt;p&gt;실시간 통신을 구현하는 방법은 크게 두 가지입니다.&lt;/p&gt;
&lt;h3 id="http-폴링-polling"&gt;HTTP 폴링 (Polling)&lt;/h3&gt;
&lt;p&gt;클라이언트가 주기적으로 서버에 요청을 보내 새 데이터를 확인합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// Short Polling
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;setInterval(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch(&lt;span style="color:#a6e3a1"&gt;&amp;#39;/api/messages&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then(res =&amp;gt; res.json())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then(data =&amp;gt; updateUI(data));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}, &lt;span style="color:#fab387"&gt;1000&lt;/span&gt;); &lt;span style="color:#6c7086;font-style:italic"&gt;// 1초마다 요청
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;단점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불필요한 요청이 많음 (데이터가 없어도 요청)&lt;/li&gt;
&lt;li&gt;서버 부하 증가 (동시 접속자 1000명 = 초당 1000건 요청)&lt;/li&gt;
&lt;li&gt;실시간성 낮음 (폴링 간격만큼 지연)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="long-polling"&gt;Long Polling&lt;/h3&gt;
&lt;p&gt;서버가 새 데이터가 있을 때까지 응답을 보류합니다.&lt;/p&gt;</description></item><item><title>SQL 기초 - DDL, DML, JOIN, 서브쿼리, 데이터 모델링</title><link>https://korobopolly.github.io/posts/database-sql-basics/</link><pubDate>Mon, 16 Feb 2026 13:20:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/database-sql-basics/</guid><description>&lt;h2 id="관계형-데이터베이스-개념"&gt;관계형 데이터베이스 개념&lt;/h2&gt;
&lt;p&gt;관계형 데이터베이스(RDBMS)는 데이터를 &lt;strong&gt;테이블&lt;/strong&gt; 형태로 저장하고 관리합니다.&lt;/p&gt;
&lt;h3 id="핵심-용어"&gt;핵심 용어&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;테이블(Table)&lt;/strong&gt;: 데이터를 저장하는 구조 (엑셀의 시트와 유사)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;행(Row)&lt;/strong&gt;: 하나의 레코드 (데이터 항목)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;열(Column)&lt;/strong&gt;: 속성 또는 필드&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;기본키(Primary Key)&lt;/strong&gt;: 각 행을 고유하게 식별하는 열 (중복 불가, NULL 불가)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;외래키(Foreign Key)&lt;/strong&gt;: 다른 테이블의 기본키를 참조하는 열&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;-- 예시: users 테이블
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;-- id (기본키), name, email, created_at (열)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;-- 각 행은 한 명의 사용자 데이터
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="ddl-data-definition-language"&gt;DDL (Data Definition Language)&lt;/h2&gt;
&lt;p&gt;테이블 구조를 정의하는 명령어입니다.&lt;/p&gt;</description></item><item><title>Spring Boot 캐싱 - Caffeine, @Cacheable, TTL 전략</title><link>https://korobopolly.github.io/posts/spring-boot-caching/</link><pubDate>Mon, 16 Feb 2026 13:19:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/spring-boot-caching/</guid><description>&lt;h2 id="캐싱이란"&gt;캐싱이란&lt;/h2&gt;
&lt;p&gt;캐싱(Caching)은 자주 사용되는 데이터를 빠르게 접근할 수 있는 임시 저장소에 보관하는 기술입니다. 데이터베이스 조회, 외부 API 호출, 복잡한 연산 결과 등을 메모리에 저장하여 동일한 요청에 대해 빠르게 응답할 수 있습니다.&lt;/p&gt;
&lt;h2 id="왜-캐싱이-필요한가"&gt;왜 캐싱이 필요한가&lt;/h2&gt;
&lt;h3 id="성능-향상"&gt;성능 향상&lt;/h3&gt;
&lt;p&gt;데이터베이스 조회는 일반적으로 수십~수백 밀리초가 걸리지만, 메모리 캐시는 마이크로초 단위로 응답합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 캐시 없이 매번 DB 조회&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; User &lt;span style="color:#89b4fa"&gt;getUser&lt;/span&gt;(Long id) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; userRepository.&lt;span style="color:#89b4fa"&gt;findById&lt;/span&gt;(id).&lt;span style="color:#89b4fa"&gt;orElseThrow&lt;/span&gt;(); &lt;span style="color:#6c7086;font-style:italic"&gt;// 평균 50ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 캐시 적용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#89b4fa;font-weight:bold"&gt;@Cacheable&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; User &lt;span style="color:#89b4fa"&gt;getUser&lt;/span&gt;(Long id) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; userRepository.&lt;span style="color:#89b4fa"&gt;findById&lt;/span&gt;(id).&lt;span style="color:#89b4fa"&gt;orElseThrow&lt;/span&gt;(); &lt;span style="color:#6c7086;font-style:italic"&gt;// 첫 조회 50ms, 이후 0.1ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="비용-절감"&gt;비용 절감&lt;/h3&gt;
&lt;p&gt;외부 API 호출이나 복잡한 연산을 캐싱하면 서버 리소스와 비용을 절감할 수 있습니다.&lt;/p&gt;</description></item><item><title>Spring Boot 이벤트 아키텍처 - @EventListener, @Async, 트랜잭션 이벤트</title><link>https://korobopolly.github.io/posts/spring-boot-event-driven/</link><pubDate>Mon, 16 Feb 2026 13:18:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/spring-boot-event-driven/</guid><description>&lt;h2 id="이벤트-기반-아키텍처란"&gt;이벤트 기반 아키텍처란&lt;/h2&gt;
&lt;p&gt;이벤트 기반 아키텍처(Event-Driven Architecture)는 시스템의 컴포넌트들이 이벤트를 통해 상호작용하는 설계 패턴입니다. 특정 작업이 완료되었을 때 이벤트를 발행하고, 관심 있는 컴포넌트가 이를 구독하여 처리하는 방식입니다.&lt;/p&gt;
&lt;p&gt;전통적인 방식에서는 서비스 A가 서비스 B, C, D를 직접 호출했다면, 이벤트 기반 방식에서는 서비스 A가 이벤트만 발행하고, B, C, D가 각자 필요한 이벤트를 구독하여 처리합니다.&lt;/p&gt;
&lt;h2 id="왜-이벤트-기반-아키텍처인가"&gt;왜 이벤트 기반 아키텍처인가&lt;/h2&gt;
&lt;h3 id="서비스-간-결합도-감소"&gt;서비스 간 결합도 감소&lt;/h3&gt;
&lt;p&gt;직접 호출 방식의 문제점:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#89b4fa;font-weight:bold"&gt;@Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;UserService&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; EmailService emailService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; NotificationService notificationService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; AuditService auditService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; AnalyticsService analyticsService;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;void&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;registerUser&lt;/span&gt;(User user) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; userRepository.&lt;span style="color:#89b4fa"&gt;save&lt;/span&gt;(user);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 모든 서비스를 직접 호출&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; emailService.&lt;span style="color:#89b4fa"&gt;sendWelcomeEmail&lt;/span&gt;(user);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationService.&lt;span style="color:#89b4fa"&gt;sendPushNotification&lt;/span&gt;(user);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auditService.&lt;span style="color:#89b4fa"&gt;logUserRegistration&lt;/span&gt;(user);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; analyticsService.&lt;span style="color:#89b4fa"&gt;trackUserSignup&lt;/span&gt;(user);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;위 코드는 &lt;code&gt;UserService&lt;/code&gt;가 4개의 서비스에 강하게 결합되어 있습니다. 새로운 기능 추가 시마다 &lt;code&gt;UserService&lt;/code&gt;를 수정해야 합니다.&lt;/p&gt;</description></item><item><title>Java 동시성 프로그래밍 - ExecutorService, 동기화, Context Switching</title><link>https://korobopolly.github.io/posts/java-concurrency/</link><pubDate>Mon, 16 Feb 2026 13:16:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/java-concurrency/</guid><description>&lt;h2 id="동시성-프로그래밍이란"&gt;동시성 프로그래밍이란&lt;/h2&gt;
&lt;p&gt;현대 애플리케이션은 여러 작업을 동시에 처리해야 합니다. 파일 업로드를 받으면서 DB 쿼리를 실행하고, API 요청을 처리하는 등 멀티태스킹은 필수입니다. Java는 강력한 동시성 라이브러리를 제공하여 이런 작업을 안전하고 효율적으로 처리할 수 있게 합니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 Thread 기본부터 ThreadPool, 동기화 메커니즘까지 실전 예제와 함께 알아봅니다.&lt;/p&gt;
&lt;h2 id="thread와-runnable-기본"&gt;Thread와 Runnable 기본&lt;/h2&gt;
&lt;h3 id="thread-생성-방법"&gt;Thread 생성 방법&lt;/h3&gt;
&lt;p&gt;Java에서 스레드를 생성하는 두 가지 방법이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Thread 클래스 상속&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;MyThread&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;extends&lt;/span&gt; Thread {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#89b4fa;font-weight:bold"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;void&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;run&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; System.&lt;span style="color:#89b4fa"&gt;out&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;println&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;Thread 실행: &amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; Thread.&lt;span style="color:#89b4fa"&gt;currentThread&lt;/span&gt;().&lt;span style="color:#89b4fa"&gt;getName&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 사용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MyThread thread &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#cba6f7"&gt;new&lt;/span&gt; MyThread();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;thread.&lt;span style="color:#89b4fa"&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Runnable 인터페이스 구현 (권장)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Java 디자인 패턴 - Singleton, Builder, Observer, Strategy 구현</title><link>https://korobopolly.github.io/posts/java-design-patterns/</link><pubDate>Mon, 16 Feb 2026 13:15:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/java-design-patterns/</guid><description>&lt;h2 id="디자인-패턴이란"&gt;디자인 패턴이란&lt;/h2&gt;
&lt;p&gt;디자인 패턴은 소프트웨어 설계에서 반복적으로 나타나는 문제들에 대한 재사용 가능한 해결책입니다. &amp;quot;바퀴를 다시 발명하지 마라&amp;quot;는 원칙처럼, 검증된 설계 방법을 배우고 적용하면 유지보수가 쉽고 확장 가능한 코드를 작성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 실무에서 자주 쓰이는 핵심 패턴들을 Java 코드와 함께 알아봅니다.&lt;/p&gt;
&lt;h2 id="디자인-패턴의-분류"&gt;디자인 패턴의 분류&lt;/h2&gt;
&lt;p&gt;GoF(Gang of Four)는 23가지 패턴을 세 가지로 분류했습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;생성 패턴 (Creational)&lt;/strong&gt;: 객체 생성 방식 - Singleton, Builder, Factory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;구조 패턴 (Structural)&lt;/strong&gt;: 객체 조합 방식 - Facade, Adapter, Decorator&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;행위 패턴 (Behavioral)&lt;/strong&gt;: 객체 간 통신 방식 - Observer, Strategy, Template Method&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;모든 패턴을 외울 필요는 없습니다. 문제를 만났을 때 &amp;quot;이 상황에 맞는 패턴이 있나?&amp;quot;를 떠올릴 수 있으면 충분합니다.&lt;/p&gt;</description></item><item><title>React Hooks 심화 - useEffect, useMemo, useReducer, 커스텀 Hook</title><link>https://korobopolly.github.io/posts/react-hooks/</link><pubDate>Mon, 16 Feb 2026 13:15:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/react-hooks/</guid><description>&lt;h2 id="react-hooks란"&gt;React Hooks란?&lt;/h2&gt;
&lt;p&gt;Hooks는 함수형 컴포넌트에서 상태 관리, 사이드 이펙트 처리, 성능 최적화 등을 가능하게 하는 함수입니다. React 16.8에서 도입되어 현재 React 개발의 핵심이 되었습니다.&lt;/p&gt;
&lt;h3 id="hooks-규칙"&gt;Hooks 규칙&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;최상위에서만 호출&lt;/strong&gt;: 조건문, 반복문, 중첩 함수 내에서 호출 금지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React 함수 내에서만 호출&lt;/strong&gt;: 일반 JavaScript 함수에서 호출 금지&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tsx" data-lang="tsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 잘못된 사용
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; BadExample() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;if&lt;/span&gt; (someCondition) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [value, setValue] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState(&lt;span style="color:#a6e3a1"&gt;&amp;#34;&amp;#34;&lt;/span&gt;); &lt;span style="color:#6c7086;font-style:italic"&gt;// 조건문 안에서 호출 금지
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 올바른 사용
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; GoodExample() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [value, setValue] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState(&lt;span style="color:#a6e3a1"&gt;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;if&lt;/span&gt; (someCondition) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// Hook이 아닌 로직은 조건문 안에서 사용 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="useeffect---사이드-이펙트-처리"&gt;useEffect - 사이드 이펙트 처리&lt;/h2&gt;
&lt;h3 id="기본-사용법"&gt;기본 사용법&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tsx" data-lang="tsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#cba6f7"&gt;import&lt;/span&gt; { useState, useEffect } &lt;span style="color:#cba6f7"&gt;from&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; UserProfile({ userId }&lt;span style="color:#89dceb;font-weight:bold"&gt;:&lt;/span&gt; { userId: &lt;span style="color:#f38ba8"&gt;number&lt;/span&gt; }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [user, setUser] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState&amp;lt;&lt;span style="color:#cba6f7"&gt;User&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;|&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;null&lt;/span&gt;&amp;gt;(&lt;span style="color:#fab387"&gt;null&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [loading, setLoading] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState(&lt;span style="color:#fab387"&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; useEffect(() &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 사이드 이펙트 실행
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setLoading(&lt;span style="color:#fab387"&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch(&lt;span style="color:#a6e3a1"&gt;`/api/users/&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;${&lt;/span&gt;userId&lt;span style="color:#a6e3a1"&gt;}&lt;/span&gt;&lt;span style="color:#a6e3a1"&gt;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then((res) &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; res.json())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then((data) &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setUser(data);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; setLoading(&lt;span style="color:#fab387"&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, [userId]); &lt;span style="color:#6c7086;font-style:italic"&gt;// userId가 변경될 때만 실행
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;if&lt;/span&gt; (loading) &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;&lt;span style="color:#f38ba8"&gt;로딩&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;중&lt;/span&gt;...&amp;lt;/&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;if&lt;/span&gt; (&lt;span style="color:#89dceb;font-weight:bold"&gt;!&lt;/span&gt;user) &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;&lt;span style="color:#f38ba8"&gt;사용자를&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;찾을&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;수&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;없습니다&lt;/span&gt;.&amp;lt;/&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;h2&lt;/span&gt;&amp;gt;{user.name}&amp;lt;/&lt;span style="color:#cba6f7"&gt;h2&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;{user.email}&amp;lt;/&lt;span style="color:#cba6f7"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#cba6f7"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="의존성-배열-패턴"&gt;의존성 배열 패턴&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-tsx" data-lang="tsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;function&lt;/span&gt; EffectPatterns() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;const&lt;/span&gt; [count, setCount] &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; useState(&lt;span style="color:#fab387"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 1. 마운트 시 1회만 실행
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; useEffect(() &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#a6e3a1"&gt;&amp;#34;컴포넌트 마운트&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, []);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 2. 특정 값 변경 시 실행
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; useEffect(() &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#a6e3a1"&gt;&amp;#34;count 변경:&amp;#34;&lt;/span&gt;, count);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, [count]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 3. 매 렌더링마다 실행 (의존성 배열 생략)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; useEffect(() &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#a6e3a1"&gt;&amp;#34;렌더링됨&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &amp;lt;&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;onClick&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt;{() &lt;span style="color:#89dceb;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; setCount(count &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#fab387"&gt;1&lt;/span&gt;)}&amp;gt;{count}&amp;lt;/&lt;span style="color:#cba6f7"&gt;button&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="클린업-함수"&gt;클린업 함수&lt;/h3&gt;
&lt;p&gt;컴포넌트 언마운트 시 또는 이펙트 재실행 전에 정리 작업을 수행합니다.&lt;/p&gt;</description></item><item><title>Java Stream API - filter, map, collect와 병렬 스트림</title><link>https://korobopolly.github.io/posts/java-stream-api/</link><pubDate>Mon, 16 Feb 2026 13:14:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/java-stream-api/</guid><description>&lt;h2 id="stream-api란"&gt;Stream API란?&lt;/h2&gt;
&lt;p&gt;Java 8에서 도입된 Stream API는 컬렉션 데이터를 함수형 스타일로 처리할 수 있게 해주는 강력한 도구입니다. Stream을 사용하면 데이터를 선언적으로 처리하고, 병렬 처리를 쉽게 구현할 수 있으며, 코드의 가독성을 크게 향상시킬 수 있습니다.&lt;/p&gt;
&lt;p&gt;Stream은 데이터의 흐름을 나타내며, 원본 데이터를 변경하지 않고 중간 연산과 최종 연산을 통해 데이터를 처리합니다. 이러한 특성 덕분에 불변성을 유지하면서도 효율적인 데이터 처리가 가능합니다.&lt;/p&gt;
&lt;h2 id="stream-생성-방법"&gt;Stream 생성 방법&lt;/h2&gt;
&lt;p&gt;Stream을 생성하는 다양한 방법이 있습니다.&lt;/p&gt;
&lt;h3 id="컬렉션으로부터-생성"&gt;컬렉션으로부터 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;List&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; list &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Arrays.&lt;span style="color:#89b4fa"&gt;asList&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;Apple&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Banana&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Cherry&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; stream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; list.&lt;span style="color:#89b4fa"&gt;stream&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 병렬 스트림&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; parallelStream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; list.&lt;span style="color:#89b4fa"&gt;parallelStream&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="배열로부터-생성"&gt;배열로부터 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;[]&lt;/span&gt; array &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; {&lt;span style="color:#a6e3a1"&gt;&amp;#34;Apple&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Banana&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Cherry&amp;#34;&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; stream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Arrays.&lt;span style="color:#89b4fa"&gt;stream&lt;/span&gt;(array);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 범위 지정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;int&lt;/span&gt;&lt;span style="color:#89dceb;font-weight:bold"&gt;[]&lt;/span&gt; numbers &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; {1, 2, 3, 4, 5};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IntStream stream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Arrays.&lt;span style="color:#89b4fa"&gt;stream&lt;/span&gt;(numbers, 1, 4); &lt;span style="color:#6c7086;font-style:italic"&gt;// 2, 3, 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="streamof-사용"&gt;Stream.of() 사용&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;String&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; stream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Stream.&lt;span style="color:#89b4fa"&gt;of&lt;/span&gt;(&lt;span style="color:#a6e3a1"&gt;&amp;#34;Apple&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Banana&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e3a1"&gt;&amp;#34;Cherry&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; numberStream &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Stream.&lt;span style="color:#89b4fa"&gt;of&lt;/span&gt;(1, 2, 3, 4, 5);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="범위-생성"&gt;범위 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 1부터 10까지 (10 포함)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IntStream range &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; IntStream.&lt;span style="color:#89b4fa"&gt;rangeClosed&lt;/span&gt;(1, 10);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 1부터 10까지 (10 미포함)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IntStream range2 &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; IntStream.&lt;span style="color:#89b4fa"&gt;range&lt;/span&gt;(1, 10);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="무한-스트림-생성"&gt;무한 스트림 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// iterate: 초기값부터 시작해서 함수를 반복 적용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; evenNumbers &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Stream.&lt;span style="color:#89b4fa"&gt;iterate&lt;/span&gt;(0, n &lt;span style="color:#89dceb;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; n &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; 2);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// generate: 매번 새로운 값을 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;Double&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; randomNumbers &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Stream.&lt;span style="color:#89b4fa"&gt;generate&lt;/span&gt;(Math::random);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;// 무한 스트림은 limit()으로 제한 필요&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stream&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;lt;&lt;/span&gt;Integer&lt;span style="color:#89dceb;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; first10Evens &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; Stream.&lt;span style="color:#89b4fa"&gt;iterate&lt;/span&gt;(0, n &lt;span style="color:#89dceb;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; n &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; 2).&lt;span style="color:#89b4fa"&gt;limit&lt;/span&gt;(10);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="중간-연산-intermediate-operations"&gt;중간 연산 (Intermediate Operations)&lt;/h2&gt;
&lt;p&gt;중간 연산은 Stream을 반환하므로 여러 개를 연결(chaining)할 수 있습니다. 중간 연산은 지연 평가(lazy evaluation)되어 최종 연산이 호출될 때까지 실행되지 않습니다.&lt;/p&gt;</description></item><item><title>React 시작하기 - JSX, 컴포넌트, Props, State, 이벤트 처리</title><link>https://korobopolly.github.io/posts/react-introduction/</link><pubDate>Mon, 16 Feb 2026 13:14:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/react-introduction/</guid><description>&lt;h2 id="react란"&gt;React란?&lt;/h2&gt;
&lt;p&gt;React는 Meta(구 Facebook)에서 개발한 UI 라이브러리입니다. &lt;strong&gt;컴포넌트 기반 아키텍처&lt;/strong&gt;로 복잡한 UI를 독립적이고 재사용 가능한 조각으로 나누어 개발할 수 있습니다.&lt;/p&gt;
&lt;h3 id="react의-핵심-특징"&gt;React의 핵심 특징&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;컴포넌트 기반&lt;/strong&gt;: UI를 독립적인 컴포넌트로 분리하여 관리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;선언적 UI&lt;/strong&gt;: 상태에 따라 UI가 자동으로 업데이트&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Virtual DOM&lt;/strong&gt;: 효율적인 렌더링으로 성능 최적화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;단방향 데이터 흐름&lt;/strong&gt;: 예측 가능한 데이터 관리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;풍부한 생태계&lt;/strong&gt;: Next.js, React Native 등 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="프로젝트-생성"&gt;프로젝트 생성&lt;/h2&gt;
&lt;h3 id="vite로-react-프로젝트-생성"&gt;Vite로 React 프로젝트 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# Vite로 React + TypeScript 프로젝트 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm create vite@latest my-react-app -- --template react-ts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 프로젝트 디렉토리로 이동&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#89dceb"&gt;cd&lt;/span&gt; my-react-app
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 의존성 설치&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6c7086;font-style:italic"&gt;# 개발 서버 시작&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="프로젝트-구조"&gt;프로젝트 구조&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;my-react-app/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── public/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── vite.svg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── src/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── assets/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── App.tsx
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── App.css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ├── main.tsx
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── index.css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── package.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── tsconfig.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── vite.config.ts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;핵심 파일 설명&lt;/strong&gt;:&lt;/p&gt;</description></item><item><title>Spring Boot REST API - Controller, JPA CRUD, 예외 처리</title><link>https://korobopolly.github.io/posts/spring-boot-rest-api/</link><pubDate>Mon, 16 Feb 2026 13:13:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/spring-boot-rest-api/</guid><description>&lt;h2 id="rest-api-설계-원칙"&gt;REST API 설계 원칙&lt;/h2&gt;
&lt;p&gt;RESTful API는 HTTP 프로토콜을 기반으로 자원(Resource)을 URI로 표현하고, HTTP 메서드로 작업을 정의하는 아키텍처 스타일입니다. Spring Boot는 REST API 개발을 위한 강력한 기능을 제공합니다.&lt;/p&gt;
&lt;h3 id="rest의-핵심-원칙"&gt;REST의 핵심 원칙&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. 자원 기반 URI 설계&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;명사를 사용하여 자원을 표현: &lt;code&gt;/users&lt;/code&gt;, &lt;code&gt;/products&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;계층 구조 표현: &lt;code&gt;/users/{id}/orders&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;복수형 사용 권장: &lt;code&gt;/users&lt;/code&gt; (O), &lt;code&gt;/user&lt;/code&gt; (X)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. HTTP 메서드 활용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET: 조회&lt;/li&gt;
&lt;li&gt;POST: 생성&lt;/li&gt;
&lt;li&gt;PUT: 전체 수정&lt;/li&gt;
&lt;li&gt;PATCH: 부분 수정&lt;/li&gt;
&lt;li&gt;DELETE: 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 상태 코드 활용&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Spring Boot 시작하기 - 프로젝트 생성, 구조, 첫 실행</title><link>https://korobopolly.github.io/posts/spring-boot-introduction/</link><pubDate>Mon, 16 Feb 2026 13:12:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/spring-boot-introduction/</guid><description>&lt;h2 id="spring-boot란-무엇인가"&gt;Spring Boot란 무엇인가?&lt;/h2&gt;
&lt;p&gt;Spring Boot는 Spring 프레임워크를 기반으로 한 오픈소스 Java 프레임워크입니다. Spring의 강력한 기능을 그대로 사용하면서도, 복잡한 설정 없이 빠르게 프로덕션 레벨의 애플리케이션을 만들 수 있도록 설계되었습니다.&lt;/p&gt;
&lt;h3 id="spring-vs-spring-boot"&gt;Spring vs Spring Boot&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;전통적인 Spring Framework&lt;/strong&gt;의 경우:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XML 기반의 복잡한 설정 파일 필요&lt;/li&gt;
&lt;li&gt;의존성 버전 관리를 직접 해야 함&lt;/li&gt;
&lt;li&gt;서버 배포를 위한 WAR 파일 생성 및 Tomcat 설치 필요&lt;/li&gt;
&lt;li&gt;설정 코드가 비즈니스 로직보다 많아지는 경우 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Spring Boot&lt;/strong&gt;는 이러한 문제를 해결합니다:&lt;/p&gt;</description></item><item><title>Lombok 핵심 어노테이션 - @Data, @Builder, Spring Boot 실전 패턴</title><link>https://korobopolly.github.io/posts/java-lombok/</link><pubDate>Mon, 16 Feb 2026 13:11:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/java-lombok/</guid><description>&lt;h2 id="lombok이란"&gt;Lombok이란&lt;/h2&gt;
&lt;p&gt;Java로 개발하다 보면 getter, setter, 생성자, toString, equals 같은 반복적인 코드를 매번 작성해야 합니다. Lombok은 어노테이션만으로 이런 보일러플레이트 코드를 컴파일 시점에 자동 생성해주는 라이브러리입니다.&lt;/p&gt;
&lt;h3 id="lombok-없이-vs-있을-때"&gt;Lombok 없이 vs 있을 때&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Lombok 없이:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;User&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; Long id;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; String name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; String email;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;User&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;User&lt;/span&gt;(Long id, String name, String email) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;id&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; id;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;name&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; name;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;email&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; email;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; Long &lt;span style="color:#89b4fa"&gt;getId&lt;/span&gt;() { &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; id; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;void&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;setId&lt;/span&gt;(Long id) { &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;id&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; id; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; String &lt;span style="color:#89b4fa"&gt;getName&lt;/span&gt;() { &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; name; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;void&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;setName&lt;/span&gt;(String name) { &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;name&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; name; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; String &lt;span style="color:#89b4fa"&gt;getEmail&lt;/span&gt;() { &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; email; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;void&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;setEmail&lt;/span&gt;(String email) { &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;email&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; email; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#89b4fa;font-weight:bold"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; String &lt;span style="color:#89b4fa"&gt;toString&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;User{id=&amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; id &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;, name=&amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; name &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;, email=&amp;#34;&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; email &lt;span style="color:#89dceb;font-weight:bold"&gt;+&lt;/span&gt; &lt;span style="color:#a6e3a1"&gt;&amp;#34;}&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#89b4fa;font-weight:bold"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;boolean&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;equals&lt;/span&gt;(Object o) { &lt;span style="color:#6c7086;font-style:italic"&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#89b4fa;font-weight:bold"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;int&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;hashCode&lt;/span&gt;() { &lt;span style="color:#6c7086;font-style:italic"&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Lombok 사용:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Java 17+ 최신 문법 - Record, Sealed Class, Pattern Matching, Virtual Thread</title><link>https://korobopolly.github.io/posts/java-modern-features/</link><pubDate>Mon, 16 Feb 2026 13:10:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/java-modern-features/</guid><description>&lt;h2 id="개요"&gt;개요&lt;/h2&gt;
&lt;p&gt;Java는 6개월마다 새로운 버전을 출시하며 지속적으로 발전하고 있습니다. Java 17은 LTS(Long-Term Support) 버전으로, 이후 Java 21도 LTS로 지정되었습니다. 이 글에서는 Java 17 이상에서 도입된 주요 기능들을 실제 코드 예제와 함께 살펴보겠습니다.&lt;/p&gt;
&lt;h2 id="1-sealed-classes-봉인-클래스"&gt;1. Sealed Classes (봉인 클래스)&lt;/h2&gt;
&lt;p&gt;Sealed Classes는 Java 17에서 정식 기능으로 추가되었으며, 클래스 계층 구조를 명시적으로 제어할 수 있게 해줍니다. 어떤 클래스가 특정 클래스를 상속할 수 있는지를 제한함으로써 더 안전하고 예측 가능한 코드를 작성할 수 있습니다.&lt;/p&gt;
&lt;h3 id="기본-문법"&gt;기본 문법&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;sealed&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;Shape&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; permits Circle, Rectangle, Triangle {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 공통 메서드&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;Circle&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;extends&lt;/span&gt; Shape {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; radius;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;Circle&lt;/span&gt;(&lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; radius) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;radius&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; radius;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;area&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; Math.&lt;span style="color:#89b4fa"&gt;PI&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;*&lt;/span&gt; radius &lt;span style="color:#89dceb;font-weight:bold"&gt;*&lt;/span&gt; radius;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;Rectangle&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;extends&lt;/span&gt; Shape {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; width;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;private&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;final&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; height;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;Rectangle&lt;/span&gt;(&lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; width, &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; height) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;width&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; width;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;this&lt;/span&gt;.&lt;span style="color:#89b4fa"&gt;height&lt;/span&gt; &lt;span style="color:#89dceb;font-weight:bold"&gt;=&lt;/span&gt; height;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;double&lt;/span&gt; &lt;span style="color:#89b4fa"&gt;area&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#cba6f7"&gt;return&lt;/span&gt; width &lt;span style="color:#89dceb;font-weight:bold"&gt;*&lt;/span&gt; height;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f38ba8"&gt;public&lt;/span&gt; non&lt;span style="color:#89dceb;font-weight:bold"&gt;-&lt;/span&gt;&lt;span style="color:#f38ba8"&gt;sealed&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;class&lt;/span&gt; &lt;span style="color:#f9e2af"&gt;Triangle&lt;/span&gt; &lt;span style="color:#f38ba8"&gt;extends&lt;/span&gt; Shape {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6c7086;font-style:italic"&gt;// 다른 클래스가 Triangle을 상속할 수 있음&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="주요-특징"&gt;주요 특징&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sealed&lt;/code&gt; 키워드로 봉인 클래스 선언&lt;/li&gt;
&lt;li&gt;&lt;code&gt;permits&lt;/code&gt; 절로 허용할 하위 클래스 명시&lt;/li&gt;
&lt;li&gt;하위 클래스는 반드시 &lt;code&gt;final&lt;/code&gt;, &lt;code&gt;sealed&lt;/code&gt;, &lt;code&gt;non-sealed&lt;/code&gt; 중 하나로 선언&lt;/li&gt;
&lt;li&gt;도메인 모델링 시 타입 안전성 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-pattern-matching-패턴-매칭"&gt;2. Pattern Matching (패턴 매칭)&lt;/h2&gt;
&lt;h3 id="instanceof-패턴-매칭"&gt;instanceof 패턴 매칭&lt;/h3&gt;
&lt;p&gt;기존의 instanceof 검사 후 캐스팅하는 번거로운 과정을 간소화합니다.&lt;/p&gt;</description></item><item><title>OMC Team과 Pipeline - 멀티 에이전트 협업, 태스크 관리, 순차 체이닝</title><link>https://korobopolly.github.io/posts/omc-team-pipeline/</link><pubDate>Mon, 16 Feb 2026 13:09:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/omc-team-pipeline/</guid><description>&lt;h2 id="멀티-에이전트-협업의-필요성"&gt;멀티 에이전트 협업의 필요성&lt;/h2&gt;
&lt;p&gt;복잡한 소프트웨어 프로젝트는 단일 개발자가 모든 것을 처리하기 어렵습니다. 프론트엔드, 백엔드, 데이터베이스, 테스트, 배포 등 각 영역은 서로 다른 전문성을 요구합니다. OMC의 Team과 Pipeline 모드는 이러한 현실을 AI 에이전트 세계에 그대로 적용합니다.&lt;/p&gt;
&lt;p&gt;여러 전문 에이전트가 각자의 역할을 수행하고, 서로 통신하며, 의존성을 관리하고, 최종 목표를 향해 협업합니다. 마치 실제 개발팀처럼 작동하는 AI 팀을 구성할 수 있습니다.&lt;/p&gt;
&lt;h2 id="team-모드---조직화된-협업"&gt;Team 모드 - 조직화된 협업&lt;/h2&gt;
&lt;p&gt;Team 모드는 Claude Code의 네이티브 팀 기능을 활용하여 여러 에이전트를 조직화하고 협업시킵니다.&lt;/p&gt;</description></item><item><title>OMC Ecomode와 Ultrawork - 토큰 절약 모드, 병렬 처리 엔진</title><link>https://korobopolly.github.io/posts/omc-ecomode-ultrawork/</link><pubDate>Mon, 16 Feb 2026 13:08:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/omc-ecomode-ultrawork/</guid><description>&lt;h2 id="실행-모드의-중요성"&gt;실행 모드의 중요성&lt;/h2&gt;
&lt;p&gt;oh-my-claudecode(OMC)는 다양한 실행 모드를 제공하여 작업의 특성에 따라 최적의 전략을 선택할 수 있습니다. 그 중에서도 Ecomode와 Ultrawork는 서로 다른 목표를 가진 두 가지 핵심 모드입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ecomode&lt;/strong&gt;: 토큰 비용을 최소화하면서 합리적인 성능 유지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ultrawork&lt;/strong&gt;: 실행 속도를 최대화하기 위한 공격적인 병렬 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 두 모드를 이해하고 적절히 활용하면 비용과 성능 사이의 균형을 프로젝트 요구사항에 맞게 조정할 수 있습니다.&lt;/p&gt;
&lt;h2 id="ecomode-토큰-효율적-실행"&gt;Ecomode: 토큰 효율적 실행&lt;/h2&gt;
&lt;h3 id="ecomode란"&gt;Ecomode란?&lt;/h3&gt;
&lt;p&gt;Ecomode는 가능한 한 적은 토큰을 사용하면서도 작업을 완수하는 것을 목표로 하는 실행 모드입니다. Claude 모델의 계층 구조를 활용하여 작업의 복잡도에 따라 적절한 모델을 자동 선택합니다.&lt;/p&gt;</description></item><item><title>OMC Autopilot과 Ralph - 자동 실행 모드와 검증 루프</title><link>https://korobopolly.github.io/posts/omc-autopilot-ralph/</link><pubDate>Mon, 16 Feb 2026 13:07:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/omc-autopilot-ralph/</guid><description>&lt;h2 id="자동화된-개발의-시작"&gt;자동화된 개발의 시작&lt;/h2&gt;
&lt;p&gt;OMC의 가장 강력한 기능은 개발 과정 전체를 자동화하는 실행 모드들입니다. Autopilot은 아이디어에서 완성된 코드까지의 전체 여정을 자동으로 처리하고, Ralph는 작업이 완벽하게 완료될 때까지 반복 검증합니다.&lt;/p&gt;
&lt;p&gt;이 두 모드는 완전 자동화와 품질 보장이라는 각각의 강점을 가지고 있으며, 작업의 성격에 따라 적절히 선택하면 개발 생산성을 획기적으로 향상시킬 수 있습니다.&lt;/p&gt;
&lt;h2 id="autopilot---완전-자동-개발"&gt;Autopilot - 완전 자동 개발&lt;/h2&gt;
&lt;p&gt;Autopilot은 사용자의 요청을 받아 분석부터 구현, 테스트, 검증까지 전체 개발 사이클을 자동으로 수행합니다.&lt;/p&gt;
&lt;h3 id="기본-사용법"&gt;기본 사용법&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/autopilot &amp;#34;JWT 기반 사용자 인증 API를 만들어줘&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;단 한 줄의 명령으로 Autopilot은 다음 워크플로우를 자동 실행합니다.&lt;/p&gt;</description></item><item><title>oh-my-claudecode 소개 - 에이전트 카탈로그, 모델 라우팅, 상태 관리</title><link>https://korobopolly.github.io/posts/omc-introduction/</link><pubDate>Mon, 16 Feb 2026 13:06:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/omc-introduction/</guid><description>&lt;h2 id="omc란-무엇인가"&gt;OMC란 무엇인가&lt;/h2&gt;
&lt;p&gt;oh-my-claudecode(OMC)는 Claude Code를 위한 멀티 에이전트 오케스트레이션 레이어입니다. Claude Code의 기본 기능 위에서 동작하며, Hooks와 MCP(Model Context Protocol) 도구를 활용하여 복잡한 소프트웨어 개발 작업을 여러 전문화된 AI 에이전트들이 협업하여 처리할 수 있도록 합니다.&lt;/p&gt;
&lt;p&gt;단일 AI 에이전트가 모든 작업을 처리하는 대신, OMC는 각 작업의 특성에 맞는 전문 에이전트에게 업무를 위임합니다. 코드 탐색, 아키텍처 설계, 구현, 테스트, 검증, 리뷰 등 각 단계마다 최적화된 에이전트가 투입되어 작업의 품질과 효율성을 극대화합니다.&lt;/p&gt;
&lt;h2 id="quick-start"&gt;Quick Start&lt;/h2&gt;
&lt;h3 id="step-1-install"&gt;Step 1: Install&lt;/h3&gt;
&lt;p&gt;Claude Code 플러그인 마켓플레이스에서 설치합니다.&lt;/p&gt;</description></item><item><title>Claude Code Plan Mode - 워크플로우, 설계 계획 수립, 활용 팁</title><link>https://korobopolly.github.io/posts/claude-code-plan-mode/</link><pubDate>Mon, 16 Feb 2026 13:05:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/claude-code-plan-mode/</guid><description>&lt;h2 id="plan-mode란-무엇인가"&gt;Plan Mode란 무엇인가&lt;/h2&gt;
&lt;p&gt;Claude Code의 Plan Mode는 복잡한 코드 변경 작업을 시작하기 전에 체계적인 설계와 탐색을 수행할 수 있는 특별한 작업 모드입니다. EnterPlanMode 도구를 통해 진입하며, 이 모드에서는 코드를 직접 수정하지 않고 코드베이스를 탐색하고 설계 문서를 작성하는 데 집중합니다.&lt;/p&gt;
&lt;p&gt;Plan Mode의 핵심 철학은 &amp;quot;생각 먼저, 구현은 나중에&amp;quot;입니다. 특히 멀티파일 변경이나 아키텍처 결정이 필요한 상황에서 성급한 코드 수정으로 인한 시행착오를 크게 줄일 수 있습니다.&lt;/p&gt;
&lt;h2 id="plan-mode를-사용해야-하는-시나리오"&gt;Plan Mode를 사용해야 하는 시나리오&lt;/h2&gt;
&lt;p&gt;Plan Mode는 모든 작업에 필요한 것은 아닙니다. 다음과 같은 경우에 특히 유용합니다.&lt;/p&gt;</description></item><item><title>Claude Code MCP 서버 - 설정, 주요 서버 소개, 커스텀 서버 구축</title><link>https://korobopolly.github.io/posts/claude-code-mcp-servers/</link><pubDate>Mon, 16 Feb 2026 13:04:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/claude-code-mcp-servers/</guid><description>&lt;h2 id="mcp란-무엇인가"&gt;MCP란 무엇인가&lt;/h2&gt;
&lt;p&gt;MCP(Model Context Protocol)는 AI 모델과 외부 도구를 연결하는 표준 프로토콜입니다. Claude Code는 MCP를 통해 파일 시스템, 데이터베이스, 웹 API, 문서 검색 엔진 등 다양한 외부 서비스와 통합할 수 있습니다.&lt;/p&gt;
&lt;p&gt;기본적으로 Claude Code는 코드 읽기, 쓰기, 실행 등의 기능을 제공하지만, MCP 서버를 추가하면 이러한 능력을 무한히 확장할 수 있습니다. 예를 들어, Context7 MCP 서버를 연결하면 최신 라이브러리 문서를 실시간으로 검색할 수 있고, Exa MCP 서버를 통해 웹 검색 결과를 활용할 수 있습니다.&lt;/p&gt;</description></item><item><title>Claude Code Hooks - 이벤트 타입, 자동 린팅, 조건부 실행 설정</title><link>https://korobopolly.github.io/posts/claude-code-hooks-automation/</link><pubDate>Mon, 16 Feb 2026 13:03:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/claude-code-hooks-automation/</guid><description>&lt;h2 id="hooks-시스템-개요"&gt;Hooks 시스템 개요&lt;/h2&gt;
&lt;p&gt;Claude Code의 Hooks는 도구 실행의 특정 시점에 자동으로 쉘 명령을 실행할 수 있는 자동화 메커니즘입니다. 파일을 편집할 때마다 자동으로 린팅을 실행하거나, 세션 시작 시 프로젝트 컨텍스트를 로딩하거나, 특정 도구 호출 후 알림을 보내는 등 다양한 자동화가 가능합니다.&lt;/p&gt;
&lt;p&gt;Hooks는 개발 워크플로우를 크게 개선할 수 있습니다. 반복적인 작업을 자동화하고, 코드 품질을 즉각적으로 검증하며, 작업 흐름을 방해하지 않으면서도 필요한 체크를 수행할 수 있습니다.&lt;/p&gt;
&lt;h2 id="hook-이벤트-타입"&gt;Hook 이벤트 타입&lt;/h2&gt;
&lt;p&gt;Claude Code는 네 가지 주요 이벤트 타입을 제공합니다.&lt;/p&gt;</description></item><item><title>Claude Code 커스텀 슬래시 커맨드 - 커맨드 파일 구조와 실전 예제</title><link>https://korobopolly.github.io/posts/claude-code-custom-slash-commands/</link><pubDate>Mon, 16 Feb 2026 13:02:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/claude-code-custom-slash-commands/</guid><description>&lt;h2 id="슬래시-커맨드란"&gt;슬래시 커맨드란?&lt;/h2&gt;
&lt;p&gt;Claude Code에서 슬래시 커맨드(Slash Command)는 &lt;code&gt;/commit&lt;/code&gt;, &lt;code&gt;/plan&lt;/code&gt;, &lt;code&gt;/review&lt;/code&gt;처럼 &lt;code&gt;/&lt;/code&gt;로 시작하는 단축 명령어입니다. 이 커맨드들은 복잡한 작업 지시를 간단한 한 줄로 실행할 수 있게 해주며, 반복적인 작업을 자동화하는 강력한 도구입니다.&lt;/p&gt;
&lt;p&gt;내장 커맨드 외에도 프로젝트별로 커스텀 슬래시 커맨드를 정의하여 팀의 워크플로우에 맞는 자동화를 구축할 수 있습니다.&lt;/p&gt;
&lt;h2 id="커스텀-커맨드의-필요성"&gt;커스텀 커맨드의 필요성&lt;/h2&gt;
&lt;h3 id="반복-작업의-문제"&gt;반복 작업의 문제&lt;/h3&gt;
&lt;p&gt;다음과 같은 상황을 생각해봅시다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;새 블로그 포스트를 만들어줘. 제목은 [title]이고, 날짜는 오늘,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;태그는 [tags], 시리즈는 [series]로 설정하고,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/content/posts/ 디렉토리에 slug 형식으로 파일명 만들고,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;프론트매터는 기존 포스트 형식 따라서...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이런 지시를 매번 반복하는 것은 비효율적입니다. 커스텀 커맨드를 만들면:&lt;/p&gt;</description></item><item><title>CLAUDE.md - 프로젝트 규칙, 코딩 컨벤션, 워크플로우 설정</title><link>https://korobopolly.github.io/posts/claude-code-claude-md/</link><pubDate>Mon, 16 Feb 2026 13:01:00 +0900</pubDate><guid>https://korobopolly.github.io/posts/claude-code-claude-md/</guid><description>&lt;h2 id="claudemd란"&gt;CLAUDE.md란?&lt;/h2&gt;
&lt;p&gt;CLAUDE.md는 Claude Code에게 프로젝트별 컨텍스트와 작업 지침을 전달하는 특수한 마크다운 파일입니다. 이 파일을 프로젝트 루트에 두면 Claude Code가 세션 시작 시 자동으로 읽어들여 프로젝트의 규칙, 코딩 스타일, 아키텍처 결정사항 등을 이해하고 작업에 반영합니다.&lt;/p&gt;
&lt;p&gt;전역 설정 파일인 &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;와 달리, 프로젝트 루트의 &lt;code&gt;CLAUDE.md&lt;/code&gt;는 해당 프로젝트에만 적용되며 팀원 간 공유가 가능하여 일관된 개발 경험을 제공합니다.&lt;/p&gt;
&lt;h2 id="claudemd의-주요-역할"&gt;CLAUDE.md의 주요 역할&lt;/h2&gt;
&lt;h3 id="1-프로젝트-컨텍스트-제공"&gt;1. 프로젝트 컨텍스트 제공&lt;/h3&gt;
&lt;p&gt;Claude Code에게 프로젝트의 목적, 기술 스택, 디렉토리 구조 등을 명시적으로 알려줄 수 있습니다.&lt;/p&gt;</description></item><item><title>소개</title><link>https://korobopolly.github.io/about/</link><pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate><guid>https://korobopolly.github.io/about/</guid><description>&lt;h2 id="이-블로그에-대해"&gt;이 블로그에 대해&lt;/h2&gt;
&lt;p&gt;실전 개발에 필요한 기술을 정리하는 기술 블로그입니다. AI 도구 활용법부터 백엔드, 프론트엔드까지 실무에서 바로 쓸 수 있는 내용을 다룹니다.&lt;/p&gt;
&lt;h3 id="다루는-주제"&gt;다루는 주제&lt;/h3&gt;
&lt;h4 id="ai-개발-도구"&gt;AI 개발 도구&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;: Plan Mode, MCP 서버, Hooks 자동화, 컨텍스트 관리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;oh-my-claudecode(OMC)&lt;/strong&gt;: 멀티 에이전트 오케스트레이션, Autopilot, Team 모드&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="백엔드"&gt;백엔드&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java&lt;/strong&gt;: 최신 기능 (Sealed Classes, Records, Virtual Threads), Stream API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring Boot&lt;/strong&gt;: 프로젝트 설정, REST API, JPA 연동&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="프론트엔드"&gt;프론트엔드&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React&lt;/strong&gt;: 컴포넌트, JSX, Hooks, 상태 관리, 커스텀 훅&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="대상-독자"&gt;대상 독자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;실전 개발 기술을 체계적으로 정리하고 싶은 개발자&lt;/li&gt;
&lt;li&gt;AI 코딩 도구를 활용한 생산성 향상에 관심이 있는 분&lt;/li&gt;
&lt;li&gt;Java, Spring Boot, React를 배우거나 복습하고 싶은 분&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="연락처"&gt;연락처&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/korobopolly"&gt;korobopolly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>