<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>heon.blog</title>
        <link>https://velog.io/</link>
        <description>유익한 글을 목표로 하는 공간입니다.</description>
        <lastBuildDate>Wed, 13 Aug 2025 11:28:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>heon.blog</title>
            <url>https://velog.velcdn.com/images/heon-blog/profile/49bedb66-1815-4ca9-8b9d-1c0abbed18d6/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. heon.blog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/heon-blog" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[네트워크] TCP / UDP의 차이와 전송 방식 이해하기]]></title>
            <link>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-TCP-vs-UDP</link>
            <guid>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-TCP-vs-UDP</guid>
            <pubDate>Wed, 13 Aug 2025 11:28:24 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>IP는 <strong>신뢰할 수 없는 통신</strong>과 <strong>비연결형 통신</strong>이라는 한계가 있습니다.<br>전송 계층은 이러한 IP의 한계를 극복하여 <strong>신뢰성 있는 통신</strong>과 <strong>연결형 통신</strong>을 제공합니다.  </p>
<p>이러한 전송 계층 프로토콜로는 <strong>TCP</strong>와 <strong>UDP</strong>가 있습니다.</p>
<blockquote>
<p>이번 포스팅에서는 <strong>TCP와 UDP의 차이</strong>를 알아보고,  연결형 통신 프로토콜인 <strong>TCP에서의 연결 수립 과정(3-way Handshake)</strong>에 대해 살펴보겠습니다.</p>
</blockquote>
<hr>
<h2 id="2-tcp와-udp-차이">2. TCP와 UDP 차이</h2>
<p>TCP와 UDP는 모두 전송 계층에서 데이터를 보내기 위한 프로토콜이지만, 성격이 크게 다릅니다.</p>
<table>
<thead>
<tr>
<th>특징</th>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody><tr>
<td>연결 방식</td>
<td>연결형 통신</td>
<td>비연결형 통신</td>
</tr>
<tr>
<td>신뢰성</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>순서 보장</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>오류 제어</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>속도</td>
<td>느림</td>
<td>빠름</td>
</tr>
<tr>
<td>용도</td>
<td>웹, 이메일, 파일 전송</td>
<td>스트리밍, 게임, 실시간 통신</td>
</tr>
</tbody></table>
<p>먼저 <strong>TCP</strong>는 <strong>연결형 통신</strong>입니다. 데이터를 보내기 전에 먼저 상대와 연결을 수립하고, 그 연결을 통해 데이터를 전송합니다.<br>이 과정 덕분에 TCP는 데이터가 <strong>순서대로 도착하는지</strong>, <strong>손실된 데이터는 없는지</strong>를 확인할 수 있으며, <strong>신뢰성을 제공하는 프로토콜</strong>이라고 할 수 있습니다.<br>또한, 너무 빨리 보내서 수신 측이 처리하지 못하면 속도를 조절하는 <strong>흐름 제어</strong>와, 네트워크가 혼잡할 때 전송 속도를 조절하는 <strong>혼잡 제어</strong> 기능도 제공합니다.<br>그래서 웹 브라우징, 이메일, 파일 전송처럼 <strong>정확한 데이터 전달이 중요한 서비스</strong>에서 주로 사용됩니다.</p>
<p>반면 <strong>UDP</strong>는 <strong>비연결형 통신</strong>이며, <strong>신뢰성이 없는 프로토콜</strong>입니다. 데이터를 보내기 전에 별도의 연결을 만들지 않고, 바로 데이터를 전송합니다.<br>이 때문에 UDP는 데이터가 순서대로 도착하는지 확인하지 않으며, 일부 데이터가 손실될 수도 있습니다. 
하지만 연결을 만들고 확인하는 과정이 없으므로 <strong>TCP보다 속도가 빠르고 오버헤드가 적습니다</strong>.  
그래서 실시간 스트리밍, 온라인 게임, 화상 통화처럼 <strong>속도가 중요하고 일부 데이터 손실이 허용되는 서비스</strong>에서 주로 사용됩니다.</p>
<hr>
<h2 id="3-tcp와-udp-전송-방식">3. TCP와 UDP 전송 방식</h2>
<p>TCP와 UDP는 데이터 전송 방식에서 큰 차이를 보입니다.</p>
<ul>
<li><p><strong>TCP</strong>는 연결형 통신이므로, 데이터를 보내기 전에 먼저 상대와 <strong>연결을 수립</strong>합니다.<br>연결이 수립되면 데이터를 순서대로 전송하며, <strong>손실된 데이터는 재전송</strong>하고, <strong>순서를 보장</strong>합니다.<br>데이터를 모두 주고받은 뒤에는 안전하게 <strong>연결을 종료</strong>합니다.<br>정리하면 TCP의 전송 과정은 다음과 같습니다.</p>
<ol>
<li>연결 수립 (<strong>3-Way Handshake</strong>)  </li>
<li>데이터 전송  </li>
<li>연결 종료 (<strong>4-Way Handshake</strong>)  </li>
</ol>
</li>
<li><p><strong>UDP</strong>는 비연결형 통신이므로, 데이터를 보내기 위해 연결을 만들 필요가 없습니다.<br>데이터를 연결 없이 보내며, 순서나 손실 여부를 확인하지 않습니다.<br>따라서 전송 속도가 빠르고 오버헤드가 적지만, 신뢰성은 보장되지 않습니다.</p>
</li>
</ul>
<blockquote>
<p>TCP는 <strong>신뢰성과 순서 보장</strong>, UDP는 <strong>빠른 전송과 낮은 오버헤드</strong>가 특징입니다.</p>
</blockquote>
<hr>
<h2 id="4-3-way-handshake">4. 3-Way Handshake</h2>
<p><strong>3-Way Handshake</strong>는 TCP가 통신을 시작할 때 사용하는 <strong>연결 수립 과정</strong>입니다.<br>송신 측과 수신 측이 서로 통신할 준비가 되었는지를 확인하고, <strong>양방향으로 동기화(Synchronize)</strong>를 수행합니다.  </p>
<p>과정은 총 세 단계로 이루어집니다.</p>
<ol>
<li><p><strong>SYN (Synchronize)</strong>  </p>
<ul>
<li>클라이언트가 서버에게 <strong>연결 요청(SYN)</strong>을 보냅니다. </li>
<li>이때 &quot;내가 보낸 데이터의 순서를 x번부터 시작하겠다&quot;라는 식으로 <strong>시퀀스 번호(Sequence Number)</strong>를 함께 보냅니다.</li>
</ul>
</li>
<li><p><strong>SYN + ACK (Synchronize + Acknowledge)</strong>  </p>
<ul>
<li>서버가 요청을 수락하며 클라이언트에게 다음 기대하는 번호는 x+1번이라고 <strong>응답(ACK)</strong> 합니다.   </li>
<li>동시에 <strong>서버 자신의 시퀀스 번호도 설정(SYN)</strong>하여 보냅니다.  </li>
<li>즉, &quot;좋아! 난 네 요청을 받았고, 내 데이터는 y번부터 시작할게&quot;라는 뜻입니다.</li>
</ul>
</li>
<li><p><strong>ACK (Acknowledge)</strong>  </p>
<ul>
<li>클라이언트가 요청을 수락하며 서버에게 다음 기대하는 번호는 y번이라고 <strong>응답(ACK)</strong> 합니다.  </li>
<li>이제 양쪽 모두 통신을 시작할 준비가 완료됩니다.</li>
</ul>
</li>
</ol>
<blockquote>
<p>3번의 메시지 교환을 통해 <strong>양측이 연결 의사를 확인하고, 시퀀스 번호를 동기화하여 데이터 전송 준비가 완료됨을 보장하는 과정</strong>이 바로 <strong>3-Way Handshake</strong>입니다.
이를 통해 TCP는 <strong>신뢰성 있는 연결</strong>을 제공합니다.</p>
</blockquote>
<hr>
<h2 id="5-4-way-handshake">5. 4-Way Handshake</h2>
<p><strong>4-Way Handshake</strong>는 <strong>TCP 연결을 종료</strong>할 때 사용되는 과정입니다.<br>통신을 끝내려는 쪽과 이를 확인하는 쪽이 서로 <strong>연결을 안전하게 닫기 위해 4번의 메시지를 주고받는 절차</strong>입니다.</p>
<p>과정은 총 네 단계로 이루어집니다.</p>
<ol>
<li><p><strong>FIN (Finish)</strong>  </p>
<ul>
<li>클라이언트가 서버에게 <strong>연결을 종료하겠다(FIN)</strong>는 요청을 보냅니다.  </li>
<li>이때 클라이언트는 더 이상 보낼 데이터가 없음을 알립니다.</li>
</ul>
</li>
<li><p><strong>ACK (Acknowledge)</strong>  </p>
<ul>
<li>서버가 FIN을 받고, <strong>알겠다(ACK)</strong>는 응답을 보냅니다.  </li>
<li>하지만 서버는 아직 보낼 데이터가 남아 있을 수도 있으므로 바로 연결을 닫지는 않습니다.</li>
</ul>
</li>
<li><p><strong>FIN (Finish)</strong>  </p>
<ul>
<li>서버가 자신도 더 이상 보낼 데이터가 없을 때, <strong>연결 종료 요청(FIN)</strong>을 클라이언트에게 보냅니다.</li>
</ul>
</li>
<li><p><strong>ACK (Acknowledge)</strong>  </p>
<ul>
<li>클라이언트가 FIN을 받고, <strong>알겠다(ACK)</strong>는 응답을 보냅니다.  </li>
<li>이제 양측 모두 데이터를 주고받을 필요가 없으므로 <strong>연결이 완전히 종료됩니다</strong>.</li>
</ul>
</li>
</ol>
<blockquote>
<p>4번의 메시지 교환을 통해 <strong>양측이 안전하게 데이터를 모두 전송했음을 보장하고, 연결을 정상적으로 종료하는 과정</strong>이 바로 <strong>4-Way Handshake</strong>입니다.</p>
</blockquote>
<hr>
<h2 id="6-tcp의-신뢰성과-전송-제어">6. TCP의 신뢰성과 전송 제어</h2>
<p>TCP는 데이터를 안정적이고 정확하게 전달하기 위해 여러 가지 기능들을 제공합니다.<br>주요 기능으로는 <strong>재전송을 통한 오류 제어, 순서 보장, 흐름 제어, 혼잡 제어</strong>가 있습니다.</p>
<h3 id="1-재전송을-통한-오류-제어">1. 재전송을 통한 오류 제어</h3>
<ul>
<li>데이터를 보낸 후 ACK를 받지 못하면 <strong>타임아웃 재전송</strong>을 수행합니다.  </li>
<li>중복 ACK를 여러 번 받으면 <strong>빠른 재전송(Fast Retransmit)</strong>으로 즉시 데이터를 다시 보냅니다.  </li>
<li>이 과정을 통해 <strong>손실된 데이터 없이 정확하게 전송</strong>할 수 있습니다.</li>
</ul>
<h3 id="2-순서-보장">2. 순서 보장</h3>
<ul>
<li>패킷이 순서대로 도착하지 않아도 TCP가 순서를 재조립하여 애플리케이션에 올바른 순서로 전달합니다.  </li>
<li><strong>체크섬</strong>을 사용해 데이터 손상 여부를 확인하고, 필요하면 재전송합니다.</li>
</ul>
<h3 id="3-흐름-제어-sliding-window">3. 흐름 제어 (Sliding Window)</h3>
<ul>
<li>수신 측의 처리 능력을 고려하여 송신 속도를 조절합니다.  </li>
<li><strong>슬라이딩 윈도우</strong> 방식을 사용하여, 수신 측이 처리 가능한 데이터 양(윈도우 크기)을 송신 측에 알려주고,<br>송신 측은 해당 범위 내에서 데이터를 전송하며, ACK를 받으면 창(Window)을 앞으로 이동시켜 다음 데이터를 보냅니다.</li>
</ul>
<h3 id="4-혼잡-제어">4. 혼잡 제어</h3>
<ul>
<li>네트워크가 혼잡할 때 전송 속도를 조절하여 패킷 손실을 줄이고 효율적으로 데이터를 전달합니다.  </li>
<li>대표적인 알고리즘으로는 Slow Start, Congestion Avoidance, Fast Retransmit, Fast Recovery 등이 있습니다.</li>
</ul>
<blockquote>
<p>TCP는 이러한 기능 덕분에 <strong>데이터 손실 없이 안정적인 통신</strong>을 보장합니다.</p>
</blockquote>
<hr>
<h2 id="7-마무리">7. 마무리</h2>
<p>이번 포스팅에서는 <strong>TCP와 UDP의 차이</strong>와, TCP에서 데이터를 안정적으로 전송하기 위한 <strong>연결 수립과 종료 과정(3-Way/4-Way Handshake)</strong>, 그리고 <strong>TCP의 전송 제어 기능</strong>에 대해 살펴보았습니다.</p>
<ul>
<li><strong>TCP</strong>는 연결형 통신으로, <strong>신뢰성, 순서 보장, 흐름 제어, 혼잡 제어</strong> 기능을 통해 데이터를 안전하게 전달합니다.</li>
<li><strong>UDP</strong>는 비연결형 통신으로, <strong>빠른 전송과 낮은 오버헤드</strong>를 제공하지만 신뢰성은 보장되지 않습니다.</li>
</ul>
<blockquote>
<p>이번 포스팅에서 배운 내용을 통해 <strong>네트워크 전송 방식 (TCP/UDP)</strong>에 대한 개념을 명확히 구분할 수 있을 것입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네트워크] ARP와 이더넷 프레임 - 패킷이 목적지까지 가는 과정]]></title>
            <link>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-ARP%EC%99%80-%EC%9D%B4%EB%8D%94%EB%84%B7-%ED%94%84%EB%A0%88%EC%9E%84-%ED%98%B8%EC%8A%A4%ED%8A%B8%EC%97%90%EC%84%9C-%EB%AA%A9%EC%A0%81%EC%A7%80%EA%B9%8C%EC%A7%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-ARP%EC%99%80-%EC%9D%B4%EB%8D%94%EB%84%B7-%ED%94%84%EB%A0%88%EC%9E%84-%ED%98%B8%EC%8A%A4%ED%8A%B8%EC%97%90%EC%84%9C-%EB%AA%A9%EC%A0%81%EC%A7%80%EA%B9%8C%EC%A7%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Wed, 13 Aug 2025 08:51:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>네트워크를 공부하다 보면 흔히 <strong>&quot;IP 주소만 있으면 패킷을 보낼 수 있다&quot;</strong>라고 생각하기 쉽습니다.<br>하지만 현실은 그렇게 간단하지 않습니다.<br>실제로 패킷이 이더넷을 통해 전달되려면 <strong>MAC 주소가 반드시 필요합니다.</strong><br>이때 <strong>IP → MAC 주소 변환을 담당하는 프로토콜이 바로 ARP (Address Resolution Protocol)</strong> 입니다.</p>
<p>이번 글에서는 <strong>ARP 요청·응답 과정</strong>을 중심으로  </p>
<ol>
<li><p><strong>같은 네트워크 내에서 이더넷 프레임이 호스트 → 스위치 → 라우터로 전달되는 과정</strong></p>
</li>
<li><p><strong>서로 다른 네트워크로 패킷이 이동할 때 라우터를 거치는 과정</strong></p>
</li>
</ol>
<p>을 살펴보겠습니다. </p>
<hr>
<h2 id="2-패킷과-이더넷-프레임">2. 패킷과 이더넷 프레임</h2>
<blockquote>
<p>패킷과 이더넷 프레임, 뭐가 다른걸까?</p>
</blockquote>
<p>네트워크를 이해하려면 <strong>&quot;패킷&quot;과 &quot;프레임&quot;의 차이</strong>를 먼저 짚어야 합니다.</p>
<ul>
<li><p><strong>패킷(Packet)</strong>  </p>
<ul>
<li>네트워크 계층(L3)에서 다루는 데이터 단위  </li>
<li><strong>IP 헤더 + 데이터(payload)</strong> 로 구성  </li>
<li>목적지 IP 주소, 출발지 IP 주소가 들어있음  </li>
<li>&quot;논리적 주소&quot; → 어디로 가야 할지를 알려줌  </li>
</ul>
</li>
<li><p><strong>이더넷 프레임(Ethernet Frame)</strong>  </p>
<ul>
<li>데이터 링크 계층(L2)에서 다루는 데이터 단위  </li>
<li><strong>MAC 헤더 + 패킷 + FCS</strong>로 구성  </li>
<li>목적지 MAC 주소, 출발지 MAC 주소가 들어있음  </li>
<li>실제 케이블이나 무선 매체를 통해 <strong>물리적으로 이동하는 단위</strong>  </li>
</ul>
</li>
</ul>
<p><strong>쉽게 말해</strong><br>패킷은 &quot;택배 상자 안에 담긴 내용물&quot;,
이더넷 프레임은 &quot;송신지와 목적지의 물리 주소가 적힌 택배 상자&quot;라고 볼 수 있습니다.  </p>
<hr>
<h2 id="3-패킷은-ip-주소만으로-이동할-수-있을까">3. 패킷은 IP 주소만으로 이동할 수 있을까?</h2>
<blockquote>
<p>패킷은 목적지 IP 주소만으로 이동할 수 없다.</p>
</blockquote>
<p>IP 주소는 논리적인 위치 정보일 뿐입니다.
택배 상자에 목적지가 적혀있지 않다면 이동할 수 없겠죠?
따라서 실제 이더넷 프레임을 전송하려면 <strong>목적지 MAC 주소가 필요합니다.</strong></p>
<ul>
<li><p>호스트 A(192.168.1.10)가 호스트 B(192.168.1.20)에게 패킷을 보내려면,<br>먼저 <strong>B의 MAC 주소를 알아야 합니다.</strong></p>
</li>
<li><p>만약 B의 MAC 주소를 모르면? → <strong>ARP 요청이 필요합니다.</strong></p>
</li>
</ul>
<hr>
<h2 id="4-arp-요청">4. ARP 요청</h2>
<p>호스트가 목적지의 MAC 주소를 알아내기 위해 ARP 요청을 브로드캐스트로 보냅니다. 
여기서 브로드캐스트란 <strong>&quot;같은 네트워크&quot;에 있는 모든 장비에게 데이터를 전달하는 것</strong>을 말합니다.</p>
<blockquote>
<p>&quot;IP 주소가 192.168.1.20인 장치 있니? 있다면 네 MAC 주소 좀 알려줘!&quot;</p>
</blockquote>
<p>스위치가 호스트로부터 브로드캐스트 프레임을 받으면 이를 <strong>모든 포트로 전달</strong>합니다. 
이 과정에서 스위치는 <strong>프레임의 출발지 MAC 주소를 스위치 테이블에 학습</strong>합니다.</p>
<p><strong>&lt;스위치 테이블&gt;</strong></p>
<table>
<thead>
<tr>
<th>MAC 주소</th>
<th>포트</th>
</tr>
</thead>
<tbody><tr>
<td>AA-AA-AA-AA-AA-AA</td>
<td>1</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-arp-응답">5. ARP 응답</h2>
<p>목적지 호스트 B는 자신의 IP가 요청에 있는 것을 확인하면 호스트 A로 <strong>유니캐스트 ARP 응답</strong>을 보냅니다. 
여기서 유니캐스트란 <strong>특정 단말 1대로 데이터를 전송하는 것</strong>을 말합니다.</p>
<blockquote>
<p>&quot;나는 192.168.1.20이고, 내 MAC 주소는 BB-BB-BB-BB-BB-BB야!&quot;</p>
</blockquote>
<p>스위치는 이 ARP 응답을 받으면서 <strong>B의 MAC 주소와 연결된 포트를 학습</strong>하여 테이블에 추가합니다.</p>
<p><strong>&lt;스위치 테이블&gt;</strong></p>
<table>
<thead>
<tr>
<th>MAC 주소</th>
<th>포트</th>
</tr>
</thead>
<tbody><tr>
<td>AA-AA-AA-AA-AA-AA</td>
<td>1</td>
</tr>
<tr>
<td>BB-BB-BB-BB-BB-BB</td>
<td>2</td>
</tr>
</tbody></table>
<p>호스트가 목적지의 MAC 주소가 적힌 이더넷 프레임을 스위치로 보내면, 스위치는 학습한 스위치 테이블의 정보를 통해 목적지 MAC 주소에 해당하는 포트로 프레임을 전달하는 역할을 합니다.</p>
<hr>
<h2 id="6-다른-네트워크로의-이동은-어떻게-하는걸까">6. 다른 네트워크로의 이동은 어떻게 하는걸까?</h2>
<p>호스트 A(192.168.1.10)가 <strong>다른 네트워크(예: 192.168.2.20)</strong>의 호스트 B로 데이터를 보내려 한다면,  호스트 A는 <strong>B의 MAC 주소를 직접 알 수 없습니다.</strong> 
이때, <strong>다른 네트워크로 데이터를 전달하는 최적 경로를 결정하는 역할은 라우터</strong>가 담당합니다.<br>따라서 호스트 A는 <strong>라우터(기본 게이트웨이)의 MAC 주소를 목적지로 하는 이더넷 프레임</strong>에 패킷을 담아 전송합니다.</p>
<p>라우터는 받은 이더넷 프레임 속 패킷을 확인하고, 목적지 IP를 기준으로 <strong>다음 네트워크로의 최적 경로를 라우팅 테이블에서 결정</strong>합니다.<br>그 후 라우터는 <strong>다음 네트워크의 목적지 MAC 주소를 ARP로 확인</strong>하고, 해당 MAC 주소를 이용해 <strong>새로운 이더넷 프레임에 패킷을 담아 전송</strong>합니다.
이 과정은 패킷이 목적지 네트워크에 도착할 때까지 <strong>각 라우터(한 홉)를 거칠 때마다 반복</strong>됩니다.<br>즉, 패킷은 각 네트워크마다 <strong>다음 홉 라우터의 MAC 주소를 확인하고 새로운 이더넷 프레임에 담겨 전송</strong>되는 과정을 거치면서 목적지에 도달하게 됩니다.</p>
<hr>
<h2 id="7-핵심-요약">7. 핵심 요약</h2>
<ol>
<li><p><strong>패킷과 이더넷 프레임 구분</strong>  </p>
<ul>
<li>패킷: 논리적 주소(IP) 포함, “택배 상자 안의 내용물”  </li>
<li>이더넷 프레임: 물리적 주소(MAC) 포함, “송신지·목적지 표시 택배 상자”  </li>
</ul>
</li>
<li><p><strong>같은 네트워크 내에서의 프레임 전달</strong>  </p>
<ul>
<li>목적지 MAC 주소를 모르면 <strong>ARP 요청(브로드캐스트)</strong>  </li>
<li>목적지 호스트가 <strong>ARP 응답(유니캐스트)</strong>  </li>
<li>스위치는 <strong>출발지/목적지 MAC 주소를 스위치 테이블에 학습</strong>하여 전달  </li>
</ul>
</li>
<li><p><strong>다른 네트워크로의 프레임 전달</strong>  </p>
<ul>
<li>호스트는 <strong>목적지 MAC을 직접 알 수 없으므로 라우터 MAC으로 프레임 전송</strong>  </li>
<li>라우터는 <strong>패킷의 목적지 IP → 라우팅 테이블 → 다음 홉 결정 → ARP로 다음 홉 MAC 확인 → 새 프레임 전송</strong>  </li>
<li><strong>각 홉마다 반복</strong>하여 패킷이 최종 목적지에 도달  </li>
</ul>
</li>
</ol>
<blockquote>
<p><strong>핵심 요약:</strong> 
호스트는 목적지 IP를 확인한 후 ARP로 목적지 MAC 주소를 알아내고,
<strong>패킷을 MAC 주소가 포함된 이더넷 프레임에 담아 네트워크로 전송</strong>합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네크워크] 네트워크 기본 구조와 핵심 개념 (2)]]></title>
            <link>https://velog.io/@heon-blog/%EB%84%A4%ED%81%AC%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-2</link>
            <guid>https://velog.io/@heon-blog/%EB%84%A4%ED%81%AC%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-2</guid>
            <pubDate>Mon, 23 Jun 2025 13:33:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>이전 글에서는 네트워크의 기본 구조와 구성 요소, 네트워크의 범위에 따른 분류, 그리고 메시지 교환 및 전송 방식에 대해 알아보았습니다.</p>
