<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>losiento_nana.log</title>
        <link>https://velog.io/</link>
        <description>개발에 대한 고민과 성장의 기록을 일기장처럼 성찰하며 남기는 공간</description>
        <lastBuildDate>Mon, 28 Jul 2025 10:03:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>losiento_nana.log</title>
            <url>https://velog.velcdn.com/images/losiento_nana/profile/0904b06e-1599-4459-9d2f-8eb1b816e2cf/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. losiento_nana.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/losiento_nana" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[놀라운 운영체제 #6 – 물리 메모리 관리 上]]></title>
            <link>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5-%EB%AC%BC%EB%A6%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-%E4%B8%8A</link>
            <guid>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5-%EB%AC%BC%EB%A6%AC-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-%E4%B8%8A</guid>
            <pubDate>Mon, 28 Jul 2025 10:03:47 GMT</pubDate>
            <description><![CDATA[<p>운영체제의 메모리 관리는 단순히 “데이터를 저장하는 공간” 그 이상입니다.<br>프로세스의 실행, 보호, 효율성에 직결되는 핵심 기술입니다.</p>
<p>이 글에서는 <strong>메모리 주소 체계와 주소 바인딩 방식</strong>을 중심으로<br>메모리 관리의 개념을 탄탄하게 정리해보겠습니다.</p>
<hr>
<h2 id="📚-목차">📚 목차</h2>
<ol>
<li><a href="#1-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC%EC%9D%98-%EA%B0%9C%EC%9A%94">메모리 관리의 개요</a>  </li>
<li><a href="#2-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%A3%BC%EC%86%8C">메모리 주소</a>  </li>
<li><a href="#3-%EC%A3%BC%EC%86%8C-%EB%B0%94%EC%9D%B8%EB%94%A9">주소 바인딩</a>  </li>
<li><a href="#4-%EB%B0%94%EC%9D%B8%EB%94%A9-%EC%8B%9C%EC%A0%90-%EB%B0%8F-%EB%B9%84%EA%B5%90">바인딩 시점</a>  </li>
</ol>
<h2 id="메모리-관리의-개요">메모리 관리의 개요</h2>
<h3 id="1-1-메모리-관리의-이중성">1-1. 메모리 관리의 이중성</h3>
<ul>
<li>일괄처리 시스템은 한번에 하나의 작업만 처리하므로 메모리 관리가 단순함</li>
<li>시분할 시스템에서는 운영체제를 포함한 모든 응용 프로그램이 메모리에 올라
와 실행되기 때문에 메모리 관리가 복잡함</li>
</ul>
<p>이때 프로세스 입장에서는 메모리를 독차지하려 하고, 메모리 관리자 입장에서는 되도록 관리를 효율적으로 하고 싶어합니다.</p>
<h3 id="1-2-메모리-관리-작업">1-2 메모리 관리 작업</h3>
<h4 id="메모리-가져오기fetch">메모리 가져오기(fetch)</h4>
<ul>
<li>실행할 프로세스와 데이터를 <strong>언제 메모리로 가져올지 결정하는 정책</strong></li>
</ul>
<ol>
<li>요구 적재: 운영체제나 시스템 프로그램, 사용자 프로그램 등 참조 요청에 따라 실행 할 프로세스를 메모리에 적재하는 기존의 방법</li>
<li>예상 적재: 지역성을 이용해서 시스템의 요청을 미리 예측하여 메모리에 적재하는 방법</li>
</ol>
<h4 id="메모리-배치placement">메모리 배치(placement)</h4>
<ul>
<li>가져온 프로세스와 데이터를 메모리의 <strong>어떤 위치에 올려놓을지 결정하는 정책</strong></li>
</ul>
<h4 id="메모리-재배치replacement">메모리 재배치(replacement)</h4>
<ul>
<li>꽉 찬 메모리에 새로운 프로세스를 가져오기 위해 <strong>보류상태</strong>로 프로세스를 내보냄</li>
<li>그때 어떤 프로세스를 내보낼지 결정한다.</li>
</ul>
<h2 id="2-메모리-주소">2. 메모리 주소</h2>
<h3 id="2-1-물리-주소와-논리-주소">2-1 물리 주소와 논리 주소</h3>
<p>메모리의 주소를 표현할때 물리주소와 논리주소로 나눠서 구분한다.</p>
<h4 id="물리-주소-공간">물리 주소 공간</h4>
<ul>
<li>하드웨어 입장에서 바라본 주소 공간으로 컴퓨터마다 크기가 다르다.</li>
<li>말 그대로 컴퓨터에 꽂힌 램 메모리의 실제 주소이다.</li>
</ul>
<h4 id="논리-주소-공간">논리 주소 공간</h4>
<ul>
<li>사용자 입장에서 바라본 주소이다.</li>
<li>CPU에 의 생성됨</li>
<li>프로세스 입장에서 상대 주소가 사용할 수 없는 영역의 위치를 알 필요가 없고, 주소가
항상 0번지부터 시작하기 때문에 편리하다.</li>
</ul>
<p>사용자 입장에서 바라본 주소? 뭔가 쉽게 와닿는 표현은 아닌거 같다.
좀 더 자세하게 설명 해보겠다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/519db16e-5aa8-418a-aadc-96efc4413246/image.png" alt=""></p>
<p>실제 주소랑 별개로 프로세스 사용할 수 있는 공간부터 0번지로 판단하고 계산한다. 그렇다면 논리주소를 어떻게 실제 물리주소로 변환 하는 걸까?</p>
<h3 id="2-2-논리주소를-물리주소로-변환하는-과정">2-2 논리주소를 물리주소로 변환하는 과정</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/de40ccd9-c6c6-417f-b088-53d24a498349/image.png" alt=""></p>
<ol>
<li><p><strong>프로그램 P1 실행 시작</strong></p>
<ul>
<li>사용자 프로그램 P1은 자신이 사용할 메모리가 <strong>0번지부터 시작한다고 가정</strong>하고 작성됨</li>
<li>예: <code>P1 → 40번지 접근 요청</code></li>
</ul>
</li>
<li><p><strong>CPU는 이 요청을 &#39;논리 주소 40&#39;으로 처리</strong></p>
<ul>
<li>실제로는 물리 메모리 위치를 알지 못함</li>
<li>CPU → <code>논리 주소 40</code>을 MMU로 전달</li>
</ul>
</li>
<li><p><strong>메모리 관리자는 &#39;재배치 레지스터&#39;를 확인</strong></p>
<ul>
<li>운영체제는 프로그램 P1을 <strong>360번지에 적재했기 때문에</strong>,  
재배치 레지스터에는 <code>360</code>이 설정되어 있음</li>
</ul>
</li>
<li><p><strong>주소 변환 수행</strong></p>
<ul>
<li>물리 주소 = 재배치 레지스터 값 + 논리 주소  </li>
<li><code>360 + 40 = 400</code></li>
<li>메모리 관리자 → 물리 주소 <code>400</code>으로 변환</li>
</ul>
</li>
<li><p><strong>메모리 접근</strong></p>
<ul>
<li>CPU는 최종적으로 <code>물리 주소 400</code>의 메모리 공간에 접근함</li>
<li>이 위치는 P1에게는 논리 주소 <code>40</code>처럼 보이지만,<br>실제로는 운영체제에 의해 보호된 안전한 공간</li>
</ul>
</li>
</ol>
<hr>
<h4 id="왜-이러한-방식을-사용할까">왜 이러한 방식을 사용할까?</h4>
<ul>
<li><strong>보안</strong>: 각 프로그램은 자신만의 논리 주소 공간을 가지고, 서로의 메모리 침범을 막음</li>
<li><strong>유연성</strong>: 프로그램을 메모리 어디에 적재하든 논리 주소는 그대로 사용 가능</li>
<li><strong>프로세스 격리</strong>: 서로 다른 프로그램이 같은 논리 주소를 사용해도, 실제 물리 주소는 다르기 때문에 충돌 없음</li>
</ul>
<hr>
<h4 id="재배치-레지스터는-어떻게-설정되나">재배치 레지스터는 어떻게 설정되나?</h4>
<ul>
<li>운영체제가 새로운 프로세스를 메모리에 적재할 때:<ol>
<li><strong>적절한 메모리 공간 확보 (예: 360~999)</strong></li>
<li>해당 시작 주소(예: 360)를 재배치 레지스터에 기록</li>
<li>이후 해당 프로세스는 <strong>0부터 시작하는 것처럼</strong> 실행됨</li>
</ol>
</li>
</ul>
<hr>
<h4 id="용어-한번-더-정리">용어 한번 더 정리</h4>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>논리 주소(Logical Address)</strong></td>
<td>프로그램(P1)이 CPU에 요청하는 주소 (ex. 40)</td>
</tr>
<tr>
<td><strong>물리 주소(Physical Address)</strong></td>
<td>실제 메모리 상의 주소 (ex. 400)</td>
</tr>
<tr>
<td><strong>재배치 레지스터(Relocation Register)</strong></td>
<td>논리 주소에 더해질 시작 주소가 기록 된 곳 (ex. 360)</td>
</tr>
<tr>
<td><strong>메모리 관리자(Memory Management Unit)</strong></td>
<td>주소 변환을 수행하는 하드웨어/소프트웨어 컴포넌트</td>
</tr>
</tbody></table>
<hr>
<h3 id="2-3-주소-바인딩">2-3 주소 바인딩</h3>
<p>프로세스는 메모리에 직접 접근하지 않는다. 프로세스는 논리주소를 CPU에 전달하면, 논리 주소를 MMU(메모리 관리 장치)를 통해 변환되어서 물리주소로 변경하고, 그 물리 주소를 통해 실제 메모리에 접근을 한다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/900f9281-6ac2-414b-8cd8-2b9b498ee9d0/image.png" alt=""></p>
<h4 id="mmu의-다양한-관리-기법">MMU의 다양한 관리 기법</h4>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>고정 분할</td>
<td>메모리를 일정 크기로 나누고 프로그램을 넣는 방식</td>
</tr>
<tr>
<td>동적 분할</td>
<td>프로그램 크기에 맞게 메모리 공간을 동적으로 배정</td>
</tr>
<tr>
<td>페이징(Paging)</td>
<td>고정된 크기의 페이지 단위로 메모리를 나눠 관리</td>
</tr>
<tr>
<td>세그먼테이션</td>
<td>논리적 의미를 갖는 단위(코드/데이터/스택 등)로 분할</td>
</tr>
<tr>
<td>페이지화된 세그먼테이션</td>
<td>위 두 기법의 결합 (실무에서 가장 많이 쓰임)</td>
</tr>
</tbody></table>
<h4 id="그렇다면-바인딩이란">그렇다면 바인딩이란?</h4>
<ul>
<li><strong>바인딩(binding)</strong>: 논리 주소와 물리 주소를 매핑시키는 작업</li>
<li><strong>매핑(mapping)</strong>: CPU의 논리 주소를 실제 메모리의 물리 주소로 연결하는 것</li>
</ul>
<blockquote>
<p>※ 바인딩은 본래 &quot;주소를 연결하는 행위&quot;이지만, 운영체제에서는 이 연결을 &quot;언제 수행하는지&quot;에 따라 분류하는 것이 핵심입니다. 그렇기 때문에 바인딩과 매핑이 용어가 비슷해서 헷갈릴 수 있으나, 바인딩은 &quot;언제 주소를 결정할지&quot;, 매핑은 &quot;그 주소를 실제로 어떻게 바꿀지&quot; 이렇게 생각하는게 편하다고 합니다.</p>
</blockquote>
<p>바인딩은 총 3가지 시점 중 하나에서 발생할 수 있음:</p>
<h3 id="2-4-바인딩-시점">2-4 바인딩 시점</h3>
<p>프로그램이 실행되기 위해서는 작성된 코드가 메모리에 올라가야 하며, 이 과정에서 논리 주소를 물리 주소에 연결하는 작업이 필요합니다. 이 연결 작업을 &quot;주소 바인딩(Address Binding)&quot;이라고 하며, <strong>그 바인딩이 언제 수행되느냐</strong>에 따라 크게 세 가지 방식으로 나뉩니다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b32af607-1295-439f-af91-fa0e31fda53c/image.png" alt=""></p>
<h4 id="컴파일-타임-바인딩-compile-time-binding">컴파일 타임 바인딩 (Compile Time Binding)</h4>
<p>컴파일 시간 바인딩은 <strong>컴파일 시점에 프로그램의 메모리 적재 위치가 결정</strong>되는 방식입니다.<br>즉, 프로그램이 실행되기도 전에 <strong>어느 메모리 주소에 로드될지 컴파일러가 이미 알고 있어야</strong> 합니다.</p>
<p>이 방식은 논리 주소와 물리 주소가 사실상 <strong>동일하게 처리</strong>되며, 별도의 주소 변환 장치(MMU)는 필요하지 않습니다. 하지만 위치가 고정되기 때문에 <strong>프로그램의 재배치가 불가능</strong>하고, 전체 프로그램을 메모리에 <strong>한 번에 로드</strong>해야 하기 때문에 <strong>유연성이 매우 떨어집니다</strong>.</p>
<p>이 방식은 일반적인 운영체제보다는 <strong>임베디드 시스템이나 마이크로컨트롤러</strong>처럼 메모리 구조가 단순하고 고정된 환경에 적합합니다.</p>
<h4 id="적재-시간-바인딩-load-time-binding">적재 시간 바인딩 (Load Time Binding)</h4>
<p>적재 시간 바인딩은 <strong>프로그램이 메모리에 로드되는 시점에</strong> 실제 물리 주소가 결정되는 방식입니다.  </p>
<p>컴파일러는 물리 주소를 모르기 때문에 <strong>상대 주소(relocatable address)</strong>를 생성하며, 실행 시 운영체제가 해당 프로그램을 메모리의 어느 위치에 올릴지를 결정하고, 그 시작 주소를 <strong>재배치 레지스터</strong>에 저장합니다.</p>
<p>이 방식은 실행 중에는 메모리 위치가 바뀌지 않지만, 로드 시점에는 <strong>다양한 위치에 프로그램을 재배치할 수 있어</strong> 컴파일 시간 바인딩보다는 유연합니다. 이때부터 MMU가 필요하게 됩니다.</p>
<hr>
<h3 id="실행-시간-바인딩-run-time-binding">실행 시간 바인딩 (Run Time Binding)</h3>
<p>실행 시간 바인딩은 프로그램이 실제로 <strong>CPU에 의해 실행되는 도중에</strong> &quot;주소 변환이 일어나는 방식입니다. 즉 <strong>명령어가 실행될 때마다</strong> 변환된다.</p>
<p>프로그램은 항상 상대 주소를 사용하고, CPU가 명령어를 실행할 때마다 MMU가 그 논리 주소를 물리 주소로 <strong>실시간 변환</strong>합니다.<br>이 방식은 <strong>현대의 대부분 운영체제에서 사용되는 방식</strong>으로, 메모리 보호, 프로세스 간 격리, 가상 메모리 등과 같은 고급 기능 구현에 필수적입니다.</p>
<p>또한 실행 중에도 메모리 위치를 자유롭게 바꿀 수 있어, <strong>프로세스 스케줄링, 페이징, 스와핑</strong> 등의 기법과 잘 어울립니다.</p>
<h4 id="시점에-따른-바인딩-방식-비교">시점에 따른 바인딩 방식 비교</h4>
<table>
<thead>
<tr>
<th>바인딩 시점</th>
<th>주소 결정 시점</th>
<th>위치 변경 가능성</th>
<th>MMU 필요 여부</th>
<th>유연성</th>
</tr>
</thead>
<tbody><tr>
<td>Compile Time</td>
<td>컴파일 시</td>
<td>❌</td>
<td>❌</td>
<td>낮음</td>
</tr>
<tr>
<td>Load Time</td>
<td>적재 시</td>
<td>❌</td>
<td>✅</td>
<td>중간</td>
</tr>
<tr>
<td>Run Time</td>
<td>실행 중</td>
<td>✅</td>
<td>✅</td>
<td>높음</td>
</tr>
</tbody></table>
<hr>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[놀라운 운영체제 #5 – 운영체제의 교착 상태]]></title>
            <link>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</link>
            <guid>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EA%B5%90%EC%B0%A9-%EC%83%81%ED%83%9C</guid>
            <pubDate>Wed, 23 Jul 2025 01:22:17 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>앞선 글에서 자원, 동기화, 병행성 이슈들을 다뤘다면<br>이번엔 시스템에서 피할 수 없는 문제, <strong>교착상태(Deadlock)</strong>에 대해 정리 해보려고 한다. 
운영체제와 데이터베이스 시스템에서 교착상태는 성능 저하, 서비스 장애의 주요 원인이 되므로<br>발생 원인부터, 진단, 예방법과 해결책까지 알아보자.</p>
<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#1-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C%EB%9E%80-deadlock-%EC%A0%95%EC%9D%98">1. 교착상태란? (Deadlock 정의)</a><ul>
<li><a href="#1-1-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C%EC%99%80-%EC%95%84%EC%82%ACstarvation-%EC%B0%A8%EC%9D%B4">1-1. 교착상태와 아사(starvation) 차이</a></li>
<li><a href="#1-2-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%EB%B0%9C%EC%83%9D-%EC%83%81%ED%99%A9%EC%98%88%EC%8B%9C">1-2. 교착상태 발생 상황/예시</a></li>
<li><a href="#1-3-%EC%9E%90%EC%9B%90-%ED%95%A0%EB%8B%B9-%EA%B7%B8%EB%9E%98%ED%94%84%EC%99%80-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C">1-3. 자원 할당 그래프와 교착상태</a></li>
</ul>
</li>
<li><a href="#2-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%EB%B0%9C%EC%83%9D%EC%9D%98-%EB%84%A4-%EA%B0%80%EC%A7%80-%ED%95%84%EC%9A%94%EC%A1%B0%EA%B1%B4">2. 교착상태 발생의 네 가지 필요조건</a></li>
<li><a href="#3-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95">3. 교착상태 해결 방법</a><ul>
<li><a href="#3-1-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%EC%98%88%EB%B0%A9-prevention">3-1. 교착상태 예방 (Prevention)</a></li>
<li><a href="#3-2-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%ED%9A%8C%ED%94%BC-avoidance">3-2. 교착상태 회피 (Avoidance)</a></li>
<li><a href="#3-3-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9C-%EA%B2%80%EC%B6%9C%ED%9A%8C%EB%B3%B5-detection--recovery">3-3. 교착상태 검출/회복 (Detection &amp; Recovery)</a></li>
</ul>
</li>
<li><a href="#reference">Reference</a></li>
</ul>
<hr>
<h2 id="1-교착상태란-deadlock-정의">1. 교착상태란? (Deadlock 정의)</h2>
<ul>
<li>두 개 이상의 프로세스가 <strong>서로가 점유한 자원을 기다리며, 아무도 작업을 못하는 상태</strong></li>
<li>OS, DB, 멀티스레드 환경, 네트워크, 철학자 문제 등 현실에서 다양하게 등장한다.</li>
</ul>
<h3 id="1-1-교착상태와-아사starvation-차이">1-1. 교착상태와 아사(starvation) 차이</h3>
<ul>
<li><strong>아사(starvation):</strong> 스케줄링 정책 등 운영체제의 문제로 <strong>특정 프로세스만 오랫동안 실행 기회를 못받는 현상</strong> (정책/알고리즘 결함)</li>
<li><strong>교착상태(deadlock):</strong> <strong>여러 프로세스가 서로 자원을 점유한 채, 자연적으로</strong> (알고리즘상 결함 없이도) <strong>아무도 전진 못하는 상태</strong>  </li>
<li>아사는 우선순위, Aging 등 정책으로 해결. 교착상태는 구조적 자원 경쟁에서 발생 → 더 근본적인 문제</li>
</ul>
<h3 id="1-3-자원-할당-그래프와-교착상태">1-3. 자원 할당 그래프와 교착상태</h3>
<p>자원 할당 그래프란 프로세스가 어떤 자원을 사용중이고, 어떤 자원을 기다리고 있는지를 방향성이 있는 그래프로 표현한 것이다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/3e5b6f78-0df6-45a5-aa17-b591db1a06f7/image.png" alt=""></p>
<ul>
<li>프로세스(원), 자원(사각형) 노드로,  </li>
<li><strong>요구/점유 상태</strong>를 간선으로 연결한 방향 그래프  </li>
<li><strong>사이클</strong>이 있으면 교착상태 가능성 있음</li>
<li>여러 자원, 동시 요청 구조는 &quot;원형&quot; 요구 패턴 → 교착상태 유발</li>
</ul>
<hr>
<h2 id="2-교착상태-발생의-네-가지-필요조건">2. 교착상태 발생의 네 가지 필요조건</h2>
<p>교착상태가 발생하려면 다음 네 가지 <strong>모든 조건</strong>이 동시에 충족되어야 한다. 단 한가지라도 만족하지 않으면 교착 상태가 발생하지 않는다.</p>
<ol>
<li><strong>상호배제(Mutual Exclusion):</strong>  <ul>
<li>한 번에 하나의 프로세스만 자원을 점유&#39;
(공유 X, 독점적 자원)
  -&gt; 배타적 자원 사용하면 교착 상태 발생</li>
</ul>
</li>
<li><strong>비선점(Non-preemptive):</strong>  <ul>
<li>점유한 자원은 스스로 반납할 때까지 빼앗을 수 없음 (강제 중단 불가)
  -&gt; 빼앗을 수 없으면 공유 안되니 교착 상태 발생</li>
</ul>
</li>
<li><strong>점유와 대기(Hold and Wait):</strong>  <ul>
<li>자원을 <strong>점유한 채</strong> 추가 자원 요청 가능 (A를 가진 상태로 B를 요청)
  -&gt; 자원을 점유하면서 다른 자원을 기다리면 교착 상태 발생</li>
</ul>
</li>
<li><strong>원형 대기(Circular Wait):</strong>  <ul>
<li>프로세스 간 자원 요구가 <strong>원형</strong>(고리) 구조를 이룸 (P1→P2→…→Pn→P1)
  -&gt; 프로세스들이 서로 양보하지 않아 교착 상태 발생</li>
</ul>
</li>
</ol>
<blockquote>
<p>이 중 <strong>하나라도 무력화</strong>하면 교착상태는 발생하지 않는다.</p>
</blockquote>
<p>그렇다면 위의 교착 상태의 필요조건을 좀 더 자세하게 보면</p>
<ul>
<li>상호 배제, 비선점 조건 : 자원이 어떤 특징을 가지는지를 나타낸다.</li>
<li>점유와 대기, 원형 대기 조건: 프로세스가 어떤 행위를 하고 있는지 나타낸다.</li>
</ul>
<p>즉 필요조건도 자원의 특징과 프로세스의 행위에 따라 나뉜다.</p>
<blockquote>
<p>원형 대기 조건은 나머지 세가지 조건이 만족했을 때의 결과로 나타나므로 엄밀히 말하면 상호 배제, 비선점 , 점유와 대기 조건이 만족하면 교착상태가 발생 할 수 있다.</p>
</blockquote>
<h3 id="2-1-교착-상태의-예시">2-1. 교착 상태의 예시</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d4a21bb9-e6b1-4958-889c-d6dc346cc38e/image.png" alt=""></p>
<p>위 그림처럼 양쪽에서 사람들이 한 명씩 강을 건너고 있다고 생각해보자.
조건에 맞게 서로 앞으로 걸어가다 서로 마주치면 누구도 앞으로 나아갈 수 없는 상황이 발생한다. 이 구조는 운영체제의 교착상태를 표현하는 것 과 같다.</p>
<hr>
<h2 id="3-교착상태-해결-방법">3. 교착상태 해결 방법</h2>
<p>실제 운영체제는 <strong>예방, 회피, 검출/회복</strong> 세 가지 접근법을 사용한다.</p>
<h3 id="3-1-교착상태-예방-prevention">3-1. 교착상태 예방 (Prevention)</h3>
<ol>
<li>상호 배제 예방</li>
</ol>
<ul>
<li>시스템 내에 있는 상호 배타적인 모든 자원, 즉 독점적으로 사용할 수 있는 자원을 없애 버리는 방법</li>
<li><blockquote>
<p>현실적으로 모든 자원을 공유할 수는 없으며 상호 배제를 적용하여 보호해야 하는 자원이 있기 때문에 상호 배제를 무력화하는 것은 사실상 어렵다.</p>
</blockquote>
</li>
</ul>
<ol start="2">
<li>비선점 예방</li>
</ol>
<ul>
<li>모든 자원을 빼앗을 수 있도록 만드는 방법 (선점형을 사용하는 방법)</li>
<li><blockquote>
<p>아사 현상이 발생할 수 있기 때문에 비선점 조건을 무력화하는 것은 사실상 어렵다.</p>
</blockquote>
</li>
</ul>
<ol start="3">
<li>점유와 대기 예방</li>
</ol>
<ul>
<li><p>프로세스가 자원을 점유한 상태에서 다른 자원을 기다리지 못하게 하는 방법(전부 할당 or 아예 할당하지 않는 방식을 적용)</p>
<p>-&gt; 자원이 아닌 프로세스의 자원 사용 방식을 변화시켜 교착 상태를 처리한다는 점에서 의미가 있다. (누가 양보를 할지 결정해야하는 문제가 있긴 하지만..)</p>
</li>
<li><p>단점: 프로세스가 자신이 사용하는 모든 자원에 대해 미리 자세히 알기 어렵다. (입력에 따라 달라지는 프로세스일 경우는 더욱더)**</p>
</li>
</ul>
<blockquote>
<p>당장 사용하지 않을 자원도 미리 선점하기 때문에 자원의 활용성이 떨어진다.
많은 자원을 사용하는 프로세스는 적은 자원을 사용하는 프로세스보다 불리하다. (많은 자원을 다 확보해야 실행이 가능하므로 실행 기회가 적음 -&gt; 일괄 작업 방식으로 동작하게 됨)</p>
</blockquote>
<ol start="4">
<li>원형 대기 예방</li>
</ol>
<ul>
<li>모든 자원에 숫자를 부여하고 숫자가 큰 방향으로만 자원을 할당받을 수 있도록 하여 원형을 이루지 못하도록 막는 방법
-&gt; 프로세스 작업 진행에 대한 유연성이 떨어지며(왜 R2&gt; R1 ?), 자원의 번호를 어떻게 부여할 것인지가 문제이다.</li>
</ul>
<h3 id="3-2-교착상태-회피-avoidance">3-2. 교착상태 회피 (Avoidance)</h3>
<p>교착 상태의 모든 발생 가능성을 미리 제거하는 것이 아닌 교착 상태 발생 가능성을 인정하고(세 가지 필요조건 허용), 교착 상태가 발생하려고 할 때 적절히 회피하는 방법이다.</p>
<h4 id="자원의-할당-제한">자원의 할당 제한</h4>
<ul>
<li><p>프로세스에 자원을 할당할 때 교착 상태가 발생하지 않는 범위를 미리 파악하여 그 범위 내에서만 자원을 할당한다.</p>
</li>
<li><p>교착 상태가 발생할 수 있는 수준 이상의 자원을 요청하면 프로세스를 대기 상태로 전환하여 교착 상태를 피한다.</p>
</li>
<li><p>즉, 자원의 할당량을 적절히 조절하여 안정 상태(safe state)를 유지하는 것이 핵심이다.</p>
</li>
<li><p>대표적인 예시: <strong>은행원 알고리즘(Banker&#39;s Algorithm)</strong></p>
</li>
</ul>
<blockquote>
<p>자원의 총수와 현재 할당된 자원의 수를 기준으로 시스템을 안정 상태와 불안정 상태로 나누고 시스템이 안정 상태를 유지하도록 자원을 할당한다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/dc756d6b-ed17-4f4d-a271-275b10f004f8/image.png" alt=""></p>
</blockquote>
<h4 id="은행원-알고리즘bankers-algorithm">은행원 알고리즘(Banker&#39;s Algorithm)</h4>
<ul>
<li><p><strong>은행이 대출을 허용하는 방식</strong>과 유사한 알고리즘이다.</p>
<ul>
<li>은행은 전체 자금 내에서 대출이 가능한 범위 내(안정 상태)에서는 대출을 허용하지만, 범위를 초과하면 거부하는 것과 유사하다.</li>
</ul>
</li>
<li><p>예시: 우동 10인분, 스파게티 20인분의 재료가 있을 때 10명 이상의 예약을 받으면 문제가 발생하므로 최대 10명까지만 예약을 받는 상황과 유사하다.</p>
</li>
</ul>
<h4 id="은행원-알고리즘의-사용-변수">은행원 알고리즘의 사용 변수</h4>
<ol>
<li><strong>전체 자원(Total)</strong>: 시스템 내의 모든 자원 수</li>
<li><strong>가용 자원(Available)</strong>: 현재 사용할 수 있는 자원의 수 (전체 자원 - 현재 할당된 모든 자원)</li>
<li><strong>최대 자원(Max)</strong>: 각 프로세스가 선언한 최대 자원 요청량</li>
<li><strong>할당 자원(Allocation)</strong>: 각 프로세스에 현재 할당된 자원 수</li>
<li><strong>기대 자원(Expect)</strong>: 각 프로세스가 앞으로 추가로 사용할 자원 수 (최대 자원 - 할당 자원)</li>
</ol>
<h4 id="은행원-알고리즘의-자원-할당-기준">은행원 알고리즘의 자원 할당 기준</h4>
<ul>
<li>각 프로세스의 기대 자원과 가용 자원을 비교하여 가용 자원이 기대 자원보다 크거나 같으면 자원을 할당한다.</li>
<li>기대 자원을 충족하지 못하는 경우 자원을 할당하지 않고 프로세스를 대기시킨다.</li>
</ul>
<h4 id="은행원-알고리즘의-안정-상태-조건">은행원 알고리즘의 안정 상태 조건</h4>
<ul>
<li>시스템의 가용 자원이 프로세스의 기대 자원보다 크거나 같은 상황이 하나라도 존재하면 안정 상태라고 본다.</li>
<li>불안정 상태라고 해서 반드시 교착 상태가 발생하는 것은 아니지만, 교착 상태 발생 가능성이 높아진다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b547249b-ee6f-42e5-9be2-2a5c156afd59/image.png" alt=""></p>
<h4 id="교착-상태-회피의-문제점">교착 상태 회피의 문제점</h4>
<ul>
<li>프로세스가 미리 사용할 모든 자원을 정확히 선언해야 한다.</li>
<li>전체 자원의 수가 고정적이어야 하며, 자원 활용률이 낮아진다.</li>
<li>실시간이나 동적 시스템에 적용하기 어렵다.</li>
</ul>
<hr>
<h3 id="3-3-교착상태-검출회복-detection--recovery">3-3. 교착상태 검출/회복 (Detection &amp; Recovery)</h3>
<ul>
<li>교착 상태가 발생할 가능성을 허용하고, 실제 교착 상태 발생 시 탐지 및 복구한다.</li>
</ul>
<h4 id="교착-상태-검출">교착 상태 검출</h4>
<ul>
<li><p>OS가 프로세스의 작업을 지속적으로 관찰하여 교착 상태 여부를 확인한다.</p>
</li>
<li><p>자원 할당 그래프를 모니터링하여 사이클이 발견되면 교착 상태로 판단한다.</p>
</li>
<li><p><strong>타임아웃(Time-out)</strong>을 통한 검출 방법도 있다.</p>
<ul>
<li>일정 시간 동안 진행되지 않은 프로세스를 교착 상태로 간주하여 종료한다.</li>
<li>구현이 간단하고 자주 발생하지 않을 상황에 적합하다.</li>
</ul>
</li>
</ul>
<h4 id="데이터베이스에서의-타임아웃-문제">데이터베이스에서의 타임아웃 문제</h4>
<ul>
<li><p>데이터베이스의 트랜잭션이 타임아웃으로 종료되면 데이터 일관성이 깨질 수 있다.</p>
</li>
<li><p>이를 방지하기 위해 <strong>체크포인트(checkpoint)</strong>와 <strong>롤백(rollback)</strong>을 사용한다.</p>
</li>
<li><p><strong>해당 부분에 관해서는 별도로 데이터베이스의 트랜잭션 파트에 필자의 벨로그에 정리해놨다.</strong></p>
<ul>
<li><strong>체크포인트</strong>: 문제가 발생하면 이전 정상 상태로 돌아가기 위한 저장점</li>
<li><strong>롤백</strong>: 문제가 발생했을 때 체크포인트로 되돌아가는 과정</li>
</ul>
</li>
</ul>
<h4 id="교착-상태-회복">교착 상태 회복</h4>
<ul>
<li>교착 상태 검출 후, 이를 해결하기 위한 작업이다.</li>
<li>주로 교착 상태를 유발한 프로세스를 <strong>강제로 종료</strong>시킨다.</li>
</ul>
<h4 id="종료-방법">종료 방법</h4>
<ol>
<li><p>모든 프로세스를 동시에 종료</p>
</li>
<li><p>특정 프로세스를 선택하여 순차적으로 종료</p>
<ul>
<li>우선순위가 낮은 프로세스를 먼저 종료</li>
<li>우선순위가 같으면 작업 시간이 짧은 프로세스를 먼저 종료</li>
<li>위 조건이 같다면 자원을 많이 사용하는 프로세스를 먼저 종료</li>
</ul>
</li>
</ol>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[놀라운 운영체제 #4 – 프로세스 동기화]]></title>
            <link>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94</guid>
            <pubDate>Tue, 22 Jul 2025 11:48:07 GMT</pubDate>
            <description><![CDATA[<p>앞선 글에서 CPU 스케줄링과 프로세스의 실행 흐름을 정리했다.
이번엔 여러 프로세스 혹은 스레드가 동시에 실행되는 환경에선, &quot;공유 자원을 어떻게 안전하게 나눠 쓰게 할지?&quot; 이러한 병행성 문제와 동기화가 반드시 따라온다. 안전하게 공유자원에 접근하기 위해 이번 글에서는 동기화와 병행성 처리에 관해 다루고자 한다.</p>
<p>그전에 실제 OS에서 프로세스/스레드의 동작 흐름을 이해하려면,
동기·비동기, 블로킹·논블로킹이라는 실행 제어의 큰 맥락을 다뤄보겠다.</p>
<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EC%84%9C%EB%A1%A0">서론</a></li>
<li><a href="#%EB%8F%99%EA%B8%B0synchronous-vs-%EB%B9%84%EB%8F%99%EA%B8%B0asynchronous">동기(Synchronous) vs 비동기(Asynchronous)</a></li>
<li><a href="#%EB%B8%94%EB%A1%9C%ED%82%B9blocking-vs-%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9non-blocking">블로킹(Blocking) vs 논블로킹(Non-Blocking)</a></li>
<li><a href="#%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0--%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EC%A1%B0%ED%95%A9">동기/비동기 &amp; 블로킹/논블로킹 조합</a></li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94%EC%99%80-%EA%B2%BD%EC%9F%81-%EC%83%81%ED%83%9C">프로세스 동기화와 경쟁 상태</a><ul>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94%EB%9E%80">프로세스 동기화란?</a></li>
<li><a href="#%EA%B2%BD%EC%9F%81-%EC%83%81%ED%83%9Crace-condition%EC%99%80-%EC%9E%84%EA%B3%84-%EA%B5%AC%EC%97%ADcritical-section">경쟁 상태(Race Condition)와 임계 구역(Critical Section)</a></li>
<li><a href="#%EC%98%88%EC%8B%9C-%EA%B2%BD%EC%9F%81-%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C">예시: 경쟁 상태 (코드)</a></li>
</ul>
</li>
<li><a href="#%EB%8F%99%EA%B8%B0%ED%99%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%A2%85%EB%A5%98-lock-mutex-semaphore-monitor">동기화 객체의 종류 (Lock, Mutex, Semaphore, Monitor)</a><ul>
<li><a href="#%EB%8C%80%EA%B8%B0-%EB%B0%A9%EC%8B%9D">대기 방식</a></li>
<li><a href="#2%EF%B8%8F%E2%83%A3-%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4%EC%84%9Cmaphore">세마포어(Semaphore)</a></li>
<li><a href="#3%EF%B8%8F%E2%83%A3-%EB%AE%A4%ED%85%8D%EC%8A%A4mutex">뮤텍스(Mutex)</a></li>
<li><a href="#%E2%9A%96%EF%B8%8F-%EB%AE%A4%ED%85%8D%EC%8A%A4-vs-%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4-%EB%B9%84%EA%B5%90%ED%91%9C">뮤텍스 vs 세마포어 비교표</a></li>
<li><a href="#%EB%AA%A8%EB%8B%88%ED%84%B0monitor">모니터(Monitor)</a></li>
</ul>
</li>
<li><a href="#reference">Reference</a></li>
</ul>
<hr>
<h2 id="동기synchronous-vs-비동기asynchronous">동기(Synchronous) vs 비동기(Asynchronous)</h2>
<blockquote>
<p><strong>동기</strong>: 요청과 결과가 같은 자리에서 동시에 일어남 (작업 완료까지 대기)</p>
<p><strong>비동기</strong>: 요청 후 결과를 기다리지 않고 바로 다음 작업 수행 (대기 시간 중 다른 일 가능)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/c4c1ecda-fa03-47c2-9c54-f8a082c5f797/image.png" alt=""></p>
<ul>
<li><strong>동기</strong><ul>
<li>간단, 직관적</li>
<li><strong>결과를 받을 때까지 다른 작업 X</strong></li>
<li>(작업 완료 여부를 호출한 쪽에서 신경 씀)</li>
<li>작업을 순서대로 처리함</li>
</ul>
</li>
<li><strong>비동기</strong><ul>
<li>구조 복잡, 자원 효율적 활용</li>
<li><strong>결과 기다리는 동안 다른 일 가능</strong></li>
<li>(작업 완료 여부를 호출된 쪽에서 신경 씀)</li>
<li>작업을 순서대로 처리하지 않음</li>
</ul>
</li>
</ul>
<hr>
<h2 id="블로킹blocking-vs-논블로킹non-blocking">블로킹(Blocking) vs 논블로킹(Non-Blocking)</h2>
<blockquote>
<p><strong>블로킹</strong>: 함수가 제어권을 호출된 함수에게 넘기고, 결과가 나올 때까지 대기<br><strong>논블로킹</strong>: 함수가 제어권을 계속 가지고 있음. 호출된 함수는 백그라운드에서 실행됨</p>
</blockquote>
<ul>
<li><strong>블로킹</strong>: 제어권을 잃고, 함수 작업이 끝날 때까지 대기
<img src="https://velog.velcdn.com/images/losiento_nana/post/75909f4a-c7de-44e2-a775-231aff37c5af/image.png" alt=""></li>
</ul>
<p>다른 요청의 작업을 처리하기 위해 현재 작업을 Block(차단,대기)한다.</p>
<ul>
<li><strong>논블로킹</strong>: 호출 후 바로 자기 일을 계속 진행, 결과가 준비되면 통보받음
<img src="https://velog.velcdn.com/images/losiento_nana/post/8480b1c6-7dd4-46d1-b97c-2a9d76acff6e/image.png" alt=""></li>
</ul>
<p>다른 요청의 작업을 처리하기 위해 현재 작업을 Block(차단,대기)하지 않는다.</p>
<hr>
<h2 id="그렇다면-동기비동기-와-블로킹논블로킹이-같은-개념-아니야">그렇다면 동기/비동기 와 블로킹/논블로킹이 같은 개념 아니야?</h2>
<p>아니다. 동기/비동기 와 블로킹/논블로킹 이 두 개념은 표현 형태는 비슷해 보일지라도, 서로 다른 차원에서 작업의 수행 방식을 설명하는 개념이다. 동기/비동기는 요청한 작업에 대해 완료 여부를 신경 써서 <strong>작업을 순차적으로 수행할지 아닌지에 대한 관점</strong>이고,블로킹/논블록킹은 단어 그대로 <strong>현재 작업이 block(차단, 대기) 되느냐 아니냐</strong>에 따라 다른 작업을 수행할 수 있는지에 대한 관점이다.</p>
<p>즉 한번 더 정리 하자면</p>
<ol>
<li>동기/비동기</li>
</ol>
<ul>
<li>작업 완료를 누가 책임지고, 언제 다음 작업을 하느냐? 에 대한 관점</li>
<li>내가 결과를 직접 챙기며 순차적으로 한다. -&gt; 동기</li>
<li>결과가 나오는건 신경스지 않고, 완료 됐다고 알려주기 -&gt; 비동기</li>
</ul>
<ol start="2">
<li>블로킹/논블로킹</li>
</ol>
<ul>
<li>기다리는 동안 호출자의 작업이 멈추냐, 계속되냐에 대한 관점</li>
<li>함수가 Block(차단/대기) 상태가 되느냐</li>
<li>아니면 기다리는 동안 계속 다른 일(논 블로킹)을 할 수 있냐의 차이</li>
</ul>
<p>두 개념은 <strong>완전히 독립적</strong>이진 않다.
동기/비동기가 더 상위(큰 관점)의 개념으로 이해하면 좋다.</p>
<blockquote>
<p>동기/비동기가 더 상위(큰 관점) 개념으로 이해하면 좋다.
“작업을 언제/누가 끝내냐” → 동기/비동기
“기다릴 때 딴짓 가능하냐” → 블로킹/논블로킹</p>
</blockquote>
<hr>
<h2 id="동기비동기--블로킹논블로킹-조합">동기/비동기 + 블로킹/논블로킹 조합</h2>
<p>위에서 동기와 비동기, 블로킹과 논블로킹의 차이에 대해 알아봤다. 프로그램 아키텍처에서는 이 두개념을 함께 조합해서 4가지 조합을 만들어서 실제로 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/4ef07e17-8e6e-4ff1-91f2-64eb6f8b612d/image.png" alt=""></p>
<ol>
<li>Sync Blocking (동기 + 블로킹)</li>
<li>Async Blocking (비동기 + 블로킹)</li>
<li>Sync Non-Blocking (동기 + 논블로킹) </li>
<li>Async Non-Blocking (비동기 + 논블로킹)</li>
</ol>
<h3 id="동기--블로킹-synchronous--blocking"><strong>동기 + 블로킹 (Synchronous + Blocking)</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/921277cf-096a-4f3d-82aa-00d46e3e693f/image.png" alt=""></p>
<h4 id="개념">개념</h4>
<ul>
<li><strong>동기(Synchronous):</strong> 함수를 호출한 후, <strong>결과가 나올 때까지 다음 코드로 진행하지 않음</strong>.</li>
<li><strong>블로킹(Blocking):</strong> 호출된 함수가 작업을 완료할 때까지 <strong>현재 스레드는 아무것도 못하고 멈춰 있음</strong>.</li>
</ul>
<h4 id="특징">특징</h4>
<ul>
<li>가장 단순하고 직관적이며, 예측 가능한 동작을 함.</li>
<li>느린 작업(네트워크 요청, 파일 I/O 등)을 수행하면 <strong>프로그램 전체가 멈추는 것처럼 보임</strong>.</li>
<li>CPU 자원을 낭비할 수 있고, 프로그램의 반응성이 저하될 수 있음.</li>
</ul>
<h4 id="예시">예시</h4>
<pre><code class="language-java">
import java.util.Scanner;

public class SyncBlockingExample {
    public static void main(String[] args) {
        System.out.println(&quot;아무 숫자나 입력하세요: &quot;);
        Scanner scanner = new Scanner(System.in);

        // (1) read()는 동기적/블로킹 방식
        int n = scanner.nextInt(); // 사용자가 입력할 때까지 프로그램(메인스레드) 멈춤

        System.out.println(&quot;입력한 숫자: &quot; + n);
        System.out.println(&quot;다음 작업 실행!&quot;);
    }
}</code></pre>
<hr>
<h3 id="비동기--논블로킹-asynchronous--non-blocking"><strong>비동기 + 논블로킹 (Asynchronous + Non-Blocking)</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/68ce04f5-9b31-4dba-98b3-65bf08f93331/image.png" alt=""></p>
<h4 id="개념-1">개념</h4>
<ul>
<li><strong>요청을 맡긴 후, 바로 다음 작업을 진행</strong>(논블로킹)</li>
<li>요청한 작업이 끝나면 <strong>콜백/이벤트</strong>로 결과를 알려준다(비동기)</li>
</ul>
<h4 id="특징-1">특징</h4>
<ul>
<li>여러 작업이 동시에 비동기로 동작. <strong>작업의 순서가 보장되지 않음</strong></li>
<li>긴 작업 중에도 <strong>다른 코드가 실행됨 → 반응성/효율성 UP</strong></li>
<li>대용량 데이터/네트워크 서비스, 서버 등에서 많이 활용</li>
</ul>
<h4 id="예시-1">예시</h4>
<pre><code class="language-javaScript">const fs = require(&#39;fs&#39;);

fs.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;, (err, data) =&gt; {
  if (err) throw err;
  console.log(&#39;file1:&#39;, data);
});

fs.readFile(&#39;file2.txt&#39;, &#39;utf8&#39;, (err, data) =&gt; {
  if (err) throw err;
  console.log(&#39;file2:&#39;, data);
});

console.log(&#39;done&#39;); // 가장 먼저 출력됨

// 콜백 대신 Promise/async-await도 가능
Promise.all([
  fs.promises.readFile(&#39;file1.txt&#39;, &#39;utf8&#39;),
  fs.promises.readFile(&#39;file2.txt&#39;, &#39;utf8&#39;)
]).then(([data1, data2]) =&gt; {
  console.log(&#39;all done:&#39;, data1, data2);
});</code></pre>
<h4 id="실제-활용-예시-프로그램">실제 활용 예시 프로그램</h4>
<ul>
<li>웹 브라우저의 파일 다운로드<ul>
<li>여러 파일을 동시에 다운로드 하면서 사용자는 자유롭게 다른 작업을 할 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h3 id="동기--논블로킹-synchronous--non-blocking"><strong>동기 + 논블로킹 (Synchronous + Non-Blocking)</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/89c5d3cd-4af6-43db-8693-dd138ee848fa/image.png" alt=""></p>
<h4 id="개념-2">개념</h4>
<ul>
<li>함수를 호출하면, 제어권은 계속 자신이 가지고 있음(논블로킹)</li>
<li>하지만 <strong>작업이 끝났는지 계속 직접 확인(폴링)</strong>함(동기)</li>
<li>즉, 결과가 준비될 때까지 프로그램이 반복적으로 체크함.</li>
</ul>
<h4 id="특징-2">특징</h4>
<ul>
<li>프로그램이 멈추지 않고 계속 진행하지만, 중간중간 직접 &quot;끝났는지&quot; 검사</li>
<li>결과가 필요할 때마다 계속 &quot;상태 체크&quot; -&gt; CPU 낭비 발생</li>
<li>주로 I/O 장치 polling, 타이머 체크 등에 사용된다.</li>
</ul>
<h4 id="예시-2">예시</h4>
<pre><code class="language-javaScript">let isDone = false;
let result = null;

// (비동기 작업 시작)
setTimeout(() =&gt; {
  result = &#39;완료!&#39;;
  isDone = true;
}, 2000);

// 폴링(동기적 논블로킹)으로 결과 체크
const interval = setInterval(() =&gt; {
  if (isDone) {
    console.log(result); // &quot;완료!&quot;
    clearInterval(interval);
  } else {
    console.log(&#39;아직 처리중...&#39;); // 2초 동안 계속 출력
  }
}, 300);

</code></pre>
<h3 id="비동기--블로킹-asynchronous--blocking"><strong>비동기 + 블로킹 (Asynchronous + Blocking)</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d07705ee-acd3-4f47-bb4b-61acd3f106bb/image.png" alt=""></p>
<h4 id="개념-3">개념</h4>
<ul>
<li>비동기로 요청(결과 순서 X)</li>
<li>하지만 작업이 끝날 때까지 그 함수는 블로킹됨(대기)</li>
<li>호출한 쪽이 대기해야 결과를 받을 수 있음</li>
</ul>
<h4 id="특징-3">특징</h4>
<ul>
<li>요청 자체는 비동기이나, 결과를 받을때까지 CPU/쓰레드가 대기</li>
<li>한 번에 한 작업만 처리 → 효율 떨어짐</li>
<li>실무에서는 잘 쓰이지 않음(비효율적 구조) -&gt; 안티패턴</li>
<li>일부 옛날 라이브러리, IO/DB 드라이버 등에서 볼 수 있음</li>
</ul>
<h4 id="예시-3">예시</h4>
<pre><code class="language-javaScript">
// async/await로 블로킹 효과 연출
function delay(ms) {
  return new Promise(resolve =&gt; setTimeout(resolve, ms));
}

async function main() {
  console.log(&#39;시작&#39;);
  await delay(2000); // 2초 동안 대기(블로킹)
  console.log(&#39;2초 후 실행&#39;);
  await delay(1000); // 또 대기
  console.log(&#39;1초 후 실행&#39;);
}

main();
</code></pre>
<hr>
<h3 id="동기비동기--블로킹논블로킹-조합-1">동기/비동기 &amp; 블로킹/논블로킹 조합</h3>
<table>
<thead>
<tr>
<th></th>
<th>Blocking (제어권 X)</th>
<th>Non-Blocking (제어권 O)</th>
</tr>
</thead>
<tbody><tr>
<td>Sync</td>
<td>작업 순서/결과 대기, 제어권 X (ex. <code>Scanner.nextInt()</code>)</td>
<td>폴링/반복확인 (중간중간 결과 체크)</td>
</tr>
<tr>
<td>Async</td>
<td>작업 순서 자유, 제어권 X (잘 안씀/안티패턴)</td>
<td><strong>콜백/이벤트</strong>: 비동기 + 논블로킹 (ex. JS setTimeout)</td>
</tr>
</tbody></table>
<ul>
<li><strong>Sync-Blocking</strong>: 전통적인 입출력 방식(입력 기다릴 때까지 멈춤)</li>
<li><strong>Sync-NonBlocking</strong>: 폴링 방식(중간중간 결과 직접 체크)</li>
<li><strong>Async-NonBlocking</strong>: 대표적 비동기 콜백 패턴(주로 JS 등에서 활용)</li>
<li><strong>Async-Blocking</strong>: 실제로는 거의 사용되지 않는 비효율 조합</li>
</ul>
<hr>
<h2 id="프로세스-동기화와-경쟁-상태">프로세스 동기화와 경쟁 상태</h2>
<p>현대 운영체제에서 여러 프로세스(혹은 스레드)가 <strong>공유 자원</strong>(메모리, 변수, 파일 등)에 동시에 접근할 때, 동기화 문제가 필연적으로 발생합니다. 동기화가 제대로 이뤄지지 않으면 프로그램의 동작이 예측 불가해지고, 데이터 손상이나 치명적 버그로 이어질 수 있습니다.</p>
<h3 id="프로세스-동기화란">프로세스 동기화란?</h3>
<ul>
<li>여러 프로세스가 공유하는 자원의 일관성을 유지하는것.</li>
<li>여러 프로세스가 서로 협력해 공유자원을 사용하는 상황에서 경쟁조건이 발생하면 공유 자원의 신뢰성이 떨어진다. 이를 방지하기 위해 프로세스들이 공유자원을 사용할 때 특별한 규칙을 만드는 것이다.</li>
</ul>
<h3 id="경쟁-상태race-condition와-임계-구역cirtical-section">경쟁 상태(Race Condition)와 임계 구역(Cirtical Section)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b93b0432-fa16-4ba7-b5dd-c7e5b090e909/image.png" alt=""></p>
<h4 id="경쟁-상태race-condition">경쟁 상태(Race Condition)</h4>
<ul>
<li>여러 프로세스(스레드)가 <strong>동시에 공유 자원에 접근</strong>해서,<br>실행 순서에 따라 <strong>결과가 달라지는 예측 불가 상황</strong>  </li>
<li>예: 두 스레드가 같은 변수 값을 동시에 증가시키면 최종 결과는 실행 타이밍에 따라 달라질 수 있음</li>
</ul>
<h4 id="임계-구역critical-section">임계 구역(Critical Section):</h4>
<ul>
<li><strong>경쟁 상태</strong>가 발생할 수 있는, 공유 자원을 접근·변경하는 코드 구간</li>
<li>안전하게 동기화하려면 반드시 아래 <strong>3가지 조건</strong>을 만족해야 함<ol>
<li><strong>상호배제(Mutual Exclusion):</strong> 한 번에 하나만 진입</li>
<li><strong>진행의 융통성(Progress):</strong> 불필요한 대기 없음</li>
<li><strong>한정 대기(Bounded Waiting):</strong> 무한 대기 방지 (기아 X)</li>
</ol>
</li>
</ul>
<h4 id="예시-경쟁-상태-코드">예시: 경쟁 상태 (코드)</h4>
<pre><code class="language-java">public class Counter {
    private int count = 0;
    public void increment() { count++; }
    public int getCount() { return count; }
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println(&quot;Final count: &quot; + counter.getCount()); // 예측 불가!
    }
}</code></pre>
<h4 id="코드-설명">코드 설명</h4>
<ul>
<li><code>count</code> 변수는 <strong>여러 스레드가 공유하는 자원</strong>입니다.</li>
<li><code>t1</code>, <code>t2</code> 두 스레드가 <strong>동시에 1000번씩</strong> <code>counter.increment()</code>를 호출합니다.<ul>
<li>정상적으로 동작하면 <strong>최종 값은 2000</strong>이어야 합니다.</li>
</ul>
</li>
<li>하지만, <code>count++</code>는 <strong>3단계 연산(count 읽기 → 1 증가 → 저장)</strong>이기 때문에<br>여러 스레드가 동시에 접근하면 <strong>중간에 값이 꼬이는 Race Condition</strong>이 발생합니다.</li>
<li>실제로 실행하면 <strong>2000보다 작은 값(1900~1999 등)</strong>이 자주 나옵니다.</li>
<li>이런 예측 불가 현상을 막으려면 <strong>임계 구역에 동기화(뮤텍스, 락 등)</strong>를 반드시 적용해야 합니다.</li>
</ul>
<hr>
<h2 id="동기화-객체의-종류-lock-mutex-semaphore-monitor">동기화 객체의 종류 (Lock, Mutex, Semaphore, Monitor)</h2>
<p>객체마다 각각 다른 방식으로 <strong>동시 접근</strong>과 <strong>대기/진입</strong>을 관리한다.</p>
<h3 id="대기-방식">대기 방식</h3>
<h4 id="busy-waiting-스핀락">busy-waiting (스핀락)</h4>
<ul>
<li>자원이 풀릴 때까지 <strong>CPU 점유</strong>하며 계속 확인 (while, polling)  </li>
<li>효율↓, 실무에서는 block 방식 선호</li>
</ul>
<h4 id="block-wakeup">block-wakeup:</h4>
<ul>
<li>자원이 점유 중이면 <strong>대기(잠듦)</strong>  </li>
<li>자원이 해제되면 <strong>다른 스레드가 깨워줌</strong> (notify/wakeup)  </li>
<li>Java의 synchronized, wait/notify 등이 대표적</li>
</ul>
<hr>
<h3 id="2️⃣-세마포어semaphore">2️⃣ 세마포어(Semaphore)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d9894935-2154-4994-8c38-4c601972c4a6/image.png" alt=""></p>
<ul>
<li><strong>동시 접근 가능한 프로세스/스레드의 개수를 카운팅</strong></li>
<li>정수값으로 관리. P/V연산(혹은 wait/signal)로 진입/해제</li>
<li><strong>Binary(0/1) 세마포어:</strong> 뮤텍스와 동등하게 동작 (1명만 접근 가능)</li>
<li><strong>Counting 세마포어:</strong> N명까지 동시 접근 허용</li>
</ul>
<pre><code class="language-java">import java.util.concurrent.Semaphore;

public class Counter {
    private int count = 0;
    private final Semaphore semaphore = new Semaphore(1); // Binary Semaphore(뮤텍스 역할)

    public void increment() {
        try {
            semaphore.acquire();    // P연산: 세마포어 획득(임계구역 진입)
            count++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();    // V연산: 세마포어 해제(임계구역 종료)
        }
    }
    public int getCount() { return count; }
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println(&quot;Final count: &quot; + counter.getCount()); // 항상 2000
    }
}
</code></pre>
<ul>
<li>Semaphore(1) : 한 번에 한 스레드만 임계구역 진입 허용 (뮤텍스와 동일)</li>
<li>acquire()로 진입, release()로 해제 (자원 1개 → 임계구역 보장)</li>
<li>모든 스레드가 순차적으로 접근 → Race Condition 방지</li>
</ul>
<hr>
<h3 id="3️⃣-뮤텍스mutex">3️⃣ 뮤텍스(Mutex)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/9aa7e1e2-0116-4b83-b7fb-3d8b73a3dc26/image.png" alt=""></p>
<ul>
<li><strong>오직 1개의 스레드만 임계 구역에 진입할 수 있도록 보장</strong></li>
<li>소유권 명확(잠근 놈만 풀 수 있음)</li>
<li>Java의 ReentrantLock 등으로 구현</li>
</ul>
<pre><code class="language-java">import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();        // 락 획득(임계구역 진입)
        try {
            count++;
        } finally {
            lock.unlock();  // 락 해제(임계구역 종료)
        }
    }
    public int getCount() { return count; }
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println(&quot;Final count: &quot; + counter.getCount()); // 항상 2000
    }
}
</code></pre>
<ul>
<li>ReentrantLock(뮤텍스): 한 번에 한 스레드만 임계구역 실행</li>
<li>lock.lock()/unlock()으로 락 관리</li>
<li>반드시 finally에서 unlock() 호출 (예외로 인한 데드락 방지)</li>
<li>역시 Race Condition 방지</li>
</ul>
<hr>
<h4 id="⚖️-뮤텍스-vs-세마포어-비교표">⚖️ 뮤텍스 vs 세마포어 비교표</h4>
<table>
<thead>
<tr>
<th align="center">구분</th>
<th align="center"><strong>뮤텍스(Mutex)</strong></th>
<th align="center"><strong>세마포어(Semaphore)</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>주요 목적</strong></td>
<td align="center">상호 배제(1개만 진입 허용)</td>
<td align="center">동시 접근 가능 개수 제한</td>
</tr>
<tr>
<td align="center"><strong>값의 범위</strong></td>
<td align="center">locked/unlocked (0 또는 1)</td>
<td align="center">0~N(카운팅), 0/1(바이너리)</td>
</tr>
<tr>
<td align="center"><strong>소유권</strong></td>
<td align="center">락을 획득한 스레드만 해제 가능</td>
<td align="center">누구든 signal(V) 호출 가능</td>
</tr>
<tr>
<td align="center"><strong>예시/비유</strong></td>
<td align="center">화장실 잠금장치(내가 잠그고 내가 풀음)</td>
<td align="center">주차장 자리 카운트, 화장실 칸 표시</td>
</tr>
<tr>
<td align="center"><strong>대표 사용</strong></td>
<td align="center">공유 변수/임계구역 보호</td>
<td align="center">DB 연결 제한, 생산자-소비자 문제</td>
</tr>
</tbody></table>
<hr>
<h3 id="모니터monitor">모니터(Monitor)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/bf94fba4-0a44-4187-8f58-cf10dff75d85/image.png" alt=""></p>
<ul>
<li><strong>공유자원에 대한 접근을 자동화/은닉</strong></li>
<li>세마포어/락의 실수 방지 (예: 락을 두번 걸거나 해제 누락 등)</li>
<li>wait/signal 대신 <strong>모니터가 자원 접근과 순서 제어</strong>  </li>
<li>자바에서 모니터는 보통 <code>synchronized</code> 블록, 메서드 등으로 제공</li>
</ul>
<h4 id="예시-4">예시</h4>
<pre><code class="language-java">public class Counter {
    private int count = 0;

    // synchronized 메서드: 이 메서드 전체가 &quot;임계 구역&quot;
    public synchronized void increment() {
        count++;
    }
    public int getCount() { return count; }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -&gt; { for (int i = 0; i &lt; 1000; i++) counter.increment(); });
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println(&quot;Final count: &quot; + counter.getCount()); // 항상 2000
    }
}</code></pre>
<ul>
<li>synchronized 키워드가 붙은 메서드(혹은 블록)는 “동시에 한 스레드만” 진입 가능
  → Java 런타임이 내부적으로 “모니터 락”을 자동으로 관리</li>
<li>실수로 락 해제/중복, 예외 처리 등 신경 쓸 필요 없이, 코드 블록 단위로 임계구역을 보장</li>
<li>여러 스레드가 동시에 접근해도 경쟁 상태 없이 항상 일관된 결과(여기선 2000)</li>
</ul>
<hr>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
<li><a href="https://eunsolsblog.tistory.com/entry/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94">[운영체제] 프로세스 동기화</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC">완벽히 이해하는 동기/비동기 &amp; 블로킹/논블로킹</a></li>
<li><a href="https://heeonii.tistory.com/14">[운영체제] Mutex 뮤텍스와 Semaphore 세마포어의 차이
출처: https://heeonii.tistory.com/14 [우당탕탕 히온이네:티스토리]</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Do it! 알고리즘 코딩테스트 with JAVA #2 Data Structure]]></title>
            <link>https://velog.io/@losiento_nana/Do-it-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-with-JAVA-2-Data-Structure</link>
            <guid>https://velog.io/@losiento_nana/Do-it-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-with-JAVA-2-Data-Structure</guid>
            <pubDate>Tue, 22 Jul 2025 05:20:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>이 글은 인프런 [하루코딩]님의 무료 강의 [Do it! 알고리즘 코딩테스트 with JAVA]를 따라가며,<br>직접 문제를 풀고 느낀 점, 새롭게 배운 개념 및 깨달음을 정리하는 글입니다.<br>문제마다 풀이 아이디어와 내가 고민했던 과정과 코드를 집어 넣었습니다.
모든 문제는 저작권 문제로 링크 형식으로 제공합니다.</strong></p>
</blockquote>
<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#1-%EB%B0%B1%EC%A4%80-11659-%EA%B5%AC%EA%B0%84%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0">1. 백준 11659 구간합 구하기</a></li>
<li><a href="#2-%EB%B0%B1%EC%A4%80-2018---%ED%88%AC%ED%8F%AC%EC%9D%B8%ED%84%B0%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9-5">2. 백준 2018 - 투포인터(수들의 합 5)</a></li>
<li><a href="#3-%EB%B0%B1%EC%A4%80-1940---%ED%88%AC%ED%8F%AC%EC%9D%B8%ED%84%B0%EC%A3%BC%EB%AA%BD">3. 백준 1940 - 투포인터(주몽)</a></li>
<li><a href="#4-%EB%B0%B1%EC%A4%80-12891---%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0dna-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8">4. 백준 12891 - 슬라이딩 윈도우(DNA 비밀번호)</a></li>
<li><a href="#5-%EB%B0%B1%EC%A4%80-11286---%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%ED%81%90%EC%A0%88%EB%8C%93%EA%B0%92-%ED%9E%99">5. 백준 11286 - 우선순위 큐(절댓값 힙)</a></li>
</ul>
<hr>
<h2 id="1-백준-11659-구간합-구하기">1. 백준 11659 구간합 구하기</h2>
<h3 id="문제-링크">문제 링크</h3>
<ul>
<li><a href="https://www.acmicpc.net/problem/11659">https://www.acmicpc.net/problem/11659</a></li>
</ul>
<hr>
<h3 id="풀이">풀이</h3>
<pre><code class="language-java">public static void main(String[] args) throws IOException {

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

        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        int[] arr = new int[n + 1];
        int[] prefixSum = new int[n + 1];

        st = new StringTokenizer(br.readLine());
        for (int i = 1; i &lt;= n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
            prefixSum[i] = prefixSum[i - 1] + arr[i]; // 누적합
        }

        for (int i = 0; i &lt; m; i++) {
            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            System.out.println(prefixSum[end] - prefixSum[start - 1]);
        }
    }</code></pre>
<hr>
<h3 id="배운점--접근법">배운점 &amp; 접근법</h3>
<p>입력값과 배열 인덱스를 맞추기 위해 배열을 한 칸 더 크게 만들고, 0번 인덱스를 비워둔 채 1번부터 채워서 인덱스 혼동 없이 구현했다.</p>
<p>처음엔 단순 반복문으로 구간합을 직접 계산하다가 시간 초과가 발생해, 더 효율적인 방법을 고민함.</p>
<p>누적합(구간합) 배열을 사용하면, [start, end] 구간의 합을 prefixSum[end] - prefixSum[start - 1] 한 줄로 빠르게 구할 수 있다는 점화식적 사고로 접근했다.</p>
<p>이런 누적합 방식은 <strong>한 번의 전처리(O(N))만 하면, 이후 구간합 쿼리를 O(1)</strong>로 처리할 수 있어 효율적임을 알게 됐다.</p>
<hr>
<h2 id="2-백준-2018---투포인터수들의-합-5">2. 백준 2018 - 투포인터(수들의 합 5)</h2>
<h3 id="문제-링크-1">문제 링크</h3>
<ul>
<li><a href="https://www.acmicpc.net/problem/2018">https://www.acmicpc.net/problem/2018</a></li>
</ul>
<hr>
<h3 id="풀이-1">풀이</h3>
<pre><code class="language-java">public static void main(String[] args) throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            int N = Integer.parseInt(br.readLine());

            int start = 1, end = 1, sum = 1, count = 0;

            while (start &lt;= N) {
                if (sum &lt; N) {
                    end++;
                    sum += end;
                } else if (sum == N) {
                    count++;
                    sum -= start;
                    start++;
                } else {
                    sum -= start;
                    start++;
                }
            }

            System.out.println(count);
        }</code></pre>
<h3 id="배운점--접근법-1">배운점 &amp; 접근법</h3>
<ul>
<li><strong>투포인터</strong> 개념을 처음 명확하게 익힌 문제였다.</li>
<li>연속된 자연수의 합으로 N을 표현하는 경우의 수를 찾으려면,<br><strong>구간의 시작과 끝을 가리키는 두 포인터(start, end)를 활용</strong>해 연속된 범위의 합을 구하는 &quot;슬라이딩 윈도우&quot; 방식이 필요하다.</li>
<li>start와 end 포인터를 적절히 움직이면서 현재 구간의 합이 N보다 작으면 end를 늘리고, 크거나 같으면 start를 이동시키는 식으로 문제를 해결했다.</li>
<li>단순히 반복문으로 모든 경우를 확인하면 시간 초과가 나기 때문에, <strong>효율적인 투포인터 사용이 필수적</strong>이었다.</li>
</ul>
<hr>
<h2 id="3-백준-1940---투포인터주몽">3. 백준 1940 - 투포인터(주몽)</h2>
<h3 id="문제-링크-2">문제 링크</h3>
<ul>
<li><a href="https://www.acmicpc.net/problem/1940">https://www.acmicpc.net/problem/1940</a></li>
</ul>
<hr>
<h3 id="풀이-2">풀이</h3>
<pre><code class="language-java">public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        int m = Integer.parseInt(br.readLine());

        String[] s = br.readLine().split(&quot; &quot;);
        ArrayList&lt;Integer&gt; numList = new ArrayList&lt;&gt;();

        for (String t : s) {
            numList.add(Integer.parseInt(t));
        }

        Collections.sort(numList); // 오름차순 정렬

        int count = 0;
        int left = 0;
        int right = n - 1;

        while (left &lt; right) {
            int sum = numList.get(left) + numList.get(right);
            if (sum == m) {
                count++;
                left++;
                right--;
            } else if (sum &lt; m) {
                left++;
            } else {
                right--;
            }
        }

        System.out.println(count);
    }</code></pre>
<hr>
<h3 id="배운점--접근법-2">배운점 &amp; 접근법</h3>
<ul>
<li><strong>정렬 후, 투포인터</strong>를 활용해 문제를 해결해야 한다는 점이 포인트였다.</li>
<li>이 문제는 <strong>임의의 두 수의 합이 특정 값이 되는 쌍의 개수</strong>를 찾는 문제이기 때문에,<br><strong>배열을 정렬한 후, 양 끝(left, right)에서 접근</strong>하는 투포인터 방식이 적합하다.</li>
<li>처음에는 2018번에서 사용한 &quot;연속된 구간&quot; 투포인터 방식으로 접근했다가,<br>정답이 제대로 나오지 않아 자료구조/정렬 방식을 다시 점검했다.</li>
<li>이 과정에서 <strong>투포인터의 &quot;구간&quot;과 &quot;쌍&quot; 패턴은 적용 대상이 다르다</strong>는 걸 직접 체감했다.</li>
<li>Map/Set을 사용해 풀 수도 있지만, <strong>투포인터 연습을 위해서 정렬 + 투포인터</strong>가 더 직관적이고 효율적이었다.</li>
</ul>
<hr>
<h2 id="📝-투포인터의-유형별-시작-위치와-적용-방식">📝 &quot;투포인터&quot;의 유형별 시작 위치와 적용 방식</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/cbd1e637-1296-4a3e-99c8-b0c14e05a10b/image.png" alt=""></p>
<h3 id="1-구간연속된-범위형-연속-부분합구간-문제">1. <strong>구간(연속된 범위)형: &quot;연속 부분합/구간&quot; 문제</strong></h3>
<ul>
<li><strong>적용 대상</strong><ul>
<li><strong>연속된 원소들의 합, 곱, 길이</strong> 등 ‘구간’ 자체의 특징을 묻는 문제  </li>
<li>예시: [백준 2018번 수들의 합 5], [백준 12891 DNA 비밀번호], [부분합(연속된 최소/최대 구간)]</li>
</ul>
</li>
<li><strong>포인터 위치</strong><ul>
<li><strong>start와 end, 두 개의 포인터를 같은 쪽(처음)에서 출발</strong></li>
<li>한 쪽(보통 end) 또는 양쪽을 이동시키며 &quot;구간&quot;을 확장/축소함</li>
<li>구간의 합(혹은 다른 조건)이 &quot;목표&quot;에 미달하면 end++, 초과하면 start++<br>(조건 만족 시 count 증가 등)</li>
</ul>
</li>
<li><strong>패턴 예시</strong>  <ul>
<li><code>while (start &lt;= N) { if (sum &lt; 목표) { end++; sum += arr[end]; } ... }</code></li>
<li>즉, <strong>start와 end가 처음부터 출발, &quot;연속된 범위&quot;를 확장/축소</strong></li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-쌍조합형임의-두-원소-조합">2. <strong>쌍/조합형(임의 두 원소 조합)</strong></h3>
<ul>
<li><strong>적용 대상</strong><ul>
<li><strong>배열/리스트 내 임의의 두 수(쌍)의 합/곱/차 등</strong>을 만족하는 쌍의 개수, 조합</li>
<li>예시: [백준 1940번 주몽], [투포인터로 푸는 2sum 류], [두 배열 합이 특정값]</li>
</ul>
</li>
<li><strong>포인터 위치</strong><ul>
<li><strong>정렬 후</strong>, <strong>left=0(첫 원소), right=N-1(마지막 원소)에서 “양끝”에서 접근</strong></li>
<li>두 수의 합이 목표값보다 작으면 left++, 크면 right--  </li>
<li>쌍의 조건을 만족하면 count 증가 후 둘 다 이동(혹은 한쪽만 이동)</li>
</ul>
</li>
<li><strong>패턴 예시</strong>  <ul>
<li><code>while (left &lt; right) { int sum = arr[left] + arr[right]; ... }</code></li>
<li>즉, <strong>정렬된 배열의 &quot;양끝에서&quot; 출발해서 서로 가까워짐</strong></li>
</ul>
</li>
</ul>
<hr>
<h3 id="✔️-정리">✔️ <strong>정리</strong></h3>
<ul>
<li><strong>연속된 범위/구간의 조건</strong> → 투포인터(동일 방향, &quot;연속 범위&quot; 탐색)  <ul>
<li>슬라이딩 윈도우(Sliding Window)로도 불림. 투포인터의 대표적 응용!</li>
</ul>
</li>
<li><strong>배열에서 쌍/조합 조건</strong> → 투포인터(양끝에서, &quot;조합/합&quot; 탐색)</li>
</ul>
<blockquote>
<p><strong>문제에서 “연속된 구간/부분합”이냐, “임의의 쌍/조합”이냐</strong>를 먼저 구분하면<br>투포인터의 시작 위치와 이동 방식이 자연스럽게 결정된다!</p>
</blockquote>
<hr>
<h4 id="💡-참고-슬라이딩-윈도우란">💡 <strong>참고: 슬라이딩 윈도우란?</strong></h4>
<ul>
<li>슬라이딩 윈도우는 위에서 말한 &quot;연속 범위&quot;형 투포인터의 한 방식으로,<br>일정 크기의 구간(윈도우)을 한 칸씩 밀면서 빠르게 조건을 체크하는 패턴을 뜻합니다.</li>
<li>투포인터로 시작점/끝점을 관리해 구간 정보를 효율적으로 유지합니다.</li>
</ul>
<hr>
<h2 id="4-백준-12891---슬라이딩-윈도우dna-비밀번호">4. 백준 12891 - 슬라이딩 윈도우(DNA 비밀번호)</h2>
<h3 id="문제-링크-3">문제 링크</h3>
<ul>
<li><a href="https://www.acmicpc.net/problem/12891">https://www.acmicpc.net/problem/12891</a></li>
</ul>
<hr>
<h3 id="풀이-3">풀이</h3>
<pre><code class="language-java">
public static void main(String[] args) throws IOException {

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

        int length  = Integer.parseInt(st.nextToken());       // DNA 문자열 길이
        int answer_length  = Integer.parseInt(st.nextToken()); // 비밀번호(부분문자열) 길이

        String s = br.readLine();
        char[] charArray = s.toCharArray();

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

        // 최소 조건 map
        HashMap&lt;Character, Integer&gt; minMap = new HashMap&lt;&gt;();
        minMap.put(&#39;A&#39;,Integer.parseInt(st.nextToken()));
        minMap.put(&#39;C&#39;,Integer.parseInt(st.nextToken()));
        minMap.put(&#39;G&#39;,Integer.parseInt(st.nextToken()));
        minMap.put(&#39;T&#39;,Integer.parseInt(st.nextToken()));

        // 윈도우 내 현재 카운트 map
        HashMap&lt;Character, Integer&gt; windowMap = new HashMap&lt;&gt;();
        windowMap.put(&#39;A&#39;, 0);
        windowMap.put(&#39;C&#39;, 0);
        windowMap.put(&#39;G&#39;, 0);
        windowMap.put(&#39;T&#39;, 0);

        int result = 0;

        // 초기 윈도우 세팅
        for (int i = 0; i &lt; answer_length; i++) {
            char c = charArray[i];
            windowMap.put(c, windowMap.get(c) + 1);
        }
        if (isSatisfied(minMap, windowMap)) result++;

        // 슬라이딩 윈도우
        for (int i = answer_length; i &lt; length; i++) {
            // 윈도우 오른쪽 문자 추가
            char in = charArray[i];
            windowMap.put(in, windowMap.get(in) + 1);
            // 윈도우 왼쪽 문자 제거
            char out = charArray[i - answer_length];
            windowMap.put(out, windowMap.get(out) - 1);

            if (isSatisfied(minMap, windowMap)) result++;
        }

        System.out.println(result);
    }

    // 최소 조건을 만족하는지 체크하는 함수
    private static boolean isSatisfied(HashMap&lt;Character, Integer&gt; minMap, HashMap&lt;Character, Integer&gt; windowMap) {
        for (char c : minMap.keySet()) {
            if (windowMap.get(c) &lt; minMap.get(c)) return false;
        }
        return true;
    }</code></pre>
<hr>
<h3 id="배운점--접근법-3">배운점 &amp; 접근법</h3>
<ul>
<li><strong>처음에는</strong> 각 구간마다 등장하는 문자의 개수를 확인하기 위해, <strong>HashMap</strong>과 이중 for문을 활용해 모든 부분 문자열마다 조건을 검사하는 방식으로 문제를 풀었다.</li>
<li>하지만 이중 for문은 매번 윈도우(부분 문자열)를 새로 검사해야 하므로 <strong>비효율적(시간복잡도 O(NL))</strong>이라는 한계를 느꼈다.</li>
<li>효율적인 방법을 고민하다가, <strong>&#39;슬라이딩 윈도우(Sliding Window)&#39;</strong>라는 알고리즘 패턴을 접했다.</li>
<li><strong>슬라이딩 윈도우</strong>를 적용하니, 한 칸씩 윈도우를 이동할 때마다 새로 등장/사라지는 문자만 업데이트해서 훨씬 빠르게 풀 수 있었다.</li>
<li>이번 문제를 통해 “구간 합/카운팅”이 필요한 경우, 단순 완전탐색보다 <strong>슬라이딩 윈도우 기법</strong>을 우선적으로 고민해볼 필요가 있다는 점을 배웠다.</li>
</ul>
<hr>
<h4 id="💡-슬라이딩-윈도우란">💡 슬라이딩 윈도우란?</h4>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b208aecb-e7bc-4ca0-ad5e-f0fe721fadf5/image.png" alt=""></p>
<ul>
<li>슬라이딩 윈도우는 <strong>연속된 구간</strong>(부분 배열/부분 문자열 등)에 대한 계산을 효율적으로 처리하는 알고리즘 기법입니다.</li>
<li>&quot;시작점과 끝점&quot; 두 개의 포인터를 사용해서 고정된 크기의 윈도우(구간)를 왼쪽에서 오른쪽으로 이동시키며,<br>윈도우가 이동할 때 바뀌는 부분만 빠르게 갱신합니다.(맨앞 , 맨뒤)</li>
<li>이 방식을 활용하면, <strong>매번 전체 구간을 다시 계산하는 것보다 훨씬 빠르게 문제를 해결</strong>할 수 있습니다.</li>
</ul>
<blockquote>
<p>&quot;부분합/카운팅&quot;과 같이 <strong>연속된 범위</strong>에서 뭔가를 구하는 문제가 나오면 사용</p>
</blockquote>
<hr>
<p>문제를 풀고 개념을 정리하다보니, 문득 드는 생각이 있었다.
&#39;투포인터와 슬라이딩 윈도우.. 너무 비슷한데?&#39; 그래서 비교 해봤다.</p>
<h2 id="🤔-투포인터-vs-슬라이딩-윈도우-뭐가-달라">🤔 투포인터 vs 슬라이딩 윈도우, 뭐가 달라?</h2>
<h2 id="🤔-투포인터-vs-슬라이딩-윈도우-뭐가-달라-1">🤔 투포인터 vs 슬라이딩 윈도우, 뭐가 달라?</h2>
<table>
<thead>
<tr>
<th></th>
<th><strong>투포인터</strong></th>
<th><strong>슬라이딩 윈도우</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>개념</strong></td>
<td>인덱스 두 개로 구간/조합 탐색</td>
<td>고정 크기 구간(윈도우) 한 칸씩 이동</td>
</tr>
<tr>
<td><strong>패턴</strong></td>
<td>두 포인터 독립/양방향 이동</td>
<td>윈도우 범위 고정, 한쪽만 이동</td>
</tr>
<tr>
<td><strong>적용 예시</strong></td>
<td>연속합/쌍/조합/부분합(2018,1940,2sum)</td>
<td>구간합/빈도(12891, 부분문자열 카운트 등)</td>
</tr>
<tr>
<td><strong>특징</strong></td>
<td>구간 확장·축소/양끝 조합/자유 이동</td>
<td>구간 크기 고정/추가·제거만 갱신</td>
</tr>
<tr>
<td><strong>관계</strong></td>
<td>투포인터가 “상위 개념”, 슬라이딩 윈도우는 하위</td>
<td>투포인터의 특수형</td>
</tr>
</tbody></table>
<ul>
<li><strong>모든 슬라이딩 윈도우는 투포인터다!</strong><br>(단, 모든 투포인터가 슬라이딩 윈도우는 아님)</li>
<li>슬라이딩 윈도우는 “구간(윈도우) 크기 고정”에서 <strong>추가/제거만</strong> 관리하며 효율을 높이는 “투포인터 활용법”이다.</li>
</ul>
<hr>
<h2 id="5-백준-11286---우선순위-큐절댓값-힙">5. 백준 11286 - 우선순위 큐(절댓값 힙)</h2>
<h3 id="문제-링크-4">문제 링크</h3>
<ul>
<li><a href="https://www.acmicpc.net/problem/11286">https://www.acmicpc.net/problem/11286</a></li>
</ul>
<hr>
<h3 id="풀이-4">풀이</h3>
<pre><code class="language-java">
 public static void main(String[] args) throws IOException {

        StringBuilder sb = new StringBuilder();

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int length = Integer.parseInt(br.readLine());


        int[] arr = new int[length];
        for (int i = 0; i &lt; length; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }



        PriorityQueue&lt;Integer&gt; pq = new PriorityQueue&lt;&gt;(
                (a,b) -&gt; {
                    if (Math.abs(a) == Math.abs(b)) {
                        return a - b;
                    } else {
                        return Math.abs(a) - Math.abs(b);
                    }
                }
        );

        for (int x : arr) {
            if (x == 0) {
                if (pq.isEmpty()) {
                    sb.append(0);
                    sb.append(&quot;\n&quot;);
                }else{
                Integer poll = pq.poll();
                sb.append(poll);
                sb.append(&quot;\n&quot;);}
            } else {
                pq.add(x);
            }
        }
        System.out.println(sb.toString());
    }
</code></pre>
<hr>
<h3 id="배운점--접근법-4">배운점 &amp; 접근법</h3>
<ul>
<li>처음에는 일반적인 큐로 접근했으나, 코드가 점점 비효율적인 방법으로 길어졌다.</li>
<li>큐에 다른 종류의 자료구조를 찾아보다가, 우선순위 큐라는 자료구조를 접했고, 활용 해보기로 했다.</li>
<li>추상클래스나 람다형식으로 사용하는게 편하겠다고 느꼈고, 그 부분에 대해서 좀 더 자세하게 공부하면서 문제에 적용 해봤다.</li>
</ul>
<p>여기서 추가적으로, 우선순위 큐의 정렬 기준을 직접 커스터마이징 하다보니<br><strong>Comparable과 Comparator의 차이점</strong>도 자연스럽게 알게 되었다.</p>
<p>두 인터페이스의 핵심 차이는 다음과 같다:</p>
<ul>
<li><strong>Comparable</strong> : &quot;자기 자신(this)과 매개변수 객체를 비교&quot;</li>
<li><strong>Comparator</strong> : &quot;두 매개변수 객체를 비교&quot;</li>
</ul>
<h2 id="✅-comparable">✅ Comparable</h2>
<ul>
<li>클래스 내부에서 정렬 기준을 <strong>직접 구현</strong>하고,<br>한 클래스에 <strong>한 가지 정렬 기준</strong>만 제공할 수 있다.</li>
</ul>
<pre><code class="language-java">public class Student implements Comparable&lt;Student&gt; {
    int score;
    public Student(int score) { this.score = score; }

    @Override
    public int compareTo(Student o) {
        return this.score - o.score; // 오름차순
    }
}

PriorityQueue&lt;Student&gt; pq = new PriorityQueue&lt;&gt;();
)</code></pre>
<p>PriorityQueue는 기본적으로 comparator가 null이기 때문에,<br>내부적으로 compareTo() 메서드 기준으로 정렬하게 된다.</p>
<h2 id="✅-comparator">✅ Comparator</h2>
<ul>
<li><strong>클래스 외부에서</strong> 별도의 정렬 기준을 만들 수 있고,<br>여러 기준을 동적으로 지정할 수 있다.</li>
</ul>
<h3 id="v2-별도-구현-클래스-사용">V2. 별도 구현 클래스 사용</h3>
<pre><code class="language-java">import java.util.Comparator;

public class ScoreDescComparator implements Comparator&lt;Student&gt; {
    @Override
    public int compare(Student a, Student b) {
        return b.score - a.score; // 내림차순
    }
}

PriorityQueue&lt;Student&gt; pq = new PriorityQueue&lt;&gt;(new ScoreDescComparator());
)</code></pre>
<h3 id="v3-익명-클래스-사용">V3. 익명 클래스 사용</h3>
<pre><code class="language-java">PriorityQueue&lt;Student&gt; pq = new PriorityQueue&lt;&gt;(
    new Comparator&lt;Student&gt;() {
        @Override
        public int compare(Student a, Student b) {
            return b.score - a.score; // 내림차순
        }
    }
);
)</code></pre>
<h3 id="v4-람다식-사용">V4. 람다식 사용</h3>
<pre><code class="language-java">PriorityQueue&lt;Student&gt; pq = new PriorityQueue&lt;&gt;(
    (a, b) -&gt; b.score - a.score // 내림차순
);
)</code></pre>
<h2 id="✅-결론">✅ 결론</h2>
<ul>
<li>Comparable은 <strong>클래스 내부에서 고정된 정렬 기준을 제공</strong></li>
<li>Comparator는 <strong>외부에서 다양한 기준으로 유연하게 정렬 가능</strong></li>
</ul>
<p>이번 문제를 풀면서 Comparable과 Comparator의 동작 방식을 실제로 체득할 수 있었고,<br>특히 람다식으로 간단하게 정렬 기준을 작성할 수 있어 실전에도 적극 활용할 수 있을 것 같다.</p>
<hr>
<h2 id="마무리-및-느낀-점">마무리 및 느낀 점</h2>
<ul>
<li><strong>자료구조를 활용한 알고리즘 문제는 무조건 실습</strong>해봐야 감이 생긴다 
“<strong>왜 이 방식이 시간/공간 면에서 효율적인가?</strong>”,<br>“<strong>내가 직접 손으로 움직이면 어떻게 되지?</strong>”  
를 생각하며 반복하니 확실히 이해가 깊어진다.</li>
<li>각 문제별로 &quot;내가 처음 막혔던 포인트&quot;, &quot;실전에서 어떤 상황에 적용할지&quot;를 메모해두면 나중에 큰 자산이 될 것 같다.</li>
</ul>
<hr>
<h2 id="reference">[Reference]</h2>
<blockquote>
<ul>
<li><a href="https://www.inflearn.com/course/%EB%91%90%EC%9E%87-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%B0%94/dashboard">Do it! 알고리즘 코딩테스트 with JAVA - 하루코딩(인프런)</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[놀라운 운영체제 #3 – CPU 스케줄링]]></title>
            <link>https://velog.io/@losiento_nana/CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</link>
            <guid>https://velog.io/@losiento_nana/CPU-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81</guid>
            <pubDate>Thu, 17 Jul 2025 08:23:55 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EA%B0%9C%EC%9A%94">개요</a></li>
<li><a href="#1-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC%EC%9D%98-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%A2%85%EB%A5%98">1. 스케줄러의 개념 및 종류</a><ul>
<li><a href="#1-1-%EC%9E%A5%EA%B8%B0%EA%B3%A0%EC%88%98%EC%A4%80-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC">1-1. 장기(고수준) 스케줄러</a></li>
<li><a href="#1-2-%EB%8B%A8%EA%B8%B0%EC%A0%80%EC%88%98%EC%A4%80-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC">1-2. 단기(저수준) 스케줄러</a></li>
<li><a href="#1-3-%EC%A4%91%EA%B8%B0%EC%A4%91%EA%B0%84%EC%88%98%EC%A4%80-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC">1-3. 중기(중간수준) 스케줄러</a></li>
</ul>
</li>
<li><a href="#2-cpu-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81%EC%9D%B4%EB%9E%80">2. CPU 스케줄링이란?</a></li>
<li><a href="#3-cpu-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81%EC%9D%98-%EB%AA%A9%EC%A0%81">3. CPU 스케줄링의 목적</a></li>
<li><a href="#4-%EC%84%A0%EC%A0%90%ED%98%95-vs-%EB%B9%84%EC%84%A0%EC%A0%90%ED%98%95-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81">4. 선점형 vs 비선점형 스케줄링</a></li>
<li><a href="#5-cpu-%EC%A7%91%EC%A4%91-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-vs-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%A7%91%EC%A4%91-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4">5. CPU 집중 프로세스 vs 입출력 집중 프로세스</a></li>
<li><a href="#6-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%EB%B0%8F-%EB%B6%84%EB%A5%98">6. 프로세스 우선순위 및 분류</a></li>
<li><a href="#7-%EB%8B%A4%EC%A4%91-%ED%81%90queue-%EA%B4%80%EB%A6%AC">7. 다중 큐(Queue) 관리</a><ul>
<li><a href="#7-1-%EC%A4%80%EB%B9%84-%EC%83%81%ED%83%9C%EC%9D%98-%EB%8B%A4%EC%A4%91-%ED%81%90">7-1. 준비 상태의 다중 큐</a></li>
<li><a href="#7-2-%EB%8C%80%EA%B8%B0-%EC%83%81%ED%83%9C%EC%9D%98-%EB%8B%A4%EC%A4%91-%ED%81%90">7-2. 대기 상태의 다중 큐</a></li>
</ul>
</li>
<li><a href="#8-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%EC%84%A0%ED%83%9D-%EA%B8%B0%EC%A4%80">8. 스케줄링 알고리즘의 선택 기준</a></li>
<li><a href="#9-%EC%A3%BC%EC%9A%94-cpu-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">9. 주요 CPU 스케줄링 알고리즘</a></li>
<li><a href="#10-%EB%8B%A4%EB%8B%A8%EA%B3%84-%ED%81%90mlq-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81">10. 다단계 큐(MLQ) 스케줄링</a></li>
<li><a href="#11-%EB%8B%A4%EB%8B%A8%EA%B3%84-%ED%94%BC%EB%93%9C%EB%B0%B1-%ED%81%90mlfq-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81">11. 다단계 피드백 큐(MLFQ) 스케줄링</a></li>
<li><a href="#reference">Reference</a></li>
</ul>
<h2 id="개요">개요</h2>
<p>앞선 글에서 프로세스와 스레드가 어떻게 운영체제에서 관리되는지 살펴봤다.</p>
<p>여러 작업이 동시에 돌아가는 상황에서,
운영체제는 “누구에게, 언제, 어떻게 CPU 사용 기회를 줄 것인가?”라는
복잡하고 중요한 결정을 내려야 한다.
만약 이 선택이 효율적이지 못하다면, 어떤 작업은 지나치게 오래 기다리거나 시스템 전체의 응답성이 떨어질 수 있다.</p>
<p>이번 글에서는,
이런 현실적인 문제와 한계를 해결하기 위해 운영체제가 어떤 기준과 원리로
각 작업의 CPU 사용 순서를 정하는지,
CPU의 선택 기준을 결정짓는 스케줄링 전략에 대해 다뤄보고자 한다.</p>
<h2 id="1-스케줄러의-개념-및-종류">1. 스케줄러의 개념 및 종류</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/15ec0b21-3d4f-4af9-bfa1-65ab7881ae6c/image.png" alt=""></p>
<p>운영체제에서 스케줄러(Scheduler)는 여러 프로세스 중 어떤 것을 실행할지 결정하는 역할을 한다. 효율적 자원 관리와 시스템의 성능 향상에 매우 중요하다.</p>
<ul>
<li><strong>장기(고수준) 스케줄러 (Long-Term Scheduler)</strong></li>
<li><strong>단기(저수준) 스케줄러 (Short-Term Scheduler)</strong></li>
<li><strong>중기(중간수준) 스케줄러 (Medium-Term Scheduler)</strong></li>
</ul>
<p>운영체제에서의 프로세스 상태 전이와도 밀접한 관련이 있다.
앞서 다룬 프로세스의 5가지 상태(생성, 준비, 실행, 대기, 종료, 보류 등)에서
각 스케줄러가 어느 단계에 관여하는지 예시로 보면 더욱 쉽게 이해할 수 있다.</p>
<hr>
<h3 id="1-1-장기고수준-스케줄러">1-1. 장기(고수준) 스케줄러</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/72cce8c2-e4c9-4efe-bd2c-a928af3272b4/image.png" alt=""></p>
<p>역할: 보조 기억장치(디스크)에 있는 작업 중 어떤 작업을 메모리(주기억장치)에 올릴지 결정한다.</p>
<p>프로세스가 생성된 후,
→ <strong>작업 큐(NEW 상태)</strong>에 있던 프로세스가
→ <strong>준비 상태(Ready Queue)</strong>로 진입할 때
→ 장기 스케줄러가 개입하여 &quot;입장관리자&quot; 역할을 한다.</p>
<p><strong>특징: 실행 빈도가 낮고, 시스템 전체의 작업 부하 조절 역할</strong></p>
<h2 id="특징-실행-빈도가-낮고-시스템-전체의-작업-부하-조절-역할">특징: 실행 빈도가 낮고, 시스템 전체의 작업 부하 조절 역할</h2>
<h3 id="1-2-단기저수준-스케줄러">1-2. 단기(저수준) 스케줄러</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/2c64e4ed-376e-47ea-8934-d39024f08419/image.png" alt=""></p>
<p>역할: 준비 상태(Ready Queue)에 있는 프로세스 중에서 CPU에 할당할 프로세스를 선택한다.</p>
<p>프로세스가 <strong>준비 상태(Ready)</strong>에서
→ <strong>실행 상태(Running)</strong>로 전환될 때
→ 단기 스케줄러가 개입하여 실제 CPU에 올라갈 프로세스를 결정한다.</p>
<p><strong>특징: 실행 빈도가 매우 높으며, 컨텍스트 스위칭(문맥교환)마다 동작</strong></p>
<hr>
<h3 id="1-3-중기중간수준-스케줄러">1-3. 중기(중간수준) 스케줄러</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/4e84c3de-20ef-4e77-8b79-5d4205a4a70d/image.png" alt=""></p>
<p>역할: 실행/대기 중인 프로세스 중 일부를 메모리에서 디스크로 내보내거나(swap-out), 다시 불러옴(swap-in)</p>
<p>예를 들어 입출력 대기(Waiting) 상태나,
시스템에 메모리가 부족할 때
→ 중기 스케줄러가 프로세스를 &quot;보류(Suspended)&quot; 상태로 전환해
일시적으로 메모리에서 내보내고, 필요할 때 다시 메모리에 불러오는 역할을 한다.</p>
<p><strong>특징: 시스템 메모리 부하 조절, 프로세스 상태 전환(ready ↔ suspended) 담당</strong></p>
<hr>
<h2 id="2-cpu-스케줄링이란">2. CPU 스케줄링이란?</h2>
<p>컴퓨터에서 동시에 실행되는 여러 프로세스(혹은 스레드)들이
모두 한 번에 CPU를 사용할 수는 없다.
따라서 운영체제는 한정된 CPU 자원을 각 작업에 어떻게, 언제, 누구에게 배분할지
지속적으로 판단하고 결정해야 한다.</p>
<p>이처럼 CPU라는 가장 중요한 자원을 여러 작업에게 효율적으로 나누어주는 작업을
바로 <strong>CPU 스케줄링(CPU Scheduling)</strong>이라고 한다.</p>
<p>CPU 스케줄링은 시스템 전체의 성능, 각 작업의 응답 속도,
자원의 효율적 분배 등 운영체제의 전반적인 품질에 직접적으로 영향을 미치는
핵심 기능 중 하나다.</p>
<hr>
<h2 id="3-cpu-스케줄링의-목적">3. CPU 스케줄링의 목적</h2>
<p>CPU 스케줄링은 아래와 같은 목표를 달성하기 위해 실행된다.</p>
<ul>
<li><strong>공평성(Fairness):</strong> 모든 프로세스가 공정하게 CPU를 사용할 수 있도록.</li>
<li><strong>효율성(Efficiency):</strong> 자원을 최대한 활용.</li>
<li><strong>안정성(Stability):</strong> 예측 가능하고 안정적인 시스템 동작.</li>
<li><strong>확장성(Scalability):</strong> 시스템 규모가 커져도 일정 성능 유지.</li>
<li><strong>반응 시간 보장(Response time):</strong> 대화형 시스템 등에서 빠른 응답.</li>
<li><strong>무한 연기 방지(Starvation 방지):</strong> 특정 프로세스가 영원히 실행되지 못하는 현상 방지.</li>
</ul>
<hr>
<h2 id="4-선점형-vs-비선점형-스케줄링">4. 선점형 vs 비선점형 스케줄링</h2>
<p>여러 프로세스가 CPU를 두고 경쟁하는 환경에서는, 작업의 순서뿐만 아니라,
CPU를 할당받은 작업을 얼마나 오랫동안 실행하냐? 또한 하나의 문제다.</p>
<p>이때 운영체제는 CPU 사용권을 프로세스에게 <strong>어떻게, 언제까지 맡길지</strong>에 따라 크게
두가지 방식의 스케줄링 전략을 사용한다.</p>
<ul>
<li><p><strong>선점형(Preemptive):</strong></p>
<ul>
<li>실행 중인 프로세스라도 더 높은 우선순위의 프로세스가 오면 CPU를 빼앗길 수 있음.</li>
<li>ex) 라운드로빈(RR), SRTF, 우선순위(선점형)</li>
</ul>
</li>
<li><p><strong>비선점형(Non-Preemptive):</strong></p>
<ul>
<li>CPU를 할당받은 프로세스의 작업이 끝나거나, 입출력등으로 CPU를 반납할 때까지 실행.</li>
<li>ex) FCFS, SJF(비선점), 우선순위(비선점)</li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-cpu-집중-프로세스-vs-입출력-집중-프로세스">5. CPU 집중 프로세스 vs 입출력 집중 프로세스</h2>
<p>운영체제에서 프로세스의 성격을 구분할 때,<br><strong>CPU 집중 프로세스</strong>와 <strong>입출력(I/O) 집중 프로세스</strong>로 나누기도 한다.</p>
<ul>
<li><p><strong>CPU 집중 프로세스</strong>  </p>
<ul>
<li>수학 계산, 암호화 등과 같이 <strong>CPU 연산을 많이 사용하는 작업</strong></li>
<li><strong>CPU 버스트(CPU burst)</strong> 구간이 길고 빈번하게 발생</li>
<li>입출력 작업 전에 CPU에서 오랜 시간 연산을 수행함</li>
</ul>
</li>
<li><p><strong>입출력 집중 프로세스</strong>  </p>
<ul>
<li>파일 복사, 데이터베이스 접근 등 <strong>입출력(I/O) 작업이 많은 작업</strong></li>
<li><strong>I/O 버스트(I/O burst)</strong> 구간이 많음</li>
<li>CPU에서 잠깐 연산한 뒤, 곧바로 저장장치 등 외부 장치와 데이터를 주고받는 작업을 반
복</li>
</ul>
<hr>
</li>
</ul>
<h2 id="6-프로세스-우선순위-및-분류">6. 프로세스 우선순위 및 분류</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/809ae5d6-2b7d-416a-bafe-ec7b1fcb5883/image.png" alt=""></p>
<p>운영체제는 모든 프로세스에게 동등한 조건으로 CPU를 배분하지 않는다.
CPU 스케줄링 시 각 프로세서의 <strong>중요도</strong>나 <strong>성격</strong>에 따라 우선순위를 부여한다.</p>
<ul>
<li><strong>커널 프로세스 &gt; 사용자 프로세스</strong>
(시스템 핵심 기능을 수행하는 커널 프로세스가 더 높은 우선순위를 가짐)</li>
<li><strong>입출력 집중 프로세스 &gt; CPU 집중 프로세스</strong><br>(I/O 작업을 많이 하는 프로세스에 CPU를 우선 할당하면 전체 시스템 효율이 높아진다)</li>
<li><strong>전면(전경) 프로세스 &gt; 후면(백그라운드) 프로세스</strong><br>(사용자 직접 사용하는 작업이 우선)</li>
</ul>
<h2 id="7-다중-큐queue-관리">7. 다중 큐(Queue) 관리</h2>
<h3 id="7-1-준비-상태의-다중-큐">7-1. 준비 상태의 다중 큐</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b743aff7-4351-4dbc-a530-7b8f07fdc34d/image.png" alt=""></p>
<ul>
<li>프로세스는 저마다 <strong>중요도(우선순위)</strong>가 다르며, 이 값은 프로세스 제어 블록(PCB)에 기록된다.</li>
<li>CPU 스케줄러가 모든 PCB를 매번 뒤져 가장 높은 우선순위 프로세스를 찾는 것은 비효율적이기 때문에,</li>
<li><strong>프로세스는 준비 상태에 들어올 때마다 자신의 우선순위에 해당하는 큐의 마지막(tail)에 삽입</strong>된다.</li>
<li>준비 큐의 개수나, 여러 큐 중 어느 프로세스에 CPU를 먼저 할당할지는 <strong>스케줄링 알고리즘</strong>에 따라 다르다.</li>
</ul>
<p><strong>우선순위 배정 방식</strong></p>
<ul>
<li><strong>고정 우선순위 (Static Priority):</strong> 프로세스가 종료될 때까지 우선순위가 변하지 않는다.</li>
<li><strong>변동 우선순위 (Dynamic Priority):</strong> 작업 도중에도 우선순위가 바뀔 수 있다.<br>(예: 오래 기다린 프로세스의 우선순위를 올려주는 &quot;에이징(aging)&quot; 등)<br>※ 우선순위가 낮은 프로세스가 높은 프로세스로 변할 때 이를 <strong>반전 우선순위(priority inversion)</strong>라고도 한다.</li>
</ul>
<hr>
<h3 id="7-2-대기-상태의-다중-큐">7-2. 대기 상태의 다중 큐</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/ff611afd-7cfe-4e61-ba60-55630fc9fc07/image.png" alt=""></p>
<ul>
<li><strong>대기 상태(Waiting)</strong>는 입출력이 완료되기를 기다리는 프로세스들이 모여 있는 곳이다.</li>
<li>시스템에는 다양한 종류의 입출력 장치가 있으므로,<br><strong>동일한 입출력(I/O) 장치를 기다리는 프로세스들끼리 별도의 큐</strong>로 관리한다.</li>
<li>이를 통해, 필요한 프로세스를 더 효율적으로 찾고 관리할 수 있다.</li>
</ul>
<p><strong>준비 큐 vs 대기 큐 차이</strong></p>
<ul>
<li><p><strong>준비 큐:</strong> 한 번에 하나의 프로세스만 꺼내서 CPU를 할당</p>
</li>
<li><p><strong>대기 큐:</strong> 여러 프로세스의 입출력이 동시에 끝나면(여러 인터럽트 발생 시),<br>관련된 여러 프로세스가 한꺼번에 준비 상태로 이동</p>
</li>
<li><p>시스템에는 여러 입출력 장치가 있어, 입출력이 동시에 완료되면 <strong>인터럽트 벡터</strong>를 통해<br>완료된 모든 프로세스 제어 블록을 준비 상태로 옮긴다.</p>
</li>
</ul>
<hr>
<h2 id="8-스케줄링-알고리즘의-선택-기준">8. 스케줄링 알고리즘의 선택 기준</h2>
<p>CPU 스케줄링 알고리즘을 선택할 때 운영체제는 여러 가지 현실적인 기준을 종합적으로 고려한다.<br>대표적인 선택 기준은 다음과 같다.</p>
<ul>
<li><p><strong>CPU 이용률(Throughput):</strong><br>단위 시간 동안 처리된 프로세스의 수를 의미.<br>(값이 높을수록 시스템이 많은 작업을 효율적으로 처리하고 있다는 뜻)</p>
</li>
<li><p><strong>평균 대기 시간(Average Waiting Time):</strong><br>각 프로세스가 준비 큐에서 실행을 기다린 평균 시간.<br>(짧을수록 사용자 입장에서 쾌적하다)</p>
</li>
<li><p><strong>반환 시간(Turnaround Time):</strong><br>프로세스가 시스템에 들어온 시점부터 완전히 끝나는 시점까지 걸린 총 시간.<br>(사용자/작업의 전체 처리 효율을 평가하는 지표)</p>
</li>
<li><p><strong>응답 시간(Response Time):</strong><br>사용자 입력(명령) 이후, 시스템이 첫 반응을 보일 때까지 걸린 시간.<br>(대화형 시스템, 실시간 시스템에서 특히 중요)</p>
</li>
<li><p><strong>공정성(Fairness):</strong><br>모든 프로세스(사용자)가 합리적으로 CPU 사용 기회를 갖는지 여부.<br>(특정 작업이 계속 무시되거나, 기아(Starvation)가 발생하지 않게끔 설계)</p>
</li>
<li><p><strong>시스템 특성/요구사항:</strong><br>예를 들어 실시간 시스템은 빠른 응답이,<br>대용량 배치 시스템은 전체 처리량이 더 중요할 수 있다.</p>
</li>
</ul>
<h2 id="9-주요-cpu-스케줄링-알고리즘">9. 주요 CPU 스케줄링 알고리즘</h2>
<h3 id="9-1-fcfs-first-come-first-served-선입선출">9-1. FCFS (First-Come, First-Served, 선입선출)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d6091fdf-4060-4a35-9c1f-cf2b02f85a60/image.png" alt=""></p>
<ul>
<li><strong>원리:</strong> 먼저 도착한 순서대로 처리(큐의 앞에서부터 순차적으로 실행)</li>
<li><strong>장점:</strong> 구현이 매우 쉽고, 공평하다</li>
<li><strong>단점:</strong> 앞에 긴 작업이 있으면 짧은 작업이 오래 대기하는 <strong>Convoy Effect(호위 효과)</strong> 발생<br>→ 평균 대기시간이 늘어날 수 있음</li>
<li><strong>적용:</strong> 배치 작업 등, 작업 순서가 중요한 경우</li>
</ul>
<hr>
<h3 id="9-2-sjf-shortest-job-first-최단-작업-우선">9-2. SJF (Shortest Job First, 최단 작업 우선)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/511877fd-cb8f-4bf2-b3b0-6e2d060e8319/image.png" alt=""></p>
<ul>
<li><strong>원리:</strong> 실행 시간이 가장 짧은 작업부터 실행</li>
<li><strong>장점:</strong> 평균 대기 시간을 최소화할 수 있음(이론상 가장 효율적)</li>
<li><strong>단점:</strong> 각 작업의 실제 실행 시간을 미리 알기 어려움.<br>긴 작업이 계속 뒤로 밀릴 경우 <strong>기아(Starvation)</strong>가 발생할 수 있음</li>
<li><strong>적용:</strong> 작업 길이를 예측할 수 있는 상황, 단순한 배치 처리 등</li>
</ul>
<hr>
<h3 id="9-3-srfsrtf-shortest-remaining-time-first-남은-시간-우선">9-3. SRF(SRTF) (Shortest Remaining Time First, 남은 시간 우선)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/6a96debd-b023-4bf5-827d-b5be34dd3358/image.png" alt=""></p>
<ul>
<li><strong>원리:</strong><br>SJF의 <strong>선점형</strong> 버전.<br>새로운 작업이 도착하면 현재 작업과 남은 시간을 비교하여<br><strong>더 짧은 작업이 있으면 바로 CPU를 빼앗아 교체(선점)</strong></li>
<li><strong>장점:</strong><br>SJF의 평균 대기시간 최소화 효과를 유지하면서<br>실시간으로 더 짧은 작업에 즉각 반응할 수 있음</li>
<li><strong>단점:</strong><br>각 작업의 남은 시간을 항상 추적/예측해야 하는 부담<br>긴 작업의 기아 가능성 존재</li>
<li><strong>적용:</strong><br>실시간 시스템, 대화형 환경 등 빠른 반응성 필요할 때</li>
</ul>
<hr>
<h3 id="9-4-hrn-highest-response-ratio-next-응답률-우선">9-4. HRN (Highest Response Ratio Next, 응답률 우선)</h3>
<ul>
<li><p><strong>원리:</strong><br>응답률 = (대기시간 + 실행시간) / 실행시간<br>이 값이 가장 높은 프로세스부터 실행</p>
</li>
<li><p><strong>장점:</strong><br>짧은 작업이 빨리 끝나면서도,<br>오래 기다린 작업의 우선순위도 자동으로 올라가<br><strong>기아(Starvation)</strong>를 방지할 수 있다.</p>
</li>
<li><p><strong>적용:</strong><br>다양한 작업이 섞인 환경에서 공정성과 효율성을 동시에 추구</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">프로세스</th>
<th align="center">대기 시간 (W)</th>
<th align="center">실행 시간 (S)</th>
<th align="center">응답률 (R)</th>
</tr>
</thead>
<tbody><tr>
<td align="center">P1</td>
<td align="center">2</td>
<td align="center">4</td>
<td align="center">(2 + 4) / 4 = <strong>1.5</strong></td>
</tr>
<tr>
<td align="center">P2</td>
<td align="center">4</td>
<td align="center">3</td>
<td align="center">(4 + 3) / 3 = <strong>2.33</strong></td>
</tr>
<tr>
<td align="center">P3</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">(1 + 2) / 2 = <strong>1.5</strong></td>
</tr>
</tbody></table>
<ul>
<li>이 경우, <strong>P2</strong>의 응답률(R)이 가장 높으므로 먼저 실행된다.</li>
<li>실행이 끝나면, 남은 프로세스들의 대기 시간이 늘어나<br>다음 선택에 자동으로 반영된다.</li>
</ul>
<hr>
<h3 id="9-5-라운드-로빈rr-round-robin">9-5. 라운드 로빈(RR, Round Robin)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/93ff4288-adcd-47de-ae5b-5c28f67aae03/image.png" alt=""></p>
<ul>
<li><strong>원리:</strong><br>각 프로세스에 <strong>동일한 시간 할당량(Quantum)</strong>을 주고,<br>할당 시간이 끝나면 준비 큐의 맨 뒤로 이동</li>
<li><strong>장점:</strong><br>모든 프로세스가 <strong>정기적으로 실행 기회</strong>를 보장받아,<br>대화형/멀티태스킹 환경에서 <strong>빠른 응답성</strong>을 가짐</li>
<li><strong>단점:</strong><br>타임퀀텀(Quantum) 크기 조절이 중요:<br>너무 작으면 오버헤드(문맥교환)가 커지고,<br>너무 크면 FCFS와 별 차이 없어짐</li>
<li><strong>적용:</strong><br>사용자와 직접 상호작용이 많은 운영체제, 멀티태스킹 환경</li>
</ul>
<hr>
<h3 id="9-6-우선순위-스케줄링priority-scheduling">9-6. 우선순위 스케줄링(Priority Scheduling)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/9a826c28-4fc8-4dd5-9ab8-d247399a4bf0/image.png" alt=""></p>
<ul>
<li><strong>원리:</strong><br>프로세스마다 <strong>우선순위</strong>를 지정해서,<br>더 높은 우선순위 작업부터 실행(선점형/비선점형 모두 지원)</li>
<li><strong>장점:</strong><br>중요한 작업을 빠르게 처리 가능(유연성)</li>
<li><strong>단점:</strong><br>낮은 우선순위 작업이 계속 밀려서 <strong>기아(Starvation)</strong> 발생 가능<br>→ 이를 보완하기 위해 <strong>에이징(Aging)</strong> 등 기법 사용</li>
<li><strong>적용:</strong><br>실시간 시스템, 미션 크리티컬한 작업이 있는 경우</li>
</ul>
<hr>
<blockquote>
<p>실제 운영체제에서는 이들 알고리즘을 조합하거나,<br>상황에 따라 동적으로 전환하며 <strong>시스템 목적(효율/응답/공정성 등)에 최적화</strong>합니다.</p>
</blockquote>
<hr>
<h2 id="10-다단계-큐mlq-스케줄링">10. 다단계 큐(MLQ) 스케줄링</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/c8bf0dfa-7d96-49fa-966c-57c5b372e802/image.png" alt=""></p>
<p>실제 운영체제에는 다양한 유형의 프로세스(대화형/배치/시스템/실시간 등)가 동시에 존재합니다.<br>각 유형의 프로세스는 요구하는 서비스 특성이 다르고,<br>한 가지 스케줄링 알고리즘만으로는 모든 요구를 충족하기 어렵습니다.</p>
<p><strong>다단계 큐(MLQ, Multi-Level Queue) 스케줄링</strong>은  
이런 현실적인 문제를 해결하기 위한 방식입니다.</p>
<ul>
<li><p><strong>동작 원리:</strong>  </p>
<ul>
<li>여러 개의 Ready Queue(준비 큐)를 운영  </li>
<li>각 큐마다 프로세스의 유형이나 우선순위, 성격(예: 시스템/대화형/배치/실시간 등)에 따라 프로세스를 분리  </li>
<li>각 큐별로 서로 다른 스케줄링 알고리즘을 적용할 수 있음<br>(예: 대화형 큐는 RR, 배치 큐는 FCFS 등)</li>
<li><strong>큐 간 이동이 불가</strong>(프로세스는 배정된 큐에서만 실행)</li>
</ul>
</li>
<li><p><strong>예시:</strong><br>1번 큐: 실시간 작업(RR, 빠른 응답 보장)<br>2번 큐: 대화형 프로그램(짧은 타임슬라이스 RR)<br>3번 큐: 배치 작업(FCFS, 긴 작업도 차례로 처리)  </p>
</li>
<li><p><strong>특징:</strong>  </p>
<ul>
<li>분류 기준(큐 배정)은 주로 프로세스의 우선순위, 생성 목적, 사용자 등으로 결정</li>
<li><strong>각 큐별로 자원 할당 비율을 달리할 수 있어 자원 관리가 유연</strong></li>
<li><strong>프로세스가 큐를 한 번 배정받으면, 다른 큐로 옮겨 다닐 수 없음</strong><br>→ <em>큐 간 이동이 없기 때문에, &quot;고정 분류&quot;에 가깝다.</em></li>
</ul>
</li>
<li><p><strong>장점:</strong>  </p>
<ul>
<li>다양한 유형의 작업을 효율적으로 분리 및 관리</li>
<li>우선순위, 시스템 특성에 맞는 차별화된 서비스 가능</li>
</ul>
</li>
<li><p><strong>단점:</strong>  </p>
<ul>
<li>큐 배정이 잘못되면 특정 큐의 작업이 영원히 실행되지 못할 수도 있음(기아 발생)</li>
</ul>
</li>
</ul>
<hr>
<h2 id="11-다단계-피드백-큐mlfq-스케줄링">11. 다단계 피드백 큐(MLFQ) 스케줄링</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/f03c0dd0-21b0-4b9e-bb2b-975646ebbb6b/image.png" alt=""></p>
<p><strong>다단계 피드백 큐(MLFQ, Multi-Level Feedback Queue)</strong>는  
다단계 큐의 한계(큐 간 이동 불가, 고정 우선순위 등)를 극복하기 위해<br>큐 간 이동과 동적 우선순위를 도입한 더욱 “현대적”인 스케줄링 기법입니다.</p>
<ul>
<li><p><strong>동작 원리:</strong>  </p>
<ul>
<li>여러 개의 큐를 두고, 프로세스가 <strong>상위 큐에서 하위 큐로 자유롭게 이동</strong>할 수 있음</li>
<li>새로 도착한 프로세스는 <strong>항상 최상위 큐(가장 높은 우선순위)에 배정</strong></li>
<li>주어진 시간(타임퀀텀) 내에 작업을 끝내지 못하면,<br>→ 더 낮은 우선순위(하위) 큐로 이동(“피드백”)</li>
<li>반대로, 오랜 대기 등 특정 조건을 만족하면 우선순위가 다시 올라갈 수도 있음(aging)</li>
</ul>
</li>
<li><p><strong>타임퀀텀(Quantum) 특징:</strong>  </p>
<ul>
<li><strong>상위 큐일수록 더 짧은 타임퀀텀</strong>을 할당<br>→ 대화형/짧은 작업에 빠른 응답성 제공</li>
<li><strong>하위 큐로 갈수록 타임퀀텀이 길어짐</strong><br>→ 긴 작업도 차별받지 않고, 결국엔 처리 보장</li>
</ul>
</li>
<li><p><strong>장점:</strong>  </p>
<ul>
<li>응답성이 중요한 대화형 작업과, 긴 배치 작업을 모두 효율적으로 관리</li>
<li>우선순위가 동적으로 변하므로, 특정 작업이 계속 무시되는 “기아(Starvation)”를 방지</li>
<li>실제 현대 운영체제(예: Windows, Linux)에서 가장 널리 사용</li>
</ul>
</li>
<li><p><strong>단점:</strong>  </p>
<ul>
<li>큐 이동/우선순위 관리가 복잡해 구현 난이도가 높다</li>
</ul>
</li>
<li><p><strong>예시:</strong>  </p>
<ul>
<li>새로 들어온 단기 작업은 상위 큐에서 빠르게 처리  </li>
<li>오래 CPU를 점유하거나, 시간이 오래 걸리는 작업은 점차 하위 큐로 내려가<br>→ CPU 자원을 “공정하게” 분배  </li>
<li>대기 시간이 길어진 작업은 다시 우선순위가 올라가기도 한다</li>
</ul>
</li>
</ul>
<hr>
<p><strong>MLQ vs MLFQ 요약 비교</strong></p>
<table>
<thead>
<tr>
<th></th>
<th>MLQ(다단계 큐)</th>
<th>MLFQ(다단계 피드백 큐)</th>
</tr>
</thead>
<tbody><tr>
<td>큐간 이동</td>
<td>X(불가, 고정 큐)</td>
<td>O(상하위 큐 이동 가능)</td>
</tr>
<tr>
<td>우선순위</td>
<td>고정</td>
<td>동적(작업 성격에 따라 변화)</td>
</tr>
<tr>
<td>목적</td>
<td>유형별 분리, 효율화</td>
<td>응답성 + 공정성 + 효율성</td>
</tr>
<tr>
<td>대표적 적용</td>
<td>제한적/과거 OS</td>
<td>현대 OS(윈도우, 리눅스 등)</td>
</tr>
</tbody></table>
<hr>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lombok 사용시 주의 사항]]></title>
            <link>https://velog.io/@losiento_nana/Lombok-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98-%EC%82%AC%ED%95%AD</link>
            <guid>https://velog.io/@losiento_nana/Lombok-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98-%EC%82%AC%ED%95%AD</guid>
            <pubDate>Tue, 15 Jul 2025 08:50:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글에서는 Lombok의 단순한 사용법이 아닌,
개발을 하면서 직접 느낀 주의해야 할 점을 중심으로 다룹니다.</p>
</blockquote>
<h2 id="1-lombok이란">1. Lombok이란?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/79936fe5-9584-4bb8-8d25-10c1502d21c8/image.png" alt=""></p>
<p>Lombok은 Java에서 반복되는 코드(예: getter, setter, toString, 생성자 등)를<br>어노테이션 한 줄로 자동 생성해주는 라이브러리다.<br>코드가 심플해지고, 생산성이 대폭 올라간다.</p>
<hr>
<h2 id="2-lombok-기본-어노테이션-요약">2. Lombok 기본 어노테이션 요약</h2>
<table>
<thead>
<tr>
<th>어노테이션</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>@Getter/@Setter</td>
<td>getter/setter 자동생성 (클래스/필드별 지정)</td>
</tr>
<tr>
<td>@NoArgsConstructor</td>
<td>기본 생성자 생성</td>
</tr>
<tr>
<td>@AllArgsConstructor</td>
<td>모든 필드 생성자 생성</td>
</tr>
<tr>
<td>@RequiredArgsConstructor</td>
<td>final/@NonNull 필드 생성자 생성</td>
</tr>
<tr>
<td>@Builder</td>
<td>빌더패턴 생성자</td>
</tr>
<tr>
<td>@EqualsAndHashCode</td>
<td>equals/hashCode 자동 생성</td>
</tr>
<tr>
<td>@ToString</td>
<td>toString 자동 생성</td>
</tr>
<tr>
<td>@Data</td>
<td>위의 대부분 기능 포함, 실무에선 비추천</td>
</tr>
<tr>
<td>@Slf4j/@Log4j2</td>
<td>로그 객체 자동 주입</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-lombok-사용시-주의-할-점">3. Lombok 사용시 주의 할 점</h2>
<h3 id="setter-남발-금지-특히-entity">Setter 남발 금지 (특히 Entity)</h3>
<ul>
<li>모든 필드에 @Setter를 남발하면, 불변성이나 캡슐화가 깨진다.</li>
<li>엔티티의 필드값이 아무곳에서나 수정되면 디버깅/유지보수에 악몽을 경험 할지도</li>
<li>되도록 생성자/빌더 패턴으로 생성하고, 꼭 필요한 필드에만 Setter을 사용하자</li>
</ul>
<pre><code class="language-java">@Getter
@NoArgsConstructor
public class User {
    private String name; // setter X
    private String email; // setter X
    private int age;

    @Setter
    private String nickname;

    }
}</code></pre>
<blockquote>
<p>예를 들어 소셜로그인으로 로그인 하는 회원에 관해 nickname 외에는 수정할 수 없게 제약을 걸어놓는 상황.</p>
</blockquote>
<hr>
<h2 id="4-entity-생성은-builder생성자로-수정은-setter별도-메서드">4. Entity 생성은 Builder/생성자로, 수정은 setter/별도 메서드</h2>
<ul>
<li><p>Entity 생성(등록):
  → 빌더 패턴(@Builder), 생성자(@AllArgsConstructor)로 필수값을 한번에 확정</p>
</li>
<li><p>Entity 값 수정(업데이트):
  → 비즈니스 로직에서만 setter or 별도의 변경 메서드로 값 변경
  (예: changeEmail(String email) 등)</p>
</li>
</ul>
<pre><code class="language-java">
@Getter
@NoArgsConstructor
public class User {
    private String name;
    private String email;
    private int age;

    @Setter
    private String nickname;

    // 빌더 패턴 사용: 모든 필드 초기화
    @Builder
    public User(String name, String email, int age, String nickname) {
        this.name = name;
        this.email = email;
        this.age = age;
        this.nickname = nickname;
    }
}

// 사용 예시

        User user = User.builder()
                .name(&quot;홍길동&quot;)
                .email(&quot;gildong@kakao.com&quot;)
                .age(25)
                .nickname(&quot;길동이&quot;)
                .build();

        // 2. 생성 후 닉네임만 setter로 변경 가능
        user.setNickname(&quot;길동쓰&quot;);

        // 3. 아래 코드는 컴파일 에러!
        // user.setName(&quot;해커&quot;);
        // user.setEmail(&quot;hacker@evil.com&quot;);
        // user.setAge(100);
</code></pre>
<hr>
<h2 id="5-data-사용-지양">5. @Data 사용 지양</h2>
<ul>
<li>@Data는 선언시 getter/setter, equals/hashCode, toString, RequiredArgsConstructor 전부 생성이 된다.</li>
<li>겉보기에는 편해보이지만, 필요하지 않는 기능까지 사용이 되면서, 객체의 불변성이나 캡슐화 이 깨지거나, 불필요한 메서드 생성이 되는 단점이 있다.</li>
<li>@Getter @Setter @Builder 등등 필요한 것만 명시적으로 사용하는게 이롭다.</li>
</ul>
<hr>
<h2 id="6-jpa에서-올바른-조합">6. JPA에서 올바른 조합</h2>
<ul>
<li><p>JPA는 DB에서 데이터를 조회할 때 프록시/리플렉션을 활용하여 기본 생성자(No-Args Constructor)로 객체를 생성함.</p>
</li>
<li><p>즉, 기본 생성자가 반드시 필요
(없으면 런타임 에러 발생: “No default constructor for entity” 등)</p>
</li>
</ul>
<pre><code class="language-java">import lombok.*;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA를 위한 기본 생성자(필수)
public class User {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private String email;

    @Builder // 빌더 패턴을 위한 생성자(직접 작성)
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
}</code></pre>
<blockquote>
<ul>
<li>@NoArgsConstructor(access = PROTECTED) : JPA 프레임워크 전용 기본 생성자 제공(외부에서 직접 사용 불가)</li>
</ul>
</blockquote>
<ul>
<li>@Builder 생성자: 개발자가 사용할 생성자 제공</li>
</ul>
<hr>
<h2 id="7-requiredargsconstructor-사용">7. @RequiredArgsConstructor 사용</h2>
<ul>
<li><p>@RequiredArgsConstructor는 클래스 내에서 <strong>final</strong> 혹은 <strong>@NonNull</strong>이 붙은 필드의 생성자 파라미터를 자동으로 생성해준다</p>
</li>
<li><p>꼭 생성 시에만 값이 할당되어야 하거나, 의존성 주입(DI)이 필요한 클래스(예: 서비스, 엔티티, DTO, 도메인 객체 등)에 사용하면 유용하다.</p>
</li>
</ul>
<pre><code class="language-java">import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class Book {

    @NonNull
    private String title;           // 생성자 파라미터 (필수)
    private final String author;    // 생성자 파라미터 (필수)
    private int pageCount;          // 생성자에 포함X (선택값)
    private String publisher;       // 생성자에 포함X (선택값)
}</code></pre>
<h2 id="lombok-실전-사용-버전">Lombok 실전 사용 버전</h2>
<pre><code class="language-java">import lombok.*;
import javax.persistence.*;

@Entity
@Table(name = &quot;bookstore&quot;)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(exclude = &quot;contactNumber&quot;)
@EqualsAndHashCode(of = {&quot;name&quot;, &quot;address&quot;}, callSuper = false)
public class Bookstore extends BaseTimeEntity {

    private String name;                // 서점명
    private String address;             // 도로명 주소
    private String businessType;        // 업종(서점/중고서점 등)
    private String owner;               // 대표자명
    private String contactNumber;       // 연락처 (toString에서 제외)
    private boolean openOnWeekend;      // 주말 영업 여부
    private String postalCode;          // 우편번호
    private double longitude;           // 경도
    private double latitude;            // 위도

    @Builder
    public Bookstore(String name, String address, String businessType, String owner,
                     String contactNumber, boolean openOnWeekend, String postalCode,
                     double longitude, double latitude) {
        this.name = name;
        this.address = address;
        this.businessType = businessType;
        this.owner = owner;
        this.contactNumber = contactNumber;
        this.openOnWeekend = openOnWeekend;
        this.postalCode = postalCode;
        this.longitude = longitude;
        this.latitude = latitude;
    }
}</code></pre>
<p>위의 예시를 통해 전달하고자 하는 내용은 아래와 같다.</p>
<h3 id="동일한-서점-정보-중복-방지">동일한 서점 정보 중복 방지</h3>
<ul>
<li>@EqualsAndHashCode(of = {&quot;name&quot;, &quot;address&quot;})</li>
<li><strong>서점명(name)과 주소(address)</strong>가 동일하다면 같은 객체로 판단</li>
</ul>
<h3 id="민감-정보-출력-방지">민감 정보 출력 방지</h3>
<ul>
<li>@ToString(exclude = &quot;contactNumber&quot;)</li>
<li><strong>연락처(contactNumber)</strong>는 toString에서 제외해, 로그 등에서 불필요하게 노출되는 것을 막음</li>
</ul>
<h3 id="생성은-빌더로">생성은 빌더로</h3>
<ul>
<li>@Builder로 여러 데이터가 섞여 들어오는 상황에서도 안전하게 객체 생성 가능</li>
</ul>
<h3 id="jpa-기본-생성자-제공">JPA 기본 생성자 제공</h3>
<ul>
<li>@NoArgsConstructor(access = PROTECTED)</li>
<li>JPA 프레임워크만 사용할 수 있도록 보호</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[한화 BEYOND SW 캠프 18기 [4주차] & 나의 개발 성장 회고
]]></title>
            <link>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 14 Jul 2025 18:12:41 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-한화-beyond-sw-캠프-4주차-wil-weekly-reflection">✨ 한화 BEYOND SW 캠프 [4주차] WIL (Weekly Reflection)</h1>
<blockquote>
<p>이 글은 BEYOND SW 캠프 4주차 동안의 성장과 배움을 스스로 돌아보고 기록하기 위한 회고입니다.</p>
</blockquote>
<hr>
<h2 id="🔗-이번주-캠프-학습노트정리">🔗 이번주 캠프 학습노트/정리</h2>
<blockquote>
<p>이번주 수업 중 새롭게 배운 내용이나, 직접 정리해 본 개념/기술에 대해<br>벨로그에 따로 아카이브해두었습니다.<br>(관심 있는 분들은 아래 링크에서 자세히 확인해 주세요)</p>
</blockquote>
<blockquote>
<p>수업에서 배운 내용 외에도 꾸준히 CS 공부를 진행중입니다. CS를 정리 한 내용을 아래에 첨부 하겠습니다. CS에 관심 있으신 분들은 읽어봐주시면 되겠습니다.</p>
</blockquote>
<ul>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #4 – 트랜잭션</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-5-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadlock">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #5 – 교착상태(Deadlock)</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EC%9D%98-%EA%B8%B0%EC%B4%88">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #6 – SQL Injection</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-NoSQL">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #6 – NoSQL</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/Welcome-to-OS-world">놀라운 운영체제 #1 – 운영체제 ?</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C">놀라운 운영체제 #2 – 운영체제 ?</a></p>
</li>
</ul>
<hr>
<h2 id="📅-이번-주-한-일-facts">📅 이번 주 한 일 (FACTS)</h2>
<ul>
<li>CS 스터디 첫번째 주제 마무리(데이터베이스)</li>
<li>첫번째 프로젝트 마무리</li>
<li>멈춰있던 개인 프로젝트 다시 진행</li>
<li>알고리즘 스터디</li>
</ul>
<hr>
<h2 id="💭-느낀-점감정-feelings">💭 느낀 점/감정 (FEELINGS)</h2>
<p>이번주에는 그동안 팀원들과 함께 준비했던 프로젝트를 발표했다. 그동안 다함께 고생(?)하면서 만든 프로젝트 이기에 끝이 났다는것에 후련했다. 또한 이 프로젝트때 ERD 및 테이블 설계에 대해 다양한 고민을 했기에, 설계에 가장 뇌가 말랑말랑 한 시기라고 판단해서 기존에 진행하던 개인 프로젝트에 설계 파트도 다시 검토하면서 수정했다. 또한 함께 CS 스터디를 하던 학생들과의 첫번째 주제인 데이터 베이스에 대한 파트도 끝이 났다. 길지 않은 시간이였지만, 매일 꾸준히 데이터베이스의 한 주제에 대해 정하여 공부하고 정리했다. 단순히 쿼리를 짜는것을 떠나 데이터 베이스에 대한 개념에 대해 많은걸 배웠던것 같다. 그리고 CS스터디를 같이 하던 동기들과 코테 준비를 위해 알고리즘도 1주일에 한번씩 진행하기로 했는데, 앞으로 기대 된다.</p>
<hr>
<h2 id="🔍-배운-점새롭게-알게-된-것-findings">🔍 배운 점/새롭게 알게 된 것 (FINDINGS)</h2>
<p>핵심적으로 많이 학습한건 SQL에 심화 과정이 아니였지 않을까 싶다. 트랜잭션,교착상태,SQL Injection, NoSQL같은 경우 수업에서 다루지 않은 내용이 였기에 보충으로 스스로 학습하기 아주 좋은 내용이였다. 또한 개인 프로젝트를 스프링 프로젝트를 진행하면서 그저 관습으로 여기고 습관처럼 쓰던 디자인 패턴들에 대해 의문점을 갖고, 찾아보면서 복습하는 시간도 좋았던 것 같다. 그리고 스스로 해야하는걸 알지만 귀찮아서 외면하던 알고리즘도 강제성을 부여해서 시작했고, 값진 시간이였다.</p>
<hr>
<h2 id="🚧-어려움--문제-problem-or-minus">🚧 어려움 &amp; 문제 (Problem or Minus)</h2>
<p>첫번째 프로젝트 발표에 앞서 마지막으로 우리가 진행했던 자료들을 정리하는데, 각 파트를 나눠서 진행하다 보니 조금씩 내용이 다르거나, 쿼리에 사용된 변수나 설정이 다른 포인트들이 있었다. 스터디를 준비하면서 내용이 많다보니까, 어느 순간 이해하지 않고 그저 글로만 정리를 하려는 느낌도 가끔 받았던 것 같다. </p>
<hr>
<h2 id="🛠️-해결-방법--시도-try-or-improve">🛠️ 해결 방법 &amp; 시도 (Try or Improve)</h2>
<p>발표 자료 정리 과정에서 각자 맡은 부분마다 조금씩 다른 내용이 있다는 걸 느꼈을 때, 팀원들과 직접 다시 모여 각 파트를 하나씩 점검하며 통일했습니다. 특히 변수명이나 쿼리 부분처럼 소소한 차이가 나도 실제로 결과가 달라질 수 있다는 점을 다시금 체감했습니다.
이후에는 초반에 작은 부분이라도 혼선을 방지하기 위해, 초안 단계에서부터 최대한 컨벤션과 공통 규칙을 맞추고 시작해야 겠다고 생각 했습니다.
CS 스터디에서는 단순히 ‘정리’만 하는 게 아니라, 내가 스스로 이해했는지 확인하기 위해 직접 예제 쿼리도 돌려보고, 서로 설명해보는 시간을 추가로 가졌습니다. 덕분에 ‘이해’ 위주로 습득할 수 있었던 것 같습니다.</p>
<hr>
<h2 id="🌱-앞으로의-계획--다짐-future">🌱 앞으로의 계획 &amp; 다짐 (Future)</h2>
<p>곧 시작될 두 번째 프로젝트에서는 이번 경험을 바탕으로 문서화와 커뮤니케이션에 더 신경 쓰려고 합니다.
또, 알고리즘 스터디도 꾸준히 유지해서 기초 코딩 실력을 탄탄하게 쌓고 싶습니다.
다음주부터 이어지는 CS/알고리즘 등 다양한 주제에서 <strong>“이해→실습→정리”</strong>의 루틴을 계속 이어가겠습니다.
특히 혼자 공부할 때와는 다르게, 팀원들과 의견을 주고받으며 배우는 점이 많다는 걸 이번에 느꼈기 때문에, 남은 기간 동안 적극적으로 참여하고, 주도적으로 의견을 내는 습관을 들이려고 합니다.</p>
<hr>
<h2 id="🙌-한-줄-마무리">🙌 한 줄 마무리</h2>
<blockquote>
<p>진짜 힙한 개발자는 기본에 충실한거 아시나요</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[놀라운 운영체제 #2 – 프로세스와 스레드]]></title>
            <link>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C</link>
            <guid>https://velog.io/@losiento_nana/%EB%86%80%EB%9D%BC%EC%9A%B4-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C</guid>
            <pubDate>Mon, 14 Jul 2025 17:26:35 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EA%B0%9C%EC%9A%94">개요</a></li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%9E%80">프로세스란?</a><ul>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%84%9C-%EC%A0%9C%EC%96%B4-%EB%B8%94%EB%A1%9Dpcb">프로세서 제어 블록(PCB)</a></li>
<li><a href="#%EA%B2%BD%EA%B3%84-%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0%EC%99%80-%ED%95%9C%EA%B3%84-%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0">경계 레지스터와 한계 레지스터</a></li>
</ul>
</li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EC%83%81%ED%83%9C">프로세스의 상태</a><ul>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80-%EC%83%81%ED%83%9C">프로세스의 다섯 가지 상태</a></li>
<li><a href="#%EB%AC%B8%EB%A7%A5%EA%B5%90%ED%99%98">문맥교환</a></li>
</ul>
</li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EA%B5%AC%EC%A1%B0">프로세스의 구조</a></li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-fork-exec">프로세스의 생성과 fork(), exec()</a></li>
<li><a href="#%EC%8A%A4%EB%A0%88%EB%93%9C%EB%9E%80">스레드란?</a><ul>
<li><a href="#%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%A0%95%EC%9D%98">스레드의 기본 정의</a></li>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80%EC%9D%98-%EA%B4%80%EA%B3%84">프로세스와의 관계</a></li>
<li><a href="#%EC%9E%91%EC%97%85%EC%9D%98-%EC%B5%9C%EC%86%8C-%EB%8B%A8%EC%9C%84%EB%9E%80">&quot;작업의 최소 단위&quot;란?</a></li>
<li><a href="#%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%99%80-cpu-%EC%9E%85%EC%9E%A5%EC%97%90%EC%84%9C%EC%9D%98-%EC%8A%A4%EB%A0%88%EB%93%9C">운영체제와 CPU 입장에서의 스레드</a></li>
</ul>
</li>
<li><a href="#%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EC%B0%A8%EC%9D%B4">스레드와 프로세스의 차이</a><ul>
<li><a href="#%ED%98%BC%EB%8F%99-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC">혼동 가능한 용어 정리</a></li>
</ul>
</li>
<li><a href="#%EC%8A%A4%EB%A0%88%EB%93%9C%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C">스레드를 왜 사용할까?</a><ul>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A7%8C-%EC%82%AC%EC%9A%A9%ED%96%88%EC%9D%84-%EB%95%8C%EC%9D%98-%ED%95%9C%EA%B3%84">프로세스만 사용했을 때의 한계</a></li>
</ul>
</li>
<li><a href="#%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C">멀티스레드</a><ul>
<li><a href="#%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EC%9E%90%EC%9B%90-%EA%B4%80%EB%A6%AC">프로세스의 자원 관리</a></li>
<li><a href="#%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%9E%90%EC%9B%90-%EA%B4%80%EB%A6%AC">스레드의 자원 관리</a></li>
<li><a href="#%EB%B3%91%ED%96%89-%EC%88%98%ED%96%89concurrency%EC%99%80-%EB%B3%91%EB%A0%AC-%EC%88%98%ED%96%89parallelism">병행 수행(Concurrency)와 병렬 수행(Parallelism)</a></li>
<li><a href="#%EB%B3%91%ED%96%89-%EC%88%98%ED%96%89concurrency%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1">병행 수행(Concurrency)의 필요성</a></li>
</ul>
</li>
<li><a href="#3-%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B5%AC%ED%98%84-%EB%AA%A8%EB%8D%B8">스레드 구현 모델</a><ul>
<li><a href="#1-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%8A%A4%EB%A0%88%EB%93%9C-user-level-thread-1n-%EB%AA%A8%EB%8D%B8">사용자 스레드 (User-Level Thread, 1:N 모델)</a></li>
<li><a href="#2-%EC%BB%A4%EB%84%90-%EC%8A%A4%EB%A0%88%EB%93%9C-kernel-level-thread-11-%EB%AA%A8%EB%8D%B8">커널 스레드 (Kernel-Level Thread, 1:1 모델)</a></li>
<li><a href="#3-%EB%A9%80%ED%8B%B0%EB%A0%88%EB%B2%A8-%EC%8A%A4%EB%A0%88%EB%93%9C-multi-level-thread-mn-%EB%AA%A8%EB%8D%B8">멀티레벨 스레드 (Multi-level Thread, M:N 모델)</a></li>
<li><a href="#%EC%BB%A4%EB%84%90-%EC%8A%A4%EB%A0%88%EB%93%9C11%EC%99%80-%EB%A9%80%ED%8B%B0%EB%A0%88%EB%B2%A8mn%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%8B%A4%EC%A0%84%EC%97%90%EC%84%9C-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EC%9D%84%EA%B9%8C">커널 스레드와 멀티레벨(M:N)의 차이, 실전에서 어떤 차이가 있을까?</a></li>
<li><a href="#%EC%8B%A4%EC%A0%84-%EC%9A%94%EC%95%BD-%ED%91%9C">실전 요약 표</a></li>
<li><a href="#Reference">Reference</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="개요">개요</h2>
<p>앞선 글에서 운영체제의 핵심 역할로 자원 관리, 자원 보호, 하드웨어/사용자 인터페이스 제공 등을 살펴봤다.
이 중에서도 CPU와 메모리 같은 컴퓨터의 핵심 자원을 여러 프로그램이 안전하고 효율적으로 사용하도록 관리하는 것이 운영체제의 가장 중요한 일 중 하나다.</p>
<p>여기서 등장하는 것이 바로 프로세스와 스레드다.</p>
<p>운영체제는 우리가 실행하는 각각의 프로그램(예: 웹브라우저, 음악플레이어, 에디터 등)을 <strong>‘프로세스’</strong>라는 단위로 관리한다.
또한, 현대 소프트웨어에서는 한 프로그램 안에서도 여러 작업을 동시에 처리할 필요가 있어,
운영체제는 ‘스레드’라는 더 작은 실행 단위를 제공한다.</p>
<p>이 글에서는</p>
<ul>
<li><p>프로세스와 스레드가 무엇인지</p>
</li>
<li><p>운영체제가 왜 이 두 가지 단위로 자원을 관리하는지</p>
</li>
<li><p>CPU 스케줄링, 메모리 관리, 자원 보호와 프로세스/스레드가 어떻게 연결되는지
차근차근 풀어볼 예정이다.</p>
</li>
</ul>
<hr>
<h2 id="프로세스란">프로세스란?</h2>
<p>프로세스란 간단히 말하면 실행중인 프로그램이다.
하지만 그냥 프로그램이 아니라 운영체제로부터 PCB(프로세스 제어 블록)을 얻은 프로그램이다.</p>
<p>그렇다면 PCB란 뭘까?</p>
<hr>
<h3 id="프로세서-제어-블록pcb">프로세서 제어 블록(PCB)</h3>
<p>프로세스 제어 블록에는 대표적으로 3가지 정보가 있다.</p>
<ol>
<li>PID(프로세스 구분자) : 각 프로세스를 구분하는 구분자</li>
<li>메모리 관련 정보: 프로세스의 메모리 위치 정보, 메모리 보호를 위한 경계 레지스터와 한계 레지스터</li>
<li>각종 중간값(프로그램 카운터, 레지스터 값, 메모리 정보 등): 프로세스의 진행 도중의 상태 정보(다시 시작될때 이어가기 위함)</li>
</ol>
<p>그 외에도 프로세스 제어 블록에는 더 많은 정보가 있지만, 모두 다루진 않겠다.</p>
<blockquote>
<h4 id="경계-레지스터와-한계-레지스터">경계 레지스터와 한계 레지스터?</h4>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0e15f69d-87ea-468d-b343-981197a3958c/image.png" alt="">
<strong>경계 레지스터와 한계 레지스터</strong>는 운영체제가 프로세스의 메모리 보호를 위해 <strong>메모리의 시작주소와 메모리의 공간의 크기</strong>를 저장하는 레지스터이다.
운영체제는 두 레지스터를 함께 써서, 프로세스가 자기 메모리 범위만 안전하게 접근하도록 운영체제가 감시한다.</p>
</blockquote>
<hr>
<h2 id="프로세스의-상태">프로세스의 상태</h2>
<p>그렇게 PCB블록을 얻고 프로세스가 되면 프로세스는 어떤 과정을 거치면서 완료하는 걸까?
프로세스의 상태를 크게 다섯가지 상태로 나눌수 있다.</p>
<h3 id="프로세스의-다섯-가지-상태">프로세스의 다섯 가지 상태</h3>
<p>프로세스의 다섯 가지 상태를 보기전에 이해를 돕기 위해 단계를 나눠보겠다.</p>
<ul>
<li>프로세스의 네 가지 상태
<img src="https://velog.velcdn.com/images/losiento_nana/post/79ca4d98-3730-4558-833e-cbb18e74803a/image.png" alt=""></li>
</ul>
<p>각 단계는 CPU 스케줄러에 의해 통제 받는다.</p>
<ol>
<li>CPU 스케줄러가 준비 상태에 있는 여러 프로세스 중 다음에 실행할 프로세스를 선정한다.</li>
<li>준비 상태의 프로세스 중 하나를 골라 실행 상태로 바꾼다.</li>
<li>프로세스가 자신에게 주어진 하나의 타임 슬라이스 동안 작업을 끝내지 못하면 다시 준비 상태로 돌아간다.</li>
</ol>
<ul>
<li>프로세스의 다섯 가지 상태
<img src="https://velog.velcdn.com/images/losiento_nana/post/d8a419bd-d06a-4b56-b467-82860e66b2ef/image.png" alt=""></li>
</ul>
<p>프로세스의 네 가지 상태에 추가로 대기상태가 추가 됐다.
대기 상태란 실행 상태의 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태다. 이는 작업의 효율을 높이기 위해 만들어진 것이다.</p>
<p><strong>표로 보는 프로세스 작업 상태</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/87edfe32-daed-4aa5-8203-4ef89cd3eb2e/image.png" alt=""></p>
<blockquote>
<p>기본적으로는 <strong>준비(Ready), 실행(Running), 대기(Waiting), 종료(Terminated)</strong>의 네 가지 상태, 그리고 대기(입출력 등)로 인한 “다섯 가지 상태”가 핵심이다.
하지만 실제 운영체제에서는,
특정 프로세스가 외부 요인(예: 메모리 부족, 관리자가 일시 중지 등)으로 인해 “일시적으로 완전히 중단”되는 상황도 존재한다.
이런 경우, 프로세스는 “보류(Suspended, 휴식) 상태”로 추가 관리된다.
<strong>참고로 활성 상태와 보류 상태의 차이는 메모리 위에 올라왔는가 아닌가로 구분하면 된다.</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/97c6cb4d-9227-4956-86fe-4d800d8df80d/image.png" alt=""></p>
</blockquote>
<p>그렇다면 여러 프로세스가 번갈아가며 실행되는 환경에서,
운영체제는 어떻게 각 프로세스의 작업을 중단했다가 다시 이어서 실행할 수 있을까?
여기서 사용되는 개념이 바로 문맥 교환이다.</p>
<hr>
<h3 id="문맥교환">문맥교환</h3>
<p>문맥교환이란 운영체제가 현재 실행 중인 프로세스의 상태(문맥)를 PCB에 저장하고,
다른 프로세스의 PCB에 기록된 정보를 다시 CPU에 복원해서
새로운 프로세스가 끊김 없이 실행을 이어가도록 하는 과정을 말한다.</p>
<p>위에서 PCB의 주요 역할중에 <strong>각종 중간값(프로그램 카운터, 레지스터 값, 메모리 정보 등)</strong>을 저장하는 역할에 대해 언급 했는데, 그 각종 중간값의 개념이 이때 사용된다.
각종 프로세스의 마지막 값을 기억하고, 다시 실행될 때 그 정보를 불러와서 그 지점부터 작업을 계속 이어간다.</p>
<ul>
<li><strong>문맥교환</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/54757bd4-d565-415e-9860-112e0825b91f/image.png" alt=""></li>
</ul>
<p>타임 슬라이스의 크기의 기준으로 문맥 교환을 진행하기 때문에 타임 슬라이스의 크기를 적절하게 설정하는것이 중요하다.</p>
<ul>
<li><strong>타임 슬라이스 크기 설정</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/02801755-e986-45c4-91f0-fe91be57ac9b/image.png" alt=""></li>
</ul>
<h2 id="프로세스의-구조">프로세스의 구조</h2>
<p>그렇다면 프로세스의 구조는 어떻게 나뉘어져 있을까?</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/7a6e78af-47af-4121-9415-44ff1cb235ac/image.png" alt=""></p>
<h3 id="코드-영역">코드 영역</h3>
<ul>
<li>프로그램의 본문이 기술된 곳</li>
<li>프로그래머가 작성한 코드가 탑재되며 탑재된 코드는 읽기 전용으로 처리됨</li>
</ul>
<h3 id="데이터-영역">데이터 영역</h3>
<ul>
<li>코드가 실행되면서 사용하는 변수나 파일 등의 각종 데이터를 모아놓은 곳</li>
<li>데이터는 변하는 값이므로 이곳의 내용은 기본적으로 읽기와 쓰기가 가능</li>
</ul>
<h3 id="힙-영역">힙 영역</h3>
<ul>
<li>실행 도중에 <strong>동적으로 할당되는 데이터</strong>(객체, 배열, 포인터 등)를 저장하는 공간</li>
<li>개발자가 명시적으로 메모리를 할당(<code>malloc</code>, <code>new</code> 등)하거나 해제해야 함</li>
<li>크기가 실행 중에 자유롭게 변하며, <strong>프로그램의 런타임에 필요한 데이터</strong>를 유연하게 관리할 수 있음</li>
</ul>
<h3 id="스택-영역">스택 영역</h3>
<ul>
<li>운영체제가 프로세스를 실행하기 위해 필요한 데이터를 모아놓은 곳</li>
<li>프로세스 내에서 함수를 호출하면 함수를 수행하고 원래 프로그램으로 되돌아
올 위치를 저장하는 곳</li>
<li>운영체제가 사용자 프로세스를 작동하기 위해 유지하는 영역으로 사용자에게
는 보이지 않음</li>
</ul>
<p>이렇게 프로세스는 각각의 영역을 나눠서 프로그램 실행에 필요한 다양한 정보를 저장한다.</p>
<hr>
<h2 id="프로세스의-생성과-fork-exec">프로세스의 생성과 fork(), exec()</h2>
<p>운영체제에서 새로운 프로세스를 생성하는 대표적인 방식이 바로 <strong>fork()</strong>와 <strong>exec()</strong> 시스템 콜이다.</p>
<h3 id="fork">fork()</h3>
<ul>
<li><code>fork()</code>는 <strong>현재 실행 중인 프로세스를 “복제”</strong>해서<br>똑같은 PCB와 메모리 구조(코드, 데이터, 스택 등)를 가진 자식 프로세스를 만들어낸다.</li>
<li>복제된 자식 프로세스는 부모 프로세스와 거의 동일하지만,<br><strong>PID(프로세스 식별자)</strong> 등 일부 값만 다르고<br>코드 실행 지점도 부모와 같다.</li>
<li>이렇게 만들어진 두 프로세스(부모와 자식)는<br><strong>각자 독립적으로 실행을 계속한다.</strong></li>
</ul>
<h3 id="exec">exec()</h3>
<ul>
<li><code>exec()</code>는 실행 중인 프로세스의 메모리(코드, 데이터, 스택 등)를<br><strong>새로운 프로그램으로 “완전히 덮어쓰는” 시스템 콜</strong>이다.</li>
<li>보통 <code>fork()</code>로 자식 프로세스를 만든 뒤,<br>자식 프로세스가 <code>exec()</code>를 호출해서<br><strong>새로운 프로그램으로 자신의 내용을 교체</strong>하는 방식으로 많이 사용된다.</li>
<li>이 과정에서 <strong>PID는 그대로 유지</strong>되지만,<br>실행 중이던 프로그램의 내용이 새 프로그램으로 바뀌고<br>기존 코드와 데이터 영역은 모두 사라진다.</li>
</ul>
<p>이 두 가지 시스템 콜을 조합하면<br>운영체제는 <strong>동시에 여러 프로그램을 독립적으로 실행</strong>할 수 있게<br>프로세스를 효과적으로 생성·관리할 수 있다.</p>
<hr>
<h2 id="스레드란">스레드란?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/5d08428b-8ea7-47ec-a978-d9deb321b291/image.png" alt=""></p>
<p>스레드는 <strong>프로세스 내에서 실제로 작업을 수행하는 실행의 흐름(작업의 최소 단위)</strong>이다.</p>
<p>즉 정리하면
• 운영체제 입장에서의 작업 단위는 프로세스
• CPU 입장에서의 작업 단위는 스레드</p>
<h3 id="스레드의-기본-정의">스레드의 기본 정의</h3>
<p>스레드는 <strong>프로세스 내에서 실제로 작업을 수행하는 실행의 흐름(작업의 최소 단위)</strong>입니다.<br>CPU가 처리할 실제 &quot;일&quot;을 의미하며, 한 프로세스 안에 여러 개의 스레드가 존재할 수 있습니다.</p>
<h3 id="프로세스와의-관계">프로세스와의 관계</h3>
<ul>
<li><strong>프로세스</strong>: 실행 중인 프로그램 전체.  <ul>
<li>자원(메모리, 파일, 입출력 등)을 독립적으로 소유</li>
<li>운영체제 입장에서 &quot;작업&quot;의 단위</li>
</ul>
</li>
<li><strong>스레드</strong>: 프로세스 내에서 실제로 코드를 실행하는 &quot;흐름&quot;.<ul>
<li>여러 개의 스레드가 하나의 프로세스 자원을 공유</li>
<li><strong>CPU 입장에서는 &quot;스레드&quot;가 실제 실행 단위</strong></li>
</ul>
</li>
</ul>
<h3 id="작업의-최소-단위란">&quot;작업의 최소 단위&quot;란?</h3>
<ul>
<li>운영체제는 프로세스를 관리하지만,<br>실제로 CPU에서 돌아가는 <strong>실행 흐름</strong>은 스레드 단위로 관리됨</li>
<li>한 프로세스가 여러 스레드를 가지면,<br>하나의 프로그램이 여러 작업을 동시에 처리할 수 있음</li>
</ul>
<h3 id="운영체제와-cpu-입장에서의-스레드">운영체제와 CPU 입장에서의 스레드</h3>
<ul>
<li><strong>운영체제(OS)</strong>: 프로세스 단위로 자원을 배분하고, 각 프로세스 내에서 여러 스레드를 관리  </li>
<li><strong>CPU</strong>: 실제로는 &quot;스레드&quot; 단위로 작업을 받아서 실행<br>(즉, 프로세스가 여러 개의 스레드를 만들고, CPU는 그 스레드들을 번갈아가며 처리)</li>
</ul>
<hr>
<h2 id="스레드와-프로세스의-차이">스레드와 프로세스의 차이</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/702f2cf1-c6c4-4385-9aba-d3924542463e/image.png" alt=""></p>
<p>기본적으로 <strong>스레드와 프로세스</strong>는 모두 운영체제에서 작업을 수행하는 실행 단위 이지만, 구조와 자원 관리 방식, 실제 동작 방식에서 차이가 있다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>프로세스(Process)</th>
<th>스레드(Thread)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>정의</strong></td>
<td>실행 중인 프로그램, 자원을 독립적으로 가지는 단위</td>
<td>프로세스 내에서 실행되는 더 작은 실행 단위</td>
</tr>
<tr>
<td><strong>자원</strong></td>
<td>각각 별도의 메모리(코드, 데이터, 힙, 스택)와 PCB 보유</td>
<td>코드, 데이터, 힙 등은 프로세스와 공유, 스택만 따로 가짐</td>
</tr>
<tr>
<td><strong>독립성</strong></td>
<td>완전히 독립적 (다른 프로세스에 영향 X)</td>
<td>같은 프로세스 내 스레드끼리는 서로 영향을 줄 수 있음</td>
</tr>
<tr>
<td><strong>통신</strong></td>
<td>프로세스 간 통신(IPC)은 복잡하고 비용이 큼</td>
<td>스레드 간 통신은 매우 빠르고 쉽다 (공유 자원 이용)</td>
</tr>
<tr>
<td><strong>오버헤드</strong></td>
<td>생성/종료/문맥교환에 많은 오버헤드(부하)가 발생</td>
<td>스레드끼리의 전환은 오버헤드가 작음</td>
</tr>
<tr>
<td><strong>에러 발생 시</strong></td>
<td>한 프로세스가 죽어도 다른 프로세스에 영향 없음</td>
<td>한 스레드가 에러로 죽으면 같은 프로세스의 다른 스레드도 영향 받을 수 있음</td>
</tr>
</tbody></table>
<h3 id="혼동-가능한-용어-정리"><strong>혼동 가능한 용어 정리</strong></h3>
<p>멀티태스킹: 운영체제가 CPU에 작업을 줄 대 시간을 잘게 나누어 배분하는 기법
멀티스레드: 프로세스 내 작업을 여러 개의 스레드를 분할해 작업 부담을 줄이는 프로세스 운영 기법
멀티 프로세싱: 여러 개 CPU로 여러 개 스레드를 동시에 처리하는 작업 환경
CPU 멀티스레드: 한 번에 하나씩 처리해야 하는 스레드를 잘게 쪼개어 동시에 처리하는 병렬 처리 기법</p>
<hr>
<h2 id="스레드를-왜-사용할까">스레드를 왜 사용할까?</h2>
<h3 id="프로세스만-사용했을-때의-한계">프로세스만 사용했을 때의 한계</h3>
<p>단일 프로세스만으로 여러 작업을 처리하면 하나의 작업이 오래 걸릴때, 전체 프로그램의 반응성이 떨어진다. 또한 I/O 작업이나 네트워크 요청으로 인해 프로세스가 블록되면 전체 기능이 멈출 수도 있다.</p>
<hr>
<h2 id="멀티스레드">멀티스레드</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/36851f86-dc6c-45d8-9106-d43eb27b7a53/image.png" alt=""></p>
<p>멀티스레드란 <strong>하나의 프로세스 안에서 여러 개의 스레드가 동시에 실행되는 구조</strong>를 의미한다. 즉, 하나의 프로그램(프로세스)이 여러 작업(스레드)을 동시에 처리할 수 있도록 만드는 기술이다.</p>
<blockquote>
<p>위 사진을 보면 쓰레드와 프로세스가 공유하는 자원에 대해 다른걸 확인 할 수 있다. 자원 공유의 차이에 대해 한번 다뤄 보겠다.</p>
</blockquote>
<hr>
<h3 id="프로세스의-자원-관리">프로세스의 자원 관리</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/43f58508-0f01-492a-93e3-a9142d77a65f/image.png" alt=""></p>
<ul>
<li><strong>프로세스</strong>는 각각 <strong>독립적인 메모리 공간(코드, 데이터, 힙, 스택 등)을 가짐</strong></li>
<li>프로세스 간에는 <strong>직접적으로 자원을 공유하지 않음</strong><ul>
<li>각 프로세스는 다른 프로세스의 변수나 데이터에 접근할 수 없음</li>
</ul>
</li>
<li><strong>장점</strong>  <ul>
<li>한 프로세스의 오류나 문제가 다른 프로세스에 영향을 주지 않음 → <strong>안정성, 보안성이 높음</strong></li>
<li>운영체제가 각 프로세스를 분리해 관리하므로 시스템 전체가 더 안전함</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>서로 다른 프로세스 간에 데이터를 주고받으려면 </li>
</ul>
<ol>
<li><strong>복잡한 통신(IPC: Inter-Process Communication) 기법</strong>, </li>
<li><strong>LPC(Local inter-Process Communication)</strong>, </li>
<li><strong>별도로 공유 메모리를 만들어서 정보를 주고받도록 설정하는 방법</strong>등 필요</li>
</ol>
</li>
</ul>
<blockquote>
<p>그러나 프로세스 자원 공유는 RAM과 CPU 사이의 캐시 메모리까지 초기화 되기때문에 자원 부담이 크다는 단점이 있다. 그렇기에 다중 작업이 필요한 경우 스레드를 이용하는것이 일반적이다.</p>
</blockquote>
<hr>
<h3 id="스레드의-자원-관리">스레드의 자원 관리</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/4991edff-1a1e-403b-bfab-2c6a11cbf621/image.png" alt=""></p>
<ul>
<li><strong>스레드</strong>는 <strong>같은 프로세스 내부에서 실행되는 여러 작업의 흐름</strong>이다.</li>
<li>같은 프로세스 내의 스레드들은 <strong>코드, 데이터, 힙 등 대부분의 자원(메모리, 파일, 전역 변수 등)을 서로 공유</strong>한다.<br>단, <strong>스택 영역</strong>만은 스레드마다 별도로 가지고 있어 함수 호출/지역 변수는 분리된다.</li>
<li><strong>장점</strong><ul>
<li><strong>빠르고 효율적인 데이터 공유</strong>가 가능해 통신이 매우 간단하다.</li>
<li>자원 복사가 필요 없어 메모리와 CPU 오버헤드가 적다.</li>
<li>스레드 간 전환이 빠르고, 멀티코어 환경에서 병렬 처리가 쉽다.</li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>공유 데이터에 여러 스레드가 동시에 접근하면 <strong>동기화 문제</strong>(Race Condition, Deadlock 등)가 발생할 수 있음 → 동기화(락, 세마포어 등) 필요</li>
<li>한 스레드의 오류나 예외가 전체 프로세스(그리고 모든 스레드)에 영향을 줄 수 있다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="병행-수행concurrency와-병렬-수행parallelism">병행 수행(Concurrency)와 병렬 수행(Parallelism)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0f48feab-f7d9-47c2-94e0-b7f6096868da/image.png" alt=""></p>
<h4 id="병렬-수행parallelism"><strong>병렬 수행(Parallelism)</strong></h4>
<ul>
<li><strong>여러 CPU(코어)</strong>를 활용해 <strong>실제로 여러 작업을 동시에 물리적으로</strong> 실행하는 방식</li>
</ul>
<h4 id="병행-수행concurrency"><strong>병행 수행(Concurrency)</strong></h4>
<ul>
<li>여러 개의 작업(스레드/프로세스)이 <strong>논리적으로 동시에</strong> 실행되는 것처럼 보이도록 운영체제가 빠르게 작업을 전환하는 방식</li>
</ul>
<hr>
<h3 id="병행-수행concurrency의-필요성">병행 수행(Concurrency)의 필요성</h3>
<p>병렬 수행의 경우 직관적으로 좋은 방식이라는게 느껴진다. 하지만 병행 수행의 경우 의문이 생길 수 있다. 병행 수행은 하나의 작업을 잘게 나누어서 조금씩 작업을 진행하고, 다음 작업으로 넘어가는 방식을 빠르게 반복하는건데, 그렇다면 결국 최종 작업이 걸리는 시간은 차이가 없다. 그렇다면 왜 번거롭게 작업을 나눠서 스위칭하며 처리하는 것일까?</p>
<h4 id="1-하드웨어적-한계">1. 하드웨어적 한계</h4>
<p>가장 근본적인 이유는 <strong>CPU(코어) 개수의 한계</strong> 때문이다.<br>실제 대부분의 시스템은 프로세서(코어) 수보다 훨씬 많은 프로그램과 작업을 동시에 처리해야 한다.<br>모든 작업을 병렬로 처리하고 싶어도,<br>물리적으로 사용 가능한 코어가 적다면<br>하나의 코어에 여러 작업을 <strong>순차적으로</strong> 몰아넣을 수밖에 없다.</p>
<blockquote>
<p>실제로 사용자는 여러 앱(코어보다 많은 갯수)을 실행하게 될 때, 운영체제는 병행 수행의 타임슬라이스를 적절하게 조정하여 마치 모든 프로그램이 동시에 동작하는 것처럼 느끼게 해준다.</p>
</blockquote>
<h4 id="2-논리적인-효율">2. 논리적인 효율</h4>
<p>병행 수행의 또 다른 목적은 <strong>시스템의 반응성과 자원 활용 효율</strong>을 높이기 위함이다.
만약 현재 4코어 8스레드로 환경에 <strong>16개의 작업</strong>이 있는데, 이때 8개의 작업은 <strong>오래 걸리는 작업</strong>, 나머지 8개의 작업은 <strong>빠르게 끝나는 작업</strong>이라고 가정할때, 만일 8개의 오래걸리는 작업이 먼저 수행되면, 나머지 8개는 금방 끝나는 작업임에도 불구하고 나머지 8개의 오래 걸리는 작업이 끝날때까지 기다려야 한다. 하지만 병행 수행을 통해 작업을 하게되면 이러한 일이 사전에 방지 된다.</p>
<hr>
<h2 id="3-스레드-구현-모델">3. 스레드 구현 모델</h2>
<p>스레드가 운영체제에서 어떻게 관리되는지에 따라<br>실제 시스템에서는 여러 방식이 사용된다.<br>아래는 각 모델의 실제 동작 예시와 함께 설명한다.</p>
<hr>
<h3 id="1-사용자-스레드-user-level-thread-1n-모델">1. 사용자 스레드 (User-Level Thread, 1:N 모델)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/2559e4a4-b2da-4418-8f96-f14cab6f6e6c/image.png" alt=""></p>
<ul>
<li><strong>정의</strong><br>한 프로세스 내 여러 개의 사용자 스레드가 <strong>커널 스레드 하나와 연결</strong>되어 실행되는 구조</li>
<li><strong>동작</strong><br>스레드 생성/전환/종료 등 모든 관리가 <strong>언어 런타리브러리</strong>(사용자 수준)에서 직접 이루어짐<br>운영체제(커널)는 프로세스 하나만 인식, 내부 스레드는 모름</li>
<li><strong>장점</strong>  <ul>
<li>스레드 생성·전환이 매우 빠르고, 오버헤드 거의 없음</li>
</ul>
</li>
<li><strong>단점</strong>  <ul>
<li><strong>커널 스레드가 I/O 등으로 블록되면, 모든 사용자 스레드가 함께 멈춤</strong></li>
<li>여러 CPU(코어)를 동시 활용 불가(병렬성 부족)</li>
</ul>
</li>
<li><strong>실제 예시</strong>  <ul>
<li><strong>구버전 자바의 Green Thread</strong>  <blockquote>
<p>5개의 크롤링 스레드 중 한 스레드가 네트워크 I/O로 블록되면,<br>나머지 4개 스레드도 모두 같이 대기 → 전체 작업 지연<br>멀티코어 CPU여도 한 코어만 사용</p>
</blockquote>
</li>
<li>일부 경량 스레드 라이브러리 등</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-커널-스레드-kernel-level-thread-11-모델">2. 커널 스레드 (Kernel-Level Thread, 1:1 모델)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/cffb1478-dff7-423c-9c46-2008a8baad24/image.png" alt=""></p>
<ul>
<li><strong>정의</strong><br>하나의 사용자 스레드가 <strong>하나의 커널 스레드</strong>와 직접 연결되어 운영체제가 관리하는 구조</li>
<li><strong>동작</strong><br>각 스레드는 운영체제 커널에 의해 <strong>독립적으로 인식, 스케줄링</strong><br>(운영체제가 스레드를 직접 관리)</li>
<li><strong>장점</strong>  <ul>
<li>한 스레드가 I/O 등으로 블록되어도, <strong>다른 스레드는 계속 동작</strong></li>
<li>멀티코어 환경에서 진짜 병렬 실행 가능</li>
<li>현대 OS/프로그램에서 가장 널리 쓰임</li>
</ul>
</li>
<li><strong>단점</strong>  <ul>
<li>스레드 생성·전환 시 시스템 콜 필요 → <strong>오버헤드(성능 부하)</strong>  </li>
<li>많은 스레드가 생기면 자원 소모가 큼</li>
</ul>
</li>
<li><strong>실제 예시</strong>  <ul>
<li><strong>리눅스, 윈도우, 현대 JVM, Python, POSIX Thread</strong>  <blockquote>
<p>예) 웹 서버에서 100개 요청을 각각 스레드로 처리할 때<br>일부 스레드가 DB I/O로 대기해도,<br>나머지 스레드는 그대로 요청을 계속 처리 (진짜 병렬성)</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-멀티레벨-스레드-multi-level-thread-mn-모델">3. 멀티레벨 스레드 (Multi-level Thread, M:N 모델)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d88d0571-1fa5-4841-bf71-f9b49c016d5c/image.png" alt=""></p>
<ul>
<li><strong>정의</strong><br>여러 개의 사용자 스레드(M)가 여러 개의 커널 스레드(N)에 유연하게 매핑되는 구조</li>
<li><strong>동작</strong><br>언어 런타임/스케줄러가 수많은 사용자 스레드를 소수의 커널 스레드에 매핑<br>커널 스레드가 I/O 등으로 블록되어도, <strong>다른 커널 스레드에서 남은 사용자 스레드를 계속 실행</strong><br>빠르게 움직여야 하는 작업(짧은 계산 등)은 사용자 스레드에서,<br>안정적이어야 하는 작업은 커널 스레드에서 처리</li>
<li><strong>장점</strong>  <ul>
<li>사용자 스레드의 빠른 전환/생성,<br>커널 스레드의 병렬 처리 및 I/O 대기 문제 해결  </li>
<li>자원 효율적, 초대규모 동시성에 유리</li>
</ul>
</li>
<li><strong>단점</strong>  <ul>
<li>내부 스케줄러 구현이 복잡, OS/런타임의 지원이 필요  </li>
<li>커널 스레드와 연동 시 여전히 약간의 오버헤드 발생</li>
</ul>
</li>
<li><strong>실제 예시</strong>  <ul>
<li><strong>Go 언어(Goroutine + worker thread), Solaris, 일부 UNIX</strong><blockquote>
<p>예) Go 서버에서 10만 개 Goroutine을 수천 개 커널 스레드에 자동 분산<br>일부 Goroutine이 I/O로 멈춰도,<br>나머지는 계속 다른 커널 스레드에서 실행(진짜 대규모 동시성)</p>
</blockquote>
</li>
<li>최신 런타임 환경</li>
</ul>
</li>
</ul>
<hr>
<h2 id="커널-스레드11와-멀티레벨mn의-차이-실전에서-어떤-차이가-있을까">커널 스레드(1:1)와 멀티레벨(M:N)의 차이, 실전에서 어떤 차이가 있을까?</h2>
<ul>
<li><p><strong>커널 스레드(1:1)</strong>  </p>
<ul>
<li>애플리케이션에서 만든 사용자 스레드 수 = 커널 스레드 수  </li>
<li>운영체제가 직접 각 스레드를 &quot;독립적&quot;으로 스케줄링  </li>
<li>I/O 블록에도 나머지 스레드는 그대로 실행  </li>
<li>단, 스레드 수가 많아지면 운영체제 커널에서 직접 관리해야 하므로<br><strong>메모리 사용량, 컨텍스트 스위칭 오버헤드가 매우 커질 수 있음</strong></li>
<li>대규모 동시접속이 필요한 환경에서는 비효율적</li>
</ul>
</li>
<li><p><strong>멀티레벨(M:N)</strong>  </p>
<ul>
<li>애플리케이션에서 10만 개 스레드를 만들어도<br>실제 운영체제(커널)에는 수백~수천 개 스레드만 등록됨  </li>
<li><strong>언어 런타임/스케줄러가 내부적으로 사용자 스레드와 커널 스레드 간 매핑 관리</strong>  </li>
<li>I/O로 일부 커널 스레드가 대기 상태가 되어도,<br>다른 커널 스레드가 남은 사용자 스레드를 계속 실행  </li>
<li><strong>운영체제의 오버헤드는 적고, 대규모 동시성에 특화</strong>  </li>
<li>대표적으로 Go(Goroutine), Erlang, Solaris Thread 등에서 사용</li>
</ul>
</li>
</ul>
<hr>
<h3 id="실전-요약-표">실전 요약 표</h3>
<table>
<thead>
<tr>
<th>모델</th>
<th>대표 기술/언어</th>
<th>I/O 블록 영향</th>
<th>멀티코어 활용</th>
<th>초대규모 동시성</th>
</tr>
</thead>
<tbody><tr>
<td>사용자 스레드(1:N)</td>
<td>Green Thread(구자바)</td>
<td>전체 멈춤</td>
<td>×</td>
<td>×</td>
</tr>
<tr>
<td>커널 스레드(1:1)</td>
<td>Linux, Java, Python</td>
<td>영향 없음(독립적)</td>
<td>○</td>
<td>△(자원부담)</td>
</tr>
<tr>
<td>멀티레벨(M:N)</td>
<td>Go, Solaris, Erlang</td>
<td>영향 적음(자동분산)</td>
<td>◎</td>
<td>◎</td>
</tr>
</tbody></table>
<hr>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4">완전히 정복하는 프로세스 vs 스레드</a></li>
<li><a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread">멀티 프로세스 vs 멀티 스레드 비교</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[놀라운 운영체제  #1 – 운영체제 ?]]></title>
            <link>https://velog.io/@losiento_nana/Welcome-to-OS-world</link>
            <guid>https://velog.io/@losiento_nana/Welcome-to-OS-world</guid>
            <pubDate>Sat, 12 Jul 2025 15:33:44 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EA%B0%9C%EC%9A%94">개요</a></li>
<li><a href="#%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EB%9E%80">운영체제란?</a></li>
<li><a href="#%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EC%97%AD%ED%95%A0">운영체제의 역할</a></li>
<li><a href="#%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C%EC%9D%98-%EB%AA%A9%ED%91%9C">운영체제의 목표</a></li>
<li><a href="#%EC%BB%A4%EB%84%90%EA%B3%BC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4">커널과 인터페이스</a></li>
<li><a href="#%EC%BB%A4%EB%84%90%EC%9D%98-%EC%A2%85%EB%A5%98">커널의 종류</a><ul>
<li><a href="#%EB%8B%A8%EC%9D%BC%ED%98%95-%EA%B5%AC%EC%A1%B0-%EC%BB%A4%EB%84%90">단일형 구조 커널</a></li>
<li><a href="#%EA%B3%84%EC%B8%B5%ED%98%95-%EA%B5%AC%EC%A1%B0-%EC%BB%A4%EB%84%90">계층형 구조 커널</a></li>
<li><a href="#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C-%EA%B5%AC%EC%A1%B0-%EC%BB%A4%EB%84%90">마이크로 구조 커널</a></li>
<li><a href="#%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%BB%A4%EB%84%90-vs-%EA%B3%84%EC%B8%B5%ED%98%95-%EC%BB%A4%EB%84%90">마이크로커널 vs 계층형 커널</a></li>
</ul>
</li>
<li><a href="#reference">Reference</a></li>
<li><a href="#%EB%8B%A4%EC%9D%8C-%EC%8B%9C%EB%A6%AC%EC%A6%88-%EC%95%88%EB%82%B4">다음 시리즈 안내</a></li>
</ul>
<hr>
<h2 id="개요">개요</h2>
<p>운영체제라고 하면 흔히 Window, Linux, Mac, Unix를 떠올릴 것이다. 맞는 말이다.
그렇다면 그런 운영체제들이 어떤 방식으로 동작하고, 어떻게 운영체제 위에서 우리의 서비스가 돌아가는 걸까? 쉽사리 대답하지 못할것이다. 또는 굳이 알고싶지 않을수도 있다.</p>
<p>“내가 백엔드 개발을 하는데, 운영체제를 굳이 깊게 알아야 할까?”
“어차피 서버도 클라우드에서 띄우고, 코드는 프레임워크가 다 처리해주는데 OS까지 알아야 할 필요가 있을까?”</p>
<p>아마 한 번쯤 이런 의문을 가져본 적이 있을 것이다.
나 역시 처음엔 “프로그래밍만 잘 하면 되지, OS는 굳이…”라는 생각을 했었다.
하지만 서비스가 커지고, 그리고 조금 더 효율적이고 안정적인 백엔드 서비스를 만들고 싶을수록
<strong>운영체제에 대한 이해가 단순한 ‘지식’이 아니라 ‘필수 역량’</strong>임을 깨닫게 된다.</p>
<p>개발자들이 구현하는 서비스들은 모두 <strong>운영체제</strong>위에서 실행된다.</p>
<p>서버가 동작하고, 프로세스와 스레드가 돌아가고, 메모리와 네트워크, 파일 시스템까지
결국엔 모든 동작이 OS의 관리 아래에서 이루어진다.</p>
<ul>
<li><p>서버가 갑자기 느려질 때, 어디서 병목이 생기는지 파악하려면?</p>
</li>
<li><p>메모리 누수, CPU 폭주, 파일 핸들 고갈 같은 문제를 디버깅하려면?</p>
</li>
<li><p>여러 요청을 동시에 처리하는 동시성 프로그래밍에서 안전하게 데이터 일관성을 보장하려면?</p>
</li>
</ul>
<p>이러한 고민이 드는 순간, 우리는 결국 운영체제의 원리와 한계에 맞닥뜨리게 된다.</p>
<p>그래서 백엔드 개발자라면 운영체제에 대한 깊이 있는 이해가 단순한 옵션이 아니라,
더 나은 서비스를 만들기 위한 필수 역량임을 점점 더 실감하게 된다.
이 시리즈에서는 바로 그 이유와 실제 현장에서 필요한 운영체제의 원리를 공부하며 하나씩 풀어가려 한다.</p>
<hr>
<h2 id="운영체제란">운영체제란?</h2>
<p>한마디로 <strong>컴퓨터 자원을 효율적으로 관리하는 소프트웨어</strong>이다.</p>
<p>운영체제는 컴퓨터를 관리하기 위해 기본적인 규칙이나 절차를 규정하여 컴퓨터 내의 모든 하드웨어와 응용 프로그램을 관리한다. 또한 사용자가 직접 자원에 접근하는것을 막음으로서, 컴퓨터의 자원을 보호한다. 대신 사용자가 자원을 이용할 수 있는 다양한 인터페이스를 지원한다.</p>
<hr>
<h2 id="운영체제의-역할">운영체제의 역할</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/8a2b302e-7513-4da4-aaab-9c09b217e271/image.png" alt=""></p>
<h4 id="1-자원관리"><strong>1. 자원관리</strong></h4>
<ul>
<li>CPU 스케줄링: 여러 프로그램(프로세스/스레드)들이 CPU를 효율적으로 나눠 쓰도록 관리</li>
<li>메모리 관리: 여러 프로그램이 안전하게 메모리를 쓰도록 할당 및 회수, 가상 메모, 메모리 보호등</li>
<li>디스크/파일 관리: 파일 시스템, 데이터 저장 및 읽기, 디스크 공간 분배 등</li>
<li>입출력(I/O)관리: 네트워크, 디바이스 등 하드웨어와의 입출력 처리</li>
</ul>
<h4 id="2-자원보호"><strong>2. 자원보호</strong></h4>
<ul>
<li>프로세스 격리: 서로 다른 프로그램이 침범하지 못하게 보호</li>
<li>권한 관리: 사용자별/프로세스별 접근 제어</li>
<li>시스템 보안: 악의적인 접근, 해킹, 데이터 손상 방지</li>
</ul>
<h4 id="3-하드웨어-인터페이스-제공"><strong>3. 하드웨어 인터페이스 제공</strong></h4>
<ul>
<li>드라이버 관리: 실제 하드웨어와 소프트웨어가 통신할 수 있게 중간다리 역할</li>
<li>추상화: 복잡한 하드웨어 동작을 쉽고 일관성 있게 다루도록 제공</li>
</ul>
<h4 id="4-사용자-인터페이스-제공"><strong>4. 사용자 인터페이스 제공</strong></h4>
<ul>
<li>쉘/커맨드라인: 사용자가 시스템을 제어할 수 있는 환경 제공</li>
<li>시스템 콜: 응용프로그램(백엔드 서버 등)에서 OS의 기능을 호출할 수 있도록 API 제공</li>
</ul>
<hr>
<h2 id="운영체제의-목표">운영체제의 목표</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b4207a77-7f08-408c-b7d7-aaec899d26e3/image.png" alt=""></p>
<h4 id="효율성">효율성</h4>
<ul>
<li>자원을 효율적으로 관리하는 것</li>
<li>같은 자원으로 더 많은 작업량 처리하거나, 같은 작업량 처리하는 데 더 적은 자원 사용하는 것</li>
</ul>
<h4 id="안정성">안정성</h4>
<ul>
<li>작업을 안정적으로 처리하는 것</li>
<li>사용자와 응용 프로그램의 안전 문제와 하드웨어적인 보안 문제 처리</li>
<li>시스템에 문제 발생시 이전으로 복구하는 결함 포용 기능 수행</li>
</ul>
<h4 id="확장성">확장성</h4>
<ul>
<li>다양한 시스템 자원을 컴퓨터에 추가/제거하기 편리한 것</li>
</ul>
<h4 id="편리성">편리성</h4>
<ul>
<li>사용자가 편리하게 작업할 수 있는 환경 제공하는 것</li>
</ul>
<hr>
<h2 id="커널과-인터페이스">커널과 인터페이스</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0cb12108-88c8-4f8d-a104-9e895df94fb2/image.png" alt=""></p>
<h3 id="커널">커널</h3>
<ul>
<li>프로세스 관리, 메모리 관리, 저장장치 관리와 같은 운영체제의 핵심적인 기능들을 모아놓은 것</li>
</ul>
<h3 id="인터페이스">인터페이스</h3>
<ul>
<li><p>커널에 사용자의 명령을 전달하고 실행 결과를 사용자에게 알려주는 역할</p>
</li>
<li><p>그래픽을 사용한 인터페이스를 GUI(Graphical User Interface)라 부름</p>
</li>
<li><p>쉽게 말해 <strong>커널은 운영체제의 핵심적인 기능의 집합체</strong>이고, <strong>인터페이스는 그 커널과 사용자들을 이어주는 중간 다리</strong>와 같다고 생각하면 될 것 같다.</p>
</li>
</ul>
<hr>
<h2 id="커널의-종류">커널의 종류</h2>
<p>과거와 오늘날 사용되는 혹은 사용 됐던 커널들에 대해 간단히 소개하겠다.</p>
<h3 id="단일형-구조-커널">단일형 구조 커널</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/67cb4833-ee29-400c-aff3-202730d3bbed/image.png" alt=""></p>
<ul>
<li>단일형 구조 커널은 초창기 운영체제의 구조로 사용됨</li>
<li>커널의 핵심 기능을 구현하는 모듈들을 구분없이 하나로 구성함</li>
<li>모듈 간의 통신 비용이 줄어들어 효율적으로 운영이 가능하지만, 모든 모듈이 하나로 묶여 있기 때문에 버그나 오류를 처리하기가 어렵고, 상호 의존성이 높아서 기능상의 작은 결함이 시스템 전체로 확살 될 수 있다.</li>
</ul>
<hr>
<h3 id="계층형-구조-커널">계층형 구조 커널</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/7c8fe528-2e90-4586-87d1-309525b03ee6/image.png" alt=""></p>
<ul>
<li>계층형 구조 커널은 비슷한 기능을 가진 모듈을 묶어서 하나의 계층으로 만들고 계층 간의 통신을 통해 운영체제를 구현하는 방식이다.</li>
<li>마이크로소프트 윈도우 및 오늘날의 대부분의 운영체제에서 사용한다.</li>
</ul>
<hr>
<h3 id="마이크로-구조-커널">마이크로 구조 커널</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b0fe5cfe-9f7c-41ad-87ac-93a289b462e0/image.png" alt=""></p>
<ul>
<li><p>마이크로 구조(micro architecture) 커널은 프로세스 관리, 메모리 관리, 프로세
스 간 통신 관리 등 가장 기본적인 기능만 제공하고 나머지 기능(드라이버,파일 시스템,네트워크 등)은 별도의 프로세스(서버)로 분리해서 운영한다.</p>
</li>
<li><p>커널의 각 모듈은 세분화되어 존재하고 모듈 간의 정보 교환은 프로세스 간 통
신을 이용하여 이루어짐</p>
</li>
<li><p>iOS, macOS, watchOS 등 애플 생태계에서 사용되는 운영체제의 기반 구조</p>
</li>
</ul>
<hr>
<h3 id="마이크로커널-vs-계층형-커널">마이크로커널 vs 계층형 커널</h3>
<table>
<thead>
<tr>
<th></th>
<th>마이크로커널</th>
<th>계층형 커널</th>
</tr>
</thead>
<tbody><tr>
<td>구조</td>
<td>최소 핵심 기능만 커널에, 나머진 분리</td>
<td>여러 계층으로 커널 구조화</td>
</tr>
<tr>
<td>확장/유지보수</td>
<td>뛰어남</td>
<td>비교적 쉬움</td>
</tr>
<tr>
<td>성능</td>
<td>다소 느릴 수 있음</td>
<td>계층이 많으면 느려질 수 있음</td>
</tr>
<tr>
<td>안정성</td>
<td>매우 높음</td>
<td>계층별 분리로 높음</td>
</tr>
<tr>
<td>예시</td>
<td>QNX, MINIX 등</td>
<td>초기 UNIX, THE OS 등</td>
</tr>
</tbody></table>
<p>즉 둘 다 안전성과 유지보수성을 높이려는 시도지만, 구조적 철학이 다르다고 생각하면 될 것 같다.</p>
<hr>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://product.kyobobook.co.kr/detail/S000200716007">쉽게 배우는 운영체제</a></li>
</ul>
<hr>
<h2 id="다음-시리즈-안내">다음 시리즈 안내</h2>
<p>운영체제의 더 깊은 개념, 주요 동작 원리, 백엔드 개발자 실무와 연결되는 다양한 내용을<br>앞으로 이어질 시리즈 글들에서 자세히 정리할 예정입니다.<br>각 항목별 심화 내용이 궁금하다면 아래 목차나 &#39;운영체제&#39; 시리즈의 다음 글들을 참고해주세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #6 – NoSQL]]></title>
            <link>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-NoSQL</link>
            <guid>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-NoSQL</guid>
            <pubDate>Wed, 09 Jul 2025 10:19:13 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>앞선 데이터베이스 시리즈에서 관계형 데이터베이스(RDBMS)의 구조와 특징,<br>그리고 실무에서 데이터베이스를 사용할 때 고려해야 할 보안 이슈들을 살펴봤습니다.</p>
<p>이번 글에서는 한 걸음 더 나아가,<br>최근 빅데이터, 실시간 처리, 대용량 트래픽 환경에서 주목받는 <strong>NoSQL 데이터베이스</strong>를 조명하고,<br>기존 SQL(RDBMS)과는 어떻게 다르고,<br>실제 서비스 개발에서 어떤 상황에 적합한지 비교해보고자 합니다.</p>
<p>NoSQL은 기존 RDBMS와는 전혀 다른 방식의 데이터 저장 구조와 확장성을 제공하지만,<br>그만큼 장단점도 뚜렷합니다.<br>이 글을 통해 NoSQL의 대표적인 저장 방식과 특징,<br>그리고 SQL과의 차이점, 선택 기준을 정리해<br>실제 서비스나 프로젝트에서 상황에 따라<br>어떤 데이터베이스가 더 적합할지 <strong>직접 비교하고 결정할 수 있도록 내용을 구성했습니다.</strong></p>
<h2 id="1-nosql이란">1. NoSQL이란?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/543306ec-04d5-4165-8a5d-2b76f7faee14/image.png" alt=""></p>
<h3 id="nosql이란-">NoSQL이란 ?</h3>
<p>Not Only SQL 혹은 Non-Relational Operational DataBase의 약자로 <strong>비관계형 데이터베이스</strong>를 지칭합니다.</p>
<p>기존에 앞선 글에서 주로 다뤘던 관계형 데이터베이스와 같은 관계형 데이터 모델과 달리,<br>대량의 분산된 <strong>비정형 데이터를 저장하고 조회하는 데 특화</strong>된 데이터베이스 모델입니다.</p>
<p>그렇다면 <strong>비정형 데이터</strong>란 무엇을 말하는 걸까요?<br>예를 들어,  </p>
<ul>
<li>소셜 미디어의 피드(글, 댓글, 좋아요 등 다양한 형태)  </li>
<li>사용자 로그(접속 기록, 행동 이력 등)  </li>
<li>센서 데이터(IoT에서 발생하는 다양한 구조의 데이터)  </li>
<li>이미지, 동영상, JSON, XML 등<br>처럼, 고정된 테이블 구조가 아니라 다양한 구조나 형태를 가진 데이터를 의미합니다.</li>
</ul>
<hr>
<h3 id="등장-배경">등장 배경</h3>
<p>초기 데이터베이스 시스템(RDBMS)은 은행, 회계 등 정형 데이터(테이블 형태)에 매우 강점을 보였습니다.<br>하지만 현대의 웹 서비스, 빅데이터 환경에서는  </p>
<ul>
<li>데이터 양이 폭발적으로 증가하고  </li>
<li>구조가 자주 변경되거나,  </li>
<li>다양한 형식의 데이터가 유입되는<br>새로운 요구가 등장했습니다.</li>
</ul>
<p>이러한 변화에 유연하게 대응하고,<br>확장성과 성능을 확보하기 위해<br>NoSQL 데이터베이스가 등장하게 되었습니다.</p>
<h2 id="2-nosql의-주요-특징">2. NoSQL의 주요 특징</h2>
<h3 id="1-스키마-유연성">1) <strong>스키마 유연성</strong></h3>
<ul>
<li>NoSQL DB는 <strong>정해진 스키마(테이블 구조)</strong> 없이 데이터를 저장할 수 있습니다.<ul>
<li>새로운 속성이 추가되거나, 데이터 구조가 바뀌어도 기존 데이터를 수정하지 않고 바로 저장할 수 있습니다.</li>
</ul>
</li>
<li><strong>빠르게 변화하는 서비스 요구사항</strong>에 대응하기 좋고,<br>스타트업이나 프로토타입 개발 등에서 구조 변경에 대한 부담이 매우 적습니다.</li>
</ul>
<hr>
<h3 id="2-대규모-데이터-처리-및-확장성">2) <strong>대규모 데이터 처리 및 확장성</strong></h3>
<ul>
<li>NoSQL은 <strong>수평적 확장(Scale-Out)에 최적화</strong>되어 있습니다.<ul>
<li>NoSQL의 경우 설계 자체가 여러 대의 서버에서 데이터 분산/병렬 처리에 최적화 되어         있습니다.</li>
<li>서버(노드)를 여러 대 추가해서 용량과 처리량을 늘릴 수 있습니다.</li>
<li>반면, 전통적인 RDBMS는 주로 수직적 확장(Scale-Up, 즉 더 좋은 서버로 업그레이드)에 의존하는 경우가 많습니다.</li>
</ul>
</li>
<li><strong>빅데이터 환경</strong>이나 <strong>트래픽이 급격하게 증가하는 서비스</strong>에 매우 유리합니다.<ul>
<li>대용량 데이터 저장, 대량의 읽기/쓰기 요청을 분산해서 처리</li>
</ul>
</li>
</ul>
<h3 id="수평적-확장-vs-수직적-확장">수평적 확장 vs 수직적 확장</h3>
<h4 id="✅-수직적-확장scale-up이란">✅ 수직적 확장(Scale-Up)이란?</h4>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/de0f8359-9d51-4616-9a9e-666754ef9cb1/image.png" alt=""></p>
<ul>
<li><p><strong>정의:</strong><br>하나의 서버(노드)에 더 좋은 CPU, 더 많은 메모리, 빠른 디스크 등 <strong>더 강력한 하드웨어</strong>로 업그레이드하는 방식</p>
</li>
<li><p><strong>특징:</strong></p>
<ul>
<li>시스템 아키텍처 변경 없이 성능 향상 가능</li>
<li>구축과 관리가 비교적 단순</li>
<li>하드웨어의 한계(물리적 한계, 비용)가 있음</li>
</ul>
</li>
<li><p><strong>예시:</strong></p>
<ul>
<li>8GB RAM 서버 → 32GB RAM 서버로 교체</li>
<li>4코어 → 32코어 CPU로 교체</li>
</ul>
</li>
<li><p><strong>단점:</strong></p>
<ul>
<li>한계에 도달하면 더 이상 확장 불가</li>
<li>고성능 장비일수록 <strong>가격이 기하급수적으로 비싸짐</strong></li>
<li>서버 다운 시 서비스 전체가 중단될 위험이 큼 (Single Point of Failure)</li>
</ul>
</li>
</ul>
<hr>
<h4 id="✅-수평적-확장scale-out이란">✅ 수평적 확장(Scale-Out)이란?</h4>
<ul>
<li><p><strong>정의:</strong><br>여러 대의 서버(노드)를 추가해서 시스템 전체 용량과 처리량을 늘리는 방식</p>
</li>
<li><p><strong>특징:</strong></p>
<ul>
<li>비교적 저렴한 서버 여러 대로 확장 가능</li>
<li>필요에 따라 <strong>서버 추가/제거가 유연</strong></li>
<li>대규모 데이터/트래픽에 강함</li>
<li>분산 환경 설계가 필요(샤딩 등)</li>
</ul>
</li>
<li><p><strong>예시:</strong></p>
<ul>
<li>8GB RAM 서버 1대 → 8GB RAM 서버 10대</li>
<li>데이터베이스 샤딩, 클러스터링, 분산 파일 시스템 등</li>
</ul>
</li>
<li><p><strong>장점:</strong></p>
<ul>
<li><strong>확장성</strong>이 거의 무한 (서버를 계속 추가)</li>
<li>장애 발생 시 일부 노드만 영향(가용성↑)</li>
<li>클라우드 환경에서 효과적(자동 스케일링 등)</li>
</ul>
</li>
<li><p><strong>단점:</strong></p>
<ul>
<li>분산 시스템 설계, 데이터 일관성 유지 등이 복잡</li>
<li>조인, 트랜잭션 등 구현 난이도↑</li>
</ul>
</li>
</ul>
<hr>
<p><strong>표로 정리</strong></p>
<table>
<thead>
<tr>
<th></th>
<th>수직적 확장(Scale-Up)</th>
<th>수평적 확장(Scale-Out)</th>
</tr>
</thead>
<tbody><tr>
<td>방법</td>
<td>서버 성능 업그레이드</td>
<td>서버 대수 추가</td>
</tr>
<tr>
<td>한계</td>
<td>하드웨어 물리적 한계</td>
<td>이론상 무한 확장 가능</td>
</tr>
<tr>
<td>비용</td>
<td>기하급수적으로 증가</td>
<td>개별 서버는 저렴, 총합 조절</td>
</tr>
<tr>
<td>장애</td>
<td>단일 장애 지점(SPOF)</td>
<td>일부 노드만 영향</td>
</tr>
<tr>
<td>대표</td>
<td>전통적 RDBMS</td>
<td>NoSQL, 분산 DB, 클라우드</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-다양한-저장-모델-지원">3) 다양한 저장 모델 지원</h3>
<ul>
<li>NoSQL은 한 가지 형태의 데이터만 저장하는 것이 아니라,<br><strong>서비스의 특성에 맞는 다양한 저장 모델</strong>을 선택할 수 있습니다.<ul>
<li>Key-Value형: 단순한 데이터 캐싱/세션 관리 등 (예: Redis)</li>
<li>Document형: 복잡한 구조의 데이터를 유연하게 저장 (예: MongoDB)</li>
<li>Column형: 대규모 분석, 타임시리즈 데이터 (예: Cassandra)</li>
<li>Graph형: 복잡한 관계/연결 데이터(예: 친구관계, 추천 등) (예: Neo4j)</li>
</ul>
</li>
<li>데이터의 <strong>특성/용도에 따라 최적의 모델을 선택</strong>할 수 있는 점이 큰 장점입니다.</li>
<li>다양한 저장 모델에 대해서는 아래에서 다시 다루도록 하겠습니다.</li>
</ul>
<hr>
<h2 id="3-nosql의-분류와-저장-방식">3. NoSQL의 분류와 저장 방식</h2>
<h3 id="1-key-value-database">1) <strong>Key-Value Database</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/af29a740-e9d4-456e-b8a8-1bbfde5194f0/image.png" alt=""></p>
<p><strong>Key-Value 형식의 특징</strong></p>
<ul>
<li><p>키와 벨류로만 이루어진, 저장과 조회라는 가장 간단한 원칙에 충실한 데이터 베이스이다.</p>
</li>
<li><p>key의 값은 중복이 안된다.</p>
</li>
<li><p>key값을 통해 value를 찾을수 있다.</p>
</li>
<li><p>In-Memory 저장 방식이다.</p>
</li>
<li><p>Key-value형태이므로 조회 속도가 빠르다.</p>
</li>
<li><p>key-value형 구조는 아래와 같은 형태의 애플리케이션에서 주로 사용된다.</p>
</li>
</ul>
<ol>
<li>성능 향상을 위해 관계형 데이터베이스에서 데이터 캐싱</li>
<li>장바구니 같은 웹 애플리케이션에서 일시적인 속성 추적</li>
<li>모바일 애플리케이션용 사용자 데이터 정보와 구성 정보 저장</li>
<li>이미지나 오디오 파일 같은 대용량 객체 저장</li>
</ol>
<p><strong>대표적인 key-value Database</strong></p>
<ul>
<li>Redis</li>
<li>Riak</li>
</ul>
<hr>
<h3 id="2-document형">2) <strong>Document형</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/92a7c634-baf6-435a-8a67-e74155c98cf1/image.png" alt=""></p>
<p><strong>Document 형식의 특징</strong></p>
<ul>
<li>Key-Value 구조에서 Value 부분이 단순 데이터가 아니라 <strong>문서(Document)</strong> 자체입니다.</li>
<li>이 문서는 보통 <strong>JSON, BSON, XML</strong>과 같은 반정형(Semi-structured) 데이터 형식으로 저장됩니다.</li>
<li><strong>문서마다 저장되는 필드(구조)가 달라도 됨</strong> → 스키마가 유연함!</li>
<li>중첩 구조, 배열 등 복잡한 데이터 구조를 한 번에 저장 가능.</li>
<li>다양한 필드 기준으로 <strong>검색/필터링/정렬</strong> 등 쿼리가 가능함.</li>
<li>하나의 문서에 실제 서비스의 “게시글”, “주문”, “사용자 정보” 등 복잡한 데이터를 통째로 담을 수 있음.</li>
</ul>
<p><strong>Document DB는 아래와 같은 애플리케이션에서 주로 사용됩니다</strong></p>
<ol>
<li>JSON 데이터를 그대로 저장/조회하는 웹·모바일 백엔드</li>
<li>구조가 자주 바뀌거나, 다양한 속성이 존재하는 데이터 관리</li>
<li>복잡하게 중첩된(비정규화된) 데이터를 사용하는 서비스</li>
<li>로그 데이터, 이벤트 데이터, 메타데이터 저장</li>
</ol>
<p><strong>대표적인 Document Database</strong></p>
<ul>
<li>MongoDB</li>
<li>CouchDB</li>
<li>Couchbase</li>
</ul>
<p><strong>Key-Value형과 Document형의 차이점</strong></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Key-Value DB (Redis)</th>
<th>Document DB (MongoDB)</th>
</tr>
</thead>
<tbody><tr>
<td>저장 구조</td>
<td>key : value (값은 단순 데이터/문자열)</td>
<td>key : document(복잡한 JSON/문서)</td>
</tr>
<tr>
<td>스키마 유연성</td>
<td>단순 (key와 value만)</td>
<td>매우 유연 (문서마다 구조 다름)</td>
</tr>
<tr>
<td>검색/조회</td>
<td>key로만 조회 (O(1) 속도)</td>
<td>key + document 내 필드로도 검색</td>
</tr>
<tr>
<td>중첩/복합 구조</td>
<td>불가 (value에 배열/객체 직접X)</td>
<td>가능 (JSON 구조 그대로 저장/검색)</td>
</tr>
<tr>
<td>예시</td>
<td>로그인 세션 저장, 캐시</td>
<td>게시글, 상품 정보, 주문 내역</td>
</tr>
</tbody></table>
<hr>
<p><strong>Document형 과 RDBMS의 차이</strong></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Document형(DB)</th>
<th>RDBMS (관계형 DB)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>저장 구조</strong></td>
<td>Collection(=Table)에 Document(=Row) 단위로 JSON/BSON 등 반정형 데이터 저장</td>
<td>Table에 Row, Column 정형 데이터 저장</td>
</tr>
<tr>
<td><strong>스키마</strong></td>
<td><strong>유연/동적</strong>: 문서마다 구조(필드) 달라도 됨</td>
<td><strong>엄격/정적</strong>: 컬럼/타입 고정, 구조 변경 어려움</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td><strong>수평 확장(Sharding) 용이</strong></td>
<td>주로 수직 확장(Scale up) 선호</td>
</tr>
<tr>
<td><strong>관계 표현</strong></td>
<td>JOIN 없음 (중첩, 비정규화, 데이터 복제)</td>
<td><strong>JOIN 지원</strong>: 테이블 간 관계 표현 용이</td>
</tr>
<tr>
<td><strong>데이터 구조</strong></td>
<td>중첩/배열/복잡 데이터 1문서에 모두 저장 가능</td>
<td>각 데이터는 테이블/행/열에 분산 저장</td>
</tr>
<tr>
<td><strong>트랜잭션</strong></td>
<td>단일 문서 단위 ACID(최근에는 멀티문서 지원 일부)</td>
<td><strong>강력한 ACID 트랜잭션</strong> 지원</td>
</tr>
<tr>
<td><strong>검색/조회</strong></td>
<td>key, document 내 필드 등 다양한 기준으로 빠른 조회</td>
<td>복잡한 SQL, 다양한 조건/조인 지원</td>
</tr>
<tr>
<td><strong>변경/확장</strong></td>
<td>구조, 필드 변경 쉽고 빠름</td>
<td>구조 변경 제약 많고, 마이그레이션 부담</td>
</tr>
<tr>
<td><strong>적합 분야</strong></td>
<td>로그, 유저 정보, 메타데이터, 구조 자주 바뀌는 서비스</td>
<td>금융, ERP, 이커머스, 강한 무결성/정합성 필요 서비스</td>
</tr>
<tr>
<td><strong>예시</strong></td>
<td>MongoDB, CouchDB 등</td>
<td>MySQL, MariaDB, Oracle, PostgreSQL 등</td>
</tr>
</tbody></table>
<hr>
<h3 id="예시로-보는-document형">예시로 보는 Document형</h3>
<pre><code class="language-json">{
  &quot;_id&quot;: &quot;user_1001&quot;,          // ← key
  &quot;name&quot;: &quot;홍길동&quot;,
  &quot;email&quot;: &quot;hong@naver.com&quot;,
  &quot;age&quot;: 31,
  &quot;roles&quot;: [&quot;admin&quot;, &quot;user&quot;],
  &quot;profile&quot;: {
    &quot;city&quot;: &quot;Seoul&quot;,
    &quot;joined&quot;: &quot;2024-01-12&quot;
  }
}</code></pre>
<hr>
<h3 id="3-column열기반-형">3) <strong>Column(열기반) 형</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/374037e1-6e0c-4eaa-b31d-a272bef1ead0/image.png" alt=""></p>
<p><strong>Column(열기반) 형식의 특징</strong></p>
<ul>
<li>한 컬럼(열)별로 데이터를 연속 저장
(ex: 이름만, 점수만 한 줄로 쭉 저장)</li>
<li>데이터의 압축률이 높음 → 저장 공간 효율, I/O 성능 개선</li>
<li>일부 컬럼만 빠르게 조회 가능 → 대용량 데이터 분석/통계에 강점</li>
<li>수평 확장(분산) 구조를 쉽게 지원</li>
</ul>
<p><strong>Column형 구조는 아래와 같은 형태의 애플리케이션에서 주로 사용된다</strong></p>
<ol>
<li>대량의 로그, 이벤트 데이터, IoT 센서 데이터 등 빠른 집계/분석이 필요한 서비스</li>
<li>BI(비즈니스 인텔리전스), 데이터 웨어하우스, 리포팅 시스템</li>
<li>수백만~수억건의 데이터에서 특정 컬럼(필드)만 추출해서 통계/분석</li>
</ol>
<p><strong>대표적인 Column Database</strong></p>
<ul>
<li>Apache HBase</li>
<li>Apache Cassandra</li>
<li>Amazon Redshift</li>
</ul>
<hr>
<h3 id="예시로-보는-column형">예시로 보는 Column형</h3>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>score</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Alice</td>
<td>23</td>
<td>85</td>
</tr>
<tr>
<td>2</td>
<td>Bob</td>
<td>25</td>
<td>92</td>
</tr>
<tr>
<td>3</td>
<td>Carol</td>
<td>22</td>
<td>78</td>
</tr>
</tbody></table>
<p>이러한 데이터가 있다고 가정하자</p>
<p>예를들어 행기반 RDBMS의 저장 형태는</p>
<pre><code class="language-data">[1, Alice, 23, 85], [2, Bob, 25, 92], [3, Carol, 22, 78]
</code></pre>
<p>한 <strong>Row(행)</strong>이 통째로 한 블록에 저장된다.
OLTP(온라인 트랜잭션)에 적합하다.</p>
<p>반대로 컬럼(행) 기반 데이터베이스가 저장되는 구조는</p>
<pre><code class="language-data">id:   [1, 2, 3]
name: [Alice, Bob, Carol]
age:  [23, 25, 22]
score:[85, 92, 78]</code></pre>
<p>같은 컬럼끼리 묶어서 저장된다.
OLAP(온라인 분석처리)에 적합하다.</p>
<hr>
<h3 id="4-graph그래프-database">4) <strong>Graph(그래프) Database</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/018f5051-c39a-403a-9d47-2fa573a8aeae/image.png" alt=""></p>
<p><strong>Graph(그래프) 형식의 특징</strong></p>
<ul>
<li>데이터(노드, Node)와 데이터 간의 관계(간선, Edge)를 그래프 구조로 저장</li>
<li><strong>노드(Node)</strong>: 사람, 장소, 제품 등 실제 엔터티(객체)를 나타냄</li>
<li><strong>간선(Edge)</strong>: 노드 간의 “관계”를 표현 (예: 친구, 팔로우, 구매함 등)</li>
<li>각 노드/간선은 다양한 속성(필드, Property)을 가질 수 있음</li>
<li><strong>관계 기반 쿼리</strong><br>(ex: &quot;A와 B가 얼마나 가까운가?&quot;, &quot;누가 누구의 친구의 친구인가?&quot;)에 최적화</li>
<li>관계를 빠르게 탐색하고, 복잡한 네트워크 분석에 강점</li>
</ul>
<p><strong>그래프형 구조는 아래와 같은 형태의 애플리케이션에서 주로 사용된다</strong></p>
<ol>
<li>소셜 네트워크 (예: 친구 관계, 팔로우 관계)</li>
<li>추천 시스템 (ex: “이 사람이 이 상품도 구매했음”)</li>
<li>조직 구조, 계층 구조, 인맥 분석</li>
<li>사기 탐지(Fraud Detection)</li>
<li>경로 탐색 (ex: 네비게이션, 배송 경로 최적화)</li>
</ol>
<p><strong>대표적인 Graph Database</strong></p>
<ul>
<li>Neo4j</li>
<li>Amazon Neptune</li>
<li>TigerGraph</li>
<li>ArangoDB (멀티모델 지원)</li>
</ul>
<hr>
<h3 id="예시로-보는-graph-형">예시로 보는 Graph 형</h3>
<p>(소셜 네트워크에서의 친구 관계 데이터)</p>
<p><strong>노드</strong>:</p>
<ul>
<li>UserA (id: 1, name: &quot;Alice&quot;)</li>
<li>UserB (id: 2, name: &quot;Bob&quot;)</li>
<li>UserC (id: 3, name: &quot;Carol&quot;)</li>
</ul>
<p><strong>간선</strong>:</p>
<ul>
<li>Alice –[FRIEND]→ Bob</li>
<li>Bob –[FRIEND]→ Carol</li>
<li>Carol –[FRIEND]→ Alice</li>
</ul>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/1aa6a25e-0aa5-4eaf-8452-f00336b5e130/image.png" alt=""></p>
<hr>
<h2 id="4-sql-vs-nosql-실제로-무엇이-다를까">4. SQL vs NoSQL: 실제로 무엇이 다를까?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/4a49a242-01e5-4815-852f-f205563d80fc/image.png" alt=""></p>
<p>그렇다면 관계형 데이터베이스와 비관계형 데이터베이스는 서로 어떤 장단점을 갖고 있을까?
<strong>실질적으로 두 시스템이 어떻게 다르고, 어떤 상황에서 더 적합</strong>한지 4가지 핵심 기준을 중심으로 정리 해보았다.</p>
<h3 id="1-데이터-모델--스키마-유연성">1) <strong>데이터 모델 / 스키마 유연성</strong></h3>
<h4 id="sqlrdbms"><strong>SQL(RDBMS)</strong></h4>
<ul>
<li><strong>정해진 스키마(테이블 구조)</strong>에 따라 데이터를 저장해야 한다.<ul>
<li>테이블을 생성할 때 각 컬럼(필드)의 데이터 타입과 구조를 미리 정의.</li>
<li>데이터의 일관성과 무결성을 <strong>DBMS 차원에서 강제</strong>함.</li>
</ul>
</li>
<li>구조가 바뀌면(컬럼 추가/삭제 등) 테이블 스키마를 변경하는 명령이 필요하고,<br>데이터 마이그레이션이 번거로울 수 있음.</li>
</ul>
<h4 id="nosql"><strong>NoSQL</strong></h4>
<ul>
<li><strong>스키마가 고정되어 있지 않거나 매우 유연</strong>하다.<ul>
<li>Document DB는 문서마다 구조(필드, 타입 등)가 달라도 저장 가능.</li>
<li>Key-Value/Column/Graph 등 각기 다른 구조를 지원.</li>
</ul>
</li>
<li>데이터 구조가 자주 바뀌는 서비스, 빠른 프로토타이핑, 다양한 데이터 포맷에 특히 유리함.</li>
<li>하지만, <strong>DB 자체에서 무결성 보장 기능이 약할 수 있음</strong> (애플리케이션에서 관리 필요).</li>
</ul>
<hr>
<h3 id="2-확장성scalability">2) <strong>확장성(Scalability)</strong></h3>
<h4 id="sqlrdbms-1"><strong>SQL(RDBMS)</strong></h4>
<ul>
<li><strong>수직적 확장(Scale-Up)</strong>이 기본 전략.<ul>
<li>더 빠른 CPU, 더 많은 메모리/스토리지로 서버 성능을 올리는 방식.</li>
<li>단일 서버 성능에 의존 → 하드웨어 한계에 도달 시 확장 한계가 명확함.</li>
</ul>
</li>
<li><strong>샤딩 등으로 분산(Scale-Out)도 가능</strong>하지만, 구조가 복잡하고,<br>대부분의 전통적인 RDBMS는 수평 확장에 최적화되어 있지 않음.</li>
</ul>
<h4 id="nosql-1"><strong>NoSQL</strong></h4>
<ul>
<li>설계 자체가 <strong>수평적 확장(Scale-Out)에 최적화</strong>되어 있음.<ul>
<li>여러 대의 서버에 데이터 분산(샤딩, 클러스터링) 구조.</li>
<li>서버(노드)를 추가하면 처리량/용량이 실시간으로 거의 선형적으로 증가.</li>
</ul>
</li>
<li><strong>빅데이터, 대규모 트래픽, 클라우드 환경</strong>에서 탁월한 확장성 제공.</li>
</ul>
<hr>
<h3 id="3-일관성-및-트랜잭션consistency--transaction">3) <strong>일관성 및 트랜잭션(Consistency &amp; Transaction)</strong></h3>
<h4 id="sqlrdbms-2"><strong>SQL(RDBMS)</strong></h4>
<ul>
<li><strong>ACID</strong>(Atomicity, Consistency, Isolation, Durability)<br>트랜잭션을 <strong>엄격하게 보장</strong>한다.<ul>
<li>복잡한 거래, 이체, 주문 등 <strong>일관성이 반드시 필요한 시스템</strong>에 매우 적합.</li>
<li>다중 테이블 조인, 복잡한 롤백, 강한 일관성 유지 등 가능.</li>
</ul>
</li>
<li>데이터 무결성/정합성이 최우선인 경우(예: 은행, 회계) 표준 선택지.</li>
</ul>
<h4 id="nosql-2"><strong>NoSQL</strong></h4>
<ul>
<li>대부분 <strong>BASE</strong>(Basically Available, Soft state, Eventual consistency)<br>원칙을 따른다.<ul>
<li><strong>완화된 일관성(최종적 일관성)</strong>을 제공하는 경우가 많다.</li>
<li>즉, 데이터가 모든 노드에 “즉시” 동일하게 반영되지 않고,<br>일정 시간이 지난 후 일관성이 맞춰질 수 있음.</li>
</ul>
</li>
<li>트랜잭션/일관성이 아예 없는 것은 아니나,<br><strong>단일 문서/객체 수준의 트랜잭션</strong>을 지원하는 것이 일반적.</li>
<li>최신 NoSQL 중에서는 <strong>멀티-문서 트랜잭션 지원</strong>이 늘고 있으나,<br>복잡도와 성능의 trade-off가 존재함.</li>
</ul>
<hr>
<h3 id="4-적합한-사용-사례환경">4) <strong>적합한 사용 사례/환경</strong></h3>
<h4 id="sqlrdbms가-적합한-경우"><strong>SQL(RDBMS)가 적합한 경우</strong></h4>
<ul>
<li>데이터 구조가 명확하고, 변하지 않는 경우(예: 회원관리, 회계)</li>
<li>트랜잭션, 강한 일관성, 무결성이 중요한 금융, ERP, 주문/결제 시스템</li>
<li>복잡한 쿼리, 다중 테이블 조인, 집계 등 관계형 데이터 처리</li>
<li>데이터를 오래, 안정적으로 보관해야 하는 핵심 업무 시스템</li>
</ul>
<h4 id="nosql이-적합한-경우"><strong>NoSQL이 적합한 경우</strong></h4>
<ul>
<li>데이터 구조가 자주 바뀌거나, 다양한 데이터가 들어오는 경우(비정형, 반정형)</li>
<li>수평 확장이 꼭 필요한 빅데이터, 대규모 트래픽 서비스(예: 소셜 미디어, IoT, 로그분석)</li>
<li>빠른 개발, 프로토타이핑, 유연한 스키마 요구</li>
<li>분산 환경/클라우드에서 성능과 가용성이 최우선인 서비스</li>
<li>“관계”가 중요한 경우(그래프 DB, 추천, 인맥 등)</li>
</ul>
<hr>
<h3 id="5-nosql의-장점단점-포함-요약-표">5) <strong>NoSQL의 장점/단점 포함 요약 표</strong></h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>SQL(RDBMS)</th>
<th>NoSQL</th>
</tr>
</thead>
<tbody><tr>
<td><strong>스키마</strong></td>
<td>고정, 명확(변경 번거로움)</td>
<td>유연/동적(변경 용이)</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td>주로 수직적(Scale-Up)</td>
<td>수평적(Scale-Out)</td>
</tr>
<tr>
<td><strong>일관성</strong></td>
<td>강한 일관성, ACID 보장</td>
<td>최종적 일관성, BASE(가벼운 트랜잭션)</td>
</tr>
<tr>
<td><strong>쿼리</strong></td>
<td>복잡한 쿼리, 조인, 집계 가능</td>
<td>단순 쿼리, 조인 약함, 집계는 제한적</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>적은 데이터/OLTP에 최적, 느려질 수 있음</td>
<td>대량 데이터/OLAP에 강점, 분산 성능</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>확장 한계, 스키마 유연성 부족</td>
<td>복잡한 관계, 트랜잭션 한계</td>
</tr>
<tr>
<td><strong>대표 사례</strong></td>
<td>금융, ERP, 전통적 엔터프라이즈 시스템</td>
<td>소셜, 빅데이터, IoT, 로그, 캐시</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-cap-이론과-nosql">5. CAP 이론과 NoSQL</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/6933be37-a5f0-4f51-a7ca-70a328f9bc3a/image.png" alt=""></p>
<p>분산 데이터베이스를 이해할 때 꼭 알아야 하는 이론이 바로 <strong>CAP 이론</strong>입니다.
해당 내용에 대한 좀 더 자세한 내용을 확인 할 수 있는 링크는 밑에 남겨두겠습니다.</p>
<p><strong>CAP</strong>이란  </p>
<ul>
<li><strong>Consistency(일관성)</strong>  </li>
<li><strong>Availability(가용성)</strong>  </li>
<li><strong>Partition Tolerance(분할 허용성)</strong><br>세 가지 속성의 약자로,<br>&quot;분산 시스템에서는 이 세 가지 중 두 가지만 동시에 완벽하게 만족시킬 수 있고,<br>세 가지를 모두 만족하는 건 불가능하다&quot;는 내용을 담고 있습니다.</li>
</ul>
<h3 id="cap-이론을-쉽게-풀이하면">CAP 이론을 쉽게 풀이하면?</h3>
<ul>
<li><p><strong>일관성(Consistency)</strong>  </p>
<ul>
<li>어떤 노드에 데이터를 저장하든, 즉시 모든 노드에서 <strong>항상 같은 데이터</strong>를 조회할 수 있다.</li>
<li>예) A에서 잔고를 변경하면, B에서도 바로 그 결과가 보여야 함</li>
</ul>
</li>
<li><p><strong>가용성(Availability)</strong>  </p>
<ul>
<li><strong>모든 요청에 반드시 응답</strong>해야 한다.  </li>
<li>일부 데이터가 최신이 아닐 수 있지만, 서비스가 멈추지 않아야 한다.</li>
<li>예) 한쪽 서버가 죽어도, 남은 서버가 항상 응답</li>
</ul>
</li>
<li><p><strong>분할 허용성(Partition Tolerance)</strong>  </p>
<ul>
<li><strong>네트워크 장애 등으로 노드 간 통신이 끊겨도</strong> 시스템 전체가 동작해야 한다.</li>
<li>예) 서버 간 연결이 끊겨도 각각 독립적으로 서비스 유지</li>
</ul>
</li>
</ul>
<hr>
<h3 id="현실-세계에서는">현실 세계에서는?</h3>
<ul>
<li>실제 분산 시스템(특히 NoSQL)은 <strong>P(Partition Tolerance)</strong>는 반드시 가져가야 합니다.  </li>
<li>그래서 보통 아래 두 가지 중 하나를 선택합니다.</li>
</ul>
<table>
<thead>
<tr>
<th>유형</th>
<th>특성</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CP (일관성+분할 허용)</strong></td>
<td>데이터 정합성이 가장 중요, 가용성은 잠시 희생할 수 있음</td>
<td>MongoDB, HBase</td>
</tr>
<tr>
<td><strong>AP (가용성+분할 허용)</strong></td>
<td>서비스 중단보단, 일관성은 나중에 맞추더라도 즉시 응답</td>
<td>Cassandra, DynamoDB</td>
</tr>
</tbody></table>
<hr>
<h4 id="비유로-정리하자면"><strong>비유로 정리하자면?</strong></h4>
<ul>
<li><p><strong>CP 시스템</strong>:  
“동기화가 될 때까지 잠시 기다려서라도 항상 같은 결과를 보여주자”<br>→ ex) 금융 서비스, 은행</p>
</li>
<li><p><strong>AP 시스템</strong>:  
“잠깐 데이터가 달라도 일단 서비스부터 계속 응답!”<br>→ ex) 소셜 서비스, 로그 저장, 알림 등</p>
</li>
</ul>
<hr>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://www.oracle.com/kr/autonomous-database/what-is-graph-database/">그래프 데이터베이스란?</a></li>
<li><a href="https://chaarlie.tistory.com/674">[DB] 열 기반 데이터베이스 vs 행 기반 데이터베이스</a></li>
<li><a href="https://www.analyticsvidhya.com/blog/2020/08/a-beginners-guide-to-cap-theorem-for-data-engineering/">A Beginner’s Guide to CAP Theorem for Data Engineering</a></li>
<li><a href="https://bbaktaeho-95.tistory.com/108">Document DB</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #6 – SQL Injection]]></title>
            <link>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-6-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B3%B4%EC%95%88%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Tue, 08 Jul 2025 03:22:20 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EA%B0%9C%EC%9A%94">개요</a></li>
<li><a href="#sql-injection%EC%9D%B4%EB%9E%80">SQL Injection이란?</a><ul>
<li><a href="#sql-injection-%EC%8B%A4%EC%A0%84-%EA%B3%B5%EA%B2%A9-%EC%98%88%EC%8B%9C">SQL Injection 실전 공격 예시</a><ul>
<li><a href="#%EA%B0%95%EC%A0%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-1%EB%B2%88">강제로그인 1번</a></li>
<li><a href="#%EA%B0%95%EC%A0%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-2%EB%B2%88">강제로그인 2번</a></li>
<li><a href="#union-select%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B3%B5%EA%B2%A9">Union Select를 활용한 공격</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#sql-injection-%EC%98%88%EB%B0%A9%EB%B2%95">SQL Injection 예방법</a><ul>
<li><a href="#statement-vs-preparedstatement">Statement vs PreparedStatement</a><ul>
<li><a href="#statement%EC%A0%95%EC%A0%81-%EC%BF%BC%EB%A6%AC">Statement(정적 쿼리)</a></li>
<li><a href="#propared-statement%EB%8F%99%EC%A0%81-%EC%BF%BC%EB%A6%AC">Propared Statement(동적 쿼리)</a></li>
</ul>
</li>
<li><a href="#stored-procedure">Stored Procedure</a></li>
<li><a href="#view%EB%A1%9C-%EC%A0%91%EA%B7%BC-%EA%B6%8C%ED%95%9C-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0">View로 접근 권한 분리하기</a></li>
<li><a href="#ormobject-relational-mapping">ORM(Object Relational Mapping)</a></li>
</ul>
</li>
<li><a href="#db-%EA%B6%8C%ED%95%9C-%EB%B0%8F-%EA%B3%84%EC%A0%95-%EA%B4%80%EB%A6%AC">DB 권한 및 계정 관리</a><ul>
<li><a href="#%EC%B5%9C%EC%86%8C-%EA%B6%8C%ED%95%9C%EC%9D%98-%EC%9B%90%EC%B9%99principle-of-least-privilege">최소 권한의 원칙(Principle of Least Privilege)</a></li>
<li><a href="#db-%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC%ED%9A%8C%EC%88%98-%EC%98%88%EC%8B%9C-%EC%A0%95%EB%A6%AC">DB 권한 부여/회수 예시 정리</a></li>
</ul>
</li>
<li><a href="#%EC%A0%95%EB%A6%AC">정리</a></li>
<li><a href="#sql-injection-%EA%B3%B5%EA%B2%A9%EC%97%90-%EC%82%AC%EC%9A%A9%EB%90%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8">SQL Injection 공격에 사용된 사이트</a></li>
<li><a href="#reference">Reference</a></li>
</ul>
<hr>
<h1 id="개요">개요</h1>
<p>데이터베이스 시리즈에서 이미 스프레드시트보다 DBMS를 사용해야 하는 여러 가지 이유를 다뤘습니다.
이번에는 한 걸음 더 나아가, DBMS를 사용할 때 반드시 주의해야 할 점,
특히 데이터베이스 보안에 대해 이야기하고자 합니다.</p>
<p>요즘 대부분의 웹 서버는 필수적으로 데이터베이스를 사용하고 있습니다.
하지만, 이러한 데이터베이스를 노리는 악의적인 공격자들도 많아졌고,
SQL Injection 같은 악의적인 쿼리 삽입,
권한 없는 사용자의 불필요한 데이터 접근,
개발자의 코드 미숙 등으로 인한 보안 취약점이 실무 현장에서도 여전히 자주 발생하고 있습니다.</p>
<p>이번 글에서는
데이터베이스 보안의 대표적인 위험과 그에 대한 방어 기법을
공격/방어 예시와 함께 구체적으로 정리해보았습니다.</p>
<hr>
<h2 id="sql-injection이란">SQL Injection이란?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0472a8ad-cc2f-46a5-9da5-1dcbc2408bb4/image.jpg" alt=""></p>
<figcaption style="text-align:center; font-size:15px; color:#808080; margin-top:40px">
    출처:backhoe
  </figcaption>


<p>SQL Injection이란 응용 프로그램 보안 상의 허점을 의도적으로 이용해, 악의적인 SQL문을 실행되게 함으로써 데이터베이스를 비정상적으로 조작하는 코드 인젝션 공격 방법입니다.</p>
<blockquote>
<p><strong>핵심 원리:</strong>  </p>
<ul>
<li><strong>사용자가 입력한 데이터가 그대로 쿼리에 들어가서</strong></li>
<li><strong>의도하지 않은 SQL 명령</strong>을 실행할 수 있게 만듭니다.</li>
</ul>
</blockquote>
<hr>
<h3 id="sql-injection-실전-공격-예시">SQL Injection 실전 공격 예시</h3>
<blockquote>
<p>⚠️ 본 실습은 <strong>해킹 연습용으로 허가된 사이트(해킹 실습 플랫폼)</strong>에서만 진행했습니다.
실제 운영 중인 웹사이트에서는 절대로 따라 하시면 안 됩니다!</p>
<p>🛑 <strong>비인가된 사이트에서의 해킹 시도는 불법이며, 법적 처벌을 받을 수 있습니다.</strong><br>항상 윤리적 해커로서, 올바른 환경에서만 연습해 주세요.</p>
</blockquote>
<hr>
<p>자 제가 잠깐동안 해커가 되어서 여러분의 소중한 은행 계좌에 접근해 돈을 빼보겠습니다.</p>
<ol>
<li><p>일단 로그인을 시도 해보지만, 아이디 비밀번호를 모르니 성공 할 리가 없죠.
<img src="https://velog.velcdn.com/images/losiento_nana/post/51ba9e59-b24a-4789-ab56-fdfff4257536/image.png" alt=""></p>
</li>
<li><p>이때 아이디에 &#39;(따옴표)를 넣어서 한번 다시 시도 해봤습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/d611b4aa-4420-4822-9208-3e4eef4d012a/image.png" alt=""></p>
</li>
</ol>
<p>이렇게 했더니 로그인 처리에 관해 이상한 에러 메시지를 웹상에서 드러냈습니다.
딱 봐도 SQL 에러 코드인거 같은데 왜 이런 현상이 나왔을까요?</p>
<hr>
<p>그 이유는 개발자가 관게형 데이터베이스를 활용해 아이디와 비밀번호를 받을때</p>
<pre><code class="language-sql">SELECT * FROM accounts
WHERE id = &#39;유저가 입력한 id&#39; AND pw = &#39;유저가 입력한 pw&#39;</code></pre>
<p>이렇게 받는데,</p>
<p>위에서 제가 로그인 할때 사용했던 aaa&#39;를 넣게 된다면 어떻게 될까요?
<strong>내부에서 실행되는 쿼리</strong></p>
<pre><code class="language-sql">SELECT * FROM accounts
WHERE id = &#39;aaa&#39; &#39; AND pw = &#39;****&#39;</code></pre>
<p>이렇게 쿼리문이 실행되면서 <strong>Sysntax error</strong>를 터트리게 됩니다.</p>
<p>자 그럼 이 사이트는 <strong>SQL Injection이 가능한 사이트</strong>라고 판단이 됩니다.
(뒤에 이유 설명)</p>
<hr>
<h2 id="강제로그인-1번"><strong>강제로그인 1번</strong></h2>
<p>다시 로그인 페이지로 넘어와서 이번에는 이렇게 입력해보겠습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/81b576ea-ca16-4079-b49f-39ed807928df/image.png" alt=""></p>
<blockquote>
<p>SQL에서 &#39;--&#39;는 뒤에 문장을 주석 처리 하겠다는 의미입니다.</p>
</blockquote>
<p>tuser이란 사람이 있으면 그 즉시 로그인을 진행하게 만들고 비밀번호를 확인 하는 쿼리문은 모두 주석처리가 됩니다.</p>
<ul>
<li><strong>내부에서 실행되는 쿼리</strong><pre><code class="language-sql">SELECT * FROM accounts
WHERE id = &#39;tuser -- &#39; AND pw = &#39;****&#39;</code></pre>
</li>
</ul>
<p><strong>성공</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/d3a1e605-fb33-484b-ad49-c23fcfb9af6b/image.png" alt=""></p>
<hr>
<h2 id="강제로그인-2번"><strong>강제로그인 2번</strong></h2>
<p>이번에는 다른 방법을 활용해 로그인을 해보겠습니다. 
<img src="https://velog.velcdn.com/images/losiento_nana/post/480e4ab2-cdb7-4cf6-907e-881dc32e0590/image.png" alt=""></p>
<blockquote>
<p>OR 1=1은 <strong>항상 참(True)</strong>입니다.</p>
</blockquote>
<p>이렇게 되면 예를들어 이렇게 데이터베이스에 tuser이란 회원 정보가 들어있다고 가정하면 Select * 조건에 만족하는 모든 행을 가져올겁니다.</p>
<p>이후 대부분의 로그인 로직에서는 아래와 같이 회원을 반환하는 로직을 작성합니다.</p>
<pre><code class="language-sql">if (rs.next()) {
   // 로그인 성공 처리
}</code></pre>
<p><strong>즉, username이 tuser가 아니더라도,
WHERE 조건을 만족하는 첫 번째(맨 위) 회원의 정보로 로그인 처리가 됩니다.</strong></p>
<ul>
<li>만약 테이블이 이렇게 되어 있다면? (실제로도 admin 또는 test 계정을 가장 상단에서 생성하곤 합니다.)**
<img src="https://velog.velcdn.com/images/losiento_nana/post/38778505-11e1-45dc-825e-8dfa958859e3/image.png" alt=""></li>
</ul>
<hr>
<p><strong>Admin 계정 로그인 성공</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/972bdc70-ac26-480c-a011-d5ee9e8a4120/image.png" alt=""></p>
<p>자 이제 어드민 계정으로 1억 달러 정도 제 계좌로 보내겠습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/c9c0ea1c-324f-45d3-9b54-d598a35ce56e/image.png" alt=""></p>
<p><del>이제 저는 부자가 되었습니다. 그 동안 벨로그를 읽어주셔서 감사합니다.</del>
<img src="https://velog.velcdn.com/images/losiento_nana/post/d66e3dbb-c59b-4e94-b244-ee4a668344e6/image.png" alt=""></p>
<hr>
<h2 id="union-select를-활용한-공격">Union Select를 활용한 공격</h2>
<p>이번에는 Union select 문법을 활용해서 다른 사이트에서 SQL Injection 공격을 진행 해보겠습니다.</p>
<p>해당사이트는 쿼리 파라미터에 작가의 id값을 넣어서 작가를 조회하는 방식으로 운영되는 방식인것을 미리 확인 했습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/4f551e84-9509-4959-8805-f32eba2b0088/image.png" alt=""></p>
<p>그렇다면 작가 번호란에 Union Select문을 넣어서 SQL Injection 공격을 해보겠습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/35d88ed7-73b8-4b88-8759-23134d9e2f0b/image.png" alt=""></p>
<p>짜잔 이렇게 해당 사이트의 어떤 유저의 아이디와 비밀번호를 훔치는데 성공 했습니다.
<img src="https://velog.velcdn.com/images/losiento_nana/post/3de1724c-b3f5-4136-9ab0-e508cec439f1/image.png" alt=""></p>
<blockquote>
<p><strong>어떻게 된 일 일까요?</strong></p>
</blockquote>
<hr>
<p>아마 해당 사이트는 작가 이름의 검색 쿼리를 이렇게 작성 했을겁니다.</p>
<pre><code class="language-sql">SELECT * FROM 어쩌구
WHERE artis= &#39; &#39;</code></pre>
<p>이때 제가 url에 넣은 유니온 쿼리를 넣어서 다시 풀 쿼리로 보겠습니다.</p>
<pre><code class="language-sql">SELECT * FROM 어쩌구
WHERE artist=-1 UNION
SELECT 1,uname,pass FROM users
WHERE uname =  &#39;test&#39;</code></pre>
<p>해당 쿼리를 해석해보면 </p>
<ul>
<li><p>author_no = -1 조건이므로 원래 테이블에서는 결과가 없음(혹은 무의미한 결과)</p>
</li>
<li><p>UNION 키워드는 두 SELECT의 결과를 합쳐서 반환
SELECT 1, uname, pass FROM users WHERE uname = &#39;test&#39;</p>
</li>
<li><p>1은 첫 번째 컬럼(숫자, 자리 맞추기용)</p>
</li>
<li><p>두 번째/세 번째 컬럼에 users 테이블의 uname(아이디), pass(비밀번호)</p>
</li>
</ul>
<blockquote>
<p>이때 select문 두개의 컬럼 두개가 같아야 합니다. 그래서 컬럼 갯수를 강제로 맞추는 노가다로 진행해야 합니다.</p>
</blockquote>
<hr>
<p><strong>훔친 아이디와 비밀번호로 로그인 해보기</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/4a52017e-3eec-44eb-904d-5b48d77795f6/image.png" alt=""></p>
<p><strong>성공~</strong></p>
<hr>
<p>이제 공격법을 봤으니 그에 맞춰서 예방법을 알아보도록 하겠습니다.</p>
<h2 id="sql-injection-예방법">SQL Injection 예방법</h2>
<h3 id="statement-vs-proparedstatement">Statement vs ProparedStatement</h3>
<p>먼저 결론부터 말하자면 ProparedStatement 방식으로 쿼리를 작성해야 합니다.</p>
<hr>
<h3 id="statement정적-쿼리">Statement(정적 쿼리)</h3>
<ul>
<li><strong>Statement</strong>는 SQL 쿼리문을 문자열(String)로 직접 만들어 실행하는 방식입니다.</li>
<li>주로 아래와 같이 사용합니다.</li>
</ul>
<pre><code class="language-java">String sql = &quot;SELECT * FROM users WHERE username=&#39;&quot; + username + &quot;&#39; AND password=&#39;&quot; + password + &quot;&#39;&quot;;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);</code></pre>
<ul>
<li>쿼리문을 코드에서 문자열로 직접 조합한다.</li>
<li>사용자 입력값이 쿼리문에 직접 삽입 됩니다.</li>
</ul>
<p>이런 방식으로 코드를 작성하면 위에서 다룬 예시처럼 SQL Injection 공격에 취약한 구조가 됩니다.</p>
<hr>
<h3 id="propared-statement동적-쿼리">Propared Statement(동적 쿼리)</h3>
<ul>
<li><p><strong>PreparedStatement는 쿼리문을 미리 컴파일하고, 실행 시 파라미터(값)를 따로 바인딩하는 방식입니다.</strong></p>
</li>
<li><p>주로 아래와 같이 사용합니다.</p>
<pre><code class="language-java">String sql = &quot;SELECT * FROM users WHERE username=? AND password=?&quot;;
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();</code></pre>
</li>
</ul>
<p>이렇게 동적 쿼리문을 사용하면 <strong>쿼리문과 데이터(입력값)을 명확하게 분리해서 처리</strong> 합니다. </p>
<ul>
<li>PreparedStatement는 SQL 쿼리의 구조(SELECT ... WHERE username=? AND password=?)를 먼저 데이터베이스에 전달해 &quot;미리 컴파일&quot;.</li>
<li>이후에 사용자가 입력한 값은 별도로 바인딩되어, 쿼리의 구조에 영향을 주지 않고 &quot;순수한 값&quot;으로만 전달됨</li>
<li>즉, 설령 입력값에 악의적인 SQL 코드가 섞여 있더라도, 이 값은 단순한 문자열로 취급될 뿐, 쿼리 자체를 변조하지 못함.</li>
</ul>
<hr>
<h3 id="stored-procedure">Stored procedure</h3>
<ul>
<li><strong>Stored Procedure(저장 프로시저)</strong>는 자주 사용하는 SQL 쿼리 로직을 데이터베이스에 미리 저장해두고, 필요할 때마다 호출해서 실행하는 방식이다.</li>
</ul>
<pre><code class="language-sql">-- 저장 프로시저 정의 (MySQL 예시)
CREATE PROCEDURE get_user_by_name(IN username VARCHAR(50))
BEGIN
    SELECT * FROM users WHERE username = username;
END;</code></pre>
<pre><code class="language-java">// Java에서 호출 예시
CallableStatement cstmt = conn.prepareCall(&quot;{call get_user_by_name(?)}&quot;);
cstmt.setString(1, username);
ResultSet rs = cstmt.executeQuery();</code></pre>
<ul>
<li>이렇게 함수 형식으로 미리 쿼리에 들어올 값과 리턴될 값을 나눠서 정의하고 입력란으로 들어오는 값을 매개인자로 넣어주며 마치 함수처럼 처리하는 방식입니다. </li>
<li>다만, 저장 프로시저 내부에서 문자열을 조합하거나 동적으로 쿼리를 만드는 경우에는 여전히 SQL Injection 위험이 있을 수 있으므로, <strong>항상 프로시저 내에서도 파라미터 바인딩을 지키는것이 중요합니다.</strong></li>
</ul>
<hr>
<h3 id="view로-접근-권한-분리하기">View로 접근 권한 분리하기</h3>
<ul>
<li><strong>View</strong>는 데이터베이스에서 자주 사용하는 쿼리 결과를 가상의 테이블 형태로 미리 정의해 놓은 객체입니다.</li>
<li>View를 활용하면, 민감 정보나 특정 컬럼만을 노출하는 테이블을 만들어서<br>애플리케이션 또는 사용자 계정별로 볼 수 있는 데이터를 제한할 수 있습니다.</li>
</ul>
<pre><code class="language-sql">CREATE VIEW user_public_view AS
SELECT id, name, email FROM users;

-- 일반 유저 계정에만 뷰 접근 권한 부여
GRANT SELECT ON user_public_view TO &#39;normal_user&#39;@&#39;%&#39;;
-- 원본 테이블(users)에는 권한 부여하지 않기
REVOKE ALL PRIVILEGES ON users FROM &#39;normal_user&#39;@&#39;%&#39;;</code></pre>
<p>즉 테이블을 볼 수 있는 계정에 제한을 걸어주는 방법입니다. 계정 권한에 대한 내용은 아래에서 다시 다루도록 하겠습니다.</p>
<hr>
<h3 id="ormobject-relational-mapping">ORM(Object Relational Mapping)</h3>
<ul>
<li><p><strong>ORM</strong>이란 객체 지향 언어(예: Java)에서 데이터베이스의 데이터를 객체로 매핑해서 다루게 해주는 기술입니다.
대표적으로 Java에서는 JPA, Hibernate, MyBatis(Mapper) 등을 사용합니다.</p>
</li>
<li><p>ORM을 사용하면 직접 SQL 쿼리를 작성하지 않고,
메서드 호출이나 JPQL(Java Persistence Query Language) 등으로 데이터를 조회/저장할 수 있습니다.</p>
</li>
</ul>
<pre><code class="language-java">// 예시: JPA Repository를 이용한 사용자 조회
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    Optional&lt;User&gt; findByUsernameAndPassword(String username, String password);
}

// 실제 서비스 코드에서
Optional&lt;User&gt; user = userRepository.findByUsernameAndPassword(username, password);</code></pre>
<ul>
<li><p>이처럼 JPA의 메서드 파라미터에 사용자 입력값을 전달하면,</p>
</li>
<li><p><em>JPA 내부에서 쿼리문과 값(파라미터)을 명확히 분리해서 데이터베이스에 전달합니다.*</em></p>
</li>
<li><p>동적 쿼리문과 마찬가지로 실제 입력값이 쿼리에 직접 삽입 되는 것이 아니라, 내부적으로 동적쿼리처럼 쿼리와 파라미터가 분리되어서 처리됩니다.</p>
</li>
</ul>
<hr>
<h2 id="db-권한-및-계정-관리">DB 권한 및 계정 관리</h2>
<p>데이터베이스의 계정과 권한 관리는 실무에서 가장 기본적이면서도 중요한 보안 요소입니다.
적절한 권한 분리와 관리 없이는, 내부자에 의한 정보 유출이나 실수, 외부 공격 시 대규모 피해로 이어질 수 있습니다.</p>
<hr>
<h3 id="최소-권한의-원칙principle-of-least-privilege">최소 권한의 원칙(Principle of Least Privilege)</h3>
<ul>
<li><p>각 DB 계정에는 업무상 꼭 필요한 최소한의 권한만을 부여해야 합니다.</p>
</li>
<li><p>예를 들어, 단순 조회 서비스라면 SELECT만, 데이터 입력 서비스라면 INSERT/UPDATE만, 운영 자동화 계정에는 DDL 권한을 부여하지 않는 것이 안전합니다.</p>
</li>
<li><p><strong>DDL(테이블 구조 변경), DCL(권한 부여/회수) 권한은 반드시 관리자 계정에만 부여 해야 합니다.</strong></p>
</li>
</ul>
<pre><code class="language-sql">-- 조회 전용 계정
GRANT SELECT ON mydb.* TO &#39;readonly_user&#39;@&#39;%&#39;;

-- 데이터 입력/수정만 가능한 계정
GRANT INSERT, UPDATE, DELETE ON mydb.orders TO &#39;order_manager&#39;@&#39;%&#39;;

-- 필요 없는 권한은 반드시 회수
REVOKE ALL PRIVILEGES ON mydb.* FROM &#39;readonly_user&#39;@&#39;%&#39;;
</code></pre>
<hr>
<h3 id="db-권한-부여회수-예시-정리">DB 권한 부여/회수 예시 정리</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>SQL 예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>조회 권한 부여</td>
<td><code>GRANT SELECT ON mydb.* TO &#39;readonly_user&#39;@&#39;%&#39;;</code></td>
<td>전체 DB(mydb) 조회만 가능</td>
</tr>
<tr>
<td>데이터 입력/수정</td>
<td><code>GRANT INSERT, UPDATE ON mydb.orders TO &#39;order_manager&#39;@&#39;%&#39;;</code></td>
<td>특정 테이블(orders)에 입력/수정만 가능</td>
</tr>
<tr>
<td>DDL 권한 부여</td>
<td><code>GRANT CREATE, ALTER ON mydb.* TO &#39;admin_user&#39;@&#39;%&#39;;</code></td>
<td>DB 구조 변경 권한(테이블 생성/수정 등) 부여</td>
</tr>
<tr>
<td>권한 회수</td>
<td><code>REVOKE ALL PRIVILEGES ON mydb.* FROM &#39;readonly_user&#39;@&#39;%&#39;;</code></td>
<td>모든 권한 회수</td>
</tr>
<tr>
<td>특정 권한 회수</td>
<td><code>REVOKE INSERT, UPDATE ON mydb.orders FROM &#39;order_manager&#39;@&#39;%&#39;;</code></td>
<td>테이블(orders) 입력/수정 권한만 회수</td>
</tr>
<tr>
<td>특정 뷰만 허용</td>
<td><code>GRANT SELECT ON user_public_view TO &#39;normal_user&#39;@&#39;%&#39;;</code></td>
<td>뷰(user_public_view)에만 SELECT 허용</td>
</tr>
<tr>
<td>원본 테이블 차단</td>
<td><code>REVOKE ALL PRIVILEGES ON users FROM &#39;normal_user&#39;@&#39;%&#39;;</code></td>
<td>users 테이블 직접 접근은 차단</td>
</tr>
</tbody></table>
<hr>
<h2 id="정리">정리</h2>
<ul>
<li>SQL 쿼리 작성 시에는 항상 파라미터 바인딩(PreparedStatement, ORM 등)을 기본 원칙으로 삼고,</li>
<li>각 계정에 꼭 필요한 최소한의 권한만 부여하며,</li>
<li>View, Stored Procedure 등 DB 자체 기능도 적극 활용해서 정보 노출을 최소화해야 합니다.</li>
</ul>
<hr>
<h2 id="sql-injection-공격에-사용된-사이트">SQL Injection 공격에 사용된 사이트</h2>
<p><a href="https://demo.testfire.net/login.jsp">주석 처리 공격</a>
<a href="http://testphp.vulnweb.com/artists.php">UNION SELECT 이용</a></p>
<hr>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=FoZ2cucLiDs&amp;t=216s">코딩애플의 SQL injection 공격</a></li>
<li><a href="https://aricode.tistory.com/12">[JDBC] PreparedStatement를 이용해 동적으로 SQL 처리하기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #5 – 교착상태(Deadlock)]]></title>
            <link>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-5-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadlock</link>
            <guid>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-5-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadlock</guid>
            <pubDate>Mon, 07 Jul 2025 12:01:43 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EA%B0%9C%EC%9A%94">개요</a></li>
<li><a href="#%EB%8D%B0%EB%93%9C%EB%9D%BDdeadlock%EC%9D%B4%EB%9E%80">데드락(Deadlock)이란?</a></li>
<li><a href="#%EC%99%9C-%EB%8D%B0%EB%93%9C%EB%9D%BD%EC%9D%B4-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94%EA%B0%80">왜 데드락이 발생하는가?</a></li>
<li><a href="#%EB%8D%B0%EB%93%9C%EB%9D%BD%EC%9D%84-%EC%8B%A4%EC%A0%84%EC%97%90-%EB%B9%84%EC%9C%A0">데드락을 실전에 비유</a></li>
<li><a href="#%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80">어떻게 해결해야 하는가?</a></li>
<li><a href="#%EC%8B%A4%EC%A0%9C-sql-%EC%98%88%EC%8B%9C-%EB%8D%B0%EB%93%9C%EB%9D%BD-%EC%8B%A4%ED%8C%A8">실제 SQL 예시 (데드락 실패)</a></li>
<li><a href="#%EC%8B%A4%EC%A0%9C-sql-%EC%98%88%EC%8B%9C-%EC%84%B1%EA%B3%B5-%EC%82%AC%EB%A1%80">실제 SQL 예시 (성공 사례)</a></li>
<li><a href="#%EB%8D%B0%EB%93%9C%EB%9D%BD-%ED%9A%8C%ED%94%BC-%EB%B0%A9%EB%B2%95">데드락 회피 방법</a><ul>
<li><a href="#%ED%83%80%EC%9E%84%EC%8A%A4%ED%83%AC%ED%94%84-%EA%B8%B0%EB%B0%98-%EB%B0%A9%EC%8B%9D">타임스탬프 기반 방식</a></li>
<li><a href="#wait-die--wound-wait-%EA%B8%B0%EB%B2%95">Wait-Die / Wound-Wait 기법</a></li>
</ul>
</li>
<li><a href="#%EB%8D%B0%EB%93%9C%EB%9D%BD-%EB%B9%88%EB%8F%84%EB%A5%BC-%EB%82%AE%EC%B6%94%EB%8A%94-%EC%8B%A4%EC%A0%84-%ED%8C%81">데드락 빈도를 낮추는 실전 팁</a></li>
<li><a href="#%EC%A0%95%EB%A6%AC">정리</a></li>
<li><a href="#reference">Reference</a></li>
</ul>
<hr>
<h1 id="개요">개요</h1>
<p>앞선 글 마지막에서 <strong>트랜잭션 관리 시 주의사항</strong>과 함께<br>&quot;두 개 이상의 트랜잭션이 서로 상대방의 락 해제를 기다리며<br>무한정 대기에 빠지는 현상&quot;인 <strong>데드락(Deadlock)</strong>을 간략히 소개했었다.</p>
<p>실제로 데이터베이스를 운영하다 보면<br>단순히 이론으로만 접했던 데드락이<br>실제 장애의 원인으로 자주 등장한다는 사실을 경험하게 됩니다.</p>
<p>특히 여러 사용자가 동시에 데이터를 수정하거나,<br>여러 트랜잭션이 복잡하게 엮이는 실시간 환경에서는<br>데드락이 발생할 가능성이 더욱 높아집니다.</p>
<p>이번 글에서는<br><strong>데드락이란 정확히 무엇이며,<br>왜 발생하고, 어떤 식으로 실무에서 문제를 일으키는지</strong><br>실제 SQL 실습 예시통해 직접 실험 해봤다.</p>
<blockquote>
<p>앞선 이론적 설명에서 한걸음 더 나아가<br><strong>&quot;실제로 마주치는 데드락 장애를 어떻게 다룰 것인가?&quot;</strong>에 집중합니다.</p>
</blockquote>
<hr>
<h2 id="데드락deadlock이란">데드락(Deadlock)이란?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/f9b12592-9aa0-41a4-b19b-fcb3304f1cb8/image.png" alt=""></p>
<figcaption style="text-align:center; font-size:15px; color:#808080; margin-top:40px">
    데드락 1초만에 이해가능
  </figcaption>


<ul>
<li><strong>데드락</strong>은 둘 이상의 트랜잭션이 서로 상대방이 점유한 자원을 기다리면서 무한 대기 상태에 빠지는 현상입니다.</li>
<li>즉, 각 트랜잭션이 서로의 ‘락 해제’를 기다리기 때문에, 아무도 작업을 진행하지 못합니다.</li>
<li>DBMS는 보통 한쪽 트랜잭션을 강제로 롤백시켜 해결하지만, 
빈번하게 발생하면 전체 서비스에 큰 영향을 미칠 수 있습니다.</li>
</ul>
<hr>
<h2 id="왜-데드락이-발생하는가">왜 데드락이 발생하는가?</h2>
<ul>
<li>동시에 여러 트랜잭션이 데이터의 행 또는 테이블에 락을 걸고, 
각각 자신이 필요한 락을 점유한 채 상대방이 점유한 락을 추가로 기다리면서 발생합니다.</li>
<li>예를 들어, 트랜잭션1이 A를 점유 후 B를 기다리고, 트랜잭션2가 B를 점유 후 A를 기다리는 구조에서 쉽게 발생합니다.</li>
</ul>
<hr>
<h2 id="데드락을-실전에-비유">데드락을 실전에 비유</h2>
<h3 id="🏢-회의실-열쇠-비유">🏢 회의실 열쇠 비유</h3>
<ul>
<li>두 명의 팀원이 각각 회의실1, 회의실2 열쇠를 먼저 들고, 
동시에 상대방이 가진 열쇠까지 필요해서 요청하면 둘 다 열쇠를 서로 줄 때까지 ‘무한정 대기’에 빠지는 상황입니다.</li>
<li>현실에서도 “서로가 끝내주기를 기다리며 아무도 진행하지 못하는” 회의/개발/업무 상황에 자주 비유됩니다.</li>
</ul>
<hr>
<h2 id="어떻게-해결해야-하는가">어떻게 해결해야 하는가?</h2>
<ul>
<li>DBMS는 <strong>데드락이 감지되면 한쪽 트랜잭션을 강제 롤백(Rollback)</strong> 시켜서 
더 이상 무한 대기가 이어지지 않게 만듭니다.</li>
<li>하지만 데드락이 잦으면 서비스 응답이 느려지거나 결제/입금 등 핵심 비즈니스 로직이 실패할 수 있으므로, 
가능한 한 원인을 줄이거나, 설계 단계에서 예방하는 것이 중요합니다.</li>
</ul>
<hr>
<h2 id="실제-sql-예시-데드락-실패">실제 SQL 예시 (데드락 실패)</h2>
<p>아래 쿼리는 MySQL 등에서 두 개의 세션을 이용해 직접 데드락 상황을 유도하는 기본 예시입니다. </p>
<blockquote>
<p>참고로 데드락 상황은 2개의 서로 다른 &quot;세션&quot;(=연결창, 터미널, 쿼리 실행창)에서 발생시켜야 합니다.</p>
</blockquote>
<p><strong>1. 실습용 테이블 생성 및 데이터 삽입</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/4f54da09-3368-454d-b076-128c5feb252e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/4a752f26-3519-45ac-ad2c-407d1723a569/image.png" alt=""></p>
<p><strong>2. 첫 번째 세션</strong></p>
<ul>
<li><p>id=1 행에 먼저 락을 획득하고 트랜잭션을 유지
<img src="https://velog.velcdn.com/images/losiento_nana/post/54089b5b-03e4-4fe7-83e6-64987878f143/image.png" alt=""></p>
</li>
<li><p>이후 다른 세션으로 id 2행에 대해 락을 획득하고 트랜잭션을 유지
<a href="https://velog.velcdn.com/images/losiento_nana/post/f96c64d5-57fd-4c1a-91d0-5b17848d2016/image.png"></a></p>
</li>
</ul>
<p><strong>3. 두 번째 세션</strong></p>
<ul>
<li><p>동시에 id=2 행에 대해 락을 획득하고 트랜잭션을 유지
<img src="https://velog.velcdn.com/images/losiento_nana/post/7443f960-b0ab-478f-ac37-10ad57b738e4/image.png" alt=""></p>
</li>
<li><p>이렇게 되면 첫 번째 세션이 id=2에, 두 번째 세션이 id=1에 상호 락 요청
두 세션 모두 서로의 락이 해제되길 무한 대기
<img src="https://velog.velcdn.com/images/losiento_nana/post/d54611a3-a2d5-4656-9587-7aa02770ce84/image.png" alt=""></p>
</li>
<li><p>이후 id= 2를 점유한 프로시저가 id = 1의 데이터를 요청할 시에</p>
</li>
<li><p>MySQL 등 대부분의 DBMS는 데드락을 감지하면,
둘 중 한 쪽 트랜잭션을 강제로 롤백하여 교착상태를 해소함
<img src="https://velog.velcdn.com/images/losiento_nana/post/b5786811-1600-45e3-8b24-71040e43e822/image.png" alt=""></p>
</li>
<li><p>에러 코드</p>
<pre><code class="language-sql">20:37:26    UPDATE deadlock_test SET value = value + 100 WHERE id = 2    Error Code: 2013. Lost connection to MySQL server during query    30.001 sec</code></pre>
</li>
</ul>
<blockquote>
<p>위의 상황에서 두 세션이 서로 락을 점유한 채 서로가 해제하기만 기다리므로,<br>DBMS가 자동으로 둘 중 하나를 롤백시켜 데드락을 해소합니다.</p>
</blockquote>
<hr>
<h2 id="실제-sql-예시-성공-사례">실제 SQL 예시 (성공 사례)</h2>
<p>트랜잭션의 <strong>락 점유 순서</strong>를 동일하게 맞추면 데드락이 발생하지 않습니다.</p>
<p><strong>1. 첫 번째 세션</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/c34d4a1a-660c-41d2-9b5a-0c91270acb34/image.png" alt=""></p>
<ul>
<li>먼저 id=1에 락을 건 뒤, id=2에 락을 걸고 모두 끝나면 COMMIT</li>
</ul>
<p><strong>2. 두 번째 세션 (첫 번째 세션이 끝난 후 시작)</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/eaee33be-c132-4acf-bc2f-6e80b729d511/image.png" alt=""></p>
<ul>
<li>SESSION 1이 완료된 후 같은 순서(id=1 → id=2)로 락을 걸고 COMMIT</li>
</ul>
<ul>
<li>모든 트랜잭션이 동일한 순서로만 자원을 점유하면,
DBMS가 순차적으로 락 점유를 대기하게 만들어 데드락이 발생하지 않게 처리해준다.</li>
</ul>
<blockquote>
<p><strong>핵심:</strong> 여러 트랜잭션이 동일한 자원에 접근할 때<br>“항상 같은 순서로 락을 걸면” 데드락이 발생하지 않습니다.</p>
</blockquote>
<hr>
<h2 id="데드락-회피-방법은-없는가">데드락 회피 방법은 없는가?</h2>
<h3 id="1-타임스탬프-기반-방식">1. <strong>타임스탬프 기반 방식</strong></h3>
<ul>
<li>트랜잭션이 시작될 때 OS 시각 또는 DB 내부 증가값(논리적 시간)으로 순번을 부여해 관리한다.</li>
<li>MySQL 등에서는 실제로도 내부적으로 Transaction ID(trx_id) 등을 사용해 트랜잭션을 구분한다.</li>
</ul>
<hr>
<h3 id="2-wait-die--wound-wait-기법">2. <strong>Wait-Die / Wound-Wait 기법</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/cfccd1eb-0b34-4d69-a81a-f2c0bd9ef9d0/image.png" alt=""></p>
<h4 id="wait-die-비선점">Wait-Die (비선점)</h4>
<ul>
<li>먼저 시작한 트랜잭션(번호가 낮음)은 <strong>기다릴 수 있음</strong>.</li>
<li>나중에 시작한 트랜잭션(번호가 높음)은 <strong>바로 롤백</strong>됨.</li>
</ul>
<h4 id="wound-wait-선점">Wound-Wait (선점)</h4>
<ul>
<li>먼저 시작한 트랜잭션은 <strong>무조건 기다리지 않고</strong>, 나중에 시작한 트랜잭션이 락을 잡고 있으면 <strong>바로 롤백</strong>시킴.</li>
</ul>
<blockquote>
<p>이 기법들은 DBMS 내부에서 자동 구현이 되어 있다.  </p>
</blockquote>
<hr>
<h2 id="데드락-빈도를-낮추는-방법">데드락 빈도를 낮추는 방법</h2>
<h3 id="1-트랜잭션을-자주-빠르게-커밋하라">1. <strong>트랜잭션을 자주, 빠르게 커밋하라</strong></h3>
<ul>
<li><p>트랜잭션을 오래 열어두면 락이 계속 유지되어 데드락 확률이 올라감.</p>
</li>
<li><p>각 작업(UPDATE, INSERT, DELETE)마다 바로 커밋하는 습관이 중요하다.</p>
</li>
<li><pre><code class="language-sql">START TRANSACTION
UPDATE accounts SET balance = balance - 100 WHERE id = 1
COMMIT```
</code></pre>
</li>
</ul>
<hr>
<h3 id="2-접근-순서를-항상-일관되게">2. <strong>접근 순서를 항상 ‘일관’되게</strong></h3>
<ul>
<li><p>모든 트랜잭션이 동일한 테이블/행에 항상 같은 순서(예: 오름차순)로 접근해야 한다.</p>
</li>
<li><p>순서가 꼬이면 &quot;서로 락을 잡고 대기&quot;하는 데드락이 발생할 수 있다.</p>
</li>
<li><pre><code class="language-sql">SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE```
</code></pre>
</li>
</ul>
<hr>
<h3 id="3-여러-행을-랜덤하게-갱신해야-한다면-테이블-락-사용">3. <strong>여러 행을 랜덤하게 갱신해야 한다면, 테이블 락 사용</strong></h3>
<ul>
<li><p>여러 row를 한 번에 업데이트할 때 WHERE 조건에 따라 순서가 예측 불가하면,
테이블 단위로 락을 걸고(LOCK TABLES) 처리하면 데드락을 예방할 수 있다.</p>
</li>
<li><p>단, 동시성은 다소 떨어질 수 있으니 꼭 필요한 경우에만 적용.</p>
</li>
<li><pre><code class="language-sql">LOCK TABLES items WRITE
UPDATE items SET stock = stock - 1 WHERE id IN (3, 8, 15)
UNLOCK TABLES```
</code></pre>
</li>
</ul>
<hr>
<h3 id="💡-정리">💡 <strong>정리</strong></h3>
<ul>
<li>락 순서 고정, 트랜잭션 시간 최소화, 빠른 커밋, 필요한 부분만 락, 인덱스 적극 활용 등으로 데드락은 줄이고, 시스템 안정성은 높일 수 있다.</li>
<li>위 전략만 잘 실천해도 대부분의 실무 데드락 위험은 충분히 예방할 수 있다고 합니다.</li>
</ul>
<hr>
<blockquote>
<p><strong>참고:</strong><br>대규모 시스템에서는 (SET innodb_lock_wait_timeout = 5) 와 같은 옵션으로
트랜잭션 대기 시간을 제한하고, 자동 롤백을 설정할 수도 있습니다.</p>
</blockquote>
<hr>
<h2 id="reference">Reference</h2>
<p><a href="https://velog.io/@juhyeon1114/MySQL-Deadlock%EC%9D%98-%EC%9B%90%EC%9D%B8%EA%B3%BC-%EC%98%88%EB%B0%A9-%EB%B0%A9%EB%B2%95">[MySQL] Deadlock의 원인과 예방 방법</a>
<a href="https://www.cs.emory.edu/~cheung/Courses/554/Syllabus/8-recv+serial/deadlock-woundwait.html">Preventing deadlock with timestamps: wound-wait scheme</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #4 – 트랜잭션]]></title>
            <link>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</link>
            <guid>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98</guid>
            <pubDate>Mon, 07 Jul 2025 03:48:41 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98transaction%EC%9D%B4%EB%9E%80">트랜잭션(Transaction)이란?</a></li>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-4%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%84%B1acid">트랜잭션의 4가지 특성(ACID)</a></li>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%98-%EC%83%81%ED%83%9C%EC%99%80-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0">트랜잭션의 상태와 생명주기</a></li>
<li><a href="#%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%A0%9C%EC%96%B4concurrency-control">동시성 제어(Concurrency Control)</a><ul>
<li><a href="#1-dirty-read-%EB%8D%94%ED%8B%B0-%EB%A6%AC%EB%93%9C">Dirty Read (더티 리드)</a></li>
<li><a href="#2-non-repeatable-read-%EB%B9%84%EB%B0%98%EB%B3%B5%EC%A0%81-%EC%9D%BD%EA%B8%B0">Non-Repeatable Read (비반복적 읽기)</a></li>
<li><a href="#3-phantom-read-%ED%8C%AC%ED%85%80-%EB%A6%AC%EB%93%9C">Phantom Read (팬텀 리드)</a></li>
</ul>
</li>
<li><a href="#%EB%9D%BDlock-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98">락(Lock) 메커니즘</a><ul>
<li><a href="#%EA%B3%B5%EC%9C%A0%EB%9D%BD%EA%B3%BC-%EB%B0%B0%ED%83%80%EB%9D%BD">공유락과 배타락</a></li>
</ul>
</li>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B2%A9%EB%A6%AC%EC%88%98%EC%A4%80isolation-level">트랜잭션 격리수준(Isolation Level)</a></li>
<li><a href="#2%EB%8B%A8%EA%B3%84-%EB%9D%BD%ED%82%B92pl-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C">2단계 락킹(2PL) 프로토콜</a></li>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EA%B4%80%EB%A6%AC-%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD">트랜잭션 관리 시 주의사항</a></li>
<li><a href="#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D-%EA%B0%80%EB%8A%A5%ED%95%9C-%EB%AC%B8%EC%A0%9C%EB%8D%B0%EB%93%9C%EB%9D%BD-%EB%93%B1-%EA%B0%84%EB%8B%A8-%EC%86%8C%EA%B0%9C">트랜잭션에서 발생 가능한 문제(데드락 등) 간단 소개</a></li>
</ul>
<hr>
<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b120f513-ced3-4eac-a92a-fafe25c3690d/image.png" alt=""></p>
<p>앞선 글에서는 정규화(Normalization)라는 설계 기법을 통해
데이터의 중복과 불일치, 이상(anomaly) 현상을
효과적으로 방지할 수 있음을 확인했다.</p>
<p>하지만, 실제로 여러 사용자가 동시에 데이터를 읽고,
수정하는 “실시간 운영 환경”에서는
단순한 설계만으로는 <strong>데이터의 ‘정확성’과 ‘일관성’</strong>을
완전히 보장하기 어렵다.</p>
<p>엑셀이나 스프레드시트처럼 단순한 파일에서는
동시에 여러 사용자가 접근할 경우
데이터가 꼬이거나, 예기치 않은 충돌이 빈번하게 발생할 수 있다.</p>
<p>예를 들어,
“송금”이나 “재고 관리” 같은 중요한 비즈니스 로직에서
하나의 데이터가 여러 작업에 의해 동시에 변경된다면
잘못된 금액이 이체되거나,
재고가 음수로 떨어지는 심각한 문제가 생길 수 있다.</p>
<p>이런 문제를 해결하기 위해
데이터베이스는 <strong>트랜잭션(Transaction)</strong>이라는
강력한 보호장치를 제공한다.</p>
<p>이번 글에서는
트랜잭션이란 무엇이고, 왜 필요한지,
그리고 실전에서 어떤 원리와 규칙으로
데이터의 신뢰성을 지키는지
쉽고 구체적으로 정리해보려 한다.</p>
<hr>
<h2 id="트랜잭션transaction이란">트랜잭션(Transaction)이란?</h2>
<p>트랜잭션(Transaction)의 사전적 의미는 거래이고,
컴퓨터 과학 분야에서의 트랜잭션(Transaction)은 &quot;더이상 분할이 불가능한 업무처리의 단위&quot;를 의미한다.</p>
<p>이것은 하나의 작업을 위해 더이상 분할될 수 없는 명령들의 모음,
즉, 한꺼번에 수행되어야 할 일련의 연산모음을 의미한다.</p>
<p>우리가 사용하는 DBMS에서 트랜잭션은 데이터베이스를 상태를 바꾸는 일종의 작업 단위이다.</p>
<p>INSERT, DELETE, UPDATE 등의 SQL 명령문을 통해 데이터를 상태를 바꾸는 일련의 모든 과정이 하나의 트랜잭션이다. 이때 오해하면 안되는게,각각 명령어가 하나의 트랜잭션이 될 수도 있지만, 묶여서 하나의 트랜잭션이 될 수도 있는 것이다.</p>
<hr>
<h2 id="트랜잭션의-4가지-특성acid">트랜잭션의 4가지 특성(ACID)</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/bc290717-2ef6-4f2e-a907-f95e1446591e/image.webp" alt=""></p>
<h3 id="트랜잭션의-4가지-특성">트랜잭션의 4가지 특성</h3>
<ul>
<li><strong>Atomicity(원자성)</strong></li>
<li><strong>Consistency(일관성)</strong></li>
<li><strong>Isolation(격리성)</strong></li>
<li><strong>Durability(영속성)</strong></li>
</ul>
<h3 id="atomicity원자성">Atomicity(원자성)</h3>
<p><strong>원자성은 트랜잭션이 데이터베이스에 모두 반영되던가, 아니면 전혀 반영되지 않아야 한다는 것이다.</strong></p>
<ul>
<li><p>트랜잭션 내의 모든 작업이 <strong>“한 덩어리”</strong>로서 수행된다.</p>
</li>
<li><p>중간에 한 단계라도 실패하면, 그 전에 진행된 모든 변경 사항도 함께 <strong>롤백(취소)</strong>된다.</p>
</li>
<li><p>원자성의 특성을 반영하면 데이터가 꼬이는 일을 방지 할 수 있다.</p>
</li>
</ul>
<h3 id="consistency일관성">Consistency(일관성)</h3>
<p><strong>일관성은 트랜잭션의 작업 처리 결과가 항상 일관성이 있어야 한다는 것이다.</strong></p>
<ul>
<li><p>트랜잭션 전/후의 데이터가 무결성(Integrity), 제약조건(Constraints), 비즈니스 규칙 등을 항상 지켜야 함을 의미합니다.</p>
</li>
<li><p>잘못된 트랜잭션은 아예 실행되지 않거나, 실행 도중 롤백됩니다.</p>
</li>
</ul>
<h3 id="isolation격리성">Isolation(격리성)</h3>
<p>독립성은 둘 이상의 트랜잭션이 동시에 실행되고 있을 경우,
<strong>어떤 하나의 트랜잭션이라도 다른 트랜잭션의 연산에 끼어들 수 없다는 점을 가리킨다.</strong></p>
<ul>
<li><p>한 트랜잭션이 완료되기 전까지 다른 트랜잭션이 그 변경사항을 볼 수 없거나, 영향을 받지 않아야 함을 의미합니다.</p>
</li>
<li><p>각각의 트랜잭션이 “내부적으로는 단독으로 실행되는 것”처럼 느껴져야 합니다.</p>
</li>
<li><p>격리 수준(4단계)마다 세부 동작이 다르지만, 기본 개념은 서로 간섭 불가능 합니다.</p>
</li>
</ul>
<h3 id="durability영속성">Durability(영속성)</h3>
<p><strong>커밋된 트랜잭션의 결과는 데이터베이스에 영구적으로 저장되어야 한다.</strong></p>
<ul>
<li><p>일단 트랜잭션이 커밋되면, 정전, 서버 다운, 시스템 장애가 발생해도 해당 데이터는 영원히 보존되야 합니다.</p>
</li>
<li><p>대부분의 데이터베이스는 커밋 시 디스크 기록(Write Ahead Logging 등)까지 완료한 뒤
성공 신호를 줍니다.</p>
</li>
</ul>
<hr>
<h2 id="트랜잭션의-상태와-생명주기">트랜잭션의 상태와 생명주기</h2>
<p>트랜잭션은 작업을 시작해서 끝날 때까지 여러 상태를 거치는데, 대표적인 상태 변화는 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/2a33ef40-2606-435a-a5e9-e50e747cb224/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>상태</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>활성 (Active)</strong></td>
<td>트랜잭션이 시작되어 현재 연산(삽입, 삭제, 갱신 등)이 진행 중인 상태. 아직 아무것도 확정되지 않은 “진행 중” 단계.</td>
</tr>
<tr>
<td><strong>부분 완료 (Partially Committed)</strong></td>
<td>트랜잭션의 마지막 연산이 끝난 상태. 하지만 완전히 DB에 기록(Commit)되진 않았으며, DBMS가 내부적으로 변경사항을 점검 중인 단계.</td>
</tr>
<tr>
<td><strong>완료 (Committed)</strong></td>
<td>트랜잭션의 모든 작업이 성공적으로 처리되어 영구적으로 데이터베이스에 반영된 상태. 더 이상 롤백이 불가능함.</td>
</tr>
<tr>
<td><strong>실패 (Failed)</strong></td>
<td>트랜잭션 처리 도중 오류(예: 제약조건 위반, 시스템 오류)가 발생하여 더 이상 진행이 불가능해진 상태.</td>
</tr>
<tr>
<td><strong>철회 (Aborted)</strong></td>
<td>실패 상태에서 Rollback 명령에 의해 트랜잭션의 모든 변경사항이 이전 상태로 되돌려진 상태. 즉, 트랜잭션이 취소되고 데이터는 아무 변화도 없는 것처럼 유지됨.</td>
</tr>
</tbody></table>
<hr>
<h3 id="1-dirty-read-더티-리드">1. Dirty Read (더티 리드)</h3>
<ul>
<li><p><strong>정의</strong><br>아직 <strong>커밋되지 않은(=확정 전)</strong> 데이터를<br>다른 트랜잭션이 읽는 현상입니다.</p>
</li>
<li><p><strong>문제점</strong><br>만약 최초 트랜잭션이 롤백된다면,<br>이미 읽어간 트랜잭션은 <strong>존재하지 않는 값</strong>을 사용하게 됩니다.</p>
</li>
<li><p><strong>예시</strong><br><img src="https://velog.velcdn.com/images/losiento_nana/post/1bca4b5b-2798-487b-a312-850d5a29e4ba/image.png" alt="">  </p>
</li>
<li><p>기본 동작</p>
</li>
<li><p><strong>Transaction 1</strong>: x에 y를 더한다.</p>
<ul>
<li><strong>Transaction 2</strong>: y를 70으로 바꾼다.<ul>
<li>동작 과정-<ol>
<li>Transaction 1이 x(10)를 읽고, y(70)를 읽어서 x+y=80으로 계산.</li>
<li>하지만 Transaction 2가 y=70 작업을 <strong>롤백(abort)</strong> 하면,<br>실제 y는 20으로 돌아간다.</li>
<li>정상적으로라면 x=30이어야 하지만,<br>이미 70을 사용해서 <strong>x=80이 되는 오류</strong>가 발생.</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-non-repeatable-read-비반복적-읽기">2. Non-Repeatable Read (비반복적 읽기)</h3>
<ul>
<li><p><strong>정의</strong><br><strong>하나의 트랜잭션</strong>이 같은 데이터를 <strong>두 번 읽을 때</strong>,  
그 사이 <strong>다른 트랜잭션이 값을 수정</strong>하여<br>두 번째 읽기에서 결과가 달라지는 현상입니다.</p>
</li>
<li><p><strong>문제점</strong><br>한 트랜잭션 내에서 <strong>읽은 값이 일관성 없이 달라질 수 있음</strong>.</p>
</li>
<li><p><strong>예시</strong><br><img src="https://velog.velcdn.com/images/losiento_nana/post/3f5e0eb4-9681-4d0d-9402-56fa1df6f04b/image.png" alt="">  </p>
<ul>
<li>기본 동작</li>
<li><strong>Transaction 1</strong>: x를 두 번 읽는다.</li>
<li><strong>Transaction 2</strong>: x에 40을 더한다.<ul>
<li>동작 과정-<ol>
<li>Transaction 1이 x=10을 읽음.</li>
<li>Transaction 2가 x=10을 읽고 40을 더해 x=50으로 바꾸고 commit.</li>
<li>Transaction 1이 다시 x를 읽음 → x=50.</li>
<li><strong>한 트랜잭션 내에서 x가 10 → 50으로 바뀌는 비반복적 읽기 현상 발생</strong>.</li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-phantom-read-팬텀-리드">3. Phantom Read (팬텀 리드)</h3>
<ul>
<li><p><strong>정의</strong><br>한 트랜잭션이 <strong>같은 조건으로 여러 번 조회</strong>할 때,<br><strong>그 사이 다른 트랜잭션이 새로운 행을 삽입/삭제</strong>해서<br>결과가 달라지는 현상입니다.</p>
</li>
<li><p><strong>문제점</strong><br><strong>한 번은 없던 데이터가, 같은 조건에서 다음 조회에는 등장</strong><br>→ 데이터의 정합성에 혼란이 생김</p>
</li>
<li><p><strong>예시</strong><br><img src="https://velog.velcdn.com/images/losiento_nana/post/97e99bda-2f3c-493b-911b-f6da66af33a2/image.png" alt="">  </p>
<ul>
<li>기본 동작</li>
<li><strong>Transaction 1</strong>: v=10인 데이터를 조회한다.</li>
<li><strong>Transaction 2</strong>: t2의 v를 50에서 10으로 바꾸고 commit.<ul>
<li>동작 과정-<ol>
<li>Transaction 1이 v=10을 조회하면 t1만 조회됨.</li>
<li>Transaction 2가 t2의 v를 10으로 바꾼 후 commit.</li>
<li>Transaction 1이 다시 v=10을 조회하면 t1, t2 모두 결과로 나옴.</li>
<li><strong>같은 조건, 다른 결과 → 팬텀(유령)처럼 행이 나타나는 현상</strong></li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h2 id="락lock-메커니즘">락(Lock) 메커니즘</h2>
<h3 id="락-메커니즘-이란">락 메커니즘 이란?</h3>
<ul>
<li>트랜재션 락 매커니즘이은 데이터베이스에서 여러 트랜잭션이 동시에 데이터에 접근할 때의 문제를 해결하기 위한 핵심 매커니즘 이다.</li>
</ul>
<h3 id="공유락과-배타락">공유락과 배타락</h3>
<ul>
<li><p><strong>공유락(Shared Lock, S Lock)</strong>  </p>
<ul>
<li>데이터를 <strong>읽기(read)</strong>만 할 때 거는 락입니다.  </li>
<li>여러 트랜잭션이 동시에 같은 데이터에 공유락을 걸 수 있어, <strong>동시 읽기</strong>는 가능하지만,  </li>
<li>이 상태에서는 <strong>쓰기(수정)</strong> 작업은 불가능합니다.</li>
<li>예: 여러 사람이 같은 책을 &#39;읽을&#39; 수는 있지만, 누군가 수정하려면 읽는 사람 모두가 책을 내려놔야 함.</li>
</ul>
</li>
<li><p><strong>배타락(Exclusive Lock, X Lock)</strong>  </p>
<ul>
<li>데이터를 <strong>쓰기(write, update, delete)</strong>할 때 거는 락입니다.</li>
<li><strong>오직 하나의 트랜잭션만</strong> 해당 데이터에 접근할 수 있습니다.  </li>
<li>이 락이 걸린 데이터는 <strong>읽기/쓰기 모두 차단</strong>되어, 다른 트랜잭션이 접근 불가합니다.</li>
<li>예: 한 사람이 책에 &#39;수정&#39;하려면, 그 동안 아무도 읽거나 쓸 수 없음.</li>
</ul>
</li>
</ul>
<h3 id="공유락s-lock과-배타락x-lock-사용-규칙-정리">공유락(S Lock)과 배타락(X Lock) 사용 규칙 정리</h3>
<h3 id="1-락이-없는-데이터라면">1. 락이 없는 데이터라면?</h3>
<ul>
<li><strong>아무 트랜잭션도 락을 안 걸고 있다면</strong><br>→ 누구든 자유롭게 공유락(S Lock) 또는 배타락(X Lock)을 걸 수 있음.</li>
</ul>
<hr>
<h3 id="2-트랜잭션이-데이터-x를-읽기만-할-때">2. 트랜잭션이 데이터 X를 읽기만 할 때</h3>
<ul>
<li><strong>읽기만 할 거면</strong><br>→ <strong>공유락 S(X)</strong>를 요청함.</li>
<li><strong>읽거나 쓸 거면(수정/삭제)</strong><br>→ <strong>배타락 X(X)</strong>를 요청함.</li>
</ul>
<hr>
<h3 id="3-이미-공유락s-lock이-걸려-있는-경우">3. 이미 공유락(S Lock)이 걸려 있는 경우</h3>
<ul>
<li><strong>누군가 S Lock을 이미 걸고 있다면?</strong>  <ul>
<li>추가로 <strong>S Lock</strong>을 거는 건 허용됨.<br>(즉, 여러 트랜잭션이 동시에 읽기 작업을 할 수 있음)</li>
<li>하지만 <strong>X Lock</strong>(쓰기/수정 락)을 요청하면 거절됨.<br>(읽는 사람 다 끝나야 쓸 수 있음)</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-이미-배타락x-lock이-걸려-있는-경우">4. 이미 배타락(X Lock)이 걸려 있는 경우</h3>
<ul>
<li><strong>누군가 X Lock을 걸고 있다면?</strong>  <ul>
<li><strong>S Lock</strong>(읽기)도, <strong>X Lock</strong>(쓰기)도<br><strong>모두 거절됨</strong></li>
<li>오직 락을 건 트랜잭션 하나만 해당 데이터에 접근 가능</li>
</ul>
</li>
</ul>
<hr>
<h3 id="5-락을-얻지-못한-트랜잭션은">5. 락을 얻지 못한 트랜잭션은?</h3>
<ul>
<li><strong>락 요청이 거절되면?</strong><ul>
<li>락이 풀릴 때까지 <strong>대기</strong> 상태가 됨(블로킹)</li>
</ul>
</li>
</ul>
<hr>
<h3 id="🔑-한-줄-요약">🔑 한 줄 요약</h3>
<ul>
<li><strong>공유락(S Lock):</strong> 여러 트랜잭션이 동시에 읽기만 가능  </li>
<li><strong>배타락(X Lock):</strong> 오직 한 트랜잭션만 읽기/쓰기가 가능, 나머지는 모두 대기</li>
</ul>
<hr>
<h4 id="예시-상황">예시 상황</h4>
<table>
<thead>
<tr>
<th>데이터 락 상태</th>
<th>신규 S Lock 요청</th>
<th>신규 X Lock 요청</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>락 없음</td>
<td>가능</td>
<td>가능</td>
<td>락이 없으니 누구나 락 가능</td>
</tr>
<tr>
<td>S Lock 한 명 있음</td>
<td>가능</td>
<td>불가</td>
<td>읽기는 OK, 쓰기는 X</td>
</tr>
<tr>
<td>S Lock 여러 명 있음</td>
<td>가능</td>
<td>불가</td>
<td>읽기는 OK, 쓰기는 X</td>
</tr>
<tr>
<td>X Lock 한 명 있음</td>
<td>불가</td>
<td>불가</td>
<td>모두 대기!</td>
</tr>
</tbody></table>
<hr>
<h2 id="트랜잭션-격리수준isolation-level">트랜잭션 격리수준(Isolation Level)</h2>
<p>트랜잭션의 <strong>격리 수준(Isolation Level)</strong>은  
여러 트랜잭션이 동시에 작업할 때 발생할 수 있는<br><strong>Dirty Read</strong>, <strong>Unrepeatable Read</strong>, <strong>Phantom Read</strong><br>문제에 대한 허용 여부를 정의합니다.</p>
<p>아래 표는 각 격리 수준별로<br>어떤 문제가 허용(○)되고,<br>어떤 문제는 방지(×)되는지 나타냅니다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/1377a9be-1769-4eb2-bae8-42cd4318c79c/image.png" alt=""></p>
<h3 id="각-격리-수준에서-허용방지되는-현상">각 격리 수준에서 허용/방지되는 현상</h3>
<ul>
<li><strong>O(○)</strong>: 해당 현상이 발생할 수 있다 (막아주지 않는다)</li>
<li><strong>X(×)</strong>: 해당 현상이 발생하지 않는다 (막아준다)</li>
</ul>
<h4 id="1-read-uncommitted">1. Read Uncommitted</h4>
<ul>
<li><strong>Dirty Read, Unrepeatable Read, Phantom Read</strong> 모두 발생 가능  </li>
<li>가장 낮은 수준의 격리(성능은 빠르지만, 신뢰성 매우 낮음)</li>
</ul>
<h4 id="2-read-committed">2. Read Committed</h4>
<ul>
<li><strong>Dirty Read</strong>는 방지, 하지만  </li>
<li><strong>Unrepeatable Read, Phantom Read</strong>는 여전히 발생 가능  </li>
<li>대부분의 RDBMS(오라클 등) 기본값</li>
</ul>
<h4 id="3-repeatable-read">3. Repeatable Read</h4>
<ul>
<li><strong>Dirty Read, Unrepeatable Read</strong>는 방지  </li>
<li><strong>Phantom Read</strong>는 발생 가능  </li>
<li>MySQL InnoDB 기본값</li>
</ul>
<h4 id="4-serializable">4. Serializable</h4>
<ul>
<li>모든 현상(Dirty/Unrepeatable/Phantom Read) 방지  </li>
<li>완벽한 일관성(단, 성능이 가장 느림, 동시성 제한 큼)</li>
</ul>
<blockquote>
<p>이걸 보면서 궁금해진게 생겼다. 왜 다막지 않고, 일부만 막는 단계(격리 수준)가 있는 걸까? 찾아보니 주요 내용은 이러했다.</p>
</blockquote>
<h4 id="1-완벽하게-다-막으면serializable">1. 완벽하게 다 막으면(Serializable)</h4>
<ul>
<li><strong>모든 트랜잭션이 순차적으로 일어나는 것처럼 동작</strong>하기 때문에 데이터 충돌, 일관성 문제는 100% 방지할 수 있습니다.</li>
<li>하지만, 동시에 여러 사용자가 접근하는 시스템에서는 <strong>속도가 너무 느려지고</strong> 대기 시간이 길어집니다.</li>
<li>예를 들어, 대형 쇼핑몰에서 모든 결제·조회·업데이트를 무조건 &#39;하나씩&#39;만 처리한다면, 실시간 서비스가 어렵습니다.</li>
</ul>
<h4 id="2-느슨하게-막으면read-committed-repeatable-read-등">2. 느슨하게 막으면(Read Committed, Repeatable Read 등)</h4>
<ul>
<li><strong>성능(동시성)</strong>이 좋아집니다. 여러 트랜잭션이 어느 정도 겹치면서 작업할 수 있기 때문입니다.</li>
<li>그 대신, 데이터 일관성에 &#39;약간의&#39; 문제(예: Dirty Read, Phantom Read)가 생길 수 있지만, 대부분의 서비스에서는 &#39;완벽&#39;한 일관성보다는 빠른 처리와 실용적인 신뢰성이 더 중요합니다.</li>
</ul>
<hr>
<h2 id="2단계-락킹2pl-two-phase-locking-프로토콜">2단계 락킹(2PL: Two-Phase Locking) 프로토콜</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/65533b93-d7e5-435a-bac3-de32547b32e8/image.png" alt=""></p>
<p>트랜잭션의 락 관리 방식 중 하나로, <strong>일관성(Serializability)</strong>을 보장하기 위해 사용합니다.<br>&quot;락을 다 얻은 후에만 해제할 수 있다&quot;라는 두 가지 단계로 운영됩니다.</p>
<ol>
<li><p><strong>성장 단계(Growing Phase)</strong></p>
<ul>
<li>트랜잭션이 필요한 락을 계속 <strong>획득</strong>할 수 있지만, <strong>해제는 할 수 없음</strong></li>
<li>필요한 락을 모두 얻을 때까지 이 단계</li>
</ul>
</li>
<li><p><strong>축소 단계(Shrinking Phase)</strong></p>
<ul>
<li>한번이라도 락을 <strong>해제</strong>하면, 그 뒤로는 <strong>획득 불가</strong></li>
<li>이미 얻은 락만 해제 가능</li>
</ul>
</li>
</ol>
<blockquote>
<p>2PL 프로토콜을 따르면 데드락이 발생할 수 있지만,<br>트랜잭션의 일관성(Serializability)은 항상 보장됨</p>
</blockquote>
<hr>
<h2 id="트랜잭션-관리-시-주의사항">트랜잭션 관리 시 주의사항</h2>
<ul>
<li><p><strong>무한루프</strong><br>트랜잭션이 정사적으로 종료되지 못하고 계속 실행되면, 시스템 리소스를 잡아먹고 다른 작업에 영향을 줌.</p>
</li>
<li><p><strong>락 장기 점유</strong><br>하나의 트랜잭션이 락을 오래 쥐고 있으면,<br>다른 트랜잭션들이 대기하면서 전체 성능이 저하됨.<br>심하면 데드락 위험도 커짐.</p>
</li>
<li><p><strong>적절한 롤백 처리</strong><br>오류나 예외 상황에서 Rollback을 해주지 않으면<br>데이터가 이상한 상태로 남게 됨.</p>
</li>
</ul>
<hr>
<h2 id="트랜잭션에서-발생-가능한-문제데드락-등-간단-소개">트랜잭션에서 발생 가능한 문제(데드락 등) 간단 소개</h2>
<ul>
<li><strong>데드락(Deadlock)</strong><br>두 개 이상의 트랜잭션이 서로 상대방의 락 해제를 기다리며<br><strong>무한정 대기</strong>에 빠지는 현상.</li>
</ul>
<blockquote>
<p>데드락의 원인, 탐지, 해결법 등은<br>별도 챕터([교착상태(Deadlock)])에서 다룰 예정입니다.</p>
</blockquote>
<hr>
<p><strong>참고 자료</strong></p>
<ul>
<li><a href="https://inpa.tistory.com/entry/MYSQL-%F0%9F%93%9A-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98Transaction-%EC%9D%B4%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC">[MYSQL] 📚 트랜잭션(Transaction) 개념 &amp; 사용</a></li>
<li><a href="https://gamsayeon.tistory.com/63">락(Locking) 메커니즘</a></li>
<li><a href="https://mozzi-devlog.tistory.com/49">트랜잭션 격리 수준(Isolation Level)에 대한 이해</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[한화 BEYOND SW 캠프 18기 [3주차] & 나의 개발 성장 회고]]></title>
            <link>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 06 Jul 2025 14:01:26 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-한화-beyond-sw-캠프-3주차-wil-weekly-reflection">✨ 한화 BEYOND SW 캠프 [3주차] WIL (Weekly Reflection)</h1>
<blockquote>
<p>이 글은 BEYOND SW 캠프 3주차 동안의 성장과 배움을 스스로 돌아보고 기록하기 위한 회고입니다.</p>
</blockquote>
<hr>
<h2 id="🔗-이번주-캠프-학습노트정리">🔗 이번주 캠프 학습노트/정리</h2>
<blockquote>
<p>이번주 수업 중 새롭게 배운 내용이나, 직접 정리해 본 개념/기술에 대해<br>벨로그에 따로 아카이브해두었습니다.<br>(관심 있는 분들은 아래 링크에서 자세히 확인해 주세요)</p>
</blockquote>
<blockquote>
<p>이번주부터 CS에 스터디를 시작했습니다. 각자 파트를 나눠서 주제를 준비해서 발표를 하는 구조로 진행하는데, 저는 어차피 공부할꺼 꼼꼼하게 하고 싶다고 생각해서 모든 파트에 대해 공부하고 개인적으로 정리 하고 있습니다. 해당 내용을 아래에 올리겠습니다.</p>
</blockquote>
<ul>
<li><p>[혹시 &#39;데이터 베이스를&#39; 아십니까 ? #1 – 데이터베이스란?]
(<a href="https://velog.io/@losiento_nana/1.-%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EC%B4%88-">https://velog.io/@losiento_nana/1.-%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EC%B4%88-</a>)</p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%EC%9E%84%EC%8B%9C">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #2 – 인덱스</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-3-%EC%A0%95%EA%B7%9C%ED%99%94">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #3 – 정규화</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-4-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #4 – 트랜잭션</a></p>
</li>
<li><p><a href="https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-5-%EA%B5%90%EC%B0%A9%EC%83%81%ED%83%9CDeadlock">혹시 &#39;데이터 베이스를&#39; 아십니까 ? #5 – 교착상태(Deadlock)</a></p>
</li>
</ul>
<hr>
<h2 id="📅-이번-주-한-일-facts">📅 이번 주 한 일 (FACTS)</h2>
<ul>
<li>CS 스터디(데이터베이스)</li>
<li>첫번째 프로젝트 요구사항 명세서, ERD 초안, 테이블 명세서 작성, 테스트 케이스 실험</li>
<li>3주차 Maria DB를 마무리 및 Git 사용법</li>
</ul>
<hr>
<h2 id="💭-느낀-점감정-feelings">💭 느낀 점/감정 (FEELINGS)</h2>
<p>이번 주는 유난히 새롭게 배우는 게 많았던 한 주였다. 데이터베이스는 분명 이전에도 사용해봤고, 익숙하다고 생각했었는데, 막상 이론적으로 파고들다 보니 아직 모르는 내용도 많다는 걸 실감했다. 스터디에서 질문을 주고받으면서, 남한테 설명할 수 있을 정도로 깊게 이해해야겠다는 생각도 들었고, 그래서인지 더 많이 고민하고 더 많이 배울 수 있었던 것 같다.
프로젝트 준비를 하면서는 직접 ERD를 그리거나, 테이블 명세서를 고민하는 과정이 참 재밌었다. 처음엔 뭐가 맞는 설계인지 헷갈릴 때도 있었지만, 직접 부딪혀보니까 내가 몰랐던 부분들도 하나씩 보이고, 조금씩 성장하고 있다는 느낌을 받았다. DB 단에서 직접 테스트 케이스 만들어보는 경험도 새로웠다</p>
<hr>
<h2 id="🔍-배운-점새롭게-알게-된-것-findings">🔍 배운 점/새롭게 알게 된 것 (FINDINGS)</h2>
<p>프로젝트를 진행하면서 특히 식별/비식별 관계나 확장성 있는 DB 설계에 대해 더 깊게 배웠다. 프로젝트 요구사항을 정리하고, ERD를 작성하면서 단순히 테이블만 나열하는 게 아니라 어떤 관계가 더 효율적인지, 속성 타입을 어떤 기준으로 잡아야 하는지까지 고민해볼 수 있었다.
특히 프로시저를 이용해서 DB 단에서 바로 테스트하는 경험은 처음이었는데, 실제로 이렇게도 테스트가 가능하다는 걸 배운 게 꽤 큰 수확이었다. 그리고 Git에 대해서도 평소에는 그냥 쓰기만 했는데, 이번 기회에 실무에서 주로 겪게 되는 오류나, 회사마다 다르게 쓰는 방식 등 여러 가지 팁을 배울 수 있어서 유익했다.</p>
<hr>
<h2 id="🚧-어려움--문제-problem-or-minus">🚧 어려움 &amp; 문제 (Problem or Minus)</h2>
<p>일단 스터디를 준비하는데 있어서 내가 발표하지 않는 모든 파트도 개인적으로 공부해서 벨로그에 정리하기로 마음을 먹었던데, 이 부분이 시간적으로 굉장히 부담이 많이 가는거 같다. 또한
ERD를 작성하면서 식별관계와 비식별관계를 구분하고, 어떻게 분할해야 할지 고민하는 게 꽤 어렵게 느껴졌다. 그리고 테이블 속성을 결정할 때, 예를 들어 ENUM 타입으로 제한할지, VARCHAR로 더 유연하게 갈지, 확장성과 제약 조건 사이에서 균형을 잡는 게 쉽지 않았다.
또한 MariaDB에서 직접 프로시저를 작성해서 테스트할 때, 익숙하지 않은 문법이나 예상치 못한 오류에 종종 막히기도 했다. Git 역시 단순한 사용은 익숙했지만, 충돌이나 branch 전략 같은 부분은 여전히 낯설게 느껴졌다.</p>
<hr>
<h2 id="🛠️-해결-방법--시도-try-or-improve">🛠️ 해결 방법 &amp; 시도 (Try or Improve)</h2>
<p>테이블 속성이나 관계를 정할 때는 다양한 시나리오를 가정해보고, 확장성이 중요한 부분은 유연하게, 확실히 제한해야 할 부분은 ENUM 등으로 명확히 하는 식으로 기준을 잡았다.
MariaDB 프로시저는 여러 번 실패하면서도 계속 수정해보고,나씩 만들어가며 익혔다. Git 사용법도 그냥 넘어가지 않고, 실제로 내가 만났던 오류 상황을 복기하면서 직접 해결법을 찾아보고, 실무에서 쓸 만한 팁도 정리해두려고 노력했다.</p>
<hr>
<h2 id="🌱-앞으로의-계획--다짐-future">🌱 앞으로의 계획 &amp; 다짐 (Future)</h2>
<p>이번에 DB와 Git, 그리고 실전 테스트에 대해 많이 배웠으니, 앞으로는 프로젝트 진행하면서 계속해서 이런 방식으로 직접 실험하고, 경험을 쌓아나가려고 한다. DB 설계나 프로시저 작성에 더 익숙해질 수 있도록 연습을 많이 해보고, Git도 팀원들과 함께 효율적으로 사용할 수 있는 방법을 더 고민해볼 예정이다.
무엇보다, 항상 궁금증을 그냥 넘기지 않고 적극적으로 질문하고, 더 깊게 공부하는 습관을 계속 이어가고 싶다.</p>
<hr>
<h2 id="🙌-한-줄-마무리">🙌 한 줄 마무리</h2>
<blockquote>
<p>바쁘다 바빠 현대사회</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #3 – 정규화]]></title>
            <link>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-3-%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@losiento_nana/%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-3-%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Sat, 05 Jul 2025 08:39:47 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EC%A0%95%EA%B7%9C%ED%99%94%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80">정규화란 무엇인가?</a></li>
<li><a href="#%EC%A0%95%EA%B7%9C%ED%99%94%EC%9D%98-%ED%83%84%EC%83%9D-%EB%B0%B0%EA%B2%BD">정규화의 탄생 배경</a></li>
<li><a href="#%EC%9D%B4%EC%83%81%EC%9D%B4%EB%9E%80">이상이란</a><ul>
<li><a href="#1-%EC%82%BD%EC%9E%85-%EC%9D%B4%EC%83%81-insertion-anomaly">삽입 이상 (Insertion Anomaly)</a></li>
<li><a href="#2-%EA%B0%B1%EC%8B%A0-%EC%9D%B4%EC%83%81-update-anomaly">갱신 이상 (Update Anomaly)</a></li>
<li><a href="#3-%EC%82%AD%EC%A0%9C-%EC%9D%B4%EC%83%81-deletion-anomaly">삭제 이상 (Deletion-Anomaly)</a></li>
</ul>
</li>
<li><a href="#%EC%A0%95%EA%B7%9C%ED%99%94%EC%9D%98-%EB%8B%A8%EA%B3%84%EC%99%80-%EC%A2%85%EB%A5%98">정규화의 단계와 종류</a><ul>
<li><a href="#%EC%A0%9C1%EC%A0%95%EA%B7%9C%ED%98%951nf-%EC%95%84%EC%A7%81-%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%EC%95%88%EB%90%9C-%ED%85%8C%EC%9D%B4%EB%B8%94">제1정규형(1NF)</a></li>
<li><a href="#%EC%A0%9C1%EC%A0%95%EA%B7%9C%ED%98%951nf%EC%A0%95%EA%B7%9C%ED%99%94-%EC%99%84%EB%A3%8C">제1정규형(1NF) (정규화 완료)</a></li>
<li><a href="#%EC%A0%9C2%EC%A0%95%EA%B7%9C%ED%98%952nf-%EC%95%84%EC%A7%81-%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%EC%95%88%EB%90%9C-%ED%85%8C%EC%9D%B4%EB%B8%94">제2정규형(2NF)</a></li>
<li><a href="#%EC%A0%9C2%EC%A0%95%EA%B7%9C%ED%98%952nf%EC%A0%95%EA%B7%9C%ED%99%94-%EC%99%84%EB%A3%8C">제2정규형(2NF) (정규화 완료)</a></li>
<li><a href="#%EC%A0%9C3%EC%A0%95%EA%B7%9C%ED%98%953nf-%EC%95%84%EC%A7%81-%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%EC%95%88%EB%90%9C-%ED%85%8C%EC%9D%B4%EB%B8%94">제3정규형(3NF)</a></li>
<li><a href="#%EC%A0%9C3%EC%A0%95%EA%B7%9C%ED%98%953nf%EC%A0%95%EA%B7%9C%ED%99%94-%EC%99%84%EB%A3%8C">제3정규형(3NF) (정규화 완료)</a></li>
<li><a href="#bcnf-%EB%B3%B4%EC%9D%B4%EC%8A%A4-%EC%BD%94%EB%93%9C-%EC%A0%95%EA%B7%9C%ED%98%95-%EC%95%84%EC%A7%81-%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%EC%95%88%EB%90%9C-%ED%85%8C%EC%9D%B4%EB%B8%94">BCNF (보이스-코드 정규형)</a></li>
<li><a href="#bcnf%EB%B3%B4%EC%9D%B4%EC%8A%A4-%EC%BD%94%EB%93%9C-%EC%A0%95%EA%B7%9C%ED%98%95%EC%A0%95%EA%B7%9C%ED%99%94-%EC%99%84%EB%A3%8C">BCNF (정규화 완료)</a></li>
</ul>
</li>
<li><a href="#%EC%97%AD%EC%A0%95%EA%B7%9C%ED%99%94denormalization">역정규화(Denormalization)</a><ul>
<li><a href="#%EC%97%AD%EC%A0%95%EA%B7%9C%ED%99%94%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1%EA%B3%BC-%EC%A0%81%EC%9A%A9-%EC%98%88%EC%8B%9C">역정규화의 필요성과 적용 예시</a></li>
</ul>
</li>
<li><a href="#%EC%A0%95%EA%B7%9C%ED%99%94%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%90">정규화의 장점과 단점</a><ul>
<li><a href="#%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%EC%A0%81%ED%95%A9%ED%95%9C-%EC%83%81%ED%99%A9">정규화가 적합한 상황</a></li>
<li><a href="#%EB%B0%98%EC%A0%95%EA%B7%9C%ED%99%94%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%83%81%ED%99%A9">반정규화가 필요한 상황</a></li>
</ul>
</li>
<li><a href="#%EB%A7%88%EB%AC%B4%EB%A6%AC-%EB%B0%8F-%EC%8B%A4%EB%AC%B4-tip">마무리 및 실무 Tip</a></li>
</ul>
<hr>
<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/aed5cf6c-5189-4324-955e-13d4b653003b/image.png" alt=""></p>
<p>앞선 글에서는 인덱스를 통해 데이터베이스가 <strong>방대한 양의 데이터</strong>에서도<br>얼마나 빠르고 효율적으로 원하는 정보를 찾을 수 있는지 살펴보았다.</p>
<p>그런데 대규모 데이터를 다룰 때,<br><strong>검색 성능</strong>만큼이나 중요한 게 있다.<br>바로 <strong>데이터의 ‘정확성’과 ‘일관성’</strong>을 유지하는 것이다.</p>
<p>엑셀, 스프레드시트처럼 데이터가 이리저리 중복되어 있다면<br>“어디가 진짜지?” “왜 이 값이 서로 다르지?”<br>같은 혼란이 쉽게 발생한다.</p>
<p>실제로 서비스 규모가 커질수록<br><strong>데이터 중복, 불일치, 이상(anomaly)</strong> 같은 문제가<br>빈번하게 발생하며,<br>이 때문에 서비스 장애, 운영 비용 증가,<br>최악의 경우엔 비즈니스 리스크로까지 번질 수 있다.</p>
<p>이런 문제를 근본적으로 해결하기 위해<br>데이터베이스에서는 <strong>‘정규화(Normalization)’</strong>라는 설계 기법을 사용한다.</p>
<h2 id="정규화란-무엇인가">정규화란 무엇인가?</h2>
<p>정규화란,<br><strong>데이터의 중복을 최소화</strong>하고<br><strong>이상(Anomaly)을 방지</strong>해  
<strong>최적의 테이블 구조로 설계</strong>하는 데이터베이스 설계 기법입니다.</p>
<blockquote>
<p>불필요하게 중복된 데이터를 줄이고,<br>데이터 변경/삽입/삭제 시 발생할 수 있는 ‘이상 현상’을 막기 위해 도입되었습니다.</p>
</blockquote>
<hr>
<h2 id="정규화의-탄생-배경">정규화의 탄생 배경</h2>
<ul>
<li><strong>왜 정규화가 필요한가?</strong><ul>
<li>초창기 DB는 설계가 단순하여,<br>데이터 중복 및 불필요한 데이터 증가로<br>‘삽입, 삭제, 갱신’ 등에서 <strong>예상치 못한 문제(이상, Anomaly)</strong>가 자주 발생함.</li>
<li>이러한 <strong>데이터 이상</strong>을 예방하기 위해<br>정규화 과정이 탄생하였다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="이상이란">이상이란?</h2>
<h3 id="데이터베이스에서의-이상anomaly이란">데이터베이스에서의 &quot;이상(Anomaly)&quot;이란?</h3>
<p><strong>이상(Anomaly)</strong>이란
테이블(릴레이션) 설계가 비효율적이거나 정규화가 제대로 이루어지지 않았을 때,
데이터를 삽입, 수정(갱신), 삭제하는 과정에서
원하지 않거나 의도치 않은 데이터의 불일치, 중복, 손실이 발생하는 현상을 말한다.
아래 예시로 각 이상을 SQL 쿼리와 함께 실험해보았다.</p>
<hr>
<p><strong>먼저 각종 이상 실험을 위해 정규화가 되지 않은 테이블을 임시로 만들었다.</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/bd003d57-8881-4060-a46a-abe638b5ace8/image.png" alt=""></p>
<h3 id="1-삽입-이상-insertion-anomaly">1. 삽입 이상 (Insertion Anomaly)</h3>
<p><strong>정의</strong>  </p>
<ul>
<li>자료를 삽입할 때 의도하지 않은 자료까지 같이 삽입하거나,  </li>
<li>일부 정보만 추가할 수 없어 NULL 값이나 임의의 데이터를 넣어야 하는 현상</li>
</ul>
<p><strong>예시 상황</strong>  </p>
<ul>
<li>새로운 학생 ‘최영수’가 아직 수강한 강의가 없음  </li>
<li>강의코드, 강의명에 값이 없으니 NULL을 넣을 수밖에 없음</li>
</ul>
<p><strong>실험 쿼리</strong></p>
<pre><code class="language-sql">INSERT INTO student_lecture
(학번, 이름, 나이, 성별, 강의코드, 강의명, 전화번호)
VALUES (1015, &#39;최영수&#39;, 22, &#39;남&#39;, NULL, NULL, &#39;010-5555-5555&#39;);</code></pre>
<ul>
<li>강의 정보 없이 학생만 추가하려다 강의코드/강의명에 NULL을 넣게 됨</li>
<li>정규화된 구조라면 이런 NULL이 발생하지 않음</li>
</ul>
<p><strong>실제 실행</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/794a0382-17ab-489d-b64b-15c97066cc1b/image.png" alt=""></p>
<h3 id="2-갱신-이상-update-anomaly">2. 갱신 이상 (Update Anomaly)</h3>
<p><strong>정의</strong>  </p>
<ul>
<li>중복된 데이터 중 일부만 수정되어 데이터 모순(불일치)이 생기는 현상</li>
</ul>
<p><strong>예시 상황</strong></p>
<ul>
<li><p>김현수(학번 1013)가 전화번호를 변경했을 때,</p>
</li>
<li><p>하나의 튜플(행)만 수정하면 데이터가 불일치하게 됨</p>
</li>
</ul>
<p><strong>실험 쿼리</strong></p>
<pre><code class="language-sql">-- 김현수(자료구조 수강)의 전화번호만 수정
UPDATE student_lecture
SET 전화번호 = &#39;010-9999-9999&#39;
WHERE 학번 = 1013 AND 강의코드 = &#39;AC3&#39;;</code></pre>
<ul>
<li>이렇게 하면 ‘AC3’은 바뀌지만,
‘AC4’는 여전히 3333-3333으로 남아 데이터 불일치가 발생!</li>
</ul>
<p><strong>실제 실행</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/124a6fbf-09cb-4fd9-ae23-8a94f089efd0/image.png" alt=""></p>
<h3 id="3-삭제-이상-deletion-anomaly">3. 삭제 이상 (Deletion Anomaly)</h3>
<p><strong>정의</strong></p>
<ul>
<li>어떤 정보를 삭제할 때 의도하지 않은 다른 정보까지 함께 사라지는 현상</li>
</ul>
<p><strong>예시 상황</strong></p>
<ul>
<li><p>이태호(1011) 학생이 듣던 “데이터베이스 개론(AC1)” 강의를 더 이상 개설하지 않아 해당 행을 삭제</p>
</li>
<li><p>이태호 학생 정보 자체도 사라지게 됨</p>
</li>
</ul>
<p><strong>실험 쿼리</strong></p>
<pre><code class="language-sql">-- AC1 강의를 테이블에서 삭제
DELETE FROM student_lecture WHERE 강의코드 = &#39;AC1&#39;;</code></pre>
<p><strong>이태호의 모든 정보(학생 정보)</strong>까지 함께 삭제됨</p>
<ul>
<li>학생/강의 정보를 따로 관리하면 이런 문제를 피할 수 있음</li>
</ul>
<p><strong>실제 실행</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/af0b2ed4-2495-48f2-8e31-1821c0dc6c01/image.png" alt=""></p>
<hr>
<p>📌 정리
삽입 이상: 학생만 추가하고 싶어도 강의 정보가 없으면 NULL이 들어가야 함</p>
<p>갱신 이상: 한 학생의 정보가 여러 줄에 중복되어, 일부만 바뀌면 데이터 불일치</p>
<p>삭제 이상: 한 강의(혹은 학생)의 정보를 삭제하다가, 연관된 다른 정보까지 한꺼번에 사라짐</p>
<p>이런 문제는 테이블을 정규화(학생/강의/수강 테이블 분리)하면 해결할 수 있다.</p>
<hr>
<h2 id="정규화의-단계와-종류">정규화의 단계와 종류</h2>
<blockquote>
<p>글에서는 실무에서 가장 많이 쓰이는 1정규화부터 BCNF단계까지만 다루도록 하겠다.</p>
</blockquote>
<ul>
<li>모든 컬럼이 <strong>원자값(Atomic Value)</strong>만 갖도록 테이블을 설계</li>
<li>예시: 컬럼에 배열/리스트 대신, 단일 값만<h2 id="제1정규형1nf-아직-정규화가-안된-테이블">제1정규형(1NF)( 아직 정규화가 안된 테이블)</h2>
<table>
<thead>
<tr>
<th>학번</th>
<th>이름</th>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>홍길동</td>
<td>컴퓨터공학과,화학과</td>
<td>김철수,박지민</td>
<td>101호,102호</td>
</tr>
</tbody></table>
</li>
</ul>
<hr>
<h3 id="제1정규형1nf정규화-완료">제1정규형(1NF)(정규화 완료)</h3>
<table>
<thead>
<tr>
<th>학번</th>
<th>이름</th>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>홍길동</td>
<td>컴퓨터공학과</td>
<td>김철수</td>
<td>101호</td>
</tr>
<tr>
<td>1001</td>
<td>홍길동</td>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
<tr>
<td>1002</td>
<td>이영희</td>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
<tr>
<td>---</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="제2정규형2nf">제2정규형(2NF)</h2>
<p>-제2정규형(2NF)은 제1정규형을 만족하면서, 
기본키의 &quot;부분 집합&quot;에만 종속된 <strong>부분 함수 종속(Partial Dependency)</strong>을 제거하는 것입니다.</p>
<ul>
<li>즉, 복합 기본키를 사용하는 테이블에서, 
기본키의 일부 컬럼에만 종속된 속성이 있다면 그것을 별도의 테이블로 분리해야 합니다.</li>
</ul>
<h3 id="2정규형2nf-아직-정규화가-안된-테이블">2정규형(2NF)( 아직 정규화가 안된 테이블)</h3>
<table>
<thead>
<tr>
<th>학번</th>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>컴퓨터공학과</td>
<td>김철수</td>
<td>101호</td>
</tr>
<tr>
<td>1001</td>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
<tr>
<td>1002</td>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
</tbody></table>
<hr>
<h3 id="제2정규형2nf정규화-완료">제2정규형(2NF)(정규화 완료)</h3>
<table>
<thead>
<tr>
<th>학번</th>
<th>학과</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>컴퓨터공학과</td>
</tr>
<tr>
<td>1001</td>
<td>화학과</td>
</tr>
<tr>
<td>1002</td>
<td>화학과</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>컴퓨터공학과</td>
<td>김철수</td>
<td>101호</td>
</tr>
<tr>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
</tbody></table>
<p>2개로 분리</p>
<hr>
<h2 id="제3정규형3nf">제3정규형(3NF)</h2>
<ul>
<li>2NF를 만족하면서,<br><strong>이행적 종속(Transitive Dependency)</strong> 제거<br>(키가 아닌 속성에 종속된 속성을 분리)</li>
</ul>
<h3 id="제3정규형3nf-아직-정규화가-안된-테이블">제3정규형(3NF)( 아직 정규화가 안된 테이블)</h3>
<table>
<thead>
<tr>
<th>학번</th>
<th>이름</th>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>홍길동</td>
<td>컴퓨터공학과</td>
<td>김철수</td>
<td>101호</td>
</tr>
<tr>
<td>1002</td>
<td>이영희</td>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
</tbody></table>
<hr>
<h3 id="제3정규형3nf정규화-완료">제3정규형(3NF)(정규화 완료)</h3>
<table>
<thead>
<tr>
<th>학번</th>
<th>이름</th>
<th>학과</th>
</tr>
</thead>
<tbody><tr>
<td>1001</td>
<td>홍길동</td>
<td>컴퓨터공학과</td>
</tr>
<tr>
<td>1002</td>
<td>이영희</td>
<td>화학과</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>학과</th>
<th>교수</th>
<th>강의실</th>
</tr>
</thead>
<tbody><tr>
<td>컴퓨터공학과</td>
<td>김철수</td>
<td>101호</td>
</tr>
<tr>
<td>화학과</td>
<td>박지민</td>
<td>102호</td>
</tr>
</tbody></table>
<hr>
<h2 id="bcnf-보이스-코드-정규형">BCNF (보이스-코드 정규형)</h2>
<h3 id="bcnf보이스-코드-정규형-아직-정규화가-안된-테이블">BCNF(보이스-코드 정규형)( 아직 정규화가 안된 테이블)</h3>
<ul>
<li>3NF보다 더 엄격한 조건,<br>모든 결정자가 후보키가 되도록 설계</li>
</ul>
<table>
<thead>
<tr>
<th>강의명</th>
<th>담당교수</th>
<th>교실</th>
</tr>
</thead>
<tbody><tr>
<td>DB</td>
<td>홍교수</td>
<td>301</td>
</tr>
<tr>
<td>DB</td>
<td>이교수</td>
<td>302</td>
</tr>
<tr>
<td>OS</td>
<td>박교수</td>
<td>303</td>
</tr>
</tbody></table>
<hr>
<h4 id="bcnf보이스-코드-정규형정규화-완료">BCNF(보이스-코드 정규형)(정규화 완료)</h4>
<table>
<thead>
<tr>
<th>강의명</th>
<th>담당교수</th>
</tr>
</thead>
<tbody><tr>
<td>DB</td>
<td>홍교수</td>
</tr>
<tr>
<td>DB</td>
<td>이교수</td>
</tr>
<tr>
<td>OS</td>
<td>박교수</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>담당교수</th>
<th>교실</th>
</tr>
</thead>
<tbody><tr>
<td>홍교수</td>
<td>301</td>
</tr>
<tr>
<td>이교수</td>
<td>302</td>
</tr>
<tr>
<td>박교수</td>
<td>303</td>
</tr>
</tbody></table>
<hr>
<h2 id="역정규화denormalization">역정규화(Denormalization)</h2>
<p>정규화를 진행하면,<br>중복은 최소화되고 데이터 무결성은 높아지지만<br>쿼리가 복잡해지고 성능이 저하될 수 있다.</p>
<h3 id="역정규화의-필요성과-적용-예시">역정규화의 필요성과 적용 예시</h3>
<ul>
<li><strong>정규화를 일부 해제</strong>하여<br>쿼리 성능, 응답 속도, 집계 효율을 높임</li>
<li>일부러 중복을 허용하거나,<br>테이블을 합치거나,<br>자주 조회되는 데이터로 컬럼을 분리하는 등의 실무적 테크닉</li>
</ul>
<hr>
<h2 id="정규화의-장점과-단점">정규화의 장점과 단점</h2>
<ul>
<li><strong>장점</strong><ul>
<li>데이터 중복 최소화,<br>무결성(Integrity) 보장,<br>이상(Anomaly) 방지  </li>
</ul>
</li>
<li><strong>단점</strong><ul>
<li>테이블/관계가 많아져 쿼리가 복잡해지고,<br>Join 성능 저하 발생 가능</li>
</ul>
</li>
</ul>
<h3 id="정규화가-적합한-상황">정규화가 적합한 상황</h3>
<ul>
<li>트랜잭션 무결성이 무엇보다 중요한 서비스</li>
<li>데이터 변경(INSERT/UPDATE/DELETE)이 잦은 환경</li>
</ul>
<h3 id="반정규화가-필요한-상황">반정규화가 필요한 상황</h3>
<ul>
<li>대용량 조회가 많고, 응답 속도가 중요한 환경</li>
<li>집계/통계/리포트 쿼리가 자주 발생하는 환경</li>
</ul>
<hr>
<h2 id="마무리-및-실무-tip">마무리 및 실무 Tip</h2>
<p>정규화와 역정규화는<br>‘어떤 상황에서, 왜 적용할지’<br><strong>실제 서비스/시스템 구조를 고려해서</strong> 유연하게 사용해야 합니다.</p>
<p>실제로는 정규화를 기본으로 설계하되,<br>필요에 따라 <strong>역정규화로 성능을 최적화</strong>하는 사례가 많다고 합니다.</p>
<hr>
<p><strong>참고 자료</strong>
<a href="https://dev-coco.tistory.com/63">이상 현상(Anomaly)이란?</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #2 – 인덱스
]]></title>
            <link>https://velog.io/@losiento_nana/%EC%9E%84%EC%8B%9C</link>
            <guid>https://velog.io/@losiento_nana/%EC%9E%84%EC%8B%9C</guid>
            <pubDate>Wed, 02 Jul 2025 14:31:32 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<hr>
<ul>
<li><a href="#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80">인덱스란 무엇인가?</a></li>
<li><a href="#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%9D%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-b-tree%EC%99%80-btree">인덱스의 자료구조: B-Tree와 B+Tree</a><ul>
<li><a href="#b-tree-%EA%B5%AC%EC%A1%B0">B-Tree 구조</a></li>
<li><a href="#btree-%EA%B5%AC%EC%A1%B0">B+Tree 구조</a></li>
</ul>
</li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4clustered-index">클러스터형 인덱스(Clustered Index)</a><ul>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%ED%98%95-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%95%B5%EC%8B%AC-%EC%9A%94%EC%95%BD">클러스터형 인덱스 핵심 요약</a></li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%83%9D%EC%84%B1%EB%B3%80%ED%99%94-%EC%98%88%EC%8B%9C">클러스터 인덱스 생성/변화 예시</a></li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%A1%B0%ED%9A%8C%EB%8B%A8%EC%9D%BC">클러스터 인덱스 조회(단일)</a></li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%A1%B0%ED%9A%8C%EB%B2%94%EC%9C%84">클러스터 인덱스 조회(범위)</a></li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%82%BD%EC%9E%85">클러스터 인덱스를 이용한 데이터 삽입</a></li>
</ul>
</li>
<li><a href="#%EB%B3%B4%EC%A1%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4secondary-index-non-clustered-index">보조 인덱스(Secondary Index, Non-Clustered Index)</a><ul>
<li><a href="#%EB%B3%B4%EC%A1%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%8A%B9%EC%A7%95">보조 인덱스 특징</a></li>
</ul>
</li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4--%EB%B3%B4%EC%A1%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%98%BC%ED%95%A9-%EB%8F%99%EC%9E%91">클러스터 인덱스 + 보조 인덱스 혼합 동작</a></li>
<li><a href="#%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%99%80-%EB%B3%B4%EC%A1%B0-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%95%9C%EB%88%88%EC%97%90-%EB%B9%84%EA%B5%90">클러스터 인덱스와 보조 인덱스, 한눈에 비교</a></li>
<li><a href="#%EC%9D%B8%EB%8D%B1%EC%8A%A4%EC%9D%98-%EC%84%B1%EB%8A%A5%EA%B3%BC-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD">인덱스의 성능과 고려사항</a></li>
<li><a href="#%EB%A7%88%EC%B9%98%EB%A9%B0">마치며</a></li>
</ul>
<hr>
<h2 id="데이터-베이스-쓰세요-두번-쓰세요">데이터 베이스 쓰세요. 두번 쓰세요.</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/c34c441f-67d6-4bab-b318-f5a5718d7f18/image.png" alt=""></p>
<p>앞선 글에서는 스프레드시트보다 데이터베이스(DBMS)가 훨씬 강력한 이유(<strong>데이터 무결성, 보안, 백업, 커뮤니티 지원</strong>)를 다뤘다.</p>
<p>이제 “왜 실무에서 DBMS가 필수가 되었는가?”에 대해 어느 정도 감이 잡힐 것이다.<br>하지만 한 가지 더 중요한 궁금증이 있다.</p>
<p>단순히 “신뢰성”이나 “보안” 때문만이 아니라,<br>과연 데이터베이스(DBMS)가 <strong>수십만, 수백만, 수천만 건</strong>의 데이터를 다룰 때<br>스프레드시트(엑셀, 구글시트)와 <strong>성능 면</strong>에서도 정말 차이가 날까?</p>
<p>정답은 <strong>‘그렇다’</strong>다.<br>특히 “빠른 데이터 검색”에서 DBMS는 더욱 강점을 갖는다.</p>
<p>이 모든 차이의 핵심에는 바로 <strong>‘인덱스(색인)’</strong>라는 개념이 있다.</p>
<hr>
<h2 id="인덱스란-무엇인가">인덱스란 무엇인가?</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/26428e02-a643-4421-abfb-67b100d5b020/image.png" alt="">
<sub>출처: <a href="https://ittrue.tistory.com/331">https://ittrue.tistory.com/331</a></sub></p>
<p><strong>인덱스(색인)</strong>란,<br>데이터 검색 속도를 극적으로 높이기 위해<br>별도로 만들어 놓는 자료구조다.</p>
<p>스프레드시트/CSV는 ‘위에서부터 쭉’ 전체를 훑는 방식(Full Scan)만 지원한다.<br>반면, DBMS는 인덱스를 활용해<br>필요한 데이터 위치를 단 몇 번의 비교만에 찾는다.</p>
<p>인덱스를 만들기 위한 대표 자료구조는<br><strong>B-Tree, B+Tree, Hash테이블</strong>가 있다.</p>
<h3 id="hash-테이블-구조"><strong>Hash 테이블 구조</strong></h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/3a1e31bb-ddcb-4db7-86b4-59b88f2934ff/image.png" alt="">
<sub>출처: <a href="https://goodgid.github.io/Hash-Table/">https://goodgid.github.io/Hash-Table/</a></sub></p>
<p>해시 테이블은 데이터를 (Key, Value) 쌍으로 저장하는 자료구조다.
특정 Key를 입력하면, 해시 함수를 거쳐 그에 해당하는 위치(Index)를 바로 찾아내기 때문에
<strong>검색 속도가 O(1)</strong>로, 사실상 ‘즉시’ 데이터를 꺼낼 수 있다.</p>
<p>DB 인덱스에 해시 테이블이 도입될 때는
“컬럼 값(데이터)” → “데이터의 실제 위치”
이렇게 맵핑하는 방식으로 활용되는데,
특정 값을 가진 데이터가 데이터 파일의 어디에 저장되어 있는지
해시 테이블에 기록해 두고,
이후 해당 값으로 검색할 때 아주 빠르게 찾을 수 있다.</p>
<p>하지만 아쉽게도 데이터 베이스에서 해시 기반 인덱스는 잘 쓰이지 않는다.
바로 “동등(=) 비교”에만 특화되어 있기 때문이다.</p>
<p>해시 함수의 원리는
Key 값이 조금이라도 다르면 완전히 다른 해시값을 만들어내기 때문에
<strong>“값이 딱 일치하는 경우”</strong>에는 매우 빠르지만,
“범위 검색”이나 “LIKE ‘가나다%’”
처럼 부등호/와일드카드/범위 연산이 들어가면
해시 인덱스는 효율이 떨어지고,
결국 모든 데이터를 다 뒤져야 하기 때문이다.</p>
<p>이런 한계 때문에
DBMS에서는 실제로 범위 탐색, 정렬 등 다양한 쿼리가 자주 쓰이는 현실을 고려해
B+Tree 구조가 표준 인덱스로 선택 되었다.</p>
<p>그래서 DBMS는 <strong>B+Tree 구조</strong>를 주로 사용한다.</p>
<hr>
<h2 id="인덱스의-자료구조-b-tree와-btree">인덱스의 자료구조: B-Tree와 B+Tree</h2>
<h3 id="b-tree-구조">B-Tree 구조</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/f59fe099-0611-4561-bb73-d7149759ed87/image.png" alt="">
<sub> 출처: 코딩애플 </sub></p>
<ul>
<li>B-tree는 이진트리와 거의 유사하지만 한 노드에 여러 개의 데이터를 담을 수 있어<br>index 구현 시 유사한 데이터를 한 노드에 담아 탐색 횟수를 줄여 효율적으로 쓸 수 있다.  </li>
</ul>
<blockquote>
<p>다만, 범위 검색을 할 때는 자식 노드의 범위를 넘어가면 다시 부모노드로 갔다가 다른 범위 노드로 가야 하기 때문에, 데이터를 찾는 과정이 비효율적인 경우가 있다</p>
</blockquote>
<hr>
<h3 id="btree-구조">B+Tree 구조</h3>
<ul>
<li>B-tree에서 진화한 형태  </li>
<li><strong>리프 노드끼리는 Linked List</strong>로 연결되어 있어서<br>범위 검색도 효율적이다.</li>
<li>데이터베이스 인덱스에 가장 널리 쓰이는 구조</li>
</ul>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0c946d9d-8d8a-4062-9f36-7087b7fda439/image.png" alt="">
<sub> 출처: 코딩애플 </sub></p>
<ul>
<li><strong>최하단 노드(리프 노드)에만 실제 데이터(혹은 데이터 주소)가 저장</strong>된다.</li>
<li>그 외의 노드는 <strong>인덱스(가이드) 역할</strong>만 한다.</li>
</ul>
<p>B+tree는 B-tree에서 최하단 노드(리프노드)에만 데이터를 보관한다.
그 외의 노드는 인덱스 노드라고 하며 데이터를 찾기 위한 가이드를 제공한다.<br>그리고, 리프노드 사이는 LinkedList로 연결한다.<br>이를 통해 범위 검색을 효율적으로 할 수 있다.</p>
<hr>
<h2 id="클러스터형-인덱스clustered-index">클러스터형 인덱스(Clustered Index)</h2>
<ul>
<li><p>표로 만든 예시
<img src="https://velog.velcdn.com/images/losiento_nana/post/9601803f-2b1f-4518-93f5-ff3a20c206c5/image.png" alt=""></p>
</li>
<li><p>트리 형태로 바꿔본 예시
<img src="https://velog.velcdn.com/images/losiento_nana/post/a5411a0c-808c-4589-b71a-329413734209/image.png" alt=""></p>
</li>
</ul>
<h3 id="클러스터형-인덱스-핵심-요약">클러스터형 인덱스 핵심 요약</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/51424544-e1c0-4523-9d94-0057133e46c4/image.png" alt=""></p>
<ul>
<li>영어 사전처럼 <strong>책 전체가 정렬되어 있는 구조</strong> (PK 순서)</li>
<li><strong>기본키</strong>로 지정된 컬럼에 자동으로 생성되기 때문에 테이블당 하나만 생성 할 수 있다.</li>
<li>생성될 때 클러스터 인덱스를 기준으로 <strong>데이터가 항상 정렬</strong></li>
<li>클러스터형 인덱스는 내부적으로 <strong>B+Tree</strong> 구조로 만들어진다.</li>
<li>인덱스(상위 노드)는 참조 범위의 첫 번째 값,<br> 오른쪽은 그 범위의 데이터가 있는 페이지 주소</li>
<li>검색할 때는 인덱스 노드를 따라가<br> <strong>리프 노드(=데이터 페이지)에서 실제 데이터</strong>를 바로 찾는다.</li>
<li>데이터가 추가될 때마다 데이터 페이지/루트 페이지 모두 정렬됨 (정렬 2번)</li>
<li><strong>리프 페이지 = 데이터 페이지</strong><br> (리프 페이지가 곧 데이터 그 자체!)</li>
</ul>
<hr>
<h3 id="클러스터-인덱스-생성변화-예시">클러스터 인덱스 생성/변화 예시</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/72f0a84c-4683-462c-995e-f1310a759066/image.png" alt=""></p>
<p>처음에는 테이블에 데이터가 아무런 순서 없이 저장되어 있다.
하지만 클러스터 인덱스를 생성하는 순간, DBMS는 <strong>인덱스 기준이 되는 컬럼(PK 등)</strong>으로
모든 데이터를 자동으로 정렬한다. 이 과정에서 기존에 흩어져 있던 데이터들이
인덱스 기준에 따라 재배치되며, 여러 데이터 페이지로 나뉜다.</p>
<p>각 데이터 페이지는 정렬되고,
페이지마다 그 범위의 첫 번째 값을 대표로 가진다.
그리고 이 대표 값들과 페이지의 주소 정보가 별도의 <strong>루트 페이지(=인덱스 페이지)</strong>에
정리되어 저장된다.</p>
<hr>
<h3 id="클러스터-인덱스-조회단일">클러스터 인덱스 조회(단일)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/011b4dcd-6d46-40bc-9b98-2b2424ca9b8d/image.png" alt=""></p>
<ul>
<li><strong>단일 검색</strong>:특정 값(PK 등)을 찾을 때는
인덱스를 따라가서 해당 데이터가 들어 있는
데이터 페이지(리프 노드)에 곧바로 접근할 수 있다.</li>
</ul>
<hr>
<h3 id="클러스터-인덱스-조회범위">클러스터 인덱스 조회(범위)</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/8f1fabc9-f7bf-4b3a-b443-eb257fcfeaf1/image.png" alt=""></p>
<ul>
<li><strong>범위 검색</strong>: 예를 들어, PK 1000~2000 구간의 데이터를 찾는다면
인덱스에서 시작 위치만 빠르게 찾고,
이후는 Linked List(페이지가 쭉 연결된 구조)로
한 번에 이어서 데이터를 순차적으로 읽어올 수 있다.</li>
</ul>
<hr>
<h3 id="클러스터-인덱스를-이용한-데이터-삽입">클러스터 인덱스를 이용한 데이터 삽입</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/d12f8698-feb5-45b9-9b37-cb94b034852e/image.png" alt=""></p>
<p>데이터를 삽입할 때는
단순히 마지막에 추가되는 게 아니라,
인덱스 정렬 기준에 따라 자동으로 위치가 조정된다.</p>
<p>새로운 데이터가 들어오면,</p>
<p>해당 데이터가 들어가야 할 위치를 찾아서 삽입,</p>
<p>만약 한 페이지가 꽉 차면,
페이지 분할이 일어나고,</p>
<p>인덱스(루트/상위 노드) 정보도 같이 조정된다.</p>
<p>즉, 데이터 정렬을 계속 유지해야 하므로
INSERT/UPDATE의 비용이 다소 늘어날 수 있다.</p>
<hr>
<h2 id="보조-인덱스secondary-index-non-clustered-index">보조 인덱스(Secondary Index, Non-Clustered Index)</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/dbcca8bc-45d9-4f85-a916-6c2bc6695e25/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/7cbaf3de-aa44-4f5a-8fc1-f6da870218ee/image.png" alt=""></p>
<h3 id="보조-인덱스-특징">보조 인덱스 특징</h3>
<ul>
<li><strong>책의 찾아보기(색인)</strong>처럼</li>
<li><em>인덱스 키 값 → 실제 데이터 주소(ROW POINTER or PK)*</em>만 보유한다.</li>
<li>한 테이블에 <strong>여러 개 생성</strong> 가능</li>
<li>실제 데이터는 클러스터 인덱스(PK) 순서로만 저장</li>
<li>데이터의 추가/삭제/수정 시에는 인덱스만 갱신하고
실제 테이블 데이터의 정렬에는 영향을 주지 않는다.</li>
<li>리프 페이지는 데이터페이지의 값을 바로 참조한다.</li>
<li>데이터페이지와 리프페이지가 나뉘어 있다.</li>
<li>데이터는 테이블에 순서 없이 추가되지만,</li>
<li>리프 페이지와 데이터 페이지는 서로 완전히 분리되어 있다.
(리프 페이지에는 인덱스 값/주소만, 데이터 페이지에는 실제 레코드 전체)</li>
</ul>
<ul>
<li>리프 페이지에서는 데이터 페이지의 주소를 <strong>정렬된 상태로</strong> 관리 (정렬 1번)</li>
</ul>
<hr>
<h2 id="클러스터-인덱스--보조-인덱스-혼합-동작">클러스터 인덱스 + 보조 인덱스 혼합 동작</h2>
<p>실제로 쿼리문을 사용하면<br>대부분 두 인덱스를 혼합해서 쓰게 된다.</p>
<pre><code class="language-sql">SELECT *
FROM 학생
WHERE 이름 = &#39;홍길동&#39;;
</code></pre>
<ul>
<li>기존의 표로 예시
<img src="https://velog.velcdn.com/images/losiento_nana/post/67981c9e-2a81-4d21-af1d-3a8b102682ea/image.png" alt=""></li>
</ul>
<p><strong>동작 절차</strong></p>
<ol>
<li><strong>보조 인덱스(B+Tree, 이름 인덱스)에서 이름을 찾는다</strong></li>
<li><strong>해당 이름에 연결된 학번(Primary Key, row pointer)을 찾는다</strong></li>
<li><strong>클러스터형 인덱스(Primary Index, 학번 인덱스)에서 실제 레코드를 찾는다</strong></li>
<li><strong>실제 데이터(행 전체)를 읽어 결과로 반환한다</strong></li>
</ol>
<blockquote>
<p>참고: 보조 인덱스는 데이터의 주소값을 갖고 있다.<br>클러스터형 인덱스에 데이터가 늘어나면 보조 인덱스 구조도 계속 바뀐다.<br>인덱스를 혼합해서 쓸 때에는 보조 인덱스의 구조가 바뀌어야 할 수 있고,<br>데이터 검색 시 손해를 볼 수도 있지만<br>데이터를 수정/삭제할 때 효율적으로 관리할 수 있다.  </p>
<p><strong>모두 제거해야 할 때 보조 인덱스부터 삭제해야 하는 이유:</strong><br>보조 인덱스는 클러스터형 인덱스를 참조하는 구조이므로<br>클러스터형 인덱스를 먼저 지우면<br>보조 인덱스의 구조가 깨진다.</p>
</blockquote>
<hr>
<h2 id="클러스터-인덱스와-보조-인덱스-한눈에-비교">클러스터 인덱스와 보조 인덱스, 한눈에 비교</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/6e24932f-9465-48a3-bcf0-1813377e2614/image.png" alt=""></p>
<hr>
<h2 id="인덱스의-성능과-고려사항">인덱스의 성능과 고려사항</h2>
<ul>
<li><strong>검색 속도</strong><br>인덱스 덕분에 수십~수억 건 데이터에서도 logN의 속도로 매우 빠르게 원하는 레코드를 찾을 수 있다.</li>
<li><strong>범위 검색/정렬/집계</strong>에도 매우 효율적이다.</li>
<li><strong>데이터 무결성, 검색 최적화</strong> 등 실무에서는 필수다.</li>
</ul>
<h3 id="그러나-단점도-있다-인덱스-유형별-성능-차이">그러나 단점도 있다! (인덱스 유형별 성능 차이)</h3>
<ul>
<li><p><strong>인덱스가 많아질수록</strong></p>
<ul>
<li><strong>INSERT/UPDATE/DELETE(쓰기 연산)</strong> 시 모든 인덱스를 함께 갱신해야 하므로 <strong>쓰기 성능이 저하</strong>된다.</li>
<li><strong>클러스터 인덱스(Primary/PK 기반)</strong>의 경우<br>데이터 자체가 인덱스 구조에 맞게 <strong>정렬되어 저장</strong>되기 때문에<br><strong>삽입/삭제/수정 시 더 많은 비용</strong>이 든다.</li>
<li><strong>보조 인덱스(Secondary/Non-Clustered)</strong>는  
실제 데이터(테이블)와 별도로 <strong>인덱스만 정렬</strong>하므로<br>데이터는 추가/삭제가 빠르지만,<br>인덱스 갱신(정렬, 포인터 변경) 오버헤드는 여전히 존재한다.</li>
</ul>
</li>
<li><p><strong>조회(SELECT) 성능은 반대로</strong></p>
<ul>
<li><strong>클러스터 인덱스</strong>는  
인덱스를 타고 곧장 실제 데이터 페이지(리프 노드)까지 바로 접근<br>→ <strong>조회가 아주 빠르다</strong> (특히 PK/순차적 범위 조회)</li>
<li><strong>보조 인덱스</strong>는  
인덱스에서 PK(ROW_POINTER)만 찾고<br>한 번 더 PK(클러스터 인덱스)를 타고 데이터를 읽으러 가야 해서<br>→ <strong>조금 더 느릴 수 있다</strong> (특히 랜덤/비순차 조회)</li>
</ul>
</li>
<li><p>인덱스 자체가 별도의 파일/디스크 공간을 차지<br>→ 저장 공간 관리 필요</p>
</li>
<li><p>불필요한 인덱스를 많이 만들면 오히려 성능이 저하<br>→ <strong>정기적으로 인덱스 점검, 적정 개수 유지</strong> 필요</p>
</li>
</ul>
<h4 id="실무에서는-이렇게-활용한다">실무에서는 이렇게 활용한다!</h4>
<ul>
<li><strong>쓰기(INSERT/UPDATE/DELETE)</strong>가 아주 잦은 테이블<br>→ <strong>불필요한 인덱스를 최소화</strong> (특히 보조 인덱스 남발 X)</li>
<li><strong>읽기(SELECT/조회)</strong>가 압도적으로 많은 테이블<br>→ <strong>클러스터 인덱스, 필요한 보조 인덱스 적극 활용</strong></li>
<li>인덱스 컬럼은 WHERE/조인/정렬/그룹핑 등에서<br><strong>자주 쓰이는 컬럼</strong>에만 설정</li>
<li><strong>Composite Index(복합 인덱스)</strong>도 효율적이나,<br>“왼쪽부터 연속적으로 조건”이 붙을 때만 100% 효과가 있으니<br><strong>조건 순서/사용 패턴</strong>을 고려해서 설계해야 한다</li>
</ul>
<hr>
<h2 id="마치며">마치며</h2>
<p>인덱스는 단순히 성능 향상을 넘어, 대규모 데이터베이스 운영에서 필수적인 요소임을 새삼 느꼈습니다.<br>이번 글을 준비하면서 저 역시 “인덱스가 실제로 어느 정도 성능 차이를 만들어낼까?”가 궁금해졌는데요.</p>
<p>실제로는 단순히 인덱스를 건다고 무조건 빠른 건 아니고,<br>테이블 구조·데이터 양·쿼리 패턴에 따라 인덱스 효과가 천차만별이라고 합니다.</p>
<p>그래서 직접 샘플 데이터를 mysql 공홈에서 받아서<br>인덱스의 유무, 인덱스 종류(B+Tree, 해시 등),<br><strong>여러 쿼리로 실험</strong>해보고 싶습니다. 
가능하면 진행하고 벨로그에 올리겠습니닷. <del>(가능하면..</del>)</p>
<hr>
<p><strong>참고 레퍼런스</strong></p>
<ul>
<li><a href="https://joinwithyou.tistory.com/87">인덱스 (feat. 인덱스의 구조 및 내부작동)</a></li>
<li><a href="https://inpa.tistory.com/entry/MYSQL-%F0%9F%93%9A-%EC%9D%B8%EB%8D%B1%EC%8A%A4index-%ED%95%B5%EC%8B%AC-%EC%84%A4%EA%B3%84-%EC%82%AC%EC%9A%A9-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC">[MYSQL] 📚 인덱스(index) 핵심 설계 &amp; 사용 문법 💯 총정리</a></li>
<li><a href="https://developers-haven.tistory.com/m/55">SQL튜닝 - 인덱스의 종류에 대하여 (클러스터/비클러스터 인덱스)
</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[혹시 '데이터 베이스를' 아십니까 ? #1 – 데이터베이스란?]]></title>
            <link>https://velog.io/@losiento_nana/1.-%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EC%B4%88-</link>
            <guid>https://velog.io/@losiento_nana/1.-%ED%98%B9%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%95%84%EC%8B%AD%EB%8B%88%EA%B9%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B8%B0%EC%B4%88-</guid>
            <pubDate>Tue, 01 Jul 2025 03:37:39 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="목차">목차</h2>
<ul>
<li><a href="#%EC%84%9C%EB%A1%A0">서론</a></li>
<li><a href="#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0">데이터베이스를 사용하는 이유</a></li>
<li><a href="#dbms%EC%9D%98-%EC%A2%85%EB%A5%98-%EB%B0%8F-%ED%8A%B9%EC%A7%95">DBMS의 종류 및 특징</a><ul>
<li><a href="#1-%EA%B4%80%EA%B3%84%ED%98%95-dbms-rdbms">1. 관계형 DBMS (RDBMS)</a></li>
<li><a href="#2-%EB%B9%84%EA%B4%80%EA%B3%84%ED%98%95nosql-dbms">2. 비관계형(NoSQL) DBMS</a></li>
</ul>
</li>
<li><a href="#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%A4%EA%B3%84%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%9A%94%EC%86%8C-%EB%85%BC%EB%A6%AC-vs-%EB%AC%BC%EB%A6%AC">데이터베이스 설계의 기본 요소 (논리-vs-물리)</a><ul>
<li><a href="#1-%EB%85%BC%EB%A6%AC%EC%A0%81-%EB%AA%A8%EB%8D%B8%EB%A7%81">1. 논리적 모델링</a></li>
<li><a href="#2-%EB%AC%BC%EB%A6%AC%EC%A0%81-%EB%AA%A8%EB%8D%B8%EB%A7%81">2. 물리적 모델링</a></li>
</ul>
</li>
<li><a href="#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%98-%EC%9D%B4%EC%A0%90-%EB%AC%B4%EA%B2%B0%EC%84%B1-%EB%B3%B4%EC%95%88-%EB%B0%B1%EC%97%85-%EB%B0%8F-%EC%9E%AC%ED%95%B4-%EB%B3%B5%EA%B5%AC-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EC%A7%80%EC%9B%90-%EB%93%B1%EB%93%B1">데이터베이스의 이점</a><ul>
<li><a href="#1-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%AC%B4%EA%B2%B0%EC%84%B1">1. 데이터 무결성</a></li>
<li><a href="#2-%EB%B3%B4%EC%95%88">2. 보안</a></li>
<li><a href="#3-%EB%B0%B1%EC%97%85-%EB%B0%8F-%EC%9E%AC%ED%95%B4-%EB%B3%B5%EA%B5%AC">3. 백업 및 재해 복구</a></li>
<li><a href="#4-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EC%A7%80%EC%9B%90-%EB%B0%8F-%EC%83%9D%ED%83%9C%EA%B3%84">4. 커뮤니티 지원 및 생태계</a></li>
</ul>
</li>
<li><a href="#reference">Reference</a></li>
</ul>
<hr>
<h2 id="서론">서론</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/671e5ace-db79-4759-986b-a4d9e24f2c7a/image.jpg" alt="">
<sub>출처: <a href="https://www.okoone.com">https://www.okoone.com</a></sub></p>
<p>요즘 데이터베이스라는 용어를 모르는 사람은 잘 없을 거다.<br>그렇다면 데이터베이스란 뭘까? 언뜻 보면 굉장히 기술적으로 어렵게 느껴질 수 있지만, 사실은 생각보다 간단하다.<br>데이터베이스란 한마디로 체계적으로 구조화되어 저장하고, 관리하는 데이터의 집합을 의미한다.</p>
<p>주변을 보면 엑셀이나 구글시트에 가계부, 출석부, 명단 같은 장부를 적어놓는 사람도 많다.<br>이렇게 작성된 장부 역시 넓은 의미에서 보면 데이터베이스다.<br>다만, 우리가 보통 컴퓨터 분야에서 이야기하는 데이터베이스는, DBMS(데이터베이스 관리 시스템)라는 별도의 소프트웨어를 이용해 훨씬 더 체계적이고 안정적으로 데이터를 저장·관리하는 시스템을 말한다.</p>
<p>그렇다면 엑셀이나 구글시트로 장부를 관리하는 것과, 기업들이 사용하는 <strong>DBMS(Database Management System)</strong>로 데이터를 관리하는 것은 도대체 뭐가 다를까?<br>한 번쯤 이런 궁금증이 들었을 것이다.</p>
<p>“그냥 스프레드시트(구글시트, 엑셀)나 CSV 파일로 데이터 관리하면 충분하지 않나?”<br>“굳이 복잡한 데이터베이스(DBMS)를 왜 따로 써야 하지?”</p>
<p>이 글에서는 그 궁금증을 시작점으로, 데이터베이스가 뭔지, 왜 실무에서는 DBMS를 써야 하는지, 그리고 실제 현장에서 어떻게 활용되는지 기초부터 하나씩 정리해보려고 한다.</p>
<hr>
<h2 id="데이터베이스를-사용하는-이유">데이터베이스를 사용하는 이유</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0b9b8f80-ab8a-4b13-b2de-c9c34270245d/image.png" alt=""></p>
<p>그렇다면 정말 스프레드시트(구글시트, 엑셀)나 CSV 같은 파일로도 충분하지 않을까?<br>왜 기업이나 서비스들은 굳이 DBMS라는 별도의 시스템을 사용하려는 걸까?</p>
<p>이 질문에는 기술적으로 중요한 이유들이 몇 가지 존재한다.</p>
<p><strong>스프레드시트와 CSV 파일, 그리고 DBMS</strong><br>이 세 가지 방식은 각각 분명한 장점과 한계가 있다.</p>
<ul>
<li><p><strong>스프레드시트/CSV</strong>는 소규모 데이터나 임시 관리에는 편리하지만,<br>여러 명이 동시에 데이터를 쓰거나, 대량의 데이터를 빠르고 안전하게 관리할 때는 한계가 있다.<br>권한 관리, 무결성(정확성·일관성) 보장, 백업과 복구, 대용량 처리, 빠른 검색·집계 기능 등에서 한계가 뚜렷하다.<br>특히 실시간 동시 접근, 자동화, 데이터 일관성·정합성 체크 같은 부분은 사실상 불가능하다.</p>
</li>
<li><p><strong>DBMS</strong>는 이런 문제를 해결하기 위해 나온 시스템이다.<br>DBMS는 대규모 데이터도 빠르고 안정적으로 관리할 수 있게 해주며,<br>여러 명이 동시에 접속해서도 데이터가 깨지지 않도록 보호하고,<br>데이터의 정확성과 일관성을 지키는 여러 기능을 기본적으로 제공한다.<br>또한 DBMS는 트랜잭션 처리, 자동 백업, 세밀한 접근 권한 등 스프레드시트에서 제공할 수 없는 기능을 갖추고 있다.</p>
</li>
</ul>
<p>따라서 실무나 서비스 환경에서는,<br>단순 파일이나 스프레드시트로는 한계가 뚜렷한 부분을 DBMS가 강력하게 해결해주기 때문에<br>기업이나 조직에서 DBMS를 도입해 사용하는 것이다.</p>
<hr>
<h2 id="dbms의-종류-및-특징">DBMS의 종류 및 특징</h2>
<p>DBMS(데이터베이스 관리 시스템)는 데이터를 어떻게 저장하고 관리하는지에 따라<br><strong>관계형(Relational, RDBMS)</strong>과 <strong>비관계형(Non-Relational, NoSQL)</strong> 두 가지로 나눌 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/63fe3fd6-177a-4251-80bf-de80137b85f6/image.svg" alt="">
<sub>출처: <a href="https://www.nisum.com/">https://www.nisum.com/</a></sub></p>
<h3 id="1-관계형-dbms-rdbms">1. 관계형 DBMS (RDBMS)</h3>
<p>관계형 DBMS는 엑셀처럼 행과 열로 이루어진 ‘테이블’ 구조를 사용한다.<br>각 행은 개별 레코드를 나타내고, 각 열은 값이 저장되는 속성을 의미한다.<br>이런 테이블 구조를 사용하면, 데이터 간의 관계를 쉽게 표현할 수 있고,<br>여러 테이블을 서로 연결해서 다양한 방식으로 데이터를 조회할 수 있다.</p>
<p><strong>관계형 데이터베이스의 특징</strong></p>
<ul>
<li>데이터를 저장하기 전에 데이터의 구조(스키마)를 먼저 정의하고, 그에 맞게 데이터를 입력해야 한다.  </li>
<li>특정한 형식과 제약조건을 통해, 데이터의 정확성과 일관성을 보장한다.</li>
<li>다양한 쿼리(SQL)로 원하는 데이터를 쉽게 조회·분석할 수 있다.</li>
<li>대표적인 관계형 DBMS: <strong>MySQL, MariaDB, Oracle, PostgreSQL</strong></li>
</ul>
<blockquote>
<p><strong>엑셀은 한 번에 하나의 표(테이블)만 관리하는 게 기본이며, 표(시트) 간의 관계·연결이 DBMS만큼 강력하지 않다.<br>데이터가 많아질수록 속도가 급격히 느려지고, 여러 표의 정보를 조합·분석하는 것도 제약이 많다.</strong></p>
</blockquote>
<h3 id="2-비관계형nosql-dbms">2. 비관계형(NoSQL) DBMS</h3>
<p>비관계형 DBMS는 테이블 구조가 아닌 다양한 형태로 데이터를 저장한다.<br>대표적으로 키-값(key-value), 문서(document), 그래프(graph), 컬럼(column) 등 여러 구조를 지원한다.</p>
<ul>
<li>데이터 구조가 유연해서, 고정된 스키마 없이 자유롭게 데이터를 저장할 수 있다.</li>
<li>대용량 데이터, 비정형 데이터(예: 로그, 센서 데이터, SNS 글) 저장과 실시간 처리에 강점을 가진다.</li>
<li>저장 방식에 따라, SQL이 필요 없는 경우도 많아서 NoSQL 데이터베이스라고도 불린다.</li>
<li>데이터를 읽을 때 스키마에 맞춰 해석하는 ‘schema on read’ 방식을 많이 쓴다.</li>
<li>대표적인 NoSQL DBMS: <strong>MongoDB(문서), Redis(키-값)</strong></li>
</ul>
<blockquote>
<p><strong>엑셀·구글시트로는 JSON, 그래프, 대용량 로그 등 다양한 데이터 형태를 효율적으로 관리하기 어렵다.<br>데이터가 유동적이거나 형태가 자주 바뀌는 경우에는 DBMS가 압도적으로 유리하다.</strong></p>
</blockquote>
<hr>
<h2 id="데이터베이스-설계의-기본-요소-논리-vs-물리">데이터베이스 설계의 기본 요소 (논리 vs 물리)</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/85a9c478-edd3-49df-a85e-70ea21376f2a/image.png" alt="">
<sub>데이터 베이스 요구사항 명세서 예시</sub></p>
<p>데이터베이스를 설계할 때, 가장 먼저 해야 할 일은<br><strong>비즈니스의 목적과 요구사항을 꼼꼼하게 분석하는 것</strong>이다.</p>
<p>이 단계가 데이터베이스 설계에서 사실상 가장 중요하고,<br>실제로도 가장 시간이 많이 걸리는 부분이라고 생각한다.</p>
<p>현실의 비즈니스 로직에는 정말 다양한 변수가 존재하고,<br>중간에 요구사항이 바뀌는 경우도 흔하다.</p>
<p>그럼에도 불구하고, <strong>핵심적으로 반드시 지켜야 할 제약조건</strong>과 <strong>필수 요구사항</strong>은  
웬만하면 초기에 명확히 정해두는 게 중요하다.</p>
<p>이 단계에서 요구사항을 제대로 정리해 두어야<br>이후 데이터베이스 구조를 설계할 때 시행착오를 줄일 수 있다.</p>
<blockquote>
<p><strong>엑셀에서는 설계 변경 시 표 전체를 복사하거나, 데이터가 뒤섞이기 쉽고<br>여러 사람이 동시에 구조를 바꾸면 데이터가 깨지기 쉽다.<br>반면 DBMS는 구조 설계와 변경, 그리고 그 기록까지 체계적으로 관리한다.</strong></p>
</blockquote>
<p>요구사항을 충분히 분석하고 정리했다면,<br>다음 단계는 바로 <strong>논리적 모델링</strong>이다.</p>
<h3 id="논리적-모델링">논리적 모델링</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/a448bf7a-1c3f-4857-8ba7-1fa10d9eee87/image.png" alt="">
<sub>개인 프로젝트에서 쓰일 임시로 만든 ERD</sub></p>
<p>논리적 모델링 단계에서는<br>주로 <strong>ERD(엔터티-관계 다이어그램)</strong>를 그려서 데이터 구조를 시각적으로 설계한다.</p>
<p>이때는 실제 데이터 타입이나 저장 방식보다는<br><strong>어떤 데이터(엔티티)가 존재하는지, 그리고 이 데이터들이 서로 어떻게 연결되어 있는지</strong>에 집중한다.</p>
<p>데이터베이스에서 생성되는 객체를 <strong>엔티티(Entity)</strong>라고 부르고,<br>각 엔티티가 어떤 속성(필드)을 가지는지,<br>엔티티들 사이에 1:1, 1:N, N:M 관계가 어떻게 형성되는지를 한눈에 볼 수 있게 만든다.</p>
<p>이 과정을 통해 전체 데이터의 구조와 관계가 명확하게 정리되고,<br>비즈니스 로직이 데이터 구조에 자연스럽게 녹아들 수 있다.</p>
<blockquote>
<p><strong>엑셀에서는 이런 관계와 연결을 시각적으로 그려서 강제할 방법이 거의 없다.<br>반면 DBMS에서는 ERD 등으로 관계를 설계하고, 시스템이 스스로 그 관계를 관리한다.</strong></p>
</blockquote>
<h3 id="물리적-모델링">물리적 모델링</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/7bf23b64-edf6-4032-9d5a-365c0577071b/image.png" alt="">
<sub>ERD를 실제 테이블로 생성하는 쿼리문 예시</sub></p>
<p>논리 모델이 완성되면,<br>그걸 실제 DBMS에서 동작할 수 있도록 테이블, 컬럼, 데이터 타입, 인덱스, 제약조건 등으로 구체화하는 단계가 <strong>물리적 모델링</strong>이다.</p>
<p>각 테이블의 컬럼 타입, 길이, PK/FK(기본키/외래키), 인덱스 설정 등<br>세부적으로 조정하며, 실제 시스템에 맞게 설계를 마무리한다.</p>
<p>이 과정은 데이터베이스의 <strong>성능</strong>이나 <strong>보안</strong>에 큰 영향을 줄 수 있기 때문에,<br>설계 시에 상당한 주의가 필요하다.</p>
<p>예를 들어, 데이터 양이 많아질수록 인덱스를 적절히 설정하지 않으면 조회 성능이 급격히 떨어질 수 있고,<br>민감한 정보는 암호화하거나 접근 권한을 세분화하는 등 실무적인 고려가 반드시 들어가야 한다.</p>
<blockquote>
<p><strong>엑셀/CSV로는 수십만, 수백만 건 데이터에 빠르게 접근하거나<br>민감 정보에 대한 세부 권한, 인덱스·최적화 등은 사실상 구현이 어렵다.<br>DBMS는 이를 손쉽게 설계하고 적용할 수 있다.</strong></p>
</blockquote>
<hr>
<h2 id="데이터베이스의-이점-무결성-보안-백업-및-재해-복구-커뮤니티-지원-등등">데이터베이스의 이점 (무결성, 보안, 백업 및 재해 복구, 커뮤니티 지원 등등)</h2>
<p>앞에서 왜 DBMS를 사용하는지 다뤄봤지만,<br>좀 더 구체적으로, <strong>DBMS 기반 데이터베이스가 제공하는 핵심적인 이점</strong>들을 하나씩 살펴보자.</p>
<p>스프레드시트나 CSV 파일로는 얻기 힘든,<br>DBMS만의 강력한 장점이 아래와 같다.</p>
<h3 id="1-데이터-무결성">1. 데이터 무결성</h3>
<p>데이터베이스는 단순히 데이터를 저장하는 것으로 끝나지 않는다.<br>가장 큰 장점 중 하나는 바로 <strong>데이터 무결성(Integrity)</strong>, 즉 데이터의 정확성, 일관성, 완전성을 보장할 수 있다는 점이다.</p>
<p>관계형 데이터베이스에서는<br><strong>프라이머리 키(Primary Key), 외래 키(Foreign Key), Not NULL, Unique, Default, Check</strong> 같은<br>다양한 제약조건(Constraint)을 통해,<br>잘못된 데이터 입력을 원천적으로 막을 수 있다.</p>
<p>이런 무결성 제약조건 덕분에, 데이터베이스에 저장된 정보는 언제나 신뢰할 수 있다.<br>비즈니스의 중요한 규칙(예: “회원 이메일은 반드시 고유해야 한다”, “나이는 0~120세만 허용” 등) 역시<br>시스템적으로 강제할 수 있다.</p>
<p>필요하다면 트리거나 저장 프로시저 같은 고급 기능을 활용해,<br>특정 상황에 맞는 추가 로직도 쉽게 구현할 수 있다.</p>
<blockquote>
<p><strong>엑셀/CSV는 이런 제약조건이 없기 때문에, 같은 데이터가 중복 입력되거나<br>잘못된 값이 들어가는 걸 막기가 어렵다.<br>DBMS는 구조적으로 이걸 방지해준다.</strong></p>
</blockquote>
<h3 id="2-보안">2. 보안</h3>
<p>DBMS는 <strong>정교한 권한 관리와 접근 제어</strong> 기능을 내장하고 있다.</p>
<p>관리자는 “누가”, “어떤 데이터에”, “어떤 작업(읽기/쓰기/삭제 등)”을 할 수 있는지<br>아주 세밀하게 지정할 수 있다.</p>
<p>DBMS에 따라서는 “셀 단위”까지 권한을 제한하는 것도 가능하다.<br>덕분에 민감한 데이터나 개인정보를 안전하게 보호할 수 있고,<br>다수의 사용자가 협업하는 환경이나, 외부 침입/유출로부터 데이터를 지켜야 하는 상황에서<br>DBMS의 권한 관리가 큰 역할을 한다.</p>
<blockquote>
<p><strong>스프레드시트의 공유 기능은 매우 단순해서, 전체 접근 권한이나 읽기/편집 정도만 나뉠 뿐이다.<br>세부적인 보안·접근 제어, 감사 로그, 암호화 등은 구현이 거의 불가능하다.</strong></p>
</blockquote>
<h3 id="3-백업-및-재해-복구">3. 백업 및 재해 복구</h3>
<p>실수, 해킹, 장애 등으로부터 데이터를 안전하게 보호하려면<br>정기적인 <strong>백업</strong>과, 문제가 발생했을 때 빠르게 복구할 수 있는 <strong>재해 복구(Recovery)</strong> 기능이 반드시 필요하다.</p>
<p>DBMS는 내장된 <strong>백업/복원 기능</strong>,  
실시간 데이터 미러링, 트랜잭션 로그 등 다양한 방법을 제공해서<br>데이터 손실 위험을 최소화할 수 있다.</p>
<p>특히 최근에는 클라우드 DBMS도 많이 사용하면서,<br>수초 만에 데이터를 복원하거나<br>지속적으로 여러 장소에 데이터를 백업·보관하는 것도 어렵지 않다.</p>
<blockquote>
<p><strong>엑셀/CSV로는 사람이 직접 복사하거나 저장본을 만들어야 하고,<br>실시간 백업이나 장애 복구는 기대하기 힘들다.<br>DBMS는 이런 기능이 내장되어 있어 장애에도 빠르게 대응할 수 있다.</strong></p>
</blockquote>
<h3 id="4-커뮤니티-지원-및-생태계">4. 커뮤니티 지원 및 생태계</h3>
<p>많은 관계형 데이터베이스(DBMS)는 오픈 소스이거나,<br>전 세계적으로 커뮤니티가 활발하게 운영되고 있다.</p>
<p>예를 들어 <strong>MySQL, MariaDB, PostgreSQL</strong> 같은 데이터베이스는<br>무료로 사용할 수 있을 뿐 아니라,<br>각종 플러그인, 튜토리얼, 문제 해결 경험, 도구 등이 공유되고 있다.</p>
<p>이 덕분에 데이터베이스를 처음 배우거나,<br>실제 실무에 도입하는 과정에서도<br>풍부한 자료와 커뮤니티의 도움을 받아 쉽게 시작하고 성장할 수 있다.</p>
<blockquote>
<p><strong>스프레드시트나 CSV 파일은 도구에 따라 자료·생태계가 제한적이고,<br>문제 해결이나 확장성 면에서 DBMS와 비교하기 어렵다.</strong></p>
</blockquote>
<hr>
<p>이렇게 데이터베이스와 DBMS의 기본 개념, 그리고 그 필요성과 실질적인 장점까지 정리해봤다.<br>추후에는 인덱스,정규화,트렌젝션,교착상태,SQL보안,NoSQL등<br>더 다양한 내용을 심화 주제로 다룰 예정이다.<br>관심 있다면 다음 글도 계속 확인해주길 바란다.</p>
<hr>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://blog.airtable.com/what-is-a-database/?utm_source=chatgpt.com">What is a database? Everything you need to know.</a></li>
<li><a href="https://ittrue.tistory.com/195">IT is True:티스토리</a></li>
<li><a href="https://aws.amazon.com/ko/compare/the-difference-between-relational-and-non-relational-databases/">관계형 데이터베이스와 비관계형 데이터베이스의 차이점은 무엇인가요?
</a></li>
<li><a href="https://aws.amazon.com/ko/relational-database/">관계형 데이터베이스란 무엇인가요?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[한화 BEYOND SW 캠프 18기 [2주차] & 나의 개발 성장 회고]]></title>
            <link>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%EB%82%98%EC%9D%98-%EA%B0%9C%EB%B0%9C-%EC%84%B1%EC%9E%A5-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 29 Jun 2025 14:04:00 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-한화-beyond-sw-캠프-2주차-wil-weekly-reflection">✨ 한화 BEYOND SW 캠프 [2주차] WIL (Weekly Reflection)</h1>
<blockquote>
<p>이 글은 BEYOND SW 캠프 2주차 동안의 성장과 배움을 스스로 돌아보고 기록하기 위한 회고입니다.</p>
</blockquote>
<hr>
<h2 id="🔗-이번주-캠프-학습노트정리">🔗 이번주 캠프 학습노트/정리</h2>
<blockquote>
<p>이번주 수업 중 새롭게 배운 내용이나, 직접 정리해 본 개념/기술에 대해<br>벨로그에 따로 아카이브해두었습니다.<br>(관심 있는 분들은 아래 링크에서 자세히 확인해 주세요)</p>
</blockquote>
<blockquote>
<p>이번주에는 주로 쿼리문에 대해 학습했는데, 그 부분에 대해서는 아직 정리하지 않았습니다. 추후 직접 문법을 다시 정리하고 관련된 문제를 풀면서 정리해서 올리도록 하겠습니다. 그 외에 개발 환경 세팅 도중 배운 점에 대해 따로 정리했습니다.</p>
</blockquote>
<ul>
<li><a href="https://velog.io/@losiento_nana/Docker-MariaDB-Ubuntu-Sequel-Ace-%EA%B8%B0%EB%B0%98-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%EA%B8%B0">Docker (MariaDB + Ubuntu) + Sequel Ace 기반 개발환경 구축기</a></li>
</ul>
<hr>
<h2 id="📅-이번-주-한-일-facts">📅 이번 주 한 일 (FACTS)</h2>
<ul>
<li>알고리즘, CS 스터디 가입</li>
<li>첫 PCCE 시험</li>
<li>첫번째 프로젝트 아이디어 회의 및 기본적인 틀 잡기</li>
<li>프로그래머스 기반 코딩테스트 공부</li>
<li>2주차 Maria DB를 사용하여 각종 쿼리문 학습 수업</li>
</ul>
<hr>
<h2 id="💭-느낀-점감정-feelings">💭 느낀 점/감정 (FEELINGS)</h2>
<ul>
<li><p>CS와 알고리즘 스터디에 가입 했는데, 본격적인 활동은 다음주부터 시작하게 될텐데, 모두 포기하지 않고 꾸준히 했으면 좋겠다고 생각한다. 코딩테스트를 통과하는 것은 어떤 회사를 지원하든 꼭 기본으로 갖춰야 할 자격이기 때문에 어렵지만 하루하루 조금씩이라도 꾸준히 해야 겠다고 생각이 든다.</p>
</li>
<li><p>PCCE를 보면서 느낀건 시험 점수가 생각보다 낮게 나왔고, 내가 아직 얼마나 많이 부족한지 스스로의 위치를 객관적으로 볼 수 있는 기회였던 것 같다. 사실 팩트로 폭행하는게 가장 아픈것처럼 내 실력에 스스로 실망을 많이 했다. 완벽하게 맞췄다고 생각한 문제에서도 감점이 된 것을 보고 더 꼼꼼히 문제를 풀어야 되겠다는 생각도 들었다. 이 부트캠프를 졸업 할때에는 PCCP 3레벨을 목표로 앞으로도 꾸준히 문제를 풀고 깃헙에 잔디를 심어야겠다.</p>
</li>
<li><p>첫 프로젝트는 현재 수업 과정속에서 배우는 데이터베이스에 대한 실습 프로젝트 였다. 팀원들과 아이디어 회의도 하고 각자 시간을 최대한 맞춰서 아이디어에 대한 구체화를 진행하는 시간을 가졌다. 나에겐 언제나 아이디어 회의가 가장 어려운것 같다.</p>
</li>
<li><p>프로그래머스 상에서 문제들을 기본기 위주로 두서없이 풀었다. 그러다 오늘 깨달은 것은 내가 아는 파트와 모르는 파트가 명확했고, 복잡한 알고리즘보단 기본기 자체에 구멍이 많이 난것을 깨달았다. 다음주부터는 각 파트별로 나눠서 직접 문제를 풀어보면서 내가 어느 파트가 부족한지 직접 알아보는 시간을 갖도록 하겠다.</p>
</li>
<li><p>개발자라면 결국 데이터베이스를 설계하고 설계된 데이터베이스에서 필요한 값을 이리저리 갖고 노는 여유정도는 필요하기에,, 꽤 중요한 파트라고 생각한다. 5개월 전에 SQLD는 자격증을 따면서 공부한 뒤에는 복습을 하지 않았고, 실제로 SQLD는 직접 쿼리를 짜는 경우가 없기 때문에 알고 있는 내용인데 막상 쓰라니까 못 쓰겠더라..<del>(마치 아는 사람 인 줄 알고 인사했는데 모르는 사람이었던 기분)</del> 다음 주에 DB 수업이 끝나기 전에 틈틈히 직접 SQL문제를 풀면서 배운 내용을  내 것으로 만드는 시간을 꼭 갖겠다고 스스로와 약속했다.
그 약속을 지키는 날에는 상단에 SQL 개념 정리와 실습 문제 풀이 과정을 벨로그에 아카이브할 예정이다.</p>
</li>
</ul>
<hr>
<h2 id="🔍-배운-점새롭게-알게-된-것-findings">🔍 배운 점/새롭게 알게 된 것 (FINDINGS)</h2>
<ul>
<li><p>강사님께서 수업중에 과거에 delete문을 잘못 사용하신 경험에 대해 공유해주시면서, 안전하게 select문으로 먼저 확인하는 꿀팁을 알려주셨는데, 사실 이전에 프로젝트 하다가 나 역시 데이터를 거하게 날려먹은 기억이 있어서.. 앞으로 꼭 저 방법을 쓰리라 생각 했다.</p>
</li>
<li><p>기존 프로젝트에서는 JPA를 사용해서 쿼리문을 직접 작성할 일이 없었는데, 회사들마다 공고를 보면 Mybatis를 사용하는 회사들도 아직 많았는데, 만약 쿼리문을 직접 짜야하는 상황을 대비해서 꼭 쿼리문과 친해져야겠다고 생각했다.</p>
</li>
<li><p>자바 컬렉션 프레임워크에 대해 이론으로만 배우고 간단한 문법만 숙지 했었는데, 각 자료구조에 대해 실제로 어떻게 자바로 구현 되어있는지 찾아보면서 공부를 하는 시간을 가졌다. 결국 완벽한 하나의 자료구조는 없기에, 어떤 상황에 어떤걸 써야하는지 이해하기 위해 노력했다.</p>
</li>
</ul>
<hr>
<h2 id="🚧-어려움--문제-problem-or-minus">🚧 어려움 &amp; 문제 (Problem or Minus)</h2>
<ul>
<li><p>해당 수업은 윈도우PC로 진행하는데, 난 기본적으로 MAC 환경으로 오랫동안 작업 해왔기에, 많이 불편했다. 결국 수업에서 설명해주는 내용을 따르지 못하고 스스로 개발 환경을 구축해야 했다.</p>
</li>
<li><p>팀 프로젝트를 위해 아이디어 회의를 하는 시간에 어떤 아이디어를 내야 할지, 그 아이디어를 어떻게 구체화해서 소개해야 하는 어려움이 있었다.</p>
</li>
<li><p>알고리즘 문제를 푸는데 시간복잡도가 터지거나, 인덱스 접근에 관련된 오류가 자주 나왔다.</p>
</li>
</ul>
<hr>
<h2 id="🛠️-해결-방법--시도-try-or-improve">🛠️ 해결 방법 &amp; 시도 (Try or Improve)</h2>
<ul>
<li><p>맥에서도 수업 내용에 맞출 수 있으면서, 내가 배우고 싶었던 기술에 대해 공부하고 Docker을 도입하여 필요한 것들을 컨테이너화 하여서 관리하고, 수업중에 배웠던 SSH를 통해 맥에서 제공되는 애플리케이션을 통해 원격으로 연결하여 수업에 맞춰 진행했다.</p>
</li>
<li><p>먼저 개인적으로 살면서 불편했던 점을 떠올리고, 그 곳에서 아이디를 떠올린뒤에, 그거에 관련된 프로젝트를 진행했던 사람들의 레퍼런스를 보면서문서화와 구체화를 어떻게 진행하는지 참고했다</p>
</li>
<li><p>주로 정렬 파트에서 시간복잡도에서 터진다는 것을 알았고, 다른 정렬들을 찾아보면서 무엇이 어울리는 정렬인지 보고 적용하였다. 또한 인덱스가 내부적으로 어떻게 생성되는지, 내가 어디서 잘못 참조했는지 확인하며 최대한 안전하게 인덱스에 접근하는 방법에 대해 찾아보고 적용했다.</p>
</li>
</ul>
<hr>
<h2 id="🌱-앞으로의-계획--다짐-future">🌱 앞으로의 계획 &amp; 다짐 (Future)</h2>
<ul>
<li>CS 스터디를 통해 공부하고, 개발 하는데 있어서 다양한 관점에서 효율적으로 로직을 설계하는 개발자가 되고싶다. 단순히 코드만 짜는 코더가 아니라 주어진 문제를 효율적으로 해결 하기 위해 다양한 사전 지식을 활용 할 수 있는 그런 개발자로 성장하고 싶다.</li>
<li>알고리즘 및 코테 공부도 열심히 해서 다양한 기업에서 진행하는 코테를 직접 경험하고 통과하고 싶다.</li>
<li>주어진 하루가 너무 짧다. 해야 할 일을 다 못하는 느낌이라서,  헛되이 쓰는 시간을 최대한 줄이고 효율적으로 활용해야겠다.</li>
<li>학우들과 잘 지내고 싶다~</li>
</ul>
<hr>
<h2 id="🙌-한-줄-마무리">🙌 한 줄 마무리</h2>
<blockquote>
<p>하루는 짧고, 일주일은 길다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ 🐳 Docker (MariaDB + Ubuntu) + Sequel Ace 기반 개발환경 구축기
]]></title>
            <link>https://velog.io/@losiento_nana/Docker-MariaDB-Ubuntu-Sequel-Ace-%EA%B8%B0%EB%B0%98-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%EA%B8%B0</link>
            <guid>https://velog.io/@losiento_nana/Docker-MariaDB-Ubuntu-Sequel-Ace-%EA%B8%B0%EB%B0%98-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%EA%B8%B0</guid>
            <pubDate>Wed, 25 Jun 2025 14:24:03 GMT</pubDate>
            <description><![CDATA[<hr>
<blockquote>
<p><strong>이 글은 한화 SW 과정 실습 중 겪은 환경 문제를<br>스스로 Docker를 도입해 해결한 실제 경험과<br>도커의 개념, 구조, 명령어, 실습 환경을<br>기술적으로 정리해 공유하는 글입니다.</strong></p>
</blockquote>
<hr>
<h1 id="목차">목차</h1>
<ol>
<li><a href="#1-docker-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%ED%95%9C-%EB%B2%88-%EB%A7%8C%EB%93%A4%EC%96%B4-%EC%96%B4%EB%94%94%EC%84%9C%EB%82%98-%EB%98%91%EA%B0%99%EC%9D%B4-%EC%8B%A4%ED%96%89">Docker 기본 개념: &quot;한 번 만들어 어디서나 똑같이 실행&quot;</a></li>
<li><a href="#2-docker-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B0%8F-%EA%B5%AC%EC%A1%B0-%EA%B7%B8%EB%A6%BC%EA%B3%BC-%ED%95%A8%EA%BB%98">Docker 동작 원리 및 구조 (그림과 함께)</a></li>
<li><a href="#3-docker%EC%99%80-%EC%8B%A4%EC%A0%9C-%EA%B0%9C%EB%B0%9C%EC%9A%B4%EC%98%81-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%80%EC%B9%98">Docker와 실제 개발/운영 환경에서의 가치</a></li>
<li><a href="#4-%EB%82%98%EB%8A%94-%EC%99%9C-docker%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%96%88%EB%82%98-%EC%8B%A4%EC%8A%B5-%ED%99%98%EA%B2%BD-%EA%B8%B0%EC%A4%80-%EB%AC%B8%EC%A0%9C%EC%A0%90%EA%B3%BC-%ED%95%B4%EA%B2%B0">나는 왜 Docker를 선택했나? (실습 환경 기준, 문제점과 해결)</a></li>
<li><a href="#5-%EC%8B%A4%EC%8A%B5-%ED%99%98%EA%B2%BD-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%ED%8F%AC%ED%8A%B8%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%9D%90%EB%A6%84">실습 환경 아키텍처 (포트/네트워크 흐름)</a></li>
<li><a href="#6-%EC%8B%A4%EC%A0%9C-%EC%84%A4%EC%B9%98%EC%8B%A4%ED%96%89-%EA%B2%BD%ED%97%98-%EB%AA%85%EB%A0%B9%EC%96%B4%EC%8B%A4%EC%A0%9C-%ED%99%94%EB%A9%B4-%EC%A0%95%EB%A6%AC">실제 설치/실행 경험 (명령어/실제 화면 정리)</a></li>
<li><a href="#7-docker%EC%9D%98-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%B0%B0%EA%B2%BD-cs-%EA%B4%80%EC%A0%90-%EC%9A%94%EC%95%BD">Docker의 기술적 배경 (CS 관점 요약)</a></li>
<li><a href="#8-%EC%95%9E%EC%9C%BC%EB%A1%9C%EC%9D%98-%EC%8B%A4%EC%8A%B5%ED%95%99%EC%8A%B5-%EB%AA%A9%ED%91%9C">앞으로의 실습/학습 목표</a></li>
<li><a href="#reference">Reference</a></li>
</ol>
<hr>
<h2 id="1-docker-기본-개념-한-번-만들어-어디서나-똑같이-실행">1. Docker 기본 개념: &quot;한 번 만들어 어디서나 똑같이 실행&quot;</h2>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/c1e420f4-5cb9-4766-bba8-fea98aec2a1f/image.png" alt="">
<sub>출처: <a href="https://www.docker.com/">https://www.docker.com/</a></sub></p>
<h3 id="📦-도커란">📦 도커란?</h3>
<ul>
<li><strong>Docker</strong>는 &quot;컨테이너(Container)&quot;라는 기술을 이용해 <strong>애플리케이션 실행 환경 전체를 캡슐화</strong>하는 오픈소스 플랫폼입니다.</li>
<li>&quot;내 컴퓨터에선 되는데?&quot;라는 문제(환경 불일치)를 해결.  </li>
<li>한 번 패키징한 이미지는 어떤 OS, 어떤 컴퓨터에서도 완벽하게 재현!</li>
</ul>
<hr>
<h2 id="2-docker-동작-원리-및-구조-그림과-함께">2. Docker 동작 원리 및 구조 (그림과 함께)</h2>
<h3 id="1-전체-동작-구조">(1) 전체 동작 구조</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/244ae492-5a31-4135-8780-e2bdb17da57f/image.png" alt="client-dockerhost-registry">
<sub>출처: <a href="https://www.docker.com">https://www.docker.com</a></sub></p>
<h4 id="구성요소">구성요소</h4>
<ul>
<li><strong>Client</strong>: <code>docker run</code>, <code>docker build</code>, <code>docker pull</code> 등 명령어로 Docker를 조작하는 주체 (보통 개발자의 터미널)</li>
<li><strong>Docker Host</strong>: Docker가 설치된 컴퓨터.  <ul>
<li><strong>Docker Daemon</strong>(서버): 실제로 이미지 관리, 컨테이너 실행, 네트워크·스토리지 관리 등 모든 동작을 수행</li>
<li><strong>Images</strong>: 실행 가능한 프로그램, 라이브러리, 의존성, 설정값 등 <strong>전체 실행 환경의 &#39;스냅샷&#39;</strong></li>
<li><strong>Containers</strong>: 이미지를 실행시켜 만들어진 독립적인 &#39;실행 인스턴스&#39;</li>
</ul>
</li>
<li><strong>Registry</strong>: 이미지의 저장소 (ex: Docker Hub).  <ul>
<li>이미지를 pull/push해서 팀/회사/커뮤니티와 공유 가능</li>
</ul>
</li>
</ul>
<h4 id="흐름">흐름</h4>
<ol>
<li>이미지는 보통 레지스트리(도커허브 등)에서 <strong>pull</strong> 해서 내려받음</li>
<li>이미지를 기반으로 컨테이너를 <strong>run</strong> 해서 실행</li>
<li>필요하다면 직접 <strong>build</strong>(Dockerfile 기반)해서 이미지 생성 가능</li>
<li>컨테이너는 격리된 상태(별도 네트워크, 파일시스템, 환경변수 등)에서 실행됨</li>
</ol>
<hr>
<h3 id="2-vmvirtual-machine-vs-docker-containers-구조-비교">(2) VM(Virtual Machine vs Docker Containers 구조 비교</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/b2dc1447-a34e-4bc8-bcb8-0af331d620d8/image.png" alt="vM vs container"></p>
<p><sub>출처: <a href="https://www.atlassian.com/microservices/cloud-computing/containers-vs-vms">https://www.atlassian.com/microservices/cloud-computing/containers-vs-vms</a></sub></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Virtual Machine(가상머신)</th>
<th>Docker(컨테이너)</th>
</tr>
</thead>
<tbody><tr>
<td>구조</td>
<td>물리서버 → Hypervisor → 게스트OS → 앱</td>
<td>물리서버 → Host OS → Docker Engine → 컨테이너(앱)</td>
</tr>
<tr>
<td>장점</td>
<td>완벽한 격리, OS마다 완전 다르게 사용 가능</td>
<td>매우 빠름, 가볍고, 필요 최소 리소스만 사용</td>
</tr>
<tr>
<td>단점</td>
<td>느림, 무거움, VM마다 OS가 따로 구동</td>
<td>Host OS 커널에 따라 약간의 제약</td>
</tr>
<tr>
<td>용도</td>
<td>완전 독립된 OS 환경 필요할 때</td>
<td>서비스 분리, 신속 배포, CI/CD, DevOps</td>
</tr>
</tbody></table>
<p><strong>핵심</strong></p>
<ul>
<li>VM은 &quot;OS까지 통째로 복제&quot;, 컨테이너는 &quot;앱과 의존성만 격리해서 초경량화&quot;</li>
<li>대용량 컨테이너도 서버 한 대에서 띄우는게 가능</li>
</ul>
<hr>
<h3 id="3-docker-실행의-핵심-단계-dockerfile-→-image-→-container">(3) Docker 실행의 핵심 단계: Dockerfile → Image → Container</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/0749f588-1337-445f-932f-5eeaa7fcdf4c/image.jpeg" alt="dockerfile -&gt; image -&gt; container"></p>
<p><sub>출처:<a href="https://www.docker.com">https://www.docker.com</a></sub></p>
<ol>
<li><strong>Dockerfile 작성</strong><ul>
<li>설치할 프로그램, 명령어, 환경변수 등 &#39;레시피&#39; 작성</li>
</ul>
</li>
<li><strong>이미지(Image)로 빌드</strong><ul>
<li>모든 설정/라이브러리/앱 파일이 포함된 스냅샷 생성</li>
</ul>
</li>
<li><strong>컨테이너(Container)로 실행</strong><ul>
<li>이미지를 기반으로 메모리 위에 띄우는 &#39;실행 인스턴스&#39;</li>
</ul>
</li>
</ol>
<h4 id="주요-명령어">주요 명령어</h4>
<ul>
<li><code>docker build -t myapp .</code> : Dockerfile에서 이미지 생성</li>
<li><code>docker pull ubuntu:22.04</code> : 도커허브에서 이미지 가져오기</li>
<li><code>docker run -d -p 8080:80 myapp</code> : 컨테이너 생성 및 포트포워딩</li>
</ul>
<hr>
<h2 id="3-docker-architecture-overview-컨테이너-생명주기와-주요-컴포넌트">3) Docker Architecture Overview: 컨테이너 생명주기와 주요 컴포넌트</h2>
<p>![Docker Architecture Overview]
(<a href="https://velog.velcdn.com/images/losiento_nana/post/3061af08-d9c6-46d4-a243-05e1affa59fa/image.jpeg">https://velog.velcdn.com/images/losiento_nana/post/3061af08-d9c6-46d4-a243-05e1affa59fa/image.jpeg</a>)
<sub>출처: <a href="https://until.blog/@xeros/docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-">https://until.blog/@xeros/docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-</a></sub></p>
<h4 id="구조-한눈에-보기">구조 한눈에 보기</h4>
<p>이 그림은 <strong>Docker의 내부 구조와 명령의 흐름</strong>을 한눈에 보여줍니다.<br>개발자가 CLI로 명령을 입력하면, 아래와 같은 경로로 동작합니다.</p>
<hr>
<h4 id="📌-각-구성요소-설명">📌 각 구성요소 설명</h4>
<ul>
<li><p><strong>Docker Client</strong><br>개발자가 터미널(혹은 기타 API, GUI)에서 <code>docker run</code>, <code>docker build</code>, <code>docker pull</code> 등 명령을 실행하는 주체.</p>
</li>
<li><p><strong>Docker Daemon (Server)</strong><br>Docker의 핵심 엔진. 클라이언트 명령을 받아 실제로 이미지 빌드, 컨테이너 실행/정지, 리소스 관리 등을 담당합니다.</p>
</li>
<li><p><strong>Docker Engine</strong><br>Daemon + REST API + CLI 등 Docker를 구성하는 전체 엔진 구조.</p>
</li>
<li><p><strong>Docker Images</strong><br>코드, 라이브러리, 환경변수 등 애플리케이션이 동작하는 데 필요한 <strong>실행 환경의 스냅샷</strong>.  
이미지를 기반으로 컨테이너를 생성합니다.</p>
</li>
<li><p><strong>Docker Containers</strong><br>이미지를 실제로 실행한 <strong>격리된 인스턴스</strong>.  
각각 독립된 환경에서 돌아가는 하나의 어플리케이션.</p>
</li>
<li><p><strong>Docker Registry</strong><br>이미지를 저장/공유하는 중앙 저장소(Docker Hub, 자체 Registry 등).<br>이미지는 Registry에서 <code>pull</code>, 내 이미지는 <code>push</code>로 업로드 가능.</p>
</li>
<li><p><strong>Running Instances</strong><br>이미지를 기반으로 실제 동작 중인 컨테이너(실행 인스턴스).</p>
</li>
</ul>
<hr>
<h4 id="⏩-동작-흐름">⏩ 동작 흐름</h4>
<ol>
<li><strong>개발자</strong>가 터미널에서 명령 실행<br>→ 예) <code>docker run mariadb:latest</code></li>
<li><strong>Client</strong>가 명령을 <strong>Docker Daemon</strong>에 전달</li>
<li>Daemon은 필요 시 <strong>Registry</strong>에서 이미지를 <strong>pull</strong></li>
<li>이미지를 바탕으로 <strong>컨테이너(Container)</strong>를 실행</li>
<li>실행 중인 컨테이너는 Daemon이 계속 관리</li>
<li>필요에 따라 이미지는 Registry에 push/pull로 공유</li>
</ol>
<hr>
<blockquote>
<p><strong>정리:</strong><br>Docker의 명령어 실행부터 실제 컨테이너 구동,<br>이미지의 생성/저장/공유까지의 <strong>전체 라이프사이클</strong>을  
하나의 그림으로 쉽게 이해할 수 있습니다.</p>
</blockquote>
<hr>
<h2 id="3-docker와-실제-개발운영-환경에서의-가치">3. Docker와 실제 개발/운영 환경에서의 가치</h2>
<ul>
<li><strong>재현성</strong>: 어디서든 같은 환경 (테스트/운영/배포)</li>
<li><strong>쉬운 확장/복제</strong>: 컨테이너 수백 개 자동화 관리 (Docker Compose, K8s 등)</li>
<li><strong>빠른 시작/종료</strong>: OS 구동 불필요, 수 초 만에 서비스 교체 가능</li>
<li><strong>DevOps, CI/CD</strong>: 테스트/배포 자동화에 필수적</li>
</ul>
<hr>
<h2 id="4-나는-왜-docker를-선택했나-실습-환경-기준-문제점과-해결">4. 나는 왜 Docker를 선택했나? (실습 환경 기준, 문제점과 해결)</h2>
<ul>
<li>로컬에 이미 <strong>MySQL</strong>이 설치되어 있고, 실습에서는 <strong>MariaDB</strong>를 별도 사용해야 하는 상황이었음</li>
<li>Mac 환경에서는 <strong>HeidiSQL</strong>(윈도우 전용 DB툴) 사용이 불가능</li>
<li><strong>Docker</strong>로 MariaDB 컨테이너를 띄우고, <strong>Sequel Ace</strong>로 연결 → <strong>포트만 분리</strong>해서 중복 걱정 없이 사용</li>
<li>VirtualBox로 리눅스 서버를 띄우지 않아도, Docker 컨테이너로 간단하게 환경 복제/관리가 가능</li>
<li>네트워크, 포트포워딩, ssh 등 실습에 필요한 거의 모든 인프라 환경을 <strong>코드(명령어)로 세팅</strong>할 수 있었음</li>
</ul>
<hr>
<h2 id="5-실습-환경-아키텍처-포트네트워크-흐름">5. 실습 환경 아키텍처 (포트/네트워크 흐름)</h2>
<ul>
<li><strong>간단하지만 실습 환경 아키텍처를 직접 그려봤다</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/bb614dd1-2769-406b-a0da-091bdc697c44/image.jpg" alt=""></li>
</ul>
<ul>
<li><strong>Mac(Host)</strong><ul>
<li>Docker Desktop 실행 (도커 엔진/컨테이너 관리)</li>
<li>Sequel Ace(로컬 클라이언트) → 포트포워딩(127.0.0.1:3307 → MariaDB 컨테이너:3306)</li>
<li>터미널 SSH → 포트포워딩(127.0.0.1:2222 → Ubuntu 컨테이너:22)</li>
</ul>
</li>
<li><strong>Docker Container</strong><ul>
<li>Ubuntu(SSH 서버 구동)</li>
<li>MariaDB(별도 포트, 데이터 볼륨, 네트워크 격리)</li>
</ul>
</li>
<li><strong>각 컨테이너는 별도 가상 네트워크(bridge)에서 동작</strong><ul>
<li>컨테이너 내부에서는 172.x.x.x와 같은 <strong>내부 IP</strong>로 직접 통신 가능 (Ubuntu → MariaDB 등)</li>
<li>Mac에서는 오직 <strong>외부로 개방한 포트</strong>로만 접근 가능</li>
</ul>
</li>
</ul>
<hr>
<h2 id="6-실제-설치실행-경험-명령어-실제-화면-정리">6. 실제 설치/실행 경험 (명령어 /실제 화면 정리)</h2>
<h3 id="1-docker-desktop-설치">1) Docker Desktop 설치</h3>
<ul>
<li><a href="https://www.docker.com/products/docker-desktop/">공식사이트</a>에서 Mac용 Docker Desktop 다운로드/설치</li>
</ul>
<h3 id="2-mariadb-컨테이너-띄우기">2) MariaDB 컨테이너 띄우기</h3>
<p><strong>MariaDB 컨테이너 실행 예시</strong></p>
<pre><code class="language-bash">docker pull mariadb:latest
docker run -d \
  --name mariadb-test \
  -e MYSQL_ROOT_PASSWORD=비밀번호 \
  -p 3307:3306 \
  mariadb:latest</code></pre>
<table>
<thead>
<tr>
<th>옵션</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>-d</code></td>
<td>컨테이너를 <strong>백그라운드(Detached)</strong>로 실행</td>
<td><code>docker run -d ...</code></td>
</tr>
<tr>
<td><code>--name</code></td>
<td>컨테이너에 <strong>이름</strong> 지정</td>
<td><code>docker run --name my-container ...</code></td>
</tr>
<tr>
<td><code>-e</code></td>
<td><strong>환경변수</strong>(ex. 비밀번호 등) 설정</td>
<td><code>docker run -e MYSQL_ROOT_PASSWORD=비번 ...</code></td>
</tr>
<tr>
<td><code>-p</code></td>
<td><strong>포트포워딩</strong> (로컬:컨테이너) 설정</td>
<td><code>docker run -p 3307:3306 ...</code></td>
</tr>
</tbody></table>
<h3 id="3ubuntu-컨테이너-띄우기">3)Ubuntu 컨테이너 띄우기</h3>
<ul>
<li><strong>Ubuntu(SSH) 컨테이너 실행 예시</strong></li>
</ul>
<pre><code class="language-bash">docker pull rastasheep/ubuntu-sshd:18.04
docker run -d \
  --name ubuntu-ssh \
  -p 2222:22 \
  rastasheep/ubuntu-sshd:18.04</code></pre>
<blockquote>
<p>기본 ubuntu 이미지에는 SSH 데몬이 설치/구동되어 있지 않으니, rastasheep/ubuntu-sshd와 같이 SSH가 포함된 이미지를 사용하면 편리함.</p>
</blockquote>
<h3 id="4-sequel-ace터미널-연결-예시">4) Sequel Ace/터미널 연결 예시</h3>
<ul>
<li><strong>Sequel Ace에서 연결 설정</strong></li>
</ul>
<p>Host: 127.0.0.1
Port: 3307 (아까 -p 3307:3306으로 포워딩한 포트)
사용자/비번: MariaDB 환경에 맞게 입력</p>
<pre><code class="language-bash">ssh root@127.0.0.1 -p 2222</code></pre>
<ul>
<li><strong>명령어를 통해 둘다 잘 연결 됐는지 확인</strong>
<img src="https://velog.velcdn.com/images/losiento_nana/post/e531c31d-ed4f-4150-be8b-ab13acd4ceb0/image.png" alt=""></li>
</ul>
<h3 id="4-컨테이너-ip포트네트워크-확인법">4) 컨테이너 IP/포트/네트워크 확인법</h3>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/02d17f05-a1df-4c40-89b5-6e6042784fde/image.png" alt=""></p>
<pre><code class="language-bash">docker ps                # 모든 컨테이너/포트 매핑 확인
docker inspect [컨테이너명] | grep IPAddress</code></pre>
<p><img src="https://velog.velcdn.com/images/losiento_nana/post/1c2803cf-e799-4bb8-8530-0396c19893a7/image.png" alt=""></p>
<pre><code class="language-bash">docker exec -it [컨테이너명] bash  # 내부 진입
ifconfig                  # 내부에서 IP 확인</code></pre>
<hr>
<h2 id="7-docker의-기술적-배경-cs-관점-요약">7. Docker의 기술적 배경 (CS 관점 요약)</h2>
<blockquote>
</blockquote>
<p>CS를 단순히 이론으로만 받아들이지 않고, 직접 사용하는 툴에 적용한 CS관점을 추후 공부하기 위해 정리 해놓으려고 한다.</p>
<ul>
<li><strong>리눅스 커널 기술</strong>: Namespace(격리), cgroups(리소스 제한), OverlayFS(계층화)</li>
<li><strong>이미지 레이어</strong>: 불필요한 중복 없이, 공통 파일을 공유하고 필요한 부분만 추가</li>
<li><strong>격리 수준</strong>: 프로세스, 네트워크, 파일시스템, 사용자 권한 등 모두 별도 공간처럼 분리</li>
<li><strong>실행 속도</strong>: OS 부팅 없이 컨테이너 프로세스만 실행. VM 대비 압도적 속도/효율</li>
</ul>
<hr>
<h2 id="8-앞으로의-실습학습-목표--무조건-하리라">8. 앞으로의 실습/학습 목표 ( 무조건 하리라..)</h2>
<ul>
<li><p><strong>docker-compose로 여러 서비스 일괄 관리 및 자동화</strong></p>
</li>
<li><p><strong>MariaDB 데이터 볼륨, 백업/복구, 데이터 이관 자동화</strong></p>
</li>
<li><p><strong>Spring과의 실제 연동 프로젝트 구축</strong></p>
</li>
<li><p><strong>CI/CD, Jenkins, Github Actions 등 DevOps 파이프라인 실습</strong></p>
</li>
<li><p><strong>AWS, GCP 등 퍼블릭 클라우드 배포 실습</strong></p>
</li>
</ul>
<hr>
<h2 id="reference">[Reference]</h2>
<ul>
<li><a href="https://www.docker.com">Docker 공홈</a></li>
<li><a href="https://www.atlassian.com/microservices/cloud-computing/containers-vs-vms">docker</a></li>
<li><a href="https://adjh54.tistory.com/352">컨테이너와 VM의 차이 (블로그)</a></li>
<li><a href="https://chanhy63.tistory.com/11">Docker + ubuntu</a></li>
<li><a href="https://dkswnkk.tistory.com/697">Docker + maria DB</a></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[한화 BEYOND SW 캠프 18기 [1주차] & 나의 개발 성장 회고]]></title>
            <link>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-WIL-Weekly-Reflection%EC%86%94%EC%A7%81%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@losiento_nana/%ED%95%9C%ED%99%94-BEYOND-SW-%EC%BA%A0%ED%94%84-18%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-WIL-Weekly-Reflection%EC%86%94%EC%A7%81%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Fri, 20 Jun 2025 20:12:18 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-한화-beyond-sw-캠프-1주차-wil-weekly-reflection">✨ 한화 BEYOND SW 캠프 [1주차] WIL (Weekly Reflection)</h1>
<blockquote>
<p>이 글은 BEYOND SW 캠프 1주차 동안의 성장과 배움을 스스로 돌아보고 기록하기 위한 회고입니다.</p>
</blockquote>
<hr>
<h2 id="🔗-이번주-캠프-학습노트정리">🔗 이번주 캠프 학습노트/정리</h2>
<blockquote>
<p>이번주 수업 중 새롭게 배운 내용이나, 직접 정리해 본 개념/기술에 대해<br>벨로그에 따로 아카이브해두었습니다.<br>(관심 있는 분들은 아래 링크에서 자세히 확인해 주세요)</p>
</blockquote>
<ul>
<li><a href="https://velog.io/@losiento_nana/Linux-%ED%94%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%98%EB%A9%B4-%EC%A6%90%EA%B2%A8%EB%9D%BC">한화 BEYOND SW 캠프 1주차 정리: Linux, SSH, Port Forwarding, bash/zsh</a>  </li>
</ul>
<hr>
<h2 id="📅-이번-주-한-일-facts">📅 이번 주 한 일 (FACTS)</h2>
<ul>
<li>토스 증권 HR팀과 커피챗</li>
<li>한화 BEYOND SW 캠프 18기 지원 및 합격</li>
<li>한화 BEYOND SW 캠프 1주차 수업 진행 (오프라인)</li>
<li>프로그래머스 기반 코딩테스트 공부 시작</li>
<li>개인 프로젝트 아이디어 도출 및 아키텍처/ERD 구상</li>
<li>스타트업(코드시그널 플랫폼) 온라인 코딩테스트 응시</li>
</ul>
<hr>
<h2 id="💭-느낀-점감정-feelings">💭 느낀 점/감정 (FEELINGS)</h2>
<ul>
<li>취업 준비를 늦게 시작해 불안했던 시간도 있었지만, 포기하지 않고 꾸준히 준비한 것이 결국 다양한 기회를 만들어준다는 걸 깨달았다.</li>
<li>오프라인 수업에서 여러 비전공자 동기들과 함께 공부하니, 학부 때와는 또 다른 간절함과 동기부여가 생겼다.</li>
<li>알고리즘/코딩테스트 공부는 예전엔 재미도 없고 막막하다고 느꼈지만, 다시 시작해보니 의외로 도전욕구가 생겼다.</li>
<li>첫 온라인 코딩테스트에서 영어로 된 문제를 푸는 경험이 쉽진 않았지만, 스스로에게 부족한 부분을 솔직하게 받아들일 수 있는 시간이었다.</li>
<li>개인 프로젝트 구상 시간은 언제나 개발자로서 가장 설레는 순간이다.</li>
</ul>
<hr>
<h2 id="🔍-배운-점새롭게-알게-된-것-findings">🔍 배운 점/새롭게 알게 된 것 (FINDINGS)</h2>
<ul>
<li>실제 IT기업(토스증권 등) HR팀과 대화하며 &quot;기본기/실력 + 다양한 도전경험&quot;의 중요성을 실감했다.</li>
<li>비전공자와 함께 듣는 수업에서도 &#39;개념의 뿌리&#39;부터 다시 질문하는 과정이, 나의 성장에 훨씬 도움이 된다는 점을 느꼈다.</li>
<li>코딩테스트 영어 지문 해석력, 시간 관리의 필요성을 깨달았고, 알고리즘은 &#39;실전형 연습&#39;이 반드시 필요하다고 느꼈다.</li>
<li>프로젝트 설계(ERD, 전체 구조도) 과정에서, 아이디어만큼이나 실제 구현 계획의 중요성을 실감했다.</li>
</ul>
<hr>
<h2 id="🚧-어려움--문제-problem-or-minus">🚧 어려움 &amp; 문제 (Problem or Minus)</h2>
<ul>
<li>자기소개서/포트폴리오 불합격이 반복되면서 &quot;내 경험 자체가 부족한가? 아니면 표현/정리가 문제인가?&quot;라는 고민이 컸다.</li>
<li>비전공자 위주 캠프 초반 수업 내용이 익숙했지만, 두번째로 듣는 개념에서도 ‘왜 이걸 쓰는지, 근본 이유’를 다시 생각하며 넘어가야 한다는 점이 쉽지 않았다.</li>
<li>코딩테스트 영어 문제 풀이가 예상 외로 어려웠고, 시간 내에 풀이하는 데 실패했다.</li>
<li>프로젝트 구상에서 실현 가능성, 세부 설계 등 미처 생각하지 못한 부분에서 막히기도 하고 다시 수정을 하는 과정을 겪었다.</li>
</ul>
<hr>
<h2 id="🛠️-해결-방법--시도-try-or-improve">🛠️ 해결 방법 &amp; 시도 (Try or Improve)</h2>
<ul>
<li>주변의 조언, 실제 현업 개발자/HR팀과의 커피챗, 다양한 레퍼런스(블로그, 유튜브 등)를 참고해서 포트폴리오/자소서 문장을 하나씩 개선 할 계획이다.</li>
<li>수업에서 배운 내용도 단순 암기나 따라가기 대신, 왜 필요한지 근본을 고민하며 &#39;배운 개념을 직접 손으로 실습&#39;하는 방식으로 수업 참여</li>
<li>프로그래머스 1~2레벨 문제로 실전 감각을 쌓고 자료구조에 대해 복습</li>
<li>개인 프로젝트는 단순 아이디어가 아니라 ERD, 기술 스택, 전체 아키텍처 등 실제로 ‘작동할 수 있는 구조’로 구체화하는 시도를 반복</li>
</ul>
<hr>
<h2 id="🌱-앞으로의-계획--다짐-future">🌱 앞으로의 계획 &amp; 다짐 (Future)</h2>
<ul>
<li>포트폴리오/자소서 작성법을 계속 개선하고, 실제 경험을 쌓는 데 더 집중할 것</li>
<li>코딩테스트 실전 감각을 높이기 위해 매일 일정 시간 꾸준히 문제 풀이/리뷰를 진행할 것</li>
<li>프로젝트를 단순 ‘계획’에서 실제 ‘구현’ 단계로 진입하기 위한 세부 일정, 기술 검증 플랜까지 세울 것. 하지만 기본적으로 주제의 핵심을 놓치지 않아야 하는걸 스스로 상기시키기</li>
<li>한 주 한 주 작은 성취라도 반드시 기록으로 남기는 &#39;습관&#39;을 만드는 것이 목표</li>
<li>동기들과 함께 으샤으샤 하면서 독하게 공부하고 싶다.</li>
</ul>
<hr>
<h2 id="🙌-한-줄-마무리">🙌 한 줄 마무리</h2>
<blockquote>
<p>좀 피곤하지만 재밌었던 1주일.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>