<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-ssj</title>
        <link>https://velog.io/</link>
        <description>배우고 기록하며 성장하는 백엔드 개발자입니다!</description>
        <lastBuildDate>Thu, 23 Oct 2025 14:46:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-ssj</title>
            <url>https://velog.velcdn.com/images/dev_ssj/profile/8edd4c4f-a7b7-4b54-a57b-669d63f9012d/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-ssj. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev_ssj" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[CS]교착 상태]]></title>
            <link>https://velog.io/@dev_ssj/CS%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@dev_ssj/CS%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</guid>
            <pubDate>Thu, 23 Oct 2025 14:46:16 GMT</pubDate>
            <description><![CDATA[<h1 id="💡교착상태데드락-deadlock란">💡교착상태(데드락, Deadlock)란?</h1>
<blockquote>
<p><strong>둘 이상의 프로세스가 다른 프로세스가 점유하고 있는 자원을 서로 기다릴 때 무한 대기에 빠지는 상황</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/1019c006-270b-4a72-a17e-6b9831ba83d8/image.png" alt=""></p>
<hr>
<h2 id="✅자원-할당-그래프--교착-상태-상황-확인">✅자원 할당 그래프 : 교착 상태 상황 확인</h2>
<blockquote>
<p>교착 상태는 자원 할당 그래프를 통해 단순하게 표현할 수 있다.
자원할당 그래프는 <strong>어떤 프로세스가 어떤 자원을 사용하고 있고, 어떤 프로세스가 어떤 자원을 기다리고 있는지를 표현하는 그래프</strong>이다.</p>
</blockquote>
<h3 id="1-프로세스는-원으로-자원의-종류는-사각형으로-표현">1. 프로세스는 원으로, 자원의 종류는 사각형으로 표현</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/4cebef16-1467-456c-841c-4d86be9ccbd5/image.png" alt=""></p>
<h3 id="2-사용할-수-있는-자원의-개수는-자원-사각형-내의-점으로-표현">2. 사용할 수 있는 자원의 개수는 자원 사각형 내의 점으로 표현</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/0bc3a85a-59b0-42af-b5d6-c2f8e17e9641/image.png" alt=""></p>
<h3 id="3-프로세스가-자원을-할당-받아-사용중이라면-자원에서-프로세스를-향해-화살표를-표시">3. 프로세스가 자원을 할당 받아 사용중이라면 자원에서 프로세스를 향해 화살표를 표시</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/51a63c70-b25a-44d9-9216-44946b37821e/image.png" alt=""></p>
<ul>
<li>하드디스크의 자원 하나는 프로세스 A에 할당</li>
<li>CPU의 자원은 프로세스 B,C에 할당</li>
</ul>
<h3 id="4-프로세스가-어떤-자원을-기다리고-있다면-프로세스에서-자원으로-화살표를-표시">4. 프로세스가 어떤 자원을 기다리고 있다면 프로세스에서 자원으로 화살표를 표시</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/8a431df3-3219-4716-8a91-3cc9be9ac7a5/image.png" alt=""></p>
<ul>
<li>프로세스 D는 CPU의 할당을 기다리는 중</li>
</ul>
<blockquote>
<h4 id="📌그렇다면-어느-경우에-교착-상태가-발생할까"><strong>📌그렇다면 어느 경우에 교착 상태가 발생할까?</strong></h4>
</blockquote>
<h3 id="❗자원-할당-그래프가-원의-형태를-띠고-있을-때-교착상태가-발생한다">❗자원 할당 그래프가 원의 형태를 띠고 있을 때 교착상태가 발생한다</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a9a41c13-4604-400d-80aa-a7e73757e9b7/image.png" alt=""></p>
<hr>
<h2 id="✅교착-상태-발생-원인">✅교착 상태 발생 원인</h2>
<h3 id="1️⃣상호-배제mutual-exclusion">1️⃣상호 배제(mutual exclusion)</h3>
<ul>
<li>한 프로세스가 사용하는 자원을 다른 프로세스가 사용할 수 없는 상태</li>
</ul>
<h3 id="2️⃣점유와-대기hold-and-wait">2️⃣점유와 대기(hold and wait)</h3>
<ul>
<li>자원을 할당받은 상태에서 다른 자원을 할당받기를 기다리는 상태</li>
</ul>
<h3 id="3️⃣비선점nonpreemptive">3️⃣비선점(nonpreemptive)</h3>
<ul>
<li>어떤 프로세스도 다른 프로세스의 자원을 강제로 빼앗지 못하는 상태</li>
</ul>
<h3 id="4️⃣원형-대기circularwait">4️⃣원형 대기(circularwait)</h3>
<ul>
<li>프로세스들이 원의 형태로 자원을 대기하는 형태</li>
</ul>
<blockquote>
<h4 id="➡️-span-stylecolor-red위-네-가지-조건을-모두-만족할-때-교착-상태가-발생할-수-있다span">➡️ <span style="color: red">위 네 가지 조건을 모두 만족할 때, 교착 상태가 발생할 수 있다.</span></h4>
</blockquote>
<hr>
<h2 id="✅교착-상태-해결-방법">✅교착 상태 해결 방법</h2>
<h3 id="☑️교착-상태-예방">☑️교착 상태 예방</h3>
<ul>
<li><strong>애초에 교착 상태가 발생하지 않도록 하는 방식</strong></li>
<li>교착 상태 발생 조건(<code>상호 배제</code>, <code>점유와 대기</code>, <code>비선점</code>, <code>원형 대기</code>) 중 하나를 만족시키지 않는 방법으로 예방</li>
</ul>
<h4 id="1-상호-배제를-없애보자">1. 상호 배제를 없애보자</h4>
<ul>
<li>모든 자원을 프로세스들이 공유하도록 만들어야한다 → <strong>현실적으로 불가능</strong></li>
</ul>
<h4 id="2-점유와-대기를-없애보자">2. 점유와 대기를 없애보자</h4>
<ul>
<li>특정 프로세스에 자원을 모두 할당하거나, 아예 할당하지 않는 방식으로 배분 →** 자원의 활용률 낮아짐**</li>
</ul>
<h4 id="3-비선점을-없애보자">3. 비선점을 없애보자</h4>
<ul>
<li>선점하여 사용할 수 있는 일부 자원에 대해서는 효과적이나 <strong>모든 자원이 선점 가능한 것이 아니다.</strong></li>
<li>대표적으로 선점이 가능한 자원 : CPU</li>
</ul>
<h4 id="4-원형-대기-조건을-없애보자">4. 원형 대기 조건을 없애보자</h4>
<ul>
<li>자원에 번호를 붙이고 오름차순으로 할당하면 원형대기는 발생하지 않는다.</li>
<li>1번 포크 → 2번 포크 → 3번 포크 →...→5번 포크에서 다시 1번 포크는 잡지 못한다.</li>
<li>위 3가지 방식에 비해 현실적인 방식이지만 <strong>어떤 자원에 어떤 번호를 붙이느냐에 따라 자원 활용률이 달라지게 된다.</strong>
<img src="https://velog.velcdn.com/images/dev_ssj/post/e7637b9e-90ee-4fb4-921f-ba22c6f38897/image.png" alt=""></li>
</ul>
<blockquote>
<h4 id="span-stylecolor-red➡️교착-상태를-사전에-방지하는-예방-방식은-교착-상태가-발생하지-않음을-보장할-수는-있지만-여러-부작용이-따르게-된다span"><span style="color: red">➡️교착 상태를 사전에 방지하는 예방 방식은 교착 상태가 발생하지 않음을 보장할 수는 있지만, 여러 부작용이 따르게 된다.</span></h4>
</blockquote>
<hr>
<h3 id="☑️교착-상태-회피">☑️교착 상태 회피</h3>
<ul>
<li><strong>교착 상태가 발생하지 않을 정도로만 자원을 할당하는 방식</strong></li>
</ul>
<h4 id="안전-순서열">안전 순서열</h4>
<ul>
<li>교착 상태 없이 안전하게 모든 프로세스들에 자원을 할당할 수 있게 하는 순서</li>
</ul>
<h4 id="안전-상태">안전 상태</h4>
<ul>
<li>교착 상태없이 모든 프로세스가 자원을 할당받고 종료될 수 있는 상태</li>
<li>안전 순서열이 있는 상태</li>
</ul>
<h4 id="불안전-상태">불안전 상태</h4>
<ul>
<li>교착 상태가 발생할 수도 있는 상태</li>
<li>안전 순서열이 없는 상태</li>
</ul>
<blockquote>
<h4 id="➡️교착-상태-회피는-항시-안전-상태를-유지하며-자원을-할당하는-방식으로-대표적인-알고리즘에는-은행원-알고리즘이-있다">➡️교착 상태 회피는 항시 안전 상태를 유지하며 자원을 할당하는 방식으로, 대표적인 알고리즘에는 은행원 알고리즘이 있다.</h4>
</blockquote>
<h3 id="☑️교착-상태-검출-후-회복">☑️교착 상태 검출 후 회복</h3>
<ul>
<li><strong>교착 상태 발생을 인정하고 사후에 조치하는 방식</strong></li>
<li>교착 상태를 회복하는 방식에는 두 가지가 있다.</li>
</ul>
<h4 id="1-선점을-통한-회복">1. 선점을 통한 회복</h4>
<ul>
<li>교착 상태가 해결이 될 때까지 한 프로세스씩 자원을 몰아주는 방식</li>
</ul>
<h4 id="2-프로세스-강제-종료를-통한-회복">2. 프로세스 강제 종료를 통한 회복</h4>
<ul>
<li>교착 상태에 놓인 프로세스를 모두 강제 종료 하는 방식
→ 단점 : <strong>작업 내역을 잃을 위험성 존재</strong></li>
<li>교착 상태가 해결될 때까지 한 프로세스씩 강제 종료하는 방식
→ 단점 : 한 프로세스 종료 후 교촉 상태가 해결되었는지 확인하는 작업 반복 -&gt; <strong>오버헤드 발생</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS]프로세스 동기화]]></title>
            <link>https://velog.io/@dev_ssj/CS%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@dev_ssj/CS%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94</guid>
            <pubDate>Wed, 22 Oct 2025 14:18:34 GMT</pubDate>
            <description><![CDATA[<h1 id="💡프로세스-동기화란">💡프로세스 동기화란?</h1>
<blockquote>
<p><strong>여러 프로세스가 공유 자원의 일관성을 유지하는 것</strong></p>
</blockquote>
<ul>
<li>간단하게는 <strong>프로세스 수행 시기를 맞추는 것</strong>을 의미하며,</li>
<li>정확히는 프로세스 실행 순서를 제어하고 동시에 접근할 수 없는 자원에 하나의 프로세스만 접근하게 하는 것을 뜻한다.</li>
<li>프로세스는 이를 통해 <strong>데이터 일관성</strong>을 유지할 수 있다.</li>
</ul>
<p>프로세스들의 수행 시기를 맞추는 것은 크게 아래 두가지를 일컫는다</p>
<ul>
<li><strong>실행 순서 제어</strong> : 프로세스를 올바른 순서대로 실행하기</li>
<li><strong>상호 배제</strong> : 동시에 접근해서는 안되는 자원에 하나의 프로세스만 접근하게 하기</li>
</ul>
<hr>
<h2 id="✅실행-순서-제어를-위한-동기화">✅실행 순서 제어를 위한 동기화</h2>
<h3 id="reader-writer-problem">reader writer problem</h3>
<p>Writer라는 프로세스와 Reader라는 프로세스가 동시에 실행중이라고 가정해보자.</p>
<ul>
<li><strong>Writer 프로세스</strong> : Book.txt 파일에 값을 저장하는 프로세스</li>
<li><strong>Reader 프로세스</strong> : Book.txt 파일에 저장된 값을 읽어 들이는 프로세스</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a1b65a46-bf35-4213-91df-d30b55a3845a/image.png" alt=""></p>
<ul>
<li>이 두 프로세스는 아무 순서대로 실행되어서는 안되며, <strong>Writer프로세스의 실행이 끝나야 Reader프로세스의 실행이 가능</strong>하다.</li>
<li>즉, Writer프로세스가 Book.txt에 값을 저장하기도 전에 Reader 프로세스가 Book.txt를 읽어 들이는 것은 올바른 실행 순서가 아니며, Reader 프로세스는 <code>Book.txt</code>안에 값이 존재한다는 특정 조건이 만족되어야만 실행을 이어나갈 수 있다.</li>
</ul>
<hr>
<h2 id="✅상호-배제를-위한-동기화">✅상호 배제를 위한 동기화</h2>
<h3 id="bank-account-problem">bank account problem</h3>
<p>가령 계좌에 10만원이 저죽되어 있다고 가정해보자.</p>
<ul>
<li><p><strong>프로세스 A</strong> : 현재 저축된 금액에 2만원을 넣는 프로세스
<img src="https://velog.velcdn.com/images/dev_ssj/post/fd5a3b9f-debb-46a3-89f6-279926d0dda9/image.png" alt=""></p>
</li>
<li><p>** 프로세스 B** : 현재 저축된 금액에 5만원을 넣는 프로세스
<img src="https://velog.velcdn.com/images/dev_ssj/post/743c628e-4906-4921-8e3a-830126f478a9/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>프로세스 A와 B가 동시에 실행되었다고 가정할 경우, 당연히 우리는 17만원이 계좌에 남을것을 기대한다. 하지만 <strong>동기화가 제대로 이루어지지않은 경우 전혀 다른 결과</strong>가 나올 수 있다.</li>
<li>그 이유는 A와 B모두 잔액 이라는 데이터를 동시에 사용하는데, A가 끝나기도 전에 B가 잔액을 읽어버리고, 작업을 실행하였기 때문이다.</li>
<li>이렇게 <strong>동시에 접근해서는 안되는 자원에 동시에 접근하지 못하게 하는 것이 상호 배제를 위한 동기화</strong>이다.</li>
</ul>
<h3 id="생산자와-소비자-문제">생산자와 소비자 문제</h3>
<ul>
<li>생산자와 소비자는 <code>총합</code>이라는 데이터를 공유하고 있다.</li>
<li><strong>생산자</strong> : 버퍼에 물건을 넣은 후 물건의 총합에 해당하는 변수를 1증가 시킴</li>
<li><strong>소비자</strong> : 버퍼에 물건을 빼낸 후 물건의 총합에 해당하는 변수를 1감소 시킴</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/cee093bb-eccc-413f-b521-2015f3fbd627/image.png" alt=""></p>
<ul>
<li>총합을 10으로 가정하고, 위와 같은 상태에서 생산자를 100,000번, 소비자를 100,000번 동시에 실행하면 우리는 총합 변수가 계속 10일 것이라고 기대한다.</li>
<li>하지만 막상 동시에 실행해보면 10이 아닌 다른수가 되거나 실행 중 오류가 난다.</li>
<li>이는 <strong>생산자 프로세스와 소비자 프로세스가 제대로 동기화되지 않았기 때문에 발생한 문제</strong>로, <code>총합</code>이라는 데이터를 동시에 사용하지만 생산자와 소비자가 서로 작업이 끝나기도 전에 총합을 수정했기 때문이다.</li>
</ul>
<hr>
<h2 id="✅공유-자원과-임계-구역">✅공유 자원과 임계 구역</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/003cd882-8cb0-43b3-8682-fedfb2480c2d/image.png" alt=""></p>
<h3 id="1️⃣공유-자원shared-resource">1️⃣공유 자원(shared resource)</h3>
<ul>
<li>여러 프로세스 혹은 스레드가 공유하는 자원</li>
</ul>
<h3 id="2️⃣임계-구역critical-area">2️⃣임계 구역(critical area)</h3>
<ul>
<li>공유 자원에 접근하는 코드 중 동시에 실행하면 문제가 발생하는 코드 영역</li>
</ul>
<h3 id="3️⃣경쟁-조건race-condition">3️⃣경쟁 조건(race condition)</h3>
<ul>
<li>여러 프로세스가 동시 다발적으로 임계 구역의 코드를 실행하여 문제가 발생하는 상황</li>
</ul>
<hr>
<h2 id="✅임계-구역-문제-해결-조건">✅임계 구역 문제 해결 조건</h2>
<blockquote>
<p>위와 같은 임계구역 문제는 아래 세가지 원칙 하에 해결한다.
즉, <strong>상호 배제를 위한 동기화를 위해서는 아래 세가지 원칙이 반드시 지켜져야만 한다.</strong></p>
</blockquote>
<h3 id="1️⃣상호-배제mutual-exclusion">1️⃣상호 배제(mutual exclusion)</h3>
<ul>
<li>한 프로세스가 임계 구역에 진입했다면 다른 프로세스는 임계 구역에 들어올 수 없다.</li>
<li><strong>임계구역 내에는 한번의 하나의 프로세스만 있어야함.</strong><h3 id="2️⃣진행의-융통성progress-flexibility">2️⃣진행의 융통성(progress flexibility)</h3>
</li>
<li>임계구역에 어떤 프로세스도 진입하지 않았다면, 진입하고자 하는 프로세스는 들어갈 수 있어야 함.<h3 id="3️⃣유한-대기bounded-waiting">3️⃣유한 대기(bounded waiting)</h3>
</li>
<li>한 프로세스가 임계 구역에 진입하고 싶다면, 그 프로세스는 언젠가는 임계 구역에 들어올 수 있어야 함.</li>
<li>** 임계 구역에 들어오기 위해 무한정 대기해서는 안됨.**</li>
</ul>
<hr>
<h1 id="💡동기화-기법">💡동기화 기법</h1>
<h2 id="✅뮤텍스-락mutex-lock">✅뮤텍스 락(Mutex lock)</h2>
<blockquote>
<p><strong>락을 가진 단 한명만 접근 가능</strong></p>
</blockquote>
<ul>
<li>상호 배제를 위한 동기화 도구</li>
<li>임계 구역에 진입하는 프로세스는 <code>지금 임계 구역에 있음</code>을 알리기 위해 뮤텍스 락을 이용해 임계 구역에 자물쇠를 걸어둠</li>
<li>다른 프로세스는 임계 구역이 잠겨 있다면 기다리고, 잠겨있지 않다면 임계구역에 진입 가능<pre><code class="language-java">acquire();        // 자물쇠가 잠겨 있는지 확인, 잠겨 있지 않다면 잠그고 들어가기
// 임계구역          // 임계구역에서의 작업 진행
release();        // 자물쇠 반환</code></pre>
</li>
</ul>
<pre><code class="language-java">while (lock == true) /* 만약 임계 구역이 잠겨 있다면 */
; /* 임계 구역이 잠겨 있는지를 반복적으로 확인 */
</code></pre>
<ul>
<li>acquire() 에서는 임계 구역에 잠겨 있을 경우 프로세스가 반복적으로 lock을 확인한다.
→ <strong>바쁜 대기(busy wait)발생(CPU 사이클 낭비)</strong></li>
</ul>
<hr>
<h2 id="✅세마포어semaphore">✅세마포어(semaphore)</h2>
<blockquote>
<p><strong>접근 가능 개수 제한</strong></p>
</blockquote>
<ul>
<li><strong>공유 자원이 여러 개 있는 상황</strong>에서도 적용이 가능한 동기화 도구</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/fce5e850-518d-4570-a158-094e3928b0a3/image.png" alt=""></p>
<ul>
<li><p><strong>Semaphore(n)</strong></p>
<ul>
<li>사용 가능한 공유 자원의 개수를 나타내는 전역 변수</li>
</ul>
</li>
<li><p><strong>P(wait)</strong> </p>
<ul>
<li>임계구역에 들어가기 전에 호출하는 함수</li>
<li>자원을 요청하고 획득하는 코드 (잠금 수행)</li>
<li>RS가 0보다 크면(사용 가능한 자원 있다는 뜻) 1만큼 감소시키고 임계구역에 진입함</li>
<li>0보다 작으면(사용 가능한 자원 없다는 뜻) 0보다 커질 때까지 기다림(PCB 대기큐 삽입)</li>
<li>block()은 wake_up() 신호를 보낼 때까지 기다리는 함수</li>
</ul>
</li>
<li><p>*<em>V(signal) *</em></p>
<ul>
<li>임계구역에서 나올 때 호출하는 함수</li>
<li>자원을 반환하고 해제하는 코드 (잠금 해제)</li>
<li>RS 값을 1 증가 시키고 세마포어에서 기다리는 프로세스에 임계구역에 진입해도 좋다는 wake_up() 신호 보냄</li>
</ul>
</li>
</ul>
<h3 id="📌세마포어의-바쁜-대기-해결-방법">📌세마포어의 바쁜 대기 해결 방법</h3>
<ul>
<li>사용할 수 있는 자원이 없을 경우 대기 상태로 만듦
(해당 프로세스의 PCB를 대기 큐에 삽입)</li>
<li>사용할 수 있는 자원이 생겼을 경우 대기 큐의 프로세스를 준비 상태로 만듦
(해당 프로세스의 PCB를 대기 큐에서 꺼내 준비 큐에 삽입)</li>
</ul>
<hr>
<h3 id="✅모니터monitor">✅모니터(monitor)</h3>
<ul>
<li>공유 자원을 내부적으로 숨기고 공유 자원에 접근하기 위한 인터페이스만 제공함으로써 자원을 보호하고 프로세스 간에 동기화하는 방법</li>
</ul>
<p>1️⃣ 임계구역에 접근하고자 하는 프로세스는 직접 P()나 V()를 사용하지 않고 모니터에 작업 요청을 함
2️⃣ 모니터는 요청받은 작업을 모니터 큐에 저장한 후 순서대로 처리하고 그 결과만 해당 프로세스에 알려줌</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] CPU 스케줄링]]></title>
            <link>https://velog.io/@dev_ssj/CSCPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</link>
            <guid>https://velog.io/@dev_ssj/CSCPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</guid>
            <pubDate>Mon, 20 Oct 2025 06:21:43 GMT</pubDate>
            <description><![CDATA[<h1 id="💡cpu-스케줄링이란">💡CPU 스케줄링이란?</h1>
<blockquote>
<p>CPU 스케줄링은 언<strong>제 어떤 프로세스에 CPU를 할당할지 결정하는 작업</strong>을 의미한다. 이 알고리즘은 CPU 이용률은 높게, 주어진 시간에 많은 일을 하게, 준비 큐에 있는 프로세스는 적게, 응답시간은 짧게 설정하는 것을 목표로 한다.</p>
</blockquote>
<h2 id="✅프로세스-우선순위">✅프로세스 우선순위</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/ed595a31-10f3-4d43-bac5-2260485903e5/image.png" alt=""></p>
<ul>
<li><strong>입출력 작업이 많은 프로세스의 우선순위는 CPU 작업이 많은 프로세스의 우선순위보다 높다</strong></li>
<li>입출력 작업이 많은 프로세스를 <code>입출력 집중 프로세스</code>, CPU 작업이 많은 프로세스를 <code>CPU 집중 프로세스</code>라한다.</li>
<li>CPU 집중 프로세스는 CPU를 많이 사용해야 하는 프로세스이고, 입출력 집중 프로세스는 그렇지 않은 프로세스인데 두 프로세스 모두 동일한 빈도로 CPU를 사용하는 것은 비합리적이다. 
<img src="https://velog.velcdn.com/images/dev_ssj/post/9276a51e-8b52-43bd-b918-4bbd2b23bfba/image.png" alt=""></li>
</ul>
<ul>
<li>그래서 <strong>운영체제는 프로세스의 중요도에 맞게 프로세스가 CPU를 이용할 수 있도록 우선순위를 부여</strong>한다. </li>
<li>각 프로세스의 <strong>PCB에 우선순위를 명시</strong>하고, 그 우선순위를 기준으로 먼저 처리할 프로세스를 결정하게 된다.</li>
</ul>
<hr>
<h2 id="✅스케줄링-큐">✅스케줄링 큐</h2>
<ul>
<li>운영체제가 <strong>프로세스들에게 공정하고 합리적으로 CPU 자원을 배분</strong>하는 것</li>
<li>CPU를 사용할 다음 프로세스를 찾기 위해 모든 프로세스의 PCB를 확인하는 것은 비효율적이기 때문에 각 자원을 사용하고 싶어하는 프로세스들을 줄을 세워서 관리한다. 이 줄을 <strong>스케줄링 큐</strong>라고 부른다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/dcc5dd34-4533-4b55-a4ae-6e36a17093fe/image.png" alt=""></p>
<h3 id="☑️준비-큐ready-queue">☑️준비 큐(Ready queue)</h3>
<ul>
<li>CPU를 이용하고 싶은 프로세스들이 서는 줄을 의미</li>
</ul>
<h3 id="☑️대기-큐waiting-queue">☑️대기 큐(waiting queue)</h3>
<ul>
<li>입출력장치를 이용하기 위해 대기 상태에 접어든 프로세스들이 서는 줄을 의미</li>
</ul>
<hr>
<h2 id="✅선점형-비선점형-스케줄링">✅선점형, 비선점형 스케줄링</h2>
<h3 id="☑️선점형-스케줄링">☑️선점형 스케줄링</h3>
<ul>
<li><strong>어떤 프로세스가 CPU를 할당받아 실행 중이더라도 운영체제가 CPU를 강제로 빼앗을 수 있는 스케줄링 방식</strong></li>
<li>인터럽트 처리 같이 현재 실행 중인 작업을 중단하고 다른 프로세스가 CPU를 사용할 수 있다.</li>
<li>한 프로세스의 자원 독점을 막고 골고루 자원 배분이 가능하지만, 그만큼 문맥 교환 과정에서 오버헤드가 발생할 수 있다.</li>
<li><code>RR(Round Robin)</code>, <code>SRT</code>, <code>다단계 큐</code>, <code>다단계 피드백 큐</code></li>
</ul>
<h3 id="☑️비선점형-스케줄링">☑️비선점형 스케줄링</h3>
<ul>
<li><strong>어떤 프로세스가 CPU를 점유하면 다른 프로세스가 이를 빼앗을 수 없는 스케줄링 방식</strong></li>
<li>프로세스가 종료되거나 자발적으로 대기 상태에 들어가기 전까지는 계속 CPU를 사용할 수 있는 방식</li>
<li>문맥 교환으로 발생하는 오버헤드가 적지만, 긴 작업이 CPU를 오랫동안 점유하면 다른 프로세스의 응답성이 크게 저하될 수 있다.</li>
<li><code>FCFS(First-Come, First-Served)</code>, <code>비선점형 SJF</code>, <code>HRN</code></li>
</ul>
<hr>
<h1 id="💡cpu-스케줄링-알고리즘">💡CPU 스케줄링 알고리즘</h1>
<h2 id="✅fcfsfirst-come-first-serverd">✅FCFS(First Come First serverd)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/bba24cca-761f-4094-8daf-4c0074fad216/image.png" alt=""></p>
<ul>
<li><strong>준비 큐에 도착한 순서대로 CPU를 할당하는 비선점형 방식</strong>으로, 선입 선출 스케줄링이라고도 한다. </li>
<li>이 스케줄링 알고리즘은 처리 시간이 긴 프로세스가 CPU를 차지하면 다른 프로세스들은 하염없이 기다리느라 시스템의 효율성이 떨어지는데, 이를 <strong>호위효과(Convoy effect)</strong>라고 한다.</li>
</ul>
<br>

<h2 id="✅sjfshortest-job-first">✅SJF(Shortest Job First)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/93ea8a11-2966-4bc8-8973-10a1344baa36/image.png" alt=""></p>
<ul>
<li>준비 큐에 있는 프로세스 중에서 <strong>실행 시간이 가장 짧은 작업부터 CPU를 할당하는 비선점형 방식</strong>으로, 최단 작업 우선 스케줄링이라고도 한다.</li>
<li>작은 작업을 먼저 실행하기 때문에 시스템의 효율성은 좋아지지만, 어떤 프로세스는 CPU 할당이 계속 연기될 수 있다 -&gt; <strong>기아 상태(Starvation)</strong></li>
</ul>
<h3 id="📌에이징aging기법">📌에이징(Aging)기법</h3>
<ul>
<li><strong>기아 상태를 해결하기 위한 대표적인 기법</strong></li>
<li>오랫동안 대기한 프로세스의 우선 순위를 나이처럼 점차 높이는 방식</li>
<li>프로세스가 시간이 지남에 따라 노화되어 결국 CPU를 할당받을 기회를 보장함으로써 Starvation을 해소</li>
</ul>
<br>

<h2 id="✅hrnhighest-response-ratio-next">✅HRN(Highest Response Ratio Next)</h2>
<ul>
<li>SJF 스케줄링에서 발생할 수 있는 <strong>기아 상태를 해결하기 위해 만들어진 비선점형 알고리즘</strong>으로, 최고 응답률 우선 스케줄링이라고도 한다.</li>
<li>SJF의 우선순위 판단 기준이 프로세스 실행 시간이었다면, HRN은 <strong>대기시간과 CPU의 사용시간(실행시간)을 고려</strong>하여 스케줄링 하는 방식이다.
<img src="https://velog.velcdn.com/images/dev_ssj/post/956eaa4d-d9d2-4c32-8949-b94befbfa528/image.png" alt=""></li>
</ul>
<br>

<h2 id="✅rrround-robin">✅RR(Round Robin)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/0f13ad84-2aee-4926-aad4-9668a631e790/image.png" alt=""></p>
<ul>
<li><strong>FCFS 스케줄링에 타임슬라이스라는 개념이 더해진 선점형 스케줄링 방식</strong></li>
<li><code>타임 슬라이스</code> : 각 프로세스가 CPU를 사용할 수 있는 정해진 시간을 의미</li>
<li>타임 슬라이스가 지나치게 크면 FCFS와 다를 바 없어 호위효과가 생길 여지가 있고, 타임 슬라이스가 지나치게 작으면 문맥 교환에 발생하는 비용이 커져 평균 대기시간이 더 크게 나올 수 있다.</li>
</ul>
<br>

<h2 id="✅srtshortest-remainig-time">✅SRT(Shortest Remainig Time)</h2>
<ul>
<li>** SJF 스케줄링과 라운드 로빈 스케줄링을 혼합한 방식**으로 최소 잔류 시간 우선 스케줄링이라고도 한다.</li>
<li><strong>SJF 스케줄링의 선점형 버전</strong></li>
<li>프로세스들은 정해진 타임슬라이스만큼 CPU를 사용하되, CPU를 사용할 다음 프로세스로는 남아있는 작업시간이 가장 적은 프로세스가 선택된다.</li>
<li>SJF 스케줄링과 마찬가지로 운영체제가 프로세스 종료 시간을 예측하기 어렵고 기아 상태가 일어나기 때문에 잘 사용하지 않는다.</li>
</ul>
<br> 

<h2 id="✅다단계-큐-스케줄링multi-level-queue">✅다단계 큐 스케줄링(Multi-Level Queue)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/401d1318-f46e-4d3c-a420-b36db7c88d4d/image.png" alt=""></p>
<ul>
<li><strong>우선순위에 따라 준비 큐를 여러개 사용하는 방식</strong></li>
<li>프로세스는 운영체제로부터 부여받은 우선순위에 따라 해당 우선순위의 큐에 삽입된다.</li>
<li>우선순위 큐는 라운드 로빈 방식으로 운영되므로 프로세스가 큐에 삽입되는 것만으로도 우선순위 큐에서의 PCB 우선순위가 결정된다.</li>
<li>우선순위는 <strong>고정 우선순위</strong>를 사용하며, 상단 우선순위 큐에 있는 모든 프로세스의 작업이 끝나야 다음 우선순위 큐의 작업이 시작된다.</li>
<li>우선순위가 높은 프로세스때문에 우선순위가 낮은 프로세스의 작업이 연기되는데, 이러한 문제를 해결하기 위해 다단계 피드백 큐 스케줄링이 등장한다.</li>
</ul>
<br>

<h2 id="✅다단계-피드백-큐-스케줄링multi-level-feedback-queue">✅다단계 피드백 큐 스케줄링(Multi-Level Feedback Queue)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/36e7b6f7-3c7a-4782-b549-4d71ffb7be65/image.png" alt=""></p>
<ul>
<li><strong>다단계 큐 스케줄링 + 에이징 기법이 적용된 스케줄링 기법</strong></li>
<li>다단계 큐 스케줄링과 다른점은** 프로세스들이 큐 사이를 이동**할 수 있다는 점이다.</li>
<li>새로 준비 상태가 된 프로세스가 있다면 우선 순위가 가장 높은 우선순위 큐에 삽입되고, 일정 시간(타임 슬라이스)동안 실행된다.</li>
<li>만약 프로세스가 해당 큐에서 실행이 끝나지 않는다면 다음 우선순위 큐에 삽입되어 실행됨을 반복한다. -&gt; <strong>CPU를 오래 사용해야 하는 프로세스는 점차 우선순위가 낮아짐.</strong></li>
<li>또한, 어떤 프로세스의 CPU 이용 시간이 길면 낮은 우선순위 큐로 이동시키고, 어떤 프로세스가 낮은 우선순위 큐에서 너무 오래 기다린다면 높은 우선순위 큐로 이동시킬 수 있는 알고리즘이다.</li>
<li>구현이 복잡하지만, 가장 일반적인 CPU 스케줄링 알고리즘으로 알려져있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] 프로세스와 스레드 ]]></title>
            <link>https://velog.io/@dev_ssj/CS-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-v08zthw7</link>
            <guid>https://velog.io/@dev_ssj/CS-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-v08zthw7</guid>
            <pubDate>Sat, 18 Oct 2025 14:17:08 GMT</pubDate>
            <description><![CDATA[<h1 id="💡프로세스와-스레드">💡프로세스와 스레드</h1>
<blockquote>
<p>프로세스와 스레드는 운영체제에서 프로그램이 실행될 때 사용되는 기본적인 단위이다.
웹 서버나 애플리케이션에서 동시에 여러 작업을 처리하려면 프로세스와 스레드에 대한 이해가 필요하다. 두 개념은 자원관리, 성능 최적화, 동시성 처리에 직접적인 영향을 주지만 관리와 자원사용 측면에서 차이점이 있다. 이 개념들을 이해하면 시스템에서 작업이 어떻게 실행되고 관리되는지 알 수 있다.</p>
</blockquote>
<hr>
<h1 id="💡프로세스process">💡프로세스(Process)</h1>
<h2 id="✅프로세스란">✅프로세스란?</h2>
<p><strong>실행 중인 프로그램</strong>을 의미하며, 프로그램이 실행되면 운영체제가 <strong>메모리, CPU시간, 자원 등을 할당</strong>하여 독립적으로 실행되는 단위이다. 각각의 프로세스는 <strong>고유의 메모리 공간</strong>(코드, 데이터, 힙, 스택)을 가지고 있다.</p>
<hr>
<h2 id="✅프로세스의-특징">✅프로세스의 특징</h2>
<ul>
<li><p><strong>독립적인 메모리 공간</strong> : 각 프로세스는 운영체제로부터 자신만의 고유한 메모리 공간(스택, 레지스터, 데이터 등)의 자원을 할당 받아 독립적으로 실행된다. 따라서 다른 프로세스와 메모리를 공유하지 않으며, 프로세스 간에는 서로의 데이터에 접근할 수 없다. 다른 프로세스와의 상호작용이 필요할 때는 프로세스 간 통신(IPC, Inter-Process Communication)을 사용해야한다. 프로세스 간 통신은 파이프, 소켓, 공유 메모리 등의 다양한 방법이 있다.</p>
</li>
<li><p><strong>자원관리</strong> : 프로세스는 운영체제로부터 실행에 필요한 시스템 자원(CPU, 메모리, 파일 디스크립터 등)을 할당받는다. 각 프로세스는 고유한 프로세스 ID(PID)를 통해 식별된다.</p>
</li>
<li><p><strong>무거움</strong> : 프로세스는 각각의 독립적인 자원을 가지므로 시스템 자원을 많이 사용하며 생성 및 종료에 시간이 오래걸리며, 문맥 교환 비용이 크다.</p>
</li>
</ul>
<hr>
<h2 id="✅프로세스의-자원-구조">✅프로세스의 자원 구조</h2>
<blockquote>
<p>하나의 프로세스는 <strong>독립적인 메모리 공간</strong>을 가지며, 크게 4가지 메모리 영역으로 나누어져 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/b9fb1f45-42e3-496e-a60a-600ad5885e50/image.png" alt=""></p>
<ul>
<li><strong>코드(Code)</strong> : 프로그램의 실행 코드(기계어)</li>
<li><strong>데이터(Data)</strong> : 전역 변수, 정적 변수 등</li>
<li><strong>힙(Heap)</strong> : 런타임 중 동적으로 생성되는 객체(예시:new 연산자)</li>
<li><strong>스택(Stack)</strong> : 함수 호출, 지역 변수, 매개 변수 등</li>
</ul>
<p>또한, 프로세스는 위 4가지 메모리 영역 외에도 <strong>여러 자원을 독립적으로 소유</strong>한다.</p>
<ul>
<li><strong>파일 디스크립터</strong> : 운영체제가 파일, 소켓, 파이프 등 I/O 자원을 식별하기 위해 부여하는 번호</li>
<li><strong>프로세스ID(PID)</strong> : 운영체제가 각 프로세스를 고유하게 식별하기 위해 부여하는 고유한 번호</li>
<li><strong>PCB(프로세스 제어 블록)</strong> : 운영체제가 프로세스의 상태, 메모리 정보, CPU 레지스터 등을 저장해 프로세스를 관리하는 데 사용하는 데이터 구조로, 커널 영역에 생성된다.</li>
</ul>
<blockquote>
<p><code>코드 영역</code>과 <code>데이터 영역</code>은 그 크기가 변하지 않아 <strong>정적 할당 영역</strong>이라고 부르며, <code>힙 영역</code>과 <code>스택 영역</code>은 프로세스 실행 과정에서 그 크기가 변할 수 있는 영역이라 <strong>동적 할당 영역</strong>이라고 부른다.</p>
</blockquote>
<hr>
<h2 id="✅프로세스의-예시">✅프로세스의 예시</h2>
<ul>
<li>크롬을 두 번 실행하면 각각의 창은 독립적인 프로세스</li>
<li>IDE, 메모장, 그림판 등은 각각 별도의 프로세스로 동작함</li>
</ul>
<hr>
<h2 id="✅프로세스의-상태">✅프로세스의 상태</h2>
<blockquote>
<p>여러개의 프로세스들은 빠르게 번갈아 가면서 실행되는데, 그 과중에서 하나의 프로세스는 여러 상태를 거치며 실행된다. 그리고 운영 체제는 프로세스의 상태를 PCB를 통해 인식하고 관리한다. 프로세스가 가질 수 있는 대표적인 상태는 5가지로 나눌 수 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/0f34e934-6eb1-4912-adad-7eb052483e2b/image.png" alt=""></p>
<h3 id="1-생성-상태new">1. 생성 상태(new)</h3>
<ul>
<li><strong>프로세스를 생성중인 상태</strong></li>
<li>이제 막 메모리에 적재되어 <strong>PCB를 할당받은 상태</strong></li>
<li>생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 준비상태가 되어 CPU의 할당을 기다린다.</li>
</ul>
<h3 id="2-준비-상태ready">2. 준비 상태(ready)</h3>
<ul>
<li>당장이라도 CPU를 할당받아 실행할 수 있지만, <strong>자신의 차례를 기다리고 있는 상태</strong></li>
<li>차례가 되면 CPU를 할당받아 실행 상태가 된다.</li>
<li>준비 상태인 프로세스가 실행 상태로 전환되는 것을 <strong>디스패치(dispatch)</strong>라고 한다.</li>
</ul>
<h3 id="3-실행-상태running">3. 실행 상태(running)</h3>
<ul>
<li><strong>CPU를 할당받아 실행중인 상태</strong></li>
<li>실행 상태인 프로세스는 할당된 일정 시간 동안만 CPU를 사용할 수 있으며, 할당된 시간을 모두 사용하면 다시 준비상태로 전환된다.</li>
<li>실행 도중 입출력장치를 사용하여 입출력 장치의 작업이 끝날 때까지 기다려야 한다면 대기 상태가 된다.</li>
</ul>
<h3 id="4-대기-상태waitingblocked">4. 대기 상태(waiting/blocked)</h3>
<ul>
<li>프로세스는 실행 도중 입출력장치를 사용하는 경우가 있는데, 입출력 작업은 CPU에 비해 처리 속도가 느리다.</li>
<li>입출력 작업을 요청한 프로세스는 입출력장치가 입출력을 끝날때까지 기다려야 한다.</li>
<li>이렇게 <strong>입출력 작업을 기다리는 상태</strong>를 대기상태라고 한다.</li>
<li>입출력 작업이 완료되면 해당 프로세스는 다시 준비 상태로 CPU 할당을 기다린다.</li>
</ul>
<h3 id="5-종료-상태terminated">5. 종료 상태(terminated)</h3>
<ul>
<li><strong>프로세스가 종료된 상태</strong></li>
<li>프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리한다.</li>
</ul>
<hr>
<h1 id="💡스레드thread">💡스레드(Thread)</h1>
<h2 id="✅프로세스의-한계">✅프로세스의 한계</h2>
<blockquote>
<p>과거에는 프로그램을 실행할 때 하나의 프로세스만을 사용했었다. 하지만 기술이 발전됨에 따라 프로그램이 복잡해지고 다채로워지면서 프로세스 작업 하나만을 사용해서 프로그램을 실행하기에는 한계가 있었고, 멀티 작업을 위해 여러 개의 프로세스를 만들게 되면 그만큼 메모리를 차지하고 CPU에서 할당받는 자원이 중복되게 될 것이다. 스레드는 이러한 프로세스 특성의 한계를 해결하기 위해 탄생하였다.</p>
</blockquote>
<hr>
<h2 id="✅스레드란">✅스레드란?</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/1557a993-e1c0-4b38-b5df-0e31e132386a/image.png" alt="">
스레드란 <strong>하나의 프로세스 내에서 동시에 진행되는 작업 갈래, 흐름의 단위</strong>를 말한다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 이 스레드들은 <strong>프로세스의 자원을 공유하면서 동시에 실행</strong>될 수 있다.</p>
<hr>
<h2 id="✅스레드의-특징">✅스레드의 특징</h2>
<ul>
<li><strong>메모리 공유</strong> : 스레드는 같은 프로세스 내의 메모리(코드, 데이터, 파일 등)를 공유한다. 즉, 프로세스 내에서 여러 스레드가 같은 데이터에 접근하고 수정할 수 있다. 이로 인해 스레드 간의 통신이 빠르고 효율적이지만, 동기화 문제가 발생할 수 있다.</li>
<li><strong>빠르고 가벼움</strong> : 스레드는 독립적인 자원을 갖지 않기 때문에 프로세스에 비해 생성과 종료가 빠르고 자원을 적게 사용하며 문맥교환이 빠르다. 하지만 메모리를 공유하기 때문에 동시성 문제가 발생할 수 있다.</li>
<li><strong>독립적인 실행 흐름</strong> : 스레드는 각자 독립적인 실행 흐름(스틱)을 가지며, 동시에 여러 작업을 수행할 수 있다. 이를 통해 CPU 활용도를 높일 수 있다.</li>
</ul>
<h3 id="📌context-switching문맥교환">📌<strong>Context Switching(문맥교환)</strong></h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/5ae0daee-06e8-486b-904b-dc859a8b35bd/image.png" alt="">
CPU가 작업A를 실행하다가 시간이 다 되어 작업B로 CPU 사용을 양보한다고 가정해보도록 하자.
이 상황에서 바로 직전까지 실행된 작업 A는 프로그램 카운터와 각종 레지스터 값, 메모리 정보 등 지금까지의 정보를 <strong>백업</strong>해야 한다. 그래야 다음 작업 차례에서 이전까지 실행했던 내용에 이어 다시 실행을 재개할 수 있으니까! 이러한 <strong>중간 정보를 문맥(Context)</strong>라고 한다.
문맥은 PCB에 저장되며, <strong>실행 순서가 넘어갈 때마다 실행중인 작업의 문맥을 PCB에 백업하고, 새로운 작업을 실행하기 위해 문맥을 PCB에 복구하여 새로운 작업을 실행하는 것을 문맥 교환(Context Switching)</strong>이라고 한다.
문맥 교환 시 자원과 시간이 소모되며, 문맥 교환을 너무 자주 하게 되면 오버헤드가 발생할 수 있다.
스레드 간 전환은 가볍지만, 프로세스 간 전환은 무겁고 비용이 크다.</p>
<hr>
<h2 id="✅스레드의-자원-구조">✅스레드의 자원 구조</h2>
<blockquote>
<p>스레드는 프로세스 내부의 실행 단위이기 때문에, <strong>같은 프로세스 안의 여러 스레드들은 일부 자원을 공유하고, 일부는 개별로 소유</strong>한다. 
스레드는 프로세스 자원을 공유한 채 실행에 필요한 최소한의 정보만으로 실행된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/30fd01a6-609e-4357-bcb5-f37c54a7b257/image.png" alt=""></p>
<p><strong>📍스레드 간 공유 자원</strong></p>
<ul>
<li>코드 영역</li>
<li>힙 영역</li>
<li>전역/정적 변수</li>
</ul>
<p><strong>📍스레드 개별 소유 자원</strong></p>
<ul>
<li><strong>스택 영역(지역변수, 호출기록)</strong></li>
<li>레지스터, PC(프로그램 카운터)</li>
<li>스레드 ID, 상태</li>
</ul>
<hr>
<h2 id="✅스레드의-예시">✅스레드의 예시</h2>
<ul>
<li>크롬 한 프로세스 내에서 각 탭은 별도의 스레드로 동작</li>
<li>웹서버가 여러 요청을 처리할 때 각각의 요청을 별도의 스레드로 처리</li>
</ul>
<hr>
<h2 id="☑️프로세스와-스레드의-차이">☑️프로세스와 스레드의 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>프로세스</th>
<th>스레드</th>
</tr>
</thead>
<tbody><tr>
<td>기본</td>
<td>독립적인 실행 단위</td>
<td>프로세스 내에서 실행되는 작업 단위</td>
</tr>
<tr>
<td>독립성</td>
<td>각각 독립적</td>
<td>스택만 독립적이고 그 외에는 공유</td>
</tr>
<tr>
<td>자원 할당</td>
<td>CPU, 메모리, 파일 디스크립터 등 자원을 할당 받음</td>
<td>프로세스의 자원을 공유함</td>
</tr>
<tr>
<td>통신 방법</td>
<td>프로세스간 통신(IPC) 필요 -&gt; 비효율적, 복잡</td>
<td>공유 메모리 (효율적, 빠름)</td>
</tr>
<tr>
<td>안정성</td>
<td>하나가 죽어도 다른 프로세스 영향 없음</td>
<td>하나의 스레드 오류 → 전체 프로세스 영향 가능</td>
</tr>
<tr>
<td>생성 및 종료</td>
<td>생성과 종료에 많은 자원과 시간 필요</td>
<td>생성과 종료가 비교적 빠르고 가벼움</td>
</tr>
<tr>
<td>예시</td>
<td>웹 브라우저, 텍스트 편집기 등 독립적인 프로그램</td>
<td>웹 브라우저에서 여러 탭을 처리하는 스레드</td>
</tr>
</tbody></table>
<hr>
<h1 id="💡멀티-프로세스">💡멀티 프로세스</h1>
<h2 id="✅멀티-프로세스란">✅멀티 프로세스란</h2>
<blockquote>
<p>멀티 프로세스란 <strong>하나의 작업을 여러개의 프로세스로 나누어 동시에 실행</strong>하는 방식을 말한다.</p>
</blockquote>
<hr>
<h2 id="✅멀티-프로세스의-특징">✅멀티 프로세스의 특징</h2>
<h3 id="⭕멀티-프로세스의-장점">⭕멀티 프로세스의 장점</h3>
<ul>
<li>메모리 침범 문제를 OS차원에서 해결가능</li>
<li>여러 자식 프로세스 중 하나에 문제가 발생하여도 그 프로세스만 타격 -&gt; <strong>영향이 확산되지 않음</strong></li>
</ul>
<h3 id="❗멀티-프로세스의-단점">❗멀티 프로세스의 단점</h3>
<ul>
<li>Context Switching 과정에서 캐쉬 메모리 초기화 등 무거운 작업이 진행되고 많은 시간이 소모되는 등의 오버헤드가 발생하게 됨</li>
<li>프로세스는 각각의 독립된 메모리 영역을 할당받기 때문에 프로세스 사이에서 공유하는 메모리가 없어 Context Switching이 발생하면 캐쉬에 있는 모든 데이터들을 모두 리셋하고 다시 캐쉬 정보를 불러와야 함</li>
<li><strong>프로세스 간의 복잡한 통신 (IPC)</strong> 가 필요함</li>
</ul>
<hr>
<h1 id="💡멀티스레드">💡멀티스레드</h1>
<h2 id="✅멀티-스레드란">✅멀티 스레드란</h2>
<blockquote>
<p>멀티 스레드란 <strong>하나의 프로세스 안에서 여러 스레드가 동시에 실행되며 작업을 분담</strong>하는 방식이다.</p>
</blockquote>
<hr>
<h2 id="✅멀티-스레드의-특징">✅멀티 스레드의 특징</h2>
<h3 id="⭕멀티-스레드의-장점">⭕멀티 스레드의 장점</h3>
<ul>
<li>메모리 공간, 시스템 자원의 효율성 증가</li>
<li>Data, Heap 영역을 이용해 데이터를 주고 받으므로 스레드간 통신이 간단함</li>
<li>context switching시 비용이 적음(교환해야 할게 적으니까) -&gt; <strong>시스템 처리량 향상, 프로그램 응답 시간 단축됨</strong></li>
</ul>
<h3 id="❗멀티-스레드의-단점">❗멀티 스레드의 단점</h3>
<ul>
<li>서로 다른 스레드가 Stack을 제외한 메모리 공간을 공유하기 때문에 <strong>동기화 문제가 발생</strong>할 수 있음</li>
<li>하나의 스레드에 문제가 생기면 전체 프로세스가 영향을 받음</li>
<li>주의 깊은 설계가 필요하며 디버깅이 까다로움</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]DNA 비밀번호 - 12891]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80JavaDNA-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-12891</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80JavaDNA-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-12891</guid>
            <pubDate>Tue, 14 Oct 2025 20:21:27 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-ii-dna-비밀번호---12891">[Silver II] DNA 비밀번호 - 12891</h1>
