<?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] 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, 16 Jul 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;서버 -&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{&quot;요청이 Simple?&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|예| S[&quot;즉시 보냄&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|아니오| P[&quot;OPTIONS preflight 먼저&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S --&gt; CHECK1[&quot;응답의 ACAO 검증&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    P --&gt; CHECK2[&quot;preflight 응답 검증&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CHECK2 --&gt;|허용| Send[&quot;실제 요청 전송&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CHECK2 --&gt;|거절| Block[&quot;브라우저 차단&quot;]&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;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; 는 RFC 위반. 브라우저가 거절. credentials 가 필요하면 &lt;em&gt;명시적 origin&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;서버-구현-가이드&quot;&gt;서버 구현 가이드&lt;/h2&gt;
&lt;h3 id=&quot;express-nodejs&quot;&gt;Express (Node.js)&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;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;h3 id=&quot;spring-boot&quot;&gt;Spring Boot&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;java&quot;&gt;&lt;code&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;Configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; CorsConfig&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;Bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; WebMvcConfigurer &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;corsConfigurer&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:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; WebMvcConfigurer&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;Override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            public&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; addCorsMappings&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(CorsRegistry &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;registry&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;                registry.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addMapping&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/api/**&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;                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;allowedOrigins&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;https://app.example.com&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;https://staging.example.com&quot;&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 style=&quot;color:#B392F0&quot;&gt;allowedMethods&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;PUT&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;DELETE&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;                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;allowedHeaders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Content-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;Authorization&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;                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;exposedHeaders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;X-Total-Count&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;                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;allowCredentials&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&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;                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;maxAge&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&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;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 style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;nginx&quot;&gt;nginx&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;nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; /api/ &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; ($request_method &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;OPTIONS) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-Origin  $http_origin;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-Methods &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;GET, POST, PUT, DELETE, OPTIONS&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;        add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-Headers &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Content-Type, Authorization&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;        add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-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:#F97583&quot;&gt;        add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Max-Age &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:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 204&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;    add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-Origin  $http_origin always;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Access-Control-Allow-Credentials &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    proxy_pass &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;http://backend;&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;[!WARNING]
nginx 에서 &lt;code&gt;$http_origin&lt;/code&gt; 를 그대로 반영하면 &lt;em&gt;모든 origin 허용&lt;/em&gt; 과 같음. 허용된 origin 목록을 &lt;code&gt;map&lt;/code&gt; 디렉티브로 먼저 검증해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;동적-origin-허용-패턴&quot;&gt;동적 Origin 허용 패턴&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Allow-Origin&lt;/code&gt; 에 &lt;em&gt;하나의 값만&lt;/em&gt; 담을 수 있다. 여러 origin 을 허용할 때:&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;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; ALLOWED_ORIGINS&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; Set&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;https://app.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:#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:#9ECBFF&quot;&gt;  &apos;http://localhost:3000&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:#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:#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:#FFAB70&quot;&gt;next&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; origin&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; req.headers.origin;&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; (&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ALLOWED_ORIGINS&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;has&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(origin)) {&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;setHeader&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Access-Control-Allow-Origin&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, origin);&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;setHeader&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Vary&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Origin&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);       &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// CDN 캐시 분리&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:#B392F0&quot;&gt;  next&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;p&gt;&lt;code&gt;Vary: Origin&lt;/code&gt; 이 없으면 CDN 이 &lt;em&gt;다른 origin 의 응답&lt;/em&gt; 을 캐시해서 제공. 반드시 추가.&lt;/p&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;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;cross-origin fetch 허용&lt;/td&gt;&lt;td&gt;cross-origin 의 위조 요청 방어&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;방향&lt;/td&gt;&lt;td&gt;브라우저 -&gt; 서버 &lt;em&gt;허용&lt;/em&gt;&lt;/td&gt;&lt;td&gt;브라우저 -&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|CSRF]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;개발-환경-팁&quot;&gt;개발 환경 팁&lt;/h2&gt;
&lt;p&gt;로컬 개발 시 &lt;code&gt;localhost:3000&lt;/code&gt; (프론트) -&gt; &lt;code&gt;localhost:8080&lt;/code&gt; (백엔드) 도 cross-origin.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;프록시&lt;/strong&gt;: &lt;code&gt;vite.config.ts&lt;/code&gt; 의 &lt;code&gt;server.proxy&lt;/code&gt; 로 &lt;code&gt;/api/*&lt;/code&gt; 를 백엔드로 라우팅 (브라우저 관점에서 same-origin)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;백엔드 allowOrigin&lt;/strong&gt;: &lt;code&gt;http://localhost:3000&lt;/code&gt; 을 명시적으로 허용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS 불일치&lt;/strong&gt;: 로컬에서 &lt;code&gt;http://&lt;/code&gt;, 프로덕션에서 &lt;code&gt;https://&lt;/code&gt; 를 혼용하면 다른 origin&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;&lt;code&gt;Allow-Origin: *&lt;/code&gt; 를 모든 경우에 박음&lt;/strong&gt;: credentials 모드에서 동작 안 함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;preflight 응답에 200 보냄&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;/strong&gt;: 브라우저 보안. &lt;em&gt;curl 은 CORS 무시&lt;/em&gt;. 서버 보안은 auth, IP allowlist 등 별도.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로컬 환경만 허용&lt;/strong&gt;: &lt;code&gt;http://localhost:3000&lt;/code&gt; 만 허용해두고 프로덕션 cross-origin 미테스트.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;cors-디버깅-체크리스트&quot;&gt;CORS 디버깅 체크리스트&lt;/h2&gt;
&lt;p&gt;브라우저 콘솔에서 CORS 오류가 났을 때:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Network 탭 확인&lt;/strong&gt;: OPTIONS preflight 가 발생했는지, 응답 코드 확인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;응답 헤더 확인&lt;/strong&gt;: &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; 이 실제로 존재하는지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;origin 비교&lt;/strong&gt;: 요청 &lt;code&gt;Origin&lt;/code&gt; 헤더와 응답 &lt;code&gt;ACAO&lt;/code&gt; 값이 정확히 일치하는지 (trailing slash 주의)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;credentials 모드&lt;/strong&gt;: &lt;code&gt;credentials: &apos;include&apos;&lt;/code&gt; 인데 &lt;code&gt;ACAO: *&lt;/code&gt; 이면 실패&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN 캐시&lt;/strong&gt;: &lt;code&gt;Vary: Origin&lt;/code&gt; 없이 캐싱된 preflight 응답이 문제일 수 있음&lt;/li&gt;
&lt;/ol&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:#6A737D&quot;&gt;// 빠른 CORS 테스트&lt;/span&gt;&lt;/span&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/health&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;  method: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;OPTIONS&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;  headers: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;Origin&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://app.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:#9ECBFF&quot;&gt;    &apos;Access-Control-Request-Method&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    &apos;Access-Control-Request-Headers&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &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&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 style=&quot;color:#B392F0&quot;&gt;then&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:#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;Status:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, res.status);&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;ACAO:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, res.headers.&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;access-control-allow-origin&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;  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;Credentials:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, res.headers.&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;access-control-allow-credentials&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;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;private-network-access-pna&quot;&gt;Private Network Access (PNA)&lt;/h2&gt;
&lt;p&gt;Chrome 94+ 에서 추가된 보안 정책. &lt;em&gt;Public internet -&gt; Private network (192.168.x.x, 10.x.x.x)&lt;/em&gt; 로의 요청도 preflight 를 요구.&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;# PNA preflight 에 추가 헤더&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Access-Control-Request-Private-Network&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;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:#85E89D&quot;&gt;Access-Control-Allow-Private-Network&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;p&gt;로컬 개발 서버 (localhost) 에 외부 스크립트가 접근할 때 발생. 서버에 위 헤더 추가 필요.&lt;/p&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|Session Cookie]]&lt;/li&gt;
&lt;li&gt;[[jwt|JWT]]&lt;/li&gt;
&lt;li&gt;[[http-1-1|HTTP/1.1]]&lt;/li&gt;
&lt;li&gt;[[tls|TLS]]&lt;/li&gt;
&lt;li&gt;[[http-2]] - HTTP/2 에서도 CORS 동일 적용&lt;/li&gt;
&lt;li&gt;[[network-http-caching]] - &lt;code&gt;Vary: Origin&lt;/code&gt; 과 캐시 관계&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] 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, 16 Jul 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의 전송 레이어를 [[tcp|TCP]] 에서 [[quic|QUIC]] 으로 교체한 버전이다. &lt;em&gt;UDP 위에 QUIC, QUIC 위에 HTTP&lt;/em&gt; 구조다.&lt;/p&gt;
&lt;p&gt;핵심 동기:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TCP HoL Blocking 해결&lt;/strong&gt;: 스트림 단위 패킷 손실 격리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0-RTT 핸드셰이크&lt;/strong&gt;: 재방문 시 연결 지연 제로&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection Migration&lt;/strong&gt;: IP 변경 시 연결 유지&lt;/li&gt;
&lt;/ul&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[&quot;HTTP/3 semantics\n(RFC 9114)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    QUIC[&quot;QUIC\n멀티스트림 + 암호화 + 혼잡제어\n(RFC 9000)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP[&quot;UDP&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    IP[&quot;IP&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;    HTTP --&gt; QUIC --&gt; UDP --&gt; IP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;http-버전-비교&quot;&gt;HTTP 버전 비교&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&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/3&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;TCP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QUIC (UDP)&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;없음 (keep-alive)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 스트림 내&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QUIC 독립 스트림&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;TCP + 앱 레벨&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 레벨&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;1 RTT + TLS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 RTT + TLS&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;1 RTT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;0 RTT&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;[[hpack&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HPACK]]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;서버 Push&lt;/td&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;없음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Connection Migration&lt;/td&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;가능&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;선택&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;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/3 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/3 response + session ticket&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 data (HTTP/3 request + cached session 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/3 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;p&gt;QUIC은 TLS 1.3 핸드셰이크를 전송 계층 협상과 통합한다. 첫 방문은 1 RTT, 이전 연결의 세션 티켓을 캐시해두면 재접속 시 0 RTT로 데이터를 즉시 전송할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;strong&gt;0-RTT는 replay 공격에 취약하다&lt;/strong&gt;. 비멱등 요청(POST, DELETE, 결제)을 0-RTT로 보내면 중간자가 같은 요청을 반복 재생할 수 있다. GET/HEAD 같은 안전한 메서드만 0-RTT를 허용하고, 나머지는 반드시 1-RTT 이후 처리해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tcp-hol-blocking-vs-quic-stream-독립&quot;&gt;TCP HoL Blocking 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;HTTP/2 over TCP&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가\n같은 TCP 시퀀스 안&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket1[&quot;패킷 1: stream 1 데이터&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket2[&quot;패킷 2: stream 3 데이터 (손실!)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TPacket3[&quot;패킷 3: stream 5 데이터&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        TBlock[&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;        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;TCP는 순서 보장 필요&quot;| TBlock&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;HTTP/3 over QUIC&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가\n각자 독립 시퀀스&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket1[&quot;패킷 1: stream 1 (도착)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket2[&quot;패킷 2: stream 3 (손실)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket3[&quot;패킷 3: stream 5 (도착)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QOK[&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;        QWait[&quot;stream 3만 대기&quot;]&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;        QPacket1 --&gt; QOK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket3 --&gt; QOK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        QPacket2 --&gt; QWait&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;p&gt;HTTP/2는 TCP 레이어에서 스트림 1, 3, 5를 하나의 바이트 스트림으로 전송한다. 패킷 2가 손실되면 TCP가 재전송을 완료할 때까지 패킷 3도 애플리케이션에 전달되지 않는다. 이것이 TCP 레벨 HoL Blocking이다.&lt;/p&gt;
&lt;p&gt;HTTP/3 QUIC은 스트림을 독립 단위로 격리한다. stream 3 패킷이 손실되어도 stream 1과 5는 즉시 애플리케이션에 전달된다.&lt;/p&gt;
&lt;h2 id=&quot;connection-migration&quot;&gt;Connection Migration&lt;/h2&gt;
&lt;p&gt;WiFi에서 4G로 전환, NAT 재할당 등 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;sequenceDiagram&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: 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;    S--&gt;&gt;C: 응답 (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 C: 지하철 진입, 4G 전환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C: IP 변경: 10.0.0.5:65432&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: 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;    Note over S: Connection ID XYZ로 식별 (IP 무관)&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: 응답 (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 C,S: TLS 세션 유지, 처음부터 다시 안 함&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TCP는 (src IP, src port, dst IP, dst port) 4-tuple로 연결을 식별하므로 IP가 바뀌면 연결이 끊긴다. QUIC은 &lt;strong&gt;Connection ID&lt;/strong&gt;로 연결을 식별하여 IP/포트 변경을 투명하게 처리한다.&lt;/p&gt;
&lt;h2 id=&quot;qpack-헤더-압축&quot;&gt;QPACK 헤더 압축&lt;/h2&gt;
&lt;p&gt;[[hpack|HPACK]]의 순서 의존성 문제를 해결한 변형이다.&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;    E[&quot;QPACK Encoder\n(클라이언트)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D[&quot;QPACK Decoder\n(서버)&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;    E --&gt;|&quot;인코더 스트림\n동적 테이블 업데이트&quot;| D&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D --&gt;|&quot;디코더 스트림\n테이블 업데이트 ACK&quot;| E&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    E --&gt;|&quot;요청 스트림\n헤더 블록 (인덱스 참조)&quot;| D&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;HPACK (HTTP/2)&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;QPACK (HTTP/3)&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;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;있음 (TCP가 보장)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;없음 (QUIC 스트림 독립)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HOL 블로킹&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 레벨&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QPACK 레벨 동기화로 제거&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;alt-svc-http3-협상-방법&quot;&gt;Alt-Svc: HTTP/3 협상 방법&lt;/h2&gt;
&lt;p&gt;브라우저는 HTTP/3를 처음부터 알 수 없다. 서버가 &lt;code&gt;Alt-Svc&lt;/code&gt; 헤더로 HTTP/3 지원을 알린다.&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;Alt-Svc&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; h3=&quot;:443&quot;; ma=86400, h3-29=&quot;:443&quot;; ma=86400&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이후 브라우저는 UDP 443 포트로 QUIC 연결을 시도하고, 성공하면 HTTP/3를 사용한다. 실패하면 HTTP/2나 HTTP/1.1로 fallback한다.&lt;/p&gt;
&lt;h2 id=&quot;실전-서버-설정&quot;&gt;실전: 서버 설정&lt;/h2&gt;
&lt;h3 id=&quot;nginx-125&quot;&gt;nginx (1.25+)&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;nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;server&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;    listen &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; quic reuseport;   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# HTTP/3 UDP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssl;              &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# HTTP/2 TCP fallback&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;    ssl_certificate &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    /path/to/cert.pem;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ssl_certificate_key &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/path/to/key.pem;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ssl_protocols &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      TLSv1.3;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# QUIC은 TLS 1.3 필수&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;    # 브라우저에 HTTP/3 지원 알림&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Alt-Svc &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;h3=&quot;:443&quot;; ma=86400&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;    # QUIC 버퍼 최적화&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    quic_retry &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# QUIC Retry 패킷으로 DoS 방어&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;h3 id=&quot;cloudflare-workers-http3-자동&quot;&gt;Cloudflare Workers (HTTP/3 자동)&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;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Cloudflare Workers는 HTTP/3를 자동 협상&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 별도 설정 없이 h3 지원&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&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:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;env&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;    // request.cf.httpProtocol 로 클라이언트 프로토콜 확인&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; proto&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; request.cf?.httpProtocol;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// &quot;HTTP/3&quot; | &quot;HTTP/2&quot; | &quot;HTTP/1.1&quot;&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:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`Protocol: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;proto&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;  },&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;h3 id=&quot;http3-연결-테스트&quot;&gt;HTTP/3 연결 테스트&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# curl로 HTTP/3 요청 (curl 7.88+, QUIC 빌드 필요)&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; --http3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://cloudflare.com&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -I&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;# HTTP/3 협상 확인&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; -v&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --http3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://example.com&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; 2&gt;&amp;#x26;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -E&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;ALPN|http&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# * ALPN: h3&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;# 연결 없이 HTTP/3 즉시 시도&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; --http3-prior-knowledge&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://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;# Python httpx로 HTTP/3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pip install httpx[http3]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; httpx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; httpx.Client&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(http2&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 style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; client:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    response&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; client.get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;&quot;https://example.com&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:#79B8FF&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;response.http_version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# HTTP/3 또는 HTTP/2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;도입-현황-2026&quot;&gt;도입 현황 (2026)&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Cloudflare / Fastly / Akamai (CDN)&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;Google 서비스&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;전면 HTTP/3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;모바일 앱 (iOS, Android)&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;약 35%+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;기업 내부망 (방화벽 UDP 차단)&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;브라우저 지원 현황: Chrome, Firefox, Safari, Edge 모두 HTTP/3를 지원한다 (2022년 이후 기본 활성).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
방화벽이 UDP 443 포트를 차단하면 HTTP/3 fallback이 발생해 HTTP/2 (TCP)로 내려간다. 사내망 환경에서 HTTP/3가 동작하지 않는 가장 흔한 원인이다.&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;: 기업망, 일부 ISP에서 UDP 443을 차단한다. Alt-Svc로 광고하더라도 실제 사용률을 모니터링해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0-RTT replay 공격&lt;/strong&gt;: POST, DELETE, 결제 등 비멱등 요청을 0-RTT로 허용하면 재생 공격 가능. GET/HEAD만 0-RTT 허용.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connection ID 추적&lt;/strong&gt;: Connection ID가 식별자처럼 쓰일 수 있어 프라이버시 우려. RFC 9000은 주기적 Connection ID 변경을 권장.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로드 밸런서 UDP 해싱 문제&lt;/strong&gt;: 전통적 LB는 5-tuple(IP+포트) 해싱. IP 변경 시 다른 백엔드로 라우팅될 수 있다. Connection ID 기반 라우팅 지원 LB 필요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세션 티켓 공유&lt;/strong&gt;: 다중 서버 환경에서 0-RTT를 위해 세션 티켓을 서버 간 공유해야 한다. 미공유 시 0-RTT 실패.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 비용&lt;/strong&gt;: QUIC의 사용자 공간 구현은 커널 바이패스 없이 암호화 비용이 TCP+TLS보다 높을 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[quic|QUIC]] - HTTP/3의 전송 계층&lt;/li&gt;
&lt;li&gt;[[http-2|HTTP/2]] - HPACK, 멀티플렉싱 도입 (전 단계)&lt;/li&gt;
&lt;li&gt;[[hpack|HPACK]] - HTTP/2 헤더 압축 (HTTP/3는 QPACK)&lt;/li&gt;
&lt;li&gt;[[udp|UDP]] - QUIC 기반 프로토콜&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - QUIC에 통합된 암호화&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking|Head-of-Line Blocking]] - HTTP/3가 해결하는 핵심 문제&lt;/li&gt;
&lt;li&gt;[[load-balancer|Load Balancer]] - QUIC/HTTP3 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] 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>Thu, 16 Jul 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 주소(포트 포함)를 변환하는 기법이다. 주로 사설 IP와 공인 IP 간 매핑에 사용된다.&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 LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph Private[&quot;사설망 (192.168.0.0/24)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        H1[&quot;호스트 A\n192.168.0.10&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        H2[&quot;호스트 B\n192.168.0.11&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        H3[&quot;호스트 C\n192.168.0.12&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Router[&quot;라우터 / NAT 장비\n공인 IP: 203.0.113.5&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Internet[&quot;인터넷&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;    H1 &amp;#x26; H2 &amp;#x26; H3 --&gt; Router --&gt; Internet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IPv4 주소 고갈&lt;/strong&gt;: 전 세계 IPv4 주소는 약 43억 개뿐이다. 사내 100대 호스트가 공인 IP 1개를 공유하면 99개를 절약한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보안&lt;/strong&gt;: 내부 호스트의 실제 IP를 외부에 노출하지 않는다.&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; 트래픽의 소스 IP/포트를 공인 IP로 변경한다. 홈 라우터나 AWS NAT Gateway의 기본 동작이다.&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;    participant H as &quot;호스트\n192.168.1.10:12345&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant R as &quot;NAT 라우터\n203.0.113.5&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as &quot;외부 서버\n1.2.3.4:443&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;    H-&gt;&gt;R: src=192.168.1.10:12345, dst=1.2.3.4:443&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over R: 매핑 테이블 등록\n192.168.1.10:12345 -&gt; 203.0.113.5:54321&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: src=203.0.113.5:54321, dst=1.2.3.4:443&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;R: src=1.2.3.4:443, dst=203.0.113.5:54321&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over R: 매핑 테이블 조회\n203.0.113.5:54321 -&gt; 192.168.1.10:12345&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R--&gt;&gt;H: src=1.2.3.4:443, dst=192.168.1.10:12345&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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; 트래픽의 목적지 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;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as &quot;외부 클라이언트\n8.8.8.8&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant R as &quot;NAT 라우터\n203.0.113.5:80&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as &quot;내부 웹 서버\n192.168.1.10:8080&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;    C-&gt;&gt;R: dst=203.0.113.5:80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over R: DNAT 규칙 적용\n:80 -&gt; 192.168.1.10:8080&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: dst=192.168.1.10:8080&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;R: src=192.168.1.10:8080&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R--&gt;&gt;C: src=203.0.113.5:80&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;code&gt;:80&lt;/code&gt; -&gt; 내부 &lt;code&gt;:8080&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;쿠버네티스 NodePort&lt;/li&gt;
&lt;li&gt;로드 밸런서의 VIP(Virtual IP) 처리&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;포트까지 포함한 매핑으로 한 공인 IP를 여러 내부 호스트가 공유한다. 실제로 대부분의 NAT는 PAT를 의미한다.&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;공인 주소 매핑&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;192.168.1.10:12345&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;203.0.113.5:1001&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;192.168.1.11:12345&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;203.0.113.5:1002&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;192.168.1.10:12346&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;203.0.113.5:1003&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;포트 번호 공간은 65535개이므로 한 공인 IP로 수만 개의 동시 연결을 처리할 수 있다. 단, 연결이 많아지면 포트 고갈이 발생한다.&lt;/p&gt;
&lt;h2 id=&quot;nat-매핑-테이블&quot;&gt;NAT 매핑 테이블&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;NAT 매핑 테이블 (PAT 예시)&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;| Proto | 내부 IP         | 내부 Port | 외부 IP         | 외부 Port | 상태     | 타임아웃 |&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;| TCP   | 192.168.1.10    | 12345     | 203.0.113.5     | 1001      | ESTABLISHED | 300s  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| TCP   | 192.168.1.11    | 12345     | 203.0.113.5     | 1002      | TIME_WAIT   | 30s   |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| UDP   | 192.168.1.10    | 54321     | 203.0.113.5     | 2001      | ACTIVE      | 60s   |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TCP&lt;/strong&gt;: 연결 상태(SYN/ESTABLISHED/FIN)를 추적해 매핑 유지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UDP&lt;/strong&gt;: stateless이므로 마지막 패킷 이후 일정 시간 후 만료 (보통 60-300초)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ICMP&lt;/strong&gt;: echo id 기반 매핑&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nat-종류별-동작-차이-rfc-3489-분류&quot;&gt;NAT 종류별 동작 차이 (RFC 3489 분류)&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;P2P 적합성&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Full Cone NAT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;내부 IP:Port가 외부 IP:Port에 고정 매핑. 누구나 외부 포트로 연결 가능&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;Restricted Cone NAT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;내부에서 먼저 통신한 외부 IP만 접근 허용&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;Port Restricted Cone NAT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;외부 IP + 포트까지 제한&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;Symmetric NAT&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;h2 id=&quot;클라우드에서의-nat&quot;&gt;클라우드에서의 NAT&lt;/h2&gt;
&lt;h3 id=&quot;aws&quot;&gt;AWS&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;    subgraph PrivateSubnet[&quot;프라이빗 서브넷&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        EC2[&quot;EC2 Instance\n10.0.1.10&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 PublicSubnet[&quot;퍼블릭 서브넷&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        NATGW[&quot;NAT Gateway\n탄력적 IP: 52.x.x.x&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;    IGW[&quot;Internet Gateway&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Internet[&quot;Internet&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;    EC2 --&gt; NATGW --&gt; IGW --&gt; Internet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NAT Gateway&lt;/strong&gt;: AWS 관리형 서비스. 고가용성, 자동 확장. 시간당 + 데이터 전송량 과금.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT Instance&lt;/strong&gt;: 직접 관리하는 EC2. 비용 절감 가능하지만 단일 장애점.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;실무에서는 NAT Gateway를 권장하며, 비용 절감 시 VPC Endpoint로 S3/DynamoDB 트래픽을 NAT 없이 직접 연결한다.&lt;/p&gt;
&lt;h3 id=&quot;iptables로-linux-nat-직접-구성&quot;&gt;iptables로 Linux NAT 직접 구성&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# SNAT: 내부망 아웃바운드 트래픽을 eth0 IP로 변환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;iptables&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nat&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -A&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; POSTROUTING&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 192.168.1.0/24&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eth0&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -j&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; MASQUERADE&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;# DNAT: 80 포트를 내부 8080으로 포워딩&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;iptables&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nat&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -A&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; PREROUTING&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eth0&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --dport&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 80&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -j&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; DNAT&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;  --to-destination&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 192.168.1.10:8080&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:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /proc/sys/net/ipv4/ip_forward&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;# 현재 NAT 테이블 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;iptables&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; nat&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -L&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;nat-traversal&quot;&gt;NAT Traversal&lt;/h2&gt;
&lt;p&gt;P2P 통신 (WebRTC, 게임, VoIP)에서 NAT 뒤에 있는 피어끼리 직접 연결을 맺으려면 NAT를 뚫어야 한다.&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 NAT_A[&quot;NAT A (Symmetric)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        A[&quot;피어 A\n192.168.1.10&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 NAT_B[&quot;NAT B (Full Cone)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        B[&quot;피어 B\n10.0.0.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;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    STUN[&quot;STUN 서버\n(공인 IP)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TURN[&quot;TURN 서버\n(릴레이)&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;    A --&gt;|&quot;1. 내 공인 IP:Port 확인 요청&quot;| STUN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    STUN --&gt;|&quot;2. 203.0.113.5:54321 응답&quot;| A&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B --&gt;|&quot;1. 내 공인 IP:Port 확인 요청&quot;| STUN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    STUN --&gt;|&quot;2. 198.51.100.1:22345 응답&quot;| B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A &amp;#x3C;--&gt;|&quot;3. 직접 연결 시도 (Hole Punching)&quot;| B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A &amp;#x3C;--&gt;|&quot;실패 시 릴레이&quot;| TURN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TURN &amp;#x3C;--&gt;|&quot;릴레이&quot;| B&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;stun-session-traversal-utilities-for-nat&quot;&gt;STUN (Session Traversal Utilities for NAT)&lt;/h3&gt;
&lt;p&gt;자신의 공인 IP:포트를 발견하고 NAT 타입을 파악한다. 두 피어가 서로 공인 주소를 교환하고 동시에 패킷을 보내 NAT에 홀을 뚫는다 (&lt;strong&gt;UDP Hole Punching&lt;/strong&gt;). Symmetric NAT 환경에서는 실패할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;turn-traversal-using-relays-around-nat&quot;&gt;TURN (Traversal Using Relays around NAT)&lt;/h3&gt;
&lt;p&gt;STUN으로 직접 연결이 불가능할 때 모든 트래픽을 릴레이 서버를 통해 전달한다. 항상 동작하지만 서버 대역폭 비용이 발생하며 지연이 증가한다.&lt;/p&gt;
&lt;h3 id=&quot;ice-interactive-connectivity-establishment&quot;&gt;ICE (Interactive Connectivity Establishment)&lt;/h3&gt;
&lt;p&gt;[[webrtc|WebRTC]]에서 사용하는 프레임워크. STUN/TURN을 조합해 최적 경로를 자동 선택한다.&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;ICE Candidate 우선순위:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. host candidate (직접 연결)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. server reflexive candidate (STUN을 통한 공인 IP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. relay candidate (TURN 릴레이)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;실전-예시-aws-vpc-nat-구성&quot;&gt;실전 예시: AWS VPC NAT 구성&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;# AWS CLI로 NAT Gateway 생성&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 1. EIP (Elastic IP) 할당&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;EIP&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:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ec2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; allocate-address&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --domain&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; vpc&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --query&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; AllocationId&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --output&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; text&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;# 2. 퍼블릭 서브넷에 NAT Gateway 생성&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;NATGW&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:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ec2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create-nat-gateway&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;  --subnet-id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; subnet-public-xxx&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;  --allocation-id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $EIP &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;  --query&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; NatGateway.NatGatewayId&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --output&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; text&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;# 3. 프라이빗 서브넷 라우트 테이블에 NAT Gateway 경로 추가&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ec2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create-route&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;  --route-table-id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; rtb-private-xxx&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;  --destination-cidr-block&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 0.0.0.0/0&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;  --nat-gateway-id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $NATGW&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;비용 최적화: S3, DynamoDB 트래픽은 VPC Endpoint를 통해 NAT 없이 직접 연결하면 NAT Gateway 비용을 줄일 수 있다.&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;# S3 VPC Endpoint 생성 (NAT 없이 S3 접근)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;aws&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ec2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create-vpc-endpoint&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;  --vpc-id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; vpc-xxx&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;  --service-name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; com.amazonaws.ap-northeast-2.s3&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;  --route-table-ids&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; rtb-private-xxx&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;NAT 매핑 만료&lt;/strong&gt;: UDP 연결은 마지막 패킷 후 60-300초면 매핑이 만료된다. WebSocket, 게임 소켓 등 장기 연결은 주기적으로 keep-alive 패킷을 전송해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hairpin NAT 미지원&lt;/strong&gt;: 내부 호스트가 자기 공인 IP로 접근하면 일부 NAT 장비가 처리 못한다. &lt;code&gt;192.168.1.10&lt;/code&gt;이 &lt;code&gt;203.0.113.5:80&lt;/code&gt;에 접근하려 할 때 반환 경로가 없어 실패한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symmetric NAT와 STUN 조합 불가&lt;/strong&gt;: 모바일 LTE 망은 Symmetric NAT가 많아 UDP Hole Punching이 실패한다. TURN 릴레이가 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;포트 고갈 (Port Exhaustion)&lt;/strong&gt;: 대규모 NAT Gateway에서 연결 수가 포트 수 (65535) 를 초과하면 새 연결이 거부된다. AWS NAT Gateway는 IP당 55000개 동시 연결을 권고한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT + IPSec 충돌&lt;/strong&gt;: ESP 프로토콜은 포트가 없어 PAT가 다중 연결을 구분하지 못한다. NAT-T (UDP 캡슐화)로 우회한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-ip|IP]] - IP 주소 체계&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting|CIDR / 서브네팅]] - 서브넷 설계&lt;/li&gt;
&lt;li&gt;[[webrtc|WebRTC]] - NAT Traversal을 활용하는 대표 프로토콜&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - NAT 뒤에서도 종단간 암호화&lt;/li&gt;
&lt;li&gt;[[quic|QUIC]] - Connection ID로 NAT 재바인딩을 자연스럽게 처리&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>nat</category><category>routing</category><category>security</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, 16 Jul 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;)은 HTTP/1.1 Upgrade로 시작하는 양방향 영속 메시지 채널이다. 하나의 [[tcp|TCP]] 연결 위에서 서버와 클라이언트가 임의 시점에 메시지를 push할 수 있다.&lt;/p&gt;
&lt;p&gt;HTTP는 클라이언트가 요청해야 서버가 응답하는 단방향 구조다. WebSocket은 이 제약을 제거해 서버가 먼저 메시지를 보낼 수 있게 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;주요 활용&lt;/strong&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;Host: example.com&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: HTTP/1.1 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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 이후 같은 TCP 위에서 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: &quot;hello&quot;)&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: &quot;world&quot;)&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 (binary: 이미지 데이터)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Sec-WebSocket-Accept&lt;/code&gt;는 클라이언트가 보낸 key와 고정 GUID를 결합한 SHA-1 해시의 Base64 인코딩이다:&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;Accept = Base64(SHA-1(Sec-WebSocket-Key + &quot;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&quot;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 절차는 프로토콜 착각(평범한 HTTP 응답으로 오해)을 방지하기 위한 핸드셰이크 무결성 검증이다.&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;/code&gt;&lt;/pre&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;FIN&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 bit&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;마지막 fragment이면 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;RSV1-3&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;각 1 bit&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;확장에 예약 (압축 확장은 RSV1 사용)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;opcode&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;4 bit&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;MASK&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 bit&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;Payload len&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;7 bit&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;125 이하면 직접 길이, 126이면 다음 2 bytes, 127이면 다음 8 bytes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Masking-key&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;4 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;MASK=1일 때만 존재&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Payload Data&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;가변&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;실제 데이터 (마스킹된 경우 XOR 복호화)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;opcode-목록&quot;&gt;Opcode 목록&lt;/h3&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;opcode&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;0x0&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Continuation (이전 frame 이어짐)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0x1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Text (UTF-8 인코딩)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0x2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Binary&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0x8&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Close&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0x9&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Ping&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0xA&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Pong&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;클라이언트 발 frame은 반드시 MASK 처리&lt;/strong&gt; (XOR with 4-byte masking key). 캐시 포이즈닝 공격 방지 목적이다. 서버 발 frame은 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;    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;    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 frame (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 frame (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 frame 전송&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ping/Pong의 목적:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NAT 매핑 유지&lt;/strong&gt;: NAT 장비는 유휴 UDP/TCP 연결을 일정 시간 후 만료. 주기적 ping으로 방지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;프록시 idle close 방지&lt;/strong&gt;: nginx, ALB 같은 중간 장비의 idle timeout 초과 방지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 생존 확인&lt;/strong&gt;: 상대방이 살아있는지 확인 (silent disconnect 감지)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
NAT 일반 idle timeout은 약 60-300초다. Ping 간격은 30-60초가 권장값이다. AWS ALB의 기본 idle timeout은 60초이므로 30초 이하 ping을 사용해야 한다.&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;/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; 101&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Switching Protocols&lt;/span&gt;&lt;/span&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&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;STOMP&lt;/strong&gt; (Simple Text Oriented Messaging Protocol): Spring WebSocket에서 주로 사용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MQTT over WebSocket&lt;/strong&gt;: IoT 브로커 연결&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GraphQL over WebSocket&lt;/strong&gt; (graphql-ws, subscriptions-transport-ws)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WAMP&lt;/strong&gt; (Web Application Messaging Protocol)&lt;/li&gt;
&lt;/ul&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ws://&lt;/code&gt; 는 일부 ISP와 프록시가 차단 또는 내용 변조 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://&lt;/code&gt; 페이지에서 &lt;code&gt;ws://&lt;/code&gt; 연결은 브라우저가 mixed content로 거부&lt;/li&gt;
&lt;li&gt;대부분의 WAF와 로드 밸런서가 WSS에 최적화&lt;/li&gt;
&lt;/ul&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[&quot;브라우저\nwss://example.com/chat&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB[&quot;로드 밸런서\n(TLS 종료)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    WS[&quot;WebSocket 서버\nws://10.0.0.5:8080&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;    Browser &amp;#x3C;--&gt;|&quot;wss (TLS)&quot;| LB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB &amp;#x3C;--&gt;|&quot;ws (평문, 내부망)&quot;| WS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실무에서는 로드 밸런서가 TLS를 종료하고 내부에서 평문 ws://로 전달하는 구성이 일반적이다.&lt;/p&gt;
&lt;h2 id=&quot;실전-예시&quot;&gt;실전 예시&lt;/h2&gt;
&lt;h3 id=&quot;nodejs-ws-라이브러리&quot;&gt;Node.js (ws 라이브러리)&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;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; WebSocket&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ws&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;// 서버&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; wss&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:#E1E4E8&quot;&gt; WebSocket.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ port: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;8080&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;wss.&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;&quot;connection&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;ws&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:#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;`클라이언트 연결: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;socket&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;remoteAddress&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 30초마다 ping&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; pingInterval&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:#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;ping&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 style=&quot;color:#E1E4E8&quot;&gt;  }, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;30000&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;  ws.&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;&quot;pong&quot;&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;&quot;pong 수신, 연결 살아있음&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;  });&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;  ws.&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;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;data&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; message&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; data.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toString&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;`수신: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;message&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:#6A737D&quot;&gt;    // 같은 채널 모든 클라이언트에게 broadcast&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    wss.clients.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;client&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; (client.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;        client.&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:#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;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ text: message }));&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 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;  ws.&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;&quot;close&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;reason&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:#B392F0&quot;&gt;    clearInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(pingInterval);&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;`연결 종료: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;reason&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;  });&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;h3 id=&quot;python-websockets&quot;&gt;Python (websockets)&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; asyncio&lt;/span&gt;&lt;/span&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; websockets&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; handler&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(websocket, path):&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;        async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; message &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; websocket:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;            print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&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 style=&quot;color:#E1E4E8&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;            await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; websocket.send(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;echo: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;    except&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; websockets.exceptions.ConnectionClosed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;연결 종료&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:#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; main&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;    # ping_interval=20: 20초마다 ping, ping_timeout=10: 10초 응답 없으면 close&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; with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; websockets.serve(handler, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;8080&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:#FFAB70&quot;&gt;                                 ping_interval&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&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 style=&quot;color:#FFAB70&quot;&gt;ping_timeout&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&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.Future()  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&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;asyncio.run(main())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;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;&quot;wss://example.com/chat&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;chat&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&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;ws.&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:#FFAB70&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;=&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;&quot;연결됨, 서브프로토콜:&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, ws.protocol);&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;(&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;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ type: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;join&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, room: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;general&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;};&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;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:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&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;=&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;(event.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;&quot;수신:&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, 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;ws.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onclose&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;event&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;`종료: code=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;code&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}, reason=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;reason&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}, clean=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;wasClean&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:#6A737D&quot;&gt;  // 자동 재연결 (exponential backoff)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  setTimeout&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; reconnect&lt;/span&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 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;ws.&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;error&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;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;WebSocket 오류:&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, error);&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;// 명시적 종료 (1000: 정상 종료)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ws.close(1000, &quot;사용자 로그아웃&quot;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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[&quot;Client 1&quot;] --&gt;|&quot;ws&quot;| N1[&quot;Server Node A&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C2[&quot;Client 2&quot;] --&gt;|&quot;ws&quot;| N2[&quot;Server Node B&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C3[&quot;Client 3&quot;] --&gt;|&quot;ws&quot;| N1&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;    N1 --&gt;|&quot;PUBLISH msg&quot;| Redis[(&quot;Redis Pub/Sub\n또는 Streams&quot;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N2 --&gt;|&quot;SUBSCRIBE&quot;| Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N1 --&gt;|&quot;SUBSCRIBE&quot;| Redis&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;    Redis --&gt;|&quot;fan-out&quot;| N1 &amp;#x26; N2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;단일 서버는 in-memory로 fan-out 가능하지만, 다중 서버 환경에서는 Redis Pub/Sub 또는 Streams로 노드 간 메시지를 전달해야 한다.&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;sequenceDiagram&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: Close frame (opcode 0x8, code=1000, reason=&quot;done&quot;)&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: Close frame (echo back)&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 연결 종료&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Close 코드:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1000&lt;/code&gt;: 정상 종료&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1001&lt;/code&gt;: Going Away (탭 닫기, 서버 재시작)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1008&lt;/code&gt;: Policy Violation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1011&lt;/code&gt;: Unexpected Error&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4000-4999&lt;/code&gt;: 애플리케이션 정의 코드 (사용자 정의)&lt;/li&gt;
&lt;/ul&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;방향&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;WebSocket&lt;/td&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;채팅, 게임, 트레이딩&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;SSE&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서버 -&gt; 클라이언트&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;Long Polling&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;양방향 (서버 hold)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;짧음&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;레거시 fallback&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HTTP/2 Push&lt;/td&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;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;WebRTC DataChannel&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;P2P 양방향&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;WebTransport&lt;/td&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;WebSocket 차세대 대안&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;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Idle timeout 사고&lt;/strong&gt;: AWS ALB, nginx의 기본 idle timeout은 60초다. PING/PONG 간격이 60초 이상이면 연결이 silent close된다. 반드시 idle timeout보다 짧은 ping 간격 설정 필요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backpressure 부재&lt;/strong&gt;: 서버가 빠르게 send하면 클라이언트 수신 버퍼가 가득 찬다. 애플리케이션 레벨 flow control (ACK/credit 방식) 이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sticky Session 필요&lt;/strong&gt;: 같은 사용자의 WebSocket 요청이 같은 서버 노드에 라우팅되어야 한다. LB cookie 또는 IP hash를 사용하거나, Redis pub/sub으로 모든 노드에 fan-out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CORS가 WebSocket에 적용 안 됨&lt;/strong&gt;: &lt;code&gt;Origin&lt;/code&gt; 헤더는 브라우저가 자동 설정하지만 서버가 수동으로 검증해야 한다. 미검증 시 CSRF 공격에 취약. &lt;code&gt;ws.upgradeReq.headers.origin&lt;/code&gt; 를 허용 목록과 대조.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;메모리 leak&lt;/strong&gt;: 연결이 close될 때 이벤트 리스너, 타이머, 방 구독을 모두 해제해야 한다. &lt;code&gt;ws.on(&quot;close&quot;)&lt;/code&gt; 핸들러에서 clearInterval, 구독 해제 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;대용량 이진 데이터&lt;/strong&gt;: WebSocket 위에서 파일 전송 시 단편화(fragmentation)가 발생하면 조각을 합쳐야 한다. FIN 비트로 마지막 fragment 판단.&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|HTTP/1.1]] - WebSocket upgrade handshake의 기반&lt;/li&gt;
&lt;li&gt;[[tcp|TCP]] - WebSocket의 영속 연결 토대&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - WSS 암호화&lt;/li&gt;
&lt;li&gt;[[realtime-comparison|실시간 기술 비교]] - WebSocket vs SSE vs Long Polling&lt;/li&gt;
&lt;li&gt;[[sse-server-sent-events|SSE]] - 서버 단방향 영속 스트림&lt;/li&gt;
&lt;li&gt;[[webrtc|WebRTC]] - P2P 데이터 채널 (DTLS 기반)&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>DTLS</title><link>https://shinkeonkim.com/wiki/network/dtls/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/dtls/</guid><pubDate>Thu, 16 Jul 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|UDP]] 위에서 동작하는 [[tls|TLS]]의 변형이다. 데이터그램 기반 통신에 TLS와 동등한 보안 보장(서버 인증, 키 교환, 암호화, 무결성)을 제공하면서 UDP의 특성(비순서, 손실 허용, 비연결)을 그대로 유지한다.&lt;/p&gt;
&lt;p&gt;[[webrtc|WebRTC]] DataChannel, VoIP, IoT, VPN 등 UDP를 사용하는 보안 통신에 광범위하게 쓰인다.&lt;/p&gt;
&lt;h2 id=&quot;왜-tls가-아닌-dtls인가&quot;&gt;왜 TLS가 아닌 DTLS인가&lt;/h2&gt;
&lt;p&gt;[[tls|TLS]]는 [[tcp|TCP]]의 “순서 보장된 신뢰성 있는 스트림” 위에서 설계되었다.&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;    subgraph TLS_STACK[&quot;TLS 스택 가정&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T1[&quot;순서 보장&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T2[&quot;손실 없음&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T3[&quot;중복 없음&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        T4[&quot;암묵적 시퀀스 번호&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 UDP_REALITY[&quot;UDP 현실&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U1[&quot;순서 뒤바뀜 가능&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U2[&quot;패킷 손실 가능&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U3[&quot;패킷 중복 가능&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        U4[&quot;시퀀스 번호 없음&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TLS_STACK -- &quot;충돌&quot; --&gt; UDP_REALITY&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;TLS (TCP 위)&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;DTLS (UDP 위)&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;가정됨 (TCP 보장)&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;명시적 (레코드 헤더에 포함)&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가 재전송 처리&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;TCP 스트림으로 분할&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;레코드 fragmentation 지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;DoS 방어&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP SYN Cookies&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Cookie Exchange (HelloVerifyRequest)&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;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;    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;    Note over C,S: Cookie Exchange (DoS 방어)&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: ClientHello (랜덤 값 포함)&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: HelloVerifyRequest (Cookie 포함)&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: ClientHello (Cookie 포함하여 재전송)&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;    Note over C,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: ServerHello + Certificate + ServerHelloDone&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: ClientKeyExchange + ChangeCipherSpec + Finished&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: ChangeCipherSpec + Finished&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;    Note over C,S: 데이터 교환 시작&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: DTLS 레코드 (seq=0)&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: DTLS 레코드 (seq=0)&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: DTLS 레코드 (seq=1) - 손실&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: DTLS 레코드 (seq=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: DTLS 레코드 (seq=1) - ACK&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;핵심: 손실된 핸드셰이크 메시지는 &lt;strong&gt;타이머 기반 재전송&lt;/strong&gt;으로 복구한다. 타이머는 지수 백오프 방식으로 늘어난다 (초기 1초, 최대 60초).&lt;/p&gt;
&lt;h2 id=&quot;cookie-exchange-dos-방어&quot;&gt;Cookie Exchange (DoS 방어)&lt;/h2&gt;
&lt;p&gt;UDP는 소스 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;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant A as &quot;공격자\n(스푸핑 IP 사용)&quot;&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;    A-&gt;&gt;S: ClientHello (src IP: 피해자 IP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: 상태 저장 없이 Cookie만 계산&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S--&gt;&gt;A: HelloVerifyRequest (Cookie = HMAC(ClientHello))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over A: 피해자 IP로 Cookie 도달 안 함&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over S: Cookie 재전송 없으면 핸드셰이크 진행 안 함&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;서버는 &lt;code&gt;HelloVerifyRequest&lt;/code&gt;를 보낼 때 &lt;strong&gt;어떤 상태도 저장하지 않는다&lt;/strong&gt;. 클라이언트가 Cookie를 포함한 &lt;code&gt;ClientHello&lt;/code&gt;를 재전송했을 때 비로소 핸드셰이크를 진행한다. HMAC으로 서명된 Cookie이므로 위조 불가.&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;기반 TLS&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;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;td align=&quot;left&quot;&gt;첫 UDP TLS 표준&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;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6347&quot;&gt;RFC 6347&lt;/a&gt;)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.2&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;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;td align=&quot;left&quot;&gt;1-RTT / 0-RTT, 연결 ID&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로 암호화된다.&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 TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SIG[&quot;시그널링 서버\n(SDP 교환)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[&quot;피어 A&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B[&quot;피어 B&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ICE[&quot;ICE / STUN / TURN\n(NAT 통과)&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;    A &amp;#x3C;--&gt;|&quot;SDP offer/answer\n(인증서 fingerprint 교환)&quot;| SIG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SIG &amp;#x3C;--&gt; B&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A &amp;#x3C;--&gt;|&quot;DTLS 핸드셰이크\n(fingerprint 검증)&quot;| ICE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ICE &amp;#x3C;--&gt;|&quot;DTLS-SCTP\n데이터 전송&quot;| B&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;흐름:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;시그널링으로 양쪽 인증서 fingerprint 사전 교환&lt;/li&gt;
&lt;li&gt;DTLS 핸드셰이크로 키 확립 (fingerprint 검증)&lt;/li&gt;
&lt;li&gt;SCTP over DTLS로 DataChannel 데이터 전송&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;WebRTC에서 DTLS는 MITM 방어를 위해 인증서 fingerprint를 시그널링 채널로 사전 교환하는 방식을 쓴다.&lt;/p&gt;
&lt;h3 id=&quot;2-dtls-srtp-미디어-채널&quot;&gt;2. DTLS-SRTP (미디어 채널)&lt;/h3&gt;
&lt;p&gt;WebRTC 음성/영상 트래픽은 SRTP로 암호화하지만, &lt;strong&gt;SRTP 키는 DTLS 핸드셰이크로 협상&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;DTLS 핸드셰이크 → keying material 추출 → SRTP 키 파생&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;RTP/SRTP 미디어 데이터 전송 (DTLS 레코드 아님)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이를 DTLS-SRTP (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc5764&quot;&gt;RFC 5764&lt;/a&gt;)라 부른다. 핸드셰이크만 DTLS이고 실제 미디어는 SRTP 패킷으로 전송된다.&lt;/p&gt;
&lt;h3 id=&quot;3-vpn--openvpn-udp-모드&quot;&gt;3. VPN / OpenVPN UDP 모드&lt;/h3&gt;
&lt;p&gt;OpenVPN은 TCP 모드와 UDP 모드를 지원한다. UDP 모드에서 DTLS와 유사한 자체 보안 레이어를 사용한다. WireGuard는 별도 프로토콜을 사용하지만 UDP 위 암호화라는 점에서 같은 범주다.&lt;/p&gt;
&lt;h3 id=&quot;4-iot-coap--dtls&quot;&gt;4. IoT: CoAP + DTLS&lt;/h3&gt;
&lt;p&gt;CoAP (Constrained Application Protocol)는 IoT 기기를 위한 HTTP 대안이다. UDP 위에서 동작하며, 보안 레이어로 DTLS를 사용한다. TLS를 쓰기에 너무 자원이 제한적인 기기(MCU)에서 DTLS 1.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;CoAP (UDP/5683)   → 평문 (테스트 환경)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CoAPS (UDP/5684)  → DTLS 암호화 (운영 환경)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;레코드-구조&quot;&gt;레코드 구조&lt;/h2&gt;
&lt;p&gt;DTLS 레코드 헤더에는 TLS와 달리 &lt;strong&gt;epoch + sequence number&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;DTLS 1.2 레코드 헤더 (13 bytes):&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;+--+--+--+--+--+--+--+--+--+--+--+--+--+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|CT|  Version | Epoch |  Sequence Num   |Length|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|1B|   2B     |  2B   |      6B         |  2B  |&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;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CT: Content Type (Handshake=22, Alert=21, ApplicationData=23)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Epoch: 키 교환 횟수 (재키잉 시 증가)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Sequence Num: 레코드 순서 번호 (중복 탐지에 사용)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;수신측은 슬라이딩 윈도우로 시퀀스 번호를 추적하여 &lt;strong&gt;재전송 중복&lt;/strong&gt;과 &lt;strong&gt;재생 공격&lt;/strong&gt;을 방어한다.&lt;/p&gt;
&lt;h2 id=&quot;실전-예시&quot;&gt;실전 예시&lt;/h2&gt;
&lt;h3 id=&quot;python으로-dtls-서버클라이언트&quot;&gt;Python으로 DTLS 서버/클라이언트&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:#6A737D&quot;&gt;# pip install dtls (PyDTLS 패키지)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; dtls &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; do_patch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;do_patch()  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# SSL 모듈을 DTLS 지원으로 패치&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;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssl&lt;/span&gt;&lt;/span&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; socket&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;# DTLS 서버&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:#B392F0&quot;&gt; dtls_server&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;    sock &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; socket.socket(socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;AF_INET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SOCK_DGRAM&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;    sock.bind((&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4433&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;    ctx &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssl.SSLContext(ssl.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;PROTOCOL_DTLS_SERVER&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;    ctx.load_cert_chain(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;server.crt&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;server.key&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:#F97583&quot;&gt;    with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ctx.wrap_socket(sock, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;server_side&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&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 style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssock:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        data, addr &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssock.recvfrom(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1024&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;        print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&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 style=&quot;color:#E1E4E8&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; from &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;addr&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;        ssock.sendto(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Hello from DTLS server!&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, addr)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;openssl로-dtls-테스트&quot;&gt;OpenSSL로 DTLS 테스트&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# DTLS 서버 시작&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_server&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -dtls1_2&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -port&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 4433&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -cert&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; server.crt&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; server.key&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;# DTLS 클라이언트 연결&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -dtls1_2&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; localhost:4433&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;# DTLS 1.3 (OpenSSL 3.2+)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_server&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -dtls&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -port&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 4433&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -cert&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; server.crt&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; server.key&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -dtls&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; localhost:4433&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;# Wireshark 캡처 - DTLS 패킷 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 필터: dtls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 복호화: Edit &gt; Preferences &gt; Protocols &gt; DTLS &gt; RSA keys list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;webrtc-datachannel-dtls-fingerprint-확인&quot;&gt;WebRTC DataChannel DTLS fingerprint 확인&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;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 브라우저에서 DataChannel DTLS 인증서 fingerprint 확인&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; pc&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; RTCPeerConnection&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;// 로컬 인증서 fingerprint (시그널링으로 상대방에게 전달)&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; cert&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; RTCPeerConnection.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;generateCertificate&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ name: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;ECDSA&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, namedCurve: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;P-256&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;console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(cert.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getFingerprints&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;// [{ algorithm: &quot;sha-256&quot;, value: &quot;AA:BB:CC:...&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:#6A737D&quot;&gt;// SDP에서 fingerprint 확인&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; offer&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; pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createOffer&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; sdpLines&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; offer.sdp.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;split&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; fingerprintLine&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; sdpLines.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; l.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;a=fingerprint:&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;console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(fingerprintLine);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// a=fingerprint:sha-256 AA:BB:CC:DD:...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;dtls-vs-quic-비교&quot;&gt;DTLS vs QUIC 비교&lt;/h2&gt;
&lt;p&gt;[[quic|QUIC]]도 UDP 위에서 TLS 1.3을 사용하지만, DTLS와는 다른 방식으로 동작한다.&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;DTLS&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;순수 보안 레이어&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 방식&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.3 핸드셰이크 응용&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 1.3 직접 통합 (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;하위 프로토콜이 처리 (SCTP 등)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;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;없음&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QPACK&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;SCTP 조합 필요&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;WebRTC, IoT, VPN&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP/3, WebTransport&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;QUIC은 “UDP 위에서 모든 것을 새로 만든” 반면, DTLS는 “기존 TLS를 UDP에 적응시킨” 접근이다.&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;/strong&gt;: 핸드셰이크 패킷 손실 시 타이머 기반 재전송이 발생한다. 고손실 환경에서 초기 연결이 TCP+TLS보다 느릴 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MTU 제약&lt;/strong&gt;: UDP 패킷이 MTU(일반적으로 1500 bytes, 터널링 시 더 작음)를 초과하면 IP fragmentation이 발생한다. DTLS 핸드셰이크 메시지가 큰 경우 레코드 단위 fragmentation이 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cookie 검증 우회&lt;/strong&gt;: DoS 방어용 Cookie Exchange를 비활성화하는 구현도 있다. 공개 서버에서는 반드시 활성화해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;epoch rollover&lt;/strong&gt;: epoch 필드(2 bytes)는 65535회 재키잉 후 오버플로우한다. 장기 실행 세션에서 주의.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인증서 검증 생략&lt;/strong&gt;: 특히 IoT 구현에서 self-signed 인증서를 검증하지 않는 경우가 많다. DTLS가 있어도 MITM에 취약해진다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[udp|UDP]] - DTLS가 동작하는 기반 프로토콜&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - DTLS의 원형&lt;/li&gt;
&lt;li&gt;[[webrtc|WebRTC]] - DTLS를 핵심 보안 레이어로 사용&lt;/li&gt;
&lt;li&gt;[[quic|QUIC]] - UDP 위 보안의 다른 접근법&lt;/li&gt;
&lt;li&gt;[[network-nat|NAT]] - DTLS와 NAT Traversal 연관성&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>security</category><category>protocol</category><category>cryptography</category><category>udp</category><category>webrtc</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>Thu, 16 Jul 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|HTTP/2]]에서 요청/응답 헤더를 압축하는 방식이다.&lt;/p&gt;
&lt;p&gt;[[http-1-1|HTTP/1.1]]에서 헤더는 매 요청마다 평문 텍스트로 반복 전송되었다. 한 페이지 로드에 수십 건의 요청이 발생하면 헤더만으로 수십 KB가 낭비된다. HPACK은 &lt;strong&gt;정적 테이블 + 동적 테이블 + Huffman 인코딩&lt;/strong&gt; 세 가지를 조합해 헤더 중복을 제거한다.&lt;/p&gt;
&lt;h2 id=&quot;문제-상황-http11-헤더의-낭비&quot;&gt;문제 상황: 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;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /api/users HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Host: example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Accept: application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Accept-Language: ko-KR,ko;q=0.9&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Accept-Encoding: gzip, deflate, br&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cookie: session_id=abc123; _ga=GA1.2.xxx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;동일한 호스트에 50개 병렬 요청을 보내면, 위 헤더가 50번 그대로 반복된다. &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Cookie&lt;/code&gt;처럼 큰 헤더가 매번 전송되어 모바일처럼 대역폭이 제한된 환경에서 특히 비효율적이다.&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;Index | Header Name          | Header Value&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;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;6     | :scheme              | http&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;7     | :scheme              | https&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;8     | :status              | 200&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;61    | www-authenticate     |&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;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;    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;    Note over C,S: 첫 번째 요청&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: &quot;Authorization: Bearer TOKEN123&quot; (리터럴로 전송 + 동적 테이블 인덱스 62 등록)&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: 두 번째 요청&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: 인덱스 62 (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: 200 OK&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;동적 테이블은 &lt;strong&gt;FIFO 큐&lt;/strong&gt;로 구성된다. 오래된 항목은 새 항목이 추가될 때 밀려나며, 테이블 최대 크기는 &lt;code&gt;SETTINGS_HEADER_TABLE_SIZE&lt;/code&gt; 프레임으로 협상한다 (기본값 4096 bytes).&lt;/p&gt;
&lt;h3 id=&quot;huffman-인코딩&quot;&gt;Huffman 인코딩&lt;/h3&gt;
&lt;p&gt;문자열로 전송해야 하는 헤더 값을 Huffman 코드로 추가 압축한다. HTTP 트래픽에서 자주 등장하는 문자에 짧은 코드를 배정해 ASCII 대비 약 30% 추가 절약을 달성한다.&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;&apos;e&apos; (0x65) → ASCII 8bit, Huffman 4bit (0b0000)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&apos;a&apos; (0x61) → ASCII 8bit, Huffman 5bit (0b00001)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&apos;q&apos; (0x71) → ASCII 8bit, Huffman 8bit (0b11101000)&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;    H[&quot;헤더 필드\n(name: value)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1{&quot;Static Table에\n존재하는가?&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2{&quot;Dynamic Table에\n존재하는가?&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    I1[&quot;인덱스 하나만 전송\n(1-2 바이트)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    I2[&quot;Dynamic Table 인덱스\n전송 (1-2 바이트)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3{&quot;Dynamic Table에\n추가할까?&quot;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L1[&quot;리터럴 + 동적 등록\n(Huffman 압축 후)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L2[&quot;리터럴만 전송\n(인덱싱 안 함)&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;    H --&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;|&quot;Yes&quot;| I1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|&quot;No&quot;| Q2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|&quot;Yes&quot;| I2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|&quot;No&quot;| Q3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|&quot;Yes&quot;| L1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|&quot;No&quot;| L2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;헤더 값이 자주 바뀌는 경우 (예: &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Content-Length&lt;/code&gt;) 는 동적 테이블 등록을 건너뛰어 테이블 공간 낭비를 방지한다. &lt;code&gt;Cache-Control: no-cache&lt;/code&gt;처럼 민감 정보가 포함된 헤더는 &lt;code&gt;Never Indexed&lt;/code&gt; 플래그로 중간 프록시의 캐시 저장을 금지할 수 있다.&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개 요청 (각 ~700 B)&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;모바일 네트워크 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;동일 호스트 반복 API 호출&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;[[http-2|HTTP/2]]의 멀티플렉싱과 함께 HPACK은 HTTP/2의 두 번째 큰 성능 이득원이다.&lt;/p&gt;
&lt;h2 id=&quot;보안-이슈-crime--hpack-bomb&quot;&gt;보안 이슈: CRIME / HPACK Bomb&lt;/h2&gt;
&lt;h3 id=&quot;crime-공격&quot;&gt;CRIME 공격&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CRIME&lt;/strong&gt; (Compression Ratio Info-leak Made Easy)은 TLS 압축 + HTTP 응답 압축을 함께 쓸 때 발생하는 사이드 채널 공격이다.&lt;/p&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;압축 전: &quot;Cookie: secret=a...&quot; → 크기 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     +  &quot;secret=a&quot;          → 압축 시 중복 제거, 크기 95 (추측 성공)&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;압축 전: &quot;Cookie: secret=a...&quot; → 크기 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     +  &quot;secret=b&quot;          → 다른 문자열, 중복 없음, 크기 102 (추측 실패)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HPACK은 이를 완전히 해결하지는 않는다. 공격자가 동일 HTTP/2 연결에서 요청을 보낼 수 있다면 동적 테이블 크기 변화를 관찰하는 비슷한 공격이 이론적으로 가능하다. RFC 7541 §7 보안 고려사항 참조.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;Never Indexed 플래그&lt;/strong&gt;: &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Cookie&lt;/code&gt; 같은 민감 헤더는 &lt;code&gt;Literal Header Field Never Indexed&lt;/code&gt; 표현을 사용해 중간 노드(프록시)의 인덱싱을 막아야 한다. HTTP/2 라이브러리 대부분이 이를 자동 처리하지만, 커스텀 구현 시 반드시 확인.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;hpack-bomb&quot;&gt;HPACK Bomb&lt;/h3&gt;
&lt;p&gt;동적 테이블에 최대 크기(4 KB)까지 헤더를 채운 뒤, 그 모든 항목을 인덱스 참조로만 이루어진 헤더 블록을 수십만 개 전송한다. 수신측은 작은 바이트를 수백 MB 메모리로 팽창시키며 디코딩해야 한다.&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;# 공격자 전송: 9 바이트 헤더 블록&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# 수신측 메모리 요구: 수 MB&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 KB)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- 참조만 있는 헤더 블록 10만 개 전송 → 테이블 전체 팽창 반복&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
서버 구현 시 &lt;code&gt;SETTINGS_MAX_HEADER_LIST_SIZE&lt;/code&gt;와 최대 헤더 블록 크기를 반드시 제한해야 한다. HTTP/2 라이브러리의 기본 설정을 확인하고 적절한 상한을 설정할 것.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;qpack-http3에서의-개선&quot;&gt;QPACK: HTTP/3에서의 개선&lt;/h2&gt;
&lt;p&gt;[[quic|QUIC]] 위에서는 HPACK의 &lt;strong&gt;순서 의존성 문제&lt;/strong&gt;가 발생한다. HTTP/2는 TCP 위에서 동작해 헤더 블록이 순서대로 도착하지만, HTTP/3의 QUIC은 스트림이 독립적이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;문제&lt;/strong&gt;: 동적 테이블 업데이트가 스트림 A에서 도착했는데, 스트림 B의 헤더가 그 업데이트를 참조한다면? QUIC에서는 스트림 B가 먼저 도착할 수 있다.&lt;/p&gt;
&lt;p&gt;&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;
&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;    E[&quot;Encoder\n(클라이언트)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D[&quot;Decoder\n(서버)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ES[&quot;Encoder Stream\n(단방향, 테이블 업데이트)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DS[&quot;Decoder Stream\n(단방향, ACK)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    RS[&quot;Request Stream\n(각 HTTP 요청)&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;    E --&gt;|&quot;동적 테이블 업데이트&quot;| ES --&gt; D&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D --&gt;|&quot;테이블 업데이트 확인&quot;| DS --&gt; E&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    E --&gt;|&quot;헤더 블록 (인덱스 참조)&quot;| RS --&gt; D&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&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;HPACK&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;QPACK&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;HTTP/2 (TCP)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP/3 (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;단일 스트림 순서 의존&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;Yes (TCP가 보장)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;No (QUIC 스트림 독립)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HOL 블로킹&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 계층에서 처리&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QPACK 레벨 동기화로 해결&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;실전-예시&quot;&gt;실전 예시&lt;/h2&gt;
&lt;h3 id=&quot;헤더-인코딩-직접-관찰-python&quot;&gt;헤더 인코딩 직접 관찰 (Python)&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:#6A737D&quot;&gt;# hpack 패키지로 HPACK 인코딩/디코딩 실습&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pip install hpack&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;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; hpack&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;encoder &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; hpack.Encoder()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;decoder &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; hpack.Decoder()&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;headers &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:#9ECBFF&quot;&gt;&quot;:method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;GET&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;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;:path&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/api/users&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;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;:scheme&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https&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;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;:authority&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;example.com&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;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;authorization&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Bearer my-token&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;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;accept&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;application/json&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;]&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;encoded &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; encoder.encode(headers)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;인코딩 전 헤더 길이: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{sum&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(k)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(v)&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:#F97583&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; k,v &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; headers)&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bytes&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:#79B8FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;HPACK 인코딩 후: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(encoded)&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bytes&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;# 디코딩&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;decoded &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; decoder.decode(encoded)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&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 style=&quot;color:#E1E4E8&quot;&gt;decoded&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;h3 id=&quot;nginx에서-hpack-확인&quot;&gt;nginx에서 HPACK 확인&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# HTTP/2 + HPACK 요청 전송&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; -v&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --http2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://example.com/api/users&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -H&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Authorization: Bearer token123&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:#6A737D&quot;&gt;# 응답 헤더에서 HTTP/2 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# &amp;#x3C; HTTP/2 200&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# &amp;#x3C; content-type: application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# &amp;#x3C; content-length: 512&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;# Wireshark에서 HTTP/2 헤더 관찰:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 필터: http2.header.name == &quot;authorization&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Python h2 라이브러리로 SETTINGS 프레임 제어&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pip install h2&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;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; h2.connection&lt;/span&gt;&lt;/span&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; h2.config&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;config &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; h2.config.H2Configuration(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;client_side&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&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;conn &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; h2.connection.H2Connection(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;config)&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;# 동적 테이블 최대 크기를 8192 bytes로 설정&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;conn.initiate_connection()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;conn.update_settings({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    h2.settings.SettingCodes.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;HEADER_TABLE_SIZE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;8192&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;    h2.settings.SettingCodes.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;MAX_HEADER_LIST_SIZE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;65536&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;함정&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;/strong&gt;: 연결이 끊기면 테이블도 초기화된다. HTTP/2 연결 풀링이 끊기는 상황 (재배포, 로드 밸런서 교체 등)에서 첫 몇 개 요청은 압축 효율이 낮다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;프록시가 테이블 공유 안 함&lt;/strong&gt;: 클라이언트-프록시, 프록시-서버 각각 별개 HPACK 컨텍스트. 프록시를 거치면 클라이언트 테이블이 서버에 전달되지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;헤더 순서가 성능에 영향&lt;/strong&gt;: 자주 등장하는 헤더를 먼저 보내면 동적 테이블 효율이 높아진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HPACK Bomb 방어 필수&lt;/strong&gt;: &lt;code&gt;SETTINGS_MAX_HEADER_LIST_SIZE&lt;/code&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|HTTP/2]] - HPACK이 사용되는 프로토콜&lt;/li&gt;
&lt;li&gt;[[http-3|HTTP/3]] - QPACK을 사용하는 후속 버전&lt;/li&gt;
&lt;li&gt;[[quic|QUIC]] - HTTP/3의 전송 계층&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - HPACK과 함께 동작하는 암호화 레이어&lt;/li&gt;
&lt;li&gt;[[http-1-1|HTTP/1.1]] - HPACK 도입 이전 버전&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>http</category><category>http2</category><category>compression</category><category>security</category><author>koa (김신건)</author></item><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>Wed, 15 Jul 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;p&gt;IP 는 [[OSI 7 Layer]] 의 L3 (Network Layer) 에서 동작하며, 라우터가 패킷을 목적지까지 전달하는 기반이 된다.&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^32 (약 43억 개), 이미 고갈 상태.&lt;/p&gt;
&lt;h3 id=&quot;주소-구조-cidr&quot;&gt;주소 구조 (CIDR)&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;IP:      192  .  168  .   1   .   1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Binary: 11000000.10101000.00000001.00000001&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        |&amp;#x3C;--- 네트워크 부분 (prefix) ---&gt;| 호스트|&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;192.168.1.0/24&lt;/code&gt; 표기에서 &lt;code&gt;/24&lt;/code&gt; 는 앞 24 bit 가 네트워크 식별자, 뒤 8 bit 가 호스트 주소임을 의미한다.&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;CIDR&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 (DHCP 실패 시 자동 할당)&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;tr&gt;&lt;td align=&quot;left&quot;&gt;브로드캐스트&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.255.255/32&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;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;CIDR&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;/8&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.0.0.0&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;16,777,214&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;대형 사내망 (Class A)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/16&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.0.0&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;65,534&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;중형 사내망 (Class B)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/24&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.255.0&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;254&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;일반 서브넷 (Class C)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/28&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.255.240&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;14&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;/30&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.255.252&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;P2P 링크&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/32&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;255.255.255.255&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;단일 호스트&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;code&gt;/24&lt;/code&gt; 서브넷에서 256개 IP 중 254개만 사용 가능하다. 첫 주소(&lt;code&gt;192.168.1.0&lt;/code&gt;)는 네트워크 주소, 마지막 주소(&lt;code&gt;192.168.1.255&lt;/code&gt;)는 브로드캐스트 주소로 예약된다.&lt;/p&gt;
&lt;/blockquote&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;VPC 10.0.0.0/16 분할:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Public subnet:  10.0.1.0/24   → 254 호스트&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Private subnet: 10.0.2.0/24   → 254 호스트&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  DB subnet:      10.0.3.0/24   → 254 호스트&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Reserved:       10.0.4.0/24 ~ 10.0.255.0/24&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;ipv6&quot;&gt;IPv6&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;128bit&lt;/strong&gt; 주소, 16진수 8 그룹으로 표기: &lt;code&gt;2001:0db8:85a3:0000:0000:8a2e:0370:7334&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;연속된 0 그룹 축약: &lt;code&gt;2001:db8:85a3::8a2e:370:7334&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;ipv4-vs-ipv6-비교&quot;&gt;IPv4 vs IPv6 비교&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;IPv4&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;IPv6&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;32&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;128&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;약 43억&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;약 3.4 x 10^38&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;code&gt;192.168.1.1&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;콜론 16진수 &lt;code&gt;::1&lt;/code&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;20 byte (가변)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;40 byte (고정)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;NAT 필요&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;/64 (LAN 하나)&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;제거됨 (상위 계층 위임)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;ipv6-특수-주소&quot;&gt;IPv6 특수 주소&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;::1&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;loopback (IPv4 의 &lt;code&gt;127.0.0.1&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;fe80::/10&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;link-local (라우터를 넘지 않음)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;fc00::/7&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;unique local (사설 주소, IPv4 의 &lt;code&gt;10.0.0.0/8&lt;/code&gt; 유사)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;ff00::/8&lt;/code&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;code&gt;2000::/3&lt;/code&gt;&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;public-vs-private&quot;&gt;Public vs Private&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;    subgraph LAN[&quot;사내 / 가정 LAN (사설 IP)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        PC1[&quot;PC1: 192.168.1.10&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        PC2[&quot;PC2: 192.168.1.11&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        Phone[&quot;모바일: 192.168.1.50&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;    NAT[&quot;NAT 라우터\n공인: 203.0.113.5&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Internet[&quot;인터넷&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC1 --&gt; NAT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC2 --&gt; NAT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Phone --&gt; NAT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    NAT --&gt; Internet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Public IP&lt;/strong&gt;: 인터넷에서 직접 라우팅 가능. ISP 가 할당.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Private IP&lt;/strong&gt;: 로컬 네트워크 전용. 외부에서 직접 접근 불가. NAT 를 통해 공인 IP 와 매핑.&lt;/p&gt;
&lt;h2 id=&quot;nat-network-address-translation&quot;&gt;NAT (Network Address Translation)&lt;/h2&gt;
&lt;p&gt;IPv4 고갈 문제를 보완하기 위해 라우터가 내부 사설 주소를 외부 공인 주소로 변환한다.&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;    participant PC as PC192168110&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant R as RouterNAT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant S as WebServer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    PC-&gt;&gt;R: SRC=192.168.1.10:4000 DST=1.2.3.4:80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: SRC=203.0.113.5:12345 DST=1.2.3.4:80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over R: NAT 테이블에 매핑 기록&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: SRC=1.2.3.4:80 DST=203.0.113.5:12345&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;PC: SRC=1.2.3.4:80 DST=192.168.1.10:4000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NAT 는 연결 추적(connection tracking) 이 필요하며, [[stateful-vs-stateless-firewall|Stateful Firewall]] 과 함께 동작한다.&lt;/p&gt;
&lt;p&gt;자세한 내용: [[network-nat|NAT]].&lt;/p&gt;
&lt;h2 id=&quot;dhcp&quot;&gt;DHCP&lt;/h2&gt;
&lt;p&gt;동적 IP 할당 프로토콜. 4단계 DORA 플로우:&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;    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 D as DHCPServer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;D: DHCPDISCOVER (broadcast)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D-&gt;&gt;C: DHCPOFFER (IP 제안, lease 시간)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;D: DHCPREQUEST (제안 수락)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D-&gt;&gt;C: DHCPACK (확정, lease 시작)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over C,D: IP 임차 기간 만료 전 갱신 필요&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discover&lt;/strong&gt;: 클라이언트가 브로드캐스트로 DHCP 서버 탐색&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offer&lt;/strong&gt;: 서버가 사용 가능한 IP 와 임차 기간 제안&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request&lt;/strong&gt;: 클라이언트가 특정 IP 를 요청&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acknowledge&lt;/strong&gt;: 서버가 IP 할당 확정&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ip-패킷-구조-ipv4&quot;&gt;IP 패킷 구조 (IPv4)&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       4       8      16      24      32 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; |Version|  IHL  |  ToS  |    Total Len  |&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|Frag Offs|&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; |   TTL |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 (optional)            |&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;TTL&lt;/strong&gt; (Time to Live): 라우터 통과 시마다 1 감소. 0 이 되면 패킷 폐기하고 ICMP Time Exceeded 전송 (루프 방지, traceroute 원리)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol&lt;/strong&gt;: 상위 계층 식별 (TCP=6, UDP=17, ICMP=1)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags + Fragment Offset&lt;/strong&gt;: MTU 초과 패킷을 단편화 (분할) 하기 위한 필드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;실전-예시&quot;&gt;실전 예시&lt;/h2&gt;
&lt;h3 id=&quot;aws-vpc-서브넷-설계&quot;&gt;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       (65,534 개 IP)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Public AZ-a:  10.0.1.0/24       (254 개, 인터넷 게이트웨이)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Public AZ-b:  10.0.2.0/24       (254 개, 인터넷 게이트웨이)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Private AZ-a: 10.0.11.0/24      (254 개, NAT 게이트웨이)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Private AZ-b: 10.0.12.0/24      (254 개, NAT 게이트웨이)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  DB AZ-a:      10.0.21.0/24      (254 개, 외부 접근 불가)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  DB AZ-b:      10.0.22.0/24      (254 개, 외부 접근 불가)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;ipv4ipv6-이중-스택-dual-stack&quot;&gt;IPv4/IPv6 이중 스택 (Dual Stack)&lt;/h3&gt;
&lt;p&gt;현재 인터넷은 IPv4 와 IPv6 를 동시 운용한다. 서버는 두 주소 모두 수신하고, 클라이언트는 &lt;strong&gt;Happy Eyeballs&lt;/strong&gt; (RFC 8305) 로 더 빠른 쪽을 선택한다.&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;ss&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -tn&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -l&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;( sport = :80 or sport = :443 )&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# IPv6 형식으로 IPv4 도 포함: ::ffff:0.0.0.0&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;# IPv6 연결 확인&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; -6&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://ipv6.google.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# IP 주소 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ip&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; addr&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; show&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;           # Linux&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ifconfig&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;               # macOS / 구형&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;ip&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; route&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; show&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:#6A737D&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;# ICMP ping (L3 연결성)&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; 1.1.1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ping6&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 2606:4700:4700::1111&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   # IPv6&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;# 경로 추적 (TTL 활용)&lt;/span&gt;&lt;/span&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:#79B8FF&quot;&gt; 1.1.1.1&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;/24 = 256개 호스트?&lt;/strong&gt; 네트워크 주소와 브로드캐스트 주소를 빼면 254개.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private IP = 보안?&lt;/strong&gt; NAT 뒤라도 내부 네트워크에서는 직접 접근 가능. 방화벽 별도 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPv6 에는 NAT 불필요 = 모든 디바이스가 공인 IP.&lt;/strong&gt; 기존 NAT 기반 보안 정책을 IPv6 에 그대로 적용하면 외부 노출 위험.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/32 는 단일 호스트&lt;/strong&gt;, /0 은 모든 IP. 라우팅 테이블이나 ACL 작성 시 혼동 주의.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;172.16.0.0/12 = 172.16.0.0 ~ 172.31.255.255.&lt;/strong&gt; 12bit prefix 라 172.16~172.31 범위. 직관적이지 않으므로 주의.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&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]], [[UDP]]&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[network-nat|NAT]] - 사설 IP 의 인터넷 연결 방법&lt;/li&gt;
&lt;li&gt;[[network-cidr-subnetting|CIDR / 서브네팅]] - 서브넷 분할 상세&lt;/li&gt;
&lt;li&gt;[[network-dns|DNS]] - 도메인 to IP 매핑&lt;/li&gt;
&lt;li&gt;[[TCP]], [[UDP]] - IP 위 전송 계층&lt;/li&gt;
&lt;li&gt;[[OSI 7 Layer]] - L3 Network 계층 맥락&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>ip</category><category>ipv4</category><category>ipv6</category><category>cidr</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>Wed, 15 Jul 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;PDU (단위)&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, SCTP&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;. 응용 프로그램이 직접 사용하는 프로토콜.&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;포트&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;HTTP/1.1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;80&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;HTTPS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;443&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP + TLS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HTTP/2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;443&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;HTTP/3&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;443 (UDP)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QUIC 기반&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;DNS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;53 (UDP/TCP)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;도메인 → IP 변환&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;SMTP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;25 / 465 (TLS)&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;SSH&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;22&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;gRPC&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;443&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP/2 위 RPC 프레임워크&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&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;GET /api/users HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Host: example.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Authorization: Bearer eyJhbGci...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 건 [[HTTP/1.1]], [[HTTP/2]], [[HTTP/3]], [[gRPC]], [[network-dns]].&lt;/p&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;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;/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;TLS (실제로는 L4-L7 사이)&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;JSON, Protobuf, MessagePack, CBOR&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;gzip, deflate, brotli, zstd&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;UTF-8, MIME Base64&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;현대 프로토콜에서는 L5/L6 경계가 모호하고, 응용 계층(L7) 과 통합되는 경우가 많다.&lt;/p&gt;
&lt;h3 id=&quot;l5-session&quot;&gt;L5 Session&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;세션 관리 계층&lt;/em&gt;. 대화의 시작/유지/종료.&lt;/p&gt;
&lt;p&gt;현대에서는 &lt;em&gt;L7 의 일부&lt;/em&gt; 또는 &lt;em&gt;애플리케이션 자체&lt;/em&gt; 가 담당한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP cookie: 상태 없는 HTTP 위에서 세션 유지&lt;/li&gt;
&lt;li&gt;JWT: 토큰 기반 세션 상태 관리&lt;/li&gt;
&lt;li&gt;WebSocket: HTTP Upgrade 후 지속 세션 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;l4-transport&quot;&gt;L4 Transport&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;end-to-end 신뢰성 + 흐름 제어&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, SSH, DB 연결&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;포트 범위:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Well-known (0-1023)&lt;/strong&gt;: HTTP(80), HTTPS(443), SSH(22), DNS(53)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registered (1024-49151)&lt;/strong&gt;: 애플리케이션 공식 등록&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic/Ephemeral (49152-65535)&lt;/strong&gt;: 클라이언트 임시 포트&lt;/li&gt;
&lt;/ul&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;/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;역할&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;IPv4 / IPv6&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;ICMP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;제어 메시지 (ping, traceroute, TTL Exceeded)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;BGP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;인터넷 AS 간 경로 교환 (인터넷의 뼈대)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;OSPF / IS-IS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;AS 내부 링크 상태 라우팅&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;ARP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;IP → MAC 주소 매핑 (L2/L3 경계)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;TTL (Time to Live): 각 라우터 통과 시 1 감소, 0 이 되면 폐기. traceroute 는 TTL 을 1씩 증가시키며 각 라우터의 ICMP TTL Exceeded 응답을 수집.&lt;/p&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; 기반 통신. 스위치, WiFi AP 의 영역.&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;설명&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Ethernet (IEEE 802.3)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;유선 LAN 표준, 최대 1500 byte MTU&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;WiFi (IEEE 802.11)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;무선 LAN, 802.11ax (WiFi 6)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;ARP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;IP 주소를 MAC 주소로 변환&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;VLAN (802.1Q)&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;PPP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;점대점 링크 (WAN, DSL)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Ethernet 프레임 구조:&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;[Dest MAC (6B)][Src MAC (6B)][EtherType (2B)][Payload][FCS (4B)]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;ul&gt;
&lt;li&gt;&lt;strong&gt;유선&lt;/strong&gt;: Cat5e/Cat6 (1Gbps), Cat7/Cat8 (10Gbps), 광섬유 (SM/MM)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;무선&lt;/strong&gt;: 2.4GHz / 5GHz / 6GHz 대역&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장비&lt;/strong&gt;: 허브, 리피터, NIC (Network Interface Card)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;패킷의-캡슐화&quot;&gt;패킷의 캡슐화&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;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;|&quot;L6/L5 처리&quot;| L6Frame[&quot;+ TLS 암호화 래퍼&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L6Frame --&gt;|&quot;L4 TCP 헤더 추가&quot;| 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;|&quot;L3 IP 헤더 추가&quot;| 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;|&quot;L2 Ethernet 프레임 래핑&quot;| 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;|&quot;L1 변환&quot;| Bit[&quot;bits on wire&quot;]&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;. 수신 측은 &lt;em&gt;역순으로 벗겨&lt;/em&gt; 올라간다.&lt;/p&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;HTTP 요청 200 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  + TLS 레코드 헤더: 5 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  = TLS payload: 205 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    + TCP 헤더: 20 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    = TCP segment: 225 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      + IP 헤더: 20 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      = IP packet: 245 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        + Ethernet 헤더: 14 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        + Ethernet FCS: 4 byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        = Ethernet frame: 263 byte&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;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;td&gt;기본 연결성 확인&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;traceroute&lt;/code&gt; / &lt;code&gt;tracert&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L3&lt;/td&gt;&lt;td&gt;경로 추적, 지연 구간 식별&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;td&gt;포트 개방 여부 확인&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;td&gt;포트 스캔&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;td&gt;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;td&gt;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;td&gt;도메인 → IP 조회&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;td&gt;패킷 캡처 및 분석&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ss / netstat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;L4&lt;/td&gt;&lt;td&gt;소켓 연결 상태 확인&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;실전-디버깅-시나리오&quot;&gt;실전 디버깅 시나리오&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;# 1. L3: 기본 연결성&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:#9ECBFF&quot;&gt; api.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;# 2. L3: 경로 추적&lt;/span&gt;&lt;/span&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; api.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;# 3. L4: 포트 접근&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;nc&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -zv&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; api.example.com&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 443&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;# 4. L6: TLS 핸드셰이크&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; api.example.com:443&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -servername&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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 5. L7: HTTP 요청&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; -v&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://api.example.com/health&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;계층-간-상호작용-예시&quot;&gt;계층 간 상호작용 예시&lt;/h2&gt;
&lt;p&gt;HTTP/1.1 GET 요청이 서버에 도달하기까지:&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;    participant App as L7 Application&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant TLS as L6 TLS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant TCP as L4 TCP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant IP as L3 IP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant ETH as L2 Ethernet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    App-&gt;&gt;TLS: HTTP 요청 평문&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TLS-&gt;&gt;TCP: 암호화된 TLS 레코드&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    TCP-&gt;&gt;IP: TCP 세그먼트&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    IP-&gt;&gt;ETH: IP 패킷&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ETH-&gt;&gt;ETH: 비트 전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over ETH: 물리 계층 전달 후 역순 복원&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;“L7 문제”로 단정&lt;/strong&gt;: 실제는 L4 timeout 일 수 있다. &lt;em&gt;계층별로 위에서 아래로 분리 진단&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS 가 L4 라는 오해&lt;/strong&gt;: TLS 는 L5/L6 사이. L4 (TCP/UDP) 위에서 동작.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS 인증서 문제를 방화벽 탓으로&lt;/strong&gt;: &lt;code&gt;openssl s_client&lt;/code&gt; 로 핸드셰이크 만 따로 확인.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS 가 L7 인지 L3 인지 혼동&lt;/strong&gt;: DNS 는 L7 (애플리케이션). UDP/TCP 53 위에서 동작.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;QUIC 은 L4?&lt;/strong&gt;: QUIC 은 L3 (IP/UDP) 위에서 L4+L5+L6 역할을 모두 수행. 기존 OSI 모델로 정확히 분류 어려움.&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>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>Wed, 15 Jul 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;네트워크 프로토콜에서는 HTTP, TCP 두 계층 모두에서 발생할 수 있으며, HTTP 버전에 따라 해결 범위가 다르다.&lt;/p&gt;
&lt;h2 id=&quot;http11-의-hol-blocking&quot;&gt;HTTP/1.1 의 HOL Blocking&lt;/h2&gt;
&lt;p&gt;HTTP/1.1 은 pipelining 을 통해 한 TCP 연결에서 여러 요청을 연속 전송할 수 있지만, 응답은 반드시 &lt;strong&gt;요청 순서대로&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;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;    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 style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: GET /slow-api&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 /icon.png&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 /style.css&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over S: slow-api 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: 200 /slow-api&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 /icon.png&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;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/icon.png&lt;/code&gt; 와 &lt;code&gt;/style.css&lt;/code&gt; 는 즉시 응답 가능해도 &lt;code&gt;/slow-api&lt;/code&gt; 가 끝날 때까지 대기해야 한다.&lt;/p&gt;
&lt;p&gt;브라우저가 도메인당 6개 TCP 연결을 병렬 유지하는 이유: HOL 영향을 연결별로 분산하기 위함.&lt;/p&gt;
&lt;h2 id=&quot;tcp-레벨-hol-blocking&quot;&gt;TCP 레벨 HOL Blocking&lt;/h2&gt;
&lt;p&gt;[[TCP]] 는 순서 보장을 위해 수신 버퍼에서 &lt;strong&gt;순서대로만&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;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;    participant S as Sender&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant R as Receiver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 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;R: Seq 2 손실&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 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;R: Seq 4 버퍼 대기&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: SACK 요청, Seq 2 누락&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over R: Seq 3,4 수신됐지만 앱 전달 불가&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 2 재전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over R: Seq 2 수신 후 2,3,4 순서 전달&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TCP 재전송 타임아웃(RTO) 이 길수록, 손실율이 높은 네트워크일수록 이 대기가 길어진다.&lt;/p&gt;
&lt;h3 id=&quot;재전송-큐와-버퍼-팽창&quot;&gt;재전송 큐와 버퍼 팽창&lt;/h3&gt;
&lt;p&gt;TCP 의 HOL 이 심각해지는 조건:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;높은 RTT 환경&lt;/strong&gt;: RTO = 2 x RTT 기본값. RTT 가 100ms 면 RTO 200ms 이상&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;무선 환경 손실&lt;/strong&gt;: WiFi, LTE 에서 패킷 손실율 2% 이상이면 TCP HOL 체감 심화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bufferbloat&lt;/strong&gt;: 중간 라우터 버퍼가 과잉 증가 → 큐잉 지연이 HOL 효과를 증폭&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Retransmit&lt;/strong&gt;: 3개 중복 ACK 로 RTO 없이 즉시 재전송 가능하지만 HOL 은 그대로&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;http2-가-해결한-것과-해결-못한-것&quot;&gt;HTTP/2 가 해결한 것과 해결 못한 것&lt;/h2&gt;
&lt;p&gt;HTTP/2 의 &lt;strong&gt;스트림 멀티플렉싱&lt;/strong&gt; 은 HTTP 레벨 HOL 을 해결한다. 단일 TCP 연결에서 stream ID 로 구분된 frame 을 인터리브해 송수신한다.&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;    subgraph H2[&quot;HTTP/2 단일 TCP 연결&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F1[&quot;Stream 1: GET /api/users&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F2[&quot;Stream 3: GET /logo.png&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F3[&quot;Stream 5: GET /style.css&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;    Wire[&quot;TCP 스트림 (frame 인터리브)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F1 -.-&gt;|frame| Wire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F2 -.-&gt;|frame| Wire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F3 -.-&gt;|frame| Wire&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그러나 &lt;strong&gt;TCP 레벨의 HOL Blocking 은 여전히 남는다&lt;/strong&gt;. 한 TCP 패킷 손실 시, 손실된 패킷이 어느 스트림에 속하든 TCP 는 해당 연결의 모든 스트림을 블로킹한다.&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;HTTP/1.1&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/2&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/3 (QUIC)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;HTTP 레벨 HOL&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;있음&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;없음&lt;/strong&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;TCP 레벨 HOL&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;있음&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;있음&lt;/strong&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;1 (pipelining)&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;&lt;strong&gt;HTTP/1.1 보다 낮을 수 있음&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;높음&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
패킷 손실율이 높은 환경(WiFi, 이동통신)에서는 HTTP/2 가 HTTP/1.1 여러 병렬 연결보다 &lt;strong&gt;오히려 느릴 수 있다&lt;/strong&gt;. 단일 TCP 연결에서 HOL 이 발생하면 멀티플렉싱된 모든 스트림이 동시에 블로킹되기 때문이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;quic-가-해결한-것&quot;&gt;[[QUIC]] 가 해결한 것&lt;/h2&gt;
&lt;p&gt;QUIC 은 UDP 위에서 각 스트림이 &lt;strong&gt;독립적인 전송 제어&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;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 QUIC[&quot;QUIC 연결 (UDP 기반)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SA[&quot;Stream A: 비디오&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SB[&quot;Stream B: 오디오&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        SC[&quot;Stream C: 메타데이터&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;    Loss[&quot;Stream A 패킷 손실&quot;] --&gt; SA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SA --&gt;|&quot;Stream A 재전송만&quot;| SA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SB --&gt;|&quot;영향 없음&quot;| OK[&quot;정상 수신&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SC --&gt;|&quot;영향 없음&quot;| OK&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 스트림은 자체 시퀀스 번호와 ACK 를 관리하므로, 스트림 간 의존성이 없다.&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;QUIC 연결 내부&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream 1 (seq: 0-100, ACK: 87)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └── seq 88 손실 → Stream 1 만 재전송 대기&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream 2 (seq: 0-50, ACK: 50) → 계속 진행&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream 3 (seq: 0-30, ACK: 30) → 계속 진행&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[[HTTP/3]] 은 QUIC 위에서 동작하며, TCP 의 HOL Blocking 을 근본적으로 해결한다.&lt;/p&gt;
&lt;h3 id=&quot;quic-connection-migration&quot;&gt;QUIC Connection Migration&lt;/h3&gt;
&lt;p&gt;[[QUIC]] 는 HOL 해결 외에도 Connection ID 기반으로 IP/Port 변경 시에도 기존 연결을 유지한다. 와이파이에서 셀룰러로 전환해도 TCP 처럼 연결이 끊기지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;hol-의-영향-측정&quot;&gt;HOL 의 영향 측정&lt;/h2&gt;
&lt;p&gt;HOL 의 영향은 패킷 손실율과 RTT 에 따라 크게 달라진다.&lt;/p&gt;
&lt;h3 id=&quot;손실율별-최대-throughput-저하-이론적-추정&quot;&gt;손실율별 최대 throughput 저하 (이론적 추정)&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;TCP 단일 연결&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/2 (30 streams)&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HTTP/3 (QUIC)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0%&lt;/td&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;기준&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;0.1%&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-5% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-15% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-1% 내외&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;1%&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-20% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-40% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-3% 내외&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;-40% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-60% 내외&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;-5% 내외&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;HTTP/2 의 멀티플렉싱 특성상 스트림이 많을수록 HOL 에 의한 cascading 블로킹 효과가 커진다.&lt;/p&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;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; -c&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 8.8.8.8&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; tail&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -2&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;# TCP 재전송율 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;netstat&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; retransmit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ss&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&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;# tcpdump 로 TCP retransmit 관찰&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;tcpdump&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; eth0&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;tcp[tcpflags] &amp;#x26; tcp-push != 0&apos;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -nn&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;브라우저-6-연결-전략-http11-시대&quot;&gt;브라우저 6-연결 전략 (HTTP/1.1 시대)&lt;/h3&gt;
&lt;p&gt;Chrome 등 브라우저는 도메인당 최대 6개의 TCP 연결을 유지해 HOL 를 병렬 분산시켰다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Domain Sharding&lt;/strong&gt;: &lt;code&gt;static1.example.com&lt;/code&gt;, &lt;code&gt;static2.example.com&lt;/code&gt; 등 서브도메인으로 리소스를 분산해 연결 한도를 우회하는 기법. HTTP/1.1 최적화였지만 HTTP/2 이후에는 오히려 TLS 핸드셰이크 오버헤드만 키운다.&lt;/p&gt;
&lt;h3 id=&quot;http3-채택-현황&quot;&gt;HTTP/3 채택 현황&lt;/h3&gt;
&lt;p&gt;Cloudflare, Fastly 등 CDN:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트 to 엣지: HTTP/3 (QUIC) 우선 사용&lt;/li&gt;
&lt;li&gt;엣지 to 오리진: HTTP/2 또는 HTTP/3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;클라이언트 쪽 HOL 는 QUIC 으로 해결하고, 신뢰할 수 있는 데이터센터 네트워크에서는 HTTP/2 도 무방하다.&lt;/p&gt;
&lt;h3 id=&quot;고성능-게임--실시간-스트리밍&quot;&gt;고성능 게임 / 실시간 스트리밍&lt;/h3&gt;
&lt;p&gt;TCP 기반 게임에서 패킷 손실로 전체 화면이 멈추는 현상(stutter) 이 발생할 수 있다. UDP 기반 자체 프로토콜이나 WebRTC DataChannel 로 게임 state, 오디오, 채팅을 독립 스트림으로 처리.&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;HOL 발생 계층&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;HTTP/1.1 pipelining&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP + TCP 모두&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;6개 병렬 연결&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;HTTP/2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 레벨만&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QUIC 전환&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;HTTP/3 (QUIC)&lt;/td&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;UDP 방화벽 차단 주의&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;WebSocket&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 레벨&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;gRPC + HTTP/2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TCP 레벨&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;gRPC over QUIC&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;sack-selective-ack-으로-hol-부분-완화&quot;&gt;SACK (Selective ACK) 으로 HOL 부분 완화&lt;/h2&gt;
&lt;p&gt;기본 TCP 는 손실 패킷 이후 모든 것을 재전송해야 했지만, &lt;strong&gt;SACK (RFC 2018)&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;Without SACK:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Sender → Receiver: 1, 2, 3, 4, 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  2번 손실 → 2, 3, 4, 5 모두 재전송&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;With SACK:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Receiver → Sender: ACK 2 요청, SACK [3-5 수신됨]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  → 2 만 재전송&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SACK 은 HOL 을 제거하지는 않지만 &lt;strong&gt;재전송 데이터양을 줄여&lt;/strong&gt; 블로킹 시간을 단축한다. 현대 운영체제는 기본적으로 SACK 을 활성화한다.&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;strong&gt;데이터베이스 단일 큐 워커&lt;/strong&gt;: 앞 작업이 느리면 뒤 작업 전체 블로킹&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kafka 파티션&lt;/strong&gt;: 파티션 내 메시지는 순서 보장, 파티션 간은 독립 처리&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;멀티스레드 ordered 결과 병합&lt;/strong&gt;: 스레드 풀 결과를 순서대로 머지하는 단계에서 발생&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;데이터베이스 MVCC&lt;/strong&gt;: 장시간 트랜잭션이 vacuum 프로세스를 블로킹&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;해결책은 보통 &lt;strong&gt;독립 처리 단위로 분할&lt;/strong&gt; (sharding, partitioning, stream 격리).&lt;/p&gt;
&lt;h3 id=&quot;스위치-레벨-hol&quot;&gt;스위치 레벨 HOL&lt;/h3&gt;
&lt;p&gt;네트워크 스위치에서도 같은 현상이 발생한다. 입력 포트에서 출력 포트로 패킷을 전달할 때, 출력 포트가 바쁘면 그 포트로 향하는 패킷이 큐의 앞을 차지해 다른 포트로 향하는 패킷을 블로킹한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Virtual Output Queue (VOQ)&lt;/strong&gt; 는 출력 포트별로 별도 큐를 만들어 이를 해결하는 기법이다. 고성능 스위치에서 사용된다.&lt;/p&gt;
&lt;h2 id=&quot;함정&quot;&gt;함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;HTTP/2 = HOL 완전 해결?&lt;/strong&gt; HTTP 레벨만 해결. 패킷 손실 환경에서는 TCP HOL 이 HTTP/1.1 다중 연결보다 더 나쁠 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP/3 = 항상 빠름?&lt;/strong&gt; UDP 차단 방화벽에서는 HTTP/2 fallback 발생. 기업 네트워크에서 HTTP/3 비율이 낮은 이유.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 많으면 무조건 유리?&lt;/strong&gt; HTTP/2 이후에는 Domain Sharding 이 TLS 핸드셰이크 오버헤드만 가중.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCP HOL = 패킷 손실만?&lt;/strong&gt; Bufferbloat (버퍼 팽창) 도 큐잉 지연을 일으켜 유사 HOL 효과를 냄.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RTT 낮으면 HOL 무관?&lt;/strong&gt; RTT 가 낮아도 손실율이 높으면 HOL 재전송 대기가 반복됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;브라우저에서-hol-체험&quot;&gt;브라우저에서 HOL 체험&lt;/h2&gt;
&lt;p&gt;개발자 도구 Network 탭에서 HTTP/1.1 vs HTTP/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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# HTTP/1.1 강제 (curl)&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; --http1.1&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /dev/null&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -w&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;%{time_total}\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://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;# HTTP/2 사용 (기본)&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; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /dev/null&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -w&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;%{time_total}\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://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;# HTTP/3 시도 (지원 시)&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; --http3&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /dev/null&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -w&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;%{time_total}\n&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chrome DevTools: Network 탭 → Protocol 컬럼 활성화. &lt;code&gt;h2&lt;/code&gt; (HTTP/2), &lt;code&gt;h3&lt;/code&gt; (HTTP/3), &lt;code&gt;http/1.1&lt;/code&gt; 구분 가능.&lt;/p&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[QUIC]] - 스트림 격리로 TCP HOL 해결&lt;/li&gt;
&lt;li&gt;[[TCP]] - HOL Blocking 의 근원 계층&lt;/li&gt;
&lt;li&gt;[[HTTP/2]] - HTTP 레벨 HOL 해결&lt;/li&gt;
&lt;li&gt;[[HTTP/3]] - QUIC 기반, 완전 해결&lt;/li&gt;
&lt;li&gt;[[OSI 7 Layer]] - 계층별 문제 진단 기준&lt;/li&gt;
&lt;li&gt;[[websocket]] - WebSocket 도 TCP 기반, 장기 HOL 영향 받음&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>http</category><category>protocol</category><category>performance</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>Wed, 15 Jul 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|UDP]] 기반 전송 프로토콜이다. [[http-3|HTTP/3]]의 기반이 된다.&lt;/p&gt;
&lt;p&gt;기존 [[tcp|TCP]]가 가지는 문제들을 OS 커널 수정 없이, 사용자 영역에서 해결하기 위해 [[udp|UDP]] 위에 구현되었다.&lt;/p&gt;
&lt;h2 id=&quot;언제-쓰이나&quot;&gt;언제 쓰이나&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HTTP/3&lt;/strong&gt;: 브라우저와 서버 간 최신 웹 전송 (Google, Cloudflare, Facebook 기본 활성)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebTransport&lt;/strong&gt;: 브라우저에서 양방향 스트림 (WebSocket 대체 후보)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;게임 / 실시간 미디어&lt;/strong&gt;: 저지연 + 패킷 손실 내성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모바일 앱&lt;/strong&gt;: Wi-Fi / 셀룰러 전환 시 연결 유지 (Connection Migration)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tcp-vs-quic-핸드셰이크&quot;&gt;TCP vs QUIC 핸드셰이크&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;    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 style=&quot;color:#E1E4E8&quot;&gt;    Note over C,S: TCP + TLS 1.3 (최초, 2 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: SYN&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: ACK + TLS ClientHello&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: TLS ServerHello + 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: TLS Finished + HTTP 요청&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 응답&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: QUIC (최초, 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&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 + ServerHello + Handshake + 1-RTT Data&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 Finished + HTTP/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: HTTP/3 응답&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: QUIC 재접속 (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 Data + ClientHello&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: 서버 응답&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;QUIC 은 TLS 1.3 핸드셰이크를 전송 계층 협상과 통합했다. 최초 연결은 1 RTT, 세션 티켓을 캐시해 두면 재접속은 0-RTT 로 데이터를 즉시 전송할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;핵심-특성&quot;&gt;핵심 특성&lt;/h2&gt;
&lt;h3 id=&quot;1-stream-격리와-hol-blocking-해결&quot;&gt;1. Stream 격리와 HOL Blocking 해결&lt;/h3&gt;
&lt;p&gt;[[head-of-line-blocking|Head-of-Line Blocking]]: TCP 의 bytestream 모델에서 한 패킷 손실이 그 이후 모든 데이터를 막는다. QUIC 은 스트림을 독립 단위로 격리해, 한 스트림의 패킷 손실이 다른 스트림에 영향을 주지 않는다.&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;    subgraph &quot;TCP (단일 바이트스트림)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        P1[&quot;패킷 1&quot;] --&gt; P2[&quot;패킷 2 손실&quot;] --&gt; P3[&quot;패킷 3 대기&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 &quot;QUIC (독립 스트림)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S1[&quot;스트림 A: 패킷 1, 3 전달&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S2[&quot;스트림 B: 패킷 2 손실 (독립)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        S3[&quot;스트림 C: 패킷 4 즉시 전달&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;h3 id=&quot;2-tls-13-통합&quot;&gt;2. TLS 1.3 통합&lt;/h3&gt;
&lt;p&gt;전송과 암호화 협상이 분리되어 있지 않다. QUIC 패킷은 헤더를 제외하고 &lt;strong&gt;항상 암호화&lt;/strong&gt;된다. 중간 네트워크 장비가 내부를 들여다볼 수 없어 이후 프로토콜 확장도 방화벽에 차단될 우려가 없다.&lt;/p&gt;
&lt;h3 id=&quot;3-connection-id-와-connection-migration&quot;&gt;3. Connection ID 와 Connection Migration&lt;/h3&gt;
&lt;p&gt;TCP 는 (소스 IP, 소스 포트, 목적지 IP, 목적지 포트) 4-tuple 로 연결을 식별한다. 모바일 환경에서 Wi-Fi 에서 셀룰러로 전환하면 IP 가 바뀌어 연결이 끊긴다.&lt;/p&gt;
&lt;p&gt;QUIC 은 별도의 &lt;strong&gt;Connection ID&lt;/strong&gt; 로 연결을 식별한다. IP 나 포트가 바뀌어도 같은 Connection ID 로 연결을 유지한다.&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;    Phone[&quot;스마트폰&quot;] --&gt;|&quot;IP: 192.168.1.5 (Wi-Fi)&quot;| Server[&quot;서버\nConnection ID: 0xABCD&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Phone --&gt;|&quot;IP: 10.0.0.2 (LTE)&quot;| Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note[&quot;같은 Connection ID\n연결 유지&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-사용자-영역-구현&quot;&gt;4. 사용자 영역 구현&lt;/h3&gt;
&lt;p&gt;TCP 는 OS 커널에 구현되어 있어 변경 배포가 느리다. QUIC 은 브라우저와 서버 라이브러리가 구현해 OS 업데이트 없이 새 기능을 배포할 수 있다.&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;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;스트림 단위 격리&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;2-3 RTT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1 RTT&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&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 4-tuple&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;tr&gt;&lt;td align=&quot;left&quot;&gt;암호화&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TLS 별도 계층&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;항상 암호화 (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;HTTP/2 [[hpack&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HPACK]]&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;실전-http3-서버-설정-nginx&quot;&gt;실전: HTTP/3 서버 설정 (nginx)&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;nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# nginx 1.25+ 기준 HTTP/3 + QUIC 설정&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;server&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;    listen &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; quic reuseport;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ssl;&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;    ssl_certificate &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    /path/to/cert.pem;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ssl_certificate_key &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;/path/to/key.pem;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ssl_protocols &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      TLSv1.3;&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;    # Alt-Svc 헤더로 브라우저에 QUIC 지원 알림&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Alt-Svc &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;h3=&quot;:443&quot;; ma=86400&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;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;실전-quic-연결-확인&quot;&gt;실전: QUIC 연결 확인&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;# curl 로 HTTP/3 요청 (curl 7.88+ + QUIC 빌드 필요)&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; --http3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://cloudflare.com&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -I&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;# HTTP/3 협상 확인&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; -v&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --http3&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://example.com&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; 2&gt;&amp;#x26;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;ALPN&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# ALPN: h3&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;# Python (aioquic 라이브러리)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# pip install aioquic&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;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; asyncio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aioquic.asyncio &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; connect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; aioquic.quic.configuration &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; QuicConfiguration&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; quic_client&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;    config &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; QuicConfiguration(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;is_client&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&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;    async&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; connect(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;example.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;configuration&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;config) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; conn:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        # QUIC 연결 위에서 HTTP/3 스트림 열기&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        reader, writer &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; conn.create_stream()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        writer.write(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;GET / HTTP/3&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;        data &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; reader.read(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;4096&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;        print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(data)&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;asyncio.run(quic_client())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;quic-패킷-구조&quot;&gt;QUIC 패킷 구조&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;QUIC 패킷 (UDP 페이로드)&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;| Header (암호화)  | Payload (암호화) |&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;| Packet Type      | STREAM Frame     |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| Connection ID    | ACK Frame        |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| Packet Number    | CRYPTO Frame     |&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;TCP 와 달리 QUIC 패킷은 &lt;strong&gt;헤더 일부를 제외하고 전부 암호화&lt;/strong&gt;된다. 중간 장비 (라우터, 방화벽) 는 Connection ID 와 패킷 번호 정도만 볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;약점-및-주의점&quot;&gt;약점 및 주의점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;UDP 차단 방화벽&lt;/strong&gt;: 기업망, 일부 ISP 에서 UDP 포트 443 을 차단하거나 throttling 한다. HTTP/3 클라이언트는 TCP 기반 HTTP/2 fallback 을 반드시 구현해야 한다. 실제로 전체 트래픽의 5-10% 는 fallback 이 발생한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;strong&gt;0-RTT 재전송 공격&lt;/strong&gt;: 0-RTT 데이터는 서버가 재생(replay) 을 방지하기 어렵다. 멱등하지 않은 요청 (POST, 결제 등) 을 0-RTT 로 보내면 replay 공격 위험이 있다. RFC 9000 §8.1 참조.&lt;/p&gt;
&lt;/blockquote&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;UDP 차단&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;기업 방화벽, ISP throttling&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;CPU 비용&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;커널 바이패스 없이 암호화 비용이 TCP+TLS 보다 높을 수 있음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;NAT rebinding&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Connection Migration 과 NAT 동작 충돌 가능&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 에 비해 라이브러리/디버거 성숙도 낮음&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;WireShark 등 도구에서 내부 분석이 어려움&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;quic-구현체&quot;&gt;QUIC 구현체&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;strong&gt;quiche&lt;/strong&gt; (Cloudflare)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Rust&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;고성능, nginx/curl 에서 사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;ngtcp2&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;libev 기반, 범용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;msquic&lt;/strong&gt; (Microsoft)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Windows 내장, .NET 지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;quic-go&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Go&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;순수 Go, HTTP/3 서버 구현에 자주 사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;aioquic&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Python&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;MsQuic + NGINX&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;C/C++&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;webtransport&quot;&gt;WebTransport&lt;/h3&gt;
&lt;p&gt;QUIC 위에서 브라우저가 양방향 스트림을 직접 열 수 있는 API. WebSocket 과 달리 HTTP 프록시를 거치지 않고 다중 독립 스트림을 사용할 수 있다.&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;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// WebTransport (Chrome 97+, HTTP/3 기반)&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; transport&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; WebTransport&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://example.com/transport&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;await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; transport.ready;&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; stream&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; transport.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createBidirectionalStream&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; writer&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; stream.writable.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getWriter&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; writer.&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:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; TextEncoder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;encode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;hello&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:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; reader&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; stream.readable.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getReader&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:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;value&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:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; reader.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;read&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:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; TextDecoder&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;decode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(value));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;도입-현황-2026-기준&quot;&gt;도입 현황 (2026 기준)&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Google&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;검색, YouTube, Gmail 기본 QUIC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Cloudflare&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;모든 CDN 트래픽&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Meta&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;Apple&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;iOS 네트워킹 스택 내장&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;Chrome, Firefox, Safari, Edge 모두 지원&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;tcp-에서-quic-으로-마이그레이션-체크리스트&quot;&gt;TCP 에서 QUIC 으로 마이그레이션 체크리스트&lt;/h2&gt;
&lt;p&gt;실제 서비스에 HTTP/3 (QUIC) 을 도입할 때 확인해야 할 사항:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UDP 허용 확인&lt;/strong&gt;: 서버 방화벽, 로드 밸런서, CDN 이 443/UDP 를 허용하는가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alt-Svc 헤더&lt;/strong&gt;: 브라우저에 HTTP/3 엔드포인트를 알리는 헤더 추가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fallback 보장&lt;/strong&gt;: UDP 차단 환경에서 자동으로 TCP/HTTP2 로 내려가는가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인증서 TLS 1.3&lt;/strong&gt;: QUIC 은 TLS 1.3 필수. 1.2 이하는 불가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;성능 모니터링&lt;/strong&gt;: QUIC 연결 비율, fallback 비율, 지연시간을 분리 측정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로드 밸런서&lt;/strong&gt;: QUIC Connection ID 기반 라우팅을 지원하는 LB 선택 (ngx_quic_lb 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세션 티켓 공유&lt;/strong&gt;: 다중 서버 환경에서 0-RTT 재접속을 위해 세션 티켓을 공유해야 함&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;quic-버전&quot;&gt;QUIC 버전&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;gQUIC&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Google 내부&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;QUIC v1&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;RFC 9000 (2021)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;현재 표준, HTTP/3 기반&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;QUIC v2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;RFC 9369 (2023)&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;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[udp|UDP]] - QUIC 의 기반 프로토콜&lt;/li&gt;
&lt;li&gt;[[tcp|TCP]] - QUIC 이 대체하려는 프로토콜&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking|Head-of-Line Blocking]] - QUIC 이 해결하는 문제&lt;/li&gt;
&lt;li&gt;[[http-3|HTTP/3]] - QUIC 위의 애플리케이션 계층&lt;/li&gt;
&lt;li&gt;[[tls|TLS]] - QUIC 에 통합된 암호화&lt;/li&gt;
&lt;li&gt;[[hpack|HPACK]] - HTTP/2 헤더 압축 (HTTP/3 는 QPACK)&lt;/li&gt;
&lt;li&gt;[[webrtc|WebRTC]] - 또 다른 UDP 기반 실시간 프로토콜&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>http</category><category>protocol</category><category>transport-layer</category><category>http3</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>Wed, 15 Jul 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;: 네트워크 혼잡 시 송신 속도 조절 (Reno, Cubic, BBR 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tcp-헤더-구조&quot;&gt;TCP 헤더 구조&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 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7&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 Port          |       Destination Port        |&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;|                        Sequence Number                        |&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;|                    Acknowledgment Number                      |&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;|DataOff|Rsvd |U|A|P|R|S|F|          Window Size              |&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;|           Checksum            |         Urgent Pointer        |&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;Sequence Number&lt;/strong&gt;: 이 세그먼트 첫 바이트의 스트림 내 위치&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acknowledgment Number&lt;/strong&gt;: 다음으로 기대하는 바이트 번호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Window Size&lt;/strong&gt;: 수신 버퍼의 남은 공간 (흐름 제어)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags&lt;/strong&gt;: SYN (연결 시작), ACK (확인), FIN (종료), RST (강제 종료), PSH (즉시 전달)&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;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;    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 style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: SYN (seq=x)&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 (seq=y, ack=x+1)&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: ACK (ack=y+1)&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: 연결 수립 완료&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SYN&lt;/strong&gt;: “내 시작 번호는 x, 연결하겠다”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SYN-ACK&lt;/strong&gt;: “OK, 내 시작 번호는 y, 너의 x+1 번째를 기다린다”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACK&lt;/strong&gt;: “알겠어, 너의 y+1 번째를 기다린다”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1 RTT 소요. [[TLS]] 까지 추가하면 보통 2-3 RTT.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;SYN Flood 공격&lt;/strong&gt;: 악의적인 클라이언트가 SYN 만 보내고 ACK 를 보내지 않아 서버의 SYN 백로그 큐를 소진시킨다. &lt;code&gt;SYN Cookies&lt;/code&gt; 로 서버 측 상태 저장 없이 방어 가능.&lt;/p&gt;
&lt;/blockquote&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;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;    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 style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: FIN (seq=u)&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: ACK (ack=u+1)&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: FIN (seq=v)&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: ACK (ack=v+1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over C: TIME_WAIT 상태 진입&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 방향(C→S, S→C) 을 독립적으로 종료하므로 4단계가 필요. 마지막 ACK 를 보낸 클라이언트는 &lt;strong&gt;TIME_WAIT&lt;/strong&gt; 상태로 &lt;code&gt;2 x MSL&lt;/code&gt; (Maximum Segment Lifetime, 보통 60s~4min) 동안 대기한다. 지연 도착 패킷이 새 연결을 오염시키는 것을 방지하기 위함.&lt;/p&gt;
&lt;h2 id=&quot;흐름-제어-flow-control&quot;&gt;흐름 제어 (Flow Control)&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;Sender                                        Receiver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │── Data (3000 bytes) ─────────────────────→ │ rwnd=65535&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │ ←── ACK, rwnd=30000 ────────────────────── │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │── Data (10000 bytes) ───────────────────→ │ rwnd=30000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │ ←── ACK, rwnd=20000 ────────────────────── │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │── Data (20000 bytes) ───────────────────→ │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │ ←── ACK, rwnd=0 (Zero Window) ─────────── │ 수신 버퍼 꽉 참&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │   [전송 중단, Zero Window Probe 대기]       │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │ ←── ACK, rwnd=32768 ────────────────────── │ 버퍼 비워짐&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  │── Data 재개 ────────────────────────────→ │&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Receive Window (rwnd)&lt;/strong&gt;: 수신 버퍼의 남은 공간&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero Window&lt;/strong&gt;: rwnd=0 이면 송신 중단. 주기적으로 Zero Window Probe 전송&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Window Scaling&lt;/strong&gt; (RFC 7323): 16bit 윈도우를 최대 30bit (1GB) 로 확장. 대역폭 지연 곱이 큰 네트워크에서 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;혼잡-제어-congestion-control&quot;&gt;혼잡 제어 (Congestion Control)&lt;/h2&gt;
&lt;p&gt;네트워크 혼잡을 감지하고 송신 속도를 조절하는 TCP 의 핵심 메커니즘.&lt;/p&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;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;    SS[&quot;Slow Start\ncwnd 지수 증가&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CA[&quot;Congestion Avoidance\ncwnd 선형 증가&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    FR[&quot;Fast Recovery\ncwnd 절반 후 선형&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SS --&gt;|&quot;cwnd &gt;= ssthresh&quot;| CA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CA --&gt;|&quot;3 dup ACK&quot;| FR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    FR --&gt;|&quot;새 ACK&quot;| CA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    CA --&gt;|&quot;timeout&quot;| SS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SS --&gt;|&quot;timeout&quot;| SS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Slow Start&lt;/strong&gt;: cwnd (Congestion Window) 를 1 MSS 에서 시작, ACK 마다 1 MSS 증가 (지수 증가)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Congestion Avoidance&lt;/strong&gt;: ssthresh 도달 후, RTT 마다 1 MSS 씩 선형 증가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Recovery&lt;/strong&gt;: 3개 중복 ACK 수신 시 ssthresh = cwnd/2, cwnd = ssthresh + 3. 타임아웃과 달리 Slow Start 로 돌아가지 않음&lt;/li&gt;
&lt;/ul&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;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;Reno&lt;/td&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;고 BDP 에서 대역폭 낭비&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;Cubic&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;손실 + 시간 함수&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Reno 대비 처리량 개선&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;손실 기반 한계&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Linux 기본&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;BBR&lt;/td&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;구현 복잡, 공정성 이슈&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Google, Cloudflare&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;BBR&lt;/strong&gt; (Bottleneck Bandwidth and RTT): 패킷 손실 대신 RTT 와 대역폭을 직접 측정해 최적 전송 속도를 산출한다. Bufferbloat 방지 효과도 있다.&lt;/p&gt;
&lt;h2 id=&quot;재전송-retransmission&quot;&gt;재전송 (Retransmission)&lt;/h2&gt;
&lt;p&gt;ACK 가 오지 않으면 재전송. 두 가지 트리거:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;RTO (Retransmission Timeout)&lt;/strong&gt;: 타이머 만료 시 재전송. 초기 RTO = 1s, 지수 백오프 적용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Retransmit&lt;/strong&gt;: 3개의 중복 ACK (Duplicate ACK) 수신 시 즉시 재전송&lt;/li&gt;
&lt;/ol&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;    participant S as Sender&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant R as Receiver&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 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;R: Seq 300&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: ACK 200 (Seq 100 확인, 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;R: Seq 400&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: ACK 200 (Dup ACK)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 500&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: ACK 200 (Dup ACK)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: ACK 200 (Dup ACK 3번째)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    note over S: Fast Retransmit 발동&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    S-&gt;&gt;R: Seq 200 재전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R-&gt;&gt;S: ACK 700 (모두 확인)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;SACK (Selective ACK)&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;Without SACK: 200 손실 → 200, 300, 400, 500, 600 모두 재전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;With SACK:    200 손실 → 200 만 재전송 (300-600 수신 확인)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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 (1 RTT)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP keep-alive, [[QUIC]]&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;한 패킷 손실이 같은 연결의 모든 스트림 영향&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;[[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;h3 id=&quot;tcp-연결-상태-확인&quot;&gt;TCP 연결 상태 확인&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;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;ss&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -tn&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; state&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; established&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;# TIME_WAIT 상태 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ss&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -tn&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; state&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; time-wait&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; wc&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -l&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;ss&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -tn&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;NR&gt;1 {print $5}&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; cut&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -f2&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; uniq&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; sort&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -rn&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;time_wait-과다-문제&quot;&gt;TIME_WAIT 과다 문제&lt;/h3&gt;
&lt;p&gt;대량의 단기 HTTP/1.0 요청 또는 특정 로드 밸런서 구성에서 TIME_WAIT 소켓이 포트를 점유해 &lt;code&gt;EADDRINUSE&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TIME_WAIT 소켓 재사용 허용 (클라이언트 측)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sysctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; net.ipv4.tcp_tw_reuse=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&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;# FIN_WAIT2 대기 시간 축소&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sysctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; net.ipv4.tcp_fin_timeout=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;15&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;nagle-알고리즘&quot;&gt;Nagle 알고리즘&lt;/h3&gt;
&lt;p&gt;작은 패킷을 모아 하나로 전송해 네트워크 효율을 높인다. SSH, 텔넷, 게임처럼 낮은 지연이 중요한 경우 &lt;code&gt;TCP_NODELAY&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;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; socket&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sock &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; socket.socket()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sock.setsockopt(socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;IPPROTO_TCP&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;TCP_NODELAY&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;/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;HTTP keep-alive 와 TIME_WAIT 혼동&lt;/strong&gt;: HTTP keep-alive 는 응용 계층(TCP 연결 유지), TIME_WAIT 는 TCP 계층(종료 후 대기). 별개 개념.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RTT = 네트워크 거리만?&lt;/strong&gt; RTT 에는 패킷 왕복 시간 외에 큐잉 지연, 처리 지연도 포함된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nagle 알고리즘 방치&lt;/strong&gt;: 낮은 지연이 중요한 서비스 (채팅, 게임) 에서 Nagle 이 활성화된 채로 소량 메시지를 반복 전송하면 지연 누적.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCP_NODELAY 와 TCP_CORK 혼동&lt;/strong&gt;: NODELAY 는 소량 패킷 즉시 전송, CORK 는 패킷 모아서 전송 (반대 효과).&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, 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;데이터베이스 프로토콜 대부분 (PostgreSQL, MySQL wire protocol)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[QUIC]] - UDP 기반 대안, HOL 해결&lt;/li&gt;
&lt;li&gt;[[TLS]] - TCP 위 암호화 프로토콜&lt;/li&gt;
&lt;li&gt;[[UDP]] - 비연결형 경량 대안&lt;/li&gt;
&lt;li&gt;[[Head-of-Line Blocking]] - TCP 의 HOL 문제&lt;/li&gt;
&lt;li&gt;[[OSI 7 Layer]] - L4 Transport 계층&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>protocol</category><category>transport-layer</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>Wed, 15 Jul 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;cipher-suite&quot;&gt;Cipher Suite&lt;/h2&gt;
&lt;p&gt;TLS 협상에서 선택하는 알고리즘 조합.&lt;/p&gt;
&lt;h3 id=&quot;tls-12-cipher-suite-구조&quot;&gt;TLS 1.2 cipher suite 구조&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;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256&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;    │      │        │            └── MAC/HMAC: SHA256&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    │      │        └─────────────── 암호화: AES-128-GCM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    │      └──────────────────────── 서명/인증: RSA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └─────────────────────────────── 키 교환: ECDHE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;tls-13-cipher-suite-단순화&quot;&gt;TLS 1.3 cipher suite (단순화)&lt;/h3&gt;
&lt;p&gt;키 교환은 별도로 협상하고 cipher suite 에서 분리됨:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Cipher Suite&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;AEAD 알고리즘&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;HKDF&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS_AES_256_GCM_SHA384&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;AES-256-GCM&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;SHA-384&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS_AES_128_GCM_SHA256&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;AES-128-GCM&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;SHA-256&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;ChaCha20-Poly1305&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;SHA-256&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;ChaCha20-Poly1305&lt;/strong&gt;: AES 하드웨어 가속이 없는 모바일 기기에서 더 빠르다. Android 저가형 기기에서 성능 이점이 크다.&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;mtls-mutual-tls&quot;&gt;mTLS (Mutual TLS)&lt;/h2&gt;
&lt;p&gt;일반 TLS 는 서버만 인증서를 제시한다. mTLS 는 &lt;strong&gt;클라이언트도 인증서를 제시&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;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;    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 style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: ClientHello&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: ServerHello, Certificate, CertificateRequest&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: Certificate, CertificateVerify, Finished&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: Finished&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: 양방향 인증 완료&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;/strong&gt; (Istio, Linkerd): Pod 간 통신에 자동 mTLS 적용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;제로 트러스트&lt;/strong&gt; 아키텍처: 내부 서비스 간 상호 인증&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B2B API&lt;/strong&gt;: 특정 파트너사만 접근 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sni-server-name-indication&quot;&gt;SNI (Server Name Indication)&lt;/h2&gt;
&lt;p&gt;TLS ClientHello 에 접속하려는 도메인 이름을 포함하는 확장.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;필요한 이유&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;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ClientHello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  extensions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    server_name: &quot;www.example.com&quot;   &amp;#x3C;- 평문 전송&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
SNI 는 평문으로 전송돼 네트워크 관찰자가 사용자가 방문하는 도메인을 볼 수 있다. &lt;strong&gt;ECH (Encrypted Client Hello)&lt;/strong&gt; 가 이를 해결하는 중 (RFC 진행 중, Cloudflare 등 일부 지원).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;alpn-application-layer-protocol-negotiation&quot;&gt;ALPN (Application-Layer Protocol Negotiation)&lt;/h2&gt;
&lt;p&gt;ClientHello 에서 지원하는 응용 프로토콜을 협상하는 TLS 확장. 80/443 포트를 유지하면서 HTTP/1.1, HTTP/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;ClientHello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  extensions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    alpn: [&quot;h2&quot;, &quot;http/1.1&quot;]    &amp;#x3C;- 클라이언트 선호 순서&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;ServerHello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  extensions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    alpn: &quot;h2&quot;                   &amp;#x3C;- 서버가 선택&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;# ALPN 협상 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com:443&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -alpn&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; h2&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; 2&gt;&amp;#x26;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;ALPN&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;session-resumption&quot;&gt;Session Resumption&lt;/h2&gt;
&lt;p&gt;TLS 핸드셰이크 비용을 줄이는 방법. 완전 핸드셰이크를 생략하고 이전 세션을 재사용.&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;TLS 버전&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;Session ID&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1.2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서버 저장&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서버가 ID 반환, 클라이언트가 다음 연결 때 제시&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;Session Ticket&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1.2 / 1.3&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;PSK / 0-RTT&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;1.3&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;Session Ticket 은 서버 측 상태 저장이 불필요해 수평 확장에 유리하다. 단, 티켓 암호화 키가 로테이션되어야 Forward Secrecy 가 보장된다.&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;h3 id=&quot;openssl-로-tls-확인&quot;&gt;openssl 로 TLS 확인&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;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TLS 핸드셰이크 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com:443&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -servername&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;# TLS 1.3 강제&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com:443&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -tls1_3&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;# ALPN 협상 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com:443&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -alpn&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; h2&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;openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; s_client&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -connect&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com:443&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; 2&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; openssl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; x509&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -noout&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -text&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;nginx-tls-13-설정&quot;&gt;nginx TLS 1.3 설정&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;nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ssl_protocols &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;TLSv1.2 TLSv1.3;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ssl_ciphers &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;HIGH:!aNULL:!MD5;&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;# Session resumption&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ssl_session_tickets &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&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;ssl_session_timeout &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1d&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;ssl_session_cache &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;shared:SSL:10m;&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;# OCSP Stapling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ssl_stapling &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&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;ssl_stapling_verify &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&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;# HSTS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;add_header &lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Strict-Transport-Security &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;max-age=31536000; includeSubDomains&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;lets-encrypt-자동-갱신&quot;&gt;Let’s Encrypt 자동 갱신&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;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;certbot&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --nginx&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example.com&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; www.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;certbot&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; renew&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --dry-run&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;# systemd timer 로 자동 갱신&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;systemctl&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; enable&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --now&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; certbot.timer&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;인증서 관리 비용&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 로 점진적 해결 중.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신뢰 모델 자체의 한계&lt;/strong&gt;: 약 150개 Root CA 중 하나라도 침해되면 임의의 도메인을 위장할 수 있음. Certificate Transparency (CT) 로 발급 로그 공개 의무화.&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;TLS = 종단간 암호화?&lt;/strong&gt; TLS 는 전송 경로 암호화. 서버에서 복호화되므로 서버 측 데이터는 평문. E2E 암호화는 별도 구현 필요.&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;TLS 1.3 0-RTT 오남용&lt;/strong&gt;: GET 이 아닌 POST/PUT/DELETE 에 0-RTT 를 허용하면 replay 공격으로 중복 처리 위험.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS = 암호화 완료?&lt;/strong&gt; 사내 프록시나 DLP 솔루션이 자체 CA 로 트래픽을 가로챌 수 있음 (SSL inspection).&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[QUIC]] - TLS 1.3 내장, 1-RTT 연결&lt;/li&gt;
&lt;li&gt;[[TCP]] - TLS 가 동작하는 전송 계층&lt;/li&gt;
&lt;li&gt;[[HTTP/2]] - HTTPS 로 동작&lt;/li&gt;
&lt;li&gt;[[HTTP/3]] - QUIC 기반 (TLS 1.3 통합)&lt;/li&gt;
&lt;li&gt;[[network-dns|DNS]] - 인증서 발급 시 도메인 소유 증명&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>UDP</title><link>https://shinkeonkim.com/wiki/network/udp/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/udp/</guid><pubDate>Wed, 15 Jul 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|TCP]] 와 대비된다.&lt;/p&gt;
&lt;p&gt;“보내고 잊는다(fire-and-forget)”, 송신측은 패킷을 던지고 끝, 받았는지 안 받았는지 확인하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;언제-쓰이나&quot;&gt;언제 쓰이나&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|QUIC]]&lt;/strong&gt; / [[http-3|HTTP/3]]: UDP 위에서 신뢰성·순서 보장을 사용자 영역에서 직접 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;헤더-구조&quot;&gt;헤더 구조&lt;/h2&gt;
&lt;p&gt;UDP 패킷 헤더는 총 &lt;strong&gt;8 bytes&lt;/strong&gt; (TCP 의 20+ bytes 와 대비).&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;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;p&gt;TCP 헤더 (20 bytes 이상, 옵션 포함 시 최대 60 bytes) 와 비교하면, UDP 헤더는 응용에 필요한 최소 정보만 담는다. 흐름 제어, 혼잡 제어, 재전송 등 모든 신뢰성 메커니즘을 제거한 결과다.&lt;/p&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;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;    participant A as 송신측&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant N as 네트워크&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant B as 수신측&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;N: 데이터그램 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N-&gt;&gt;B: 데이터그램 1 도착&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;N: 데이터그램 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N--xB: 데이터그램 2 손실&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A-&gt;&gt;N: 데이터그램 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    N-&gt;&gt;B: 데이터그램 3 도착&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over A: 손실 여부 모름, 계속 전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over B: 1, 3 수신. 2 누락 인지 못함&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UDP 는 핸드셰이크 없이 즉시 송신한다. ACK 없음, 손실되어도 송신측은 알 수 없다.&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;TCP&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;UDP&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;연결 없음&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;없음&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;보장 안 함&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;없음&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;없음&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;20-60 bytes&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;8 bytes&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;낮음&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;가능&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;mtu-와-단편화&quot;&gt;MTU 와 단편화&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;MTU (Maximum Transmission Unit)&lt;/strong&gt;: 이더넷 표준 1500 bytes. IP 헤더 20 bytes + UDP 헤더 8 bytes = 실데이터 최대 &lt;strong&gt;1472 bytes&lt;/strong&gt; (이더넷 기준).&lt;/p&gt;
&lt;p&gt;이보다 큰 데이터그램은 IP 계층에서 &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;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;    D[&quot;UDP 데이터그램\n3000 bytes&quot;] --&gt;|&quot;IP 단편화&quot;| F1[&quot;IP 프래그먼트 1\n1480 bytes&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D --&gt; F2[&quot;IP 프래그먼트 2\n1480 bytes&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D --&gt; F3[&quot;IP 프래그먼트 3\n40 bytes&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F1 --&gt;|&quot;수신측 재조립&quot;| R[&quot;재조립된 데이터그램&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F2 --&gt; R&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    F3 --&gt; R&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
단편화는 성능 저하 + 재조립 실패 위험이 있다. 중간 라우터에서 한 조각만 잃어도 전체 데이터그램을 드롭한다. 실전에서는 Path MTU Discovery 로 경로 MTU 를 알아낸 뒤 그 이하로 전송한다.&lt;/p&gt;
&lt;/blockquote&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;최적 페이로드 크기 = Path MTU - IP 헤더 - UDP 헤더&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;이더넷 기준: 1500 - 20 - 8 = 1472 bytes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;브로드캐스트--멀티캐스트&quot;&gt;브로드캐스트 / 멀티캐스트&lt;/h2&gt;
&lt;p&gt;UDP 는 1:N 전송을 지원한다. TCP 는 1:1 연결만 가능.&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;설명&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;UDP&lt;/th&gt;&lt;th align=&quot;center&quot;&gt;TCP&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;1:1&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;O&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;O&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:네트워크 전체&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;O&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;X&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:그룹 구독자&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;O&lt;/td&gt;&lt;td align=&quot;center&quot;&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DHCP&lt;/strong&gt;: 브로드캐스트로 서버 탐색&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mDNS / DNS-SD&lt;/strong&gt;: 로컬 서비스 발견 (Bonjour)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPTV&lt;/strong&gt;: 멀티캐스트로 동일 스트림을 여러 수신자에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;udp-위에-신뢰성을-다시-만든다는-역설&quot;&gt;”UDP 위에 신뢰성을 다시 만든다”는 역설&lt;/h2&gt;
&lt;p&gt;[[quic|QUIC]], [[webrtc|WebRTC]] DTLS/SRTP, 게임 엔진의 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|Head-of-Line Blocking]], 연결 변경 시 끊김&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;선택적 신뢰성&lt;/strong&gt;: 메시지마다 “ordered / unordered, reliable / unreliable” 선택 가능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;빠른 진화&lt;/strong&gt;: 사용자 영역 구현이라 OS 업데이트 없이 개선&lt;/li&gt;
&lt;/ul&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;    UDP[&quot;UDP (비연결, 비신뢰)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; QUIC[&quot;QUIC\n신뢰성 + 순서 + 암호화 + 멀티스트림&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; WebRTC[&quot;WebRTC DTLS/SRTP\n암호화 미디어 스트림&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; Game[&quot;게임 Reliable UDP\n선택적 신뢰성&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; DNS[&quot;DNS\n단순 질의/응답&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    UDP --&gt; NTP[&quot;NTP / SNMP\n단순 프로토콜&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UDP 의 “단순함”은 양날의 검, 직접 구현해야 하지만 그만큼 유연하다.&lt;/p&gt;
&lt;h2 id=&quot;실전-예시&quot;&gt;실전 예시&lt;/h2&gt;
&lt;h3 id=&quot;dns-질의-python&quot;&gt;DNS 질의 (Python)&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; socket&lt;/span&gt;&lt;/span&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; struct&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; build_dns_query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(hostname: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;bytes&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;간이 DNS A 레코드 요청 패킷&quot;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    transaction_id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\xab\xcd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    flags &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x01\x00&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&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;    questions &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x00\x01&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    answers &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rrcount &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; additional &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x00\x00&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&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:#6A737D&quot;&gt;    # QNAME 인코딩&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    qname &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&quot;&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; part &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; hostname.split(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.&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;        qname &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bytes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;len&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(part)]) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; part.encode()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    qname &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x00&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&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;    qtype &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x00\x01&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   # A 레코드&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    qclass &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; b&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\x00\x01&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # IN (인터넷)&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; transaction_id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; flags &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; questions &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; answers &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rrcount &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; additional &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; qname &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; qtype &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; qclass&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&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; dns_lookup&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(hostname: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;str&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) -&gt; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;bytes&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;    sock &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; socket.socket(socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;AF_INET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SOCK_DGRAM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# SOCK_DGRAM = UDP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    sock.settimeout(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2.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:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    query &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; build_dns_query(hostname)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    sock.sendto(query, (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;8.8.8.8&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;53&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;))  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Google Public DNS&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:#6A737D&quot;&gt;    # 응답이 없으면 timeout 예외 -&gt; 재시도 (애플리케이션 레벨 신뢰성)&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; sock.recvfrom(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;512&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;    sock.close()&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; response&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;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; socket&lt;/span&gt;&lt;/span&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; struct&lt;/span&gt;&lt;/span&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; time&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;sock &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; socket.socket(socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;AF_INET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SOCK_DGRAM&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;def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; send_position&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(x: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, y: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;float&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, seq: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, server: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;tuple&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;    # 패킷: seq(4B) + x(4B) + y(4B) = 12 bytes&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; struct.pack(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;!Iff&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, seq, x, y)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    sock.sendto(payload, server)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    # ACK 없음: 손실된 위치는 무시, 다음 패킷이 곧 도착&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:#79B8FF&quot;&gt;SERVER&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;game.example.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;7777&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;seq &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&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;    send_position(player.x, player.y, seq, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SERVER&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;    seq &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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    time.sleep(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0.05&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 20 Hz 업데이트&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;udp-서버-에코-서버&quot;&gt;UDP 서버 (에코 서버)&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; socket&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; udp_echo_server&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(port: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 9999&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;    sock &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; socket.socket(socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;AF_INET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, socket.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;SOCK_DGRAM&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;    sock.bind((&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, port))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;UDP Echo Server on port &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;port&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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:#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;        data, addr &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; sock.recvfrom(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1024&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;        print&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Received &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{len&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(data)&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bytes from &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;addr&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&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;        sock.sendto(data, addr)  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 에코 (연결 없이 즉시 응답)&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;strong&gt;NAT 는 UDP 를 잘 모른다.&lt;/strong&gt; TCP 는 SYN/FIN 으로 상태를 추적하지만 UDP 는 상태가 없어 NAT 테이블 타임아웃이 짧다 (보통 30초). 장시간 UDP 세션이 필요하면 keepalive 패킷으로 NAT 테이블을 유지해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;strong&gt;UDP 증폭 공격 (DRDoS)&lt;/strong&gt;: 요청 패킷은 작고 응답이 매우 클 수 있는 DNS, NTP, SSDP 등에서 소스 IP 를 위조한 UDP 요청으로 증폭 공격이 발생한다. 공개 UDP 서비스는 응답 크기 제한 + 소스 IP 검증 필수.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;체크섬은 IPv4 에서 선택 사항이었다.&lt;/strong&gt; 하지만 현대 OS 는 기본적으로 체크섬을 계산한다. IPv6 에서는 UDP 체크섬이 필수다. 체크섬은 내용 변조 감지는 하지만 재전송을 보장하지는 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;udp-포트-번호-주요-사례&quot;&gt;UDP 포트 번호 주요 사례&lt;/h2&gt;
&lt;p&gt;잘 알려진 UDP 포트 (Well-Known Ports):&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;center&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;center&quot;&gt;53&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;DNS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;도메인 이름 조회&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;67/68&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;DHCP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;IP 주소 자동 할당&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;123&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;NTP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;시간 동기화&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;161/162&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;SNMP&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;네트워크 장비 관리&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;443&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;QUIC&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;HTTP/3 (TLS over UDP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;5353&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;mDNS&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;로컬 서비스 발견&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;center&quot;&gt;5004&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;RTP&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;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[tcp|TCP]] - 신뢰성 있는 대안 프로토콜&lt;/li&gt;
&lt;li&gt;[[quic|QUIC]] - UDP 위에 구현된 차세대 전송 계층&lt;/li&gt;
&lt;li&gt;[[http-3|HTTP/3]] - QUIC 기반 HTTP&lt;/li&gt;
&lt;li&gt;[[head-of-line-blocking|Head-of-Line Blocking]] - TCP 의 단점&lt;/li&gt;
&lt;li&gt;[[dtls|DTLS]] - UDP 기반 TLS&lt;/li&gt;
&lt;li&gt;[[webrtc|WebRTC]] - UDP 기반 실시간 미디어&lt;/li&gt;
&lt;li&gt;[[osi-7-layer|OSI 7계층]] - 전송 계층 (Layer 4) 컨텍스트&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>protocol</category><category>transport-layer</category><author>koa (김신건)</author></item><item><title>Stateful vs Stateless Firewall</title><link>https://shinkeonkim.com/wiki/network/stateful-vs-stateless-firewall/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/network/stateful-vs-stateless-firewall/</guid><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;방화벽 (Firewall)&lt;/strong&gt; 은 네트워크 트래픽을 정책에 따라 허용/차단 하는 보안 장치. 상태 관리 방식으로 두 유형이 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stateless (상태 비저장)&lt;/strong&gt;: 각 패킷을 &lt;strong&gt;독립적&lt;/strong&gt; 으로 검사. 이전 패킷과 관계 없음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateful (상태 저장)&lt;/strong&gt;: &lt;strong&gt;연결 (connection) 상태를 추적&lt;/strong&gt; 하여 관련 패킷을 인식.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 차이가 AWS VPC 의 &lt;strong&gt;Security Group (stateful)&lt;/strong&gt; 과 &lt;strong&gt;Network ACL (stateless)&lt;/strong&gt; 의 근본입니다.&lt;/p&gt;
&lt;h2 id=&quot;왜-상태-추적이-문제인가&quot;&gt;왜 상태 추적이 문제인가&lt;/h2&gt;
&lt;h3 id=&quot;tcp-통신은-양방향&quot;&gt;TCP 통신은 양방향&lt;/h3&gt;
&lt;p&gt;TCP 연결은 &lt;strong&gt;3-way handshake&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;Client (192.168.1.10:54321)                Server (10.0.0.5:443)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ─────── SYN ───────&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       &amp;#x3C;──── SYN+ACK ─────&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ─────── ACK ───────&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ─────── HTTP GET ─&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       &amp;#x3C;──── HTTP 200 ────&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ─────── FIN ───────&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       &amp;#x3C;────── FIN ────────&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;Outbound (client → server)&lt;/strong&gt;: &lt;code&gt;dst 10.0.0.5:443 tcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inbound (server → client)&lt;/strong&gt;: &lt;code&gt;src 10.0.0.5:443 tcp&lt;/code&gt; (응답)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;stateless-firewall&quot;&gt;Stateless Firewall&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;각 패킷을 규칙과 매칭&lt;/strong&gt;. 연결 개념 없음.&lt;/p&gt;
&lt;h3 id=&quot;특징&quot;&gt;특징&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;양방향 규칙 명시 필수&lt;/strong&gt;: outbound 뿐 아니라 return traffic 도 별도 허용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;간단, 빠름&lt;/strong&gt;: 상태 테이블 없어 리소스 적음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;에페메랄 포트&lt;/strong&gt; 문제: 클라이언트 소스 포트가 랜덤 (32768-65535) 이라 방화벽이 예측 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;규칙-예시-stateless&quot;&gt;규칙 예시 (stateless)&lt;/h3&gt;
&lt;p&gt;Client 가 &lt;code&gt;dst port 443&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;Outbound:  ALLOW  proto=TCP  src=any:any  dst=any:443&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Inbound:   ALLOW  proto=TCP  src=any:443  dst=any:1024-65535   ← 응답 허용 별도&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Inbound 규칙 없으면&lt;/strong&gt; 웹 응답이 방화벽에 막힘. Stateless 는 대칭 규칙 필수.&lt;/p&gt;
&lt;h3 id=&quot;순서--우선순위&quot;&gt;순서 &amp;#x26; 우선순위&lt;/h3&gt;
&lt;p&gt;Stateless 는 대개 &lt;strong&gt;번호 기반 순서 매칭&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;100: ALLOW  tcp  src=any  dst=443&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;110: ALLOW  tcp  src=443  dst=1024-65535&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;120: DENY   tcp  src=any  dst=any                ← 남은 모두 차단&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;stateful-firewall&quot;&gt;Stateful Firewall&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;연결 테이블 (state table / conntrack)&lt;/strong&gt; 유지. 첫 패킷의 outbound 규칙이 통과하면 &lt;strong&gt;관련 return traffic 은 자동 허용&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;특징-1&quot;&gt;특징&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;한 방향만 규칙 정의&lt;/strong&gt;: return 은 자동&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 추적&lt;/strong&gt;: TCP handshake, 진행 상태 인지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UDP 유사 처리&lt;/strong&gt;: “요청/응답” 쌍을 시간 window 로 추적&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ICMP 도 매칭&lt;/strong&gt;: request/reply&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;리소스 사용&lt;/strong&gt;: 연결당 상태 테이블 엔트리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;규칙-예시-stateful&quot;&gt;규칙 예시 (stateful)&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;Outbound:  ALLOW  proto=TCP  dst=any:443&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Inbound 규칙 불필요&lt;/strong&gt;. 방화벽이 outbound 시 연결 등록 -&gt; return 자동 인식.&lt;/p&gt;
&lt;h3 id=&quot;connection-tracking-상태&quot;&gt;Connection Tracking 상태&lt;/h3&gt;
&lt;p&gt;Linux netfilter conntrack 이 관리하는 상태:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NEW&lt;/strong&gt;: 첫 패킷 (SYN)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ESTABLISHED&lt;/strong&gt;: 3-way handshake 완료, 데이터 전송 중&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RELATED&lt;/strong&gt;: 기존 연결의 파생 (FTP data 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;INVALID&lt;/strong&gt;: 알 수 없는 상태&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UNTRACKED&lt;/strong&gt;: 추적 안 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;방화벽 규칙에서 &lt;code&gt;state ESTABLISHED,RELATED&lt;/code&gt; 는 자동 허용의 근간.&lt;/p&gt;
&lt;h2 id=&quot;stateful-vs-stateless-비교&quot;&gt;Stateful vs Stateless 비교&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;Stateless&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Stateful&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;X&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;O&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;명시 필요&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;별도 처리&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;낮음&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;빠름&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;DDoS 저항&lt;/strong&gt;&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;규칙 많고 복잡&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;규칙 매칭 만&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;aws-vpc-의-두-계층&quot;&gt;AWS VPC 의 두 계층&lt;/h2&gt;
&lt;p&gt;VPC 는 &lt;strong&gt;두 방화벽 계층&lt;/strong&gt; 을 정의:&lt;/p&gt;
&lt;h3 id=&quot;security-group-sg---stateful&quot;&gt;Security Group (SG) - Stateful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;인스턴스 (ENI) 단위&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;화이트리스트 (allow only)&lt;/strong&gt;: 명시 허용, 나머지 deny&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateful&lt;/strong&gt;: 응답 자동 허용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인바운드 + 아웃바운드 규칙&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;기본: 모든 outbound 허용, 모든 inbound 차단&lt;/li&gt;
&lt;li&gt;여러 SG 를 한 ENI 에 붙일 수 있음 (union)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;network-acl-nacl---stateless&quot;&gt;Network ACL (NACL) - Stateless&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;서브넷 단위&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;allow / deny 둘 다&lt;/strong&gt; (블랙리스트 가능)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateless&lt;/strong&gt;: 응답도 명시 규칙 필요&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;번호 순서 매칭&lt;/strong&gt; (낮은 번호 우선)&lt;/li&gt;
&lt;li&gt;기본 NACL: all in/out 허용. Custom NACL: 기본 all deny.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sg-vs-nacl-상세-비교&quot;&gt;SG vs NACL 상세 비교&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;Security Group&lt;/th&gt;&lt;th align=&quot;left&quot;&gt;Network ACL&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;ENI (인스턴스)&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;Stateful&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Stateless&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;Allow only&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Allow + Deny&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;모든 규칙 (union)&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;Deny (in), Allow (out)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;(Default NACL) Allow all&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;인스턴스 시작 시 or 연결&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;ENI 당 5 SG (기본)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;서브넷 당 1 NACL&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;SG 당 60 (기본)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;NACL 당 20 (기본)&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;다른 SG 참조 가능&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;IP CIDR 만&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;VPC Flow Logs (간접)&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;statefulstateless-실전-시나리오&quot;&gt;Stateful/Stateless 실전 시나리오&lt;/h2&gt;
&lt;h3 id=&quot;시나리오-1-웹-서버가-인터넷에서-요청-받기&quot;&gt;시나리오 1: 웹 서버가 인터넷에서 요청 받기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Security Group (stateful)&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;Inbound:   ALLOW  TCP 80,443  from 0.0.0.0/0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Outbound:  ALLOW  ALL         to   0.0.0.0/0   (기본)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;응답 자동 허용. 끝.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network ACL (stateless)&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;Inbound:   100 ALLOW  TCP 80,443       from 0.0.0.0/0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;           110 ALLOW  TCP 1024-65535   from 0.0.0.0/0   ← 서버 -&gt; 클라 응답 (예:패키지 다운로드) 인바운드&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Outbound:  100 ALLOW  TCP 80,443       to   0.0.0.0/0   (server-side 응답)&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;Actually with servers, the pattern is:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Inbound:   100 ALLOW  TCP 80,443       from 0.0.0.0/0    (client → server 요청)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;           110 ALLOW  TCP 1024-65535   from 0.0.0.0/0    (server → client 응답의 대칭 트래픽 아님)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Outbound:  100 ALLOW  TCP 1024-65535   to   0.0.0.0/0    (server → client 응답, 에페메랄 dst)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;           110 ALLOW  TCP 80,443       to   0.0.0.0/0    (서버가 outbound 요청 시)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stateless 는 응답까지 명시 필요 = &lt;strong&gt;에페메랄 포트 범위 허용&lt;/strong&gt; 필수.&lt;/p&gt;
&lt;h3 id=&quot;시나리오-2-서버가-외부-api-호출&quot;&gt;시나리오 2: 서버가 외부 API 호출&lt;/h3&gt;
&lt;p&gt;Server (10.0.1.5) → api.example.com:443&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security Group&lt;/strong&gt; (stateful): outbound &lt;code&gt;TCP 443&lt;/code&gt; 만.
&lt;strong&gt;NACL&lt;/strong&gt;: outbound &lt;code&gt;TCP 443&lt;/code&gt; + inbound &lt;code&gt;TCP 1024-65535&lt;/code&gt; (에페메랄).&lt;/p&gt;
&lt;h3 id=&quot;시나리오-3-ddos-방어&quot;&gt;시나리오 3: DDoS 방어&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stateless NACL&lt;/strong&gt;: 알려진 악의 IP 블랙리스트 (Deny 규칙). SG 는 deny 없음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateful SG&lt;/strong&gt;: 정상 트래픽 허용, 나머지 차단.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;두 계층 병행.&lt;/p&gt;
&lt;h2 id=&quot;defense-in-depth-심층-방어&quot;&gt;Defense in Depth (심층 방어)&lt;/h2&gt;
&lt;p&gt;VPC 는 두 방화벽 계층 + 추가:&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;Internet&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;[AWS WAF / Shield]              ← Layer 7 / DDoS&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;[Route 53 / CloudFront]         ← DNS / Edge&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;[Application Load Balancer]     ← LB SG&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;[NACL (Subnet)]                 ← Layer 3/4 stateless&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;[Security Group (ENI)]          ← Layer 3/4 stateful&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 firewall (iptables)]      ← OS 수준&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;[Application-level auth]        ← 앱 인증&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 계층은 독립. 한 계층 실패해도 다른 계층 방어.&lt;/p&gt;
&lt;h2 id=&quot;다른-방화벽-예&quot;&gt;다른 방화벽 예&lt;/h2&gt;
&lt;h3 id=&quot;stateless&quot;&gt;Stateless&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS NACL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iptables INPUT chain without conntrack&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Global Accelerator flow rules&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;초기 라우터 ACL&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;stateful&quot;&gt;Stateful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS Security Group&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Network Firewall&lt;/strong&gt; (stateful engine)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iptables + conntrack&lt;/strong&gt; (Linux 기본)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pf (BSD)&lt;/strong&gt;, &lt;strong&gt;nftables&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Palo Alto, Fortinet, Check Point&lt;/strong&gt; (모든 next-gen firewall)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows Firewall&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;연결-테이블-크기&quot;&gt;연결 테이블 크기&lt;/h2&gt;
&lt;p&gt;Stateful 은 연결당 메모리:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux conntrack: ~300 bytes/entry&lt;/li&gt;
&lt;li&gt;100만 연결 = ~300 MB&lt;/li&gt;
&lt;li&gt;DDoS: 대량 half-open 연결로 테이블 소진 유발&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;방어:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;nf_conntrack_max&lt;/code&gt; 증가&lt;/li&gt;
&lt;li&gt;SYN cookies (Linux 기본)&lt;/li&gt;
&lt;li&gt;짧은 timeout&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;aws-network-firewall-2020&quot;&gt;AWS Network Firewall (2020+)&lt;/h2&gt;
&lt;p&gt;VPC 안의 &lt;strong&gt;관리형 방화벽 서비스&lt;/strong&gt;. Stateful + Stateless 둘 다 지원.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stateless rule engine&lt;/strong&gt;: 5-tuple 매칭 (Suricata syntax 지원)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateful rule engine&lt;/strong&gt;: 연결 추적, Suricata 규칙 (IDS/IPS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS SNI 검사&lt;/strong&gt;: 도메인 기반 필터&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;위협 인텔리전스&lt;/strong&gt;: AWS managed feeds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gateway Load Balancer 통합&lt;/strong&gt;: 3rd-party appliance 뒤에&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Advanced 시나리오: NACL/SG 로 부족한 딥 검사.&lt;/p&gt;
&lt;h2 id=&quot;함정&quot;&gt;함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;NACL 는 stateless&lt;/strong&gt;. 응답 트래픽 별도 허용 필수. Ephemeral port range (1024-65535 또는 32768-61000) 열어야.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;strong&gt;SG 는 모든 규칙 union&lt;/strong&gt;. 여러 SG 붙이면 허용이 OR. 잘못 붙이면 예상보다 넓게 허용.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;SG deny 규칙 없음&lt;/strong&gt;. Whitelist 만. 특정 IP 차단은 NACL 로.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;NACL 규칙 번호는 sparse 하게&lt;/strong&gt; (100, 200, 300). 나중 규칙 삽입 여지.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;strong&gt;NACL 순서가 중요&lt;/strong&gt;. 낮은 번호가 먼저 매칭. Deny 를 아래 두면 위 allow 가 우선.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;strong&gt;Return traffic 잘 몰라서 NACL “AllowAll”&lt;/strong&gt; 로 두는 실수. Stateless 라서 그렇게 두면 방어 무의미.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;SG 는 인스턴스 재부팅 없이 즉시 변경&lt;/strong&gt;. NACL 도 마찬가지. 신속한 대응 가능.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[aws-vpc|VPC]] - 네트워크 기본&lt;/li&gt;
&lt;li&gt;[[aws-sg-vs-nacl|SG vs NACL]] - AWS 관점 상세 비교&lt;/li&gt;
&lt;li&gt;[[aws-security-hub|Security Hub]] - 보안 자세 모니터링&lt;/li&gt;
&lt;li&gt;[[aws-network-firewall|AWS Network Firewall]] - 관리형 stateful/stateless firewall&lt;/li&gt;
&lt;li&gt;[[aws-shield|Shield]] - DDoS 방어&lt;/li&gt;
&lt;li&gt;[[aws-privatelink|PrivateLink]] - VPC endpoint 대안&lt;/li&gt;
&lt;li&gt;[[tcp|TCP]] - 연결 기반 프로토콜 배경&lt;/li&gt;
&lt;li&gt;[[udp|UDP]] - Stateless 프로토콜&lt;/li&gt;
&lt;li&gt;[[network-nat|NAT]] - 관련 상태 추적&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>network</category><category>security</category><category>firewall</category><category>packet-filtering</category><category>aws</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;interceptor-미들웨어&quot;&gt;Interceptor (미들웨어)&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: 서버 interceptor, 인증 검사&lt;/span&gt;&lt;/span&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; AuthInterceptor&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;grpc&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ServerInterceptor&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:#B392F0&quot;&gt; intercept_service&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, continuation, handler_call_details):&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:#79B8FF&quot;&gt; dict&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(handler_call_details.invocation_metadata)&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; metadata.get(&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 style=&quot;color:#F97583&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Bearer valid-token&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;            def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; deny&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(request, context):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                context.abort(grpc.StatusCode.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;UNAUTHENTICATED&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Invalid token&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;            return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; grpc.unary_unary_rpc_method_handler(deny)&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; continuation(handler_call_details)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Interceptor = gRPC 의 미들웨어&lt;/em&gt;. 인증, 로깅, 메트릭, retry 등 횡단 관심사 처리. 체인 가능.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;metadata-header-역할&quot;&gt;Metadata (Header 역할)&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;# 클라이언트: 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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;request-id&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;abc-123&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 style=&quot;color:#9ECBFF&quot;&gt;&apos;x-tenant-id&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;org-1&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 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;metadata&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;metadata)&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:#F97583&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; GetItem&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(self, request, context):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    md &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; dict&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(context.invocation_metadata())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    tenant &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; md.get(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;x-tenant-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:#79B8FF&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Metadata = HTTP 헤더 역할&lt;/em&gt;. 요청마다 전달되며 Interceptor 에서도 읽기 가능.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;health-check-protocol&quot;&gt;Health Check 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;protobuf&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// grpc.health.v1.Health 표준&lt;/span&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; Health&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;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Check&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;HealthCheckRequest&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;HealthCheckResponse&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;  rpc&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Watch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;HealthCheckRequest&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; HealthCheckResponse&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;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;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# K8s 1.24+ 네이티브 gRPC probe&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;livenessProbe&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:#85E89D&quot;&gt;  grpc&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:#85E89D&quot;&gt;    port&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;50051&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    service&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;shop.v1.Shop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  initialDelaySeconds&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&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:#B392F0&quot;&gt;grpc_health_probe&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -addr=:50051&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -service=shop.v1.Shop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;load-balancing&quot;&gt;Load Balancing&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; Envoy[&quot;Envoy / L7 LB&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Envoy --&gt;|round-robin| B1[Backend 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Envoy --&gt;|round-robin| B2[Backend 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Envoy --&gt;|round-robin| B3[Backend 3]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTTP/2 multiplexing 때문에 &lt;em&gt;TCP 레벨 (L4) LB 는 단일 서버로 몰림&lt;/em&gt;. L7 LB 또는 클라이언트 사이드 LB 필요.&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;클라이언트 사이드&lt;/td&gt;&lt;td&gt;DNS 조회 후 round-robin&lt;/td&gt;&lt;td&gt;gRPC core 기본 지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Envoy proxy&lt;/td&gt;&lt;td&gt;sidecar 패턴, Istio&lt;/td&gt;&lt;td&gt;서비스 메시 환경&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;K8s HeadlessService&lt;/td&gt;&lt;td&gt;Pod IP 직접 노출&lt;/td&gt;&lt;td&gt;클라이언트 LB 와 조합&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;connectrpc-grpc-대안&quot;&gt;ConnectRPC (gRPC 대안)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;ConnectRPC&lt;/em&gt; (buf.build) = HTTP/1.1, HTTP/2 모두 지원. curl 로 테스트 가능. gRPC 호환. 브라우저 직접 호출 (grpc-web proxy 불필요). 2026 시점 생태계 성장 중.&lt;/p&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;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[GraphQL]]&lt;/li&gt;
&lt;li&gt;[[Microservices]]&lt;/li&gt;
&lt;li&gt;[[load-balancer]]&lt;/li&gt;
&lt;li&gt;[[jsonrpc-vs-grpc]]&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;http-method-의미론&quot;&gt;HTTP Method 의미론&lt;/h2&gt;





















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Method&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;code&gt;GET&lt;/code&gt;&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;리소스 조회&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;HEAD&lt;/code&gt;&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;헤더만 조회 (본문 없음)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;OPTIONS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;허용 메서드 확인 (CORS preflight)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;리소스 전체 교체&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;리소스 삭제&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;리소스 생성 / 액션&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;리소스 부분 수정&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;멱등 (idempotent)&lt;/em&gt;: 같은 요청을 여러 번 보내도 결과 동일&lt;/li&gt;
&lt;li&gt;&lt;em&gt;안전 (safe)&lt;/em&gt;: 서버 상태를 변경하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;code&gt;PUT&lt;/code&gt; 은 &lt;em&gt;전체 교체&lt;/em&gt;, &lt;code&gt;PATCH&lt;/code&gt; 는 &lt;em&gt;부분 수정&lt;/em&gt;. REST API 에서 혼용하면 클라이언트가 &lt;em&gt;누락 필드를 null 로 덮어쓰는&lt;/em&gt; 버그 발생.&lt;/p&gt;
&lt;/blockquote&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;range-requests-206-partial-content&quot;&gt;Range Requests (206 Partial Content)&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;대용량 파일 다운로드 재개, 동영상 seek&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;# 요청: 0~999 바이트&lt;/span&gt;&lt;/span&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; /video.mp4 &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;Range&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bytes=0-999&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;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; 206&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Partial Content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Content-Range&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bytes 0-999/50000000&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; 1000&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;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bytes&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;서버가 Range 를 지원하면 &lt;code&gt;Accept-Ranges: bytes&lt;/code&gt; 헤더를 응답에 포함. 지원 안 하면 &lt;code&gt;200 OK&lt;/code&gt; 로 전체 응답.&lt;/p&gt;
&lt;h2 id=&quot;content-negotiation&quot;&gt;Content Negotiation&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:#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, text/html;q=0.9, */*;q=0.8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Accept-Language&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ko-KR, ko;q=0.9, en;q=0.8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; gzip, br, deflate&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;Accept&lt;/code&gt;&lt;/td&gt;&lt;td&gt;응답 MIME 타입 선호도&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Accept-Language&lt;/code&gt;&lt;/td&gt;&lt;td&gt;언어 선호도&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Accept-Encoding&lt;/code&gt;&lt;/td&gt;&lt;td&gt;압축 방식 (gzip, br)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Content-Type&lt;/code&gt;&lt;/td&gt;&lt;td&gt;실제 응답 타입&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Content-Encoding&lt;/code&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;[!TIP]
&lt;code&gt;br&lt;/code&gt; (Brotli) 는 gzip 대비 &lt;em&gt;20-30% 더 압축&lt;/em&gt;. 모던 브라우저 + HTTPS 환경에서 &lt;em&gt;br 우선 설정&lt;/em&gt; 권장. nginx: &lt;code&gt;brotli on; brotli_comp_level 6;&lt;/code&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;http-인증-기초&quot;&gt;HTTP 인증 기초&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:#6A737D&quot;&gt;# 서버가 인증 요구&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; 401&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Unauthorized&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;WWW-Authenticate&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Bearer realm=&quot;api&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:#6A737D&quot;&gt;# 클라이언트 재요청&lt;/span&gt;&lt;/span&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/me &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;Authorization&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Bearer eyJhbGci...&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;Basic&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Base64 인코딩 (HTTPS 필수)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Bearer&lt;/code&gt;&lt;/td&gt;&lt;td&gt;JWT / OAuth 토큰&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Digest&lt;/code&gt;&lt;/td&gt;&lt;td&gt;해시 기반 (거의 미사용)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;API-Key&lt;/code&gt;&lt;/td&gt;&lt;td&gt;커스텀 헤더 (&lt;code&gt;X-API-Key&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
&lt;code&gt;Authorization&lt;/code&gt; 헤더는 &lt;em&gt;브라우저 캐시에 저장되지 않음&lt;/em&gt;. 반면 쿠키는 &lt;code&gt;HttpOnly + Secure + SameSite=Strict&lt;/code&gt; 설정 시 XSS / CSRF 방어.&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;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;connection-preface&quot;&gt;Connection Preface&lt;/h2&gt;
&lt;p&gt;HTTP/2 연결의 첫 24 바이트 = &lt;em&gt;magic octets&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;PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이후 클라이언트 &lt;code&gt;SETTINGS&lt;/code&gt; frame 전송. &lt;code&gt;h2c&lt;/code&gt; (cleartext) 직접 연결 식별에 사용. &lt;code&gt;h2&lt;/code&gt; (TLS+ALPN) 에서는 ALPN 협상 후 magic 전송.&lt;/p&gt;
&lt;h2 id=&quot;alpn-협상&quot;&gt;ALPN 협상&lt;/h2&gt;
&lt;p&gt;HTTP/2 를 사용하려면 TLS 핸드셰이크 중 &lt;strong&gt;ALPN&lt;/strong&gt; (Application-Layer Protocol Negotiation) 으로 버전 협상.&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;    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 style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;S: TLS ClientHello + ALPN: h2, http/1.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: TLS ServerHello + ALPN: h2&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: 이후 HTTP/2 바이너리 프레임 교환&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;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;Cleartext HTTP/2 (내부망, gRPC mesh)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;http/1.1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;ALPN fallback (h2 미지원 시)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;서버 설정 예:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nginx: &lt;code&gt;listen 443 ssl; http2 on;&lt;/code&gt; (nginx 1.26+ 권장)&lt;/li&gt;
&lt;li&gt;Caddy: 자동 ALPN 협상 (별도 설정 불필요)&lt;/li&gt;
&lt;li&gt;Go &lt;code&gt;net/http&lt;/code&gt;: &lt;code&gt;h2.ConfigureServer&lt;/code&gt; 또는 기본 TLS 서버&lt;/li&gt;
&lt;li&gt;Java Netty: &lt;code&gt;ApplicationProtocolNegotiationHandler&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;stream-상태-머신&quot;&gt;Stream 상태 머신&lt;/h2&gt;
&lt;p&gt;각 HTTP/2 stream 은 &lt;em&gt;상태 머신&lt;/em&gt;. &lt;code&gt;RST_STREAM&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;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;stateDiagram-v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [*] --&gt; idle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    idle --&gt; open : HEADERS 전송 또는 수신&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    open --&gt; half_closed_local : END_STREAM 전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    open --&gt; half_closed_remote : END_STREAM 수신&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    half_closed_local --&gt; closed : RST_STREAM 또는 END_STREAM 수신&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    half_closed_remote --&gt; closed : RST_STREAM 또는 END_STREAM 전송&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    open --&gt; closed : RST_STREAM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    closed --&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;의미&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;idle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;미사용. 클라이언트 = 홀수 ID, 서버 push = 짝수 ID&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;open&lt;/code&gt;&lt;/td&gt;&lt;td&gt;양방향 송수신 가능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;half_closed_local&lt;/code&gt;&lt;/td&gt;&lt;td&gt;로컬 전송 완료, 수신만&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;half_closed_remote&lt;/code&gt;&lt;/td&gt;&lt;td&gt;원격 전송 완료, 송신만&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;closed&lt;/code&gt;&lt;/td&gt;&lt;td&gt;완전 종료. ID 재사용 불가&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
스트림 ID 는 &lt;em&gt;단조 증가&lt;/em&gt;. 연결 수명 동안 최대 2^31 개. 한도 도달 시 &lt;code&gt;GOAWAY&lt;/code&gt; 로 연결 재시작.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;grpc-와-http2&quot;&gt;gRPC 와 HTTP/2&lt;/h2&gt;
&lt;p&gt;gRPC 는 HTTP/2 를 &lt;em&gt;전송 계층&lt;/em&gt;으로 사용. Protobuf 를 &lt;code&gt;DATA&lt;/code&gt; frame 에 실어 보낸다.&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;    Caller[&quot;gRPC Caller&amp;#x3C;br/&gt;Protobuf 직렬화&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Caller --&gt; H2[&quot;HTTP/2 DATA frames&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    H2 --&gt; Mux[&quot;Stream Multiplexing&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;    Mux --&gt; Svr[&quot;gRPC Server&amp;#x3C;br/&gt;Protobuf 역직렬화&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;HTTP/2 개념&lt;/th&gt;&lt;th&gt;gRPC 매핑&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;stream 1개&lt;/td&gt;&lt;td&gt;RPC 호출 1개&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;HEADERS&lt;/code&gt; frame&lt;/td&gt;&lt;td&gt;gRPC metadata, trailer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DATA&lt;/code&gt; frame&lt;/td&gt;&lt;td&gt;Protobuf payload&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: MAX_CONCURRENT_STREAMS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;최대 동시 RPC 수 제한&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;gRPC 4가지 통신 모드:&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;Unary&lt;/td&gt;&lt;td&gt;1:1&lt;/td&gt;&lt;td&gt;단순 API 호출&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server streaming&lt;/td&gt;&lt;td&gt;1:N&lt;/td&gt;&lt;td&gt;실시간 피드, 로그 스트림&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Client streaming&lt;/td&gt;&lt;td&gt;N:1&lt;/td&gt;&lt;td&gt;파일 업로드, 배치 수집&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bidirectional streaming&lt;/td&gt;&lt;td&gt;N:N&lt;/td&gt;&lt;td&gt;채팅, 협업, 게임&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
L4 LB 앞 gRPC: &lt;em&gt;1 TCP 연결 = 1 백엔드 고착&lt;/em&gt; → 편향 분산. &lt;strong&gt;L7 gRPC-aware LB&lt;/strong&gt; (Envoy, Istio, k8s Ingress) 로 stream 레벨 분산 필요. 자세히는 [[grpc]].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;http2-vs-http3-빠른-비교&quot;&gt;HTTP/2 vs HTTP/3 빠른 비교&lt;/h2&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;HTTP/2&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;전송 계층&lt;/td&gt;&lt;td&gt;TCP&lt;/td&gt;&lt;td&gt;QUIC (UDP 기반)&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;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;헤더 압축&lt;/td&gt;&lt;td&gt;HPACK (dynamic table)&lt;/td&gt;&lt;td&gt;QPACK&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0-RTT 재개&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;td&gt;있음 (session ticket)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;배포 성숙도&lt;/td&gt;&lt;td&gt;광범위&lt;/td&gt;&lt;td&gt;CDN, 모던 브라우저 지원 확대 중&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;자세히는 [[http-3]] 참고.&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;h2 협상 여부&lt;/td&gt;&lt;td&gt;&lt;code&gt;curl -I --http2 https://example.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ALPN 선택값&lt;/td&gt;&lt;td&gt;&lt;code&gt;openssl s_client -alpn h2 -connect example.com:443&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chrome h2 세션&lt;/td&gt;&lt;td&gt;&lt;code&gt;chrome://net-internals/#http2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;금지 헤더 제거&lt;/td&gt;&lt;td&gt;&lt;code&gt;Connection&lt;/code&gt;, &lt;code&gt;Upgrade&lt;/code&gt;, &lt;code&gt;Keep-Alive&lt;/code&gt; 없애기&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server push 제거&lt;/td&gt;&lt;td&gt;&lt;code&gt;http2_push&lt;/code&gt; 지시자 제거, &lt;code&gt;103 Early Hints&lt;/code&gt; 로 대체&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gRPC LB&lt;/td&gt;&lt;td&gt;Envoy 또는 client-side LB 적용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;우선순위 힌트&lt;/td&gt;&lt;td&gt;RFC 9218 Extensible Priorities (Urgency, Incremental 헤더)&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]] (이전 버전)&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] 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;connection-draining-드레이닝&quot;&gt;Connection Draining (드레이닝)&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;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant LB as Load Balancer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Old as Old Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant New as New Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB-&gt;&gt;Old: 새 연결 중단 신호&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB-&gt;&gt;New: 새 연결 전달&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Old--&gt;&gt;LB: 진행 중 요청 처리 완료 대기&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Old--&gt;&gt;LB: 완전 종료&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over LB,Old: grace period 30-60s 내 완료&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;AWS ALB: &lt;em&gt;deregistration delay&lt;/em&gt; (default 300s, 조정 가능)&lt;/li&gt;
&lt;li&gt;Kubernetes: &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; + readiness probe 제거&lt;/li&gt;
&lt;li&gt;드레이닝 없이 &lt;em&gt;즉시 종료&lt;/em&gt; = 진행 중 요청 손실.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;blue-green-배포--canary-배포&quot;&gt;Blue-Green 배포 / Canary 배포&lt;/h2&gt;
&lt;p&gt;LB 는 &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;    LB[Load Balancer]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Blue[&quot;Blue v1.0&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;    Green[&quot;Green v1.1&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;    LB --&gt;|&quot;100% → 0%&quot;| Blue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB --&gt;|&quot;0% → 100%&quot;| Green&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;Blue-Green&lt;/td&gt;&lt;td&gt;동시 운영, 트래픽 전환&lt;/td&gt;&lt;td&gt;즉시&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Canary&lt;/td&gt;&lt;td&gt;소수 % 부터 점진 증가&lt;/td&gt;&lt;td&gt;비율 감소&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rolling&lt;/td&gt;&lt;td&gt;순차 교체 (구/신 혼재)&lt;/td&gt;&lt;td&gt;느림&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Shadow&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;[!TIP]
&lt;em&gt;Canary&lt;/em&gt; 기준 지표: 에러율, P99 지연, 비즈니스 메트릭. 이상 감지 시 자동 롤백 (Argo Rollouts, Flagger).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;dsr-direct-server-return&quot;&gt;DSR (Direct Server Return)&lt;/h2&gt;
&lt;p&gt;트래픽 &lt;em&gt;반환 경로&lt;/em&gt;를 LB 를 거치지 않고 &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;    Client --&gt;|&quot;요청 via LB&quot;| LB[&quot;L4 LB&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB --&gt;|&quot;포워딩 (DIP to RIP)&quot;| Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt;|&quot;응답 직접 반환&quot;| Client&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;응답 트래픽&lt;/em&gt; (보통 요청 대비 10x 이상) 이 LB 를 우회 → &lt;em&gt;LB 처리량 병목 해소&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;주로 &lt;em&gt;L4 LB&lt;/em&gt; (IPVS, HAProxy) 에서 구현.&lt;/li&gt;
&lt;li&gt;서버가 &lt;em&gt;VIP (Virtual IP)&lt;/em&gt; 를 loopback 에 설정해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;global-lb-vs-regional-lb&quot;&gt;Global LB vs Regional LB&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;구분&lt;/th&gt;&lt;th&gt;Global LB&lt;/th&gt;&lt;th&gt;Regional LB&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;역할&lt;/td&gt;&lt;td&gt;DNS 기반 지역 선택&lt;/td&gt;&lt;td&gt;지역 내 서버 분산&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;지연 최적화&lt;/td&gt;&lt;td&gt;GeoDNS + anycast&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 Route 53, Cloudflare&lt;/td&gt;&lt;td&gt;AWS ALB, nginx&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;페일오버 단위&lt;/td&gt;&lt;td&gt;지역 전체 이전&lt;/td&gt;&lt;td&gt;서버 단위&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;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;    User[글로벌 사용자]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    User --&gt; DNS[&quot;GeoDNS / Anycast&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DNS --&gt; RegionA[&quot;Region A (서울) ALB&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DNS --&gt; RegionB[&quot;Region B (도쿄) ALB&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    RegionA --&gt; SA1[Server A1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    RegionA --&gt; SA2[Server A2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    RegionB --&gt; SB1[Server B1]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;em&gt;DNS TTL&lt;/em&gt; 이 길면 지역 이전 시 &lt;em&gt;일부 사용자가 오래된 IP&lt;/em&gt; 를 캐시. TTL 60-120s 권장. &lt;em&gt;Active-Active&lt;/em&gt; 구성에서 데이터 동기화 전략 필수.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;클라이언트-사이드-lb&quot;&gt;클라이언트 사이드 LB&lt;/h2&gt;
&lt;p&gt;인프라 LB 대신 &lt;em&gt;클라이언트가 서버 목록을 직접 관리&lt;/em&gt;하여 선택.&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;구분&lt;/th&gt;&lt;th&gt;서버 사이드 LB&lt;/th&gt;&lt;th&gt;클라이언트 사이드 LB&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;예&lt;/td&gt;&lt;td&gt;AWS ALB, nginx&lt;/td&gt;&lt;td&gt;gRPC, Ribbon (Netflix)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;장점&lt;/td&gt;&lt;td&gt;투명, 단순&lt;/td&gt;&lt;td&gt;레이턴시 1 hop 감소&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;단점&lt;/td&gt;&lt;td&gt;중앙 병목 가능&lt;/td&gt;&lt;td&gt;클라이언트 복잡도&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;서버 목록&lt;/td&gt;&lt;td&gt;LB 가 관리&lt;/td&gt;&lt;td&gt;서비스 레지스트리 (Consul, etcd)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
마이크로서비스 환경에서 &lt;em&gt;gRPC 는 기본 클라이언트 사이드 LB&lt;/em&gt; (round-robin, pick-first). Kubernetes headless service 와 함께 사용.&lt;/p&gt;
&lt;/blockquote&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;권장 설정&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;idle timeout 조정 (&gt;60s), &lt;code&gt;proxy_read_timeout&lt;/code&gt; 증가&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gRPC&lt;/td&gt;&lt;td&gt;HTTP/2 지원 LB, keepalive 설정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Auto-scaling&lt;/td&gt;&lt;td&gt;Connection Draining 활성화, Health check 간격 단축&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;무중단 배포&lt;/td&gt;&lt;td&gt;Blue-Green 또는 Canary + draining&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;세션 유지 필요&lt;/td&gt;&lt;td&gt;Sticky session (단, Redis 세션 스토어 권장)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;초저지연&lt;/td&gt;&lt;td&gt;DSR 고려 (L4), IPVS&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;[[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] 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;webrtc-시그널링-stun--turn&quot;&gt;WebRTC 시그널링: STUN / TURN&lt;/h2&gt;
&lt;p&gt;P2P 연결 수립을 위한 &lt;em&gt;시그널링 서버 + NAT traversal&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;    participant A as Client 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&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant ST as STUN/TURN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant B as Client B&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: SDP 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: SDP 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;Sig: SDP 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: SDP 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;ST: STUN Binding Request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ST--&gt;&gt;A: Public IP:Port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B-&gt;&gt;ST: STUN Binding Request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ST--&gt;&gt;B: Public IP:Port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A--&gt;&gt;B: ICE Candidates 교환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over A,B: P2P 연결 수립&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;STUN&lt;/td&gt;&lt;td&gt;공인 IP:Port 탐색 (cheap, UDP)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TURN&lt;/td&gt;&lt;td&gt;NAT 뒤 relay (expensive, 모든 트래픽 경유)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ICE&lt;/td&gt;&lt;td&gt;STUN + TURN 조합으로 최적 경로 탐색&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SDP&lt;/td&gt;&lt;td&gt;미디어 능력 / 코덱 협상&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
TURN 서버 없으면 &lt;em&gt;Symmetric NAT (기업 방화벽)&lt;/em&gt; 뒤 클라이언트 연결 불가. 실서비스는 TURN 필수. TURN 비용 = 전체 트래픽 relay.&lt;/p&gt;
&lt;/blockquote&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 TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB[L7 Load Balancer]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB --&gt;|&quot;SSE / WebSocket&quot;| Sticky[&quot;Sticky Session 필요&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    LB --&gt;|&quot;Polling&quot;| Stateless[&quot;완전 Stateless 분산&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sticky --&gt; Redis[&quot;Redis Pub/Sub&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;    Redis --&gt; N1[Node 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt; N2[Node 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt; N3[Node 3]&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;Polling&lt;/td&gt;&lt;td&gt;Stateless, 그냥 확장&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;Connection 수 관리&lt;/td&gt;&lt;td&gt;Nginx keep-alive 튜닝&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SSE&lt;/td&gt;&lt;td&gt;Sticky session + 팬아웃&lt;/td&gt;&lt;td&gt;Redis Pub/Sub&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebSocket&lt;/td&gt;&lt;td&gt;Sticky session + 팬아웃&lt;/td&gt;&lt;td&gt;Redis Pub/Sub, Socket.IO&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebRTC&lt;/td&gt;&lt;td&gt;시그널링 서버 분리&lt;/td&gt;&lt;td&gt;STUN/TURN 별도&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;자세히는 [[redis-pubsub-vs-streams]], [[sticky-session]].&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;Node.js 서버&lt;/th&gt;&lt;th&gt;브라우저 클라이언트&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;SSE&lt;/td&gt;&lt;td&gt;&lt;code&gt;res.write(&apos;data: ...\n\n&apos;)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;new EventSource(&apos;/stream&apos;)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebSocket&lt;/td&gt;&lt;td&gt;&lt;code&gt;ws&lt;/code&gt;, &lt;code&gt;socket.io&lt;/code&gt;, &lt;code&gt;uWebSockets.js&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;new WebSocket(&apos;wss://...&apos;)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Long Polling&lt;/td&gt;&lt;td&gt;일반 HTTP, timeout 30s&lt;/td&gt;&lt;td&gt;&lt;code&gt;fetch&lt;/code&gt; + 재시도 루프&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebRTC&lt;/td&gt;&lt;td&gt;&lt;code&gt;node-webrtc&lt;/code&gt;, Mediasoup&lt;/td&gt;&lt;td&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt; API&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;SSE 연결 한계&lt;/strong&gt;: HTTP/1.1 = 브라우저당 도메인 6 연결 제한. &lt;em&gt;HTTP/2 에서는 다중 SSE 가능&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;브라우저-지원--제약&quot;&gt;브라우저 지원 &amp;#x26; 제약&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;기능&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Firefox&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;th&gt;IE/Edge legacy&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&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;td&gt;SSE 없음&lt;/td&gt;&lt;/tr&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;td&gt;IE11 지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebRTC&lt;/td&gt;&lt;td&gt;지원&lt;/td&gt;&lt;td&gt;지원&lt;/td&gt;&lt;td&gt;지원 (iOS 제한)&lt;/td&gt;&lt;td&gt;미지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebTransport&lt;/td&gt;&lt;td&gt;지원 (실험)&lt;/td&gt;&lt;td&gt;지원 (실험)&lt;/td&gt;&lt;td&gt;미지원&lt;/td&gt;&lt;td&gt;미지원&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
iOS Safari 는 &lt;em&gt;SSE 응답 버퍼링 문제&lt;/em&gt; 가 있음 (iOS 14 이전). &lt;code&gt;text/event-stream&lt;/code&gt; + 작은 청크 크기로 완화. iOS WebRTC 는 &lt;em&gt;Audio 백그라운드 제한&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;SSE + nginx 버퍼링&lt;/strong&gt; = 기본 response buffering 으로 &lt;em&gt;메시지 지연&lt;/em&gt;. &lt;code&gt;X-Accel-Buffering: no&lt;/code&gt; + &lt;code&gt;proxy_buffering off&lt;/code&gt; 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebSocket + ALB 60s idle timeout&lt;/strong&gt; = &lt;em&gt;heartbeat ping/pong&lt;/em&gt; 으로 연결 유지.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebRTC + CORS&lt;/strong&gt; = 시그널링 서버 CORS 설정 + TURN 서버 credential 보호.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long Polling 서버 연결 수&lt;/strong&gt; = 클라이언트 N명 = 서버 N개 연결 점유. &lt;em&gt;non-blocking I/O&lt;/em&gt; (Node.js, async Python) 필수.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSE 재연결 폭풍&lt;/strong&gt; = 서버 재시작 시 &lt;em&gt;모든 클라이언트 동시 재연결&lt;/em&gt;. &lt;code&gt;Last-Event-ID&lt;/code&gt; + &lt;em&gt;jitter&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;[[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;백엔드-구현-python--fastapi&quot;&gt;백엔드 구현 (Python / FastAPI)&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;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; fastapi &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; FastAPI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; fastapi.responses &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; StreamingResponse&lt;/span&gt;&lt;/span&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; asyncio, json, time&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;app &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; FastAPI()&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; event_generator&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(last_event_id: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;int&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 style=&quot;color:#E1E4E8&quot;&gt;    event_id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; last_event_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&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;        payload &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; json.dumps({&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;t&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: time.time(), &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: event_id})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        yield&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; f&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;id: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;event_id&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;data: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;payload&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;}\n\n&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        event_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&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;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&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;@app.get&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; stream&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(last_event_id: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;int&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 style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; StreamingResponse(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        event_generator(last_event_id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;        media_type&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;text/event-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:#FFAB70&quot;&gt;        headers&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:#9ECBFF&quot;&gt;            &quot;Cache-Control&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;no-cache&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;X-Accel-Buffering&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;no&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# nginx proxy buffering 비활성화&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;blockquote&gt;
&lt;p&gt;[!TIP]
FastAPI / Starlette 는 &lt;code&gt;StreamingResponse&lt;/code&gt; 로 SSE 를 쉽게 구현. &lt;code&gt;X-Accel-Buffering: no&lt;/code&gt; 헤더로 &lt;em&gt;nginx 버퍼링 비활성화&lt;/em&gt; 필수.&lt;/p&gt;
&lt;/blockquote&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;fan-out--scale-out-아키텍처&quot;&gt;Fan-out / Scale-out 아키텍처&lt;/h2&gt;
&lt;p&gt;SSE 서버가 여러 인스턴스로 수평 확장될 때, &lt;em&gt;이벤트를 모든 인스턴스에 전파&lt;/em&gt;하는 fan-out 레이어가 필요:&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;    API[&quot;API Server&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;    Redis[&quot;Redis Pub/Sub&amp;#x3C;br/&gt;또는 Streams&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W1[&quot;SSE Worker 1&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W2[&quot;SSE Worker 2&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W3[&quot;SSE Worker 3&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C1[User A]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C2[User B]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C3[User C]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    API --&gt; Redis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt; W1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt; W2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Redis --&gt; W3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W1 --&gt; C1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W2 --&gt; C2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    W3 --&gt; C3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;API Server&lt;/em&gt; 가 이벤트를 Redis Pub/Sub 채널에 발행&lt;/li&gt;
&lt;li&gt;각 &lt;em&gt;SSE Worker&lt;/em&gt; 가 채널을 구독, 연결된 클라이언트에 전달&lt;/li&gt;
&lt;li&gt;클라이언트는 &lt;em&gt;어느 Worker 에 연결되든&lt;/em&gt; 동일 이벤트 수신&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;Sticky session 없이&lt;/em&gt; 수평 확장 가능. 단 &lt;em&gt;Redis 가 SPOF&lt;/em&gt; 가 되므로 Redis Cluster 또는 Sentinel 구성 권장.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;인증-패턴&quot;&gt;인증 패턴&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;EventSource&lt;/code&gt; 는 &lt;em&gt;커스텀 헤더 (Authorization) 를 지원하지 않음&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;/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;/td&gt;&lt;td&gt;HttpOnly + SameSite=Strict&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;URL 쿼리 파라미터&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;?token=xxx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;HTTPS 필수, 로그 노출 주의&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;초기 핸드셰이크 토큰&lt;/em&gt;&lt;/td&gt;&lt;td&gt;REST 로 단기 토큰 발급 후 SSE URL 에 포함&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;js&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:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;streamToken&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:#F97583&quot;&gt; await&lt;/span&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;/api/stream-token&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;  headers: { Authorization: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`Bearer ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;jwt&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;}).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&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;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;`/api/stream?token=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;streamToken&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;/code&gt;&lt;/pre&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;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>[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;|&quot;behind NAT&quot;| 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;|&quot;behind NAT&quot;| 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;h3 id=&quot;ice-candidate-유형&quot;&gt;ICE Candidate 유형&lt;/h3&gt;
&lt;p&gt;ICE 는 여러 후보를 수집해 우선순위 순으로 연결 시도합니다.&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;설명&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;host&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;로컬 NIC IP/port&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;192.168.1.5:50001&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;srflx&lt;/strong&gt; (Server Reflexive)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;STUN 으로 알아낸 공용 IP/port&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;203.0.113.7:50001&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;prflx&lt;/strong&gt; (Peer Reflexive)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;상대방이 받은 내 패킷에서 알아낸 주소&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;NAT 연결 중 발견&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;relay&lt;/strong&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;TURN 서버 주소&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;198.51.100.2:3478&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;우선순위: host &gt; srflx &gt; prflx &gt; relay. Symmetric NAT 이면 host/srflx 모두 실패하고 relay 로 fallback.&lt;/p&gt;
&lt;h3 id=&quot;ice-restart&quot;&gt;ICE Restart&lt;/h3&gt;
&lt;p&gt;네트워크 변경 (wifi → LTE 전환 등) 시 기존 ICE 연결이 끊깁니다. &lt;code&gt;pc.restartIce()&lt;/code&gt; 로 새 offer 를 만들어 ICE 를 재수집하면 연결을 유지할 수 있습니다.&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;javascript&quot;&gt;&lt;code&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; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handleNetworkChange&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;  pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;restartIce&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; offer&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; pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createOffer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ iceRestart: &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; pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setLocalDescription&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(offer);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // signaling 으로 offer 전송&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;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;dtls-srtp-미디어-암호화&quot;&gt;DTLS-SRTP (미디어 암호화)&lt;/h2&gt;
&lt;p&gt;WebRTC 는 모든 미디어를 &lt;strong&gt;DTLS-SRTP&lt;/strong&gt; 로 암호화합니다. DTLS 로 키 교환 후 SRTP 로 미디어 패킷을 암호화.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DTLS&lt;/strong&gt;: UDP 위의 TLS. 핸드셰이크에서 &lt;code&gt;a=fingerprint:sha-256 ...&lt;/code&gt; (SDP 에서 교환된 인증서 지문) 검증&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRTP&lt;/strong&gt;: 대칭키로 RTP 패킷의 payload 암호화 + HMAC-SHA1 무결성&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;브라우저에서 평문 WebRTC 는 표준상 불가. MITM 차단.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;simulcast--svc-다중-해상도-스트림&quot;&gt;Simulcast / SVC (다중 해상도 스트림)&lt;/h2&gt;
&lt;p&gt;SFU 에서 화면 크기가 다른 구독자에게 서로 다른 품질을 주기 위한 기술.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simulcast&lt;/strong&gt;: 송신자가 같은 영상을 여러 해상도 (720p, 360p, 180p) 로 동시 인코딩해 전송. SFU 가 선택.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SVC (Scalable Video Coding)&lt;/strong&gt;: 레이어 구조로 인코딩, 하위 레이어 위에 상위 레이어를 쌓는 방식. 대역폭 효율 좋음 (예: AV1-SVC, H264-SVC).&lt;/li&gt;
&lt;/ul&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;    Sender[&quot;발신자&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sender --&gt;|&quot;720p stream&quot;| SFU&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sender --&gt;|&quot;360p stream&quot;| SFU&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sender --&gt;|&quot;180p stream&quot;| SFU&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SFU --&gt;|&quot;720p&quot;| ClientHD[&quot;고대역폭 수신자&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SFU --&gt;|&quot;360p&quot;| ClientMD[&quot;중대역폭 수신자&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SFU --&gt;|&quot;180p&quot;| ClientLD[&quot;저대역폭 수신자&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;수신자 대역폭/CPU 에 따라 SFU 가 실시간으로 레이어를 전환해줍니다.&lt;/p&gt;
&lt;h2 id=&quot;bandwidth-estimation-remb--twcc&quot;&gt;Bandwidth Estimation (REMB / TWCC)&lt;/h2&gt;
&lt;p&gt;WebRTC 는 수신자의 네트워크 상태를 추정해 송신자가 비트레이트를 자동 조절합니다.&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;방향&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;REMB&lt;/strong&gt; (Receiver Estimated Max Bitrate)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;수신 → 송신&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;수신자가 추정 최대 속도를 RTCP 로 전송&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;TWCC&lt;/strong&gt; (Transport-Wide Congestion Control)&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;TWCC 가 더 정확하고 최신 표준. GCC (Google Congestion Control) 알고리즘이 TWCC 피드백을 사용합니다.&lt;/p&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;getstats-로-품질-모니터링&quot;&gt;getStats() 로 품질 모니터링&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:#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;async&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; stats&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; pc.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getStats&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;  stats.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;stat&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; (stat.type &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;inbound-rtp&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; stat.kind &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;audio&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;      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;jitter&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, stat.jitter);&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;packetsLost&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, stat.packetsLost);&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;roundTripTime&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, stat.roundTripTime);&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 style=&quot;color:#E1E4E8&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2000&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;/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;기준 (음성 품질)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;jitter&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&amp;#x3C; 30ms 이면 양호&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;packetsLost&lt;/code&gt; (비율)&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&amp;#x3C; 1% 이면 양호, &gt; 5% 이면 품질 저하&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;roundTripTime&lt;/code&gt;&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;&amp;#x3C; 150ms 이면 양방향 통화에 OK&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;framesDropped&lt;/code&gt;&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;흔한-함정&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;ip-라우팅-흐름&quot;&gt;IP 라우팅 흐름&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[&quot;호스트 A (192.168.1.10)&quot;] --&gt;|&quot;1. 패킷 생성&quot;| R1[&quot;Router/NAT&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R1 --&gt;|&quot;2. public IP 변환&quot;| ISP[인터넷 라우터]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ISP --&gt;|&quot;3. longest prefix match&quot;| R2[Edge Router]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    R2 --&gt;|&quot;4. TTL 감소 후 전달&quot;| B[&quot;호스트 B (8.8.8.8)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ISP --&gt;|&quot;TTL=0, ICMPv4 반환&quot;| A&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 hop 에서 라우터 동작:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;목적지 IP 의 라우팅 테이블 &lt;strong&gt;longest prefix match&lt;/strong&gt; 조회&lt;/li&gt;
&lt;li&gt;매칭 경로 없으면 &lt;strong&gt;default route&lt;/strong&gt; (&lt;code&gt;0.0.0.0/0&lt;/code&gt;) 사용&lt;/li&gt;
&lt;li&gt;TTL 1 감소. 0 이면 패킷 폐기 + ICMP Time Exceeded 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;서브넷--cidr&quot;&gt;서브넷 / CIDR&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CIDR (Classless Inter-Domain Routing)&lt;/strong&gt;: 가변 길이 prefix 로 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;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;          └─ prefix 24 bit → subnet mask 255.255.255.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;               → 192.168.1.0 ~ 192.168.1.255 (256개 주소)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;               → 사용 가능: 192.168.1.1 ~ 192.168.1.254 (네트워크 주소/브로드캐스트 제외)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Prefix&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;주소 수&lt;/th&gt;&lt;th align=&quot;right&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;/30&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;4&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;P2P 링크&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/29&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;8&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;6&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;/24&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;256&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;254&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;일반 LAN&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;/22&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1024&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1022&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;/16&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;65536&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;65534&lt;/td&gt;&lt;td align=&quot;left&quot;&gt;대규모 VPC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;subnetting-계산&quot;&gt;Subnetting 계산&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;10.0.0.0/8 (Class A private) 를 /24 로 쪼개면:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  10.0.0.0/24, 10.0.1.0/24, ..., 10.255.255.0/24 → 65536 개 서브넷&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;AWS VPC 설계 예:&lt;/span&gt;&lt;/span&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;    ├── Public:  10.0.1.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├── Private: 10.0.2.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └── DB:      10.0.3.0/24&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;bitwise AND 로 네트워크 주소 계산:&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;IP:   192.168.1.100 = 11000000.10101000.00000001.01100100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Mask: 255.255.255.0 = 11111111.11111111.11111111.00000000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;AND:  192.168.1.0   = 11000000.10101000.00000001.00000000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;arp-address-resolution-protocol&quot;&gt;ARP (Address Resolution Protocol)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;IPv4 에서 L3 → L2 변환&lt;/strong&gt;: IP 주소 → MAC 주소 결정.&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;송신: ARP Request (broadcast)  &quot;192.168.1.1 의 MAC 주소는?&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;수신: ARP Reply (unicast)       &quot;내 MAC 은 aa:bb:cc:dd:ee:ff&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;캐싱: ARP 테이블에 IP-MAC 매핑 저장 (보통 TTL ~20분)&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;# ARP 테이블 확인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;arp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&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:#6A737D&quot;&gt;# IPv6 NDP 테이블 (Neighbor Discovery Protocol)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ip&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -6&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; neigh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IPv6 에서는 &lt;strong&gt;NDP (Neighbor Discovery Protocol)&lt;/strong&gt; 가 ARP 대체. ICMPv6 기반, Solicited-Node Multicast 사용 → broadcast 불필요.&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></channel></rss>