<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hi_soap.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 08 May 2026 03:02:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hi_soap.log</title>
            <url>https://velog.velcdn.com/images/hi_soap/profile/dfb4c39c-d5a8-48d4-8633-af3ea1720216/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hi_soap.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hi_soap" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[폰켓몬]]></title>
            <link>https://velog.io/@hi_soap/%ED%8F%B0%EC%BC%93%EB%AA%AC</link>
            <guid>https://velog.io/@hi_soap/%ED%8F%B0%EC%BC%93%EB%AA%AC</guid>
            <pubDate>Fri, 08 May 2026 03:02:15 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.08</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.
홍 박사님 연구실의 폰켓몬은 종류에 따라 번호를 붙여 구분합니다. 따라서 같은 종류의 폰켓몬은 같은 번호를 가지고 있습니다. 예를 들어 연구실에 총 4마리의 폰켓몬이 있고, 각 폰켓몬의 종류 번호가 [3번, 1번, 2번, 3번]이라면 이는 3번 폰켓몬 두 마리, 1번 폰켓몬 한 마리, 2번 폰켓몬 한 마리가 있음을 나타냅니다. 이때, 4마리의 폰켓몬 중 2마리를 고르는 방법은 다음과 같이 6가지가 있습니다.</p>
<ol>
<li>첫 번째(3번), 두 번째(1번) 폰켓몬을 선택</li>
<li>첫 번째(3번), 세 번째(2번) 폰켓몬을 선택</li>
<li>첫 번째(3번), 네 번째(3번) 폰켓몬을 선택</li>
<li>두 번째(1번), 세 번째(2번) 폰켓몬을 선택</li>
<li>두 번째(1번), 네 번째(3번) 폰켓몬을 선택</li>
<li>세 번째(2번), 네 번째(3번) 폰켓몬을 선택</li>
</ol>
<p>이때, 첫 번째(3번) 폰켓몬과 네 번째(3번) 폰켓몬을 선택하는 방법은 한 종류(3번 폰켓몬 두 마리)의 폰켓몬만 가질 수 있지만, 다른 방법들은 모두 두 종류의 폰켓몬을 가질 수 있습니다. 따라서 위 예시에서 가질 수 있는 폰켓몬 종류 수의 최댓값은 2가 됩니다.
당신은 최대한 다양한 종류의 폰켓몬을 가지길 원하기 때문에, 최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려 합니다. N마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아, 그때의 폰켓몬 종류 번호의 개수를 return 하도록 solution 함수를 완성해주세요.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>nums는 폰켓몬의 종류 번호가 담긴 1차원 배열입니다.</li>
<li>nums의 길이(N)는 1 이상 10,000 이하의 자연수이며, 항상 짝수로 주어집니다.</li>
<li>폰켓몬의 종류 번호는 1 이상 200,000 이하의 자연수로 나타냅니다.</li>
<li>가장 많은 종류의 폰켓몬을 선택하는 방법이 여러 가지인 경우에도, 선택할 수 있는 폰켓몬 종류 개수의 최댓값 하나만 return 하면 됩니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c72c5e95-8709-448b-a072-e89138c6920f/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-5분">소요 시간: 5분</h4>
<h4 id="시간-복잡도-on">시간 복잡도: O($n$)</h4>
<pre><code class="language-java">import java.util.Map;
import java.util.HashMap;

