<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>김신건의 로그 - 위키 / network</title><description>&apos;network&apos; 카테고리 위키 페이지 최신 업데이트</description><link>https://shinkeonkim.com/</link><language>ko-KR</language><item><title>[Network] IP: IPv4, IPv6, CIDR</title><link>https://shinkeonkim.com/wiki/network/network-ip/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/network-ip/</guid><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;IP (Internet Protocol) 주소&lt;/strong&gt; 는 인터넷에서 노드를 식별하는 논리 주소. IPv4 (32bit) 와 IPv6 (128bit) 두 버전이 병존.&lt;/p&gt;
&lt;h2 id=&quot;ipv4&quot;&gt;IPv4&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;4 개 8bit 옥텟&lt;/strong&gt;: &lt;code&gt;192.168.1.1&lt;/code&gt;. 총 2³² ≈ 43억 개, 이미 고갈.&lt;/p&gt;
&lt;h3 id=&quot;표준-주소-범위&quot;&gt;표준 주소 범위&lt;/h3&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;클래스&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;범위&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;용도&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;사설 A&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;10.0.0.0/8&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;대규모 사내망&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;사설 B&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;172.16.0.0/12&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;사설 C&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;192.168.0.0/16&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;홈 라우터 기본&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;루프백&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;127.0.0.0/8&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;localhost&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;링크 로컬&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;169.254.0.0/16&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;APIPA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;멀티캐스트&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;224.0.0.0/4&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;cidr-표기&quot;&gt;CIDR 표기&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;192.168.1.0/24&lt;/code&gt; = 앞 24 bit 가 네트워크, 뒤 8 bit 가 호스트. 256 개 주소 (실제 254 개 사용).&lt;/p&gt;
&lt;h2 id=&quot;ipv6&quot;&gt;IPv6&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;128bit&lt;/strong&gt;, &lt;code&gt;2001:0db8:85a3:0000:0000:8a2e:0370:7334&lt;/code&gt; (연속 0 은 &lt;code&gt;::&lt;/code&gt; 로 축약).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;주소 수: 2^128 ≈ 3.4 × 10³⁸ 개&lt;/li&gt;
&lt;li&gt;서브넷: /64 가 기본 (LAN 하나)&lt;/li&gt;
&lt;li&gt;특별 주소: &lt;code&gt;::1&lt;/code&gt; (loopback), &lt;code&gt;fe80::/10&lt;/code&gt; (link-local), &lt;code&gt;fc00::/7&lt;/code&gt; (unique local)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;public-vs-private&quot;&gt;Public vs Private&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Public IP&lt;/strong&gt;: 인터넷에서 라우팅 가능. ISP 가 할당.
&lt;strong&gt;Private IP&lt;/strong&gt;: 로컬 네트워크. NAT 로 public 과 매핑 ([[network-nat|자세히]]).&lt;/p&gt;
&lt;h2 id=&quot;dhcp&quot;&gt;DHCP&lt;/h2&gt;
&lt;p&gt;동적 IP 할당 프로토콜. 클라이언트가 요청하면 DHCP 서버가 임차 (lease).&lt;/p&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-nat|NAT]]&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting|CIDR / 서브네팅]]&lt;/li&gt;
&lt;li&gt;[[network-dns|DNS]]&lt;/li&gt;
&lt;li&gt;[[tcp|TCP]], [[udp|UDP]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>ip</category><category>ipv4</category><category>ipv6</category><author>koa (김신건)</author></item><item><title>[Network] NAT: Network Address Translation</title><link>https://shinkeonkim.com/wiki/network/network-nat/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/network-nat/</guid><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;NAT (Network Address Translation)&lt;/strong&gt; 는 IP 주소를 변환하는 기법. 주로 사설 IP 를 public IP 로, 또는 반대로 매핑.&lt;/p&gt;
&lt;h2 id=&quot;왜-필요한가&quot;&gt;왜 필요한가&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IPv4 주소 고갈&lt;/strong&gt;: 사내 100 대 호스트가 1 public IP 공유&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보안&lt;/strong&gt;: 내부 호스트 은닉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정책&lt;/strong&gt;: 특정 서비스만 외부 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;종류&quot;&gt;종류&lt;/h2&gt;
&lt;h3 id=&quot;snat-source-nat&quot;&gt;SNAT (Source NAT)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;아웃바운드&lt;/strong&gt; 트래픽의 source IP 를 변경. 홈 라우터 기본 동작:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;내부 &lt;code&gt;192.168.1.10:12345&lt;/code&gt; → &lt;code&gt;203.0.113.5:12345&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;응답 오면 매핑 테이블 참조하여 원래 클라이언트로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;dnat-destination-nat&quot;&gt;DNAT (Destination NAT)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;인바운드&lt;/strong&gt; 트래픽의 destination IP 를 변경. 포트 포워딩:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;외부 &lt;code&gt;203.0.113.5:80&lt;/code&gt; → 내부 &lt;code&gt;192.168.1.10:8080&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pat-port-address-translation-napt&quot;&gt;PAT (Port Address Translation, NAPT)&lt;/h3&gt;
&lt;p&gt;포트까지 매핑. 한 public IP 를 여러 호스트가 공유:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.1.10:12345&lt;/code&gt; → &lt;code&gt;203.0.113.5:1001&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.11:12345&lt;/code&gt; → &lt;code&gt;203.0.113.5:1002&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;거의 모든 실제 NAT 는 PAT.&lt;/p&gt;
&lt;h2 id=&quot;클라우드-nat&quot;&gt;클라우드 NAT&lt;/h2&gt;
&lt;h3 id=&quot;aws&quot;&gt;AWS&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NAT Gateway&lt;/strong&gt;: 관리형, 프라이빗 서브넷의 아웃바운드&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT Instance&lt;/strong&gt;: 자체 관리 EC2&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;흐름&quot;&gt;흐름&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private subnet → NAT Gateway (public subnet) → Internet Gateway → Internet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;함정&quot;&gt;함정&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NAT traversal&lt;/strong&gt;: P2P (WebRTC, BitTorrent) 는 NAT 통과 어려움 → STUN, TURN 서버 필요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hairpin NAT&lt;/strong&gt;: 내부에서 자기 public IP 로 접근하는 경우&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT 만료&lt;/strong&gt;: 매핑 테이블은 유휴 시 만료 → long-lived TCP 는 keep-alive 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-ip|IP]]&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting|CIDR / 서브네팅]]&lt;/li&gt;
&lt;li&gt;[[aws-vpc|AWS VPC]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>nat</category><category>routing</category><author>koa (김신건)</author></item><item><title>[Network] CORS: Same-Origin Policy, preflight, credentials</title><link>https://shinkeonkim.com/wiki/network/cors/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/cors/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CORS (Cross-Origin Resource Sharing)&lt;/strong&gt; 는 &lt;em&gt;Same-Origin Policy (SOP)&lt;/em&gt; 의 &lt;em&gt;완화 메커니즘&lt;/em&gt;. 브라우저가 &lt;em&gt;origin 이 다른&lt;/em&gt; 리소스에 &lt;em&gt;조건부로 접근&lt;/em&gt; 할 수 있게 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
CORS 는 &lt;em&gt;브라우저의 보안 모델&lt;/em&gt;. &lt;em&gt;서버 → 서버&lt;/em&gt; 호출에는 영향 없음. &lt;em&gt;curl, Postman 도 CORS 적용 안 함&lt;/em&gt;. &lt;em&gt;오로지 브라우저가 JS 코드의 cross-origin fetch&lt;/em&gt; 를 막거나 허용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;origin--scheme--host--port&quot;&gt;Origin = scheme + host + port&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;URL&lt;/th&gt;&lt;th&gt;Origin&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;https://app.example.com/page&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;https://app.example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;https://app.example.com:443&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;https://app.example.com&lt;/code&gt; (default port)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;http://app.example.com&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;http://app.example.com&lt;/code&gt; (scheme 다름)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;https://api.example.com&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;https://api.example.com&lt;/code&gt; (host 다름)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;simple-request-vs-preflight&quot;&gt;Simple Request vs Preflight&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q{요청이 Simple?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|예| S[즉시 보냄]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|아니오| P[OPTIONS preflight 먼저]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S --&gt; CHECK1[응답의 ACAO 검증]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    P --&gt; CHECK2[preflight 응답 검증]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CHECK2 --&gt;|허용| Send[실제 요청 전송]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CHECK2 --&gt;|거절| Block[브라우저 차단]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;simple-request-조건-모두-만족&quot;&gt;Simple Request 조건 (모두 만족):&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Method: GET, HEAD, POST&lt;/li&gt;
&lt;li&gt;헤더: 안전한 헤더 + &lt;code&gt;Content-Type: application/x-www-form-urlencoded | multipart/form-data | text/plain&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;이벤트 listener 없음 (XHR upload)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReadableStream&lt;/code&gt; body 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 위 조건 &lt;em&gt;하나라도 어기면 preflight&lt;/em&gt;. JSON &lt;code&gt;Content-Type: application/json&lt;/code&gt; 도 &lt;em&gt;preflight 유발&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;preflight-흐름&quot;&gt;Preflight 흐름&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant B as Browser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;S: OPTIONS /api/users&amp;#x3C;br/&gt;Origin: https://app.example.com&amp;#x3C;br/&gt;Access-Control-Request-Method: POST&amp;#x3C;br/&gt;Access-Control-Request-Headers: content-type, authorization&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;B: 204 No Content&amp;#x3C;br/&gt;Access-Control-Allow-Origin: https://app.example.com&amp;#x3C;br/&gt;Access-Control-Allow-Methods: GET, POST, DELETE&amp;#x3C;br/&gt;Access-Control-Allow-Headers: content-type, authorization&amp;#x3C;br/&gt;Access-Control-Max-Age: 86400&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over B: 캐시 (Max-Age)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;S: POST /api/users&amp;#x3C;br/&gt;Origin: https://app.example.com&amp;#x3C;br/&gt;Content-Type: application/json&amp;#x3C;br/&gt;{...}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;B: 201 Created&amp;#x3C;br/&gt;Access-Control-Allow-Origin: https://app.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;response-헤더-가이드&quot;&gt;Response 헤더 가이드&lt;/h2&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&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;code&gt;Access-Control-Allow-Origin&lt;/code&gt;&lt;/td&gt;&lt;td&gt;허용된 origin (&lt;em&gt;하나만&lt;/em&gt; 또는 &lt;code&gt;*&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt;&lt;/td&gt;&lt;td&gt;preflight 응답에서 허용 method&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt;&lt;/td&gt;&lt;td&gt;허용 헤더&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Access-Control-Expose-Headers&lt;/code&gt;&lt;/td&gt;&lt;td&gt;JS 가 읽을 수 있는 응답 헤더&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Access-Control-Max-Age&lt;/code&gt;&lt;/td&gt;&lt;td&gt;preflight 캐시 (초)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt; 면 cookie/Authorization 허용&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;credentials-cookie--authorization&quot;&gt;Credentials (cookie / Authorization)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://api.example.com/me&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  credentials: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;include&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// cookie 보내기&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 경우 서버는 &lt;em&gt;반드시&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://app.example.com   ← * 불가능!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
*&lt;code&gt;Allow-Origin: *&lt;/code&gt; + &lt;code&gt;Allow-Credentials: true&lt;/code&gt; 는 &lt;em&gt;RFC 위반&lt;/em&gt;. 브라우저가 거절. credentials 가 필요하면 &lt;em&gt;명시적 origin&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Allow-Origin: *&lt;/code&gt; 를 &lt;em&gt;모든 경우에&lt;/em&gt; 박음&lt;/strong&gt; = credentials 모드에서 동작 안 함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;preflight 응답에 &lt;em&gt;200&lt;/em&gt; 보냄&lt;/strong&gt; = 보통 &lt;em&gt;204 No Content&lt;/em&gt; 가 정석. 200 도 동작은 함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Vary: Origin&lt;/code&gt; 누락&lt;/strong&gt; = CDN 이 &lt;em&gt;다른 origin 응답&lt;/em&gt; 을 캐시. 모든 사용자에게 &lt;em&gt;같은 origin 의 응답&lt;/em&gt; 이 감.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CORS 가 &lt;em&gt;서버 보안&lt;/em&gt; 이라고 오해&lt;/strong&gt; = 브라우저 보안. &lt;em&gt;curl 은 CORS 무시&lt;/em&gt;. &lt;em&gt;서버 보안은 별도&lt;/em&gt; (auth, IP allowlist 등).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로컬 개발 환경 (&lt;code&gt;localhost:3000&lt;/code&gt;) 만 허용&lt;/strong&gt; 으로 &lt;em&gt;프로덕션 cross-origin 미테스트&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;cors-vs-csrf&quot;&gt;CORS vs CSRF&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;CORS&lt;/th&gt;&lt;th&gt;CSRF&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;em&gt;cross-origin fetch 허용&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;cross-origin 의 &lt;em&gt;위조 요청&lt;/em&gt; 방어&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;방향&lt;/td&gt;&lt;td&gt;브라우저 → 서버 &lt;em&gt;허용&lt;/em&gt;&lt;/td&gt;&lt;td&gt;브라우저 → 서버 &lt;em&gt;거절&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;토대&lt;/td&gt;&lt;td&gt;Same-Origin Policy 의 &lt;em&gt;완화&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Same-Origin Policy 의 &lt;em&gt;우회 방어&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;도구&lt;/td&gt;&lt;td&gt;Response 헤더 (ACAO)&lt;/td&gt;&lt;td&gt;CSRF token, SameSite cookie&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;자세한 건 [[CSRF]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;서버-구현-예시-express&quot;&gt;서버 구현 예시 (Express)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;cors&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  origin: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://app.example.com&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://staging.example.com&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  credentials: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  methods: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;PUT&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;DELETE&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  allowedHeaders: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Authorization&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  exposedHeaders: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;X-Total-Count&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  maxAge: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;86400&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;sop-가-막는-것들&quot;&gt;SOP 가 막는 것들&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fetch(&apos;https://other.com&apos;)&lt;/code&gt; 의 응답 읽기&lt;/li&gt;
&lt;li&gt;다른 origin iframe 의 DOM 접근&lt;/li&gt;
&lt;li&gt;다른 origin 의 cookie 읽기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SOP 가 &lt;em&gt;막지 않는 것&lt;/em&gt; (CSRF 의 토대):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;#x3C;form action=&quot;https://other.com&quot;&gt;&lt;/code&gt; 의 POST&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;#x3C;img src=&quot;https://other.com/api&quot;&gt;&lt;/code&gt; (GET)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;#x3C;script src=&quot;https://other.com/api&quot;&gt;&lt;/code&gt; (실행됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[CSRF]]&lt;/li&gt;
&lt;li&gt;[[Session Cookie]]&lt;/li&gt;
&lt;li&gt;[[JWT]]&lt;/li&gt;
&lt;li&gt;[[HTTP/1.1]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>cors</category><category>security</category><category>browser</category><category>network</category><category>backend</category><author>koa (김신건)</author></item><item><title>[Network] gRPC: HTTP/2 + Protobuf, 4가지 streaming 패턴</title><link>https://shinkeonkim.com/wiki/network/grpc/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/grpc/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;gRPC&lt;/strong&gt; 는 &lt;em&gt;HTTP/2 위에서 Protobuf 직렬화&lt;/em&gt; 로 동작하는 &lt;em&gt;고성능 RPC 프레임워크&lt;/em&gt;. Google 내부 &lt;em&gt;Stubby&lt;/em&gt; 의 오픈소스 후계.&lt;/p&gt;
&lt;p&gt;핵심 4가지:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Protobuf&lt;/strong&gt;: IDL + binary 직렬화 (JSON 대비 &lt;em&gt;수배 작고 빠름&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP/2&lt;/strong&gt;: multiplexing + binary framing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming 4종&lt;/strong&gt;: unary, server-stream, client-stream, bidi&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code generation&lt;/strong&gt;: &lt;code&gt;.proto&lt;/code&gt; → 다국어 stub 자동 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;proto-정의&quot;&gt;Proto 정의&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;protobuf&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;syntax&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;proto3&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; shop.v1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;service&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Shop&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Unary: 1 req → 1 res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; GetItem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;GetItemRequest&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Server streaming: 1 req → N res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ListItems&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ListRequest&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Client streaming: N req → 1 res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UploadEvents&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Event&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;UploadAck&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Bidirectional: N req ↔ N res&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Chat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Message&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;returns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;stream&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Message&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  int32&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; price_cents &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4가지-streaming-패턴&quot;&gt;4가지 Streaming 패턴&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph Unary[&quot;Unary RPC&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U1[Client] --&gt;|1 request| U2[Server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U2 --&gt;|1 response| U1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph ServerStream[&quot;Server Streaming&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S1[Client] --&gt;|1 request| S2[Server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S2 --&gt;|stream of N| S1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph ClientStream[&quot;Client Streaming&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C1[Client] --&gt;|stream of N| C2[Server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C2 --&gt;|1 response| C1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph BiDi[&quot;Bidirectional Streaming&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B1[Client] --&gt;|stream of N| B2[Server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B2 --&gt;|stream of M| B1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&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;Unary&lt;/td&gt;&lt;td&gt;일반 API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server stream&lt;/td&gt;&lt;td&gt;라이브 피드, log tail, 실시간 시세&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Client stream&lt;/td&gt;&lt;td&gt;파일 업로드, 이벤트 일괄 전송&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bidi stream&lt;/td&gt;&lt;td&gt;채팅, 게임, 협업&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;http2-위-동작&quot;&gt;HTTP/2 위 동작&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:method = POST&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:scheme = https&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:path = /shop.v1.Shop/GetItem&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:authority = api.example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;content-type = application/grpc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;grpc-encoding = gzip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;grpc-accept-encoding = identity,deflate,gzip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;grpc-timeout = 1S&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;protobuf-encoded&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; body&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 RPC = HTTP/2 &lt;em&gt;stream 하나&lt;/em&gt;. &lt;em&gt;Trailers&lt;/em&gt; 로 &lt;code&gt;grpc-status&lt;/code&gt;, &lt;code&gt;grpc-message&lt;/code&gt; 전달.&lt;/p&gt;
&lt;h2 id=&quot;status-code&quot;&gt;Status Code&lt;/h2&gt;













































































&lt;table&gt;&lt;thead&gt;&lt;tr&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;0 OK&lt;/td&gt;&lt;td&gt;성공&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1 CANCELLED&lt;/td&gt;&lt;td&gt;클라이언트가 cancel&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2 UNKNOWN&lt;/td&gt;&lt;td&gt;정체불명 에러&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3 INVALID_ARGUMENT&lt;/td&gt;&lt;td&gt;입력 오류&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4 DEADLINE_EXCEEDED&lt;/td&gt;&lt;td&gt;timeout&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5 NOT_FOUND&lt;/td&gt;&lt;td&gt;리소스 없음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6 ALREADY_EXISTS&lt;/td&gt;&lt;td&gt;중복&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7 PERMISSION_DENIED&lt;/td&gt;&lt;td&gt;권한&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8 RESOURCE_EXHAUSTED&lt;/td&gt;&lt;td&gt;quota 초과&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9 FAILED_PRECONDITION&lt;/td&gt;&lt;td&gt;사전조건 위배&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10 ABORTED&lt;/td&gt;&lt;td&gt;동시성 충돌&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11 OUT_OF_RANGE&lt;/td&gt;&lt;td&gt;범위 외&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;12 UNIMPLEMENTED&lt;/td&gt;&lt;td&gt;구현 안됨&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;13 INTERNAL&lt;/td&gt;&lt;td&gt;내부 에러&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;14 UNAVAILABLE&lt;/td&gt;&lt;td&gt;일시 불가, &lt;em&gt;retry 권장&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;15 DATA_LOSS&lt;/td&gt;&lt;td&gt;손실&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;16 UNAUTHENTICATED&lt;/td&gt;&lt;td&gt;인증 실패&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;UNAVAILABLE (14)&lt;/em&gt; 만 retry 안전. &lt;em&gt;DEADLINE_EXCEEDED (4)&lt;/em&gt; 는 &lt;em&gt;side effect 가 발생했을 수&lt;/em&gt; 있어 &lt;em&gt;idempotent operation&lt;/em&gt; 만 retry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;deadline-timeout&quot;&gt;Deadline (timeout)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Python&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; stub.GetItem(req, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;timeout&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 1초&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 또는 metadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;metadata &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;grpc-timeout&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1S&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;클라이언트의 deadline 은 &lt;em&gt;서버에 전파 (header)&lt;/em&gt;. 서버가 더 깊은 RPC 를 부르면 &lt;em&gt;남은 deadline 만 전달&lt;/em&gt;. &lt;em&gt;분산 timeout chaining&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;grpc-vs-rest&quot;&gt;gRPC vs REST&lt;/h2&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;gRPC&lt;/th&gt;&lt;th&gt;REST&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Wire 포맷&lt;/td&gt;&lt;td&gt;Binary (Protobuf)&lt;/td&gt;&lt;td&gt;Text (JSON)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;크기&lt;/td&gt;&lt;td&gt;&lt;em&gt;수배 작음&lt;/em&gt;&lt;/td&gt;&lt;td&gt;크다&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Schema&lt;/td&gt;&lt;td&gt;&lt;em&gt;IDL 필수&lt;/em&gt;&lt;/td&gt;&lt;td&gt;옵션 (OpenAPI)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Streaming&lt;/td&gt;&lt;td&gt;&lt;em&gt;4 모드&lt;/em&gt;&lt;/td&gt;&lt;td&gt;SSE / WebSocket 별도&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;브라우저 직접 호출&lt;/td&gt;&lt;td&gt;grpc-web 필요&lt;/td&gt;&lt;td&gt;&lt;em&gt;기본 지원&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Debugging&lt;/td&gt;&lt;td&gt;도구 필요&lt;/td&gt;&lt;td&gt;curl 가능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;캐싱&lt;/td&gt;&lt;td&gt;직접 구현&lt;/td&gt;&lt;td&gt;HTTP cache&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mobile / IoT&lt;/td&gt;&lt;td&gt;&lt;em&gt;유리&lt;/em&gt;&lt;/td&gt;&lt;td&gt;OK&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;내부 microservice 통신&lt;/em&gt; → gRPC. &lt;em&gt;외부 public API&lt;/em&gt; → REST/GraphQL.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;grpc-web&quot;&gt;gRPC-Web&lt;/h2&gt;
&lt;p&gt;브라우저는 HTTP/2 trailer 미지원 → &lt;em&gt;Envoy / proxy&lt;/em&gt; 가 &lt;em&gt;gRPC ↔ gRPC-Web&lt;/em&gt; 변환:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Browser --&gt;|gRPC-Web&amp;#x3C;br/&gt;over HTTP/1.1| Envoy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Envoy --&gt;|gRPC&amp;#x3C;br/&gt;over HTTP/2| Backend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Field number 재사용&lt;/strong&gt; = proto 의 &lt;em&gt;tag 번호&lt;/em&gt; 가 &lt;em&gt;wire format 의 identity&lt;/em&gt;. 한 번 쓴 번호 &lt;em&gt;영구히 reserved&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;required&lt;/code&gt; 필드&lt;/strong&gt; = proto2 의 &lt;em&gt;legacy&lt;/em&gt;. proto3 는 &lt;em&gt;모든 필드가 optional 류&lt;/em&gt;. &lt;em&gt;&lt;code&gt;required&lt;/code&gt; 사용 금지&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;string vs bytes&lt;/strong&gt; = string 은 &lt;em&gt;UTF-8 검증&lt;/em&gt;. binary 데이터는 &lt;em&gt;bytes&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stream 의 &lt;em&gt;완료 표시&lt;/em&gt;&lt;/strong&gt; = 클라이언트 stream 은 &lt;em&gt;반드시 half-close (closeSend)&lt;/em&gt; 해야 서버가 finalize.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[HTTP/2]]&lt;/li&gt;
&lt;li&gt;[[Protobuf]] (별도 자료)&lt;/li&gt;
&lt;li&gt;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[GraphQL]]&lt;/li&gt;
&lt;li&gt;[[Microservices]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>grpc</category><category>rpc</category><category>network</category><category>backend</category><category>protobuf</category><category>http2</category><author>koa (김신건)</author></item><item><title>[Network] HTTP/1.1: keep-alive, pipelining, chunked transfer</title><link>https://shinkeonkim.com/wiki/network/http-1-1/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/http-1-1/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP/1.1&lt;/strong&gt; (1997, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9112&quot;&gt;RFC 9112&lt;/a&gt; 으로 재정리) 는 &lt;em&gt;텍스트 기반 stateless request/response 프로토콜&lt;/em&gt;. 2026 시점에도 &lt;em&gt;대부분의 트래픽이 HTTP/1.1 + 일부 HTTP/2/3&lt;/em&gt; 분포.&lt;/p&gt;
&lt;p&gt;핵심 진화 (HTTP/1.0 대비):&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;기능&lt;/th&gt;&lt;th&gt;HTTP/1.0&lt;/th&gt;&lt;th&gt;HTTP/1.1&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;em&gt;매 요청 새 TCP&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;Persistent (keep-alive)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Host 헤더&lt;/td&gt;&lt;td&gt;옵션&lt;/td&gt;&lt;td&gt;&lt;em&gt;필수&lt;/em&gt; (가상호스팅)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chunked transfer&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;td&gt;&lt;em&gt;있음&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;캐시 제어&lt;/td&gt;&lt;td&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Cache-Control&lt;/code&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;em&gt;Range / 206 Partial Content&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;메시지-구조&quot;&gt;메시지 구조&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /api/users/42 &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Host&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; api.example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;User-Agent&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; curl/8.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Accept&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Length&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;koa&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;tier&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;pro&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;keep-alive-persistent-connection&quot;&gt;Keep-Alive (Persistent Connection)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Connection: keep-alive&lt;/code&gt; 헤더로 &lt;em&gt;TCP 연결 재사용&lt;/em&gt;. 한 연결에서 &lt;em&gt;여러 요청/응답&lt;/em&gt;. TLS 핸드셰이크 비용 절감의 &lt;em&gt;결정적 기능&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: TCP SYN / handshake (3-way)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: SYN-ACK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 OK (Connection: keep-alive)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /b (같은 TCP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /c (같은 TCP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: idle timeout (예: 60s) 후 close&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&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;code&gt;Connection: keep-alive&lt;/code&gt;&lt;/td&gt;&lt;td&gt;연결 재사용 (HTTP/1.1 기본)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Connection: close&lt;/code&gt;&lt;/td&gt;&lt;td&gt;응답 후 즉시 close&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Keep-Alive: timeout=60, max=100&lt;/code&gt;&lt;/td&gt;&lt;td&gt;idle 60s, max 100 요청&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
서버 (nginx, ELB) 의 &lt;em&gt;idle timeout&lt;/em&gt; 과 클라이언트 &lt;em&gt;connection pool TTL&lt;/em&gt; 의 불일치가 &lt;em&gt;간헐적 connection reset&lt;/em&gt; 의 가장 흔한 원인. 보통 &lt;em&gt;클라이언트 &amp;#x3C; 서버&lt;/em&gt; 로 잡는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;pipelining-구현-함정&quot;&gt;Pipelining (구현 함정)&lt;/h2&gt;
&lt;p&gt;같은 연결에서 &lt;em&gt;응답을 기다리지 않고 여러 요청 동시 전송&lt;/em&gt;. &lt;em&gt;Head-of-Line Blocking&lt;/em&gt; 문제 → 실전 거의 사용 안 됨.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;요청: A → B → C → D (즉시 연속)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;응답: A → B → C → D (반드시 순서대로)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;문제: A 가 느리면 B/C/D 도 모두 대기&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ HTTP/2 의 &lt;em&gt;multiplexing&lt;/em&gt; 이 이 문제를 &lt;em&gt;프레임 인터리빙&lt;/em&gt; 으로 해결. 자세한 건 [[head-of-line-blocking]] / [[HTTP/2]].&lt;/p&gt;
&lt;h2 id=&quot;content-length-vs-chunked&quot;&gt;Content-Length vs Chunked&lt;/h2&gt;
&lt;p&gt;본문 끝을 알리는 &lt;em&gt;두 가지 방법&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 1. Content-Length (크기 미리 알 때)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Length&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 12345&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;12345&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bytes&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;# 2. Transfer-Encoding: chunked (streaming)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;HTTP/1.1 200 OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Transfer-Encoding: chunked&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;7\r\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Mozilla\r\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;9\r\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Developer\r\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;0\r\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\r\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




















&lt;table&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;code&gt;Content-Length&lt;/code&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;code&gt;chunked&lt;/code&gt;&lt;/td&gt;&lt;td&gt;청크 단위 전송&lt;/td&gt;&lt;td&gt;&lt;em&gt;streaming&lt;/em&gt; (SSE, long-poll, 대용량 응답)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;동일 응답에 두 방식 모두 설정&lt;/em&gt; 하면 &lt;em&gt;RFC 위반&lt;/em&gt;. 일부 프록시는 chunked 우선, 일부는 Content-Length 우선 → &lt;em&gt;HTTP Request Smuggling&lt;/em&gt; 공격의 토대. 항상 &lt;em&gt;하나만&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;상태-코드-카테고리&quot;&gt;상태 코드 카테고리&lt;/h2&gt;



































&lt;table&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;1xx&lt;/td&gt;&lt;td&gt;Informational&lt;/td&gt;&lt;td&gt;&lt;code&gt;100 Continue&lt;/code&gt;, &lt;code&gt;101 Switching Protocols&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2xx&lt;/td&gt;&lt;td&gt;Success&lt;/td&gt;&lt;td&gt;&lt;code&gt;200 OK&lt;/code&gt;, &lt;code&gt;201 Created&lt;/code&gt;, &lt;code&gt;204 No Content&lt;/code&gt;, &lt;code&gt;206 Partial Content&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3xx&lt;/td&gt;&lt;td&gt;Redirection&lt;/td&gt;&lt;td&gt;&lt;code&gt;301 Moved Permanently&lt;/code&gt;, &lt;code&gt;302 Found&lt;/code&gt;, &lt;code&gt;304 Not Modified&lt;/code&gt;, &lt;code&gt;307 Temporary Redirect&lt;/code&gt;, &lt;code&gt;308 Permanent Redirect&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4xx&lt;/td&gt;&lt;td&gt;Client Error&lt;/td&gt;&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;, &lt;code&gt;401 Unauthorized&lt;/code&gt;, &lt;code&gt;403 Forbidden&lt;/code&gt;, &lt;code&gt;404 Not Found&lt;/code&gt;, &lt;code&gt;409 Conflict&lt;/code&gt;, &lt;code&gt;422 Unprocessable&lt;/code&gt;, &lt;code&gt;429 Too Many Requests&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5xx&lt;/td&gt;&lt;td&gt;Server Error&lt;/td&gt;&lt;td&gt;&lt;code&gt;500&lt;/code&gt;, &lt;code&gt;502 Bad Gateway&lt;/code&gt;, &lt;code&gt;503 Service Unavailable&lt;/code&gt;, &lt;code&gt;504 Gateway Timeout&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;307/308&lt;/code&gt; vs &lt;code&gt;302/301&lt;/code&gt; 의 핵심 차이: &lt;strong&gt;307/308 은 HTTP method 를 보존&lt;/strong&gt;. &lt;code&gt;POST → 302 → GET&lt;/code&gt; 의 &lt;em&gt;옛 클라이언트 동작&lt;/em&gt; 이 &lt;em&gt;307 에서는 POST → POST&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;cache-헤더-http11-의-가장-큰-진화&quot;&gt;Cache 헤더 (HTTP/1.1 의 가장 큰 진화)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; public, max-age=3600, must-revalidate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ETag&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;abc123&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Last-Modified&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Wed, 25 Jun 2026 12:00:00 GMT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[클라이언트&amp;#x3C;br/&gt;If-None-Match: abc123] --&gt; B[서버]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B --&gt;|ETag 일치| C[304 Not Modified&amp;#x3C;br/&gt;body 없음]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B --&gt;|ETag 불일치| D[200 OK&amp;#x3C;br/&gt;새 본문 + 새 ETag]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 캐시 패턴은 [[network-http-caching]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Host&lt;/code&gt; 헤더 누락&lt;/strong&gt; = HTTP/1.1 에서 &lt;em&gt;400 Bad Request&lt;/em&gt;. 가상호스팅 때문에 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content-Length 거짓말&lt;/strong&gt; = 본문이 짧으면 &lt;em&gt;클라이언트가 무한 대기&lt;/em&gt;, 길면 &lt;em&gt;다음 요청과 섞임&lt;/em&gt; (smuggling).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Connection: close&lt;/code&gt; 남발&lt;/strong&gt; = TCP/TLS 비용 폭증. 명시적 이유 없으면 &lt;em&gt;기본 keep-alive&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Cache-Control: no-cache&lt;/code&gt; 와 &lt;code&gt;no-store&lt;/code&gt; 혼동&lt;/strong&gt; = no-cache 는 &lt;em&gt;재검증 필요&lt;/em&gt;, no-store 는 &lt;em&gt;저장 자체 금지&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[TCP]], [[TLS]]&lt;/li&gt;
&lt;li&gt;[[HTTP/2]] (multiplexing 으로 HoL blocking 해결)&lt;/li&gt;
&lt;li&gt;[[HTTP/3]] (QUIC 위 HTTP)&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking]], [[hpack]]&lt;/li&gt;
&lt;li&gt;[[network-http-caching]] (캐시 헤더 깊게)&lt;/li&gt;
&lt;li&gt;[[Load Balancer]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>http</category><category>network</category><category>backend</category><category>protocol</category><author>koa (김신건)</author></item><item><title>[Network] HTTP/2: multiplexing, HPACK, server push의 종말</title><link>https://shinkeonkim.com/wiki/network/http-2/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/http-2/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP/2&lt;/strong&gt; (2015, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9113&quot;&gt;RFC 9113&lt;/a&gt;) 는 HTTP/1.1 의 &lt;em&gt;문법은 유지하되 전송을 바이너리 + 멀티플렉싱&lt;/em&gt; 으로 바꾼다. &lt;em&gt;Head-of-Line Blocking&lt;/em&gt; (HTTP 레벨) 해결이 핵심.&lt;/p&gt;
&lt;p&gt;핵심 변경 3가지:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Binary framing&lt;/strong&gt;: 텍스트 → 바이너리. 파싱 효율 + 정확성.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stream multiplexing&lt;/strong&gt;: 한 TCP 연결에서 &lt;em&gt;여러 stream 병렬&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HPACK 헤더 압축&lt;/strong&gt;: 반복 헤더의 &lt;em&gt;압축 + 인덱싱&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;binary-framing&quot;&gt;Binary Framing&lt;/h2&gt;
&lt;p&gt;HTTP/1.1 의 &lt;em&gt;텍스트 메시지&lt;/em&gt; 가 &lt;em&gt;frame&lt;/em&gt; 으로 분해:&lt;/p&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Frame Type&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;code&gt;DATA&lt;/code&gt;&lt;/td&gt;&lt;td&gt;본문&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;HEADERS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;헤더&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PRIORITY&lt;/code&gt;&lt;/td&gt;&lt;td&gt;스트림 우선순위&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;RST_STREAM&lt;/code&gt;&lt;/td&gt;&lt;td&gt;스트림 취소&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;SETTINGS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;연결 설정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PUSH_PROMISE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;server push (대부분 비활성)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PING&lt;/code&gt;&lt;/td&gt;&lt;td&gt;keep-alive 측정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GOAWAY&lt;/code&gt;&lt;/td&gt;&lt;td&gt;연결 종료&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;WINDOW_UPDATE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;flow control&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CONTINUATION&lt;/code&gt;&lt;/td&gt;&lt;td&gt;큰 헤더 분할&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    H1[&quot;HTTP/1.1 메시지&amp;#x3C;br/&gt;(텍스트)&quot;] --&gt;|HTTP/2| Frames&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Frames[&quot;[HEADERS frame]&amp;#x3C;br/&gt;[DATA frame]&amp;#x3C;br/&gt;[DATA frame]&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;multiplexing&quot;&gt;Multiplexing&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;한 TCP 연결에서 &lt;em&gt;여러 stream (요청/응답 쌍)&lt;/em&gt; 이 &lt;em&gt;frame 인터리빙&lt;/em&gt; 으로 동시 진행:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: HEADERS stream=1 (GET /a)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: HEADERS stream=3 (GET /b)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: HEADERS stream=5 (GET /c)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: HEADERS stream=3 (작은 응답 먼저)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: DATA stream=3 (완료)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: HEADERS stream=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: DATA stream=5 (부분)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: DATA stream=1 (부분)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: DATA stream=1 (완료)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: DATA stream=5 (완료)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;HTTP/1.1&lt;/th&gt;&lt;th&gt;HTTP/2&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;em&gt;N개 TCP 연결&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;1 TCP, N streams&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HoL Blocking (HTTP)&lt;/td&gt;&lt;td&gt;있음 (pipelining)&lt;/td&gt;&lt;td&gt;&lt;em&gt;없음&lt;/em&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;em&gt;HPACK&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Binary&lt;/td&gt;&lt;td&gt;텍스트&lt;/td&gt;&lt;td&gt;&lt;em&gt;binary&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
Multiplexing 은 &lt;em&gt;HTTP 레벨의 HoL blocking&lt;/em&gt; 만 해결. &lt;em&gt;TCP 패킷 손실&lt;/em&gt; 이 발생하면 &lt;em&gt;그 아래 모든 stream 이 대기&lt;/em&gt;. 이 한계가 &lt;em&gt;HTTP/3 (QUIC)&lt;/em&gt; 의 등장 이유.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;hpack-헤더-압축&quot;&gt;HPACK 헤더 압축&lt;/h2&gt;
&lt;p&gt;반복되는 헤더 (&lt;code&gt;User-Agent&lt;/code&gt;, &lt;code&gt;Cookie&lt;/code&gt; 등) 를 &lt;em&gt;인덱스로 참조&lt;/em&gt;. &lt;em&gt;Static Table (61개 미리 정의) + Dynamic Table (연결마다 갱신)&lt;/em&gt; + Huffman.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;요청 1: :method=GET, :path=/a, user-agent=Mozilla/5.0...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;→ 보내고 dynamic table 에 등록 (예: index 62)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;요청 2: :method=GET, :path=/b, user-agent=Mozilla/5.0...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;→ index 62 만 보냄 (수십 바이트 → 1 바이트)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 건 [[hpack]] 참고.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;em&gt;CRIME / BREACH 공격&lt;/em&gt; 의 변형 (HPACK 압축 oracle). 일반적으로 안전하지만, &lt;em&gt;비밀 헤더 + 사용자 입력이 같은 응답&lt;/em&gt; 에 들어가면 위험.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;server-push-사실상-폐기&quot;&gt;Server Push (사실상 폐기)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/index.html&lt;/code&gt; 요청에 &lt;em&gt;서버가 미리&lt;/em&gt; &lt;code&gt;/style.css&lt;/code&gt;, &lt;code&gt;/app.js&lt;/code&gt; 도 push:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: PUSH_PROMISE /style.css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: PUSH_PROMISE /app.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 /index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 /style.css (요청 없이 미리)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 /app.js (요청 없이 미리)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;2022 Chrome 이 server push 를 제거&lt;/strong&gt;. &lt;em&gt;push 캐시 적중률이 낮고&lt;/em&gt; &lt;em&gt;bandwidth 낭비&lt;/em&gt; + &lt;em&gt;priority 충돌&lt;/em&gt;. &lt;strong&gt;2026 시점 server push 는 사실상 죽은 기능&lt;/strong&gt;. 대신 &lt;em&gt;103 Early Hints&lt;/em&gt; + &lt;em&gt;&lt;code&gt;&amp;#x3C;link rel=preload&gt;&lt;/code&gt;&lt;/em&gt; 패턴.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;h2-vs-h2c&quot;&gt;h2 vs h2c&lt;/h2&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&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;code&gt;h2&lt;/code&gt;&lt;/td&gt;&lt;td&gt;TLS 위 HTTP/2 (대부분)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;h2c&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;cleartext&lt;/em&gt; (내부 네트워크, gRPC mesh)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;브라우저는 &lt;code&gt;h2&lt;/code&gt; 만. 서버끼리는 &lt;code&gt;h2c&lt;/code&gt; 도 흔함 (TLS 종료를 가까운 sidecar/proxy 에서).&lt;/p&gt;
&lt;h2 id=&quot;flow-control&quot;&gt;Flow Control&lt;/h2&gt;
&lt;p&gt;스트림 / 연결마다 &lt;em&gt;window size&lt;/em&gt;. &lt;code&gt;WINDOW_UPDATE&lt;/code&gt; 로 &lt;em&gt;backpressure&lt;/em&gt;. TCP 의 sliding window 와 별개 &lt;em&gt;application 레벨&lt;/em&gt; 제어.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt;|WINDOW_UPDATE +65535| Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client --&gt;|&quot;DATA (window 안에서만)&quot;| Server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;다수 TCP 연결의 &lt;em&gt;습관&lt;/em&gt;&lt;/strong&gt; = HTTP/2 환경에서 &lt;em&gt;6 connections per origin&lt;/em&gt; 같이 옛 패턴을 유지하면 &lt;em&gt;멀티플렉싱 효과 0&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Connection&lt;/code&gt;, &lt;code&gt;Upgrade&lt;/code&gt;, &lt;code&gt;Proxy-Connection&lt;/code&gt; 헤더&lt;/strong&gt; = HTTP/2 에서 &lt;em&gt;금지된 헤더&lt;/em&gt;. 보내면 연결 RST.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Priority 잘못 설정&lt;/strong&gt; = 일부 클라이언트가 &lt;em&gt;priority frame 무시&lt;/em&gt;. RFC 9218 의 &lt;em&gt;priority hints&lt;/em&gt; 가 더 신뢰.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PUSH 의존 코드&lt;/strong&gt; = 위 server push 폐기 때문에 &lt;em&gt;fallback&lt;/em&gt; 필수.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[HTTP/1.1]] (이전 버전)&lt;/li&gt;
&lt;li&gt;[[HTTP/3]] (QUIC 위)&lt;/li&gt;
&lt;li&gt;[[hpack]] (헤더 압축 깊게)&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking]] (TCP 레벨 HoL)&lt;/li&gt;
&lt;li&gt;[[TCP]], [[TLS]]&lt;/li&gt;
&lt;li&gt;[[gRPC]] (HTTP/2 위)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>http</category><category>network</category><category>backend</category><category>protocol</category><category>multiplexing</category><author>koa (김신건)</author></item><item><title>[Network] HTTP/3: QUIC 위의 HTTP, 0-RTT, connection migration</title><link>https://shinkeonkim.com/wiki/network/http-3/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/http-3/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP/3&lt;/strong&gt; (2022, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9114&quot;&gt;RFC 9114&lt;/a&gt;) 는 HTTP 의 &lt;em&gt;전송 레이어를 TCP → QUIC&lt;/em&gt; 으로 교체한 버전. &lt;em&gt;UDP 위에 QUIC, QUIC 위에 HTTP&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;핵심 동기: &lt;strong&gt;TCP HoL blocking 해결&lt;/strong&gt; + &lt;strong&gt;0-RTT 핸드셰이크&lt;/strong&gt; + &lt;strong&gt;connection migration&lt;/strong&gt; (IP 변경 시 연결 유지).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    HTTP[HTTP semantics] --&gt; QUIC[QUIC&amp;#x3C;br/&gt;multi-streams + crypto + congestion]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    QUIC --&gt; UDP[UDP]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; IP[IP]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;quic-의-핵심-1분-요약&quot;&gt;QUIC 의 핵심 (1분 요약)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;특성&lt;/th&gt;&lt;th&gt;TCP+TLS+HTTP/2&lt;/th&gt;&lt;th&gt;QUIC+HTTP/3&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;핸드셰이크&lt;/td&gt;&lt;td&gt;3-way + TLS = 2-3 RTT&lt;/td&gt;&lt;td&gt;&lt;em&gt;1 RTT&lt;/em&gt; (또는 0-RTT 재방문)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HoL Blocking&lt;/td&gt;&lt;td&gt;TCP 레벨 발생&lt;/td&gt;&lt;td&gt;&lt;em&gt;stream 별 독립&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Connection migration&lt;/td&gt;&lt;td&gt;불가&lt;/td&gt;&lt;td&gt;&lt;em&gt;가능&lt;/em&gt; (NAT 변경, WiFi → 4G)&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;em&gt;필수&lt;/em&gt; (TLS 1.3 통합)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;패킷 손실 영향&lt;/td&gt;&lt;td&gt;모든 stream 대기&lt;/td&gt;&lt;td&gt;&lt;em&gt;해당 stream 만&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;구현 위치&lt;/td&gt;&lt;td&gt;OS (TCP) + 라이브러리 (TLS)&lt;/td&gt;&lt;td&gt;&lt;em&gt;전부 user-space&lt;/em&gt; (배포 빠름)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;handshake-0-rtt-의-마법&quot;&gt;Handshake: 0-RTT 의 마법&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    rect rgb(220,240,220)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 첫 방문 (1-RTT)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: Initial (TLS ClientHello + crypto frames)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: Initial + Handshake (server params + cert)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: Handshake (finish) + HTTP request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: HTTP response&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    rect rgb(255,235,220)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 재방문 (0-RTT)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: 0-RTT (HTTP request + cached params)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: HTTP response (즉시)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;em&gt;0-RTT 데이터는 replay 공격에 취약&lt;/em&gt;. &lt;em&gt;비멱등 요청&lt;/em&gt; (POST 결제) 은 &lt;em&gt;반드시 1-RTT 후 처리&lt;/em&gt;. GET / HEAD 같은 &lt;em&gt;안전한 method&lt;/em&gt; 만 0-RTT 권장.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tcp-hol-vs-quic-stream-독립&quot;&gt;TCP HoL vs QUIC stream 독립&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph T[&quot;TCP (HTTP/2)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TS[&quot;stream 1, 3, 5 가&amp;#x3C;br/&gt;같은 TCP 시퀀스 안&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket1[패킷 1: 일부 stream 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket2[패킷 2: 일부 stream 3, *손실!*]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket3[패킷 3: 일부 stream 5]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TS --&gt; TPacket1 &amp;#x26; TPacket2 &amp;#x26; TPacket3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket2 -.-&gt;|&quot;모든 stream 대기&quot;| Block1[&quot;모든 stream 대기&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph Q[&quot;QUIC (HTTP/3)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QS[&quot;stream 1, 3, 5 가&amp;#x3C;br/&gt;각자 시퀀스&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket1[패킷 1: stream 1 ✓]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket2[패킷 2: stream 3, *손실*]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket3[패킷 3: stream 5 ✓]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QS --&gt; QPacket1 &amp;#x26; QPacket2 &amp;#x26; QPacket3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket2 -.-&gt;|stream 3 만 대기| Block2[&quot;stream 1, 5 계속&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;connection-migration&quot;&gt;Connection Migration&lt;/h2&gt;
&lt;p&gt;WiFi → 4G 전환, NAT 재할당 시 &lt;em&gt;연결 유지&lt;/em&gt;. &lt;em&gt;Connection ID&lt;/em&gt; 가 &lt;em&gt;src IP/port 와 무관&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Server: WiFi IP 192.168.1.10:54321 (conn_id=XYZ)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server--&gt;&gt;Client: 응답 (conn_id=XYZ)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Client: 지하철 진입, 4G 전환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Server: 4G IP 10.0.0.5:65432 (conn_id=XYZ)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server--&gt;&gt;Client: 응답 (같은 connection 이어감)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Client,Server: TLS / 세션 유지, 처음부터 다시 안 함&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;qpack-헤더-압축&quot;&gt;QPACK 헤더 압축&lt;/h2&gt;
&lt;p&gt;HPACK 의 &lt;em&gt;순서 의존성 문제&lt;/em&gt; 를 해결한 변형. &lt;em&gt;encoder/decoder stream 분리&lt;/em&gt;.&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HPACK&lt;/th&gt;&lt;th&gt;QPACK&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;헤더가 &lt;em&gt;정확한 순서&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;stream 독립 디코딩&lt;/em&gt; 가능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;인덱스 갱신 → 모든 stream 영향&lt;/td&gt;&lt;td&gt;별도 stream 으로 동기화&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;도입-현황-2026&quot;&gt;도입 현황 (2026)&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&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;Cloudflare / Fastly / Akamai (CDN)&lt;/td&gt;&lt;td&gt;&lt;em&gt;기본 활성&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;모바일 앱 (gRPC, 자체 클라이언트)&lt;/td&gt;&lt;td&gt;빠르게 증가&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;일반 웹 사이트 (브라우저 트래픽)&lt;/td&gt;&lt;td&gt;약 35%+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;기업 내부 (방화벽 UDP 차단)&lt;/td&gt;&lt;td&gt;더딤&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;em&gt;방화벽이 UDP 를 차단&lt;/em&gt; 하면 HTTP/3 fallback → HTTP/2 (TCP). 도입의 가장 큰 &lt;em&gt;현실 장벽&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;방화벽 / 미들박스의 UDP 차단&lt;/strong&gt; = 사내망에서 H3 가 안 되는 가장 흔한 이유.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0-RTT 의 replay 위험&lt;/strong&gt; = POST / DELETE 등 비멱등은 0-RTT 거절.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection ID 가 &lt;em&gt;식별자처럼&lt;/em&gt; 쓰임&lt;/strong&gt; = 추적 / privacy 우려. RFC 가 &lt;em&gt;주기적 변경&lt;/em&gt; 권장.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;부하 분산기의 &lt;em&gt;UDP 5-tuple 해싱&lt;/em&gt;&lt;/strong&gt; = NAT 변경 시 &lt;em&gt;다른 백엔드&lt;/em&gt; 로. &lt;em&gt;connection ID 기반 라우팅&lt;/em&gt; 필요.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[HTTP/2]] (전 단계)&lt;/li&gt;
&lt;li&gt;[[QUIC]] (전송 레이어)&lt;/li&gt;
&lt;li&gt;[[UDP]]&lt;/li&gt;
&lt;li&gt;[[TLS]] (QUIC 내부)&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking]]&lt;/li&gt;
&lt;li&gt;[[Load Balancer]] (UDP / QUIC LB)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>http</category><category>network</category><category>backend</category><category>protocol</category><category>quic</category><category>udp</category><author>koa (김신건)</author></item><item><title>[Network] Load Balancer: L4 vs L7, 알고리즘, sticky session</title><link>https://shinkeonkim.com/wiki/network/load-balancer/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/load-balancer/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Load Balancer&lt;/strong&gt; 는 &lt;em&gt;트래픽을 여러 백엔드로 분산&lt;/em&gt; 하는 인프라. &lt;em&gt;수평 확장 + 가용성 + 무중단 배포&lt;/em&gt; 의 토대.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;l4-vs-l7&quot;&gt;L4 vs L7&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;구분&lt;/th&gt;&lt;th&gt;L4 (Transport)&lt;/th&gt;&lt;th&gt;L7 (Application)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;본 계층&lt;/td&gt;&lt;td&gt;TCP/UDP&lt;/td&gt;&lt;td&gt;HTTP/gRPC/WebSocket&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;보는 정보&lt;/td&gt;&lt;td&gt;IP + port&lt;/td&gt;&lt;td&gt;URL, 헤더, 쿠키, body&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;처리 비용&lt;/td&gt;&lt;td&gt;가벼움&lt;/td&gt;&lt;td&gt;무거움 (parse)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;기능&lt;/td&gt;&lt;td&gt;단순 분산&lt;/td&gt;&lt;td&gt;path/header routing, rewrite, auth, rate limit&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TLS 종료&lt;/td&gt;&lt;td&gt;옵션 (passthrough 가능)&lt;/td&gt;&lt;td&gt;보통 종료 후 분배&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;예&lt;/td&gt;&lt;td&gt;AWS NLB, HAProxy TCP, IPVS&lt;/td&gt;&lt;td&gt;AWS ALB, nginx, Envoy, Traefik&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph L4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C1[Client] --&gt;|TCP| LB4[L4 LB]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        LB4 --&gt;|TCP forward| B1[Backend 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        LB4 --&gt;|TCP forward| B2[Backend 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph L7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C2[Client] --&gt;|HTTP| LB7[L7 LB]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        LB7 --&gt;|URL = /api| BA[API service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        LB7 --&gt;|URL = /static| BS[Static service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;알고리즘&quot;&gt;알고리즘&lt;/h2&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&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;strong&gt;Round Robin&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;순서대로&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Weighted RR&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;가중치 비율&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Least Connections&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;현재 연결 가장 적은 백엔드&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Least Response Time&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;평균 응답 시간 가장 짧은&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;IP Hash&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;클라이언트 IP 해시 → 같은 IP 는 같은 백엔드&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Consistent Hashing&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;hash 링 → 노드 추가/제거 시 &lt;em&gt;최소 재분배&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Random (P2C)&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;무작위 2개 중 &lt;em&gt;덜 바쁜&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;power-of-two-choices-p2c&quot;&gt;Power of Two Choices (P2C)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R[Request] --&gt;|random pick 2| P[Pool of N]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    P --&gt; S1[Server A: load=5]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    P --&gt; S2[Server B: load=8]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    P --&gt;|선택| S1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;em&gt;완전 random&lt;/em&gt; 보다 좋고 &lt;em&gt;least-conn&lt;/em&gt; 보다 &lt;em&gt;cheap&lt;/em&gt;. Netflix, Twitter 등 &lt;em&gt;대규모 서비스의 기본&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;consistent-hashing&quot;&gt;Consistent Hashing&lt;/h3&gt;
&lt;p&gt;노드 추가/제거 시 &lt;em&gt;N/M 만 재분배&lt;/em&gt; (vs 일반 hash 의 &lt;em&gt;전체 재분배&lt;/em&gt;).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Ring((&quot;Hash Ring&amp;#x3C;br/&gt;0 → 2^32&quot;)) --&gt; N1[Node A&amp;#x3C;br/&gt;at hash 100]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Ring --&gt; N2[Node B&amp;#x3C;br/&gt;at hash 500]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Ring --&gt; N3[Node C&amp;#x3C;br/&gt;at hash 900]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Key1[key: hash 300] --&gt;|시계방향| N2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Key2[key: hash 700] --&gt;|시계방향| N3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Sticky session&lt;/em&gt;, &lt;em&gt;cache 라우팅&lt;/em&gt;, &lt;em&gt;DB sharding&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;virtual node&lt;/em&gt; (각 노드를 &lt;em&gt;수십 개 ring 점&lt;/em&gt;) 로 &lt;em&gt;분포 균등화&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;health-check&quot;&gt;Health Check&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loop 30초마다&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        LB-&gt;&gt;Backend: GET /health&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        Backend--&gt;&gt;LB: 200 OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Backend: 다운&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB-&gt;&gt;Backend: GET /health&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Backend-xLB: timeout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over LB: 3회 연속 실패 → 풀에서 제외&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Active&lt;/em&gt;: LB 가 주기 ping.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Passive&lt;/em&gt;: 실제 요청 실패율로 판단.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Composite&lt;/em&gt;: 둘 다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
Health check 가 &lt;em&gt;너무 단순&lt;/em&gt; (예: 200 OK 만) 하면 &lt;em&gt;DB 다운인데 healthy&lt;/em&gt; 로 판정. &lt;em&gt;deep health&lt;/em&gt; (DB, cache 까지 ping) vs &lt;em&gt;shallow health&lt;/em&gt; (애플리케이션만) 의 &lt;em&gt;분리&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;sticky-session&quot;&gt;Sticky Session&lt;/h2&gt;

























&lt;table&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;Cookie&lt;/td&gt;&lt;td&gt;LB 가 &lt;em&gt;쿠키 박음&lt;/em&gt;&lt;/td&gt;&lt;td&gt;쿠키 위변조 / 제거 시 깨짐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IP Hash&lt;/td&gt;&lt;td&gt;클라이언트 IP 기준&lt;/td&gt;&lt;td&gt;NAT 뒤 사용자가 &lt;em&gt;같은 노드 몰림&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Application&lt;/td&gt;&lt;td&gt;앱이 &lt;em&gt;자기 ID&lt;/em&gt; 응답 헤더로&lt;/td&gt;&lt;td&gt;구현 복잡&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;자세한 건 [[Sticky Session]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;보안--옵저버빌리티&quot;&gt;보안 / 옵저버빌리티&lt;/h2&gt;
&lt;p&gt;L7 LB 의 추가 능력:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TLS termination&lt;/strong&gt; (인증서 관리)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WAF&lt;/strong&gt; (SQLi, XSS 차단)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rate limiting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request/response 로깅&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Header rewrite, redirect&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path-based routing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A/B testing, canary&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;실전-lb-선택&quot;&gt;실전 LB 선택&lt;/h2&gt;























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;도구&lt;/th&gt;&lt;th&gt;L4/L7&lt;/th&gt;&lt;th&gt;사용처&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;AWS ALB&lt;/td&gt;&lt;td&gt;L7&lt;/td&gt;&lt;td&gt;AWS 표준 L7&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS NLB&lt;/td&gt;&lt;td&gt;L4&lt;/td&gt;&lt;td&gt;초저지연, gRPC, WebSocket&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS CLB&lt;/td&gt;&lt;td&gt;L4/L7&lt;/td&gt;&lt;td&gt;(legacy)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nginx&lt;/td&gt;&lt;td&gt;L7 (L4 옵션)&lt;/td&gt;&lt;td&gt;self-host 가장 흔함&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HAProxy&lt;/td&gt;&lt;td&gt;L4/L7&lt;/td&gt;&lt;td&gt;고성능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Envoy&lt;/td&gt;&lt;td&gt;L7&lt;/td&gt;&lt;td&gt;service mesh, gRPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Traefik&lt;/td&gt;&lt;td&gt;L7&lt;/td&gt;&lt;td&gt;K8s 친화&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IPVS&lt;/td&gt;&lt;td&gt;L4&lt;/td&gt;&lt;td&gt;커널 모듈, 극저지연&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Caddy&lt;/td&gt;&lt;td&gt;L7&lt;/td&gt;&lt;td&gt;자동 TLS&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;WebSocket 의 &lt;em&gt;idle timeout&lt;/em&gt;&lt;/strong&gt; = ALB 60s default 가 silent close. &lt;em&gt;ping/pong&lt;/em&gt; 으로.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gRPC 의 &lt;em&gt;long-lived HTTP/2 연결&lt;/em&gt;&lt;/strong&gt; = L4 LB 의 &lt;em&gt;connection 단위 분산&lt;/em&gt; 때문에 &lt;em&gt;불균등 분포&lt;/em&gt;. &lt;em&gt;gRPC 자체 load balancing&lt;/em&gt; (Envoy, GRPC client-side LB) 필요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health check 의 &lt;em&gt;서버 자살&lt;/em&gt;&lt;/strong&gt; = HC 자체가 부하 → 서버 다운. &lt;em&gt;분리된 endpoint&lt;/em&gt; + &lt;em&gt;경량 응답&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sticky session + auto-scaling&lt;/strong&gt; = scale-in 시 &lt;em&gt;stuck&lt;/em&gt; 사용자 세션 손실. &lt;em&gt;세션을 &lt;em&gt;외부 store&lt;/em&gt; (Redis)&lt;/em&gt; 로 옮기는 게 정통.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[Sticky Session]]&lt;/li&gt;
&lt;li&gt;[[HTTP/2]] (long-lived 연결과 LB)&lt;/li&gt;
&lt;li&gt;[[Redis Pub Sub vs Streams]] (분산 fan-out)&lt;/li&gt;
&lt;li&gt;[[Kubernetes Service]] (K8s LB)&lt;/li&gt;
&lt;li&gt;[[AWS ALB NLB]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>load-balancer</category><category>network</category><category>devops</category><category>infrastructure</category><author>koa (김신건)</author></item><item><title>[Network] OSI 7 Layer: 각 계층의 의미와 실전 매핑</title><link>https://shinkeonkim.com/wiki/network/osi-7-layer/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/osi-7-layer/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;OSI 7 Layer 모델&lt;/strong&gt; 은 &lt;em&gt;네트워크 통신을 7 계층으로 추상화&lt;/em&gt; 한 ITU-T 표준 (1984). 실제 인터넷은 &lt;em&gt;TCP/IP 4-5 계층&lt;/em&gt; 이지만, OSI 의 &lt;em&gt;개념적 분리&lt;/em&gt; 가 &lt;em&gt;디버깅 / 도구 분류&lt;/em&gt; 의 토대.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;7-layer-매트릭스&quot;&gt;7 Layer 매트릭스&lt;/h2&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Layer&lt;/th&gt;&lt;th&gt;이름&lt;/th&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;strong&gt;L7&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Application&lt;/td&gt;&lt;td&gt;message&lt;/td&gt;&lt;td&gt;HTTP, gRPC, DNS, SMTP, MQTT, SSH&lt;/td&gt;&lt;td&gt;curl, postman&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L6&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Presentation&lt;/td&gt;&lt;td&gt;data&lt;/td&gt;&lt;td&gt;TLS, JSON, Protobuf, MIME, gzip&lt;/td&gt;&lt;td&gt;openssl&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L5&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Session&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;NetBIOS, RPC, &lt;em&gt;대부분 통합&lt;/em&gt;&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L4&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Transport&lt;/td&gt;&lt;td&gt;segment&lt;/td&gt;&lt;td&gt;TCP, UDP, QUIC&lt;/td&gt;&lt;td&gt;netstat, ss, tcpdump&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L3&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Network&lt;/td&gt;&lt;td&gt;packet&lt;/td&gt;&lt;td&gt;IP, ICMP, BGP, OSPF&lt;/td&gt;&lt;td&gt;traceroute, ping&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L2&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Data Link&lt;/td&gt;&lt;td&gt;frame&lt;/td&gt;&lt;td&gt;Ethernet, WiFi, ARP, MAC&lt;/td&gt;&lt;td&gt;arp, wireshark&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L1&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Physical&lt;/td&gt;&lt;td&gt;bit&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;h2 id=&quot;tcpip-와의-매핑&quot;&gt;TCP/IP 와의 매핑&lt;/h2&gt;
&lt;p&gt;실제 인터넷은 &lt;em&gt;4-5 계층&lt;/em&gt; 의 TCP/IP. OSI 의 5/6/7 이 &lt;em&gt;Application&lt;/em&gt; 으로 통합:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph OSI[&quot;OSI 7 Layer&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O7[L7 Application]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O6[L6 Presentation]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O5[L5 Session]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O4[L4 Transport]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O3[L3 Network]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O2[L2 Data Link]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        O1[L1 Physical]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph TCPIP[&quot;TCP/IP 4 Layer&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T4[Application]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T3[Transport]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T2[Internet]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T1[Link]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O7 --&gt; T4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O6 --&gt; T4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O5 --&gt; T4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O4 --&gt; T3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O3 --&gt; T2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O2 --&gt; T1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    O1 --&gt; T1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;각-계층-깊게&quot;&gt;각 계층 깊게&lt;/h2&gt;
&lt;h3 id=&quot;l7-application&quot;&gt;L7 Application&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;사용자 의도와 가장 가까운 계층&lt;/em&gt;. HTTP, gRPC, DNS, SMTP, SSH.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP: &lt;code&gt;GET /api/users HTTP/1.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;DNS: &lt;code&gt;A example.com → 93.184.216.34&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;gRPC: &lt;code&gt;/shop.Shop/GetItem&lt;/code&gt; over HTTP/2&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;자세한 건 [[HTTP/1.1]], [[HTTP/2]], [[HTTP/3]], [[gRPC]], [[network-dns]].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;l6-presentation&quot;&gt;L6 Presentation&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;데이터 표현 변환&lt;/em&gt;. 직렬화, 압축, 암호화.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLS (실제로는 L4-L7 사이)&lt;/li&gt;
&lt;li&gt;JSON, Protobuf, MessagePack&lt;/li&gt;
&lt;li&gt;gzip, deflate&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;l5-session&quot;&gt;L5 Session&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;세션 관리&lt;/em&gt;. 현대에서는 &lt;em&gt;L7 의 일부&lt;/em&gt; 또는 &lt;em&gt;애플리케이션 자체&lt;/em&gt; 가 담당. (HTTP cookie, JWT 등)&lt;/p&gt;
&lt;h3 id=&quot;l4-transport&quot;&gt;L4 Transport&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;end-to-end 신뢰성 + flow control&lt;/em&gt;.&lt;/p&gt;








































&lt;table&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;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;TCP&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;HTTP, FTP, SSH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UDP&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;DNS, VoIP, 게임, QUIC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;QUIC&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O (stream 별)&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;HTTP/3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SCTP&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;옵션&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;WebRTC DataChannel&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;자세한 건 [[TCP]], [[UDP]], [[QUIC]].&lt;/p&gt;
&lt;h3 id=&quot;l3-network&quot;&gt;L3 Network&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;IP 라우팅&lt;/em&gt;. &lt;em&gt;어떤 경로로 패킷이 갈지&lt;/em&gt;. 라우터의 영역.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IPv4, IPv6&lt;/li&gt;
&lt;li&gt;ICMP (ping, traceroute)&lt;/li&gt;
&lt;li&gt;BGP (인터넷 라우팅 프로토콜)&lt;/li&gt;
&lt;li&gt;OSPF, IS-IS (내부 라우팅)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자세한 건 [[IP]].&lt;/p&gt;
&lt;h3 id=&quot;l2-data-link&quot;&gt;L2 Data Link&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;같은 LAN 안&lt;/em&gt; 의 &lt;em&gt;MAC 주소&lt;/em&gt; 기반 통신. Switch, WiFi AP.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ethernet (IEEE 802.3)&lt;/li&gt;
&lt;li&gt;WiFi (IEEE 802.11)&lt;/li&gt;
&lt;li&gt;ARP (IP → MAC 매핑)&lt;/li&gt;
&lt;li&gt;VLAN (Virtual LAN)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;l1-physical&quot;&gt;L1 Physical&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;실제 전기 / 광 신호&lt;/em&gt;. 케이블, 무선, 광섬유.&lt;/p&gt;
&lt;h2 id=&quot;패킷의-캡슐화&quot;&gt;패킷의 캡슐화&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L7Msg[&quot;L7 HTTP: GET /api/x&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L7Msg --&gt;|L6/L5| L7Frame[&quot;+ TLS 암호화&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L7Frame --&gt;|L4 TCP| L4Frame[&quot;TCP header + payload&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L4Frame --&gt;|L3 IP| L3Frame[&quot;IP header + TCP segment&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L3Frame --&gt;|L2 Ethernet| L2Frame[&quot;Ethernet header + IP packet + FCS&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L2Frame --&gt;|L1| Bit[bits on wire]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 계층이 자기 &lt;em&gt;header&lt;/em&gt; 를 &lt;em&gt;덧붙임&lt;/em&gt;. 수신 측은 &lt;em&gt;역순으로 벗겨&lt;/em&gt; 올라간다.&lt;/p&gt;
&lt;h2 id=&quot;실전-어디서-무엇이-깨졌나&quot;&gt;실전: 어디서 무엇이 깨졌나?&lt;/h2&gt;
&lt;p&gt;문제 진단의 &lt;em&gt;층 별 도구&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Issue{문제 발생} --&gt; Q1{ping 가능?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|아니오| L3Layer[&quot;L3 문제&amp;#x3C;br/&gt;routing, firewall, DNS&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|예| Q2{telnet/nc port?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|아니오| L4Layer[&quot;L4 문제&amp;#x3C;br/&gt;port closed, firewall&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| Q3{TLS handshake OK?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|아니오| L6Layer[&quot;L6 문제&amp;#x3C;br/&gt;cert, SNI&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|예| Q4{HTTP 200?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|아니오| L7Layer[&quot;L7 문제&amp;#x3C;br/&gt;application, auth&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|예| AppLogic[&quot;애플리케이션 로직&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&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;code&gt;ping&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L3 (ICMP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;traceroute&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;nc / telnet host port&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;nmap&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;openssl s_client&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L6 (TLS)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;curl -v&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L7 (HTTP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;dig / nslookup&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L7 (DNS)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;tcpdump / wireshark&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L2-L7 (raw)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;“L7 문제”&lt;/em&gt; 라고 단정&lt;/strong&gt; = 실제는 L4 timeout 일 수 있다. &lt;em&gt;층별로 위에서 아래로 분리 진단&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS 가 &lt;em&gt;L4 라는 오해&lt;/em&gt;&lt;/strong&gt; = TLS 는 &lt;em&gt;L5/L6 사이&lt;/em&gt;. L4 (TCP/UDP) 위에서 동작.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS 인증서 문제를 &lt;em&gt;방화벽 탓&lt;/em&gt;&lt;/strong&gt; = &lt;code&gt;openssl s_client&lt;/code&gt; 로 &lt;em&gt;handshake&lt;/em&gt; 만 따로 확인.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS 가 &lt;em&gt;L7 인지 L3 인지 헷갈림&lt;/em&gt;&lt;/strong&gt; = &lt;em&gt;L7 (애플리케이션)&lt;/em&gt;. UDP/TCP 53 위에서.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[TCP]], [[UDP]], [[QUIC]]&lt;/li&gt;
&lt;li&gt;[[IP]], [[TLS]]&lt;/li&gt;
&lt;li&gt;[[HTTP/1.1]], [[HTTP/2]], [[HTTP/3]]&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting]]&lt;/li&gt;
&lt;li&gt;[[network-dns]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>osi</category><category>network</category><category>model</category><category>fundamentals</category><author>koa (김신건)</author></item><item><title>[Network] Realtime 전송 비교: Polling / SSE / WebSocket / WebRTC</title><link>https://shinkeonkim.com/wiki/network/realtime-comparison/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/realtime-comparison/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;실시간 (서버 → 클라이언트) 데이터 전송&lt;/em&gt; 의 7가지 패턴 비교.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;결정-트리&quot;&gt;결정 트리&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1{양방향 필요?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|아니오| Q2{영속 연결 OK?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|예| Q3{브라우저 / 네이티브?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| SSE[SSE]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|아니오| Q4{레거시 호환?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|예| LP[Long Polling]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|아니오| Poll[Polling]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|브라우저| WS[WebSocket]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|P2P 미디어| RTC[WebRTC]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;7가지-비교&quot;&gt;7가지 비교&lt;/h2&gt;





























































&lt;table&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;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;strong&gt;Short Polling&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;C → S&lt;/td&gt;&lt;td&gt;짧음&lt;/td&gt;&lt;td&gt;HTTP&lt;/td&gt;&lt;td&gt;단순 / legacy&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Long Polling&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;C → S&lt;/td&gt;&lt;td&gt;짧음 (hold)&lt;/td&gt;&lt;td&gt;HTTP&lt;/td&gt;&lt;td&gt;legacy fallback&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;HTTP/2 server push&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;S → C&lt;/td&gt;&lt;td&gt;(폐기)&lt;/td&gt;&lt;td&gt;HTTP/2&lt;/td&gt;&lt;td&gt;(deprecated)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SSE&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;S → C&lt;/td&gt;&lt;td&gt;영속&lt;/td&gt;&lt;td&gt;HTTP&lt;/td&gt;&lt;td&gt;알림, 라이브 피드&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;양방향&lt;/td&gt;&lt;td&gt;영속&lt;/td&gt;&lt;td&gt;RFC 6455&lt;/td&gt;&lt;td&gt;채팅, 게임&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;WebTransport&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;양방향 (datagrams)&lt;/td&gt;&lt;td&gt;영속&lt;/td&gt;&lt;td&gt;HTTP/3 + QUIC&lt;/td&gt;&lt;td&gt;차세대 (실험적)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;WebRTC DataChannel&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;양방향 (P2P)&lt;/td&gt;&lt;td&gt;영속&lt;/td&gt;&lt;td&gt;DTLS + SCTP&lt;/td&gt;&lt;td&gt;게임 P2P, 파일&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;polling&quot;&gt;Polling&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loop 5초마다&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C-&gt;&gt;S: GET /new&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S--&gt;&gt;C: 200 [] (변화 없음)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 5초 후&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /new&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 [msg1]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;가장 단순. 가장 &lt;em&gt;비효율적&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;평균 지연 = &lt;em&gt;polling 간격 / 2&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;메시지 없어도 &lt;em&gt;매 요청 발생&lt;/em&gt; → 서버 부담.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
자주 polling = 서버 부담. 드물게 polling = 지연 큼. &lt;em&gt;둘 다 안 좋음&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;long-polling&quot;&gt;Long Polling&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /new (대기 30s)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: 이벤트 없음, hold&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: 메시지 도착!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 [msg1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /new (즉시 다음 요청)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: timeout 30s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 [] (timeout)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /new (재시도)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;적은 빈도, 낮은 지연&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;legacy 환경&lt;/em&gt; (옛 IE, 일부 프록시) 의 fallback.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;connection 점유&lt;/em&gt; 가 polling 보다 길다 → 서버 connection pool 압박.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sse-단방향-영속&quot;&gt;SSE (단방향 영속)&lt;/h2&gt;
&lt;p&gt;자세한 건 [[SSE]] 참고.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /stream&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 (영속)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: data: msg1\n\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: data: msg2\n\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C: 끊김 시 자동 재연결&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: data: msg3\n\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;서버 → 클라이언트 일방향&lt;/em&gt; 충분하면 &lt;em&gt;제일 단순&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;자동 재연결 + Last-Event-ID 재개.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;프록시 / 방화벽 친화&lt;/em&gt; (HTTP).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;websocket-양방향-영속&quot;&gt;WebSocket (양방향 영속)&lt;/h2&gt;
&lt;p&gt;자세한 건 [[WebSocket]] 참고.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: HTTP Upgrade (handshake)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 101 Switching Protocols&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C--&gt;&gt;S: WS frame&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: WS frame&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;양방향 + 영속&lt;/em&gt;. 채팅 / 게임 / 협업 / 트레이딩.&lt;/li&gt;
&lt;li&gt;HTTP 위에서 시작하지만 &lt;em&gt;별도 프레임 프로토콜&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;webtransport-실험적-차세대&quot;&gt;WebTransport (실험적, 차세대)&lt;/h2&gt;
&lt;p&gt;HTTP/3 (QUIC) 위 양방향 + &lt;em&gt;unreliable datagram&lt;/em&gt; + &lt;em&gt;bidirectional streams&lt;/em&gt;. &lt;em&gt;WebSocket + UDP datagram&lt;/em&gt; 의 결합.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2026 시점 &lt;em&gt;모던 브라우저 지원 진입&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;게임 + 라이브 영상 + AR/VR.&lt;/li&gt;
&lt;li&gt;아직 &lt;em&gt;대중적 도입 전&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;webrtc-datachannel-p2p&quot;&gt;WebRTC DataChannel (P2P)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트 &lt;em&gt;↔&lt;/em&gt; 클라이언트 &lt;em&gt;직접&lt;/em&gt;. 서버는 &lt;em&gt;signaling 만&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;DTLS + SCTP 위.&lt;/li&gt;
&lt;li&gt;P2P 게임, 파일 전송, 영상통화 보조.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;NAT traversal&lt;/em&gt; (STUN/TURN) 복잡도.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;처리량-vs-지연-vs-복잡도&quot;&gt;처리량 vs 지연 vs 복잡도&lt;/h2&gt;
&lt;!-- mdx-component: ChartJs --&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[SSE]], [[WebSocket]]&lt;/li&gt;
&lt;li&gt;[[HTTP/2]], [[HTTP/3]]&lt;/li&gt;
&lt;li&gt;[[Sticky Session]]&lt;/li&gt;
&lt;li&gt;[[Redis Pub Sub vs Streams]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>realtime</category><category>network</category><category>websocket</category><category>sse</category><category>polling</category><category>webrtc</category><category>backend</category><author>koa (김신건)</author></item><item><title>[Network] SSE (Server-Sent Events): 단방향 영속 스트림</title><link>https://shinkeonkim.com/wiki/network/sse-server-sent-events/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/sse-server-sent-events/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt; 는 &lt;em&gt;HTTP 위 단방향 (서버 → 클라이언트) 영속 스트림&lt;/em&gt;. &lt;code&gt;text/event-stream&lt;/code&gt; MIME + 클라이언트의 &lt;code&gt;EventSource&lt;/code&gt; API.&lt;/p&gt;
&lt;p&gt;장점: &lt;em&gt;HTTP/1.1 위에서 동작&lt;/em&gt;, &lt;em&gt;자동 재연결&lt;/em&gt;, &lt;em&gt;브라우저 native&lt;/em&gt;, &lt;em&gt;프록시/방화벽 친화&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;메시지-포맷&quot;&gt;메시지 포맷&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data: hello\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data: line 1\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data: line 2\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;event: ping\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data: {&quot;t&quot;:1234}\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;id: 42\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;data: msg\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;retry: 5000\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&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;code&gt;data:&lt;/code&gt;&lt;/td&gt;&lt;td&gt;본문 (여러 줄 가능, JOIN 으로 합쳐짐)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;event:&lt;/code&gt;&lt;/td&gt;&lt;td&gt;커스텀 이벤트 이름 (없으면 &lt;code&gt;message&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;id:&lt;/code&gt;&lt;/td&gt;&lt;td&gt;메시지 ID. 재연결 시 &lt;code&gt;Last-Event-ID&lt;/code&gt; 헤더로 보냄&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;retry:&lt;/code&gt;&lt;/td&gt;&lt;td&gt;재연결 간격 (ms)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;\n\n&lt;/code&gt;&lt;/td&gt;&lt;td&gt;메시지 끝 구분&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;서버-응답&quot;&gt;서버 응답&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; text/event-stream&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; no-cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {&quot;price&quot;:42000}\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; {&quot;price&quot;:42100}\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;클라이언트-브라우저&quot;&gt;클라이언트 (브라우저)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; es&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; EventSource&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/prices/stream&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;es.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onopen&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;open&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;es.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onmessage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;price&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, data.price);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;es.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;ping&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;ping&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, e.data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;es.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onerror&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 자동 재연결 (브라우저가 처리)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, e);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 명시 종료&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;es.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;동작-흐름&quot;&gt;동작 흐름&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /stream&amp;#x3C;br/&gt;Accept: text/event-stream&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 OK&amp;#x3C;br/&gt;Content-Type: text/event-stream&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loop 연결 유지&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S--&gt;&gt;C: data: ...\n\n&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C: 네트워크 끊김&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C: 자동 재시도 (3s 기본)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /stream&amp;#x3C;br/&gt;Last-Event-ID: 42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 200 OK (id=43 부터 재개)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;em&gt;서버는 &lt;code&gt;Last-Event-ID&lt;/code&gt; 헤더를 검증하고 &lt;em&gt;그 ID 이후 이벤트&lt;/em&gt; 만 보내야&lt;/em&gt; 완벽한 재개. 보통 이벤트 ID 를 &lt;em&gt;DB 또는 Redis Stream offset&lt;/em&gt; 으로.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;백엔드-구현-node&quot;&gt;백엔드 구현 (Node)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/stream&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;res&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  res.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;writeHead&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;Content-Type&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;text/event-stream&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;Cache-Control&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;no-cache&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;Connection&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;keep-alive&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; lastId&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; req.headers[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;last-event-id&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; startFrom&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; lastId &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;lastId &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; startFrom;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    res.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`id: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    res.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`data: ${&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;JSON&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;({ t: &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;() })&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\n\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  req.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;close&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; clearInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(t));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;sse-vs-websocket-vs-polling&quot;&gt;SSE vs WebSocket vs Polling&lt;/h2&gt;





















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;SSE&lt;/th&gt;&lt;th&gt;WebSocket&lt;/th&gt;&lt;th&gt;Long Polling&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;/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;HTTP/1.1 (또는 H2/H3)&lt;/td&gt;&lt;td&gt;자체 (RFC 6455)&lt;/td&gt;&lt;td&gt;HTTP&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;자동 재연결&lt;/td&gt;&lt;td&gt;&lt;em&gt;내장&lt;/em&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;em&gt;텍스트만&lt;/em&gt; (UTF-8)&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;em&gt;최고&lt;/em&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;모던 OK&lt;/td&gt;&lt;td&gt;모던 OK&lt;/td&gt;&lt;td&gt;모두&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;서버 자원 (idle 연결)&lt;/td&gt;&lt;td&gt;가벼움&lt;/td&gt;&lt;td&gt;가벼움&lt;/td&gt;&lt;td&gt;&lt;em&gt;무거움&lt;/em&gt; (반복 연결)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;서버 → 클라이언트 일방향 푸시&lt;/em&gt; (알림, 라이브 피드, 진행률) 면 &lt;em&gt;SSE 가 거의 항상 더 단순&lt;/em&gt;. 양방향이 &lt;em&gt;진짜 필요할 때만&lt;/em&gt; WebSocket.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;http2-위-sse&quot;&gt;HTTP/2 위 SSE&lt;/h2&gt;
&lt;p&gt;HTTP/2 에서는 &lt;em&gt;multiplexing&lt;/em&gt; 으로 &lt;em&gt;여러 SSE stream + 일반 요청&lt;/em&gt; 한 연결에서. &lt;em&gt;HTTP/1.1 의 6 connection limit&lt;/em&gt; 문제 해결.&lt;/p&gt;
&lt;h2 id=&quot;활용&quot;&gt;활용&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;알림 / 토스트&lt;/strong&gt;: 새 알림이 도착했을 때 push&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;라이브 대시보드&lt;/strong&gt;: 메트릭 실시간&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI Streaming (LLM 응답)&lt;/strong&gt;: OpenAI / Anthropic SDK 의 &lt;em&gt;streaming&lt;/em&gt; 이 SSE 위에서&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;빌드 / 배포 로그 tail&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주식 / 코인 시세&lt;/strong&gt; (단방향 충분)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;PROXY 의 &lt;em&gt;response buffering&lt;/em&gt;&lt;/strong&gt; = nginx 의 &lt;code&gt;proxy_buffering on&lt;/code&gt; 이 &lt;em&gt;모든 데이터를 모아 보냄&lt;/em&gt;. SSE 는 &lt;em&gt;즉시 flush&lt;/em&gt; 필요. &lt;code&gt;proxy_buffering off&lt;/code&gt; + &lt;code&gt;X-Accel-Buffering: no&lt;/code&gt; 헤더.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP/1.1 의 6 connection limit&lt;/strong&gt; = 한 도메인에 &lt;em&gt;6개 이상&lt;/em&gt; SSE 불가. HTTP/2 로 해결.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;EventSource&lt;/code&gt; 가 &lt;em&gt;재연결 시 자동&lt;/em&gt; 인데 &lt;em&gt;지수 백오프 없음&lt;/em&gt;&lt;/strong&gt; = 서버 과부하 위험. &lt;em&gt;서버에서 &lt;code&gt;retry:&lt;/code&gt; 헤더로 조정&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CORS preflight + EventSource&lt;/strong&gt; = EventSource 는 &lt;em&gt;기본 헤더만&lt;/em&gt; 지원. 커스텀 헤더 (Authorization) 가 필요하면 &lt;em&gt;URL 쿼리&lt;/em&gt; 또는 &lt;em&gt;쿠키&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[WebSocket]] (양방향)&lt;/li&gt;
&lt;li&gt;[[realtime-comparison]]&lt;/li&gt;
&lt;li&gt;[[HTTP/1.1]], [[HTTP/2]]&lt;/li&gt;
&lt;li&gt;[[Redis Pub Sub vs Streams]] (fan-out)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>sse</category><category>network</category><category>realtime</category><category>backend</category><category>streaming</category><author>koa (김신건)</author></item><item><title>[Network] WebSocket: HTTP upgrade, frame, ping/pong</title><link>https://shinkeonkim.com/wiki/network/websocket/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/websocket/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;WebSocket&lt;/strong&gt; (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6455&quot;&gt;RFC 6455&lt;/a&gt;) 은 &lt;em&gt;HTTP/1.1 upgrade 로 시작&lt;/em&gt; 하는 &lt;em&gt;양방향 영속 메시지 채널&lt;/em&gt;. 한 TCP 위에서 &lt;em&gt;서버 ↔ 클라이언트 임의 시점 push&lt;/em&gt; 가능.&lt;/p&gt;
&lt;p&gt;활용: 실시간 채팅, 트레이딩 시세, 게임, 알림, 공동 편집, 라이브 대시보드.&lt;/p&gt;
&lt;h2 id=&quot;handshake-http-upgrade&quot;&gt;Handshake: HTTP Upgrade&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /chat HTTP/1.1&amp;#x3C;br/&gt;Upgrade: websocket&amp;#x3C;br/&gt;Connection: Upgrade&amp;#x3C;br/&gt;Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==&amp;#x3C;br/&gt;Sec-WebSocket-Version: 13&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;C: 101 Switching Protocols&amp;#x3C;br/&gt;Upgrade: websocket&amp;#x3C;br/&gt;Connection: Upgrade&amp;#x3C;br/&gt;Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 이후 같은 TCP 위에서&amp;#x3C;br/&gt;WS frame 양방향 교환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: WS frame (text/binary)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;C: WS frame (서버 push)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Sec-WebSocket-Accept&lt;/code&gt; = base64(SHA-1(key + “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”)). &lt;em&gt;handshake 무결성 검증&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frame-구조&quot;&gt;Frame 구조&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 0                   1                   2                   3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-+-+-+-+-------+-+-------------+-------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|F|R|R|R| opcode|M| Payload len |    Extended payload length    |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|N|V|V|V|       |S|             |   (if payload len == 126/127) |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| |1|2|3|       |K|             |                               |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|     Extended payload length continued, if payload len == 127  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+ - - - - - - - - - - - - - - - +-------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|                               |Masking-key, if MASK set to 1  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------------------------------+-------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|    Masking-key (continued)    |          Payload Data         |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------------------------------- - - - - - - - - - - - - - - - +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;:                     Payload Data continued ...                :&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|                     Payload Data continued ...                |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;opcode&lt;/th&gt;&lt;th&gt;의미&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0x0&lt;/td&gt;&lt;td&gt;Continuation (다음 frame 이어짐)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x1&lt;/td&gt;&lt;td&gt;Text (UTF-8)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x2&lt;/td&gt;&lt;td&gt;Binary&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x8&lt;/td&gt;&lt;td&gt;Close&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0x9&lt;/td&gt;&lt;td&gt;Ping&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0xA&lt;/td&gt;&lt;td&gt;Pong&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;클라이언트 → 서버 frame 은 반드시 MASK&lt;/em&gt; (XOR with 4-byte key). cache poisoning 방지 목적. 서버 → 클라이언트 는 unmask.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;ping--pong-keep-alive&quot;&gt;Ping / Pong (keep-alive)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loop 30초마다&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S-&gt;&gt;C: PING (opcode 0x9)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C--&gt;&gt;S: PONG (opcode 0xA, payload 동일)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: 90초 PONG 없음 → close&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;em&gt;NAT timeout&lt;/em&gt; 또는 &lt;em&gt;프록시 idle close&lt;/em&gt; 를 방지. 보통 30~60초 간격. &lt;em&gt;NAT 일반 idle timeout 이 60s&lt;/em&gt; 이므로.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;subprotocol&quot;&gt;Subprotocol&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Sec-WebSocket-Protocol&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; chat, soap&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;서버가 &lt;em&gt;선택해서&lt;/em&gt; 응답 헤더에 같은 것 반환. 클라이언트가 &lt;em&gt;프로토콜 분기&lt;/em&gt; (예: GraphQL over WS, MQTT over WS).&lt;/p&gt;
&lt;h2 id=&quot;wss-websocket-secure&quot;&gt;WSS (WebSocket Secure)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wss://&lt;/code&gt; = WebSocket over TLS. 운영에서는 &lt;em&gt;거의 필수&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일부 ISP / 프록시가 &lt;em&gt;ws://&lt;/em&gt; 를 차단&lt;/li&gt;
&lt;li&gt;mixed content (https 페이지에서 ws://) 브라우저 거부&lt;/li&gt;
&lt;li&gt;&lt;em&gt;대부분의 WAF / LB&lt;/em&gt; 가 wss 에 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Idle timeout 사고&lt;/strong&gt; = ALB/nginx 의 60초 default 가 silent close. PING/PONG 간격 &amp;#x3C; idle timeout 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backpressure 부재&lt;/strong&gt; = 서버가 빠르게 send → 클라이언트 buffer 폭발. &lt;em&gt;application 레벨 flow control&lt;/em&gt; (ACK / credit) 필요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sticky session 필요&lt;/strong&gt; = 같은 사용자가 같은 노드. LB cookie 또는 IP hash. 또는 &lt;em&gt;connection ID + Redis pub/sub&lt;/em&gt; 으로 fan-out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CORS 가 WebSocket 에 &lt;em&gt;그대로&lt;/em&gt; 적용 안 됨&lt;/strong&gt; = &lt;code&gt;Origin&lt;/code&gt; 헤더는 &lt;em&gt;수동 검증&lt;/em&gt;. 미검증 시 CSRF.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;패턴-채팅-fan-out&quot;&gt;패턴: 채팅 fan-out&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C1[Client 1] --&gt;|WS| N1[Server Node A]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C2[Client 2] --&gt;|WS| N2[Server Node B]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C3[Client 3] --&gt;|WS| N1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N1 --&gt;|Redis PUB| Redis[(Redis Pub/Sub)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N2 --&gt;|Redis SUB| Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N1 --&gt;|Redis SUB| Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt;|fan-out| N1 &amp;#x26; N2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 fan-out 은 [[Redis Pub Sub vs Streams]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;대안-비교&quot;&gt;대안 비교&lt;/h2&gt;









































&lt;table&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;th&gt;사용&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;WebSocket&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;tr&gt;&lt;td&gt;SSE&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;tr&gt;&lt;td&gt;Long polling&lt;/td&gt;&lt;td&gt;양방향 (서버에서 hold)&lt;/td&gt;&lt;td&gt;짧음&lt;/td&gt;&lt;td&gt;legacy fallback&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HTTP/2 server push&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;tr&gt;&lt;td&gt;WebRTC DataChannel&lt;/td&gt;&lt;td&gt;양방향 P2P&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&gt;자세한 건 [[realtime-comparison]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[HTTP/1.1]] (upgrade handshake)&lt;/li&gt;
&lt;li&gt;[[TCP]] (영속 연결의 토대)&lt;/li&gt;
&lt;li&gt;[[realtime-comparison]] (대안 비교)&lt;/li&gt;
&lt;li&gt;[[SSE]] (단방향 영속)&lt;/li&gt;
&lt;li&gt;[[Redis Pub Sub vs Streams]] (fan-out 패턴)&lt;/li&gt;
&lt;li&gt;[[Sticky Session]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>websocket</category><category>network</category><category>realtime</category><category>backend</category><author>koa (김신건)</author></item><item><title>[Voice AI] WebRTC: PeerConnection, ICE, STUN/TURN, SFU</title><link>https://shinkeonkim.com/wiki/network/webrtc/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/webrtc/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;WebRTC&lt;/strong&gt; = 브라우저 / 모바일 &lt;em&gt;P2P 실시간 미디어 + 데이터&lt;/em&gt; 표준. 음성, 비디오, 데이터 채널. 2026 시점 &lt;em&gt;voice agent + 영상 통화 + 화면 공유&lt;/em&gt; 의 표준.&lt;/p&gt;
&lt;h2 id=&quot;동작-직관&quot;&gt;동작 직관&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;핵심-컴포넌트&quot;&gt;핵심 컴포넌트&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    JS[&quot;JavaScript API&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    JS --&gt; PC[RTCPeerConnection]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    JS --&gt; GU[getUserMedia]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    JS --&gt; DC[RTCDataChannel]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC --&gt; Media[&quot;미디어 트랙 (Audio/Video)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC --&gt; Sig[&quot;Signaling (별도 채널)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC --&gt; ICE[ICE candidates]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ICE --&gt; STUN[STUN server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ICE --&gt; TURN[&quot;TURN server (relay)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC --&gt; DTLS[DTLS handshake]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC --&gt; SRTP[&quot;SRTP (암호화 미디어)&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;API&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;code&gt;getUserMedia&lt;/code&gt;&lt;/td&gt;&lt;td&gt;마이크 / 카메라 접근&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt;&lt;/td&gt;&lt;td&gt;P2P 연결&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;RTCDataChannel&lt;/code&gt;&lt;/td&gt;&lt;td&gt;임의 데이터&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;RTCRtpSender/Receiver&lt;/code&gt;&lt;/td&gt;&lt;td&gt;미디어 송수신 제어&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;signaling-webrtc-가-직접-다루지-않음&quot;&gt;Signaling (WebRTC 가 &lt;em&gt;직접&lt;/em&gt; 다루지 않음)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant A as Browser A&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Sig as Signaling Server&amp;#x3C;br/&gt;(WebSocket)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant B as Browser B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;A: createOffer (SDP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;Sig: send offer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sig-&gt;&gt;B: relay offer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;B: setRemoteDescription&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;B: createAnswer (SDP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;Sig: send answer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sig-&gt;&gt;A: relay answer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;A: setRemoteDescription&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    par 양쪽 ICE candidates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A-&gt;&gt;Sig: ICE candidate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        Sig-&gt;&gt;B: relay&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B-&gt;&gt;Sig: ICE candidate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        Sig-&gt;&gt;A: relay&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;B: P2P 직접 연결 (DTLS + SRTP)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Signaling 은 WebRTC 표준 외부&lt;/em&gt;. WebSocket / HTTP 폴링 등 &lt;em&gt;자유&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;sdp-session-description-protocol&quot;&gt;SDP (Session Description Protocol)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;v=0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;o=- 1719318060 2 IN IP4 127.0.0.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;s=-&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;t=0 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a=rtpmap:111 opus/48000/2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a=rtpmap:103 ISAC/16000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a=fmtp:111 minptime=10;useinbandfec=1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a=setup:actpass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;a=fingerprint:sha-256 AC:8D:...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;코덱 / 포맷 / 보안 / ICE 후보 등. &lt;em&gt;offer/answer 모델&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;ice--stun--turn&quot;&gt;ICE / STUN / TURN&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[Browser A] --&gt;|behind NAT| Net[(Internet)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B[Browser B] --&gt;|behind NAT| Net&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A -.STUN query.-&gt; STUN[STUN server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    STUN -.public IP/port.-&gt; A&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A -.candidate exchange.-&gt; B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A --&gt;|direct| B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A -.failed?.-&gt; TURN[&quot;TURN server (relay)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TURN --&gt;|relay| B&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;STUN&lt;/th&gt;&lt;th&gt;TURN&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;em&gt;내 공용 IP/Port 알려줌&lt;/em&gt;&lt;/td&gt;&lt;td&gt;미디어 &lt;em&gt;중계&lt;/em&gt; (P2P 실패 시)&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;em&gt;높음&lt;/em&gt; (모든 미디어 흐름)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;비용&lt;/td&gt;&lt;td&gt;거의 0&lt;/td&gt;&lt;td&gt;&lt;em&gt;비쌈&lt;/em&gt; (대역폭)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;예&lt;/td&gt;&lt;td&gt;&lt;code&gt;stun:stun.l.google.com:19302&lt;/code&gt;&lt;/td&gt;&lt;td&gt;coturn (self-host)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;약 20% 의 사용자가 &lt;em&gt;symmetric NAT / 방화벽&lt;/em&gt; 때문에 &lt;em&gt;TURN 필요&lt;/em&gt;. coturn 운영 필수.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;datachannel-임의-데이터&quot;&gt;DataChannel (임의 데이터)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; dc&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createDataChannel&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;chat&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { ordered: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onopen&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;dc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onmessage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.data);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&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;ordered + reliable&lt;/td&gt;&lt;td&gt;TCP 같은 (기본)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;unordered + unreliable&lt;/td&gt;&lt;td&gt;UDP 같은 (게임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;reliable + maxPacketLifeTime&lt;/td&gt;&lt;td&gt;시간 한도&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;sfu-selective-forwarding-unit&quot;&gt;SFU (Selective Forwarding Unit)&lt;/h2&gt;
&lt;p&gt;P2P 는 &lt;em&gt;N 명 → N×(N-1)/2 연결&lt;/em&gt;. 4명 = 6 연결, 10명 = 45 연결. 폭발.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph P2P[&quot;P2P (mesh)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A1[A] --- B1[B]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A1 --- C1[C]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A1 --- D1[D]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B1 --- C1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B1 --- D1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C1 --- D1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph SFU[&quot;SFU (server forwards)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SFUS[SFU Server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A2[A] --&gt; SFUS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SFUS --&gt; A2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B2[B] --&gt; SFUS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SFUS --&gt; B2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        C2[C] --&gt; SFUS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SFUS --&gt; C2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D2[D] --&gt; SFUS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SFUS --&gt; D2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;P2P (mesh)&lt;/th&gt;&lt;th&gt;SFU&lt;/th&gt;&lt;th&gt;MCU&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;적합&lt;/td&gt;&lt;td&gt;1:1 / 작은 그룹&lt;/td&gt;&lt;td&gt;&lt;em&gt;5-100 명&lt;/em&gt;&lt;/td&gt;&lt;td&gt;TV/대규모&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;클라이언트 부하&lt;/td&gt;&lt;td&gt;N-1 stream&lt;/td&gt;&lt;td&gt;1 publish + 멀티 download&lt;/td&gt;&lt;td&gt;1 stream&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;서버 비용&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;&lt;em&gt;중간 (forwarding)&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;높음 (transcoding)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;예&lt;/td&gt;&lt;td&gt;일반 WebRTC&lt;/td&gt;&lt;td&gt;LiveKit, mediasoup, Janus&lt;/td&gt;&lt;td&gt;Jitsi Videobridge&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;음성-에이전트--webrtc&quot;&gt;음성 에이전트 + WebRTC&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    User[Browser/Mobile] --&gt;|WebRTC| SFU[LiveKit SFU]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SFU --&gt;|미디어 stream| Agent[&quot;Voice Agent (Pipecat / LiveKit Agents)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Agent --&gt;|STT| LLM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LLM --&gt;|TTS audio| Agent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Agent --&gt;|미디어 stream| SFU&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SFU --&gt;|WebRTC| User&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 건 [[pipecat-livekit]].&lt;/p&gt;
&lt;h2 id=&quot;webrtc-vs-websocket-vs-http3-voice&quot;&gt;WebRTC vs WebSocket vs HTTP/3 (voice)&lt;/h2&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;WebRTC&lt;/th&gt;&lt;th&gt;WebSocket&lt;/th&gt;&lt;th&gt;HTTP/3&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Latency&lt;/td&gt;&lt;td&gt;&lt;em&gt;최저&lt;/em&gt; (UDP)&lt;/td&gt;&lt;td&gt;중간 (TCP)&lt;/td&gt;&lt;td&gt;중간&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Packet loss&lt;/td&gt;&lt;td&gt;&lt;em&gt;FEC + loss concealment&lt;/em&gt;&lt;/td&gt;&lt;td&gt;TCP 재전송 (HoL)&lt;/td&gt;&lt;td&gt;QUIC stream 별&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Echo cancel&lt;/td&gt;&lt;td&gt;&lt;em&gt;내장 (browser)&lt;/em&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;NAT 통과&lt;/td&gt;&lt;td&gt;ICE&lt;/td&gt;&lt;td&gt;TCP/HTTP 기본 OK&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;Opus, VP8/9, H264&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;voice / video&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;blockquote&gt;
&lt;p&gt;&lt;em&gt;voice agent = WebRTC 가 거의 정답&lt;/em&gt;. 단 &lt;em&gt;방화벽 UDP 차단 환경&lt;/em&gt; 은 WebSocket fallback.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;STUN 만 + TURN 없음&lt;/strong&gt; = 20% 사용자 연결 실패. &lt;em&gt;coturn 운영&lt;/em&gt; 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;에코 (사용자 음성 마이크로 다시)&lt;/strong&gt; = &lt;code&gt;getUserMedia({ audio: { echoCancellation: true } })&lt;/code&gt;. AEC 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signaling = WebSocket 권장&lt;/strong&gt; = HTTP long-polling 은 &lt;em&gt;느리고 깨짐 잦음&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mesh 4명+ 운영&lt;/strong&gt; = N² 폭발. SFU 로 &lt;em&gt;반드시&lt;/em&gt; 전환.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDP 직접 편집&lt;/strong&gt; = browser 버전마다 다름. &lt;em&gt;libwebrtc / SFU lib 활용&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[voice-agent-architecture]]&lt;/li&gt;
&lt;li&gt;[[pipecat-livekit]]&lt;/li&gt;
&lt;li&gt;[[WebSocket]]&lt;/li&gt;
&lt;li&gt;[[UDP]]&lt;/li&gt;
&lt;li&gt;[[TLS]] (DTLS 기반)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>webrtc</category><category>p2p</category><category>sfu</category><category>voice</category><category>video</category><category>realtime</category><author>koa (김신건)</author></item><item><title>[Voice AI] WebSocket 미디어 스트림: 양방향 풀듀플렉스, 프레임 버퍼링</title><link>https://shinkeonkim.com/wiki/network/websocket-media-stream/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/websocket-media-stream/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;WebSocket Media Stream&lt;/strong&gt; = 양방향 &lt;em&gt;바이너리 오디오 스트림&lt;/em&gt;. WebRTC 가 어려운 &lt;em&gt;전화 / 서버↔서버 / 옛 클라이언트&lt;/em&gt; 에서 표준.&lt;/p&gt;
&lt;p&gt;자세한 일반 WebSocket 은 [[WebSocket]].&lt;/p&gt;
&lt;h2 id=&quot;풀듀플렉스-full-duplex&quot;&gt;풀듀플렉스 (Full Duplex)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client --&gt;|마이크 오디오 stream| Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt;|TTS 오디오 stream| Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note[양방향 동시]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ws&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; WebSocket&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;wss://voice-agent.example.com/stream&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.binaryType &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;arraybuffer&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 양방향&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; audioContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; AudioContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ sampleRate: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Send: 마이크 → ws&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; processor&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; audioContext.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createScriptProcessor&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2048&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;processor.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onaudioprocess&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; pcm&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; float32To16BitPCM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.inputBuffer.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getChannelData&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (ws.readyState &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; WebSocket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;OPEN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ws.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(pcm);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Receive: ws → 스피커&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onmessage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (e.data &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ArrayBuffer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; pcm&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Int16Array&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; float32&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; int16ToFloat32&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(pcm);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    audioQueue.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(float32);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    playFromQueue&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // JSON event&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; event&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.data);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    handleEvent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(event);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;프레임-버퍼링&quot;&gt;프레임 버퍼링&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Net[네트워크 jitter] --&gt; Buf[Jitter Buffer]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Buf --&gt;|일정 속도| Player[Audio Player]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note[&quot;jitter buffer 가 일정 속도로 재생,&amp;#x3C;br/&gt;네트워크 변동 흡수&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; JitterBuffer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; __init__&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, target_ms&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.queue &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; deque()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.target_ms &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; target_ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, chunk_with_ts):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # timestamp 순으로 정렬 삽입&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        heapq.heappush(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.queue, chunk_with_ts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; pop&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # buffer 가 충분히 차면 일정 속도로 pop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;._buffered_ms() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.target_ms:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; heapq.heappop(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.queue)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; None&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&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;strong&gt;Target&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;보통 50-150ms (latency vs 안정성 trade-off)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Adaptive&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;네트워크 jitter 측정 → buffer 크기 동적 조정&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;twilio-media-streams&quot;&gt;Twilio Media Streams&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;#x3C;!-- Twilio TwiML --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;#x3C;!-- mdx-component: Response --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Stream&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;wss://your-server/stream&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      &amp;#x3C;!-- mdx-component: Parameter --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Stream&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 서버&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;@app.websocket&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/stream&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; media_stream&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(ws: WebSocket):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ws.accept()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    while&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; True&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        msg &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ws.receive_json()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; msg[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            stream_sid &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; msg[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;streamSid&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        elif&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; msg[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            payload &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; base64.b64decode(msg[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # mulaw 8kHz → PCM 16kHz 변환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            pcm &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mulaw_to_pcm(payload)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; stt_send(pcm)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        elif&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; msg[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;stop&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            break&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # TTS 결과 전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; tts_audio_chunk:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            mulaw &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pcm_to_mulaw(tts_audio_chunk)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ws.send_json({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                &quot;event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                &quot;streamSid&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: stream_sid,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                &quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: base64.b64encode(mulaw).decode()},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;오디오-포맷&quot;&gt;오디오 포맷&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&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;strong&gt;PCM 16-bit 16kHz mono&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;표준 (STT, 내부)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;mulaw 8kHz&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;전화 (PSTN, Twilio)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Opus&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;압축 (대역폭 제한)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AAC&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;iOS / 일부 SDK&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;sample-rate-변환&quot;&gt;Sample Rate 변환&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; resampy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; resample_to_16k&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(audio: np.ndarray, sr: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) -&gt; np.ndarray:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; sr &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 16000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; audio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; resampy.resample(audio, sr, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;16000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Twilio = 8kHz mulaw&lt;/em&gt; → 내부 STT 가 &lt;em&gt;16kHz PCM&lt;/em&gt; 이면 &lt;em&gt;resampling&lt;/em&gt; 필요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;binary--json-혼용&quot;&gt;Binary + JSON 혼용&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Pattern 1: 모두 binary (audio 만)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.send(pcm_bytes)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Pattern 2: JSON event + binary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.send(json.dumps({&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: session_id}))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.send(pcm_bytes)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.send(json.dumps({&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;stop&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Pattern 3: JSON with base64 audio (Twilio 식)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ws.send(json.dumps({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;event&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;media&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: base64.b64encode(pcm).decode()},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Base64 = &lt;em&gt;33% 크기 증가&lt;/em&gt;. 대역폭 critical = binary frame.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;heartbeat--reconnect&quot;&gt;Heartbeat / Reconnect&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; VoiceWebSocket&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; heartbeat_loop&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        while&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; True&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; asyncio.sleep(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                await&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.ws.ping()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            except&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                await&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.reconnect()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; reconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; attempt &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;                self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.ws &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; connect(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;                # Session 복구&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                await&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.ws.send_json({&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;resume&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.session_id})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                return&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            except&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Exception&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;                await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; asyncio.sleep(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; **&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; attempt)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;backpressure&quot;&gt;Backpressure&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BoundedWebSocket&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &quot;&quot;&quot;WebSocket buffer 가 차면 입력 drop 또는 압축&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    MAX_BUFFER&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1024&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1024&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # 1MB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; send_safe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, data):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.ws.transport.get_write_buffer_size() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;MAX_BUFFER&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;            # 옛 데이터 drop (오디오는 lossy OK)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        await&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.ws.send(data)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; True&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 backpressure 는 [[backpressure]].&lt;/p&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;base64 사용&lt;/strong&gt; = 대역폭 33% 증가. Binary frame 권장.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jitter buffer 없음&lt;/strong&gt; = 네트워크 변동 시 &lt;em&gt;오디오 끊김&lt;/em&gt;. 100ms buffer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sample rate 불일치&lt;/strong&gt; = 16kHz 데이터를 8kHz 로 처리 → 발음 깨짐.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heartbeat 부재&lt;/strong&gt; = NAT timeout (60s) 후 silent close.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;압축 안 함 (큰 PCM)&lt;/strong&gt; = 1분 통화 = 1.92MB (16kHz, 16-bit). Opus / mulaw 압축.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[WebSocket]]&lt;/li&gt;
&lt;li&gt;[[webrtc]]&lt;/li&gt;
&lt;li&gt;[[stt-streaming]]&lt;/li&gt;
&lt;li&gt;[[tts-streaming-ssml]]&lt;/li&gt;
&lt;li&gt;[[backpressure]]&lt;/li&gt;
&lt;li&gt;[[voice-pipeline-decoupling]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>websocket</category><category>media-stream</category><category>full-duplex</category><category>frame-buffering</category><category>audio</category><author>koa (김신건)</author></item><item><title>[Network] CIDR와 Subnetting</title><link>https://shinkeonkim.com/wiki/network/network-cidr-subnetting/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/network-cidr-subnetting/</guid><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CIDR (Classless Inter-Domain Routing)&lt;/strong&gt; 은 IPv4 / IPv6 주소를 &lt;strong&gt;prefix length&lt;/strong&gt; 로 표기하는 표준. 1993년 등장 (RFC 1519, 현재 RFC 4632).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;192.168.1.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ↑&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        prefix length = 상위 24 bit 가 network, 하위 8 bit 가 host&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CIDR 이전의 class-based (A/B/C) 는 비효율적 → 클래스 폐지, prefix 자유 지정.&lt;/p&gt;
&lt;h2 id=&quot;cidr-표기&quot;&gt;CIDR 표기&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;주소 / prefix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;192.168.1.0/24       → 256 개 IP (호스트 254 개 + network + broadcast)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;192.168.0.0/16       → 65536 개 IP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.0.0.0/8           → 16,777,216 개 IP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2001:db8::/32        → IPv6 prefix&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fe80::/64            → 일반 IPv6 subnet (호스트 64 bit)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;prefix = network bit 수. host bit = 32 - prefix (IPv4) 또는 128 - prefix (IPv6).&lt;/p&gt;
&lt;h2 id=&quot;subnet-mask-변환&quot;&gt;subnet mask 변환&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prefix    binary subnet mask                 dotted decimal&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/8        11111111.00000000.00000000.00000000   255.0.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/16       11111111.11111111.00000000.00000000   255.255.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/24       11111111.11111111.11111111.00000000   255.255.255.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/25       11111111.11111111.11111111.10000000   255.255.255.128&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/30       11111111.11111111.11111111.11111100   255.255.255.252&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/32       11111111.11111111.11111111.11111111   255.255.255.255  (단일 host)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CIDR notation 이 dotted mask 보다 간결 + 라우터 설정에 일관됨.&lt;/p&gt;
&lt;h2 id=&quot;host-수-계산&quot;&gt;host 수 계산&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;/N&lt;/code&gt; 의 host 수 = &lt;code&gt;2^(32-N) - 2&lt;/code&gt;:&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Prefix&lt;/th&gt;&lt;th&gt;IP 총 개수&lt;/th&gt;&lt;th&gt;사용 가능 host&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;/24&lt;/td&gt;&lt;td&gt;256&lt;/td&gt;&lt;td&gt;254 (network + broadcast 제외)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/25&lt;/td&gt;&lt;td&gt;128&lt;/td&gt;&lt;td&gt;126&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/26&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/27&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/28&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/29&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;/30&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;2 (point-to-point link)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;-2&lt;/code&gt; 는 network address (모두 0) + broadcast (모두 1) 제외.&lt;/p&gt;
&lt;p&gt;예외:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/31&lt;/code&gt; : 2 host (P2P, RFC 3021)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/32&lt;/code&gt; : 1 host (single)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;subnet-계산-예&quot;&gt;subnet 계산 예&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;192.168.1.130/25&lt;/code&gt; 의 subnet:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;binary: 192.168.1.10000010&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mask  : 11111111.11111111.11111111.10000000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;network: 192.168.1.10000000 = 192.168.1.128&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;broadcast: 192.168.1.11111111 = 192.168.1.255&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;host range: 192.168.1.129 ~ 192.168.1.254&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/24&lt;/code&gt; 를 2 개 &lt;code&gt;/25&lt;/code&gt; 로 분할:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.1.0/25&lt;/code&gt;: 0 ~ 127 (network 0, broadcast 127, host 1-126)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.1.128/25&lt;/code&gt;: 128 ~ 255 (network 128, broadcast 255, host 129-254)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;vlsm-variable-length-subnet-masking&quot;&gt;VLSM (Variable Length Subnet Masking)&lt;/h2&gt;
&lt;p&gt;같은 organization 안에서 subnet 마다 prefix 다르게:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;회사 네트워크: 10.0.0.0/16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── 본사 (host 1000개):     10.0.0.0/22   (1022 host)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── 지사 A (host 100개):    10.0.4.0/25   (126 host)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── 지사 B (host 50개):     10.0.4.128/26  (62 host)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── DMZ (host 6개):         10.0.4.192/29  (6 host)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  └── 관리 vlan:              10.0.4.200/29  (6 host)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VLSM 이 없으면 모든 subnet 이 같은 크기 = 낭비.&lt;/p&gt;
&lt;h2 id=&quot;라우팅-longest-prefix-match&quot;&gt;라우팅 (longest prefix match)&lt;/h2&gt;
&lt;p&gt;라우터는 가장 긴 prefix 가 매칭되는 경로 선택:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;라우팅 테이블:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;0.0.0.0/0           → default (interface eth0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.0.0.0/8          → eth1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.0.0/16         → eth2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.5.0/24         → eth3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;목적지: 10.1.5.20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;→ 매칭: /0 (default), /8, /16, /24 4개&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;→ 가장 긴 /24 선택 → eth3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;supernetting-route-summarization&quot;&gt;supernetting (route summarization)&lt;/h2&gt;
&lt;p&gt;작은 subnet 여러 개를 큰 prefix 로 묶기:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.0.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.1.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.2.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.3.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   ↓ 묶으면&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;10.1.0.0/22&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;라우팅 테이블 크기 줄여 라우터 부하 감소. BGP 의 핵심.&lt;/p&gt;
&lt;h2 id=&quot;private-vs-public&quot;&gt;private vs public&lt;/h2&gt;
&lt;p&gt;RFC 1918 private (인터넷 라우팅 불가):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10.0.0.0/8&lt;/li&gt;
&lt;li&gt;172.16.0.0/12&lt;/li&gt;
&lt;li&gt;192.168.0.0/16&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NAT 로 public IP 공유 ([[network-nat]]). 사내 / docker / k8s 모두 위 범위 사용.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;조직 1: 10.0.0.0/8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;조직 2: 10.0.0.0/8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;└─ 충돌 없음 (각자 사설)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;└─ VPN 연결 시 충돌 (overlap)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;큰 조직은 VPN / merger 대비 충돌 회피 가능한 범위 선택.&lt;/p&gt;
&lt;h2 id=&quot;ipv6-subnetting&quot;&gt;IPv6 subnetting&lt;/h2&gt;
&lt;p&gt;IPv6 은 보통 &lt;code&gt;/64&lt;/code&gt; subnet (host 64 bit). ISP 가 organization 에 &lt;code&gt;/48&lt;/code&gt; 또는 &lt;code&gt;/56&lt;/code&gt; 부여:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/48 organization → 65536 개 /64 subnet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/56 organization → 256 개 /64 subnet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;예: 2001:db8:1234::/48&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── 2001:db8:1234:0001::/64  (sub-1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── 2001:db8:1234:0002::/64  (sub-2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  └── 2001:db8:1234:0003::/64&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IPv6 의 &lt;code&gt;/64&lt;/code&gt; 가 표준인 이유: SLAAC (Stateless Address Autoconfiguration) 가 MAC → host 64 bit 자동 생성.&lt;/p&gt;
&lt;h2 id=&quot;자주-보는-시나리오&quot;&gt;자주 보는 시나리오&lt;/h2&gt;
&lt;h3 id=&quot;시나리오-1-aws-vpc&quot;&gt;시나리오 1: AWS VPC&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;VPC: 10.0.0.0/16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── subnet-public-a:   10.0.1.0/24 (AZ-a, public)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── subnet-public-b:   10.0.2.0/24 (AZ-b, public)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ├── subnet-private-a:  10.0.10.0/24 (AZ-a, private)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  └── subnet-private-b:  10.0.20.0/24 (AZ-b, private)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VPC 의 &lt;code&gt;/16&lt;/code&gt; 안에서 &lt;code&gt;/24&lt;/code&gt; 들로 subdivide. AZ 별 + public/private 별.&lt;/p&gt;
&lt;h3 id=&quot;시나리오-2-kubernetes&quot;&gt;시나리오 2: Kubernetes&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Pod CIDR:     10.244.0.0/16   (Pod IP pool)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Service CIDR: 10.96.0.0/12    (ClusterIP pool)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Node 1: 10.244.0.0/24   (256 pod 한계)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Node 2: 10.244.1.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Node 3: 10.244.2.0/24&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CNI plugin (Calico, Flannel) 이 위 할당. 노드 수 × subnet 크기가 Pod CIDR 보다 작아야.&lt;/p&gt;
&lt;h3 id=&quot;시나리오-3-p2p-link&quot;&gt;시나리오 3: P2P link&lt;/h3&gt;
&lt;p&gt;라우터 간 직접 연결은 host 2 개면 충분:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RouterA --- 10.255.1.0/30 --- RouterB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .1                            .2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/30&lt;/code&gt;: network=.0, host=.1+.2, broadcast=.3. 또는 &lt;code&gt;/31&lt;/code&gt; (RFC 3021).&lt;/p&gt;
&lt;h2 id=&quot;빠른-계산-트릭&quot;&gt;빠른 계산 트릭&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;몇 host?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/24 → 2^8 - 2 = 254&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/25 → 2^7 - 2 = 126&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/26 → 2^6 - 2 = 62&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/27 → 2^5 - 2 = 30&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/28 → 2^4 - 2 = 14&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/29 → 2^3 - 2 = 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/30 → 2^2 - 2 = 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/N&lt;/code&gt; 의 host = &lt;code&gt;2^(32-N) - 2&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;몇 subnet?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/24 를 /26 으로 → 4 개 (host bit 2 → 2^2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/24 를 /27 으로 → 8 개&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/16 을 /24 로 → 256 개&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;prefix 가 길어진 bit 수 = 2 의 지수.&lt;/p&gt;
&lt;h2 id=&quot;함정과-베스트-프랙티스&quot;&gt;함정과 베스트 프랙티스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VPC / overlay 의 CIDR 미리 큰 범위로&lt;/strong&gt;: 작게 잡으면 나중에 확장 불가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;subnet 충돌 회피&lt;/strong&gt;: VPN / multi-cloud 환경에서 organization 별 다른 prefix&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS VPC: &lt;code&gt;/16&lt;/code&gt; 권장&lt;/strong&gt;: subnet 분할 여지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker 의 기본 &lt;code&gt;172.17.0.0/16&lt;/code&gt;&lt;/strong&gt; 충돌 시: &lt;code&gt;daemon.json&lt;/code&gt; 으로 변경&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;K8s 의 Pod CIDR + Service CIDR + Node IP 가 충돌 금지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPv6 의 &lt;code&gt;/64&lt;/code&gt; 는 거의 절대 분할 X&lt;/strong&gt;: SLAAC 망가짐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;longest prefix match&lt;/strong&gt; 가 라우터 결정 규칙&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;summarization 으로 라우팅 테이블 축소&lt;/strong&gt;: BGP 환경에서 critical&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-ip]]&lt;/li&gt;
&lt;li&gt;[[network-nat]]&lt;/li&gt;
&lt;li&gt;[[network-dns]]&lt;/li&gt;
&lt;li&gt;[[tcp]]&lt;/li&gt;
&lt;li&gt;[[udp]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>ip</category><category>cidr</category><category>subnetting</category><category>l3</category><author>koa (김신건)</author></item><item><title>[Network] DNS: 도메인 이름 해석</title><link>https://shinkeonkim.com/wiki/network/network-dns/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/network-dns/</guid><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;DNS (Domain Name System)&lt;/strong&gt; 는 도메인 이름 (&lt;code&gt;example.com&lt;/code&gt;) 을 IP 주소 (&lt;code&gt;93.184.216.34&lt;/code&gt;) 로 변환하는 분산 데이터베이스. 인터넷의 가장 중요한 인프라.&lt;/p&gt;
&lt;p&gt;기본 동작:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트 → resolver 에게 “example.com 의 IP” 요청&lt;/li&gt;
&lt;li&gt;resolver → root → TLD → authoritative 서버를 순회 (recursive)&lt;/li&gt;
&lt;li&gt;resolver → 결과를 캐시 + 클라이언트에 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;L7 protocol, 보통 UDP 53 port (큰 응답은 TCP 53, modern 은 DoT/DoH).&lt;/p&gt;
&lt;h2 id=&quot;도메인-계층&quot;&gt;도메인 계층&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.                              ← root (.)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── com.                       ← TLD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   ├── google.com.            ← second-level&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   │   ├── www.google.com.    ← subdomain&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   │   └── mail.google.com.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   └── example.com.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;├── kr.                        ← ccTLD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;│   └── naver.kr.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;└── org.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;오른쪽에서 왼쪽으로 읽기. &lt;code&gt;.&lt;/code&gt; 가 root.&lt;/p&gt;
&lt;p&gt;FQDN (Fully Qualified Domain Name): &lt;code&gt;www.example.com.&lt;/code&gt; (마지막 &lt;code&gt;.&lt;/code&gt; 까지).&lt;/p&gt;
&lt;h2 id=&quot;dns-record-종류&quot;&gt;DNS Record 종류&lt;/h2&gt;

































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&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;strong&gt;A&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IPv4 주소&lt;/td&gt;&lt;td&gt;&lt;code&gt;example.com → 93.184.216.34&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AAAA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IPv6 주소&lt;/td&gt;&lt;td&gt;&lt;code&gt;example.com → 2606:2800:220:1::6:34&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CNAME&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;별칭 (canonical name)&lt;/td&gt;&lt;td&gt;&lt;code&gt;www.example.com → example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;MX&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;메일 서버 + 우선순위&lt;/td&gt;&lt;td&gt;&lt;code&gt;example.com → 10 mail.example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;NS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;nameserver&lt;/td&gt;&lt;td&gt;&lt;code&gt;example.com → ns1.example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;TXT&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;임의 텍스트 (SPF, DKIM, 인증)&lt;/td&gt;&lt;td&gt;&lt;code&gt;v=spf1 ...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SRV&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;service location (port + host)&lt;/td&gt;&lt;td&gt;&lt;code&gt;_sip._tcp.example.com → 10 60 5060 sip.example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;PTR&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;reverse lookup (IP → 도메인)&lt;/td&gt;&lt;td&gt;&lt;code&gt;34.216.184.93.in-addr.arpa → example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CAA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;cert authority 제한&lt;/td&gt;&lt;td&gt;&lt;code&gt;example.com → issue &quot;letsencrypt.org&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SOA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;zone authority&lt;/td&gt;&lt;td&gt;&lt;code&gt;nameserver, email, serial, refresh, ...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DNSKEY / DS / RRSIG&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;DNSSEC 서명&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;resolution-흐름&quot;&gt;Resolution 흐름&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;client → recursive resolver → root → TLD → authoritative&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         (&quot;8.8.8.8&quot; 등)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;상세:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 클라이언트: &quot;www.example.com 의 A 레코드?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. recursive resolver (ISP, 8.8.8.8, 1.1.1.1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - 캐시 hit 시 즉시 반환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - 캐시 miss:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. root (.) 에 질문: &quot;.com 의 nameserver?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - 응답: &quot;a.gtld-servers.net 등&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. .com TLD 에 질문: &quot;example.com 의 nameserver?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - 응답: &quot;ns1.example.com 등&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. example.com 의 authoritative 에 질문: &quot;www.example.com 의 A?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - 응답: &quot;93.184.216.34&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6. resolver 캐시 + 클라이언트에 반환&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 단계는 TTL 만큼 캐시. 일반적으로 5분 ~ 24시간.&lt;/p&gt;
&lt;h2 id=&quot;recursive-vs-iterative&quot;&gt;recursive vs iterative&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;recursive query&lt;/strong&gt;: “최종 답을 줘”
&lt;ul&gt;
&lt;li&gt;클라이언트 → resolver&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iterative query&lt;/strong&gt;: “당신이 아는 것만 줘”
&lt;ul&gt;
&lt;li&gt;resolver → root / TLD / authoritative&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;대부분 OS / 브라우저는 recursive 만 사용. resolver 가 iterative 로 순회.&lt;/p&gt;
&lt;h2 id=&quot;캐싱과-ttl&quot;&gt;캐싱과 TTL&lt;/h2&gt;
&lt;p&gt;DNS 응답에 TTL (초). 짧으면 변경 빠르게 반영, 길면 부하 감소.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;www.example.com.    300    IN    A    93.184.216.34&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ↑&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    300 초 (5분) 캐시&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다층 캐싱:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;브라우저 cache (~수분)&lt;/li&gt;
&lt;li&gt;OS resolver cache (&lt;code&gt;systemd-resolved&lt;/code&gt;, &lt;code&gt;dnsmasq&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;ISP / public resolver cache&lt;/li&gt;
&lt;li&gt;authoritative cache 없음 (source of truth)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;서비스 마이그레이션 / DNS 변경 시 &lt;strong&gt;TTL 미리 줄이기&lt;/strong&gt; (1시간 → 60초) 가 일반 패턴.&lt;/p&gt;
&lt;h2 id=&quot;cname-함정&quot;&gt;CNAME 함정&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;www.example.com.    300    IN    CNAME    example.com.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com.        300    IN    A        93.184.216.34&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;www.example.com&lt;/code&gt; 조회 → CNAME → &lt;code&gt;example.com&lt;/code&gt; 다시 조회 → A.&lt;/p&gt;
&lt;p&gt;함정:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;apex / root 에는 CNAME 불가&lt;/strong&gt; (RFC). &lt;code&gt;example.com&lt;/code&gt; 에는 CNAME 안 됨.
&lt;ul&gt;
&lt;li&gt;회피: AWS Route 53 의 &lt;strong&gt;ALIAS&lt;/strong&gt; (CNAME 처럼 보이지만 A 레코드 반환)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CNAME 체인 길어지면 latency&lt;/strong&gt;: 가능하면 직접 A&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;dns-over-https-doh--dns-over-tls-dot&quot;&gt;DNS over HTTPS (DoH) / DNS over TLS (DoT)&lt;/h2&gt;
&lt;p&gt;기존 DNS = 평문 UDP 53. 도청 / 변조 가능.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DoT (RFC 7858)&lt;/strong&gt;: TLS 위에서 DNS. Port 853.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DoH (RFC 8484)&lt;/strong&gt;: HTTPS 위에서 DNS. Port 443. 일반 HTTPS 트래픽처럼 보임.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;브라우저 (Firefox, Chrome) 가 기본 DoH 활성화 중. 단점: ISP / 기업의 DNS 기반 필터링 우회 가능 → 정책 충돌.&lt;/p&gt;
&lt;p&gt;DoQ (DNS over QUIC) 도 표준화 (RFC 9250).&lt;/p&gt;
&lt;h2 id=&quot;dnssec&quot;&gt;DNSSEC&lt;/h2&gt;
&lt;p&gt;응답의 서명 검증:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.            DNSKEY  → ZSK / KSK (root signing key)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;com.         DS      → .com 의 KSK 해시 (root 가 서명)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com. DS      → example.com 의 KSK 해시 (.com 이 서명)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com. RRSIG   → A 레코드의 서명&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;resolver 가 root → TLD → authoritative 전 chain 검증.&lt;/p&gt;
&lt;p&gt;복잡 + 운영 어려움 → 채택률 낮음 (Top 1M 사이트의 약 4%). 큰 ISP / 정부 사이트는 적용.&lt;/p&gt;
&lt;h2 id=&quot;edns-extension&quot;&gt;EDNS (Extension)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;DNS 메시지 크기 확장 (기본 512 byte → 4096 등)&lt;/li&gt;
&lt;li&gt;EDNS Client Subnet (ECS): resolver 가 클라이언트 subnet 을 authoritative 에 전달 → geo-IP 정확도 향상 (CDN 핵심)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;실용-예-서비스-운영&quot;&gt;실용 예: 서비스 운영&lt;/h2&gt;
&lt;h3 id=&quot;a-레코드--cdn&quot;&gt;A 레코드 + CDN&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;www.example.com    300    A    104.21.x.x   (Cloudflare edge)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;www.example.com    300    A    172.67.x.x   (다른 edge)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여러 A 레코드 = round-robin 또는 anycast 라우팅.&lt;/p&gt;
&lt;h3 id=&quot;mx-우선순위&quot;&gt;MX 우선순위&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com    3600    MX    10    primary-mail.example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com    3600    MX    20    backup-mail.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;낮은 숫자 = 높은 우선순위. primary 실패 시 backup.&lt;/p&gt;
&lt;h3 id=&quot;txt-spf--dkim--dmarc&quot;&gt;TXT (SPF / DKIM / DMARC)&lt;/h3&gt;
&lt;p&gt;이메일 인증:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;example.com    300    TXT    &quot;v=spf1 include:_spf.google.com ~all&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;default._domainkey.example.com  300  TXT  &quot;v=DKIM1; k=rsa; p=MIGfMA0...&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;_dmarc.example.com  300  TXT  &quot;v=DMARC1; p=reject; rua=mailto:...&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;스팸 / 위장 방지의 필수.&lt;/p&gt;
&lt;h3 id=&quot;health-check--failover&quot;&gt;Health check + failover&lt;/h3&gt;
&lt;p&gt;Route 53 / Cloudflare 같은 managed DNS 가 health check 수행:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;primary 가 200 OK → primary A 반환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;primary 가 5xx → backup A 반환&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TTL 짧게 (30~60초) 두면 빠른 failover.&lt;/p&gt;
&lt;h2 id=&quot;dns-측정-도구&quot;&gt;DNS 측정 도구&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 기본 lookup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; AAAA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; MX&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 특정 resolver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; @8.8.8.8&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 전체 trace&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; +trace&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 짧게&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;dig&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; +short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# nslookup (legacy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nslookup&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# host (간결)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;host&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# DoH&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -H&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Accept: application/dns-json&apos;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;https://1.1.1.1/dns-query?name=example.com&amp;#x26;type=A&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;자주-보는-함정&quot;&gt;자주 보는 함정&lt;/h2&gt;
&lt;h3 id=&quot;함정-1-캐시-stale&quot;&gt;함정 1: 캐시 stale&lt;/h3&gt;
&lt;p&gt;DNS 변경 후 일부 사용자 / ISP 가 옛 IP 캐시 → 일관성 문제. TTL 미리 줄이기.&lt;/p&gt;
&lt;h3 id=&quot;함정-2-round-robin-의-불균등&quot;&gt;함정 2: round-robin 의 불균등&lt;/h3&gt;
&lt;p&gt;A 레코드 여러 개를 resolver 가 순서대로 반환하지만 클라이언트가 첫 IP 만 시도하면 불균등. anycast / load balancer 가 더 정확.&lt;/p&gt;
&lt;h3 id=&quot;함정-3-soa-refresh--retry&quot;&gt;함정 3: SOA refresh / retry&lt;/h3&gt;
&lt;p&gt;secondary nameserver 가 primary 의 변경을 늦게 감지. SOA 의 refresh / retry / expire 값 적절히.&lt;/p&gt;
&lt;h3 id=&quot;함정-4-ptr-이-없으면-이메일-거부&quot;&gt;함정 4: PTR 이 없으면 이메일 거부&lt;/h3&gt;
&lt;p&gt;송신 메일 서버의 PTR (reverse DNS) 가 매칭 안 되면 스팸 처리. 자체 SMTP 운영 시 필수.&lt;/p&gt;
&lt;h3 id=&quot;함정-5-split-horizon-dns&quot;&gt;함정 5: split-horizon DNS&lt;/h3&gt;
&lt;p&gt;내부에서 &lt;code&gt;api.example.com&lt;/code&gt; → 사내 IP, 외부에서 → public IP. VPN 전환 시 캐시 혼선 가능.&lt;/p&gt;
&lt;h2 id=&quot;함정과-베스트-프랙티스&quot;&gt;함정과 베스트 프랙티스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TTL 운영 변경 전 미리 줄이기&lt;/strong&gt;: 마이그레이션 24시간 전부터&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNSSEC 은 큰 조직만&lt;/strong&gt;: 운영 복잡, 채택률 낮음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DoH / DoT 활용&lt;/strong&gt;: 도청 방지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SPF + DKIM + DMARC 필수&lt;/strong&gt;: 이메일 운영 시&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;multiple authoritative nameserver&lt;/strong&gt;: 단일 장애점 회피 (보통 2~4개)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDNS Client Subnet&lt;/strong&gt;: CDN 의 geo-IP 정확도 의존성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PTR 도 관리&lt;/strong&gt;: 메일 / SSH 인증 영향&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;monitoring&lt;/strong&gt;: TTL, 응답 시간, error rate (NXDOMAIN 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dig +trace&lt;/strong&gt; 가 디버깅의 가장 강력한 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-ip]]&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting]]&lt;/li&gt;
&lt;li&gt;[[tcp]]&lt;/li&gt;
&lt;li&gt;[[udp]]&lt;/li&gt;
&lt;li&gt;[[tls]]&lt;/li&gt;
&lt;li&gt;[[quic]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>dns</category><category>l7</category><author>koa (김신건)</author></item><item><title>[Network] HTTP Caching: Cache-Control, ETag, Last-Modified</title><link>https://shinkeonkim.com/wiki/network/network-http-caching/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/network-http-caching/</guid><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP Caching&lt;/strong&gt; 은 HTTP 응답을 클라이언트 / 중간 proxy / CDN 이 보관해 같은 요청을 다시 처리하지 않게 하는 메커니즘. 성능 + 비용 절감의 핵심.&lt;/p&gt;
&lt;p&gt;3가지 levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Private cache&lt;/strong&gt;: 브라우저 (사용자 1명만)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared cache&lt;/strong&gt;: CDN, reverse proxy (여러 사용자)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin cache&lt;/strong&gt;: 서버 자체 (e.g., Redis)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 페이지는 HTTP layer 의 1, 2번 (Cache-Control, ETag 등).&lt;/p&gt;
&lt;h2 id=&quot;캐시-전략의-두-축&quot;&gt;캐시 전략의 두 축&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;신선함 (freshness)&quot;: TTL 기반 - 만료 전까진 캐시 사용 (서버 안 가)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;검증 (validation)&quot;: ETag / Last-Modified - 서버에 &quot;변경됐어?&quot; 물어봄&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;조합:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;fresh&lt;/strong&gt;: 캐시 즉시 사용 (네트워크 X)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stale + valid (304)&lt;/strong&gt;: 캐시 사용 + 짧은 검증 요청&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stale + invalid (200)&lt;/strong&gt;: 새 응답 받기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cache-control&quot;&gt;Cache-Control&lt;/h2&gt;
&lt;p&gt;핵심 directive:&lt;/p&gt;

















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Directive&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;code&gt;max-age=N&lt;/code&gt;&lt;/td&gt;&lt;td&gt;N 초 동안 fresh&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;s-maxage=N&lt;/code&gt;&lt;/td&gt;&lt;td&gt;shared cache (CDN) 만 적용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;public&lt;/code&gt;&lt;/td&gt;&lt;td&gt;모든 cache 에 저장 가능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;private&lt;/code&gt;&lt;/td&gt;&lt;td&gt;브라우저만 (CDN 금지)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt;&lt;/td&gt;&lt;td&gt;캐시 OK 하지만 사용 전 반드시 검증&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/td&gt;&lt;td&gt;절대 캐시 금지 (민감 정보)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;must-revalidate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;만료 후 stale 사용 금지, 검증 필수&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;immutable&lt;/code&gt;&lt;/td&gt;&lt;td&gt;절대 안 바뀜 (검증도 안 함)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;stale-while-revalidate=N&lt;/code&gt;&lt;/td&gt;&lt;td&gt;만료 후 N 초 동안 stale 반환 + 백그라운드 갱신&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;stale-if-error=N&lt;/code&gt;&lt;/td&gt;&lt;td&gt;서버 에러 시 N 초 동안 stale 사용&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;응답-예&quot;&gt;응답 예&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; text/css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; public, max-age=31536000, immutable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ETag&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;abc123&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;body { &lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;immutable&lt;/code&gt; + 긴 max-age = “이 자원은 절대 안 바뀐다” → CDN / 브라우저 완전 신뢰. Vite / webpack 의 hashed filename (&lt;code&gt;app.abc123.css&lt;/code&gt;) 패턴에 최적.&lt;/p&gt;
&lt;h3 id=&quot;요청-측-cache-control&quot;&gt;요청 측 Cache-Control&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /api/data &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; no-cache    # 강제 검증&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; max-age=0   # 동일&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; no-store    # 캐시 비활성&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;etag-entity-tag&quot;&gt;ETag (Entity Tag)&lt;/h2&gt;
&lt;p&gt;응답의 unique identifier (보통 hash):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ETag&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;v2.1-abc123&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다음 요청에서 클라이언트가 &lt;code&gt;If-None-Match&lt;/code&gt; 로 보냄:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /api/data &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;If-None-Match&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;v2.1-abc123&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;서버 응답:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;변경 없음 → &lt;strong&gt;&lt;code&gt;304 Not Modified&lt;/code&gt;&lt;/strong&gt; (body 없음, 짧음)&lt;/li&gt;
&lt;li&gt;변경 있음 → &lt;code&gt;200 OK&lt;/code&gt; + 새 body + 새 ETag&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;strong-vs-weak-etag&quot;&gt;Strong vs Weak ETag&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ETag: &quot;abc123&quot;        # strong (byte-for-byte 동일)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ETag: W/&quot;abc123&quot;      # weak (의미적으로 동일, gzip 후도 OK)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;대부분 weak 가 적합 (compression, encoding 차이 무시).&lt;/p&gt;
&lt;h2 id=&quot;last-modified&quot;&gt;Last-Modified&lt;/h2&gt;
&lt;p&gt;ETag 의 timestamp 버전:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Last-Modified&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Wed, 24 Jun 2026 10:00:00 GMT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요청:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /api/data &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;If-Modified-Since&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Wed, 24 Jun 2026 10:00:00 GMT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;응답: 304 또는 200.&lt;/p&gt;
&lt;p&gt;ETag 와 Last-Modified 둘 다 있으면 ETag 우선. ETag 만으로 충분한 경우 많음.&lt;/p&gt;
&lt;h2 id=&quot;vary&quot;&gt;Vary&lt;/h2&gt;
&lt;p&gt;같은 URL 인데 응답이 다를 수 있을 때 (Accept-Encoding 별 gzip/br, Accept-Language 별 i18n):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Vary&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Accept-Encoding, Accept-Language&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;캐시가 Vary 헤더에 명시된 헤더 값별로 별도 저장:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; → gzip 버전 캐시&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Accept-Encoding: br&lt;/code&gt; → brotli 버전 캐시&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Vary 가 너무 많으면 캐시 hit 률 ↓&lt;/strong&gt;. 가능하면 적게 사용.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Vary: User-Agent&lt;/code&gt; 는 일반적으로 비추천 (UA 종류가 많음 → 거의 캐시 miss).&lt;/p&gt;
&lt;h2 id=&quot;age--expires&quot;&gt;Age &amp;#x26; Expires&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; max-age=3600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Age&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 1200         # 캐시에서 1200 초 머묾 (남은 신선도 2400초)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Expires&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Wed, 24 Jun 2026 11:00:00 GMT  # legacy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Age&lt;/code&gt; 가 CDN 디버깅에 유용. &lt;code&gt;Expires&lt;/code&gt; 는 HTTP/1.0 legacy, &lt;code&gt;Cache-Control&lt;/code&gt; 우선.&lt;/p&gt;
&lt;h2 id=&quot;pragma-deprecated&quot;&gt;Pragma (deprecated)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Pragma&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; no-cache&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTTP/1.0 호환용. &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; 가 표준.&lt;/p&gt;
&lt;h2 id=&quot;패턴-별-권장-설정&quot;&gt;패턴 별 권장 설정&lt;/h2&gt;
&lt;h3 id=&quot;static-asset-hash-파일명-immutable&quot;&gt;Static asset (hash 파일명, immutable)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /assets/app.abc123.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: public, max-age=31536000, immutable&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1년 + immutable. CDN edge 에서 영구 사용.&lt;/p&gt;
&lt;h3 id=&quot;html-변경-가능-항상-신선&quot;&gt;HTML (변경 가능, 항상 신선)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: no-cache, must-revalidate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ETag: &quot;v1&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ETag 로 검증, 변경 시 새 HTML 받기. SPA 의 index.html 패턴.&lt;/p&gt;
&lt;h3 id=&quot;api-변경-빈도-보통&quot;&gt;API (변경 빈도 보통)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /api/products&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: private, max-age=60, stale-while-revalidate=300&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ETag: &quot;x&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;60초 fresh, 그 후 5분간 stale 반환 + 백그라운드 갱신. 사용자 경험과 부하의 균형.&lt;/p&gt;
&lt;h3 id=&quot;private-user-data&quot;&gt;Private user data&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /api/me&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: private, no-cache, no-store&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CDN 캐시 금지, 브라우저도 캐시 안 함.&lt;/p&gt;
&lt;h3 id=&quot;public-api-with-no-cookie&quot;&gt;Public API with no Cookie&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /api/public/articles&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: public, max-age=300, s-maxage=3600&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;브라우저는 5분, CDN 은 1시간. &lt;code&gt;s-maxage&lt;/code&gt; 가 &lt;code&gt;max-age&lt;/code&gt; override (shared cache 만).&lt;/p&gt;
&lt;h2 id=&quot;cdn-통합&quot;&gt;CDN 통합&lt;/h2&gt;
&lt;p&gt;대부분 CDN (Cloudflare, Fastly, CloudFront) 가 위 헤더 자동 처리. 추가 제어:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: public, max-age=300&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CDN-Cache-Control: max-age=3600      # CDN 전용&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cloudflare-CDN-Cache-Control: max-age=86400   # Cloudflare 만&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;origin 부담 줄이려면 CDN 에 길게 캐시 + 클라이언트엔 짧게.&lt;/p&gt;
&lt;h3 id=&quot;캐시-invalidation&quot;&gt;캐시 invalidation&lt;/h3&gt;
&lt;p&gt;수동:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Cloudflare API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -X&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; POST&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;https://api.cloudflare.com/.../purge_cache&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -H&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Authorization: Bearer &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$TOKEN&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -d&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;{&quot;files&quot;:[&quot;https://example.com/path&quot;]}&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또는 URL 에 hash / version 포함:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/assets/app.abc123.js&lt;/code&gt; → 새 빌드는 &lt;code&gt;/assets/app.def456.js&lt;/code&gt; (URL 자체가 바뀌므로 invalidation 불필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;자주-보는-함정&quot;&gt;자주 보는 함정&lt;/h2&gt;
&lt;h3 id=&quot;함정-1-no-cache-vs-no-store-혼동&quot;&gt;함정 1: no-cache vs no-store 혼동&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;no-cache&lt;/code&gt; : 캐시 OK, 사용 전 검증&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-store&lt;/code&gt; : 캐시 자체 금지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;민감 정보는 &lt;code&gt;no-store&lt;/code&gt;. “항상 최신” 만 원하면 &lt;code&gt;no-cache&lt;/code&gt; + ETag.&lt;/p&gt;
&lt;h3 id=&quot;함정-2-cookie-와-캐시&quot;&gt;함정 2: Cookie 와 캐시&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Set-Cookie&lt;/code&gt; 가 있는 응답을 CDN 이 캐시하면 모든 사용자가 같은 cookie 받음 (재앙). 보통 CDN 이 자동 회피하지만 명시:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cache-Control: private    # 또는 no-store&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;함정-3-vary--또는-너무-많은-vary&quot;&gt;함정 3: Vary: * 또는 너무 많은 Vary&lt;/h3&gt;
&lt;p&gt;캐시 hit 률 0%. 불필요한 Vary 제거.&lt;/p&gt;
&lt;h3 id=&quot;함정-4-짧은-max-age--큰-트래픽--origin-부하&quot;&gt;함정 4: 짧은 max-age + 큰 트래픽 = origin 부하&lt;/h3&gt;
&lt;p&gt;CDN 에 &lt;code&gt;s-maxage&lt;/code&gt; 길게 + 클라이언트엔 &lt;code&gt;max-age&lt;/code&gt; 짧게 분리.&lt;/p&gt;
&lt;h3 id=&quot;함정-5-authorization-헤더와-캐시&quot;&gt;함정 5: Authorization 헤더와 캐시&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Authorization&lt;/code&gt; 헤더 있는 요청은 기본적으로 캐시 안 됨 (RFC 9111). &lt;code&gt;Cache-Control: public&lt;/code&gt; 명시 시만.&lt;/p&gt;
&lt;h3 id=&quot;함정-6-응답-status&quot;&gt;함정 6: 응답 status&lt;/h3&gt;
&lt;p&gt;비-2xx 도 캐시 가능:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;301 Moved Permanently&lt;/code&gt; : 영구 캐시 (브라우저가 매우 길게)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;404 Not Found&lt;/code&gt; : 짧게 캐시 가능 (다음 요청도 404 위험)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;디버깅&quot;&gt;디버깅&lt;/h2&gt;
&lt;p&gt;브라우저 DevTools Network 탭:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(memory cache)&lt;/code&gt; / &lt;code&gt;(disk cache)&lt;/code&gt; : 히트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;200&lt;/code&gt; : origin / CDN 응답&lt;/li&gt;
&lt;li&gt;&lt;code&gt;304&lt;/code&gt; : 검증 hit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;ETag&lt;/code&gt;, &lt;code&gt;Age&lt;/code&gt; 헤더 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CDN debug header:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cf-cache-status: HIT/MISS/REVALIDATED&lt;/code&gt; (Cloudflare)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x-cache: HIT/MISS&lt;/code&gt; (CloudFront, Fastly)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;함정과-베스트-프랙티스&quot;&gt;함정과 베스트 프랙티스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static asset 은 immutable + 1년 + URL hash&lt;/strong&gt;: 가장 효율적&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML 은 ETag + no-cache&lt;/strong&gt;: SPA 와 자연스러움&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API 는 stale-while-revalidate 적극 활용&lt;/strong&gt;: UX 향상&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Vary&lt;/code&gt; 는 최소화&lt;/strong&gt;: hit 률 보호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/strong&gt; 와 &lt;strong&gt;&lt;code&gt;private&lt;/code&gt;&lt;/strong&gt; 차이 명확히 인지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN debug header 모니터링&lt;/strong&gt;: hit 률 추적&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;invalidation 보다 versioned URL&lt;/strong&gt;: 더 안전&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;민감 데이터는 &lt;code&gt;no-store&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;stale-if-error&lt;/code&gt; 로 자동 fallback&lt;/strong&gt;: origin 다운 시 UX 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-dns]]&lt;/li&gt;
&lt;li&gt;[[network-ip]]&lt;/li&gt;
&lt;li&gt;[[hpack]]&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking]]&lt;/li&gt;
&lt;li&gt;[[quic]]&lt;/li&gt;
&lt;li&gt;[[spring-cache]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>http</category><category>caching</category><category>performance</category><category>l7</category><author>koa (김신건)</author></item><item><title>[Network] IP: IPv4, IPv6, 패킷 구조, fragmentation</title><link>https://shinkeonkim.com/wiki/network/ip/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/ip/</guid><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;IP (Internet Protocol)&lt;/strong&gt; 는 OSI L3 (네트워크 계층) 의 핵심 프로토콜. &lt;strong&gt;best-effort&lt;/strong&gt; delivery (재전송 없음, 순서 보장 없음, 손실 가능). 신뢰성은 TCP 같은 상위 layer 가 담당.&lt;/p&gt;
&lt;p&gt;두 버전:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IPv4&lt;/strong&gt;: 32-bit 주소 (≈ 43억), 1981년 RFC 791&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPv6&lt;/strong&gt;: 128-bit 주소 (사실상 무한), 1998년 RFC 2460 → 2017년 RFC 8200&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;현재 인터넷은 듀얼 스택. 점진적 IPv6 전환 중 (구글 클라이언트 통계 약 40-50%).&lt;/p&gt;
&lt;h2 id=&quot;ipv4-주소&quot;&gt;IPv4 주소&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Dotted decimal&lt;/strong&gt;: &lt;code&gt;192.168.1.1&lt;/code&gt; = 32-bit (4 octet, 0~255 each).&lt;/p&gt;


















































&lt;table&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;0.0.0.0/8&lt;/td&gt;&lt;td&gt;미지정 / “this network”&lt;/td&gt;&lt;td&gt;0.0.0.0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10.0.0.0/8&lt;/td&gt;&lt;td&gt;private (Class A)&lt;/td&gt;&lt;td&gt;10.x.x.x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;127.0.0.0/8&lt;/td&gt;&lt;td&gt;loopback&lt;/td&gt;&lt;td&gt;127.0.0.1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;169.254.0.0/16&lt;/td&gt;&lt;td&gt;link-local (DHCP 실패 시)&lt;/td&gt;&lt;td&gt;169.254.x.x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;172.16.0.0/12&lt;/td&gt;&lt;td&gt;private (Class B)&lt;/td&gt;&lt;td&gt;172.16-31.x.x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;192.168.0.0/16&lt;/td&gt;&lt;td&gt;private (Class C)&lt;/td&gt;&lt;td&gt;192.168.x.x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;224.0.0.0/4&lt;/td&gt;&lt;td&gt;multicast&lt;/td&gt;&lt;td&gt;224.0.0.1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;255.255.255.255&lt;/td&gt;&lt;td&gt;broadcast&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;NAT 가 있는 이유: IPv4 고갈 → 사설망 안에서 private + 인터넷 진출 시 NAT 가 public IP 로 변환. [[network-nat]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;ipv6-주소&quot;&gt;IPv6 주소&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Colon-separated hex&lt;/strong&gt;: &lt;code&gt;2001:0db8:85a3:0000:0000:8a2e:0370:7334&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;축약 규칙:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 그룹의 leading zero 생략: &lt;code&gt;2001:db8:85a3:0:0:8a2e:370:7334&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;연속 0 그룹은 &lt;code&gt;::&lt;/code&gt; 한 번만 사용 가능: &lt;code&gt;2001:db8:85a3::8a2e:370:7334&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특수 주소:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;::1&lt;/code&gt; : loopback (IPv4 의 &lt;code&gt;127.0.0.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;::&lt;/code&gt; : 미지정 (&lt;code&gt;0.0.0.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fe80::/10&lt;/code&gt; : link-local&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fc00::/7&lt;/code&gt; : ULA (Unique Local, private)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2000::/3&lt;/code&gt; : global unicast&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IPv6 는 broadcast 없음 (multicast 만). NDP (Neighbor Discovery Protocol) 가 ARP 대체.&lt;/p&gt;
&lt;h2 id=&quot;ipv4-헤더-20-byte-기본&quot;&gt;IPv4 헤더 (20 byte 기본)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 0                   1                   2                   3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------+-------+---------------+-------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|Version|  IHL  |Type of Service|          Total Length         |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------+-------+---------------+-----+-------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|         Identification        |Flags|      Fragment Offset    |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------+---------------+-----+-------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|  Time to Live |    Protocol   |         Header Checksum       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------+---------------+-------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|                       Source Address                          |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|                    Destination Address                        |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|                    Options (옵션, 가변)                       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;핵심 필드:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version&lt;/strong&gt;: 4&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IHL&lt;/strong&gt;: 헤더 길이 (32-bit word 단위, 기본 5 = 20 byte)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total Length&lt;/strong&gt;: 전체 패킷 크기 (max 65535)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identification + Flags + Fragment Offset&lt;/strong&gt;: fragmentation 용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL&lt;/strong&gt;: hop 수 제한 (0 도달 시 ICMP TTL Exceeded 후 폐기)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol&lt;/strong&gt;: 다음 layer (TCP=6, UDP=17, ICMP=1)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source/Destination&lt;/strong&gt;: 32-bit IP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ipv6-헤더-고정-40-byte&quot;&gt;IPv6 헤더 (고정 40 byte)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------+--------------+----------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|Version| Traffic Class|     Flow Label       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+-------+--------------+----------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|    Payload Length    | Next Header | Hop Lim|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+----------------------+-------------+--------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|             Source Address (128 bit)        |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|          Destination Address (128 bit)      |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+---------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IPv6 의 단순화:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;고정 길이&lt;/strong&gt; (옵션은 extension header)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;헤더 checksum 제거&lt;/strong&gt; (L2/L4 가 검증)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fragmentation 없음&lt;/strong&gt; (source 만, router 는 거부)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flow Label&lt;/strong&gt;: QoS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;fragmentation-ipv4&quot;&gt;Fragmentation (IPv4)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;MTU (Maximum Transmission Unit)&lt;/strong&gt;: link 가 한 번에 전송 가능한 최대 크기.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ethernet: 1500 byte&lt;/li&gt;
&lt;li&gt;VPN tunnel: 줄어들 수 있음 (1400 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IP 패킷이 MTU 보다 크면 &lt;strong&gt;fragmentation&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;원본: 4000 byte 패킷&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   ↓ MTU 1500 link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fragment 1: 1500 byte (More Fragments=1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fragment 2: 1500 byte (More Fragments=1, offset=1480)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fragment 3: 1040 byte (More Fragments=0, offset=2960)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;목적지 host 에서 reassembly. 함정:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;한 fragment 손실 = 전체 재전송 필요&lt;/li&gt;
&lt;li&gt;보안: fragment overlap 공격&lt;/li&gt;
&lt;li&gt;IPv6 는 source-only fragmentation (router 는 거부 + ICMPv6 Too Big)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pmtud-path-mtu-discovery&quot;&gt;PMTUD (Path MTU Discovery)&lt;/h3&gt;
&lt;p&gt;Don’t Fragment (DF) flag 켜고 보내며 router 가 “Too Big” ICMP 반환하면 줄임:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;source ─→ link 1500 ─→ tunnel 1400 ─→ destination&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓ DF=1, 1500&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ← ICMP &quot;Frag needed, MTU=1400&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ↓ retry with 1400&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ✓&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ICMP 차단된 환경에선 PMTUD 깨짐 (“PMTUD black hole”). TCP MSS clamping 으로 회피.&lt;/p&gt;
&lt;h2 id=&quot;icmp-internet-control-message-protocol&quot;&gt;ICMP (Internet Control Message Protocol)&lt;/h2&gt;
&lt;p&gt;IP 의 control plane:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Echo Request/Reply&lt;/code&gt;: ping&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Destination Unreachable&lt;/code&gt;: 목적지 도달 불가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TTL Exceeded&lt;/code&gt;: traceroute 의 원리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Fragmentation Needed&lt;/code&gt;: PMTUD&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Redirect&lt;/code&gt;: 더 좋은 route 알림 (보안상 위험)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ICMPv6 는 더 광범위 (NDP, MLD 등 통합).&lt;/p&gt;
&lt;h2 id=&quot;ttl-time-to-live&quot;&gt;TTL (Time To Live)&lt;/h2&gt;
&lt;p&gt;각 router 통과 시 1 씩 감소. 0 도달 시 패킷 폐기 + &lt;code&gt;ICMP Time Exceeded&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;목적: 라우팅 루프 무한 폭주 방지. 일반적 OS 기본값:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux: 64&lt;/li&gt;
&lt;li&gt;Windows: 128&lt;/li&gt;
&lt;li&gt;Cisco router: 255&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;traceroute 가 이걸 활용:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;traceroute&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; google.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TTL=1 보냄 → 1번째 router 가 TTL Exceeded 반환 (그 router 의 IP 노출)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TTL=2 보냄 → 2번째 router 가 반환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 hop 의 IP 와 latency 측정. [[network-cidr-subnetting]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;주요-함정&quot;&gt;주요 함정&lt;/h2&gt;
&lt;h3 id=&quot;함정-1-nat-뒤의-ip&quot;&gt;함정 1: NAT 뒤의 IP&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;192.168.1.10&lt;/code&gt; 에서 외부 접속 시 라우터가 SNAT → public IP. 외부에서 보면 라우터의 IP. inbound 는 port forwarding 필요.&lt;/p&gt;
&lt;h3 id=&quot;함정-2-ipv6-dual-stack-의-happy-eyeballs&quot;&gt;함정 2: IPv6 dual-stack 의 happy eyeballs&lt;/h3&gt;
&lt;p&gt;브라우저는 IPv4 / IPv6 둘 다 시도해 빠른 쪽 사용. IPv6 라우팅이 깨졌어도 IPv4 fallback 되므로 사용자는 모름 → 진단 어려움.&lt;/p&gt;
&lt;h3 id=&quot;함정-3-mtu-mismatch&quot;&gt;함정 3: MTU mismatch&lt;/h3&gt;
&lt;p&gt;Docker / Kubernetes 의 overlay network 가 보통 MTU 1450 (encapsulation overhead). 호스트가 1500 으로 보내면 성능 저하 / 패킷 손실.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 진단&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ping&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -M&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; do&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1472&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; google.com&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   # 1472 + 28 (IP+ICMP) = 1500&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;함정-4-ipv4-exhaustion-과-cgnat&quot;&gt;함정 4: IPv4 exhaustion 과 CGNAT&lt;/h3&gt;
&lt;p&gt;ISP 의 Carrier-Grade NAT 환경에서 같은 IP 가 수천 명 공유. P2P / inbound 어려움. IPv6 보급 가속 이유.&lt;/p&gt;
&lt;h2 id=&quot;함정과-베스트-프랙티스&quot;&gt;함정과 베스트 프랙티스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;항상 dual-stack 고려&lt;/strong&gt;: IPv4 + IPv6 동시 지원&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MTU 명시&lt;/strong&gt;: container / VPN 환경에서 1500 가정 위험&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;traceroute / mtr 활용&lt;/strong&gt;: 경로 진단의 기본&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL 짧으면 우회&lt;/strong&gt;: anycast / CDN 에서 같은 IP 가 여러 위치&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PMTUD 깨질 때 MSS clamping&lt;/strong&gt;: TCP 헤더에서 협상 가능한 최대 크기&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;private IP 충돌 회피&lt;/strong&gt;: 사내 다중 사이트 시 RFC 1918 범위 신중 할당&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPv6 ULA&lt;/strong&gt;: 사설망에선 &lt;code&gt;fd00::/8&lt;/code&gt; 권장 (link-local 은 라우팅 안 됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[tcp]]&lt;/li&gt;
&lt;li&gt;[[udp]]&lt;/li&gt;
&lt;li&gt;[[quic]]&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting]]&lt;/li&gt;
&lt;li&gt;[[network-dns]]&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>ip</category><category>ipv4</category><category>ipv6</category><category>l3</category><author>koa (김신건)</author></item><item><title>TLS / SSL</title><link>https://shinkeonkim.com/wiki/network/tls/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/tls/</guid><pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TLS&lt;/strong&gt; (Transport Layer Security)는 [[TCP]] (또는 [[UDP]] 위 [[QUIC]]) 위에서 동작하는 암호화·인증 프로토콜이다. &lt;strong&gt;SSL&lt;/strong&gt; 은 TLS 의 전신 (Netscape 1995, 더 이상 사용되지 않음).&lt;/p&gt;
&lt;p&gt;웹의 HTTPS 가 바로 “HTTP + TLS”. 인증서 검증, 키 교환, 암호화된 데이터 전송 모두 TLS 가 담당한다.&lt;/p&gt;
&lt;h2 id=&quot;버전-역사&quot;&gt;버전 역사&lt;/h2&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;버전&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;출시&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;상태&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;SSL 1.0&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;1994&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;내부 사용, 공개 안 됨&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;SSL 2.0&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;1995&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;폐기 (취약점 다수)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;SSL 3.0&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;1996&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;폐기 (POODLE 공격)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.0&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;1999&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;폐기 (BEAST 공격)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.1&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2006&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;폐기&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.2&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2008&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;현재도 널리 사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TLS 1.3&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;2018&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;권장&lt;/strong&gt;, [[QUIC]] 에 통합됨&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
“SSL 인증서” 라는 용어는 관습적으로 남아있지만, 실제로 사용되는 건 TLS 다. SSL 1.0~3.0 은 모두 폐기되었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tls-가-하는-일&quot;&gt;TLS 가 하는 일&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;서버 인증&lt;/strong&gt;: 인증서로 “당신이 정말 example.com 입니까?” 검증&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;키 교환&lt;/strong&gt;: 양측이 공유할 대칭키를 안전하게 생성 (ECDHE 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;대칭 암호화&lt;/strong&gt;: 이후 데이터를 AES-GCM, ChaCha20-Poly1305 등으로 암호화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;무결성 검증&lt;/strong&gt;: 메시지 변조 감지 (AEAD 알고리즘에 내장)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt;
&lt;h3 id=&quot;비대칭-vs-대칭-암호의-역할-분리&quot;&gt;비대칭 vs 대칭 암호의 역할 분리&lt;/h3&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;알고리즘 예&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;사용 시점&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;특성&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;비대칭 (공개키)&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;RSA, ECDSA, ECDHE&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;핸드셰이크 단계&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;느림, 인증서 검증·키 교환에만 사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;대칭&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;AES-GCM, ChaCha20&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;응용 데이터 단계&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;빠름, 핸드셰이크에서 합의한 세션 키로 암호화&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;핸드셰이크에서 비대칭으로 안전하게 세션 키를 만들고, 이후 모든 데이터는 빠른 대칭 암호로 처리한다.&lt;/p&gt;
&lt;h3 id=&quot;forward-secrecy&quot;&gt;Forward Secrecy&lt;/h3&gt;
&lt;p&gt;ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) 기반 키 교환은 매 세션마다 새 키를 만든다. 서버의 장기 비밀키가 나중에 유출되어도 과거 세션은 복호화 불가. TLS 1.3 은 이런 ephemeral 키 교환만 허용한다.&lt;/p&gt;
&lt;h3 id=&quot;aead-authenticated-encryption-with-associated-data&quot;&gt;AEAD (Authenticated Encryption with Associated Data)&lt;/h3&gt;
&lt;p&gt;암호화와 무결성 검증을 한 번에 수행하는 알고리즘. 별도의 MAC 계산 없이 변조 감지 가능. TLS 1.3 은 AEAD 만 허용.&lt;/p&gt;
&lt;h2 id=&quot;tls-12-full-handshake-2-rtt&quot;&gt;TLS 1.2 Full Handshake (2-RTT)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;단계별 흐름:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ClientHello&lt;/strong&gt;: 클라이언트가 지원하는 cipher suite 목록 + random 값을 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ServerHello + Certificate + ServerKeyExchange + ServerHelloDone&lt;/strong&gt;: 서버가 cipher 를 고르고, 인증서 체인 + 서버 측 키 교환 파라미터를 한 번에 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClientKeyExchange + ChangeCipherSpec + Finished&lt;/strong&gt;: 클라이언트가 pre-master 비밀을 보내고, 이후 대칭키 모드로 전환한다는 신호와 함께 Finished 메시지를 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ChangeCipherSpec + Finished&lt;/strong&gt;: 서버도 대칭키 모드로 전환했음을 알리고 Finished 로 핸드셰이크 무결성을 검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;총 &lt;strong&gt;2 RTT&lt;/strong&gt; 소요. 응용 데이터 전송은 그 이후부터.&lt;/p&gt;
&lt;h2 id=&quot;tls-13-handshake-1-rtt&quot;&gt;TLS 1.3 Handshake (1-RTT)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TLS 1.3 의 핵심 개선:&lt;/p&gt;
&lt;h3 id=&quot;keyshare-동봉&quot;&gt;KeyShare 동봉&lt;/h3&gt;
&lt;p&gt;ClientHello 에 클라이언트가 미리 계산한 DH 공개키 후보 (&lt;code&gt;KeyShare&lt;/code&gt;) 를 동봉한다. 서버는 같은 cipher / 그룹을 선택하면 즉시 같은 세션 키를 도출할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;인증서를-핸드셰이크-안에서-암호화&quot;&gt;인증서를 핸드셰이크 안에서 암호화&lt;/h3&gt;
&lt;p&gt;TLS 1.2 에서는 인증서가 평문으로 전송되었으나 TLS 1.3 에서는 ServerHello 직후부터 핸드셰이크 메시지도 (handshake traffic key 로) 암호화된다. 사용자가 어떤 도메인을 방문하는지 외부에서 보기 어려워진다. (단, SNI 는 여전히 평문, ECH 로 보완 중)&lt;/p&gt;
&lt;h3 id=&quot;0-rtt-resumption&quot;&gt;0-RTT Resumption&lt;/h3&gt;
&lt;p&gt;이전 세션의 PSK (Pre-Shared Key) 또는 세션 티켓을 첨부하면, 첫 패킷에 응용 데이터를 동봉할 수 있다. RTT 0 으로 응답 시작 가능.&lt;/p&gt;
&lt;p&gt;⚠️ 0-RTT 는 &lt;strong&gt;replay attack&lt;/strong&gt; 위험이 있다. 같은 데이터가 재전송될 수 있으므로 멱등(idempotent) 요청 (GET 등) 에만 권장. 결제 같은 부작용 있는 요청은 1-RTT 로 강제해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;cipher-suite-단순화&quot;&gt;Cipher Suite 단순화&lt;/h3&gt;
&lt;p&gt;TLS 1.2 의 약한·취약 cipher (RC4, 3DES, CBC mode 등) 가 모두 제거되었다. 5개의 AEAD cipher suite 만 남았다.&lt;/p&gt;
&lt;h2 id=&quot;인증서-체인-검증&quot;&gt;인증서 체인 검증&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;브라우저는 서버가 보낸 인증서가 신뢰할 수 있는지 다음 절차로 검증한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;도메인 일치 확인&lt;/strong&gt;: 인증서의 SAN (Subject Alternative Name) 또는 CN 이 접속한 도메인과 일치하는가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;만료 확인&lt;/strong&gt;: &lt;code&gt;notBefore&lt;/code&gt; ~ &lt;code&gt;notAfter&lt;/code&gt; 범위 안에 있는가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;체인 서명 검증&lt;/strong&gt;: Leaf 인증서의 서명을 Intermediate CA 의 공개키로 검증, Intermediate 의 서명을 Root CA 의 공개키로 검증&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Root 일치 확인&lt;/strong&gt;: 체인의 최상위가 OS / 브라우저의 truststore 에 내장된 신뢰된 Root CA 인가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;폐기 여부 확인 (선택)&lt;/strong&gt;: OCSP (Online Certificate Status Protocol) 또는 CRL (Certificate Revocation List) 로 인증서가 revoke 되지 않았는지&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;서버는 보통 &lt;strong&gt;Leaf + Intermediate&lt;/strong&gt; 만 전송한다. Root 는 클라이언트가 이미 truststore 에 가지고 있다.&lt;/p&gt;
&lt;h3 id=&quot;인증서-발급-흐름&quot;&gt;인증서 발급 흐름&lt;/h3&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;단계&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;누가&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;무엇을&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;1. CSR 생성&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서버 운영자&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;도메인 + 공개키로 CSR (Certificate Signing Request) 만든다&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;2. 도메인 소유 증명&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;운영자 → CA&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;DNS TXT 레코드 (DNS-01) 또는 HTTP &lt;code&gt;/.well-known/&lt;/code&gt; (HTTP-01)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;3. 서명&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;CA&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;검증 통과 시 CA 의 비밀키로 Leaf 인증서에 서명&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;4. 갱신&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서버 운영자&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Let’s Encrypt 는 90일, 보통 60일 전 자동 갱신&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Let’s Encrypt, Cloudflare 등이 무료 + 자동화된 TLS 인증서를 제공하면서 HTTPS 가 사실상 표준이 되었다.&lt;/p&gt;
&lt;h2 id=&quot;quic-와-tls&quot;&gt;[[QUIC]] 와 TLS&lt;/h2&gt;
&lt;p&gt;QUIC 은 TLS 1.3 을 &lt;strong&gt;전송 계층에 통합&lt;/strong&gt;했다.&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;프로토콜 조합&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;핸드셰이크 RTT&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TCP + TLS 1.2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP (1 RTT) + TLS (2 RTT) = 3 RTT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TCP + TLS 1.3&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP (1 RTT) + TLS (1 RTT) = 2 RTT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;QUIC (TLS 1.3 통합)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 RTT (full) 또는 0 RTT (resume)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;QUIC 은 핸드셰이크와 전송을 분리하지 않고, 같은 패킷 안에서 처리한다. 결과적으로 HTTP/3 는 첫 페이지 로딩이 HTTP/2 보다 빠르다.&lt;/p&gt;
&lt;h2 id=&quot;약점과-주의점&quot;&gt;약점과 주의점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;인증서 관리 비용&lt;/strong&gt;: 갱신 자동화 (ACME / certbot / cert-manager) 가 필수. 만료 사고는 흔하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;중간자 (MITM) 위험&lt;/strong&gt;: 사내 프록시나 일부 디바이스가 truststore 에 자체 CA 를 심어 트래픽을 가로채는 경우가 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0-RTT replay&lt;/strong&gt;: TLS 1.3 의 0-RTT 는 부작용 있는 요청에 사용 금지.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SNI 노출&lt;/strong&gt;: 어떤 도메인에 접속하는지는 ClientHello 의 SNI 에 평문으로 노출됨. ECH (Encrypted Client Hello) 로 점진적 해결 중.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신뢰 모델 자체의 한계&lt;/strong&gt;: 약 150개 Root CA 중 하나라도 침해되면 임의의 도메인을 위장할 수 있음. Certificate Transparency (CT) 로 발급 로그 공개 의무화.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>security</category><category>protocol</category><category>cryptography</category><author>koa (김신건)</author></item><item><title>DTLS</title><link>https://shinkeonkim.com/wiki/network/dtls/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/dtls/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;DTLS&lt;/strong&gt; (Datagram Transport Layer Security)는 [[UDP]] 위에서 동작하는 [[TLS]] 의 변형. 데이터그램 (UDP 패킷) 기반 통신에 보안을 제공한다.&lt;/p&gt;
&lt;p&gt;[[QUIC]] 와 WebRTC DataChannel 등이 사용한다. TLS 와 거의 같은 보안 보장 (서버 인증, 키 교환, 암호화, 무결성)을 제공하지만 UDP 의 특성 (비순서, 손실, 비신뢰)을 다룬다.&lt;/p&gt;
&lt;h2 id=&quot;왜-tls-가-아닌-dtls-인가&quot;&gt;왜 TLS 가 아닌 DTLS 인가&lt;/h2&gt;
&lt;p&gt;TLS 는 [[TCP]] 의 “순서 보장된 신뢰성 있는 stream” 위에서 설계되었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLS 핸드셰이크 메시지는 정확한 순서로 수신될 것을 가정&lt;/li&gt;
&lt;li&gt;TLS record 의 sequence number 는 암묵적 (보내지 않음, TCP 가 순서 보장)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UDP 환경에서는 이런 가정이 깨진다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;패킷 순서가 바뀜&lt;/li&gt;
&lt;li&gt;패킷이 손실됨&lt;/li&gt;
&lt;li&gt;패킷이 중복됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DTLS 는 이를 다루기 위해:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;TLS&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;DTLS&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;핸드셰이크 순서 가정&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;핸드셰이크 메시지 번호화 + 재전송&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;sequence number 암묵&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;sequence number 명시적 (record 헤더에 포함)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;손실 가정 안 함&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;record 손실/중복 허용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;record fragmentation X&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;record fragmentation 지원 (MTU 대응)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;버전&quot;&gt;버전&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;버전&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;출시&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;기반 TLS&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;DTLS 1.0&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2006&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;DTLS 1.2&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2012&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;DTLS 1.3&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;2022&lt;/strong&gt; (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9147&quot;&gt;RFC 9147&lt;/a&gt;)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.3&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;DTLS 1.3 은 TLS 1.3 의 1-RTT / 0-RTT 핸드셰이크 개선을 그대로 가져왔다.&lt;/p&gt;
&lt;h2 id=&quot;사용처&quot;&gt;사용처&lt;/h2&gt;
&lt;h3 id=&quot;1-webrtc-datachannel&quot;&gt;1. WebRTC DataChannel&lt;/h3&gt;
&lt;p&gt;브라우저 간 P2P 데이터 채널은 모두 DTLS 로 암호화된다. 시그널링으로 키 교환 fingerprint 를 미리 합의 → DTLS 핸드셰이크로 키 확립 → SCTP 데이터 송수신.&lt;/p&gt;
&lt;h3 id=&quot;2-quic-간접적&quot;&gt;2. [[QUIC]] (간접적)&lt;/h3&gt;
&lt;p&gt;QUIC 은 TLS 1.3 핸드셰이크를 직접 통합 (DTLS 가 아닌). 그러나 비슷한 “UDP 위 보안” 문제를 같은 방식으로 해결한다.&lt;/p&gt;
&lt;h3 id=&quot;3-vpn--iot&quot;&gt;3. VPN / IoT&lt;/h3&gt;
&lt;p&gt;OpenVPN UDP 모드, CoAP (IoT 프로토콜) 등이 DTLS 사용.&lt;/p&gt;
&lt;h3 id=&quot;4-미디어-srtp-dtls&quot;&gt;4. 미디어 (SRTP-DTLS)&lt;/h3&gt;
&lt;p&gt;WebRTC 의 미디어 채널은 SRTP 로 암호화되지만, SRTP 의 키는 DTLS 핸드셰이크로 합의한다 (DTLS-SRTP).&lt;/p&gt;
&lt;h2 id=&quot;dtls-의-한계&quot;&gt;DTLS 의 한계&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;재전송 비용&lt;/strong&gt;: 핸드셰이크 패킷 손실 시 재전송 → 초기 연결이 TCP+TLS 보다 느릴 수 있음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MTU 의존&lt;/strong&gt;: UDP 패킷이 MTU 를 넘으면 IP fragmentation → 큰 핸드셰이크 record 는 분할 필요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DoS 공격 노출&lt;/strong&gt;: UDP 는 spoofing 이 쉬워 핸드셰이크 시 cookie 메커니즘으로 보호 (HelloVerifyRequest)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>security</category><category>protocol</category><category>cryptography</category><author>koa (김신건)</author></item><item><title>Head-of-Line Blocking</title><link>https://shinkeonkim.com/wiki/network/head-of-line-blocking/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/head-of-line-blocking/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Head-of-Line Blocking (HOLB)&lt;/strong&gt; 은 처리 순서가 정해진 큐에서 &lt;strong&gt;맨 앞 항목의 지연이 그 뒤 항목 전체의 지연으로 전파되는 현상&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;네트워크 프로토콜에서는 여러 층에서 발생할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;http11-의-hol-blocking&quot;&gt;HTTP/1.1 의 HOL Blocking&lt;/h2&gt;
&lt;p&gt;한 TCP 연결로 여러 요청을 순차 처리 (pipelining 사용해도), 첫 요청의 응답이 느리면 그 뒤 요청들도 모두 대기.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Request 1 →  (서버에서 5초 처리 중)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Request 2 →  (대기)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Request 3 →  (대기)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Response 1 ←&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Response 2 ←  ← 5초 뒤에야 처리 시작&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Response 3 ←&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;브라우저가 도메인당 6개 TCP 연결을 만드는 이유의 일부.&lt;/p&gt;
&lt;h2 id=&quot;http2-가-해결한-것&quot;&gt;HTTP/2 가 해결한 것&lt;/h2&gt;
&lt;p&gt;HTTP/2의 multiplexing은 &lt;strong&gt;HTTP 레벨의 HOL Blocking&lt;/strong&gt;을 해결한다. 한 TCP 연결에서 stream ID로 구분된 frame을 인터리브해 송수신.&lt;/p&gt;
&lt;p&gt;그러나 &lt;strong&gt;TCP 레벨의 HOL Blocking&lt;/strong&gt;은 여전히 남는다. 한 TCP 패킷이 손실되면, 그 패킷이 어느 stream에 속하든 TCP는 순서를 유지하기 위해 모든 stream의 후속 패킷을 대기시킨다.&lt;/p&gt;
&lt;h2 id=&quot;quic-가-해결한-것&quot;&gt;[[QUIC]] 가 해결한 것&lt;/h2&gt;
&lt;p&gt;QUIC은 stream 격리를 본격적으로 구현한다. UDP 위에서 각 stream이 자기 sequence number와 ACK을 따로 관리. 한 stream의 패킷 손실은 다른 stream에 영향 없음.&lt;/p&gt;
&lt;p&gt;→ HTTP/3 (QUIC 위에서 동작)이 TCP의 HOL Blocking을 근본적으로 해결.&lt;/p&gt;
&lt;h2 id=&quot;다른-사례&quot;&gt;다른 사례&lt;/h2&gt;
&lt;p&gt;HOL Blocking은 네트워크 외 일반 큐 처리 시스템에서도 발생한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터베이스의 단일 큐 워커&lt;/li&gt;
&lt;li&gt;메시지 브로커의 partition 단위 순차 처리&lt;/li&gt;
&lt;li&gt;멀티스레드 작업에서 ordered 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;해결책은 보통: &lt;strong&gt;독립 처리 단위로 분할&lt;/strong&gt; (sharding, partitioning, stream 격리).&lt;/p&gt;</content:encoded><category>network</category><category>http</category><category>protocol</category><category>performance</category><author>koa (김신건)</author></item><item><title>HPACK</title><link>https://shinkeonkim.com/wiki/network/hpack/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/hpack/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HPACK&lt;/strong&gt; (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7541&quot;&gt;RFC 7541&lt;/a&gt;)은 HTTP/2 에서 요청·응답 헤더를 압축하기 위해 설계된 방식.&lt;/p&gt;
&lt;p&gt;HTTP/1.1 의 헤더는 매 요청마다 텍스트로 반복 전송되었다 (한 페이지 로드에 수십 KB가 헤더만으로 사용). HPACK 은 이를 동적·정적 인덱스 테이블로 줄인다.&lt;/p&gt;
&lt;h2 id=&quot;압축-원리&quot;&gt;압축 원리&lt;/h2&gt;
&lt;h3 id=&quot;static-table&quot;&gt;Static Table&lt;/h3&gt;
&lt;p&gt;자주 쓰이는 헤더 이름·값 조합을 미리 정의한 고정 인덱스 (61개).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1: :authority&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2: :method GET&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3: :method POST&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4: :path /&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5: :path /index.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;요청에 &lt;code&gt;:method GET&lt;/code&gt; 이 있으면 그냥 인덱스 &lt;code&gt;2&lt;/code&gt; 한 바이트만 전송.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-table&quot;&gt;Dynamic Table&lt;/h3&gt;
&lt;p&gt;연결 단위로 유지되는 헤더 캐시. 처음 보낸 헤더는 인덱스로 등록되고, 다음 요청부터는 인덱스로만 전송.&lt;/p&gt;
&lt;h3 id=&quot;huffman-인코딩&quot;&gt;Huffman 인코딩&lt;/h3&gt;
&lt;p&gt;문자열로 전송해야 할 경우 Huffman 코드로 압축. ASCII에 최적화되어 보통 ~30% 추가 절약.&lt;/p&gt;
&lt;h2 id=&quot;효과&quot;&gt;효과&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;시나리오&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/1.1&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/2 + HPACK&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;페이지 로드 (50 요청, 각 헤더 ~700B)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;~35 KB 헤더&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;~3-5 KB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;모바일 네트워크 (TCP slow start 단계)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;큰 영향&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;작은 영향&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;동일 호스트 반복 요청&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;반복 비용&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;거의 0&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;HTTP/2 의 multiplexing 외의 두 번째 큰 성능 이득.&lt;/p&gt;
&lt;h2 id=&quot;보안-이슈&quot;&gt;보안 이슈&lt;/h2&gt;
&lt;p&gt;HPACK 의 dynamic table 은 &lt;strong&gt;CRIME/BREACH 같은 사이드 채널 공격&lt;/strong&gt; 방어를 위해 일부 제약을 둔다 (예: 압축률을 외부에서 측정해 헤더 값을 추측하는 공격). RFC 7541 §7 의 보안 고려사항 참조.&lt;/p&gt;
&lt;p&gt;HTTP/3 의 [[QUIC]] 환경에서는 HPACK 의 stream 간 의존성 문제를 해결한 &lt;strong&gt;QPACK&lt;/strong&gt; (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9204&quot;&gt;RFC 9204&lt;/a&gt;) 이 사용된다.&lt;/p&gt;</content:encoded><category>network</category><category>http</category><category>http2</category><category>compression</category><author>koa (김신건)</author></item><item><title>QUIC</title><link>https://shinkeonkim.com/wiki/network/quic/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/quic/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;QUIC&lt;/strong&gt; (Quick UDP Internet Connections)은 Google이 2012년 처음 제안하고 2021년 &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9000&quot;&gt;RFC 9000&lt;/a&gt;으로 표준화된 UDP 기반 전송 프로토콜이다. HTTP/3의 기반이 된다.&lt;/p&gt;
&lt;p&gt;기존 TCP가 가지는 문제들을 OS 커널 수정 없이(=사용자 영역에서) 해결하기 위해 UDP 위에 구현되었다.&lt;/p&gt;
&lt;h2 id=&quot;핵심-특성&quot;&gt;핵심 특성&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stream 격리&lt;/strong&gt;: 한 연결 안에 여러 독립적인 stream. 한 stream의 패킷 손실이 다른 stream에 영향을 주지 않음 → TCP의 [[Head-of-Line Blocking]] 문제 해결&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS 1.3 통합&lt;/strong&gt;: 전송과 암호화 핸드셰이크가 분리되어 있지 않다. 최초 연결도 1-RTT, 재접속은 0-RTT 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection ID&lt;/strong&gt;: IP나 포트가 바뀌어도 (와이파이 ↔ 셀룰러 전환) 같은 ID로 같은 연결 유지 (Connection Migration)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;사용자 영역 구현&lt;/strong&gt;: 브라우저·서버가 직접 구현 → OS 업데이트 없이 새 기능 배포 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tcp-대비-장점&quot;&gt;TCP 대비 장점&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;TCP&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;QUIC&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;전송&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;OS 커널&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;사용자 영역 (라이브러리)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HOL Blocking&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;있음&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Stream 단위로 격리&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;최초 핸드셰이크&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 3-way + TLS = 2-3 RTT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 RTT (TLS 통합)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;재접속&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 RTT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0 RTT (TLS 1.3 세션 티켓)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Connection 식별&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;(IP, Port) 튜플&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Connection ID&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;IP 변경 시&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;연결 끊김&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Migration으로 유지&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;약점주의점&quot;&gt;약점·주의점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;UDP 차단&lt;/strong&gt;: 일부 기업 방화벽은 UDP를 막거나 throttling함 → HTTP/3 ↔ HTTP/2 fallback 필요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 부담&lt;/strong&gt;: 사용자 영역 구현이라 패킷 처리 CPU 비용이 TCP보다 클 수 있음 (커널 우회 효과는 일부)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT rebinding&lt;/strong&gt;: Connection Migration이 NAT 동작과 충돌할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;사용-사례&quot;&gt;사용 사례&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/3 (Google, Facebook, Cloudflare 등 대형 CDN)&lt;/li&gt;
&lt;li&gt;WebTransport (브라우저 양방향 stream 신 API)&lt;/li&gt;
&lt;li&gt;일부 게임·실시간 미디어 프로토콜&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>http</category><category>protocol</category><author>koa (김신건)</author></item><item><title>TCP</title><link>https://shinkeonkim.com/wiki/network/tcp/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/tcp/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TCP&lt;/strong&gt; (Transmission Control Protocol)는 신뢰성 있는 연결 지향 전송 계층 프로토콜이다. &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9293&quot;&gt;RFC 9293&lt;/a&gt; 으로 정의되어 있다. HTTP/1.1·2, SMTP, SSH, FTP 등 인터넷의 대부분의 응용 프로토콜이 TCP 위에서 동작한다.&lt;/p&gt;
&lt;p&gt;[[UDP]] 와 대비된다, UDP 가 “보내고 잊는” 방식이라면 TCP 는 “확실히 도착한 순서대로 전달”.&lt;/p&gt;
&lt;h2 id=&quot;핵심-특성&quot;&gt;핵심 특성&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;연결 지향(Connection-oriented)&lt;/strong&gt;: 통신 전에 3-way handshake 로 연결 확립&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;순서 보장(In-order delivery)&lt;/strong&gt;: 패킷에 sequence number 부여, 수신측에서 재정렬&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신뢰성(Reliable)&lt;/strong&gt;: ACK 미수신 시 재전송, 손실 자동 복구&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;흐름 제어(Flow Control)&lt;/strong&gt;: receiver window 로 수신측 처리 속도 맞춤&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;혼잡 제어(Congestion Control)&lt;/strong&gt;: 네트워크 혼잡 시 송신 속도 조절 (TCP Reno, Cubic, BBR 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-way-handshake-연결-수립&quot;&gt;3-way Handshake (연결 수립)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1 RTT 소요. [[TLS]] 까지 추가하면 보통 2-3 RTT.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Client                          Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   │── SYN  (seq=x)  ────────────→ │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   │ ←──── SYN-ACK (seq=y, ack=x+1) ─── │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   │── ACK (ack=y+1) ────────────→ │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   ←── 데이터 송수신 가능 ──→&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4-way-handshake-연결-종료&quot;&gt;4-way Handshake (연결 종료)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 방향 (C→S, S→C)을 독립적으로 종료하므로 4단계가 필요. Client 는 마지막에 TIME_WAIT 상태로 2×MSL 동안 대기, 지연 패킷이 새 연결을 오염시키지 않게 하기 위함.&lt;/p&gt;
&lt;h2 id=&quot;tcp-의-한계&quot;&gt;TCP 의 한계&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;문제&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;해결 시도&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;연결 비용&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;매번 3-way handshake&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP keep-alive&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;[[Head-of-Line Blocking]]&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;한 패킷 손실이 같은 연결의 모든 stream 영향&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP/2 가 부분 해결, [[QUIC]] 가 완전 해결&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;연결 변경 시 끊김&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;IP/Port 바뀌면 새 연결&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;[[QUIC]] Connection Migration&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;커널 종속&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;OS 업데이트 없이는 개선 불가&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;[[QUIC]] 사용자 영역 구현&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;사용처&quot;&gt;사용처&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/1.1, HTTP/2 (HTTPS 포함)&lt;/li&gt;
&lt;li&gt;WebSocket (HTTP Upgrade 후 TCP 그대로 사용)&lt;/li&gt;
&lt;li&gt;SSH, SMTP, FTP&lt;/li&gt;
&lt;li&gt;Database 프로토콜 대부분 (PostgreSQL, MySQL wire protocol)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>protocol</category><category>transport-layer</category><author>koa (김신건)</author></item><item><title>UDP</title><link>https://shinkeonkim.com/wiki/network/udp/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/udp/</guid><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;UDP&lt;/strong&gt; (User Datagram Protocol)는 비연결·비신뢰 전송 계층 프로토콜이다. &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc768&quot;&gt;RFC 768&lt;/a&gt; (1980) 로 정의되었다. [[TCP]] 와 대비된다.&lt;/p&gt;
&lt;p&gt;“보내고 잊는다(fire-and-forget)”, 송신측은 패킷을 던지고 끝, 받았는지 안 받았는지 확인하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;핵심-특성&quot;&gt;핵심 특성&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;비연결(Connectionless)&lt;/strong&gt;: 핸드셰이크 없음, 즉시 송신&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비순서&lt;/strong&gt;: 수신 순서 보장 없음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비신뢰&lt;/strong&gt;: 손실되어도 재전송 없음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;가벼움&lt;/strong&gt;: 헤더 8 bytes (TCP 의 20+ bytes 와 비교)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;단순함&lt;/strong&gt;: 흐름 제어 / 혼잡 제어 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;헤더-구조&quot;&gt;헤더 구조&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UDP 패킷 헤더는 총 &lt;strong&gt;8 bytes&lt;/strong&gt; (TCP 의 20+ bytes 와 대비).&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;필드&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;크기&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Source Port&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;송신측 포트&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Destination Port&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;수신측 포트&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Length&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;헤더 + 데이터 길이&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Checksum&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;2 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;무결성 검증&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;전송-방식-fire-and-forget&quot;&gt;전송 방식, Fire-and-Forget&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UDP 는 핸드셰이크 없이 즉시 송신한다. ACK 없음, 손실되어도 송신측은 알 수 없다.&lt;/p&gt;
&lt;h2 id=&quot;언제-udp-를-쓰는가&quot;&gt;언제 UDP 를 쓰는가&lt;/h2&gt;
&lt;p&gt;신뢰성보다 &lt;strong&gt;저지연·저오버헤드&lt;/strong&gt;가 더 중요할 때.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DNS&lt;/strong&gt;: 작은 질의·응답, 응답 안 오면 재시도하면 됨&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실시간 미디어&lt;/strong&gt;: 비디오 통화에서 1 프레임 손실은 재전송할 시간이 없음, 다음 프레임이 더 중요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;게임&lt;/strong&gt;: 위치 업데이트가 1 패킷 잃어도 다음 패킷이 곧 옴&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DHCP, NTP, SNMP&lt;/strong&gt;: 단순한 요청·응답&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;[[QUIC]]&lt;/strong&gt; (그리고 HTTP/3): UDP 위에서 신뢰성·순서 보장을 사용자 영역에서 직접 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;udp-위에-신뢰성을-다시-만든다는-역설&quot;&gt;”UDP 위에 신뢰성을 다시 만든다”는 역설&lt;/h2&gt;
&lt;p&gt;QUIC, WebRTC SCTP, 게임 엔진의 reliable UDP, 모두 UDP 위에서 TCP 와 유사한 기능을 직접 구현한다. 왜?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TCP 의 한계를 우회&lt;/strong&gt;: OS 커널 의존, [[Head-of-Line Blocking]], 연결 변경 시 끊김&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;선택적 신뢰성&lt;/strong&gt;: 메시지마다 “ordered / unordered, reliable / unreliable” 선택 가능 (SCTP 의 partial reliability)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;빠른 진화&lt;/strong&gt;: 사용자 영역 구현이라 OS 업데이트 없이 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UDP 의 “단순함”은 양날의 검, 직접 구현해야 하지만 그만큼 유연하다.&lt;/p&gt;</content:encoded><category>network</category><category>protocol</category><category>transport-layer</category><author>koa (김신건)</author></item></channel></rss>