<blockquote>
<p>이번 포스팅에서는 네트워크의 작동 원리를 이해하기 위해 필요한 핵심 개념인 프로토콜, 네트워크 참조 모델, 캡슐화, PDU, 그리고 트래픽과 네트워크 성능 지표에 대해 살펴봅시다.</p>
</blockquote>
<h2 id="2-프로토콜">2. 프로토콜</h2>
<p>프로토콜은 네트워크에 연결된 <strong>노드 간에 정보를 정확하게 주고받기 위해 서로 합의한 규칙이나 절차</strong>를 의미합니다. 서로 다른 통신 장치들이 원활하게 정보를 주고받기 위해서는 동일한 프로토콜을 따라야 하며, 우리가 인터넷을 이용하거나 파일을 전송할 수 있는 것도 상대 호스트와 같은 프로토콜을 사용하기 때문에 가능한 일입니다.</p>
<ul>
<li><p>IP는 패킷을 수신지까지 전달하기 위해 사용되는 프로토콜입니다.</p>
</li>
<li><p>ARP는 192.168.1.1과 같은 형태의 <code>IP 주소</code>를 A1:B2:C3:D4:E5:F6과 같은 형태의 <code>MAC 주소</code>로 대응하기 위해 사용되는 프로토콜입니다.</p>
</li>
<li><p>HTTPS는 HTTP와 비교해서 보안상 더 안전한 프로토콜입니다.</p>
</li>
<li><p>TCP는 UDP에 비해 일반적으로 느리지만 신뢰성이 높은 프로토콜입니다.</p>
</li>
</ul>
<p>위에서의 프로토콜 예시를 통해 알 수 있는 것은 저마다의 목적과 특징이 있다는 것입니다. 
각 프로토콜의 특징을 잘 이해하면, 네트워크가 어떻게 동작하는지 훨씬 더 쉽게 파악할 수 있습니다.</p>
<h2 id="3-네트워크-참조-모델">3. 네트워크 참조 모델</h2>
<p>통신이 일어나는 각 과정을 계층으로 나눈 구조를 네트워크 참조 모델이라고 합니다.
통신 과정을 계층으로 나눈 이유로는 크게 <code>두 가지</code>가 있습니다.</p>
<blockquote>
<p>첫 번째로는, 네트워크 구성과 설계에 용이합니다.</p>
</blockquote>
<p>각 계층이 수행해야 할 역할이 정해져 있으므로, 계층의 목적에 맞게 프로토콜과 네트워크 장비를 계층별로 구성할 수 있습니다. 
물론 모든 프로토콜과 네트워크 장비가 참조 모델에 완벽하게 부합하는 것은 아니지만,
<strong>네트워크를 구성하고 설계할 때 중요한 가이드라인</strong>으로 <strong>네트워크 참조 모델이 활용</strong>됩니다.</p>
<blockquote>
<p>두 번째로는, 네트워크 문제 진단과 해결에 용이합니다.</p>
</blockquote>
<p>통신 과정에서 문제가 발생했을 때, 네트워크를 계층별로 나누어 점검하면 어느 단계에서 문제가 발생했는지 파악하기 쉬워집니다. 이러한 점에서 <strong>네트워크 참조 모델은 문제의 원인을 체계적으로 진단하고 해결</strong>하는 데에 도움이 됩니다.</p>
<p>다음으로 대표적인 네트워크 참조 모델인 OSI 모델과 TCP/IP 모델에 대해서 알아보겠습니다.</p>
<h3 id="1-osi-모델">(1) OSI 모델</h3>
<p>OSI 모델은 통신 과정을 <code>7개의 계층</code>으로 나눕니다.
하위 계층부터 순서대로 물리 계층, 데이터 링크 계층, 네트워크 계층, 전송 계층, 세션 계층, 표현 계층, 응용 계층으로 구성되어 있습니다.</p>
<ul>
<li><p>1) <code>물리 계층 (Physical Layer)</code>
1과 0으로 표현되는 비트 신호를 주고 받는 계층입니다.
비트 데이터를 통신 매체에 적합한 신호(전기, 빛, 전파 등)로 변환하여 전송합니다.</p>
</li>
<li><p>2) <code>데이터 링크 계층 (Data Link Layer)</code>
물리 계층을 통해 주고 받는 데이터에 오류가 없는지 확인합니다.
또한, MAC 주소를 사용하여 같은 네트워크 내에서 송신자와 수신자를 식별할 수 있습니다. 
전송 중 충돌이 발생할 수 있는 환경에서는 충돌을 감지하거나 회피하여 데이터가 안정적으로 전달되도록 합니다.</p>
</li>
<li><p>3) <code>네트워크 계층 (Network Layer)</code>
메시지를 다른 네트워크에 속한 수신지까지 전달하는 계층입니다.
IP 주소라는 주소 체계를 통해 통신하고자 하는 수신지 호스트와 네트워크를 식별하고, 원하는 수신지에 도달하기 위한 최적의 경로를 결정합니다.</p>
</li>
<li><p>4) <code>전송 계층 (Transport Layer)</code>
신뢰성 있고 안정적인 데이터 전송을 보장하는 계층입니다.
데이터의 흐름을 조절하고, 전송 중 발생할 수 있는 오류를 검출 및 복구하여 데이터가 정확하게 전달되도록 합니다.
또한, 포트 번호를 사용해 여러 응용 프로그램을 구분하고 식별함으로써, 올바른 프로그램에 데이터가 전달되도록 합니다.</p>
</li>
<li><p>5) <code>세션 계층 (Session Layer)</code>
통신하는 두 호스트 간의 연결(세션)을 관리하는 계층입니다.
세션을 생성, 유지, 종료하는 역할을 하여 통신이 원활하게 이루어지도록 돕습니다.</p>
</li>
<li><p>6) <code>표현 계층 (Presentation Layer)</code>
서로 다른 시스템이 이해할 수 있도록 데이터 형식을 변환하는 계층입니다.
예를 들어, 문자 인코딩(UTF-8, ASCII) 변환, 데이터 압축, 암호화와 복호화 등을 수행하여 데이터가 올바르게 전달되고 해석될 수 있도록 돕습니다.</p>
</li>
<li><p>7) <code>응용 계층 (Application Layer)</code>
사용자가 이용할 응용 프로그램에 다양한 네트워크 서비스를 제공합니다.
예를 들어, 웹 브라우저가 웹 페이지를 요청하는 HTTP 서비스, 이메일 송수신을 위한 SMTP 서비스, 파일 전송을 위한 FTP 서비스 등이 여기에 해당합니다.</p>
</li>
</ul>
<h3 id="2-tcpip-모델">(2) TCP/IP 모델</h3>
<p>OSI 모델은 네트워크를 이론적으로 기술하고 이해할 때 사용하는 반면에
TCP/IP 모델은 이론보다는 <strong>실제 구현에 중점</strong>을 둔 네트워크 참조 모델입니다. 
하위 계층부터 순서대로 네트워크 액세스 계층, 인터넷 계층, 전송 계층, 응용 계층으로 구성되어 있습니다.</p>
<ul>
<li><p>1) <code>네트워크 액세스 계층 (Network Access Layer)</code>
OSI 모델의 데이터 링크 계층과 물리 계층 역할을 포함하며, 
실제 네트워크 하드웨어와 데이터 전송을 담당합니다.</p>
</li>
<li><p>2) <code>인터넷 계층 (Internet Layer)</code>
OSI 모델의 네트워크 계층과 유사하며, 
IP 주소를 기반으로 한 패킷 전달과 라우팅을 담당합니다.</p>
</li>
<li><p>3) <code>전송 계층 (Transport Layer)</code>
OSI 모델의 전송 계층과 유사하며, 
신뢰성 있는 데이터 전송과 흐름 제어, 오류 제어를 담당합니다.</p>
</li>
<li><p>4) <code>응용 계층 (Application Layer)</code>
OSI 모델의 세션 계층, 표현 계층, 응용 계층의 기능을 통합한 계층으로, 
사용자 응용프로그램에 다양한 네트워크 서비스를 제공합니다.</p>
</li>
</ul>
<h2 id="4-캡슐화">4. 캡슐화</h2>
<blockquote>
<p>앞에서 학습한 프로토콜과 네트워크 참조 모델을 토대로 실제로 패킷이 어떻게 송수신되는지 알아봅시다.</p>
</blockquote>
<p>패킷은 송신 과정에서 캡슐화가 이루어지고, 수신 과정에서 역캡슐화가 이루어집니다.
송신지 입장에서는 <strong>상위 계층 → 하위 계층</strong> 방향으로 데이터를 내려보내고, 수신지에서는 <strong>하위 계층 → 상위 계층</strong> 방향으로 데이터를 처리합니다.</p>
<h3 id="1-캡슐화">(1) 캡슐화</h3>
<p><strong>캡슐화(Encapsulation)</strong>는 데이터를 전송할 때 각 계층에서 필요한 정보를 덧붙여가며 데이터를 감싸는 과정입니다. </p>
<ul>
<li><p>1) 응용 계층에서 메시지를 생성합니다. 
메시지는 패킷 단위로 송신됩니다.</p>
<blockquote>
<p>패킷은 <strong>헤더(Header)</strong>와 <strong>페이로드(Payload)</strong>, 그리고 경우에 따라 <strong>트레일러(Trailer)</strong>를 포함하여 구성됩니다. </p>
</blockquote>
</li>
<li><p><em>헤더*</em>는 데이터 전달을 위한 제어 정보(예: 주소, 포트 번호 등)를 담고 있으며, <strong>각 계층에서 추가</strong>됩니다. 
페이로드는 실제로 전송하고자 하는 데이터로, 상위 계층에서 전달받은 패킷이 여기에 해당합니다. 
트레일러는 주로 데이터 링크 계층에서 추가되며, 오류 검출 등의 기능을 수행합니다.</p>
</li>
<li><p>2) 각 계층을 내려가면서 해당 계층의 제어 정보를 헤더(Header)로 추가합니다.</p>
</li>
<li><p>3) 이 과정을 통해 최종적으로 프레임이 생성되어 네트워크를 통해 전송됩니다.</p>
</li>
</ul>
<blockquote>
<p>예: 응용 계층 데이터 → 전송 계층 (TCP 헤더 추가) → 인터넷 계층 (IP 헤더 추가) → 네트워크 인터페이스 계층 (MAC 헤더 및 트레일러 추가)</p>
</blockquote>
<h3 id="2-역캡슐화">(2) 역캡슐화</h3>
<p><strong>역캡슐화(Decapsulation)</strong>는 수신 측에서 데이터를 받을 때, 각 계층에서 자신의 헤더를 제거하고 상위 계층에 전달하는 과정입니다.</p>
<ul>
<li><p>4) 네트워크 인터페이스 계층에서 프레임을 수신합니다.</p>
</li>
<li><p>5) 각 계층은 자신에게 필요한 헤더만 읽고 제거한 후 상위 계층으로 전달합니다.</p>
</li>
<li><p>6) 최종적으로 응용 계층에 도달하면, 사용자가 이해할 수 있는 실제 데이터가 완성됩니다.</p>
</li>
</ul>
<blockquote>
<p>예: 네트워크 인터페이스 계층 (MAC 헤더 제거) -&gt; 인터넷 계층 (IP 헤더 제거) -&gt; 전송 계층 (TCP 헤더 제거) -&gt; 응용 계층 (최종 데이터 처리)</p>
</blockquote>
<p>이러한 과정을 거쳐서, 송신 측에서 생성된 데이터는 각 계층을 따라 헤더와 트레일러가 순차적으로 붙은 상태로 전송되며, 수신 측에서는 이 정보들을 하나씩 제거(역캡슐화)하면서 원래의 데이터로 복원합니다.
이처럼 <strong>캡슐화와 역캡슐화</strong> 과정은 <strong>데이터가 네트워크를 통해 정확하고 안전하게 전달되도록 보장</strong>하며, 계층별 역할 분담과 독립적인 설계를 가능하게 합니다.</p>
<h2 id="5-pdu">5. PDU</h2>
<p><strong>각 계층에서 송수신되는 메시지의 단위</strong>를 PDU라고 합니다.
상위 계층에서 전달받은 데이터에 현재 계층의 프로토콜 헤더(및 트레일러)를 추가하면 현재 계층의 PDU가 됩니다.</p>
<table>
<thead>
<tr>
<th>OSI 계층</th>
<th>PDU 명칭</th>
</tr>
</thead>
<tbody><tr>
<td>응용 / 표현 / 세션 계층</td>
<td>데이터 (Data)</td>
</tr>
<tr>
<td>전송 계층 (Transport)</td>
<td>세그먼트 (Segment) / 데이터그램 (Datagram)</td>
</tr>
<tr>
<td>네트워크 계층 (Network)</td>
<td>패킷 (Packet)</td>
</tr>
<tr>
<td>데이터 링크 계층 (Data Link)</td>
<td>프레임 (Frame)</td>
</tr>
<tr>
<td>물리 계층 (Physical)</td>
<td>비트 (Bit)</td>
</tr>
</tbody></table>
<p>전송 계층의 PDU는 TCP 프로토콜이 사용되었을 경우에는 세그먼트(Segment), 
UDP 프로토콜이 사용되었을 경우에는 데이터그램(Datagram)이 됩니다.</p>
<h2 id="6-네트워크-성능-지표">6. 네트워크 성능 지표</h2>
<p>네트워크 성능을 평가할 때는 데이터를 주고받는 양과 속도가 중요한 기준이 됩니다. 
이 중 하나인 <strong>트래픽은 일정 시간 동안 네트워크를 통해 전달되는 데이터의 양</strong>을 뜻합니다. 특정 노드(장치나 서버)에 트래픽이 집중되면, 해당 노드가 처리해야 할 데이터가 많아져 과부하가 발생할 수 있습니다. 이 과부하는 네트워크 속도 저하나 지연을 일으켜 전체 성능에 영향을 미치게 됩니다.</p>
<blockquote>
<p>그렇다면 네트워크의 성능을 평가하는 지표로는 어떤 것들이 있을까요?</p>
</blockquote>
<h3 id="1-처리율">(1) 처리율</h3>
<p>단위 시간당 네트워크를 통해 <strong>실제로 전송에 성공한 정보량</strong>을 말합니다.</p>
<ul>
<li><p>처리율은 네트워크가 얼마나 효율적으로 데이터를 전달하는지를 보여줍니다.</p>
</li>
<li><p>대역폭과 달리, 네트워크 상태나 트래픽 상황에 따라 처리율은 변동될 수 있습니다.</p>
</li>
<li><p>예를 들어, 대역폭이 1Gbps라 해도 장애나 혼잡이 있으면 실제 처리율은 그보다 낮을 수 있습니다.</p>
<h3 id="2-대역폭">(2) 대역폭</h3>
<p>단위 시간 동안 통신 매체를 통해 <strong>송수신할 수 있는 최대 정보량</strong>을 말합니다.</p>
</li>
<li><p>네트워크가 이론적으로 지원할 수 있는 최대 속도를 나타냅니다.</p>
</li>
</ul>
<h3 id="3-패킷-손실">(3) 패킷 손실</h3>
<p>송수신되는 패킷이 손실된 상황을 말합니다.</p>
<ul>
<li><p>네트워크 과부하, 장비 장애, 노드의 처리 지연 등으로 인해 발생합니다.</p>
</li>
<li><p>손실된 패킷은 재전송이 필요해 전체 네트워크 지연과 성능 저하를 일으킵니다.</p>
</li>
<li><p>손실률이 높으면 데이터 신뢰성에 문제를 일으키게 됩니다.</p>
</li>
</ul>
<h2 id="7-마무리">7. 마무리</h2>
<p>이번 포스팅에서는 네트워크의 작동 원리를 이해하는 데 꼭 필요한 핵심 개념들을 살펴보았습니다. </p>
<blockquote>
<p>이전 포스팅에서 다룬 네트워크의 기본 구조와 함께 이번 내용을 정리해 두면,
네트워크 전반에 대한 기초적인 배경지식을 쌓을 수 있을 것입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네크워크] 네트워크 기본 구조와 핵심 개념 (1)
]]></title>
            <link>https://velog.io/@heon-blog/%EB%84%A4%ED%81%AC%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-1</link>
            <guid>https://velog.io/@heon-blog/%EB%84%A4%ED%81%AC%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-1</guid>
            <pubDate>Tue, 17 Jun 2025 11:46:02 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>이번 포스팅에서는 네트워크의 기본 구조와 이를 구성하는 요소들, 네트워크의 범위에 따른 분류, 그리고 메시지 교환 방식과 전송 방식의 차이점을 체계적으로 살펴보겠습니다. 
