<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>code_name_js.log</title>
        <link>https://velog.io/</link>
        <description>배움을 추구하는 개발자</description>
        <lastBuildDate>Mon, 06 Apr 2026 03:34:36 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>code_name_js.log</title>
            <url>https://velog.velcdn.com/images/code_name_js/profile/3acd4839-a90c-4dd9-9696-ae4dccae45e8/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. code_name_js.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/code_name_js" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[경우의 수]]></title>
            <link>https://velog.io/@code_name_js/%EA%B2%BD%EC%9A%B0%EC%9D%98-%EC%88%98</link>
            <guid>https://velog.io/@code_name_js/%EA%B2%BD%EC%9A%B0%EC%9D%98-%EC%88%98</guid>
            <pubDate>Mon, 06 Apr 2026 03:34:36 GMT</pubDate>
            <description><![CDATA[<p>경우의 수를 구하는 방법.
조합론.</p>
<ul>
<li><p>여러 선택지중에 하나를 고르는 경우 =&gt; 더하기 ( 이거 또는 저거)
ex) 학교를 가야 하는데
버스를 타는 경우 3가지
지하철 타는 경우 2가지.
=&gt; 하나를 골라야함. 3 + 2 </p>
</li>
<li><p>여러 선택지중에 동시에 고르는 경우 =&gt; 곱하기 ( 이거 그리고 저거)
ex) 메뉴를 골라야 하는데
메뉴 3가지 음료 2가지.
=&gt; 동시에 골라야함. 3 * 2
만약 아무것도 안골라야 하는 상황도 있다면?
메뉴 3가지 + (아무것도안고름 + 1)
음료 2가지 + (아무것도안고름 + 1)
=&gt; 4 * 3 
다 아무것도 안고르는 경우는 없다면?
=&gt; 4 * 3 - 1</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[day2 - 코딩테스트]]></title>
            <link>https://velog.io/@code_name_js/day2-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@code_name_js/day2-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Fri, 07 Nov 2025 09:03:09 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> dfs, bfs 개념 공부진행함.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[DAY1 - 코테준비]]></title>
            <link>https://velog.io/@code_name_js/DAY1-%EC%BD%94%ED%85%8C%EC%A4%80%EB%B9%84</link>
            <guid>https://velog.io/@code_name_js/DAY1-%EC%BD%94%ED%85%8C%EC%A4%80%EB%B9%84</guid>
            <pubDate>Thu, 06 Nov 2025 05:16:02 GMT</pubDate>
            <description><![CDATA[<p>Day1 - 코딩테스트 풀이.</p>
<p>String 에서 인덱스로 접근 하는 방법 =&gt; 문자열.chartAt(인덱스)
Character 형식으로 변환함.</p>
<p>문자열 비교 할때, 대소문자 상관 없다면,
문자열이면 =&gt; 문자열.toUpperCase 
문자이면 =&gt; Character.toUpperCase(문자)
이렇게 변환해서 사용.</p>
<p>문자열 대소문자 확인 하는 메소드
Character.isUpperCase(문자)
Character.isLowerCase(문자)</p>
<p>아스키 코드로 풀어도됨. ( 아스키 코드)
아스키 소문자 65 ~ 90 이니까 + 32 하면 대,소문자 구분됨.</p>
<p>Scanner 에서 NextInt() 로 받으면, 버퍼가 남겨짐.
그래서 sc.NextInt() 후엔 sc.nextLine() 같은걸로 버퍼를 없애줘야함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🌐 REST API 완전 정리 - 웹개발자가 꼭 알아야 할 핵심 개념]]></title>
            <link>https://velog.io/@code_name_js/REST-API-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EC%9B%B9%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@code_name_js/REST-API-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EC%9B%B9%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Wed, 30 Jul 2025 03:58:46 GMT</pubDate>
            <description><![CDATA[<h2 id="🧩-rest-api란">🧩 REST API란?</h2>
<p>REST API는 <strong>HTTP 프로토콜 위에서 동작</strong>하는 <strong>웹 기반 데이터 통신 아키텍처 스타일</strong>입니다.
리소스(자원)를 URL로 식별하고, **HTTP 메서드(GET, POST 등)**를 통해 이를 조작합니다.</p>
<blockquote>
<p>REST = REpresentational State Transfer
자원의 표현을 HTTP를 통해 전송하는 방식</p>
</blockquote>
<hr>
<h2 id="🔧-rest-api의-핵심-특징">🔧 REST API의 핵심 특징</h2>
<ul>
<li><strong>프로토콜:</strong> HTTP 기반</li>
<li><strong>형식:</strong> JSON을 주로 사용</li>
<li><strong>자원 중심:</strong> URL로 자원을 명시적으로 표현</li>
<li><strong>표준화된 동작:</strong> HTTP 메서드 사용</li>
</ul>
<hr>
<h2 id="📦-http-메서드-종류">📦 HTTP 메서드 종류</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>GET</code></td>
<td>리소스 조회</td>
</tr>
<tr>
<td><code>POST</code></td>
<td>리소스 생성</td>
</tr>
<tr>
<td><code>PUT</code></td>
<td>리소스 전체 수정</td>
</tr>
<tr>
<td><code>PATCH</code></td>
<td>리소스 일부 수정</td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td>리소스 삭제</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧪-rest-api-예시">🧪 REST API 예시</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>URL</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>GET</code></td>
<td><code>/users</code></td>
<td>사용자 목록 조회</td>
</tr>
<tr>
<td><code>GET</code></td>
<td><code>/users/1</code></td>
<td>ID가 1인 사용자 조회</td>
</tr>
<tr>
<td><code>POST</code></td>
<td><code>/users</code></td>
<td>새로운 사용자 생성</td>
</tr>
<tr>
<td><code>PUT</code></td>
<td><code>/users/1</code></td>
<td>ID가 1인 사용자 정보 전체 수정</td>
</tr>
<tr>
<td><code>PATCH</code></td>
<td><code>/users/1</code></td>
<td>ID가 1인 사용자 정보 일부 수정</td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td><code>/users/1</code></td>
<td>ID가 1인 사용자 삭제</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔷-rest-api의-6가지-설계-원칙">🔷 REST API의 6가지 설계 원칙</h2>
<ol>
<li><strong>클라이언트-서버 구조:</strong> 프론트와 백 분리</li>
<li><strong>무상태성(Stateless):</strong> 서버는 클라이언트 상태를 저장하지 않음</li>
<li><strong>캐시 가능:</strong> 응답은 명시적으로 캐시 가능</li>
<li><strong>계층 구조:</strong> 중간 계층(API Gateway 등) 허용</li>
<li><strong>일관된 인터페이스:</strong> URL + 메서드로 조작</li>
<li><strong>코드 온 디맨드(Optional):</strong> 서버가 코드 전달 가능 (거의 사용 안 함)</li>
</ol>
<hr>
<h2 id="✅-rest-api의-장점">✅ REST API의 장점</h2>
<ul>
<li>HTTP 표준을 활용 → 학습 비용 낮음</li>
<li>JSON 사용 → 경량 통신</li>
<li>범용성 높음 → 웹, 앱, IoT 등에서 사용 가능</li>
<li>개발자 친화적 URL 구성</li>
</ul>
<hr>
<h2 id="🔐-http-상태-코드-자주-쓰는-것">🔐 HTTP 상태 코드 자주 쓰는 것</h2>
<table>
<thead>
<tr>
<th>코드</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>200</code></td>
<td>OK: 요청 성공</td>
</tr>
<tr>
<td><code>201</code></td>
<td>Created: 생성 성공</td>
</tr>
<tr>
<td><code>400</code></td>
<td>Bad Request: 잘못된 요청</td>
</tr>
<tr>
<td><code>401</code></td>
<td>Unauthorized: 인증 필요</td>
</tr>
<tr>
<td><code>404</code></td>
<td>Not Found: 리소스 없음</td>
</tr>
<tr>
<td><code>500</code></td>
<td>Internal Server Error: 서버 내부 오류</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔧-rest-api-사용-환경">🔧 REST API 사용 환경</h2>
<table>
<thead>
<tr>
<th>사용 환경</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>🌐 웹 애플리케이션</td>
<td>React, Vue 같은 프론트가 REST API 호출</td>
</tr>
<tr>
<td>📱 모바일 앱</td>
<td>Android, iOS 앱이 서버와 통신할 때 사용</td>
</tr>
<tr>
<td>🖥️ 데스크탑 앱</td>
<td>Electron, .NET 기반 앱에서도 사용됨</td>
</tr>
<tr>
<td>🤖 IoT 기기</td>
<td>스마트기기(CCTV, 에어컨 등)와 서버 통신 시 사용</td>
</tr>
<tr>
<td>🧩 마이크로서비스 간 통신</td>
<td>Spring → FastAPI 등 서버 간 API 통신에 활용됨</td>
</tr>
</tbody></table>
<hr>
<h2 id="❌-rest-api가-아닌-기술들">❌ REST API가 아닌 기술들</h2>
<table>
<thead>
<tr>
<th>기술</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>gRPC</strong></td>
<td>고성능 바이너리 기반 API. IoT, 마이크로서비스에서 선호됨</td>
</tr>
<tr>
<td><strong>GraphQL</strong></td>
<td>클라이언트가 원하는 데이터만 요청 가능. 복잡한 쿼리 처리에 강함</td>
</tr>
<tr>
<td><strong>WebSocket</strong></td>
<td>실시간 양방향 통신 (예: 채팅, 게임 등)</td>
</tr>
<tr>
<td><strong>SOAP</strong></td>
<td>XML 기반 레거시 API (은행, 공공기관 등에서 여전히 사용)</td>
</tr>
</tbody></table>
<blockquote>
<p><code>WebSocket</code>도 REST와는 전혀 다른 방식!</p>
</blockquote>
<hr>
<h2 id="❓-자주-묻는-질문-faq">❓ 자주 묻는 질문 (FAQ)</h2>
<table>
<thead>
<tr>
<th>질문</th>
<th>답변</th>
</tr>
</thead>
<tbody><tr>
<td>REST API는 웹 전용인가요?</td>
<td>❌ 웹 중심이지만, IoT/앱 등에서도 사용됩니다.</td>
</tr>
<tr>
<td>REST는 표준인가요?</td>
<td>✅ 사실상 웹 API 설계에서 표준처럼 사용됩니다.</td>
</tr>
<tr>
<td>JSON만 써야 하나요?</td>
<td>아니요, XML 등도 가능하지만 JSON이 가장 일반적입니다.</td>
</tr>
<tr>
<td>REST API를 쓰면 좋은 이유는?</td>
<td>직관적이고, 확장성 있고, 대부분의 시스템과 호환됩니다.</td>
</tr>
</tbody></table>
<hr>
<p><img src="https://velog.velcdn.com/images/code_name_js/post/5631c554-395b-4e7e-b9ba-4b9ae140cad0/image.png" alt=""></p>
<h2 id="🧭-마무리">🧭 마무리</h2>
<ul>
<li>REST API는 <strong>현대 웹/앱 개발의 핵심 표준</strong></li>
<li>HTTP + 자원 중심 URL + 표준 메서드(GET, POST 등)</li>
<li>JSON을 통해 가볍고 빠른 통신이 가능</li>
<li>FastAPI, Spring Boot 등 다양한 프레임워크에서 쉽게 구현 가능</li>
</ul>
<hr>
<h2 id="🧪-예제-코드-fastapi">🧪 예제 코드 (FastAPI)</h2>
<pre><code class="language-python">from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/users/{user_id}&quot;)
def get_user(user_id: int):
    return {&quot;user_id&quot;: user_id, &quot;name&quot;: &quot;홍길동&quot;}</code></pre>
<h2 id="🧪-예제-코드-spring-boot">🧪 예제 코드 (Spring Boot)</h2>
<pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {
    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;UserDto&gt; getUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.findById(id));
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[JPA의 Lazy Loading과 LazyInitializationException 이해하기]]></title>
            <link>https://velog.io/@code_name_js/JPA%EC%9D%98-Lazy-Loading%EA%B3%BC-LazyInitializationException-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@code_name_js/JPA%EC%9D%98-Lazy-Loading%EA%B3%BC-LazyInitializationException-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 16 Apr 2025 05:20:18 GMT</pubDate>
            <description><![CDATA[<p>JPA에서 FetchType.LAZY로 설정된 연관 필드에 대해, 영속성 컨텍스트(Session)가 닫힌 이후에 접근할 때 발생하는 예외야.</p>
<p>brandService.getBrandById를 호출하는 시점이 service단 @Transactional(readOnly = true) 환경에서 실행되고,
그 이후 컨트롤러에서 brand.getBranches()를 호출하는 순간에 세션이 닫혀 있으면 LazyInitializationException 발생함.</p>
<p>@Transactional의 역할은
→ EntityManager (=영속성 컨텍스트 = 세션)를 열고 닫는 라이프사이클을 관리하는 것</p>
<p>상황    결과
@Transactional 안에서 Lazy 필드 접근    ✅ 정상 작동
@Transactional 밖에서 Lazy 필드 접근    ❌ LazyInitializationException 발생</p>
<p>brandService.getBrandById() 안에서는 
service단) @Transactional(readOnly = true) 트랜잭션 안에서 실행됨 이건 OK
하지만, 그 이후 getBrandById()가 끝나고 나면 트랜잭션이 종료되고, EntityManager가 close됨. ( 컨트롤러나 그 이후 상황...)
이 상황에서 컨트롤러에서 brand.getBranches()를 호출하면
연관 데이터는 아직 DB에서 안 가져왔는데, 영속성 컨텍스트는 이미 사라짐.
-&gt; 즉, JPA가 데이터를 가져올 수 없음... -&gt; LazyInitalizationException이 발생함.</p>
<p>결국 엔티티를 그대로 노출 하는건 위험하고 DTO 노출로 가야함.
CRUD를 쓰는 Entity들은 다 DTO로 감싸서 보내야함.</p>
<ul>
<li>Lazy 문제를 방지함.</li>
<li>꼭 필요한 정보만 전달함. ( 프론트엔드에 맞춘 데이터 형식 가공 가능함.)</li>
<li>엔티티 직접 노출로부터 보호. ( 엔티티 수정시. 의도치 못한 버그 발생 가능함.)</li>
<li>계층 간 의존성 분리.
엔티티는 DB와 밀접한 구조 -&gt; 리포지토리/도메인 계층
DTO는 view/API 전용 구조 -&gt; 컨트롤러/프론트엔드용.
✅ 엔티티 구조가 바뀌어도 API 스펙을 바꾸지 않아도 됨 → 유지보수 용이
성능 최적화. ( select 쿼리 튜징쉬움 )</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바에서 안전한 자원 정리와 예외 처리]]></title>
            <link>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EC%9E%90%EC%9B%90-%EC%A0%95%EB%A6%AC%EC%99%80-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EC%9E%90%EC%9B%90-%EC%A0%95%EB%A6%AC%EC%99%80-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Thu, 13 Mar 2025 07:22:41 GMT</pubDate>
            <description><![CDATA[<h2 id="1-자원-정리resource-cleanup의-중요성">1. 자원 정리(Resource Cleanup)의 중요성</h2>
<p>자바에서 파일, 네트워크 소켓, 데이터베이스 연결 같은 자원은 사용 후 반드시 <strong>정리(close)</strong> 해야 한다. 그렇지 않으면 <strong>메모리 누수(memory leak)</strong> 또는 <strong>리소스 잠금(locking issue)</strong> 같은 문제가 발생할 수 있다.</p>
<p>자원을 정리하는 일반적인 방법은 <strong><code>try-finally</code></strong> 를 사용하는 것이다. 하지만 <strong>예외 처리</strong>를 제대로 하지 않으면 예상치 못한 문제들이 발생할 수 있다.</p>
<hr>
<h2 id="2-문제-상황">2. 문제 상황</h2>
<p>아래 코드는 두 개의 리소스를 생성하고, 하나의 메서드에서 호출하는 과정에서 예외가 발생할 경우를 다룬다.</p>
<pre><code class="language-java">package network.tcp.autocloseable;

public class ResourceCloseMainV2 {

    public static void main(String[] args) {
        try {
            logic();
        } catch (CallException e) {
            System.out.println(&quot;CallException 예외 처리&quot;);
            e.printStackTrace();
        } catch (CloseException e) {
            System.out.println(&quot;CloseException 예외 처리&quot;);
            e.printStackTrace();
        }
    }

    private static void logic() throws CallException, CloseException {
        ResourceV1 resource1 = null;
        ResourceV1 resource2 = null;

        try {
            resource1 = new ResourceV1(&quot;resource1&quot;);
            resource2 = new ResourceV1(&quot;resource2&quot;);
            resource1.call();
            resource2.callEx(); // CallException 발생
        } catch (CallException e) {
            System.out.println(&quot;ex: &quot; + e);
            throw e; // CallException 다시 던짐
        } finally {
            if (resource2 != null) {
                resource2.closeEx(); // CloseException 발생
            }
            if (resource1 != null) {
                resource1.closeEx();
            }
        }
    }
}</code></pre>
<p>이 코드에서 발생할 수 있는 주요 문제들을 살펴보자.</p>
<hr>
<h2 id="3-문제점-분석">3. 문제점 분석</h2>
<h3 id="❌-1-null-체크-문제">❌ 1. <code>null</code> 체크 문제</h3>
<ul>
<li><code>finally</code> 블록을 사용하면 예외가 발생해도 자원 정리 코드가 실행된다.</li>
<li>하지만, <strong>객체를 생성하기 전에 예외가 발생하면 해당 객체는 <code>null</code></strong> 이 된다.</li>
<li>따라서 <code>null</code> 체크를 하지 않으면 <strong><code>NullPointerException</code></strong> 이 발생할 수 있다.</li>
</ul>
<h3 id="❌-2-자원-정리-중-예외-발생-문제">❌ 2. 자원 정리 중 예외 발생 문제</h3>
<ul>
<li><code>finally</code> 블록에서 <code>closeEx()</code> 메서드를 호출하는데, 이 과정에서 <strong>예외가 발생하면 그 이후 코드가 실행되지 않는다.</strong></li>
<li>즉, 하나의 리소스 정리 중 예외가 발생하면 <strong>다른 리소스는 정리되지 못할 수 있다.</strong></li>
</ul>
<h3 id="❌-3-핵심-예외callexception가-사라짐">❌ 3. 핵심 예외(CallException)가 사라짐</h3>
<ul>
<li>코드의 핵심 예외는 <code>callEx()</code>에서 발생한 <code>CallException</code>이다.</li>
<li>하지만 <code>finally</code> 블록에서 추가로 <code>CloseException</code>이 발생하면 <strong>핵심 예외가 덮어씌워지는 문제</strong>가 발생할 수 있다.</li>
<li><strong>최초 발생한 예외가 유지되지 않으면 디버깅이 어렵다.</strong></li>
</ul>
<hr>
<h2 id="4-해결-방법">4. 해결 방법</h2>
<h3 id="✅-1-try-with-resources-사용-권장">✅ 1. <code>try-with-resources</code> 사용 (권장)</h3>
<ul>
<li><code>AutoCloseable</code>을 구현하면 <code>try-with-resources</code> 문법을 활용할 수 있다.</li>
<li>자원 정리 과정에서 발생하는 예외를 자동으로 관리하고, <code>null</code> 체크도 필요 없다.</li>
</ul>
<pre><code class="language-java">public class ResourceCloseMainV3 {
    public static void main(String[] args) {
        try (ResourceV1 resource1 = new ResourceV1(&quot;resource1&quot;);
             ResourceV1 resource2 = new ResourceV1(&quot;resource2&quot;)) {
            resource1.call();
            resource2.callEx(); // CallException 발생
        } catch (CallException e) {
            System.out.println(&quot;CallException 예외 처리&quot;);
            e.printStackTrace();
        } catch (CloseException e) {
            System.out.println(&quot;CloseException 예외 처리&quot;);
            e.printStackTrace();
        }
    }
}</code></pre>
<h3 id="✅-2-suppressed-예외-활용하기">✅ 2. <code>suppressed</code> 예외 활용하기</h3>
<ul>
<li><code>try-with-resources</code>는 <strong>리소스 정리 중 발생한 예외를 억제(suppressed)하여 관리</strong>할 수 있다.</li>
<li><code>getSuppressed()</code>를 사용하면 <strong>숨겨진 예외</strong>를 확인할 수 있다.</li>
</ul>
<pre><code class="language-java">catch (Exception e) {
    Throwable[] suppressed = e.getSuppressed();
    for (Throwable t : suppressed) {
        t.printStackTrace();
    }
}</code></pre>
<h3 id="✅-3-finally-블록에서-여러-개의-예외를-안전하게-처리하기">✅ 3. <code>finally</code> 블록에서 여러 개의 예외를 안전하게 처리하기</h3>
<ul>
<li><code>try-catch</code>를 추가하여 <strong>한 리소스의 정리 실패가 다른 리소스 정리에 영향을 주지 않도록</strong> 한다.</li>
</ul>
<pre><code class="language-java">finally {
    try {
        if (resource2 != null) resource2.closeEx();
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        if (resource1 != null) resource1.closeEx();
    } catch (Exception e) {
        e.printStackTrace();
    }
}</code></pre>
<hr>
<h2 id="5-정리">5. 정리</h2>
<p>✅ <strong><code>try-with-resources</code>를 사용하면 자원 정리가 자동화되고, 예외 처리도 안전하게 관리 가능</strong></p>
<p>✅ <strong><code>getSuppressed()</code>로 숨겨진 예외까지 추적 가능</strong></p>
<p>✅ <strong><code>finally</code> 블록에서 여러 개의 자원을 정리할 때는 개별 <code>try-catch</code>를 사용하여 다른 자원의 정리 실패를 방지해야 함</strong></p>
<hr>
<p>자원 관리를 안전하게 처리하는 것은 안정적인 애플리케이션 개발의 핵심이다. <code>try-with-resources</code>와 적절한 예외 처리를 활용하여 더 견고한 코드를 작성하자! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP/IP 소켓 통신 정리]]></title>
            <link>https://velog.io/@code_name_js/TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@code_name_js/TCPIP-%EC%86%8C%EC%BC%93-%ED%86%B5%EC%8B%A0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 13 Mar 2025 07:16:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-네트워크-통신에서-ip-주소의-역할">1. 네트워크 통신에서 IP 주소의 역할</h2>
