<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>do_it.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 14 Dec 2025 05:30:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. do_it.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/do_e" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Problem Solving] BJ 12919. A와 B]]></title>
            <link>https://velog.io/@do_e/Problem-Solving-BJ-12919.-A%EC%99%80-B</link>
            <guid>https://velog.io/@do_e/Problem-Solving-BJ-12919.-A%EC%99%80-B</guid>
            <pubDate>Sun, 14 Dec 2025 05:30:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>S와 T 두 문자열이 주어짐.</li>
<li>두 문자열은 모두 A, B로만 이루어져 있고, 다음 두 연산을 사용할 수 있음.<ol>
<li>문자열 뒤에 A를 추가</li>
<li>문자열 뒤에 B를 추가하고, 문자열 전체를 뒤집음</li>
</ol>
</li>
<li>이 연산을 여러 번 사용해서 문자열 S를 문자열 T로 만들 수 있으면 1, 없으면 0을 출력하는 문제</li>
</ul>
<pre><code class="language-jsx">// [input]
A // S
BABA // T

// [output]
1 // or 0</code></pre>
<br/>

<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="정방향-s-→-t">정방향 (S → T)</h2>
<ul>
<li>정방향은 길이를 1씩 늘리는 방법</li>
<li>매 단계마다 2개의 선택지가 있음
1) 뒤에 A를 붙이기 or 
2) 뒤에 B를 붙이고 뒤집기</li>
<li>S의 길이가 n, T의 길이가 m이면 총 m-n번 연산을 해야 함 ⇒ 가능한 연산 수  2^(m-n)</li>
<li><ul>
<li>문자열 뒤집는 연산</li>
</ul>
</li>
<li>모든 경우를 만들어 보는 방법(완전 탐색)은 시간/ 메모리적 한계</li>
</ul>
<h2 id="역방향-t-→-s">역방향 (T → S)</h2>
<ul>
<li>역방향은 매번 길이를 1씩 줄임</li>
<li>역연산의 조건
1) 끝이 A일 때만 마지막 A 제거
2) 시작이 B일 때만 첫 B 제거 후 뒤집기 기능</li>
<li>메 단계마다 2가지 경우가 아니라, 조건이 맞을 때만 연산 수행 가능</li>
<li>역방향 풀이가 유리한 이유?<ul>
<li>깊이가 최대 m-n으로 고정됨 (길이가 계속 줄어듦)</li>
<li>항상 2가지 경우가 아닌 조건부이므로 분기가 줄어듦</li>
<li>중복 문자열은 visited로 차단 가능</li>
<li>DFS/ BFS로 T를 S 길이까지 줄여서 같아지는지 검사</li>
</ul>
</li>
</ul>
<br>

<h1 id="3-풀이">3. 풀이</h1>
<h2 id="주요-로직">주요 로직</h2>
<p>역방향으로 문자열(cur)을 한 단계씩 검사</p>
<ul>
<li>cur의 마지막 글자가 A라면 (….A)
→ 정방향 연산에서 A를 붙였던 경우이므로 마지막 A를 제거</li>
<li>cur의 첫 글자가 B라면 (B…..)
→ 정방향 연산에서 뒤에 B를 붙이고 뒤집었던 경우이므로 첫 글자 B를 지우고 뒤집어서 다시 복구</li>
<li>길이가 1씩 줄어들으므로 DFS / 백트래킹으로 T를 S 길이까지 줄이며 확인하기</li>
</ul>
<br>

<h1 id="4-코드">4. 코드</h1>
<h2 id="주요-로직-1">주요 로직</h2>
<h3 id="1-전역-변수-visited">1) 전역 변수: visited</h3>
<p><code>static Set&lt;String&gt; visited = new HashSet&lt;&gt;();</code></p>
<ul>
<li>Set 타입: 같은 값 중복 저장 X → 이미 값이 들어있는지를 O(1)로 빠르게 연산 가능</li>
<li>DFS를 돌 떄 같은 문자열 상태가 나오면 함수 실행을 막음</li>
<li>현재 문자열 cur 상태에서 가능한 연산을 visited에 저장
→ 같은 cur이 나오면 계속 볼 필요 X</li>
</ul>
<h3 id="2-dfs-탈출조건">2) dfs 탈출조건</h3>
<ul>
<li>(1) 전역 변수 found가 true인 경우
S와 길이가 같고, 값이 같을 때</li>
<li>(2) <code>!visited.add(cur)</code> 이</li>
<li>(3) 길이가 S와 같아질 경우
더 줄이면 길이가 달라져 절대 S가 될 수 없음</li>
</ul>
<pre><code class="language-jsx">
public class Main {
    static String S,T;
    static Set&lt;String&gt; visited = new HashSet&lt;&gt;();
    static boolean found = false;
    public static void main(String[] args) throws IOException {

        // [input]
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        S = br.readLine().trim();
        T = br.readLine().trim();

        // [logic]
        dfs(T);

        // [output]
        System.out.println(found?1:0);

    }

    static void dfs(String cur) {
        if(found) return;

        if (visited.contains(cur)) return;
        visited.add(cur);

        if(cur.length() == S.length()) {
            if(cur.equals(S)) found = true;
            return;
        }

        // 마지막 글자가 A이면
        if(cur.charAt(cur.length()-1)==&#39;A&#39;) {
            dfs(cur.substring(0,cur.length()-1)); // 마지막 글자 A 지우고 다시 탐
        }

        // 첫 글자가 B이면
        if(cur.charAt(0)==&#39;B&#39;) {
            String trimmed = cur.substring(1); // == substring(1,cur.length())
            String next = new StringBuilder(trimmed).reverse().toString();
            dfs(next);
        }
    }
}
</code></pre>
<br>


<h1 id="5-de-fault">5. De-fault</h1>
<h3 id="1-visited-배열이-왜-필요한가">1) visited 배열이 왜 필요한가?</h3>
<ul>
<li>길이가 매번 1씩 줄어들면서 서로 다른 경로로 줄여오다가 같은 문자열 상태인 경우가 생김</li>
<li>visited가 없으면 해당 문자열에서 시작되는 하위 탐색을 똑같이 수행하기 때문에 낭비가 커짐</li>
<li>역 연산은 2가지 방법이 가능 (끝이 A일 경우, B로 시작하는 경우)</li>
<li>어떤 문자열은 두 조건을 동시에 맞목할 수 있고, 두 연산을 다른 순서로 적용하면서 내려오다 보면 결과가 같은 문자열로 합쳐질 수 있음</li>
<li>Set 자료 구조 이용
visited는 이미 탐색한 문자열 상태를 저장해두고, 같은 cur가 다시 나오면 재탐색을 막는 가지치기 용도
→ 방문 여부 확인 &amp; 없으면 기록하는 데 평균적으로 빠른 자료구조는 HashSet</li>
</ul>
<pre><code class="language-jsx">e.g. BAA

[1]
BAA -&gt; BA (끝이 A)
BA -&gt; A (B제거 후 뒤집기)

[2] 
BAA -&gt; AA (B제거 후 뒤집기)
AA -&gt; A (끝이 A)</code></pre>
<h3 id="2-stringbuilder를-사용하는-이유">2) StringBuilder를 사용하는 이유</h3>
<ul>
<li>String은 immutable이므로 문자열을 뒤집거나 붙이는 작업을 String으로 처리하면 내부적으로 새 문자열이 계속 만들어져 비용이 커질 수 있음</li>
<li>StringBuilder는 mutable이라서 내부 버퍼에서 문자를 처리하여 reverse() 같은 연산을 효율적으로 수행할 수 있음</li>
</ul>
<br>


<p>이 코드에서 <strong>잘못된 부분</strong>이나 <strong>개선할 점</strong>이 있다면 언제든지 <strong>댓글로</strong> 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Problem Solving] BJ 20922. 겹치는 건 싫어!]]></title>
            <link>https://velog.io/@do_e/Problem-Solving-BJ-20922.-%EA%B2%B9%EC%B9%98%EB%8A%94-%EA%B1%B4-%EC%8B%AB%EC%96%B4</link>
            <guid>https://velog.io/@do_e/Problem-Solving-BJ-20922.-%EA%B2%B9%EC%B9%98%EB%8A%94-%EA%B1%B4-%EC%8B%AB%EC%96%B4</guid>
            <pubDate>Sun, 07 Dec 2025 10:53:59 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>주어진 N 길이의 수열에서 같은 숫자가 K번 이하로만 나오도록 하는 가장 긴 부분 수열의 길이를 구하는 문제</li>
<li><em>⇒ 같은 값이 K번 이하로만 등장하도록 유지하면서 연속 구간을 최대로 늘리는 문제*</em></li>
<li><strong>[제약 조건] 연속 조건 &amp; 등장 횟수</strong></li>
</ul>
<pre><code class="language-java">// [input]
9 2 // N K
1 1 2 1 2 2 1 1 2 // 수열</code></pre>
<br>

<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="0-문제-풀이">0) 문제 풀이</h2>
<ul>
<li><strong>문제 유형: 투 포인터</strong></li>
<li>각 숫자의 등장 횟수를 관리해야 하므로 빈도수 배열 / 맵을 사용</li>
<li>right 포인터로 구간을 확장하며 숫자 등장 횟수를 카운팅</li>
<li>어떤 숫자의 빈도가 K를 초과하면 left포인터를 이동시키면서 초과한 숫자를 줄임</li>
<li>구간 길이 갱신</li>
</ul>
<br>

<h1 id="3-풀이">3. 풀이</h1>
<h2 id="주요-알고리즘">주요 알고리즘</h2>
<ul>
<li>슬라이딩 윈도우 (투 포인터) + 빈도 카운팅</li>
</ul>
<h2 id="문제-쪼개기">문제 쪼개기</h2>
<ol>
<li><code>freq[x]</code> 배열을 입력값 최대의 크기로 설정</li>
<li>left, right 포인터를 0에서 시작</li>
<li>right를  0 ~ N-1까지 1씩 늘리며 구간 확인 [left, right]<ul>
<li>숫자의 등장 횟수를 카운팅: <code>freq[x]</code>로 현재 구간에서 x가 몇 번 나왔는지 카운팅</li>
<li>K를 초과하면 left를 이동시키며 등장횟수를 -1</li>
</ul>
</li>
<li>조건을 만족하는 구간의 길이 갱신하기 (right - left + 1)</li>
<li>right가 끝까지 이동할 때까지 반복</li>
</ol>
<br>

<h1 id="4-코드">4. 코드</h1>
<pre><code class="language-java">import java.io.*;
import java.util.*;

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

        // [input]
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int K = Integer.parseInt(st.nextToken());

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

        // [logic]
        // 입력값의 크기의 카운팅 배열 만들기
        int[] freq = new int[100001];
        int left = 0;
        int maxLen = 0;

        for (int right = 0; right &lt; N; right++) {
            freq[arr[right]]++;

            // 겹치는 원소가 K개를 초과하면
            while (freq[arr[right]] &gt; K) {
                freq[arr[left]]--;
                left++;
            }

            maxLen = Math.max(maxLen, right - left + 1);
        }

        System.out.println(maxLen);
    }
}
</code></pre>
<br>

<h1 id="5-de-fault">5. De-fault</h1>
<pre><code class="language-java">freq[arr[right]]++;

while (freq[arr[right]] &gt; K) {
    freq[arr[left]]--;
    left++;
}</code></pre>
<ul>
<li>right를 하나씩 늘리면서 구간 [left, right] 안에 포함된 원소들의 등장 횟수를 freq에 기록한 후,
어떤 값 x가 K을 초과하여 등장했을 때 로직을 처리하는 부분이 어려웠음.</li>
<li>처음에 직관적으로는 ‘K번 횟수를 넘긴 그 값이 있는 위치를 찾아서 그 원소가 K번 이하로 나와야 하는게 아닌가?’라는 생각이 들었음</li>
<li><strong>왜 left를 오른쪽으로 한 칸씩 밀어야 하는가?</strong>
연속 부분 수열을 유지하면서 조건을 만족시키는 유일한 축소 방법이기 때문</li>
<li>문제의 핵심은 연속 부분 수열(구간)만 허용하는 점임.
right를 하나 늘렸을 때 <code>freq[arr[right]] &gt; K</code> 이면 해당 구간은 조건을 위반한 상태가 됨.</li>
<li>연속 구간을 유지하면서 이 위반 상태를 없애는 방법은
왼쪽 포인터 left를 오른쪽으로 밀면서, 하나씩 구간 밖으로 내보내는 것</li>
<li>중간에 있는 원소를 선택해 빼버리면 연속 부분 수열이 아니게 되기 때문에 오직 left ++를 통해서 구간을 줄일 수 있음
왼쪽으로 계속 밀다 보면 언젠가는 K번의 등장 횟수를 초과한 원소와 같은 값 하나가 잘려나감</li>
<li>이 시점에 <code>freq[arr[right]]</code> 이 다시 K이하로 떨어지고 해당 구간은 조건을 반족하게 됨
이 때 이 순간의 구간길이 <code>right - left + 1</code> max 값 갱신</li>
</ul>
<br>

<p>이 코드에서 <strong>잘못된 부분</strong>이나 <strong>개선할 점</strong>이 있다면 언제든지 <strong>댓글로</strong> 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Problem Solving] Programmers 42579. 베스트 앨범]]></title>
            <link>https://velog.io/@do_e/Problem-Solving-Programmers-42579.-%EB%B2%A0%EC%8A%A4%ED%8A%B8-%EC%95%A8%EB%B2%94</link>
            <guid>https://velog.io/@do_e/Problem-Solving-Programmers-42579.-%EB%B2%A0%EC%8A%A4%ED%8A%B8-%EC%95%A8%EB%B2%94</guid>
            <pubDate>Sun, 30 Nov 2025 10:03:26 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시</li>
</ul>
<h3 id="문제-조건">문제 조건</h3>
<ul>
<li>속한 노래가 많이 재생된 장르를 먼저 수록함. (장르별 재생 횟수 내림차순)</li>
<li>장르 내에서 많이 재생된 노래를 먼저 수록함. (노래 재생 횟수 내림차순)</li>
<li>장르 내에서 재생 횟수가 같은 노래들은 고유 번호가 낮은 노래를 먼저 수록함.(재생 횟수 비교 -&gt; 노래 id 비교)<pre><code>genres: [&quot;classic&quot;, &quot;pop&quot;, &quot;classic&quot;, &quot;classic&quot;, &quot;pop&quot;]    
plays:    [500, 600, 150, 800, 2500]
return: [4, 1, 3, 0]</code></pre></li>
</ul>
<br>


<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="풀이-해시맵-hash-map">풀이: 해시맵 (Hash Map)</h2>
<h3 id="0-song-클래스-정의">0) Song 클래스 정의</h3>
<h3 id="1-for문-장르별-총-재생-횟수--각-장르별-노래-목록-정리-장르-내부-정렬">1) [for문] 장르별 총 재생 횟수 &amp; 각 장르별 노래 목록 정리 (장르 내부 정렬)</h3>
<ul>
<li><strong>장르별 총 재생수 계산</strong>
<code>genrePlayMap: Map&lt;String, Integer&gt;</code> 타입에 &lt;장르, 재생 횟수&gt; 값 담기 </li>
<li><strong>장르별 노래 목록 정렬</strong>
<code>genrePQMap: Map&lt;String, PriorityQueue&lt;Song&gt;</code> 타입에 &lt;장르, 장르별 노래 목록의 PQ&gt; 값 담기</li>
</ul>
<h3 id="2-장르-순서-정렬-장르---장르">2) 장르 순서 정렬 (장르 &lt;-&gt; 장르)</h3>
<ul>
<li><code>genrePlayMap</code>의 keySet을 통해 장르들의 키 값을 ArrayList로 감싸<code>List&lt;String&gt; genreOrder</code>에 담기</li>
<li>sort 함수를 통해 내림차순하여 인기 장르부터 순서대로 정렬</li>
</ul>
<h3 id="3-장르-순서대로-상위-2곡씩-꺼내기">3) 장르 순서대로 상위 2곡씩 꺼내기</h3>
<ul>
<li>결과를 담을 <code>List&lt;Integer&gt; result</code> 선언</li>
<li><code>List&lt;String&gt;  genreOrder</code>를 for문으로 순회하며 각 장르의 pq에서 2번씩 poll</li>
<li><code>List&lt;Integer&gt; result</code>를 stream을 통해 int[] 배열로 만들기<br>


</li>
</ul>
<h1 id="3-코드">3. 코드</h1>
<pre><code class="language-java">import java.util.*;

class Solution {

    // 0. Song 클래스 정의 
    static class Song {
        int id;
        int plays;

        Song(int id, int plays) {
            this.id = id;
            this.plays = plays;
        }
    }

    public int[] solution(String[] genres, int[] plays) {

        Map&lt;String, Integer&gt; genrePlayMap = new HashMap&lt;&gt;();
        Map&lt;String, PriorityQueue&lt;Song&gt;&gt; genrePQMap = new HashMap&lt;&gt;();

        // 1. 장르별 총 재생수 &amp; 곡 리스트 구성 
        for (int i = 0; i &lt; genres.length; i++) {
            String genre = genres[i];
            int playCount = plays[i];

            // 장르별 총 재생수 관리
            if (genrePlayMap.containsKey(genre)) {
                int currentTotal = genrePlayMap.get(genre);
                genrePlayMap.put(genre, currentTotal + playCount);
            } else {
                genrePlayMap.put(genre, playCount);
            }

            // 장르별 PQ가 없다면 만들기
            if (!genrePQMap.containsKey(genre)) {
                genrePQMap.put(genre, new PriorityQueue&lt;&gt;(
                    (s1, s2) -&gt; {
                        if (s1.plays == s2.plays) return s1.id - s2.id; // id 오름차순
                        return s2.plays - s1.plays;                     // plays 내림차순
                    }
                ));
            }

            // 있다면 장르별 PQ에 곡 추가
            genrePQMap.get(genre).offer(new Song(i, playCount));
        }

        // 2. 장르 정렬 (총 재생수 내림차순)
        List&lt;String&gt; genreOrder = new ArrayList&lt;&gt;(genrePlayMap.keySet());
        genreOrder.sort((a, b) -&gt; genrePlayMap.get(b) - genrePlayMap.get(a));


        // 3. PQ에서 상위 곡 2개만 꺼내기
        List&lt;Integer&gt; result = new ArrayList&lt;&gt;();

        for (String g : genreOrder) {
            PriorityQueue&lt;Song&gt; pq = genrePQMap.get(g);

            if (!pq.isEmpty()) result.add(pq.poll().id);
            if (!pq.isEmpty()) result.add(pq.poll().id);
        }

        // [output]
        return result.stream().mapToInt(i -&gt; i).toArray();
    }
}
</code></pre>
<br>

<h1 id="4-de-fault">4. De-fault</h1>
<h2 id="시간-복잡도">시간 복잡도</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>복잡도</th>
</tr>
</thead>
<tbody><tr>
<td>장르별 총 재생수 계산</td>
<td>O(N)</td>
</tr>
<tr>
<td>장르별 PQ 삽입</td>
<td>O(N log N)</td>
</tr>
<tr>
<td>장르 정렬</td>
<td>O(G log G)   (G = 장르 개수)</td>
</tr>
<tr>
<td>각 장르에서 곡 2개 선택</td>
<td>O(G log N)</td>
</tr>
<tr>
<td>총합</td>
<td>O(N log N)</td>
</tr>
</tbody></table>
<br>

