<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>leoOH</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 16 Dec 2025 13:19:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>leoOH</title>
            <url>https://velog.velcdn.com/images/j_blin/profile/f7770374-acd8-4ba7-af51-b43be2ce7f20/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. leoOH. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/j_blin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[데이터 페이스 정규화 정리 ]]></title>
            <link>https://velog.io/@j_blin/%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%95%EA%B7%9C%ED%99%94-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@j_blin/%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%95%EA%B7%9C%ED%99%94-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 16 Dec 2025 13:19:57 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스-정규화1nf3nf">데이터베이스 정규화(1NF~3NF)</h1>
<hr>
<h2 id="1-정규형-개념-1nf--3nf">1) 정규형 개념 (1NF ~ 3NF)</h2>
<ul>
<li><strong>1NF</strong>(제1 정규형) : 모든 컬럼 값은 <strong>원자값</strong>(하나의 값)이어야 한다. <ul>
<li><strong>2NF</strong>(제2 정규형) : 1NF + <strong>부분 함수 종속 제거</strong></li>
<li><strong>3NF</strong>(제3 정규형) : 2NF + <strong>이행적 종속 제거</strong> </li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>함수 종속</strong> : <code>X → Y</code>는 “X가 정해지면 Y가 하나로 정해진다”는 의미</p>
</blockquote>
<hr>
<h2 id="2--주문-테이블을-1nf-→-2nf-→-3nf로-분해">2)  “주문” 테이블을 1NF → 2NF → 3NF로 분해</h2>
<h3 id="2-1-비정규형unf-예시">2-1. 비정규형(UNF) 예시</h3>
<p><strong>UNF_주문</strong> (문제: 한 칸에 상품목록이 여러 개 들어감)</p>
<table>
<thead>
<tr>
<th>주문ID</th>
<th>고객ID</th>
<th>고객명</th>
<th>고객주소</th>
<th>상품목록(상품ID:수량)</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>C10</td>
<td>홍길동</td>
<td>서울</td>
<td>P1:2, P2:1</td>
</tr>
<tr>
<td>O101</td>
<td>C11</td>
<td>김영희</td>
<td>부산</td>
<td>P2:3</td>
</tr>
</tbody></table>
<p><strong>문제점</strong></p>
<ul>
<li><code>상품목록</code>이 <strong>다중 값</strong>(리스트) → 1NF 위반</li>
<li>특정 상품 수량만 수정/삭제하기가 까다로움</li>
</ul>
<hr>
<h3 id="2-2-1nf-적용-원자값으로-행을-풀기">2-2. 1NF 적용 (원자값으로 “행을 풀기”)</h3>
<p>상품목록을 행으로 풀어 <strong>주문-상품 라인 아이템</strong> 형태로 만든다.</p>
<p><strong>1NF_주문라인(임시)</strong></p>
<table>
<thead>
<tr>
<th>주문ID</th>
<th>상품ID</th>
<th align="right">수량</th>
<th>고객ID</th>
<th>고객명</th>
<th>고객주소</th>
<th>상품명</th>
<th align="right">상품가격</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>P1</td>
<td align="right">2</td>
<td>C10</td>
<td>홍길동</td>
<td>서울</td>
<td>아메리카노</td>
<td align="right">3000</td>
</tr>
<tr>
<td>O100</td>
<td>P2</td>
<td align="right">1</td>
<td>C10</td>
<td>홍길동</td>
<td>서울</td>
<td>베이글</td>
<td align="right">2500</td>
</tr>
<tr>
<td>O101</td>
<td>P2</td>
<td align="right">3</td>
<td>C11</td>
<td>김영희</td>
<td>부산</td>
<td>베이글</td>
<td align="right">2500</td>
</tr>
</tbody></table>
<ul>
<li>각 컬럼은 원자값 → <strong>1NF 만족</strong></li>
<li>하지만 아직 중복이 많고(고객명/주소, 상품명/가격 반복) 규칙(함수 종속)에 어긋나는 구조가 남아 있다.</li>
</ul>
<p><strong>키(Primary Key) 가정</strong></p>
<ul>
<li>이 테이블의 PK를 <code>(주문ID, 상품ID)</code>로 잡으면 “한 주문에서 같은 상품은 한 번만 등장”한다고 가정할 수 있다.</li>
</ul>
<hr>
<h3 id="2-3-2nf-적용-부분-함수-종속-제거">2-3. 2NF 적용 (부분 함수 종속 제거)</h3>
<h4 id="1-현재-함수-종속fd-예시">(1) 현재 함수 종속(FD) 예시</h4>
<ul>
<li><code>(주문ID, 상품ID) -&gt; 수량</code></li>
<li><code>주문ID → 고객ID, 고객명, 고객주소</code>   <strong>주문ID(키 일부)에만 의존</strong></li>
<li><code>상품ID → 상품명, 상품가격</code>  <strong>상품ID(키 일부)에만 의존</strong></li>
</ul>
<p>즉 PK가 복합키 <code>(주문ID, 상품ID)</code>인데,</p>
<ul>
<li>고객 관련 컬럼은 <code>주문ID</code>만으로 결정</li>
<li>상품 관련 컬럼은 <code>상품ID</code>만으로 결정<br>→ <strong>부분 함수 종속</strong>이므로 2NF 위반</li>
</ul>
<h4 id="2-2nf-분해-결과">(2) 2NF 분해 결과</h4>
<p>부분 종속되는 속성을 각각의 테이블로 분리한다.</p>
<p><strong>(A) Orders_2NF (주문)</strong></p>
<table>
<thead>
<tr>
<th>주문ID (PK)</th>
<th>고객ID</th>
<th>고객명</th>
<th>고객주소</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>C10</td>
<td>홍길동</td>
<td>서울</td>
</tr>
<tr>
<td>O101</td>
<td>C11</td>
<td>김영희</td>
<td>부산</td>
</tr>
</tbody></table>
<p><strong>(B) Products_2NF (상품)</strong></p>
<table>
<thead>
<tr>
<th>상품ID (PK)</th>
<th>상품명</th>
<th align="right">상품가격</th>
</tr>
</thead>
<tbody><tr>
<td>P1</td>
<td>아메리카노</td>
<td align="right">3000</td>
</tr>
<tr>
<td>P2</td>
<td>베이글</td>
<td align="right">2500</td>
</tr>
</tbody></table>
<p><strong>(C) OrderItems_2NF (주문 상세)</strong></p>
<table>
<thead>
<tr>
<th>주문ID (PK, FK)</th>
<th>상품ID (PK, FK)</th>
<th align="right">수량</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>P1</td>
<td align="right">2</td>
</tr>
<tr>
<td>O100</td>
<td>P2</td>
<td align="right">1</td>
</tr>
<tr>
<td>O101</td>
<td>P2</td>
<td align="right">3</td>
</tr>
</tbody></table>
<p> 이제 <code>상품명/가격</code>은 Products로, <code>고객명/주소</code>는 Orders로 이동하여 <strong>2NF 만족</strong></p>
<hr>
<h3 id="2-4-3nf-적용-이행적-종속-제거">2-4. 3NF 적용 (이행적 종속 제거)</h3>
<p>2NF까지 끝냈는데도 <code>Orders_2NF</code> 안을 보면:</p>
<ul>
<li><code>주문ID → 고객ID</code></li>
<li><code>고객ID → 고객명, 고객주소</code> (고객ID가 고객 정보를 결정)</li>
<li>결과적으로 <code>주문ID → 고객명, 고객주소</code>가 <strong>이행적(간접)으로</strong> 성립</li>
</ul>
<p>즉 <code>주문ID → 고객ID → 고객명/주소</code> 구조가 남아 있어 <strong>3NF 위반</strong></p>
<h4 id="3nf-분해-결과">3NF 분해 결과</h4>
<p>고객 정보를 별도 테이블로 분리하고, 주문은 고객ID만 참조하도록 만듭니다.</p>
<p><strong>(A) Customers_3NF (고객)</strong></p>
<table>
<thead>
<tr>
<th>고객ID (PK)</th>
<th>고객명</th>
<th>고객주소</th>
</tr>
</thead>
<tbody><tr>
<td>C10</td>
<td>홍길동</td>
<td>서울</td>
</tr>
<tr>
<td>C11</td>
<td>김영희</td>
<td>부산</td>
</tr>
</tbody></table>
<p><strong>(B) Orders_3NF (주문)</strong></p>
<table>
<thead>
<tr>
<th>주문ID (PK)</th>
<th>고객ID (FK)</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>C10</td>
</tr>
<tr>
<td>O101</td>
<td>C11</td>
</tr>
</tbody></table>
<p><strong>(C) Products_3NF (상품)</strong> <em>(2NF와 동일)</em></p>
<table>
<thead>
<tr>
<th>상품ID (PK)</th>
<th>상품명</th>
<th align="right">상품가격</th>
</tr>
</thead>
<tbody><tr>
<td>P1</td>
<td>아메리카노</td>
<td align="right">3000</td>
</tr>
<tr>
<td>P2</td>
<td>베이글</td>
<td align="right">2500</td>
</tr>
</tbody></table>
<p><strong>(D) OrderItems_3NF (주문 상세)</strong> <em>(2NF와 동일)</em></p>
<table>
<thead>
<tr>
<th>주문ID (PK, FK)</th>
<th>상품ID (PK, FK)</th>
<th align="right">수량</th>
</tr>
</thead>
<tbody><tr>
<td>O100</td>
<td>P1</td>
<td align="right">2</td>
</tr>
<tr>
<td>O100</td>
<td>P2</td>
<td align="right">1</td>
</tr>
<tr>
<td>O101</td>
<td>P2</td>
<td align="right">3</td>
</tr>
</tbody></table>
<p> 이제 주문 테이블에는 “주문 자체”의 속성만 남고, 고객/상품 정보는 각 마스터 테이블에만 존재 → <strong>3NF 만족</strong>한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub Issues 작성 가이드]]></title>
            <link>https://velog.io/@j_blin/GitHub-Issues-%EC%9E%91%EC%84%B1-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@j_blin/GitHub-Issues-%EC%9E%91%EC%84%B1-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Thu, 11 Dec 2025 14:16:51 GMT</pubDate>
            <description><![CDATA[<p>GitHub Issues는 프로젝트의 작업을 체계적으로 관리하고 협업 효율을 높여주는 핵심 도구이다.<br>이 문서는 <strong>명확하고 재사용 가능한 이슈 템플릿</strong>을 작성하는 방법을 정리한 가이드이다.
계획대로 진행되지 않는 것이 개발이기 때문에 자세한 내용은 PR에 작성하고 
이슈에서는 간략하게 적는 것이 좋다. </p>
<hr>
<h2 id="1-이슈-작성-목적">1. 이슈 작성 목적</h2>
<p>GitHub Issues는 다음과 같은 상황에서 사용된다.</p>
<ul>
<li>새로운 기능(Feature) 구현</li>
<li>버그(Bug) 리포트 및 해결</li>
<li>리팩토링, 테스트 코드, 문서화 작업 요청</li>
<li>작업을 세분화하여 프로젝트 관리 효율 극대화</li>
<li>협업 시 작업 중복 방지 및 진행 상태 공유</li>
</ul>
<hr>
<h2 id="2-이슈-기본-구성-요소">2. 이슈 기본 구성 요소</h2>
<h3 id="✔-1-이슈-제목title">✔ 1) 이슈 제목(Title)</h3>
<ul>
<li>간결하게 핵심을 담아쓰기  </li>
<li><code>[Feature] WebSocket 메시지 읽음 처리</code></li>
<li><code>[Bug] JWT Refresh Token 검증 실패</code></li>
<li><code>[Refactor] MemberService 비밀번호 검증 로직 개선</code> 등등 </li>
</ul>
<hr>
<h3 id="✔-2-라벨label">✔ 2) 라벨(Label)</h3>
<p>이슈의 성격을 빠르게 파악하기 위해 라벨을 적용합니다.</p>
<p>추천 라벨:</p>
<ul>
<li><code>feature</code></li>
<li><code>bug</code></li>
<li><code>refactor</code></li>
<li><code>docs</code></li>
<li><code>test</code></li>
<li><code>hotfix</code></li>
<li><code>enhancement</code></li>
</ul>
<hr>
<h3 id="✔-3-본문body">✔ 3) 본문(Body)</h3>
<p>이슈 설명은 아래 4가지를 기본으로 구성합니다.</p>
<hr>
<h4 id="1-summary--이슈-개요">(1) Summary — 이슈 개요</h4>
<p>이 이슈가 무엇을 위한 것인지 한 문장으로 설명합니다.</p>
<pre><code>WebSocket 기반 채팅에서 메시지 읽음 처리 기능을 구현한다.</code></pre><hr>
<h4 id="2-detail--상세-설명">(2) Detail — 상세 설명</h4>
<p>현재 상태와 해결해야 할 목표 상태를 구체적으로 작성합니다.</p>
<pre><code>현재 메시지를 읽어도 읽음 상태가 서버에 저장되지 않아 다른 사용자에게 반영되지 않는다.
채팅방별로 유저의 lastReadMessageId 값을 저장하고 이를 기반으로 읽음 처리를 구현해야 한다.</code></pre><hr>
<h4 id="3-acceptance-criteria--완료-조건">(3) Acceptance Criteria — 완료 조건</h4>
<p><strong>개발이 끝났음을 판단할 수 있는 체크리스트</strong>를 작성합니다.</p>
<pre><code>- [ ] 사용자가 채팅방을 구독하면 마지막 메시지 ID를 전달한다.
- [ ] 서버는 lastReadMessageId 계산 후 DB 또는 캐시에 저장한다.
- [ ] 읽음 상태를 WebSocket 이벤트로 전달한다.
- [ ] 명세서(ws-api.md)에 읽음 처리 플로우가 추가된다.</code></pre><hr>
<h4 id="4-reference--참고-자료">(4) Reference — 참고 자료</h4>
<p>관련 문서, ERD, API 명세, PR 링크 등을 포함하면 큰 도움이 됩니다.</p>
<pre><code>- ERD: /docs/chat_erd.png
- API 명세서: /docs/websocket-api.md
- 관련 PR: #23</code></pre><hr>
<h2 id="3-이슈-세분화-기준">3. 이슈 세분화 기준</h2>
<p>큰 기능 단위는 반드시 작은 이슈들로 나누는 것이 좋습니다.<br>기능을 잘게 쪼개면 아래 장점이 있습니다:</p>
<ul>
<li>PR 크기가 작아져 리뷰가 쉬움  </li>
<li>트러블슈팅이 쉬워짐  </li>
<li>작업 책임 범위가 명확해짐  </li>
<li>일정 관리 가능 (칸반 보드에서 트래킹하기 쉬움)</li>
</ul>
<hr>
<h3 id="예-채팅-서비스-기능-세분화">예: 채팅 서비스 기능 세분화</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>나눌 수 있는 세부 이슈</th>
</tr>
</thead>
<tbody><tr>
<td>WebSocket 연결</td>
<td>WebSocket 설정, 핸드셰이크 인증 처리</td>
</tr>
<tr>
<td>채팅방 구독</td>
<td>구독 이벤트 처리, 유저-방 매핑 구조</td>
</tr>
<tr>
<td>메시지 전송</td>
<td>DTO 정의, 메시지 저장, 메시지 브로드캐스트</td>
</tr>
<tr>
<td>메시지 읽음 처리</td>
<td>lastReadMessageId 계산, 저장 로직, 읽음 이벤트 송신</td>
</tr>
<tr>
<td>채팅방 종료</td>
<td>세션 종료 처리, 사용자 상태 업데이트</td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Polling, WebSocket, SSE]]></title>
            <link>https://velog.io/@j_blin/Polling-WebSocket-SSE</link>
            <guid>https://velog.io/@j_blin/Polling-WebSocket-SSE</guid>
            <pubDate>Tue, 09 Dec 2025 14:03:49 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="polling-short-polling">Polling (Short Polling)</h2>