이를 통해 네트워크의 핵심 개념을 이해하고, 보다 복잡한 네트워크 기술을 학습하는 데 필요한 배경지식을 갖출 수 있게 됩니다.</p>
<h2 id="2-네트워크의-기본-구조">2. 네트워크의 기본 구조</h2>
<p>네트워크란 여러 장치가 서로 연결되어 정보를 주고받을 수 있는 통신망입니다.
이 통신망의 모양은 <strong>그래프</strong>의 형태를 띄고 있는데, 
그래프란 <strong>노드와 노드를 연결하는 간선</strong>으로 이루어진 자료 구조입니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/22f495a3-d51e-4068-a37c-7ab240e09d69/image.png" alt=""> 출처: <a href="https://www.geeksforgeeks.org/computer-networks/types-of-node-devices-in-a-computer-network-end-devices-and-intermediary-devices/" target="_blank">geeksforgeeks.org - Types of Node Devices in a Computer Network
</a></p>
<p>모든 네트워크는 <strong>&#39;노드&#39;</strong>, 노드를 연결하는 <strong>&#39;간선&#39;</strong>, 노드 간 주고받는 <strong>&#39;메시지&#39;</strong>로 구성됩니다. 
노드는 정보를 주고받을 수 있는 장치 (그림에서의 디바이스), 간선은 정보를 주고받을 수 있는 유무선의 통신 매체(그림에서 디바이스 간의 선)를 말합니다.</p>
<blockquote>
<p>네트워크의 기본 구조를 정리하자면,
네트워크는 가장자리 노드인 <strong>호스트</strong>, 중간 노드인 <strong>네트워크 장비</strong>, 노드들을 연결하는 간선인 <strong>통신 매체</strong>, 노드들이 주고받는 정보인 <strong>메시지</strong>로 구성됩니다.</p>
</blockquote>
<h3 id="1-호스트">(1) 호스트</h3>
<p>네트워크의 <strong>가장자리에 위치한 노드</strong>는 <strong>네트워크를 통해 흐르는 정보를 최초로 생성 및 송신하고, 최종적으로 수신하는 역할</strong>을 합니다. 우리가 일상에서 사용하는 네트워크 기기(데스크톱, 노트북, 스마트폰 등)의 대부분이 여기에 속한다고 봐도 무방합니다.
이러한 가장자리 노드를 네트워크에서는 <strong>호스트</strong>라고 부릅니다.</p>
<p>호스트의 대표적인 역할로는 <strong>서버와 클라이언트</strong>가 있습니다.</p>
<p><strong>서버</strong>라는 용어는 &quot;serve(제공하다)&quot; 에서 비롯되었는데, 서비스를 제공하는 역할을 하는 호스트가 바로 서버입니다. 여기서 서비스는 파일, 웹 페이지, 메일 등이 있습니다.</p>
<p><strong>클라이언트</strong>란 서버에게 서비스를 요청하고 그에 대한 응답을 제공받는 호스트입니다. 예를 들어, 여러분이 노트북에서 웹 브라우저를 열고 구글에 접속을 시도했다고 해 봅시다. 그럼 구글의 서버는 해당 요청을 받고, 그 요청에 맞는 웹 페이지를 여러분의 웹 브라우저에 전달합니다. 여기서 여러분의 노트북은 클라이언트로서 구글 서버에 웹 페이지를 요청하고, 그에 대한 응답을  제공받게 된 것입니다.</p>
<h3 id="2-네트워크-장비">(2) 네트워크 장비</h3>
<p>네트워크 노드는 호스트뿐만 아니라, 네트워크 가장자리에 위치하지 않은 <strong>중간 노드</strong>들도 있습니다. 이러한 중간 노드들을 <strong>네트워크 장비</strong>라고 하며, 대표적으로 <strong>이더넷 허브, 스위치, 라우터, 공유기</strong> 등이 있습니다.
네트워크 장비는 호스트 간 주고받는 정보를 목적지까지 안정적이고 안전하게 전송될 수 있도록 합니다.</p>
<h3 id="3-통신-매체">(3) 통신 매체</h3>
<p>위 그림에서 호스트와 네트워크 장비들이 간선으로 연결되어 있는 것을 확인할 수 있는데, 
이렇게 각 노드를 연결하는 간선을 <strong>통신 매체</strong>라고 합니다.
이 통신 매체에는 노드들을 유선으로 연결하는 <strong>유선 매체</strong>, 무선으로 연결하는 <strong>무선 매체</strong>가 있습니다.</p>
<h3 id="4-메시지">(4) 메시지</h3>
<p>통신 매체로 연결된 노드가 주고받는 정보를 <strong>메시지</strong>라고 합니다. 
메시지는 웹 페이지나 파일, 또는 이메일처럼 여러 형태로 존재할 수 있습니다.</p>
<h2 id="3-범위에-따른-네트워크의-분류">3. 범위에 따른 네트워크의 분류</h2>
<p>네트워크의 구성 범위는 한 기업이 될 수도 있고, 한 지역이 될 수도 있고, 국가가 될 수도 있습니다.  이처럼 네트워크의 구성 범위가 다양한 만큼, 네트워크를 범위에 따라 분류하는 기준이 존재합니다. 이러한 기준으로 <strong>LAN</strong>과 <strong>WAN</strong>이 있습니다.</p>
<h3 id="1-lan">(1) LAN</h3>
<p><strong>LAN</strong>은 Local Area Network로, 가까운 지역을 연결한 <strong>근거리 통신망</strong>을 의미합니다.
가정, 학교, 기업처럼 <strong>한정된 공간에서의 네트워크</strong>가 바로 LAN입니다.</p>
<h3 id="2-wan">(2) WAN</h3>
<p><strong>WAN</strong>은 Wide Area Network로, 먼 지역을 연결하는 <strong>광역 통신망</strong>을 의미합니다.
멀리 떨어진 LAN을 연결할 수 있는 네트워크가 바로 WAN입니다.</p>
<h2 id="4-메시지-교환-방식에-따른-네트워크-분류">4. 메시지 교환 방식에 따른 네트워크 분류</h2>
<blockquote>
<p>호스트들은 네트워크에서 어떤 방식으로 메시지를 주고 받을까요?
<strong>회선 교환 방식</strong>과 <strong>패킷 교환 방식</strong>이 있습니다.</p>
</blockquote>
<h3 id="1-회선-교환-방식">(1) 회선 교환 방식</h3>
<p><strong>회선 교환 방식</strong>은 메시지 전송로인 <strong>회선</strong>을 설정하고 이 경로를 통해 메시지를 주고받는 방식입니다. 회선 교환 네트워크에서는 호스트들이 메시지를 주고받기 전에 미리 두 호스트를 연결한 후, 연결된 경로로 메시지를 주고받습니다.</p>
<p>회선 교환 네트워크가 올바르게 동작하기 위해서는 호스트 간의  회선이 적절하게 설정되어야 하는데, 이를 수행하는 네트워크 장비로는 <strong>회선 스위치</strong>가 있습니다.
즉, 회선 스위치는 호스트 사이에 일대일 정소로를 확보하는 네트워크 장비입니다.</p>
<p>회선 교환 방식은 두 호스트 사이에 연결을 확보한 후에 메시지를 주고받는 특성 덕분에 <strong>주어진 시간 동안 전송되는 정보의 양이 비교적 일정하다는 장점</strong>이 있습니다. 
반면, <strong>회선의 이용 효율이 낮아질 수 있다는 단점</strong>이 있는데요.
어떤 호스트가 메시지를 주고받기 위해 특정 회선을 사용해야 하는데, 그 회선을 다른 호스트가 이미 점유하고 있다면 다른 호스트는 이를 사용할 수 없습니다. 이 때문에 회선이 점유되어 있는 동안 데이터가 전송되지 않는다면 회선 자원이 낭비되는 것입니다.</p>
<blockquote>
<p>예를 들어, A와 B가 전화 통화를 하고 있다면 통신 회선이 일대일로 연결되기 때문에 A나 B는 동시에 다른 사람인 C와 통화할 수 없습니다. 이때 A와 B가 통화 중임에도 불구하고 아무 말도 하지 않는다면, 그 시간 동안 회선 자원이 낭비되는 셈입니다.</p>
</blockquote>
<h3 id="2-패킷-교환-방식">(2) 패킷 교환 방식</h3>
<p><strong>패킷 교환 방식</strong>은 앞서 언급한 <strong>회선 교환 방식의 단점을 보완한 방식</strong>으로, 메시지를 작은 단위인 <strong>패킷</strong>으로 나누어 전송하는 방식입니다. 패킷으로 나누어진 메시지들은 수신지로 도달한 뒤 재조립됩니다. 현재 대부분의 네트워크에서 패킷 교환 방식을 사용하고 있습니다. </p>
<p>사전에 설정된 경로만으로 통신하는 회선 교환 방식과는 달리, 패킷 교환 방식은 정해진 경로만으로 메시지를 송수신하지 않습니다. 때문에 회선 교환 방식보다 회선의 이용 효율이 상대적으로 높습니다. 패킷으로 나누어진 메시지는 다양한 중간 노드를 거칠 수 있는데, 이때 중간 노드인 <strong>패킷 스위치</strong>는 패킷의 송수신지 주소를 확인하여, 수신지까지 도달할 최적의 경로를 결정합니다. 
대표적인 패킷 스위치 네트워크 장비로는 <strong>라우터</strong>와 <strong>스위치</strong>가 있습니다.
<strong>라우터</strong>는 서로 다른 네트워크 간에 데이터를 전달하며, 목적지 IP 주소를 기반으로 최적의 경로를 찾아 패킷을 전송하는 장비이고, <strong>스위치</strong>는 같은 네트워크 내부에서 장치들 간의 데이터를 목적지 MAC 주소를 기반으로 빠르고 효율적으로 전달하는 장비입니다.</p>
<p>패킷은 주로 <strong>헤더(Header)</strong>, <strong>페이로드(Payload)</strong>, <strong>트레일러(Trailer)</strong>의 세 부분으로 구성됩니다. 헤더에는 송수신지 주소, 순서 정보, 헤더 자체의 무결성을 확인하기 위한 제어 정보 등이 담겨 있으며, 페이로드는 실제로 전송하고자 하는 데이터가 담긴 부분입니다. 마지막으로 트레일러에는 전송된 데이터의 오류 검출을 위한 정보가 포함되어, 데이터가 올바르게 수신되었는지 확인하는 데 사용됩니다.</p>
<h2 id="5-주소와-송수신지-유형에-따른-전송-방식">5. 주소와 송수신지 유형에 따른 전송 방식</h2>
<p>패킷의 헤더에 담기는 대표적인 정보로는 주소가 있습니다. 주소는 송수신지를 특정하는 정보를 의미합니다. 이러한 주소에는 <strong>IP 주소</strong>와 <strong>MAC 주소</strong>가 있습니다.</p>
<p>이렇게 송수신지를 특정할 수 있는 주소가 있다면 송수신지 유형에 따라 다양한 방식으로 메시지를 보낼 수 있게 됩니다. 이러한 송수신지 유형에 따른 대표적인 전송 방식으로는 <strong>유니캐스트</strong>와 <strong>브로드캐스트</strong>가 있습니다.</p>
<h3 id="1-유니캐스트">(1) 유니캐스트</h3>
<p><strong>유니캐스트</strong>는 가장 일반적인 형태의 송수신 방식으로, 하나의 수신지에 메시지를 전송하는 방식입니다. 송신지와 수신지가 일대일로 메시지를 주고받는 경우입니다.</p>
<h3 id="2-브로드캐스트">(2) 브로드캐스트</h3>
<p><strong>브로드캐스트</strong>는 자신을 제외한 네트워크상의 모든 호스트에게 전송하는 방식입니다. 브로드캐스트가 전송되는 범위를 <strong>브로드캐스트 도메인</strong>이라고 합니다. 즉, 브로드캐스트의 수신지는 브로드캐스트 도메인이며 이는 자신을 제외한 네트워크상의 모든 호스트를 말합니다.</p>
<p>이외에도 네트워크 내의 동일 그룹에 속한 호스트에게만 전송하는 방식인 <strong>멀티캐스트</strong>, 네트워크 내의 동일 그룹에 속한 호스트 중 가장 가까운 호스트에게 전송하는 방식인 <strong>애니캐스트</strong> 등 다양한 방식이 있습니다.</p>
<blockquote>
<p>네트워크의 기본 구조와 핵심 개념을 다음 포스팅에서 이어서 다루겠습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[네트워크] 컴퓨터 네트워크, 왜 알아야 할까?]]></title>
            <link>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%99%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@heon-blog/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%99%9C-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Mon, 16 Jun 2025 07:41:21 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>개발자는 단순히 코드를 잘 작성하는 것뿐만 아니라, 그 코드가 <strong>어떻게 다른 시스템과 소통하는지</strong>도 이해할 필요가 있습니다. 그 중심에는 <strong>컴퓨터 네트워크</strong>가 있습니다.</p>
<blockquote>
<p>이 글에서는 컴퓨터 네트워크의 기본 개념부터, 개발자가 왜 네트워크를 알아야 하는지, 그리고 어떤 상황에서 네트워크 지식이 활용되는지까지 알아보겠습니다.</p>
</blockquote>
<hr>
<h2 id="2-네트워크란">2. 네트워크란?</h2>
<p>우리가 사용하는 스마트폰, 노트북, 데스크탑 등은 주변 기기들과 유선 혹은 무선으로 연결되어 데이터를 주고받습니다.<br>이 장치들은 서로 직접 연결되기도 하고, 다른 장치를 거쳐 간접적으로 연결되기도 합니다.  </p>
<p>이처럼 수많은 장치들이 <strong>서로 얽혀 정보를 주고받을 수 있도록 구성된 구조</strong>를 <strong>네트워크</strong>라고 합니다. 마치 거미줄처럼 장치들이 촘촘히 연결되어 있는 구조라고 보면 이해하기 쉽습니다.</p>
<p>여러 장치들이 네트워크를 통해 서로 연결되면 주변의 장치하고만 정보를 주고 받는 것이 아니라, 네트워크와 연결된 지구 반대편에 있는 장치와도 정보를 주고받을 수 있습니다. 이를 가능하게 하는 기술이 바로 <strong>인터넷</strong>입니다.</p>
<blockquote>
<p>인터넷은 여러 네트워크를 연결한 &#39;네트워크의 네트워크&#39;를 의미합니다.</p>
</blockquote>
<hr>
<h2 id="3-개발자가-네트워크를-알아야-하는-이유">3. 개발자가 네트워크를 알아야 하는 이유</h2>
<p>일상적으로 실행하는 많은 프로그램들은 <strong>하나의 장치 안에서만 실행되기보다는 네트워크를 통해 다른 장치와 상호 작용하며 실행되는 경우가 많습니다.</strong><br>프로그램이 네트워크를 통해 다른 장치와 상호 작용하며 실행되는 경우가 많다는 것은, 그만큼 <strong>개발자가 네트워크를 이용하는 프로그램을 개발하는 경우가 많다는 것</strong>을 의미합니다.  </p>
<p>개발자가 네트워크를 제대로 이해해야 하는 것도 이러한 이유 때문입니다.</p>
<blockquote>
<p>그렇다면 개발자는 네트워크 지식을 어디에, 어떻게 활용하게 될까요?</p>
</blockquote>
<h3 id="1-프로그램을-만드는-데-네트워크-지식이-활용됩니다">(1) 프로그램을 만드는 데 네트워크 지식이 활용됩니다.</h3>
<p>웹 애플리케이션을 개발하거나 서버와 통신하는 기능을 구현하다 보면, 단순히 기능만 구현하는 코드로는 해결하기 어려운 상황들이 생깁니다.<br>예를 들어, 사용자가 로그인할 때 데이터를 서버에 안전하게 전송하고, 서버는 그 요청에 대해 성공 여부를 명확하게 알려줘야 하는데, 이 과정에서 <strong>적절한 HTTP 메서드와 상태 코드</strong>를 제대로 사용하는 것이 매우 중요합니다.<br>이런 상황에서 <strong>네트워크에 대한 이해</strong>가 중요한 역할을 하며, 이를 바탕으로 <strong>더 안정적이고 효율적인 통신</strong>을 구현할 수 있습니다.</p>
<p>또한 <strong>Django, Spring, Express</strong> 같은 프레임워크를 사용할 때도  </p>
<ul>
<li>라우팅 처리  </li>
<li>쿠키와 세션 관리 </li>
<li>CORS 설정</li>
<li>포트 번호 설정</li>
</ul>
<p>등과 같은 기능을 제대로 사용하려면 네트워크에 대한 개념을 이해하고 있어야 합니다.</p>
<p>REST API, 웹 소켓, OAuth 인증 등도 네트워크 프로토콜 위에서 동작하므로,<br><strong>TCP/UDP, DNS, HTTPS</strong>와 같은 <strong>네트워크 지식이 뒷받침</strong>되어야 기능을 올바르게 이해하고 구현할 수 있습니다.</p>
<h3 id="2-프로그램을-유지-보수하기-위해-네트워크-지식이-필요합니다">(2) 프로그램을 유지 보수하기 위해 네트워크 지식이 필요합니다.</h3>
<p>서비스를 운영하다 보면 다음과 같은 <strong>예상치 못한 문제들</strong>이 생깁니다:</p>
<ul>
<li>클라이언트에서 서버로 요청이 전달되지 않음</li>
<li>특정 포트가 막혀 통신이 되지 않음</li>
<li>갑자기 웹 서버가 응답하지 않음 </li>
<li>DNS 설정 오류로 인해 도메인이 작동하지 않음</li>
</ul>
<p>이때 <strong>네트워크 구조와 동작 원리</strong>를 알고 있다면, 원인을 빠르게 파악하고 적절한 조치를 취할 수 있게 됩니다.<br>단순히 &quot;통신에 문제가 있어요&quot;라고 말하는 수준을 넘어서, <strong>실제로 어디에서 문제가 발생했는지 분석하고 해결할 수 있는 능력</strong>이 생기는 것입니다.</p>
<hr>
<h2 id="4-마무리">4. 마무리</h2>
<p>개발자는 단순히 코드를 작성하는 것에 그치지 않고, 그 코드가 네트워크 위에서 어떻게 동작하고 상호작용하는지 이해해야 더 나은 프로그램을 만들 수 있습니다.
네트워크에 대한 이해는 단지 기능 구현을 넘어, <strong>서비스의 안정성과 유지 보수성</strong>을 높이는 데 큰 도움이 됩니다.</p>
<blockquote>
<p>다음 포스팅에서는 네트워크에서의 <strong>필수 배경지식</strong>들에 대해 알아보겠습니다.
이해를 돕기 위해 최대한 쉽게 풀어 설명할 예정이니, 함께 학습해 보시기 바랍니다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 최소 신장 트리(MST) 완전 정복 – 크루스칼과 프림 알고리즘의 구현과 이해]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%ACMST-%ED%81%AC%EB%A3%A8%EC%8A%A4%EC%B9%BC%EA%B3%BC-%ED%94%84%EB%A6%BC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B5%AC%ED%98%84%EA%B3%BC-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%ACMST-%ED%81%AC%EB%A3%A8%EC%8A%A4%EC%B9%BC%EA%B3%BC-%ED%94%84%EB%A6%BC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B5%AC%ED%98%84%EA%B3%BC-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Wed, 11 Jun 2025 15:11:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>복잡한 네트워크를 구성할 때, 어떻게 하면 최소 비용으로 모든 지점을 연결할 수 있을까? 
이 문제를 해결하기 위한 핵심 개념이 <strong>최소 신장 트리(MST, Minimum Spanning Tree)</strong>입니다. 
MST는 그래프의 모든 정점을 연결하되, 불필요한 연결 없이 최소 비용만으로 구성된 네트워크를 의미합니다.</p>
<blockquote>
<p>이번 포스팅에서는 MST의 개념과 대표적인 알고리즘(크루스칼과 프림), 그리고 실제 구현 방법까지 함께 살펴보겠습니다.</p>
</blockquote>
<hr>
<h2 id="2-mst란">2. MST란?</h2>
<p><strong>MST (Minimum Spanning Tree)</strong>는 무방향 가중치 그래프에서 모든 정점을 연결하면서, 간선 가중치의 합이 최소가 되도록 구성한 트리를 의미합니다.</p>
<p>조건은 다음과 같습니다:</p>
<ul>
<li><strong>모든 정점이 연결되어야 함</strong>  </li>
<li>트리이므로 <strong>사이클이 없어야 함</strong></li>
<li><strong>간선 가중치 합이 최소여야 함</strong></li>
</ul>
<p>MST를 구하는 대표적인 알고리즘으로 <strong>크루스칼</strong>과 <strong>프림</strong> 알고리즘이 있습니다.</p>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>핵심 접근 방식</th>
<th>시간 복잡도</th>
</tr>
</thead>
<tbody><tr>
<td>크루스칼</td>
<td>간선 중심</td>
<td>O(E log E)</td>
</tr>
<tr>
<td>프림</td>
<td>정점 중심</td>
<td>O(E log V)</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-mst-구현-방법">3. MST 구현 방법</h2>
<h3 id="3-1-크루스칼-알고리즘-kruskal">3-1. 크루스칼 알고리즘 (Kruskal)</h3>
<p><strong>크루스칼 알고리즘</strong>은 <strong>간선을 중심</strong>으로 최소 신장 트리를 만들어 나가는 방식입니다.</p>
<p>우선 모든 간선을 가중치 기준으로 <strong>오름차순 정렬</strong>한 뒤, 가장 가중치가 작은 간선부터 하나씩 선택하면서 <strong>사이클이 생기지 않도록 주의하여 연결</strong>합니다. </p>
<p>이때 사이클 여부를 효율적으로 판단하기 위해 <strong>유니온 파인드(Union-Find)</strong> 자료구조를 사용합니다.</p>
<pre><code class="language-java">import java.util.*;

