<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Boto3 on Devlog in the SKY</title>
    <link>https://skyoo2003.github.io/ko/tags/boto3/</link>
    <description>Recent content in Boto3 on Devlog in the SKY</description>
    <generator>Hugo</generator>
    <language>ko</language>
    <lastBuildDate>Sun, 19 Apr 2026 00:00:00 +0900</lastBuildDate>
    <atom:link href="https://skyoo2003.github.io/ko/tags/boto3/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>로컬 AWS 에뮬레이터에서 boto3 호환성 달성하기</title>
      <link>https://skyoo2003.github.io/ko/posts/2026/04/19/local-aws-emulator-boto3-compatibility/</link>
      <pubDate>Sun, 19 Apr 2026 00:00:00 +0900</pubDate>
      <guid>https://skyoo2003.github.io/ko/posts/2026/04/19/local-aws-emulator-boto3-compatibility/</guid>
      <description>&lt;h2 id=&#34;들어가며&#34;&gt;들어가며&lt;/h2&gt;
&lt;p&gt;클라우드 네이티브 애플리케이션을 개발하다 보면 느끼는 불편함이 있다. CI 파이프라인에서 AWS를 호출하면 비용이 청구되고, VPN이 없으면 개발이 멈추며, 온보딩에는 자격 증명 설정이 필요하다. &lt;a href=&#34;https://github.com/skyoo2003/devcloud&#34;&gt;DevCloud&lt;/a&gt;는 이 문제를 로컬에서 해결하는 AWS 에뮬레이터다.&lt;/p&gt;
&lt;p&gt;boto3 호환성 테스트에서 **671/699 케이스 통과 (96%)**를 달성했다. 이 숫자는 단순한 테스트 통과율이 아니라, 프로토콜 설계가 얼마나 정확한지를 보여주는 지표다.&lt;/p&gt;
&lt;h2 id=&#34;왜-aws-에뮬레이션이-어려운가&#34;&gt;왜 AWS 에뮬레이션이 어려운가&lt;/h2&gt;
&lt;p&gt;해결책을 설명하기 전에, AWS 에뮬레이션이 왜 근본적으로 어려운지 이해할 필요가 있다.&lt;/p&gt;
&lt;p&gt;대부분의 API는 프로토콜이 하나다. gRPC 서비스는 protobuf를 쓰고, REST API는 JSON을 쓰며, GraphQL은 자체 쿼리 언어를 사용한다. AWS는 &lt;strong&gt;서로 다른 다섯 가지 프로토콜&lt;/strong&gt;을 서비스별로 사용하며, 각 프로토콜마다 직렬화 규칙, 에러 포맷, 인증 방식이 다르다. 단일한 &amp;ldquo;AWS API&amp;quot;는 존재하지 않는다 — 사실상 하나의 서비스 모델을 공유하는 다섯 개의 프로토콜 구현체를 만드는 셈이다.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>클라우드 네이티브 애플리케이션을 개발하다 보면 느끼는 불편함이 있다. CI 파이프라인에서 AWS를 호출하면 비용이 청구되고, VPN이 없으면 개발이 멈추며, 온보딩에는 자격 증명 설정이 필요하다. <a href="https://github.com/skyoo2003/devcloud">DevCloud</a>는 이 문제를 로컬에서 해결하는 AWS 에뮬레이터다.</p>
<p>boto3 호환성 테스트에서 **671/699 케이스 통과 (96%)**를 달성했다. 이 숫자는 단순한 테스트 통과율이 아니라, 프로토콜 설계가 얼마나 정확한지를 보여주는 지표다.</p>
<h2 id="왜-aws-에뮬레이션이-어려운가">왜 AWS 에뮬레이션이 어려운가</h2>
<p>해결책을 설명하기 전에, AWS 에뮬레이션이 왜 근본적으로 어려운지 이해할 필요가 있다.</p>
<p>대부분의 API는 프로토콜이 하나다. gRPC 서비스는 protobuf를 쓰고, REST API는 JSON을 쓰며, GraphQL은 자체 쿼리 언어를 사용한다. AWS는 <strong>서로 다른 다섯 가지 프로토콜</strong>을 서비스별로 사용하며, 각 프로토콜마다 직렬화 규칙, 에러 포맷, 인증 방식이 다르다. 단일한 &ldquo;AWS API&quot;는 존재하지 않는다 — 사실상 하나의 서비스 모델을 공유하는 다섯 개의 프로토콜 구현체를 만드는 셈이다.</p>
<p>두 번째 난제는 **동작 충실도(behavioral fidelity)**다. 올바른 JSON 구조를 반환하는 것만으로는 충분하지 않다. 타임스탬프는 정해진 정밀도의 ISO 8601 포맷이어야 하고, 에러 코드는 AWS 특정 문자열과 정확히 일치해야 하며, 페이지네이션 토큰은 SDK가 파싱할 수 있는 형태여야 하고, XML 응답은 boto3가 기대하는 정확한 네임스페이스 선언을 가져야 한다. 이 디테일 각각이 잠재적 호환성 실패 원인이다.</p>
<h2 id="5가지-프로토콜-하나의-게이트웨이">5가지 프로토콜, 하나의 게이트웨이</h2>
<p>DevCloud는 다섯 가지 AWS 프로토콜을 단일 HTTP 게이트웨이에서 처리한다:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 640 329"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='0' y='228' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='8' y='52' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='8' y='228' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>▼</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='16' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='84' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='16' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='116' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='132' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='148' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='164' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='180' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='196' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='212' fill='currentColor' style='font-size:1em'>▼</text>
<text text-anchor='middle' x='16' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='16' y='244' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='16' y='260' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='16' y='276' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='16' y='292' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='16' y='308' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='228' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='260' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='276' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='292' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='308' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='32' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='40' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='40' y='84' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='40' y='228' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='40' y='260' fill='currentColor' style='font-size:1em'>X</text>
<text text-anchor='middle' x='40' y='276' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='40' y='292' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='40' y='308' fill='currentColor' style='font-size:1em'>기</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='48' y='84' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='100' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='48' y='116' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='48' y='132' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='48' y='148' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='48' y='164' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='48' y='180' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='48' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='48' y='260' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='48' y='276' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='48' y='292' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='308' fill='currentColor' style='font-size:1em'>본</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='84' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='56' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='148' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='164' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='180' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='228' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='56' y='260' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='56' y='276' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='292' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='64' y='84' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='64' y='260' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='276' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='64' y='292' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='84' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='72' y='100' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='72' y='116' fill='currentColor' style='font-size:1em'>B</text>
<text text-anchor='middle' x='72' y='132' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='72' y='148' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='72' y='164' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='72' y='180' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='72' y='228' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='72' y='260' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='72' y='276' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='292' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='80' y='84' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='100' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='80' y='116' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='80' y='132' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='80' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='180' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='80' y='228' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='260' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='80' y='276' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='88' y='84' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='88' y='100' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='88' y='116' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='88' y='132' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='88' y='148' fill='currentColor' style='font-size:1em'>q</text>
<text text-anchor='middle' x='88' y='164' fill='currentColor' style='font-size:1em'>q</text>
<text text-anchor='middle' x='88' y='180' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='88' y='228' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='260' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='88' y='276' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='292' fill='currentColor' style='font-size:1em'>서</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='96' y='84' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='100' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='96' y='116' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='96' y='132' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='96' y='148' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='164' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='180' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='96' y='228' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='96' y='260' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='276' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='96' y='292' fill='currentColor' style='font-size:1em'>명</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='104' y='84' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='100' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='116' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='104' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='180' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='104' y='228' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='104' y='260' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='104' y='276' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='104' y='292' fill='currentColor' style='font-size:1em'>에</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='112' y='84' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='112' y='100' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='112' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='112' y='132' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='112' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='164' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='112' y='180' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='112' y='228' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='112' y='260' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='112' y='276' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='120' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='116' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='120' y='132' fill='currentColor' style='font-size:1em'>크</text>
<text text-anchor='middle' x='120' y='148' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='164' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='120' y='180' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='120' y='228' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='260' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='276' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='292' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='84' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='128' y='100' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='128' y='116' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='128' y='132' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='128' y='148' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='128' y='164' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='128' y='180' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='128' y='228' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='128' y='260' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='276' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='128' y='292' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='136' y='84' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='136' y='100' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='116' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='132' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='136' y='148' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='136' y='164' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='136' y='180' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='136' y='276' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='136' y='292' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='144' y='84' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='144' y='100' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='144' y='164' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='144' y='180' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='260' fill='currentColor' style='font-size:1em'>헤</text>
<text text-anchor='middle' x='144' y='292' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='152' y='84' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='100' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='116' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='152' y='132' fill='currentColor' style='font-size:1em'>오</text>
<text text-anchor='middle' x='152' y='148' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='152' y='164' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='180' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='152' y='260' fill='currentColor' style='font-size:1em'>더</text>
<text text-anchor='middle' x='152' y='276' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='152' y='292' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='160' y='52' fill='currentColor' style='font-size:1em'>4</text>
<text text-anchor='middle' x='160' y='84' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='160' y='100' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='160' y='116' fill='currentColor' style='font-size:1em'>요</text>
<text text-anchor='middle' x='160' y='132' fill='currentColor' style='font-size:1em'>리</text>
<text text-anchor='middle' x='160' y='148' fill='currentColor' style='font-size:1em'>X</text>
<text text-anchor='middle' x='160' y='164' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='180' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='160' y='276' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='292' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='168' y='100' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='168' y='116' fill='currentColor' style='font-size:1em'>청</text>
<text text-anchor='middle' x='168' y='132' fill='currentColor' style='font-size:1em'>진</text>
<text text-anchor='middle' x='168' y='148' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='168' y='164' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='168' y='260' fill='currentColor' style='font-size:1em'>존</text>
<text text-anchor='middle' x='168' y='276' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='176' y='52' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='176' y='148' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='176' y='180' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='176' y='260' fill='currentColor' style='font-size:1em'>재</text>
<text text-anchor='middle' x='176' y='276' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='176' y='292' fill='currentColor' style='font-size:1em'>경</text>
<text text-anchor='middle' x='184' y='100' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='184' y='116' fill='currentColor' style='font-size:1em'>크</text>
<text text-anchor='middle' x='184' y='132' fill='currentColor' style='font-size:1em'>처</text>
<text text-anchor='middle' x='184' y='148' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='184' y='164' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='184' y='180' fill='currentColor' style='font-size:1em'>대</text>
<text text-anchor='middle' x='184' y='276' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='184' y='292' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='192' y='100' fill='currentColor' style='font-size:1em'>패</text>
<text text-anchor='middle' x='192' y='116' fill='currentColor' style='font-size:1em'>기</text>
<text text-anchor='middle' x='192' y='132' fill='currentColor' style='font-size:1em'>리</text>
<text text-anchor='middle' x='192' y='148' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='192' y='164' fill='currentColor' style='font-size:1em'>구</text>
<text text-anchor='middle' x='192' y='180' fill='currentColor' style='font-size:1em'>시</text>
<text text-anchor='middle' x='192' y='276' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='200' y='100' fill='currentColor' style='font-size:1em'>닉</text>
<text text-anchor='middle' x='200' y='132' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='200' y='148' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='200' y='164' fill='currentColor' style='font-size:1em'>조</text>
<text text-anchor='middle' x='200' y='180' fill='currentColor' style='font-size:1em'>보</text>
<text text-anchor='middle' x='200' y='276' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='208' y='116' fill='currentColor' style='font-size:1em'>제</text>
<text text-anchor='middle' x='208' y='148' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='208' y='164' fill='currentColor' style='font-size:1em'>화</text>
<text text-anchor='middle' x='208' y='180' fill='currentColor' style='font-size:1em'>드</text>
<text text-anchor='middle' x='208' y='276' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='216' y='100' fill='currentColor' style='font-size:1em'>복</text>
<text text-anchor='middle' x='216' y='116' fill='currentColor' style='font-size:1em'>한</text>
<text text-anchor='middle' x='216' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='216' y='276' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='224' y='100' fill='currentColor' style='font-size:1em'>구</text>
<text text-anchor='middle' x='224' y='116' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='224' y='148' fill='currentColor' style='font-size:1em'>q</text>
<text text-anchor='middle' x='224' y='164' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='224' y='180' fill='currentColor' style='font-size:1em'>실</text>
<text text-anchor='middle' x='224' y='276' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='232' y='100' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='232' y='148' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='232' y='164' fill='currentColor' style='font-size:1em'>깅</text>
<text text-anchor='middle' x='232' y='180' fill='currentColor' style='font-size:1em'>시</text>
<text text-anchor='middle' x='232' y='276' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='232' y='292' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='240' y='148' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='240' y='164' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='240' y='180' fill='currentColor' style='font-size:1em'>간</text>
<text text-anchor='middle' x='240' y='260' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='240' y='276' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='248' y='148' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='248' y='276' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='248' y='292' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='256' y='148' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='180' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='256' y='260' fill='currentColor' style='font-size:1em'>J</text>
<text text-anchor='middle' x='256' y='276' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='256' y='292' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='256' y='308' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='148' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='264' y='180' fill='currentColor' style='font-size:1em'>그</text>
<text text-anchor='middle' x='264' y='260' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='264' y='276' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='264' y='292' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='272' y='148' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='272' y='180' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='272' y='260' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='272' y='276' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='272' y='292' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='272' y='308' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='280' y='148' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='280' y='260' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='280' y='276' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='280' y='292' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='280' y='308' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='288' y='148' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='288' y='276' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='288' y='292' fill='currentColor' style='font-size:1em'>J</text>
<text text-anchor='middle' x='288' y='308' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='296' y='260' fill='currentColor' style='font-size:1em'>프</text>
<text text-anchor='middle' x='296' y='276' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='296' y='292' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='296' y='308' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='304' y='260' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='304' y='276' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='304' y='292' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='304' y='308' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='312' y='260' fill='currentColor' style='font-size:1em'>토</text>
<text text-anchor='middle' x='312' y='276' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='312' y='292' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='312' y='308' fill='currentColor' style='font-size:1em'>X</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>K</text>
<text text-anchor='middle' x='320' y='260' fill='currentColor' style='font-size:1em'>콜</text>
<text text-anchor='middle' x='320' y='308' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='328' y='276' fill='currentColor' style='font-size:1em'>+</text>
<text text-anchor='middle' x='328' y='292' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='328' y='308' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='336' y='260' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='336' y='292' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='344' y='260' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='344' y='276' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='344' y='292' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='344' y='308' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='352' y='260' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='352' y='276' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='352' y='292' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='352' y='308' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='360' y='260' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='360' y='276' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='360' y='292' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='360' y='308' fill='currentColor' style='font-size:1em'>3</text>
<text text-anchor='middle' x='368' y='260' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='368' y='276' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='292' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='368' y='308' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='376' y='260' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='376' y='276' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='376' y='292' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='384' y='260' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='384' y='276' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='384' y='292' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='392' y='260' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='392' y='276' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='400' y='260' fill='currentColor' style='font-size:1em'>B</text>
<text text-anchor='middle' x='408' y='260' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='408' y='276' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='424' y='260' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='424' y='276' fill='currentColor' style='font-size:1em'>Q</text>
<text text-anchor='middle' x='432' y='260' fill='currentColor' style='font-size:1em'>Q</text>
<text text-anchor='middle' x='432' y='276' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='440' y='260' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='440' y='276' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='448' y='276' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='456' y='260' fill='currentColor' style='font-size:1em'>J</text>
<text text-anchor='middle' x='456' y='276' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='464' y='260' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='472' y='260' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='472' y='276' fill='currentColor' style='font-size:1em'>프</text>
<text text-anchor='middle' x='480' y='260' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='480' y='276' fill='currentColor' style='font-size:1em'>로</text>
<text text-anchor='middle' x='488' y='260' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='488' y='276' fill='currentColor' style='font-size:1em'>토</text>
<text text-anchor='middle' x='496' y='276' fill='currentColor' style='font-size:1em'>콜</text>
<text text-anchor='middle' x='512' y='276' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='520' y='276' fill='currentColor' style='font-size:1em'>I</text>
<text text-anchor='middle' x='528' y='276' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='536' y='276' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='544' y='276' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='560' y='276' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='568' y='276' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='576' y='276' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='584' y='276' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='600' y='276' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='608' y='276' fill='currentColor' style='font-size:1em'>Q</text>
<text text-anchor='middle' x='616' y='276' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='624' y='276' fill='currentColor' style='font-size:1em'>)</text>
</g>

    </svg>
  