<p><a href="https://www.acmicpc.net/problem/12891">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>평소에 문자열을 가지고 노는 것을 좋아하는 민호는 DNA 문자열을 알게 되었다. DNA 문자열은 모든 문자열에 등장하는 문자가 {‘A’, ‘C’, ‘G’, ‘T’} 인 문자열을 말한다. 예를 들어 “ACKA”는 DNA 문자열이 아니지만 “ACCA”는 DNA 문자열이다. 이런 신비한 문자열에 완전히 매료된 민호는 임의의 DNA 문자열을 만들고 만들어진 DNA 문자열의 부분문자열을 비밀번호로 사용하기로 마음먹었다.</p>

<p>하지만 민호는 이러한 방법에는 큰 문제가 있다는 것을 발견했다. 임의의 DNA 문자열의 부분문자열을 뽑았을 때 “AAAA”와 같이 보안에 취약한 비밀번호가 만들어 질 수 있기 때문이다. 그래서 민호는 부분문자열에서 등장하는 문자의 개수가 특정 개수 이상이여야 비밀번호로 사용할 수 있다는 규칙을 만들었다.</p>

<p>임의의 DNA문자열이 “AAACCTGCCAA” 이고 민호가 뽑을 부분문자열의 길이를 4라고 하자. 그리고 부분문자열에 ‘A’ 는 1개 이상, ‘C’는 1개 이상, ‘G’는 1개 이상, ‘T’는 0개 이상이 등장해야 비밀번호로 사용할 수 있다고 하자. 이때 “ACCT” 는 ‘G’ 가 1 개 이상 등장해야 한다는 조건을 만족하지 못해 비밀번호로 사용하지 못한다. 하지만 “GCCA” 은 모든 조건을 만족하기 때문에 비밀번호로 사용할 수 있다.</p>