public class KruskalMST {
    static int[] parent;

    static class Edge implements Comparable&lt;Edge&gt; {
        int from, to, weight;

        Edge(int from, int to, int weight) { 
            this.from = from;
            this.to = to;
            this.weight = weight;
        }

        @Override
        public int compareTo(Edge other) {
            return this.weight - other.weight;
        }
    }

    static int find(int x) {  
        if (parent[x] == x) return x;
        return parent[x] = find(parent[x]);
    }

    static void union(int a, int b) { 
        int aRoot = find(a);
        int bRoot = find(b);
        if (aRoot != bRoot) {
            parent[bRoot] = aRoot;
        }
    }

    public static void main(String[] args) {
        int n = 5; // 정점 개수
        List&lt;Edge&gt; edges = Arrays.asList(
            new Edge(1, 2, 3),
            new Edge(1, 3, 2),
            new Edge(2, 3, 1),
            new Edge(2, 4, 4),
            new Edge(3, 5, 6)
        );

        Collections.sort(edges);

        parent = new int[n + 1];
        for (int i = 1; i &lt;= n; i++) {
            parent[i] = i;
        }

        int total = 0;
        for (Edge e : edges) {
            if (find(e.from) != find(e.to)) {
                union(e.from, e.to);
                total += e.weight;
            }
        }

        System.out.println(&quot;최소 신장 트리 가중치 합: &quot; + total);
    }
}</code></pre>
<p>다음은 구현 과정입니다.</p>
<ol>
<li><p><strong>그래프의 모든 간선을 모읍니다.</strong>
연결 정보(정점1, 정점2, 가중치)를 간선 리스트에 저장합니다.</p>
</li>
<li><p><strong>간선들을 가중치 기준으로 오름차순 정렬합니다.</strong>
가장 작은 가중치부터 하나씩 선택할 수 있도록 정렬합니다.</p>
</li>
<li><p><strong>유니온-파인드(Disjoint Set)를 준비합니다.</strong>
각각의 정점이 자신을 부모로 갖도록 초기화하여, 나중에 사이클 여부를 빠르게 판단할 수 있게 합니다.</p>
</li>
<li><p><strong>정렬된 간선을 하나씩 보면서, 두 정점이 연결되어 있지 않다면 연결합니다.</strong>
이때 서로 다른 집합에 속해 있는지(= 사이클이 생기지 않는지)를 find()로 확인합니다. 연결해도 괜찮다면 union()을 통해 합칩니다.</p>
</li>
<li><p><strong>간선을 연결할 때마다 가중치를 누적합니다.</strong>
최종적으로 모든 정점을 연결하게 되면, 누적된 가중치 합이 바로 최소 신장 트리의 비용입니다.</p>
</li>
</ol>
<h3 id="3-2-프림-알고리즘-prim">3-2. 프림 알고리즘 (Prim)</h3>
<p><strong>프림 알고리즘</strong>은 <strong>정점을 중심</strong>으로 최소 신장 트리를 확장해 나가는 방식입니다.</p>
<p>처음에 아무 정점 하나에서 시작해서, 현재 방문한 정점과 인접한 간선 중 가장 작은 것을 선택해 트리에 포함시킵니다. 이를 반복하여 모든 정점이 연결될 때까지 진행합니다.</p>
<p><strong>우선순위 큐(PriorityQueue)</strong>를 사용하여 가장 가중치가 작은 간선을 빠르게 선택합니다.</p>
<pre><code class="language-java">import java.util.*;

public class PrimMST {
    static class Node implements Comparable&lt;Node&gt; {
        int to, weight;

        Node(int to, int weight) {
            this.to = to;
            this.weight = weight;
        }

        @Override
        public int compareTo(Node other) {
            return this.weight - other.weight;
        }
    }

    public static void main(String[] args) {
        int n = 5;
        List&lt;List&lt;Node&gt;&gt; graph = new ArrayList&lt;&gt;();
        for (int i = 0; i &lt;= n; i++) {
            graph.add(new ArrayList&lt;&gt;());
        }

        // 무방향 간선 추가
        graph.get(1).add(new Node(2, 3));
        graph.get(2).add(new Node(1, 3));
        graph.get(1).add(new Node(3, 2));
        graph.get(3).add(new Node(1, 2));
        graph.get(2).add(new Node(3, 1));
        graph.get(3).add(new Node(2, 1));
        graph.get(2).add(new Node(4, 4));
        graph.get(4).add(new Node(2, 4));
        graph.get(3).add(new Node(5, 6));
        graph.get(5).add(new Node(3, 6));

        boolean[] visited = new boolean[n + 1];
        PriorityQueue&lt;Node&gt; pq = new PriorityQueue&lt;&gt;();
        pq.add(new Node(1, 0));

        int total = 0;
        while (!pq.isEmpty()) {
            Node current = pq.poll();
            if (visited[current.to]) continue;
            visited[current.to] = true;
            total += current.weight;

            for (Node next : graph.get(current.to)) {
                if (!visited[next.to]) {
                    pq.add(next);
                }
            }
        }

        System.out.println(&quot;최소 신장 트리 가중치 합: &quot; + total);
    }
}</code></pre>
<p>다음은 구현 과정입니다.</p>
<ol>
<li><p><strong>인접 리스트 형태로 그래프를 구성합니다.</strong>
각 정점마다 연결된 간선들을 저장합니다. 무방향 그래프이므로 양쪽 모두 추가합니다.</p>
</li>
<li><p><strong>임의의 시작 정점을 정하고, 우선순위 큐에 넣습니다.</strong>
처음에는 연결된 간선이 없으므로 가중치 0으로 시작 정점을 큐에 넣습니다.</p>
</li>
<li><p><strong>우선순위 큐에서 가장 가중치가 작은 간선을 꺼냅니다.</strong>
만약 해당 정점이 이미 방문된 정점이라면 무시하고, 아니라면 방문 처리합니다.</p>
</li>
<li><p><strong>선택된 간선의 가중치를 누적하고, 해당 정점에서 뻗어나가는 모든 간선을 큐에 추가합니다.</strong>
단, 아직 방문하지 않은 정점들만 대상으로 합니다.</p>
</li>
<li><p><strong>모든 정점을 방문할 때까지 반복합니다.</strong>
우선순위 큐가 빌 때까지 진행하면, 최소 신장 트리의 비용이 완성됩니다.</p>
</li>
</ol>
<hr>
<h2 id="4-mst-vs-다익스트라-vs-플로이드-워셜">4. MST vs 다익스트라 vs 플로이드-워셜</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>MST</th>
<th>다익스트라</th>
<th>플로이드-워셜</th>
</tr>
</thead>
<tbody><tr>
<td><strong>목적</strong></td>
<td>최소 연결 비용</td>
<td>단일 시작점 최단 거리</td>
<td>모든 정점 쌍 최단 거리</td>
</tr>
<tr>
<td><strong>방향성</strong></td>
<td>무방향 그래프</td>
<td>방향 그래프 가능</td>
<td>방향 그래프 가능</td>
</tr>
<tr>
<td><strong>음수 가중치</strong></td>
<td>불가 (사이클 없이 양수만)</td>
<td>불가</td>
<td>가능</td>
</tr>
<tr>
<td><strong>기술 방식</strong></td>
<td>크루스칼/프림 (Union-Find, PQ)</td>
<td>우선순위 큐 (힙)</td>
<td>3중 for문</td>
</tr>
<tr>
<td><strong>시간 복잡도</strong></td>
<td>O(E log E), O(E log V)</td>
<td>O((V + E) log V)</td>
<td>O(V³)</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-백준-문제-추천">5. 백준 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제 번호 / 제목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 1197</td>
<td><a href="https://www.acmicpc.net/problem/1197">최소 스패닝 트리</a></td>
<td>MST 기본 구현 문제</td>
</tr>
<tr>
<td>백준 1922</td>
<td><a href="https://www.acmicpc.net/problem/1922">네트워크 연결</a></td>
<td>MST 구현 문제</td>
</tr>
<tr>
<td>백준 1647</td>
<td><a href="https://www.acmicpc.net/problem/1647">도시 분할 계획</a></td>
<td>MST 응용 문제</td>
</tr>
<tr>
<td>백준 4386</td>
<td><a href="https://www.acmicpc.net/problem/4386">별자리 만들기</a></td>
<td>좌표 기반 + MST</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>최소 신장 트리는 <strong>여러 지점을 가장 효율적으로 연결</strong>하는 과정입니다.
불필요한 연결을 줄이고, 꼭 필요한 간선만으로 전체를 하나로 묶는 것이 핵심이죠.
모든 노드를 <strong>최소 비용으로 연결</strong>해야 하는 상황에서 반드시 알아야 할 개념입니다.</p>
<p>MST를 구하는 방법에는 앞서 다룬 것처럼,
<strong>크루스칼 알고리즘에서 간선을 중심으로 확장하는 방법</strong>과
<strong>프림 알고리즘에서 정점을 중심으로 확장하는 방법</strong>이 있습니다.
그래프의 구조와 조건에 따라 더 적합한 알고리즘을 선택하는 것이 중요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 유니온 파인드(Union-Find) 완전 정복 – 집합의 연결 관계 이해]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8Union-Find-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%A7%91%ED%95%A9-%EA%B4%80%EB%A6%AC%EC%99%80-%EC%97%B0%EA%B2%B0-%EC%97%AC%EB%B6%80-%ED%8C%90%EB%8B%A8</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%9C%A0%EB%8B%88%EC%98%A8-%ED%8C%8C%EC%9D%B8Union-Find-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%A7%91%ED%95%A9-%EA%B4%80%EB%A6%AC%EC%99%80-%EC%97%B0%EA%B2%B0-%EC%97%AC%EB%B6%80-%ED%8C%90%EB%8B%A8</guid>
            <pubDate>Sun, 08 Jun 2025 06:38:36 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p><strong>유니온 파인드</strong>(Union-Find)는 서로소 집합(Disjoint Set) 자료구조라고도 하며,
여러 개의 원소를 여러 집합으로 나누어 관리합니다.
이를 통해 <strong>두 원소가 같은 집합에 속하는지 빠르게 확인</strong>할 수 있고, <strong>서로 다른 집합을 하나로 합치는 연산</strong>도 효율적으로 처리할 수 있습니다.</p>
<hr>
<h2 id="2-유니온-파인드란">2. 유니온 파인드란?</h2>
<ul>
<li><strong>union(x, y)</strong>: 두 원소 x와 y가 속한 집합을 합친다.  </li>
<li><strong>find(x)</strong>: 원소 x가 속한 집합의 대표자(루트)를 찾는다.  </li>
</ul>
<p>집합의 대표자(루트)를 찾음으로써 <strong>두 원소가 같은 집합인지 확인</strong>할 수 있습니다.  </p>
<p>유니온 파인드는 다음과 같은 문제에서 매우 유용합니다.</p>
<ul>
<li><strong>네트워크 연결 여부 확인</strong>: 두 컴퓨터가 같은 네트워크 상에 있는지 확인</li>
<li><strong>친구 관계 그래프</strong>: 두 사람이 친구 그룹에 속해 있는지 확인</li>
<li><strong>사이클 탐지</strong>: 그래프에서 사이클이 형성되는지 여부 검사</li>
<li><strong>최소 신장 트리(MST) 알고리즘</strong>: 크루스칼 알고리즘에서 사이클 여부 확인에 필수</li>
</ul>
<blockquote>
<p>예시 ) 
사람들의 친구 관계를 관리할 때 각 사람을 노드로 보고, 친구 관계를 <code>union</code> 연산으로 묶어 나가면, 특정 두 사람이 같은 친구 그룹에 있는지 <code>find</code> 연산을 통해 쉽게 판단할 수 있습니다.</p>
</blockquote>
<hr>
<h2 id="3-유니온-파인드-핵심-개념">3. 유니온 파인드 핵심 개념</h2>
<h3 id="3-1-경로-압축-path-compression">3-1. 경로 압축 (Path Compression)</h3>
<p><code>find(x)</code> 연산을 수행할 때, <strong>x의 부모 노드를 따라 루트 노드를 찾게 되는데</strong>, 이 과정에서 <strong>방문한 모든 노드의 부모를 루트로 직접 연결</strong>합니다.<br>이렇게 하면 모든 노드가 <strong>루트에 직접 연결</strong>되기 때문에, 다음 find 연산은 <strong>거의 한 번만에 루트를 찾을 수 있게 됩니다.</strong></p>
<pre><code class="language-java">public int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]);  // 경로 압축
    }
    return parent[x];
}
</code></pre>
<blockquote>
<p>예시 )
초기 상태: 1 → 2 → 3 (3이 루트)
find(1)을 호출하면: 1 → 3, 2 → 3 으로 갱신됨 (모두 루트로 직접 연결됨)</p>
</blockquote>
<hr>
<h3 id="3-2-높이-또는-크기-기반-합치기-union-by-rank--size">3-2. 높이 또는 크기 기반 합치기 (Union by Rank / Size)</h3>
<p>두 트리를 합칠 때, 트리의 <strong>높이(랭크)</strong>나 <strong>노드 개수(크기)</strong>를 기준으로 작은 쪽을 큰 쪽에 붙입니다.<br>이렇게 하면 전체 트리의 최대 깊이가 불필요하게 증가하는 것을 방지할 수 있어, <code>find</code> 연산 시 루트까지 올라가는 시간이 줄어듭니다.<br>즉, 트리가 한쪽으로 길게 자라는 것을 막아 시간 복잡도를 <strong>O(α(N))</strong>(거의 상수 시간)으로 유지할 수 있습니다.</p>
<pre><code class="language-java">public void union(int x, int y) {
    int xRoot = find(x);
    int yRoot = find(y);

    if (xRoot == yRoot) return;

    if (rank[xRoot] &lt; rank[yRoot]) {
        parent[xRoot] = yRoot;
    } else {
        parent[yRoot] = xRoot;
        if (rank[xRoot] == rank[yRoot]) {
            rank[xRoot]++;
        }
    }
}</code></pre>
<blockquote>
<p>예시 ) 
x 트리의 높이가 1이고 y 트리의 높이가 2일 때,
x를 y에 붙이면 전체 트리의 높이는 그대로 2로 유지됩니다.
반대로 y를 x에 붙이면 트리의 높이가 3으로 불필요하게 증가하게 되어, 탐색 시간이 더 오래 걸리게 됩니다.</p>
</blockquote>
<hr>
<h2 id="4-유니온-파인드-구현-방법-java">4. 유니온 파인드 구현 방법 (Java)</h2>
<pre><code class="language-java">import java.util.*;

public class UnionFind {
    static int[] parent;
    static int[] rank;  // 각 노드의 랭크(트리 높이) 저장

    public static void main(String[] args) {
        int n = 7; // 노드 개수
        parent = new int[n + 1];
        rank = new int[n + 1];

        // 초기화: 각 노드는 자기 자신을 부모로, 랭크는 0으로 초기화
        for (int i = 1; i &lt;= n; i++) {
            parent[i] = i;
            rank[i] = 0;
        }

        // union 연산 수행
        union(1, 2);
        union(2, 3);
        union(4, 5);
        union(6, 7);
        union(5, 6);

        // 같은 집합인지 확인
        System.out.println(find(1) == find(3)); // true
        System.out.println(find(1) == find(4)); // false
        System.out.println(find(5) == find(7)); // true
    }