</div>
<h3 id="프로토콜-자동-감지">프로토콜 자동 감지</h3>
<p>들어오는 요청의 헤더만으로 프로토콜을 판별한다:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#6272a4">// internal/gateway/protocol.go</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> <span style="color:#50fa7b">DetectProtocol</span>(r <span style="color:#ff79c6">*</span>http.Request) (protocol <span style="color:#8be9fd">string</span>, serviceID <span style="color:#8be9fd">string</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// 1. JSON 프로토콜: X-Amz-Target 헤더 존재</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> target <span style="color:#ff79c6">:=</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">&#34;X-Amz-Target&#34;</span>); target <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>        contentType <span style="color:#ff79c6">:=</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">&#34;Content-Type&#34;</span>)
</span></span><span style="display:flex;"><span>        proto <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">jsonProtocolFromContentType</span>(contentType)
</span></span><span style="display:flex;"><span>        service <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">serviceFromTarget</span>(target)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> proto, service
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// 2. Query 프로토콜: form-encoded body에 Action= 파라미터</span>
</span></span><span style="display:flex;"><span>    contentType <span style="color:#ff79c6">:=</span> r.Header.<span style="color:#50fa7b">Get</span>(<span style="color:#f1fa8c">&#34;Content-Type&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> strings.<span style="color:#50fa7b">Contains</span>(contentType, <span style="color:#f1fa8c">&#34;application/x-www-form-urlencoded&#34;</span>) {
</span></span><span style="display:flex;"><span>        bodyBytes, err <span style="color:#ff79c6">:=</span> io.<span style="color:#50fa7b">ReadAll</span>(r.Body)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">if</span> err <span style="color:#ff79c6">==</span> <span style="color:#ff79c6">nil</span> {
</span></span><span style="display:flex;"><span>            r.Body = io.<span style="color:#50fa7b">NopCloser</span>(bytes.<span style="color:#50fa7b">NewReader</span>(bodyBytes))
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">if</span> strings.<span style="color:#50fa7b">Contains</span>(<span style="color:#8be9fd;font-style:italic">string</span>(bodyBytes), <span style="color:#f1fa8c">&#34;Action=&#34;</span>) {
</span></span><span style="display:flex;"><span>                service <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">serviceFromQueryRequest</span>(r, <span style="color:#8be9fd;font-style:italic">string</span>(bodyBytes))
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">return</span> <span style="color:#f1fa8c">&#34;query&#34;</span>, service
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// 3. SigV4 서명에서 REST 스타일 서비스 추출</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> svc <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">serviceFromSigV4</span>(r); svc <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">&#34;&#34;</span> <span style="color:#ff79c6">&amp;&amp;</span> svc <span style="color:#ff79c6">!=</span> <span style="color:#f1fa8c">&#34;s3&#34;</span> {
</span></span><span style="display:flex;"><span>        normalized <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">normalizeServiceID</span>(svc)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> <span style="color:#f1fa8c">&#34;rest-json&#34;</span>, normalized
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// 4. 기본: REST-XML (S3)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">return</span> <span style="color:#f1fa8c">&#34;rest-xml&#34;</span>, <span style="color:#f1fa8c">&#34;s3&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></td></tr></table>
</div>
</div><p>감지 순서가 중요하다. <code>X-Amz-Target</code>을 먼저 확인해야 JSON 프로토콜 서비스(DynamoDB, Lambda)가 Query 프로토콜로 오인되지 않는다. SQS는 JSON과 Query 프로토콜을 모두 지원하는 특이한 서비스로, <code>X-Amz-Target</code> 헤더 유무로 구분한다.</p>
<h3 id="서비스-이름-정규화">서비스 이름 정규화</h3>
<p>게이트웨이는 100개 이상의 AWS 서비스 이름을 내부 ID로 매핑하는 광범위한 테이블을 유지한다. 특수 케이스가 많다:</p>
<ul>
<li>SES → <code>sesv2</code> (Query가 아닌 REST-JSON)</li>
<li><code>opensearch</code> vs <code>elasticsearchservice</code> (경로 기반 구분)</li>
<li>서비스 이름 정규화 (공백 제거, 소문자 변환)</li>
</ul>
<p>SDK가 서비스를 참조하는 방식에 관계없이 올바른 플러그인에 도달하도록 보장한다.</p>
<h2 id="프로토콜별-직렬화-상세">프로토콜별 직렬화 상세</h2>
<p>각 프로토콜의 직렬화 방식은 완전히 다르다. 에뮬레이터에서 가장 까다로운 부분이 바로 이 직렬화/역직렬화 계층이다. 구체적인 예시를 살펴보자.</p>
<h3 id="rest-xml-s3">REST-XML (S3)</h3>
<p>S3는 가장 복잡한 프로토콜이다. 오퍼레이션은 HTTP 메서드와 경로로 결정되며, 응답은 특정 네임스페이스 선언이 있는 유효한 XML이어야 한다.</p>
<p><strong>요청 라우팅:</strong></p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 536 89"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='8' y='36' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='8' y='52' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='8' y='68' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='16' y='36' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='16' y='68' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='24' y='36' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='24' y='52' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='24' y='68' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='56' y='68' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='72' y='68' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='80' y='68' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='88' y='68' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='36' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='104' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='112' y='36' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='128' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='136' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='136' y='68' fill='currentColor' style='font-size:1em'>?</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='36' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='144' y='68' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='152' y='36' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='152' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='36' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='52' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='160' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='168' y='36' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='168' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='176' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='176' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='184' y='36' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='184' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='184' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='200' y='36' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='200' y='52' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='200' y='68' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='208' y='36' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='208' y='52' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='208' y='68' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='216' y='36' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='216' y='52' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='216' y='68' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='224' y='36' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='224' y='52' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='224' y='68' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='36' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='52' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='68' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='240' y='36' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='52' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='240' y='68' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='248' y='36' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='68' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='256' y='36' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='256' y='52' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='344' y='20' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='344' y='36' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='344' y='52' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>→</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='360' y='20' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='360' y='36' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='360' y='52' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='360' y='68' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='368' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='368' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='368' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='376' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='376' y='36' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='376' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='376' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='384' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='384' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='384' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='384' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='392' y='20' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='392' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='392' y='52' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='392' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='400' y='20' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='400' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='400' y='52' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='400' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='408' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='408' y='36' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='408' y='52' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='408' y='68' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='416' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='36' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='416' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='416' y='68' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='424' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='36' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='424' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='424' y='68' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='432' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='432' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='432' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='432' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='440' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='440' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='440' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='448' y='20' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='448' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='448' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='456' y='20' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='456' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='472' y='68' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='480' y='68' fill='currentColor' style='font-size:1em'>다</text>
<text text-anchor='middle' x='488' y='68' fill='currentColor' style='font-size:1em'>중</text>
<text text-anchor='middle' x='504' y='68' fill='currentColor' style='font-size:1em'>객</text>
<text text-anchor='middle' x='512' y='68' fill='currentColor' style='font-size:1em'>체</text>
<text text-anchor='middle' x='520' y='68' fill='currentColor' style='font-size:1em'>)</text>
</g>

    </svg>
  