<p>HTTP 기반 가장 단순한 실시간 통신 방식</p>
<h3 id="작동-방식">작동 방식</h3>
<p>클라이언트가 <strong>일정 주기마다 반복적으로 HTTP 요청</strong>을 보냄<br>→ &quot;새 데이터 있어?&quot; → 서버는 즉시 응답</p>
<h3 id="장점">장점</h3>
<ul>
<li>구현이 매우 쉽다 (타이머 사용)</li>
<li>별도 서버 설정 없음 — 기존 HTTP로 끝</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li><strong>데이터가 없어도 계속 요청 → 트래픽 낭비</strong></li>
<li>실시간성이 떨어짐<br>(데이터가 0.1초 전에 생겨도 다음 요청 때까진 모름)</li>
</ul>
<hr>
<h2 id="long-polling">Long Polling</h2>
<p>Polling의 단점을 보완한 방식</p>
<h3 id="작동-방식-1">작동 방식</h3>
<ol>
<li>클라이언트가 HTTP 요청을 보냄  </li>
<li><strong>서버는 데이터가 생길 때까지 연결 유지</strong>  </li>
<li>데이터 발생 시 응답 후 연결 종료  </li>
<li>클라이언트는 다시 요청 보내며 반복</li>
</ol>
<h3 id="장점-1">장점</h3>
<ul>
<li>Short Polling보다 훨씬 효율적</li>
<li>실시간성과 비슷한 수준으로 동작</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>데이터가 너무 자주 발생하면<br><strong>연결을 계속 끊고 새로 맺어야 하므로 비효율</strong></li>
<li>완전한 실시간 스트리밍 수준은 아님</li>
</ul>
<hr>
<h2 id="sse-server-sent-events">SSE (Server-Sent Events)</h2>
<p>HTTP 기반 <strong>실시간 단방향(Server → Client)</strong> 스트리밍</p>
<h3 id="작동-방식-2">작동 방식</h3>
<ol>
<li>클라이언트가 서버에 “구독 요청(EventSource)”  </li>
<li>서버가 연결을 유지한 채 <strong>필요할 때마다 이벤트 Push</strong></li>
<li>클라이언트는 <strong>받기만</strong> 가능 (단방향)</li>
<li>서버로 데이터 보내려면 일반 HTTP 요청 사용</li>
</ol>
<h3 id="장점-2">장점</h3>
<ul>
<li>WebSocket보다 <strong>구현이 간단</strong></li>
<li>자동 재연결, 이벤트 스트림 기반</li>
<li>HTTP 인프라 그대로 사용 가능 (프록시, 로드밸런서 친화적)</li>
<li>단방향 실시간에 매우 적합</li>
</ul>
<h3 id="단점-2">단점</h3>
<ul>
<li>클라이언트 → 서버 방향은 지원 X</li>
<li>다량의 양방향 실시간 통신에는 부적합</li>
</ul>
<h3 id="언제-쓰면-좋을까">언제 쓰면 좋을까?</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>서버에서 일방적으로 알려주기만 하면 됨</td>
<td>SNS 알림</td>
</tr>
<tr>
<td>실시간 점수 중계</td>
<td>스포츠</td>
</tr>
<tr>
<td>잦은 가격 갱신</td>
<td>주식/코인 시세</td>
</tr>
<tr>
<td>실시간 상태 모니터링</td>
<td>IoT 센서</td>
</tr>
</tbody></table>
<hr>
<h2 id="websocket">WebSocket</h2>
<p><strong>클라이언트 ↔ 서버 간 양방향 실시간 통신</strong></p>
<h3 id="작동-방식-3">작동 방식</h3>
<ul>
<li>최초 한번 HTTP 기반 Handshake</li>
<li>이후 HTTP가 아닌 WebSocket 프로토콜로 전환</li>
<li><strong>양방향/연결 유지 상태에서 바로 전송</strong></li>
</ul>
<h3 id="장점-3">장점</h3>
<ul>
<li><strong>완전한 실시간</strong></li>
<li>양방향 통신</li>
<li>메시지 헤더가 가벼워 효율적</li>
</ul>
<h3 id="단점-3">단점</h3>
<ul>
<li>서버 구현 난이도 ↑</li>
<li>상태를 유지해야 하므로 관리 비용 ↑</li>
<li>프록시/로드밸런서 환경에서 설정 필요</li>
</ul>
<hr>
<h2 id="기술-비교-요약표">기술 비교 요약표</h2>
<table>
<thead>
<tr>
<th>기술</th>
<th>통신 방향</th>
<th>연결 지속</th>
<th>실시간성</th>
<th>사용 예</th>
</tr>
</thead>
<tbody><tr>
<td>Short Polling</td>
<td>Client → Server</td>
<td>❌</td>
<td>느림</td>
<td>간단한 반복 업데이트</td>
</tr>
<tr>
<td>Long Polling</td>
<td>Client → Server</td>
<td>⏳ (대기 후 종료)</td>
<td>중간</td>
<td>채팅 알림</td>
</tr>
<tr>
<td>SSE</td>
<td>Server → Client</td>
<td>⭕ 유지</td>
<td>좋음</td>
<td>알림 / 시세 / 모니터링</td>
</tr>
<tr>
<td>WebSocket</td>
<td>양방향</td>
<td>⭕ 유지</td>
<td>매우 높음</td>
<td>실시간 채팅, 게임, 콜</td>
</tr>
</tbody></table>
<hr>
<h2 id="어떤-상황에서-무엇을-사용할까">어떤 상황에서 무엇을 사용할까?</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>추천</th>
</tr>
</thead>
<tbody><tr>
<td><strong>매우 가벼운 요청, 구현 쉬움</strong>이 우선</td>
<td>Short Polling</td>
</tr>
<tr>
<td><strong>실시간 + HTTP 기반 + 단방향이면 충분</strong></td>
<td>SSE</td>
</tr>
<tr>
<td><strong>양방향 대화형 서비스 (채팅, 게임, 협업툴)</strong></td>
<td>WebSocket</td>
</tr>
<tr>
<td><strong>실시간처럼 보이면 되지만 부하는 줄이고 싶음</strong></td>
<td>Long Polling</td>
</tr>
</tbody></table>
<hr>
<h2 id="결론">결론</h2>
<table>
<thead>
<tr>
<th>요구사항</th>
<th>해결책</th>
</tr>
</thead>
<tbody><tr>
<td>데이터가 자주 변하지 않음</td>
<td>Polling</td>
</tr>
<tr>
<td>실시간이 필요하지만 단방향</td>
<td>SSE</td>
</tr>
<tr>
<td>양방향이 반드시 필요</td>
<td>WebSocket</td>
</tr>
<tr>
<td>간단한 실시간 구현</td>
<td>Long Polling</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[WebSocket과 HTTP 차이점 ]]></title>
            <link>https://velog.io/@j_blin/WebSocket%EA%B3%BC-HTTP-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@j_blin/WebSocket%EA%B3%BC-HTTP-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Mon, 08 Dec 2025 13:05:09 GMT</pubDate>
            <description><![CDATA[<h1 id="websocket--tcp--핵심-기술-정리">WebSocket &amp; TCP — 핵심 기술 정리</h1>
<h2 id="실시간-처리의-핵심-양방향-통신">실시간 처리의 핵심: 양방향 통신</h2>
<p>웹소켓(WebSocket)은 <strong>서버와 클라이언트가 동시에 데이터를 주고받을 수 있는 기술</strong>입니다.<br>전화 통화 방식과 매우 비슷한 구조를 가집니다.</p>
<hr>
<h2 id="전화-통화에-비유한-실시간-통신">전화 통화에 비유한 실시간 통신</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>연결(Handshake)</td>
<td>전화를 거는 과정 (&quot;여보세요? 들리세요?&quot;)</td>
</tr>
<tr>
<td>유지</td>
<td>연결된 상태를 계속 유지</td>
</tr>
<tr>
<td>자유로운 대화</td>
<td>요청 없이도 서로 말을 건넬 수 있음</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>한 번 연결되면 끊기 전까지 계속 유지되고, 양방향으로 실시간 통신이 가능하다.</strong></p>
</blockquote>
<hr>
<h2 id="websocketws-프로토콜이란">WebSocket(WS) 프로토콜이란?</h2>
<p><strong>WebSocket은 기존 HTTP 요청을 기반으로 프로토콜을 업그레이드하여 실시간 양방향 통신을 수행하는 기술입니다.</strong><br>HTTP를 대체하는 것이 아니라 <strong>HTTP에서 출발하여 WebSocket으로 변경(Upgrade)</strong> 되는 개념입니다.</p>
<h3 id="websocket-url">WebSocket URL</h3>
<table>
<thead>
<tr>
<th>프로토콜</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>ws://</code></td>
<td>기본 WebSocket 연결</td>
</tr>
<tr>
<td><code>wss://</code></td>
<td>HTTPS처럼 SSL 보안 적용된 WebSocket</td>
</tr>
</tbody></table>
<hr>
<h2 id="websocket-작동-원리">WebSocket 작동 원리</h2>
<h3 id="1-handshake-http-업그레이드-요청">1. Handshake (HTTP 업그레이드 요청)</h3>
<p>초기 요청은 HTTP GET 으로 전송됩니다.</p>
<p>📌 Client</p>
<pre><code>GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: (랜덤 키)
Sec-WebSocket-Version: 13</code></pre><p>📌 Server</p>
<pre><code>HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: (계산된 응답 키)</code></pre><p>➡ 의미:<br><strong>&quot;HTTP로 만나서 WebSocket으로 대화하자&quot;</strong> 라는 합의 완료.</p>
<hr>
<h3 id="2-프로토콜-전환-connection-open">2. 프로토콜 전환 (Connection Open)</h3>
<blockquote>
<p>HTTP → WebSocket 으로 변경되고 연결이 유지됩니다.</p>
</blockquote>
<p>🔹 HTTP  </p>
<ul>
<li>요청 후 응답하면 연결 종료</li>
</ul>
<p>🔹 WebSocket  </p>
<ul>
<li>연결을 유지하고, 끊기 전까지 계속 사용 (Stateful)</li>
</ul>
<hr>
<h3 id="3-양방향full-duplex-실시간-통신">3. 양방향(Full Duplex) 실시간 통신</h3>
<table>
<thead>
<tr>
<th>HTTP Polling</th>
<th>WebSocket</th>
</tr>
</thead>
<tbody><tr>
<td>계속 요청 필요</td>
<td>요청 없이 push 가능</td>
</tr>
<tr>
<td>지연 발생 가능</td>
<td>지연 최소화</td>
</tr>
<tr>
<td>불필요한 트래픽</td>
<td>효율적</td>
</tr>
<tr>
<td>서버 먼저 전송 불가</td>
<td>서버 → 클라이언트 가능</td>
</tr>
</tbody></table>
<p>📌 주요 사용 사례</p>
<ul>
<li>채팅 시스템</li>
<li>실시간 알림</li>
<li>온라인 게임</li>
<li>주식/코인 시세 전송</li>
<li>실시간 지도/위치 공유</li>
</ul>
<hr>
<h2 id="tcp란-무엇인가">TCP란 무엇인가?</h2>
<p>WebSocket은 <strong>TCP 위에서 동작</strong>합니다.<br>TCP는 인터넷의 <strong>안전하고 정확한 배송 서비스</strong>라고 할 수 있습니다.</p>
<h3 id="tcp의-3가지-핵심-특징">TCP의 3가지 핵심 특징</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>신뢰성</td>
<td>패킷이 제대로 갔는지 ACK 확인</td>
</tr>
<tr>
<td>순서 보장</td>
<td>순서가 바뀌어도 원래대로 정렬</td>
</tr>
<tr>
<td>연결 지향</td>
<td>통신 전 반드시 연결 수립</td>
</tr>
</tbody></table>
<hr>
<h2 id="tcp-연결-과정--3-way-handshake">TCP 연결 과정 — 3-Way Handshake</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>메시지</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>SYN</td>
<td>접속 요청</td>
</tr>
<tr>
<td>2</td>
<td>SYN + ACK</td>
<td>요청 수락 + 서버도 요청</td>
</tr>
<tr>
<td>3</td>
<td>ACK</td>
<td>최종 수락</td>
</tr>
</tbody></table>
<p>➡ 이후 데이터 전송이 시작됩니다.</p>
<hr>
<h2 id="websocket과-tcp의-관계">WebSocket과 TCP의 관계</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>WebSocket</th>
<th>TCP</th>
</tr>
</thead>
<tbody><tr>
<td>레이어</td>
<td>응용 계층</td>
<td>전송 계층</td>
</tr>
<tr>
<td>역할</td>
<td>실시간 통신</td>
<td>안정적 데이터 전달</td>
</tr>
<tr>
<td>기반</td>
<td>TCP 기반</td>
<td>원천 기술</td>
</tr>
</tbody></table>
<p>📌 WebSocket이 실시간 통신을 효율적으로 수행할 수 있는 이유<br>→ TCP가 <strong>순서 보장 + 신뢰성 + 연결 유지</strong>를 제공하기 때문입니다.</p>
<hr>
<h2 id="websocket이-필요한-이유-정리">WebSocket이 필요한 이유 정리</h2>
<table>
<thead>
<tr>
<th>기존 방식 (HTTP Polling)</th>
<th>WebSocket</th>
</tr>
</thead>
<tbody><tr>
<td>지속적인 요청 발생</td>
<td>연결 유지</td>
</tr>
<tr>
<td>지연 발생</td>
<td>지연 최소</td>
</tr>
<tr>
<td>서버 Push 불가</td>
<td>서버 Push 가능</td>
</tr>
<tr>
<td>트래픽 낭비</td>
<td>효율적 통신</td>
</tr>
</tbody></table>
<hr>
<h2 id="결론">결론</h2>
<ul>
<li>WebSocket은 <strong>HTTP 요청에서 시작하여 WebSocket으로 업그레이드</strong>된다.</li>
<li>WebSocket은 <strong>TCP의 연결 위에서 실시간 양방향 통신을 제공</strong>한다.</li>
<li>실시간 정보 업데이트가 필요한 모든 시스템의 핵심 기술이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Security]]></title>
            <link>https://velog.io/@j_blin/Spring-Security</link>
            <guid>https://velog.io/@j_blin/Spring-Security</guid>
            <pubDate>Wed, 19 Nov 2025 14:22:23 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-security">Spring Security</h1>