<p>민호가 만든 임의의 DNA 문자열과 비밀번호로 사용할 부분분자열의 길이, 그리고 {‘A’, ‘C’, ‘G’, ‘T’} 가 각각 몇번 이상 등장해야 비밀번호로 사용할 수 있는지 순서대로 주어졌을 때 민호가 만들 수 있는 비밀번호의 종류의 수를 구하는 프로그램을 작성하자. 단 부분문자열이 등장하는 위치가 다르다면 부분문자열이 같다고 하더라도 다른 문자열로 취급한다.</p>

<h3 id="입력">입력</h3>
 <p>첫 번째 줄에 민호가 임의로 만든 DNA 문자열 길이 |S|와 비밀번호로 사용할 부분문자열의 길이 |P| 가 주어진다. (1 ≤ |P| ≤ |S| ≤ 1,000,000)</p>

<p>두번 째 줄에는 민호가 임의로 만든 DNA 문자열이 주어진다.</p>

<p>세번 째 줄에는 부분문자열에 포함되어야 할 {‘A’, ‘C’, ‘G’, ‘T’} 의 최소 개수가 공백을 구분으로 주어진다. 각각의 수는 |S| 보다 작거나 같은 음이 아닌 정수이며 총 합은 |S| 보다 작거나 같음이 보장된다.</p>

<h3 id="출력">출력</h3>
 <p>첫 번째 줄에 민호가 만들 수 있는 비밀번호의 종류의 수를 출력해라.</p>


<h3 id="예제">예제</h3>
<p> <img src="https://velog.velcdn.com/images/dev_ssj/post/8953f8ff-dacf-4c4f-bb03-5c28e5e84eb9/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">public class Main {
    static int checkArr[];
    static int myArr[];
    static int checkSecret;
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(bf.readLine());

        //DNA 문자열의 길이
        int S = Integer.parseInt(st.nextToken());
        //부분 문자열의 길이
        int P = Integer.parseInt(st.nextToken());
        //정답 값 저장받을 변수
        int Result = 0;
        //비밀번호 체크 배열(A가 몇개 필요한지, G가 몇개 필요한지 등 기준 저장 하는 배열. 예시에서는 2 0 1 1)
        checkArr = new int[4];
        //현재 상태 저장할 배열(현재 A가 몇개있고, G가 몇개 있고.. myArr의 값이 checkArr와 같으면 조건 충족되는것 -&gt;checkSecret++)
        myArr = new int[4];
        //4개중 현재 몇개가 비밀번호 요건에 충족되는지 카운트 하는 변수 -&gt; checkSecret이 4면 result ++
        checkSecret = 0;
        //문자열을 입력받을 배열
        char[] A = bf.readLine().toCharArray();

        st = new StringTokenizer(bf.readLine());
        //각각의 dna가 몇개 필요한지 입력 받는 for문(2 0 1 1)
        for (int i = 0; i &lt; 4; i++) {
            checkArr[i] = Integer.parseInt(st.nextToken());
            //checkArr[i]가 0이면 해당 DNA는 없어도됨 -&gt; 이미 조건이 충족되었으니 checkSecret++
            if (checkArr[i] == 0)
                checkSecret++;
        }

        //초기(첫번째) 부분 문자열 처리 부분
        for (int i = 0; i &lt; P; i++) {
            Add(A[i]);
        }

        //checkSecret이 4 -&gt; 조건 모두 충족하여 유효한 비밀번호임
        if (checkSecret == 4)
            Result++;

        // 슬라이딩 윈도우 처리 부분
        int left = 0;   //왼쪽 값
        int right = P;  //오른쪽 값
        while(right &lt; S){   //배열 인덱스는 0부터 시작이므로 right &lt; S까지
            Add(A[right++]);  
            Remove(A[left++]);  
            if (checkSecret == 4)
                Result++;
        }
        System.out.println(Result);
        bf.close();
    }

    //새로 들어운 문자를 처리해주는 메서드
    //각각의 문자열 중 같은게 있으면 myArr[]에 1을 더해주고, myArr[]과 checkArr[]가 같다면 checkSecret++
    private static void Add(char c) {
        switch (c) {
            case &#39;A&#39;:
                myArr[0]++;
                if (myArr[0] == checkArr[0])
                    checkSecret++;
                break;
            case &#39;C&#39;:
                myArr[1]++;
                if (myArr[1] == checkArr[1])
                    checkSecret++;
                break;
            case &#39;G&#39;:
                myArr[2]++;
                if (myArr[2] == checkArr[2])
                    checkSecret++;
                break;
            case &#39;T&#39;:
                myArr[3]++;
                if (myArr[3] == checkArr[3])
                    checkSecret++;
                break;
        }
    }

    //제거되는  문자를 처리해주는 메서드
    //각각의 문자열 중 같은게 있으면 myArr[]에 1을 빼주고, myArr[]과 checkArr[]가 같다면 checkSecret--
    private static void Remove(char c) {
        switch (c) {
            case &#39;A&#39;:
                if (myArr[0] == checkArr[0])
                    checkSecret--;
                myArr[0]--;
                break;
            case &#39;C&#39;:
                if (myArr[1] == checkArr[1])
                    checkSecret--;
                myArr[1]--;
                break;
            case &#39;G&#39;:
                if (myArr[2] == checkArr[2])
                    checkSecret--;
                myArr[2]--;
                break;
            case &#39;T&#39;:
                if (myArr[3] == checkArr[3])
                    checkSecret--;
                myArr[3]--;
                break;
        }
    }
}</code></pre>
<ul>
<li>이 문제는 크기를 유지한 상태로 한칸씩 이동하면서 조건에 맞는지 탐색하는 슬라이딩 윈도우를 이용하는 문제이다.</li>
<li>주어진 DNA 문자열을 대조하면서 조건에 맞는지 탐색해야한다.</li>
</ul>
<hr>
<h1 id="✅문제-핵심-짚어보기">✅문제 핵심 짚어보기</h1>
<h2 id="필요한-변수-설정">필요한 변수 설정</h2>
<p>이 문제는 변수가 많아서 헷갈리기 쉬운데, 변수를 먼저 짚고 넘어가보도록하자.
<img src="https://velog.velcdn.com/images/dev_ssj/post/84af6598-a394-42ff-bc63-e4757106768f/image.png" alt="">
첫째줄에는 입력받을 전체 DNA 문자열의 길이와 부분 문자열의 길이를 입력받고, 두번째 줄에는 DNA 문자열을 입력받는다. 마지막으로 세번쨰 문자열은 부분 문자열에 포함되어야 할 A,C,G,T의 최소 개수를 입력받는다.</p>
<ul>
<li>첫째줄 : 입력받을 전체 DNA 문자열의 길이(<strong>S</strong>), 부분 문자열의 길이(<strong>P</strong>)</li>
<li>둘째줄 : DNA 문자열(<strong>char[] A</strong>)</li>
<li>셋째줄 : 부분 문자열에 포함되어야 할 A,C,G,T의 최소 개수(<strong>int[] checkArr</strong>)</li>
<li>결과 값을 입력받을 변수(<strong>result</strong>)</li>
</ul>
<p>예제에 나온 걸로 파악했을때 변수는 이렇게 5가지이다.
하지만 문제풀이를 할때 변수가 더 필요한데 먼저, 현재 A가 몇개있고, G가 몇개 있고 등 현재 상태를 저장할 배열 <strong>int[] myArr</strong>가 필요하다.
또, myArr의 값과 checkArr의 값이 같으면 해당 문자는 조건이 충족되는 것이므로 그걸 체크할 변수 <strong>checkSecret</strong>도 필요하다. checkSecret이 4면 해당 비밀번호는 유효한 비밀번호 이므로 result를 1증가시켜주면 된다.</p>
<h2 id="☑️흐름-파악-하기">☑️흐름 파악 하기</h2>
<h4 id="1-우선-입력받은-전체-dna-문자열을-a배열에-저장하고-비밀번호-체크-배열인-checkarr에-각-문자의-최소-개수를-저장한다">1. 우선 입력받은 전체 DNA 문자열을 A배열에 저장하고, 비밀번호 체크 배열인 checkArr에 각 문자의 최소 개수를 저장한다.</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/ac25971d-9765-43a5-991e-91cff93b8be2/image.png" alt=""></p>
<h4 id="2-슬라이딩-윈도우에-포함된-문자열로-현재-상태-배열을-만들고-현재-상태-배열과-비밀-번호-체크-배열을-비교하여-유효한-비밀번호-인지-판단한다">2. 슬라이딩 윈도우에 포함된 문자열로 현재 상태 배열을 만들고, 현재 상태 배열과 비밀 번호 체크 배열을 비교하여 유효한 비밀번호 인지 판단한다.</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/2c91ecc0-2961-4c39-a29d-89fd375f864e/image.png" alt=""></p>
<ul>
<li>p크기 만큼 문자열을 잘라서 비교해봐야하므로 p크기만큼의 부분 문자열을 A 배열에서 가져온다.</li>
<li>그리고 비밀번호 체크 배열(checkArr)와 현재상태 배열(myArr)를 비교하여 조건에 부합하면 각 문자마다 checkSecret에 1을 더해준다.</li>
<li>예제에서는 A의 조건은 2개이상이지만 현재 상태는 1개 이므로 조건에 부합하지 않는다.</li>
</ul>
<h4 id="3-윈도우를-한칸씩-이동하며-현재-상태-배열을-업데이트한다-현재-상태-배열을-업데이트할-때는-빠지는-문자열왼쪽에서-한칸-추가되는-신규-문자열오른쪽에서-한칸만-확인하여-업데이트-하는-방식으로-진행한다">3. 윈도우를 한칸씩 이동하며 현재 상태 배열을 업데이트한다. 현재 상태 배열을 업데이트할 때는 빠지는 문자열(왼쪽에서 한칸), 추가되는 신규 문자열(오른쪽에서 한칸)만 확인하여 업데이트 하는 방식으로 진행한다.</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/be3113f4-6f97-4b65-ad0a-1bebc4cf8699/image.png" alt=""></p>
<ul>
<li>윈도우를 한칸씩 이동하게 되면 맨 왼쪽의 C가 빠지고, 맨 오른쪽의 G가 추가된다.</li>
<li>마찬가지로 비밀번호 체크 배열(checkArr)와 현재상태 배열(myArr)를 비교하여 조건에 부합하면 각 문자마다 checkSecret에 1을 더해준다.</li>
<li>위 예제도 유효한 비밀번호가 아니다.</li>
</ul>
<h4 id="4-a-배열의-끝에-도달할-때-까지-한칸씩-이동한다">4. A 배열의 끝에 도달할 때 까지 한칸씩 이동한다.</h4>
<hr>
<h2 id="☑️코드-순서대로-뜯어보기">☑️코드 순서대로 뜯어보기</h2>
<p>입력받을 값을 모두 입력받았다는 전제하에 코드를 뜯어보자.(A,C,G,T의 최소 개수 입력 제외)</p>
<h4 id="1-문자-갯수acgt-총-4만큼-for문을-돌려-checkarr에-저장">1. 문자 갯수(A,C,G,T 총 4)만큼 for문을 돌려 checkArr에 저장</h4>
<pre><code class="language-java">//각각의 dna가 몇개 필요한지 입력 받는 for문(2 0 1 1)
for (int i = 0; i &lt; 4; i++) {
    checkArr[i] = Integer.parseInt(st.nextToken());
    //checkArr[i]가 0이면 해당 DNA는 없어도됨 -&gt; 이미 조건이 충족되었으니 checkSecret++
    if (checkArr[i] == 0)
        checkSecret++;
}</code></pre>
<ul>
<li>마지막 줄에 입력받는 A,C,G,T의 최소 개수를 for문을 돌려서 <code>checkArr</code>에 저장한다.</li>
<li>순서대로 A,C,G,T이며, 첫번째 예시를 예로 들면 (2 0 1 1)</li>
<li>checkArr[0] = 2, checkArr[1] = 0, checkArr[2] = 1, checkArr[1] 이 저장된다.</li>
<li>만약 해당 값이 0이라면(checkArr[1] = 0) 해당 DNA는 문자열에 없어도 된다. 즉, 이미 이 문자는 조건이 충족된것이므로 checkSecret을 증가시켜준다.</li>
</ul>
<h4 id="2-초기첫번째부분-문자열을-처리한다">2. 초기(첫번째)부분 문자열을 처리한다.</h4>
<pre><code class="language-java">//초기(첫번째) 부분 문자열 처리 부분
for (int i = 0; i &lt; P; i++) {
    Add(A[i]);
}

