<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>김신건의 로그 - 위키 / api-design</title><description>&apos;api-design&apos; 카테고리 위키 페이지 최신 업데이트</description><link>https://shinkeonkim.com/</link><language>ko-KR</language><item><title>[API Design] API Versioning: URL / Header / Date 비교</title><link>https://shinkeonkim.com/wiki/api-design/api-versioning/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/api-design/api-versioning/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;API Versioning&lt;/strong&gt; 은 &lt;em&gt;breaking change 를 클라이언트에 강요 없이&lt;/em&gt; 호환성을 유지하는 전략. 잘못 잡으면 &lt;em&gt;모든 클라이언트 동시 마이그레이션 강요&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;무엇이-breaking-change&quot;&gt;무엇이 Breaking Change?&lt;/h2&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Breaking&lt;/th&gt;&lt;th&gt;Non-breaking&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;필드 제거 / 이름 변경&lt;/td&gt;&lt;td&gt;새 필드 추가 (옵션)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;필드 타입 변경&lt;/td&gt;&lt;td&gt;새 endpoint 추가&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;필수 필드 추가&lt;/td&gt;&lt;td&gt;새 enum 값 추가 (옵션 일 때만)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Enum 값 제거&lt;/td&gt;&lt;td&gt;새 query param (옵션)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Endpoint 삭제&lt;/td&gt;&lt;td&gt;새 응답 헤더&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;응답 status code 변경&lt;/td&gt;&lt;td&gt;rate limit 조정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Auth 방식 변경&lt;/td&gt;&lt;td&gt;에러 메시지 개선 (코드 유지)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;4가지-버전-전략&quot;&gt;4가지 버전 전략&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q{버전 표시 방법}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; URL[&quot;URL 버전&amp;#x3C;br/&gt;/v1/users&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; Header[&quot;Accept 헤더&amp;#x3C;br/&gt;application/vnd.api+json;version=2&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; Custom[&quot;커스텀 헤더&amp;#x3C;br/&gt;X-API-Version: 2&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; Date[&quot;날짜 버전&amp;#x3C;br/&gt;Stripe-Version: 2023-10-16&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;1-url-버전-v1&quot;&gt;1. URL 버전 (&lt;code&gt;/v1/...&lt;/code&gt;)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /v1/users/42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GET /v2/users/42&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;: 명시적, 브라우저 친화, 캐시 가능, debugging 쉬움.
&lt;strong&gt;단점&lt;/strong&gt;: URL 영구 변경. 자원이 &lt;em&gt;같은데 URL 다름&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;가장 흔한 방식. &lt;em&gt;시작점으로 권장&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2-accept-헤더&quot;&gt;2. Accept 헤더&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /users/42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Accept&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; application/vnd.example+json;version=2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;: URL 유지.
&lt;strong&gt;단점&lt;/strong&gt;: 디버깅 어려움 (&lt;code&gt;curl&lt;/code&gt; 시 헤더 잊음), 캐싱 복잡.&lt;/p&gt;
&lt;h3 id=&quot;3-커스텀-헤더&quot;&gt;3. 커스텀 헤더&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /users/42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;X-API-Version&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;: 단순.
&lt;strong&gt;단점&lt;/strong&gt;: 표준 아님.&lt;/p&gt;
&lt;h3 id=&quot;4-날짜-기반-stripe-방식&quot;&gt;4. 날짜 기반 (Stripe 방식)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /users/42&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;Stripe-Version&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 2023-10-16&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Stripe&lt;/em&gt; 는 &lt;em&gt;모든 변경에 발표 날짜&lt;/em&gt;. 클라이언트는 &lt;em&gt;고정 날짜로 pin&lt;/em&gt;. 새 버전으로 &lt;em&gt;명시적 upgrade&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[&quot;2023-10-16&amp;#x3C;br/&gt;(고객 1 pin)&quot;] --&gt; Implementation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B[2024-04-10] --&gt; Implementation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C[2025-01-15] --&gt; Implementation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D[&quot;2026-06-25&amp;#x3C;br/&gt;(최신)&quot;] --&gt; Implementation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Implementation[서버&amp;#x3C;br/&gt;다중 버전 동시 지원]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;방식&lt;/th&gt;&lt;th&gt;권장 사용처&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;URL&lt;/td&gt;&lt;td&gt;일반 SaaS API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Accept&lt;/td&gt;&lt;td&gt;RESTful 엄격 + 캐시 인프라 신뢰&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;커스텀 헤더&lt;/td&gt;&lt;td&gt;내부 / 단순&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;날짜 (Stripe 식)&lt;/td&gt;&lt;td&gt;결제 / 큰 SDK 생태계 / 세밀한 backward compat&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;deprecation-policy&quot;&gt;Deprecation Policy&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gantt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    title API v1 의 라이프사이클&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    dateFormat YYYY-MM-DD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section Active&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    v1 활성 + 지원       :a1, 2024-01-01, 2025-12-31&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section Deprecated&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Deprecation 경고     :crit, d1, 2025-06-01, 2026-06-01&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section Sunset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Sunset 일시 차단     :s1, 2026-06-01, 2026-12-31&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    완전 종료            :milestone, m1, 2026-12-31, 0d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;단계&lt;/th&gt;&lt;th&gt;의미&lt;/th&gt;&lt;th&gt;헤더&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Active&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;정상 사용&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Deprecated&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;새 사용 자제&lt;/td&gt;&lt;td&gt;&lt;code&gt;Sunset: &amp;#x3C;date&gt;&lt;/code&gt;, &lt;code&gt;Deprecation: &amp;#x3C;bool&gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Sunset (graceful)&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;사용 가능, 적극 경고&lt;/td&gt;&lt;td&gt;응답에 경고, 410 응답 일부 endpoint&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Removed&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;410 Gone&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
Sunset 까지 &lt;em&gt;최소 6-12개월&lt;/em&gt; 예고가 정통. &lt;em&gt;고객사가 단순 라이브러리만 따라잡으면 되도록&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;호환성-카테고리&quot;&gt;호환성 카테고리&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Forward[Forward Compatible&amp;#x3C;br/&gt;옛 클라이언트 → 새 응답 OK]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Backward[Backward Compatible&amp;#x3C;br/&gt;새 클라이언트 → 옛 서버 OK]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Both[양방향 호환]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Tip1[&quot;새 필드 추가 (옵션) → 양방향&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Tip2[&quot;필드 제거 → 양방향 깨짐&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Tip3[&quot;응답에 옵션 필드 → forward compatible&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Forward --&gt; Tip1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Forward --&gt; Tip3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Backward --&gt; Tip1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Both --&gt; Tip2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;클라이언트-라이브러리-정책&quot;&gt;클라이언트 라이브러리 정책&lt;/h2&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;정책&lt;/th&gt;&lt;th&gt;효과&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Unknown field 무시&lt;/em&gt;&lt;/td&gt;&lt;td&gt;forward compatible. &lt;em&gt;클라이언트 라이브러리의 기본&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;em&gt;Unknown field 에러&lt;/em&gt;&lt;/td&gt;&lt;td&gt;엄격하지만 &lt;em&gt;호환성 깨짐&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Optional 만 처리&lt;/td&gt;&lt;td&gt;가장 안전&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Protobuf, JSON 모두 &lt;em&gt;unknown field 무시&lt;/em&gt; 가 기본.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;버전 &lt;em&gt;없이&lt;/em&gt; 시작&lt;/strong&gt; = breaking change 강요. 처음부터 &lt;em&gt;v1&lt;/em&gt; 박기.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;POST&lt;/code&gt; 응답에 &lt;em&gt;enum 값 추가&lt;/em&gt;&lt;/strong&gt; = 클라이언트의 &lt;em&gt;switch case 폭발&lt;/em&gt;. &lt;em&gt;unknown case&lt;/em&gt; 처리 강제.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;deprecation &lt;em&gt;없이&lt;/em&gt; 종료&lt;/strong&gt; = 고객 폭동. 최소 6개월 전 알림.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;버전 수 &lt;em&gt;폭증&lt;/em&gt;&lt;/strong&gt; = 5개 동시 지원 = 코드 분기 폭발. &lt;em&gt;최대 2-3개&lt;/em&gt; 동시 지원 권장.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[OpenAPI Swagger]]&lt;/li&gt;
&lt;li&gt;[[Zero Downtime Deployment]]&lt;/li&gt;
&lt;li&gt;[[Idempotency Keys]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>api</category><category>versioning</category><category>backend</category><category>design</category><author>koa (김신건)</author></item><item><title>[API Design] GraphQL: 단일 endpoint, N+1, persisted queries</title><link>https://shinkeonkim.com/wiki/api-design/graphql/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/api-design/graphql/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GraphQL&lt;/strong&gt; (Facebook, 2015) 은 &lt;em&gt;클라이언트가 필요한 필드만 명시&lt;/em&gt; 하는 query language + 런타임. &lt;em&gt;단일 endpoint&lt;/em&gt;, &lt;em&gt;typed schema&lt;/em&gt;, &lt;em&gt;over-fetching / under-fetching 해소&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;rest-vs-graphql&quot;&gt;REST vs GraphQL&lt;/h2&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;REST&lt;/th&gt;&lt;th&gt;GraphQL&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Endpoint&lt;/td&gt;&lt;td&gt;여러 개&lt;/td&gt;&lt;td&gt;단일 (&lt;code&gt;/graphql&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;응답 형태&lt;/td&gt;&lt;td&gt;서버 결정&lt;/td&gt;&lt;td&gt;클라이언트 결정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Over-fetching&lt;/td&gt;&lt;td&gt;흔함&lt;/td&gt;&lt;td&gt;적음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Multiple resources&lt;/td&gt;&lt;td&gt;N 번 요청&lt;/td&gt;&lt;td&gt;1번 쿼리&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Type&lt;/td&gt;&lt;td&gt;옵션 (OpenAPI)&lt;/td&gt;&lt;td&gt;&lt;em&gt;필수 (SDL)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;캐싱&lt;/td&gt;&lt;td&gt;HTTP cache&lt;/td&gt;&lt;td&gt;응답 단위 (별도)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;모니터링&lt;/td&gt;&lt;td&gt;URL 별&lt;/td&gt;&lt;td&gt;쿼리 별 (복잡)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;학습 곡선&lt;/td&gt;&lt;td&gt;낮음&lt;/td&gt;&lt;td&gt;중간&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;schema-sdl&quot;&gt;Schema (SDL)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;graphql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;User&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;limit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Int&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;cursor&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;UserConnection&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Mutation&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  createUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;CreateUserInput&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  deleteUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Boolean&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; User&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  email&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  orders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;OrderStatus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;): [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Order&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;enum&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; OrderStatus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; PENDING PAID CANCELLED&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;query--response&quot;&gt;Query / Response&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;graphql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; GetUserWithOrders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;$id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;$id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    email&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    orders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; PAID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;      id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;      total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;      items&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;        product&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;        quantity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;user&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;koa&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;email&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;koa@x.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      &quot;orders&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;o_1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;total&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;19900&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;          &quot;items&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [{ &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Book&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;quantity&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;n1-문제-graphql-의-함정&quot;&gt;N+1 문제 (GraphQL 의 함정)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;graphql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;              # 1 query&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    orders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;           # 각 user 마다 별도 query → N+1!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;      total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;GraphQL: query users { orders }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    GraphQL-&gt;&gt;DB: SELECT * FROM users      (1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DB--&gt;&gt;GraphQL: 100 users&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loop 각 user&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        GraphQL-&gt;&gt;DB: SELECT * FROM orders WHERE user_id = ?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over GraphQL,DB: 1 + 100 = 101 queries (N+1!)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;해결-dataloader&quot;&gt;해결: DataLoader&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; orderLoader&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; DataLoader&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;userIds&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; orders&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; db.orders.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ user_id: { $in: userIds } });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; userIds.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;uid&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; orders.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; o.user_id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; uid));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; resolvers&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  User: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    orders&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; orderLoader.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(user.id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DataLoader 가 &lt;em&gt;같은 tick 의 모든 load() 호출을 모아 1 query 로&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    GraphQL-&gt;&gt;DataLoader: load(1), load(2), load(3), ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over DataLoader: 같은 tick 의 호출 모음&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DataLoader-&gt;&gt;DB: SELECT * FROM orders WHERE user_id IN (1,2,3,...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DB--&gt;&gt;DataLoader: 모든 orders&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DataLoader--&gt;&gt;GraphQL: per-user 결과 분배&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
DataLoader 는 &lt;em&gt;GraphQL 의 &lt;em&gt;필수&lt;/em&gt; 동반자&lt;/em&gt;. 없으면 거의 모든 nested query 가 &lt;em&gt;N+1&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;persisted-queries&quot;&gt;Persisted Queries&lt;/h2&gt;
&lt;p&gt;매 요청에 &lt;em&gt;전체 쿼리 문자열&lt;/em&gt; 보내면 &lt;em&gt;대역폭 + 보안 문제&lt;/em&gt; (악의적 deep nested query 로 DoS).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Persisted Query&lt;/strong&gt;: &lt;em&gt;쿼리를 미리 등록 + 해시로 호출&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Build[빌드 시] --&gt;|쿼리 → 해시| Reg[(서버에 등록)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client[클라이언트] --&gt;|hash 만 전송| Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt;|hash → 쿼리 조회| Cache[(query cache)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Cache --&gt; Exec[실행]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;대역폭 &lt;em&gt;대폭 감소&lt;/em&gt; (긴 쿼리 → 32바이트 해시).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;whitelisted query&lt;/em&gt; 만 실행 → &lt;em&gt;보안 강화&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Apollo Persisted Queries / Relay Compiler 가 자동.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;subscription-실시간&quot;&gt;Subscription (실시간)&lt;/h2&gt;
&lt;p&gt;WebSocket 또는 SSE 위에서.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;graphql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;subscription&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; OnNewMessage&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;$roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  messageAdded&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;$roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    body&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;    sender&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;내부적으로 &lt;em&gt;WebSocket (graphql-ws)&lt;/em&gt; 또는 &lt;em&gt;SSE&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;federation-단일-graph-다중-서비스&quot;&gt;Federation (단일 graph, 다중 서비스)&lt;/h2&gt;
&lt;p&gt;여러 서비스가 &lt;em&gt;각자 schema 일부&lt;/em&gt; 를 담당. &lt;em&gt;gateway 가 통합 schema&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client --&gt;|query| Gateway[Apollo Router]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gateway --&gt;|user subgraph| UserSvc[User Service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gateway --&gt;|order subgraph| OrderSvc[Order Service]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gateway --&gt;|payment subgraph| PaySvc[Payment Service]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마이크로서비스 환경의 &lt;em&gt;GraphQL 표준 패턴&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;캐싱&quot;&gt;캐싱&lt;/h2&gt;
&lt;p&gt;GraphQL 은 &lt;em&gt;HTTP cache 가 어려움&lt;/em&gt; (POST + body):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;응답 캐시&lt;/strong&gt;: 같은 query + 변수 → 같은 응답. CDN level 어렵, &lt;em&gt;Apollo Cache&lt;/em&gt; / &lt;em&gt;@cacheControl&lt;/em&gt; 디렉티브.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DataLoader&lt;/strong&gt;: per-request 캐싱.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persisted Query + GET&lt;/strong&gt; = &lt;em&gt;URL 캐시 가능&lt;/em&gt;. CDN 친화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;깊이--복잡도-제한-보안&quot;&gt;깊이 / 복잡도 제한 (보안)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; server&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ApolloServer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  schema,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  validationRules: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    depthLimit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    createComplexityLimitRule&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
클라이언트가 &lt;em&gt;극단적 nested query&lt;/em&gt; (&lt;code&gt;user { friends { friends { friends { ... } } } }&lt;/code&gt;) 를 보내면 &lt;em&gt;서버 DoS&lt;/em&gt;. &lt;em&gt;depth + complexity 제한&lt;/em&gt; 필수.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DataLoader 미사용&lt;/strong&gt; = 모든 nested 필드 N+1. &lt;em&gt;처음부터 도입&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Depth limit 없음&lt;/strong&gt; = DoS 공격.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 필드 &lt;em&gt;nullable&lt;/em&gt;&lt;/strong&gt; = 응답 전체가 &lt;em&gt;null union&lt;/em&gt; 형태. 명확한 nullability 명시.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP cache 의 &lt;em&gt;부재&lt;/em&gt;&lt;/strong&gt; = GraphQL 의 큰 단점. 무거운 쿼리는 &lt;em&gt;외부 cache&lt;/em&gt; + &lt;em&gt;persisted query&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[gRPC]]&lt;/li&gt;
&lt;li&gt;[[n-plus-one-problem]]&lt;/li&gt;
&lt;li&gt;[[Redis Cache Patterns]] (응답 캐싱)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>graphql</category><category>api-design</category><category>backend</category><author>koa (김신건)</author></item><item><title>[API Design] JSON-RPC vs gRPC vs REST: RPC 패턴 비교</title><link>https://shinkeonkim.com/wiki/api-design/jsonrpc-vs-grpc/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/api-design/jsonrpc-vs-grpc/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;RPC (Remote Procedure Call)&lt;/strong&gt; = &lt;em&gt;원격 함수 호출&lt;/em&gt; 추상화. 세 가지 주요 형태:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSON-RPC&lt;/strong&gt;: JSON 위 RPC. 가벼움.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gRPC&lt;/strong&gt;: Protobuf + HTTP/2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REST&lt;/strong&gt; (RPC 스타일): &lt;code&gt;POST /api/getUser&lt;/code&gt; 같이 동사 endpoint.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;json-rpc-20-메시지&quot;&gt;JSON-RPC 2.0 메시지&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 요청&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;getUser&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;params&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 성공 응답&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;result&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;koa&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 에러 응답&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;code&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;-32601&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;message&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Method not found&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 알림 (응답 없음)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;log&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;params&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;user logged in&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Batch (배열)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;jsonrpc&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;표준 에러 코드&lt;/th&gt;&lt;th&gt;의미&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;-32700&lt;/td&gt;&lt;td&gt;Parse error&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-32600&lt;/td&gt;&lt;td&gt;Invalid request&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-32601&lt;/td&gt;&lt;td&gt;Method not found&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-32602&lt;/td&gt;&lt;td&gt;Invalid params&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-32603&lt;/td&gt;&lt;td&gt;Internal error&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-32000 ~ -32099&lt;/td&gt;&lt;td&gt;server error&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;세-가지-rpc-비교&quot;&gt;세 가지 RPC 비교&lt;/h2&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;JSON-RPC&lt;/th&gt;&lt;th&gt;gRPC&lt;/th&gt;&lt;th&gt;REST (RPC 스타일)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Wire 포맷&lt;/td&gt;&lt;td&gt;JSON&lt;/td&gt;&lt;td&gt;Protobuf&lt;/td&gt;&lt;td&gt;JSON&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;전송&lt;/td&gt;&lt;td&gt;HTTP/WS/TCP&lt;/td&gt;&lt;td&gt;HTTP/2&lt;/td&gt;&lt;td&gt;HTTP/1.1+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Schema&lt;/td&gt;&lt;td&gt;옵션&lt;/td&gt;&lt;td&gt;&lt;em&gt;필수 (proto)&lt;/em&gt;&lt;/td&gt;&lt;td&gt;OpenAPI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Streaming&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;td&gt;&lt;em&gt;4 모드&lt;/em&gt;&lt;/td&gt;&lt;td&gt;SSE/WS 별도&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;코드 생성&lt;/td&gt;&lt;td&gt;옵션&lt;/td&gt;&lt;td&gt;&lt;em&gt;자동&lt;/em&gt;&lt;/td&gt;&lt;td&gt;OpenAPI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;크기 / 속도&lt;/td&gt;&lt;td&gt;JSON 만큼&lt;/td&gt;&lt;td&gt;&lt;em&gt;수배 작고 빠름&lt;/em&gt;&lt;/td&gt;&lt;td&gt;JSON&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;브라우저&lt;/td&gt;&lt;td&gt;직접&lt;/td&gt;&lt;td&gt;grpc-web 필요&lt;/td&gt;&lt;td&gt;&lt;em&gt;직접&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Batch&lt;/td&gt;&lt;td&gt;&lt;em&gt;기본 지원&lt;/em&gt;&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;json-rpc-의-강점&quot;&gt;JSON-RPC 의 강점&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[가벼운 spec&amp;#x3C;br/&gt;1 페이지] --&gt; Use1[블록체인 노드&amp;#x3C;br/&gt;예: Ethereum]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A --&gt; Use2[IDE / 에디터 ↔ Language Server&amp;#x3C;br/&gt;예: LSP]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A --&gt; Use3[VSCode extension API]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A --&gt; Use4[debugger DAP]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;표준&lt;/th&gt;&lt;th&gt;RPC 토대&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LSP&lt;/strong&gt; (Language Server Protocol)&lt;/td&gt;&lt;td&gt;JSON-RPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DAP&lt;/strong&gt; (Debug Adapter Protocol)&lt;/td&gt;&lt;td&gt;JSON-RPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol)&lt;/td&gt;&lt;td&gt;JSON-RPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ethereum JSON-RPC&lt;/td&gt;&lt;td&gt;JSON-RPC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bitcoin RPC&lt;/td&gt;&lt;td&gt;JSON-RPC&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
JSON-RPC 는 &lt;em&gt;VSCode / LSP / MCP / 블록체인&lt;/em&gt; 의 &lt;em&gt;de facto&lt;/em&gt;. &lt;em&gt;복잡도 vs gRPC&lt;/em&gt; 의 sweet spot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;언제-어떤-rpc&quot;&gt;언제 어떤 RPC?&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1{브라우저 직접 호출?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|예| Q2{내부 API?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|&quot;아니오 (외부)&quot;| REST[REST]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| Q3{스트리밍 / typed?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|typed 강력 필요| RestOpenAPI[REST + OpenAPI]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|아니오| REST&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|&quot;아니오 (server-to-server)&quot;| Q4{성능 critical?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|예| GRPC[gRPC]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|아니오| Q5{spec 단순함 중요?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q5 --&gt;|예| JsonRpc[JSON-RPC]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q5 --&gt;|아니오| GRPC&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;json-rpc-의-함정&quot;&gt;JSON-RPC 의 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;method 이름 충돌&lt;/strong&gt; = namespace 없음. 큰 시스템에서 &lt;em&gt;접두사&lt;/em&gt; (&lt;code&gt;user.get&lt;/code&gt;, &lt;code&gt;order.create&lt;/code&gt;) 필요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP status code 무관&lt;/strong&gt; = JSON-RPC 는 &lt;em&gt;항상 200&lt;/em&gt;. error 는 &lt;em&gt;body 의 error 객체&lt;/em&gt;. 모니터링 도구가 &lt;em&gt;에러 못 잡음&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch + notification 의 응답 매핑&lt;/strong&gt; = id 기반 매핑이 복잡. 잘못하면 응답이 &lt;em&gt;섞임&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;버전 관리 없음&lt;/strong&gt; = &lt;em&gt;spec 자체에 버전 없음&lt;/em&gt;. 직접 method 이름에 &lt;em&gt;v2&lt;/em&gt; 박는 식.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[gRPC]]&lt;/li&gt;
&lt;li&gt;[[GraphQL]]&lt;/li&gt;
&lt;li&gt;[[OpenAPI Swagger]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>rpc</category><category>json-rpc</category><category>grpc</category><category>rest</category><category>api-design</category><author>koa (김신건)</author></item><item><title>[API Design] OpenAPI / Swagger: 스펙, 코드 생성, contract testing</title><link>https://shinkeonkim.com/wiki/api-design/openapi-swagger/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/api-design/openapi-swagger/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;OpenAPI Specification (OAS)&lt;/strong&gt; 은 &lt;em&gt;REST API 를 기술하는 YAML/JSON 형식&lt;/em&gt;. 옛 이름 &lt;em&gt;Swagger&lt;/em&gt;. 2017 부터 &lt;em&gt;OpenAPI Initiative&lt;/em&gt; (Linux Foundation) 관리.&lt;/p&gt;
&lt;p&gt;핵심 가치:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사람이 읽는 &lt;em&gt;문서&lt;/em&gt; (Swagger UI, Redoc)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;클라이언트 SDK 자동 생성&lt;/em&gt; (다국어)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;서버 stub 생성&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;contract testing&lt;/em&gt; (스펙 ↔ 실제 응답 검증)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;mock server&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;기본-구조&quot;&gt;기본 구조&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;openapi&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3.1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;info&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Shop API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;servers&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;https://api.example.com/v1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;paths&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  /items/{id}&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      summary&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Get item by ID&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      parameters&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          required&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          schema&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;            type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      responses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;200&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          content&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;            application/json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;              schema&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;                $ref&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#/components/schemas/Item&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        &apos;404&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          description&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Not found&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  schemas&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    Item&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;object&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      required&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;price&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      properties&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        price&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;integer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          minimum&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;워크플로-api-first&quot;&gt;워크플로 (API-first)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Design[스펙 작성&amp;#x3C;br/&gt;openapi.yaml] --&gt; Review[리뷰]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Review --&gt; Gen[코드 생성]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gen --&gt; Server[서버 stub]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gen --&gt; Client[클라이언트 SDK]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gen --&gt; Mock[Mock server]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Gen --&gt; Docs[문서 UI]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt; Impl[실제 구현]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Impl --&gt; Test[contract test]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Test --&gt;|스펙 위반| Design&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;openapi-31-의-주요-기능&quot;&gt;OpenAPI 3.1 의 주요 기능&lt;/h2&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;기능&lt;/th&gt;&lt;th&gt;의미&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;$ref&lt;/code&gt;&lt;/td&gt;&lt;td&gt;정의 재사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;components.schemas&lt;/code&gt;&lt;/td&gt;&lt;td&gt;모델 분리&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;components.parameters&lt;/code&gt;&lt;/td&gt;&lt;td&gt;공통 파라미터&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;components.responses&lt;/code&gt;&lt;/td&gt;&lt;td&gt;공통 응답&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;components.securitySchemes&lt;/code&gt;&lt;/td&gt;&lt;td&gt;인증 방식&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;oneOf&lt;/code&gt; / &lt;code&gt;anyOf&lt;/code&gt; / &lt;code&gt;allOf&lt;/code&gt;&lt;/td&gt;&lt;td&gt;다형성&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;discriminator&lt;/code&gt;&lt;/td&gt;&lt;td&gt;type 필드로 분기&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;links&lt;/code&gt;&lt;/td&gt;&lt;td&gt;HATEOAS 표현&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Webhooks (3.1+)&lt;/td&gt;&lt;td&gt;비동기 콜백&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;인증-표현&quot;&gt;인증 표현&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;components&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  securitySchemes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    bearerAuth&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;http&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      scheme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;bearer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      bearerFormat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;JWT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    apiKeyAuth&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;apiKey&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;header&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;X-API-Key&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    oauth2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;oauth2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      flows&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        authorizationCode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          authorizationUrl&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;https://example.com/oauth/authorize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          tokenUrl&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;https://example.com/oauth/token&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          scopes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;            read:items&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Read items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;            write:items&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Modify items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;security&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;bearerAuth&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: []&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;코드-생성-openapi-generator&quot;&gt;코드 생성 (openapi-generator)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# TypeScript axios 클라이언트&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openapi-generator-cli&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; generate&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; openapi.yaml&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -g&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; typescript-axios&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./generated/client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Spring Boot 서버 stub&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;openapi-generator-cli&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; generate&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; openapi.yaml&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -g&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; spring&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./generated/server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;지원 언어 &lt;em&gt;수십 가지&lt;/em&gt;. 모든 &lt;em&gt;주요 stack&lt;/em&gt; 의 &lt;em&gt;클라이언트 SDK&lt;/em&gt; 가 자동.&lt;/p&gt;
&lt;h2 id=&quot;contract-testing&quot;&gt;Contract Testing&lt;/h2&gt;
&lt;p&gt;스펙과 &lt;em&gt;실제 응답&lt;/em&gt; 이 일치하는지 검증:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Spec[openapi.yaml] --&gt;|검증| Validator&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server --&gt;|실제 응답| Validator&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Validator --&gt;|불일치| Fail[테스트 실패]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Validator --&gt;|일치| Pass[테스트 통과]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;도구: &lt;a href=&quot;https://schemathesis.readthedocs.io/&quot;&gt;Schemathesis&lt;/a&gt;, &lt;a href=&quot;https://dredd.org/&quot;&gt;Dredd&lt;/a&gt;, &lt;a href=&quot;https://meta.stoplight.io/docs/prism/&quot;&gt;Prism&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Schemathesis 로 spec 기반 fuzzing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;schemathesis&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; openapi.yaml&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --url=https://api.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;spec 의 &lt;em&gt;모든 endpoint + 가능한 입력 조합&lt;/em&gt; 자동 생성 + 응답 검증.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;문서-ui&quot;&gt;문서 UI&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;도구&lt;/th&gt;&lt;th&gt;특징&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Swagger UI&lt;/td&gt;&lt;td&gt;표준, &lt;em&gt;try it out&lt;/em&gt; 패널&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Redoc&lt;/td&gt;&lt;td&gt;세련된 정적 문서, 사이드 nav&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Scalar&lt;/td&gt;&lt;td&gt;신예, dark mode + 빠름&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Stoplight Elements&lt;/td&gt;&lt;td&gt;임베드 친화&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;openapi-의-한계&quot;&gt;OpenAPI 의 한계&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Strong[잘 표현되는 것] --&gt; CRUD[CRUD REST]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Strong --&gt; Webhook[webhook 페이로드]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Strong --&gt; Type[type-safe 모델]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Weak[표현 약한 것] --&gt; StateMachine[복잡한 상태 머신]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Weak --&gt; Realtime[&quot;실시간 (WS, SSE)&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Weak --&gt; BinaryProto[바이너리 프로토콜]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Weak --&gt; Workflow[multi-step workflow]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;gRPC&lt;/em&gt; 는 &lt;em&gt;proto&lt;/em&gt;, &lt;em&gt;GraphQL&lt;/em&gt; 은 &lt;em&gt;SDL&lt;/em&gt;, &lt;em&gt;AsyncAPI&lt;/em&gt; 는 &lt;em&gt;비동기 / 이벤트&lt;/em&gt; 의 OpenAPI 등가체.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;스펙과 &lt;em&gt;실제 응답&lt;/em&gt; 의 표류&lt;/strong&gt; = contract test 없으면 &lt;em&gt;문서가 거짓말&lt;/em&gt;. CI 에 포함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 응답을 &lt;em&gt;200 OK&lt;/em&gt; 로 spec&lt;/strong&gt; = 4xx/5xx 도 &lt;em&gt;명시&lt;/em&gt;. 클라이언트 SDK 의 error type 이 정확해짐.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;oneOf&lt;/code&gt; 의 &lt;em&gt;없는 discriminator&lt;/em&gt;&lt;/strong&gt; = 클라이언트가 &lt;em&gt;어떤 타입인지 구분 못함&lt;/em&gt;. 항상 discriminator 명시.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;너무 늦게 spec 작성&lt;/strong&gt; = &lt;em&gt;코드부터 작성 후 spec&lt;/em&gt; 은 &lt;em&gt;retrospective spec&lt;/em&gt; 으로 &lt;em&gt;문서 정확도 떨어짐&lt;/em&gt;. &lt;em&gt;API-first&lt;/em&gt; 가 정통.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[REST API Design]]&lt;/li&gt;
&lt;li&gt;[[gRPC]] (proto 대안)&lt;/li&gt;
&lt;li&gt;[[GraphQL]] (SDL 대안)&lt;/li&gt;
&lt;li&gt;[[JWT]] (인증 표현)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>openapi</category><category>swagger</category><category>api-design</category><category>backend</category><category>documentation</category><author>koa (김신건)</author></item><item><title>[API Design] REST API: 원칙, 자원 모델링, HATEOAS, 버전 관리</title><link>https://shinkeonkim.com/wiki/api-design/rest-api-design/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/api-design/rest-api-design/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;REST (Representational State Transfer)&lt;/strong&gt; 는 Roy Fielding 의 2000년 박사논문에서 정립된 &lt;em&gt;분산 시스템 아키텍처 스타일&lt;/em&gt;. HTTP 의 &lt;em&gt;자원 / 표현 / 상태 전이&lt;/em&gt; 를 자연스럽게 활용.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
“REST” 라는 단어는 &lt;em&gt;대부분 실무에서 &lt;em&gt;HTTP+JSON CRUD&lt;/em&gt; 를 의미&lt;/em&gt;. Fielding 의 &lt;em&gt;순수 REST&lt;/em&gt; (HATEOAS 포함) 는 &lt;em&gt;드물게&lt;/em&gt; 적용. 이 페이지는 &lt;em&gt;실무 REST&lt;/em&gt; 와 &lt;em&gt;진짜 REST&lt;/em&gt; 둘 다 정리.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;richardson-maturity-model-rest-의-4-단계&quot;&gt;Richardson Maturity Model (REST 의 4 단계)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L0[Level 0: 단일 endpoint&amp;#x3C;br/&gt;POST /api 로 모든 작업] --&gt; L1[Level 1: 자원 분리&amp;#x3C;br/&gt;GET /users, /orders]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L1 --&gt; L2[Level 2: HTTP method + status&amp;#x3C;br/&gt;GET/POST/PUT/DELETE]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    L2 --&gt; L3[Level 3: HATEOAS&amp;#x3C;br/&gt;응답에 next action 링크]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;실무 대부분은 Level 2 에 머문다&lt;/em&gt;. Level 3 은 &lt;em&gt;Stripe, GitHub API 의 일부&lt;/em&gt; 정도.&lt;/p&gt;
&lt;h2 id=&quot;자원-모델링&quot;&gt;자원 모델링&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;좋은 패턴&lt;/th&gt;&lt;th&gt;나쁜 패턴&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GET /users/42&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;GET /getUser?id=42&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST /users&lt;/code&gt; (생성)&lt;/td&gt;&lt;td&gt;&lt;code&gt;POST /createUser&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DELETE /users/42&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;POST /deleteUser&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GET /users/42/orders&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;GET /userOrders?uid=42&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST /users/42/email/verify&lt;/code&gt; (action)&lt;/td&gt;&lt;td&gt;&lt;code&gt;GET /verifyUserEmail?uid=42&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;규칙:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;명사로 자원 표현&lt;/em&gt; (&lt;code&gt;users&lt;/code&gt;, &lt;code&gt;orders&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;복수형&lt;/em&gt; 권장 (&lt;code&gt;users&lt;/code&gt; not &lt;code&gt;user&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;상태 변경&lt;/em&gt; 은 HTTP method 로&lt;/li&gt;
&lt;li&gt;&lt;em&gt;체이닝&lt;/em&gt; 자원으로 &lt;em&gt;관계 표현&lt;/em&gt; (&lt;code&gt;/users/42/orders/9&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;비-CRUD action&lt;/em&gt; 은 &lt;em&gt;복합 명사&lt;/em&gt; (&lt;code&gt;/orders/9/cancel&lt;/code&gt;, &lt;code&gt;/payments/p_123/refund&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;http-method-매핑&quot;&gt;HTTP Method 매핑&lt;/h2&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Method&lt;/th&gt;&lt;th&gt;멱등&lt;/th&gt;&lt;th&gt;안전&lt;/th&gt;&lt;th&gt;사용&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;조회&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HEAD&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;헤더만&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;POST&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;생성, 비-멱등 action&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PUT&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;전체 교체&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PATCH&lt;/td&gt;&lt;td&gt;X (대개)&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;부분 갱신&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DELETE&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;삭제&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;em&gt;PUT&lt;/em&gt; 은 &lt;em&gt;idempotent&lt;/em&gt;. 두 번 호출 = 한 번 호출 결과 같음. &lt;em&gt;POST&lt;/em&gt; 는 &lt;em&gt;매번 새 자원 생성&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;status-code-패턴&quot;&gt;Status Code 패턴&lt;/h2&gt;





















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;코드&lt;/th&gt;&lt;th&gt;사용&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;200&lt;/td&gt;&lt;td&gt;성공 (GET, PUT, PATCH)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;201&lt;/td&gt;&lt;td&gt;생성 (POST). &lt;code&gt;Location&lt;/code&gt; 헤더로 새 자원 URL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;202&lt;/td&gt;&lt;td&gt;비동기 접수&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;204&lt;/td&gt;&lt;td&gt;본문 없음 성공 (DELETE)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;400&lt;/td&gt;&lt;td&gt;입력 형식 / 필수 누락&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;401&lt;/td&gt;&lt;td&gt;인증 필요&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;403&lt;/td&gt;&lt;td&gt;인증 됐지만 권한 없음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;404&lt;/td&gt;&lt;td&gt;자원 없음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;409&lt;/td&gt;&lt;td&gt;충돌 (중복, 동시성)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;422&lt;/td&gt;&lt;td&gt;형식은 OK, 의미 검증 실패&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;429&lt;/td&gt;&lt;td&gt;rate limit 초과&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;api-버전-관리&quot;&gt;API 버전 관리&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q{버전 표시 방법}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; URL[&quot;URL: /v1/users&amp;#x3C;br/&gt;/v2/users&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; Header[&quot;헤더: Accept: application/vnd.api+json;v=2&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt; Query[&quot;Query: /users?api-version=2&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;방식&lt;/th&gt;&lt;th&gt;장점&lt;/th&gt;&lt;th&gt;단점&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;URL (&lt;code&gt;/v1/&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;명시적, 캐싱 친화&lt;/td&gt;&lt;td&gt;URL 영구 변경&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Accept 헤더&lt;/td&gt;&lt;td&gt;URL 깔끔&lt;/td&gt;&lt;td&gt;디버깅 / 캐시 어려움&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Query&lt;/td&gt;&lt;td&gt;단순&lt;/td&gt;&lt;td&gt;URL 변형&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Stripe&lt;/em&gt; 는 &lt;em&gt;날짜 기반 버전&lt;/em&gt; (&lt;code&gt;Stripe-Version: 2023-10-16&lt;/code&gt;). 가장 &lt;em&gt;세밀하고 안전한 호환성&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;페이지네이션&quot;&gt;페이지네이션&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[Offset/Limit&amp;#x3C;br/&gt;?offset=20&amp;#x26;limit=10] --&gt; B[페이지 N 빠른 점프 가능]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C[Cursor&amp;#x3C;br/&gt;?cursor=abc123] --&gt; D[연속 스트림, 정확]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    E[Page/Size&amp;#x3C;br/&gt;?page=3&amp;#x26;size=10] --&gt; F[옛 패턴]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;방식&lt;/th&gt;&lt;th&gt;장점&lt;/th&gt;&lt;th&gt;단점&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Offset&lt;/td&gt;&lt;td&gt;임의 점프&lt;/td&gt;&lt;td&gt;큰 offset 시 &lt;em&gt;O(N) DB query&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cursor&lt;/td&gt;&lt;td&gt;빠름, 정확&lt;/td&gt;&lt;td&gt;임의 점프 불가&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Keyset&lt;/td&gt;&lt;td&gt;offset + cursor 의 장점&lt;/td&gt;&lt;td&gt;구현 복잡&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;em&gt;대량 데이터 + 무한 스크롤&lt;/em&gt; = 거의 항상 &lt;em&gt;cursor 기반&lt;/em&gt;. &lt;em&gt;Stripe, GitHub, Twitter API&lt;/em&gt; 모두.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;응답-포맷-envelope&quot;&gt;응답 포맷 (envelope)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;meta&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;total&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1024&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;page&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;per_page&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;links&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;self&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;next&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;...&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;prev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또는 &lt;em&gt;납작&lt;/em&gt; (JSON:API 스펙):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [{ &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;users&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#FDAEB7;font-style:italic&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } }]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
envelope 의 &lt;em&gt;너무 깊은 nesting&lt;/em&gt; 은 &lt;em&gt;클라이언트 코드 복잡&lt;/em&gt;. &lt;em&gt;flat + 표준 (RFC 7807 problem details for errors)&lt;/em&gt; 권장.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;에러-응답-rfc-7807&quot;&gt;에러 응답 (RFC 7807)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://example.com/probs/validation&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Validation failed&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;422&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;detail&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Email is required&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;instance&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/requests/12345&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;errors&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;REQUIRED&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;hateoas&quot;&gt;HATEOAS&lt;/h2&gt;
&lt;p&gt;응답에 &lt;em&gt;다음 가능 action 의 링크&lt;/em&gt; 포함:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;o_123&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;status&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;links&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;self&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/orders/o_123&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;cancel&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/orders/o_123/cancel&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;pay&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/orders/o_123/pay&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
클라이언트가 &lt;em&gt;URL 을 미리 알 필요 없이&lt;/em&gt; 응답의 링크만 따라 가는 &lt;em&gt;진짜 REST&lt;/em&gt;. 실무에서는 &lt;em&gt;드물지만 GitHub API 가 좋은 예&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;/users/getById&lt;/code&gt; 같은 &lt;em&gt;동사 URL&lt;/em&gt;&lt;/strong&gt; = REST 의 &lt;em&gt;자원 사상&lt;/em&gt; 위반.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 변경에 &lt;code&gt;PUT&lt;/code&gt;&lt;/strong&gt; = PATCH 가 적절한 부분 갱신에도 PUT 사용 → race condition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;POST&lt;/code&gt; 의 &lt;em&gt;비-멱등&lt;/em&gt; 위험&lt;/strong&gt; = 네트워크 재시도 시 &lt;em&gt;중복 생성&lt;/em&gt;. &lt;em&gt;Idempotency-Key&lt;/em&gt; 헤더로 해결.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;버전 &lt;em&gt;없이&lt;/em&gt; 시작&lt;/strong&gt; = breaking change 시 &lt;em&gt;모든 클라이언트 동시 마이그레이션&lt;/em&gt; 강요.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[GraphQL]] (대안)&lt;/li&gt;
&lt;li&gt;[[gRPC]] (RPC)&lt;/li&gt;
&lt;li&gt;[[OpenAPI Swagger]]&lt;/li&gt;
&lt;li&gt;[[Idempotency Keys]]&lt;/li&gt;
&lt;li&gt;[[HTTP/1.1]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>rest</category><category>api-design</category><category>backend</category><category>http</category><author>koa (김신건)</author></item></channel></rss>