class Solution {
    public int solution(int[] nums) {
        int answer = 0;
        int len = nums.length;
        Map&lt;Integer, Integer&gt; map = new HashMap&lt;&gt;();

        for (int i = 0; i &lt; len; i++) {
            if (map.size() &gt;= len / 2) {
                break;
            }    
            Integer mon = map.get(nums[i]);
            if (mon == null) { // 해시맵에 해당 포켓몬이 존재하지 않을 때
                map.put(nums[i], 1);
            }
            else {
                continue;
            }
        }

        return map.size();
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on-1">시간 복잡도: O($n$)</h4>
<hr>
<p>존재 여부만 확인하면 되므로 중복을 자동으로 제거하는 <code>HashSet</code>을 사용</p>
<hr>
<pre><code class="language-java">import java.util.HashSet;
import java.util.Set;

class Solution {
    public int solution(int[] nums) {
        Set&lt;Integer&gt; set = new HashSet&lt;&gt;();
        int limit = nums.length / 2;

        for (int num : nums) {
            set.add(num);
            if (set.size() &gt;= limit) break;
        }

        return set.size();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[완주하지 못한 선수]]></title>
            <link>https://velog.io/@hi_soap/%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</link>
            <guid>https://velog.io/@hi_soap/%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</guid>
            <pubDate>Thu, 07 May 2026 02:17:27 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.07</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.</p>
<p>마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.</li>
<li>completion의 길이는 participant의 길이보다 1 작습니다.</li>
<li>참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.</li>
<li>참가자 중에는 동명이인이 있을 수 있습니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e9e0337b-b6c3-4a54-91e0-29f7d5e57d16/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-18분">소요 시간: 18분</h4>
<h4 id="시간-복잡도-on">시간 복잡도: O($n$)</h4>
<pre><code class="language-java">import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collector;

class Solution {
    public String solution(String[] participant, String[] completion) {
        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        List&lt;String&gt; list = new ArrayList&lt;&gt;();

        for (int i = 0 ; i &lt; participant.length; i++) {
            String name = participant[i];
            map.put(name, map.getOrDefault(name, 0) + 1);
        }

        for (int i = 0; i &lt; completion.length; i++) {
            String name = completion[i];
            map.put(name, map.get(name) - 1);
        }

        for (Map.Entry&lt;String, Integer&gt; entry : map.entrySet()) {
            if (entry.getValue() &gt; 0) {
                list.add(entry.getKey());
            }
        }

        return String.join(&quot;, &quot;, list);
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on-1">시간 복잡도: O($n$)</h4>
<hr>
<p>문제 조건을 보면 완주하지 못하는 사람은 1명이므로,
완주하지 못한 사람이 나오면 바로 그 값을 리턴하면 된다.</p>
<p>문제를 제대로 보지 않고 푸는 습관 때문에 코딩 테스트 문제를 풀며 
알고리즘 설계를 처음부터 다시 시작하는 일이 꽤 잦았다.
문제를 꼼꼼히 살피는 습관을 가져야 할 것 같다.</p>
<hr>
<pre><code class="language-java">import java.util.Map;
import java.util.HashMap;

class Solution {
    public String solution(String[] participant, String[] completion) {
        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();

        for (String name : participant) {
            map.put(name, map.getOrDefault(name, 0) + 1);
        }
        for (String name : completion) {
            map.put(name, map.get(name) - 1);
        }
        for (Map.Entry&lt;String, Integer&gt; entry : map.entrySet()) {
            if (entry.getValue() &gt; 0) {
                return entry.getKey();  // 바로 반환
            }
        }

        return &quot;&quot;;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[08. 메모리 관리]]></title>
            <link>https://velog.io/@hi_soap/08.-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@hi_soap/08.-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Wed, 06 May 2026 07:19:43 GMT</pubDate>
            <description><![CDATA[<h2 id="강의-목표">강의 목표</h2>
<ul>
<li><p>컴퓨터 시스템에 존재하는 다양한 저장소들로 구성되는 
<span style="background-color: #dcffe4">메모리 계층 구조</span>를 이해하고 필요성을 안다.</p>
</li>
<li><p>메모리에 대한 <span style="background-color: #dcffe4">물리 주소와 논리 주소</span>를 이해하고 
프로그램 실행 중에 <span style="background-color: #dcffe4">논리 주소가 물리 주소로 변환됨</span>을 안다.</p>
</li>
<li><p>프로세스의 실행에 필요한 <span style="background-color: #dcffe4">메모리 할당 정책</span>에 대해 이해한다.</p>
</li>
<li><p>연속 메모리 할당*</p>
</li>
<li><p>분할 메모리 할당*</p>
</li>
<li><p>모든 메모리 할당에는 사용할 수 없는 조각 메모리(단편화)가 발생하는데, <span style="background-color: #dcffe4">단편화</span>에 대해 이해한다.</p>
</li>
<li><p><span style="background-color: #dcffe4">홀 선택(동적 메모리 할당)알고리즘</span>을 이해한다.</p>
</li>
<li><p>first-fit, best-fit, worst-fit*</p>
</li>
<li><p>메모리 관리 기법 중 <span style="background-color: #dcffe4">세그멘테이션</span>을 구체적으로 이해한다.</p>
</li>
</ul>
<h2 id="01-메모리-계층-구조와-메모리-관리-핵심">01. 메모리 계층 구조와 메모리 관리 핵심</h2>
<h3 id="11-메모리-계층-구조">1.1 메모리 계층 구조</h3>
<p><strong>메모리</strong>: CPU가 실행할 프로그램 코드와 데이터를 저장하는 물리 장치
<em>메모리가 없는 컴퓨터는 존재할 수 없음</em></p>
<ul>
<li><p>메모리는 컴퓨터 시스템 여러 곳에 <span style="background-color: #dcffe4">계층적</span>으로 존재</p>
</li>
<li><p>메모리 계층 구조의 중심은 <span style="background-color: #dcffe4">메인 메모리(RAM)</span></p>
</li>
<li><p>전원이 끊어지면 저장된 정보가 사라짐
하드 디스크가 없는 컴퓨터는 존재할 수 있지만, 
<span style="background-color: #dcffe4">메인 메모리가 없는 컴퓨터는 존재할 수 없음</span>*</p>
</li>
<li><p>CPU로부터 멀어질수록 <span style="background-color: #dcffe4">용량이 커지고, 속도가 느려지며, 가격이 비싸진다</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e9da1825-8e30-4986-9dc9-f582ca1d0fa0/image.png" alt=""></p>
<h4 id="메모리-계층화-성능과-비용의-절충">메모리 계층화, 성능과 비용의 절충</h4>
<ul>
<li><p>CPU 성능 향상 → 더 빠른 메모리 요구 
→ 작지만 빠른 off-chip 캐시 등장
→ 더 빠른 액세스를 위해 on-chip 캐시 
→ 멀티 코에어 적합한 L1/2/3 캐시
<span style="background-color: #dcffe4"><em>빠른 메모리일수록 고가이므로 작은 용량 사용</em></span></p>
</li>
<li><p>컴퓨터의 성능 향상 → 처리할 데이터 대형화 
→ 저장 장치(하드디스크)의 대형화 → 빠른 저장 장치 요구
→ SSD 등장</p>
</li>
</ul>
<h4 id="메모리-계층-구조의-각-요소">메모리 계층 구조의 각 요소</h4>
<h4 id="cpu-레지스터cpu-registers-byte">CPU 레지스터(CPU registers, $byte$)</h4>
<ul>
<li><p><span style="background-color: #dcffe4">현재 실행할 코드와 데이터</span>, 혹은 <span style="background-color: #dcffe4">다음에 실행할 몇 개의 코드와 데이터 저장</span></p>
</li>
<li><p>레지스터의 크기와 개수는 <span style="background-color: #dcffe4">CPU의 종류에 따라 다름</span></p>
</li>
</ul>
<h4 id="캐시메모리cache-memory-kb-mb">캐시메모리(cache memory, $KB, MB$)</h4>
<ul>
<li><p>CPU의 빠른 처리 속도에 맞추기 위해 도입됨</p>
</li>
<li><p>캐시 메모리가 있는 컴퓨터에서는 
CPU가 <span style="background-color: #dcffe4">캐시 메모리에서 프로그램 코드와 데이터를 읽어 실행함</span>
→ 코드와 데이터들은 메인 메모리로부터 캐시 메모리로 <span style="background-color: #dcffe4">미리</span> 복사되어야 함</p>
</li>
<li><p>캐시 메모리는 <span style="background-color: #dcffe4">응답 속도와 위치</span>에 따라 여러 레벨로 나누어 사용됨</p>
</li>
<li><p>멀티 코어 CPU의 경우, <span style="background-color: #dcffe4">코어 별로 <strong>L1/L2(MB 단위)</strong> 이름의 캐시</span>를 두고,
<span style="background-color: #dcffe4">모든 코어들이 공유하는 <strong>L3(KB 단위)</strong> 캐시</span>를 둠(Core-i7의 경우 내부에 두기도 함)*</p>
</li>
<li><p><strong><span style="background-color: #dcffe4">SRAM(Static RAM)</span></strong></p>
</li>
<li><p>전원이 공급되는 한 <span style="background-color: #dcffe4">정보가 안정적으로 유지됨*</span></p>
</li>
</ul>
<h4 id="메인-메모리main-memory-ram-gb">메인 메모리(main memory, RAM, $GB$)</h4>
<ul>
<li><p><span style="background-color: #dcffe4"><strong>현재 실행 중인</strong> 모든 프로세스의 코드와 데이터</span>, 
읽거나 쓰고 있는 <span style="background-color: #dcffe4">여러 파일들의 블록</span>,
운영체제의 <span style="background-color: #dcffe4">커널 코드와 커널 데이터</span>들이 저장됨</p>
</li>
<li><p>메모리에 있는 <span style="background-color: #dcffe4">사용자 프로그램과 운영체제 커널을 구분하지 않고</span> 
당장 실행을 위해 필요한 <span style="background-color: #dcffe4">일부분의 코드와 데이터</span>가 캐시 메모리로 복사된다</p>
</li>
<li><p><strong><span style="background-color: #dcffe4">DRAM(Dynamic RAM)</span></strong></p>
</li>
<li><p>시간이 지나면 저장된 정보가 사라지기 때문에,* 
<span style="background-color: #dcffe4"><em>사라지기 전에 동일한 정보를 다시 기록해주어야 함(</em> <strong>refresh</strong> <em>)</span></em></p>
</li>
<li><p>refresh를 <span style="background-color: #dcffe4">지속적으로</span> 수행해야 함*</p>
</li>
</ul>
<h4 id="보조기억장치secondary-memory-tb">보조기억장치(secondary memory, $TB$)</h4>
<ul>
<li><p>전원을 꺼도 지워지지 않는 대용량 저장 장치</p>
</li>
<li><p>파일이나 데이터베이스 등을 저장</p>
</li>
<li><p>메인 메모리의 크기 한게로 인해 
<span style="background-color: #dcffe4">메모리에 적재된 프로그램 코드와 데이터의 일부를 일시 저장(*<em>스왑 영역 *</em>)</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/78a133e5-a58e-456d-8273-fa6ce73f2c66/image.png" alt="">
<em>속도도 잘 봐둘 것</em></p>
<h4 id="프로그램의-실행과-메모리-계층-구조">프로그램의 실행과 메모리 계층 구조</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/6c519416-3399-4e54-bf8c-ceff809f43d8/image.png" alt=""></p>
<ul>
<li><p>보조기억장치에 저장된 실행 파일을 메인 메모리에 적재
→ 메인 메모리의 일부 코드와 실행에 필요한 데이터가 L3 캐시로 복사
→ L3 캐시에서 <span style="background-color: #dcffe4">당장 실행할 코드와 데이터의 일부분</span>이 L1/2 캐시로 복사
→ CPU 코어는 L1/2 캐시에서 <span style="background-color: #dcffe4">현재 실행할 명령과 데이터를 
레지스터로</span> 읽은 후 연산 실행</p>
</li>
<li><p>캐시가 없는 컴퓨터 → CPU가 <span style="background-color: #dcffe4">메인 메모리로부터</span> 데이터를 가져와 명령 실행
캐시를 가진 컴퓨터 → <span style="background-color: #dcffe4">캐시로부터만</span> 프로그램 코드와 데이터를 읽고 실행</p>
</li>
<li><p>실행에 필요한 코드와 데이터는 <span style="background-color: #dcffe4">반드시 캐시로 복사되어야 함</span>*</p>
</li>
</ul>
<h4 id="메인-메모리와-캐시">메인 메모리와 캐시</h4>
<p><strong>캐시 미스(cache miss)</strong></p>
<ul>
<li><p>CPU가 현재 프로세스나 스레드의 실행을 중단하고 
다른 프로세스나 스레드를 실행할 때, 
L1/2 캐시에서 <span style="background-color: #dcffe4">새로 실행하고자 하는 프로세스/스레드의 
명령과 데이터를 찾을 수 없는 현상</span></p>
</li>
<li><p>L1/2에 없다면 L3에서, L3에도 없다면 메인 메모리에서 데이터를 가져옴</p>
</li>
<li><p>캐시 미스는 <span style="background-color: #dcffe4">연쇄적으로 발생함</span>*</p>
</li>
<li><p>새로운 프로세스나 스레드의 코드와 데이터가 캐시로 들어오기 전에,
현재 캐시의 수정된 데이터는 다시 L3 캐시나 메인 메모리에 기록되어야 함</p>
</li>
<li><p>캐시 미스는 프로그램의 <span style="background-color: #dcffe4">실행 중에도 발생</span></p>
</li>
</ul>
<h4 id="메인-메모리와-보조기억장치">메인 메모리와 보조기억장치</h4>
<p><strong>가상 메모리(virtual memory)</strong></p>
<ul>
<li>메인 메모리 전체가 사용 중 이거나 빈 영역이 일정 이하로 줄어들었을 때,
메인 메모리에 적재된 <span style="background-color: #dcffe4">코드나 데이터의 일부분을 
하드디스크나 SSD에 저장</span>하고 메인 메모리에 빈 공간 확보</li>
</ul>
<h3 id="12-메모리-계층화의-성공-이유-참조의-지역성">1.2 메모리 계층화의 성공 이유, 참조의 지역성</h3>
<p><strong>참조의 지역성(locality of reference)</strong></p>
<ul>
<li>코드나 데이터, 자원 등이 아주 짧은 시간 내에 <span style="background-color: #dcffe4">다시 사용되는 프로그램 특성</span></li>
</ul>
<h3 id="14-메모리-관리">1.4 메모리 관리</h3>
<p>메모리에는 <span style="background-color: #dcffe4">현재 실행 중인 모든 프로세스들의 코드와 데이터,</span>
그리고 <span style="background-color: #dcffe4">운영체제의 코드와 데이터</span>가 적재되어 있다.</p>
<h4 id="메모리-관리-이유">메모리 관리 이유</h4>
<p><strong>메모리가 운영체제에 의해 관리되어야 하는 이유</strong></p>
<ul>
<li><p><strong>1) 메모리는 공유 자원</strong>
여러 프로세스 사이에 메모리 공유
각 프로세스에게 물리 메모리 할당</p>
</li>
<li><p><strong>2) 메모리 보호</strong>
프로세스의 독립된 메모리 공간 보장</p>
</li>
<li><p>다른 프로세스로부터 보호*
사용자 코드로부터 커널 공간 보호</p>
</li>
<li><p><strong>3) 메모리 용량 한계 극복</strong>
설치된 물리 메모리보다 큰 프로세스 지원 필요
여러 프로세스의 메모리 합이 설치된 물리 메모리보다 큰 경우</p>
</li>
<li><p><strong>4) 메모리 효율성 증대</strong>
가능한 많은 개수의 프로세스를 실행시키기 위함
(DOM, Degree of Multiprogramming)</p>
</li>
<li><p>프로세스당 최소한의 메모리만 할당*</p>
</li>
</ul>
<h4 id="메모리-관리-기능">메모리 관리 기능</h4>
<p>기본적인 메모리 관리 기능은 <span style="background-color: #dcffe4">할당과 보호의 2개</span>로 요약할 수 있음
어떤 운영체제이든 이 2가지 기능은 <span style="background-color: #dcffe4">반드시 지원되어야 함</span></p>
<ul>
<li><p><strong>메모리 할당</strong>
프로세스에게 메모리를 할당하는 기능</p>
</li>
<li><p><strong>메모리 보호</strong>
프로세스가 다른 프로세스의 메모리 영역이나, 
운영체제 영역을 침범·훼손하지 못하도록 보호하는 기능</p>
</li>
</ul>
<h2 id="메모리-주소">메모리 주소</h2>
<h3 id="21-물리-주소와-논리-주소">2.1 물리 주소와 논리 주소</h3>
<p>메모리는 <span style="background-color: #dcffe4">오직 주소</span>를 이용해서만 접근됨</p>
<p><strong>물리 주소(physical address)</strong>: 실제 메모리 주소(하드웨어 주소)</p>
<ul>
<li><p>물리 메모리(RAM)에 새겨진 주소</p>
</li>
<li><p>컴퓨터를 설계·제작하는 시점에 <span style="background-color: #dcffe4">하드웨어에 의해</span> 매겨지는 <span style="background-color: #dcffe4">고정된 주소</span></p>
</li>
<li><p><span style="background-color: #dcffe4">0에서 시작</span>하는 <span style="background-color: #dcffe4">연속</span>되는 주소 체계</p>
</li>
<li><p>메모리는 <span style="background-color: #dcffe4">시스템 주소 버스를 통해</span> 물리 주소(이진 신호)의 신호를 받음</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e9ed1489-f019-423a-bd6e-3b791e58aa91/image.png" alt=""></p>
<p><strong>논리/가상 주소(logical/virtual address)</strong>: 프로그램 내에서 사용되는 주소</p>
<ul>
<li><p>개발자/프로세스가 <span style="background-color: #dcffe4">프로세스 내에서 사용하는 주소</span>로 
코드나 변수 등에 대한 주소</p>
</li>
<li><p><span style="background-color: #dcffe4">0에서 시작</span>하여 <span style="background-color: #dcffe4">연속</span>되는 주소 체계, 프로세스 내에서 매겨진 <span style="background-color: #dcffe4">상대 주소</span></p>
</li>
<li><p>프로그램에서 변수 n의 주소가 100번지라면, 
<span style="background-color: #dcffe4">논리 주소를 의미</span>. 물리 주소는 알 수 없음*</p>
</li>
<li><p><span style="background-color: #dcffe4">컴파일러와 링커에 의해</span> 매겨진 주소</p>
</li>
<li><p>실행 파일 내에 만들어진 목적 코드나 데이터의 주소들은 <span style="background-color: #dcffe4">모두 논리 주소*</span></p>
</li>
<li><p>CPU 내에서 프로세스를 실행하는 동안 다루는 주소는 <span style="background-color: #dcffe4">모두 논리 주소</span></p>
</li>
<li><p>사용자나 프로세스는 <span style="background-color: #dcffe4">결코 물리 주소를 알 수 없음</span></p>
</li>
</ul>
<h3 id="22-논리-주소의-물리-주소-변환">2.2 논리 주소의 물리 주소 변환</h3>
<p><strong>MMU(Memory Management Unit) 
또는 주소 변환 하드웨어(Address Translation H/W)</strong></p>
<ul>
<li><p><span style="background-color: #dcffe4">논리 주소를 물리 주소로</span> 바꾸는 하드웨어 장치</p>
</li>
<li><p>CPU가 발생시킨 논리 주소는 MMU에 의해 물리 주소로 바뀌어 메모리에 도달*</p>
</li>
<li><p>오늘날 MMU는 MMU 덕분으로 여러 프로세스가 하나의 물리 메모리에서 실행되도록 됨
<span style="background-color: #dcffe4">CPU 패키지에 내장</span>
MMU 덕분으로 여러 프로세스가 하나의 물리 메모리에서 실행되도록 됨</p>
</li>
<li><p>CPU가 발생시키는 주소
MMU가 CPU 칩(CPU 패키지) 외부에 위치 → 논리 주소
MMU가 CPU 칩 내부에 위치 → 물리 주소</p>
</li>
<li><p>CPU 패키지 = CPU + MMU 등*</p>
</li>
</ul>
<h3 id="23-컴파일과-논리-주소-논리-주소가-사용되는-이유">2.3 컴파일과 논리 주소, 논리 주소가 사용되는 이유</h3>
<ul>
<li><p>컴파일러는 프로그램을 <span style="background-color: #dcffe4">논리 주소</span>로 컴파일</p>
</li>
<li><p>컴파일 시점에 물리 메모리 몇 번지에 적재될지 알 수 없음*
코드와 전역 변수들은 0번지에서부터 시작하는 <span style="background-color: #dcffe4">논리 주소에 할당</span></p>
</li>
<li><p>프로그램 실행 시작 시, <span style="background-color: #dcffe4">운영체제에 의해</span> 
프로그램을 물리 메모리의 적절한 위치에 적재하고, 매핑 테이블이 생성됨</p>
</li>
<li><p>현재 실행하는 명령의 물리 주소를 아는 것은 <span style="background-color: #dcffe4">오직 MMU 밖에 없음</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/fbe308ed-5383-4ea0-95fb-6752e1f4033e/image.png" alt=""></p>
<ul>
<li><p><strong>80386 칩</strong>은 CPU(Central Processing Unit), 
BIU(Bus Interface Unit),MMU(Memory Management Unit)로 구성됨</p>
</li>
<li><p><strong>MMU</strong>는 Segmentation Unit, Paging Unit으로 구성됨</p>
</li>
<li><p><strong>PAGE CACHE</strong>
MMU 내부에 위치하며, 매핑 테이블의 일부를 저장해두는 캐시이다.</p>
</li>
</ul>
<h3 id="탐구-8-1">탐구 8-1</h3>
<pre><code class="language-c">#include &lt;stdio.h&gt;
int n = 0;
int main() {
        printf(&quot;변수 n의 주소는 %p\n&quot;, &amp;n); // n의 주소 출력
}</code></pre>
<pre><code class="language-c">$ gcc -no-pie -o logical logicaladdress.c
$ ./logical
변수 n의 주소는 0x60103c
$ ./logical
변수 n의 주소는 0x60103c
$ ./logical
변수 n의 주소는 0x60103c
$</code></pre>
<p><em>전역 변수 <code>n</code>의 주소는 논리 주소이기 때문에
실행할 때 마다 주소가 같다.</em></p>
<p>단, 온라인 터미널 (Cocalc)등에서 실행 시, 
메모리 보호 기능인 <span style="background-color: #dcffe4">PIE</span>에 의해 전역 변수와 코드의 <span style="background-color: #dcffe4">논리 주소가 매번 변경</span>된다.
<span style="background-color: #dcffe4"><em>PIE는 ASLR과 달리 코드 영역과 데이터 영역의 논리 주소까지 바꿈</em></span>
<code>$ gcc –no-pie –o logical logicaladdress.c</code>: 보호 기능 제거 후 컴파일</p>
<h3 id="tip-aslr">TIP: ASLR</h3>
<p><strong>ASLR</strong></p>
<ul>
<li><p>해커들의 <span style="background-color: #dcffe4">메모리 공격에 대한 대비책</span>, 오늘날 대부분의 운영체제가 사용</p>
</li>
<li><p>주소 공간 랜덤 배치
프로세스의 주소 공간 내에서 스택이나 힙, 라이브러리 영역의 <span style="background-color: #dcffe4">랜덤 배치</span>
실행할 때 마다 논리 주소가 바뀌게 하는 기법 
→ 실행 마다 함수의 <span style="background-color: #dcffe4">지역 변수와 동적 할당 받는 메모리의 논리 주소가 바뀜</span></p>
</li>
<li><p><span style="background-color: #dcffe4">코드와 전역 변수</span>가 적재되는 데이터 영역의 논리 주소는 <span style="background-color: #dcffe4">바뀌지 않음</span>
<img src="https://velog.velcdn.com/images/hi_soap/post/8920739c-8296-4deb-95f0-1b4e0044f74e/image.png" alt=""></p>
</li>
</ul>
<h2 id="03-물리-메모리-관리">03. 물리 메모리 관리</h2>
<h3 id="31-메모리-할당memory-allocation">3.1 메모리 할당(memory allocation)</h3>
<p><strong>메모리 할당</strong></p>
<ul>
<li><p>운영체제가 <span style="background-color: #dcffe4">새 프로세스를 실행</span>시키거나 
<span style="background-color: #dcffe4">실행 중인 프로세스가 메모리를 필요로 할 때</span> 
프로세스에게 <span style="background-color: #dcffe4">물리 메모리를 할당</span>하는 것</p>
</li>
<li><p>프로세스의 실행은 <span style="background-color: #dcffe4">할당된 물리 메모리</span>에서 이루어짐</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/d79e3858-890b-4ac2-8986-a8a7757ba29e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/61605b81-729e-49dd-8fe2-606e363f7979/image.png" alt=""></p>
<h3 id="32-연속-메모리-할당contiguous-memory-allocation">3.2 연속 메모리 할당(contiguous memory allocation)</h3>
<ul>
<li>프로세스별로 <span style="background-color: #dcffe4"><strong>연속된 한 덩어리(single contiguous memory)</strong>의 메모리 할당</span></li>
<li>프로세스가 할당받은 메모리가 <span style="background-color: #dcffe4">한 덩어리로 연속된 공간</span>이라는 의미*</li>
</ul>
<h4 id="고정-크기-할당fixed-size-partition-allocation">고정 크기 할당(fixed size partition allocation)</h4>
<ul>
<li><p>메모리를 고정 크기의 파티션으로 나누고, 프로세스 당 하나의 파티션 할당</p>
</li>
<li><p>파티션이 미리 나누어져 있기 때문에 
<span style="background-color: #dcffe4"><strong>고정 할당(fixed partitioning)</strong>
</span> 또는 <span style="background-color: #dcffe4"><strong>정적 할당(static partitioning)</strong></span>이라고도 한다.</p>
</li>
</ul>
<h4 id="가변-크기-할당variable-size-partition-allocation">가변 크기 할당(variable size partition allocation)</h4>
<ul>
<li><p>메모리를 가변 크기의 파티션으로 나누고, 프로세스 당 하나의 파티션 할당</p>
</li>
<li><p>파티션이 미리 나누어져 있지 않고, 프로세스의 크기나 요청에 따라 바뀌므로
<span style="background-color: #dcffe4"><strong>동적 할당(dynamic partiitioning)</strong></span>이라고도 한다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/2b82285e-05e8-41c2-bdd9-182d052d3e3f/image.png" alt=""></p>
<p><strong>단점</strong></p>
<ul>
<li><p>연속된 메모리를 할당하기 때문에 메모리 할당의 <span style="background-color: #dcffe4">유연성이 부족</span></p>
</li>
<li><p>메모리 전체에 비어 있는 <span style="background-color: #dcffe4"><strong>작은 공간(홀)</strong></span>들을 합하면 충분한 공간이 있음에도,
프로세스를 할당할만큼 <span style="background-color: #dcffe4">연속된 메모리가 없어</span> 적재할 수 없음</p>
</li>
</ul>
<h3 id="33-분할-메모리-할당non-contiguous-memory-allocation">3.3 분할 메모리 할당(non-contiguous memory allocation)</h3>
<ul>
<li>프로세스에게 <span style="background-color: #dcffe4"><strong>여러 덩어리의 메모리</strong></span> 할당</li>
</ul>
<h4 id="가변-크기-할당-세그먼테이션segmentation">가변 크기 할당: 세그먼테이션(segmentation)</h4>
<ul>
<li><p>가변 크기의 덩어리 메모리를 여러 개 분산 할당</p>
</li>
<li><p><strong>세그먼트(segment)</strong>
프로세스 내에서 하나의 단위로 다룰 수 있는 <span style="background-color: #dcffe4">의미 있는 블록</span></p>
</li>
<li><p>하나의 함수, 객체가 될 수 있음*
프로세스에 존재하는 <span style="background-color: #dcffe4">각 세그먼트의 크기가 다름</span></p>
</li>
<li><p>대부분의 시스템에서는 프로세스를
<span style="background-color: #dcffe4">코드, 데이터, 스택, 힙의 4개 세그먼트로 분할·할당</span>한다.</p>
</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li>프로세스들이 실해되고 종료되기를 반복하고 나면, 메모리에 적재된 세그먼트들 사이에 빈 공간이 생기게 되며, 일부는 크기가 작아
새 프로세스에게 할당하지 못하는 <span style="background-color: #dcffe4"><strong>메모리 낭비(외부 단편화)</strong></span>가 초래됨</li>
</ul>
<h4 id="고정-크기-할당-페이징paging">고정 크기 할당: 페이징(paging)</h4>
<ul>
<li><p>고정 크기의 덩어리 메모리를 여러 개 분산 할당</p>
</li>
<li><p>세그먼테이션의 <span style="background-color: #dcffe4">메모리 낭비를 해결하기 위해</span> 도입됨</p>
</li>
<li><p>프로세스를 논리적인 단위로 분할하지 않고, 논리주소 0번지부터
<span style="background-color: #dcffe4"><strong>페이지(page)</strong></span>라고 부르는 <span style="background-color: #dcffe4">고정 크기(동일한 크기)</span>로 분할</p>
</li>
<li><p>물리 메모리 또한 <span style="background-color: #dcffe4">페이지와 동일한 크기로 분할하여 <strong>프레임(frame)</strong></span>이라고 함</p>
</li>
</ul>
<p><em>현대 컴퓨터 시스템은 <span style="background-color: #dcffe4">페이징 기법을 기반으로</span> 세그먼테이션을 혼합 사용함</em></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/3ede26cf-4af0-4003-b6d1-f6c6f1fad1ca/image.png" alt=""></p>
<h2 id="04-연속-메모리-할당">04. 연속 메모리 할당</h2>
<ul>
<li><p>프로세스를 1개의 <span style="background-color: #dcffe4">연속된 메모리 공간</span>에 배치</p>
</li>
<li><p>메모리 전체를 <span style="background-color: #dcffe4">여러 개의 파티션</span>으로 분할*</p>
</li>
<li><p>각 프로세스에게 <span style="background-color: #dcffe4">한 파티션 할당</span>*</p>
</li>
<li><p>연속 메모리 할당은 <span style="background-color: #dcffe4">초기 운영체제</span>에서 사용</p>
</li>
<li><p>MS-DOS: 한 프로세스가 <span style="background-color: #dcffe4">전체 메모리 독점</span>*</p>
</li>
<li><p>초기의 다중프로그래밍 운영체제*</p>
</li>
</ul>
<h3 id="41-고정-크기-할당">4.1 고정 크기 할당</h3>
<ul>
<li>메모리를 처음부터 <span style="background-color: #dcffe4">파티션(partition)</span>이라고 부르는 <span style="background-color: #dcffe4">고정 크기</span>로 나누고,
프로세스에게 <span style="background-color: #dcffe4">1개의 파티션을 할당</span></li>
<li>4/8/16KB 등 몇가지 파티션들을 만들어두고 적합한 파티션을 할당*</li>
</ul>
<p><strong>IBM OS/360 MFT(Multiple Programming with a Fixed Number of Tasks)</strong></p>
<ul>
<li><p>메모리 전체를 <span style="background-color: #dcffe4">n개의 동일한 파티션으로 분할</span>하여 <span style="background-color: #dcffe4">프로세스마다 하나씩 할당</span></p>
</li>
<li><p><span style="background-color: #dcffe4">동시에 실행시킬 수 있는 프로세스</span> 또한 <span style="background-color: #dcffe4">n개</span></p>
</li>
<li><p>메모리가 부족할 때, 프로세스는 <span style="background-color: #dcffe4">큐에서 대기</span></p>
</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li><p>프로세스가 파티션의 크기보다 작은 경우 메모리의 일부가 낭비됨</p>
</li>
<li><p>파티션보다 큰 크기의 프로세스는 처음부터 실행될 수 없음
→ 시스템 운영자에 의해 
실행시킬 전체 응용프로그램들의 크기가 <span style="background-color: #dcffe4">사전에 계산됨</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/4d8ecc4d-a5b2-41d3-b7cd-746a27f1e2d1/image.png" alt=""></p>
<h3 id="42-가변-크기-할당">4.2 가변 크기 할당</h3>
<ul>
<li><p>고정 크기 할당의 <span style="background-color: #dcffe4">메모리 낭비(내부 단편화)</span>를 해결하기 위해 제시됨</p>
</li>
<li><p>처음부터 파티션을 나누어 놓지 않고, <span style="background-color: #dcffe4">프로세스와 동일한 크기의 메모리 할당</span></p>
</li>
</ul>
<p><strong>IBM OS/360 MVT(Multiple Programming with a Variable Number of Tasks)</strong></p>
<ul>
<li><p>할당되는 메모리 공간을 <span style="background-color: #dcffe4"><strong>리전(region)</strong></span>이라고 부름</p>
</li>
<li><p>수용 가능한 프로세스의 개수는 <span style="background-color: #dcffe4">가변적</span></p>
</li>
</ul>
<p><strong><em>고정 크기 할당과 가변 크기 할당 모두 <span style="background-color: #dcffe4">가상 메모리 기법을 지원하지 않음</span></em></strong></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/827ac891-ba18-4063-b2bf-fb9b898a3467/image.png" alt=""></p>
<h3 id="43-단편화fragmentation">4.3 단편화(fragmentation)</h3>
<ul>
<li>프로세스에게 할당할 수 없는 <span style="background-color: #dcffe4">조각 메모리들이 생기는 현상</span></li>
<li>조각 메모리를 <span style="background-color: #dcffe4"><strong>홀(hole)</strong></span>이라고 부름</li>
</ul>
<h4 id="내부-단편화internal-fragmentation">내부 단편화(internal fragmentation)</h4>
<ul>
<li><span style="background-color: #dcffe4">할당된 메모리 내부에</span> 사용할 수 없는 <span style="background-color: #dcffe4">홀이 생기는 현상</span></li>
<li>파티션보다 작은 프로세스를 할당하는 경우, <span style="background-color: #dcffe4">파티션 내에 홀 발생</span>*</li>
<li>IBM OS/360 MFT*</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/ccbe041c-b596-4ab6-b82f-227775205511/image.png" alt=""></p>
<h4 id="외부-단편화external-fragmentation">외부 단편화(external fragmentation)</h4>
<ul>
<li><p><span style="background-color: #dcffe4">할당된 메모리들 사이에</span> 사용할 수 없는 <span style="background-color: #dcffe4">홀이 생기는 현상</span></p>
</li>
<li><p><span style="background-color: #dcffe4">가변 크기</span>의 파티션이 생기고 반환되는 과정에서 작은 홀 생성*</p>
</li>
<li><p><span style="background-color: #dcffe4">홀이 프로세스의 크기보다 작으면 할당할 수 없음</span>*</p>
</li>
<li><p>IBM OS/360 MVT*</p>
</li>
<li><p>→ <span style="background-color: #dcffe4">MFT의 내부 단편화 문제를 해결하기 위해</span> 만들어진 개선된 운영체제*</p>
</li>
<li><p><strong>메모리 압축(memory compaction)</strong>
외부 단편화로 인해 할당할 메모리가 부족해지면
<span style="background-color: #dcffe4">파티션을 이동시켜 홀을 없애는 방법</span></p>
</li>
</ul>
<h3 id="44-연속-메모리-할당-구현">4.4 연속 메모리 할당 구현</h3>
<ul>
<li>연속 메모리 할당 구현을 위해서는 고정/가변 관계 없이 모두
<span style="background-color: #dcffe4">하드웨어와 운영체제의 지원이 필요함</span></li>
</ul>
<h4 id="하드웨어-지원">하드웨어 지원</h4>
<ul>
<li>논리 주소를 물리 주소로 변환하는 기능</li>
<li>프로세스가 다른 프로세스의 메모리 액세스를 금지하는 기능</li>
</ul>
<p><strong>base 레지스터</strong>: <span style="background-color: #dcffe4">현재 실행 중인 프로세스</span>에게 할당된 <span style="background-color: #dcffe4"><strong>물리 메모리</strong> 시작 주소</span>
<strong>limit 레지스터</strong>: <span style="background-color: #dcffe4">현재 실행 중인 프로세스</span>에게 할당된 <span style="background-color: #dcffe4">메모리 크기</span>
<strong>주소 레지스터</strong>: <span style="background-color: #dcffe4">현재 액세스하는 메모리의 논리 주소</span>
<strong>주소 변환 하드웨어(MMU)</strong>: 논리 주소를 물리 주소로 변환하는 장치</p>
<ul>
<li>MMU는 논리 주소를 논리 주소로 바꾸기 전, 
<span style="background-color: #dcffe4">다른 프로세스의 메모리 보호를 위해</span> 논리 주소를 <span style="background-color: #dcffe4">limit 레지스터</span>의 값과 비교
할당된 메모리 주소 범위를 넘어섰다면 (논리주소 ≥ limit)
→ 오류 처리 커널 코드를 실행하고, <span style="background-color: #dcffe4">현재 프로세스를 강제 중단</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/18c6ef3c-29da-4573-9b2d-5a82fd6e035a/image.png" alt="">
<em>현재 1000~1799번지에 적재된 상황</em></p>
<h4 id="운영체제의-지원">운영체제의 지원</h4>
<ul>
<li><p>운영체제는 모든 프로세스에 대해 
<span style="background-color: #dcffe4">할당된 <strong>물리 메모리의 시작 주소</strong>와 </p>
</li>
<li><p><em>크기 정보*</em>를 저장·관리 및 <strong>비어 있는 메모리 영역 관리</strong></span></p>
</li>
<li><p>새 프로세스 스케줄링 시, 
<span style="background-color: #dcffe4">프로세스의 물리 메모리 시작 주소와
크기 정보를 <strong>CPU 내부의 base/limit 레지스터에 적재</strong></span></p>
</li>
</ul>
<h3 id="45-홀-선택-알고리즘hole-selection-algorithm">4.5 홀 선택 알고리즘(hole selection algorithm)</h3>
<ul>
<li><p>또는 <span style="background-color: #dcffe4"><strong>동적 메모리 할당(dynamic memory allocation)</strong></span></p>
</li>
<li><p>프로세스를 처음 실행시키거나 실행 도중 메모리가 요구될 때,
<span style="background-color: #dcffe4">적당한 홀을 선택해서 할당하는 기법</span></p>
</li>
</ul>
<p><strong>고정 크기</strong></p>
<ul>
<li>운영체제는 홀(파티션)들을 <span style="background-color: #dcffe4">가용 메모리 리스트</span>로 만들어 관리·선택함</li>
</ul>
<p><strong>가변 크기</strong></p>
<ul>
<li><p>홀마다 <span style="background-color: #dcffe4">시작 주소와 크기</span> 정보에 대한 홀 리스트 생성 및 관리</p>
</li>
<li><p><strong>first-fit</strong>
홀 리스트에서 <span style="background-color: #dcffe4">처음</span> 만나는 <span style="background-color: #dcffe4">요청 크기보다 큰 홀</span> 선택</p>
</li>
<li><p><span style="background-color: #dcffe4">할당 속도가 빠르지만, 외부 단편화</span>로 인한 메모리 낭비가 큼*</p>
</li>
<li><p><strong>best-fit</strong>
홀 리스트에서 <span style="background-color: #dcffe4">요청 크기를 수용하는 것 중 <strong>가장 작은 홀</strong></span> 선택</p>
</li>
<li><p>리스트가 정렬되지 않았을 때, <span style="background-color: #dcffe4">탐색 부담*</span></p>
</li>
<li><p>할당 후 <span style="background-color: #dcffe4"><strong>가장 작은</strong> 홀</span>이 생성됨*</p>
</li>
<li><p><strong>worst-fit</strong>
홀 리스트에서 <span style="background-color: #dcffe4">요청 크기를 수용하는 것 중 <strong>가장 큰 홀</strong></span> 선택</p>
</li>
<li><p>리스트가 정렬되지 않았을 때, <span style="background-color: #dcffe4">탐색 부담</span>*</p>
</li>
<li><p>할당 후 <span style="background-color: #dcffe4"><strong>가장 큰</strong> 홀</span>이 생성됨*</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/eaae1664-4518-42de-8b08-c0f789b2350b/image.png" alt="">
<em>worst-fit 기법은 잘 사용하지 않음</em></p>
<p><strong>단편화</strong>: 홀이 생기는 현상이 아닌, 
<span style="background-color: #dcffe4">메모리 할당 요청에 <strong>사용할 수 없게 된 작은 홀</strong>이 생기는 현상</span></p>
<h3 id="46-연속-메모리-할당의-장단점">4.6 연속 메모리 할당의 장단점</h3>
<p><strong>장점</strong></p>
<ul>
<li>논리 주소 → 물리 주소의 과정이 단순</li>
<li>CPU의 메모리 액세스 속도 빠름</li>
<li>운영체제가 관리할 정보량이 적어서 부담이 덜함</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li><span style="background-color: #dcffe4">메모리 할당의 유연성이 떨어짐</span></li>
<li>작은 홀들을 합쳐 충분한 크기의 메모리가 있음에도,
연속된 메모리를 할당할 수 없는 문제가 발생</li>
</ul>
<h3 id="47-버디-시스템">4.7 버디 시스템</h3>
<ul>
<li><p><span style="background-color: #dcffe4">오늘날 리눅스의 커널 메모리 관리에 사용</span>되는 메모리 할당 알고리즘</p>
</li>
<li><p>고정 크기 할당과 가변 크기 할당의 장점을 <span style="background-color: #dcffe4">결합</span>하여 사용</p>
</li>
</ul>
<h2 id="05-세그먼테이션-메모리-관리">05. 세그먼테이션 메모리 관리</h2>
<h3 id="51-세그먼테이션의-개요">5.1 세그먼테이션의 개요</h3>
<p><strong>세그먼트(segment)</strong></p>
<ul>
<li>프로그램을 구성하는 <span style="background-color: #dcffe4">논리적 단위</span></li>
<li>각 세그먼트는 서로 <span style="background-color: #dcffe4">크기가 다르다</span></li>
<li>세그먼트는 <span style="background-color: #dcffe4">코드/데이터/스택/힙 세그먼트</span>로 나뉨</li>
</ul>
<h4 id="세그먼테이션-기법">세그먼테이션 기법</h4>
<ul>
<li>프로세스를 논리 세그먼트들로 나누고, 
각 논리 세그먼트를 물리 메모리에 할당하는 메모리 관리 기법</li>
</ul>
<p><strong>프로세스의 주소 공간</strong></p>
<ul>
<li>프로세스의 주소 공간은 <span style="background-color: #dcffe4">여러 개의 논리 세그먼트들로 구성</span></li>
<li>각 논리 세그먼트는 물리 세그먼트에 매핑</li>
<li>프로세스를 논리 세그먼트로 나누는 과정은 <span style="background-color: #dcffe4">컴파일러와 링커</span>에 의해 이루어짐</li>
<li><ul>
<li><em>컴파일러와 링커는 응용프로그램과 라이브러리의 코드를 모아 
<span style="background-color: #dcffe4">코드 세그먼트를 구성</span>하고, 전역변수들을 모아 <span style="background-color: #dcffe4">데이터 세그먼트 구성</em></span></li>
</ul>
</li>
<li><ul>
<li><em>운영체제와 로더는 실행 파일에 구성된 <span style="background-color: #dcffe4">각 논리 세그먼트를 
물리 세그먼트에 할당</span>, 논리 세그먼트 적재</em></li>
</ul>
</li>
</ul>
<h3 id="52-논리-세그먼트와-물리-세그먼트의-매핑">5.2 논리 세그먼트와 물리 세그먼트의 매핑</h3>
<ul>
<li><p>운영체제는 프로세스의 각 논리 세그먼트가 할당된 
물리 메모리 위치를 관리하기 위해 <span style="background-color: #dcffe4">세그먼트 테이블</span>을 구성함
<img src="https://velog.velcdn.com/images/hi_soap/post/f395b104-8c72-45c4-b8b4-5f7fde4e56eb/image.png" alt=""></p>
</li>
<li><p>세그먼트 테이블에는 <span style="background-color: #dcffe4">세그먼트 <strong>크기</strong>를 담은 limit</span>와
<span style="background-color: #dcffe4">세그먼트 <strong>시작 물리 주소</strong>를 담은 base</span> 정보가 저장된다.</p>
</li>
<li><p>세그먼트 테이블은 <span style="background-color: #dcffe4">시스템에 <strong>1개</strong>만 존재함</span></p>
</li>
<li><p>프로세스의 실행과 종료에 따라 물리 메모리에는 홀이 생기며, 
이로 인해 <span style="background-color: #dcffe4">외부 단편화</span>가 초래됨</p>
</li>
<li><p>새로운 프로세스가 실행되면 적절한 홀을 찾기 위한 <span style="background-color: #dcffe4">홀 선택 알고리즘</span>이나 <span style="background-color: #dcffe4">동적 메모리 할당 알고리즘</span>을 사용함</p>
</li>
</ul>
<h3 id="53-세그먼테이션의-구현">5.3 세그먼테이션의 구현</h3>
<ul>
<li>CPU, 컴파일러와 링커, 운영체제, 로더 등이 모두 세그먼테이션을 지원</li>
<li>세그먼테이션의 구현은 <span style="background-color: #dcffe4">CPU에 매우 의존적</span></li>
</ul>
<h4 id="하드웨어-지원-1">하드웨어 지원</h4>
<p><strong>논리주소 구성</strong>
<code>세그먼테이션의 논리주소 = [세그먼트 번호(s), 옵셋(offset)]</code>
<em>옵셋: 세그먼트 내 상대 주소</em></p>
<h4 id="cpu">CPU</h4>
<ul>
<li>세그먼트 테이블의 시작 주소를 가리키는 레지스터
(segment table base register)</li>
</ul>
<h4 id="mmu-장치">MMU 장치</h4>
<ul>
<li>논리 주소 → 물리 주소 변환 장치</li>
<li>논리 주소가 세그먼트 범위를 넘는지 판별<span style="background-color: #dcffe4">(메모리 보호)</span></li>
<li>(offset ≥ limit) 판별, offset이 더 작아야 함*</li>
<li>논리 주소의 물리 주소 변환<span style="background-color: #dcffe4">(메모리 할당)</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/3691c060-f39c-41ab-b3ef-bc4f003faf3e/image.png" alt=""></p>
<h4 id="세그먼트-테이블">세그먼트 테이블</h4>
<ul>
<li>메모리에 저장</li>
<li>세그먼트별로 <span style="background-color: #dcffe4"><strong>시작 물리 주소</strong>와 세그먼트 <strong>크기 정보</strong></span></li>
<li>세그먼트 테이블의 일부를 <span style="background-color: #dcffe4">MMU 내</span>에 두기도 함</li>
</ul>
<h4 id="운영체제-지원">운영체제 지원</h4>
<ul>
<li><p><span style="background-color: #dcffe4"><strong>할당된 물리 세그먼트들</strong>과 <strong>빈 메모리(홀)</strong> 리스트로 만들고 관리</span></p>
</li>
<li><p>논리 세그먼트를 적재할 <span style="background-color: #dcffe4">물리 세그먼트 <strong>할당·반환</strong> 기능</span></p>
</li>
</ul>
<h4 id="컴파일러-링커-로더-지원">컴파일러, 링커, 로더 지원</h4>
<ul>
<li><p>사용자 프로그램은 컴파일러에 의해 <span style="background-color: #dcffe4">사전에 정의된 세그먼트들로 분할·링킹</span></p>
</li>
<li><p>기계 명령에 들어가는 메모리 주소 <code>[세그먼트 번호, 옵셋]</code> 형식으로 컴파일</p>
</li>
<li><p>로더는 실행 파일에서 만들어진 <span style="background-color: #dcffe4">논리 세그먼트 인지·세그먼트 테이블 갱신</span></p>
</li>
</ul>
<h4 id="54-단편화">5.4 단편화</h4>
<ul>
<li><p>세그먼테이션 기법은 <span style="background-color: #dcffe4">가변 크기</span>로 물리 세그먼트들을 할당하므로
<span style="background-color: #dcffe4">외부 단편화가 필연적으로 발생</span></p>
</li>
<li><p>내부 단편화는 <span style="background-color: #dcffe4">발생하지 않음</span></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[숫자 변환하기]]></title>
            <link>https://velog.io/@hi_soap/%EC%88%AB%EC%9E%90-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hi_soap/%EC%88%AB%EC%9E%90-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 06 May 2026 04:29:56 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.06</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>자연수 x를 y로 변환하려고 합니다. 사용할 수 있는 연산은 다음과 같습니다.</p>
<ul>
<li>x에 n을 더합니다</li>
<li>x에 2를 곱합니다.</li>
<li>x에 3을 곱합니다.
자연수 x, y, n이 매개변수로 주어질 때, x를 y로 변환하기 위해 필요한 최소 연산 횟수를 return하도록 solution 함수를 완성해주세요. 이때 x를 y로 만들 수 없다면 -1을 return 해주세요.</li>
</ul>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>1 ≤ x ≤ y ≤ 1,000,000</li>
<li>1 ≤ n &lt; y</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/1a165639-992c-4988-915a-4d19001c4a86/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1차-실행-오류">1차 실행 오류</h3>
<hr>
<p>메모리 초과
동일한 숫자가 큐에 삽입되는 것이 원인</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int x, int y, int n) {
        int answer = 0;
        Deque&lt;int[]&gt; queue = new ArrayDeque&lt;&gt;();
        queue.add(new int[]{x, 0});

        while(!queue.isEmpty()) {
            int[] cur = queue.poll();
            int X = cur[0], count = cur[1];

            if (X == y) {
                return count;
            }
            if (X * 2 &lt;= y) {
                queue.add(new int[]{X * 2, count + 1});
            }
            if (X * 3 &lt;= y) {
                queue.add(new int[]{X * 3, count + 1});
            }
            if (X + n &lt;= y) {
                queue.add(new int[]{X + n, count + 1});
            }

        }

        return -1;
    }
}</code></pre>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-1시간-7분">소요 시간: 1시간 7분</h4>
<h4 id="시간-복잡도-oy">시간 복잡도: O($y$)</h4>
<hr>
<p><code>x *= 3</code>을 먼저 하고 <code>x &gt; y</code>가 될 때 <code>x /= 3</code>을 하고 <code>x *= 2</code>...
<code>y / x</code>도 해보며 이런저런 규칙을 찾아보려고 했지만,
규칙이 없었다.</p>
<p>claude AI에게 힌트를 요청했더니 <code>BFS</code> 방식으로 풀어야 한다고 알려주었다.</p>
<p>이를 통해 수학적인 규칙이 없는 문제들은 
모든 조건을 탐색해야 한다는 깨달음을 얻었다.</p>
<p>이 문제의 경우 <strong>최소 조건</strong>을 알아내는 문제이므로
모든 조건을 탐색하는 <code>DFS</code>가 아닌, 
최소 조건에 만족하면 바로 종료하는 <code>BFS</code>가 필요했다.</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int x, int y, int n) {
        int answer = 0;
        Deque&lt;int[]&gt; queue = new ArrayDeque&lt;&gt;();
        boolean[] visited = new boolean[y + 1];
        queue.add(new int[]{x, 0});
        visited[x] = true;

        while(!queue.isEmpty()) {
            int[] cur = queue.poll();
            int X = cur[0], count = cur[1];

            if (X == y) {
                return count;
            }
            if (X * 2 &lt;= y &amp;&amp; !visited[X * 2]) {
                queue.add(new int[]{X * 2, count + 1});
                visited[X * 2] = true;
            }
            if (X * 3 &lt;= y &amp;&amp; !visited[X * 3]) {
                queue.add(new int[]{X * 3, count + 1});
                visited[X * 3] = true;
            }
            if (X + n &lt;= y &amp;&amp; !visited[X + n]) {
                queue.add(new int[]{X + n, count + 1});
                visited[X + n] = true;
            }

        }

        return -1;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-oy-1">시간 복잡도: O($y$)</h4>
<hr>
<p>로직은 동일하나, <code>forEach</code> 문을 이용한 가독성을 증가시켰다.</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int x, int y, int n) {
        if (x == y) return 0;

        boolean[] visited = new boolean[y + 1];
        Deque&lt;int[]&gt; queue = new ArrayDeque&lt;&gt;();
        queue.add(new int[]{x, 0});
        visited[x] = true;

        while (!queue.isEmpty()) {
            int[] cur = queue.poll();
            int X = cur[0], count = cur[1];

            for (int nx : new int[]{X * 2, X * 3, X + n}) {
                if (nx == y) return count + 1;
                if (nx &lt; y &amp;&amp; !visited[nx]) {
                    visited[nx] = true;
                    queue.add(new int[]{nx, count + 1});
                }
            }
        }

        return -1;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[택배상자]]></title>
            <link>https://velog.io/@hi_soap/%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</link>
            <guid>https://velog.io/@hi_soap/%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90</guid>
            <pubDate>Tue, 05 May 2026 05:34:25 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.05</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>영재는 택배상자를 트럭에 싣는 일을 합니다. 영재가 실어야 하는 택배상자는 크기가 모두 같으며 1번 상자부터 n번 상자까지 번호가 증가하는 순서대로 컨테이너 벨트에 일렬로 놓여 영재에게 전달됩니다. 컨테이너 벨트는 한 방향으로만 진행이 가능해서 벨트에 놓인 순서대로(1번 상자부터) 상자를 내릴 수 있습니다. 하지만 컨테이너 벨트에 놓인 순서대로 택배상자를 내려 바로 트럭에 싣게 되면 택배 기사님이 배달하는 순서와 택배상자가 실려 있는 순서가 맞지 않아 배달에 차질이 생깁니다. 따라서 택배 기사님이 미리 알려준 순서에 맞게 영재가 택배상자를 실어야 합니다.</p>