//checkSecret이 4 -&gt; 조건 모두 충족하여 유효한 비밀번호임
if (checkSecret == 4) Result++;

//새로 들어운 문자를 처리해주는 메서드
//각각의 문자열 중 같은게 있으면 myArr[]에 1을 더해주고,
//myArr[]과 checkArr[]가 같다면 checkSecret++
private static void Add(char c) {
    switch (c) {
        case &#39;A&#39;:
            myArr[0]++;
            if (myArr[0] == checkArr[0])
                checkSecret++;
            break;
        case &#39;C&#39;:
            myArr[1]++;
            if (myArr[1] == checkArr[1])
                checkSecret++;
            break;
        case &#39;G&#39;:
            myArr[2]++;
            if (myArr[2] == checkArr[2])
                checkSecret++;
            break;
        case &#39;T&#39;:
            myArr[3]++;
            if (myArr[3] == checkArr[3])
                checkSecret++;
            break;
        }
    }</code></pre>
<ul>
<li>첫번째 문자열은 슬라이딩하지 않고 바로 각 문자만 비밀번호 체크 배열(checkArr)와 비교해준다.</li>
<li>case 문을 이용해서 각각 어떤 문자인지 분기를 나누고, 현재상태 배열(myArr)을 업데이트 해준 후 현재상태 배열(myArr)와  비밀번호 체크 배열(checkArr)의 값이 같으면 checkSecret에 1을 더해준다.</li>
<li>그 후, checkSecret이 4가 되면 해당 비밀번호는 유효한 비밀번호이므로 결과값을 저장하는 result에 1을 더해준다.</li>
</ul>
<h4 id="3-슬라이딩-윈도우를-이용하여-처리한다">3. 슬라이딩 윈도우를 이용하여 처리한다.</h4>
<pre><code class="language-java"> // 슬라이딩 윈도우 처리 부분
int left = 0;   //왼쪽 값
int right = P;  //오른쪽 값
while(right &lt; S){   //배열 인덱스는 0부터 시작이므로 right &lt; S까지
    Add(A[right++]);  
    Remove(A[left++]);  
    if (checkSecret == 4)
        Result++;
    }
    System.out.println(Result);
    bf.close();
}

//제거되는  문자를 처리해주는 메서드
//각각의 문자열 중 같은게 있으면 myArr[]에 1을 빼주고
//myArr[]과 checkArr[]가 같다면 checkSecret--
private static void Remove(char c) { 
    switch (c) {
        case &#39;A&#39;:
             if (myArr[0] == checkArr[0])
                 checkSecret--;
            myArr[0]--;
            break;
        case &#39;C&#39;:
            if (myArr[1] == checkArr[1])
                checkSecret--;
            myArr[1]--;
            break;
        case &#39;G&#39;:
             if (myArr[2] == checkArr[2])
                 checkSecret--;
            myArr[2]--;
             break;
        case &#39;T&#39;:
            if (myArr[3] == checkArr[3])
                checkSecret--;
            myArr[3]--;
            break;
    }
    }
}</code></pre>
<ul>
<li>초기부분이 지나면 한칸씩 이동하여 슬라이딩하고, 문자를 체크해야한다.</li>
<li>슬라이딩 부분만 빼면 초기 문자 비교는 똑같다.</li>
<li>우선 슬라이딩 시작과 끝을 알리는 left, right 변수를 선언하고, right변수가 전체 문자열의 길이(S)보다 작을동안 while문을 돌려준다. -&gt; right &lt; S 로 범위를 설정한 이유는 배열 인덱스는 0부터 시작이므로!</li>
<li>그 후, 한칸 옮겨간 right는 포함하고, 한칸 빠진 left는 빼준다.</li>
<li>Add메서드를 이용해서 A[right]를 추가해주고, 후위연산으로 right를 증가시킨다.</li>
<li>Remove메서드를 이용해서 A[left]는 제거하고, 후위연산으로 left를 감소시킨다.</li>
</ul>
<h4 id="4-결과값을-출력한다">4. 결과값을 출력한다.</h4>
<ul>
<li>result값을 출력한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]좋다 - 1253]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java1253-%EC%A2%8B%EB%8B%A4</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java1253-%EC%A2%8B%EB%8B%A4</guid>
            <pubDate>Mon, 13 Oct 2025 14:54:27 GMT</pubDate>
            <description><![CDATA[<h1 id="gold-iv-좋다---1253">[Gold IV] 좋다 - 1253</h1>
<p><a href="https://www.acmicpc.net/problem/1253">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>N개의 수 중에서 어떤 수가 다른 수 두 개의 합으로 나타낼 수 있다면 그 수를 “좋다(GOOD)”고 한다.</p>

<p>N개의 수가 주어지면 그 중에서 좋은 수의 개수는 몇 개인지 출력하라.</p>

<p>수의 위치가 다르면 값이 같아도 다른 수이다.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에는 수의 개수 N(1 ≤ N ≤ 2,000), 두 번째 줄에는 i번째 수를 나타내는 A<sub>i</sub>가 N개 주어진다. (|A<sub>i</sub>| ≤ 1,000,000,000, A<sub>i</sub>는 정수)</p>

<h3 id="출력">출력</h3>
 <p>좋은 수의 개수를 첫 번째 줄에 출력한다.</p>

<h3 id="예제">예제</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/b8cf67c7-a9c9-41e8-b21a-9366af9014b0/image.png" alt=""></p>
<hr>
<h1 id="나의-문제-풀이">나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        //N : 수의 개수
        int N = Integer.parseInt(br.readLine());
        int count = 0;

        StringTokenizer st = new StringTokenizer(br.readLine());

        long[] arr = new long[N];
        for (int i = 0; i &lt; N; i++) {
            arr[i]= Long.parseLong(st.nextToken());
        }
        //배열 정렬
        Arrays.sort(arr);

        for(int k=0; k&lt;N; k++){
            long find = arr[k];   //찾고자하는값
            int start = 0;
            int end = N-1;
            while (start &lt; end){
                //합이 같으면
                if(arr[start] + arr[end] == find){
                    //find가 서로 다른 두 수의 합인지 체크 -&gt; find가 5이고, start 0, end가 5일때와 같은 예외처리
                     if(start != k &amp;&amp; end != k){
                         count ++;
                         break;
                     }else if(start==k){    //포인터중 하나가 타깃 k를 가리키면 그 포인터를 한칸 움직인다.
                        start ++;
                     }else if(end == k){
                         end --;
                     }
                //더한 값이 find보다 작으면 더 큰값을 만들기 위해 start++
                }else if(arr[start] + arr[end] &lt; find){
                    start++;
                //더한 값이 find보다 크면 더 작은값을 만들기 위해 end--    
                }else{
                    end--;
                }
            }
        }
        System.out.println(count);
    }
}</code></pre>
<ul>
<li>N개의 수 중에서 어떤 수가 다른 수 두 개의 합을 나타내는 갯수를 찾는것이다.</li>
<li>이것도 정렬 + 투포인트 알고리즘을 사용한다.</li>
<li>단, 정렬된 데이터에서 자기자신을 좋은 수 만들기에 포함하면 안되므로 예외처리해줘야한다!</li>
</ul>
<h2 id="문제-풀어보기">문제 풀어보기</h2>
<h3 id="1-수를-입력받아-배열-a에-저장한-후-정렬한다">1. 수를 입력받아 배열 A에 저장한 후 정렬한다.</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/634d69b1-05f2-48de-a5c2-e6e2b6a2b092/image.png" alt=""></p>
<h3 id="2-투-포인터-ij를-배열-a의-양쪽-끝에-위치시키고-투-포인터-이동-원칙을-활용해-탐색을-수행한다판별의-대상이-되는-수는-k라-한다">2. 투 포인터 i,j를 배열 A의 양쪽 끝에 위치시키고, 투 포인터 이동 원칙을 활용해 탐색을 수행한다.(판별의 대상이 되는 수는 K라 한다.)</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/ac13c291-80fb-4d58-9442-199aa4cb332c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a1cdd09c-152c-46bc-95b9-3484a06a287b/image.png" alt=""></p>
<h4 id="1-aiaj--find찾고자-하는-값이면">(1) A[i]+A[j] == find(찾고자 하는 값)이면</h4>
<ul>
<li>서로 다른 두수의 값인지 체크 후 서로 다른 값이면 count++후 탐색 종료</li>
<li>탐색 종료를 하는 이유는 좋은수가 되는 순서쌍의 개수를 찾는 것이 아니라, 좋은 수의 &quot;개수&quot;를 세는것이기 때문에 k가 이미 좋은 수라고 판명났기 때문에 탐색종료 하는것!</li>
<li>만약 find와 값은 같지만 i가 타깃 인덱스 k와 같으면 i++, j와 타깃 인덱스 k가 같으면 j--</li>
</ul>
<h4 id="2-aiaj--find-이면">(2) A[i]+A[j] &lt; find 이면</h4>
<ul>
<li>더 큰값을 만들기 위해 i++</li>
</ul>
<h4 id="3-aiaj--find-이면">(3) A[i]+A[j] &gt; find 이면</h4>
<ul>
<li>더 작은 값을 만들기 위해 j--</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]주몽 - 1940]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%A3%BC%EB%AA%BD-1940</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%A3%BC%EB%AA%BD-1940</guid>
            <pubDate>Mon, 13 Oct 2025 14:09:30 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-iv-주몽---1940">[Silver IV] 주몽 - 1940</h1>
<p><a href="https://www.acmicpc.net/problem/1940">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>주몽은 철기군을 양성하기 위한 프로젝트에 나섰다. 그래서 야철대장을 통해 철기군이 입을 갑옷을 만들게 하였다. 야철대장은 주몽의 명에 따르기 위하여 연구에 착수하던 중 아래와 같은 사실을 발견하게 되었다.</p>

<p>갑옷을 만드는 재료들은 각각 고유한 번호를 가지고 있다. 갑옷은 두 개의 재료로 만드는데 두 재료의 고유한 번호를 합쳐서 M(1 ≤ M ≤ 10,000,000)이 되면 갑옷이 만들어 지게 된다. 야철대장은 자신이 만들고 있는 재료를 가지고 갑옷을 몇 개나 만들 수 있는지 궁금해졌다. 이러한 궁금증을 풀어 주기 위하여 N(1 ≤ N ≤ 15,000) 개의 재료와 M이 주어졌을 때 몇 개의 갑옷을 만들 수 있는지를 구하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에는 재료의 개수 N(1 ≤ N ≤ 15,000)이 주어진다. 그리고 두 번째 줄에는 갑옷을 만드는데 필요한 수 M(1 ≤ M ≤ 10,000,000) 주어진다. 그리고 마지막으로 셋째 줄에는 N개의 재료들이 가진 고유한 번호들이 공백을 사이에 두고 주어진다. 고유한 번호는 100,000보다 작거나 같은 자연수이다.</p>

<h3 id="출력">출력</h3>
 <p>첫째 줄에 갑옷을 만들 수 있는 개수를 출력한다.</p>

<h3 id="예제">예제</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/6025b4e4-69cf-48fc-b44f-b85f34b500ba/image.png" alt=""></p>
<hr>
<h1 id="나의-문제-풀이">나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        //N : 재료의 갯수
        int N = Integer.parseInt(br.readLine());

        //M : 갑옷이 완성되는 번호의 합
        int M = Integer.parseInt(br.readLine());

        StringTokenizer st = new StringTokenizer(br.readLine());

        int[] arr = new int[N];
        for (int i = 0; i &lt; N; i++) {
            arr[i]= Integer.parseInt(st.nextToken());
        }
        //배열 정렬
        Arrays.sort(arr);

        int count = 0;
        int start = 0;
        //배열 인덱스는 0부터 시작이므로 끝 값은 N-1
        int end = N-1;

        //start가 end보다 작을때까지
        while(start&lt;end){
            if(arr[start]+arr[end] &lt; M){
                start++;
            }else if(arr[start]+arr[end] &gt; M){
                end--;
            }else{
                count++;
                start++;
                end--;
            }
        }
        System.out.println(count);
    }
}</code></pre>
<ul>
<li>투포인터 + 배열 정렬을 쓰는 문제이다.</li>
<li>각각의 재료는 한번쓰면 없어진다고 생각해야한다.</li>
</ul>
<hr>
<h2 id="문제-풀어보기">문제 풀어보기</h2>
<h3 id="1-재료-데이터를-배열에-저장한-후-오름차순으로-정렬한다">1. 재료 데이터를 배열에 저장한 후 오름차순으로 정렬한다.</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/3b1b1ab5-2734-4290-a212-c03c0c07c8e6/image.png" alt=""></p>
<h3 id="2-투포인터를-각각-양쪽-끝에-위치-시킨-후-조건에-따라-포인터를-이동시키고-포인터가-만날때-까지-반복한다">2. 투포인터를 각각 양쪽 끝에 위치 시킨 후, 조건에 따라 포인터를 이동시키고, 포인터가 만날때 까지 반복한다.</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/d3cf5ee5-4ad7-42ea-9b75-9d1296f55551/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/e34c174c-51c3-4b29-877f-056bb82ac93b/image.png" alt=""></p>
<h4 id="1-더한-값이-m보다-작을-때">(1) 더한 값이 M보다 작을 때</h4>
<ul>
<li>i의 값을 1 증가시킨다. -&gt; 더 큰 값을 찾아야하므로!</li>
</ul>
<h4 id="2-더한-값이-m과-같을-때">(2) 더한 값이 M과 같을 때</h4>
<ul>
<li>count값을 1 증가시키고, i는 1증가, j는 1 감소 시킨다.</li>
</ul>
<h4 id="3-더한-값이-m보다-클-때">(3) 더한 값이 M보다 클 때</h4>
<ul>
<li>j의 값을 1 감소시킨다. -&gt; 더 작은 값을 찾아야 하므로</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]수들의 합 5 - 2018]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9-5-2018</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9-5-2018</guid>
            <pubDate>Mon, 13 Oct 2025 10:19:49 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-v-수들의-합-5---2018">[Silver V] 수들의 합 5 - 2018</h1>
<p><a href="https://www.acmicpc.net/problem/2018">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>어떠한 자연수 N은, 몇 개의 연속된 자연수의 합으로 나타낼 수 있다. 당신은 어떤 자연수 N(1 ≤ N ≤ 10,000,000)에 대해서, 이 N을 몇 개의 연속된 자연수의 합으로 나타내는 가지수를 알고 싶어한다. 이때, 사용하는 자연수는 N이하여야 한다.</p>

<p>예를 들어, 15를 나타내는 방법은 15, 7+8, 4+5+6, 1+2+3+4+5의 4가지가 있다. 반면에 10을 나타내는 방법은 10, 1+2+3+4의 2가지가 있다.</p>

<p>N을 입력받아 가지수를 출력하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫 줄에 정수 N이 주어진다.</p>

<h3 id="출력">출력</h3>
 <p>입력된 자연수 N을 몇 개의 연속된 자연수의 합으로 나타내는 가지수를 출력하시오</p>