</div>
<p><strong>응답 XML 구조:</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#ff79c6">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;ListObjectsV2Output&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;Name&gt;</span>my-bucket<span style="color:#ff79c6">&lt;/Name&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;Prefix&gt;&lt;/Prefix&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;KeyCount&gt;</span>2<span style="color:#ff79c6">&lt;/KeyCount&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;MaxKeys&gt;</span>1000<span style="color:#ff79c6">&lt;/MaxKeys&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;IsTruncated&gt;</span>false<span style="color:#ff79c6">&lt;/IsTruncated&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;Contents&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;Key&gt;</span>hello.txt<span style="color:#ff79c6">&lt;/Key&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;LastModified&gt;</span>2026-04-19T12:00:00.000Z<span style="color:#ff79c6">&lt;/LastModified&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;ETag&gt;</span>&#34;d41d8cd98f00b204e9800998ecf8427e&#34;<span style="color:#ff79c6">&lt;/ETag&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;Size&gt;</span>13<span style="color:#ff79c6">&lt;/Size&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">&lt;StorageClass&gt;</span>STANDARD<span style="color:#ff79c6">&lt;/StorageClass&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">&lt;/Contents&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">&lt;/ListObjectsV2Output&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>boto3는 서비스 모델의 <code>xmlNamespace</code> 트레이트를 사용하여 이 XML을 파싱한다. 네임스페이스 선언이나 엘리먼트 순서가 틀리면, 역직렬화가 조용히 실패하거나 잘못된 값을 생성한다.</p>
<h3 id="json-1011-dynamodb-lambda">JSON 1.0/1.1 (DynamoDB, Lambda)</h3>
<p>JSON 프로토콜은 라우팅이 단순하지만 자체적인 뉘앙스가 있다:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 592 73"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>X</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>{</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='8' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='16' y='52' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='40' y='52' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='160' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>B</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='184' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='192' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='192' y='52' fill='currentColor' style='font-size:1em'>K</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='200' y='20' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='200' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='208' y='20' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='52' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='216' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='216' y='52' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='224' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='224' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='232' y='20' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='232' y='52' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='240' y='20' fill='currentColor' style='font-size:1em'>z</text>
<text text-anchor='middle' x='240' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='248' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='248' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='256' y='20' fill='currentColor' style='font-size:1em'>j</text>
<text text-anchor='middle' x='256' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='264' y='20' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='264' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='52' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='280' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='20' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='288' y='52' fill='currentColor' style='font-size:1em'>[</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='296' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='296' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='304' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='304' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='312' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='312' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='320' y='52' fill='currentColor' style='font-size:1em'>]</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='328' y='52' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='344' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='352' y='52' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='360' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='368' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='376' y='52' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='384' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='392' y='52' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='400' y='52' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='408' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='416' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='424' y='52' fill='currentColor' style='font-size:1em'>D</text>
<text text-anchor='middle' x='432' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='440' y='52' fill='currentColor' style='font-size:1em'>f</text>
<text text-anchor='middle' x='448' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='456' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='464' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='472' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='480' y='52' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='488' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='496' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='504' y='52' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='52' fill='currentColor' style='font-size:1em'>"</text>
<text text-anchor='middle' x='520' y='52' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='536' y='52' fill='currentColor' style='font-size:1em'>[</text>
<text text-anchor='middle' x='544' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='552' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='560' y='52' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='568' y='52' fill='currentColor' style='font-size:1em'>]</text>
<text text-anchor='middle' x='576' y='52' fill='currentColor' style='font-size:1em'>}</text>
</g>

    </svg>
  