<p>만약 컨테이너 벨트의 맨 앞에 놓인 상자가 현재 트럭에 실어야 하는 순서가 아니라면 그 상자를 트럭에 실을 순서가 될 때까지 잠시 다른 곳에 보관해야 합니다. 하지만 고객의 물건을 함부로 땅에 둘 수 없어 보조 컨테이너 벨트를 추가로 설치하였습니다. 보조 컨테이너 벨트는 앞 뒤로 이동이 가능하지만 입구 외에 다른 면이 막혀 있어서 맨 앞의 상자만 뺄 수 있습니다(즉, 가장 마지막에 보조 컨테이너 벨트에 보관한 상자부터 꺼내게 됩니다). 보조 컨테이너 벨트를 이용해도 기사님이 원하는 순서대로 상자를 싣지 못 한다면, 더 이상 상자를 싣지 않습니다.</p>
<p>예를 들어, 영재가 5개의 상자를 실어야 하며, 택배 기사님이 알려준 순서가 기존의 컨테이너 벨트에 네 번째, 세 번째, 첫 번째, 두 번째, 다섯 번째 놓인 택배상자 순서인 경우, 영재는 우선 첫 번째, 두 번째, 세 번째 상자를 보조 컨테이너 벨트에 보관합니다. 그 후 네 번째 상자를 트럭에 싣고 보조 컨테이너 벨트에서 세 번째 상자 빼서 트럭에싣습니다. 다음으로 첫 번째 상자를 실어야 하지만 보조 컨테이너 벨트에서는 두 번째 상자를, 기존의 컨테이너 벨트에는 다섯 번째 상자를 꺼낼 수 있기 때문에 더이상의 상자는 실을 수 없습니다. 따라서 트럭에는 2개의 상자만 실리게 됩니다.</p>
<p>택배 기사님이 원하는 상자 순서를 나타내는 정수 배열 order가 주어졌을 때, 영재가 몇 개의 상자를 실을 수 있는지 return 하는 solution 함수를 완성하세요.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>1 ≤ order의 길이 ≤ 1,000,000</li>
<li>order는 1이상 order의 길이 이하의 모든 정수가 한번씩 등장합니다.</li>
<li>order[i]는 기존의 컨테이너 벨트에 order[i]번째 상자를 i+1번째로 트럭에 실어야 함을 의미합니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/a00b8dcc-ca7b-4513-8500-f097b64af83c/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1차-실행-오류">1차 실행 오류</h3>
<hr>
<p>문제를 제대로 이해하지 못함
<code>orders</code> 배열의 원소 그 자체가 내려야 하는 순서로 이해함</p>
<p>그게 아니라 <code>order[0] = 4</code> 이면 첫번째로 내려야 하는 상자는
<code>order[3]</code>이 되는 것임</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int[] orders) {
        Deque&lt;Integer&gt; container = new ArrayDeque&lt;&gt;();
        Deque&lt;Integer&gt; subContainer = new ArrayDeque&lt;&gt;();
        int order = 1;
        for (int i = 0; i &lt; orders.length; i++) {
            if (orders[i] == order) { // 메인 컨테이너로 넣기
                container.push(orders[i]);
                order++;
            }
            else { // 서브 컨테이너로 넣기
                subContainer.push(orders[i]);
            }
        }
        while(!subContainer.isEmpty()) {
            if (subContainer.peek() == order) {
                container.push(subContainer.pop());
                order++;
            }
            else {
                break;
            }
        }
        return container.size();
    }
}</code></pre>
<h3 id="2차-실행-오류">2차 실행 오류</h3>
<hr>
<p><code>orders = [1, 2, 3, 4, 5]</code> 인 경우,
<code>i = 0</code>일 때 <code>subContainer</code>에 <code>[1, 1]</code> 삽입 후
<code>i = 1</code>일 때 <code>subContainer</code>에서 <code>pop()</code> 후에 <code>[2, 2]</code> 가 저장이 되지 않음</p>
<p>조건문이 잘못되었음</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;
import java.util.Map;