<p>네트워크 통신에서 서버와 통신하려면 <strong>IP 주소</strong>가 필요하다. 도메인(호스트 이름)만으로는 통신할 수 없으며, 도메인을 실제 IP 주소로 변환해야 한다.</p>
<p>자바에서는 <code>InetAddress</code> 클래스를 사용하여 도메인 이름을 IP 주소로 변환할 수 있다.</p>
<pre><code class="language-java">import java.net.*;

public class HostToIP {
    public static void main(String[] args) {
        try {
            InetAddress address = InetAddress.getByName(&quot;www.google.com&quot;);
            System.out.println(&quot;IP Address: &quot; + address.getHostAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<hr>
<h2 id="2-서버와-클라이언트의-데이터-송수신">2. 서버와 클라이언트의 데이터 송수신</h2>
<h3 id="inputstream과-outputstream"><strong>InputStream과 OutputStream</strong></h3>
<p>소켓 통신에서 데이터를 주고받을 때 <code>InputStream</code>과 <code>OutputStream</code>을 사용한다.</p>
<ul>
<li><strong>InputStream</strong>: 서버 -&gt; 클라이언트로 데이터를 받을 때 사용</li>
<li><strong>OutputStream</strong>: 클라이언트 -&gt; 서버로 데이터를 보낼 때 사용</li>
</ul>
<p>하지만, 기본 스트림을 그대로 사용하면 <strong>byte</strong> 단위로 데이터를 변환해야 하는 불편함이 있다. 이를 해결하기 위해 <strong>보조 스트림</strong>을 사용한다.</p>
<h3 id="datainputstream과-dataoutputstream"><strong>DataInputStream과 DataOutputStream</strong></h3>
<p><code>DataInputStream</code>과 <code>DataOutputStream</code>을 사용하면 <strong>자바 기본 타입(int, float, String 등)</strong> 을 편리하게 송수신할 수 있다.</p>
<pre><code class="language-java">import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) {
        try (Socket socket = new Socket(&quot;localhost&quot;, 12345);
             DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
             DataInputStream dis = new DataInputStream(socket.getInputStream())) {

            dos.writeUTF(&quot;Hello Server&quot;); // 문자열 전송
            System.out.println(&quot;Server response: &quot; + dis.readUTF()); // 응답 수신
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<hr>
<h2 id="3-서버-소켓과-클라이언트-소켓">3. 서버 소켓과 클라이언트 소켓</h2>
<h3 id="서버-소켓의-역할"><strong>서버 소켓의 역할</strong></h3>
<p>서버는 특정 <strong>포트</strong>를 열어두어야 클라이언트가 접속할 수 있다.</p>
<ul>
<li><strong>ServerSocket</strong>: 클라이언트의 요청을 받아들여 <strong>연결만 담당</strong>하는 특별한 소켓</li>
<li><strong>Socket</strong>: 서버와 클라이언트가 데이터를 주고받을 수 있는 <strong>실제 통신 소켓</strong></li>
</ul>
<h3 id="tcp-3-way-handshake-과정"><strong>TCP 3-Way Handshake 과정</strong></h3>
<ol>
<li>클라이언트가 서버의 포트(예: 12345)로 연결 요청 (SYN)</li>
<li>서버가 요청을 받아 승인 (SYN-ACK)</li>
<li>클라이언트가 승인 확인 (ACK)</li>
<li>연결 완료 → <strong>Backlog Queue</strong>에 TCP 연결 정보 저장</li>
</ol>
<h3 id="서버의-동작-과정"><strong>서버의 동작 과정</strong></h3>
<ol>
<li><code>ServerSocket</code>을 생성하고 특정 포트(예: 12345)에서 대기</li>
<li><code>accept()</code>를 호출하여 클라이언트 요청을 대기</li>
<li>클라이언트가 연결하면 <strong>새로운 <code>Socket</code> 객체</strong>를 생성하여 통신</li>
</ol>
<pre><code class="language-java">import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(12345)) {
            System.out.println(&quot;서버 대기 중...&quot;);

            while (true) {
                Socket socket = serverSocket.accept(); // 클라이언트 연결 대기 (블로킹)
                System.out.println(&quot;클라이언트 연결 완료!&quot;);

                new Thread(new ClientHandler(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ClientHandler implements Runnable {
    private Socket socket;

    public ClientHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try (DataInputStream dis = new DataInputStream(socket.getInputStream());
             DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {

            String message = dis.readUTF(); // 클라이언트 메시지 수신
            System.out.println(&quot;Received: &quot; + message);
            dos.writeUTF(&quot;Hello Client!&quot;); // 응답 전송
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<hr>
<h2 id="4-블로킹과-멀티스레드-처리">4. 블로킹과 멀티스레드 처리</h2>
<h3 id="accept의-블로킹-문제"><strong>accept()의 블로킹 문제</strong></h3>
<ul>
<li><code>accept()</code> 메서드는 클라이언트 연결 요청이 없으면 <strong>무한 대기</strong> 상태(블로킹)가 된다.</li>
<li><code>readUTF()</code> 같은 입력 메서드도 데이터를 받을 때까지 대기 상태가 된다.</li>
<li>따라서, 서버는 <strong>멀티스레드 방식</strong>으로 각각의 클라이언트 요청을 별도의 스레드에서 처리해야 한다.</li>
</ul>
<h3 id="멀티스레드-방식의-서버"><strong>멀티스레드 방식의 서버</strong></h3>
<p>각 클라이언트가 연결될 때마다 새로운 <code>Thread</code>를 생성하여 별도로 처리하면, 여러 클라이언트를 동시에 처리할 수 있다.</p>
<ul>
<li><strong>메인 스레드</strong>: <code>ServerSocket</code>을 관리하고 새로운 연결이 올 때마다 <code>Session</code> 스레드를 생성</li>
<li><strong>Session 스레드</strong>: 특정 클라이언트와 1:1로 데이터를 주고받는 역할</li>
</ul>
<hr>
<h2 id="5-정리">5. 정리</h2>
<p>✅ <strong>서버 소켓과 클라이언트 소켓</strong></p>
<ul>
<li><code>ServerSocket</code>은 <strong>연결만 담당</strong>하는 특별한 소켓</li>
<li>실제 데이터 송수신은 <code>Socket</code> 객체를 통해 이루어짐</li>
</ul>
<p>✅ <strong>TCP 연결 과정</strong></p>
<ul>
<li>클라이언트가 <code>connect()</code> 요청하면 <strong>TCP 3-Way Handshake</strong> 후 <code>Backlog Queue</code>에 저장</li>
<li><code>accept()</code> 호출 시 <code>Socket</code> 객체 생성 후 클라이언트와 통신 가능</li>
</ul>
<p>✅ <strong>스트림을 이용한 데이터 송수신</strong></p>
<ul>
<li><code>DataInputStream</code> / <code>DataOutputStream</code>을 활용하면 <strong>편리한 데이터 송수신</strong> 가능</li>
</ul>
<p>✅ <strong>멀티스레드 처리 필요</strong></p>
<ul>
<li><code>accept()</code>와 <code>readUTF()</code> 같은 블로킹 메서드는 <strong>별도 스레드에서 실행해야 함</strong></li>
</ul>
<hr>
<p>이제 자바 소켓 프로그래밍에 대한 기본 개념을 정리했으니, 실전 프로젝트에서 활용해 보자! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[입력 스트림과 버퍼: 자바 I/O 성능 최적화]]></title>
            <link>https://velog.io/@code_name_js/%EC%9E%85%EB%A0%A5-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EA%B3%BC-%EB%B2%84%ED%8D%BC-%EC%9E%90%EB%B0%94-IO-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@code_name_js/%EC%9E%85%EB%A0%A5-%EC%8A%A4%ED%8A%B8%EB%A6%BC%EA%B3%BC-%EB%B2%84%ED%8D%BC-%EC%9E%90%EB%B0%94-IO-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Mon, 10 Mar 2025 05:38:26 GMT</pubDate>
            <description><![CDATA[<p>자바에서 파일이나 네트워크와 같은 외부 장치로 데이터를 읽고 쓰는 과정은 <strong>스트림(stream)</strong> 을 통해 일관되게 처리할 수 있습니다. 이 글에서는 입력 스트림의 동작 원리와 버퍼를 활용하여 성능을 최적화하는 방법에 대해 살펴보겠습니다.</p>
<hr>
<h2 id="1-입력-스트림과-버퍼의-기본-원리">1. 입력 스트림과 버퍼의 기본 원리</h2>
<p>자바에서 <code>read()</code> 메소드를 사용할 때, 미리 생성된 <code>byte[]</code> 버퍼를 전달하여 한 번에 여러 바이트를 읽어올 수 있습니다.  </p>
<ul>
<li><strong>byte[] 버퍼:</strong> 데이터를 임시로 저장하는 공간입니다.  </li>
<li><strong>offset:</strong> 버퍼에서 데이터를 기록하기 시작할 인덱스입니다.  </li>
<li><strong>length:</strong> 읽어올 최대 바이트 수를 지정합니다.</li>
</ul>
<p>현대 컴퓨터는 <strong>바이트 단위</strong>로 데이터를 주고받으며, 스트림을 사용하면 파일 I/O, 네트워크 통신 등 다양한 데이터 전송 방식을 동일한 방식으로 다룰 수 있습니다.</p>
<blockquote>
<p><strong>참고:</strong> <code>read()</code>와 <code>write()</code> 메소드가 호출될 때마다 OS의 시스템 콜이 발생하는데, 시스템 콜은 상대적으로 무거운 작업입니다. HDD나 SDD와 같은 저장 장치에서도 한 바이트씩 처리하면 성능에 큰 영향을 주므로, 버퍼를 활용하여 여러 바이트를 한 번에 처리하는 것이 효율적입니다.</p>
</blockquote>
<hr>
<h2 id="2-버퍼-사용의-중요성">2. 버퍼 사용의 중요성</h2>
<h3 id="21-버퍼의-역할">2.1 버퍼의 역할</h3>
<ul>
<li><p><strong>시스템 콜 최소화:</strong><br>한 번에 많은 데이터를 전달하면 시스템 콜 호출 횟수가 줄어들어 성능이 향상됩니다.</p>
</li>
<li><p><strong>하드웨어 최적화:</strong><br>실제 디스크나 파일 시스템은 보통 4KB 또는 8KB 단위로 데이터를 읽고 쓰므로, 이보다 작은 단위로 데이터를 다루면 불필요한 오버헤드가 발생합니다.</p>
</li>
</ul>
<h3 id="22-bufferedxxx-클래스">2.2 BufferedXXX 클래스</h3>
<p>자바에서는 기본 스트림(예: <code>FileOutputStream</code>, <code>FileInputStream</code>) 외에도, 보조 스트림으로서 <strong>BufferedOutputStream</strong>과 <strong>BufferedInputStream</strong>을 제공하여 버퍼 기능을 쉽게 사용할 수 있습니다.</p>
<ul>
<li><p><strong>BufferedOutputStream:</strong><br>내부적으로 버퍼를 사용하여 데이터를 모았다가 한 번에 출력 스트림에 전달합니다.  </p>
<blockquote>
<p><strong>주의:</strong> 반드시 대상 스트림(예: <code>FileOutputStream</code>)이 필요합니다.</p>
</blockquote>
</li>
<li><p><strong>BufferedInputStream:</strong><br>미리 정해진 버퍼 크기만큼 데이터를 읽어와 저장해두고, 이후 <code>read()</code> 호출 시 버퍼에서 데이터를 반환하여 성능을 개선합니다.</p>
</li>
</ul>
<hr>
<h2 id="3-성능-최적화-전략">3. 성능 최적화 전략</h2>
<ul>
<li><p><strong>작은 파일 처리:</strong><br>파일의 크기가 작아 메모리 부담이 적다면, 전체 파일을 한 번에 처리하는 것도 좋은 선택입니다.</p>
</li>
<li><p><strong>큰 파일 또는 성능이 중요한 경우:</strong><br>파일이 크거나 성능이 중요한 경우, 버퍼 크기를 적절히 조절하여 파일을 나누어 처리하는 것이 좋습니다.</p>
</li>
<li><p><strong>동기화 비용 고려:</strong><br>BufferedXXX 클래스는 내부에 동기화 코드를 포함하고 있으므로, 멀티스레드 환경에서의 안전성을 제공하지만 단일 스레드 환경에서는 오버헤드가 발생할 수 있습니다.</p>
</li>
</ul>
<hr>
<h2 id="4-자바-코드-예제">4. 자바 코드 예제</h2>
<p>다음 예제는 <code>BufferedInputStream</code>과 <code>BufferedOutputStream</code>을 사용하여 파일을 읽고 쓰는 과정을 보여줍니다.</p>
<pre><code class="language-java">import java.io.*;

public class BufferedFileCopy {
    public static void main(String[] args) {
        // 원본 파일과 대상 파일 경로 설정
        String inputFile = &quot;input.txt&quot;;
        String outputFile = &quot;output.txt&quot;;

        // 4KB 크기의 버퍼 사용 (파일 시스템의 기본 단위와 유사)
        byte[] buffer = new byte[4096];

        try (
            // 기본 스트림에 보조 스트림(버퍼 기능)을 추가
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile))
        ) {
            int bytesRead;
            // 버퍼에 데이터를 읽어오면서 파일 전체를 복사
            while ((bytesRead = bis.read(buffer, 0, buffer.length)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            // 버퍼에 남아있는 데이터를 출력 스트림에 완전히 전달
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[한글이 깨지는 가장 이유...]]></title>
            <link>https://velog.io/@code_name_js/%ED%95%9C%EA%B8%80%EC%9D%B4-%EA%B9%A8%EC%A7%80%EB%8A%94-%EA%B0%80%EC%9E%A5-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@code_name_js/%ED%95%9C%EA%B8%80%EC%9D%B4-%EA%B9%A8%EC%A7%80%EB%8A%94-%EA%B0%80%EC%9E%A5-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Fri, 07 Mar 2025 08:31:24 GMT</pubDate>
            <description><![CDATA[<h1 id="텍스트-데이터와-문자-인코딩-컴퓨터-저장-원리와-실무-활용법">텍스트 데이터와 문자 인코딩: 컴퓨터 저장 원리와 실무 활용법</h1>
<p>컴퓨터가 데이터를 저장하는 기본 원리는 <strong>이진수(0과 1)</strong> 입니다. 컴퓨터 메모리는 수많은 전구처럼 작동하는 <strong>트랜지스터</strong>로 구성되어 있으며, 이 트랜지스터는 전류가 흐르거나 흐르지 않는 두 가지 상태를 표현합니다. 이와 같이, 모든 데이터는 결국 0과 1의 조합으로 저장됩니다.</p>
<hr>
<h2 id="1-문자-인코딩과-디코딩">1. 문자 인코딩과 디코딩</h2>
<ul>
<li><p><strong>문자 인코딩 (Encoding):</strong><br>문자를 특정한 숫자(바이트)로 변환하는 과정입니다.<br>예시: <code>A</code> → 65, <code>B</code> → 66</p>
</li>
<li><p><strong>문자 디코딩 (Decoding):</strong><br>인코딩된 숫자(바이트)를 원래의 문자로 변환하는 과정입니다.</p>
</li>
</ul>
<p>프로그래밍 언어에서 문자열을 바이트로 변환할 때는 항상 <strong>문자 집합(Charset)</strong> 을 명시해야 합니다. 예를 들어, 자바에서는 <code>String.getBytes(Charset charset)</code> 메소드를 사용하여 인코딩할 수 있습니다.</p>
<hr>
<h2 id="2-아스키ascii-코드와-utf-8">2. 아스키(ASCII) 코드와 UTF-8</h2>
<h3 id="아스키ascii-문자-집합">아스키(ASCII) 문자 집합</h3>
<ul>
<li><strong>정의:</strong><br>아스키 코드는 128개의 문자(0~127)를 표현할 수 있는 인코딩 방식입니다.</li>
<li><strong>용도:</strong><br>주로 영어 알파벳, 숫자, 기본 구두점 등을 다룹니다.</li>
</ul>
<h3 id="utf-8-인코딩">UTF-8 인코딩</h3>
<ul>
<li><strong>특징:</strong>  <ul>
<li><strong>가변 길이 인코딩:</strong> 1바이트부터 최대 4바이트까지 사용하여 문자를 표현합니다.</li>
<li><strong>아스키와 호환:</strong> 아스키 범위(0~127)의 문자는 UTF-8에서도 동일한 바이트 값을 사용합니다.</li>
</ul>
</li>
<li><strong>효율성:</strong>  <ul>
<li><strong>저장 공간 절약:</strong> 대부분의 서양 문자와 ASCii 문자는 1바이트만 사용합니다.</li>
<li><strong>네트워크 효율성:</strong> 영문 텍스트를 전송할 때 UTF-8은 UTF-16보다 최대 2배 더 효율적입니다.<br>참고로 웹상의 문서 중 80% 이상이 영문이므로, UTF-8의 효율성은 매우 중요합니다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-실무에서의-문자-인코딩-활용">3. 실무에서의 문자 인코딩 활용</h2>
<h3 id="문자-집합-선택">문자 집합 선택</h3>
<p>문자열을 바이트로 변환할 때는 반드시 사용할 문자 집합(예: <code>UTF-8</code>, <code>EUC-KR</code>, <code>ISO-8859-1</code>)을 지정해야 합니다. 잘못된 문자 집합을 사용할 경우, 특히 한글과 같이 다국어 문자의 경우 <strong>문자 깨짐(garbled text)</strong> 문제가 발생할 수 있습니다.</p>
<h3 id="한글-깨짐-문제의-주요-원인">한글 깨짐 문제의 주요 원인</h3>
<ol>
<li><p><strong>EUC-KR과 UTF-8의 호환성 문제:</strong><br>UTF-8로 인코딩한 한글을 EUC-KR(ms949)로 디코딩하거나, 반대로 EUC-KR로 인코딩한 한글을 UTF-8로 디코딩할 때 문제가 발생합니다.</p>
</li>
<li><p><strong>ISO-8859-1과의 혼용:</strong><br>EUC-KR이나 UTF-8로 인코딩한 한글을 ISO-8859-1로 디코딩하면, 지원하지 않는 문자로 인해 깨짐 현상이 발생합니다.</p>
</li>
</ol>
<p>예시</p>
<pre><code>package charset;

import java.nio.charset.Charset;
import java.util.Arrays;

import static java.nio.charset.StandardCharsets.*;

public class EncodingMain2 {

    private static final Charset EUC_KR = Charset.forName(&quot;EUC-KR&quot;);
    private static final Charset MS_949 = Charset.forName(&quot;MS949&quot;);


    public static void main(String[] args) {
        System.out.println(&quot;== 영문 ASCII 인코딩 ==&quot;);
        test(&quot;A&quot;, US_ASCII, US_ASCII);
        test(&quot;A&quot;, US_ASCII, ISO_8859_1); // ASCII 확장(LATIN-1)
        test(&quot;A&quot;, US_ASCII, EUC_KR); // ASCII 포함
        test(&quot;A&quot;, US_ASCII, MS_949); // ASCII 포함
        test(&quot;A&quot;, US_ASCII, UTF_8); // ASCII 포함
        test(&quot;A&quot;, US_ASCII, UTF_16BE); // UTF_16 디코딩 실패


        System.out.println(&quot;== 한글 인코딩 - 기본 ==&quot;);
        test(&quot;가&quot;, US_ASCII, US_ASCII); // X
        test(&quot;가&quot;, ISO_8859_1, ISO_8859_1); // X
        test(&quot;가&quot;, EUC_KR, EUC_KR);
        test(&quot;가&quot;, MS_949, MS_949);
        test(&quot;가&quot;, UTF_8, UTF_8);
        test(&quot;가&quot;, UTF_16BE, UTF_16BE);


        System.out.println(&quot;== 한글 인코딩 - 복잡한 문자 ==&quot;);
        test(&quot;뷁&quot;, EUC_KR, EUC_KR); // X
        test(&quot;뷁&quot;, MS_949, MS_949);
        test(&quot;뷁&quot;, UTF_8, UTF_8);
        test(&quot;뷁&quot;, UTF_16BE, UTF_16BE);


        System.out.println(&quot;== 한글 인코딩 - 디코딩이 다른 경우 ==&quot;);
        test(&quot;가&quot;, EUC_KR, MS_949);
        test(&quot;뷁&quot;, MS_949, EUC_KR); // 인코딩 가능, 디코딩 X
        test(&quot;가&quot;, EUC_KR, UTF_8); // X
        test(&quot;가&quot;, MS_949, UTF_8); // X
        test(&quot;가&quot;, UTF_8, MS_949); // X


        System.out.println(&quot;== 영문 인코딩 - 디코딩이 다른 경우 ==&quot;);
        test(&quot;A&quot;, EUC_KR, UTF_8);
        test(&quot;A&quot;, MS_949, UTF_8);
        test(&quot;A&quot;, UTF_8, MS_949);
        test(&quot;A&quot;, UTF_8, UTF_16BE); // X
    }



    private static void test(String text, Charset encodingCharset, Charset decodingCharset){
        byte[] encoded = text.getBytes(encodingCharset);
        String decoded = new String(encoded, decodingCharset);
        System.out.printf(&quot;%s -&gt; [%s] 인코딩 -&gt; %s %sbyte -&gt; [%s] 디코딩 -&gt; %s\n&quot;,
                text, encodingCharset, Arrays.toString(encoded),
                encoded.length,
                decodingCharset, decoded);


    }


}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[스레드 생성 비용 및 Future 활용에 관한 고찰]]></title>
            <link>https://velog.io/@code_name_js/%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%9D%EC%84%B1-%EB%B9%84%EC%9A%A9-%EB%B0%8F-Future-%ED%99%9C%EC%9A%A9%EC%97%90-%EA%B4%80%ED%95%9C-%EA%B3%A0%EC%B0%B0</link>
            <guid>https://velog.io/@code_name_js/%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%9D%EC%84%B1-%EB%B9%84%EC%9A%A9-%EB%B0%8F-Future-%ED%99%9C%EC%9A%A9%EC%97%90-%EA%B4%80%ED%95%9C-%EA%B3%A0%EC%B0%B0</guid>
            <pubDate>Thu, 06 Mar 2025 05:31:22 GMT</pubDate>
            <description><![CDATA[<p>최근 애플리케이션의 성능 최적화와 동시에 동시성 처리가 중요한 이슈로 떠오르고 있습니다.<br>이 글에서는 스레드 생성 비용, Runnable 인터페이스의 한계, 그리고 ThreadPoolExecutor와 Future를 활용해<br>어떻게 효율적인 비동기 작업 처리를 할 수 있는지에 대해 자세히 알아보겠습니다.</p>
<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#1-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%9D%EC%84%B1-%EB%B9%84%EC%9A%A9%EA%B3%BC-%EC%84%B1%EB%8A%A5-%EB%AC%B8%EC%A0%9C">1. 스레드 생성 비용과 성능 문제</a></li>
<li><a href="#2-runnable-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4%EC%9D%98-%EB%B6%88%ED%8E%B8%ED%95%A8">2. Runnable 인터페이스의 불편함</a></li>
<li><a href="#3-threadpoolexecutor%EC%99%80-future%EC%9D%98-%EB%93%B1%EC%9E%A5">3. ThreadPoolExecutor와 Future의 등장</a></li>
<li><a href="#4-futureget-%ED%98%B8%EC%B6%9C%EA%B3%BC-blockingnon-blocking">4. Future.get() 호출과 Blocking/Non-Blocking</a></li>
<li><a href="#5-%EC%9E%91%EC%97%85-%EC%B7%A8%EC%86%8C-cancel-%EB%A9%94%EC%84%9C%EB%93%9C%EC%9D%98-%ED%99%9C%EC%9A%A9">5. 작업 취소: cancel() 메서드의 활용</a></li>
<li><a href="#6-%EC%B6%94%EA%B0%80%EC%A0%81%EC%9D%B8-%ED%8C%81%EA%B3%BC-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD">6. 추가적인 팁과 고려사항</a></li>
<li><a href="#%EA%B2%B0%EB%A1%A0">결론</a></li>
</ul>
<hr>
<h2 id="1-스레드-생성-비용과-성능-문제">1. 스레드 생성 비용과 성능 문제</h2>
<p>스레드를 직접 생성할 경우,  </p>
<ul>
<li><strong>메모리 할당</strong>: 새로운 스레드를 위한 스택 메모리 할당  </li>
<li><strong>운영체제 자원 사용</strong>: 스레드 관리를 위한 커널 자원 사용  </li>
<li><strong>스케줄러 설정</strong>: OS의 스케줄러가 각 스레드를 관리하도록 해야 함  </li>
</ul>
<p>이런 작업들은 상당한 비용을 수반합니다. 특히, 많은 작업을 짧은 시간 내에 처리해야 하는 경우라면<br>스레드 생성 및 소멸에 드는 오버헤드가 전체 성능 저하로 이어질 수 있습니다.</p>
<blockquote>
<p><strong>예시</strong>:  
다수의 작업을 순차적으로 처리하는 경우, 작업마다 새로운 스레드를 생성하면 매번 OS에 부담을 주게 됩니다.</p>
</blockquote>
<hr>
<h2 id="2-runnable-인터페이스의-불편함">2. Runnable 인터페이스의 불편함</h2>
<p>자바에서 스레드를 구현할 때 흔히 사용되는 <strong>Runnable 인터페이스</strong>는 다음과 같은 한계가 있습니다.</p>
<ul>
<li><strong>반환 값이 없음</strong>: Runnable의 <code>run()</code> 메서드는 실행 결과를 반환하지 않으므로, 작업 결과를 외부에서 직접 받아볼 수 없습니다.</li>
<li><strong>결과 전달 방식의 번거로움</strong>: 실행 결과를 별도의 멤버 변수에 저장한 후, <code>join()</code> 등을 통해 스레드가 종료되길 기다려야 합니다.</li>
</ul>
<p>이러한 한계를 극복하기 위해 등장한 것이 <strong>Callable 인터페이스</strong>입니다.<br>Callable은 결과를 반환할 수 있으며 예외 처리도 보다 유연하게 할 수 있습니다.</p>
<blockquote>
<p><strong>Runnable 예시</strong>:</p>
<pre><code class="language-java">public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 작업 수행
        System.out.println(&quot;Runnable 작업 수행&quot;);
    }
}</code></pre>
</blockquote>
<blockquote>
<p><strong>Callable 예시</strong>:</p>
<pre><code class="language-java">public class MyCallable implements Callable&lt;Integer&gt; {
    @Override
    public Integer call() {
        // 작업 수행 후 결과 반환
        return 42;
    }
}</code></pre>
</blockquote>
<hr>
<h2 id="3-threadpoolexecutor와-future의-등장">3. ThreadPoolExecutor와 Future의 등장</h2>
<p>스레드 생성 비용 문제와 Runnable의 한계를 보완하기 위해 <strong>ThreadPoolExecutor</strong>를 사용합니다.<br>ThreadPoolExecutor는 요청이 들어올 때마다 새로운 스레드를 생성하는 대신, 미리 생성된 스레드 풀을 활용해 작업을 할당합니다.</p>
<ul>
<li><strong>스레드 풀의 장점</strong>:  <ul>
<li>스레드 생성 오버헤드 감소</li>
<li>재사용 가능한 스레드로 효율적인 자원 관리</li>
<li>작업 큐를 통한 작업 관리</li>
</ul>
</li>
</ul>
<p>또한, <code>submit()</code> 메서드를 사용하면 <strong>Future</strong> 객체가 즉시 반환되어 요청 스레드가 블로킹되지 않습니다.</p>
<blockquote>
<p><strong>예시</strong>:</p>
<pre><code class="language-java">ExecutorService executor = Executors.newFixedThreadPool(5);
Future&lt;Integer&gt; future = executor.submit(new MyCallable());
// 요청 스레드는 다른 작업을 수행할 수 있음.</code></pre>
</blockquote>
<hr>
<h2 id="4-futureget-호출과-blockingnon-blocking">4. Future.get() 호출과 Blocking/Non-Blocking</h2>
<p>Future 객체는 제출된 작업의 미래 결과를 나타냅니다.<br><code>Future.get()</code> 메서드를 호출하면 두 가지 상황이 발생할 수 있습니다:</p>
<ol>
<li><strong>작업이 완료된 경우</strong>  <ul>
<li>Future 내부에 작업 결과가 존재하므로, 즉시 반환됩니다.</li>
</ul>
</li>
<li><strong>작업이 아직 완료되지 않은 경우</strong>  <ul>
<li>Future는 결과가 준비될 때까지 요청 스레드를 블로킹 상태로 만듭니다.</li>
</ul>
</li>
</ol>
<p>이러한 동작 덕분에 요청 스레드는 작업 결과가 필요한 시점에만 기다리게 되어,<br>비동기적으로 작업을 수행하면서도 결과를 적시에 받을 수 있습니다.</p>
<blockquote>
<p><strong>예시</strong>:</p>
<pre><code class="language-java">// 비동기적으로 두 작업을 제출
Future&lt;Integer&gt; future1 = executor.submit(task1);
Future&lt;Integer&gt; future2 = executor.submit(task2);

// 필요 시 결과를 블로킹 호출로 받아올 수 있음
Integer result1 = future1.get();
Integer result2 = future2.get();</code></pre>
</blockquote>
<hr>
<h2 id="5-작업-취소-cancel-메서드의-활용">5. 작업 취소: cancel() 메서드의 활용</h2>
<p>Future는 아직 완료되지 않은 작업을 취소할 수 있는 기능도 제공합니다.<br><code>cancel(boolean mayInterruptIfRunning)</code> 메서드는 다음과 같이 동작합니다:</p>
<ul>
<li><strong>cancel(true)</strong>:  <ul>
<li>Future를 취소 상태로 변경하고, 만약 작업이 실행 중이라면 <code>Thread.interrupt()</code>를 호출하여 중단시킵니다.</li>
</ul>
</li>
<li><strong>cancel(false)</strong>:  <ul>
<li>Future를 취소 상태로 변경하지만, 실행 중인 작업은 중단시키지 않습니다.</li>
</ul>
</li>
</ul>
<p>이러한 기능을 통해 필요 없는 작업에 대한 자원 낭비를 방지할 수 있습니다.</p>
<blockquote>
<p><strong>예시</strong>:</p>
<pre><code class="language-java">// 작업 취소 예제
if (!future.isDone()) {
    future.cancel(true); // 실행 중인 작업이라도 중단을 시도
}</code></pre>
</blockquote>
<hr>
<h2 id="6-추가적인-팁과-고려사항">6. 추가적인 팁과 고려사항</h2>
<ul>
<li><p><strong>예외 처리</strong>:  
Future의 <code>get()</code> 메서드 호출 시 <code>InterruptedException</code>과 <code>ExecutionException</code>에 대한 처리가 필요합니다.<br>예외 상황에 대비하여 적절한 로그 기록이나 리트라이 로직을 구현하세요.</p>
</li>
<li><p><strong>스레드 풀 사이즈</strong>:  
애플리케이션의 특성에 맞춰 스레드 풀의 크기를 조정하세요.<br>너무 작은 풀은 작업 대기열이 쌓일 수 있고, 너무 큰 풀은 자원 낭비를 초래할 수 있습니다.</p>
</li>
<li><p><strong>자원 정리</strong>:  
ExecutorService 사용 후에는 <code>shutdown()</code> 또는 <code>shutdownNow()</code>를 호출해 스레드 풀을 적절히 종료하는 것이 좋습니다.</p>
</li>
<li><p><strong>실시간 모니터링</strong>:  
운영 중인 스레드 풀의 상태를 모니터링하여, 필요 시 동적으로 스레드 수를 조정하거나 문제를 사전에 파악할 수 있도록 하세요.</p>
</li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>스레드 생성 비용으로 인한 성능 문제와 Runnable 인터페이스의 한계를 극복하기 위해,<br>자바에서는 ThreadPoolExecutor와 Future를 통한 비동기 처리 기법을 제공합니다.<br>이를 통해 불필요한 스레드 생성 오버헤드를 줄이고,<br>요청 스레드가 필요할 때만 결과를 블로킹하여 받아올 수 있는 유연한 구조를 구현할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[생산자-소비자 문제 (Producer-Consumer Problem)]]></title>
            <link>https://velog.io/@code_name_js/%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C-Producer-Consumer-Problem</link>
            <guid>https://velog.io/@code_name_js/%EC%83%9D%EC%82%B0%EC%9E%90-%EC%86%8C%EB%B9%84%EC%9E%90-%EB%AC%B8%EC%A0%9C-Producer-Consumer-Problem</guid>
            <pubDate>Fri, 21 Feb 2025 04:06:40 GMT</pubDate>
            <description><![CDATA[<p>멀티스레드 프로그래밍을 하다 보면 생산자-소비자 문제(Producer-Consumer Problem)를 자주 만나게 됩니다. 이 문제는 생산자가 데이터를 생성하고, 소비자가 그 데이터를 소비하는 과정에서 발생하는 동시성 문제입니다.</p>
<h2 id="1-생산자-소비자-문제란">1. 생산자-소비자 문제란?</h2>
<p>생산자(Producer)와 소비자(Consumer)가 한정된 버퍼(Buffer)를 통해 데이터를 주고받는 상황을 가정합니다.</p>
<ul>
<li><strong>생산자(Producer)</strong>: 데이터를 생성하여 버퍼에 저장하는 역할.</li>
<li><strong>소비자(Consumer)</strong>: 버퍼에서 데이터를 가져와 사용하는 역할.</li>
<li><strong>버퍼(Buffer)</strong>: 생산자가 생성한 데이터를 일시적으로 저장하는 공간. 크기가 제한됨.</li>
</ul>
<h3 id="문제-상황">문제 상황</h3>
<ul>
<li><strong>생산자가 너무 빠름</strong> → 버퍼가 가득 차면 더 이상 데이터를 넣을 수 없음. </li>
<li><strong>소비자가 너무 빠름</strong> → 버퍼가 비어있으면 소비할 데이터가 없음.</li>
</ul>
<p>이를 해결하기 위해 <strong>생산자는 버퍼가 가득 찼을 때 대기</strong>, <strong>소비자는 버퍼가 비었을 때 대기</strong>해야 합니다.</p>
<h2 id="2-한정된-버퍼-문제-bounded-buffer-problem">2. 한정된 버퍼 문제 (Bounded-Buffer Problem)</h2>
<p>생산자-소비자 문제는 결국 <strong>버퍼의 크기가 제한되어 있기 때문</strong>에 발생합니다. 이를 해결하기 위해 <strong>스레드 동기화(Synchronization)</strong>가 필요합니다.</p>
<h2 id="3-해결-방법-wait와-notify-사용">3. 해결 방법: wait()와 notify() 사용</h2>
<p>자바에서는 <code>wait()</code>과 <code>notify()</code>를 사용하여 이 문제를 해결할 수 있습니다.</p>
<h3 id="wait-메서드"><code>wait()</code> 메서드</h3>
<ul>
<li>현재 스레드가 가진 락(Lock)을 반납하고 대기.</li>
<li>synchronized 블록 안에서만 호출 가능.</li>
<li>다른 스레드가 <code>notify()</code>를 호출하면 다시 실행됨.</li>
</ul>
<h3 id="notify-메서드"><code>notify()</code> 메서드</h3>
<ul>
<li><code>wait()</code> 상태에서 대기 중인 스레드 중 하나를 깨움.</li>
<li>synchronized 블록 안에서 호출해야 함.</li>
</ul>
<h3 id="notifyall-메서드"><code>notifyAll()</code> 메서드</h3>
<ul>
<li><code>wait()</code> 상태에서 대기 중인 <strong>모든</strong> 스레드를 깨움.</li>
</ul>
<h2 id="4-구현-코드-java">4. 구현 코드 (Java)</h2>
<p>아래는 <code>wait()</code>와 <code>notify()</code>를 사용하여 <strong>생산자가 버퍼가 가득 찼을 때 대기하고, 소비자가 버퍼가 비었을 때 대기하는 코드</strong>입니다.</p>
<pre><code class="language-java">package thread.bounded;

import java.util.ArrayDeque;
import java.util.Queue;

import static util.MyLogger.log;

public class BoundedQueueV3 implements BoundedQueue {

    private final Queue&lt;String&gt; queue = new ArrayDeque&lt;&gt;();
    private final int max;

    public BoundedQueueV3(int max) {
        this.max = max;
    }

    @Override
    public synchronized void put(String data) {
        while (queue.size() == max) {
            log(&quot;[put] 큐가 가득 참. 생산자 대기&quot;);
            try {
                wait(); // 큐가 가득 찼으므로 대기
                log(&quot;[put] 생산자 깨어남&quot;);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        queue.offer(data);
        log(&quot;[put] 생산자 데이터 저장, notify() 호출&quot;);
        notify(); // 대기 중인 소비자 깨움
    }

    @Override
    public synchronized String take() {
        while (queue.isEmpty()) {
            log(&quot;[take] 큐에 데이터가 없음, 소비자 대기&quot;);
            try {
                wait(); // 큐가 비었으므로 대기
                log(&quot;[take] 소비자 깨어남&quot;);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        String data = queue.poll();
        log(&quot;[take] 소비자 데이터 획득, notify() 호출&quot;);
        notify(); // 대기 중인 생산자 깨움
        return data;
    }

    @Override
    public String toString() {
        return queue.toString();
    }
}</code></pre>
<h2 id="5-코드-설명">5. 코드 설명</h2>
<h3 id="1-생산자-put-메서드">1) 생산자 (<code>put</code> 메서드)</h3>
<ul>
<li><code>synchronized</code>를 사용하여 동기화.</li>
<li>큐가 가득 차면 <code>wait()</code>을 호출하여 락을 반납하고 대기.</li>
<li>빈 공간이 생기면 <code>notify()</code>를 호출하여 대기 중인 소비자를 깨움.</li>
</ul>
<h3 id="2-소비자-take-메서드">2) 소비자 (<code>take</code> 메서드)</h3>
<ul>
<li><code>synchronized</code>를 사용하여 동기화.</li>
<li>큐가 비어있으면 <code>wait()</code>을 호출하여 락을 반납하고 대기.</li>
<li>데이터가 들어오면 <code>notify()</code>를 호출하여 대기 중인 생산자를 깨움.</li>
</ul>
<h2 id="6-실행-흐름">6. 실행 흐름</h2>
<ol>
<li>생산자 스레드가 데이터를 넣으려 하지만 <strong>버퍼가 가득 차면 대기</strong> (<code>wait()</code>).</li>
<li>소비자가 데이터를 가져가면 <strong>생산자에게 공간이 생겼다고 알림</strong> (<code>notify()</code>).</li>
<li>소비자 스레드는 데이터를 가져가려 하지만 <strong>버퍼가 비어 있으면 대기</strong> (<code>wait()</code>).</li>
<li>생산자가 새로운 데이터를 넣으면 <strong>소비자에게 데이터를 사용할 수 있다고 알림</strong> (<code>notify()</code>).</li>
</ol>
<h2 id="7-결론">7. 결론</h2>
<ul>
<li><strong><code>wait()</code>와 <code>notify()</code>를 사용하면 생산자-소비자 문제를 효과적으로 해결</strong>할 수 있다.</li>
<li><strong>스레드가 필요할 때만 대기하고, 필요할 때만 실행되므로 성능이 향상</strong>된다.</li>
<li><strong>synchronized 블록 내에서 <code>wait()</code>와 <code>notify()</code>를 사용해야 한다.</strong></li>
</ul>
<p>이렇게 하면 생산자가 데이터를 버리거나, 소비자가 빈 데이터를 가져가는 문제를 해결할 수 있습니다! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바의 Synchronized와 Lock의 차이점 및 단점]]></title>
            <link>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94%EC%9D%98-Synchronized%EC%99%80-Lock%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B0%8F-%EB%8B%A8%EC%A0%90</link>
            <guid>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94%EC%9D%98-Synchronized%EC%99%80-Lock%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%B0%8F-%EB%8B%A8%EC%A0%90</guid>
            <pubDate>Fri, 21 Feb 2025 01:28:07 GMT</pubDate>
            <description><![CDATA[<p>자바에서 동기화를 위해 <code>synchronized</code> 키워드를 많이 사용하지만, 몇 가지 단점이 있습니다. 이를 해결하기 위해 <code>Lock</code> 인터페이스와 <code>ReentrantLock</code>이 등장했습니다. 이번 글에서는 <code>synchronized</code>의 단점과 <code>Lock</code>의 동작 방식에 대해 쉽게 설명하겠습니다.</p>
<hr>
<h2 id="🔒-synchronized의-단점">🔒 synchronized의 단점</h2>
<h3 id="1️⃣-무한-대기-blocked-상태">1️⃣ 무한 대기 (Blocked 상태)</h3>
<p><code>synchronized</code>를 사용하면, 스레드가 락을 획득하지 못할 경우 <strong>무한 대기</strong>하게 됩니다. 즉, 락이 풀릴 때까지 계속 기다려야 하며 다음과 같은 기능이 없습니다.</p>
<ul>
<li>특정 시간까지만 대기하는 <strong>타임아웃 기능❌</strong></li>
<li>중간에 다른 스레드가 강제로 깨우는 <strong>인터럽트 기능❌</strong></li>
</ul>
<p>이러한 문제 때문에 스레드가 <strong>영원히 실행되지 못하는 상황</strong>이 발생할 수도 있습니다.</p>
<h3 id="2️⃣-공정성-문제">2️⃣ 공정성 문제</h3>
<p>락이 풀렸을 때 <strong>어떤 스레드가 락을 획득할지 보장되지 않습니다</strong>. 여러 개의 스레드가 대기 중이라면 특정 스레드가 <strong>너무 오랫동안 락을 얻지 못할 수도 있음</strong> (기아 현상 발생 가능).</p>
<p>➡️ 이러한 문제를 해결하기 위해 <strong><code>Lock</code> 인터페이스와 <code>ReentrantLock</code>이 등장</strong>했습니다.</p>
<hr>
<h2 id="🔑-lock과-locksupport-활용">🔑 Lock과 LockSupport 활용</h2>
<p><code>Lock</code> 인터페이스는 <code>synchronized</code>와 다르게 <strong>객체 내부에 있는 모니터 락을 사용하지 않고</strong>, 별도로 락을 관리할 수 있습니다. 대표적인 구현체는 <code>ReentrantLock</code>입니다.</p>
<h3 id="✅-lock-선언-및-사용-방법">✅ Lock 선언 및 사용 방법</h3>
<pre><code class="language-java">private final Lock lock = new ReentrantLock();</code></pre>
<p><code>synchronized(this)</code> 대신 <code>lock.lock()</code>을 사용하여 락을 획득합니다.</p>
<pre><code class="language-java">lock.lock();
try {
    // 임계 영역
} finally {
    lock.unlock(); // 반드시 unlock() 호출해야 함!
}</code></pre>
<blockquote>
<p><strong>💡주의!</strong> <code>lock.unlock()</code>은 반드시 <code>finally</code> 블록에서 호출해야 합니다.
그래야 중간에 예외가 발생해도 락이 정상적으로 해제됨.</p>
</blockquote>
<hr>
<h2 id="⚡-lock의-대기-큐와-locksupport">⚡ Lock의 대기 큐와 LockSupport</h2>
<p><code>ReentrantLock</code> 내부에는 <strong>대기 큐</strong>가 존재합니다. 락을 얻지 못한 스레드는 <strong>대기 큐에 저장되고</strong> 이후 락이 해제되면 차례로 깨워집니다.</p>
<h3 id="✅-locksupport-활용">✅ LockSupport 활용</h3>
<p>자바에서 스레드를 대기 상태로 만들고 깨우는 기능은 <code>LockSupport</code> 클래스를 활용합니다.</p>
<pre><code class="language-java">LockSupport.park();</code></pre>
<p>위 코드를 실행하면 스레드는 <code>WAITING</code> 상태가 됩니다.</p>
<pre><code class="language-java">LockSupport.unpark(thread);</code></pre>
<p>외부에서 스레드를 깨울 수 있습니다.</p>
<h3 id="🧐-blocked-vs-waiting-vs-timed_waiting">🧐 <code>Blocked</code> vs <code>Waiting</code> vs <code>Timed_Waiting</code></h3>
<table>
<thead>
<tr>
<th>상태</th>
<th>특징</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>Blocked</code></td>
<td>락을 기다리는 상태, 인터럽트해도 빠져나올 수 없음</td>
<td><code>synchronized</code> 사용 시 발생</td>
</tr>
<tr>
<td><code>Waiting</code></td>
<td>외부에서 깨울 때까지 대기</td>
<td><code>LockSupport.park()</code>, <code>Object.wait()</code></td>
</tr>
<tr>
<td><code>Timed_Waiting</code></td>
<td>일정 시간만 기다림</td>
<td><code>Thread.sleep(ms)</code>, <code>LockSupport.parkNanos(ns)</code></td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <code>synchronized</code>에서는 <code>Blocked</code> 상태만 존재하지만, <code>Lock</code>을 사용하면 더 유연한 대기 방식이 가능함.</p>
</blockquote>
<hr>
<h2 id="🔄-reentrantlock-동작-방식">🔄 ReentrantLock 동작 방식</h2>
<p>1️⃣ 스레드 <code>t2</code>가 락을 획득하지 못하면 <code>Waiting</code> 상태가 되고 <strong>대기 큐에 등록됨</strong> (<code>LockSupport.park()</code> 호출됨).</p>
<p>2️⃣ <code>t1</code>이 락을 사용한 후 <code>unlock()</code>을 호출하면 <strong>대기 큐에서 대기 중인 스레드를 깨움</strong> (<code>LockSupport.unpark(thread)</code> 호출됨).</p>
<p>3️⃣ <code>t2</code>는 <code>Runnable</code> 상태로 전환되고, 다시 락을 획득하려고 시도함.</p>
<p>➡️ 이렇게 하면 <code>synchronized</code>보다 <strong>더 정교하게 동기화를 제어할 수 있음</strong>.</p>
<hr>
<h2 id="✅-정리">✅ 정리</h2>
<table>
<thead>
<tr>
<th>비교</th>
<th><code>synchronized</code></th>
<th><code>Lock (ReentrantLock)</code></th>
</tr>
</thead>
<tbody><tr>
<td>대기 방식</td>
<td>무한 대기 (Blocked)</td>
<td>타임아웃, 인터럽트 가능</td>
</tr>
<tr>
<td>공정성</td>
<td>보장 X</td>
<td><code>fair</code> 설정 가능 (공정한 락 분배)</td>
</tr>
<tr>
<td>사용 방식</td>
<td>JVM 모니터 락 사용</td>
<td>직접 락 관리 (더 유연함)</td>
</tr>
<tr>
<td>대기 상태</td>
<td><code>Blocked</code></td>
<td><code>Waiting</code>, <code>Timed_Waiting</code> 지원</td>
</tr>
</tbody></table>
<p>👉 <code>synchronized</code>는 간단하지만 제한적이고, <code>Lock</code>은 유연하지만 직접 관리해야 한다는 점이 다름!</p>
<hr>
<h2 id="🚀-결론">🚀 결론</h2>
<ul>
<li><code>synchronized</code>는 기본적인 동기화에는 적합하지만 <strong>무한 대기, 공정성 문제</strong>가 있음.</li>
<li><code>Lock (ReentrantLock)</code>을 사용하면 <strong>더 유연한 락 제어</strong>가 가능하고 <strong>공정성</strong>도 보장할 수 있음.</li>
<li>하지만 <code>lock.lock()</code>을 사용한 경우 반드시 <code>unlock()</code>을 <strong>명시적으로 호출해야 함</strong> (안 하면 교착 상태 발생 가능!).</li>
<li><code>LockSupport</code>를 활용하면 특정 스레드를 직접 깨울 수도 있음.</li>
</ul>
<p>➡️ 따라서 복잡한 동기화가 필요한 경우 <code>Lock</code>을 적극적으로 활용하는 것이 좋음! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 멀티스레드 동시성 문제와 해결 방법]]></title>
            <link>https://velog.io/@code_name_js/Java-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C%EC%99%80-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@code_name_js/Java-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C%EC%99%80-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 17 Feb 2025 07:48:02 GMT</pubDate>
            <description><![CDATA[<h2 id="1-동시성-문제란">1. 동시성 문제란?</h2>
<p>멀티스레드를 사용할 때 가장 주의해야 할 점은 <strong>여러 스레드가 공유 자원에 동시에 접근하는 것</strong>으로 인해 발생하는 <strong>동시성 문제</strong>입니다.</p>
<p>대표적인 공유 자원은 <strong>인스턴스 필드(멤버 변수)</strong>입니다.
만약 여러 스레드가 동시에 공유 자원을 읽고 수정한다면, 데이터 불일치나 예상치 못한 동작이 발생할 수 있습니다.</p>
<h2 id="2-예제-코드">2. 예제 코드</h2>
<p>아래는 은행 계좌에서 출금을 처리하는 <code>BankAccountV1</code>과 <code>BankAccountV2</code> 클래스입니다.</p>
<h3 id="1-동시성-문제-발생-코드-bankaccountv1">(1) 동시성 문제 발생 코드: <code>BankAccountV1</code></h3>
<pre><code class="language-java">public class BankAccountV1 implements BankAccount {
    private int balance;

    public BankAccountV1(int initialBalance) {
        this.balance = initialBalance;
    }

    @Override
    public boolean widthdraw(int amount) {
        log(&quot;거래 시작: &quot; + getClass().getSimpleName());
        log(&quot;[검증 시작] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
        if (balance &lt; amount) {
            log(&quot;[검증 실패] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
            return false;
        }
        log(&quot;[검증 완료] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
        sleep(1000); // 출금 처리 시간
        balance = balance - amount;
        log(&quot;[출금 완료] 출금액: &quot; + amount + &quot;, 변경 잔액: &quot; + balance);
        log(&quot;거래 종료&quot;);
        return true;
    }

    @Override
    public int getBalance() {
        return balance;
    }
}</code></pre>
<h3 id="2-문제-발생-시나리오">(2) 문제 발생 시나리오</h3>
<p>아래처럼 두 개의 스레드(<code>t1</code>, <code>t2</code>)가 동시에 출금을 시도하면 문제가 발생할 수 있습니다.</p>
<pre><code class="language-java">public static void main(String[] args) throws InterruptedException {
    BankAccount account = new BankAccountV1(1000);

    Thread t1 = new Thread(new WithdrawTask(account, 800), &quot;t1&quot;);
    Thread t2 = new Thread(new WithdrawTask(account, 800), &quot;t2&quot;);
    t1.start();
    t2.start();

    sleep(500);
    log(&quot;t1 state: &quot; + t1.getState());
    log(&quot;t2 state: &quot; + t2.getState());
    t1.join();
    t2.join();
    log(&quot;최종 잔액: &quot; + account.getBalance());
}</code></pre>
<h3 id="3-문제-설명">(3) 문제 설명</h3>
<ol>
<li><code>t1</code>과 <code>t2</code>가 거의 동시에 <code>balance</code>를 확인합니다. (<code>balance = 1000</code>)</li>
<li><code>t1</code>은 800원을 출금하기 위해 검증을 통과합니다.</li>
<li><code>t2</code>도 같은 시점에서 800원을 출금할 수 있다고 판단합니다.</li>
<li><code>t1</code>이 출금을 완료하여 잔액이 200원이 됩니다.</li>
<li><code>t2</code>도 출금을 시도하여 잔액이 -600원이 되어버립니다. ❌(비정상 동작)</li>
</ol>
<h2 id="3-해결-방법-synchronized-키워드-사용">3. 해결 방법: <code>synchronized</code> 키워드 사용</h2>
<p>멀티스레드 환경에서 <strong>한 번에 하나의 스레드만 실행</strong>하도록 하려면 <strong>임계 영역(critical section)을 보호</strong>해야 합니다.</p>
<p>Java에서는 <code>synchronized</code> 키워드를 사용하여 공유 자원에 대한 동기화를 적용할 수 있습니다.</p>
<h3 id="1-동기화-적용-코드-bankaccountv2">(1) 동기화 적용 코드: <code>BankAccountV2</code></h3>
<pre><code class="language-java">public class BankAccountV2 implements BankAccount {
    private int balance;

    public BankAccountV2(int initialBalance) {
        this.balance = initialBalance;
    }

    @Override
    public synchronized boolean widthdraw(int amount) {
        log(&quot;거래 시작: &quot; + getClass().getSimpleName());
        log(&quot;[검증 시작] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
        if (balance &lt; amount) {
            log(&quot;[검증 실패] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
            return false;
        }
        log(&quot;[검증 완료] 출금액: &quot; + amount + &quot;, 잔액: &quot; + balance);
        sleep(1000); // 출금 처리 시간
        balance = balance - amount;
        log(&quot;[출금 완료] 출금액: &quot; + amount + &quot;, 변경 잔액: &quot; + balance);
        log(&quot;거래 종료&quot;);
        return true;
    }

    @Override
    public int getBalance() {
        return balance;
    }
}</code></pre>
<h3 id="2-synchronized가-하는-일">(2) <code>synchronized</code>가 하는 일</h3>
<ul>
<li><code>synchronized</code> 키워드를 메서드에 추가하면 <strong>한 번에 하나의 스레드만 해당 메서드를 실행</strong>할 수 있습니다.</li>
<li>즉, <code>widthdraw()</code> 메서드가 실행 중이면 다른 스레드는 해당 메서드에 접근할 수 없습니다.</li>
<li>덕분에 <code>balance</code> 값이 변경되는 과정이 안전하게 보호됩니다.</li>
</ul>
<h3 id="3-동기화-적용-후-결과">(3) 동기화 적용 후 결과</h3>
<ul>
<li><code>t1</code>이 <code>balance</code>를 확인하고 출금을 시작하면, <code>t2</code>는 대기합니다.</li>
<li><code>t1</code>이 출금을 완료한 후 <code>t2</code>가 실행되므로, <code>t2</code>는 출금 불가(잔액 부족) 상태를 올바르게 인식합니다.</li>
<li>결과적으로 데이터 불일치 문제가 해결됩니다.</li>
</ul>
<h2 id="4-정리">4. 정리</h2>
<table>
<thead>
<tr>
<th>해결 방법</th>
<th>동시성 문제 발생 여부</th>
</tr>
</thead>
<tbody><tr>
<td><code>BankAccountV1</code> (동기화 X)</td>
<td>✅ 발생 (출금 중간에 다른 스레드 개입 가능)</td>
</tr>
<tr>
<td><code>BankAccountV2</code> (<code>synchronized</code> 사용)</td>
<td>❌ 해결 (한 번에 하나의 스레드만 실행)</td>
</tr>
</tbody></table>
<p>멀티스레드 환경에서 <strong>공유 자원에 대한 동기화</strong>는 필수입니다. <code>synchronized</code>를 사용하면 <strong>임계 영역을 보호</strong>하여 데이터 불일치를 방지할 수 있습니다. 🚀</p>
<p>김영한님의 강의를 참고해서 만들었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[메모리 가시성 (Memory Visibility)]]></title>
            <link>https://velog.io/@code_name_js/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1-Memory-Visibility</link>
            <guid>https://velog.io/@code_name_js/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1-Memory-Visibility</guid>
            <pubDate>Mon, 17 Feb 2025 04:42:57 GMT</pubDate>
            <description><![CDATA[<h2 id="💡-cpu와-메모리의-관계">💡 CPU와 메모리의 관계</h2>
<p>CPU는 매우 빠르게 연산을 수행하지만, 메모리 속도는 상대적으로 느립니다.
이 문제를 해결하기 위해 <strong>캐시 메모리(Cache Memory)</strong>가 사용됩니다.</p>
<h3 id="🖥️-cpu와-메모리-구조">🖥️ CPU와 메모리 구조</h3>
<pre><code>main 스레드 -&gt; CPU 코어1 -&gt; 캐시 메모리 -&gt; 메인 메모리
work 스레드 -&gt; CPU 코어2 -&gt; 캐시 메모리 -&gt; 메인 메모리</code></pre><ul>
<li><strong>메인 메모리(RAM)</strong>: 용량이 크지만 CPU 입장에서 거리가 멀고 속도가 느림</li>
<li><strong>캐시 메모리(Cache Memory)</strong>: CPU 가까이에 있으며 속도가 매우 빠름, 하지만 용량이 작고 가격이 비쌈</li>
</ul>
<h2 id="🔥-멀티-스레드에서의-문제-메모리-가시성">🔥 멀티 스레드에서의 문제: 메모리 가시성</h2>
<h3 id="🎭-runflag-문제-상황">🎭 runFlag 문제 상황</h3>
<pre><code class="language-java">volatile boolean runFlag = true;

Thread mainThread = new Thread(() -&gt; {
    runFlag = false;
});

Thread workThread = new Thread(() -&gt; {
    while (runFlag) {
        // do something...
    }
});</code></pre>
<p>위 코드에서 <code>mainThread</code>가 <code>runFlag</code> 값을 <code>false</code>로 변경해도,
<code>workThread</code>에서는 여전히 <code>true</code>로 보일 수 있습니다. 왜 그럴까요?</p>
<h3 id="🧠-캐시-메모리의-역할">🧠 캐시 메모리의 역할</h3>
<p>각 스레드는 CPU 코어에서 실행되며, 코어는 캐시 메모리를 활용합니다.
<strong>runFlag 값이 캐시 메모리에 저장되면, 각 코어는 자신의 캐시 값을 계속 사용</strong>하게 됩니다.
즉, <code>mainThread</code>가 <code>runFlag = false;</code>로 변경해도, <code>workThread</code>가 실행되는 CPU 코어의 캐시 메모리에는 반영되지 않을 수 있습니다.</p>
<h2 id="🕵️♂️-언제-메인-메모리에-반영될까">🕵️‍♂️ 언제 메인 메모리에 반영될까?</h2>
<p>이 부분은 <strong>CPU 설계 및 캐시 동기화 정책</strong>에 따라 다릅니다. 즉, <strong>명확한 시점을 보장할 수 없습니다</strong>.</p>
<ul>
<li>어떤 CPU에서는 <strong>즉시 반영될 수도</strong> 있고,</li>
<li>어떤 CPU에서는 <strong>한참 뒤에 반영될 수도</strong> 있습니다.</li>
</ul>
<p>이런 <strong>메모리 가시성 문제</strong>를 해결하지 않으면, 멀티 스레드 프로그래밍에서 <strong>의도치 않은 동작</strong>이 발생할 수 있습니다.</p>
<h2 id="🏆-해결-방법">🏆 해결 방법</h2>
<h3 id="✅-volatile-키워드-사용">✅ <code>volatile</code> 키워드 사용</h3>
<pre><code class="language-java">volatile boolean runFlag = true;</code></pre>
<ul>
<li><code>volatile</code>을 사용하면 <strong>모든 스레드가 항상 메인 메모리 값을 읽고 씁니다.</strong></li>
<li>즉, 캐시를 거치지 않기 때문에 <strong>변경된 값이 모든 스레드에 즉시 반영</strong>됩니다.</li>
</ul>
<h3 id="✅-synchronized-블록-사용">✅ <code>synchronized</code> 블록 사용</h3>
<pre><code class="language-java">synchronized (this) {
    runFlag = false;
}</code></pre>
<ul>
<li><code>synchronized</code>를 사용하면 <strong>스레드 간 동기화</strong>가 보장됩니다.</li>
<li>하지만 <strong>성능이 저하</strong>될 수 있으므로 주의해야 합니다.</li>
</ul>
<h3 id="✅-lock-사용">✅ <code>Lock</code> 사용</h3>
<pre><code class="language-java">Lock lock = new ReentrantLock();
lock.lock();
try {
    runFlag = false;
} finally {
    lock.unlock();
}</code></pre>
<ul>
<li><code>Lock</code>을 사용하면 <code>synchronized</code>보다 유연한 동기화가 가능합니다.</li>
</ul>
<h2 id="📌-정리">📌 정리</h2>
<ul>
<li><strong>멀티 스레드 환경에서는 메모리 가시성 문제가 발생할 수 있다.</strong></li>
<li><strong>캐시 메모리 때문에 스레드마다 다른 값을 볼 수도 있다.</strong></li>
<li><strong>메모리 가시성 문제를 해결하려면 <code>volatile</code>, <code>synchronized</code>, <code>Lock</code> 등을 활용해야 한다.</strong></li>
</ul>
<p>이제 멀티 스레드 프로그래밍을 할 때 <strong>메모리 가시성</strong>을 꼭 고려 하시길 바랍니다.!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java에서 스레드 인터럽트 (Thread Interrupt) 제대로 이해하기]]></title>
            <link>https://velog.io/@code_name_js/Java%EC%97%90%EC%84%9C-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8-Thread-Interrupt-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@code_name_js/Java%EC%97%90%EC%84%9C-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8-Thread-Interrupt-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 12 Feb 2025 08:50:30 GMT</pubDate>
            <description><![CDATA[<h2 id="스레드-인터럽트란">스레드 인터럽트란?</h2>
<p>Java에서 <code>interrupt()</code> 메서드를 사용하면 <strong>Waiting, Timed_Waiting 상태의 스레드를 직접 깨워</strong> Runnable 상태로 만들 수 있다. 하지만 <code>interrupt()</code>를 호출한다고 해서 <strong>즉시</strong> <code>InterruptedException</code>이 발생하는 것은 아니다. 오직 <code>sleep()</code>, <code>wait()</code>, <code>join()</code> 같은 <strong>인터럽트 예외를 던지는 메서드</strong>를 호출하거나 호출 중일 때만 예외가 발생한다.</p>
<h2 id="예제-코드로-살펴보는-인터럽트">예제 코드로 살펴보는 인터럽트</h2>
<p>아래 여러 버전의 코드를 통해 스레드를 중단하는 다양한 방법을 확인해 보자.</p>
<hr>
<h3 id="1-volatile-플래그를-사용한-방법-threadstopmainv1">1. <code>volatile</code> 플래그를 사용한 방법 (ThreadStopMainV1)</h3>
<pre><code class="language-java">static class MyTask implements Runnable {
    volatile boolean runFlag = true;

    @Override
    public void run() {
        while (runFlag) {
            log(&quot;작업중&quot;);
            sleep(3000);
        }
        log(&quot;자원 정리&quot;);
        log(&quot;작업 종료&quot;);
    }
}</code></pre>
<ul>
<li><code>volatile</code> 변수를 사용하여 스레드가 <code>false</code>를 감지하면 반복문을 종료하도록 설계했다.</li>
<li>하지만 이 방법은 <code>sleep()</code> 상태에서는 즉시 반응하지 않기 때문에 <strong>반응 속도가 느릴 수 있다</strong>.</li>
</ul>
<hr>
<h3 id="2-interrupt를-사용한-방법-threadstopmainv2">2. <code>interrupt()</code>를 사용한 방법 (ThreadStopMainV2)</h3>
<pre><code class="language-java">static class MyTask implements Runnable {
    @Override
    public void run() {
        try {
            while (true) {
                log(&quot;작업중&quot;);
                Thread.sleep(3000);
            }
        } catch (InterruptedException e) {
            log(&quot;인터럽트 발생: &quot; + e.getMessage());
        }
        log(&quot;자원 정리&quot;);
        log(&quot;작업 종료&quot;);
    }
}</code></pre>
<ul>
<li><code>interrupt()</code>를 사용하면 <code>sleep()</code>이 실행 중일 때 <strong>즉시 InterruptedException이 발생</strong>하여 종료된다.</li>
<li>하지만 <strong>인터럽트 상태는 자동으로 초기화(false)</strong> 된다.</li>
</ul>
<hr>
<h3 id="3-isinterrupted를-사용한-방법-threadstopmainv3">3. <code>isInterrupted()</code>를 사용한 방법 (ThreadStopMainV3)</h3>
<pre><code class="language-java">static class MyTask implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            log(&quot;작업중&quot;);
        }
        try {
            log(&quot;자원 정리 시도&quot;);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log(&quot;자원 정리 실패 - 인터럽트 발생&quot;);
        }
        log(&quot;작업 종료&quot;);
    }
}</code></pre>
<ul>
<li><code>isInterrupted()</code>는 <strong>단순히 인터럽트 상태를 확인하는 역할</strong>을 한다. (초기화 X)</li>
<li>하지만 <code>sleep()</code> 호출 시 다시 <code>InterruptedException</code>이 발생하면, <strong>한번 더 예외 처리가 필요</strong>하다.</li>
</ul>
<hr>
<h3 id="4-threadinterrupted를-사용한-방법-threadstopmainv4">4. <code>Thread.interrupted()</code>를 사용한 방법 (ThreadStopMainV4)</h3>
<pre><code class="language-java">static class MyTask implements Runnable {
    @Override
    public void run() {
        while (!Thread.interrupted()) {
            log(&quot;작업중&quot;);
        }
        try {
            log(&quot;자원 정리 시도&quot;);
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log(&quot;자원 정리 실패 - 인터럽트 발생&quot;);
        }
        log(&quot;작업 종료&quot;);
    }
}</code></pre>
<ul>
<li><code>Thread.interrupted()</code>는 <strong>현재 스레드의 인터럽트 상태를 확인한 후 초기화(false)</strong> 해준다.</li>
<li>따라서 인터럽트 상태가 계속 유지되지 않고 한 번만 감지되어 정상적으로 종료된다.</li>
</ul>
<hr>
<h2 id="인터럽트-상태-관리의-중요성">인터럽트 상태 관리의 중요성</h2>
<ol>
<li><code>interrupt()</code>가 호출된다고 즉시 <code>InterruptedException</code>이 발생하는 것이 아니다.</li>
<li><code>sleep()</code>, <code>wait()</code>, <code>join()</code> 같은 메서드를 호출해야만 <code>InterruptedException</code>이 발생한다.</li>
<li>인터럽트 상태가 <strong>true</strong>라면, <code>InterruptedException</code>이 발생하면서 다시 <strong>false</strong>로 초기화된다.</li>
<li><code>isInterrupted()</code>는 단순 확인만 하고 초기화하지 않는다.</li>
<li><code>Thread.interrupted()</code>는 인터럽트 상태를 확인한 후 <strong>false로 초기화</strong>하여 연속적인 예외 발생을 방지한다.</li>
</ol>
<h2 id="결론">결론</h2>
<p>Java에서 <strong>스레드를 안전하게 중단하려면</strong> <code>interrupt()</code>와 <code>Thread.interrupted()</code>를 적절히 활용해야 한다. 특히, 인터럽트 예외 발생 후 상태를 초기화하지 않으면 스레드가 계속 인터럽트 상태를 유지하여 예상치 못한 동작을 유발할 수 있다. 따라서 <strong>올바른 방식으로 인터럽트를 처리하여 리소스를 안전하게 정리하는 것이 중요하다.</strong></p>
<p>이제 인터럽트를 제대로 이해하고, 올바르게 적용해 보자! 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[this란 무엇인가?]]></title>
            <link>https://velog.io/@code_name_js/this%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@code_name_js/this%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Wed, 12 Feb 2025 06:22:26 GMT</pubDate>
            <description><![CDATA[<h2 id="1-this의-개념">1. this의 개념</h2>
<p>프로그래밍을 하다 보면 <code>this</code>라는 키워드를 자주 보게 됩니다. 특히 객체지향 언어(Java, JavaScript 등)에서 <code>this</code>는 매우 중요한 역할을 합니다. 그렇다면 <code>this</code>란 정확히 무엇일까요?</p>
<p>간단히 말해서, <strong><code>this</code>는 호출된 인스턴스 메서드가 소속된 객체를 가리키는 참조 값</strong>입니다. 즉, 어떤 객체에서 메서드를 호출했는지 기억하는 역할을 합니다.</p>
<h2 id="2-스택-프레임과-this">2. 스택 프레임과 this</h2>
<p>어떤 스레드가 메서드를 호출하면, 스레드는 해당 메서드를 실행하기 위해 <strong>스택 프레임(Stack Frame)</strong> 을 생성합니다.</p>
<p>스택 프레임에는 메서드 실행과 관련된 정보가 저장되는데, 여기에는 <strong>this도 포함</strong>됩니다.</p>
<p>이해하기 쉽게 정리하면:</p>
<ul>
<li>인스턴스의 메서드가 호출되면, <strong>어떤 인스턴스에서 호출되었는지 기억해야 합니다.</strong></li>
<li>이를 위해, 메서드가 실행될 때 해당 <strong>인스턴스의 참조값(this)을 스택 프레임에 저장</strong>합니다.</li>
<li>이후 메서드 내부에서 <code>this</code>를 사용하면, <strong>스택 프레임에 저장된 this 값이 불러와집니다.</strong></li>
</ul>
<h2 id="3-this가-중요한-이유">3. this가 중요한 이유</h2>
<p><code>this</code>가 있기 때문에 <strong>멀티 스레드 환경에서도 각 스레드는 자신이 실행 중인 객체를 구분</strong>할 수 있습니다.</p>
<p>예를 들어, 두 개의 스레드가 서로 다른 인스턴스를 사용한다고 가정해봅시다.</p>
<pre><code class="language-java">class Example {
    int value;

    void setValue(int value) {
        this.value = value; // this를 사용하여 현재 객체의 필드에 접근
    }
}

public class Main {
    public static void main(String[] args) {
        Example obj1 = new Example();
        Example obj2 = new Example();

        Thread thread1 = new Thread(() -&gt; {
            obj1.setValue(10);
        });

        Thread thread2 = new Thread(() -&gt; {
            obj2.setValue(20);
        });

        thread1.start();
        thread2.start();
    }
}</code></pre>
<p>위 코드에서 <code>this</code>가 없다면, <code>setValue</code> 메서드가 <strong>어떤 인스턴스의 필드를 변경해야 하는지</strong> 알 수 없습니다. 하지만 <code>this</code> 덕분에 <strong>각 스레드는 자신이 실행 중인 객체의 필드에 접근</strong>할 수 있습니다.</p>
<h2 id="4-this의-역할-요약">4. this의 역할 요약</h2>
<ol>
<li><code>this</code>는 <strong>현재 실행 중인 메서드가 속한 객체의 참조 값</strong>이다.</li>
<li>메서드가 실행될 때, <code>this</code>는 <strong>스택 프레임 내부에 저장</strong>된다.</li>
<li><code>this</code> 덕분에 <strong>스레드별로 각 인스턴스를 구별하여 사용할 수 있다.</strong></li>
<li><code>this</code>는 필드에 접근할 때 <strong>자동으로 사용</strong>되므로 생략해도 된다.</li>
</ol>
<h2 id="5-결론">5. 결론</h2>
<p><code>this</code>는 객체 지향 프로그래밍에서 필수적인 개념으로, <strong>인스턴스 메서드가 어느 객체에서 실행되고 있는지 추적</strong>하는 역할을 합니다. 또한 멀티스레드 환경에서도 객체를 올바르게 구별하는 데 중요한 역할을 합니다.</p>
<p>이제 <code>this</code>의 개념을 명확하게 이해하고, 코드에서 적극적으로 활용해보세요!</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Java 스레드의 생명 주기와 상태]]></title>
            <link>https://velog.io/@code_name_js/Java-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%83%9D%EB%AA%85-%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@code_name_js/Java-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%83%9D%EB%AA%85-%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%83%81%ED%83%9C</guid>
            <pubDate>Wed, 12 Feb 2025 04:04:55 GMT</pubDate>
            <description><![CDATA[<p>##스레드의 생명 주기
스레드는 생성(Create)되고, 실행(Run)되며, 종료(Terminate)되는 생명 주기를 갖습니다. 자바에서 스레드는 다양한 상태를 거치며 실행되며, 이 과정에서 여러 상태 전이를 경험합니다.</p>
<hr>
<h2 id="스레드의-상태-thread-states">스레드의 상태 (Thread States)</h2>
<p>자바에서 스레드는 다음과 같은 주요 상태를 가집니다.</p>
<h3 id="1-new-새로운-상태">1. <strong>New (새로운 상태)</strong></h3>
<ul>
<li>스레드가 생성되었지만 아직 실행되지 않은 상태입니다.</li>
<li><code>Thread</code> 객체가 생성되었지만 <code>start()</code> 메서드가 호출되지 않은 경우.</li>
<li>예제:<pre><code class="language-java">Thread thread = new Thread(runnable);</code></pre>
</li>
</ul>
<h3 id="2-runnable-실행-가능-상태">2. <strong>Runnable (실행 가능 상태)</strong></h3>
<ul>
<li>스레드가 실행 준비가 완료된 상태입니다.</li>
<li><code>start()</code> 메서드가 호출되면 이 상태로 전이됩니다.</li>
<li>CPU가 할당되면 실행됩니다.</li>
<li>예제:<pre><code class="language-java">thread.start();</code></pre>
</li>
<li>참고: <strong>Runnable 상태에 있다고 해서 반드시 실행되는 것은 아닙니다!</strong> CPU 스케줄링에 의해 실행 여부가 결정됩니다.</li>
</ul>
<h3 id="3-blocked-차단-상태">3. <strong>Blocked (차단 상태)</strong></h3>
<ul>
<li>스레드가 동기화 락을 얻지 못해 기다리는 상태입니다.</li>
<li>예를 들어 <code>synchronized</code> 블록에 진입하려고 할 때 락을 얻지 못하면 <code>Blocked</code> 상태가 됩니다.</li>
</ul>
<h3 id="4-waiting-대기-상태">4. <strong>Waiting (대기 상태)</strong></h3>
<ul>
<li>스레드가 <strong>무기한으로</strong> 다른 스레드의 작업을 기다리는 상태입니다.</li>
<li><code>wait()</code>, <code>join()</code> 등의 메서드를 호출하면 이 상태가 됩니다.</li>
<li>예제:<pre><code class="language-java">synchronized(obj) {
    obj.wait(); // 무기한 대기 상태
}</code></pre>
</li>
</ul>
<h3 id="5-timed-waiting-시간-제한-대기-상태">5. <strong>Timed Waiting (시간 제한 대기 상태)</strong></h3>
<ul>
<li>스레드가 <strong>일정 시간 동안</strong> 다른 스레드의 작업을 기다리는 상태입니다.</li>
<li><code>sleep(long millis)</code>, <code>wait(long timeout)</code>, <code>join(long millis)</code> 메서드 호출 시 해당 상태가 됩니다.</li>
<li>예제:<pre><code class="language-java">Thread.sleep(1000); // 1초 동안 대기 (Timed Waiting)</code></pre>
</li>
</ul>
<h3 id="6-terminated-종료-상태">6. <strong>Terminated (종료 상태)</strong></h3>
<ul>
<li>스레드의 실행이 완료된 상태입니다.</li>
<li>정상 종료되거나 예외로 인해 종료될 수 있습니다.</li>
</ul>
<hr>
<h2 id="스레드-상태-전이-과정">스레드 상태 전이 과정</h2>
<p>자바 스레드는 아래와 같은 상태 전이를 겪습니다.</p>
<ol>
<li><p><strong>New → Runnable</strong></p>
<ul>
<li><code>start()</code> 호출 시 실행 가능 상태(Runnable)로 전이됨.</li>
</ul>
</li>
<li><p><strong>Runnable → Blocked / Waiting / Timed Waiting</strong></p>
<ul>
<li><code>Blocked</code>: 스레드가 동기화 락을 얻지 못할 때.</li>
<li><code>Waiting</code>: <code>wait()</code>, <code>join()</code> 등으로 무기한 대기할 때.</li>
<li><code>Timed Waiting</code>: <code>sleep(time)</code>, <code>wait(time)</code>, <code>join(time)</code> 등으로 일정 시간 대기할 때.</li>
</ul>
</li>
<li><p><strong>Blocked / Waiting / Timed Waiting → Runnable</strong></p>
<ul>
<li>락을 얻거나 대기 시간이 종료되면 다시 실행 가능 상태로 전이됨.</li>
</ul>
</li>
<li><p><strong>Runnable → Terminated</strong></p>
<ul>
<li><code>run()</code> 메서드 실행이 끝나면 종료 상태(Terminated)가 됨.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="마무리">마무리</h2>
<p>자바 스레드는 <strong>New → Runnable → Running → Blocked/Waiting/Timed Waiting → Runnable → Terminated</strong>의 흐름을 따르며 실행됩니다. 운영체제의 스케줄러가 CPU 시간을 관리하므로, <strong>Runnable 상태에 있다고 즉시 실행되는 것이 아니라 스케줄러의 결정에 따라 실행된다</strong>는 점을 기억해야 합니다.</p>
<p>이해를 돕기 위해 아래 그림을 참고하면 좋습니다.</p>
<pre><code class="language-plaintext">New → Runnable → Running → Terminated
                ↘
          Blocked / Waiting / Timed Waiting → Runnable</code></pre>
<p>스레드의 상태를 적절히 관리하면 성능을 최적화하고, 동시성 문제를 방지할 수 있습니다. 스레드를 사용할 때 상태 전이를 잘 이해하고 활용하세요!
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java에서 Thread 상속 vs Runnable 구현 - 어떤 것이 더 좋을까?]]></title>
            <link>https://velog.io/@code_name_js/Java%EC%97%90%EC%84%9C-Thread-%EC%83%81%EC%86%8D-vs-Runnable-%EA%B5%AC%ED%98%84-%EC%96%B4%EB%96%A4-%EA%B2%83%EC%9D%B4-%EB%8D%94-%EC%A2%8B%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@code_name_js/Java%EC%97%90%EC%84%9C-Thread-%EC%83%81%EC%86%8D-vs-Runnable-%EA%B5%AC%ED%98%84-%EC%96%B4%EB%96%A4-%EA%B2%83%EC%9D%B4-%EB%8D%94-%EC%A2%8B%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Wed, 12 Feb 2025 01:19:12 GMT</pubDate>
            <description><![CDATA[<p>Java에서 멀티스레드를 사용할 때, Thread 클래스를 직접 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다. 이 두 가지 방식은 각각 장점과 단점이 있는데, 일반적으로 Runnable을 구현하는 방식이 더 추천된다. 왜 그런지 한 번 알아보자!</p>
<ol>
<li>Thread 클래스를 상속하는 방식</li>
</ol>
<p>✅ 장점</p>
<p>구현이 간단함 → Thread 클래스를 상속받고 run() 메서드만 오버라이딩하면 됨.</p>
<p>❌ 단점</p>
<p>상속의 제한 → Java는 단일 상속만을 지원하므로, 이미 다른 클래스를 상속받고 있다면 Thread를 상속받을 수 없음.</p>
<p>유연성이 부족함 → 실행할 작업을 스레드와 분리해서 관리하기 어려움.</p>
<p>📌 예제 코드</p>
<p>class MyThread extends Thread {
    public void run() {
        System.out.println(&quot;Thread 실행 중...&quot;);
    }
}</p>
<p>public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}</p>
<ol start="2">
<li>Runnable 인터페이스를 구현하는 방식</li>
</ol>
<p>✅ 장점</p>
<p>상속이 자유로움 → 이미 다른 클래스를 상속받고 있어도 Runnable을 구현할 수 있음.</p>
<p>코드의 분리 → 스레드와 실행할 작업을 분리하여 관리할 수 있어 유지보수가 쉬움.</p>
<p>자원 관리가 용이함 → 여러 스레드가 같은 Runnable 객체를 공유할 수 있어 자원을 효율적으로 관리할 수 있음.</p>
<p>❌ 단점</p>
<p>코드가 약간 복잡해짐 → Runnable 객체를 생성하고 이를 Thread에 전달하는 과정이 필요함.</p>
<p>📌 예제 코드</p>
<p>class MyRunnable implements Runnable {
    public void run() {
        System.out.println(&quot;Runnable 실행 중...&quot;);
    }
}</p>
<p>public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}</p>
<ol start="3">
<li>결론</li>
</ol>
<p>✅ Runnable 인터페이스를 구현하는 방식을 사용하자!</p>
<p>Runnable 방식이 Thread 상속 방식보다 유연하고 유지보수하기 쉬운 코드 작성이 가능하다. 특히, 객체 지향적으로 코드를 관리하고 싶다면 Runnable을 적극적으로 활용하는 것이 좋다!</p>
<p>🚀 즉, Thread를 상속하는 방식은 단순하지만 제약이 많고, Runnable을 구현하는 방식이 더 확장성이 좋다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 멀티스레드: `Thread` 클래스의 `start()` 메서드 이해하기]]></title>
            <link>https://velog.io/@code_name_js/Java-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-Thread-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-start-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@code_name_js/Java-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C-Thread-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-start-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 12 Feb 2025 01:08:04 GMT</pubDate>
            <description><![CDATA[<h2 id="📝-개요">📝 개요</h2>
<p>Java에서 멀티스레드를 사용하려면 <code>Thread</code> 클래스를 상속받아 새로운 스레드를 생성할 수 있습니다. 이때 중요한 점은 <strong><code>start()</code> 메서드를 호출하면 새로운 스레드가 실행</strong>되며, <strong><code>run()</code> 메서드를 직접 호출하지 않는다는 점</strong>입니다.  </p>
<p>이번 글에서는 <code>Thread</code> 클래스를 상속받아 실행하는 간단한 예제를 통해 <code>start()</code>와 <code>run()</code>의 차이점을 이해해보겠습니다.</p>
<hr>
<h2 id="💻-코드-예제">💻 코드 예제</h2>
<h3 id="1️⃣-thread-클래스-상속하여-스레드-생성">1️⃣ <code>Thread</code> 클래스 상속하여 스레드 생성</h3>
<pre><code class="language-java">package thread.start;

public class HelloThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + &quot;: run()&quot;);
    }
}</code></pre>
<p>Thread 클래스를 상속받아 HelloThread 클래스를 만듭니다.
run() 메서드를 오버라이딩하여 실행할 코드를 작성합니다.
Thread.currentThread().getName()을 사용해 현재 실행 중인 스레드의 이름을 출력합니다.</p>
<pre><code>package thread.start;

public class HelloThreadMain {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + &quot;: main() start&quot;);

        HelloThread helloThread = new HelloThread();  // 새로운 스레드 객체 생성
        System.out.println(Thread.currentThread().getName() + &quot;: start() 호출 전&quot;);

        helloThread.start();  // 새로운 스레드를 시작

        System.out.println(Thread.currentThread().getName() + &quot;: start() 호출 후&quot;);
        System.out.println(Thread.currentThread().getName() + &quot;: main() end&quot;);
    }
}</code></pre><p>📌 실행 결과 예측
실제 실행 결과는 OS와 JVM 스케줄러에 따라 달라질 수 있지만, 일반적으로 아래와 같은 순서로 실행됩니다.</p>
<p>main: main() start
main: start() 호출 전
main: start() 호출 후
main: main() end
Thread-0: run()</p>
<p>여기서 중요한 점을 살펴보겠습니다.</p>
<ul>
<li>main 스레드가 실행되면서 HelloThread 객체를 생성합니다.</li>
<li>helloThread.start();를 호출하면 새로운 Thread-0 스레드가 시작됩니다.</li>
<li>start()를 호출한 직후 main 스레드는 바로 다음 코드로 진행하며, run()이 실행될 때까지 기다리지 않습니다.</li>
<li>Thread-0이 run() 메서드를 실행합니다.</li>
<li>스레드 간 실행 순서는 보장되지 않습니다. 따라서 main() 종료 전에 Thread-0이 실행될 수도 있고, 그 후에 실행될 수도 있습니다.</li>
</ul>
<p>🚀 start() vs run() 차이점
메서드    동작 방식</p>
<ul>
<li>start()    새로운 스레드를 생성하고, run() 메서드를 실행하도록 함.</li>
<li>run()    단순히 메서드를 실행할 뿐, 새로운 스레드를 생성하지 않음.
helloThread.run(); // ❌ 새로운 스레드가 생성되지 않음 (일반 메서드 호출)
helloThread.start(); // ✅ 새로운 스레드를 생성하여 실행</li>
</ul>
<p>🔥 핵심 정리
✅ main 스레드는 start()를 호출하여 새로운 스레드에게 실행을 지시할 뿐, 직접 run()을 실행하지 않는다.
✅ start()를 호출하면 새로운 스레드가 생성되고, run()이 실행된다.
✅ 스레드 실행 순서는 보장되지 않는다.</p>
<p>멀티스레드를 사용할 때는 스레드의 실행 흐름을 예측하기 어렵다는 점을 항상 염두에 두어야 합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 메모리 구조 완벽 정리: 메서드 영역 vs 스택 영역 vs 힙 영역]]></title>
            <link>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%81%EC%97%AD-vs-%EC%8A%A4%ED%83%9D-%EC%98%81%EC%97%AD-vs-%ED%9E%99-%EC%98%81%EC%97%AD</link>
            <guid>https://velog.io/@code_name_js/%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%98%81%EC%97%AD-vs-%EC%8A%A4%ED%83%9D-%EC%98%81%EC%97%AD-vs-%ED%9E%99-%EC%98%81%EC%97%AD</guid>
            <pubDate>Wed, 12 Feb 2025 00:54:48 GMT</pubDate>
            <description><![CDATA[<p>자바(Java) 프로그램이 실행될 때 JVM(Java Virtual Machine)은 프로그램이 사용할 메모리를 여러 영역으로 나누어 관리합니다.이 중에서도 <strong>메서드 영역(Method Area), 스택 영역(Stack), 힙 영역(Heap)</strong>은 가장 중요한 메모리 공간이며,각각의 역할과 차이점을 이해하는 것이 메모리 최적화 및 성능 튜닝에 매우 중요합니다.</p>
<p>이 글에서는 메서드 영역, 스택 영역, 힙 영역의 개념과 차이점을 쉽게 설명하고,필요한 경우 자바 예제 코드도 함께 제공하겠습니다. 🚀</p>
<hr>
<ol>
<li>메서드 영역 (Method Area)</li>
</ol>
<p>📌 프로그램 실행에 필요한 공통 데이터를 저장하는 공간</p>
<p>✅ 주요 특징</p>
<ul>
<li><p>JVM이 프로그램 실행을 위해 클래스 정보를 저장하는 공간</p>
</li>
<li><p>모든 스레드(Thread)에서 공유됨</p>
</li>
<li><p>프로그램이 종료될 때까지 유지됨</p>
</li>
</ul>
<p>✅ 저장되는 데이터</p>
<ul>
<li><p>클래스 정보: 클래스 실행 코드(바이트코드), 필드 정보, 메서드 코드</p>
</li>
<li><p>static 변수: 모든 인스턴스에서 공유하는 static 변수 저장</p>
</li>
<li><p>런타임 상수 풀: final static 값과 같은 상수 저장</p>
</li>
</ul>
<p>✅ 메서드 영역 관련 자바 예제</p>
<p>public class MethodAreaExample {
    // static 변수 (메서드 영역에 저장됨)
    static int staticVar = 100;</p>
<pre><code>// final static 변수 (런타임 상수 풀에 저장됨)
final static int CONSTANT = 200;

public static void main(String[] args) {
    System.out.println(&quot;Static Variable: &quot; + staticVar);
    System.out.println(&quot;Constant Value: &quot; + CONSTANT);
}</code></pre><p>}</p>
<hr>
<ol start="2">
<li>스택 영역 (Stack Area)</li>
</ol>
<p>📌 메서드 실행을 위한 메모리 공간 (LIFO 구조)</p>
<p>✅ 주요 특징</p>
<ul>
<li><p>각 스레드(Thread)마다 개별적으로 할당됨</p>
</li>
<li><p>메서드가 호출되면 <strong>스택 프레임(Stack Frame)</strong>이 생성되고, 실행이 끝나면 제거됨</p>
</li>
<li><p>GC(Garbage Collection)의 영향을 받지 않음 (자동 해제됨)</p>
</li>
<li><p>LIFO(Last In, First Out) 구조</p>
</li>
</ul>
<p>✅ 저장되는 데이터</p>
<ul>
<li><p>지역 변수(Local Variables): 메서드 내에서 선언된 변수</p>
</li>
<li><p>매개변수(Parameters): 메서드 호출 시 전달된 인자 값</p>
</li>
<li><p>연산 중간 값: 연산을 수행하는 과정에서 저장되는 임시 값</p>
</li>
<li><p>리턴 값(Return Value): 메서드 실행이 끝난 후 반환되는 값</p>
</li>
</ul>
<p>✅ 스택 영역 관련 자바 예제</p>
<p>public class StackExample {
    public static void main(String[] args) {
        int x = 10;   // 지역 변수 (스택에 저장됨)
        int y = 20;   // 지역 변수 (스택에 저장됨)
        int sum = add(x, y);  // add() 메서드 호출 → 새로운 스택 프레임 생성
        System.out.println(&quot;Sum: &quot; + sum);
    }</p>
<pre><code>public static int add(int a, int b) {
    int result = a + b;  // 새로운 스택 프레임에 변수 저장
    return result;  // 메서드 종료 시 스택 프레임 제거
}</code></pre><p>}</p>
<hr>
<ol start="3">
<li>힙 영역 (Heap Area)</li>
</ol>
<p>📌 객체와 배열이 저장되는 공간 (GC의 관리 대상)</p>
<p>✅ 주요 특징</p>
<ul>
<li><p>객체(Object)와 배열(Array)이 저장됨</p>
</li>
<li><p>GC(Garbage Collector)가 사용되지 않는 객체를 자동으로 회수</p>
</li>
<li><p>모든 스레드에서 공유됨</p>
</li>
<li><p>JVM이 시작될 때 생성되며, 애플리케이션이 종료될 때까지 유지됨</p>
</li>
</ul>
<p>✅ 저장되는 데이터</p>
<ul>
<li><p>객체(Object): new 키워드로 생성된 객체</p>
</li>
<li><p>배열(Array): new 키워드로 생성된 배열</p>
</li>
<li><p>인스턴스 변수(Instance Variables): 객체 내부의 멤버 변수</p>
</li>
</ul>
<p>✅ 힙 영역 관련 자바 예제</p>
<p>public class HeapExample {
    int instanceVar; // 인스턴스 변수 (힙 영역에 저장됨)</p>
<pre><code>public HeapExample(int value) {
    this.instanceVar = value;
}

public static void main(String[] args) {
    HeapExample obj1 = new HeapExample(10); // 힙 영역에 객체 생성
    HeapExample obj2 = new HeapExample(20); // 힙 영역에 또 다른 객체 생성
    System.out.println(&quot;Object 1: &quot; + obj1.instanceVar);
    System.out.println(&quot;Object 2: &quot; + obj2.instanceVar);
}</code></pre><p>}</p>
<hr>
<ol start="4">
<li>메모리 영역 비교 정리</li>
</ol>
<table>
<thead>
<tr>
<th>구분</th>
<th>메서드 영역 (Method Area)</th>
<th>스택 영역 (Stack Area)</th>
<th>힙 영역 (Heap Area)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>저장 데이터</strong></td>
<td>클래스 정보, static 변수, 상수</td>
<td>지역 변수, 매개변수, 연산 중간 값</td>
<td>객체, 배열, 인스턴스 변수</td>
</tr>
<tr>
<td><strong>할당 주체</strong></td>
<td>JVM에 의해 자동 할당</td>
<td>메서드 실행 시 자동 할당</td>
<td><code>new</code> 키워드로 동적 할당</td>
</tr>
<tr>
<td><strong>관리 방식</strong></td>
<td>프로그램 종료 시까지 유지</td>
<td>메서드 실행 종료 시 제거</td>
<td>GC(Garbage Collector)가 관리</td>
</tr>
<tr>
<td><strong>공유 여부</strong></td>
<td>모든 스레드에서 공유</td>
<td>각 스레드마다 개별 스택 할당</td>
<td>모든 스레드에서 공유</td>
</tr>
<tr>
<td><strong>관련 오류</strong></td>
<td><code>OutOfMemoryError: Metaspace</code></td>
<td><code>StackOverflowError</code></td>
<td><code>OutOfMemoryError: Java heap space</code></td>
</tr>
</tbody></table>
<hr>
<ol start="5">
<li>결론</li>
</ol>
<p>자바의 메모리 구조를 이해하면 메모리 누수(Memory Leak) 방지, GC 최적화, 성능 개선 등에 도움이 됩니다.</p>
<p>💡 메모리 관리 최적화 방법</p>
<p>스택 오버플로우(StackOverflowError) 방지 → 재귀 호출 대신 반복문 사용</p>
<p>힙 메모리 부족(OutOfMemoryError) 방지 → 객체 참조 해제, 캐시 사용 고려</p>
<p>GC 최적화 → JVM 옵션(-Xms, -Xmx, -XX:+UseG1GC) 설정</p>
<p>이제 자바 프로그램에서 효율적인 메모리 관리를 할 수 있겠죠? 🚀</p>
]]></description>
        </item>
    </channel>
</rss>