</div>
<p>JSON 1.0은 <code>application/x-amz-json-1.0</code>을 사용하고, 특정 <code>X-Amz-Target</code> 포맷을 따른다: <code>ServiceName_APIVersion.OperationName</code>. JSON 1.1은 <code>application/x-amz-json-1.1</code>과 약간 다른 타겟 포맷을 사용한다.</p>
<p>에러 응답은 특정 구조를 따른다:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#ff79c6">&#34;__type&#34;</span>: <span style="color:#f1fa8c">&#34;ResourceNotFoundException&#34;</span>, <span style="color:#ff79c6">&#34;message&#34;</span>: <span style="color:#f1fa8c">&#34;Requested resource not found&#34;</span>}
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="query-iam-sts">Query (IAM, STS)</h3>
<p>Query 프로토콜은 모든 파라미터를 form-urlencoded 형식으로 인코딩한다:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 392 25"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>V</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>5</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>&amp;</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>U</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>=</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>e</text>
</g>

    </svg>
  
</div>
<p>이 프로토콜은 몇 가지 독특한 직렬화 과제를 가지고 있다:</p>
<ul>
<li><strong>ECMAScript 날짜 포맷</strong>: 타임스탬프가 <code>20260419T120000Z</code> (하이픈과 콜론 없음)으로 인코딩되며, ISO 8601이 아니다</li>
<li><strong>평탄화 리스트</strong>: <code>member.1=Value1&amp;member.2=Value2</code> — JSON 배열이 아닌 인덱스 기반 표현</li>
<li><strong>구조화 맵 키</strong>: <code>AttributeName.1.Name=id&amp;AttributeName.1.Value=userId</code> — 맵 키도 인덱스로 인코딩</li>
<li><strong>부울린 인코딩</strong>: <code>true</code>와 <code>false</code>를 소문자 문자열로 표현</li>
</ul>
<h2 id="96-호환성의-의미">96% 호환성의 의미</h2>
<p>boto3 호환성 테스트는 실제 AWS SDK가 보내는 요청을 에뮬레이터에 재생하고, 응답이 SDK가 기대하는 구조와 일치하는지 검증한다. 이것은 단위 테스트가 아니라 <strong>와이어 프로토콜 수준의 통합 테스트</strong>다.</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 368 153"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>6</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='36' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='0' y='52' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='84' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='116' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='0' y='132' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='8' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='24' y='20' fill='currentColor' style='font-size:1em'>6</text>
<text text-anchor='middle' x='24' y='36' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='24' y='52' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='24' y='68' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='24' y='84' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='24' y='100' fill='currentColor' style='font-size:1em'>│</text>
<text text-anchor='middle' x='24' y='116' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>7</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='32' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='32' y='84' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='32' y='116' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>테</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='48' y='52' fill='currentColor' style='font-size:1em'>페</text>
<text text-anchor='middle' x='48' y='84' fill='currentColor' style='font-size:1em'>타</text>
<text text-anchor='middle' x='48' y='116' fill='currentColor' style='font-size:1em'>예</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>트</text>
<text text-anchor='middle' x='56' y='36' fill='currentColor' style='font-size:1em'>실</text>
<text text-anchor='middle' x='56' y='52' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='56' y='68' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='56' y='84' fill='currentColor' style='font-size:1em'>임</text>
<text text-anchor='middle' x='56' y='100' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='56' y='116' fill='currentColor' style='font-size:1em'>외</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>통</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>패</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>지</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='64' y='84' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='64' y='100' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='64' y='132' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>케</text>
<text text-anchor='middle' x='72' y='20' fill='currentColor' style='font-size:1em'>과</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>네</text>
<text text-anchor='middle' x='72' y='84' fill='currentColor' style='font-size:1em'>탬</text>
<text text-anchor='middle' x='72' y='116' fill='currentColor' style='font-size:1em'>메</text>
<text text-anchor='middle' x='72' y='132' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='80' y='68' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='80' y='84' fill='currentColor' style='font-size:1em'>프</text>
<text text-anchor='middle' x='80' y='100' fill='currentColor' style='font-size:1em'>밀</text>
<text text-anchor='middle' x='80' y='116' fill='currentColor' style='font-size:1em'>시</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='88' y='52' fill='currentColor' style='font-size:1em'>션</text>
<text text-anchor='middle' x='88' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='88' y='100' fill='currentColor' style='font-size:1em'>리</text>
<text text-anchor='middle' x='88' y='116' fill='currentColor' style='font-size:1em'>지</text>
<text text-anchor='middle' x='88' y='132' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>9</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='96' y='84' fill='currentColor' style='font-size:1em'>포</text>
<text text-anchor='middle' x='96' y='100' fill='currentColor' style='font-size:1em'>초</text>
<text text-anchor='middle' x='96' y='132' fill='currentColor' style='font-size:1em'>W</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>실</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>6</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>엣</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='104' y='84' fill='currentColor' style='font-size:1em'>맷</text>
<text text-anchor='middle' x='104' y='116' fill='currentColor' style='font-size:1em'>텍</text>
<text text-anchor='middle' x='104' y='132' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>행</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>지</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='112' y='100' fill='currentColor' style='font-size:1em'>정</text>
<text text-anchor='middle' x='112' y='116' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>0</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='120' y='84' fill='currentColor' style='font-size:1em'>미</text>
<text text-anchor='middle' x='120' y='100' fill='currentColor' style='font-size:1em'>밀</text>
<text text-anchor='middle' x='120' y='116' fill='currentColor' style='font-size:1em'>트</text>
<text text-anchor='middle' x='120' y='132' fill='currentColor' style='font-size:1em'>정</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>%</text>
<text text-anchor='middle' x='128' y='52' fill='currentColor' style='font-size:1em'>케</text>
<text text-anchor='middle' x='128' y='68' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='128' y='84' fill='currentColor' style='font-size:1em'>세</text>
<text text-anchor='middle' x='128' y='100' fill='currentColor' style='font-size:1em'>도</text>
<text text-anchor='middle' x='128' y='132' fill='currentColor' style='font-size:1em'>확</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='136' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='136' y='100' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='136' y='116' fill='currentColor' style='font-size:1em'>차</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>스</text>
<text text-anchor='middle' x='144' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='144' y='84' fill='currentColor' style='font-size:1em'>차</text>
<text text-anchor='middle' x='144' y='116' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='144' y='132' fill='currentColor' style='font-size:1em'>문</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='152' y='84' fill='currentColor' style='font-size:1em'>이</text>
<text text-anchor='middle' x='152' y='100' fill='currentColor' style='font-size:1em'>타</text>
<text text-anchor='middle' x='152' y='116' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='152' y='132' fill='currentColor' style='font-size:1em'>구</text>
<text text-anchor='middle' x='160' y='68' fill='currentColor' style='font-size:1em'>포</text>
<text text-anchor='middle' x='160' y='84' fill='currentColor' style='font-size:1em'>:</text>
<text text-anchor='middle' x='160' y='100' fill='currentColor' style='font-size:1em'>임</text>
<text text-anchor='middle' x='160' y='132' fill='currentColor' style='font-size:1em'>와</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>1</text>
<text text-anchor='middle' x='168' y='68' fill='currentColor' style='font-size:1em'>맷</text>
<text text-anchor='middle' x='168' y='100' fill='currentColor' style='font-size:1em'>존</text>
<text text-anchor='middle' x='168' y='116' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='176' y='52' fill='currentColor' style='font-size:1em'>2</text>
<text text-anchor='middle' x='176' y='68' fill='currentColor' style='font-size:1em'>,</text>
<text text-anchor='middle' x='176' y='84' fill='currentColor' style='font-size:1em'>8</text>
<text text-anchor='middle' x='176' y='116' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='176' y='132' fill='currentColor' style='font-size:1em'>불</text>
<text text-anchor='middle' x='184' y='52' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='184' y='84' fill='currentColor' style='font-size:1em'>개</text>
<text text-anchor='middle' x='184' y='100' fill='currentColor' style='font-size:1em'>처</text>
<text text-anchor='middle' x='184' y='132' fill='currentColor' style='font-size:1em'>일</text>
<text text-anchor='middle' x='192' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='192' y='100' fill='currentColor' style='font-size:1em'>리</text>
<text text-anchor='middle' x='192' y='132' fill='currentColor' style='font-size:1em'>치</text>
<text text-anchor='middle' x='200' y='68' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='208' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='216' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='224' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='232' y='68' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='240' y='68' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='248' y='68' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='272' y='68' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='280' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='288' y='68' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='296' y='68' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='312' y='68' fill='currentColor' style='font-size:1em'>K</text>
<text text-anchor='middle' x='320' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='328' y='68' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='344' y='68' fill='currentColor' style='font-size:1em'>처</text>
<text text-anchor='middle' x='352' y='68' fill='currentColor' style='font-size:1em'>리</text>
</g>

    </svg>
  