    // find 연산 (경로 압축 적용)
    static int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    // union 연산 (Union by Rank 적용)
    static void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);

        if (rootX == rootY) return;

        if (rank[rootX] &lt; rank[rootY]) {
            parent[rootX] = rootY;
        } else {
            parent[rootY] = rootX;
            if (rank[rootX] == rank[rootY]) {
                rank[rootX]++;
            }
        }
    }
}</code></pre>
<p>다음은 union 연산의 동작 과정입니다.</p>
<ol>
<li>union(1, 2)</li>
</ol>
<ul>
<li><code>find(1) → 1, find(2) → 2</code> (서로 다른 집합)</li>
<li><code>rank[1] = rank[2]</code> 이므로 
1을 루트 노드로 설정하고 2를 연결, <code>rank[1]++</code><blockquote>
<p>parent: [0, 1, 1, 3, 4, 5, 6, 7]
rank: [0, 1, 0, 0, 0, 0, 0, 0]</p>
</blockquote>
</li>
</ul>
<ol start="2">
<li>union(2, 3)</li>
</ol>
<ul>
<li><code>find(2) → 1, find(3) → 3</code></li>
<li><code>rank[1] &gt; rank[3]</code> 이므로 3을 {1, 2} 집합에 붙임 <blockquote>
<p>parent: [0, 1, 1, 1, 4, 5, 6, 7]
rank: [0, 1, 0, 0, 0, 0, 0, 0]</p>
</blockquote>
</li>
</ul>
<ol start="3">
<li>union(4, 5)</li>
</ol>
<ul>
<li><code>find(4) → 4, find(5) → 5</code></li>
<li><code>rank[4] = rank[5]</code> 이므로 
4를 루트 노드로 설정하고 5를 연결, <code>rank[4]++</code><blockquote>
<p>parent: [0, 1, 1, 1, 4, 4, 6, 7]
rank: [0, 1, 0, 0, 1, 0, 0, 0]</p>
</blockquote>
</li>
</ul>
<ol start="4">
<li>union(6, 7)</li>
</ol>
<ul>
<li><code>find(6) → 6, find(7) → 7</code></li>
<li><code>rank[6] = rank[7]</code> 이므로 
6을 루트 노드로 설정하고 7을 연결, <code>rank[6]++</code><blockquote>
<p>parent: [0, 1, 1, 1, 4, 4, 6, 6]
rank: [0, 1, 0, 0, 1, 0, 1, 0]</p>
</blockquote>
</li>
</ul>
<ol start="5">
<li>union(5, 6)</li>
</ol>
<ul>
<li><code>find(5) → 4, find(6) → 6</code></li>
<li><code>rank[4] = rank[6]</code> 이므로
4를 루트 노드로 설정하고 6을 연결, rank[4]++<blockquote>
<p>parent: [0, 1, 1, 1, 4, 4, 4, 6]
rank: [0, 1, 0, 0, 2, 0, 1, 0]</p>
</blockquote>
</li>
</ul>
<p>결과적으로 union() 연산을 통해 {1, 2, 3}, {4, 5, 6, 7} 두 개의 집합이 형성되고,
find() 연산을 통해 두 노드가 같은 집합에 속하는지 확인할 수 있게 됩니다.</p>
<hr>
<h2 id="5-백준-문제-추천">5. 백준 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 1717</td>
<td><a href="https://www.acmicpc.net/problem/1717">집합의 표현</a></td>
<td>유니온 파인드 기본 문제</td>
</tr>
<tr>
<td>백준 1976</td>
<td><a href="https://www.acmicpc.net/problem/1976">여행 가자</a></td>
<td>연결 여부 판단 문제</td>
</tr>
<tr>
<td>백준 4195</td>
<td><a href="https://www.acmicpc.net/problem/4195">친구 네트워크</a></td>
<td>문자열 매핑 + 유니온 파인드 활용</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>유니온 파인드는 서로소 집합을 관리하고 연결 여부를 확인하는 데 아주 유용한 알고리즘입니다. 특히, <strong>크루스칼 알고리즘에서 MST(최소 신장 트리)를 구현할 때 핵심적으로 사용합니다.</strong></p>
<p>find()의 경로 압축과 union()의 루트 합치기 방식은 트리의 깊이를 최소화하여 시간 복잡도를 거의 상수 시간에 가깝게 최적화하는 기법입니다. 이 두 가지 최적화를 통해 여러 번의 find()와 union() 연산이 효율적으로 처리됩니다.</p>
<p>따라서 유니온 파인드는 MST뿐만 아니라 그래프에서 연결성, 사이클 검출 등 다양한 분야에서 폭넓게 쓰이며, <strong>경로 압축과 루트 합치기 최적화  기법은 이러한 연산을 매우 빠르게 해주는 핵심 기술</strong>입니다.</p>
<blockquote>
<p>👉 다음 포스팅에서는 유니온 파인드를 기반으로 한 최소 신장 트리(MST)에 대해 자세히 다뤄보겠습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 위상 정렬(Topological Sort) 완전 정복 – 방향 그래프의 순서 정리]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%9C%84%EC%83%81-%EC%A0%95%EB%A0%ACTopological-Sort-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%B0%A9%ED%96%A5-%EA%B7%B8%EB%9E%98%ED%94%84%EC%9D%98-%EC%84%A0%ED%9B%84-%EA%B4%80%EA%B3%84-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EC%9C%84%EC%83%81-%EC%A0%95%EB%A0%ACTopological-Sort-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%B0%A9%ED%96%A5-%EA%B7%B8%EB%9E%98%ED%94%84%EC%9D%98-%EC%84%A0%ED%9B%84-%EA%B4%80%EA%B3%84-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 05 Jun 2025 16:47:49 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>위상 정렬(Topological Sort)은 <strong>방향 그래프</strong>에서 <strong>사이클이 없는 경우</strong>에 사용할 수 있는 알고리즘으로,<br><strong>모든 간선 (u → v)에 대해 u가 v보다 먼저 오도록</strong>, 정점들을 <strong>일정한 순서로 나열</strong>하는 방법입니다.</p>
<p>쉽게 말해, <strong>선행 조건이 있는 작업들의 순서를 정하는 알고리즘</strong>입니다.  </p>
<blockquote>
<p>예시) </p>
</blockquote>
<ol>
<li>작업 A가 작업 B보다 먼저 끝나야 할 때</li>
<li>수강 과목을 순서대로 들어야 할 때</li>
</ol>
<hr>
<h2 id="2-위상-정렬이란">2. 위상 정렬이란?</h2>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/339c5f85-98b8-45b9-a679-ddcf71a64fc7/image.gif" alt=""> <span style="color: gray">출처: <a href="https://anushkabhave.medium.com/graph-theory-algorithms-a816640610e3" target="_blank">medium.com - Graph Theory Algorithms
</a></span></p>
<p>다음은 그림에 대한 동작 과정입니다.</p>
<ol>
<li><p><code>진입 차수가 0</code>인 E부터 탐색을 시작합니다.</p>
<ul>
<li>진입 차수가 0인 노드가 여러 개일 경우,<br><strong>알파벳 순으로 먼저 처리하기 위해 우선순위 큐</strong>를 사용합니다.</li>
</ul>
</li>
<li><p>E → B, F 간선을 따라 B와 F의 진입 차수를 1씩 감소시킵니다.<br>B의 진입 차수가 0이 되어 큐에 추가됩니다.</p>
</li>
<li><p>B를 꺼내고 → A, C의 진입 차수를 감소시킵니다.<br>진입 차수가 0이 된 A, C가 큐에 추가됩니다.</p>
</li>
<li><p>이후 과정은 <strong>동일한 패턴의 반복</strong>입니다.</p>
<ul>
<li>큐에서 알파벳 순으로 노드를 꺼냅니다.</li>
<li>해당 노드가 가리키는 노드들의 진입 차수를 감소시킵니다.</li>
<li>진입 차수가 0이 된 노드는 다시 큐에 추가됩니다.</li>
<li>이 과정을 큐가 빌 때까지 반복합니다.</li>
</ul>
</li>
</ol>
<p>위 과정을 반복하면, 그래프 내 모든 노드들이 선후 관계에 맞게 차례대로 정렬됩니다.
특히, 알파벳 순서대로 처리하기 위해 우선순위 큐를 사용했기 때문에
노드들이 <code>E B A C F D I K H J G</code> 순서로 정렬되는 것을 확인할 수 있습니다.</p>
<blockquote>
<p><strong>특정 순서 조건이 없다면</strong> 우선순위 큐 대신 <strong>일반 큐</strong>를 써도 무방합니다.</p>
</blockquote>
<hr>
<h3 id="2-1-위상-정렬을-이해하려면">2-1. 위상 정렬을 이해하려면?</h3>
<p><strong>큐와 진입 차수</strong>를 이용한 BFS 방식을 구현하기 위해서는 <strong>진입 차수</strong>의 이해가 필요합니다.</p>
<p><strong>진입 차수</strong>란?</p>
<blockquote>
<p><strong>어떤 정점(노드)으로 들어오는 간선(화살표)의 개수</strong>를 의미합니다.</p>
</blockquote>
<ul>
<li><p>예를 들어, <code>A → B</code>라는 간선이 있다면, 노드 B의 진입 차수는 1입니다.  </p>
</li>
<li><p>위상 정렬에서는 진입 차수가 0인 노드부터 처리합니다.</p>
</li>
<li><p><strong>사이클이 없는 방향 그래프</strong>에서는<br>항상 <strong>진입 차수가 0인 노드가 최소 1개 이상 존재</strong>합니다.</p>
</li>
<li><p>이 노드를 시작점으로 삼아 위상 정렬을 수행할 수 있습니다.</p>
</li>
</ul>
<hr>
<h2 id="3-위상-정렬-구현-방법">3. 위상 정렬 구현 방법</h2>
<h3 id="3-1-bfs-기반-방식-큐와-진입-차수를-이용">3-1. BFS 기반 방식 (큐와 진입 차수를 이용)</h3>
<pre><code class="language-java">import java.util.*;

public class TopologicalSort {
    public static void main(String[] args) {
        int n = 11;
        List&lt;List&lt;Integer&gt;&gt; graph = new ArrayList&lt;&gt;();
        int[] inDegree = new int[n + 1];

        for (int i = 0; i &lt;= n; i++) {
            graph.add(new ArrayList&lt;&gt;());
        }

        // 단방향 간선 추가 (그림에서 A ~ K까지를 1 ~ 11로 변환)
        graph.get(1).add(4); inDegree[4]++;
        graph.get(2).add(1); inDegree[1]++;
        graph.get(2).add(3); inDegree[3]++;
        graph.get(3).add(7); inDegree[7]++;
        graph.get(4).add(7); inDegree[7]++;
        graph.get(5).add(2); inDegree[2]++;
        graph.get(5).add(6); inDegree[6]++;
        graph.get(6).add(4); inDegree[4]++;
        graph.get(6).add(8); inDegree[8]++;
        graph.get(8).add(10); inDegree[10]++;
        graph.get(9).add(8); inDegree[8]++;
        graph.get(9).add(11); inDegree[11]++;
        graph.get(10).add(7); inDegree[7]++;
        graph.get(11).add(8); inDegree[8]++;

        // 특정 순서를 요구하지 않는 위상 정렬에서는 큐를 사용
        // 그림에서는 번호가 작은 노드부터 먼저 처리하기 때문에 우선 순위 큐를 사용
        Queue&lt;Integer&gt; queue = new PriorityQueue&lt;&gt;();

        for (int i = 1; i &lt;= n; i++) {
            if (inDegree[i] == 0) {
                queue.add(i);
            }
        }

        List&lt;Integer&gt; result = new ArrayList&lt;&gt;();

        while (!queue.isEmpty()) {
            int current = queue.poll();
            result.add(current);

            for (int next : graph.get(current)) {
                inDegree[next]--;
                if (inDegree[next] == 0) {
                    queue.add(next);
                }
            }
        }

        if (result.size() != n) {
            System.out.println(&quot;사이클이 존재합니다.&quot;);
        } else {
            StringBuilder sb = new StringBuilder();
            for (int num : result) {
                sb.append((char) (num + &#39;A&#39; - 1)).append(&quot; &quot;);
            }
            System.out.println(&quot;위상 정렬 결과: &quot; + sb.toString().trim());
        }
    }
}
</code></pre>
<h3 id="3-2-dfs-기반-방식">3-2. DFS 기반 방식</h3>
<pre><code class="language-java">public class DFSTopologicalSort {
    static boolean[] visited;
    static Deque&lt;Integer&gt; stack = new ArrayDeque&lt;&gt;();
    static List&lt;List&lt;Integer&gt;&gt; graph;

    public static void dfs(int node) {
        visited[node] = true;
        for (int next : graph.get(node)) {
            if (!visited[next]) {
                dfs(next);
            }
        }
        stack.push(node); // 나중에 방문한 걸 먼저 출력
    }

    public static void main(String[] args) {
        int n = 11;
        graph = new ArrayList&lt;&gt;();
        visited = new boolean[n + 1];

        for (int i = 0; i &lt;= n; i++) {
            graph.add(new ArrayList&lt;&gt;());
        }

        // 단방향 간선 추가 (그림에서 A ~ K까지를 1 ~ 11로 변환)
        graph.get(1).add(4); 
        graph.get(2).add(1); 
        graph.get(2).add(3); 
        graph.get(3).add(7); 
        graph.get(4).add(7);
        graph.get(5).add(2);
        graph.get(5).add(6);
        graph.get(6).add(4); 
        graph.get(6).add(8);
        graph.get(8).add(10);
        graph.get(9).add(8);
        graph.get(9).add(11);
        graph.get(10).add(7);
        graph.get(11).add(8);

        for (int i = 1; i &lt;= n; i++) {
            if (!visited[i]) {
                dfs(i);
            }
        }

        while (!stack.isEmpty()) {
            System.out.print(stack.pop() + &quot; &quot;);
        }
    }
}
</code></pre>
<h3 id="3-3-bfs-방식과-dfs-방식의-차이점">3-3. BFS 방식과 DFS 방식의 차이점</h3>
<p>위상 정렬을 할 때 두 방식은 같은 그래프라도 결과가 다르게 나올 수 있습니다.  </p>
<p><strong>BFS 방식</strong>에서는 진입 차수가 0인 노드들을 큐에 넣고 처리하는데,  </p>
<ul>
<li><strong>일반 큐(Queue)</strong>를 사용하면 노드가 큐에 들어온 순서대로 처리되어, 특정한 순서를 보장하지 않습니다.  </li>
<li>다만, <strong>진입 차수가 0인 노드가 항상 하나뿐이고,</strong> 큐에 담긴 노드가 처리되는 동안 <strong>항상 큐의 크기가 1이라면,</strong> 일반 큐를 쓰더라도 <strong>결과는 항상 동일</strong>하게 나옵니다. 즉, 이 경우엔 우선순위 큐를 쓰지 않아도 순서가 보장됩니다.<blockquote>
<p><code>선후 관계 문제</code>에서 <code>진입 차수가 0인 노드가 여러 개</code>일 경우, 
탐색 순서가 일관적이지 않으므로 명확한 순서를 정할 수 없다는 점을 유의해주세요.</p>
</blockquote>
</li>
<li>반면 <strong>우선순위 큐(PriorityQueue)</strong>를 사용하면 노드 번호가 작은 것부터 차례대로 처리되기 때문에, 번호 순서에 맞는 정렬 결과를 얻을 수 있습니다.</li>
<li>또한, BFS 방식은 진입 차수를 이용하기 때문에 <strong>사이클이 존재하는 경우 모든 노드를 탐색하지 못하게 되며,</strong> 이를 통해 사이클 유무를 <strong>자동으로 탐지</strong>할 수 있습니다.</li>
</ul>
<p><strong>DFS 방식</strong>은 </p>
<ul>
<li>한 방향으로 깊게 탐색하며 노드를 쌓기 때문에, 방문 순서에 따라 결과가 달라질 수 있습니다.</li>
<li>또한 사이클 탐지의 경우, 방문 상태를 3단계(미방문, 방문중, 방문완료)로 별도 관리해야 하며, 현재 방문 중인 노드를 다시 만나면 사이클이 존재한다고 판단합니다. </li>
</ul>
</br>

<p>결론적으로, <strong>BFS 방식</strong>이 <strong>위상 정렬을 구현하는 데 직관적</strong>입니다. 또한, <strong>사이클 탐지</strong>가 필요한 경우에도 BFS 방식의 구현이 더 간단합니다.</p>
<p>반면, <strong>DFS 방식</strong>은 위상 정렬 과정에서 추가적인 정보나 경로 추적 등 다양한 응용이 가능하므로, 두 방식 모두 상황에 맞게 유용하게 활용할 수 있습니다.</p>
<hr>
<h2 id="4-위상-정렬-vs-다익스트라-vs-플로이드-워셜">4. 위상 정렬 vs 다익스트라 vs 플로이드-워셜</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>위상 정렬</th>
<th>다익스트라</th>
<th>플로이드-워셜</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>순서 정하기</td>
<td>최단 거리</td>
<td>모든 경로 계산</td>
</tr>
<tr>
<td>입력 그래프</td>
<td>사이클이 없는 방향 그래프</td>
<td>양의 가중치</td>
<td>음수 가중치 허용</td>
</tr>
<tr>
<td>방식</td>
<td>큐 or DFS</td>
<td>우선순위 큐</td>
<td>3중 for문</td>
</tr>
<tr>
<td>시간 복잡도</td>
<td>O(V + E)</td>
<td>O((V + E)logV)</td>
<td>O(V³)</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-백준-문제-추천">5. 백준 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 2252</td>
<td><a href="https://www.acmicpc.net/problem/2252">줄 세우기</a></td>
<td>기본 위상 정렬</td>
</tr>
<tr>
<td>백준 1005</td>
<td><a href="https://www.acmicpc.net/problem/1005">ACM Craft</a></td>
<td>DP + 위상 정렬</td>
</tr>
<tr>
<td>백준 1516</td>
<td><a href="https://www.acmicpc.net/problem/1516">게임 개발</a></td>
<td>건설 시간 + 위상 정렬 응용</td>
</tr>
<tr>
<td>백준 3665</td>
<td><a href="https://www.acmicpc.net/problem/3665">최종 순위</a></td>
<td>위상 정렬 + 순위 변화</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>위상 정렬은 그래프가 <strong>사이클이 없을 때</strong>, 그리고 <strong>선후관계가 있을 때</strong> 사용 가능한 알고리즘입니다.  </p>
<p><strong>큐와 진입 차수를 이용한  BFS 방식</strong>은 직관적이며,<br><strong>DFS 기반 방식</strong>은 재귀를 활용해 간결하게 구현할 수 있습니다.</p>
<p>위상 정렬을 적용하기 전에는 <strong>방향 그래프</strong>에 <strong>사이클이 존재하는지 확인</strong>하는 과정이 매우 중요합니다. 사이클이 존재하면 위상 정렬이 불가능하므로, <strong>사이클 유무를 미리 체크</strong>하는 습관을 들이는 것이 좋습니다.</p>
<blockquote>
<p>👉 그래프 탐색 알고리즘이 익숙하지 않다면 [이전 포스트]들을 참고해보세요!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 플로이드-워셜(Floyd-Warshall) 완전 정복 – 모든 정점 간 최단 거리]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9CFloyd-Warshall-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%AA%A8%EB%93%A0-%EC%A0%95%EC%A0%90-%EA%B0%84-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9CFloyd-Warshall-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%AA%A8%EB%93%A0-%EC%A0%95%EC%A0%90-%EA%B0%84-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC</guid>
            <pubDate>Mon, 02 Jun 2025 06:53:50 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>플로이드-워셜(Floyd-Warshall) 알고리즘은 <strong>모든 정점에서 모든 정점까지의 최단 거리</strong>를 구할 수 있는 알고리즘입니다.</p>
<p>다익스트라는 <strong>단일 시작점</strong>에서 출발하는 최단 거리만 구할 수 있고,<br>벨만-포드는 <strong>음수 간선과 사이클 탐지</strong>에 유용하지만 <strong>단일 시작점</strong> 기준입니다.</p>
<p>반면, 플로이드-워셜은 <strong>모든 정점 쌍 간의 최단 거리</strong>를 구할 수 있어 그래프 내 모든 경로 정보를 필요로 하는 문제에서 효과적입니다.</p>
<blockquote>
<p>입력 그래프가 <strong>정점 수가 적고 간선 수가 많을 때</strong> 적합합니다.<br>간선에 <strong>음수 가중치는 허용되지만, 음수 사이클은 허용되지 않습니다.</strong></p>
</blockquote>
<hr>
<h2 id="2-플로이드-워셜이란">2. 플로이드-워셜이란?</h2>
<p>플로이드-워셜은 <strong>‘모든 정점 쌍 사이의 최단 거리’</strong>를 한 번에 구하는 방법입니다. 
<strong>각 정점을 거쳐 가는 경로</strong>를 차례대로 확인하면서 <strong>최단 거리를 갱신</strong>해 나가는 방식으로 동작합니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/2db38fa6-66ad-4356-ae35-738edbb7d835/image.gif" alt="">
 <span style="color: gray">출처: <a href="https://memgraph.com/blog/graph-search-algorithms-developers-guide" target="_blank">medium.com - Understanding Floyd-Warshall Algorithm
</a></span></p>
</br>
다음은 그림에 대한 동작 과정입니다.

<ol>
<li><p>4 x 4 크기의 dist[][] 배열을 INF 값으로 초기화합니다.</p>
</li>
<li><p>자기 자신으로 가는 비용을 0으로 설정합니다.</p>
</li>
</ol>
<blockquote>
<p>dist[i][i] = 0;</p>
</blockquote>
<ol start="3">
<li><code>k</code>, <code>i</code>, <code>j</code> 세 정점을 잡고, 현재 <code>i → j</code>로 가는 최단 거리보다 <code>i → k</code> + <code>k → j</code>를 거쳐 가는 경로가 더 짧다면, 해당 경로로 최단 거리를 <strong>갱신</strong>합니다.</li>
</ol>
<blockquote>
<p>dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);</p>
</blockquote>
<p>이 과정을 <code>k = 1 ~ n</code>까지 모든 정점에 대해 반복하면, 최종적으로 <strong>모든 정점 쌍 간의 최단 거리</strong>가 dist[][] 배열에 저장됩니다.</p>
<hr>
<h2 id="3-플로이드-워셜-구현-java">3. 플로이드-워셜 구현 (Java)</h2>
<pre><code class="language-java">import java.util.*;