<h2 id="20251119">2025.11.19</h2>
<h2 id="1-spring-security의-필요성">1. Spring Security의 필요성</h2>
<h3 id="표준화된-단일-인증인가-체계-제공">표준화된 단일 인증/인가 체계 제공</h3>
<ul>
<li>ID/Password, OAuth, JWT 등 다양한 인증 방식을 통합적으로 지원</li>
</ul>
<h3 id="세션-및-토큰-관리-기능-제공">세션 및 토큰 관리 기능 제공</h3>
<ul>
<li>세션 기반 인증 / JWT 기반 인증 모두 지원</li>
</ul>
<h3 id="암호화와-비밀번호-보안-강화">암호화와 비밀번호 보안 강화</h3>
<ul>
<li>BCrypt 같은 강력한 해시 알고리즘으로 비밀번호를 안전하게 저장</li>
</ul>
<h3 id="권한-제어-기능-제공">권한 제어 기능 제공</h3>
<ul>
<li>URL 단위, Method 단위(<code>@PreAuthorize</code>)에서 Role 기반 접근 제어</li>
</ul>
<h3 id="웹-보안-취약점-방어">웹 보안 취약점 방어</h3>
<ul>
<li>CSRF 토큰 자동 생성</li>
<li>로그인 성공 시 세션ID 자동 재발급 → CSRF, 세션 고정 공격 방어</li>
</ul>
<hr>
<h2 id="2-인증authentication--인가authorization">2. 인증(Authentication) / 인가(Authorization)</h2>
<ul>
<li><strong>인증(Authentication)</strong>: 사용자가 누구인지 식별하는 과정  </li>
<li><strong>인가(Authorization)</strong>: 인증된 사용자가 특정 자원에 접근 가능한지 확인하는 과정</li>
</ul>
<hr>
<h2 id="3-세션-기반-인증-vs-토큰-기반-인증jwt">3. 세션 기반 인증 vs 토큰 기반 인증(JWT)</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>세션 기반 인증</th>
<th>토큰 기반 인증(JWT)</th>
</tr>
</thead>
<tbody><tr>
<td>저장 위치</td>
<td>서버 세션 저장소</td>
<td>클라이언트(브라우저, 앱)</td>
</tr>
<tr>
<td>클라이언트 보관</td>
<td>세션ID(쿠키)</td>
<td>JWT(Access Token)</td>
</tr>
<tr>
<td>상태</td>
<td>Stateful</td>
<td>Stateless</td>
</tr>
<tr>
<td>서버 확장성</td>
<td>세션 공유 문제 존재</td>
<td>무상태 → 확장성 우수</td>
</tr>
<tr>
<td>보안 고려사항</td>
<td>세션 탈취 방지</td>
<td>토큰 탈취·만료·재발급 관리 필요</td>
</tr>
<tr>
<td>적합 환경</td>
<td>단일 서버, 소규모 서비스</td>
<td>분산 서버, MSA, 대규모 서비스</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-인증-→-인가-→-응답-처리-흐름">4. 인증 → 인가 → 응답 처리 흐름</h2>
<ol>
<li>클라이언트에서 요청이 들어오면 <strong>Spring Security FilterChain</strong>을 통과</li>
<li><code>UsernamePasswordAuthenticationFilter</code>가 ID/PW 추출</li>
<li><code>AuthenticationManager</code>가 <code>UserDetailsService</code>로 사용자 정보 검증  <ul>
<li>성공 → Authentication 객체 생성 후 <code>SecurityContext</code>에 저장  </li>
<li>실패 → <code>ExceptionTranslationFilter</code>가 401 처리</li>
</ul>
</li>
<li><code>FilterSecurityInterceptor</code>가 권한 확인  <ul>
<li>권한 없음 → 403 반환  </li>
<li>권한 있음 → Controller로 전달</li>
</ul>
</li>
<li>Controller에서 정상 응답 반환</li>
</ol>
<hr>
<h2 id="5-spring-security-구조">5. Spring Security 구조</h2>
<p><img src="https://velog.velcdn.com/images/j_blin/post/27c0900c-0dec-427b-b106-2a480def2da2/image.png" alt=""></p>
<h3 id="security-filter-chain">Security Filter Chain</h3>
<ul>
<li>요청 URL에 매칭되는 필터 체인이 선택되어<br>여러 보안 필터가 순서대로 인증/인가 처리</li>
</ul>
<h3 id="delegatingfilterproxy">DelegatingFilterProxy</h3>
<ul>
<li>서블릿 컨테이너 필터  </li>
<li>실제 보안 처리는 <code>FilterChainProxy</code>로 위임하는 브릿지 역할</li>
</ul>
<h3 id="filterchainproxy">FilterChainProxy</h3>
<ul>
<li>요청과 매칭되는 SecurityFilterChain 선택 및 실행</li>
</ul>
<hr>
<h2 id="6-주요-동작-필터">6. 주요 동작 필터</h2>
<h3 id="usernamepasswordauthenticationfilter">UsernamePasswordAuthenticationFilter</h3>
<ul>
<li>로그인 요청에서 사용자명/비밀번호 추출  </li>
<li>인증 성공 시 Authentication 생성 → SecurityContext 저장</li>
</ul>
<h3 id="exceptiontranslationfilter">ExceptionTranslationFilter</h3>
<ul>
<li>인증 실패(401), 접근 거부(403) 예외 처리</li>
</ul>
<h3 id="filtersecurityinterceptor">FilterSecurityInterceptor</h3>
<ul>
<li>최종 권한 체크  </li>
<li>접근 가능 여부 결정</li>
</ul>
<hr>
<h2 id="7-주요-컴포넌트">7. 주요 컴포넌트</h2>
<h3 id="authenticationmanager">AuthenticationManager</h3>
<ul>
<li>로그인 요청 검증  </li>
<li>Authentication 객체 반환</li>
</ul>
<h3 id="userdetailsservice">UserDetailsService</h3>
<ul>
<li>DB에서 사용자 정보 조회  </li>
<li>비밀번호 검증</li>
</ul>
<h3 id="securitycontext">SecurityContext</h3>
<ul>
<li>인증된 사용자 정보를 저장  </li>
<li>요청마다 <code>SecurityContextHolder</code>로 관리</li>
</ul>
<hr>
<h2 id="8-요청-흐름-spring-boot">8. 요청 흐름 (Spring Boot)</h2>
<ol>
<li>클라이언트가 <code>/login</code>으로 인증 요청 (ID/PW)</li>
<li><code>DelegatingFilterProxy</code>가 요청을 Security Filter Chain으로 전달</li>
<li><code>UsernamePasswordAuthenticationFilter</code>가 ID/PW 추출 후<br><code>AuthenticationManager</code>에게 인증 위임  <ul>
<li>내부적으로 <code>AuthenticationProvider</code> →<br><code>UserDetailsService</code> + <code>PasswordEncoder</code> 사용</li>
</ul>
</li>
<li>인증 성공 → Authentication 생성 → SecurityContext 저장<br>인증 실패 → <code>ExceptionTranslationFilter</code>가 401 응답 처리</li>
<li><code>FilterSecurityInterceptor</code>가 URL/메서드 권한 체크  <ul>
<li>권한 없음 → 403</li>
</ul>
</li>
<li>권한 통과 후 Controller → Response 반환</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Builder, From]]></title>
            <link>https://velog.io/@j_blin/Builder-From</link>
            <guid>https://velog.io/@j_blin/Builder-From</guid>
            <pubDate>Mon, 03 Nov 2025 12:47:25 GMT</pubDate>
            <description><![CDATA[<h1 id="builderm-from">Builderm, From</h1>
<h2 id="20251103">2025.11.03</h2>
<h2 id="1-builder란">1) Builder란</h2>
<h3 id="11-문제-상황-텔레스코핑-생성자파라미터-폭탄">1.1 문제 상황: 텔레스코핑 생성자(파라미터 폭탄)</h3>
<pre><code class="language-java">// 나쁨: 어떤 값이 무엇인지, 순서를 외우기 어렵다
new Product(&quot;MacBook&quot;, &quot;M3 Max&quot;, 3890000, 20, &quot;노트북&quot;, null);</code></pre>
<ul>
<li><strong>가독성↓</strong>: 인자의 의미가 불명확</li>
<li><strong>안전성↓</strong>: 순서 실수 시 컴파일이 통과해도 런타임 버그</li>
<li><strong>옵셔널 필드</strong>가 많아질수록 생성자가 폭발</li>
</ul>
<h3 id="12-해결-builder-패턴">1.2 해결: Builder 패턴</h3>
<ul>
<li><strong>필드명을 키로</strong> 값을 채워 넣음 -&gt; <strong>가독성↑, 안전성↑</strong></li>
<li><strong>옵셔널은 필드만 선택적으로</strong> 넣을 수 있음</li>
<li><strong>불변 DTO</strong> 조합에 적합하다</li>
</ul>
<pre><code class="language-java">@Getter
@Builder
public class ProductDto {
    private final String name;
    private final String description;
    private final int price;
    private final int stockQuantity;
    private final String category;
    private final String imageUrl;
}

// 사용
ProductDto dto = ProductDto.builder()
    .name(&quot;MacBook Pro 17&quot;)
    .description(&quot;M3 Max 칩&quot;)
    .price(3_890_000)
    .stockQuantity(20)
    .category(&quot;노트북&quot;)
    .imageUrl(&quot;https://...&quot;)
    .build();</code></pre>
<hr>
<h2 id="2-lombok-builder-핵심-옵션">2) Lombok <code>@Builder</code> 핵심 옵션</h2>
<h3 id="21-사용법">2.1 사용법</h3>
<ul>
<li><strong>DTO</strong>: 클래스 레벨 <code>@Builder</code> 권장 (불변 DTO 지향)</li>
<li><strong>엔티티(JPA)</strong>:<ul>
<li><code>@NoArgsConstructor(protected)</code> + <strong>빌더 생성자</strong>에 <code>@Builder</code>
붙이기 권장</li>
<li>무분별한 필드 세터보다 <strong>의미 있는 생성 경로</strong>를 제공</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = &quot;products&quot;)
public class Product extends BaseTimeEntity {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable=false) private String name;
    @Column(nullable=false) private String description;
    @Column(nullable=false) private int price;
    @Column(nullable=false) private int stockQuantity;
    @Column(nullable=false) private String category;
    private String imageUrl;

    @Builder // ← 생성자에 붙여 의도된 생성 흐름 강제
    private Product(String name, String description, int price, int stockQuantity,
                    String category, String imageUrl) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.stockQuantity = stockQuantity;
        this.category = category;
        this.imageUrl = imageUrl;
    }
}</code></pre>
<h3 id="22-자주-쓰는-추가-기능">2.2 자주 쓰는 추가 기능</h3>
<ul>
<li><p><code>@Builder.Default</code> : 빌더 미지정 시 기본값 유지</p>
<pre><code class="language-java">@Builder
public class ItemDto {
    @Builder.Default
    private String status = &quot;ACTIVE&quot;; // builder().build() 시에도 ACTIVE 유지
}</code></pre>
</li>
<li><p><code>@Singular</code> : 컬렉션 필드를 <code>addXxx()</code> 식으로 누적</p>
<pre><code class="language-java">@Singular
private List&lt;String&gt; tags; // .tag(&quot;a&quot;).tag(&quot;b&quot;).build()</code></pre>
</li>
<li><p><code>toBuilder=true</code> : 기존 객체를 복제 후 일부만 변경</p>
<pre><code class="language-java">ItemDto changed = original.toBuilder().status(&quot;PAUSED&quot;).build();</code></pre>
</li>
</ul>
<h3 id="23-json·검증과의-궁합">2.3 JSON·검증과의 궁합</h3>
<ul>
<li>DTO는 <strong><code>@Getter</code> 필수</strong> (JSON 직렬화용)</li>
<li>요청 DTO는 <code>@NotNull</code>, <code>@Positive</code> 등 <strong>Bean Validation</strong>으로 선제
검증\</li>
<li>엔티티를 직렬화 대상에 직접 쓰지 말고 <strong>DTO로 분리</strong> (N+1, 순환참조 방지)</li>
</ul>
<hr>
<h2 id="3-from이란">3) <code>from()</code>이란</h2>
<h3 id="31-정의">3.1 정의</h3>
<ul>
<li><strong>정적 팩토리 메서드</strong>로, 보통 <strong>엔티티 → DTO</strong> 변환 로직을 한 곳에
모아둠.</li>
<li>서비스/컨트롤러에서 변환 코드를 흩뿌리지 않고 <strong>캡슐화</strong>.</li>
</ul>
<pre><code class="language-java">@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PostProductResponse {
    private Long id;
    private String name;
    private String description;
    private int price;
    private int stockQuantity;
    private String category;
    private String status;
    private String imageUrl;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private String message;
    private String warning;

    public static PostProductResponse from(Product p, String message, String warning) {
        return PostProductResponse.builder()
            .id(p.getId())
            .name(p.getName())
            .description(p.getDescription())
            .price(p.getPrice())
            .stockQuantity(p.getStockQuantity())
            .category(p.getCategory())
            .status(p.getStockQuantity() &gt; 0 ? &quot;AVAILABLE&quot; : &quot;OUT_OF_STOCK&quot;)
            .imageUrl(p.getImageUrl())
            .createdAt(p.getCreatedAt())
            .updatedAt(p.getUpdatedAt())
            .message(message)
            .warning(warning)
            .build();
    }
}</code></pre>
<h3 id="32-dto에서-사용하는-이유">3.2 DTO에서 사용하는 이유</h3>
<ul>
<li><strong>중복 제거</strong>: 변환 규칙을 한 곳에서 관리</li>
<li><strong>변경 용이</strong>: 필드 추가/명 변경 시 <code>from()</code>만 수정하면 전역 반영</li>
<li><strong>테스트 용이</strong>: 변환 규칙 단위 테스트 가능</li>
</ul>
<hr>
<h2 id="4-설계-방법">4) 설계 방법</h2>
<ol>
<li><strong>DTO는 불변</strong> + <code>@Builder</code> + <code>@Getter</code><ul>
<li>생성 이후 변경 불가 → 사이드이펙트 최소화</li>
</ul>
</li>
<li><strong>엔티티 생성 경로 통일</strong><ul>
<li><code>@Builder</code> 생성자만 허용(세터 지양)</li>
<li>의미 있는 필수값만 인자로 받게 설계</li>
</ul>
</li>
<li><strong><code>from()</code>로 변환 로직 캡슐화</strong><ul>
<li>서비스/컨트롤러에서 변환 코드 제거, 테스트 쉬움</li>
</ul>
</li>
<li><strong>검증은 요청 DTO에서</strong><ul>
<li><code>@Valid</code> + Bean Validation으로 최대한 앞단에서 걸러내기</li>
</ul>
</li>
<li><strong>HTTP 응답 코드 일관성</strong><ul>
<li>중복/권한/검증 실패/리소스 없음 등 표준 상태코드 매핑</li>
</ul>
</li>
<li><strong>DB 제약으로 이중 보호</strong><ul>
<li><code>@Table(uniqueConstraints=...)</code> 또는 컬럼 <code>unique = true</code></li>
<li>동시성 상황을 DB가 보조</li>
</ul>
</li>
</ol>
<hr>
<h2 id="5-한-줄-요약">5) 한 줄 요약</h2>
<ul>
<li><strong>Builder</strong>: &quot;필드명 기반&quot;으로 <strong>안전하고 읽기 쉬운 객체 생성</strong>을
만든다.</li>
<li><strong><code>from()</code></strong>: <strong>엔티티 -&gt; DTO 변환 규칙의 단일 진실(SSOT)</strong> 을
제공한다.</li>
<li>둘을 함께 쓰면 <strong>가독성/유지보수성/테스트 용이성</strong>이 크게 향상된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[과제 트러블슈팅]]></title>
            <link>https://velog.io/@j_blin/%EA%B3%BC%EC%A0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@j_blin/%EA%B3%BC%EC%A0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</guid>
            <pubDate>Thu, 30 Oct 2025 04:50:40 GMT</pubDate>
            <description><![CDATA[<p>작성중입니다.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키, 세션, JWT]]></title>
            <link>https://velog.io/@j_blin/%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT</link>
            <guid>https://velog.io/@j_blin/%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT</guid>
            <pubDate>Wed, 22 Oct 2025 15:39:25 GMT</pubDate>
            <description><![CDATA[<h1 id="쿠키-세션-jwt">쿠키, 세션, JWT</h1>
<h2 id="20251023">2025.10.23</h2>
<h1 id="쿠키cookie-세션session-jwtjson-web-token-정리">쿠키(Cookie), 세션(Session), JWT(Json Web Token) 정리</h1>
<hr>
<h2 id="쿠키-cookie">쿠키 (Cookie)</h2>
<p><strong>개념:</strong>
클라이언트(웹 브라우저)에 저장되는 작은 데이터 파일로, 사용자의 상태나 설정 정보를 저장하기 위해 사용됩니다.</p>
<p><strong>주요 특징:</strong></p>
<ul>
<li>서버가 브라우저에 전달하고, 브라우저가 이를 저장함.</li>
<li>이후 요청 시 자동으로 쿠키를 포함해 서버에 전송.</li>
<li>주로 로그인 유지, 팝업 미표시 설정, 장바구니 정보 등에 사용됨.</li>
</ul>
<p><strong>구성 요소:</strong>
| 항목 | 설명 |
|------|------|
| <strong>Name</strong> | 쿠키 이름 (고유 식별자) |
| <strong>Value</strong> | 쿠키 값 |
| <strong>Domain</strong> | 쿠키가 적용되는 도메인 |
| <strong>Path</strong> | 쿠키가 적용되는 경로 |
| <strong>Expires</strong> | 만료 시점 (없으면 세션 쿠키로 브라우저 종료 시 삭제) |</p>
<p><strong>저장 위치:</strong> 클라이언트(웹 브라우저)</p>
<p><strong>만료 시점:</strong> 쿠키 생성 시 지정 가능 (브라우저 종료 후에도 유지 가능)</p>
<p><strong>보안:</strong> 취약 (클라이언트에서 쉽게 접근 가능)</p>
<hr>
<h2 id="세션-session">세션 (Session)</h2>
<p><strong>개념:</strong>
서버에서 일정 시간 동안 클라이언트의 상태를 유지하기 위해 사용하는 기술입니다.</p>
<p><strong>주요 특징:</strong></p>
<ul>
<li>서버가 클라이언트마다 고유한 <strong>세션 ID</strong>를 발급.</li>
<li>해당 ID는 쿠키 형태로 클라이언트에 저장되어 요청 시 서버가 이를 이용해 클라이언트를 식별.</li>
<li>로그인 정보 등 민감한 데이터를 서버에 저장하기 때문에 보안성이 높음.</li>
</ul>
<p><strong>동작 방식:</strong></p>
<ol>
<li>클라이언트가 서버에 요청을 보냄.</li>
<li>서버가 세션을 생성하고 세션 ID를 클라이언트에 전달.</li>
<li>클라이언트는 세션 ID를 쿠키로 저장.</li>
<li>이후 요청마다 쿠키에 담긴 세션 ID를 함께 전송.</li>
<li>서버는 세션 ID로 클라이언트를 식별하여 동일 사용자임을 인식.</li>
</ol>
<p><strong>저장 위치:</strong> 서버</p>
<p><strong>만료 시점:</strong></p>
<ul>
<li>브라우저 종료 시</li>
<li>클라이언트 로그아웃 시</li>
<li>서버에서 설정한 유효시간 만료 시</li>
</ul>
<p><strong>보안:</strong> 비교적 안전 (서버 저장 방식)</p>
<hr>
<h2 id="jwt-json-web-token">JWT (Json Web Token)</h2>
<p><strong>개념:</strong>
서버가 클라이언트를 인증하기 위해 JSON 형태로 인코딩된 토큰을 발급하고, 클라이언트가 이를 직접 보관하는 <strong>무상태(stateless)</strong> 인증 방식입니다.</p>
<p><strong>주요 특징:</strong></p>
<ul>
<li>인증 정보가 서버가 아닌 <strong>토큰 내부에 포함</strong>됨.</li>
<li>서버는 요청 시 토큰의 서명을 검증해 신뢰할 수 있는 사용자임을 확인.</li>
<li>세션과 달리 서버에 상태 정보를 저장하지 않음 → 확장성 우수.</li>
</ul>
<p><strong>구조:</strong>
JWT는 3부분으로 구성됩니다.</p>
<pre><code>Header.Payload.Signature</code></pre><table>
<thead>
<tr>
<th>구성</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Header</strong></td>
<td>알고리즘 및 토큰 타입 정보 (예: HS256, JWT)</td>
</tr>
<tr>
<td><strong>Payload</strong></td>
<td>사용자 정보 및 토큰 만료 시간 등의 데이터</td>
</tr>
<tr>
<td><strong>Signature</strong></td>
<td>Header + Payload를 비밀키로 서명한 값</td>
</tr>
</tbody></table>
<p><strong>동작 방식:</strong></p>
<ol>
<li>사용자가 로그인 요청을 보냄.</li>
<li>서버가 사용자 인증 후 JWT를 생성하여 클라이언트에 전달.</li>
<li>클라이언트는 JWT를 LocalStorage 또는 쿠키에 저장.</li>
<li>이후 요청 시 <code>Authorization: Bearer &lt;JWT&gt;</code> 헤더로 전달.</li>
<li>서버는 토큰 서명을 검증하여 사용자 식별.</li>
</ol>
<p><strong>저장 위치:</strong> 클라이언트 (LocalStorage, SessionStorage, 또는 쿠키)</p>
<p><strong>만료 시점:</strong> 토큰 내부 <code>exp</code>(만료 시간) 필드로 지정.</p>
<p><strong>보안:</strong> 안전하지만 토큰 탈취 시 재발급 필요 (서버가 상태를 저장하지 않기 때문)</p>
<hr>
<h2 id="쿠키-세션-jwt-비교-요약">쿠키, 세션, JWT 비교 요약</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>쿠키 (Cookie)</th>
<th>세션 (Session)</th>
<th>JWT (Json Web Token)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>설명</strong></td>
<td>브라우저에 저장되는 작은 데이터 파일</td>
<td>서버가 클라이언트 상태를 유지하기 위한 기술</td>
<td>클라이언트가 인증 정보를 직접 보관하는 토큰 방식</td>
</tr>
<tr>
<td><strong>저장 위치</strong></td>
<td>클라이언트(브라우저)</td>
<td>서버</td>
<td>클라이언트(LocalStorage, 쿠키 등)</td>
</tr>
<tr>
<td><strong>유형</strong></td>
<td>상태 유지용</td>
<td>상태 유지용</td>
<td>무상태(Stateless) 인증</td>
</tr>
<tr>
<td><strong>사용 예시</strong></td>
<td>팝업 미표시, 자동 로그인</td>
<td>로그인 세션 유지</td>
<td>API 인증, OAuth 인증</td>
</tr>
<tr>
<td><strong>만료 시점</strong></td>
<td>설정 가능 (영구/세션)</td>
<td>서버 설정 시간, 브라우저 종료 시</td>
<td>토큰 내부 exp 필드로 지정</td>
</tr>
<tr>
<td><strong>보안성</strong></td>
<td>취약 (변조/탈취 위험)</td>
<td>안전 (서버 저장)</td>
<td>비교적 안전하지만 탈취 시 위험</td>
</tr>
<tr>
<td><strong>서버 부하</strong></td>
<td>없음</td>
<td>높음 (모든 세션 관리 필요)</td>
<td>낮음 (서버 상태 저장 불필요)</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td>보통</td>
<td>낮음 (세션 공유 필요)</td>
<td>매우 높음</td>
</tr>
<tr>
<td><strong>대표 저장 데이터</strong></td>
<td>사용자 설정 정보</td>
<td>로그인 상태</td>
<td>사용자 인증 토큰</td>
</tr>
</tbody></table>
<hr>
<blockquote>
<p><strong>정리 요약</strong></p>
<ul>
<li><strong>쿠키:</strong> 클라이언트 중심, 단순 데이터 저장</li>
<li><strong>세션:</strong> 서버 중심, 로그인 상태 유지</li>
<li><strong>JWT:</strong> 무상태 인증, 서버 부담 감소 및 확장성 향상</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 기초 2일차]]></title>
            <link>https://velog.io/@j_blin/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-2%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@j_blin/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-2%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 16 Oct 2025 12:38:28 GMT</pubDate>
            <description><![CDATA[<h1 id="백엔드-기초-지식-공부">백엔드 기초 지식 공부</h1>
<h2 id="20251016">2025.10.16</h2>
<h2 id="orm이란">ORM이란</h2>
<p><strong>ORM(Object-Relational Mapping)</strong>
:   객체지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 테이블을
    자동으로 매핑해주는 기술.</p>
<p>** 주요 특징** - 객체와 테이블 간의 데이터 변환 자동화 - SQL을 직접
작성하지 않아도 CRUD 가능 - 패러다임 불일치 문제를 해결함</p>
<p>** 패러다임 불일치란** 데이터베이스는 한 칸에 <code>String</code>, <code>Integer</code> 같은
단일 값만 넣을 수 있지만,
자바 객체는 <code>List</code>, <code>Object</code> 등을 포함할 수 있는 구조적 차이를 말한다.</p>
<hr>
<h2 id="jdbc란">JDBC란</h2>
<p><strong>JDBC(Java Database Connectivity)</strong>
자바 프로그램에서 데이터베이스에 접속하고 SQL문을 실행하기 위한 API.
직접 SQL을 작성하고 결과를 처리해야 하므로 코드가 복잡하고 유지보수가
어려움.</p>
<hr>
<h2 id="jpa란">JPA란</h2>
<p><strong>JPA(Java Persistence API)</strong>
Java와 관계형 데이터베이스 간의 패러다임 불일치 문제를 해결하여
데이터베이스 작업을 객체지향적으로 수행할 수 있도록 지원하는 ORM 기술의
표준 인터페이스.</p>
<ul>
<li>JPA는 <strong>인터페이스</strong> 이므로 단독 사용 불가.</li>
<li><strong>구현체</strong>로 대표적으로 <strong>Hibernate</strong>를 사용.</li>
<li>Hibernate는 내부적으로 JDBC를 이용해 DB와 통신.</li>
</ul>
<p><strong>JPA 계층 구조</strong></p>
<p><img src="https://velog.velcdn.com/images/j_blin/post/7b8a9ff3-71bf-4011-b667-0ac163b9b4e5/image.webp" alt=""></p>
<hr>
<h2 id="spring-data-jpa">Spring Data JPA</h2>
<p>Spring에서 JPA를 쉽게 사용할 수 있도록 제공하는 <strong>추상화된 모듈</strong>.</p>
<ul>
<li>Repository 인터페이스만 정의하면 CRUD 기능 자동 제공.</li>
<li>복잡한 EntityManager 사용 없이 간단하게 데이터 처리 가능.</li>
</ul>
<hr>
<h2 id="entity란">Entity란</h2>
<p>Entity는 데이터베이스 테이블과 1:1로 매핑되는 <strong>Java 클래스</strong>.</p>
<p><strong>관련 어노테이션</strong> </p>
<ul>
<li><p><strong><code>@Entity</code></strong>
→ 해당 클래스가 JPA 엔티티임을 선언
(즉, 데이터베이스 테이블과 매핑되는 클래스임을 나타냄.)</p>
</li>
<li><p><strong><code>@Table(name = &quot;members&quot;)</code></strong>
→ 엔티티와 매핑할 데이터베이스 테이블 이름을 지정한다.
(지정하지 않으면 클래스 이름이 테이블 이름으로 사용됨.)</p>
</li>
<li><p><strong><code>@Id</code></strong>
→ 기본 키(Primary Key)를 지정한다.
(각 엔티티는 반드시 하나의 <code>@Id</code> 필드를 가져야 한다.)</p>
</li>
<li><p><strong><code>@GeneratedValue</code></strong>
→ 기본 키 자동 생성 전략을 지정한다.
(예: MySQL에서는 <code>GenerationType.IDENTITY</code> 사용)</p>
</li>
<li><p><strong><code>@Column</code></strong>
→ 엔티티 필드와 데이터베이스 컬럼의 매핑 정보를 지정한다.
속성 예시:</p>
<ul>
<li><code>name</code> : 컬럼명 지정</li>
<li><code>length</code> : 문자열 길이 제한</li>
<li><code>nullable</code> : <code>null</code> 허용 여부</li>
<li><code>unique</code> : 유니크 제약 조건 설정</li>
</ul>
</li>
</ul>
<p><strong>@NoArgsConstructor(access = AccessLevel.PROTECTED)</strong>
Lombok 어노테이션으로 기본 생성자를 protected로 생성하여 외부에서 임의로
객체 생성을 제한.</p>
<hr>
<h2 id="spring-layer-architecture">Spring Layer Architecture</h2>
<p><strong>Spring 애플리케이션의 3계층 구조</strong></p>
<p><img src="https://velog.velcdn.com/images/j_blin/post/7b14083e-8f52-4081-a967-563cbdb99990/image.png" alt=""></p>
<h3 id="controller-layer">Controller Layer</h3>
<ul>
<li>HTTP 요청을 받고 응답을 반환하는 역할</li>
<li>요청 파라미터 검증, 예외 처리 담당</li>
</ul>
<p><strong>해야 할 일</strong> - @RestController 또는 @Controller 사용 - 요청 매핑
(@GetMapping, @PostMapping 등) - 요청 데이터 DTO로 받기 - 응답 데이터
반환 (JSON)</p>
<hr>
<h3 id="service-layer">Service Layer</h3>
<ul>
<li>비즈니스 로직 처리 계층</li>
<li>핵심 로직, 트랜잭션, 데이터 검증 수행</li>
</ul>
<p><strong>해야 할 일</strong> - @Service 사용 - 여러 Repository 호출 및 조합 - 비즈니스
규칙, 데이터 검증 - 트랜잭션 경계 설정 (@Transactional)</p>
<hr>
<h3 id="repository-layer">Repository Layer</h3>
<ul>
<li>데이터베이스와 직접 상호작용</li>
<li>Spring Data JPA의 <code>JpaRepository</code>를 상속받아 CRUD 자동화</li>
</ul>
<p><strong>해야 할 일</strong> - CRUD 메서드 정의 또는 쿼리 메서드 작성 - 쿼리 최적화 및
데이터 매핑</p>
<hr>
<h2 id="계층-간-통신-규칙">계층 간 통신 규칙</h2>
<hr>
<p>  ✅ 허용 </p>
<ul>
<li><p>상위 → 하위 계층 호출</p>
</li>
<li><p>동일 계층 내 통신</p>
<p>❌ 금지                    </p>
</li>
<li><p>하위 → 상위 계층 호출</p>
</li>
<li><p>계층 건너뛰기 (Controller → Repository 직접 접근 금지)</p>
</li>
</ul>
<hr>
<h2 id="dto란-data-transfer-object">DTO란 (Data Transfer Object)</h2>
<p>데이터를 계층 간 전달하기 위한 순수한 데이터 객체.</p>
<p><strong>역할</strong> - Controller에서 클라이언트의 요청(JSON 등)을 DTO로 변환 - DTO
→ Entity 변환 후 DB 저장 - Entity → DTO 변환 후 응답 반환</p>
<p><strong>예시</strong></p>
<pre><code class="language-java">public class MemberDTO {
    private String name;
    private String email;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 기초 1일차]]></title>
            <link>https://velog.io/@j_blin/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@j_blin/%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 14 Oct 2025 13:14:16 GMT</pubDate>
            <description><![CDATA[<h1 id="백엔드-기초-지식-공부">백엔드 기초 지식 공부</h1>
<h2 id="20251014">2025.10.14</h2>
<h2 id="📦-properties-of-http-request-methods">📦 Properties of HTTP Request Methods</h2>
<table>
<thead>
<tr>
<th>Method</th>
<th>Request Body</th>
<th>Response Body</th>
<th>Safe</th>
<th>Idempotent</th>
<th>Cacheable</th>
</tr>
</thead>
<tbody><tr>
<td>GET</td>
<td>Optional</td>
<td>Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>HEAD</td>
<td>Optional</td>
<td>No</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>POST</td>
<td>Yes</td>
<td>Yes</td>
<td>❌ No</td>
<td>❌ No</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>PUT</td>
<td>Yes</td>
<td>Yes</td>
<td>❌ No</td>
<td>✅ Yes</td>
<td>❌ No</td>
</tr>
<tr>
<td>DELETE</td>
<td>Optional</td>
<td>Yes</td>
<td>❌ No</td>
<td>✅ Yes</td>
<td>❌ No</td>
</tr>
<tr>
<td>CONNECT</td>
<td>Optional</td>
<td>Yes</td>
<td>❌ No</td>
<td>❌ No</td>
<td>❌ No</td>
</tr>
<tr>
<td>OPTIONS</td>
<td>Optional</td>
<td>Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>❌ No</td>
</tr>
<tr>
<td>TRACE</td>
<td>No</td>
<td>Yes</td>
<td>✅ Yes</td>
<td>✅ Yes</td>
<td>❌ No</td>
</tr>
<tr>
<td>PATCH</td>
<td>Yes</td>
<td>Yes</td>
<td>❌ No</td>
<td>❌ No</td>
<td>❌ No</td>
</tr>
</tbody></table>
<p> <strong>Optional</strong>: 있을 수도 있고 없을 수도 있다.</p>
<hr>
<h2 id="개념-정리">개념 정리</h2>
<h3 id="🔸-safe-안전성">🔸 Safe (안전성)</h3>
<ul>
<li><code>GET</code> 메서드는 데이터를 조회만 하므로 안전하다.  </li>
<li><code>POST</code>, <code>PUT</code>, <code>DELETE</code>, <code>PATCH</code>는 데이터를 생성·수정·삭제하기 때문에 <strong>안전하지 않다.</strong></li>
</ul>
<h3 id="🔸-idempotent-멱등성">🔸 Idempotent (멱등성)</h3>
<ul>
<li><strong>한 번 호출하든 여러 번 호출하든 결과가 같다.</strong></li>
<li><code>GET</code>, <code>PUT</code>, <code>DELETE</code>는 멱등성을 보장한다.  </li>
<li><code>POST</code>는 멱등성을 보장하지 않는다.</li>
</ul>
<h4 id="멱등성이-중요한-이유">멱등성이 중요한 이유</h4>
<ol>
<li><strong>요청 실패 시 재시도 가능</strong><br>→ 항상 같은 결과면 여러 번 요청해도 문제 없음<br>→ 멱등성이 없으면 중복 요청으로 데이터가 중복 생성될 수 있음</li>
<li><strong>캐시 가능성</strong>  <ul>
<li><code>GET</code>, <code>HEAD</code>, <code>POST</code>는 캐시 가능  </li>
<li>일반적으로는 <code>GET</code>, <code>HEAD</code>만 캐시로 사용</li>
</ul>
</li>
</ol>
<hr>
<h2 id="캐시cache">캐시(Cache)</h2>
<ul>
<li>클라이언트가 한 번 요청한 데이터를 <strong>임시 저장</strong>하여,<br>매번 서버에 재요청하지 않고 빠르게 접근할 수 있도록 하는 기술.</li>
</ul>
<hr>
<h2 id="상태-코드-http-status-codes">상태 코드 (HTTP Status Codes)</h2>
<table>
<thead>
<tr>
<th>코드</th>
<th>의미</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>2xx</td>
<td>성공</td>
<td>요청이 성공적으로 처리됨</td>
</tr>
<tr>
<td>3xx</td>
<td>리다이렉션</td>
<td>다른 URI로 이동 필요 (예: 구글/카카오 로그인 같은 상황에서 자주 나옴)</td>
</tr>
<tr>
<td>4xx</td>
<td>클라이언트 오류</td>
<td>잘못된 요청, 권한 문제 등</td>
</tr>
<tr>
<td>5xx</td>
<td>서버 오류</td>
<td>서버 내부 문제로 처리 실패</td>
</tr>
</tbody></table>
<hr>
<h2 id="rest-api--restful-api-디자인-규칙">REST API / RESTful API 디자인 규칙</h2>
<ol>
<li><strong>동사보다는 명사</strong>, 단수보다는 <strong>복수</strong> 사용  <ul>
<li><code>/users</code> O  <code>/getUsers</code> X </li>
</ul>
</li>
<li>마지막에 <code>/</code> 붙이지 않기  </li>
<li><code>_</code> 대신 <code>-</code> 사용, <strong>대문자 사용하지 않기</strong>  </li>
<li>확장자(<code>.svg</code>, <code>.png</code>, <code>.exe</code>) 포함하지 않기  </li>
<li><strong>계층적 구조 설계</strong>  <ul>
<li><code>/users/1/posts</code> 처럼 상위-하위 관계 표현</li>
</ul>
</li>
</ol>
<hr>
<h2 id="annotation-어노테이션">Annotation (어노테이션)</h2>
<blockquote>
<p>자바 코드에 <strong>메타데이터</strong>를 추가하여,<br>컴파일러나 런타임에서 특정 동작을 수행하도록 하는 기능</p>
</blockquote>
<p>예시:</p>
<ul>
<li><code>@Override</code> : 상속받은 메서드를 재정의</li>
<li><code>@Autowired</code> : 스프링이 자동으로 의존성 주입</li>
<li><code>@RestController</code>, <code>@Service</code>, <code>@Repository</code> : 역할 지정</li>
</ul>
<blockquote>
<p>복잡한 기능을 단순하게 사용할 수 있도록 도와준다.</p>
</blockquote>
<hr>
<h2 id="lombok-라이브러리">Lombok 라이브러리</h2>
<blockquote>
<p><strong>반복적인 코드(보일러플레이트)</strong>를 자동으로 생성해주는 라이브러리</p>
</blockquote>
<ul>
<li><code>@Getter</code>, <code>@Setter</code> : getter/setter 자동 생성  </li>
<li><code>@ToString</code> : toString() 자동 생성  </li>
<li><code>@AllArgsConstructor</code> : 모든 필드를 매개변수로 받는 생성자 생성  </li>
<li><code>@NoArgsConstructor</code> : 기본 생성자 생성  </li>
<li><code>@Slf4j</code> : Logger 객체 자동 생성 (로그 출력용)</li>
</ul>
<h3 id="보일러플레이트-코드란">보일러플레이트 코드란?</h3>
<blockquote>
<p>getter/setter, 생성자, toString() 등<br>반복적으로 작성되는 코드들을 의미한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 탐색 과제 troubleshooting]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%ED%83%90%EC%83%89-%EA%B3%BC%EC%A0%9C-troubleshooting</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%ED%83%90%EC%83%89-%EA%B3%BC%EC%A0%9C-troubleshooting</guid>
            <pubDate>Mon, 13 Oct 2025 13:58:57 GMT</pubDate>
            <description><![CDATA[<h1 id="java-탐색-및-성능-측정-과제">Java 탐색 및 성능 측정 과제</h1>
<h2 id="20251013">2025.10.13</h2>
<h2 id="학습-주제">학습 주제</h2>
<p><code>List</code>를 이용한  <strong>상품 탐색(binary search / linear search)</strong> 구현 및<br><strong>성능 비교(performance test)</strong></p>
<hr>
<h2 id="문제-1-문자열string-비교-방식">문제 1: 문자열(String) 비교 방식</h2>
<h3 id="상황">상황</h3>
<p><code>binarySearch(String productName)</code>을 구현할 때<br>“문자열을 기준으로 좌/우를 어떻게 비교해야 하는지” 감이 안왔다.</p>
<pre><code class="language-java">if (productName.compareTo(midProduct.getName()) == 0) {
    ...
}</code></pre>
<h3 id="✅-해결-방법">✅ 해결 방법</h3>
<ul>
<li><code>String.compareTo()</code> 메소드 사용.<ul>
<li><code>compareTo()</code>는 <strong>사전순 비교</strong>를 수행하며<br>반환값이 양수/음수/0이면 각각 <strong>오른쪽/왼쪽/같음</strong>을 의미함.</li>
<li><strong>정렬된 리스트</strong> 에서만 사용이 가능하다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">int cmp = productName.compareTo(midProduct.getName());
if (cmp &lt; 0) right = mid - 1;
else if (cmp &gt; 0) left = mid + 1;
else return mid;</code></pre>
<h3 id="배운-점">배운 점</h3>
<blockquote>
<p>문자열 비교는 <code>==</code>이 아니라 <code>.compareTo()</code> 또는 <code>.equals()</code>로 해야 한다.<br>“왼쪽/오른쪽” 판단은 <code>compareTo()</code>의 반환값으로 처리한다.</p>
</blockquote>
<hr>
<h2 id="문제-2-listproduct-자동-생성-및-정렬">문제 2: <code>List&lt;Product&gt;</code> 자동 생성 및 정렬</h2>
<h3 id="상황-1">상황</h3>
<p>10,000개의 상품 데이터를 자동으로 생성해야 했음.<br>상품명은 <code>&quot;Product_0001&quot;</code> ~ <code>&quot;Product_10000&quot;</code> 형태여야 함.</p>
<h3 id="✅-해결-방법-1">✅ 해결 방법</h3>
<ul>
<li><code>String.format(&quot;Product_%04d&quot;, i)</code> 사용 → 0패딩된 문자열 생성</li>
</ul>
<pre><code class="language-java">for (int i = 1; i &lt;= 10000; i++) {
    String name = String.format(&quot;Product_%04d&quot;, i);
    products.add(new Product(name));
}</code></pre>
<h3 id="배운-점-1">배운 점</h3>
<blockquote>
<p>정렬된 리스트를 전제로 하는 binary search에서는<br>반드시 동일한 기준으로 정렬해야 한다.<br>문자열 포맷팅(<code>String.format</code>)은 숫자형 인덱스를 문자열로 표현할 때 매우 유용하다.</p>
</blockquote>
<hr>
<h2 id="문제-3-binarysearch-비교문-작성-실수">문제 3: binarySearch() 비교문 작성 실수</h2>
<h3 id="상황-2">상황</h3>
<p><code>SearchEngine</code> 클래스의 이진탐색 구현 중</p>
<pre><code class="language-java">if (productName.compareTo(productName) == 0)</code></pre>
<p>처럼 <strong>자기 자신끼리 비교</strong>하는 코드가 되어버림 → 항상 0 반환.</p>
<h3 id="어려움의-본질">어려움의 본질</h3>
<ul>
<li>비교 대상이 중간값(<code>midProduct</code>)이 아니라, 자기 자신(<code>productName</code>)이었음.</li>
<li>결과적으로 탐색이 제대로 이루어지지 않음.</li>
</ul>
<h3 id="✅-해결-방법-2">✅ 해결 방법</h3>
<ul>
<li>비교 기준을 명확히 구분:<ul>
<li>왼쪽(right)/오른쪽(left) 이동 판단은 <code>productName</code> vs <code>midProduct.getName()</code> 비교로 수정.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">int compare = productName.compareTo(midProduct.getName());
if (compare == 0) return midProduct;
else if (compare &lt; 0) right = mid - 1;
else left = mid + 1;</code></pre>
<h3 id="배운-점-2">배운 점</h3>
<blockquote>
<p>“무엇을 무엇과 비교하고 있는가”를 항상 명확히 해야 한다.<br>특히 문자열 비교에서는 <code>compareTo()</code>의 인자를 잘못 넣으면 논리 전체가 무너진다.</p>
</blockquote>
<hr>
<h2 id="추가--숫자-표기-10_000-vs-10000">추가 : 숫자 표기 <code>10_000</code> vs <code>10000</code></h2>
<h3 id="상황-3">상황</h3>
<p>코드에 <code>generateProducts(10_000)</code>처럼 숫자 사이에 <code>_</code>를 사용하는 사람이 있어서 찾아봤다.</p>
<ul>
<li><p><code>_</code>는 단순히 <strong>가독성을 높이는 숫자 구분자</strong>이며,<br>실제 값은 동일 (<code>10_000 == 10000</code>).</p>
</li>
<li><p>언더스코어는 Java 7 이후 숫자 리터럴 내에서 자유롭게 사용 가능함을 확인.</p>
</li>
<li><p>성능이나 계산 결과에는 전혀 영향 없음.</p>
</li>
</ul>
<pre><code class="language-java">int a = 10_000; // = 10000</code></pre>
<h3 id="배운-점-3">배운 점</h3>
<blockquote>
<p><code>_</code>는 단순한 “시각적 구분자”일 뿐, 값의 의미는 변하지 않는다.</p>
</blockquote>
<hr>
<h1 id="오늘-배운-핵심-요약"><strong>오늘 배운 핵심 요약</strong></h1>
<table>
<thead>
<tr>
<th>주제</th>
<th>배운 점</th>
</tr>
</thead>
<tbody><tr>
<td>문자열 비교</td>
<td><code>compareTo()</code> 사용법</td>
</tr>
<tr>
<td>리스트 정렬</td>
<td><code>Comparator.comparing()</code></td>
</tr>
<tr>
<td>이진 탐색 로직</td>
<td><code>left</code>, <code>right</code>, <code>mid</code> 개념</td>
</tr>
<tr>
<td>숫자 리터럴</td>
<td><code>10_000 == 10000</code></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 스트림 개념정리]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 02 Oct 2025 12:00:27 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-10-02-스파르타-코딩클럽-24일차">2025 10 02 스파르타 코딩클럽 24일차</h1>
<p>오늘 스트림 개념을 정리하면서 쓰레드와 헷갈리는 부분이 있어서 추가로 학습한 내용을 정리했다.</p>
<h2 id="1-쓰레드thread-개념">1. 쓰레드(Thread) 개념</h2>
<ul>
<li><strong>정의</strong>: CPU가 실제로 작업을 수행하는 최소 실행 단위.  </li>
<li><strong>특징</strong>  <ul>
<li>하나의 프로세스 안에 여러 개의 쓰레드 존재 가능 → 멀티쓰레딩  </li>
<li>각각의 쓰레드는 <strong>독립된 실행 흐름</strong>을 가짐  </li>
<li>동시성(Concurrency), 병렬성(Parallelism) 구현의 핵심  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-스트림stream-개념">2. 스트림(Stream) 개념</h2>
<ul>
<li><strong>정의</strong>: 자바 8에서 도입된 <strong>데이터 처리 API</strong><br>(배열, 컬렉션의 요소를 필터링·매핑·집계 같은 연산을 SQL처럼 선언적으로 다룰 수 있음)  </li>
<li><strong>특징</strong><ul>
<li>원본 데이터 변경 X (불변성)  </li>
<li>중간 연산(filter, map 등)은 지연실행 → 최종 연산(collect, forEach 등) 시 수행  </li>
<li>내부 반복 (반복문 로직을 직접 안 짜도 됨)  </li>
<li>병렬 처리 지원 (parallelStream)  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-스트림에서-단일-쓰레드-vs-멀티-쓰레드">3. 스트림에서 단일 쓰레드 vs 멀티 쓰레드</h2>
<h3 id="1-단일-쓰레드-스트림-stream">(1) 단일 쓰레드 스트림 (<code>stream()</code>)</h3>
<ul>
<li>기본 스트림은 <strong>단일 쓰레드</strong>에서 순차 처리.</li>
<li>내부적으로 하나의 쓰레드만 사용하므로 데이터는 입력 순서대로 처리됨.</li>
<li>작은 데이터, 순서 중요한 경우 적합.</li>
</ul>
<pre><code class="language-java">list.stream()
    .filter(x -&gt; x &gt; 10)
    .map(x -&gt; x * 2)
    .forEach(System.out::println);</code></pre>
<p>실행 순서: <strong>한 쓰레드가 처음부터 끝까지 순차적으로</strong></p>
<hr>
<h3 id="2-멀티-쓰레드-스트림-parallelstream">(2) 멀티 쓰레드 스트림 (<code>parallelStream()</code>)</h3>
<ul>
<li>내부적으로 <strong>ForkJoinPool (공용 스레드 풀, 기본적으로 CPU 코어 수만큼의 워커 스레드)</strong> 사용.  </li>
<li>데이터를 여러 조각으로 나누고 병렬로 처리한 후 최종적으로 합침.  </li>
<li>대량의 데이터 처리 시 성능 향상 가능.  </li>
<li>단, <strong>작은 데이터에서는 오히려 성능 저하</strong> (스레드 관리 오버헤드 때문).  </li>
<li>순서 보장이 필요하다면 <code>forEachOrdered()</code> 사용해야 함.</li>
</ul>
<pre><code class="language-java">list.parallelStream()
    .filter(x -&gt; x &gt; 10)
    .map(x -&gt; x * 2)
    .forEach(System.out::println); // 순서 보장 X</code></pre>
<p>실행 순서: <strong>여러 쓰레드가 동시에 조각을 나눠서 처리</strong></p>
<hr>
<h2 id="최종-정리">최종 정리</h2>
<ul>
<li><strong>쓰레드(Thread)</strong> → CPU 실행 단위 (작은 범위, 저수준)  </li>
<li><strong>스트림(Stream)</strong> → 데이터 처리 API (큰 범위, 고수준)  </li>
<li><strong>단일 스트림(stream)</strong> → 순차 처리 (1개의 쓰레드)  </li>
<li><strong>병렬 스트림(parallelStream)</strong> → 병렬 처리 (여러 쓰레드, ForkJoinPool 활용)  </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 기초]]></title>
            <link>https://velog.io/@j_blin/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@j_blin/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Tue, 30 Sep 2025 11:10:53 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-30-스파르타-코딩클럽-22일차">2025 09 30 스파르타 코딩클럽 22일차</h1>
<h2 id="1-알고리즘-성능-분석">1. 알고리즘 성능 분석</h2>
<ul>
<li><p><strong>알고리즘 성능 분석이란?</strong><br>문제를 해결하는 여러 알고리즘 중에서, 주어진 시간과 자원 제약 안에서 가장 효율적인 방법을 찾기 위해 성능(시간, 메모리 등)을 평가하는 과정.</p>
</li>
<li><p><strong>시간 복잡도 (Time Complexity)</strong>  </p>
<ul>
<li>입력 크기 <code>n</code>에 따른 알고리즘의 실행 시간을 분석  </li>
<li>Big-O 표기법을 사용: <code>O(1)</code>, <code>O(log n)</code>, <code>O(n)</code>, <code>O(n log n)</code>, <code>O(n^2)</code> 등  </li>
</ul>
</li>
<li><p><strong>공간 복잡도 (Space Complexity)</strong>  </p>
<ul>
<li>알고리즘 실행 시 필요한 메모리 사용량 분석  </li>
<li>예: 재귀 호출의 스택 메모리, 추가 배열/자료구조 사용량 등  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-알고리즘-문제해결-5단계">2. 알고리즘 문제해결 5단계</h2>
<h3 id="1단계-문제-이해-및-요구사항-분석">1단계: 문제 이해 및 요구사항 분석</h3>
<ul>
<li>문제를 정확히 읽고 해석하기  </li>
<li>입력과 출력 형식, 제약 조건 정리  </li>
<li>예시 입력/출력으로 문제 의도 파악  </li>
</ul>
<h3 id="2단계-접근-방법-구상하기">2단계: 접근 방법 구상하기</h3>
<ul>
<li>유사 문제/패턴 떠올리기  </li>
<li>사용할 수 있는 알고리즘 유형 고려하기 (완전 탐색, DP, Greedy 등)  </li>
<li>해결 아이디어의 시간·공간 효율성 예측  </li>
</ul>
<h3 id="3단계-세부-설계-및-검토">3단계: 세부 설계 및 검토</h3>
<ul>
<li>알고리즘 절차를 단계별로 구체화  </li>
<li>발생할 수 있는 예외 상황 점검  </li>
<li>극단적인 입력값(엣지 케이스) 고려  </li>
<li><strong>시간/공간 복잡도 분석 필수</strong>  </li>
</ul>
<h3 id="4단계-코드-작성-및-구현">4단계: 코드 작성 및 구현</h3>
<ul>
<li>설계한 알고리즘을 실제 코드로 변환  </li>
<li>가독성 있는 변수명, 함수명 사용  </li>
<li>구현 과정에서 알고리즘의 효율성 점검  </li>
</ul>
<h3 id="5단계-테스트와-디버깅">5단계: 테스트와 디버깅</h3>
<ul>
<li>다양한 테스트 케이스 실행 (일반, 극단, 예외 상황)  </li>
<li>오류 발생 시 디버깅  </li>
<li>실행 시간이 길 경우 알고리즘/자료구조 최적화  <ul>
<li>중복 연산 제거 (메모이제이션)  </li>
<li>불필요한 탐색 제거 (가지치기)  </li>
<li>적절한 자료구조 선택 (예: 해시, 우선순위 큐)  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-알고리즘-유형">3. 알고리즘 유형</h2>
<ul>
<li><p><strong>구현 (Implementation) &amp; 시뮬레이션 (Simulation)</strong>  </p>
<ul>
<li>단순히 문제의 요구사항을 그대로 코드로 옮겨 구현하는 방식  </li>
</ul>
</li>
<li><p><strong>완전 탐색 (Brute Force)</strong>  </p>
<ul>
<li>가능한 모든 경우를 탐색 (효율성은 낮지만 정답 보장)  </li>
</ul>
</li>
<li><p><strong>그리디 (Greedy) 알고리즘</strong>  </p>
<ul>
<li>매 순간 최적이라고 생각되는 선택을 반복 → 전역 최적 보장 여부는 문제 특성에 따라 다름  </li>
</ul>
</li>
<li><p><strong>백트래킹 (Backtracking)</strong>  </p>
<ul>
<li>모든 경우를 탐색하되, 불가능한 경우는 조기에 탐색 중단 (가지치기)  </li>
</ul>
</li>
<li><p><strong>분할 정복 (Divide and Conquer)</strong>  </p>
<ul>
<li>문제를 작은 부분 문제로 나눠 해결 후, 결과를 합치는 방식 (예: 병합 정렬, 퀵 정렬)  </li>
</ul>
</li>
<li><p><strong>동적 계획법 (Dynamic Programming, DP)</strong>  </p>
<ul>
<li>부분 문제의 해를 저장하여 중복 계산을 줄임 (메모이제이션, 점화식 기반)  </li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-좋은-알고리즘을-선택하는-기준">4. 좋은 알고리즘을 선택하는 기준</h2>
<ol>
<li><strong>입력 크기 (n)</strong>: n이 작으면 단순 탐색도 가능, 크면 최적화 필요  </li>
<li><strong>제약 조건</strong>: 시간 제한, 메모리 제한 고려  </li>
<li><strong>정확성 vs 효율성</strong>: 빠르지만 근사값만 주는 알고리즘도 선택 가능  </li>
<li><strong>문제 유형</strong>: 최단 경로 → 다익스트라, 최적화 → DP/Greedy, 탐색 → DFS/BFS  </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 자료구조]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Mon, 29 Sep 2025 12:02:17 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-29-스파르타-코딩클럽-21일차">2025 09 29 스파르타 코딩클럽 21일차</h1>
<h2 id="1-스택stack">1. 스택(Stack)</h2>
<h3 id="개념과-특징">개념과 특징</h3>
<ul>
<li>LIFO (Last In, First Out) 구조 → 마지막에 들어온 데이터가 먼저
나간다.</li>
<li>대표적 활용: 함수 호출(콜 스택), 실행 취소(Undo), 수식 계산 등</li>
</ul>
<h3 id="기본-연산">기본 연산</h3>
<ul>
<li><code>push(item)</code>: 데이터 삽입</li>
<li><code>pop()</code>: 데이터 삭제 및 반환</li>
<li><code>peek()</code>: 최상단(top)의 데이터 확인 (삭제 X)</li>
<li><code>isEmpty()</code>: 비어있는지 확인</li>
</ul>
<h3 id="장단점">장단점</h3>
<ul>
<li>장점: 구현 단순, 빠른 연산 (O(1))</li>
<li>단점: 메모리 제한(배열 기반), 중간 요소 접근 불가</li>
</ul>
<h3 id="응용-예시">응용 예시</h3>
<ul>
<li>문자열 뒤집기</li>
<li>웹 브라우저 방문 기록 (뒤로/앞으로)</li>
<li>DFS(깊이 우선 탐색)</li>
<li>괄호 유효성 검사</li>
</ul>
<hr>
<h2 id="2-큐queue">2. 큐(Queue)</h2>
<h3 id="개념과-특징-1">개념과 특징</h3>
<ul>
<li>FIFO (First In, First Out) 구조 → 먼저 들어온 데이터가 먼저 나간다.</li>
<li>대표적 활용: 대기열(프린터, 은행 번호표), 프로세스 스케줄링</li>
</ul>
<h3 id="기본-연산-1">기본 연산</h3>
<ul>
<li><code>enqueue(item)</code>: 데이터 삽입 (뒤에서)</li>
<li><code>dequeue()</code>: 데이터 삭제 및 반환 (앞에서)</li>
<li><code>peek()</code>: front 데이터 확인</li>
<li><code>isEmpty()</code>: 비어있는지 확인</li>
</ul>
<h3 id="큐의-변형">큐의 변형</h3>
<ul>
<li><strong>원형 큐(Circular Queue)</strong>: 공간 낭비 해결</li>
<li><strong>Deque(Double Ended Queue)</strong>: 양쪽에서 삽입/삭제 가능</li>
<li><strong>Priority Queue</strong>: 우선순위에 따라 꺼내는 순서 결정</li>
</ul>
<h3 id="장단점-1">장단점</h3>
<ul>
<li>장점: 공정한 순서 보장, 구현 단순</li>
<li>단점: 배열 기반 구현 시 메모리 낭비 가능</li>
</ul>
<h3 id="응용-예시-1">응용 예시</h3>
<ul>
<li>BFS(너비 우선 탐색)</li>
<li>운영체제 프로세스 스케줄링</li>
<li>네트워크 데이터 버퍼 관리</li>
</ul>
<hr>
<h2 id="3-stack-vs-queue-비교">3. Stack vs Queue 비교</h2>
<p>  구분          Stack (스택)          Queue (큐)</p>
<hr>
<p>  구조          LIFO (후입선출)       FIFO (선입선출)
  삽입 위치     Top (맨 위)           Rear (뒤)
  삭제 위치     Top (맨 위)           Front (앞)
  시간 복잡도   O(1)                  O(1)
  주요 응용     재귀, 되돌리기, DFS   대기열, BFS, 스케줄링</p>
