@wooojin/forgen 0.4.8 โ†’ 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/assets/dev-guide/be/README.md +226 -0
  3. package/assets/dev-guide/be/adapters/build-agents-md.sh +63 -0
  4. package/assets/dev-guide/be/principles/common.md +433 -0
  5. package/assets/dev-guide/be/principles/go.md +469 -0
  6. package/assets/dev-guide/be/principles/node.md +388 -0
  7. package/assets/dev-guide/be/skills/go/be-build/SKILL.md +262 -0
  8. package/assets/dev-guide/be/skills/go/be-perf/SKILL.md +308 -0
  9. package/assets/dev-guide/be/skills/go/be-review/SKILL.md +119 -0
  10. package/assets/dev-guide/be/skills/go/be-security/SKILL.md +362 -0
  11. package/assets/dev-guide/be/skills/node/be-build/SKILL.md +239 -0
  12. package/assets/dev-guide/be/skills/node/be-perf/SKILL.md +272 -0
  13. package/assets/dev-guide/be/skills/node/be-review/SKILL.md +118 -0
  14. package/assets/dev-guide/be/skills/node/be-security/SKILL.md +355 -0
  15. package/assets/dev-guide/be/sources/12factor/INDEX.md +53 -0
  16. package/assets/dev-guide/be/sources/api-design/INDEX.md +56 -0
  17. package/assets/dev-guide/be/sources/ddia/INDEX.md +55 -0
  18. package/assets/dev-guide/be/sources/go-runtime/INDEX.md +62 -0
  19. package/assets/dev-guide/be/sources/node-runtime/INDEX.md +60 -0
  20. package/assets/dev-guide/be/sources/otel/INDEX.md +53 -0
  21. package/assets/dev-guide/be/sources/owasp-api/INDEX.md +52 -0
  22. package/assets/dev-guide/be/sources/postgres/INDEX.md +55 -0
  23. package/assets/dev-guide/be/sources/sre-book/INDEX.md +48 -0
  24. package/assets/dev-guide/fe/README.md +197 -0
  25. package/assets/dev-guide/fe/adapters/build-agents-md.sh +63 -0
  26. package/assets/dev-guide/fe/adapters/refresh.sh +68 -0
  27. package/assets/dev-guide/fe/principles/common.md +160 -0
  28. package/assets/dev-guide/fe/principles/react.md +183 -0
  29. package/assets/dev-guide/fe/principles/vue.md +196 -0
  30. package/assets/dev-guide/fe/skills/react/fe-build/SKILL.md +139 -0
  31. package/assets/dev-guide/fe/skills/react/fe-perf/SKILL.md +179 -0
  32. package/assets/dev-guide/fe/skills/react/fe-review/SKILL.md +141 -0
  33. package/assets/dev-guide/fe/skills/vue/fe-build/SKILL.md +148 -0
  34. package/assets/dev-guide/fe/skills/vue/fe-perf/SKILL.md +163 -0
  35. package/assets/dev-guide/fe/skills/vue/fe-review/SKILL.md +136 -0
  36. package/assets/dev-guide/fe/sources/a11y-dx/INDEX.md +41 -0
  37. package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-memory.md +150 -0
  38. package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-performance.md +99 -0
  39. package/assets/dev-guide/fe/sources/a11y-dx/lighthouse-audits.md +146 -0
  40. package/assets/dev-guide/fe/sources/a11y-dx/react-devtools-profiler.md +128 -0
  41. package/assets/dev-guide/fe/sources/a11y-dx/wcag22-new-criteria.md +174 -0
  42. package/assets/dev-guide/fe/sources/perf/01-core-web-vitals.md +58 -0
  43. package/assets/dev-guide/fe/sources/perf/02-inp.md +83 -0
  44. package/assets/dev-guide/fe/sources/perf/03-lcp-cls.md +130 -0
  45. package/assets/dev-guide/fe/sources/perf/04-speculation-rules.md +148 -0
  46. package/assets/dev-guide/fe/sources/perf/05-view-transitions.md +153 -0
  47. package/assets/dev-guide/fe/sources/perf/06-nextjs-caching.md +188 -0
  48. package/assets/dev-guide/fe/sources/perf/07-server-components.md +181 -0
  49. package/assets/dev-guide/fe/sources/perf/08-ppr.md +133 -0
  50. package/assets/dev-guide/fe/sources/perf/09-nextjs-image.md +200 -0
  51. package/assets/dev-guide/fe/sources/perf/10-optimize-lcp.md +201 -0
  52. package/assets/dev-guide/fe/sources/perf/INDEX.md +88 -0
  53. package/assets/dev-guide/fe/sources/react/INDEX.md +41 -0
  54. package/assets/dev-guide/fe/sources/react/keeping-components-pure.md +135 -0
  55. package/assets/dev-guide/fe/sources/react/no-effect-patterns.md +183 -0
  56. package/assets/dev-guide/fe/sources/react/react-compiler.md +182 -0
  57. package/assets/dev-guide/fe/sources/react/server-components.md +194 -0
  58. package/assets/dev-guide/fe/sources/react/server-functions.md +192 -0
  59. package/assets/dev-guide/fe/sources/react/suspense.md +218 -0
  60. package/assets/dev-guide/fe/sources/react/use-action-state.md +123 -0
  61. package/assets/dev-guide/fe/sources/react/use-form-status.md +158 -0
  62. package/assets/dev-guide/fe/sources/react/use-hook.md +153 -0
  63. package/assets/dev-guide/fe/sources/react/use-optimistic.md +194 -0
  64. package/assets/dev-guide/fe/sources/toss-ff/INDEX.md +58 -0
  65. package/assets/dev-guide/fe/sources/toss-ff/cohesion-code-directory.md +79 -0
  66. package/assets/dev-guide/fe/sources/toss-ff/cohesion-form-fields.md +110 -0
  67. package/assets/dev-guide/fe/sources/toss-ff/cohesion-magic-number.md +47 -0
  68. package/assets/dev-guide/fe/sources/toss-ff/coupling-item-edit-modal.md +124 -0
  69. package/assets/dev-guide/fe/sources/toss-ff/coupling-use-bottom-sheet.md +57 -0
  70. package/assets/dev-guide/fe/sources/toss-ff/coupling-use-page-state.md +71 -0
  71. package/assets/dev-guide/fe/sources/toss-ff/overview-4-principles.md +77 -0
  72. package/assets/dev-guide/fe/sources/toss-ff/predictability-hidden-logic.md +59 -0
  73. package/assets/dev-guide/fe/sources/toss-ff/predictability-http.md +77 -0
  74. package/assets/dev-guide/fe/sources/toss-ff/predictability-use-user.md +110 -0
  75. package/assets/dev-guide/fe/sources/toss-ff/readability-comparison-order.md +52 -0
  76. package/assets/dev-guide/fe/sources/toss-ff/readability-condition-name.md +64 -0
  77. package/assets/dev-guide/fe/sources/toss-ff/readability-login-start-page.md +183 -0
  78. package/assets/dev-guide/fe/sources/toss-ff/readability-magic-number.md +53 -0
  79. package/assets/dev-guide/fe/sources/toss-ff/readability-submit-button.md +73 -0
  80. package/assets/dev-guide/fe/sources/toss-ff/readability-ternary-operator.md +38 -0
  81. package/assets/dev-guide/fe/sources/toss-ff/readability-use-page-state.md +77 -0
  82. package/assets/dev-guide/fe/sources/toss-ff/readability-user-policy.md +98 -0
  83. package/assets/dev-guide/fe/sources/vue/INDEX.md +17 -0
  84. package/assets/dev-guide/fe/sources/vue/composition-api.md +251 -0
  85. package/assets/dev-guide/fe/sources/vue/nuxt-data-fetching.md +232 -0
  86. package/assets/dev-guide/fe/sources/vue/pinia-state-management.md +134 -0
  87. package/assets/dev-guide/fe/sources/vue/reactivity-pitfalls.md +261 -0
  88. package/assets/dev-guide/fe/sources/vue/style-guide-priority-a.md +117 -0
  89. package/assets/dev-guide/fe/sources/vue/style-guide-priority-b.md +231 -0
  90. package/assets/dev-guide/fe/sources/vue/style-guide-priority-c.md +86 -0
  91. package/assets/dev-guide/fe/sources/vue/style-guide-priority-d.md +72 -0
  92. package/dist/cli.js +42 -0
  93. package/dist/core/dashboard-cli.d.ts +12 -0
  94. package/dist/core/dashboard-cli.js +226 -0
  95. package/dist/core/dev-guide-injector.d.ts +26 -0
  96. package/dist/core/dev-guide-injector.js +137 -0
  97. package/dist/core/init.js +53 -0
  98. package/dist/core/lifecycle-classifier.d.ts +23 -0
  99. package/dist/core/lifecycle-classifier.js +104 -0
  100. package/dist/core/observability-backfill.d.ts +31 -0
  101. package/dist/core/observability-backfill.js +178 -0
  102. package/dist/core/observability-store.d.ts +58 -0
  103. package/dist/core/observability-store.js +195 -0
  104. package/dist/core/session-store.js +4 -0
  105. package/dist/core/spawn.d.ts +17 -0
  106. package/dist/core/spawn.js +179 -2
  107. package/dist/core/statusline-cli.js +34 -1
  108. package/dist/engine/compound-extractor.js +39 -0
  109. package/dist/engine/compound-loop.js +6 -0
  110. package/dist/engine/compound-retire.d.ts +20 -0
  111. package/dist/engine/compound-retire.js +85 -0
  112. package/dist/hooks/context-guard.js +25 -1
  113. package/dist/hooks/post-tool-use.js +48 -0
  114. package/dist/hooks/solution-injector.js +93 -0
  115. package/dist/host/install-claude.d.ts +6 -2
  116. package/dist/host/install-claude.js +74 -2
  117. package/dist/host/install-codex.d.ts +4 -0
  118. package/dist/host/install-codex.js +71 -0
  119. package/dist/host/install-orchestrator.js +1 -0
  120. package/package.json +6 -6
  121. package/plugin.json +1 -1
  122. package/scripts/postinstall.js +134 -0