class Solution {
    public int solution(int[] orders) {
        Deque&lt;Integer&gt; container = new ArrayDeque&lt;&gt;();
        Deque&lt;int[]&gt; subContainer = new ArrayDeque&lt;&gt;();
        int index = 0;
        for (int i = 0; i &lt; orders.length; i++) {
            if (subContainer.peek() != null &amp;&amp; orders[index] == subContainer.peek()[0]) {
                container.push(subContainer.pop()[1]);
                index++;
            }
            else { // 서브 컨테이너로 넣기
                subContainer.push(new int[]{i + 1, orders[i]}); // 순서와 값 저장
            }
        }
        while(!subContainer.isEmpty()) {
            if (subContainer.peek()[0] == orders[index]) {
                container.push(subContainer.pop()[1]);
                index++;
            }
            else {
                break;
            }
        }
        return container.size();
    }
}</code></pre>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-1시간-3분">소요 시간: 1시간 3분</h4>
<h4 id="시간-복잡도-on">시간 복잡도: O($n$)</h4>
<hr>
<p><code>Stack</code>을 이용하였다. 
상자 하차 순서를 저장한 <code>orders</code> 배열을 <code>index</code>로 추적하게 하였고,
우선 <code>subContainer</code>에 상자를 넣은 후, 
하차 순서와 비교해가며 꺼낼 수 있을 때 까지 비교하는 과정을 반복하였다.</p>
<p>그 후 <code>subContainer</code>에 남은 값을 꺼낼 수 있는지 루프를 돌아 마무리하였다.</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int[] orders) {
        Deque&lt;Integer&gt; container = new ArrayDeque&lt;&gt;();
        Deque&lt;int[]&gt; subContainer = new ArrayDeque&lt;&gt;();
        int index = 0;
        for (int i = 0; i &lt; orders.length; i++) {
            subContainer.push(new int[]{i + 1, orders[i]});
            while(true) {
                if (subContainer.peek() != null &amp;&amp; orders[index] == subContainer.peek()[0]) {
                    container.push(subContainer.pop()[1]);
                    index++;
                }
                else {
                    break;
                }
            }
        }
        while(!subContainer.isEmpty()) {
            if (subContainer.peek()[0] == orders[index]) {
                container.push(subContainer.pop()[1]);
                index++;
            }
            else {
                break;
            }
        }
        return container.size();
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on-1">시간 복잡도: O($n$)</h4>
<hr>
<p>메인 컨테이너에는 어떤 값이 들어있어도 관계 없기 때문에
<code>container</code>를 <code>Stack</code>으로 정의할 필요가 없음</p>
<p><code>container</code>를 <code>count</code>로 대체하고, <code>pop()[1]</code>같은 경우도 그냥 <code>pop()</code>을 사용함</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int solution(int[] orders) {
        Deque&lt;int[]&gt; subContainer = new ArrayDeque&lt;&gt;();
        int index = 0;
        int count = 0;

        for (int i = 0; i &lt; orders.length; i++) {
            subContainer.push(new int[]{i + 1, orders[i]});
            while (!subContainer.isEmpty() &amp;&amp; orders[index] == subContainer.peek()[0]) {
                subContainer.pop();
                count++;
                index++;
            }
        }
        while (!subContainer.isEmpty()) {
            if (orders[index] == subContainer.peek()[0]) {
                subContainer.pop();
                count++;
                index++;
            } else {
                break;
            }
        }

        return count;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[압축]]></title>
            <link>https://velog.io/@hi_soap/%EC%95%95%EC%B6%95</link>
            <guid>https://velog.io/@hi_soap/%EC%95%95%EC%B6%95</guid>
            <pubDate>Mon, 04 May 2026 04:15:03 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.04</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>신입사원 어피치는 카카오톡으로 전송되는 메시지를 압축하여 전송 효율을 높이는 업무를 맡게 되었다. 메시지를 압축하더라도 전달되는 정보가 바뀌어서는 안 되므로, 압축 전의 정보를 완벽하게 복원 가능한 무손실 압축 알고리즘을 구현하기로 했다.</p>
<p>어피치는 여러 압축 알고리즘 중에서 성능이 좋고 구현이 간단한 LZW(Lempel–Ziv–Welch) 압축을 구현하기로 했다. LZW 압축은 1983년 발표된 알고리즘으로, 이미지 파일 포맷인 GIF 등 다양한 응용에서 사용되었다.</p>
<p>LZW 압축은 다음 과정을 거친다.</p>
<ol>
<li>길이가 1인 모든 단어를 포함하도록 사전을 초기화한다.</li>
<li>사전에서 현재 입력과 일치하는 가장 긴 문자열 w를 찾는다.</li>
<li>w에 해당하는 사전의 색인 번호를 출력하고, 입력에서 w를 제거한다.</li>
<li>입력에서 처리되지 않은 다음 글자가 남아있다면(c), w+c에 해당하는 단어를 사전에 등록한다.</li>
<li>단계 2로 돌아간다.
압축 알고리즘이 영문 대문자만 처리한다고 할 때, 사전은 다음과 같이 초기화된다. 사전의 색인 번호는 정수값으로 주어지며, 1부터 시작한다고 하자.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/b13bebe0-9f31-4a27-b93e-30e7b418b846/image.png" alt=""></p>
<p>예를 들어 입력으로 KAKAO가 들어온다고 하자.</p>
<ol>
<li>현재 사전에는 KAKAO의 첫 글자 K는 등록되어 있으나, 두 번째 글자까지인 KA는 없으므로, 첫 글자 K에 해당하는 색인 번호 11을 출력하고, 다음 글자인 A를 포함한 KA를 사전에 27 번째로 등록한다.</li>
<li>두 번째 글자 A는 사전에 있으나, 세 번째 글자까지인 AK는 사전에 없으므로, A의 색인 번호 1을 출력하고, AK를 사전에 28 번째로 등록한다.</li>
<li>세 번째 글자에서 시작하는 KA가 사전에 있으므로, KA에 해당하는 색인 번호 27을 출력하고, 다음 글자 O를 포함한 KAO를 29 번째로 등록한다.</li>
<li>마지막으로 처리되지 않은 글자 O에 해당하는 색인 번호 15를 출력한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/8198bab9-740f-4801-bc26-8b413c5a9aa3/image.png" alt=""></p>
<p>이 과정을 거쳐 다섯 글자의 문장 KAKAO가 4개의 색인 번호 [11, 1, 27, 15]로 압축된다.</p>
<p>입력으로 TOBEORNOTTOBEORTOBEORNOT가 들어오면 다음과 같이 압축이 진행된다.</p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c03487bd-190d-4441-b046-108320f484fc/image.png" alt=""></p>
<h2 id="입력-형식">입력 형식</h2>
<p>입력으로 영문 대문자로만 이뤄진 문자열 msg가 주어진다. msg의 길이는 1 글자 이상, 1000 글자 이하이다.</p>
<h2 id="출력-형식">출력 형식</h2>
<p>주어진 문자열을 압축한 후의 사전 색인 번호를 배열로 출력하라.</p>
<h2 id="입출력-예제">입출력 예제</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/4a975ec5-452e-40b1-bdba-6a07cb248ecf/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-1시간-1분">소요 시간: 1시간 1분</h4>
<h4 id="시간-복잡도-on2">시간 복잡도: O($n^2$)</h4>
<hr>
<p><code>HashMap</code>을 사용하여 해시맵에 존재하지 않을 때 까지 문자를 계속 더한다.</p>
<p>존재하지 않는 문자가 나올 경우엔 해당 문자를 제외한 해시맵 값을 리스트에 저장</p>
<p>제외한 값은 다시 문자의 시작이 됨</p>
<hr>
<pre><code class="language-java">import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

class Solution {
    public int[] solution(String msg) {
        int index = 1;
        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();

        // 해시맵에 색인 번호와 단어 추가
        for (char c = &#39;A&#39;; c &lt;= &#39;Z&#39;; c++) {
            map.put(String.valueOf(c), index++);
        }
        String str = &quot;&quot;;

        for (int i = 0; i &lt; msg.length(); i++) {
            char c = msg.charAt(i);
            str += c;
            if (map.get(str) == null) { // 해시맵에 해당 키 값이 존재하지 않을 때
                map.put(str, index++); // 해시맵에 해당 값 저장
                list.add(map.get(str.substring(0, str.length() - 1))); // 이전의 문자열의 해시맵 값을 저장
                str = String.valueOf(c);
            }
        }
        if (str != &quot;&quot;) { // 마지막 남은 문자 삽입
            list.add(map.get(str));
        }

        return list.stream().mapToInt(Integer::intValue).toArray();
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on">시간 복잡도: O($n$)</h4>
<hr>
<p><code>str +=</code>는 매번 새로운 객체를 생성하므로
<code>StringBuilder</code> 사용</p>
<hr>
<pre><code class="language-java">import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

class Solution {
    public int[] solution(String msg) {
        int index = 1;
        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();

        for (char c = &#39;A&#39;; c &lt;= &#39;Z&#39;; c++) {
            map.put(String.valueOf(c), index++);
        }

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i &lt; msg.length(); i++) {
            sb.append(msg.charAt(i));
            String str = sb.toString();
            if (!map.containsKey(str)) {
                map.put(str, index++);
                list.add(map.get(sb.substring(0, sb.length() - 1)));
                sb.setLength(0);
                sb.append(msg.charAt(i));
            }
        }
        if (sb.length() &gt; 0) {
            list.add(map.get(sb.toString()));
        }

        return list.stream().mapToInt(Integer::intValue).toArray();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spec-Kit_세션은 어떻게 누적되고 복원되는가?]]></title>
            <link>https://velog.io/@hi_soap/Spec-Kit%EC%84%B8%EC%85%98%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%88%84%EC%A0%81%EB%90%98%EA%B3%A0-%EB%B3%B5%EC%9B%90%EB%90%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@hi_soap/Spec-Kit%EC%84%B8%EC%85%98%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%88%84%EC%A0%81%EB%90%98%EA%B3%A0-%EB%B3%B5%EC%9B%90%EB%90%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Sun, 03 May 2026 14:03:21 GMT</pubDate>
            <description><![CDATA[<h2 id="두-가지-핵심-질문">두 가지 핵심 질문</h2>
<ul>
<li><p><strong>Q1.</strong> 한 세션에서 단계가 진행될수록, <strong><span style="background-color: #dcffe4">컨텍스트는 가벼워지는가? 무거워지는가?</strong></span></p>
</li>
<li><p><strong>Q2.</strong> 세션이 끊기고 새 세션을 시작하면 <span style="background-color: #dcffe4"><strong>AI는 무엇을 기억하고 무엇을 잊나?</strong></span></p>
</li>
</ul>
<h2 id="실제-컨텍스트-흐름">실제 컨텍스트 흐름</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/61ed8702-2bb3-4e75-a32a-1edd3a34777b/image.png" alt="">
<img src="https://velog.velcdn.com/images/hi_soap/post/ab44a4f7-e39b-4be9-9748-8baac6315120/image.png" alt="">
<strong><em>이전 단계에서 진행한 모든 컨텍스트를 다시 읽음. 
<span style="background-color: #dcffe4">단계가 진행될수록 컨텍스트가 누적되고 읽어야 할 컨텍스트가 많아짐</span></em></strong></p>
<h2 id="spec-kit-작동-원리">Spec-Kit 작동 원리</h2>
<h4 id="큰-컨텍스트-윈도우">큰 컨텍스트 윈도우</h4>
<ul>
<li>200k의 컨텍스트 윈도우를 가짐, (보통 프로젝트는 100k 이하)<h4 id="잘-구조화된-문서">잘 구조화된 문서</h4>
</li>
<li><code>spec.md</code>, <code>plan.md</code>는 정형화된 형식이라 토큰 효율이 좋음<h4 id="헌법이-닻-역할">헌법이 닻 역할</h4>
</li>
<li>헌법 원칙은 항상 참조</li>
<li>일관성의 기준점</li>
</ul>
<h2 id="한계-큰-프로젝트의-경우">한계: 큰 프로젝트의 경우</h2>
<h4 id="컨텍스트-오버플로">컨텍스트 오버플로</h4>
<ul>
<li>사용자 스토리 10개 + 또는 작업 50개 + → 200k 한도 초과 시 앞부분 잘림</li>
</ul>
<h4 id="implement-후반-품질-저하">Implement 후반 품질 저하</h4>
<ul>
<li>T001 깨끗 → T030은 100K + 누적 
→ 뒷부분 작업 품질이 앞부분만 못함 (<em>context rot</em>)</li>
</ul>
<h4 id="토큰-비용-증가">토큰 비용 증가</h4>
<ul>
<li>매 명령마다 누적 컨텍스트 전체 처리 → 후반으로 갈수록 한 번 호출 비용↑</li>
</ul>
<h2 id="대응-전략-세션-분리">대응 전략: 세션 분리</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/030e4dd0-6a3b-4b98-86fa-03fde31f540e/image.png" alt="">
<em>큰 프로젝트는 의도적으로 세션을 끊을 것</em></p>
<h3 id="세션-분리-후">세션 분리 후</h3>
<h4 id="사라지는-것">사라지는 것</h4>
<ul>
<li>이전 대화 기록</li>
<li>AI가 기억하던 미묘한 맥락</li>
<li>진행 중이던 작업의 상태</li>
<li>임시 결정 사항</li>
</ul>
<h4 id="남는-것">남는 것</h4>
<ul>
<li>모든 <code>.specify/</code> 파일들</li>
<li>작성 코드 파일들</li>
<li><code>tasks.md</code>의 <code>[X]</code> 마커</li>
<li>git 히스토리</li>
</ul>
<p><span style="background-color: #dcffe4"><strong><em>영속적인 것에 의존하는 것이 spec-kit의 핵심</em></strong></span></p>
<h3 id="새-세션-시작-후-t009부터-다시-시작할-때">새 세션 시작 후: T009부터 다시 시작할 때</h3>
<h4 id="복원되는-컨텍스트">복원되는 컨텍스트</h4>
<ul>
<li><code>constitution.md</code></li>
<li><code>tasks.md</code></li>
<li>T009 관련 문서</li>
<li>ex) <code>constracts/storage.md</code>*</li>
</ul>
<h3 id="시나리오-별-위험도">시나리오 별 위험도</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/842e230e-ac65-410d-9753-73f27444a474/image.png" alt="">
<em>끊는 지점은 깔끔한 작업 완료 시점</em></p>
<h4 id="implement-중간-상태-3가지">Implement 중간 상태 3가지</h4>
<ul>
<li><p><strong>1) T008이 끝난 후 T009은 시작도 하지 않은 상태 → OK!</strong></p>
</li>
<li><p><strong>2) T009 코드를 절반쯤 쓰다가 종료한 경우</strong>
<code>tasks.md</code>에는 T009가 빈 박스이지만, 
코드 폴더에는 T009의 일부 코드가 존재
다음 세션의 AI는 T009를 처음부터 다시 시작하려고 함
→ 이미 존재하는 절반의 코드와 충돌 가능</p>
</li>
<li><p><strong>3) TDD 중간 단계: 테스트는 작성했지만, 구현은 하지 않은 상태</strong>
→ AI가 이를 어떻게 해석할 지 애매함</p>
</li>
</ul>
<h3 id="안전한-운영-체크리스트">안전한 운영 체크리스트</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/f67f3d0c-878b-4ae7-aec3-4cbb9d294716/image.png" alt="">
<strong>종료 전 체크</strong></p>
<ul>
<li><code>tasks.md</code>에 체크되어 있지 않다면, 수동으로 체크할 것</li>
<li>대화 중 결정 사항이 <code>spec.md</code>, <code>plan.md</code>에 반영되었는지 확인</li>
<li>다음에 무슨 작업을 할 지 <code>tasks.md</code>나 <code>README</code>에 작성하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spec-Kit_AI는 어떻게 기억하는가?]]></title>
            <link>https://velog.io/@hi_soap/Spec-KitAI%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B8%B0%EC%96%B5%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@hi_soap/Spec-KitAI%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B8%B0%EC%96%B5%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Sun, 03 May 2026 13:18:58 GMT</pubDate>
            <description><![CDATA[<h2 id="오늘의-핵심-질문">오늘의 핵심 질문</h2>
<p>세션이 중단되고 다시 시작할 때,
<span style="background-color: #dcffe4"><strong>매 단계마다 모든 문서를 다시 읽어야 하나?</strong></span></p>
<p>정답은 <span style="background-color: #dcffe4"><strong>필요한 것만 선택적으로 읽기</strong></span></p>
<p><span style="background-color: #dcffe4"><strong><em>단, 새로 읽는 파일은 선택적이지만, 이미 읽은 내용은 컨텍스트에 누적됨</em></strong></span></p>
<h2 id="ai는-정말-기억하는가">AI는 정말 &quot;기억&quot;하는가?</h2>
<p>AI는 <span style="background-color: #dcffe4"><strong>기억하지 않는다</strong></span></p>
<p>정답은 <strong><span style="background-color: #dcffe4">파일을 AI의 메모리처럼 쓴다</span></strong></p>
<h2 id="문서-계층과-참조-방향">문서 계층과 참조 방향</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/2a561e15-c106-48ab-b6c1-d25c1477da98/image.png" alt=""></p>
<p>각 단계에서 읽어야 할 문서가 <span style="background-color: #dcffe4">자동으로 결정됨</span>
<em>모든 것을 다 읽을 필요가 없음</em></p>
<p><strong>단계별 읽기 패턴</strong>
<img src="https://velog.velcdn.com/images/hi_soap/post/0f9de5a7-a9e4-4bd6-82af-05bf54d94bc4/image.png" alt=""></p>
<p><strong>4가지 핵심 원칙</strong></p>
<p><strong>1) 피라미드 위→아래 참조</strong></p>
<ul>
<li>아래 단계는 <strong><span style="background-color: #dcffe4">위 문서를 읽음</span></strong></li>
<li>위 단계는 <strong><span style="background-color: #dcffe4">아래를 무시</span></strong></li>
</ul>
<p><strong>2) Constitution은 항상 읽힘</strong></p>
<ul>
<li>7단계 중 6단계가 참조</li>
<li>공통 context의 닻 (anchor)</li>
</ul>
<p><strong>3) 템플릿 vs 인스턴스 분리</strong></p>
<ul>
<li><strong>템플릿</strong>: <span style="background-color: #dcffe4">한번 읽으면 끝</span></li>
<li><strong>인스턴스</strong>: <span style="background-color: #dcffe4">단계별로 필요한 것만</span></li>
</ul>
<p><strong>4) Tasks.md가 체크포인트</strong></p>
<ul>
<li><code>[X]</code> 마커로 진행 상황 기록</li>
<li>재개 시 <span style="background-color: #dcffe4">복구 포인트</span></li>
</ul>
<h2 id="재개-시나리오">재개 시나리오</h2>
<h3 id="a-constitution-작성-후-speckitspecify-실행">A) constitution 작성 후, /speckit.specify 실행</h3>
<p><strong>AI가 읽는 파일</strong></p>
<ul>
<li><code>.specify/memory/constitution.md</code></li>
<li><code>.specify/templates.spec-template.md // spec을 작성할 때의 형식을 명시</code></li>
</ul>
<p><strong>읽지 않아도 되는 것</strong></p>
<ul>
<li>과거 세션1의 대화 기록</li>
<li>헌법 작성 시의 토론 내용</li>
<li>이전 명령어 히스토리</li>
</ul>
<p><em>결국 <span style="background-color: #dcffe4">이전 대화 내용</span>은 읽지 않아도 됨</em></p>
<h3 id="b-t001--t008-완료-t009-중-실행-중단">B) T001 ~ T008 완료, T009 중 실행 중단</h3>
<p><strong>읽는 것</strong></p>
<ul>
<li><code>tasks.md</code>(체크포인트)</li>
<li><code>constitution.md</code> (T009만 참조)</li>
</ul>
<p><strong>읽지 않는 것</strong></p>
<ul>
<li><code>research.md</code>(기술 결정 끝)</li>
<li>완료된 소스 파일</li>
<li>이전 세션 대화 기록</li>
</ul>
<p>*<span style="background-color: #dcffe4"><code>tasks.md</code>의 <code>[x]</code>마커가 세션 복구 포인트</span>*
<em>(T009부터 재개, T009가 참조하는 문서만 <span style="background-color: #dcffe4">선택적 로딩</span>)</em></p>
<h2 id="파일-시스템--외부-메모리">파일 시스템 = 외부 메모리</h2>
<ul>
<li><strong>세션 간 상태 유지</strong></li>
<li><strong>병렬 작업 가능</strong></li>
<li><strong>도구 간 이식성</strong></li>
</ul>
<h2 id="한계">한계</h2>
<h4 id="코드-문서-불일치">코드-문서 불일치</h4>
<ul>
<li>직접 코드 수정 시, <code>tasks.md</code> 상태와 어긋남 → 수동 동기화 필요<h4 id="뉘앙스-손실">뉘앙스 손실</h4>
</li>
<li>미묘한 맥락은 대화에서만 → Clarification에 명시 필수<h4 id="기능-간-얽힘">기능 간 얽힘</h4>
</li>
<li>여러 기능이 같은 data-model 수정 시 복잡도 ↑ → <code>/speckit.analyze</code> 필수<h4 id="디버깅-부적합">디버깅 부적합</h4>
</li>
<li>해당 에러에 대한 질문은 대화형이 빠름 → spec-kit 외부에서 디버깅 후 반영</li>
</ul>
<h2 id="마무리">마무리</h2>
<h4 id="ai는-기억하지-않고-span-stylebackground-color-dcffe4문서가-기억한다span">AI는 기억하지 않고, <span style="background-color: #dcffe4">문서가 기억한다.</span></h4>
<h4 id="한번에-다-밀어넣지-말고-span-stylebackground-color-dcffe4필요한-것만-읽혀라span">한번에 다 밀어넣지 말고, <span style="background-color: #dcffe4">필요한 것만 읽혀라</span></h4>
<h4 id="세션이-끊겨도-괜찮다-span-stylebackground-color-dcffe4파일이-남아있다span">세션이 끊겨도 괜찮다, <span style="background-color: #dcffe4">파일이 남아있다.</span></h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spec-Kit]]></title>
            <link>https://velog.io/@hi_soap/Spec-Kit</link>
            <guid>https://velog.io/@hi_soap/Spec-Kit</guid>
            <pubDate>Sun, 03 May 2026 08:48:39 GMT</pubDate>
            <description><![CDATA[<h2 id="spec-kit">Spec-Kit</h2>
<ul>
<li>GitHub에서 공개한 <span style="background-color: #dcffe4">오픈소스 툴킷</span></li>
<li>SDD를 실천할 때에 필요한 것들을 한 상자에 모아놓은 것</li>
</ul>
<h3 id="구성-요소">구성 요소</h3>
<ul>
<li><p><strong>Specify CLI</strong>
프로젝트 초기 세팅 도구
프로젝트 폴더 구조 자동 생성</p>
</li>
<li><p><strong>템플릿 모음</strong>
명세·계획·작업 표준 양식
명세서는 어떻게 생겨야 하는지, 계획서에는 어떠한 항목들이 들어가야 하는지</p>
</li>
<li><p><strong>슬래시 명령어</strong>
/speckit.* AI 역할 정의
AI와 대화할 때 특별한 명령어를 쓸 수 있게 해줌</p>
</li>
</ul>
<h3 id="7단계-워크플로-전체-그림">7단계 워크플로 전체 그림</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/977b4858-73bf-454a-8137-1dd9d884e783/image.png" alt="">
<span style="background-color: #dcffe4">각 단계의 산출물이 다음 단계의 입력이 됨</span></p>
<h4 id="1-constitution">1. Constitution</h4>
<p><strong><code>.specify/memory/constitution.md</code></strong></p>
<ul>
<li>프로젝트의 헌법</li>
<li>AI의 시키지도 않은 일 까지 하려는 경향을 원천 차단</li>
</ul>
<p><em>예)</em></p>
<ul>
<li><em>모든 코드 테스트를 먼저 작성(TDD)</em></li>
<li><em>프로젝트는 최대 3개까지만</em></li>
<li><em>프레임워크는 직접 사용, 래퍼 금지</em></li>
<li><em>API 응답은 500ms 이내</em></li>
</ul>
<p><span style="color: red">위반 시 ERROR로 중단·모든 단계의 기준점</span></p>
<h4 id="2-문서-계층-구조">2. 문서 계층 구조</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/19a4be22-3286-4d37-a0ad-dfb000eb0e2a/image.png" alt=""></p>
<ul>
<li>위로 갈수록 &#39;왜&#39;(철학)</li>
<li>아래로 갈수록 &#39;어떻게&#39;(실행)</li>
<li>변경은 <span style="background-color: #dcffe4">위→아래로만 전파</span></li>
<li>관심사 분리: 각 단계는 자기 차원의 고민만</li>
<li>spec: 요구사항, plan: 기술, Tasks: 순서*</li>
<li>이러한 구조 덕분에 프로젝트가 커져도 <span style="background-color: #dcffe4">추적이 가능함</span></li>
</ul>
<h4 id="3-자동-검증-장치">3. 자동 검증 장치</h4>
<ul>
<li><p><strong>Constitution Check: Plan 단계</strong>
기술 선택이 헌법 위반이 아닌지 확인
위반 시 ERROR로 중단</p>
</li>
<li><p><strong>Analyze Cross-Validation: TASKS 후</strong>
spec·plan·tasks 모순 검증
CRITICAL / HIGH / MEDIUM 심각도</p>
</li>
<li><p><strong>TDD GATE: Implement 단계</strong>
테스트 없이 코드 작성 금지
Red→Green 사이클 강제</p>
</li>
</ul>
<p><em>AI를 자유롭게 풀어주는 것이 아닌, <span style="background-color: #dcffe4">구조 안에서 자유롭게 움직이게 하는 것</span></em></p>
<h3 id="8개-명령어-전체-요약">8개 명령어 전체 요약</h3>
<h4 id="핵심-명령5개-필수">핵심 명령(5개, 필수)</h4>
<p><code>/speckit.constitution</code>: 헌법 수립
<code>/speckit.specify</code>: 명세 작성
<code>/speckit.plan</code>: 기술 설계
<code>/speckit.tasks</code>: 작업 분해
<code>/speckit.implement</code>: 코드 구현</p>
<h4 id="선택-명령3개-품질">선택 명령(3개, 품질)</h4>
<p><code>/speckit.clarify</code>: spec 모호할 때
<code>/speckit.checklist</code>: 도메인별 품질
<code>/speckit.analyze</code>: 구현 전 검증</p>
<h3 id="3가지-핵심">3가지 핵심</h3>
<h4 id="1-말하기-전에-쓰기">1. 말하기 전에 쓰기</h4>
<ul>
<li><span style="background-color: #dcffe4">명세가 먼저</span>, 코드는 그 다음. AI는 우리 머릿속을 보지 못함<h4 id="2-ai의-자유를-제한하라">2. AI의 자유를 제한하라</h4>
</li>
<li><span style="background-color: #dcffe4">헌법·게이트·체크리스트</span>가 일관된 결과를 만든다.<h4 id="3-문서는-공통-언어다">3. 문서는 공통 언어다</h4>
</li>
<li><code>spec.md</code>는 팀원과도, AI와도, 미래의 나와도 소통하는 매개체</li>
</ul>
<h3 id="specify">Specify</h3>
<p><strong>1) <code>spec.md</code> - user story</strong></p>
<ul>
<li>Given, When, Then</li>
</ul>
<p><strong>2) Functional Requirements(FR-001~)</strong></p>
<ul>
<li>시스템이 <span style="background-color: #dcffe4">반드시 해야할 일</span></li>
</ul>
<p><strong>3) Success Criteria(SC-001~)</strong></p>
<ul>
<li><span style="background-color: #dcffe4">성공 기준</span></li>
<li>측정 가능한 지표로만 작성*</li>
</ul>
<p><strong>4) Assumptions</strong></p>
<ul>
<li><span style="background-color: #dcffe4">가정</span></li>
</ul>
<p><strong>NEEDS CLARIFICATION</strong></p>
<ul>
<li>AI가 확신하지 못한 부분을 명시적으로 남김</li>
<li>다음 단계인 <code>clarify</code> 단계에서 Q&amp;A로 해결함*</li>
</ul>
<h3 id="clarify">Clarify</h3>
<ul>
<li><span style="background-color: #dcffe4">NEEDS CLARIFICATION이 있을 때만</span> 실행</li>
</ul>
<h3 id="plan">Plan</h3>
<ul>
<li><span style="background-color: #dcffe4">기술 스택을 결정</span>하는 단계</li>
<li><strong>Constitution Check</strong>
Plan의 내용이 Constitution에 대해 원칙을 지키는지 점검</li>
<li>하나라도 만족하지 못할 시, 진행 불가*</li>
</ul>
<p><strong>1) research.md</strong></p>
<ul>
<li><span style="background-color: #dcffe4">기술 선택에 대한 근거</span><pre><code>Decision: useReducer
Rationale: 배열 상태 단순
Alternatives:
  Redux, Zustand</code></pre></li>
</ul>
<p><strong>2) data-model.md</strong></p>
<ul>
<li><span style="background-color: #dcffe4">데이터 구조</span><pre><code>interface Todo {
  id: string;
  date: string;
  text: string;
  completed: boolean;
}</code></pre></li>
</ul>
<p><strong>3) contracts/storage.md</strong></p>
<pre><code>// localStorage 스키마
key: &quot;calendar-todos&quot;
value: Todo[]
        (JSON 배열)</code></pre><p><strong>4) quickstart.md</strong></p>
