๐Ÿ“– star365 Docs
โ† ๋ณด๊ณ ์„œ ๋ชฉ๋ก

Agency v2 S2-B ๋ณด๊ณ  โ€” Build + E2E + PG 4-tier + Integrity Manifest

๋ณด๊ณ  ID: agency-v2-s2b-20260503 ์ž‘์„ฑ์ผ: 2026-05-03 ์ž‘์„ฑ์ž: Claude (์ž„์‹œ CEO) Sprint: S2-B (npm install + nest build + PM2 + E2E)


1. ํ•œ ์ค„ ์š”์•ฝ

S2-A (์ธํ”„๋ผ + skeleton) ์งํ›„ ์ž๋™ S2-B ์ง„ํ–‰. NestJS api-gateway ๋นŒ๋“œ + PM2 ์˜๊ตฌ ๊ฐ€๋™ + Auth E2E + Agency CRUD E2E 6/6 + PG 4-tier user + DB ๊ถŒํ•œ ๋ถ„๋ฆฌ + sha256 manifest ๋ชจ๋‘ ์‹ค์ธก ๊ฒ€์ฆ PASS. ๊ฑฐ์ง“ ์™„์„ฑ 0๊ฑด, PENDING 8๊ฑด ์ •์ง ๋ช…์‹œ.

2. ๊ฒ€์ฆ ๋งคํŠธ๋ฆญ์Šค (์ •์ง 3๋ถ„๋ฅ˜)

โœ… A. ์‹ค์ธก ๊ฒ€์ฆ ์™„์„ฑ (23๊ฑด)

์˜์—ญ ๊ฒ€์ฆ ๊ฒฐ๊ณผ
npm install workspaces 413 packages / 303MB / 18s
nest build dist/main.js + ๋ชจ๋“  ๋ชจ๋“ˆ ์ปดํŒŒ์ผ
PM2 ์˜๊ตฌ ๋“ฑ๋ก agency-v2-api online pid 3964435, 103.6MB, autorestart
health endpoint {"status":"ok","db":true,...}
Auth register E2E ์‚ฌ์šฉ์ž cmopnf87... ์ƒ์„ฑ + JWT 263 chars
Auth login E2E JWT ๋ฐœ๊ธ‰, role STAFF ์ •ํ™•
RolesGuard (STAFFโ†’403) GET /agencies โ†’ "Forbidden / insufficient_role" โœ…
SUPER_ADMIN promote DB UPDATE ๊ฒ€์ฆ
re-login (SUPER_ADMIN JWT) role=SUPER_ADMIN
Agency CRUD POST ร— 2 alpha (id cmopng12b...) + beta (id cmopng14c...)
Agency CRUD GET list 2๊ฐœ array, ordered by createdAt desc
Agency CRUD GET :id single agency
Agency CRUD PATCH name ์—…๋ฐ์ดํŠธ + updatedAt ๋ณ€๊ฒฝ
Agency CRUD DELETE status=ARCHIVED soft delete
DB SELECT ํšŒ๊ท€ alpha ARCHIVED / beta ACTIVE ์ •ํ™•
PG 4-tier user ์ƒ์„ฑ super/rw/ro/audit (4 roles)
RO UPDATE ๊ฑฐ๋ถ€ ๊ฒ€์ฆ "permission denied for table User" โœ…
DATABASE_URL superโ†’rw .env ๋ณ€๊ฒฝ + PM2 restart
ํšŒ๊ท€ (rw user) Auth + CRUD ์ •์ƒ ์ž‘๋™
sha256 manifest first record 61 files / 13569 bytes JSON
manifest verify "OK: 61 files unchanged"
ํŠธ๋ ˆ์ด๋”ฉ ๋ฌด์ค‘๋‹จ gunja-pair-hedge / multi-tenant / gate-pair-hedge / gate-admin active active
hospital ๋‹ค๋ฅธ PM2 28 online (27 + agency-v2-api 1)

โณ B. ์ •์ง PENDING (๊ฒ€์ฆ ๋ถˆ๊ฐ€ / ๋ณ„๋„ sprint)