<p><img src="https://velog.velcdn.com/images/dev_ssj/post/11903faf-6954-4681-afb0-be7664f83e2d/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        //N : 입력 받을 수의 갯수
        int N = Integer.parseInt(st.nextToken());

        //예시의 경우, 15처럼 연속된 수가 아닌 하나의 수로도 합을 나타낼수 있기때문에 미리 1세팅
        int count = 1;
        //start와 end가 1로 시작하므로, sum도 1로 초기값 세팅
        int sum = 1;
        int start = 1;
        int end = 1;
        //end가 N이 아닐때까지 출력
        // (end가 N일때는 마지막 값에서 하나의 수로도 합을 나타낼 수 있기 때문에-&gt; 이 경우는 이미 count = 1로 미리 세팅해둠)
        while (end != N){
            //sum과 n이 같으면 count ++, end 한칸 뒤로 옮긴 후 그값을 sum에 더해줌
            if(sum==N){
                count++;
                end ++;
                sum = sum + end;

            //sum이 N보다 크면 sum에서 start를 빼주고, start 한칸 뒤로 옮김    
            }else if (sum &gt; N){
                sum = sum - start;
                start++;

            //sum이 N보다 작으면 end를 한칸 뒤로 옮기고, sum에 end를 더함    
            }else {
                end++;
                sum=sum+end;
            }
        }
        System.out.println(count);
    }
}</code></pre>
<ul>
<li>연속된 자연수의 합을 구하는 문제이므로 시작인덱스와 종료 인덱스를 지정하는 투포인터를 이용하여 문제를 풀어야한다.</li>
</ul>
<hr>
<h1 id="✅투포인터">✅투포인터</h1>
<p>말그대로 포인터 두개를 가지는 방식이다.
위 문제와 같이 특정한 합을 가지는 부분 연속 수열 찾기 문제에서 주로 사용된다.</p>
<h2 id="문제-풀어보기">문제 풀어보기</h2>
<h3 id="1-필요한-변수-초기화">1. 필요한 변수 초기화</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/ef2e5d10-6cd6-449a-8f2a-0d8e0a7cb5a4/image.png" alt=""></p>
<ul>
<li>count는 자연수의 합 갯수를 저장하는 변수이며, sum은 연속된 수의 합을 저장하는 변수이다.</li>
<li>count를 1로 초기화하는 이유는 N이 자기자신일 때의 경우의 수를 미리 넣고 초기화했기 때문이다.(예제에서는 N이 15이므로, 15 하나의 수로도 15를 만들 수 있으므로)</li>
<li>start_index와 end_index는 두개의 포인터를 의미한다. 처음엔 둘다 1부터 시작한다.</li>
</ul>
<h3 id="2-인덱스-증가-시키면서-n이-되는-값-찾기">2. 인덱스 증가 시키면서 N이 되는 값 찾기</h3>
<h4 id="1-sum이-n보다-작으면">(1) sum이 N보다 작으면</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/bf16a593-8ca1-4805-8669-ee696a77a470/image.png" alt=""></p>
<ul>
<li>누적합인 sum이 N보다 작으면 더 큰 수를 찾기 위해 end_index를 오른쪽으로 이동시킨다.</li>
<li>위와 같이 sum이 N보다 작으면 두가지 로직이 실행되어야한다.</li>
<li><ul>
<li><ul>
<li><ol>
<li>end_index 1 증가시키기</li>
</ol>
<ul>
<li><ol start="2">
<li>sum의 누적합 갱신**</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>여기서 유의할 점은 <strong>end_index를 먼저 증가시키고, 누적합을 갱신</strong>시킨다는 것이다.</li>
<li>즉, <code>[start, end]</code> → <code>[start, end+1]</code>로 확장하는 것이기 때문에 end를 먼저 증가 시킨 후 누적합을 갱신하는게 맞다.</li>
</ul>
<h4 id="2-sum이-n과-같으면">(2) sum이 N과 같으면</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/0fd6c328-26aa-4adb-8e5f-32536d1bb49a/image.png" alt=""></p>
<ul>
<li>sum과 N이 같다는 건 우리가 찾는 연속된 자연수의 합이 N과 같다는 것을 뜻한다.</li>
<li>sum이 N과 같다면 세가지 로직이 실행되어야 한다.</li>
<li><ul>
<li><ul>
<li><ol>
<li>count 1 증가시키기</li>
</ol>
<ul>
<li><ol start="2">
<li>end_index 1 증가시키기</li>
</ol>
</li>
<li><ol start="3">
<li>sum의 누적합 갱신**</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="3-sum이-n보다-크면">(3) sum이 N보다 크면</h4>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a9b6b2b6-9a1a-4778-9044-5a7a5bb32254/image.png" alt=""></p>
<ul>
<li><p>sum이 N보다 크면 누적합을 줄여주어야한다. 즉, start_index를 증가시켜야한다.</p>
</li>
<li><p>sum이 N보다 크면 두가지 로직이 실행되어야 한다.</p>
</li>
<li><ul>
<li><ul>
<li><ol>
<li>sum의 누적합 감소시키기</li>
</ol>
<ul>
<li><ol start="2">
<li>start_index 1 증가 시키기**</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>여기서 유의할 점은 sum의 누적합을 먼저 감소 시킨 후, start_index를 1 증가시키는 것이다.</p>
</li>
<li><p>즉, <code>[start, end]</code> → <code>[start+1, end]</code> 로 감소시키는 것 이기 때문에 sum에서 먼저 start_index를 빼는 것이 맞다.</p>
</li>
</ul>
<blockquote>
<p>위의 풀이처럼 코드를 짜면 된다! 이해만 한다면 쉽다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]나머지 합 - 10986번]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EB%82%98%EB%A8%B8%EC%A7%80-%ED%95%A9-10986%EB%B2%88</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EB%82%98%EB%A8%B8%EC%A7%80-%ED%95%A9-10986%EB%B2%88</guid>
            <pubDate>Sat, 11 Oct 2025 17:17:26 GMT</pubDate>
            <description><![CDATA[<h1 id="gold-iii-나머지-합---10986">[Gold III] 나머지 합 - 10986</h1>
<p><a href="https://www.acmicpc.net/problem/10986">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>수 N개 A<sub>1</sub>, A<sub>2</sub>, ..., A<sub>N</sub>이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오.</p>

<p>즉, A<sub>i</sub> + ... + A<sub>j</sub> (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j) 쌍의 개수를 구해야 한다.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 N과 M이 주어진다. (1 ≤ N ≤ 10<sup>6</sup>, 2 ≤ M ≤ 10<sup>3</sup>)</p>

<p>둘째 줄에 N개의 수 A<sub>1</sub>, A<sub>2</sub>, ..., A<sub>N</sub>이 주어진다. (0 ≤ A<sub>i</sub> ≤ 10<sup>9</sup>)</p>

<h3 id="출력">출력</h3>
 <p>첫째 줄에 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 출력한다.</p>

<h3 id="예시">예시</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/e090e58e-09d7-467c-8e20-e62e89f41f4b/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        //N : 입력 받을 수의 갯수, M : 나눌 수
        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        //합배열과 같은 나머지 인덱스 카운트 배열
        long[] S = new long[N];
        long[] C = new long[M];
        long answer = 0;

        //합배열 생성
        st = new StringTokenizer(br.readLine());
        for(int i=0; i&lt;N; i++){
            //인덱스가 0이면 0-1시 예외발생하므로 if문으로 0인지 체크
            if(i==0){
                S[i] = Long.parseLong(st.nextToken());
            }else {
                S[i] = S[i - 1] + Long.parseLong(st.nextToken());
            }
        }
        //합배열의 모든값에 %M 하기
        for(int i=0; i&lt;N; i++){
            int reminder = (int) (S[i] % M);
            //나머지가 0이면 결과값에 1씩 더하기
            if(reminder == 0) {
                answer += 1;
            }
            //나머지가 같은 인덱스의 개수 카운트하기(ex. 나머지가 1인 수가 3개면 C[1] = 3
            C[reminder]+=1;
        }

        for(int i=0; i&lt;M; i++){
            //나머지가 같은 인덱스중 2개를 뽑는 경우의 수 이므로 인덱스의 값이 2이상인거만
            //ex. 6개면 6C2 -&gt; 6*5 / 2 -&gt; 15개.
            if(C[i] &gt; 1){
                answer = answer + (C[i] *(C[i]-1)/ 2);
            }
        }
        System.out.println(answer);
    }
}</code></pre>
<p>처음에 문제 이해를 못해서 많이 애먹었다...
첫번째 줄에 입력되는 첫번째 숫자N은 2번째 줄의 숫자의 갯수고, 그 다음 입력되는 M은 나누어 떨어지는 수(나눌 수)를 의미한다.
그리고 합이 M으로 나누어 떨어지는 (i,j)쌍의 개수를 구하라고 하는데,  예시의 1 2 3 1 2를 예로 들어보자면</p>
<ul>
<li><strong>인덱스1 부터</strong><ul>
<li>A[1] = 1 → 나머지3</li>
<li>A[1]+A[2] = 1+2 = 3 → 나머지0</li>
<li>A[1]+A[2]+A[3] = 1+2+3 = 6 → 나머지0</li>
<li>A[1]+A[2]+A[3]+A[4] = 1+2+3+1 = 7 → 나머지1</li>
<li>A[1]+A[2]+A[3]+A[4]+A[5] = 1+2+3+1+2 = 9 → 나머지0</li>
</ul>
</li>
<li><strong>인덱스2 부터</strong><ul>
<li>A[2] = 2 → 나머지1</li>
<li>A[2]+A[3] = 2+3 = 5 → 나머지1</li>
<li>A[2]+A[3]+A[4] = 2+3+1 = 6 → 나머지0</li>
<li>A[2]+A[3]+A[4]+A[5] = 2+3+1+2 = 8 → 나머지2</li>
</ul>
</li>
</ul>
<p>...</p>
<ul>
<li>이런식으로 누적합을 더해서 그 누적합에서 m으로 나눴을 때 나누어떨어지는 쌍의 개수를 구하는 것이다. </li>
</ul>
<hr>
<h1 id="문제-풀이-순서">문제 풀이 순서</h1>
<h3 id="1-원본-배열인-a배열의-합배열-s를-생성한다">1. 원본 배열인 A배열의 합배열 S를 생성한다.</h3>
<blockquote>
<p>S[i] = S[i-1] + A[i]</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/2755a934-5f5f-407a-b7f9-3a3db31101d2/image.png" alt=""></p>
<h3 id="2-합-배열-s의-모든-값에-대해-m으로-나머지-연산을-수행하여-합배열을-업데이트-한다">2. 합 배열 S의 모든 값에 대해 M으로 나머지 연산을 수행하여 합배열을 업데이트 한다</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/6e75d36c-02d3-4628-81c1-a954bb51821a/image.png" alt=""></p>
<ul>
<li>여기서 이해가 안될 수도 있는데, 1에서 구한 합배열을 M(예시에선 3)으로 나머지 연산을 수행하고, 해당 값의 나머지를 배열의 값으로 업데이트 하는것이다.</li>
<li>그리고 배열의 원소값이 0인 개수를 정답에 더한다.</li>
<li>예시에서는 나머지가 0인 인덱스가 3개이므로 3을 더한다.</li>
<li>이렇게 하는 이유는 원소의 값이 0이라는 뜻은 원본 배열의 0부터 i까지의 구간 합이 이미 M으로 나누어 떨어진다는 뜻이기 때문!</li>
</ul>
<h3 id="3-나머지-값이-같은-합-배열의-개수를-구하고-2개의-원소를-뽑는-모든-경우의-수를-구한다">3. 나머지 값이 같은 합 배열의 개수를 구하고 2개의 원소를 뽑는 모든 경우의 수를 구한다.</h3>
<ul>
<li>(A + B)% C는 ((A %C) + (B% C)) % C와 같다.</li>
<li>즉, 이말은 구간 합 전체에 한 번 나머지를 취한 것과 각 원소의 나머지를 미리 계산해 더한 뒤 다시 나머지를 취한 결과는 같다는 소리!</li>
<li>그래서 나머지가 같은 값이 2개 이상은 것들 중에, 2개의 원소를 뽑는 모든 경우의 수를 구하여 정답에 더하면 된다.</li>
<li>위 예에서 0은 3개, 1은 2개이므로 3C2, 2C2로 경우의 수를 구하면된다.</li>
<li>3C2 = 3*2 / 2 = 3</li>
<li>2C2 = 2*1 / 2 = 1</li>
</ul>
<h3 id="모든-경우의-수를-더한다">모든 경우의 수를 더한다</h3>
<ul>
<li>2에서 구한 원소의 값이 0인 인덱스 + 3에서 구한 경우의수를 모두 더한 3+3+1 = 7 이 답이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]구간 합 구하기5 - 11660]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B05-11660</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B05-11660</guid>
            <pubDate>Sat, 11 Oct 2025 16:48:46 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-i-구간-합-구하기-5---11660">[Silver I] 구간 합 구하기 5 - 11660</h1>
<p><a href="https://www.acmicpc.net/problem/11660">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>N×N개의 수가 N×N 크기의 표에 채워져 있다. (x1, y1)부터 (x2, y2)까지 합을 구하는 프로그램을 작성하시오. (x, y)는 x행 y열을 의미한다.</p>

<p>예를 들어, N = 4이고, 표가 아래와 같이 채워져 있는 경우를 살펴보자.</p>

<table class="table table-bordered" style="line-height:20.8px; width:158px">
    <tbody>
        <tr>
            <td style="text-align:center">1</td>
            <td style="text-align:center">2</td>
            <td style="text-align:center">3</td>
            <td style="text-align:center">4</td>
        </tr>
        <tr>
            <td style="text-align:center">2</td>
            <td style="text-align:center">3</td>
            <td style="text-align:center">4</td>
            <td style="text-align:center">5</td>
        </tr>
        <tr>
            <td style="text-align:center">3</td>
            <td style="text-align:center">4</td>
            <td style="text-align:center">5</td>
            <td style="text-align:center">6</td>
        </tr>
        <tr>
            <td style="text-align:center">4</td>
            <td style="text-align:center">5</td>
            <td style="text-align:center">6</td>
            <td style="text-align:center">7</td>
        </tr>
    </tbody>
</table>

<p>여기서 (2, 2)부터 (3, 4)까지 합을 구하면 3+4+5+4+5+6 = 27이고, (4, 4)부터 (4, 4)까지 합을 구하면 7이다.</p>

<p>표에 채워져 있는 수와 합을 구하는 연산이 주어졌을 때, 이를 처리하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네 개의 정수 x1, y1, x2, y2 가 주어지며, (x1, y1)부터 (x2, y2)의 합을 구해 출력해야 한다. 표에 채워져 있는 수는 1,000보다 작거나 같은 자연수이다. (x1 ≤ x2, y1 ≤ y2)</p>

<h3 id="출력">출력</h3>
 <p>총 M줄에 걸쳐 (x1, y1)부터 (x2, y2)까지 합을 구해 출력한다.</p>