<ul>
<li>앱이 제대로 만들어졌는지 <span style="background-color: #dcffe4">검증할 시나리오</span></li>
</ul>
<p><strong>5) plan.md</strong></p>
<ul>
<li>위 모든 문서들을 <span style="background-color: #dcffe4">총괄하는 문서</span></li>
</ul>
<p><em>5개의 문서는 <span style="background-color: #dcffe4">서로 일관되어야 함</em></span>
<em>데이터 모델의 타입이 컨트랙트에 나타나야 하며,
스펙의 성공 기준이 퀵스타트에서 검증되어야 하는 등
→ <code>analyze</code> 단계에서 체크함</em></p>
<h3 id="tasks">Tasks</h3>
<ul>
<li>플랜을 실제 손으로 쓸 수 있는 <span style="background-color: #dcffe4">작업 단위로 쪼갬</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e022e6de-d273-46e2-8098-7503825120dc/image.png" alt=""></p>
<ul>
<li><span style="background-color: #dcffe4">phase</span>로 구분함</li>
<li><strong>[P]</strong>: parallel, 병렬 실행 가능</li>
</ul>
<h3 id="analyze">Analyze</h3>
<ul>
<li>지금까지 만든 4개의 문서 
<code>spec, plan, taks, constitution</code>이 서로 모순되지 않는지 검사</li>
<li><span style="color: red">파일을 절대 수정하지 않음, 읽기 전용</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/a49e9300-a20b-49a8-980e-3f4ed767ec80/image.png" alt=""></p>
<p><span style="background-color: #dcffe4">CRITICAL, HIGH는 반드시 해결</span></p>
<h3 id="implement">Implement</h3>
<ul>
<li><span style="background-color: #dcffe4">실제 코드 작성</span></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스_SQL]]></title>
            <link>https://velog.io/@hi_soap/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4SQL-4md3p0tz</link>
            <guid>https://velog.io/@hi_soap/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4SQL-4md3p0tz</guid>
            <pubDate>Sun, 03 May 2026 08:09:44 GMT</pubDate>
            <description><![CDATA[<h2 id="집합-연산자">집합 연산자</h2>
<p><code>&lt;student&gt;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/33c19947-a6d9-4ba4-9d78-6b16f89d53e3/image.png" alt=""></p>
<p><code>&lt;instructor&gt;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/91fcc0fd-56e9-4627-95bb-285958f1e1a2/image.png" alt=""></p>
<h3 id="union">Union</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/7ab2e126-2ec5-460a-88f9-36059538a909/image.png" alt=""></p>
<p><code>select * from student union select * from instructor;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/0db1e51f-bc95-4180-a7f9-963d315e0472/image.png" alt=""></p>
<ul>
<li>연산 시, <span style="background-color: #dcffe4">열의 개수와 각 열의 타입</span>이 같아야 함</li>
<li>기본적으로 <span style="background-color: #dcffe4">중복을 제거</span>하여 출력함</li>
<li>중복을 허용하고 싶다면 <code>union all</code> 사용*</li>
</ul>
<h3 id="intersect">intersect</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/aab6bb2d-7243-4a4d-8c06-c65fa1e2300a/image.png" alt=""></p>
<p><code>select dept_name from student</code>
<code>intersect</code>
<code>select dept_name from instructor;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/bdb611be-29d6-4d34-ab11-7c446d641342/image.png" alt=""></p>
<h3 id="except">except</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/006a688d-2fc3-4a02-adbd-e662ce5b89ae/image.png" alt=""></p>
<p><code>select dept_name from student</code> 
<code>except select dept_name from instructor where dept_name = &#39;음악학과&#39;;</code></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/faddb3d4-8ed1-423f-bf7b-74851a69f5c7/image.png" alt=""></p>
<h2 id="테이블-수정">테이블 수정</h2>
<p><strong>참고</strong></p>
<ul>
<li><code>drop</code>: <span style="background-color: #dcffe4">테이블 자체</span>를 삭제</li>
<li><code>delete</code>: <span style="background-color: #dcffe4">테이블 내용</span>을 삭제</li>
</ul>
<h3 id="alter-table">ALTER TABLE</h3>
<ul>
<li>기존의 <span style="background-color: #dcffe4">테이블 구조를 변경</span>하는 데에 사용</li>
</ul>
<p><strong><code>alter table ~ add</code></strong>
<code>ALTER TABLE student ADD age INT;</code></p>
<ul>
<li><span style="background-color: #dcffe4">새로운 속성(필드, 열)을 추가</span>할 때 사용</li>
</ul>
<p><strong><code>alter table ~ drop</code></strong>
<code>ALTER TABLE student DROP COLUMN age;</code></p>
<ul>
<li><span style="background-color: #dcffe4">기존 속성(필드, 열)을 삭제</span>할 때 사용</li>
<li>저장되어 있던 <span style="background-color: #dcffe4">데이터도 함께 삭제</span></li>
</ul>
<p><strong><code>alter table ~ modify</code></strong> 
<code>ALTER TABLE student MODIFY name CHAR(10);</code></p>
<ul>
<li><span style="background-color: #dcffe4">기존 속성(필드, 열)의 자료형, 크기, 제약조건 등을 변경</span>할 때 사용</li>
<li>데이터 유형을 변경할 때, 해당 열은 <span style="background-color: #dcffe4">비워져 있어야 함</span>
<code>ALTER TABLE student MODIFY stu_year NULL; // not null 제약 해제</code>
<code>UPDATE student SET stu_year = null; // 비우기</code>
<code>ALTER TABLE student MODIFY stu_year char(12);</code></li>
</ul>
<h2 id="describe">describe</h2>
<ul>
<li><code>desc</code> or <code>describe</code>: 특정 테이블의 구조를 보여줌</li>
<li>해당 테이블의 열 이름, 데이터 유형, null 허용 여부 등</li>
</ul>
<p><code>&lt;student&gt;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/05815426-e25e-4c26-bda3-3836b1fcd732/image.png" alt=""></p>
<p><code>desc student;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/7234db46-8a71-4bf7-a026-017c981995d1/image.png" alt=""></p>
<p><em>ID는 키본키이므로 <span style="background-color: #dcffe4">실제로는 unique 제약 포함.</span> 따로 보이지만 않음</em></p>
<p><code>ALTER TABLE student ADD stu_year INT;</code>
<code>UPDATE student SET stu_year = 1;</code>
<code>ALTER TABLE student MODIFY stu_year NOT NULL;</code>
→ <code>ALTER TABLE student ADD stu_year INT DEFAULT 1 NOT NULL;</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/94dc09e6-b946-4947-b2d0-117ac6c42d8e/image.png" alt="">
<img src="https://velog.velcdn.com/images/hi_soap/post/ed9d9eb3-1ffd-41c3-8a37-ec168ceabb11/image.png" alt=""></p>
<p><code>&lt;MySQL&gt;</code>
<code>alter table student add age int after name;</code>
이름 뒤에 <code>age</code> 속성 추가
<em>기본값은 맨 뒤</em></p>
<h2 id="테이블-정의">테이블 정의</h2>
<pre><code class="language-sql">create table student(
    ID            varchar(5) primary key,
    name        varchar(20) not null,
    dept_name    varchar(20)
                    references department(dept_name),
    tot_cred    numeric(3, 0));</code></pre>
<p><em>단, 속성 옆에 <code>primary/foreign key</code> 등을 붙이는 방식은
<span style="background-color: #dcffe4">단일 속성으로</span> key가 되는 경우에만 적용 가능 (<span style="background-color: #dcffe4">복합키의 경우 사용 불가능</span>)</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[땅따먹기]]></title>
            <link>https://velog.io/@hi_soap/%EB%95%85%EB%94%B0%EB%A8%B9%EA%B8%B0</link>
            <guid>https://velog.io/@hi_soap/%EB%95%85%EB%94%B0%EB%A8%B9%EA%B8%B0</guid>
            <pubDate>Sun, 03 May 2026 06:37:09 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.03</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>땅따먹기 게임을 하려고 합니다. 땅따먹기 게임의 땅(land)은 총 N행 4열로 이루어져 있고, 모든 칸에는 점수가 쓰여 있습니다. 1행부터 땅을 밟으며 한 행씩 내려올 때, 각 행의 4칸 중 한 칸만 밟으면서 내려와야 합니다. 단, 땅따먹기 게임에는 한 행씩 내려올 때, 같은 열을 연속해서 밟을 수 없는 특수 규칙이 있습니다.</p>
<p>예를 들면,</p>
<p>| 1 | 2 | 3 | 5 |</p>
<p>| 5 | 6 | 7 | 8 |</p>
<p>| 4 | 3 | 2 | 1 |</p>
<p>로 땅이 주어졌다면, 1행에서 네번째 칸 (5)를 밟았으면, 2행의 네번째 칸 (8)은 밟을 수 없습니다.</p>
<p>마지막 행까지 모두 내려왔을 때, 얻을 수 있는 점수의 최대값을 return하는 solution 함수를 완성해 주세요. 위 예의 경우, 1행의 네번째 칸 (5), 2행의 세번째 칸 (7), 3행의 첫번째 칸 (4) 땅을 밟아 16점이 최고점이 되므로 16을 return 하면 됩니다.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>행의 개수 N : 100,000 이하의 자연수</li>
<li>열의 개수는 4개이고, 땅(land)은 2차원 배열로 주어집니다.</li>
<li>점수 : 100 이하의 자연수</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/2a49bd23-13ac-4d0d-8f9f-9837d2defee1/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1차-실행-오류">1차 실행 오류</h3>
<hr>
<p>시간 초과 오류</p>
<hr>
<pre><code class="language-java">class Solution {
    private int answer = 0;
    public void route(int[][] land, int col, int sum, int before) {
        if (col &gt;= land.length) {
            answer = Math.max(answer, sum);
            return;
        }
        for (int i = 0; i &lt; land[0].length; i++) {
            if (i == before) {
                continue;
            }
            else {
                route(land, col + 1, sum + land[col][i], i);
            }
        }
    }
    int solution(int[][] land) {
        route(land, 0, 0, -1);

        return answer;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<hr>
<p><code>DP(Dynamic Programming, 동적 프로그래밍)</code>을 사용하여 해결</p>
<p>어차피 바로 이전 행은 더하지 못하므로 
그 전 값은 어떤 값을 골라도 가장 큰 값만 골라도 다음 행과 더하는 데에 
전혀 영향을 주지 않음</p>
<p>행렬 문제가 너무 많이 헷갈리고 어렵다.
더 많은 연습이 필요하다.</p>
<hr>
<pre><code class="language-java">class Solution {
    int solution(int[][] land) {
        int rows = land.length;
        int cols = land[0].length;

        // dp[i][j] = i행 j열까지 왔을 때 최댓값
        int[][] dp = new int[rows][cols];

        // 첫 행 초기화
        for (int j = 0; j &lt; cols; j++) {
            dp[0][j] = land[0][j];
        }

        // 각 행마다 이전 행의 다른 열 최댓값 + 현재 값
        for (int i = 1; i &lt; rows; i++) {
            for (int j = 0; j &lt; cols; j++) {
                for (int k = 0; k &lt; cols; k++) {
                    if (k == j) continue;  // 같은 열 스킵
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][k] + land[i][j]);
                }
            }
        }

        // 마지막 행의 최댓값
        int answer = 0;
        for (int j = 0; j &lt; cols; j++) {
            answer = Math.max(answer, dp[rows-1][j]);
        }
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[메뉴와 네비게이션 UI]]></title>
            <link>https://velog.io/@hi_soap/%EB%A9%94%EB%89%B4%EC%99%80-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-UI</link>
            <guid>https://velog.io/@hi_soap/%EB%A9%94%EB%89%B4%EC%99%80-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-UI</guid>
            <pubDate>Sat, 02 May 2026 10:51:48 GMT</pubDate>
            <description><![CDATA[<h2 id="학습-목표">학습 목표</h2>
<ul>
<li><span style="background-color: #dcffe4">Scaffold</span>를 이해하고 사용할 수 있다</li>
<li><span style="background-color: #dcffe4">앱바(App Bar)</span>를 이해하고 사용할 수 있다</li>
<li><span style="background-color: #dcffe4">메뉴(DropdownMenu)</span>를 이해하고 사용할 수 있다</li>
<li><span style="background-color: #dcffe4">다이얼로그(AlertDialog BottomSheet)</span>를 구현할 수 있다</li>
<li><span style="background-color: #dcffe4">네비게이션(NavigationBar, NavigationDrawer, NavHost)</span>을 이해·구현할 수 있다</li>
<li><span style="background-color: #dcffe4">스낵바(Snackbar)</span>를 이해하고 사용할 수 있다.</li>
<li><span style="background-color: #dcffe4">코틀린 코루틴</span>을 이해하고 설명할 수 있다.</li>
</ul>
<hr>
<h2 id="scaffold">Scaffold</h2>
<ul>
<li>Material Design에서 복잡한 사용자 인터페이스를 위한 기본 구조</li>
<li>App Bar, Floating Action Button등을 포함하고 일관된 디자인 제공</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/5a9301da-4939-4e75-a380-31e74c967b0e/image.png" alt=""></p>
<hr>
<h2 id="앱바app-bar">앱바(App Bar)</h2>
<ul>
<li><span style="background-color: #dcffe4">사용자에게 현재 화면에 필요한 액션과 네비게이션 액션을 제공하는 컨테이너</span></li>
<li>앱의 상단 또는 하단에 표시됨</li>
<li><strong>TopAppBar</strong></li>
<li><ul>
<li>네비게이션 및 현재 필요 액션</li>
</ul>
</li>
<li><strong>BottomAppBar</strong></li>
<li><ul>
<li>현재 화면에 필요한 액션 중심</li>
</ul>
</li>
</ul>
<hr>
<h2 id="메뉴dropdownmenu">메뉴(DropdownMenu)</h2>
<ul>
<li><p>사용자가 아이콘, 텍스트 필드 또는 기타 컴포넌트를 클릭한 다음
<span style="background-color: #dcffe4">일시적으로 나타나는 드롭다운 메뉴(옵션 목록)에서 옵션을 선택</span>할 수 있음</p>
</li>
<li><p>DropdownMenu, DropdownMenuItem을 이용하여 구현</p>
</li>
<li><p>DropdownMenu</p>
</li>
<li><ul>
<li>expanded: 메뉴 표시 여부</li>
</ul>
</li>
<li><ul>
<li>onDismissRequest: 메뉴가 닫힐 때 호출</li>
</ul>
</li>
<li><ul>
<li>content: 드롭다운 메뉴 옵션, DropdownMenuItem 컴포저블 사용</li>
</ul>
</li>
<li><p>DropdownMenuItem</p>
</li>
<li><ul>
<li>text: 메뉴 옵션 이름</li>
</ul>
</li>
<li><ul>
<li>onClick: 옵션 선택 시 호출</li>
</ul>
</li>
</ul>
<hr>
<h2 id="다이얼로그alertdialog">다이얼로그(AlertDialog)</h2>
<ul>
<li><span style="background-color: #dcffe4">앱 콘텐츠 위에 대화상자로 메시지를 표시하거나 입력 받기 위한 UI</span></li>
<li><ul>
<li>파일 삭제와 같은 작업 진행 전에 사용자에게 확인 요청</li>
</ul>
</li>
<li><ul>
<li>일정 관리에서 일정 추가할 때 <span style="background-color: #dcffe4">사용자 입력을 받아야 하는 경우</span></li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/8e7c58ed-5d05-46e1-9324-6f7e0e1f5d97/image.png" alt=""></p>
<hr>
<h2 id="bottom-sheet-dialog">Bottom Sheet Dialog</h2>
<ul>
<li><span style="background-color: #dcffe4">앱 아래쪽에 표시</span>하는 다이얼로그</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/a74f23aa-499c-4ada-9814-363e62e997d1/image.png" alt=""></p>
<hr>
<h2 id="navigationbar">NavigationBar</h2>
<ul>
<li><p>보통 BottomBar에 같은 수준의 <span style="background-color: #dcffe4">3-5개 대상 화면에 대해 화면 전환할 때 사용</span></p>
</li>
<li><p>NavigationBar로 대상 선택 메뉴 구성</p>
</li>
<li><ul>
<li>NavigationBarItem</li>
</ul>
</li>
<li><p>NavHost로 실제 보여질 화면을 구성</p>
</li>
<li><p>NavigationBar와 NavHost의 연결은
navigation controller를 통해서</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/227af3be-06e0-4ddf-b6ad-e06cf002705c/image.png" alt=""></p>
<p><em>NavHost와 navigation controller를 사용하지 않아도 됨</em></p>
<hr>
<h2 id="navigationdrawer">NavigationDrawer</h2>
<ul>
<li>앱의 여러 화면으로 전환하는 대상을 선택할 수 있는 서랍</li>
<li>ModalNavigationDrawer, ModalDrawerSheet를 이용하여 탐색 서랍을 구성</li>
<li><ul>
<li>NavigationDrawerItem으로 대상 하나를 정의</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/9f42a530-8146-425f-a8d5-7b4492325138/image.png" alt=""></p>
<hr>
<h2 id="snackbar">Snackbar</h2>
<ul>
<li>화면 하단에 표시되는 간단한 알림</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/cae75108-0662-4dab-8c93-3e82e44f0e75/image.png" alt=""></p>
<hr>
<h2 id="코틀린-코루틴coroutines">코틀린 코루틴(Coroutines)</h2>
<ul>
<li><p><span style="background-color: #dcffe4">협력적 멀티태스킹</span></p>
</li>
<li><p>모바일 앱은 애니메이션, 네트워크 통신이나 DB 접근처럼 
<span style="background-color: #dcffe4">시간이 오래 걸리는 작업이 많음</span></p>
</li>
<li><ul>
<li><strong>문제점</strong>
작업이 완료될 때 까지 기다리는 동안 메인 스레드가 멈추게 되면(Blocking), <span style="background-color: #dcffe4">화면이 멈추고 ANR(Application Not Responding) 발생 가능</span></li>
</ul>
</li>
<li><ul>
<li><strong>해결책</strong>
기다리는 동안 스레드를 양보하고, 작업이 끝나면 다시 돌아오는 비차단(non-blocking) 방식 필요</li>
</ul>
</li>
<li><p>코루틴은 스레드에 비해 가벼움</p>
</li>
<li><ul>
<li>스레드는 생성 및 컨텍스트 스위칭 <span style="background-color: #dcffe4">비용이 높고, 강제로 선점</span></li>
</ul>
</li>
<li><ul>
<li>코루틴은 <span style="background-color: #dcffe4">자발적인 양보</span>를 필요로 하며, <span style="background-color: #dcffe4">매우 가볍고 부담 없음</span></li>
</ul>
</li>
<li><ul>
<li>하나의 스레드에 여러 코루틴이 
<span style="background-color: #dcffe4">자발적으로 양보하며, 협력적으로 멀티태스킹을 수행</span></li>
</ul>
</li>
<li><ul>
<li>함수 앞에 <code>suspend</code> 키워드로 코루틴임을 나타냄</li>
</ul>
</li>
<li><p><code>suspend fun myCoroutine()</code>*</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/a8a28153-d5a4-43b9-af86-7fa06d1def39/image.png" alt=""></p>
<h3 id="코루틴-3대-요소">코루틴 3대 요소</h3>
<h4 id="코루틴-스코프coroutinescope">코루틴 스코프(CoroutineScope)</h4>
<ul>
<li>코루틴이 수행되는 범위, <span style="background-color: #dcffe4">코루틴은 반드시 코루틴 스코프 내에서 실행</span></li>
<li>예)
<code>viewModelScope</code>: 뷰모델 생명주기에 맞춰 종료
<code>lifecycleScope</code>: 액티비티/프래그먼트 생명주기에 맞춰 종료*</li>
</ul>
<h4 id="디스패쳐dispatcher--스레드">디스패쳐(Dispatcher) = 스레드</h4>
<ul>
<li>어떤 스레드에서 실행?</li>
<li><strong>Dispatcher.Main</strong>: UI 업데이트용 (메인 스레드)</li>
<li><strong>Dispatcher.IO</strong>: 네트워크 디스크 읽기/쓰기 등 무거운 작업용</li>
<li><strong>Dispatcher.Default</strong>: CPU 집약적인 계산 작업용</li>
</ul>
<h4 id="coroutine-context">Coroutine Context</h4>
<ul>
<li>어떤 정보를 담고 있는가?</li>
<li>코루틴의 이름, Dispatcher, Job 등을 담고 있는 컨텍스트</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[n진수 게임]]></title>
            <link>https://velog.io/@hi_soap/n%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@hi_soap/n%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Sat, 02 May 2026 05:52:26 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.02</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>튜브가 활동하는 코딩 동아리에서는 전통적으로 해오는 게임이 있다. 이 게임은 여러 사람이 둥글게 앉아서 숫자를 하나씩 차례대로 말하는 게임인데, 규칙은 다음과 같다.</p>
<ol>
<li>숫자를 0부터 시작해서 차례대로 말한다. 첫 번째 사람은 0, 두 번째 사람은 1, … 열 번째 사람은 9를 말한다.</li>
<li>10 이상의 숫자부터는 한 자리씩 끊어서 말한다. 즉 열한 번째 사람은 10의 첫 자리인 1, 열두 번째 사람은 둘째 자리인 0을 말한다.
이렇게 게임을 진행할 경우,
<code>0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4, …</code>
순으로 숫자를 말하면 된다.</li>
</ol>
<p>한편 코딩 동아리 일원들은 컴퓨터를 다루는 사람답게 이진수로 이 게임을 진행하기도 하는데, 이 경우에는
<code>0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, …</code>
순으로 숫자를 말하면 된다.</p>
<p>이진수로 진행하는 게임에 익숙해져 질려가던 사람들은 좀 더 난이도를 높이기 위해 이진법에서 십육진법까지 모든 진법으로 게임을 진행해보기로 했다. 숫자 게임이 익숙하지 않은 튜브는 게임에 져서 벌칙을 받는 굴욕을 피하기 위해, 자신이 말해야 하는 숫자를 스마트폰에 미리 출력해주는 프로그램을 만들려고 한다. 튜브의 프로그램을 구현하라.</p>
<h2 id="입력-형식">입력 형식</h2>
<p>진법 n, 미리 구할 숫자의 갯수 t, 게임에 참가하는 인원 m, 튜브의 순서 p 가 주어진다.</p>
<ul>
<li>2 ≦ n ≦ 16</li>
<li>0 ＜ t ≦ 1000</li>
<li>2 ≦ m ≦ 100</li>
<li>1 ≦ p ≦ m
출력 형식
튜브가 말해야 하는 숫자 t개를 공백 없이 차례대로 나타낸 문자열. 단, 10<del>15는 각각 대문자 A</del>F로 출력한다.</li>
</ul>
<h2 id="입출력-예제">입출력 예제</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c38e6d0e-a3c1-4bcf-99c8-ebc6f92c52a1/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-24분">소요 시간: 24분</h4>
<h4 id="시간-복잡도-on-log-n">시간 복잡도: O(n log n)</h4>
<hr>
<p><code>k진수에서 소수 개수 구하기</code> 문제에서 사용했던 <code>toString()</code>을 사용하여
쉽게 풀 수 있었다.</p>
<hr>
<pre><code class="language-java">class Solution {
    public String solution(int n, int t, int m, int p) {
        String answer = &quot;&quot;;
        int index = 0;
        int order = 1; // 전체 순서
        int currentOrder = 1; // 현재 순서
        int count = 0; // 현재 구한 숫자의 개수
        while(true) {
            if (count &gt;= t) {
                break;
            }
            String str = Integer.toString(index, n).toUpperCase(); // n진수 변환
            for (int i = 0; i &lt; str.length(); i++) {
                if (currentOrder == p) {
                    answer += str.charAt(i);
                    count++;
                    if (count &gt;= t) {
                        break;
                    }
                }
                currentOrder = order % m + 1;
                order++;
            }
            index++;
        }
        return answer;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on-log-n-1">시간 복잡도: O(n log n)</h4>
<hr>
<p><code>+=</code>를 사용하면 계속해서 새 객체를 생성하므로
<code>StringBuilder</code>를 이용하여 불필요한 객체 생성을 방지
또한 길이 추적도 <code>count</code>변수를 따로 두는 것이 아닌,
문자의 총 길이로 추적함</p>
<hr>
<pre><code class="language-java">class Solution {
    public String solution(int n, int t, int m, int p) {
        StringBuilder sb = new StringBuilder();
        int index = 0;
        int total = 0; // 전체 문자 순서

        while (sb.length() &lt; t) {
            String str = Integer.toString(index++, n).toUpperCase();
            for (int i = 0; i &lt; str.length() &amp;&amp; sb.length() &lt; t; i++) {
                total++;
                if (total % m == p - 1) {  // p번째 순서
                    sb.append(str.charAt(i));
                }
            }
        }

        return sb.toString();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[제품 요구사항 문서(PRD, Product Requirements Document)]]></title>
            <link>https://velog.io/@hi_soap/%EC%A0%9C%ED%92%88-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EB%AC%B8%EC%84%9CPRD-Product-Requirements-Document</link>
            <guid>https://velog.io/@hi_soap/%EC%A0%9C%ED%92%88-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD-%EB%AC%B8%EC%84%9CPRD-Product-Requirements-Document</guid>
            <pubDate>Fri, 01 May 2026 10:01:32 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hi_soap/post/e93289f2-8f47-498b-903e-80c7272dfc46/image.png" alt=""></p>
<h3 id="1단계-제품-명세서-작성">1단계: 제품 명세서 작성</h3>
<ul>
<li><code>prd.md</code> 작성</li>
</ul>
<p><strong>사람은</strong></p>
<ul>
<li>AI에게 기초적인 아이디어를 제공</li>
</ul>
<p><strong>AI는</strong></p>
<ul>
<li>기초적인 아이디어를 구체화하고, 요구사항을 논리적으로 정의</li>
<li><code>prd.md</code> 파일을 완성할 수 있도록 상세한 가이드를 제공</li>
</ul>
<h3 id="2단계-기술-설계서-작성">2단계: 기술 설계서 작성</h3>
<ul>
<li><p><code>techspec.md</code> (기술 설계 문서 또는 <code>design.md</code>) 작성</p>
</li>
<li><p>앞에서 만든 <span style="background-color: #dcffe4"><code>prd.md</code>를 바탕</span>으로 시스템의 뼈대를 잡는 과정</p>
</li>
<li><p>AI의 도움을 받아 시스템 구조와 데이터베이스, API 등을 설계</p>
</li>
<li><p>이 단계가 끝나게 되면 개발의 전체적인 청사진이 완성됨</p>
</li>
</ul>
<h3 id="3단계-구현-계획-수립">3단계: 구현 계획 수립</h3>
<ul>
<li><p><code>tasks.md</code> 파일이 준비됨</p>
</li>
<li><p><code>techspec.md</code>를 바탕으로 실제 개발에서 해결해야 할 <span style="background-color: #dcffe4">세부 작업들을 정의</span></p>
</li>
<li><p>AI는 복잡한 설계를 작은 단위의 <span style="background-color: #dcffe4">태스크</span>로 나누고, 우선순위를 정함</p>
</li>
</ul>
<h3 id="4단계-티켓-주도-개발-진행">4단계: 티켓 주도 개발 진행</h3>
<ul>
<li><p>칸반 보드에 올라온 작업 티켓을 하나씩 해결</p>
</li>
<li><p><span style="background-color: #dcffe4">AI와의 협력</span>으로 <span style="background-color: #dcffe4">실제 코드를 구현하고 테스트</span></p>
</li>
<li><p>AI는 <span style="background-color: #dcffe4">코드 작성 뿐만 아니라 버그 수정과 최적화까지 지원</span></p>
</li>
</ul>
<hr>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c15c301d-0b64-4345-b2ef-ae524b04c040/image.png" alt=""></p>
<hr>
<h3 id="0-문서-정보">0. 문서 정보</h3>
<ul>
<li><strong>메타 데이터(Meta data)</strong>
이 문서가 언제 작성됐고, 누가 썼으며, 현재 확정된 버전인지
아니면 아직 고치고 있는 &#39;Draft&#39; 상태인지를 명시</li>
</ul>
<hr>
<h3 id="1-프로젝트-개요">1. 프로젝트 개요</h3>
<h4 id="11-배경-및-목적background">1.1 배경 및 목적(Background)</h4>
<ul>
<li><p>프로젝트를 시작하는 이유</p>
</li>
<li><p>단순히 기능을 나열하는 것이 아닌, <span style="background-color: #dcffe4">현재 사용자가 겪는 고통(Pain Point)</span>과
우리가 그것을 <span style="background-color: #dcffe4">해결해야 하는 이유</span></p>
</li>
<li><p>이 문서 한장으로 &#39;이 문서는 무엇이며, 우리는 왜 이 일을 하는가?&#39;
에 대한 대답이 되어야 함*</p>
</li>
</ul>
<h4 id="12-목표-및-가치goals">1.2 목표 및 가치(Goals)</h4>
<ul>
<li><p>목표: 프로젝트가 끝났을 때 도달해야 할 목적지</p>
</li>
<li><p><strong>Core Goal</strong>: &quot;이 제품을 통해 사용자에게 어떤 가치를 줄 것인가?&quot;를
<span style="background-color: #dcffe4">한 문장</span>으로 정의</p>
</li>
</ul>
<h4 id="13-범위scope">1.3 범위(Scope)</h4>
<ul>
<li><strong>In-Scope</strong>: 이번 버전에서 반드시 개발할 기능</li>
<li><strong>Out-Of-Scope</strong>: 이번 버전에서 제외된 기능</li>
</ul>
<hr>
<h3 id="2-유저-스토리">2. 유저 스토리</h3>
<p><strong>사용자 시나리오</strong></p>
<ul>
<li><p><span style="background-color: #dcffe4">사용자의 관점</span>에서 소프트웨어가 제공해야 하는 기능을 짧고 명확하게 기술</p>
</li>
<li><p><strong>3단 구성 공식</strong>: <strong><span style="background-color: #dcffe4">[누가, who]</span></strong>로서 <strong><span style="background-color: #dcffe4">[무엇, what]</span></strong>을 원한다. 
왜냐하면 <strong><span style="background-color: #dcffe4">[이유, why]</span></strong> 때문이다.</p>
</li>
</ul>
<p><strong>AC(Acceptance Criteria)</strong></p>
<ul>
<li><p>인수 기준은 <span style="background-color: #dcffe4">명확하고 구체적</span>이어야 한다.</p>
</li>
<li><p>좋은 인수 기준은 <span style="background-color: #dcffe4">&quot;테스트할 수 있는가?&quot;</span></p>
</li>
<li><p>예)
사용하기 편해야 한다 [X] - 편하다는 기준은 사람마다 다름
업로드 버튼을 누르고 1초 이내에 메시지가 떠야 한다 [O]*</p>
</li>
<li><p>더 체계적이고 전문적으로 쓰고 싶을 경우 <span style="background-color: #dcffe4">
<strong>EARS(Easy Approach to Requirements Syntax)</strong> 문법을 활용</span></p>
<ul>
<li>자연어 요구사항의 모호함을 제거하기 위해, 상황별 키워드를 사용하여
시스템의 동작을 <span style="background-color: #dcffe4">표준화된 템플릿 형태로 기술하는 작성 방법론</span></li>
</ul>
</li>
<li><p>할루시네이션을 방지하거나 줄이는 데에 핵심 도구*</p>
</li>
</ul>
<p><strong>AI의 역할</strong></p>
<ul>
<li><strong>스토리 생성</strong>: 모호한 아이디어를 <span style="background-color: #dcffe4">표준 템플릿 문장으로 변환</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/65b3c74f-b938-481f-8d3d-99b3e50a61df/image.png" alt=""></p>
<p><strong>우선순위(Prioritization)-중증도 분류(Triage): 기능 우선순위</strong></p>
<ul>
<li><p><strong>P0 (Immediate)</strong>
&quot;이게 없으면 제품이 아니다&quot; 싶은 <span style="background-color: #dcffe4">필수 기능</span></p>
</li>
<li><p><strong>P1 (Urgent)</strong>
&quot;있으면 정말 좋지만, 없어도 당장 죽지는 않는&quot; 기능</p>
</li>
<li><p><strong>P2 (Non-Urgent)</strong>
&quot;여유가 생기면 추가할 사치품&quot;</p>
</li>
</ul>
<p><em>이러한 우선순위를 잘 정해놓아야, 마감 기한이 다가왔을 때
<span style="background-color: #dcffe4">무엇을 버릴지</span> 잘 결정할 수 있음</em></p>
<p><strong>사용자 스토리 우선순위 정하기: MoSCoW 기법</strong>
우리가 가진 제한된 시간과 자원 내에서 사용자에게 최대의 가치를 전달하기 위해
<span style="background-color: #dcffe4">무엇에 집중해야 할지 결정하는 나침반</span>
<img src="https://velog.velcdn.com/images/hi_soap/post/296e0b83-ab37-40c2-8e1d-a247bbc5b52e/image.png" alt=""></p>
<hr>
<h3 id="3-대상-사용자target-audience">3. 대상 사용자(Target-Audience)</h3>
<p>&quot;모두를 만족시키는 제품은 누구도 만족시키지 못하는 결과물이 됨&quot;</p>
<p><strong>주요 페르소나(Primary Persona)</strong>
핵심 타겟의 특성 및 행동 양식</p>
<p><strong>사용자 세그먼트(User Segmentation)</strong>
그룹 분류 (숙련도, 연령 등)</p>
<p><strong>사용자 니즈(User Needs)</strong>
제품을 통해 해결하고자 하는 요구사항</p>
<hr>
<h3 id="4-핵심-성과-지표kpis">4. 핵심 성과 지표(KPIs)</h3>
<h4 id="41-비즈니스-지표business-metrics">4.1 비즈니스 지표(Business Metrics)</h4>
<ul>
<li><p><strong>재방문률(Retension)</strong>
사용자가 얼마나 우리 앱에 자주 들어오는지 보여줌 </p>
</li>
<li><p><strong>전환율(Conversion Rate)</strong>
실제로 물건을 구매했는지 보여줌</p>
</li>
</ul>
<h4 id="42-기술-지표technical-metrics">4.2 기술 지표(Technical Metrics)</h4>
<ul>
<li>로딩 속도, 에러율 등의 목표 수치</li>
</ul>
<h4 id="북극성-지표north-star-metric">북극성 지표(North Star Metric)</h4>
<ul>
<li><p>모든 지표 중 팀이 <span style="background-color: #dcffe4">가장 우선적으로 바라봐야 할 단 하나의 목표</span></p>
</li>
<li><p>구체적이고 측정 가능하며, 달성 가능한 숫자로 적어야 함</p>
</li>
</ul>
<hr>
<h3 id="5-상세-기능-요건functional-requirements">5. 상세 기능 요건(Functional Requirements)</h3>
<ul>
<li>앞에서 무엇을 만들지를 결정했다면, 
이제는 그 기능들이 실제로 어떻게 움직여야 하는지 <span style="background-color: #dcffe4">시스템의 언어</span>로 번역</li>
</ul>
<h4 id="51-기능-그룹">5.1 기능 그룹</h4>
<ul>
<li>개발자가 코드를 짤 때, 오해의 소지가 없도록
아주 정밀하게 <span style="background-color: #dcffe4">로직(Logic)</span>을 정의하는 것이 핵심</li>
</ul>
<h4 id="상세-기능-요건을-작성할-때-고려해야-할-조건-3가진">상세 기능 요건을 작성할 때, 고려해야 할 조건 3가진</h4>
<ul>
<li><p><strong>로직(Logic)</strong>
시스템이 동작하는 <span style="background-color: #dcffe4">기본 원리와 알고리즘</span></p>
</li>
<li><p><strong>밸리데이션(validation)</strong>
입력값의 <span style="background-color: #dcffe4">유효성</span>을 검사하는 규칙</p>
</li>
<li><p><strong>엣지 케이스(Edge Case)</strong>
예외적이거나 극단적인 상황에서의 <span style="background-color: #dcffe4">처리 방식</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/5fc911ae-811f-44e1-8f48-f3538b541fb0/image.png" alt=""></p>
<h4 id="우선-순위와-기능-요건의-연결">우선 순위와 기능 요건의 연결</h4>
<p>각 요건에는 <strong>F-01, F-02</strong>와 같은 고유한 ID를 부여해야 함
<img src="https://velog.velcdn.com/images/hi_soap/post/cafa6bc5-a850-491d-aaa9-fcd58d00d94d/image.png" alt=""></p>
<p><em>비즈니스 우선 순위가 높은 것 부터 시스템의 언어로 풀어가는 과정임</em></p>
<hr>
<h3 id="6-uiux-상세-요건">6. UI/UX 상세 요건</h3>
<ul>
<li><p>예쁜 그림을 보여주는 곳이 아닌,
사용자가 어떤 경로로 이동하고 화면이 어떤 논리적 구조를 갖는지 정의</p>
</li>
<li><p>디자인의 느낌보다 <span style="background-color: #dcffe4">컴포넌트의 배치와 동작 규칙을 전달</span></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/22219199-a5cc-43d8-b260-25a1abf87cf5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/05eb5f8d-fed1-43c1-b14a-2fdbb9f7d3db/image.png" alt=""></p>
<h4 id="1-사용자-흐름user-flow">1. 사용자 흐름(User Flow)</h4>
<ul>
<li>사용자가 서비스에 접속해서 목적을 달성할 때 까지 거치는 <span style="background-color: #dcffe4">모든 단계</span></li>
</ul>
<h4 id="2-와이어프레임wireframe">2. 와이어프레임(Wireframe)</h4>
<ul>
<li>정밀한 디자인 파일이 없더라도,
헤더에는 무엇이 들어가고, 메인 영역은 몇개의 열로 구성되는지 등의 정의</li>
</ul>
<h4 id="3-인터랙션-규칙interaction-rules">3. 인터랙션 규칙(Interaction Rules)</h4>
<ul>
<li>버튼을 눌렀을 때 로딩 표시를 어떻게 할 지 등</li>
</ul>
<hr>
<h3 id="7-기술적-제약">7. 기술적 제약</h3>
<h4 id="품질-속성-qualiy-attributes">품질 속성 (Qualiy Attributes)</h4>
<h4 id="비기능-요구사항non-functional-requirements-nfr">비기능 요구사항(Non-Functional Requirements, NFR)</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c70d2842-2580-42ae-9845-4842b5585da8/image.png" alt=""></p>
<ul>
<li><p>기능이 무엇을 하는지(what)라면, 
품질 속성은 그것을 <span style="background-color: #dcffe4">얼마나 잘 하는지(How Well)</span></p>
</li>
<li><p>시스템의 기능이 수행되는 동안 만족해야 하는 <span style="background-color: #dcffe4">운영적 특성이나 제약 사항</span></p>
</li>
<li><p>예)
개발자가 코드를 짤 때, 기능은 <strong>구현할 대상</strong>
품질 속성은 <span style="background-color: #dcffe4">구현의 범위와 한계를 정하는 <strong>제약 조건</strong> *</span></p>
</li>
<li><p>품질 속성은 반드시 측정 가능, <strong>measureable</strong> 해야 함</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/9442a1dd-cabc-4f70-afde-bbbc00c90c03/image.png" alt=""></p>
<ul>
<li>기능은 <span style="background-color: #dcffe4">동작을 정의</span></li>
<li>품질은 <span style="background-color: #dcffe4">상태를 정의</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/c0e444ae-9639-489e-ba31-6873db82b5a3/image.png" alt=""></p>
<hr>
<h3 id="prd-생성-전략">PRD 생성 전략</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/5794601a-2c6a-47f5-933d-ae433024c042/image.png" alt=""></p>
<h4 id="1-프롬프트-템플릿-기반-전략">1. 프롬프트 템플릿 기반 전략</h4>
<ul>
<li>AI에게 아주 명확한 청사진(템플릿)을 미리 제공하는 것</li>
<li><span style="background-color: #dcffe4">일관성과 전문성</span></li>
<li>조직 내에서 <span style="background-color: #dcffe4">즉시 공유 가능한</span> 수준의 PRD 초안을 얻을 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/2f72db11-ddcd-4ce4-95fc-333731fca595/image.png" alt=""></p>
<ul>
<li>Input Section에서 사용자는 아이디어를 모호하게 전달하지만, 
AI는 정해진 Role에 따라 이를 구체적인 아이디어로 구현해감</li>
<li>최소한의 Input 데이터를 통해, 고성능의 결과물을 뽑아내는 과정*</li>
</ul>
<h4 id="2-질문-유도-방식">2. 질문 유도 방식</h4>
<ul>
<li><span style="background-color: #dcffe4">일부 주도권을 AI에게 넘기는 방식</span></li>
<li>AI는 사람에게 <span style="background-color: #dcffe4">탐색형 질의</span>를 던짐</li>
<li>이러한 과정 속에서 추상적이었던 아이디어가 구체적으로 다듬어짐*</li>
<li>AI와의 문답 과정을 통해 사용자의 <span style="background-color: #dcffe4">Pain Point를 AI가 질문으로 짚어줌</span></li>
<li>기획의 빈틈을 매우는 데 탁월함*</li>
<li>아이디어가 아직 구체화되지 않은 <span style="background-color: #dcffe4">초기 단계</span>에서 강력함</li>
</ul>
<h4 id="3-단계별-전략step-by-step-or-chain-of-thoughtcot">3. 단계별 전략(step by step) or chain of thought(COT)</h4>
<ul>
<li>시스템 전체를 논리적인 단계에 따라 <span style="background-color: #dcffe4">작업을 쪼개서 진행</span></li>
<li>비전 정의 → 사용자 스토리 → 구체적 인수 기준의 과정을 통해
AI의 인지 부하를 줄여 <span style="background-color: #dcffe4">할루시네이션을 최소화, 각 섹션의 상세도를 극대화</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/710e5bc5-bb54-4ae0-833a-f7fa35230b9d/image.png" alt=""></p>
<p><strong>1. 비전 정의</strong>
게임 개발을 예로 들었을 때,
게임의 본질적인 목적과 타켓 사용자를 정의해야 함
<span style="background-color: #dcffe4">방향성</span>이 이 단계에서 결정됨
*<span style="background-color: #dcffe4">사용자가 해당 프로그램을 통해 얻을 수 있는 가장 중요한 가치 정의*</p>
<p><strong>2. 사용자 경험 설계</strong>
이 과정에서 시스템의 <span style="background-color: #dcffe4">필수 요소</span>들이 자연스럽게 리스트업 됨</p>
<p><strong>3. 핵심 로직 상세화</strong>
핵심 비즈니스 로직을 <span style="background-color: #dcffe4">기술적으로 상세화</span>하는 과정임
<span style="background-color: #dcffe4">반드시 edge 케이스와 validation 로직</span>을 이 단계에서 보완해야 함</p>
<p><strong>4. 수용 기준 확정</strong>
각 기능이 완벽하게 구현되었는지 판단할 수 있는 기준인
<span style="background-color: #dcffe4">Acceptance Criteria를 확정</span>하며 문서를 마무리</p>
<p><strong><em>이 세 가지 전략을 상황에 따라 유연하게 결합하는 <span style="background-color: #dcffe4">하이브리드 접근법</span>이 가장 강력</em></strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[07. 투상 변환과 뷰 포트 변환]]></title>
            <link>https://velog.io/@hi_soap/07.-%ED%88%AC%EC%83%81-%EB%B3%80%ED%99%98%EA%B3%BC-%EB%B7%B0-%ED%8F%AC%ED%8A%B8-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@hi_soap/07.-%ED%88%AC%EC%83%81-%EB%B3%80%ED%99%98%EA%B3%BC-%EB%B7%B0-%ED%8F%AC%ED%8A%B8-%EB%B3%80%ED%99%98</guid>
            <pubDate>Fri, 01 May 2026 08:57:20 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.05.01</strong></p>
<h2 id="학습목표">학습목표</h2>
<ul>
<li><span style="background-color: #dcffe4">평행투상과 원근투상</span>의 차이점을 이해한다.</li>
<li><span style="background-color: #dcffe4">가시부피 설정방식</span>을 이해한다.</li>
<li><span style="background-color: #dcffe4"><code>glOrtho()</code> 함수와 <code>glPerspective()</code> 함수 파라미터</span>를 이해한다.</li>
<li><span style="background-color: #dcffe4">전방 절단면은 되도록 시점에서 멀리 가져가는 이유</span>를 이해한다.</li>
<li><span style="background-color: #dcffe4">시점좌표, 절단좌표, 정규화 장치좌표, 화면좌표로의 변환과정</span>을 이해한다.</li>
</ul>
<h2 id="01-투상">01. 투상</h2>
<p><strong>투상(Projection) = 가시변환(Viewing Transformation)</strong>
<img src="https://velog.velcdn.com/images/hi_soap/post/d58d7311-199b-453a-a058-4d789160863c/image.png" alt=""></p>
<ul>
<li><p>투상면(View Plane, Projection Plane)</p>
</li>
<li><p>관찰자 위치(View Point, Eye Position)
= 카메라 위치(Camera Position) = <span style="color: red">투상중심(Center of Projection, COP)</span>
= 시점좌표계 원점(Origin of VCS)</p>
</li>
<li><p>투상선(Projectors): 물체 곳곳을 향함</p>
</li>
<li><p>시선(Line of Sight): WCS 원점 또는 초점을 향함</p>
</li>
<li><p>투상면(Projection Plane, View Plane)</p>
</li>
</ul>
<h3 id="1-1-평행-투상parallel-projection">1-1 평행 투상(Parallel Projection)</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/7b320825-977b-4753-975c-633f96f25e7e/image.png" alt="">
<span style="background-color: #dcffe4">원근법을 고려하지 않고</span> 시점이 물체로부터 무한대의 거리에 있다고 간주</p>
<ul>
<li>투상선이 <span style="background-color: #dcffe4">평행</span></li>
<li>원래 물체의 평행선은 <span style="background-color: #dcffe4">투상 후에도 평행</span></li>
<li><span style="background-color: #dcffe4">시점과의 거리에 무관</span>하게 같은 길이의 물체는 같은 길이로 투상</li>
</ul>
<h3 id="1-2-원근-투상perspective-projection">1-2 원근 투상(Perspective Projection)</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/3840a1ac-e3a4-420d-b967-5163d011482b/image.png" alt="">
시점이 물체로부터 <span style="background-color: #dcffe4">유한한 거리</span>에 있다고 간주</p>
<ul>
<li>투상선이 시점에서 출발하여 방사선 모양으로 펴져감</li>
<li>카메라나 사람의 눈이 물체를 포착하는 방법</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e82d62d2-3e91-4756-a62f-1a9026453147/image.png" alt=""></p>
<p>*<em>원근감 *</em>
동일한 크기의 물체라도 
<span style="background-color: #dcffe4">시점으로부터 멀리 있는 것은 작게 보이고, 가까운 것은 크게 보임</span></p>
<p><span style="background-color: #dcffe4">소실점</span>이 생겨남</p>
<p><strong>원근변환(Perspective Transformation)</strong></p>
<ul>
<li>직선→직선, 평면→평면</li>
<li>직선은 그대로 직선을, 평면은 그대로 평면을 유지함*</li>
</ul>
<h2 id="02-지엘의-투상-변환">02. 지엘의 투상 변환</h2>
<h3 id="2-1-지엘의-평행-투상">2-1 지엘의 평행 투상</h3>
<p><strong>투상</strong>: void glMatrixMode(GL_PROJECTION);
<span style="background-color: #dcffe4">P&#39; = MParallel · P</span>
<img src="https://velog.velcdn.com/images/hi_soap/post/5061bd19-c63a-4e28-beed-8692e3d4ddf5/image.png" alt=""></p>
<pre><code>|1  0  0  0|
|0  1  0  0|
|0  0  0 -d|
|0  0  0  1|

역행렬이 존재하지 않음(특이변환)

해당 행렬은 암기해놓기</code></pre><p>기본 평행투상</p>
<ul>
<li>모델 좌표, 전역 좌표, 시점 좌표 순서로 변환된 상태</li>
<li>P, P&#39;은 시점 좌표계 기준의 좌표. <span style="background-color: #dcffe4">거리 d에 무관하게 동일</span></li>
<li><span style="color: red">특이변환(Singular Transformation)</span>: 역변환이 없는 변환</li>
<li><ul>
<li><em>점 P→P&#39;, Q→P&#39;은 가능하지만, P&#39;→P, P&#39;→Q는 불가능함</em></li>
</ul>
</li>
<li><ul>
<li>그러나 나중에 깊이 정보(P&#39;→Q, P&#39;→P 거리)를 활용하기 때문에,
<span style="background-color: #dcffe4">실제로 지엘은 이러한 변환을 가하지 않음</span></li>
</ul>
</li>
<li><ul>
<li><span style="background-color: #dcffe4">개념적 설명임</span></li>
</ul>
</li>
<li><ul>
<li>실제 지엘 내부에서는 투상결과 여전히 <span style="background-color: #dcffe4">3차원 좌표가 유지됨</span></li>
</ul>
</li>
<li><span style="background-color: #dcffe4">눈이 원점</span>에 있다고 가정하고 -z 방향으로 관찰하고 있다고 생각함</li>
<li>모든 것은 <span style="background-color: #dcffe4">눈</span>을 기준으로 한 좌표*</li>
</ul>
<h4 id="가시부피에-의한-평행투상">가시부피에 의한 평행투상</h4>
<p><strong>가시부피(View Volume)</strong>: 장면의 범위를 지정할 필요성
<img src="https://velog.velcdn.com/images/hi_soap/post/571fbf6d-5dd4-4420-b295-686d88ced97d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/064ee086-312d-4368-b324-c72bafed7a64/image.png" alt=""></p>
<p><code>void glOrtho(GLdouble left, righ, bottom, top, near, far)</code></p>
<ul>
<li>모두 <span style="background-color: #dcffe4">카메라 기준 좌표계</span>(눈)의 좌표</li>
<li><code>near, far</code>은 [-near, -far]로 변환된다(-z축의 방향이기 때문)</li>
</ul>
<h4 id="정규화-가시부피">정규화 가시부피</h4>
<p><strong>정규화 가시부피(CVV: Canonical View Volume)</strong></p>
<ul>
<li>가로, 세로, 높이가 2인 정육면체로 투상</li>
<li>정규화 변환(Normalization Transformation)</li>
<li>변환 후 <span style="background-color: #dcffe4">z축 방향이 반대</span>가 됨</li>
<li>계산의 편리를 위함*</li>
</ul>
<p><strong>정규화 이유</strong></p>
<ul>
<li><p>평행투상, 원근투상을 <span style="background-color: #dcffe4">동일한 모습의 정규화 가시부피</span>로 변형</p>
</li>
<li><p>동일 파이프라인 사용*</p>
</li>
<li><p>정육면체를 기준으로 하면 연산이 간단함</p>
</li>
<li><p>다양한 해상도의 화면 좌표계로 변환하기가 간단함</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/45a4583b-d746-4c05-a489-6f5d3513ead9/image.png" alt=""></p>
<h4 id="정규화-가시부피-변환">정규화 가시부피 변환</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/607ae782-cb6c-4bc0-abb8-6011c064cf30/image.png" alt=""></p>
<p>T변환(중점 이동) → S변환(크기변환) → R변환(z축 반대로 이동)
<img src="https://velog.velcdn.com/images/hi_soap/post/5778a1f3-64c2-42c0-b11a-9fb80d581b02/image.png" alt=""></p>
<p>예제) 점(l,b,-n,1), (r,t,-f,1)의 변환 후 좌표를 구하시오.
<img src="https://velog.velcdn.com/images/hi_soap/post/4eb66744-6e0b-4871-a282-8c7f71130a8d/image.png" alt=""></p>
<p><span style="background-color: #dcffe4">이렇게 만들어진 좌표계를 <span style="color: red">절단 좌표계(Clip Coordinate System, CCS)</span> 라고 함</span></p>
<h3 id="2-2-지엘의-원근-투상">2-2 지엘의 원근 투상</h3>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/f970efb1-ea2e-4a04-a42e-9b3a1ef87d93/image.png" alt="">
<img src="https://velog.velcdn.com/images/hi_soap/post/ef602bbd-a413-4bf8-8f04-223320d2e7fe/image.png" alt=""></p>
<p><em>4행의 3열에 특정 숫자가 있다 = 원근 투상이다?</em></p>
<p>현재</p>
<pre><code>|  x  |
|  y  |
| -z  |
| z/d |</code></pre><p>에서 <span style="background-color: #dcffe4">실제 3차원 공간의 x, y, z 좌표</span>를 계산하기 위해서는 
동차좌표의 마지막 요소(z/d)로 이전 요소로 나누는 작업이 필요함
이러한 작업을 <span style="background-color: #dcffe4"><strong>원근분할(Perspective Division)</strong></span>이라고 함</p>
<h4 id="정규화-가시-부피에-의한-투상">정규화 가시 부피에 의한 투상</h4>
<p>절단 사각뿔(frustum) = 절두체
<img src="https://velog.velcdn.com/images/hi_soap/post/0192ed83-0213-4e69-95f7-69283741ea8f/image.png" alt=""></p>
<ul>
<li>절두체 내부에 있는 물체만 관측 가능하고, 외부에 있는 물체는 관측 불가능함</li>
</ul>
<h4 id="원근-투상에서의-가시-부피-정규화">원근 투상에서의 가시 부피 정규화</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/e0181e59-b2a5-4eb6-84e1-03b9a57f644b/image.png" alt="">
일반적 형태의 가시부피
<code>void glFrustum(GLdouble left, right, bottom, top, near, far)</code>
<img src="https://velog.velcdn.com/images/hi_soap/post/7b85db15-3151-426e-868c-8fa01ce659b6/image.png" alt=""></p>
<ol>
<li>(a) 형태의 모양을 좌우 대칭시켜서 (b) 모양으로 만듦</li>
<li>(b) 형태의 모양을 늘려서 각도를 90º로 만듦</li>
<li>(c) 형태의 좌표를 (d) 좌표로 표현함</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[주식가격]]></title>
            <link>https://velog.io/@hi_soap/%EC%A3%BC%EC%8B%9D%EA%B0%80%EA%B2%A9</link>
            <guid>https://velog.io/@hi_soap/%EC%A3%BC%EC%8B%9D%EA%B0%80%EA%B2%A9</guid>
            <pubDate>Thu, 30 Apr 2026 03:48:48 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.04.30</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요.</p>
<h2 id="제한사항">제한사항</h2>
<ul>
<li>prices의 각 가격은 1 이상 10,000 이하인 자연수입니다.</li>
<li>prices의 길이는 2 이상 100,000 이하입니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/f7e9ceaf-e2b2-40de-a2a5-139b30cef646/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="나의-코드">나의 코드</h3>
<h4 id="소요-시간-32분">소요 시간: 32분</h4>
<h4 id="시간-복잡도-on">시간 복잡도: O($n$)</h4>
<hr>
<p><code>뒤에 있는 큰 수 찾기</code> 문제와 같은 알고리즘을 사용하여
<code>Monotonic Stack</code>을 사용해서 뒤에 큰 수가 오면 일괄적으로 해결하는 방식을 사용하였다.</p>
<p>현재 값보다 큰 값은 while에서 전부 제거되기 때문에 스택에 큰 값이 남아있을 수가 없다.</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int[] solution(int[] prices) {
        int len = prices.length;
        int n;
        int[] answer = new int[len];
        Deque&lt;Integer&gt; stack = new ArrayDeque&lt;&gt;();

        for (int i = 0; i &lt; len; i++) {
            while(!stack.isEmpty() &amp;&amp; prices[stack.peek()] &gt; prices[i]) {
                n = stack.pop();
                answer[n] = i - n;
            }
            stack.push(i); // [0]: 인덱스값, [1]: 실제 값
        }
        while(!stack.isEmpty()) {
            n = stack.pop();
            answer[n] = len - n - 1;
        }

        return answer;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<h4 id="시간-복잡도-on-1">시간 복잡도: O($n$)</h4>
<hr>
<p>변수 선언 위치만 변경됨
<code>int n</code>은 스택 메모리에 할당되는데, 루프마다 선언/해제가 일어나도 스택 메모리는 매우 빠르고 컴파일러가 최적화함.</p>
<p>변수는 사용하는 범위(scope)에서만 선언하는 게 좋은 습관임. 성능보다 코드 안전성과 가독성을 위한 것임.</p>
<hr>
<pre><code class="language-java">import java.util.Deque;
import java.util.ArrayDeque;

class Solution {
    public int[] solution(int[] prices) {
        int len = prices.length;
        int[] answer = new int[len];
        Deque&lt;Integer&gt; stack = new ArrayDeque&lt;&gt;();

        for (int i = 0; i &lt; len; i++) {
            while (!stack.isEmpty() &amp;&amp; prices[stack.peek()] &gt; prices[i]) {
                int n = stack.pop();  // n을 루프 안에서 선언
                answer[n] = i - n;
            }
            stack.push(i);
        }
        while (!stack.isEmpty()) {
            int n = stack.pop();
            answer[n] = len - n - 1;
        }

        return answer;
    }
}</code></pre>
<h2 id="java-컬렉션-스택큐-구조">Java 컬렉션 스택/큐 구조</h2>
<pre><code>Deque (인터페이스)
├── ArrayDeque    // 배열 기반, 가장 빠름 ✅ 권장
└── LinkedList    // 연결 리스트 기반

Stack (클래스)   // 구식, 잘 안 씀
└── Vector 상속

Queue (인터페이스)
├── LinkedList
├── PriorityQueue
└── ArrayDeque

Deque&lt;Integer&gt; deque = new ArrayDeque&lt;&gt;();</code></pre><pre><code class="language-java">// 스택으로 사용 (LIFO)
deque.push(1);    // 앞에 삽입
deque.pop();      // 앞에서 꺼내기

// 큐로 사용 (FIFO)
deque.offer(1);   // 뒤에 삽입
deque.poll();     // 앞에서 꺼내기

// 양방향
deque.addFirst(1);  // 앞에 삽입
deque.addLast(1);   // 뒤에 삽입
deque.peekFirst();  // 앞 보기
deque.peekLast();   // 뒤 보기</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자기소개서 작성법]]></title>
            <link>https://velog.io/@hi_soap/%EC%9E%90%EA%B8%B0%EC%86%8C%EA%B0%9C%EC%84%9C-%EC%9E%91%EC%84%B1%EB%B2%95</link>
            <guid>https://velog.io/@hi_soap/%EC%9E%90%EA%B8%B0%EC%86%8C%EA%B0%9C%EC%84%9C-%EC%9E%91%EC%84%B1%EB%B2%95</guid>
            <pubDate>Wed, 29 Apr 2026 06:26:44 GMT</pubDate>
            <description><![CDATA[<p>LinkedIn 활용하기</p>
<h2 id="chapter_01">chapter_01</h2>
<h3 id="변화하는-채용환경-전략적-취업준비">변화하는 채용환경, 전략적 취업준비?</h3>
<h4 id="2026-채용시장-특징">2026 채용시장 특징</h4>
<ul>
<li>인공지능 활용 증가</li>
<li>직무중심 채용 증가</li>
<li>경력중심 채용 증가</li>
</ul>
<p>희망직무와 자기소개서의 지원 동기는 반드시 일치해야 함</p>
<p>자격증</p>
<ul>
<li>취득일자가 최신 또는 직무에서 가장 임팩트 있는 것 부터 맨 앞에 배치</li>
</ul>
<p>활동, 경력사항</p>
<ul>
<li>돈을 받고 하면 경력 + 어떤 역할을 했는지?</li>
<li>고객 응대 및 재고 관리 → 월 고객 클레임 20% 감소에 기여</li>
</ul>
<h3 id="자소서-쓰기-전">자소서 쓰기 전</h3>
<h4 id="경험정리">경험정리</h4>
<ul>
<li>학년별 경험정리</li>
<li>만다라트, 마인드 맵</li>
</ul>
<h4 id="직무-설정-및-분석">직무 설정 및 분석</h4>
<h4 id="희망기업-분석">희망기업 분석</h4>
<h3 id="자소서-작성-기법">자소서 작성 기법</h3>
<h4 id="soara-기법">SOARA 기법</h4>
<h4 id="지원동기">지원동기</h4>
<p>회사 분석과 그에 따른 내가 하고자 하는 기여
주요포인트에 대한 나만의 생각과 목표
주요포인트에 대한 나의 경험 서술
회사/직무에 기여하고 싶은 부분</p>
<h4 id="입사-후-포부">입사 후 포부</h4>
<p>회사에서 이루고 싶은 것(장기적)
목표를 이루기 위한 플랜
직무에 대해 연차별 계획
한번 더 이루고자 하는 꿈
CDP</p>
<h4 id="실습-희망분야">실습 희망분야</h4>
<p>이 분야에 관심을 갖게 된 구체적 계기?
연결 경험과 보유 역량
이 기업을 선택한 이유
실습을 통해 얻고 싶은 것</p>
<h4 id="직무-전문성soara-기법으로-작성">직무 전문성(SOARA 기법으로 작성)</h4>
<p>핵심역량 정의와 자신의 역량 관련 경험
경험 관련 상황/과제/문제
구체적인 문제해결을 위한 노력
성과 및 기여 의지</p>
<h4 id="열정도전-경험">열정/도전 경험</h4>
<p>도전 동기 &amp; 상황 정의
포기하지 않은 이유 &amp; 나만의 생각
구체적 행동
결과 &amp; 배운점</p>
<h4 id="성격-장단점">성격 장단점</h4>
<p>회사에서 일을 하는데에 필요한 성격인가?
장점 언급</p>
<p>장점 사례
단점과 단점으로 인한 문제
극복방법과 마무리</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[k진수에서 소수 개수 구하기]]></title>
            <link>https://velog.io/@hi_soap/k%EC%A7%84%EC%88%98%EC%97%90%EC%84%9C-%EC%86%8C%EC%88%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hi_soap/k%EC%A7%84%EC%88%98%EC%97%90%EC%84%9C-%EC%86%8C%EC%88%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 29 Apr 2026 06:25:21 GMT</pubDate>
            <description><![CDATA[<p><strong>2026.04.29</strong></p>
<h2 id="문제-설명">문제 설명</h2>
<p>양의 정수 n이 주어집니다. 이 숫자를 k진수로 바꿨을 때, 변환된 수 안에 아래 조건에 맞는 소수(Prime number)가 몇 개인지 알아보려 합니다.</p>
<ul>
<li>0P0처럼 소수 양쪽에 0이 있는 경우</li>
<li>P0처럼 소수 오른쪽에만 0이 있고 왼쪽에는 아무것도 없는 경우</li>
<li>0P처럼 소수 왼쪽에만 0이 있고 오른쪽에는 아무것도 없는 경우</li>
<li>P처럼 소수 양쪽에 아무것도 없는 경우</li>
<li>단, P는 각 자릿수에 0을 포함하지 않는 소수입니다.</li>
<li><ul>
<li>예를 들어, 101은 P가 될 수 없습니다.</li>
</ul>
</li>
</ul>
<p>예를 들어, 437674을 3진수로 바꾸면 211020101011입니다. 여기서 찾을 수 있는 조건에 맞는 소수는 왼쪽부터 순서대로 211, 2, 11이 있으며, 총 3개입니다. (211, 2, 11을 k진법으로 보았을 때가 아닌, 10진법으로 보았을 때 소수여야 한다는 점에 주의합니다.) 211은 P0 형태에서 찾을 수 있으며, 2는 0P0에서, 11은 0P에서 찾을 수 있습니다.</p>
<p>정수 n과 k가 매개변수로 주어집니다. n을 k진수로 바꿨을 때, 변환된 수 안에서 찾을 수 있는 위 조건에 맞는 소수의 개수를 return 하도록 solution 함수를 완성해 주세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>1 ≤ n ≤ 1,000,000</li>
<li>3 ≤ k ≤ 10</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/b5c1dcad-515a-42a4-9236-28d6461450bb/image.png" alt=""></p>
<h2 id="문제-풀이">문제 풀이</h2>
<h3 id="1차-실행-오류">1차 실행 오류</h3>
<hr>
<p>런타임 에러 및 실패
<code>str</code>에 0이 저장되는 경우 발생</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;

class Solution {
    private String str = &quot;&quot;;
    private List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    public void baseK(int num, int k) {
        if (num == 0) {
            return;
        }
        baseK(num / k, k);
        if (str != &quot;&quot; &amp;&amp; (num % k) == 0) {
            list.add(Integer.parseInt(str));
            str = &quot;&quot;;
        }
        else {
            str += String.valueOf(num % k);
        }
    }
    public boolean isPrime(int num) {
        if (num == 1) {
            return false;
        }
        for (int i = 2; i &lt; num / 2; i++) {
            if (num % 2 == 0) {
                return false;
            }
        }
        return true;
    }
    public int solution(int n, int k) {
        int answer = 0;
        baseK(n, k);
        if (str != &quot;&quot;) { // n이 비지 않았을 때
            list.add(Integer.parseInt(str));
        }
        for (int i = 0; i &lt; list.size(); i++) {
            if (isPrime(list.get(i))) {
                answer++;
            }
        }
        return answer;
    }
}</code></pre>
<h3 id="2차-실행-오류">2차 실행 오류</h3>
<hr>
<p>런타임 오류 및 실패
<code>String</code>을 <code>==</code>를 이용한 비교 오류, <code>.equals()</code>사용해야함
<code>isPrime()</code> <code>i % 2</code>로만 나누는 오류 발견</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;

class Solution {
    private String str = &quot;&quot;;
    private List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    public void baseK(int num, int k) {
        if (num == 0) {
            return;
        }
        baseK(num / k, k);
        if (str != &quot;&quot; &amp;&amp; (num % k) == 0) {
            list.add(Integer.parseInt(str));
            str = &quot;&quot;;
        }
        else if (num % k != 0) {
            str += String.valueOf(num % k);
        }
    }
    public boolean isPrime(int num) {
        if (num == 1) {
            return false;
        }
        for (int i = 2; i &lt; num / 2; i++) {
            if (num % 2 == 0) {
                return false;
            }
        }
        return true;
    }
    public int solution(int n, int k) {
        int answer = 0;
        baseK(n, k);
        if (str != &quot;&quot;) { // n이 비지 않았을 때
            list.add(Integer.parseInt(str));
        }
        for (int i = 0; i &lt; list.size(); i++) {
            if (isPrime(list.get(i))) {
                answer++;
            }
        }
        return answer;
    }
}</code></pre>
<h3 id="3차-실행-오류">3차 실행 오류</h3>
<hr>
<p>런타임 오류</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;

class Solution {
    private String str = &quot;&quot;;
    private List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    public void baseK(int num, int k) {
        if (num == 0) {
            return;
        }
        baseK(num / k, k);
        if (!str.equals(&quot;&quot;) &amp;&amp; (num % k) == 0) { // str이 비어있지 않고, num % k == 0일 때 저장
            list.add(Integer.parseInt(str));
            str = &quot;&quot;;
        }
        else if (num % k != 0) { // str에는 0 저장하지 않음
            str += String.valueOf(num % k);
        }
    }
    public boolean isPrime(int num) {
        if (num &lt; 2) {
            return false;
        }
        for (int i = 2; i &lt; num / 2; i++) {
            if (num % i == 0) {
                return false;
            }
        }
        return true;
    }
    public int solution(int n, int k) {
        int answer = 0;
        baseK(n, k);
        if (!str.equals(&quot;&quot;)) { // n이 비지 않았을 때
            list.add(Integer.parseInt(str));
        }
        for (int i = 0; i &lt; list.size(); i++) {
            if (isPrime(list.get(i))) {
                answer++;
            }
        }
        return answer;
    }
}</code></pre>
<h3 id="나의-코드ai-활용">나의 코드(AI 활용)</h3>
<h4 id="소요-시간-58분">소요 시간: 58분</h4>
<h4 id="시간-복잡도-o√n-×-log-n">시간 복잡도: O(√n × log n)</h4>
<hr>
<p><code>int</code>로 변환되는 수가 범위를 초과할 가능성이 있기 때문에
<code>long</code> 타입으로 선언해주어야 함
타입의 <span style="background-color: #dcffe4">범위 초과 오류</span>를 고려해서 코드를 작성할 것</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;

class Solution {
    private String str = &quot;&quot;;
    private List&lt;Long&gt; list = new ArrayList&lt;&gt;();
    public void baseK(int num, int k) {
        if (num == 0) {
            return;
        }
        baseK(num / k, k);
        if ((num % k) == 0) { // str이 비어있지 않고, num % k == 0일 때 저장
            if (!str.equals(&quot;&quot;)) {
                list.add(Long.parseLong(str));
                str = &quot;&quot;;
            }
        }
        else { // str에는 0 저장하지 않음
            str += String.valueOf(num % k);
        }
    }
    public boolean isPrime(long num) {
        if (num &lt; 2) {
            return false;
        }
        for (long i = 2; i * i &lt;= num; i++) {
            if (num % i == 0) {
                return false;
            }
        }
        return true;
    }
    public int solution(int n, int k) {
        int answer = 0;
        baseK(n, k);
        if (!str.equals(&quot;&quot;)) { // n이 비지 않았을 때
            list.add(Long.parseLong(str));
        }
        for (int i = 0; i &lt; list.size(); i++) {
            if (isPrime(list.get(i))) {
                answer++;
            }
        }
        return answer;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<hr>
<p><code>.toString()</code> 오버로딩 방법에 따라</p>
<ul>
<li>1) 그냥 문자열 반환</li>
<li>2) n진수를 문자열로 반환</li>
</ul>
<hr>
<pre><code class="language-java">class Solution {
    public boolean isPrime(long num) {
        if (num &lt; 2) return false;
        for (long i = 2; i * i &lt;= num; i++) {
            if (num % i == 0) return false;
        }
        return true;
    }

    public int solution(int n, int k) {
        // 1. k진수 변환 (Java 내장 함수 사용)
        String converted = Integer.toString(n, k);

        // 2. 0으로 split 후 바로 소수 판별
        int answer = 0;
        for (String part : converted.split(&quot;0&quot;, -1)) {
            if (part.isEmpty()) continue;
            if (isPrime(Long.parseLong(part))) answer++;
        }

        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[07. 교착상태]]></title>
            <link>https://velog.io/@hi_soap/07.-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@hi_soap/07.-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C</guid>
            <pubDate>Tue, 28 Apr 2026 07:34:31 GMT</pubDate>
            <description><![CDATA[<h2 id="강의-목표">강의 목표</h2>
<ul>
<li><span style="background-color: #dcffe4">식사하는 철학자 문제</span>를 통해 처음으로 제기된 교착상태 개념을 이해한다.</li>
<li>컴퓨터 시스템에서의 <span style="background-color: #dcffe4">교착상태</span> 정의와 
교착상태를 판단할 수 있는 <span style="background-color: #dcffe4">자원 할당 그래프</span>를 이해한다.</li>
<li>교착상태가 유발 가능한 <span style="background-color: #dcffe4">코프만의 4가지 조건</span>을 이해한다.</li>
<li>교착상태의 <span style="background-color: #dcffe4">해결 방법</span>을 이해한다.</li>
<li>교착상태 예방, 회피, 감지 및 복구, 무시*</li>
<li>현대에서 교착상태 해결 방법은 <span style="background-color: #dcffe4">교착상태 무시 방법</span>임을 안다.</li>
</ul>
<h2 id="01-교착상태-문제-제기">01. 교착상태 문제 제기</h2>
<h3 id="11-무한-대기와-교착상태">1.1 무한 대기와 교착상태</h3>
<h4 id="교착상태deadlock">교착상태(deadlock)</h4>
<ul>
<li>자원을 소유한 채, 모두 상대방이 소유한 자원을 기다리면서 무한 대기</li>
<li>컴퓨터 시스템에서 교착상태는 <span style="background-color: #dcffe4">다중프로그래밍 도입과 함께 발생</span>하게 되었다.*</li>
<li><span style="background-color: #dcffe4">스레드 동기화 문제의 연장선</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/35757fbd-6e1d-4144-b7ac-311d9ec19af0/image.png" alt=""></p>
<h3 id="12-식사하는-철학자-문제dining-philosophers-problem">1.2 식사하는 철학자 문제(Dining Philosophers Problem)</h3>
<h4 id="문제의-개요">문제의 개요</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/f38bb422-16d6-44e8-a844-fade737009c5/image.png" alt=""></p>
<p>1965 년 Edsger Dijkstra에 의해 처음으로 문제화
병렬처리(concurrent programming)에서의 동기화 이슈와 해결 방법을 설명하고자 낸 학생 시험 문제(네델란드의 Eindhoven University of Technology) </p>
<ul>
<li>5명의 철학자가 원탁에서 식사. 식사 시간은 서로 다를 수 있음</li>
<li>자리마다 스파게티 1개와 양 옆에 포크 있음</li>
<li>각 철학자는 옆의 철학자에게 말할 수 없음</li>
<li>식사를 하기 위해서는 양 옆의 포크를 모두 확보하여야 함 </li>
<li>왼쪽 포크를 먼저 들고, 다음 오른쪽 포크를 드는 순서</li>
<li>포크가 사용 중이면 대기</li>
</ul>
<p><strong>문제</strong>
모두 <span style="background-color: #dcffe4">거의 동시에</span> 왼쪽 포크를 든 후, 오른쪽 포크를 들려고 할 때, 
모두 상대가 가진 포크를 기다리면서 먹을 수 없는 교착 상태 발생</p>
<p><strong>해결 방법</strong>
5명 중 마지막 사람을 제외한 4명이 왼쪽 포크를 먼저 잡고,
오른쪽 포크를 잡는 순서로 진행하고, 
마지막 사람은 오른쪽 포크를 잡고 왼쪽 포크를 잡도록 함</p>
<h4 id="철학자가-식사하는-모든-경우-분석">철학자가 식사하는 모든 경우 분석</h4>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/d2506514-e023-4084-b9cc-fe65f1de218f/image.png" alt=""></p>
<h4 id="철학자들의-교착상태-원인---환형-요청대기circular-requestwait">철학자들의 교착상태 원인 - 환형 요청/대기(circular request/wait)</h4>
<ul>
<li>5명 모두 왼쪽 포크를 가지고 오른쪽 포크를 요청하는 <span style="background-color: #dcffe4">환형 고리</span> 형성</li>
<li>환형 고리는 <span style="background-color: #dcffe4">스스로 인식하거나 해체 불가</span></li>
</ul>
<h4 id="교착상태-해결---환형-요청대기가-생기지-않게-규칙-변경">교착상태 해결 - 환형 요청/대기가 생기지 않게 규칙 변경</h4>
<ul>
<li>마지막 철학자만 오른쪽 포크를 먼저 잡고 왼쪽을 잡도록 <span style="background-color: #dcffe4">규칙 수정</span>
→ <span style="color: red">알고리즘으로 어떻게 표현할 것인가?</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/ee00629d-b211-44df-bdd5-b1dfc3e47bcf/image.png" alt=""></p>
<h3 id="13-식사하는-철학자와-컴퓨터-시스템">1.3 식사하는 철학자와 컴퓨터 시스템</h3>
<h4 id="식사하는-철학자-문제는-교착-상태-문제를-비유">식사하는 철학자 문제는 교착 상태 문제를 비유</h4>
<p><em>교착상태는 다중프로그래밍 시스템 초기에 노출된 문제점</em></p>
<ul>
<li>철학자: 스레드</li>
<li>포크: 자원</li>
<li>스파게티: 스레드가 처리할 작업</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/6f9191a3-c42f-496a-bc88-bada09fd393f/image.png" alt=""></p>
<h2 id="02-교착상태">02. 교착상태</h2>
<h3 id="21-교착상태-정의">2.1 교착상태 정의</h3>
<h4 id="교착상태deadlock-1">교착상태(deadlock)</h4>
<ul>
<li>자원을 소유한 2개 이상의 스레드들 사이에서, 
각 스레드는 다른 스레드가 소유한 자원을 요청하여 무한정 대기하는 현상</li>
<li>1965년 Dijkstra의 banker&#39;s algorithm research에서 처음 제기*</li>
<li><span style="background-color: #dcffe4">풀지 못하는 포옹(deadly embrace)</span>이라고도 함*</li>
</ul>
<h4 id="교착상태-발생-위치">교착상태 발생 위치</h4>
<ul>
<li><p>사용자가 작성한 <span style="background-color: #dcffe4">멀티스레드 응용프로그램</span>에서 주로 발생</p>
</li>
<li><p>정교하지 못한 코딩에서 비롯*</p>
</li>
<li><p><span style="background-color: #dcffe4">커널 내</span>에서도 발생</p>
</li>
<li><p>하지만 거의 발생하지 않음, 매우 정교하게 작성되었기 때문*</p>
</li>
<li><p>교착상태를 막도록 운영하는 컴퓨터 시스템은 거의 없음</p>
</li>
<li><p>막는 데에 <span style="background-color: #dcffe4">많은 시간과 공간 비용 때문</span>*</p>
</li>
<li><p>교착상태가 발생하도록 두고, 교착상태가 발생한 것 같으면, 
<span style="background-color: #dcffe4"><strong>사용자가</strong> 시스템 재시작, 또는 의심스러운 몇몇 프로그램 종료</span>*</p>
</li>
</ul>
<h4 id="전형적인-멀티스레드-교착상태-사례">전형적인 멀티스레드 교착상태 사례</h4>
<ul>
<li><p>2개의 스레드가 <span style="background-color: #dcffe4">각각 락 소유</span>하고, 상대가 가진 락을 요청하고 기다릴 때</p>
</li>
<li><p><span style="background-color: #dcffe4">단일 CPU/다중 CPU 모두에서 발생</span> 
T1과 T2가 <span style="background-color: #dcffe4">서로 다른 CPU에서 실행될 때에도 발생</span>*</p>
</li>
<li><p><span style="background-color: #dcffe4">락과 자원에 대한 경쟁</span>이 있는 한 교착상태는 언제든 발생 가능
<img src="https://velog.velcdn.com/images/hi_soap/post/024e8c15-aaa1-4a03-8ede-36f47353b9a5/image.png" alt=""></p>
</li>
</ul>
<h3 id="22-컴퓨터-시스템에-잠재된-교착상태-유발-요인">2.2 컴퓨터 시스템에 잠재된 교착상태 유발 요인</h3>
<h4 id="1-자원">1) 자원</h4>
<ul>
<li>자원은 <span style="background-color: #dcffe4">교착 상태의 발원지</span></li>
<li>교착상태는 멀티스레드가 <span style="background-color: #dcffe4">자원을 동시에 사용하려는 충돌</span>이 요인</li>
<li><em>컴퓨터 시스템의 자원*</em></li>
<li><em>소프트웨어 자원*</em>: 뮤텍스, 스핀락, 세마포, 파일, 데이터베이스 파일 락</li>
<li>파일 락: 스레드가 파일의 일부나 전체를 독점 사용하기 위한 잠금
(Unix에서 <code>flock</code>, <code>POSIX</code>의 파일락 등)*</li>
<li><em>하드웨어 자원*</em>: 프린터, 메모리, 프로세서 등</li>
</ul>
<h4 id="2-자원과-스레드">2) 자원과 스레드</h4>
<ul>
<li>한 스레드가 <span style="background-color: #dcffe4">여러 자원을 동시에 필요</span>로 하는 상황이 요인
<span style="background-color: #dcffe4"><em>한 개의 자원만 필요한 경우 교착상태가 발생하지 않음</em></span></li>
</ul>
<h4 id="3-자원과-운영체제">3) 자원과 운영체제</h4>
<ul>
<li>한 번에 하나씩 자원을 할당하는 <span style="background-color: #dcffe4">운영체제 정책</span>이 요인</li>
<li>스레드가 자원을 필요로 할 때, <span style="background-color: #dcffe4">반드시 운영체제로부터 할당받아야 함</span></li>
<li>운영체제로부터 필요한 자원을 <span style="background-color: #dcffe4">한 번에 모두</span> 할당받는다면, 
교착상태가 발생하지 않을 수 있음*</li>
</ul>
<h4 id="4-자원-비선점">4) 자원 비선점</h4>
<ul>
<li>할당된 자원은 스레드가 자발적으로 내놓기 전에 <span style="background-color: #dcffe4">강제로 뺏지 못하는 정책</span></li>
<li>대부분의 운영체제는 스레드가 할당받은 자원을 <span style="background-color: #dcffe4">강제로 빼앗지 못한다.</span></li>
<li>강제로 빼앗아 기다리는 스레드에게 줄 수 있다면, 
교착상태가 발생하지 않는다.*</li>
<li>빼앗긴 스레드에게는 문제가 발생*</li>
</ul>
<h3 id="23-교착상태-모델링">2.3 교착상태 모델링</h3>
<h4 id="자원-할당-그래프resource-allocation-graph-rag">자원 할당 그래프(Resource Allocation Graph, RAG)</h4>
<ul>
<li><p>컴퓨터 시스템에서 자원 할당 그래프를 이용하여 <span style="background-color: #dcffe4">교착 상태를 표현</span>하고
이 그래프를 기반으로 <span style="background-color: #dcffe4">교착상태 예방, 회피, 감지</span> 등이 운용됨</p>
</li>
<li><p>컴퓨터 시스템에 존재하는 자원과 스레드들의 상태를 나타내는
<span style="background-color: #dcffe4">방향성 그래프(graph)</span>이다.</p>
</li>
</ul>
<p><em>부모/자식 관계가 성립하는 트리 구조로는 구현할 수 없음</em></p>
<h4 id="그래프의-요소">그래프의 요소</h4>
<ul>
<li><strong>꼭짓점(vertex)</strong>
스레드(원), 자원(사각형)</li>
<li><strong>간선(edge)</strong>
할당 간선(allocation edge)과 요청 간선(request edge), 소유-요청 관계</li>
</ul>
<h4 id="표현되는-정보">표현되는 정보</h4>
<ul>
<li>컴퓨터 시스템에 <span style="background-color: #dcffe4">실행 중인 전체 스레드와 자원</span></li>
<li>각 자원의 <span style="background-color: #dcffe4">총 인스턴스 개수와 할당 가능한 인스턴스 개수</span></li>
<li>각 스레드가 할당받아 <span style="background-color: #dcffe4">소유하고 있는 자원의 인스턴스 개수</span></li>
<li>각 스레드가 실행에 필요한 <span style="background-color: #dcffe4">자원 유형과 인스턴스 개수</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/5897661c-ff76-43b9-a6ed-fe4596d755aa/image.png" alt=""></p>
<h4 id="교착상태가-발생한-자원-할당-그래프-모양">교착상태가 발생한 자원 할당 그래프 모양</h4>
<ul>
<li>스레드들이 교착 상태에 있게 되면, 
스레드와 자원들을 연결하는 간선들의 <span style="background-color: #dcffe4">환형 고리</span>가 나타남</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/3c9562a2-5d7d-4b80-85ee-91151b7eb1df/image.png" alt=""></p>
<h2 id="03-교착상태-해결">03. 교착상태 해결</h2>
<h3 id="31-코프만-조건coffman-condition">3.1 코프만 조건(coffman condition)</h3>
<ul>
<li>교착상태를 유발할 수 있는 <span style="background-color: #dcffe4">4가지의 필요충분 조건</span></li>
</ul>
<h4 id="다음-4가지-조건을-모두-가진-시스템에서는-언제든-교착-상태가-발생할-수-있음">다음 4가지 조건을 모두 가진 시스템에서는 언제든 교착 상태가 발생할 수 있음</h4>
<ul>
<li><p><strong>상호 배제(Mutual Exclusion)</strong>
각 자원은 한 번에 한 스레드에게만 할당</p>
</li>
<li><p><strong>소유하면서 대기(Hold &amp; Wait)</strong>
스레드가 자원을 소유하면서 다른 자원 대기 </p>
</li>
<li><p><strong>강제 자원 반환 불가(No Preemption)</strong>
스레드에게 할당된 자원을 강제로 빼앗지 못함</p>
</li>
<li><p><strong>환형 대기(Circular Wait)</strong>
한 그룹의 스레드들에서 각 스레드가 
다른 스레드가 소유한 자원을 요청하는 환형 고리 형성</p>
</li>
</ul>
<p>→ 이 4가지 조건 중 <span style="background-color: #dcffe4">한가지라도 성립되지 않으면</span> 교착상태에 빠지지 않음</p>
<h3 id="32-교착상태-해결-방법">3.2 교착상태 해결 방법</h3>
<h4 id="1-교착상태-예방prevention">1) 교착상태 예방(prevention)</h4>
<ul>
<li>코프만의 4가지 조건 중 하나 이상의 조건이 아예 성립되지 못하도록 
시스템을 설계하고 구성하여 <span style="background-color: #dcffe4">교착상태가 발생할 여지가 없도록 예방하는 것</span></li>
</ul>
<h4 id="2-교착상태-회피avoidance">2) 교착상태 회피(avoidance)</h4>
<ul>
<li><p>운영체제가 자원을 할당할 때 
<span style="background-color: #dcffe4">교착상태에 빠지지 않을 것이라고 확신하는 경우에만 자원을 할당</span></p>
</li>
<li><p>시스템을 <span style="background-color: #dcffe4">안전한 상태와 불안전한 상태</span>로 분류하여
<span style="background-color: #dcffe4">안전한 상태인 경우에만 자원 할당</span></p>
</li>
<li><p>자원을 할당할 때 마다 교착상태 가능성을 검사하므로 
<span style="background-color: #dcffe4">시스템의 성능을 많이 저하시킴</span></p>
</li>
</ul>
<h4 id="3-교착상태-감지-및-복구detection-and-recovery">3) 교착상태 감지 및 복구(detection and recovery)</h4>
<ul>
<li><p>운영체제가 교착상태를 감지하는 <span style="background-color: #dcffe4">별도의 프로그램을 백그라운드로 구동</span>시켜
교착상태에 빠진 스레드 그룹을 발견하면, 교착상태로부터 해제</p>
</li>
<li><p>교착상태를 감시하는 작업이 주기적으로 실행되어야 하므로
<span style="background-color: #dcffe4">시스템에 많은 부담</span></p>
</li>
</ul>
<h4 id="4-교착상태-무시ignore-reboot">4) 교착상태 무시(ignore reboot)</h4>
<ul>
<li><p><span style="background-color: #dcffe4">교착상태가 발생하도록 내버려 두는 방법</span></p>
</li>
<li><p><span style="background-color: #dcffe4">교착상태는 웬만해서 발생하지 않으며,</span> 
발생했을 때 <span style="background-color: #dcffe4">사용자나 관리자가 대책</span>을 세우면 된다고 생각함</p>
</li>
<li><p>교차상태가 발생한다 하더라도, <span style="background-color: #dcffe4">범용 시스템에서는 큰 문제가 되지 않음</span></p>
</li>
<li><p>현재 대부분의 범용 운영체제에서 사용하고 있음</p>
</li>
<li><p>교착상태 무시 알고리즘을 ostrich 알고리즘(타조 알고리즘)이라고 함</p>
</li>
</ul>
<h4 id="tip-교착상태로-인해-시스템-전체가-중단되는가">TIP 교착상태로 인해 시스템 전체가 중단되는가?</h4>
<ul>
<li>교착상태가 발생한다고 해서 <span style="background-color: #dcffe4">시스템 전체가 멈추지 않음</span></li>
<li>많은 스레드들이 교착상태에 있을 때는 시스템을 <span style="background-color: #dcffe4">재시작(reboot)</span></li>
</ul>
<h3 id="33-교착상태-예방">3.3 교착상태 예방</h3>
<ul>
<li>코프만의 4가지 조건 중 최소 하나를 성립하지 못하게 함</li>
</ul>
<h4 id="1-상호배제mutual-exclusion-→-상호배제-없애기">1. 상호배제(Mutual Exclusion) → 상호배제 없애기</h4>
<ul>
<li>2개 이상의 스레드가 <span style="background-color: #dcffe4">동시에 자원을 사용할 수 있도록 허용</span></li>
</ul>
<p><span style="color: red">근본적으로 적용 불가능한 방법</span></p>
<h4 id="2-소유하면서-대기hold--wait-→-기다리지-않게">2. 소유하면서 대기(Hold &amp; Wait) → 기다리지 않게</h4>
<ul>
<li><p>1) 스레드가 필요한 자원을 <span style="background-color: #dcffe4">처음부터 모두 가지게 함</span></p>
</li>
<li><p>당장 사용하지 않는 자원을 스레드에게 묶어두기 때문에 
<span style="background-color: #dcffe4">자원 활용률이 떨어짐</span>*</p>
</li>
<li><p>2) 스레드가 자원을 소유한 상태에서 새로운 자원이 필요하게 되면,
<span style="background-color: #dcffe4">현재 할당받은 모든 자원을 반환하고, 
필요한 모든 자원을 한꺼번에 모두 요청하는 방법</span></p>
</li>
</ul>
<p><span style="color: red">1, 2 모두 가능하지 않거나, 매우 비효율적</span>
<em>범용 시스템에서는 불가능, <span style="background-color: #dcffe4">임베디드 시스템에서는 가능</em></span></p>
<h4 id="3-강제-자원-반환-불가no-preemption-→-자원의-선점-허용">3. 강제 자원 반환 불가(No-Preemption) → 자원의 선점 허용</h4>
<ul>
<li><p>더 높은 순위의 스레드가 자원을 요청하면, 
자원을 가진 낮은 순위의 스레드에게서 <span style="background-color: #dcffe4">강제로 자원을 빼앗음</span></p>
</li>
<li><p>하지만 자원을 강제 반환하게 된 스레드가 자원을 다시 사용하게 될 때,
이전 상태로 되돌아갈 수 있도록 <span style="background-color: #dcffe4">상태를 관리해야함</span></p>
</li>
</ul>
<p><span style="color: red">간단하지 않으며 오버헤드가 큼</span></p>
<h4 id="4-환형-대기circular-wait-→-환형-대기-제거">4. 환형 대기(Circular Wait) → 환형 대기 제거</h4>
<ul>
<li>모든 자원에 번호를 매기고, 스레드는 <span style="background-color: #dcffe4">반드시 번호 순으로 자원을 할당</span></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/7583eafa-404c-4916-a974-fc5f72618860/image.png" alt=""></p>
<h3 id="34-교착상태-회피">3.4 교착상태 회피</h3>
<ul>
<li><p><span style="background-color: #dcffe4">자원할당 알고리즘</span>을 이용하여 교착상태를 방지하는 기법</p>
</li>
<li><p><strong>자원 할당 알고리즘</strong>
자원 할당을 요청받았을 때, 
앞으로 <span style="background-color: #dcffe4">환형 대기가 발생하지 않는다는 확신</span>이 서는 경우에만 자원을 할당</p>
</li>
</ul>
<h4 id="bankers-알고리즘">banker&#39;s 알고리즘</h4>
<ul>
<li><p>Edsger Dijkstra에 의해 개발된 알고리즘</p>
</li>
<li><p>자원 할당 전에 <span style="background-color: #dcffe4">미래에 교착 상태가 발생하지 않을 것인지</span> 판단하는 알고리즘</p>
</li>
<li><p><strong>안전한 상태</strong>
현재 프로세스들을 어떤 순서로 실행시켰을 때, 
모든 프로세스들이 자신이 요청하는 자원을 가지고 실행할 수 있는 상태</p>
</li>
<li><p><strong>불안전한 상태</strong>
환형 대기에 빠질 수 있는 상태</p>
</li>
<li><p><strong>알고리즘</strong>
1) 각 프로세스가 실행 시작 전에 <span style="background-color: #dcffe4">필요한 전체 자원의 수를 운영체제에게 알림</span>
2) 자원을 할당할 때 마다 안전한 상태인지, 불안전한 상태인지 판단
3) 이러한 상태는 각 스레드가 필요로 하는 자원의 개수, 
현재 실행 중인 각 스레드가 할당 받은 자원의 개수,
시스템 내 할당할 수 있는 자원의 개수를 토대로 판단함</p>
</li>
<li><p><strong>비현실적</strong>
각 프로세스가 실행 전에 필요한 자원의 개수를 아는 것은 불가능
프로세스의 개수도 동적으로 변하기 때문에, 
<span style="background-color: #dcffe4">미리 프로세스의 개수를 정적으로 고정시키는 것은 불가능</span></p>
</li>
</ul>
<p><em>사용할 자원의 개수를 미리 알 수 있는 
<span style="background-color: #dcffe4">임베디드 시스템이나, 실시간 제어 시스템에서 사용 가능</em></span></p>
<h3 id="35-교착상태-감지-및-복구">3.5 교착상태 감지 및 복구</h3>
<ul>
<li><p>교착상태를 감지하는 백그라운드 프로그램을 상시적으로 실행시켜, 
교착상태를 감지하고 해제한다</p>
</li>
<li><p>자원 할당 그래프에 <span style="background-color: #dcffe4">환형 대기 모양</span>을 갖는 부분이 있는지 판단</p>
</li>
</ul>
<h4 id="자원-강제-선점preemption">자원 강제 선점(preemption)</h4>
<ul>
<li>교착 상태에 빠진 스레드 중 하나를 선택하고,
스레드가 소유한 자원을 강제로 빼앗아 이 자원을 기다리는 스레드에게 할당</li>
<li>이렇게 함으로써 스레드의 <span style="background-color: #dcffe4">환형 대기 고리를 끊음</span>*</li>
</ul>
<h4 id="롤백rollback">롤백(rollback)</h4>
<ul>
<li>교착상태가 발생할 것으로 예측되는 스레드들의 상태를 주기적으로 저장</li>
<li>교착상태가 발생하면 <span style="background-color: #dcffe4">가장 최근에 저장해둔 상태로 복구</span></li>
<li>다시 시작하면 스케줄링 등 여러 요인에 의해 <span style="background-color: #dcffe4">자원 할당 순서가 달라짐</span></li>
</ul>
<p><em>롤백을 위한 주기적인 상태 저장으로 인해, 시스템의 시간과 저장 공간을 소모하여 <span style="background-color: #dcffe4">시스템 성능을 저하시킴</span></em></p>
<h4 id="스레드-강제-종료kill-process">스레드 강제 종료(kill process)</h4>
<ul>
<li>교착 상태에 빠진 스레드 중 하나를 강제로 종료시키는 방법</li>
<li><span style="background-color: #dcffe4">사용자나 시스템 관리자가 사용하는 방법</span></li>
</ul>
<h3 id="36-교착상태-무시-타조ostrich-알고리즘">3.6 교착상태 무시: 타조(ostrich) 알고리즘</h3>
<p><em>ostrich: 현실도피주의자</em></p>
<h4 id="교착상태를-해결할-필요가-있을까">교착상태를 해결할 필요가 있을까?</h4>
<ul>
<li>컴퓨터 시스템에서 <span style="background-color: #dcffe4">교착상태에 대한 통계치 없음</span></li>
</ul>
<h4 id="교착상태는-반드시-발생함">교착상태는 반드시 발생함</h4>
<ul>
<li>하지만, 발생 가능성이 극히 적고, 피하기 위한 비용이 많이 들어감</li>
</ul>
<h4 id="최적의-알고리즘-타조-알고리즘">최적의 알고리즘: 타조 알고리즘</h4>
<ul>
<li><p>put your head in the sand 접근법
교착상태는 발생하지 않을 것이라 생각하고 <span style="background-color: #dcffe4">아무 대책을 취하지 않는 접근법</span></p>
</li>
<li><p>Unix(리눅스)와 윈도우 등 <span style="background-color: #dcffe4">거의 모든 운영체제에서 사용</span>
의심 가는 스레드를 <span style="background-color: #dcffe4">종료시키거나 시스템 재시작(reboot)</span></p>
</li>
<li><p>관련 데이터를 잃어버릴 수 있으나, 비용 면에서 더 나은 방법임*</p>
</li>
<li><p>핵 시스템, 비행기, 미사일 등 시스템 재시작이 파국을 초래할 수 있는
hard-real time 시스템이나, 환자 감시 시스템 등에는 적합하지 않음</p>
</li>
</ul>
<blockquote>
</blockquote>
<p><strong>*&quot;Not everything worth doing is worth doing well&quot;*</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[더 맵게]]></title>
            <link>https://velog.io/@hi_soap/%EB%8D%94-%EB%A7%B5%EA%B2%8C</link>
            <guid>https://velog.io/@hi_soap/%EB%8D%94-%EB%A7%B5%EA%B2%8C</guid>
            <pubDate>Tue, 28 Apr 2026 04:26:01 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-설명">문제 설명</h2>