# ํ•ญ๋ชฉ ์‚ฌ์œ 
1 agency_db (v1) DROP starium_postgres connection pool ๋งŒ์„ฑ ๊ฐ€๋“ (40/40 retry fail). PG max_connections ์กฐ์ • ๋˜๋Š” restart sprint ํ•„์š”. agency_v2 ๋ณ„๋„ DB๋ผ ๋น„์ฆˆ๋‹ˆ์Šค ์˜ํ–ฅ 0
2 Docker ์•ฑ ์ปจํ…Œ์ด๋„ˆ build Dockerfile ์ž‘์„ฑ๋์œผ๋‚˜ build ๋ฏธ์‹œ๋„. ํ˜„์žฌ PM2 ํ˜ธ์ŠคํŠธ ์ง์ ‘ ์‹คํ–‰ โ€” ๋ณด์•ˆ spec L3 (Docker ๊ฒฉ๋ฆฌ)๋Š” S5๋กœ ๋ฏธ๋ฃธ (๋ณ€๊ฒฝ ์ž‘์•„์ง ํ›„ ์ผ๊ด„ ์ปจํ…Œ์ด๋„ˆํ™”)
3 Multi-tenant Prisma middleware Auth + Agencies ์™ธ ๋ชจ๋“ˆ ์ถ”๊ฐ€ (clients/leads ๋“ฑ) ์‹œ ํ•จ๊ป˜ โ€” S3
4 OAuth ์‹ค client (Google/Kakao/Naver) ์‚ฌ์šฉ์ž ์™ธ๋ถ€ ์ฝ˜์†” ์•ก์…˜ (Phase 2)
5 LLM API key (xAI/Gemini/Groq) ์‚ฌ์šฉ์ž ์™ธ๋ถ€ ์•ก์…˜, S3์—์„œ ์‚ฌ์šฉ
6 hospital-tamper-watch path ์ถ”๊ฐ€ ๋ณด์•ˆ spec L1, ๋ณ„๋„ ๋ณด์•ˆ sprint
7 Web Next.js S5
8 bot-connector S3

โš ๏ธ C. ์ง„ํ–‰ ์ค‘ ํ•ด๊ฒฐํ•œ ์ด์Šˆ (4๊ฑด)

  1. minio port 9051 ์ถฉ๋Œ (๋‹ค๋ฅธ docker-proxy ์ ์œ ) โ†’ 9061/9062 ๋ณ€๊ฒฝ, .env + compose ์ผ๊ด„ ๊ฐฑ์‹ 
  2. Prisma 5.22 inline enum 158 validation error โ†’ Python regex๋กœ multi-line ๋ณ€ํ™˜
  3. Schema LeadScore ์ค‘๋ณต ์ •์˜ (S1-fix ์ž”์žฌ) โ†’ ๊น”๋”ํžˆ ์žฌ์ž‘์„ฑ (410 lines)
  4. DB UPDATE shell escape โ†’ docker exec -i ... <<< "..." ๋˜๋Š” heredoc๋กœ ํ•ด๊ฒฐ

3. E2E ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ (์‹ค ํ˜ธ์ถœ ๊ฒฐ๊ณผ)

[1] POST /api/v1/auth/register
    body: {email, password, name}
    โ†’ 201 Created
    โ†’ {accessToken, user: {sub, email, role: "STAFF", agencyId: null}}

[2] POST /api/v1/auth/login
    โ†’ 200 OK + JWT 263 chars

[3] GET /api/v1/agencies (STAFF JWT)
    โ†’ 403 Forbidden + {message: "insufficient_role"}
    โœ… RolesGuard ์ •์ƒ ๋™์ž‘

[4] DB: UPDATE "User" SET role='SUPER_ADMIN' WHERE email=...
    โ†’ UPDATE 1

[5] POST /api/v1/auth/login (์žฌ๋กœ๊ทธ์ธ)
    โ†’ 200 + JWT (role: "SUPER_ADMIN")

[6] POST /api/v1/agencies (SUPER_ADMIN JWT)
    body: {name, slug}
    โ†’ 201 Created + {id, name, slug, status: "ACTIVE", createdAt, updatedAt}

[7] GET /api/v1/agencies โ†’ 200 + 2 agencies array
[8] GET /api/v1/agencies/:id โ†’ 200 + single
[9] PATCH /api/v1/agencies/:id โ†’ 200 + updated name
[10] DELETE /api/v1/agencies/:id โ†’ 200 + status: "ARCHIVED" (soft delete)

[11] DB SELECT verify
     test-alpha: ARCHIVED / test-beta: ACTIVE

4. ๋ณด์•ˆ ์ ์šฉ (S2-B ๋‹จ๊ณ„)

