<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>피터의 개발이야기</title>
    <link>https://peterica.tistory.com/</link>
    <description>기록으로 실력을 쌓자! 
기록하고 기억하며 새로운 개발 능력을 창출하는 백엔드 개발자를 꿈꿉니다.</description>
    <language>ko</language>
    <pubDate>Sat, 8 Aug 2026 17:07:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>기록하는 백엔드개발자</managingEditor>
    <image>
      <title>피터의 개발이야기</title>
      <url>https://tistory1.daumcdn.net/tistory/4353622/attach/b46072b914e149bd8a07eb59d30816ea</url>
      <link>https://peterica.tistory.com</link>
    </image>
    <item>
      <title>[바이브코딩] 04. AI에게도 SDLC가 필요하다 - ChatGPT, Claude, Codex를 팀처럼 일하게 만든 방법</title>
      <link>https://peterica.tistory.com/1118</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWMGnF/dJMcagl8sje/xJ5qIwbKPKZufBtNzGgcI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWMGnF/dJMcagl8sje/xJ5qIwbKPKZufBtNzGgcI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWMGnF/dJMcagl8sje/xJ5qIwbKPKZufBtNzGgcI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWMGnF%2FdJMcagl8sje%2FxJ5qIwbKPKZufBtNzGgcI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 혼자 일하는 도구가 아니라 역할을 나누어 협업하는 팀원이 될 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩을 처음 시작하면 대부분 하나의 AI에게 모든 일을 맡긴다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;기획해줘.

설계해줘.

구현해줘.

리뷰해줘.

문서도 작성해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 꽤 잘 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 프로젝트가 커질수록 이상한 점을 발견하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 자신이 만든 설계를 스스로 검토하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 작성한 코드를 스스로 리뷰하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 내린 결론을 다시 자신이 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이라면 설계자와 개발자, 리뷰어를 분리하는데 AI에게는 모두 같은 역할을 맡기고 있었던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 ContextBase 프로젝트를 진행하면서 &lt;b&gt;AI를 하나의 개발팀처럼 운영한 방법&lt;/b&gt;을 정리해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 개발팀은 왜 역할을 나눌까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 개발 조직을 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기획자는 요구사항을 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 코드를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰어는 구현이 설계와 일치하는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QA는 테스트를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 역할을 한 사람이 수행할 수도 있지만, 규모가 커질수록 역할을 분리하는 것이 품질과 생산성을 높인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI도 마찬가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI도 역할을 나누기 시작했다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ContextBase에서는 AI를 세 명의 팀원처럼 사용했다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;ChatGPT
&amp;darr;
Codex
&amp;darr;
Claude&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 AI는 서로 다른 책임을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ChatGPT&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 전체 이해&lt;/li&gt;
&lt;li&gt;요구사항 분석&lt;/li&gt;
&lt;li&gt;설계&lt;/li&gt;
&lt;li&gt;다음 작업 계획&lt;/li&gt;
&lt;li&gt;프롬프트 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;Technical Lead&lt;/b&gt; 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Codex&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설계 구현&lt;/li&gt;
&lt;li&gt;테스트 작성&lt;/li&gt;
&lt;li&gt;결과 문서 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;Senior Developer&lt;/b&gt; 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Claude&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구현하지 않는다.&lt;/li&gt;
&lt;li&gt;설계를 변경하지 않는다.&lt;/li&gt;
&lt;li&gt;코드 리뷰만 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;Principal Reviewer&lt;/b&gt; 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ContextNote 활용기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1048&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Context&amp;nbsp;Note&amp;nbsp;&amp;mdash;&amp;nbsp;AI&amp;nbsp;대화의&amp;nbsp;컨텍스트는&amp;nbsp;왜&amp;nbsp;&amp;ldquo;쌓지&amp;nbsp;않고&amp;nbsp;구조화&amp;rdquo;해야&amp;nbsp;할까?&lt;/a&gt;&lt;br /&gt;일전에 ChatGPT와 대화를 하면서 컨텍스트를 정리하기 위해 ContextNote만들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화를 진행하면서 중요한 컨텍스트는 문서로 정리하고, 문서의 일부를 선택하여 대화를 집중적으로 수행할 수 있었다.&lt;br /&gt;말로 전하는 것보다 문서를 보면 맥락을 ChatGPT가 잘 알 수 있어서 대화가 딴방향으로 가는 것을 많이 막을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 대화 중 핵심내용을 정리하여 문서로 바로 옮길 수 있어서 편리하고 사용 중이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2549&quot; data-origin-height=&quot;1482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGk5e9/dJMcaf1Otzh/4UcTN3oK0Mh7KVpgtqurYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGk5e9/dJMcaf1Otzh/4UcTN3oK0Mh7KVpgtqurYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGk5e9/dJMcaf1Otzh/4UcTN3oK0Mh7KVpgtqurYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGk5e9%2FdJMcaf1Otzh%2F4UcTN3oK0Mh7KVpgtqurYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2549&quot; height=&quot;1482&quot; data-origin-width=&quot;2549&quot; data-origin-height=&quot;1482&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT Atlas를 사용하고 있다. 웹과 함께 대화를 이어가면서 아이디어 구상, 작업 검토 등을 수행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 프로젝트의 md파일들을 직접 contextNote에 연결하여 활용하면 claude와 codex의 지시사항을 문서로 바로 등록이 가능하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 이렇게 쓰는 이유는 chatGPT가 대화를 통해 활용하면 Token의 제약을 거의 받지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 이 Atlas는 26.08.09에 종료된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 처음에는 너무 느렸다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 품질을 높이기 위해 모든 단계를 검토했다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;Plan
&amp;darr;
Plan Review
&amp;darr;
Implementation
&amp;darr;
Code Review
&amp;darr;
Completion Report
&amp;darr;
Document Alignment&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;품질은 매우 높았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 기능 하나를 구현하는 데 AI가 여러 번 왕복하면서 개발 속도가 크게 떨어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이것이 맞는 방식이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 프로젝트가 진행될수록 병목이 보이기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 중요한 것은 검증이 아니라 역할이었다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 P3, P4, P5를 거치면서 느낀 것은 의외였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 AI의 성능이 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &lt;b&gt;같은 AI가 모든 역할을 수행하는 구조&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할이 분리되자 각 AI는 자신의 책임에만 집중할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 설계를 고민하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 구현에 집중하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 객관적으로 리뷰한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 개발 프로세스를 AI에게 그대로 적용한 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 그래서 개발 방식도 바뀌었다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 훨씬 단순한 흐름을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;설계
&amp;darr;
구현
&amp;darr;
최종 리뷰&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT가 설계를 확정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex가 구현과 테스트를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude가 최종 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한 중간 검토를 줄이면서도 설계와 구현, 리뷰의 책임은 유지했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI에게도 SDLC가 필요했다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 AI 활용 능력을 프롬프트 작성 능력이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 프로젝트를 진행하면서 얻은 결론은 조금 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 아무리 뛰어나도 역할이 명확하지 않으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설계와 구현, 리뷰가 서로 섞이기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 중요한 것은 프롬프트가 아니라 &lt;b&gt;프로세스&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람에게 SDLC가 필요하듯,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게도 SDLC가 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 네트워크 비유로 생각해 보면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서 TCP는 모든 일을 하나의 패킷이 처리하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송은 Packet이 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 선택은 Router가 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 복구는 TCP가 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 계층이 자신의 역할만 수행하기 때문에 복잡한 네트워크도 안정적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 협업도 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 설계를 담당하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 구현을 담당하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 검증을 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할이 명확해질수록 프로젝트는 더 안정적으로 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 하나에게 모든 역할을 맡기면 객관적인 검증이 어려워진다.&lt;/li&gt;
&lt;li&gt;사람의 개발 조직처럼 AI도 역할을 분리할 수 있다.&lt;/li&gt;
&lt;li&gt;ChatGPT는 설계, Codex는 구현, Claude는 리뷰를 담당했다.&lt;/li&gt;
&lt;li&gt;프로젝트가 안정된 이후에는 Gate 기반으로 프로세스를 단순화했다.&lt;/li&gt;
&lt;li&gt;중요한 것은 AI 모델이 아니라 역할과 책임을 정의하는 개발 프로세스였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/바이브코딩</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1118</guid>
      <comments>https://peterica.tistory.com/1118#entry1118comment</comments>
      <pubDate>Wed, 5 Aug 2026 22:17:51 +0900</pubDate>
    </item>
    <item>
      <title>[AWS] LG Uplus의 BSS 현대화를 위한 클라우드 마이그레이션 여정, DevOps 포인트</title>
      <link>https://peterica.tistory.com/1117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Summit Seoul 2024에서 공개된 &lt;a href=&quot;https://www.youtube.com/watch?v=ZEJRN-p9QYw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LG Uplus의 BSS 현대화 사례&lt;/a&gt;를 보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 LGUplus에서 일했을 때에 알고 있었던 UCube 시스템의 고도화 사례를 소개하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 레거시 시스템을 DevOps관점에서 어떻게 바꾸었는지 기술적으로 배울점이 많아 정리해 보았다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ BSS(Ucube)는 무엇?&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYBzVe/dJMcadv4rns/HncxhfwvVdGvj7foaMwv8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYBzVe/dJMcadv4rns/HncxhfwvVdGvj7foaMwv8K/img.png&quot; data-alt=&quot;https://www.youtube.com/watch?v=ZEJRN-p9QYw&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYBzVe/dJMcadv4rns/HncxhfwvVdGvj7foaMwv8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYBzVe%2FdJMcadv4rns%2FHncxhfwvVdGvj7foaMwv8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;210&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.youtube.com/watch?v=ZEJRN-p9QYw&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BSS는 통신사의 고객, 계약, 과금, 청구를 담당하는 핵심 시스템이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LG Uplus의 BSS는 홈&amp;middot;기업&amp;middot;모바일 전 영역의 정보를 다루는 대형 기관계 시스템이고, 운영 규모도 상당하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매달 청구되는 휴대폰요금부터 시작해서 기업메시징 비용까지, 모든 계약과 요금들이 이 시스템을 통해 정산 청구된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통신사가 오래된 만큼 시 오래 운영된 시스템도 문제가 많았다.&amp;nbsp;&lt;br /&gt;오래된 UI 구조, 복잡한 분기 로직, 백엔드 중복 코드, DB 종속성 때문에 유지보수가 점점 어려워졌다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 수천만 건 규모의 트랜잭션과 배치가 오가는 대형 시스템이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 레거시에서 클라우드로 가는 문제점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 레거시 시스템은 대규모 DB의존적인 경우가 많다. 트래픽이 많지면서 컨넥션 갯수도 용량도 부족하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 과도한 트랜젝션은 감당하기 어려워 진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;느린 시스템&lt;/b&gt;보다 더 어려운 점은&amp;nbsp;&lt;b&gt;바꾸기 어려운 시스템&lt;/b&gt;이라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메뉴가 약 2,500개, 화면이 4,000개...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 추가도 어려워지고, 변경 영향 범위가 크다. 점점 감당하기 어려운 파스타소스가 되어가는 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 현대화의 방향&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SzTCB/dJMcadQqft9/9nYygiRmnNAnFkouAsObd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SzTCB/dJMcadQqft9/9nYygiRmnNAnFkouAsObd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SzTCB/dJMcadQqft9/9nYygiRmnNAnFkouAsObd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSzTCB%2FdJMcadQqft9%2F9nYygiRmnNAnFkouAsObd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;1878&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LG&amp;nbsp;Uplus가&amp;nbsp;세운&amp;nbsp;현대화&amp;nbsp;전략은&amp;nbsp;크게&amp;nbsp;세&amp;nbsp;가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 분리된 서비스는 비동기 방식으로 연결한다. =&amp;gt; 소스를 쪼개고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스를 독립적으로 분리하고 CI/CD를 구축한다. =&amp;gt; 민첩하게 배포하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클라우드와 오픈 소스를 활용해 유연하고 효율적인 인프라를 만든다. =&amp;gt; 확장가능한 인프라를 구축&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA로 쪼개고, 비동기로 만들고, k8s로 배포의 용이성과 장애 대응을 이루는 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 적용 기술&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kS8Ml/dJMcahSOhL7/ctKI3zyWuKnVt8Jz9F73L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kS8Ml/dJMcahSOhL7/ctKI3zyWuKnVt8Jz9F73L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kS8Ml/dJMcahSOhL7/ctKI3zyWuKnVt8Jz9F73L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkS8Ml%2FdJMcahSOhL7%2FctKI3zyWuKnVt8Jz9F73L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;266&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용기술을 보면서 사용하지 못한 기술들이 있어서 매우 흥미로웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 내가 해보지 못한 고난이도의 트랜젝션 처리를 위한 SAGA와 이를 지원해주기 윈한 Eventuate가 그것이었다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 143px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.3333%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 31.5891%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 18.3333%; height: 71px;&quot; rowspan=&quot;3&quot;&gt;Agility&amp;nbsp;강화&lt;/td&gt;
&lt;td style=&quot;width: 31.5891%; height: 35px;&quot;&gt;Framework&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 35px;&quot;&gt;&lt;span&gt;Spring Boot&lt;br /&gt;Eventuate&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 31.5891%; height: 19px;&quot;&gt;데이터베이스&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 19px;&quot;&gt;&lt;span&gt;Mysql(Aurora, MariaDB), Oracle&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 31.5891%; height: 17px;&quot;&gt;CI/CD&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 17px;&quot;&gt;&lt;span&gt;Gitlab, Jenkins, Helm, ECR, Harbor, ArgoCD&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.3333%; height: 19px;&quot; rowspan=&quot;4&quot;&gt;&lt;span&gt;서비스 안정성&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.5891%; height: 19px;&quot;&gt;Event Driven&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 19px;&quot;&gt;&lt;span&gt;Message Broker (kafka), Eventuate CDC&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 31.5891%; height: 17px;&quot;&gt;Observability&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 17px;&quot;&gt;&lt;span&gt;WhaTap, Dynatrace, EFK&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5891%;&quot;&gt;컨테이너관리&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%;&quot;&gt;&lt;span&gt;Public:&amp;nbsp;AWS&amp;nbsp;EKS,&amp;nbsp;Private:&amp;nbsp;Openshift&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5891%;&quot;&gt;Service&amp;nbsp;Mesh&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%;&quot;&gt;&lt;span&gt;Istio&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.3333%; height: 19px;&quot;&gt;&lt;span&gt;자원사용 최적화&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.5891%; height: 19px;&quot;&gt;Virtual&amp;nbsp;Machine&lt;/td&gt;
&lt;td style=&quot;width: 50.0775%; height: 19px;&quot;&gt;Public:&amp;nbsp;AWS&amp;nbsp;EC2,&amp;nbsp;Private:&amp;nbsp;VMware&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 서비스 분리 방식&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q5SQm/dJMcag7tqwR/W3hRLCGAeoEBfSL1y0rkK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q5SQm/dJMcag7tqwR/W3hRLCGAeoEBfSL1y0rkK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q5SQm/dJMcag7tqwR/W3hRLCGAeoEBfSL1y0rkK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq5SQm%2FdJMcag7tqwR%2FW3hRLCGAeoEBfSL1y0rkK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;242&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 기능을 무조건 마이크로서비스로 나눈지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경이 잦은 기능은 마이크로서비스로, 안정성과 일관성이 주용한 기능은 비교적 큰 단위로 분리 전략을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무조건 작은 서비스로 쪼개는 방식이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 빈도와 중요도에 따라 &lt;b&gt;마이크로/매크로로&lt;/b&gt; 나눴다는 점이 인상적이었다.&lt;span data-pplx-citation-url=&quot;https://www.youtube.com/watch?v=ZEJRN-p9QYw&quot; data-pplx-citation=&quot;&quot;&gt;&lt;span data-state=&quot;open&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dB3elH/dJMcajwq1m4/qaBqS0exTgmEOJ8YwD84G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dB3elH/dJMcajwq1m4/qaBqS0exTgmEOJ8YwD84G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dB3elH/dJMcajwq1m4/qaBqS0exTgmEOJ8YwD84G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdB3elH%2FdJMcajwq1m4%2FqaBqS0exTgmEOJ8YwD84G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;239&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산된 서비스에 대한 처리는 비동기 설계를 원칙으로 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 영역에서 Message Broker를 적용하여 개별 Micro서비스의 장애 시에도 처리를 보장하고자 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 정합성은 어떻게 해결했나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSA의 경우 도메인별, 혹은 서비스별로 모듈화하여 빠르게 신규 서비스를 적용하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스장애로부터 전체 시스템에게 영향력을 최소화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 데이터의 분리로 인해 데이터 정합성을 위한 트렌젝션 처리는 난감해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 부분은 예전부터 내가 가장 궁금했던 주제이기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로서비스는 서비스를 독립적으로 분리하지만, 그만큼 데이터도 분산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 간 결합도는 낮아지지만, 반대로 하나의 트랜잭션으로 처리하던 작업은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 서비스에 걸쳐 수행되어야 한다. 그러면 서로 다른 서비스에 저장된 데이터를 어떻게 일관성 있게 유지할 것인가?&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;고민&amp;nbsp;때문에&amp;nbsp;예전에도&amp;nbsp;MSA를&amp;nbsp;공부하면서&lt;br /&gt;'&lt;a href=&quot;https://peterica.tistory.com/380&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;12가지&amp;nbsp;마이크로서비스&amp;nbsp;패턴&lt;/a&gt;',&amp;nbsp;'&lt;a href=&quot;https://peterica.tistory.com/582&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;배민&amp;nbsp;마이크로서비스&amp;nbsp;여행기&lt;/a&gt;',&amp;nbsp;'&lt;a href=&quot;https://peterica.tistory.com/584&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CQRS&amp;nbsp;패턴&lt;/a&gt;'&amp;nbsp;등을&amp;nbsp;정리한&amp;nbsp;적이&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 배민 사례에서는 변경이 잦은 영역과 안정성이 중요한 영역을 다르게 분리하는 전략을 소개했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CQRS는 읽기와 쓰기를 분리하여 서비스의 확장성과 성능을 확보하는 대신,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 데이터 정합성을 어떻게 유지할 것인지가 핵심 과제가 된다는 점을 설명하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 MSA의 핵심은 서비스를 잘게 나누는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산된 데이터의 정합성과 장애를 어떻게 설계할 것인지에 있다는 점을 계속 고민해 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 LG U+ 사례는 그 고민에 대한 실제 운영 사례를 보여주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 CQRS를 적용하거나 이벤트를 사용하는 수준이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transactional Outbox, Saga, 시멘틱 락, Dead Letter Queue를 함께 활용하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 일관성(Eventual Consistency)을 확보하는 방식을 선택한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;결국&amp;nbsp;MSA의&amp;nbsp;핵심은&amp;nbsp;서비스를&amp;nbsp;잘&amp;nbsp;나누는&amp;nbsp;것이&amp;nbsp;아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산된 데이터의 정합성과 장애를 어떻게 설계할 것인가에 있다는 점을 계속 고민해 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산&amp;nbsp;환경에서&amp;nbsp;가장&amp;nbsp;어려운&amp;nbsp;건&amp;nbsp;결국&amp;nbsp;데이터&amp;nbsp;정합성이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbLY9F/dJMcaaM1xOD/vrxGKnCySZNUK5tiFt6xwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbLY9F/dJMcaaM1xOD/vrxGKnCySZNUK5tiFt6xwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbLY9F/dJMcaaM1xOD/vrxGKnCySZNUK5tiFt6xwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbLY9F%2FdJMcaaM1xOD%2FvrxGKnCySZNUK5tiFt6xwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;343&quot; data-origin-width=&quot;3338&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를&amp;nbsp;위해&amp;nbsp;Transactional&amp;nbsp;Outbox,&amp;nbsp;Saga&amp;nbsp;패턴,&amp;nbsp;락&amp;nbsp;전략,&amp;nbsp;재처리용&amp;nbsp;별도&amp;nbsp;서비스&amp;nbsp;등을&amp;nbsp;조합해&amp;nbsp;운영했다고&amp;nbsp;설명한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;Transactional Outbox&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;데이터베이스 변경과 메시지 발행을 하나의 트랜잭션으로 묶어 원자성을 보장하는 설계 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;Saga&amp;nbsp;패턴&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;마이크로서비스 아키텍처(MSA)에서 분산된 여러 서비스의 데이터 일관성을 유지하기 위해 사용하는 &lt;b&gt;로컬 트랜잭션 연속 실행 및 보상 트랜잭션&lt;/b&gt; 관리 방식&lt;br /&gt;단일 거대 트랜잭션 대신 각 서비스별로 DB를 업데이트하고 이벤트를 통해 다음 단계를 진행하며, 중간에 실패하면 &lt;b&gt;보상 트랜잭션&lt;/b&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-complete=&quot;true&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;으로 이전 작업을 되돌려 &lt;b&gt;최종 일관성(Eventual Consistency)&lt;/b&gt;을 보장&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;시멘틱&amp;nbsp;락&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;데이터베이스 수준의 락이 아니라 &lt;b&gt;애플리케이션 수준&lt;/b&gt;에서 데이터에 플래그(표식)를 두어 아직 처리 중이거나 변경될 수 있는 상태임을 나타내는 동시성 제어 기법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;Dead Letter Queue&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;메시지 큐 시스템에서 오류나 기타 이유로 &lt;b&gt;정상 처리되지 못한 메시지들을 별도로 모아두는 특수한 대기열(토픽)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;부분이&amp;nbsp;특히&amp;nbsp;인상적이었다.&amp;nbsp;&amp;nbsp;&lt;br /&gt;MSA 전환은 &amp;ldquo;서비스를 쪼개는 일&amp;rdquo;처럼 보이지만, 실제로는 &lt;b&gt;정합성을 어떻게 유지할지 결정하는 일&lt;/b&gt;에 더 중요하다.&lt;br /&gt;기술적으로&amp;nbsp;멋진&amp;nbsp;구조보다,&amp;nbsp;장애&amp;nbsp;상황에서도&amp;nbsp;데이터가&amp;nbsp;틀어지지&amp;nbsp;않게&amp;nbsp;만드는&amp;nbsp;설계가&amp;nbsp;더&amp;nbsp;중요하다는&amp;nbsp;메시지가&amp;nbsp;분명했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 돈과 계약에 관련된 사안들은 더욱 그러하다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 데이터&amp;nbsp;마이그레이션과&amp;nbsp;검증&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kAJ0r/dJMcaiEhHUT/se8hxxcdLZ2Co8kLfbQY11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kAJ0r/dJMcaiEhHUT/se8hxxcdLZ2Co8kLfbQY11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kAJ0r/dJMcaiEhHUT/se8hxxcdLZ2Co8kLfbQY11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkAJ0r%2FdJMcaiEhHUT%2Fse8hxxcdLZ2Co8kLfbQY11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;285&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈은 그래도 맞지 않는 경우가 발생한다. 이를 찾기 위해서 일일 대사를 비교하곤 하는데 그 작업이 쉬운게 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 전자결제팀에서 정산데이터를 다루는 SQL문을 함부로 돌릴 수도 없었다. 부하가 발생하기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS를 사용한다면, Read쪽에서 돌리던가, DB 인스턴스를 복제하여 부하로 인한 운영 영향력을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 소개하는 것은 조금 다른 방법이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방해한 로그를 S3에 적제하고 있었든데, 이를 Athena를 통해 특정 유입을 분석한 경험은 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이를 정산 데이터에 적용한 부분은 AWS의 새로운 가능성을 제시해 준다고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방대한 데이터를 AWS DMS, Glue, S3, Athena, Lambda, EventBridge를 활용해 데이터 비교와 검증을 하였다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.0698%;&quot;&gt;요소&lt;/td&gt;
&lt;td style=&quot;width: 65.9302%;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.0698%;&quot;&gt;AWS DMS&lt;/td&gt;
&lt;td style=&quot;width: 65.9302%;&quot;&gt;데이터베이스와 분석 시스템을 안전하고 빠르게 다른 곳으로 옮겨주는 ⁠AWS Database Migration Service 클라우드 서비스&lt;br /&gt;원본 데이터베이스의 데이터를 손실 없이 대상 데이터베이스로 복제하며, 서비스 중단 시간을 최소화함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.0698%;&quot;&gt;AWS Glue&lt;/td&gt;
&lt;td style=&quot;width: 65.9302%;&quot;&gt;여러 소스의 데이터를 쉽게 검색, 준비, 이동, 통합할 수 있도록 돕는 &lt;b&gt;서버리스 데이터 통합 서비스&lt;/b&gt;이자 &lt;b&gt;완전 관리형 ETL(추출, 변환, 적재) 서비스&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.0698%;&quot;&gt;AWS Athena&lt;/td&gt;
&lt;td style=&quot;width: 65.9302%;&quot;&gt;표준&amp;nbsp;SQL을&amp;nbsp;사용해&amp;nbsp;⁠Amazon&amp;nbsp;S3에&amp;nbsp;저장된&amp;nbsp;데이터를&amp;nbsp;직접&amp;nbsp;분석할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;서버리스&amp;nbsp;대화형&amp;nbsp;쿼리&amp;nbsp;서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.0698%;&quot;&gt;EventBridge&lt;/td&gt;
&lt;td style=&quot;width: 65.9302%;&quot;&gt;다양한 소스에서 발생한 이벤트를 받아 원하는 곳으로 전달해 주는 서버리스 이벤트 라우팅 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- AWS DMS와 Glue로 검증 대상 데이터를 S3로 추출.&lt;br /&gt;&amp;nbsp;- Athena로 비교 검증.&lt;br /&gt;&amp;nbsp;- Lambda와 EventBridge로 전체 과정을 자동화.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게&amp;nbsp;해서&amp;nbsp;사용자가&amp;nbsp;쉽게&amp;nbsp;활용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;자동&amp;nbsp;데이터&amp;nbsp;검증&amp;nbsp;플랫폼을&amp;nbsp;만들었고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 분산 서비스 간 대용량 데이터 추출과 배치 작업에도 활용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 클라우드&amp;nbsp;전략과&amp;nbsp;마이그레이션&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smHwH/dJMb99N07bl/bWZJqeE6TM9A326TlvQkKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smHwH/dJMb99N07bl/bWZJqeE6TM9A326TlvQkKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smHwH/dJMb99N07bl/bWZJqeE6TM9A326TlvQkKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmHwH%2FdJMb99N07bl%2FbWZJqeE6TM9A326TlvQkKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;302&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 AWS를 선정하고 인프라의 가용성과 효율성을 정하고 민감정보는 Private, 민첩성이 중요한 서비스는 퍼블릭에 둔 하이브리드 클라우드를 택하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 기존 기능과 신규 기능을 &lt;b&gt;BFF 레이어&lt;/b&gt;로 라우팅해서 점진적으로 페이드아웃시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 기능은 차세대 쪽으로, 기존 기능은 레거시 쪽으로 보내면서 사용자가 자연스럽게 전환되도록 한 구조이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.279%; height: 191px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 17.9618%;&quot;&gt;기술&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 81.9108%;&quot;&gt;대상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 17.9618%;&quot;&gt;&lt;b&gt;BFF&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 81.9108%;&quot;&gt;Frontend를 위한 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 17.9618%;&quot;&gt;&lt;b&gt;API Gateway&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 81.9108%;&quot;&gt;외부 요청의 입구&lt;br /&gt;모든 API 요청이 먼저 Gateway를 통과&lt;br /&gt;인증,권한 Routing, Load Balancing 처리&lt;br /&gt;대표: Envoy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 17.9618%;&quot;&gt;&lt;b&gt;GraphQL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 81.9108%;&quot;&gt;필요한 데이터만 조회는 API 조회 방식&lt;br /&gt;&lt;br /&gt;REST라면 3번 호출할 것을 다음처럼 한번에 호출하는 API 형태이다.&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;user&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;orders&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;product&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px; width: 17.9618%;&quot;&gt;&lt;b&gt;Istio&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 81.9108%;&quot;&gt;서비스 간 통신 관리이다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAX 관점에서는&lt;/p&gt;
&lt;pre id=&quot;code_1785810883741&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Client
  &amp;darr;
 BFF
  &amp;darr;
API Gateway
  &amp;darr;
Kubernetes
  &amp;darr;
Istio(Service Mesh)
  &amp;darr;
Microservices&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 기존&amp;nbsp;데이터와&amp;nbsp;신규&amp;nbsp;데이터를&amp;nbsp;어떻게&amp;nbsp;공존시켰나&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cba1EA/dJMcaafc0f6/r0fFpq1k3MUxkO7LshCqL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cba1EA/dJMcaafc0f6/r0fFpq1k3MUxkO7LshCqL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cba1EA/dJMcaafc0f6/r0fFpq1k3MUxkO7LshCqL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcba1EA%2FdJMcaafc0f6%2Fr0fFpq1k3MUxkO7LshCqL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;360&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레거시 모놀리스에서 신규 도메인을 분리할 때는 참조 관계가 가장 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 스키마를 광범위하게 바꿔야 하므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 팀은 전환 기간 동안 &lt;b&gt;원본 스키마를 유지하면서 신규 스키마와 데이터를 동기화&lt;/b&gt;하는 방식을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로는 신규 도메인 서비스 영역을 읽기 전용처럼 두고, 갱신이 발생하면 그 변경을 모놀리스에 복제해 최신 상태를 유지하여,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;기존 서비스와 신규 서비스 간 데이터 동화를 유지했다. 결국 이 영역은 전환이 끝나면 모놀리스와 함께 페이드아웃된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;ㅁ 차세대&amp;nbsp;이후의&amp;nbsp;최적화&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;네트워크 개선&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8KLRC/dJMcajiTC5c/VYJTUwTxEpYv3Bcr4f1obK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8KLRC/dJMcajiTC5c/VYJTUwTxEpYv3Bcr4f1obK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8KLRC/dJMcajiTC5c/VYJTUwTxEpYv3Bcr4f1obK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8KLRC%2FdJMcajiTC5c%2FVYJTUwTxEpYv3Bcr4f1obK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;521&quot; height=&quot;293&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;기존에는 중앙 집중식 네트워크에서 Direct Connect(전용선)를 타고 IDC의 시 서버를 경유해 인터넷으로 나가는 구조였는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이 방식은 EKS와 애플리케이션 각각에 별도 설정이 필요해 관리가 어려웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이를 개선하기 위해 쉐어드 어카운트에 AWS Network Firewall을 두고 인터넷 통신 구조를 재설계했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이렇게 하면 각 계정마다 NAT Gateway나 IGW를 따로 만들지 않아도 되어, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;공인 IP 사용을 줄이고 보안 정책에 맞는 도메인만 통신하도록 통제할 수 있다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SXqvf/dJMcahZCkYU/pjNXHqdpzgfRWBunPuoaLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SXqvf/dJMcahZCkYU/pjNXHqdpzgfRWBunPuoaLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SXqvf/dJMcahZCkYU/pjNXHqdpzgfRWBunPuoaLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSXqvf%2FdJMcahZCkYU%2FpjNXHqdpzgfRWBunPuoaLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;1786&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;또한 기존 172 대역 24비트 CIDR의 IP 고갈 문제를 해결하려고, 10점대 16비트 CIDR을 적용한 새 VPC를 설계했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;EKS 서비스가 기존 DB와 통신할 때는 VPC Peering을 쓰고, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;IDC/온프레미스와는 Private NAT Gateway로 IP 변환 통신하게 개선했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;나도 IP고갈문제로 인해 작성한 글이 있어서 공감되었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/281&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[AWS&amp;nbsp;VPN]&amp;nbsp;Amazon&amp;nbsp;VPC의&amp;nbsp;CIDR&amp;nbsp;블록&amp;nbsp;IP&amp;nbsp;확장방법,&amp;nbsp;AWS&amp;nbsp;IP증설&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;코드 기반 인프라&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7hG6M/dJMcacqAtBt/ELSzDGeKTztKgVIRdMRSM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7hG6M/dJMcacqAtBt/ELSzDGeKTztKgVIRdMRSM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7hG6M/dJMcacqAtBt/ELSzDGeKTztKgVIRdMRSM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7hG6M%2FdJMcacqAtBt%2FELSzDGeKTztKgVIRdMRSM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;1786&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 AWS 콘솔 작업이 많아서 생성&amp;middot;변경&amp;middot;삭제 이력이 추적하기 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 Terraform과 Atlantis로 가져가고 GitHub에서 관리해 히스토리 추적이 가능하게 만들었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctei5x/dJMcagsUH43/FIjJcp6mWqWIa7YkKENAf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctei5x/dJMcagsUH43/FIjJcp6mWqWIa7YkKENAf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctei5x/dJMcagsUH43/FIjJcp6mWqWIa7YkKENAf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fctei5x%2FdJMcagsUH43%2FFIjJcp6mWqWIa7YkKENAf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;261&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포도 Helm 명령에서 GitOps 방식으로 바꿔 Argo CD 멀티클러스터 구조로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;cicd&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;CI/CD와 배포 가시성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/besY06/dJMcab6d1gP/AIrr5IXyfUNPgRYaUEwMD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/besY06/dJMcab6d1gP/AIrr5IXyfUNPgRYaUEwMD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/besY06/dJMcab6d1gP/AIrr5IXyfUNPgRYaUEwMD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbesY06%2FdJMcab6d1gP%2FAIrr5IXyfUNPgRYaUEwMD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;462&quot; height=&quot;260&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 테스트와 CI/CD가 한 파이프라인에 있어 불필요한 테스트 코드 때문에 빌드와 배포가 느려졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 분리해 테스트가 통과한 후 머지되면 빌드와 배포가 진행되도록 바꿨고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포된 앱의 상태는 Argo CD와 Git으로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;접근제어&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/prKbe/dJMcabZwrLy/OJ4vdM7xJ7ZfD8jjFIn630/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/prKbe/dJMcabZwrLy/OJ4vdM7xJ7ZfD8jjFIn630/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/prKbe/dJMcabZwrLy/OJ4vdM7xJ7ZfD8jjFIn630/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FprKbe%2FdJMcabZwrLy%2FOJ4vdM7xJ7ZfD8jjFIn630%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;255&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 VDI 환경에서 Bastion을 거쳐 EC2/EKS에 접근해야 했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 사내 SSO와 GitHub SSO, 그리고 Teleport를 활용해 접근 제어를 개선했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누가 어떤 세션으로 어떤 행위를 했는지 세션 레코딩도 가능해 보안성과 추적성이 좋아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;배치 서버리스화&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crslm3/dJMcagzFFeB/jaTLNodIY7dGIzL6v3MKUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crslm3/dJMcagzFFeB/jaTLNodIY7dGIzL6v3MKUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crslm3/dJMcagzFFeB/jaTLNodIY7dGIzL6v3MKUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrslm3%2FdJMcagzFFeB%2FjaTLNodIY7dGIzL6v3MKUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;252&quot; data-origin-width=&quot;3174&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 EC2를 24시간 켜두고 에이전트가 배치를 실행하는 구조였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 Jenkins와 CodeBuild 기반으로 바꿔 배치 잡이 실행될 때만 서버가 뜨고 끝나면 죽는 서버리스 형태로 개선했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 Datadog, Sumo Logic으로 가시성도 확보했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마지막 메시지&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Agility: 내부 개발자가 빠르게 비즈니스 로직을 바꿀 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;Continuity: 큰 변화 없이 지속적으로 개발&amp;middot;배포&amp;middot;모니터링&amp;middot;성능 개선을 이어가야 한다.&lt;/li&gt;
&lt;li&gt;Optimization: 성능과 비용을 계속 최적화해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 팀이 원하는 방향은 단발성 대규모 전환이 아니라 지속적으로 진화하는 플랫폼화된 DevOps의 완전체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표의 마지막 인용문 &lt;b&gt;&amp;ldquo;완벽함은 더할 것이 없을 때가 아니라 뺄 것이 남지 않을 때 완성된다&amp;rdquo;&lt;/b&gt;는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 레거시를 계속 덧붙이는 게 아니라 불필요한 걸 줄이고 단순하게 만드는 현대화의 철학을 강조하면서도,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevOps 가치를 강조하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/213&quot;&gt;[DevOps] DevOps란 무엇입니까?&lt;/a&gt;에게 DevOps의 의미를 정리한 적이 있다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;DevOps는 기술적 스팩만을 말하는 것이 아니라 기술을 어떻게 고객의 요구상황에 맞게 빠르고 안전하게 실현하는데 있다. DevOps는 일종의&amp;nbsp;&amp;nbsp;문화와 철학이며, 이를 이루기 위해 기술적이고 인프라차원의 환경이 필요한 것이다. 실질적인 공유와 협업의 정신을 무시하고 우선적으로 자신의 처지에만 취중할 때에 전체 조직의 퍼포먼스를 줄어들 수 밖에 없다. - 블로그 글 중 -&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DevOps는 협업과 통합을 강조하는 개발 문화이자 운영 방식이다. 개발조직과 운영조직간의 상호 의존적 대응이며 조직이 소프트웨어 제품과 서비스를 빠른 시간에 개발 및 배포하는 것을 목적으로 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 동영상에서는 BSS 현대화 과정에서 DevOps의 핵심 원칙과 조직 문화가 실제 업무에 어떻게 적용되는지 잘 보여준 발표였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 요구사항을 빠르고 안정적으로 전달하기 위해 DevOps가 어떻게 활용되는지 확인할 수 있었다.&lt;/p&gt;</description>
      <category>AWS</category>
      <category>aws</category>
      <category>DevOps</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1117</guid>
      <comments>https://peterica.tistory.com/1117#entry1117comment</comments>
      <pubDate>Tue, 4 Aug 2026 14:33:52 +0900</pubDate>
    </item>
    <item>
      <title>[독후감] 아주 작은 습관의 힘</title>
      <link>https://peterica.tistory.com/1113</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TY6ws/dJMcaf8wkz7/7Ge60tuHjuLflYyejRMxK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TY6ws/dJMcaf8wkz7/7Ge60tuHjuLflYyejRMxK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TY6ws/dJMcaf8wkz7/7Ge60tuHjuLflYyejRMxK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTY6ws%2FdJMcaf8wkz7%2F7Ge60tuHjuLflYyejRMxK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작가에 대한 이해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제임스 클리어(James Clear)는 습관과 행동 변화에 대해 연구하는 작가이자 강연자이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 특징은 새로운 심리학 이론을 제시하는 사람이 아니라, 심리학&amp;middot;행동경제학&amp;middot;신경과학 등 여러 연구를 종합하여 누구나 실천할 수 있는 행동 시스템으로 정리하는 데 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그가 가장 중요하게 생각하는 것은 목표(Goal)가 아니라 시스템(System)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 흔히 목표를 달성하면 성공한다고 생각하지만, 제임스 클리어는 목표는 방향을 정할 뿐이며 실제 결과를 만드는 것은 매일 반복하는 시스템이라고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나의 핵심은 정체성(Identity)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 억지로 행동을 바꾸는 것이 아니라, '나는 어떤 사람이 되고 싶은가'라는 정체성에서 시작된다. 행동은 정체성을 증명하는 과정이며, 반복될수록 그 정체성은 더욱 강해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 제임스 클리어는 사람을 변화시키려는 사람이 아니라, 사람이 자연스럽게 변화할 수 있는 구조를 설계하는 사람에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 책은 자기계발서라기보다 행동 설계 매뉴얼이라는 관점으로 읽는 것이 저자의 의도를 이해하는 데 도움이 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이 책을 읽으며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 습관을 만드는 방법을 알려주는 교양서라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 구체적인 사례와 행동 변화의 법칙을 따라가며, 일상에서 바로 적용할 수 있는 실천적인 안내서로 읽게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 작가는 글의 시작에 일화를 예시로 사용하고 있다. 목차의 내용은 작가가 말하려고 하는 핵심적인 주제를 잘 나타내고 있다.&lt;br /&gt;그리고 챕터 마지막에는 썸머리가 있어서 일화와 구체적인 서술, 근거로 이야기 하려던 내용을 한번 더 명확히 정리해 주고 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 아주 작은 습관의 힘이란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'아주 작은 습관의 힘'이라는 제목을 처음 접했을 때는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙적인 생활 습관을 만들고, 좋은 습관을 유지하는 방법을 설명하는 교양서라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 책을 읽으며 생각이 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제임스 클리어가 말하는 아주 작은 습관의 힘은 단순히 결과를 바꾸는 힘이 아니라, &lt;b&gt;정체성을 만들어 가는 힘&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 거대한 결심으로 변하는 것이 아니라, 매일 반복하는 작은 행동을 통해 스스로를 만들어 간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 작은 행동 하나가 내일의 정체성을 만들고, 그 정체성이 결국 나의 삶을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 힘이 바로 &lt;b&gt;아주 작은 습관의 힘&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 책의 핵심내용 정리&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 1. 아주 작은 습관이 만드는 극적인 변화&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 01. 아주 작은 습관이 만드는 극적인 변화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 목표를 이루지 못하는 이유를 의지력이나 능력 부족에서 찾는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 저자는 문제의 원인이 목표가 아니라 &lt;b&gt;시스템&lt;/b&gt;에 있다고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 생산성을 높이고, 지식을 쌓으며, 더 나은 관계를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 나쁜 습관은 스트레스를 쌓고 부정적인 행동을 반복하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 방향을 제시할 뿐 결과를 보장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 결과를 얻기 위해서는 목표보다 매일 반복할 수 있는 시스템을 만드는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공은 목표가 아니라 시스템의 결과다. 인생을 바꾸고 싶다면 목표보다 매일 반복하는 시스템을 먼저 바꿔야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 02. 정체성, 사람을 움직이는 가장 큰 비밀&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동 변화에는 &lt;b&gt;결과 변화&lt;/b&gt;, &lt;b&gt;과정 변화&lt;/b&gt;, &lt;b&gt;정체성 변화&lt;/b&gt;의 세 단계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 사람은 결과를 바꾸려고 하지만, 저자는 진정한 변화는 정체성에서 시작된다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 단순히 행동을 반복하는 것이 아니라 '나는 어떤 사람인가'를 스스로에게 증명하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 행동은 정체성을 만들고, 그 정체성은 다시 행동을 강화하는 선순환을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관의 진정한 가치는 더 나은 결과를 얻는 것이 아니라, 자신에 대한 믿음을 변화시키는 데 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 원하는 결과를 얻기 위한 수단이 아니라, 원하는 정체성을 만들어 가는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 03. 무엇이든 쉽게, 재밌게, 단순하게&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 반복을 통해 자동화된 행동이다. 우리의 뇌는 반복되는 행동을 습관으로 만들어 적은 에너지로 문제를 해결하도록 진화했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 습관은 &lt;b&gt;신호 &amp;rarr; 열망 &amp;rarr; 반응 &amp;rarr; 보상&lt;/b&gt;이라는 동일한 과정을 거쳐 형성된다. 이 구조를 이해하면 좋은 습관은 강화하고, 나쁜 습관은 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 책에서 설명하는 네 가지 행동 변화 법칙도 이러한 습관 형성 과정을 바탕으로 만들어진 원칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 습관은 같은 원리로 만들어진다. 그 원리를 이해하면 원하는 습관을 의도적으로 설계할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 2. 첫 번째 법칙, 분명해야 달라진다&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 04. 인식해야 변화할 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충분히 익숙해진 습관은 뇌가 특정한 결과를 가져올 신호를 자동으로 인식하면서 작동한다. 습관이 자동화되면 행동에 주의를 기울이지 않게 되므로, 변화를 시작하려면 먼저 자신이 어떤 행동을 반복하고 있는지 알아차려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행동을 말로 표현하는 '확인하고 외치기'는 무의식적인 습관을 의식의 영역으로 끌어올려 인지 수준을 높여준다. 습관 점수표 역시 현재의 행동을 객관적으로 살펴보게 하는 간단하면서도 효과적인 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관을 만들기 위해서는 먼저 자신이 어떤 습관을 가지고 있는지 정확하게 인식해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 05. 아주 구체적으로 쪼개고 붙여라&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 행동 변화 법칙은 &lt;b&gt;'분명하게 만들어라'&lt;/b&gt;이다. 새로운 습관을 만드는 가장 강력한 신호는 시간과 장소이므로, &lt;b&gt;실행 의도&lt;/b&gt;를 활용해 언제 어디서 무엇을 할지 구체적으로 정해야 한다. 실행 의도의 공식은 &lt;b&gt;'나는 [언제] [어디서] [어떤 행동]을 할 것이다.'&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;습관 쌓기&lt;/b&gt;는 기존 습관 뒤에 새로운 습관을 연결하는 전략이다. &lt;b&gt;'[현재의 습관]을 하고 나서 [새로운 습관]을 할 것이다.'&lt;/b&gt;라는 공식으로 표현하면 새로운 행동을 일상의 흐름에 자연스럽게 결합할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 막연한 의지가 아니라 구체적인 시간과 장소, 그리고 기존 습관과의 연결을 통해 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 06. 환경이 행동을 결정한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 매일 하는 많은 행동은 목적이나 의지보다 눈에 보이는 환경의 영향을 더 크게 받는다. 습관은 신호에서 시작되므로, 만들고 싶은 습관과 관련된 신호가 눈에 띄지 않으면 행동으로 이어지기 어렵다. 좋은 습관의 신호를 자주 접할 수 있도록 배치하는 작은 환경 변화도 시간이 지나면 행동에 큰 차이를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 하나의 사물만이 아니라 행동을 둘러싼 전체 환경과 맥락 속에서 형성된다. 같은 장소도 개인의 기억과 경험에 따라 서로 다른 행동을 유도하며, 기존 신호가 사라진 새로운 환경에서는 새로운 습관을 만들기가 더 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경은 의지보다 강력하다. 행동을 바꾸고 싶다면 자신을 바꾸기보다 환경을 먼저 설계해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 07. 나쁜 습관 피하기 기술&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 행동 변화 법칙을 반대로 적용하면 &lt;b&gt;'보이지 않게 하라.'&lt;/b&gt;가 된다. 한 번 형성된 습관은 쉽게 사라지지 않기 때문에 나쁜 습관을 유발하는 신호에 노출되는 일을 줄여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자제력이 높은 사람은 유혹을 잘 참는 사람이 아니라 유혹이 적은 환경을 선택하는 사람이다. 자제력은 단기적인 전략일 뿐이므로, 장기적인 변화를 위해서는 유혹과 싸우기보다 환경을 바꾸는 편이 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 습관과 싸우기보다 나쁜 습관이 시작되는 환경과 신호를 제거하는 것이 훨씬 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 3. 두 번째 법칙, 매력적이어야 달라진다&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 08. 왜 어떤 습관은 더 하고 싶을까&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 단순히 행동의 반복으로 만들어지는 것이 아니라, 그 행동이 얼마나 매력적으로 느껴지는지에 따라 형성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 보상을 얻었을 때보다 보상을 기대할 때 더 큰 동기를 느낀다. 이러한 기대는 도파민 분비를 유도하고, 행동을 반복하도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관을 만들고 싶다면 '해야 하는 일'을 '하고 싶은 일'과 연결해야 한다. 저자는 이를 &lt;b&gt;유혹 묶기(Temptation Bundling)&lt;/b&gt; 전략이라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 필요한 행동보다 하고 싶은 행동을 선택한다. 좋은 습관도 매력적으로 느껴질 때 지속될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 09. 왜 주위 사람에 따라 내 습관이 변할까&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 어떤 행동을 선택하는지는 개인의 의지보다 자신이 속한 문화와 집단의 영향을 크게 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 가족과 친구, 자신이 속한 다수의 집단, 그리고 존경하는 사람들의 행동을 자연스럽게 모방한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관을 만들고 싶다면 의지만 키우는 것이 아니라, 원하는 행동이 자연스러운 문화와 환경 속으로 들어가는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 혼자 만드는 것이 아니다. 내가 속한 환경과 문화가 결국 나의 행동을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 10. 나쁜 습관도 즐겁게 고칠 수 있을까&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 습관을 없애려면 두 번째 행동 변화 법칙을 반대로 적용해야 한다. 즉, &lt;b&gt;매력적이지 않게 만들어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 행동은 표면적인 욕구보다 더 근본적인 욕망을 해결하기 위해 일어난다. 같은 욕망이라도 어떤 의미를 부여하느냐에 따라 좋은 습관이 될 수도, 나쁜 습관이 될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 습관을 하지 않았을 때 얻는 이점을 떠올리고, 좋은 습관을 시작하기 전에 긍정적인 감정을 만들어 주는 의식을 더하면 행동을 지속하기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 감정과 연결된다. 좋은 습관에는 긍정적인 의미를 부여하고, 나쁜 습관에는 부정적인 의미를 연결해야 행동이 변화한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 4. 세 번째 법칙, 쉬워야 달라진다&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 11. 1만 시간의 법칙은 틀렸다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 흔히 많은 시간을 투자해야 습관이 만들어진다고 생각한다. 하지만 저자는 시간보다 &lt;b&gt;반복 횟수&lt;/b&gt;가 더 중요하다고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 행동을 반복하는 과정에서 점차 자동화된다. 계획을 세우는 것보다 직접 실행하는 경험이 습관을 만드는 데 훨씬 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 습관은 얼마나 오래 했는지가 아니라, 얼마나 자주 반복했는지가 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 시간을 투자한다고 만들어지는 것이 아니라, 반복을 통해 몸에 익을 때 비로소 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 12. 웬만하면 쉽게 갑시다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간은 본능적으로 가장 적은 노력이 드는 선택을 한다. 따라서 좋은 습관도 어렵게 만들면 지속되기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관을 만들고 싶다면 행동의 마찰을 줄이고, 나쁜 습관을 줄이고 싶다면 마찰을 늘려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경을 조금만 바꾸어도 원하는 행동을 훨씬 쉽게 반복할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의지력을 키우기보다 좋은 행동은 쉽게, 나쁜 행동은 어렵게 만드는 환경을 설계해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 13. 변화를 위한 최소한의 시간&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 변화는 거창한 시작이 아니라 아주 작은 행동에서 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 새로운 습관을 시작할 때 &lt;b&gt;2분 규칙&lt;/b&gt;을 제안한다. 어떤 습관이든 처음에는 2분 안에 끝낼 수 있을 만큼 작게 시작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 행동이라도 반복되면 습관이 되고, 그 습관이 더 큰 행동으로 자연스럽게 확장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 크게 시작하는 것이 아니라, 실패할 수 없을 만큼 작게 시작해야 오래 지속된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 14. 그들은 어떻게 나쁜 습관을 버리는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 행동 변화 법칙을 반대로 적용하면 &lt;b&gt;'하기 어렵게 만들어라.'&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 습관을 없애기 위해서는 의지력보다 행동 자체를 어렵게 만드는 장치가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동이체, 자동 저축처럼 한 번의 선택으로 미래의 행동을 자동화하면 좋은 습관은 유지되고 나쁜 습관은 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 자동화하고, 나쁜 습관은 실행하기 어렵게 만들어야 행동이 자연스럽게 변화한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 5. 네 번째 법칙, 만족스러워야 달라진다&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 15. 재미와 보상 두 마리 토끼를 잡는 법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 시작하는 것보다 계속 유지하는 것이 더 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 습관을 지속시키는 가장 중요한 요소를 &lt;b&gt;즉각적인 만족감&lt;/b&gt;이라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인간은 장기적인 보상보다 지금 당장의 보상을 우선하도록 진화했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 좋은 습관도 즉시 성취감을 느낄 수 있어야 반복될 가능성이 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보상은 습관을 시작하게 만들고, 정체성은 습관을 지속하게 만든다. 작은 성공을 자주 경험할수록 습관은 자연스럽게 삶의 일부가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 미래의 보상만으로 유지되지 않는다. 지금 이 순간의 작은 만족감이 반복을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 16. 어떻게 매일 반복할 것인가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 꾸준함에서 완성된다. 저자는 이를 위해 &lt;b&gt;습관 추적(Habit Tracking)&lt;/b&gt;을 제안한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달력에 표시를 하거나 기록을 남기는 단순한 행동만으로도 자신의 성장을 눈으로 확인할 수 있고, 이는 반복하려는 동기가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽하게 지키는 것보다 중요한 것은 흐름을 끊지 않는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루를 놓쳤다면 가능한 한 빨리 원래의 습관으로 돌아와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꾸준함은 완벽함에서 나오는 것이 아니라, 중단하더라도 다시 시작하는 데서 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 17. 누군가 당신을 지켜보고 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 번째 행동 변화 법칙을 반대로 적용하면 &lt;b&gt;'불만족스럽게 만들어라.'&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 습관에 즉각적인 대가가 따른다면 그 행동은 자연스럽게 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관 계약이나 책임 파트너처럼 자신의 행동을 다른 사람에게 공개하면 책임감이 생기고, 약속을 지키려는 동기가 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 혼자 있을 때보다 누군가에게 책임을 질 때 행동을 더 잘 유지한다. 좋은 습관은 책임감을 통해 더욱 강해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Part 6. 최고의 습관은 어떻게 만들어지는가&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 18. 습관에도 적성이 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관이라고 해서 모두에게 같은 효과를 내는 것은 아니다. 자신의 성향과 강점에 맞는 습관을 선택해야 꾸준히 지속할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유전자는 바꿀 수 없지만, 자신에게 유리한 환경을 선택하는 것은 가능하다. 자신의 강점이 자연스럽게 발휘되는 분야를 선택하면 습관을 만드는 과정도 훨씬 수월해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 다른 사람을 따라 하는 것이 아니라, 자신에게 가장 잘 맞는 방식을 찾는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 습관은 남을 따라 하는 것이 아니라, 자신의 강점과 환경에 맞게 선택할 때 가장 오래 지속된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 19. 계속 해내는 힘은 어디서 오는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관을 오래 유지하는 가장 큰 적은 실패가 아니라 &lt;b&gt;지루함&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 자신의 능력보다 조금 어려운 과제를 수행할 때 가장 큰 동기를 느낀다. 이를 저자는 &lt;b&gt;골디락스 법칙&lt;/b&gt;으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전문가와 아마추어의 차이는 재능이 아니라, 흥미가 사라진 뒤에도 꾸준히 반복하는 능력에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구나 의욕이 있을 때는 행동한다. 진짜 차이는 지루한 순간에도 계속 반복하는 사람에게서 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chapter 20. 습관의 반격&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 내용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 행동을 자동화한다는 장점이 있지만, 익숙함 때문에 자신의 행동을 돌아보지 않게 만든다는 단점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관만 반복한다고 성장하는 것은 아니다. 습관에 &lt;b&gt;의도적인 연습과 성찰&lt;/b&gt;이 더해질 때 비로소 실력이 향상된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 하나의 정체성에만 지나치게 집착하면 새로운 변화와 성장을 받아들이기 어려워질 수 있다. 습관을 만들었다면 꾸준히 점검하고 수정하는 과정도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저자가 말하고 싶은 핵심&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관은 끝이 아니라 시작이다. 지속적인 성찰과 개선이 더해질 때 습관은 성장으로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 좋은 습관을 만드는 4가지 법칙&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(p268의 내용을 가져왔습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;첫 번째 법칙 &amp;middot; 분명하게 만들어라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;습관 점수표를 활용하여 현재 습관을 인식한다.&lt;/li&gt;
&lt;li&gt;실행 의도를 사용한다.&lt;/li&gt;
&lt;li&gt;습관 쌓기를 활용한다.&lt;/li&gt;
&lt;li&gt;좋은 습관의 신호가 잘 보이도록 환경을 설계한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 번째 법칙 &amp;middot; 매력적으로 만들어라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유혹 묶기를 활용한다.&lt;/li&gt;
&lt;li&gt;원하는 행동을 주변의 일반적인 행동으로 만든다.&lt;/li&gt;
&lt;li&gt;동기 부여 의식을 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;세 번째 법칙 &amp;middot; 하기 쉽게 만들어라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좋은 습관의 마찰을 줄인다.&lt;/li&gt;
&lt;li&gt;좋은 습관이 일어나기 쉬운 환경을 만든다.&lt;/li&gt;
&lt;li&gt;결정적 순간을 잘 활용한다.&lt;/li&gt;
&lt;li&gt;2분 규칙으로 시작한다.&lt;/li&gt;
&lt;li&gt;습관을 자동화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;네 번째 법칙 &amp;middot; 만족스럽게 만들어라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉시 보상받을 수 있도록 만든다.&lt;/li&gt;
&lt;li&gt;나쁜 습관을 피할 방법을 미리 준비한다.&lt;/li&gt;
&lt;li&gt;습관 추적을 한다.&lt;/li&gt;
&lt;li&gt;두 번 연속 거르지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 나쁜 습관을 버리는 4가지 법칙&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;첫 번째 법칙&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보이지 않게 만들어라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 번째 법칙&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매력적이지 않게 만들어라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;세 번째 법칙&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하기 어렵게 만들어라.&lt;/li&gt;
&lt;li&gt;이행 장치로 미래의 선택을 제한한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;네 번째 법칙&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불만족스러운 것으로 만들어라.&lt;/li&gt;
&lt;li&gt;책임 파트너와 습관 계약을 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『아주 작은 습관의 힘』은 단순히 좋은 습관을 만드는 방법을 설명하는 책이 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 규칙적인 생활과 자기관리를 위한 교양서라고 생각했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책을 읽을수록 저자가 말하고 싶은 핵심은 &lt;b&gt;'어떤 사람이 될 것인가'라는 질문&lt;/b&gt;에 있다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제임스 클리어는 &lt;b&gt;결과보다 시스템&lt;/b&gt;을, &lt;b&gt;시스템보다 정체성&lt;/b&gt;을 중요하게 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 행동 하나가 반복되면 습관이 되고, 습관은 정체성을 만들며, 정체성은 결국 삶의 방향을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 변화는 거대한 결심에서 시작되는 것이 아니라, 오늘 반복한 아주 작은 행동에서 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 책은 의지력에만 의존하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경을 설계하고, 행동의 마찰을 줄이며, 작은 성공을 반복할 수 있는 시스템을 만드는 것이 더 중요하다고 이야기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 좋은 습관은 의지가 아니라 설계의 결과라는 점이 인상 깊었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책을 덮고 나니 &lt;b&gt;'나는 어떤 습관을 가져야 하는가'&lt;/b&gt;보다 &lt;b&gt;'나는 어떤 사람이 되고 싶은가'&lt;/b&gt;를 먼저 고민하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 정체성이 분명해질수록 어떤 습관을 반복해야 하는지도 자연스럽게 결정될 것이라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결국 『아주 작은 습관의 힘』은 습관에 관한 책이 아니라, &lt;br /&gt;&lt;/span&gt;반복되는 작은 행동을 통해 자신만의 정체성을 만들어 가고,&lt;br /&gt;자신다운 삶을 살아가기 위한 행동 설계 매뉴얼이었다.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;</description>
      <category>책이야기</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1113</guid>
      <comments>https://peterica.tistory.com/1113#entry1113comment</comments>
      <pubDate>Sun, 2 Aug 2026 23:33:51 +0900</pubDate>
    </item>
    <item>
      <title>[생각] 공정은 법을 적용하는 것이 아니라 공동체를 살리는 것이다 - 아피케이아(Epieikeia)</title>
      <link>https://peterica.tistory.com/1107</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UYaut/dJMcajpsjuh/BlgmoT0ArCPbqsZ5vRzC60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UYaut/dJMcajpsjuh/BlgmoT0ArCPbqsZ5vRzC60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UYaut/dJMcajpsjuh/BlgmoT0ArCPbqsZ5vRzC60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUYaut%2FdJMcajpsjuh%2FBlgmoT0ArCPbqsZ5vRzC60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 윤리위원이라는 역할을 맡게 되면서 자연스럽게 '공정'이라는 단어를 다시 생각하게 되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 공정이란 규정을 동일하게 적용하는 것이라고 생각했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 누가 잘못했는지 판단하고,&lt;br /&gt;&amp;nbsp; 규정에 맞게 결론을 내리는 것.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 그것이 가장 공정한 일이라고 믿었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 여러 철학과 사례를 접하면서 생각이 조금씩 바뀌기 시작했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;과연 법을 가장 정확하게 적용하는 것이 공정일까.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 아니면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; 공동체를 가장 건강하게 만드는 판단이 공정일까.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 질문은 나를 아리스토텔레스의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;아피케이아(Epieikeia)&lt;/i&gt;라는 개념으로 이끌었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 아피케이아(Epieikeia)란 무엇인가&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아리스토텔레스는 법이 아무리 잘 만들어져도 모든 상황을 예측할 수는 없다고 보았다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;법은 일반적인 상황을 기준으로 만들어진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 현실은 언제나 예외로 가득하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 그는 훌륭한 판단은 법을 무시하는 것이 아니라,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;법이 존재하는 목적을 실현하는 것&lt;/b&gt;이라고 설명했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것이 바로 아피케이아다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아피케이아는 법을 약하게 만드는 개념이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오히려 법을 인간답게 만드는 마지막 덕목이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 법은 목적이 아니라 수단이다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리는 종종 법과 규정을 절대적인 기준으로 생각한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 규정은 공동체를 유지하기 위한 최소한의 약속이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 규정이 존재하는 이유는 사람을 지키기 위해서다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;규정을 지키는 것이 목적이 되어버리면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어느 순간 공동체보다 절차가 중요해지고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람보다 규정이 우선하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그때부터 공정은 정의가 아니라 형식이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 윤리위원에게 필요한 것은 판단보다 지혜다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;윤리위원의 역할은 법률가와 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;법률은 위법 여부를 판단하지만,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;윤리는 공동체가 앞으로 어떻게 살아갈 것인가를 고민한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 윤리위원은 단순히&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;b&gt;&quot;누가 잘못했는가.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만을 묻는 사람이 아니라,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&quot;왜 이런 일이 발생했는가.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&quot;어떻게 하면 같은 일이 반복되지 않을 것인가.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;를 함께 고민해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;징계가 필요한 경우도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 모든 갈등의 해답이 징계일 수는 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;갈등의 원인을 제거하지 못한다면,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람만 바뀔 뿐 같은 문제는 다시 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 공정은 공동체를 회복시키는 힘이다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;성경 속 솔로몬의 재판은 유명하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람들은 솔로몬이 어려운 판결을 내렸다고 기억하지만,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;본질은 다른 곳에 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;솔로몬은 법 조항을 찾은 것이 아니라,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;무엇이 생명을 살리는 판단인지를 보았다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공정도 마찬가지다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공정은 가장 엄격한 처벌을 내리는 능력이 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공동체의 신뢰를 회복하고,&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사람들이 다시 함께 살아갈 수 있도록 만드는 판단이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 과정에서 법은 중요한 기준이 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그러나 법은 언제나 공동체를 위한 도구여야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘날 우리는 공정을 이야기할 때 너무 쉽게 '법대로'를 떠올린다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 법은 공정의 출발점일 뿐, 목적지는 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공정의 목적지는 공동체다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공동체를 살리는 판단.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람을 회복시키는 판단.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 법이 만들어진 이유를 끝까지 지켜내려는 지혜.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나는 그것이 아리스토텔레스가 말한 아피케이아이며,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘날 윤리위원에게 가장 필요한 덕목이라고 생각한다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;결국 진정한 공정은&lt;br /&gt;&amp;nbsp; 법을 가장 잘 적용하는 사람이 아니라,&lt;br /&gt;&amp;nbsp; 공동체를 가장 오래 건강하게 만드는 사람이 만들어 가는 것인지도 모른다.&lt;/blockquote&gt;</description>
      <category>LifeStory/블로그</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1107</guid>
      <comments>https://peterica.tistory.com/1107#entry1107comment</comments>
      <pubDate>Fri, 31 Jul 2026 20:41:40 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 27. 편지 한 통의 여정을 혼자 설명할 수 있을까?</title>
      <link>https://peterica.tistory.com/1104</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ulHvU/dJMcaalIATP/r2Ag7O4ccRuM5KM2Jcxznk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ulHvU/dJMcaalIATP/r2Ag7O4ccRuM5KM2Jcxznk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ulHvU/dJMcaalIATP/r2Ag7O4ccRuM5KM2Jcxznk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FulHvU%2FdJMcaalIATP%2Fr2Ag7O4ccRuM5KM2Jcxznk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; 에필로그. 우리는 인터넷이 아니라 물류 시스템을 이해했다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Browser의 주소창에 URL 하나를 입력했다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://server.example/orders&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 뒤 주문 목록이 화면에 나타났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 흔히 이 짧은 사건을 이렇게 말한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Server에 접속했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 그 한 문장 안에서는 수많은 결정이 일어났다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름을 어느 IP 주소로 바꿀지 정했다.&lt;/li&gt;
&lt;li&gt;목적지가 같은 동네인지 다른 동네인지 판단했다.&lt;/li&gt;
&lt;li&gt;다음 전달자의 MAC 주소를 알아냈다.&lt;/li&gt;
&lt;li&gt;Switch와 Router가 각자의 표를 보고 다음 길을 골랐다.&lt;/li&gt;
&lt;li&gt;사설 주소를 공인 주소와 Port의 조합으로 바꾸었다.&lt;/li&gt;
&lt;li&gt;상대와 연결을 만들고, 암호화할 상대가 맞는지 확인했다.&lt;/li&gt;
&lt;li&gt;사라진 Data를 다시 보내고, 받는 쪽과 길의 형편에 맞춰 속도를 조절했다.&lt;/li&gt;
&lt;li&gt;돌아온 응답을 원래 요청한 Process와 Browser Tab에 전달했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가운데 어느 하나만 빠져도 화면에는 그저 &lt;code&gt;timeout&lt;/code&gt;이나 연결 오류가 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 지금까지 배운 용어를 외우지 않고도, 이 편지 한 통의 여정을 처음부터 끝까지 자기 언어로 설명할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 새로운 Protocol을 배우지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 흩어져 있던 &lt;code&gt;DNS&lt;/code&gt;, &lt;code&gt;ARP&lt;/code&gt;, &lt;code&gt;Switch&lt;/code&gt;, &lt;code&gt;Routing&lt;/code&gt;, &lt;code&gt;TTL&lt;/code&gt;, &lt;code&gt;MTU&lt;/code&gt;, &lt;code&gt;NAT&lt;/code&gt;, &lt;code&gt;Firewall&lt;/code&gt;, &lt;code&gt;TCP&lt;/code&gt;, &lt;code&gt;TLS&lt;/code&gt;, &lt;code&gt;HTTP&lt;/code&gt;를 하나의 시간 순서로 다시 잇는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연재의 마지막 질문은 &lt;b&gt;무엇이 동작했는가&lt;/b&gt;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;각 기술이 왜 그 자리에 있어야 했는가&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한 통의 편지를 끝까지 따라가기 위한 약속&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 흐름을 놓치지 않도록 다음 환경을 사용한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 55.5814%; height: 326px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;td&gt;값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;192.168.10.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client가 선택한 Source Port&lt;/td&gt;
&lt;td&gt;51514&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS Resolver&lt;/td&gt;
&lt;td&gt;192.168.10.53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default Gateway&lt;/td&gt;
&lt;td&gt;192.168.10.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT 장비의 공인 IP&lt;/td&gt;
&lt;td&gt;203.0.113.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT가 바꾼 Source Port&lt;/td&gt;
&lt;td&gt;40001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Server&lt;/td&gt;
&lt;td&gt;198.51.100.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Service Port&lt;/td&gt;
&lt;td&gt;443/TCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;https://server.example/orders&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;192.168.10.0/24&lt;/code&gt;는 사설 주소 대역이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;198.51.100.0/24&lt;/code&gt;와 &lt;code&gt;203.0.113.0/24&lt;/code&gt;는 문서 예시를 위해 예약된 주소 대역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 인터넷 Service를 뜻하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명을 단순하게 만들기 위해 IPv4를 중심으로 따라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 Broadcast 기반 ARP 대신 ICMPv6의 &lt;b&gt;Neighbor Discovery Protocol&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 즉 &lt;code&gt;NDP&lt;/code&gt;가 이웃과 Router를 찾고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 일반적으로 IPv4 주소 공유를 위한 NAT와는 다른 방식으로 주소를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나 기억할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application은 처음부터 완성된 &quot;편지 한 통&quot;을 Network에 던지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 계층은 자기 역할에 필요한 Header를 붙이고, 반대편은 이를 차례로 해석한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;HTTP Message
  &amp;darr; TLS로 보호
TLS Record
  &amp;darr; TCP가 순서와 신뢰성 정보 추가
TCP Segment
  &amp;darr; IP가 출발지와 최종 목적지 정보 추가
IP Packet
  &amp;darr; Ethernet이 현재 Link의 전달자 정보 추가
Ethernet Frame
  &amp;darr;
Bit와 Signal&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유의 편지는 하나처럼 보이지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 실제 Network에는 계층별 Header와 여러 Packet이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS Query, ARP Request, TCP Handshake, TLS Handshake까지 포함하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP Request 하나를 보내기 전에도 이미 여러 번의 통신이 오간다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 1단계: Browser는 URL에서 먼저 무엇을 꺼낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Browser가 받은 것은 IP 주소가 아니라 URL이다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://server.example/orders&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Browser는 이 문자열을 적어도 다음 요소로 나눈다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 68.6047%; height: 155px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL 요소&lt;/td&gt;
&lt;td&gt;값&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheme&lt;/td&gt;
&lt;td&gt;https&lt;/td&gt;
&lt;td&gt;어떤 방식으로 통신할지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host&lt;/td&gt;
&lt;td&gt;server.example&lt;/td&gt;
&lt;td&gt;어느 Host를 찾을지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port&lt;/td&gt;
&lt;td&gt;생략됨, 기본값&lt;span&gt;&amp;nbsp;&lt;/span&gt;443&lt;/td&gt;
&lt;td&gt;어느 Service와 대화할지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path&lt;/td&gt;
&lt;td&gt;/orders&lt;/td&gt;
&lt;td&gt;Service 안의 어느 Resource를 요청할지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 주소로 보면 나라와 도시, 건물, 부서를 나누는 일과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 기술에서 이 값들은 서로 다른 계층의 결정에 쓰인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Host 이름은 DNS 조회의 입력이 된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;443&lt;/code&gt;은 TCP Destination Port가 된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/orders&lt;/code&gt;는 TCP 연결이 만들어지고 TLS가 준비된 뒤 HTTP Request에 담긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;code&gt;/orders&lt;/code&gt;를 Router가 보고 길을 고르는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 일반적으로 URL Path를 알지 못하며 IP Header를 중심으로 전달을 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로 필요한 것은 &lt;code&gt;server.example&lt;/code&gt;이라는 이름을 숫자 주소로 바꾸는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 2단계: 이름은 어떻게 &lt;code&gt;198.51.100.20&lt;/code&gt;이 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 이름을 기억하기 쉽지만, IP 계층은 Destination IP 주소가 있어야 Packet을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Host는 먼저 가까운 기억을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Browser DNS Cache
    &amp;darr; 없으면
Operating System Cache와 Hosts 설정
    &amp;darr; 없으면
설정된 DNS Resolver&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache에 유효한 Answer가 없다면 Client는 설정된 Resolver &lt;code&gt;192.168.10.53&lt;/code&gt;에 질문한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;server.example의 A Record는 무엇입니까?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resolver는 자신의 Cache를 확인하고, 필요하다면 DNS 계층을 따라 권한 있는 Name Server의 Answer를 찾아 다음과 같이 돌려준다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;server.example.  300  IN  A  198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;300&lt;/code&gt;은 이 Answer를 무조건 영원히 기억하라는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TTL 300초&lt;/code&gt; 동안 Cache할 수 있다는 뜻이다. DNS의 TTL은 IP Header의 TTL과 이름은 같지만 역할이 다르다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 61.8605%; height: 91px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 이름&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;실제 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;DNS TTL&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Record를 Cache할 수 있는 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IP TTL&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Packet이 통과할 수 있는 Router 수의 한계&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;A&lt;/code&gt; Record는 IPv4 주소를, &lt;code&gt;AAAA&lt;/code&gt; Record는 IPv6 주소를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 주소가 돌아오면 Client는 주소 Family, 도달 가능성, 정책과 구현에 따라 연결 후보와 시도 순서를 정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS는 공용 주소록과 닮았지만, 종이 주소록처럼 단순한 한 권의 책은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산된 권한과 Cache로 규모를 감당하는 이름 체계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DNS Query 자체도 Network Packet이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resolver가 같은 Subnet에 있다면 곧 살펴볼 Route 판단과 ARP가 DNS Query를 보낼 때도 먼저 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여정은 완전히 일직선인 목록이 아니라, 작은 여정이 큰 여정 안에 중첩된 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결한 문제는 &lt;b&gt;사람이 숫자 주소를 모두 기억하지 않고도 목적지를 찾는 확장성&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 3단계: 목적지를 알았으면 어느 문으로 내보낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Operating System은 목적지 &lt;code&gt;198.51.100.20&lt;/code&gt;을 안다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 IP 주소만 안다고 바로 Signal을 보낼 수 있는 것은 아니다. 먼저 자신의 Routing Table을 조회해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 Table이 다음과 같다고 해 보자.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;192.168.10.0/24 dev eth0  src 192.168.10.20
default via 192.168.10.1 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Operating System은 Destination과 가장 길게 일치하는 Prefix를 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 &lt;b&gt;Longest Prefix Match&lt;/b&gt;, 최장 접두사 일치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;198.51.100.20&lt;/code&gt;은 &lt;code&gt;192.168.10.0/24&lt;/code&gt;에 속하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 구체적인 Route도 없으므로 &lt;code&gt;default&lt;/code&gt; Route가 선택된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source IP    192.168.10.20
Destination  198.51.100.20
Egress       eth0
Next Hop     192.168.10.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 구분이 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;최종 목적지 IP&lt;/b&gt;는 &lt;code&gt;198.51.100.20&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지금 Link에서 건넬 상대&lt;/b&gt;는 Gateway &lt;code&gt;192.168.10.1&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼 지역으로 가는 편지에도 봉투에는 최종 수신인의 주소를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 집 앞에서는 그 편지를 가까운 우체국에 맡긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Gateway는 모든 목적지를 직접 아는 마법의 장비가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 구체적인 Route가 없을 때 선택한 &lt;b&gt;다음 전달자&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결한 문제는 모든 Host가 인터넷의 모든 길을 기억하지 않고도 먼 목적지로 출발할 수 있게 하는 &lt;b&gt;확장성&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 4단계: Gateway의 IP만 알면 Frame을 보낼 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet Link에서 Frame을 보내려면 Destination MAC 주소가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 자신의 Neighbor Table에서 &lt;code&gt;192.168.10.1&lt;/code&gt;에 대응하는 MAC 주소를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효한 항목이 없다면 ARP Request를 Broadcast한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;누가 192.168.10.1을 사용하고 있습니까?
192.168.10.20에게 알려 주십시오.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Broadcast Domain의 장비들은 Request를 받을 수 있지만, &lt;code&gt;192.168.10.1&lt;/code&gt;을 가진 Gateway가 Reply한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;192.168.10.1은 02:00:00:00:10:01입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 이 대응 관계를 Neighbor Table에 일정 시간 저장한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;192.168.10.1 &amp;rarr; 02:00:00:00:10:01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 첫 Ethernet Frame의 주소를 채울 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 40.4651%; height: 127px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Header&lt;/td&gt;
&lt;td&gt;출발지&lt;/td&gt;
&lt;td&gt;목적지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ethernet&lt;/td&gt;
&lt;td&gt;Client MAC&lt;/td&gt;
&lt;td&gt;Gateway MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPv4&lt;/td&gt;
&lt;td&gt;192.168.10.20&lt;/td&gt;
&lt;td&gt;198.51.100.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;51514&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 먼 Server의 MAC 주소를 ARP로 묻지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP는 같은 L2 범위에서 &lt;b&gt;지금 직접 건넬 이웃&lt;/b&gt;을 찾기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유에서 ARP는 같은 동네의 실제 집을 찾는 주소 조회와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제 ARP는 사람의 이름을 IP로 바꾸는 DNS가 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Link에서 Next-Hop IPv4 주소를 MAC 주소에 대응시키는 Protocol이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결한 문제는 논리적 IP 목적지를 실제 Local Link 전달에 필요한 주소와 연결하는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 5단계: Switch는 Server까지의 길을 알고 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Frame을 내보내면 Switch가 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch는 Frame의 Source MAC을 보고 다음 사실을 학습한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client MAC은 이 Port 뒤에 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Destination MAC, 즉 Gateway MAC을 MAC Address Table에서 조회한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Entry가 있으면 해당 Port로 전달한다.&lt;/li&gt;
&lt;li&gt;Entry가 없으면 같은 VLAN의 다른 Port로 Unknown Unicast Flooding한다.&lt;/li&gt;
&lt;li&gt;Broadcast인 ARP Request는 같은 VLAN의 Broadcast Domain에 Flooding한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch는 &lt;code&gt;198.51.100.20&lt;/code&gt;으로 가는 인터넷 Route를 고르는 장비가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시점의 Switch가 관심을 갖는 주소는 현재 Ethernet Frame의 Destination MAC이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN이 있다면 하나의 물리 Switch도 여러 Broadcast Domain으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 VLAN 사이의 통신에는 L3 Routing이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이중화 Link가 Layer 2에서 원을 만들면 Broadcast와 Unknown Unicast가 끝없이 복제될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STP 계열 Protocol은 일부 Link를 논리적으로 차단해 Loop 없는 전달 구조를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch, VLAN, STP가 함께 해결한 문제는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 가까운 길을 많은 장비가 &lt;b&gt;공유&lt;/b&gt;하면서도 Frame을 필요한 방향으로 보내고 Loop를 막는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 6단계: Router는 봉투를 그대로 다음 우체국에 넘길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gateway는 자신의 MAC 주소로 온 Frame을 받고 Ethernet Header를 벗겨 IP Packet을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination IP는 여전히 &lt;code&gt;198.51.100.20&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 자신의 Routing Table에서 다시 Longest Prefix Match를 수행해 다음 Hop과 Egress Interface를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Router는 IP TTL을 1 줄인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 64 &amp;rarr; TTL 63&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header가 바뀌었으므로 Header Checksum도 다시 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 0이 되면 Router는 Packet을 폐기하고, 일반적으로 출발지에 ICMP &lt;code&gt;Time Exceeded&lt;/code&gt; 메시지를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 잘못된 Route가 원을 그릴 때 Packet이 Network 자원을 영원히 소비하지 않게 하는 안전장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Link로 내보낼 때 Router는 이전 Ethernet Frame을 그대로 재사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Hop의 MAC 주소를 ARP로 확인한 뒤 &lt;b&gt;새 Ethernet Header&lt;/b&gt;를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;첫 번째 Link
Client MAC &amp;rarr; Gateway LAN MAC

두 번째 Link
Gateway WAN MAC &amp;rarr; ISP Next-Hop MAC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 봉투를 통째로 새 봉투에 넣는다고 상상할 수 있지만, 비유의 경계를 분명히 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 각 L2 Link에 맞는 Frame Header가 매 Hop 새로 만들어지고, 그 안의 IP Packet은 Route를 따라 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 해결한 문제는 각 장비가 전체 경로가 아니라 &lt;b&gt;다음 길&lt;/b&gt;만 골라도 멀리 확장할 수 있게 하는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 7단계: 소포가 다음 길보다 크다면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route가 있다고 모든 크기의 Packet이 그대로 지나갈 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Link에는 한 Frame이 실을 수 있는 IP Packet 크기의 한계인 &lt;b&gt;MTU&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet에서 흔히 보는 값은 &lt;code&gt;1500 bytes&lt;/code&gt;지만, Tunnel이나 다른 Link를 지나면 더 작을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Packet이 Egress MTU보다 크면 조건에 따라 다음 중 하나가 일어난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fragmentation이 허용되면 Router 또는 출발지가 Packet을 여러 Fragment로 나눈다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DF&lt;/code&gt;, Don't Fragment가 설정되어 있으면 Router는 Packet을 버리고 ICMP &lt;code&gt;Fragmentation Needed&lt;/code&gt;를 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출발지는 이 정보를 이용해 경로에서 통과 가능한 크기를 알아내고 Packet 크기를 조절할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 &lt;b&gt;Path MTU Discovery, PMTUD&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 보통 Interface MTU와 협상한 &lt;code&gt;MSS&lt;/code&gt;를 고려해 Segment의 Payload 크기를 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS는 TCP Payload의 최대 크기에 관한 값이고, MTU는 Link가 실을 수 있는 IP Packet 전체 크기에 관한 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP가 무조건 불필요한 Traffic이라며 중간에서 사라지면 작은 Request는 성공하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 TLS Record나 Response만 멈추는 &lt;b&gt;PMTUD Black Hole&lt;/b&gt;이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결하려는 문제는 서로 다른 크기의 길을 지나는 동안 자원을 낭비하지 않고 전달 가능한 크기를 찾는 &lt;b&gt;효율&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 8단계: 사설 주소는 인터넷에서 어떻게 돌아올 주소가 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 Source IP &lt;code&gt;192.168.10.20&lt;/code&gt;은 사설 주소다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Internet의 Router가 이 주소를 전 세계의 고유 목적지로 전달하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경계의 NAT 장비는 Packet이 밖으로 나갈 때 Source를 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;변환 전
192.168.10.20:51514 &amp;rarr; 198.51.100.20:443

변환 후
203.0.113.5:40001 &amp;rarr; 198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소뿐 아니라 Port까지 바꾸어 여러 내부 연결을 구분하는 동작을 흔히 &lt;code&gt;NAPT&lt;/code&gt; 또는 PAT라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일상적으로는 이를 포함해 NAT라고 말하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 장비는 다음과 같은 Mapping State를 기억한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP 203.0.113.5:40001 &amp;harr; 192.168.10.20:51514
Remote Peer: 198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소나 TCP Port가 바뀌면 해당 값을 포함해 계산한 Checksum도 맞게 갱신해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 Reply는 다음 주소로 돌아온다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;198.51.100.20:443 &amp;rarr; 203.0.113.5:40001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 장비는 저장한 State를 찾아 Destination을 원래 Client로 되돌린다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;198.51.100.20:443 &amp;rarr; 192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 여러 집이 하나의 공인 주소를 함께 쓰게 해 IPv4 주소 부족을 견디는 데 큰 역할을 했다. 이것은 &lt;b&gt;공유&lt;/b&gt;의 해법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 NAT가 보안을 자동으로 완성하는 것은 아니다. State에 없는 외부 연결이 안쪽으로 들어오기 어려운 현상과 명시적인 Security Policy는 구분해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 NAT가 있는 구간에서는 IP 주소와 Port가 End-to-End로 완전히 동일하게 유지되지 않는다. 장애를 분석할 때 안쪽 Capture와 바깥쪽 Capture의 5-Tuple이 달라지는 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 9단계: Firewall은 주소만 보고 문을 열까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경계에는 NAT와 별개로 Firewall Policy가 적용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 Rule은 다음 정보를 기준으로 허용하거나 차단할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Source와 Destination IP&lt;/li&gt;
&lt;li&gt;Protocol&lt;/li&gt;
&lt;li&gt;Source와 Destination Port&lt;/li&gt;
&lt;li&gt;Ingress와 Egress Interface 또는 Zone&lt;/li&gt;
&lt;li&gt;연결 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 TCP Packet 하나만 고립해서 보지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Packet이 새 연결을 시작하는지 이미 허용된 연결의 일부인지 추적한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 환경에서는 이 상태 추적을 &lt;code&gt;Conntrack&lt;/code&gt;과 연결해 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;NEW &amp;rarr; ESTABLISHED &amp;rarr; FIN/종료 또는 Timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 보안실이 발신지와 수신 부서, 현재 진행 중인 방문인지 확인하는 모습과 닮았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Firewall이 &lt;code&gt;443/TCP&lt;/code&gt;를 허용했다는 사실은 다음을 보장하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server Process가 실제로 Listen 중인지&lt;/li&gt;
&lt;li&gt;TLS Certificate가 올바른지&lt;/li&gt;
&lt;li&gt;HTTP 요청 권한이 있는지&lt;/li&gt;
&lt;li&gt;응답이 정상인지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall은 &lt;b&gt;통신을 허용할지&lt;/b&gt; 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 &lt;b&gt;상대와 내용의 신뢰&lt;/b&gt;를 다루고, Application 인증은 &lt;b&gt;사용자가 무엇을 할 수 있는지&lt;/b&gt; 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 10단계: TCP는 왜 Data부터 보내지 않을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 Policy를 통과할 수 있게 되었지만, Client는 곧바로 HTTP Request부터 보내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 &lt;code&gt;443/TCP&lt;/code&gt;와 신뢰성 있는 Byte Stream을 만들기 위해 TCP Three-Way Handshake를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명을 위해 Client의 초기 Sequence Number를 &lt;code&gt;1000&lt;/code&gt;, Server의 초기 Sequence Number를 &lt;code&gt;7000&lt;/code&gt;이라고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server  SYN      Seq=1000
Server &amp;rarr; Client  SYN-ACK  Seq=7000 Ack=1001
Client &amp;rarr; Server  ACK      Seq=1001 Ack=7001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번의 교환에는 각각 이유가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Client가 Server에 도달할 수 있고 연결 의사가 있음을 알린다.&lt;/li&gt;
&lt;li&gt;Server가 Client의 요청을 받았고, 반대 방향으로도 도달할 수 있음을 알린다.&lt;/li&gt;
&lt;li&gt;Client가 Server의 응답까지 받았음을 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN과 FIN은 Sequence Number 공간에서 1을 소비한다. 그래서 SYN &lt;code&gt;Seq=1000&lt;/code&gt;에 대한 확인은 &lt;code&gt;Ack=1001&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결은 단순히 &quot;두 IP가 연결되었다&quot;고만 구분하지 않는다. 일반적으로 다음 5-Tuple로 흐름을 식별한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Protocol, Source IP, Source Port, Destination IP, Destination Port&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 안쪽에서 보면 다음 흐름이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP, 192.168.10.20, 51514, 198.51.100.20, 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 바깥에서는 다음처럼 보인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP, 203.0.113.5, 40001, 198.51.100.20, 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port 덕분에 한 Host의 여러 Process와 여러 연결이 같은 IP 주소를 함께 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것도 제한된 주소와 경로를 함께 쓰는 &lt;b&gt;공유&lt;/b&gt;의 장치다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 11단계: Handshake 뒤에는 무엇이 신뢰를 만들까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Handshake가 끝났다고 이후 Data가 사라지지 않는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Network는 Packet의 전달, 순서와 중복 제거를 완전히 보장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 여러 약속을 조합해 Application에 순서 있는 Byte Stream을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Sequence Number&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Byte의 위치를 나타낸다. Segment가 순서가 바뀌어 도착해도 어느 자리에 놓을지 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Acknowledgment&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신자가 다음에 기대하는 Byte를 알린다. &lt;code&gt;Ack=2501&lt;/code&gt;은 일반적으로 &lt;code&gt;2500&lt;/code&gt;까지 연속해서 받았고 다음은 &lt;code&gt;2501&lt;/code&gt;이라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;ㅇ Retransmission&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK가 정해진 시간 안에 오지 않거나 중복 ACK 같은 손실 단서가 생기면 필요한 Data를 다시 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Receive Window와 Flow Control&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신자가 감당할 수 있는 Buffer 범위를 광고한다. 빠른 Sender가 느린 Receiver를 압도하지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Congestion Control&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network 길 자체가 감당할 수 있는 양을 추정해 전송량을 조절한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver의 여유와 Network의 여유는 다른 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 전송 가능량은 대략 다음 두 한계 가운데 더 작은 쪽의 영향을 받는다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;min(Receiver Window, Congestion Window)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 신뢰성은 Packet 하나에 붙은 특별한 도장 하나가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서 번호, ACK, Timer, 재전송, 흐름 제어와 혼잡 제어가 함께 만든 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결한 문제는 손실과 지연이 있는 공유 길 위에서도 대화를 이어 가는 &lt;b&gt;신뢰&lt;/b&gt;와 &lt;b&gt;효율&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 12단계: TCP 연결이 되면 상대를 믿어도 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 성공은 &lt;code&gt;198.51.100.20:443&lt;/code&gt;까지 Byte를 주고받을 수 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 상대가 정말 &lt;code&gt;server.example&lt;/code&gt;인지, 중간 누군가가 내용을 읽거나 바꾸지 않았는지는 말해 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS에서는 TCP 위에서 TLS Handshake가 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 세부 Message와 선택 사항은 상황에 따라 달라지지만, 큰 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ClientHello
  - 지원하는 TLS Version과 Cipher Suite
  - Key Share
  - SNI: server.example

ServerHello와 Server 인증 정보
  - 선택한 Parameter
  - Key Share
  - Certificate Chain
  - Certificate로 증명한 서명

양쪽의 Finished 검증
  - Handshake가 변조되지 않았는지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Certificate를 받을 뿐 아니라 다음을 검증해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신뢰하는 CA로 이어지는 Chain인가?&lt;/li&gt;
&lt;li&gt;Certificate의 유효 기간 안인가?&lt;/li&gt;
&lt;li&gt;접속한 Host 이름 &lt;code&gt;server.example&lt;/code&gt;과 일치하는가?&lt;/li&gt;
&lt;li&gt;필요한 사용 목적과 정책을 만족하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Key Exchange 결과로 공유한 Secret에서 Traffic Key를 만들어 이후 TLS Record를 암호화하고 무결성을 보호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유로는 봉투를 잠그고 수신인의 신분증을 확인하는 일과 닮았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제 TLS는 모든 중간 Router를 신뢰해서 내용을 맡기는 방식이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Endpoint끼리 암호학적으로 상대와 Message를 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SNI나 IP&amp;middot;Port 같은 일부 Metadata는 구성과 TLS Version, 추가 보호 기술에 따라 여전히 관찰될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화는 Packet의 존재와 모든 Header를 사라지게 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 해결하는 문제는 믿을 수 없는 중간 경로에서도 상대와 내용에 대한 &lt;b&gt;신뢰&lt;/b&gt;를 만드는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 13단계: 드디어 HTTP Request가 출발한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP와 TLS가 준비된 뒤에야 Browser는 보호된 연결 안으로 HTTP 의미를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는 다음과 같은 Request다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;GET /orders HTTP/1.1
Host: server.example
Accept: application/json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Service는 HTTP/2나 HTTP/3를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시는 지금까지 따라온 TCP와 TLS의 연결을 분명하게 보기 위해 TCP 기반 HTTPS 흐름으로 단순화했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/3는 TCP 대신 QUIC을 UDP 위에서 사용하므로 Transport의 세부 여정이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 쪽에서는 역순으로 계층을 해석한다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;NIC가 Frame 수신
&amp;rarr; IP가 Local Destination 확인
&amp;rarr; TCP가 443 Port의 Socket과 연결 식별
&amp;rarr; TLS가 Record 복호화와 무결성 검증
&amp;rarr; HTTP Server가 Method, Host, Path 해석
&amp;rarr; Application이 주문 목록 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server가 Request를 받았다는 사실과 업무가 성공했다는 사실도 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP ACK는 Byte 수신을 확인한다.&lt;/li&gt;
&lt;li&gt;TLS는 보호된 Record의 무결성을 확인한다.&lt;/li&gt;
&lt;li&gt;HTTP Status는 Request 처리 결과를 표현한다.&lt;/li&gt;
&lt;li&gt;업무 Transaction과 Log는 Application 내부 결과를 보여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;HTTP 500&lt;/code&gt;은 Network 연결 실패가 아니라, Network와 TLS를 지나 Application이 오류 응답을 돌려준 증거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 처리 결과를 HTTP Response로 만들고 TLS로 보호한 뒤 TCP Byte Stream에 실어 보낸다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json

[{&quot;orderId&quot;: 42, &quot;status&quot;: &quot;ready&quot;}]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 14단계: 응답은 왔던 길을 그대로 되돌아올까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 그렇지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Internet Routing은 방향마다 독립적으로 결정될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 &lt;code&gt;A &amp;rarr; B &amp;rarr; C&lt;/code&gt;를 지났더라도 응답이 &lt;code&gt;C &amp;rarr; D &amp;rarr; A&lt;/code&gt;로 돌아올 수 있다. 이를 &lt;b&gt;Asymmetric Routing&lt;/b&gt;, 비대칭 경로라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 Reply는 Server 쪽 Routing Table부터 다시 조회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 바깥에서 Reply의 주소는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;198.51.100.20:443 &amp;rarr; 203.0.113.5:40001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경계 NAT 장비에 도착하면 기존 Mapping에 따라 다음처럼 복원된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;198.51.100.20:443 &amp;rarr; 192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 내부 Route와 Neighbor Table을 사용해 Client에 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall이나 NAT가 연결 상태를 기억한다면 Reply가 그 State를 볼 수 있는 경로로 돌아와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭 경로 자체는 IP Routing에서 가능하지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 방향의 상태만 아는 장비를 우회하거나 다른 장비로 들어오면 정상 Reply도 새롭거나 알 수 없는 Traffic으로 판단될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에 도착한 TCP Segment는 5-Tuple과 Socket 상태를 바탕으로 원래 연결에 Demultiplexing된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Sequence Number를 보고 Byte를 순서대로 조립하고, TLS는 Record를 검증해 복호화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Browser는 HTTP Response를 원래 요청과 연결해 화면을 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌아오는 길은 단순한 후일담이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 목적지에 도착했는데도 사용자가 실패를 보는 이유는 &lt;b&gt;응답 Route, NAT State, Firewall Policy와 Client Socket까지 모두 이어져야 하기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한눈에 보면 무엇이 바뀌고 무엇이 남을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet을 분석할 때 모든 주소가 처음부터 끝까지 그대로라고 생각하면 NAT와 Router의 동작을 혼동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Router가 모든 주소를 바꾼다고 생각해도 틀린다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;정보&lt;/td&gt;
&lt;td&gt;일반적인 변화&lt;/td&gt;
&lt;td&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ethernet Source/Destination MAC&lt;/td&gt;
&lt;td&gt;L2 Link를 지날 때마다 바뀜&lt;/td&gt;
&lt;td&gt;현재 Link의 Sender와 Next Hop을 가리킴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Source/Destination&lt;/td&gt;
&lt;td&gt;Router를 지나도 보통 유지&lt;/td&gt;
&lt;td&gt;최종 Endpoint를 표현함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Source/Destination의 예외&lt;/td&gt;
&lt;td&gt;NAT 구간에서 변환될 수 있음&lt;/td&gt;
&lt;td&gt;주소 공유 또는 주소 영역 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP TTL&lt;/td&gt;
&lt;td&gt;Router마다 1씩 감소&lt;/td&gt;
&lt;td&gt;무한 Routing Loop 방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPv4 Header Checksum&lt;/td&gt;
&lt;td&gt;TTL이나 주소가 바뀌면 갱신&lt;/td&gt;
&lt;td&gt;변경된 Header의 오류 검출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP Source/Destination Port&lt;/td&gt;
&lt;td&gt;보통 연결 동안 유지&lt;/td&gt;
&lt;td&gt;Endpoint의 Socket과 Service 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP Port의 예외&lt;/td&gt;
&lt;td&gt;NAPT에서 변환될 수 있음&lt;/td&gt;
&lt;td&gt;하나의 공인 IP에서 연결 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP Sequence/Acknowledgment&lt;/td&gt;
&lt;td&gt;Data 교환에 따라 계속 진행&lt;/td&gt;
&lt;td&gt;Byte의 순서와 수신 범위 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS Application Payload&lt;/td&gt;
&lt;td&gt;중간 구간에서 암호문으로 보임&lt;/td&gt;
&lt;td&gt;내용의 기밀성과 무결성 보호&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시의 첫 Link에서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Ethernet  Client MAC &amp;rarr; Gateway MAC
IP        192.168.10.20 &amp;rarr; 198.51.100.20, TTL=64
TCP       51514 &amp;rarr; 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 바깥쪽 Link에서는 다음과 같이 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Ethernet  NAT WAN MAC &amp;rarr; ISP Next-Hop MAC
IP        203.0.113.5 &amp;rarr; 198.51.100.20, TTL=63
TCP       40001 &amp;rarr; 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 근처의 마지막 Link에서는 MAC 주소가 다시 달라지고 TTL은 더 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT가 추가로 없다면 IP와 TCP의 변환된 값은 유지된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Ethernet  Last Router MAC &amp;rarr; Server MAC
IP        203.0.113.5 &amp;rarr; 198.51.100.20, TTL=여러 Hop만큼 감소
TCP       40001 &amp;rarr; 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구분만 명확해도 Capture 지점에 따라 같은 연결이 왜 다른 모습으로 보이는지 설명할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 실제 용어로 되돌려 놓기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유는 출발점이지 기술 정의가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에는 각 장면을 실제 용어로 다시 말할 수 있어야 한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;실제 핵심 용어&lt;/td&gt;
&lt;td&gt;정확한 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이름을 숫자 주소로 찾는 주소록&lt;/td&gt;
&lt;td&gt;DNS,&lt;span&gt;&amp;nbsp;&lt;/span&gt;A,&lt;span&gt;&amp;nbsp;&lt;/span&gt;AAAA, Resolver, Cache TTL&lt;/td&gt;
&lt;td&gt;Domain Name을 IP 주소 정보로 해석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;같은 동네인지 판단&lt;/td&gt;
&lt;td&gt;Subnet Mask, Prefix, Routing Table&lt;/td&gt;
&lt;td&gt;Destination과 Route Prefix를 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;가까운 우체국에 맡김&lt;/td&gt;
&lt;td&gt;Default Gateway, Next Hop&lt;/td&gt;
&lt;td&gt;직접 연결되지 않은 목적지의 다음 L3 전달자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;같은 동네의 실제 집 찾기&lt;/td&gt;
&lt;td&gt;ARP, Neighbor Table, IPv6&lt;span&gt;&amp;nbsp;&lt;/span&gt;NDP&lt;/td&gt;
&lt;td&gt;Next-Hop IP와 Link-Layer 주소 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동네 안의 분류소&lt;/td&gt;
&lt;td&gt;Switch, MAC Address Table&lt;/td&gt;
&lt;td&gt;L2 Frame을 적절한 Port로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동네 경계 나누기&lt;/td&gt;
&lt;td&gt;VLAN, Broadcast Domain&lt;/td&gt;
&lt;td&gt;하나의 L2 기반을 논리적으로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순환 도로 일부를 닫기&lt;/td&gt;
&lt;td&gt;STP&lt;/td&gt;
&lt;td&gt;L2 Loop와 Frame 증폭 방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;지역 간 중계소&lt;/td&gt;
&lt;td&gt;Router, Routing Table, Longest Prefix Match&lt;/td&gt;
&lt;td&gt;Destination Prefix에 맞는 Next Hop 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최대 중계 횟수&lt;/td&gt;
&lt;td&gt;IP&lt;span&gt;&amp;nbsp;&lt;/span&gt;TTL, Hop Limit&lt;/td&gt;
&lt;td&gt;Routing Loop의 자원 낭비 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;좁은 길에 맞는 소포 크기&lt;/td&gt;
&lt;td&gt;MTU, MSS, PMTUD, ICMP&lt;/td&gt;
&lt;td&gt;경로가 전달 가능한 Packet 크기 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;한 공인 주소를 여러 집이 사용&lt;/td&gt;
&lt;td&gt;NAT, NAPT/PAT, Mapping&lt;/td&gt;
&lt;td&gt;IPv4 주소와 Port를 변환하고 연결 State 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;출입 정책을 확인하는 보안실&lt;/td&gt;
&lt;td&gt;Firewall, Stateful Inspection, Conntrack&lt;/td&gt;
&lt;td&gt;정해진 통신 Policy와 연결 상태 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;건물 안의 담당 창구&lt;/td&gt;
&lt;td&gt;Port, Socket, Demultiplexing&lt;/td&gt;
&lt;td&gt;한 Host의 올바른 Process와 연결 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접수와 수령 확인&lt;/td&gt;
&lt;td&gt;TCP SYN, ACK, Sequence Number&lt;/td&gt;
&lt;td&gt;연결 의사와 Byte 수신 범위 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;분실물 재발송&lt;/td&gt;
&lt;td&gt;Retransmission, RTO, Duplicate ACK&lt;/td&gt;
&lt;td&gt;손실로 추정한 Data를 다시 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수신 창고의 남은 공간&lt;/td&gt;
&lt;td&gt;Receive Window, Flow Control&lt;/td&gt;
&lt;td&gt;Receiver 처리 능력에 Sender 속도 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도로 전체의 혼잡&lt;/td&gt;
&lt;td&gt;Congestion Window, Congestion Control&lt;/td&gt;
&lt;td&gt;Network가 감당할 수 있는 전송량 추정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;봉인을 하고 수신인을 확인&lt;/td&gt;
&lt;td&gt;TLS, Certificate, Hostname Verification, Traffic Key&lt;/td&gt;
&lt;td&gt;상대 인증과 Data 기밀성&amp;middot;무결성 보호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;편지 안의 업무 요청&lt;/td&gt;
&lt;td&gt;HTTP Method, Host, Path, Status&lt;/td&gt;
&lt;td&gt;Application 요청과 처리 결과 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유로 설명한 뒤 이 표의 오른쪽 문장으로 돌아올 수 있다면, 비유가 개념을 가린 것이 아니라 이해를 도운 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 화면에 &lt;code&gt;timeout&lt;/code&gt; 하나만 보인다면 어디부터 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 오류 문구도 멈춘 단계는 다를 수 있다. 그래서 도구부터 고르는 대신 여정의 순서대로 질문한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;단계&lt;/td&gt;
&lt;td&gt;먼저 물을 질문&lt;/td&gt;
&lt;td&gt;남을 수 있는 증거와 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Link&lt;/td&gt;
&lt;td&gt;Interface가 올라와 있고 주소가 있는가?&lt;/td&gt;
&lt;td&gt;ip link,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ip addr, Interface Counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이름&lt;/td&gt;
&lt;td&gt;Host 이름은 어떤 주소와 RCODE를 받는가?&lt;/td&gt;
&lt;td&gt;dig, Resolver Log, DNS Cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route&lt;/td&gt;
&lt;td&gt;어떤 Source, Interface, Next Hop이 선택되는가?&lt;/td&gt;
&lt;td&gt;ip route get&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neighbor&lt;/td&gt;
&lt;td&gt;Next Hop의 MAC을 알아냈는가?&lt;/td&gt;
&lt;td&gt;ip neigh, ARP/NDP Capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L2 전달&lt;/td&gt;
&lt;td&gt;Frame이 올바른 VLAN과 Port로 가는가?&lt;/td&gt;
&lt;td&gt;Switch MAC Table, VLAN/STP 상태,&lt;span&gt;&amp;nbsp;&lt;/span&gt;tcpdump -e&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L3 경로&lt;/td&gt;
&lt;td&gt;Packet이 어느 구간까지 가는가?&lt;/td&gt;
&lt;td&gt;ping,&lt;span&gt;&amp;nbsp;&lt;/span&gt;traceroute, Router Route와 Counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MTU&lt;/td&gt;
&lt;td&gt;작은 Data는 되고 큰 Data만 멈추는가?&lt;/td&gt;
&lt;td&gt;tracepath, 크기를 바꾼&lt;span&gt;&amp;nbsp;&lt;/span&gt;ping, ICMP Capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT/Firewall&lt;/td&gt;
&lt;td&gt;변환 State와 허용 Rule에 일치하는가?&lt;/td&gt;
&lt;td&gt;NAT/Conntrack Table, Rule Counter와 Log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP Port&lt;/td&gt;
&lt;td&gt;SYN과 SYN-ACK가 오가는가?&lt;/td&gt;
&lt;td&gt;nc,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ss,&lt;span&gt;&amp;nbsp;&lt;/span&gt;tcpdump의 TCP Flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS&lt;/td&gt;
&lt;td&gt;Certificate와 Hostname 검증이 성공하는가?&lt;/td&gt;
&lt;td&gt;openssl s_client, TLS Alert, Server Log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP/Application&lt;/td&gt;
&lt;td&gt;어떤 Status와 처리 결과가 돌아오는가?&lt;/td&gt;
&lt;td&gt;curl -v, Access/Error Log, Trace와 Metric&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰 결과의 의미도 넘겨짚지 않아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ &lt;code&gt;dig&lt;/code&gt;가 성공했다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름을 주소로 바꿨다는 뜻이다. 해당 Web Port가 열려 있거나 Application이 정상이라는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ &lt;code&gt;ping&lt;/code&gt;이 실패했다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Echo Reply를 받지 못했다는 뜻이다. 정책상 ICMP만 차단했을 수 있으므로 HTTPS도 실패했다고 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ &lt;code&gt;traceroute&lt;/code&gt;에 &lt;code&gt;*&lt;/code&gt;가 보인다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 Probe의 응답을 받지 못했다는 뜻이다. 그 Router가 모든 Transit Traffic을 버렸다는 직접 증거는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ SYN은 나가는데 SYN-ACK가 없다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 응답을 받지 못했다는 뜻이다. SYN의 전방 손실, Server/Port 문제, SYN-ACK의 역방향 손실 가운데 어디인지 한 지점의 Capture만으로 구분할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ TCP Handshake가 성공했다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport 연결이 되었다는 뜻이다. TLS 검증이나 HTTP 업무 성공은 아직 별개의 단계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Server NIC에서 Request가 보인다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frame 또는 Packet이 Capture 지점까지 왔다는 뜻이다. Process가 &lt;code&gt;read()&lt;/code&gt;했고 업무 Transaction을 완료했다는 뜻은 아니므로 Socket과 Application Log를 함께 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진단은 &quot;무엇이 고장 났을까?&quot;라는 추측 대회가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마지막으로 확인된 정상 사건과 처음 사라진 실패 사건 사이&lt;/b&gt;로 범위를 줄이는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 관찰하기: URL 한 번의 여정 기록표 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 복잡한 Lab을 만들지 않아도 한 번의 접속을 단계별로 관찰할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신이 관리하거나 점검 허가를 받은 HTTPS Host를 하나 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령은 Linux 기준이며 운영체제에 따라 Option과 출력 형식이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 변수에 Host 이름을 넣는다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;HOST=your-authorized-host.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1. DNS Answer를 기록한다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;dig &quot;$HOST&quot; A
dig &quot;$HOST&quot; AAAA&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;status&lt;/code&gt;, &lt;code&gt;ANSWER SECTION&lt;/code&gt;, Record Type과 DNS TTL을 적는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 IP가 있다면 이번에 관찰할 IPv4 주소 하나를 선택한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;IP=$(dig +short A &quot;$HOST&quot; | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ { print; exit }')
printf 'selected_ip=%s\n' &quot;$IP&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 값이라면 다음 단계로 넘어가지 말고 DNS Answer와 입력한 Host부터 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2. Kernel의 Route 결정을 묻는다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get &quot;$IP&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 다음 값을 기록한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 Source IP&lt;/li&gt;
&lt;li&gt;Egress Interface&lt;/li&gt;
&lt;li&gt;&lt;code&gt;via&lt;/code&gt;가 있다면 Next Hop&lt;/li&gt;
&lt;li&gt;일치한 Route에 관한 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에 적힌 Route를 추측하지 말고 실제 Kernel이 내린 결정을 기준으로 삼는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 3. Neighbor State를 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼 Host라면 Neighbor는 Server IP가 아니라 Route가 선택한 Gateway다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;ip neigh show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gateway 항목의 MAC 주소와 &lt;code&gt;REACHABLE&lt;/code&gt;, &lt;code&gt;STALE&lt;/code&gt;, &lt;code&gt;DELAY&lt;/code&gt;, &lt;code&gt;INCOMPLETE&lt;/code&gt;, &lt;code&gt;FAILED&lt;/code&gt; 같은 State를 확인한다. Entry가 없으면 실제 Traffic을 한 번 발생시킨 뒤 다시 비교할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4. TCP와 TLS를 분리해 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Port 도달 여부를 먼저 살펴본다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;nc -vz -w 3 &quot;$HOST&quot; 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 TLS에서 SNI와 Hostname 검증을 명시한다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;openssl s_client \
  -connect &quot;$HOST:443&quot; \
  -servername &quot;$HOST&quot; \
  -verify_hostname &quot;$HOST&quot; \
  -brief &amp;lt;/dev/null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 성공과 Certificate 검증 성공을 한 칸에 뭉뚱그리지 않고 따로 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 5. HTTP 결과를 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;curl -v --connect-timeout 5 &quot;https://$HOST/&quot; -o /dev/null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Verbose 출력에는 DNS 후보, 연결한 IP와 Port, TLS 정보, HTTP Status 같은 단서가 나타날 수 있다. Version과 Build Option에 따라 표현은 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 6. 필요한 범위에서 Packet을 함께 본다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신에게 Capture 권한이 있는 Host와 Interface에서만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 정보와 개인정보가 포함될 수 있으므로 수집 범위를 최소화한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo tcpdump -ni any &quot;host $IP and tcp port 443&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 Terminal에서 &lt;code&gt;curl&lt;/code&gt;을 한 번 실행하고 Capture를 종료한다. 다음 사건이 실제로 보이는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;SYN &amp;rarr; SYN-ACK &amp;rarr; ACK
TLS Handshake Data
Application Data
FIN 또는 다른 종료 방식&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS가 적용되었다면 HTTP Path와 Body가 평문으로 보이지 않는 것이 정상이다. 또한 &lt;code&gt;any&lt;/code&gt; Capture와 NIC Offload 때문에 중복처럼 보이는 Packet, Wire와 다른 크기, 잘못된 것처럼 보이는 Checksum이 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 7. 자기 언어로 한 문단을 완성한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 빈칸을 실제 관찰값으로 채운다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;나는 ______라는 이름을 DNS Resolver ______에 물어 ______를 받았다.
Kernel은 Source ______, Interface ______, Next Hop ______를 선택했다.
Local Link에서는 Next Hop의 MAC ______를 사용했다.
TCP는 Local Port ______에서 Remote Port 443으로 Handshake를 만들었다.
TLS는 Hostname ______와 Certificate Chain을 검증했다.
HTTP는 Status ______를 돌려주었다.
내가 직접 확인하지 못한 중간 단계는 ______이며, 이를 단정하지 않는다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 문장이 특히 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client 한 대의 명령으로 ISP Router의 NAT Table이나 Server Process 내부 처리를 직접 보았다고 말할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰한 사실, 다른 증거로 추론한 내용, 아직 모르는 영역을 나누는 것이 Network를 정확히 설명하는 방법이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 네 가지 질문으로 다시 보면 기술은 따로 떨어져 있지 않다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 여정을 용어 목록으로 다시 흩뜨리지 않으려면 각 기술이 해결한 제약을 묻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 공유: 제한된 것을 어떻게 함께 쓸까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Switch는 하나의 L2 기반을 여러 장비가 함께 사용하게 한다.&lt;/li&gt;
&lt;li&gt;Port는 하나의 IP 주소에서 여러 Process와 연결을 구분한다.&lt;/li&gt;
&lt;li&gt;NAT/NAPT는 하나의 공인 IPv4 주소를 여러 내부 연결이 함께 쓰게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 확장: 모두가 모든 것을 알지 않고도 어떻게 커질까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VLAN과 Subnet은 가까운 전달 범위를 나눈다.&lt;/li&gt;
&lt;li&gt;Default Gateway와 Routing은 각 Node가 다음 길 중심으로 판단하게 한다.&lt;/li&gt;
&lt;li&gt;DNS는 분산된 권한과 Cache로 이름 체계를 확장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 효율: 한정된 길과 처리 능력을 어떻게 낭비하지 않을까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Switch의 학습은 불필요한 Flooding을 줄인다.&lt;/li&gt;
&lt;li&gt;TTL은 Loop Packet이 자원을 영원히 소비하지 않게 한다.&lt;/li&gt;
&lt;li&gt;MTU, MSS와 PMTUD는 경로에 맞는 크기를 찾는다.&lt;/li&gt;
&lt;li&gt;TCP Flow Control과 Congestion Control은 Receiver와 Network 상태에 맞춰 속도를 조절한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 신뢰: 손실과 믿을 수 없는 길에서도 어떻게 대화할까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP의 Sequence, ACK와 Retransmission은 순서 있는 Byte Stream을 만든다.&lt;/li&gt;
&lt;li&gt;Firewall은 허용한 통신의 경계를 세운다.&lt;/li&gt;
&lt;li&gt;TLS는 상대를 검증하고 내용의 기밀성과 무결성을 보호한다.&lt;/li&gt;
&lt;li&gt;계층별 관찰과 Log는 문제가 생겼을 때 추측을 증거로 바꾼다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 Network 기술을 만났을 때도 이름부터 외울 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 물으면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기술은 무엇을 함께 쓰게 하는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어디까지 확장하기 위해 생겼는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 낭비를 줄이는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구와 무엇을 신뢰하게 만드는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이제 혼자 설명할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음의 URL로 돌아가 보자.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;https://server.example/orders&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &quot;Client가 Server에 접속했다&quot;라는 한 문장을 더 정확하게 펼칠 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Browser는 URL에서 Scheme, Host, Port와 Path를 구분했다.&lt;/li&gt;
&lt;li&gt;DNS Cache와 Resolver를 통해 Host 이름의 A 또는 AAAA 주소를 얻었다.&lt;/li&gt;
&lt;li&gt;Kernel은 Routing Table에서 Longest Prefix Match로 Source, Interface와 Next Hop을 골랐다.&lt;/li&gt;
&lt;li&gt;같은 Link의 Next Hop MAC을 ARP 또는 NDP와 Neighbor Table로 알아냈다.&lt;/li&gt;
&lt;li&gt;Switch는 MAC Table과 VLAN 범위 안에서 Frame을 Gateway 방향으로 전달했다.&lt;/li&gt;
&lt;li&gt;Router들은 Destination Prefix에 맞는 다음 Hop을 선택하고 TTL을 줄이며 새 L2 Frame을 만들었다.&lt;/li&gt;
&lt;li&gt;출발지는 MTU와 PMTUD의 단서를 바탕으로 경로가 감당할 크기에 맞췄다.&lt;/li&gt;
&lt;li&gt;경계 NAT/NAPT는 사설 Source IP와 Port를 공인 IP와 Port로 Mapping했다.&lt;/li&gt;
&lt;li&gt;Firewall은 주소, Port, 방향과 연결 상태를 Policy에 따라 판단했다.&lt;/li&gt;
&lt;li&gt;TCP는 5-Tuple로 연결을 구분하고 Three-Way Handshake를 만들었다.&lt;/li&gt;
&lt;li&gt;Sequence, ACK, 재전송, Flow Control과 Congestion Control이 Byte Stream의 신뢰와 효율을 지켰다.&lt;/li&gt;
&lt;li&gt;TLS는 Certificate와 Hostname을 검증하고 이후 Data를 암호화해 보호했다.&lt;/li&gt;
&lt;li&gt;HTTP Server와 Application이 &lt;code&gt;/orders&lt;/code&gt;를 처리해 Status와 Body를 만들었다.&lt;/li&gt;
&lt;li&gt;응답은 독립적인 Return Route를 따라와 NAT State를 거쳐 원래 Socket과 Browser 요청에 전달되었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 문제가 생겼다면 이렇게 말할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;DNS Answer까지는 확인했다.
Route와 Neighbor도 정상이다.
Client의 SYN은 Router Ingress에서 보이지만 Egress에서는 보이지 않는다.
따라서 Server가 죽었다고 단정하지 않고, 두 Capture 지점 사이의 Forwarding,
Firewall Rule과 Counter를 확인하겠다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문장에는 모든 장비의 모든 정보를 안다는 허세가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 어디까지 확인했고, 어느 구간이 아직 설명되지 않았으며, 다음에 어떤 증거를 볼지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것이 이 연재에서 만들고 싶었던 Network 이해다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL 접속은 DNS, Route 선택, Neighbor 확인, L2 전달, Routing, NAT, TCP, TLS와 HTTP가 시간 순서로 이어진 결과다.&lt;/li&gt;
&lt;li&gt;DNS Query 자체도 Route와 ARP/NDP가 필요한 작은 Packet Journey이며, 단계들은 상황에 따라 Cache되거나 중첩된다.&lt;/li&gt;
&lt;li&gt;Destination이 다른 Subnet이면 Host는 최종 Server IP를 유지한 채 Ethernet Frame을 Default Gateway MAC으로 보낸다.&lt;/li&gt;
&lt;li&gt;Switch는 현재 L2 Frame의 MAC을 보고, Router는 IP Destination의 Longest Prefix Match로 다음 Hop을 고른다.&lt;/li&gt;
&lt;li&gt;Ethernet MAC 주소는 Link마다 바뀌고, IP 주소는 보통 End-to-End로 유지되지만 NAT에서 바뀔 수 있으며 TTL은 Router마다 감소한다.&lt;/li&gt;
&lt;li&gt;NAPT는 공인 IP와 Port의 Mapping State를 기억해 Reply를 원래 사설 Client 연결로 돌려보낸다.&lt;/li&gt;
&lt;li&gt;Firewall의 통신 허용, TLS의 상대&amp;middot;내용 보호, Application의 사용자 권한은 서로 다른 문제다.&lt;/li&gt;
&lt;li&gt;TCP 신뢰성은 Handshake 하나가 아니라 Sequence, ACK, Timer, 재전송, Flow Control과 Congestion Control의 조합이다.&lt;/li&gt;
&lt;li&gt;MTU와 PMTUD가 깨지면 작은 통신은 되는데 큰 Data만 멈출 수 있다.&lt;/li&gt;
&lt;li&gt;Request Route와 Return Route는 다를 수 있으며, Stateful 장비가 있다면 Reply가 필요한 State를 만날 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;TCP ACK, TLS 성공, HTTP Status와 업무 Transaction은 각각 다른 단계의 성공을 뜻한다.&lt;/li&gt;
&lt;li&gt;진단은 마지막으로 확인한 정상 사건과 처음 사라진 기대 사건 사이를 Route, Capture, Counter와 Log로 좁히는 일이다.&lt;/li&gt;
&lt;li&gt;하나의 관찰 지점에서 보이지 않았다는 사실을 Network 전체에 존재하지 않았다는 뜻으로 확대하지 않는다.&lt;/li&gt;
&lt;li&gt;새 기술은 공유, 확장, 효율, 신뢰 가운데 어떤 제약을 해결하려는지 물을 때 기존 기술과 연결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: URL, DNS, A, AAAA, Resolver, DNS Cache, Subnet, Routing Table, Longest Prefix Match, Default Gateway, Next Hop, ARP, NDP, Neighbor Table, Ethernet Frame, MAC Address Table, VLAN, STP, Router, TTL, MTU, MSS, PMTUD, ICMP, NAT, NAPT, PAT, 5-Tuple, Firewall, Stateful Inspection, Conntrack, TCP Three-Way Handshake, Sequence Number, Acknowledgment, Retransmission, Receive Window, Congestion Window, TLS, Certificate, Hostname Verification, HTTP, Asymmetric Routing, Socket, Demultiplexing, Evidence-Based Troubleshooting&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷은 편지를 한 번에 목적지로 보내는 거대한 장치가 아니다. &lt;br /&gt;제한된 길과 주소를 함께 쓰면서, &lt;br /&gt;각 지점이 자신이 맡은 작은 결정을 이어 붙여 결과적으로 한 통의 편지를 도착시키는 물류 시스템이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 연재를 마치며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 ARP, Switch, Router, TTL, NAT, DNS와 TCP가 서로 떨어진 용어처럼 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 하나가 빠질 때 다음 질문이 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS가 주소를 알려 주어도 누가 같은 Link의 Next Hop을 찾을까?&lt;/li&gt;
&lt;li&gt;ARP가 Gateway MAC을 알려 주어도 누가 먼 지역의 다음 길을 고를까?&lt;/li&gt;
&lt;li&gt;Route가 있어도 주소가 부족하면 여러 사용자가 어떻게 Public Internet을 함께 쓸까?&lt;/li&gt;
&lt;li&gt;Packet이 도착해도 손실과 순서 뒤바뀜 속에서 어떻게 대화를 이어 갈까?&lt;/li&gt;
&lt;li&gt;TCP가 Byte를 전달해도 믿을 수 없는 중간 길에서 상대와 내용을 어떻게 지킬까?&lt;/li&gt;
&lt;li&gt;모든 계층을 거쳤어도 업무가 실패하면 어느 증거부터 거꾸로 따라갈까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술은 앞선 기술이 틀려서 생긴 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많은 사람이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 길을 &lt;b&gt;공유&lt;/b&gt;하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 더 먼 곳까지 &lt;b&gt;확장&lt;/b&gt;하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 한정된 자원을 &lt;b&gt;효율&lt;/b&gt;적으로 쓰며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 불완전한 길 위에서도 &lt;b&gt;신뢰&lt;/b&gt;를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들기 위해 필요한 선택이 하나씩 더해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Packet을 볼 때 Header의 숫자만 보지 않았으면 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 숫자가 어떤 문제를 해결하기 위해 그 자리에 놓였는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그 다음 길을 선택했는지, 어디에서 처음 기대한 사건이 사라졌는지를 이야기할 수 있다면 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 인터넷의 용어를 모두 외운 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;편지 한 통이 목적지까지 가는 이유를 이해했다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1104</guid>
      <comments>https://peterica.tistory.com/1104#entry1104comment</comments>
      <pubDate>Wed, 29 Jul 2026 22:27:43 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 26. 패킷을 직접 보면 모든 원인을 알 수 있을까?</title>
      <link>https://peterica.tistory.com/1103</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dooC9x/dJMcafm9mgX/NeGh8Nfl3aKQCMAP5fT2c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dooC9x/dJMcafm9mgX/NeGh8Nfl3aKQCMAP5fT2c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dooC9x/dJMcafm9mgX/NeGh8Nfl3aKQCMAP5fT2c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdooC9x%2FdJMcafm9mgX%2FNeGh8Nfl3aKQCMAP5fT2c0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 8. 편지가 멈춘 곳을 찾는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애가 생기면 Wireshark를 열고 Packet부터 보고 싶어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet은 추측보다 강한 증거다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;SYN을 실제로 보냈다.
SYN-ACK이 실제로 돌아왔다.
DNS가 NXDOMAIN을 응답했다.
Router가 ICMP Unreachable을 보냈다.
같은 TCP Sequence가 재전송됐다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Capture 화면은 Network 전체를 내려다보는 CCTV가 아니다.&lt;/p&gt;
&lt;pre class=&quot;monkey&quot;&gt;&lt;code&gt;어느 Host의
어느 Interface에서
어느 방향을
어느 시간 동안
어떤 Filter와 크기로
수집했는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건 안에서 보인 Packet만 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Capture에 SYN이 없다고 Server가 받지 않았음을 증명하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 Interface를 봤거나 Capture가 늦게 시작됐거나 BPF Filter가 제외했을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN이 보인다고 NIC를 떠나 Wire에 실렸다는 뜻도 아닐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 위치가 NIC Offload 이전일 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture는 답을 자동으로 주는 도구가 아니라 &lt;b&gt;가설을 관찰 가능한 사건으로 바꾸는 도구&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;traceroute&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;openssl&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, Wireshark와 Log를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층별 질문에 연결하고 &lt;b&gt;Multi-point Capture&lt;/b&gt;로 원인 범위를 줄이는 방법을 정리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 도구를 열기 전에 어떤 질문을 써야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Network가 안 된다&amp;rdquo;는 질문은 너무 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 관찰 가능한 질문으로 나눈다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Hostname이 어느 Address로 해석되는가?
Kernel은 어느 Route와 Source Address를 선택하는가?
Next-Hop MAC Address를 알고 있는가?
TCP SYN이 Host를 떠나는가?
SYN-ACK 또는 RST가 돌아오는가?
TLS ClientHello 뒤에 ServerHello가 오는가?
HTTP Request가 Server Process까지 도착하는가?
Server가 Reply를 만들고 어느 Route로 보내는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문이 달라지면 필요한 증거와 Capture 지점도 달라진다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;DNS 질문에 TCP Retransmission Filter만 봄
&amp;rarr; 필요한 증거를 놓침

Application 500 오류에 ARP만 봄
&amp;rarr; 계층이 맞지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구보다 질문을 먼저 정하면 수많은 Packet에서 무엇을 찾아야 하는지 분명해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture는 어떤 문장으로 읽어야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 두 문장은 범위가 다르다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Packet이 없다.&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10:30:00부터 10:30:15까지 Client의 eth0 Ingress와 Egress를
Snaplen 0, TCP 443 Filter로 Capture했지만
198.51.100.20을 향한 SYN을 관찰하지 못했다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 문장은 다음 확인점을 남긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 범위가 맞는가?&lt;/li&gt;
&lt;li&gt;실제 Traffic이 eth0을 사용했는가?&lt;/li&gt;
&lt;li&gt;IPv4와 IPv6 Address가 맞는가?&lt;/li&gt;
&lt;li&gt;Port가 정말 443인가?&lt;/li&gt;
&lt;li&gt;Capture Process가 Packet을 Drop하지 않았는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture의 부재는 &lt;b&gt;정의한 관찰 창 안에서 보이지 않았다&lt;/b&gt;는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰 조건을 기록하지 않은 &amp;ldquo;없음&amp;rdquo;은 약한 증거다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 가장 먼저 Packet을 잡아야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture 전에 Local State를 확인하면 더 빠르게 범위를 줄일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Interface가 Down
Route 없음
잘못된 DNS Answer
Service가 Listen하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 상태는 Packet 수천 개를 읽지 않고도 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 순서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. 증상과 시간, Source&amp;middot;Destination 정의
2. Local Interface와 Address 확인
3. Route와 Neighbor 확인
4. DNS Answer 확인
5. 실제 Transport와 Application Probe
6. 필요한 지점에서 Packet Capture
7. Firewall Counter, Conntrack와 Application Log 대조&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서는 고정된 의식이 아니다. 이미 장애 시간의 PCAP만 남았다면 Capture부터 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 한 도구의 결과를 전체 원인처럼 확대하지 않는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;ip link&lt;/code&gt;는 무엇을 묻는가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 Interface 상태와 Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ip -s link&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 항목은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;monkey&quot;&gt;&lt;code&gt;Interface UP / DOWN
Carrier 상태
MTU
RX / TX Packet과 Byte
Error와 Drop Counter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface가 &lt;code&gt;UP&lt;/code&gt;이라고 Cable, Switch Port, VLAN, Routing과 Remote Host가 모두 정상이라는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 RX Drop Counter가 증가해도 곧바로 Network 손실 원인을 확정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Driver Queue, Buffer, Offload와 Counter 정의를 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip link 증거
&amp;rarr; Local Interface 상태와 Counter

증명하지 않는 것
&amp;rarr; End-to-End Service 정상&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;ip addr&lt;/code&gt;는 무엇을 확인할까?&lt;/h4&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;ip addr show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 질문에 답한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기대한 IPv4 또는 IPv6 Address가 있는가?&lt;/li&gt;
&lt;li&gt;Prefix Length가 맞는가?&lt;/li&gt;
&lt;li&gt;어느 Interface에 연결되어 있는가?&lt;/li&gt;
&lt;li&gt;Address가 Tentative, Deprecated 같은 상태인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address가 있다고 해당 Source로 Packet이 나간다고 보장할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route Selection이 다른 Interface와 Source를 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Container, Pod, VRF와 Network Namespace마다 별도 Address와 Interface가 있으므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Application의 Network Context에서 실행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;ip route get&lt;/code&gt;은 왜 유용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Table 전체를 보는 것보다 특정 Destination에 대한 Kernel의 결정을 직접 묻는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에 따라 다음 정보가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Selected Route
Next Hop
Outgoing Interface
Preferred Source Address
Route Table과 Metric 관련 정보&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source를 지정해 Policy Routing 차이를 볼 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 198.51.100.20 from 10.0.0.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ip route get&lt;/code&gt; 결과는 현재 Kernel Route Lookup의 증거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet이 이후 Firewall, Tunnel과 Neighbor Resolution을 모두 통과했다는 증거는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Policy Routing을 사용하면 다음도 함께 본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip rule show
ip route show table all&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;ip neigh&lt;/code&gt;는 어느 단계의 장부일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Link의 Destination이나 Default Gateway로 Frame을 보내려면 Next-Hop IP에 대응하는 MAC Address가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;ip neigh show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neighbor Entry는 다음 상태를 가질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;REACHABLE
STALE
DELAY
PROBE
INCOMPLETE
FAILED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;INCOMPLETE&lt;/code&gt;나 &lt;code&gt;FAILED&lt;/code&gt;가 반복되면 ARP Request 또는 IPv6 Neighbor Solicitation에 응답이 없는 이유를 조사한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;VLAN 불일치
Switch Port 문제
잘못된 Subnet
Gateway Down
L2 Security Policy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STALE은 곧 장애라는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요할 때 다시 Reachability를 확인할 수 있는 정상 상태다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ macOS와 다른 운영체제에서는 무엇을 사용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령 이름은 달라도 질문은 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Interface와 Address
&amp;rarr; ifconfig, ipconfig 등

Route
&amp;rarr; route, netstat, Get-NetRoute 등

Neighbor
&amp;rarr; arp, ndp, Get-NetNeighbor 등&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 Field와 State 의미는 운영체제마다 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 명령을 외우는 것보다 &amp;ldquo;현재 Source에서 어느 Interface와 Next Hop을 선택했는가?&amp;rdquo;라는 질문을 유지한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;dig&lt;/code&gt;는 무엇을 분리해 줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hostname 장애는 Network 연결 전에 시작될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;dig server.example A
dig server.example AAAA&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Resolver에 직접 물어 비교한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dig @192.0.2.53 server.example A +time=2 +tries=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답을 다음처럼 구분한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NOERROR + Answer
&amp;rarr; 해당 Resolver가 Address Answer를 제공

NXDOMAIN
&amp;rarr; 이름이 존재하지 않는다는 DNS 응답

SERVFAIL
&amp;rarr; Resolver가 질의를 완료하지 못함

Timeout
&amp;rarr; 정해진 시간 안에 DNS 응답을 관찰하지 못함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;dig&lt;/code&gt;가 받은 Answer는 Cache에서 왔을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authoritative Server의 현재 Zone을 직접 읽었다고 자동으로 가정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNSSEC 검증, Search Suffix와 Application 내부 Resolver처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Application과 &lt;code&gt;dig&lt;/code&gt;의 조회 경로가 다를 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DNS Packet은 어디에서 확인할까?&lt;/h4&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;sudo tcpdump -nn -i eth0 -s 0 \
  'port 53'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 흐름을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Query가 Host를 떠나는가?
어느 Resolver로 보내는가?
UDP인가 TCP인가?
Response가 돌아오는가?
Transaction ID와 질문이 맞는가?
Response Code와 Answer는 무엇인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DoH와 DoT를 사용하는 Application은 일반 UDP/TCP 53 Filter에 DNS 이름이 보이지 않을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;port 53에 Packet 없음
&amp;ne; Application이 DNS를 전혀 사용하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Resolver 경로와 암호화 방식을 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;nc&lt;/code&gt;는 어떤 범위까지 시험할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Port 연결 가능성을 빠르게 시험할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -v -w 3 198.51.100.20 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공하면 해당 Source에서 TCP Handshake가 완료되었다는 단서를 얻는다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;nc TCP 연결 성공
&amp;ne; TLS 성공
&amp;ne; HTTP 정상
&amp;ne; 업무 처리 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패 모습도 나눈다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Timeout
&amp;rarr; SYN 또는 Reply Drop, 경로, Policy 등 여러 가능성

Connection Refused
&amp;rarr; RST를 받음, Listener 부재 또는 중간 장비 Reject 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nc&lt;/code&gt; Option은 구현마다 다르며 UDP Mode는 TCP처럼 Handshake 성공을 확인하지 못한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;curl&lt;/code&gt;은 어느 계층까지 올라갈까?&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;curl -v --connect-timeout 3 https://server.example/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Verbose 출력에서 다음 단계를 나눌 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;DNS Address 선택
TCP Connection
TLS Handshake와 Certificate 검증
HTTP Request
HTTP Status와 Header
Response Body&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;curl&lt;/code&gt; 성공은 실행한 URL과 Method, 해당 시점의 Response에 대한 증거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 API, 사용자 권한, Backend와 전체 Service가 정상이라는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-k&lt;/code&gt;로 Certificate 검증을 끄면 TLS Identity 문제를 숨길 수 있으므로 진단의 기본 해결책으로 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;openssl s_client&lt;/code&gt;는 무엇을 보여줄까?&lt;/h4&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;openssl s_client \
  -connect server.example:443 \
  -servername server.example \
  -verify_hostname server.example \
  -verify_return_error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 정보를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TLS Version과 Cipher
Server Certificate Chain
Hostname과 Trust 검증 결과
ALPN 협상
TLS Alert&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-servername&lt;/code&gt;을 빠뜨리면 Virtual Host가 다른 Certificate를 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 Timeout과 Certificate Verification Error를 같은 &amp;ldquo;TLS 실패&amp;rdquo;로 묶지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ tcpdump를 시작할 때 무엇을 정해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명용 Capture 명령은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;sudo tcpdump \
  -nn \
  -i eth0 \
  -s 0 \
  -B 4096 \
  -w incident-20260715.pcap \
  'host 198.51.100.20 and tcp port 443'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Option의 목적을 나누어 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-nn
&amp;rarr; Hostname과 Service Name 변환을 하지 않고 숫자로 표시

-i eth0
&amp;rarr; Capture할 Interface 선택

-s 0
&amp;rarr; 구현이 허용하는 전체 Packet을 자르지 않고 저장

-B 4096
&amp;rarr; Capture Buffer 크기 지정

-w file.pcap
&amp;rarr; 화면 해석 대신 원본 Packet을 파일로 저장

BPF 표현식
&amp;rarr; 수집할 Traffic 범위를 Capture 단계에서 제한&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Option 의미와 단위는 &lt;code&gt;tcpdump&lt;/code&gt; Version과 운영체제에 따라 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 파일 이름에는 시간, Host와 Interface 같은 Context를 넣으면 여러 지점 자료를 맞추기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Capture Filter와 Display Filter는 같은 문법일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Capture Filter&lt;/b&gt;는 Packet을 저장하기 전에 어떤 Traffic을 수집할지 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcpdump&lt;/code&gt;와 Wireshark Capture 단계에서 BPF 문법을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;host 198.51.100.20 and tcp port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Display Filter&lt;/b&gt;는 이미 Capture된 Packet 중 화면에 무엇을 보여 줄지 정한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ip.addr == 198.51.100.20 &amp;amp;&amp;amp; tcp.port == 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 중요하다.&lt;/p&gt;
&lt;pre class=&quot;scss&quot;&gt;&lt;code&gt;Capture Filter에서 제외
&amp;rarr; PCAP에 저장되지 않음, 나중에 복구 불가

Display Filter에서 숨김
&amp;rarr; PCAP에는 남아 있어 Filter를 바꾸면 다시 볼 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 아직 모를 때 Capture Filter를 지나치게 좁히면 ARP, DNS, ICMP 오류와 다른 Address Family를 놓칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Storage와 Privacy를 고려하면서 가설에 필요한 범위를 잡는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ BPF Filter는 어떻게 조합할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 Capture Filter는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;특정 Host
host 198.51.100.20

특정 방향
src host 10.0.0.10 and dst host 198.51.100.20

특정 Network
net 10.0.0.0/8

TCP Service
tcp port 443

DNS와 ICMP
port 53 or icmp or icmp6

복합 조건
host 198.51.100.20 and (tcp port 443 or icmp)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt;의 우선순위를 오해하지 않도록 괄호를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Filter를 본 Capture 전에 작은 Sample로 검증한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;sudo tcpdump -nn -i eth0 -c 10 \
  'host 198.51.100.20 and (tcp port 443 or icmp)'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Filter가 실제 장애 Traffic의 IPv4&amp;middot;IPv6, NAT 전후 Tuple과 Port를 포함하는지 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Snaplen이 작으면 무엇을 잃을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Snapshot Length&lt;/b&gt;(Snaplen)는 Packet마다 저장할 최대 Byte 수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snaplen이 Header보다 충분히 크지만 Payload보다 작으면 주소와 Port는 볼 수 있어도 Application Data 뒤는 잘린다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Wire Packet 1500바이트
Snaplen 96바이트
&amp;rarr; 앞 96바이트만 PCAP에 저장&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark는 &lt;code&gt;Packet size limited during capture&lt;/code&gt; 같은 표시를 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘린 Byte는 Display Filter나 재분석으로 되살릴 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 Payload가 필요한지, Header만으로 충분한지와 민감 정보 수집 위험을 함께 고려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-s 0&lt;/code&gt;은 분석에는 편리하지만 Password, Cookie와 개인정보까지 저장할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Capture Process도 Packet을 놓칠 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traffic 속도가 Disk, CPU와 Buffer 처리 능력보다 빠르면 Capture Tool이 Packet을 Drop할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcpdump&lt;/code&gt; 종료 통계에는 환경에 따라 다음 Counter가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;packets captured
packets received by filter
packets dropped by kernel&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;dropped by kernel&lt;/code&gt;이 증가했다면 PCAP의 Packet 부재를 Network Drop으로 해석하기 전에 Capture 누락을 고려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완화 방법은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BPF로 필요한 Traffic 범위 제한&lt;/li&gt;
&lt;li&gt;Capture Buffer 조정&lt;/li&gt;
&lt;li&gt;빠른 Storage 사용&lt;/li&gt;
&lt;li&gt;화면 출력 대신 &lt;code&gt;-w&lt;/code&gt;로 저장&lt;/li&gt;
&lt;li&gt;여러 CPU Queue와 전용 Capture 장비 사용&lt;/li&gt;
&lt;li&gt;Ring Buffer로 파일 Rotation&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Drop Counter 자체도 플랫폼별 정의를 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 긴 장애를 파일 하나에 계속 저장해도 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래 기다려야 재현되는 장애는 PCAP가 매우 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcpdump&lt;/code&gt;는 크기나 시간 기준으로 파일을 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명용 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;sudo tcpdump -nn -i eth0 -s 0 \
  -C 100 -W 10 \
  -w incident.pcap \
  'host 198.51.100.20'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 tcpdump에서 &lt;code&gt;-C 100&lt;/code&gt;은 파일이 약 100 million bytes에 도달하기 전에 다음 파일로 전환하고, &lt;code&gt;-W 10&lt;/code&gt;은 파일 수를 제한하는 데 사용된다. 정확한 단위와 이름 규칙은 해당 Version의 Manual을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ring Buffer는 Disk 고갈을 막지만 오래된 증거를 덮어쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alarm 시각과 보존 범위를 맞추고 필요한 PCAP를 즉시 별도 보관한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Promiscuous Mode면 Switch의 모든 Packet이 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promiscuous Mode는 NIC가 자신에게 전달된 Frame 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination MAC이 자신의 것이 아닌 Frame도 Host에 넘기도록 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Switch가 해당 Port로 Frame을 보내지 않았다면 NIC가 받을 수 없다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;다른 두 Host의 Unicast Traffic
&amp;rarr; Switch가 그 둘의 Port로만 Forward
&amp;rarr; 내 Capture Port에는 도착하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 Link의 Traffic을 관찰하려면 다음 같은 구성이 필요할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Switch SPAN 또는 Port Mirroring&lt;/li&gt;
&lt;li&gt;Network TAP&lt;/li&gt;
&lt;li&gt;Router&amp;middot;Firewall 자체 Capture&lt;/li&gt;
&lt;li&gt;Virtual Switch Mirror&lt;/li&gt;
&lt;li&gt;Endpoint Capture&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPAN Port도 과부하되면 Packet을 놓치거나 Timing 특성이 바뀔 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TAP과 Mirror의 방향, VLAN Tag 보존과 Capacity를 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;any&lt;/code&gt; Interface에서 같은 Packet이 두 번 보일 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 &lt;code&gt;tcpdump -i any&lt;/code&gt;는 여러 Interface를 함께 관찰한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 Packet을 한 Interface로 받고 다른 Interface로 내보내면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 논리적 Packet이 Ingress와 Egress에서 각각 Capture될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;eth0 Ingress  &amp;rarr; Packet 한 번 관찰
eth1 Egress   &amp;rarr; 같은 Packet 다시 관찰&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bridge, veth, Container와 Host Capture를 겹치면 더 많은 복사본처럼 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 실제 Network Duplicate로 단정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Interface ID, Direction, TTL 변화, MAC Header와 Timestamp를 비교한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Checksum이 Bad라고 정말 Wire에서 손상됐을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TX Checksum Offload가 활성화된 Host에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kernel Capture 시점에 TCP&amp;middot;UDP Checksum 계산이 아직 NIC에 맡겨진 상태일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;Kernel이 Packet을 Capture
&amp;rarr; Checksum이 아직 완성되지 않음
&amp;rarr; NIC가 Wire 전송 전에 계산&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Capture에서는 &lt;code&gt;Bad Checksum&lt;/code&gt;처럼 보여도 실제 Wire Packet은 정상일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 RX Offload와 Capture 위치도 해석에 영향을 준다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Sender Host에서만 Bad Checksum
수신 Host에서는 정상 수신
&amp;rarr; TX Offload 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 지점 Capture와 NIC Offload 설정을 함께 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Checksum 경고 한 줄만으로 Link 손상을 확정하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MTU보다 큰 TCP Packet이 보이면 Jumbo Frame일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TSO와 GSO는 Host 내부에서 큰 TCP Data 단위를 NIC나 하위 계층에 넘긴 뒤 실제 Wire 크기로 나눌 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GRO와 LRO는 받은 여러 Packet을 Host 내부에서 합쳐 큰 단위로 상위 계층에 전달할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TSO / GSO
&amp;rarr; 분할 전 큰 Packet처럼 Capture 가능

GRO / LRO
&amp;rarr; 병합 후 큰 Packet처럼 Capture 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host PCAP에서 32KB TCP Packet이 보였다고 Wire에 32KB Ethernet Frame 하나가 존재했다고 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 지점과 Offload 전후를 구분하고 필요하면 SPAN/TAP에서 Wire Traffic을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ VLAN Tag가 안 보이면 Untagged였을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIC가 VLAN Tag를 Hardware에서 제거한 뒤 Kernel에 Metadata로 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Capture 위치에 따라 Packet Byte에는 802.1Q Header가 보이지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Trunk SPAN에서는 VLAN Tag가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Host Capture에서 Tag 없음
&amp;ne; Wire에서 반드시 Untagged&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIC Offload, Driver와 Mirror 설정을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 문제는 Switch Port의 Access/Trunk Policy와 양쪽 Capture를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Wireshark의 분석 표시는 Packet Header일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark는 Packet Header뿐 아니라 Capture 순서와 시간 관계를 분석해 편리한 표시를 만든다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;tcp.analysis.retransmission
tcp.analysis.fast_retransmission
tcp.analysis.duplicate_ack
tcp.analysis.out_of_order
tcp.analysis.zero_window&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값은 Wire에 들어 있는 독립적인 TCP Flag가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture가 연결 중간부터 시작되거나 한 방향 Packet이 빠졌거나 Capture Drop이 있으면 분석 결과도 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Wireshark가 Retransmission으로 표시
&amp;rarr; Capture 관점에서 같은 Sequence 범위를 다시 관찰
&amp;rarr; 왜 원본이 확인되지 않았는지는 별도 조사&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편리한 Expert Info를 결론이 아니라 가설 생성 도구로 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Display Filter로 대화를 어떻게 좁힐까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 저장한 PCAP에서 다음처럼 범위를 줄일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;특정 Address
ip.addr == 198.51.100.20

특정 TCP Port
tcp.port == 443

특정 TCP Stream
tcp.stream eq 3

SYN
tcp.flags.syn == 1

RST
tcp.flags.reset == 1

DNS
dns

ICMP 오류
icmp || icmpv6

재전송 분석
tcp.analysis.retransmission&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcp.stream&lt;/code&gt; 번호는 해당 Capture 파일에서 Wireshark가 부여한 분석 번호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 PCAP의 같은 실제 Flow가 같은 번호를 갖는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 양쪽에서는 5-Tuple이 달라지므로 Sequence Number, 시간과 변환 Table을 이용해 연결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP SYN만 반복되면 어디가 문제일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Capture에서 다음 흐름이 보인다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Client &amp;rarr; Server : SYN Retransmission
Client &amp;rarr; Server : SYN Retransmission&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Capture 하나로 알 수 있는 것은 다음이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client TCP가 SYN을 만들었다.
Client Capture 지점에서 SYN을 관찰했다.
Client가 SYN-ACK 또는 유효한 응답을 받지 못해 재전송했다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 아직 모른다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;원래 SYN이 Wire를 떠났는가?
Forward Path 어디에서 Drop됐는가?
Server가 SYN을 받았는가?
Server가 SYN-ACK을 보냈는가?
SYN-ACK이 Return Path에서 Drop됐는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client PCAP의 SYN Retransmission은 손실의 존재를 암시하지만 Drop 위치를 알려 주지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RST가 보이면 원인을 확정할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN 뒤에 RST가 돌아오면 Silent Timeout보다 정보가 많다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Server &amp;rarr; Client : RST, ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination Host의 닫힌 Port가 RST를 보냈을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Firewall, Load Balancer와 Proxy가 Reject Policy로 RST를 생성할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RST의 Source Address, TTL, Sequence와 Capture 지점을 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Capture에 SYN이 없는데 Client가 RST를 받았다면 중간 장비가 생성했을 가능성을 조사한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;RST 수신
&amp;rarr; 누군가 TCP 연결을 명시적으로 거절
&amp;rarr; 누가 생성했는지는 추가 증거 필요&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 3-Way Handshake 뒤 Data가 멈추면 무엇을 볼까?&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SYN
SYN-ACK
ACK
Client Data
Client Data Retransmission&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결은 성립했지만 Data나 ACK가 진행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 항목을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Data Packet 크기와 DF, ICMP Too Big 여부&lt;/li&gt;
&lt;li&gt;Server Capture에 Data가 도착하는가?&lt;/li&gt;
&lt;li&gt;Server가 ACK를 보내는가?&lt;/li&gt;
&lt;li&gt;ACK가 Client에 도착하는가?&lt;/li&gt;
&lt;li&gt;Advertised Window가 0인가?&lt;/li&gt;
&lt;li&gt;TLS ClientHello 뒤 응답이 있는가?&lt;/li&gt;
&lt;li&gt;Application Process가 Socket을 읽는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake 성공만으로 큰 Data, TLS와 Application까지 정상이라고 판단하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS Capture에서 평문이 없으면 분석할 수 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3 PCAP을 Session Key 없이 보면 ClientHello와 ServerHello 이후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 Handshake와 Application Payload가 암호화되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 읽지 못해도 다음은 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TCP Handshake 성공 여부
ClientHello 전송 여부
ServerHello 또는 Alert 존재 여부
Record 크기와 방향
Retransmission과 Timeout
Connection 종료 시점&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 HTTP URL, Header와 Body는 보이지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Key Log로 Lab Traffic을 복호화할 수 있는 환경도 있지만 Key Log는 보호된 평문을 드러낼 수 있는 민감 정보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Production에서 임의로 수집하거나 공유하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화된 Payload 문제는 Endpoint의 TLS Log와 Application Log가 더 강한 증거가 될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Server Capture에 Request가 보이면 Application도 받았을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server NIC 근처 Capture에 TCP Data가 보였다고 Application의 &lt;code&gt;read()&lt;/code&gt;가 성공했다는 뜻은 아니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NIC / Driver
&amp;rarr; IP Stack
&amp;rarr; Host Firewall
&amp;rarr; TCP Reassembly
&amp;rarr; Socket Receive Buffer
&amp;rarr; Application read()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 단계에서 Checksum 검증, Firewall, Socket 상태와 Buffer 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP ACK가 돌아왔다면 Server TCP Stack이 Byte를 수신했다는 강한 단서지만 Application 업무 처리 완료는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application Access Log, Error Log와 Trace ID를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Application Log만 보면 Network는 알 수 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application Log도 중요한 관찰 지점이다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Packet Capture
&amp;rarr; Network와 Transport에서 무엇이 오갔는가?

Application Log
&amp;rarr; Process가 어떤 요청을 해석하고 어떤 결과를 만들었는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Access Log에 Request가 없다면 다음 가능성이 남는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Request가 Network에서 도착하지 않음&lt;/li&gt;
&lt;li&gt;TLS Proxy나 Load Balancer에서 종료됨&lt;/li&gt;
&lt;li&gt;Host Firewall 또는 Kernel에서 Drop&lt;/li&gt;
&lt;li&gt;Application Log 조건에서 제외&lt;/li&gt;
&lt;li&gt;Log Buffer와 수집 Pipeline 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Log 부재도 Capture 부재처럼 관찰 조건을 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet, Conntrack, Proxy Log와 Application Trace를 동일한 시간축과 Request ID로 연결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 Capture의 Clock이 다르면 무엇이 꼬일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client와 Server PCAP를 비교할 때 Timestamp가 정확해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client Clock가 실제보다 500ms 빠름
Server Clock가 실제보다 300ms 느림

화면상 차이 800ms
&amp;rarr; Network Delay로 오해 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NTP나 PTP로 Clock을 동기화하고 Capture Host의 Timestamp Source와 정밀도를 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기화가 완벽하지 않다면 TCP Sequence, IP ID, Payload Length와 사건 순서로 상대적인 Offset을 추정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 One-Way Delay를 주장하려면 Capture Timestamp 정확도와 Clock Error 범위를 함께 제시한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Multi-point Capture는 무엇을 추가할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Flow를 여러 지점에서 동시에 관찰한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client
  │ Capture A
  ▼
Firewall Ingress
  │ Capture B
Firewall Egress
  │ Capture C
  ▼
Server
  │ Capture D&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN의 관찰 결과를 비교하면 Drop 범위를 좁힐 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client Egress&lt;/td&gt;
&lt;td&gt;Firewall Ingress&lt;/td&gt;
&lt;td&gt;Firewall Egress&lt;/td&gt;
&lt;td&gt;Server Ingress&lt;/td&gt;
&lt;td&gt;해석 가능한 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;Client Capture 이후와 Firewall Ingress 사이 또는 Capture 누락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;Firewall 내부 Policy&amp;middot;Routing&amp;middot;Processing 범위 우선 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;안 보임&lt;/td&gt;
&lt;td&gt;Firewall Egress 이후와 Server Ingress 사이 또는 Capture 누락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;보임&lt;/td&gt;
&lt;td&gt;Forward SYN 전달 확인, Server Host와 Reply 생성&amp;middot;Return Path 조사&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표의 어느 행도 단일 원인을 자동 확정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall Ingress에는 보이고 Egress에는 없더라도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Drop, Local Routing, Inspection, Queue Drop와 Capture 설정이 후보로 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter, Drop Log와 장비 내부 Trace로 한 단계 더 좁힌다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Forward Path만 Capture하면 절반만 보는 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request가 Server에 도착하고 Reply가 돌아오지 않는 장애가 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Forward Path Capture: Request 정상
Return Path Capture : Reply가 중간에서 Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward 방향만 보면 Server나 Application을 의심하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양방향 Filter와 Return Route를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Original Tuple
Client:53000 &amp;rarr; Server:443

Reply Tuple
Server:443 &amp;rarr; Client:53000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT가 있으면 Capture 지점마다 Tuple이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall과 Load Balancer가 있다면 Return Packet이 같은 State Owner를 지나는지도 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Capture에서 Packet이 사라진 경계를 찾으면 원인이 끝날까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client와 Firewall Ingress에는 SYN이 있고 Firewall Egress에는 없다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인 범위는 Firewall 내부로 크게 줄었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 정확한 이유는 여전히 여러 가지다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Firewall Policy Drop
No Route 또는 Wrong VRF
NAT Resource Exhaustion
Inspection Engine Drop
State Table Full
Egress Queue Drop
Capture Hook 위치 차이&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 증거를 이어 본다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rule Hit Counter&lt;/li&gt;
&lt;li&gt;Drop Log와 Reason Code&lt;/li&gt;
&lt;li&gt;Routing/VRF Lookup&lt;/li&gt;
&lt;li&gt;NAT와 Conntrack Entry&lt;/li&gt;
&lt;li&gt;Interface Error/Drop Counter&lt;/li&gt;
&lt;li&gt;장비 CPU와 Session Capacity&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-point Capture는 원인 &lt;b&gt;위치 범위&lt;/b&gt;를 줄이고 장비 State가 원인 &lt;b&gt;이유&lt;/b&gt;를 설명한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SPAN Capture에서 Packet이 없으면 Source가 안 보냈을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPAN 구성도 검증해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;올바른 Source Port 또는 VLAN을 Mirror했는가?
Ingress와 Egress 방향이 모두 포함됐는가?
SPAN Destination Port가 과부하되지 않았는가?
Encapsulation과 VLAN Tag를 보존했는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mirror Traffic 합계가 Capture Port 용량을 넘으면 SPAN에서 Packet이 Drop될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPAN PCAP의 부재를 실제 Data Plane 부재로 해석하기 전에 Mirror Counter와 구성을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ PCAP는 왜 민감한 정보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture에는 다음 정보가 들어갈 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 IP와 Network 구조&lt;/li&gt;
&lt;li&gt;DNS Query&lt;/li&gt;
&lt;li&gt;평문 HTTP Header와 Body&lt;/li&gt;
&lt;li&gt;Cookie와 Token&lt;/li&gt;
&lt;li&gt;Email, File과 개인정보&lt;/li&gt;
&lt;li&gt;TLS Handshake Metadata&lt;/li&gt;
&lt;li&gt;취약한 Protocol의 Credential&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 분석이라는 이유로 필요 이상 Traffic을 장시간 수집하면 새로운 보안 위험을 만든다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;최소 범위 Capture
접근 권한 제한
암호화된 저장과 전송
보존 기간과 안전한 삭제
공유 전 Redaction
감사 기록&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Session Key Log와 복호화된 PCAP는 특히 더 엄격하게 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Production Capture는 조직의 승인과 개인정보 정책을 따라야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 좋은 장애 기록은 무엇을 포함할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Context를 함께 남긴다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Incident 시간과 Timezone
Source Host, Namespace, Address와 Port
Destination Name, Address와 Port
IPv4 또는 IPv6
실제 Application 요청과 Error
Capture Host, Interface, Direction
Capture 시작&amp;middot;종료 시각
Capture와 Display Filter
Snaplen과 Packet Drop Counter
Offload와 SPAN 구성
관련 Rule ID, Conntrack와 Log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCAP 파일만 전달하면 다음 분석자가 관찰 범위를 다시 추측해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증거를 재현 가능한 문장과 함께 전달한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 계층별 질문과 도구를 연결하면&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;질문&lt;/td&gt;
&lt;td&gt;먼저 볼 State&amp;middot;도구&lt;/td&gt;
&lt;td&gt;Packet에서 찾을 증거&lt;/td&gt;
&lt;td&gt;아직 증명하지 않는 것&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface가 준비됐나?&lt;/td&gt;
&lt;td&gt;ip -s link,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ip addr&lt;/td&gt;
&lt;td&gt;ARP/NDP와 실제 TX/RX&lt;/td&gt;
&lt;td&gt;End-to-End Route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;어느 길로 나가나?&lt;/td&gt;
&lt;td&gt;ip route get,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ip rule&lt;/td&gt;
&lt;td&gt;Source, Next Hop, TTL&lt;/td&gt;
&lt;td&gt;중간 Router의 전체 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next Hop을 찾았나?&lt;/td&gt;
&lt;td&gt;ip neigh&lt;/td&gt;
&lt;td&gt;ARP Request/Reply, NDP&lt;/td&gt;
&lt;td&gt;Remote Service 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이름이 맞나?&lt;/td&gt;
&lt;td&gt;dig&lt;/td&gt;
&lt;td&gt;DNS Query/Response, RCODE&lt;/td&gt;
&lt;td&gt;선택된 Address의 Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ICMP 왕복이 되나?&lt;/td&gt;
&lt;td&gt;ping&lt;/td&gt;
&lt;td&gt;Echo Request/Reply&lt;/td&gt;
&lt;td&gt;TCP&amp;middot;TLS&amp;middot;Application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중간 응답은 어디까지인가?&lt;/td&gt;
&lt;td&gt;traceroute&lt;/td&gt;
&lt;td&gt;TTL Probe, Time Exceeded&lt;/td&gt;
&lt;td&gt;고정된 전체 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP Port가 연결되나?&lt;/td&gt;
&lt;td&gt;nc,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ss&lt;/td&gt;
&lt;td&gt;SYN/SYN-ACK/ACK 또는 RST&lt;/td&gt;
&lt;td&gt;TLS와 업무 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS Identity가 맞나?&lt;/td&gt;
&lt;td&gt;openssl,&lt;span&gt;&amp;nbsp;&lt;/span&gt;curl -v&lt;/td&gt;
&lt;td&gt;ClientHello, ServerHello, Alert&lt;/td&gt;
&lt;td&gt;Application Authorization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP가 응답하나?&lt;/td&gt;
&lt;td&gt;curl, Access Log&lt;/td&gt;
&lt;td&gt;TLS Record와 Connection 종료&lt;/td&gt;
&lt;td&gt;모든 API와 Backend Health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;어디에서 Packet이 사라지나?&lt;/td&gt;
&lt;td&gt;Multi-point&lt;span&gt;&amp;nbsp;&lt;/span&gt;tcpdump&lt;/td&gt;
&lt;td&gt;동일 Flow의 지점별 존재&lt;/td&gt;
&lt;td&gt;장비 내부 Drop Reason&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구 하나가 다음 계층의 질문까지 대신하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 가설 기반 진단은 어떻게 반복할까?&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 관찰된 증상을 한 문장으로 제한
2. 가능한 원인을 계층별로 나눔
3. 후보를 구분할 가장 싼 증거 선택
4. 명령 또는 Capture 실행
5. 결과가 증명한 범위 기록
6. 남은 후보로 다음 질문 이동&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 HTTPS Timeout을 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;가설 A: DNS가 잘못된 Address 반환
&amp;rarr; dig와 Application Resolver 결과 비교

가설 B: TCP SYN이 Drop
&amp;rarr; Client와 Firewall 양쪽 Capture

가설 C: TLS Certificate 문제
&amp;rarr; TCP 성공 후 openssl 검증 결과

가설 D: HTTP Backend 지연
&amp;rarr; TLS 성공, Access Log와 Trace 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 모든 Packet을 읽지 않고 각 증거가 후보를 어떻게 나누는지 설계한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 진단 State와 증거로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우리 건물의 문과 주소 확인&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Interface / Address State&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Local Link와 IP 설정 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;어떤 우체국에 맡길지 조회&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Route Lookup&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Next Hop, Interface와 Source 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;옆 우체국의 실제 위치 장부&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ARP / NDP Neighbor Table&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Next-Hop IP를 Link Address에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;수신인 이름을 주소록에서 찾음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;DNS Query&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Name을 A&amp;middot;AAAA 등 Resource Record로 해석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;창구가 열렸는지 시험&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;TCP Probe&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN Handshake 또는 RST 관찰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;봉인과 신분증을 확인&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;TLS Probe&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Certificate, Hostname, Cipher와 Alert 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;길목마다 설치한 관찰소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Multi-point Capture&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 Flow의 지점별 Packet 존재 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;관찰소가 기록한 봉투 일부&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;PCAP&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;특정 Interface&amp;middot;시간&amp;middot;Filter에서 수집한 Packet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우체국 내부 처리 장부&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Firewall / Conntrack / Application Log&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Packet 밖의 Policy와 Process State 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;관찰 카메라 자체가 놓친 장면&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Capture Drop&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Buffer&amp;middot;CPU&amp;middot;SPAN 한계로 PCAP에서 누락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;포장 전후 크기가 다르게 보임&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;NIC Offload&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Capture 위치 때문에 Wire와 다른 Packet 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;봉인된 편지의 겉면만 관찰&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Encrypted Payload&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Metadata는 보이지만 Application 평문은 숨겨짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진단 흐름을 한 장면으로 모으면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Configuration State
ip link / addr / route / neigh
            &amp;darr;
Name과 Protocol Probe
dig / ping / traceroute / nc / curl / openssl
            &amp;darr;
Packet Evidence
Client ─ Firewall In ─ Firewall Out ─ Server
            &amp;darr;
Internal State와 의미
Rule Counter / Conntrack / TLS / Application Log
            &amp;darr;
가설 제거와 다음 관찰&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 Multi-point Evidence를 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;python3&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;이 있는 개인 Linux VM에서 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 Network Namespace 안에서만 TCP 8080을 Drop한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;cap-client                 cap-router                 cap-server
10.26.1.2 ────────── 10.26.1.1 | 10.26.2.1 ──────── 10.26.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add cap-client
sudo ip netns add cap-router
sudo ip netns add cap-server

sudo ip link add veth-cc type veth peer name veth-cri
sudo ip link add veth-cro type veth peer name veth-cs

sudo ip link set veth-cc netns cap-client
sudo ip link set veth-cri netns cap-router
sudo ip link set veth-cro netns cap-router
sudo ip link set veth-cs netns cap-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n cap-client addr add 10.26.1.2/24 dev veth-cc
sudo ip -n cap-router addr add 10.26.1.1/24 dev veth-cri
sudo ip -n cap-router addr add 10.26.2.1/24 dev veth-cro
sudo ip -n cap-server addr add 10.26.2.2/24 dev veth-cs

sudo ip -n cap-client link set lo up
sudo ip -n cap-router link set lo up
sudo ip -n cap-server link set lo up
sudo ip -n cap-client link set veth-cc up
sudo ip -n cap-router link set veth-cri up
sudo ip -n cap-router link set veth-cro up
sudo ip -n cap-server link set veth-cs up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 IPv4 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n cap-client route add default via 10.26.1.1
sudo ip -n cap-server route add default via 10.26.2.1
sudo ip netns exec cap-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 HTTP Server를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-server \
  python3 -m http.server 8080 --bind 10.26.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 Client에서 Server로 가는 TCP 8080을 Drop하게 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-router \
  iptables -I FORWARD 1 \
  -i veth-cri -o veth-cro \
  -p tcp --dport 8080 -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 세 지점에서 동시에 Capture하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 명령을 별도 Terminal에서 시작한다. 15초 뒤 자동 종료되므로 모두 시작한 직후 Client 요청을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Capture:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-client \
  timeout 15 tcpdump -nn -i veth-cc -s 0 \
  -w /tmp/cap-client-drop.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router Capture:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-router \
  timeout 15 tcpdump -nn -i any -s 0 \
  -w /tmp/cap-router-drop.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Capture:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-server \
  timeout 15 tcpdump -nn -i veth-cs -s 0 \
  -w /tmp/cap-server-drop.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 HTTP 요청을 보낸다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo ip netns exec cap-client \
  curl --connect-timeout 3 http://10.26.2.2:8080/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 관찰은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client PCAP
&amp;rarr; SYN과 Retransmission 보임

Router PCAP
&amp;rarr; veth-cri Ingress의 SYN 보임
&amp;rarr; veth-cro Egress에는 SYN 없음

Server PCAP
&amp;rarr; SYN 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router Rule Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-router \
  iptables -L FORWARD -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 증거는 Router의 FORWARD Drop Rule과 Match했다는 범위까지 강하게 좁힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCAP만으로 Rule ID를 알게 된 것이 아니라 Rule Counter를 함께 확인했기 때문에 Policy 이유를 연결할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 저장한 PCAP를 Command Line에서 비교하기&lt;/h4&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;sudo tcpdump -nn -r /tmp/cap-client-drop.pcap 'tcp port 8080'
sudo tcpdump -nn -r /tmp/cap-router-drop.pcap 'tcp port 8080'
sudo tcpdump -nn -r /tmp/cap-server-drop.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 표를 직접 채운다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;관찰 지점&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN-ACK&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Retransmission&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;해석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Client&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보임&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보임&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;응답을 받지 못해 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Router Ingress&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보임&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보임&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Client에서 Router까지 도착&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Router Egress&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Router 내부에서 전달되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Server&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Server 도착 전 범위에서 중단&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;any&lt;/code&gt; PCAP에서는 Interface ID를 확인해 Ingress와 Egress를 분리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Drop Rule을 제거한 뒤 성공 흐름과 비교하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Drop Rule을 제거한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec cap-router \
  iptables -D FORWARD \
  -i veth-cri -o veth-cro \
  -p tcp --dport 8080 -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 세 Capture를 새 파일 이름으로 다시 실행한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;/tmp/cap-client-ok.pcap
/tmp/cap-router-ok.pcap
/tmp/cap-server-ok.pcap&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 &lt;code&gt;curl&lt;/code&gt;을 다시 실행한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo ip netns exec cap-client \
  curl --connect-timeout 3 http://10.26.2.2:8080/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공 PCAP에서는 다음 흐름을 기대할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SYN
SYN-ACK
ACK
HTTP Request Data
HTTP Response Data
FIN 또는 Connection 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패와 성공 Capture를 비교하면 정상 기준선에서 사라진 첫 사건을 찾기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal의 HTTP Server를 &lt;code&gt;Ctrl+C&lt;/code&gt;로 종료한 뒤 Namespace를 삭제한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del cap-client
sudo ip netns del cap-router
sudo ip netns del cap-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;와 &lt;code&gt;tcpdump&lt;/code&gt; Option은 Linux 배포판과 Version에 따라 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Host Firewall을 실습용으로 바꾸지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCAP에는 HTTP Directory Listing 같은 Payload가 포함될 수 있으므로 &lt;code&gt;/tmp&lt;/code&gt; 파일도 실습 후 조직 정책에 맞게 삭제한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Packet Capture는 특정 Host&amp;middot;Interface&amp;middot;방향&amp;middot;시간&amp;middot;Filter에서 관찰한 사실이며 Network 전체의 전지적 기록이 아니다.&lt;/li&gt;
&lt;li&gt;도구를 열기 전에 Source, Destination, Protocol과 관찰할 사건을 질문으로 정의해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip link&lt;/code&gt;, &lt;code&gt;ip addr&lt;/code&gt;, &lt;code&gt;ip route get&lt;/code&gt;, &lt;code&gt;ip neigh&lt;/code&gt;는 Local Interface, Source&amp;middot;Route와 Next-Hop State를 Packet보다 먼저 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dig&lt;/code&gt;는 DNS Answer와 RCODE를, &lt;code&gt;nc&lt;/code&gt;는 Transport 연결을, &lt;code&gt;curl&lt;/code&gt;과 &lt;code&gt;openssl&lt;/code&gt;은 TLS&amp;middot;Application 단계를 분리한다.&lt;/li&gt;
&lt;li&gt;Capture Filter는 저장 전 Packet을 제외하고 Display Filter는 저장된 Packet의 화면만 좁힌다.&lt;/li&gt;
&lt;li&gt;Snaplen, Capture Buffer, CPU&amp;middot;Disk와 SPAN Capacity 때문에 PCAP 자체에서 Packet이 누락될 수 있다.&lt;/li&gt;
&lt;li&gt;Promiscuous Mode는 Switch가 해당 Port로 보내지 않은 Unicast Traffic까지 가져오지 못한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;any&lt;/code&gt; Interface, Bridge와 veth Capture는 같은 논리적 Packet을 여러 번 보여 줄 수 있다.&lt;/li&gt;
&lt;li&gt;Checksum Offload는 Host Capture의 Bad Checksum을, TSO/GSO/GRO/LRO는 Wire와 다른 Packet 크기를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;Wireshark의 &lt;code&gt;tcp.analysis.*&lt;/code&gt;는 Header Flag가 아니라 Capture 관점의 분석 결과다.&lt;/li&gt;
&lt;li&gt;Client의 SYN Retransmission은 응답 부재를 보여 주지만 Forward Packet과 Return Reply 중 어디에서 Drop됐는지 말하지 않는다.&lt;/li&gt;
&lt;li&gt;Server NIC Capture에 Data가 보이는 것과 Application이 읽고 업무를 처리한 것은 다른 사건이다.&lt;/li&gt;
&lt;li&gt;Multi-point Capture는 Packet이 마지막으로 보인 지점과 처음 사라진 지점 사이로 위치 범위를 줄인다.&lt;/li&gt;
&lt;li&gt;정확한 Drop 이유는 Rule Counter, Conntrack, Route, Interface Counter와 Application Log를 추가로 연결해야 한다.&lt;/li&gt;
&lt;li&gt;여러 PCAP의 One-Way 시간을 비교하려면 Clock Synchronization과 Timestamp 정확도가 필요하다.&lt;/li&gt;
&lt;li&gt;PCAP와 TLS Key Log는 Credential과 개인정보를 포함할 수 있으므로 최소 수집, 접근 통제와 보존 정책이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어:&amp;nbsp;Evidence-Based Troubleshooting,&amp;nbsp;Interface State,&amp;nbsp;Route Lookup,&amp;nbsp;Neighbor Table,&amp;nbsp;dig,&amp;nbsp;netcat,&amp;nbsp;curl,&amp;nbsp;openssl s_client,&amp;nbsp;tcpdump,&amp;nbsp;Wireshark,&amp;nbsp;Packet Capture,&amp;nbsp;PCAP,&amp;nbsp;Capture Point,&amp;nbsp;Capture Filter,&amp;nbsp;Berkeley Packet Filter,&amp;nbsp;BPF,&amp;nbsp;Display Filter,&amp;nbsp;Snaplen,&amp;nbsp;Capture Drop,&amp;nbsp;Promiscuous Mode,&amp;nbsp;SPAN,&amp;nbsp;Network TAP,&amp;nbsp;Checksum Offload,&amp;nbsp;TSO,&amp;nbsp;GSO,&amp;nbsp;GRO,&amp;nbsp;LRO,&amp;nbsp;Expert Info,&amp;nbsp;Multi-point Capture,&amp;nbsp;Clock Synchronization,&amp;nbsp;NTP,&amp;nbsp;PTP,&amp;nbsp;Rule Counter,&amp;nbsp;Conntrack,&amp;nbsp;Application Log&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet은 거짓말하지 않는다고 말하지만, Capture는 모든 Packet을 보지 않는다. &lt;br /&gt;어느 지점에서 무엇을 보았고 무엇은 보지 못했는지 관찰 범위를 적을 때 PCAP는 추측을 줄이는 증거가 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이름, Route, Neighbor, Transport, TLS와 Application을 계층별로 확인하고 Packet이 사라진 구간을 증거로 좁힐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 편지 한 통의 전체 여정을 처음부터 끝까지 다시 연결할 차례다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Browser에 URL을 입력한 순간부터 DNS, ARP, Switch, Gateway, Routing, NAT, TCP, TLS와 HTTP Response까지 어떤 순서로 움직일까? 중간 한 단계가 빠지면 어떤 증거가 남을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;편지 한 통의 여정을 혼자 설명할 수 있을까?&lt;/b&gt;」라는 질문으로 지금까지의 기술을 하나의 Packet Journey와 진단 Checklist에 연결한다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Evidence-Based Troubleshooting</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>tcpdump</category>
      <category>wireshark</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1103</guid>
      <comments>https://peterica.tistory.com/1103#entry1103comment</comments>
      <pubDate>Tue, 28 Jul 2026 22:26:06 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 25. traceroute는 보이지 않는 길을 어떻게 보여줄까?</title>
      <link>https://peterica.tistory.com/1102</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LZOVT/dJMcaasAunz/kleZKRXjGN3ZrknboKZog0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LZOVT/dJMcaasAunz/kleZKRXjGN3ZrknboKZog0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LZOVT/dJMcaasAunz/kleZKRXjGN3ZrknboKZog0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLZOVT%2FdJMcaasAunz%2FkleZKRXjGN3ZrknboKZog0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 8. 편지가 멈춘 곳을 찾는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;은 Destination까지 Echo가 왕복했는지 알려 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중간에 어느 Router를 지났는지는 보여 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;code&gt;traceroute&lt;/code&gt;는 Router의 Routing Table을 차례로 읽어 오는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 Internet의 전체 지도를 요청할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 수명이 짧은 Probe를 보내 일부러 첫 Router에서 끝나게 하고, 다음 Probe는 두 번째 Router에서 끝나게 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 1인 Probe &amp;rarr; 첫 번째 Router에서 만료
TTL 2인 Probe &amp;rarr; 두 번째 Router에서 만료
TTL 3인 Probe &amp;rarr; 세 번째 Router에서 만료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Router가 돌려준 ICMP 오류를 모으면 지나간 경로의 일부를 역으로 추론할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 단어는 &amp;ldquo;조회&amp;rdquo;가 아니라 &lt;b&gt;추론&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 답하지 않을 수도 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe마다 다른 길을 갈 수도 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Reply가 돌아오는 길도 Forward Path와 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;TTL/Hop Limit&lt;/b&gt;, &lt;b&gt;ICMP Time Exceeded&lt;/b&gt;, &lt;b&gt;UDP&amp;middot;ICMP&amp;middot;TCP Probe&lt;/b&gt;, &lt;b&gt;Hop RTT&lt;/b&gt;, &lt;b&gt;Asymmetric Routing&lt;/b&gt;, &lt;b&gt;ECMP&lt;/b&gt;, &lt;b&gt;Rate Limiting&lt;/b&gt;, &lt;b&gt;Paris Traceroute&lt;/b&gt;를 통해 결과가 보여 주는 범위와 빈칸의 의미를 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute는 왜 일부러 Packet을 만료시킬까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Packet의 TTL은 Router를 통과할 때마다 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source가 TTL 3으로 전송
R1이 3 &amp;rarr; 2
R2가 2 &amp;rarr; 1
R3이 1 &amp;rarr; 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 0이 되면 R3은 Packet을 다음 Hop으로 전달하지 않고 폐기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R3은 Source에 다음 ICMP 오류를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Time Exceeded
Type 11, Code 0
TTL Exceeded in Transit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 오류를 보낸 Address를 보고 &amp;ldquo;TTL 3인 Probe가 이 지점에서 끝났다&amp;rdquo;는 단서를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 정상 Packet의 TTL 만료를 기다리는 것이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL을 1부터 의도적으로 늘려 이 안전장치를 관찰 도구로 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 첫 번째 Hop은 어떻게 나타날까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source가 TTL 1인 Probe를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Source ── TTL 1 ──&amp;gt; R1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 Forwarding 과정에서 TTL을 줄인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 1 &amp;rarr; 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe는 R1을 넘어가지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1이 ICMP Time Exceeded를 보내면 &lt;code&gt;traceroute&lt;/code&gt;의 첫 줄에 R1의 응답 Address와 RTT가 표시된다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1  10.25.1.1  0.401 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;1&lt;/code&gt;은 Router의 고유 번호가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 1인 Probe에 응답한 Hop이라는 뜻이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 번째와 세 번째 Hop은 어떻게 찾을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Probe의 TTL을 2로 설정한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Source ── TTL 2 ──&amp;gt; R1 ── TTL 1 ──&amp;gt; R2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2에서 TTL이 0이 되고 Time Exceeded가 돌아오면 두 번째 Hop을 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 3도 같은 방식이다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Source ── TTL 3 ──&amp;gt; R1 ── TTL 2 ──&amp;gt; R2 ── TTL 1 ──&amp;gt; R3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력은 다음처럼 쌓인다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1  10.25.1.1     0.401 ms
2  10.25.2.2     0.612 ms
3  10.25.3.2     0.811 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 줄은 하나의 Packet이 모든 Hop을 지나며 남긴 흔적이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 서로 다른 &lt;b&gt;별개의 Probe와 Reply&lt;/b&gt;를 모은 결과다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Hop 1 결과 &amp;rarr; TTL 1 Probe
Hop 2 결과 &amp;rarr; TTL 2 Probe
Hop 3 결과 &amp;rarr; TTL 3 Probe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 도중 Route가 바뀌면 한 화면에 서로 다른 순간의 경로가 섞일 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Router는 원래 Probe의 무엇을 돌려줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Time Exceeded에는 문제가 된 원래 IP Header와 상위 Protocol을 식별할 수 있는 원래 Packet 일부가 인용된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Time Exceeded
└─ Original Probe의 IP Header와 일부 Payload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 인용된 정보를 보고 어느 TTL과 어느 Probe에 대한 응답인지 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe 방식에 따라 다음 값이 단서가 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UDP Source와 Destination Port&lt;/li&gt;
&lt;li&gt;ICMP Identifier와 Sequence Number&lt;/li&gt;
&lt;li&gt;TCP Source와 Destination Port 및 Sequence 관련 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 오류가 도착했다는 사실만 보는 것이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내가 보낸 어느 Probe의 오류인가&lt;/b&gt;를 맞추는 과정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT나 Firewall이 Tuple을 바꾸거나 인용 영역을 충분히 전달하지 않으면 매칭이 어려워질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Destination에 도착한 Probe는 왜 끝나지 않을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 충분히 커지면 Probe는 Destination에 도착한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 TTL이 만료되지 않으므로 Destination이 다른 방식으로 최종 응답을 보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 응답은 Probe Protocol에 따라 다르다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Classic UDP Probe
&amp;rarr; 닫힌 높은 UDP Port
&amp;rarr; ICMP Destination Unreachable, Port Unreachable

ICMP Echo Probe
&amp;rarr; ICMP Echo Reply

TCP SYN Probe
&amp;rarr; SYN-ACK 또는 RST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 UDP Port Unreachable은 일반적으로 다음 값이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Type 3, Code 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지의 닫힌 Port 응답은 실패가 아니라 &amp;ldquo;Probe가 Destination까지 도착했다&amp;rdquo;는 종료 신호로 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Probe도 Port가 열려 있으면 SYN-ACK, 닫혀 있으면 RST가 돌아올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 Destination 도달의 단서가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute는 왜 운영체제마다 다르게 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 Unix 계열 &lt;code&gt;traceroute&lt;/code&gt;는 높은 Destination Port의 UDP Probe를 사용하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows &lt;code&gt;tracert&lt;/code&gt;는 일반적으로 ICMP Echo Probe를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 여러 &lt;code&gt;traceroute&lt;/code&gt; 구현은 Option으로 ICMP 또는 TCP Probe를 선택할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;traceroute 기본값
&amp;rarr; 구현에 따라 UDP 등

tracert 기본값
&amp;rarr; ICMP Echo 계열

TCP traceroute
&amp;rarr; 선택한 TCP Port로 SYN Probe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Source와 Destination이라도 Probe Protocol과 Port가 다르면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Firewall Policy와 ECMP Hash가 달라져 결과도 바뀔 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;내 traceroute와 상대의 traceroute가 다르다&amp;rdquo;는 사실을 비교하려면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 먼저 사용한 Protocol, Port, Address Family와 Source Address를 맞춰야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 세 개의 시간은 같은 Packet을 세 번 측정한 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 출력에서 한 Hop에 세 개의 RTT가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;5  192.0.2.5  12.1 ms  13.4 ms  11.9 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 같은 TTL을 가진 Probe를 세 번 따로 보내고 각 Reply 시간을 표시한 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 5, Probe 1 &amp;rarr; 12.1 ms
TTL 5, Probe 2 &amp;rarr; 13.4 ms
TTL 5, Probe 3 &amp;rarr; 11.9 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 Probe가 반드시 같은 Route를 통과한다고 보장할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP가 Transport Header를 Hash에 사용하면 Probe마다 Port가 달라져 다른 Next Hop을 선택할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 줄에 서로 다른 Router Address가 나타나는 이유가 될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;5  192.0.2.5  12.1 ms  192.0.2.9  13.4 ms  192.0.2.5  11.9 ms&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Hop의 RTT는 어느 구간의 지연일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop 5의 &lt;code&gt;12 ms&lt;/code&gt;는 Hop 4에서 Hop 5로 가는 Link가 12ms라는 뜻이 아니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source
&amp;rarr; TTL 5 Probe가 Hop 5까지 이동
&amp;rarr; Hop 5가 ICMP를 생성&amp;middot;처리
&amp;rarr; ICMP가 Return Path로 Source까지 이동&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;즉, 해당 Probe의 &lt;b&gt;Source부터 응답 Router까지의 왕복 시간&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 요소가 섞인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Forward Path의 모든 Link와 Queue&lt;/li&gt;
&lt;li&gt;Router Control Plane의 ICMP 처리와 Scheduling&lt;/li&gt;
&lt;li&gt;ICMP Reply의 Return Path&lt;/li&gt;
&lt;li&gt;Source의 송수신 Scheduling&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop 4가 10ms이고 Hop 5가 30ms라고 다음처럼 단정하면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Hop 4 &amp;rarr; Hop 5 Link Delay = 30 - 10 = 20ms  (확정 불가)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Hop의 ICMP Reply가 다른 Return Path와 우선순위를 사용할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 중간 Hop만 느리고 뒤 Hop은 빠를 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 결과를 보자.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;4  192.0.2.4  8 ms
5  192.0.2.5  80 ms
6  192.0.2.6  10 ms
7  198.51.100.20  11 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop 5가 지나가는 Data Packet을 80ms씩 늦췄다면 뒤 Hop도 누적된 지연의 영향을 받을 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Hop 6과 Destination RTT는 다시 10~11ms다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 Hop 5가 Transit Forwarding은 빠르게 하면서 자신에게 향한 ICMP 생성만 낮은 우선순위로 처리했을 가능성을 보여 준다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;중간 Hop의 높은 RTT
+ 이후 Hop의 정상 RTT
&amp;rarr; 해당 Router의 ICMP Control Plane 우선순위 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 한 줄의 큰 숫자만 보고 병목이라고 결론 내리지 않는다. 지연이 뒤 Hop과 Destination까지 지속되는지 함께 본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 별표 하나는 무엇을 뜻할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정해진 시간 안에 해당 Probe의 응답을 받지 못하면 &lt;code&gt;*&lt;/code&gt;가 표시될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;5  *  *  *&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별표가 직접 증명하는 것은 다음뿐이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 TTL의 Probe에 대응하는 응답을 Timeout 안에 관찰하지 못했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 원인은 여러 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Probe가 Forward Path에서 Drop&lt;/li&gt;
&lt;li&gt;Router가 Time Exceeded를 만들지 않음&lt;/li&gt;
&lt;li&gt;ICMP 생성이 Rate Limit&lt;/li&gt;
&lt;li&gt;ICMP Reply가 Return Path에서 Drop&lt;/li&gt;
&lt;li&gt;Firewall이 특정 Protocol이나 ICMP를 차단&lt;/li&gt;
&lt;li&gt;Reply가 Timeout 뒤 늦게 도착&lt;/li&gt;
&lt;li&gt;&lt;code&gt;traceroute&lt;/code&gt;가 Reply를 원래 Probe와 매칭하지 못함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;*&lt;/code&gt;는 Router가 없다는 표시도, 그 Router가 Down이라는 표시도 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 별표 뒤에 다음 Hop이 다시 나타날 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;4  192.0.2.4       8 ms
5  * * *
6  192.0.2.6      10 ms
7  198.51.100.20  11 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 5인 Probe는 다섯 번째 Router에서 만료되었다. 그 Router가 Time Exceeded에 응답하지 않아 별표가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 6인 다음 Probe는 다섯 번째 Router를 TTL이 남은 상태로 통과한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 5 Probe
&amp;rarr; Hop 5에서 만료, ICMP 응답 없음

TTL 6 Probe
&amp;rarr; Hop 5 통과
&amp;rarr; Hop 6에서 만료, ICMP 응답 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop 5가 Transit Packet을 전달하지 못했다면 뒤 Hop도 보이기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤 Hop이 나타난다는 사실은 해당 지점이 &lt;b&gt;Forwarding은 했지만 자신이 응답하지 않았을 가능성&lt;/b&gt;을 보여 준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 별표 뒤가 비어 있으면 어디가 끊겼을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 결과를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1  10.0.0.1       1 ms
2  192.0.2.1      5 ms
3  * * *
4  * * *
5  * * *
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 응답 Hop 다음에서 Data Forwarding이 실패했을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음 가능성도 남는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 뒤 경로 전체가 ICMP Time Exceeded를 차단&lt;/li&gt;
&lt;li&gt;Probe Protocol 자체가 Firewall에서 차단&lt;/li&gt;
&lt;li&gt;Return Path의 ICMP가 Source로 오지 못함&lt;/li&gt;
&lt;li&gt;Destination까지 전달되지만 종료 응답이 차단&lt;/li&gt;
&lt;li&gt;Maximum Hop 안에 Destination에 도달하지 못함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;마지막으로 보인 Router가 문제&amp;rdquo;라고 단정하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 응답 Router는 자신이 응답할 수 있었다는 증거일 뿐, 바로 다음 Link에서 Drop했음을 증명하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Forward Path와 ICMP Return Path는 같을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 5 Probe가 Router R5에서 만료되었다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Probe Forward Path
Source &amp;rarr; R1 &amp;rarr; R2 &amp;rarr; R3 &amp;rarr; R4 &amp;rarr; R5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R5의 ICMP Time Exceeded는 별도의 IP Packet이다. Source로 돌아가기 위해 R5의 Routing Table을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;tp&quot;&gt;&lt;code&gt;ICMP Return Path
R5 &amp;rarr; X3 &amp;rarr; X2 &amp;rarr; X1 &amp;rarr; Source&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt; 한 줄의 Address는 Forward Probe가 만료된 지점의 응답 Source다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 RTT에는 Probe의 Forward Path와 ICMP의 Return Path가 함께 들어 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;화면에 보이는 Hop 목록
&amp;ne; Reply Return Path까지 모두 펼친 지도&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination에서 Source 방향으로 실행한 Reverse Traceroute도 원래 Return Path와 같다고 보장할 수 없다. Source Address, Policy와 ECMP Hash가 달라질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 응답 Address는 Router의 어느 Interface일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router에는 여러 Interface Address가 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;R5
├─ Probe가 들어온 Interface Address
├─ Probe가 나가려던 Interface Address
├─ ICMP가 Source로 돌아갈 Interface Address
└─ Loopback Address&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Time Exceeded의 Source Address 선택은 구현과 Route에 따라 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력된 Address가 반드시 Probe가 들어온 Interface이거나 Source와 직접 연결된 Link Address라고 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router의 한 Address만 보고 장비 위치와 소유자를 확정하기 어려운 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이름이 보이면 Router의 정체를 알 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 &lt;code&gt;traceroute&lt;/code&gt;는 응답 IP의 Reverse DNS(PTR)를 조회해 이름을 표시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;5  edge-seoul.example.net (192.0.2.5)  12 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PTR 이름은 운영자가 붙인 Label이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유용한 단서가 될 수 있지만 물리 위치, 장비 역할과 소유권을 암호학적으로 증명하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reverse DNS 조회가 느리거나 실패하면 &lt;code&gt;traceroute&lt;/code&gt; 출력 자체가 늦게 보일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 Address만 빠르게 보려면 구현에 따라 &lt;code&gt;-n&lt;/code&gt; Option을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;traceroute -n 198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서는 &lt;code&gt;tracert -d&lt;/code&gt;처럼 다른 Option을 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ECMP는 한 Hop에 여러 주소를 어떻게 만들까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 같은 Destination Prefix로 비용이 같은 Next Hop을 여러 개 가지고 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;            ┌&amp;rarr; Path A
Source &amp;rarr; R1 ┤
            └&amp;rarr; Path B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP는 일반적으로 Flow의 5-Tuple 같은 Header 값을 Hash해 한 Flow의 Next Hop을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Classic UDP traceroute가 Probe마다 Destination Port를 바꾸면 ECMP Hash도 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TTL 5 Probe 1, UDP Dst Port 33450 &amp;rarr; Path A
TTL 5 Probe 2, UDP Dst Port 33451 &amp;rarr; Path B
TTL 5 Probe 3, UDP Dst Port 33452 &amp;rarr; Path A&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번의 출력에 실제로 동시에 사용되지 않은 여러 Flow의 경로가 섞일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Paris Traceroute&lt;/b&gt; 계열의 접근은 Flow Identifier를 일정하게 유지하면서 Probe를 구분해 Per-Flow Load Balancing이 만든 경로 혼합을 줄이려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 Per-Packet Load Balancing, Route 변화와 Reply Path 차이까지 모두 제거하는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 실제 HTTPS와 같은 길을 시험하려면 TCP 443이면 충분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP traceroute가 차단되고 TCP 443은 허용되는 Network가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Probe를 Destination Port 443으로 보내면 실제 HTTPS와 더 비슷한 Firewall Policy와 ECMP 입력을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 구현의 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;sudo traceroute -T -p 443 -n server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 완전히 같은 Flow라고 보장할 수는 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Source Port가 실제 Application 연결과 다를 수 있다.&lt;/li&gt;
&lt;li&gt;Probe의 TCP Flag와 Option이 실제 TLS 연결과 다르다.&lt;/li&gt;
&lt;li&gt;Route가 측정 사이에 바뀔 수 있다.&lt;/li&gt;
&lt;li&gt;Load Balancer가 Application 상태에 따라 다른 Backend를 선택할 수 있다.&lt;/li&gt;
&lt;li&gt;Destination의 SYN-ACK 또는 RST Policy가 다를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe Protocol을 실제 Traffic과 가깝게 맞추는 것은 가설을 개선하지만 실제 Application Packet의 완전한 복제는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP, ICMP, TCP 결과가 다르면 어느 것이 맞을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 결과가 서로 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;UDP traceroute  &amp;rarr; Hop 4부터 별표
ICMP traceroute &amp;rarr; Destination까지 표시
TCP 443 trace   &amp;rarr; Destination까지 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 하나가 거짓이고 하나가 참이라는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Probe가 다른 Policy와 Hash 조건을 통과했을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;UDP 결과
&amp;rarr; UDP Probe의 관찰 경로와 응답 정책

ICMP 결과
&amp;rarr; ICMP Echo Probe의 관찰 경로와 응답 정책

TCP 결과
&amp;rarr; 해당 TCP Port Probe의 관찰 경로와 응답 정책&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 HTTPS라면 TCP 443 결과에 더 무게를 둘 수 있지만 실제 &lt;code&gt;curl&lt;/code&gt;과 Packet Capture 증거를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MPLS와 Tunnel은 Hop을 숨길 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Packet이 MPLS나 Tunnel 구간을 지날 때 내부 Hop의 TTL 또는 Hop Limit이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Header와 어떻게 연동되는지는 구성에 따라 다르다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Router &amp;rarr; Tunnel Ingress ═════ Tunnel Egress &amp;rarr; IP Router
                         내부 Hop 여러 개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tunnel 내부 Router가 &lt;code&gt;traceroute&lt;/code&gt;에 모두 나타날 수도 있고 하나의 긴 구간처럼 숨을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MPLS Label 정보가 ICMP Extension으로 보이는 환경도 있지만 모든 장비가 제공하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Hop 수가 적다고 물리 장비와 전달 단계가 반드시 적은 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Overlay Network에서는 Virtual Router와 Underlay Router의 경로를 별도로 관찰해야 할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT를 지나면 Probe 연결 정보는 어떻게 달라질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 Probe의 Source Address와 UDP/TCP Port, ICMP Identifier를 바꿀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Return ICMP에는 변환 전후 Tuple을 연결할 State가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;내부 Probe
10.0.0.10:33450
        &amp;darr; NAT
203.0.113.5:40001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 Stateful NAT는 ICMP 오류에 인용된 외부 Tuple을 내부 Probe와 연결해 Source로 돌려줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Fragment, 짧은 ICMP 인용, Timeout과 비대칭 경로 때문에 매칭이 실패할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 NAT 경계가 Hop으로 보이거나 내부 Address가 ICMP Payload에 남는 등 구현별 결과도 나타날 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Routing Loop는 어떤 모습으로 나타날까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1과 R2가 Destination Route를 서로에게 가리킨다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL을 늘린 Probe는 두 Router 사이에서 반복해서 만료된다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;4  192.0.2.1
5  192.0.2.2
6  192.0.2.1
7  192.0.2.2
8  192.0.2.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Address가 번갈아 반복되는 패턴은 Routing Loop의 강한 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Address 반복 하나만으로 확정하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ICMP Source Address 선택 때문에 같은 Address가 여러 위치처럼 보일 수 있다.&lt;/li&gt;
&lt;li&gt;ECMP Probe가 서로 다른 Path를 오갈 수 있다.&lt;/li&gt;
&lt;li&gt;NAT와 Tunnel이 Address 표시를 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Router의 Route Lookup, TTL별 Capture와 Interface Counter로 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL을 크게 늘리는 것은 Loop를 고치지 않고 Packet이 더 오래 돌게 할 뿐이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 경로가 실행할 때마다 바뀌면 장애일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Internet Routing과 Load Balancing은 동적이다.&lt;/p&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;첫 실행: A &amp;rarr; B &amp;rarr; C &amp;rarr; Destination
다음 실행: A &amp;rarr; D &amp;rarr; E &amp;rarr; Destination&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화 가능한 원인은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECMP Hash 입력 변화&lt;/li&gt;
&lt;li&gt;BGP 또는 IGP Route 변화&lt;/li&gt;
&lt;li&gt;Anycast Site 선택 변화&lt;/li&gt;
&lt;li&gt;Link 장애와 Convergence&lt;/li&gt;
&lt;li&gt;Traffic Engineering Policy&lt;/li&gt;
&lt;li&gt;Mobile과 VPN 경로 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로가 다르다는 사실만으로 장애라고 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화 시점과 Packet Loss, Destination RTT, Application Error가 함께 발생했는지 시간축으로 연결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ &lt;code&gt;!H&lt;/code&gt;, &lt;code&gt;!N&lt;/code&gt;, &lt;code&gt;!X&lt;/code&gt; 같은 표시는 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 &lt;code&gt;traceroute&lt;/code&gt; 구현은 ICMP 오류 Code를 짧은 기호로 표시한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;!N  Network Unreachable
!H  Host Unreachable
!P  Protocol Unreachable
!X  Communication Administratively Prohibited
!F  Fragmentation Needed 관련 표시 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 기호와 의미는 구현마다 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표시는 응답 장비가 명시적인 오류를 보냈다는 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류를 보낸 Source Address와 인용된 Probe, Type과 Code를 Capture에서 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;!H&lt;/code&gt;가 보였다고 반드시 최종 Destination Host가 자신이 Down이라고 답한 것은 아니다. 중간 Router가 다음 Hop에 전달하지 못해 오류를 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv6 traceroute는 무엇을 바꿀까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 TTL 대신 &lt;b&gt;Hop Limit&lt;/b&gt;을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop Limit이 Transit 중 0이 되면 Router는 다음 ICMPv6 오류를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMPv6 Time Exceeded
Type 3, Code 0
Hop Limit Exceeded in Transit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP Probe가 IPv6 Destination의 닫힌 Port에 도착하면 ICMPv6 Destination Unreachable, Port Unreachable을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4와 IPv6 Route, Firewall, Source Address가 다르므로 결과도 별도로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;traceroute -4 server.example
traceroute -6 server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 운영체제는 &lt;code&gt;traceroute6&lt;/code&gt;나 다른 Option을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에 필요한 ICMPv6를 일괄 차단하면 Hop 관찰뿐 아니라 PMTUD 같은 정상 기능도 깨질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute의 Maximum Hop은 경로의 끝일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구는 무한히 TTL을 늘리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maximum Hop Limit에 도달하면 Destination 응답이 없어도 측정을 끝낸다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;30  * * *
traceroute 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Destination이 정확히 30 Hop 밖에 있다는 뜻이 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 경로가 더 길 수 있다.&lt;/li&gt;
&lt;li&gt;Routing Loop로 Destination에 도달하지 못할 수 있다.&lt;/li&gt;
&lt;li&gt;Destination 종료 응답이 차단될 수 있다.&lt;/li&gt;
&lt;li&gt;중간부터 모든 ICMP가 보이지 않을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maximum Hop과 Timeout은 측정의 종료 조건이지 Network의 물리적 경계를 선언하는 값이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute와 mtr은 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 TTL을 늘린 Probe를 일정 횟수 보내 한 시점의 경로 단서를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mtr&lt;/code&gt; 계열 도구는 traceroute와 반복 측정을 결합해 Hop별 응답률과 RTT 변화를 시간에 따라 보여 줄 수 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;traceroute
&amp;rarr; 짧은 경로 Snapshot

mtr
&amp;rarr; 반복 Probe를 통한 변화 관찰&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 중간 Router의 ICMP Rate Limiting 문제는 mtr에서도 남는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;중간 Hop Loss 80%
뒤 Hop과 Destination Loss 0%
&amp;rarr; 중간 Router의 ICMP 응답 제한 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Hop의 Loss가 뒤 Hop에 이어지지 않으면 Transit Data Loss로 곧바로 해석하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 Probe는 장비에 부담을 줄 수 있으므로 횟수와 간격을 제한하고 허가된 대상에서 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute 결과를 어떻게 문장으로 남길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰보다 넓은 결론을 피한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Hop 8부터 별표다.
&amp;rarr; Hop 8 Router가 Down이다.  (근거 부족)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증거 범위에 맞는 기록은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;10:30 KST, Client 10.0.0.10에서 Destination 198.51.100.20으로
UDP 기반 traceroute를 실행했다.
TTL 1~7 Probe에는 ICMP Time Exceeded가 돌아왔고
TTL 8 이후에는 Timeout 안에 대응 응답을 관찰하지 못했다.
같은 Source의 TCP 443 연결은 성공했다.
UDP Probe 또는 ICMP Reply Policy 차이를 우선 비교한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 정보도 함께 기록한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Source Host와 Address&lt;/li&gt;
&lt;li&gt;Destination Name과 실제 선택된 Address&lt;/li&gt;
&lt;li&gt;IPv4 또는 IPv6&lt;/li&gt;
&lt;li&gt;Probe Protocol과 Destination Port&lt;/li&gt;
&lt;li&gt;Probe 수, Timeout과 Maximum Hop&lt;/li&gt;
&lt;li&gt;실행 시각&lt;/li&gt;
&lt;li&gt;실제 Application의 성공 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구 출력만 붙이지 말고 어떤 질문으로 실행했는지 남긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 진단할 때 어떤 순서로 비교할까?&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DNS가 선택한 Destination Address를 확인한다.&lt;/li&gt;
&lt;li&gt;Local Routing Table과 실제 Source Address를 확인한다.&lt;/li&gt;
&lt;li&gt;숫자 출력으로 Reverse DNS 지연을 분리한다.&lt;/li&gt;
&lt;li&gt;기본 UDP, ICMP, 실제 Service와 가까운 TCP Probe를 비교한다.&lt;/li&gt;
&lt;li&gt;별표가 뒤 Hop까지 계속되는지, 다음 Hop이 다시 보이는지 확인한다.&lt;/li&gt;
&lt;li&gt;중간 Hop의 높은 RTT와 Loss가 Destination까지 이어지는지 본다.&lt;/li&gt;
&lt;li&gt;반대 방향 Host에서도 측정하되 원래 Return Path와 같다고 가정하지 않는다.&lt;/li&gt;
&lt;li&gt;문제가 발생한 시각의 Route 변화와 Application Error를 연결한다.&lt;/li&gt;
&lt;li&gt;필요한 지점에서 Packet Capture와 Router Counter를 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 원인을 확정하는 마지막 명령이 아니라 Capture할 지점과 비교할 Protocol을 정하는 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Probe와 응답으로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중계 가능 횟수를 1로 적은 시험 편지&lt;/td&gt;
&lt;td&gt;&lt;b&gt;TTL 1 Probe&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;첫 Router에서 수명이 끝나도록 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중계 가능 횟수를 하나씩 늘림&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Incrementing TTL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;각 Hop에서 차례로 Probe를 만료시킴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수명이 끝났다는 반송 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Time Exceeded&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IPv4 Type 11 Code 0 또는 ICMPv6 Type 3 Code 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반송장에 포함된 원래 봉투 일부&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Quoted Original Packet&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;응답을 특정 Probe와 연결할 Header 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;해당 중계소까지 갔다 돌아온 시간&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Hop RTT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Probe Forward Path와 ICMP Return Path의 왕복 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반송 안내가 오지 않은 칸&lt;/td&gt;
&lt;td&gt;&lt;b&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;Timeout&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;해당 Probe의 응답을 시간 안에 관찰하지 못함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;같은 횟수 편지가 서로 다른 길로 감&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ECMP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Flow Hash에 따라 여러 Next Hop 중 하나를 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접수 번호를 고정해 길 혼합을 줄임&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Paris Traceroute&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Flow Identifier 변화를 줄여 Per-Flow ECMP 영향 완화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적지의 닫힌 창구 반송&lt;/td&gt;
&lt;td&gt;&lt;b&gt;UDP Port Unreachable&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Classic UDP Probe가 Destination에 도착한 종료 신호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실제 서비스 창구로 보내는 시험&lt;/td&gt;
&lt;td&gt;&lt;b&gt;TCP Traceroute&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;선택한 TCP Port와 유사한 Policy 경로를 관찰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안내가 돌아오는 별도 길&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Asymmetric Return Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ICMP Reply가 Probe Forward Path와 다른 Route 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;가 만드는 지도는 다음처럼 이해해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TTL 1 Probe + Reply ─┐
TTL 2 Probe + Reply ─┤
TTL 3 Probe + Reply ─┼─&amp;gt; 서로 다른 관찰을 한 화면에 조합
TTL 4 Probe + Reply ─┤
...                  ┘

결과
&amp;rarr; 각 TTL에서 응답한 지점의 단서
&amp;rarr; Router의 전체 Routing Table이나 고정된 물리 지도는 아님&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서 직접 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Classic UDP traceroute를 Capture하면 TTL이 증가하는 Probe와 ICMP Reply를 함께 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark Display Filter 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;icmp.type == 11 || udp.dstport &amp;gt;= 33434&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Probe를 함께 본다면 다음처럼 좁힐 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;icmp || tcp.port == 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 Field는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip.ttl 또는 ipv6.hlim
icmp.type와 icmp.code
icmpv6.type와 icmpv6.code
인용된 Original IP Header
UDP 또는 TCP Source / Destination Port
Probe와 Reply Timestamp
ICMP Reply Source Address&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Router에서 Capture한 Probe의 수신 TTL과 다음 Interface로 Forward된 TTL도 비교할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Reply는 별도의 Packet이므로 새로운 TTL, Source와 Destination, Return Route를 갖는다는 점을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 3-Hop 경로를 만들어 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;traceroute&lt;/code&gt;가 있는 개인 Linux VM에서 수행한다. 네 개의 Network Namespace와 가상 Link만 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;trace-client       trace-r1          trace-r2        trace-server
10.25.1.2 ── 10.25.1.1 | 10.25.2.1 ── 10.25.2.2 | 10.25.3.1 ── 10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 세 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add trace-client
sudo ip netns add trace-r1
sudo ip netns add trace-r2
sudo ip netns add trace-server

sudo ip link add veth-tc type veth peer name veth-r1c
sudo ip link add veth-r1r2 type veth peer name veth-r2r1
sudo ip link add veth-r2s type veth peer name veth-ts

sudo ip link set veth-tc netns trace-client
sudo ip link set veth-r1c netns trace-r1
sudo ip link set veth-r1r2 netns trace-r1
sudo ip link set veth-r2r1 netns trace-r2
sudo ip link set veth-r2s netns trace-r2
sudo ip link set veth-ts netns trace-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n trace-client addr add 10.25.1.2/24 dev veth-tc
sudo ip -n trace-r1 addr add 10.25.1.1/24 dev veth-r1c
sudo ip -n trace-r1 addr add 10.25.2.1/24 dev veth-r1r2
sudo ip -n trace-r2 addr add 10.25.2.2/24 dev veth-r2r1
sudo ip -n trace-r2 addr add 10.25.3.1/24 dev veth-r2s
sudo ip -n trace-server addr add 10.25.3.2/24 dev veth-ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loopback과 Interface를 올린다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n trace-client link set lo up
sudo ip -n trace-r1 link set lo up
sudo ip -n trace-r2 link set lo up
sudo ip -n trace-server link set lo up

sudo ip -n trace-client link set veth-tc up
sudo ip -n trace-r1 link set veth-r1c up
sudo ip -n trace-r1 link set veth-r1r2 up
sudo ip -n trace-r2 link set veth-r2r1 up
sudo ip -n trace-r2 link set veth-r2s up
sudo ip -n trace-server link set veth-ts up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n trace-client route add default via 10.25.1.1
sudo ip -n trace-r1 route add 10.25.3.0/24 via 10.25.2.2
sudo ip -n trace-r2 route add 10.25.1.0/24 via 10.25.2.1
sudo ip -n trace-server route add default via 10.25.3.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Router Namespace에서 IPv4 Forwarding을 활성화한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-r1 \
  sysctl -w net.ipv4.ip_forward=1

sudo ip netns exec trace-r2 \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Terminal에서 R1을 지나는 Probe와 Reply를 저장한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-r1 \
  tcpdump -n -vv -i any -s 0 -w /tmp/traceroute-lab.pcap \
  'icmp or udp or tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 숫자 출력과 Probe 하나씩으로 Classic UDP traceroute를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-client \
  traceroute -n -q 1 -w 1 10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경이 정상이라면 R1, R2와 Destination의 응답을 차례로 기대할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1  10.25.1.1
2  10.25.2.2
3  10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Source Address 선택에 따라 표시 Address가 달라질 수 있으므로 Capture와 함께 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 응답하지 않는 Hop 뒤가 보이는 장면을 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2가 자신이 생성한 ICMP Time Exceeded만 Drop하게 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-r2 \
  iptables -I OUTPUT 1 \
  -p icmp --icmp-type time-exceeded -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 traceroute를 다시 실행한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-client \
  traceroute -n -q 1 -w 1 10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1  10.25.1.1
2  *
3  10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 2 Probe는 R2에서 만료되지만 Time Exceeded가 R2의 OUTPUT Rule에서 Drop된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 3 Probe는 R2를 통과해 Server에 도착하고 Server의 UDP Port Unreachable이 돌아오므로 Destination은 표시될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-r2 \
  iptables -L OUTPUT -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule을 제거한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-r2 \
  iptables -D OUTPUT \
  -p icmp --icmp-type time-exceeded -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP Probe와 비교해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 TCP 8080을 Listen한다. &lt;code&gt;nc&lt;/code&gt; Option은 구현마다 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-server nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 TCP SYN Probe를 사용한다. 이 Option은 Linux &lt;code&gt;traceroute&lt;/code&gt; 구현 기준이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec trace-client \
  traceroute -T -p 8080 -n -q 1 -w 1 10.25.3.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP 결과와 TCP 결과의 Hop과 종료 응답을 Capture에서 비교한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;UDP Destination 종료: ICMP Port Unreachable
TCP Open Port 종료   : SYN-ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP traceroute가 SYN-ACK을 받은 뒤 연결을 완성하지 않고 정리하는 방식은 구현에 따라 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Application이 반드시 &lt;code&gt;accept()&lt;/code&gt;까지 완료했다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Firewall Rule과 가상 Interface도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del trace-client
sudo ip netns del trace-r1
sudo ip netns del trace-r2
sudo ip netns del trace-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Internet Host에 많은 Probe를 반복하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt; Option과 기본 Protocol은 운영체제와 구현마다 다르므로 Manual을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;traceroute는 Router의 지도를 읽는 것이 아니라 TTL 또는 Hop Limit이 다른 Probe의 만료 응답을 모아 경로를 추론한다.&lt;/li&gt;
&lt;li&gt;IPv4 Router는 Transit 중 TTL이 끝나면 ICMP Type 11 Code 0 Time Exceeded를 보낼 수 있다.&lt;/li&gt;
&lt;li&gt;Destination의 종료 응답은 UDP Port Unreachable, ICMP Echo Reply, TCP SYN-ACK 또는 RST처럼 Probe 방식에 따라 다르다.&lt;/li&gt;
&lt;li&gt;한 Hop의 여러 RTT는 같은 TTL의 별도 Probe 결과이며 서로 다른 ECMP 경로를 탈 수 있다.&lt;/li&gt;
&lt;li&gt;Hop RTT는 앞 Hop과의 Link 지연이 아니라 Source에서 해당 Router까지 갔다가 ICMP가 돌아온 왕복 시간이다.&lt;/li&gt;
&lt;li&gt;중간 Hop만 느리고 뒤 Hop이 정상이라면 해당 Router의 ICMP Control Plane 우선순위 차이일 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt;는 해당 Probe의 응답을 Timeout 안에 보지 못했다는 뜻이며 Router Down이나 Forwarding 실패를 직접 증명하지 않는다.&lt;/li&gt;
&lt;li&gt;응답하지 않는 Hop도 Transit Packet은 전달할 수 있으므로 별표 뒤의 Hop과 Destination이 다시 나타날 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP Reply는 별도 Return Route를 사용하므로 출력과 RTT에는 Asymmetric Path가 영향을 준다.&lt;/li&gt;
&lt;li&gt;ECMP와 Probe Port 변화는 한 실행에 여러 Flow의 경로를 섞을 수 있고 Paris Traceroute는 이를 줄이려 한다.&lt;/li&gt;
&lt;li&gt;UDP, ICMP와 TCP Probe는 Firewall Policy와 Hash가 달라 서로 다른 결과를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;MPLS, Tunnel, NAT와 ICMP Source Address 선택 때문에 실제 장비와 출력 Hop이 1:1로 보이지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;Reverse DNS 이름과 ICMP Address는 물리 위치와 장비 소유권의 확정적인 증명이 아니다.&lt;/li&gt;
&lt;li&gt;IPv6에서는 Hop Limit과 ICMPv6 Type 3 Code 0 Time Exceeded를 사용한다.&lt;/li&gt;
&lt;li&gt;traceroute는 원인을 확정하는 마지막 명령이 아니라 다음 Capture 지점과 비교할 Protocol을 정하는 도구다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: traceroute, tracert, TTL, Hop Limit, Probe, Hop, ICMP Time Exceeded, ICMP Type 11 Code 0, ICMPv6 Type 3 Code 0, Quoted Packet, UDP Port Unreachable, ICMP Echo Probe, TCP SYN Probe, Hop RTT, Timeout, Rate Limiting, Control Plane, Data Plane, Asymmetric Routing, ECMP, Flow Hash, Paris Traceroute, Reverse DNS, MPLS, Tunnel, Maximum Hop, mtr&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;traceroute가 보여 주는 것은 고정된 길의 사진이 아니다. &lt;br /&gt;서로 다른 수명의 Probe가 각 지점에서 돌려준 응답을 시간 순서로 이어 붙인 추론이다.&lt;br /&gt;빈칸과 RTT도 그 응답이 보이지 않거나 늦었다는 범위 안에서 해석해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;과 &lt;code&gt;traceroute&lt;/code&gt;는 Endpoint와 중간 Router가 돌려준 응답으로 Network를 간접 관찰한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세히 보려면 Interface를 지나는 실제 Frame과 Packet을 Capture해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Wireshark에 Packet이 보이기만 하면 원인을 모두 알 수 있을까? Capture 지점 밖에서 일어난 Drop, 암호화된 Payload, Offload와 누락된 Packet은 어떻게 구분할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;패킷을 직접 보면 모든 원인을 알 수 있을까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, Wireshark를 계층별 질문에 연결하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-point Evidence로 원인 범위를 줄이는 방법을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>TraceRoute</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1102</guid>
      <comments>https://peterica.tistory.com/1102#entry1102comment</comments>
      <pubDate>Mon, 27 Jul 2026 22:25:29 +0900</pubDate>
    </item>
    <item>
      <title>[바이브코딩] 03. Claude 5는 왜 Context Engineering을 이야기할까? - Prompt에서 Context로</title>
      <link>https://peterica.tistory.com/1111</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsP9ph/dJMcahrOK99/4CTPSx0bLmYKBsdiLdGKDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsP9ph/dJMcahrOK99/4CTPSx0bLmYKBsdiLdGKDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsP9ph/dJMcahrOK99/4CTPSx0bLmYKBsdiLdGKDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsP9ph%2FdJMcahrOK99%2F4CTPSx0bLmYKBsdiLdGKDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic은 &lt;a href=&quot;https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Claude 5 세대 모델을 소개&lt;/a&gt;하며 새로운 방향을 제시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 시스템 프롬프트를 대폭 줄였지만 성능 저하는 크지 않았다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 규칙을 많이 적을수록 AI가 안정적으로 동작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5는 다른 방향을 제시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;규칙보다 컨텍스트가 중요하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 AI 개발 방식이 Prompt Engineering에서 Context Engineering으로 바뀌는 이유를 정리한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이전 글에서 이야기한 Context Engineering&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Engineering은 이번에 처음 등장한 개념이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서도 프롬프트의 한계와 컨텍스트의 중요성을 여러 관점에서 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/search/Context%20Engineering&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Context Engineering 관련 글 모아보기&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Prompt Engineering에서 Context Engineering으로 발전하는 과정&lt;/li&gt;
&lt;li&gt;문서 기반 개발과 Context Engineering의 관계&lt;/li&gt;
&lt;li&gt;SSOT, MECE를 활용한 컨텍스트 구조화&lt;/li&gt;
&lt;li&gt;Garbage Context와 Context Pollution을 줄이는 방법&lt;/li&gt;
&lt;li&gt;Context를 넘어 Harness Engineering으로 발전하는 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브코딩 시리즈에서도 같은 내용을 이어서 다뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[바이브코딩] 01. AI와 일할 때 왜 문서가 필요할까?&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PRD, UX, 기술 스택, 데이터베이스, 도메인, API의 역할을 구분한다.&lt;/li&gt;
&lt;li&gt;하나의 결정은 하나의 문서에서 관리한다.&lt;/li&gt;
&lt;li&gt;SSOT와 MECE를 통해 사람과 AI가 같은 기준을 보게 한다.&lt;/li&gt;
&lt;li&gt;문서를 이전 작업의 결과이자 다음 작업의 컨텍스트로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1110&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[바이브코딩] 02. AI와 오래 협업하려면 어떻게 해야 할까?&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 대화에서는 하나의 주제에 집중한다.&lt;/li&gt;
&lt;li&gt;현재 주제에 맞는 역할과 작업 범위를 지정한다.&lt;/li&gt;
&lt;li&gt;이미 확정된 문서를 현재 작업의 기준으로 제공한다.&lt;/li&gt;
&lt;li&gt;Plan Mode에서 방향을 논의한 뒤 결정된 내용만 문서로 남긴다.&lt;/li&gt;
&lt;li&gt;완성한 문서를 다음 대화의 컨텍스트로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 글의 핵심은 명확하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 프롬프트를 반복해서 작성하는 것보다 AI가 참고할 기준을 구조화하는 일이 중요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 내가 AI와 협업하며 발견한 필요성을 중심으로 Context Engineering을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Claude 5를 만든 Anthropic이 같은 문제를 어떻게 바라보고 있는지 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Claude 5에서 달라진 것은 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5가 Context Engineering이라는 새로운 개념을 만든 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달라진 점은 모델의 능력이 발전하면서 컨텍스트를 구성하는 방법도 바뀌었다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic은 &lt;b&gt;Claude 5&lt;/b&gt; 세대 모델을 적용하며 &lt;b&gt;Claude Code의 시스템 프롬프트를 80% 이상 제거&lt;/b&gt;했다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 모델의 실수를 막기 위해 많은 규칙을 미리 넣었다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;항상 테스트를 작성한다.
코드 리뷰 절차를 따른다.
작업 후 검증한다.
문서를 임의로 변경하지 않는다.
불필요한 주석을 작성하지 않는다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 규칙은 각각 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 작업에 항상 필요한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙이 많아지면 서로 충돌하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업과 무관한 정보가 컨텍스트를 차지하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Claude 5&lt;/b&gt;에서는 모든 규칙을 앞에 넣는 대신, 모델이 주변 코드와 현재 작업을 보고 판단하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증이 필요한 순간에는 검증 절차를 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰가 필요한 순간에는 코드 리뷰 Skill을 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 작업에서는 배포 문서와 설정을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;기존 방식

모든 규칙
    &amp;darr;
모든 문서
    &amp;darr;
사용자 요청
    &amp;darr;
작업 수행
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Claude 5 방식

사용자 요청
    &amp;darr;
현재 작업 판단
    &amp;darr;
필요한 문서와 Skill 선택
    &amp;darr;
작업 수행
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 변화는 컨텍스트의 양이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트를 불러오는 시점과 구조다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 것을 제공하는 방식에서 필요한 것을 찾는 방식으로&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Context Engineering에서는 AI에게 충분한 정보를 제공하는 일이 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5에서는 여기서 한 단계 더 나아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 정보를 한 번에 제공하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 현재 작업에 필요한 정보를 선택적으로 찾게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic은 이를 Progressive Disclosure라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Progressive Disclosure는 필요한 정보를 필요한 시점에 점진적으로 공개하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 API를 구현할 때는 다음 정보를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;CLAUDE.md
    &amp;darr;
PRD
    &amp;darr;
API 문서
    &amp;darr;
Backend Convention
    &amp;darr;
관련 코드와 테스트
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포를 검증할 때는 다른 정보를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;CLAUDE.md
    &amp;darr;
Verification Skill
    &amp;darr;
배포 문서
    &amp;darr;
CI/CD 설정
    &amp;darr;
테스트 결과
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 구현에 배포 절차 전체를 넣을 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 검수에 데이터베이스 운영 규칙을 모두 넣을 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업에 필요한 컨텍스트만 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이전 글에서 이야기한 내용은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 다음 대화의 컨텍스트로 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5가 추가로 강조하는 내용은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 문서를 모두 제공하지 않고, 현재 작업에 필요한 문서만 불러온다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Engineering의 방향이 바뀐 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 방법이 더 구체적으로 발전했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 필요한 정보를 불러온다는 것은 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 정보를 불러온다는 것은 모든 문서를 항상 기억하게 만드는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 문서를 목적별로 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업과 관련된 문서만 찾아 읽게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 구성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;project/
├── CLAUDE.md
├── docs/
│   ├── product/
│   │   └── PRD.md
│   ├── architecture/
│   │   ├── SYSTEM.md
│   │   └── DATABASE.md
│   ├── development/
│   │   ├── BACKEND.md
│   │   └── FRONTEND.md
│   └── operations/
│       └── DEPLOYMENT.md
└── .claude/
    └── skills/
        ├── code-review/
        ├── document-review/
        └── release-check/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 &lt;code&gt;CLAUDE.md&lt;/code&gt;는 모든 내용을 설명하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어디에서 어떤 정보를 찾아야 하는지 알려준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ CLAUDE.md는 규칙집이 아니라 지도다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람이 &lt;code&gt;CLAUDE.md&lt;/code&gt;에 모든 규칙을 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 파일이 커지면 문제가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 작업과 무관한 정보까지 포함된다.&lt;/li&gt;
&lt;li&gt;중요한 규칙과 사소한 규칙이 섞인다.&lt;/li&gt;
&lt;li&gt;오래된 내용이 현재 기준과 충돌한다.&lt;/li&gt;
&lt;li&gt;같은 내용이 여러 문서에 중복된다.&lt;/li&gt;
&lt;li&gt;유지보수가 어려워진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5에서 &lt;code&gt;CLAUDE.md&lt;/code&gt;는 백과사전이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 지도다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;프로젝트의 목적
    &amp;darr;
공통 작업 원칙
    &amp;darr;
문서의 위치
    &amp;darr;
작업별 Skill 안내&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세 내용은 각 문서가 담당한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;CLAUDE.md
    &amp;darr;
PRD.md
    &amp;darr;
DOMAIN.md
    &amp;darr;
API.md
    &amp;darr;
DATABASE.md
    &amp;darr;
DEPLOYMENT.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;는 모든 내용을 담지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 컨텍스트로 이동하게 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 문서는 SSOT가 되어야 한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Engineering이 제대로 동작하려면 문서를 신뢰할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 요구사항이 여러 문서에 다르게 적혀 있으면 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 무엇을 기준으로 판단해야 할지 알기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 문서는 SSOT 역할을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 기준을 정한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;제품 요구사항      &amp;rarr; PRD.md
사용자 흐름        &amp;rarr; UX.md
도메인 규칙        &amp;rarr; DOMAIN.md
API 계약           &amp;rarr; API.md
데이터 구조        &amp;rarr; DATABASE.md
배포 절차          &amp;rarr; DEPLOYMENT.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 정보는 하나의 기준 문서에서 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 문서에서는 링크로 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 SSOT와 MECE 원칙과 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서가 중복되지 않아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할도 명확하게 나뉘어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 AI도 필요한 컨텍스트를 정확하게 찾는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Skill은 특정 작업을 위한 절차서다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는 프로젝트 정보를 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill은 특정 작업을 수행하는 방법을 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰 Skill에는 다음 절차를 넣을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 변경된 파일을 확인한다.
2. 관련 요구사항 문서를 읽는다.
3. 기존 코드 패턴과 비교한다.
4. 오류 가능성과 영향 범위를 확인한다.
5. 테스트 누락 여부를 검토한다.
6. 결과를 정해진 형식으로 작성한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 검증 Skill에는 다른 절차를 넣는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 변경 범위를 확인한다.
2. 테스트 결과를 확인한다.
3. 환경 변수를 점검한다.
4. 마이그레이션 여부를 확인한다.
5. 롤백 절차를 확인한다.
6. 배포 가능 여부를 판단한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 절차를 매번 프롬프트에 입력할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill로 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 순간에만 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill은 AI를 위한 표준 작업 절차서다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Prompt보다 중요한 것은 프로젝트 구조다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 장기 프로젝트를 이해하려면 한 번의 멋진 문장으로는 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 정보를 찾을 수 있는 구조가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 새로운 프로젝트에 들어가면 다음 순서로 이해한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 소개를 읽는다.&lt;/li&gt;
&lt;li&gt;시스템 구조를 본다.&lt;/li&gt;
&lt;li&gt;필요한 문서를 찾는다.&lt;/li&gt;
&lt;li&gt;관련 코드를 확인한다.&lt;/li&gt;
&lt;li&gt;작업 절차를 따른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI도 크게 다르지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 프롬프트 하나로 모든 문제를 해결하려 하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 필요한 정보를 찾아갈 수 있는 환경을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Prompt
    &amp;darr;
현재 요청을 전달한다.

Context
    &amp;darr;
요청을 이해하는 정보를 제공한다.

Structure
    &amp;darr;
정보를 찾을 수 있게 한다.

Skill
    &amp;darr;
작업 수행 절차를 제공한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Claude 5는 하나의 모델이 아니다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic의 글 제목은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;The New Rules of Context Engineering for Claude 5 Generation Models&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 나는 &quot;Claude 5&quot;를 하나의 모델 이름으로 이해하였다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 Anthropic이 말하는 Claude 5는 특정 모델 하나가 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5세대 모델군(Generation Models)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 의미한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 기준으로는 다음 모델이 여기에 포함된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sonnet 5&lt;/li&gt;
&lt;li&gt;Fable 5&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, Context Engineering에 대한 새로운 권장 사항은 Claude 5 세대 전체를 대상으로 설명하는 내용이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 Opus 4.8과 Haiku 4.5는 각각 다른 계열의 모델이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 이 모델들도 Context Engineering을 활용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 Anthropic이 이야기하는 새로운 설계 철학은 Claude 5 세대를 중심으로 설명된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 Claude Code에서 모델을 선택하면 다음과 같은 목록을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Sonnet 5
Fable 5
Opus 4.8
Haiku 4.5
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 Sonnet 5와 Fable 5가 같은 세대에 속한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Claude 5에서는 모델 선택도 영향을 줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context Engineering 원칙은 &lt;b&gt;Claude 5&lt;/b&gt; 세대 모델을 중심으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 기준으로 Sonnet 5와 Fable 5가 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델마다 긴 작업과 컨텍스트 탐색 능력에 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sonnet 5는 일반 개발과 문서 작업에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 수정과 일상적인 작업에 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fable 5는 많은 문서와 코드를 장시간 검토하는 작업에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 리팩터링과 긴 작업에 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Opus 4.8과 Haiku 4.5도 컨텍스트를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Claude 5의 새로운 작업 방식은 Sonnet 5와 Fable 5를 중심으로 이해하는 것이 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 좋아도 문서 구조는 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델의 능력이 좋아질수록 규칙을 더 많이 줄 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;판단에 필요한 근거와 구조를 제공해야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI와 오래 협업하려면 무엇이 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 한두 번 대화하는 것은 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 프로젝트에서 며칠이나 몇 달 동안 협업하는 것은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안정적인 작업 흐름이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Plan
    &amp;darr;
작업 범위와 방향을 정한다.

Document
    &amp;darr;
결정한 내용을 문서에 반영한다.

Implement
    &amp;darr;
문서를 기준으로 구현한다.

Verify
    &amp;darr;
구현 결과를 검증한다.

Update
    &amp;darr;
변경 내용을 다시 문서에 반영한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 문서는 단순한 기록이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람과 AI가 함께 보는 기준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화에서 결정한 내용을 문서에 반영하지 않으면 다음 세션에서 다시 설명해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서로 남기면 모델이나 세션이 바뀌어도 작업을 이어갈 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 앞으로의 AI 개발자는 무엇을 잘해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 코드를 많이 작성하는 사람이 생산성이 높았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성형 AI가 등장한 뒤에는 좋은 프롬프트 작성 능력이 중요해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 프로젝트 구조와 컨텍스트 설계 능력이 더 중요해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 역할은 단순히 명령을 작성하는 것이 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;판단 기준을 정한다.&lt;/li&gt;
&lt;li&gt;정보를 기록할 위치를 정한다.&lt;/li&gt;
&lt;li&gt;문서 사이의 관계를 설계한다.&lt;/li&gt;
&lt;li&gt;작업 절차를 정의한다.&lt;/li&gt;
&lt;li&gt;검증 기준을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prompt Engineer를 넘어 Context Architect의 역할이 필요하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude 5가 말하는 Context Engineering은 새로운 프롬프트 작성법이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 일하는 방식의 변화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 AI에게 더 많은 규칙을 주는 것이 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 AI가 필요한 정보를 찾고 판단할 수 있도록 프로젝트를 구조화해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;는 거대한 규칙집이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 지도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD, UX, 도메인, 데이터베이스, API 문서는 각각 SSOT 역할을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰, 문서 검수, 배포 확인 같은 반복 작업은 Skill로 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 AI 협업은 좋은 프롬프트 하나에서 시작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 정리된 문서와 프로젝트 구조에서 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 순간에 적절한 정보를 제공하는 컨텍스트 설계가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 글에서 이야기한 SSOT, MECE, Plan Mode, 문서화는 독립된 개념이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 AI와 오래 협업하기 위한 Context Engineering의 구성 요소다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 참고 자료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Anthropic, The New Rules of Context Engineering for Claude 5 Generation Models&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[바이브코딩] 01. AI와 일할 때 왜 문서가 필요할까? - SSOT, MECE, 컨텍스트&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1110&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;[바이브코딩] 02. AI와 오래 협업하려면 어떻게 해야 할까? - Plan Mode, 문서화, 컨텍스트&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/바이브코딩</category>
      <category>Claude 5</category>
      <category>context Engineer</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1111</guid>
      <comments>https://peterica.tistory.com/1111#entry1111comment</comments>
      <pubDate>Mon, 27 Jul 2026 20:28:56 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 24. ping이 실패하면 정말 서버가 죽은 것일까?</title>
      <link>https://peterica.tistory.com/1101</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6GbB/dJMcaiD8gLl/4PrpBD7lmS5r6VXcrKk630/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6GbB/dJMcaiD8gLl/4PrpBD7lmS5r6VXcrKk630/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6GbB/dJMcaiD8gLl/4PrpBD7lmS5r6VXcrKk630/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6GbB%2FdJMcaiD8gLl%2F4PrpBD7lmS5r6VXcrKk630%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 8. 편지가 멈춘 곳을 찾는 법&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에 접속되지 않을 때 가장 먼저 떠올리는 명령이 있다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;ping server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답이 오면 &amp;ldquo;Network는 정상&amp;rdquo;이라고 말하고, 응답이 없으면 &amp;ldquo;Server가 죽었다&amp;rdquo;고 말하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;ping&lt;/code&gt;이 묻는 질문은 그보다 좁다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;이 Destination을 향한 ICMP Echo Request가 전달되고
누군가 Echo Reply를 만들며
그 Reply가 다시 이 Host까지 돌아올 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP Port가 열려 있는지, TLS Certificate가 유효한지, Application이 요청을 처리할 수 있는지는 묻지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Echo Reply가 없다고 Destination Host가 반드시 꺼져 있는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall이 ICMP만 차단하거나 Reply의 Return Path가 끊겼을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진단 도구의 결과를 정확히 쓰려면 &amp;ldquo;무엇을 증명했는가?&amp;rdquo;와 &amp;ldquo;무엇은 아직 모르는가?&amp;rdquo;를 함께 적어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ICMP Echo Request/Reply&lt;/b&gt;, &lt;b&gt;Identifier&lt;/b&gt;, &lt;b&gt;Sequence Number&lt;/b&gt;, &lt;b&gt;Round-Trip Time(RTT)&lt;/b&gt;, &lt;b&gt;Packet Loss&lt;/b&gt;, &lt;b&gt;TTL/Hop Limit&lt;/b&gt;, &lt;b&gt;Destination Unreachable&lt;/b&gt;을 따라가며 &lt;code&gt;ping&lt;/code&gt;의 관찰 범위를 정리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping은 어느 Protocol로 대화할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 &lt;code&gt;ping&lt;/code&gt;은 &lt;b&gt;Internet Control Message Protocol&lt;/b&gt;(ICMP)의 Echo Message를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4에서는 다음 Type과 Code를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Echo Request
Type 8, Code 0

Echo Reply
Type 0, Code 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 ICMPv6의 Type이 다르다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMPv6 Echo Request
Type 128, Code 0

ICMPv6 Echo Reply
Type 129, Code 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP나 UDP처럼 Destination Port를 지정하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 198.51.100.20:443  (X)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;은 Host의 특정 Web Service가 아니라 IP 계층의 ICMP 응답 가능성을 관찰한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Echo Message에는 무엇이 들어 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Echo Message의 대표적인 Field는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Type
Code
Checksum
Identifier
Sequence Number
Data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Identifier&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Host에서 여러 &lt;code&gt;ping&lt;/code&gt; Process가 동시에 실행될 때 Request와 Reply를 구분하는 단서로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제와 구현에 따라 Process ID나 별도 값에서 정할 수 있지만 항상 Process ID와 같다고 단정할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Sequence Number&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Echo Request의 순서를 표시한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;icmp_seq=1
icmp_seq=2
icmp_seq=3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 Request의 Reply가 사라졌거나 순서가 바뀌었는지 확인하는 데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Sequence Number처럼 Byte Stream의 위치를 나타내는 번호는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Data&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기와 Pattern을 조절할 수 있는 Payload다. 일부 구현은 전송 시각을 넣어 RTT 계산에 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Checksum&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송 오류를 탐지한다. 공격자가 다시 계산할 수 있으므로 TLS 같은 암호학적 Integrity나 상대 Authentication을 제공하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Hostname을 ping하면 ICMP부터 보낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping server.example&lt;/code&gt;처럼 이름을 사용하면 먼저 이름을 IP Address로 바꿔야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Resolver가 server.example 조회
2. A 또는 AAAA Address 선택
3. 선택한 Address로 ICMP Echo Request 전송&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS가 실패하면 Echo Request 자체를 한 번도 보내지 못한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ping: unknown host
ping: cannot resolve&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과는 Destination이 ICMP에 응답하지 않았다는 뜻이 아니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Hostname ping 실패
&amp;rarr; DNS 단계 또는 Address 선택 문제 가능

IP Address ping 성공
&amp;rarr; ICMP 경로는 되지만 이름 조회 문제 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진단할 때 이름과 IP를 나누어 시험하는 이유다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;dig server.example
ping 198.51.100.20
ping server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령과 Option은 운영체제에 따라 다를 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Echo Request가 나가기 전에도 여러 단계가 필요하다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination Address를 알았다고 Packet이 즉시 Wire로 나가는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host는 다음 과정을 거친다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Destination이 같은 Subnet인가?
        &amp;darr;
Routing Table에서 Route 선택
        &amp;darr;
Outgoing Interface와 Next Hop 결정
        &amp;darr;
ARP 또는 NDP로 Next-Hop Link Address 확인
        &amp;darr;
ICMP Echo Request를 Frame에 실어 전송&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;ping&lt;/code&gt; 실패는 Destination Host 하나만의 문제가 아닐 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Local Interface가 Down&lt;/li&gt;
&lt;li&gt;Address나 Subnet Mask 오류&lt;/li&gt;
&lt;li&gt;Route 없음&lt;/li&gt;
&lt;li&gt;Default Gateway 오류&lt;/li&gt;
&lt;li&gt;ARP/NDP 실패&lt;/li&gt;
&lt;li&gt;Local Firewall의 Outbound Drop&lt;/li&gt;
&lt;li&gt;Network Namespace나 VRF를 잘못 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Request가 Local Host를 실제로 떠났는지 Capture로 확인해야 원격 Host를 의심할 근거가 생긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Echo Reply가 오려면 어떤 길이 완성되어야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Request는 Forward Path를 지나 Destination에 도착한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination IP Stack이 Echo Reply를 만들면 Reply는 Return Path를 따라 돌아온다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Source
  │ Echo Request
  ▼
Forward Path
  │
  ▼
Destination
  │ Echo Reply
  ▼
Return Path
  │
  ▼
Source&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Path가 정상이어도 Return Route가 없으면 Source는 Reply를 받지 못한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Request는 Destination 도착
Reply는 Return Path에서 Drop
Source에서 본 결과: Timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽에서 응답이 없다는 사실만으로 어느 방향이 끊겼는지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양 Endpoint와 중간 지점의 Capture가 필요한 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping 성공은 정확히 무엇을 증명할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; Reply 하나를 받았다면 최소한 다음 사실의 증거가 생긴다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;선택한 Source에서 Echo Request를 보낼 수 있었다.
Request가 해당 Address에 응답하는 지점까지 도달했다.
그 지점이 Echo Reply를 생성했다.
Reply가 Source까지 돌아왔다.
해당 시점의 ICMP 왕복이 완료됐다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 다음까지 증명하지는 않는다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;TCP 443이 열려 있다.
TLS Handshake가 성공한다.
Certificate가 유효하다.
HTTP가 200을 응답한다.
Database와 Dependency가 정상이다.
사용자 요청의 업무 처리가 성공한다.
다음 Packet도 반드시 같은 경로로 성공한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; 성공은 &lt;b&gt;ICMP Reachability의 긍정적인 증거&lt;/b&gt;이지 모든 Layer의 Health Check가 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 누가 Echo Reply를 보냈는지도 항상 단순할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination IP가 하나의 물리 Server만을 가리킨다고 보장할 수 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Anycast Address를 여러 지점이 광고할 수 있다.&lt;/li&gt;
&lt;li&gt;Load Balancer나 Network Appliance가 Address를 소유할 수 있다.&lt;/li&gt;
&lt;li&gt;High Availability 전환으로 응답 장비가 바뀔 수 있다.&lt;/li&gt;
&lt;li&gt;NAT와 Tunnel 때문에 실제 Application Server와 ICMP 응답 지점이 다를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;VIP가 ping에 응답
&amp;ne; 뒤의 모든 Backend가 정상&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Reply의 Source IP와 Capture 위치, 실제 Service Architecture를 함께 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Echo 자체에는 TLS Certificate 같은 암호학적 상대 인증이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Packet을 위조할 수 있는 환경에서는 Reply를 받았다는 사실만으로 응답 주체의 강한 Identity까지 증명하지 못한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping 실패는 무엇을 의미할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Reply를 받지 못했다면 다음 중 하나 이상일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Echo Request를 보내지 못함
Echo Request가 Forward Path에서 Drop
Destination이 Echo Request를 무시
Destination가 Down
Echo Reply가 Return Path에서 Drop
ICMP가 Rate Limit
응답이 Timeout보다 늦게 도착
Packet 크기가 Path MTU 문제를 만남&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; 실패가 증명하는 것은 좁다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정해진 시간과 조건 안에서 기대한 Echo Reply를 관찰하지 못했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Server Down&amp;rdquo;은 가능한 가설 중 하나일 뿐이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Firewall이 ICMP만 막을 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall은 Protocol과 ICMP Type별로 다른 Policy를 적용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TCP 443                 ALLOW
ICMP Echo Request       DROP
ICMP Fragmentation Needed ALLOW
ICMP Time Exceeded      Rate Limit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 환경에서는 HTTPS가 정상이어도 &lt;code&gt;ping&lt;/code&gt;은 실패할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;ping 실패
curl 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 ICMP Echo를 허용하고 TCP 443을 차단할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;ping 성공
curl Timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;ICMP 허용 여부&amp;rdquo;와 &amp;ldquo;Application Service 허용 여부&amp;rdquo;는 별도의 Firewall Rule이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ICMP를 모두 막는 것이 안전할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP에는 Echo 외에도 IP 전달에 필요한 오류와 제어 메시지가 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Destination Unreachable
Fragmentation Needed
Time Exceeded
Parameter Problem&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 Neighbor Discovery와 Packet Too Big 같은 ICMPv6 기능이 정상 동작에 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Request에 응답하지 않겠다는 Policy와 모든 ICMP를 Drop하는 Policy는 다르다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ICMP Echo를 제한
&amp;ne; ICMP 전체를 차단&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type, Code, Direction, Rate와 관련 Connection State를 기준으로 필요한 Control Traffic을 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping 출력의 한 줄은 무엇을 뜻할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 계열의 설명용 출력은 다음처럼 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;64 bytes from 198.51.100.20: icmp_seq=1 ttl=52 time=18.4 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항목을 나누어 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;64 bytes
&amp;rarr; 구현이 보고한 ICMP Message 크기

from 198.51.100.20
&amp;rarr; Echo Reply의 Source Address

icmp_seq=1
&amp;rarr; 어느 Echo Request에 대한 Reply인지 나타내는 Sequence

ttl=52
&amp;rarr; Reply가 Source에 도착했을 때 남은 IPv4 TTL

time=18.4 ms
&amp;rarr; Request 전송부터 Reply 수신까지 측정한 RTT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 형식과 Byte 계산은 구현마다 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Linux의 일반적인 기본값에서 ICMP Data 56바이트에 ICMP Header 8바이트를 더해 &lt;code&gt;64 bytes&lt;/code&gt;라고 표시할 수 있고, IPv4 Header 20바이트까지 포함한 IP Packet은 84바이트가 될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;ICMP Data   56
ICMP Header  8
IPv4 Header 20
----------------
IP Total    84&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet Frame 전체 크기는 여기에 Link Layer Header와 Trailer가 더 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RTT는 어디까지의 시간일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Round-Trip Time&lt;/b&gt;(RTT)은 Source에서 Request를 보내고 Reply를 받을 때까지의 왕복 시간이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;RTT
= Forward Path 전송과 Queue 대기
+ Destination 처리 시간
+ Return Path 전송과 Queue 대기
+ 양 Endpoint의 Scheduling 영향&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTT 20ms라고 Forward와 Return이 각각 10ms였다고 단정할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Forward  3ms + Return 17ms = RTT 20ms
Forward 15ms + Return  5ms = RTT 20ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 &lt;code&gt;ping&lt;/code&gt;의 RTT만으로 One-Way Delay와 어느 방향의 지연인지 분리할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One-Way Delay를 정확히 비교하려면 양 Endpoint의 Clock Synchronization과 별도 측정 설계가 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 첫 번째 Reply만 느리면 Network가 불안정할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령 실행 후 첫 결과가 나오기 전에는 다음 준비가 추가로 필요할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS Cache Miss&lt;/li&gt;
&lt;li&gt;ARP 또는 NDP Neighbor Resolution&lt;/li&gt;
&lt;li&gt;Route와 Neighbor Cache 생성&lt;/li&gt;
&lt;li&gt;무선 절전 상태 해제&lt;/li&gt;
&lt;li&gt;방화벽이나 Tunnel의 초기 State 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS Resolution 시간은 일반적으로 각 Echo Reply 줄의 &lt;code&gt;time=&lt;/code&gt;으로 표시되는 ICMP RTT에 포함되지 않지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 명령을 실행한 뒤 첫 결과를 보기까지의 시간에는 영향을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 Echo Request 자체는 ARP/NDP와 State 준비 때문에 대기할 수 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 어느 시간이 RTT에 포함되는지는 구현과 측정 지점에 따라 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Packet은 이미 만들어진 Cache와 State를 사용할 수 있어 더 빨라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;icmp_seq=1 time=25 ms
icmp_seq=2 time=3 ms
icmp_seq=3 time=3 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 값 하나만으로 지속적인 Network 지연이라고 결론 내리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 첫 Packet을 항상 버리고 나머지만 평균 내면 실제 사용자가 겪는 Connection Setup 비용을 놓칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 목적에 따라 Cold Start와 Steady State를 구분한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Loss는 어떻게 계산할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;은 보낸 Echo Request 수와 받은 Echo Reply 수로 Loss 비율을 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;5 packets transmitted
4 packets received

Packet Loss = (5 - 4) &amp;divide; 5 &amp;times; 100
            = 20%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과는 &lt;b&gt;보낸 ICMP Echo Sample 중 Reply를 받지 못한 비율&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 시간의 TCP Application Packet도 정확히 20% 손실되었다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router와 Host는 Control Plane 보호를 위해 ICMP Echo를 Rate Limit하거나 낮은 우선순위로 처리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Echo Loss 20%
TCP Service Traffic 정상&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 장면이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 ICMP는 잘 통과하지만 특정 Queue, DSCP Class, Tunnel 또는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Packet에서 Application Traffic만 손실될 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 평균 RTT만 보면 충분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명용 통계가 다음과 같다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;min/avg/max/mdev = 10.1/20.0/80.2/15.4 ms&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;min&lt;/code&gt;: 관찰한 Sample 중 가장 작은 RTT&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avg&lt;/code&gt;: 산술 평균 RTT&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max&lt;/code&gt;: 가장 큰 RTT&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mdev&lt;/code&gt; 또는 &lt;code&gt;stddev&lt;/code&gt;: RTT 변동 폭을 나타내는 통계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균이 같아도 사용자 경험은 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;경로 A: 19, 20, 21ms
경로 B: 5, 5, 50ms

평균은 비슷할 수 있지만 변동성은 다름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 음성과 Game은 평균 RTT뿐 아니라 변동, 흔히 &lt;b&gt;Jitter&lt;/b&gt;라고 부르는 현상과 Loss에 민감하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;code&gt;ping&lt;/code&gt; 출력의 &lt;code&gt;mdev&lt;/code&gt;와 Application이 정의하는 Jitter 지표가 항상 같은 계산은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample 수와 간격, 크기, 시간대를 함께 기록해야 비교가 의미 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Reply의 TTL로 Router 수를 정확히 알 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력의 &lt;code&gt;ttl=52&lt;/code&gt;는 Reply가 Source에 도착했을 때 남은 TTL이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination이 처음 설정한 Initial TTL을 알면 지나온 Hop 수를 추정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;가정한 Initial TTL 64
- 도착 TTL 52
= 약 12 Hop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Initial TTL은 운영체제와 설정에 따라 64, 128, 255 등 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Tunnel과 장비가 TTL 처리에 영향을 줄 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;도착 TTL만 알고 Initial TTL은 모름
&amp;rarr; 정확한 Hop 수를 확정할 수 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL 값으로 운영체제나 경로 길이를 추측할 수는 있지만 확정적인 Identity 증명으로 사용하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Hop을 관찰하려는 질문은 다음 글의 &lt;code&gt;traceroute&lt;/code&gt;가 더 직접적으로 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Sequence가 빠지거나 순서가 바뀌면 무엇을 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Reply를 받았다고 하자.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;icmp_seq=1
icmp_seq=3
icmp_seq=2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번이 늦게 도착해 Reply 순서가 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 원인은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECMP나 경로 변화&lt;/li&gt;
&lt;li&gt;서로 다른 Queue 지연&lt;/li&gt;
&lt;li&gt;Wireless 재전송&lt;/li&gt;
&lt;li&gt;Endpoint Scheduling&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Sequence의 Reply가 여러 번 오면 Duplicate Packet이나 중복 응답일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번의 Reordering만으로 Routing Loop나 심각한 장애를 단정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 IP ID, ICMP Identifier, Sequence, 시간과 경로 변화를 함께 본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Timeout과 Unreachable은 같은 실패일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답이 전혀 없으면 &lt;code&gt;ping&lt;/code&gt;은 Timeout처럼 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Request timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Request 또는 Reply가 사라졌다는 뜻일 뿐 Drop 위치와 이유를 알려 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Host나 Router가 명시적인 ICMP 오류를 돌려줄 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Destination Network Unreachable
Destination Host Unreachable
Communication Administratively Prohibited&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 문장만 보지 말고 &lt;b&gt;누가 ICMP 오류를 보냈는지&lt;/b&gt; 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Local Host가 Network Unreachable 생성
&amp;rarr; Local Route 문제 가능

First-Hop Router가 Host Unreachable 생성
&amp;rarr; 다음 Hop의 ARP 실패나 Route 문제 가능

중간 Firewall이 Administratively Prohibited 생성
&amp;rarr; 해당 정책 지점에서 거절 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 오류도 잘못 생성되거나 중간에서 바뀌고 차단될 수 있으므로 Source Address와 인용된 원래 Packet을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적인 오류는 Silent Timeout보다 원인 범위를 줄이는 강한 단서지만 최종 판결은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Destination Host Unreachable이면 Destination이 답했을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문장은 실제 Destination Host가 보냈다는 뜻이 아닐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Subnet의 Destination MAC Address를 찾지 못한 Source가 오류를 만들 수도 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Router가 다음 Hop으로 전달하지 못해 만들 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;From 10.22.1.1 Destination Host Unreachable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 &lt;code&gt;10.22.1.1&lt;/code&gt;이 오류 생성 지점이라는 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 ping 대상과 오류 메시지의 Source를 구분해서 읽어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv4와 IPv6 중 어느 쪽을 시험했을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hostname에 A와 AAAA Record가 모두 있으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; 구현과 운영체제 정책에 따라 IPv4 또는 IPv6 Address를 선택할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IPv4 ping 성공
IPv6 ping 실패

또는

IPv6 ping 성공
IPv4 ping 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽 결과로 Dual-Stack 전체를 판단하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에 따라 다음처럼 Address Family를 명시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ping -4 server.example
ping -6 server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 운영체제는 &lt;code&gt;ping6&lt;/code&gt; 같은 별도 명령을 사용하며 Option이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 Link-Local Address는 어느 Interface의 Scope인지 함께 지정해야 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;fe80::1%eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Address 문자열이라도 Source Address, Interface와 Network Namespace가 다르면 경로가 달라진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Source Address를 바꾸면 결과가 달라질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface가 여러 개인 Host는 Destination Route에 따라 Source Address와 Outgoing Interface를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall, Policy Routing과 Return Route는 선택한 Source에 따라 다르게 동작할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source 10.0.0.10 &amp;rarr; 성공
Source 192.168.0.10 &amp;rarr; Return Route 없어 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 계열에서는 환경에 따라 &lt;code&gt;-I&lt;/code&gt;로 Interface나 Source Address를 지정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ping -I eth0 198.51.100.20
ping -I 10.0.0.10 198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한과 Option은 구현마다 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Container, Pod, VRF와 Network Namespace 안에서 발생한 장애라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Shell이 아니라 실제 Traffic이 시작되는 Network Context에서 시험해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Host에서 ping 성공
&amp;ne; Container에서도 같은 Route와 Policy로 성공&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작은 ping은 되는데 큰 ping만 실패할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Echo Data 크기를 늘리면 IP Packet 전체 크기도 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet MTU 1500, IPv4 Header 20바이트, ICMP Header 8바이트를 가정하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;ping Data 1472
+ ICMP Header 8
+ IPv4 Header 20
= IP Packet 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DF가 설정된 1501바이트 Packet은 PMTU 1500 경로에서 전달되지 못한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;기본 크기 ping 성공
큰 DF ping 실패
&amp;rarr; Path MTU와 ICMP Fragmentation Needed 경로 조사&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 설명용 명령은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping -M do -c 2 -s 1472 198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-M&lt;/code&gt;과 &lt;code&gt;-s&lt;/code&gt;의 의미는 운영체제마다 다르므로 해당 시스템의 Manual을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Ping 실패가 곧 일반적인 작은 TCP Segment 실패는 아니지만 MTU Black Hole 가설을 시험하는 단서가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping이 느리면 Application도 같은 만큼 느릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP RTT는 Network 지연의 한 Sample이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 응답 시간에는 더 많은 단계가 포함된다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;DNS Resolution
+ TCP/QUIC Handshake
+ TLS Handshake
+ Request 전송
+ Server Queue와 업무 처리
+ Database와 외부 API
+ Response 전송
= 사용자가 느끼는 전체 시간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Network 장비가 ICMP를 낮은 우선순위로 처리하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; RTT만 높고 Application Traffic은 정상일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service Latency를 측정하려면 실제 Protocol과 요청을 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;curl -v --connect-timeout 3 https://server.example/
openssl s_client -connect server.example:443 -servername server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP와 Application 측정을 경쟁시키는 것이 아니라 서로 다른 계층의 증거로 조합한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어디부터 ping해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무작정 먼 Destination부터 시험하기보다 범위를 단계적으로 넓힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1. Loopback&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 127.0.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local IP Stack의 기본 동작을 본다. 성공해도 NIC와 외부 Network는 지나지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2. 자신의 Interface Address&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 10.22.1.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Address 설정을 확인한다. 이 역시 Packet이 실제 Link를 왕복했다고 보장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 3. 같은 Link의 Default Gateway&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 10.22.1.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Interface, VLAN, ARP/NDP와 첫 Hop까지의 단서를 얻는다. Gateway가 Echo를 차단하면 실패할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4. Remote Address&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 198.51.100.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing된 ICMP 왕복을 시험한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 5. Hostname&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;ping server.example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS Resolution과 선택된 Address까지 포함해 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계의 성공과 실패 경계가 원인 범위를 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 중간 장비가 ICMP를 차단하면 그 경계가 실제 Data Path 장애 위치와 같지는 않을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Service 상태는 무엇으로 확인할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 다음에는 실제 Service와 같은 Protocol을 시험한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;질문: TCP Port가 열려 있는가?
도구: nc, telnet, TCP SYN 관찰

질문: TLS가 성공하는가?
도구: openssl s_client, curl -v

질문: HTTP가 정상 응답하는가?
도구: curl, Application Health Endpoint

질문: 업무 기능이 정상인가?
도구: 실제 Transaction과 Server Log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux와 macOS의 &lt;code&gt;nc&lt;/code&gt; Option은 구현마다 다르지만 다음처럼 TCP 연결을 시험할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -v -w 3 198.51.100.20 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 성공은 TLS와 HTTP 성공을 보장하지 않는다. 계층마다 다음 질문으로 한 단계씩 올라간다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 진단 결과를 어떻게 문장으로 써야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋지 않은 결론은 관찰보다 범위가 넓다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ping 실패
&amp;rarr; Server Down이다.  (근거 부족)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증거 범위에 맞는 문장은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;10:30부터 10:35까지 Client A에서 198.51.100.20으로
IPv4 ICMP Echo Request 20개를 보냈지만 Echo Reply를 받지 못했다.
같은 시각 TCP 443 연결은 성공했다.
따라서 Host 전체 장애보다 ICMP Policy 또는 Echo 처리 차이를 우선 조사한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예시다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Echo Reply는 RTT 3ms로 돌아왔다.
TCP 443 SYN에는 응답이 없었다.
따라서 IP/ICMP 왕복 경로는 확인했지만 TCP 443 Policy와 Service 상태는 미확인이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구 이름이 아니라 Source, Destination, Address Family, Protocol, 시간과 관찰 결과를 기록한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Monitoring에서 ping 하나만 사용하면 무엇을 놓칠까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Monitor는 가볍고 많은 Host의 기본 Reachability와 RTT 변화를 보기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음 장애를 놓칠 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Web Process Down&lt;/li&gt;
&lt;li&gt;TLS Certificate 만료&lt;/li&gt;
&lt;li&gt;Database Dependency 장애&lt;/li&gt;
&lt;li&gt;특정 API의 권한 또는 업무 오류&lt;/li&gt;
&lt;li&gt;큰 Response에서만 발생하는 MTU 문제&lt;/li&gt;
&lt;li&gt;DNS가 잘못된 Address를 반환하는 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Echo Rate Limit 때문에 Service가 정상인데 ICMP Monitor만 Alarm을 낼 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Check
+ TCP Port Check
+ TLS Certificate Check
+ HTTP Health Check
+ 실제 업무 Synthetic Transaction&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Layer의 Monitor를 조합하고 각 Alarm이 의미하는 범위를 분리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping을 많이 보내면 더 정확해질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sample이 늘면 일시적인 변동과 지속적인 현상을 구분하는 데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 높은 빈도와 큰 Payload로 무제한 Probe를 보내면 Network와 Destination의 Control Plane에 부담을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping -f&lt;/code&gt; 같은 Flood Option은 권한이 필요할 수 있으며 운영 Network와 허가받지 않은 Host에서 사용하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰 목적에 맞는 간격, 횟수, Payload를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;진단 목적: 짧은 구간에서 제한된 Sample
Monitoring : 합의된 주기와 Rate Limit
Load Test  : 별도 승인과 전용 도구&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Rate Limit이 있는 환경에서는 Probe 빈도를 높일수록 오히려 인위적인 Loss를 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Message와 관찰 범위로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;답장을 요청하는 빈 엽서&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Echo Request&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IPv4 Type 8 또는 ICMPv6 Type 128의 도달성 Probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;받은 엽서를 되돌려 보냄&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Echo Reply&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IPv4 Type 0 또는 ICMPv6 Type 129의 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여러 시험 엽서를 구분하는 접수표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Identifier&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;같은 ping Session의 Request와 Reply를 연결하는 단서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;몇 번째 시험인지 적은 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Sequence Number&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Loss, Reordering과 Duplicate Reply를 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보내고 답장을 받기까지 걸린 시간&lt;/td&gt;
&lt;td&gt;&lt;b&gt;RTT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Forward와 Return Path를 합친 왕복 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보냈지만 돌아오지 않은 엽서 비율&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Packet Loss&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;관찰한 Echo Sample 중 Reply가 없던 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;답장에 남은 중계 횟수&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Reply TTL / Hop Limit&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Reply가 Source에 도착했을 때 남은 수명 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배달할 수 없다는 반송 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Destination Unreachable&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Host나 Router가 전달 실패 이유를 알리는 오류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;답장을 하지 않는 접수 정책&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Filtering / Rate Limiting&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Service 상태와 무관하게 Echo 응답을 제한할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작은 엽서는 되지만 큰 엽서는 실패&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Path MTU 문제&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Packet 크기에 따라 전달 결과가 달라짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; 결과의 논리적 범위를 한 그림으로 모으면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Echo Reply 수신
        &amp;darr;
해당 Source&amp;middot;Destination&amp;middot;Address Family에서
그 시점의 ICMP 왕복은 확인
        &amp;darr;
TCP Port, TLS, HTTP, 업무 Health는 별도 확인

Echo Reply 미수신
        &amp;darr;
정해진 조건에서 Reply를 관찰하지 못함
        &amp;darr;
Local 전송, Forward Path, ICMP Policy,
Destination, Return Path 중 어디인지는 추가 증거 필요&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서 직접 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Echo Message를 보는 Wireshark Display Filter는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;icmp.type == 8 || icmp.type == 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMPv6 Echo는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;icmpv6.type == 128 || icmpv6.type == 129&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command Line에서는 Interface를 지정해 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;sudo tcpdump -n -vv -i eth0 'icmp or icmp6'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Field를 비교한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source / Destination IP
ICMP Type / Code
Identifier
Sequence Number
IPv4 TTL 또는 IPv6 Hop Limit
Packet Length
Request와 Reply의 Timestamp 차이&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source에서 Request만 보이고 Reply가 없다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source가 Packet을 보냈다는 증거는 생겼지만 Destination 도착 여부는 아직 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination Capture에서 Request가 보이면 Forward Path는 좁혀지고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reply도 보냈는데 Source에 없으면 Return Path를 조사한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 두 반례를 만들어 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;python3&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;이 있는 개인 Linux VM에서 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Host Firewall이 아니라 새 Network Namespace 안에서만 ICMP를 차단한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping-client                ping-router               ping-server
10.24.1.2 ────────── 10.24.1.1 | 198.51.100.1 ──── 198.51.100.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add ping-client
sudo ip netns add ping-router
sudo ip netns add ping-server

sudo ip link add veth-pc type veth peer name veth-pri
sudo ip link add veth-pro type veth peer name veth-ps

sudo ip link set veth-pc netns ping-client
sudo ip link set veth-pri netns ping-router
sudo ip link set veth-pro netns ping-router
sudo ip link set veth-ps netns ping-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n ping-client addr add 10.24.1.2/24 dev veth-pc
sudo ip -n ping-router addr add 10.24.1.1/24 dev veth-pri
sudo ip -n ping-router addr add 198.51.100.1/24 dev veth-pro
sudo ip -n ping-server addr add 198.51.100.2/24 dev veth-ps

sudo ip -n ping-client link set lo up
sudo ip -n ping-router link set lo up
sudo ip -n ping-server link set lo up
sudo ip -n ping-client link set veth-pc up
sudo ip -n ping-router link set veth-pri up
sudo ip -n ping-router link set veth-pro up
sudo ip -n ping-server link set veth-ps up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 IPv4 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n ping-client route add default via 10.24.1.1
sudo ip -n ping-server route add default via 198.51.100.1
sudo ip netns exec ping-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router Capture Terminal에서 ICMP와 HTTP Traffic을 저장한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec ping-router \
  tcpdump -n -vv -i any -s 0 -w /tmp/ping-scope.pcap \
  'icmp or tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 간단한 HTTP Server를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec ping-server \
  python3 -m http.server 8080 --bind 198.51.100.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 상태에서 Client가 ICMP와 HTTP를 각각 시험한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo ip netns exec ping-client \
  ping -c 2 198.51.100.2

sudo ip netns exec ping-client \
  curl --connect-timeout 2 http://198.51.100.2:8080/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 요청이 모두 성공하는지 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping 실패와 Service 성공을 만들어 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 Client에서 Server로 가는 ICMP Echo Request만 Drop하게 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec ping-router \
  iptables -I FORWARD 1 \
  -i veth-pri -o veth-pro \
  -p icmp --icmp-type echo-request -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 두 명령을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo ip netns exec ping-client \
  ping -c 2 -W 1 198.51.100.2

sudo ip netns exec ping-client \
  curl --connect-timeout 2 http://198.51.100.2:8080/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 관찰은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;ping: Echo Reply 없음
curl: HTTP Response 수신&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec ping-router \
  iptables -L FORWARD -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Drop Rule을 제거한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec ping-router \
  iptables -D FORWARD \
  -i veth-pri -o veth-pro \
  -p icmp --icmp-type echo-request -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ping 성공과 Service 실패를 만들어 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal의 &lt;code&gt;python3 -m http.server&lt;/code&gt;를 &lt;code&gt;Ctrl+C&lt;/code&gt;로 종료한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 다시 시험한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo ip netns exec ping-client \
  ping -c 2 198.51.100.2

sudo ip netns exec ping-client \
  curl --connect-timeout 2 http://198.51.100.2:8080/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 관찰은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ping: Echo Reply 수신
curl: TCP RST 또는 Connection Refused&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host의 IP Stack과 ICMP는 동작하지만 TCP 8080에 Listening Socket이 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 Echo Reply와 TCP SYN 뒤의 RST를 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다. 내부 Firewall Rule과 가상 Interface도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del ping-client
sudo ip netns del ping-router
sudo ip netns del ping-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping -W&lt;/code&gt; Option의 단위와 지원 여부는 구현마다 다르다. 실제 운영 Network에 ICMP 차단 Rule을 추가하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IPv4 ping은 ICMP Type 8 Echo Request와 Type 0 Echo Reply를 사용하고 IPv6에서는 Type 128과 129를 사용한다.&lt;/li&gt;
&lt;li&gt;ICMP Echo에는 Port가 없으며 Identifier와 Sequence Number로 Request와 Reply를 구분한다.&lt;/li&gt;
&lt;li&gt;Hostname을 ping할 때 DNS가 실패하면 Echo Request를 보내기 전 단계에서 멈춘다.&lt;/li&gt;
&lt;li&gt;Echo Request 전에도 Route, Outgoing Interface와 ARP/NDP Neighbor Resolution이 필요하다.&lt;/li&gt;
&lt;li&gt;Echo Reply는 Forward Path와 Return Path가 모두 성립하고 ICMP Policy가 허용되어야 Source에 도착한다.&lt;/li&gt;
&lt;li&gt;ping 성공은 해당 시점의 ICMP 왕복 증거이며 TCP Port, TLS, HTTP와 업무 처리를 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;ping 실패는 정해진 조건에서 Reply를 보지 못했다는 뜻이며 Host Down, 경로, Firewall, Rate Limit과 Return Path가 모두 후보다.&lt;/li&gt;
&lt;li&gt;RTT는 양방향 지연과 처리 시간을 합친 값이므로 One-Way Delay를 직접 분리하지 못한다.&lt;/li&gt;
&lt;li&gt;ICMP Packet Loss와 Application Traffic의 Loss는 Policy와 Queue가 달라 같지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;Reply TTL은 남은 값이며 Initial TTL을 모르면 정확한 Hop 수를 확정할 수 없다.&lt;/li&gt;
&lt;li&gt;Timeout과 명시적인 Destination Unreachable은 제공하는 정보량이 다르고 오류의 Source를 확인해야 한다.&lt;/li&gt;
&lt;li&gt;IPv4와 IPv6, Source Address, Interface, Namespace가 달라지면 같은 Destination도 결과가 달라질 수 있다.&lt;/li&gt;
&lt;li&gt;작은 Echo와 큰 DF Echo의 결과 차이는 Path MTU 문제의 단서가 된다.&lt;/li&gt;
&lt;li&gt;진단은 Loopback, Local Address, Gateway, Remote IP, Hostname과 실제 Service 순서로 증거 범위를 넓힌다.&lt;/li&gt;
&lt;li&gt;Monitoring은 ICMP뿐 아니라 TCP, TLS, HTTP와 업무 Transaction Check를 계층별로 조합해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: ping, Internet Control Message Protocol, ICMP, ICMPv6, Echo Request, Echo Reply, ICMP Type 8, ICMP Type 0, ICMPv6 Type 128, ICMPv6 Type 129, Identifier, ICMP Sequence Number, Round-Trip Time, RTT, Packet Loss, Jitter, TTL, Hop Limit, Destination Unreachable, Request Timeout, ICMP Filtering, Rate Limiting, Path MTU, Source Address, Address Family&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ping은 Server의 생사를 판정하는 망치가 아니다. 특정 Source에서 특정 Address로 보낸 ICMP Echo가 왕복했는지 묻는 작은 질문이다. 답이 오든 오지 않든 그 범위를 넘어선 결론에는 다른 계층의 증거가 필요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;은 Destination까지의 ICMP 왕복 여부와 시간을 보여 주지만 중간에 어떤 Router를 지났는지는 알려 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 Hop까지 Packet이 갔는지 보려면 일부러 TTL이 작은 Probe를 보내 중간 Router에서 수명을 끝내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 &lt;code&gt;traceroute&lt;/code&gt;의 &lt;code&gt;* * *&lt;/code&gt;는 그 Router가 고장 났다는 뜻일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 보이는 경로는 Forward Path 그 자체일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;traceroute는 보이지 않는 길을 어떻게 보여줄까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; TTL, ICMP Time Exceeded, UDP&amp;middot;ICMP&amp;middot;TCP Probe, Asymmetric Path와 Rate Limiting을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Echo Reply</category>
      <category>Echo Request</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>ping</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1101</guid>
      <comments>https://peterica.tistory.com/1101#entry1101comment</comments>
      <pubDate>Sun, 26 Jul 2026 22:24:42 +0900</pubDate>
    </item>
    <item>
      <title>[바이브코딩] 02. AI와 오래 협업하려면 어떻게 해야 할까? - Plan Mode, 문서화, 컨텍스트</title>
      <link>https://peterica.tistory.com/1110</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NWgKw/dJMcaasE4Wg/7QbAPtM52VtMPq0XYNCm6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NWgKw/dJMcaasE4Wg/7QbAPtM52VtMPq0XYNCm6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NWgKw/dJMcaasE4Wg/7QbAPtM52VtMPq0XYNCm6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNWgKw%2FdJMcaasE4Wg%2F7QbAPtM52VtMPq0XYNCm6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 긴 프로젝트를 진행할 때는 전체 작업을 한꺼번에 맡기지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 대화에서는 하나의 주제만 다루고, AI에게 그 주제에 맞는 역할을 부여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 확정된 문서를 컨텍스트로 제공한 뒤 Plan Mode에서 범위와 결과물을 먼저 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI의 제안과 사용자의 결정을 구분하고, 대화 전체가 아니라 확정된 내용만 문서로 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 문서를 다음 대화의 컨텍스트로 사용하는 반복 과정이 컨텍스트 엔지니어링이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;AI가 지금 무엇을 위해, 어떤 역할로, 어떤 정보를 참고하고, 어디까지 작업해야 하는지를 명확하게 준비하는 과정이다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 짧은 작업을 할 때는 한 번의 요청만으로도 충분할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문장을 다듬거나 작은 아이디어를 정리하는 일이라면 결과를 바로 확인하고 수정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 하나의 서비스를 만드는 일은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 번의 대화가 이어지고, 결정해야 할 주제가 계속 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 목적을 정한 사람과 화면을 설계하는 AI가 같은 기준을 보지 않으면 프로젝트는 쉽게 흔들린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 필요한 것은 더 화려한 프롬프트가 아니라 &lt;b&gt;대화를 결정으로 바꾸고, 그 결정을 다음 대화에 전달하는 반복 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;하나의 주제 선택
      │
      ▼
페르소나 지정
      │
      ▼
기존 문서 제공
      │
      ▼
Plan Mode 토론
      │
      ▼
불필요한 확장 제거
      │
      ▼
핵심 결정
      │
      ▼
문서화
      │
      ▼
다음 단계의 컨텍스트로 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름을 차례로 살펴보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI에게 모든 일을 한꺼번에 맡기면 왜 흔들릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;긴 프로젝트에는 서로 다른 종류의 질문이 들어 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 어떤 서비스를 만들 것인지 정하는 &lt;b&gt;질문&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 사용자가 무엇을 보게 될지 정하는 &lt;b&gt;질문&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 어떤 기술적 방향이 적합한지 검토하는 &lt;b&gt;질문, &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 어떤 정보를 관리할지 정하는 &lt;b&gt;질문&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은 서로 연결되어 있지만 같은 질문은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이 모든 것을 한 번에 요청하면 AI는 빈칸을 채우기 위해 범위를 넓히기 쉽다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 지금 원하는 것은 서비스의 목적을 정하는 일인데, AI는 화면과 기술, 데이터 구조, 향후 기능까지 한꺼번에 제안할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 답을 받는다고 프로젝트가 더 선명해지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 결정할 주제와 나중에 다룰 주제가 섞이면 오히려 무엇부터 선택해야 할지 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와의 협업은 &amp;ldquo;전체 프로젝트를 알아서 완성해 달라&amp;rdquo;고 맡기는 방식보다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; 한 번에 하나의 결정을 함께 완성하는 방식&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 하나의 대화에서는 왜 하나의 주제만 다뤄야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화를 시작하기 전에 먼저 이번에 결정할 주제를 하나 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 목적을 정하는 대화라면 화면의 색상이나 개발 방법으로 넘어가지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 경험을 정하는 대화라면 이미 확정된 목적을 기준으로 사용자가 어떤 순서로 행동하는지에 집중한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트의 주제를 다음처럼 나눌 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서비스의 목적&lt;/li&gt;
&lt;li&gt;사용자 경험&lt;/li&gt;
&lt;li&gt;기술적 방향&lt;/li&gt;
&lt;li&gt;데이터 구조&lt;/li&gt;
&lt;li&gt;기능 인터페이스&lt;/li&gt;
&lt;li&gt;작업 계획&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 목록은 모든 프로젝트가 반드시 따라야 할 고정된 절차가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;중요한 것은 서로 다른 종류의 결정을 구분하는 것이다. &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 단계의 결과를 문서로 확정한 뒤, 그 문서를 들고 다음 주제로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식당을 준비하는 과정으로 생각하면 더 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 식당의 콘셉트를 정하고 있다면 메뉴판 디자인이나 주방 설비를 함께 결정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘셉트가 정해진 뒤에야 다음 담당자가 그 기준을 이어받을 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI에게 역할을 부여하면 무엇이 달라질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제를 골랐다면 AI에게 현재 작업에 맞는 역할을 부여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할을 부여하는 이유는 AI에게 더 많은 권한을 주기 위해서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 &lt;b&gt;생각할 관점을 제한하기 위해서&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 식당을 보더라도 메뉴 기획자, 주방 설계자, 요리사, 운영 담당자가 집중하는 부분은 다르다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 33.7209%; height: 143px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;현재 주제&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;AI에게 부여할 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;서비스 목적&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;서비스 기획자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;사용자 흐름&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;UX 설계자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;기술 방향&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;기술 책임자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;블로그 재구성&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;기술 콘텐츠 에디터&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할과 함께 제외할 범위도 알려 주면 관점이 더 분명해진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너는 지금 서비스 기획자다.&lt;br /&gt;Speed Quiz의 목적과 핵심 사용자 경험만 정리한다.&lt;br /&gt;기술 구현이나 화면 세부 내용은 확장하지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청에서 중요한 부분은 &amp;ldquo;서비스 기획자&amp;rdquo;라는 이름보다 &amp;ldquo;무엇에 집중하고 무엇을 확장하지 않는가&amp;rdquo;라는 경계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;좋은 페르소나는 AI를 더 자유롭게 만드는 것이 아니라 현재 주제에 머물게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 기존 문서를 왜 먼저 제공해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 대화를 시작할 때마다 이전 과정을 처음부터 설명할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 확정된 문서를 제공하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 서비스의 목적과 요구사항이 PRD에 정리되어 있다면, 사용자 경험을 논의할 때 그 PRD를 먼저 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 &amp;ldquo;어떤 서비스를 만들고 있는가&amp;rdquo;를 다시 추측하지 않고, 확정된 목적을 기준으로 다음 질문에 집중할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 관련 문서를 모두 한꺼번에 넣는 것이 항상 좋은 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 작업에 필요한 기준만 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 흐름을 논의하는데 검토 중인 기술 아이디어나 오래된 메모까지 함께 주면 어떤 정보가 우선인지 다시 모호해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 제공할 때는 세 가지를 분명히 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이 문서가 현재 작업의 기준이라는 점&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문서에서 이미 확정된 내용을 임의로 바꾸지 않는다는 점&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문서에 없는 내용은 추측하지 않고 필요한 경우 질문한다는 점&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는 AI가 모든 것을 기억하게 만드는 장치가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 필요한 기억을 정확히 선택해 주는 장치다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Plan Mode에서는 무엇을 확인해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 문서를 제공했다면 바로 최종 결과를 만들기보다 Plan Mode에서 이번 작업의 경계를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plan Mode는 단순히 실행을 늦추는 단계가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 만들기 전에 무엇을 결정해야 하는지 함께 검토하고, 잘못된 방향으로 빠르게 달려가는 일을 줄이는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 다음 다섯 가지를 확인한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이번에 결정할 내용은 무엇인가?&lt;/li&gt;
&lt;li&gt;이미 확정된 내용은 무엇인가?&lt;/li&gt;
&lt;li&gt;아직 결정되지 않은 내용은 무엇인가?&lt;/li&gt;
&lt;li&gt;이번 단계에서 제외할 내용은 무엇인가?&lt;/li&gt;
&lt;li&gt;최종 결과물은 어떤 형태인가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 너무 많은 내용을 제시하면 범위를 다시 줄인다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 지금은 화면 구성만 다룬다. 기술 구현은 다음 단계에서 정리한다.&lt;br /&gt;- &lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;현재 목적에 필요한 내용만 남긴다. 확장 아이디어는 이번 문서에 포함하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충분히 논의한다는 것은 아이디어를 끝없이 늘린다는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 선택지가 있는지 살펴보고, 지금 필요한 것만 남기는 일이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI의 제안과 사용자의 결정은 어떻게 구분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 제시한 내용은 검토할 수 있는 제안이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴듯하고 구체적으로 보이더라도 사용자가 채택하기 전까지는 확정된 요구사항이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구분이 없으면 대화 중 잠깐 나온 아이디어가 다음 단계에서 반드시 구현해야 할 기능처럼 바뀔 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 자신이 앞서 제안한 내용을 문맥의 일부로 보고 계속 발전시킬 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 대화 중에는 상태를 명확하게 표시한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제안:&lt;/b&gt; 검토 중이며 아직 채택하지 않은 내용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확정:&lt;/b&gt; 사용자가 선택했고 다음 작업의 기준이 되는 내용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;제외:&lt;/b&gt; 이번 범위에서 다루지 않기로 한 내용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI의 역할은 선택지를 정리하고 놓친 관점을 보여 주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 채택할지는 사용자가 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 AI가 방향을 대신 정하는 과정이 아니라, 사용자가 AI의 도움을 받아 결정을 더 빠르게 구체화하는 과정이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 대화 전체를 문서로 남기면 안 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 대화에는 최종 결정만 들어 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문, 중간 아이디어, 잘못 이해한 내용, 제외하기로 한 제안도 함께 남아 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 대화 전체를 다음 단계의 기준으로 사용하면 AI는 다시 무엇이 중요한지 해석해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검토만 했던 기능을 확정된 요구사항으로 받아들이거나, 이미 수정한 내용을 이전 답변에서 다시 가져올 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화가 끝나면 다음 순서로 정리한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이번 대화에서 결정한 내용을 고른다.&lt;/li&gt;
&lt;li&gt;목적과 맞지 않는 제안은 제외한다.&lt;/li&gt;
&lt;li&gt;결정된 내용을 짧고 명확하게 다시 쓴다.&lt;/li&gt;
&lt;li&gt;아직 검토 중인 내용은 확정된 결정과 구분한다.&lt;/li&gt;
&lt;li&gt;하나의 주제에 맞는 문서로 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는 대화의 요약본과도 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화에서 무슨 말을 했는지를 줄이는 것이 아니라, &lt;b&gt;다음 작업에 필요한 결정만 남기는 것&lt;/b&gt;이 목적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 거치면 대화는 사라져도 결정은 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 프로젝트의 방향을 계속 통제할 수 있고, AI는 다음 작업에서 참고할 명확한 기준을 얻는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 문서를 다음 대화의 컨텍스트로 어떻게 사용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확정된 문서는 다음 대화의 출발점이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 목적을 정리한 문서는 UX를 논의할 때 사용하고, 확정된 UX 문서는 기술적 방향을 검토할 때 참고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 문서는 이전 단계의 결정을 보존하면서 다음 단계가 임의로 출발하지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 다음 대화에는 긴 이전 대화 대신 필요한 문서와 현재 작업의 지시를 제공한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 PRD는 확정된 기준이다.&lt;br /&gt;너는 UX 설계자의 관점에서 사용자가 보게 될 화면과 이용 순서만 검토한다.&lt;br /&gt;기술 선택과 구현 방법은 이번 범위에서 제외한다.&lt;br /&gt;먼저 결정할 내용과 확인이 필요한 내용을 정리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 특별한 프롬프트 문법이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적, 역할, 기준 문서, 작업 범위, 기대 결과물을 분명히 전달한 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 다음 컨텍스트로 사용하는 순간, 문서는 단순한 기록이 아니라 사람과 AI 사이의 공통 기억이 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이 반복 과정은 왜 컨텍스트 엔지니어링일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 엔지니어링이라는 말은 어렵게 들리지만, 핵심은 간단하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 지금 무엇을 위해, 어떤 역할로, 어떤 정보를 참고하고, 어디까지 작업해야 하는지를 명확하게 준비하는 과정이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 다섯 가지 요소로 정리할 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 52.7907%; height: 166px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;요소&lt;/td&gt;
&lt;td&gt;확인할 질문&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;이번 작업을 왜 하는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;td&gt;AI는 어떤 관점으로 검토해야 하는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참고 문서&lt;/td&gt;
&lt;td&gt;무엇이 이미 확정된 기준인가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작업 범위&lt;/td&gt;
&lt;td&gt;이번에는 어디까지 다루는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기대 결과물&lt;/td&gt;
&lt;td&gt;대화, 초안, 확정 문서 중 무엇을 만드는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 엔지니어링은 프롬프트를 길게 쓰는 기술만을 뜻하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 기준 문서를 만들고, 현재 작업에 필요한 문서만 고르고, AI가 집중할 범위와 역할을 제공하는 전체 과정에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 SSOT와 MECE 원칙을 적용하면 기준은 더 선명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 결정은 하나의 문서에서 관리하고, 각 문서는 겹치지 않는 역할을 맡되 필요한 내용은 빠짐없이 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 AI가 충돌하는 정보를 임의로 조합하거나 비어 있는 부분을 추측할 가능성을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 구조화된 컨텍스트가 AI의 오류나 할루시네이션을 완전히 없애 주는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 어떤 정보를 우선해야 하는지 분명하게 만들고, 불필요한 추측이 필요한 상황을 줄여 준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Speed Quiz에는 이 방식을 어떻게 적용했을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Speed Quiz는 이 협업 방식을 설명하기 위한 사례다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 어떤 기능을 어떤 코드로 만들었는지가 아니라, 하나의 서비스를 만들기 위한 결정을 어떻게 나누고 연결했는가다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &amp;ldquo;어떤 서비스를 만들 것인가&amp;rdquo;라는 주제를 다루고 그 결과를 PRD로 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 대화에서는 PRD를 기준으로 사용자가 보게 될 흐름을 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음에는 확정된 목적과 경험을 바탕으로 기술적 방향을 검토했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계에서 AI에게 현재 주제에 맞는 역할을 부여했고, 이번에 다루지 않을 범위를 함께 정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 제시한 모든 내용을 요구사항으로 채택하지 않고, 필요한 결정만 골라 문서에 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 만들어진 문서가 다음 단계의 컨텍스트가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Speed Quiz의 사례를 한 줄로 정리하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 주제를 논의한다 &amp;rarr; 결정만 문서로 남긴다 &amp;rarr; 그 문서로 다음 주제를 시작한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식이라면 사용자가 처음부터 모든 개발 지식을 알고 있을 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 결정할 질문을 분명하게 만들고, AI와 함께 선택지를 검토하고, 확정된 결과를 다음 단계에 전달하면 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 대화는 사라져도 결정은 남아야 한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 긴 프로젝트를 시작하기 전에 다음 내용을 확인해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지금 대화에서 다룰 주제는 하나인가?&lt;/li&gt;
&lt;li&gt;AI에게 현재 주제에 맞는 역할을 주었는가?&lt;/li&gt;
&lt;li&gt;이미 확정된 문서를 기준으로 제공했는가?&lt;/li&gt;
&lt;li&gt;이번 단계에서 제외할 범위를 밝혔는가?&lt;/li&gt;
&lt;li&gt;AI의 제안과 사용자의 결정을 구분했는가?&lt;/li&gt;
&lt;li&gt;대화 전체가 아니라 확정된 내용만 문서로 남겼는가?&lt;/li&gt;
&lt;li&gt;그 문서를 다음 대화의 컨텍스트로 사용할 수 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 답할 수 있다면 프로젝트 전체를 한꺼번에 지시하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 매 단계에서 하나의 결정에 집중하고, AI는 현재 역할과 범위에 집중할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩의 핵심은 AI에게 모든 일을 맡기는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자가 목적과 범위를 결정하고, AI가 그 결정을 구체화하도록 돕고, 문서가 다음 협업을 이어 주게 만드는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 완벽한 문서를 만들 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 결정한 내용을 분명하게 남기고, 다음 대화에서 그 문서를 정확한 기준으로 사용하는 것부터 시작하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 쌓인 문서는 어느 순간 하나의 서비스를 만들기 위한 설계가 되어 있을 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 읽으면 좋은 글&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[바이브코딩] 01. AI와 일할 때 왜 문서가 필요할까? - SSOT, MECE, 컨텍스트&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- PRD부터 API까지 문서의 역할과 SSOT&amp;middot;MECE 원칙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1048&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Context Note &amp;mdash; AI 대화의 컨텍스트는 왜 &amp;ldquo;쌓지 않고 구조화&amp;rdquo;해야 할까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- ChatGPT와 함께 컨텍스트를 문서 중심으로 관리하기 위해 만든 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- GPT Atlas에서 트리 구조의 노트, 마크다운 편집기, ChatGPT 대화창을 함께 사용하며 필요한 컨텍스트를 구조화할 수 있다.&lt;/p&gt;</description>
      <category>AI/바이브코딩</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1110</guid>
      <comments>https://peterica.tistory.com/1110#entry1110comment</comments>
      <pubDate>Sun, 26 Jul 2026 07:41:50 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 23. 우체부를 믿지 않고도 편지 내용을 지킬 수 있을까?</title>
      <link>https://peterica.tistory.com/1100</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BYwA5/dJMcadiyDFV/jAHpUB72m3fiQWmoN8SZzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BYwA5/dJMcadiyDFV/jAHpUB72m3fiQWmoN8SZzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BYwA5/dJMcadiyDFV/jAHpUB72m3fiQWmoN8SZzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBYwA5%2FdJMcadiyDFV%2FjAHpUB72m3fiQWmoN8SZzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 7. 인터넷의 주소를 함께 쓰고 경계를 지키는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall이 HTTPS 통신을 허용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet은 집의 Router와 ISP, 여러 중간 Router를 지나 Server에 도착한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 경로의 모든 장비를 믿어야만 Password와 결제 정보를 보낼 수 있다면 인터넷은 안전한 업무 공간이 되기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로에서 일어날 수 있는 위협을 세 가지로 나누어 보자.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;도청
&amp;rarr; 편지 내용을 몰래 읽음

변조
&amp;rarr; 전달 중 내용을 바꿈

사칭
&amp;rarr; 공격자가 진짜 Server인 척함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화 하나만으로 세 문제를 모두 해결할 수는 없다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Confidentiality
&amp;rarr; 허가되지 않은 사람이 내용을 읽지 못하게 함

Integrity
&amp;rarr; 전달 중 내용이 바뀌었는지 확인함

Authentication
&amp;rarr; 통신 상대의 Identity를 확인함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Transport Layer Security&lt;/b&gt;(TLS)는 이 속성을 결합해 신뢰할 수 없는 Network 위에 보호된 Application Channel을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 TLS 1.3의 &lt;b&gt;ClientHello&lt;/b&gt;, &lt;b&gt;ServerHello&lt;/b&gt;, &lt;b&gt;Key Exchange&lt;/b&gt;, &lt;b&gt;Certificate Chain&lt;/b&gt;, &lt;b&gt;CertificateVerify&lt;/b&gt;, &lt;b&gt;Finished&lt;/b&gt;, &lt;b&gt;AEAD&lt;/b&gt;, &lt;b&gt;Session Resumption&lt;/b&gt;을 따라가며 HTTPS가 해결하는 문제와 해결하지 않는 문제를 구분한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 자물쇠만 채우면 상대를 믿을 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client와 공격자가 암호화된 연결을 만들었다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 연결 안의 내용은 중간의 다른 사람에게 보이지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Client가 공격자를 진짜 Bank Server로 착각했다면 안전한 암호화가 공격자와의 비밀 통로를 만들어 준 셈이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;harr; 암호화 &amp;harr; Attacker

Encryption 성공
Server Authentication 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 진짜 Server의 Certificate를 확인했지만 Application Data를 평문으로 보내면 경로에서 내용이 노출될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS가 여러 단계를 사용하는 이유다.&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;상대가 제시한 Public Key는 누구의 것인가?
그 Public Key에 대응하는 Private Key를 실제로 가지고 있는가?
양쪽이 만든 Session Key가 같은가?
Handshake 메시지가 중간에서 바뀌지 않았는가?
이후 Application Data를 안전하게 보호할 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 자물쇠가 아니라 서로 다른 증명을 연결해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ HTTPS는 HTTP와 다른 Transport Protocol일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 HTTP/1.1과 HTTP/2의 HTTPS 연결은 TCP 위에 TLS를 놓고 그 안에 HTTP를 전달한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;HTTP Application Message
        &amp;darr;
TLS Record
        &amp;darr;
TCP Byte Stream
        &amp;darr;
IP Packet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 순서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. DNS로 Server Address 확인
2. TCP 3-Way Handshake
3. TLS Handshake
4. 보호된 HTTP Request와 Response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Handshake 성공은 TLS 성공이 아니고, TLS 성공은 HTTP 요청 처리 성공이 아니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TCP 성공 + TLS 인증서 오류
&amp;rarr; Port와 경로는 열렸지만 보안 연결 실패

TLS 성공 + HTTP 500
&amp;rarr; 보호된 Channel은 생겼지만 Application 처리 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/3는 TCP 대신 UDP 기반 QUIC을 사용하고 TLS 1.3 Handshake를 QUIC 연결 설정에 통합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계층의 배치는 다르지만 상대 인증, Key 합의와 보호된 Application Data라는 목표는 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS는 왜 비대칭키와 대칭키를 함께 사용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Key Cryptography는 상대 인증과 Key Agreement에 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 대용량 Application Data를 모두 Public Key 연산으로 처리하는 것은 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대칭키 암호는 같은 Secret Key를 공유한 뒤 많은 Data를 빠르게 보호하는 데 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 역할을 나눈다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Public Key Cryptography
├─ Certificate의 Signature 검증
├─ Server의 Private Key 소유 증명
└─ Ephemeral Key Agreement와 인증 연결

Symmetric Cryptography
├─ Handshake Message 보호
└─ Application Data의 Encryption과 Integrity&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate가 Web Page 전체를 직접 암호화하는 것도 아니고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Private Key가 매 TCP Segment를 암호화하는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake에서 공유 Secret을 만들고 그로부터 방향별 Traffic Key를 파생한 뒤 대칭키로 TLS Record를 보호한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS Handshake 전에 TCP는 무엇을 끝냈을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 기반 TLS를 가정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 TCP 연결이 만들어진다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Server &amp;rarr; Client : SYN-ACK
Client &amp;rarr; Server : ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계가 확인하는 것은 TCP Endpoint와 양방향 Byte Stream이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 다음은 확인하지 않았다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Server Certificate가 유효한가?
접속한 Hostname과 Certificate가 일치하는가?
Application Data를 암호화할 Key가 있는가?
HTTP Message가 보호되는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 위에서 TLS Handshake가 별도로 시작되는 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 첫 번째 보안 인사 &amp;mdash; ClientHello&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 &lt;b&gt;ClientHello&lt;/b&gt;로 자신이 지원하는 조건과 Key Agreement 재료를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 대표적인 내용은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ClientHello
├─ Supported Versions
├─ Cipher Suites
├─ Supported Groups
├─ Signature Algorithms
├─ Key Share
├─ Server Name Indication(SNI)
├─ Application-Layer Protocol Negotiation(ALPN) 제안
└─ Random과 Extension&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Supported Versions&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 사용할 수 있는 TLS Version을 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Cipher Suites&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3 Cipher Suite는 주로 Application Data를 보호할 AEAD 알고리즘과 Hash 조합을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.2 시대의 이름처럼 Certificate Signature와 Key Exchange 방식까지 Cipher Suite 이름 하나에 모두 넣는 구조로 이해하면 안 된다. Supported Groups와 Signature Algorithms 같은 Extension이 별도로 역할을 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Key Share&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Ephemeral Diffie-Hellman 계열 Key Agreement에 사용할 Public 값을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private 값 자체를 Network로 보내는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ SNI&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 IP Address에서 여러 Domain을 서비스할 때 Client가 원하는 Server Name을 알릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 ClientHello의 SNI는 중간 장비에 보일 수 있다. &lt;b&gt;Encrypted ClientHello&lt;/b&gt;(ECH)를 사용하는 구성에서는 ClientHello의 민감한 부분을 암호화할 수 있지만 지원과 구성에 따라 관찰 가능성은 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ ALPN&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 &lt;code&gt;h2&lt;/code&gt;, &lt;code&gt;http/1.1&lt;/code&gt;처럼 TLS 위에서 사용할 Application Protocol 후보를 제안한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port 443만으로 HTTP Version을 결정하는 것이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 번째 보안 인사 &amp;mdash; ServerHello&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 Client가 제시한 조건에서 사용할 Version, Cipher Suite와 Key Share를 선택해 &lt;b&gt;ServerHello&lt;/b&gt;를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ServerHello
├─ Selected TLS Version
├─ Selected Cipher Suite
├─ Server Key Share
└─ Random 등&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client와 Server는 상대의 Public Key Share와 자신의 Ephemeral Private 값을 이용해 같은 Shared Secret을 계산할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client가 계산한 Shared Secret
= Server가 계산한 Shared Secret

Network로 Shared Secret 자체를 전송하지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Public Key Share를 Capture해도 Ephemeral Private 값을 모르면 같은 Secret을 계산하기 어렵도록 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3에서는 ServerHello 뒤의 Handshake Message가 파생된 Handshake Traffic Key로 보호되기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Passive Capture에서 TLS 1.3 Certificate가 평문으로 보이지 않을 수 있는 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Server가 선택한 Application Protocol은 어디에서 알릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 &lt;b&gt;EncryptedExtensions&lt;/b&gt;에서 ClientHello의 여러 Extension에 대한 선택 결과를 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 ALPN으로 선택한 Application Protocol이 들어갈 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;ClientHello ALPN Offer
├─ h2
└─ http/1.1

EncryptedExtensions ALPN Selection
└─ h2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3에서는 EncryptedExtensions가 ServerHello 이후 암호화된 Handshake 영역에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 HTTP/2를 원했다고 항상 &lt;code&gt;h2&lt;/code&gt;가 선택되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽이 공통으로 지원하고 Server Policy가 허용한 결과가 선택된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Certificate에는 무엇이 들어 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 자신의 Identity와 Public Key를 연결한 &lt;b&gt;X.509 Certificate&lt;/b&gt;와 필요한 Chain을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Leaf Certificate의 대표 정보는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Subject 또는 Identity 정보
Subject Alternative Name(SAN)
Public Key
Issuer
Serial Number
Validity: Not Before / Not After
Key Usage와 Extended Key Usage
Issuer의 Digital Signature&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate에는 Server의 &lt;b&gt;Public Key&lt;/b&gt;가 들어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 &lt;b&gt;Private Key&lt;/b&gt;는 들어 있지 않으며 외부로 보내면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Certificate 배포 가능
Private Key 비밀 유지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate 파일과 Private Key 파일을 같은 것으로 생각하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 인증서 체인은 왜 여러 장일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web PKI의 일반적인 Chain은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Root CA Certificate
        &amp;darr; 서명
Intermediate CA Certificate
        &amp;darr; 서명
Server Leaf Certificate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Server가 보낸 Leaf와 Intermediate를 따라가며 자신이 이미 신뢰하는 Root CA에 도달할 수 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 보통 Leaf와 필요한 Intermediate를 보내고 Root Certificate는 보내지 않거나 보내도 신뢰의 근거가 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Root를 신뢰하는 이유는 Network에서 방금 받았기 때문이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client 운영체제나 Browser의 &lt;b&gt;Trust Store&lt;/b&gt;에 미리 신뢰 대상으로 설치되어 있기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Server가 Root를 함께 보냄
&amp;ne; Client가 그 Root를 자동 신뢰&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구나 자기 이름으로 Root와 Certificate를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 Client가 어떤 Trust Anchor를 어떤 절차로 신뢰하도록 구성했는가다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Certificate Signature는 무엇을 증명할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intermediate CA가 Server Certificate의 내용을 자신의 Private Key로 서명했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Intermediate Certificate의 Public Key로 Signature를 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증이 성공하면 다음을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Certificate 내용이 서명 후 바뀌지 않았다.
해당 Intermediate의 Private Key 소유자가 이 Certificate를 발급했다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Signature 하나만 맞는다고 검증이 끝나지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Issuer Chain이 신뢰하는 Root까지 이어지는가?&lt;/li&gt;
&lt;li&gt;CA Certificate가 Certificate 발급 용도로 허용되어 있는가?&lt;/li&gt;
&lt;li&gt;Name Constraints와 Policy가 적용되는가?&lt;/li&gt;
&lt;li&gt;Certificate가 현재 유효 기간 안에 있는가?&lt;/li&gt;
&lt;li&gt;Server Authentication 용도로 사용할 수 있는가?&lt;/li&gt;
&lt;li&gt;접속한 Hostname과 SAN이 일치하는가?&lt;/li&gt;
&lt;li&gt;폐기된 Certificate인지 확인할 정책은 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate Validation은 &amp;ldquo;서명 수학 한 번&amp;rdquo;이 아니라 여러 조건을 함께 검증하는 과정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Hostname은 Certificate의 어디와 비교할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 &lt;code&gt;https://shop.example&lt;/code&gt;에 접속했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate가 신뢰되는 CA에서 발급되었어도 &lt;code&gt;bank.example&lt;/code&gt;만을 위한 Certificate라면 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 자신이 접속하려던 Reference Hostname을 Certificate의 &lt;b&gt;Subject Alternative Name&lt;/b&gt;(SAN)과 비교한다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;접속 Hostname: shop.example

SAN: DNS:shop.example
&amp;rarr; 일치 가능

SAN: DNS:bank.example
&amp;rarr; 불일치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 Common Name만 보는 방식에 의존하지 않고 SAN을 기준으로 검증하는 것이 현대적인 동작이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wildcard에도 범위가 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;*.example.com
&amp;rarr; api.example.com에는 일치 가능
&amp;rarr; deep.api.example.com까지 모두 일치한다고 단정할 수 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS가 공격받아 다른 IP로 연결되더라도 공격자가 해당 Hostname에 유효한 Certificate와 Private Key를 제시하지 못하면 TLS Hostname 검증에서 막을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 유효 기간과 Clock은 왜 중요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate에는 &lt;code&gt;Not Before&lt;/code&gt;와 &lt;code&gt;Not After&lt;/code&gt;가 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;현재 시각 &amp;lt; Not Before
&amp;rarr; 아직 유효하지 않음

현재 시각 &amp;gt; Not After
&amp;rarr; 만료됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client System Clock이 크게 틀리면 정상 Certificate도 아직 유효하지 않거나 만료된 것으로 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 오류를 조사할 때 Server 설정뿐 아니라 Client의 시간 동기화도 확인해야 하는 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효 기간이 남았어도 Private Key 유출이나 잘못된 발급 때문에 Certificate를 조기에 폐기해야 할 수 있다. CRL과 OCSP 같은 Revocation 메커니즘이 있지만 Client Policy, Network 도달성과 실패 처리 방식이 달라 단순한 한 단계로 끝나지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Certificate만 보내면 Private Key 소유를 증명할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate는 Public 정보이므로 누구나 복사할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 진짜 Server의 Certificate 파일만 복사해 보내는 것을 막으려면 대응하는 Private Key를 가지고 있음을 증명해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 &lt;b&gt;CertificateVerify&lt;/b&gt;가 이 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 지금까지의 Handshake Transcript에 관련된 값을 자신의 Certificate Private Key로 서명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Certificate의 Public Key로 Signature를 검증한다.&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;Certificate 복사본만 가진 공격자
&amp;rarr; Private Key가 없어 유효한 CertificateVerify 생성 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate Chain 검증은 &amp;ldquo;이 Public Key가 해당 Hostname과 연결되어 신뢰되는가?&amp;rdquo;를 확인하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CertificateVerify는 &amp;ldquo;상대가 그 Public Key에 대응하는 Private Key를 실제로 가지고 있는가?&amp;rdquo;를 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Finished는 무엇을 마지막으로 확인할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server와 Client는 지금까지 주고받은 Handshake Message의 Transcript Hash와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파생된 Secret을 이용해 &lt;b&gt;Finished&lt;/b&gt; Message를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대가 Finished를 올바르게 검증하면 다음 단서를 얻는다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;상대도 올바른 Handshake Secret을 계산했다.
Handshake Transcript가 양쪽에서 동일하다.
중간에서 Version, Cipher, Key Share 등이 몰래 바뀌지 않았다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 Certificate Signature만 검증하고 Finished를 생략하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key Agreement와 전체 Handshake의 무결성을 끝까지 묶을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순화한 TLS 1.3 Handshake는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;Client                                             Server

ClientHello
Key Share, Version, Cipher, SNI, ALPN -----------&amp;gt;

                                      ServerHello
                            Selected Key Share &amp;lt;---

                          EncryptedExtensions &amp;lt;---
                                 Certificate &amp;lt;---
                           CertificateVerify &amp;lt;---
                                    Finished &amp;lt;---

Certificate Chain, Hostname,
CertificateVerify, Finished 검증

Finished ----------------------------------------&amp;gt;

&amp;lt;========== Protected Application Data ==========&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Handshake에는 선택한 인증 방식, Resumption과 Client Authentication 등에 따라 Message가 달라질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 중간 공격자가 Key Share를 바꾸면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 ClientHello와 ServerHello 사이에 들어와 서로 다른 Key Agreement를 만들려 한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Client &amp;harr; Attacker &amp;harr; Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key Agreement만 있고 Authentication이 없다면 Client와 Server가 각각 공격자와 Secret을 만들 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 Server Certificate의 Private Key로 Handshake Transcript에 서명하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finished로 전체 Transcript와 파생 Secret을 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Key Share나 선택 Parameter를 바꾸면 유효한 CertificateVerify와 Finished를 함께 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 Server의 Private Key와 Handshake Secret이 없으면 검증을 통과시키기 어렵다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Key Agreement
+ Certificate Authentication
+ Transcript Integrity
= Active Man-in-the-Middle 방어&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 기능이 따로가 아니라 하나의 Handshake Transcript에 묶이는 것이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Forward Secrecy는 무엇을 지킬까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 일반적인 Ephemeral Diffie-Hellman Key Agreement에서는 연결마다 일시적인 Private 값을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake가 끝난 뒤 이 Ephemeral Secret을 안전하게 폐기했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 Server Certificate의 장기 Private Key가 유출되어도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 Capture한 Session의 Key를 장기 Key만으로 복원하기 어렵게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Forward Secrecy&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;장기 Certificate Private Key
&amp;rarr; Server Identity 서명에 사용

연결별 Ephemeral Key
&amp;rarr; Session Shared Secret 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Secrecy가 Endpoint Memory 탈취, Session Key Log 유출과 Application 평문 저장까지 막아 주는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ephemeral Key 생성과 폐기, Random Number 품질과 Endpoint 보안이 함께 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Application Data는 어떤 방식으로 보호할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake가 끝나면 양쪽은 파생된 방향별 Application Traffic Key로 TLS Record를 보호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3은 &lt;b&gt;Authenticated Encryption with Associated Data(AEAD)&lt;/b&gt; 알고리즘을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 역할은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;Plaintext
        &amp;darr; Encryption
Ciphertext

Record Metadata와 Ciphertext
        &amp;darr; Authentication Tag 검증
변조 여부 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AES-GCM이나 ChaCha20-Poly1305 같은 AEAD 방식은 기밀성과 무결성 보호를 결합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Ciphertext의 Bit를 바꾸면 Receiver의 Authentication Tag 검증이 실패하고 정상 Application Data로 전달되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Checksum도 전송 오류를 탐지하지만 공격자가 다시 계산할 수 있으므로 암호학적 무결성을 제공하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Integrity는 Secret Key를 모르는 공격자가 유효한 Record를 위조하기 어렵게 만든다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS Record와 TCP Segment는 같은 크기일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 TCP의 Byte Stream 위에서 자체 Record를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;HTTP Message
&amp;rarr; TLS Record 하나 또는 여러 개
&amp;rarr; TCP Segment 하나 또는 여러 개
&amp;rarr; IP Packet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 TLS Record가 여러 TCP Segment에 나뉠 수 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 작은 Record가 전송 과정에서 하나의 큰 Host Buffer처럼 관찰될 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;TLS Record Boundary
&amp;ne; TCP Segment Boundary
&amp;ne; HTTP Message Boundary&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture에서 TCP 한 줄과 TLS Message 하나를 1:1로 대응시키면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 순서 있는 Byte를 전달하고 TLS Record Layer가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 Byte에서 Record를 복원해 인증한 뒤 Application에 평문을 제공한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Client도 Certificate를 보여줄 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Web HTTPS에서는 Server가 Certificate로 자신을 인증하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 그 뒤 Password, Cookie, Token 등으로 Application 인증을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 Client Certificate를 요청해 양쪽을 인증할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Mutual TLS&lt;/b&gt;(mTLS)라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Server Certificate
&amp;rarr; Client가 Server Identity 검증

Client Certificate
&amp;rarr; Server가 Client Identity 검증&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mTLS에서 Client도 Certificate Chain과 Private Key 소유를 증명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Certificate가 곧 최종 업무 권한은 아니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;mTLS Authentication
&amp;rarr; 연결 주체 확인

Application Authorization
&amp;rarr; 해당 주체가 어떤 API와 Data를 사용할 수 있는지 결정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증과 권한 부여를 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한 번 연결할 때마다 전체 Handshake를 반복할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 이전 Session에서 만든 정보를 이용해 다음 연결을 더 빠르게 시작할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 &lt;b&gt;Session Resumption&lt;/b&gt;은 Server가 제공한 Ticket과 PSK(Pre-Shared Key) 관련 정보를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;첫 연결
&amp;rarr; Full Handshake와 Server Certificate 검증
&amp;rarr; Resumption 정보 획득

다음 연결
&amp;rarr; PSK 기반 Resumed Handshake 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resumption도 새 연결의 Key를 파생하고 Handshake 무결성을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 Application Traffic Key를 그대로 계속 재사용한다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ticket의 수명, Server Key Rotation, Cluster 간 State와 Client Cache 정책이 Resumption 가능 여부에 영향을 준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 0-RTT Data는 왜 모든 요청에 쓰면 안 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3 Resumption에서는 조건이 맞으면 Client가 Handshake 완료 전에 &lt;b&gt;Early Data&lt;/b&gt;, 즉 0-RTT Data를 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지연을 줄일 수 있지만 Replay 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Capture한 Early Data를 Server나 Cluster의 다른 Node에 다시 전달할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;GET /static-image
&amp;rarr; 반복되어도 영향이 작도록 설계 가능

POST /transfer-money
&amp;rarr; Replay되면 업무가 중복 실행될 위험&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0-RTT를 사용하려면 Application 요청의 Idempotency와 Anti-Replay 설계를 함께 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS가 Record를 암호화했다는 사실만으로 업무 요청이 Exactly-Once 실행되는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS가 있어도 Replay와 중복 처리가 남을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 TLS Record Layer는 연결 안의 Record 순서와 무결성을 보호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 사용자가 응답을 받지 못해 Application 요청 전체를 새 연결에서 다시 보내는 문제는 별도다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Client가 결제 요청 전송
Server가 처리 후 응답
응답이 Client에 도착하기 전 연결 종료
Client가 새 연결에서 재시도&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 TLS 연결이 모두 안전해도 결제가 두 번 처리될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application은 Request ID, Idempotency Key, Transaction 상태로 중복 실행을 제어해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS의 Integrity와 Application의 Exactly-Once 의미를 혼동하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS를 종료하는 Load Balancer 뒤도 계속 암호화될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 두 TLS Endpoint 사이의 구간을 보호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Load Balancer나 Reverse Proxy가 TLS를 종료하면 Client가 만든 보호 구간은 그 장비에서 끝난다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Client
  ║ TLS 보호 구간 1
Load Balancer
  │ Plain HTTP 또는
  ║ 새 TLS 보호 구간 2
Application Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backend로 평문을 보내면 Client와 Load Balancer 사이만 TLS로 보호된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backend에도 TLS를 사용하면 별도의 Certificate, Key, Trust와 관찰 지점을 가진 두 보안 연결이 생긴다.&lt;/p&gt;
&lt;pre class=&quot;cal&quot;&gt;&lt;code&gt;End-to-End라는 표현
&amp;rarr; 실제 TLS Endpoint가 어디인지 확인해야 함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Inspection Proxy도 Client와 원래 Server 사이의 하나의 TLS Channel을 그대로 들여다보는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client 쪽 연결을 종료하고 자신의 Certificate를 제시한 뒤 Server와 새 TLS 연결을 만드는 방식이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 조직의 Inspection CA를 신뢰하도록 구성되어 있어야 하며 Proxy가 평문과 Private Key 관련 책임을 갖게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS가 숨기지 못하는 정보는 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 Application Payload와 보호 대상 Handshake Message를 암호화하지만 &lt;br /&gt;Packet 전달에 필요한 모든 Metadata를 없애지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Network에서 일반적으로 관찰 가능한 정보가 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source와 Destination IP
TCP 또는 UDP Port
Packet 크기와 방향
전송 시간과 연결 지속 시간
TLS 사용 사실과 일부 Handshake Metadata&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 TLS ClientHello의 SNI와 ALPN Offer도 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECH가 적용된 구성에서는 ClientHello 내부 정보의 노출 범위가 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 질의를 평문으로 보냈다면 접속 전에 조회한 Domain도 Network에서 관찰될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS와 DNS Privacy는 별도의 구간과 Protocol 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traffic Analysis까지 완전히 막는 익명화 Protocol로 TLS를 이해하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS가 해결하지 않는 문제는 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS가 성공해 Browser에 자물쇠가 보인다고 Site의 모든 행동이 안전하다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS가 해결하지 않는 대표 문제는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server Application의 SQL Injection과 권한 검증 오류&lt;/li&gt;
&lt;li&gt;사용자가 공격자의 유사 Domain을 직접 방문한 Phishing&lt;/li&gt;
&lt;li&gt;Client나 Server Endpoint의 Malware와 Memory 탈취&lt;/li&gt;
&lt;li&gt;Server가 받은 평문 Data의 안전하지 않은 저장&lt;/li&gt;
&lt;li&gt;인증된 사용자의 과도한 권한&lt;/li&gt;
&lt;li&gt;DDoS와 Service Availability&lt;/li&gt;
&lt;li&gt;Traffic 크기와 시간 같은 Metadata 노출&lt;/li&gt;
&lt;li&gt;안전하게 처리되지 않은 Application 재시도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자도 자신이 소유한 Domain에 유효한 Certificate를 발급받을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Certificate 유효
&amp;rarr; 그 Domain과 Public Key의 연결이 검증됨

Certificate 유효
&amp;ne; 그 Site가 선하거나 업무상 승인됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 보호된 Channel의 속성을 제공하고 Application의 신뢰성과 업무 판단은 별도 계층에서 해결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Certificate 오류는 무시해도 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Client가 경고를 내는 이유를 구분해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Unknown CA
&amp;rarr; Chain이 신뢰하는 Root로 이어지지 않음

Hostname Mismatch
&amp;rarr; 접속 Hostname이 SAN과 일치하지 않음

Expired / Not Yet Valid
&amp;rarr; Certificate 유효 기간 또는 Client Clock 문제

Incomplete Chain
&amp;rarr; 필요한 Intermediate를 Server가 보내지 않음

Signature / Key Usage 오류
&amp;rarr; Chain이나 Certificate 용도 검증 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 환경의 Self-Signed Certificate라고 Browser 경고를 습관적으로 무시하면 실제 Man-in-the-Middle과 단순한 Local 설정을 구분하는 검증 절차가 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발용 CA나 Certificate를 해당 환경의 Trust Store에 명시적으로 제한해 설치하고 Production Trust와 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--insecure&lt;/code&gt; 같은 검증 비활성화 Option은 암호화된 상대가 누구인지 확인하지 않게 만들므로 정상 운영 해결책이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Private Key가 유출되면 무엇을 해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate 파일은 배포할 수 있지만 Private Key는 Server Identity 증명의 핵심 비밀이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private Key가 유출되면 공격자가 해당 Key로 Server를 사칭할 수 있는 위험이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대응에는 다음이 포함될 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;노출된 Key의 사용을 즉시 중단한다.&lt;/li&gt;
&lt;li&gt;새 Private Key를 안전한 환경에서 생성한다.&lt;/li&gt;
&lt;li&gt;새 Certificate를 발급하고 배포한다.&lt;/li&gt;
&lt;li&gt;기존 Certificate의 폐기 절차를 수행한다.&lt;/li&gt;
&lt;li&gt;Key가 사용된 시스템과 Log를 조사한다.&lt;/li&gt;
&lt;li&gt;자동화 Token과 Backup 등 추가 유출 경로를 제거한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 권한만으로 끝나는 문제가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HSM이나 Key Management System, 접근 통제, Rotation과 Audit가 운영 설계에 포함될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Secrecy는 과거 Session 일부를 보호하는 데 도움이 되지만 유출된 장기 Key로 앞으로의 사칭을 막아 주지는 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS 실패를 어느 단계에서 나눠 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;HTTPS가 안 된다&amp;rdquo;는 한 문장에는 여러 단계가 숨어 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. DNS Resolution
2. Route와 ARP/NDP
3. TCP 또는 QUIC 연결
4. TLS Version과 Cipher 협상
5. Certificate Chain과 Hostname 검증
6. Finished 검증과 Key 확정
7. HTTP Request 전송
8. Application Response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증상도 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TCP SYN Timeout
&amp;rarr; TLS ClientHello도 보내지 못함

TLS Alert: unknown_ca
&amp;rarr; TCP는 연결됐지만 Trust 검증 실패

TLS Alert: handshake_failure
&amp;rarr; Version, Algorithm, Client 인증 등 협상 문제 가능

HTTP 403
&amp;rarr; TLS는 성공했고 Application이 요청을 거절&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Error String만으로 원인을 확정하지 말고 Packet Capture, TLS Client 출력, Server Log와 Certificate 내용을 함께 본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 TLS Message와 검증으로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;사용할 자물쇠 방식과 재료를 제안&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;ClientHello&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Version, Cipher, Key Share, SNI, ALPN 등을 제시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;사용할 방식과 Server 재료를 선택&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;ServerHello&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;TLS Version, Cipher Suite와 Server Key Share 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;공인된 신분증과 Public Key&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Certificate Chain&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Host Identity를 Public Key와 신뢰 체인에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;신분증 이름과 방문 주소 비교&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Hostname Verification&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;접속 Hostname을 SAN과 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;신분증 주인의 비밀 서명&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;&lt;b&gt;CertificateVerify&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;Server가 Certificate Private Key를 소유했음을 Transcript 서명으로 증명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;인사 내용 전체의 봉인 확인&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Finished&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Handshake Secret과 Transcript의 일치 및 무결성 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;양쪽만 계산한 일회용 자물쇠 재료&lt;/td&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;&lt;b&gt;Ephemeral Key Exchange&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;Shared Secret을 Network로 직접 보내지 않고 합의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;이후 편지의 잠금과 변조 봉인&lt;/td&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;&lt;b&gt;AEAD Record Protection&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px;&quot;&gt;Application Data의 Confidentiality와 Integrity 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;미리 신뢰하도록 설치한 발급 기관&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Trust Store / Root CA&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Certificate Chain 검증의 Local Trust Anchor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;양쪽 모두 신분증 제시&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;mTLS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Server와 Client Certificate Authentication 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;이전 접수표로 빠르게 재연결&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Session Resumption&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;PSK와 Ticket으로 Handshake 비용을 줄임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;확인 전 먼저 보내는 재배송 가능 편지&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;&lt;b&gt;0-RTT Early Data&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;지연을 줄이지만 Replay 위험을 고려해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3의 핵심 증명을 모으면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;Certificate Chain + Hostname
&amp;rarr; 이 Public Key가 접속하려던 이름과 신뢰 체인에 연결되는가?

CertificateVerify
&amp;rarr; 상대가 그 Public Key의 Private Key를 실제로 소유하는가?

Ephemeral Key Exchange
&amp;rarr; 양쪽이 연결별 Shared Secret을 만들었는가?

Finished
&amp;rarr; Handshake Transcript와 파생 Secret이 양쪽에서 일치하는가?

AEAD
&amp;rarr; 이후 Record가 읽히거나 바뀌지 않도록 보호되는가?&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서는 무엇이 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3 연결을 암호 해제 Key 없이 Capture하면 일반적으로 다음 흐름을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TCP 3-Way Handshake
ClientHello
ServerHello
암호화된 Handshake와 Application Data
TCP 연결 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 1.3에서는 ServerHello 뒤의 Certificate와 CertificateVerify도 암호화되므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Passive Capture만으로 Certificate 내용을 읽을 수 없을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark Display Filter 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;tls || tcp.port == 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 항목은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClientHello가 실제로 전송되었는가?&lt;/li&gt;
&lt;li&gt;ServerHello 또는 TLS Alert가 돌아왔는가?&lt;/li&gt;
&lt;li&gt;선택한 TLS Version은 무엇인가?&lt;/li&gt;
&lt;li&gt;TCP Retransmission 때문에 Handshake가 지연되는가?&lt;/li&gt;
&lt;li&gt;TLS 성공 뒤 Application Data Record가 이어지는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Key Log를 Client가 명시적으로 제공하면 Wireshark가 Lab Traffic을 복호화할 수 있는 환경도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Session Key Log는 보호된 내용을 평문으로 드러낼 수 있는 민감 정보이므로 Production에서 수집하거나 공유하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ OpenSSL로 Certificate 내용을 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate 파일이 있다면 다음처럼 주요 Field를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;openssl x509 -in server.crt -noout \
  -subject -issuer -serial -dates \
  -ext subjectAltName \
  -fingerprint -sha256&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 다음 질문을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Subject와 Issuer가 누구인가?&lt;/li&gt;
&lt;li&gt;SAN에 접속할 DNS Name이나 IP가 있는가?&lt;/li&gt;
&lt;li&gt;Not Before와 Not After 사이에 현재 시간이 있는가?&lt;/li&gt;
&lt;li&gt;SHA-256 Fingerprint가 배포 문서와 일치하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fingerprint 비교는 신뢰할 수 있는 별도 Channel에서 기대값을 얻었을 때 의미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 Certificate와 Fingerprint를 같은 Channel에서 함께 바꾸면 독립적인 검증이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Local TLS Server를 직접 만들어 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 개인 Computer의 Loopback에서만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;openssl&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;가 필요하며 외부 Server나 저장소를 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 실습 Directory를 만들고 Private Key 파일의 기본 권한을 제한한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;mkdir tls-local-lab
cd tls-local-lab
umask 077&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 동안만 사용할 Self-Signed Certificate와 Private Key를 만든다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;openssl req -x509 -newkey rsa:2048 -sha256 -nodes \
  -keyout server.key \
  -out server.crt \
  -days 1 \
  -subj '/CN=localhost' \
  -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' \
  -addext 'basicConstraints=critical,CA:TRUE'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 실습에서는 하나의 Self-Signed Certificate를 Server Certificate이자 Local Trust Anchor로 명시적으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Public Web PKI의 Root&amp;ndash;Intermediate&amp;ndash;Leaf 구성을 재현한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-nodes&lt;/code&gt;는 Private Key를 Password로 암호화하지 않는다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 실행을 단순화한 Local Lab 설정이며 파일을 외부에 공유하거나 운영 Server에 그대로 적용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Certificate Field를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;openssl x509 -in server.crt -noout \
  -subject -issuer -dates \
  -ext subjectAltName \
  -fingerprint -sha256&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 Terminal에서 TLS Server를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;openssl s_server \
  -accept 8443 \
  -cert server.crt \
  -key server.key \
  -www&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 Terminal에서 Certificate를 Local Trust Anchor로 지정하고 Hostname을 검증한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;openssl s_client \
  -connect 127.0.0.1:8443 \
  -servername localhost \
  -CAfile server.crt \
  -verify_hostname localhost \
  -verify_return_error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 다음을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Protocol
Cipher
Peer Certificate
Verification: OK 또는 Verify return code: 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenSSL Version에 따라 출력 표현은 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;curl&lt;/code&gt;로 HTTPS 요청을 보낸다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;curl --cacert server.crt https://localhost:8443/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--cacert&lt;/code&gt;는 이 명령에서 신뢰할 Certificate를 명시한다. 검증을 끄는 &lt;code&gt;-k&lt;/code&gt;와 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Hostname 검증 실패를 비교해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Certificate를 신뢰하더라도 접속하려는 이름이 SAN과 다르면 검증이 실패해야 한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;openssl s_client \
  -connect 127.0.0.1:8443 \
  -servername wrong.example \
  -CAfile server.crt \
  -verify_hostname wrong.example \
  -verify_return_error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은 실패를 관찰하기 위한 것이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Trust Anchor 일치
+ Certificate Signature 검증 가능
+ Hostname 불일치
= 전체 Identity 검증 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Certificate를 신뢰한다&amp;rdquo;와 &amp;ldquo;이 Certificate가 지금 접속한 이름에 유효하다&amp;rdquo;는 별도 조건임을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TLS Packet을 Capture해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server를 실행한 상태에서 세 번째 Terminal로 Loopback Packet을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 보통 &lt;code&gt;lo&lt;/code&gt;, macOS에서는 보통 &lt;code&gt;lo0&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;sudo tcpdump -n -i lo -s 0 -w tls-local.pcap \
  'tcp port 8443'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 &lt;code&gt;curl&lt;/code&gt;이나 &lt;code&gt;openssl s_client&lt;/code&gt;를 다시 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서 다음 순서를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TCP SYN, SYN-ACK, ACK
TLS ClientHello
TLS ServerHello
암호화된 TLS Record
TCP FIN 또는 RST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에 HTTP 응답 본문이 평문으로 보이지 않는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ClientHello의 SNI, ALPN Offer와 ServerHello 뒤 암호화 범위도 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenSSL과 curl의 Option, TLS Version에 따라 실제 Extension은 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습이 끝나면 Server를 종료하고 Private Key를 포함한 파일을 안전하게 삭제한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;rm -f server.key server.crt tls-local.pcap
cd ..
rmdir tls-local-lab&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 업무 Certificate와 Private Key를 실습 파일처럼 삭제하거나 공유하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TLS는 도청, 변조, 사칭에 대응하기 위해 Confidentiality, Integrity, Authentication을 결합한다.&lt;/li&gt;
&lt;li&gt;TCP Handshake, TLS Handshake와 HTTP 처리는 서로 다른 단계다.&lt;/li&gt;
&lt;li&gt;TLS는 Public Key Cryptography로 Identity와 Key Agreement를 연결하고 대칭키로 대량의 Record를 보호한다.&lt;/li&gt;
&lt;li&gt;ClientHello는 Version, Cipher, Key Share, SNI와 ALPN 후보를 제시하고 ServerHello는 사용할 조건과 Server Key Share를 선택한다.&lt;/li&gt;
&lt;li&gt;TLS 1.3에서는 ServerHello 뒤의 Handshake Message가 암호화되므로 Passive Capture에 Certificate가 보이지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;Certificate Chain은 Public Key와 Host Identity를 Trust Store의 Root에 연결한다.&lt;/li&gt;
&lt;li&gt;Client는 Chain Signature뿐 아니라 SAN Hostname, 유효 기간, Key Usage와 Trust Policy를 함께 검증한다.&lt;/li&gt;
&lt;li&gt;CertificateVerify는 Server가 Certificate Public Key에 대응하는 Private Key를 소유했음을 Handshake Transcript 서명으로 증명한다.&lt;/li&gt;
&lt;li&gt;Finished는 양쪽의 Handshake Secret과 Transcript가 일치하는지 확인한다.&lt;/li&gt;
&lt;li&gt;Ephemeral Key Exchange와 안전한 Secret 폐기는 장기 Private Key 유출이 과거 Session에 미치는 영향을 줄이는 Forward Secrecy를 제공한다.&lt;/li&gt;
&lt;li&gt;AEAD는 TLS Record의 Encryption과 암호학적 Integrity를 함께 제공한다.&lt;/li&gt;
&lt;li&gt;TLS Record, TCP Segment와 HTTP Message의 Boundary는 서로 같지 않다.&lt;/li&gt;
&lt;li&gt;mTLS의 Client Authentication과 Application Authorization은 별도 단계다.&lt;/li&gt;
&lt;li&gt;Session Resumption은 연결 비용을 줄이지만 0-RTT Early Data는 Replay 위험 때문에 요청 의미를 고려해야 한다.&lt;/li&gt;
&lt;li&gt;TLS는 IP&amp;middot;Port&amp;middot;Traffic 크기와 시간까지 모두 숨기지 않으며 Endpoint 침해와 Application 취약점을 해결하지 않는다.&lt;/li&gt;
&lt;li&gt;Self-Signed Certificate 경고를 무시하기보다 제한된 Trust Anchor와 Hostname 검증을 명시적으로 구성해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어 : Transport Layer Security, TLS 1.3, HTTPS, Confidentiality, Integrity, Authentication, ClientHello, ServerHello, EncryptedExtensions, SNI, ALPN, Cipher Suite, Key Share, Ephemeral Diffie-Hellman, Certificate, X.509, Certificate Chain, Root CA, Intermediate CA, Leaf Certificate, Trust Store, Subject Alternative Name, Hostname Verification, CertificateVerify, Finished, Handshake Transcript, Traffic Key, AEAD, Forward Secrecy, mTLS, Session Resumption, PSK, 0-RTT, Replay, TLS Termination&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 우체부를 믿게 만드는 기술이 아니다. 우체부가 편지를 운반할 수는 있어도 내용을 읽거나 몰래 바꾸기 어렵게 만들고, 받은 편지가 신뢰한 상대와 합의한 Key로 보호되었는지 Endpoint가 직접 검증하게 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 편지는 주소를 찾고, 경로를 지나고, 크기를 조절하고, 허용된 문을 통과해 암호화된 Channel 안에서 전달되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실제 장애를 만났다고 하자. 가장 먼저 실행하기 쉬운 명령은 &lt;code&gt;ping&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Echo Reply가 오지 않는다는 사실만으로 Server가 꺼졌다고 말할 수 있을까? 반대로 &lt;code&gt;ping&lt;/code&gt;이 성공하면 HTTPS와 Application도 정상이라고 말할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;ping이 실패하면 정말 서버가 죽은 것일까?&lt;/b&gt;」라는 질문을 통해 ICMP Echo, RTT, Packet Loss, Firewall Policy와 Layer별 진단 범위를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Certificate</category>
      <category>HTTPS</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>PSK</category>
      <category>Root CA</category>
      <category>tls</category>
      <category>Transport Layer Security</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1100</guid>
      <comments>https://peterica.tistory.com/1100#entry1100comment</comments>
      <pubDate>Sat, 25 Jul 2026 22:23:26 +0900</pubDate>
    </item>
    <item>
      <title>[바이브코딩] 01. AI와 일할 때 왜 문서가 필요할까? - SSOT, MECE, 컨텍스트</title>
      <link>https://peterica.tistory.com/1109</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HE9gv/dJMcaaTFU59/ncMTHldfY9sfrYvqLSkSL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HE9gv/dJMcaaTFU59/ncMTHldfY9sfrYvqLSkSL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HE9gv/dJMcaaTFU59/ncMTHldfY9sfrYvqLSkSL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHE9gv%2FdJMcaaTFU59%2FncMTHldfY9sfrYvqLSkSL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;좋은 문서는 대화를 보관하는 기록이 아니라, 다음 대화를 더 정확하게 만드는 컨텍스트다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 긴 프로젝트를 진행할 때 결과가 흔들리는 이유는 코딩 능력만의 문제가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 목적, 사용자 경험, 기술, 데이터, 기능을 한꺼번에 다루면 제안과 결정이 섞이고 무엇이 기준인지 모호해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD, UX, 기술 스택, 데이터베이스, 도메인, API는 서로 다른 결정을 나누어 담는 문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서들을 SSOT와 MECE 원칙으로 정리하면 사람과 AI가 같은 기준을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서가 할루시네이션을 완전히 제거하지는 않지만, 충돌하는 정보와 불필요한 추측이 발생할 가능성을 줄여 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 활용 방법에 대한 질문을 많이 받는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 AI에게 아이디어를 설명하고 &amp;ldquo;서비스로 만들어 줘&amp;rdquo;라고 요청하면 처음에는 놀라울 만큼 빠르게 결과가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면도 생기고, 기능도 붙고, 미처 생각하지 못했던 아이디어까지 제안받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 대화가 길어질수록 이상한 일이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 중요하다고 했던 목적이 흐려지고, 잠깐 검토했던 아이디어가 꼭 필요한 기능처럼 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 이야기를 하다가 데이터 저장 방식이 등장하고, 기술을 정하다가 서비스의 대상 사용자까지 다시 바뀌기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 줄이려면 프롬프트를 무조건 길게 써야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭 그렇지는 않다. 긴 프로젝트에서 더 중요한 것은 &lt;b&gt;결정을 주제별로 나누고, 확정된 내용을 문서로 남기는 일&lt;/b&gt;이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스를 만드는 일은 식당을 준비하는 일과 비슷하다. &lt;br /&gt;각 문서는 서로 다른 결정을 담당하고, 확정된 문서는 다음 작업의 기준이 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 개발 문서를 식당을 준비하는 과정에 비유해 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ AI에게 서비스를 한 번에 맡기면 왜 흔들릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;친구들과 할 수 있는 게임 서비스를 만들어 줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 문장에는 아직 정해지지 않은 것이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구를 위한 게임인지, 어떤 상황에서 사용하는지, 사용자는 어떤 화면을 보게 되는지, 어떤 정보를 저장해야 하는지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 대답을 만들기 위해 비어 있는 부분을 추측하거나 여러 가능성을 한꺼번에 제안할 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제안 자체가 문제는 아니다. 문제는 &lt;b&gt;제안과 결정이 섞이는 순간&lt;/b&gt; 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 선택하지 않은 아이디어가 다음 대화에서 확정된 요구사항처럼 쓰이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 단계의 이야기가 한 문서에 들어가면 무엇이 기준인지 알기 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;그래서 서비스를 만드는 과정을 몇 개의 문서로 나눈다. &lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;PRD, UX, 기술 스택, 데이터베이스, 도메인, API&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 많이 만들기 위해서가 아니라, 지금 어떤 질문에 답하고 있는지를 분명히 하기 위해서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개별 문서들이 왜 필요한지, 무엇을 위해 필요한지 이해를 돕기 위해 음식점에 비유를 들어 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 서비스를 만드는 것은 왜 식당을 준비하는 일과 비슷할까?&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyAA7x/dJMcaiqCuhr/5zsEJLaK6DaBxxasHq4yzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyAA7x/dJMcaiqCuhr/5zsEJLaK6DaBxxasHq4yzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyAA7x/dJMcaiqCuhr/5zsEJLaK6DaBxxasHq4yzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyAA7x%2FdJMcaiqCuhr%2F5zsEJLaK6DaBxxasHq4yzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식당을 준비한다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;식당 하나 만들어 주세요&amp;rdquo;라는 말만으로는 바로 문을 열 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 어떤 손님을 위한 어떤 식당인지 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손님이 보게 될 메뉴판과 주문 순서를 생각하고, 어떤 방식으로 요리할지 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재료를 보관할 창고와 분류 기준을 만들고, 메뉴마다 레시피도 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 문서는 어려운 기술 용어를 늘어놓기 위한 설명서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서로 다른 종류의 결정을 나누어 담는 그릇&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;어떤 식당인가?
      │
      ▼
손님은 어떻게 이용하는가?
      │
      ▼
어떤 방식으로 요리하는가?
      │
      ▼
재료를 어떻게 보관하고 분류하는가?
      │
      ▼
메뉴별로 어떻게 처리하는가?&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 46.0465%; height: 257px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;서비스 문서&lt;/td&gt;
&lt;td&gt;식당을 준비하는 일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PRD&lt;/td&gt;
&lt;td&gt;어떤 식당을 만들 것인가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UX&lt;/td&gt;
&lt;td&gt;손님에게 보여 줄 메뉴판과 음식 사진&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기술 스택&lt;/td&gt;
&lt;td&gt;한식&amp;middot;중식&amp;middot;일식과 같은 조리 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스&lt;/td&gt;
&lt;td&gt;음식 재료 창고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도메인&lt;/td&gt;
&lt;td&gt;재료를 종류별로 정리한 공간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;메뉴별 레시피&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 대응 관계를 알면 개발 지식이 많지 않아도 각 문서가 왜 필요한지 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ PRD &amp;mdash; 어떤 식당을 만들 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD는 어떤 식당을 만들지 정하는 문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구를 위한 식당인지, 어떤 음식을 제공할지, 손님에게 어떤 경험을 주고 싶은지를 먼저 정한다. 가족이 편하게 식사하는 한식당과 빠르게 한 끼를 해결하는 분식점은 메뉴와 공간, 운영 방식이 모두 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스도 목적과 대상 사용자가 먼저 정해져야 한다. Speed Quiz를 예로 들면, 중요한 질문은 어떤 기술을 쓸지가 아니다. &amp;ldquo;누가, 어떤 상황에서, 무엇을 더 편하게 하려고 이 서비스를 사용하는가?&amp;rdquo;가 먼저다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD가 정리되지 않은 상태에서 화면이나 기술부터 결정하면 멋진 결과는 나올 수 있어도 처음 해결하려던 문제와는 멀어질 수 있다. PRD는 이후의 모든 판단이 돌아와 확인할 출발점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ UX &amp;mdash; 손님에게 무엇을 보여 줄 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식당의 방향을 정했다면 이제 손님이 무엇을 보고 어떻게 이용할지를 생각한다. 메뉴판은 어디에 있는지, 주문은 어떤 순서로 하는지, 음식이 나오기까지 손님은 무엇을 보게 되는지 정하는 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 UX의 역할이다. UX는 화면을 예쁘게 꾸미는 일에만 머물지 않는다. 사용자가 서비스를 처음 만난 순간부터 목적을 달성할 때까지 어떤 행동을 하고 어떤 안내를 받는지 그려 보는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD가 &amp;ldquo;어떤 식당인가&amp;rdquo;에 답한다면 UX는 &amp;ldquo;손님이 그 식당을 어떻게 경험하는가&amp;rdquo;에 답한다. 이 단계에서는 조리도구나 재료 창고까지 결정하지 않는다. 손님의 경험이라는 한 가지 주제에 집중한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 기술 스택 &amp;mdash; 어떤 방식으로 요리할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 스택은 음식을 만드는 조리 방식과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한식, 중식, 일식은 필요한 도구와 재료를 다루는 방식이 다르다. 그렇다고 식당의 목적을 정하기도 전에 조리 방식부터 고르는 것은 자연스럽지 않다. 어떤 손님에게 무엇을 제공할지 정한 다음, 그 목적을 실현하기에 알맞은 방식을 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에서도 기술은 목적을 이루기 위한 수단이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유행하는 기술을 먼저 선택한 뒤 서비스의 모습을 거기에 맞추는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들고 싶은 경험이 정해진 뒤 그에 맞는 기술적 방향을 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비개발자가 모든 기술을 직접 비교할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;&amp;ldquo;왜 이 방식이 지금 만들려는 서비스에 맞는가?&amp;rdquo;&lt;/b&gt;라는 질문은 놓치지 않아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 데이터베이스 &amp;mdash; 재료를 어디에 보관할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스는 식당에서 사용하는 재료를 보관하는 창고와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재료를 아무 곳에나 쌓아 두지 않고, 필요한 순간에 찾고 사용할 수 있도록 정해진 기준에 따라 보관한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에도 계속 기억해야 하는 정보가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누가 참여했는지, 어떤 게임방인지, 어떤 문제가 나왔는지, 점수가 얼마인지 같은 정보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 정보를 어떤 형태로 저장하고 관리할지 정하는 것이 데이터베이스의 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 도메인 &amp;mdash; 재료를 어떻게 분류할 것인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인은 창고 안의 재료를 성격에 따라 나누는 기준과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채소, 고기, 해산물, 양념을 구분해 두면 필요한 재료를 쉽게 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Speed Quiz에서도 사용자, 게임방, 문제, 점수처럼 서로 관련 있는 정보를 묶어서 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스가 &amp;ldquo;정보를 보관하는 곳&amp;rdquo;이라면 도메인은 &amp;ldquo;그 정보를 어떤 의미의 묶음으로 이해할 것인가&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개념을 구분하면 저장할 정보와 서비스가 다루는 대상을 더 명확하게 바라볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ API &amp;mdash; 메뉴별 레시피&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API는 메뉴별 레시피와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주문이 들어오면 어떤 재료를 사용하고, 어떤 순서로 처리해, 어떤 결과를 내놓을지 정해져 있어야 한다. 같은 메뉴를 주문할 때마다 전혀 다른 음식이 나온다면 식당을 안정적으로 운영할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 기능도 비슷하다. 게임방 만들기, 참가하기, 게임 시작하기처럼 어떤 요청이 들어왔을 때 무엇을 확인하고 어떤 결과를 돌려줄지 약속이 필요하다. API는 기능들이 서로 요청과 결과를 주고받는 방식을 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 구체적인 규격이나 구현법까지 알 필요는 없다. API가 메뉴별 레시피처럼 &lt;b&gt;기능의 동작을 일정하게 이어 주는 약속&lt;/b&gt;이라는 점을 이해하면 충분하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 문서는 왜 다음 작업의 기준이 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서는 회의나 대화를 기록해 두는 메모에 그치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확정된 문서는 다음 작업이 시작될 기준이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식당의 콘셉트가 정리되어 있다면 메뉴판을 만드는 사람도, 주방을 설계하는 사람도 같은 식당을 상상할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;담당자가 바뀌더라도 매번 &amp;ldquo;어떤 식당을 만들고 있었지?&amp;rdquo;라고 처음부터 추측할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 일할 때도 마찬가지다. 문서는 다음과 같은 역할을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결정된 내용을 대화 밖에 보존한다.&lt;/li&gt;
&lt;li&gt;사람과 AI가 같은 기준을 보게 한다.&lt;/li&gt;
&lt;li&gt;다음 대화에서 다시 사용할 컨텍스트가 된다.&lt;/li&gt;
&lt;li&gt;현재 작업의 범위를 분명하게 한다.&lt;/li&gt;
&lt;li&gt;프로젝트가 임의로 확장되는 일을 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 문서는 이전 작업의 결과인 동시에 다음 작업의 입력이다. 한 단계에서 만든 문서가 다음 단계의 출발점이 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SSOT와 MECE는 왜 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 만든다고 해서 언제나 기준이 선명해지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 결정을 여러 문서에 조금씩 다르게 적거나, 서로 다른 주제를 한 문서에 모두 넣으면 오히려 혼란이 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 문서를 정리할 때 SSOT와 MECE라는 두 가지 원칙을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ SSOT &amp;mdash; 하나의 결정은 하나의 기준에서 관리한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSOT(Single Source of Truth)는 하나의 사실과 결정을 하나의 기준 문서에서 관리한다는 뜻이다. 식당의 콘셉트를 메뉴 기획 문서와 주방 문서에서 서로 다르게 정하지 않고, 하나의 확정된 기준을 함께 보는 것과 같다. 서비스의 목적과 요구사항은 PRD에서 관리하고, 다른 문서는 그 내용을 다시 정의하기보다 기준으로 삼는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ MECE &amp;mdash; 역할은 겹치지 않게, 내용은 빠짐없이 정리한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MECE(Mutually Exclusive, Collectively Exhaustive)는 역할은 겹치지 않게 나누되 필요한 내용은 빠짐없이 담는다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PRD는 목적과 요구사항, UX는 사용자의 경험, 기술 문서는 기술적 선택을 맡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 각 문서 안에서는 그 주제를 이해하고 다음 작업을 진행하는 데 필요한 내용이 빠지지 않도록 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원칙은 AI에게 매우 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준이 하나면 충돌하는 정보 중 무엇을 따라야 할지 해석하는 부담이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할이 나뉘고 필요한 내용이 갖춰져 있으면, 누락된 부분을 임의로 추측하거나 여러 문서의 내용을 억지로 결합할 가능성도 낮아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;다만 SSOT와 MECE가 할루시네이션을 완전히 제거하는 것은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 여전히 잘못 이해하거나 틀린 답을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 원칙은 AI가 참고할 기준을 더 선명하게 만들어 &lt;b&gt;불필요한 추측과 정보 충돌의 가능성을 줄이는 방법&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 좋은 문서는 왜 좋은 컨텍스트가 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 나눈 긴 대화에는 여러 종류의 정보가 섞여 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확정된 결정도 있지만, 잠깐 검토한 아이디어와 제외하기로 한 제안, 중간에 잘못 이해한 내용도 들어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화 전체를 다음 단계에 넘기면 AI는 그중 무엇이 진짜 기준인지 다시 추측해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 확정된 내용만 역할에 맞는 문서로 정리하면 다음 대화의 출발점이 분명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;문서는 이전 결정을 보존하는 기억이 되고, 다음 작업의 범위를 알려 주는 안내판이 되며, 사람과 AI가 함께 보는 공통 기준이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 좋은 문서는 다음 대화를 만든다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩에서 중요한 것은 처음부터 모든 개발 지식을 아는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 결정해야 할 주제를 구분하고, 그 결정을 문서로 남겨 다음 작업에 사용하는 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 문서는 대화를 보관하는 기록이 아니라, 다음 대화를 더 정확하게 만드는 컨텍스트다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 문서를 실제 AI 협업에서는 어떻게 사용해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 하나의 주제를 선택하고, 역할을 부여하고, Plan Mode에서 논의한 뒤,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확정된 내용만 다음 컨텍스트로 넘기는 방법을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 읽으면 좋은 글&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://peterica.tistory.com/1110&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[바이브코딩] 02. AI와 오래 협업하려면 어떻게 해야 할까? - Plan Mode, 문서화, 컨텍스트&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;nbsp;하나의 주제, 페르소나, Plan Mode, 문서화를 반복하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/1048&quot;&gt;Context Note &amp;mdash; AI 대화의 컨텍스트는 왜 &amp;ldquo;쌓지 않고 구조화&amp;rdquo;해야 할까?&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;ChatGPT와 함께 컨텍스트를 문서 중심으로 관리하기 위해 만든 도구이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;GPT Atlas에서 트리 구조의 노트, 마크다운 편집기, ChatGPT 대화창을 함께 사용하며 필요한 컨텍스트를 구조화할 수 있다.&lt;/p&gt;</description>
      <category>AI/바이브코딩</category>
      <category>MECE</category>
      <category>PETERICA</category>
      <category>ssot</category>
      <category>바이브코딩</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1109</guid>
      <comments>https://peterica.tistory.com/1109#entry1109comment</comments>
      <pubDate>Sat, 25 Jul 2026 13:00:12 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 22. 방화벽은 편지의 무엇을 보고 문을 열어줄까?</title>
      <link>https://peterica.tistory.com/1099</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dwkc7/dJMcai46BKe/kbDbtKsSVrlv26XecLzBPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dwkc7/dJMcai46BKe/kbDbtKsSVrlv26XecLzBPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dwkc7/dJMcai46BKe/kbDbtKsSVrlv26XecLzBPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDwkc7%2FdJMcai46BKe%2FkbDbtKsSVrlv26XecLzBPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 7. 인터넷의 주소를 함께 쓰고 경계를 지키는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network 경계에 경비실을 세웠다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경비원에게 &amp;ldquo;좋은 편지는 통과시키고 나쁜 편지는 막아 달라&amp;rdquo;고 말하면 Policy가 완성될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet에는 발신자의 의도가 적혀 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽이 직접 관찰할 수 있는 것은 다음과 같은 정보다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;어느 Interface와 Zone에서 들어왔는가?
Source와 Destination IP는 무엇인가?
TCP, UDP, ICMP 중 어떤 Protocol인가?
Source와 Destination Port는 무엇인가?
TCP Flag와 연결 상태는 무엇인가?
기존에 허용한 대화의 응답인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽은 이 증거를 미리 정의한 Rule과 비교해 &lt;b&gt;ALLOW&lt;/b&gt;, &lt;b&gt;DROP&lt;/b&gt;, &lt;b&gt;REJECT&lt;/b&gt; 같은 결정을 내린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 방화벽의 핵심은 &amp;ldquo;많이 막기&amp;rdquo;가 아니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;누가
어디에서 어디로
어떤 Protocol과 Service를
어느 방향으로
왜 사용해야 하는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 답할 수 있을 만큼 필요한 흐름을 정확히 정의하는 것이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;5-Tuple&lt;/b&gt;, &lt;b&gt;Stateless ACL&lt;/b&gt;, &lt;b&gt;Stateful Firewall&lt;/b&gt;, &lt;b&gt;Connection Tracking&lt;/b&gt;, &lt;b&gt;NEW&lt;/b&gt;, &lt;b&gt;ESTABLISHED&lt;/b&gt;, &lt;b&gt;RELATED&lt;/b&gt;, &lt;b&gt;INVALID&lt;/b&gt;, &lt;b&gt;Default Deny&lt;/b&gt;, &lt;b&gt;Least Privilege&lt;/b&gt;를 하나의 TCP 연결로 추적한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 방화벽은 Packet의 어느 층까지 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 방화벽이 같은 정보를 보는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 Packet Filter는 주로 Network Layer와 Transport Layer Header를 본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Layer 3
├─ Source IP
├─ Destination IP
└─ IP Protocol

Layer 4
├─ Source Port
├─ Destination Port
└─ TCP Flag 등&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비가 배치된 위치에 따라 Incoming Interface, Outgoing Interface, VLAN과 Security Zone도 판단 기준이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 높은 계층을 이해하는 Proxy, Web Application Firewall, IDS/IPS는 HTTP Method, URL, Protocol 문법 같은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 정보까지 분석할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Network Firewall
&amp;rarr; 주소, Protocol, Port, State 중심

Application-Aware Firewall / Proxy / WAF
&amp;rarr; Application Protocol과 요청 의미까지 추가 분석 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;방화벽&amp;rdquo;이라는 이름만으로 어떤 Field를 어디까지 해석하는지 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품의 처리 계층, 암호화 상태와 배치 방식을 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 5-Tuple은 어떤 대화를 가리킬까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Web Server에 연결한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client 10.22.1.2:53000
Server 198.51.100.2:443
Protocol TCP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow를 구분하는 5-Tuple은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source IP        10.22.1.2
Source Port      53000
Destination IP   198.51.100.2
Destination Port 443
Protocol         TCP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Return Packet에서는 Source와 Destination이 뒤집힌다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source IP        198.51.100.2
Source Port      443
Destination IP   10.22.1.2
Destination Port 53000
Protocol         TCP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Packet은 방향별 5-Tuple이 다르지만 하나의 양방향 대화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 Original Direction과 Reply Direction을 하나의 Connection Tracking Entry로 연결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Stateless ACL은 어떻게 판단할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stateless Access Control List&lt;/b&gt;(ACL)은 각 Packet을 독립적으로 Rule과 비교한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;현재 Packet의 Header
        &amp;darr;
Rule 목록과 비교
        &amp;darr;
Permit 또는 Deny&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 Client의 SYN을 허용했다는 사실을 다음 SYN-ACK 판단에 자동으로 기억하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 Server의 HTTPS로 나가는 Rule을 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Permit TCP
Source      10.22.1.0/24
Destination 198.51.100.2
Destination Port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Rule은 Client의 요청 방향에 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Server의 응답은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP 198.51.100.2:443
&amp;rarr;   10.22.1.2:53000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답을 받으려면 반대 방향 Rule도 필요하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Permit TCP
Source      198.51.100.2
Source Port 443
Destination 10.22.1.0/24
Destination Port Ephemeral Range&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 Ephemeral Port가 연결마다 달라지므로 Stateless Rule은 Return 범위를 넓게 열거나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Flag 같은 추가 조건을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 방향을 명시적으로 설계한다는 장점이 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 동적 연결에서 Rule 범위가 커지고 요청과 응답의 관계를 정확히 표현하기 어려울 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ACK Flag만 있으면 정상 응답이라고 믿어도 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거의 단순한 Stateless Rule은 TCP ACK가 설정된 Packet을 &amp;ldquo;이미 만들어진 연결의 응답&amp;rdquo;처럼 허용하기도 했다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TCP ACK=1
&amp;rarr; Established Traffic으로 간주&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Packet Header의 ACK Bit는 누구나 만들어 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽이 실제 3-Way Handshake를 관찰하지 않았다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 ACK가 정상 연결에 속하는지 ACK Flag 하나만으로 확정할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ACK Flag 존재
&amp;ne; 방화벽이 확인한 기존 Connection 존재&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Flag 검사는 유용한 조건이지만 Connection Tracking State를 그대로 대신하지는 못한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Stateful Firewall은 무엇을 기억할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 허용된 새 Flow의 Packet을 관찰하고 Connection Tracking State를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 SYN이 통과했다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Original Direction
TCP 10.22.1.2:53000 &amp;rarr; 198.51.100.2:443

Expected Reply Direction
TCP 198.51.100.2:443 &amp;rarr; 10.22.1.2:53000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명용 State Table은 다음처럼 보일 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;Original Tuple&lt;/td&gt;
&lt;td&gt;Reply Tuple&lt;/td&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;10.22.1.2:53000 &amp;rarr; 198.51.100.2:443&lt;/td&gt;
&lt;td&gt;198.51.100.2:443 &amp;rarr; 10.22.1.2:53000&lt;/td&gt;
&lt;td&gt;SYN 관찰&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 SYN-ACK이 돌아오면 Firewall은 5-Tuple을 뒤집어 기존 Entry의 Reply Direction과 맞는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;기존 요청의 정상 Reply로 확인
&amp;rarr; 별도의 넓은 Inbound Ephemeral Port Rule 없이 허용 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 ACK와 Data, FIN/RST를 관찰하면서 State와 Timeout을 갱신할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Conntrack의 NEW와 TCP의 SYN-SENT는 같은 상태일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux Netfilter 같은 환경에서는 Packet을 다음 &lt;b&gt;Conntrack State&lt;/b&gt;로 분류할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;NEW
ESTABLISHED
RELATED
INVALID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이름을 TCP State Machine의 &lt;code&gt;SYN-SENT&lt;/code&gt;, &lt;code&gt;ESTABLISHED&lt;/code&gt;, &lt;code&gt;TIME-WAIT&lt;/code&gt;와 그대로 같은 것으로 이해하면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TCP State
&amp;rarr; Endpoint TCP Stack의 연결 상태

Conntrack State
&amp;rarr; 중간 장비가 관찰한 Packet 관계 상태&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall이 모든 Packet을 보지 못했거나 Timeout과 구현 방식이 다르면 두 상태가 일치하지 않을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NEW는 반드시 첫 SYN만 뜻할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NEW&lt;/b&gt;는 기존 Conntrack Entry에 속하지 않으면서 새 Flow를 만들 수 있는 Packet을 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 TCP 연결의 시작에서는 보통 다음 SYN이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SYN=1, ACK=0
Conntrack State=NEW&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 구현과 Tracking 설정에 따라 SYN이 아닌 TCP Packet이 NEW로 분류될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Internet에서 내부 Server로 들어오는 새 TCP 연결을 허용할 때 다음처럼 의도를 더 구체적으로 표현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Protocol TCP
Destination Server와 Port 일치
Conntrack State NEW
TCP SYN 조건&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;NEW&lt;/code&gt;라는 이름만 보고 &amp;ldquo;정상적인 Handshake가 이미 검증됐다&amp;rdquo;고 생각하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ESTABLISHED는 Application 인증까지 끝났다는 뜻일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conntrack의 &lt;b&gt;ESTABLISHED&lt;/b&gt;는 일반적으로 양방향 Packet이 관찰되어 기존 Flow의 일부로 인식된다는 뜻이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client SYN 통과
Server SYN-ACK 관찰
&amp;rarr; Reply Direction 확인
&amp;rarr; ESTABLISHED로 분류 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 다음 사실을 보장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TLS 인증서 검증 완료
사용자 Login 성공
Application 요청이 안전함
Database 작업 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall의 ESTABLISHED와 Endpoint TCP의 ESTABLISHED, Application Session의 인증 완료는 서로 다른 계층의 상태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Rule의 &lt;code&gt;ESTABLISHED&lt;/code&gt;는 &amp;ldquo;이 Packet이 이미 추적 중인 양방향 Flow에 속한다&amp;rdquo;는 의미로 읽어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RELATED는 어떤 Packet을 가리킬까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RELATED&lt;/b&gt;는 새 Flow 또는 Control Packet이 기존 Conntrack Entry와 관련된 것으로 식별된 상태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예는 기존 Flow의 Packet을 인용한 ICMP 오류다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;기존 TCP Packet
&amp;rarr; Router에서 MTU 초과
&amp;rarr; ICMP Fragmentation Needed
&amp;rarr; 원래 Flow와 RELATED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 ICMP 안에 인용된 원래 Header를 보고 기존 Flow와 연결해 필요한 오류를 허용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 Protocol Helper는 Control Connection 안에서 협상된 별도 Data Connection을 RELATED로 연결할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Helper가 Payload를 해석하고 동적으로 Pin Hole을 여는 기능은 공격 표면도 늘릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 Protocol에만 제한하고 암호화된 Payload에서는 해석할 수 없다는 점도 고려해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ INVALID는 무조건 공격 Packet일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;INVALID&lt;/b&gt;는 정상적으로 추적할 수 없거나 기대한 Connection State와 맞지 않는 Packet을 뜻할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 원인은 다양하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 Conntrack Entry가 Timeout으로 사라진 뒤 늦게 온 Packet&lt;/li&gt;
&lt;li&gt;비정상적인 TCP Flag 또는 Sequence 관계&lt;/li&gt;
&lt;li&gt;Fragment Reassembly나 Tracking에 필요한 정보 부족&lt;/li&gt;
&lt;li&gt;Asymmetric Path 때문에 Firewall이 한 방향만 관찰&lt;/li&gt;
&lt;li&gt;손상되거나 해석할 수 없는 Packet&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INVALID를 Drop하는 Policy는 흔하지만 INVALID라는 결과만으로 외부 공격이라고 단정하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 Traffic이 INVALID로 분류되면 Path Symmetry, State Timeout, Fragment와 장비 Capacity를 함께 조사해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP에는 연결 상태가 없는데 Stateful Firewall은 어떻게 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP Protocol에는 TCP Handshake와 FIN이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 Stateful Firewall은 UDP Tuple과 시간을 바탕으로 임시 State를 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client 10.22.1.2:54000
&amp;rarr; DNS Server 198.51.100.53:53

State 생성
Expected Reply
198.51.100.53:53
&amp;rarr; 10.22.1.2:54000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대 방향 Packet이 일정 시간 안에 오면 기존 UDP Flow의 Reply로 허용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traffic이 없으면 UDP State는 Idle Timeout 후 사라진다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;UDP Conntrack State
&amp;ne; UDP Protocol이 TCP처럼 연결을 맺음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 장비가 편의를 위해 만든 &lt;b&gt;Pseudo-State&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application의 응답이 NAT&amp;middot;Firewall Timeout보다 늦으면 정상 Reply도 새롭거나 Invalid한 Traffic처럼 처리될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ICMP는 Port가 없는데 무엇으로 판단할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP에는 TCP&amp;middot;UDP Port가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽은 ICMP Type, Code, Source와 Destination, 인용된 원래 Packet과 Conntrack 관계를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Echo Request / Reply
Destination Unreachable
Time Exceeded
Fragmentation Needed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 ICMP를 하나의 Protocol 번호만 보고 허용하거나 차단하면 필요한 제어 기능까지 함께 영향을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 PMTUD에 필요한 Fragmentation Needed를 차단하면 MTU Black Hole이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Type과 Code, 방향과 관련 State를 기준으로 필요한 메시지를 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Rule은 어떤 순서로 읽힐까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 ACL과 Firewall Rule Set은 위에서 아래로 평가해 처음 일치한 Action을 적용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Allow TCP from Any to Server Port 443
2. Deny TCP from 203.0.113.0/24 to Server Port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 Rule이 먼저 일치하면 두 번째의 구체적인 Deny는 도달하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Rule Shadowing&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 구체적인 Rule과 예외를 앞에 두고 넓은 Rule을 뒤에 두는 방식이 흔한 이유다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Deny 예외 Source
2. Allow 승인된 Source 범위
3. Default Deny&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 플랫폼이 단순한 First Match 방식인 것은 아니다. Cloud Security Group처럼 여러 Allow Rule을 합산하거나 별도의 우선순위를 사용하는 제품도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 중인 Policy Engine의 평가 순서와 기본 Action을 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Default Deny는 왜 마지막에 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 Traffic을 하나씩 정의한 뒤 나머지를 허용하면 Policy의 빈틈을 예측하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Default Deny&lt;/b&gt;는 명시적으로 허용하지 않은 Traffic을 기본적으로 차단하는 원칙이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Allow: 내부 Client &amp;rarr; 승인된 DNS Resolver TCP/UDP 53
Allow: 내부 Client &amp;rarr; Web Proxy TCP 443
Allow: 기존 Flow의 ESTABLISHED, RELATED Reply
Deny : 그 밖의 Traffic&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 Service가 생기면 자동으로 열리는 것이 아니라 필요성과 범위를 검토한 뒤 Allow Rule을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Deny가 곧 좋은 Policy를 보장하는 것은 아니다. &lt;code&gt;Allow Any Any&lt;/code&gt;가 앞에 있으면 마지막 Deny는 의미가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Action과 실제 Allow 범위를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 최소 권한은 Port 하나만 좁히는 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Principle of Least Privilege&lt;/b&gt;는 업무에 필요한 최소 범위만 허용하는 원칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 차원을 함께 좁힌다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source
Destination
Protocol
Destination Port
Direction
Interface 또는 Zone
Connection State
필요한 시간과 운영 주체&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;Database Port 5432 허용&amp;rdquo;만으로는 부족하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source      Application Subnet
Destination Database Server
Protocol    TCP
Port        5432
Direction   App &amp;rarr; DB
State       NEW 허용, Return ESTABLISHED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 Flow를 문장으로 설명할 수 있어야 Rule의 목적과 삭제 조건도 관리할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Port 443이면 항상 HTTPS일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port Number는 Application을 배치하는 관례이자 Socket 식별 정보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 Application도 TCP 443에서 Listen할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Destination Port 443
&amp;ne; Payload가 반드시 정상 HTTPS
&amp;ne; 상대가 반드시 신뢰할 수 있는 Web Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Layer 4 Firewall이 Port 443을 허용한다는 것은 해당 TCP Endpoint로 연결할 수 있게 한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 문법, URL, 사용자 권한과 악성 Payload까지 검증하려면 Application 계층의 추가 통제가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port 기반 Rule은 중요한 경계이지만 Application Identity의 완전한 증명은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Source IP를 알면 사용자를 믿을 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source IP는 Routing과 Policy의 중요한 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사용자 Identity와 같지는 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NAT 뒤에서는 여러 사용자가 하나의 Public IP를 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;DHCP와 이동 환경에서는 한 사용자의 IP가 바뀔 수 있다.&lt;/li&gt;
&lt;li&gt;내부 Network에서도 Source Address Spoofing 가능성을 고려해야 한다.&lt;/li&gt;
&lt;li&gt;Proxy와 Load Balancer가 있으면 Server가 직접 보는 Source가 중간 장비일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source IP Allowlist는 범위를 줄일 수 있지만 강한 사용자 인증을 대신하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 계정, 인증서, Token, Application Authorization 같은 상위 계층 통제가 별도로 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DROP과 REJECT는 어떻게 다르게 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Policy에 맞지 않는 Packet을 조용히 버리면 &lt;b&gt;DROP&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Firewall : SYN
Firewall          : DROP
Client            : 응답을 기다리고 재전송
최종 결과         : Timeout 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거절 사실을 응답으로 알리면 &lt;b&gt;REJECT&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결에는 RST를, 다른 Traffic에는 적절한 ICMP 오류를 돌려보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Firewall : SYN
Firewall &amp;rarr; Client : TCP RST
최종 결과         : Connection Refused처럼 빠른 실패 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REJECT 응답도 Return Route가 없거나 중간에서 차단되면 Client에 도착하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP이 항상 더 안전하고 REJECT가 항상 위험하다는 단순한 규칙은 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 운영 환경에서는 빠른 REJECT가 장애 진단과 재시도 제어에 도움이 될 수 있다.&lt;/li&gt;
&lt;li&gt;외부 경계에서는 정보 노출, 응답 증폭, 운영 요구를 고려해 DROP을 선택할 수 있다.&lt;/li&gt;
&lt;li&gt;PMTUD 같은 필요한 ICMP 오류까지 조용히 버리면 정상 통신이 깨진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Policy 목적과 Protocol 동작을 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Ingress와 Egress Policy는 왜 둘 다 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-subtree=&quot;aimfl,mfl&quot;&gt;네트워크에서 &lt;/span&gt;&lt;/span&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;b&gt;Ingress(인그레스)&lt;/b&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;는 외부에서 시스템 내부로 들어오는 트래픽(인바운드)을 의미하며, &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;b&gt;Egress(이그레스)&lt;/b&gt;는 내부에서 외부로 나가는 트래픽(아웃바운드)을 뜻한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ingress Filtering&lt;/b&gt;은 경계로 들어오는 Traffic을 통제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Egress Filtering&lt;/b&gt;은 내부에서 외부로 나가는 Traffic을 통제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 공격만 생각하고 Egress를 모두 허용하면 다음 문제를 놓칠 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;침해된 Host가 외부 Command Server에 연결&lt;/li&gt;
&lt;li&gt;Data Exfiltration&lt;/li&gt;
&lt;li&gt;허가되지 않은 외부 DNS나 SMTP 사용&lt;/li&gt;
&lt;li&gt;내부 Source Address를 위조한 Packet의 유출&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;Ingress: 외부에서 무엇이 들어올 수 있는가?
Egress : 내부에서 무엇이 나갈 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 Default Deny로 시작할 수 있지만 운영에 필요한 Update, DNS, NTP, Monitoring과 제어 Protocol을 정확히 식별해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Host Firewall과 Network Firewall은 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network Firewall은 여러 Network나 Zone의 경계에서 Transit Traffic을 통제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Firewall은 개별 Server나 Client의 Network Stack 가까이에서 해당 Host의 Traffic을 통제한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Internet
   │
Network Firewall
   │
Server Network
   │
Host Firewall
   │
Application&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network 경계를 통과한 뒤 같은 Subnet의 다른 Host에서 오는 Traffic은 경계 Firewall을 다시 지나지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host Firewall은 최종 Workload 단위의 Policy를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽이 다른 쪽을 무조건 대체하는 것이 아니라 서로 다른 관찰 위치에서 방어 계층을 만든다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT와 Firewall Rule은 어느 주소를 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 장비가 NAT와 Firewall을 모두 수행하면 Packet이 어느 처리 단계에서 Rule과 만나는지가 중요하다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;DNAT 전 Destination: 203.0.113.5:8443
DNAT 후 Destination: 192.168.10.50:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall Rule이 NAT 전 Public Tuple을 기준으로 쓰이는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환 후 Private Tuple을 기준으로 쓰이는지는 플랫폼과 Hook 위치에 따라 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Policy에는 Public IP를 적었는데 실제 Rule은 DNAT 후 Address를 평가
&amp;rarr; 예상과 다른 Match&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Processing Order를 확인하지 않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT와 Firewall Rule을 별개 화면으로만 보면 허용과 차단 원인을 잘못 해석할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture도 NAT 전후 Interface에서 함께 해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Return Path가 다른 Firewall로 가면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall A가 Client의 SYN을 보고 State를 만들었다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Forward: Client &amp;rarr; Firewall A &amp;rarr; Server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing이 비대칭이라 SYN-ACK이 Firewall B로 돌아온다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Return: Server &amp;rarr; Firewall B &amp;rarr; Client&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall B가 A의 State를 공유하지 않으면 Reply Packet을 기존 Connection과 연결하지 못할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Firewall A: State 있음, Reply는 오지 않음
Firewall B: State 없음, SYN-ACK 도착
&amp;rarr; INVALID 또는 허용되지 않은 NEW처럼 처리 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 Firewall Cluster는 State Synchronization을 제공할 수 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 State와 전환 순간이 완벽히 동기화된다고 가정하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Middlebox가 있는 경로에서는 Routing Symmetry와 State Ownership이 설계 조건이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ State Table도 가득 찰 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 Flow마다 Memory와 Timer를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;동시 Connection 증가
Half-Open TCP 증가
UDP Mapping 증가
긴 Timeout
Fragment State 증가
&amp;rarr; State Table 자원 소비&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;State Table이 가득 차면 기존 Flow는 동작하지만 새 Connection이 실패하거나 장비 처리 성능이 급격히 떨어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN Flood처럼 마지막 ACK를 보내지 않는 요청은 Half-Open State를 쌓을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capacity Planning에는 최대 동시 Connection 수뿐 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초당 새 Connection 수, Timeout, 평균 Flow 수명과 Failover 동기화 비용도 포함해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful이 항상 Stateless보다 우월하다는 뜻은 아니다. 필요한 Policy 표현력과 State 비용을 함께 고려한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Fragment는 Port Rule을 어떻게 어렵게 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Fragment의 첫 조각에는 TCP 또는 UDP Header가 있지만 뒤 Fragment에는 Transport Port가 없을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Fragment 1: IP Header + TCP Header + Data
Fragment 2: IP Header + Data
Fragment 3: IP Header + Data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port 기반 Rule이 뒤 Fragment만 독립적으로 보면 어느 Service에 속하는지 알기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 Fragment를 추적하거나 Reassembly한 뒤 검사할 수 있지만 Memory와 처리 비용이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비정상 Offset과 겹치는 Fragment는 보안 장비와 Endpoint의 해석 차이를 만들 수 있어 엄격하게 차단될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;첫 Fragment가 허용됐으니 모든 뒤 Fragment도 무조건 안전하다&amp;rdquo;는 단순한 결론을 피해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 암호화된 Packet에서 방화벽은 무엇을 볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS로 Application Payload가 암호화되면 중간의 일반 Network Firewall은 HTTP 본문과 Password를 읽을 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 다음 Metadata는 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source와 Destination IP
TCP/UDP Port
Packet 크기와 시간
Connection State
일부 암호화되지 않았거나 별도로 노출되는 Handshake Metadata&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Inspection Proxy가 연결을 중간에서 종료하고 다시 암호화하면 Application 내용을 검사할 수 있지만 인증서 신뢰, Privacy, 성능과 Key 관리라는 새로운 책임이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽이 허용한 경로의 내용을 누가 읽을 수 있고 상대를 어떻게 믿을지는 다음 글의 TLS에서 이어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv4 Rule을 만들면 IPv6도 함께 보호될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플랫폼에 따라 IPv4와 IPv6 Rule Set, Address Object와 처리 도구가 분리될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IPv4 Inbound Default Deny 구성 완료
&amp;ne; IPv6 Inbound도 같은 Policy 적용 완료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host와 Network에 IPv6 Address와 Route가 있는데 IPv6 Policy를 확인하지 않으면 의도하지 않은 별도 경로가 열릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 IPv4에서 사용한 &amp;ldquo;ICMP는 모두 차단&amp;rdquo; 같은 Rule을 그대로 복사하면 IPv6의 정상 동작을 깨뜨릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6는 다음과 같은 ICMPv6 기능에 의존한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Neighbor Discovery의 Neighbor Solicitation과 Advertisement&lt;/li&gt;
&lt;li&gt;Router Solicitation과 Advertisement&lt;/li&gt;
&lt;li&gt;Path MTU Discovery의 Packet Too Big&lt;/li&gt;
&lt;li&gt;Time Exceeded와 Parameter Problem 같은 오류 보고&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메시지가 필요한 위치와 방향은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Neighbor Discovery는 같은 Link의 Neighbor를 찾는 Control Traffic이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router를 넘어 그대로 Forward하는 일반 Application Traffic이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 Firewall도 Default Deny와 최소 권한을 적용하되,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Protocol이 정상 동작하는 데 필요한 ICMPv6 Type과 Scope를 구분해 허용해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Logging은 모든 진실을 남길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall Log는 Rule Match와 진단에 중요한 증거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유용한 항목은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Timestamp
Action
Rule ID
Ingress / Egress Interface 또는 Zone
Source / Destination IP와 Port
Protocol
Conntrack State
Packet와 Byte Counter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 Packet을 무제한 기록하면 Log 처리 자체가 병목과 저장 비용을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 많은 Drop Packet을 보내 Logging 자원을 소모하게 할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rate Limiting, Flow 단위 Log, Sampling과 중앙 수집을 목적에 맞게 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Log가 없다고 Packet이 통과했다는 뜻은 아니고, Log가 있다고 Endpoint Application이 처리했다는 뜻도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter, Multi-point Capture, Conntrack State와 Application Log를 함께 연결해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Policy를 작성할 때 어떤 질문을 남겨야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule 한 줄마다 다음 질문에 답할 수 있어야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어떤 업무 Flow를 위해 필요한가?&lt;/li&gt;
&lt;li&gt;Source와 Destination을 더 좁힐 수 있는가?&lt;/li&gt;
&lt;li&gt;TCP와 UDP 중 어떤 Protocol인가?&lt;/li&gt;
&lt;li&gt;새 연결과 Return Traffic을 어떻게 구분하는가?&lt;/li&gt;
&lt;li&gt;ICMP와 DNS 같은 보조 Flow도 필요한가?&lt;/li&gt;
&lt;li&gt;Rule의 우선순위와 앞뒤 Shadowing은 없는가?&lt;/li&gt;
&lt;li&gt;언제 검토하고 삭제할 것인가?&lt;/li&gt;
&lt;li&gt;Drop 시 어떤 Log와 Counter로 검증할 것인가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 Firewall Policy는 Rule 수가 많은 Policy가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무의 필요한 통신을 최소 범위로 설명하고, 나머지 Traffic이 왜 허용되지 않는지 예측할 수 있는 Policy다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Header와 State로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;봉투마다 주소표만 보고 독립 심사&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Stateless ACL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이전 Packet을 기억하지 않고 현재 Header를 Rule과 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;발신&amp;middot;수신 주소와 창구 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;5-Tuple&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Protocol과 양쪽 IP&amp;middot;Port로 Flow를 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;처음 접수한 대화의 왕복표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Connection Tracking&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Original과 Reply Tuple, State와 Timeout 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;새 배송 요청&lt;/td&gt;
&lt;td&gt;&lt;b&gt;NEW&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기존 Entry에 속하지 않는 새 Flow 후보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미 왕복이 확인된 배송&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ESTABLISHED&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Conntrack이 양방향 Flow의 일부로 인식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 배송에서 생긴 반송 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;RELATED&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기존 Flow와 관련된 ICMP 오류나 보조 Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장부와 맞지 않는 봉투&lt;/td&gt;
&lt;td&gt;&lt;b&gt;INVALID&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정상적인 State로 추적할 수 없는 Packet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;명시하지 않은 배송은 기본 거절&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Default Deny&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Allow Rule에 없는 Traffic을 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필요한 주소와 창구만 최소 개방&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Least Privilege&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;업무에 필요한 Source&amp;middot;Destination&amp;middot;Protocol 범위만 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;말없이 편지를 폐기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;DROP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;응답 없이 Packet을 버려 Sender가 Timeout을 겪을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;거절 안내를 돌려보냄&lt;/td&gt;
&lt;td&gt;&lt;b&gt;REJECT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;TCP RST나 ICMP 오류로 거절을 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;앞의 넓은 규칙에 뒤 규칙이 가려짐&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Rule Shadowing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;평가 순서 때문에 뒤 Rule이 Match되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;들어오는 문과 나가는 문을 따로 관리&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Ingress / Egress Filtering&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;양방향 Traffic Policy를 각각 통제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 하나를 Stateful Policy로 판단하는 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client SYN
NEW + 승인된 Destination:443
        &amp;darr; Allow, State 생성

Server SYN-ACK
기존 Entry의 Reply Tuple + ESTABLISHED
        &amp;darr; Allow

Client ACK와 Data
기존 Entry의 Original Tuple + ESTABLISHED
        &amp;darr; Allow

FIN/RST 또는 Timeout
        &amp;darr; State 정리

그 밖의 새 Inbound Packet
명시적 Allow 없음
        &amp;darr; Default Deny&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet과 State를 직접 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark나 &lt;code&gt;tcpdump&lt;/code&gt;에서는 Firewall이 통과시키기 전과 후의 Packet을 비교한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client 쪽 Capture : SYN 전송 확인
Firewall 입구     : SYN 도착 확인
Firewall 출구     : SYN 전달 여부
Server 쪽 Capture : SYN 수신 여부&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 지점에서 Packet이 보이지 않는다는 사실만으로 바로 그 지점이 Drop했다고 단정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞 구간에서 도착했는지 먼저 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 Rule과 Counter는 환경에 따라 다음처럼 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;sudo iptables -L FORWARD -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conntrack 도구가 있다면 다음처럼 State를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo conntrack -L -p tcp
sudo conntrack -L -p udp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter가 증가하면 해당 Rule에 Packet이 Match됐다는 증거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 Packet이 Destination Application에서 정상 처리됐다는 증거는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 Stateful Policy를 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;가 있는 개인 Linux VM에서 수행한다. 실제 Host Firewall이 아니라 새 Network Namespace 안의 FORWARD Policy만 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;fw-client                  fw-router                  fw-server
10.22.1.2 ────────── 10.22.1.1 | 198.51.100.1 ───── 198.51.100.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add fw-client
sudo ip netns add fw-router
sudo ip netns add fw-server

sudo ip link add veth-fc type veth peer name veth-fwi
sudo ip link add veth-fwo type veth peer name veth-fs

sudo ip link set veth-fc netns fw-client
sudo ip link set veth-fwi netns fw-router
sudo ip link set veth-fwo netns fw-router
sudo ip link set veth-fs netns fw-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n fw-client addr add 10.22.1.2/24 dev veth-fc
sudo ip -n fw-router addr add 10.22.1.1/24 dev veth-fwi
sudo ip -n fw-router addr add 198.51.100.1/24 dev veth-fwo
sudo ip -n fw-server addr add 198.51.100.2/24 dev veth-fs

sudo ip -n fw-client link set lo up
sudo ip -n fw-router link set lo up
sudo ip -n fw-server link set lo up
sudo ip -n fw-client link set veth-fc up
sudo ip -n fw-router link set veth-fwi up
sudo ip -n fw-router link set veth-fwo up
sudo ip -n fw-server link set veth-fs up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 IPv4 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n fw-client route add default via 10.22.1.1
sudo ip -n fw-server route add default via 198.51.100.1
sudo ip netns exec fw-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 Namespace의 FORWARD Chain을 비우고 Default Policy를 DROP으로 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router iptables -F FORWARD
sudo ip netns exec fw-router iptables -P FORWARD DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INVALID를 차단하고, 기존 Flow의 ESTABLISHED&amp;middot;RELATED Packet을 허용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  iptables -A FORWARD \
  -m conntrack --ctstate INVALID -j DROP

sudo ip netns exec fw-router \
  iptables -A FORWARD \
  -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Network에서 Server의 TCP 8080으로 시작하는 새 연결만 허용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  iptables -A FORWARD \
  -i veth-fwi -o veth-fwo \
  -s 10.22.1.0/24 -d 198.51.100.2 \
  -p tcp --syn --dport 8080 \
  -m conntrack --ctstate NEW -j ACCEPT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule 순서와 Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  iptables -L FORWARD -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Terminal에서 Firewall 양쪽 Packet을 저장한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  tcpdump -n -vv -i any -s 0 -w /tmp/firewall-state.pcap \
  'tcp port 8080 or tcp port 9090'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 8080을 Listen한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-server nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Terminal에서 연결한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-client nc 198.51.100.2 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 SYN은 NEW Allow Rule과 Match한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 SYN-ACK은 Source Port 8080에서 Client의 임시 Port로 돌아오지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Entry의 ESTABLISHED Reply이므로 허용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;conntrack&lt;/code&gt;이 설치되어 있다면 연결 중 Tuple과 State를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router conntrack -L -p tcp&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DROP과 REJECT를 같은 경로에서 비교해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8080 연결을 종료한 뒤 Server가 9090에서 Listen하더라도 현재 Allow Rule에는 9090이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-server nc -l 9090&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 연결을 시도한다. &lt;code&gt;nc&lt;/code&gt; Timeout Option은 구현마다 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-client \
  nc -v -w 3 198.51.100.2 9090&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN이 Default DROP에 걸리면 응답이 없어 재전송 후 Timeout으로 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 9090을 명시적으로 REJECT하는 Rule을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  iptables -A FORWARD \
  -i veth-fwi -o veth-fwo \
  -p tcp --dport 9090 \
  -j REJECT --reject-with tcp-reset&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Client 명령을 다시 실행하면 Firewall이 보낸 RST 때문에 더 빠르게 실패할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 다음 차이를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;DROP   : Client SYN 반복, 응답 없음
REJECT : Client SYN 뒤 TCP RST 응답&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rule Counter와 순서도 다시 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec fw-router \
  iptables -L FORWARD -n -v --line-numbers&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다. 내부 Conntrack State와 Firewall Rule도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del fw-client
sudo ip netns del fw-router
sudo ip netns del fw-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Host의 기본 Firewall Policy를 실습 목적으로 바꾸지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방화벽은 Packet의 의도를 직접 아는 것이 아니라 Header, 방향, Zone, State를 Policy와 비교한다.&lt;/li&gt;
&lt;li&gt;5-Tuple은 Protocol과 Source&amp;middot;Destination IP 및 Port로 Flow를 구분한다.&lt;/li&gt;
&lt;li&gt;Stateless ACL은 각 Packet을 독립적으로 판단하므로 요청과 Return Traffic을 양방향 Rule로 표현해야 한다.&lt;/li&gt;
&lt;li&gt;TCP ACK Flag 하나는 방화벽이 관찰한 기존 Connection의 증거를 대신하지 못한다.&lt;/li&gt;
&lt;li&gt;Stateful Firewall은 Original Tuple과 Reply Tuple, State, Timeout을 Connection Tracking Entry로 기억한다.&lt;/li&gt;
&lt;li&gt;Conntrack의 NEW&amp;middot;ESTABLISHED는 Endpoint TCP State나 Application 인증 상태와 같은 개념이 아니다.&lt;/li&gt;
&lt;li&gt;RELATED는 기존 Flow에 관련된 ICMP 오류나 보조 Flow를 표현할 수 있고 INVALID는 정상 추적이 어려운 Packet이다.&lt;/li&gt;
&lt;li&gt;UDP Conntrack은 Handshake가 아니라 Tuple과 Timeout을 바탕으로 만든 중간 장비의 Pseudo-State다.&lt;/li&gt;
&lt;li&gt;Default Deny는 명시적인 Allow가 없는 Traffic을 차단하며 Least Privilege는 필요한 Source&amp;middot;Destination&amp;middot;Protocol 범위를 최소화한다.&lt;/li&gt;
&lt;li&gt;Rule 순서와 평가 방식에 따라 넓은 Rule이 구체적인 Rule을 가리는 Shadowing이 생길 수 있다.&lt;/li&gt;
&lt;li&gt;DROP은 응답 없이 버려 Timeout을 만들 수 있고 REJECT는 RST나 ICMP로 빠르게 거절을 알릴 수 있다.&lt;/li&gt;
&lt;li&gt;NAT, Firewall, Application 인증과 TLS는 각각 주소 변환, 통신 허용, 사용자 권한, 내용 보호라는 다른 문제를 해결한다.&lt;/li&gt;
&lt;li&gt;Stateful 장비는 Asymmetric Path, State Table 고갈, Fragment와 Timeout의 영향을 받는다.&lt;/li&gt;
&lt;li&gt;Firewall Log와 Rule Counter는 Match 증거이며 Destination Application의 처리 완료 증거는 아니다.&lt;/li&gt;
&lt;li&gt;IPv6에서는 Address 공유 NAT가 없어도 Firewall Policy가 필요하고 필수 ICMPv6까지 IPv4 Rule처럼 일괄 차단하면 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심&amp;nbsp;용어:&amp;nbsp;Firewall,&amp;nbsp;Packet&amp;nbsp;Filter,&amp;nbsp;Access&amp;nbsp;Control&amp;nbsp;List,&amp;nbsp;ACL,&amp;nbsp;Stateless&amp;nbsp;Firewall,&amp;nbsp;Stateful&amp;nbsp;Firewall,&amp;nbsp;5-Tuple,&amp;nbsp;Connection&amp;nbsp;Tracking,&amp;nbsp;Conntrack,&amp;nbsp;NEW,&amp;nbsp;ESTABLISHED,&amp;nbsp;RELATED,&amp;nbsp;INVALID,&amp;nbsp;Default&amp;nbsp;Deny,&amp;nbsp;Principle&amp;nbsp;of&amp;nbsp;Least&amp;nbsp;Privilege,&amp;nbsp;Rule&amp;nbsp;Order,&amp;nbsp;Rule&amp;nbsp;Shadowing,&amp;nbsp;ALLOW,&amp;nbsp;DROP,&amp;nbsp;REJECT,&amp;nbsp;Ingress&amp;nbsp;Filtering,&amp;nbsp;Egress&amp;nbsp;Filtering,&amp;nbsp;Host&amp;nbsp;Firewall,&amp;nbsp;Network&amp;nbsp;Firewall,&amp;nbsp;State&amp;nbsp;Table,&amp;nbsp;Asymmetric&amp;nbsp;Routing&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽은 좋은 의도를 판별하는 심판이 아니다. 관찰 가능한 주소&amp;middot;Port&amp;middot;방향&amp;middot;연결 상태를 명확한 Policy와 비교하는 장치다. 그래서 보안은 &amp;ldquo;무엇을 막을까&amp;rdquo;보다 &amp;ldquo;어떤 업무 흐름을 왜 허용해야 하는가&amp;rdquo;를 설명하는 일에서 시작한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall은 필요한 통신 경로를 선택적으로 열었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 통과가 허용된 Router와 ISP, Wi-Fi 운영자가 Packet 내용을 읽거나 바꾸지 않을 것이라고 믿을 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소와 Port를 허용하는 것만으로 상대가 진짜 Server인지, 전달 중 내용이 바뀌지 않았는지 어떻게 확인할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;우체부를 믿지 않고도 편지 내용을 지킬 수 있을까?&lt;/b&gt;」라는 질문을 통해 TLS Handshake, Certificate, Authentication, Key Exchange, Encryption과 Integrity가 서로 다른 문제를 어떻게 함께 해결하는지 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>firewall</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>Stateful firewall</category>
      <category>Stateless Firewall</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1099</guid>
      <comments>https://peterica.tistory.com/1099#entry1099comment</comments>
      <pubDate>Fri, 24 Jul 2026 22:22:44 +0900</pubDate>
    </item>
    <item>
      <title>[Container] Kubernetes에서 Docker 이미지 빌드는 왜 어려울까? - Daemon, Root, Privileged의 차이</title>
      <link>https://peterica.tistory.com/1108</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vrMlu/dJMcabZpAVi/ksovufif4IKHJzshZkf6P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vrMlu/dJMcabZpAVi/ksovufif4IKHJzshZkf6P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vrMlu/dJMcabZpAVi/ksovufif4IKHJzshZkf6P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvrMlu%2FdJMcabZpAVi%2Fksovufif4IKHJzshZkf6P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지를 빌드하는 작업은 단순히 파일을 압축하는 과정이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile의 명령을 실행하고, 파일시스템의 변경 사항을 추적하며, 이를 이미지 레이어로 만드는 작업이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 &lt;code&gt;docker build&lt;/code&gt;에서는 이 작업을 Docker Daemon이 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Docker Daemon은 일반적으로 호스트에서 root 권한으로 실행되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes Pod 안에서 실행하려면 &lt;code&gt;privileged&lt;/code&gt; 권한까지 요구하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Kubernetes 환경에서는 Docker Daemon을 Pod 안에 실행하는 DinD보다,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한된 권한으로 이미지를 만들 수 있는 전용 이미지 빌더가 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 &lt;a href=&quot;https://peterica.tistory.com/1106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Container] Harbor와 Kaniko는 왜 함께 사용할까? - 이미지는 만들고, 저장하고, 배포한다&lt;/a&gt; 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 이미지가 다음 세 단계를 거친다고 정리했다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Build
  │
  ▼
Store
  │
  ▼
Deploy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 이미지를 만드는 Builder이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harbor는 생성된 이미지를 저장하는 Registry이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 다음과 같이 간략히 설명했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes에서는 Docker Daemon을 실행하지 않는 환경이 많다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Kubernetes에서는 왜 Docker Daemon을 실행하기 어려울까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 Docker가 설치되어 있지 않기 때문일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 이해하려면 먼저 &lt;code&gt;docker build&lt;/code&gt; 명령 뒤에서 실제로 어떤 일이 일어나는지 살펴봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ docker build는 누가 실행할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 이미지를 만들 때 다음 명령을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker build -t my-app:1.0 .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 Docker CLI가 Dockerfile을 읽고 이미지를 만드는 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Docker CLI는 실제 빌드를 직접 수행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker CLI는 Docker Daemon에 빌드를 요청하는 클라이언트이다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;사용자
  │
  ▼
Docker CLI
  │
  │ Build 요청
  ▼
Docker Daemon
  │
  ├─ Dockerfile 해석
  ├─ Base Image 다운로드
  ├─ RUN 명령 실행
  ├─ COPY 파일 반영
  ├─ 파일시스템 변경 추적
  └─ Image Layer 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 일반적인 &lt;code&gt;docker build&lt;/code&gt; 구조는 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 47.2093%; height: 113px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;구성 요소&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;Docker CLI&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;사용자의 명령을 Docker Daemon에 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;Docker Daemon&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;컨테이너와 이미지 빌드 작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;Docker Registry&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;생성된 이미지를 저장하고 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker CLI는 요청만 전달하고, 실제 이미지 생성은 Docker Daemon이 담당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이미지 빌드는 단순한 파일 압축이 아니다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지는 여러 개의 읽기 전용 레이어로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile의 명령어가 실행될 때마다 파일시스템이 변경되고, 그 변경 사항이 새로운 레이어로 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 Dockerfile이 있다고 가정해보자.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM ubuntu:24.04

RUN apt-get update
RUN apt-get install -y curl

COPY app.sh /app/app.sh

CMD [&quot;/app/app.sh&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 과정에서는 다음과 같은 일이 발생한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;ubuntu:24.04
      │
      ▼
apt-get update 실행
      │
      ▼
변경된 파일을 Layer로 저장
      │
      ▼
curl 설치
      │
      ▼
변경된 파일을 Layer로 저장
      │
      ▼
app.sh 복사
      │
      ▼
변경된 파일을 Layer로 저장&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;code&gt;RUN&lt;/code&gt; 명령은 단순한 문자열이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 명령을 실행할 수 있는 격리 환경을 만들고, 실행 결과로 변경된 파일을 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌더는 이 과정에서 다음과 같은 작업을 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스 실행&lt;/li&gt;
&lt;li&gt;파일시스템 마운트&lt;/li&gt;
&lt;li&gt;파일 생성과 삭제&lt;/li&gt;
&lt;li&gt;소유권과 권한 변경&lt;/li&gt;
&lt;li&gt;네트워크 접근&lt;/li&gt;
&lt;li&gt;이미지 레이어 생성&lt;/li&gt;
&lt;li&gt;레이어 압축 및 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이미지 빌드는 일반 애플리케이션 실행보다 운영체제와 파일시스템에 더 가까운 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Docker Daemon은 왜 root 권한으로 실행될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Daemon은 컨테이너를 만들고 실행하기 위해 Linux 커널의 여러 기능을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 다음 기능들이 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 53.0233%; height: 189px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;커널 기능&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Namespace&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;프로세스, 네트워크, 파일시스템 격리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;cgroup&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;CPU와 메모리 등 자원 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;OverlayFS&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;이미지 레이어와 컨테이너 파일시스템 구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Capability&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;root 권한을 세분화하여 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Virtual Network&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;컨테이너 네트워크와 브리지 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 기능 중 일부는 일반 사용자 권한으로 사용할 수 없거나 추가적인 설정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 전통적인 Docker Daemon은 호스트에서 root 권한으로 실행된다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Host OS
  │
  └─ Docker Daemon
       └─ root 권한&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Daemon이 root 권한을 갖는다는 것은 Docker Daemon을 제어할 수 있는 사용자도 매우 강한 권한을 가질 수 있다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Docker Socket은 왜 위험할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker CLI는 일반적으로 Unix Socket을 통해 Docker Daemon과 통신한다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트에서 다음 명령을 실행하면 Docker Socket의 위치를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;ls -l /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes Pod 안에서 Docker 명령을 사용하기 위해 호스트의 Docker Socket을 마운트하는 방식이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 DooD(Docker out of Docker)라고 부른다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: docker-builder
spec:
  containers:
    - name: docker
      image: docker:cli
      volumeMounts:
        - name: docker-sock
          mountPath: /var/run/docker.sock  &amp;lt;== host docker 통신경로

  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Kubernetes Pod
      │
      ▼
/var/run/docker.sock
      │
      ▼
Host Docker Daemon
      │
      ▼
Host Container Runtime&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod 내부의 Docker CLI는 Pod 안에서 명령을 실행하지만, 실제 작업은 호스트의 Docker Daemon이 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;문제는 Docker Socket에 접근할 수 있는 프로세스가 호스트 Docker Daemon을 사실상 모두 제어할 수 있다는 점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 호스트 파일시스템을 컨테이너에 마운트할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;docker run \
  --rm \
  -it \
  -v /:/host \
  alpine \
  chroot /host&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령을 실행할 수 있다면 &lt;b&gt;컨테이너 내부에서 호스트 파일시스템에 접근&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Docker Socket을 Pod에 마운트하는 것은 단순한 파일 공유가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;호스트 Docker Daemon의 관리 권한을 Pod에 넘기는 것에 가깝다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DooD와 DinD는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 환경에서 Docker 이미지를 빌드할 때 자주 등장하는 방식은 DooD와 DinD이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;DooD: Docker out of Docker&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DooD는 Pod 안에서 호스트의 Docker Daemon을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;Pod
 │
 ├─ Docker CLI
 │
 └─ Host Docker Socket Mount
          │
          ▼
    Host Docker Daemon&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 구조가 단순하고 기존 Docker 명령을 그대로 사용할 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Pod가 호스트 Docker Daemon을 직접 제어하기 때문에 보안 위험이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;DinD: Docker in Docker&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DinD는 Pod 안에 별도의 Docker Daemon을 실행한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Pod
 │
 ├─ Jenkins Agent
 │
 └─ Docker Daemon
       │
       └─ Build Container&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 Docker Socket을 직접 공유하지 않는다는 점에서는 DooD와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Pod 내부에서 Docker Daemon이 컨테이너와 파일시스템을 제어하려면 강한 권한이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 다음 설정이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;securityContext:
  privileged: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DinD는 호스트 Docker Daemon을 공유하지 않지만, 대신 Pod의 격리 수준을 크게 낮추는 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Root와 Privileged는 같은 권한일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 보안을 이해할 때 가장 혼동하기 쉬운 개념이 &lt;b&gt;root&lt;/b&gt;와 &lt;b&gt;privileged&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 같은 의미가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;컨테이너 내부 Root&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서 UID 0으로 실행되는 상태이다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;securityContext:
  runAsUser: 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서는 파일 소유권 변경이나 패키지 설치 같은 작업을 수행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적인 컨테이너 격리가 유지된다면 호스트 전체를 직접 제어할 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Privileged 컨테이너&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Privileged 컨테이너는 일반 컨테이너에 적용되는 여러 보안 제한을 해제한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;securityContext:
  privileged: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Privileged 모드에서는 다음과 같은 권한이 확대될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 Linux Capability 사용&lt;/li&gt;
&lt;li&gt;호스트 장치 접근&lt;/li&gt;
&lt;li&gt;파일시스템 마운트&lt;/li&gt;
&lt;li&gt;네트워크 설정 변경&lt;/li&gt;
&lt;li&gt;커널 기능 접근&lt;/li&gt;
&lt;li&gt;보안 프로파일 제한 완화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이를 정리하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 62.093%; height: 205px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;컨테이너 내부 Root&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;Privileged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;UID&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;일반적으로 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;컨테이너 격리&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;유지 가능&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;크게 완화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;Host Device 접근&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;제한됨&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;광범위하게 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;커널 기능 사용&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;제한됨&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;대부분 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;보안 위험&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;상대적으로 제한적&lt;/td&gt;
&lt;td style=&quot;width: ;height: 19px;&quot;&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 문장은 정확하지 않다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Root 컨테이너와 Privileged 컨테이너는 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 정확하게 표현하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Privileged 컨테이너는 보통 root로 실행되지만, &lt;br /&gt;컨테이너 내부에서 root로 실행된다고 해서 모두 Privileged 컨테이너인 것은 아니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DinD는 왜 Privileged 권한을 필요로 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DinD의 Docker Daemon은 Pod 안에서 다시 컨테이너를 생성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 다음과 같은 작업이 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Namespace 생성&lt;/li&gt;
&lt;li&gt;cgroup 제어&lt;/li&gt;
&lt;li&gt;OverlayFS 마운트&lt;/li&gt;
&lt;li&gt;컨테이너 네트워크 생성&lt;/li&gt;
&lt;li&gt;iptables 설정&lt;/li&gt;
&lt;li&gt;프로세스 격리&lt;/li&gt;
&lt;li&gt;파일시스템 레이어 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적인 Kubernetes Pod는 이러한 커널 기능을 자유롭게 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DinD는 보통 다음과 같이 구성된다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: dind-builder
spec:
  containers:
    - name: docker
      image: docker:dind
      securityContext:
        privileged: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정을 사용하면 Docker Daemon은 동작하기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 Kubernetes가 제공하는 컨테이너 격리의 상당 부분을 포기하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, DinD의 문제는 Docker Daemon이 Pod 안에 있다는 사실만이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Docker Daemon을 실행하기 위해 Pod에 과도한 권한을 부여해야 한다는 점이 더 큰 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Kubernetes의 보안 정책과 충돌한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes에는 Pod가 사용할 수 있는 권한을 제한하기 위한 Pod Security Standards가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 보안 수준은 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 40.6977%; height: 113px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;정책&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Privileged&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;대부분의 권한 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Baseline&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;일반적인 위험 설정 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Restricted&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;최소 권한 원칙을 강하게 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DinD에서 사용하는 &lt;code&gt;privileged: true&lt;/code&gt; 설정은 Baseline과 Restricted 정책에서 허용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Namespace에 Restricted 정책이 적용되어 있다면 다음과 같이 설정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Namespace
metadata:
  name: ci
  labels:
    pod-security.kubernetes.io/enforce: restricted&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Namespace에 Privileged DinD Pod를 생성하면 Pod 자체가 거부될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Error from server (Forbidden):
pod violates PodSecurity &quot;restricted&quot;:
privileged container is not allowed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 정책이 강화된 Kubernetes에서는 DinD 파이프라인이 실패하는 이유가 Docker 명령 오류가 아닐 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 시작되기 전에 Pod 생성 단계에서 거부될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Rootless와 Daemonless도 구분해야 한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드 도구를 비교할 때 다음 세 가지 표현이 자주 등장한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Daemonless
Rootless
Unprivileged&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷해 보이지만 서로 다른 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Daemonless&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상주하는 Docker Daemon 없이 이미지를 빌드한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Dockerfile
   │
   ▼
Builder Process
   │
   ▼
Container Image&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Rootless&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 프로세스나 관련 런타임을 비root 사용자로 실행한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;User ID != 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Unprivileged&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes Pod에 &lt;code&gt;privileged: true&lt;/code&gt;를 설정하지 않고 실행한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;securityContext:
  privileged: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이를 정리하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 63.2558%; height: 139px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;개념&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Daemonless&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;상주하는 Docker Daemon 없이 빌드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Rootless&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;비root 사용자 권한으로 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Unprivileged&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Privileged 컨테이너 없이 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Daemonless라고 해서 반드시 Rootless인 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Root로 실행되더라도 Privileged 권한이나 호스트 Docker Socket 없이 동작하는 빌더가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;따라서 이미지 빌더를 선택할 때는 단순히 &amp;ldquo;Docker Daemon을 사용하지 않는다&amp;rdquo;는 설명만 확인해서는 부족하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 항목을 함께 확인해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너 내부에서 root로 실행되는가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;privileged: true&lt;/code&gt;가 필요한가?&lt;/li&gt;
&lt;li&gt;호스트 Socket을 마운트하는가?&lt;/li&gt;
&lt;li&gt;HostPath를 사용하는가?&lt;/li&gt;
&lt;li&gt;Linux Capability를 추가하는가?&lt;/li&gt;
&lt;li&gt;Seccomp와 AppArmor를 비활성화하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Kaniko는 무엇을 바꿨을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 Docker Daemon 없이 Dockerfile을 해석하고 컨테이너 이미지를 생성하는 빌더이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 Docker 빌드 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Docker CLI
    │
    ▼
Docker Daemon
    │
    ▼
Build Container
    │
    ▼
Container Image&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko의 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Dockerfile
    │
    ▼
Kaniko Executor
    │
    ├─ Base Image 추출
    ├─ Dockerfile 명령 실행
    ├─ 파일시스템 변경 탐색
    ├─ Image Layer 생성
    └─ Registry Push&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 각 Dockerfile 명령이 실행된 후 파일시스템의 변경 사항을 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 파일을 기준으로 스냅샷을 생성하고, 이를 이미지 레이어로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 과정에서는 Docker Daemon을 실행하지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같은 구성이 필요하지 않다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;securityContext:
  privileged: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 호스트 Docker Socket을 마운트할 필요도 없다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;/var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kaniko가 Kubernetes CI/CD 환경에서 사용된 핵심 이유는 단순히 Docker 명령을 대체했기 때문이 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Docker Daemon과 Privileged 권한에 의존하던 이미지 빌드 구조를 변경했기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Kaniko가 Root 권한 문제를 모두 해결한 것은 아니다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko를 설명할 때 다음과 같이 표현하기도 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 Rootless 이미지 빌더이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 표현은 주의해서 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko의 핵심은 다음 두 가지이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker Daemon을 요구하지 않는다.&lt;/li&gt;
&lt;li&gt;Privileged 컨테이너를 요구하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Dockerfile에 따라 컨테이너 내부에서 root 권한이 필요한 작업은 여전히 존재할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 명령은 파일의 소유권이나 시스템 경로를 변경할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;RUN apt-get update
RUN apt-get install -y curl
RUN chown -R app:app /app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Kaniko의 특징은 다음처럼 표현하는 것이 더 정확하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 Docker Daemon과 Privileged 컨테이너 없이 이미지를 빌드할 수 있도록 설계된 이미지 빌더이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 컨테이너 내부의 UID가 항상 0이 아닌가가 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 컨테이너가 호스트 커널과 런타임에 어느 정도의 권한을 갖는가이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 빌드 방식별 권한 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 환경에서 사용할 수 있는 대표적인 빌드 구조를 비교하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Docker Daemon&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Docker Socket&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Privileged&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;주요 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Host Docker&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;로컬 사용&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Host root&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;빌드 서버 자체 권한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;DooD&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Host Daemon 사용&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;보통 불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Host Docker 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;DinD&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Pod 내부 실행&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;일반적으로 필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Pod 격리 약화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Daemonless Builder&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;일반적으로 불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;빌더별 권한 검토 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Rootless Builder&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;도구에 따라 다름&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;불필요&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;커널과 파일시스템 호환성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 Docker Socket을 제거했다고 보안 문제가 모두 해결되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Docker Daemon을 사용한다고 해서 모든 구성이 동일하게 위험한 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 다음 세 가지이다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Daemon이 어디에서 실행되는가?
          │
          ▼
어떤 사용자 권한으로 실행되는가?
          │
          ▼
Host 자원에 얼마나 접근할 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Kubernetes 이미지 빌더를 선택하는 기준&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes에서 이미지 빌더를 선택할 때는 기능과 성능뿐 아니라 권한 모델을 함께 확인해야 한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 54.6512%; height: 426px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;확인 항목&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;질문&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Daemon&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;별도의 빌드 Daemon이 필요한가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Root&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;비root 사용자로 실행할 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Privileged&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;privileged: true가 필요한가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Socket&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Host Docker Socket을 마운트하는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Storage&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;OverlayFS나 FUSE 같은 기능이 필요한가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Security Profile&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Seccomp와 AppArmor를 유지할 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Cache&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;원격 캐시를 사용할 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Registry&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;결과 이미지를 직접 Registry에 Push할 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Compatibility&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;기존 Dockerfile을 그대로 사용할 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;Maintenance&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;현재도 유지보수되고 있는 도구인가?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이미지 빌더의 선택은 &amp;ldquo;어떤 도구가 가장 빠른가?&amp;rdquo;만의 문제가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;보안 정책, 빌드 속도, Dockerfile 호환성, 캐시 구조, 운영 난이도를 함께 고려해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Dockerfile은 같은데 빌드 방식은 달라질 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌더가 달라지더라도 Dockerfile은 동일하게 사용할 수 있는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM eclipse-temurin:21-jre

COPY app.jar /app/app.jar

ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;/app/app.jar&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Dockerfile을 실행하는 방식은 서로 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Docker
  └─ Docker Daemon이 Dockerfile 실행

DinD
  └─ Pod 내부 Docker Daemon이 Dockerfile 실행

Kaniko
  └─ Executor가 Dockerfile 실행 후 Snapshot 생성

BuildKit
  └─ LLB 그래프로 변환하여 빌드 작업 실행

Buildah
  └─ OCI 기반 도구로 컨테이너와 이미지를 구성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Dockerfile은 이미지의 조립 방법을 정의하는 문서이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 빌더가 그 문서를 실행하는지는 별도의 문제이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 문제는 Docker 명령이 아니라 권한 구조다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes에서 Docker 이미지 빌드가 어려운 이유는 &lt;code&gt;docker build&lt;/code&gt; 명령을 사용할 수 없기 때문이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드는 다음과 같은 시스템 작업을 포함하기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스 실행&lt;/li&gt;
&lt;li&gt;파일시스템 마운트&lt;/li&gt;
&lt;li&gt;레이어 생성&lt;/li&gt;
&lt;li&gt;네트워크 접근&lt;/li&gt;
&lt;li&gt;파일 권한 변경&lt;/li&gt;
&lt;li&gt;컨테이너 격리 환경 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 Docker는 이러한 작업을 root 권한의 Docker Daemon에 맡겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 PC나 전용 빌드 서버에서는 이 구조가 자연스럽게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Kubernetes에서는 빌드 작업도 일반적인 Pod로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod 안에서&lt;br /&gt;&amp;nbsp; 다시 Docker Daemon을 실행하려면 P&lt;b&gt;rivileged 권한이 필요&lt;/b&gt;하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 호스트 &lt;b&gt;Docker Socket을 공유하면 호스트 제어 권한이 노출&lt;/b&gt;될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;전통적인 방식

Docker CLI
    │
    ▼
Root Docker Daemon
    │
    ▼
Container Image&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Kubernetes에서 필요한 방향

Build Pod
    │
    ▼
제한된 권한의 Image Builder
    │
    ▼
Container Registry&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Kubernetes 이미지 빌드의 핵심 질문은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Daemon을 사용할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다 더 정확한 질문은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드에 필요한 권한을 어떤 프로세스에, 어느 범위까지 허용할 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko와 같은 빌더가 필요했던 이유도 여기에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Daemon을 단순히 다른 위치로 옮긴 것이 아니라, Kubernetes의 보안 모델에 맞게 이미지 빌드 방식을 다시 설계한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 보면 좋은 사이트&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b13ci0/dJMcabrwhjI/9qyaGEdukXY1iRpRd5Ygdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b13ci0/dJMcabrwhjI/9qyaGEdukXY1iRpRd5Ygdk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b13ci0/dJMcabrwhjI/9qyaGEdukXY1iRpRd5Ygdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb13ci0%2FdJMcabrwhjI%2F9qyaGEdukXY1iRpRd5Ygdk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;259&quot; height=&quot;676&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;span data-state=&quot;closed&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://tech.osci.kr/kaniko-vs-dind-kubernetes-image-build/?utm_source=chatgpt.com&quot;&gt;오픈소스컨설팅 기술 블로그 글 보기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;509&quot; data-start=&quot;269&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 컨테이너 이미지 빌드 방식 비교&lt;/p&gt;
&lt;p data-end=&quot;509&quot; data-start=&quot;269&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 보안 &amp;bull; 성능 &amp;bull; 운영 관점에서 Kaniko를 권장하는 이유&lt;/p&gt;
&lt;p data-end=&quot;509&quot; data-start=&quot;269&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://nangman14.tistory.com/92&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컨테이너&amp;nbsp;빌드&amp;nbsp;도구&amp;nbsp;선택을&amp;nbsp;위한&amp;nbsp;특성&amp;nbsp;및&amp;nbsp;성능&amp;nbsp;비교&amp;nbsp;(Kaniko,&amp;nbsp;Buildah,&amp;nbsp;Buildkit)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Docker</category>
      <category>DinD</category>
      <category>docker</category>
      <category>DooD</category>
      <category>kaniko</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1108</guid>
      <comments>https://peterica.tistory.com/1108#entry1108comment</comments>
      <pubDate>Fri, 24 Jul 2026 16:26:10 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 21. 여러 집이 하나의 공인 주소를 함께 쓸 수 있을까?</title>
      <link>https://peterica.tistory.com/1098</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uCSfj/dJMcaiRE5eT/luaSgmWfTLzKbPaBVM2SqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uCSfj/dJMcaiRE5eT/luaSgmWfTLzKbPaBVM2SqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uCSfj/dJMcaiRE5eT/luaSgmWfTLzKbPaBVM2SqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuCSfj%2FdJMcaiRE5eT%2FluaSgmWfTLzKbPaBVM2SqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 7. 인터넷의 주소를 함께 쓰고 경계를 지키는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집 안에는 Phone, Notebook, TV, Game Console처럼 많은 기기가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 기기는 서로 다른 IPv4 주소를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Phone       192.168.10.10
Notebook    192.168.10.20
TV          192.168.10.30
Game Console 192.168.10.40&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 인터넷에서 이 집을 바라보면 여러 연결이 하나의 공인 IPv4 주소에서 온 것처럼 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Internet에서 보이는 Source IP
203.0.113.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 내부 주소가 어떻게 하나의 외부 주소를 동시에 사용할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소만 똑같이 바꾸면 답장이 왔을 때 어느 기기로 돌려줘야 하는지 구분할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;203.0.113.5로 답장이 도착
&amp;rarr; Phone의 대화인가?
&amp;rarr; Notebook의 대화인가?
&amp;rarr; TV의 대화인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정용 Router가 흔히 수행하는 기능은 주소만 바꾸는 단순한 NAT가 아니다. Address와 Transport Port를 함께 바꾸고 그 대응 관계를 기억하는 &lt;b&gt;NAPT&lt;/b&gt;, 또는 &lt;b&gt;PAT&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Private IPv4 Address&lt;/b&gt;, &lt;b&gt;NAT&lt;/b&gt;, &lt;b&gt;NAPT/PAT&lt;/b&gt;, &lt;b&gt;SNAT&lt;/b&gt;, &lt;b&gt;DNAT&lt;/b&gt;, &lt;b&gt;Translation Table&lt;/b&gt;, &lt;b&gt;Connection Tracking&lt;/b&gt;, &lt;b&gt;Port Forwarding&lt;/b&gt;, &lt;b&gt;CGNAT&lt;/b&gt;를 실제 왕복 Packet으로 추적한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 기기에 전 세계 고유 주소를 줄 수 없었을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Address는 32비트다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^32 = 4,294,967,296&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 43억 개의 Bit Pattern이 있지만 전부 일반 Host에 자유롭게 배정할 수 있는 것은 아니다. Network 운영과 특수 목적을 위한 범위가 있고, Internet에 연결되는 장비와 서비스는 계속 늘어났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 주소 부족을 완화하기 위해 내부 Network에서는 전 세계적으로 유일하지 않아도 되는 &lt;b&gt;Private Address&lt;/b&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFC 1918에서 정의한 범위는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.0.0.0/8
172.16.0.0/12
192.168.0.0/16&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 집과 회사가 똑같이 &lt;code&gt;192.168.10.10&lt;/code&gt;을 사용해도 내부 Network가 분리되어 있으면 충돌하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;집 A의 Notebook  192.168.10.10
집 B의 Notebook  192.168.10.10

각자 내부에서는 서로 다른 Network&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Private Address는 공용 Internet에서 전역적으로 Route되는 주소로 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Host가 Internet Server와 통신하려면 경계 장비가 외부에서 Route 가능한 주소로 바꾸는 과정이 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 공인 주소와 사설 주소는 보안 등급일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Public Address&lt;/b&gt;는 공용 Internet에서 전역적으로 Route할 수 있도록 배정&amp;middot;광고되는 주소를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Private Address&lt;/b&gt;는 제한된 내부 Network에서 재사용하도록 예약한 범위다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Public / Private
&amp;rarr; Routing Scope의 차이
&amp;rarr; 신뢰도나 암호화 등급의 차이가 아님&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Address가 있다고 누구나 해당 Host에 접속할 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall Policy와 Service Listen 상태가 별도로 허용되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private Address를 사용한다고 Packet 내용이 자동으로 암호화되거나 내부 Host가 안전해지는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글의 예시에 사용하는 &lt;code&gt;192.0.2.0/24&lt;/code&gt;, &lt;code&gt;198.51.100.0/24&lt;/code&gt;, &lt;code&gt;203.0.113.0/24&lt;/code&gt;는 문서 예시용 주소 범위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 공인 주소처럼 인터넷 서비스에 사용하기 위한 값은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT는 정확히 무엇을 바꿀까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Network Address Translation&lt;/b&gt;(NAT)은 경계 장비가 IP Packet의 Address를 다른 Address로 바꾸는 기능을 넓게 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 형태는 하나의 내부 주소를 하나의 외부 주소에 대응시키는 1:1 변환이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;내부 10.0.0.10
&amp;harr; 외부 203.0.113.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address Pool에서 사용할 외부 주소를 동적으로 배정할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;내부 Host 여러 개
&amp;harr; 외부 Address Pool 여러 개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 하나의 공인 IPv4 Address를 여러 내부 Host가 동시에 공유하려면 IP Address만으로는 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport Port까지 함께 사용해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT와 NAPT, PAT는 같은 말일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Network Address and Port Translation&lt;/b&gt;(NAPT)은 IP Address와 TCP&amp;middot;UDP Port를 함께 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Port Address Translation(PAT)&lt;/b&gt; 또는 Port Overload라고 부르기도 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NAT
└─ Address 변환을 가리키는 넓은 표현

NAPT / PAT
└─ Address와 Port를 함께 변환해 다수 연결을 공유&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정용 Router에서 흔히 &amp;ldquo;NAT를 한다&amp;rdquo;고 말할 때 실제 동작은 NAPT인 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용어를 지나치게 엄격히 나누지 않는 제품도 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Host가 하나의 공인 주소를 어떻게 동시에 쓰는지 설명하려면 Port 변환을 빼놓을 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 내부 Client 한 대가 HTTPS 연결을 시작하면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notebook이 외부 Web Server의 443 Port로 TCP 연결을 시작한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Notebook
192.168.10.20:51514

NAT Router의 Public Address
203.0.113.5

Web Server
198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 전 내부 Packet의 5-Tuple은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Protocol        TCP
Source IP       192.168.10.20
Source Port     51514
Destination IP  198.51.100.20
Destination Port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router는 외부에서 사용할 Source Port 40001을 배정했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은&amp;nbsp;구현은&amp;nbsp;먼저&amp;nbsp;원래&amp;nbsp;Source&amp;nbsp;Port&amp;nbsp;51514를&amp;nbsp;그대로&amp;nbsp;쓰려고&amp;nbsp;시도하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은&amp;nbsp;공인&amp;nbsp;주소에서&amp;nbsp;그&amp;nbsp;Port가&amp;nbsp;이미&amp;nbsp;다른&amp;nbsp;연결에&amp;nbsp;쓰이는&amp;nbsp;등&amp;nbsp;충돌이&amp;nbsp;있을&amp;nbsp;때만&amp;nbsp;40001처럼&amp;nbsp;다른&amp;nbsp;Port로&amp;nbsp;재배정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 설명을 위해 바뀐 경우를 예로 든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;변환 전: 192.168.10.20:51514
변환 후: 203.0.113.5:40001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부로 나가는 Packet은 다음처럼 보인다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP 203.0.113.5:40001
&amp;rarr;   198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination은 그대로이고 Source Address와 Source Port가 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 Source 기준으로 보면 &lt;b&gt;Source NAT&lt;/b&gt;(SNAT)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 Address뿐 아니라 Source Port까지 함께 바꾸므로, 앞서 정의한&amp;nbsp;&lt;b&gt;NAPT/PAT&lt;/b&gt;에도 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 이름은 층위가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SNAT는&amp;nbsp;&lt;b&gt;어느 쪽 주소를 바꾸는가&lt;/b&gt;(방향)를, NAPT는&amp;nbsp;&lt;b&gt;Port까지 바꿔 다수 연결을 공유하는가&lt;/b&gt;(기법)를&amp;nbsp;가리킨다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Router는 답장을 위해 무엇을 기억할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router는 변환하면서 대응 관계를 상태로 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명용 Translation Table의 한 행은 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Protocol&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;내부 Endpoint&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;외부에서 보이는 Endpoint&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Remote Endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;TCP&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;192.168.10.20:51514&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;203.0.113.5:40001&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;198.51.100.20:443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 NAT가 Mapping을 구분하는 범위는 구현과 동작 방식에 따라 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Protocol, 내부 Endpoint, Remote Endpoint, Connection State 등이 함께 사용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 외부 Tuple을 원래 내부 Tuple로 되돌릴 정보를 기억한다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;203.0.113.5:40001로 돌아온 TCP 응답
&amp;rarr; 192.168.10.20:51514로 복원&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태가 없으면 공인 주소만 보고 어느 내부 Host와 Socket에 답장을 전달할지 알 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Server의 답장은 어떻게 원래 Notebook으로 돌아갈까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web Server는 자신이 본 Source로 응답한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP 198.51.100.20:443
&amp;rarr;   203.0.113.5:40001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet이 NAT Router에 도착하면 Translation Table을 조회한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Protocol TCP
Destination 203.0.113.5:40001

Mapping 발견
&amp;rarr; 내부 192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환된 내부 방향 Packet은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TCP 198.51.100.20:443
&amp;rarr;   192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;나갈 때 Source를 바꾼 것이 SNAT였다면,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;돌아올 때는 저장해 둔 대응 관계로&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Destination을 원래 내부 Endpoint로 되돌린다&lt;/b&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;방향으로 보면 이쪽은 목적지를 바꾸는 변환&lt;/span&gt;(&lt;span&gt;Destination NAT,&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;DNAT에 해당&lt;/span&gt;)&lt;span&gt;이다.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;하나의 연결에서 나갈 때와 돌아올 때가 서로 대칭을 이룬다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왕복 흐름을 한 번에 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Notebook                 NAT Router                   Web Server

192.168.10.20:51514
        ────────────────&amp;gt; 203.0.113.5:40001
                              ───────────────────────&amp;gt; 198.51.100.20:443

                              &amp;lt;─────────────────────── 198.51.100.20:443
        &amp;lt;──────────────── 203.0.113.5:40001
192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Server는 원래 Private Address를 보지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router의 Public Address와 변환된 Port를 Client Endpoint로 인식한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 Client가 같은 Source Port를 사용하면 충돌하지 않을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Phone과 Notebook이 우연히 같은 Source Port 51514를 사용해 같은 Web Server에 연결한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Phone    192.168.10.10:51514 &amp;rarr; 198.51.100.20:443
Notebook 192.168.10.20:51514 &amp;rarr; 198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router는 외부 Port를 다르게 배정할 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;내부 Endpoint&lt;/td&gt;
&lt;td&gt;외부에서 보이는 Endpoint&lt;/td&gt;
&lt;td&gt;Remote Endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;192.168.10.10:51514&lt;/td&gt;
&lt;td&gt;203.0.113.5:40001&lt;/td&gt;
&lt;td&gt;198.51.100.20:443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;192.168.10.20:51514&lt;/td&gt;
&lt;td&gt;203.0.113.5:40002&lt;/td&gt;
&lt;td&gt;198.51.100.20:443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 응답 Destination Port가 40001이면 Phone으로, 40002이면 Notebook으로 복원한다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;203.0.113.5라는 Address는 공유
40001과 40002라는 Port로 Mapping을 구분&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAPT가 하나의 Public Address를 다수의 동시 Flow로 확장하는 핵심 원리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT가 항상 원래 Source Port를 바꾸는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌이 없고 정책이 허용하면 같은 Port를 보존할 수 있으며 필요할 때 다른 Port를 선택할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Address와 Port를 바꾸면 Checksum은 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header의 Source 또는 Destination Address가 바뀌면 IPv4 Header Checksum도 다시 맞아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP와 0이 아닌 UDP Checksum 계산에는 IP Source와 Destination을 포함한 Pseudo Header가 관여한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port까지 바뀌므로 Transport Checksum도 영향을 받는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NAT가 바꾸는 값
├─ IPv4 Source 또는 Destination Address
├─ TCP/UDP Source 또는 Destination Port 가능
├─ IPv4 Header Checksum 갱신
└─ TCP/UDP Checksum 갱신&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 구현은 전체를 처음부터 다시 계산하거나 변경분을 이용해 효율적으로 갱신할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 UDP에서 Checksum Field가 0이면 Checksum을 사용하지 않는다는 특별한 의미이므로 별도로 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 Packet 겉면만 별도의 표에 기록하는 장치가 아니다. 전달 중인 Packet Header를 실제로 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 암호학적으로 Header 변경까지 보호하는 Protocol이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Payload 안에 Address를 직접 넣는 Application은 NAT와 추가 조정이 필요할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Translation Table은 언제 만들어지고 사라질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적인 NAPT Mapping은 영구히 남지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP에서는 SYN, ACK, FIN, RST 같은 Flag와 Connection State를 관찰해 Mapping의 수명을 관리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;TCP SYN 관찰
&amp;rarr; 새 Mapping 생성

ESTABLISHED Traffic
&amp;rarr; State 갱신

FIN/RST 또는 긴 Idle
&amp;rarr; 일정 조건 후 Mapping 제거&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP에는 TCP Handshake와 FIN이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 같은 Tuple의 Packet이 일정 시간 보이지 않으면 UDP Mapping을 만료시키는 식으로 상태를 관리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;UDP Packet 관찰
&amp;rarr; Mapping 생성

Idle Timeout 경과
&amp;rarr; Mapping 제거&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 Timeout은 장비, Protocol, Connection State와 정책에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 NAT Mapping을 오래 유지하려고 Keepalive를 사용할 수 있지만 지나치게 잦은 Keepalive는 Battery, Traffic, NAT State를 소비한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT Rule과 Connection Tracking은 같은 표일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 같은 Stateful NAT 구현을 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Rule은 어떤 Traffic을 어떤 방식으로 변환할지 정한다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;내부 Prefix에서 외부 Interface로 나가는 Packet
&amp;rarr; 외부 Address로 Source NAT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 Packet이 Rule과 Connection Tracking을 통해 Mapping을 만들면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Flow의 뒤 Packet과 Return Packet에는 저장된 상태가 일관되게 적용된다.&lt;/p&gt;
&lt;pre class=&quot;julia&quot;&gt;&lt;code&gt;NAT Rule
&amp;rarr; 변환 정책

Conntrack Entry
&amp;rarr; 현재 Flow의 원래 Tuple, 응답 Tuple, State와 변환 관계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Rule 목록의 Packet Counter만 보고 현재 활성 Mapping을 모두 알 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 환경에 따라 &lt;code&gt;conntrack&lt;/code&gt; 도구로 활성 Connection Tracking Entry를 관찰할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Port도 유한하다면 무한히 공유할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP와 UDP Port Field는 각각 16비트다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0부터 65,535까지의 값&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 값을 동적 변환에 자유롭게 사용하는 것도 아니며 이미 사용 중인 Mapping, 예약 범위와 정책이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Public Address가 만들 수 있는 동시 Mapping에는 한계가 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;내부 Client 수 증가
&amp;times; Client당 동시 Connection 증가
&amp;times; 목적지와 Protocol 조합
&amp;rarr; NAT State와 외부 Port 소비 증가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 가능한 외부 Port와 Connection Tracking 자원이 부족해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 Mapping을 만들지 못하는 상황을 &lt;b&gt;Port Exhaustion&lt;/b&gt; 또는 NAT Resource Exhaustion이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증상은 기존 연결은 유지되지만 새로운 연결만 간헐적으로 실패하는 모습으로 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비는 Public Address Pool을 늘리거나 Mapping 재사용 정책, Timeout, Port Block 할당과 Capacity를 조정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &amp;ldquo;Public IP 하나면 내부 Host 수가 무한해진다&amp;rdquo;는 뜻은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 외부에서 먼저 연결하면 왜 내부 Host를 찾지 못할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Client가 다음 Packet을 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Destination 203.0.113.5:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router에 해당 Port의 기존 Mapping도, 정적인 변환 Rule도 없다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;203.0.113.5:8080
&amp;rarr; 어느 내부 Address와 Port인가?
&amp;rarr; 알 수 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적인 NAPT는 보통 내부에서 먼저 시작한 Flow를 보며 Mapping을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 갑자기 도착한 Packet은 원래 내부 목적지를 복원할 근거가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Server를 외부에 공개하려면 명시적인 정적 Mapping을 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Public 203.0.113.5:8443
&amp;rarr; Private 192.168.10.50:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Port Forwarding&lt;/b&gt;이라고 흔히 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Packet의 Destination을 내부 Address와 Port로 바꾸므로 &lt;b&gt;Destination NAT&lt;/b&gt;(DNAT)의 한 형태다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;외부 Client &amp;rarr; 203.0.113.5:8443
NAT Router  &amp;rarr; 192.168.10.50:443으로 DNAT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port Forwarding Rule이 있어도 Firewall이 Forward Traffic을 허용하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Server가 Listen하며 Return Route가 성립해야 실제 연결이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SNAT와 DNAT는 방향 이름일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SNAT와 DNAT는 단순히 &amp;ldquo;밖으로 나감&amp;rdquo;과 &amp;ldquo;안으로 들어옴&amp;rdquo;의 이름이라기보다 어느 Address Field를 바꾸는지에 초점을 둔다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;SNAT
&amp;rarr; Source Address를 변환

DNAT
&amp;rarr; Destination Address를 변환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Internet Access에서는 내부 Packet의 Source를 Public Address로 바꾸는 SNAT가 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port Forwarding이나 Load Balancing에서는 외부 Packet의 Destination을 내부 Server로 바꾸는 DNAT가 사용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Return Traffic에는 저장된 State에 따라 반대 변환이 적용된다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Forward 방향 SNAT
&amp;harr; Return 방향에서 Destination 복원

Forward 방향 DNAT
&amp;harr; Return 방향에서 Source 복원&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet 방향만 보고 SNAT와 DNAT를 외우기보다 바뀌는 Header Field를 확인하는 편이 정확하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 내부 Host가 Public Address로 자기 Server에 접속하면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 DNS나 설정이 Server의 Public Address &lt;code&gt;203.0.113.5:8443&lt;/code&gt;을 알려 준다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Client가 그 주소로 접속하면 Packet이 NAT Router에 갔다가 다시 내부 Server로 돌아와야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;192.168.10.20
&amp;rarr; 203.0.113.5:8443
&amp;rarr; NAT Router
&amp;rarr; 192.168.10.50:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Hairpin NAT&lt;/b&gt;, &lt;b&gt;NAT Loopback&lt;/b&gt;, NAT Reflection이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비가 해당 동작을 지원하고 DNAT와 Return Path를 일관되게 처리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원하지 않으면 외부에서는 Public Address로 접속되지만 같은 내부 Network에서는 실패할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 DNS가 내부 Address를 따로 응답하는 Split DNS도 한 가지 설계 방법이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT가 있으면 Firewall은 필요 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 NAPT는 Mapping이 없는 외부 Packet을 어느 내부 Host로 보낼지 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 특성 때문에 외부에서 먼저 시작한 연결이 자연스럽게 제한되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 NAT와 Firewall은 목적이 다르다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NAT
&amp;rarr; Address와 Port를 변환

Firewall
&amp;rarr; Policy에 따라 Traffic을 허용하거나 차단&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Rule이 곧 보안 허용 Rule인 것도 아니고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소를 변환하지 않는 Routed Network에서도 Stateful Firewall을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 잘못된 Port Forwarding과 넓은 Firewall 허용 Rule이 있으면 NAT 뒤의 Service도 외부에 노출될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;NAT가 있으니 안전하다  (X)
Private Address이니 신뢰해도 된다  (X)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 Firewall이 Packet의 어떤 정보를 보고 별도의 허용 결정을 내리는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT가 내용을 숨겨 주는 암호화일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Server는 내부 Private Address 대신 공유 Public Address를 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 Packet 내용이 암호화되는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;NAT가 숨기는 것
&amp;rarr; 외부 Packet Header에서 원래 Private Address

NAT가 숨기지 않는 것
&amp;rarr; 암호화되지 않은 Application Payload
&amp;rarr; 접속한 Destination
&amp;rarr; Traffic 크기와 시간
&amp;rarr; Cookie와 계정 같은 Application 식별자&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Public Address를 공유한다고 사용자들이 서로 암호학적으로 익명화되는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용의 기밀성, 상대 인증과 무결성은 TLS 같은 별도 보안 Protocol이 맡는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT는 End-to-End 모델을 어떻게 바꿨을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 IP의 기본 모델에서는 두 Endpoint가 서로 Route 가능한 Address를 사용해 직접 Packet을 주고받는 모습을 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAPT가 중간에 들어오면 Address와 Port가 경계에서 바뀌고 Mapping State가 생긴다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;End Host ── Stateful NAT ── End Host
              │
              └─ Translation State 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 변화는 여러 문제를 만든다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 Peer가 내부 Host로 먼저 연결하기 어렵다.&lt;/li&gt;
&lt;li&gt;Return Traffic이 Mapping을 가진 NAT로 돌아와야 한다.&lt;/li&gt;
&lt;li&gt;Mapping Timeout보다 긴 Idle Connection은 끊길 수 있다.&lt;/li&gt;
&lt;li&gt;Payload 안에 IP와 Port를 기록하는 Protocol은 Header 변환만으로 동작하지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;Fragment의 뒤 조각에는 Transport Port가 없어 상태 연결이 복잡하다.&lt;/li&gt;
&lt;li&gt;NAT가 여러 겹이면 어느 경계에서 Mapping이 만들어졌는지 추적하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 Protocol은 Application Layer Gateway(ALG), Port Mapping, STUN, TURN, ICE 같은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보조 기술로 NAT Traversal을 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 IPv4 주소 공유를 가능하게 했지만 중간 Network가 End-to-End 대화 상태를 기억하게 만들었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ CGNAT를 지나면 무엇이 하나 더 생길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-complete=&quot;true&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;b&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 700; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-subtree=&quot;aimfl,mfl&quot;&gt;CGNAT(Carrier-Grade NAT)&lt;/span&gt;&lt;/b&gt;는 &lt;/span&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-complete=&quot;true&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;인터넷 서비스 제공업체(ISP)가 부족한 공인 IPv4 주소를 아끼기 위해, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-complete=&quot;true&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;여러 명의 사용자에게 하나의 공인 IP를 공유하게 만드는 대규모 네트워크 주소 변환 기술이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정이나 사무실에서 공유기가 사설 IP를 할당하듯, 통신사(ISP)가 한 번 더 사설 IP를 할당하여 인터넷으로 내보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정 Router가 한 번 NAT하고 Internet Service Provider가 다시 NAT할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Home Private Address
        &amp;darr; Home NAT
ISP Shared Address
        &amp;darr; Carrier-Grade NAT
Public Internet Address&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Carrier-Grade NAT(CGNAT)&lt;/b&gt; 또는 Large Scale NAT라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGNAT 내부에서 사용할 수 있도록 &lt;code&gt;100.64.0.0/10&lt;/code&gt; Shared Address Space가 예약되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 범위는 RFC 1918 Private Address 세 범위에 포함되지 않으며 일반 Public Internet용 주소도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGNAT 환경에서는 다음 제약이 커질 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 ISP NAT의 Port Forwarding을 직접 설정하기 어렵다.&lt;/li&gt;
&lt;li&gt;하나의 Public Address를 더 많은 가입자가 공유해 Port 자원이 제한될 수 있다.&lt;/li&gt;
&lt;li&gt;장애와 Abuse 추적에는 Public IP뿐 아니라 Source Port와 정확한 시간이 함께 필요하다.&lt;/li&gt;
&lt;li&gt;VPN, Game, P2P와 Inbound Service가 NAT Traversal에 더 의존한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Public Address만 기록하면 어느 가입자의 어느 Mapping인지 구분하지 못할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv6에도 주소 공유 NAT가 꼭 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6는 매우 큰 Address Space를 제공하므로 IPv4 주소 부족 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Host가 하나의 주소와 Port를 공유해야 했던 이유가 약해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Host가 전역적으로 Route 가능한 IPv6 Address를 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 Firewall이 불필요해지는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Address 공유 필요 감소
&amp;ne; 모든 Inbound Traffic 허용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateful Firewall은 NAT 없이도 내부에서 시작한 Flow의 Return Traffic을 허용하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부의 새 연결을 Policy로 제한할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT66 같은 IPv6 Address 변환도 존재하지만 IPv4 주소 고갈 완화를 위한 NAPT와 같은 필요성으로 이해하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Addressing, Routing, Security Policy는 서로 다른 문제다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Header와 State로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;건물 안에서만 사용하는 호수&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Private IPv4 Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;내부 Network에서 재사용하는 RFC 1918 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;외부 우편망에서 보이는 대표 주소&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Public IPv4 Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;공용 Internet에서 Route 가능한 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;봉투의 주소를 대표 주소로 교체&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;NAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;IP Source 또는 Destination Address 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;대표 주소와 접수 번호를 함께 교체&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;NAPT / PAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Address와 TCP&amp;middot;UDP Port를 함께 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;발신 주소를 외부 주소로 변경&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;SNAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Packet의 Source Address 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;수신 주소를 내부 Server로 변경&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;DNAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Packet의 Destination Address 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;원래 세대와 외부 접수 번호의 대응표&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Translation Table&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;외부 Tuple을 내부 Tuple로 복원할 Mapping State&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;외부 창구 번호를 내부 Server에 고정 연결&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Port Forwarding&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Public Address와 Port를 Private Endpoint로 DNAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;답장이 같은 번역소로 돌아옴&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Path Symmetry&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Mapping State가 있는 NAT가 Return Traffic 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;외부 접수 번호가 모두 사용됨&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Port Exhaustion&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;새 동적 Mapping에 사용할 Port나 State 부족&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;통신사가 한 번 더 대표 주소로 변환&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;CGNAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;다수 가입자가 ISP의 Public Address Pool을 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;같은 건물에서 외부 주소로 되돌아 접속&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Hairpin NAT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;내부 Client가 NAT의 Public Endpoint를 통해 내부 Server 접속&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 왕복을 한 번 더 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;내부에서 나감
192.168.10.20:51514
        &amp;darr; SNAT + Port Translation
203.0.113.5:40001
        &amp;darr;
198.51.100.20:443

외부에서 돌아옴
198.51.100.20:443
        &amp;darr;
203.0.113.5:40001
        &amp;darr; Translation State 조회
192.168.10.20:51514&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서 무엇을 비교할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 장비의 내부와 외부 Interface를 동시에 Capture하면 같은 Flow의 변환 전후를 비교할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Inside Capture
192.168.10.20:51514 &amp;rarr; 198.51.100.20:443

Outside Capture
203.0.113.5:40001 &amp;rarr; 198.51.100.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 Field는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ip.src
ip.dst
tcp.srcport 또는 udp.srcport
tcp.dstport 또는 udp.dstport
ip.checksum
tcp.checksum 또는 udp.checksum&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark의 &lt;code&gt;tcp.stream&lt;/code&gt;은 하나의 Capture 안에서 관련 TCP Packet을 따라가는 데 유용하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 양쪽 Capture를 별도로 열면 같은 End-to-End 대화가 서로 다른 Tuple로 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽 Capture의 시간, TCP Sequence Number, Flag와 Payload 길이를 함께 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router에서 &lt;code&gt;any&lt;/code&gt; Interface로 Capture하면 같은 Packet이 변환 전후 Interface에서 두 번 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 Packet 복제로 오해하지 않고 Capture Interface 정보와 Tuple을 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 NAT를 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;가 있는 개인 Linux VM에서 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Documentation Address와 Network Namespace만 사용하므로 실제 Network의 Address나 Firewall을 바꾸지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nat-client                nat-router                 nat-server
10.21.0.2 ───────── 10.21.0.1 | 198.51.100.1 ───── 198.51.100.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add nat-client
sudo ip netns add nat-router
sudo ip netns add nat-server

sudo ip link add veth-nc type veth peer name veth-nri
sudo ip link add veth-nro type veth peer name veth-ns

sudo ip link set veth-nc netns nat-client
sudo ip link set veth-nri netns nat-router
sudo ip link set veth-nro netns nat-router
sudo ip link set veth-ns netns nat-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n nat-client addr add 10.21.0.2/24 dev veth-nc
sudo ip -n nat-router addr add 10.21.0.1/24 dev veth-nri
sudo ip -n nat-router addr add 198.51.100.1/24 dev veth-nro
sudo ip -n nat-server addr add 198.51.100.2/24 dev veth-ns

sudo ip -n nat-client link set lo up
sudo ip -n nat-router link set lo up
sudo ip -n nat-server link set lo up
sudo ip -n nat-client link set veth-nc up
sudo ip -n nat-router link set veth-nri up
sudo ip -n nat-router link set veth-nro up
sudo ip -n nat-server link set veth-ns up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 IPv4 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n nat-client route add default via 10.21.0.1
sudo ip -n nat-server route add default via 198.51.100.1
sudo ip netns exec nat-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Prefix에서 외부 Interface로 나가는 Traffic에 MASQUERADE를 적용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-router \
  iptables -t nat -A POSTROUTING \
  -s 10.21.0.0/24 -o veth-nro -j MASQUERADE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router Capture Terminal에서 변환 전후를 함께 저장한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-router \
  tcpdump -n -vv -i any -s 0 -w /tmp/nat-translation.pcap \
  'tcp port 8080 or tcp port 18080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 Listen한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-server nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Terminal에서 연결하고 짧은 문장을 입력한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-client nc 198.51.100.2 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 다음 두 Source를 비교한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;Router Inside Interface : 10.21.0.2:&amp;lt;client-port&amp;gt;
Router Outside Interface: 198.51.100.1:&amp;lt;translated-port&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 Port를 그대로 유지할 수도 있으므로 항상 Port가 달라질 것이라고 가정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Rule Counter를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-router \
  iptables -t nat -L POSTROUTING -n -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;conntrack&lt;/code&gt; 도구가 설치되어 있다면 활성 Flow의 원래 Tuple과 응답 Tuple을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-router conntrack -L -p tcp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Rule 목록과 Conntrack Entry가 서로 다른 정보를 보여 주는지 비교한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 같은 환경에서 Port Forwarding을 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 외부 &lt;code&gt;nat-server&lt;/code&gt;가 NAT Router의 18080 Port로 연결하면 내부 &lt;code&gt;nat-client&lt;/code&gt;의 8080 Port로 전달되게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNAT Rule을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-router \
  iptables -t nat -A PREROUTING \
  -i veth-nro -p tcp --dport 18080 \
  -j DNAT --to-destination 10.21.0.2:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 Client Terminal에서 Server처럼 Listen한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-client nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 Terminal에서 NAT Router의 외부 Address로 연결한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec nat-server nc 198.51.100.1 18080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 Destination이 어떻게 바뀌는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;변환 전 Destination: 198.51.100.1:18080
변환 후 Destination: 10.21.0.2:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경에서는 DNAT뿐 아니라 Forward Chain의 Firewall 허용 Rule도 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Namespace는 기본 Policy가 ACCEPT라는 전제의 관찰용 구성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다. 내부 NAT Rule, Conntrack State와 가상 Interface도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del nat-client
sudo ip netns del nat-router
sudo ip netns del nat-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Host의 Firewall이나 NAT Rule을 실습용으로 바꾸지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RFC 1918 Private IPv4 범위는 &lt;code&gt;10.0.0.0/8&lt;/code&gt;, &lt;code&gt;172.16.0.0/12&lt;/code&gt;, &lt;code&gt;192.168.0.0/16&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;Public과 Private은 Routing Scope의 차이이며 보안 등급이나 암호화 여부가 아니다.&lt;/li&gt;
&lt;li&gt;NAT는 IP Address 변환을 넓게 가리키고 NAPT/PAT는 Address와 TCP&amp;middot;UDP Port를 함께 변환한다.&lt;/li&gt;
&lt;li&gt;가정용 Router가 하나의 Public Address를 여러 Host에 공유할 때 실제 핵심은 Port Mapping이다.&lt;/li&gt;
&lt;li&gt;Translation Table과 Connection Tracking State는 외부 Tuple을 원래 내부 Tuple로 복원한다.&lt;/li&gt;
&lt;li&gt;NAT는 Address와 Port 변경에 맞춰 IPv4와 TCP&amp;middot;UDP Checksum도 갱신해야 한다.&lt;/li&gt;
&lt;li&gt;Dynamic Mapping은 TCP State나 UDP Idle Timeout에 따라 생성되고 만료될 수 있다.&lt;/li&gt;
&lt;li&gt;SNAT는 Source Address를, DNAT는 Destination Address를 바꾸며 Return Traffic에는 State에 따른 역변환이 적용된다.&lt;/li&gt;
&lt;li&gt;Port Forwarding은 Public Endpoint를 내부 Server에 연결하는 정적 DNAT의 한 형태다.&lt;/li&gt;
&lt;li&gt;NAT의 외부 Port와 Connection Tracking 자원은 유한하므로 Port Exhaustion이 생길 수 있다.&lt;/li&gt;
&lt;li&gt;NAT는 주소를 변환하고 Firewall은 Policy로 허용 여부를 결정하므로 같은 기능이 아니다.&lt;/li&gt;
&lt;li&gt;CGNAT는 ISP 경계에서 가입자들이 Public Address를 다시 공유하며 &lt;code&gt;100.64.0.0/10&lt;/code&gt; Shared Address를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;NAT는 Payload를 암호화하거나 사용자를 익명화하지 않으며 End-to-End 연결에 Mapping State와 Traversal 문제를 추가한다.&lt;/li&gt;
&lt;li&gt;IPv6에서 Address 공유 필요가 줄어도 Stateful Firewall Policy는 별도로 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: IPv4 Address Exhaustion, RFC 1918, Private Address, Public Address, Network Address Translation, NAT, Network Address and Port Translation, NAPT, Port Address Translation, PAT, SNAT, DNAT, Translation Table, Connection Tracking, Conntrack, Port Mapping, Port Forwarding, MASQUERADE, Hairpin NAT, Port Exhaustion, Carrier-Grade NAT, CGNAT, 100.64.0.0/10, NAT Traversal, End-to-End Principle&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Host가 하나의 공인 IPv4 주소를 공유할 수 있는 이유는 대표 주소 하나만 붙였기 때문이 아니다.&lt;br /&gt;외부 Port까지 나누어 쓰고, 그 번호가 어느 내부 Socket의 대화인지 Stateful Translation Table에 기억하기 때문이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT Router는 주소와 Port의 변환 관계를 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Mapping이 있다는 사실과 Packet을 보안 정책상 허용해야 한다는 판단은 같은 문제가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 온 새 연결과 내부 요청의 정상 응답은 어떻게 구분할까? Source Address와 Destination Port만 보면 충분할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;방화벽은 편지의 무엇을 보고 문을 열어줄까?&lt;/b&gt;」라는 질문을 통해 Stateless ACL, Stateful Firewall, 5-Tuple, Connection State, Default Deny와 최소 권한 원칙을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>CGNAT</category>
      <category>dnat</category>
      <category>NAPT</category>
      <category>NAT</category>
      <category>Network</category>
      <category>PAT</category>
      <category>PETERICA</category>
      <category>snat</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1098</guid>
      <comments>https://peterica.tistory.com/1098#entry1098comment</comments>
      <pubDate>Thu, 23 Jul 2026 22:21:12 +0900</pubDate>
    </item>
    <item>
      <title>[Container] Harbor와 Kaniko는 왜 함께 사용할까? - 이미지는 만들고, 저장하고, 배포한다</title>
      <link>https://peterica.tistory.com/1106</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqxjhb/dJMcaae5wUZ/8fuuW9ygrauflfFoscuTI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqxjhb/dJMcaae5wUZ/8fuuW9ygrauflfFoscuTI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqxjhb/dJMcaae5wUZ/8fuuW9ygrauflfFoscuTI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqxjhb%2FdJMcaae5wUZ%2F8fuuW9ygrauflfFoscuTI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 이미지를 운영하는 과정은 크게 &lt;b&gt;빌드(Build)&lt;/b&gt;, &lt;b&gt;저장(Store)&lt;/b&gt;, &lt;b&gt;배포(Deploy)&lt;/b&gt; 의 세 단계로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 &lt;b&gt;Docker 이미지를 생성하는 Builder&lt;/b&gt;이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harbor는 &lt;b&gt;생성된 이미지를 저장하는 Registry&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 경쟁 관계가 아니라 서로 역할이 다른 프로젝트이며, 함께 사용할 때 가장 큰 효과를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 처음 공부하면 자연스럽게 다음 명령어를 사용하게 된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;docker build
docker push
docker pull&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Kubernetes 환경에서는 이야기가 조금 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 서버에는 Docker Engine이 없을 수도 있고, 보안상 Docker Daemon을 실행하기 어려운 환경도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Docker 이미지는 어떻게 만들고 어디에 저장할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 등장한 대표적인 오픈소스가 &lt;b&gt;Kaniko&lt;/b&gt;와 &lt;b&gt;Harbor&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 컨테이너 이미지는 세 단계를 거친다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 이미지는 다음과 같은 흐름으로 관리된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Source Code
      │
      ▼
Image Build
      │
      ▼
Image Registry
      │
      ▼
Kubernetes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계는 서로 다른 책임을 가진다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 37.4419%; height: 93px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1553%;&quot;&gt;단계&lt;/td&gt;
&lt;td style=&quot;width: 74.5342%;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1553%;&quot;&gt;Build&lt;/td&gt;
&lt;td style=&quot;width: 74.5342%;&quot;&gt;Dockerfile을 이용하여 이미지 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1553%;&quot;&gt;Store&lt;/td&gt;
&lt;td style=&quot;width: 74.5342%;&quot;&gt;생성된 이미지를 저장 및 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.1553%;&quot;&gt;Deploy&lt;/td&gt;
&lt;td style=&quot;width: 74.5342%;&quot;&gt;저장된 이미지를 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Kaniko는 이미지를 만드는 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 Dockerfile을 읽어 Docker 이미지를 생성하는 &lt;b&gt;Image Builder&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 Docker 이미지를 만들기 위해 Docker Daemon이 반드시 필요했다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Kubernetes에서는 Docker Daemon을 실행하지 않는 환경이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는 Docker Daemon 없이도 이미지를 생성할 수 있기 때문에 Kubernetes 환경에서 널리 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kaniko의 주요 역할&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 59.3023%; height: 161px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기능&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dockerfile 실행&lt;/td&gt;
&lt;td&gt;Dockerfile 기반 이미지 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Daemon 불필요&lt;/td&gt;
&lt;td&gt;별도의 Docker Engine 없이 빌드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes 실행&lt;/td&gt;
&lt;td&gt;Pod 내부에서 이미지 빌드 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD 연동&lt;/td&gt;
&lt;td&gt;Jenkins, GitLab CI, Tekton 등과 연계&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Kaniko는 &lt;b&gt;이미지를 만드는 것(Build)&lt;/b&gt; 에만 집중한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Harbor는 이미지를 저장하는 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지가 만들어졌다면 저장할 공간이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harbor는 Docker Hub를 직접 구축하는 것과 같은 &lt;b&gt;Private Container Registry&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Harbor의 주요 역할&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 32.5581%; height: 223px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기능&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지 저장&lt;/td&gt;
&lt;td&gt;Private Registry 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트 관리&lt;/td&gt;
&lt;td&gt;Repository 및 권한 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지 복제&lt;/td&gt;
&lt;td&gt;Registry 간 Replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;취약점 검사&lt;/td&gt;
&lt;td&gt;Trivy 기반 이미지 스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지 서명&lt;/td&gt;
&lt;td&gt;Cosign / Notary 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;웹 UI&lt;/td&gt;
&lt;td&gt;이미지 관리 및 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Harbor는 &lt;b&gt;이미지를 저장하고 관리하는 것(Store)&lt;/b&gt; 에 집중한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Harbor와 Kaniko는 어떻게 함께 사용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 CI/CD에서는 다음과 같은 구조를 많이 사용한다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Git Repository
        │
        ▼
     Jenkins
(GitLab CI / Tekton)
        │
        ▼
      Kaniko
 (Docker Image Build)
        │
        ▼
      Harbor
 (Image Registry)
        │
        ▼
   Kubernetes
 (Image Pull &amp;amp; Run)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할을 비교하면 더욱 명확하다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 43.9535%; height: 105px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트&lt;/td&gt;
&lt;td&gt;담당 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kaniko&lt;/td&gt;
&lt;td&gt;Dockerfile을 읽어 이미지 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harbor&lt;/td&gt;
&lt;td&gt;생성된 이미지 저장 및 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;Harbor에서 이미지를 내려받아 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 역할을 분리할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이유는 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관심사의 분리(Separation of Concerns)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko는&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;b&gt;&quot;어떻게 이미지를 만들 것인가?&quot;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/b&gt;를 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Harbor는&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;b&gt;&quot;어떻게 이미지를 안전하게 저장하고 관리할 것인가?&quot;&lt;/b&gt;를 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes는&amp;nbsp; &lt;b&gt;&quot;어떻게 이미지를 실행할 것인가?&quot;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/b&gt;를 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 시스템이 하나의 책임만 가지기 때문에 유지보수가 쉬워지고 확장성도 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 컨테이너 플랫폼의 핵심 흐름&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 플랫폼은 단순히 Docker 이미지를 만드는 것으로 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이미지를 &lt;b&gt;빌드(Build)&lt;/b&gt; 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 안전하게 &lt;b&gt;저장(Store)&lt;/b&gt; 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 필요한 환경에서 &lt;b&gt;배포(Deploy)&lt;/b&gt; 하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 과정을 하나의 파이프라인으로 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kaniko와 Harbor는 이 과정에서 각각 &lt;b&gt;Builder&lt;/b&gt;와 &lt;b&gt;Registry&lt;/b&gt;라는 역할을 맡아 컨테이너 플랫폼의 핵심 기반을 구성한다.&lt;/p&gt;</description>
      <category>DevOps</category>
      <category>harbor</category>
      <category>kaniko</category>
      <category>Kubernetis</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1106</guid>
      <comments>https://peterica.tistory.com/1106#entry1106comment</comments>
      <pubDate>Thu, 23 Jul 2026 20:55:26 +0900</pubDate>
    </item>
    <item>
      <title>[Search] 검색 미들서버 앞에 Gateway가 필요한 이유 - 검색은 검색만 잘하면 된다</title>
      <link>https://peterica.tistory.com/1105</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTaWKy/dJMcacD0eUQ/QiBgaf84kKmNIwU5imk7gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTaWKy/dJMcacD0eUQ/QiBgaf84kKmNIwU5imk7gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTaWKy/dJMcacD0eUQ/QiBgaf84kKmNIwU5imk7gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTaWKy%2FdJMcacD0eUQ%2FQiBgaf84kKmNIwU5imk7gK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 미들서버(SMS)의 역할은 &lt;b&gt;좋은 검색 결과를 만드는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 서비스에서는 검색과 관계없는 운영 기능이 훨씬 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 대규모 검색 시스템은 SMS 앞단에 &lt;b&gt;Gateway(Reverse Proxy)&lt;/b&gt; 를 두고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 분산, 장애 대응, 캐시, 인증 등을 Gateway가 담당하도록 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Gateway의 존재 이유는 &lt;b&gt;검색 미들서버가 검색 로직에만 집중하도록 만드는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 회의에서 들었던 검색미들을 위한 Gateway의 역할에 대해서 알게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 시스템을 처음 접하면서 자연스럽게 이런 생각을 하게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 미들서버가 요청을 받아 검색하고 결과를 돌려주면 되는 것 아닌가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 역시 처음에는 그렇게 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 서비스에서는 검색보다 운영을 위한 기능이 훨씬 많이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 상황을 생각해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 서버가 50대라면 어느 서버로 요청을 보낼까?&lt;/li&gt;
&lt;li&gt;특정 서버가 장애가 나면 어떻게 할까?&lt;/li&gt;
&lt;li&gt;동일한 검색어는 다시 검색해야 할까?&lt;/li&gt;
&lt;li&gt;비정상적인 요청은 어떻게 막을까?&lt;/li&gt;
&lt;li&gt;모든 요청 로그는 어디에서 관리할까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 기능을 모두 검색 미들서버가 담당한다면 검색 로직과 운영 로직이 하나의 시스템 안에 뒤섞이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 검색 미들서버는 검색만 잘하면 된다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 미들서버(SMS)의 가장 중요한 역할은 검색이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색어 분석&lt;/li&gt;
&lt;li&gt;검색 정책 적용&lt;/li&gt;
&lt;li&gt;랭킹 계산&lt;/li&gt;
&lt;li&gt;검색 결과 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 &lt;b&gt;검색 품질&lt;/b&gt;과 직접 관련된 기능에 집중해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 운영을 위한 기능은 성격이 전혀 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Load Balancing&lt;/li&gt;
&lt;li&gt;Cache&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Rate Limiting&lt;/li&gt;
&lt;li&gt;Retry&lt;/li&gt;
&lt;li&gt;Failover&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능들은 검색 알고리즘과는 아무런 관련이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 대규모 서비스는 자연스럽게 역할을 분리하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Gateway가 맡는 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 시스템은 보통 다음과 같이 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PGJzV/dJMcadCHoRx/niK8wqCs9iWk8ULNATGcf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PGJzV/dJMcadCHoRx/niK8wqCs9iWk8ULNATGcf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PGJzV/dJMcadCHoRx/niK8wqCs9iWk8ULNATGcf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPGJzV%2FdJMcadCHoRx%2FniK8wqCs9iWk8ULNATGcf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gateway는 검색과 관계없는 공통 기능을 담당한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 58.3721%; height: 287px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gateway의 역할&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요청 분산&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 SMS에 트래픽을 균등하게 분배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;장애 우회&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;장애가 발생한 SMS를 자동 제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;캐시&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동일 요청을 재사용하여 응답 속도 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;인증&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;사용자 인증 및 권한 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요청 제한&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;과도한 요청 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;로그 수집&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모든 요청과 응답 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Retry / Timeout&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;네트워크 오류 자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 SMS는 검색에만 집중한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 13.7209%; height: 157px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SMS의 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검색어 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검색 정책 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;랭킹 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검색 결과 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 대표적인 Gateway 오픈소스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gateway는 특정 제품이 아니라 역할이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 다음과 같은 오픈소스가 많이 사용된다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 78.1395%; height: 179px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;프로젝트&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;적합한 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Envoy&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;고성능 L7 Proxy, 동적 라우팅, Observability&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Kubernetes, MSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Apache Traffic Server&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;HTTP Cache와 Reverse Proxy에 특화&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;포털, CDN, 검색 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;NGINX&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;범용 Reverse Proxy&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;대부분의 웹 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;HAProxy&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;초고성능 Load Balancer&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;금융, 대규모 트래픽&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Envoy&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8S0WX/dJMcafAFpBz/ZC2KsxyzMBE2jJkEkp5vX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8S0WX/dJMcafAFpBz/ZC2KsxyzMBE2jJkEkp5vX0/img.png&quot; data-alt=&quot;https://gateway.envoyproxy.io/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8S0WX/dJMcafAFpBz/ZC2KsxyzMBE2jJkEkp5vX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8S0WX%2FdJMcafAFpBz%2FZC2KsxyzMBE2jJkEkp5vX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;1278&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://gateway.envoyproxy.io/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Envoy는 CNCF 프로젝트이며 현재 클라우드 네이티브 환경에서 가장 많이 사용하는 Gateway 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Service Discovery&lt;/li&gt;
&lt;li&gt;Retry&lt;/li&gt;
&lt;li&gt;Circuit Breaker&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능이 뛰어나 Kubernetes 환경에서 사실상의 표준으로 자리 잡았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Apache Traffic Server&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szbvw/dJMcaa0oKtk/vXT4PLktflWRFwKI8vK4U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szbvw/dJMcaa0oKtk/vXT4PLktflWRFwKI8vK4U1/img.png&quot; data-alt=&quot;https://trafficserver.apache.org/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szbvw/dJMcaa0oKtk/vXT4PLktflWRFwKI8vK4U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fszbvw%2FdJMcaa0oKtk%2FvXT4PLktflWRFwKI8vK4U1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;970&quot; data-origin-width=&quot;1984&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://trafficserver.apache.org/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Traffic Server(ATS)는 Yahoo!가 개발한 HTTP Reverse Proxy이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP Cache&lt;/li&gt;
&lt;li&gt;대규모 트래픽 처리&lt;/li&gt;
&lt;li&gt;Reverse Proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능이 뛰어나 CDN과 검색 서비스에서 오랫동안 활용되어 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 시스템에서는 인기 검색어를 캐시하여 검색 미들서버의 부하를 줄이는 데 큰 장점을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 이렇게 분리할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이유는 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관심사의 분리(Separation of Concerns)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gateway는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &quot;요청을 안정적으로 전달하는 책임&quot; &lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Search Middleware는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &quot;가장 좋은 검색 결과를 만드는 책임&quot;&lt;/b&gt; &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자의 책임이 명확해질수록 시스템은 단순해지고 유지보수는 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 검색 시스템이 커질수록 Gateway의 가치가 커진다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 서비스는 규모가 커질수록 검색 알고리즘보다 운영 복잡도가 더 빠르게 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Gateway는 검색과 무관한 공통 기능을 모두 흡수하여 검색 미들서버를 단순하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 검색 미들서버는 &lt;b&gt;&quot;어떻게 요청을 받을 것인가&quot;&lt;/b&gt; 를 고민하는 것이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;어떻게 더 좋은 검색 결과를 만들 것인가&quot;&lt;/b&gt; 에만 집중할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 이것이 대규모 검색 시스템에서 Gateway를 두는 가장 큰 이유일 것이다.&lt;/p&gt;</description>
      <category>DevOps</category>
      <category>Apache Traffic Server</category>
      <category>envoy</category>
      <category>Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1105</guid>
      <comments>https://peterica.tistory.com/1105#entry1105comment</comments>
      <pubDate>Thu, 23 Jul 2026 06:51:00 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 20. 가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까?</title>
      <link>https://peterica.tistory.com/1097</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JWqBi/dJMcah6lw7q/qvjozZZn2R02KDiQBRyWjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JWqBi/dJMcah6lw7q/qvjozZZn2R02KDiQBRyWjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JWqBi/dJMcah6lw7q/qvjozZZn2R02KDiQBRyWjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJWqBi%2FdJMcah6lw7q%2FqvjozZZn2R02KDiQBRyWjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 6. 너무 큰 소포와 좁은 길의 문제&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 자신이 연결된 첫 Link의 MTU를 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Destination까지 이어지는 모든 Link의 MTU를 처음부터 알고 있지는 않다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source ── 1500 ── R1 ── 1400 ── R2 ── 1280 ── Destination&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경로에서 단편화 없이 보낼 수 있는 최대 IP Packet은 1280바이트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 Source가 통신을 시작할 때 모든 Router에 전화를 걸어 MTU를 조사하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route가 바뀌면 가장 좁은 Link도 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 Packet을 보내고, 전달할 수 없는 Router가 돌려주는 제어 메시지를 단서로 크기를 조절한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정이 &lt;b&gt;Path MTU Discovery&lt;/b&gt;(PMTUD)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &amp;ldquo;너무 크다&amp;rdquo;는 안내가 돌아오지 않을 때 생긴다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;TCP Handshake 성공
작은 요청 성공
큰 Data Packet은 Drop
ICMP 안내도 Drop

&amp;rarr; 연결은 살아 있는 것처럼 보이지만 Data가 멈춤&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Path MTU&lt;/b&gt;, &lt;b&gt;ICMP Fragmentation Needed&lt;/b&gt;, &lt;b&gt;ICMPv6 Packet Too Big&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PMTU Cache&lt;/b&gt;, &lt;b&gt;MTU Black Hole&lt;/b&gt;, &lt;b&gt;MSS Clamping&lt;/b&gt;, &lt;b&gt;Packetization Layer PMTUD&lt;/b&gt;(PLPMTUD)를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 전달 과정으로 연결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Link MTU와 Path MTU는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Link MTU&lt;/b&gt;는 하나의 Link에서 단편화 없이 운반할 수 있는 IP Packet 크기다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Path MTU&lt;/b&gt;는 Source에서 Destination으로 가는 한 방향 경로에 있는 Link MTU 중 가장 작은 값이다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Link MTU
&amp;rarr; 한 구간의 한계

Path MTU
&amp;rarr; 경로 전체의 최소 한계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 경로를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source
  │ MTU 1500
  ▼
R1
  │ MTU 1450
  ▼
R2
  │ MTU 1280
  ▼
Destination&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산은 단순하다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PMTU = min(1500, 1450, 1280)
     = 1280&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 인터넷에서는 Source가 이 목록을 직접 가지고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Path와 Return Path가 다를 수도 있으므로 반대 방향 PMTU도 같다고 보장할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;A &amp;rarr; B PMTU = 1280
B &amp;rarr; A PMTU = 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMTU는 두 Host 사이의 영구적인 고정 속성이 아니라 &lt;b&gt;특정 방향과 현재 경로에 대한 상태&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv4 PMTUD는 왜 DF Flag를 사용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Router가 큰 Packet을 자동으로 Fragmentation하면 Source는 경로가 좁다는 사실을 모른 채 계속 큰 Packet을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Fragmentation을 피하려면 Source가 IPv4 Packet에 &lt;b&gt;Don't Fragment(DF)&lt;/b&gt; Flag를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source가 DF=1 Packet 전송
        &amp;darr;
Router의 Outgoing MTU 안에 들어감
        &amp;darr;
계속 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 Router에서 Packet이 Outgoing MTU보다 커지면 조건이 달라진다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Packet Length: 1500
Outgoing MTU : 1400
DF           : 1

&amp;rarr; Router가 Fragmentation할 수 없음
&amp;rarr; Packet Drop
&amp;rarr; Source에 ICMP 오류 전송 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DF는 단순히 &amp;ldquo;조각내지 마라&amp;rdquo;는 금지 표시에 그치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Packet을 그대로 보내 보고, 지나갈 수 없는 지점에서 명시적인 오류를 받게 만들어 Source가 경로의 한계를 학습하도록 돕는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Router는 너무 크다는 사실을 어떻게 알릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Router는 DF가 설정된 큰 Packet을 전달할 수 없을 때 일반적으로 다음 ICMP 메시지를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Destination Unreachable
Type 3
Code 4
Fragmentation Needed and DF Set&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대적인 형식의 메시지에는 다음 Hop이 받아들일 수 있는 MTU, 즉 &lt;b&gt;Next-Hop MTU&lt;/b&gt;가 포함될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Router &amp;rarr; Source

이 Packet은 1500바이트다.
다음 Link는 1400바이트까지만 전달할 수 있다.
더 작게 만들어 다시 보내라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 오류에는 문제가 된 원래 IP Header와 상위 계층을 식별할 수 있는 원래 Packet의 일부가 인용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 인용된 주소와 Protocol, Port 등의 정보를 이용해 어느 통신에 대한 오류인지 연결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 메시지도 새로운 IP Packet이므로 Source로 돌아오는 Route와 정책이 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Source는 ICMP를 받으면 무엇을 바꿀까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source가 Next-Hop MTU 1400이라는 ICMP를 받았다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 해당 Destination 또는 Route와 관련된 PMTU 상태를 갱신할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;기존 추정 PMTU: 1500
ICMP가 알린 MTU: 1400
새 추정 PMTU  : 1400&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP라면 IP Packet 전체가 새 PMTU 안에 들어가도록 Segment의 Data 크기를 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header 20바이트, TCP Header 20바이트만 있다고 가정하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;PMTU 1400
- IPv4 Header 20
- TCP Header 20
= TCP Data 최대 1360바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Header Option이 있으면 한 Packet에 넣을 Data는 더 작아질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;큰 TCP Segment 재전송
&amp;rarr; PMTU에 맞는 더 작은 IP Packet
&amp;rarr; 중간 Fragmentation 없이 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMTUD는 Application의 File 크기를 줄이는 과정이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Byte Stream을 더 작은 Packet 단위로 나누어 보내는 과정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ICMP 하나를 받으면 경로의 모든 MTU를 알게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번의 오류는 현재 막힌 Link의 한계를 알려 줄 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 뒤에 더 좁은 Link가 있을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;처음 Packet 1500
&amp;rarr; MTU 1400 Link에서 Drop
&amp;rarr; PMTU를 1400으로 낮춤

다시 Packet 1400
&amp;rarr; MTU 1280 Link에서 Drop
&amp;rarr; PMTU를 1280으로 낮춤&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 오류를 받을 때마다 추정치를 더 낮출 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 PMTUD는 모든 Link를 먼저 열람해 정답을 한 번에 얻는 절차가 아니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;보냄
&amp;rarr; 너무 크다는 응답
&amp;rarr; 크기를 줄임
&amp;rarr; 다시 보냄
&amp;rarr; 전달 가능한 크기에 도달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로가 바뀌거나 PMTU 상태의 유효 시간이 지나면 운영체제가 다시 더 큰 크기를 탐색할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ PMTU Cache는 왜 영원히 믿을 수 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 Packet마다 처음부터 크기를 다시 찾으면 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 발견한 PMTU를 Destination이나 Route 상태와 연관해 일정 시간 기억할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;PMTU Cache&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Destination A &amp;rarr; PMTU 1500
Destination B &amp;rarr; PMTU 1280
Destination C &amp;rarr; PMTU 1420&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 인터넷 Route는 변한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;이전 경로: 최소 MTU 1280
새 경로  : 최소 MTU 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 작은 값만 영원히 사용하면 전달은 되더라도 필요 이상으로 작은 Packet을 계속 보내 효율이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 새 경로가 더 좁아졌는데 오래된 큰 값을 믿으면 다시 ICMP 오류나 손실을 만난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 PMTU 정보는 구현별 유효 시간과 재검증 과정을 가진 동적인 추정값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache의 범위와 갱신 방식은 운영체제마다 다를 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP MSS와 PMTU는 같은 값일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 관련 있지만 같은 계층의 값은 아니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PMTU
&amp;rarr; 경로가 허용하는 IP Packet 전체 크기

MSS
&amp;rarr; TCP Segment에 담을 Data의 최대 크기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Endpoint는 Handshake의 MSS Option으로 자신이 받을 수 있는 TCP Data 크기를 상대에게 알린다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Server SYN-ACK: MSS=1460&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Server 쪽 Interface 조건을 반영할 수 있지만, 경로 중간의 Tunnel이나 더 작은 Link를 모두 알고 광고한 값이라고 보장할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;상대가 광고한 MSS: 1460
중간 경로 PMTU   : 1280
IPv4/TCP 기본 Header 기준 안전한 Data: 1240&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 상대 MSS와 자신이 발견한 PMTU, Header 크기 등을 함께 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake가 끝난 뒤 PMTU가 더 작다는 사실을 발견해도 기존 TCP 연결은 더 작은 Segment로 조정할 수 있다. MSS Option을 다시 협상하는 새 Handshake가 필요한 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 TCP Handshake는 되는데 큰 Data만 멈출까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP SYN과 순수 ACK는 Payload가 거의 없어서 IP Packet도 작다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;SYN        : 작은 Packet &amp;rarr; 통과
SYN-ACK    : 작은 Packet &amp;rarr; 통과
ACK        : 작은 Packet &amp;rarr; 통과

큰 Data Segment: PMTU 초과 &amp;rarr; Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3-Way Handshake 성공은 큰 Packet도 전달된다는 증거가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 현상이 나타날 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSH Login과 짧은 명령은 되지만 큰 출력에서 멈춤&lt;/li&gt;
&lt;li&gt;작은 HTTP 응답은 되지만 큰 응답이나 Upload가 정체됨&lt;/li&gt;
&lt;li&gt;TLS 연결 중 작은 ClientHello는 가지만 큰 인증서 메시지 부근에서 멈춤&lt;/li&gt;
&lt;li&gt;VPN 연결 후 일부 Site만 열리지 않음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ping&lt;/code&gt; 기본 크기는 성공하지만 큰 DF Probe는 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 문제처럼 보이지만 Packet 크기 경계가 원인일 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MTU Black Hole은 어떻게 만들어질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 경로를 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source ── MTU 1500 ── Router ── MTU 1200 ── Destination&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source가 1500바이트 DF Packet을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 1200바이트 Link로 내보낼 수 없어 Packet을 버리고 ICMP Fragmentation Needed를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Firewall이 ICMP Type 3 Code 4를 차단한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source &amp;rarr; Router : 큰 DF Packet
Router          : Packet Drop
Router &amp;rarr; Source : ICMP Fragmentation Needed
Firewall        : ICMP Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source가 보는 장면은 단순하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Packet을 보냈다.
ACK가 오지 않는다.
왜 사라졌는지는 모른다.
같은 크기로 재전송한다.
다시 사라진다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적인 오류도 없고 Data도 앞으로 가지 않는 경로를 &lt;b&gt;PMTU Black Hole&lt;/b&gt; 또는 &lt;b&gt;MTU Black Hole&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;ICMP는 ping에만 쓰이니 모두 막아도 된다&amp;rdquo;는 정책이 정상 TCP 전달을 깨뜨릴 수 있는 대표적인 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 ICMP를 무조건 허용해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP가 PMTUD에 필요하다는 사실과 모든 ICMP Packet을 아무 검증 없이 신뢰해야 한다는 말은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가짜 ICMP 오류로 PMTU를 불필요하게 낮추려는 입력도 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host와 Firewall은 다음 정보를 이용해 ICMP 오류가 실제 Flow와 관련되는지 검증할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ICMP에 인용된 원래 Source와 Destination&lt;/li&gt;
&lt;li&gt;원래 IP Protocol&lt;/li&gt;
&lt;li&gt;인용된 TCP 또는 UDP Port와 Sequence 관련 정보&lt;/li&gt;
&lt;li&gt;현재 연결과 Route 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 정책은 필요한 ICMP 오류 종류를 허용하면서 Rate Limiting과 Stateful Validation을 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음 두 극단을 피하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;ICMP는 위험하니 전부 Drop       (X)
ICMP라고 표시되면 전부 무조건 신뢰 (X)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 전달에 필요한 Control Plane 메시지의 역할과 검증 범위를 함께 설계해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv6 PMTUD는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 Router는 지나치게 큰 Packet을 중간에서 Fragmentation하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Outgoing Link로 전달할 수 없으면 Packet을 버리고 Source에 다음 메시지를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMPv6 Packet Too Big
Type 2, Code 0
MTU 값 포함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 알려진 MTU에 맞춰 Packet을 더 작게 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하다면 IPv6 Source가 Fragment Extension Header를 사용해 Fragment할 수 있지만 Router가 대신 나누지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6가 모든 Link에서 기대하는 최소 MTU는 1280바이트다. 1280보다 작은 Link 기술은 IPv6 계층 아래에서 Fragmentation과 Reassembly 같은 적응을 제공해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IPv4 PMTUD
&amp;rarr; DF Packet + ICMP Type 3 Code 4

IPv6 PMTUD
&amp;rarr; Router Fragmentation 없음
&amp;rarr; ICMPv6 Type 2 Packet Too Big&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMPv6 Packet Too Big을 차단하면 IPv6의 정상적인 크기 조절도 깨질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ICMP가 오지 않아도 크기를 알아낼 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 PMTUD는 Router가 보내는 ICMP 오류에 크게 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 ICMP가 차단되거나 장비가 올바른 MTU를 알리지 않는 경로도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Packetization Layer Path MTU Discovery&lt;/b&gt;(PLPMTUD)는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; TCP 같은 Packetization Layer가 여러 크기의 Probe를 보내고 End-to-End 전달 결과로 사용할 크기를 탐색하는 접근이다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;작은 크기: 전달 성공
조금 큰 Probe: 전달 성공
더 큰 Probe: 응답 없음

&amp;rarr; 확인된 크기와 실패한 크기 사이에서 안전한 값 탐색&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PLPMTUD의 핵심은 ICMP만을 유일한 진실로 기다리지 않는 데 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ACK가 돌아온 Probe 크기는 End-to-End로 전달됐음을 확인한다.&lt;/li&gt;
&lt;li&gt;큰 Probe가 실패해도 작은 Packet으로 연결 상태를 계속 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP 정보가 도착하면 추가 단서로 활용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 Data Loss와 크기 때문에 발생한 Loss를 구분하는 일은 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Probe 식별, 재시도, 확인된 Base 크기 같은 상태가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 구현은 반복 재전송에서 Black Hole을 의심해 Segment 크기를 낮추는 완화 동작을 제공할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 탐색과 Fallback 방식은 Protocol과 운영체제 구현에 따라 달라진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ PMTUD와 PLPMTUD는 무엇을 관찰할까?&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;전통적인 PMTUD&lt;/td&gt;
&lt;td&gt;PLPMTUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 단서&lt;/td&gt;
&lt;td&gt;Router의 ICMP Too Big 오류&lt;/td&gt;
&lt;td&gt;End-to-End Probe의 성공과 실패&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPv4 출발점&lt;/td&gt;
&lt;td&gt;DF가 설정된 Packet&lt;/td&gt;
&lt;td&gt;Packetization Layer가 크기별 Probe 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;Router가 정확한 Next-Hop MTU를 알려 줄 수 있음&lt;/td&gt;
&lt;td&gt;ICMP가 사라져도 탐색 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;어려움&lt;/td&gt;
&lt;td&gt;ICMP 차단 시 Black Hole&lt;/td&gt;
&lt;td&gt;Loss 원인이 MTU인지 혼잡인지 구분 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태 위치&lt;/td&gt;
&lt;td&gt;IP/Route PMTU 상태&lt;/td&gt;
&lt;td&gt;TCP&amp;middot;QUIC 같은 Packetization Layer 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공통 목표&lt;/td&gt;
&lt;td&gt;중간 Fragmentation 없이 경로가 운반할 수 있는 Packet 크기 사용&lt;/td&gt;
&lt;td&gt;같은 목표&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식을 서로 완전히 배타적인 선택으로 볼 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현은 ICMP 오류를 받아 빠르게 줄이면서 End-to-End Probe로 전달 여부를 검증할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MSS Clamping은 무엇을 바꿀까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tunnel이나 Firewall 장비가 TCP SYN의 MSS Option을 더 작은 값으로 바꾸는 구성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;TCP MSS Clamping&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client SYN MSS=1460
        &amp;darr; Tunnel Gateway가 조정
상대가 보는 MSS=1360&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대는 처음부터 더 작은 TCP Segment를 보내므로 Tunnel 내부에서 PMTU를 넘는 Packet이 생길 가능성을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS Clamping은 다음 상황에서 실용적인 완화책이 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tunnel Overhead 때문에 내부 PMTU가 줄어듦&lt;/li&gt;
&lt;li&gt;제어할 수 없는 경로에서 ICMP가 잘못 차단됨&lt;/li&gt;
&lt;li&gt;TCP 연결이 큰 Segment에서 반복적으로 멈춤&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 한계도 분명하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP SYN의 MSS만 조정하므로 UDP와 다른 Protocol 문제는 해결하지 않는다.&lt;/li&gt;
&lt;li&gt;잘못된 값을 사용하면 필요 이상으로 작은 Segment를 만들어 효율이 떨어진다.&lt;/li&gt;
&lt;li&gt;ICMP 차단이나 MTU 구성 오류라는 근본 원인을 숨길 수 있다.&lt;/li&gt;
&lt;li&gt;이미 성립한 연결의 SYN을 과거로 돌아가 바꾸지는 못한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS Clamping은 &amp;ldquo;모든 MTU 문제의 정답&amp;rdquo;이 아니라 관리 경계에서 신중하게 사용하는 보완책이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 큰 ping 하나로 PMTU를 확정할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DF가 설정된 ICMP Echo Request의 크기를 바꾸며 성공 경계를 찾는 방법은 유용한 진단 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header Option이 없고 ICMP Header가 8바이트라면 다음처럼 계산한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;추정 PMTU = ping Data 크기 + IPv4 Header 20 + ICMP Header 8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Linux에서 다음 Probe를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping -M do -c 1 -s 1472 192.0.2.10&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1472 + 20 + 8 = 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data 크기를 1473으로 늘리면 IPv4 Packet은 1501바이트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 결과 하나만으로 모든 Application의 PMTU를 확정하면 안 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ping&lt;/code&gt; Option과 크기 의미는 운영체제마다 다르다.&lt;/li&gt;
&lt;li&gt;ICMP Echo만 정책적으로 차단될 수 있다.&lt;/li&gt;
&lt;li&gt;ECMP 때문에 Flow별 경로가 다를 수 있다.&lt;/li&gt;
&lt;li&gt;반대 방향 PMTU가 다를 수 있다.&lt;/li&gt;
&lt;li&gt;Tunnel과 Header Option이 실제 Application Packet 크기를 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt;은 경계 가설을 시험하는 도구이지 모든 Protocol과 경로의 절대적인 증명서는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ tracepath는 무엇을 보여줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 &lt;code&gt;tracepath&lt;/code&gt;는 Hop을 관찰하면서 PMTU 변화 단서를 표시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;tracepath 192.0.2.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에는 환경에 따라 다음과 같은 정보가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;pmtu 1500
...
pmtu 1400&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tracepath&lt;/code&gt;도 경로 장비의 ICMP 응답, Return Path, 정책의 영향을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답이 없거나 일부 Hop이 보이지 않는다고 해당 Link가 존재하지 않는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표시된 PMTU 역시 관찰한 Probe에 기반한 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한과 설치 여부, Option은 운영체제와 배포판에 따라 다를 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서는 무엇을 찾아야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 PMTUD 문제를 조사할 때 다음 흐름을 함께 본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;큰 TCP Data Packet
&amp;rarr; 같은 Sequence Number의 Retransmission
&amp;rarr; ICMP Type 3 Code 4 존재 여부
&amp;rarr; 재전송 Packet 크기 변화 여부&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark Display Filter 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;icmp.type == 3 &amp;amp;&amp;amp; icmp.code == 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 Packet Too Big은 다음처럼 찾을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;icmpv6.type == 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함께 확인할 Field는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;IPv4: ip.len, ip.flags.df, icmp.type, icmp.code, icmp.mtu
IPv6: ipv6.plen, icmpv6.type, icmpv6.mtu
TCP : tcp.seq, tcp.len, tcp.analysis.retransmission&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 지점이 중요하다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Router 앞에서 큰 Packet 확인
Router 뒤에서 Packet 없음
Source 쪽에서 ICMP 없음

&amp;rarr; Router Drop과 ICMP Return Path를 나누어 조사&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIC Offload 때문에 Host Capture에서 MTU보다 큰 TCP 단위가 보일 수 있으므로 Wire Packet 크기와 혼동하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MTU 문제를 진단하는 순서는 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;작은 것은 되고 큰 것은 안 된다&amp;rdquo;는 증상만으로 곧바로 MTU 문제라고 단정할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 순서로 가설을 좁힐 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DNS와 Route, TCP Handshake가 실제로 성공하는지 확인한다.&lt;/li&gt;
&lt;li&gt;실패가 전체 연결인지 특정 크기 이후인지 확인한다.&lt;/li&gt;
&lt;li&gt;양 Endpoint에서 Packet 크기, DF, Retransmission을 Capture한다.&lt;/li&gt;
&lt;li&gt;ICMP Fragmentation Needed 또는 Packet Too Big이 생성되는지 확인한다.&lt;/li&gt;
&lt;li&gt;생성된 ICMP가 Source까지 돌아오는지 각 정책 지점을 확인한다.&lt;/li&gt;
&lt;li&gt;Interface와 Tunnel MTU, Encapsulation Overhead를 계산한다.&lt;/li&gt;
&lt;li&gt;TCP라면 SYN의 MSS와 실제 Segment Payload 크기를 비교한다.&lt;/li&gt;
&lt;li&gt;DF Probe와 &lt;code&gt;tracepath&lt;/code&gt; 결과를 보조 단서로 사용한다.&lt;/li&gt;
&lt;li&gt;임시 MSS Clamping 전에 ICMP와 MTU 구성의 근본 원인을 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰의 핵심은 실패한 Application만 보는 것이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 큰 Packet이 마지막으로 보인 지점과 ICMP가 마지막으로 보인 지점을 따로 찾는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Protocol과 상태로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경로 전체에서 가장 작은 소포 규격&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Path MTU&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;한 방향 경로의 Link MTU 중 최솟값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소포에 나누지 말라는 표시&lt;/td&gt;
&lt;td&gt;&lt;b&gt;IPv4 DF Flag&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간 Router의 Fragmentation을 금지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다음 길에는 너무 크다는 반송 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMP Type 3 Code 4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DF Packet이 Outgoing MTU를 넘었음을 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반송장에 적힌 허용 크기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Next-Hop MTU&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;다음 Link가 전달할 수 있는 Packet 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적지별로 기억한 포장 규격&lt;/td&gt;
&lt;td&gt;&lt;b&gt;PMTU Cache&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;발견한 PMTU를 Route 상태와 연관해 임시 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반송 안내가 사라지는 구간&lt;/td&gt;
&lt;td&gt;&lt;b&gt;MTU Black Hole&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;큰 Packet과 ICMP 오류가 모두 전달되지 않아 정체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPv6의 너무 큰 소포 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ICMPv6 Packet Too Big&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IPv6 Router가 Source에 MTU 한계를 통보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;크기를 조금씩 바꿔 시험 배송&lt;/td&gt;
&lt;td&gt;&lt;b&gt;PLPMTUD&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;End-to-End Probe 성공 여부로 안전한 크기를 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접수 시 소포 본문 한도를 줄임&lt;/td&gt;
&lt;td&gt;&lt;b&gt;MSS Clamping&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;TCP SYN의 MSS 값을 관리 장비가 조정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;터널 포장 때문에 줄어든 내부 공간&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Encapsulation Overhead&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Outer Header만큼 내부에서 쓸 크기가 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Source가 큰 DF Packet 전송
            &amp;darr;
     Router의 MTU 초과
            &amp;darr;
       Packet Drop
            &amp;darr;
ICMP Too Big이 Source에 도착하는가?
       ┌────┴────┐
      Yes        No
       │          │
 PMTU 갱신     같은 크기 재전송
 Packet 축소    ACK 진전 없음
       │          │
 전달 재개     MTU Black Hole&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 Black Hole을 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;가 있는 개인 Linux VM에서만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 정책과 MTU 변경은 세 Network Namespace 안에 격리한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;pmtu-client ── MTU 1500 ── pmtu-router ── MTU 1200 ── pmtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Interface는 1500을 유지하고 Router의 Server 방향 Outgoing Interface만 1200으로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이 때문에 Server가 광고한 TCP MSS만으로는 중간의 작은 MTU를 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add pmtu-client
sudo ip netns add pmtu-router
sudo ip netns add pmtu-server

sudo ip link add veth-pc type veth peer name veth-prc
sudo ip link add veth-prs type veth peer name veth-ps

sudo ip link set veth-pc netns pmtu-client
sudo ip link set veth-prc netns pmtu-router
sudo ip link set veth-prs netns pmtu-router
sudo ip link set veth-ps netns pmtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n pmtu-client addr add 10.20.1.2/24 dev veth-pc
sudo ip -n pmtu-router addr add 10.20.1.1/24 dev veth-prc
sudo ip -n pmtu-router addr add 10.20.2.1/24 dev veth-prs
sudo ip -n pmtu-server addr add 10.20.2.2/24 dev veth-ps

sudo ip -n pmtu-client link set lo up
sudo ip -n pmtu-router link set lo up
sudo ip -n pmtu-server link set lo up
sudo ip -n pmtu-client link set veth-pc up
sudo ip -n pmtu-router link set veth-prc up
sudo ip -n pmtu-router link set veth-prs up mtu 1200
sudo ip -n pmtu-server link set veth-ps up mtu 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n pmtu-client route add default via 10.20.1.1
sudo ip -n pmtu-server route add default via 10.20.2.1
sudo ip netns exec pmtu-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Terminal에서 Packet을 Capture한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec pmtu-client \
  tcpdump -n -vv -i veth-pc -s 0 -w /tmp/pmtu-blackhole.pcap \
  'icmp or tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Router가 생성한 ICMP Fragmentation Needed를 Router 안에서만 버린다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec pmtu-router \
  iptables -I OUTPUT -p icmp \
  --icmp-type fragmentation-needed -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 TCP 수신을 시작한다. &lt;code&gt;nc&lt;/code&gt; Option은 구현마다 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec pmtu-server nc -l 8080 &amp;gt; /dev/null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Terminal에서 충분히 큰 Data를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;dd if=/dev/zero bs=1M count=4 | \
  sudo ip netns exec pmtu-client nc 10.20.2.2 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음을 관찰한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3-Way Handshake는 작은 Packet이라 성공하는가?&lt;/li&gt;
&lt;li&gt;큰 TCP Data Packet의 DF가 설정되어 있는가?&lt;/li&gt;
&lt;li&gt;같은 Sequence 범위가 재전송되는가?&lt;/li&gt;
&lt;li&gt;Client Capture에 ICMP Type 3 Code 4가 보이지 않는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 TCP 구현은 반복 손실 후 Black Hole Fallback으로 Segment 크기를 낮춰 스스로 회복할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &amp;ldquo;영원히 멈춤&amp;rdquo; 대신 긴 정체 후 회복으로 보일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송이 정체된 동안 Router의 Drop Rule을 제거한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec pmtu-router \
  iptables -D OUTPUT -p icmp \
  --icmp-type fragmentation-needed -j DROP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 큰 재전송에서 Router의 ICMP가 Client에 도착하면 PMTU가 갱신되고 더 작은 TCP Segment로 전송이 재개되는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 DF Probe로 1200바이트 경계를 확인할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 &lt;code&gt;-M probe&lt;/code&gt;는 권한이 필요하며 Kernel의 기존 PMTU 확인을 우회해 Probe를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec pmtu-client \
  ping -M probe -c 1 -s 1172 10.20.2.2

sudo ip netns exec pmtu-client \
  ping -M probe -c 1 -s 1173 10.20.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1172 + IPv4 20 + ICMP 8 = 1200 &amp;rarr; 경계 안
1173 + IPv4 20 + ICMP 8 = 1201 &amp;rarr; 경계 초과&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;iptables&lt;/code&gt;가 없거나 nftables Backend 정책이 다른 환경에서는 명령을 그대로 사용할 수 없을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Host Firewall에 Drop Rule을 추가하지 말고 Namespace 내부에서만 실습한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다. 내부 Firewall Rule과 가상 Interface도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del pmtu-client
sudo ip netns del pmtu-router
sudo ip netns del pmtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Link MTU는 한 Link의 크기 한계이고 Path MTU는 한 방향 경로에 있는 Link MTU의 최솟값이다.&lt;/li&gt;
&lt;li&gt;IPv4 PMTUD는 DF Packet이 너무 클 때 Router가 보내는 ICMP Type 3 Code 4를 이용해 PMTU를 낮춘다.&lt;/li&gt;
&lt;li&gt;Source는 PMTU에서 IP와 Transport Header를 제외한 크기로 TCP Segment Data를 조절한다.&lt;/li&gt;
&lt;li&gt;ICMP 한 번이 경로의 모든 Link를 알려 주는 것은 아니며 더 좁은 Link에서 다시 오류를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;PMTU Cache는 Route 변화 때문에 영구적인 정답이 아니며 유효 시간과 재탐색이 필요하다.&lt;/li&gt;
&lt;li&gt;TCP MSS는 TCP Data 크기이고 PMTU는 IP Packet 전체 크기이므로 같은 값이 아니다.&lt;/li&gt;
&lt;li&gt;TCP Handshake와 작은 Packet이 성공해도 큰 DF Data Packet은 중간 MTU에서 실패할 수 있다.&lt;/li&gt;
&lt;li&gt;필요한 ICMP Too Big 오류가 차단되면 Source가 Packet을 줄이지 못하는 MTU Black Hole이 생긴다.&lt;/li&gt;
&lt;li&gt;IPv6 Router는 Fragmentation하지 않고 ICMPv6 Type 2 Packet Too Big으로 MTU를 알린다.&lt;/li&gt;
&lt;li&gt;PLPMTUD는 End-to-End Probe의 성공과 실패를 이용해 ICMP에만 의존하지 않고 크기를 탐색한다.&lt;/li&gt;
&lt;li&gt;MSS Clamping은 TCP의 실용적인 완화책일 수 있지만 UDP를 해결하지 못하고 근본 MTU 오류를 숨길 수 있다.&lt;/li&gt;
&lt;li&gt;진단할 때 큰 Packet의 마지막 관찰 지점과 ICMP 오류의 Return Path를 분리해서 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Link MTU, Path MTU, PMTU, Path MTU Discovery, PMTUD, DF, ICMP Type 3 Code 4, Fragmentation Needed, Next-Hop MTU, PMTU Cache, Maximum Segment Size, MSS, MTU Black Hole, ICMPv6 Packet Too Big, Packetization Layer PMTUD, PLPMTUD, MSS Clamping, Encapsulation Overhead&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMTUD는 출발 전에 모든 길의 규격을 읽는 절차가 아니다. &lt;br /&gt;큰 Packet을 보낸 뒤 돌아오는 &amp;ldquo;이 길에는 너무 크다&amp;rdquo;는 제어 메시지와 End-to-End 전달 결과로 &lt;br /&gt;현재 경로의 한계를 계속 학습하는 과정이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Source는 경로가 허용하는 크기에 맞춰 Packet을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 IPv4에는 또 다른 한계가 있다. 인터넷에 연결하려는 기기는 계속 늘어났지만 전 세계에서 고유하게 사용할 수 있는 IPv4 주소의 수는 제한되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집 안의 여러 Computer와 Phone이 하나의 공인 IPv4 주소로 동시에 인터넷을 사용하는 것은 어떻게 가능할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;여러 집이 하나의 공인 주소를 함께 쓸 수 있을까?&lt;/b&gt;」라는 질문을 통해 Private Address, NAT, NAPT/PAT, Translation Table과 End-to-End 연결의 변화를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Link MTU</category>
      <category>Network</category>
      <category>Path MTU</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1097</guid>
      <comments>https://peterica.tistory.com/1097#entry1097comment</comments>
      <pubDate>Wed, 22 Jul 2026 22:20:32 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 19. 작은 편지는 가는데 큰 소포만 멈추는 이유는 무엇일까?</title>
      <link>https://peterica.tistory.com/1096</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5dc8Z/dJMcadpcO2M/PKALKMer7wrwlF5HzxkMk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5dc8Z/dJMcadpcO2M/PKALKMer7wrwlF5HzxkMk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5dc8Z/dJMcadpcO2M/PKALKMer7wrwlF5HzxkMk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5dc8Z%2FdJMcadpcO2M%2FPKALKMer7wrwlF5HzxkMk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 6. 너무 큰 소포와 좁은 길의 문제&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Server에 연결했는데 짧은 요청은 성공하고 큰 Data를 보낼 때만 멈추는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소도 맞고 Route도 있으며 작은 Packet은 왕복한다. 그렇다면 연결 전체가 끊긴 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &amp;ldquo;얼마나 많이 보내는가&amp;rdquo;가 아니라 &amp;ldquo;한 Packet에 얼마나 크게 담았는가&amp;rdquo;일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;전송량의 문제
&amp;rarr; 한동안 몇 Byte를 길 위에 둘 것인가?
&amp;rarr; rwnd, cwnd

Packet 크기의 문제
&amp;rarr; 한 번에 몇 Byte를 한 Link에 실을 수 있는가?
&amp;rarr; MTU&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞 글의 Window는 이동 중인 Data의 총량을 제한했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;b&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 700; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-subtree=&quot;aimfl,mfl&quot;&gt;MTU(Maximum Transmission Unit)&lt;/span&gt;&lt;/b&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;는 &lt;/span&gt;&lt;/span&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 500; margin: 0px; text-decoration: none; border-bottom: 0px rgb(238, 240, 255);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;네트워크를 통해 한 번에 전송할 수 있는 &lt;b&gt;최대 데이터 패킷 크기(바이트 단위)&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;를 말한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;&lt;span data-copy-service-computed-style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot; data-sfc-cb=&quot;&quot; data-sfc-root=&quot;ep&quot; data-sfc-cp=&quot;&quot;&gt;다시 말해, MTU&lt;/span&gt;&lt;/span&gt;은 한 Link에서 하나의 Network Layer Packet을 단편화 없이 운반할 수 있는 크기를 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Packet이 좁은 Link를 만나면 작게 나누거나, 보내지 못한다는 안내를 Source로 돌려보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; Ethernet MTU&lt;/b&gt;, &lt;b&gt;IPv4 Fragmentation&lt;/b&gt;, &lt;b&gt;Identification&lt;/b&gt;, &lt;b&gt;DF/MF Flag&lt;/b&gt;, &lt;b&gt;Fragment Offset&lt;/b&gt;, &lt;b&gt;Reassembly&lt;/b&gt;, &lt;b&gt;TCP MSS&lt;/b&gt;를 실제 Byte 계산으로 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 큰 파일과 큰 패킷은 같은 말일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1GB 파일을 보낸다고 1GB짜리 IP Packet 하나가 만들어지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application Data는 여러 계층을 지나며 작은 전송 단위로 나뉜다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1GB File
   &amp;darr; Application이 Stream에 기록
TCP Segment 여러 개
   &amp;darr;
IP Packet 여러 개
   &amp;darr;
각 Link의 Frame 여러 개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Byte Stream을 MSS에 맞는 Segment로 나누어 보내는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Application이 큰 UDP Datagram 하나를 만들면 IP 계층에 큰 Packet 하나로 전달될 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 조건에 따라 IP Fragmentation이 필요해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 문장은 구분해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;큰 파일을 전송한다
&amp;ne; 큰 IP Packet 하나를 전송한다

큰 Application Message
&amp;ne; 항상 큰 IP Packet 하나&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 기준은 전체 업무 Data 크기가 아니라 &lt;b&gt;각 IP Packet이 다음 Link의 MTU 안에 들어가는가&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ MTU는 Frame 전체 크기일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet에서 흔히 보는 MTU는 1500바이트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &amp;ldquo;Ethernet Frame 전체가 1500바이트&amp;rdquo;라고 이해하면 계층의 크기가 섞인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Ethernet MTU 1500은 Ethernet Payload로 단편화 없이 실을 수 있는 &lt;b&gt;IP Packet의 최대 크기&lt;/b&gt;를 뜻한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Ethernet Frame
├─ Ethernet Header
├─ Payload: IP Packet, 최대 1500바이트
└─ Frame Check Sequence 등&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet Header와 FCS까지 포함한 실제 Frame은 1500바이트보다 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN Tag, Preamble, Inter-Frame Gap을 어느 계산에 포함하는지도 목적에 따라 달라진다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Ethernet MTU 1500
&amp;rarr; &amp;ldquo;선 위의 모든 Byte를 합친 크기&amp;rdquo;가 아님
&amp;rarr; Layer 3 Packet을 실을 수 있는 Payload 한계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MTU는 Interface와 Link 기술마다 다를 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 Ethernet에서 흔한 값: 1500&lt;/li&gt;
&lt;li&gt;Tunnel 내부에서 Overhead를 고려해 더 작게 설정한 값&lt;/li&gt;
&lt;li&gt;모든 구간이 지원하도록 구성한 Jumbo Frame 환경의 더 큰 값&lt;/li&gt;
&lt;li&gt;PPP 계열이나 특수 Link의 다른 값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;인터넷의 모든 MTU는 1500&amp;rdquo;이라는 규칙은 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Router는 어느 MTU를 확인할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host A에서 Host B까지 세 Link를 지난다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Host A ── MTU 1500 ── R1 ── MTU 1200 ── R2 ── MTU 1500 ── Host B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host A가 1400바이트 IPv4 Packet을 만들면 첫 Link에는 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 R1이 다음 Hop으로 내보낼 Interface의 MTU는 1200이다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;들어온 Packet: 1400바이트
나갈 Link MTU: 1200바이트

1400 &amp;gt; 1200&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 확인하는 핵심 한계는 &lt;b&gt;Outgoing Interface의 MTU&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 Link에서 들어올 수 있었다는 사실이 다음 Link에도 같은 크기로 나갈 수 있음을 보장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 전체에서 가장 작은 MTU를 &lt;b&gt;Path MTU&lt;/b&gt;(PMTU)라고 한다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;PMTU = min(경로를 이루는 각 Link의 MTU)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 경로의 PMTU는 1200이다. Source가 이를 아직 모르면 중간 Router에서 큰 Packet 문제를 처음 만날 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv4 Router는 큰 Packet을 어떻게 나눌까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Packet이 Outgoing MTU보다 크고 &lt;b&gt;Don't Fragment(DF)&lt;/b&gt; Flag가 설정되지 않았다면 Router가 Packet을 여러 Fragment로 나눌 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;원래 IPv4 Packet
        &amp;darr; Fragmentation
Fragment 1
Fragment 2
Fragment 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Fragment는 독립적으로 전달되는 IPv4 Packet이다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;각 Fragment
├─ 자체 IPv4 Header
├─ 원래 Payload의 일부
└─ 같은 원본임을 나타내는 Fragment 정보&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Frame을 조각내 이어 붙여 보내는 것이 아니다. 원래 IPv4 Payload를 나누고 각 조각 앞에 IPv4 Header를 새로 붙인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Fragmentation은 Header 수와 처리해야 할 Packet 수를 늘린다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 조각은 서로 같은 원본임을 어떻게 알까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header에는 Fragmentation과 Reassembly에 필요한 Field가 있다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Identification
Flags
├─ DF: Don't Fragment
└─ MF: More Fragments
Fragment Offset
Total Length&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Identification&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 원래 Datagram에서 만들어진 Fragment들이 공유하는 식별값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination은 Source Address, Destination Address, Protocol, Identification 같은 정보를 함께 사용해 어느 Fragment가 같은 원본에 속하는지 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ DF &amp;mdash; Don't Fragment&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DF=1&lt;/code&gt;이면 Router가 IPv4 Packet을 단편화하지 말라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet이 Outgoing MTU보다 크면 Router는 그대로 전달하거나 나눌 수 없으므로 Packet을 버리고 오류를 알릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ MF &amp;mdash; More Fragments&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;MF=1&lt;/code&gt;이면 이 Fragment 뒤에 원래 Datagram의 조각이 더 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 Fragment는 &lt;code&gt;MF=0&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Fragment Offset&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Fragment의 Payload가 원래 IPv4 Payload에서 어디부터 시작하는지 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Header에는 8바이트 단위로 저장된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제 Payload 시작 위치 = Fragment Offset 값 &amp;times; 8바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막을 제외한 Fragment의 Payload 크기를 8바이트 배수로 맞추는 이유가 여기에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Total Length&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Fragment 자신의 IPv4 Header와 Payload를 합한 전체 길이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Fragment의 Total Length가 원래 Packet 길이와 같지는 않다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 4000바이트 Packet은 MTU 1500에서 어떻게 나뉠까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Option이 없는 20바이트 Header를 가정하자.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;원래 IPv4 Total Length: 4000바이트
IPv4 Header           :   20바이트
원래 IPv4 Payload     : 3980바이트
Outgoing MTU          : 1500바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fragment 하나에도 20바이트 IPv4 Header가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 Fragment에 실을 수 있는 최대 Payload는 다음처럼 계산한다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1500 - 20 = 1480바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1480은 8바이트로 나누어떨어진다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1480 &amp;divide; 8 = 185&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 Fragment는 원래 Payload의 앞 1480바이트를 담는다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Fragment 1
Payload 범위 : [0, 1480)
Payload 길이 : 1480
Total Length : 20 + 1480 = 1500
Offset Field : 0 &amp;divide; 8 = 0
MF           : 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 Fragment는 그다음 1480바이트를 담는다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Fragment 2
Payload 범위 : [1480, 2960)
Payload 길이 : 1480
Total Length : 20 + 1480 = 1500
Offset Field : 1480 &amp;divide; 8 = 185
MF           : 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남은 Payload는 1020바이트다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;3980 - 1480 - 1480 = 1020&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 Fragment가 마지막 조각을 담는다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Fragment 3
Payload 범위 : [2960, 3980)
Payload 길이 : 1020
Total Length : 20 + 1020 = 1040
Offset Field : 2960 &amp;divide; 8 = 370
MF           : 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표로 모으면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fragment&lt;/td&gt;
&lt;td&gt;Payload 범위&lt;/td&gt;
&lt;td&gt;Payload 길이&lt;/td&gt;
&lt;td&gt;Total Length&lt;/td&gt;
&lt;td&gt;Offset Field&lt;/td&gt;
&lt;td&gt;MF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;[0, 1480)&lt;/td&gt;
&lt;td&gt;1480&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;[1480, 2960)&lt;/td&gt;
&lt;td&gt;1480&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;td&gt;185&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;[2960, 3980)&lt;/td&gt;
&lt;td&gt;1020&lt;/td&gt;
&lt;td&gt;1040&lt;/td&gt;
&lt;td&gt;370&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 Fragment의 Payload를 합하면 원래 Payload와 같아야 한다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1480 + 1480 + 1020 = 3980&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 새 IPv4 Header를 가지므로 Link를 지나는 IP Byte 수는 늘어난다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1500 + 1500 + 1040 = 4040바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 4000바이트보다 40바이트가 늘었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 하나였던 IPv4 Header가 세 개가 되면서 Header 두 개가 추가된 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header에 Option이 있으면 Header 길이와 Fragment 계산도 달라질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Fragment Offset은 왜 8바이트 단위일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header의 Fragment Offset Field는 13비트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Byte 단위로만 표현하면 약 8KB 위치까지만 가리킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8바이트 단위로 해석하면 제한된 Bit로 IPv4 최대 길이 범위의 위치를 나타낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Offset Field 185
&amp;rarr; 원래 Payload의 185 &amp;times; 8
&amp;rarr; 1480바이트 위치부터 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture 도구는 Offset Field의 원래 단위 값이나 Byte로 환산한 위치를 표시할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 볼 때 도구가 어느 단위를 보여 주는지 함께 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 Offset이 &amp;ldquo;두 번째 조각&amp;rdquo; 같은 Fragment 순번이 아니라 원래 Payload 안의 시작 위치라는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 조각은 중간 Router에서 다시 합칠까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 IPv4 Forwarding에서 Router가 만든 Fragment는 최종 Destination에서 Reassembly된다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Source
  │ 원래 Packet
  ▼
Router R1
  │ Fragmentation
  ├─ F1 ───────────────┐
  ├─ F2 ───────────────┼─&amp;gt; Destination에서 Reassembly
  └─ F3 ───────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Router가 조각을 다시 합쳐 원본으로 만든 뒤 전달하는 방식이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fragment들은 서로 다른 Queue와 경로를 거칠 수도 있고 순서가 바뀌어 도착할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination은 Fragment Offset을 이용해 제자리를 맞춘다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;도착 순서: F2 &amp;rarr; F1 &amp;rarr; F3
재조립 결과: F1 + F2 + F3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination은 모든 조각이 도착할 때까지 Reassembly 상태와 Buffer를 유지해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 조각 하나만 사라지면 나머지는 사용할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 IPv4 Packet의 Fragment 세 개 중 두 개만 도착했다고 하자.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;F1 도착
F2 손실
F3 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP는 F1과 F3만으로 원래 Payload를 상위 Protocol에 전달할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reassembly Timer 안에 필요한 Fragment가 모두 모이지 않으면 Destination은 미완성 상태를 버린다.&lt;/p&gt;
&lt;pre class=&quot;julia&quot;&gt;&lt;code&gt;Fragment 하나 손실
&amp;rarr; 원래 IPv4 Datagram 전체 재조립 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 자체에는 &amp;ldquo;F2만 다시 보내 달라&amp;rdquo;는 End-to-End 재전송 기능이 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP Data였다면 TCP가 ACK와 Timer로 손실을 감지해 해당 Byte 범위를 재전송할 수 있다.&lt;/li&gt;
&lt;li&gt;UDP Datagram이었다면 UDP가 자동으로 원본 Datagram을 재전송하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 큰 Packet이 여러 Fragment로 나뉘면 그중 하나라도 손실될 표면이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조각 수가 많을수록 Reassembly 실패 가능성과 처리 비용도 커진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Fragmentation은 왜 가능하면 피하려 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Fragmentation은 서로 다른 MTU를 가진 Link를 이어 주는 호환 장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 비용이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Packet과 Header가 늘어난다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Fragment에 IPv4 Header가 필요하므로 전송 Byte와 Packet 처리 횟수가 늘어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Fragment 하나의 손실이 원본 전체에 영향을 준다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 계층은 완성되지 않은 원래 Datagram을 받지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Destination의 Reassembly 자원을 사용한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미완성 Fragment를 일정 시간 기억해야 하므로 Memory와 상태가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 중간 장비의 처리가 어려워진다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP와 UDP Port는 일반적으로 첫 Fragment의 Transport Header에만 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;첫 Fragment
├─ IP Header
├─ TCP/UDP Header
└─ Data 일부

뒤 Fragment
├─ IP Header
└─ Data의 나머지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall이나 NAT가 Transport Port를 기준으로 판단할 때 뒤 Fragment만 보고는 같은 정보를 바로 확인하기 어렵다. 장비는 Fragment 상태를 추적하거나 정책에 따라 Fragment를 제한할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 보안 검사가 복잡해진다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비정상적인 Offset이나 겹치는 Fragment는 Reassembly 해석 차이를 악용할 수 있다. 운영체제와 보안 장비는 이런 입력을 엄격하게 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 현대의 일반적인 End-to-End 통신은 Source가 Path MTU에 맞는 Packet을 만들어 중간 Router의 Fragmentation을 피하는 방향을 선호한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ DF가 설정된 큰 Packet은 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Packet이 Outgoing MTU보다 큰데 &lt;code&gt;DF=1&lt;/code&gt;이라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 Packet을 나눌 수 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Packet 1400바이트
Outgoing MTU 1200
DF=1

&amp;rarr; Fragmentation 금지
&amp;rarr; Packet Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 일반적으로 Source에 다음 ICMP 오류를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ICMP Destination Unreachable
Type 3, Code 4
Fragmentation Needed and DF Set&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 정보에는 다음 Hop에서 허용되는 MTU 값이 포함될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 이 안내를 바탕으로 더 작은 Packet을 만들어 다시 시도할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ICMP가 정책으로 차단되거나 Return Path에서 사라지면 Source는 &amp;ldquo;작게 보내라&amp;rdquo;는 정보를 얻지 못한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;작은 Packet &amp;rarr; MTU 안에 들어가 성공
큰 Packet   &amp;rarr; DF 때문에 Drop
ICMP 안내   &amp;rarr; 차단

결과: 연결은 되는 것처럼 보이지만 큰 Data에서 멈춤&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 현상을 &lt;b&gt;Path MTU Black Hole&lt;/b&gt;이라고 부른다. Source가 경로의 가장 작은 MTU를 알아내고 이에 대응하는 과정은 다음 글에서 자세히 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP는 큰 Data를 보낼 때 항상 IP Fragmentation을 만들까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로는 그렇지 않도록 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Handshake에서 Endpoint는 자신이 받을 수 있는 최대 TCP Payload 크기를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Maximum Segment Size&lt;/b&gt;(MSS)로 알릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet MTU 1500, Option 없는 IPv4 Header 20바이트, 기본 TCP Header 20바이트를 가정하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;1500 - IPv4 Header 20 - TCP Header 20
= TCP Payload 1460바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 흔히 보는 IPv4 TCP MSS가 1460이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 기본 Header 40바이트를 가정하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;1500 - IPv6 Header 40 - TCP Header 20
= TCP Payload 1440바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Header Option이나 Extension Header가 있으면 실제 한 Packet에 담을 TCP Data 크기는 더 작아질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Application이 수 MB를 한 번에 &lt;code&gt;write()&lt;/code&gt;해도 적절한 Segment로 나누어 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Application write: 1MB
        &amp;darr;
TCP Segment: 대략 MSS 이하의 Payload 여러 개
        &amp;darr;
MTU 이하의 IP Packet 여러 개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS는 TCP Payload 기준이고 MTU는 IP Packet 전체 기준이라는 차이를 기억해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS만으로 경로 중간의 더 작은 MTU를 항상 알 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대가 광고한 MSS는 주로 상대 Interface 쪽 수신 조건을 반영하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Tunnel이나 더 좁은 Link는 별도로 발견해야 할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP와 ping의 크기는 어떻게 계산할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Option이 없고 MTU가 1500이라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 IPv4 Packet에 단편화 없이 담을 수 있는 UDP Payload의 단순 최대값은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;1500 - IPv4 Header 20 - UDP Header 8
= UDP Payload 1472바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Echo Request의 &lt;code&gt;ping -s&lt;/code&gt; 값도 많은 Linux 구현에서 ICMP Data 크기를 뜻한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;IPv4 Header 20
+ ICMP Header 8
+ ping Data 1472
= IPv4 Total Length 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Ethernet MTU 1500 경로에서 IPv4 &lt;code&gt;ping -s 1472&lt;/code&gt;가 경계 확인 예시로 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 IP Option, Tunnel, 다른 Link MTU, 명령 구현 차이에 따라 숫자는 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping -s&lt;/code&gt;가 항상 IP Packet 전체 크기라는 뜻도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP API는 1472보다 큰 Datagram을 만들 수 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것이 Ethernet MTU 1500 경로에서 하나의 IP Packet으로 그대로 전달된다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Fragmentation이나 오류가 발생할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IPv6 Router도 큰 Packet을 나눌까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 중간 Router가 Packet을 Fragmentation하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 다음 Link로 전달하기에 너무 큰 IPv6 Packet을 받으면 Packet을 버리고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source에 &lt;b&gt;ICMPv6 Packet Too Big&lt;/b&gt; 메시지를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IPv4
&amp;rarr; 조건에 따라 Router Fragmentation 가능

IPv6
&amp;rarr; Router Fragmentation 없음
&amp;rarr; Source만 Fragment Header를 사용해 Fragment 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 IPv6에서는 Source가 경로의 MTU를 알고 Packet 크기를 조절하는 과정이 필수적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMPv6 Packet Too Big을 무조건 차단하면 큰 Packet 전달이 멈출 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP가 단순한 진단용 부가 기능이 아니라 정상적인 전달 과정의 제어 메시지이기도 한 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Tunnel을 지나면 왜 MTU가 더 작아질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPN이나 Overlay Tunnel은 원래 Packet 바깥에 새로운 Header를 붙일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Outer IP Header
Tunnel Header
Inner IP Packet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리 Link가 허용하는 전체 크기는 그대로인데 바깥 Header가 공간을 차지하면 내부 Packet에 사용할 수 있는 크기는 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;물리 Link MTU 1500
- Tunnel Overhead
= Tunnel 내부에서 안전한 Packet 크기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tunnel Endpoint가 내부 Interface MTU를 줄이거나 TCP MSS를 조정하는 이유가 여기에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 양 끝 Host만 Jumbo Frame을 지원한다고 중간 모든 Link에서 큰 Packet이 통과하는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Layer 2 구간과 경로 장비 전체가 해당 크기를 일관되게 지원해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Capture에서 Fragment를 어떻게 알아볼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서 IPv4 Fragment 관련 Field를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ip.id
ip.len
ip.flags.df
ip.flags.mf
ip.frag_offset&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Fragment를 좁혀 보는 Display Filter는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ip.flags.mf == 1 || ip.frag_offset &amp;gt; 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 원래 Datagram의 Fragment에서는 다음을 비교한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ip.id&lt;/code&gt;가 같은가?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ip.len&lt;/code&gt;은 각 Fragment의 Total Length와 맞는가?&lt;/li&gt;
&lt;li&gt;마지막을 제외한 Fragment에 &lt;code&gt;MF=1&lt;/code&gt;이 있는가?&lt;/li&gt;
&lt;li&gt;Offset을 따라 Payload 범위가 이어지는가?&lt;/li&gt;
&lt;li&gt;Reassembly 결과가 표시되는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤 Fragment에는 Transport Header가 없으므로 Wireshark가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 Fragment와 Reassembly 상태를 보지 못하면 TCP나 UDP로 완전히 해석하지 못할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 큰 Packet처럼 보이는데 실제 Link에도 나갔을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host 내부에서 Packet Capture를 보면 Interface MTU보다 큰 TCP Packet처럼 보일 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network Interface Card와 Kernel은 CPU 비용을 줄이기 위해 여러 최적화를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TSO / GSO
&amp;rarr; Kernel에서 큰 단위로 넘기고 NIC 또는 하위 계층에서 실제 전송 크기로 분할

GRO / LRO
&amp;rarr; 받은 여러 Packet을 Host 내부에서 큰 단위로 합쳐 상위 계층에 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 지점이 분할 전이거나 병합 후이면 실제 Wire에 존재하지 않았던 큰 단위가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Host Capture에서 32KB로 보임
&amp;ne; Wire에 32KB Ethernet Frame 하나가 나감&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 Wire 크기를 확인하려면 Offload 영향을 이해하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 다른 장비의 Mirror Port나 적절한 Capture 지점에서 관찰해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MTU 문제를 분석할 때 Capture 화면의 한 줄만 보고 &amp;ldquo;Jumbo Packet이 전송됐다&amp;rdquo;고 결론 내리면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Header와 동작으로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;height: 229px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한 도로가 운반할 수 있는 소포 크기&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Link MTU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;해당 Link에서 단편화 없이 실을 수 있는 IP Packet 한계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;전체 경로에서 가장 좁은 소포 규격&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Path MTU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Source와 Destination 경로의 최소 MTU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;큰 소포를 여러 상자로 나눔&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;IPv4 Fragmentation&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;원래 IP Payload를 나누고 각 조각에 IPv4 Header를 부착&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 주문의 상자 번호표&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Identification&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 원래 IPv4 Datagram의 Fragment를 연관시킴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;절대 나누지 말라는 표시&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;DF Flag&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Router Fragmentation을 금지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;뒤에 상자가 더 있다는 표시&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MF Flag&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;마지막이 아닌 Fragment임을 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;원래 내용에서 조각의 위치&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Fragment Offset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;8바이트 단위로 Payload 시작 위치 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;목적지에서 모든 상자를 다시 맞춤&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Reassembly&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Fragment를 원래 IPv4 Datagram으로 복원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TCP 한 봉투에 넣을 본문 한계&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MSS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TCP Segment의 최대 Data 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;너무 크니 줄여 달라는 반송 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ICMP Fragmentation Needed&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;DF Packet이 MTU를 넘었음을 Source에 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;포장지가 차지하는 공간&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Tunnel Overhead&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Outer Header 때문에 내부 Packet 가용 크기가 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기 관계를 한 번 더 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Application Data
       &amp;darr; TCP가 MSS를 고려해 나눔
TCP Header + TCP Data
       &amp;darr; IP Header를 붙임
IP Packet &amp;le; Path MTU가 되도록 조절
       &amp;darr; Link Header와 Trailer를 붙임
Link Frame&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 직접 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;가 있는 개인 Linux VM에서 수행한다. 세 Network Namespace 안에만 가상 경로를 만들며 실제 Interface MTU는 바꾸지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;mtu-client ── MTU 1500 ── mtu-router ── MTU 1200 ── mtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace와 가상 Link를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add mtu-client
sudo ip netns add mtu-router
sudo ip netns add mtu-server

sudo ip link add veth-c type veth peer name veth-rc
sudo ip link add veth-rs type veth peer name veth-s

sudo ip link set veth-c netns mtu-client
sudo ip link set veth-rc netns mtu-router
sudo ip link set veth-rs netns mtu-router
sudo ip link set veth-s netns mtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Address와 Interface를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n mtu-client addr add 10.19.1.2/24 dev veth-c
sudo ip -n mtu-router addr add 10.19.1.1/24 dev veth-rc
sudo ip -n mtu-router addr add 10.19.2.1/24 dev veth-rs
sudo ip -n mtu-server addr add 10.19.2.2/24 dev veth-s

sudo ip -n mtu-client link set lo up
sudo ip -n mtu-router link set lo up
sudo ip -n mtu-server link set lo up
sudo ip -n mtu-client link set veth-c up
sudo ip -n mtu-router link set veth-rc up
sudo ip -n mtu-router link set veth-rs up mtu 1200
sudo ip -n mtu-server link set veth-s up mtu 1200&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route와 IPv4 Forwarding을 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip -n mtu-client route add default via 10.19.1.1
sudo ip -n mtu-server route add default via 10.19.2.1
sudo ip netns exec mtu-router \
  sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Terminal에서 Router의 모든 Interface를 관찰한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec mtu-router \
  tcpdump -n -vv -i any -s 0 -w /tmp/mtu-fragments.pcap \
  'icmp or (ip[6:2] &amp;amp; 0x3fff != 0)'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 실험은 DF를 사용하지 않아 Router가 Fragmentation할 수 있게 한다. Linux &lt;code&gt;ping&lt;/code&gt;의 &lt;code&gt;-s&lt;/code&gt;는 ICMP Data 크기다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec mtu-client \
  ping -M dont -c 1 -s 1400 10.19.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header 20바이트와 ICMP Header 8바이트를 더하면 1428바이트다. 첫 Link의 MTU 1500에는 들어가지만 Router의 다음 Link MTU 1200은 넘는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 같은 &lt;code&gt;ip.id&lt;/code&gt;를 가진 Fragment와 MF, Offset을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 실험은 DF를 설정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec mtu-client \
  ping -M do -c 1 -s 1400 10.19.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router는 1428바이트 Packet을 MTU 1200 Link로 내보내기 위해 나눌 수 없으므로 Drop하고 ICMP Fragmentation Needed를 돌려보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 실험은 전체 IPv4 Packet을 정확히 1200바이트에 맞춘다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;IPv4 Header 20 + ICMP Header 8 + Data 1172 = 1200&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec mtu-client \
  ping -M do -c 1 -s 1172 10.19.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DF가 있어도 Packet이 Path MTU 안에 들어가므로 정상 Echo Reply를 기대할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux &lt;code&gt;ping -M&lt;/code&gt; 동작과 출력은 iputils 버전에 따라 다를 수 있다. 이 실습은 IPv4 기본 Header를 가정하며 Offload와 Kernel 설정에 따라 Capture 표현이 조금 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다. 가상 Interface와 MTU 설정도 함께 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del mtu-client
sudo ip netns del mtu-router
sudo ip netns del mtu-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공용 Server에 큰 Probe를 반복해서 보내지 않는다. MTU 변경과 고의적인 Fragmentation은 개인 격리 환경에서만 관찰한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰 파일과 큰 IP Packet은 같은 개념이 아니며 TCP는 큰 Byte Stream을 여러 Segment로 나눈다.&lt;/li&gt;
&lt;li&gt;일반적인 Ethernet MTU 1500은 Ethernet Frame 전체가 아니라 Frame Payload로 실을 수 있는 IP Packet 크기 기준이다.&lt;/li&gt;
&lt;li&gt;Router는 Packet을 내보낼 Outgoing Interface의 MTU와 IPv4 DF Flag를 확인한다.&lt;/li&gt;
&lt;li&gt;IPv4에서 DF가 없으면 Router가 큰 Packet의 Payload를 여러 Fragment로 나눌 수 있다.&lt;/li&gt;
&lt;li&gt;같은 원본의 Fragment는 Identification을 공유하고 MF와 Fragment Offset으로 끝과 위치를 나타낸다.&lt;/li&gt;
&lt;li&gt;Fragment Offset Field는 8바이트 단위이며 마지막을 제외한 Fragment Payload는 8바이트 배수로 구성된다.&lt;/li&gt;
&lt;li&gt;IPv4 Fragment는 일반적으로 최종 Destination에서 Reassembly된다.&lt;/li&gt;
&lt;li&gt;Fragment 하나가 손실되면 원래 Datagram 전체를 재조립할 수 없으며 IPv4가 조각만 재전송하지는 않는다.&lt;/li&gt;
&lt;li&gt;DF Packet이 Outgoing MTU보다 크면 Router는 Drop하고 ICMP Type 3 Code 4를 보낼 수 있다.&lt;/li&gt;
&lt;li&gt;MSS는 TCP Payload 크기이고 MTU는 IP Packet 전체 크기라는 차이가 있다.&lt;/li&gt;
&lt;li&gt;IPv6 Router는 Fragmentation하지 않으며 너무 큰 Packet에 ICMPv6 Packet Too Big으로 응답한다.&lt;/li&gt;
&lt;li&gt;Tunnel Header와 Offload는 Capture에서 보이는 크기와 실제 Link 크기를 해석할 때 함께 고려해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Maximum Transmission Unit, MTU, Path MTU, IPv4 Fragmentation, Identification, Don't Fragment, DF, More Fragments, MF, Fragment Offset, Total Length, Reassembly, Reassembly Timer, Maximum Segment Size, MSS, ICMP Fragmentation Needed, IPv6 Packet Too Big, Tunnel Overhead, TSO, GSO, GRO, LRO&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MTU는 한 번의 배송에 담을 수 있는 IP Packet의 크기다. 큰 Packet을 억지로 조각내면 전달은 이어질 수 있지만, 조각 수와 Header, 손실 표면, 재조립 상태가 함께 늘어난다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Router가 큰 IPv4 Packet을 Fragmentation할 수 있다는 것은 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 현대 네트워크는 중간 Fragmentation을 피하고 Source가 처음부터 경로에 맞는 크기로 보내는 방식을 선호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 아직 가 보지 않은 경로의 가장 작은 MTU를 어떻게 알 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &amp;ldquo;너무 크다&amp;rdquo;는 ICMP 안내가 Firewall에서 사라지면 왜 Handshake와 작은 Data는 되는데 큰 Data만 멈출까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까?&lt;/b&gt;」라는 질문을 통해 Path MTU Discovery, ICMP Fragmentation Needed, ICMPv6 Packet Too Big, PLPMTUD와 MTU Black Hole을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1096</guid>
      <comments>https://peterica.tistory.com/1096#entry1096comment</comments>
      <pubDate>Tue, 21 Jul 2026 22:19:12 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 18. 받는 사람이 감당하지 못할 만큼 보내면 어떻게 될까?</title>
      <link>https://peterica.tistory.com/1095</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be8fvG/dJMcahSEMXS/VoAUEiqqMlpJwE8kbMRw21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be8fvG/dJMcahSEMXS/VoAUEiqqMlpJwE8kbMRw21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be8fvG/dJMcahSEMXS/VoAUEiqqMlpJwE8kbMRw21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe8fvG%2FdJMcahSEMXS%2FVoAUEiqqMlpJwE8kbMRw21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 5. 믿을 수 없는 길 위에서 믿을 수 있게 보내기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 잃어버린 Byte를 찾아 다시 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 확인 응답이 오기 전에 Data를 무제한으로 보내도 되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;받는 Application이 천천히 읽으면 Receiver의 Memory Buffer가 가득 찬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽 Computer가 충분히 빨라도 중간 Router로 Packet이 한꺼번에 몰리면 Queue가 넘친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로는 둘 다 &amp;ldquo;Sender가 너무 빨리 보냈다&amp;rdquo;는 문제처럼 보인다. 그러나 보호해야 할 대상이 다르다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;Receiver가 감당하지 못함
&amp;rarr; Flow Control
&amp;rarr; Receive Window(rwnd)

Network가 감당하지 못함
&amp;rarr; Congestion Control
&amp;rarr; Congestion Window(cwnd)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편으로 말하면 하나는 수신 창고의 남은 공간이고, 다른 하나는 도시 전체 도로의 혼잡도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창고가 넓어도 도로가 막힐 수 있고, 도로가 비어 있어도 창고가 가득 찰 수 있다. TCP는 두 한계를 동시에 지켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Receive Buffer&lt;/b&gt;, &lt;b&gt;Advertised Window&lt;/b&gt;, &lt;b&gt;Sliding Window&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Zero Window&lt;/b&gt;, &lt;b&gt;Persist Timer&lt;/b&gt;, &lt;b&gt;Congestion Window&lt;/b&gt;, &lt;b&gt;Slow Start&lt;/b&gt;, &lt;b&gt;Congestion Avoidance&lt;/b&gt;를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Byte 수로 연결해 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP 연결에는 두 개의 대기실이 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 TCP를 사용하는 장면을 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sending Application
        │ write()
        ▼
   Send Buffer
        │ TCP 전송
        ▼
      Network
        │ TCP 수신
        ▼
  Receive Buffer
        │ read()
        ▼
Receiving Application&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sending Application이 &lt;code&gt;write()&lt;/code&gt;한 Data가 즉시 Network로 모두 나가는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 운영체제의 &lt;b&gt;Send Buffer&lt;/b&gt;에 들어갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver에서도 Packet이 도착했다고 곧바로 Application 처리가 끝난 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 순서를 맞춘 Byte는 &lt;b&gt;Receive Buffer&lt;/b&gt;에 머물다가 Application의 &lt;code&gt;read()&lt;/code&gt;로 소비된다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Network 도착 속도 &amp;gt; Application 읽기 속도
&amp;rarr; Receive Buffer 사용량 증가
&amp;rarr; 남은 공간 감소&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 남은 공간을 Sender에게 알리지 않으면 Sender는 Buffer가 넘칠 때까지 계속 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 막는 것이 &lt;b&gt;TCP Flow Control&lt;/b&gt;, 흐름 제어다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Receiver는 남은 공간을 어떻게 알릴까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Header에는 16비트 &lt;b&gt;Window&lt;/b&gt; Field가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver는 ACK를 보낼 때 자신이 추가로 받아들일 수 있는 Byte 범위를 이 값으로 광고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Advertised Receive Window&lt;/b&gt;, 보통 &lt;code&gt;rwnd&lt;/code&gt;라고 부른다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ACK=5001, Window=4000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순화하면 다음 뜻이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;5000번 Byte까지 연속으로 받았다.
다음에는 5001번을 기다린다.
5001번부터 추가로 4000바이트를 받을 공간이 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 보낼 수 있는 수신 창의 범위는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[5001, 9001)

Byte 5001부터 9000까지
총 4000바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window의 오른쪽 끝은 대략 &lt;code&gt;ACK Number + rwnd&lt;/code&gt;로 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 Application의 읽기 속도와 Buffer 상태를 바탕으로 Window를 광고하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 그 범위를 넘는 새 Data를 보내지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Sliding Window는 무엇이 미끄러진다는 뜻일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 ACK를 기다릴 때마다 한 Segment씩 멈춘다면 긴 경로를 효율적으로 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Window 안에서 여러 Byte를 연속으로 보낸 뒤 누적 ACK를 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 허용된 범위가 다음과 같다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ACK=5001, rwnd=4000
허용 범위: [5001, 9001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 5001부터 7000까지 2000바이트를 이미 보냈지만 아직 ACK를 받지 못했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;허용 Window  : [5001, 9001)  4000바이트
In-flight    : [5001, 7001)  2000바이트
추가 전송 가능: [7001, 9001)  2000바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Receiver가 앞의 2000바이트를 읽고 다음 ACK를 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ACK=7001, rwnd=4000
새 허용 범위: [7001, 11001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인된 Byte만큼 Window의 왼쪽과 오른쪽 경계가 앞으로 이동했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;이전: [5001 -------- 9001)
이후:       [7001 -------- 11001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 &lt;b&gt;Sliding Window&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 구현은 Sequence Number, ACK, Buffer 상태, 손실 복구를 함께 관리하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 확인되지 않은 Data의 양을 제한하면서 ACK가 전진할 때 새 Byte를 보낼 공간도 함께 앞으로 이동시키는 데 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Window는 왜 줄어들기도 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver TCP가 Data를 받는 속도보다 Application이 읽는 속도가 느리다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Receive Buffer 전체: 8000바이트

처음 사용 중: 2000바이트 &amp;rarr; 남은 공간 6000
추가 수신 후: 5000바이트 &amp;rarr; 남은 공간 3000
추가 수신 후: 8000바이트 &amp;rarr; 남은 공간 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 보내는 Window도 작아질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Window=6000
Window=3000
Window=0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Flow Control&lt;/b&gt;은 Receiver Application을 빠르게 만드는 기술이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 느릴 때 Sender가 그 사실을 모르고 Receive Buffer를 넘치게 하지 않도록 &lt;b&gt;압력을 Sender 방향으로 되돌려 보내는 Backpressure&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 압력은 결국 Sending Application에도 전달될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Send Buffer까지 가득 차면 &lt;code&gt;send()&lt;/code&gt;나 &lt;code&gt;write()&lt;/code&gt;가 기다리거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Non-blocking Socket에서는 지금 당장 쓸 수 없다는 결과를 돌려줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 느려진 것이 아니라 전체 처리 사슬이 가장 느린 소비자의 속도에 맞춰진 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Window가 0이면 연결이 끊긴 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver Buffer에 여유가 없으면 ACK에 &lt;b&gt;Zero Window&lt;/b&gt;를 광고할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Receiver &amp;rarr; Sender
ACK=9001, Window=0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 오류나 연결 종료가 아니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;지금까지의 Byte는 받았다.
하지만 새 Data를 받을 공간이 현재는 없다.
잠시 멈춰 달라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 새 Data 전송을 멈춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Receiver Application이 Buffer를 읽으면 Receiver는 더 큰 Window를 알리는 &lt;b&gt;Window Update&lt;/b&gt;를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;Receiver Application read()
&amp;rarr; Buffer 공간 생성
&amp;rarr; Window Update: Window=4096
&amp;rarr; Sender 전송 재개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Window Update Packet이 돌아오는 길에서 사라지면 어떻게 될까?&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Sender   : Window가 여전히 0이라고 생각
Receiver : 공간이 생겼지만 Update가 손실됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽이 영원히 기다리는 상황을 막기 위해 Sender는 &lt;b&gt;Persist Timer&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timer가 만료되면 작은 &lt;b&gt;Zero Window Probe&lt;/b&gt;를 보내 Receiver가 현재 ACK와 Window 정보를 다시 알리게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe의 구체적인 구성과 간격은 구현에 따라 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 역할은 &amp;ldquo;공간이 생겼다는 안내 하나가 손실되어 연결이 영원히 멈추는 것&amp;rdquo;을 방지하는 데 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Header의 16비트 Window로 충분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Header의 Window Field는 16비트이므로 원래 값만 사용하면 최대 65,535까지 표현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;2^16 - 1 = 65,535바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고속이면서 RTT가 긴 경로에서는 이 크기가 너무 작을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 100Mbps 경로의 RTT가 40ms라고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;100,000,000 bit/s &amp;times; 0.040s = 4,000,000 bit
4,000,000 bit &amp;divide; 8 = 500,000 byte&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로를 계속 채우려면 단순 계산으로 약 500,000바이트가 이동 중일 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;65,535바이트 Window만으로는 ACK를 기다리는 동안 Link가 비게 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대역폭과 왕복 시간의 곱을 &lt;b&gt;Bandwidth-Delay Product&lt;/b&gt;(BDP)라고 한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;BDP = Bandwidth &amp;times; RTT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Handshake의 &lt;b&gt;Window Scale Option&lt;/b&gt;으로 Header의 Window 값을 왼쪽으로 몇 Bit 이동해 해석할지 협상할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Header Window 값: 32,768
Window Scale     : 4

실제 Window = 32,768 &amp;times; 2^4
            = 524,288바이트&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Endpoint는 자신이 보낼 Receive Window 값을 상대가 어떻게 확장해 해석할지 SYN에서 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scale은 방향별일 수 있으며 SYN과 SYN-ACK의 Window 값 자체에는 확장 해석을 적용하지 않고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Segment의 Window Field에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window Scale은 Receive Buffer 공간을 실제로 만들어 주는 기술이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Buffer와 긴 경로를 활용할 수 있도록 더 큰 Window를 Header에서 표현하게 해 준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Receiver가 충분히 빠르면 마음껏 보내도 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Receiver Application이 매우 빠르고 &lt;code&gt;rwnd&lt;/code&gt;도 충분히 크다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 Sender의 Packet이 하나의 느린 Link로 몰린다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender A ─┐
Sender B ─┼─&amp;gt; Router Queue ─&amp;gt; 좁은 Link
Sender C ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;들어오는 속도가 나가는 속도보다 빠르면 Router Queue가 쌓인다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Queue 여유 있음 &amp;rarr; 잠시 대기 후 전달
Queue 증가      &amp;rarr; 지연 증가
Queue 가득 참   &amp;rarr; Packet Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver의 &lt;code&gt;rwnd&lt;/code&gt;는 이 Router Queue 상태를 나타내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 창고가 비어 있다는 사실만 믿고 모두가 최대 속도로 보내면 중간 길이 무너질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크가 감당할 것으로 추정되는 In-flight Data의 양을 Sender가 제한하는 값이 &lt;b&gt;Congestion Window&lt;/b&gt;, &lt;code&gt;cwnd&lt;/code&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ cwnd는 Packet Header에서 누가 알려줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;rwnd&lt;/code&gt;는 Receiver가 TCP Header의 Window Field로 광고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;code&gt;cwnd&lt;/code&gt;는 일반적으로 Receiver나 Router가 &amp;ldquo;현재 32KB로 하라&amp;rdquo;고 직접 보내는 Header Field가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender의 TCP Stack이 ACK 도착, 손실, RTT, ECN 같은 신호를 관찰해 내부적으로 관리한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;rwnd
├─ Receiver가 계산
├─ TCP Window Field로 광고
└─ Receiver Buffer 보호

cwnd
├─ Sender가 추정&amp;middot;관리
├─ 일반 TCP Window Field로 직접 전송되지 않음
└─ Network 혼잡 완화&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture만 보고 현재 &lt;code&gt;cwnd&lt;/code&gt;를 정확히 읽을 수 없는 이유가 여기에 있다. Capture에서는 ACK, 손실, 전송 간격을 관찰할 수 있지만 &lt;code&gt;cwnd&lt;/code&gt; 자체는 Sender 운영체제의 내부 상태다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 실제로 보낼 수 있는 양은 어느 Window를 따를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Sender는 &lt;code&gt;rwnd&lt;/code&gt;와 &lt;code&gt;cwnd&lt;/code&gt; 중 더 작은 한계를 넘어 새 Data를 In-flight 상태로 만들 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순화한 계산은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;전송 Window = min(rwnd, cwnd)

추가 전송 가능량
= 전송 Window - 현재 In-flight Data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 상태를 보자.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;rwnd      = 64KB
cwnd      = 24KB
In-flight = 16KB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;min(64KB, 24KB) - 16KB = 8KB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver에는 64KB 공간이 있지만 Network 한계 추정치가 더 작으므로 새로 8KB 정도만 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대 상황도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;rwnd      = 12KB
cwnd      = 40KB
In-flight = 8KB

min(12KB, 40KB) - 8KB = 4KB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network는 더 받을 수 있을 것으로 보이지만 Receiver 공간이 작아 4KB만 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 전송량에는 Application이 준비한 Data, Send Buffer, MSS, Pacing, 손실 복구 상태 같은 조건도 관여한다. &lt;code&gt;min(rwnd, cwnd)&lt;/code&gt;는 두 핵심 Window의 관계를 이해하기 위한 중심 식이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 처음 만난 길의 수용량은 어떻게 알까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 TCP 연결이 시작될 때 Sender는 경로의 정확한 가용 용량을 알지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 매우 많은 Packet을 보내면 좁은 Link의 Queue를 한꺼번에 넘칠 수 있다. 반대로 지나치게 조심하면 빠른 경로를 오랫동안 활용하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 전통적인 접근은 작은 Initial Congestion Window에서 시작해 ACK가 정상적으로 돌아오는 동안 전송량을 빠르게 늘려 보는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Slow Start&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름과 달리 매 RTT마다 &lt;code&gt;cwnd&lt;/code&gt;가 대략 두 배로 커질 수 있어 성장 속도는 빠르다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;설명용 예시

시작      : cwnd 10 MSS
약 1 RTT 후: cwnd 20 MSS
약 2 RTT 후: cwnd 40 MSS
약 3 RTT 후: cwnd 80 MSS&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK 하나가 도착하면 일부 Data가 Network를 빠져나갔다는 단서가 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 Window를 열어 새 Data를 보낸다. 이 흐름을 &lt;b&gt;ACK Clocking&lt;/b&gt;이라고 설명하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 &lt;code&gt;cwnd&lt;/code&gt;의 정확한 크기와 증가 방식은 TCP 구현과 알고리즘에 따라 달라질 수 있다. 위 숫자는 &amp;ldquo;ACK가 돌아오면 빠르게 탐색한다&amp;rdquo;는 원리를 보기 위한 예시다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 빠르게 늘리기만 하면 결국 다시 넘치지 않을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞는 이야기다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slow Start의 지수적 성장을 계속하면 언젠가 병목을 만날 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 &lt;b&gt;Slow Start Threshold&lt;/b&gt;, &lt;code&gt;ssthresh&lt;/code&gt;를 기준으로 더 조심스럽게 늘리는 &lt;b&gt;Congestion Avoidance&lt;/b&gt; 단계로 전환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 Reno 계열의 원리를 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Slow Start
&amp;rarr; ACK가 돌아오는 동안 빠르게 증가

Congestion Avoidance
&amp;rarr; 대략 RTT당 1 MSS 수준으로 완만하게 증가

Congestion 신호
&amp;rarr; cwnd를 줄이고 다시 탐색&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 흔히 &lt;b&gt;Additive Increase, Multiplicative Decrease&lt;/b&gt;(AIMD)라는 방향으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실 신호의 강도도 같지 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Duplicate ACK와 Fast Retransmit는 뒤의 Packet이 계속 도착한다는 단서가 있다.&lt;/li&gt;
&lt;li&gt;RTO는 한동안 ACK 진전이 없었다는 더 강한 신호다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 알고리즘은 두 상황에서 &lt;code&gt;cwnd&lt;/code&gt;를 다르게 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 현대 TCP가 Reno의 숫자를 그대로 쓰는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CUBIC처럼 Window 성장 곡선을 다르게 설계한 알고리즘도 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BBR처럼 전달률과 RTT 모델을 중심으로 경로를 추정하는 접근도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통된 목적은 하나다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로가 감당할 수 있는 양을 알 수 없으므로 관찰하면서 탐색하고, 혼잡 신호가 보이면 물러난다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Drop만 혼잡 신호일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 Loss-based Congestion Control은 Packet Drop을 중요한 혼잡 신호로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Queue가 가득 찰 때까지 기다려 Packet을 버리는 것만이 방법은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router가 혼잡해지기 시작할 때 Packet을 버리지 않고 IP Header에 혼잡 표시를 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 이를 TCP ACK로 되돌려 줄 수 있다. 이를 &lt;b&gt;Explicit Congestion Notification&lt;/b&gt;(ECN)이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;Router: 혼잡 조짐을 ECN으로 Mark
Receiver: 혼잡 표시를 ACK로 전달
Sender: cwnd와 전송 동작 조정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECN은 양 Endpoint와 경로 장비가 관련 기능을 지원하고 협상해야 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 일부 Congestion Control은 RTT 증가를 Queue가 쌓이는 단서로 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 신호를 얼마나 반영하는지는 알고리즘마다 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Queue가 크면 Packet을 버리지 않아 더 좋을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Queue는 순간적인 Burst를 흡수할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 지나치게 큰 Queue가 계속 가득 차면 Packet Loss는 줄어 보여도 대기 시간이 매우 길어진다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;전송 시간 자체는 짧음
+ Router Queue 대기 수백 ms 또는 수초
= 사용자가 느끼는 큰 지연&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Bufferbloat&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 다운로드 중 작은 SSH 입력이나 음성 통화가 갑자기 느려지는 현상으로 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 네트워크는 Packet을 하나도 버리지 않는 네트워크만을 뜻하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리량, 지연, 공정성 사이의 균형이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Active Queue Management와 ECN은 Queue가 완전히 가득 차기 전에 혼잡 신호를 주어 긴 지연을 줄이는 데 활용될 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 혼잡 제어는 왜 인터넷 전체의 약속일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Sender가 하나의 병목 Link를 함께 쓴다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;병목 용량: 100Mbps

Sender A가 100Mbps를 계속 차지
Sender B도 100Mbps를 계속 시도
Sender C도 100Mbps를 계속 시도&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자가 손실 때마다 더 빠르게 재전송하면 Queue와 Drop이 늘고 유효하게 도착하는 Data는 오히려 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Congestion Control은 자신의 연결 하나만 빠르게 만드는 기능이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼잡 신호에 반응해 여러 Flow가 공유 자원을 지속해서 사용할 수 있게 하는 협력 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 모든 Flow가 언제나 정확히 같은 대역폭을 얻는다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTT, Congestion Control 알고리즘, 연결 수, 병목 구조, Application 동작이 다르면 분배도 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 네트워크의 상태와 무관하게 무제한 전송하는 것을 피한다는 데 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Flow Control과 Congestion Control을 다시 구분하면&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Flow Control&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Congestion Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보호 대상&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Receiver의 Receive Buffer&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Network의 Link와 Router Queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;핵심 값&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;rwnd&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;cwnd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;누가 관리하나&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Receiver가 계산해 Sender에 광고&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Sender TCP가 신호를 보고 추정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;어디에서 보이나&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TCP Header의 Window Field&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보통 Sender Kernel 내부 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;주요 신호&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Application 읽기와 Buffer 여유&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;ACK, Loss, RTO, ECN, RTT 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한계가 작아지면&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;새 Data를 덜 보내거나 Zero Window에서 정지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;In-flight Data를 줄이고 천천히 재탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;해결하지 않는 문제&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;중간 Network의 혼잡&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;느린 Receiver Buffer 보호 자체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 독립적인 질문에 답한다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;rwnd: 받는 쪽에 놓을 자리가 있는가?
cwnd: 가는 길에 이만큼 올려도 되는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 둘 중 하나만 선택하지 않는다. 더 엄격한 한계를 동시에 따른다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유를 실제 Packet과 운영체제 상태로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 이야기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수신 창고&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Receive Buffer&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;도착했지만 Application이 아직 읽지 않은 Byte를 보관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;창고의 남은 공간 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Advertised Window, rwnd&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Receiver가 추가로 받을 수 있다고 광고한 Byte 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;처리될수록 앞으로 움직이는 접수 범위&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Sliding Window&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ACK 진전에 따라 허용되는 Sequence 범위가 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;창고가 가득 차 접수 중지&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Zero Window&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;현재 새 Data를 받을 공간이 없음을 광고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;접수 재개 여부를 다시 묻는 확인&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Persist Timer / Probe&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Window Update 손실로 영구 정지하는 상황 방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;먼 길에 맞는 큰 접수표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Window Scale&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;16비트 Window Field를 더 큰 값으로 해석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도로에 올려도 될 우편물의 추정량&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Congestion Window, cwnd&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Sender가 허용하는 Network 내 미확인 Data 한도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;처음 길을 빠르게 탐색&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Slow Start&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ACK가 돌아오면 cwnd를 빠르게 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;혼잡 근처에서 조심스럽게 탐색&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Congestion Avoidance&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가용 용량을 더 완만하게 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도로가 막혔다는 표시&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Loss / ECN / RTT 증가&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Congestion Control이 반응할 수 있는 Network 신호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;큰 대기 창고 때문에 늦어지는 배송&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Bufferbloat&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;과도한 Queue 체류가 큰 지연을 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 전송 결정은 다음 그림으로 모을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Receiving Application 읽기 속도
              &amp;darr;
       Receive Buffer 여유
              &amp;darr;
        rwnd를 ACK로 광고 ───┐
                           │
Network의 ACK&amp;middot;Loss&amp;middot;ECN&amp;middot;RTT  │
              &amp;darr;            │
       Sender가 cwnd 계산 ───┤
                           ▼
                    min(rwnd, cwnd)
                           &amp;darr;
                 현재 In-flight를 제외
                           &amp;darr;
                   새로 보낼 수 있는 양&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서는 무엇을 볼 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서 다음 Field를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;tcp.ack
tcp.window_size_value
tcp.window_size_scalefactor
tcp.window_size&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;tcp.window_size_value&lt;/code&gt;는 Header에 기록된 원래 16비트 값이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tcp.window_size_scalefactor&lt;/code&gt;는 협상된 Scale 해석에 관련된 값이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tcp.window_size&lt;/code&gt;는 Wireshark가 Scale을 반영해 계산한 Window 크기다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 관련 상황은 다음 Display Filter로 좁혀 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;tcp.analysis.zero_window ||
tcp.analysis.window_full ||
tcp.analysis.window_update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcp.analysis.*&lt;/code&gt;는 TCP Header의 독립 Flag가 아니라 Wireshark가 Capture를 분석해 붙인 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 제한도 있다.&lt;/p&gt;
&lt;pre class=&quot;ldif&quot;&gt;&lt;code&gt;Packet Capture에서 직접 관찰 가능: Receiver가 광고한 rwnd
Packet Capture에서 직접 읽기 어려움: Sender 내부의 cwnd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cwnd&lt;/code&gt;는 Sender Host에서 운영체제 상태를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Linux에서 연결 상태를 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 전송 중인 TCP 연결을 다음처럼 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ss -tin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경과 Kernel 버전에 따라 다음과 같은 정보가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;rtt
cwnd
ssthresh
unacked
bytes_acked
bytes_received
rcv_space
rwnd_limited&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cwnd&lt;/code&gt;는 보통 MSS 단위로 표시될 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;rcv_space&lt;/code&gt; 같은 진단 값이 곧 Capture 시점의 Advertised Window와 완전히 같은 의미라고 단정하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 값의 단위와 의미는 사용 중인 &lt;code&gt;ss&lt;/code&gt;와 Kernel 문서를 함께 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송이 매우 짧으면 상태가 빠르게 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 Data를 전송하는 동안 다음처럼 반복 관찰하면 변화가 더 잘 보인다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;watch -n 0.2 'ss -tin'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 &lt;code&gt;netstat&lt;/code&gt;, &lt;code&gt;lsof&lt;/code&gt;, Packet Capture 등으로 Socket과 Window를 관찰할 수 있지만 Linux &lt;code&gt;ss&lt;/code&gt;와 출력 항목이 같지는 않다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 느린 Receiver로 Flow Control을 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 개인 Computer의 Loopback에서만 실행한다. 특정 외부 저장소나 공용 Server를 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver는 한 번에 1KB만 읽고 잠시 쉰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;slow_receiver.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import socket
import time

with socket.socket() as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096)
    server.bind((&quot;127.0.0.1&quot;, 8080))
    server.listen(1)
    conn, address = server.accept()

    total = 0
    with conn:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            total += len(data)
            time.sleep(0.01)

    print(f&quot;received={total}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 큰 Byte Stream을 가능한 만큼 Socket에 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;fast_sender.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;import socket
import time

payload = b&quot;x&quot; * (1024 * 1024)

with socket.create_connection((&quot;127.0.0.1&quot;, 8080)) as sock:
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
    started = time.monotonic()
    sock.sendall(payload)
    sock.shutdown(socket.SHUT_WR)
    elapsed = time.monotonic() - started

print(f&quot;sendall elapsed={elapsed:.3f}s&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver를 먼저 실행하고 Sender를 시작한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 slow_receiver.py&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 fast_sender.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 Packet을 관찰한다. Interface 이름은 Linux에서 보통 &lt;code&gt;lo&lt;/code&gt;, macOS에서 보통 &lt;code&gt;lo0&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;sudo tcpdump -n -i lo -s 0 -w tcp-flow-control.pcap \
  'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서 다음 질문을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Receiver가 광고하는 계산된 Window는 시간에 따라 어떻게 변하는가?&lt;/li&gt;
&lt;li&gt;Sending Application의 &lt;code&gt;sendall()&lt;/code&gt;이 즉시 끝나지 않는 이유는 무엇인가?&lt;/li&gt;
&lt;li&gt;Zero Window 또는 Window Update가 관찰되는가?&lt;/li&gt;
&lt;li&gt;ACK는 전진하는데 전송 속도는 왜 Receiver의 읽기 속도에 가까워지는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제의 Buffer Auto-tuning, Loopback 최적화, Socket Buffer 계산 방식에 따라 Zero Window가 나타나지 않을 수도 있다. &lt;code&gt;SO_RCVBUF=4096&lt;/code&gt;이 Capture에서 정확히 4096바이트 Window를 뜻하는 것도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 실습의 목표는 특정 숫자를 강제로 재현하는 것이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느린 소비가 Send Buffer와 Advertised Window를 거쳐 Sender에 Backpressure로 전달되는 흐름을 관찰하는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP Flow Control은 Receiver의 Receive Buffer를 보호하고 Congestion Control은 Network의 Link와 Router Queue를 보호한다.&lt;/li&gt;
&lt;li&gt;Receiver는 ACK의 Window Field로 추가 수신 가능한 &lt;code&gt;rwnd&lt;/code&gt;를 광고한다.&lt;/li&gt;
&lt;li&gt;Sliding Window는 ACK가 전진할 때 확인된 범위만큼 새 Data를 보낼 Sequence 범위를 앞으로 이동시킨다.&lt;/li&gt;
&lt;li&gt;Receiver 공간이 없으면 Zero Window를 광고하고, Persist Timer와 Probe는 Window Update 손실로 영구 정지하는 것을 막는다.&lt;/li&gt;
&lt;li&gt;Window Scale은 16비트 Window Field보다 큰 Receive Window를 표현하게 한다.&lt;/li&gt;
&lt;li&gt;BDP가 큰 경로를 채우려면 충분한 Window와 Buffer가 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cwnd&lt;/code&gt;는 Sender가 ACK, 손실, RTO, ECN, RTT 같은 신호를 바탕으로 관리하는 내부 Congestion Control 상태다.&lt;/li&gt;
&lt;li&gt;새 Data의 핵심 전송 한도는 &lt;code&gt;min(rwnd, cwnd)&lt;/code&gt;이며 이미 In-flight인 Byte를 제외해야 한다.&lt;/li&gt;
&lt;li&gt;Slow Start는 모르는 경로의 용량을 빠르게 탐색하고 Congestion Avoidance는 혼잡 근처에서 더 조심스럽게 탐색한다.&lt;/li&gt;
&lt;li&gt;실제 TCP 알고리즘은 Reno, CUBIC, BBR 등에서 증가와 감소 방식이 다를 수 있다.&lt;/li&gt;
&lt;li&gt;큰 Queue는 Loss를 늦출 수 있지만 긴 지연을 만드는 Bufferbloat를 일으킬 수 있다.&lt;/li&gt;
&lt;li&gt;Packet Capture에서는 &lt;code&gt;rwnd&lt;/code&gt;를 볼 수 있지만 &lt;code&gt;cwnd&lt;/code&gt;는 일반적으로 Sender 운영체제의 내부 상태를 함께 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Send Buffer, Receive Buffer, Flow Control, Advertised Window, Receive Window, rwnd, Sliding Window, Zero Window, Window Update, Persist Timer, Window Scale, Bandwidth-Delay Product, Congestion Control, Congestion Window, cwnd, In-flight Data, Slow Start, ssthresh, Congestion Avoidance, ACK Clocking, AIMD, ECN, Bufferbloat&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 보내는 양은 하나의 속도계로 결정되지 않는다. &lt;br /&gt;받는 쪽의 빈자리인 rwnd와 길이 감당할 것으로 추정한 양인 cwnd 중 더 작은 한계를 따라야 대화와 길이 함께 살아남는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Receiver와 Network가 감당할 수 있는 전송량을 조절한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 전송량을 적절히 줄였어도 Packet 하나의 크기가 다음 Link가 운반할 수 있는 한계보다 크면 그대로 지나갈 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 소포는 누가, 어디에서 나누어야 할까? 그리고 일부 조각만 사라지면 원래 Packet 전체에는 어떤 일이 생길까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;「&lt;b&gt;작은 편지는 가는데 큰 소포만 멈추는 이유는 무엇일까?&lt;/b&gt;」라는 질문을 통해&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; MTU, IP Fragmentation, DF Flag, Fragment Offset과 재조립 비용을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Advertised Window</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>Receive Buffer</category>
      <category>Send Buffer</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1095</guid>
      <comments>https://peterica.tistory.com/1095#entry1095comment</comments>
      <pubDate>Mon, 20 Jul 2026 20:18:37 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 17. 편지가 사라졌다는 사실은 어떻게 알아챌까?</title>
      <link>https://peterica.tistory.com/1094</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGJv2X/dJMcahSEMNX/WaM9v9PHQrgx72bWkmECSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGJv2X/dJMcahSEMNX/WaM9v9PHQrgx72bWkmECSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGJv2X/dJMcahSEMNX/WaM9v9PHQrgx72bWkmECSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGJv2X%2FdJMcahSEMNX%2FWaM9v9PHQrgx72bWkmECSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 5. 믿을 수 없는 길 위에서 믿을 수 있게 보내기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결이 만들어졌다고 해서 길이 안전해지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP는 여전히 Best-Effort 방식으로 Packet을 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Router의 Queue가 가득 차거나, 전송 중 Bit가 손상되거나, 경로가 순간적으로 바뀌면 TCP Segment가 사라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 네트워크에는 분실 신고서가 항상 도착하는 것도 아니다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;Sender가 알 수 있는 것
├─ 어떤 Byte를 보냈는가
├─ Receiver가 다음에 어떤 Byte를 기다리는가
└─ 응답이 언제, 어떤 순서로 돌아왔는가

Sender가 직접 볼 수 없는 것
├─ 어느 Router에서 Segment가 버려졌는가
├─ Data Segment가 사라졌는가
└─ ACK가 돌아오는 길에서 사라졌는가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 &amp;ldquo;이 Segment가 몇 번째 Router에서 분실됐다&amp;rdquo;는 확정 통보를 기다리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 &lt;b&gt;Sequence Number&lt;/b&gt;, &lt;b&gt;Acknowledgment Number&lt;/b&gt;, &lt;b&gt;Timer&lt;/b&gt;를 단서로 손실을 추론하고 필요한 Byte를 다시 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 실제 Byte 번호를 따라가며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; Cumulative ACK&lt;/b&gt;, &lt;b&gt;Retransmission Timeout(RTO)&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;Duplicate ACK&lt;/b&gt;, &lt;b&gt;Fast Retransmit&lt;/b&gt;, &lt;b&gt;Selective Acknowledgment&lt;/b&gt;(SACK)가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 서로 다른 방식으로 손실을 발견하고 복구하는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP는 편지 봉투가 아니라 바이트 장부를 본다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 TCP Socket에 3,000바이트를 썼다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 이를 여러 Segment로 나누어 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 설명을 위해 1,000바이트씩 나뉘었다고 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake에서 SYN이 Sequence Number 하나를 소비했고, 첫 Data Byte의 번호가 1001이라고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Segment A: Seq=1001, Length=1000  &amp;rarr; Byte [1001, 2001)
Segment B: Seq=2001, Length=1000  &amp;rarr; Byte [2001, 3001)
Segment C: Seq=3001, Length=1000  &amp;rarr; Byte [3001, 4001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;[1001, 2001)&lt;/code&gt;은 1001부터 2000까지, 모두 1,000바이트라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 Sequence Number는 Segment의 순번이 아니라 &lt;b&gt;Byte Stream에서 첫 Data Byte가 놓인 위치&lt;/b&gt;를 가리킨다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Seq=2001, Length=1000

의미:
이 Segment에는 2001번부터 3000번까지의 Byte가 들어 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Segment 크기가 달라져도 Receiver는 각 Byte가 Stream의 어디에 들어갈지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유에서 TCP는 소포에 1번, 2번, 3번을 붙이는 것보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 소포 겉면에 &amp;ldquo;문서 1001쪽부터 2000쪽까지&amp;rdquo;라고 적어 보내는 방식에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ACK 번호는 무엇을 확인할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 Segment A를 정상적으로 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 보내는 ACK는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ACK=2001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 번호는 &amp;ldquo;2001번 Byte를 받았다&amp;rdquo;는 뜻이 아니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ACK=2001

의미:
2000번까지 연속해서 받았고
다음에는 2001번 Byte를 기다린다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Acknowledgment Number는 &lt;b&gt;다음에 기대하는 Byte 번호&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Segment B까지 연속으로 받으면 ACK는 3001이 된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Segment A 수신 &amp;rarr; ACK=2001
Segment B 수신 &amp;rarr; ACK=3001
Segment C 수신 &amp;rarr; ACK=4001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP ACK는 일반적으로 지금까지 끊김 없이 받은 범위를 한 번에 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Cumulative Acknowledgment&lt;/b&gt;, 누적 확인 응답이라고 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ACK 하나가 Segment 하나의 영수증은 아니다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK=4001이 도착했다면 Sender는 다음 사실을 알 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Byte 1001부터 4000까지 연속 수신 완료
Receiver가 다음에 기다리는 Byte는 4001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간의 ACK=2001과 ACK=3001이 돌아오는 길에서 사라졌더라도 마지막 ACK=4001이 도착하면 앞선 범위까지 함께 확인된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender                                      Receiver
  Seq=1001, Len=1000 ----------------------&amp;gt;
                            &amp;lt;---- ACK=2001  (손실)
  Seq=2001, Len=1000 ----------------------&amp;gt;
                            &amp;lt;---- ACK=3001  (손실)
  Seq=3001, Len=1000 ----------------------&amp;gt;
                            &amp;lt;---- ACK=4001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 ACK=4001을 보고 세 Segment의 Byte가 모두 도착했음을 안다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 ACK 하나가 보이지 않는다고 곧바로 Data를 재전송하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤의 누적 ACK가 더 넓은 범위를 확인할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 모든 Segment에 독립적인 영수증을 붙이는 비용을 줄인다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 가운데 편지만 사라지면 ACK는 어디에서 멈출까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Segment B가 중간에서 사라졌다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Segment A: Byte [1001, 2001)  &amp;rarr; 도착
Segment B: Byte [2001, 3001)  &amp;rarr; 손실
Segment C: Byte [3001, 4001)  &amp;rarr; 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver는 A를 받고 ACK=2001을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 C가 도착했지만 2001부터 시작하는 B가 비어 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;받은 범위: [1001, 2001), [3001, 4001)
빈 범위  : [2001, 3001)
다음 기대: 2001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Receiver는 ACK 번호를 4001로 올릴 수 없다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender                                      Receiver
  Seq=1001, Len=1000 ----------------------&amp;gt;
                            &amp;lt;---- ACK=2001
  Seq=2001, Len=1000 --------X  손실
  Seq=3001, Len=1000 ----------------------&amp;gt;
                            &amp;lt;---- ACK=2001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 ACK=2001은 &amp;ldquo;A를 또 받았다&amp;rdquo;는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤의 Data가 도착했지만 여전히 2001번 Byte부터 기다리고 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Acknowledgment Number가 반복되므로 이를 &lt;b&gt;Duplicate ACK&lt;/b&gt;, 중복 ACK라고 부른다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 중복 ACK는 왜 손실의 단서가 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 Segment를 더 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Segment C: Seq=3001
Segment D: Seq=4001
Segment E: Seq=5001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B가 빠진 상태에서 C, D, E가 차례로 도착할 때마다 Receiver는 여전히 ACK=2001을 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;최초 ACK       : ACK=2001
C 도착 후      : ACK=2001  &amp;rarr; Duplicate ACK 1
D 도착 후      : ACK=2001  &amp;rarr; Duplicate ACK 2
E 도착 후      : ACK=2001  &amp;rarr; Duplicate ACK 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &amp;ldquo;3개의 중복 ACK&amp;rdquo;는 최초 ACK를 제외하고 같은 ACK가 세 번 더 도착했다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender 입장에서 이 패턴은 중요한 단서다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;뒤의 Segment들은 Receiver까지 계속 도착하고 있다.
그런데 ACK가 2001에서 전진하지 않는다.
그러면 2001부터 시작하는 구간에 빈틈이 있을 가능성이 높다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 TCP 동작에서는 보통 &lt;b&gt;3개의 Duplicate ACK&lt;/b&gt;를 받으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTO가 만료되기 전에 빠진 Segment를 재전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Fast Retransmit&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender                                      Receiver
  Seq=2001, Len=1000 재전송 ----------------&amp;gt;
                            &amp;lt;---- ACK=6001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver가 C, D, E를 Out-of-Order Buffer에 보관하고 있었다면 B가 채워지는 순간 Byte 6000까지 연속 범위가 완성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK는 누적되어 6001로 한 번에 전진할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 중복 ACK가 곧 손실의 확정 증거일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 그렇지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서 Packet 순서가 바뀔 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;dns&quot;&gt;&lt;code&gt;보낸 순서: A &amp;rarr; B &amp;rarr; C
도착 순서: A &amp;rarr; C &amp;rarr; B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C가 먼저 도착하면 Receiver는 B를 기다리며 Duplicate ACK를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 뒤 B가 도착하면 빈틈이 채워지고 ACK가 전진한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 소수의 Duplicate ACK는 다음과 같은 이유로도 생길 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로 변화에 따른 Packet Reordering&lt;/li&gt;
&lt;li&gt;Segment 복제&lt;/li&gt;
&lt;li&gt;Receiver의 ACK 생성 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 첫 번째 Duplicate ACK만 보고 즉시 재전송하면 단순한 순서 변경에도 불필요한 복사본을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Duplicate ACK를 기다리는 기준은 빠른 복구와 불필요한 재전송 사이의 절충이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영체제의 최신 TCP 구현은 SACK, Reordering 관찰, 시간 기반 손실 탐지 등 추가 정보를 함께 사용할 수 있다. &amp;ldquo;항상 정확히 세 번만 세는 단순한 기계&amp;rdquo;로 이해하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 뒤따르는 편지가 없다면 무엇을 기다려야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fast Retransmit에는 빠진 Segment 뒤로 도착하는 Data가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 Segment 하나만 보냈는데 그것이 사라졌다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender &amp;rarr; Receiver : Seq=2001, Len=1000  X

뒤따르는 Data 없음
Duplicate ACK도 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver는 받지 못한 Segment에 대해 ACK를 만들 수 없다. Sender에게는 아무 응답도 오지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 필요한 두 번째 단서가 &lt;b&gt;시간&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender는 확인되지 않은 가장 오래된 Data에 &lt;b&gt;Retransmission Timer&lt;/b&gt;를 둔다. 일정 시간이 지나도록 ACK가 전진하지 않으면 해당 Data를 다시 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 만료 시간을 &lt;b&gt;Retransmission Timeout&lt;/b&gt;(RTO)이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Data 전송
   &amp;darr;
ACK 기다림
   &amp;darr;
RTO 만료
   &amp;darr;
확인되지 않은 Data 재전송&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTO 기반 재전송은 뒤따르는 Data가 없어 Duplicate ACK를 만들 수 없는 상황에서도 동작한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Timeout은 왜 고정된 숫자일 수 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서울 안의 짧은 경로와 대륙을 건너는 경로의 왕복 시간은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTO를 항상 너무 짧게 잡으면 ACK가 정상적으로 오고 있는데도 재전송한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;실제 RTT 200ms
고정 RTO 100ms

100ms: Timeout, 불필요한 재전송
200ms: 원래 ACK 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 RTO가 지나치게 길면 실제 손실을 복구하기까지 오래 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 보낸 시점과 ACK가 돌아온 시점의 차이로 &lt;b&gt;Round-Trip Time(RTT)&lt;/b&gt; Sample을 얻고, 경로의 평균적인 지연과 변동 폭을 추정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 개념은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;SRTT   : 평활화한 RTT 추정값
RTTVAR : RTT 변동 폭의 추정값
RTO    : SRTT와 RTTVAR를 바탕으로 정한 재전송 대기 시간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 하한과 Timer 정밀도는 운영체제 구현에 따라 달라질 수 있다. 중요한 원리는 RTO가 단순한 고정 상수가 아니라 &lt;b&gt;관찰한 왕복 지연과 그 흔들림을 반영한다&lt;/b&gt;는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 재전송했는데도 ACK가 오지 않으면 어떻게 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼잡한 길에서 같은 간격으로 계속 재전송하면 이미 막힌 Queue를 더 채울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTO가 반복해서 만료될 때 TCP는 일반적으로 대기 시간을 늘린다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;첫 번째 Timeout  &amp;rarr; 재전송, 다음 대기 증가
두 번째 Timeout  &amp;rarr; 재전송, 다음 대기 더 증가
세 번째 Timeout  &amp;rarr; 재전송, 다음 대기 더 증가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Exponential Backoff&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 혼잡할 가능성이 큰 상황에서 무작정 같은 속도로 재촉하지 않고 한 걸음 물러나는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timeout은 단순한 분실 복구 신호에 그치지 않는다. TCP는 이를 네트워크가 전송량을 감당하지 못한다는 강한 혼잡 신호로도 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송 속도를 어떻게 줄이는지는 다음 글의 Congestion Control에서 이어서 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 재전송한 Segment의 RTT는 왜 계산하기 어려울까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Byte 범위를 두 번 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;t0: 원본 Segment 전송
t1: RTO 만료, 같은 범위 재전송
t2: ACK 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t2에 도착한 ACK가 원본에 대한 늦은 응답인지, 재전송본에 대한 빠른 응답인지 ACK 번호만으로는 구분하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Retransmission Ambiguity&lt;/b&gt;, 재전송 모호성이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모호한 Sample로 RTT를 계산하면 RTO 추정이 흔들릴 수 있다. 전통적으로 TCP는 재전송된 Segment의 모호한 RTT Sample을 피하는 &lt;b&gt;Karn's Algorithm&lt;/b&gt;과 Timeout 시 Backoff를 사용해 이 문제를 완화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Timestamp Option이 협상된 연결에서는 Timestamp 정보가 RTT 측정에 추가 단서를 제공할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SACK은 누적 ACK의 빈틈을 어떻게 보완할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누적 ACK만 보면 Receiver가 ACK 번호 뒤의 어떤 구간을 이미 보관하고 있는지 자세히 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 상태를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;연속 수신: [1001, 2001)
손실 구간: [2001, 3001)
추가 수신: [3001, 5001)

Cumulative ACK: 2001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK=2001은 연속 범위의 끝을 정확히 알려 주지만, [3001, 5001)도 도착했다는 사실은 표현하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake에서 양쪽이 &lt;b&gt;SACK Permitted&lt;/b&gt; Option으로 지원 여부를 알렸다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Receiver는 ACK의 SACK Option에 불연속적으로 받은 Byte Block을 표시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ACK=2001
SACK Block=[3001, 5001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;다음에 기다리는 연속 Byte는 2001이다.
하지만 3001부터 5000까지는 이미 따로 보관하고 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SACK은 Cumulative ACK를 대체하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ACK Number는 연속해서 받은 범위의 끝을 나타낸다.&lt;/li&gt;
&lt;li&gt;SACK Block은 그 뒤에 따로 도착한 범위를 추가로 알려 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 구간이 손실되었을 때 Sender는 SACK 정보를 이용해 이미 도착한 범위를 피하고 실제 빈 구간을 더 효율적으로 재전송할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Receiver는 순서가 바뀐 Data를 Application에 바로 줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 Application에 약속한 것은 순서 있는 Byte Stream이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B가 빠지고 C가 먼저 도착했을 때 Receiver TCP는 C를 Buffer에 보관할 수 있지만, 일반적으로 B보다 앞서 C의 Byte를 Application Stream으로 넘기지 않는다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;네트워크 도착: A &amp;rarr; C &amp;rarr; B
TCP 재조립   : A &amp;rarr; B &amp;rarr; C
Application  : A &amp;rarr; B &amp;rarr; C&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 구간이 채워질 때까지 뒤의 Data 전달이 기다리는 현상은 TCP의 &lt;b&gt;Head-of-Line Blocking&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재전송은 단순히 사라진 봉투를 한 번 더 보내는 기능이 아니다. Receiver가 순서 있는 Stream을 다시 완성할 수 있도록 Byte의 빈칸을 채우는 과정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 같은 Byte가 두 번 도착하면 Application도 두 번 받을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 Segment가 늦게 도착한 상태에서 재전송본까지 도착할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;원본 Seq=2001   &amp;rarr; 늦게 도착
재전송 Seq=2001 &amp;rarr; 함께 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Sequence Number를 이용해 이미 받은 Byte 범위를 식별하고 중복 Data를 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 TCP Byte Stream에서는 같은 Byte를 Application에 두 번 전달하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기서 범위를 정확히 구분해야 한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;TCP가 제공하는 것
└─ 연결 안에서 순서 있는 Byte Stream과 중복 Byte 억제

TCP가 보장하지 않는 것
├─ 상대 Application이 업무 처리를 끝냈는가
├─ Database Transaction이 한 번만 Commit되었는가
└─ 연결이 끊긴 뒤 재요청까지 포함한 Exactly-Once 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP ACK는 Receiver의 TCP Stack이 해당 Byte를 받았다는 뜻이지, 주문이나 결제가 한 번만 처리되었다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 수준의 재시도와 중복 방지는 별도의 요청 ID나 Idempotency 설계가 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ACK가 늦는 것과 손실된 것은 어떻게 구분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 기다리는 동안에는 확실히 구분할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data가 손실됐을 수도 있고, ACK가 손실됐을 수도 있으며, 단지 Queue에서 오래 기다리는 중일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receiver는 ACK Traffic을 줄이기 위해 상황에 따라 짧게 기다렸다가 여러 Segment를 누적 확인하는 &lt;b&gt;Delayed ACK&lt;/b&gt;를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 식은 성립하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;ACK가 즉시 안 보임 = Data 손실 확정  (X)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 관찰 가능한 신호를 조합한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;같은 ACK가 반복됨
&amp;rarr; 뒤의 Data는 도착했지만 앞에 빈칸이 있을 가능성

RTO 동안 ACK가 전진하지 않음
&amp;rarr; 확인되지 않은 Data 또는 그 ACK가 손실&amp;middot;지연됐을 가능성

SACK Block이 보임
&amp;rarr; 누적 ACK 뒤에 실제로 도착한 불연속 범위 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실 복구는 네트워크 내부를 모두 보고 내리는 판결이 아니라 End Host가 제한된 단서로 내리는 합리적인 추론이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Checksum 오류도 Sender에게는 손실처럼 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Segment가 전송 중 손상되어 Checksum 검증에 실패하면 Receiver는 해당 Segment를 정상 Data로 받아들이지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender에게 &amp;ldquo;Checksum 오류가 났다&amp;rdquo;는 TCP ACK가 반드시 돌아오는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 Sender 관점에서는 다음 상황이 비슷하게 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Router Queue에서 Drop
Link에서 손상되어 폐기
Data는 도착했지만 ACK가 Drop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 ACK의 정체나 Timeout으로 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 End-to-End 복구가 특정 Link의 오류 원인을 직접 고치는 것은 아니다. 원인을 알 수 없어도 Stream의 빈 Byte 범위를 다시 보내 대화를 이어가게 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 손실 복구는 혼자 동작하지 않는다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sender가 확인되지 않은 Data를 무제한으로 쏟아낼 수 있다면 Duplicate ACK와 재전송만으로는 길을 안정시킬 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 실제로 보낼 수 있는 양에는 두 종류의 창이 관여한다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;Receiver가 받을 수 있는 양 &amp;rarr; Receive Window, rwnd
Network가 감당할 것으로 추정한 양 &amp;rarr; Congestion Window, cwnd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실은 재전송할 Byte를 알려 주는 동시에 Congestion Control이 전송량을 줄이는 신호가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &amp;ldquo;무엇을 다시 보낼 것인가&amp;rdquo;에 집중했다. &amp;ldquo;한 번에 얼마나 보내도 되는가&amp;rdquo;는 다음 글에서 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유는 흐름을 이해하는 출발점일 뿐이다. 실제 Packet에서 확인할 용어로 다시 연결해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 209px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우편 이야기&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;실제 용어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;정확한 의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;문서의 쪽 번호&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Sequence Number&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Segment에 담긴 첫 Data Byte의 Stream 위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;다음에는 2001쪽을 보내 달라는 회신&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ACK=2001&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;2000번까지 연속 수신했고 2001번을 기대함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;지금까지 받은 쪽을 한 번에 확인&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Cumulative ACK&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;연속 수신한 전체 Byte 범위를 누적 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 빈 쪽 번호를 반복해서 요청&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Duplicate ACK&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;뒤 Data가 왔지만 다음 기대 Byte가 바뀌지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;세 차례 반복 요청 후 바로 재발송&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Fast Retransmit&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;RTO 전에 손실 가능성이 큰 Segment를 재전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;영수증을 기다리는 제한 시간&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;RTO&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;ACK 진전이 없을 때 재전송을 시작하는 Timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;재촉할수록 기다리는 간격을 늘림&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Exponential Backoff&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;반복 Timeout 때 재전송 대기 시간을 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;따로 도착해 보관 중인 쪽 목록&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;SACK Block&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;누적 ACK 뒤에서 수신한 불연속 Byte 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;순서가 맞을 때까지 보관하는 선반&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Out-of-Order Buffer&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;빈 구간 뒤에 도착한 Data를 재조립용으로 보관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;빠진 쪽 때문에 뒤 문서를 못 읽음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Head-of-Line Blocking&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;앞 Byte가 복구될 때까지 뒤 Byte 전달이 지연&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 흐름을 한 장면으로 모으면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Sender                                             Receiver
Seq=1001, Len=1000 -------------------------------&amp;gt;
                                      &amp;lt;----- ACK=2001

Seq=2001, Len=1000 -----------X  손실
Seq=3001, Len=1000 -------------------------------&amp;gt; 보관
                                      &amp;lt;----- ACK=2001, SACK=[3001,4001)
Seq=4001, Len=1000 -------------------------------&amp;gt; 보관
                                      &amp;lt;----- ACK=2001, SACK=[3001,5001)
Seq=5001, Len=1000 -------------------------------&amp;gt; 보관
                                      &amp;lt;----- ACK=2001, SACK=[3001,6001)

같은 ACK가 세 번 추가로 도착
&amp;rarr; Fast Retransmit

Seq=2001, Len=1000 재전송 ------------------------&amp;gt;
                                      &amp;lt;----- ACK=6001&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Packet Capture에서 직접 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 연결에서는 Capture 도구가 Sequence Number를 읽기 쉬운 상대 번호로 표시할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark의 기본 설정에서 첫 Data Byte가 &lt;code&gt;Seq=1&lt;/code&gt;처럼 보이더라도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 실제 Header의 ISN이 0이나 1이라는 뜻은 아닐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요하면 TCP Protocol 설정의 Relative Sequence Number 표시 여부를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture에서 다음 필드를 함께 본다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;tcp.seq       : Segment의 첫 Byte 위치
tcp.len       : TCP Payload 길이
tcp.ack       : 다음에 기대하는 Byte 위치
tcp.options.sack : 선택 확인된 Byte Block&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark가 분석한 재전송 후보는 다음 Display Filter로 좁혀 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;tcp.analysis.retransmission ||
tcp.analysis.fast_retransmission ||
tcp.analysis.duplicate_ack ||
tcp.options.sack&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tcp.analysis.*&lt;/code&gt;는 Packet Header에 적힌 Flag가 아니라&amp;nbsp;Wireshark가 Capture 순서와 시간을 바탕으로 계산한 분석 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture 시작 시점이 늦거나 Packet이 누락되면 판단도 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command Line에서는 다음처럼 TCP Packet을 저장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Interface 이름은 Linux에서 보통 &lt;code&gt;lo&lt;/code&gt;, macOS에서 보통 &lt;code&gt;lo0&lt;/code&gt;이며 실제 환경에 맞게 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;sudo tcpdump -n -i lo -s 0 -w tcp-loss.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적인 Loopback 통신은 손실이 거의 없어 재전송이 보이지 않는 것이 자연스럽다. 재전송을 만들기 위해 공용 서비스에 부하나 고의적인 손실을 주면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 격리된 Linux 환경에서 손실을 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 실습은 &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;tc&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;nc&lt;/code&gt;가 있는 개인 Linux VM에서만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Network Namespace와 가상 Link를 만들기 때문에 일반 Network Interface에는 손실을 적용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 Client와 Server를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns add tcp-client
sudo ip netns add tcp-server
sudo ip link add veth-c type veth peer name veth-s
sudo ip link set veth-c netns tcp-client
sudo ip link set veth-s netns tcp-server

sudo ip -n tcp-client addr add 10.17.0.1/24 dev veth-c
sudo ip -n tcp-server addr add 10.17.0.2/24 dev veth-s
sudo ip -n tcp-client link set lo up
sudo ip -n tcp-server link set lo up
sudo ip -n tcp-client link set veth-c up
sudo ip -n tcp-server link set veth-s up&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 보내는 Packet의 일부만 가상 Link에서 임의로 버린다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec tcp-client \
  tc qdisc add dev veth-c root netem loss 10%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Terminal에서 수신을 시작한다. &lt;code&gt;nc&lt;/code&gt; Option은 구현마다 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec tcp-server nc -l 8080 &amp;gt; /dev/null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Capture Terminal에서 Packet을 저장한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns exec tcp-client \
  tcpdump -n -i veth-c -s 0 -w /tmp/tcp-loss.pcap 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client Terminal에서 충분한 Data를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;dd if=/dev/zero bs=1M count=10 | \
  sudo ip netns exec tcp-client nc 10.17.0.2 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송과 Capture를 종료한 뒤 &lt;code&gt;/tmp/tcp-loss.pcap&lt;/code&gt;을 Wireshark에서 열어 다음 질문을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재전송 Segment의 &lt;code&gt;Seq&lt;/code&gt;와 &lt;code&gt;Len&lt;/code&gt;은 어느 Byte 범위인가?&lt;/li&gt;
&lt;li&gt;Duplicate ACK의 &lt;code&gt;Ack&lt;/code&gt; 값은 어디에서 멈췄는가?&lt;/li&gt;
&lt;li&gt;SACK Block은 누적 ACK 뒤의 어떤 범위를 가리키는가?&lt;/li&gt;
&lt;li&gt;Fast Retransmission과 RTO 기반 Retransmission은 시간 간격이 어떻게 다른가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 마치면 Namespace를 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace 삭제와 함께 내부 가상 Interface와 &lt;code&gt;netem&lt;/code&gt; 설정도 제거된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo ip netns del tcp-client
sudo ip netns del tcp-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의 손실이므로 한 번의 Capture에 모든 현상이 나타난다고 보장할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실률을 지나치게 높이면 Handshake 자체가 지연되고 흐름을 읽기 어려워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP Sequence Number는 Segment 번호가 아니라 Byte Stream에서 첫 Data Byte의 위치다.&lt;/li&gt;
&lt;li&gt;ACK 번호는 마지막으로 받은 Byte가 아니라 다음에 기대하는 Byte 번호다.&lt;/li&gt;
&lt;li&gt;Cumulative ACK는 끊김 없이 받은 전체 범위를 한 번에 확인한다.&lt;/li&gt;
&lt;li&gt;누적 ACK 뒤의 Data가 먼저 도착하면 같은 ACK 번호가 반복되는 Duplicate ACK가 생길 수 있다.&lt;/li&gt;
&lt;li&gt;전통적으로 3개의 Duplicate ACK는 RTO 전에 빠진 Segment를 보내는 Fast Retransmit의 단서가 된다.&lt;/li&gt;
&lt;li&gt;Packet Reordering도 Duplicate ACK를 만들 수 있으므로 하나의 중복 ACK가 곧 손실 확정은 아니다.&lt;/li&gt;
&lt;li&gt;뒤따르는 Data가 없으면 Duplicate ACK가 생기지 않으므로 RTO가 마지막 복구 수단이 된다.&lt;/li&gt;
&lt;li&gt;RTO는 관찰한 RTT와 변동 폭을 반영하며 반복 Timeout에서는 Exponential Backoff가 적용된다.&lt;/li&gt;
&lt;li&gt;SACK은 Cumulative ACK 뒤에 이미 받은 불연속 Byte Block을 알려 효율적인 재전송을 돕는다.&lt;/li&gt;
&lt;li&gt;Receiver TCP는 중복 Byte를 제거하고 빈 구간을 채운 뒤 순서 있는 Stream을 Application에 제공한다.&lt;/li&gt;
&lt;li&gt;TCP ACK는 상대 TCP Stack의 수신 확인이며 Application 업무의 Exactly-Once 처리를 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;Sender는 손실 지점을 직접 보는 것이 아니라 ACK의 진행, 반복, 시간이라는 End-to-End 단서로 손실을 추론한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Sequence Number, Acknowledgment Number, Byte Stream, Cumulative ACK, Duplicate ACK, Fast Retransmit, Retransmission Timer, Retransmission Timeout, RTT, SRTT, RTTVAR, Exponential Backoff, Retransmission Ambiguity, Karn's Algorithm, SACK, Out-of-Order, Head-of-Line Blocking&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 사라진 Packet을 직접 목격하지 않는다. 다음에 기다리는 Byte 번호가 멈추고 같은 ACK가 반복되거나 시간이 지나도 ACK가 전진하지 않는 모습을 보고 빈칸을 추론한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실된 Byte를 찾아 다시 보내는 방법은 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Sender가 Receiver의 처리 속도보다 빠르게 보내면 Receiver Buffer가 가득 찬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Receiver가 충분히 빨라도 중간 Network가 감당하지 못하면 Router Queue가 넘친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;받는 사람의 한계와 길 전체의 한계는 어떻게 구분해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;받는 사람이 감당하지 못할 만큼 보내면 어떻게 될까?&lt;/b&gt;」라는 질문을 통해 Receive Window, Sliding Window, Window Scaling, Congestion Window와 Flow Control&amp;middot;Congestion Control의 차이를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>acknowledgment number</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>sequence number</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1094</guid>
      <comments>https://peterica.tistory.com/1094#entry1094comment</comments>
      <pubDate>Mon, 20 Jul 2026 20:17:10 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 16. 대화를 시작하기 전에 왜 세 번이나 인사할까?</title>
      <link>https://peterica.tistory.com/1093</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/THJ7B/dJMcajpmv7N/kcao1r43pT1QAfoO8baLwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/THJ7B/dJMcajpmv7N/kcao1r43pT1QAfoO8baLwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/THJ7B/dJMcajpmv7N/kcao1r43pT1QAfoO8baLwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTHJ7B%2FdJMcajpmv7N%2Fkcao1r43pT1QAfoO8baLwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 5. 믿을 수 없는 길 위에서 믿을 수 있게 보내기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Application에 순서 있는 신뢰 가능한 Byte Stream을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러려면 양쪽이 각 Byte의 위치를 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 보낸 첫 Byte와 Server가 보낸 첫 Byte는 서로 다른 Sequence Space에서 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 보내기 전에 다음 질문에 답해야 한다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;상대가 해당 IP와 Port에서 연결을 받을 준비가 되었는가?
Client가 사용할 초기 Sequence Number는 무엇인가?
Server가 사용할 초기 Sequence Number는 무엇인가?
양쪽 방향의 패킷과 응답이 도달할 수 있는가?
어떤 TCP Option을 사용할 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 &lt;b&gt;3-Way Handshake&lt;/b&gt;로 이 정보를 교환한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Server &amp;rarr; Client : SYN-ACK
Client &amp;rarr; Server : ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 줄만 외우면 동작의 모양은 기억할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 왜 세 번째 ACK가 필요한지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN이 Sequence Number 하나를 소비한다는 것이 무엇인지 설명하기는 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Initial Sequence Number(ISN)&lt;/b&gt;, &lt;b&gt;SYN&lt;/b&gt;, &lt;b&gt;SYN-ACK&lt;/b&gt;, &lt;b&gt;ACK&lt;/b&gt;, &lt;b&gt;TCP State&lt;/b&gt;, &lt;b&gt;MSS&lt;/b&gt;, &lt;b&gt;Window Scale&lt;/b&gt;, &lt;b&gt;SACK Permitted&lt;/b&gt;를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 숫자로 추적하고 Handshake 실패가 Timeout과 Refused로 다르게 보이는 이유를 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 전화 연결보다 두 개의 등기 장부에 가깝다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결을 전화 통화에 비유하면 서로 연결한 뒤 대화한다는 점은 이해하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 TCP는 양쪽 방향의 Byte를 각각 번호로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 보내는 Byte의 번호와 Server가 보내는 Byte의 번호는 별도다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server Sequence Space: Client ISN에서 시작
Server &amp;rarr; Client Sequence Space: Server ISN에서 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 사람이 각자 발송 장부를 하나씩 갖고 있다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 &amp;ldquo;나는 번호 1000부터 보내겠다&amp;rdquo;고 알리고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 &amp;ldquo;나는 번호 5000부터 보내겠다&amp;rdquo;고 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대는 다음에 받을 번호를 ACK로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Client의 장부 시작 번호를 Server가 확인
Server의 장부 시작 번호를 Client가 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 방향의 번호만 맞춰서는 양방향 Byte Stream을 만들 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3-Way Handshake는 연결 가능성 확인과 함께 &lt;b&gt;서로 독립적인 두 Sequence Space의 시작점을 동기화하는 과정&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Handshake 전 양쪽은 어떤 상태일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Application은 Local Address와 Port에 Bind하고 Listen한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Server: 203.0.113.20:443
TCP State: LISTEN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 연결 전 Socket에서 Server Endpoint로 Active Open을 시도한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client: 192.0.2.10:52000
Server: 203.0.113.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결을 구분하는 4-Tuple은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source IP       192.0.2.10
Source Port     52000
Destination IP  203.0.113.20
Destination Port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 &lt;code&gt;connect()&lt;/code&gt;를 호출하면 운영체제의 TCP Stack이 Handshake를 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 SYN Packet을 직접 만드는 것이 아니다. 일반적인 Socket API에서는 Kernel TCP Stack이 상태와 재전송을 관리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 첫 번째 인사 &amp;mdash; SYN&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 자신의 &lt;b&gt;Initial Sequence Number&lt;/b&gt;를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명을 위해 Client ISN을 1000이라고 가정하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server
Flags: SYN
Sequence Number: 1000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 TCP State는 일반적으로 다음처럼 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;CLOSED &amp;rarr; SYN-SENT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN은 다음 의미를 전달한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 Endpoint와 TCP 연결을 시작하고 싶다.
내 Sequence Space의 시작점은 1000이다.
내가 지원하거나 제안하는 TCP Option은 이것이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN Segment에 Application Data가 없어도 SYN Flag 자체는 Sequence Number 하나를 소비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Server가 정상적으로 SYN을 받았다면 다음에 기대하는 번호는 1001이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client SYN seq 1000
&amp;rarr; Server가 기대하는 다음 번호 1001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN과 FIN이 Sequence Space에서 하나를 소비하기 때문에 연결 제어도 ACK로 명확하게 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 번째 인사 &amp;mdash; SYN-ACK&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 Port 443에 Listening Socket이 있고 SYN을 받아들일 수 있다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server도 자신의 ISN을 선택한다. 설명을 위해 5000이라고 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server가 보내는 Segment는 두 역할을 함께 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Server &amp;rarr; Client
Flags: SYN, ACK
Sequence Number: 5000
Acknowledgment Number: 1001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ ACK 1001의 의미&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client의 SYN seq 1000을 받았다.
다음에는 seq 1001부터 보내 달라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ SYN seq 5000의 의미&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나도 연결할 준비가 되었다.
내 Sequence Space의 시작점은 5000이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 연결 정보를 만들고 일반적으로 다음 상태로 이동한다.&lt;/p&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;LISTEN &amp;rarr; SYN-RECEIVED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 Server 입장에서 Handshake가 완전히 끝난 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Server의 SYN-ACK을 실제로 받았는지 확인하지 못했기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 세 번째 인사 &amp;mdash; ACK&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 SYN-ACK을 받으면 다음 사실을 알 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음 보낸 SYN이 Server에 도착했다.&lt;/li&gt;
&lt;li&gt;Server의 응답이 Client로 돌아오는 경로도 동작했다.&lt;/li&gt;
&lt;li&gt;Server가 Port에서 연결을 받을 준비가 있다.&lt;/li&gt;
&lt;li&gt;Server의 ISN은 5000이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Server의 SYN을 확인하는 ACK를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server
Flags: ACK
Sequence Number: 1001
Acknowledgment Number: 5001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK 5001의 의미는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Server의 SYN seq 5000을 받았다.
다음에는 seq 5001부터 보내 달라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 SYN-ACK을 받고 ACK를 보낸 시점에 일반적으로 &lt;code&gt;ESTABLISHED&lt;/code&gt; 상태가 된다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Client: SYN-SENT &amp;rarr; ESTABLISHED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server가 마지막 ACK를 받으면 자신의 SYN이 Client에 도착했음을 확인하고 &lt;code&gt;ESTABLISHED&lt;/code&gt; 상태가 된다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Server: SYN-RECEIVED &amp;rarr; ESTABLISHED&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 양쪽은 서로의 Sequence Space 시작점을 확인했다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Client의 다음 Data seq: 1001
Server의 다음 Data seq: 5001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 ACK는 TCP Header만 있는 순수 ACK일 수 있고 Client의 첫 Application Data를 함께 실을 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 두 번으로 끝낼 수 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN과 SYN-ACK까지만 교환했다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Server의 SYN-ACK을 받았으므로 양방향 경로가 동작하고 Server의 ISN이 5000이라는 사실을 안다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Server는 자신의 SYN-ACK이 Client에게 도착했는지 알 수 없다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN 도착
Server &amp;rarr; Client : SYN-ACK 전송
                 실제 도착 여부를 Server는 모름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN-ACK이 중간에서 사라졌는데 Server가 연결을 완성했다고 판단하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 Server ISN을 모르는 상태인데 Server만 연결됐다고 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 ACK가 Server에 도착해야 Server는 다음을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server &amp;rarr; Client 방향으로 보낸 SYN이 도착했다.&lt;/li&gt;
&lt;li&gt;Client가 Server의 Sequence Number를 받았다.&lt;/li&gt;
&lt;li&gt;Client &amp;rarr; Server 방향의 ACK도 돌아왔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 메시지는 각 방향의 ISN을 알리고 상대가 그 ISN을 받았다는 확인을 완성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 단순히 &amp;ldquo;양쪽이 송신과 수신을 모두 할 수 있는지 세 번 확인한다&amp;rdquo;고 표현할 수 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;두 개의 독립적인 Sequence Space를 양쪽 모두가 확인하는 데 세 번째 ACK가 필요하다&lt;/b&gt;는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Initial Sequence Number는 왜 0부터 시작하지 않을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 ISN은 예제처럼 항상 1000이나 5000으로 시작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Stack은 연결마다 변하는 32비트 ISN을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ISN을 변화시키는 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 연결에서 늦게 도착한 Segment와 새로운 연결의 Segment를 구분하는 데 도움을 준다.&lt;/li&gt;
&lt;li&gt;Sequence Number를 쉽게 예측해 위조 Segment를 삽입하는 공격을 어렵게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture 도구는 읽기 쉽게 실제 Sequence Number를 0부터 시작하는 &lt;b&gt;Relative Sequence Number&lt;/b&gt;로 표시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;실제 Client ISN: 2873459012
Capture 표시    : Seq=0
다음 표시       : Seq=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서 &lt;code&gt;Seq=0&lt;/code&gt;, &lt;code&gt;Ack=1&lt;/code&gt;이 보인다고 실제 TCP Header의 ISN이 0이라는 뜻은 아닐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Raw Sequence Number와 Relative Sequence Number를 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Handshake에서는 어떤 TCP Option을 나눌까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN과 SYN-ACK에는 연결에서 사용할 기능과 한계를 알리는 TCP Option이 들어갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ MSS &amp;mdash; Maximum Segment Size&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 측이 하나의 TCP Segment에서 받을 수 있는 TCP Payload 크기를 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 방향에서 상대가 광고한 MSS를 고려해 Segment 크기를 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSS는 Ethernet Frame 전체 크기나 IP Packet 전체 크기가 아니라 TCP Data 크기 기준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Window Scale&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Header의 16비트 Window Size만으로 큰 대역폭&amp;middot;지연 경로의 수신 Window를 충분히 표현하기 어려울 때 배율을 협상한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window Scale Option은 SYN 교환 시 알려야 연결에서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ SACK Permitted&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 측이 &lt;b&gt;Selective Acknowledgment&lt;/b&gt;(SACK)을 지원함을 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속되지 않은 여러 수신 구간을 알려 손실 복구를 더 효율적으로 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Timestamps&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTT 측정과 오래된 중복 Segment 구분 등에 사용할 Timestamp 정보를 교환할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;SYN Option 교환
├─ MSS
├─ Window Scale
├─ SACK Permitted
└─ Timestamps&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake는 연결을 열기만 하는 과정이 아니라 이후 Byte Stream을 운영할 조건도 준비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽 SYN에 어떤 Option이 있었는지 확인하면 성능 문제와 중간 장비의 Option 제거 문제를 조사하는 데 도움이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SYN에 응답이 없으면 무엇을 알 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 SYN을 보냈지만 아무 응답도 받지 못했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 SYN이 손실되었는지, Server가 응답했지만 Return Path에서 손실되었는지 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정 Timer 뒤 SYN을 재전송한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
                  응답 없음
Client &amp;rarr; Server : SYN Retransmission
                  응답 없음
...
Client           : Connect Timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 원인은 다양하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Forward Path에서 SYN Drop&lt;/li&gt;
&lt;li&gt;Firewall의 Silent Drop&lt;/li&gt;
&lt;li&gt;Server 또는 Network 장애&lt;/li&gt;
&lt;li&gt;Return Path에서 SYN-ACK Drop&lt;/li&gt;
&lt;li&gt;Stateful 장비의 비대칭 경로 문제&lt;/li&gt;
&lt;li&gt;잘못된 IP 또는 Port&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN 재전송만으로 어느 지점이 범인인지 확정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client, Server, 중간 지점의 Packet Capture를 함께 봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RST가 바로 돌아오면 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN이 Server Host에 도착했지만 해당 Destination Port에 Listening Socket이 없다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 TCP Stack은 일반적으로 RST가 포함된 응답을 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Server &amp;rarr; Client : RST, ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서는 다음과 같이 즉시 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Connection refused&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timeout과 비교하면 중요한 단서가 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Timeout : SYN 또는 응답이 보이지 않음 &amp;mdash; 여러 Drop 가능성
Refused : TCP 응답이 돌아옴 &amp;mdash; Host 도달 후 Listener 부재 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 Firewall이 Reject 정책으로 RST를 대신 보낼 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RST가 왔다는 사실만으로 반드시 Server Application이 직접 거절했다고 단정할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RST Packet의 Source 위치와 TTL, 여러 지점의 Capture를 함께 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마지막 ACK가 사라지면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 SYN-ACK을 받고 마지막 ACK를 보냈지만 ACK가 중간에서 손실되었다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Server &amp;rarr; Client : SYN-ACK
Client &amp;rarr; Server : ACK  손실&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 SYN-ACK을 정상적으로 받았으므로 &lt;code&gt;ESTABLISHED&lt;/code&gt;로 이동했을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 마지막 ACK를 받지 못해 &lt;code&gt;SYN-RECEIVED&lt;/code&gt;에 남는다. Timer가 만료되면 SYN-ACK을 재전송할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Server &amp;rarr; Client : SYN-ACK Retransmission
Client &amp;rarr; Server : ACK 재응답&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client TCP는 이미 받은 SYN에 대한 재전송임을 알고 ACK를 다시 보낼 수 있다. 일시적 손실이라면 Handshake가 회복될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 ACK 하나가 손실되었다고 연결이 반드시 영구 실패하는 것은 아니다. TCP의 재전송과 상태 처리가 연결 설정에도 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 첫 Data Segment가 ACK Flag와 함께 도착하면 Server가 Handshake 완료를 확인하는 데 기여할 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Handshake가 끝나면 Application도 준비된 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Handshake는 Kernel의 TCP Stack 사이에서 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server Process가 Listen하고 있고 Kernel Queue가 연결을 받아들일 수 있으면 Handshake가 완료될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음 사실까지 보장하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server Application이 연결을 즉시 &lt;code&gt;accept()&lt;/code&gt;했다.&lt;/li&gt;
&lt;li&gt;Worker Thread가 요청을 처리할 여유가 있다.&lt;/li&gt;
&lt;li&gt;Database와 외부 API가 정상이다.&lt;/li&gt;
&lt;li&gt;TLS Handshake가 성공했다.&lt;/li&gt;
&lt;li&gt;HTTP 요청에 정상 응답할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;TCP Connect 성공
&amp;ne; TLS 성공
&amp;ne; HTTP 성공
&amp;ne; Application 업무 처리 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 연결 성공은 Transport Layer의 준비가 완료되었다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application Health를 확인하려면 실제 Protocol 요청과 응답까지 관찰해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ SYN이 너무 많이 오면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server가 SYN을 받으면 완성되지 않은 연결 상태를 일정 시간 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 많은 SYN을 보내고 마지막 ACK를 보내지 않으면 &lt;b&gt;SYN Backlog&lt;/b&gt;가 가득 차 정상 Client의 연결을 방해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;SYN Flood&lt;/b&gt; 공격이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;공격자 &amp;rarr; Server : SYN 다수
Server &amp;rarr; 공격자 : SYN-ACK
공격자           : 마지막 ACK 없음
Server           : Half-Open Connection 상태 누적&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는 Backlog 크기 조절, SYN-ACK 재시도 제한, Rate Limiting, &lt;b&gt;SYN Cookies&lt;/b&gt; 같은 방어 기법을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN Cookies는 초기 SYN 상태의 일부를 Server에 저장하는 대신 Sequence Number에 검증 가능한 정보를 인코딩해 마지막 ACK가 돌아왔을 때 상태를 복원하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 동작과 사용 조건, Option 처리 영향은 운영체제 구현에 따라 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP Handshake와 TLS Handshake는 같은 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTPS 연결에서는 여러 단계가 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 TCP 기반 HTTPS 흐름을 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. DNS Lookup
2. TCP 3-Way Handshake
3. TLS Handshake
4. HTTP Request / Response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP Handshake는 Transport 연결과 Sequence Space를 준비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS Handshake는 암호화 Parameter를 합의하고 인증서를 통해 Server Identity를 확인하며 Session Key를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 Handshake는 계층과 목적이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/3는 QUIC을 사용하므로 TCP 3-Way Handshake 없이 UDP 위의 QUIC/TLS 연결 설정을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Handshake 실패&amp;rdquo;라고만 말하지 말고 TCP, TLS, Application 중 어느 단계인지 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;우편 시스템의 모습&lt;/th&gt;
&lt;th&gt;네트워크 용어&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;연결을 시작하겠다는 첫 등기&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SYN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client ISN과 TCP Option을 알리는 연결 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요청 확인과 Server 장부 번호 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SYN-ACK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client SYN을 ACK하고 Server ISN을 알리는 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server 번호를 받았다는 최종 확인&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ACK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Server SYN의 수신을 확인해 Handshake 완성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;양쪽 발송 장부의 첫 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Initial Sequence Number&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;각 방향 TCP Sequence Space의 시작점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;창구가 연결 요청을 기다림&lt;/td&gt;
&lt;td&gt;&lt;b&gt;LISTEN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Server Socket이 새 연결을 받을 준비 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;첫 확인 답장을 기다림&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SYN-SENT&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client가 SYN 전송 후 응답을 기다리는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;마지막 확인을 기다림&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SYN-RECEIVED&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Server가 SYN-ACK 후 최종 ACK를 기다리는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;양쪽 장부가 동기화됨&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ESTABLISHED&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;TCP Data를 주고받을 수 있는 연결 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;처리 가능한 소포 크기 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;MSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;상대에게 받을 수 있는 TCP Payload 크기를 광고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;미완성 접수 대기 줄&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SYN Backlog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Handshake가 끝나지 않은 연결 요청을 관리하는 Queue&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sequence Number를 포함한 Handshake는 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client                                      Server
SYN-SENT                                    LISTEN
  SYN, Seq=1000 --------------------------&amp;gt;
                          &amp;lt;---- SYN-ACK, Seq=5000, Ack=1001
  ACK, Seq=1001, Ack=5001 ----------------&amp;gt;
ESTABLISHED                                 ESTABLISHED&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 Handshake를 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 TCP Handshake를 Packet Capture로 관찰할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 Terminal에서 개인 개발 환경의 TCP Port를 Listen한다. &lt;code&gt;nc&lt;/code&gt; 구현에 따라 옵션 문법이 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 Terminal에서 접속한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -v 127.0.0.1 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 Terminal에서 Loopback Traffic을 Capture한다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;sudo tcpdump -n -i lo -vv 'tcp port 8080'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 Loopback Interface가 일반적으로 &lt;code&gt;lo0&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 다음 Flag 순서를 찾는다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Flags [S]   : SYN
Flags [S.]  : SYN-ACK
Flags [.]   : ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Wireshark에서는 다음 Display Filter로 SYN이 포함된 Segment를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;tcp.flags.syn == 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP State를 운영체제에서 확인한다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;ss -tn state syn-sent
ss -tn state syn-recv
ss -tn state established&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 Handshake는 매우 빠르게 끝나므로 SYN 상태를 명령으로 포착하기 어려울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture가 상태 전이 순서를 확인하는 데 더 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공용 Server에 반복 SYN을 보내거나 SYN Flood를 재현하는 행위는 서비스에 영향을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 Loopback 또는 허가된 격리 환경에서 정상 연결 한 건만 관찰한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP 3-Way Handshake는 연결 가능성을 확인하고 양방향 Sequence Space의 시작점을 동기화한다.&lt;/li&gt;
&lt;li&gt;Client는 SYN에 자신의 ISN을 담고 SYN-SENT 상태로 이동한다.&lt;/li&gt;
&lt;li&gt;Server는 SYN-ACK으로 Client ISN을 확인하고 자신의 ISN을 알리며 SYN-RECEIVED가 된다.&lt;/li&gt;
&lt;li&gt;Client의 마지막 ACK가 Server ISN 수신을 확인해야 Server도 ESTABLISHED가 된다.&lt;/li&gt;
&lt;li&gt;SYN은 Application Data가 없어도 Sequence Number 하나를 소비한다.&lt;/li&gt;
&lt;li&gt;실제 ISN은 연결마다 변하며 Capture 도구는 Relative Sequence Number로 표시할 수 있다.&lt;/li&gt;
&lt;li&gt;SYN Option으로 MSS, Window Scale, SACK Permitted, Timestamp 등을 알릴 수 있다.&lt;/li&gt;
&lt;li&gt;응답 없는 SYN은 Timeout을, Listener 없는 Port의 RST는 Refused를 만들 수 있지만 중간 장비도 응답을 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;마지막 ACK가 손실되어도 SYN-ACK 재전송과 ACK 재응답으로 회복될 수 있다.&lt;/li&gt;
&lt;li&gt;TCP Handshake 성공은 TLS와 Application의 정상 동작을 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;SYN Flood는 Half-Open Connection 상태를 쌓으며 SYN Cookies 등이 완화에 사용될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심&amp;nbsp;용어:&amp;nbsp;TCP&amp;nbsp;3-Way&amp;nbsp;Handshake,&amp;nbsp;SYN,&amp;nbsp;SYN-ACK,&amp;nbsp;ACK,&amp;nbsp;Initial&amp;nbsp;Sequence&amp;nbsp;Number,&amp;nbsp;Sequence&amp;nbsp;Space,&amp;nbsp;Relative&amp;nbsp;Sequence&amp;nbsp;Number,&amp;nbsp;LISTEN,&amp;nbsp;SYN-SENT,&amp;nbsp;SYN-RECEIVED,&amp;nbsp;ESTABLISHED,&amp;nbsp;MSS,&amp;nbsp;Window&amp;nbsp;Scale,&amp;nbsp;SACK&amp;nbsp;Permitted,&amp;nbsp;Timestamp,&amp;nbsp;SYN&amp;nbsp;Backlog,&amp;nbsp;SYN&amp;nbsp;Flood,&amp;nbsp;SYN&amp;nbsp;Cookies,&amp;nbsp;RST&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 ACK는 형식적인 마지막 인사가 아니다. Server의 Sequence Number도 Client에게 도착했다는 사실을 확인해 양방향 장부를 완성한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Handshake가 끝나 양쪽의 Sequence Space가 준비되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Client가 여러 TCP Segment에 Byte를 나누어 보낸다. 그런데 중간에서 Segment 하나가 사라지면 Sender는 무엇을 보고 손실을 알아챌까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACK가 오지 않는 시간과 같은 ACK가 반복되는 현상은 각각 어떤 단서가 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;편지가 사라졌다는 사실은 어떻게 알아챌까?&lt;/b&gt;」라는 질문을 통해 Cumulative ACK, Retransmission Timeout, Duplicate ACK, Fast Retransmit와 SACK을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>tcp</category>
      <category>TCP Handshake</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1093</guid>
      <comments>https://peterica.tistory.com/1093#entry1093comment</comments>
      <pubDate>Sun, 19 Jul 2026 20:16:55 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 15. 모든 편지에 수령 확인이 필요할까?</title>
      <link>https://peterica.tistory.com/1092</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk0OA1/dJMcacjwXvz/UinjFEp1HTpLxsJMl6gNe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk0OA1/dJMcacjwXvz/UinjFEp1HTpLxsJMl6gNe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk0OA1/dJMcacjwXvz/UinjFEp1HTpLxsJMl6gNe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk0OA1%2FdJMcacjwXvz%2FUinjFEp1HTpLxsJMl6gNe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 4. 이름을 주소로 바꾸고 대화를 시작하기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Address와 Port Number를 알면 어느 Host의 어떤 Application으로 데이터를 보낼지 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 남은 질문은 어떻게 보낼 것인가다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은행 송금 요청처럼 빠짐없이 순서대로 전달되어야 하는 데이터가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 부분이라도 사라지면 전체 의미가 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 실시간 음성처럼 조금 손실되더라도 오래된 데이터를 기다리는 것보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 데이터를 제때 전달하는 편이 나은 경우도 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;모든 데이터를 확인하고 다시 보낼 것인가?
아니면 필요한 정보만 담아 바로 보낼 것인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport Layer의 대표적인 두 선택이 &lt;b&gt;TCP&lt;/b&gt;(Transmission Control Protocol)와 &lt;b&gt;UDP&lt;/b&gt;(User Datagram Protocol)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 &lt;b&gt;TCP&lt;/b&gt;는 느리지만 안전하고 &lt;b&gt;UDP&lt;/b&gt;는 빠르지만 불안전하다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 표현만으로는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; HTTP/3&lt;/b&gt;가 &lt;b&gt;UDP&lt;/b&gt; 위의 &lt;b&gt;QUIC&lt;/b&gt;을 사용하는 이유,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; DNS&lt;/b&gt;가 &lt;b&gt;UDP&lt;/b&gt;와 &lt;b&gt;TCP&lt;/b&gt;를 모두 사용하는 이유,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Application이 UDP 위에서 신뢰성을 구현할 수 있는 이유를 설명하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; Byte Stream&lt;/b&gt;, &lt;b&gt;Datagram&lt;/b&gt;, &lt;b&gt;Reliability&lt;/b&gt;, &lt;b&gt;Ordering&lt;/b&gt;, &lt;b&gt;Message Boundary&lt;/b&gt;, &lt;b&gt;Overhead&lt;/b&gt;, &lt;b&gt;Head-of-Line Blocking&lt;/b&gt;을 중심으로 TCP와 UDP가 각각 무엇을 제공하고 무엇을 Application에 맡기는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ IP는 배달을 어디까지 약속할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP는 패킷을 목적지 주소 방향으로 전달하는 &lt;b&gt;Best-Effort&lt;/b&gt; 서비스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 중 Packet Loss, 순서 변경, 중복, 지연이 발생할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;보낸 순서: Packet 1 &amp;rarr; Packet 2 &amp;rarr; Packet 3

가능한 도착:
Packet 1 &amp;rarr; Packet 3 &amp;rarr; Packet 2
Packet 1 &amp;rarr; Packet 3
Packet 1 &amp;rarr; Packet 2 &amp;rarr; Packet 2 &amp;rarr; Packet 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 자체는 다음을 End-to-End로 보장하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 Packet의 도착&lt;/li&gt;
&lt;li&gt;보낸 순서와 같은 도착 순서&lt;/li&gt;
&lt;li&gt;중복 없는 전달&lt;/li&gt;
&lt;li&gt;Application이 데이터를 처리했다는 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport Protocol은 이 불완전한 IP 위에서 Application이 원하는 통신 모델을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TCP&lt;/b&gt;는 순서 있는 신뢰 가능한 Byte Stream을 제공하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDP&lt;/b&gt;는 최소한의 Transport 정보로 독립적인 Datagram을 전달하고 추가 동작을 Application에 맡긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 일반 우편과 등기우편으로만 나누면 충분할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유에서는 UDP를 일반 우편, TCP를 등기우편에 자주 비유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 우편은 보내고 끝나며 등기우편은 접수와 수령 과정을 확인한다고 설명하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 TCP는 편지 한 통씩 수령증을 붙이는 방식과 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TCP는 Application Message가 아니라 연속된 Byte Stream을 다룬다.&lt;/li&gt;
&lt;li&gt;여러 Byte 범위를 묶어 누적 ACK할 수 있다.&lt;/li&gt;
&lt;li&gt;전송량을 수신자의 여유와 네트워크 혼잡에 맞춘다.&lt;/li&gt;
&lt;li&gt;손실된 범위를 추론해 재전송한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDP&lt;/b&gt;도 무조건 확인할 수 없는 우편은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 Sequence Number, ACK, 재전송, 오류 복구를 직접 추가할 수 있다. QUIC이 UDP 위에서 연결과 신뢰성, 혼잡 제어를 구현하는 것이 대표적인 사례다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우편 비유의 핵심은 다음 정도로 두는 것이 정확하다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;TCP: 운송 체계가 순서와 재전송을 관리하는 연속 배송 서비스
UDP: 독립된 봉투를 전달하고 추가 규칙은 이용자가 정하는 배송 서비스&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP는 무엇을 담아 보낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 Application Data를 &lt;b&gt;Datagram&lt;/b&gt; 단위로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP Header의 기본 크기는 8바이트이며 네 필드로 이루어진다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;필드&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;크기&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Source Port&lt;/td&gt;
&lt;td&gt;16비트&lt;/td&gt;
&lt;td&gt;보내는 Endpoint의 Port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Destination Port&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;16비트&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;받을 Endpoint의 Port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Length&lt;/td&gt;
&lt;td&gt;16비트&lt;/td&gt;
&lt;td&gt;UDP Header와 Data의 전체 길이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Checksum&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;16비트&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;전송 중 손상 탐지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDP&lt;/b&gt; Header에는 다음 기능을 위한 필드가 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결 설정 Handshake&lt;/li&gt;
&lt;li&gt;Sequence Number&lt;/li&gt;
&lt;li&gt;Acknowledgment Number&lt;/li&gt;
&lt;li&gt;Receiver Window&lt;/li&gt;
&lt;li&gt;Protocol 자체의 재전송 Timer&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDP&lt;/b&gt;는 Datagram을 IP에 넘기고 도착 여부를 기본적으로 추적하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단순함은 Header와 상태 관리 부담을 줄이지만, 필요한 신뢰성을 Application이 결정해야 한다는 뜻이기도 하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP Checksum이 있으면 신뢰할 수 있는 것 아닐까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Checksum은 데이터가 전송 중 손상되었는지 탐지하는 데 도움을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Checksum이 다음을 제공하는 것은 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;손상된 Datagram의 자동 복구&lt;/li&gt;
&lt;li&gt;사라진 Datagram의 재전송&lt;/li&gt;
&lt;li&gt;Datagram 순서 정렬&lt;/li&gt;
&lt;li&gt;중복 Datagram 제거&lt;/li&gt;
&lt;li&gt;보낸 상대의 신원 인증&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Checksum 검증에 실패하면 Datagram은 폐기될 수 있지만 UDP가 새 Datagram을 요청하지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 UDP에서는 Checksum 값 0으로 미사용을 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 UDP Checksum은 일반적으로 필수이며 일부 제한적인 예외는 별도 규격과 환경에 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP와 UDP의 Checksum도 암호학적 무결성과 인증을 제공하는 기능이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도적인 변조와 도청을 막으려면 TLS, DTLS, QUIC 같은 보안 계층이 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Datagram은 편지 한 통의 경계를 기억한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 UDP Socket에 세 번 데이터를 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;send: &quot;ABC&quot;
send: &quot;DEFG&quot;
send: &quot;HI&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 각각을 독립된 Datagram으로 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 도착했다면 수신 Application은 세 Datagram의 경계를 구분해 받을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;recv: &quot;ABC&quot;
recv: &quot;DEFG&quot;
recv: &quot;HI&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Message Boundary를 보존한다&lt;/b&gt;고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 수신 Buffer가 Datagram보다 작으면 나머지 부분이 잘리거나 API별 동작이 달라질 수 있다. 큰 UDP Datagram은 IP Fragmentation과 손실 위험도 키울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Datagram은 독립적이므로 다음처럼 도착할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;DEFG&quot; &amp;rarr; &quot;ABC&quot; &amp;rarr; &quot;HI&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 하나가 사라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;ABC&quot; &amp;rarr; &quot;HI&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDP&lt;/b&gt; Protocol은 이를 원래 순서로 재배치하거나 누락을 자동으로 채우지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP는 편지보다 긴 종이 두루마리에 가깝다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TCP&lt;/b&gt;는 Application에 &lt;b&gt;Ordered Byte Stream&lt;/b&gt;을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 다음처럼 데이터를 써도 수신 측의 읽기 단위가 그대로 일치한다고 보장하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;Sender write 1: &quot;ABC&quot;
Sender write 2: &quot;DEFG&quot;

가능한 Receiver read:
&quot;ABCDEFG&quot;

또는
&quot;AB&quot;
&quot;CDE&quot;
&quot;FG&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Byte의 순서는 보존하지만 Application Message의 경계는 보존하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 TCP 위의 Application Protocol은 메시지 경계를 스스로 표현해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고정 길이 Header&lt;/li&gt;
&lt;li&gt;Length Prefix&lt;/li&gt;
&lt;li&gt;줄바꿈이나 특정 Delimiter&lt;/li&gt;
&lt;li&gt;HTTP처럼 Header와 Content-Length, Chunked Encoding 등의 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP를 사용한다고 Application Message가 자동으로 한 번의 &lt;code&gt;read&lt;/code&gt;에 온전히 도착하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 놓치면 TCP Stream에서 Message가 합쳐지거나 나뉘는 현상을 Packet 오류로 오해할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP Header에는 어떤 약속이 들어 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Option이 없는 TCP Header의 최소 크기는 20바이트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 필드는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;필드&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Source / Destination Port&lt;/td&gt;
&lt;td&gt;양쪽 Application Endpoint 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Sequence Number&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Byte Stream에서 Segment Data의 위치 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Acknowledgment Number&lt;/td&gt;
&lt;td&gt;다음에 받기를 기대하는 Byte 위치 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Data Offset&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;TCP Header 길이 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Flags&lt;/td&gt;
&lt;td&gt;SYN, ACK, FIN, RST 등 연결 상태 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Window Size&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;수신 가능한 Byte 범위 광고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Checksum&lt;/td&gt;
&lt;td&gt;Header와 Data 손상 탐지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Options&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;MSS, Window Scale, SACK 관련 정보 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP는 이 정보를 사용해 다음 기능을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Connection Establishment와 종료&lt;/li&gt;
&lt;li&gt;Byte 순서 정렬&lt;/li&gt;
&lt;li&gt;ACK와 재전송을 통한 신뢰성&lt;/li&gt;
&lt;li&gt;중복 Byte 처리&lt;/li&gt;
&lt;li&gt;Receiver 능력에 맞춘 Flow Control&lt;/li&gt;
&lt;li&gt;Network 혼잡에 맞춘 Congestion Control&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능들은 Header 12바이트 차이만의 문제가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양 끝이 Connection State, Buffer, Timer, Sequence Space와 혼잡 상태를 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 Overhead는 Packet Header 크기뿐 아니라 연결 설정과 상태 관리, ACK, 재전송, 제어 알고리즘을 포함한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP의 신뢰성은 무엇을 보장할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 신뢰 가능하다는 말은 일반적으로 다음 의미다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;연결이 유지되는 동안
상대 TCP가 전달한 Byte를
중복 없이 순서대로
Application이 읽을 수 있는 Stream에 제공한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 TCP ACK가 다음 사실까지 보장하는 것은 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 화면에서 내용을 보았다.&lt;/li&gt;
&lt;li&gt;Server Application이 업무 처리를 완료했다.&lt;/li&gt;
&lt;li&gt;Database Transaction이 Commit되었다.&lt;/li&gt;
&lt;li&gt;요청이 정확히 한 번만 비즈니스에 반영되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP ACK는 Transport Layer의 수신 상태를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application 처리 완료 확인이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 결제 요청의 Byte가 Server에 도착한 뒤 응답 전에 연결이 끊기면 Client는 결제가 처리되었는지 알기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무작정 재시도하면 중복 결제가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 Application Protocol의 응답, Idempotency Key, Transaction 상태 조회 같은 별도의 설계로 해결해야 한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;TCP Reliability &amp;ne; Exactly-Once Business Processing&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ UDP는 빠르고 TCP는 느리다는 말은 항상 맞을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP에는 TCP Handshake와 기본 재전송, 순서 정렬, Flow/Congestion Control이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 질의 하나를 즉시 보내는 상황에서는 적은 제어 절차가 장점이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 UDP를 사용한다고 Application이 자동으로 빨라지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신뢰성, 혼잡 제어, 보안을 Application이 모두 구현해야 한다면 복잡성과 제어 Traffic이 다시 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP도 이미 연결된 상태에서는 지속적으로 데이터를 효율적으로 전송할 수 있고 운영체제에 오랫동안 최적화되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 성능은 다음 요소의 영향을 받는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연결이 새로 필요한가, 재사용할 수 있는가?&lt;/li&gt;
&lt;li&gt;손실률과 RTT는 얼마인가?&lt;/li&gt;
&lt;li&gt;데이터 크기와 메시지 빈도는 어떤가?&lt;/li&gt;
&lt;li&gt;순서 보장이 필요한가?&lt;/li&gt;
&lt;li&gt;Application이 재전송과 혼잡 제어를 어떻게 구현했는가?&lt;/li&gt;
&lt;li&gt;TLS와 Protocol Handshake가 몇 번 필요한가?&lt;/li&gt;
&lt;li&gt;Kernel과 NIC Offload가 어떤 처리를 지원하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 선택 기준은 단순한 속도 서열이 아니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;TCP: 공통 신뢰성과 Stream 모델을 Transport가 제공
UDP: 최소 Datagram 모델 위에서 Application이 필요한 기능을 선택&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한 조각이 사라지면 뒤의 데이터도 기다려야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Byte를 순서대로 Application에 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Segment 중 두 번째가 사라졌다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Segment 1 도착
Segment 2 손실
Segment 3 도착
Segment 4 도착&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 TCP는 뒤의 Byte를 Buffer에 보관할 수 있지만 빠진 Byte가 복구되기 전에는 순서대로 Application에 전달하지 못할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 TCP Stream의 &lt;b&gt;Head-of-Line(HOL) Blocking&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 TCP 연결 위에서 여러 논리적 요청을 Multiplexing하는 Protocol은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 Byte 손실 때문에 뒤의 응답도 영향을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;QUIC&lt;/b&gt;은 UDP 위에서 신뢰성, 암호화, 혼잡 제어와 여러 독립 Stream을 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 QUIC Stream에서 손실이 발생해도 다른 Stream의 Application Data 전달까지 TCP 연결 전체처럼 막지 않도록 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;QUIC&lt;/b&gt;도 손실된 Packet에 있던 해당 Stream Data를 복구해야 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 혼잡과 재전송의 영향이 사라지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP가 해결한 것이 아니라 UDP의 최소 기능 위에서 QUIC이 다른 신뢰성 모델을 구현한 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어떤 Application이 UDP를 선택할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP가 어울릴 수 있는 상황은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 작은 질문과 짧은 응답&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 DNS 질의처럼 작은 Request와 Response가 중심인 경우 연결 설정을 줄일 수 있다. 다만 DNS도 큰 응답과 Zone Transfer 등에서는 TCP를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 최신 데이터의 시간이 중요한 실시간 통신&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Voice와 Video에서 너무 늦게 도착한 오래된 Frame은 재생 가치가 낮을 수 있다. Application은 일부 손실을 허용하고 Jitter Buffer, 선택적 복구, Codec의 오류 은폐를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Application이 독자적인 신뢰성 모델을 원하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QUIC은 UDP 위에서 Stream별 신뢰성, 연결 이동, 혼잡 제어를 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Multicast 또는 Broadcast가 필요한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 IP Multicast와 Broadcast Datagram에 사용할 수 있다. TCP는 두 Endpoint 사이의 연결 기반 Unicast Protocol이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP 사용이 곧 Packet Loss를 무시한다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application이 자신의 목적에 맞는 손실 처리와 혼잡 제어를 책임진다는 뜻이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어떤 Application이 TCP를 선택할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 어울릴 수 있는 상황은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 모든 Byte를 순서대로 받아야 하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 전송, 원격 터미널, 일반적인 Database 연결처럼 누락된 Byte를 그대로 허용하기 어려운 통신이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 공통의 재전송과 혼잡 제어를 활용하려는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application마다 신뢰성과 네트워크 공정성을 새로 구현하지 않고 운영체제의 TCP Stack을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Stream 기반 Protocol인 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP/1.1, HTTP/2, TLS 기반의 많은 Application Protocol이 TCP Byte Stream 위에서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP를 선택해도 Application Timeout, 요청 재시도, 업무 중복 방지, 보안은 별도로 설계해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transport가 제공하는 신뢰성과 Application이 필요로 하는 신뢰성의 범위를 구분해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP와 UDP는 보안을 제공할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 연결과 ACK는 상대의 Application 신원을 인증하거나 내용을 암호화하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP의 Checksum도 공격자에 의한 변조를 막는 암호학적 보호가 아니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;TCP/UDP가 제공하지 않는 것
├─ 기밀성
├─ 상대 신원 인증
└─ 암호학적 무결성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 위에서는 TLS를, UDP Datagram 기반 Application에서는 DTLS를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QUIC은 TLS 1.3 Handshake를 통합해 보안 연결을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port 443을 사용하거나 TCP로 연결되었다는 사실만으로 Traffic이 안전하다고 판단할 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;독립된 봉투 한 장&lt;/td&gt;
&lt;td&gt;&lt;b&gt;UDP Datagram&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Message Boundary가 있는 독립적인 전송 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;이어 붙인 긴 종이 두루마리&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;TCP Byte Stream&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Message Boundary 없이 순서 있는 Byte의 흐름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;봉투 번호와 수령 확인&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Sequence Number / ACK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Byte 위치와 수신 범위를 추적하는 TCP 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;사라진 내용을 다시 발송&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Retransmission&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;손실로 판단한 Data를 다시 보내는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;받는 창구의 남은 공간 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Flow Control&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Receiver Buffer에 맞춰 전송량을 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;도로 혼잡에 맞춘 발송량 조절&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Congestion Control&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Network 상태에 맞춰 In-flight Data를 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;앞의 종이가 빠져 뒤도 읽지 못함&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Head-of-Line Blocking&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;손실된 Byte 복구 전 뒤의 Stream 전달이 지연&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;필요한 배송 규칙을 이용자가 직접 추가&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Application-Level Reliability&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;UDP 위에서 ACK&amp;middot;재전송 등을 Application이 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP와 UDP의 기본 차이는 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;통신 모델&lt;/td&gt;
&lt;td&gt;Connection-oriented Byte Stream&lt;/td&gt;
&lt;td&gt;Connectionless Datagram&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message Boundary&lt;/td&gt;
&lt;td&gt;보존하지 않음&lt;/td&gt;
&lt;td&gt;보존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순서 보장&lt;/td&gt;
&lt;td&gt;Byte 순서 제공&lt;/td&gt;
&lt;td&gt;Protocol이 제공하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;손실 복구&lt;/td&gt;
&lt;td&gt;ACK와 재전송 제공&lt;/td&gt;
&lt;td&gt;Protocol이 제공하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flow/Congestion Control&lt;/td&gt;
&lt;td&gt;제공&lt;/td&gt;
&lt;td&gt;Application에 맡김&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기본 Header&lt;/td&gt;
&lt;td&gt;최소 20바이트&lt;/td&gt;
&lt;td&gt;8바이트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multicast/Broadcast&lt;/td&gt;
&lt;td&gt;지원하지 않음&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 Header와 전달 단위를 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 TCP와 UDP Socket을 구분해 본다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;ss -tn
ss -uan&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Packet Capture에서 두 Protocol의 Header를 비교할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;sudo tcpdump -n -vv -i eth0 'tcp or udp'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Port만 보고 싶다면 다음처럼 Filter를 좁힌다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;sudo tcpdump -n -vv -i eth0 'tcp port 8080 or udp port 8081'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 출력에서는 다음 항목을 찾아본다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Flags, Sequence Number, Acknowledgment, Window, Options&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP 출력에서는 다음 항목을 비교한다.&lt;/p&gt;
&lt;pre class=&quot;vhdl&quot;&gt;&lt;code&gt;Source Port, Destination Port, Length, Checksum&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 개발 환경에서 &lt;code&gt;nc&lt;/code&gt;로 UDP Datagram을 주고받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현마다 Listen 옵션 문법이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 Terminal:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -u -l 8081&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;송신 Terminal:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc -u 127.0.0.1 8081&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 TCP처럼 Handshake로 연결 상태를 만들지 않는다는 점을 &lt;code&gt;ss -uan&lt;/code&gt;과 Packet Capture에서 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제 Socket API에서 &lt;code&gt;connect&lt;/code&gt;한 UDP Socket은 Peer가 표시될 수 있지만 TCP Handshake가 수행되었다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loopback Interface를 관찰할 때 Linux에서는 보통 &lt;code&gt;lo&lt;/code&gt;, macOS에서는 보통 &lt;code&gt;lo0&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Interface 이름은 시스템에서 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP는 Packet의 도착, 순서, 중복 없는 전달을 End-to-End로 보장하지 않는 Best-Effort Protocol이다.&lt;/li&gt;
&lt;li&gt;UDP는 8바이트 Header로 독립된 Datagram을 전달하고 Message Boundary를 보존한다.&lt;/li&gt;
&lt;li&gt;UDP Protocol은 연결 설정, 순서 정렬, ACK, 재전송, Flow/Congestion Control을 기본 제공하지 않는다.&lt;/li&gt;
&lt;li&gt;TCP는 연결 상태를 관리하며 순서 있는 신뢰 가능한 Byte Stream을 제공한다.&lt;/li&gt;
&lt;li&gt;TCP는 Byte 순서는 보존하지만 Application Message Boundary는 보존하지 않는다.&lt;/li&gt;
&lt;li&gt;TCP ACK는 상대 Application의 업무 처리 완료나 Exactly-Once 처리를 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;UDP라고 항상 빠르고 TCP라고 항상 느린 것은 아니며 필요한 기능과 환경을 함께 비교해야 한다.&lt;/li&gt;
&lt;li&gt;TCP Stream은 손실된 Byte 때문에 뒤의 Data 전달도 지연되는 HOL Blocking을 겪을 수 있다.&lt;/li&gt;
&lt;li&gt;QUIC은 UDP 위에서 필요한 신뢰성, 보안, 혼잡 제어와 독립 Stream을 구현한다.&lt;/li&gt;
&lt;li&gt;TCP와 UDP의 Checksum은 암호화나 신원 인증을 제공하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Best-Effort,&amp;nbsp;TCP,&amp;nbsp;UDP,&amp;nbsp;Byte Stream,&amp;nbsp;Datagram,&amp;nbsp;Message Boundary,&amp;nbsp;Sequence Number,&amp;nbsp;Acknowledgment,&amp;nbsp;Retransmission,&amp;nbsp;Reliability,&amp;nbsp;Ordering,&amp;nbsp;Flow Control,&amp;nbsp;Congestion Control,&amp;nbsp;Head-of-Line Blocking,&amp;nbsp;QUIC,&amp;nbsp;TLS,&amp;nbsp;DTLS&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TCP와 UDP의 차이는 안전과 속도의 단순한 선택이 아니다. &lt;br /&gt;공통 신뢰성을 Transport가 맡을지, 목적에 맞는 규칙을 Application이 선택할지의 차이다.&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 Byte Stream의 순서와 손실 복구를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 데이터를 보내기 전에 양쪽이 서로 보내고 받을 준비가 되었는지, Sequence Number를 어디에서 시작할지 합의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 두 번이 아니라 세 번의 메시지가 필요할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;대화를 시작하기 전에 왜 세 번이나 인사할까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP State, SYN, SYN-ACK, ACK, Initial Sequence Number와 3-Way Handshake를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>tcp</category>
      <category>UDP</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1092</guid>
      <comments>https://peterica.tistory.com/1092#entry1092comment</comments>
      <pubDate>Sun, 19 Jul 2026 20:15:10 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 14. 한 컴퓨터의 여러 프로그램은 편지를 어떻게 나눠 받을까?</title>
      <link>https://peterica.tistory.com/1091</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NSYVO/dJMcagzuOPk/eQDpkNGMpfQ11zVJI8CvS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NSYVO/dJMcagzuOPk/eQDpkNGMpfQ11zVJI8CvS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NSYVO/dJMcagzuOPk/eQDpkNGMpfQ11zVJI8CvS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNSYVO%2FdJMcagzuOPk%2FeQDpkNGMpfQ11zVJI8CvS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 4. 이름을 주소로 바꾸고 대화를 시작하기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS를 통해 Server의 IP 주소를 알아냈다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;example.com &amp;rarr; Server IP Address&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소를 알면 패킷을 어느 컴퓨터까지 보낼지는 정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 한 컴퓨터에서는 여러 프로그램이 동시에 네트워크를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Server 한 대
├─ Web Server
├─ SSH Server
├─ DNS Server
└─ Database Server&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;패킷이 Server의 Network Interface에 도착한 뒤 운영체제는 데이터를 어느 프로그램에 전달해야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소만으로는 컴퓨터 안의 수신 프로그램을 구분할 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 TCP와 UDP Header에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Port Number&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소가 건물을 찾는 주소라면 Port Number는 그 안에서 서비스를 접수하는 창구 번호다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 Port를 프로그램에 붙은 고정 번호라고만 이해하면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 Web Server가 수천 명의 Client 연결을 동시에 처리하는 이유를 설명할 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Transport Layer&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Port Number&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Socket&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Listen&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Ephemeral Port&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;4-Tuple&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;5-Tuple&lt;/b&gt;을 통해&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제가 여러 프로그램과 연결을 어떻게 구분하는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ IP 주소가 건물이라면 Port는 창구다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 우체국 건물 안에 여러 창구가 있다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;우체국 주소: 서울시 어느 거리 10

1번 창구: 일반 우편
2번 창구: 국제 우편
3번 창구: 소포
4번 창구: 등기&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;건물 주소만 적으면 우편물은 우체국까지 도착할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 어느 업무 창구에서 처리해야 할지는 알 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크에서도 IP Layer는 패킷을 목적지 Host까지 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 안의 TCP 또는 UDP는 Destination Port를 보고 해당 데이터를 기다리는 Socket으로 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Demultiplexing&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반대로 여러 Application의 데이터를 Transport Layer가 Port 정보와 함께&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크로 내보내는 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Multiplexing&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;관점으로 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;송신 Host
여러 Application &amp;rarr; Socket/Port &amp;rarr; TCP&amp;middot;UDP &amp;rarr; IP

수신 Host
IP &amp;rarr; TCP&amp;middot;UDP &amp;rarr; Destination Port/Socket &amp;rarr; 해당 Application&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port는 네트워크 패킷이 Host 안의 올바른 통신 끝점으로 전달되게 하는 식별자다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ TCP Port와 UDP Port는 같은 공간일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port Number는 TCP와 UDP Header에 각각 존재하는 16비트 값이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;표현 가능한 범위는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;0 ~ 65535&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP와 UDP는 서로 다른 Transport Protocol이므로 Port 공간도 별도로 구분된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 한 Host에서 다음 두 Socket이 동시에 존재할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;TCP Port 53
UDP Port 53&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS Server가 UDP 53과 TCP 53을 모두 사용하는 것이 대표적인 예다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Firewall Rule에서도 Protocol을 생략하고 &amp;ldquo;Port 53을 허용한다&amp;rdquo;고만 생각하면 한쪽만 허용하거나 필요 이상의 트래픽을 열 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;UDP/53과 TCP/53은 서로 다른 Endpoint다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port Number만으로 통신을 완전히 식별할 수 없는 첫 번째 이유다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Port 번호는 누가 정할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IANA의 Service Name and Transport Protocol Port Number Registry에서는 Port 범위를 다음과 같이 구분한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 67px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;범위&lt;/td&gt;
&lt;td&gt;이름&lt;/td&gt;
&lt;td&gt;일반적인 용도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;0~1023&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;System Ports 또는 Well-Known Ports&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;널리 알려진 표준 Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;1024~49151&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;User Ports 또는 Registered Ports&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;등록된 Application과 일반 Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;49152~65535&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Dynamic/Private Ports&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동적으로 선택하거나 사적으로 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대표적으로 잘 알려진 Port는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;기본 Port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;UDP/TCP&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPS (HTTP/1.1&amp;middot;HTTP/2)&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP/3 (QUIC)&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 Port Number가 그 Application을 강제로 보장하는 것은 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Web Server를 TCP 8080에서 실행할 수 있다.&lt;/li&gt;
&lt;li&gt;TCP 443에서 HTTPS가 아닌 임의의 Protocol을 실행할 수도 있다.&lt;/li&gt;
&lt;li&gt;아무 Process도 TCP 80에서 기다리지 않을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port는 관례와 설정으로 Service에 연결된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;TCP 443이 열려 있음 &amp;ne; 반드시 올바른 HTTPS Service&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 Protocol이 맞는지는 Application Layer의 대화까지 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ URL에 Port를 적지 않아도 되는 이유는 무엇일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저에 다음 주소를 입력한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;https://example.com/&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;URL에 Port가 보이지 않지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;https&lt;span&gt;&amp;nbsp;&lt;/span&gt;Scheme에는 기본 Port 443이 정의되어 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;HTTP/1.1과 HTTP/2는 일반적으로 TCP 443을 사용하고, HTTP/3는 QUIC을 통해 UDP 443을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;http://example.com/  &amp;rarr; 기본 Port 80
https://example.com/ &amp;rarr; 기본 Port 443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다른 Port를 사용하려면 URL에 명시할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;https://example.com:8443/&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS는&lt;span&gt;&amp;nbsp;&lt;/span&gt;example.com의 IP 주소를 찾는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 A/AAAA 조회가 Service Port까지 알려 주는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;DNS   : 어느 Host IP로 갈 것인가?
Scheme와 설정: 어느 Transport와 Port를 사용할 것인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Service Discovery를 위해 SRV, HTTPS 같은 DNS Record Type을 활용하는 경우도 있지만&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 URL 연결의 기본 Port 선택과는 구분해서 이해해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Server는 창구를 어떻게 열까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Server Application이 TCP 8080에서 Client를 기다리는 과정을 단순화해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. Socket을 만든다.
2. Local IP Address와 Port 8080에 Bind한다.
3. TCP Socket을 Listen 상태로 만든다.
4. Client의 연결 요청을 Accept한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Socket을 만든다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Application은 운영체제에 사용할 Address Family와 Transport Protocol에 맞는 Socket을 요청한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Socket은 Application이 Network Stack과 데이터를 주고받는 운영체제 객체 또는 통신 끝점으로 이해할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Bind한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어느 Local Address와 Port에서 데이터를 받을지 연결한다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;127.0.0.1:8080
192.168.10.20:8080
0.0.0.0:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 세 설정은 의미가 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Listen한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP Server는 해당 Socket을 Client의 연결 요청을 받을 수 있는 Listening 상태로 둔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port가 방화벽에서 허용되어 있어도 Listen하는 Socket이 없다면 Application은 연결을 받을 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 127.0.0.1과 0.0.0.0은 무엇이 다를까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Server가 어느 Local Address에 Bind했는지는 외부 접근 가능성에 영향을 준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 127.0.0.1:8080&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IPv4 Loopback Address에만 Bind한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 Host 안에서는 접속할 수 있지만 다른 컴퓨터에서 Server의 LAN IP로 접속할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;같은 Host &amp;rarr; 127.0.0.1:8080 가능
외부 Host &amp;rarr; 192.168.10.20:8080 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 192.168.10.20:8080&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 Interface의 주소에 Bind한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 주소로 들어오는 연결은 받을 수 있지만 다른 Local IP로 들어오는 연결은 별개다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 0.0.0.0:8080&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 Host의 모든 IPv4 Local Address에서 Port 8080을 받는 Wildcard Bind를 뜻한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IPv6의&lt;span&gt;&amp;nbsp;&lt;/span&gt;::&lt;span&gt;&amp;nbsp;&lt;/span&gt;Wildcard Bind와 IPv4 동시 처리 관계는 운영체제와&lt;span&gt;&amp;nbsp;&lt;/span&gt;IPV6_V6ONLY&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정에 따라 다를 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Process가 실행 중이다&amp;rdquo;라는 사실만으로 외부에서 접속할 수 있다고 판단하면 안 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 세 조건을 나누어 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;vhdl&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Process가 실행 중인가?
원하는 IP와 Port에 Listen하는가?
경로와 Firewall이 외부 연결을 허용하는가?&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Client도 Port가 필요할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client는 Server의 Destination Port를 알고 접속한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Server: 203.0.113.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 Server가 응답을 돌려줄 Client의 창구도 필요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client 운영체제는 연결을 시작할 때 사용 가능한 임시 Source Port를 선택한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Ephemeral Port&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Client: 192.0.2.10:53124
Server: 203.0.113.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요청 Segment의 주소는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source Port      : 53124
Destination Port : 443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Server의 응답에서는 방향이 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source Port      : 443
Destination Port : 53124&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client Port 덕분에 운영체제는 돌아온 데이터를 연결을 시작한 Browser Socket에 전달할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ephemeral Port&lt;/b&gt;의 실제 할당 범위와 선택 알고리즘은 운영체제와 설정에 따라 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IANA Dynamic/Private 범위가&lt;span&gt;&amp;nbsp;&lt;/span&gt;49152~65535라고 해서 모든 운영체제가 반드시 그 범위만 사용하는 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux에서는 다음 값으로 로컬 Ephemeral Port 범위를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;sysctl net.ipv4.ip_local_port_range&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Server Port 하나로 여러 Client를 받을 수 있을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수천 명의 Client가 모두 같은 Web Server의 TCP 443에 연결할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port 443 하나뿐인데 운영체제는 각 연결을 어떻게 구분할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP 연결은 다음 네 값을 함께 사용해 구분할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source IP
Source Port
Destination IP
Destination Port&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;4-Tuple&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 Client가 같은 Server에 접속하는 예를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;192.0.2.10:51001 &amp;rarr; 203.0.113.20:443
192.0.2.11:51002 &amp;rarr; 203.0.113.20:443
192.0.2.12:51003 &amp;rarr; 203.0.113.20:443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Destination IP와 Port는 같지만 Source IP 또는 Source Port가 다르므로 서로 다른 연결이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Transport Protocol까지 포함하면 흔히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;5-Tuple&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Protocol
Source IP
Source Port
Destination IP
Destination Port&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;TCP, 192.0.2.10, 51001, 203.0.113.20, 443
TCP, 192.0.2.11, 51002, 203.0.113.20, 443&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Firewall, NAT, Load Balancer도 Flow를 구분할 때 이 5-Tuple을 중요한 기준으로 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 Listening Port가 하나의 연결만 의미하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Listening Socket은 새로운 연결 요청을 받는 창구이고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Accept된 각 TCP 연결은 서로 다른 4-Tuple을 가진 연결 Socket으로 관리된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Listening Socket과 연결 Socket은 어떻게 다를까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP Server의 상태를 단순화하면 두 종류의 Socket을 볼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Listening Socket&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Local Address : 0.0.0.0:443
State         : LISTEN&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새로운 Client의 연결 요청을 받기 위한 Socket이다. 아직 특정 Remote IP와 Port 하나에 연결되지 않았다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Established Socket&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Local  : 203.0.113.20:443
Remote : 192.0.2.10:51001
State  : ESTABLISHED&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 Client와 연결된 통신 Socket이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다른 Client가 접속하면 같은 Local Port 443을 사용하면서 Remote Endpoint가 다른 Established Socket이 추가된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;LISTEN 0.0.0.0:443
├─ ESTABLISHED 203.0.113.20:443 &amp;harr; 192.0.2.10:51001
├─ ESTABLISHED 203.0.113.20:443 &amp;harr; 192.0.2.11:51002
└─ ESTABLISHED 203.0.113.20:443 &amp;harr; 192.0.2.12:51003&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 구조가 Server Port 하나로 많은 Client 연결을 동시에 처리할 수 있게 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Port는 Process ID일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port Number와 Process ID는 다른 개념이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 Process가 여러 Port에 Listen할 수 있다.&lt;/li&gt;
&lt;li&gt;Process가 재시작되면 PID가 바뀌어도 같은 Port에 다시 Bind할 수 있다.&lt;/li&gt;
&lt;li&gt;하나의 Process가 여러 Socket과 연결을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;운영체제 기능과 설정에 따라 여러 Worker가 같은 Port를 공유할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제는 Socket과 Process의 관계를 관리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port는 Network Stack이 데이터를 전달할 Endpoint를 찾는 정보이지 운영체제 Process 자체의 고유 번호가 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port가 사용 중이라는 오류가 발생하면 같은 Local Address, Port, Protocol 조합에 이미 충돌하는 Bind가 있는지 확인해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SO_REUSEADDR,&lt;span&gt;&amp;nbsp;&lt;/span&gt;SO_REUSEPORT, Wildcard Bind 같은 옵션은&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Bind 충돌과 공유 방식에 영향을 주지만 운영체제마다 세부 동작이 다를 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ TCP와 UDP는 Socket을 같은 방식으로 구분할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP는 연결을 설정하고 각 연결의 상태를 관리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Listening Socket에서 Accept된 연결은 4-Tuple로 구분된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UDP는 TCP와 같은 Handshake와 Connection State를 기본적으로 제공하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 UDP Socket이 여러 Remote Endpoint에서 오는 Datagram을 받을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Application은 수신 API가 제공하는 Source IP와 Source Port를 보고 누구에게 응답할지 결정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UDP Socket도 운영체제 API에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;connect할 수 있지만 이는 TCP Handshake를 만든다는 뜻이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본 Remote Endpoint를 지정하고 다른 Source의 Datagram 처리나 오류 전달 방식을 제한하는 로컬 Socket 설정에 가깝다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;TCP Socket : 연결 상태와 Byte Stream을 관리
UDP Socket : 독립적인 Datagram 송수신 Endpoint&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port Number는 두 Protocol에 모두 있지만 통신 의미는 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Port가 닫혀 있으면 어떤 응답이 올까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Host까지 IP Packet이 정상적으로 도착했지만 Destination Port에서 기다리는 Application이 없을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ TCP Port에 Listener가 없음&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제는 일반적으로 TCP RST로 연결을 거절할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client에서는 다음과 같은 즉시 오류가 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Connection refused&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이는 Server Host까지의 경로가 살아 있고 해당 Port에 Listener가 없다는 강한 단서가 될 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Firewall이 Packet을 조용히 Drop&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client의 SYN이 폐기되고 오류 응답도 없다면 재전송 뒤 Timeout이 발생할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Connection timed out&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ UDP Port에 Listener가 없음&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제는 ICMP Destination Unreachable, Port Unreachable을 보낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 ICMP 정책과 Application API에 따라 Client가 이를 바로 확인하지 못할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오류 메시지는 환경과 정책에 따라 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Refused  : Host 응답은 왔지만 TCP Listener가 없을 가능성
Timeout  : 경로, Firewall, Server 무응답 등 여러 가능성&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Port 상태만으로 전체 Application의 정상 동작까지 보장되지는 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;네트워크 용어&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;우체국 건물 주소&lt;/td&gt;
&lt;td&gt;&lt;b&gt;IP Address&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Network에서 Host 또는 Interface를 식별하는 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;건물 안 업무 창구 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Port Number&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Host 안의 Transport Endpoint를 구분하는 16비트 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;창구를 열고 기다림&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Bind / Listen&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Local Address와 Port에서 연결 요청을 받을 준비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용자가 잠시 배정받은 답장 창구&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Ephemeral Port&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client가 연결 Source에 동적으로 사용하는 Port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;창구와 Application을 잇는 접점&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Socket&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Application과 Network Stack 사이의 통신 Endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여러 창구의 우편을 한 운송망에 실음&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Multiplexing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;여러 Application Traffic을 Transport/IP로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도착 우편을 알맞은 창구로 분류&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Demultiplexing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Header 정보를 보고 올바른 Socket에 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보내는 곳과 받는 곳의 네 주소&lt;/td&gt;
&lt;td&gt;&lt;b&gt;4-Tuple&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Source/Destination IP와 Port로 TCP 연결 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운송 방식까지 포함한 주소표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;5-Tuple&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Protocol과 Source/Destination IP&amp;middot;Port로 Flow 구분&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Application 연결 준비의 흐름은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. DNS로 Server IP Address를 찾는다.
2. Scheme과 Application 설정으로 Transport Protocol과 Destination Port를 정한다.
3. Client OS가 Source IP와 Ephemeral Port를 선택한다.
4. Server의 Listening Socket에 연결을 시도한다.
5. 운영체제가 5-Tuple을 기준으로 Traffic을 해당 Socket에 전달한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 Port와 Socket을 관찰해 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux에서 Listening TCP Socket을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ss -lnt&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주요 옵션은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;-l : Listening Socket
-n : Service Name 대신 숫자로 표시
-t : TCP&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UDP Listening Endpoint를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ss -lnu&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Process 정보까지 보려면 권한이 필요할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;sudo ss -lntup&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 TCP 연결의 Local과 Peer Address를 본다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ss -tn&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;macOS에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;lsof로 Listening TCP Socket을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;lsof -nP -iTCP -sTCP:LISTEN&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단한 TCP Listener를 개인 환경에서 열어 볼 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;nc&lt;span&gt;&amp;nbsp;&lt;/span&gt;구현에 따라 옵션 문법이 다를 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 Terminal:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;nc -l 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 번째 Terminal:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;nc -v 127.0.0.1 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;연결한 상태에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;ss -tn&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;lsof -nP -iTCP:8080을 실행해 다음을 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server의 Listening Port 8080&lt;/li&gt;
&lt;li&gt;Client가 선택한 Ephemeral Port&lt;/li&gt;
&lt;li&gt;Local Address와 Peer Address가 뒤바뀐 양쪽 Socket&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TCP 1024 미만의 System Port에 Bind하려면 운영체제에서 높은 권한이 필요할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;관찰 실습에는 일반 사용자로 열 수 있는 8080 같은 높은 Port를 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공유 Server에서 임의의 Listener를 열면 정책 위반이나 Port 충돌이 생길 수 있으므로 개인 개발 환경에서만 수행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP Address만으로는 Host 안의 여러 Application을 구분할 수 없어 Transport Port가 필요하다.&lt;/li&gt;
&lt;li&gt;TCP와 UDP Port는 각각 16비트이며 Protocol별로 별도의 Endpoint 공간을 갖는다.&lt;/li&gt;
&lt;li&gt;Port 범위는 System 0&lt;s&gt;1023, User 1024&lt;/s&gt;49151, Dynamic/Private 49152~65535로 등록상 구분된다.&lt;/li&gt;
&lt;li&gt;Well-Known Port는 관례이며 그 Port에서 실제로 어떤 Application Protocol이 동작하는지 보장하지 않는다.&lt;/li&gt;
&lt;li&gt;Server는 Local Address와 Port에 Bind하고 TCP에서는 Listen해 연결 요청을 기다린다.&lt;/li&gt;
&lt;li&gt;Loopback, 특정 IP, Wildcard Address 중 어디에 Bind했는지에 따라 접근 범위가 달라진다.&lt;/li&gt;
&lt;li&gt;Client는 응답을 받을 Source Endpoint에 Ephemeral Port를 사용한다.&lt;/li&gt;
&lt;li&gt;같은 Server Port의 여러 TCP 연결은 Source/Destination IP와 Port의 4-Tuple로 구분된다.&lt;/li&gt;
&lt;li&gt;Protocol까지 포함한 5-Tuple은 Firewall, NAT, Load Balancer의 Flow 구분에도 사용된다.&lt;/li&gt;
&lt;li&gt;Port는 Process ID가 아니며 Socket과 Process의 관계는 운영체제가 관리한다.&lt;/li&gt;
&lt;li&gt;TCP Refused와 Timeout은 실패한 단계가 다를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어:&amp;nbsp;Transport Layer,&amp;nbsp;Port Number,&amp;nbsp;TCP Port,&amp;nbsp;UDP Port,&amp;nbsp;Well-Known Port,&amp;nbsp;Registered Port,&amp;nbsp;Dynamic/Private Port,&amp;nbsp;Ephemeral Port,&amp;nbsp;Socket,&amp;nbsp;Bind,&amp;nbsp;Listen,&amp;nbsp;Accept,&amp;nbsp;Loopback,&amp;nbsp;Wildcard Address,&amp;nbsp;Multiplexing,&amp;nbsp;Demultiplexing,&amp;nbsp;4-Tuple,&amp;nbsp;5-Tuple,&amp;nbsp;LISTEN,&amp;nbsp;ESTABLISHED,&amp;nbsp;RST&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Port는 프로그램의 이름표가 아니다. 하나의 Host에서 수많은 통신 흐름을 올바른 Socket으로 나누기 위한 Transport Layer의 창구 번호다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP Address와 Port Number를 정하면 어느 Host의 어떤 Application과 통신할지 표현할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 데이터를 어떤 규칙으로 보낼지 선택해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 메시지에 연결 준비, 순서 확인, 수령 확인과 재전송이 필요할까? 아니면 일부 메시지는 빠르게 보내고 Application이 필요한 신뢰성을 직접 결정하는 편이 나을까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;모든 편지에 수령 확인이 필요할까?&lt;/b&gt;」라는 질문을 통해 TCP와 UDP의 Header, Byte Stream과 Datagram, Reliability, Ordering, Overhead를 비교한다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>port</category>
      <category>TCP Port</category>
      <category>transport layer</category>
      <category>UDP Port</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1091</guid>
      <comments>https://peterica.tistory.com/1091#entry1091comment</comments>
      <pubDate>Sun, 19 Jul 2026 20:14:09 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 13. 우리는 왜 숫자 대신 이름으로 접속할까?</title>
      <link>https://peterica.tistory.com/933</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ4Jnw/dJMcab52Ww1/80mAFAXwmfUCFOY0WBWk11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ4Jnw/dJMcab52Ww1/80mAFAXwmfUCFOY0WBWk11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ4Jnw/dJMcab52Ww1/80mAFAXwmfUCFOY0WBWk11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ4Jnw%2FdJMcab52Ww1%2F80mAFAXwmfUCFOY0WBWk11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 4. 이름을 주소로 바꾸고 대화를 시작하기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터는 Destination IP를 보고 패킷의 다음 길을 선택한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 사용자는 웹사이트에 접속할 때 IP 주소를 직접 입력하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dts&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;https://example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람은&lt;span&gt;&amp;nbsp;&lt;/span&gt;example.com이라는 이름을 기억한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 IP Packet을 보내려는 운영체제는 목적지 IP 주소가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;사람이 아는 정보 : example.com
네트워크가 필요한 정보 : IPv4 또는 IPv6 Address&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이름과 주소 사이에는 누군가 답해야 할 질문이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;example.com은 어느 IP 주소에 있는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 질문을 전 세계에서 하나의 서버가 모두 처리한다면 그 서버의 장애와 부하가 인터넷 전체에 영향을 준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 이름을 한 파일에 저장해 배포해도 변경 사항을 빠르게 반영하기 어렵다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS는 이름 공간을 계층적으로 나누고, 각 영역의 관리 권한을 위임하고, 반복되는 답은 일정 시간 캐시하는 방식으로 이 문제를 해결한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;Stub Resolver&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Recursive Resolver&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Root Name Server&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;TLD Name Server&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Authoritative Name Server&lt;/b&gt;를&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;거치는 흐름과 주요&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DNS Record&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Cache TTL&lt;/b&gt;, 그리고 이름 조회 실패가 어떤 모습으로 나타나는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 인터넷 주소록은 한 권일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동네의 모든 집 주소를 한 사람이 관리할 수는 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 전 세계의 사람과 조직, 서비스 이름을 한 권의 주소록에 넣는다면 다음 문제가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 이름을 등록할 때마다 중앙 관리자가 수정해야 한다.&lt;/li&gt;
&lt;li&gt;모든 조회가 한 장소로 몰린다.&lt;/li&gt;
&lt;li&gt;주소록 한 권이 고장 나면 누구도 이름을 찾지 못한다.&lt;/li&gt;
&lt;li&gt;서로 다른 조직이 자신의 이름을 독립적으로 관리하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS는 이름을 오른쪽에서 왼쪽으로 이어지는 계층으로 구성한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;www.example.com.
│   │       │  └─ Root
│   │       └──── Top-Level Domain: com
│   └──────────── Domain: example
└──────────────── Host 또는 Service Label: www&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마지막의 점은 DNS 계층의 Root를 나타낸다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;www.example.com.처럼 Root까지 모두 적은 이름을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;FQDN&lt;/b&gt;(Fully Qualified Domain Name)이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일상에서는 마지막 점을 생략한&lt;span&gt;&amp;nbsp;&lt;/span&gt;www.example.com을 주로 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 점 사이의 문자열을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Label&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;상위 영역은 하위 영역의 모든 레코드를 직접 관리하는 대신 관리 권한을 위임할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Root는 com의 위치를 안내
com은 example.com의 권한 서버를 안내
example.com의 권한 서버가 www의 실제 Record를 답변&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 권의 주소록이 아니라 여러 관리자가 맡은 계층적인 주소 체계다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 애플리케이션은 누구에게 이름을 물을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저가&lt;span&gt;&amp;nbsp;&lt;/span&gt;www.example.com에 접속하려고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저 또는 애플리케이션은 일반적으로 Root Server부터 직접 찾아가지 않는다. 운영체제의 이름 해석 기능을 호출한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;호스트 안에서 애플리케이션의 요청을 받아 DNS 질의를 시작하는 구성 요소를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Stub Resolver&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Stub Resolver는 시스템에 설정된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Recursive Resolver&lt;/b&gt;에게 질문한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Application
    &amp;darr; 이름 해석 요청
Stub Resolver
    &amp;darr; Recursive Query
Recursive Resolver&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Recursive Resolver는 가정용 공유기, 인터넷 사업자, 회사 내부 DNS, 또는 공개 DNS 서비스일 수 있다. DHCP나 수동 설정을 통해 주소를 받을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Stub Resolver가 기대하는 것은 보통 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 곳에 대신 물어보고 최종 답이나 실패 결과를 돌려 달라.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;Recursive라는 이름을 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Recursive Resolver는 먼저 기억을 확인한다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Resolver는 외부에 질문하기 전에 Cache를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;www.example.com A Record가 Cache에 있는가?
├─ 있다 &amp;rarr; 남은 TTL과 함께 즉시 응답
└─ 없다 &amp;rarr; DNS 계층에 질의 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;캐시된 답이 있으면 Root, TLD, Authoritative Server에 다시 질의할 필요가 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이는 두 가지 효과를 만든다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자에게 더 빠르게 답할 수 있다.&lt;/li&gt;
&lt;li&gt;DNS 계층 서버의 반복 조회 부하를 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS가 인터넷 규모로 확장될 수 있었던 중요한 이유는 계층 구조와 Cache가 함께 있기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cache Miss라면 Resolver는 답을 찾기 위한 과정을 시작한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;ㅁ Root Server는 모든 IP 주소를 알고 있을까?&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Recursive Resolver가&lt;span&gt;&amp;nbsp;&lt;/span&gt;www.example.com의 A Record를 찾는 흐름을 단순화해 보자.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1단계: Root Name Server에 묻는다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Resolver는 Root Name Server에 질문한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;www.example.com의 A Record는 무엇인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Root Server는 보통&lt;span&gt;&amp;nbsp;&lt;/span&gt;www.example.com의 IP 주소를 직접 답하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대신&lt;span&gt;&amp;nbsp;&lt;/span&gt;.com을 담당하는 TLD Name Server의 위치를 안내한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;나는 최종 답을 갖고 있지 않다.
com 영역은 이 Name Server들에게 물어보라.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 다음 권한 영역을 안내하는 답을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Referral&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2단계: TLD Name Server에 묻는다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Resolver&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;.com&lt;span&gt;&amp;nbsp;&lt;/span&gt;TLD Name Server에 같은 질문을 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TLD Server도 보통&lt;span&gt;&amp;nbsp;&lt;/span&gt;www의 A Record를 직접 관리하지 않는다. 대신&lt;span&gt;&amp;nbsp;&lt;/span&gt;example.com을 담당하는 &lt;b&gt;Authoritative Name Server&lt;/b&gt;의 &lt;b&gt;NS Record&lt;/b&gt;를 안내한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;example.com은 이 Authoritative Name Server들에게 물어보라.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 3단계: Authoritative Name Server에 묻는다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Resolver&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;example.com&lt;/b&gt;의 권한 서버에 질문한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 서버가 해당 Zone의 Record를 관리하고 있다면 최종 답을 제공한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;www.example.com A &amp;lt;IPv4 Address&amp;gt;
TTL &amp;lt;seconds&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4단계: 답을 Cache하고 Stub Resolver에 돌려준다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Recursive Resolver는 받은 Record를 TTL 범위 안에서 Cache하고 Client에게 응답한다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Stub &amp;rarr; Recursive &amp;rarr; Root &amp;rarr; TLD &amp;rarr; Authoritative
Stub &amp;larr; Recursive &amp;larr; 최종 답과 TTL&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Root와 TLD는 모든 Host의 IP 주소를 저장하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 관리 영역을 찾을 수 있도록 위임 정보를 제공한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 분산 위임 구조가 각 조직이 자신의 Domain을 독립적으로 관리하면서도 하나의 전역 이름 공간을 사용할 수 있게 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Recursive Query와 Iterative Query는 무엇이 다를까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Stub Resolver는 Recursive Resolver에게 최종 답을 요청한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;coq&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Recursive Query:
&amp;ldquo;대신 끝까지 찾아서 답을 달라.&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Recursive Resolver가 DNS 계층을 따라갈 때는 각 서버가 자신이 아는 최선의 정보나 다음 서버를 안내하는 방식의 질의를 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Iterative Resolution:
Root          &amp;rarr; com Server를 안내
com Server    &amp;rarr; example.com Server를 안내
Authoritative &amp;rarr; 최종 Record 응답&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 Resolver 구현은 위임 정보와 NS 주소도 Cache하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여러 Authoritative Server 중 응답 가능한 서버를 선택하며,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DNSSEC 검증 같은 추가 작업을 수행할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;입문 단계에서 핵심 차이는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Stub은 Recursive Resolver에게 최종 답을 맡긴다.
Recursive Resolver는 여러 DNS Server의 안내를 따라 답을 찾는다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 이름 하나에는 어떤 Record가 있을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS는 이름을 IP 주소로만 바꾸는 시스템이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이름에 관한 여러 종류의 정보를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Resource Record&lt;/b&gt;(RR)로 저장한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;Record Type&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;A&lt;/td&gt;
&lt;td&gt;이름을 IPv4 주소에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;AAAA&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;이름을 IPv6 주소에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;CNAME&lt;/td&gt;
&lt;td&gt;한 이름을 다른 Canonical Name의 별칭으로 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;NS&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;Zone의 Authoritative Name Server를 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;MX&lt;/td&gt;
&lt;td&gt;Domain의 Mail Exchanger를 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;TXT&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;정책 검증 등 다양한 Text 정보를 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;SOA&lt;/td&gt;
&lt;td&gt;Zone의 시작과 관리&amp;middot;갱신 관련 기본 정보를 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;PTR&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;IP 주소에 대응하는 이름을 찾는 Reverse DNS에 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저가 접속할 주소를 찾을 때 A와 AAAA Record를 함께 질의할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CNAME을 만나면 별칭이 가리키는 이름의 A 또는 AAAA Record를 다시 찾아야 할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;www.example.com CNAME service.example.net
service.example.net A &amp;lt;IPv4 Address&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MX Record는 Mail Server의 이름을 알려 주며,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 이름에 대한 A 또는 AAAA 조회가 추가로 필요할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Record Type마다 질문과 답의 의미가 다르다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;DNS가 된다&amp;rdquo;는 표현보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;어떤 이름의 어떤 Record를 조회했는가&lt;/b&gt;를 명확히 하는 것이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ DNS TTL은 패킷의 TTL과 같은 것일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS Record에도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;TTL&lt;/b&gt;(Time To Live)이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이름은 IPv4 Header의 TTL과 같지만 목적은 다르다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;IP TTL&lt;/td&gt;
&lt;td&gt;DNS TTL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;위치&lt;/td&gt;
&lt;td&gt;IPv4 Header&lt;/td&gt;
&lt;td&gt;DNS Resource Record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;단위&amp;middot;동작&lt;/td&gt;
&lt;td&gt;Router Hop마다 감소&lt;/td&gt;
&lt;td&gt;Cache가 답을 보관할 수 있는 시간(초)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;Packet의 무한 Routing Loop 제한&lt;/td&gt;
&lt;td&gt;DNS 답의 재사용 기간 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;만료 결과&lt;/td&gt;
&lt;td&gt;Packet 폐기와 ICMP 가능&lt;/td&gt;
&lt;td&gt;다음 조회에서 새 DNS 질의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS TTL이 300이면 Resolver는 일반적으로 해당 Record를 최대 300초 동안 재사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Cache에서 응답할 때는 남은 TTL이 줄어든 모습으로 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;권한 서버 응답 TTL : 300
100초 뒤 Cache 응답: 남은 TTL 약 200
TTL 만료 후         : 권한 계층에 다시 질의&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS TTL은 변경 사항이 전 세계에 &amp;ldquo;전파되는 속도&amp;rdquo;를 직접 지시하는 값이라기보다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 Cache가 기존 답을 언제까지 재사용할 수 있는지를 제한하는 값이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 서버 주소를 바꿨는데 왜 예전 주소로 갈까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서비스 운영자가 A Record를 새 IP 주소로 변경했다고 하자.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;이전 A Record: Old IP, TTL 3600
새 A Record  : New IP&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;권한 서버에서는 새 주소를 답하지만 변경 전에 Old IP를 받은 Resolver는 남은&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;TTL 동안 기존 답을 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같은 여러 Cache 계층도 영향을 줄 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 또는 브라우저 Cache&lt;/li&gt;
&lt;li&gt;운영체제 Resolver Cache&lt;/li&gt;
&lt;li&gt;로컬 네트워크의 DNS Forwarder&lt;/li&gt;
&lt;li&gt;Recursive Resolver Cache&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 같은 시간에도 사용자가 사용하는 Resolver와 기존 Cache 시점에 따라 서로 다른 IP 주소를 받을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;주소 변경을 계획할 때는 일반적으로 다음 흐름을 고려한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. 변경 전 충분한 시간에 TTL을 낮춘다.
2. 기존의 긴 TTL Cache가 만료될 시간을 기다린다.
3. A/AAAA Record를 새 주소로 변경한다.
4. 이전 주소도 전환 기간 동안 요청을 처리할 수 있게 유지한다.
5. 안정화 후 TTL을 운영 값으로 조정한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미 Cache된 답의 TTL을 권한 서버에서 나중에 낮춰도 기존 Cache의 남은 시간을 즉시 줄일 수는 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS 변경은 Record 한 줄을 수정하는 순간보다 Cache에 남아 있는 이전 답이 사라지는 기간까지 포함해 계획해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 없는 이름도 Cache할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Resolver는 성공한 답만 Cache하는 것이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;존재하지 않는 이름이라는 답도 일정 시간 Cache할 수 있다. 이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Negative Caching&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 잘못된 이름을 조회해 NXDOMAIN을 받은 직후 Record를 생성해도&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일부 Resolver는 이전의 부정 응답을 잠시 재사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Negative Cache 기간은 Zone의 SOA 정보와 Resolver 정책 등의 영향을 받는다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Positive Cache : &amp;ldquo;이 이름의 A Record는 이 주소다.&amp;rdquo;
Negative Cache : &amp;ldquo;이 이름은 존재하지 않는다.&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새 Record를 만들었는데도 일부 환경에서 NXDOMAIN이 계속 보인다면 성공 응답 Cache뿐 아니라 Negative Cache도 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 주소가 여러 개면 어느 서버로 갈까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 이름에 여러 A 또는 AAAA Record가 있을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;service.example.com A 192.0.2.10
service.example.com A 192.0.2.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS Server와 Resolver는 답의 순서를 바꾸거나 여러 주소를 함께 전달할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client는 자신의 주소 선택과 연결 로직에 따라 하나를 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 단순한 부하 분산에 활용할 수 있지만 DNS Record가 있다는 사실이 Server의 상태를 자동으로 보장하지는 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 주소 중 하나의 Server가 죽었는데 DNS 답에 계속 포함되어 있으면 사용자에게 다음과 같이 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;어떤 연결은 빠르게 성공
어떤 연결은 Timeout 후 다른 주소로 시도
환경에 따라 일부 요청은 그대로 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Client의 재시도 방식, DNS 기반 Health Check, Load Balancer 구성에 따라 결과는 달라진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;DNS에 IP가 여러 개 있으니 자동으로 장애 조치된다&amp;rdquo;고 가정하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ DNS가 실패했다는 말은 무엇을 뜻할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이름 해석 실패도 여러 상태로 나뉜다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ NXDOMAIN&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;질의한 Domain Name이 존재하지 않는다는 권한 있는 부정 응답이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오타, Record 생성 전, 잘못된 Search Domain 등이 원인일 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ NOERROR지만 원하는 Answer가 없음&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이름은 존재하지만 요청한 Record Type의 데이터가 없을 수 있다. 이를 흔히 NODATA 상황으로 설명한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이름에는 A Record가 있지만 AAAA Record가 없을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ SERVFAIL&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Resolver가 질의를 처리했지만 정상적인 답을 만들지 못했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;권한 서버 실패, DNSSEC 검증 실패, 위임 문제 등 여러 원인이 있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Timeout&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정해진 시간 안에 DNS 응답을 받지 못했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Resolver까지의 경로, UDP/TCP 53 정책, 서버 과부하, Packet Loss 등을 확인해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ REFUSED&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS Server가 정책상 해당 질의를 처리하지 않겠다고 응답할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;NXDOMAIN : 이름이 없다는 답을 받음
NODATA   : 이름은 있지만 요청 Type의 답이 없음
SERVFAIL : 서버가 정상 답을 만들지 못함
Timeout  : 응답 자체를 받지 못함
REFUSED  : 정책상 질의를 거부함&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모두 애플리케이션에서는 &amp;ldquo;이름 해석 실패&amp;rdquo;로 보일 수 있지만 조사할 계층은 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ DNS는 UDP만 사용할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전통적인 DNS 질의는 주로 UDP Port 53을 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UDP는 연결 설정 없이 질문과 답을 주고받을 수 있어 작은 DNS 메시지에 효율적이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 DNS는 TCP Port 53도 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UDP 응답이 잘려&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;TC(Truncated)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Flag가 설정되면 Client가 TCP로 다시 질의할 수 있다.&lt;/li&gt;
&lt;li&gt;큰 DNSSEC 응답 등에서 TCP가 필요할 수 있다.&lt;/li&gt;
&lt;li&gt;Zone Transfer는 일반적으로 TCP를 사용한다.&lt;/li&gt;
&lt;li&gt;Resolver와 Server 구현은 필요에 따라 TCP를 직접 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;EDNS를 사용하면 UDP에서 더 큰 메시지 크기를 협상할 수 있지만 경로 MTU와 Fragment 처리 문제도 고려해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;Firewall&lt;/b&gt;에서 UDP 53만 허용하고 TCP 53을 모두 차단하면 작은 질의는 되지만 특정 큰 응답만 실패하는 장애가 생길 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ DoH와 DoT는 무엇을 바꿀까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전통적인 DNS는 질의 이름이 네트워크에서 평문으로 보일 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;암호화된 Resolver 전송 방식으로 다음 기술을 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DoT(DNS over TLS)&lt;/b&gt;: DNS 메시지를 TLS로 보호하며 일반적으로 전용 Port 853 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DoH(DNS over HTTPS)&lt;/b&gt;: DNS 메시지를 HTTPS 위에서 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DoH와 DoT는 Client와 선택한 Resolver 사이의 DNS 전송을 암호화한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DNSSEC&lt;/b&gt;은 DNS 데이터가 권한 있는 출처에서 왔고 중간에 변조되지 않았는지 검증하기 위한 서명 체계다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;DoH / DoT : DNS 질의 전송의 기밀성과 보호
DNSSEC    : DNS 데이터의 출처 인증과 무결성 검증&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNSSEC이 질의 내용을 암호화하는 것은 아니며,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DoH/DoT가 모든 DNS Record의 권한 체인을 자동으로 검증한다는 뜻도 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서로 다른 문제를 해결한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 190px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;네트워크 용어&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;사용자가 주소 찾기를 요청하는 창구&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Stub Resolver&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;애플리케이션의 이름 해석 요청을 DNS Resolver로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 주소록을 대신 찾아보는 안내소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Recursive Resolver&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Cache를 확인하고 필요한 DNS 계층 질의를 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;최상위 분류 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Root Name Server&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TLD Name Server로 가는 위임 정보를 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;com, net 같은 구역 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;TLD Name Server&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;하위 Domain의 권한 서버를 안내&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;해당 조직이 관리하는 공식 주소록&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Authoritative Name Server&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Zone의 권한 있는 Record를 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;이름에 연결된 정보 한 줄&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Resource Record&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;A, AAAA, CNAME, NS 등 DNS 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;답을 다시 사용해도 되는 기간&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;DNS TTL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Cache가 Record를 보관할 수 있는 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;다음 주소록 담당자를 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Referral&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;하위 위임 영역의 NS 정보를 제공하는 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;존재하지 않는 이름도 잠시 기록&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Negative Caching&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;NXDOMAIN 같은 부정 응답을 Cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 DNS 조회 흐름은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. Application이 Stub Resolver에 이름 해석을 요청한다.
2. Stub은 설정된 Recursive Resolver에 질의한다.
3. Resolver는 먼저 Cache를 확인한다.
4. Cache Miss이면 Root &amp;rarr; TLD &amp;rarr; Authoritative 순으로 답을 찾는다.
5. Resource Record와 TTL을 Cache한다.
6. A/AAAA 주소를 Application에 돌려준다.
7. Application이 받은 IP 주소로 실제 연결을 시작한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ dig로 주소록의 답을 읽어 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS 진단 도구인&lt;span&gt;&amp;nbsp;&lt;/span&gt;dig로 A Record를 조회한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;dig example.com A&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출력에서 다음 구역을 살펴본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;HEADER     : Status, Flag, 질의 ID
QUESTION   : 물어본 이름과 Record Type
ANSWER     : 최종 Resource Record와 TTL
AUTHORITY  : 권한 또는 위임 관련 정보
ADDITIONAL : 관련 주소나 EDNS 정보&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AAAA Record도 별도로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;dig example.com AAAA&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 Recursive Resolver에 직접 질의하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;@Resolver-IP&lt;span&gt;&amp;nbsp;&lt;/span&gt;형식을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;dig @SERVER_IP example.com A&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SERVER_IP는 확인하려는 Resolver의 실제 IP 주소로 바꾼다. 로컬 시스템이 사용하는 Resolver와 다른 서버에 물으면 Cache와 정책이 달라 결과도 다를 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS 계층을 따라가는 모습을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;dig +trace example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;+trace는&lt;span&gt;&amp;nbsp;&lt;/span&gt;dig가 Root부터 위임을 따라 직접 질의한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;회사 방화벽이 외부 DNS Server로의 직접 질의를 막거나 네트워크 정책이 특정 Resolver만 허용하면 실패할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux에서 Resolver 설정을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;cat /etc/resolv.conf
resolvectl status&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;systemd-resolved를 사용하지 않는 환경에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;resolvectl이 없을 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;/etc/resolv.conf도 로컬 Stub 주소만 가리키고 실제 Upstream Resolver는 별도 서비스 설정에 있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;macOS에서는 다음 명령으로 DNS Resolver 구성을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;scutil --dns&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;평문 DNS Packet을 관찰하려면 다음처럼 캡처할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;sudo tcpdump -n -i eth0 'port 53'&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DoH를 사용하는 애플리케이션의 질의는 HTTPS Traffic으로 보이므로 이 Filter에서 DNS 이름이 보이지 않을 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DNS는 계층적 이름 공간, 권한 위임, Cache를 사용해 인터넷 규모의 이름 조회를 제공한다.&lt;/li&gt;
&lt;li&gt;애플리케이션은 Stub Resolver를 통해 Recursive Resolver에 최종 답을 요청한다.&lt;/li&gt;
&lt;li&gt;Cache Miss이면 Resolver는 Root, TLD, Authoritative Name Server의 위임을 따라 답을 찾는다.&lt;/li&gt;
&lt;li&gt;A와 AAAA는 주소, CNAME은 별칭, NS는 권한 서버 등 Record Type마다 역할이 다르다.&lt;/li&gt;
&lt;li&gt;DNS TTL은 Cache 보관 시간이며 IPv4 Packet의 TTL과 다른 개념이다.&lt;/li&gt;
&lt;li&gt;이미 Cache된 이전 답은 권한 Record를 수정해도 남은 TTL 동안 사용될 수 있다.&lt;/li&gt;
&lt;li&gt;존재하지 않는다는 답도 Negative Caching의 대상이 될 수 있다.&lt;/li&gt;
&lt;li&gt;여러 A/AAAA Record가 있다고 자동으로 Health Check와 완전한 장애 조치가 보장되지는 않는다.&lt;/li&gt;
&lt;li&gt;NXDOMAIN, NODATA, SERVFAIL, Timeout, REFUSED는 서로 다른 실패다.&lt;/li&gt;
&lt;li&gt;DNS는 UDP 53뿐 아니라 TCP 53도 사용한다.&lt;/li&gt;
&lt;li&gt;DoH/DoT는 전송 암호화에, DNSSEC은 DNS 데이터의 출처 인증과 무결성에 초점을 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어:&amp;nbsp;DNS,&amp;nbsp;FQDN,&amp;nbsp;Label,&amp;nbsp;Zone,&amp;nbsp;Delegation,&amp;nbsp;Stub Resolver,&amp;nbsp;Recursive Resolver,&amp;nbsp;Root Name Server,&amp;nbsp;TLD Name Server,&amp;nbsp;Authoritative Name Server,&amp;nbsp;Referral,&amp;nbsp;Resource Record,&amp;nbsp;A,&amp;nbsp;AAAA,&amp;nbsp;CNAME,&amp;nbsp;NS,&amp;nbsp;MX,&amp;nbsp;SOA,&amp;nbsp;PTR,&amp;nbsp;DNS TTL,&amp;nbsp;Negative Caching,&amp;nbsp;NXDOMAIN,&amp;nbsp;SERVFAIL,&amp;nbsp;DoH,&amp;nbsp;DoT,&amp;nbsp;DNSSEC&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS는 이름을 주소로 바꾸는 한 대의 서버가 아니다. 권한을 나누고 답을 캐시해 전 세계가 함께 사용하는 분산 주소 체계다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DNS를 통해&lt;span&gt;&amp;nbsp;&lt;/span&gt;example.com의 IP 주소를 알아냈다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 하나의 Server에서는 Web Server, SSH Server, Database처럼 여러 프로그램이 동시에 통신할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소가 건물을 찾았다면 그 안의 어느 창구에 데이터를 건네야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;한 컴퓨터의 여러 프로그램은 편지를 어떻게 나눠 받을까?&lt;/b&gt;」라는 질문을 통해 Transport Layer, Port Number, Socket, Well-Known Port, Ephemeral Port와 5-Tuple을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>dns</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>Root Name Server</category>
      <category>Stub Resolver</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/933</guid>
      <comments>https://peterica.tistory.com/933#entry933comment</comments>
      <pubDate>Sun, 19 Jul 2026 20:13:41 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 12. 길이 바뀌면 지도는 어떻게 새로 그려질까?</title>
      <link>https://peterica.tistory.com/1090</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcj7G3/dJMcaiRCnTz/gkugGLolZVw6jVAG3fM7Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcj7G3/dJMcaiRCnTz/gkugGLolZVw6jVAG3fM7Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcj7G3/dJMcaiRCnTz/gkugGLolZVw6jVAG3fM7Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcj7G3%2FdJMcaiRCnTz%2FgkugGLolZVw6jVAG3fM7Ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 3. 수많은 길 가운데 다음 길을 고르는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop가 생겨도 TTL은 한 패킷이 영원히 돌지 않도록 끝을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 TTL은 잘못된 Route를 고치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 패킷도 같은 Loop에 들어가 TTL이 끝날 때까지 네트워크 자원을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근본적인 해결은 라우터들이 실제 연결 상태에 맞는 경로를 갖는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터가 세 대뿐이라면 관리자가 Static Route를 직접 수정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;R1 ── R2 ── R3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 라우터가 수백 대이고 링크가 계속 생기고 사라진다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애가 날 때마다 모든 장비에 접속해 경로를 고치는 동안 네트워크는 오래 멈춰 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 관리자가 동시에 수정하면 경로가 일치하지 않아 Loop가 생길 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들이 이웃과 도달 가능한 네트워크 정보를 교환하고, 변화가 생기면 각자의 Routing Table을 다시 계산할 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Dynamic Routing&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Static Routing과 Dynamic Routing의 차이, 한 조직 안에서 지도를 공유하는 &lt;b&gt;OSPF&lt;/b&gt;, 조직 사이에서 도달 경로와 정책을 교환하는 &lt;b&gt;BGP&lt;/b&gt;, 변화 후 네트워크가 다시 안정되는 &lt;b&gt;Convergence&lt;/b&gt;를 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 손으로 적은 배송표는 언제 유용할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Static Route는 관리자가 Destination Prefix와 Next Hop을 직접 지정한 경로다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10.30.0.0/24 via 192.0.2.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 지점 네트워크처럼 출구가 하나뿐이라면 단순하고 예측하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Static Routing의 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;a1e64847-0b66-4b3a-9f04-ed333f85ef8e&quot;&gt;경로가 관리자의 의도 없이 바뀌지 않는다.&lt;/li&gt;
&lt;li data-uuid=&quot;07b03848-9c86-4c1d-a9f0-f7df37a082b4&quot;&gt;라우팅 프로토콜을 위한 제어 트래픽과 계산이 필요 없다.&lt;/li&gt;
&lt;li data-uuid=&quot;52b9b8ad-4500-48c0-82ea-e0d6dc94054a&quot;&gt;작은 토폴로지에서는 동작과 장애 범위를 이해하기 쉽다.&lt;/li&gt;
&lt;li data-uuid=&quot;6c00ae06-480c-41ba-be89-b73c3617575a&quot;&gt;기본 경로, 특정 관리망, 의도적인 Blackhole처럼 명확한 목적에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Static Routing의 한계&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;ca331b2d-a1c8-4e50-8d19-035411457c20&quot;&gt;링크 장애를 발견해도 대체 경로로 자동 변경되지 않는다.&lt;/li&gt;
&lt;li data-uuid=&quot;4f64d9d4-a09c-4783-98f6-f62988ec3564&quot;&gt;네트워크가 커질수록 장비마다 설정해야 할 Route가 늘어난다.&lt;/li&gt;
&lt;li data-uuid=&quot;0640f10a-a9a2-4bf1-b216-3911b80692c6&quot;&gt;추가&amp;middot;삭제 작업의 누락과 잘못된 Next Hop이 장애를 만들 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;0cf5209f-5b72-45a3-8f12-f17b7a34ad32&quot;&gt;여러 관리자가 가진 네트워크 지식을 일관되게 유지하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Static Routing은 오래된 방식이라서 나쁜 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화가 적고 경로가 단순한 곳에서는 여전히 적절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 사람이 관리해야 하는 변화의 수가 감당할 수 있는 범위를 넘어설 때다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Dynamic Routing은 패킷을 대신 배달할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Routing Protocol은 애플리케이션의 데이터 패킷을 직접 운반하는 프로토콜이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들이 다음 정보를 서로 교환하기 위한 &lt;b&gt;Control Plane Protocol&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;eadc8a21-a2ca-43d0-b6f2-0a52caf7af9c&quot;&gt;어떤 Prefix에 도달할 수 있는가?&lt;/li&gt;
&lt;li data-uuid=&quot;2bf1b51d-972d-4a30-b2f0-dcb5cf23d5d6&quot;&gt;그 Prefix는 어느 이웃을 통해 배웠는가?&lt;/li&gt;
&lt;li data-uuid=&quot;a6d5d130-7f4d-47cb-bbf3-301bb4b9855f&quot;&gt;경로의 비용과 속성은 무엇인가?&lt;/li&gt;
&lt;li data-uuid=&quot;bac23a2b-0cda-4eb0-9045-6ad80c1bc85b&quot;&gt;기존 경로가 사라졌는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 받은 정보와 자신의 연결 상태를 바탕으로 최적 경로를 계산해 RIB에 반영하고, 실제 전달에 사용할 경로를 FIB에 설치한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Routing Protocol Message
        &amp;darr;
경로 학습과 선택 &amp;mdash; Control Plane / RIB
        &amp;darr;
전달 정보 설치 &amp;mdash; FIB
        &amp;darr;
일반 IP Packet 전달 &amp;mdash; Data Plane
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅은 모든 패킷에 길을 물어보는 방식이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Control Plane에서 미리 지도를 만들고, Data Plane은 그 결과를 사용해 패킷을 빠르게 전달한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ㅁ 경로가 바뀌면 어떤 일이 일어날까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 두 개의 경로가 있는 네트워크를 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;        R2
       /  \
Client R1  R4 Server
       \  /
        R3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평상시에는 R1-R2-R4 경로의 비용이 더 낮아 사용한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2와 R4 사이의 링크가 끊어지면 라우팅 프로토콜은 대략 다음 과정을 거친다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 링크 또는 이웃의 장애를 감지한다.
2. 기존 경로가 더 이상 유효하지 않다는 정보를 전파한다.
3. 각 Router가 대체 경로를 계산한다.
4. 새로운 Route를 RIB와 FIB에 반영한다.
5. R1-R3-R4 경로로 Packet을 전달한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화를 감지한 순간부터 관련 라우터들이 일관된 새 경로를 사용하기까지의 과정을 &lt;b&gt;Convergence&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Convergence 중에는 모든 라우터가 같은 순간에 정보를 갱신하지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;4582327b-1d65-4c44-8f52-d0e7d563bc3d&quot;&gt;일부 라우터는 이전 경로를 사용할 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;f27ba382-12ed-4747-9e4c-4c61ed7d57bc&quot;&gt;일부 라우터는 새 경로를 이미 설치했을 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;8fec5106-e62f-479a-add8-6c062f4121a2&quot;&gt;짧은 Packet Loss나 일시적 Routing Loop가 생길 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;a32831db-d6ac-4f87-b6f9-0c4a7891a963&quot;&gt;Stateful Session은 경로 변경의 영향을 받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 동적 라우팅의 목표는 단지 변화를 알아내는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변화를 빠르고 안정적으로 공유해 다시 일관된 전달 상태에 도달하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 라우터가 같은 방식으로 지도를 만들까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Routing Protocol은 네트워크의 범위와 해결하려는 문제에 따라 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 한 관리 영역 안의 경로 &amp;mdash; IGP&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 조직이 관리하는 네트워크 내부에서 사용하는 프로토콜을 &lt;b&gt;IGP&lt;/b&gt;(Interior Gateway Protocol)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 OSPF, IS-IS, RIP 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직은 내부 링크의 상태와 비용을 바탕으로 빠르게 경로를 계산하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 서로 다른 관리 영역 사이의 경로 &amp;mdash; EGP&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷처럼 서로 다른 조직의 네트워크가 연결될 때는 단순한 내부 최단거리만으로 경로를 정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 조직은 비용, 계약, 보안, 트래픽 엔지니어링 정책에 따라 어떤 경로를 받아들이고 알릴지 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 인터넷의 대표적인 Inter-domain Routing Protocol은 &lt;b&gt;BGP&lt;/b&gt;(Border Gateway Protocol)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 관리 영역을 &lt;b&gt;AS&lt;/b&gt;(Autonomous System)라고 하고 번호인 &lt;b&gt;ASN&lt;/b&gt;(Autonomous System Number)으로 식별한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;AS 65001 ── BGP ── AS 65002 ── BGP ── AS 65003
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF와 BGP는 둘 다 경로를 동적으로 교환하지만 문제의 범위와 판단 기준이 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ OSPF는 동네의 도로 상태를 함께 그린다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OSPF&lt;/b&gt;(Open Shortest Path First)는 Link State 방식의 IGP다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 단순히 &amp;ldquo;이 목적지는 나를 통해 가라&amp;rdquo;고만 알리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 링크와 이웃 상태를 나타내는 정보를 공유하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 OSPF Area의 라우터들이 공통된 토폴로지 지도를 만들도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF의 기본 흐름을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1단계: 이웃을 발견한다 &amp;mdash; Hello&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF Router는 Hello Packet을 교환해 같은 Link에 있는 OSPF 이웃을 찾고 설정이 맞는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Area, Hello/Dead Timer, 인증과 네트워크 설정 등 중요한 조건이 맞지 않으면 정상적인 Neighbor 관계를 만들지 못할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF는 TCP나 UDP Port를 사용하지 않고 IP Protocol Number 89를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2단계: 필요한 이웃과 인접 관계를 만든다 &amp;mdash; Adjacency&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들은 Link State Database를 동기화하기 위한 &lt;b&gt;Adjacency&lt;/b&gt;를 형성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Broadcast Network에서는 모든 Router가 서로 완전한 Adjacency를 맺어 교환량이 커지지 않도록 DR과 BDR을 선출하는 구조를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 3단계: 링크 상태를 알린다 &amp;mdash; LSA&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 자신의 연결 정보와 도달 가능한 Prefix 등을 &lt;b&gt;LSA&lt;/b&gt;(Link-State Advertisement)로 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSA는 필요한 범위에 Flooding되어 같은 Area의 라우터들이 Link State 정보를 공유하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4단계: 공통 지도를 만든다 &amp;mdash; LSDB&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들은 받은 LSA를 &lt;b&gt;LSDB&lt;/b&gt;(Link-State Database)에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Area의 정상적으로 수렴한 Router들은 해당 Area에 대해 일관된 Link State 정보를 갖는 것을 목표로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 5단계: 자신을 기준으로 최단 경로를 계산한다 &amp;mdash; SPF&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Router는 자신의 LSDB에 &lt;b&gt;SPF(Shortest Path First)&lt;/b&gt; 알고리즘을 적용한다. 흔히 Dijkstra Algorithm으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크의 OSPF Cost를 바탕으로 각 목적지까지 가장 낮은 비용의 경로를 계산하고 Routing Table에 반영한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Hello &amp;rarr; Neighbor / Adjacency
LSA Flooding &amp;rarr; LSDB 동기화
SPF Calculation &amp;rarr; Best Route
RIB / FIB Install &amp;rarr; Packet Forwarding
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들은 같은 지도를 공유하지만 출발점이 서로 다르므로 각자 자신의 위치를 기준으로 경로를 계산한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ OSPF Area는 왜 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Router가 모든 Link 변화를 하나의 거대한 LSDB에 담으면 규모가 커질수록 Flooding과 SPF 계산 부담도 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF는 네트워크를 &lt;b&gt;Area&lt;/b&gt;로 나눠 Link State 정보의 범위를 계층화할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Area 1 ── Area 0 ── Area 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Area 0&lt;/b&gt;은 Backbone Area 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 일반 Area 사이의 연결은 기본적으로 Backbone을 중심으로 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Area 경계의 Router는 영역 사이의 경로 정보를 전달하고 Summary를 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Area를 나누면 다음 효과를 기대할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;9a3fd7c6-68ca-49bd-9275-9ba78bd0e044&quot;&gt;Link 변화의 Flooding 범위를 제한한다.&lt;/li&gt;
&lt;li data-uuid=&quot;9ab7b007-dc55-43d5-abf4-3aa2e627931c&quot;&gt;Router가 유지할 LSDB와 SPF 계산 범위를 줄인다.&lt;/li&gt;
&lt;li data-uuid=&quot;536be93f-d79b-46bb-a795-b0313c3b49ba&quot;&gt;주소 Summary를 통해 Route 수를 줄일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Area 설계가 복잡하거나 연결 규칙을 어기면 도달성 문제와 비효율적인 경로를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 네트워크에서는 무리하게 여러 Area로 나누는 것보다 단순한 Area 0 구성이 이해하기 쉬울 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ BGP는 전체 도로 지도를 공유할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷을 연결하는 조직들은 내부의 모든 Router와 Link 상태를 서로 공개하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 다음과 같은 도달 가능성을 알린다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;203.0.113.0/24 Prefix는
AS 65003, AS 65002를 거쳐 도달할 수 있다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BGP&lt;/b&gt;는 Prefix와 함께 경로 속성을 전달하는 &lt;b&gt;Path Vector Protocol&lt;/b&gt;로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BGP Update에는 도달 가능한 Prefix 정보인 &lt;b&gt;NLRI&lt;/b&gt;(Network Layer Reachability Information)와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 선택에 사용하는 여러 Path Attribute가 포함될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 속성은 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;속성&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS_PATH&lt;/td&gt;
&lt;td&gt;목적지 Prefix 광고가 거쳐 온 AS 번호의 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEXT_HOP&lt;/td&gt;
&lt;td&gt;해당 BGP Route를 전달할 다음 홉 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LOCAL_PREF&lt;/td&gt;
&lt;td&gt;한 AS 내부에서 외부로 나갈 경로의 선호도를 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MED&lt;/td&gt;
&lt;td&gt;이웃 AS에 여러 진입점의 상대적 선호를 제안&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORIGIN&lt;/td&gt;
&lt;td&gt;경로 정보가 BGP에 들어온 기원을 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BGP Router는 단순히 물리적 거리가 가장 짧은 경로를 고르지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직의 Import/Export Policy와 Path Attribute를 바탕으로 경로를 선택하고 광고한다.&lt;/p&gt;
&lt;pre class=&quot;ldif&quot;&gt;&lt;code&gt;OSPF의 중심 질문: 내부 토폴로지에서 비용이 낮은 길은 무엇인가?
BGP의 중심 질문 : 어떤 경로를 받아들이고 선택하고 이웃에게 알릴 것인가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 BGP Best Path 선택 순서와 기본 정책은 구현 및 설정에 따라 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 BGP가 인터넷 조직 사이의 정책 기반 도달성 교환이라는 점이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ BGP의 AS_PATH는 Loop를 어떻게 발견할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix 광고가 AS를 지날 때 eBGP Router는 일반적으로 자신의 ASN을 AS_PATH에 추가한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;AS 65003에서 시작
AS_PATH: 65003

AS 65002를 통과
AS_PATH: 65002 65003

AS 65001에 도착
AS_PATH: 65002 65003
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AS 65001이 이 경로를 다시 광고하면 자신의 ASN이 추가될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 AS가 받은 광고의 AS_PATH에서 자신의 ASN을 발견하면 해당 경로가 자신을 거쳐 되돌아온 것으로 판단해 일반적으로 거부한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 AS 수준의 Routing Loop를 막는 BGP의 핵심 장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AS_PATH가 없애는 것은 BGP 경로 광고의 AS Loop다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 Static Route, Route Redistribution, 내부 Policy로 생기는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Data Plane Loop까지 자동으로 해결하는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ BGP Session이 연결되면 경로도 자동으로 생길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BGP Peer는 TCP Port 179를 사용해 Session을 맺는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Session 상태가 &lt;b&gt;Established&lt;/b&gt;라면 두 Router 사이의 BGP 메시지 교환 채널이 준비되었다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Established가 곧 원하는 Route가 Routing Table에 있다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계가 각각 성립해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 광고할 Prefix가 BGP에 들어와 있는가?
2. Export Policy가 이웃에게 광고를 허용하는가?
3. Peer가 Update를 수신했는가?
4. Import Policy가 경로를 허용하는가?
5. NEXT_HOP에 도달할 수 있는가?
6. Best Path로 선택되었는가?
7. RIB와 FIB에 설치될 조건을 만족하는가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 BGP 장애는 다음처럼 나누어 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;TCP/Peer Session 문제
&amp;rarr; Route Advertisement 문제
&amp;rarr; Policy Filtering 문제
&amp;rarr; Best Path Selection 문제
&amp;rarr; Next-Hop Resolution 문제
&amp;rarr; RIB/FIB Install 문제
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;BGP가 Established인데 통신이 안 된다&amp;rdquo;는 상황은 모순이 아니다. 제어 채널 뒤의 경로 처리 단계가 별도로 남아 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ eBGP와 iBGP는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 AS 사이의 BGP Session을 &lt;b&gt;eBGP&lt;/b&gt;(External BGP)라고 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;AS 65001 ── eBGP ── AS 65002
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 AS 안에서 BGP Route를 공유하는 Session을 &lt;b&gt;iBGP&lt;/b&gt;(Internal BGP)라고 한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;AS 65001 내부
Router A ── iBGP ── Router B
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BGP가 EGP 역할을 한다고 해서 모든 BGP Session이 AS 경계를 넘는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 받은 경로를 조직 내부의 BGP Router에 전달하기 위해 iBGP를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 iBGP에서는 모든 Router 사이의 Full Mesh 부담을 줄이기 위해 Route Reflector 같은 구조를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSPF와 iBGP도 경쟁 관계로만 볼 수 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;da8078e3-1914-45c2-b3f6-a14e6807d292&quot;&gt;OSPF는 AS 내부의 Router와 BGP NEXT_HOP에 도달할 기반 경로를 제공할 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;870ad335-0ce2-496e-92f1-ad4c36e11f24&quot;&gt;iBGP는 외부 Prefix와 정책 정보를 AS 내부에 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 문제를 맡아 함께 동작할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 빠르게 바뀌는 지도가 항상 좋은 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크 장애를 빨리 감지하고 새 경로를 빨리 설치하면 Packet Loss 시간을 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 지나치게 민감하면 짧은 흔들림에도 경로가 계속 바뀌는 &lt;b&gt;Route Flapping&lt;/b&gt;이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 변화마다 다음 작업이 반복될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;f44f0110-b12d-40ef-bd89-460880af5d78&quot;&gt;Neighbor 상태 변경&lt;/li&gt;
&lt;li data-uuid=&quot;87de6883-5aa3-4ad0-beb1-671efc6270c4&quot;&gt;Update 또는 LSA 전파&lt;/li&gt;
&lt;li data-uuid=&quot;cf492b23-f6e5-4d5b-8b2b-8e9694d12c83&quot;&gt;Best Path 또는 SPF 재계산&lt;/li&gt;
&lt;li data-uuid=&quot;9e1d92aa-7405-4f96-82ae-7f4dcd4d68c8&quot;&gt;RIB/FIB 갱신&lt;/li&gt;
&lt;li data-uuid=&quot;3d4b09f7-df5b-429c-8dd1-8dfddce16158&quot;&gt;Stateful Flow의 경로 변화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 규모가 클수록 변화의 파급도 커질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Dynamic Routing은 속도와 안정성 사이의 균형을 다룬다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;bf4c5450-b0f0-44b8-b953-9969964e83fa&quot;&gt;장애 감지를 빠르게 하는 Timer와 BFD&lt;/li&gt;
&lt;li data-uuid=&quot;65bcf6c3-44e0-448b-8d5d-58fbb0d124ed&quot;&gt;변화가 연속될 때 계산을 조절하는 SPF Throttling&lt;/li&gt;
&lt;li data-uuid=&quot;f197da83-0e83-4c4b-87f2-34193bc18ce9&quot;&gt;Route Flapping의 영향을 제한하는 정책&lt;/li&gt;
&lt;li data-uuid=&quot;3b1d0045-034c-44e8-9924-f978bbeb066a&quot;&gt;Summary와 Area로 변화 범위를 줄이는 설계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 기본값과 적절한 조정 값은 프로토콜, 장비, 토폴로지에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 Timer를 가장 짧게 만드는 것이 항상 좋은 설계는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Dynamic Routing을 사용하면 Static Route는 사라질까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 네트워크에서는 여러 경로 출처를 함께 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Connected Route : 인터페이스에 직접 연결된 네트워크
Static Route    : 관리자가 명시한 특별 경로
OSPF Route      : 조직 내부에서 동적으로 학습한 경로
BGP Route       : 다른 AS 또는 BGP Peer에서 학습한 경로
Default Route   : 더 구체적인 경로가 없을 때 사용할 출구
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지점 Router는 내부 Prefix를 OSPF로 배우고 인터넷 방향은 Static Default Route를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경계 Router는 BGP로 외부 경로를 받고 내부에는 Default Route만 알릴 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 서로 다른 출처의 Route를 아무 계획 없이 재배포할 때 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Route Redistribution&lt;/b&gt;은 한 Routing Domain의 경로를 다른 Protocol에 넣는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결에는 유용하지만 경로 출처가 흐려지고 Metric 변환, Loop, 피드백 문제가 생길 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;OSPF Route를 BGP로 재배포
       &amp;darr;
BGP Route를 다시 OSPF로 재배포
       &amp;darr;
원래 경로가 다른 출처로 되돌아올 위험
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tagging, Filtering, Summary와 명확한 단방향 정책 없이 재배포하면 Control Plane Loop 또는 잘못된 우선순위를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅은 자동이지만 설계 의도까지 자동으로 만들어 주지는 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;네트워크 용어&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리자가 손으로 적은 배송표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Static Route&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;운영자가 Destination과 Next Hop을 직접 지정한 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;우체국끼리 자동으로 변경 정보를 교환&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Dynamic Routing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Protocol로 도달성과 경로 변화를 학습하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;한 조직 내부의 배송 체계&lt;/td&gt;
&lt;td&gt;&lt;b&gt;IGP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;하나의 관리 영역 안에서 사용하는 Routing Protocol 범주&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;내부 도로 상태를 함께 그린 지도&lt;/td&gt;
&lt;td&gt;&lt;b&gt;OSPF Link State&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LSA와 LSDB를 이용해 내부 토폴로지를 공유하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서로 다른 물류 회사의 관리 영역&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Autonomous System&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;하나의 Routing Policy 아래 관리되는 네트워크 집합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;회사 사이의 배송 가능 지역과 정책 교환&lt;/td&gt;
&lt;td&gt;&lt;b&gt;BGP Path Vector&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Prefix와 Path Attribute를 교환하는 Inter-domain 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;바뀐 길을 모두 반영해 다시 안정됨&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Convergence&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;변화 후 일관된 새 Forwarding 상태에 도달하는 과정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;계속 생겼다 사라지는 배송 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Route Flapping&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;경로가 반복해서 광고되고 철회되는 현상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서로 다른 지도 체계 사이에 경로 전달&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Route Redistribution&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;한 Protocol의 Route를 다른 Protocol로 주입하는 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic Routing의 전체 흐름은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Neighbor 또는 Peer 관계를 만든다.
2. Prefix와 Topology 또는 Path 정보를 교환한다.
3. Protocol별 규칙과 Policy로 Best Path를 선택한다.
4. 선택 경로를 RIB와 FIB에 반영한다.
5. 변화가 생기면 정보를 갱신하고 다시 Converge한다.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 지도의 변화를 따라가 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 OSPF 토폴로지를 종이에 그려 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;             R2
          10/  \10
           /    \
         R1      R4
           \    /
          30\  /30
             R3
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1에서 R4까지 두 후보를 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R1 &amp;rarr; R2 &amp;rarr; R4 = 10 + 10 = 20
R1 &amp;rarr; R3 &amp;rarr; R4 = 30 + 30 = 60
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 상태에서는 비용 20인 R2 경로가 선택된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2-R4 링크가 끊어지면 다음 질문을 순서대로 생각해 보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-uuid=&quot;212008af-5217-4c6a-94e4-7922e6d94673&quot;&gt;어떤 Router가 Link Down을 먼저 감지하는가?&lt;/li&gt;
&lt;li data-uuid=&quot;0ebf6111-95d3-4497-a072-17aafc66d79c&quot;&gt;변경된 Link State는 어떤 이웃에게 전달되는가?&lt;/li&gt;
&lt;li data-uuid=&quot;771df655-5105-43c2-8760-c817573b0937&quot;&gt;각 Router의 LSDB는 어떻게 바뀌는가?&lt;/li&gt;
&lt;li data-uuid=&quot;884e0772-fcba-4825-beb5-d2255f0575ad&quot;&gt;R1의 SPF 결과는 어느 경로로 바뀌는가?&lt;/li&gt;
&lt;li data-uuid=&quot;1b60d855-69f0-4543-a4b3-a01ff1d19845&quot;&gt;FIB가 갱신되기 전까지 어떤 Packet Loss가 생길 수 있는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 Kernel Routing Table의 경로 출처와 Next Hop을 관찰한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -4 route show
ip route get 203.0.113.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FRRouting이 이미 구성된 개인 실습 환경이라면 읽기 명령으로 Control Plane 상태를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sml&quot;&gt;&lt;code&gt;sudo vtysh -c 'show ip ospf neighbor'
sudo vtysh -c 'show ip ospf database'
sudo vtysh -c 'show ip route'
sudo vtysh -c 'show bgp summary'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 명령은 다음 질문에 답한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;OSPF Neighbor : 이웃 관계가 형성되었는가?
OSPF Database : Link State 정보가 동기화되었는가?
IP Route      : Best Route가 선택되었는가?
BGP Summary   : Peer Session과 받은 Prefix 상태는 어떤가?
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FRRouting이 설치&amp;middot;구성되지 않은 일반 호스트에서는 명령이 없거나 결과가 비어 있는 것이 정상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰을 위해 OSPF/BGP Timer, Route Redistribution, BGP Policy를 운영 장비에서 변경하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광범위한 경로 장애를 만들 수 있으므로 읽기 명령으로만 상태를 확인한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-uuid=&quot;a892afa2-9164-443a-8494-d075db0ddd25&quot;&gt;Static Route는 단순하고 예측 가능하지만 변화가 생겨도 자동으로 갱신되지 않는다.&lt;/li&gt;
&lt;li data-uuid=&quot;991c2487-107a-4693-a31b-bd23bcaf45ac&quot;&gt;Dynamic Routing Protocol은 Control Plane에서 경로 정보를 교환하고 RIB/FIB를 갱신한다.&lt;/li&gt;
&lt;li data-uuid=&quot;8b74a2fb-b8ed-4849-91a2-edaf88f75173&quot;&gt;장애 감지부터 새 경로가 안정적으로 사용되기까지의 과정을 Convergence라고 한다.&lt;/li&gt;
&lt;li data-uuid=&quot;1864893c-2290-4535-97f7-bc5a157a5533&quot;&gt;OSPF는 IGP이며 Hello, Adjacency, LSA, LSDB, SPF를 통해 Link State 경로를 계산한다.&lt;/li&gt;
&lt;li data-uuid=&quot;965513b5-601b-490a-8952-8423f5eb98ca&quot;&gt;OSPF Area는 Link State 정보와 계산 범위를 계층화하며 Area 0이 Backbone 역할을 한다.&lt;/li&gt;
&lt;li data-uuid=&quot;d1a4253f-50bb-4ce6-ac34-b3fbfc25a4ab&quot;&gt;BGP는 Prefix와 AS_PATH, NEXT_HOP 같은 Path Attribute를 교환하는 정책 기반 Path Vector Protocol이다.&lt;/li&gt;
&lt;li data-uuid=&quot;83382eaa-b586-4ca7-9c8a-ea139d8b00dd&quot;&gt;BGP Established는 메시지 채널이 준비되었다는 뜻이며 원하는 Route의 광고&amp;middot;선택&amp;middot;설치를 보장하지 않는다.&lt;/li&gt;
&lt;li data-uuid=&quot;c42c60e9-aaaa-4a42-ae7c-34eace3a6719&quot;&gt;eBGP는 AS 사이에, iBGP는 같은 AS 안에서 BGP Route를 교환한다.&lt;/li&gt;
&lt;li data-uuid=&quot;ff530ee3-2cab-4a78-ac95-cef902ecce42&quot;&gt;빠른 장애 감지와 안정적인 Control Plane 사이에는 균형이 필요하다.&lt;/li&gt;
&lt;li data-uuid=&quot;c0c951c3-c456-4ae0-b970-ae15d251671f&quot;&gt;Dynamic Route, Static Route, Default Route는 설계 목적에 따라 함께 사용될 수 있다.&lt;/li&gt;
&lt;li data-uuid=&quot;47541b62-4bfd-4ba1-97c3-8525c14fc8eb&quot;&gt;Route Redistribution은 유용하지만 출처와 Loop를 통제할 명확한 정책이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Static Routing, Dynamic Routing, Control Plane, Data Plane, Convergence, IGP, OSPF, Hello, Adjacency, LSA, LSDB, SPF, Area 0, AS, ASN, BGP, NLRI, AS_PATH, Path Attribute, eBGP, iBGP, Route Flapping, Route Redistribution&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅은 정답 지도를 한 번 배포하는 기술이 아니다. 계속 변하는 연결 상태를 공유하고 각 라우터가 다시 길을 계산하게 하는 약속이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 편지는 같은 동네의 MAC 주소를 찾고, Gateway를 지나, 여러 Router가 계산한 경로를 따라 원격 네트워크까지 갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사용자는 웹사이트에 접속할 때 203.0.113.10 같은 숫자 주소를 입력하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://example.com&quot;&gt;example.com&lt;/a&gt;이라는 이름만 알고도 실제 IP 주소를 어떻게 찾을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;우리는 왜 숫자 대신 이름으로 접속할까?&lt;/b&gt;」라는 질문을 통해 DNS Stub Resolver, Recursive Resolver, Root&amp;middot;TLD&amp;middot;Authoritative Name Server, Record와 Cache TTL을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1090</guid>
      <comments>https://peterica.tistory.com/1090#entry1090comment</comments>
      <pubDate>Thu, 16 Jul 2026 20:12:07 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 11. 길을 잃은 편지가 영원히 돌면 어떻게 될까?</title>
      <link>https://peterica.tistory.com/1089</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/celbk6/dJMcaasxPGw/n3NpSS4FlA2f3nKFRaKHfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/celbk6/dJMcaasxPGw/n3NpSS4FlA2f3nKFRaKHfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/celbk6/dJMcaasxPGw/n3NpSS4FlA2f3nKFRaKHfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcelbk6%2FdJMcaasxPGw%2Fn3NpSS4FlA2f3nKFRaKHfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 3. 수많은 길 가운데 다음 길을 고르는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로가 없으면 라우터는 패킷을 더 이상 전달할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 잘못된 경로가 있으면 패킷은 멈추지 않고 엉뚱한 방향으로 갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 목적지로 가려면 R2에게 보내야 한다고 생각하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2는 같은 목적지로 가려면 R1에게 보내야 한다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Destination Network: 10.30.0.0/24

R1 Route: 10.30.0.0/24 via R2
R2 Route: 10.30.0.0/24 via R1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷은 두 라우터 사이를 오간다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;R1 &amp;rarr; R2 &amp;rarr; R1 &amp;rarr; R2 &amp;rarr; R1 &amp;rarr; ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Packet에는 요청이 지나온 경로 목록이 없으므로 라우터는 &amp;ldquo;아까 본 패킷&amp;rdquo;이라는 이유만으로 전달을 멈추지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 패킷은 영원히 네트워크 자원을 점유할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Header에는 &lt;b&gt;TTL&lt;/b&gt;(Time To Live)이라는 안전장치가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터를 지날 때마다 값이 줄고, 더 이상 전달할 수 없는 순간에는 &lt;b&gt;ICMP Time Exceeded&lt;/b&gt;가 패킷의 수명이 끝났음을 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Routing Loop&lt;/b&gt;, &lt;b&gt;TTL&lt;/b&gt;, &lt;b&gt;Hop Limit&lt;/b&gt;, &lt;b&gt;ICMP Time Exceeded&lt;/b&gt;를 살펴보고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;가 이 안전장치를 이용해 보이지 않는 라우터를 발견하는 원리를 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 우체국이 서로를 다음 배송지로 가리킨다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민수의 편지가 제주도로 가야 한다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서울 우체국의 안내표에는 다음과 같이 적혀 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;제주도 편지 &amp;rarr; 대전 우체국으로&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 대전 우체국의 안내표에는 반대로 적혀 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;제주도 편지 &amp;rarr; 서울 우체국으로&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서울은 대전으로 보내고 대전은 다시 서울로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 우체국 모두 자신의 안내표를 정상적으로 따르고 있지만 편지는 제주도에 가까워지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서도 각 라우터의 Route Entry가 서로를 Next Hop으로 가리키면 &lt;b&gt;Routing Loop&lt;/b&gt;가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 토폴로지를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client ── R1 ── R2
          &amp;uarr;     │
          └─────┘

목적지: 10.30.0.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1과 R2가 가진 경로가 다음과 같다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R1: 10.30.0.0/24 via R2
R2: 10.30.0.0/24 via R1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 Routing Table Lookup 결과에 따라 R2로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2도 같은 목적지 주소를 보고 자신의 테이블에 따라 R1로 돌려보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 결정만 보면 규칙에 맞지만 두 결정을 연결하면 원이 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ L2 Loop와 Routing Loop는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 스위치 사이의 L2 Loop도 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 루프는 경로가 원을 만든다는 점은 같지만 영향을 만드는 방식이 다르다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;L2 Loop&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;L3 Routing Loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;전달 단위&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Ethernet Frame&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IP Packet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;대표 원인&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;스위치 사이의 순환 링크&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;라우터의 잘못되거나 수렴 중인 경로 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;수명 제한&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;일반 Frame에 스위치 홉 TTL 없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv4 TTL 또는 IPv6 Hop Limit 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;대표 증상&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Broadcast Storm, MAC Flapping&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;반복되는 Router Hop, TTL Exceeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;대표 방지&amp;middot;완화&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;STP/RSTP로 논리적 루프 제거&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;경로 설계&amp;middot;프로토콜 루프 방지와 TTL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;L2 Loop에서는 브로드캐스트 프레임이 여러 포트로 복제되며 급격히 늘어날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop에서는 일반적으로 하나의 IP Packet이 라우터 사이를 반복해 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라우터가 링크에 맞는 새 L2 Frame을 만들지만 안쪽 IP Packet의 목적지는 그대로다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 있으므로 한 패킷이 영원히 돌지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 새로운 패킷이 계속 들어오면 각 패킷은 TTL이 소진될 때까지 반복 전달되므로 링크와 라우터 자원을 계속 낭비할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 Routing Loop의 원인을 고치는 기능이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;루프가 생겼을 때 한 패킷의 피해를 유한하게 만드는 최후의 안전장치&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TTL은 시간일까, 횟수일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL의 이름은 &lt;code&gt;Time To Live&lt;/code&gt;, 즉 살아 있을 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적인 IP Forwarding에서 관찰하는 핵심 동작은 라우터를 통과할 때마다 값이 최소 1씩 감소하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Source에서 TTL 64로 전송
Router 1 통과 후 TTL 63
Router 2 통과 후 TTL 62
Router 3 통과 후 TTL 61&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실질적으로 패킷이 통과할 수 있는 Router Hop의 수를 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 TTL 필드는 8비트이므로 0부터 255까지 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;송신 운영체제가 초기값을 정하며 흔히 64, 128, 255 같은 값을 볼 수 있지만 시스템과 프로토콜에 따라 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 TTL을 보고 운영체제를 추측하는 기법도 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 장비와 설정이 값을 바꿀 수 있으므로 확정적인 식별 방법은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 같은 목적의 필드를 &lt;b&gt;Hop Limit&lt;/b&gt;이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 실제 동작을 더 직접적으로 표현한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IPv4 : TTL
IPv6 : Hop Limit&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 라우터는 TTL을 언제 줄일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터가 IPv4 Packet을 전달할 때의 흐름을 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Ethernet Frame을 수신한다.
2. 안쪽 IPv4 Packet을 확인한다.
3. TTL을 감소시킨다.
4. TTL이 유효한지 확인한다.
5. Routing Table을 조회하고 다음 Link로 전달한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 감소해 0이 되면 라우터는 패킷을 다음 홉으로 전달하지 않고 폐기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 IPv4 Header의 일부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 바뀌면 IPv4 Header Checksum도 갱신해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Payload의 내용이나 Destination IP가 TTL 감소 때문에 바뀌는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;바뀌는 값: TTL, IPv4 Header Checksum, 다음 링크의 L2 Header
유지되는 값: 일반 라우팅에서 Source IP, Destination IP, Payload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6 기본 Header에는 IPv4와 같은 Header Checksum이 없으므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hop Limit 감소 때문에 IP Header Checksum을 다시 계산하는 과정은 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TTL 4인 패킷이 루프에 들어가면 어떻게 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 초기 TTL 4로 패킷을 보냈다고 하자.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Client &amp;rarr; R1 &amp;rarr; R2 &amp;rarr; R1 &amp;rarr; R2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라우터에서 보이는 값을 따라가 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;순서&lt;/td&gt;
&lt;td&gt;패킷을 받은 라우터&lt;/td&gt;
&lt;td&gt;수신 시 TTL&lt;/td&gt;
&lt;td&gt;전달 시 TTL&lt;/td&gt;
&lt;td&gt;결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;R1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;R2로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;R2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;R1로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;R1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;R2로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;R2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;폐기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷은 목적지에 도착하지 못하지만 네 번째 라우터 처리에서 수명이 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 TTL이 64라면 더 오래 순환하고, 255라면 더 많은 라우터 처리를 거칠 수 있다. 그래도 한 패킷의 순환 횟수는 유한하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 수명이 끝난 사실을 Source에게 알리기 위해 ICMP 메시지를 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 수명이 끝난 편지는 어떤 답장을 보낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Router는 Forwarding 중 TTL이 만료된 패킷을 폐기하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 &lt;b&gt;ICMP Time Exceeded&lt;/b&gt; 메시지를 Source로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 ICMP에서는 다음 값으로 표현한다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;Type 11 : Time Exceeded
Code 0  : Time to Live exceeded in transit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 메시지에는 문제가 발생한 원래 IP Header와 상위 계층을 식별하는 데 필요한 원래 패킷의 일부가 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 어떤 전송에 대한 오류인지 연결할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Router &amp;rarr; Source
ICMP Time Exceeded
&amp;ldquo;이 패킷은 전달 중 TTL이 끝났다.&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ICMP 메시지도 새로운 IP Packet이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source로 돌아가는 Route가 없거나 Firewall이 ICMP를 차단하면 Source는 Time Exceeded를 받지 못할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 원래 패킷을 폐기하는 안전장치로는 동작하지만, 오류 메시지가 반드시 송신자에게 보인다고 보장하지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv6에서는 &lt;b&gt;ICMPv6 Time Exceeded&lt;/b&gt;가 Hop Limit 만료를 알린다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute는 일부러 수명이 짧은 패킷을 보낸다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 TTL 만료를 장애가 아니라 관찰 도구로 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 TTL 1인 Probe Packet을 보낸다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;TTL 1
Source &amp;rarr; R1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1이 TTL을 0으로 만들고 ICMP Time Exceeded를 돌려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Source는 첫 번째 Hop이 R1이라는 단서를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 TTL 2인 Probe를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;TTL 2
Source &amp;rarr; R1 &amp;rarr; R2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1을 지나 TTL 1이 되고 R2에서 0이 된다. R2가 ICMP Time Exceeded를 보내면 두 번째 Hop을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 TTL 3, 4, 5로 늘려 반복한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 1 &amp;rarr; 첫 번째 Router 응답
TTL 2 &amp;rarr; 두 번째 Router 응답
TTL 3 &amp;rarr; 세 번째 Router 응답
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지에 도착하면 Probe 방식에 따라 다른 종료 응답을 받는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적인 UDP Probe는 닫힌 높은 Port에 도착해 ICMP Port Unreachable을 받을 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP Echo Probe는 Echo Reply를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;TCP Probe는 SYN-ACK 또는 RST 같은 TCP 응답을 받을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제와 &lt;code&gt;traceroute&lt;/code&gt; 구현, 옵션에 따라 사용하는 Probe Protocol은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 라우터가 가진 전체 지도를 읽어 오는 명령이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서로 다른 TTL의 Probe를 보내고 각 수명이 끝난 위치에서 돌아온 응답을 모아 경로를 추론하는 명령&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Routing Loop는 traceroute에서 어떻게 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1과 R2 사이에 Loop가 있으면 Hop 목록에서 같은 주소가 반복될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1  192.0.2.1   R1
2  192.0.2.2   R2
3  192.0.2.1   R1
4  192.0.2.2   R2
5  192.0.2.1   R1
6  192.0.2.2   R2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL을 늘린 Probe가 같은 두 라우터에서 번갈아 만료되므로 반복 패턴이 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 주소 반복만으로 모든 Routing Loop를 완벽히 증명할 수 있는 것은 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ECMP 때문에 Probe마다 다른 경로를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;라우터가 ICMP 응답에 사용하는 Source IP가 예상과 다를 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP Rate Limiting 때문에 일부 Hop이 &lt;code&gt;*&lt;/code&gt;로 보일 수 있다.&lt;/li&gt;
&lt;li&gt;Firewall이 특정 Probe 또는 응답을 차단할 수 있다.&lt;/li&gt;
&lt;li&gt;Forward Path와 ICMP Return Path가 서로 다를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;의 반복 패턴은 강한 단서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 각 라우터의 Route Lookup과 인터페이스 캡처를 함께 확인해야 원인을 확정할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TTL을 크게 늘리면 통신이 될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop에서 TTL이 만료되므로 TTL을 크게 설정하면 목적지에 도착할 것처럼 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 경로 정보가 서로를 가리키는 한 패킷은 목적지 방향으로 나아가지 않는다. TTL을 늘리면 더 오래 순환할 뿐이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;TTL 64  &amp;rarr; Loop를 64 Hop 이내에서 종료
TTL 255 &amp;rarr; 더 오래 순환한 뒤 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL을 높이는 것은 Routing Loop의 해결책이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고쳐야 하는 것은 잘못된 Route Entry다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;잘못된 상태
R1: 10.30.0.0/24 via R2
R2: 10.30.0.0/24 via R1

해결 방향 예시
R2: 10.30.0.0/24를 실제 목적지 Interface 또는 올바른 Next Hop으로
R1: R2를 통해 정상 경로 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모르는 목적지를 서로의 Default Route로 넘기는 구조도 Loop를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로를 삭제하는 것과 Blackhole 또는 Unreachable Route로 명시적으로 종료하는 것 가운데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 방법이 적절한지는 설계 목적에 따라 달라진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Routing Loop는 왜 생길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop는 여러 상황에서 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 잘못된 Static Route&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 라우터가 특정 Prefix 또는 Default Route를 서로에게 향하도록 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 경로가 바뀌는 동안의 정보 불일치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크 장애 뒤 일부 라우터는 새 상태를 알고 다른 라우터는 이전 상태를 유지할 수 있다. 네트워크가 아직 &lt;b&gt;Convergence&lt;/b&gt;하지 않은 순간에 임시 Loop가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Route Redistribution과 Summary 설계 오류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Routing Protocol 사이에 경로를 재배포하거나 넓은 Summary Route를 만들 때 경로의 출처와 실제 도달성을 잘못 표현하면 패킷이 되돌아올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 잘못된 Policy Routing&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 Destination Route와 별도로 Source, Mark, Interface에 따른 정책이 서로 충돌해 예상하지 못한 순환을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅 프로토콜은 Loop를 줄이기 위한 여러 장치를 갖는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Distance Vector 계열의 Split Horizon과 Route Poisoning&lt;/li&gt;
&lt;li&gt;Link State 계열의 공통 Topology Database와 SPF 계산&lt;/li&gt;
&lt;li&gt;BGP의 AS_PATH를 이용한 Autonomous System Loop 탐지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 잘못된 설정과 정책까지 모든 Loop를 자동으로 막아 주는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장치들은 다음 글의 동적 라우팅에서 더 자세히 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;두 우체국이 서로를 다음 배송지로 가리킴&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Routing Loop&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Route Entry가 순환하는 Next Hop을 만드는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;봉투에 적힌 남은 중계 횟수&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;TTL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv4 Packet이 통과할 수 있는 Router Hop을 제한하는 필드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv6 봉투의 남은 중계 횟수&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Hop Limit&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv6에서 TTL과 같은 역할을 하는 필드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;횟수가 끝난 편지를 폐기&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;TTL Expiration&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TTL 감소 결과가 0이 되어 Forwarding을 중단하는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;수명이 끝났다는 반송 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ICMP Time Exceeded&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TTL 만료를 Source에 알리는 제어 메시지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;짧은 수명의 편지를 차례로 보냄&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;traceroute&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;TTL을 증가시키며 각 Hop의 응답을 수집하는 경로 추론&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모든 우체국이 같은 새 지도를 갖게 됨&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Convergence&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;경로 변화 후 Routing 정보가 안정된 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop 안에서 한 Packet이 끝나는 과정은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Source가 초기 TTL을 설정해 Packet을 보낸다.
2. 각 Router는 Forwarding 전에 TTL을 감소시킨다.
3. 잘못된 Route 때문에 Packet이 같은 Router들을 반복한다.
4. TTL이 0이 된 Router가 Packet을 폐기한다.
5. 가능하면 ICMP Time Exceeded를 Source로 보낸다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 일반적인 경로를 숫자 주소로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;traceroute -n 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 Hop 수를 작게 제한해 관찰할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;traceroute -n -m 5 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 &lt;code&gt;tracepath&lt;/code&gt;가 설치되어 있다면 권한 없이 경로와 PMTU 정보를 함께 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;tracepath -n 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux &lt;code&gt;ping&lt;/code&gt; 구현에서는 TTL을 작게 설정한 Echo Request를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping -c 1 -t 1 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 Router가 ICMP Time Exceeded를 보내고 Return Path와 정책이 허용한다면 응답을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS &lt;code&gt;ping&lt;/code&gt;에서는 TTL 지정 옵션이 다를 수 있으므로 해당 시스템의 &lt;code&gt;man ping&lt;/code&gt;을 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP 메시지와 IP TTL을 패킷 수준에서 보려면 다음처럼 캡처할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;sudo tcpdump -n -vv -i eth0 icmp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 이름과 권한은 환경에 맞게 조정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공용 목적지에 대한 경로는 네트워크 정책, VPN, 방화벽, ECMP에 따라 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 Hop이나 &lt;code&gt;*&lt;/code&gt;를 발견해도 운영 환경의 Routing Loop라고 즉시 단정하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도적으로 잘못된 Route를 추가해 Loop를 만드는 실험은 공유 네트워크와 운영 장비에 영향을 줄 수 있다. 격리된 개인 실습 환경이 아니라면 읽기와 패킷 관찰만 수행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Routing Loop는 여러 Router의 Route Entry가 Packet을 순환시키는 상태다.&lt;/li&gt;
&lt;li&gt;L2 Loop의 일반 Ethernet Frame과 달리 IP Packet에는 TTL 또는 Hop Limit이 있다.&lt;/li&gt;
&lt;li&gt;IPv4 TTL은 Router를 통과할 때마다 감소하며 0이 되면 Packet을 폐기한다.&lt;/li&gt;
&lt;li&gt;TTL 변경 시 IPv4 Header Checksum도 갱신된다.&lt;/li&gt;
&lt;li&gt;Router는 TTL 만료를 ICMP Type 11 Code 0 Time Exceeded로 알릴 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP 응답도 Return Route와 정책이 필요하므로 Source에 반드시 도착하는 것은 아니다.&lt;/li&gt;
&lt;li&gt;traceroute는 TTL을 1부터 늘린 Probe의 만료 응답을 모아 Hop을 추론한다.&lt;/li&gt;
&lt;li&gt;TTL을 크게 설정해도 잘못된 Route는 고쳐지지 않고 Packet이 더 오래 순환할 뿐이다.&lt;/li&gt;
&lt;li&gt;동적 Routing Protocol은 여러 Loop 방지 장치를 갖지만 잘못된 정책과 설정까지 모두 막지는 못한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Routing Loop, TTL, Hop Limit, TTL Expiration, ICMP Time Exceeded, ICMP Type 11 Code 0, traceroute, Router Hop, Convergence, Split Horizon, Route Poisoning, AS_PATH&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL은 패킷을 목적지로 안내하지 않는다. 잘못된 안내가 네트워크 자원을 영원히 점유하지 못하게 끝을 정한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Static Route만 사용하는 작은 네트워크에서는 관리자가 잘못된 경로를 직접 찾아 고칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 수백, 수천 개의 라우터가 연결된 네트워크에서 링크가 바뀔 때마다 모든 Routing Table을 사람이 수정할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터들은 새로운 길과 사라진 길을 어떻게 서로 알릴까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;길이 바뀌면 지도는 어떻게 새로 그려질까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Static Routing과 Dynamic Routing의 차이, OSPF의 Link State, BGP의 Path Vector와 Convergence를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>icmp</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>TraceRoute</category>
      <category>TTL</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1089</guid>
      <comments>https://peterica.tistory.com/1089#entry1089comment</comments>
      <pubDate>Thu, 16 Jul 2026 20:11:40 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 10. 가는 길이 있으면 돌아오는 길도 있는 것일까?</title>
      <link>https://peterica.tistory.com/1088</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7o36p/dJMcahypkax/dE7IdwfQPZx8HaCQTzuOa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7o36p/dJMcahypkax/dE7IdwfQPZx8HaCQTzuOa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7o36p/dJMcahypkax/dE7IdwfQPZx8HaCQTzuOa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7o36p%2FdJMcahypkax%2FdE7IdwfQPZx8HaCQTzuOa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 3. 수많은 길 가운데 다음 길을 고르는 법&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 Destination IP와 Routing Table을 비교해 다음 경로를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 라우터가 내린 결정은 다음 라우터에 전달되는 여행 계획이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라우터는 자신에게 도착한 패킷의 목적지를 보고 독립적으로 다음 홉을 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 패킷이 서버에 도착했다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client ── R1 ── R2 ── Server
          요청 도착 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서버가 응답하면 요청이 지나온 길을 거꾸로 돌아갈 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 IP 패킷에는 &amp;ldquo;지나온 라우터 목록&amp;rdquo;이 일반적으로 기록되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 패킷은 Client의 IP 주소를 새로운 Destination으로 삼고 다시 Routing Table Lookup을 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가는 길과 오는 길은 서로 다른 두 번의 결정이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 도착했다는 사실은 응답이 돌아갈 경로까지 존재한다는 뜻이 아니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Forward Path&lt;/b&gt;, &lt;b&gt;Return Path&lt;/b&gt;, &lt;b&gt;Missing Route&lt;/b&gt;, &lt;b&gt;Asymmetric Routing&lt;/b&gt;을 살펴보고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateless한 IP 전달에서는 가능한 비대칭 경로가 Stateful Firewall과 NAT를 만나면 왜 장애가 될 수 있는지 알아본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 편지에는 지나온 우체국 목록이 적히지 않는다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민수가 다른 지역의 영희에게 편지를 보냈다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편지는 여러 우체국을 거쳐 영희에게 도착한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;민수 &amp;rarr; 우체국 A &amp;rarr; 우체국 B &amp;rarr; 영희&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영희가 답장을 쓸 때 원래 봉투를 뒤집어 같은 길로 보내는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 봉투의 받는 사람에 민수의 주소를 적고 자신의 동네 우체국에 맡긴다. 답장을 받은 우체국들은 민수의 주소를 기준으로 각자의 배송표를 조회한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;영희 &amp;rarr; 우체국 C &amp;rarr; 우체국 D &amp;rarr; 민수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청과 답장의 경로가 같을 수도 있고 다를 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Routing도 같은 원리다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client에서 Server로 가는 경로를 &lt;b&gt;Forward Path&lt;/b&gt;라고 한다.&lt;/li&gt;
&lt;li&gt;Server에서 Client로 돌아오는 경로를 &lt;b&gt;Return Path&lt;/b&gt;라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 경로는 각 방향의 &lt;b&gt;Destination IP&lt;/b&gt;와 &lt;b&gt;Routing Table&lt;/b&gt;에 의해 별도로 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP가 연결 상태를 관리하더라도 라우터가 요청 경로를 기억해 응답을 되돌려 주는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP의 상태와 IP Routing의 경로 선택은 서로 다른 계층의 문제다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 요청은 도착했는데 응답은 길을 잃을 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 네트워크를 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client LAN              Transit              Server LAN
10.1.0.0/24          192.0.2.0/30           10.2.0.0/24

Client ── R1 ───────────────── R2 ── Server
10.1.0.10  .1   .1             .2   .1  10.2.0.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소를 조금 더 명확히 적으면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client       : 10.1.0.10/24, Gateway 10.1.0.1
R1 Client IF : 10.1.0.1/24
R1 Transit IF: 192.0.2.1/30
R2 Transit IF: 192.0.2.2/30
R2 Server IF : 10.2.0.1/24
Server       : 10.2.0.20/24, Gateway 10.2.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1에는 Server LAN으로 가는 경로가 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R1: 10.2.0.0/24 via 192.0.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Path는 성립한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client 10.1.0.10
  &amp;rarr; Gateway R1
  &amp;rarr; R2
  &amp;rarr; Server 10.2.0.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 요청을 받고 응답을 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Source IP      : 10.2.0.20
Destination IP : 10.1.0.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 다른 네트워크에 있으므로 Server는 응답을 Gateway R2에 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 R2에 &lt;code&gt;10.1.0.0/24&lt;/code&gt;로 가는 경로가 없다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;R2 Routing Table
├─ 10.2.0.0/24 dev server-if
├─ 192.0.2.0/30 dev transit-if
└─ 10.1.0.0/24 경로 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route도 없다면 R2는 응답을 전달할 수 없다. Return Path가 끊어진 것이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Forward Path : Client &amp;rarr; R1 &amp;rarr; R2 &amp;rarr; Server  성공
Return Path  : Server &amp;rarr; R2 &amp;rarr; ?             실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 네트워크 로그나 패킷 캡처에는 요청 패킷이 들어온 흔적이 남을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Client는 응답을 받지 못해 타임아웃을 경험한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TCP에서는 어떤 모습으로 보일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 Server의 TCP Port에 접속한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 SYN은 Forward Path를 따라 Server에 도착한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server는 SYN을 받고 SYN-ACK을 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Server &amp;rarr; Client : SYN-ACK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Return Path가 없으면 SYN-ACK은 Client에 도착하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client는 서버가 SYN을 받았는지 알 수 없으므로 일정 시간 뒤 SYN을 재전송한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client &amp;rarr; Server : SYN
Client &amp;rarr; Server : SYN 재전송
Client &amp;rarr; Server : SYN 재전송
...
Client           : Connection timeout&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰 위치에 따라 서로 다른 장면이 보인다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 62.3256%; height: 165px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;관찰 위치&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;보이는 패킷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Client&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;반복되는 SYN, SYN-ACK 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Server&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN 수신, SYN-ACK 전송 반복&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;R1의 Client 방향&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN 통과, SYN-ACK 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;R2의 Server 방향&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;SYN과 SYN-ACK 모두 보일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 캡처만 보면 서버나 방화벽이 SYN을 버린 것처럼 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 캡처를 함께 보면 요청이 도착했고 응답도 생성되었다는 사실을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 지점의 증거를 맞춰야 Return Path 문제로 범위를 좁힐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP처럼 연결 설정과 재전송을 프로토콜이 보장하지 않는 통신은 단순히 응답 없는 타임아웃으로 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICMP Echo Request가 도착해도 Echo Reply의 Return Path가 없으면 &lt;code&gt;ping&lt;/code&gt;은 실패한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ping&lt;/code&gt; 성공 여부는 단방향 도달이 아니라 왕복이 모두 성립했는지를 보여준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 경로가 없으면 항상 오류를 돌려줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2가 목적지 경로를 찾지 못하면 ICMP Destination Unreachable 메시지를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 오류 메시지도 IP 패킷이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에게 도착하려면 R2에서 &lt;code&gt;10.1.0.10&lt;/code&gt;으로 가는 경로가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 원인이 Return Route 부재라면 오류조차 Client에게 돌아가지 못할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽이 ICMP를 차단하거나 장비가 오류 생성을 제한하는 경우도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Client에서는 명확한 &amp;ldquo;경로 없음&amp;rdquo; 오류 대신 &lt;b&gt;조용한 Timeout만 보일 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크가 실패 이유를 알고 있어도, 그 이유를 알려 줄 돌아오는 길이 없으면 사용자는 침묵만 보게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왕복 경로가 꼭 같아야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Path와 Return Path가 다른 것을 &lt;b&gt;Asymmetric Routing&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Forward Path : Client &amp;rarr; R1 &amp;rarr; R3 &amp;rarr; Server
Return Path  : Server &amp;rarr; R2 &amp;rarr; R4 &amp;rarr; Client&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭 경로 자체는 IP Routing에서 잘못이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에서는 각 네트워크의 정책과 연결 상태가 다르므로 요청과 응답이 서로 다른 사업자와 라우터를 지날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 조건을 만족하면 비대칭 경로에서도 통신할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Forward Path의 모든 라우터가 Server 방향 경로를 안다.&lt;/li&gt;
&lt;li&gt;Return Path의 모든 라우터가 Client 방향 경로를 안다.&lt;/li&gt;
&lt;li&gt;양쪽 경로의 MTU와 정책이 트래픽을 허용한다.&lt;/li&gt;
&lt;li&gt;중간 장비가 반대 방향 트래픽을 반드시 같은 장비에서 보도록 요구하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 두 상황을 구분해야 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Asymmetric Routing + 양방향 경로와 정책 정상 &amp;rarr; 통신 가능
Asymmetric Routing + Return Route/State 누락 &amp;rarr; 통신 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 경로가 다르다는 사실이 아니라, 돌아오는 경로가 없거나 중간 장비의 상태와 맞지 않는다는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Stateful Firewall은 왜 양방향을 함께 보려 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateless Router는 각 패킷의 Destination IP를 보고 독립적으로 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;Stateful Firewall&lt;/b&gt;은 요청과 응답을 하나의 연결로 묶어 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client의 SYN이 Firewall A를 지나면 A는 다음과 같은 상태를 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client IP:Port &amp;rarr; Server IP:Port, TCP SYN 관찰&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SYN-ACK이 같은 Firewall A로 돌아오면 기존 연결에 대한 정상 응답으로 허용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Return Path가 Firewall B를 지나고 A와 B가 상태를 공유하지 않는다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B는 SYN-ACK에 대응하는 요청을 본 적이 없다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Forward : Client &amp;rarr; Firewall A &amp;rarr; Server  상태는 A에 생성
Return  : Server &amp;rarr; Firewall B &amp;rarr; Client  B에는 상태 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall B는 이 패킷을 비정상적인 새 연결이나 Invalid State로 판단해 차단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로는 양쪽 모두 존재하지만 Stateful Policy 때문에 통신이 실패한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 방화벽은 상태 동기화를 사용하기도 하지만 동기화 범위와 장애 전환 동작은 제품과 구성에 따라 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ NAT를 지나간 응답은 왜 같은 번역소로 돌아와야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT도 요청을 보면서 주소와 포트의 변환 관계를 기억한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;내부 10.1.0.10:50000
       &amp;darr; NAT A에서 변환
공인 203.0.113.10:40000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server의 응답이 NAT A로 돌아오면 저장된 변환 정보를 사용해 원래 내부 주소로 복원할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 응답이 상태를 공유하지 않는 NAT B로 가면 B는 &lt;code&gt;203.0.113.10:40000&lt;/code&gt;을 어느 내부 연결로 되돌려야 하는지 모른다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Forward : Client &amp;rarr; NAT A &amp;rarr; Server  변환 상태는 A에 있음
Return  : Server &amp;rarr; NAT B &amp;rarr; Client  B에는 변환 상태 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 Stateful Firewall, NAT, Load Balancer가 있는 구간에서는 &lt;b&gt;Path Symmetry&lt;/b&gt;가 중요한 설계 조건이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 물리적으로 같은 모든 링크를 거쳐야 한다는 뜻은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 연결 상태를 가진 장비 또는 상태가 동기화된 장비를 Return Traffic이 지나야 한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT의 주소 변환 과정은 뒤의 Part에서 자세히 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Source Validation도 비대칭 경로를 막을 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 시스템은 패킷이 들어온 인터페이스가 Source IP로 돌아갈 최적 경로와 맞는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux의 &lt;b&gt;Reverse Path Filtering&lt;/b&gt;(rp_filter)이 대표적인 예다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄격한 검사에서는 다음과 같이 판단할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;패킷 Source IP로 돌아가는 최적 경로는 eth0
그런데 패킷은 eth1으로 들어옴
&amp;rarr; 비정상 Source로 판단해 Drop 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Source Address Spoofing을 줄이는 데 도움이 되지만 정상적인 Asymmetric Routing 환경에서는 올바른 패킷을 차단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reverse Path 검사에는 엄격한 방식과 느슨한 방식이 있으며 운영체제와 설정에 따라 동작이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비대칭 경로를 사용하는 환경에서는 라우팅 정책과 Source Validation 설정을 함께 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 현재 값을 읽을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.default.rp_filter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스별 값도 별도로 존재할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 의미를 이해하지 않고 이 값을 끄는 것은 적절한 진단 방법이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ECMP는 응답도 같은 길로 보내 줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP는 같은 비용의 여러 Next Hop 사이에 트래픽을 분산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 보통 Source/Destination IP와 Port 같은 Flow 정보를 해시해 하나의 방향에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Flow가 같은 Next Hop을 사용하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 반대 방향에서는 Source와 Destination이 서로 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비의 해시 방식과 토폴로지에 따라 Return Path가 다른 링크나 장비를 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stateless한 네트워크에서는 문제가 없을 수 있지만 특정 Stateful Middlebox를 반드시 통과해야 한다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정책 기반 라우팅, 상태 동기화, 대칭 해시 설계 같은 추가 고려가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP가 곧 비대칭 장애를 뜻하지는 않는다. 여러 경로와 상태 의존 장비가 만날 때 설계를 확인해야 한다는 뜻이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어디에서 사라졌는지 어떻게 찾을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왕복 장애는 한 지점의 &lt;code&gt;ping&lt;/code&gt;만으로 원인을 확정하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷의 여정을 여러 지점에서 나누어 관찰해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Client에서 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client가 요청을 실제로 내보내는지, 응답이 돌아오는지 본다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo tcpdump -n -i eth0 host 10.2.0.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Server에서 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 도착했는지, Server가 응답을 생성하는지 본다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo tcpdump -n -i eth0 host 10.1.0.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 각 Router에서 양쪽 인터페이스를 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청과 응답이 어느 인터페이스까지 보이는지 비교한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;sudo tcpdump -n -i any 'host 10.1.0.10 and host 10.2.0.20'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-i any&lt;/code&gt;는 어느 인터페이스에서 관찰되었는지 출력과 캡처 형식이 환경에 따라 다를 수 있다. 정확한 입출력 구분이 필요하면 각 인터페이스에서 별도로 캡처한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 양쪽에서 경로를 조회한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Client에서 Server 방향을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 10.2.0.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server 또는 Return Router에서 Client 방향을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 10.1.0.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽의 &lt;code&gt;ip route get&lt;/code&gt;만으로 왕복 경로를 증명할 수 없다. 방향마다 Destination과 Source가 달라지므로 양 끝과 관련 라우터에서 각각 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Stateful 상태를 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firewall과 NAT가 있다면 연결 상태가 어느 장비에 생성되었는지 확인한다. Linux 환경에서는 권한과 구성에 따라 &lt;code&gt;conntrack&lt;/code&gt; 도구로 상태를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo conntrack -L&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은 연결 수가 많은 운영 장비에서 출력과 부하가 클 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 환경에서는 주소와 프로토콜 필터를 사용하고 장비별 운영 절차를 따라야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ traceroute는 Return Path도 보여 줄까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 목적지 방향으로 TTL을 다르게 한 Probe를 보내고 중간 라우터가 돌려주는 ICMP 응답을 모아 경로를 추정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 표시되는 각 홉의 응답도 다시 관찰자에게 돌아와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;traceroute&lt;/code&gt; 결과는 순수하게 Forward Path만 복사해 보여 주는 기록이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Probe가 나가는 길과 ICMP 응답이 돌아오는 길, 중간 장비의 응답 정책이 함께 영향을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 홉이 &lt;code&gt;*&lt;/code&gt;로 표시된다고 그 지점에서 Forwarding이 반드시 실패했다고 단정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라우터가 응답하지 않거나 Return Path에서 응답이 사라질 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;traceroute&lt;/code&gt;는 유용한 단서지만 왕복 경로 전체를 한 번에 증명하는 도구는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;민수에서 영희로 가는 배달길&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Forward Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;요청 패킷이 Source에서 Destination으로 가는 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;영희에서 민수로 오는 답장길&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Return Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;응답 패킷이 원래 Source로 돌아오는 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;답장 주소에 대한 안내가 없음&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Missing Return Route&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;응답 목적지로 가는 Route가 없는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요청과 답장이 서로 다른 우체국을 지남&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Asymmetric Routing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;양방향 패킷이 서로 다른 네트워크 경로를 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요청을 본 경비실만 답장을 허용&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Stateful Firewall&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;연결 상태를 기준으로 응답 트래픽을 판단하는 장비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;번역표를 가진 우체국으로 답장이 돌아옴&lt;/td&gt;
&lt;td&gt;&lt;b&gt;NAT State&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;주소와 Port 변환 관계를 저장한 상태 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;발신 지역과 들어온 문을 대조&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Reverse Path Filtering&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Source IP 경로와 수신 인터페이스를 검증하는 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여러 길에 같은 흐름을 나누어 배정&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ECMP Hashing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Flow 정보를 사용해 여러 Next Hop을 선택하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왕복 통신의 확인 순서는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Forward Path에서 요청이 각 구간을 통과하는지 확인한다.
2. Destination이 요청을 받고 응답을 생성하는지 확인한다.
3. Return Path의 각 장비가 Source Network로 가는 Route를 갖는지 확인한다.
4. Firewall, NAT, Load Balancer의 State가 Return Traffic과 일치하는지 확인한다.
5. Source Validation과 정책이 비대칭 경로를 허용하는지 확인한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 경로를 복원해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 캡처 결과를 보고 패킷이 멈춘 위치를 추정해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Client capture
10.1.0.10 &amp;rarr; 10.2.0.20  ICMP Echo Request
10.1.0.10 &amp;rarr; 10.2.0.20  ICMP Echo Request

Server capture
10.1.0.10 &amp;rarr; 10.2.0.20  ICMP Echo Request
10.2.0.20 &amp;rarr; 10.1.0.10  ICMP Echo Reply

R2 transit-side capture
10.1.0.10 &amp;rarr; 10.2.0.20  ICMP Echo Request&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 수 있는 사실은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Echo Request는 Server까지 도착했다.&lt;/li&gt;
&lt;li&gt;Server는 Echo Reply를 생성했다.&lt;/li&gt;
&lt;li&gt;Reply는 R2의 Server 쪽으로 나왔지만 Transit 쪽에서는 보이지 않는다.&lt;/li&gt;
&lt;li&gt;R2의 Client Network Route, Forwarding Policy, Firewall을 우선 확인해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 증거만으로 R2의 Missing Route라고 단정할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2 내부의 Firewall이나 Source Validation이 Reply를 Drop했을 가능성도 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관찰은 범인을 바로 지목하는 일이 아니라 가능한 실패 범위를 줄이는 일이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Forward Path와 Return Path는 서로 독립적인 Routing Table Lookup 결과다.&lt;/li&gt;
&lt;li&gt;요청이 Destination에 도착해도 Return Route가 없으면 통신은 완성되지 않는다.&lt;/li&gt;
&lt;li&gt;TCP에서는 SYN이 도착하고 SYN-ACK이 돌아오지 않아 Client가 SYN을 재전송할 수 있다.&lt;/li&gt;
&lt;li&gt;ICMP 오류 메시지도 돌아올 경로가 없으면 Client에 도착하지 않는다.&lt;/li&gt;
&lt;li&gt;Asymmetric Routing 자체는 IP 네트워크에서 정상적으로 동작할 수 있다.&lt;/li&gt;
&lt;li&gt;Stateful Firewall과 NAT는 요청을 본 장비에 상태가 있으므로 Return Path가 달라지면 Drop할 수 있다.&lt;/li&gt;
&lt;li&gt;Reverse Path Filtering은 Source Spoofing을 줄이지만 정상 비대칭 경로에 영향을 줄 수 있다.&lt;/li&gt;
&lt;li&gt;양 끝과 중간 지점의 캡처 및 양방향 Route Lookup을 함께 봐야 패킷 여정을 복원할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ping&lt;/code&gt;과 &lt;code&gt;traceroute&lt;/code&gt; 한 가지 결과만으로 단방향 성공과 실패 위치를 확정할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;핵심 용어: Forward Path, Return Path, Return Route, Missing Route, Asymmetric Routing, Path Symmetry, Stateful Firewall, NAT State, Reverse Path Filtering, rp_filter, ECMP Hashing, Multi-point Capture&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통신은 요청이 도착하는 사건이 아니다. 서로 독립적으로 선택된 가는 길과 오는 길이 모두 이어지는 사건이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로가 없으면 패킷은 목적지에 도착하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 잘못된 경로가 존재하면 더 이상한 일이 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 R2를 가리키고 R2는 다시 R1을 가리키면 패킷은 두 라우터 사이를 계속 맴돈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 패킷은 이 순환에서 어떻게 빠져나올까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;길을 잃은 편지가 영원히 돌면 어떻게 될까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Loop, TTL, ICMP Time Exceeded와 traceroute가 경로를 드러내는 원리를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Asymmetric Routing</category>
      <category>Forward Path</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>Return Path</category>
      <category>Return Route</category>
      <category>Reverse Path Filtering</category>
      <category>Stateful firewall</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1088</guid>
      <comments>https://peterica.tistory.com/1088#entry1088comment</comments>
      <pubDate>Thu, 16 Jul 2026 20:10:46 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 09. 라우터는 수많은 목적지의 길을 어떻게 고를까?</title>
      <link>https://peterica.tistory.com/1087</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yuhSS/dJMcacw5ZBX/z8XX9f1tUKkP1O2UGeJjp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yuhSS/dJMcacw5ZBX/z8XX9f1tUKkP1O2UGeJjp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yuhSS/dJMcacw5ZBX/z8XX9f1tUKkP1O2UGeJjp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyuhSS%2FdJMcacw5ZBX%2Fz8XX9f1tUKkP1O2UGeJjp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 3. 수많은 길 가운데 다음 길을 고르는 법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 다른 네트워크로 가는 패킷을 Default Gateway에 맡긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 프레임에서 IP Packet을 꺼내 Destination IP를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 패킷을 어느 인터페이스와 Next Hop으로 보낼지 결정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터의 Routing Table에 다음 경로가 있다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;0.0.0.0/0       &amp;rarr; 우체국 A
10.0.0.0/8      &amp;rarr; 우체국 B
10.20.0.0/16    &amp;rarr; 우체국 C
10.20.30.0/24   &amp;rarr; 우체국 D&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;10.20.30.40&lt;/code&gt;은 네 경로와 모두 일치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어느 길을 선택해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가까운 라우터를 선택할까? 먼저 등록된 경로를 선택할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metric이 가장 작은 경로를 선택할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅에서 첫 번째 기준은 따로 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지와 가장 길게 일치하는 Prefix, 즉 가장 구체적인 경로를 선택한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Longest Prefix Match&lt;/b&gt;(LPM)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Routing Table의 Route Entry가 어떤 정보를 담는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPM이 왜 먼저 적용되는지, 같은 Prefix 후보 사이에서 경로 출처의 선호도와 Metric은 어떤 역할을 하는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 라우팅 테이블은 전체 여행 경로일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편집중국에는 목적지 지역별로 다음 배송처를 적은 표가 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;서울 강남구 &amp;rarr; 수도권 남부 집중국
서울 전체   &amp;rarr; 수도권 집중국
그 밖의 지역 &amp;rarr; 중앙 집중국&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표는 편지가 최종 목적지까지 거칠 모든 우체국을 순서대로 기록한 여행 계획이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 우체국이 &lt;b&gt;다음으로 어디에 넘길지&lt;/b&gt;를 결정하는 표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Table도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Route Entry에는 다음과 같은 정보가 들어 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 133px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;정보&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Destination Prefix&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;이 경로가 적용되는 목적지 주소 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Next Hop&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;패킷을 다음으로 전달할 라우터 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Outgoing Interface&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;패킷을 내보낼 인터페이스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Route Source&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Connected, Static, OSPF, BGP 등 경로를 알게 된 출처&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Preference / Distance&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 Prefix의 여러 출처 중 어떤 경로를 신뢰할지 판단하는 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Metric&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한 출처 또는 프로토콜 안에서 더 좋은 경로를 비교하는 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 운영체제와 라우터가 이 정보를 같은 이름과 형식으로 보여 주는 것은 아니다. 하지만 핵심 질문은 같다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;이 목적지 범위의 패킷을 어느 인터페이스와 Next Hop으로 보낼 것인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 패킷마다 Destination IP를 이 표와 비교한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 하나의 주소가 여러 구역에 포함될 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;10.20.30.40&lt;/code&gt;이라는 목적지를 구역별로 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0.0.0.0/0       : 모든 IPv4 주소                &amp;rarr; 일치
10.0.0.0/8      : 10으로 시작하는 주소          &amp;rarr; 일치
10.20.0.0/16    : 10.20으로 시작하는 주소       &amp;rarr; 일치
10.20.30.0/24   : 10.20.30으로 시작하는 주소    &amp;rarr; 일치
10.20.30.40/32  : 정확히 10.20.30.40 하나       &amp;rarr; 일치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 배송 구역 안에 더 작은 구역이 겹쳐 있는 모습이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;모든 지역 /0
└─ 10 지역 /8
   └─ 10.20 지역 /16
      └─ 10.20.30 지역 /24
         └─ 10.20.30.40 한 주소 /32&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 주소는 이 모든 범위에 포함될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;/32&lt;/code&gt;가 가장 길게 일치하고 가장 구체적이다. &lt;code&gt;/24&lt;/code&gt;, &lt;code&gt;/16&lt;/code&gt;, &lt;code&gt;/8&lt;/code&gt;, &lt;code&gt;/0&lt;/code&gt; 순으로 범위가 넓어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Longest Prefix Match는 일치하는 Route Entry 가운데 Prefix Length가 가장 긴 경로를 선택한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 45.1163%; height: 182px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;목적지&lt;/td&gt;
&lt;td&gt;선택되는 가장 구체적인 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.20.30.40&lt;/td&gt;
&lt;td&gt;10.20.30.40/32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.20.30.50&lt;/td&gt;
&lt;td&gt;10.20.30.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.20.40.50&lt;/td&gt;
&lt;td&gt;10.20.0.0/16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.30.40.50&lt;/td&gt;
&lt;td&gt;10.0.0.0/8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;192.0.2.10&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 테이블의 첫 줄부터 읽고 처음 일치한 경로를 고르는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 내부에서는 효율적인 검색 구조를 사용할 수 있지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 보이는 선택 결과는 가장 긴 Prefix Match를 따라야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 더 구체적인 안내가 먼저인 이유는 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 안내판에 다음 두 문장이 있다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;서울로 가는 모든 편지 &amp;rarr; 수도권 집중국
서울 강남구 역삼동 편지 &amp;rarr; 강남 지역 우체국&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역삼동 편지는 두 규칙에 모두 해당한다. 하지만 역삼동을 정확히 가리키는 안내가 서울 전체를 가리키는 안내보다 구체적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서도 넓은 범위를 하나의 경로로 묶어 표현하면서 일부 목적지만 예외 경로로 보낼 수 있어야 한다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.0.0.0/8      &amp;rarr; 기본적으로 Router B
10.20.30.0/24   &amp;rarr; 이 구역만 Router D&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPM이 없다면 &lt;code&gt;/8&lt;/code&gt;과 &lt;code&gt;/24&lt;/code&gt; 가운데 무엇을 먼저 적용할지 테이블 순서나 별도 규칙에 의존해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPM은 Prefix의 구체성이라는 일관된 기준으로 예외 경로를 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조 덕분에 다음 두 목표를 함께 달성할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;넓은 주소 범위는 Summary Route 하나로 줄여 Routing Table을 작게 유지한다.&lt;/li&gt;
&lt;li&gt;특별한 처리가 필요한 작은 범위는 More Specific Route로 덮어쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Route Aggregation&lt;/b&gt;과 &lt;b&gt;More Specific Route&lt;/b&gt;의 관계로 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Default Route는 왜 항상 마지막 후보일까?&amp;iexcl;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4 Default Route는 &lt;code&gt;0.0.0.0/0&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Length가 0이므로 모든 IPv4 주소와 일치한다. 동시에 가능한 경로 가운데 가장 덜 구체적이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10.20.30.0/24와 일치 &amp;rarr; /24 선택
10.20.0.0/16과만 일치 &amp;rarr; /16 선택
구체적 경로가 없음   &amp;rarr; /0 선택&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route가 Routing Table의 위에 표시되더라도 더 구체적인 경로보다 먼저 선택되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route는 다른 길을 모두 이기는 최우선 경로가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더 정확한 안내가 없을 때 사용하는 마지막 포괄 경로&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Metric이 낮으면 무조건 선택될까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 두 경로를 보자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.0.0.0/8      via Router A metric 10
10.20.30.0/24   via Router B metric 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;10.20.30.40&lt;/code&gt;에는 &lt;code&gt;/8&lt;/code&gt;과 &lt;code&gt;/24&lt;/code&gt;가 모두 일치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/8&lt;/code&gt;의 Metric이 10으로 더 낮지만 &lt;code&gt;/24&lt;/code&gt;가 더 긴 Prefix다. 따라서 먼저 &lt;code&gt;/24&lt;/code&gt; 경로가 선택된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1차 기준: Longest Prefix Match &amp;rarr; /24 승리
Metric 비교: 서로 다른 Prefix라면 먼저 비교할 기준이 아님&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metric은 보통 &lt;b&gt;같은 Destination Prefix로 가는 후보 경로&lt;/b&gt;를 비교할 때 의미가 있다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.20.30.0/24 via Router B metric 100
10.20.30.0/24 via Router C metric 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 출처와 정책 조건이 같다면 더 낮은 Metric의 Router C 경로를 선호할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metric이 표현하는 의미는 프로토콜마다 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OSPF는 링크 Cost를 합산한다.&lt;/li&gt;
&lt;li&gt;RIP은 Hop Count를 사용한다.&lt;/li&gt;
&lt;li&gt;BGP는 단일한 최단거리 Metric보다 여러 Path Attribute와 정책을 비교한다.&lt;/li&gt;
&lt;li&gt;운영체제의 Static Route Metric은 경로 우선순위를 나타내는 방식으로 사용될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서로 다른 프로토콜의 Metric 숫자를 단순 비교해 &amp;ldquo;20이 100보다 작으니 더 좋다&amp;rdquo;고 판단할 수는 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 서로 다른 출처가 같은 길을 알려 주면 누구를 믿을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 &lt;code&gt;10.20.30.0/24&lt;/code&gt; 경로를 Static Route와 OSPF가 동시에 알려 줄 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;10.20.30.0/24 &amp;rarr; Static Route
10.20.30.0/24 &amp;rarr; OSPF Route&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Length는 같다. 하지만 두 경로는 서로 다른 방식으로 학습되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 시스템은 어떤 출처를 더 선호할지 나타내는 값을 사용할 수 있다. 네트워크 장비에서는 이를 흔히 &lt;b&gt;Administrative Distance(AD)&lt;/b&gt; 또는 &lt;b&gt;Route Preference&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 값이 낮을수록 더 선호하도록 구현하는 플랫폼이 많지만 기본값과 명칭, 비교 순서는 제조사와 운영체제에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 AD와 Metric의 역할을 구분하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Route Preference / AD : 서로 다른 경로 출처 중 무엇을 신뢰할지 비교
Metric                : 선택된 출처나 프로토콜 안에서 어느 경로가 더 좋은지 비교&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 모든 플랫폼에 동일하게 적용되는 하나의 고정 공식으로 외우면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux, 라우터 운영체제, 각 동적 라우팅 프로토콜은 경로를 설치하는 절차와 표시 방식이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통적으로 기억할 안전한 순서는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;패킷 전달 시 Destination IP와 가장 길게 일치하는 Prefix를 찾는다.&lt;/li&gt;
&lt;li&gt;같은 Prefix의 후보 가운데 정책과 경로 출처의 선호도를 적용한다.&lt;/li&gt;
&lt;li&gt;같은 조건의 후보 사이에서 Metric과 프로토콜별 규칙을 적용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 비용이 같으면 한 길만 사용해야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 Prefix로 가는 두 경로의 비용이 같을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.20.30.0/24 via Router B metric 20
10.20.30.0/24 via Router C metric 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비와 설정이 지원한다면 두 경로를 모두 활성화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;ECMP&lt;/b&gt;(Equal-Cost Multi-Path)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP는 여러 Next Hop에 트래픽을 분산해 링크를 함께 사용하고 하나의 경로에 장애가 생겼을 때 대체 경로를 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷을 단순히 한 장씩 번갈아 보내면 순서가 바뀔 수 있으므로 실제 장비는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 Source/Destination IP, Port 같은 흐름 정보를 해시해 같은 Flow가 같은 Next Hop을 사용하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확한 해시 필드와 분산 방식은 장비와 설정에 따라 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMP가 있다고 해서 모든 링크에 정확히 같은 양의 트래픽이 흐르는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow의 수와 크기가 다르면 분포도 달라질 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Next Hop도 찾아갈 수 있어야 한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route Entry에 Next Hop 주소가 적혀 있다고 해서 바로 프레임을 만들 수 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 경로를 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10.20.30.0/24 via 192.0.2.2 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 &lt;code&gt;192.0.2.2&lt;/code&gt;의 MAC 주소를 알아내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러려면 Next Hop이 &lt;code&gt;eth0&lt;/code&gt;에서 On-link이거나, Next Hop 자체로 가는 경로를 다시 찾을 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Recursive Next-Hop Resolution&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;목적지 10.20.30.40
   &amp;darr; Route Lookup
10.20.30.0/24 via 192.0.2.2
   &amp;darr; Next Hop Resolution
192.0.2.0/24 dev eth0
   &amp;darr; Neighbor Resolution
192.0.2.2의 MAC을 ARP로 조회&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination Prefix는 맞지만 Next Hop으로 가는 경로가 없다면 해당 Route를 실제 전달에 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Routing Table을 볼 때 목적지 경로뿐 아니라 Next Hop이 어느 인터페이스에서 해결되는지도 함께 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 더 구체적인 경로가 오히려 편지를 사라지게 할 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPM은 일관된 규칙이지만 경로 정보가 올바르다는 사실까지 보장하지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 테이블을 보자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.20.30.0/24    via 정상 Router
10.20.30.40/32   blackhole&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;10.20.30.50&lt;/code&gt;은 &lt;code&gt;/24&lt;/code&gt; 경로를 따라 정상적으로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;10.20.30.40&lt;/code&gt;은 더 구체적인 &lt;code&gt;/32&lt;/code&gt; Blackhole Route와 일치하므로 폐기된다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;10.20.30.40 &amp;rarr; /32 Blackhole 선택 &amp;rarr; 폐기
10.20.30.50 &amp;rarr; /24 정상 경로 선택 &amp;rarr; 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주변 주소는 모두 되는데 특정 IP 하나만 통신되지 않는다면 More Specific Route를 의심할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blackhole Route&lt;/b&gt;는 트래픽을 의도적으로 조용히 폐기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 잘못된 트래픽을 막거나 경로 루프를 방지하는 용도로 사용할 수 있지만, 잘못 설정하면 찾기 어려운 장애를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제에 따라 &lt;code&gt;unreachable&lt;/code&gt;, &lt;code&gt;prohibit&lt;/code&gt; 같은 경로 유형도 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷을 폐기하면서 호출자에게 돌려주는 오류가 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPM은 가장 올바른 경로가 아니라 가장 구체적으로 일치하는 경로를 고른다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RIB와 FIB는 무엇이 다를까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 프로토콜과 정적 설정을 통해 여러 후보 경로가 들어오면 제어 영역은 최적 경로를 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경로 정보를 다루는 논리적인 테이블을 흔히 &lt;b&gt;RIB&lt;/b&gt;(Routing Information Base)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 패킷을 빠르게 전달하기 위해 선택된 경로는 전달에 적합한 형태로 &lt;b&gt;FIB&lt;/b&gt;(Forwarding Information Base)에 반영된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Connected / Static / OSPF / BGP
              &amp;darr;
       RIB에서 경로 선택
              &amp;darr;
       FIB에 전달 정보 반영
              &amp;darr;
    Packet별 빠른 Forwarding Lookup&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비와 운영체제에 따라 테이블 구조와 이름은 다르지만 개념적인 역할은 구분할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Control Plane은 경로를 학습하고 최적 경로를 계산한다.&lt;/li&gt;
&lt;li&gt;Data Plane은 선택된 정보를 사용해 실제 패킷을 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Routing Table&lt;/code&gt;이라는 표현이 문맥에 따라 RIB 또는 사용자에게 보이는 선택 경로를 포괄해 사용되기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 경로를 만드는 과정과 패킷을 전달하는 과정을 나누어 이해하는 것이 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;네트워크 용어&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적지 지역별 다음 배송처 표&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Routing Table&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Destination Prefix와 Next Hop, Interface를 연결한 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배송 구역 한 줄&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Route Entry&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;특정 Prefix에 적용되는 하나의 경로 항목&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;가장 상세한 주소 안내를 우선&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Longest Prefix Match&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일치하는 경로 중 Prefix Length가 가장 긴 항목 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전국 단위의 포괄 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Summary Route&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;여러 작은 주소 범위를 하나로 묶은 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 동네만 다른 길로 보내는 예외&lt;/td&gt;
&lt;td&gt;&lt;b&gt;More Specific Route&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;더 긴 Prefix로 일부 목적지를 별도 처리하는 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;안내 정보의 출처 신뢰도&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Administrative Distance / Preference&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;같은 Prefix의 서로 다른 출처를 비교하는 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;같은 방식의 길 가운데 드는 비용&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Metric&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로토콜 안에서 경로 품질을 비교하는 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비용이 같은 여러 배송길&lt;/td&gt;
&lt;td&gt;&lt;b&gt;ECMP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;같은 비용의 여러 Next Hop을 함께 사용하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의도적으로 폐기하는 주소 안내&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Blackhole Route&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일치하는 패킷을 전달하지 않고 폐기하는 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;경로 후보를 관리하는 지도&lt;/td&gt;
&lt;td&gt;&lt;b&gt;RIB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제어 영역의 라우팅 정보 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실제 분류 창구가 사용하는 지도&lt;/td&gt;
&lt;td&gt;&lt;b&gt;FIB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;패킷 전달에 사용하는 Forwarding 정보 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 선택의 핵심 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Destination IP와 일치하는 Prefix를 모두 찾는다.
2. Longest Prefix Match로 가장 구체적인 Prefix를 선택한다.
3. 같은 Prefix 후보에 경로 출처의 Preference와 정책을 적용한다.
4. 같은 조건의 후보를 Metric과 프로토콜 규칙으로 비교한다.
5. 선택된 Next Hop과 Outgoing Interface로 패킷을 전달한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 경로를 골라 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Routing Table을 보고 각 목적지에 선택될 경로를 찾아보자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;0.0.0.0/0        via A
172.16.0.0/12    via B
172.16.0.0/16    via C
172.16.10.0/24   via D
172.16.10.128/25 via E&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 47.907%; height: 206px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;목적지&lt;/td&gt;
&lt;td&gt;일치하는 가장 긴 Prefix&lt;/td&gt;
&lt;td&gt;선택 Next Hop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8.8.8.8&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;172.20.1.1&lt;/td&gt;
&lt;td&gt;172.16.0.0/12&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;172.16.200.1&lt;/td&gt;
&lt;td&gt;172.16.0.0/16&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;172.16.10.20&lt;/td&gt;
&lt;td&gt;172.16.10.0/24&lt;/td&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;172.16.10.200&lt;/td&gt;
&lt;td&gt;172.16.10.128/25&lt;/td&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/12&lt;/code&gt;의 범위는 두 번째 옥텟 기준으로 &lt;code&gt;16~31&lt;/code&gt;이므로 &lt;code&gt;172.20.1.1&lt;/code&gt;도 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 Main Routing Table을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -4 route show table main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 목적지에 실제 선택되는 경로를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 8.8.8.8
ip route get 172.16.10.200&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 &lt;code&gt;via&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;src&lt;/code&gt;를 살펴본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;via : 선택된 Next Hop
dev : Outgoing Interface
src : 패킷에 사용할 Source IP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 테이블에 예제 Prefix가 없다면 실제 결과는 예제 표와 다르다. 명령의 목적은 시스템이 가진 경로에서 특정 Destination IP에 어떤 Route를 선택하는지 확인하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 설정을 추가하거나 Blackhole Route를 만드는 작업은 실제 통신을 끊을 수 있으므로 운영 환경에서는 읽기 명령으로만 관찰한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Routing Table의 Route Entry는 Destination Prefix, Next Hop, Interface, Source와 비용 정보를 담는다.&lt;/li&gt;
&lt;li&gt;하나의 Destination IP는 여러 Prefix와 동시에 일치할 수 있다.&lt;/li&gt;
&lt;li&gt;라우터는 가장 긴 Prefix가 일치하는 경로를 먼저 선택한다.&lt;/li&gt;
&lt;li&gt;Default Route &lt;code&gt;/0&lt;/code&gt;은 모든 주소와 일치하지만 가장 덜 구체적인 마지막 후보다.&lt;/li&gt;
&lt;li&gt;서로 다른 Prefix에서는 Metric이 낮다는 이유로 더 구체적인 경로를 이길 수 없다.&lt;/li&gt;
&lt;li&gt;같은 Prefix 후보 사이에서는 경로 출처의 Preference, 정책, Metric과 프로토콜 규칙이 적용된다.&lt;/li&gt;
&lt;li&gt;동일 비용 경로는 장비와 설정에 따라 ECMP로 함께 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;Next Hop 자체도 연결된 경로나 다른 Route를 통해 해결할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;잘못된 More Specific Route는 특정 목적지만 사라지는 장애를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;RIB는 경로 학습과 선택에, FIB는 실제 Packet Forwarding에 초점을 둔다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 용어: &lt;code&gt;Routing Table&lt;/code&gt;, &lt;code&gt;Route Entry&lt;/code&gt;, &lt;code&gt;Destination Prefix&lt;/code&gt;, &lt;code&gt;Longest Prefix Match&lt;/code&gt;, &lt;code&gt;Summary Route&lt;/code&gt;, &lt;code&gt;More Specific Route&lt;/code&gt;, &lt;code&gt;Administrative Distance&lt;/code&gt;, &lt;code&gt;Route Preference&lt;/code&gt;, &lt;code&gt;Metric&lt;/code&gt;, &lt;code&gt;ECMP&lt;/code&gt;, &lt;code&gt;Recursive Next-Hop Resolution&lt;/code&gt;, &lt;code&gt;Blackhole Route&lt;/code&gt;, &lt;code&gt;RIB&lt;/code&gt;, &lt;code&gt;FIB&lt;/code&gt;, &lt;code&gt;Control Plane&lt;/code&gt;, &lt;code&gt;Data Plane&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 가장 가까워 보이는 길을 고르지 않는다. 목적지 주소와 가장 구체적으로 일치하는 안내를 먼저 따른다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 Longest Prefix Match를 사용해 요청 패킷의 다음 길을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 요청이 목적지에 도착한 순간 통신도 성공한 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 패킷은 요청이 지나온 길을 기억해 그대로 되돌아가는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 호스트와 각 라우터는 응답의 Destination IP를 기준으로 새로운 경로 선택을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;가는 길이 있으면 돌아오는 길도 있는 것일까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward Path, Return Path, Asymmetric Routing과 Missing Route가 만드는 장애를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>routing table</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1087</guid>
      <comments>https://peterica.tistory.com/1087#entry1087comment</comments>
      <pubDate>Thu, 16 Jul 2026 20:09:04 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 08. 다른 동네의 편지는 누구에게 맡겨야 할까?</title>
      <link>https://peterica.tistory.com/1085</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgjDKs/dJMcai43Hmk/rb9JuELnPngeihUUA5AmDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgjDKs/dJMcai43Hmk/rb9JuELnPngeihUUA5AmDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgjDKs/dJMcai43Hmk/rb9JuELnPngeihUUA5AmDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgjDKs%2FdJMcai43Hmk%2Frb9JuELnPngeihUUA5AmDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 2. 편지에 두 종류의 주소가 필요한 이유&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 Subnet Mask와 라우팅 테이블을 사용해 목적지가 같은 네트워크인지 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 네트워크의 On-link 목적지라면 상대의 MAC 주소를 ARP로 찾고 직접 프레임을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 목적지가 다른 네트워크에 있다면 이야기가 달라진다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PC A
IP 192.168.10.10/24

목적지
IP 192.168.20.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A는 &lt;code&gt;192.168.20.20&lt;/code&gt;이 자신의 네트워크인 &lt;code&gt;192.168.10.0/24&lt;/code&gt; 밖에 있다는 사실은 안다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 목적지까지 이어지는 모든 라우터와 링크를 알지는 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇다면 첫 프레임을 누구에게 맡겨야 할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 다른 지역으로 편지를 보낼 때 전국의 모든 배달길을 직접 고르지 않는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소를 적은 편지를 동네 우체국에 맡기면 우체국이 다음 지역으로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에서 이 첫 번째 우체국 역할을 하는 장비가 &lt;b&gt;Default Gateway&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Default Route&lt;/b&gt;, &lt;b&gt;Next Hop&lt;/b&gt;, &lt;b&gt;ARP&lt;/b&gt;, &lt;b&gt;IP Forwarding&lt;/b&gt;이 어떻게 연결되는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 라우터를 지날 때 IP 패킷과 Ethernet Frame 중 무엇이 유지되고 무엇이 바뀌는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 목적지의 길을 호스트가 알아야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에는 수많은 네트워크가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC가 각 네트워크로 가는 경로를 모두 저장해야 한다면 다음과 같은 지도가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.20.0/24는 Router A로
172.16.0.0/16은 Router B로
10.20.30.0/24는 Router C로
그 밖의 수많은 네트워크는 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 호스트가 인터넷 전체의 경로를 알고 관리하는 것은 비효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트가 꼭 알아야 하는 것은 더 단순하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;직접 연결된 네트워크는 자신의 인터페이스로 보낸다.&lt;/li&gt;
&lt;li&gt;나머지 목적지는 동네 밖 출구에 맡긴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &amp;ldquo;더 구체적인 경로가 없는 모든 목적지&amp;rdquo;에 사용하는 경로가 &lt;b&gt;Default Route&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IPv4에서는 다음 Prefix로 표현한다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;0.0.0.0/0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Length가 0이라는 것은 어떤 목적지 주소와도 일치할 수 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 라우팅 테이블에서는 다음과 같이 보일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;default via 192.168.10.1 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 풀어 쓰면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;더 구체적인 경로가 없는 목적지는
eth0 인터페이스를 통해
다음 홉 192.168.10.1로 보내라.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;192.168.10.1&lt;/code&gt;이 PC A의 Default Gateway다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Default Gateway와 Default Route는 같은 말일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 용어는 밀접하지만 가리키는 대상이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Default Route는 경로 규칙이다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 목적지와 일치하는 더 구체적인 경로가 없을 때 사용할 라우팅 테이블의 항목이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Destination Prefix : 0.0.0.0/0
Next Hop           : 192.168.10.1
Output Interface   : eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Default Gateway는 다음으로 넘겨받을 장비다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route가 선택되었을 때 패킷을 전달받는 현재 링크의 라우터다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Default Route   : 모르는 목적지에 적용할 규칙
Default Gateway : 그 규칙이 가리키는 다음 라우터&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일상에서는 두 표현을 비슷하게 사용하지만 장애를 분석할 때는 구분하는 것이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Default Route 자체가 없을 수 있다.&lt;/li&gt;
&lt;li&gt;Route는 있지만 잘못된 Gateway를 가리킬 수 있다.&lt;/li&gt;
&lt;li&gt;Gateway IP는 맞지만 ARP로 MAC 주소를 찾지 못할 수 있다.&lt;/li&gt;
&lt;li&gt;Gateway까지는 전달되지만 라우터가 다음 경로를 모를 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;게이트웨이 문제&amp;rdquo;라는 한 문장 안에도 서로 다른 실패 지점이 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 게이트웨이는 어디에 있어야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 Default Gateway &lt;code&gt;192.168.10.1&lt;/code&gt;에게 Ethernet Frame을 보내려면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게이트웨이의 MAC 주소를 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP는 같은 Broadcast Domain 안에서만 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 일반적인 구성에서 Default Gateway의 IP 주소는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트가 직접 도달할 수 있는 &lt;b&gt;On-link Network&lt;/b&gt;에 있어야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PC A       : 192.168.10.10/24
Gateway    : 192.168.10.1/24
같은 Subnet: 192.168.10.0/24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A는 다음 ARP Request를 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Who has 192.168.10.1?
Tell 192.168.10.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게이트웨이가 다음처럼 응답한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;192.168.10.1 is at 11:11:11:11:11:01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 PC A는 원격 목적지로 가는 첫 프레임을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Gateway가 꼭 &lt;code&gt;.1&lt;/code&gt; 주소일 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.254&lt;/code&gt;나 다른 사용 가능한 주소도 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 관습적인 숫자가 아니라 호스트의 라우팅 설정이 가리키는 On-link 라우터라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정이나 사무실에서는 DHCP의 Router Option을 통해 Default Gateway 주소를 자동으로 받는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적으로 설정할 수도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 최종 목적지는 서버인데 왜 프레임은 게이트웨이에게 보낼까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 PC B로 보내는 패킷을 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PC A
IP  : 192.168.10.10/24
MAC : AA:AA:AA:AA:AA:AA

Router R1 - 왼쪽 인터페이스
IP  : 192.168.10.1/24
MAC : 11:11:11:11:11:01

Router R1 - 오른쪽 인터페이스
IP  : 192.168.20.1/24
MAC : 11:11:11:11:11:02

PC B
IP  : 192.168.20.20/24
MAC : BB:BB:BB:BB:BB:BB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PC A ── 192.168.10.0/24 ── R1 ── 192.168.20.0/24 ── PC B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 만드는 IP Packet의 목적지는 처음부터 PC B다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Header
├─ Source IP      : 192.168.10.10
└─ Destination IP : 192.168.20.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 현재 Ethernet Link에서 직접 프레임을 받을 장비는 R1이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;Ethernet Header
├─ Source MAC      : AA:AA:AA:AA:AA:AA
└─ Destination MAC : 11:11:11:11:11:01&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 목적지는 서로 다르다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Destination       : 전체 여정의 최종 목적지 PC B
Ethernet Destination : 현재 링크의 다음 홉 R1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 봉투에는 최종 수신인의 주소를 적지만, 현재 배달 단계에서는 동네 우체국이 편지를 먼저 넘겨받는 것과 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 라우터는 받은 프레임을 그대로 넘길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 보낸 Ethernet Frame은 R1의 왼쪽 인터페이스에 도착한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 목적지 MAC 주소가 자신의 인터페이스 주소이므로 프레임을 받아들인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤 다음 순서로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1단계: Ethernet Header를 벗긴다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 링크에서 사용한 Ethernet Header와 Trailer를 처리하고 안쪽의 IP Packet을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[Ethernet Header][IP Packet][Ethernet Trailer]
        &amp;darr; 제거          &amp;darr;
                  [IP Packet]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2단계: Destination IP를 확인한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Destination IP &lt;code&gt;192.168.20.20&lt;/code&gt;은 라우터 자신의 주소가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 이 패킷을 다른 인터페이스로 전달해야 하는지 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 3단계: TTL을 1 줄인다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 IPv4 Header의 TTL 값을 1 감소시킨다. TTL이 0이 되면 패킷을 폐기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTL이 변경되므로 IPv4 Header Checksum도 다시 계산해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4단계: 라우팅 테이블을 조회한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 Destination IP와 일치하는 경로를 찾는다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.20.0/24 dev eth1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지가 오른쪽 인터페이스에 직접 연결되어 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 5단계: 다음 L2 수신인의 MAC을 찾는다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC B의 MAC 주소가 Neighbor Table에 없다면 R1은 오른쪽 네트워크에서 &lt;code&gt;192.168.20.20&lt;/code&gt;을 ARP로 찾는다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;192.168.20.20 &amp;rarr; BB:BB:BB:BB:BB:BB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 6단계: 새 Ethernet Frame을 만든다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R1은 같은 IP Packet을 오른쪽 링크에 맞는 새 Ethernet Frame에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Ethernet Header
├─ Source MAC      : 11:11:11:11:11:02
└─ Destination MAC : BB:BB:BB:BB:BB:BB

IP Header
├─ Source IP      : 192.168.10.10
└─ Destination IP : 192.168.20.20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 프레임의 FCS도 해당 링크에서 전송할 내용에 맞게 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 PC A의 Ethernet Frame을 그대로 연장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP Packet을 꺼내 다음 경로를 판단한 뒤, 새 링크의 Source MAC과 Destination MAC으로 다시 캡슐화한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 주소는 여정에서 어떻게 바뀔까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A에서 R1을 거쳐 PC B까지의 주소를 표로 비교해 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 64.8837%; height: 135px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구간&lt;/td&gt;
&lt;td&gt;Source MAC&lt;/td&gt;
&lt;td&gt;Destination MAC&lt;/td&gt;
&lt;td&gt;Source IP&lt;/td&gt;
&lt;td&gt;Destination IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PC A &amp;rarr; R1&lt;/td&gt;
&lt;td&gt;PC A MAC&lt;/td&gt;
&lt;td&gt;R1 왼쪽 MAC&lt;/td&gt;
&lt;td&gt;PC A IP&lt;/td&gt;
&lt;td&gt;PC B IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R1 &amp;rarr; PC B&lt;/td&gt;
&lt;td&gt;R1 오른쪽 MAC&lt;/td&gt;
&lt;td&gt;PC B MAC&lt;/td&gt;
&lt;td&gt;PC A IP&lt;/td&gt;
&lt;td&gt;PC B IP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MAC 주소는 링크마다 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소는 일반적인 라우팅 과정에서 최종 목적지까지 유지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 TTL과 IPv4 Header Checksum 같은 헤더 값은 라우터를 지날 때 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;MAC Address : Hop-by-Hop
IP Address  : End-to-End 목적지 표현&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 이해하면 &amp;ldquo;원격 서버의 MAC 주소를 왜 내 PC에서 볼 수 없는가?&amp;rdquo;라는 질문에도 답할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 PC가 알아야 하는 것은 원격 서버의 MAC 주소가 아니라 현재 링크에서 첫 프레임을 받을 Default Gateway의 MAC 주소다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 라우터는 왜 패킷을 전달할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소를 가진 모든 장비가 자동으로 라우터가 되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터 역할을 하려면 보통 다음 조건이 필요하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;둘 이상의 네트워크에 연결된 인터페이스가 있다.&lt;/li&gt;
&lt;li&gt;목적지에 맞는 라우팅 정보가 있다.&lt;/li&gt;
&lt;li&gt;인터페이스 사이의 &lt;b&gt;IP Forwarding&lt;/b&gt;이 활성화되어 있다.&lt;/li&gt;
&lt;li&gt;방화벽과 정책이 해당 전달을 허용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 호스트는 자신의 IP를 목적지로 하는 패킷을 처리하지만 다른 호스트의 패킷을 인터페이스 사이로 넘기지 않는 것이 기본인 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 IPv4 Forwarding 상태를 다음과 같이 읽을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sysctl net.ipv4.ip_forward&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;net.ipv4.ip_forward = 0  &amp;rarr; IPv4 Forwarding 비활성
net.ipv4.ip_forward = 1  &amp;rarr; IPv4 Forwarding 활성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forwarding이 활성화되어 있어도 경로나 방화벽 정책이 올바르지 않으면 패킷은 전달되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Gateway는 단순히 IP 주소가 하나 더 설정된 장비가 아니라, 다른 네트워크로 패킷을 전달할 준비가 된 라우터여야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ㅁ 기본 경로가 없으면 어떤 일이 생길까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A의 라우팅 테이블에 직접 연결된 경로만 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.10.0/24 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;192.168.20.20&lt;/code&gt;은 이 경로에 포함되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route도 없다면 운영체제는 패킷을 어느 인터페이스와 Next Hop으로 보낼지 결정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션에서는 다음과 같은 즉시 오류를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Network is unreachable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류는 패킷이 먼 네트워크에서 사라졌다는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 호스트가 보낼 경로를 찾지 못해 출발조차 하지 못했을 가능성을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Default Route는 있지만 Gateway의 MAC 주소를 ARP로 찾지 못하는 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP 재시도 뒤 지연되거나 Host Unreachable 계열 오류가 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Default Route 없음
&amp;rarr; 경로 선택 실패
&amp;rarr; 출발 전 즉시 오류 가능

Default Route 있음, Gateway ARP 실패
&amp;rarr; 링크에서 이웃 탐색 재시도
&amp;rarr; 지연 후 실패 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로는 모두 &amp;ldquo;인터넷이 안 된다&amp;rdquo;지만 실패한 단계는 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Default Route가 여러 개일 수도 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북에 유선과 무선이 함께 연결되거나 라우터에 여러 인터넷 회선이 있으면 Default Route가 여러 개 존재할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;default via 192.168.10.1 dev eth0 metric 100
default via 192.168.30.1 dev wlan0 metric 600&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 목적지 Prefix에 여러 경로가 있을 때 운영체제는 Metric과 정책 같은 정보를 사용해 경로를 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metric이 낮은 경로를 더 선호하는 구성이 흔하지만 정확한 선택 규칙은 운영체제, 라우팅 프로토콜, 정책 라우팅 구성에 따라 달라질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Route는 반드시 하나뿐이라는 규칙이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Default Route보다 더 구체적인 목적지 경로가 있으면 구체적인 경로가 우선한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;10.20.0.0/16 via 192.168.10.2
default       via 192.168.10.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;10.20.30.40&lt;/code&gt;은 &lt;code&gt;/16&lt;/code&gt; 경로를 사용하고, 일치하는 구체적 경로가 없는 다른 목적지는 Default Route를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 구체적인 경로가 먼저 선택되는지는 다음 글의 Longest Prefix Match에서 자세히 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 편지가 도착하면 통신이 끝난 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A의 패킷이 R1을 거쳐 PC B에 도착했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 통신이 완성되려면 PC B의 응답도 PC A로 돌아와야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC B 역시 &lt;code&gt;192.168.10.10&lt;/code&gt;이 자신의 네트워크 밖에 있다는 것을 판단하고 자신의 Default Gateway인 &lt;code&gt;192.168.20.1&lt;/code&gt;로 답장을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 같은 R1이 양쪽 네트워크에 직접 연결되어 있으므로 돌아오는 경로도 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 네트워크에서는 요청 경로와 응답 경로가 서로 별개의 라우팅 결정이다. 가는 길이 존재한다고 돌아오는 길이 자동으로 보장되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 Part 3의 「&lt;b&gt;가는 길이 있으면 돌아오는 길도 있는 것일까?&lt;/b&gt;」에서 더 자세히 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;height: 172px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;네트워크 용어&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네 밖 편지를 맡기는 우체국&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Default Gateway&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;구체적인 경로가 없는 원격 패킷을 처음 전달받는 라우터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모르는 모든 지역에 적용할 규칙&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Default Route&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv4에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;0.0.0.0/0으로 표현하는 기본 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;편지를 다음으로 넘겨받는 우체국&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Next Hop&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;현재 장비가 패킷을 전달할 다음 라우터 또는 이웃&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우체국까지의 현재 배달 봉투&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Ethernet Frame&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한 링크 안에서 다음 L2 수신인에게 전달하는 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;봉투 안의 최종 주소가 적힌 편지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;IP Packet&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 네트워크를 지나 최종 목적지를 나타내는 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;편지를 꺼내 새 봉투에 넣음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Re-encapsulation&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;라우터가 다음 링크의 L2 헤더로 패킷을 다시 감싸는 과정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;다른 지역으로 편지를 넘김&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;IP Forwarding&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;수신한 IP 패킷을 다른 인터페이스로 전달하는 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;지금 구간의 배달 주소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Hop-by-Hop MAC Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;링크마다 바뀌는 Source와 Destination MAC 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트에서 라우터를 거쳐 목적지까지의 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. Destination IP에 대해 Routing Table을 조회한다.
2. Default Route가 선택되면 Next Hop인 Gateway를 확인한다.
3. Gateway MAC을 Neighbor Table에서 찾거나 ARP로 조회한다.
4. IP Packet을 Gateway MAC 목적지의 Ethernet Frame에 넣어 보낸다.
5. Router는 L2 Header를 벗기고 TTL을 줄인 뒤 경로를 조회한다.
6. 다음 링크의 MAC 주소로 새 Frame을 만들어 전달한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 현재 라우팅 테이블을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -4 route show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 항목을 찾는다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;default via 192.168.10.1 dev eth0
192.168.10.0/24 dev eth0 scope link src 192.168.10.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 원격 목적지에 선택되는 Gateway, Interface, Source IP를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과의 핵심은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;8.8.8.8 via 192.168.10.1 dev eth0 src 192.168.10.10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게이트웨이의 MAC 주소가 이웃 테이블에 있는지도 살펴본다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ip neigh show 192.168.10.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 홉을 숫자 주소로 확인하려면 다음 명령을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;traceroute -n 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에 따라 &lt;code&gt;traceroute&lt;/code&gt;가 설치되어 있지 않거나 방화벽과 ICMP 정책 때문에 일부 홉이 응답하지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 홉이 항상 화면에 보인다고 보장할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 다음 명령으로 기본 경로와 목적지 경로를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;route -n get default
route -n get 8.8.8.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 중인 환경에서 Default Route를 삭제하거나 Gateway를 변경하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 연결이 즉시 끊어질 수 있으므로 읽기 명령으로 상태만 관찰한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트는 모든 인터넷 경로를 저장하지 않고 구체적 경로가 없는 목적지를 Default Route로 보낸다.&lt;/li&gt;
&lt;li&gt;IPv4 Default Route는 &lt;code&gt;0.0.0.0/0&lt;/code&gt;으로 표현한다.&lt;/li&gt;
&lt;li&gt;Default Gateway는 Default Route가 가리키는 현재 링크의 Next Hop 라우터다.&lt;/li&gt;
&lt;li&gt;일반적으로 Gateway는 호스트가 ARP로 MAC 주소를 찾을 수 있는 On-link Network에 있어야 한다.&lt;/li&gt;
&lt;li&gt;원격 통신에서 IP Destination은 최종 서버지만 첫 Ethernet Destination은 Gateway MAC이다.&lt;/li&gt;
&lt;li&gt;라우터는 L2 Header를 벗기고 TTL을 줄인 뒤 경로를 조회해 다음 링크의 새 Frame으로 Re-encapsulation한다.&lt;/li&gt;
&lt;li&gt;MAC 주소는 링크마다 바뀌고 IP Source와 Destination은 일반적인 라우팅 중 유지된다.&lt;/li&gt;
&lt;li&gt;IP 주소가 여러 개 있다고 자동으로 라우터가 되는 것은 아니며 IP Forwarding, 경로, 정책이 필요하다.&lt;/li&gt;
&lt;li&gt;Default Route가 없을 때와 Gateway ARP가 실패할 때는 장애가 발생하는 단계가 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 용어: &lt;code&gt;Default Gateway&lt;/code&gt;, &lt;code&gt;Default Route&lt;/code&gt;, &lt;code&gt;0.0.0.0/0&lt;/code&gt;, &lt;code&gt;Next Hop&lt;/code&gt;, &lt;code&gt;Routing Table&lt;/code&gt;, &lt;code&gt;ARP&lt;/code&gt;, &lt;code&gt;IP Forwarding&lt;/code&gt;, &lt;code&gt;Re-encapsulation&lt;/code&gt;, &lt;code&gt;Hop-by-Hop&lt;/code&gt;, &lt;code&gt;TTL&lt;/code&gt;, &lt;code&gt;IPv4 Header Checksum&lt;/code&gt;, &lt;code&gt;Metric&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Gateway는 최종 목적지를 대신하는 주소가 아니다. &lt;br /&gt;호스트가 모르는 먼 길을 처음 맡아 줄 수 있는 같은 동네의 라우터다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 자신이 모르는 목적지를 Default Gateway에 맡겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 라우터는 Routing Table에서 패킷을 보낼 다음 길을 선택해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 하나의 Destination IP가 여러 경로 Prefix와 동시에 일치하면 어느 길을 선택해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;라우터는 수많은 목적지의 길을 어떻게 고를까?&lt;/b&gt;」라는 질문을 통해 Routing Table, Route Entry, Longest Prefix Match, Administrative Distance와 Metric의 역할을 구분해 본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>default gateway</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1085</guid>
      <comments>https://peterica.tistory.com/1085#entry1085comment</comments>
      <pubDate>Thu, 16 Jul 2026 20:08:05 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 07. 같은 동네와 다른 동네는 누가 판단할까?</title>
      <link>https://peterica.tistory.com/1086</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KgfUI/dJMcabZiPZ0/a8fPFk44yGfkMX4P3xH4kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KgfUI/dJMcabZiPZ0/a8fPFk44yGfkMX4P3xH4kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KgfUI/dJMcabZiPZ0/a8fPFk44yGfkMX4P3xH4kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKgfUI%2FdJMcabZiPZ0%2Fa8fPFk44yGfkMX4P3xH4kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 2. 편지에 두 종류의 주소가 필요한 이유&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 글에서 ARP는 IPv4 주소에 대응하는 MAC 주소를 찾는다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ARP Request는 같은 Broadcast Domain 안에서만 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 일반적으로 이 브로드캐스트를 다른 네트워크로 넘기지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 다음 목적지로 데이터를 보낸다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;내 IP 주소 : 192.168.10.70
목적지 A   : 192.168.10.120
목적지 B   : 192.168.10.130&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 주소가 모두 &lt;code&gt;192.168.10&lt;/code&gt;으로 시작한다. 그러면 둘 다 같은 동네일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소의 겉모습만으로는 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC는 패킷을 보내기 전에 먼저 다음 결정을 내려야 한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;같은 네트워크라면 &amp;rarr; 목적지의 MAC 주소를 ARP로 찾는다.
다른 네트워크라면 &amp;rarr; 기본 게이트웨이의 MAC 주소를 ARP로 찾는다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경계를 알려 주는 정보가 &lt;b&gt;Subnet Mask&lt;/b&gt;이며, 이를 간단히 표현하는 방법이 &lt;b&gt;Prefix Length&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 IPv4 주소의 비트 구조, &lt;code&gt;/24&lt;/code&gt;의 의미, Network Address를 계산하는 AND 연산, On-link와 Off-link 판단을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 주소만 적으면 동네의 경계를 알 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;192.168.10.70&lt;/code&gt;이라는 IPv4 주소를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 읽기 쉽게 점으로 나뉜 네 개의 숫자로 표시하지만, 컴퓨터는 32개의 비트로 처리한다.&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;192        168        10         70
11000000 . 10101000 . 00001010 . 01000110&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 32비트 안에는 네트워크를 나타내는 부분과 그 네트워크 안의 호스트를 나타내는 부분이 함께 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[ Network Portion ][ Host Portion ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 IP 주소만 봐서는 두 부분의 경계가 어디인지 알 수 없다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 &lt;code&gt;192.168.10.70&lt;/code&gt;이라도 다음처럼 해석할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;192.168.10.70/24 &amp;rarr; 앞 24비트가 Network Portion
192.168.10.70/26 &amp;rarr; 앞 26비트가 Network Portion&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소는 같지만 &lt;code&gt;/24&lt;/code&gt;와 &lt;code&gt;/26&lt;/code&gt;은 서로 다른 크기의 동네를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소와 Prefix Length는 함께 있어야 완전한 네트워크 주소 정보를 표현할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Subnet Mask는 경계선을 그린다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Subnet Mask&lt;/b&gt;도 IPv4 주소와 같은 32비트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 부분에는 연속된 &lt;code&gt;1&lt;/code&gt;, 호스트 부분에는 연속된 &lt;code&gt;0&lt;/code&gt;을 둔다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Address  : 11000000.10101000.00001010.01000110
Subnet Mask : 11111111.11111111.11111111.00000000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subnet Mask를 십진수로 표현하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;255.255.255.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서부터 &lt;code&gt;1&lt;/code&gt;인 비트가 24개이므로 더 간단히 &lt;code&gt;/24&lt;/code&gt;라고 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;CIDR Prefix Length&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;255.0.0.0       = /8
255.255.0.0     = /16
255.255.255.0   = /24
255.255.255.192 = /26&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefix Length가 길수록 네트워크 부분이 많고 호스트 부분은 적어진다. 따라서 하나의 서브넷에 포함되는 주소의 수는 줄어든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/24 &amp;rarr; Host bit 8개  &amp;rarr; 2^8  = 256개 주소
/26 &amp;rarr; Host bit 6개  &amp;rarr; 2^6  = 64개 주소
/30 &amp;rarr; Host bit 2개  &amp;rarr; 2^2  = 4개 주소&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거의 Class A, B, C 방식보다 필요한 크기에 맞춰 Prefix Length를 정하는 &lt;b&gt;CIDR(Classless Inter-Domain Routing)&lt;/b&gt; 방식이 현재 주소 표현과 라우팅의 기본이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 동네 이름은 어떻게 계산할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트가 속한 네트워크를 나타내는 주소를 &lt;b&gt;Network Address&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소와 Subnet Mask를 비트 단위 &lt;b&gt;AND 연산&lt;/b&gt;하면 Network Address를 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AND 연산은 두 비트가 모두 &lt;code&gt;1&lt;/code&gt;일 때만 결과가 &lt;code&gt;1&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;192.168.10.70/24&lt;/code&gt;를 계산해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Address   11000000.10101000.00001010.01000110
Subnet Mask  11111111.11111111.11111111.00000000
             ----------------------------------- AND
Network      11000000.10101000.00001010.00000000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;십진수로 바꾸면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.10.0/24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subnet Mask의 &lt;code&gt;1&lt;/code&gt; 영역은 IP 주소의 네트워크 비트를 남기고, &lt;code&gt;0&lt;/code&gt; 영역은 호스트 비트를 모두 0으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Network Address는 해당 서브넷 자체를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 IPv4 서브넷에서는 호스트 인터페이스에 Network Address를 할당하지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 목적지에도 내 마스크를 적용한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC가 목적지를 같은 네트워크로 판단할 때 목적지의 Subnet Mask를 물어보는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;송신자는 자신의 인터페이스에 설정된 Prefix를 기준으로 목적지 주소가 연결된 네트워크에 포함되는지 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 설정을 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;내 IP Address : 192.168.10.70
Subnet Mask   : 255.255.255.192
Prefix Length : /26&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/26&lt;/code&gt;의 마지막 옥텟 마스크는 &lt;code&gt;192&lt;/code&gt;, 이진수로 &lt;code&gt;11000000&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 자신의 Network Address를 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;70    = 01000110
192   = 11000000
        -------- AND
64    = 01000000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자신의 네트워크는 &lt;code&gt;192.168.10.64/26&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;192.168.10.120&lt;/code&gt;에도 같은 &lt;code&gt;/26&lt;/code&gt; 마스크를 적용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;120   = 01111000
192   = 11000000
        -------- AND
64    = 01000000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과가 자신의 Network Address와 같은 &lt;code&gt;192.168.10.64&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 같은 네트워크의 이웃, 즉 &lt;b&gt;On-link Destination&lt;/b&gt;으로 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 목적지 &lt;code&gt;192.168.10.130&lt;/code&gt;을 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;130   = 10000010
192   = 11000000
        -------- AND
128   = 10000000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 &lt;code&gt;192.168.10.128&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 Network Address인 &lt;code&gt;192.168.10.64&lt;/code&gt;와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;192.168.10.130&lt;/code&gt;은 직접 연결된 네트워크 밖의 &lt;b&gt;Off-link Destination&lt;/b&gt;으로 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소 앞부분이 &lt;code&gt;192.168.10&lt;/code&gt;으로 같아 보여도 &lt;code&gt;/26&lt;/code&gt; 경계에서는 서로 다른 동네다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ /26은 동네를 어떻게 나눌까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;192.168.10.0/24&lt;/code&gt;를 &lt;code&gt;/26&lt;/code&gt;으로 더 작게 나누면 64개 주소씩 네 개의 서브넷이 생긴다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 63.0233%; height: 91px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;서브넷&lt;/td&gt;
&lt;td&gt;Network Address&lt;/td&gt;
&lt;td&gt;일반 호스트 범위&lt;/td&gt;
&lt;td&gt;Broadcast Address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;첫 번째&lt;/td&gt;
&lt;td&gt;192.168.10.0/26&lt;/td&gt;
&lt;td&gt;192.168.10.1~62&lt;/td&gt;
&lt;td&gt;192.168.10.63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;두 번째&lt;/td&gt;
&lt;td&gt;192.168.10.64/26&lt;/td&gt;
&lt;td&gt;192.168.10.65~126&lt;/td&gt;
&lt;td&gt;192.168.10.127&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;세 번째&lt;/td&gt;
&lt;td&gt;192.168.10.128/26&lt;/td&gt;
&lt;td&gt;192.168.10.129~190&lt;/td&gt;
&lt;td&gt;192.168.10.191&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;네 번째&lt;/td&gt;
&lt;td&gt;192.168.10.192/26&lt;/td&gt;
&lt;td&gt;192.168.10.193~254&lt;/td&gt;
&lt;td&gt;192.168.10.255&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서브넷에서 호스트 비트가 모두 &lt;code&gt;0&lt;/code&gt;이면 Network Address다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 비트가 모두 &lt;code&gt;1&lt;/code&gt;이면 해당 서브넷의 모든 호스트를 대상으로 하는 &lt;b&gt;Directed Broadcast Address&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 호스트 주소로는 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 전통적인 일반 IPv4 서브넷에서는 전체 주소 수에서 Network Address와 Broadcast Address를 제외해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 가능한 호스트 주소 수를 계산한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/26 전체 주소 64개
일반 호스트 주소 62개&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 Prefix에 이 공식을 기계적으로 적용하면 안 된다. &lt;code&gt;/31&lt;/code&gt;은 점대점 링크에서 두 주소를 모두 사용할 수 있도록 정의되어 있고, &lt;code&gt;/32&lt;/code&gt;는 하나의 호스트 또는 경로를 나타내는 데 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 계산법을 이해하되 실제 사용 방식에는 예외가 있다는 점을 함께 기억해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 같은 동네라면 누구의 MAC 주소를 찾을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network Address 비교가 끝나면 실제 전달 대상이 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ On-link Destination&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지가 같은 네트워크에 있으면 목적지 호스트의 MAC 주소를 ARP로 찾는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Destination  : 192.168.10.120
ARP Target IP   : 192.168.10.120
Ethernet Target : 목적지 호스트의 MAC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ Off-link Destination&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지가 다른 네트워크에 있으면 호스트가 직접 목적지 MAC을 찾지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 라우팅 테이블에서 다음 홉을 찾고, 보통 기본 게이트웨이의 MAC 주소를 ARP로 조회한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;IP Destination  : 192.168.10.130
ARP Target IP   : 기본 게이트웨이 IP
Ethernet Target : 기본 게이트웨이 MAC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 패킷의 Destination IP는 원래 목적지를 유지하지만 현재 이더넷 프레임은 게이트웨이에게 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Subnet Mask는 주소 범위를 설명하는 숫자에 그치지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ARP로 누구를 찾을지 결정하는 경계선&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 운영체제는 매번 직접 AND 계산을 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는 IP 주소와 마스크를 AND 연산해 같은 네트워크인지 판단한다고 설명할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영체제는 인터페이스 주소와 Prefix가 설정될 때 직접 연결된 네트워크에 대한 경로를 라우팅 테이블에 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 주소가 설정되어 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.10.70/26 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 테이블에는 다음과 같은 &lt;b&gt;Connected Route&lt;/b&gt;가 생길 수 있다.&lt;/p&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;192.168.10.64/26 dev eth0 scope link&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지 &lt;code&gt;192.168.10.120&lt;/code&gt;은 이 경로에 포함되므로 &lt;code&gt;eth0&lt;/code&gt;에서 직접 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;192.168.10.130&lt;/code&gt;은 포함되지 않으므로 다른 경로나 기본 경로를 찾아야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;목적지 IP
   &amp;darr;
Routing Table Lookup
   &amp;darr;
Connected Route와 일치 &amp;rarr; On-link &amp;rarr; 목적지 ARP
Gateway Route와 일치   &amp;rarr; Off-link &amp;rarr; Next-hop ARP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 비트 AND를 직접 수행하지 않더라도 Prefix를 기반으로 만들어진 라우팅 정보가 같은 판단을 수행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Subnet Mask를 잘못 적으면 어떤 일이 생길까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소와 게이트웨이가 맞아 보여도 Subnet Mask가 틀리면 호스트가 목적지의 위치를 잘못 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 실제보다 넓게 설정한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트의 실제 네트워크가 &lt;code&gt;192.168.10.0/24&lt;/code&gt;인데 실수로 &lt;code&gt;/16&lt;/code&gt;을 설정했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 &lt;code&gt;192.168.20.10&lt;/code&gt;도 같은 네트워크에 있다고 판단할 수 있다. 게이트웨이로 보내지 않고 직접 ARP Request를 반복한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ARP 브로드캐스트는 라우터를 넘지 못하므로 대상의 MAC 주소를 얻지 못한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;잘못된 판단: 같은 동네 &amp;rarr; 목적지 ARP 반복 &amp;rarr; 응답 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 실제보다 좁게 설정한 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 네트워크가 &lt;code&gt;/24&lt;/code&gt;인데 호스트만 &lt;code&gt;/25&lt;/code&gt;로 설정했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 L2 네트워크의 &lt;code&gt;192.168.10.200&lt;/code&gt;을 다른 네트워크로 판단해 게이트웨이로 보내려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게이트웨이 설정과 네트워크 기능에 따라 우회 통신이 되거나, 비효율적인 경로를 사용하거나, 통신이 실패할 수 있다. 중요한 것은 물리적으로 가까운 목적지를 호스트가 직접 이웃으로 판단하지 못한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subnet Mask 불일치는 &amp;ldquo;같은 스위치에 연결되어 있는데 왜 통신이 안 되지?&amp;rdquo;라는 장애를 만들 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ VLAN과 Subnet은 같은 것일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN은 L2 Broadcast Domain을 나누고 Subnet은 IP 주소의 L3 범위를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 계층의 개념이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;VLAN  : Ethernet Broadcast의 경계
Subnet: IP Address와 On-link 판단의 경계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 하나의 VLAN에 하나의 IP Subnet을 대응시키는 구성이 일반적이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;VLAN 10 &amp;harr; 192.168.10.0/24
VLAN 20 &amp;harr; 192.168.20.0/24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 L2 브로드캐스트 범위와 L3 On-link 범위가 일치해 이해하고 운영하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 두 개념이 자동으로 같은 것은 아니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 Subnet이라고 설정했지만 서로 다른 VLAN에 있으면 ARP Request가 경계를 넘지 못해 직접 통신할 수 없다.&lt;/li&gt;
&lt;li&gt;같은 VLAN에 있어도 서로 다른 Subnet으로 설정하면 호스트는 상대를 Off-link로 판단해 게이트웨이를 찾는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적인 연결, VLAN, IP Prefix가 함께 맞아야 &amp;ldquo;같은 동네&amp;rdquo;라는 판단과 실제 전달 범위가 일치한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;전체 주소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;IPv4 Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;32비트로 표현하는 논리 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네와 집 번호의 경계선&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Subnet Mask&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Network Portion과 Host Portion을 구분하는 32비트 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;앞에서부터 동네에 사용하는 비트 수&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Prefix Length&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;/24,&lt;span&gt;&amp;nbsp;&lt;/span&gt;/26처럼 네트워크 비트 수를 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네 자체의 이름&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Network Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IP 주소와 Subnet Mask를 AND한 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네 모든 집을 부르는 주소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Broadcast Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;호스트 비트가 모두 1인 IPv4 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 동네의 목적지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;On-link&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;직접 연결된 네트워크 안에 있는 목적지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;다른 동네의 목적지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Off-link&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;라우터 또는 다음 홉을 거쳐야 하는 목적지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;주소와 마스크를 겹쳐 보는 계산&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Bitwise AND&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Network Address를 구하는 비트 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;현재 인터페이스에 직접 붙은 동네 지도&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Connected Route&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Prefix 설정으로 생성되는 직접 연결 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트의 판단 순서는 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1. 자신의 IP Address와 Prefix로 Connected Network를 구한다.
2. 목적지 IP가 Connected Route에 포함되는지 확인한다.
3-1. 포함되면 On-link로 판단하고 목적지 IP를 ARP로 찾는다.
3-2. 포함되지 않으면 Off-link로 판단하고 다른 경로 또는 Gateway를 찾는다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 계산하고 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 호스트를 기준으로 목적지가 On-link인지 Off-link인지 계산해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Host: 10.10.10.100/27&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/27&lt;/code&gt;은 마지막 옥텟에서 앞 3비트가 네트워크 부분이므로 블록 크기는 32다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;서브넷 경계: 0, 32, 64, 96, 128, 160, 192, 224&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;10.10.10.100&lt;/code&gt;은 &lt;code&gt;10.10.10.96/27&lt;/code&gt;에 속한다. 일반 호스트 범위는 &lt;code&gt;.97~.126&lt;/code&gt;, Broadcast Address는 &lt;code&gt;.127&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같이 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서는 인터페이스의 IPv4 주소와 Prefix를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -4 address show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connected Route와 기본 경로를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -4 route show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 목적지를 운영체제가 어떻게 판단하는지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip route get 10.10.10.110
ip route get 10.10.10.130&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;On-link 목적지는 일반적으로 &lt;code&gt;dev &amp;lt;interface&amp;gt;&lt;/code&gt;가 직접 표시되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게이트웨이를 사용하는 경로에는 &lt;code&gt;via &amp;lt;gateway&amp;gt;&lt;/code&gt;가 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정책 라우팅과 여러 인터페이스가 있는 환경에서는 결과가 더 복잡할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 다음 명령으로 인터페이스와 특정 목적지의 경로를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ifconfig
route -n get 10.10.10.130&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령 결과에서 주소만 보지 말고 Prefix, 출력 인터페이스, Gateway가 함께 어떻게 선택되는지 살펴보자.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IPv4 주소는 32비트이며 Network Portion과 Host Portion을 함께 담는다.&lt;/li&gt;
&lt;li&gt;Subnet Mask는 연속된 1과 0으로 두 부분의 경계를 나타낸다.&lt;/li&gt;
&lt;li&gt;Prefix Length는 &lt;code&gt;/24&lt;/code&gt;, &lt;code&gt;/26&lt;/code&gt;처럼 네트워크 비트 수를 표현한다.&lt;/li&gt;
&lt;li&gt;IP 주소와 Subnet Mask를 Bitwise AND하면 Network Address를 구할 수 있다.&lt;/li&gt;
&lt;li&gt;호스트는 자신의 Prefix를 기준으로 목적지가 On-link인지 Off-link인지 판단한다.&lt;/li&gt;
&lt;li&gt;On-link 목적지는 목적지 MAC을 ARP로 찾고 Off-link 목적지는 다음 홉의 MAC을 찾는다.&lt;/li&gt;
&lt;li&gt;실제 운영체제는 Prefix에서 만들어진 Connected Route를 포함한 라우팅 테이블을 조회한다.&lt;/li&gt;
&lt;li&gt;VLAN은 L2 경계이고 Subnet은 L3 주소 경계이며, 둘은 관련되지만 같은 개념이 아니다.&lt;/li&gt;
&lt;li&gt;잘못된 Subnet Mask는 ARP 대상과 Gateway 선택을 바꿔 통신 장애를 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 용어: &lt;code&gt;IPv4 Address&lt;/code&gt;, &lt;code&gt;Subnet Mask&lt;/code&gt;, &lt;code&gt;Prefix Length&lt;/code&gt;, &lt;code&gt;CIDR&lt;/code&gt;, &lt;code&gt;Network Portion&lt;/code&gt;, &lt;code&gt;Host Portion&lt;/code&gt;, &lt;code&gt;Network Address&lt;/code&gt;, &lt;code&gt;Broadcast Address&lt;/code&gt;, &lt;code&gt;Bitwise AND&lt;/code&gt;, &lt;code&gt;On-link&lt;/code&gt;, &lt;code&gt;Off-link&lt;/code&gt;, &lt;code&gt;Connected Route&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Subnet Mask는 주소를 보기 좋게 나누는 숫자가 아니다. 목적지에게 직접 물을지, 우체국에 맡길지를 결정하는 경계선이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 Subnet Mask와 라우팅 테이블을 사용해 목적지가 다른 네트워크에 있다는 사실을 알아냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 목적지까지의 모든 길을 알고 있는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동네 밖으로 나가는 편지를 처음 누구에게 맡겨야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;다른 동네의 편지는 누구에게 맡겨야 할까?&lt;/b&gt;」라는 질문을 통해 Default Gateway, Default Route, Next Hop, IP Forwarding과 라우터를 지날 때 프레임이 어떻게 바뀌는지 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>cidr</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>vrakkkkkkkkkkkkkkkkkkk</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1086</guid>
      <comments>https://peterica.tistory.com/1086#entry1086comment</comments>
      <pubDate>Wed, 15 Jul 2026 23:37:33 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 06. IP 주소만 알면 바로 편지를 건넬 수 있을까?</title>
      <link>https://peterica.tistory.com/1084</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmr6pC/dJMcahyo7sK/b8uYcHiB1T27ob3IISylxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmr6pC/dJMcahyo7sK/b8uYcHiB1T27ob3IISylxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmr6pC/dJMcahyo7sK/b8uYcHiB1T27ob3IISylxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcmr6pC%2FdJMcahyo7sK%2Fb8uYcHiB1T27ob3IISylxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 2. 편지에 두 종류의 주소가 필요한 이유&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;VLAN은 하나의 물리적인 스위치 안에 여러 Broadcast Domain을 만든다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 같은 VLAN 10에 연결된 두 컴퓨터가 통신한다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;PC A ─ Access VLAN 10 ─ Switch ─ Access VLAN 10 ─ PC B&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A가 알고 있는 정보는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;내 IP 주소     : 192.168.10.10
상대의 IP 주소 : 192.168.10.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;애플리케이션은 상대의 IP 주소를 목적지로 데이터를 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 스위치는 IP 주소가 아니라 이더넷 프레임의 목적지 MAC 주소를 보고 출력 포트를 고른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A가 프레임을 만들려면 다음 빈칸을 채워야 한다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source MAC      : PC A의 MAC 주소
Destination MAC : ???&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소는 알고 있지만 MAC 주소는 모른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 첫 프레임은 누구에게 보내야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IPv4 네트워크는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ARP&lt;/b&gt;(Address Resolution Protocol)를 사용해 이 질문에 답한다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;192.168.10.20을 사용하는 장비는 자신의 MAC 주소를 알려 달라.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 IP 주소와 MAC 주소가 왜 함께 필요한지,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP Request와 ARP Reply가 어떤 순서로 오가는지,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;알아낸 결과를 ARP Cache에 왜 저장하는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 주소가 하나면 더 간단하지 않을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우편물을 보낼 때는 도시와 도로를 따라 목적지 지역을 찾아갈 주소가 필요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;목적지 동네에 도착한 뒤에는 그 동네 안에서 실제 수신인의 문까지 전달할 방법도 필요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크에서는 IP 주소와 MAC 주소가 서로 다른 범위의 문제를 맡는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ IP 주소는 목적지가 어디에 속하는지 나타낸다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IP Address&lt;/b&gt;는 논리적인 주소다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크의 경계를 표현하고 라우터가 서로 다른 네트워크 사이에서 다음 경로를 선택할 때 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;장비가 다른 네트워크로 이동하면 일반적으로 그 네트워크에 맞는 IP 주소를 사용해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ MAC 주소는 현재 이더넷 구간에서 프레임을 전달한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MAC Address&lt;/b&gt;는 이더넷 인터페이스를 식별하는 L2 주소다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 이더넷 구간에서 스위치는 목적지 MAC 주소를 보고 프레임을 전달한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;IP Address  : 목적지 호스트를 논리적으로 식별하고 경로 선택에 사용
MAC Address : 현재 이더넷 링크에서 다음 수신 인터페이스에 프레임 전달&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 주소는 같은 대상을 표현하는 중복 정보가 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소는 전체 여정의 목적지를 나타내고, MAC 주소는 현재 구간에서 프레임을 누구에게 건넬지 나타낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터를 지날 때마다 이더넷 프레임은 새 구간에 맞게 다시 만들어지므로 출발지와 목적지 MAC 주소도 바뀐다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 IP 패킷의 출발지와 목적지 주소는 일반적인 라우팅 과정에서는 유지된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;NAT처럼 IP 주소를 변경하는 기능은 뒤의 글에서 별도로 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 이름은 아는데 어느 집인지 모른다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PC A&lt;/b&gt;가&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.20으로 IP 패킷을 보내려고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;PC A&lt;/b&gt;가 목적지를 같은 네트워크의 이웃이라고 이미 판단했다고 가정한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 기준으로 같은 네트워크와 다른 네트워크를 구분하는지는 다음 글에서 서브넷 마스크와 함께 살펴본다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A가 만들 IP 패킷은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source IP      : 192.168.10.10
Destination IP : 192.168.10.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 패킷을 이더넷으로 보내려면 프레임 안에 넣어야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Ethernet Frame
├─ Source MAC      : AA:AA:AA:AA:AA:AA
├─ Destination MAC : 아직 모름
└─ Payload         : IP Packet
   ├─ Source IP      : 192.168.10.10
   └─ Destination IP : 192.168.10.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A는 먼저 자신이 기억하고 있는 이웃 정보에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.20의 MAC 주소를 찾는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정보가 없다면 ARP Request를 보낸다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 모르는 집을 찾으려면 동네 전체에 묻는다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A는 누가&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.20을 사용하는지 모른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 장비의 MAC 주소를 모르므로 그 장비에게만 질문을 보낼 수도 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 같은 Broadcast Domain의 모두에게 ARP Request를 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이더넷 프레임은 다음 목적지 MAC 주소를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Destination MAC : FF:FF:FF:FF:FF:FF&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이는 Ethernet Broadcast Address다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP Request의 핵심 내용은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;보내는 사람의 MAC : AA:AA:AA:AA:AA:AA
보내는 사람의 IP  : 192.168.10.10
찾는 대상의 IP     : 192.168.10.20

질문: 192.168.10.20을 가진 장비는 MAC 주소를 알려 달라.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 브로드캐스트 프레임을 VLAN 10의 다른 포트로 플러딩한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;PC A ─ ARP Request ─ Switch ┬─ PC B
                            ├─ PC C
                            └─ PC D&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 VLAN의 PC B, C, D가 모두 요청을 받는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 각 장비는 ARP 메시지의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Target Protocol Address&lt;/b&gt;, 즉 찾는 대상 IP를 확인한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PC C의 IP가&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.30이면 자신의 요청이 아니므로 응답하지 않는다.&lt;/li&gt;
&lt;li&gt;PC D의 IP가&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.40이면 자신의 요청이 아니므로 응답하지 않는다.&lt;/li&gt;
&lt;li&gt;PC B의 IP가&lt;span&gt;&amp;nbsp;&lt;/span&gt;192.168.10.20이면 자신의 주소이므로 응답한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;질문은 모두에게 전달되지만 답할 장비는 IP 주소의 주인 하나다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 주소의 주인은 자신의 MAC 주소를 답한다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC B는 PC A에게 ARP Reply를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;192.168.10.20은
BB:BB:BB:BB:BB:BB에 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC B는 ARP Request 안에 들어 있던 PC A의 IP 주소와 MAC 주소를 이미 알고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 ARP Reply는 일반적으로 PC A의 MAC 주소를 목적지로 하는 유니캐스트 프레임으로 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Ethernet
├─ Source MAC      : BB:BB:BB:BB:BB:BB
└─ Destination MAC : AA:AA:AA:AA:AA:AA

ARP Reply
├─ Sender IP  : 192.168.10.20
├─ Sender MAC : BB:BB:BB:BB:BB:BB
├─ Target IP  : 192.168.10.10
└─ Target MAC : AA:AA:AA:AA:AA:AA&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 앞에서 배운 MAC Address Table을 사용해 이 답장을 PC A가 있는 포트로 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A는 이제 빈칸을 채울 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;192.168.10.20 &amp;rarr; BB:BB:BB:BB:BB:BB&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 원래 보내려던 IP 패킷을 이더넷 프레임에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Ethernet Frame
├─ Source MAC      : AA:AA:AA:AA:AA:AA
├─ Destination MAC : BB:BB:BB:BB:BB:BB
└─ Payload         : IP Packet
   ├─ Source IP      : 192.168.10.10
   └─ Destination IP : 192.168.10.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 스위치는 목적지 MAC 주소&lt;span&gt;&amp;nbsp;&lt;/span&gt;BB:...를 보고 PC B 방향으로 프레임을 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP는 데이터를 전달하는 대신, 데이터를 담을 첫 이더넷 프레임의 목적지 주소를 준비한 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 매번 동네 전체에 물어봐야 할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PC A가 패킷을 보낼 때마다 ARP Request를 브로드캐스트하면 같은 질문이 계속 반복된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 운영체제는 알아낸 IP 주소와 MAC 주소의 관계를 잠시 저장한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 흔히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ARP Cache&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux에서는 IPv4 ARP 정보와 IPv6 Neighbor Discovery 정보를 함께 다루는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Neighbor Table&lt;/b&gt;이라는 표현과 명령을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;IP Address       MAC Address          Interface
192.168.10.20    BB:BB:BB:BB:BB:BB    eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 전송에서는 이 항목을 사용해 ARP Request 없이 바로 프레임을 만들 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;캐시를 영원히 유지하지는 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;장비가 교체되거나 네트워크 인터페이스가 바뀌면 같은 IP 주소가 다른 MAC 주소를 사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오래된 정보를 계속 믿으면 잘못된 장비로 프레임을 보낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 동적으로 학습한 이웃 정보에는 상태와 유효 시간이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제는 일정 시간이 지나거나 통신 가능성을 확인할 필요가 생기면 이웃 정보를 다시 검증한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux의 Neighbor Unreachability Detection 상태에서는 다음과 같은 표현을 볼 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REACHABLE: 최근 통신 가능하다고 확인된 이웃&lt;/li&gt;
&lt;li&gt;STALE: 정보는 있지만 최근 도달 가능성은 확인되지 않은 상태&lt;/li&gt;
&lt;li&gt;DELAY,&lt;span&gt;&amp;nbsp;&lt;/span&gt;PROBE: 도달 가능성을 다시 확인하는 과정&lt;/li&gt;
&lt;li&gt;INCOMPLETE: 주소 확인을 시도 중이지만 아직 MAC 주소를 얻지 못함&lt;/li&gt;
&lt;li&gt;FAILED: 주소 확인 또는 도달 가능성 확인에 실패함&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 운영체제가 같은 이름과 상태 전이를 그대로 보여 주는 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 ARP 결과가 영구적인 정답이 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;현재 링크의 이웃에 대한 임시 정보&lt;/b&gt;라는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ ARP는 어디까지 질문할 수 있을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP Request는 Ethernet Broadcast Frame으로 전달된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터는 일반적으로 이 L2 브로드캐스트를 다른 네트워크로 전달하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;VLAN도 Broadcast Domain을 분리하므로 ARP Request는 같은 VLAN 범위 안에 머문다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;VLAN 10 ─ ARP Request ─X─ Router ─ VLAN 20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;목적지 IP가 인터넷의 원격 서버라면 PC가 그 서버의 MAC 주소를 ARP로 찾는 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원격 목적지로 가는 첫 번째 L2 수신인은 같은 네트워크에 있는 기본 게이트웨이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 PC는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;게이트웨이 IP 주소에 대응하는 MAC 주소&lt;/b&gt;를 ARP로 찾고, 이더넷 프레임을 게이트웨이에게 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;IP Packet의 목적지 IP       : 원격 서버 IP
첫 Ethernet Frame의 목적지 MAC : 기본 게이트웨이 MAC&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 목적지와 현재 프레임의 MAC 목적지가 서로 다를 수 있다는 점이 중요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP는 전 세계의 주소록이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 L2 링크에서 다음으로 프레임을 받을 이웃을 찾는 방법&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 경우에 상대 호스트를 직접 찾고 어떤 경우에 게이트웨이를 찾아야 하는지는&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 두 글에서 서브넷 마스크와 기본 게이트웨이로 이어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ ARP 메시지 안에는 무엇이 들어 있을까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;패킷 캡처 도구에서 ARP를 열어 보면 다음 필드를 볼 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;필드&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Hardware Type&lt;/td&gt;
&lt;td&gt;사용하는 L2 기술의 종류이며 이더넷은 일반적으로 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Protocol Type&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;해석하려는 상위 프로토콜이며 IPv4는&lt;span&gt;&amp;nbsp;&lt;/span&gt;0x0800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Hardware Address Length&lt;/td&gt;
&lt;td&gt;MAC 주소 길이이며 이더넷은 6바이트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Protocol Address Length&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;IPv4 주소 길이인 4바이트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Opcode&lt;/td&gt;
&lt;td&gt;Request는 1, Reply는 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Sender Hardware Address&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;보내는 장비의 MAC 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Sender Protocol Address&lt;/td&gt;
&lt;td&gt;보내는 장비의 IPv4 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Target Hardware Address&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;대상 장비의 MAC 주소이며 Request에서는 아직 모를 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Target Protocol Address&lt;/td&gt;
&lt;td&gt;찾으려는 대상의 IPv4 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이더넷 헤더의 EtherType에는 ARP임을 나타내는&lt;span&gt;&amp;nbsp;&lt;/span&gt;0x0806이 사용된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 필드를 외우는 것보다 다음 네 가지를 읽을 수 있으면 동작을 추적하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;누가 묻는가?       Sender IP / Sender MAC
누구를 찾는가?      Target IP
요청인가 답변인가?   Opcode
누구에게 전달되는가?  Ethernet Destination MAC&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 주소를 묻는 답변을 믿어도 될까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본 ARP에는 답변자의 신원을 인증하는 절차가 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 L2 네트워크의 공격자가 &amp;ldquo;게이트웨이 IP 주소는 내 MAC 주소에 있다&amp;rdquo;고 거짓 ARP 정보를 보내면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다른 장비의 ARP Cache가 잘못 갱신될 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ARP Spoofing&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ARP Poisoning&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공격자는 트래픽을 자신에게 우회시켜 내용을 엿보거나 변경하고, 전달하지 않아 통신을 끊을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치 환경에서 ARP의 신뢰를 보완하기 위해 DHCP Snooping 정보와 연계한 Dynamic ARP Inspection 같은 기능을 사용할 수 있다. 정적 ARP 항목도 특정 환경에서는 도움이 되지만 규모가 커지면 관리가 어렵다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서 기억해야 할 점은 ARP가 편리한 자동 주소 조회인 동시에, 같은 L2 네트워크의 답변을 기본적으로 신뢰하도록 설계된 프로토콜이라는 사실이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ Gratuitous ARP는 묻지 않았는데 왜 답할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;장비가 자신의 IP와 MAC 주소 관계를 요청 없이 네트워크에 알리는 ARP를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Gratuitous ARP&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황에서 활용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP 주소가 새 MAC 주소로 이동했음을 주변 장비에 알릴 때&lt;/li&gt;
&lt;li&gt;고가용성 구성에서 활성 장비가 바뀌어 이웃 캐시를 갱신할 때&lt;/li&gt;
&lt;li&gt;같은 IP 주소를 다른 장비가 사용하고 있는지 중복 가능성을 확인할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Gratuitous ARP의 구체적인 패킷 형태와 운영체제 동작에는 차이가 있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 다른 장비가 먼저 질문하기를 기다리지 않고 자신의 IPv4 주소와 MAC 주소 관계를 같은 L2 영역에 알린다는 점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;height: 198px; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;806&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 26px;&quot;&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; height: 26px;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; height: 26px;&quot;&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; height: 26px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;도시와 동네를 포함한 논리 주소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;IP Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;호스트와 네트워크를 식별하고 라우팅에 사용하는 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;현재 동네에서 실제 문을 찾는 주소&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MAC Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;이더넷 구간에서 인터페이스를 식별하는 L2 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&amp;ldquo;이 IP 주소의 주인은 누구인가?&amp;rdquo;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ARP Request&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IPv4 주소에 대응하는 MAC 주소를 묻는 브로드캐스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&amp;ldquo;이 IP는 이 MAC에 있다&amp;rdquo;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ARP Reply&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요청한 주소의 대응 관계를 알려 주는 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네 전체에 묻는 공지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Ethernet Broadcast&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;목적지 MAC&lt;span&gt;&amp;nbsp;&lt;/span&gt;FF:FF:FF:FF:FF:FF로 전달하는 프레임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;잠시 적어 둔 이웃 주소록&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ARP Cache / Neighbor Table&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;IP와 MAC 주소의 대응 관계를 임시 저장한 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;요청 없이 새 위치를 알림&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Gratuitous ARP&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;자신의 IP와 MAC 관계를 주변에 알리는 ARP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;거짓 주소 안내&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;ARP Spoofing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;잘못된 IP-MAC 관계를 학습시키는 공격&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP의 전체 동작은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. 목적지 또는 다음 홉 IPv4 주소를 결정한다.
2. Neighbor Table에서 대응하는 MAC 주소를 찾는다.
3. 정보가 없으면 ARP Request를 Broadcast한다.
4. 주소의 주인이 ARP Reply로 자신의 MAC 주소를 알린다.
5. 결과를 Cache에 저장한다.
6. 알아낸 MAC 주소로 Ethernet Frame을 만들어 전송한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 관찰해 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 IPv4 네트워크에 있는 장비의 주소를 알고 있다면 ARP 동작을 직접 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux에서 현재 이웃 테이블을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ip neigh show&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 네트워크의 아직 통신하지 않은 주소로&lt;span&gt;&amp;nbsp;&lt;/span&gt;ping을 보내기 전과 후의 결과를 비교해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ping -c 1 192.168.10.20
ip neigh show 192.168.10.20&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 인터페이스가&lt;span&gt;&amp;nbsp;&lt;/span&gt;eth0이라면 다른 터미널에서 ARP 패킷을 관찰할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;sudo tcpdump -n -e -i eth0 arp&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출력에서는 다음과 비슷한 흐름을 찾는다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ARP, Request who-has 192.168.10.20 tell 192.168.10.10
ARP, Reply 192.168.10.20 is-at bb:bb:bb:bb:bb:bb&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;macOS에서는 이웃 정보를 다음 명령으로 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;arp -a&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제, 권한, 네트워크 캐시 상태에 따라 ARP Request가 바로 보이지 않을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미 캐시된 항목이 있다면 운영체제가 그 정보를 사용하기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영 중인 시스템에서 캐시를 강제로 삭제하면 짧은 통신 단절이나 예상하지 못한 재조회가 발생할 수 있으므로,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;관찰을 위해 무리하게 상태를 변경하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IPv6에서는 ARP를 사용하지 않고 ICMPv6 기반의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Neighbor Discovery Protocol&lt;/b&gt;(NDP)을 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 IPv6 트래픽만 있는 환경에서 ARP가 보이지 않는 것은 정상이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IP 주소는 논리적인 목적지를 나타내고 MAC 주소는 현재 이더넷 구간의 수신 인터페이스를 나타낸다.&lt;/li&gt;
&lt;li&gt;같은 IPv4 네트워크에서 목적지 MAC 주소를 모르면 ARP Request를 브로드캐스트한다.&lt;/li&gt;
&lt;li&gt;대상 IP 주소의 주인은 ARP Reply로 자신의 MAC 주소를 알린다.&lt;/li&gt;
&lt;li&gt;알아낸 IP-MAC 관계는 ARP Cache 또는 Neighbor Table에 임시로 저장된다.&lt;/li&gt;
&lt;li&gt;ARP Request는 라우터를 넘어가지 않으며 같은 Broadcast Domain 안에서 동작한다.&lt;/li&gt;
&lt;li&gt;원격 목적지에는 서버의 MAC이 아니라 현재 링크의 다음 홉인 기본 게이트웨이 MAC으로 프레임을 보낸다.&lt;/li&gt;
&lt;li&gt;ARP에는 기본 인증 절차가 없어 ARP Spoofing의 대상이 될 수 있다.&lt;/li&gt;
&lt;li&gt;IPv6는 ARP 대신 ICMPv6 기반 NDP를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 용어:&lt;span&gt;&amp;nbsp;&lt;/span&gt;IP Address,&lt;span&gt;&amp;nbsp;&lt;/span&gt;MAC Address,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ARP,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ARP Request,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ARP Reply,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ethernet Broadcast,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ARP Cache,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Neighbor Table,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Opcode,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Gratuitous ARP,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ARP Spoofing,&lt;span&gt;&amp;nbsp;&lt;/span&gt;NDP&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;ARP는 최종 목적지를 찾는 프로토콜이 아니다. 현재 이더넷 구간에서 다음으로 프레임을 받을 이웃의 MAC 주소를 찾는 프로토콜이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ARP를 사용하면 IP 주소에 대응하는 MAC 주소를 알아낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 ARP Request는 같은 Broadcast Domain 안에서만 전달된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 PC는 ARP를 보내기 전에 목적지가 같은 동네에 있는지, 라우터 너머의 다른 동네에 있는지 먼저 판단해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;IP 주소만 보고 그 경계를 어떻게 알 수 있을까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;같은 동네와 다른 동네는 누가 판단할까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IPv4 Address, Subnet Mask, Prefix Length, Network Address&lt;/b&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;AND&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;연산을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>ARP request</category>
      <category>ip address</category>
      <category>MAC address</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1084</guid>
      <comments>https://peterica.tistory.com/1084#entry1084comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:06:27 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 05. 한 건물을 여러 동네로 나눌 수 있을까?</title>
      <link>https://peterica.tistory.com/1083</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GSFjV/dJMcad3LvpH/w6gaj9FKxeik1KTGTEHUD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GSFjV/dJMcad3LvpH/w6gaj9FKxeik1KTGTEHUD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GSFjV/dJMcad3LvpH/w6gaj9FKxeik1KTGTEHUD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGSFjV%2FdJMcad3LvpH%2Fw6gaj9FKxeik1KTGTEHUD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 MAC 주소를 학습해 이미 아는 목적지의 프레임을 필요한 포트로만 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STP는 스위치 사이의 순환 경로를 막아 브로드캐스트 프레임이 끝없이 복제되지 않게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 하나의 L2 네트워크를 안전하게 운영할 수 있을 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 회사가 커져 한 스위치에 개발팀, 인사팀, 방문객용 장비가 함께 연결되면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발팀에서 보낸 브로드캐스트가 인사팀과 방문객에게도 전달된다. 조직과 목적이 다르지만 스위치 입장에서는 모두 같은 동네의 주민이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 조직을 나누기 위해 스위치를 따로 구매할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;개발팀 ─ 개발팀 전용 스위치
인사팀 ─ 인사팀 전용 스위치
방문객 ─ 방문객 전용 스위치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 부서가 생기거나 자리를 옮길 때마다 장비와 배선을 다시 구성해야 한다. 포트가 남아 있어도 다른 조직은 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적인 스위치 한 대를 사용하면서도 서로 다른 동네처럼 나눌 수는 없을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Broadcast Domain&lt;/b&gt;, &lt;b&gt;VLAN&lt;/b&gt;, &lt;b&gt;Access Port&lt;/b&gt;, &lt;b&gt;Trunk&lt;/b&gt;, &lt;b&gt;IEEE 802.1Q Tag&lt;/b&gt;가 이 문제를 어떻게 해결하는지 살펴본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 같은 방송을 듣는 범위가 하나의 동네다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 글에서 브로드캐스트 프레임의 목적지 MAC 주소는 다음과 같다고 했다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;FF:FF:FF:FF:FF:FF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 이 프레임을 들어온 포트를 제외한 같은 L2 영역의 다른 포트로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브로드캐스트 프레임이 도달할 수 있는 범위를 &lt;b&gt;Broadcast Domain&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;개발자 PC ─┐
인사팀 PC ─┼─ Switch ─ 같은 Broadcast Domain
방문객 PC ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 개발자 PC가 브로드캐스트를 보내면 인사팀 PC와 방문객 PC가 연결된 방향으로도 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비 수가 적을 때는 큰 문제가 아닐 수 있다. 하지만 하나의 Broadcast Domain이 커지면 다음 문제가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브로드캐스트를 확인해야 하는 장비가 늘어난다.&lt;/li&gt;
&lt;li&gt;장애나 잘못된 트래픽의 영향 범위가 커진다.&lt;/li&gt;
&lt;li&gt;서로 다른 조직과 용도의 장비가 같은 L2 영역을 공유한다.&lt;/li&gt;
&lt;li&gt;주소 정책과 접근 정책을 조직별로 나누기 어려워진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치가 유니캐스트 전달을 효율화해도 브로드캐스트의 목적은 모두에게 전달하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MAC 주소 학습만으로는 이 범위를 줄일 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 네트워크에는 &lt;b&gt;어디까지를 같은 동네로 볼 것인가&lt;/b&gt;를 정하는 새로운 경계가 필요하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 건물은 하나지만 출입 구역은 나눌 수 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 큰 건물에 여러 회사가 입주해 있다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;건물과 엘리베이터는 함께 사용하지만 각 회사의 사무실과 우편함은 분리되어 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;물리적인 건물 한 채
├─ 10번 구역: 개발팀
├─ 20번 구역: 인사팀
└─ 30번 구역: 방문객&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10번 구역의 공지는 10번 구역 안에서만 전달된다. 20번과 30번 구역에는 들리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치에서도 이와 같은 논리적인 구역을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;VLAN&lt;/b&gt;(Virtual Local Area Network)이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Port 1 ─ 개발자 PC ─ VLAN 10
Port 2 ─ 개발자 PC ─ VLAN 10
Port 3 ─ 인사팀 PC ─ VLAN 20
Port 4 ─ 방문객 PC ─ VLAN 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적으로는 같은 스위치에 연결되어 있지만 논리적으로는 서로 다른 세 개의 L2 네트워크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 10에서 발생한 브로드캐스트는 VLAN 10에 속한 포트로만 전달된다. VLAN 20과 VLAN 30은 별도의 Broadcast Domain이므로 같은 프레임을 받지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN이 만드는 가장 중요한 변화는 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적인 연결 위치와 논리적인 네트워크의 경계를 분리한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직이 다른 층으로 이동해도 포트가 속한 VLAN을 바꾸면 같은 논리적 네트워크를 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 스위치 자원을 여러 조직이 나누어 쓰면서도 브로드캐스트 범위는 분리할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 일반 컴퓨터는 VLAN 번호를 알아야 할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 PC가 연결된 포트가 VLAN 10에 속한다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 PC는 자신이 VLAN 10에 연결되었다는 사실을 알 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소처럼 태그가 없는 이더넷 프레임을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 프레임이 들어온 포트의 설정을 보고 VLAN 10의 프레임으로 분류한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PC가 보낸 Untagged Frame
          &amp;darr;
VLAN 10으로 설정된 스위치 포트
          &amp;darr;
스위치 내부에서 VLAN 10 소속으로 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 보통 하나의 VLAN에 속한 단말을 연결하는 포트를 &lt;b&gt;Access Port&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Port의 기본 역할은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단말에서 들어온 태그 없는 프레임을 지정된 VLAN에 소속시킨다.&lt;/li&gt;
&lt;li&gt;같은 VLAN에서 단말로 나가는 프레임을 일반적으로 태그 없이 전달한다.&lt;/li&gt;
&lt;li&gt;단말이 802.1Q 태그를 직접 처리하지 않아도 VLAN을 사용할 수 있게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편 비유로 보면 각 출입문에 구역 번호가 정해져 있는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방문자가 번호표를 붙이지 않아도 어느 출입문으로 들어왔는지 보고 구역을 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비와 설정 방식에 따라 &lt;code&gt;Access VLAN&lt;/code&gt;, &lt;code&gt;Port VLAN ID&lt;/code&gt;, &lt;code&gt;PVID&lt;/code&gt; 같은 표현을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 동작과 용어는 장비마다 차이가 있지만, &lt;b&gt;태그 없는 입력 프레임을 어느 VLAN에 넣을 것인가&lt;/b&gt;를 결정한다는 기본 목적은 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 스위치가 두 대라면 구역마다 선이 필요할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 층에 있는 Switch A와 다른 층의 Switch B에 개발팀과 인사팀 장비가 나뉘어 연결되어 있다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;개발팀 A ─ VLAN 10 ─ Switch A
인사팀 A ─ VLAN 20 ─ Switch A

개발팀 B ─ VLAN 10 ─ Switch B
인사팀 B ─ VLAN 20 ─ Switch B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 스위치의 VLAN 10끼리 연결하고 VLAN 20끼리도 연결하려면 VLAN마다 별도의 물리적 링크를 둘 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Switch A ══ VLAN 10 전용 링크 ══ Switch B
Switch A ══ VLAN 20 전용 링크 ══ Switch B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN이 늘어날 때마다 스위치 사이의 선도 늘어난다면 물리적 자원을 아끼기 위해 VLAN을 만든 의미가 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여러 VLAN의 프레임을 하나의 링크로 운반하는 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역할을 하는 포트를 &lt;b&gt;Trunk Port&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;              VLAN 10, 20, 30
Switch A ═══════ Trunk ═══════ Switch B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 트렁크 링크에는 서로 다른 VLAN의 프레임이 함께 지나간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Switch B는 도착한 프레임이 VLAN 10의 것인지 VLAN 20의 것인지 어떻게 구분할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임에 VLAN 번호가 적힌 꼬리표를 붙인다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 802.1Q 태그는 봉투에 붙인 구역 번호다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이더넷 프레임에 VLAN 정보를 표시하는 표준이 &lt;b&gt;IEEE 802.1Q&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 트렁크로 프레임을 보낼 때 이더넷 헤더에 4바이트의 &lt;b&gt;802.1Q Tag&lt;/b&gt;를 추가할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;일반 Ethernet Frame
[Destination MAC][Source MAC][EtherType][Payload][FCS]

802.1Q Tagged Frame
[Destination MAC][Source MAC][802.1Q Tag][EtherType][Payload][FCS]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태그 안에는 어떤 VLAN의 프레임인지 나타내는 &lt;b&gt;VLAN ID&lt;/b&gt;(VID)가 들어 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;VLAN 10 Frame &amp;rarr; Tag: VID 10
VLAN 20 Frame &amp;rarr; Tag: VID 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch B는 태그의 VLAN ID를 읽고 프레임을 해당 VLAN 안에서만 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 10의 브로드캐스트 프레임이 트렁크를 지나더라도 Switch B의 VLAN 10 포트로만 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 20 포트에는 전달되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;802.1Q 태그의 주요 필드는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;필드&lt;/td&gt;
&lt;td&gt;크기&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TPID&lt;/td&gt;
&lt;td&gt;16비트&lt;/td&gt;
&lt;td&gt;802.1Q 태그가 있음을 표시하며 일반적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;0x8100&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCP&lt;/td&gt;
&lt;td&gt;3비트&lt;/td&gt;
&lt;td&gt;프레임 우선순위를 표현하는 데 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEI&lt;/td&gt;
&lt;td&gt;1비트&lt;/td&gt;
&lt;td&gt;혼잡 시 폐기 가능성을 표시하는 데 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VLAN ID&lt;/td&gt;
&lt;td&gt;12비트&lt;/td&gt;
&lt;td&gt;프레임이 속한 VLAN을 식별&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN ID는 12비트이므로 0부터 4095까지 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가운데 0과 4095는 특별한 용도로 예약되어 일반적인 VLAN 식별에는 보통 1~4094를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서 모든 비트를 외울 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음 한 문장이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트렁크에서는 여러 VLAN의 프레임이 같은 선을 사용하므로, 802.1Q 태그로 소속을 구분한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 프레임 한 장의 이동을 따라가 보자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 10의 PC A가 다른 스위치에 연결된 VLAN 10의 PC B에게 프레임을 보낸다고 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PC A ─ Access VLAN 10 ─ Switch A
                         ║
                    802.1Q Trunk
                         ║
PC B ─ Access VLAN 10 ─ Switch B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임은 다음 순서로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1단계: PC A가 태그 없는 프레임을 보낸다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 단말인 PC A는 평소처럼 Untagged Frame을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 2단계: Switch A가 VLAN 10으로 분류한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임이 들어온 Access Port가 VLAN 10으로 설정되어 있으므로 스위치는 이 프레임을 VLAN 10에 소속시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MAC 주소 학습도 VLAN의 범위 안에서 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 MAC 주소라도 VLAN이 다르면 별개의 논리적 영역으로 다룰 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 3단계: 트렁크에서 VLAN 10 태그를 붙인다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch A는 프레임을 트렁크로 보내면서 VLAN ID 10을 나타내는 802.1Q Tag를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 4단계: Switch B가 태그를 읽는다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Switch B는 VLAN ID 10을 확인하고 프레임을 자신의 VLAN 10 영역에서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 5단계: PC B에는 태그 없이 전달한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC B가 연결된 Access Port로 프레임을 내보낼 때 스위치는 일반적으로 802.1Q Tag를 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC B는 태그 없는 원래 형태의 이더넷 프레임을 받는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Untagged &amp;rarr; VLAN 10으로 분류 &amp;rarr; Tagged VID 10 &amp;rarr; VLAN 10 확인 &amp;rarr; Untagged&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Port와 Trunk Port가 서로 다른 역할을 맡기 때문에 단말은 VLAN 태그를 몰라도 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치 사이는 여러 VLAN을 하나의 링크로 운반할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Native VLAN은 무엇일까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;802.1Q 트렁크에서는 특정 VLAN의 프레임을 태그 없이 운반하도록 구성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 VLAN을 흔히 &lt;b&gt;Native VLAN&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽 스위치는 태그 없는 프레임을 VLAN 10으로 해석하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대쪽 스위치는 VLAN 20으로 해석한다면 같은 프레임을 서로 다른 구역의 것으로 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Native VLAN Mismatch&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Native VLAN의 기본값과 처리 방식은 장비 및 설정에 따라 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 트렁크 양쪽의 태그 정책과 허용 VLAN 목록을 명확히 맞춰야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기초 원리는 간단하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Tagged Frame은 VLAN ID로 소속을 판단한다.&lt;/li&gt;
&lt;li&gt;Untagged Frame을 어느 VLAN으로 볼지는 포트 설정에 달려 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ VLAN이 다르면 스위치로 통신할 수 있을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 10과 VLAN 20은 서로 다른 Broadcast Domain이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 같은 VLAN 안에서 MAC 주소를 기준으로 프레임을 전달하지만, VLAN의 경계를 넘어 프레임을 그대로 전달하지 않는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;VLAN 10 ─X─ VLAN 20
       L2 경계&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 VLAN 사이에서 통신하려면 IP 주소를 기준으로 두 네트워크 사이를 중계하는 &lt;b&gt;L3 Routing&lt;/b&gt;이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터의 물리 인터페이스를 사용하거나 L3 스위치의 &lt;b&gt;SVI&lt;/b&gt;(Switched Virtual Interface)를 게이트웨이로 구성할 수 있다. 이를 &lt;b&gt;Inter-VLAN Routing&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;VLAN 10 ─ Gateway / Routing ─ VLAN 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN은 네트워크를 분리하지만 그 자체가 완전한 보안 정책은 아니다. 라우팅을 허용한 뒤 어느 통신을 통과시킬지는 방화벽이나 ACL 같은 별도의 정책으로 결정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 두 문장을 구분해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VLAN은 L2 Broadcast Domain을 나눈다.&lt;/li&gt;
&lt;li&gt;보안 정책은 나뉜 영역 사이에 어떤 통신을 허용할지 정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;네트워크 용어&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;같은 공지를 받는 동네&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Broadcast Domain&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;하나의 브로드캐스트 프레임이 도달하는 L2 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;건물 안의 논리적인 구역&lt;/td&gt;
&lt;td&gt;&lt;b&gt;VLAN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;하나의 물리 네트워크를 나눈 논리적 L2 네트워크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;한 구역의 일반 출입문&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Access Port&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;보통 하나의 VLAN에 단말을 연결하는 포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여러 구역이 함께 쓰는 통로&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Trunk Port&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;여러 VLAN의 프레임을 하나의 링크로 운반하는 포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;봉투에 붙인 구역 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;802.1Q Tag&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;트렁크에서 프레임의 VLAN 소속을 표시하는 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구역 번호&lt;/td&gt;
&lt;td&gt;&lt;b&gt;VLAN ID&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;VLAN을 식별하는 12비트 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;번호표가 붙은 봉투&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Tagged Frame&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;802.1Q VLAN 정보가 포함된 프레임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;번호표가 없는 봉투&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Untagged Frame&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;802.1Q VLAN 정보가 없는 프레임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다른 구역으로 넘기는 우체국&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Inter-VLAN Routing&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서로 다른 VLAN 사이의 L3 통신&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN의 전체 동작은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Access Port : 태그 없는 단말 프레임을 특정 VLAN에 소속시킨다.
Switch 내부 : VLAN마다 학습과 전달 범위를 구분한다.
Trunk Port  : 802.1Q 태그로 여러 VLAN을 한 링크에 실어 보낸다.
L3 Gateway  : 서로 다른 VLAN 사이의 통신을 라우팅한다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 확인해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 구조에서 브로드캐스트 프레임이 어디까지 전달되는지 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;PC A ─ Access VLAN 10 ─ Switch A
PC C ─ Access VLAN 20 ─ Switch A
                           ║ Trunk: VLAN 10, 20 허용
PC B ─ Access VLAN 10 ─ Switch B
PC D ─ Access VLAN 20 ─ Switch B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A가 브로드캐스트를 보내면 다음과 같이 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Switch A는 프레임을 VLAN 10으로 분류한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. VLAN 10을 허용하는 트렁크에 VID 10 태그를 붙여 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Switch B는 VLAN 10 프레임으로 처리해 PC B 방향으로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. VLAN 20에 속한 PC C와 PC D에는 전달하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux 브리지가 VLAN 필터링을 사용하는 환경에서는 다음 명령으로 포트별 VLAN 정보를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;bridge vlan show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN 인터페이스가 구성되어 있다면 다음 명령으로 VLAN ID와 부모 인터페이스를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ip -details link show type vlan&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 &lt;code&gt;vlan id&lt;/code&gt;, &lt;code&gt;PVID&lt;/code&gt;, &lt;code&gt;Egress Untagged&lt;/code&gt; 같은 항목을 찾아보자. 표시 내용은 실제 구성과 &lt;code&gt;iproute2&lt;/code&gt; 버전에 따라 다르며, VLAN이나 브리지가 구성되지 않은 일반 호스트에서는 결과가 비어 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 중인 스위치의 VLAN 설정을 관찰 목적만으로 변경하면 네트워크가 즉시 단절될 수 있으므로 읽기 명령으로 현재 상태만 확인한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Broadcast Domain은 하나의 브로드캐스트 프레임이 도달하는 L2 범위다.&lt;/li&gt;
&lt;li&gt;VLAN은 하나의 물리적인 스위치를 여러 논리적 Broadcast Domain으로 나눈다.&lt;/li&gt;
&lt;li&gt;Access Port는 일반적으로 하나의 VLAN에 단말을 연결하며 태그 없는 프레임을 처리한다.&lt;/li&gt;
&lt;li&gt;Trunk Port는 여러 VLAN의 프레임을 하나의 물리 링크로 운반한다.&lt;/li&gt;
&lt;li&gt;IEEE 802.1Q Tag의 VLAN ID가 트렁크에서 프레임의 소속을 구분한다.&lt;/li&gt;
&lt;li&gt;Native VLAN과 Untagged Frame 처리 정책은 트렁크 양쪽에서 일치해야 한다.&lt;/li&gt;
&lt;li&gt;서로 다른 VLAN 사이의 통신에는 Inter-VLAN Routing이 필요하다.&lt;/li&gt;
&lt;li&gt;VLAN 분리와 접근 제어 정책은 같은 개념이 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 용어: &lt;code&gt;Broadcast Domain&lt;/code&gt;, &lt;code&gt;VLAN&lt;/code&gt;, &lt;code&gt;Access Port&lt;/code&gt;, &lt;code&gt;Trunk Port&lt;/code&gt;, &lt;code&gt;IEEE 802.1Q&lt;/code&gt;, &lt;code&gt;VLAN ID&lt;/code&gt;, &lt;code&gt;Tagged Frame&lt;/code&gt;, &lt;code&gt;Untagged Frame&lt;/code&gt;, &lt;code&gt;PVID&lt;/code&gt;, &lt;code&gt;Native VLAN&lt;/code&gt;, &lt;code&gt;Inter-VLAN Routing&lt;/code&gt;, &lt;code&gt;SVI&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN은 스위치를 물리적으로 나누지 않고도, 브로드캐스트가 머무는 논리적인 동네의 경계를 만든다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VLAN을 사용하면 하나의 건물 안에 여러 동네를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 VLAN 10 안의 PC A가 같은 동네에 있는 PC B에게 데이터를 보내려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC A는 애플리케이션이 알려 준 B의 IP 주소를 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 스위치가 실제 프레임을 전달할 때 확인하는 것은 IP 주소가 아니라 목적지 MAC 주소다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소는 아는데 MAC 주소를 모르면 첫 프레임은 누구에게 보내야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;IP 주소만 알면 바로 편지를 건넬 수 있을까?&lt;/b&gt;」라는 질문을 통해 IP Address와 MAC Address의 역할, ARP Request, ARP Reply, ARP Cache를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>802.1q</category>
      <category>Broadcast domain</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>trunk</category>
      <category>vlan</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1083</guid>
      <comments>https://peterica.tistory.com/1083#entry1083comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:05:14 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 04.길이 원을 그리면 편지는 왜 끝없이 늘어날까?</title>
      <link>https://peterica.tistory.com/1081</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0yNBL/dJMcabLNnXt/FhnRsdwAWgH3PZqspiSht0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0yNBL/dJMcabLNnXt/FhnRsdwAWgH3PZqspiSht0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0yNBL/dJMcabLNnXt/FhnRsdwAWgH3PZqspiSht0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0yNBL%2FdJMcabLNnXt%2FFhnRsdwAWgH3PZqspiSht0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞의 글에서 스위치는 출발지 MAC 주소를 보고 장비의 위치를 학습한다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;목적지를 알면 해당 포트로만 프레임을 전달하고, 모르면 들어온 포트를 제외한 다른 포트로 플러딩한다. 덕분에 허브처럼 모든 프레임을 항상 모든 방향으로 보낼 필요가 없어졌다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 스위치 사이의 길이 끊어지면 어떻게 될까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나뿐인 연결이 끊어지면 그 뒤의 네트워크는 통신할 수 없다. 그래서 관리자는 스위치 사이에 여분의 링크를 하나 더 연결하고 싶어진다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Switch A ───── Switch B
    └─────────┘
       여분의 길&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 길이 끊어져도 다른 길을 사용할 수 있으니 더 안전해 보인다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 두 길을 동시에 열어 두면 경로가 원을 만든다. 그리고 이 원 안으로 브로드캐스트 프레임 한 장이 들어오면, 처음 보낸 사람의 손을 떠난 뒤에도 계속 복제되어 돌아다닐 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왜 스위치는 같은 편지가 계속 돌고 있다는 사실을 알아채지 못할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;L2 Loop&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Broadcast Storm&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MAC Flapping&lt;/b&gt;이 발생하는 이유와,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;STP&lt;/b&gt;가 여분의 길을 버리지 않고도 순환을 막는 방법을 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 배달길은 많을수록 좋은 것 아닐까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 개의 동네 우체국이 있다고 생각해 보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 일렬로 연결되어 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;우체국 A ───── 우체국 B ───── 우체국 C&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;A와 B 사이의 길이 끊어지면 A는 나머지 지역과 고립된다. 이를 막기 위해 C와 A 사이에 여분의 길을 만든다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          우체국 A
         /        \
   우체국 B ───── 우체국 C&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 어느 한 길이 끊어져도 우회할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물리적인 이중화 측면에서는 더 안전해졌다. 문제는 세 길이 모두 열려 있을 때다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;민수가 동네의 모든 사람에게 전달할 공지 편지를 우체국 A에 맡겼다고 하자. A는 모두에게 전달하기 위해 B와 C 양쪽으로 편지를 복사한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;B가 받은 편지는 A에서 들어왔으므로 A를 제외한 C 방향으로 전달한다. C도 A에서 받은 편지를 B 방향으로 전달한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;A &amp;rarr; B &amp;rarr; C &amp;rarr; A &amp;rarr; B &amp;rarr; C &amp;rarr; ...
A &amp;rarr; C &amp;rarr; B &amp;rarr; A &amp;rarr; C &amp;rarr; B &amp;rarr; ...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 우체국은 도착한 공지를 다시 다른 길로 보낸다. 원래 한 장이었던 공지는 순환하면서 여러 장으로 늘어난다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여분의 길이 장애를 피하기 위한 안전장치에서, 편지를 끝없이 복제하는 순환로로 바뀐 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ L2 프레임에는 남은 중계 횟수가 없다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터가 전달하는 IP 패킷에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;TTL&lt;/b&gt;(Time To Live)이라는 값이 있다. 라우터를 지날 때마다 값이 줄고, 0이 되면 패킷을 폐기한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 같은 LAN 안에서 스위치가 전달하는 일반적인 이더넷 프레임에는 라우터의 TTL과 같은 중계 횟수 제한이 없다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;IP Packet       : TTL이 있어 L3 순환을 끝낼 수 있음
Ethernet Frame  : 스위치 홉마다 줄어드는 TTL이 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 프레임이 몇 번째로 자신을 방문했는지 기록하지 않는다. 같은 내용의 프레임이 다시 들어와도 새로 들어온 프레임처럼 처리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특히 브로드캐스트 프레임의 목적지 MAC 주소는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;FF:FF:FF:FF:FF:FF&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 이 프레임을 들어온 포트를 제외한 같은 브로드캐스트 도메인의 다른 포트로 플러딩한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;순환 경로가 있으면 다른 포트로 보낸 프레임이 또 다른 스위치를 지나 원래 스위치의 다른 포트로 돌아온다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 돌아온 프레임을 다시 플러딩한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;멈출 TTL도 없고, 이미 처리한 프레임인지 판별하는 일반적인 중복 제거 장치도 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 L2 Loop는 스스로 끝나지 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 한 장의 공지가 동네 전체를 마비시킨다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;순환하는 브로드캐스트 프레임은 각 스위치에서 다시 복제된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프레임의 수는 링크가 감당할 수 있는 한계까지 빠르게 증가한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Broadcast Storm&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;폭풍이 시작되면 네트워크에서는 다음과 같은 일이 함께 나타난다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 링크 대역폭이 소진된다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반복되는 브로드캐스트 복사본이 링크를 채운다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정상적인 유니캐스트 프레임이 지나갈 자리가 줄어들고 응답 지연과 손실이 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 스위치와 호스트의 처리 부담이 커진다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 프레임을 계속 수신하고 복제해야 한다. 같은 브로드캐스트 도메인의 호스트도 쏟아지는 프레임을 받아 확인해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 정상 통신도 함께 느려지거나 멈춘다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브로드캐스트를 발생시킨 하나의 장비만 문제가 되는 것이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 L2 네트워크를 공유하는 여러 장비가 영향을 받는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ㅇ 장애가 매우 빠르게 확산된다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;루프는 새로운 트래픽을 만들기 위해 사용자를 기다리지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미 들어온 프레임을 계속 순환하고 복제하기 때문에 짧은 시간 안에 링크를 포화시킬 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공유된 네트워크에서는 한 장의 공지가 모두의 길을 점유할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 스위치의 주소 장부도 흔들린다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브로드캐스트 스톰만 문제가 되는 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞의 글에서 스위치는 출발지 MAC 주소가 들어온 포트를 보고 FDB를 학습한다고 했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정상적인 환경에서 A의 프레임이 1번 포트로 들어오면 다음처럼 기억한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;MAC A &amp;rarr; Port 1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 루프가 있으면 A가 보낸 같은 프레임이 다른 스위치를 돌아 2번 포트로 다시 들어올 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;처음 관찰 : MAC A &amp;rarr; Port 1
다시 관찰 : MAC A &amp;rarr; Port 2
또 관찰   : MAC A &amp;rarr; Port 1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치 입장에서는 A가 어느 포트에 있는지 계속 바뀌는 것처럼 보인다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 MAC 주소와 포트의 대응 관계가 짧은 시간에 반복해서 변경된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MAC Flapping&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MAC Address Flapping&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MAC Flapping이 발생하면 목적지가 있는 포트를 안정적으로 선택하기 어렵다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;유니캐스트 프레임도 잘못된 방향으로 전달되거나 플러딩이 증가할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현장에서 같은 MAC 주소가 여러 포트 사이를 빠르게 이동한다는 로그가 보인다면 실제 장비 이동뿐 아니라&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;L2 Loop 가능성도 살펴봐야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 여분의 길을 없애면 해결될까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 단순한 해결책은 여분의 링크를 뽑는 것이다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          Switch A
         /        X
   Switch B ───── Switch C&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원이 끊어지므로 프레임이 계속 순환하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 처음에 여분의 길을 만든 이유도 사라진다. 사용 중인 링크 하나가 고장 나면 다시 네트워크가 분리된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 원하는 상태는 다음 두 조건을 함께 만족하는 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정상 상태에서는 프레임이 순환하지 않아야 한다.&lt;/li&gt;
&lt;li&gt;사용 중인 길이 끊어지면 여분의 길을 사용할 수 있어야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물리적인 링크는 남겨 두되, 논리적으로는 원이 없는 하나의 경로만 사용해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 등장한 것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;STP&lt;/b&gt;(Spanning Tree Protocol)다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ STP는 길을 없애지 않고 잠시 쉬게 한다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Spanning Tree는 모든 지점을 연결하면서 순환은 없는 나무 모양의 구조를 뜻한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;STP를 사용하는 스위치들은 서로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;BPDU&lt;/b&gt;(Bridge Protocol Data Unit)를 주고받는다. BPDU에는 어떤 스위치를 중심으로 삼을지, 그 중심까지 어느 경로가 더 좋은지 판단하는 정보가 들어 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치들은 이 정보를 비교해 다음 순서로 루프 없는 길을 만든다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 중심이 될 스위치를 정한다 &amp;mdash; Root Bridge&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Bridge ID&lt;/b&gt;를 비교한다. Bridge ID는 우선순위 값과 스위치를 식별하는 MAC 주소 정보를 바탕으로 구성된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 낮은 Bridge ID를 가진 스위치가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Root Bridge&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          Root Bridge
          Switch A
         /        \
   Switch B ───── Switch C&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 중심으로 가는 가장 좋은 길을 고른다 &amp;mdash; Root Port&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Root Bridge가 아닌 각 스위치는 루트까지 가는 비용을 비교한다. 자신에게 가장 좋은 경로가 되는 포트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Root Port&lt;/b&gt;로 선택한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 각 구간에서 전달을 담당할 포트를 고른다 &amp;mdash; Designated Port&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 링크 구간에서는 루트 방향으로 더 좋은 경로를 제공하는 포트가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Designated Port&lt;/b&gt;가 되어 프레임을 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 남은 중복 경로는 전달하지 않게 한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;루프를 만드는 나머지 포트는 일반 데이터 프레임을 전달하지 않는 상태가 된다. 고전적인 STP에서는 이를 Blocking 상태로 설명한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘날 많이 사용하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;RSTP&lt;/b&gt;(Rapid Spanning Tree Protocol)에서는 역할과 상태 표현이 조금 다르다. 중복 경로의 포트가 Alternate Port 역할을 맡고 Discarding 상태에 있을 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;입문 단계에서 중요한 원리는 같다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;물리적인 링크는 연결된 채로 두고, 논리적인 전달 경로 하나를 쉬게 해 원을 끊는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 사용 중인 길이 끊어지면 어떻게 될까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정상 상태에서 세 스위치는 다음처럼 동작할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          Switch A
         /        \
   Switch B ──X── Switch C
          차단된 경로&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;B와 C 사이의 물리적인 링크는 살아 있지만 일반 데이터 프레임을 전달하지 않는다. 논리적인 구조는 A를 중심으로 한 나무가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이후 A와 C 사이의 사용 중인 링크가 끊어졌다고 하자.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          Switch A
         /        X
   Switch B ───── Switch C
          새 전달 경로&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치들은 토폴로지 변화를 감지하고 경로를 다시 계산한다. 쉬고 있던 B-C 구간이 전달 가능한 상태로 바뀌면 C는 B를 거쳐 A와 통신할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 장애 뒤에 새로운 루프 없는 구조를 만드는 과정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Convergence&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;STP는 여분의 길을 제거하는 프로토콜이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;평상시에는 루프를 막고, 장애가 생기면 남겨 둔 길을 새로운 전달 경로로 사용할 수 있게 하는 프로토콜이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우체국 사이의 원형 배달길&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;L2 Loop&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;스위치 사이에 순환 가능한 전달 경로가 생긴 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;끝없이 복제되는 공지 편지&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Broadcast Storm&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;브로드캐스트 프레임이 급증해 네트워크를 점유하는 현상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한 사람의 위치가 계속 바뀌는 주소 장부&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;MAC Flapping&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 MAC 주소가 여러 포트에서 반복 학습되는 현상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;남은 중계 횟수가 없는 봉투&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;No TTL at L2&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;이더넷 프레임에 스위치 홉 수를 제한하는 TTL이 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;우체국이 주고받는 경로 정보&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;BPDU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;STP 계산을 위해 스위치가 교환하는 제어 프레임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;기준이 되는 중앙 우체국&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Root Bridge&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;STP 트리의 기준점으로 선출된 스위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;중심까지 가는 가장 좋은 길&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Root Port&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;비루트 스위치가 루트로 가기 위해 선택한 최적 포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;쉬게 한 여분의 길&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Blocking / Discarding&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;루프를 막기 위해 일반 프레임을 전달하지 않는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;장애 뒤 새 길을 정하는 과정&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Convergence&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;토폴로지 변화 뒤 전달 구조를 다시 안정화하는 과정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;STP가 해결하는 문제를 한 문장으로 정리하면 다음과 같다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;물리적 이중화(Redundancy)는 유지하고 논리적 토폴로지에서는 순환을 제거한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 따라가 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 삼각형 구조를 종이에 그려 보자.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;          Switch A
         /        \
   Switch B ───── Switch C&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ STP가 없을 때&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A가 브로드캐스트 프레임을 B와 C로 보낸다.&lt;/li&gt;
&lt;li&gt;B는 C로, C는 B로 프레임을 보낸다.&lt;/li&gt;
&lt;li&gt;B와 C가 받은 복사본은 다시 A 방향으로 전달될 수 있다.&lt;/li&gt;
&lt;li&gt;프레임을 폐기할 TTL이 없으므로 순환과 복제가 계속된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 단계마다 프레임이 어느 링크로 이동하는지 화살표로 그려 보면 한 장의 프레임이 왜 여러 복사본으로 늘어나는지 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ STP가 있을 때&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A를 Root Bridge라고 가정한다.&lt;/li&gt;
&lt;li&gt;B와 C는 A로 직접 연결된 포트를 Root Port로 선택한다.&lt;/li&gt;
&lt;li&gt;B-C 링크의 한쪽 포트가 일반 프레임을 전달하지 않는 상태가 된다.&lt;/li&gt;
&lt;li&gt;논리적인 전달 구조에서 원이 사라진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux 브리지를 사용하는 환경이라면 다음 명령으로 브리지와 포트 정보를 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;ip -details link show type bridge
bridge -details link show&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출력에서 브리지의&lt;span&gt;&amp;nbsp;&lt;/span&gt;stp_state와 포트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;state,&lt;span&gt;&amp;nbsp;&lt;/span&gt;priority,&lt;span&gt;&amp;nbsp;&lt;/span&gt;cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;등을 살펴볼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시스템과&lt;span&gt;&amp;nbsp;&lt;/span&gt;iproute2&lt;span&gt;&amp;nbsp;&lt;/span&gt;버전에 따라 출력 형식은 다를 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순 호스트에 Linux 브리지가 구성되어 있지 않다면 결과가 비어 있어도 정상이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제 운영 네트워크에서 STP를 켜거나 끄는 변경은 전체 L2 통신에 영향을 줄 수 있으므로 관찰 목적만으로 설정을 변경해서는 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여분의 링크는 가용성을 높이지만 동시에 L2 Loop를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;이더넷 프레임에는 라우터의 TTL과 같은 중계 횟수 제한이 없다.&lt;/li&gt;
&lt;li&gt;루프 안의 브로드캐스트는 반복해서 복제되어 Broadcast Storm을 만들 수 있다.&lt;/li&gt;
&lt;li&gt;같은 출발지 MAC 주소가 여러 포트로 돌아오면 MAC Flapping이 발생한다.&lt;/li&gt;
&lt;li&gt;STP는 BPDU를 교환하고 Root Bridge를 기준으로 루프 없는 논리적 트리를 만든다.&lt;/li&gt;
&lt;li&gt;중복 링크는 평상시에 전달을 쉬고, 장애 시 재수렴을 거쳐 새로운 경로가 될 수 있다.&lt;/li&gt;
&lt;li&gt;STP와 RSTP는 포트 역할과 상태 표현에 차이가 있지만 루프를 제거한다는 목적은 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 용어:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Redundancy,&lt;span&gt;&amp;nbsp;&lt;/span&gt;L2 Loop,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Broadcast Storm,&lt;span&gt;&amp;nbsp;&lt;/span&gt;MAC Flapping,&lt;span&gt;&amp;nbsp;&lt;/span&gt;BPDU,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Bridge ID,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Root Bridge,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Root Port,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Designated Port,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Blocking,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Discarding,&lt;span&gt;&amp;nbsp;&lt;/span&gt;STP,&lt;span&gt;&amp;nbsp;&lt;/span&gt;RSTP,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Convergence&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;좋은 네트워크는 여분의 길을 없애지 않는다. 평상시에는 순환하지 않게 하고, 필요할 때만 새 길로 바꾼다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;STP는 스위치 사이의 길이 원을 그리지 않도록 만들어 하나의 L2 네트워크를 안전하게 유지한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 조직이 커지면 또 다른 문제가 생긴다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;회사 전체가 하나의 동네라면 한 부서의 브로드캐스트도 다른 모든 부서에 전달된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;서로 다른 조직과 보안 구역을 나누려면 스위치를 물리적으로 따로 사야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;한 건물을 여러 동네로 나눌 수 있을까?&lt;/b&gt;」라는 질문을 통해&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Broadcast Domain, VLAN, Access Port, Trunk와 802.1Q Tag가 필요한 이유를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>blocking</category>
      <category>BPDU</category>
      <category>Broadcast Storm</category>
      <category>MAC Flapping</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1081</guid>
      <comments>https://peterica.tistory.com/1081#entry1081comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:04:40 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 03. 스위치는 어떻게 편지가 갈 길을 기억할까?</title>
      <link>https://peterica.tistory.com/1082</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjckGZ/dJMcadCCuEk/wB3GhEykOl2KWDuk2KJK6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjckGZ/dJMcadCCuEk/wB3GhEykOl2KWDuk2KJK6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjckGZ/dJMcadCCuEk/wB3GhEykOl2KWDuk2KJK6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjckGZ%2FdJMcadCCuEk%2FwB3GhEykOl2KWDuk2KJK6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞의 글에서는 여러 컴퓨터가 하나의 통신 매체를 공유할 때 생기는 문제를 살펴보았다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;허브는 한 포트에서 받은 신호를 다른 모든 포트로 반복한다. 누구에게 가는 데이터인지 판단하지 않기 때문에 하나의 충돌 도메인을 모두가 공유한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 중간 장치가 편지의 목적지를 읽고 필요한 방향으로만 보내면 어떨까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제는 중간 장치가 처음부터 각 컴퓨터의 위치를 알지 못한다는 점이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새 컴퓨터는 언제든 연결될 수 있고, 다른 포트로 옮겨질 수도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 주소와 위치를 사람이 미리 입력하는 방식은 불편하고 확장하기 어렵다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 이 문제를 의외로 단순한 방법으로 해결한다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;편지를 보낸 사람의 위치를 보고 길을 배운다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 스위치가 MAC 주소를 학습하고, 목적지를 아는 프레임과 모르는 프레임을 어떻게 다르게 처리하는지 살펴본다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우체국에 주소별 창구가 생겼다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네 집이 하나의 동네 우체국에 연결되어 있다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;민수의 집 ── 1번 길 ─┐
영희의 집 ── 2번 길 ─┤
                      동네 우체국
철수의 집 ── 3번 길 ─┤
지수의 집 ── 4번 길 ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 우체국에는 네 개의 배달길이 있지만 처음에는 어느 길 끝에 누가 사는지 모른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;민수가 영희에게 편지를 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;편지는 1번 길을 통해 우체국에 들어온다. 봉투에는 두 가지 정보가 적혀 있다.&lt;/p&gt;
&lt;pre class=&quot;&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;보낸 사람: 민수
받는 사람: 영희&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우체국은 아직 영희가 어느 길에 있는지 모른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 한 가지는 새로 알게 되었다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;민수의 편지가 1번 길에서 들어왔으니, 민수에게 가는 길은 1번이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우체국은 이 정보를 주소 장부에 적는다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 34.6512%; height: 67px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; height: 19px; width: 51.0067%;&quot;&gt;사람&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; height: 19px; width: 48.6577%;&quot;&gt;연결된 길&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;background-color: #efefef; height: 19px; width: 51.0067%;&quot;&gt;민수&lt;/td&gt;
&lt;td style=&quot;height: 19px; width: 48.6577%;&quot;&gt;1번&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치의 MAC 주소 학습도 이 장면과 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 스위치는 받는 사람이 아니라 보낸 사람을 보고 배운다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이더넷에서 데이터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;프레임&lt;/b&gt;(Ethernet Frame)이라는 단위로 전달된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프레임의 헤더에는 대표적으로 다음 두 주소가 들어 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source MAC Address      : 보낸 장비의 MAC 주소
Destination MAC Address : 받을 장비의 MAC 주소&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 프레임이 들어오면 먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;출발지 MAC 주소&lt;/b&gt;(Source MAC Address)를 확인한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 다음 관계를 기록한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;이 프레임의 출발지 MAC 주소는 이 포트 방향에 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 프레임이 1번 포트로 들어왔다고 하자.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source MAC      AA:AA:AA:AA:AA:AA
Destination MAC BB:BB:BB:BB:BB:BB
Ingress Port    1&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 주소 테이블에 다음 정보를 추가한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 40.6977%; height: 87px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; width: 28.8063%;&quot;&gt;MAC 주소&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; width: 26.1938%;&quot;&gt;포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 28.8063%;&quot;&gt;AA:AA:AA:AA:AA:AA&lt;/td&gt;
&lt;td style=&quot;width: 26.1938%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 목적지 주소&lt;span&gt;&amp;nbsp;&lt;/span&gt;BB:...를 보고 B가 있는 포트를 배우지 않는다. 목적지 주소에는 B가 실제로 어느 포트에 있는지 증명할 정보가 없기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 출발지 A의 프레임이 1번 포트로 직접 들어왔다는 사실은 A가 1번 포트 방향에 있다는 관찰 결과다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 스위치는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;출발지 주소로 위치를 학습하고, 목적지 주소로 보낼 길을 결정한다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 목적지를 모르면 어떻게 해야 할까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 A의 위치를 배웠지만 아직 B의 위치는 모른다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다고 프레임을 버리면 첫 통신은 영원히 시작할 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 프레임이 들어온 포트를 제외한 나머지 모든 포트로 복사해 보낸다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;             ┌─ 2번 포트로 전송
1번 포트 ── 스위치 ─ 3번 포트로 전송
             └─ 4번 포트로 전송&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Unknown Unicast Flooding&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unicast는 한 수신인에게 보내는 프레임이라는 뜻이다.&lt;/li&gt;
&lt;li&gt;Unknown은 목적지 MAC 주소가 테이블에 없다는 뜻이다.&lt;/li&gt;
&lt;li&gt;Flooding은 들어온 포트를 제외한 같은 네트워크의 다른 포트로 프레임을 보내는 동작이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;B가 3번 포트에 연결되어 있다면 B는 프레임을 받아들인다. 목적지 주소가 자신과 다른 C와 D는 프레임을 받아들이지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;허브와 결과가 비슷해 보이지만 차이가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;허브는 주소를 이해하지 못해 항상 신호를 반복한다. 스위치는 주소를 확인했지만 아직 목적지를 배우지 못했기 때문에 일시적으로 플러딩한다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;허브는 몰라도 계속 모두에게 보내고, 스위치는 모를 때만 모두에게 묻는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 답장이 돌아오면 길이 완성된다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;B가 A에게 답장을 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;답장 프레임은 3번 포트로 스위치에 들어온다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Source MAC      BB:BB:BB:BB:BB:BB
Destination MAC AA:AA:AA:AA:AA:AA
Ingress Port    3&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 출발지 B의 주소를 보고 새로운 정보를 학습한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; width: 30.9302%;&quot;&gt;MAC 주소&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff; width: 68.9535%;&quot;&gt;포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 30.9302%;&quot;&gt;AA:AA:AA:AA:AA:AA&lt;/td&gt;
&lt;td style=&quot;width: 68.9535%;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef; width: 30.9302%;&quot;&gt;BB:BB:BB:BB:BB:BB&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9; width: 68.9535%;&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 목적지 A의 주소가 테이블에 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 답장 프레임을 1번 포트로만 보낸다. 2번과 4번 포트에는 전달하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 동작을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Known Unicast Forwarding&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;B의 답장 &amp;rarr; 3번 포트 &amp;rarr; 스위치 &amp;rarr; 1번 포트 &amp;rarr; A&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 편지에서는 목적지를 몰라 여러 방향으로 보냈지만, 한 번의 왕복이 일어난 뒤에는 A와 B의 위치를 모두 알게 되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이후 두 장비 사이의 프레임은 필요한 포트로만 전달할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 스위치의 주소 장부는 무엇일까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치가 MAC 주소와 포트의 관계를 저장하는 장부를 보통&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MAC Address Table&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;환경과 문서에 따라 다음 이름도 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FDB(Forwarding Database)&lt;/b&gt;: 프레임을 어느 포트로 전달할지 판단하는 데이터베이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CAM Table&lt;/b&gt;: 스위치가 주소 검색에 사용하는 메모리 방식에서 유래한 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 용어는 문맥에 따라 조금씩 강조점이 다르지만, 입문 단계에서는 다음 장부를 가리킨다고 이해해도 충분하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;MAC Address                    Port
AA:AA:AA:AA:AA:AA              1
BB:BB:BB:BB:BB:BB              3
CC:CC:CC:CC:CC:CC              2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 이 테이블을 사용해 목적지 MAC 주소가 어느 포트에 있는지 찾는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 장부는 관리자가 모든 항목을 직접 작성하지 않아도 된다. 스위치가 실제 프레임을 관찰하며 동적으로 채운다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MAC Learning&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 장부를 영원히 믿어도 될까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터의 위치는 바뀔 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;노트북이 다른 포트에 연결될 수도 있고, 가상 머신이 다른 호스트로 이동할 수도 있다. 그런데 스위치가 예전 정보를 영원히 기억한다면 프레임을 잘못된 포트로 보낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 동적으로 학습한 MAC 주소 항목에는 유효 시간이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일정 시간 동안 해당 출발지에서 프레임이 들어오지 않으면 스위치는 오래된 항목을 삭제한다. 이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Aging&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;프레임 관찰
   &amp;darr;
MAC 주소와 포트 학습
   &amp;darr;
일정 시간 동안 다시 관찰되지 않음
   &amp;darr;
테이블에서 제거&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;삭제된 주소가 다시 통신하면 처음처럼 출발지 주소를 보고 새 위치를 학습한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이미 등록된 MAC 주소가 다른 포트에서 들어오면 스위치는 최신 관찰에 맞춰 포트 정보를 갱신할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치의 테이블은 고정된 주소록이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 관찰한 네트워크의 모습을 잠시 기억한 지도&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 브로드캐스트는 알고 있어도 모두에게 보낸다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Unknown Unicast는 목적지를 모르기 때문에 여러 포트로 보낸다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 처음부터 모두에게 전달하기 위한 프레임도 있다. 목적지 MAC 주소가 다음과 같은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Broadcast Address&lt;/b&gt;인 경우다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;FF:FF:FF:FF:FF:FF&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 브로드캐스트 프레임을 들어온 포트를 제외한 같은 브로드캐스트 도메인의 다른 포트로 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 Unknown Unicast와 Broadcast의 차이를 구분해야 한다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 54.8837%; height: 101px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;여러 포트로 보내는 이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Unknown Unicast&lt;/td&gt;
&lt;td&gt;한 수신인에게 보내려 하지만 그 위치를 모름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Broadcast&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;처음부터 같은 네트워크의 모두에게 보내려 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치가 MAC 주소를 모두 학습해도 브로드캐스트를 특정 포트 하나로만 보낼 수는 없다. 모든 장비에게 전달하는 것이 프레임의 목적이기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 스위치는 불필요한 유니캐스트 전달을 줄이지만 브로드캐스트의 범위까지 자동으로 나누지는 않는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 동작을 실제 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;네트워크 용어&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;봉투&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Ethernet Frame&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이더넷 구간에서 전달되는 데이터 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;보낸 사람 주소&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Source MAC Address&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;스위치가 장비의 위치를 학습하는 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;받는 사람 주소&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Destination MAC Address&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스위치가 출력 포트를 결정할 때 조회하는 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;편지가 들어온 길&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Ingress Port&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;프레임이 스위치로 들어온 포트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;주소별 배달 장부&lt;/td&gt;
&lt;td&gt;&lt;b&gt;MAC Address Table / FDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MAC 주소와 포트의 대응 관계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;보낸 사람의 위치 기록&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;MAC Learning&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;출발지 MAC 주소를 수신 포트와 연결해 저장하는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;모르는 주소를 여러 길로 전달&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Unknown Unicast Flooding&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;목적지 항목이 없을 때 다른 포트로 전송하는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;아는 주소의 길만 선택&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;b&gt;Known Unicast Forwarding&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;테이블에 있는 목적지 포트로만 전달하는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;오래된 주소 기록 삭제&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Aging&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일정 시간 사용되지 않은 동적 항목을 제거하는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치의 전체 판단 순서는 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;1. 프레임을 받는다.
2. Source MAC Address를 Ingress Port와 함께 학습한다.
3. Destination MAC Address를 FDB에서 찾는다.
4-1. 목적지를 알면 해당 포트로 Forwarding한다.
4-2. 목적지를 모르면 들어온 포트를 제외하고 Flooding한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 다섯 단계가 스위치의 기본 동작을 이해하는 뼈대다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 따라가 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같이 세 장비가 스위치에 연결되어 있다고 가정해 보자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;A(AA:AA) ── Port 1
B(BB:BB) ── Port 2 ── Switch
C(CC:CC) ── Port 3&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 FDB가 비어 있다. 아래 프레임이 차례로 들어올 때 테이블과 출력 포트가 어떻게 바뀌는지 적어 보자.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;순서&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;들어온 포트&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;출발지&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;목적지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;AA:AA&lt;/td&gt;
&lt;td&gt;BB:BB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;BB:BB&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;AA:AA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;CC:CC&lt;/td&gt;
&lt;td&gt;AA:AA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정답은 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ 첫 번째 프레임&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AA:AA &amp;rarr; Port 1을 학습한다.&lt;/li&gt;
&lt;li&gt;BB:BB는 아직 모르므로 Port 2와 Port 3으로 플러딩한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ 두 번째 프레임&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BB:BB &amp;rarr; Port 2를 학습한다.&lt;/li&gt;
&lt;li&gt;AA:AA는 Port 1에 있으므로 Port 1로만 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ 세 번째 프레임&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CC:CC &amp;rarr; Port 3을 학습한다.&lt;/li&gt;
&lt;li&gt;AA:AA는 Port 1에 있으므로 Port 1로만 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Linux가 브리지로 동작하는 환경이라면 다음 명령으로 커널의 FDB를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;bridge fdb show&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 브리지의 항목만 보고 싶다면 다음처럼 확인한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;bridge fdb show br br0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;표시 형식과 지원 옵션은 Linux 및&lt;span&gt;&amp;nbsp;&lt;/span&gt;iproute2&lt;span&gt;&amp;nbsp;&lt;/span&gt;버전에 따라 다를 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반 PC가 단순한 호스트로만 동작하고 브리지가 없다면 학습된 FDB가 보이지 않는 것이 정상이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스위치는 출발지 MAC 주소와 프레임이 들어온 포트를 연결해 학습한다.&lt;/li&gt;
&lt;li&gt;목적지 MAC 주소는 전달할 출력 포트를 결정하는 데 사용한다.&lt;/li&gt;
&lt;li&gt;목적지를 알면 해당 포트로만 Known Unicast Forwarding한다.&lt;/li&gt;
&lt;li&gt;목적지를 모르면 들어온 포트를 제외한 다른 포트로 Unknown Unicast Flooding한다.&lt;/li&gt;
&lt;li&gt;Broadcast Frame은 처음부터 모두에게 보내는 프레임이므로 학습이 끝나도 여러 포트로 전달된다.&lt;/li&gt;
&lt;li&gt;동적으로 학습한 항목은 Aging을 통해 오래된 정보가 되기 전에 제거된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 용어:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ethernet Frame,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Source MAC,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Destination MAC,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Ingress Port,&lt;span&gt;&amp;nbsp;&lt;/span&gt;MAC Address Table,&lt;span&gt;&amp;nbsp;&lt;/span&gt;FDB,&lt;span&gt;&amp;nbsp;&lt;/span&gt;CAM Table,&lt;span&gt;&amp;nbsp;&lt;/span&gt;MAC Learning,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Forwarding,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Flooding,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Aging&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 목적지를 보고 배우지 않는다. 출발지를 보고 길을 배우고, 목적지를 보고 그 길을 선택한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스위치는 프레임을 관찰하며 주소와 포트를 학습한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;덕분에 이미 아는 목적지의 프레임은 필요한 길로만 보낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 스위치 사이에 여분의 길을 하나 더 만들면 더 안전해질까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 길이 끊어졌을 때 다른 길을 사용할 수 있으니 좋아 보인다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 두 길이 원을 만들면 브로드캐스트 프레임은 어느 지점에서 멈춰야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 「&lt;b&gt;길이 원을 그리면 편지는 왜 끝없이 늘어날까?&lt;/b&gt;」라는 질문을 통해 L2 Loop, Broadcast Storm, MAC Flapping과 STP가 필요한 이유를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>FDB</category>
      <category>Flooding</category>
      <category>MAC Learning</category>
      <category>Network</category>
      <category>PETERICA</category>
      <category>switch</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1082</guid>
      <comments>https://peterica.tistory.com/1082#entry1082comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:03:19 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 02. 모두가 동시에 말하면 누가 들을 수 있을까?</title>
      <link>https://peterica.tistory.com/1080</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOJu6/dJMcag7hWmD/x1pFkOLLvKPiuksavuW1k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOJu6/dJMcag7hWmD/x1pFkOLLvKPiuksavuW1k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOJu6/dJMcag7hWmD/x1pFkOLLvKPiuksavuW1k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOJu6%2FdJMcag7hWmD%2Fx1pFkOLLvKPiuksavuW1k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 글에서는 모든 컴퓨터를 서로 직접 연결하는 방식의 한계를 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터가 &lt;code&gt;N대&lt;/code&gt;라면 모든 컴퓨터를 직접 연결하는 데 필요한 선은 &lt;code&gt;N &amp;times; (N - 1) &amp;divide; 2&lt;/code&gt;개까지 늘어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 각 컴퓨터를 공통의 중간 지점에 연결해 길을 함께 쓰는 구조를 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선의 수는 크게 줄었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이번에는 다른 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 길을 모두가 함께 사용하는데 두 컴퓨터가 동시에 데이터를 보내면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회의실에서 여러 사람이 동시에 말하면 누구의 말도 제대로 알아듣기 어려운 것처럼, 공유된 통신 매체에서도 신호가 겹칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글의 질문은 단순하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길을 공유하면서도 서로의 말을 방해하지 않으려면 어떤 질서가 필요할까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 하나의 배달 통로를 여러 집이 함께 쓴다면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 집이 하나의 긴 배달 통로를 공유한다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수의 집 ──┬── 영희의 집 ──┬── 철수의 집 ──┬── 지수의 집
             하나의 공유된 배달 통로&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민수가 영희에게 편지를 보내기 위해 통로에 운반 상자를 밀어 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 통로는 모든 집 앞을 지나므로 철수와 지수도 상자를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 봉투에 적힌 수신인이 영희라면 영희만 편지를 받고 나머지는 지나가게 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기의 공유형 이더넷도 이와 비슷한 성격을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 통신 매체에 연결된 장비들은 그 매체를 함께 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 장비가 프레임을 보내면 같은 구간의 다른 장비도 신호를 접할 수 있었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 장비는 목적지 MAC 주소를 확인해 자신에게 온 프레임인지 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서는 한 번에 한 장비가 말할 때는 문제가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 두 장비가 거의 같은 순간에 전송을 시작할 때다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 두 개의 상자가 좁은 통로에서 부딪혔다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민수와 철수가 동시에 통로로 상자를 밀었다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수의 상자 ─────&amp;rarr;  충돌  &amp;larr;───── 철수의 상자&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상자가 부딪히면 어느 쪽도 목적지까지 온전하게 갈 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유된 전기적 통신 매체에서도 두 신호가 동시에 전송되면 신호가 겹쳐 원래 데이터를 해석할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;충돌(Collision)&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌이 발생하면 두 프레임 가운데 하나를 골라 전달할 수 있는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌한 전송은 실패한 것으로 보고 다시 보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 같은 통신 매체에서 서로의 전송과 충돌할 수 있는 범위를 &lt;b&gt;충돌 도메인(Collision Domain)&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브에 여러 컴퓨터가 연결된 환경을 생각하면 이해하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;컴퓨터 A ─┐
컴퓨터 B ─┼─ 허브
컴퓨터 C ─┤
컴퓨터 D ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브는 들어온 신호를 이해해 목적지를 고르는 장치가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 포트로 들어온 신호를 나머지 포트로 반복해 내보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 허브에 연결된 장비들은 하나의 공유된 길을 사용한다. A와 C가 동시에 전송하면 서로의 신호가 충돌할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 대가 연결되어 있어도 동시에 자유롭게 사용할 수 있는 네 개의 길이 생긴 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 길을 네 대가 나누어 쓰는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 먼저 듣고, 비어 있으면 말한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들이 회의실에서 대화할 때는 보통 다른 사람이 말하고 있는지 먼저 듣는다. 아무도 말하지 않을 때 말을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유형 이더넷에도 이와 비슷한 규칙이 있었다. 바로 &lt;b&gt;CSMA/CD&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름은 길지만 동작은 네 단계로 나누어 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 먼저 듣는다 &amp;mdash; Carrier Sense&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비는 전송하기 전에 통신 매체에 다른 신호가 흐르고 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가 사용 중이면 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 모두 같은 길을 사용할 수 있다 &amp;mdash; Multiple Access&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 전송 매체를 여러 장비가 공동으로 사용한다. 특정 장비가 길을 영구히 소유하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 비어 있으면 보낸다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 전송이 들리지 않으면 프레임을 보내기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 충돌을 알아채면 멈춘다 &amp;mdash; Collision Detection&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송 중 충돌을 감지하면 계속 보내지 않는다. 충돌 사실을 같은 구간에 알리고 전송을 중단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 각 장비는 임의의 시간 동안 기다렸다가 다시 시도한다. 이를 &lt;b&gt;임의 대기(Random Backoff)&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;듣기 &amp;rarr; 비어 있으면 전송 &amp;rarr; 충돌 감지 &amp;rarr; 임의 시간 대기 &amp;rarr; 재전송&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 임의로 기다린다는 점이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌한 두 장비가 똑같은 시간에 다시 보내면 또 충돌한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 시간만큼 기다리면 다음 시도에서는 한 장비가 먼저 보낼 가능성이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌이 반복될수록 기다릴 수 있는 시간의 범위도 넓어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 혼잡할 때 모두가 계속 달려드는 대신 전송 시도를 늦추는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 규칙이 있어도 사람이 많아지면 느려진다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSMA/CD는 충돌을 없애는 규칙이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌 가능성이 있는 공유 매체에서 &lt;b&gt;충돌을 다루는 규칙&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 적고 전송이 드물면 대부분 매체가 비어 있으므로 바로 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 사용자가 많아지고 전송량이 늘면 상황이 달라진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;누군가 전송 중이라 기다리는 시간이 늘어난다.&lt;/li&gt;
&lt;li&gt;비슷한 시점에 전송을 시작할 가능성이 커진다.&lt;/li&gt;
&lt;li&gt;충돌한 프레임을 다시 보내느라 같은 데이터를 반복해서 전송한다.&lt;/li&gt;
&lt;li&gt;재시도가 늘면서 실제 데이터가 사용할 수 있는 시간은 줄어든다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도로를 여러 사람이 공유하면서 사고가 날 때마다 출발지로 돌아가 다시 출발하는 것과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 많을수록 길의 운송 능력보다 충돌과 대기에 더 많은 시간을 쓰게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결되어 있다는 사실은 변하지 않는다. 모든 컴퓨터는 여전히 같은 매체에 연결되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 연결되어 있다고 해서 항상 효율적으로 통신할 수 있는 것은 아니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유는 자원을 절약하지만, 공유 범위가 커질수록 서로 기다리고 충돌을 복구하는 비용도 커진다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 사람이 모든 편지를 보게 되는 문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유 매체에는 충돌 외에 또 하나의 비효율이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브는 목적지를 판단하지 않으므로 들어온 신호를 연결된 다른 포트로 내보낸다. 영희에게 보내는 프레임도 철수와 지수 쪽으로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 컴퓨터는 목적지 MAC 주소를 보고 자신에게 온 프레임이 아니라면 받아들이지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 신호가 불필요한 방향으로 전달되는 것 자체는 피할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 두 현상을 구분해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;충돌&lt;/b&gt;: 여러 장비가 공유 매체에서 동시에 전송해 신호가 겹치는 현상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브로드캐스트&lt;/b&gt;: 송신자가 같은 네트워크의 모든 장비에게 전달해 달라고 보낸 프레임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브가 모든 포트로 신호를 반복하는 것과, 송신자가 의도적으로 모두에게 브로드캐스트하는 것은 같은 말이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 허브 환경에서는 일반적인 단일 목적지 프레임도 다른 장비가 연결된 방향까지 퍼진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적지를 아는 중간 장치가 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 자연스럽게 다음 질문이 생긴다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간 장치가 수신인의 위치를 기억해서 필요한 길로만 보내면 되지 않을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 오늘날에도 이더넷 충돌이 자주 일어날까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 일반적인 유선 LAN에서는 충돌을 거의 보지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 보통 허브가 아니라 스위치의 독립된 포트에 연결된다. 각 포트는 다른 포트와 분리된 전송 구간을 만들고, 송신과 수신을 동시에 할 수 있는 &lt;b&gt;전이중(Full Duplex)&lt;/b&gt; 방식으로 동작한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;컴퓨터 A &amp;larr;──── 송신과 수신 ────&amp;rarr; 스위치
컴퓨터 B &amp;larr;──── 송신과 수신 ────&amp;rarr; 스위치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A와 B는 스위치까지 각자의 링크를 사용하므로 과거의 허브처럼 하나의 전기적 매체에서 경쟁하지 않는다. 전이중 링크에서는 CSMA/CD를 사용하지 않으며 정상적인 상황에서 충돌도 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 CSMA/CD는 최신 LAN의 사용법이라기보다, &lt;b&gt;공유 매체가 가진 한계와 스위치가 필요해진 이유를 이해하는 열쇠&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무선 LAN은 여전히 공기라는 매체를 여러 장비가 공유한다. 다만 전송하면서 충돌을 감지하기 어려워 충돌을 피하려는 CSMA/CA 계열의 방법을 사용한다. 공유 자원에는 여전히 질서가 필요하다는 원리는 남아 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 살펴본 장면을 네트워크 용어와 연결하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네트워크 용어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 집이 함께 쓰는 통로&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Shared Medium&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 장비가 하나의 전송 매체를 공유하는 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;들어온 상자를 모든 방향으로 전달&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Hub / Repeater&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;신호를 해석하지 않고 다른 포트로 반복하는 장치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;좁은 통로에서 상자가 부딪힘&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Collision&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동시 전송된 신호가 겹쳐 프레임이 손상되는 현상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;서로 부딪힐 수 있는 배달 구역&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Collision Domain&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;같은 충돌의 영향을 받는 네트워크 범위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;한쪽이 말할 때 다른 쪽은 기다림&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Half Duplex&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;송신과 수신을 동시에 수행하지 못하는 통신 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;먼저 듣고 충돌하면 다시 시도&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;CSMA/CD&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;공유형 이더넷에서 매체 접근과 충돌을 처리하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;양방향 전용 통로&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Full Duplex&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;송신과 수신을 동시에 수행하는 통신 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 기반 이더넷은 하나의 &lt;b&gt;Shared Medium&lt;/b&gt;과 &lt;b&gt;Collision Domain&lt;/b&gt;을 여러 장비가 공유하는 &lt;b&gt;Half Duplex&lt;/b&gt; 환경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 환경에서는 누군가 전송 중일 때 다른 장비가 기다려야 하며, 동시 전송이 발생하면 CSMA/CD로 충돌을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 포트마다 충돌 도메인을 분리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 Full Duplex가 사용되면 송신 경로와 수신 경로가 분리되므로 충돌을 조정하기 위한 CSMA/CD도 필요하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 허브에서 스위치로의 변화는 단순히 더 빠른 장비로 교체한 것이 아니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 충돌 도메인을 모두가 공유하던 구조에서, 장비마다 독립된 통신 구간을 사용하는 구조로 바뀐 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 관찰해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 Linux 컴퓨터에서는 현재 링크가 전이중으로 동작하는지 확인해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 인터페이스 이름을 찾는다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;ip -brief link&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유선 인터페이스가 &lt;code&gt;eth0&lt;/code&gt;이라면 다음과 같이 링크 정보를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo ethtool eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 다음 항목을 살펴본다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Speed: 1000Mb/s
Duplex: Full
Link detected: yes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Duplex: Full&lt;/code&gt;은 송신과 수신이 분리된 전이중 링크라는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스의 통계도 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;ip -s link show eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대적인 스위치 환경이라면 충돌 수치는 보통 0이다. 드라이버와 운영체제에 따라 충돌 통계를 표시하는 위치나 지원 여부는 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 충돌을 억지로 재현하는 일이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지금 충돌이 보이지 않는 이유가 스위치와 전이중 링크가 공유 매체의 충돌 범위를 없앴기 때문이라는 점&lt;/b&gt;을 확인하는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유형 통신 매체에서는 여러 장비의 동시 전송이 충돌할 수 있다.&lt;/li&gt;
&lt;li&gt;허브는 목적지를 판단하지 않고 들어온 신호를 다른 포트로 반복한다.&lt;/li&gt;
&lt;li&gt;CSMA/CD는 먼저 듣고 전송하며, 충돌하면 임의의 시간 뒤 다시 시도하는 규칙이다.&lt;/li&gt;
&lt;li&gt;참여자와 전송량이 늘면 대기, 충돌, 재전송 비용도 커진다.&lt;/li&gt;
&lt;li&gt;충돌과 브로드캐스트는 서로 다른 현상이다.&lt;/li&gt;
&lt;li&gt;현대의 스위치 기반 전이중 이더넷에서는 일반적으로 CSMA/CD와 충돌이 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 용어: &lt;code&gt;Shared Medium&lt;/code&gt;, &lt;code&gt;Hub&lt;/code&gt;, &lt;code&gt;Repeater&lt;/code&gt;, &lt;code&gt;Collision&lt;/code&gt;, &lt;code&gt;Collision Domain&lt;/code&gt;, &lt;code&gt;Half Duplex&lt;/code&gt;, &lt;code&gt;CSMA/CD&lt;/code&gt;, &lt;code&gt;Random Backoff&lt;/code&gt;, &lt;code&gt;Full Duplex&lt;/code&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 길을 공유하는 것만으로는 충분하지 않았다. 누구에게 가는 데이터인지 알고, 필요한 길로만 보내는 장치가 필요했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브는 데이터를 받으면 연결된 모든 방향으로 신호를 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 동네 우체국은 모든 편지를 모든 집에 배달하지 않는다. 어느 집이 어느 방향에 있는지 알고 필요한 배달길을 선택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크의 중간 장치도 수신인의 위치를 기억할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 &lt;b&gt;「스위치는 어떻게 편지가 갈 길을 기억할까?」&lt;/b&gt;라는 질문을 통해 MAC 주소 학습과 전달, 그리고 목적지를 모를 때의 플러딩을 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Collision</category>
      <category>Collision domain</category>
      <category>Full duplex</category>
      <category>Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1080</guid>
      <comments>https://peterica.tistory.com/1080#entry1080comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:01:07 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 01. 컴퓨터 두 대를 연결하면 끝이 아니었을까?</title>
      <link>https://peterica.tistory.com/1057</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2cOJJ/dJMcabETOvB/I33BIa7TfHfG6I3keActM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2cOJJ/dJMcabETOvB/I33BIa7TfHfG6I3keActM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2cOJJ/dJMcabETOvB/I33BIa7TfHfG6I3keActM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2cOJJ%2FdJMcabETOvB%2FI33BIa7TfHfG6I3keActM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;목차:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/817&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;br /&gt;TCP/IP를 우편 시스템으로 이해하기 &amp;mdash; Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터 두 대가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 컴퓨터 사이에 선을 하나 연결하면 데이터를 주고받을 수 있다. 한쪽에서 보낸 신호는 그 선을 따라 다른 쪽에 도착한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 네트워크는 이것으로 완성된 것 아닐까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 대만 생각하면 그렇다. 문제는 세 번째 컴퓨터가 들어오는 순간 시작된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 스위치나 라우터의 동작부터 설명하지 않는다. 그보다 먼저, 왜 컴퓨터를 직접 연결하는 방식만으로는 충분하지 않았는지를 살펴보려고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크의 첫 번째 문제는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;연결할 수 있는가&lt;/b&gt;가 아니었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;늘어나는 연결을 감당할 수 있는가&lt;/b&gt;였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 두 사람이라면 전용 배달길 하나로 충분하다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 집에 사는 사람이 서로 편지를 주고받는다고 생각해 보자.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 집 사이에 전용 배달길을 하나 만들면 된다.&lt;/p&gt;
&lt;pre class=&quot;&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;민수의 집 ───────── 영희의 집&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 길에는 두 사람의 편지만 다닌다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어느 길로 보내야 할지 고민할 필요가 없다.&lt;/li&gt;
&lt;li&gt;다른 사람의 편지와 섞이지 않는다.&lt;/li&gt;
&lt;li&gt;길을 사용하는 사람도 두 명뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터 두 대의 직접 연결도 이와 비슷하다. 두 장비 사이에 전용 통신 경로가 있으면 목적지는 명확하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;점대점 연결(Point-to-Point)&lt;/b&gt;이라고 한다. 지금도 장비와 장비를 직접 연결하거나 특정 두 지점을 잇는 구간에서 유용하게 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러니 점대점 연결 자체가 잘못된 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 모든 컴퓨터를 서로 직접 연결하려 할 때 문제가 생긴다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 세 번째 집이 생기면 길도 하나만 늘어날까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;철수가 이 동네로 이사 왔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 사람이 누구와도 직접 편지를 주고받으려면 어떻게 해야 할까?&lt;/p&gt;
&lt;pre class=&quot;livescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;        민수의 집
       /         \
영희의 집 ───── 철수의 집&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;필요한 길은 세 개다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 명이 더 이사 와서 네 명이 되면 길은 여섯 개가 필요하다. 다섯 명이면 열 개다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사람은 한 명씩 늘어나지만 길은 훨씬 빠르게 늘어난다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 사람이 서로 전용길을 가져야 한다면, 사람이&lt;span&gt;&amp;nbsp;&lt;/span&gt;N명일 때 필요한 길의 수는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;필요한 길의 수 = N &amp;times; (N - 1) &amp;divide; 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 46.0465%; height: 191px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;컴퓨터 수&lt;/td&gt;
&lt;td&gt;필요한 전용 연결 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2대&lt;/td&gt;
&lt;td&gt;1개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3대&lt;/td&gt;
&lt;td&gt;3개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5대&lt;/td&gt;
&lt;td&gt;10개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10대&lt;/td&gt;
&lt;td&gt;45개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100대&lt;/td&gt;
&lt;td&gt;4,950개&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;100대의 컴퓨터를 모두 직접 연결하려면 4,950개의 선이 필요하다. 컴퓨터 한 대마다 다른 99대와 연결할 단자도 있어야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새 컴퓨터 한 대를 추가할 때도 기존의 모든 컴퓨터까지 새 선을 연결해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;연결은 가능하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 확장할 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 선만 늘어나는 것이 아니다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전용 연결이 늘어나면 선의 개수만 문제가 되는 것은 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 연결할 자리가 필요하다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 컴퓨터는 상대마다 별도의 통신 인터페이스를 가져야 한다. 사람이 늘어날수록 컴퓨터 한 대에 필요한 단자도 함께 늘어난다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 새로운 참여자를 추가하기 어렵다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새 컴퓨터 한 대를 연결하려면 기존의 모든 컴퓨터에 손을 대야 한다. 참여자가 많아질수록 한 대를 추가하는 작업도 커진다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 고장 지점이 많아진다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;선이 많으면 끊어질 수 있는 지점도 많아진다. 통신이 되지 않을 때 수많은 전용선 가운데 어느 길이 문제인지 찾아야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 남는 길을 함께 쓰기 어렵다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;민수와 영희가 대화하지 않는 동안에도 두 사람의 전용길은 다른 사람에게 도움이 되지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자원이 비어 있어도 그 길의 주인이 아니면 사용할 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전용길은 단순하고 명확하지만, 참여자가 많아질수록 비싼 방식이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 해결해야 할 문제도 조금 달라진다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어떻게 더 많은 선을 연결할까?&amp;nbsp;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가 아니라&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어떻게 적은 수의 선을 더 많은 사람이 함께 사용할까?&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가 된다.&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 모든 집을 우체국에 연결하면 무엇이 달라질까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 집과 집 사이에 전용 배달길을 만들지 않고, 각 집이 동네 우체국 하나와 연결되어 있다고 생각해 보자.&lt;/p&gt;
&lt;pre class=&quot;&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;민수의 집 ─┐
영희의 집 ─┼─ 동네 우체국
철수의 집 ─┤
지수의 집 ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새로운 사람이 이사 오면 기존의 모든 집에 길을 만들 필요가 없다. 새 집과 우체국 사이의 길 하나만 추가하면 된다.&lt;/p&gt;
&lt;pre class=&quot;&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;전용 연결 방식: 새 집과 기존의 모든 집을 연결
중간 지점 방식: 새 집과 중간 지점만 연결&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;열 명이 서로 직접 연결하려면 45개의 길이 필요하지만,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;열 명이 하나의 중간 지점에 연결되면 각 사람에게 하나씩, 10개의 길로 시작할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;중간 지점은 편지를 받아 알맞은 목적지 쪽으로 넘겨준다. 여러 사람이 배달 시설과 길을 함께 사용하게 된 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크에서도 비슷한 역할을 하는 장치가 필요해졌다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘날의 LAN에서는 보통 컴퓨터를 서로 전부 직접 연결하지 않는다. 각 컴퓨터를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;스위치&lt;/b&gt;에 연결하고, 스위치가 데이터를 목적지 방향으로 전달한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;컴퓨터 A ─┐
컴퓨터 B ─┼─ 스위치
컴퓨터 C ─┤
컴퓨터 D ─┘&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 아직 스위치라는 장비의 이름이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;연결 구조가 바뀌었다는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 상대와 전용선을 만드는 구조에서&lt;/li&gt;
&lt;li&gt;공통의 중간 지점을 함께 사용하는 구조로 바뀌었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크는 이렇게 연결을 계속 늘리는 대신, 연결과 전달 자원을 공유하는 방향으로 확장할 수 있게 되었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 우편 비유를 네트워크 용어로 바꾸면&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 이야기를 실제 네트워크 용어로 옮기면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 112px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;우편 시스템의 모습&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;네트워크&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #008300; color: #ffffff; text-align: start;&quot;&gt;용어&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;두 집을 전용길로 연결&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Point-to-Point&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;두 장비 사이에 직접 통신 경로를 만드는 연결 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모든 집을 서로 연결&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Full Mesh Topology&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;모든 장비가 다른 모든 장비와 직접 연결된 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;여러 집이 중앙 우체국에 연결&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Star Topology&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;각 장비가 중앙 장치를 통해 연결되는 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;동네 우체국&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Switch&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;LAN에서 프레임을 목적지 방향으로 전달하는 중앙 장치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;사람이 늘어도 감당할 수 있는가&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Scalability&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;참여자와 트래픽이 증가해도 구조를 확장할 수 있는 성질&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Topology는 장비와 링크가 어떤 모양으로 연결되어 있는지를 뜻한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 장비를 직접 잇는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Full Mesh&lt;/b&gt;는 특정 장비 사이에 전용 경로를 제공하지만, 장비 수가 증가하면 링크와 인터페이스가 빠르게 늘어난다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Star Topology&lt;/b&gt;에서는 각 장비가 중앙의 스위치에 연결되므로 새로운 장비를 추가하기 쉽다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 차이를 설명하는 핵심 용어가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;확장성(Scalability)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;확장성&lt;/b&gt;은 단순히 장비를 더 연결할 수 있다는 뜻이 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자가 늘어날 때 필요한 비용과 작업, 복잡도가 감당할 수 있는 범위에서 증가해야 한다는 뜻이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 이 글의 질문은 네트워크 용어로 다음과 같이 바꿀 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Full Mesh Topology의 확장성 문제를 어떻게 해결할 것인가?&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LAN에서는 여러 장비를 스위치 중심의 Star Topology로 연결하는 것이 대표적인 답이 되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 그렇다면 공유하면 모든 문제가 해결될까?&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아직 그렇지 않다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모두가 같은 배달 시설을 사용하면 새로운 질문이 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 사람이 동시에 데이터를 보내면 어떻게 될까?&lt;/li&gt;
&lt;li&gt;받은 데이터가 누구의 것인지 어떻게 구분할까?&lt;/li&gt;
&lt;li&gt;중간 장치는 목적지가 어느 방향에 있는지 어떻게 알까?&lt;/li&gt;
&lt;li&gt;한 사람의 통신이 다른 사람의 통신을 방해하지 않을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전용길에서는 생각하지 않아도 되었던 문제다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공유는 연결 수를 줄여 주었지만, 그 대신&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;질서&lt;/b&gt;가 필요해졌다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크의 여러 기술은 바로 이 지점에서 등장한다. 누가 말할 차례인지 정하고, 목적지를 구분하고, 필요한 방향으로만 데이터를 전달해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, 공유는 문제의 끝이 아니라 네트워크 설계의 시작이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 직접 생각해 보기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종이 한 장에 컴퓨터를 점으로 표시하고 모든 컴퓨터가 서로 직접 연결되도록 선을 그어 보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컴퓨터 2대, 3대, 5대일 때 각각 몇 개의 선이 필요한가?&lt;/li&gt;
&lt;li&gt;컴퓨터를 한 대 추가할 때 새로 그어야 하는 선은 몇 개인가?&lt;/li&gt;
&lt;li&gt;가운데에 공통 연결 지점 하나를 두면 선의 수는 어떻게 달라지는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;계산할 때는 다음 두 식을 비교하면 된다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;모두 직접 연결: N &amp;times; (N - 1) &amp;divide; 2
중간 지점에 연결: N&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 작은 그림만으로도 네트워크가 왜 &lt;b&gt;&amp;ldquo;더 많은 연결&amp;rdquo;&lt;/b&gt;보다 &lt;b&gt;&amp;ldquo;공유할 수 있는 구조&amp;rdquo;&lt;/b&gt;를 선택했는지 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 실제 네트워크에서는 중간 장치도 처리 용량과 포트 수의 한계가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;규모가 커지면 여러 스위치를 계층적으로 연결하고 경로를 나눈다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 장치가 세상 모든 컴퓨터를 연결하는 것은 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 핵심 정리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터 두 대의 점대점 연결은 단순하고 유용하다.&lt;/li&gt;
&lt;li&gt;모든 컴퓨터를 서로 직접 연결하면 연결 수가&lt;span&gt;&amp;nbsp;&lt;/span&gt;N &amp;times; (N - 1) &amp;divide; 2로 증가한다.&lt;/li&gt;
&lt;li&gt;참여자가 늘수록 선, 인터페이스, 추가 작업, 장애 지점도 빠르게 늘어난다.&lt;/li&gt;
&lt;li&gt;중간 지점을 함께 사용하면 새 참여자는 연결 하나만 추가해 통신망에 들어올 수 있다.&lt;/li&gt;
&lt;li&gt;네트워크가 해결해야 했던 첫 번째 과제는 연결 자체보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;연결 자원의 공유&lt;/b&gt;였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;네트워크는&amp;nbsp;선을&amp;nbsp;많이&amp;nbsp;연결해서&amp;nbsp;커진&amp;nbsp;것이&amp;nbsp;아니라,&amp;nbsp;하나의&amp;nbsp;길을&amp;nbsp;함께&amp;nbsp;쓰는&amp;nbsp;방법을&amp;nbsp;찾아서&amp;nbsp;커졌다.&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 다음 이야기&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;공유할 중간 지점을 만들었으니 연결 문제는 해결된 것처럼 보인다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 여러 컴퓨터가 같은 길에서 동시에 말하면 어떻게 될까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 사람의 신호와 다른 사람의 신호가 겹친다면, 중간에서는 누구의 데이터를 들어야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;「모두가 동시에 말하면 누가 들을 수 있을까?」&lt;/b&gt;라는 질문을 통해 공유된 통신 매체와 충돌의 문제를 살펴본다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1057</guid>
      <comments>https://peterica.tistory.com/1057#entry1057comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:00:36 +0900</pubDate>
    </item>
    <item>
      <title>[Network] TCP/IP를 우편 시스템으로 이해하기</title>
      <link>https://peterica.tistory.com/817</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ElsSC/dJMcaiYg3E2/CoB9dPgBVCyG86Y1Cu9jJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ElsSC/dJMcaiYg3E2/CoB9dPgBVCyG86Y1Cu9jJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ElsSC/dJMcaiYg3E2/CoB9dPgBVCyG86Y1Cu9jJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FElsSC%2FdJMcaiYg3E2%2FCoB9dPgBVCyG86Y1Cu9jJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편지 한 통의 여정으로 이해하는 네트워크의 기본 원리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편지를 보내려면 주소만 적으면 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 사람이 편지를 주고받는 일은 간단하다. 하지만 수많은 사람이 같은 길을 나눠 쓰고, 도시와 나라의 경계를 넘어, 사라진 편지는 다시 보내면서도 제때 도착하게 하려면 이야기가 달라진다. 스위치와 라우터, TCP와 DNS는 그 문제를 하나씩 해결하며 만들어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연재는 용어를 외우는 순서가 아니다. &lt;b&gt;공유&lt;/b&gt;, &lt;b&gt;확장&lt;/b&gt;, &lt;b&gt;효율&lt;/b&gt;, &lt;b&gt;신뢰&lt;/b&gt;라는 네 가지 질문을 따라 편지 한 통이 목적지에 도착하는 과정을 이해하는 이야기다. 설명은 공개 표준과 일반적인 TCP/IP 원리를 바탕으로 하며, 예시와 관찰 활동은 연재를 위해 독립적으로 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 프롤로그. 우리는 편지를 보내고 있을 뿐이다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;00. 인터넷은 왜 거대한 우체국이 되었을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 두 대를 선으로 연결하는 일에서 시작해 수많은 사람이 하나의 통신망을 나눠 쓰는 일까지 나아간다. 네트워크를 장비와 프로토콜의 목록이 아니라, 제한된 길을 함께 쓰기 위해 발전한 물류 시스템으로 바라본다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 읽기: &lt;a href=&quot;https://peterica.tistory.com/1079&quot;&gt;00. 네트워크 이해를 위한 우체국 편지 비유&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;Part 1. 모두가 하나의 길을 함께 쓰려면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 컴퓨터끼리 선을 직접 연결하면 충분해 보였다. 그러나 연결할 사람이 늘자 선의 수와 충돌도 함께 늘었다. 이 Part에서는 하나의 길을 여러 사람이 안전하고 효율적으로 공유하기 위해 어떤 장치가 필요했는지 살펴본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 문제: 공유 &amp;mdash; 모두가 같은 길을 쓰면서도 서로 방해하지 않을 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;01. 컴퓨터 두 대를 연결하면 끝이 아니었을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 대를 잇는 전용선은 단순하지만 참여자가 늘어날수록 필요한 선이 폭발적으로 증가한다. 점대점 연결의 한계에서 네트워크가 왜 공동의 길을 필요로 했는지 출발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/1057&quot;&gt;[Network] 01. 컴퓨터 두 대를 연결하면 끝이 아니었을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;02. 모두가 동시에 말하면 누가 들을 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 통신 매체를 공유하면 신호가 충돌하고 불필요한 데이터까지 모두에게 전달된다. 허브와 공유 매체의 한계를 통해 &amp;ldquo;연결되어 있다&amp;rdquo;와 &amp;ldquo;효율적으로 통신한다&amp;rdquo;의 차이를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1080&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 02. 모두가 동시에 말하면 누가 들을 수 있을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;03. 스위치는 어떻게 편지가 갈 길을 기억할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치는 지나가는 프레임의 발신 MAC 주소를 보고 어느 포트에 누가 있는지 학습한다. 목적지를 알면 필요한 길로만 보내고, 모르면 모두에게 묻는 학습&amp;middot;전달&amp;middot;플러딩의 원리를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1082&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;03.&amp;nbsp;스위치는&amp;nbsp;어떻게&amp;nbsp;편지가&amp;nbsp;갈&amp;nbsp;길을&amp;nbsp;기억할까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;04. 길이 원을 그리면 편지는 왜 끝없이 늘어날까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치 사이에 순환 경로가 생기면 브로드캐스트는 사라지지 않고 계속 복제된다. 브로드캐스트 스톰을 통해 여분의 길이 언제 안전장치가 아니라 위험이 되는지, STP가 왜 일부 길을 쉬게 하는지 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1081&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;04.길이&amp;nbsp;원을&amp;nbsp;그리면&amp;nbsp;편지는&amp;nbsp;왜&amp;nbsp;끝없이&amp;nbsp;늘어날까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;05. 한 건물을 여러 동네로 나눌 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규모가 커지면 모든 사람이 모든 소식을 들을 필요가 없다. VLAN은 하나의 물리적인 스위치를 여러 방송 구역으로 나누고, 트렁크는 여러 구역의 데이터를 한 길로 운반한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1083&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;05.&amp;nbsp;한&amp;nbsp;건물을&amp;nbsp;여러&amp;nbsp;동네로&amp;nbsp;나눌&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ Part 2. 편지에 두 종류의 주소가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크에는 가까운 거리에서 실제로 전달할 때 쓰는 주소와 멀리 떨어진 목적지를 찾을 때 쓰는 주소가 함께 존재한다. 이 Part에서는 MAC 주소와 IP 주소가 역할을 나누는 이유와, 주소 범위가 네트워크의 경계를 만드는 과정을 따라간다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 문제: 확장 &amp;mdash; 가까운 이웃을 찾는 방법만으로 전 세계까지 갈 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;06. IP 주소만 알면 바로 편지를 건넬 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소는 목적지를 설명하지만 같은 네트워크에서 실제 프레임을 전달하려면 MAC 주소가 필요하다. ARP가 &amp;ldquo;이 IP 주소를 가진 사람은 누구인가?&amp;rdquo;라고 묻고 답을 잠시 기억하는 과정을 살펴본다.&lt;br /&gt;&lt;a href=&quot;https://peterica.tistory.com/1084&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;06.&amp;nbsp;IP&amp;nbsp;주소만&amp;nbsp;알면&amp;nbsp;바로&amp;nbsp;편지를&amp;nbsp;건넬&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;07. 같은 동네와 다른 동네는 누가 판단할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷 마스크는 IP 주소를 네트워크 부분과 호스트 부분으로 나눈다. 송신자는 이를 이용해 목적지가 가까운 이웃인지, 라우터에게 맡겨야 할 먼 곳인지 먼저 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1086&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;07.&amp;nbsp;같은&amp;nbsp;동네와&amp;nbsp;다른&amp;nbsp;동네는&amp;nbsp;누가&amp;nbsp;판단할까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;08. 다른 동네의 편지는 누구에게 맡겨야 할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 게이트웨이는 동네 밖으로 나가는 편지를 처음 맡는 우체국이다. 호스트가 모든 목적지의 길을 알지 않아도 하나의 출구를 통해 더 큰 네트워크로 나갈 수 있는 이유를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1085&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 08. 다른 동네의 편지는 누구에게 맡겨야 할까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ Part 3. 수많은 길 가운데 다음 길을 고르는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동네를 벗어난 편지는 여러 라우터를 거쳐 목적지로 간다. 각 라우터는 전체 여정을 지휘하지 않고 자신이 알고 있는 정보로 다음 구간만 선택한다. 이 단순한 결정이 반복되어 인터넷 규모의 배송이 완성된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장의 핵심: 모든 길을 알지 않고도 다음 길을 올바르게 선택할 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;09. 라우터는 수많은 목적지의 길을 어떻게 고를까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 테이블은 목적지 주소 범위와 다음 홉을 연결한 지도다. 여러 경로가 일치할 때 더 구체적인 주소 범위를 우선하는 최장 접두사 일치의 기본 원리를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1087&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 09. 라우터는 수많은 목적지의 길을 어떻게 고를까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;10. 가는 길이 있으면 돌아오는 길도 있는 것일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청과 응답은 서로 독립적으로 경로를 선택한다. 편지가 목적지에 도착했더라도 답장이 돌아올 경로가 없다면 통신은 완성되지 않는다. 네트워크를 항상 왕복 여정으로 봐야 하는 이유를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1088&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 10. 가는 길이 있으면 돌아오는 길도 있는 것일까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;11. 길을 잃은 편지가 영원히 돌면 어떻게 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 경로 정보가 서로를 가리키면 패킷은 라우터 사이를 맴돈다. TTL은 라우터를 지날 때마다 줄어들어 하나의 실수가 네트워크 자원을 영원히 차지하지 못하도록 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1089&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;11.&amp;nbsp;길을&amp;nbsp;잃은&amp;nbsp;편지가&amp;nbsp;영원히&amp;nbsp;돌면&amp;nbsp;어떻게&amp;nbsp;될까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;12. 길이 바뀌면 지도는 어떻게 새로 그려질까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 네트워크에서는 경로를 직접 적을 수 있지만 규모가 커지면 자동으로 정보를 나눠야 한다. 정적 라우팅과 동적 라우팅의 차이를 통해 OSPF와 BGP가 해결하려는 문제를 개념 수준에서 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1090&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;12.&amp;nbsp;길이&amp;nbsp;바뀌면&amp;nbsp;지도는&amp;nbsp;어떻게&amp;nbsp;새로&amp;nbsp;그려질까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;Part 4. 이름을 주소로 바꾸고 대화를 시작하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람은 숫자 주소보다 이름을 기억하고, 하나의 컴퓨터에서는 여러 프로그램이 동시에 통신한다. 이 Part에서는 DNS가 목적지 주소를 찾고 포트가 받을 프로그램을 구분한 뒤, UDP와 TCP가 서로 다른 방식으로 데이터를 전달하는 과정을 살펴본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달의 시작: 누구에게, 어떤 프로그램으로, 어떤 방식으로 보낼 것인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;13. 우리는 왜 숫자 대신 이름으로 접속할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS는 사람이 기억하는 이름을 IP 주소로 바꾼다. 계층적으로 답을 찾아가는 과정과 캐시가 반복 조회를 줄이는 이유, 오래된 캐시가 때로는 장애를 만드는 이유를 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/933&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;13.&amp;nbsp;우리는&amp;nbsp;왜&amp;nbsp;숫자&amp;nbsp;대신&amp;nbsp;이름으로&amp;nbsp;접속할까?&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;14. 한 컴퓨터의 여러 프로그램은 편지를 어떻게 나눠 받을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소가 건물을 찾는 주소라면 포트 번호는 그 안의 수신 창구와 같다. 클라이언트와 서버, 잘 알려진 포트와 임시 포트의 역할을 통해 하나의 호스트가 여러 대화를 구분하는 법을 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1091&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;14.&amp;nbsp;한&amp;nbsp;컴퓨터의&amp;nbsp;여러&amp;nbsp;프로그램은&amp;nbsp;편지를&amp;nbsp;어떻게&amp;nbsp;나눠&amp;nbsp;받을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;15. 모든 편지에 수령 확인이 필요할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UDP는 확인 절차를 줄여 빠르고 단순하게 전달하고, TCP는 순서와 도착 여부를 관리한다. 어느 하나가 항상 우월한 것이 아니라 애플리케이션이 원하는 효율과 신뢰에 따라 선택이 달라진다는 점을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1092&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 15. 모든 편지에 수령 확인이 필요할까?&lt;/a&gt;&lt;span style=&quot;background-color: #fafbfd; color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;Part 5. 믿을 수 없는 길 위에서 믿을 수 있게 보내기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP는 패킷의 도착을 보장하지 않는다. TCP는 연결을 확인하고, 잃어버린 데이터를 다시 보내고, 받는 쪽과 네트워크가 감당할 수 있는 속도에 맞춘다. 이 Part에서는 신뢰가 한 번의 기능이 아니라 여러 약속의 조합임을 이해한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 문제: 신뢰 &amp;mdash; 사라지고 늦고 순서가 바뀌는 길에서 어떻게 대화를 이어갈까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;16. 대화를 시작하기 전에 왜 세 번이나 인사할까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP 3-way handshake는 양쪽이 보내고 받을 준비가 되었는지 확인하고 초기 순서 번호를 맞춘다. SYN, SYN-ACK, ACK가 각각 무엇을 확인하는지 편지를 주고받는 장면으로 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1093&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;16.&amp;nbsp;대화를&amp;nbsp;시작하기&amp;nbsp;전에&amp;nbsp;왜&amp;nbsp;세&amp;nbsp;번이나&amp;nbsp;인사할까?&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;17. 편지가 사라졌다는 사실은 어떻게 알아챌까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP는 순서 번호와 ACK로 받은 범위를 알리고, 응답이 없거나 빠진 구간이 확인되면 재전송한다. 타임아웃과 중복 ACK가 손실을 알아채는 서로 다른 단서가 되는 이유를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1094&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network] 17. 편지가 사라졌다는 사실은 어떻게 알아챌까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;18. 받는 사람이 감당하지 못할 만큼 보내면 어떻게 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름 제어는 수신자의 처리 능력에 맞추고, 혼잡 제어는 네트워크 전체의 상태에 맞춰 전송량을 조절한다. 빠르게 보내는 것보다 모두가 길을 지속해서 공유하는 것이 중요한 이유를 살펴본다. &lt;br /&gt;&lt;a href=&quot;https://peterica.tistory.com/1095&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;18.&amp;nbsp;받는&amp;nbsp;사람이&amp;nbsp;감당하지&amp;nbsp;못할&amp;nbsp;만큼&amp;nbsp;보내면&amp;nbsp;어떻게&amp;nbsp;될까?&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;Part 6. 너무 큰 소포와 좁은 길의 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 네트워크 구간에는 한 번에 운반할 수 있는 크기의 한계가 있다. 패킷이 너무 크면 나누거나 더 작게 다시 보내야 하고, 이 사실을 알려주는 제어 메시지도 필요하다. 이 Part에서는 제한된 길을 효율적으로 사용하는 방법을 살펴본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 번째 문제: 효율 &amp;mdash; 길마다 다른 한계에 맞춰 얼마만큼씩 보내야 할까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;19. 작은 편지는 가는데 큰 소포만 멈추는 이유는 무엇일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MTU는 한 구간에서 전달할 수 있는 프레임의 최대 크기다. IP 단편화가 큰 패킷을 나누는 방식과, 단편화가 처리 비용과 손실 위험을 키우는 이유를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1096&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;19.&amp;nbsp;작은&amp;nbsp;편지는&amp;nbsp;가는데&amp;nbsp;큰&amp;nbsp;소포만&amp;nbsp;멈추는&amp;nbsp;이유는&amp;nbsp;무엇일까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;20. 가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path MTU Discovery는 경로에서 처리할 수 없는 크기를 ICMP 메시지로 알린다. 안내 메시지를 무조건 차단했을 때 작은 데이터만 되고 큰 데이터는 멈추는 MTU 블랙홀이 생기는 이유를 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1097&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;20.&amp;nbsp;가장&amp;nbsp;좁은&amp;nbsp;길의&amp;nbsp;크기를&amp;nbsp;출발지에서&amp;nbsp;어떻게&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;Part 7. 인터넷의 주소를 함께 쓰고 경계를 지키는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에 연결되는 기기는 빠르게 늘었지만 IPv4 주소는 한정되어 있다. NAT는 여러 내부 기기가 하나의 공인 주소를 나눠 쓰게 했고, 방화벽은 통신의 경계를 만들었다. 마지막으로 TLS가 전달 경로를 믿지 않고도 내용을 보호하는 방법을 살펴본다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유와 신뢰의 타협: 주소는 아끼면서 필요한 통신만 안전하게 허용할 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;21. 여러 집이 하나의 공인 주소를 함께 쓸 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT는 내부 주소와 포트를 외부 주소와 포트로 바꾸고 그 대응 관계를 기억한다. 사설 주소가 인터넷으로 나가는 기본 과정과, 주소 공유가 종단 간 연결에 가져온 변화를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1098&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;21.&amp;nbsp;여러&amp;nbsp;집이&amp;nbsp;하나의&amp;nbsp;공인&amp;nbsp;주소를&amp;nbsp;함께&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;22. 방화벽은 편지의 무엇을 보고 문을 열어줄까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽은 주소와 포트, 프로토콜, 연결 상태를 기준으로 통신을 허용하거나 차단한다. 무조건 막는 것이 아니라 필요한 흐름을 명확히 정의하는 것이 보안의 출발점임을 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1099&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;22.&amp;nbsp;방화벽은&amp;nbsp;편지의&amp;nbsp;무엇을&amp;nbsp;보고&amp;nbsp;문을&amp;nbsp;열어줄까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;23. 우체부를 믿지 않고도 편지 내용을 지킬 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS는 암호화로 내용을 숨기고, 인증서로 상대의 신원을 확인하며, 무결성 검증으로 전송 중 변경을 알아챈다. HTTPS가 해결하는 문제와 해결하지 않는 문제를 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;23.&amp;nbsp;우체부를&amp;nbsp;믿지&amp;nbsp;않고도&amp;nbsp;편지&amp;nbsp;내용을&amp;nbsp;지킬&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;Part 8. 편지가 멈춘 곳을 찾는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 장애는 종종 같은 얼굴, 즉 타임아웃으로 나타난다. 그래서 추측보다 여정의 각 단계에서 증거를 모아야 한다. 이 Part에서는 도구 이름을 외우는 대신 질문을 세우고 관찰 범위를 좁히는 기본 진단법을 정리한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 원리를 잇는 마지막 능력: 패킷이 어디까지 갔는지 증거로 설명할 수 있을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;24. ping이 실패하면 정말 서버가 죽은 것일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ping은 ICMP 응답 여부와 왕복 시간을 보여주지만 서비스의 정상 동작까지 보장하지 않는다. 응답이 없을 때 호스트 장애, 경로 문제, 정책 차단을 성급히 단정하지 않는 법을 배운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;24.&amp;nbsp;ping이&amp;nbsp;실패하면&amp;nbsp;정말&amp;nbsp;서버가&amp;nbsp;죽은&amp;nbsp;것일까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;25. traceroute는 보이지 않는 길을 어떻게 보여줄까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;traceroute는 TTL을 조금씩 늘리며 각 구간에서 돌아오는 ICMP 메시지를 모은다. 경로가 일부만 보이거나 응답이 없을 때 그 결과가 의미하는 범위와 한계를 이해한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1102&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;25.&amp;nbsp;traceroute는&amp;nbsp;보이지&amp;nbsp;않는&amp;nbsp;길을&amp;nbsp;어떻게&amp;nbsp;보여줄까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;26. 패킷을 직접 보면 모든 원인을 알 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ip&lt;/code&gt;와 &lt;code&gt;dig&lt;/code&gt;로 설정과 이름을 확인하고, &lt;code&gt;curl&lt;/code&gt;과 &lt;code&gt;netcat&lt;/code&gt;으로 애플리케이션 연결을 시험한다. 마지막에는 &lt;code&gt;tcpdump&lt;/code&gt;와 Wireshark로 실제 패킷을 관찰한다. 한 도구의 결과가 아니라 여러 계층의 증거를 맞추는 순서를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1103&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;26.&amp;nbsp;패킷을&amp;nbsp;직접&amp;nbsp;보면&amp;nbsp;모든&amp;nbsp;원인을&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ㅁ&amp;nbsp;에필로그. 우리는 인터넷이 아니라 물류 시스템을 이해했다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;27. 편지 한 통의 여정을 혼자 설명할 수 있을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름을 주소로 바꾸고, 같은 동네의 수신인을 찾고, 게이트웨이에 편지를 맡긴다. 라우터는 다음 길을 선택하고, NAT는 주소를 나눠 쓰게 하며, TCP는 대화를 신뢰할 수 있게 만든다. 문제가 생기면 여정의 각 지점에 남은 증거를 거꾸로 따라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP, Routing, NAT, DNS는 더 이상 따로 외워야 할 용어가 아니다. 모두 &lt;b&gt;공유&lt;/b&gt;, &lt;b&gt;확장&lt;/b&gt;, &lt;b&gt;효율&lt;/b&gt;, &lt;b&gt;신뢰&lt;/b&gt;를 지키면서 편지 한 통을 목적지까지 보내기 위해 생겨난 선택이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/1104&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;27.&amp;nbsp;편지&amp;nbsp;한&amp;nbsp;통의&amp;nbsp;여정을&amp;nbsp;혼자&amp;nbsp;설명할&amp;nbsp;수&amp;nbsp;있을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이 시리즈를 모두 읽으면&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAC 주소와 IP 주소가 왜 함께 필요한지 설명할 수 있다.&lt;/li&gt;
&lt;li&gt;패킷이 왜 특정 게이트웨이와 다음 홉을 선택했는지 이해할 수 있다.&lt;/li&gt;
&lt;li&gt;DNS, TCP, NAT, MTU가 편지의 여정에서 맡은 역할을 연결할 수 있다.&lt;/li&gt;
&lt;li&gt;같은 타임아웃에서도 이름, 경로, 포트, 전송 문제를 순서대로 구분할 수 있다.&lt;/li&gt;
&lt;li&gt;새로운 네트워크 기술을 만났을 때 무엇을 공유하고, 어디까지 확장하며, 무엇을 효율화하고, 어떤 신뢰를 보장하려는지 질문할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 얻게 되는 것은 용어 목록이 아니다. &lt;b&gt;편지가 왜 그 길을 선택했고 어디에서 멈췄는지를 자기 언어로 설명하는 힘&lt;/b&gt;이다.&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/817</guid>
      <comments>https://peterica.tistory.com/817#entry817comment</comments>
      <pubDate>Wed, 15 Jul 2026 20:00:07 +0900</pubDate>
    </item>
    <item>
      <title>[Network] 00. 네트워크 이해를 위한 우체국 편지 비유</title>
      <link>https://peterica.tistory.com/1079</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRw4I6/dJMcaazhXoc/BF38h3iMJHctl5ZCwKq8fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRw4I6/dJMcaazhXoc/BF38h3iMJHctl5ZCwKq8fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRw4I6/dJMcaazhXoc/BF38h3iMJHctl5ZCwKq8fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRw4I6%2FdJMcaazhXoc%2FBF38h3iMJHctl5ZCwKq8fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차: &lt;a href=&quot;https://peterica.tistory.com/817&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Network]&amp;nbsp;TCP/IP를&amp;nbsp;우편&amp;nbsp;시스템으로&amp;nbsp;이해하기&lt;/a&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 글은 만화 주인공 같은 크루가 &lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://github.com/gnu-gnu/network-fundamentals-lab&quot;&gt;(GitHub)&lt;/a&gt;&lt;a href=&quot;https://github.com/gnu-gnu/network-fundamentals-lab&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;network-fundamentals-lab&lt;/a&gt;의 내용을 보고 설명을 요청하면서 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 network-fundamentals-lab 프로젝트는 네트워크 실습 프로젝트을 위한 튜토리얼이다.&amp;nbsp; 보며 다시 생각한 TCP/IP의 본질&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 단순히 TCP/IP 개념을 설명하는 것이 아니라, &lt;b&gt;의도적으로 네트워크를 고장 낸 뒤 그 장애를 직접 관찰하고 해결하는 방식&lt;/b&gt;으로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP, VLAN, Routing, NAT, TCP, MTU, DNS, Dynamic Routing까지...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 17개의 실습을 통해 실제 네트워크에서 발생하는 문제를 재현하고, tcpdump, traceroute, ip route 같은 도구로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 추적해 보는 상당히 잘 만들어진 교육용 저장소였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 살펴보면서 한 가지 생각이 들었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;네트워크를 기술 용어로 설명하기보다, 우편 시스템으로 설명하면 훨씬 이해하기 쉽지 않을까?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCP/IP는 많은 사람이 어렵다고 느끼는 분야다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARP, MAC Address, Routing, TTL, MTU&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용어 하나하나를 이해하려고 하다 보면 금세 길을 잃는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 조금만 시선을 바꾸면 네트워크는 우리가 이미 매일 경험하고 있는 시스템과 매우 닮아 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;우편 시스템&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;우리는 매일 편지를 보내고 있다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구에게 편지를 보낸다고 생각해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편지를 봉투에 넣고 주소를 적은 뒤 우체통에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후의 과정은 어떻게 될까요?&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;나
 │
우체통
 │
집배원
 │
동네 우체국
 │
우편집중국
 │
지역 우체국
 │
집배원
 │
상대방&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이 과정을 자세히 알지 못한다.&amp;nbsp;편지가 도착하기만 하면 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷도 마찬가지.&amp;nbsp;웹사이트에 접속하고, 메신저를 보내고, AI에게 질문을 던지는 순간에도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 이와 비슷한 과정을 거쳐 목적지까지 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;우리는 결과만 사용할 뿐, 그 내부에서 어떤 일이 일어나는지는 거의 의식하지 않는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 네트워크의 시작은 '연결'이 아니라 '공유'였다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 질문이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 이렇게 복잡한 우편 시스템이 만들어졌을까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 사람 두 명만 편지를 주고받으면 된다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;나 ───────── 친구
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 전달하면 끝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사람이 열 명, 백 명, 천 명으로 늘어나면 이야기가 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 사람이 서로 직접 편지를 전달하려면 연결은 기하급수적으로 증가하고, 결국 아무도 감당할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우리는 &lt;b&gt;우체국&lt;/b&gt;이라는 중간 시스템을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;b&gt;집배원&lt;/b&gt;을 공유하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;b&gt;우편차&lt;/b&gt;를 공유하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;b&gt;우편집중국&lt;/b&gt;을 공유하면서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수많은 사람이 하나의 우편 시스템을 함께 사용할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷도 완전히 같은 원리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 두 대를 연결하는 것은 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 수십억 대의 컴퓨터가 하나의 네트워크를 함께 사용하려면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연결을 늘리는 것보다 자원을 효율적으로 공유하는 방법&lt;/b&gt;이 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 &lt;b&gt;스위치&lt;/b&gt;가 생기고, &lt;b&gt;라우터&lt;/b&gt;가 생기고, &lt;b&gt;DNS&lt;/b&gt;가 생기고, &lt;b&gt;NAT&lt;/b&gt;가 생기고, &lt;b&gt;TCP&lt;/b&gt;가 만들어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기술들은 새로운 기능을 추가하기 위해 만들어진 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한정된 네트워크 자원을 더 많은 사람이 안정적으로 함께 사용할 수 있도록 발전해 온 설계 원칙&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관점으로 바라보면 TCP/IP는 어려운 기술들의 집합이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'데이터를 목적지까지 효율적으로 전달하기 위해 만들어진 거대한 물류 시스템'&lt;/b&gt;이라고 이해하는 것이 더 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 글의 전체 구조도 더 단단해집니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;들어가며
    &amp;darr;
우리는 매일 편지를 보내고 있다
    &amp;darr;
네트워크의 시작은 '공유'였다   &amp;larr; 추가
    &amp;darr;
하지만 DevOps 엔지니어는 다르다
    &amp;darr;
그래서 우편 시스템으로 설명하려고 한다
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 시리즈에서는 자연스럽게 &quot;이 공유 시스템을 구현하기 위해 어떤 기술이 등장했는가&quot;라는 관점으로 &lt;b&gt;ARP &amp;rarr; Routing &amp;rarr; TTL &amp;rarr; NAT &amp;rarr; DNS&lt;/b&gt;를 연결할 수 있습니다. 독자는 각각의 기술을 암기하는 대신, 하나의 설계 철학 안에서 이해하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;하지만 DevOps엔지니어 혹은 네트워크 엔지니어는 다르다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 생기는 순간 이야기가 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 말한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;인터넷이 안 됩니다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우편으로 바꾸면 이렇다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;편지가 도착하지 않았습니다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 순간부터는 내부 과정을 하나씩 확인해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우체통에서는 정상적으로 수거되었는가?&lt;/li&gt;
&lt;li&gt;집배원이 제대로 전달했는가?&lt;/li&gt;
&lt;li&gt;어느 우체국까지는 도착했는가?&lt;/li&gt;
&lt;li&gt;주소를 잘못 적은 것은 아닌가?&lt;/li&gt;
&lt;li&gt;우편집중국에서 다른 지역으로 잘 전달되었는가?&lt;/li&gt;
&lt;li&gt;혹시 같은 우체국을 계속 돌고 있는 것은 아닌가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 엔지니어도 같은 질문을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷이 어디까지 도착했는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어디에서 사라졌는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 목적지까지 가지 못했는지를 추적하는 것이 네트워크의 본질이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;network-fundamentals-lab&lt;/b&gt;을 보면서 가장 인상 깊었던 점은 &lt;b&gt;고장을 통해 네트워크를 이해하도록 만든 교육 철학&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 역시 이 프로젝트를 따라가며 각 장을 정리해 볼 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 조금 다른 방식으로 접근해 보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기술 용어를 먼저 설명하는 것이 아니라, 우편 시스템에 비유하여 왜 이런 기술이 필요한지를 먼저 이야기하려고 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관점으로 보면 각 기술의 존재 이유도 자연스럽게 연결된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;689&quot; data-end=&quot;983&quot;&gt;
&lt;li data-start=&quot;689&quot; data-end=&quot;727&quot;&gt;&lt;b&gt;ARP&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 같은 동네에서 정확한 집을 찾기 위한 주소 조회&lt;/li&gt;
&lt;li data-start=&quot;728&quot; data-end=&quot;767&quot;&gt;&lt;b&gt;Switch&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 한 통신선을 여러 장비가 공유하기 위한 장치&lt;/li&gt;
&lt;li data-start=&quot;768&quot; data-end=&quot;809&quot;&gt;&lt;b&gt;Router&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 지역 간 우편을 효율적으로 전달하기 위한 중계소&lt;/li&gt;
&lt;li data-start=&quot;810&quot; data-end=&quot;854&quot;&gt;&lt;b&gt;TTL&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 네트워크 자원이 무한 루프에 낭비되지 않도록 붙여 놓은 '최대 중계 횟수' 안전장치&lt;/li&gt;
&lt;li data-start=&quot;855&quot; data-end=&quot;896&quot;&gt;&lt;b&gt;NAT&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 하나의 공인 IP를 여러 사용자가 공유하기 위한 기술&lt;/li&gt;
&lt;li data-start=&quot;897&quot; data-end=&quot;936&quot;&gt;&lt;b&gt;DNS&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 숫자 주소를 모두 외우지 않도록 하는 공용 주소록&lt;/li&gt;
&lt;li data-start=&quot;937&quot; data-end=&quot;983&quot;&gt;&lt;b&gt;TCP&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 신뢰성을 확보하면서도 네트워크를 효율적으로 사용하는 전송 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 바라보면 TCP/IP는 개별 기술들의 집합이 아니라,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한 통의 편지가 목적지까지 안전하게 전달되기 위해 만들어진 거대한 물류 시스템&lt;/b&gt;이라는 사실을 자연스럽게 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DevOps/Network</category>
      <category>Network</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1079</guid>
      <comments>https://peterica.tistory.com/1079#entry1079comment</comments>
      <pubDate>Wed, 15 Jul 2026 00:40:09 +0900</pubDate>
    </item>
    <item>
      <title>RONCO 모션데스크 메모리형 리모컨 사용방법</title>
      <link>https://peterica.tistory.com/1068</link>
      <description>&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;2874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/79hVO/dJMb99UCKkA/9R8IwZKraHeynocKEwETv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/79hVO/dJMb99UCKkA/9R8IwZKraHeynocKEwETv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/79hVO/dJMb99UCKkA/9R8IwZKraHeynocKEwETv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F79hVO%2FdJMb99UCKkA%2F9R8IwZKraHeynocKEwETv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1818&quot; height=&quot;2874&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;2874&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/50Tko/dJMcagzrL3P/CljYmyE8S1fBSCbxTS7KK1/%E1%84%83%E1%85%B2%E1%84%8B%E1%85%A5%E1%86%AF%E1%84%91%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5%E1%84%89%E1%85%B3%20%E1%84%86%E1%85%A9%E1%84%89%E1%85%A7%E1%86%AB%E1%84%83%E1%85%A6%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%20%E1%84%8C%E1%85%A9%E1%84%85%E1%85%B5%E1%86%B8%E1%84%89%E1%85%A5%E1%86%AF%E1%84%86%E1%85%A7%E1%86%BC%E1%84%89%E1%85%A5.pdf?attach=1&amp;amp;knm=tfile.pdf&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;듀얼플러스 모션데스크 조립설명서.pdf&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;3.41MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>LifeStory/블로그</category>
      <category>PETERICA</category>
      <category>RONCO</category>
      <category>모션테스크</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1068</guid>
      <comments>https://peterica.tistory.com/1068#entry1068comment</comments>
      <pubDate>Tue, 14 Jul 2026 22:32:11 +0900</pubDate>
    </item>
    <item>
      <title>[AI] 448MB가 113MB 되는 길 &amp;mdash; ONNX INT8 양자화 실전</title>
      <link>https://peterica.tistory.com/1069</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엣지 RAG의 임베딩 모델은 폰 안에 들어가야 한다.&lt;br /&gt;그런데 HuggingFace에서 받은 원본은 &lt;b&gt;448MB&lt;/b&gt;다.&lt;br /&gt;113MB에&amp;nbsp;맞춰야&amp;nbsp;APK&amp;nbsp;크기도&amp;nbsp;살고,&amp;nbsp;메모리도&amp;nbsp;산다.&lt;br /&gt;&lt;br /&gt;이 글은 &lt;b&gt;multilingual-e5-small-ko-v2&lt;/b&gt;를 ONNX INT8로 줄이고, 품질이 얼마나 보존되는지 테스트한 기록이다.&lt;br /&gt;파이프라인은 네 단계. 코드는 거의 한 줄씩이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 양자화가 하는 일 &amp;mdash; float32 &amp;rarr; int8&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 &quot;&lt;a href=&quot;https://peterica.tistory.com/997&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LLM 양자화 &amp;amp; 로컬 추론 최적화&lt;/a&gt;&quot;를 언젠가 제대로 다룰 주제로 적어둔 적이 있다.&lt;br /&gt;파라미터가 GPT-3의 10배로 뛰던 시대(관련 글: &lt;a href=&quot;https://peterica.tistory.com/762&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[AI] 최근 대규모 언어 모델(LLM)의 급격한 성장 이유&lt;/a&gt;)의 반대편에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글이 그 숙제를 실전으로 꺼내온다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ㅇ 버리는 것과 지키는 것&lt;/b&gt;&lt;br /&gt;&amp;nbsp; 양자화의 이론적 배경은 [&lt;a href=&quot;https://llm-study-web.vercel.app/topic/quantization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LLM System Lab의 Quantization 토픽&lt;/a&gt;]에 더 깊게 정리해두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 그 위에서 &lt;b&gt;실전 파이프라인&lt;/b&gt;에 집중한다.&lt;br /&gt;&lt;br /&gt;모델 파라미터는 기본적으로 &lt;b&gt;float32&lt;/b&gt;로 저장된다.&lt;br /&gt;한&amp;nbsp;숫자에&amp;nbsp;4바이트를&amp;nbsp;쓴다.&lt;br /&gt;양자화는 이걸 &lt;b&gt;int8&lt;/b&gt;(1바이트)로&amp;nbsp;압축한다&amp;nbsp;&amp;mdash;&amp;nbsp;용량&amp;nbsp;1/4.&lt;br /&gt;&lt;br /&gt;단순히&amp;nbsp;&quot;0~255&amp;nbsp;범위로&amp;nbsp;잘라&amp;nbsp;넣기&quot;는&amp;nbsp;아니다.&lt;br /&gt;각 레이어의 가중치 범위를 측정해서 &lt;b&gt;scale&lt;/b&gt;과 &lt;b&gt;zero point&lt;/b&gt;로 매핑한다.&lt;br /&gt;쉽게 말하면 - &lt;b&gt;scale&lt;/b&gt;은 float 값을 INT8 범위(-128~127)에 &lt;b&gt;얼마나 압축할지&lt;/b&gt;의 비율,&lt;br /&gt;&lt;b&gt;zero point&lt;/b&gt;는 float의 0이 &lt;b&gt;INT 축 위 어디에 놓이는지&lt;/b&gt;의 기준점이다.&lt;br /&gt;1바이트&amp;nbsp;정수&amp;nbsp;하나를&amp;nbsp;&quot;원래&amp;nbsp;float의&amp;nbsp;근사치&quot;로&amp;nbsp;되돌릴&amp;nbsp;수&amp;nbsp;있게.&lt;br /&gt;&lt;br /&gt;&amp;rarr; 정보가 일부 &lt;b&gt;손실&lt;/b&gt;된다.&lt;br /&gt;하지만 자연어 임베딩은 &lt;b&gt;상대적&amp;nbsp;유사도&lt;/b&gt;만&amp;nbsp;보존하면&amp;nbsp;되므로&amp;nbsp;손실이&amp;nbsp;뒤쪽까지&amp;nbsp;잘&amp;nbsp;이어지지&amp;nbsp;않는다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ㅇ 동적 양자화 vs 정적 양자화&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 72px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;방식&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;가중치&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;활성화&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;필요한 것&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;&lt;b&gt;Dynamic&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;INT8 저장&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;추론 시 동적 계산&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 19px;&quot;&gt;없음 &amp;mdash; 한 줄로 끝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Static&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;INT8&amp;nbsp;저장&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;INT8 사전 계산&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;calibration 데이터셋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;QAT&amp;nbsp;(훈련&amp;nbsp;중&amp;nbsp;양자화)&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;INT8&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;INT8&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;재학습&amp;nbsp;필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이번에 쓴 건 &lt;b&gt;Dynamic.&lt;/b&gt;&lt;br /&gt;이유: calibration 데이터셋 준비 비용이 크고, 임베딩 모델은 &lt;b&gt;activation 분포가 비교적 고른&lt;/b&gt;편이라 정적까지 안 가도 품질이 유지된다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;실전&amp;nbsp;파이프라인&amp;nbsp;4단계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 1. PyTorch &amp;rarr; ONNX export&lt;/b&gt;&lt;br /&gt;HuggingFace `optimum` CLI 한 줄로 끝.&lt;/p&gt;
&lt;pre id=&quot;code_1784858184425&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;optimum-cli export onnx \
  --model dragonkue/multilingual-e5-small-ko-v2 \
  --task feature-extraction \
  ./e5-ko-v2-onnx/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;결과:&lt;br /&gt;- &lt;b&gt;model.onnx&lt;/b&gt; &amp;mdash; 448MB (float32)&lt;br /&gt;- &lt;b&gt;tokenizer.json &lt;/b&gt;&amp;mdash; 16MB&lt;br /&gt;-&amp;nbsp;메타&amp;nbsp;파일들&amp;nbsp;(config,&amp;nbsp;tokenizer_config&amp;nbsp;등)&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;단계만으로도&amp;nbsp;이미&amp;nbsp;추론은&amp;nbsp;가능하다.&lt;br /&gt;PyTorch 없이 ONNX Runtime만으로 돌릴 수 있어 &lt;b&gt;크로스&amp;nbsp;플랫폼&amp;nbsp;의존성&lt;/b&gt;이 사라진다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ㅇ 2. INT8 동적 양자화&lt;/b&gt;&lt;br /&gt;Python&amp;nbsp;몇&amp;nbsp;줄.&lt;/p&gt;
&lt;pre id=&quot;code_1784858250493&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from onnxruntime.quantization import quantize_dynamic, QuantType

quantize_dynamic(
    model_input=&quot;e5-ko-v2-onnx/model.onnx&quot;,
    model_output=&quot;model_int8.onnx&quot;,
    weight_type=QuantType.QInt8,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;결과: &lt;b&gt;112.6MB&lt;/b&gt;&amp;nbsp;(원본의 25%).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;weight_type&lt;/b&gt;은&amp;nbsp;INT8/UINT8&amp;nbsp;둘&amp;nbsp;다&amp;nbsp;가능.&lt;br /&gt;&lt;b&gt;QInt8&lt;/b&gt;는 부호 있는 정수 &amp;mdash; 음수 가중치가 있는 일반적인 선택.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ㅇ 3. 품질 검증&lt;/b&gt;&lt;br /&gt;양자화로&amp;nbsp;얼마나&amp;nbsp;망가졌는지&amp;nbsp;실제로&amp;nbsp;재본다.&lt;br /&gt;원본 모델과 INT8 모델을 같은 텍스트에 돌려 &lt;b&gt;cosine&amp;nbsp;similarity&lt;/b&gt;를 잰다.&lt;/p&gt;
&lt;pre id=&quot;code_1784858322694&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 원본
ref = sentence_transformer.encode(&quot;query: 쿠버네티스 헬스체크&quot;)

# INT8 ONNX
onnx_vec = onnx_session.run(...)

# 두 벡터 사이 cosine
sim = cosine(ref, onnx_vec)  # 0.97~0.98&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;0.97 이상이면 실용 OK&lt;/b&gt;.&lt;br /&gt;1.0에서&amp;nbsp;멀어진&amp;nbsp;만큼&amp;nbsp;두&amp;nbsp;벡터가&amp;nbsp;달라진&amp;nbsp;건&amp;nbsp;맞다.&lt;br /&gt;하지만 검색은 &lt;b&gt;절대&amp;nbsp;거리가&amp;nbsp;아니라&amp;nbsp;상대&amp;nbsp;순위&lt;/b&gt;로&amp;nbsp;동작한다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본이 뽑는 Top-3 문서와 양자화 모델이 뽑는 Top-3 문서가 같은 순서로 나오면 사용자 경험은 구별되지 않는다.&lt;br /&gt;0.97은&amp;nbsp;경험상&amp;nbsp;이&amp;nbsp;&quot;순위&amp;nbsp;보존&quot;&amp;nbsp;조건을&amp;nbsp;거의&amp;nbsp;항상&amp;nbsp;충족하는&amp;nbsp;값이다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ㅇ 4. 모바일 assets 배치&lt;/b&gt;&lt;br /&gt;Android 앱의 `&lt;b&gt;assets/&lt;/b&gt;` 폴더에 두 파일을 넣는다.&lt;/p&gt;
&lt;pre id=&quot;code_1784858381693&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mobile/app/src/main/assets/
├── model.onnx      (113MB)
└── tokenizer.onnx  (5MB, ORT-extensions)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;토크나이저는 `tokenizer.json`(16MB) 대신 &lt;b&gt;ONNX 그래프로 변환&lt;/b&gt;(5MB)해서 넣었다.&lt;br /&gt;이유는 아래 &quot;짚어볼 것들&quot;에서 설명한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 품질은 어디까지 떨어졌나&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국어 쿼리 5개로 검색 품질을 실제 측정했다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;쿼리&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;원본 Top-1 distance&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;INT8 Top-1 distance&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;일치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;쿠버네티스 헬스체크&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.412&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.415&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;✓ 동일 문서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;APM 모니터링&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.337&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.340&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;RAG 시스템&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.289&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.295&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;graceful shutdown&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.356&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.361&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;prometheus pull&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.421&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;0.427&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;거리 값이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;0.003~0.006 증가&lt;/b&gt;했지만 Top-1 문서는 모두 동일.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;br /&gt;ㅁ&amp;nbsp;짚어볼&amp;nbsp;것들&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 동적 양자화는 가중치만 &amp;mdash; 활성화는 그대로다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;INT8 모델&quot;이라고 하면 모든 연산이 8비트로 도는 것처럼 들린다.&lt;br /&gt;하지만 동적 양자화는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가중치만 INT8로 저장&lt;/b&gt;하고, 실제 행렬곱은 INT8&amp;harr;float 변환이 섞인다.&lt;br /&gt;CPU에서는 작은 연산이라 큰 문제가 없지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;NPU/GPU 가속이 필요할 땐 한계&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;rarr; 모바일 NPU를 최대 활용하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;정적 양자화&lt;/b&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;QAT&lt;/b&gt;로 가야 한다.&lt;br /&gt;정적 양자화는 activation의 scale까지&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사전에 고정&lt;/b&gt;해 추론 내내 INT8로만 돈다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;QAT(훈련 중 양자화)는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;훈련 때부터 양자화 오차를 학습에 반영&lt;/b&gt;해 품질 손실을 더 줄인다.&lt;br /&gt;둘 다 NPU가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;INT8 연산만 빠르게 처리하는 특성&lt;/b&gt;에 맞춘 최적화다.&lt;br /&gt;이번엔 CPU 추론이 목표라 동적이 충분했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 토크나이저도 배포에 포함해야 한다 (함정 1)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모델만 양자화하면 끝이 아니다.&lt;br /&gt;임베딩 전에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;텍스트를 토큰 ID로 바꾸는 토크나이저&lt;/b&gt;가 필요하다.&lt;br /&gt;tokenizer.json(16MB)은 HuggingFace 표준이지만, Android에서 쓰려면 별도 라이브러리(DJL 등)가 필요했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DJL이 Android arm64 네이티브 바이너리를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;공식 배포하지 않는다&lt;/b&gt;는 사실을 뒤늦게 확인했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대안:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; onnxruntime-extensions의&lt;span&gt;&amp;nbsp;&lt;/span&gt;SentencepieceTokenizer를 ONNX 그래프로 뽑아 &lt;b&gt;model.onnx 옆에&lt;span&gt;&amp;nbsp;&lt;/span&gt;tokenizer.onnx로 동봉&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;rarr; 양자화 파이프라인의 출력물은 모델 하나가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모델 + 토크나이저 세트&lt;/b&gt;다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ 품질 검증은 벤치마크가 아니라 &quot;내 쿼리&quot;로 한다&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MTEB 같은 공개 벤치마크 점수는 참고용이다.&lt;br /&gt;양자화 전후&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;내가 실제로 쓸 쿼리&lt;/b&gt;로 Top-1이 유지되는지 재보는 게 더 중요하다.&lt;br /&gt;118 청크 규모에선 5~10개 대표 쿼리로도 충분히 의사결정할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &quot;벤치마크 점수가 0.85에서 0.83으로 떨어졌다&quot;보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&quot;내 실제 쿼리 5개가 전부 같은 문서를 뽑는다&quot;&lt;/b&gt;가 더 믿음직하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ㅇ INT4, BF16, Matryoshka &amp;mdash; 다음 선택지&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;INT8에서 멈출 이유는 없다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;&lt;b&gt;선택&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;크기&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;품질&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;비용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;INT4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~56MB&lt;/td&gt;
&lt;td&gt;약간 손실 더&lt;/td&gt;
&lt;td&gt;도구는 성숙, 검증 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;BF16&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;~224MB&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;거의 무손실&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;CPU 지원 고르지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;Matryoshka 차원 절단&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비례&lt;/td&gt;
&lt;td&gt;점진적 손실&lt;/td&gt;
&lt;td&gt;모델이 지원해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;rarr; 앞으로 실험할 방향.&lt;br /&gt;특히 Matryoshka는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;양자화와 차원 절단이 독립&lt;/b&gt;이라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;둘을 곱해&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;쓸 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 448MB와 INT8 113MB는 **같은 지식을 담은 서로 다른 그릇**이다.&lt;br /&gt;손실은&amp;nbsp;분명히&amp;nbsp;있지만,&amp;nbsp;Top-1&amp;nbsp;검색&amp;nbsp;관점에서는&amp;nbsp;눈에&amp;nbsp;띄지&amp;nbsp;않는다.&lt;br /&gt;덕분에&amp;nbsp;모델이&amp;nbsp;**주머니에&amp;nbsp;들어갔다.**&lt;br /&gt;&lt;br /&gt;&amp;rarr;&amp;nbsp;양자화는&amp;nbsp;모델을&amp;nbsp;작게&amp;nbsp;만드는&amp;nbsp;게&amp;nbsp;아니라,&amp;nbsp;**쓸모의&amp;nbsp;모양을&amp;nbsp;맞추는&amp;nbsp;과정**이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;함께&amp;nbsp;보면&amp;nbsp;좋은&amp;nbsp;사이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ&amp;nbsp;양자화&amp;nbsp;도구&lt;br /&gt;-&amp;nbsp;HuggingFace&amp;nbsp;Optimum&amp;nbsp;(ONNX&amp;nbsp;export):&amp;nbsp;&lt;a href=&quot;https://huggingface.co/docs/optimum&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/docs/optimum&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;ONNX&amp;nbsp;Runtime&amp;nbsp;Quantization:&amp;nbsp;&lt;a href=&quot;https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;onnxruntime-extensions&amp;nbsp;(tokenizer&amp;nbsp;as&amp;nbsp;ONNX):&amp;nbsp;&lt;a href=&quot;https://github.com/microsoft/onnxruntime-extensions&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/microsoft/onnxruntime-extensions&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ㅇ&amp;nbsp;모델&lt;br /&gt;-&amp;nbsp;dragonkue/multilingual-e5-small-ko-v2:&amp;nbsp;&lt;a href=&quot;https://huggingface.co/dragonkue/multilingual-e5-small-ko-v2&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/dragonkue/multilingual-e5-small-ko-v2&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;MTEB&amp;nbsp;벤치마크:&amp;nbsp;&lt;a href=&quot;https://huggingface.co/spaces/mteb/leaderboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://huggingface.co/spaces/mteb/leaderboard&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ㅇ&amp;nbsp;더&amp;nbsp;공부하기&amp;nbsp;&amp;mdash;&amp;nbsp;LLM&amp;nbsp;System&amp;nbsp;Lab&lt;br /&gt;-&amp;nbsp;Quantization&amp;nbsp;(이론&amp;nbsp;심화):&amp;nbsp;&lt;a href=&quot;https://llm-study-web.vercel.app/topic/quantization&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://llm-study-web.vercel.app/topic/quantization&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;Embedding:&amp;nbsp;&lt;a href=&quot;https://llm-study-web.vercel.app/topic/embedding&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://llm-study-web.vercel.app/topic/embedding&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;Embedding&amp;nbsp;Space&amp;nbsp;(인터랙티브&amp;nbsp;Lab):&amp;nbsp;&lt;a href=&quot;https://llm-study-web.vercel.app/labs/embedding-space&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://llm-study-web.vercel.app/labs/embedding-space&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ㅇ&amp;nbsp;연관된&amp;nbsp;이전&amp;nbsp;글&amp;nbsp;(양자화&amp;nbsp;흐름)&lt;br /&gt;-&amp;nbsp;LLM&amp;nbsp;파라미터가&amp;nbsp;커지던&amp;nbsp;시대:&amp;nbsp;&lt;a href=&quot;https://peterica.tistory.com/762&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://peterica.tistory.com/762&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;RAG/Agent/Infra&amp;nbsp;엔지니어의&amp;nbsp;지식&amp;nbsp;정리법&amp;nbsp;(양자화&amp;nbsp;관심&amp;nbsp;주제로&amp;nbsp;등록):&amp;nbsp;&lt;a href=&quot;https://peterica.tistory.com/997&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://peterica.tistory.com/997&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ㅇ&amp;nbsp;시리즈&lt;br /&gt;- &lt;a href=&quot;https://peterica.tistory.com/1064&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[AI] 1,000개 블로그 글 Wiki에 로컬 RAG 챗봇 붙이기 &amp;mdash; peterica-blog-chat&lt;/a&gt;&lt;br /&gt;- &lt;a href=&quot;https://peterica.tistory.com/1065&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[AI] sqlite-vec vs ANN: 왜 지금은 KNN이 더 적합한가&lt;/a&gt;: qlite-vec 선택 이유&lt;br /&gt;- &lt;a href=&quot;https://peterica.tistory.com/1071&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[온디바이스AI] 내 폰으로 나만의 RAG 만들기(온디바이스 RAG 최소 아키텍처)&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI/AI코딩 | 실습</category>
      <category>AI</category>
      <category>onnx</category>
      <category>PETERICA</category>
      <category>quantization</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1069</guid>
      <comments>https://peterica.tistory.com/1069#entry1069comment</comments>
      <pubDate>Tue, 14 Jul 2026 21:07:43 +0900</pubDate>
    </item>
    <item>
      <title>[Gradle] compileOnly는 &amp;quot;배포 안 함&amp;quot;이 아니다 &amp;mdash; AAR과 POM은 서로 다른 것을 배포한다</title>
      <link>https://peterica.tistory.com/1070</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리를 만들어 Maven에 올려본 적이 있다면 &lt;code&gt;compileOnly&lt;/code&gt;를 한 번쯤 써봤을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;컴파일할 때만 필요하고 배포물에는 안 들어간다&quot;는 설명은 어디에나 있다. 맞는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 문장에는 숨은 주어가 있다. &lt;b&gt;누가&lt;/b&gt; 배포하지 않는다는 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;compileOnly&lt;/code&gt;가 보장하는 건 &quot;&lt;b&gt;내 바이너리&lt;/b&gt;에 안 들어간다&quot;이지, &quot;&lt;b&gt;내 소비자의 바이너리&lt;/b&gt;에 안 들어간다&quot;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 둘 사이를 갈라놓는 것이 POM이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이선스 이슈를 대응하기 위해 내 AAR에는 안 넣되고, 소비자에게는 전달하고 싶은 경우 사용할 수 있는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 라이브러리는 두 개를 배포한다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven 저장소에 올라가는 것을 뜯어보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;com/example/mylib/1.0.0/
├── mylib-1.0.0.aar      &amp;larr; 바이너리 (코드)
├── mylib-1.0.0.pom      &amp;larr; 메타데이터 (의존성 선언)
└── mylib-1.0.0.module   &amp;larr; Gradle Module Metadata (선택)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- AAR은 &quot;내가 무엇을 담고 있는가&quot;를 말한다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- POM은 &quot;나를 쓰려면 무엇이 더 필요한가&quot;를 말한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소비자의 Gradle이 &lt;code&gt;implementation 'com.example:mylib:1.0.0'&lt;/code&gt;을 만나면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AAR을 다운로드하기 전에 POM부터 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기 적힌 &lt;code&gt;&amp;lt;dependency&amp;gt;&lt;/code&gt;들을 의존성 그래프에 편입시키고, 함께 다운로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;POM은 실행 가능한 지시문이다.&lt;/b&gt; 단순한 설명서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 각 스코프는 POM에 어떻게 기록되는가&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;Gradle 스코프&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;내 컴파일&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;내 런타임&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;POM 기록&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;&lt;b&gt;소비자 컴파일&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;&lt;b&gt;소비자 런타임&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;api&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;compile&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;implementation&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;runtime&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;compileOnly&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;&lt;b&gt;없음&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;compileOnlyApi&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;compile&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 142px;&quot;&gt;runtimeOnly&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 61px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 79px;&quot;&gt;runtime&lt;/td&gt;
&lt;td style=&quot;width: 89px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 146px;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;b&gt;&lt;code&gt;implementation&lt;/code&gt;은 POM에 &lt;code&gt;runtime&lt;/code&gt; 스코프로 기록된다.&lt;/b&gt; 소비자의 컴파일 클래스패스에는 안 올라가지만 APK에는 들어간다. 캡슐화는 되지만 배포는 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;b&gt;&lt;code&gt;compileOnly&lt;/code&gt;만이 유일하게 POM에 아무 흔적도 남기지 않는다.&lt;/b&gt; Gradle의 &lt;code&gt;apiElements&lt;/code&gt;/&lt;code&gt;runtimeElements&lt;/code&gt; 어느 configuration에도 속하지 않기 때문이다. maven-publish 플러그인이 POM을 만들 때 참조하는 게 바로 이 configuration들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;b&gt;&lt;code&gt;compileOnlyApi&lt;/code&gt;는 헷갈리기 쉽다.&lt;/b&gt; 이름은 &lt;code&gt;compileOnly&lt;/code&gt; 계열이지만 POM에는 &lt;code&gt;compile&lt;/code&gt;로 기록된다. 소비자의 컴파일 클래스패스에 전파된다. 어노테이션 라이브러리처럼 &quot;소비자도 컴파일 시점엔 필요하지만 런타임엔 없어도 되는&quot; 것에 쓴다. AGP(Android&amp;nbsp;Gradle&amp;nbsp;Plugin)에서의 지원 여부는 버전마다 다르니 확인이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담으로, Maven에는 Gradle의 &lt;code&gt;compileOnly&lt;/code&gt;에 정확히 대응하는 스코프가 없다. 가장 가까운 건 &lt;code&gt;provided&lt;/code&gt;인데, 이건 &quot;런타임 환경이 제공해줄 것&quot;이라는 의미라 뉘앙스가 다르다. maven-publish는 &lt;code&gt;provided&lt;/code&gt;를 생성하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 번들과 전파를 구분하라&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 두 개념을 분리해야 한다. 실무에서 가장 많이 섞이는 지점이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;번들 (bundling)
  내 AAR의 classes.jar 안에 서드파티 .class 파일이 물리적으로 들어감
  &amp;rarr; &quot;내 바이너리가 그 코드를 담고 있다&quot;

전파 (propagation)
  내 POM의 dependency 선언 때문에 소비자 빌드에 자동 유입됨
  &amp;rarr; &quot;내 바이너리엔 없지만, 내 선언 때문에 소비자가 받아간다&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;번들&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;전파&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;어디에 기록&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;AAR&lt;span&gt;&amp;nbsp;&lt;/span&gt;classes.jar,&lt;span&gt;&amp;nbsp;&lt;/span&gt;libs/&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;POM&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;dependencies&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;확인 방법&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;jar tf&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;cat *.pom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;compileOnly&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;implementation&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: ;&quot;&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;shadow/fat-jar&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;&lt;b&gt;O&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: ;height: ;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Gradle 라이브러리는 &lt;b&gt;번들하지 않는다.&lt;/b&gt; &lt;code&gt;implementation&lt;/code&gt;으로 선언해도 상대 라이브러리의 클래스가 내 jar에 복사되지는 않는다. 복사는 Shadow 플러그인 같은 걸 명시적으로 써야 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &quot;우리 AAR에 그 코드 없습니다&quot;는 대부분의 경우 참이지만, 동시에 대부분의 경우 &lt;b&gt;의미 없는 문장&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 없다. 중요한 건 전파 여부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 그래서 build.gradle과 publish.gradle은 무엇을 하는가&lt;/h4&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;build.gradle    &amp;rarr; &quot;내가 어떻게 컴파일되는가&quot;를 정의
publish.gradle  &amp;rarr; &quot;내가 어떻게 배포되는가&quot;를 정의&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이상적인 세계에서는 후자가 전자로부터 자동 유도된다. AGP 7.0부터는 이렇게 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;// build.gradle
android {
    publishing {
        singleVariant(&quot;release&quot;) {
            withSourcesJar()
        }
    }
}

// publish.gradle
afterEvaluate {
    publishing {
        publications {
            release(MavenPublication) {
                from components.release   // &amp;larr; 여기가 핵심
                groupId = 'com.example'
                artifactId = 'mylib'
                version = '1.0.0'
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;&lt;code&gt;from components.release&lt;/code&gt;가 하는 일이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;이건 AGP가 만들어준 &lt;b&gt;SoftwareComponent&lt;/b&gt;를 publication에 연결하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;그 컴포넌트는 &lt;code&gt;apiElements&lt;/code&gt;와 &lt;code&gt;runtimeElements&lt;/code&gt;를 알고 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;maven-publish는 그걸 읽어서 POM의 &lt;code&gt;&amp;lt;dependencies&amp;gt;&lt;/code&gt;를 &lt;b&gt;자동 생성&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;api&lt;/code&gt; 선언 &amp;rarr; &lt;code&gt;&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;implementation&lt;/code&gt; 선언 &amp;rarr; &lt;code&gt;&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compileOnly&lt;/code&gt; 선언 &amp;rarr; 아무것도 안 나옴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;진실의 원천이 하나다.&lt;/b&gt; &lt;code&gt;build.gradle&lt;/code&gt;만 고치면 POM이 따라온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ pom.withXml &amp;mdash; 수동으로 POM을 조작하기&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;publishing {
    publications {
        release(MavenPublication) {
            // from components.release 가 없다!
            def output = variant.getPackageLibraryProvider().get().getArchivePath()
            artifact(output) {
                builtBy variant.getAssembleProvider().get()
            }

            pom.withXml {
                Node dependenciesNode = asNode().appendNode('dependencies')
                def addDependencyNode = { group, name, ver -&amp;gt;
                    Node d = dependenciesNode.appendNode('dependency')
                    d.appendNode('groupId', group)
                    d.appendNode('artifactId', name)
                    d.appendNode('version', ver)
                    d.appendNode('scope', 'compile')
                }
                addDependencyNode('org.jetbrains.kotlin', 'kotlin-stdlib', kotlinVersion)
                addDependencyNode('com.android.installreferrer', 'installreferrer', '1.0')
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;from components.release&lt;/code&gt; 대신 &lt;b&gt;&lt;code&gt;artifact(output)&lt;/code&gt;으로 AAR 파일을 raw로 붙였다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SoftwareComponent가 없으니 maven-publish는 의존성 정보를 알 방법이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POM의 &lt;code&gt;&amp;lt;dependencies&amp;gt;&lt;/code&gt;는 텅 비어서 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;code&gt;pom.withXml&lt;/code&gt;로 &lt;b&gt;직접 XML 노드를 박아 넣는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;asNode()&lt;/code&gt;가 POM의 루트를 반환하고, 거기에 &lt;code&gt;&amp;lt;dependencies&amp;gt;&lt;/code&gt;를 통째로 만들어 붙이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;왜 이렇게 하는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 세 가지 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하나.&lt;/b&gt; 레거시. AGP 3.x 시절에는 &lt;code&gt;components.release&lt;/code&gt;가 없었다. &lt;code&gt;pom.withXml&lt;/code&gt;이 유일한 방법이었고, 그 코드가 살아남았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;둘.&lt;/b&gt; &lt;code&gt;compileOnly&lt;/code&gt;로 선언한 것을 POM에는 넣고 싶을 때. 내 AAR에는 안 넣되, 소비자에게는 전달하고 싶은 경우다. Gradle 스코프만으로는 이 조합이 안 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셋.&lt;/b&gt; 세밀한 제어. 버전 범위, &lt;code&gt;optional&lt;/code&gt;, &lt;code&gt;exclusions&lt;/code&gt; 등을 직접 쓰고 싶을 때.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;영향도&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pom.withXml&lt;/code&gt;로 &lt;code&gt;scope=compile&lt;/code&gt;을 박아 넣으면 어떻게 되는가.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;build.gradle:   compileOnly 'com.android.installreferrer:installreferrer:1.0'
                &amp;rarr; 내 AAR에 없음. 내 컴파일에만 쓰임.

publish.gradle: &amp;lt;dependency&amp;gt;
                  &amp;lt;groupId&amp;gt;com.android.installreferrer&amp;lt;/groupId&amp;gt;
                  &amp;lt;artifactId&amp;gt;installreferrer&amp;lt;/artifactId&amp;gt;
                  &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
                  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;        &amp;larr; Maven compile = transitive
                &amp;lt;/dependency&amp;gt;
                &amp;rarr; 소비자가 자동으로 받아감. APK에 들어감.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 같은 라이브러리가 &lt;b&gt;이중생활&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 내 빌드에서는 &lt;code&gt;compileOnly&lt;/code&gt;, 소비자 빌드에서는 &lt;code&gt;compile&lt;/code&gt;. 번들은 아니지만 전파는 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 의도한 것이라면 정당한 설계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;특히 재배포 제한이 있는 라이브러리(Google Play Services 계열 등)를 fat-jar로 번들하지 않으면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;소비자에게 필요한 의존성을 알려주는 방법이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;번들하면 라이선스 위반 소지가 있지만, POM 선언은 &quot;Google Maven에서 직접 받아가세요&quot;라는 안내에 불과하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;문제는 &lt;b&gt;의도하지 않았을 때&lt;/b&gt; 이 구조가 조용하다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ pom.withXml의 함정 다섯 가지&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;진실의 원천이 두 개가 된다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;build.gradle&lt;/code&gt;에서 의존성 버전을 올려도 &lt;code&gt;publish.gradle&lt;/code&gt;의 하드코딩된 문자열은 그대로다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 이런 어긋남을 자주 본다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// build.gradle
implementation &quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22&quot;

// publish.gradle
addDependencyNode('org.jetbrains.kotlin', 'kotlin-stdlib', kotlinVersion)
//                                         ^^^^^^^^^^^^^ 아티팩트 이름이 다름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 컴파일한 것과 소비자가 받아가는 것이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Gradle Module Metadata가 있으면 POM이 무시된다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.module&lt;/code&gt; 파일이 함께 발행되면, &lt;b&gt;Gradle 소비자는 POM이 아니라 &lt;code&gt;.module&lt;/code&gt;을 읽는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POM은 Maven 소비자용 폴백이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;code&gt;from components.release&lt;/code&gt;와 &lt;code&gt;pom.withXml&lt;/code&gt;을 동시에 쓰면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pom.withXml&lt;/code&gt;로 추가한 의존성이 Gradle 사용자에게는 &lt;b&gt;보이지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven 사용자에게만 보인다. 재현하기 까다로운 종류의 버그다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 예제 코드처럼 &lt;code&gt;artifact(output)&lt;/code&gt;만 쓰면 SoftwareComponent가 없어 &lt;code&gt;.module&lt;/code&gt;이 생성되지 않으므로 이 문제는 안 생긴다. 대신 다른 것들을 다 잃는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;의존성 관리 기능이 죽는다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.module&lt;/code&gt;이 없으면 Gradle이 제공하는 것들이 함께 사라진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;variant-aware 의존성 해석&lt;/li&gt;
&lt;li&gt;&lt;code&gt;platform()&lt;/code&gt; / BOM을 통한 버전 정렬&lt;/li&gt;
&lt;li&gt;능력(capability) 기반 충돌 감지&lt;/li&gt;
&lt;li&gt;rich version constraint (&lt;code&gt;strictly&lt;/code&gt;, &lt;code&gt;prefer&lt;/code&gt;, &lt;code&gt;reject&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;툴이 못 본다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 스캐너, SBOM 생성기, 라이선스 검증 도구는 대개 &lt;code&gt;build.gradle&lt;/code&gt;의 선언을 파싱하거나 Gradle 의존성 그래프를 조회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pom.withXml&lt;/code&gt; 클로저 &lt;b&gt;안에서&lt;/b&gt; Groovy 코드로 만들어지는 노드는 빌드를 실행해야만 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;./gradlew dependencies&lt;/code&gt;에도 안 나온다. 그 태스크는 configuration을 보지, POM을 보지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;scope=compile은 생각보다 강하다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven의 &lt;code&gt;compile&lt;/code&gt; 스코프는 전파된다. 소비자의 컴파일 클래스패스와 런타임 클래스패스에 모두 올라가고, 그 소비자의 소비자에게까지 전파된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 경우 &lt;code&gt;runtime&lt;/code&gt;이 더 적절하다. 소비자가 그 API를 직접 호출할 게 아니라면.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어떻게 쓸 것인가&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 133px;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;원하는 것&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;쓸 것&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;내 구현에만 필요, 소비자는 몰라도 됨&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;소비자가 내 API를 쓰려면 타입이 필요&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;api&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;컴파일에만 필요, 런타임엔 없어도 됨 (어노테이션 등)&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;compileOnly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;위와 같지만 소비자 컴파일에도 필요&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;compileOnlyApi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;선택적 기능. 쓰려면 소비자가 직접 추가&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;compileOnly&lt;span&gt;&amp;nbsp;&lt;/span&gt;+ 리플렉션 +&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;문서화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;내 바이너리엔 없지만 소비자는 반드시 받아야 함&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;implementation&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는 명시적 POM 선언&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 줄이 &lt;code&gt;pom.withXml&lt;/code&gt;이 필요해지는 지점이다. 그리고 그때조차, 가능하면 이렇게 쓰는 게 낫다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;// pom.withXml 대신
publishing {
    publications {
        release(MavenPublication) {
            from components.release
            pom {
                withXml {
                    // 정말 불가피한 경우에만
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;from components.release&lt;/code&gt;로 기본 의존성을 자동 생성시키고, 정말 예외적인 항목만 손대는 것이다. &lt;br /&gt;그리고 &lt;code&gt;pom.withXml&lt;/code&gt;을 썼다면 &lt;b&gt;README에 반드시 적어야 한다.&lt;/b&gt; 코드를 읽는 사람도, 스캐너도 그 안을 들여다보지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp; 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하나.&lt;/b&gt; &lt;code&gt;compileOnly&lt;/code&gt;가 보장하는 건 &quot;내 바이너리에 없다&quot;이지 &quot;소비자에게 안 간다&quot;가 아니다. 그 차이를 POM이 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;둘.&lt;/b&gt; 번들과 전파는 다른 문제다. 일반적인 Gradle 라이브러리는 애초에 번들하지 않으므로, &quot;우리 AAR에 없습니다&quot;는 대개 참이면서 동시에 무의미한 문장이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셋.&lt;/b&gt; &lt;code&gt;pom.withXml&lt;/code&gt;은 &lt;code&gt;build.gradle&lt;/code&gt;과 별개의 진실을 만든다. 강력하지만, 그 강력함이 정확히 위험한 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포되는 것을 알고 싶으면 &lt;code&gt;build.gradle&lt;/code&gt;이 아니라 &lt;b&gt;POM을 열어보면 된다.&lt;/b&gt; 5초면 끝난다.&lt;/p&gt;</description>
      <category>Programming/Spring</category>
      <category>gradle</category>
      <category>PETERICA</category>
      <category>pom withxml</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1070</guid>
      <comments>https://peterica.tistory.com/1070#entry1070comment</comments>
      <pubDate>Fri, 10 Jul 2026 23:35:10 +0900</pubDate>
    </item>
    <item>
      <title>[온디바이스AI] 로컬 LLM에서 중요한 것은 CPU가 아니라 메모리다 - 맥미니와 모델 크기의 관계</title>
      <link>https://peterica.tistory.com/1078</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhwv0U/dJMcajoF5qx/geeobYKEmsyWtYJopU0zB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhwv0U/dJMcajoF5qx/geeobYKEmsyWtYJopU0zB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhwv0U/dJMcajoF5qx/geeobYKEmsyWtYJopU0zB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhwv0U%2FdJMcajoF5qx%2FgeeobYKEmsyWtYJopU0zB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TL;DR&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 LLM 환경에서 가장 먼저 부딪히는 벽은 연산 성능보다 메모리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 모델은 &amp;ldquo;문장을 생성&amp;rdquo;하지만,&lt;br /&gt;큰 모델은 &amp;ldquo;컨텍스트를 유지하며 추론&amp;rdquo;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 로컬 AI 머신의 핵심은 CPU 속도가 아니라,&lt;br /&gt;얼마나 큰 모델과 Context를 메모리에 안정적으로 올릴 수 있는가에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 한 크루가 맥미니를 구매해서 로컬 LLM 기반 서비스를 직접 만들어 보고 싶다고 이야기했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 자연스럽게 이런 질문이 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;맥미니를 산다면 CPU가 중요할까, GPU가 중요할까?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 보통 코어 수나 GPU 성능을 먼저 보게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 실제로 로컬 LLM을 구성해보면 병목은 생각보다 다른 곳에서 먼저 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp; &amp;nbsp; 좀 더 자세한 글) &lt;a href=&quot;https://peterica.tistory.com/1067&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[AI] GPU vs PIM vs NPU - 내 시스템의 병목은 연산인가, 메모리인가&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 커질수록 가장 먼저 부족해지는 것은 연산 성능보다 메모리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Apple Silicon은 통합 메모리 구조이기 때문에 이 특징이 더 강하게 드러난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 로컬 LLM은 왜 메모리를 많이 먹는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 PC에서는 CPU 메모리와 GPU VRAM이 분리되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Apple Silicon은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU, GPU, Neural Engine이 모두 같은 메모리를 공유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 로컬 LLM을 실행할 때 메모리는 단순히 &amp;ldquo;프로그램 실행 공간&amp;rdquo;이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 요소들이 모두 메모리를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 가중치(weight)&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://llm-study-web.vercel.app/learn/kv-cache&quot;&gt;KV Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Context Window&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://llm-study-web.vercel.app/learn/embedding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Embedding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://llm-study-web.vercel.app/learn/attention&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Attention&lt;/a&gt; 중간 상태&lt;/li&gt;
&lt;li&gt;RAG 데이터 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 모델이 커질수록 단순히 저장 공간만 늘어나는 것이 아니라,&lt;br /&gt;&amp;nbsp; &amp;ldquo;계속 읽어와야 하는 데이터&amp;rdquo;가 급격히 증가한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 추론(decode) 단계에서는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 토큰 상태를 계속 참조하고&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://llm-study-web.vercel.app/learn/kv-cache&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KV Cache&lt;/a&gt;를 반복적으로 읽고&lt;/li&gt;
&lt;li&gt;긴 Context를 유지해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 AI 추론은 생각보다 &amp;ldquo;계산&amp;rdquo;보다 &amp;ldquo;메모리 접근&amp;rdquo; 비용이 더 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 로컬 LLM에서는 CPU 성능보다 메모리 용량과 대역폭이 훨씬 중요하게 체감된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작은 모델은 왜 쉽게 맥락을 잃는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 로컬 LLM을 접하면 7B~8B 모델만으로도 꽤 놀랍다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 번역도 되고,&lt;br /&gt;&amp;nbsp; 요약도 되고,&lt;br /&gt;&amp;nbsp; 코드도 어느 정도 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 조금만 복잡한 작업을 시키면 한계가 드러난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 조건을 잊는다&lt;/li&gt;
&lt;li&gt;긴 문서를 읽다가 핵심을 놓친다&lt;/li&gt;
&lt;li&gt;여러 제약을 동시에 유지하지 못한다&lt;/li&gt;
&lt;li&gt;Agent 흐름이 쉽게 무너진다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &amp;ldquo;프롬프트를 잘못 준 것 아닐까?&amp;rdquo;라고 생각했는데, 계속 사용하다 보니 단순 프롬프트 문제가 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작은 모델은 &amp;ldquo;문장을 생성&amp;rdquo;하는 능력은 괜찮지만, 긴 흐름을 유지하며 추론하는 능력은 제한적이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;말은 자연스럽지만, 생각의 지속성이 약하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 모델 크기가 커질수록 달라지는 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;14B~16B 정도부터는 확실히 느낌이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구간부터는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 조건을 동시에 유지하고&lt;/li&gt;
&lt;li&gt;문서 구조를 이해하려 하고&lt;/li&gt;
&lt;li&gt;이전 Context를 덜 잊는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 RAG와 연결했을 때 차이가 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7B에서는 검색된 문서 일부만 참고하다가 핵심을 놓치는 경우가 많았는데,&lt;br /&gt;16B급부터는 &amp;ldquo;문서를 기반으로 답하려는 느낌&amp;rdquo;이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 32B 구간부터는 체감이 더 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시점부터는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장기 Context 유지&lt;/li&gt;
&lt;li&gt;설계 reasoning&lt;/li&gt;
&lt;li&gt;Multi-Agent 흐름&lt;/li&gt;
&lt;li&gt;코드 구조 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 작업이 꽤 안정적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는 이 구간부터 로컬 LLM이 단순 장난감이 아니라 &amp;ldquo;도구&amp;rdquo;처럼 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실사용 관점으로 정리하면 대략 이런 느낌이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;모델급&lt;/td&gt;
&lt;td&gt;권장&amp;nbsp;메모리&lt;/td&gt;
&lt;td&gt;체감&amp;nbsp;능력&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B~8B&lt;/td&gt;
&lt;td&gt;16GB&lt;/td&gt;
&lt;td&gt;번역, 요약, 단순 코드 보조, 짧은 질답&lt;/td&gt;
&lt;td&gt;추론 깊이가 얕음. 긴 대화에서 맥락 손실 잦음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14B~16B&lt;/td&gt;
&lt;td&gt;24~32GB&lt;/td&gt;
&lt;td&gt;간단한 추론, 문서 구조 이해, 기본 Agent 가능&lt;/td&gt;
&lt;td&gt;여러 조건 비교/장기 계획 약함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32B&lt;/td&gt;
&lt;td&gt;64GB&lt;/td&gt;
&lt;td&gt;&amp;ldquo;생각하는 느낌&amp;rdquo;이 생김. RAG 활용 안정화&lt;/td&gt;
&lt;td&gt;복합 추론에서 아직 실수 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70B&lt;/td&gt;
&lt;td&gt;128GB+&lt;/td&gt;
&lt;td&gt;클라우드 모델에 가까운 품질 체감&lt;/td&gt;
&lt;td&gt;비용/속도 부담 큼&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MoE 대형 모델&lt;/td&gt;
&lt;td&gt;128GB~256GB&lt;/td&gt;
&lt;td&gt;특정 영역 매우 강력&lt;/td&gt;
&lt;td&gt;로컬 운영 난이도 매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 결국 맥미니에서 중요한 것은 메모리다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흥미로운 점은 &lt;b&gt;로컬 LLM 환경에서는 CPU 업그레이드보다 메모리 업그레이드 체감이 훨씬 크다는 점&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 다음처럼 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 &amp;ldquo;실행 가능&amp;rdquo;과 &amp;ldquo;안정적으로 운용 가능&amp;rdquo;은 완전히 다르다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리가 부족하면 일부 데이터를 SSD swap으로 넘기게 되는데, 이 순간부터 응답 속도가 급격히 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 단순히 느려지는 것을 넘어서,&amp;nbsp;추론 흐름 자체가 끊기는 느낌이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 로컬 LLM의 병목은 결국 메모리다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 로컬 AI 머신이라고 하면 CPU나 GPU 성능을 먼저 떠올리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 계속 실험해보면 생각보다 더 중요한 것은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;ldquo;얼마나 큰 모델과 Context를 메모리에 안정적으로 유지할 수 있는가&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 로컬 LLM 환경에서 중요한 것은 단순 FLOPS가 아니라,&lt;br /&gt;Context를 잃지 않고 계속 유지할 수 있는 메모리 구조에 더 가까운 것 같다.&lt;/p&gt;</description>
      <category>AI/RAG</category>
      <category>Mac Mini</category>
      <category>PETERICA</category>
      <category>로컬 llm</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1078</guid>
      <comments>https://peterica.tistory.com/1078#entry1078comment</comments>
      <pubDate>Wed, 20 May 2026 11:20:07 +0900</pubDate>
    </item>
    <item>
      <title>[독후감] 협상의 기술</title>
      <link>https://peterica.tistory.com/1077</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5JlSV/dJMcaiiXaCN/oMOkFwvu0uJ5tcbtX7k041/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5JlSV/dJMcaiiXaCN/oMOkFwvu0uJ5tcbtX7k041/img.png&quot; data-alt=&quot;https://product.kyobobook.co.kr/detail/S000000597965&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5JlSV/dJMcaiiXaCN/oMOkFwvu0uJ5tcbtX7k041/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5JlSV%2FdJMcaiiXaCN%2FoMOkFwvu0uJ5tcbtX7k041%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;680&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://product.kyobobook.co.kr/detail/S000000597965&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 예전에 읽었던 &lt;b&gt;세이노의 가르침&lt;/b&gt;에서 추천했던 책이다.&lt;br /&gt;나는&amp;nbsp;책을&amp;nbsp;읽을&amp;nbsp;때&amp;nbsp;단순히&amp;nbsp;내용을&amp;nbsp;따라가기보다,&amp;nbsp;작가와&amp;nbsp;대화하듯&amp;nbsp;읽으려고&amp;nbsp;한다.&amp;nbsp;그래서&amp;nbsp;먼저&amp;nbsp;작가가&amp;nbsp;어떤&amp;nbsp;사람인지&amp;nbsp;이해하고,&amp;nbsp;어떤&amp;nbsp;경험과&amp;nbsp;관점으로&amp;nbsp;자신의&amp;nbsp;생각을&amp;nbsp;전달하려&amp;nbsp;하는지&amp;nbsp;살펴본다.&amp;nbsp;그리고&amp;nbsp;각&amp;nbsp;챕터가&amp;nbsp;그&amp;nbsp;핵심적인&amp;nbsp;생각을&amp;nbsp;어떤&amp;nbsp;방식으로&amp;nbsp;풀어내고&amp;nbsp;있는지&amp;nbsp;작가의&amp;nbsp;입장에서&amp;nbsp;따라가며&amp;nbsp;읽으려&amp;nbsp;노력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ ChatGPT 작가의 사고방식을 이해하며 읽기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨을 이해하기 위해 단순히 책의 내용만 따라가지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책을&amp;nbsp;읽다가&amp;nbsp;생기는&amp;nbsp;궁금한&amp;nbsp;점이나&amp;nbsp;이해되지&amp;nbsp;않는&amp;nbsp;관점들을&amp;nbsp;계속&amp;nbsp;정리하며,&amp;nbsp;작가가&amp;nbsp;왜&amp;nbsp;이런&amp;nbsp;사례를&amp;nbsp;들었는지,&amp;nbsp;결국&amp;nbsp;어떤&amp;nbsp;생각을&amp;nbsp;전달하려&amp;nbsp;하는지&amp;nbsp;스스로&amp;nbsp;해석해보려&amp;nbsp;했다.&lt;br /&gt;&lt;br /&gt;특히&amp;nbsp;허브&amp;nbsp;코헨은&amp;nbsp;일반적인&amp;nbsp;자기계발서처럼&amp;nbsp;핵심&amp;nbsp;이론을&amp;nbsp;먼저&amp;nbsp;설명하는&amp;nbsp;방식으로&amp;nbsp;글을&amp;nbsp;쓰지&amp;nbsp;않는다.&amp;nbsp;오히려&amp;nbsp;다양한&amp;nbsp;사례를&amp;nbsp;먼저&amp;nbsp;보여주고,&amp;nbsp;독자가&amp;nbsp;그&amp;nbsp;안에서&amp;nbsp;스스로&amp;nbsp;핵심을&amp;nbsp;발견하도록&amp;nbsp;유도하는&amp;nbsp;방식에&amp;nbsp;가깝다.&amp;nbsp;그래서&amp;nbsp;처음에는&amp;nbsp;&amp;ldquo;이&amp;nbsp;사례는&amp;nbsp;왜&amp;nbsp;등장했을까?&amp;rdquo;,&amp;nbsp;&amp;ldquo;결국&amp;nbsp;무엇을&amp;nbsp;말하려는&amp;nbsp;걸까?&amp;rdquo;라는&amp;nbsp;생각이&amp;nbsp;자주&amp;nbsp;들었다.&lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;이런&amp;nbsp;흐름을&amp;nbsp;따라가다&amp;nbsp;보니,&amp;nbsp;허브&amp;nbsp;코헨은&amp;nbsp;단순히&amp;nbsp;협상&amp;nbsp;기술을&amp;nbsp;설명하려는&amp;nbsp;사람이&amp;nbsp;아니라,&amp;nbsp;독자가&amp;nbsp;기존의&amp;nbsp;고정된&amp;nbsp;사고방식을&amp;nbsp;다시&amp;nbsp;바라보게&amp;nbsp;만드는&amp;nbsp;사람이라는&amp;nbsp;점을&amp;nbsp;조금씩&amp;nbsp;이해하게&amp;nbsp;되었다.&amp;nbsp;그는&amp;nbsp;정답을&amp;nbsp;직접&amp;nbsp;설명하기보다,&amp;nbsp;사례를&amp;nbsp;통해&amp;nbsp;독자의&amp;nbsp;생각을&amp;nbsp;흔들고&amp;nbsp;스스로&amp;nbsp;깨닫게&amp;nbsp;만드는&amp;nbsp;방식으로&amp;nbsp;글을&amp;nbsp;전개하고&amp;nbsp;있었다.&lt;br /&gt;&lt;br /&gt;이런 흐름을 더 깊게 이해하기 위해, 나는 ChatGPT에게 허브 코헨의 관점과 말하는 방식을 함께 분석하여 작가처럼 답변해달라고 요청했다. 단순히 내용을 요약해달라는 것이 아니라, 마치 허브 코헨 작가와 대화하듯 &amp;ldquo;왜 이런 사례를 들었는가&amp;rdquo;, &amp;ldquo;이 상황에서 무엇을 말하려는 것인가&amp;rdquo;를 계속 질문하며 읽었다.&lt;br /&gt;&lt;br /&gt;특히&amp;nbsp;이해가&amp;nbsp;어려웠던&amp;nbsp;부분은&amp;nbsp;『세상에&amp;nbsp;불가능한&amp;nbsp;협상은&amp;nbsp;없다』&amp;nbsp;챕터였다.&amp;nbsp;사례는&amp;nbsp;계속&amp;nbsp;이어지는데&amp;nbsp;명확한&amp;nbsp;결론이&amp;nbsp;바로&amp;nbsp;드러나지&amp;nbsp;않아&amp;nbsp;처음에는&amp;nbsp;흐름을&amp;nbsp;따라가기&amp;nbsp;어려웠다.&amp;nbsp;하지만&amp;nbsp;대화를&amp;nbsp;이어가며,&amp;nbsp;허브&amp;nbsp;코헨은&amp;nbsp;협상&amp;nbsp;기술&amp;nbsp;자체를&amp;nbsp;설명하려는&amp;nbsp;사람이&amp;nbsp;아니라&amp;nbsp;독자의&amp;nbsp;고정된&amp;nbsp;사고방식을&amp;nbsp;흔들고&amp;nbsp;새로운&amp;nbsp;관점으로&amp;nbsp;상황을&amp;nbsp;바라보게&amp;nbsp;만드는&amp;nbsp;사람이라는&amp;nbsp;점을&amp;nbsp;조금씩&amp;nbsp;이해하게&amp;nbsp;되었다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;과정은&amp;nbsp;단순히&amp;nbsp;책&amp;nbsp;내용을&amp;nbsp;정리하는&amp;nbsp;것과는&amp;nbsp;달랐다.&amp;nbsp;오히려&amp;nbsp;작가의&amp;nbsp;머릿속&amp;nbsp;사고&amp;nbsp;흐름을&amp;nbsp;따라가며&amp;nbsp;&amp;ldquo;왜&amp;nbsp;이런&amp;nbsp;생각을&amp;nbsp;했는가&amp;rdquo;를&amp;nbsp;추적하는&amp;nbsp;경험에&amp;nbsp;가까웠다.&amp;nbsp;덕분에&amp;nbsp;책의&amp;nbsp;내용을&amp;nbsp;외우기보다,&amp;nbsp;허브&amp;nbsp;코헨이&amp;nbsp;세상을&amp;nbsp;바라보는&amp;nbsp;방식을&amp;nbsp;조금&amp;nbsp;더&amp;nbsp;이해하며&amp;nbsp;읽을&amp;nbsp;수&amp;nbsp;있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작가에 대한 이해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot; data-sfc-root=&quot;c&quot; data-wiz-uids=&quot;Ne8njb_h&quot; data-sfc-cb=&quot;&quot; data-processed=&quot;true&quot; data-copy-service-computed-style=&quot;font-family: Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot;&gt;&lt;span data-subtree=&quot;aimfl,mfl&quot; data-processed=&quot;true&quot; data-copy-service-computed-style=&quot;font-family: Arial, sans-serif; font-size: 16px; font-weight: 400; margin: 0px; text-decoration: none; border-bottom: 0px rgb(230, 232, 240);&quot;&gt;허브 코헨(Herb Cohen)&lt;/span&gt;&lt;/span&gt;(1931~2015)은 50년 넘게 미국 대통령, 법무부, FBI, CIA 등에서 협상 자문을 맡아온 세계적인 실전 협상 전문가이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 특징은 &lt;b&gt;협상을 비즈니스 기술서&lt;/b&gt;가 아니라 &lt;b&gt;일상 인간관계의 본질&lt;/b&gt;로 본다는 점이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;여러분 각자 자신의 자리와 신념의 체계 안에서 자신만의 고유한 요구를 바탕으로 자신이 원하는 것을 얻는 방법을 깨닫기 바란다.&lt;br /&gt;머릿말, p9&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그는 협상을 기술적이나 법률적으로 설명하지 않고, 일상에 포함한 인간 행동의 문제로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 책에서도 법칙이나 이론보다 실제 사례와 심리 묘사가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그의 문체 특징도 뚜렷하다.&lt;br /&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;이론보다&amp;nbsp;이야기&amp;nbsp;중심&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;우화&amp;middot;실화&amp;middot;농담이&amp;nbsp;많음&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;상대&amp;nbsp;심리를&amp;nbsp;해석하는&amp;nbsp;방식&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;독자에게&amp;nbsp;&amp;ldquo;세상은&amp;nbsp;이렇게&amp;nbsp;돌아간다&amp;rdquo;를&amp;nbsp;체험시키는&amp;nbsp;구조&lt;br /&gt;그래서 책을 읽다 보면 &amp;ldquo;논리적으로 설명한다&amp;rdquo;기보다, 마치 경험 많은 협상가가 옆에서 사례를 들려주는 느낌이 강하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨의 핵심 관점은 크게 세 가지로 정리된다.&lt;br /&gt;&lt;b&gt;협상은 정보, 시간, 힘의 게임이다.&lt;/b&gt; p22&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;협상 능력이란 행동에 영향을 미치는 정보, 시간, 힘을 분석하여 요구가 자신이 원하는 방식대로 이루어지도록 만드는 것이다. p23&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;협상은 한계를 넘어서는 힘인 것이다. 하지만 그것이 윤리적 판단은 중립적이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;협상가로서 위험을 감수하고, 과거의 경험에서 벗어나 자신이 세운 가정에 도전하고, 목표 수준을 높이고, 기대치를 높여야한다. p33&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그의 글에는 &lt;b&gt;극단적인 초기 입장&lt;/b&gt;, &lt;b&gt;허세&lt;/b&gt;, &lt;b&gt;시간 끌기&lt;/b&gt;, &lt;b&gt;애매한 표현&lt;/b&gt;, &lt;b&gt;심리 압박&lt;/b&gt; 같은 요소가 자주 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 책을 읽으면서&amp;nbsp; 불편함을 느꼈던 부분도 바로 이런 지점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 기준에서는 다소 공격적이거나 조작적으로 보일 수 있는 사례도 많아 비도덕적으로 느껴진다. 예로 말보로 담배 한개비 p78&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;힘이 그 자체로 좋거나 나쁘지는 않다. 도덕적이거나 비도덕적이지도 않다. 윤리적인 것도 비윤리적인 것도 아니다. 힘은 중립적이다. p73&lt;br /&gt;가능한 여러 목표에 따라 힘을 나눠보면 목표는 유쾌하게 '좋은 것' 또는 가증스럽게 '나쁜 것'일 수 있지만, 그 목표를 달성하는 데 사용하는 힘은 전기나 바람처럼 중립적이다. p74&lt;br /&gt;합당한 범위 내에서, 자신이 가진 옵션에 대해 알고 있고, 자신이 세운 가정을 시험해보고, 확실한 정보를 기반으로 계산한 위험을 감수하고, 자신이 힘을 가지고 있다고 믿는다면 당신은 자신이 원하는 것을 얻을 수 있다. p78&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;다만 허브 코헨은 &lt;b&gt;협상이 단순히 속이는 기술&lt;/b&gt;을 말하려는 사람은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 인간 사회 자체가 완전히 합리적이지 않다는 현실을 인정하고, 그 현실 속에서 손해 보지 않기 위한 관찰력을 강조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;윤리적인 윈윈 협상가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작가는 이상적인 윤리만으로는 실제 협상에서 밀릴 수 있다는 현실주의적 시각이 강하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;일반적으로 협상이라고 하면 자신의 이익을 얻기 위한 기술을 떠올리기 쉽지만, 작가는 오히려 상대를 억압하거나 파괴하지 않고 사람들의 사고방식 자체를 변화시키려 했던 인물들을 예로 들고 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;두 사람은 초라한 옷을 입고 돌아다니며 사람들에게 질문하며 정보를 수집했다. 1명은 삼단논법을, 다른 1명은 비유를 사용했다. 그들에게는 목표와 기준이 있었다. 그들은 기꺼이 위험을 감수했다. ~중간생략~&lt;br /&gt;그 두 사람은 예수 그리스도와 소크라테스다. 내가 보기에 그 둘은 협상가였다. 그들은 윤리적인 윈윈 협상가들이었고, 힘을 가진 사람들이었다. p23&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예수님는 비도더적인 상황에서 십자가에 못박혀 돌아가셨다. 하지만 그의 협상가로서 성경을 통해 오늘도 그 힘은 영향력을 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소크라테스도 당시&amp;nbsp;아테네&amp;nbsp;사회의&amp;nbsp;기존&amp;nbsp;가치와&amp;nbsp;권위에&amp;nbsp;계속&amp;nbsp;질문을&amp;nbsp;던졌고,&amp;nbsp;결국&amp;nbsp;많은&amp;nbsp;사람들에게&amp;nbsp;불편한&amp;nbsp;존재가&amp;nbsp;되었다.&amp;nbsp;특히&amp;nbsp;젊은이들을&amp;nbsp;타락시킨다는&amp;nbsp;혐의와&amp;nbsp;신을&amp;nbsp;부정했다는&amp;nbsp;이유로&amp;nbsp;재판에&amp;nbsp;넘겨졌고,&amp;nbsp;끝내&amp;nbsp;사형&amp;nbsp;선고를&amp;nbsp;받아&amp;nbsp;독배를&amp;nbsp;마시고&amp;nbsp;죽었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예수는&amp;nbsp;사랑과&amp;nbsp;용서를&amp;nbsp;통해&amp;nbsp;사람들의&amp;nbsp;삶의&amp;nbsp;방향을&amp;nbsp;바꾸려&amp;nbsp;했고,&amp;nbsp;소크라테스는&amp;nbsp;질문과&amp;nbsp;대화를&amp;nbsp;통해&amp;nbsp;스스로&amp;nbsp;생각하게&amp;nbsp;만들려&amp;nbsp;했다.&amp;nbsp;둘&amp;nbsp;다&amp;nbsp;강압적으로&amp;nbsp;상대를&amp;nbsp;이기려&amp;nbsp;하지&amp;nbsp;않았고,&amp;nbsp;오히려&amp;nbsp;사람들에게&amp;nbsp;새로운&amp;nbsp;관점을&amp;nbsp;제시하며&amp;nbsp;스스로&amp;nbsp;깨닫게&amp;nbsp;만들었다는&amp;nbsp;공통점이&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현실 세계에서 도덕적 방식은 언제나 환영받는 것은 아니었다. 기존 질서와 사고방식을 흔드는 사람은 결국 불편한 존재가 되기 쉽기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 허브 코헨이 말하는 &lt;b&gt;&amp;ldquo;윤리적인 윈윈 협상가&amp;rdquo;&lt;/b&gt;라는 표현은 단순히 착하고 부드러운 사람이라는 뜻이 아니라, 자신의 기준과 가치관을 유지하면서도 사람들의 생각과 선택을 변화시키려 했던 사람들에 더 가까워 보였다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;권력에 의지하기 위해 폭력적이 될 필요는 없고,&amp;nbsp;&lt;br /&gt;양심적으로 말하기 위해 온순할 필요는 없다.&lt;br /&gt;가장 효과적인 행동은 권력에 의지하면서&amp;nbsp;&lt;br /&gt;양심적으로 행동하는 것이다. (바바라 데밍) p369&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이해한 작가가 말하고자 하는 협상가는 상대를 이기려는 단일 목적이 아니라,&amp;nbsp; 사람들에게 새로운 관점을 제시하여 상생하는 윈윈 협상가이다. 다만 그 협상은 한계를 넘어서기 위해서 자신의 힘, 시간, 정보를 기반으로 합당한 범위 내에서 위험을 감수해야 한다. 작가는 이를 설명하기 위해 이 책에서 법칙이나 이론보다 실제 사례와 심리 묘사로 스스로 해석하게 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 협상으로 이루어진 세상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 협상이란 무엇인가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨은 협상을 단순한 대화 기술이 아니라, 상대의 선택과 행동을 변화시키는 과정으로 설명한다.&lt;br /&gt;특히 협상은 특별한 상황이 아니라 인간관계 속에서 이미 매일 반복되고 있는 일상적인 구조라고 바라본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인상적이었던 점은 협상을 &amp;ldquo;이기는 기술&amp;rdquo;보다 &amp;ldquo;원하는 결과를 얻기 위한 조율 과정&amp;rdquo;으로 설명한다는 점이었다.&lt;br /&gt;결국 협상은 말을 잘하는 능력보다, 사람과 상황을 어떻게 이해하고 움직이느냐의 문제에 가까워 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 세상에 불가능한 협상은 없다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 챕터는 처음 읽을 때 가장 이해하기 어려웠던 부분이었다.&lt;br /&gt;사례는 계속 이어지는데 명확한 결론이 바로 드러나지 않았기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 계속 읽다 보니 허브 코헨은 &amp;ldquo;불가능한 협상은 없다&amp;rdquo;는 말을 낙관적으로 사용하는 것이 아니었다.&lt;br /&gt;오히려 사람들이 스스로 조건을 고정된 것으로 생각하기 때문에 협상이 막히는 것이며, 관점을 바꾸면 새로운 선택지가 보일 수 있다는 점을 말하려 했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 챕터의 핵심은 협상 기술보다, 기존 사고방식을 흔드는 데 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 협상의 세계에 발 들여놓기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨은 협상을 거창한 비즈니스 기술이 아니라, 이미 우리의 삶 전체에 포함된 구조로 설명한다.&lt;br /&gt;가족, 친구, 직장, 식당 주문처럼 아주 작은 선택과 조율 속에서도 협상은 계속 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 챕터를 읽으며 느꼈던 것은, 협상을 특별한 능력으로 바라보기보다 인간관계를 이해하는 관점 자체로 받아들이고 있다는 점이었다. 경쟁을 붙이기, 필요 충족하기, 할인 중 등 직원과의 관계에서 안될 것 같은 협상에 정말 다양한 대안을 제시하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 협상을 좌우하는 3가지 변수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨는 협상의 흐름을 결정하는 핵심 요소로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 힘(당신에게 힘이 있다는 사실을 인지하라),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시간(협상은 인내심 싸움이다),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정보(상대가 말하지 않는 정보까지 캐내라)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 제시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 협상은 결국 이 세 가지 변수를 얼마나 잘 이해하고 활용하느냐에 따라 결과가 달라진다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 인상적이었던 점은, 이 요소들이 단순히 객관적인 사실만으로 작동하지 않는다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 힘보다 &lt;b&gt;&amp;ldquo;힘이 있다고 믿게 만드는 인식&amp;rdquo;&lt;/b&gt;이 더 중요하기도 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 자체보다 &lt;b&gt;&amp;ldquo;누가 더 조급한가&amp;rdquo;&lt;/b&gt;가 협상의 흐름을 결정하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 협상은 논리만의 문제가 아니라 인간 심리와 상황 인식의 문제에 더 가까워 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작가는 힘을 매우 현실적으로 바라본다.&lt;br /&gt;힘은 선하거나 악한 것이 아니라 목표를 이루기 위해 사용하는 중립적인 도구라고 설명한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;힘은 그 자체로 좋거나 나쁘지 않다. 힘은 결과가 아니라 수단이다.&amp;rdquo; p73~76&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 책 속에서는 경쟁 유도, 시간 끌기, 애매한 표현, 심리 압박 같은 현실적인 사례들도 자주 등장한다. 처음에는 이런 방식들이 다소 불편하게 느껴졌다. 하지만 허브 코헨은 이를 단순한 속임수로 설명하기보다, 현실 사회가 실제로 어떻게 움직이는지를 보여주려 했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 챕터에서 가장 중요하게 느껴졌던 부분은, 협상은 말을 잘하는 사람이 유리한 게임이 아니라는 점이었다. 상대가 무엇을 원하고 있는지, 누가 더 급한지, 어떤 정보와 선택지를 가지고 있는지를 얼마나 잘 이해하느냐가 협상의 흐름을 결정하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 2가지 협상 스타일&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨은 협상 방식을 크게 두 가지 스타일로 나누어 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7.&amp;nbsp;&amp;ldquo;무슨&amp;nbsp;수를&amp;nbsp;써서라도&amp;nbsp;이긴다&amp;rdquo;&amp;nbsp;소련&amp;nbsp;스타일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 상대를 이겨야만 성공이라고 생각하는 &amp;ldquo;소련 스타일&amp;rdquo;이다. 이 방식은 극단적인 요구, 심리 압박, 시간 끌기, 허세 같은 방법을 적극적으로 사용한다. 협상을 서로의 조율 과정이 아니라 승패의 문제로 바라보며, 상대보다 우위를 점하는 것 자체를 중요하게 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이런 방식이 다소 공격적이고 비도덕적으로 느껴졌다. 하지만 허브 코헨은 이런 사례들을 단순히 따라 하라고 설명하기보다, 현실 사회에서는 실제로 이런 방식으로 움직이는 사람들도 존재한다는 점을 보여주려 했던 것 같다. 결국 이런 스타일을 이해해야 상대의 전략에 휘둘리지 않을 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;8.&amp;nbsp;&amp;ldquo;협상은&amp;nbsp;쌍방을&amp;nbsp;위한&amp;nbsp;것&amp;rdquo;&amp;nbsp;윈윈&amp;nbsp;스타일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 작가가 궁극적으로 지향하는 방식은 &amp;ldquo;윈윈 스타일&amp;rdquo;이다. 이 방식은 상대를 무너뜨리는 것이 아니라, 서로가 원하는 것을 조율하며 함께 만족할 수 있는 결과를 찾는 협상에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흥미로웠던 점은 허브 코헨이 단순히 착하고 양보하는 태도를 윈윈이라고 설명하지 않는다는 점이었다. 오히려 자신의 힘과 정보, 기준을 명확히 이해한 상태에서 상대와 현실적인 균형점을 찾는 과정을 더 중요하게 바라본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;9. 상호&amp;nbsp;만족을&amp;nbsp;위한&amp;nbsp;협상의&amp;nbsp;기술&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 챕터에서는 실제로 어떻게 서로 만족할 수 있는 협상을 만들어가는지 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 책 초반에 예수와 소크라테스를 &amp;ldquo;윤리적인 윈윈 협상가&amp;rdquo;로 설명한 부분이 인상적이었다. 두 사람 모두 상대를 강압적으로 이기려 하기보다, 질문과 대화를 통해 사람들의 사고방식 자체를 변화시키려 했기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;나는 유혹이나 사기 게임에 대해 말하는 것이 아니다. 협력적인 셥셩에서는 음해, 위협, 수작을 부리기 위한 유려한 말솜씨, 조작, 허튼소리, 수완이나 흥정이 필요없다.&amp;nbsp;&lt;br /&gt;반대로 지속적인 관계를 만들고 유지하는 방향으로 전략을 수립하기 제안한다. 신뢰하는 당사자들은 동증하며 상호 간에 이익을 취할 수 있는 방향으로 문제를 해결하기 위해 에너지를 쏟아붓는다. p303&lt;br /&gt;&amp;nbsp;승리란 자신의 신념과 가치에 맞게 당신의 요구를 충족시키는 것을 의미한다. 승리는 상대측이 정말로 원하는 것을 아라내고 그들에게 당신이 원하는 것을 얻을 방법을 보여주는 것을 의미한다. p308&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 이 챕터를 읽으며 느꼈던 것은, 허브 코헨이 말하는 좋은 협상가는 단순히 말을 잘하거나 상대를 이기는 사람이 아니라는 점이었다. 자신의 기준과 가치관을 유지하면서도, 사람들의 선택과 생각을 긍정적인 방향으로 움직일 수 있는 사람이 더 이상적인 협상가에 가까워 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;어디서나,&amp;nbsp;누구와도&amp;nbsp;협상하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허브 코헨은 마지막 파트에서 협상을 단순한 이론이 아니라, 현실 속 인간관계와 실제 상황에서 어떻게 적용해야 하는지 설명한다.&lt;br /&gt;특히&amp;nbsp;이&amp;nbsp;챕터에서는&amp;nbsp;협상이&amp;nbsp;결국&amp;nbsp;사람&amp;nbsp;사이에서&amp;nbsp;이루어지는&amp;nbsp;일이라는&amp;nbsp;점을&amp;nbsp;반복해서&amp;nbsp;강조한다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;10.&amp;nbsp;비대면&amp;nbsp;전화&amp;nbsp;협상에서&amp;nbsp;합의&amp;nbsp;각서는&amp;nbsp;필수&lt;/b&gt;&lt;br /&gt;허브 코헨은 전화 협상이나 비대면 협상에서는 특히 기록과 문서가 중요하다고 설명한다.&lt;br /&gt;대면 상황에서는 표정과 분위기, 관계를 통해 어느 정도 의도가 전달되지만, 전화나 문서 중심 협상에서는 작은 표현 차이도 오해로 이어질 수 있기 때문이다.&lt;br /&gt;그래서 그는 협상이 끝난 뒤 반드시 합의 내용을 정리하고 확인하는 과정을 중요하게 본다.&lt;br /&gt;특히&amp;nbsp;기억에&amp;nbsp;의존하지&amp;nbsp;말고,&amp;nbsp;서로가&amp;nbsp;이해한&amp;nbsp;내용을&amp;nbsp;문서로&amp;nbsp;남겨야&amp;nbsp;이후의&amp;nbsp;갈등과&amp;nbsp;책임&amp;nbsp;문제를&amp;nbsp;줄일&amp;nbsp;수&amp;nbsp;있다고&amp;nbsp;말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 확실히 공감이 되는 부분인데, 나 또한 회의록을 중요시 여기기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 구두 내용이라도 메신저에 짧게 공유하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;읽으며&amp;nbsp;느꼈던&amp;nbsp;점은,&amp;nbsp;협상은&amp;nbsp;단순히&amp;nbsp;말을&amp;nbsp;잘하는&amp;nbsp;기술이&amp;nbsp;아니라&amp;nbsp;결국&amp;nbsp;신뢰와&amp;nbsp;책임의&amp;nbsp;문제라는&amp;nbsp;점이었다.&lt;br /&gt;좋은&amp;nbsp;협상은&amp;nbsp;상대를&amp;nbsp;설득하는&amp;nbsp;데서&amp;nbsp;끝나는&amp;nbsp;것이&amp;nbsp;아니라,&amp;nbsp;이후에도&amp;nbsp;서로&amp;nbsp;같은&amp;nbsp;이해를&amp;nbsp;유지할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;구조를&amp;nbsp;만드는&amp;nbsp;과정에&amp;nbsp;가까워&amp;nbsp;보였다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;11.&amp;nbsp;결정권자와&amp;nbsp;협상하라&lt;/b&gt;&lt;br /&gt;이 챕터에서 허브 코헨은 매우 현실적인 이야기를 한다.&lt;br /&gt;아무리&amp;nbsp;좋은&amp;nbsp;협상을&amp;nbsp;해도,&amp;nbsp;실제&amp;nbsp;결정을&amp;nbsp;내릴&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;사람과&amp;nbsp;이야기하고&amp;nbsp;있다면&amp;nbsp;결과는&amp;nbsp;바뀌지&amp;nbsp;않을&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연한 부분이다.&lt;br /&gt;&lt;br /&gt;실무자와&amp;nbsp;긴&amp;nbsp;시간&amp;nbsp;대화를&amp;nbsp;나누더라도,&amp;nbsp;결국&amp;nbsp;최종&amp;nbsp;승인권자가&amp;nbsp;따로&amp;nbsp;있다면&amp;nbsp;협상&amp;nbsp;내용이&amp;nbsp;뒤집히거나&amp;nbsp;처음부터&amp;nbsp;다시&amp;nbsp;시작되는&amp;nbsp;경우가&amp;nbsp;많다.&amp;nbsp;그래서&amp;nbsp;작가는&amp;nbsp;협상&amp;nbsp;초반부터&amp;nbsp;&amp;ldquo;누가&amp;nbsp;실제&amp;nbsp;결정권자인가&amp;rdquo;를&amp;nbsp;파악하는&amp;nbsp;것을&amp;nbsp;매우&amp;nbsp;중요하게&amp;nbsp;설명한다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;부분을&amp;nbsp;읽으며&amp;nbsp;협상은&amp;nbsp;단순히&amp;nbsp;논리의&amp;nbsp;문제가&amp;nbsp;아니라&amp;nbsp;구조의&amp;nbsp;문제라는&amp;nbsp;생각이&amp;nbsp;들었다.&lt;br /&gt;무엇을&amp;nbsp;말할&amp;nbsp;것인가도&amp;nbsp;중요하지만,&amp;nbsp;누구와&amp;nbsp;이야기하고&amp;nbsp;있는가&amp;nbsp;역시&amp;nbsp;협상의&amp;nbsp;중요한&amp;nbsp;변수였다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;12.&amp;nbsp;사람&amp;nbsp;대&amp;nbsp;사람으로&amp;nbsp;협상하라&lt;/b&gt;&lt;br /&gt;허브 코헨이 마지막까지 강조하는 것은 결국 협상은 사람과 사람 사이의 관계라는 점이었다.&lt;br /&gt;&lt;br /&gt;회사,&amp;nbsp;조직,&amp;nbsp;계약이라는&amp;nbsp;형식을&amp;nbsp;이야기하고&amp;nbsp;있지만,&amp;nbsp;실제로는&amp;nbsp;결국&amp;nbsp;감정을&amp;nbsp;가진&amp;nbsp;사람들이&amp;nbsp;선택하고&amp;nbsp;판단한다.&amp;nbsp;그래서&amp;nbsp;그는&amp;nbsp;지나치게&amp;nbsp;형식적이거나&amp;nbsp;딱딱한&amp;nbsp;태도보다,&amp;nbsp;인간적으로&amp;nbsp;관계를&amp;nbsp;만들고&amp;nbsp;상대를&amp;nbsp;이해하려는&amp;nbsp;태도를&amp;nbsp;더&amp;nbsp;중요하게&amp;nbsp;바라본다.&lt;br /&gt;&lt;br /&gt;특히&amp;nbsp;인상적이었던&amp;nbsp;부분은,&amp;nbsp;사람들은&amp;nbsp;논리만으로&amp;nbsp;움직이지&amp;nbsp;않는다는&amp;nbsp;점이었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;협상의 터닝 포인트(p375)&lt;br /&gt;1. 경찰관의 고나심을 교통 위반 딱지에서 다른 곳으로 돌린다.&lt;br /&gt;2. 경찰관이 당신을 개인적인 대상으로 인식하게 한다.&lt;br /&gt;3. 경찰관이 교통 위반 딱지에 볼펜을 갖다 대는 것을 막거나 적어도 지연시킨다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존중받고&amp;nbsp;있다고&amp;nbsp;느끼는가,&amp;nbsp;자신의&amp;nbsp;입장이&amp;nbsp;이해받고&amp;nbsp;있다고&amp;nbsp;느끼는가&amp;nbsp;같은&amp;nbsp;감정적인&amp;nbsp;요소들도&amp;nbsp;협상&amp;nbsp;결과에&amp;nbsp;큰&amp;nbsp;영향을&amp;nbsp;미친다.&lt;br /&gt;&lt;br /&gt;결국&amp;nbsp;허브&amp;nbsp;코헨이&amp;nbsp;말하는&amp;nbsp;좋은&amp;nbsp;협상가는&amp;nbsp;단순히&amp;nbsp;말을&amp;nbsp;잘하거나&amp;nbsp;상대를&amp;nbsp;압박하는&amp;nbsp;사람이&amp;nbsp;아니었다.&amp;nbsp;상대를&amp;nbsp;하나의&amp;nbsp;인간으로&amp;nbsp;이해하고,&amp;nbsp;서로의&amp;nbsp;관계&amp;nbsp;속에서&amp;nbsp;현실적인&amp;nbsp;합의점을&amp;nbsp;만들어갈&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;사람이&amp;nbsp;더&amp;nbsp;좋은&amp;nbsp;협상가에&amp;nbsp;가까워&amp;nbsp;보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『협상의 기술』은 단순히 협상 기술을 설명하는 책이 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실 이 책을 읽으며 힘들었던 점은, 허브 코헨이 정답을 직접 설명하기보다 사례를 통해 그 의도 스스로 깨닫게 만든다는 점이었다. 그래서 이 책은 읽는 과정에서 중심 과제를 놓치게 되는 경향이 있었다. 작가의 핵심 의도를 먼저 파악하는게 중요하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;처음에는 말 잘하는 방법이나 설득 기술에 대한 이야기라고 생각했지만, 읽을수록 인간의 심리와 관계, 그리고 사람들이 어떻게 선택하고 움직이는지를 관찰하는 책에 더 가까웠다. 다시 말하지만, 중간 결론이 없어 ??만 남았지만 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 특히 허브 코헨은 협상을 단순한 승패의 문제로 바라보지 않았다. 현실적으로는 힘, 시간, 정보 같은 요소들이 매우 중요하게 작동하지만, 궁극적으로는 상대를 무너뜨리는 것이 아니라 서로가 원하는 방향을 조율하며 함께 살아가는 과정으로 협상을 바라보고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 물론 책 속 일부 사례들은 지금 기준에서 다소 공격적이거나 불편하게 느껴지기도 했다. 하지만 그런 부분들조차 현실 사회가 실제로 어떻게 움직이는지를 보여주기 위한 관찰의 과정이라고 생각하니, 단순히 기술을 배우는 느낌보다는 세상을 바라보는 새로운 관점을 배우는 경험에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 결국 내가 이해한 허브 코헨의 핵심 메시지는 단순히 &amp;ldquo;잘 협상하는 법&amp;rdquo;이 아니었다. 사람과 상황을 더 넓게 이해하고, 자신의 기준을 유지하면서도 서로가 함께 살아갈 수 있는 방향을 찾는 것. 그것이 그가 말하는 궁극적인 윈윈 협상가의 모습에 더 가까워 보였다.&lt;/p&gt;</description>
      <category>책이야기</category>
      <category>PETERICA</category>
      <category>독후감</category>
      <category>협상의 기술</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1077</guid>
      <comments>https://peterica.tistory.com/1077#entry1077comment</comments>
      <pubDate>Sun, 10 May 2026 00:25:26 +0900</pubDate>
    </item>
    <item>
      <title>[AI] GPU vs PIM vs NPU - 내 시스템의 병목은 연산인가, 메모리인가</title>
      <link>https://peterica.tistory.com/1067</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYukyP/dJMcad2ThL3/YEQKub7tupFXW97eAaFalk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYukyP/dJMcad2ThL3/YEQKub7tupFXW97eAaFalk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYukyP/dJMcad2ThL3/YEQKub7tupFXW97eAaFalk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYukyP%2FdJMcad2ThL3%2FYEQKub7tupFXW97eAaFalk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #666666;&quot; data-ke-size=&quot;size16&quot;&gt;TL;DR&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPU와 PIM은 같은 축의 다음 세대가 아니라, 서로 다른 병목을 푸는 도구다.&lt;/li&gt;
&lt;li&gt;GPU는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;FLOPS를 늘리고&lt;/b&gt;, PIM은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Byte 이동을 줄인다&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 경쟁재가 아니라 조합재.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/767&quot;&gt;[AI] 인공지능에 대한 이해(2017에 정리한 리포트)&lt;/a&gt;에서 PIM을 언급하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://peterica.tistory.com/767&quot;&gt;[AI] PIM(Processing In Memory)란?&lt;/a&gt;에서 PIM을 단독으로 정리한 적이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이번에는 GPU&amp;middot;NPU와 함께 놓고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;'병목 위치'&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;관점에서 다시 본다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델에 GPU를 쓰는 이유에 대한 글을 읽다가 자연스럽게 PIM(Processing In Memory)이 떠올랐다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 &quot;GPU 다음 세대&quot; 정도로 막연하게 묶어 두고 있었는데,&lt;br /&gt;정리하다 보니 둘은 같은 축이 아니었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결론부터 말하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GPU는 연산을 빠르게 하는 구조, PIM은 데이터 이동을 줄이는 구조다.&lt;/b&gt;&lt;br /&gt;같은 문제의 다른 단계를 푸는 도구다. 이 글은 그 차이를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;병목 위치 관점&lt;/b&gt;에서 정리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 연산이 아니라 데이터 이동이 비싸졌다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPU의 강점은 분명하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대규모 병렬 행렬&amp;middot;벡터 연산. 딥러닝의 핵심 연산 &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;행렬 곱, 벡터 누적&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;은 GPU의 SIMT 구조와 정확히 맞아떨어진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문제는 모델이 커지면서 병목 위치가 이동했다는 점이다. 파라미터, activation, batch가 늘어나면 다음 증상이 나타난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 대역폭이 먼저 포화된다&lt;/li&gt;
&lt;li&gt;GPU 코어는 대기 상태인데 데이터가 도착하지 않는다&lt;/li&gt;
&lt;li&gt;전력의 상당 부분이 &quot;연산&quot;이 아니라 &quot;이동&quot;하는데 쓰인다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;연산은 점점 싸지고, 데이터 이동이 비싸지는 방향으로 비용 구조가 뒤집혔다. 이 지점에서 PIM이 등장한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ GPU vs PIM &amp;mdash; 두 구조의 핵심 차이&lt;/h4&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;축&lt;/td&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;PIM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설계 목표&lt;/td&gt;
&lt;td&gt;FLOPS 극대화&lt;/td&gt;
&lt;td&gt;Byte 이동 최소화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;연산 위치&lt;/td&gt;
&lt;td&gt;코어에서 (DRAM에서 로드 후 계산)&lt;/td&gt;
&lt;td&gt;메모리 내부 (in-situ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;강점 워크로드&lt;/td&gt;
&lt;td&gt;compute-bound (조밀 행렬곱)&lt;/td&gt;
&lt;td&gt;memory-bound (attention, embedding lookup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;약점&lt;/td&gt;
&lt;td&gt;메모리 대역폭에 종속&lt;/td&gt;
&lt;td&gt;일반 연산 유연성이 낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비유&lt;/td&gt;
&lt;td&gt;빠른 셰프, 식재료는 매번 멀리서 옮겨 옴&lt;/td&gt;
&lt;td&gt;식재료 옆에서 바로 손질하는 작은 부엌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;표를 한 줄로 압축하면 -&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPU는 &quot;&lt;b&gt;FLOPS를 늘리는 전략&lt;/b&gt;&quot;,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PIM은 &quot;&lt;b&gt;Byte 이동을 깎는 전략&lt;/b&gt;&quot;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘은 경쟁재가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 층의 병목을 푸는 조합재&lt;/b&gt;다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ AI 워크로드는 왜 memory-bound 인가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/1071&quot;&gt;[온디바이스AI] 내 폰으로 나만의 RAG 만들기(온디바이스 RAG 최소 아키텍처)&lt;/a&gt;를 진행하면서 흥미로운 차이를 확인했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;맥미니 M1(8GB)과 갤럭시 S23 Ultra(12GB)에서 동일 모델을 실행했을 때,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로는 CPU 성능이 더 좋은 맥이 빠를 것으로 예상했지만 실제 응답 속도는 메모리가 더 큰 갤럭시가 더 빨랐다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 결과는 연산 성능보다 메모리 접근이 병목이 되는 상황을 잘 보여준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 추론 과정에서는 단순한 계산보다, 큰 파라미터와 KV 캐시를 반복적으로 읽어오는 비용이 더 크게 작용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말을 풀어서 보면, AI 모델은 &amp;ldquo;계산을 많이 하는 것&amp;rdquo;보다 &amp;ldquo;필요한 데이터를 계속 가져오는 것&amp;rdquo;에 더 많은 시간을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 두 가지 작업이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 &lt;b&gt;Attention&lt;/b&gt;이다.&lt;br /&gt;모델이 문장을 이해하거나 다음 단어를 예측할 때, 앞에서 나온 단어들을 다시 참고하는 과정이다.&lt;br /&gt;이때 이전에 계산해 둔 값(KV 캐시)을 계속 읽어와야 하는데, 문장이 길어질수록 이 데이터 양이 빠르게 증가한다.&lt;br /&gt;즉, 계산 자체보다 &amp;ldquo;과거 정보를 다시 가져오는 비용&amp;rdquo;이 더 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;Embedding lookup&lt;/b&gt;이다.&lt;br /&gt;단어를 숫자로 바꾸는 과정이라고 보면 된다.&lt;br /&gt;모델은 &amp;ldquo;사전&amp;rdquo;처럼 아주 큰 테이블(차원)을 가지고 있고, 각 단어마다 대응되는 벡터가 저장되어 있다.&lt;br /&gt;특정 단어가 들어오면 이 테이블에서 해당 위치를 찾아 값을 꺼내는데, 문제는 이 접근이 랜덤하게 일어난다는 점이다.&lt;br /&gt;그래서 CPU나 GPU의 캐시가 잘 활용되지 않고, 결국 메모리에서 직접 데이터를 가져오는 비용이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 두 경우 모두 공통점은 하나다.&lt;br /&gt;계산이 복잡해서 느린 것이 아니라, 필요한 데이터를 계속 불러오느라 시간이 걸린다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 AI 워크로드는 &amp;ldquo;&lt;b&gt;compute-bound&lt;/b&gt;&amp;rdquo;가 아니라 &amp;ldquo;&lt;b&gt;memory-bound&lt;/b&gt;&amp;rdquo;라고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 온디바이스/엣지에서 더 명확해진다&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모바일과 엣지로 가면 제약이 세 가지로 좁혀진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;전력&lt;/b&gt;, &lt;b&gt;메모리 대역폭&lt;/b&gt;, &lt;b&gt;발열&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 센터에서는 보조적이던 제약이 여기서는 1차 제약으로 올라온다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 다이어그램은 방향성을 보여 주는 도식이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PIM은 아직 데이터센터&amp;middot;온디바이스 모두에서 초기 단계이며, 표준화된 프로그래밍 모델은 자리잡는 중이다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;  워크로드 진화               하드웨어 진화
───────────────       ──────────────────────
순차 알고리즘       ──&amp;rarr;         CPU
대규모 병렬 학습     ──&amp;rarr;         GPU
거대 모델 추론      ──&amp;rarr;          ?    &amp;larr; bandwidth wall
온디바이스 추론     ──&amp;rarr;     NPU + PIM (memory-centric)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NPU&lt;/b&gt;는&lt;b&gt; 연산 패턴을 좁혀 와트당 효율을 끌어올리는 쪽&lt;/b&gt;이고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PIM&lt;/b&gt;은 &lt;b&gt;이동 비용 자체를 깎는 쪽&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘은 서로를 대체하지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;같은 디바이스 안에서도 어떤 연산은 &lt;b&gt;NPU&lt;/b&gt;로,&lt;br /&gt;어떤 접근은 메모리 근처에서 처리하는 식으로 역할이 갈린다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 자연스럽게 따라오는 질문이 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GPU는 언제 안 쓰는가?&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 이동 전력이 워크로드 본체 전력을 넘기 시작할 때,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 모델이 한 번에 계산하는 방식이 아니라,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단어를 하나씩 생성(decode)하면서 이전에 저장해 둔 값을 계속 꺼내 쓰고(lookup), 그 과정이 반복되는 형태일 때다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 센터의 학습은 여전히 GPU의 무대지만, 엣지의 추론은 그렇지 않다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리 &amp;mdash; 내 시스템의 병목은 연산인가, 메모리인가&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPU와 PIM은 경쟁 관계가 아니다. 같은 문제의 다른 층을 푸는 도구다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;선택의 기준은 &quot;더 좋은 칩&quot;이 아니라 &quot;내 워크로드의 병목이 어디인가&quot;다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 보면 좋은 사이트&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://llm-study-web.vercel.app/&quot;&gt;llm-study-web&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: LLM 시스템의 전체 흐름을 개념학습, 시스템 맵, 인터랙티브 실험으로 배우는 교육 플랫폼&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica-website.vercel.app/&quot;&gt;peterica-website&lt;/a&gt;: 기록하는 백엔드개발자 피터 포트폴리오 웹사이트&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ㅇ&lt;/p&gt;</description>
      <category>AI/AI이론 | 공부</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1067</guid>
      <comments>https://peterica.tistory.com/1067#entry1067comment</comments>
      <pubDate>Sat, 2 May 2026 07:10:11 +0900</pubDate>
    </item>
    <item>
      <title>[독후감] 죽을 때 후회하는 스물다섯 가지</title>
      <link>https://peterica.tistory.com/1076</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/upoNw/dJMcabrcmlp/iqojZ1pYiupdrGIBuTTMv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/upoNw/dJMcabrcmlp/iqojZ1pYiupdrGIBuTTMv1/img.jpg&quot; data-alt=&quot;https://www.yes24.com/product/goods/124988906&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/upoNw/dJMcabrcmlp/iqojZ1pYiupdrGIBuTTMv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FupoNw%2FdJMcabrcmlp%2FiqojZ1pYiupdrGIBuTTMv1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;1200&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.yes24.com/product/goods/124988906&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 중학교 시절에 죽음에 대한 깊은 고민에 빠진 적이 있다. 이 물음에서 나는 인생의 중요한 선택인 수도자의 길을 선택하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;죽음은 끝이 아니라 과정일 뿐이며, 살아있는 동안 어떻게 살아가느냐가 행복의 중요한 덕목이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책을 읽으며 느낀 것은, 이 책이 죽음 자체를 이야기하는 것이 아니라 결국 &quot;어떻게 살아야 하는가&quot;를 이야기하고 있다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 이 책은 성공하는 법이나 행복해지는 방법을 설명하지 않는다. 대신 삶의 마지막 순간을 맞이한 사람들이 실제로 어떤 후회를 남겼는지를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 책을 읽을 때 단순히 내용을 따라가기보다 작가가 왜 이런 이야기를 하는지, 어떤 관점으로 세상을 바라보는지를 이해하려고 노력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작가에 대한 이해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오츠 슈이치는 일본의 종양 전문의로, 오랜 시간 말기 환자들을 진료하며 수많은 사람들의 마지막 순간을 지켜본 의사이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그는 환자들이 죽음을 앞두고 남긴 이야기들을 기록하면서 흥미로운 사실을 발견한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들은 죽음을 앞두고 자신이 이루지 못한 성공이나 더 벌지 못한 돈보다, 삶 속에서 놓쳐버린 관계와 선택들에 대해 더 많이 후회한다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 책은 죽음 자체를 설명하는 철학서라기보다, 삶을 다시 한번 돌아보게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나와 같은 생각으로 작가도 사람들에게 죽음이 끝이 아니기 때문에 죽음을 두려워하라고 말하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 죽음 앞에서 반복적으로 나타나는 후회를 통해, 지금 무엇을 중요하게 생각하며 살아야 하는지를 질문한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;어느 순간 나는 많은 사람의 마지막을 지켜보면서 누구나 느끼는 후회, 인생에서 풀지 못한 숙제에 공통분모가 있다는 사실을 깨달았다. 지금부터 그 공통분모를 여러 사람과 나누고자 한다.&quot; p13&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작가가 발견한 후회의 공통점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에 등장하는 스물다섯 가지 후회는 나다움, 관계, 현재를 살지 못하는 삶이라는 공통된 주제로 모인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 자신답게 살지 못한 삶이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남의 기대에 맞추어 살거나, 실패가 두려워 도전하지 못했던 일들이 후회로 남았다. 특히 하고 싶었던 일을 미루다가 결국 시도조차 하지 못한 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 관계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들은 사랑하는 사람에게 더 표현하지 못한 것을 후회했다. 가족과 더 많은 시간을 보내지 못한 것, 고마움을 전하지 못한 것, 화해하지 못한 것이 마지막 순간에 남는다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째는 현재를 살지 못한 삶이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들은 미래를 위해 현재를 희생하며 살아가지만, 정작 마지막 순간에는 그 미래를 충분히 누리지 못한 경우가 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 작가가 이야기하는 후회는 '잘못한 일'에 대한 반성보다 '하지 못한 일'에 대한 안타까움이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 내가 인상 깊게 느낀 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책을 읽으며 가장 인상 깊었던 점은, 후회가 특별한 사건에서 생기는 것이 아니라는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 25가지 내용은 대부분의 후회는 아주 평범한 일상 속에서 만들어지고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp; 가족에게 한 번 더 전화할 수 있었던 순간,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;고맙다고 말할 수 있었던 순간,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;도전해볼 수 있었던 순간,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 쉬어갈 수 있었던 순간들을 지나치며 살아간 결과가 마지막에 후회로 남고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 사람들은 돈을 더 벌지 못한 것보다, 시간을 어떻게 사용했는지에 대해 더 많이 이야기했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 모습을 보며 나 역시 지금 중요하다고 생각하는 일들이 정말 중요한 것인지 다시 생각하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이 책이 내게 던진 질문&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 정답을 알려주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 한 가지 질문을 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;만약 지금 삶이 끝난다면, 나는 무엇을 가장 후회할 것인가?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 그 질문에 쉽게 답할 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 분명한 것은, 후회 없는 삶이란 미래 어느 날 완성되는 것이 아니라 지금의 선택 속에서 만들어진다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;죽음은 언젠가 찾아오는 특별한 사건이 아니라, 오늘의 삶을 비추어 보는 거울에 가까워 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 나에게 중요한 선택이 필요한 시기이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이들과 가정을 위해 육아휴직을 사용하려는 시기에 지금의 선택이 미래를 위한 더 큰 의미를 만들어 준다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『죽을 때 후회하는 스물다섯 가지』는 죽음을 준비하는 책이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 삶의 우선순위를 다시 점검하게 만드는 책에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들은 마지막 순간에 더 많은 돈이나 더 높은 지위를 이야기하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 사랑, 관계, 용기, 그리고 미루어 두었던 선택들에 대해 이야기했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 내가 이해한 작가의 핵심 메시지는 단순했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후회 없는 죽음을 준비하는 가장 좋은 방법은, 후회 없는 오늘을 살아가는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에겐 가족과 아이들의 관계성을 다시금 생각할 수 있었던 시간이었다.&lt;/p&gt;</description>
      <category>책이야기</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1076</guid>
      <comments>https://peterica.tistory.com/1076#entry1076comment</comments>
      <pubDate>Sat, 2 May 2026 01:00:39 +0900</pubDate>
    </item>
    <item>
      <title>어디서든 브라우저 VS Code로 마크다운만 떨어뜨리면 LLM 이 개인 위키로 자동 정리 - silva-omnium</title>
      <link>https://peterica.tistory.com/1075</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ 언제 어디서든 브라우저 VS Code로 정보를 입력하면, 모든 것이 쌓이는 개인 지식 숲을 만들었습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트는 한 번만 작성합니다. 분류&amp;middot;요약&amp;middot;교차참조&amp;middot;인용 부착&amp;middot;모순 표시는 LLM 이 알아서 &amp;mdash; 영속 위키에 계속 누적되도록.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/80ADX/dJMcahqHwPD/LXo7L0GIZHswM4zKa7gfn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/80ADX/dJMcahqHwPD/LXo7L0GIZHswM4zKa7gfn1/img.png&quot; data-origin-width=&quot;3034&quot; data-origin-height=&quot;2020&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9058%; margin-right: 10px;&quot; data-widthpercent=&quot;50.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/80ADX/dJMcahqHwPD/LXo7L0GIZHswM4zKa7gfn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80ADX%2FdJMcahqHwPD%2FLXo7L0GIZHswM4zKa7gfn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3034&quot; height=&quot;2020&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7YHCo/dJMcaf0F4V9/SElDu13zJklUqFhICNw7z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7YHCo/dJMcaf0F4V9/SElDu13zJklUqFhICNw7z1/img.png&quot; data-origin-width=&quot;3016&quot; data-origin-height=&quot;2048&quot; data-is-animation=&quot;false&quot; width=&quot;604&quot; style=&quot;width: 48.9314%;&quot; data-widthpercent=&quot;49.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7YHCo/dJMcaf0F4V9/SElDu13zJklUqFhICNw7z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7YHCo%2FdJMcaf0F4V9%2FSElDu13zJklUqFhICNw7z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3016&quot; height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 문제: 매번 0 부터 발견&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 으로 문서를 다루는 가장 흔한 패턴 &amp;mdash; PDF 나 노트를 챗 인터페이스에 붙여 넣고 질문, 답을 받고, 닫는다. 다음에 같은 주제가 또 떠오르면 같은 문서를 다시 붙이고, 같은 추론을 다시 하게 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 자료에 10번 질문하면 LLM 은 그 자료를 10번 처음부터 발견한다. 토큰값과 시간이 든다. 더 큰 문제는 &lt;b&gt;이전에 얻은 통찰이 어디에도 누적되지 않는다&lt;/b&gt;는 것이다. 다음에 비슷한 주제를 마주쳐도 처음 만나는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 발상: 한 번만 발견하고 위키에 쌓자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스를 LLM 에 한 번만 읽게 한다. LLM 은:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 소스를 영속 위키 페이지로 종합한다&lt;/li&gt;
&lt;li&gt;기존 페이지와 연관 있으면 합치거나 모순을 표시한다&lt;/li&gt;
&lt;li&gt;모든 사실에 원본을 가리키는 각주를 단다&lt;/li&gt;
&lt;li&gt;카테고리&amp;middot;태그&amp;middot;별칭 같은 메타데이터를 채운다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 같은 주제가 또 들어오면 LLM 은 새 raw 만 보면 된다. 위키는 이미 그 주제의 누적 종합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10번째 질문 즈음이면 위키가 이미 답을 갖고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 이름은 왜 silva-omnium 인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라틴어로 &quot;사물의 숲&quot;. 두 가지 계보를 합쳤다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Andrej Karpathy 의 LLM Wiki 패턴&lt;/b&gt; &amp;mdash; 본인 노트를 LLM 으로 정리해 자기만의 위키를 만드는 &lt;a href=&quot;https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f&quot;&gt;디자인 원형&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;폴란드-리투아니아 연합의 silva rerum&lt;/b&gt; &amp;mdash; 17~18세기 가문 대대로 이어 쓴 잡록. 일기&amp;middot;계약서&amp;middot;시&amp;middot;여행기&amp;middot;조리법 다 들어가는 두꺼운 한 권. 후손이 이어 쓰면서 가문의 기억이 책 자체에 누적된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 &lt;a href=&quot;https://en.wikipedia.org/wiki/Memex&quot;&gt;Vannevar Bush 의 Memex (1945)&lt;/a&gt; &amp;mdash; &quot;기억의 확장 장치&quot; 개념 &amp;mdash; 도 함께 둔다. 50년 후 하이퍼링크로 절반 정도 구현된 그 아이디어를, LLM 이 나머지 절반을 채워줄 수 있다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 어떻게 동작하는가?&lt;/h4&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;raw/ ──(watcher / make ingest)──▶ wiki/ ──(make build)──▶ 정적 사이트 + Obsidian 그래프&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2284&quot; data-origin-height=&quot;1910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUtLEp/dJMcaiQCQ9w/JHMscVRWXububjxwdxaJ3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUtLEp/dJMcaiQCQ9w/JHMscVRWXububjxwdxaJ3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUtLEp/dJMcaiQCQ9w/JHMscVRWXububjxwdxaJ3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUtLEp%2FdJMcaiQCQ9w%2FJHMscVRWXububjxwdxaJ3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;373&quot; data-origin-width=&quot;2284&quot; data-origin-height=&quot;1910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;사람&lt;/b&gt;: &lt;code&gt;raw/&lt;/code&gt; 디렉토리에 노트를 떨어뜨린다. 형식 자유. 원본은 절대 수정 안 함 (불변).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LLM&lt;/b&gt; (Ollama 또는 Claude): 새 raw 가 생기면 읽고, 위키에 어떻게 통합할지 결정한다 &amp;mdash; 새 페이지 만들기 / 기존 페이지에 붙이기 / 모순 표시 &amp;mdash; 그리고 frontmatter (title, category, tags, src 인용) 까지 채운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9LpcQ/dJMcabquw1z/70UoNAwJ95j1boL6DGg8Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9LpcQ/dJMcabquw1z/70UoNAwJ95j1boL6DGg8Lk/img.png&quot; data-origin-width=&quot;3034&quot; data-origin-height=&quot;2020&quot; data-is-animation=&quot;false&quot; width=&quot;441&quot; style=&quot;width: 49.9058%; margin-right: 10px;&quot; data-widthpercent=&quot;50.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9LpcQ/dJMcabquw1z/70UoNAwJ95j1boL6DGg8Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9LpcQ%2FdJMcabquw1z%2F70UoNAwJ95j1boL6DGg8Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3034&quot; height=&quot;2020&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwZvf0/dJMcaduY4L6/g2x2xIIoxbvyCxx6vakIJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwZvf0/dJMcaduY4L6/g2x2xIIoxbvyCxx6vakIJK/img.png&quot; width=&quot;323&quot; height=&quot;219&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;2048&quot; data-origin-width=&quot;3016&quot; style=&quot;width: 48.9314%;&quot; data-widthpercent=&quot;49.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwZvf0/dJMcaduY4L6/g2x2xIIoxbvyCxx6vakIJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwZvf0%2FdJMcaduY4L6%2Fg2x2xIIoxbvyCxx6vakIJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3016&quot; height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위키&lt;/b&gt;: 시간에 따라 쌓인다. Astro Starlight 로 빌드하면 검색 가능한 사이트가 되고, 같은 디렉토리를 Obsidian vault 로 열면 그래프 뷰가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJAwa/dJMcadV48ak/QxNyM9vpHGFunPGsKNt5B1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJAwa/dJMcadV48ak/QxNyM9vpHGFunPGsKNt5B1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJAwa/dJMcadV48ak/QxNyM9vpHGFunPGsKNt5B1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJAwa%2FdJMcadV48ak%2FQxNyM9vpHGFunPGsKNt5B1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;178&quot; height=&quot;227&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;648&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;raw 와 wiki 의 분리&lt;/b&gt;다. raw 는 사용자의 원본 흐름 &amp;mdash; Obsidian 클리핑이든, 블로그 즐겨찾기든, 회의록이든. wiki 는 LLM 이 그 흐름을 종합한 영속층. 둘 다 마크다운 파일이라 어떤 도구로도 열 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 작은 설계 결정들&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 인용 강제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 이 만든 위키 페이지의 모든 사실은 &lt;code&gt;[^src-2026-04-27-tailscale-funnel]&lt;/code&gt; 같은 각주를 달고, 페이지 끝에 정의가 붙는다. &lt;b&gt;prompt 에 hard rule 로 박았다.&lt;/b&gt; 안 그러면 LLM 이 자기 사전 지식과 raw 의 사실을 섞어 출력한다 &amp;mdash; 위키가 LLM 의 환각을 누적하는 도구가 되어버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 모순 callout&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;raw 의 사실이 기존 위키 페이지와 충돌하면 &quot;병합&quot; 하지 않는다. 기존 페이지에 &lt;code&gt;&amp;gt; [!conflict] ...&lt;/code&gt; 형태의 callout 으로 양쪽을 다 적는다. 사용자가 직접 판단할 수 있게 &amp;mdash; 어느 쪽이 더 최신인지, 더 신뢰할 출처인지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. provider 플러그형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;scripts/llm_providers/&lt;/code&gt; 에 ollama, claude (Anthropic SDK), claude-cli (&lt;code&gt;claude -p&lt;/code&gt; subprocess) 3개.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 인터페이스, 다른 백엔드. ingest 명령 하나로 전환:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;make ingest                  # default = claude-cli
make ingest-ollama           # 로컬 Ollama (오프라인)
make ingest-claude           # Anthropic API key&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 ollama qwen2.5:3b 로 시작했는데, 8GB RAM 환경에서 큰 raw 가 timeout 났다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;1046&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KLWzd/dJMcadhv3AV/LWKwWE8c5Pi0lT96pxwPck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KLWzd/dJMcadhv3AV/LWKwWE8c5Pi0lT96pxwPck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KLWzd/dJMcadhv3AV/LWKwWE8c5Pi0lT96pxwPck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKLWzd%2FdJMcadhv3AV%2FLWKwWE8c5Pi0lT96pxwPck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;393&quot; height=&quot;335&quot; data-origin-width=&quot;1226&quot; data-origin-height=&quot;1046&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 모델은 instruction following 이 약해 카테고리&amp;middot;슬러그도 흔들렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLH3S/dJMcaaE7Ize/3mTTzmtWEFQVDl8O3KlvD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLH3S/dJMcaaE7Ize/3mTTzmtWEFQVDl8O3KlvD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLH3S/dJMcaaE7Ize/3mTTzmtWEFQVDl8O3KlvD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLH3S%2FdJMcaaE7Ize%2F3mTTzmtWEFQVDl8O3KlvD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;448&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;claude-cli 로 default 를 바꾸니 같은 prompt 인데 분류 정확도가 눈에 띄게 좋아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 비용은 구독 안에 들어가서 추가 청구 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;자체호스팅 (선택)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상시 켜져 있는 머신 (예: Mac mini) 에 띄우면 어디서든 브라우저로 편집&amp;middot;열람&amp;middot;AI 협업이 가능하다. 구성:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;code-server&lt;/b&gt; (VS Code in browser) &amp;mdash; 트리&amp;middot;에디터&amp;middot;미리보기&amp;middot;통합 터미널&amp;middot;&lt;code&gt;claude&lt;/code&gt; 명령 사전 설치&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Caddy&lt;/b&gt; &amp;mdash; 정적 위키 서빙 + &lt;code&gt;/edit/*&lt;/code&gt; 를 code-server 로 reverse_proxy&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tailscale Funnel&lt;/b&gt; &amp;mdash; 공개 URL (&lt;code&gt;&amp;lt;host&amp;gt;.&amp;lt;tailnet&amp;gt;.ts.net&lt;/code&gt;) 자동 발급. 도메인&amp;middot;포트포워딩 불필요. 무료&lt;/li&gt;
&lt;li&gt;&lt;b&gt;fswatch + launchd&lt;/b&gt; &amp;mdash; &lt;code&gt;raw/&lt;/code&gt; 변경 감지 &amp;rarr; &lt;code&gt;make ingest &amp;amp;&amp;amp; make build&lt;/code&gt; 자동 트리거&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 ollama 만 native, 나머지는 Docker로 구성하였다. 다른 프로젝트와 brew 의존이 섞이지 않도록 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저로 같은 URL 을 열면 mini 의 code-server 가 그대로 뜬다. AI 호출과 git push 모두 mini 의 네트워크에서 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한계 &amp;mdash; 솔직하게&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠 운영하며 드러난 것들:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Watcher race condition&lt;/b&gt; &amp;mdash; raw 여러 파일이 한 번에 도착하면 (예: git pull) 디바운스가 진행 중인 ingest 를 kill 해 &lt;br /&gt;부분 누락 가능. lockfile 또는 더 큰 latency 필요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Funnel + 단일 비밀번호&lt;/b&gt; &amp;mdash; code-server 가 password 만으로 보호. 추측 어려운 24자 비밀번호긴 하지만, &lt;br /&gt;다음 단계로 Caddy basic_auth 추가 또는 Cloudflare Access 전환 후보.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ollama 11434 외부 노출&lt;/b&gt; &amp;mdash; &lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt; 로 두면 같은 LAN 의 다른 디바이스가 인증 없이 접근 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;categories.yaml 일관성&lt;/b&gt; &amp;mdash; 작은 모델로 ingest 시 새 카테고리 만들고 yaml 등록 안 함. claude-cli 전환으로 거의 해결됐지만 완벽하진 않음.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 결함들도 위키에 페이지로 들어간다 &amp;mdash; 자기 자신을 자료 삼아 누적되는 게 silva rerum 의 정신.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ&amp;nbsp;코드 + 셋업&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub: &lt;a href=&quot;https://github.com/peterica/silva-omnium&quot;&gt;https://github.com/peterica/silva-omnium&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quick start (1분):&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;git clone https://github.com/peterica/silva-omnium.git
cd silva-omnium
make setup

mkdir -p raw/2026
echo &quot;# 첫 노트&quot; &amp;gt; raw/2026/test.md

make ingest &amp;amp;&amp;amp; make build &amp;amp;&amp;amp; make dev
# &amp;rarr; http://localhost:4321&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체호스팅까지 셋업하려면 &lt;code&gt;infra/README.md&lt;/code&gt; 의 1회 부트스트랩 스크립트 실행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기억을 쌓는 도구가 결국 자기 자신을 자료로 쌓는다는 게 흥미롭다. 다음 글에선 ingest 파이프라인의 내부 구조 (provider 추상화, 인용 강제 prompt, frontmatter 스키마) 를 더 자세히 다루겠습니다.&lt;/p&gt;</description>
      <category>AI/RAG</category>
      <category>LLM-WIKI</category>
      <category>PETERICA</category>
      <category>나만의 WIKI</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1075</guid>
      <comments>https://peterica.tistory.com/1075#entry1075comment</comments>
      <pubDate>Tue, 28 Apr 2026 01:42:56 +0900</pubDate>
    </item>
    <item>
      <title>[온디바이스AI] 청크는 쌓는 게 아니라 덜어내는 일 &amp;mdash; 약한 청크 25개를 덜어내 MRR이 0.15 올랐다</title>
      <link>https://peterica.tistory.com/1074</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ&lt;span&gt;&amp;nbsp;&lt;/span&gt;TL;DR&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;약한 청크 =&lt;span&gt;&amp;nbsp;&lt;/span&gt;moc/&amp;middot;entities/&amp;middot;concepts/의 &quot;관련 포스트&quot;, &quot;주요 태그&quot;, 연도 그룹, 순수 링크 목록&lt;/li&gt;
&lt;li&gt;본문 없고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;키워드만 풍부&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; 임베딩 유사도가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가짜로 높게&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나온다&lt;/li&gt;
&lt;li&gt;해법: 경로&amp;middot;헤딩 한 줄 필터 (새 모델&amp;middot;인덱스&amp;middot;리랭커 0개)&lt;/li&gt;
&lt;li&gt;실측 기준:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;청크 수:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;118 &amp;rarr; 93&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(-25, -21%)&lt;/li&gt;
&lt;li&gt;MRR(Mean Reciprocal Rank):&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;0.794 &amp;rarr; 0.947&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(+0.15)&lt;/li&gt;
&lt;li&gt;R@3(Recall@3):&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;0.933 &amp;rarr; 1.000&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결론:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모델을 바꾸기 전에 청크를 봐야 한다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 이 문제를 발견했나&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;폰에서 &quot;쿠버네티스에서 graceful shutdown이란?&quot;을 물었다.&lt;br /&gt;top-3에 정답 블로그 183번 글과 함께,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;MOC 파일의 &quot;관련 포스트&quot; 링크 목록&lt;/b&gt;이 두 개 섞여 들어왔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 목록 청크의 본문을 열어봤다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 내용 거의 없음&lt;/li&gt;
&lt;li&gt;&quot;kubernetes, graceful shutdown, pod, deployment...&quot; 키워드만 나열&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;링크 목록이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;본문보다 먼저 검색된다&lt;/b&gt;는 건 설계 실패에 가깝다.&lt;br /&gt;모델을 바꿀까 생각하다가 멈췄다 &amp;mdash; 이건 모델 문제가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;청크 문제&lt;/b&gt;였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 약한 청크의 정체&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MOC(Map of Content)&amp;middot;entity&amp;middot;concepts 경로의 파일은 원래&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지식의 허브 역할&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 그 안의 일부 섹션은 검색 관점에서 본문이라고 부를 수 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;관련 포스트&quot; 링크 목록&lt;/li&gt;
&lt;li&gt;&quot;주요 태그&quot; 나열&lt;/li&gt;
&lt;li&gt;연도 그룹 헤딩 (&quot;2024년&quot;, &quot;2023년 작성 글&quot;)&lt;/li&gt;
&lt;li&gt;반복되는 테이블&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정답 본문은 없고 관련 키워드만 모인 청크라서, 임베딩 벡터가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;주변 문서들의 키워드 평균&lt;/b&gt;처럼 형성된다.&lt;br /&gt;본문이 없으니 구체성은 없는데, 키워드 밀도가 높아&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;유사도가 가짜로 높게&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나온다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  결과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 쿼리가 어느 주제든 이 청크들이 top-k에 쉽게 얹힌다&lt;/li&gt;
&lt;li&gt;정답 본문이 top-3 밖으로 밀려나는 경우가 생긴다&lt;/li&gt;
&lt;li&gt;LLM은 근거로 받은 &quot;키워드 목록&quot;으로 답을 짜야 해서 대답이 빈약해진다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 해법은 한 번의 경로&amp;middot;헤딩 필터&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;chunk.py에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;경로+헤딩 매칭 필터&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;한 규칙을 넣었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로:&lt;span&gt;&amp;nbsp;&lt;/span&gt;moc/,&lt;span&gt;&amp;nbsp;&lt;/span&gt;entities/,&lt;span&gt;&amp;nbsp;&lt;/span&gt;concepts/&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나에 속하면&lt;/li&gt;
&lt;li&gt;헤딩 또는 섹션 내용이 &quot;관련 포스트&quot;, &quot;주요 태그&quot;, 연도 그룹, 순수 링크 목록에 해당하면&lt;/li&gt;
&lt;li&gt;청크 생성에서 제외&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 외에는 건드리지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 그대로&lt;/li&gt;
&lt;li&gt;차원 그대로&lt;/li&gt;
&lt;li&gt;검색 알고리즘 그대로&lt;/li&gt;
&lt;li&gt;인덱스 그대로&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;배포 구성이나 검색 스택을 바꾸지 않는, 데이터 단계의 단일 개입이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 실측 &amp;mdash; 22 쿼리 평가 하네스&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;변경 전후를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;같은 쿼리 세트&lt;/b&gt;로 비교해야 의미가 있다.&lt;br /&gt;한국어 22 쿼리(DevOps&amp;middot;RAG&amp;middot;K8s&amp;middot;모니터링)에 정답 청크를 라벨링해 둔 평가 하네스(server/scripts/embed_eval.py)로 돌렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지표필터 전필터 후변화&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;청크 수&lt;/td&gt;
&lt;td&gt;118&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;td&gt;-25 (-21%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MRR&lt;/td&gt;
&lt;td&gt;0.794&lt;/td&gt;
&lt;td&gt;&lt;b&gt;0.947&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;+0.153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R@3&lt;/td&gt;
&lt;td&gt;0.933&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1.000&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;+0.067&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모델&amp;middot;차원&amp;middot;검색 알고리즘은 전혀 건드리지 않은 결과다.&lt;br /&gt;이번 실험에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;모델을 바꾸지 않고도, 데이터 정리만으로 MRR이 0.153 올랐다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/peterica/peterica-edge-rag/blob/main/ondevice-rag/02-cases/chunking/moc-entity-filter.md&quot;&gt;청크 필터 case 문서&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 운영 &amp;mdash; ETag로 자동 재동기화&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;필터 규칙이 바뀌면 폰의 로컬 DB도 최신으로 맞춰야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ETag에&lt;span&gt;&amp;nbsp;&lt;/span&gt;chunker_version을 포함시켜, 필터 규칙이 바뀌면 폰 DB가 자동 재동기화되게 했다.&lt;br /&gt;필터 변경 없는 날은 304 &amp;mdash; 전송량 0.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  청크 필터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;단순 규칙 + 단순 동기화&lt;/b&gt;로 운영 복잡도도 거의 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 이 결정이 &quot;최소 구현&quot;에 맞는가&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;추가 인프라 0&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 새 모델&amp;middot;인덱스&amp;middot;리랭커 없음. 데이터 정리 한 번.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;평가 하네스가 증거&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 22 쿼리 전후 비교가 의사결정을 대신한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단순한 규칙&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 경로&amp;middot;헤딩 매칭만으로 동작. 새 위키 구조에 이식하기 쉽다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;확장 여지 유지&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 청크가 수만 개가 되면 정답 라벨 쿼리 평가를 다시 돌려 필터를 진화시키면 된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;덜&quot;이 정당성&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 최소 구현이 &quot;덜 넣는&quot; 쪽으로 편향되는 근거가 됐다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 내가 바꾼 생각&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 이렇게 접근했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서를 많이 쌓자&lt;/li&gt;
&lt;li&gt;임베딩만 좋으면 품질은 따라온다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금은 이렇게 바뀌었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문서는 선별해서 쌓자&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;품질은 모델보다 데이터가 결정한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;  결론:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RAG 품질을 올리는 가장 빠른 길은 모델 교체가 아니라, 잘못 넣은 청크를 덜어내는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 참고&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;이 프로젝트 문서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/peterica/peterica-edge-rag/blob/main/ondevice-rag/02-cases/chunking/moc-entity-filter.md&quot;&gt;청크 필터 case 문서&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 초기 가정&amp;middot;실험 설계&amp;middot;재검토 트리거&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/peterica/peterica-edge-rag/blob/main/ondevice-rag/02-cases/embedding/embedding-benchmark-ko.md&quot;&gt;임베딩 벤치마크 case&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 모델 선택 근거&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/peterica/peterica-edge-rag/blob/main/ondevice-rag/00-main/ondevice-rag-minimal-guide.md&quot;&gt;최소 구현 가이드&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 8단계 파이프라인&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/peterica/peterica-edge-rag/tree/main/ondevice-rag&quot;&gt;문서 허브&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 독자 유형별 읽기 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;관련 블로그&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/1071&quot;&gt;1071 &amp;mdash; 내 폰이 내 블로그에 답하게 만들었다: 다섯 가지 기술&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 본편 (섹션 2 &quot;청크 품질&quot;이 이 글의 배경)&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/1072&quot;&gt;1072 &amp;mdash; 처음 만드는 온디바이스 RAG: 핵심원칙 10가지&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 원칙 3 &quot;데이터가 성능&quot;&amp;middot;원칙 4 &quot;청크는 제거&quot;의 심도편&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://peterica.tistory.com/1073&quot;&gt;1073 &amp;mdash; LiteRT-LM: 온디바이스 LLM을 &quot;돌리게&quot; 만드는 런타임&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 같은 시리즈의 런타임 편&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;더 공부하기 &amp;mdash; LLM System Lab&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://llm-study-web.vercel.app/topic/rag-pipeline&quot;&gt;RAG Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://llm-study-web.vercel.app/topic/embedding&quot;&gt;Embedding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://llm-study-web.vercel.app/labs/chunking&quot;&gt;Chunking (실험)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;ㅁ 한 줄 정리&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;청크는 쌓는 작업이 아니라 덜어내는 작업이다. 잘 섞지 않는 것도 엔지니어링이다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>AI/RAG</category>
      <category>PETERICA</category>
      <category>Rag</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1074</guid>
      <comments>https://peterica.tistory.com/1074#entry1074comment</comments>
      <pubDate>Thu, 23 Apr 2026 23:00:10 +0900</pubDate>
    </item>
    <item>
      <title>[AI] RTK &amp;mdash; AI 에이전트 터미널 출력을 60~90% 압축하는 CLI 프록시</title>
      <link>https://peterica.tistory.com/1066</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TL;DR&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTK는&lt;span&gt;&amp;nbsp;&lt;/span&gt;github.com/rtk-ai/rtk&lt;span&gt;&amp;nbsp;&lt;/span&gt;의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;실존 OSS&lt;/b&gt;다. 아키텍처 개념이 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;단일 Rust 바이너리&lt;/b&gt;로 배포되는 CLI 프록시.&lt;/li&gt;
&lt;li&gt;AI 에이전트(Claude Code, Cursor, Gemini CLI 등)가 실행한 터미널 명령 출력을 가로채,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;60~90% 압축&lt;/b&gt;한 뒤 LLM에 전달한다.&lt;/li&gt;
&lt;li&gt;동작 방식은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;shell hook 이&lt;span&gt;&amp;nbsp;&lt;/span&gt;git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;를&lt;span&gt;&amp;nbsp;&lt;/span&gt;rtk git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 자동 치환&lt;/b&gt;. 에이전트 코드 수정 없이 붙는다.&lt;/li&gt;
&lt;li&gt;오버헤드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;10ms, 공식 지원 서브커맨드 30+ (git, test, jest, vitest, tsc, next, docker, kubectl 등). Apache-2.0 / MIT.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 이게 필요한가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 매 작업마다 아래를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;npm install
pytest
git status
docker build .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력은 대부분 &lt;b&gt;진행바, 다운로드 로그, 성공 라인&lt;/b&gt; 이다. LLM은 이 노이즈를 전부 context window 에 싣고 추론한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨텍스트 창이 빠르게 소진된다&lt;/li&gt;
&lt;li&gt;토큰 과금이 불필요하게 증가한다&lt;/li&gt;
&lt;li&gt;attention 이 노이즈에 분산돼 판단이 무뎌진다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ RTK 가 실제로 하는 일&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서 기준 &lt;b&gt;12가지 최적화 전략&lt;/b&gt;을 사용한다. 핵심만 추리면:&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;전략&lt;/td&gt;
&lt;td style=&quot;background-color: #008300; color: #ffffff;&quot;&gt;동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Stats Extraction&lt;/td&gt;
&lt;td&gt;git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;의 수십 줄 &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;3 modified, 1 added, 1 untracked&lt;span&gt;&amp;nbsp;&lt;/span&gt;한 줄로 요약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Failure Focus&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;테스트 러너의 통과 로그는 버리고, 실패 블록만 남김&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Grouping&lt;/td&gt;
&lt;td&gt;TypeScript 에러를 파일별로 묶어&lt;span&gt;&amp;nbsp;&lt;/span&gt;TS2322 (5x)&lt;span&gt;&amp;nbsp;&lt;/span&gt;식으로 집계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Deduplication&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;동일한 연속 로그는 카운터로 접음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Progress Filtering&lt;/td&gt;
&lt;td&gt;ANSI escape, 다운로드 진행바 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Three-Tier JSON Parsing&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;full &amp;rarr; partial &amp;rarr; passthrough 순서로 JSON 보존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;Tree Compression&lt;/td&gt;
&lt;td&gt;디렉토리 구조는 20 dir / 10 file 단위로 요약&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;나머지 5종: NDJSON Streaming, Block-Based State Machine, Language-Aware Code Stripping, JSON Object Extraction, Economic Metrics Tracking.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;단순 텍스트 trim 이 아니라 의미 단위 재구성&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 설치와 동작 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS 기준 Homebrew 가 가장 깔끔하다 (공식 formula, Apache-2.0).&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 설치
brew install rtk
# 또는 공식 install.sh (linux/macOS 공용)
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh

# 버전 확인
rtk --version   # &amp;rarr; rtk 0.37.2 (2026-04 기준)

# 수동 호출
rtk git status
rtk ls -la
rtk tree -L 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 끝이면 에이전트가 매번 &lt;code&gt;rtk&lt;/code&gt; 를 prefix 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;shell auto-rewrite hook&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;1116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNSlA3/dJMcah5mmVj/Q520YeEQfUnLEXT7RifE0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNSlA3/dJMcah5mmVj/Q520YeEQfUnLEXT7RifE0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNSlA3/dJMcah5mmVj/Q520YeEQfUnLEXT7RifE0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNSlA3%2FdJMcah5mmVj%2FQ520YeEQfUnLEXT7RifE0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2010&quot; height=&quot;1116&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;1116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;rtk init -g   # 전역 hook 설치&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hook 을 활성화하면, 에이전트가 &lt;code&gt;git status&lt;/code&gt; 를 보내도 shell 이 내부적으로 &lt;code&gt;rtk git status&lt;/code&gt; 로 치환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;에이전트 코드 수정 0줄, 설정 변경 0줄&lt;/b&gt;로 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원 확인된 에이전트:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude Code, Cursor, GitHub Copilot (VS Code / CLI)&lt;/li&gt;
&lt;li&gt;Gemini CLI, Codex, Windsurf, Cline / Roo Code&lt;/li&gt;
&lt;li&gt;OpenCode, OpenClaw, Kilo Code, Google Antigravity&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ Before / After 예시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는&amp;nbsp;이&amp;nbsp;블로그를&amp;nbsp;쓰는&amp;nbsp;리포지토리&amp;nbsp;(peterica-blog)&amp;nbsp;에서&amp;nbsp;rtk&amp;nbsp;0.37.2&amp;nbsp;로&amp;nbsp;직접&amp;nbsp;측정한&amp;nbsp;결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Before &amp;mdash; 원본&lt;span&gt;&amp;nbsp;&lt;/span&gt;git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;(705 bytes)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;On branch main
No commits yet
Changes to be committed:
  (use &quot;git rm --cached &amp;lt;file&amp;gt;...&quot; to unstage)
    new file:   CLAUDE_CODE_PROJECT_SETUP_PROMPT.md
    new file:   README.md
    new file:   writing/0415/idea.md
Changes not staged for commit:
  (use &quot;git add &amp;lt;file&amp;gt;...&quot; to update what will be committed)
  (use &quot;git restore &amp;lt;file&amp;gt;...&quot; to discard changes in working directory)
    modified:   CLAUDE_CODE_PROJECT_SETUP_PROMPT.md
    ...
Untracked files:
  (use &quot;git add &amp;lt;file&amp;gt;...&quot; to include in what will be committed)
    .gitignore
    PRD.md
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;After &amp;mdash;&lt;span&gt;&amp;nbsp;&lt;/span&gt;rtk git status&lt;span&gt;&amp;nbsp;&lt;/span&gt;(475 bytes, 44.1% 절감)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;* No commits yet on main
+ Staged: 3 files
   CLAUDE_CODE_PROJECT_SETUP_PROMPT.md
   README.md
   writing/0415/idea.md
~ Modified: 3 files
   CLAUDE_CODE_PROJECT_SETUP_PROMPT.md
   ...
? Untracked: 10 files
   .gitignore
   PRD.md
   ...&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사라진 것&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;(use &quot;git rm --cached ...&quot; to unstage)&lt;span&gt;&amp;nbsp;&lt;/span&gt;류의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;도움말 라인 전부&lt;/b&gt;. 사람에겐 유용하지만 LLM 에겐 노이즈.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;rtk find&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 99.8% 절감의 정체&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;원본&lt;span&gt;&amp;nbsp;&lt;/span&gt;find . -type f&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 2,309 라인을 모두 출력한다 (374KB).&lt;span&gt;&amp;nbsp;&lt;/span&gt;rtk find&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 이걸 이렇게 바꾼다:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;2309F 26D:

./ PRD.md README.md requirements.txt
data/ posts.jsonl posts_wiki.jsonl reclassify_llm.json urls.txt wiki_id_to_stem.json
data/shards/ 0001.jsonl 0001.raw.jsonl 0001.txt 0002.jsonl ... 0014.txt
+2259 more

ext: .md(2203) .jsonl(42) .py(26) .txt(22) .sh(5)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 변환&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 카운트 (2309F 26D) 를 맨 위에 배치&lt;/li&gt;
&lt;li&gt;디렉토리별로 파일을 한 줄에 묶음&lt;/li&gt;
&lt;li&gt;일정 수 이후는&lt;span&gt;&amp;nbsp;&lt;/span&gt;+2259 more&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 생략&lt;/li&gt;
&lt;li&gt;맨 아래&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;확장자별 통계&lt;/b&gt;를 집계 &amp;mdash; &quot;이 프로젝트는 md 2203 개가 주류&quot; 를 LLM 이 즉시 파악&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순 truncation 이 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&quot;이 디렉토리가 어떤 곳인지&quot;를 압축해서 보여주는&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;설계다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 Rust 인가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔지니어링 관점에서 Rust 선택은 합리적이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 바이너리&lt;/b&gt;: 런타임&amp;middot;인터프리터 없음. 어떤 환경에도 copy &amp;amp; run.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;stdin/stdout 스트림 처리&lt;/b&gt;: 블로킹 없는 파이프 중계.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;10ms 오버헤드&lt;/b&gt;: 매 명령마다 프록시를 통과해도 체감되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, Rust 는 수단이다. Go 나 Zig 로도 동일 요구사항을 만족할 수 있다. 본질은 &lt;b&gt;터미널 출력 스트림을 낮은 오버헤드로 중간 가공하는 파이프 레이어&lt;/b&gt; 라는 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한계와 주의점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTK 를 무작정 켜기 전에 알아야 할 것.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;exit code 는 보존되지만, 일부 CI 파서는 깨질 수 있다&lt;/b&gt;. 에이전트 용 프록시지 CI 용이 아니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;100+ 명령 외&lt;/b&gt;는 passthrough 되거나 압축률이 낮다. 지원 목록 확인 필요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그 기반 디버깅이 필요한 순간&lt;/b&gt;엔 명령 앞의 `rtk` 를 떼고 원본 출력을 그대로 받아야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;shell hook 방식&lt;/b&gt;은 사용자의 shell rc 파일을 수정한다. 팀 환경 배포 시 주의.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 유사 제품 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTK 만 있는 건 아니다. 같은 문제를 다른 각도로 푸는 도구들이 있다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style14&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;도구&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;위치&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;RTK&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;터미널 출력 단&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;shell hook, Rust 바이너리, 60~90%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;Tamp&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;에이전트 &amp;harr; API 중간&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;tool result 분류 후 압축, 60~70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;LeanCTX&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;파일 read 단&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;intent-aware, 재읽기 캐시 시 99%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RTK 는 &lt;b&gt;쉘 레이어&lt;/b&gt;, Tamp 는 &lt;b&gt;API 레이어&lt;/b&gt;, LeanCTX 는 &lt;b&gt;파일 read 레이어&lt;/b&gt; 로 삽입 지점이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조합해서 써도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 마무리&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RTK 는 &quot;AI 에이전트용 로그 전처리 프록시&quot;다. 추상 개념이 아니라 실제로 설치&amp;middot;실행 가능한 OSS.&lt;/li&gt;
&lt;li&gt;적용 지점은 &lt;b&gt;shell hook&lt;/b&gt;, 효과는 &lt;b&gt;60~90% 토큰 절감&lt;/b&gt;, 오버헤드는 &lt;b&gt;&amp;lt;10ms&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;단순 길이 축소가 아니라 &lt;b&gt;12가지 의미 단위 재구성 전략&lt;/b&gt;을 적용한다.&lt;/li&gt;
&lt;li&gt;장기적으로는 RAG 전처리, 온디바이스 LLM 입력 최적화에도 같은 구조가 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 느리거나 컨텍스트가 빨리 차는 팀이라면, 붙여보고 &lt;code&gt;rtk gain&lt;/code&gt; 으로 실측하는 것이 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 참고&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;rtk-ai/rtk &amp;mdash; &lt;a href=&quot;https://github.com/rtk-ai/rtk&quot;&gt;https://github.com/rtk-ai/rtk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;DeepWiki: Token Optimization Strategies &amp;mdash; &lt;a href=&quot;https://deepwiki.com/rtk-ai/rtk/3.2-token-optimization-strategies&quot;&gt;https://deepwiki.com/rtk-ai/rtk/3.2-token-optimization-strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tamp (비교군) &amp;mdash; &lt;a href=&quot;https://github.com/sliday/tamp&quot;&gt;https://github.com/sliday/tamp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 보면 좋은 사이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ 모델&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.googleblog.com/bring-state-of-the-art-agentic-skills-to-the-edge-with-gemma-4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gemma 4 Edge 블로그&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://huggingface.co/google/embeddinggemma-300m&quot;&gt;google/embeddinggemma-300m&amp;nbsp;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ 도구&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google AI Edge Gallery: &lt;a href=&quot;https://github.com/google-ai-edge/gallery&quot;&gt;https://github.com/google-ai-edge/gallery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;minja (llama.cpp 내장 Jinja2): &lt;a href=&quot;https://github.com/google/minja&quot;&gt;https://github.com/google/minja&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;LiteRT: &lt;a href=&quot;https://ai.google.dev/edge/litert&quot;&gt;https://ai.google.dev/edge/litert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;sqlite-vec: &lt;a href=&quot;https://github.com/asg017/sqlite-vec&quot;&gt;https://github.com/asg017/sqlite-vec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ 더 공부하기 &amp;mdash; &lt;a href=&quot;https://llm-study-web.vercel.app/&quot;&gt;LLM System Lab&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM 시스템의 전체 흐름을 개념학습, 시스템 맵, 인터랙티브 실험으로 배우는 교육 플랫폼&lt;/li&gt;
&lt;li&gt;RAG Pipeline: &lt;a href=&quot;https://llm-study-web.vercel.app/topic/rag-pipeline&quot;&gt;https://llm-study-web.vercel.app/topic/rag-pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Embedding: &lt;a href=&quot;https://llm-study-web.vercel.app/topic/embedding&quot;&gt;https://llm-study-web.vercel.app/topic/embedding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Production RAG 사례: &lt;a href=&quot;https://llm-study-web.vercel.app/case-studies/production-rag-system&quot;&gt;https://llm-study-web.vercel.app/case-studies/production-rag-system&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ 이전 글&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://peterica-website.vercel.app/&quot;&gt;peterica-website&lt;/a&gt;: 기록하는 백엔드개발자 피터 포트폴리오 웹사이트&lt;/li&gt;
&lt;li&gt;Mac Mini RAG 구축기: &lt;a href=&quot;https://peterica.tistory.com/1064&quot;&gt;https://peterica.tistory.com/1064&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;sqlite-vec 선택 이유: &lt;a href=&quot;https://peterica.tistory.com/1065&quot;&gt;https://peterica.tistory.com/1065&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI/AI코딩 | 실습</category>
      <category>google ai edge</category>
      <category>litert</category>
      <category>minja</category>
      <category>PETERICA</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1066</guid>
      <comments>https://peterica.tistory.com/1066#entry1066comment</comments>
      <pubDate>Thu, 23 Apr 2026 20:44:55 +0900</pubDate>
    </item>
    <item>
      <title>[온디바이스AI] LiteRT-LM &amp;mdash; 온디바이스 LLM을 실제로 &amp;ldquo;돌리게&amp;rdquo; 만드는 런타임</title>
      <link>https://peterica.tistory.com/1073</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HeULY/btsNebTHMD3/bX97l0SKsxrXT44KfuWkC1/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHeULY%2FbtsNebTHMD3%2FbX97l0SKsxrXT44KfuWkC1%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;233&quot; data-filename=&quot;블로그 로고.png&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://peterica.tistory.com/761&quot;&gt;[AI] Peterica의 AI공부와 비젼 정리&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 들어가며&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://github.com/google-ai-edge/litert-lm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LiteRT-LM&lt;/a&gt; &amp;mdash; 온디바이스 LLM을 실제로 &amp;ldquo;돌리게&amp;rdquo; 만드는 런타임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ TL;DR&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LiteRT-LM = 온디바이스 LLM &lt;b&gt;추론 런타임&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;핵심 차별점: &lt;b&gt;&lt;code&gt;.litertlm&lt;/code&gt; (모델 + 토크나이저 + 템플릿 번들)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;서버 런타임(vLLM/Ollama)은 모바일에 그대로 못 옮긴다&lt;/li&gt;
&lt;li&gt;실제 기준:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Galaxy S23 Ultra&lt;/li&gt;
&lt;li&gt;Gemma 4 E2B&lt;/li&gt;
&lt;li&gt;약 40초/응답&lt;/li&gt;
&lt;li&gt;모델 크기 약 2.4GB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결론: &lt;b&gt;모델만큼 런타임 선택이 초기에 결정된다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 왜 LiteRT-LM까지 오게 됐는가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemma 4 E2B를 폰에서 돌리는 데 48시간을 썼다.&lt;br /&gt;시간의 대부분은 모델이 아니라 &lt;b&gt;런타임에서 막혔다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;llama.cpp &amp;rarr; Android NDK / NPU 활용 제한&lt;/li&gt;
&lt;li&gt;서버 구조(vLLM/Ollama) &amp;rarr; 모바일 이식 불가&lt;/li&gt;
&lt;li&gt;tokenizer / ABI / 메모리 문제 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 문제는 하나였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;&amp;ldquo;모델이 아니라 실행 계층이 없다.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ LiteRT-LM의 핵심 역할 (고유성)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;:contentReference[oaicite:0]{index=0}은 단순한 추론 엔진이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 이 포맷이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;.&lt;b&gt;litertlm&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 파일에 포함:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 weight&lt;/li&gt;
&lt;li&gt;토크나이저&lt;/li&gt;
&lt;li&gt;chat template&lt;/li&gt;
&lt;li&gt;실행 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  결과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;별도 tokenizer 배포 불필요&lt;/li&gt;
&lt;li&gt;프롬프트 포맷 일관성 유지&lt;/li&gt;
&lt;li&gt;모바일 배포 단순화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 다른 런타임과의 결정적 차이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 서버 런타임이 그대로 안 되는 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 기준 사고:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;GPU&lt;/li&gt;
&lt;li&gt;Python runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모바일 현실:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;arm64 + NDK&lt;/li&gt;
&lt;li&gt;제한된 메모리&lt;/li&gt;
&lt;li&gt;NPU delegate (Hexagon 등)&lt;/li&gt;
&lt;li&gt;앱 내 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cross compile 필요&lt;/li&gt;
&lt;li&gt;tokenizer 네이티브 의존성&lt;/li&gt;
&lt;li&gt;GPU 대신 NPU 경로 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  그래서 구조가 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;&amp;ldquo;모델 서버&amp;rdquo; &amp;rarr; &amp;ldquo;앱 내 추론 시스템&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 실제 실행 흐름 (LiteRT-LM 기준)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 LLM 흐름과 다르게, LiteRT-LM은 이 단계가 핵심이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;HuggingFace 모델 준비&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.litertlm&lt;/code&gt; 포맷으로 변환 (핵심 단계)&lt;/li&gt;
&lt;li&gt;앱 assets 또는 외부 저장소 배치&lt;/li&gt;
&lt;li&gt;LiteRT-LM 런타임 로딩&lt;/li&gt;
&lt;li&gt;delegate 선택 (CPU / GPU / NPU)&lt;/li&gt;
&lt;li&gt;프롬프트 입력 &amp;rarr; 토큰 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  차이:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tokenizer 별도 로딩 없음&lt;/li&gt;
&lt;li&gt;prompt template 내장&lt;/li&gt;
&lt;li&gt;디바이스 최적화 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 실제 측정 기준 (내 환경)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Device: Galaxy S23 Ultra&lt;/li&gt;
&lt;li&gt;Model: Gemma 4 E2B&lt;/li&gt;
&lt;li&gt;Size: 약 2.4GB&lt;/li&gt;
&lt;li&gt;Latency: 약 40초 / 응답&lt;/li&gt;
&lt;li&gt;Acceleration: Hexagon NPU&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 수치가 의미하는 건 하나다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;돌아간다&amp;rdquo;와 &amp;ldquo;쓸 수 있다&amp;rdquo;는 다르다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 내가 바꾼 생각&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이렇게 접근했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 모델이 좋을까&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 이렇게 바뀌었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 런타임으로 돌릴 수 있을까&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  결론:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;모델을 고르는 문제만큼,&lt;br /&gt;&amp;nbsp; 런타임을 고르는 문제도 초기에 결정된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 참고&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LiteRT-LM GitHub&lt;/li&gt;
&lt;li&gt;Gemma 4 E2B 실행 케이스 (Galaxy S23)&lt;/li&gt;
&lt;li&gt;온디바이스 RAG 전체 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 한 줄 정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LiteRT-LM은 &amp;ldquo;모델을 실행 가능한 형태로 바꾸는 계층&amp;rdquo;이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ㅁ 함께 보면 좋은 사이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅇ &lt;a href=&quot;https://llm-study-web.vercel.app/&quot;&gt;llm-study-web&lt;/a&gt; : LLM 시스템의 전체 흐름을 개념학습, 시스템 맵, 인터랙티브 실험으로 배우는 교육 플랫폼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ㅇ&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://github.com/google-ai-edge/litert-lm&quot;&gt;LiteRT-LM&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 온디바이스 LLM을 실제로 &amp;ldquo;돌리게&amp;rdquo; 만드는 런타임&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI/RAG</category>
      <category>LiteRT-LM</category>
      <category>LLM</category>
      <category>PETERICA</category>
      <category>온디바이스AI</category>
      <author>기록하는 백엔드개발자</author>
      <guid isPermaLink="true">https://peterica.tistory.com/1073</guid>
      <comments>https://peterica.tistory.com/1073#entry1073comment</comments>
      <pubDate>Thu, 23 Apr 2026 00:00:58 +0900</pubDate>
    </item>
  </channel>
</rss>