public class FloydWarshallExample {
    static final int INF = Integer.MAX_VALUE / 2; // Integer.MAX_VALUE 사용하면 overflow 위험 존재

    public static void main(String[] args) {
        int n = 4; // 정점 수
        int[][] dist = new int[n][n];

        // 초기화
        for (int i = 0; i &lt; n; i++) {
            Arrays.fill(dist[i], INF);
            dist[i][i] = 0; // 자기 자신으로 가는 비용은 0
        }

        // 간선 정보 (예시: 단방향)
        // 같은 간선의 정보가 여러 개일 경우 최솟값 반영
        dist[0][1] = 1;
        dist[1][2] = 3;
        dist[0][2] = 2;
        dist[2][3] = -2;

        // 알고리즘 실행
        for (int k = 0; k &lt; n; k++) {
            for (int i = 0; i &lt; n; i++) {
                for (int j = 0; j &lt; n; j++) {
                    if (dist[i][k] != INF &amp;&amp; dist[k][j] != INF &amp;&amp; dist[i][k] + dist[k][j] &lt; dist[i][j]) {
                        dist[i][j] = dist[i][k] + dist[k][j];
                    }
                }
            }
        }

        // 출력
        for (int i = 0; i &lt; n; i++) {
            for (int j = 0; j &lt; n; j++) {
                System.out.print((dist[i][j] == INF ? &quot;INF&quot; : dist[i][j]) + &quot; &quot;);
            }
            System.out.println();
        }
    }
}
</code></pre>
<h3 id="3-1-구현-핵심-요약">3-1. 구현 핵심 요약</h3>
<p>플로이드-워셜 알고리즘을 구현할 때 핵심이 되는 흐름은 다음과 같습니다.</p>
<ol>
<li><code>dist[i][j]</code> 배열을 만들어, <strong>정점 i에서 j로 가는 최단 거리</strong>를 저장합니다.<br>초기에는 i에서 j로 가는 간선이 있다면 그 가중치로, 없다면 <strong>충분히 큰 값(INF)</strong>으로 설정합니다.
여기서 INF 값으로 <code>Integer.MAX_VALUE</code>를 사용하면 덧셈 과정에서 오버플로우가 발생할 수 있으므로,  </li>
</ol>
<p><strong>INF는 <code>Integer.MAX_VALUE / 2</code> 정도로 큰 값</strong>으로 사용합니다.</p>
<ol start="2">
<li><p>세 정점 i, j, k를 잡고, 정점 k를 거쳐 i에서 j로 가는 경로가<br>현재 알고 있는 최단 거리보다 더 짧은지 확인하면서 거리를 업데이트합니다.<br>이 과정을 다음과 같은 식으로 표현할 수 있습니다:
<code>dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])</code></p>
<ol start="3">
<li>위 작업을 모든 정점 쌍 <code>(i, j)</code>에 대해, 그리고 모든 정점 <code>k</code>에 대해 반복합니다.  </li>
</ol>
</li>
</ol>
<p>이 과정을 통해, <strong>모든 정점 쌍 간의 최단 거리</strong>를 효율적으로 구할 수 있으며, <strong>음수 가중치가 있어도 동작한다</strong>는 장점이 있습니다.</p>
<hr>
<h2 id="4-다익스트라-vs-벨만-포드-vs-플로이드-워셜">4. 다익스트라 vs 벨만-포드 vs 플로이드-워셜</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>다익스트라</th>
<th>벨만-포드</th>
<th>플로이드-워셜</th>
</tr>
</thead>
<tbody><tr>
<td>출발점</td>
<td>단일</td>
<td>단일</td>
<td><strong>모든 정점 쌍</strong></td>
</tr>
<tr>
<td>음수 가중치</td>
<td>X</td>
<td>O</td>
<td>O</td>
</tr>
<tr>
<td>음수 사이클 탐지</td>
<td>X</td>
<td>O</td>
<td>O (<code>dist[i][i] &lt; 0 체크</code>)</td>
</tr>
<tr>
<td>시간 복잡도</td>
<td>O((V + E) log V)</td>
<td>O(V × E)</td>
<td>O(V³)</td>
</tr>
<tr>
<td>적합한 그래프</td>
<td>희소 그래프</td>
<td>음수 포함 그래프</td>
<td>정점 수 적고 간선 많은 그래프</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-백준-문제-추천">5. 백준 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 11404</td>
<td><a href="https://www.acmicpc.net/problem/11404">플로이드</a></td>
<td>기본적인 모든 쌍 최단 거리 문제</td>
</tr>
<tr>
<td>백준 1956</td>
<td><a href="https://www.acmicpc.net/problem/1956">운동</a></td>
<td>음수 사이클 검출 (플로이드-워셜 응용)</td>
</tr>
<tr>
<td>백준 11780</td>
<td><a href="https://www.acmicpc.net/problem/11780">플로이드 2</a></td>
<td>경로 복원</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>플로이드-워셜은 <strong>모든 정점 쌍 간의 최단 경로를 한 번에 구할 수 있는 강력한 알고리즘</strong>입니다. 구현이 간단하면서도, 음수 간선까지 처리할 수 있다는 장점이 있어 <strong>그래프 전체의 경로 정보를 빠르게 파악해야 하는 상황</strong>에서 유용합니다.  </p>
<p>하지만 시간 복잡도가 $O(N^3)$으로, <strong>정점의 수가 많아질수록 성능이 급격히 저하</strong>될 수 있기 때문에 상황에 따라 <strong>다익스트라, 벨만-포드, 플로이드-워셜 중 가장 적절한 알고리즘을 선택하는 것이 중요</strong>합니다.</p>
<blockquote>
<p>👉 다익스트라와 벨만-포드 알고리즘이 익숙하지 않다면 [이전 포스트]를 참고해 주세요!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 벨만-포드(Bellman-Ford) 완전 정복 - 최단 거리 알고리즘의 핵심 (음수 가중치와 사이클 탐지까지)

]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9CBellman-Ford-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9D%8C%EC%88%98-%EA%B0%80%EC%A4%91%EC%B9%98%EC%99%80-%EC%82%AC%EC%9D%B4%ED%81%B4-%ED%83%90%EC%A7%80%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EB%B2%A8%EB%A7%8C-%ED%8F%AC%EB%93%9CBellman-Ford-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9D%8C%EC%88%98-%EA%B0%80%EC%A4%91%EC%B9%98%EC%99%80-%EC%82%AC%EC%9D%B4%ED%81%B4-%ED%83%90%EC%A7%80%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Sat, 31 May 2025 08:39:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>벨만-포드(Bellman-Ford) 알고리즘은 <strong>음의 가중치</strong>가 포함된 그래프에서도 <strong>최단 거리</strong>를 구할 수 있는 알고리즘입니다.  </p>
<p>다익스트라는 가중치가 <strong>모두 양수</strong>일 때만 동작하지만, 벨만-포드는 <strong>음수 간선이 있어도</strong> 사용 가능합니다. 또한 <strong>음수 사이클</strong>(계속 돌면 비용이 줄어드는 경우)도 판별할 수 있어, 다양한 문제에서 유용하게 활용됩니다.</p>
<p>이 글에서는 벨만-포드 알고리즘의 개념, 구현, 다익스트라와의 차이점, 그리고 실전 문제 활용까지 단계별로 정리해보겠습니다.</p>
<hr>
<h2 id="2-벨만-포드란">2. 벨만-포드란?</h2>
<p>벨만-포드는 <strong>모든 간선 정보를 반복적으로 확인하면서</strong> 최단 거리를 갱신하는 방식입니다. </p>
<blockquote>
</blockquote>
<p>&quot;최대 (정점 수 - 1)번인 모든 간선을 반복적으로 확인하면, 최단 거리를 보장할 수 있다.&quot; 를 전제로 합니다.</p>
<p>마지막 반복 이후에도 최단 거리가 갱신된다면, <strong>음수 사이클이 존재함을 탐지</strong>할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/3f7c2c12-b010-45e6-bddf-562c6c2f7604/image.gif" alt=""> <span style="color: gray">출처: <a href="https://memgraph.com/blog/graph-search-algorithms-developers-guide" target="_blank">memgraph.com - Graph Search Algorithms: Developer&#39;s Guide
</a></span></p>
</br>

<p>아래는 그림과 같이 <strong>최단 거리</strong>를 구하는 과정입니다.</p>
<ol>
<li><strong>시작 정점 A의 거리를 0으로 설정</strong>하고, <strong>나머지 정점들은 모두 무한대(∞)</strong>로 초기화합니다.  </li>
</ol>
<blockquote>
<p>최단 거리 배열은 <strong>A:0, B:∞, C:∞, D:∞, E:∞</strong>가 됩니다.</p>
</blockquote>
<ol start="2">
<li><strong>첫 번째 반복</strong>입니다.<br>모든 간선을 차례로 검사하며 최단 거리를 갱신합니다.  </li>
</ol>
<ul>
<li>A → B 간선 검사: B까지 거리를 0 + 4 = 4로 갱신합니다.  </li>
<li>D → A 간선 검사: A의 최단 거리는 0이므로 변화가 없습니다.  </li>
<li>B → D 간선 검사: D까지 최단 거리를 4 + (-1) = 3으로 갱신합니다.  </li>
<li>D → E 간선 검사: E까지 최단 거리를 3 + 8 = 11로 갱신합니다.  </li>
<li>B → E 간선 검사: E까지 최단 거리를 4 + (-3) = 1로 갱신합니다.</li>
<li>C → B 간선 검사: B의 최단 거리 4보다 크므로 변화가 없습니다.  </li>
<li>E → C 간선 검사: C까지 최단 거리를 1 + 6 = 7로 갱신합니다.  </li>
</ul>
<blockquote>
<p>첫 번째 반복 후 최단 거리 배열은 <strong>A:0, B:4, C:7, D:3, E:1</strong>이 됩니다.</p>
</blockquote>
<ol start="3">
<li><p><strong>두 번째 반복</strong>입니다.<br>모든 간선을 다시 검사하지만, 거리 배열에 변화가 없습니다.<br>이후 반복(<strong>세 번째, 네 번째</strong>)에서도 거리가 더 이상 갱신되지 않습니다.</p>
</li>
<li><p>마지막 반복(네 번째)을 마친 뒤 <strong>음수 사이클을 탐지</strong>합니다.
한 번 더 모든 간선을 검사하여 거리 갱신 여부를 확인합니다.  </p>
</li>
</ol>
<p><strong>갱신이 없으므로, 음수 사이클이 존재하지 않음을 확인</strong>할 수 있습니다.</p>
<blockquote>
<p>최종적으로 각 정점까지의 최단 거리는  <strong>A:0, B:4, C:7, D:3, E:1</strong> 로 계산됩니다.</p>
</blockquote>
<hr>
<h3 id="2-1-벨만-포드를-이해하려면">2-1. 벨만-포드를 이해하려면?</h3>
<p>벨만-포드는 <strong>음수 가중치가 있는 그래프</strong>에서 <strong>최단 거리</strong>를 찾을 수 있는 알고리즘입니다.
이를 정확히 이해하려면 아래와 같은 개념을 알고 있어야 합니다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>정점 (Vertex)</td>
<td>이동의 대상이 되는 지점</td>
</tr>
<tr>
<td>간선 (Edge)</td>
<td>정점 간 연결된 길</td>
</tr>
<tr>
<td>가중치 (Weight)</td>
<td>간선을 지나가는 데 드는 비용</td>
</tr>
<tr>
<td>최단 거리 배열 (Distance Array)</td>
<td>각 정점까지의 최소 비용 저장</td>
</tr>
<tr>
<td>음수 사이클</td>
<td>반복해서 돌면 비용이 계속 감소하는 경로</td>
</tr>
</tbody></table>
<p>벨만-포드는 이 개념들을 바탕으로 <strong>모든 간선을 여러 번 반복해서 확인</strong>하며, 각 정점까지의 최단 거리를 점차 갱신해 나가는 방식으로 동작합니다.  </p>
<p>최단 경로는 최대 <strong>(정점 수 - 1)</strong> 개의 간선으로 이루어질 수 있기 때문에, 
모든 간선을 <strong>(정점 수 - 1)</strong> 번 반복해서 검사하며 최단 거리 배열을 업데이트합니다.
이 과정에서 더 짧은 경로가 발견되면 즉시 최단 거리를 갱신합니다. </p>
<p>만약 마지막 반복 후에도 최단 거리 갱신이 일어난다면, 이는 <strong>음수 사이클이 존재한다는 뜻</strong>으로, 최단 거리가 무한히 작아질 수 있어 정확한 결과를 구할 수 없음을 의미합니다.  </p>
<p>벨만-포드는 이러한 <strong>음수 사이클의 존재 여부</strong>를 판단할 수 있는 기능을 갖추고 있습니다.</p>
<h3 id="2-2-벨만-포드의-특징">2-2. 벨만-포드의 특징</h3>
<ul>
<li><p>벨만-포드는 <strong>음수 가중치가 포함된 그래프</strong>에서 정확하게 <strong>최단 거리</strong>를 계산할 수 있습니다. </p>
</li>
<li><p><strong>음수 사이클</strong>이 있으면 최단 거리가 무한히 작아져서 정의될 수 없는데, 벨만-포드는 이를 <strong>감지</strong>할 수 있습니다.</p>
</li>
<li><p>다익스트라 알고리즘에 비해 구현이 좀 더 복잡하고 연산 시간이 더 오래 걸리지만, <strong>음수 가중치</strong>가 포함된 상황에서는 필수적인 알고리즘입니다.</p>
</li>
</ul>
<hr>
<h2 id="3-벨만-포드-구현-java">3. 벨만-포드 구현 (Java)</h2>
<pre><code class="language-java">import java.util.*;

public class BellmanFordExample {
    static class Edge {
        int from, to, cost;

        Edge(int from, int to, int cost) {
            this.from = from;
            this.to = to;
            this.cost = cost;
        }
    }

    static final int INF = Integer.MAX_VALUE;
    static int n, m;
    static List&lt;Edge&gt; edges;
    static long[] dist;

    public static void main(String[] args) {
        n = 5; // 정점 수 (A~E)
        m = 7; // 간선 수

        edges = new ArrayList&lt;&gt;();
        dist = new long[n + 1];

        // 거리 초기화
        Arrays.fill(dist, INF);
        dist[1] = 0; // 시작 노드: A (1번)

        // 간선 정보 (from, to, cost)
        edges.add(new Edge(1, 2, 4));   // A -&gt; B (4)
        edges.add(new Edge(4, 1, 2));   // D -&gt; A (2)
        edges.add(new Edge(2, 4, -1));  // B -&gt; D (-1)
        edges.add(new Edge(4, 5, 8));   // D -&gt; E (8)
        edges.add(new Edge(2, 5, -3));  // B -&gt; E (-3)
        edges.add(new Edge(3, 2, 3));   // C -&gt; B (3)
        edges.add(new Edge(5, 3, 6));   // E -&gt; C (6)

        boolean hasCycle = bellmanFord(1); // 시작 노드 A (1번)

        if (hasCycle) {
            System.out.println(&quot;음수 사이클이 존재합니다.&quot;);
        } else {
            for (int i = 1; i &lt;= n; i++) {
                System.out.println(i + &quot;번 노드까지 최단 거리: &quot; + (dist[i] == INF ? &quot;도달 불가&quot; : dist[i]));
            }
        }
    }