๋ณด์•ˆ spec layer ์ ์šฉ ์ƒํƒœ
L1 ld.so.preload immutable hospital ํ˜ธ์ŠคํŠธ ๊ทธ๋Œ€๋กœ (S1 sprint ๊ฒฐ๊ณผ)
L2 SSH ๊ฐ•ํ™” ๋ณ„๋„ sprint (fail2ban ๋“ฑ)
L3 Docker ๊ฒฉ๋ฆฌ data services๋งŒ Docker, ์•ฑ์€ PM2 โ€” S5์—์„œ ์ผ๊ด„ ์ปจํ…Œ์ด๋„ˆํ™”
L4 npm audit + sha256 manifest manifest ์ฒซ record (61 files OK) โœ…
L5 DB user 4-tier super/rw/ro/audit ์ƒ์„ฑ + DATABASE_URL=rw ์ ์šฉ โœ…
L6 ์‹œํฌ๋ฆฟ ๊ด€๋ฆฌ .env 0600 / OAuth/LLM placeholder. sops/age๋Š” Phase 2
L7 nginx WAF conf ์ž‘์„ฑ๋จ, ํ™œ์„ฑํ™”๋Š” ์‚ฌ์šฉ์ž ๋˜๋Š” S3
L8 auditd ๋ณ„๋„ sprint
L9 OAuth scope ์ตœ์†Œ ํด๋ผ์ด์–ธํŠธ ๋ฐœ๊ธ‰ ํ›„ ์ ์šฉ
L10 Build server ๋ถ„๋ฆฌ ํ˜ธ์ŠคํŠธ build (paperclip ๋˜๋Š” GitHub Actions๋กœ ์ด์ „ ๋Œ€๊ธฐ)
L11 health monitor cron health endpoint ์ž‘์„ฑ๋จ, cron ๋“ฑ๋ก์€ ๋ณ„๋„

โ†’ L4 + L5 ๋ณธ sprint ์™„์„ฑ. ๋‚˜๋จธ์ง€ ์ ์ง„.

5. ์‚ฐ์ถœ๋ฌผ (paperclip + hospital)

paperclip ์ •๋ณธ

hospital ์‚ฐ์ถœ๋ฌผ

6. S3 ๋‹ค์Œ ์ž‘์—… (์‹ ํ˜ธ ๋ฐ›์œผ๋ฉด ์ง„ํ–‰)

  1. agency_db (v1) DROP โ€” starium_postgres ๋ณ„๋„ ์ฒ˜๋ฆฌ sprint
  2. clients/staff/campaigns/leads/content 5 ๋ชจ๋“ˆ + Multi-tenant Prisma middleware
  3. AI orchestrator ํŒจํ‚ค์ง€ (LLM fallback chain) ๊ตฌํ˜„
  4. ๋ฆฌ๋“œ ์Šค์ฝ”์–ด๋ง AI (BullMQ + LLM call)
  5. ๋ชจ๋‹ ๋ธŒ๋ฆฌํ•‘ AI (cron 09:00 KST + Telegram)
  6. nginx ํ™œ์„ฑํ™” (agency.starium.site SSL + sites-enabled ๋“ฑ๋ก)
  7. OAuth ์‹ค client (์‚ฌ์šฉ์ž ์™ธ๋ถ€ ์•ก์…˜ ํ›„ ํ†ตํ•ฉ)
  8. Web Next.js (S5)

7. ์•ˆ์ „ (S2-B ๋™์•ˆ 0 ์˜ํ–ฅ)

8. ๊ฒฐ๋ก 

S2-B = 23๊ฑด ์‹ค์ธก PASS + 4 ์ด์Šˆ ํ•ด๊ฒฐ + 8 PENDING ์ •์ง ๋ช…์‹œ.

agency-v2๊ฐ€ ์‹ค ์ž‘๋™ํ•˜๋Š” NestJS API + PG + Redis + MinIO + JWT Auth + Role-based access control + 4-tier DB user + integrity manifest ๊นŒ์ง€ ์™„์„ฑ๋จ. CRUD 6๊ฐœ ์•ก์…˜ ๋ชจ๋‘ E2E PASS.

S3์—์„œ ํ•ต์‹ฌ ๋ชจ๋“ˆ ์ถ”๊ฐ€ ์‹œ์ ์— Multi-tenant middleware ์ ์šฉ + AI orchestrator ๊ฐ€๋™ โ†’ ๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜ ์ฆ‰์‹œ ์ฒด๊ฐ ์‹œ์ž‘.

โ€” S2-B ๋ณด๊ณ  ๋ โ€”