<hr>
<h2 id="4-자바-설계-예시">4. 자바 설계 예시</h2>
<h3 id="stack-구현-arraylist-활용">Stack 구현 (ArrayList 활용)</h3>
<pre><code class="language-java">class Stack {
    private List&lt;Integer&gt; list = new ArrayList&lt;&gt;();

    public void push(int value) {
        list.add(value);
    }

    public int pop() {
        if (list.isEmpty()) throw new RuntimeException(&quot;Stack is empty&quot;);
        return list.remove(list.size() - 1);
    }

    public int peek() {
        if (list.isEmpty()) throw new RuntimeException(&quot;Stack is empty&quot;);
        return list.get(list.size() - 1);
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }
}</code></pre>
<h3 id="queue-구현-linkedlist-활용">Queue 구현 (LinkedList 활용)</h3>
<pre><code class="language-java">class Queue {
    private LinkedList&lt;Integer&gt; list = new LinkedList&lt;&gt;();

    public void enqueue(int value) {
        list.addLast(value);
    }

    public int dequeue() {
        if (list.isEmpty()) throw new RuntimeException(&quot;Queue is empty&quot;);
        return list.removeFirst();
    }

    public int peek() {
        if (list.isEmpty()) throw new RuntimeException(&quot;Queue is empty&quot;);
        return list.getFirst();
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 기본 문법(4)]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%954</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%954</guid>
            <pubDate>Mon, 22 Sep 2025 12:05:08 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-17-스파르타-코딩클럽-13일차">2025 09 17 스파르타 코딩클럽 13일차</h1>
<h2 id="1-인터페이스-interface">1. 인터페이스 (Interface)</h2>
<ul>
<li><strong>설계의 표준</strong>: 클래스가 따라야 할 최소한의 공통 규칙을 정의.\</li>
<li><strong>목적</strong>: 일관성을 유지하면서 클래스가 고유한 특색을 확장할 수
있도록 함.\</li>
<li><strong>특징</strong>:<ul>
<li>모든 메서드는 기본적으로 <code>public abstract</code> (Java 8 이후
<code>default</code>, <code>static</code> 메서드 허용).\</li>
<li>모든 변수는 <code>public static final</code> (상수).\</li>
<li>다중 상속 가능 (<code>interface A extends B, C</code>).\</li>
</ul>
</li>
<li><strong>사용 키워드</strong>:<ul>
<li><code>implements</code> → 클래스가 인터페이스를 구현할 때.\</li>
<li><code>extends</code> → 클래스 → 클래스 상속 / 인터페이스 → 인터페이스 상속.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-캡슐화-encapsulation">2. 캡슐화 (Encapsulation)</h2>
<ul>
<li><strong>정의</strong>: 외부에서 직접 접근하지 못하게 하고, 접근을 제어하는 것.\</li>
<li><strong>방법</strong>:<ul>
<li>멤버 변수는 <code>private</code>으로 선언.\</li>
<li><code>getter</code>, <code>setter</code> 메서드를 통해 접근.\</li>
</ul>
</li>
<li><strong>장점</strong>: 데이터 보호, 유지보수 용이, 코드 안정성 향상.</li>
</ul>
<hr>
<h2 id="3-상속-inheritance">3. 상속 (Inheritance)</h2>
<ul>
<li><strong>정의</strong>: 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아
사용하는 것.\</li>
<li><strong>목적</strong>: 코드 재사용성, 확장성.\</li>
<li><strong>관련 키워드</strong>:<ul>
<li><code>super</code> → 부모의 생성자, 변수, 메서드 호출.\</li>
<li>메소드 오버라이딩(Overriding): 부모 메서드를 자식이 재정의.\</li>
<li>접근 제어자는 부모보다 <strong>좁게는 불가능</strong>, 더 넓게는 가능.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-추상-클래스-abstract-class">4. 추상 클래스 (Abstract Class)</h2>
<ul>
<li><strong>정의</strong>: 공통 기능을 제공하면서, 일부 메서드 구현을 강제.\</li>
<li><strong>특징</strong>:<ul>
<li><code>abstract</code> 키워드 사용.\</li>
<li>추상 메서드는 구현이 없음 → 반드시 자식 클래스에서 구현해야 함.\</li>
<li>인스턴스 생성 불가 (new 불가).</li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-추상화-abstraction">5. 추상화 (Abstraction)</h2>
<ul>
<li><strong>정의</strong>: 불필요한 세부 사항은 감추고 본질적인 것만 표현하는 것.\</li>
<li><strong>목적</strong>: 복잡성을 줄이고 핵심 로직에 집중.\</li>
<li><strong>방법</strong>:<ul>
<li>인터페이스, 추상 클래스 사용.\</li>
<li>예: 자동차 → &quot;운전한다&quot;는 기능만 신경, 엔진 구조는 감춤.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="6-다형성-polymorphism">6. 다형성 (Polymorphism)</h2>
<ul>
<li><strong>정의</strong>: 하나의 타입으로 여러 객체를 다룰 수 있는 성질.\</li>
<li><strong>조건</strong>: 상속과 오버라이딩이 전제.\</li>
<li><strong>형변환</strong>:<ul>
<li>Upcasting: 자식 → 부모 (자동 형변환, 자식 고유 기능 X).\</li>
<li>Downcasting: 부모 → 자식 (강제 형변환 필요).</li>
</ul>
</li>
</ul>
<pre><code class="language-java">Parent p = new Child(); // 업캐스팅
Child c = (Child) p;    // 다운캐스팅</code></pre>
<hr>
<h2 id="7-예외-처리-exception-handling">7. 예외 처리 (Exception Handling)</h2>
<ul>
<li><strong>try-catch</strong>: 예외 발생 시 프로그램 비정상 종료를 막음.\</li>
<li><strong>throw</strong>: 의도적으로 예외 발생 가능.\</li>
<li><strong>예외 구분</strong>:<ul>
<li><code>RuntimeException</code> (Unchecked Exception): 컴파일러가 예외처리
강제 X.\</li>
<li><code>Exception</code> (Checked Exception): 반드시 예외처리 필요. 미처리 시
컴파일 에러.\</li>
</ul>
</li>
<li><strong>throws 키워드</strong>: 현재 메소드가 아닌 상위 메소드로 예외를 전달.\</li>
<li><strong>예외 전파</strong>: 예외는 발생 지점에서 처리되지 않으면 상위 메소드로
계속 전파됨.</li>
</ul>
<hr>
<h2 id="8-optional">8. Optional</h2>
<ul>
<li><strong>목적</strong>: <code>null</code> 안전하게 다루기 위한 컨테이너 객체.\</li>
<li><strong>장점</strong>: <code>NullPointerException</code> 방지.\</li>
<li><strong>특징</strong>: 값이 있을 수도 있고, 없을 수도 있음.</li>
</ul>
<hr>
<h2 id="9-컬렉션-collection">9. 컬렉션 (Collection)</h2>
<ul>
<li><strong>정의</strong>: 자바에서 제공하는 자료구조 모음.</li>
</ul>
<h3 id="arraylist">ArrayList</h3>
<ul>
<li>크기 가변적, 중복 가능, 순서 유지.\</li>
<li><code>add</code>, <code>get</code>, <code>remove</code> 메소드 사용.</li>
</ul>
<h3 id="hashset">HashSet</h3>
<ul>
<li>순서 보장 X, 중복 불가.\</li>
<li><code>add</code>, <code>remove</code> 사용 가능.\</li>
<li><code>get</code> 불가능 (순서 없음).</li>
</ul>
<h3 id="hashmap">HashMap</h3>
<ul>
<li>키-값 구조, 키 중복 불가.\</li>
<li><code>put</code>, <code>get</code>, <code>remove</code>, <code>keySet</code>, <code>values</code> 메소드 제공.</li>
</ul>
<hr>
<h2 id="10-제네릭-generics">10. 제네릭 (Generics)</h2>
<ul>
<li><strong>정의</strong>: 클래스, 메소드 등에 타입 매개변수 <code>&lt;T&gt;</code>를 지정.\</li>
<li><strong>장점</strong>: 코드 재사용성, 타입 안정성.\</li>
<li><strong>특징</strong>:<ul>
<li>컴파일 시 타입 체크.\</li>
<li>다운캐스팅 필요 없음.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">class Box&lt;T&gt; {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}</code></pre>
<hr>
<h2 id="11-익명-클래스--람다">11. 익명 클래스 &amp; 람다</h2>
<ul>
<li><strong>익명 클래스</strong>: 이름이 없는 클래스, 일회성 정의.\</li>
<li><strong>람다식</strong>: 익명 클래스를 더 간결하게 표현.\</li>
<li><strong>함수형 인터페이스</strong>: 추상 메서드 1개만 선언 가능.</li>
</ul>
<pre><code class="language-java">Runnable r = () -&gt; System.out.println(&quot;람다식 실행&quot;);
new Thread(r).start();</code></pre>
<hr>
<h2 id="12-스트림-stream">12. 스트림 (Stream)</h2>
<ul>
<li><strong>목적</strong>: 데이터를 효율적으로 처리하는 흐름 제공.\</li>
<li><strong>단계</strong>:<ol>
<li>데이터 준비 (<code>stream()</code>, <code>parallelStream()</code>)\</li>
<li>중간 연산 (<code>map</code>, <code>filter</code>, <code>sorted</code>)\</li>
<li>최종 연산 (<code>collect</code>, <code>forEach</code>, <code>count</code>)</li>
</ol>
</li>
</ul>
<pre><code class="language-java">List&lt;String&gt; names = Arrays.asList(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;);
names.stream().filter(s -&gt; s.equals(&quot;B&quot;)).forEach(System.out::println);</code></pre>
<hr>
<h2 id="13-쓰레드-thread">13. 쓰레드 (Thread)</h2>
<ul>
<li><strong>정의</strong>: 독립적으로 실행되는 작업 단위.\</li>
<li><strong>싱글 쓰레드</strong>: 한 번에 하나의 작업.\</li>
<li><strong>멀티 쓰레드</strong>: 병렬 작업 가능 → 성능 향상.</li>
</ul>
<h3 id="구현-방법">구현 방법</h3>
<ol>
<li><code>Thread</code> 클래스 상속 → <code>run()</code> 오버라이드.\</li>
<li><code>Runnable</code> 인터페이스 구현 (권장).</li>
</ol>
<h3 id="주의사항">주의사항</h3>
<ul>
<li>실행 시 반드시 <code>start()</code> 사용.\</li>
<li><code>join()</code> → 특정 쓰레드 종료까지 대기.</li>
</ul>
<pre><code class="language-java">class MyThread extends Thread {
    public void run() {
        System.out.println(&quot;Thread 실행&quot;);
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        t.join();
        System.out.println(&quot;메인 종료&quot;);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 기본 문법(3)]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%953</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%953</guid>
            <pubDate>Wed, 17 Sep 2025 11:46:40 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-17-스파르타-코딩클럽-13일차">2025 09 17 스파르타 코딩클럽 13일차</h1>
<h2 id="클래스와-객체">클래스와 객체</h2>
<ul>
<li><strong>클래스(Class)</strong>: 객체를 생성하기 위한 설계도.</li>
<li><strong>객체(Object)</strong>: 클래스에서 생성된 실체.</li>
<li><strong>인스턴스화(객체화)</strong>: <code>new</code> 키워드를 사용하여 객체를 생성하는 것.</li>
</ul>
<h3 id="클래스-내부-구성">클래스 내부 구성</h3>
<ul>
<li><strong>속성(Field)</strong>: 객체의 상태(데이터)를 저장.</li>
<li><strong>생성자(Constructor)</strong>: 객체 생성 시 호출되는 특별한 메서드.<ul>
<li>생성자를 정의하지 않으면 <strong>컴파일러가 기본 생성자(매개변수 없는
생성자)</strong>를 자동 제공.</li>
<li>직접 생성자를 정의하면 기본 생성자는 자동으로 제공되지 않음.</li>
<li>매개변수를 맞추지 않으면 컴파일 에러 발생.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-jvm-메모리-영역">2. JVM 메모리 영역</h2>
<p>자바 프로그램 실행 시 JVM은 메모리를 다음 영역으로 나눔:</p>
<ul>
<li><strong>Method Area (메서드 영역)</strong><ul>
<li>클래스 정보(메타데이터), static 변수, 상수, 메서드 코드 저장.</li>
</ul>
</li>
<li><strong>Stack Area (스택 영역)</strong><ul>
<li>메서드 호출 시 지역 변수, 매개변수 저장.</li>
<li><strong>LIFO(Last-In First-Out)</strong> 구조.</li>
</ul>
</li>
<li><strong>Heap Area (힙 영역)</strong><ul>
<li><code>new</code> 키워드로 생성된 객체와 배열 저장.</li>
<li>변수에는 객체의 <strong>주소(참조 값)</strong> 가 저장됨.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-wrapper-class">3. Wrapper Class</h2>
<ul>
<li><strong>정의</strong>: 기본 자료형(Primitive type)을 객체로 다룰 수 있도록 감싼
클래스.</li>
<li><strong>종류</strong>:<ul>
<li><code>int → Integer</code></li>
<li><code>double → Double</code></li>
<li><code>boolean → Boolean</code></li>
<li><code>char → Character</code> 등</li>
</ul>
</li>
</ul>
<h3 id="사용-이유">사용 이유</h3>
<ul>
<li>기본형을 객체처럼 다루기 위함.</li>
<li>제네릭, 컬렉션, 매개변수 등에서 객체 타입만 지원할 때 필요.</li>
<li>데이터 처리 기능(메서드 제공)이 있어 편리.</li>
</ul>
<h3 id="오토박싱--언박싱">오토박싱 &amp; 언박싱</h3>
<ul>
<li><strong>오토박싱(Auto-boxing)</strong>: 기본형 → 래퍼 클래스</li>
<li><strong>언박싱(Unboxing)</strong>: 래퍼 클래스 → 기본형</li>
</ul>
<pre><code class="language-java">int a = 10;
Integer b = a;   // 오토박싱
int c = b;       // 언박싱</code></pre>
<p>래퍼 클래스는 힙 영역의 객체를 참조하기 때문에 기본형보다 처리 속도가
느릴 수 있음.
빠른 연산에는 기본형을 쓰는 것이 권장됨.</p>
<hr>
<h2 id="4-static">4. static</h2>
<ul>
<li><strong>정의</strong>: 클래스 차원에서 공유되는 멤버.</li>
<li><strong>특징</strong>:<ul>
<li>객체 생성 없이 <code>클래스명.변수,메서드</code> 로 접근 가능.</li>
<li>프로그램 시작 시 메모리에 올라가고 종료될 때까지 유지됨.</li>
<li>메서드 영역(Method Area)에 저장됨.</li>
</ul>
</li>
<li><strong>인스턴스 멤버</strong>: 객체마다 독립적으로 생성, 힙 영역에 저장됨.</li>
<li>static은 남발 시 메모리 낭비와 설계 혼란을 줄 수 있으므로 꼭 필요할
때만 사용.</li>
</ul>
<hr>
<h2 id="5-final">5. final</h2>
<ul>
<li><strong>final 변수</strong>: 상수(값 변경 불가).</li>
<li><strong>final 메서드</strong>: 오버라이딩 불가.</li>
<li><strong>final 클래스</strong>: 상속 불가.</li>
</ul>
<h3 id="불변-객체">불변 객체</h3>
<ul>
<li>내부 상태를 변경할 수 없는 객체.</li>
<li><code>final</code> + <strong>setter 메서드 없음</strong> 으로 설계.</li>
<li>대표 예시: <code>String</code>, <code>Integer</code> 등 래퍼 클래스.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 기본 문법(2)]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%952</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%952</guid>
            <pubDate>Tue, 16 Sep 2025 12:12:03 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-16-스파르타-코딩클럽-12일차">2025 09 16 스파르타 코딩클럽 12일차</h1>
<h2 id="1-형-변환">1. 형 변환</h2>
<h3 id="1-다운캐스팅-명시적-형-변환">(1) 다운캐스팅 (명시적 형 변환)</h3>
<ul>
<li>큰 데이터 타입 → 작은 데이터 타입</li>
<li>직접 형 변환을 해주어야 함  </li>
<li><strong>데이터 손실</strong> 발생 가능  </li>
</ul>
<pre><code class="language-java">double d = 3.14;
int i = (int) d; // 3 (소수점 이하 손실)</code></pre>
<hr>
<h3 id="2-업캐스팅-자동-형-변환">(2) 업캐스팅 (자동 형 변환)</h3>
<ul>
<li>작은 데이터 타입 → 큰 데이터 타입</li>
<li>자동으로 변환되므로 별도의 코드 불필요  </li>
</ul>
<pre><code class="language-java">int a = 10;
double b = a; // 10.0</code></pre>
<hr>
<h2 id="2-연산">2. 연산</h2>
<h3 id="1--모듈러-연산">(1) % 모듈러 연산</h3>
<ul>
<li>나머지를 구하는 연산</li>
<li>시간 계산, 짝/홀수 판별 등에 자주 사용  </li>
</ul>
<pre><code class="language-java">int n = 10;
System.out.println(n % 2); // 0 → 짝수
System.out.println(n % 3); // 1</code></pre>
<hr>
<h3 id="2-연산자-우선순위">(2) 연산자 우선순위</h3>
<ul>
<li>산술 → 비교 → 논리 → 대입  </li>
<li>논리 연산자: <code>!</code> → <code>&amp;&amp;</code> → <code>||</code></li>
</ul>
<hr>
<h3 id="3-문자열-비교">(3) 문자열 비교</h3>
<ul>
<li>문자열은 <code>==</code> (메모리 주소 비교 ) 대신 <code>.equals()</code> 사용  </li>
</ul>
<pre><code class="language-java">String s1 = &quot;hello&quot;;
String s2 = new String(&quot;hello&quot;);

System.out.println(s1 == s2);       // false (주소 비교)
System.out.println(s1.equals(s2));  // true (값 비교)</code></pre>
<hr>
<h2 id="3-배열">3. 배열</h2>
<h3 id="1-정적-배열">(1) 정적 배열</h3>
<ul>
<li>크기 고정, 한 번 정하면 변경 불가  </li>
</ul>
<pre><code class="language-java">int[] arr = new int[3]; // 크기 3</code></pre>
<hr>
<h3 id="2-동적-배열">(2) 동적 배열</h3>
<ul>
<li>크기 변경 가능  </li>
<li>Java에서는 <code>ArrayList</code>로 구현  </li>
</ul>
<pre><code class="language-java">import java.util.ArrayList;

ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
list.add(10);
list.add(20);
System.out.println(list); // [10, 20]</code></pre>
<hr>
<h2 id="4-반복문">4. 반복문</h2>
<h3 id="1-for문">(1) for문</h3>
<ul>
<li>반복 횟수가 명확할 때 사용  </li>
</ul>
<pre><code class="language-java">for (int i = 0; i &lt; 5; i++) {
    System.out.println(i);
}</code></pre>
<hr>
<h3 id="2-while문">(2) while문</h3>
<ul>
<li>조건이 참일 때 반복 실행  </li>
</ul>
<pre><code class="language-java">int i = 0;
while (i &lt; 5) {
    System.out.println(i);
    i++;
}</code></pre>
<hr>
<h3 id="3-do-while문">(3) do-while문</h3>
<ul>
<li>최소 1회 실행 보장  </li>
</ul>
<pre><code class="language-java">int i = 0;
do {
    System.out.println(i);
    i++;
} while (i &lt; 5);</code></pre>
<hr>
<h3 id="4-향상된-for문-for-each">(4) 향상된 for문 (for-each)</h3>
<ul>
<li>배열이나 컬렉션 전체를 순회할 때 사용  </li>
</ul>
<pre><code class="language-java">int[] numbers = {1, 2, 3, 4, 5};

for (int n : numbers) {
    System.out.println(n);
}</code></pre>
<hr>
<h2 id="5-조건문">5. 조건문</h2>
<h3 id="1-if문">(1) if문</h3>
<ul>
<li>조건이 참일 때만 실행  </li>
</ul>
<pre><code class="language-java">int n = 10;

if (n &gt; 0) {
    System.out.println(&quot;양수&quot;);
}</code></pre>
<hr>
<h3 id="2-if-else문">(2) if-else문</h3>
<ul>
<li>조건에 따라 두 가지 경우 처리  </li>
</ul>
<pre><code class="language-java">int n = -5;

if (n &gt;= 0) {
    System.out.println(&quot;양수&quot;);
} else {
    System.out.println(&quot;음수&quot;);
}</code></pre>
<hr>
<h3 id="3-if-else-if-else문">(3) if-else if-else문</h3>
<ul>
<li>여러 조건 분기 처리  </li>
</ul>
<pre><code class="language-java">int score = 85;

if (score &gt;= 90) {
    System.out.println(&quot;A&quot;);
} else if (score &gt;= 80) {
    System.out.println(&quot;B&quot;);
} else {
    System.out.println(&quot;C 이하&quot;);
}</code></pre>
<hr>
<h3 id="4-switch문">(4) switch문</h3>
<ul>
<li>값에 따라 분기 처리  </li>
</ul>
<pre><code class="language-java">int day = 3;

switch (day) {
    case 1:
        System.out.println(&quot;월요일&quot;);
        break;
    case 2:
        System.out.println(&quot;화요일&quot;);
        break;
    case 3:
        System.out.println(&quot;수요일&quot;);
        break;
    default:
        System.out.println(&quot;기타 요일&quot;);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 기본 문법(1)]]></title>
            <link>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%951</link>
            <guid>https://velog.io/@j_blin/%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%951</guid>
            <pubDate>Mon, 15 Sep 2025 13:45:44 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-15-스파르타-코딩클럽-11일차">2025 09 15 스파르타 코딩클럽 11일차</h1>