<h2 id="til">TIL</h2>
<h3 id="1-람다식을-통한-comparator-작성">1) 람다식을 통한 Comparator 작성</h3>
<ul>
<li>PriorityQueue, List 정렬 등 Comparator 사용 시 람다식 작성을 통해 가독성을 높임.<pre><code class="language-java">// 기존 방식
Comparator&lt;Integer&gt; comp = new Comparator&lt;Integer&gt;() {
  @Override
  public int compare(Integer a, Integer b) {
      return b - a;
  }
};
</code></pre>
</li>
</ul>
<p>// 람다식 방식
Comparator<Integer> comp = (a, b) -&gt; b - a;</p>
<pre><code>
### 2) Stream을 통한 타입 변환
- Stream API는 컬렉션 데이터를 처리하는 파이프라인 기능임.
- 문제에서 `List&lt;Integer&gt;` -&gt; `int[]` 타입 변환
```java
return result.stream()
             .mapToInt(i -&gt; i)
             .toArray();</code></pre><ul>
<li><p>리스트를 스트림으로 변환한 후</p>
</li>
<li><p>Integer 객체를 기본 int로 언박싱하여 (IntStream 생성)</p>
</li>
<li><p><code>toArray()</code>를 통해 IntStream을 <code>int[]</code>로 변환함.</p>
<br>


</li>
</ul>
<p>이 코드에서 <strong>잘못된 부분</strong>이나 <strong>개선할 점</strong>이 있다면 언제든지 <strong>댓글로</strong> 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers.43162] 네트워크]]></title>
            <link>https://velog.io/@do_e/Provlem-Solving-Programmers-43162.-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@do_e/Provlem-Solving-Programmers-43162.-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Sun, 23 Nov 2025 10:53:32 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>n개의 컴퓨터가 있을 때, 컴퓨터들이 연결되어 있는 정보를 담은 2차원 배열이 주어짐.</li>
<li>연결된 컴퓨터들을 하나의 네트워크로 간주할 때 총 네트워크의 개수를 반환해야 함.</li>
</ul>
<pre><code class="language-jsx">// [input]
 n (1 ≤ n ≤ 200) : 컴퓨터의 개수
computers (n × n 배열): 연결된 컴퓨터들에 대한 정보. (1: 연결)
</code></pre>
<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="풀이-그래프-탐색-dfs">풀이: 그래프 탐색 (DFS)</h2>
<ul>
<li>각 컴퓨터(노드)에서 연결된 다른 컴퓨터들을 재귀적으로 방문함.</li>
</ul>
<ol>
<li>모든 컴퓨터를 순회하면서 아직 방문하지 않은 컴퓨터라면
→ 먼저 네트워크 수를 ++
→ 그 컴퓨터와 연결된 컴퓨터를 dfs 함수를 호출하면서 모두 방문 처리하여 같은 네트워크로 묶음</li>
<li>메인 반복문으로 돌아가 아직 방문하지 않은 컴퓨터가 있다면 
→ 네트워크 수 ++
→ dfs로 같은 네트워크로 묶는 과정을 반복</li>
</ol>
<h1 id="3-코드">3. 코드</h1>
<pre><code class="language-java">class Solution {
    public int solution(int n, int[][] computers) {
        int answer = 0;
        boolean[] visited = new boolean[n];

        for (int i = 0; i &lt; n; i++) {
            if (!visited[i]) { // 아직 방문하지 않은 컴퓨터면
                answer++; // 네트워크 개수 추가
                dfs(i, visited, computers, n); // 연결된 모든 컴퓨터 찾기
            }
        }
        return answer;
    }

        // dfs: 연결된 컴퓨터 방문 처리
    void dfs(int current, boolean[] visited, int[][] computers, int n) {
        visited[current] = true; // 현재 컴퓨터 방문 처리

           // 현재 컴퓨터와 연결된 다른 컴퓨터 체크 
        for (int i = 0; i &lt; n; i++) {
            if (computers[current][i] == 1 &amp;&amp; !visited[i]) {
                dfs(i, visited, computers, n); // 그 컴퓨터로 이동 -&gt; 다시 연결 확인
            }
        }
    }
}</code></pre>
<h1 id="4-de-fault">4. De-fault</h1>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li><strong>DFS 시간 복잡도</strong>: <code>O(n^2)</code> (n: 컴퓨터 수)</li>
</ul>
<h2 id="개선-방향">개선 방향</h2>
<ul>
<li><strong>Union-Find 방식</strong>을 사용하면 네트워크의 개수를 세는 작업을 <strong>더 빠르게</strong> 할 수 있음
이 방법은 <code>O(n)</code>에 가까운 시간 복잡도로 해결할 수 있으며, 트리 구조로 연결을 관리하기 때문에 탐색 속도가 빠름.</li>
</ul>
<p>이 코드에서 <strong>잘못된 부분</strong>이나 <strong>개선할 점</strong>이 있다면 언제든지 <strong>댓글로</strong> 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] B+Tree]]></title>
            <link>https://velog.io/@do_e/DB-BTree</link>
            <guid>https://velog.io/@do_e/DB-BTree</guid>
            <pubDate>Thu, 20 Nov 2025 15:19:35 GMT</pubDate>
            <description><![CDATA[<h1 id="2-b-tree">2. B-Tree</h1>
<h2 id="2-1-b-tree-balanced-tree">2-1. B-Tree (Balanced Tree)</h2>
<h3 id="1-b-tree의-정의와-특징">1) B-Tree의 정의와 특징</h3>
<ul>
<li>B-Tree는 이름처럼 균형 잡힌 탐색 트리(Search Tree) 자료구조임.</li>
<li>DB의 인덱스나 파일 시스템에서 디스크 I/O를 최소화하여 대용량 데이터를 효율적으로 관리하기 위해 설계됨.</li>
<li>하나의 노드가 2개 이상의 데이터(Key)와 2개 이상의 자식 노드를 가질 수 있는 다진 트리 구조임.
(M-way Tree)</li>
</ul>
<br>

<h3 id="2-이진-탐색-트리와의-차이점">2) 이진 탐색 트리와의 차이점</h3>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>B-Tree (B-트리)</strong></th>
<th><strong>이진 탐색 트리 (BST)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>노드당 Key 수</strong></td>
<td><strong>여러 개</strong> (M-1개 이하)</td>
<td><strong>1개</strong></td>
</tr>
<tr>
<td><strong>자식 노드 수</strong></td>
<td><strong>2개 이상</strong> (최대 M개)</td>
<td><strong>최대 2개</strong></td>
</tr>
<tr>
<td><strong>균형 유지</strong></td>
<td><strong>자동 균형</strong> 유지 (항상 균형 잡힌 구조)</td>
<td>데이터 삽입 순서에 따라 <strong>불균형</strong>해질 수 있음 (별도의 균형 메커니즘 필요)</td>
</tr>
<tr>
<td><strong>주 사용처</strong></td>
<td>디스크 기반 환경 (DB 인덱스, 파일 시스템)</td>
<td>메모리 기반 환경 (알고리즘 구현)</td>
</tr>
<tr>
<td><strong>높이</strong></td>
<td>노드당 많은 Key를 가지므로 <strong>트리 높이가 매우 낮음</strong></td>
<td>노드당 1개의 Key를 가지므로 B-Tree보다 <strong>트리 높이가 높음</strong></td>
</tr>
<tr>
<td>- B-Tree는 노드당 더 많은 정보를 저장하여 트리의 전체 높이를 낮춤.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 트리의 높이가 낮다는 것은 디스크 I/O 횟수가 줄어들어 대용량 데이터 검색 속도가 빨라짐을 의미함.</td>
<td></td>
<td></td>
</tr>
<tr>
<td><br></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h3 id="3-균형-트리의-개념">3) 균형 트리의 개념</h3>
<ul>
<li>균형이란 삽입, 삭제 등의 작업이 발생해도 트리의 높이가 한쪽으로 치우치지 않고 일정하게 유지되는 것을 의미함.</li>
<li>B-Tree는 항상 균형을 유지하여, 어떤 데이터를 검색하든지 루트 노드에서 리프 노드까지의 경로 길이(트리 높이)가 같도록 보장함</li>
<li>이 덕분에 최악의 경우에도 검색 시간 복잡도가 O(log n)으로 안정적임. (BST는 최악의 경우 O(N))</li>
</ul>
<br>

<h2 id="2-2-b-tree의-계층적-구조">2-2. B-Tree의 계층적 구조</h2>
<p>B-Tree의 모든 노드는 기본적으로 같은 구조를 가지지만, 트리의 계층에 따라 역할이 구분됨.</p>
<h3 id="1-루트-노드-root-node">1) 루트 노드 (Root Node)</h3>
<ul>
<li>트리의 <strong>가장 위에 있는 유일한 노드</strong>이자 모든 탐색의 시작점dla.</li>
<li>트리가 비어있지 않은 한 반드시 존재하며, 다른 노드와 달리 키 개수와 자식 노드 개수에 대한 최소 제한 조건이 완화될 수 있음.</li>
</ul>
<h3 id="2-내부브랜치-노드-internalbranch-node">2) 내부(브랜치) 노드 (Internal/Branch Node)</h3>
<ul>
<li>루트 노드와 리프 노드 사이에 위치하며, 탐색 경로를 안내하는 역할을 함.</li>
<li>항상 <strong>자식 노드를 가지는 노드</strong></li>
<li>키(Key): 다음 탐색 경롤를 결정하기 위한 기준값</li>
<li>자식 포인터(Pointer): 하위 노드들을 가리키는 주소값</li>
</ul>
<h3 id="3-리프-노드-leaf-node">3) 리프 노드 (Leaf Node)</h3>
<ul>
<li>트리의 가장 낮은 레벨에 위치하며, 실제 데이터 또는 데이터의 위치를 담고 있는 노드</li>
<li>자식 노드를 가지지 않으며 B-Tree에서는 <strong>모든 리프 노드가 항상 동일한 깊이(Level)에 위치</strong>하여 균형을 유지함.</li>
</ul>
<br>

<h2 id="2-3-키와-포인터">2-3. 키와 포인터</h2>
<h3 id="1-b-tree-차수-order">1) B-Tree 차수 (Order)</h3>
<ul>
<li><p>B-Tree에서 <strong>하나의 노드가 가질 수 있는 최대 자식 수</strong>를 <strong>차수($M$)</strong> 또는 <strong>Order라고 함</strong></p>
</li>
<li><p>키 개수와의 관계</p>
<ul>
<li><p>차수가 M인 B-Tree는 하나의 노드에 <strong>최대 M-1개</strong>의 키(데이터)를 저장할 수 있음.</p>
</li>
<li><p>노드의 키 개수가 N개라면, 자식 포인터(자식 노드를 가리키는 포인터)는 <strong>N+1개를 가짐.</strong></p>
</li>
<li><p>이 차수(M)가 클수록 노드당 담는 키가 많아지므로 트리의 높이는 더욱 낮아져 디스크 I/O에 유리해짐.</p>
</li>
<li><p>e.g. 4차 B-Tree (M=4)</p>
<p>  최대 자식 수: 4개, 최대 키 개수: 4-1 = 3개</p>
</li>
</ul>
</li>
</ul>
<h3 id="2-노드의-저장-형태">2) 노드의 저장 형태</h3>
<p>차수가 M인 B-Tree의 한 노드는 $P_1, K_1, P_2, K_2, \dots, P_n, K_n, P_{n+1}$로 구성됨.</p>
<ul>
<li>n : 현재 노드가 가지고 있는 <strong>키(데이터)의 개수</strong></li>
<li>$Ki$: 데이터 (키) 값, 키 값은 정렬된 상태로 저장됨</li>
<li>$Pi$ : 자식 노드를 가리키는 포인터 (주소)</li>
</ul>
<h3 id="3-탐색-구조의-원리">3) 탐색 구조의 원리</h3>
<ul>
<li>키의 역할 (K)<ul>
<li>노드 내에서 해당 키의 왼쪽에 있는 모든 키는 $Ki$보다 작고, 오른쪽에 있는 모든 키는 크거나 같도록 트리를 분할하는 경계 역할을 함.</li>
</ul>
</li>
<li>포인터의 역할 (P)<ul>
<li>$P1$은 $K1$보다 작은 값을 가진 자식 노드를 가리킴</li>
</ul>
</li>
</ul>
<p>루트 노드에서 원하는 데이터를 검색할 때 <strong>매 노드마다 많은 수의 키를 비교</strong>하고 하나의 포인터를 따라 내려가므로, 트리의 높이가 낮아져 디스크 I/O 횟수를 최소화할 수 있음.</p>
<br>


<h1 id="3-btree의-기초">3. B+Tree의 기초</h1>
<h2 id="3-1-btree란-무엇인가">3-1. B+Tree란 무엇인가?</h2>
<ul>
<li><p>B+Tree는 자체 균형(self-balancing)이 가능한 <strong>트리 자료구조</strong>의 한 종류로, 특히 <strong>대용량 데이터</strong>를 저장하는 디스크 환경에서 최적화된 구조</p>
</li>
<li><p>범위 검색 최적화
리프 노드가 순차적으로 연결되어 있어, 시작점만 찾으면 리스트를 따라 순차적으로 데이터를 빠르게 탐색할 수 있음.</p>
</li>
<li><p>디스크 I/O 최소화
내부 노드가 키와 자식 노드의 포인터만 저장하여 노드 하나에 더 많은 키를 담을 수 있음.
이는 트리의 높이를 낮춰 데이터를 찾기 위해 디스크에 접근하는 횟수를 획기적으로 줄여줌.</p>
</li>
</ul>
<ul>
<li>DB에서 레코드를 빠르게 검색, 삽입, 삭제할 수 있도록 돕는 인덱스를 구현하는 데 가장 널리 사용됨.</li>
<li>디스크 접근 횟수를 최소화하여 검색 성능을 향상시키는 것이 주된 목적임.</li>
</ul>
<br>


<h2 id="3-2-btree의-시간-복잡도-및-균형-유지-원리">3-2. B+Tree의 시간 복잡도 및 균형 유지 원리</h2>
<p>B+Tree는 대용량 디스크 환경에 최적화된 자료구조로, 모든 주요 연산에서 일관되고 효율적인 로그 시간 복잡도를 제공하며, 이를 노드 분할(Split) &amp; 병합(Merge)을 통해 균형있게 유지함.</p>
<br>

<h3 id="1-btree의-시간-복잡도">1) B+Tree의 시간 복잡도</h3>
<p>B+Tree의 시간복잡도는 트리의 높이(h)에 의해 결정됨.</p>
<p>트리의 차수와 전체 데이터 레코드의 개수(n)을 사용할 때, 높이 h는 $O(\log_m n)$에 비례함</p>
<table>
<thead>
<tr>
<th><strong>연산</strong></th>
<th><strong>시간 복잡도</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>탐색 (Search)</strong></td>
<td>$O(h)$ 또는 $O(\log_m n)$</td>
<td>루트에서 시작해 리프 노드까지 도달하는 경로를 따라 내려가므로, 트리의 높이에 비례함.</td>
</tr>
<tr>
<td><strong>삽입 (Insertion)</strong></td>
<td>$O(h)$ 또는 $O(\log_m n)$</td>
<td>리프 노드를 찾는 시간 $O(h)$ 와 노드 분할(Split)이 발생할 경우 상위 노드로 전파되는 시간 $O(h)$ 가 더해져 로그 시간 복잡도를 가짐.</td>
</tr>
<tr>
<td><strong>삭제 (Deletion)</strong></td>
<td>$O(h)$ 또는 $O(\log_m n)$</td>
<td>리프 노드를 찾는 시간 $O(h)$ 와 노드 병합(Merge) 또는 재분배(Redistribution)가 발생할 경우 상위 노드로 전파되는 시간 $O(h)$ 가 더해져 로그 시간 복잡도를 가짐.</td>
</tr>
<tr>
<td><strong>범위 검색 (Range Search)</strong></td>
<td>$O(h + k)$</td>
<td>$k$는 원하는 범위에 속하는 데이터 레코드의 수임.</td>
</tr>
<tr>
<td>시작점을 찾는 데 $O(h)$가 소요된 후, 리프 노드의 연결 리스트를 따라 $k$개의 레코드를 순차적으로 탐색함.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<br>

<h3 id="2-btree의-균형-유지-원리-self-balancing-principle">2) B+Tree의 균형 유지 원리 (Self-Balancing Principle)</h3>
<p>B+Tree는 데이터의 삽입과 삭제가 빈번하게 일어나더라도 트리의 모든 리프 노드가 항상 같은 깊이에 위치하도록 유지하여 검색 성능의 일관성을 보장함.</p>
<p>이 메커니즘을 통해 B+Tree는 데이터의 동적인 변화에도 불구하고 항상 로그 시간 복잡도를 유지하며 효율적인 인덱스 역할을 수행할 수 있음.</p>
<p>이 균형을 유지하는 주요 매커니즘은 노드 분할과 노드 병합/재분배임.</p>
<p><strong>(1) 삽입 시 균형 유지: 노드 분할 (Node Split)</strong></p>
<p>데이터를 삽입하여 특정 노드의 키 개수가 최대 허용 개수를 초과했을 때 발생함.</p>
<ul>
<li>꽉 찬 노드를 두 개의 노드로 나눔.</li>
<li>나눠진 두 노드를 구분하는 중간 키(Median Key)를 선택함.</li>
<li>이 중간 키는 부모 노드로 승격되어 인텍스 엔트리로 사용되고, 부모 노드의 키 개수 규칙을 따르면서 분할된 두 자식 노드를 가리키게 됨</li>
</ul>
<p><strong>[전파, Propagation]</strong></p>
<p>승격된 키로 부모 노드 또한 최대 키 개수를 초과하면, 이 과정은 루트 노드까지 재귀적으로 반복될 수 있음.</p>
<p>루트노드까지 분할되면 트리의 높이가 1 증가함.</p>
<p><strong>(2) 삭제 시 균형 유지: 병합 및 재분배(Merge &amp; Redistribution)</strong></p>
<p>데이터를 삭제하여 특정 노드의 키 개수가 최소 허용 개수 미만이 되었을 때 발생함.</p>
<ul>
<li><p>재분배(<strong>Redistribution)</strong></p>
<p>  형제 노드에게서 키를 빌려와 최소 개수를 충족시키려고 시도함.</p>
<p>  이웃 노드가 여유 키를 가지고 있다면, 부모 노드의 키를 포함하여 키를 재분배하고 균형을 맞춤.</p>
</li>
<li><p>병합 (Merge)</p>
<p>  형제 노드마저 키가 부족하여 빌려줄 수 없다면, 해당 노드와 형제 노드를 하나로 함침.</p>
</li>
</ul>
<p><strong>[전파, Propagation]</strong></p>
<p>두 노드가 병합되면, 부모 노드에서 해당 두 노드를 연결하던 인덱스 키가 제거됨.</p>
<p>이 제거로 인해 부모 노드가 최소 키 개수 미만이 되면, 이 과정은 루트 노드까지 재귀적으로 반복, 병합되어 키가 하나도 남지 않게 되면 트리의 높이가 1 감소함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 인덱스]]></title>
            <link>https://velog.io/@do_e/DB-%EC%9D%B8%EB%8D%B1%EC%8A%A4</link>
            <guid>https://velog.io/@do_e/DB-%EC%9D%B8%EB%8D%B1%EC%8A%A4</guid>
            <pubDate>Thu, 20 Nov 2025 15:16:36 GMT</pubDate>
            <description><![CDATA[<h1 id="1-인덱스">1. 인덱스</h1>
<h2 id="1-1-인덱스란-무엇인가">1-1. 인덱스란 무엇인가?</h2>
<h3 id="1-인덱스의-정의와-역할">1) 인덱스의 정의와 역할</h3>
<ul>
<li>인덱스는 DB 테이블에 대한 검색 성능을 높이기 위해 특별한 자료구조로 구성된 매커니즘</li>
<li>마치 책의 목차나 색인과 같이, 원하는 데이터를 찾기 위해 테이블 전체를 훑어볼(Full Table Scan)  필요 없이 빠르게 해당 데이터가 위치한 곳을 찾아줌.</li>
<li>핵심 역할은 <strong>I/O(Input/Output) 횟수를 줄이는 것, 디스크 I/O는 성능 저하의 주범이므로, 이를 줄여 검색 속도를 획기적으로 향상시킴.</strong></li>
</ul>
<br>

<h3 id="2-왜-인덱스를-사용하는가">2) 왜 인덱스를 사용하는가?</h3>
<ul>
<li>검색 속도 향상<ul>
<li>특정 조건에 맞는 데이터를 찾을 때, 인덱스를 사용하면 테이블의 특정 범위만 빠르게 탐색할 수 있음.</li>
</ul>
</li>
<li>정렬 및 그룹화 용이<ul>
<li>데이터를 정렬 (ORDER BY)하거나 그룹화(GROUP BY)할 때 이미 인덱스 자체가 정렬된 상태이므로, 별도의 정렬 작업 없이 인덱스를 활용하여 처리 속도를 높임.</li>
</ul>
</li>
<li>고유성 보장<ul>
<li><code>NIQUE</code> 인덱스를 설정하면 해당 컬럼의 데이터 중복을 방지하여 데이터의 <strong>무결성</strong>을 보장할 수 있음</li>
</ul>
</li>
</ul>
<br>

<h3 id="3-인덱스가-crud-성능에-미치는-영향">3) 인덱스가 CRUD 성능에 미치는 영향</h3>
<p>인덱스는 <strong>검색(Read)</strong> 성능은 높여주지만, <strong>데이터 변경(Write)</strong> 작업에는 추가 비용을 발생시킴.</p>
<p>인덱스가 많아질수록 <strong>검색(Read)</strong> 성능은 좋아지지만, <strong>삽입/수정/삭제(Write)</strong> 시의 부하가 커지므로, 테이블 전체적인 작업 부하를 고려하여 <strong>적절한 개수와 컬럼</strong>에만 인덱스를 설정해야 함.</p>
<ul>
<li>CREATE (삽입)<ul>
<li>새로운 데이터가 삽입될 때, <strong>데이터 레코드</strong>뿐만 아니라 <strong>인덱스 페이지</strong>에도 새로운 키를 삽입하는 작업이 추가로 발생함.</li>
</ul>
</li>
<li>READ<ul>
<li>인덱스를 통해 필요한 레코드에 빠르게 접근하므로, 가장 큰 성능 이점을 얻는 작업임.</li>
</ul>
</li>
<li>UPDATE<ul>
<li>인덱스가 설정된 컬럼의 값이 변경될 경우, 기존 인덱스를 <strong>삭제</strong>하고 새로운 인덱스를 <strong>삽입</strong>하는 두 가지 작업이 발생하여 비용이 큼.</li>
</ul>
</li>
<li>DELETE<ul>
<li>데이터 레코드를 삭제하고, 해당 레코드에 대한 <strong>인덱스 항목도 함께 삭제</strong>해야 하므로 추가 작업이 필요함.</li>
</ul>
</li>
</ul>
<br>

<h2 id="1-2-클러스터형-인덱스--비클러스터형-인덱스">1-2. 클러스터형 인덱스 &amp; 비클러스터형 인덱스</h2>
<p>DB에서 데이터를 저장하고 찾는 방식에 따라 인덱스는 크게 클러스터형과 비클러스터형으로 나뉨.</p>
<p>이 둘은 데이터의 물리적 정렬 방식과 인덱스 리프 노드에 무엇이 저장되는지에 차이가 있음.</p>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>클러스터형 인덱스 (Clustered Index)</strong></th>
<th><strong>비클러스터형 인덱스 (Non-Clustered Index)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>물리적 정렬</strong></td>
<td><strong>O</strong> (데이터 자체가 인덱스 순서로 정렬됨)</td>
<td><strong>X</strong> (인덱스만 정렬되고, 데이터는 임의의 위치에 저장됨)</td>
</tr>
<tr>
<td><strong>개수 제한</strong></td>
<td>테이블당 <strong>1개</strong>만 가능</td>
<td>테이블당 <strong>여러 개</strong> 생성 가능</td>
</tr>
<tr>
<td><strong>리프 노드</strong></td>
<td><strong>실제 데이터 레코드</strong> (데이터가 곧 인덱스)</td>
<td><strong>데이터의 위치를 가리키는 포인터(주소값)</strong></td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>검색 속도가 가장 빠름 (특히 범위 검색)</td>
<td>유연하게 여러 컬럼에 생성 가능, 삽입/수정 부하 상대적으로 적음</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>삽입/수정 시 데이터 정렬 부하 큼</td>
<td>데이터를 찾기 위해 <strong>추가적인 Lookup</strong> 필요</td>
</tr>
<tr>
<td><strong>주요 사용처</strong></td>
<td>기본 키(Primary Key)</td>
<td>외래 키(Foreign Key)나 자주 검색되는 컬럼</td>
</tr>
</tbody></table>
<br>

<h3 id="클러스터형-인덱스-clustered-index">클러스터형 인덱스 (Clustered Index)</h3>
<ul>
<li><p><strong>정의 &amp; 특징</strong></p>
<ul>
<li>테이블의 물리적인 데이터 행을 인덱스 키 값의 순서대로 정렬하여 저장하는 방식</li>
<li>데이터 자체가 묶여 한 가지 방식으로만 물리적으로 정렬되어서 저장됨.</li>
<li>테이블당 오직 하나만 생성할 수 있음.</li>
<li>대부분의 DB에서는 PK를 지정하면 자동으로 클러스터형 인덱스가 생성됨.</li>
</ul>
</li>
<li><p><strong>구조적 특징</strong></p>
<ul>
<li>인덱스의 리프 노드가 곧 실제 데이터 레코드 자체임.</li>
<li>인덱스를 탐색하는 것 자체가 실제 데이터에 도달하는 것을 의미함.</li>
</ul>
</li>
<li><p><strong>장점</strong></p>
<ul>
<li>인덱스 검색 후 추가적인 데이터 조회 과정이 필요 없어 검색 속도가 매우 빠름</li>
<li>데이터가 이미 순서대로 물리적으로 인접하게 저장되어 있기 때문에 범위 검색(Range Query)에 매우 효율적임.</li>
</ul>
</li>
<li><p><strong>단점</strong></p>
<ul>
<li>데이터 삽입 밑 수정 시. ㅔ이터의 물리적 위치를 재정렬해야 하는 경우가 생겨 부하가 큼.</li>
<li>인덱스의 크기가 곧 테이블의 크기와 같기 때문에 인덱스를 생성하는 데 시간이 오래 걸림.</li>
</ul>
</li>
</ul>
<br>

<h3 id="비클러스터형-인덱스-secondarynon-clustered-index">비클러스터형 인덱스 (Secondary/Non-Clustered Index)</h3>
<ul>
<li><p><strong>정의 &amp; 특징</strong></p>
<ul>
<li>데이터의 물리적 정렬과는 별개로, 인덱스만 따로 정렬하여 관리하는 방식</li>
<li>테이블당 여러 개를 생성할 수 있음</li>
<li>PK가 아닌 일반 컬럼에 생성하는 모든 인덱스가 여기에 해당됨.</li>
</ul>
</li>
<li><p><strong>구조적 특징</strong></p>
<ul>
<li>인덱스의 리프 노드에는 데이터 레코드 자체가 아닌, 해당 데이터를 찾을 수 있는 주소값(포인터)이 저장됨.<ul>
<li>MySQL InnoDB: 클러스터형 인덱스의 <strong>기본 키 값</strong>이 주소값으로 저장됨.</li>
<li>SQL Server 등: 물리적 위치 주소(RID)나 클러스터 키 값 등이 저장됨.</li>
</ul>
</li>
<li>따라서 검색 과정은 인덱스 검색 → 주소값 획득 → 데이터 레코드 재조회 (Lookup)의 2단계로 이루어짐</li>
</ul>
</li>
<li><p><strong>장점</strong></p>
<ul>
<li>데이터 삽입 / 수정 시 데이터 레코드의 물리적 위치를 변경할 필요가 없어 클러스터형 인덱스보다 <strong>부하가 적음.</strong></li>
<li>테이블당 여러 개를 만들 수 있어 다양한 컬럼 조합으로 검색 성능을 높일 수 있음.</li>
</ul>
</li>
<li><p><strong>단점</strong></p>
<ul>
<li>인덱스를 통해 데이터의 주소를 얻은 후 실제 데이터를 다시 한 번 찾아가는 추가적 작업(Lookup)이 필요하므로, 클러스터형 인덱스보다 검색 속도가 약간 느릴 수 있음.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Problem Solving] Programmers 42898. 등굣길]]></title>
            <link>https://velog.io/@do_e/Problem-Solving-Programmers-42898.-%EB%93%B1%EA%B5%A3%EA%B8%B8</link>
            <guid>https://velog.io/@do_e/Problem-Solving-Programmers-42898.-%EB%93%B1%EA%B5%A3%EA%B8%B8</guid>
            <pubDate>Sun, 16 Nov 2025 10:35:26 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>m * n 크기의 격자 모양</li>
<li>집의 좌표 (1,1), 학교의 좌표 (m,n)</li>
<li>int[][] puddles 좌표가 주어짐</li>
<li>오른쪽, 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단 경로의 개수를 1,000,000,007로 나눈 나머지를 return</li>
</ul>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>1 &lt;= n, m &lt;= 100</li>
<li>puddles는 0개 이상 10개 이하</li>
<li>집과 학교가 물에 잠긴 경우는 입력으로 주어지지 않음 </li>
</ul>
<br>


<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="문제-유형-파악-동적-계획법dp">문제 유형 파악: 동적 계획법(DP)</h2>
<ul>
<li><p>** 최적 부분 구조를 가짐**
큰 문제의 해답이 작은 문제의 해답들로 구성되는 경우!
집 ~ 목표 지점까지의 최단 경로의 경우의 수는 그 이전 지점들로 오는 경우의 수들의 합으로 결정됨
문제의 제약 조건: 오른쪽 또는 아래쪽으로만 이동 가능
=&gt; 해당 칸으로 이동하는 경우의 수 = 왼쪽에서 오는 경우 + 위쪽에서 오는 경우</p>
</li>
<li><p><strong>DFS 탐색으로 푸는 경우</strong>
모든 경로를 단순 재귀 호출 (DFS)로 탐색한다면, 여러 경로를 탐색하는 과정에서 동일한 중간지점까지 도달하는 경우의 수를 반복해서 계산하게 됨
DP를 사용할 경우 한 번 계산된 결과를 메모이제이션해두고, 필요할 때마다 저장된 값을 재활용하여 계산 속도를 높임.</p>
<br>

</li>
</ul>
<h1 id="3-풀이">3. 풀이</h1>
<h2 id="로직-문제-쪼개기">[로직] 문제 쪼개기</h2>
<h3 id="1-dp-배열-정의--초기화">1) DP 배열 정의 &amp; 초기화</h3>
<pre><code class="language-java">        // 1. dp 배열 선언 
        int[][] dp = new int[n][m];

        // 2. 물 웅덩이에 -1 표시 (0-based index)
        for(int[] puddle: puddles){
            int x = puddle[0]-1;
            int y= puddle[1]-1;
            dp[y][x] =-1;
        }

        // 3. 시작위치 표시: 집(0,0)에 1 표시
        if(dp[0][0]!=-1){
            dp[0][0]=1;
        }else{
            return 0;
        }</code></pre>
<ul>
<li>dp 2차원 배열: 1-based index -&gt; 0-based index</li>
<li>dp 배열 선언 시 사이징 (열, 행) =&gt; (행, 열)
문제에서 m : 가로 길이 =&gt; 열, n : 세로 길이 =&gt; 행
학교의 (행,열)위치 좌표가 (3,4)이지만 (4,3)으로 표기됨
이것을 다시 (행, 열)로 바꾸어서 문제 풀기</li>
</ul>
<h3 id="2-dp-테이블-채우기">2) DP 테이블 채우기</h3>
<pre><code class="language-java">    for(int i=0; i&lt; n; i++){
            for(int j=0; j&lt;m; j++){
                int leftPaths=0;
                int topPaths=0;

                // 0) 시작점 채워져 있음 -&gt; pass
                if(i==0 &amp;&amp; j==0) continue;

                // 1) 물 웅덩이라면
                if(dp[i][j]==-1){
                    dp[i][j]=0; // 경로 개수 = 0
                    continue; // 다음 칸으로
                }

                // 3) + 위쪽 칸 경로 개수
                if(i&gt;0){
                    topPaths = dp[i-1][j];
                }
                // 4) + 왼쪽 칸 경로 개수
                if(j&gt;0){
                    leftPaths=dp[i][j-1];
                }

                // 5) !! 합산 &amp; 모듈러 연산
                dp[i][j] = (topPaths + leftPaths) % mod;
            }
        }</code></pre>
<ul>
<li>2차원 배열을 2중 for문으로 순회하면서 탐색 좌표가 물웅덩이이거나 시작점이면 건너뜀 (continue)</li>
<li>위쪽 경로의 경우의 수를 구할 때 경계 확인: i&gt;0 
(i=0 위의 행이 없기 때문)</li>
<li>왼쪽 경로의 경우의 수를 구할 때 경계 확인: j&gt;0 
(j=0 왼쪽의 행이 없기 때문)</li>
</ul>
<h3 id="3-출력">3) 출력</h3>
<pre><code class="language-java">answer = dp[n-1][m-1];
return answer;</code></pre>
<br>


<h1 id="4-코드">4. 코드</h1>
<pre><code class="language-java">class Solution {
    public int solution(int m, int n, int[][] puddles) {

        final int mod = 1000000007;
        int answer = 0;

        // !!
        // m : 가로 길이 =&gt; 열
        // n : 세로 길이 =&gt; 행
        // 학교의 위치: (m,n)-&gt; (4,3) =&gt; (3,4)
        // 1. dp 배열 선언 
        int[][] dp = new int[n][m];

        // 2. 물 웅덩이에 -1 표시 (0-based index)
        for(int[] puddle: puddles){
            int x = puddle[0]-1;
            int y= puddle[1]-1;
            dp[y][x] =-1;
        }

        // 3. 시작위치 표시: 집(0,0)에 1 표시
        if(dp[0][0]!=-1){
            dp[0][0]=1;
        }else{
            return 0;
        }

        // 4. dp 테이블 채우기
        for(int i=0; i&lt; n; i++){
            for(int j=0; j&lt;m; j++){
                int leftPaths=0;
                int topPaths=0;

                // 0) 시작점 채워져 있음 -&gt; pass
                if(i==0 &amp;&amp; j==0) continue;

                // 1) 물 웅덩이라면
                if(dp[i][j]==-1){
                    dp[i][j]=0; // 경로 개수 = 0
                    continue; // 다음 칸으로
                }

                // 3) + 위쪽 칸 경로 개수
                if(i&gt;0){
                    topPaths = dp[i-1][j];
                }
                // 4) + 왼쪽 칸 경로 개수
                if(j&gt;0){
                    leftPaths=dp[i][j-1];
                }

                // 5) !! 합산 &amp; 모듈러 연산
                dp[i][j] = (topPaths + leftPaths) % mod;
            }
        }

        answer = dp[n-1][m-1];

        return answer;
    }
}</code></pre>
<br>

<h1 id="5-de-fault">5. De-fault</h1>
<h2 id="fault-1-2차원-배열의-인덱싱">fault-1. 2차원 배열의 인덱싱</h2>
<ul>
<li><strong>문제</strong>
문제에서 (m,n)으로 주어졌을 때 바로 dp[m][n]으로 배열을 생성하면 인덱스가 꼬임</li>
<li><strong>해결</strong>
문제에서 (n,m) &amp; 1-based index
=&gt; (m, n) &amp; 0-based index로 바꾸기</li>
</ul>
<h2 id="fault-2-자료형과-오버-플로우">fault-2. 자료형과 오버 플로우</h2>
<ul>
<li><strong>문제</strong>
경로의 개수가 int 범위를 쉽게 초과할 수 있음.</li>
<li><strong>해결</strong>
DP 배열을 long 타입으로 선언하고, 덧셈 연산 후 반드시 % 1000000007 모듈러 연산을 적용하는 방식을 고려.<br>

</li>
</ul>
<h2 id="fault-3-경계-및-물웅덩이-처리">fault-3. 경계 및 물웅덩이 처리</h2>
<ul>
<li><strong>문제</strong>
맨 위 줄(i=0)이나 맨 왼쪽 줄(j=0)에서 
DP[i-1][j]나 DP[i][j-1]를 참조하면 <code>ArrayIndexOutOfBoundsException</code> 발생.</li>
<li><strong>해결</strong>
if (i &gt; 0) 및 if (j &gt; 0) 조건문을 사용하여 경계 밖 참조를 막음.
물웅덩이는 미리 0으로 설정하여, 덧셈에 포함되더라도 경로 개수 합산에 오류를 일으키지 않도록 함.</li>
</ul>
<h2 id="시간-복잡도-onm">시간 복잡도: O(N*M)</h2>
<ol>
<li>*<em>DP 배열을 순회하는 2중 for문 *</em> (<code>O(N*M)</code>)
N,M &lt;= 100</li>
<li><strong>각 칸의서의 연산</strong> (<code>O(1)</code>)</li>
</ol>
<hr>
<br>
이 코드에서 잘못된 부분이나 개선할 점이 있다면 언제든지 댓글로 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] ACID]]></title>
            <link>https://velog.io/@do_e/ACID</link>
            <guid>https://velog.io/@do_e/ACID</guid>
            <pubDate>Thu, 13 Nov 2025 22:49:44 GMT</pubDate>
            <description><![CDATA[<h1 id="1-acid란-무엇인가">1. ACID란 무엇인가?</h1>
<h2 id="1-1-acid의-정의">1-1. ACID의 정의</h2>
<ul>
<li><p><strong>ACID의 정의</strong>: 데이터베이스 <strong>트랜잭션</strong>의 <strong>안정성</strong> 및 <strong>신뢰성</strong>을 보장하는 4가지 핵심 속성 (Atomicity, Consistency, Isolation, Durability)의 약자임.</p>
<p>  <strong>A</strong>: <strong>Atomicity</strong> (원자성)</p>
<p>  <strong>C</strong>: <strong>Consistency</strong> (일관성)</p>
<p>  <strong>I</strong>: <strong>Isolation</strong> (격리성)</p>
<p>  <strong>D</strong>: <strong>Durability</strong> (지속성)</p>
</li>
<li><p><strong>각 속성의 상호 관계</strong></p>
<p>  4가지 속성은 <strong>독립적</strong>이지만, 트랜잭션의 성공을 위해 서로 <strong>상호 보완적임.</strong></p>
<p>  이 중 하나라도 깨지면 트랜잭션이 <strong>실패</strong>하거나 <strong>데이터 문제</strong>가 발생함.</p>
</li>
<li><p><strong>트랜잭션의 정의 &amp; ACID의 관계</strong></p>
<p>  <strong>트랜잭션</strong>: 데이터베이스의 상태를 변화시키는 하나의 <strong>논리적 작업 단위</strong></p>
<p>  <strong>ACID</strong>는 이 트랜잭션이 성공적으로 완료되기 위한 <strong>필수 조건</strong></p>
</li>
</ul>
<br>

<h2 id="1-2-왜-필요한가">1-2. 왜 필요한가?</h2>
<ul>
<li><p><strong>데이터 유실, 불일치 등의 문제 발생 시나리오 방지</strong></p>
<p>  트랜잭션 처리 중 시스템 오류가 발생했을 때 <strong>데이터 불일치</strong>나 <strong>유실</strong>을 막기 위해 필수적임</p>
<p>  e.g. 은행 계좌 이체 중 송금은 완료되고 입금이 되지 않는 <strong>심각한 오류</strong>를 방지함.</p>
</li>
<li><p><strong>특히 금융 거래 등 중요 시스템에서의 역할</strong></p>
<p>  금융, 예약 시스템 등 <strong>데이터의 정확성과 신뢰성</strong>이 절대적으로 요구되는 시스템에서 <strong>시스템 신뢰도</strong>를 보장함.</p>
</li>
</ul>
<br>

<hr>
<h2 id="2-a-원자성-atomicity">2. A: 원자성 (Atomicity)</h2>
<p>All or Nothing </p>
<p>트랜잭션 내의 모든 작업은 하나의 단위로 실행되며, <strong>일부만 실행되거나 실패하지 않음</strong>을 보장함.</p>
<ul>
<li><strong>작동 원리</strong>:<ul>
<li>트랜잭션 내 모든 작업은 하나로 묶여 처리됨.</li>
<li><strong>성공 시</strong> 모든 작업 <strong>커밋 (Commit)</strong>, <strong>실패 시</strong> 모든 작업 <strong>롤백 (Rollback)</strong> 처리됨.</li>
</ul>
</li>
<li><strong>핵심 목표</strong>: 트랜잭션의 <strong>부분 완료 방지</strong>를 통해 데이터의 불일치 또는 불완전한 상태를 방지함.</li>
<li><strong>원자성 위반 시 문제</strong>: <strong>부분 업데이트 (Partial Update)</strong> 발생</li>
</ul>
<br>
---

<h2 id="3-c-일관성-consistency">3. C: 일관성 (Consistency)</h2>
<p> <strong>*<em>트랜잭션 전후 데이터베이스가 *</em>유효한 상태를 유지함.</strong></p>
<ul>
<li><p>데이터베이스에 정의된 제약 조건 (Constraints)을 반드시 준수해야 함.</p>
</li>
<li><p><strong>작동 원리</strong>:</p>
<ul>
<li>트랜잭션이 아무리 복잡해도 <strong>외래 키, 도메인 제약, 무결성 제약</strong> 등 모든 <strong>데이터 규칙</strong>을 준수함.</li>
<li>트랜잭션 전후 데이터베이스는 항상 유효한 상태임.</li>
</ul>
</li>
<li><p><strong>핵심 목표</strong>: 데이터베이스의 <strong>논리적 규칙</strong>을 깨지 않도록 보장함.</p>
</li>
<li><p><strong>일관성 위반 시 문제</strong>: <strong>데이터베이스 제약 조건 파괴</strong></p>
<p>  e.g. 외래 키를 위반하거나 계좌 <strong>잔액이 마이너스</strong>가 되는 경우 시스템이 잘못된 상태로 처리됨</p>
</li>
</ul>
<br>
---

<h2 id="4-i-격리성-isolation">4. I: 격리성 (Isolation)</h2>
<p>여러 트랜잭션이 동시에 실행되어도 서로 <strong>독립적으로 보이도록</strong> 보장함.</p>
<p>각 트랜잭션은 다른 트랜잭션의 <strong>영향을 받지 않음</strong>을 보장함.</p>
<ul>
<li><strong>핵심 목표</strong>: <strong>데이터 부정합</strong>을 방지하는 <strong>동시성 제어</strong></li>
<li><strong>격리성 위반 시 문제</strong>: <strong>동시성 제어 문제</strong><ul>
<li><strong>Dirty Read (더티 읽기)</strong>: 롤백될 데이터를 읽는 문제.</li>
<li><strong>Non-Repeatable Read (반복 불가능 읽기)</strong>: 같은 데이터를 두 번 읽었을 때 값이 달라지는 문제.</li>
<li><strong>Phantom Read (유령 읽기)</strong>: 트랜잭션 내에서 새로운 데이터가 추가/삭제되어 보이는 문제.</li>
</ul>
</li>
</ul>
<h3 id="격리-수준-isolation-levels"><strong>격리 수준 (Isolation Levels)</strong></h3>
<p>동시성 문제를 다루기 위해 표준화된 4가지 레벨이 있음 (낮은 수준 → 높은 수준)</p>
<ul>
<li><strong>Read Uncommitted</strong>: 커밋되지 않은 데이터도 읽을 수 있어 <strong>Dirty Read</strong> 가능.</li>
<li><strong>Read Committed</strong>: 커밋된 데이터만 읽을 수 있음.</li>
<li><strong>Repeatable Read</strong>: 같은 데이터에 대해 반복해서 읽었을 때 값이 같아야 함 (<strong>Non-Repeatable Read</strong> 방지).</li>
<li><strong>Serializable</strong>: 모든 트랜잭션이 순차적으로 실행되는 것처럼 보임 (가장 엄격).</li>
</ul>
<br>
---

<h2 id="5-d-지속성-durability">5. D: 지속성 (Durability)</h2>
<p> <strong>*<em>트랜잭션이 *</em>커밋된 후</strong>의 데이터는 <strong>영구적으로 기록</strong>되어 유실되지 않음을 보장함.</p>
<p>커밋된 데이터는 시스템 <strong>장애</strong>나 <strong>정전</strong>이 발생해도 보존됨.</p>
<ul>
<li><p><strong>작동 원리</strong></p>
<ul>
<li>커밋 후, 데이터는 주로 <strong>로그 파일</strong> 기록 및 <strong>디스크 쓰기</strong> 메커니즘을 통해 안전하게 영구 저장됨.</li>
<li>메모리뿐만 아니라 영구 저장 장치 (디스크)에 기록되는 것을 의미함.</li>
</ul>
</li>
<li><p><strong>핵심 목표</strong>: <strong>데이터 영구 보존</strong> 보장</p>
</li>
<li><p><strong>지속성 위반 시 문제</strong>: <strong>커밋된 데이터 유실 (Lost Commit)</strong></p>
<p>  e.g. 시스템 장애 발생 시, 커밋 직후의 데이터가 디스크에 기록되지 못하고 메모리에서 사라져 데이터가 유실됨.</p>
</li>
</ul>
<br>
---

<h2 id="6-acid-i--격리주준별-보장-범위-및-성능-trade-off">6. ACID-I ) 격리주준별 보장 범위 및 성능 Trade-off</h2>
<h3 id="1-read-uncommitted-가장-낮은-격리-수준">1) Read Uncommitted (가장 낮은 격리 수준)</h3>
<ul>
<li><p><strong>보장 범위</strong></p>
<p>  <strong>가장 낮음</strong>: <strong>커밋되지 않은 데이터</strong>의 읽기를 허용</p>
</li>
<li><p><strong>발생 가능한 문제</strong></p>
<ul>
<li><strong>Dirty Read</strong> (오염된 데이터 읽기): 다른 트랜잭션이 Rollback할 데이터를 읽음.</li>
<li><strong>Non-Repeatable Read</strong> 및 <strong>Phantom Read</strong> 모두 발생 가능</li>
</ul>
</li>
<li><p><strong>성능 Trade-off</strong>:</p>
<ul>
<li><strong>성능 우수 (가장 빠름)</strong>: Lock 사용을 <strong>최소화</strong>하여 동시성이 가장 높음.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-read-committed-중간-격리-수준-대부분의-dbms-기본값">2) Read Committed (중간 격리 수준, 대부분의 DBMS 기본값)</h3>
<ul>
<li><p><strong>보장 범위</strong></p>
<p>  <strong>Dirty Read 방지</strong>: <strong>커밋된 데이터</strong>만 읽도록 보장함.</p>
</li>
<li><p><strong>발생 가능한 문제</strong></p>
<ul>
<li><strong>Non-Repeatable Read</strong> 및 <strong>Phantom Read</strong> 발생 가능</li>
</ul>
</li>
<li><p><strong>성능 Trade-off</strong></p>
<ul>
<li><strong>성능 균형</strong>: 적절한 Lock 사용으로 Dirty Read를 막으면서도 합리적인 동시성을 유지함.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-repeatable-read-높은-격리-수준">3) Repeatable Read (높은 격리 수준)</h3>
<ul>
<li><p><strong>보장 범위</strong></p>
<p>  Dirty Read 방지.</p>
<p>  <strong>Non-Repeatable Read 방지</strong>: </p>
<p>  트랜잭션 내에서 같은 데이터를 여러 번 읽을 때 항상 <strong>동일한 값</strong>을 보장함.</p>
</li>
<li><p><strong>발생 가능한 문제</strong></p>
<ul>
<li><strong>Phantom Read 발생 가능</strong>: SELECT 조건에 맞는 행의 수는 달라질 수 있음.</li>
</ul>
</li>
<li><p><strong>성능 Trade-off</strong></p>
<ul>
<li><strong>성능 저하</strong>: 읽기 작업에도 Lock을 걸어 트랜잭션 종료 시까지 유지하므로 동시성 저하가 발생함.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-serializable-가장-높은-격리-수준">4) Serializable (가장 높은 격리 수준)</h3>
<ul>
<li><p><strong>보장 범위</strong></p>
<p>  Dirty Read, Non-Repeatable Read, Phantom Read <strong>모두 방지</strong> (데이터 정합성 최고).</p>
<p>  모든 트랜잭션이 <strong>순차적으로 실행</strong>되는 것과 같은 효과를 보장함.</p>
</li>
<li><p><strong>발생 가능한 문제</strong></p>
<ul>
<li><strong>없음</strong>: Isolation이 <strong>완벽하게 보장됨.</strong></li>
</ul>
</li>
<li><p><strong>성능 Trade-off</strong></p>
<ul>
<li><strong>성능 매우 저하 (가장 느림)</strong>: 광범위하고 엄격한 Lock 사용으로 인해 동시성 처리 성능이 크게 감소하고 <strong>Deadlock</strong> 가능성이 증가합니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 이상현상과 정규화]]></title>
            <link>https://velog.io/@do_e/DB-%EC%9D%B4%EC%83%81%ED%98%84%EC%83%81%EA%B3%BC-%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@do_e/DB-%EC%9D%B4%EC%83%81%ED%98%84%EC%83%81%EA%B3%BC-%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Wed, 05 Nov 2025 15:26:54 GMT</pubDate>
            <description><![CDATA[<h1 id="1-정규화normalization">1. 정규화(Normalization)</h1>
<h2 id="1-1-정규화란-무엇인가"><strong>1-1. 정규화란 무엇인가?</strong></h2>
<p>DB 설계는 데이터의 중복을 최소화하고, 일관성 &amp; 무결성을 유지하기 위한 기본 과정이고, 정규화는 이 설계의 핵심 기법임.</p>
<p>정규화?</p>
<p>데이터의 일관성 확보 및 중복을 최소화하기 위해 테이블을 구조화하는 과정</p>
<h2 id="1-2-이상-현상">1-2. 이상 현상</h2>
<p>DB가 잘못 설계되어 데이터가 중복적으로 쌓여있으면, 데이터를 다룰 때 심각한 문제가 발생함</p>
<p>정규화되지 않은 테이블에서 자주 발생하는 문제를 ‘이상현상’이라 부름</p>
<h3 id="삽입-이상-insertion-anomaly"><strong>삽입 이상 (Insertion Anomaly)</strong></h3>
<p>불필요한 데이터 없이는 삽입이 불가능한 문제</p>
<h3 id="갱신-이상-update-anomaly"><strong>갱신 이상 (Update Anomaly)</strong></h3>
<p><strong>동일한 데이터가 여러 곳에 중복 저장되어, 수정할 때 일부만 수정되고 일부는 누락되는 현상</strong></p>
<h3 id="삭제-이상-deletion-anomaly"><strong>삭제 이상 (Deletion Anomaly)</strong></h3>
<p><strong>특정 데이터를 삭제했을 때,</strong> 필요한 정보까지 함께 삭제되는 문제</p>
<h2 id="1-3-정규화-목적과-필요성"><strong>1-3. 정규화 목적과 필요성</strong></h2>
<ul>
<li>중복 최소화</li>
<li>데이터 무결성 향상</li>
<li>구조적 일관성 확보</li>
<li>쿼리 성능 &amp; 유지보수 용이성 확보</li>
</ul>
<h1 id="2-정규화를-위한-핵심-개념-함수-종속성과-키">2. 정규화를 위한 핵심 개념: 함수 종속성과 키</h1>
<h2 id="1-함수-종속성-fd-functional-dependency"><strong>1) 함수 종속성 (FD, Functional Dependency)</strong></h2>
<p>하나의 속성이 다른 속성의 값을 고유하게 결정짓는 관계</p>
<ul>
<li>정의: A가 B를 결정한다 A → B
특정 속성이 다른 속성 값을 결정하는 관계
(e.g. <code>학생ID</code> → <code>학생이름</code>)</li>
</ul>
<h3 id="함수-종속성의-종류">함수 종속성의 종류</h3>
<p><strong>1) 완전 함수 종속 (Full Functional Dependency)</strong></p>
<p>복합키 전체에 의해 다른 속성이 결정되는 경우</p>
<ul>
<li>(학생 ID, 과목코드) → 성적
성적은 학생 ID, 과목코드 모두 필요</li>
</ul>
<p><strong>2) 부분적 함수 종속 (Partial Functional Dependency)</strong></p>
<p>복합키 일부만으로 다른 속성이 결정되는 경우</p>
<ul>
<li>(학생 ID, 과목코드) → 학생 이름
학생 이름은 학생 ID에만 종속되어 있으므로, 복합키 전체에 종속되지 않음</li>
</ul>
<p><strong>3) 이행적 종속 (Transitive Functional Dependency)</strong></p>
<p>A → B, B → C이면 A → C 관계가 성립하는 경우</p>
<ul>
<li>학번 → 학과 코드, 학과코드 → 학과명
학번 → 학과명 (이행적 종속)</li>
</ul>
<h3 id="함수-종속성--정규화의-관계">함수 종속성 &amp; 정규화의 관계</h3>
<table>
<thead>
<tr>
<th>정규형</th>
<th>제거 대상</th>
<th>관련된 함수 종속성</th>
</tr>
</thead>
<tbody><tr>
<td>1NF</td>
<td>비원자값</td>
<td>-</td>
</tr>
<tr>
<td>2NF</td>
<td>부분 함수 종속</td>
<td>복합키의 일부 종속</td>
</tr>
<tr>
<td>3NF</td>
<td>이행적 함수 종속</td>
<td>A → B → C 구조</td>
</tr>
<tr>
<td>BCNF</td>
<td>비후보키 결정자</td>
<td>모든 결정자가 후보키여야 함</td>
</tr>
<tr>
<td>4NF</td>
<td>다중값 종속</td>
<td>독립적 다대다 관계</td>
</tr>
<tr>
<td>5NF</td>
<td>조인 종속</td>
<td>조인 시 중복 발생 제거</td>
</tr>
</tbody></table>
<p>정규화는 함수 종속성을 분석하고, 그 관계를 기준으로 테이블을 분리하는 과정을 수행함.</p>
<h2 id="2-키">2) 키</h2>
<p>정규화의 모든 단계는 결국 어떤 속성이 어떤 키에 종속되는가를 기준으로 판단하기 때문에 키는 정규화의 축이라고 할 수 있음</p>
<p><strong>후보키 (Candidate Key)</strong></p>
<ul>
<li>한 릴레이션에서 각 튜플(행)을 <strong>유일하게 식별할 수 있는 최소 속성 집합</strong></li>
<li>여러 개 존재할 수 있음</li>
<li>e.g. (주민등록번호), (이메일), (전화번호) — 모두 유일하다면 후보키</li>
</ul>
<p><strong>기본키 (Primary Key)</strong></p>
<ul>
<li>후보키 중에서 <strong>대표로 선택된 하나의 키</strong></li>
<li>NULL이거나 중복될 수 없음</li>
<li>데이터 무결성의 중심이 되는 키</li>
</ul>
<p><strong>대체키 (Alternate Key)</strong></p>
<ul>
<li>후보키 중 기본키로 선택되지 않은 나머지 키</li>
<li>e.g. 이메일이나 전화번호를 대체키로 설정 가능</li>
</ul>
<p><strong>외래키 (Foreign Key)</strong></p>
<ul>
<li>다른 테이블의 기본키를 참조하는 속성</li>
<li>테이블 간의 <strong>관계(Relationship)</strong> 를 표현</li>
<li>e.g. <code>주문.회원ID</code> → <code>회원.회원ID</code></li>
</ul>
<h3 id="정규화--키">정규화 &amp; 키</h3>
<table>
<thead>
<tr>
<th>개념</th>
<th>정규화에서의 역할</th>
</tr>
</thead>
<tbody><tr>
<td>후보키</td>
<td>종속 관계의 기준 (결정자 판단 근거)</td>
</tr>
<tr>
<td>기본키</td>
<td>테이블의 중심 식별자, 완전 종속의 기준</td>
</tr>
<tr>
<td>외래키</td>
<td>테이블 간 종속 관계 표현</td>
</tr>
<tr>
<td>정규화</td>
<td>키를 기준으로 비정상 종속을 제거하는 과정</td>
</tr>
</tbody></table>
<p>정규화는 속성들이 키에 대해 어떻게 종속되어 있는가를 기준으로 단계가 나뉨</p>
<p>함수 종속성을 판단하려면 먼저 기준 키가 명확해야 함</p>
<h1 id="3-정규형-normal-forms-단계별-이해">3. 정규형 (Normal Forms) 단계별 이해</h1>
<p>테이블에 존재하는 <strong>불필요한 종속성</strong>을 제거해 중복과 이상(anomalies)을 줄이는 단계별 규칙이자 레벨</p>
<p>각 정규형은 ‘어떤 종류의 종속을 없애야 하는가?’로 정의되고, 해당 정의를 만족시키지 못하면 테이블을 분해하여 해결함.</p>
<ul>
<li>1NF (원자값) ⊂ 2NF (부분 중복 제거) ⊂ 3NF (간접 중복 제거) ⊂ BCNF (모든 결정자=후보키)</li>
<li><strong>정규화의 장단점</strong><ul>
<li><strong>장점:</strong> &#39;이상 현상&#39;이 제거되고, &#39;데이터 중복&#39;이 최소화되어 무결성 보장</li>
<li><strong>단점:</strong> 테이블 분리로 인한 JOIN 연산 증가로 성능 저하 가능성</li>
</ul>
</li>
<li><strong>비정규화(Denormalization)
성</strong>능 향상을 위해 의도적으로 중복을 허용하거나 정규형을 낮추는 전략</li>
</ul>
<h2 id="1-제1-정규형-1nf---원자성-atomicity">1) 제1 정규형 (1NF) - 원자성 (Atomicity)</h2>
<p>테이블의 모든 속성 값이 원자값(Atomic Value)을 가져야 함.</p>
<p>반복 그룹, 배열, 리스트와 같은 비원자 값을 허용하지 않음.
하나의 컬럼에 여러 값이 포함될 수 없음 - &quot;축구, 농구&quot; (X)</p>
<pre><code class="language-python">학생(학번, 이름, 전화번호)
행: (1001, &#39;홍&#39;, &#39;010-1111-2222,010-3333-4444&#39;)  → 1NF 위반</code></pre>
<h2 id="2-제2-정규형-2nf---부분-중복-제거">2. 제2 정규형 (2NF) - 부분 중복 제거</h2>
<p>1NF를 만족하고, <strong>부분 함수 종속이 제거</strong>되어야 함.</p>
<p>복합키의 일부에만 종속되는 속성이 있어서는 안 됨</p>
<pre><code class="language-python">수강(학번, 과목코드, 학생이름, 성적)
기본키 = (학번, 과목코드)
학생이름 → 학번에만 종속  → 부분 종속(2NF 위반)</code></pre>
<h2 id="3-제-3정규형-3nf---이행적-종속-제거">3. 제 3정규형 (3NF) - 이행적 종속 제거</h2>
<p>2NF를 만족하고, <strong>이행적 종속이 제거</strong>되어야 함</p>
<pre><code class="language-python">학생(학번, 학과코드, 학과명)
학번 → 학과코드, 학과코드 → 학과명  → 학번 → 학과명 (이행적 종속)</code></pre>
<ul>
<li>학생(학번 → 학과코드 → 학과명) → 학과명은 학과코드에만 종속 → 학과 테이블 분리</li>
</ul>
<hr>
<h2 id="4-bcnf-boyce-codd-normal-form-강력한-3nf">4. BCNF (Boyce-Codd Normal Form): 강력한 3NF</h2>
<p>제3 정규형(3NF)을 만족하고, 모든 결정자가 후보키여야 함.</p>
<p>모든 결정자( 어떤 속성 집합 X가 Y를 결정할 때 X )는 <strong>후보키(Candidate Key)</strong> 여야 함.</p>
<pre><code class="language-python">강의(교수, 과목, 강의실)
FD: 과목 → 교수   (한 과목은 특정 교수 전담)
FD: 교수, 과목 → 강의실

과목은 후보키가 아님(과목만으로 행을 식별 못함), 그런데 과목 → 교수 이므로 BCNF 위반</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] SQL Injection]]></title>
            <link>https://velog.io/@do_e/DB-SQL-Injection</link>
            <guid>https://velog.io/@do_e/DB-SQL-Injection</guid>
            <pubDate>Wed, 05 Nov 2025 15:25:09 GMT</pubDate>
            <description><![CDATA[<hr>
<h1 id="1-기본-개념-이해-what--why">1. 기본 개념 이해 (What &amp; Why?)</h1>
<h2 id="sql-injection이란"><strong>SQL Injection이란?</strong></h2>
<p>사용자의 입력값이 <strong>데이터</strong>로 처리되지 않고, 개발자가 의도하지 않은 DB를 조작하는 SQL 쿼리(명령어)의 일부로 실행되는 웹 보안 취약점</p>
<ul>
<li><strong>정상적인 상황</strong>
웹사이트가 사용자에게 ID를 물어보면, 사용자는 <code>my_user_id</code> 같은 데이터(값)를 입력
서버는 이 값을 받아 &quot;이 ID를 가진 사용자를 찾아줘&quot;라는 쿼리를 실행</li>
<li><strong>공격 상황</strong>
공격자는 <code>my_user_id</code> 대신 <code>admin&#39; OR &#39;1&#39;=&#39;1</code> 같은 명령어(쿼리문)를 입력
서버가 이 입력을 제대로 거르지 못하면, 이 값이 그대로 쿼리문에 포함되어 개발자가 의도하지 않은 명령이 실행됨.</li>
</ul>
<h2 id="왜-발생하는가-핵심-원인"><strong>왜 발생하는가? (핵심 원인)</strong></h2>
<h3 id="동적-쿼리dynamic-query">동적 쿼리(Dynamic Query)</h3>
<p>사용자 입력을 검증 없이 문자열로 이어붙여(<code>+</code> 또는 <code>concat</code>) 쿼리문을 만들 때 주로 발생함</p>
<p>공격자는 쿼리문의 구문(Syntax)를 깨뜨리는 특수문자를 삽입하여 쿼리를 조작함</p>
<br>

<h1 id="2-주요-공격-유형-types">2. 주요 공격 유형 (Types)</h1>
<p>공격자가 “어떻게” 정보를 빼내는지에 따라 유형이 나뉨.</p>
<h2 id="1-in-band"><strong>1) In-band</strong></h2>
<p>공격자가 보낸 쿼리의 결과를 현재 웹 페이지 화면을 통해 직접 볼 수 있을 때 사용함</p>
<h3 id="union-based"><strong>Union-based</strong></h3>
<p> <code>UNION</code> 연산자는 두 개 이상의 SELECT문 결과를 하나로 합치는 명령어</p>
<p>공격자는 이를 악용해 원래 쿼리 결과에 추가로 다른 테이블의 데이터를 붙여서 탈취</p>
<ul>
<li>e.g.
게시판 검색 쿼리를 조작하여 users 테이블의 id, pw를 탈취
…WHERE title=’검색어’ UNION SELECT id, pw FROM users;</li>
</ul>
<h3 id="error-based"><strong>Error-based</strong></h3>
<p>고의로 SQL 쿼리 오류를 발생시켜, 반환되는 오류 메시지를 통해 데이터베이스의 구조(테이블명, 컬럼명 등)를 알아내는 방식</p>
<p>만약 서버가 DB 오류 메세지를 화면에 그대로 노출하도록 설정되어 있다면, 공격자는  이 오류 메세지를 통해 DB의 버전, 테이블 이름, 칼럼명 등 민감한 DB 구조 정보를 알아낼 수 있음</p>
<h2 id="2-inferential-blind-sql-injection"><strong>2) Inferential (Blind SQL Injection)</strong></h2>
<p>추론 방식, 서버가 공격 결과나 메세지를 전혀 보여주지 않고, 단지 정상/ 오류 페이지만 보여줄 때 사용하는 고난도 기법</p>
<p>공격자는 참/ 거짓 반응 또는 응답 시간의 차이를 이용해 데이터를 한 글자씩 빼냄</p>
<h3 id="boolean-based"><strong>Boolean-based</strong></h3>
<p>참 / 거짓일 때의 페이지 반응이 다른 점을 이용함</p>
<ul>
<li>공격: ‘admin 계정의 비밀번호 첫 번째 글자가 a인가?’의 쿼리를 보냄</li>
<li>결과: 게시물이 존재하는 경우 페이지가 뜨면 → T</li>
<li>공격: admin 계정의 비밀번호 첫 번째 글자가 b인가?</li>
<li>결과: 게시물이 없습니다의 페이지가 뜨면 → F</li>
<li>이런 식으로 한 글자, 한 글자 모든 비밀번호를 알아냄 (느리지만 자동화 스크립트로 가능)</li>
</ul>
<h3 id="time-based"><strong>Time-based</strong></h3>
<p>DB에 내장된 <code>SLEEP()</code>, <code>WAITFOR</code> 등의 함수를 사용해, 응답 시간을 기준으로 데이터를 유추</p>
<ul>
<li>공격: 첫 글자가 &#39;a&#39;이면 5초 대기</li>
<li>결과: 서버 응답이 5초 이상 걸리면 → T / 서버 응답이 즉시 오면 → F</li>
<li>응답 시간을 측정하여 데이터를 빼냄</li>
</ul>
<br>

<h1 id="3-방어-기법-prevention">3. 방어 기법 (Prevention)</h1>
<h2 id="1-prepared-statement-매개변수-기반-쿼리"><strong>1) Prepared Statement (매개변수 기반 쿼리)</strong></h2>
<h3 id="작동원리">작동원리</h3>
<p>쿼리의 틀(Template) &amp; 사용자 값(Data)을 분리해서 DB에 전송함.</p>
<p> DB는 쿼리 틀을 먼저 컴파일하고, 값은 나중에 대입하므로 입력값이 절대로 명령어로 해석되지 않음.</p>
<ol>
<li>컴파일 (틀 전송)
먼저 <code>SELECT * FROM users WHERE id = ? AND pw = ?;</code> 와 같이, 값이 들어갈 자리를 <code>?</code> (placeholder)로 표시한 &#39;쿼리 틀&#39;을 DB로 보냄
DB는 이 틀을 미리 컴파일 / 분석함</li>
<li>실행 (값 전송)
DB는 이미 틀을 분석해두었기 때문에, 나중에 전송된 admin’—값을 명령어로 절대 해석하지 않음
? 자리에 들어가야 할 단순한 문자열 데이터로만 취급함</li>
</ol>
<ul>
<li>Java의 <code>PreparedStatement</code> (JDBC), Python의 <code>cursor.execute(query, params)</code>, PHP의 <code>PDO</code> 등</li>
</ul>
<h2 id="2-orm-object-relational-mapping"><strong>2) ORM (Object-Relational Mapping)</strong></h2>
<ul>
<li>ORM은 내부적으로 Prepared Statement를 사용하여 SQL Injection을 원천적으로 방어해 줍니다.
개발자가 <code>userRepository.findById(&quot;admin&quot;);</code> 처럼 객체지향 코드를 작성하면, 내부적으로 <strong>안전한 Prepared Statement</strong>를 생성하여 DB와 통신함.
개발자가 실수로 동적 쿼리를 만들 여지를 원천적으로 줄임.</li>
<li>Java(Spring)의 <strong>JPA/Hibernate</strong>, Python의 <strong>SQLAlchemy</strong> 등</li>
</ul>
<h3 id="입력값-검증-input-validation">입력값 검증 (Input Validation)</h3>
<p>서버단에서 사용자의 입력이 예상된 형식인지 검사</p>
<h3 id="최소-권한-원칙-least-privilege">최소 권한 원칙 (Least Privilege)</h3>
<p>DB에 접속하는 웹 애플리케이션 계정에 필요한 최소한의 권한만 부여함</p>
<p>웹사이트는 데이터를 읽고(SELECT), 쓰고(INSERT), 수정(UPDATE)할 뿐 테이블을 삭제(DROP)할 필요는 없음</p>
<p>DROP 권한을 아예 주지 않으면, 공격자가 DROP TABLE 쿼리를 주입해도 권한 없음 오류로 실패하게 됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] JOIN]]></title>
            <link>https://velog.io/@do_e/DB-JOIN</link>
            <guid>https://velog.io/@do_e/DB-JOIN</guid>
            <pubDate>Fri, 31 Oct 2025 14:06:09 GMT</pubDate>
            <description><![CDATA[<h1 id="1-join">1. Join?</h1>
<p>2개 이상의 테이블을 연결해 데이터를 조회하는 방법</p>
<p>여러 테이블에 분산된 데이터를 논리적으로 결합해 하나의 결과 집합으로 만드는 연산</p>
<p>두 테이블의 조인을 위해서 PK와 FK 관계로 맺어져야 함.</p>
<pre><code class="language-sql">SELECT 컬럼명
FROM 테이블A
JOIN 테이블B ON 테이블A.공통컬럼 = 테이블B.공통컬럼;
</code></pre>
<ul>
<li>공통컬럼은 두 테이블의 관계를 연결하는 키 역할을 함</li>
</ul>
<h2 id="2-join-유형">2. JOIN 유형</h2>
<table>
<thead>
<tr>
<th>JOIN 유형</th>
<th>기준 테이블 유지</th>
<th>일치하지 않는 데이터 처리</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td>INNER JOIN</td>
<td>없음</td>
<td>제외 (공통키의 행만 결합)</td>
<td>교집합</td>
</tr>
<tr>
<td>LEFT JOIN</td>
<td>왼쪽</td>
<td>NULL</td>
<td>왼쪽 우선</td>
</tr>
<tr>
<td>RIGHT JOIN</td>
<td>오른쪽</td>
<td>NULL</td>
<td>오른쪽 우선</td>
</tr>
<tr>
<td>FULL JOIN</td>
<td>양쪽</td>
<td>NULL</td>
<td>모든 데이터</td>
</tr>
<tr>
<td>CROSS JOIN</td>
<td>없음</td>
<td>곱집합</td>
<td>조합 계산</td>
</tr>
<tr>
<td>SELF JOIN</td>
<td>동일 테이블</td>
<td>-</td>
<td>계층 구조 탐색</td>
</tr>
</tbody></table>
<p>내부조인은 두 테이블 모두 데이터가 있어야 결과가 나오지만, 외부 조인은 한쪽에만 데이터가 있어도 결과가 나옴</p>
<h3 id="1-inner-join">1) INNER JOIN</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/3725989f-edca-4628-b32e-35e73b24bcb6/image.png" alt=""></p>
<p>두 테이블 모두에서 조건이 일치하는 행만 가져옴</p>
<pre><code class="language-sql">SELECT 열목록
FROM 첫번째테이블
 INNER JOIN 두번째테이블
 ON 조인조건
 WHERE 검색조건</code></pre>
<h3 id="2-left-join-outer">2) LEFT JOIN (OUTER)</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/77e011c3-a148-46b9-bf01-c275f5284008/image.png" alt=""></p>
<p><strong>왼쪽</strong> 테이블의 모든 데이터 + <strong>오른쪽</strong> 테이블의 일치하는 데이터 (없으면 <code>NULL</code>)</p>
<pre><code class="language-sql">SELECT 열목록
FROM 첫번째테이블
    LEFT/RIGHT/FULL OUTER JOIN 두번쨰테이블
    ON 조인조건
    WHERE 검색조건</code></pre>
<h3 id="3-right-join">3) RIGHT JOIN</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/d7a25ee2-4a8f-487c-94a5-f4469241cae9/image.png" alt=""></p>
<p><strong>오른쪽</strong> 테이블의 모든 데이터 + <strong>왼쪽</strong> 테이블의 일치하는 데이터 (없으면 <code>NULL</code>)</p>
<h3 id="4-full-join">4) FULL JOIN</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/98f6ffa7-c399-418d-9b0c-1f15ebd40d57/image.png" alt=""></p>
<p><strong>양쪽</strong> 테이블의 모든 데이터를 결합 (일치하지 않는 부분은 <code>NULL</code>)</p>
<h3 id="5-cross-join">5) CROSS JOIN</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/0448de26-4d5a-44e6-bd88-68c5e57c1817/image.png" alt=""></p>
<p>모든 경우의 수(카티전 곱)를 결합</p>
<p>한 쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인하여 모든 가능한 조합은 반환함.</p>
<p>ON 조건절이 필요없음.</p>
<h3 id="6-self-join">6) SELF JOIN</h3>
<p><img src="https://velog.velcdn.com/images/do_e/post/a85dcfd9-aa1e-4130-b4a7-e78d26b19233/image.png" alt=""></p>
<p>같은 테이블을 두 번 참조하여 스스로 조인하는 기법</p>
<p>테이블에 별칭(Alias)을 붙여서 구분함.</p>
<p>주로 계층형 데이터를 다룰 때 사용함.</p>
<pre><code class="language-sql">SELECT e.name AS employee, m.name as manager
FROM employess e
JOIN emplyees m
ON e.manager_id = m.id;</code></pre>
<h2 id="3-join-조건">3. JOIN 조건</h2>
<h3 id="1-on">1) ON</h3>
<ul>
<li>모든 JOIN 유형에서 사용 가능</li>
<li>조인을 수행할 때 사용됨 (테이블을 결합할 때의 조건)</li>
<li>LEFT/RIGHT/FULL JOIN등에서 일지하지 않는 데이터는 NULL로 채워짐.</li>
<li>JOIN 이후에도 WHERE로 추가 필터링 가능함.</li>
</ul>
<pre><code class="language-sql">SELECT * FROM users u
JOIN orders o
ON u.id = o.user_id;</code></pre>
<h3 id="2-using">2) USING</h3>
<ul>
<li>두 테이블에 컬럼 이름이 동일할 때만 사용 가능함.</li>
<li>결합 시 동일 이름의 컬럼을 하나의 컬럼으로 합쳐서 반환함.</li>
</ul>
<pre><code class="language-sql">SELECT *
FROM employees
JOIN departments
USING (dept_id);</code></pre>
<h3 id="3-where">3) WHERE</h3>
<ul>
<li>JOIN이 끝난 후에 결과 행을 필터링함.</li>
<li>보통 INNER JOIN에서 사용됨.</li>
<li>ON 없이 WHERE만 사용할 수 있음.</li>
</ul>
<h2 id="3-null-처리의-이해">3. NULL 처리의 이해</h2>
<table>
<thead>
<tr>
<th>JOIN 유형</th>
<th>NULL 처리 방식</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>INNER JOIN</td>
<td>조건 불일치(NULL)는 제외</td>
<td>NULL 행 제외</td>
</tr>
<tr>
<td>LEFT JOIN</td>
<td>오른쪽 매칭 없으면 NULL 채움</td>
<td>NULL 포함</td>
</tr>
<tr>
<td>RIGHT JOIN</td>
<td>왼쪽 매칭 없으면 NULL 채움</td>
<td>NULL 포함</td>
</tr>
<tr>
<td>FULL JOIN</td>
<td>매칭 없는 쪽은 NULL 채움</td>
<td>양쪽 NULL 가능</td>
</tr>
</tbody></table>
<p>JOIN 조건에서 NULL은 비교 불가능함.</p>
<p>SQL에서 NULL은 ‘값이 없음’이기 때문에 비교연산자 <code>!=</code> , <code>=</code> 로 비교 불가함</p>
<pre><code class="language-sql">-- WHERE을 사용하는 경우
WHERE dept_id IS NULL; </code></pre>
<h3 id="inner-join에서의-null">INNER JOIN에서의 NULL</h3>
<p>INNER JOIN은 두 테이블의 조건이 일치해야 하므로, NULL이 있는 경우 비교가 불가능해 조인되지 않음.</p>
<pre><code class="language-sql">SELECT e.name, d.name 
FROM emplyoees e
INNER JOIN departments d
ON e.dept_id = d.id;
-- dept_id가 null인 직원에 대해서는 조인되지 않음</code></pre>
<h3 id="left-join에서의-null">LEFT JOIN에서의 NULL</h3>
<p>오른쪽 테이블에 매칭이 없을 경우, NULL로 채움</p>
<pre><code class="language-sql">SELECT e.name, d.name
FROM empployees e
LEFT JOIN departments d
ON e.dept_id = d.id;</code></pre>
<p>[employess]</p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>dept_id</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Alice</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>Bob</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>Carol</td>
<td>NULL</td>
</tr>
<tr>
<td>4</td>
<td>David</td>
<td>5</td>
</tr>
</tbody></table>
<p>[deparments]</p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Sales</td>
</tr>
<tr>
<td>2</td>
<td>HR</td>
</tr>
<tr>
<td>3</td>
<td>Marketing</td>
</tr>
<tr>
<td>- 왼쪽 테이블(<code>employees</code>)은 모두 유지됨.</td>
<td></td>
</tr>
<tr>
<td>- <code>ON e.dept_id = [d.id](http://d.id/)</code> 조건으로</td>
<td></td>
</tr>
<tr>
<td>→ dept_id가 일치하는 경우에만 departments의 정보(<a href="http://d.name/"><code>d.name</code></a>)가 붙음</td>
<td></td>
</tr>
<tr>
<td>- 일치하지 않거나, dept_id가 NULL이면 NULL로 표시됨</td>
<td></td>
</tr>
</tbody></table>
<p>[result]</p>
<table>
<thead>
<tr>
<th>e.name</th>
<th>d.name</th>
</tr>
</thead>
<tbody><tr>
<td>Alice</td>
<td>Sales</td>
</tr>
<tr>
<td>Bob</td>
<td>HR</td>
</tr>
<tr>
<td>Carol</td>
<td>NULL</td>
</tr>
<tr>
<td>David</td>
<td>NULL</td>
</tr>
</tbody></table>
<h3 id="right-join에서의-null">RIGHT JOIN에서의 NULL</h3>
<pre><code class="language-sql">SELECT *
FROM employees e
RIGHT JOIN departments d
ON e.dept_id = d.id;</code></pre>
<p>[employess]</p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>dept_id</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Alice</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>Bob</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>Carol</td>
<td>NULL</td>
</tr>
<tr>
<td>4</td>
<td>David</td>
<td>5</td>
</tr>
</tbody></table>
<p>[deparments]</p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Sales</td>
</tr>
<tr>
<td>2</td>
<td>HR</td>
</tr>
<tr>
<td>3</td>
<td>Marketing</td>
</tr>
<tr>
<td>4</td>
<td>Engineering</td>
</tr>
<tr>
<td>- 오른쪽 테이블 departments가 유지되고, e테이블에서 일치하는 값이 추가됨</td>
<td></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>e.id</th>
<th>e.name</th>
<th>e.dept_id</th>
<th>d.id</th>
<th>d.name</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Alice</td>
<td>1</td>
<td>1</td>
<td>Sales</td>
</tr>
<tr>
<td>2</td>
<td>Bob</td>
<td>2</td>
<td>2</td>
<td>HR</td>
</tr>
<tr>
<td>NULL</td>
<td>NULL</td>
<td>NULL</td>
<td>3</td>
<td>Marketing</td>
</tr>
<tr>
<td>NULL</td>
<td>NULL</td>
<td>NULL</td>
<td>4</td>
<td>Engineering</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] SQL & NoSQL]]></title>
            <link>https://velog.io/@do_e/DB-SQL-NoSQL</link>
            <guid>https://velog.io/@do_e/DB-SQL-NoSQL</guid>
            <pubDate>Fri, 31 Oct 2025 14:01:25 GMT</pubDate>
            <description><![CDATA[<h1 id="sql--nosql">SQL &amp; NoSQL</h1>
<h2 id="1-sql--nosql">1. SQL &amp; NoSQL</h2>
<h3 id="sql-structured-query-laguage">SQL (Structured Query Laguage)</h3>
<ul>
<li>관계형 DB</li>
<li>데이터를 <strong>테이블 기반 구조</strong>로 저장하는 관계형 데이터베이스</li>
<li>e.g. MySQL, PostgreSQL, Oracle, SQL Server</li>
</ul>
<h3 id="nosql-not-only-sql">NoSQL (Not Only SQL)</h3>
<ul>
<li>비관계형 DB</li>
<li><strong>비정형 또는 반정형 데이터</strong>를 저장하는 유연한 데이터베이스</li>
<li>MongoDB, Redis, Cassandra, DynamoDB</li>
</ul>
<h2 id="2-구조적-차이점">2. 구조적 차이점</h2>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>SQL</th>
<th>NoSQL</th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터 모델</strong></td>
<td>정형(Structured)<br>테이블 기반, 행(row)과 열(column) 구성</td>
<td>비정형/반정형(Unstructured/Semi-structured)<br>JSON, 문서, 그래프, Key-Value 등 다양한 구조</td>
</tr>
<tr>
<td><strong>스키마(Schema)</strong></td>
<td>고정(Fixed Schema)<br>사전에 정의 필요 ⇒ 스키마 변경 어려움</td>
<td>유연(Flexible Schema)<br>필드 추가/삭제 자유 ⇒ 스키마 변경이 유연함</td>
</tr>
<tr>
<td><strong>데이터 관계</strong></td>
<td>외래키(FK)로 관계 정의 (정규화 중심)</td>
<td>데이터 중복 허용, 관계는 애플리케이션 단에서 처리</td>
</tr>
<tr>
<td><strong>확장성(Scalability)</strong></td>
<td>수직 확장(Vertical Scaling)<br>서버 성능 업그레이드</td>
<td>수평 확장(Horizontal Scaling)<br>노드 추가로 확장</td>
</tr>
<tr>
<td><strong>트랜잭션</strong></td>
<td>ACID (Atomicity, Consistency, Isolation, Durability)</td>
<td>BASE (Basically Available, Soft state, Eventually consistent)</td>
</tr>
<tr>
<td><strong>쿼리 언어</strong></td>
<td>표준 SQL 문법</td>
<td>데이터베이스별 고유 쿼리 언어 (MongoDB의 find, Redis의 get/set 등)</td>
</tr>
<tr>
<td><strong>성능 특성</strong></td>
<td>일관성(Consistency) 중시</td>
<td>가용성(Availability) 및 확장성 중심</td>
</tr>
</tbody></table>
<h3 id="sql">SQL</h3>
<ul>
<li>모든 데이터는 정형화된 스키마를 따라야 함.</li>
<li>데이터는 행과 열로 구성된 테이블에 저장됨.</li>
<li>테이블 간 관계를 외래키(FK)로 연결함</li>
<li>데이터 중복을 최소화하기 위해 정규화를 수행함.</li>
</ul>
<h3 id="nosql">NoSQL</h3>
<ul>
<li>비정형 데이터도 저장 가능함. (JSON, Key-Value, Graph등)</li>
<li>테이블 대신 컬렉션, 문서, Key-Value Store 형태로 저장함.</li>
<li>관계 대신 중첩 구조로 데이터 내포 가능함.</li>
<li>데이터 중복을 허용하여 읽기 성능을 극대화함.</li>
</ul>
<h2 id="3-트랜잭션-관리">3. 트랜잭션 관리</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>SQL</th>
<th>NoSQL</th>
</tr>
</thead>
<tbody><tr>
<td><strong>트랜잭션 모델</strong></td>
<td><strong>ACID</strong> (정합성 우선)</td>
<td><strong>BASE</strong> (가용성, 확장성 우선)</td>
</tr>
<tr>
<td><strong>Consistency (일관성)</strong></td>
<td>즉시 보장</td>
<td>최종 일관성(Eventual Consistency)</td>
</tr>
<tr>
<td><strong>사용 예시</strong></td>
<td>금융, 회계, ERP 시스템</td>
<td>로그, 세션, 캐시, SNS 데이터 등</td>
</tr>
</tbody></table>
<p>트랜잭션이란 DB에서 한 번에 수행되어야 하는 논리적 작업 단위임.</p>
<p>여러 쿼리를 하나의 작업처럼 묶어서 성공 / 실패를 함께 처리함.</p>
<h3 id="sql의-트랜잭션-관리-방식-acid-원칙">SQL의 트랜잭션 관리 방식: ACID 원칙</h3>
<table>
<thead>
<tr>
<th>원칙</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Atomicity (원자성)</strong></td>
<td>모든 연산이 하나의 단위로 실행되어야 함 — 일부만 성공 불가</td>
<td>결제 실패 시 주문도 취소됨</td>
</tr>
<tr>
<td><strong>Consistency (일관성)</strong></td>
<td>트랜잭션 전후 데이터 무결성이 유지되어야 함</td>
<td>재고 수량이 음수가 되면 안 됨</td>
</tr>
<tr>
<td><strong>Isolation (격리성)</strong></td>
<td>동시에 실행되는 트랜잭션 간의 간섭 방지</td>
<td>두 사용자가 동시에 같은 주문 수정 불가</td>
</tr>
<tr>
<td><strong>Durability (지속성)</strong></td>
<td>커밋된 데이터는 영구 저장</td>
<td>시스템 다운 후에도 데이터 유지</td>
</tr>
<tr>
<td>- SQL 데이터베이스에는 정부 / 업계 표준을 준수해야 해는 뱅킹 / 금융 정보가 포함되어 있음</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h3 id="nosql의-트랜잭션-관리-방식-base-원칙">NoSQL의 트랜잭션 관리 방식: BASE 원칙</h3>
<table>
<thead>
<tr>
<th>원칙</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Basically Available</strong></td>
<td>시스템은 언제나 응답을 반환해야 함 (완벽하지 않아도 됨)</td>
</tr>
<tr>
<td><strong>Soft state</strong></td>
<td>데이터는 일시적으로 불안정할 수 있음</td>
</tr>
<tr>
<td><strong>Eventually consistent</strong></td>
<td>시간이 지나면 데이터는 최종적으로 일관된 상태에 수렴</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] ERD]]></title>
            <link>https://velog.io/@do_e/DB-ERD</link>
            <guid>https://velog.io/@do_e/DB-ERD</guid>
            <pubDate>Fri, 31 Oct 2025 13:58:56 GMT</pubDate>
            <description><![CDATA[<h1 id="erd-entity-relationship-diagram">ERD (Entity-Relationship Diagram)</h1>
<ul>
<li>개체-관계 다이어그램
DB 설계를 시작할 때, 시스템의 데이터 구조 &amp; 데이터 간의 관계를 시각화하는 핵심 도구</li>
<li>무엇이 있고(엔티티), 서로 어떻게 연결되어 있는지(관계)를 도식화한 그림</li>
</ul>
<h2 id="1-erd의-주요-구성-요소">1. ERD의 주요 구성 요소</h2>
<table>
<thead>
<tr>
<th>요소</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Entity (개체)</strong></td>
<td>데이터베이스에서 관리해야 할 대상</td>
<td><code>User</code>, <code>Product</code>, <code>Order</code></td>
</tr>
<tr>
<td><strong>Attribute (속성)</strong></td>
<td>엔티티의 특성</td>
<td><code>User.name</code>, <code>Product.price</code></td>
</tr>
<tr>
<td><strong>Relationship (관계)</strong></td>
<td>두 개체 간의 연결</td>
<td><code>User</code> — <em>places</em> → <code>Order</code></td>
</tr>
<tr>
<td><strong>Primary Key (기본키)</strong></td>
<td>각 엔티티에서 유일한 식별자</td>
<td><code>user_id</code>, <code>order_id</code></td>
</tr>
<tr>
<td><strong>Foreign Key (외래키)</strong></td>
<td>다른 엔티티의 기본키를 참조</td>
<td><code>order.user_id</code> → <code>user.user_id</code></td>
</tr>
</tbody></table>
<br>

<h2 id="2-erd-작성-단계">2. ERD 작성 단계</h2>
<h3 id="1-요구사항-분석">(1) 요구사항 분석</h3>
<ul>
<li>비즈니스 로직이나 서비스 기능을 분석하여 어떤 데이터가 필요한지 파악</li>
<li>e.g. 사용자가 상품을 주문함 → User, Product, Order 엔티티 도출</li>
</ul>
<h3 id="2-엔티티-식별">(2) 엔티티 식별</h3>
<ul>
<li>관리할 대상을 추출 (명사형)</li>
<li>User, Product, Order, Payment</li>
</ul>
<h3 id="3-속성-정의">(3) 속성 정의</h3>
<ul>
<li>각 엔티티의 세부 속성 (칼럼)을 정의</li>
<li>User: user_id, email
Product: product_id, name, price</li>
</ul>
<h3 id="4-관계-설정">(4) 관계 설정</h3>
<ul>
<li>엔티티 간의 관계를 파악</li>
<li>User ↔ Order (1: N)</li>
<li>Order ↔ Product (N: M)</li>
</ul>
<h3 id="5-관계의-카디널리티-명시-cardinality">(5) 관계의 카디널리티 명시 (Cardinality)</h3>
<ul>
<li>관계의 유형을 명확히 표현함</li>
<li>1:1, 1:N, N:M</li>
</ul>
<h3 id="6-정규화">(6) 정규화</h3>
<ul>
<li>데이터 중복을 줄이고 구조를 효율화하기 위해 정규화 과정을 거침</li>
<li>1NF: 각 컬럼은 원자값을 가짐
2NF: 부분 종속 제거
3NF: 이행 종속 제거</li>
</ul>
<h3 id="7--다이어그램-시각화">(7)  다이어그램 시각화</h3>
<ul>
<li>draw.io, ERD cloud,…</li>
</ul>
<h2 id="3-erd-작성-시-주의할-점">3. ERD 작성 시 주의할 점</h2>
<ul>
<li>엔티티 이름은 단수형 명사로 작성 (User)</li>
<li>모든 엔티티에 PK 지정</li>
<li>외래키 관계 명확히 표시</li>
<li>중복 데이터 제거 후 정규화</li>
<li>실제 비즈니스 로직과 일치하는지 검증</li>
</ul>
<br>

<h2 id="4-erd-작성-방법">4. ERD 작성 방법</h2>
<h3 id="주식별자-pk">주식별자 (PK)</h3>
<p>각 엔티티의 인스턴스를 유일하게 구분할 수 있는 속성</p>
<ul>
<li>중복 불가(Unique), NOT NULL</li>
<li>주로 id, —-_id 형태로 명명</li>
</ul>
<h3 id="외래-식별자-fk">외래 식별자 (FK)</h3>
<p>다른 엔티티의 주식별자를 참조하는 속성, 두 엔티티 간의 관계를 나타냄</p>
<ul>
<li>부모 테이블의 기본키를 참조</li>
<li>데이터 간 참조 무결성 유지</li>
</ul>
<h3 id="not-null">NOT NULL</h3>
<ul>
<li>특정 속성이 반드시 값을 가져야 하는 제약 조건 (데이터 누락 방지)</li>
<li>ERD에서 속성명 앞에 NN / NOT NULL로 표시</li>
</ul>
<h3 id="관계-표기법">관계 표기법</h3>
<ul>
<li>관계의 종류 (Cardinality)</li>
</ul>
<pre><code>| 관계 | 설명 | 예시 |
| --- | --- | --- |
| **1:1 (One-to-One)** | 하나의 엔티티 인스턴스가 다른 하나와만 연결 | 한 사람은 하나의 여권만 가짐 |
| **1:N (One-to-Many)** | 하나의 인스턴스가 여러 개와 연결 | 한 사용자 → 여러 주문 |
| **N:M (Many-to-Many)** | 여러 인스턴스가 서로 여러 개와 연결 | 학생 ↔ 강좌 (수강) |</code></pre><ul>
<li>관계의 필수 / 선택 여부 (Participation)</li>
</ul>
<pre><code>| 기호 | 의미 | 예시 |
| --- | --- | --- |
| ─│ | 반드시 존재 (Mandatory, 필수 참여) | 사용자는 반드시 주문을 가져야 함 |
| ─O | 없어도 됨 (Optional, 선택 참여) | 사용자가 주문이 없을 수도 있음 |</code></pre><ul>
<li><p>Crow’s Foot 표기법
엔티티 간의 관계를 선과 기호로 표현하는 방법
관계의 카디널리티를 직관적으로 표현</p>
<table>
<thead>
<tr>
<th>기호</th>
<th>의미</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>─│</td>
<td>1 (One and only one)</td>
<td>반드시 하나 존재</td>
</tr>
<tr>
<td>─O</td>
<td>0 또는 1 (Zero or one)</td>
<td>선택적으로 하나 존재</td>
</tr>
<tr>
<td>─&lt;</td>
<td>여러 개 (Many)</td>
<td>하나 이상 존재</td>
</tr>
<tr>
<td>─O&lt;</td>
<td>0개 이상 (Zero or many)</td>
<td>선택적 다수</td>
</tr>
</tbody></table>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 제약 조건]]></title>
            <link>https://velog.io/@do_e/DB-%EC%A0%9C%EC%95%BD-%EC%A1%B0%EA%B1%B4</link>
            <guid>https://velog.io/@do_e/DB-%EC%A0%9C%EC%95%BD-%EC%A1%B0%EA%B1%B4</guid>
            <pubDate>Fri, 31 Oct 2025 13:52:21 GMT</pubDate>
            <description><![CDATA[<h2 id="제약-조건">제약 조건?</h2>
<table>
<thead>
<tr>
<th><strong>제약 조건</strong></th>
<th><strong>핵심 규칙</strong></th>
<th><strong>목적 (예시)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>NOT NULL</strong></td>
<td><code>NULL</code> 값 비허용</td>
<td>(필수 입력) <code>이름</code>, <code>아이디</code></td>
</tr>
<tr>
<td><strong>UNIQUE</strong></td>
<td>중복 값 비허용</td>
<td>(유일성 보장) <code>이메일</code>, <code>주민번호</code></td>
</tr>
<tr>
<td><strong>PRIMARY KEY</strong></td>
<td><code>NOT NULL</code> + <code>UNIQUE</code></td>
<td>(행의 고유 식별) <code>학번</code>, <code>회원ID</code></td>
</tr>
<tr>
<td><strong>FOREIGN KEY</strong></td>
<td>다른 테이블의 PK만 참조 가능</td>
<td>(테이블 관계 연결) <code>수강신청</code>의 <code>학번</code></td>
</tr>
<tr>
<td><strong>CHECK</strong></td>
<td>지정된 조건 만족</td>
<td>(데이터 검증) <code>나이 &gt; 0</code>, <code>성별 IN (&#39;남&#39;, &#39;여&#39;)</code></td>
</tr>
<tr>
<td><strong>DEFAULT</strong></td>
<td>값 생략 시 자동 입력</td>
<td>(기본값 설정) <code>포인트 = 0</code>, <code>가입일 = 오늘 날짜</code></td>
</tr>
</tbody></table>
<p>DB 설계에서 데이터의 정합성(Intergrity)을 유지하고, 무결성(Consistency)을 보장하기 위해 사용되는 규칙</p>
<ul>
<li>제약 조건은 테이블에 삽입, 업데이트, 삭제되는 데이터가 특정 조건을 만족하도록 강제하는 규칙</li>
</ul>
<h3 id="제약-조건을-사용해야-하는-이유">제약 조건을 사용해야 하는 이유</h3>
<ul>
<li>데이터 무결성 유지: 데이터의 유효성 &amp; 일관성</li>
<li>참조 무결성 유지: 외래 키</li>
<li>비즈니스 로직 강제: CHECK</li>
<li>중복 데이터 방지: UNIQUE</li>
</ul>
<h2 id="1-not-null">1. NOT NULL</h2>
<p>컬럼에 NULL 값이 들어가는 것을 방지</p>
<ul>
<li>해당 칼럼에는 값이 반드시 존재해야 함</li>
<li>중요한 데이터가 누락되는 것을 방지하기 위해 사용됨</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Employees (
    employee_id INT NOT NULL,
    name VARCHAR(100) NOT NULL,
    hire_date DATE
);</code></pre>
<h2 id="2-unique">2. UNIQUE</h2>
<p>컬럼에 중복된 값이 들어가는 것을 방지</p>
<ul>
<li>해당 컬럼은 유일한 값만 저장할 수 있게됨</li>
<li>중복 데이터를 방지하고, 데이터의 고유성을 보장함</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Users (
    user_id INT NOT NULL UNIQUE,
    email VARCHAR(100) UNIQUE
);</code></pre>
<h2 id="3-primary-key">3. PRIMARY KEY</h2>
<p>테이블에서 각 레코드를 고유하게 식별할 수 있는 칼럼</p>
<ul>
<li>기본키는 반드시 고유하며, NULL 값을 허용하지 않음</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100)
);
</code></pre>
<h2 id="4-foreign-key">4. FOREIGN KEY</h2>
<p>다른 테이블의 기본 키나 고유 키를 참조하여 테이블 간의 관계를 정의</p>
<ul>
<li>두 테이블 간의 참조 무결성을 유지하며
한 테이블의 값이 다른 테이블에 존재하는 값이어야 한다는 규칙을 강제</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    FOREIGN KEY (customer_id) REFERENCES Customers(customer_id)
);
# Orders 테이블의 customer_id가 Customers 테이블의 customer_id를 참조하도록 외래 키 제약을 설정</code></pre>
<h2 id="5-check">5. CHECK</h2>
<p>칼럼의 값이 지정된 조건을 만족하는지 검사 → 데이터 유효성 보장</p>
<ul>
<li>비즈니스 규칙 반영에 유용함
수치, 날짜 등의 값에 대해 범위 조건을 지정하거나, 문자열 패턴을 확인하는 데 사용</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Employees (
    employee_id INT PRIMARY KEY,
    salary DECIMAL(10, 2),
    CHECK (salary &gt;= 0) # salary는 음수가 될 수 없음
);</code></pre>
<h2 id="6-default">6. DEFAULT</h2>
<p>칼럼에 값이 입력되지 않았을 경우 기본값을 자동으로 삽입</p>
<ul>
<li>데이터 입력 시 해당 칼럼이 비어있으면 기본값이 자동을 삽입됨</li>
</ul>
<pre><code class="language-sql">CREATE TABLE Orders (
    order_id INT PRIMARY KEY,
    order_status VARCHAR(20) DEFAULT &#39;Pending&#39;
);</code></pre>
<h2 id="7-index">7. INDEX</h2>
<p>칼럼에 인덱스를 생성하는 기능</p>
<ul>
<li>인덱스를 사용하면 조회 성능이 향상되지만,
삽입/ 업데이트 성능은 다소 저하될 수 있음
→ 주로 검색 성능을 최적화하는 데 사용됨</li>
<li>기본키, 고유키는 자동으로 인덱스를 생성하지만 명시적으로 인덱스를 생성할 수도 있음</li>
</ul>
<pre><code class="language-sql">CREATE INDEX idx_employee_name ON Employees (name);
# Employees 테이블의 name 컬럼에 대해 인덱스를 생성 (검색 성능 향상)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] 키]]></title>
            <link>https://velog.io/@do_e/DB-%ED%82%A4</link>
            <guid>https://velog.io/@do_e/DB-%ED%82%A4</guid>
            <pubDate>Fri, 31 Oct 2025 13:51:11 GMT</pubDate>
            <description><![CDATA[<p>DB에서 키는 테이블에서 각 레코드를 고유하게 식별하거나, 데이터 무결성을 유지하는데 중요한 역할능 함 (키 - DB의 정합성, 무결성 성능)</p>
<table>
<thead>
<tr>
<th><strong>분류 기준</strong></th>
<th><strong>키(Key) 종류</strong></th>
<th><strong>정의 (간단하게)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>행 식별</strong> (유일성)</td>
<td><strong>슈퍼키</strong> (Super Key)</td>
<td>행을 식별할 수 있는 <strong>모든 열의 조합</strong> (최소성 X)</td>
</tr>
<tr>
<td></td>
<td><strong>후보 키</strong> (Candidate Key)</td>
<td>행을 식별할 수 있는 <strong>최소한의 열 조합</strong> (PK 후보)</td>
</tr>
<tr>
<td></td>
<td><strong>기본 키</strong> (Primary Key, PK)</td>
<td>후보 키 중 <strong>대표로 선택된 단 하나의 키</strong> (NOT NULL, 중복 X)</td>
</tr>
<tr>
<td></td>
<td><strong>대체 키</strong> (Alternate Key)</td>
<td>기본 키로 <strong>선택되지 않은</strong> 나머지 후보 키</td>
</tr>
<tr>
<td><strong>테이블 관계</strong></td>
<td><strong>외래 키</strong> (Foreign Key, FK)</td>
<td><strong>다른 테이블의 기본 키(PK)를 참조</strong>하는 키 (테이블 연결)</td>
</tr>
</tbody></table>
<h2 id="1-기본키-primary-key">1. 기본키 (Primary Key)</h2>
<p>후보 키 중에서 데이터베이스 설계자가 <strong>대표로 선택한 단 하나의 키</strong></p>
<ul>
<li>유일성 (Unique): 중복 X</li>
<li>NOT NULL</li>
<li>기본 키 칼럼은 레코드를 고유하게 식별 → 데이터 무결성 유지의 중요 요소</li>
</ul>
<h2 id="2-외래키-foreign-key">2. 외래키 (Foreign Key)</h2>
<p>다른 테이블의 기본 키를 참조 하는 키</p>
<ul>
<li>두 테이블 간의 관계를 정의</li>
<li>외래키를 사용하면 참조 무결성을 유지할 수 있으며,
외래키는 참조하는 테이블의 기본 키나 고유 키와 연결됨</li>
<li>외래 키가 참조하는 값은 참조되는 테이블에서 존재하는 값이어야 함 (참조 무결성 보장)</li>
<li>외래 키 칼럼은 중복 허용</li>
<li>Null 값 허용 X</li>
</ul>
<h2 id="3-후보-키-candidate-key">3. 후보 키 (Candidate Key)</h2>
<p>테이블에서 레코드를 유일하게 식별할 수 있는 키</p>
<ul>
<li>유일성 &amp; 최소성을 만족하는 키 → Null 값 허용 X</li>
<li>기본 키는 후보 키 중 하나를 선택한 것
후보 키는 기본 키가 될 수 있는 모든 후보임 (여러 개 일 수 있음)</li>
</ul>
<h2 id="4-대체-키-alternate-key">4. 대체 키 (Alternate Key)</h2>
<p>후보 키 중에서 기본 키로 선택되지 않은 나머지 키</p>
<ul>
<li>기본키 외에 레코드를 유일하게 식별할 수 있는 다른 후보 키들</li>
<li>대체키도 유일성을 보장하고, Null값 허용 X</li>
</ul>
<h2 id="5-슈퍼-키-super-key">5. 슈퍼 키 (Super Key)</h2>
<p>테이블의 각 행을 <strong>유일하게 식별</strong>할 수 있는 <strong>하나 이상의 열(Column) 집합</strong></p>
<ul>
<li>슈퍼키는 기본키, 후보키, 대체키 등을 포함한 더 넓은 범위의 키</li>
<li>유일성만 만족하면 됨 → 불필요한 열이 포함될 수 있음</li>
<li>e.g. 학생 테이블 
학번 - 주민번호 - 이름
학번 : 슈퍼키
주민번호: 슈퍼키
학번, 이름: 슈퍼키
주민번호, 이름, 학번: 슈퍼키</li>
</ul>
<h2 id="6-고유-키-unique-key">6. 고유 키 (Unique Key)</h2>
<p>테이블의 특정 칼럼이나 칼럼 조합에 대해 중복을 허용하지 않도록 하는 키</p>
<ul>
<li>중복 값을 허용하지 않음</li>
<li>하나의 테이블에 여러 개의 고유 키 설정할 수 있음</li>
<li>Null 값 허용 (기본키와 차이점)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] DB의 기초]]></title>
            <link>https://velog.io/@do_e/DB%EC%9D%98-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@do_e/DB%EC%9D%98-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Fri, 31 Oct 2025 13:49:34 GMT</pubDate>
            <description><![CDATA[<h1 id="1-db--dbms">1. DB &amp; DBMS</h1>
<h2 id="1-1--dbdatabase">1-1.  DB(Database)</h2>
<p>: 데이터를 효율적으로 저장하고 관리할 수 있도록 설계된 시스템</p>
<ul>
<li>⇒ 데이터를 체계적으로 저장하고 관리하는 논리적 집합</li>
<li>목표: 데이터의 저장, 검색, 갱신을 효과적 / 효율적으로 처리하기 위함.</li>
</ul>
<h2 id="1-2-dbms-database-management-system">1-2. DBMS (Database Management System)</h2>
<p>데이터를 효율적으로 관리하고, 데이터를 안전하게 저장하며, 여러 사용자들이 데이터베이스에 접근할 수 있도록 제어 / 관리를 수행하는 소프트웨어</p>
<ul>
<li>⇒ 데이터베이스를 생성, 관리, 조작할 수 있게 해주는 소프트웨어</li>
<li>DB를 직접 관리하지 않고, DBMS가 중간에서 요청을 처리함.</li>
<li>DBMS는 데이터의 CRUD를 효율적이고 안전하게 수행함.</li>
<li>목표: 데이터베이스 내의 데이터를 효율적으로 관리하고, 이를 손쉽게 조작하고, 다수의 사용자가 동시에 데이터를 처리할 수 있도록 동시성 제어를 제공함.</li>
</ul>
<h3 id="crud">CRUD</h3>
<p>데이터베이스에서 데이터를 다룰 떄 수행하는 4가지 기본 작업</p>
<table>
<thead>
<tr>
<th>연산</th>
<th>SQL 명령어</th>
<th>의미</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>C (Create)</strong></td>
<td><code>INSERT</code></td>
<td>새 데이터 추가</td>
<td><code>INSERT INTO employees VALUES (...);</code></td>
</tr>
<tr>
<td><strong>R (Read)</strong></td>
<td><code>SELECT</code></td>
<td>데이터 조회</td>
<td><code>SELECT * FROM employees;</code></td>
</tr>
<tr>
<td><strong>U (Update)</strong></td>
<td><code>UPDATE</code></td>
<td>기존 데이터 수정</td>
<td><code>UPDATE employees SET salary = 5000 WHERE id = 1;</code></td>
</tr>
<tr>
<td><strong>D (Delete)</strong></td>
<td><code>DELETE</code></td>
<td>데이터 삭제</td>
<td><code>DELETE FROM employees WHERE id = 1;</code></td>
</tr>
</tbody></table>
<br>

<hr>
<h1 id="2-sql">2. SQL</h1>
<p>SQL, Structured Query Language</p>
<p>DBMS에게 지시를 내리는 명령어의 전체 집합</p>
<p>크게 데이터 정의, 조작, 제어, 트랜젝션 관리 기능 포함</p>
<table>
<thead>
<tr>
<th>분류</th>
<th>역할</th>
<th>SQL 명령어 예시</th>
</tr>
</thead>
<tbody><tr>
<td>DDL</td>
<td>데이터 구조 정의</td>
<td>CREATE, ALTER, DROP, TRUNCATE</td>
</tr>
<tr>
<td>DML</td>
<td>데이터 조작</td>
<td>SELECT, INSERT, UPDATE, DELETE</td>
</tr>
<tr>
<td>DCL</td>
<td>권한 제어</td>
<td>GRANT, REVOKE</td>
</tr>
<tr>
<td>TCL</td>
<td>트랜잭션 제어</td>
<td>COMMIT, ROLLBACK, SAVEPOINT</td>
</tr>
<tr>
<td>- DDL ⇒ 설계도를 그리는 도구</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- DML ⇒ 실제 데이터 작업 도구</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- DCL ⇒ 권한 관리 도구</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- TCL ⇒ 실행 기록 관리 도구</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="2-1ddl-data-definition-language">2-1.<strong>DDL (Data Definition Language)</strong></h2>
<p>데이터 구조를 정의하는 언어</p>
<p>스키마와 테이블 구조를 만드는 역할을 수행함.</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>CREATE</code></td>
<td>데이터베이스, 테이블, 인덱스 생성</td>
<td><code>CREATE TABLE employees (...);</code></td>
</tr>
<tr>
<td><code>ALTER</code></td>
<td>테이블 구조 변경</td>
<td><code>ALTER TABLE employees ADD COLUMN age INT;</code></td>
</tr>
<tr>
<td><code>DROP</code></td>
<td>테이블, DB, 인덱스 삭제</td>
<td><code>DROP TABLE employees;</code></td>
</tr>
<tr>
<td><code>TRUNCATE</code></td>
<td>데이터 전체 삭제 (구조 유지)</td>
<td><code>TRUNCATE TABLE employees;</code></td>
</tr>
<tr>
<td>- DB의 설계도를 다루는 부분으로, 스키마 관리 계층에 속함.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 데이터 구조 변경 작업 (데이터 조작 X)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 자동 커밋 (즉시 반영)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 롤백 불가능</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="2-2-dml-data-manipulation-language">2-2. <strong>DML (Data Manipulation Language)</strong></h2>
<p>데이터를 조작하는 언어</p>
<p>CRUD와 직접적으로 관련 있음</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>INSERT</code></td>
<td>새 데이터 추가</td>
<td><code>INSERT INTO employees VALUES (...);</code></td>
</tr>
<tr>
<td><code>SELECT</code></td>
<td>데이터 조회</td>
<td><code>SELECT * FROM employees;</code></td>
</tr>
<tr>
<td><code>UPDATE</code></td>
<td>데이터 수정</td>
<td><code>UPDATE employees SET salary=6000 WHERE id=1;</code></td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td>데이터 삭제</td>
<td><code>DELETE FROM employees WHERE id=1;</code></td>
</tr>
<tr>
<td>- DBMS 내부에서 데이터 조작 계층에 속함. (Data Manipulation Layer)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- TCL과 함꼐 사용 가능 (COMMIT, ROLLBACK)</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="2-3-dcl-data-control-language">2-3. <strong>DCL (Data Control Language)</strong></h2>
<p>데이터 접근 권한을 제어하는 언어
사용자, 권한, 보안 관련 제어를 담당</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>GRANT</code></td>
<td>사용자에게 권한 부여</td>
<td><code>GRANT SELECT ON employees TO user1;</code></td>
</tr>
<tr>
<td><code>REVOKE</code></td>
<td>권한 회수</td>
<td><code>REVOKE SELECT ON employees FROM user1;</code></td>
</tr>
</tbody></table>
<h3 id="2-4-tcl-transaction-control-language">2-4. <strong>TCL (Transaction Control Language)</strong></h3>
<p>트랜젝션 단위를 제어하는 언어</p>
<p>데이터 조작(DML)의 변경 시점을 관리</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><code>COMMIT</code></td>
<td>변경사항 확정</td>
<td><code>COMMIT;</code></td>
</tr>
<tr>
<td><code>ROLLBACK</code></td>
<td>변경사항 취소</td>
<td><code>ROLLBACK;</code></td>
</tr>
<tr>
<td><code>SAVEPOINT</code></td>
<td>중간 저장점 설정</td>
<td><code>SAVEPOINT pointA;</code></td>
</tr>
<tr>
<td><code>SET TRANSACTION</code></td>
<td>트랜잭션 속성 설정</td>
<td><code>SET TRANSACTION READ ONLY;</code></td>
</tr>
<tr>
<td>- TCL은 DBMS의 트랜잭션 관리 계층에 속함.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- DML과 함께 사용됨.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- DDL은 자동 커밋이므로 TCL의 영향을 받지 않음.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<br>

<hr>
<h1 id="3-테이블--스키마">3. 테이블 &amp; 스키마</h1>
<h2 id="3-1-db-테이블">3-1. DB 테이블</h2>
<p>: DB에서 데이터를 구조적으로 저장하는 기본 단위</p>
<p>각 테이블은 하나의 엔티티를 나타내며,
행 &amp; 열로 이루어져 있음.</p>
<ul>
<li>행 (Row, Tuple, Record)
테이블에서 하나의 레코드 / 튜플을 나타냄.
각 행은 테이블 내에서 하나의 개별 데이터를 나타내며, 여러 열(속성)을 포함함.
(학생 테이블 - 한 명의 학생이 하나의 행을 차지)</li>
<li>열 (Column, Attribute)
테이블에서 데이터를 속성 단위로저장하는 부분 
각 열은 특정 속성의 데이터를 저장함.
(학생 테이블 - 이름, 나이, 학번)</li>
</ul>
<h2 id="3-2-스키마">3-2. 스키마</h2>
<h3 id="스키마란">스키마란?</h3>
<p>: 테이블의 구조와 제약 조건을 정의한 설계도</p>
<ul>
<li>DB내에 포함될 데이터의 구조, 테이블, 속성(필드), 관계, 제약 조건 등을 정의하는 것</li>
<li>스키마 설계는 DB가 어떻게 구성될지에 대한 논리적 구조를 정의하며, DB 설계에서 매우 중요한 단계</li>
<li>스키마는 DB를 효율적이고 일관성 있게 관리하고 무결성을 보장하기 위해 살용됨</li>
<li>스키마 &amp; DB
스키마 ⇒ 데이터의 구조적 설계도
데이터베이스 ⇒ 그 설계대로 채워진 실제 데이터 공간</li>
</ul>
<h3 id="스키마의-구성-요소">스키마의 구성 요소</h3>
<ul>
<li>테이블 구조
각 테이블의 칼럼(속성)과 데이터 타입</li>
<li>테이블 간의 관계
1:1, 1:N, M:N 등</li>
<li>제약 조건</li>
<li><em>Primary Key*</em>, <strong>Foreign Key</strong>, <strong>Unique</strong>, <strong>Not Nul</strong></li>
<li><strong>뷰(View)</strong>, <strong>인덱스(Index)</strong>, <strong>프로시저(Stored Procedures)</strong> 등</li>
</ul>
<h3 id="스키마-3계층">스키마 3계층</h3>
<p>사용자, 설계자, 시스템의 관점을 분리하여 DB를 설계하고 관리하는 표준적인 방식</p>
<p>스키마를 3단계로 나누는 가장 큰 이유는 <strong>데이터 독립성</strong>을 확보하기 위함임.</p>
<ul>
<li><strong>논리적 데이터 독립성</strong>
개념 스키마가 변경되어도, 기존 외부 스키마는 영향을 받지 않는 것을 의미함.
DB의 논리적 구조가 확장되거나 변경되어도, 기존에 사용하던 프로그램 코드를 수정할 필요가 없음.</li>
<li><strong>물리적 데이터 독립성</strong>
내부 스키마가 변경되어도, 개념 스키마와 외부 스키마는 영향을 받지 않는 것을 의미함.
DB 관리자가 성능 튜닝을 위해 물리적 구조를 바꿔도, 사용자나 애플리케이션은 동일하게 DB를 사용할 수 있음.</li>
</ul>
<ol>
<li><p><strong>외부 스키마 (External Schema)</strong></p>
<p> [관점] 최종 사용자 / 응용 프로그램</p>
<p> 개별 사용자나 특정 애플리케이션이 필요로 하는 DB의 일부를 정의한 것</p>
<p> 전체 DB 중 사용자가 보게 될 창 / 뷰로 생각할 수 있음.</p>
<p> e.g. 쇼핑몰 DB) 고객 - 자신의 주문 내역만 볼 수 있고, 배송팀- 배송지 주소와 상품명만 볼 수 있음.</p>
<ul>
<li>하나의 DB에 여러 개의 외부 스키마가 존재할 수 있음</li>
<li>DB의 전체 구조를 숨기고, 사용자가 필요한 부분에만 간단하게 접근할 수 있게 함.</li>
<li>서브 스키마라고도 불림</li>
</ul>
</li>
<li><p><strong>개념 스키마 (Conceptual Schema)</strong></p>
<p> [관점] DBA(DB 관리자 / 설계자)관맂</p>
<p> DB의 전체적인 논리적 구조를 정의한 것</p>
<p> DB에 무엇이 저장되는지 나타냄
 : 어떤 엔티티가 있고, 어떤 속성을 가지며, 개체들 간 어떤 관계를 가지는지?</p>
<p> 스키마 / ERD로 그리는 대상</p>
<ul>
<li>DB에 단 하나만 존재함</li>
<li>데이터의 무결성, 보안 규칙, 제약 조건 등 모두 이곳에 정의됨</li>
<li>물리적 저장 방식(내부 스키마)이나 개별 사용자 뷰(외부 스키마)와는 독립적임</li>
</ul>
</li>
<li><p><strong>내부 스키마 (Internal Schema)</strong></p>
<p> [관점] DBMS / 시스템 프로그래머</p>
<p> 데이터가 하드디스크와 같은 물리적 저장 장치에 실제로 어떻게 저장되는지 정의한 것</p>
<p> 데이터의 물리적인 저장 구조, 파일 구성 방식, 인덱스 생성 방법, 데이터 압축 등을 다룸</p>
<p> DB의 성능, 효율성에 직접적인 영향을 줌</p>
<ul>
<li>DB에 단 하나만 존재함</li>
<li>물리적 스키마(Physical Schcema)라고도 부름</li>
<li>개념 스키마와 논리적 구조가 실제로 어떻게 구현될지 구체적으로 명시함</li>
</ul>
</li>
</ol>
<h2 id="3-3-스키마-설계">3-3. 스키마 설계</h2>
<p>스키마 설계는 데이터의 논리적 관계와 제약을 설계하는 과정임.</p>
<ol>
<li>데이터 무결성 &amp; 정확성 확보 <ul>
<li>PK 정의
모든 테이블의 각 행은 고유하게 식별할 수 있는 기본 키를 가져야 함.</li>
<li>정확한 데이터 타입 설정
데이터를 가장 잘 표현할 수 있는 타입을 사용하여야 함.
e.g. 나이 (INT)</li>
<li>제약조건 적극 활용
NOT NULL, UNIQUE, FK,…</li>
</ul>
</li>
<li>데이터 중복 최소화 (정규화, Normalization)<ul>
<li><strong>정규화 (Normalization)</strong>
중복된 데이터를 최소화 &amp; 데이터 무결성을 유지하기 위해 데이터를 적절히 분할하는 과정
보통 1NF (제1정규형)부터 5NF(제5정규형)까지 여러 단계로 나뉘어 데이터 구조를 최적화
정규화는 데이터 중복을 피하고 갱신 / 삭제 이상 문제를 예방하는 데 유리하지만, 조인 연산이 많아져서 성능에 영향을 미칠 수 있음.<ul>
<li><strong>1NF</strong>: 각 컬럼에 원자값(Atomic Value)만을 포함하여 데이터를 중복하지 않도록 함.</li>
<li><strong>2NF</strong>: 부분 종속성을 제거하여 데이터 무결성을 강화함.</li>
<li><strong>3NF</strong>: 이행적 종속성을 제거하여 데이터의 의존 관계를 단순화함.</li>
<li><strong>BCNF(보이스-코드 정규형)</strong>: 3NF보다 한 단계 더 엄격한 정규화</li>
</ul>
</li>
</ul>
</li>
<li>명확성 &amp; 일관성 유지<ul>
<li>일관된 이름 규칙 (Naming Convention)
테이블 이름: 복수형 / 단수형 중 하나로 통일 (복수형이 일반적)
컬럼 이름: 보통 snake_case
직곽적인 이름 &amp; 주석 추가 (설명)</li>
</ul>
</li>
<li>성능 &amp; 확장성 고려</li>
</ol>
<p>  데이터와 사용자가 많아졌을 때도 시스템이 원활하게 동작하도록 설계해야 함.</p>
<ul>
<li>적절한 인덱스 설계
  WHERE 절에서 자주 조회되거나, JOIN의 연결고리가 되는 컬럼에서는 인덱스를 생성하여 조회속도를 향상시킴.
  단 ,인덱스는 INSERT / UPDATE 성능을 저하시킬 수 있으므로 무분별한 사용 X<ul>
<li>반정규화의 전략적 사용
정규화(원칙 2)를 철저하게 지킬 경우 JOIN이 많아져 성능이 저하될 수 있음.
조회 성능을 높이기 위해 의도적으로 중복을 허용하거나 테이블을 합침. (반정규화)
   반정규화는 정규화를 충분히 거친 후 성능상 이점이 확실할 때만 선택적으로 적용되어야 함.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Problem Solving] BJ20055. 컨베이어 벨트 위의 로봇]]></title>
            <link>https://velog.io/@do_e/Problem-Solving-42898.-%EB%93%B1%EA%B5%A3%EA%B8%B8</link>
            <guid>https://velog.io/@do_e/Problem-Solving-42898.-%EB%93%B1%EA%B5%A3%EA%B8%B8</guid>
            <pubDate>Mon, 27 Oct 2025 08:40:48 GMT</pubDate>
            <description><![CDATA[<h1 id="1-문제-설명">1. 문제 설명</h1>
<ul>
<li>2N 길이의 컨베이어 벨트 (N *2)
로봇을 올리는 위치: 1번칸(인덱스: 0)
로봇을 내리는 위치: N번칸(인덱스: N-1)</li>
<li>로봇을 내린다? 벨트에서 완전히 제거 (밑 라인으로 이동 X)</li>
<li>각 칸은 내구도를 가짐
로봇을 올리는 위치에 올리거나, 로봇이 어떤 칸으로 이동하면
그 칸의 내구도는 1만큼 감소함</li>
<li>내구도가 0인 칸의 개수가 K개 이상이라면 과정을 종료
→ 몇 번째 단계에서 진행이 멈추는가?</li>
</ul>
<h2 id="컨테이너-벨트의-작동-과정">컨테이너 벨트의 작동 과정</h2>
<ol>
<li><strong>컨베이어 벨트의 회전</strong>
벨트는 각 칸 위에 있는 로봇과 함께 회전함</li>
<li><strong>회전 후 로봇의 이동</strong>
가장 먼저 올라간 로봇부터, 회전 방향으로 1칸 이동 가능하다면 이동, 아니라면 가만히 있음</li>
</ol>
<ul>
<li>로봇의 이동 조건:
이동하려는 칸에 로봇이 없으며, 그 칸의 내구도가 1 이상 남아 있어야 함</li>
</ul>
<ol start="3">
<li><strong>로봇 올리기</strong>
올리는 위치의 내구도가 0이 아니면 가능</li>
<li><strong>내구도가 0인 칸의 개수 ≥ K → 종료</strong></li>
</ol>
<ul>
<li>내구도 감소 조건:
로봇을 올리는 위치에 올리거나 / 로봇이 어떤 칸으로 이동할 경우, 그 칸의 내구도 —</li>
</ul>
<pre><code class="language-jsx">// [input]
3 2
1 2 1 2 1 2

// [output]
2</code></pre>
<h1 id="2-스크래치">2. 스크래치</h1>
<h2 id="문제-유형-파악-시뮬레이션">문제 유형 파악: 시뮬레이션</h2>
<p>주어진 환경에서 여러 작업을 순차적으로 실행</p>
<p>상태 변화와 규칙에 따라 동작을 구현</p>
<ol>
<li><strong>상태 변화 관리 &amp; 규칙의 순차적 처리</strong></li>
</ol>
<ul>
<li>컨베이어 벨트의 회전 → 벨트의 내구도, 로봇의 위치가 변화함</li>
<li>회전 이후 로봇 이동, 로봇 추가, 로봇 제거의 작업이 발생</li>
</ul>
<ol>
<li><strong>종료 조건</strong></li>
</ol>
<ul>
<li>문제에서 주어진 K ≤ 내구도가 0인 칸의 개수</li>
</ul>
<h1 id="3-풀이">3. 풀이</h1>
<h2 id="로직-문제-쪼개기">[로직] 문제 쪼개기</h2>
<pre><code class="language-jsx">// [input]

// [logic]
// 로봇의 배열 &amp; 스텝수 정의
boolean[] robots = new boolean[N];
int step = 0; 

while(true) {
    // 스텝 수 증가
    step ++;

    // 1. 벨트 회전 -&gt; 내구도, 로봇 배열 변화

    // 2. 회전 후 로봇의 이동 조건 확인 -&gt; 이동 가능하다면 &amp; 이동 후 로봇의 위치 &amp; 내구도 처리

    // 3. 로봇 올리기 조건 확인

    // 4. 종료 조건 확인 -&gt; break를 통해 while문 탈출
}

// [output]: step 수 출력</code></pre>
<h3 id="1-벨트-회전">1. 벨트 회전</h3>
<pre><code class="language-jsx">            int last = belt[2*N-1];
            System.arraycopy(belt, 0, belt, 1, 2*N-1); // System.arraycopy(src,srcPos,dest,destPost, length)
            belt[0] = last;

            boolean[] newRobots = new boolean[N];
            for(int i=0; i&lt;N-1; i++) newRobots[i+1] = robots[i];
            robots = newRobots;
            robots[N-1] = false; // 마지막 위치에 오면 내림</code></pre>
<ul>
<li><strong>belt 배열 회전</strong>
마지막 원소 값을 저장한 후, 원ㅇ본 배열을 복사해 0번째 인덱스에 마지막 원소값 씌우기</li>
<li><strong>robots 배열 변화</strong>
새로운 배열 newRobots 배열에 1칸씩 당겨진 값을 저장</li>
<li><strong>마지막 위치(N-1인덱스)에서의 로봇 제거</strong></li>
</ul>
<h3 id="2-로봇의-이동">2. 로봇의 이동</h3>
<pre><code class="language-jsx">            for(int i=N-2; i&gt;=0; i--){
                if(robots[i] &amp;&amp; !robots[i+1] &amp;&amp; belt[i+1]&gt;0){
                    robots[i]=false;
                    robots[i+1]=true;
                    belt[i+1]--;
                }
            }
            robots[N-1] = false;</code></pre>
<ul>
<li>가장 먼저 올라간 로봇부터 뒤로 1칸씩 이동 → 뒤에서부터 for문으로 처리</li>
<li>i번째 위치 한 로봇 → i+1의 위치로 옮기기</li>
<li>옮긴 위치 (i+1)의 내구도 감소시키기</li>
<li>for문으로 모든 로봇을 옮긴 후 N-1에 위치한 로봇 제거하기 (내리기)</li>
</ul>
<h3 id="3-로봇-올리기">3. 로봇 올리기</h3>
<pre><code class="language-jsx">            if(belt[0]&gt;0 &amp;&amp; !robots[0]){
                robots[0]=true;
                belt[0]--;
            }</code></pre>
<ul>
<li>조건: 0번 인덱스의 내구도가 0보다 크고 로봇이 없을 경우</li>
<li>로봇을 올리고 내구도 1 감소시키기</li>
</ul>
<h3 id="4-종료-조건">4. 종료 조건</h3>
<pre><code class="language-jsx">                int zeroCount = 0;
            for(int val : belt) if(val==0) zeroCount++;
            if(zeroCount &gt;= K) break;</code></pre>
<h1 id="4-코드">4. 코드</h1>
<pre><code class="language-jsx">public class Main {
    static int N,K;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader (new InputStreamReader(System.in));

        // [input]
        String[] tmp = br.readLine().split(&quot; &quot;);
        N = Integer.parseInt(tmp[0]);
        K = Integer.parseInt(tmp[1]);

        // 0-based
        String[] temp = br.readLine().split(&quot; &quot;);
        int[] belt = new int[2*N];

        for(int i=0; i&lt;N*2; i++) {
            belt[i] = Integer.parseInt(temp[i]);
        }

        // [logic]
        boolean[] robots = new boolean[N];

        int step = 0;
        while(true){
            step++;

            // 1. 벨트 회전
            int last = belt[2*N-1];
            System.arraycopy(belt, 0, belt, 1, 2*N-1); // System.arraycopy(src,srcPos,dest,destPost, length)
            belt[0] = last;

            boolean[] newRobots = new boolean[N];
            for(int i=0; i&lt;N-1; i++) newRobots[i+1] = robots[i];
            robots = newRobots;
            robots[N-1] = false; // 마지막 위치에 오면 내림

            // 2. 로봇 이동
            for(int i=N-2; i&gt;=0; i--){
                if(robots[i] &amp;&amp; !robots[i+1] &amp;&amp; belt[i+1]&gt;0){
                    robots[i]=false;
                    robots[i+1]=true;
                    belt[i+1]--;
                }
            }
            robots[N-1] = false;

            // 3. 로봇 올리기
            if(belt[0]&gt;0 &amp;&amp; !robots[0]){
                robots[0]=true;
                belt[0]--;
            }

            // 4. 종료 조건
            int zeroCount = 0;
            for(int val : belt) if(val==0) zeroCount++;
            if(zeroCount &gt;= K) break;
        }

        // 출력
        System.out.println(step);


    }
}</code></pre>
<h1 id="5-de-fault">5. De-fault</h1>
<h2 id="fault-1-배열의-회전-내구도-배열">fault-1. 배열의 회전: 내구도 배열</h2>
<h3 id="배열-회전-방법">배열 회전 방법</h3>
<ol>
<li><strong>배열 복사 (<code>System.arraycopy(src,srcPos,dest,destPost, length)</code> )</strong></li>
<li><strong>큐(Deque) 자료구조 사용</strong></li>
</ol>
<pre><code class="language-jsx">              // 배열을 오른쪽으로 1칸 회전
        int last = deque.removeLast();  // 마지막 요소 제거
        deque.addFirst(last);           // 마지막 요소를 첫 번째로 추가

        // 배열을 왼쪽으로 1칸 회전
        int first = deque.removeFirst();  // 첫 번째 요소 제거
        deque.addLast(first);             // 첫 번째 요소를 마지막으로 추가</code></pre>
<h2 id="fault-2-로봇을-이동시킬-때-for문-처리">fault-2. 로봇을 이동시킬 때: for문 처리</h2>
<p>로봇의 이동은 벨트가 회전한 후 뒤에서 앞으로 이동해야 하므로 for문을 뒤에서부터 작성해야 함</p>
<p><strong>뒤에서부터 처리해야 하는 이유?</strong></p>
<p>앞에서부터 처리할 경우 이미 이동한 로봇을 덮어쓰게 되어 로봇이 이동하지 않는 오류가 발생</p>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ol>
<li><strong>벨트 회전</strong> (<code>O(N)</code>)
<code>System.arraycopy(belt, 0, belt, 1, 2*N-1)</code>를 사용한 배열의 회전 </li>
<li><strong>로봇 이동</strong> (<code>O(N)</code>)
한 번에 벨트의 크기 N 만큼 반복</li>
<li><strong>로봇 올리기</strong> (<code>O(1)</code>)
한 번의 조건 체크와 연산</li>
<li><strong>종료 조건 체크</strong> (<code>O(N)</code>)
belt 배열을 1번 순회</li>
</ol>
<p>⇒ 각 시뮬레이션 단계에서 수행하는 연산은 <strong>최대 <code>O(N)</code></strong> 시간이 걸림</p>
<p>시뮬레이션을 <strong>최대 <code>K</code>번 반복</strong>해야 하기 때문에, 전체 시간 복잡도는 <code>O(N * K)</code></p>
<p>이 코드에서 <strong>잘못된 부분</strong>이나 <strong>개선할 점</strong>이 있다면 언제든지 <strong>댓글로</strong> 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 파일 시스템]]></title>
            <link>https://velog.io/@do_e/OS-%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@do_e/OS-%ED%8C%8C%EC%9D%BC-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Thu, 23 Oct 2025 16:19:29 GMT</pubDate>
            <description><![CDATA[<h1 id="1-파일-시스템-개념">1. 파일 시스템 개념</h1>
<h2 id="1-1-파일-시스템이란">1-1. 파일 시스템이란?</h2>
<p>사용자가 데이터를 효율적으로 저장, 접근, 관리할 수 있도록 해주는 핵심 메커니즘이자, 
보조 기억 장치(하드 디스크, SSD 등)의 물리적인 공간을 논리적인 파일과 디렉토리 형태로 추상화해주는 체계</p>
<ul>
<li>파일 시스템의 궁극적인 목저은 보조 기억 장치에 저장된 데이터를 사용자가 편리하게 이용하도록 돕는 것</li>
</ul>
<h3 id="핵심-특징-및-역할">핵심 특징 및 역할</h3>
<ol>
<li>커널 영역 동작
파일 시스템의 핵심 기능(메모리 할당, 디스크 I/O 등)은 안정성이 중요한 <strong>커널 모드</strong>에서 실행됨</li>
<li><strong>목적 (CRUD)
파일의 생성(Create), 읽기(Read), 수정(Update), 삭제(Delete) 기능을 원활하고 안정적으로 수행함</strong></li>
<li>계층적 구조
파일을 관리하기 쉬운 트리 구조로 구성하여 파일 탐색 및 분류를 용이하게 함</li>
<li>보조 저장소 관리
파일 자체 뿐만 아니라, 파일을 저장하는 디스크 공간(빈 공간, 할당된 공간)도 효율적으로 관리함</li>
<li>무결성 메커니즘
시스템 충돌이나 전원 문제 발생 시에도 데이터의 손상(파일 무결성)을 최소화하고 복구할 수 있는 메커니즘(e.g. 저널링)을 제공</li>
</ol>
<h1 id="2-파일과-디렉토리">2. 파일과 디렉토리</h1>
<h2 id="2-1-파일-file">2-1. 파일 (File)</h2>
<ul>
<li>사용자가 인식하는 정보의 논리적인 저장 단위</li>
<li>파일은 이름, 속성(유형, 크기, 생성 시간, 접근 권한 등), 데이터를 가짐</li>
<li>OS는 이러한 속성은 파일 제어 블록 (FCB, File Control Block)에 저장하여 관리함</li>
</ul>
<h3 id="파일의-구성-요소">파일의 구성 요소</h3>
<ul>
<li><strong>메타 영역 (Metadata Area):</strong><ul>
<li>파일 자체의 데이터가 아닌, 파일을 설명하는 정보(속성)를 저장합니다.</li>
<li>포함 내용: <strong>파일 이름, 파일의 데이터가 저장된 위치 주소, 크기, 소유자, 접근 권한, 생성/수정/접근 시간, 삭제 여부</strong> 등.</li>
<li>이 메타 정보를 저장하는 대표적인 구조가 <strong>FCB (File Control Block)</strong> 또는 <strong>Inode (아이노드)</strong>입니다.</li>
</ul>
</li>
<li><strong>데이터 영역 (Data Area):</strong><ul>
<li>실제 파일의 내용(바이너리 코드, 텍스트, 이미지 등)이 저장되는 영역입니다.</li>
</ul>
</li>
</ul>
<h2 id="2-2-디렉토리-directory">2-2. 디렉토리 (Directory)</h2>
<ul>
<li>파일을 계층적으로 묶어 구조화하고 관리하는 도구</li>
<li>파일 이름과 해당 파일을 찾기 위한 정보를 매핑하여 저장함</li>
</ul>
<h3 id="디렉토리-구조">디렉토리 구조</h3>
<ol>
<li>단일 레벨 디렉토리 (Single-Level Directory)
모든 파일이 <strong>하나의 디렉터리</strong> 아래에 저장됨</li>
<li>2단계 디렉토리 </li>
<li>계층적/트리 구조 디렉토리</li>
</ol>
<table>
<thead>
<tr>
<th><strong>구조</strong></th>
<th><strong>특징</strong></th>
<th><strong>장점</strong></th>
<th><strong>단점</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>1단계 (Single-Level)</strong></td>
<td>시스템 내 파일이 <strong>하나의 디렉터리</strong>에만 존재함</td>
<td>구현이 <strong>가장 단순</strong>함</td>
<td>파일 이름 <strong>충돌</strong> 발생 쉬움. 파일 관리 어려움</td>
</tr>
<tr>
<td><strong>2단계 (Two-Level)</strong></td>
<td>사용자별 <strong>UFD</strong>를 두고 <strong>MFD</strong>가 관리함</td>
<td>사용자 간 <strong>이름 충돌 해소</strong>됨. 사용자별 파일 구분이 명확함</td>
<td>사용자 간 <strong>파일 공유</strong> 및 파일 그룹화 어려움</td>
</tr>
<tr>
<td><strong>트리 구조 (Tree Structure)</strong></td>
<td><strong>루트</strong>를 정점으로 하는 <strong>계층적 다단계</strong> 구조임</td>
<td>파일의 <strong>논리적 그룹화</strong> 용이함. <strong>절대/상대 경로</strong> 접근 가능함</td>
<td>탐색 시 <strong>시간 오버헤드</strong> 발생 가능함. 파일 공유가 간접적임</td>
</tr>
<tr>
<td><strong>그래프 구조 (Acyclic-Graph)</strong></td>
<td>트리 구조에 <strong>링크</strong>를 추가하여 파일 공유를 허용함</td>
<td>파일 및 디렉터리 <strong>공유가 가능</strong>하여 효율성 높음</td>
<td><strong>순환(Cycle) 처리</strong> 복잡함. 삭제 시 <strong>댕글링 포인터</strong> 문제 발생함</td>
</tr>
</tbody></table>
<h1 id="3-파일-할당-방식">3. 파일 할당 방식</h1>
<p>파일 시스템은 파일의 논리적인 블록을 디스크의 물리적인 블록에 어떻게 대응시켜 저장할지를 결정함</p>
<ol>
<li><p>연속 할당(Contiguous Allocation)</p>
<p> 파일의 모든 데이터 블록을 디스크의 연속된 물리적 블록에 저장함</p>
<p> [+] 순차 접근 및 직접 접근이 매우 빠르고, 구현이 단순함</p>
<p> [-] 파일 크기 변경이 어렵고, 외부 단편화 문제가 심각하게 발생함</p>
</li>
<li><p>연결 할당(Linked Allocation)
파일의 각 데이터 블록을 디스크에 흩어져 저장됨
각 블록은 다음 블록의 주소를 포인터 형태로 저장하여 연결함
[+] 외부 단편화가 발생하지 않고, 파일 크기 변경이 용이함
[-] 직접 접근이 불가능하고 순차 접근만 가능하며, 포인터 저장 공간으로 인한 낭비가 발생하고, 포인터 손상 시 데이터 접근이 끊어지는 신뢰성 문제</p>
</li>
<li><p>인덱스 할당(Indexed Allocation)
파일의 모든 디스크 블록 주소를 모아놓은 인덱스 블록을 사용함
파일 접근 시 인덱스 블록을 먼저 참조하여 데이터의 위치를 파악함
[+] 직접 접근이 가능하고, 외부 단편화가 없음
[-] 인덱스 블록을 위한 추가적인 공간이 필요하며, 대용량 파일의 경우 인덱스 블록을 관리하는 것이 복잡해짐</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 메모리 관리]]></title>
            <link>https://velog.io/@do_e/OS-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@do_e/OS-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Thu, 23 Oct 2025 16:19:01 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th>순서</th>
<th>학습 주제</th>
<th>핵심 질문</th>
</tr>
</thead>
<tbody><tr>
<td>①</td>
<td>메모리 계층 구조</td>
<td>CPU~디스크까지 어떤 계층이 있는가?</td>
</tr>
<tr>
<td>②</td>
<td>캐시 메모리 원리</td>
<td>CPU는 왜 캐시가 필요한가?</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>페이징, TLB, 스왑</td>
<td>가상 메모리는 성능을 어떻게 유지하는가?</td>
</tr>
<tr>
<td>⑦</td>
<td>캐시 vs 가상 메모리 통합</td>
<td>두 시스템은 어떤 관계인가?</td>
</tr>
</tbody></table>
<h1 id="1-메모리">1. 메모리</h1>
<p>프로세스가 메모리를 어떻게 사용하는가 → OS가 이를 어떻게 효율적으로 관리하는가</p>
<h2 id="1-1-메모리-계층-구조">1-1. 메모리 계층 구조</h2>
<p>CPU~디스크까지 어떤 계층이 있는가?</p>
<table>
<thead>
<tr>
<th>계층</th>
<th>구성요소</th>
<th>관리 주체</th>
<th>속도</th>
<th>용량</th>
<th>주요 구성요소</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><strong>레지스터(Register)</strong></td>
<td>CPU 하드웨어</td>
<td>가장 빠름</td>
<td>매우 작음</td>
<td>CPU 내부 레지스터</td>
</tr>
<tr>
<td>2</td>
<td><strong>캐시(Cache)</strong></td>
<td>CPU 하드웨어</td>
<td>빠름</td>
<td>작음</td>
<td>L1, L2, L3 캐시</td>
</tr>
<tr>
<td>3</td>
<td><strong>메인 메모리(Main Memory, RAM)</strong></td>
<td>운영체제(OS)</td>
<td>중간</td>
<td>중간</td>
<td>DRAM (Dynamic RAM)</td>
</tr>
<tr>
<td>4</td>
<td><strong>보조기억장치(Secondary Storage)</strong></td>
<td>운영체제(OS) /</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>파일 시스템</td>
<td>느림</td>
<td>큼</td>
<td>SSD, HDD</td>
<td></td>
<td></td>
</tr>
<tr>
<td>5</td>
<td><strong>보관 장치(Backup Storage)</strong></td>
<td>사용자 / 서버</td>
<td>가장 느림</td>
<td>매우 큼</td>
<td>외장 드라이브, 클라우드, 테이프 등</td>
</tr>
<tr>
<td>- <strong>캐시메모리: CPU ↔ 메인 메모리 간 최적화</strong></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <strong>가상 메모리: 메인 메모리 ↔ 디스크 간 최적화</strong></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="1-2-지역성의-원리-principle-of-locality">1-2. 지역성의 원리 (Principle of Locality)</h2>
<p>모든 메모리 관리 기법의 핵심 전제</p>
<p>CPU가 데이터를 접근할 때 특정 영역을 집중적으로 접근하는 경향</p>
<ul>
<li><strong>시간적 지역성:</strong> 최근에 사용된 데이터는 곧 다시 사용될 가능성이 높음</li>
<li><strong>공간적 지역성:</strong> 현재 참조된 주소와 가까운 주소의 내용이 곧 참조될 가능성이 높음</li>
</ul>
<h1 id="2-캐시-메모리-cache-memory"><strong>2. 캐시 메모리 (Cache Memory)</strong></h1>
<p>CPU는 왜 캐시가 필요한가? → CPU 속도 vs RAM 속도의 차이 (메모리 병목 문제)</p>
<p><strong>CPU의 메모리 접근 속도를 보완하기 위한 하드웨어적 메모리 관리 기법</strong></p>
<p>OS가 직접 개입하지 않으며, CPU와 MMU 수준에서 관리됨</p>
<h2 id="2-1-기본-동작">2-1. 기본 동작</h2>
<ul>
<li><strong>캐시 라인(Cache Line):</strong> 캐시가 데이터를 저장하는 최소 단위</li>
<li><strong>Cache Hit (적중) / Miss (실패):</strong> 데이터가 캐시에 있을 때와 없을 때의 처리</li>
</ul>
<h2 id="2-2-주소-매핑-기법">2-2. 주소 매핑 기법</h2>
<p>데이터가 RAM에서 캐시로 올라올 때 어느 위치에 저장될지 결정하는 방법</p>
<ul>
<li><strong>직접 매핑 (Direct Mapping):</strong> 가장 단순하지만 충돌(Conflict Miss)이 자주 발생합니다.</li>
<li><strong>완전 연관 매핑 (Fully Associative Mapping):</strong> 충돌은 적지만 비용이 높고 탐색이 느립니다.</li>
<li><strong>집합 연관 매핑 (Set-Associative Mapping):</strong> 두 방식의 절충안이며 현대 시스템에서 주로 사용됩니다.</li>
</ul>
<h2 id="1-2-메인-메모리--프로세스-주소-공간">1-2. 메인 메모리 &amp; 프로세스 주소 공간</h2>
<p>RAM이 어떻게 사용되는가?</p>
<ul>
<li>프로그램 실행 시 <strong>Code / Data / Heap / Stack</strong> 구조로 메모리 배치</li>
<li>프로세스마다 <strong>독립된 주소 공간</strong>을 가짐</li>
<li>논리 주소(Logical)와 물리 주소(Physical)의 구분 등장</li>
<li>여기서 OS는 <strong>논리 주소 → 물리 주소 매핑</strong>을 관리해야 함</li>
</ul>
<p>➡ 이 논리 주소 매핑을 효율적으로 관리하기 위한 기술이 <strong>가상 메모리</strong></p>
<h3 id="논리-주소-vs-물리-주소">논리 주소 vs 물리 주소</h3>
<ul>
<li><strong>논리 주소(Logical Address):</strong> CPU가 생성하는 주소 (가상 주소, Virtual Address)</li>
<li><strong>물리 주소(Physical Address):</strong> 실제 RAM 상의 주소</li>
</ul>
<p>운영체제는 <strong>논리 주소 → 물리 주소</strong> 변환을 통해 프로세스마다 독립적인 주소 공간을 제공하며, 이것이</p>
<p><strong>가상 메모리 시스템의 핵심 동작 원리</strong></p>
<h3 id="mmu-memory-management-unit">MMU (Memory Management Unit)</h3>
<p>MMU는 CPU 안에 내장된 하드웨어로, <strong>주소 변환(Address Translation)</strong> 을 수행함</p>
<ul>
<li>논리 주소를 물리 주소로 변환</li>
<li>페이지 테이블(Page Table) 참조</li>
<li>접근 권한 체크(읽기/쓰기/실행 여부)</li>
<li>캐시 및 TLB 관리</li>
</ul>
<h3 id="주소-공간의-구조">주소 공간의 구조</h3>
<p>프로세스가 실행될 때, 운영체제는 프로세스마다 독립적은 주소공간을 제공함</p>
<p>주소공간은 일반적으로 다음 4개의 영역으로 구성됨</p>
<table>
<thead>
<tr>
<th>영역</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>코드(Code)</strong></td>
<td>실행 명령어 저장</td>
<td>함수, 제어문</td>
</tr>
<tr>
<td><strong>데이터(Data)</strong></td>
<td>전역 변수, static 변수</td>
<td><code>int a = 10;</code></td>
</tr>
<tr>
<td><strong>힙(Heap)</strong></td>
<td>동적 메모리 할당 공간</td>
<td><code>malloc()</code>, <code>new</code></td>
</tr>
<tr>
<td><strong>스택(Stack)</strong></td>
<td>함수 호출, 지역 변수 저장</td>
<td>함수 매개변수, 지역 변수</td>
</tr>
<tr>
<td>- 코드, 데이터는 고정 크기</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 힙, 스택은 실행 중 동적으로 변함</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h3 id="메모리-바인딩">메모리 바인딩</h3>
<p>주소를 결정하는 시점에 따라 3가지로 구분됨</p>
<table>
<thead>
<tr>
<th>바인딩 시점</th>
<th>설명</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Compile Time</strong></td>
<td>실행 전에 절대 주소로 결정</td>
<td>주소 고정, 유연성 낮음</td>
</tr>
<tr>
<td><strong>Load Time</strong></td>
<td>프로그램 로드 시 주소 결정</td>
<td>재배치 가능</td>
</tr>
<tr>
<td><strong>Run Time</strong></td>
<td>실행 중 동적으로 주소 변환</td>
<td>가상 메모리 필요, 가장 유연함</td>
</tr>
</tbody></table>
<h1 id="3-가상-메모리-virtual-memory">3. 가상 메모리 (Virtual Memory)</h1>
<p><strong>메모리 용량을 확장하고, 프로세스 간 메모리 독립성을 보장하는 OS 관리 기법</strong></p>
<p>왜 필요한가? 물리적 메모리 한계 극복, 메모리 보호, 다중 프로그래밍 효율성 증대</p>
<ul>
<li>물리적으로 전재하지 않는 메모리를 마치 존재하는 것처럼 사용 → 셀제 RAM보다 큰 공간을 사용하는 프로그램도 문제없이 실행할 수 있도록 하는 논리적 메모리 확장 기법</li>
<li><strong>프로세스마다 독립된 논리적 주소 공간을 제공하고, 필요한 데이터만 실제 물리 메모리(RAM)에 적재</strong></li>
</ul>
<p>[주소 공간 구조]</p>
<table>
<thead>
<tr>
<th>구성요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>가상 주소(Virtual Address)</strong></td>
<td>프로세스가 바라보는 주소</td>
</tr>
<tr>
<td><strong>물리 주소(Physical Address)</strong></td>
<td>실제 RAM 상의 주소</td>
</tr>
<tr>
<td><strong>MMU (Memory Management Unit)</strong></td>
<td>주소 변환을 수행하는 하드웨어 장치</td>
</tr>
<tr>
<td><strong>페이지 테이블(Page Table)</strong></td>
<td>가상 주소 → 물리 주소 매핑 정보 저장</td>
</tr>
</tbody></table>
<p> 프로세스는 OS로부터 <strong>가상 주소 공간(Virtual Address Space)</strong>을 할당받는데 </p>
<p>이 가상 주소는 실제 물리 주소로 직접 연결되지 않고,</p>
<p><strong>MMU(Memory Management Unit)</strong>가 페이지 테이블(Page Table)을 통해 변환(mapping)</p>
<h2 id="3-1-기본-동작-페이징-기법-paging">3-1. 기본 동작: 페이징 기법 (Paging)</h2>
<p>가상 메모리는 일반적으로 페이징 기법을 사용함</p>
<ul>
<li>가상 메모리와 물리 메모리를 <strong>같은 크기의 블록 단위</strong>로 나눔<ul>
<li>가상 메모리의 단위 → <strong>페이지(Page)</strong></li>
<li>물리 메모리의 단위 → <strong>프레임(Frame)</strong></li>
</ul>
</li>
<li>페이지와 프레임은 1:1로 매핑됨</li>
</ul>
<table>
<thead>
<tr>
<th>용어</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>페이지(Page)</td>
<td>가상 메모리의 고정 크기 블록 (예: 4KB)</td>
</tr>
<tr>
<td>프레임(Frame)</td>
<td>물리 메모리의 고정 크기 블록 (예: 4KB)</td>
</tr>
<tr>
<td>페이지 폴트(Page Fault)</td>
<td>접근하려는 페이지가 메모리에 없을 때 발생</td>
</tr>
<tr>
<td>스왑(Swap)</td>
<td>디스크에서 페이지를 불러오거나 내보내는 작업</td>
</tr>
<tr>
<td>1. 프로세스가 가상 주소에 접근</td>
<td></td>
</tr>
<tr>
<td>2. MMU가 페이지 테이블을 통해 해당 페이지가 물리 메모리에 있는지 확인</td>
<td></td>
</tr>
<tr>
<td>3. <strong>있으면 (Page Hit)</strong> → 바로 접근</td>
<td></td>
</tr>
<tr>
<td>4. <strong>없으면 (Page Fault)</strong> → 디스크에서 해당 페이지를 불러와 RAM의 빈 프레임에 적재</td>
<td></td>
</tr>
<tr>
<td>5. 페이지 교체가 필요할 경우 → <strong>페이지 교체 알고리즘</strong>으로 결정 (LRU, FIFO 등)</td>
<td></td>
</tr>
</tbody></table>
<p>⇒ 즉, OS는 필요할 때만 디스크에서 데이터를 메모리에 수요에 따라(Demand Paging)</p>
<hr>
<h1 id="4-페이징--세그멘테이션">4. 페이징 &amp; 세그멘테이션</h1>
<p>페이징과 세그멘테이션 모두 프로세스의 메모리 공간을 비연속적으로 물리 메모리에 할당하여 가상 메모리를 구현하는 기법</p>
<p>두 기법의 주요 차이점은 메모리를 나누는 단위와 기준에 있음</p>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>페이징 (Paging)</strong></th>
<th><strong>세그멘테이션 (Segmentation)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>분할 단위</strong></td>
<td><strong>고정 크기의 페이지(Page)</strong></td>
<td><strong>가변 크기의 세그먼트(Segment)</strong></td>
</tr>
<tr>
<td><strong>분할 기준</strong></td>
<td>물리적 주소 공간의 효율적 관리</td>
<td><strong>논리적 의미 단위</strong> (코드, 데이터, 힙, 스택, 함수 등)</td>
</tr>
<tr>
<td><strong>주소 구조</strong></td>
<td>&lt;페이지 번호, 오프셋&gt;</td>
<td>&lt;세그먼트 번호, 오프셋&gt;</td>
</tr>
<tr>
<td><strong>주체</strong></td>
<td><strong>운영체제와 하드웨어</strong>에 의해 투명하게 관리 (사용자에게 숨김)</td>
<td><strong>사용자/컴파일러</strong>의 논리적 구조를 반영 (사용자에게 노출)</td>
</tr>
<tr>
<td><strong>주요 단편화</strong></td>
<td><strong>내부 단편화</strong> (Internal Fragmentation)</td>
<td><strong>외부 단편화</strong> (External Fragmentation)</td>
</tr>
<tr>
<td><strong>메모리 공유/보호</strong></td>
<td>페이지 단위로 세밀한 보호 어려움</td>
<td><strong>논리적 단위</strong>이므로 공유 및 보호가 용이함</td>
</tr>
</tbody></table>
<h2 id="1-페이징-paging">1) 페이징 (Paging)</h2>
<p>메모리를 고정된 크기의 조각(Page/ Frame)으로 나누어 관리하여 외부 단편화를 완전히 제거함</p>
<p>대신 페이지 크기 때문에 발생하는 내부 단편화가 존재함</p>
<h3 id="장점-pros">장점 (Pros)</h3>
<ol>
<li><strong>외부 단편화 제거:</strong> 메모리가 고정된 크기의 <strong>프레임 단위로 관리</strong>되어 외부 단편화가 발생하지 않음</li>
<li><strong>단순한 메모리 관리:</strong> 할당/해제가 프레임 단위로 이루어져 <strong>빈 공간 관리</strong>가 단순함</li>
<li><strong>프로그래머 투명성:</strong> 주소 변환이 OS와 MMU에 의해 처리되어 <strong>프로그래머는 신경 쓸 필요가 없음</strong></li>
</ol>
<h3 id="단점-cons">단점 (Cons)</h3>
<ol>
<li><strong>내부 단편화 발생:</strong> 프로세스 크기가 페이지 크기의 정수배가 아닐 경우, 마지막 페이지에서 <strong>사용되지 않는 공간</strong>이 발생함</li>
<li><strong>페이지 테이블 오버헤드:</strong> 모든 프로세스가 <strong>페이지 테이블을 유지</strong>해야 하므로, 테이블 저장 공간 및 크기 관리 비용이 발생함</li>
<li><strong>느린 주소 변환:</strong> 물리 주소 변환을 위해 최소 <strong>두 번의 메모리 접근</strong>이 필요하여 속도가 느려짐 (TLB를 통해 완화됨)</li>
</ol>
<h2 id="2-세그멘테이션-segmentation">2) 세그멘테이션 (Segmentation)</h2>
<p>프로그램의 코드, 데이터, 스택 등 의미 있는 논리적 단위(Segment)로 나누어 관리함</p>
<p>사용자 관점의 메모리 구조를 반영하지만, 크기가 가변적이라 외부 단편화 문제가 발생할 수 있음</p>
<h3 id="장점-pros-1">장점 (Pros)</h3>
<ol>
<li><strong>내부 단편화 최소화/제거:</strong> 세그먼트 크기가 논리적 크기와 <strong>정확히 일치</strong>하여 내부 단편화가 발생하지 않음</li>
<li><strong>효율적인 보호 및 공유:</strong> 코드/데이터 등 <strong>논리적 단위</strong>로 분할되어 공유 및 접근 권한 설정이 용이함</li>
<li><strong>프로그래머 편의성:</strong> 프로그램 구조에 따라 메모리를 구성하므로 디버깅 및 관리가 <strong>직관적임</strong></li>
</ol>
<h3 id="단점-cons-1">단점 (Cons)</h3>
<ol>
<li><strong>외부 단편화 발생:</strong> 세그먼트 크기가 <strong>가변적</strong>이어서, 메모리 할당/해제 시 <strong>크기가 다른 빈 공간</strong>이 발생함</li>
<li><strong>복잡한 메모리 관리:</strong> 외부 단편화 해결을 위해 <strong>메모리 압축(Compaction)</strong> 등 복잡한 기법이 요구됨</li>
<li><strong>가변적인 크기 관리:</strong> 가변적인 세그먼트 크기에 맞춰 <strong>적합한 빈 공간</strong>을 찾는 최적화 과정이 복잡함</li>
</ol>
<h2 id="페이지-교체-알고리즘">페이지 교체 알고리즘</h2>
<p>프로세스가 접근하려는 페이지가 물리 메모리(RAM)에 없을 때 페이지 폴트(Page Fault) 발생</p>
<p>→ 해당 페이지를 디스크에서 물리 메모리로 가져와야 하는데</p>
<p>물리 메모리의 모든 프레임이 이미 사용중인 경우, OS는 현재 메모리에 있는 페이지 중 하나를 희생시켜 공간을 확보함 (Swap Out)</p>
<p>이 때 어떤 페이지를 희생시킬것인가를 결정하는 규칙이 페이지 교체 알고리즘</p>
<ul>
<li>목표: 페이지 폴트 발생 확률의 최소화
가장 오랫동안 사용되지 않을 것으로 예상되는 페이지를 교체 대상으로 선택해야 함</li>
</ul>
<h2 id="주요-알고리즘">주요 알고리즘</h2>
<p>페이지 교체 알고리즘은 대체로 지역성의 원리에 기반하여 작동함</p>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>개념</th>
</tr>
</thead>
<tbody><tr>
<td><strong>FIFO</strong></td>
<td>가장 오래된 페이지 제거</td>
</tr>
<tr>
<td><strong>LRU (Least Recently Used)</strong></td>
<td>가장 오랫동안 사용되지 않은 페이지 제거</td>
</tr>
<tr>
<td><strong>OPT (Optimal)</strong></td>
<td>앞으로 가장 늦게 참조될 페이지 제거 (이론적 최적)</td>
</tr>
<tr>
<td><strong>Clock</strong></td>
<td>LRU 근사 알고리즘 (효율성과 단순함의 절충)</td>
</tr>
</tbody></table>
<h3 id="1-opt-optimal-최적-알고리즘">1) OPT (Optimal, 최적) 알고리즘</h3>
<ul>
<li>앞으로 <strong>가장 오랫동안 사용되지 않을 페이지</strong>를 교체함</li>
<li>페이지 폴트율이 <strong>가장 낮음</strong>
다른 알고리즘 성능 평가의 기준(Benchmark)이 됨</li>
<li>[+] 이론적으로 <strong>최상의 성능</strong>을 보장함</li>
<li>[-] CPU가 미래 접근 순서를 미리 알 수 없어 실제 구현은 불가능함</li>
</ul>
<h3 id="2-fifo-first-in-first-out-선입선출">2) FIFO (First-In, First-Out, 선입선출)</h3>
<ul>
<li>물리 메모리에 <strong>가장 먼저 들어온(가장 오래된)</strong> 페이지를 교체함</li>
<li>구현이 <strong>매우 간단</strong>함 
페이지 유입 시간만 추적함</li>
<li>[+] 단순한 구현</li>
<li>[-] 오래되었어도 자주 사용되는 페이지를 교체할 수 있어 효율이 나쁨
’벨라디의 모순(Belady&#39;s Anomaly)&#39; 현상 발생 가능성이 있음</li>
</ul>
<h3 id="3-lru-least-recently-used-최소-최근-사용">3) LRU (Least Recently Used, 최소 최근 사용)</h3>
<ul>
<li><strong>가장 오랫동안 사용되지 않은</strong> 페이지를 교체함 (시간적 지역성 기반 예측)</li>
<li>OPT 다음으로 <strong>좋은 성능</strong>을 보임. 가장 이상적인 <strong>실용 알고리즘</strong>으로 평가됨</li>
<li>[+] 지역성의 원리를 잘 반영하여 <strong>페이지 폴트율이 낮음</strong></li>
<li>[-]모든 페이지의 최근 사용 시간 기록에 필요한 하드웨어적 오버헤드가 매우 큼</li>
</ul>
<h3 id="4-lfu-least-frequently-used-최소-빈도-사용">4) LFU (Least Frequently Used, 최소 빈도 사용)</h3>
<ul>
<li>물리 메모리 페이지 중 <strong>가장 적게 사용된 횟수</strong>를 가진 페이지를 교체함</li>
<li>페이지의 <strong>사용 빈도</strong>에 초점을 맞춤</li>
<li>[+] 오랫동안 메모리에 있었더라도 <strong>자주 사용된 페이지</strong>를 보호함</li>
<li>[-]  초기에 집중 사용 후 현재는 사용되지 않는 페이지가 계속 메모리에 남는 문제 발생 가능함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] Deadlock]]></title>
            <link>https://velog.io/@do_e/OS-Deadlock</link>
            <guid>https://velog.io/@do_e/OS-Deadlock</guid>
            <pubDate>Thu, 23 Oct 2025 16:17:55 GMT</pubDate>
            <description><![CDATA[<h1 id="1-교착-상태deadlock">1. 교착 상태(Deadlock)</h1>
<h2 id="1-1-교착-상태란">1-1. 교착 상태란?</h2>
<p>2개 이상의 스레드가 서로가 가지고 있는 자원을 기다리며 영원히 멈춰 버리는 상태 (순환 대기)</p>
<p>→ 자원 취득 순서 / 타임 아웃  / 검출 알고리즘 필요</p>
<ul>
<li>e.g. 식사하는 철학자 문제</li>
</ul>
<p>[교착 상태를 해결하기 위해서]</p>
<ol>
<li>교착 상태가 발생했을 때 상황을 표현</li>
<li>교착 상태가 일어나는 근본적 이유 이해</li>
</ol>
<h2 id="1-2-자원-할당-그래프">1-2. 자원 할당 그래프</h2>
<p>교착상태가 어떤 조건에서 발생하는지 파악할 수 있음</p>
<ul>
<li>어떤 프로세스가 어떤 자원을 할당 받아 사용 중인지 확인 가능</li>
<li>어떤 프로세스가 어떤 자원을 기다리고 있는지 확인 가능</li>
</ul>
<p><img src="attachment:9898f193-380b-4082-bae9-2a8cc323a0a9:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.07.15.png" alt="스크린샷 2025-10-16 15.07.15.png"></p>
<p><img src="attachment:358c577f-6177-49dc-acea-b4c032615ba8:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.08.07.png" alt="스크린샷 2025-10-16 15.08.07.png"></p>
<p><img src="attachment:1fdb3761-d30a-4241-8520-a0517c36af32:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.08.25.png" alt="스크린샷 2025-10-16 15.08.25.png"></p>
<p><img src="attachment:2262392b-c96c-4fe2-bf4e-6cd592882157:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.08.47.png" alt="스크린샷 2025-10-16 15.08.47.png"></p>
<p>c.f. 식사하는 철하자의 자원할당그래프</p>
<ul>
<li><p>교착 상태가 일어난 그래프는 자원할당그래프가 원의 형태를 띄고 있음</p>
<p>  <img src="attachment:7750f6d5-ffdb-4666-a00f-28210355754b:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.11.24.png" alt="스크린샷 2025-10-16 15.11.24.png"></p>
</li>
</ul>
<h2 id="1-3-교착-상태-발생-조건-coffman의-4가지-조건">1-3. 교착 상태 발생 조건 (Coffman의 4가지 조건)</h2>
<p>교착 상태는 아래 4가지 조건이 모두 동시에 만족될 때 발생함</p>
<ol>
<li><strong>상호 배제 (Mutual Exclusion)</strong>
자원은 한 번에 오직 하나의 프로세스만 사용할 수 있음</li>
<li><strong>점유와 대기(hold &amp; wait)</strong>
자원을 할당 받은 상태에서 다른 자원을 기다리고 있는 상태</li>
<li><strong>비선점 (No Preemption)</strong>
어떤 프로세스도 다른 프로세스의 자원을 강제로 빼앗을 수 없음</li>
<li><strong>원형 대기 (Circular Wait)</strong>
프로세스들이 원의 형태로 자원을 대기하는 형태</li>
</ol>
<h2 id="1-4-교착-상태-해결-방법">1-4. 교착 상태 해결 방법</h2>
<p>교착 상태를 해결하는 방법으로는 크게 3가지가 있음</p>
<p>: 예방, 회피, 검출 후 회복</p>
<h3 id="1-교착-상태-예방-prevention">1) 교착 상태 예방 (Prevention)</h3>
<p>애초에 교착 상태가 발생하지 않도록</p>
<p>교착 상태 조건(4가지) 중 하나를 없애기</p>
<ol>
<li><p>상호 배제 X → 모든 자원을 공유 가능하게 만들기
현실적으로 모든 자원을 공유하는 것은 불가능함</p>
</li>
<li><p>점유와 대기 X → 특정 프로세스에 자원을 모두 할당하거나, 아예 할당하지 않는 방식으로 배분
→ 자원의 활용률을 낮출 수 있는 방식</p>
</li>
<li><p>비선점 조건 X → 선점이 가능한 자원 (e.g. CPU)에 한해 효과적
→ 모든 자원이 선정 가능한 것은 아님</p>
</li>
<li><p>원형 대기 조건 X → 모든 자원에 번호를 붙이고, 오름차순으로 자원을 할당</p>
<p> <img src="attachment:05ed255d-c84f-4606-8274-f98af461bbf8:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.21.11.png" alt="스크린샷 2025-10-16 15.21.11.png"></p>
<p> 하지만 모든 자원에 번호를 붙이는 작업은 어려움
 어떤 자원에 어떤 번호를 부여하는지에 따라 활용률이 다름</p>
</li>
</ol>
<h3 id="2-교착-상태-회피-avoidance">2) 교착 상태 회피 (Avoidance)</h3>
<p>교착 상태를 무분별한 자원 할당으로 인해 발생했다고 간주함</p>
<p>교착 상태가 발생하지 않을만큼 조심하여 할당함</p>
<p>배분할 수 있는 자원의 양을 고려하여 교착 상태가 발생하지 않을 만큼만 자원을 배분함</p>
<ul>
<li>안전 순서열: 교착 상태 없이 안전하게 프로세스들에게 자원을 할당할 수 있는 순서
e.g. A-B-C: 교착 상태 , A-C-A: 교착 상태 X → 안전 순서열!</li>
<li>안전 상태: 교착 상태 없이 모든 프로세스가 자원을 할당 받고 종료될 수 있는 상태
→ 안전 순서열이 있는 상태</li>
<li>불안전 상태: 교착 상태가 발생할 수도 있는 상태
→ 안전 순서열이 없는 상태</li>
<li>교착 상태 회피는 안전 상태에서 안전 상태로 움직이는 경우에만 자원을 할당하는 방식
항시 안전 상태를 유지하도록 자원을 할당함
e.g. 은행원 알고리즘</li>
</ul>
<p><img src="attachment:01945fce-5053-470b-b280-70b59c5060b1:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.27.16.png" alt="스크린샷 2025-10-16 15.27.16.png"></p>
<p><img src="attachment:c8233527-99aa-43bf-aeab-4d1ae3f60882:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.29.23.png" alt="스크린샷 2025-10-16 15.29.23.png"></p>
<p><img src="attachment:ae56fb67-4843-49f8-aab9-9fee868443c0:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.30.29.png" alt="스크린샷 2025-10-16 15.30.29.png"></p>
<p><img src="attachment:4798e573-604b-49cf-be3d-981adca696ed:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.31.50.png" alt="스크린샷 2025-10-16 15.31.50.png"></p>
<p><img src="attachment:f7ce423a-d0d3-474d-b4b3-48eb6855ba47:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.32.32.png" alt="스크린샷 2025-10-16 15.32.32.png"></p>
<p><img src="attachment:801b6b55-b6d9-496a-8e37-242775676aca:%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-10-16_15.33.25.png" alt="스크린샷 2025-10-16 15.33.25.png"></p>
<h3 id="3-교착-상태-검출-후-회복-detection--recovery">3) 교착 상태 검출 후 회복 (Detection &amp; Recovery)</h3>
<p>교착 상태의 발생을 인정하고 사후에 조치하는 방식</p>
<p>프로세스가 자원을 요구하면 일단 할당, 교착 상태가 검출되면 회복</p>
<ul>
<li>선점을 통한 회복
교착 상태가 해결될 때까지 한 프로세스씩 자원을 몰아주는 방식</li>
<li>프로세스 강제 종료를 통한 회복
교착 상태에 놓인 프로세스 모두 강제 종료 (작업 내역을 잃을 위험)
교착 상태가 해결될 때까지 한 프로세스씩 강제 종료 (오버헤드)</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>