    static boolean bellmanFord(int start) {
        for (int i = 0; i &lt; n - 1; i++) {
            for (Edge e : edges) {
                if (dist[e.from] != INF &amp;&amp; dist[e.to] &gt; dist[e.from] + e.cost) {
                    dist[e.to] = dist[e.from] + e.cost;
                }
            }
        }

        // n번째 반복에서 갱신되면 음수 사이클 존재
        for (Edge e : edges) {
            if (dist[e.from] != INF &amp;&amp; dist[e.to] &gt; dist[e.from] + e.cost) {
                return true;
            }
        }

        return false;
    }
}</code></pre>
<h3 id="3-1-구현-핵심-요약">3-1. 구현 핵심 요약</h3>
<p>벨만-포드 알고리즘을 구현할 때 핵심이 되는 흐름은 다음과 같습니다.</p>
<ol>
<li><p><strong>최단 거리 배열을 무한대(INF)로 초기화합니다.</strong><br>이는 아직 어떤 노드에도 도달하지 않았음을 의미합니다.</p>
</li>
<li><p><strong>시작 노드의 최단 거리를 0으로 설정합니다.</strong></p>
</li>
<li><p><strong>전체 간선을 V-1번 반복하여 확인합니다.</strong><br>각 반복마다 모든 간선을 순회하며,<br><code>현재 노드를 거쳐 가는 경로가 더 짧은 경우</code> 최단 거리를 갱신합니다.</p>
</li>
<li><p><strong>V번째 반복에서 갱신이 발생하는지 확인합니다.</strong><br>이 단계에서 <strong>거리가 또 갱신된다면</strong>, 이는 <strong>음수 사이클이 존재</strong>함을 의미합니다.</p>
</li>
</ol>
<p>이러한 과정을 통해, 출발 노드에서 모든 노드까지의 최단 거리를 구할 수 있으며,<br><strong>음수 가중치가 포함된 그래프</strong>에서도 사용할 수 있다는 점이 특징입니다.</p>
<hr>
<h2 id="4-다익스트라-vs-벨만-포드">4. 다익스트라 vs 벨만-포드</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>다익스트라</th>
<th>벨만-포드</th>
</tr>
</thead>
<tbody><tr>
<td>음수 간선</td>
<td>X (음수x, 간선x)</td>
<td>O</td>
</tr>
<tr>
<td>음수 사이클 판별</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>시간 복잡도</td>
<td>O((V+E) log V)</td>
<td>O(V×E)</td>
</tr>
<tr>
<td>사용 자료구조</td>
<td>우선순위 큐</td>
<td>리스트</td>
</tr>
<tr>
<td>특징</td>
<td>빠르고 효율적</td>
<td>범용적, 느림</td>
</tr>
<tr>
<td>활용</td>
<td>비용 최소, 내비게이션</td>
<td>사이클 검출, 금융 그래프 등</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-백준-문제-추천">5. 백준 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 11657</td>
<td><a href="https://www.acmicpc.net/problem/11657">타임머신</a></td>
<td>음수 가중치 간선 + 음수 사이클 탐지</td>
</tr>
<tr>
<td>백준 1738</td>
<td><a href="https://www.acmicpc.net/problem/1738">골목길</a></td>
<td>벨만-포드 + DFS</td>
</tr>
<tr>
<td>백준 1865</td>
<td><a href="https://www.acmicpc.net/problem/1865">웜홀</a></td>
<td>음수 사이클 탐지</td>
</tr>
<tr>
<td>백준 1219</td>
<td><a href="https://www.acmicpc.net/problem/1219">오민식의 고민</a></td>
<td>음수 가중치 간선 + 음수 사이클 탐지</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p><strong>벨만-포드</strong>는 <strong>음수 간선과 음수 사이클</strong>까지 고려할 수 있는 유일한 <strong>최단 경로</strong> 알고리즘입니다.</p>
<p>다익스트라에 비해 느리지만, <strong>음수 가중치</strong>가 있는 복잡한 그래프 문제에서는 벨만-포드 알고리즘을 사용해야 합니다.</p>
<p>📌 <strong>벨만-포드</strong>를 정확히 이해하고 구현할 수 있다면, 음수 가중치와 음수 사이클 탐지 등 다양한 그래프 문제에서 응용력을 크게 높일 수 있을 것입니다.</p>
<blockquote>
<p>👉 아직 다익스트라가 익숙하지 않다면, 이전 [다익스트라 완전 정복 포스트]를 참고해 보세요!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] 다익스트라(Dijkstra) 완전 정복 - 최단 거리 알고리즘의 핵심]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BCDijkstra-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%ED%95%B5%EC%8B%AC</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-%EB%8B%A4%EC%9D%B5%EC%8A%A4%ED%8A%B8%EB%9D%BCDijkstra-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EC%B5%9C%EB%8B%A8-%EA%B1%B0%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%98-%ED%95%B5%EC%8B%AC</guid>
            <pubDate>Sat, 24 May 2025 06:40:54 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p>다익스트라(Dijkstra)는 <strong>가중치가 있는 그래프</strong>에서 한 정점에서 다른 모든 정점까지의 <strong>최단 거리</strong>를 구하는 데 가장 널리 쓰이는 알고리즘입니다.<br>BFS와는 다르게 <strong>간선에 가중치가 있을 때</strong>도 사용 가능하며, 코딩 테스트에서  자주 등장합니다.</p>
<blockquote>
<p>이번 포스팅에서는 <strong>다익스트라 알고리즘의 개념, 구현, BFS와의 차이점, 그리고 실전 문제 활용</strong>까지 단계별로 살펴보겠습니다</p>
</blockquote>
<hr>
<h2 id="2-다익스트라란">2. 다익스트라란?</h2>
<p><strong>다익스트라</strong>는 <strong>우선순위 큐(최소 힙)</strong> 를 활용해 <strong>가중치</strong>가 있는 그래프의 <strong>최단 거리</strong>를 효율적으로 계산하는 알고리즘입니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/7278ced6-d3d5-4eb7-98bf-90dd83dd22b5/image.gif" alt=""> <span style="color: gray">출처: <a href="https://medium.com/analytics-vidhya/a-quick-explanation-of-dfs-bfs-depth-first-search-breadth-first-search-b9ef4caf952c" target="_blank">medium.com - Path Finding Algorithms
</a></span></p>
<blockquote>
<p>예시) 어떤 도시에서 출발해 다른 도시로 이동한다고 할 때, 
각 <strong>길(간선)</strong>의 <strong>거리(가중치)</strong>가 다를 수 있습니다.<br>이때 <strong>가장 적은 비용</strong>으로 목표 지점에 도달하는 <strong>경로</strong>를 찾는 것이 바로 <strong>다익스트라</strong>입니다.</p>
</blockquote>
<p>아래는 그림과 같이 <strong>최단 거리</strong>를 구하는 과정입니다.</p>
<ol>
<li><p>먼저, 시작 노드인 <strong>A에서 출발</strong>합니다.
A에서 갈 수 있는 노드는 B(거리 4), C(거리 1)입니다.
B까지의 최단 거리를 4, C까지의 최단 거리를 1로 저장합니다.</p>
</li>
<li><p>다음으로, <strong>시작점에서 가장 가까운 노드인 C(거리 1)</strong>를 선택합니다.
C에서 연결된 노드는 D(거리 2), F(거리 6)입니다.
A → C → D는 거리 1 + 2 = 3이므로, D까지의 최단 거리를 3으로 저장합니다.
A → C → F는 거리 1 + 6 = 7이므로, F까지의 최단 거리를 7로 저장합니다.</p>
</li>
<li><p><strong>C 노드에서 가까운 거리로 도달 가능한 D</strong>를 방문합니다.
D에서는 E까지의 거리는 4입니다.
A → C → D → E의 경로는 거리 3 + 4 = 7이므로, E까지의 최단 거리를 7로 저장합니다.</p>
</li>
<li><p>이제 <strong>A -&gt; B(거리 4)</strong>를 방문합니다.
B에서 갈 수 있는 노드는 D(거리 3), E(거리 8)입니다.
하지만 D는 이미 최단 거리가 3으로 저장되어 있으므로, 갱신할 필요가 없습니다.
E 역시 최단 거리가 7으로 저장되어 있기 때문에 그대로 둡니다.</p>
</li>
</ol>
<p>*<em>A -&gt; B -&gt; D와 A -&gt; B -&gt; E의 경로는 
D, E 까지 도달하는 모든 경로 중에서 최단 거리가 아니므로, 
A -&gt; B를 거치는 모든 경로는 최단 경로에서 제외됩니다. *</em></p>
<ol start="5">
<li><p>이후에는 <strong>D에서 가까운 E(거리 7)</strong>를 방문합니다.
E에서는 G(거리 2)로 갈 수 있습니다.
A → C → D → E → G의 경로는 거리 7 + 2 = 9이므로, G까지의 최단 거리는 9로 저장됩니다.</p>
</li>
<li><p>마지막으로 <strong>F(거리 7)</strong>를 방문합니다.
F에서도 G(거리 8)로 갈 수 있지만, A → C → F → G는 거리 7 + 8 = 15로,
이미 구한 G까지의 거리 9보다 크기 때문에 최단 거리를 갱신하지 않습니다.</p>
</li>
</ol>
<h3 id="2-1-다익스트라를-이해하려면">2-1. 다익스트라를 이해하려면?</h3>
<p>다익스트라는 <strong>가중치가 있는 그래프</strong>에서 <strong>최단 거리</strong>를 구하는 알고리즘입니다.
이를 정확히 이해하려면 아래와 같은 개념을 알고 있어야 합니다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>정점 (Node, Vertex)</td>
<td>이동의 대상이 되는 지점 (도시, 장소 등)</td>
</tr>
<tr>
<td>간선 (Edge)</td>
<td>정점 간 연결된 길</td>
</tr>
<tr>
<td>가중치 (Weight)</td>
<td>간선을 지나가는 데 드는 비용 (거리, 시간 등)</td>
</tr>
<tr>
<td>최단 거리 배열 (Distance Array)</td>
<td>각 정점까지의 최소 비용을 저장</td>
</tr>
<tr>
<td>우선순위 큐 (Priority Queue)</td>
<td>최소 비용 노드를 빠르게 선택하기 위한 자료구조</td>
</tr>
</tbody></table>
<p>다익스트라는 이 개념들을 바탕으로 <strong>우선순위 큐</strong>를 이용해 <strong>가장 적은 비용</strong>부터 <strong>하나씩 탐색</strong>하며, 각 경로를 지나면서 <strong>더 짧은 거리가 발견</strong>되면 즉시 <strong>갱신</strong>하는 방식으로 동작합니다.</p>
<h3 id="2-2-다익스트라의-특징">2-2. 다익스트라의 특징</h3>
<ul>
<li><p>다익스트라는 <strong>간선의 가중치가 음수가 아닌 그래프</strong>에서만 정확하게 동작합니다.<br>음수 가중치가 포함된 경우에는 벨만-포드 알고리즘을 사용해야 합니다.</p>
</li>
<li><p>BFS보다 알고리즘 구조는 더 복잡하지만, <strong>정확한 비용 기반의 거리 계산이 가능</strong>합니다.</p>
</li>
<li><p><strong>한 번의 실행으로 모든 정점까지의 최단 거리를 계산</strong>할 수 있어, 다양한 경로 탐색 문제에서 유용하게 활용됩니다.</p>
</li>
</ul>
<hr>
<h2 id="3-다익스트라-구현-방법-java">3. 다익스트라 구현 방법 (Java)</h2>
<pre><code class="language-java">import java.util.*;

public class DijkstraExample {
    static class Node implements Comparable&lt;Node&gt; {
        int index, cost;

        Node(int index, int cost) {
            this.index = index;
            this.cost = cost;
        }

        @Override
        public int compareTo(Node other) {
            return Integer.compare(this.cost, other.cost); // 비용이 적은 순으로 정렬
        }
    }

    static List&lt;List&lt;Node&gt;&gt; graph;
    static int[] dist;
    static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        int n = 7; // A, B, C, D, E, F, G 7개 노드

        graph = new ArrayList&lt;&gt;();
        dist = new int[n + 1];

        for (int i = 0; i &lt;= n; i++) {
            graph.add(new ArrayList&lt;&gt;());
            dist[i] = INF;
        }

        // 단방향 그래프에서의 간선 정보 추가
        // A부터 G까지의 각 노드를 인덱스 1~7으로 적용
        graph.get(1).add(new Node(2, 4));
        graph.get(1).add(new Node(3, 1));
        graph.get(2).add(new Node(4, 3));
        graph.get(2).add(new Node(5, 8));
        graph.get(3).add(new Node(4, 2));
        graph.get(3).add(new Node(6, 6));
        graph.get(4).add(new Node(5, 4));
        graph.get(5).add(new Node(7, 2));
        graph.get(6).add(new Node(7, 8));

        dijkstra(1); // 1번 노드에서 시작

        for (int i = 1; i &lt;= n; i++) {
            System.out.println(i + &quot;번 노드까지 최단 거리: &quot; + (dist[i] == INF ? &quot;도달 불가&quot; : dist[i]));
        }
    }

    static void dijkstra(int start) {
        PriorityQueue&lt;Node&gt; pq = new PriorityQueue&lt;&gt;();
        dist[start] = 0;
        pq.offer(new Node(start, 0));

        while (!pq.isEmpty()) {
            Node current = pq.poll();

            // 이미 더 짧은 경로로 방문된 경우 무시
            if (current.cost &gt; dist[current.index]) continue;

            for (Node neighbor : graph.get(current.index)) {
                int newCost = dist[current.index] + neighbor.cost;
                if (newCost &lt; dist[neighbor.index]) {
                    dist[neighbor.index] = newCost;
                    pq.offer(new Node(neighbor.index, newCost));
                }
            }
        }
    }
}</code></pre>
<h3 id="3-1-구현-핵심-요약">3-1. 구현 핵심 요약</h3>
<p>다익스트라 알고리즘을 구현할 때 핵심이 되는 흐름은 다음과 같습니다.</p>
<ol>
<li><p><strong>최단 거리 배열을 무한대(INF)로 초기화</strong>합니다.<br>이는 아직 어떤 노드에도 도달하지 않았음을 의미합니다.</p>
</li>
<li><p><strong>시작 노드의 최단 거리를 0으로 설정</strong>하고, 우선순위 큐에 넣습니다.</p>
</li>
<li><p>이후, <strong>우선순위 큐에서 가장 비용이 적은 노드를 꺼내어</strong> 해당 노드의 인접 노드들을 확인합니다.</p>
</li>
<li><p>현재 노드를 거쳐 인접 노드로 가는 비용이 <strong>기존에 기록된 최단 거리보다 작으면</strong>, 해당 <strong>값을 갱신</strong>하고 <strong>인접 노드를 큐</strong>에 다시 넣습니다.</p>
</li>
<li><p>이러한 과정을 큐가 빌 때까지 반복하면, <strong>출발 노드에서 모든 노드까지의 최단 거리</strong>를 구할 수 있습니다.</p>
</li>
</ol>
<hr>
<h2 id="4-bfs-vs-다익스트라">4. BFS vs 다익스트라</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>BFS</th>
<th>다익스트라</th>
</tr>
</thead>
<tbody><tr>
<td>탐색 대상</td>
<td>가중치 없는 그래프</td>
<td>가중치 있는 그래프</td>
</tr>
<tr>
<td>자료구조</td>
<td>큐 (Queue)</td>
<td>우선순위 큐 (PriorityQueue)</td>
</tr>
<tr>
<td>특징</td>
<td>거리 = 간선 수</td>
<td>거리 = 누적 가중치</td>
</tr>
<tr>
<td>시간 복잡도</td>
<td>O(V + E)</td>
<td>O((V + E) log V)</td>
</tr>
<tr>
<td>활용</td>
<td>최단 간선 수, 미로, 단계 탐색</td>
<td>최소 비용 경로, 이동 시간 계산</td>
</tr>
</tbody></table>
<blockquote>
<p>참고: <code>V = 정점(노드)의 수</code>, <code>E = 간선의 수</code></p>
</blockquote>
<hr>
<h2 id="5-백준--프로그래머스-문제-추천">5. 백준 &amp; 프로그래머스 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 1753</td>
<td><a href="https://www.acmicpc.net/problem/1753">최단경로</a></td>
<td>다익스트라 기본 구현</td>
</tr>
<tr>
<td>백준 1916</td>
<td><a href="https://www.acmicpc.net/problem/1916">최소비용 구하기</a></td>
<td>최소 비용만 출력</td>
</tr>
<tr>
<td>백준 1504</td>
<td><a href="https://www.acmicpc.net/problem/1504">특정한 최단 경로</a></td>
<td>경유지 경로 계산</td>
</tr>
<tr>
<td>프로그래머스</td>
<td><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12978">배달</a></td>
<td>음식 배달 최소 시간 계산</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>다익스트라는 <strong>가중치 그래프</strong>에서 <strong>최단 거리</strong>를 구하는 데 가장 적합한 알고리즘입니다.    </p>
<p>BFS와 비교해 조금 더 복잡하지만, 문제 해결 능력을 높이기 위해 <strong>반드시 익혀야 할 알고리즘</strong>입니다.</p>
<p>📌 <strong>다익스트라</strong>를 정확히 이해하고 구현할 수 있다면, 이후 <strong>벨만-포드</strong>, <strong>플로이드-워셜</strong> 등 다른 최단 경로 알고리즘도 쉽게 접근할 수 있을 것입니다.</p>
<blockquote>
<p>👉 아직 BFS가 익숙하지 않다면, 이전 [BFS 완전 정복 포스트]를 참고해 보세요!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] BFS 완전 정복 - 너비 우선 탐색 (DFS와의 차이, 활용까지)]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-BFS-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89-%ED%81%90-%ED%99%9C%EC%9A%A9-DFS%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-BFS-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89-%ED%81%90-%ED%99%9C%EC%9A%A9-DFS%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Fri, 09 May 2025 14:24:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p><strong>BFS(Breadth-First Search)</strong>는 DFS와 함께 가장 기초이자 중요한 그래프 탐색 알고리즘입니다.<br><strong>최단 거리 문제</strong>, <strong>단계별 탐색</strong>, <strong>미로 탈출</strong> 등에서 필수적으로 활용되며, DFS와는 다른 방식으로 동작합니다.</p>
<p>이 글에서는 <strong>BFS의 개념부터 구현</strong>, <strong>DFS와의 차이</strong>, <strong>실전 문제 활용</strong>까지 정리해 보겠습니다.</p>
<hr>
<h2 id="2-bfs란">2. BFS란?</h2>
<p>BFS는 그래프를 <strong>너비 우선</strong>으로 탐색하는 알고리즘입니다.<br>즉, 시작 정점에서 <strong>가까운 노드부터</strong> 먼저 방문한 뒤, 점점 멀리 있는 노드를 탐색합니다.<br>이를 위해 <strong>큐(Queue)</strong> 자료구조를 사용하여 탐색 순서를 관리합니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/93e98bdd-05c5-4797-8ead-f2472d023aaa/image.gif" alt=""> <span style="color: gray">출처: <a href="https://medium.com/analytics-vidhya/a-quick-explanation-of-dfs-bfs-depth-first-search-breadth-first-search-b9ef4caf952c" target="_blank">medium.com - A quick explanation of DFS &amp; BFS</a></span></p>
<blockquote>
<p>예시)  미로에서 <strong>출발 지점에서 가까운 길</strong>부터 <strong>차례대로 살펴보는 방식</strong>과 유사합니다. 
먼저 한 칸 거리의 길을 전부 확인하고, 그 다음 두 칸 거리, 세 칸 거리 순으로 탐색을 확장해 나갑니다. 출구에 도달했을 때 그 경로는 최단 경로가 됩니다.</p>
</blockquote>
<h3 id="2-1-bfs를-이해하려면">2-1. BFS를 이해하려면?</h3>
<p>BFS는 그래프에서 <strong>너비를 우선으로 탐색</strong>하는 알고리즘입니다. 
이를 이해하기 위해서는 먼저 <strong>그래프</strong>와 관련된 기본 개념을 알고 있어야 합니다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>정점 (Node, Vertex)</strong></td>
<td>탐색의 대상이 되는 지점 또는 위치 (예: 방, 도시, 사람 등)</td>
</tr>
<tr>
<td><strong>간선 (Edge)</strong></td>
<td>정점과 정점을 연결하는 선 (예: 길, 통로, 관계 등)</td>
</tr>
<tr>
<td><strong>인접 리스트 (Adjacency List)</strong></td>
<td>각 정점에 연결된 다른 정점들을 리스트로 저장하는 자료구조</td>
</tr>
<tr>
<td><strong>방문 배열 (Visited Array)</strong></td>
<td>각 정점을 한 번만 방문하도록 체크하는 배열</td>
</tr>
<tr>
<td><strong>큐 (Queue)</strong></td>
<td>BFS의 핵심 자료구조로, 먼저 들어온 데이터를 먼저 꺼내는 선입선출(FIFO) 방식</td>
</tr>
</tbody></table>
<p>BFS는 이 개념들을 바탕으로 <strong>큐를 이용해 인접한 정점들을 순서대로 방문</strong>합니다.  </p>
<hr>
<h2 id="3-bfs-구현-방법-java">3. BFS 구현 방법 (Java)</h2>
<pre><code class="language-java">import java.util.*;