@@ -0,0 +1,433 @@
1
+ ---
2
+ title: ๊ณตํ†ต BE ์›์น™ (์Šคํƒ ์ค‘๋ฆฝ)
3
+ version: 2026-05-18
4
+ sources:
5
+ - sources/12factor/
6
+ - sources/sre-book/
7
+ - sources/owasp-api/
8
+ - sources/otel/
9
+ - sources/ddia/
10
+ - sources/api-design/
11
+ - sources/postgres/
12
+ ---
13
+
14
+ # ๊ณตํ†ต BE ์›์น™
15
+
16
+ > ๋ชจ๋“  ๋ฐฑ์—”๋“œ ์ฝ”๋“œ(Node.js/Go/๊ธฐํƒ€)์— ์ ์šฉ๋˜๋Š” ํ•ฉ์˜ ์›์น™.
17
+ > ์Šคํƒ ํŠนํ™” ๊ฐ€์ด๋“œ๋Š” [`node.md`](./node.md), [`go.md`](./go.md) ์ฐธ์กฐ.
18
+
19
+ ## ์ถœ์ฒ˜ ์šฐ์„ ์ˆœ์œ„ (์ถฉ๋Œ ์‹œ)
20
+
21
+ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด ๋‹ค์Œ ์ˆœ์„œ๋กœ ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์ถœ์ฒ˜๋ฅผ ๋”ฐ๋ฅธ๋‹ค:
22
+
23
+ 1. **12-Factor App** โ€” ์‹คํ–‰ ํ™˜๊ฒฝ ์ด์‹์„ฑยท์šด์˜ ํ•ฉ์˜ (config, logs, disposability)
24
+ 2. **Google SRE Book** โ€” ๊ฐ€์šฉ์„ฑยทSLOยท์—๋Ÿฌ ์˜ˆ์‚ฐ ๊ธฐ์ค€
25
+ 3. **OWASP API Security Top 10** โ€” ๋ณด์•ˆ ๊ธฐ์ค€์„  (2023)
26
+ 4. **OpenTelemetry** โ€” ๊ด€์ฐฐ๊ฐ€๋Šฅ์„ฑ ๊ณ„์ธก ํ‘œ์ค€
27
+ 5. **DDIA** (Designing Data-Intensive Applications) โ€” ๋ฐ์ดํ„ฐ ๋ชจ๋ธยท๋ถ„์‚ฐ ์‹œ์Šคํ…œ ์˜์‚ฌ๊ฒฐ์ •
28
+ 6. **ํ”„๋ ˆ์ž„์›Œํฌ ๊ณต์‹ ๋ฌธ์„œ** โ€” Fastify / NestJS / Go stdlib ๋“ฑ
29
+ 7. **๋ฒค๋” ๊ถŒ์žฅ** โ€” AWS / GCP / Stripe ๋“ฑ
30
+
31
+ ์‚ฌ์šฉ์ž ์˜ํ–ฅ ์šฐ์„ ์ˆœ์œ„: **security > availability > correctness > performance > readability**.
32
+
33
+ ---
34
+
35
+ ## A. API ์„ค๊ณ„ 4์›์น™
36
+
37
+ > fe-guide์˜ ์ฝ”๋“œ ํ’ˆ์งˆ 4์›์น™(๊ฐ€๋…์„ฑ/์˜ˆ์ธก์„ฑ/์‘์ง‘๋„/๊ฒฐํ•ฉ๋„)๊ณผ ๋ฏธ๋Ÿฌ๋ง.
38
+ > ๊ฐ ์›์น™์€ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„ ๊ด€๊ณ„๋‹ค. ๋™์‹œ ๋งŒ์กฑ ๋ถˆ๊ฐ€๋Šฅ ์‹œ ์œ„ ์šฐ์„ ์ˆœ์œ„๋กœ ํŒ๋‹จ.
39
+
40
+ ### A.1 ๋ช…์‹œ์„ฑ (Explicitness) โ€” ์ตœ์šฐ์„ 
41
+
42
+ **API ๊ณ„์•ฝ์€ ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ €, ๋ช…์‹œ์ ์œผ๋กœ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค.**
43
+
44
+ - OpenAPI/Protobuf ์ŠคํŽ™ ํŒŒ์ผ์ด ๊ตฌํ˜„๋ณด๋‹ค ๋จผ์ € ์ž‘์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ตฌํ˜„์„ reverse๋กœ ์ƒ์„ฑํ•œ ์ŠคํŽ™์€ ๊ณ„์•ฝ์ด ์•„๋‹ˆ๋‹ค.
45
+ - ์š”์ฒญ/์‘๋‹ต ํ•„๋“œ์˜ optional/required๋ฅผ ์ŠคํŽ™๊ณผ ๊ฒ€์ฆ ์ฝ”๋“œ์—์„œ ๋™์ผํ•˜๊ฒŒ ๋ช…์‹œํ•œ๋‹ค.
46
+ - ์ŠคํŽ™์— optional์ธ ํ•„๋“œ๋ฅผ ์„œ๋ฒ„ ๊ฒ€์ฆ์—์„œ required ์ทจ๊ธ‰ํ•˜๋ฉด ๋ช…์„ธ ์œ„๋ฐ˜.
47
+ - ๋ฐ˜๋Œ€๋กœ required ํ•„๋“œ๋ฅผ ์„œ๋ฒ„์—์„œ ๊ฒ€์ฆ ์—†์ด ํ†ต๊ณผ์‹œํ‚ค๋ฉด runtime error ์›์ธ.
48
+ - **nullable vs optional์€ ๋‹ค๋ฅด๋‹ค**. `"value": null`(nullable, ํ•„๋“œ ์กด์žฌ) vs ํ•„๋“œ ์—†์Œ(absent, optional). ํ˜ผ์šฉ ๊ธˆ์ง€.
49
+ - ์—๋Ÿฌ ์‘๋‹ต ์ŠคํŽ™๋„ ๋™์ผํ•˜๊ฒŒ ์ •์˜ํ•œ๋‹ค. "์—๋Ÿฌ ์‹œ ์•Œ์•„์„œ"๋Š” ๊ณ„์•ฝ์ด ์•„๋‹ˆ๋‹ค.
50
+
51
+ ๊ทผ๊ฑฐ: `sources/api-design/`
52
+
53
+ ### A.2 ์ผ๊ด€์„ฑ (Consistency)
54
+
55
+ **๊ฐ™์€ ๊ฐœ๋…์€ API ์ „์ฒด์—์„œ ๋™์ผํ•œ ์ด๋ฆ„ยท๋ชจ์–‘์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.**
56
+
57
+ - ๋ฆฌ์†Œ์Šค ์ด๋ฆ„: ๋ณต์ˆ˜ํ˜• ๋ช…์‚ฌ (`/users`, `/orders`). ๋™์‚ฌ ๊ธˆ์ง€ (`/getUser` X).
58
+ - HTTP ๋ฉ”์„œ๋“œ: CRUD โ†’ GET/POST/PUTยทPATCH/DELETE. ๋ฉฑ๋“ฑ์„ฑ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ ํƒ.
59
+ - GET: ์กฐํšŒ, ๋ถ€์ž‘์šฉ ์—†์Œ
60
+ - POST: ์ƒ์„ฑ ๋˜๋Š” ๋น„๋ฉฑ๋“ฑ ์•ก์…˜
61
+ - PUT: ์ „์ฒด ๊ต์ฒด (๋ฉฑ๋“ฑ)
62
+ - PATCH: ๋ถ€๋ถ„ ์ˆ˜์ • (๋ฉฑ๋“ฑ์ด ๋ฐ”๋žŒ์งํ•˜๋‚˜ ํ•„์ˆ˜ ์•„๋‹˜)
63
+ - DELETE: ์‚ญ์ œ (๋ฉฑ๋“ฑ)
64
+ - ๋‚ ์งœ/์‹œ๊ฐ„: ํ•ญ์ƒ ISO 8601 UTC (`2026-05-18T07:00:00Z`). epoch int๋Š” ms ๋‹จ์œ„ ๋ช…์‹œ.
65
+ - ํŽ˜์ด์ง€๋„ค์ด์…˜: ์ปค์„œ ๊ธฐ๋ฐ˜ ์šฐ์„  (`cursor` + `limit`). ์˜คํ”„์…‹ ๊ธฐ๋ฐ˜์€ ๋Œ€์šฉ๋Ÿ‰์—์„œ ํ‡ดํ™”.
66
+ ```json
67
+ { "data": [...], "nextCursor": "eyJpZCI6MTIzfQ==", "hasMore": true }
68
+ ```
69
+ - ์—๋Ÿฌ ์ฝ”๋“œ: `SNAKE_CASE` ์ƒ์ˆ˜ (`PAYMENT_DECLINED`, `RESOURCE_NOT_FOUND`). HTTP ์ƒํƒœ๋งŒ์œผ๋กœ ๊ตฌ๋ถ„ ๊ธˆ์ง€.
70
+
71
+ ๊ทผ๊ฑฐ: `sources/api-design/` (Stripe API + GitHub API + Google AIP)
72
+
73
+ ### A.3 ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ (Predictability)
74
+
75
+ **๊ฐ™์€ ์ž…๋ ฅ์—๋Š” ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ. ๋ถ€์ž‘์šฉ์€ ๋ช…์‹œ๋œ ๊ฒƒ๋งŒ.**
76
+
77
+ - GET ์š”์ฒญ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค. CQRS(๋ช…๋ น/์กฐํšŒ ๋ถ„๋ฆฌ)๋ฅผ ๊ธฐ๋ณธ ๋งˆ์Œ๊ฐ€์ง์œผ๋กœ.
78
+ - ๊ฐ™์€ ์กฐ๊ฑด์—์„œ ๋™์ผ ์—”๋“œํฌ์ธํŠธ๋Š” ํ•ญ์ƒ ๊ฐ™์€ HTTP ์ƒํƒœ ์ฝ”๋“œ์™€ ์‘๋‹ต ๊ตฌ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
79
+ - ๋ฒŒํฌ ์—ฐ์‚ฐ์—์„œ ์ผ๋ถ€ ์„ฑ๊ณต/์ผ๋ถ€ ์‹คํŒจ ์‹œ ์‘๋‹ต ๋ชจ์–‘: `{ "succeeded": [...], "failed": [{"id": ..., "error": {...}}] }` ํ˜•์‹์œผ๋กœ ๋ช…์‹œ. 207 Multi-Status ํ™œ์šฉ.
80
+ - **์ˆจ์€ ๋ถ€์ž‘์šฉ ๊ธˆ์ง€**: ์กฐํšŒ API ๋‚ด๋ถ€์—์„œ ์ด๋ฒคํŠธ ๋ฐœํ–‰, ํ†ต๊ณ„ ๊ฐฑ์‹  ๋“ฑ์„ ๋ชฐ๋ž˜ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ•„์š”ํ•˜๋ฉด ๋ณ„๋„ endpoint ๋˜๋Š” ๋ช…์‹œ์  ๋ฌธ์„œํ™”.
81
+
82
+ ### A.4 ์ง„ํ™” ๊ฐ€๋Šฅ์„ฑ (Evolvability)
83
+
84
+ **API๋Š” ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊นจ์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.**
85
+
86
+ - **ํ•˜์œ„ ํ˜ธํ™˜ ๋ณ€๊ฒฝ** (ํด๋ผ์ด์–ธํŠธ ์•Œ๋ฆผ ์—†์ด ๊ฐ€๋Šฅ):
87
+ - ์ƒˆ optional ํ•„๋“œ ์ถ”๊ฐ€
88
+ - ์ƒˆ endpoint ์ถ”๊ฐ€
89
+ - ์ƒˆ enum ๊ฐ’ ์ถ”๊ฐ€ (ํด๋ผ์ด์–ธํŠธ๊ฐ€ unknown ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ „์ œ)
90
+ - **ํŒŒ๊ดด์  ๋ณ€๊ฒฝ** (๋ฒ„์ €๋‹ ๋˜๋Š” deprecation notice ํ•„์ˆ˜):
91
+ - ํ•„๋“œ ์ œ๊ฑฐ ๋˜๋Š” ์ด๋ฆ„ ๋ณ€๊ฒฝ
92
+ - ํƒ€์ž… ๋ณ€๊ฒฝ (string โ†’ int)
93
+ - ๊ธฐ์กด enum ๊ฐ’ ์ œ๊ฑฐ
94
+ - HTTP ๋ฉ”์„œ๋“œ/๊ฒฝ๋กœ ๋ณ€๊ฒฝ
95
+ - URL ๋ฒ„์ €๋‹: `/v1/`, `/v2/` โ€” ๋‹จ์ˆœํ•˜๊ณ  ์บ์‹œ ์นœํ™”์ .
96
+ - Path ๋ฒ„์ €๋‹ ์šฐ์„ . Header ๋ฒ„์ €๋‹์€ ์บ์‹œ ๋ ˆ์ด์–ด์—์„œ ๋ฌธ์ œ.
97
+ - Deprecation ์ •์ฑ…: ์ตœ์†Œ 6๊ฐœ์›” notice + `Deprecation` ์‘๋‹ต ํ—ค๋” ๋ถ€์ฐฉ.
98
+ ```
99
+ Deprecation: Sun, 01 Jan 2027 00:00:00 GMT
100
+ Sunset: Sun, 01 Jan 2027 00:00:00 GMT
101
+ Link: <https://docs.example.com/migration/v2>; rel="deprecation"
102
+ ```
103
+
104
+ ๊ทผ๊ฑฐ: `sources/api-design/` (Google AIP-180)
105
+
106
+ ---
107
+
108
+ ## B. Error Model
109
+
110
+ **์ ˆ๋Œ€ silent fail ๊ธˆ์ง€. ์—๋Ÿฌ๋Š” ๊ตฌ์กฐํ™”๋œ ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.**
111
+
112
+ ### B.1 ์—๋Ÿฌ ์‘๋‹ต ๊ตฌ์กฐ
113
+
114
+ ๋ชจ๋“  ์—๋Ÿฌ ์‘๋‹ต์€ ๋‹ค์Œ ๊ตฌ์กฐ๋ฅผ ์ค€์ˆ˜ํ•œ๋‹ค:
115
+
116
+ ```json
117
+ {
118
+ "error": {
119
+ "code": "PAYMENT_DECLINED",
120
+ "message": "๊ฒฐ์ œ๊ฐ€ ๊ฑฐ์ ˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์นด๋“œ ์ •๋ณด๋ฅผ ํ™•์ธํ•˜์„ธ์š”.",
121
+ "details": [
122
+ {
123
+ "field": "card.number",
124
+ "reason": "INVALID_FORMAT"
125
+ }
126
+ ],
127
+ "requestId": "req_01HX2V3K8..."
128
+ }
129
+ }
130
+ ```
131
+
132
+ - `code`: ๊ธฐ๊ณ„๊ฐ€ ์ฝ๋Š” SNAKE_CASE ์ƒ์ˆ˜. ํด๋ผ์ด์–ธํŠธ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ์šฉ.
133
+ - `message`: ์‚ฌ๋žŒ์ด ์ฝ๋Š” ์„ค๋ช…. ํ•„์š” ์‹œ ๋‹ค๊ตญ์–ดํ™”.
134
+ - `details`: ํ•„๋“œ๋ณ„ ๊ฒ€์ฆ ์˜ค๋ฅ˜ ๋ฐฐ์—ด (optional). ๋นˆ ๋ฐฐ์—ด์ด๋ฉด ์ƒ๋žต.
135
+ - `requestId`: ๋กœ๊ทธ ์ถ”์ ์šฉ. ํ•ญ์ƒ ํฌํ•จ. (correlation ID)
136
+
137
+ ### B.2 4xx vs 5xx ๊ฒฝ๊ณ„
138
+
139
+ | ์ƒํ™ฉ | ์ฝ”๋“œ | ์›์น™ |
140
+ |------|------|------|
141
+ | ํด๋ผ์ด์–ธํŠธ ์ž…๋ ฅ ์˜ค๋ฅ˜ | 400 | ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ณ ์ณ์•ผ ํ•จ โ€” ์žฌ์‹œ๋„ ์˜๋ฏธ ์—†์Œ |
142
+ | ์ธ์ฆ ์—†์Œ | 401 | ์ž๊ฒฉ์ฆ๋ช… ์ œ๊ณต ํ•„์š” |
143
+ | ๊ถŒํ•œ ์—†์Œ | 403 | ์ž๊ฒฉ์ฆ๋ช… ์žˆ์–ด๋„ ๋ถˆ๊ฐ€ |
144
+ | ๋ฆฌ์†Œ์Šค ์—†์Œ | 404 | ์กด์žฌํ•˜์ง€ ์•Š์Œ |
145
+ | ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™ ์œ„๋ฐ˜ | 422 | ์ž…๋ ฅ ํ˜•์‹์€ ๋งž์ง€๋งŒ ๋„๋ฉ”์ธ ๊ฑฐ๋ถ€ |
146
+ | ์†๋„ ์ œํ•œ | 429 | `Retry-After` ํ—ค๋” ํ•„์ˆ˜ |
147
+ | ์„œ๋ฒ„ ๋‚ด๋ถ€ ์˜ค๋ฅ˜ | 500 | ์„œ๋ฒ„๊ฐ€ ๊ณ ์ณ์•ผ ํ•จ |
148
+ | ์™ธ๋ถ€ ์˜์กด์„ฑ ์‹คํŒจ | 502/503 | ์ธํ”„๋ผ/upstream ๋ฌธ์ œ |
149
+
150
+ **ํ™ฉ๊ธˆ ๊ทœ์น™**: 4xx๋Š” ํด๋ผ์ด์–ธํŠธ ์ฑ…์ž„, 5xx๋Š” ์„œ๋ฒ„ ์ฑ…์ž„. ์„œ๋ฒ„ ์˜ค๋ฅ˜๋ฅผ 200 + `{ "success": false }` ๋กœ ์ˆจ๊ธฐ์ง€ ๋งˆ๋ผ.
151
+
152
+ ### B.3 ์—๋Ÿฌ ๋กœ๊น… ๊ธฐ์ค€
153
+
154
+ - 4xx: `warn` ๋ ˆ๋ฒจ (ํด๋ผ์ด์–ธํŠธ ๋ฌธ์ œ, ์šด์˜ ์•Œ๋žŒ ๋ถˆํ•„์š”)
155
+ - 5xx: `error` ๋ ˆ๋ฒจ + stack trace + requestId (์ฆ‰์‹œ ์•Œ๋žŒ ๋Œ€์ƒ)
156
+ - ๋นˆ catch ๋ธ”๋ก ์ ˆ๋Œ€ ๊ธˆ์ง€. ์ตœ์†Œ `logger.error(err, { context: '...' })` + re-throw.
157
+
158
+ ---
159
+
160
+ ## C. Observability Triple
161
+
162
+ **๋กœ๊ทธ + ๋ฉ”ํŠธ๋ฆญ + ํŠธ๋ ˆ์ด์Šค. ์„ธ ๊ฐ€์ง€ ์—†์œผ๋ฉด ํ”„๋กœ๋•์…˜ ๋ถˆ๊ฐ€.**
163
+
164
+ ### C.1 ๊ตฌ์กฐํ™” ๋กœ๊ทธ (Structured Logging)
165
+
166
+ ๊ทผ๊ฑฐ: `sources/sre-book/`, `sources/12factor/`
167
+
168
+ - **JSON ํ˜•์‹** ๊ฐ•์ œ. ํ‰๋ฌธ ๋กœ๊ทธ๋Š” ํŒŒ์‹ฑ ๋ถˆ๊ฐ€ โ†’ ๊ฒ€์ƒ‰ ๋ถˆ๊ฐ€.
169
+ ```json
170
+ {
171
+ "timestamp": "2026-05-18T07:00:00.123Z",
172
+ "level": "info",
173
+ "message": "Order created",
174
+ "service": "order-service",
175
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
176
+ "spanId": "00f067aa0ba902b7",
177
+ "orderId": "ord_01HX2V3K8",
178
+ "userId": "usr_01HX2V3K8",
179
+ "durationMs": 142
180
+ }
181
+ ```
182
+ - **๋ฏผ๊ฐ ์ •๋ณด ๊ธˆ์ง€**: ๋น„๋ฐ€๋ฒˆํ˜ธ, ์นด๋“œ๋ฒˆํ˜ธ, ๊ฐœ์ธ์‹๋ณ„์ •๋ณด๋ฅผ ๋กœ๊ทธ์— ์ง์ ‘ ๊ธฐ๋กํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋งˆ์Šคํ‚น ๋˜๋Š” ํ† ํฐํ™”.
183
+ - 12-Factor App Factor 11: ์•ฑ์€ ๋กœ๊ทธ๋ฅผ ํŒŒ์ผ์— ์“ฐ์ง€ ์•Š๊ณ  stdout์œผ๋กœ๋งŒ ์ถœ๋ ฅ. ์ˆ˜์ง‘์€ ์ธํ”„๋ผ ์ฑ…์ž„.
184
+
185
+ ### C.2 ๋ฉ”ํŠธ๋ฆญ โ€” RED Method
186
+
187
+ ๊ทผ๊ฑฐ: `sources/sre-book/` (4 Golden Signals์˜ ์„œ๋น„์Šค ์ค‘์‹ฌ ๋ณ€ํ˜•)
188
+
189
+ ๋ชจ๋“  ์„œ๋น„์Šค/์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•ด:
190
+
191
+ | ๋ฉ”ํŠธ๋ฆญ | ์„ค๋ช… | ์˜ˆ์‹œ |
192
+ |--------|------|------|
193
+ | **Rate** | ์š”์ฒญ ์ˆ˜ / ์ดˆ | `http_requests_total{method, path, status}` |
194
+ | **Errors** | ์—๋Ÿฌ ๋น„์œจ (5xx) | `http_errors_total{method, path, status}` |
195
+ | **Duration** | ์‘๋‹ต ์‹œ๊ฐ„ ๋ถ„ํฌ | `http_request_duration_seconds{quantile}` |
196
+
197
+ - histogram ๋ฒ„ํ‚ท: 5ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2.5s, 5s, 10s.
198
+ - p50/p95/p99 SLO๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค README ๋˜๋Š” `docs/slo.md`์— ๋ช…์‹œ ์˜๋ฌด.
199
+
200
+ ### C.3 ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์Šค (OpenTelemetry)
201
+
202
+ ๊ทผ๊ฑฐ: `sources/otel/`
203
+
204
+ - **์ž๋™ ๊ณ„์ธก(auto-instrumentation) ๋จผ์ €**: HTTP ์„œ๋ฒ„, DB ํด๋ผ์ด์–ธํŠธ, ๋ฉ”์‹œ์ง€ ํ๋Š” OTel SDK ์ž๋™ ๊ณ„์ธก ์‚ฌ์šฉ.
205
+ - **์ˆ˜๋™ span ์ถ”๊ฐ€ ๊ธฐ์ค€**: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฒฝ๊ณ„, ์™ธ๋ถ€ API ํ˜ธ์ถœ, ์ค‘์š”ํ•œ ๋‚ด๋ถ€ ํ•จ์ˆ˜.
206
+ ```typescript
207
+ const span = tracer.startSpan('processPayment');
208
+ try {
209
+ span.setAttributes({ 'payment.amount': amount, 'payment.currency': currency });
210
+ const result = await chargeCard(cardId, amount);
211
+ span.setStatus({ code: SpanStatusCode.OK });
212
+ return result;
213
+ } catch (err) {
214
+ span.recordException(err);
215
+ span.setStatus({ code: SpanStatusCode.ERROR });
216
+ throw err;
217
+ } finally {
218
+ span.end();
219
+ }
220
+ ```
221
+ - W3C TraceContext ํ—ค๋”(`traceparent`) ์ „ํŒŒ: ๋ชจ๋“  outbound HTTP ์š”์ฒญ์— ํฌํ•จ.
222
+ - `traceId`๋Š” ์—๋Ÿฌ ์‘๋‹ต์˜ `requestId`์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด ๋กœ๊ทธ-ํŠธ๋ ˆ์ด์Šค ์—ฐ๊ฒฐ.
223
+
224
+ ---
225
+
226
+ ## D. Idempotency & Retry
227
+
228
+ ### D.1 ๋ฉฑ๋“ฑ์„ฑ ์„ค๊ณ„
229
+
230
+ - GET / DELETE / PUT: ๋ณธ๋ž˜ ๋ฉฑ๋“ฑ. ์žฌ์‹œ๋„ ์•ˆ์ „.
231
+ - POST (์ƒ์„ฑ ์•ก์…˜): **Idempotency-Key** ํ—ค๋”๋กœ ๋ฉฑ๋“ฑ์„ฑ ๋ณด์žฅ.
232
+ ```
233
+ POST /v1/payments
234
+ Idempotency-Key: a8098c1a-f86e-11da-bd1a-00112444be1e
235
+ ```
236
+ - ์„œ๋ฒ„๋Š” Key๋ฅผ ์บ์‹œํ•˜๊ณ  ๋™์ผ Key ์žฌ์š”์ฒญ ์‹œ ์บ์‹œ๋œ ์‘๋‹ต ๋ฐ˜ํ™˜ (24์‹œ๊ฐ„~7์ผ TTL).
237
+ - ์‘๋‹ต์— `Idempotency-Key` ๋ฐ˜์˜ ๊ถŒ์žฅ.
238
+ - **์ž์—ฐ์Šค๋Ÿฝ์ง€ ์•Š์€ ๋ฉฑ๋“ฑ์„ฑ ๊ฐ•์ œ ๊ธˆ์ง€**: ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์–ต์ง€๋กœ ๋ฉฑ๋“ฑํ™”ํ•˜๊ธฐ๋ณด๋‹ค ํด๋ผ์ด์–ธํŠธ ์žฌ์‹œ๋„ ์ „๋žต์„ ๋ช…ํ™•ํžˆ ํ•˜๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.
239
+
240
+ ### D.2 ์žฌ์‹œ๋„ ์ „๋žต (Exponential Backoff + Jitter)
241
+
242
+ ํด๋ผ์ด์–ธํŠธ ์žฌ์‹œ๋„ ๊ธฐ์ค€:
243
+
244
+ - ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ: 429, 500, 502, 503, 504, ๋„คํŠธ์›Œํฌ ํƒ€์ž„์•„์›ƒ
245
+ - ์žฌ์‹œ๋„ ๋ถˆ๊ฐ€: 400, 401, 403, 404, 422 (ํด๋ผ์ด์–ธํŠธ ์˜ค๋ฅ˜๋Š” ์žฌ์‹œ๋„ ์˜๋ฏธ ์—†์Œ)
246
+
247
+ ๊ถŒ์žฅ ๊ณต์‹:
248
+ ```
249
+ delay = min(base * 2^attempt + random(0, base), max_delay)
250
+ ```
251
+ - base: 1s, max_delay: 30s, max_attempts: 5
252
+ - **Full Jitter**: `random(0, min(cap, base * 2^attempt))` โ€” thundering herd ๋ฐฉ์ง€
253
+
254
+ ์„œ๋ฒ„ ์ธก: 429 ์‘๋‹ต์— `Retry-After: 60` ํ—ค๋” ํ•„์ˆ˜.
255
+
256
+ ---
257
+
258
+ ## E. Security Baseline
259
+
260
+ ๊ทผ๊ฑฐ: `sources/owasp-api/`
261
+
262
+ ### E.1 ์ž…๋ ฅ ๊ฒ€์ฆ ๊ฒฝ๊ณ„
263
+
264
+ **์™ธ๋ถ€์—์„œ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ์‹ ๋ขฐํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์‹ ๋ขฐ ๊ฒฝ๊ณ„๋ฅผ ๋ช…ํ™•ํžˆ ๊ทธ์–ด๋ผ.
265
+
266
+ ```
267
+ [Client] โ†’ [API Gateway / Load Balancer] โ†’ [Service] โ†’ [DB/Storage]
268
+ โ†‘ TLS Termination โ†‘ ์—ฌ๊ธฐ์„œ ๊ฒ€์ฆ
269
+ โ†‘ Rate Limiting โ†‘ Business rule ๊ฒ€์ฆ
270
+ โ†‘ Auth token ๊ฒ€์ฆ
271
+ ```
272
+
273
+ - **๊ฒฝ๊ณ„์—์„œ ์ฆ‰์‹œ ๊ฒ€์ฆ**: ์ปจํŠธ๋กค๋Ÿฌ/ํ•ธ๋“ค๋Ÿฌ ์ง„์ž… ์‹œ ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ํ†ต๊ณผ ํ›„์—๋งŒ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ง„์ž….
274
+ - Path parameter, query string, request body, headers ๋ชจ๋‘ ๊ฒ€์ฆ ๋Œ€์ƒ.
275
+ - SQL/NoSQL Injection: ORM/parameterized query ๊ฐ•์ œ. ๋ฌธ์ž์—ด concatenation์œผ๋กœ ์ฟผ๋ฆฌ ์ž‘์„ฑ ์ ˆ๋Œ€ ๊ธˆ์ง€.
276
+ - ํŒŒ์ผ ์—…๋กœ๋“œ: MIME type + magic bytes ๊ฒ€์ฆ, ์ €์žฅ ๊ฒฝ๋กœ path traversal ๋ฐฉ์ง€.
277
+
278
+ ### E.2 ์ธ์ฆ/์ธ๊ฐ€ ๋ถ„๋ฆฌ
279
+
280
+ - **์ธ์ฆ(Authentication)**: "๋ˆ„๊ตฌ์ธ๊ฐ€?" โ€” JWT / OAuth 2.0 / API Key
281
+ - **์ธ๊ฐ€(Authorization)**: "๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?" โ€” RBAC / ABAC
282
+ - **OWASP API1**: Broken Object Level Authorization โ€” ๋งค ์š”์ฒญ๋งˆ๋‹ค ๋ฆฌ์†Œ์Šค ์†Œ์œ ๊ถŒ ํ™•์ธ. `userId`๋ฅผ JWT์—์„œ ์ถ”์ถœ, ๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ `userId`์™€ ์ผ์น˜ ๊ฒ€์ฆ. URL ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€์กฐ๋กœ ํƒ€์ธ ๋ฐ์ดํ„ฐ ์ ‘๊ทผํ•˜๋Š” ํŒจํ„ด.
283
+ - ์ธ๊ฐ€ ๋กœ์ง์€ ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ. ํ”„๋ ˆ์ž„์›Œํฌ ๋ฏธ๋“ค์›จ์–ด์— ์ „์ ์œผ๋กœ ์˜์กด ๊ธˆ์ง€.
284
+
285
+ ### E.3 ๋น„๋ฐ€ ๊ด€๋ฆฌ
286
+
287
+ - `.env` ํŒŒ์ผ ์ปค๋ฐ‹ ๊ธˆ์ง€ (`.gitignore` + pre-commit hook).
288
+ - ํ”„๋กœ๋•์…˜ ์‹œํฌ๋ฆฟ: AWS Secrets Manager / GCP Secret Manager / Vault.
289
+ - ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์‹œํฌ๋ฆฟ ํƒ์ง€: `git-secrets` ๋˜๋Š” CI gitleaks.
290
+ - **ํ™˜๊ฒฝ๋ณ„ ๋ถ„๋ฆฌ**: dev/staging/prod ์‹œํฌ๋ฆฟ ์™„์ „ ๋ถ„๋ฆฌ. dev ์‹œํฌ๋ฆฟ์ด prod์— ๋‹ฟ์•„์„œ๋Š” ์•ˆ ๋จ.
291
+ - 12-Factor App Factor 3: Config๋ฅผ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ €์žฅ.
292
+
293
+ ### E.4 OWASP API Security Top 10 (2023) ๋งคํ•‘
294
+
295
+ | # | ์ทจ์•ฝ์  | ํ•ต์‹ฌ ๋Œ€์‘ |
296
+ |---|--------|-----------|
297
+ | API1 | Broken Object Level Authorization | ๋ชจ๋“  ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ์— ์†Œ์œ ๊ถŒ ๊ฒ€์ฆ |
298
+ | API2 | Broken Authentication | JWT ์„œ๋ช… ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ช…์‹œ (`RS256`), ํ† ํฐ ๋งŒ๋ฃŒ ๊ฒ€์ฆ |
299
+ | API3 | Broken Object Property Level Auth | ์‘๋‹ต์—์„œ ๋ฏผ๊ฐ ํ•„๋“œ ์„ ํƒ์  ๋…ธ์ถœ (allowlist projection) |
300
+ | API4 | Unrestricted Resource Consumption | Rate limiting + ํŽ˜์ด๋กœ๋“œ ํฌ๊ธฐ ์ œํ•œ |
301
+ | API5 | Broken Function Level Authorization | admin/user ๊ธฐ๋Šฅ ๋ถ„๋ฆฌ, HTTP ๋ฉ”์„œ๋“œ ๋ณ„ ๊ถŒํ•œ ๊ฒ€์ฆ |
302
+ | API6 | Unrestricted Access to Sensitive Flows | ๋กœ๊ทธ์ธ/OTP์— rate limit + account lockout |
303
+ | API7 | Server Side Request Forgery | ์™ธ๋ถ€ URL fetch ์ „ allowlist ๊ฒ€์ฆ |
304
+ | API8 | Security Misconfiguration | CORS, ๋ถˆํ•„์š” HTTP ๋ฉ”์„œ๋“œ, ๋””๋ฒ„๊ทธ ์—”๋“œํฌ์ธํŠธ ๋น„ํ™œ์„ฑํ™” |
305
+ | API9 | Improper Inventory Management | API ๋ฒ„์ „ ํ๊ธฐ ์ •์ฑ… + ์Šคํ…Œ์ด์ง• ์—”๋“œํฌ์ธํŠธ ๋…ธ์ถœ ๊ธˆ์ง€ |
306
+ | API10 | Unsafe Consumption of APIs | ์™ธ๋ถ€ API ์‘๋‹ต๋„ ๊ฒ€์ฆ (์‹ ๋ขฐํ•˜์ง€ ์•Š์Œ) |
307
+
308
+ ---
309
+
310
+ ## F. Performance Baseline
311
+
312
+ ### F.1 SLO ๋ช…์‹œ ์˜๋ฌด
313
+
314
+ **p50/p95/p99 SLO๋ฅผ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋ฌธ์„œํ™”ํ•˜์ง€ ์•Š์œผ๋ฉด "๋น ๋ฅด๋‹ค"๋Š” ์ฃผ์žฅ์€ ์˜๋ฏธ ์—†๋‹ค.**
315
+
316
+ - `docs/slo.md` ๋˜๋Š” ์„œ๋น„์Šค README์— ๋ช…์‹œ:
317
+ ```
318
+ | Endpoint | p50 | p95 | p99 | Error Budget |
319
+ |------------------|-------|--------|--------|--------------|
320
+ | GET /orders | 50ms | 200ms | 500ms | 99.9% / mo |
321
+ | POST /payments | 200ms | 500ms | 1000ms | 99.95% / mo |
322
+ ```
323
+ - ์‹ ๊ทœ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ ํ•ด๋‹น ์—”๋“œํฌ์ธํŠธ SLO ์˜ํ–ฅ๋„ ๊ฒ€ํ†  ์˜๋ฌด.
324
+
325
+ ### F.2 N+1 ์ฟผ๋ฆฌ ๊ธˆ์ง€
326
+
327
+ ORM ์‚ฌ์šฉ ์‹œ N+1์€ ๊ฐ€์žฅ ํ”ํ•œ ์„ฑ๋Šฅ ํ•จ์ •:
328
+
329
+ ```typescript
330
+ // WRONG: N๊ฐœ ์ฃผ๋ฌธ๋งˆ๋‹ค N๋ฒˆ ์ฟผ๋ฆฌ
331
+ const orders = await Order.findAll();
332
+ for (const order of orders) {
333
+ order.user = await User.findById(order.userId); // N๋ฒˆ!
334
+ }
335
+
336
+ // RIGHT: JOIN ๋˜๋Š” ๋ณ„๋„ IN ์ฟผ๋ฆฌ๋กœ ํ•œ ๋ฒˆ์—
337
+ const orders = await Order.findAll({ include: [User] });
338
+ // ๋˜๋Š”
339
+ const userIds = orders.map(o => o.userId);
340
+ const users = await User.findAll({ where: { id: { [Op.in]: userIds } } });
341
+ ```
342
+
343
+ - Prisma: `include` / `select` ๋ช…์‹œ. `findMany` ํ›„ ๋ฃจํ”„ ๋‚ด `findUnique` ํŒจํ„ด ๊ธˆ์ง€.
344
+ - GORM: `Preload`, `Joins` ํ™œ์šฉ.
345
+ - DataLoader ํŒจํ„ด: GraphQL ๋˜๋Š” ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์ปจํ…์ŠคํŠธ.
346
+
347
+ ### F.3 ์บ์‹ฑ ๊ณ„์ธต ๋ช…์‹œ
348
+
349
+ ์บ์‹ฑ ์ „๋žต์„ ์ฝ”๋“œ ์ฃผ์„ ๋˜๋Š” ๋ฌธ์„œ์— ๋ช…์‹œ:
350
+
351
+ ```
352
+ [์š”์ฒญ] โ†’ [CDN / Edge Cache] โ†’ [API] โ†’ [Redis / Memcached] โ†’ [DB]
353
+ ```
354
+
355
+ | ๊ณ„์ธต | ์ ํ•ฉํ•œ ๋ฐ์ดํ„ฐ | TTL ๊ธฐ์ค€ |
356
+ |------|---------------|----------|
357
+ | CDN Edge | ๊ณต๊ฐœ ์ •์ /์ค€์ •์  API | ๋ถ„~์‹œ๊ฐ„ |
358
+ | Redis/Memcached | ์„ธ์…˜, ์ž์ฃผ ์ฝํžˆ๋Š” ๋น„๊ณต๊ฐœ ๋ฐ์ดํ„ฐ | ์ดˆ~๋ถ„ |
359
+ | Local (in-process) | ์„ค์ •, enum ๊ฐ™์€ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ | ์•ฑ ์ˆ˜๋ช… |
360
+
361
+ - **์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต ๋ช…์‹œ**: TTL ๊ธฐ๋ฐ˜ / ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ค‘ ์„ ํƒ. "๋‚˜์ค‘์— ์ƒ๊ฐ"์€ ์บ์‹œ ๋ถˆ์ผ์น˜ ๋ฒ„๊ทธ ์›์ธ.
362
+ - **์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ๋ฐฉ์ง€**: ๋งŒ๋ฃŒ TTL์— jitter ์ถ”๊ฐ€ + lock/singleflight ํŒจํ„ด.
363
+
364
+ ---
365
+
366
+ ## G. DB ๊ธฐ๋ณธ๊ธฐ
367
+
368
+ ๊ทผ๊ฑฐ: `sources/ddia/`, `sources/postgres/`
369
+
370
+ ### G.1 ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ๋ช…์‹œ
371
+
372
+ - ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„๋ฅผ ์ฝ”๋“œ ์ฃผ์„์œผ๋กœ ๋ช…์‹œ:
373
+ ```typescript
374
+ // TX: order ์ƒ์„ฑ + stock ์ฐจ๊ฐ ์›์ž์  ์ฒ˜๋ฆฌ
375
+ await db.transaction(async (trx) => {
376
+ await Order.create({ ... }, { transaction: trx });
377
+ await Stock.decrement('quantity', { by: 1, where: { productId }, transaction: trx });
378
+ });
379
+ ```
380
+ - **๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์€ ํ”ผํ•œ๋‹ค**. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„ 2PC ๋Œ€์‹  Saga ํŒจํ„ด ๋˜๋Š” ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜.
381
+ - ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์™ธ๋ถ€ API ํ˜ธ์ถœ ๊ธˆ์ง€ โ€” ๋„คํŠธ์›Œํฌ ์ง€์—ฐ์ด lock ๋ณด์œ  ์‹œ๊ฐ„์„ ๋Š˜๋ฆฐ๋‹ค.
382
+
383
+ ### G.2 ๋ฌด์ค‘๋‹จ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (Expand/Contract)
384
+
385
+ **DB ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์€ ๋ฐฐํฌ์™€ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.** Breaking change๋ฅผ ํ”ผํ•˜๋Š” 3๋‹จ๊ณ„:
386
+
387
+ ```
388
+ Phase 1 โ€” Expand: ์ƒˆ ์ปฌ๋Ÿผ/ํ…Œ์ด๋ธ” ์ถ”๊ฐ€. ๊ธฐ์กด ์ปฌ๋Ÿผ ์œ ์ง€. ์•ฑ์€ ๋‘˜ ๋‹ค ์“ธ ์ˆ˜ ์žˆ๊ฒŒ.
389
+ Phase 2 โ€” Migrate: ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ ๊ตฌ์กฐ๋กœ ์ด์ „. ์•ฑ ๋ฐฐํฌ.
390
+ Phase 3 โ€” Contract: ๊ตฌ ์ปฌ๋Ÿผ/ํ…Œ์ด๋ธ” ์ œ๊ฑฐ. (Phase 2 ์™„๋ฃŒ ํ›„ ์ตœ์†Œ 1 ๋ฆด๋ฆฌ์Šค ํ›„)
391
+ ```
392
+
393
+ - `DROP COLUMN` / `RENAME COLUMN` / ํƒ€์ž… ๋ณ€๊ฒฝ์€ ๋ฐ˜๋“œ์‹œ Expand/Contract.
394
+ - NOT NULL ์ปฌ๋Ÿผ ์ถ”๊ฐ€: ๊ธฐ๋ณธ๊ฐ’ ์žˆ๋Š” nullable๋กœ ๋จผ์ € ์ถ”๊ฐ€ โ†’ ๋ฐ์ดํ„ฐ ์ฑ„์›€ โ†’ NOT NULL ์ œ์•ฝ.
395
+ - ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋„๊ตฌ: Flyway / Liquibase / Alembic / golang-migrate. ํŒŒ์ผ ์ด๋ฆ„์— ๋ฒ„์ „/ํƒ€์ž„์Šคํƒฌํ”„.
396
+
397
+ ### G.3 ์ธ๋ฑ์Šค ์˜๋„ ์ฃผ์„
398
+
399
+ ```sql
400
+ -- ์ฃผ๋ฌธ ๋ชฉ๋ก API: WHERE user_id = ? ORDER BY created_at DESC LIMIT 20
401
+ -- ๋ณตํ•ฉ ์ธ๋ฑ์Šค๋กœ index scan + filesort ์—†์ด ์ฒ˜๋ฆฌ
402
+ CREATE INDEX idx_orders_user_created
403
+ ON orders(user_id, created_at DESC);
404
+ ```
405
+
406
+ - ์ธ๋ฑ์Šค ์ถ”๊ฐ€ ์‹œ ๋ฐ˜๋“œ์‹œ "์–ด๋–ค ์ฟผ๋ฆฌ๋ฅผ ์œ„ํ•œ ์ธ๋ฑ์Šค์ธ๊ฐ€" ์ฃผ์„.
407
+ - ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์ธ๋ฑ์Šค๋Š” ์ œ๊ฑฐํ•œ๋‹ค (write ๋ถ€ํ•˜ + vacuum ๋น„์šฉ).
408
+ - `EXPLAIN ANALYZE` ๊ฒฐ๊ณผ๋ฅผ PR์— ์ฒจ๋ถ€ํ•˜๋Š” ๊ด€ํ–‰์„ ์œ ์ง€ํ•œ๋‹ค.
409
+
410
+ ---
411
+
412
+ ## H. ์•ˆํ‹ฐํŒจํ„ด ์นดํƒˆ๋กœ๊ทธ (๋ฆฌ๋ทฐ์—์„œ ์ฆ‰์‹œ [HIGH] ์žก์•„๋ผ)
413
+
414
+ | ์•ˆํ‹ฐํŒจํ„ด | ๊ทผ๊ฑฐ | ํ”ฝ์Šค |
415
+ |----------|------|------|
416
+ | ๋นˆ catch ๋ธ”๋ก | global rules / anti-pattern | ์ตœ์†Œ ๋กœ๊ทธ + re-throw |
417
+ | 50์ค„ ์ดˆ๊ณผ ํ•จ์ˆ˜ | global rules | ์ฑ…์ž„๋ณ„ ๋ถ„๋ฆฌ |
418
+ | ์ค‘์ฒฉ ๊นŠ์ด 5+ | global rules | early return |
419
+ | HTTP 200 + `{ "success": false }` | Error Model B.2 | ์ ์ ˆํ•œ 4xx/5xx ์‚ฌ์šฉ |
420
+ | ๋ฌธ์ž์—ด concatenation SQL | Security E.1 | parameterized query |
421
+ | ๋ฃจํ”„ ๋‚ด DB ์ฟผ๋ฆฌ (N+1) | Performance F.2 | ๋ฐฐ์น˜ ์ฟผ๋ฆฌ / JOIN |
422
+ | ํ•˜๋“œ์ฝ”๋”ฉ ์‹œํฌ๋ฆฟ | Security E.3 | ํ™˜๊ฒฝ๋ณ€์ˆ˜ / ์‹œํฌ๋ฆฟ ๋งค๋‹ˆ์ € |
423
+ | ํŠธ๋žœ์žญ์…˜ ๋‚ด ์™ธ๋ถ€ API ํ˜ธ์ถœ | DB G.1 | ํŠธ๋žœ์žญ์…˜ ๋ฐ–์œผ๋กœ ์ด๋™ |
424
+ | ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜ (2PC) | DB G.1 | Saga / ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ |
425
+ | SLO ์—†์ด "๋น ๋ฅด๋‹ค" ์ฃผ์žฅ | Performance F.1 | docs/slo.md ์ž‘์„ฑ |
426
+ | ์ธ๋ฑ์Šค ์˜๋„ ์ฃผ์„ ์—†์Œ | DB G.3 | ์ฟผ๋ฆฌ โ†’ ์ธ๋ฑ์Šค ์ฃผ์„ |
427
+ | URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํƒ€์ธ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ | OWASP API1 | ์†Œ์œ ๊ถŒ ๊ฒ€์ฆ |
428
+ | ์‘๋‹ต์— ์ „์ฒด ๋ชจ๋ธ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ | OWASP API3 | allowlist projection |
429
+ | ๋กœ๊ทธ์— ๊ฐœ์ธ์ •๋ณด | Security E.1 | ๋งˆ์Šคํ‚น / ํ† ํฐํ™” |
430
+ | ํ™˜๊ฒฝ๋ณ„ ์‹œํฌ๋ฆฟ ๋ฏธ๋ถ„๋ฆฌ | Security E.3 | ํ™˜๊ฒฝ๋ณ„ ์™„์ „ ๋ถ„๋ฆฌ |
431
+ | `DROP COLUMN` ์ฆ‰์‹œ ์‹คํ–‰ | DB G.2 | Expand/Contract ํŒจํ„ด |
432
+ | ํ‰๋ฌธ ๋กœ๊ทธ (๋น„๊ตฌ์กฐํ™”) | Observability C.1 | JSON ๊ตฌ์กฐํ™” ๋กœ๊ทธ |
433
+ | traceId ์—†๋Š” ์—๋Ÿฌ ์‘๋‹ต | Observability C.3 | requestId ํฌํ•จ |