</div>
<p>실패한 28개의 대부분은 기능적 오류가 아니라 <strong>포맷 미세 차이</strong>다:</p>
<ol>
<li>
<p><strong>페이지네이션 (12개 실패)</strong>: boto3는 한 응답의 <code>NextToken</code>을 다음 요청의 입력으로 사용한다. 토큰 포맷이 AWS와 다르면 페이지네이션이 깨진다. 우리 토큰은 base64 인코딩 포인터인 반면, AWS는 불투명한 암호화 토큰을 사용한다. SDK는 파싱할 수 있지만, 일부 엣지 케이스에서 라운드트립이 실패한다.</p>
</li>
<li>
<p><strong>타임스탬프 (8개 실패)</strong>: AWS는 가변 정밀도의 타임스탬프를 반환한다 — 밀리초(<code>2026-04-19T12:00:00.123Z</code>)가 있고, 없는 것(<code>2026-04-19T12:00:00Z</code>)도 있다. SDK는 필드별로 특정 포맷을 기대한다. 우리 타임스탬프는 항상 밀리초 정밀도인데, 일부 필드는 소수점이 없는 것을 기대한다.</p>
</li>
<li>
<p><strong>에러 메시지 (8개 실패)</strong>: <code>The bucket you are attempting to access must be addressed using the specified endpoint.</code> 같은 에러 메시지는 AWS의 정확한 문구와 일치해야 한다. 우리 메시지는 동일한 의미를 전달하지만 다른 표현을 사용한다.</p>
</li>
</ol>
<p>이 실패들은 실제 애플리케이션 동작에 영향을 주지 않는다. 애플리케이션은 에러 메시지 텍스트가 아닌 에러 코드(예: <code>NoSuchBucket</code>)를 확인하며, 우리 에러 코드는 정확히 일치한다.</p>
<h2 id="서비스-플러그인-아키텍처">서비스 플러그인 아키텍처</h2>
<p>모든 서비스는 <code>ServicePlugin</code> 인터페이스를 구현한다:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> ServicePlugin <span style="color:#8be9fd;font-style:italic">interface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">ServiceID</span>() <span style="color:#8be9fd">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">ServiceName</span>() <span style="color:#8be9fd">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">Protocol</span>() ProtocolType
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">Init</span>(config PluginConfig) <span style="color:#8be9fd">error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">Shutdown</span>(ctx context.Context) <span style="color:#8be9fd">error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">HandleRequest</span>(ctx context.Context, op <span style="color:#8be9fd">string</span>, req <span style="color:#ff79c6">*</span>http.Request) (<span style="color:#ff79c6">*</span>Response, <span style="color:#8be9fd">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">ListResources</span>(ctx context.Context) ([]Resource, <span style="color:#8be9fd">error</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#50fa7b">GetMetrics</span>(ctx context.Context) (<span style="color:#ff79c6">*</span>ServiceMetrics, <span style="color:#8be9fd">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="플러그인-레지스트리">플러그인 레지스트리</h3>
<p>서비스는 중앙 레지스트리를 통해 등록된다:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">type</span> Registry <span style="color:#8be9fd;font-style:italic">struct</span> {
</span></span><span style="display:flex;"><span>    mu        sync.RWMutex
</span></span><span style="display:flex;"><span>    factories <span style="color:#8be9fd;font-style:italic">map</span>[<span style="color:#8be9fd">string</span>]PluginFactory
</span></span><span style="display:flex;"><span>    active    <span style="color:#8be9fd;font-style:italic">map</span>[<span style="color:#8be9fd">string</span>]ServicePlugin
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> (r <span style="color:#ff79c6">*</span>Registry) <span style="color:#50fa7b">Register</span>(serviceID <span style="color:#8be9fd">string</span>, factory PluginFactory) {
</span></span><span style="display:flex;"><span>    r.mu.<span style="color:#50fa7b">Lock</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">defer</span> r.mu.<span style="color:#50fa7b">Unlock</span>()
</span></span><span style="display:flex;"><span>    r.factories[serviceID] = factory
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">func</span> (r <span style="color:#ff79c6">*</span>Registry) <span style="color:#50fa7b">Init</span>(serviceID <span style="color:#8be9fd">string</span>, cfg PluginConfig) (ServicePlugin, <span style="color:#8be9fd">error</span>) {
</span></span><span style="display:flex;"><span>    factory, ok <span style="color:#ff79c6">:=</span> r.factories[serviceID]
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> !ok {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> <span style="color:#ff79c6">nil</span>, fmt.<span style="color:#50fa7b">Errorf</span>(<span style="color:#f1fa8c">&#34;unknown service: %s&#34;</span>, serviceID)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    p <span style="color:#ff79c6">:=</span> <span style="color:#50fa7b">factory</span>(cfg)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> err <span style="color:#ff79c6">:=</span> p.<span style="color:#50fa7b">Init</span>(cfg); err <span style="color:#ff79c6">!=</span> <span style="color:#ff79c6">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> <span style="color:#ff79c6">nil</span>, fmt.<span style="color:#50fa7b">Errorf</span>(<span style="color:#f1fa8c">&#34;init %s: %w&#34;</span>, serviceID, err)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    r.active[serviceID] = p
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">return</span> p, <span style="color:#ff79c6">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></td></tr></table>
</div>
</div><p>이 분리 덕분에 새 서비스를 추가하려면:</p>
<ol>
<li>코드 생성기를 실행하여 타입과 스텁 생성</li>
<li><code>ServicePlugin</code> 인터페이스 구현</li>
<li>팩토리에 등록</li>
</ol>
<p>프로토콜 처리, 직렬화, 라우팅은 자동 생성 코드가 전부 처리한다.</p>
<h2 id="실제-사용-예시-코드-변경-제로">실제 사용 예시: 코드 변경 제로</h2>
<p>DevCloud를 띄우면 기존 코드 변경 없이 엔드포인트만 바꾸면 된다:</p>
<h3 id="python-boto3">Python (boto3)</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">import</span> boto3
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 프로덕션</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># s3 = boto3.client(&#39;s3&#39;, endpoint_url=&#39;https://s3.amazonaws.com&#39;)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># DevCloud 로컬</span>
</span></span><span style="display:flex;"><span>s3 <span style="color:#ff79c6">=</span> boto3<span style="color:#ff79c6">.</span>client(<span style="color:#f1fa8c">&#39;s3&#39;</span>, endpoint_url<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;http://localhost:4747&#39;</span>,
</span></span><span style="display:flex;"><span>                  aws_access_key_id<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;test&#39;</span>,
</span></span><span style="display:flex;"><span>                  aws_secret_access_key<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;test&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>s3<span style="color:#ff79c6">.</span>create_bucket(Bucket<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;my-bucket&#39;</span>)
</span></span><span style="display:flex;"><span>s3<span style="color:#ff79c6">.</span>put_object(Bucket<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;my-bucket&#39;</span>, Key<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;hello.txt&#39;</span>, Body<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">b</span><span style="color:#f1fa8c">&#39;Hello, DevCloud!&#39;</span>)
</span></span><span style="display:flex;"><span>response <span style="color:#ff79c6">=</span> s3<span style="color:#ff79c6">.</span>get_object(Bucket<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;my-bucket&#39;</span>, Key<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;hello.txt&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">print</span>(response[<span style="color:#f1fa8c">&#39;Body&#39;</span>]<span style="color:#ff79c6">.</span>read())  <span style="color:#6272a4"># b&#39;Hello, DevCloud!&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="docker">Docker</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run -p 4566:4566 skyoo2003/devcloud
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="terraform">Terraform</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">export</span> <span style="color:#8be9fd;font-style:italic">AWS_ENDPOINT_URL</span><span style="color:#ff79c6">=</span>http://localhost:4747
</span></span><span style="display:flex;"><span>terraform apply
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="aws-cli">AWS CLI</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>aws --endpoint-url http://localhost:4747 s3 ls
</span></span></code></pre></td></tr></table>
</div>
</div><p>AWS SDK를 사용하는 모든 도구가 수정 없이 동작한다. DevCloud는 AWS와 동일한 와이어 프로토콜을 구현하므로, SDK 입장에서는 진짜 AWS인지 로컬 에뮬레이터인지 구별할 수 없다.</p>
<h2 id="핵심-인사이트">핵심 인사이트</h2>
<h3 id="1-프로토콜이-복잡성의-핵심이다">1. 프로토콜이 복잡성의 핵심이다</h3>
<p>비즈니스 로직보다 직렬화/역직렬화가 구현 난이도를 결정한다. 96개 서비스에 대해 5개 프로토콜 직렬화기를 수동으로 구현하는 것은 유지보수 악몽이다. 코드 생성으로 자동화하는 것이 올바른 접근이었다.</p>
<h3 id="2-호환성은-포맷-정밀도에서-갈린다">2. 호환성은 포맷 정밀도에서 갈린다</h3>
<p>96%에서 99.9%로 가는 길은 기능 추가가 아니라 타임스탬프 포맷, 에러 메시지 문구, 페이지네이션 토큰 같은 디테일에 있다. 이 디테일은 지루하지만 필수적이다. 96%를 넘어선 각 퍼센트 포인트마다 점점 더 구체적인 포맷 매칭이 필요하다.</p>
<h3 id="3-플러그인-아키텍처로-관심사를-분리하라">3. 플러그인 아키텍처로 관심사를 분리하라</h3>
<p>프로토콜 처리와 서비스 로직을 분리하면 양쪽을 독립적으로 발전시킬 수 있다. 코드 생성 쪽에서 프로토콜 처리를 개선해도 서비스 구현에 영향이 없고, 서비스 개발자는 직렬화 세부사항을 몰라도 기능을 추가할 수 있다.</p>
<h3 id="4-sdk-관점에서-테스트하라">4. SDK 관점에서 테스트하라</h3>
<p>호환 가능한 에뮬레이터를 구축하는 가장 빠른 방법은 서버 관점이 아니라 SDK 관점에서 테스트하는 것이다. 실제 SDK 요청을 녹화하고, 에뮬레이터에 재생한 뒤 응답을 비교한다. 서버 중심 테스트가 놓치는 포맷 이슈를 잡을 수 있다.</p>
<h2 id="마치며">마치며</h2>
<p>96% boto3 호환성은 로컬 개발 환경에서 AWS SDK를 그대로 사용할 수 있다는 것을 의미한다. CI/CD 파이프라인의 클라우드 비용을 없애고, 오프라인 개발(비행기, 제한된 네트워크 환경)을 가능하게 하며, 새 팀원의 온보딩을 <code>docker run</code> 하나로 줄일 수 있다.</p>
<p>남은 4%는 포맷 정밀도 문제다 — 페이지네이션 토큰, 타임스탬프 포맷, 에러 메시지 문구. 이것도 매주 Smithy 모델 동기와 함께 점진적으로 개선되고 있다.</p>
<p>전체 소스 코드는 <a href="https://github.com/skyoo2003/devcloud">github.com/skyoo2003/devcloud</a>에서 확인할 수 있다.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