<p>자바기본 문법에 대한 공부를 진행했고, 그것에 대한 TIL을 적었다.</p>
<h2 id="1-자바의-플랫폼-독립성">1. 자바의 플랫폼 독립성</h2>
<ul>
<li><strong>작성 → 컴파일 → 실행 과정</strong><ol>
<li>.java (자바 소스파일)  </li>
<li><strong>Javac 컴파일러</strong> → <code>.class</code> (바이트코드)  </li>
<li><strong>JVM(Java Virtual Machine)</strong> → OS에 맞게 실행  </li>
</ol>
</li>
</ul>
<p><strong>핵심 이유</strong> : 바이트코드는 특정 운영체제에 종속되지 않고, JVM만 있으면 실행 가능하다.  </p>
<hr>
<h2 id="2-컴퓨터의-기억-장치">2. 컴퓨터의 기억 장치</h2>
<h3 id="1-주-기억-장치-ram">(1) 주 기억 장치 (RAM)</h3>
<ul>
<li>프로그램 실행 중 데이터 임시 저장</li>
<li><strong>휘발성</strong>: 전원 끄면 내용 사라짐</li>
<li>빠른 속도로 CPU와 통신 가능</li>
</ul>
<h3 id="2-보조-기억-장치-hddssd">(2) 보조 기억 장치 (HDD/SSD)</h3>
<ul>
<li><strong>영구 저장 장치</strong></li>
<li>RAM보다는 속도 느림</li>
</ul>
<hr>
<h2 id="3-메모리-관리-방식">3. 메모리 관리 방식</h2>
<ul>
<li>메모리는 <strong>1바이트 단위</strong>로 주소가 매겨져 관리됨</li>
<li><strong>1바이트 = 8비트</strong>  </li>
<li><strong>비트(bit)</strong>: 메모리 최소 단위 (0 또는 1 저장)</li>
<li>저장 공간은 1비트 증가 시마다 2배 확장 (2진수 원리)</li>
</ul>
<hr>
<h2 id="4-자바-기본-자료형-primitive-types">4. 자바 기본 자료형 (Primitive Types)</h2>
<table>
<thead>
<tr>
<th>자료형</th>
<th>크기 (byte)</th>
<th>값의 범위</th>
</tr>
</thead>
<tbody><tr>
<td><strong>byte</strong></td>
<td>1</td>
<td>-128 ~ 127</td>
</tr>
<tr>
<td><strong>short</strong></td>
<td>2</td>
<td>-32,768 ~ 32,767</td>
</tr>
<tr>
<td><strong>int</strong></td>
<td>4</td>
<td>-2,147,483,648 ~ 2,147,483,647 (약 ±21억)</td>
</tr>
<tr>
<td><strong>long</strong></td>
<td>8</td>
<td>-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 (약 ±9.2경)</td>
</tr>
<tr>
<td><strong>float</strong></td>
<td>4</td>
<td>약 ±3.4E38 (소수점 7자리 정밀도)</td>
</tr>
<tr>
<td><strong>double</strong></td>
<td>8</td>
<td>약 ±1.7E308 (소수점 15자리 정밀도)</td>
</tr>
<tr>
<td><strong>char</strong></td>
<td>2</td>
<td>0 ~ 65,535 (Unicode 문자 저장)</td>
</tr>
<tr>
<td><strong>boolean</strong></td>
<td>1 (이론상 bit, JVM 구현에 따라 다름)</td>
<td>true / false</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-변수의-특징">5. 변수의 특징</h2>
<h3 id="선언과-초기화">선언과 초기화</h3>
<pre><code class="language-java">int age = 25;
double pi = 3.14;
String name = &quot;홍길동&quot;;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[ConcurrentHashMap이란]]></title>
            <link>https://velog.io/@j_blin/ConcurrentHashMap%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@j_blin/ConcurrentHashMap%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Wed, 10 Sep 2025 13:07:42 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-10-스파르타-코딩클럽-8일차">2025 09 10 스파르타 코딩클럽 8일차</h1>