<p>매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같이 특별한 방법으로 섞어 새로운 음식을 만듭니다.</p>
<blockquote>
</blockquote>
<p>섞은 음식의 스코빌 지수 = 가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)</p>
<p>Leo는 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복하여 섞습니다.
Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때, 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는 최소 횟수를 return 하도록 solution 함수를 작성해주세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>scoville의 길이는 2 이상 1,000,000 이하입니다.</li>
<li>K는 0 이상 1,000,000,000 이하입니다.</li>
<li>scoville의 원소는 각각 0 이상 1,000,000 이하입니다.</li>
<li>모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우에는 -1을 return 합니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<p><img src="https://velog.velcdn.com/images/hi_soap/post/4337c484-c26c-4a2e-a342-2e6119a417ad/image.png" alt=""></p>
<h3 id="문제-풀이">문제 풀이</h3>
<h3 id="1차-실행-오류">1차 실행 오류</h3>
<hr>
<p>K 이상으로 만들 수 없는 경우 -1을 리턴하는 조건문을 추가했지만,
여전히 오류
실패, 런타임 오류, 실행 시간 초과</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;

        List&lt;Integer&gt; list = Arrays.stream(scoville).boxed().collect(Collectors.toCollection(ArrayList::new));
        list.sort(null); // 오름차순 정렬
        while(true) {
            if (list.get(0) &gt;= K) {
                break;
            }
            int first = list.get(0);
            list.remove(0);
            int second = list.get(1);
            list.remove(0);
            int res = first + second * 2;
            list.add(res);
            list.sort(null);
            answer++;
        }
        return answer;
    }
}</code></pre>
<h3 id="2차-실행-오류">2차 실행 오류</h3>
<hr>
<p><code>second</code>를 가져올 때 인덱스가 당겨진 상태라 0을 가져왔어야 하는데 1을 가져옴
실패, 런타임 오류, 실행 시간 초과</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;

        List&lt;Integer&gt; list = Arrays.stream(scoville).boxed().collect(Collectors.toCollection(ArrayList::new));
        list.sort(null); // 오름차순 정렬
        while(true) {
            if (list.size() &lt; 2 &amp;&amp; list.get(0) &lt; K) {
                return -1;
            }
            if (list.get(0) &gt;= K) {
                break;
            }
            int first = list.get(0);
            list.remove(0);
            int second = list.get(1);
            list.remove(0);
            int res = first + second * 2;
            list.add(res);
            list.sort(null);
            answer++;
        }
        return answer;
    }
}</code></pre>
<h3 id="3차-실행-오류">3차 실행 오류</h3>
<hr>
<p><code>sort()</code>로 인한 실행 시간 오류</p>
<hr>
<pre><code class="language-java">import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;

        List&lt;Integer&gt; list = Arrays.stream(scoville).boxed().collect(Collectors.toCollection(ArrayList::new));
        list.sort(null); // 오름차순 정렬
        while(true) {
            if (list.size() &lt; 2 &amp;&amp; list.get(0) &lt; K) {
                return -1;
            }
            if (list.get(0) &gt;= K) {
                break;
            }
            int first = list.get(0);
            list.remove(0);
            int second = list.get(0);
            list.remove(0);
            int res = first + second * 2;
            list.add(res);
            list.sort(null);
            answer++;
        }
        return answer;
    }
}</code></pre>
<h3 id="ai-코드">AI 코드</h3>
<hr>
<p><code>PriorityQueue</code>를 이용한 자동 정렬</p>
<hr>
<pre><code class="language-java">import java.util.PriorityQueue;

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;

        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;();
        for (int x : scoville) {
            pq.add(x);
        }

        while (pq.peek() &lt; K) {
            if (pq.size() &lt; 2) return -1;

            int first = pq.poll();
            int second = pq.poll();
            pq.add(first + second * 2);
            answer++;
        }

        return answer;
    }
}</code></pre>
<h2 id="인터페이스--구조체-패턴">인터페이스 = 구조체 패턴</h2>
<pre><code>Collection (인터페이스)
├── List        → 순서 O, 중복 O
│   ├── ArrayList     // 동적 배열, 조회 빠름
│   ├── LinkedList    // 연결 리스트, 삽입/삭제 빠름
│   └── Vector        // ArrayList의 동기화 버전 (구식)
│
├── Set         → 순서 X, 중복 X
│   ├── HashSet       // 순서 없음, 가장 빠름
│   ├── LinkedHashSet // 삽입 순서 유지
│   └── TreeSet       // 자동 오름차순 정렬
│
└── Queue       → FIFO (선입선출)
    ├── LinkedList    // 일반 큐
    ├── PriorityQueue // 우선순위 큐 (최소 힙)
    └── ArrayDeque    // 양방향 큐 (스택으로도 사용)

    └──Deque (인터페이스)
    ├── ArrayDeque  // ✅ 권장
    └── LinkedList

    Stack (클래스, 구식)
    └── Vector 상속

Map (인터페이스) → Key-Value 쌍
├── HashMap           // 순서 없음, 가장 빠름
├── LinkedHashMap     // 삽입 순서 유지
└── TreeMap           // 키 기준 자동 오름차순 정렬</code></pre><p><img src="https://velog.velcdn.com/images/hi_soap/post/5b1f7af3-bde5-47c9-8896-6a04d3a0b49b/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>