@wooojin/forgen 0.4.7 โ 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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +40 -0
- package/assets/dev-guide/be/README.md +226 -0
- package/assets/dev-guide/be/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/be/principles/common.md +433 -0
- package/assets/dev-guide/be/principles/go.md +469 -0
- package/assets/dev-guide/be/principles/node.md +388 -0
- package/assets/dev-guide/be/skills/go/be-build/SKILL.md +262 -0
- package/assets/dev-guide/be/skills/go/be-perf/SKILL.md +308 -0
- package/assets/dev-guide/be/skills/go/be-review/SKILL.md +119 -0
- package/assets/dev-guide/be/skills/go/be-security/SKILL.md +362 -0
- package/assets/dev-guide/be/skills/node/be-build/SKILL.md +239 -0
- package/assets/dev-guide/be/skills/node/be-perf/SKILL.md +272 -0
- package/assets/dev-guide/be/skills/node/be-review/SKILL.md +118 -0
- package/assets/dev-guide/be/skills/node/be-security/SKILL.md +355 -0
- package/assets/dev-guide/be/sources/12factor/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/api-design/INDEX.md +56 -0
- package/assets/dev-guide/be/sources/ddia/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/go-runtime/INDEX.md +62 -0
- package/assets/dev-guide/be/sources/node-runtime/INDEX.md +60 -0
- package/assets/dev-guide/be/sources/otel/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/owasp-api/INDEX.md +52 -0
- package/assets/dev-guide/be/sources/postgres/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/sre-book/INDEX.md +48 -0
- package/assets/dev-guide/fe/README.md +197 -0
- package/assets/dev-guide/fe/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/fe/adapters/refresh.sh +68 -0
- package/assets/dev-guide/fe/principles/common.md +160 -0
- package/assets/dev-guide/fe/principles/react.md +183 -0
- package/assets/dev-guide/fe/principles/vue.md +196 -0
- package/assets/dev-guide/fe/skills/react/fe-build/SKILL.md +139 -0
- package/assets/dev-guide/fe/skills/react/fe-perf/SKILL.md +179 -0
- package/assets/dev-guide/fe/skills/react/fe-review/SKILL.md +141 -0
- package/assets/dev-guide/fe/skills/vue/fe-build/SKILL.md +148 -0
- package/assets/dev-guide/fe/skills/vue/fe-perf/SKILL.md +163 -0
- package/assets/dev-guide/fe/skills/vue/fe-review/SKILL.md +136 -0
- package/assets/dev-guide/fe/sources/a11y-dx/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-memory.md +150 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-performance.md +99 -0
- package/assets/dev-guide/fe/sources/a11y-dx/lighthouse-audits.md +146 -0
- package/assets/dev-guide/fe/sources/a11y-dx/react-devtools-profiler.md +128 -0
- package/assets/dev-guide/fe/sources/a11y-dx/wcag22-new-criteria.md +174 -0
- package/assets/dev-guide/fe/sources/perf/01-core-web-vitals.md +58 -0
- package/assets/dev-guide/fe/sources/perf/02-inp.md +83 -0
- package/assets/dev-guide/fe/sources/perf/03-lcp-cls.md +130 -0
- package/assets/dev-guide/fe/sources/perf/04-speculation-rules.md +148 -0
- package/assets/dev-guide/fe/sources/perf/05-view-transitions.md +153 -0
- package/assets/dev-guide/fe/sources/perf/06-nextjs-caching.md +188 -0
- package/assets/dev-guide/fe/sources/perf/07-server-components.md +181 -0
- package/assets/dev-guide/fe/sources/perf/08-ppr.md +133 -0
- package/assets/dev-guide/fe/sources/perf/09-nextjs-image.md +200 -0
- package/assets/dev-guide/fe/sources/perf/10-optimize-lcp.md +201 -0
- package/assets/dev-guide/fe/sources/perf/INDEX.md +88 -0
- package/assets/dev-guide/fe/sources/react/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/react/keeping-components-pure.md +135 -0
- package/assets/dev-guide/fe/sources/react/no-effect-patterns.md +183 -0
- package/assets/dev-guide/fe/sources/react/react-compiler.md +182 -0
- package/assets/dev-guide/fe/sources/react/server-components.md +194 -0
- package/assets/dev-guide/fe/sources/react/server-functions.md +192 -0
- package/assets/dev-guide/fe/sources/react/suspense.md +218 -0
- package/assets/dev-guide/fe/sources/react/use-action-state.md +123 -0
- package/assets/dev-guide/fe/sources/react/use-form-status.md +158 -0
- package/assets/dev-guide/fe/sources/react/use-hook.md +153 -0
- package/assets/dev-guide/fe/sources/react/use-optimistic.md +194 -0
- package/assets/dev-guide/fe/sources/toss-ff/INDEX.md +58 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-code-directory.md +79 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-form-fields.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-magic-number.md +47 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-item-edit-modal.md +124 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-bottom-sheet.md +57 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-page-state.md +71 -0
- package/assets/dev-guide/fe/sources/toss-ff/overview-4-principles.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-hidden-logic.md +59 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-http.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-use-user.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-comparison-order.md +52 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-condition-name.md +64 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-login-start-page.md +183 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-magic-number.md +53 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-submit-button.md +73 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-ternary-operator.md +38 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-use-page-state.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-user-policy.md +98 -0
- package/assets/dev-guide/fe/sources/vue/INDEX.md +17 -0
- package/assets/dev-guide/fe/sources/vue/composition-api.md +251 -0
- package/assets/dev-guide/fe/sources/vue/nuxt-data-fetching.md +232 -0
- package/assets/dev-guide/fe/sources/vue/pinia-state-management.md +134 -0
- package/assets/dev-guide/fe/sources/vue/reactivity-pitfalls.md +261 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-a.md +117 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-b.md +231 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-c.md +86 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-d.md +72 -0
- package/dist/checks/self-score-deflation.js +6 -4
- package/dist/cli.js +47 -2
- package/dist/core/auto-compound-runner.js +6 -2
- package/dist/core/dashboard-cli.d.ts +12 -0
- package/dist/core/dashboard-cli.js +226 -0
- package/dist/core/dashboard.js +2 -2
- package/dist/core/dev-guide-injector.d.ts +26 -0
- package/dist/core/dev-guide-injector.js +137 -0
- package/dist/core/doctor.d.ts +10 -0
- package/dist/core/doctor.js +49 -8
- package/dist/core/harness.js +8 -2
- package/dist/core/init.js +53 -0
- package/dist/core/inspect-cli.js +4 -4
- package/dist/core/lifecycle-classifier.d.ts +23 -0
- package/dist/core/lifecycle-classifier.js +104 -0
- package/dist/core/migrate-evidence-host.js +1 -1
- package/dist/core/notify.js +7 -0
- package/dist/core/observability-backfill.d.ts +31 -0
- package/dist/core/observability-backfill.js +178 -0
- package/dist/core/observability-store.d.ts +58 -0
- package/dist/core/observability-store.js +195 -0
- package/dist/core/paths.d.ts +16 -2
- package/dist/core/paths.js +16 -2
- package/dist/core/session-store.d.ts +12 -1
- package/dist/core/session-store.js +77 -1
- package/dist/core/spawn.d.ts +17 -0
- package/dist/core/spawn.js +191 -8
- package/dist/core/statusline-cli.js +34 -1
- package/dist/core/v1-bootstrap.d.ts +7 -0
- package/dist/core/v1-bootstrap.js +28 -6
- package/dist/engine/compound-extractor.js +40 -1
- package/dist/engine/compound-loop.js +6 -0
- package/dist/engine/compound-retire.d.ts +20 -0
- package/dist/engine/compound-retire.js +85 -0
- package/dist/engine/learn-cli.js +2 -2
- package/dist/engine/lifecycle/bypass-detector.js +3 -2
- package/dist/engine/lifecycle/meta-reclassifier.js +1 -1
- package/dist/engine/lifecycle/signals.js +2 -2
- package/dist/engine/lifecycle/trigger-t1-correction.js +1 -1
- package/dist/engine/solution-candidate.js +1 -1
- package/dist/engine/solution-outcomes.js +1 -1
- package/dist/engine/solution-quarantine.js +1 -1
- package/dist/engine/solution-weakness.js +8 -2
- package/dist/forge/cli.js +1 -1
- package/dist/hooks/context-guard.js +25 -1
- package/dist/hooks/keyword-detector.js +1 -1
- package/dist/hooks/post-tool-use.js +48 -0
- package/dist/hooks/secret-filter.js +2 -2
- package/dist/hooks/shared/hook-response.js +1 -1
- package/dist/hooks/shared/hook-timing.js +3 -3
- package/dist/hooks/solution-injector.js +94 -1
- package/dist/hooks/stop-guard.js +3 -3
- package/dist/host/install-claude.d.ts +6 -2
- package/dist/host/install-claude.js +74 -2
- package/dist/host/install-codex.d.ts +4 -0
- package/dist/host/install-codex.js +72 -1
- package/dist/host/install-orchestrator.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/preset/facet-catalog.js +2 -2
- package/dist/renderer/rule-renderer.js +7 -7
- package/dist/store/compound-usage-store.js +1 -1
- package/dist/store/implicit-feedback-store.js +2 -2
- package/dist/store/profile-store.d.ts +11 -0
- package/dist/store/profile-store.js +23 -0
- package/package.json +6 -6
- package/plugin.json +1 -1
- 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 ํฌํจ |
|