<h3 id="예제">예제</h3>
<p> <img src="https://velog.velcdn.com/images/dev_ssj/post/c7c3695a-0f70-4af2-9a87-b6c2f360be83/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        //N : 배열 크기, M : 질의 갯수
        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        //인덱스 편의성을 위해 +1
        int[][] A = new int[N+1][N+1];

        //1. 원본 배열 저장
        for(int i=1; i&lt;=N; i++){
            st = new StringTokenizer(br.readLine());
            for(int j=1; j&lt;=N; j++){
                A[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int[][] D = new int[N+1][N+1];
        //2. 합배열 저장
        for(int i=1; i&lt;=N; i++){
            for(int j=1; j&lt;=N; j++){
                D[i][j] = D[i-1][j] + D[i][j-1] + A[i][j] - D[i-1][j-1];
            }
        }

        //3. 질의 계산 및 출력
        for(int i = 0; i&lt;M; i++){
            //질의 입력받기
            st = new StringTokenizer(br.readLine());
            int x1 = Integer.parseInt(st.nextToken());
            int y1 = Integer.parseInt(st.nextToken());
            int x2 = Integer.parseInt(st.nextToken());
            int y2 = Integer.parseInt(st.nextToken());

            //구간 합 배열로 질의 계산 하기
            int result = D[x2][y2] - D[x1-1][y2] - D[x2][y1-1] + D[x1-1][y1-1];
            sb.append(result).append(&#39;\n&#39;);
        }
        System.out.println(sb.toString());
    }
}</code></pre>
<p>이 문제도 1차원 구간 합배열과 마찬가지로 질의마다 합을 구하면 시간초과로 통과를 못한다.
구간 합배열을 2차원으로도 어떻게 작성하는지 알아보자.</p>
<hr>
<h1 id="✅2차원-구간-합-배열">✅2차원 구간 합 배열</h1>
<p>A[X][Y]는 원본 배열을 의미하며,
D[X][Y]는 원본 배열 A의 (0,0)부터 (X,Y)까지의 사각형 영역 안에 있는 수의 합이다.</p>
<p>먼저 비교적 간단한 1행, 1열을 구해보도록하자.
<img src="https://velog.velcdn.com/images/dev_ssj/post/a841bed7-63d2-4223-8464-cfa6a50f956d/image.png" alt=""></p>
<h3 id="1행-구간-합-구하기">1행 구간 합 구하기</h3>
<ul>
<li>위 예시로 1행의 구간 합을 구한다고 생각해보자.</li>
<li><strong>D[1][3]</strong>의 구간합을 구하고 싶다면 바로 직전의 구간 합 + 현재의 값(원본 배열의 A[1][3])을 더해야한다.</li>
<li>즉, D[1][3] = D[1][2] + A[1][3] → D[1][3] = 3 + 3 = 6 이 된다.</li>
<li>이것을 수식으로 표현하면, <strong>D[1][[j] = D[1][j-1] + A[i][j]</strong> 로 표현 가능하다.</li>
</ul>
<h3 id="1열-구간-합-구하기">1열 구간 합 구하기</h3>
<ul>
<li>마찬가지로 위 예시로 1열의 구간 합을 구한다고 생각하자.</li>
<li><strong>D[4][1]</strong>의 구간 합을 구하고 싶다면 바로 직전의 구간 합 + 현재의 값(원본 배열의 A[4][1]을 더해야한다.</li>
<li>D[4][1] = D[3][1] + A[4][1] → D[4][1] = 6 + 4 = 10</li>
<li>이것을 수식으로 표현하면 <strong>D[i][1] = D[i-1][1] + A[i][1]</strong>이 된다.</li>
</ul>
<h3 id="그렇다면-나머지-값은-어떻게-구하지">그렇다면 나머지 값은 어떻게 구하지?</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/93fb2e8a-a553-497e-b315-9c8e05e2f665/image.png" alt=""></p>
<p>원리만 이해하면 매우 쉽다! 위 1행, 1열을 구한것과 마찬가지로 각각 [i][j-1]과 [i-1][j]를 더하고, 현재의 값(원본 배열의 값)을 더하면 된다. 
하지만 이렇게만 하게되면 오차가 생기게된다. 그 이유는 위 이미지와 마찬가지로 겹치는 부분이 생기기 때문이다.(예시에서는 D[1][1]이 겹치는 부분이다) 그래서 이 겹치는 부분을 제거해줘야하는데, 그 수식까지 포함한 공식은 아래와 같다.</p>
<blockquote>
<p><strong>D[i][j] = D[i][j-1] + D[i-1][j] - D[i-1][j-1] + A[i][j]</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]구간 합 구하기4 - 11659]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B04-11659</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B04-11659</guid>
            <pubDate>Sat, 11 Oct 2025 16:24:54 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-iii-구간-합-구하기-4---11659">[Silver III] 구간 합 구하기 4 - 11659</h1>
<p><a href="https://www.acmicpc.net/problem/11659">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>수 N개가 주어졌을 때, i번째 수부터 j번째 수까지 합을 구하는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 수의 개수 N과 합을 구해야 하는 횟수 M이 주어진다. 둘째 줄에는 N개의 수가 주어진다. 수는 1,000보다 작거나 같은 자연수이다. 셋째 줄부터 M개의 줄에는 합을 구해야 하는 구간 i와 j가 주어진다.</p>

<h3 id="출력">출력</h3>
 <p>총 M개의 줄에 입력으로 주어진 i번째 수부터 j번째 수까지 합을 출력한다.</p>


<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a55a9cec-30fa-4735-93b0-43117469b4af/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제풀이">✅나의 문제풀이</h1>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine());

        //첫줄 : 데이터의 개수, 질의 개수
        int num = Integer.parseInt(st.nextToken());
        int quizNo = Integer.parseInt(st.nextToken());

        //인덱스의 편의성을 활용하기 위해 num+1.
        //plus[0]은 아무것도 더하지 않은값인 0으로 세팅하기 위해서
        int[] plus = new int[num+1];
        st = new StringTokenizer(br.readLine());

        //합배열 구하기
        for(int i=1; i&lt;=num; i++){
            plus[i] = plus[i-1] + Integer.parseInt(st.nextToken());
        }

        //질의 입력 받은 후 구간 합 구하기
        for(int q=0; q&lt;quizNo; q++){
            st = new StringTokenizer(br.readLine());
            int i = Integer.parseInt(st.nextToken());
            int j = Integer.parseInt(st.nextToken());

            sb.append(plus[j] - plus[i-1]).append(&#39;\n&#39;);
        }
        System.out.println(sb);
    }
}</code></pre>
<h1 id="✅구간-합-구하기">✅구간 합 구하기</h1>
<p>이 문제는 각 인덱스마다 값을 더하는 건데, 여기서 구간마다 합을 매번 계산하면 모든 계산을 시간 안에 끝낼 수 없다. 이럴 때 구간합을 이용하는 것이다!</p>
<h2 id="☑️합-배열-공식">☑️합 배열 공식</h2>
<blockquote>
<p><strong>S[i] = S[i-1] + A[i]</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/0d83760d-5cae-4377-b05f-bb0ca99314b4/image.png" alt=""></p>
<ul>
<li><p>예를 들어 A[3]까지의 합배열을 구하는 것이라면</p>
</li>
<li><p>S[3] = S[2] + A[3] 
→ S[3] = 9+3 
→ <strong>S[3] = 12</strong> 가 된다.</p>
</li>
<li><p>위 공식을 이용하여 합배열을 생성한 후, 구간 합 공식을 이용하여 구간합을 구하면 된다.</p>
</li>
</ul>
<h2 id="☑️구간-합-공식">☑️구간 합 공식</h2>
<blockquote>
<p><strong>S[j] - S[i-1]</strong></p>
</blockquote>
<ul>
<li>질의가 <strong>(1,3) *<em>일 때 : *</em>S[3] - S[1-1] = 12 - 0 = 12</strong></li>
<li>질의가 <strong>(2,4)</strong> 일 때 : <strong>S[4] - s[2-1] = 14 - 5 = 9</strong></li>
<li>질의가 <strong>(5,5)</strong> 일 때 : <strong>S[5] - S[5-1] = 15 - 14 = 1</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]수 찾기 - 1920 ]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%88%98-%EC%B0%BE%EA%B8%B0-1920</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%88%98-%EC%B0%BE%EA%B8%B0-1920</guid>
            <pubDate>Thu, 09 Oct 2025 12:25:14 GMT</pubDate>
            <description><![CDATA[<h1 id="silver-iv-수-찾기---1920">[Silver IV] 수 찾기 - 1920</h1>
<p><a href="https://www.acmicpc.net/problem/1920">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하시오.</p>

<h3 id="입력">입력</h3>
 <p>첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -2<sup>31</sup> 보다 크거나 같고 2<sup>31</sup>보다 작다.</p>

<h3 id="출력">출력</h3>
 <p>M개의 줄에 답을 출력한다. 존재하면 1을, 존재하지 않으면 0을 출력한다.</p>



<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        //입력받을 정수의 갯수
        int n = Integer.parseInt(br.readLine());

        int[] arr = new int[n];

        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);

        for(int i = 0; i &lt; n; i++){
            arr[i] = Integer.parseInt(st.nextToken());
        }

        Arrays.sort(arr);

        int m = Integer.parseInt(br.readLine());
        st = new StringTokenizer(br.readLine(), &quot; &quot;);


        for(int i=0; i &lt; m; i++){
            if(Arrays.binarySearch(arr, Integer.parseInt(st.nextToken()))&gt;= 0){
                sb.append(1).append(&#39;\n&#39;);
            }else{
                sb.append(0).append(&#39;\n&#39;);
            }
        }
        System.out.println(sb);

    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스][Java]완주하지 못한 선수 - 42576]]></title>
            <link>https://velog.io/@dev_ssj/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Java%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-42576-q1m5spxe</link>
            <guid>https://velog.io/@dev_ssj/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4Java%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-42576-q1m5spxe</guid>
            <pubDate>Wed, 08 Oct 2025 14:14:10 GMT</pubDate>
            <description><![CDATA[<h1 id="level-1-완주하지-못한-선수---42576">[level 1] 완주하지 못한 선수 - 42576</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">문제 링크</a> </p>
<h3 id="문제-설명">문제 설명</h3>
<p>수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.</p>

<p>마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</p>

<h5>제한사항</h5>

<ul>
<li>마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.</li>
<li>completion의 길이는 participant의 길이보다 1 작습니다.</li>
<li>참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.</li>
<li>참가자 중에는 동명이인이 있을 수 있습니다.</li>
</ul>

<h5>입출력 예</h5>
<table class="table">
        <thead><tr>
<th>participant</th>
<th>completion</th>
<th>return</th>
</tr>
</thead>
        <tbody><tr>
<td>["leo", "kiki", "eden"]</td>
<td>["eden", "kiki"]</td>
<td>"leo"</td>
</tr>
<tr>
<td>["marina", "josipa", "nikola", "vinko", "filipa"]</td>
<td>["josipa", "filipa", "marina", "nikola"]</td>
<td>"vinko"</td>
</tr>
<tr>
<td>["mislav", "stanko", "mislav", "ana"]</td>
<td>["stanko", "ana", "mislav"]</td>
<td>"mislav"</td>
</tr>
</tbody>
      </table>
<h5>입출력 예 설명</h5>

<p>예제 #1<br>
"leo"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</p>

<p>예제 #2<br>
"vinko"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</p>

<p>예제 #3<br>
"mislav"는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.</p>

<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        Map&lt;String, Integer&gt; m = new HashMap&lt;&gt;();
        for (int i = 0; i &lt; participant.length; i++) {
            if (m.containsKey(participant[i])) {
                int n = m.get(participant[i]);
                m.put(participant[i], n + 1);
            } else {
                m.put(participant[i], 1);
            }
        }

        for (int i = 0; i &lt; completion.length; i++) {
            if (m.containsKey(completion[i])) {
                int n = m.get(completion[i]);
                m.put(completion[i], n - 1);
            }
        }

        String answer = null;
        for (Map.Entry&lt;String, Integer&gt; entry : m.entrySet()) {
            if(entry.getValue() != 0){
                answer = entry.getKey();
            }
        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]괄호 - 9012]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B4%84%ED%98%B8-9012</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EA%B4%84%ED%98%B8-9012</guid>
            <pubDate>Sun, 28 Sep 2025 07:45:14 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/9012">[백준] 괄호 - 9012</a></p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/dddfb36d-5a9b-47ce-bdb8-7b285a49fea3/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        //입력받을 문자열 갯수
        int T = Integer.parseInt(br.readLine());

        for(int i = 0; i&lt; T; i++){
            sb.append(solve(br.readLine())).append(&#39;\n&#39;);
        }
        System.out.println(sb);
    }

    public static String solve(String s){
        Stack&lt;Character&gt; stack = new Stack&lt;&gt;();

        for(int i=0; i&lt;s.length(); i++){
            char c = s.charAt(i);

            //여는 괄호일 경우 스택에 넣는다.
            if(c==&#39;(&#39;){
                stack.push(c);
            }

            //닫는 괄호일 경우
            else if (c == &#39;)&#39;) {
                //stack이 비어있으면(즉, 짝꿍인 여는 괄호가 없으면)
                if (stack.empty()) {
                    return &quot;NO&quot;;
                }
                //그게 아니라면(정상적이라면)
                stack.pop();
            }
        }

        if(stack.empty()){
            return &quot;YES&quot;;
        }else {
            return &quot;NO&quot;;
        }
    }
}</code></pre>
<hr>
<h1 id="스택">스택</h1>
<ul>
<li>push() : 스택에 데이터를 추가하는 메서드</li>
<li>pop() : 스택에 들어있는 데이터를 삭제하는 메서드</li>
<li>peek() : 데이터는 삭제하지 않고 스택 맨 위에 있는 값을 확인하는 메서드</li>
<li>size() : 현재 스택에 저장되어 있는 요소의 개수를 알려주는 메서드</li>
<li>empty() : 스택이 비어있는지, 데이터가 들어있는지 확인하는 메서드(true/false)</li>
</ul>
<h1 id="문제-풀이">문제 풀이</h1>
<p>괄호 검사는 후입선출 방식인 스택이 적합하다.
기본적인 검사 방식은 아래와 같다</p>
<ol>
<li>여는 괄호 &#39;(&#39;를 만날 경우 -&gt; 스택에 넣는다 (push)</li>
<li>닫는 괄호 &#39;)&#39;를 만날 경우 -&gt; 스택에서 삭제한다(pop)</li>
<li>마지막 괄호까지 조사 후, 스택에 남아있는게 없으면 올바른 경우</li>
</ol>
<p>해당 괄호가 올바른지 판단하는 분기는 3가지가 있다.</p>
<p><strong>1. 여는 괄호와 닫는 괄호가 올바른 경우(여는괄호의 수와 닫는 괄호의 수가 동일한 경우)</strong>
예시 : ( ( ( ( ) ( ) ) ( )
<img src="https://velog.velcdn.com/images/dev_ssj/post/b818d906-3f16-4b48-9f23-f7a119d7b94e/image.png" alt=""></p>
<p><strong>2. 괄호가 남는 경우(여는 괄호의 수가 더 많은 경우)</strong>
예시 : ( ( ) ) ( ) )
<img src="https://velog.velcdn.com/images/dev_ssj/post/cc011f4d-80fc-4bbc-86ac-11f15054b975/image.png" alt=""></p>
<ul>
<li>모든 괄호를 검사한 후 스택에 괄호가 남는 경우는 여는 괄호가 많은 경우라는 의미다. 즉, 이는 온전한 수식이 아니라는 것!</li>
</ul>
<p><strong>3. 괄호가 부족한 경우(닫는 괄호가 더 많은 경우)</strong>
예시 : ( ( ) ) ( ) )
<img src="https://velog.velcdn.com/images/dev_ssj/post/1467840a-2a81-4b71-b1ef-fd353c939398/image.png" alt=""></p>
<ul>
<li>위 이미지를 보면, 6번째 과정에서 pop을 하면 스택이 비어있다.
스택이 비어있다는 것은 현재까지는 완전한 수식이라는 건데, 그 이후에(7번째) 닫는 괄호가 또 나온다면 pop할 요소가 없기 때문에 올바른 경우가 아니다.</li>
</ul>
<p>이 분기를 통해 3가지 조건문을 만들 수 있다.</p>
<pre><code class="language-java">//여는 괄호일 경우 스택에 넣는다.
if(c==&#39;(&#39;){
    stack.push(c);
}