<p>오늘은 전에 사둔 spring 강의를 마저 보면서 새로운 해시맵인 ConcurrnetHashMap을 알게 되어서 ConcurrnetHashMap이 무엇인지 알아볼 것이다.</p>
<hr>
<h2 id="1-map-소개">1. Map 소개</h2>
<p><code>Map</code>은 Java 컬렉션 프레임워크의 인터페이스로, <strong>키(Key)와 값(Value) 쌍</strong>을 저장하는 자료구조입니다.  </p>
<ul>
<li>각 <strong>키는 유일(unique)</strong>해야 하고, 값은 중복될 수 있습니다.  </li>
<li>대표적인 메서드:<ul>
<li><code>put(key, value)</code> : 데이터 추가</li>
<li><code>get(key)</code> : 키에 해당하는 값 조회</li>
<li><code>remove(key)</code> : 데이터 삭제</li>
<li><code>containsKey(key)</code> : 특정 키 존재 여부 확인</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-map의-주요-구현체">2. Map의 주요 구현체</h2>
<p>Java에는 다양한 <code>Map</code> 구현체가 존재하며, 각기 다른 특성과 사용 목적이 있습니다.</p>
<h3 id="1-hashmap">(1) HashMap</h3>
<ul>
<li>가장 일반적으로 사용되는 <code>Map</code> 구현체</li>
<li>해시 테이블을 기반으로 데이터 저장</li>
<li>순서를 보장하지 않음</li>
<li><code>null</code> 키 1개와 <code>null</code> 값 여러 개 허용</li>
</ul>
<h3 id="2-linkedhashmap">(2) LinkedHashMap</h3>
<ul>
<li>입력된 순서(또는 접근 순서)를 유지</li>
<li>순서 보장이 필요한 경우 사용</li>
</ul>
<h3 id="3-treemap">(3) TreeMap</h3>
<ul>
<li>키를 자동으로 정렬 (기본은 오름차순)</li>
<li>내부적으로 Red-Black Tree 사용</li>
<li>범위 검색이나 정렬이 필요할 때 적합</li>
</ul>
<h3 id="4-hashtable">(4) Hashtable</h3>
<ul>
<li><code>HashMap</code>과 유사하지만, 모든 메서드가 동기화 처리됨</li>
<li>레거시 클래스라 잘 사용하지 않음</li>
</ul>
<h3 id="5-concurrenthashmap">(5) ConcurrentHashMap</h3>
<ul>
<li>멀티스레드 환경에서 안전한 <code>Map</code></li>
<li><code>Hashtable</code>보다 효율적 (버킷 단위로 락을 걸어 성능 향상)</li>
<li><code>null</code> 키와 <code>null</code> 값은 허용하지 않음</li>
</ul>
<hr>
<h2 id="3-concurrenthashmap-소개">3. ConcurrentHashMap 소개</h2>
<h3 id="특징">특징</h3>
<ul>
<li><strong>스레드 안전(Thread-safe)</strong><br>여러 스레드가 동시에 접근해도 데이터가 깨지지 않음</li>
<li><strong>부분 락(lock striping)</strong><br>전체 맵이 아니라 일부 버킷에만 락을 걸어 성능을 높임</li>
<li><strong>읽기 연산은 락 없이</strong><br>대부분의 <code>get()</code>은 락 없이 진행 → 빠른 조회 가능</li>
<li><strong>null 불가</strong><br><code>null</code> 키와 <code>null</code> 값은 허용하지 않음</li>
</ul>
<h3 id="언제-사용할까">언제 사용할까?</h3>
<ul>
<li>멀티스레드 환경에서 공유 데이터를 저장/조회할 때</li>
<li>동시성 문제가 생기지 않으면서 빠른 성능이 필요한 경우</li>
<li>예: 캐시(Cache) 시스템, 스레드 간 데이터 공유</li>
</ul>
<hr>
<h2 id="4-코드-예시">4. 코드 예시</h2>
<pre><code class="language-java">import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        // ConcurrentHashMap 생성
        ConcurrentHashMap&lt;String, Integer&gt; map = new ConcurrentHashMap&lt;&gt;();

        // 데이터 추가
        map.put(&quot;apple&quot;, 10);
        map.put(&quot;banana&quot;, 20);

        // 데이터 조회
        System.out.println(&quot;apple: &quot; + map.get(&quot;apple&quot;)); // apple: 10

        // 값 수정 (동기적으로 안전하게 처리)
        map.compute(&quot;apple&quot;, (k, v) -&gt; v + 1);
        System.out.println(&quot;apple after update: &quot; + map.get(&quot;apple&quot;)); // apple after update: 11

        // 키 존재 여부 확인
        if (map.containsKey(&quot;banana&quot;)) {
            System.out.println(&quot;banana exists!&quot;);
        }

        // 멀티스레드 환경에서 안전하게 접근 가능
        Runnable task = () -&gt; {
            for (int i = 0; i &lt; 5; i++) {
                map.merge(&quot;banana&quot;, 1, Integer::sum);
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[2주차 과제(2)]]></title>
            <link>https://velog.io/@j_blin/2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C2</link>
            <guid>https://velog.io/@j_blin/2%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C2</guid>
            <pubDate>Tue, 09 Sep 2025 12:00:21 GMT</pubDate>
            <description><![CDATA[<h1 id="2025-09-09-스파르타-코딩클럽-7일차">2025 09 09 스파르타 코딩클럽 7일차</h1>
<p>오늘은 남은 과제 2개 진행했고, 진행하면서 발생한 에러 정리와 해결한 방법을 공유한다.</p>
<hr>
<h3 id="1-jsp-파일-위치-문제">1. JSP 파일 위치 문제</h3>
<p><strong>문제</strong>:</p>
<ul>
<li>JSP 파일이 <code>src/main/resources/web/WEB-INF/jsp/</code>에 위치</li>
<li>Spring Boot가 JSP를 찾지 못해 404 오류 발생</li>
</ul>
<p><strong>해결</strong>:</p>
<pre><code class="language-bash"># 올바른 위치로 JSP 파일 이동
mkdir -p src/main/webapp/WEB-INF/jsp
cp -r src/main/resources/web/WEB-INF/jsp/* src/main/webapp/WEB-INF/jsp/</code></pre>
<p><strong>원인</strong>: Spring Boot는 <code>src/main/webapp/WEB-INF/jsp/</code> 경로에서 JSP 파일을 찾음</p>
<hr>
<h3 id="2-servlet-api-버전-충돌">2. Servlet API 버전 충돌</h3>
<p><strong>문제</strong>:</p>
<ul>
<li><code>javax.servlet</code> vs <code>jakarta.servlet</code> API 충돌</li>
<li>Spring Boot 3.x는 <code>jakarta.servlet</code> 사용</li>
</ul>
<p><strong>해결</strong>:</p>
<ul>
<li>Spring Boot 방식으로 컨트롤러 변경</li>
<li><code>@WebServlet</code> → <code>@Controller</code> + <code>@GetMapping/@PostMapping</code></li>
<li><code>HttpServlet</code> 상속 → Spring MVC 방식</li>
</ul>
<hr>
<h3 id="3-의존성-주입-설정">3. 의존성 주입 설정</h3>
<p><strong>문제</strong>:</p>
<ul>
<li><code>MenuService</code>를 직접 인스턴스화하여 사용</li>
<li>Spring의 의존성 주입 활용 안 함</li>
</ul>
<p><strong>해결</strong>:</p>
<pre><code class="language-java">// Before
private final MenuService menuService = new MenuService();

// After
private final MenuService menuService;

public MenuController(MenuService menuService) {
    this.menuService = menuService;
}</code></pre>
<hr>
<h3 id="4-jsp-뷰-리졸버-설정">4. JSP 뷰 리졸버 설정</h3>
<p><strong>문제</strong>:</p>
<ul>
<li>JSP 파일을 찾지 못하는 404 오류</li>
</ul>
<p><strong>해결</strong>:
<code>application.properties</code>에 올바른 설정 추가:</p>
<pre><code class="language-properties">spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp</code></pre>
<hr>
<h3 id="5-ch5-서블릿jsp-사용-실패-이유">5. CH5 서블릿/JSP 사용 실패 이유</h3>
<h4 id="spring-boot-3x에서-jsp-지원-제한">Spring Boot 3.x에서 JSP 지원 제한</h4>
<ul>
<li><p><strong>문제</strong>: Spring Boot 3.x부터 JSP 지원이 공식적으로 제한됨</p>
</li>
<li><p><strong>에러</strong>: JSP 파일들이 404 에러로 접근 불가</p>
</li>
<li><p><strong>원인</strong>:</p>
<ul>
<li>Spring Boot 3.x는 Jakarta EE 기반으로 변경</li>
<li>JSP 컴파일러 설정이 복잡해짐</li>
<li>내장 톰캣에서 JSP 처리 방식 변경</li>
</ul>
<p>따라서 마지막 과제는 JSP 대신 Thymeleaf로 해결했다. </p>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>