public class BFSExample {
    static List&lt;Integer&gt;[] graph;
    static boolean[] visited;

    public static void main(String[] args) {
        int n = 9;
        graph = new ArrayList[n + 1];
        visited = new boolean[n + 1];

        for (int i = 1; i &lt;= n; i++) {
            graph[i] = new ArrayList&lt;&gt;();
        }

        // 간선 추가
        graph[1].addAll(List.of(2, 3, 4));
        graph[2].addAll(List.of(1, 5));
        graph[3].addAll(List.of(1, 6, 7));
        graph[4].addAll(List.of(1, 8));
        graph[5].add(2);
        graph[6].addAll(List.of(3, 9));
        graph[7].add(3);
        graph[8].add(4);
        graph[9].add(6);

        bfs(1); // 1번 노드부터 탐색 시작
    }

    static void bfs(int start) {
        Deque&lt;Integer&gt; queue = new ArrayDeque&lt;&gt;();
        queue.addLast(start);
        visited[start] = true;

        while (!queue.isEmpty()) {
            int node = queue.pollFirst();
            System.out.print(node + &quot; &quot;);

            List&lt;Integer&gt; neighbors = graph[node];
            Collections.sort(neighbors); // 작은 번호부터 방문하도록 정렬

            for (int next : graph[node]) {
                if (!visited[next]) {
                    visited[next] = true;
                    queue.addLast(next);
                }
            }
        }
    }
}</code></pre>
<blockquote>
<p>아래는 <strong>BFS 구현 동작 과정</strong>입니다.</p>
</blockquote>
<ol>
<li>시작 노드부터 <strong>가까운 노드</strong>부터 차례대로 탐색합니다.</li>
<li><strong>큐(Queue)</strong> 자료구조를 사용하여 탐색 순서를 보장합니다. (큐는 먼저 들어온 노드가 먼저 나오는 <strong>선입선출 방식</strong>이므로, <strong>최단 거리</strong>나 <strong>단계별 탐색</strong>에 유용합니다.)</li>
<li>각 노드는 <strong>한 번만 방문</strong>되며, 방문한 노드는 다시 큐에 넣지 않습니다.</li>
<li>탐색이 끝날 때까지 <strong>큐에 노드가 남아 있다면</strong> <strong>다음 인접 노드를 탐색</strong>합니다.<ul>
<li>4-1. 만약 <strong>큐에 노드가 남아 있지만, 이미 원하는 조건(예: 최단 거리)이 만족되었다면</strong>, 더 이상 탐색을 진행하지 않고 <strong>탐색을 멈춥니다</strong>.</li>
</ul>
</li>
</ol>
<p>💡 <strong>큐(Queue)</strong> 덕분에 <strong>단계별 탐색</strong>이 자연스럽게 이루어지며, 탐색 순서가 확실히 보장됩니다.</p>
<p>❗ <strong>단점</strong>: 큐에 저장할 노드가 많으면 <strong>메모리 사용량</strong>이 많아질 수 있습니다.</p>
<h3 id="3-1-시간-및-공간-복잡도">3-1. 시간 및 공간 복잡도</h3>
<table>
<thead>
<tr>
<th>복잡도</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>시간 복잡도</td>
<td><code>O(V + E)</code> → 모든 정점(V)과 간선(E)을 한 번씩 방문</td>
</tr>
<tr>
<td>공간 복잡도</td>
<td><code>O(V)</code> → 방문 배열 + 큐 사용</td>
</tr>
</tbody></table>
<blockquote>
<p>참고: <code>V = 정점(노드)의 수</code>, <code>E = 간선의 수</code></p>
</blockquote>
<hr>
<h2 id="4-dfs-vs-bfs-차이">4. DFS vs BFS 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>DFS</th>
<th>BFS</th>
</tr>
</thead>
<tbody><tr>
<td>탐색 방식</td>
<td>깊이 우선</td>
<td>너비 우선</td>
</tr>
<tr>
<td>자료구조</td>
<td>스택(또는 재귀)</td>
<td>큐</td>
</tr>
<tr>
<td>활용</td>
<td>백트래킹, 조합/순열, 트리 순회</td>
<td>최단 거리, 단계별 탐색</td>
</tr>
<tr>
<td>특징</td>
<td>한 경로 끝까지 탐색</td>
<td>가까운 노드부터 탐색</td>
</tr>
</tbody></table>
<p><strong>DFS</strong>는 <strong>모든 경로를 깊이 우선으로 탐색</strong>할 때 유용한 알고리즘입니다. 경우의 수를 모두 따지며 깊이 있는 탐색을 통해 문제를 해결합니다. 주로 <strong>백트래킹, 조합/순열, 트리 순회</strong> 등에 적합하며, <strong>재귀</strong>나 <strong>스택</strong> 자료구조를 사용하여 탐색을 진행합니다.</p>
<p><strong>BFS</strong>는 <strong>너비 우선으로 탐색</strong>하며 <strong>조건을 만족하는 가장 빠른 경로(최단 거리)</strong>를 찾을 때 적합한 알고리즘입니다. 시작점에서 가까운 노드부터 차례로 탐색하며 문제를 해결합니다. <strong>큐(Queue)</strong>를 사용하여 <strong>탐색 순서를 보장</strong>하며, <strong>최단 거리</strong>나 <strong>단계별 탐색</strong>을 필요로 하는 문제에 매우 유용합니다.</p>
<hr>
<h2 id="5-백준--프로그래머스-문제-추천">5. 백준 &amp; 프로그래머스 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 2178</td>
<td><a href="https://www.acmicpc.net/problem/2178">미로 탐색</a></td>
<td>최단 경로 탐색 (2차원 배열 최단 경로 계산)</td>
</tr>
<tr>
<td>백준 1697</td>
<td><a href="https://www.acmicpc.net/problem/1697">숨바꼭질</a></td>
<td>수빈이가 동생을 찾는 최단 시간 계산</td>
</tr>
<tr>
<td>백준 7562</td>
<td><a href="https://www.acmicpc.net/problem/7562">나이트의 이동</a></td>
<td>나이트가 최소 이동 횟수로 목표 위치로 이동</td>
</tr>
<tr>
<td>프로그래머스</td>
<td><a href="https://programmers.co.kr/learn/courses/30/lessons/43163">단어 변환</a></td>
<td>단어를 변환하는 최소 횟수 찾기</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p><strong>BFS</strong>는 그래프를 단계적으로 탐색하며, <strong>최단 거리 계산에 매우 유용</strong>한 알고리즘입니다.<br>DFS와 마찬가지로 <strong>코딩테스트에 자주 등장</strong>하며, 다양한 문제 풀이의 기본이 됩니다.</p>
<p><strong>DFS는 깊이</strong>, <strong>BFS는 거리에 초점</strong>을 맞춘 탐색입니다.<br>DFS와 BFS 모두 충분히 연습해보고, 어떤 문제에서 어떤 탐색이 적절한지 <strong>판단하는 연습</strong>이 중요합니다.</p>
<blockquote>
<p>👉 아직 <strong>DFS</strong>에 대해 익숙하지 않다면, 이전 <strong>DFS 완전 정복 포스트</strong>도 함께 확인해 보세요!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘 / JAVA] DFS 완전 정복 - 깊이 우선 탐색 
(재귀 vs 스택 방식,  활용까지)]]></title>
            <link>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-DFS-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89-%EC%9E%AC%EA%B7%80-vs-%EC%8A%A4%ED%83%9D-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@heon-blog/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-JAVA-DFS-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EA%B9%8A%EC%9D%B4-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89-%EC%9E%AC%EA%B7%80-vs-%EC%8A%A4%ED%83%9D-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Thu, 08 May 2025 15:10:21 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p><strong>DFS (Depth-First Search)</strong> 는 그래프 탐색의 가장 기본이 되는 알고리즘입니다.<br><strong>미로 탐색</strong>부터 <strong>백트래킹, 연결 요소 탐색, 트리 순회, 사이클 검출</strong> 등 다양한 문제에서 
자주 등장합니다.</p>
<p>이 글에서는 <strong>DFS의 개념</strong>부터 <strong>시간/공간 복잡도</strong>, <strong>재귀와 반복 구현 방식</strong>, <strong>실전 문제 활용</strong>까지 다뤄보겠습니다.</p>
<hr>
<h2 id="2-dfs란">2. DFS란?</h2>
<p>DFS는 <strong>그래프를 깊이 우선으로 탐색</strong>하는 알고리즘입니다.<br>현재 정점에서 갈 수 있는 <strong>가장 깊은 곳까지 먼저 이동</strong>한 후, 막히면 <strong>되돌아가며(backtracking)</strong> 다른 경로를 탐색합니다.</p>
<p><img src="https://velog.velcdn.com/images/heon-blog/post/9173b323-fe7b-4a2f-9f16-fc661d9154e6/image.gif" alt=""> <span style="color: gray">출처: <a href="https://medium.com/analytics-vidhya/a-quick-explanation-of-dfs-bfs-depth-first-search-breadth-first-search-b9ef4caf952c" target="_blank">medium.com - A quick explanation of DFS &amp; BFS</a></span></p>
<blockquote>
<p>예시) 미로에서 한 방향으로 쭉 가보다가 막히면 돌아가서 다른 길을 찾는 방식입니다.</p>
</blockquote>
<h3 id="2-1-dfs를-이해하려면">2-1. DFS를 이해하려면?</h3>
<p>DFS는 주로 <strong>그래프(Graph)</strong> 자료구조를 탐색할 때 사용됩니다. 그래프는 다양한 문제에서 관계나 연결을 표현하는 데 쓰이며, 아래와 같은 요소들로 구성되어 있습니다:</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>정점 (Node, Vertex)</strong></td>
<td>탐색의 대상이 되는 지점 또는 객체 (예: 도시, 방, 사용자 등)</td>
</tr>
<tr>
<td><strong>간선 (Edge)</strong></td>
<td>두 정점을 연결하는 선으로, 정점 간의 관계나 경로를 나타냅니다 (예: 도로, 통로, 친구 관계 등)</td>
</tr>
<tr>
<td><strong>인접 리스트 (Adjacency List)</strong></td>
<td>각 정점이 연결된 다른 정점들을 저장하는 방식. 효율적으로 그래프를 표현할 수 있습니다.</td>
</tr>
<tr>
<td><strong>방문 배열 (Visited Array)</strong></td>
<td>각 정점을 한 번만 방문하도록 체크하는 배열로, 무한 루프를 방지하고 탐색 상태를 추적합니다.</td>
</tr>
</tbody></table>
<p>이 개념들을 기반으로 DFS는 한 경로를 끝까지 따라가며 깊이 있게 탐색한 뒤,
다시 돌아와 다른 경로를 탐색하는 방식으로 작동합니다.<br><strong>재귀 함수</strong>나 <strong>스택</strong>을 이용해 구현하는 경우가 많습니다.</p>
<hr>
<h2 id="3-dfs-구현-방법">3. DFS 구현 방법</h2>
<h3 id="3-1-재귀-recursive-방식">3-1. 재귀 (Recursive) 방식</h3>
<pre><code class="language-java">import java.util.*;

public class DFSExample {
    // 인접 리스트 선언
    static List&lt;Integer&gt;[] graph;
    // 방문 배열 선언
    static boolean[] visited;

    public static void main(String[] args) {
        int n = 9; // 노드 수
        graph = new ArrayList[n + 1];
        visited = new boolean[n + 1];

        // 인접 리스트 초기화
        for (int i = 1; i &lt;= n; i++) {
            graph[i] = new ArrayList&lt;&gt;();
        }

        // 간선 추가
        graph[1].add(2);
        graph[2].add(1);
        graph[1].add(4);
        graph[4].add(1);
        graph[1].add(8);
        graph[8].add(1);
        graph[2].add(3);
        graph[3].add(2);
        graph[4].add(5);
        graph[5].add(4);
        graph[5].add(6);
        graph[6].add(5);
        graph[4].add(7);
        graph[7].add(4);
        graph[8].add(9);
        graph[9].add(8);

        dfs(1); // 1번 노드부터 탐색 시작
    }

    // 재귀 방식
    static void dfs(int node) {
        visited[node] = true;
        System.out.print(node + &quot; &quot;);

        for (int next : graph[node]) {
            if (!visited[next]) {
                dfs(next);
            }
        }
    }
}</code></pre>
<blockquote>
<p><strong>재귀 방식</strong>의 <strong>구현 동작 과정</strong>은 위의 이미지와 같습니다.
<strong>1 → 2 → 3 → (막힘) → 4 → 5 → 6 → (막힘) → 7 → (막힘) → 8 → 9</strong></p>
</blockquote>
<p>깊게 파고들고 막히면 한 단계씩 돌아와 다른 경로를 탐색합니다.
💡 <strong>재귀 구조</strong> 덕분에 <strong>백트래킹</strong>이 자연스럽게 구현됩니다.</p>
<p>❗단점으로는 <strong>깊이가 깊을 경우 StackOverflow 위험이 존재</strong>합니다.</p>
<h3 id="3-2-스택-stack-반복문-방식">3-2. 스택 (Stack) 반복문 방식</h3>
<pre><code class="language-java">void dfsIterative(int start) {
    Deque&lt;Integer&gt; stack = new ArrayDeque&lt;&gt;(); // 스택 사용
    boolean[] visited = new boolean[graph.length]; 

    stack.push(start); // 시작 노드 스택에 추가

    while (!stack.isEmpty()) {
        int node = stack.pop(); // 스택에서 노드 꺼내기

        if (!visited[node]) {
            visited[node] = true; // 방문 표시
            System.out.print(node + &quot; &quot;); // 방문한 노드 출력

            // 인접 노드들을 내림차순으로 스택에 추가
            List&lt;Integer&gt; neighbors = graph[node];
            Collections.sort(neighbors, Collections.reverseOrder()); // 방문 순서 조정

            for (int next : neighbors) {
                if (!visited[next]) {
                    stack.push(next); // 방문하지 않은 노드 스택에 푸시
                }
            }
        }
    }
}</code></pre>
<p>이 방식은 재귀 호출 없이 DFS를 구현하는 방법입니다.
<strong>명시적인 스택(Deque)</strong>을 사용해서 직접 호출 흐름을 제어합니다.</p>
<blockquote>
<p>아래는 <strong>스택 방식</strong>의 <strong>구현 동작 과정</strong>입니다.</p>
</blockquote>
<ol>
<li>탐색 시작 노드를 먼저 스택에 넣고, 스택에서 꺼낸 노드를 방문합니다.</li>
<li>인접한 노드들을 스택에 넣되, 내림차순으로 정렬해 넣으면 작은 번호가 먼저 탐색됩니다.
(스택은 후입선출이므로 뒤에 넣은 게 먼저 꺼내지기 때문입니다.)</li>
<li>이미 방문한 노드는 다시 방문하지 않도록 체크합니다.</li>
</ol>
<ul>
<li>재귀 호출 대신 <strong>반복문과 스택</strong>을 사용해 <strong>StackOverflow  문제를 방지</strong>할 수 있습니다.</li>
<li>명시적인 스택을 사용하므로 호출 흐름이 눈에 보이며 <strong>디버깅</strong>이 쉬워집니다.</li>
<li>재귀 방식보다 <strong>메모리 사용량 제어에 유리</strong>합니다.</li>
</ul>
<h3 id="3-3-시간-및-공간-복잡도">3-3. 시간 및 공간 복잡도</h3>
<table>
<thead>
<tr>
<th>복잡도</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>시간 복잡도</strong></td>
<td><code>O(V + E)</code> → 모든 정점(V)과 간선(E)을 한 번씩 방문</td>
</tr>
<tr>
<td><strong>공간 복잡도</strong></td>
<td><code>O(V)</code> → 방문 배열 + (재귀 호출 스택 또는 명시적 스택) 사용</td>
</tr>
</tbody></table>
<blockquote>
<p>참고: <code>V = 정점(노드)의 수</code>, <code>E = 간선의 수</code></p>
</blockquote>
<hr>
<h2 id="4-dfs와-백트래킹의-차이">4. DFS와 백트래킹의 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>DFS</th>
<th>백트래킹</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>모든 경로 탐색</td>
<td>조건을 만족하는 경로 탐색</td>
</tr>
<tr>
<td>전략</td>
<td>깊이 우선 탐색</td>
<td>조건 위배 시 조기 종료 (pruning)</td>
</tr>
<tr>
<td>예시</td>
<td>그래프 순회, 연결 요소 탐색</td>
<td>N-Queen, 순열/조합, 사다리 조작</td>
</tr>
</tbody></table>
<p>DFS와 백트래킹은 모두 <strong>재귀적 구조를 활용하는 탐색 기법</strong>이라는 공통점이 있지만, 문제 해결을 위한 목적과 전략 면에서 차이점이 존재합니다.</p>
<p>DFS는 <strong>모든 경로를 탐색</strong>하며 문제의 구조를 파악하거나 전체 상태를 순회할 때 유용합니다. 반면, 백트래킹은 탐색 중 <strong>조건에 맞지 않는 경우를 조기에 걸러내어(pruning)</strong> <strong>필요한 경우만 탐색</strong>하기 때문에 <strong>불필요한 계산을 줄일 수 있는 효율적인 기법</strong>입니다.</p>
<p>즉, DFS는 <strong>탐색 그 자체에 중점</strong>을 두고, 백트래킹은 <strong>조건에 맞는 답을 찾기 위해 DFS를 응용한 전략</strong>이라고 볼 수 있습니다.</p>
<hr>
<h2 id="5-백준--프로그래머스-문제-추천">5. 백준 &amp; 프로그래머스 문제 추천</h2>
<table>
<thead>
<tr>
<th>사이트</th>
<th>문제명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>백준 2606</td>
<td><a href="https://www.acmicpc.net/problem/2606">바이러스</a></td>
<td>연결 요소 탐색</td>
</tr>
<tr>
<td>백준 11724</td>
<td><a href="https://www.acmicpc.net/problem/11724">연결 요소의 개수</a></td>
<td>DFS 기본기 연습</td>
</tr>
<tr>
<td>프로그래머스</td>
<td><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43165">타겟 넘버</a></td>
<td>DFS + 백트래킹 활용</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-마무리">6. 마무리</h2>
<p>DFS는 단순한 그래프 탐색을 넘어서<br><strong>다양한 문제 해결의 기반이 되는 알고리즘</strong>입니다.</p>
<ul>
<li><strong>재귀 방식</strong>은 구현이 간결하고 직관적이며,  </li>
<li><strong>반복문(스택) 방식</strong>은 메모리 관리에 유리합니다.  </li>
</ul>
<p>상황에 따라 두 방식 중 적절한 방법을 선택하는 것이 중요합니다.</p>
<p>또한, DFS는 <strong>탐색 그 자체</strong>뿐만 아니라,<br><strong>백트래킹</strong>, <strong>트리 순회</strong>, <strong>사이클 탐지</strong>, <strong>조합 생성</strong> 등  다양한 알고리즘 기법의 기반이 되므로,<br>초급 단계를 넘어 더 복잡한 문제를 풀기 위해서도 꼭 숙지해야 할 알고리즘입니다.</p>
<p>DFS 문제를 많이 접하고 직접 구현해보면서 내 것으로 만들면 
실전 코딩테스트에서 훨씬 빠르고 정확하게 적용할 수 있을 것입니다.</p>
<blockquote>
<p>다음 글에서는 DFS와 자주 비교되는 <strong>BFS (너비 우선 탐색)</strong> 에 대해 정리해보겠습니다.<br>DFS, BFS의 차이와 각 알고리즘 문제를 비교해 보며 실전 감각을 키워봅시다!</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>