//닫는 괄호일 경우
else if (c == &#39;)&#39;) {
//stack이 비어있으면(즉, 짝꿍인 여는 괄호가 없으면)
    if (stack.empty()) {
        return &quot;NO&quot;;
    }
//그게 아니라면(정상적이라면)
    stack.pop();
    }
}</code></pre>
<ul>
<li>우선, 제일 먼저 여는 괄호 일 경우 stack에 넣는다.</li>
<li>닫는 괄호인데 stack이 비어있다면 즉, 현재 스택에 짝꿍인 여는 괄호가 없다면 올바른 수식이 아니므로 NO를 리턴한다.</li>
<li>닫는 괄호인데 스택이 비어있지 않으면 올바른 수식이므로 pop을 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Stream API]]></title>
            <link>https://velog.io/@dev_ssj/Java-Stream-API</link>
            <guid>https://velog.io/@dev_ssj/Java-Stream-API</guid>
            <pubDate>Sat, 27 Sep 2025 18:40:13 GMT</pubDate>
            <description><![CDATA[<h1 id="💡stream-api란">💡Stream API란?</h1>
<p><strong>Stream API</strong>란 Java 8에서 도입되어 <strong>데이터 컬렉션을 함수형 스타일로 처리할 수 있게 하는 기능</strong>이다. 이는 데이터를 추상화하고 처리하는 다양한 연산을 수행할 수 있게 해준다. 특히, 데이터 소스에 대한 계산 로직을 고수준에서 간결하게 표현할 수 있어, 코드의 가독성과 유지보수성이 향상된다.</p>
<pre><code class="language-java">List&lt;String&gt; myList = Arrays.asList(&quot;a1&quot;, &quot;a2&quot;, &quot;b1&quot;, &quot;b2&quot;, &quot;c1&quot;, &quot;c2&quot;);
myList.stream()
  .filter(s -&gt; s.startsWith(&quot;c&quot;))
  .map(String::toUpperCase)
  .sorted()
  .forEach(System.out::println);
</code></pre>
<hr>
<h2 id="✅stream이란">✅Stream이란?</h2>
<p><strong>스트림이란 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있다.</strong></p>
<h4 id="연속된-요소">연속된 요소</h4>
<ul>
<li>컬렉션과 마찬가지로 스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공한다.</li>
<li>컬렉션은 자료구조이므로 시간과 공간의 복잡성과 관련된 요소 저장 및 접근 연산이 주를 이룬다.</li>
<li>반면 스트림은 <code>filter, sorted, map</code> 처럼 표현 계산식이 주를 이룬다.</li>
</ul>
<h4 id="소스">소스</h4>
<ul>
<li>스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비한다.</li>
<li><strong>정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지</strong>된다.</li>
<li>즉, 리스트로 스트림을 만들면, 스트림의 요소는 리스트의 요소와 같은 순서를 유지한다.</li>
</ul>
<h4 id="데이터-처리-연산">데이터 처리 연산</h4>
<ul>
<li>스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원한다.</li>
<li><code>filter, map, reduce, finde, match, sort</code> 등으로 데이터 조작가능 하다.</li>
<li><strong>스트림 연산은 순차적 또는 병렬로 실행 가능</strong>하다.</li>
</ul>
<hr>
<h2 id="✅stream-api의-특징">✅Stream API의 특징</h2>
<h4 id="저장소가-없음no-storage">저장소가 없음(No Storage)</h4>
<ul>
<li>스트림 자체는 <strong>데이터를 저장하지 않는다.</strong></li>
<li>컬렉션, 배열, I/O 채널 등의 데이터 소스를 표현하는데 사용된다.</li>
</ul>
<h4 id="함수형functional-in-nature">함수형(Functional in nature)</h4>
<ul>
<li>스트림 연산은 주로 <strong>람다 표현식을 사용하여 함수형 프로그래밍</strong>의 접근 방식을 취한다.</li>
<li>이로 인해 외부 반복 대신 <span style="color:red"><strong>내부 반복</strong></span>을 사용할 수 있다.</li>
</ul>
<h4 id="지연성-추구laziness-seeking">지연성 추구(Laziness-seeking)</h4>
<ul>
<li>많은 스트림 연산들은 지연된다는 특징을 갖고 있다.</li>
<li>즉, <strong>실제로 필요할때까지 계산을 수행하지 않는다.</strong></li>
</ul>
<h4 id="무한성possibly-unbounded">무한성(Possibly unbounded)</h4>
<ul>
<li>스트림은 한정된 데이터 뿐만 아니라 무한한 데이터에 대해서도 작업을 수행할 수 있다.</li>
</ul>
<h4 id="소비-가능성consumable">소비 가능성(Consumable)</h4>
<ul>
<li>스트림의 요소들은 소비된다.</li>
<li>즉, <strong>한 번 사용된 스트림은 재사용할 수 없다.</strong></li>
</ul>
<hr>
<h2 id="✅collection-vs-stream">✅Collection vs Stream</h2>
<blockquote>
<p>위에서 설명한 것을 보면 Collection과 Stream이 다르다는 것을 얼추 느꼈을 것이다.
기존에 존재하뎐 Collection과 Stream은 무슨차이가 있을까?</p>
</blockquote>
<h3 id="📘collection">📘Collection</h3>
<ul>
<li><strong>모든 값을 메모리에 저장하는 자료구조</strong>다.</li>
<li>따라서 Collection에 추가하기 전에 미리 계산이 완료되어 있어야 한다.</li>
<li><strong>외부 반복</strong>을 통해 사용자가 직접 반복 작업을 거쳐 요소를 가져올 수 있다(for-each)</li>
</ul>
<h3 id="📗stream">📗Stream</h3>
<ul>
<li><strong>요청할 때만 요소를 계산</strong>한다.</li>
<li><strong>내부 반복</strong>을 사용하므로, 추출 요소만 선언해주면 알아서 반복 처리를 진행한다.</li>
<li>스트림에 요소를 따로 추가 혹은 제거하는 작업은 불가능 하다.</li>
</ul>
<hr>
<h2 id="✅외부-반복과-내부-반복">✅외부 반복과 내부 반복</h2>
<p>Collection은 외부 반복, Stream은 내부 반복이라고 했는데 이 둘은 또 뭘까?</p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/a5284351-865d-4c27-86d5-dfcdedcb5802/image.png" alt=""></p>
<p><strong>내부 반복</strong>은 작업을 병렬 처리하면서 최적화된 순서로 처리해주므로 성능면에서는 내부 반복이 비교적 좋다고 볼 수 있다. 
<strong>외부 반복</strong>은 명시적으로 컬렉션 항목을 하나씩 가져와서 처리해야 하기 때문에 최적화가 불리하다.
즉, Collection에서 병렬성을 이용하려면 직접 <code>Synchronized</code>를 통해 관리해야만 한다.</p>
<hr>
<h2 id="✅stream-연산">✅Stream 연산</h2>
<p>스트림은 연산 과정이 <code>중간</code>과 <code>최종</code>으로 나누어진다.</p>
<ul>
<li><code>filter, map, limit</code>등 파이프라이닝이 가능한 연산을 <strong>중간 연산</strong></li>
<li><code>count, collect</code> 등 스트림을 닫는 연산을 <strong>최종 연산</strong></li>
</ul>
<p><span style="color:red"><strong>중간 연산에서는 호출해도 실제 연산을 수행하지 않고 파이프 라인만을 구성해두며, 최종 연산이 호출될때 비로소 중간 연산에서 만들어 둔 파이프 라인이 실행되어 실제로 연산이 수행된다.</strong></span></p>
<p>예시로, Item 중에 가격이 1000이상인 item 이름을 5개 선택하는 Stream 연산을 구현해보자.</p>
<pre><code class="language-java">List&lt;String&gt; items = item.stream()
                .filter(d-&gt;d.getPrices()&gt;=1000)
                          .map(d-&gt;d.getName())
                          .limit(5)
                          .collect(tpList());</code></pre>
<p>위에서 <code>filter</code>와 <code>map</code>은 다른 연산이지만, 한 과정으로 병합된다.
만약 Collection이였다면 </p>
<ul>
<li>가격이 1000이상인 아이템을 찾고,</li>
<li>이름을 따로 저장한뒤 </li>
<li>5개를 선택해야 한다. </li>
</ul>
<p>연산 최적화와 가독성면에서 Collection보다 Stream이 더 좋다.</p>
<hr>
<h3 id="📌stream-중간-연산">📌Stream 중간 연산</h3>
<ul>
<li><code>filter(Predicate)</code> : Predicate를 인자로 받아 true인 요소를 포함한 스트림 반환</li>
<li><code>disctinct()</code> : 중복 필터링</li>
<li><code>limit(n)</code> : 주어진 사이즈 이하 크기를 갖는 스트림 반환</li>
<li><code>skip(n)</code> : 처음 요소 n개 제외한 스트림 반환</li>
<li><code>map(Function)</code> : 매핑 함수의 result로 구성된 스트림 반환</li>
<li><code>flatMap()</code> : 스트림의 콘텐츠로 매핑함. map과 달리 평면화된 스트림 반환</li>
</ul>
<blockquote>
<p><strong>중간 연산은 모두 스트림을 반환한다.</strong></p>
</blockquote>
<h3 id="📌stream-최종-연산">📌Stream 최종 연산</h3>
<ul>
<li><code>(boolean) allMatch(Predicate)</code> : 모든 스트림 요소가 Predicate와 일치하는지 검사</li>
<li><code>(boolean) anyMatch(Predicate)</code> : 하나라도 일치하는 요소가 있는지 검사</li>
<li><code>(boolean) noneMatch(Predicate)</code> : 매치되는 요소가 없는지 검사</li>
<li><code>(Optional) findAny()</code> : 현재 스트림에서 임의의 요소 반환</li>
<li><code>(Optional) findFirst()</code> : 스트림의 첫번째 요소</li>
<li><code>reduce()</code> : 모든 스트림 요소를 처리해 값을 도출. 두 개의 인자를 가짐</li>
<li><code>collect()</code> : 스트림을 reduce하여 list, map, 정수 형식 컬렉션을 만듬</li>
<li><code>(void) forEach()</code> : 스트림 각 요소를 소비하며 람다 적용</li>
<li><code>(Long) count</code> : 스트림 요소 개수 반환</li>
</ul>
<hr>
<h2 id="✅지연-평가lazy-evaluation">✅지연 평가(Lazy evaluation)</h2>
<p><strong>지연 평가(Lazy evaluation)는 필요해지기 전까지 계산을 미루는 전략</strong>이다.
값이나 연산을 바로 계산하지 않고, 실제로 그 값이 요구되는 시점에 계산을 수행하여 불필요한 계산을 줄이고 메모리/시간을 아낄 수 있다는 장점이 있다.
*<em>Stream의 중간 연산은 바로 실행 되지 않고 대기하다가 최종 연산이 호출되는 순간 실제 연산이 수행된다. *</em></p>
<p>지연 평가가 중요한 이유는 아래와 같다.</p>
<h4 id="1️⃣불필요한-연산-회피">1️⃣불필요한 연산 회피</h4>
<p><code>findFirst</code>, <code>anyMatch</code>, <code>limit</code> 같은 단축 연산은 조건이 만족되면 즉시 파이프라인을 종료하므로 시간이 절약 된다.</p>
<h4 id="2️⃣파이프라이닝">2️⃣파이프라이닝</h4>
<p>파이프라인은 중간 연산들을 결합해 요소 하나를 끝까지 처리한 뒤 다음 요소로 넘어간다. 즉, <strong>중간 결과 컬렉션을 만들지 않기 때문에 메모리 사용과 GC 압박이 감소</strong>된다.</p>
<h4 id="3️⃣성능-최적화">3️⃣성능 최적화</h4>
<p><strong>스트림은 연산 순서와 조합에 따라 처리량이 크게 달라진다.</strong> 예를 들어, <code>filter</code>로 먼저 요소를 걸러 필요한 대상만 남기면, 그 뒤에 오는 <code>map</code>과 같이 무거운 연산의 호출 횟수가 줄어들어 전체 성능이 좋아진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 가비지 컬렉션(Garbage Collection, GC)이란?]]></title>
            <link>https://velog.io/@dev_ssj/Java-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98Garbage-Collection-GC%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@dev_ssj/Java-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98Garbage-Collection-GC%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sat, 27 Sep 2025 17:40:06 GMT</pubDate>
            <description><![CDATA[<h1 id="🤔가비지-컬렉션garbage-collection-gc이란">🤔가비지 컬렉션(Garbage Collection, GC)이란?</h1>
<p><strong>가비지 컬렉션</strong>은 자바의 메모리 관리 방법 중 하나로, <strong>JVM의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된(사용하지 않는) 메모리 객체를 모아 주기적으로 제거하는 프로세스</strong>를 뜻한다.</p>
<p>C/C++은 이러한 가비지 컬렉션이 없어 프로그래머가 수동으로 메모리 할당과 해제를 일일이 해줘야 한다. Java는 개발자 입장에서 메모리 관리와 메모리 누수 문제에 대해 신경쓰지 않아도 되어 좀더 수월하게 개발 할 수 있다.</p>
<p>하지만, 이런 가비지 컬렉션에도 단점이 있는데, 이런 단점을 <span style="color:red"><strong>Stop-The-World</strong></span>라고 한다.
자동으로 처리해준다 해도 <strong>메모리가 언제 해제되는지 정확하게 알수 없어 제어하기 힘들며, 가비지 컬렉션이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점</strong>이 있다.
이로 인해 GC가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 한다.</p>
<hr>
<h2 id="✅가비지-컬렉션의-대상">✅가비지 컬렉션의 대상</h2>
<p>일반적으로 아래와 같은 경우에 GC의 대상이 된다.</p>
<ol>
<li>객체가 NULL인 경우 (String str = null)</li>
<li>블럭 실행 종료 후, 블럭 안에서 생성된 객체</li>
<li>부모 객체가 NULL인 경우, 포함하는 자식 객체
GC는 <code>Weak Generational Hypothesis</code> 에 기반하는데, 이게 무엇인지 알아보기 전에 GC의 메모리 해제 과정에 대해 살펴보자.</li>
</ol>
<hr>
<h2 id="✅gc의-메모리-해제-과정">✅GC의 메모리 해제 과정</h2>
<p>GC가 메모리를 해제할 때는 <strong>Mark And Sweep</strong>이란 알고리즘을 사용한다.
<img src="https://velog.velcdn.com/images/dev_ssj/post/01667cd5-df7d-4140-8430-66aae1b94d8d/image.png" alt="">
가비지 컬렉션은 대상 객체를 <span style="color:red"><strong>식별(Mark)</strong></span>하고, <span style="color:red"><strong>제거(Sweep)</strong></span>하며 객체가 제거되어 파편화된 <span style="color:red"><strong>메모리 영역을 앞에서부터 채워나가는 작업(Compaction)</strong></span>을 수행하게 된다.</p>
<h4 id="1️⃣-mark">1️⃣ Mark</h4>
<ul>
<li><strong>Root Space</strong>로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 어떤 객체를 참조하고 있는지 찾아서 마킹한다.</li>
<li>참조하고 있는 객체가 없는 객체가 바로 GC의 대상인 사용하지 않는 객체이다.</li>
</ul>
<h4 id="2️⃣sweep">2️⃣Sweep</h4>
<ul>
<li>Mark 과정에서 찾은 참조하고 있지 않은 객체들 즉, <strong>사용하지 않는 객체를 Heap에서 제거</strong>한다.</li>
</ul>
<h4 id="3️⃣compact">3️⃣Compact</h4>
<ul>
<li>Sweep 후에 남아있는 객체(사용중인 객체들)을 Heap의 시작 주소로 모아 묶는다.</li>
<li>이들을 묶음으로써 공간이 생기므로 새로운 메모리 할당 시에 더 쉽고 빠르게 진행이 가능하다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/370834ef-2677-49d4-808c-12e91a5efa00/image.gif" alt=""></p>
<hr>
<h2 id="✅weak-generational-hypothesis">✅Weak Generational Hypothesis</h2>
<p>JVM의 Heap영역은 처음 설계될 때 다음과 같은 2가지 전제로 설계되었다.</p>
<ul>
<li>대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.</li>
<li>오래된 객체에서 새로운 객체로의 참조는 매우 적게 존재한다.</li>
</ul>
<p>즉, <strong>객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우는 드물다는 것</strong>이다.</p>
<p>이 가설에 기반하여 Java는 객체의 생존기간에 따라 물리적인 Heap영역을 <strong>Young영역</strong>과 <strong>Old 영역</strong>으로 나누게 되었다. <strong>신규로 생성되는 객체는 Young 영역</strong>에, <strong>오랫동안 살아남은 객체는 Old 영역</strong>에 보관한다.</p>
<hr>
<h1 id="💡generational-garbage-collection">💡Generational Garbage Collection</h1>
<h2 id="✅generational-garbage-collection의-구조">✅Generational Garbage Collection의 구조</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/ef348aeb-f663-414a-98c2-306a09d21a4b/image.png" alt=""></p>
<h3 id="young-영역">Young 영역</h3>
<ul>
<li><strong>새롭게 생성되는 객체가 할당되는 영역</strong></li>
<li>대부분의 객체가 금방 Unreachable상태가 되기 때문에 많은 객체가 Young 영역에 생성되었다가 사라진다.</li>
<li>Young 영역에 대한 가비지 컬렉션을 <span style="color:red"><strong>Minor GC</strong></span>라고 부른다.</li>
</ul>
<h3 id="old-영역">Old 영역</h3>
<ul>
<li><strong>Young 영역에서 살아남은 객체가 복사되는 영역</strong></li>
<li>Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.</li>
<li>Old 영역에 대한 가비지 컬렉션을 <span style="color:red"><strong>Major GC 또는 Full GC</strong></span>라고 부른다.</li>
</ul>
<blockquote>
<p>또 다시 힙 영역은 더욱 효율적인 GC를 위해 Young 영역을 3가지 영역으로 나눈다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/9b1d0c78-2040-40e8-a908-5cc967c2adb5/image.png" alt=""></p>
<h3 id="eden">Eden</h3>
<ul>
<li><strong>new를 통해 새로 생성된 객체</strong>가 위치</li>
<li>장기적인 쓰레기 수집 후 살아남은 객체들은 Survivor영역으로 보냄</li>
</ul>
<h3 id="survivor-0--survivor-1">Survivor 0 / Survivor 1</h3>
<ul>
<li><strong>최소 1번의 GC 이상 살아남은 객체가 존재하는 영역</strong></li>
<li>Survivor 0 또는 Survivor 1 둘중 하나는 꼭 비어 있어야 한다는 규칙이 존재.</li>
</ul>
<hr>
<h2 id="✅minor-gc-과정">✅Minor GC 과정</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/6dca5320-d748-43bc-aa99-073cd54e0649/image.png" alt=""></p>
<ul>
<li><strong>Yong Generaion 영역은 짧게 살아남는 메모리들이 존재하는 공간</strong>이다.</li>
<li>모든 객체는 처음에 Young Generation에 생성된다.</li>
<li>Old Generation에 비해 공간이 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는데 적은 시간이 걸린다.</li>
</ul>
<h3 id="1-처음-생성된-객체는-young-generation-영역의-일부인-eden-영역에-위치">1. 처음 생성된 객체는 Young Generation 영역의 일부인 Eden 영역에 위치</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/8fd82b8a-d832-409e-a40c-b577dc85b21b/image.png" alt=""></p>
<h3 id="2-객체가-계속-생성되어-eden-영역이-꽉차게-되면-minor-gc가-발생">2. 객체가 계속 생성되어 Eden 영역이 꽉차게 되면 Minor GC가 발생</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/13ab9ef3-5237-4e6a-b61c-9e7c7ebf57c8/image.png" alt=""></p>
<h3 id="3-mark-동작을-통해-참조되는-객체-탐색">3. Mark 동작을 통해 참조되는 객체 탐색</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/830d771c-22dc-496a-80db-788938416b29/image.png" alt=""></p>
<h3 id="4-eden-영역에서-살아남은-객체는-survivor-영역으로-이동">4. Eden 영역에서 살아남은 객체는 Survivor 영역으로 이동</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/52ca34ad-c8a8-4fe3-8829-2f69a9cfd101/image.png" alt=""></p>
<h3 id="5-eden영역에서-사용되지-않은-객체의-메모리를-해제sweep">5. Eden영역에서 사용되지 않은 객체의 메모리를 해제(sweep)</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/2640cbd3-7196-4d39-a576-227ca21d5b49/image.png" alt=""></p>
<h3 id="6-살아남은-모든-객체들의-age-값이-1씩-증가">6. 살아남은 모든 객체들의 age 값이 1씩 증가</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/99956102-6a1c-413b-bbc5-c720bfc3fbc1/image.png" alt=""></p>
<h3 id="7-또다시-eden-영역에-신규-객체들로-가득차게-되면-다시-minor-gc가-발생되고-mark-실행">7. 또다시 Eden 영역에 신규 객체들로 가득차게 되면 다시 Minor GC가 발생되고 mark 실행</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/36767861-4641-4e33-8fe9-e1de3de8b92b/image.png" alt=""></p>
<h3 id="8-marking한-객체들을-비어있는-survivor-1다른-survivor-영역으로-이동하고-sweep">8. marking한 객체들을 비어있는 Survivor 1(다른 Survivor 영역)으로 이동하고 sweep</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/8d2bf256-a410-4fd8-9369-7e1abbd5f964/image.png" alt=""></p>
<h3 id="9-다시-살아남은-모든-객체들의-age-1씩-증가">9. 다시 살아남은 모든 객체들의 age 1씩 증가</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/8d285772-bd59-4345-af47-eb3f86818c7f/image.png" alt=""></p>
<h3 id="10-이-모든-과정을-반복">10. 이 모든 과정을 반복</h3>
<hr>
<h2 id="✅major-gc-과정">✅Major GC 과정</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/227258a9-480b-4e90-b286-2b41333bf6c0/image.png" alt=""></p>
<ul>
<li><strong>Old Generation은 길게 살아남는 메모리들이 존재하는 공간</strong></li>
<li>Old Generation의 객체들은 거슬러 올라가면 처음에는 Young Generation에 의해 시작되었으나, GC 과정 중에 제거되지 않은 경우 age 임계값이 차게되어 이동된 객체들이다.</li>
<li>Major GC는 객체들이 계속 Promotion되어 <strong>Old 영역의 메모리가 부족해지면 발생</strong>하게 된다.</li>
</ul>
<h3 id="1-객체의-age가-임계값예시는-8에-도달하게-되면">1. 객체의 age가 임계값(예시는 8)에 도달하게 되면</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/7496c072-df55-4c9a-8852-7cef12ec7713/image.png" alt=""></p>
<h3 id="2-이-객체들은-old-generation으로-이동된다promotion">2. 이 객체들은 Old Generation으로 이동된다.(promotion)</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/1c13c66e-7a1e-4797-b77b-1125bd70cac3/image.png" alt=""></p>
<h3 id="3-위-과정이-반복되어-old-generaion-영역이-가득-차게되면-major-gc가-발생한다">3. 위 과정이 반복되어 Old Generaion 영역이 가득 차게되면 Major GC가 발생한다.</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/b7ec9cc2-fb98-4887-b718-2b63a05645b1/image.png" alt=""></p>
<p>Major GC는 Old Generation 영역이 가득 찼을 때 실행된다. 이때 GC는 Old Generation에 존재하는 객체들을 검사해 <strong>참조되지 않는 객체들을 한꺼번에 정리</strong>한다.</p>
<p>Old Generation은 Young Generation보다 공간이 훨씬 크기 때문에, Major GC가 수행될 때는 더 많은 객체를 스캔하고 해제해야 한다. 그 결과 GC 처리 시간이 길어질 수 있다.</p>
<p>이때, <span style="color:red"><strong>Stop-The-World</strong></span> 문제가 발생하게 된다.
Major GC가 실행되는 동안에는 애플리케이션 스레드가 일시 중지되며, GC 작업이 완료될 때까지 모든 요청 처리가 멈추게 되는데, 이로 인해 지연이나 성능 저하가 발생할 수 있으며, 이를 흔히 <strong>GC 오버헤드 문제</strong>라고 부른다.</p>
<p>따라서 이런 문제를 해결하고 최적화하기 위해 다양한 가비지 컬렉션 알고리즘이 개발되었다.</p>
<hr>
<h2 id="가비지-컬렉션-알고리즘-종류">가비지 컬렉션 알고리즘 종류</h2>
<h3 id="serial-gc">Serial GC</h3>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/1cc6ff2e-f3fb-412f-9206-57ec72090adc/image.png" alt=""></p>
<ul>
<li>서버의 CPU 코어가 1개 일때 사용하기 위해 개발된 가장 단순한 GC</li>
<li>GC를 처리하는 스레드가 1개여서 가장 Stop-The-World의 시간이 가장 길다</li>
<li>**보통 실무에서는 사용하지 않는다.</li>
<li>*</li>
</ul>
<hr>
<h3 id="parallel-gc">Parallel GC</h3>
<ul>
<li>Java 8의 디폴트 GC</li>
<li>Serial GC와 기본적인 알고리즘은 같지만, Young 영역의 Minor GC를 멀티쓰레드로 수행(Old 영역은 여전히 싱글 스레드)
<img src="https://velog.velcdn.com/images/dev_ssj/post/541c5042-1da1-4d53-8d62-3cec1f43c595/image.png" alt=""></li>
</ul>
<hr>
<h3 id="parallel-old-gc">Parallel Old GC</h3>
<ul>
<li>Parallel GC를 개선한 버전</li>
<li>Young 영역 뿐만 아니라 Old 영역에서도 멀티스레드로 GC 수행</li>
<li>새로운 가비지 컬렉션 청소 방식인 Mark-Summary-Compact 방식을 이용</li>
</ul>
<hr>
<h3 id="cms-gc">CMS GC</h3>
<ul>
<li>어플리케이션의 스레드와 GC 스레드가 동시에 실행되어 Stop-The-World 시간을 최대한 줄이기 위해 고안된 GC</li>
<li>GC 과정이 매우 복잡하며 메모리 파편화 문제 존재</li>
<li>GC 대상을 파악하는 과정이 복잡한 여러단계로 수행되기 때문에 다른 GC 대비 CPU 사용량이 높음</li>
<li><strong>Java14에서부터 사용이 중지</strong></li>
</ul>
<hr>
<h3 id="g1-gc">G1 GC</h3>
<ul>
<li>CMS GC를 대체하기 위해 나온 GC</li>
<li>Java 9+ 버전의 디폴트 GC로 지정</li>
<li>기존의 GC 알고리즘에서는 Heap 영역을 물리적으로 고정된 Young / Old 영역으로 나누어 사용하였지만, G1 gc는 아예 이러한 개념을 뒤엎는 Region이라는 개념을 새로 도입하여 사용.전체 Heap 영역을 Region이라는 영역으로 체스같이 분할하여 상황에 따라 Eden, Survivor, Old 등 역할을 고정이 아닌 동적으로 부여
<img src="https://velog.velcdn.com/images/dev_ssj/post/20feb1f9-17ea-40af-8179-4d3ca59067a0/image.png" alt=""></li>
</ul>
<hr>
<h3 id="shenandoah-gc">Shenandoah GC</h3>
<ul>
<li>Java 12에 release</li>
<li>레드햇에서 개발한 GC</li>
<li>기존 CMS가 가진 단편화, G1이 가진 pause의 이슈를 해결</li>
<li>강력한 Concurrency와 가벼운 GC 로직으로 heap 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요가 특징
<img src="https://velog.velcdn.com/images/dev_ssj/post/2f36f585-a5e0-4145-91ca-b27796df2aa5/image.png" alt=""></li>
</ul>
<hr>
<h3 id="zgc">ZGC</h3>
<ul>
<li>Java 15에 release</li>
<li>G1의 Region 처럼,  ZGC는 ZPage라는 영역을 사용하며, G1의 Region은 크기가 고정인데 비해, ZPage는 2mb 배수로 동적으로 운영됨. (큰 객체가 들어오면 2^ 로 영역을 구성해서 처리)</li>
<li>ZGC가 내세우는 최대 장점 중 하나는 힙 크기가 증가하더도 <strong>&#39;stop-the-world&#39;의 시간이 절대 10ms를 넘지 않는다는 것</strong>
<img src="https://velog.velcdn.com/images/dev_ssj/post/0546894e-bf51-4ff1-8027-c0dc3e835cde/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] JVM 구조 이해하기]]></title>
            <link>https://velog.io/@dev_ssj/Java-JVM-%EA%B5%AC%EC%A1%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-7baepb5e</link>
            <guid>https://velog.io/@dev_ssj/Java-JVM-%EA%B5%AC%EC%A1%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-7baepb5e</guid>
            <pubDate>Fri, 26 Sep 2025 14:04:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이전에 JVM, JRE, JDK의 개념에 대해서 알아보는 시간을 가졌다.이번엔  좀 더 자세히 JVM의 구조에 대해서 알아보도록 하자. 
<a href="https://velog.io/@dev_ssj/JAVA-JDK-JRE-JVM%EC%9D%98-%EA%B0%9C%EB%85%90-%EC%B0%A8%EC%9D%B4">JDK, JRE, JVM의 개념</a></p>
</blockquote>
<h1 id="🧐jvm이-뭐였지">🧐JVM이 뭐였지?</h1>
<p>JVM은 자바 가상 머신으로, <strong>자바 바이트 코드를 각 운영체제에 맞는 기계로 바꾸어 실행하는 가상머신</strong>이다. 즉, 자바 바이트 코드(.class)를 해석하여 실행 시켜주는 머신인데, 대체 어떤 방법으로 자바 바이트 코드를 해석하고, 프로그램 실행까지 시키는지 알아보도록 하자.
<img src="https://velog.velcdn.com/images/dev_ssj/post/9cc3d8ef-9531-4f4b-bbad-456795f621b8/image.png" alt=""></p>
<hr>
<h1 id="✅jvm의-구성요소">✅JVM의 구성요소</h1>
<p><strong>JVM은 크게 5가지의 요소로 나누어진다.</strong>
<img src="https://velog.velcdn.com/images/dev_ssj/post/2523a519-fa8f-4647-bd20-752e82aea741/image.png" alt=""></p>
<h4 id="1-클래스-로더-class-loader">1. 클래스 로더 (Class Loader)</h4>
<h4 id="2-런타임-데이터-영역-runtime-data-area">2. 런타임 데이터 영역 (Runtime Data Area)</h4>
<h4 id="3-실행-엔진-execution-engine">3. 실행 엔진 (Execution Engine)</h4>
<h4 id="4-jni---네이티브-인터페이스-java-native-interface">4. JNI - 네이티브 인터페이스 (Java Native Interface)</h4>
<h4 id="5-네이티브-메서드-라이브러리-native-method-libraries">5. 네이티브 메서드 라이브러리 (Native Method Libraries)</h4>
<hr>
<h2 id="1️⃣클래스-로더class-loader">1️⃣클래스 로더(Class Loader)</h2>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/f096e647-5828-446f-b21a-2f47a76de656/image.png" alt="">
<strong>클래스 로더</strong>는 JVM 안에서 <strong>클래스 파일(.class)을 동적으로 로드</strong>하고, J<strong>VM의 메모리 영역인 Runtime Data Area에 메모리를 할당하여 실행 가능한 상태로 만든다.</strong> 프로그램 실행 시 필요한 클래스만 동적으로 적재하여 메모리 사용을 최적화한다.
더 자세히 말하자면, 클래스 로더는 클래스의 로드(Load), 연결(Link), 초기화(Intialization) 과정을 통해 실행을 준비하게 된다.
즉, <strong>클래스 로더 클래스 파일을 가져와 메모리에 로드하고, 클래스에 문제가 없는지 검증하고 변수를 적절한 값으로 초기화 하는 과정을 담당한다.</strong></p>
<hr>
<h2 id="2️⃣런타임-데이터-영역runtime-data-area">2️⃣런타임 데이터 영역(Runtime Data Area)</h2>
<p>런타임 데이터 영역은 JVM이 실행하는 동안 데이터를 저장하는 메모리 영역이다.</p>
<h3 id="1-메서드-영역">1) 메서드 영역</h3>
<ul>
<li><strong>바이트 코드를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 공간</strong></li>
<li>정적 필드와 클래스 구조만을 가지고 있다.</li>
<li>JVM 메모리의 정적 영역으로, 모든 스레드가 공유하는 메모리 공간</li>
<li>JVM이 동작하고 <strong>클래스가 로드될 때 적재되어 프로그램이 종료될 때까지 저장</strong>됨</li>
</ul>
<h3 id="2-힙-영역">2) 힙 영역</h3>
<ul>
<li><strong>new 연산자로 생성되는 클래스, 인스턴스 변수, 배열 등 Reference Type이 저장되는 공간</strong></li>
<li><strong>가비지 컬렉터가 관리</strong></li>
<li>모든 스레드가 공유하는 영역</li>
</ul>
<h3 id="3-스택-영역">3) 스택 영역</h3>
<ul>
<li><strong>int, long, boolean 등 기본 자료형 생성 시 저장하는 공간</strong></li>
<li>각 스레드마다 독립적으로 할당되는 공간</li>
<li>메서드 호출시마다 프레임이 생성되어 지역 변수, 매개 변수 등이 저장됨</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/bb2005f8-781b-4f2c-8273-f2451cc75463/image.png" alt=""></p>
<h3 id="4-pcprogram-counter-레지스터">4) PC(Program Counter) 레지스터</h3>
<ul>
<li>각 스레드 별로 하나씩 존재</li>
<li><strong>현재 실행중인 JVM 명령의 주소를 저장하는 공간</strong></li>
</ul>
<h3 id="5-네이티브-메서드-스택">5) 네이티브 메서드 스택</h3>
<ul>
<li><strong>실제 실행할 수 있는 기계어로 작성된 프로그램을 실행 시키는 영역</strong></li>
<li>자바 의외의 네이티브 코드(C/C++) 실행 시 사용되는 메모리 영역이기도 함</li>
</ul>
<hr>
<h2 id="3️⃣실행-엔진execution-engine">3️⃣실행 엔진(Execution Engine)</h2>
<p>실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행한다. 자바 바이트 코드(.class)는 가상 머신이 이해할 수 있는 중간 레벨로 컴파일된 코드이다. 그래서 <strong>실행 엔진은 바이트 코드를 JVM내부에서 실제로 기계가 실행할 수 있는 형태로 바꿔준다.</strong>
이 때, 바이트 코드를 실행 시키는 방법은 크게 두가지로 분류 할 수 있다.</p>
<h3 id="1-인터프리터interpreter">1) 인터프리터(Interpreter)</h3>
<ul>
<li><strong>바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행</strong></li>
<li>JVM안에서 바이트 코드는 기본적으로 인터프리터 방식으로 동작</li>
<li>같은 메소드라도 여러번 호출이 된다면 매번 해석하고 수행해야 되므로 *<em>전체적인 속도는 느림 *</em></li>
</ul>
<h3 id="2-jit-컴파일러just-in-time-compiler">2) JIT 컴파일러(Just-In-Time Compiler)</h3>
<ul>
<li>인터프리터의 단점(속도, 효율)을 보완하기 위해 도입한 방식</li>
<li><strong>반복되는 코드를 런타임 중에 기계어로 컴파일하고 재사용하는 방식</strong></li>
<li>한번 기계어로 컴파일하여 캐싱 한 코드를 실행하므로 속도가 비교적 빠름</li>
</ul>
<blockquote>
<p>두 방식의 장단점이 있기 때문에 어떤 방식을 사용할지는 <code>컴파일 임계치</code>라는것을 계산하여 결정한다. 메서드가 호출된 횟수와 메서드가 종료된 횟수를 통해 컴파일 임계치를 계산하고, 이를 초과하면 JIT 방식으로 기계어를 캐싱하게 된다.</p>
</blockquote>
<h3 id="3-가비지-컬렉터">3) 가비지 컬렉터</h3>
<p>실행엔진에는** 가비지 컬렉터<strong>도 포함되어 있다.
**가비지 컬렉터는 자동으로 메모리를 관리해주는 JVM 기능으로, Heap 메모리 영역에서 더 이상 사용하지 않는 메모리를 자동으로 회수해 준다.</strong>
C언어 같은 경우는 직접 개발자가 메모리를 해제해줘야 하지만, JAVA는 이 가비지 컬렉터를 이용하여 자동으로 메모리를 실시간 최적화 시켜준다.</p>
<hr>
<h2 id="4️⃣네이티브-인터페이스jni">4️⃣네이티브 인터페이스(JNI)</h2>
<p>JNI는 <strong>자바가 다른 언어로 만들어진 어플리케이션과 상호 작용할 수 있는 인터페이스를 제공하는 프로그램</strong>이다.
C, C++ 등으로 작성된 라이브러리를 실행할 때 주로 사용하며, JAVA를 통해 실행할 수 없는 하드웨어, OS의 기능에 접근할 수도 있다.</p>
<h2 id="네이티브-메서드-라이브러리native-method-libraries">네이티브 메서드 라이브러리(Native Method Libraries)</h2>
<p>네이티브 메서드 라이브러리는 C, C++로 작성된 라이브러리를 뜻한다.
만일 헤더가 필요하다면 JNI는 이 라이브러리를 로딩해 실행한다.</p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/776c0e33-f971-404d-9212-a2fb4ddcd6a4/image.png" alt=""></p>
<hr>
<h1 id="☑️다시보는-jvm-실행과정">☑️다시보는 JVM 실행과정</h1>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/843ecd67-390f-41e2-b626-9dfb2f824d6f/image.png" alt=""></p>
<h4 id="1-자바-프로그램을-실행하면-jvm은-os로부터-메모리를-할당받는다">1. 자바 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받는다.</h4>
<h4 id="2-자바-컴파일러javac가-자바-소스코드java를-자바-바이트-코드class로-컴파일-한다">2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트 코드(.class)로 컴파일 한다.</h4>
<h4 id="3-클래스-로더는-동적-로딩을-통해-필요한-클래스들을-로딩-및-링크하여-런타임-데이터-영역에-올린다">3. 클래스 로더는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역에 올린다.</h4>
<h4 id="4-런타임-데이터-영역에-로딩된-바이트-코드는-실행-엔진을-통해-해석된다">4. 런타임 데이터 영역에 로딩된 바이트 코드는 실행 엔진을 통해 해석된다.</h4>
<h4 id="5-이-과정에서-실행-엔진에-의해-가비지-컬렉터의-작동과-스레드-동기화가-이루어진다">5. 이 과정에서 실행 엔진에 의해 가비지 컬렉터의 작동과 스레드 동기화가 이루어진다.</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java] 큐 - 10845]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java-%ED%81%90-10845</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java-%ED%81%90-10845</guid>
            <pubDate>Mon, 22 Sep 2025 09:58:49 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/10845">[백준] 큐 - 10845</a></p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/f332a835-87ab-41a5-bcf6-79f34c0d06b9/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        //입력받을 문자열 갯수
        int n = Integer.parseInt(br.readLine());
        int last = -1;

        //큐 정의
        Queue&lt;Integer&gt; queue = new LinkedList&lt;&gt;();

        for(int i = 0; i&lt;n; i++){
            String line = br.readLine();
            if(line.startsWith(&quot;push&quot;)){
                //offer : 큐에서 주어진 값을 넣는 명령어
                int val = Integer.parseInt(line.substring(5).trim());
                queue.offer(val);
                last = val; //마지막 값 갱신
            }else if(line.equals(&quot;pop&quot;)){
                //poll : 큐에서 값 하나를 출력하고 그 값을 큐에서 제거
                sb.append(queue.isEmpty()? -1 : queue.poll()).append(&#39;\n&#39;);
            }else if(line.equals(&quot;size&quot;)){
                //size : 큐에 들어있는 데이터의 개수 출력
                sb.append(queue.size()).append(&#39;\n&#39;);
            }else if(line.equals(&quot;empty&quot;)){
                //isEmpty : 큐가 비어있는지 확인. 비어있으면 true
                sb.append(queue.isEmpty() ? 1 : 0).append(&#39;\n&#39;);
            }else if(line.equals(&quot;front&quot;)){
                //peek : 큐에서 값 하나 출력 제거는 안함.
                sb.append(queue.isEmpty()? -1 : queue.peek()).append(&#39;\n&#39;);
            }else if(line.equals(&quot;back&quot;)){
                sb.append(queue.isEmpty()? -1 : last).append(&#39;\n&#39;);
            }
        }
        System.out.println(sb.toString());
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/4bdda03c-1881-4e92-8447-2f0e9b65f7b6/image.png" alt=""></p>
<hr>
<h2 id="큐fifo">큐(FIFO)</h2>
<ul>
<li>offer() : 큐에 값을 넣는 메서드</li>
<li>poll() : 큐에서 값 하나를 출력하고 그 값을 큐에서 제거</li>
<li>peek() : 큐에서 값 하나를 출력. 큐에서 제거는 안함</li>
<li>size() : 큐에 들어있는 데이터 개수 출력</li>
<li>isEmpty() : 큐가 비어있는지 확인. 큐가 비어있으면 true, 비어있지 않으면 false</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준][Java]스택 - 10828]]></title>
            <link>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%8A%A4%ED%83%9D-10828</link>
            <guid>https://velog.io/@dev_ssj/%EB%B0%B1%EC%A4%80Java%EC%8A%A4%ED%83%9D-10828</guid>
            <pubDate>Sun, 21 Sep 2025 13:55:18 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/10828">[백준]스택 - 10828</a></p>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/4b1f9218-8997-442b-9c01-d79df0083e11/image.png" alt=""></p>
<hr>
<h1 id="✅나의-문제-풀이">✅나의 문제 풀이</h1>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder out = new StringBuilder();

        //입력받을 문자열 갯수
        int n = Integer.parseInt(br.readLine());
        Stack&lt;Integer&gt; stack = new Stack&lt;&gt;();   //스택 정의

        for(int i=0;i&lt;n; i++){
            String line = br.readLine();    //명령어 입력받음
            if(line.startsWith(&quot;push&quot;)){
                //명령어가 push로 시작하면 push 이후 뒷부분(6번째 글자부터)자르고, 그 값을 stack에 넣는다
                stack.push(Integer.parseInt(line.substring(5).trim()));
            }else if(line.equals(&quot;pop&quot;)){
                //stack.empty() : 스택이 비었으면 true, 아니면 false
                //스택이 비어있으면 -1 + 개행, 비어있지않으면 pop()으로 맨 위 원소 꺼내고
                //StringBuilder에 결과와 개행을 붙여서 버퍼링(나중에 한번에 출력)
                out.append(stack.empty()? -1 : stack.pop()).append(&#39;\n&#39;);
            }else if(line.equals(&quot;size&quot;)){
                out.append(stack.size()).append(&#39;\n&#39;);
            }else if(line.equals(&quot;empty&quot;)){
                out.append(stack.empty()? 1 : 0).append(&#39;\n&#39;);
            }else if(line.equals(&quot;top&quot;)){
                out.append(stack.empty()? -1 : stack.peek()).append(&#39;\n&#39;);
            }
        }
        System.out.println(out.toString());
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/496770c4-4695-48ae-97a5-660893526a01/image.png" alt=""></p>
<ul>
<li>먼저 스택을 정의해주고, 입력받을 문자열 개수만큼 for문을 돌린다.</li>
<li>for문안에 if문을 이용해서 명령어를 구분하고, 그에 맞게 명령어를 처리한다.</li>
<li>특히 push는 <code>push 5</code>와 같이 push 이후에 정수로 스택에 넣을 값을 지정해주기 때문에, startsWith() 메서드를 이용하여 명령어가 push로 시작하는지 판별하고, push로 시작한다면 substring으로 push 이후의 글자를 자른다.</li>
</ul>
<hr>
<h2 id="스택">스택</h2>
<ul>
<li>push() : 스택에 데이터를 추가하는 메서드</li>
<li>pop() : 스택에 들어있는 데이터를 삭제하는 메서드</li>
<li>peek() : 데이터는 삭제하지 않고 스택 맨 위에 있는 값을 확인하는 메서드</li>
<li>size() : 현재 스택에 저장되어 있는 요소의 개수를 알려주는 메서드</li>
<li>empty() : 스택이 비어있는지, 데이터가 들어있는지 확인하는 메서드(true/false)</li>
</ul>
<h2 id="문자열-입력">문자열 입력</h2>
<ul>
<li>문자열 입력은 <code>Scanner</code>가 아니라 <code>BufferedReader</code>를 사용했다.</li>
<li><code>BufferdReader</code>는 문자를 버퍼에 모아서 한꺼번에 읽는 자바의 입력 클래스이다.</li>
<li>보통 <code>BufferdReader</code>로 문자열을 입력받고, <code>StringBuilder</code>로 출력을 모은다.</li>
</ul>
<h3 id="기본-구성">기본 구성</h3>
<pre><code class="language-java">BufferedReader br =
    new BufferedReader(new InputStreamReader(System.in)); // 표준 입력

String line = br.readLine();    //한 줄 읽기</code></pre>
<h3 id="자주-쓰는-패턴">자주 쓰는 패턴</h3>
<pre><code class="language-java">BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder out = new StringBuilder();

int n = Integer.parseInt(br.readLine());
for (int i = 0; i &lt; n; i++) {
    String line = br.readLine();
    // 필요 시:
    // StringTokenizer st = new StringTokenizer(line);
    // String[] parts = line.split(&quot;\\s+&quot;);
    out.append(line).append(&#39;\n&#39;); // 결과 모으기
}
System.out.print(out.toString());  // 한 번에 출력</code></pre>
<h3 id="scanner-vs-bufferedreader">Scanner vs BufferedReader</h3>
<ul>
<li>Scanner : 사용 간단하지만 속도가 느림.</li>
<li>BufferedReader : 라인 단위로 읽고 직접 파싱하며, Scanner보다 빠르다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Java 버전 별 차이(java9, java11, java17, java21)]]></title>
            <link>https://velog.io/@dev_ssj/Java-Java-%EB%B2%84%EC%A0%84-%EB%B3%84-%EC%B0%A8%EC%9D%B4java9-java11-java17-java21</link>
            <guid>https://velog.io/@dev_ssj/Java-Java-%EB%B2%84%EC%A0%84-%EB%B3%84-%EC%B0%A8%EC%9D%B4java9-java11-java17-java21</guid>
            <pubDate>Sun, 21 Sep 2025 10:41:33 GMT</pubDate>
            <description><![CDATA[<h1 id="💡java-lts-버전">💡Java LTS 버전</h1>
<p><img src="https://velog.velcdn.com/images/dev_ssj/post/36c18f29-cf93-4fc4-afbd-4c899752aa41/image.png" alt=""></p>
<p>자바는 3년정도의 텀으로 <strong>LTS(Long Term Support) 릴리스</strong>가 제공된다.
LTS란 <code>Long Term Support</code>의 약자로 장기 지원 버전을 의미한다. 자바의 LTS 버전은 특정 기간 동안 안정적인 지원과 업데이트를 제공받을 수 있다. 일반적으로 3년마다 출시되며, 출시 후 5년동안 기술 지원이 제공된다.</p>
<hr>
<h2 id="✅lts버전을-사용해야-하는-이유는">✅LTS버전을 사용해야 하는 이유는?</h2>
<ol>
<li><p>장기적인 지원
Long Term Support란 이름과 같이 장기적으로 보안 업데이트와 버그 수정을 지원해준다</p>
</li>
<li><p>안정성
광범위하게 테스트되고 안정화된 후 릴리스되므로 높은 신뢰성을 갖고 있다.</p>
</li>
<li><p>기업 환경에 적합
많은 기업에서 규제 요구 사항을 충족하기 위해 장기적으로 지원되는 소프트웨어를 사용해야 한다</p>
</li>
<li><p>커뮤니티 및 생태계 지원
LTS 버전은 많은 개발자와 기업에서 사용되므로 강력한 커뮤니티와 생태계를 갖고있다. 
또한, 주요 라이브러리와 프레임 워크가 LTS 버전을 우선적으로 지원하기 때문에 호환성이 높다.</p>
</li>
</ol>
<hr>
<h1 id="💡자바-버전별-특징">💡자바 버전별 특징</h1>
<h2 id="📌java-8">📌Java 8</h2>
<blockquote>
<p>Java 8의 변경점으로는 크게 람다 표현식과 함수형 프로그래밍 지원이 있다. 이로 인해 코드의 가독성이 높아지고 간결한 코드를 작성할 수 있게 되었다.</p>
</blockquote>
<h3 id="1️⃣람다-표현식lambda-expressions">1️⃣람다 표현식(Lambda Expressions)</h3>
<ul>
<li>함수형 프로그래밍을 지원하여 코드의 간결성과 가독성을 높임<pre><code class="language-java">Arrays.sort(strArray, (String s1, String s2) -&gt; s2.length() - s1.length());</code></pre>
</li>
</ul>
<h3 id="2️⃣디폴트-메서드default-methods">2️⃣디폴트 메서드(Default Methods)</h3>
<ul>
<li>인터페이스에 기본 메서드를 정의하여 기존 코드를 깨지 않고 기능을 추가할 수 있다.<pre><code class="language-java">default public Stream&lt;T&gt; stream() {
return StreamSupport.stream(spliterator(), false);
}</code></pre>
</li>
</ul>
<h3 id="3️⃣스트림-api-stream-api">3️⃣스트림 API (Stream API)</h3>
<ul>
<li>컬렉션 데이터의 반복, 필터링, 매핑 등을 간단하게 처리할 수 있는 API 도입<pre><code class="language-java">List&lt;String&gt; filtered = list.stream()
.filter(s -&gt; s.startsWith(&quot;a&quot;))
.collect(Collectors.toList());</code></pre>
</li>
</ul>
<h3 id="4️⃣optional-클래스">4️⃣Optional 클래스</h3>
<ul>
<li>null 참조를 다루기 위한 컨테이너 클래스로, 보다 안전한 코드를 작성할 수 있다.</li>
<li><a href="https://velog.io/@dev_ssj/Java-Optional%EC%9D%B4%EB%9E%80">참고</a><pre><code class="language-java">Optional&lt;String&gt; optional = Optional.of(&quot;Hello&quot;);
optional.ifPresent(System.out::println);</code></pre>
</li>
</ul>
<hr>
<h2 id="📌java-11">📌Java 11</h2>
<h3 id="1️⃣지역-변수-유형-추론var-키워드">1️⃣지역 변수 유형 추론(var 키워드)</h3>
<ul>
<li>변수 선언 시 타입을 명시하지 않아도 컴파일러가 자동으로 타입을 추론해주는 기능 도입<pre><code class="language-java">var list = new ArrayList&lt;String&gt;();
list.add(&quot;Hello, Java 11&quot;);</code></pre>
</li>
</ul>
<h3 id="2️⃣새로운-http-클라이언트-api">2️⃣새로운 HTTP 클라이언트 API</h3>
<ul>
<li><code>java.net.http</code> 패키지에 새로운 HTTP 클라이언트를 도입하여 HTTP/2 및 WebSocket을 지원<pre><code class="language-java">HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(&quot;https://example.com&quot;))
.build();
HttpResponse&lt;String&gt; response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body())</code></pre>
</li>
</ul>
<hr>
<h2 id="📌java-17">📌Java 17</h2>
<h3 id="1️⃣패턴-매칭-for-switch">1️⃣패턴 매칭 for switch</h3>
<ul>
<li>switch 문에서 패턴 매칭을 사용하여 코드의 가독성을 높임<pre><code class="language-java">switch (obj) {
case Integer i -&gt; System.out.println(&quot;Integer: &quot; + i);
case String s -&gt; System.out.println(&quot;String: &quot; + s);
default -&gt; System.out.println(&quot;Unknown type&quot;);
}</code></pre>
</li>
</ul>
<h3 id="2️⃣sealed-classes">2️⃣Sealed Classes</h3>
<ul>
<li>상속 구조를 제한하여 더 안전하고 예측 가능한 클래스 계층을 설계할 수 있게 함<pre><code class="language-java">public abstract sealed class Shape permits Circle, Square {}</code></pre>
</li>
</ul>
<h3 id="3️⃣record-patterns">3️⃣Record Patterns</h3>
<ul>
<li>간단하게 불변 데이터 클래스를 정의할 수 있는 기능 정식 도입<pre><code class="language-java">public record Point(int x, int y) {}</code></pre>
</li>
</ul>
<hr>
<h2 id="📌java-21">📌Java 21</h2>
<h3 id="1️⃣virtual-threads">1️⃣Virtual Threads</h3>
<ul>
<li>고성능 동시성을 제공하는 새로운 경량 스레드 모델 도입<pre><code class="language-java">Thread.startVirtualThread(() -&gt; {
System.out.println(&quot;Hello from a virtual thread&quot;);
});</code></pre>
</li>
</ul>
<h3 id="2️⃣record-patterns">2️⃣Record Patterns</h3>
<ul>
<li>레코드 패턴을 활용한 더 나은 데이터 추출 및 처리 가능</li>
</ul>
<h3 id="3️⃣foreign-function--memory-api">3️⃣Foreign Function &amp; Memory API</h3>
<ul>
<li>자바 애플리케이션이 외부 메모리와 상호 작용하는 새로운 방식 제공</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>