Skip to content

๐Ÿš€ Release : BoardPort v1.2.1 ์•ˆ์ •ํ™” ๋ฐ˜์˜#60

Merged
DoHeonLim merged 10 commits into
masterfrom
develop
Jun 29, 2026
Merged

๐Ÿš€ Release : BoardPort v1.2.1 ์•ˆ์ •ํ™” ๋ฐ˜์˜#60
DoHeonLim merged 10 commits into
masterfrom
develop

Conversation

@DoHeonLim

Copy link
Copy Markdown
Owner

Summary

BoardPort v1.2.0 ๋ฆด๋ฆฌ์ฆˆ ์ดํ›„ ํ™•์ธํ•œ ์ ‘๊ทผ ๊ฒฝ๊ณ„, ๋ฌธ์„œ ๊ตฌ์กฐ, ํ•ต์‹ฌ E2E, ์ธ์ฆ rate limit, SMS ํ† ํฐ ์†Œ์œ ๊ถŒ ๊ฒฝ๊ณ„๋ฅผ ์ •๋ฆฌํ•œ ์•ˆ์ •ํ™” ํŒจ์น˜์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ PR์€ ์‹ ๊ทœ ๋Œ€ํ˜• ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ฆด๋ฆฌ์ฆˆ ํ›„ ์ฝ”๋“œ ๊ฐ์‚ฌ์™€ QA ๊ณผ์ •์—์„œ ํ™•์ธํ•œ ์šด์˜ ์•ˆ์ •์„ฑยท๋ณด์•ˆ ๊ฒฝ๊ณ„ยท๊ฒ€์ฆ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋ณด๊ฐ•ํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.

Changes

[๐Ÿ” Access Boundary]

  • ์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API์˜ viewerId query fallback์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชฉ๋ก ์กฐํšŒ ๊ธฐ์ค€์„ ์„œ๋ฒ„ ์„ธ์…˜ ID๋กœ ๊ณ ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Cloudflare Stream/Destination webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Stream/VOD ๊ฐœ์ธํ™” query key์™€ ํŒ”๋กœ์ž‰ cache seed ์ •ํ•ฉ์„ฑ์„ ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค.

[๐Ÿ“š Docs]

  • ๋ฃจํŠธ README ์ฒซ ํ™”๋ฉด์„ ํ•ต์‹ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ ์‚ฌ๋ก€ ์ค‘์‹ฌ์œผ๋กœ ์••์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • docs ์ถ”์ฒœ ์ฝ๊ธฐ ์ˆœ์„œ๋ฅผ README ํ•ต์‹ฌ ์‚ฌ๋ก€์™€ ๋งž๊ฒŒ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ถŒํ•œ / ์ ‘๊ทผ ์ œ์–ด ๋งคํŠธ๋ฆญ์Šค ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • rate limit ์šด์˜ ์ •์ฑ…๊ณผ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์˜ˆ์‹œ๋ฅผ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

[โ˜”๏ธ E2E]

  • ์ฑ„ํŒ… ์•ฝ์† ์ˆ˜๋ฝ ํ›„ ์ƒํ’ˆ ์˜ˆ์•ฝ ์ƒํƒœ ์œ ์ง€ ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒํ’ˆ ๋ชจ๋‹ฌ ์ƒ์„ธ ์ˆ˜์ • ํ›„ ์›๋ž˜ ๋ชฉ๋ก ๋ฌธ๋งฅ ๋ณต๊ท€ ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํŒ”๋กœ์šฐ ํ›„ ํŒ”๋กœ์›Œ ์ „์šฉ VOD ์ ‘๊ทผ ์ƒํƒœ ์ˆ˜๋ ด ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

[๐Ÿ” Auth / Rate Limit]

  • SMS ์ธ์ฆ๋ฒˆํ˜ธ TTL, ์žฌ์ „์†ก ์ฟจ๋‹ค์šด, IP ๊ธฐ์ค€ ๋ฐœ์†ก ์š”์ฒญ ์ œํ•œ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํšŒ์›๊ฐ€์ž… ์ œ์ถœ IP ๊ธฐ์ค€ ๋‹จ๊ธฐ ์ œํ•œ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • rate limit ์‹๋ณ„์ž๋ฅผ RATE_LIMIT_SALT ๊ธฐ๋ฐ˜ HMAC hash๋กœ ์ €์žฅํ•˜๋„๋ก ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • PostgreSQL advisory lock์œผ๋กœ rate limit ๊ธฐ๋ก ๊ฒฝํ•ฉ์„ ์ง๋ ฌํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

[๐Ÿ“ฑ SMS]

  • SMS ๋กœ๊ทธ์ธ ํ† ํฐ์ด ์ธ์ฆ ์ „ User.phone์„ ์ ์œ ํ•˜์ง€ ์•Š๋„๋ก ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • SMS ๋กœ๊ทธ์ธ ํ† ํฐ๊ณผ ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ ์ธ์ฆ ํ† ํฐ์˜ userId ๋ชฉ์ ์„ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœํ•„ ์ธ์ฆ ํ† ํฐ์ด SMS ๋กœ๊ทธ์ธ ๊ฒ€์ฆ์—์„œ ์†Œ๋น„๋˜์ง€ ์•Š๋„๋ก ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • SMS ๋ฐœ์†ก ์‹คํŒจ ์‹œ ์ด์ „ ํ† ํฐ ์ƒํƒœ์™€ userId๋ฅผ ์กฐ๊ฑด๋ถ€ ๋ณต๊ตฌํ•˜๋„๋ก ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค.

[โ˜๏ธ Upload]

  • Cloudflare Images direct upload URL ๋ฐœ๊ธ‰ ์ „ ์„ธ์…˜๊ณผ ์‚ฌ์šฉ์ž ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋„๋ก ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋น„๋กœ๊ทธ์ธ/์ •์ง€ ์‚ฌ์šฉ์ž upload URL ๋ฐœ๊ธ‰ ๊ฑฐ์ ˆ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

[๐Ÿ—„๏ธ Database / Env]

  • SMSToken expires_at migration์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • AuthRateLimitEvent ๋ชจ๋ธ๊ณผ ์กฐํšŒ/์ •๋ฆฌ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • SMSToken.userId nullable ์ „ํ™˜ migration์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • RATE_LIMIT_SALT ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

Verification

  • GitHub Actions
  • Vercel Preview
  • npm run test
  • npm run test:e2e
  • npm run build
  • npx prisma validate
  • git diff --check

Deployment Notes

  • Production ๋ฐฐํฌ ์‹œ Prisma migration ์ ์šฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • Vercel Preview/Production ํ™˜๊ฒฝ์— RATE_LIMIT_SALT๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

DoHeonLim added 10 commits June 26, 2026 11:46
[๐Ÿ”Ž Audit]

/api/* ๊ฒฝ๋กœ๊ฐ€ middleware ์ธ์ฆ ๊ฐ€๋“œ๋ฅผ ํƒ€์ง€ ์•Š๋Š” ๊ตฌ์กฐ์—์„œ ๋ชฉ๋ก API๊ฐ€ ์ž์ฒด ๊ถŒํ•œ ๊ฒฝ๊ณ„๋ฅผ ์ฑ…์ž„ํ•˜๋„๋ก ์ •๋ฆฌ
๋น„๋กœ๊ทธ์ธ ์ง์ ‘ API ์š”์ฒญ์ด query viewerId๋กœ ํƒ€์ธ์˜ ๊ฐœ์ธํ™” ์กฐํšŒ์ž ์ปจํ…์ŠคํŠธ๋ฅผ ํ‰๋‚ด๋‚ผ ์ˆ˜ ์žˆ๋˜ ๊ฒฝ๋กœ ์ฐจ๋‹จ
Cloudflare webhook secret ๋ˆ„๋ฝ ์‹œ production์—์„œ ๊ฒ€์ฆ์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋˜ fail-open ๊ฐ€๋Šฅ์„ฑ ์ฐจ๋‹จ
Stream/VOD ๊ฐœ์ธํ™” ์‘๋‹ต์„ query key๊ฐ€ ์ถฉ๋ถ„ํžˆ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋˜ ์บ์‹œ ์ •ํ•ฉ์„ฑ ๋ณด๊ฐ•

[๐ŸŽฅ Stream]

์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API์˜ viewerId query fallback ์ œ๊ฑฐ
๋ชฉ๋ก ์กฐํšŒ ๊ถŒํ•œ ๊ธฐ์ค€์„ ์„œ๋ฒ„ ์„ธ์…˜ ID๋กœ ๊ณ ์ •
๋น„๋กœ๊ทธ์ธ ๋ชฉ๋ก API ์ง์ ‘ ํ˜ธ์ถœ ์‹œ ๋นˆ ๋ชฉ๋ก์œผ๋กœ ์ข…๋ฃŒ
Server Action ๋ชฉ๋ก ์กฐํšŒ๋„ ์„ธ์…˜ ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ query key์— viewerId์™€ ํŒ”๋กœ์ž‰ ํ•„ํ„ฐ ๋ฐ˜์˜
ํŒ”๋กœ์šฐ ์งํ›„ ๊ธฐ๋ณธ ํŒ”๋กœ์ž‰ ์ŠคํŠธ๋ฆผ cache seed key๋ฅผ ์กฐํšŒ์ž๋ณ„ key์™€ ์ผ์น˜์‹œํ‚ด

[โ˜๏ธ Webhook]

Cloudflare Stream webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์ฒ˜๋ฆฌ
Cloudflare Destination webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์ฒ˜๋ฆฌ
ํ•ธ๋“œ์…ฐ์ดํฌ์™€ ๋นˆ body ์š”์ฒญ์€ ์ƒํƒœ ๋ณ€๊ฒฝ ์—†์ด ๊ธฐ์กด ํ†ต๊ณผ ์ •์ฑ… ์œ ์ง€
secret ๋ˆ„๋ฝ ์ •์ฑ…์„ webhookAuth ์œ ํ‹ธ๋กœ ๋ถ„๋ฆฌ

[โ˜”๏ธ Test]

์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API viewerId spoof ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
์„ธ์…˜์ด ์žˆ์„ ๋•Œ query viewerId๋ณด๋‹ค session ID๊ฐ€ ์šฐ์„ ๋˜๋Š”์ง€ ๊ฒ€์ฆ
production webhook secret ๋ˆ„๋ฝ ์‹œ DB ๊ฐฑ์‹ ๊ณผ Realtime ์†ก์‹ ์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”์ง€ ๊ฒ€์ฆ
webhook secret ์ •์ฑ… ์œ ํ‹ธ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[โœ… Verification]

์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API์˜ ๋น„๋กœ๊ทธ์ธ ๊ณต๊ฐœ ์†Œ๋น„์ฒ˜๊ฐ€ ์—†์Œ์„ grep์œผ๋กœ ํ™•์ธ
npx tsc --noEmit
npm run lint
npm run test # 23 files / 107 tests
npm run build
npm run test:e2e # 7 passed / 19 skipped
git diff --check
[๐Ÿ”Ž Audit]
 - /api/* ๊ฒฝ๋กœ๊ฐ€ middleware ์ธ์ฆ ๊ฐ€๋“œ๋ฅผ ํƒ€์ง€ ์•Š๋Š” ๊ตฌ์กฐ์—์„œ ๋ชฉ๋ก API๊ฐ€ ์ž์ฒด ๊ถŒํ•œ ๊ฒฝ๊ณ„๋ฅผ ์ฑ…์ž„ํ•˜๋„๋ก ์ •๋ฆฌ 
 - ๋น„๋กœ๊ทธ์ธ ์ง์ ‘ API ์š”์ฒญ์ด query viewerId๋กœ ํƒ€์ธ์˜ ๊ฐœ์ธํ™” ์กฐํšŒ์ž ์ปจํ…์ŠคํŠธ๋ฅผ ํ‰๋‚ด๋‚ผ ์ˆ˜ ์žˆ๋˜ ๊ฒฝ๋กœ ์ฐจ๋‹จ
 - Cloudflare webhook secret ๋ˆ„๋ฝ ์‹œ production์—์„œ ๊ฒ€์ฆ์„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ๋˜ fail-open ๊ฐ€๋Šฅ์„ฑ ์ฐจ๋‹จ
 - Stream/VOD ๊ฐœ์ธํ™” ์‘๋‹ต์„ query key๊ฐ€ ์ถฉ๋ถ„ํžˆ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋˜ ์บ์‹œ ์ •ํ•ฉ์„ฑ ๋ณด๊ฐ•

[๐ŸŽฅ Stream]
 - ์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API์˜ viewerId query fallback ์ œ๊ฑฐ
 - ๋ชฉ๋ก ์กฐํšŒ ๊ถŒํ•œ ๊ธฐ์ค€์„ ์„œ๋ฒ„ ์„ธ์…˜ ID๋กœ ๊ณ ์ •
 - ๋น„๋กœ๊ทธ์ธ ๋ชฉ๋ก API ์ง์ ‘ ํ˜ธ์ถœ ์‹œ ๋นˆ ๋ชฉ๋ก์œผ๋กœ ์ข…๋ฃŒ
 - Server Action ๋ชฉ๋ก ์กฐํšŒ๋„ ์„ธ์…˜ ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
 - ์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ query key์— viewerId์™€ ํŒ”๋กœ์ž‰ ํ•„ํ„ฐ ๋ฐ˜์˜
 - ํŒ”๋กœ์šฐ ์งํ›„ ๊ธฐ๋ณธ ํŒ”๋กœ์ž‰ ์ŠคํŠธ๋ฆผ cache seed key๋ฅผ ์กฐํšŒ์ž๋ณ„ key์™€ ์ผ์น˜์‹œํ‚ด

[โ˜๏ธ Webhook]
 - Cloudflare Stream webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์ฒ˜๋ฆฌ 
 - Cloudflare Destination webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์ฒ˜๋ฆฌ
 - ํ•ธ๋“œ์…ฐ์ดํฌ์™€ ๋นˆ body ์š”์ฒญ์€ ์ƒํƒœ ๋ณ€๊ฒฝ ์—†์ด ๊ธฐ์กด ํ†ต๊ณผ ์ •์ฑ… ์œ ์ง€
 - secret ๋ˆ„๋ฝ ์ •์ฑ…์„ webhookAuth ์œ ํ‹ธ๋กœ ๋ถ„๋ฆฌ

[โ˜”๏ธ Test]
 - ์ŠคํŠธ๋ฆผ/๋‹ค์‹œ๋ณด๊ธฐ ๋ชฉ๋ก API viewerId spoof ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
 - ์„ธ์…˜์ด ์žˆ์„ ๋•Œ query viewerId๋ณด๋‹ค session ID๊ฐ€ ์šฐ์„ ๋˜๋Š”์ง€ ๊ฒ€์ฆ
 - production webhook secret ๋ˆ„๋ฝ ์‹œ DB ๊ฐฑ์‹ ๊ณผ Realtime ์†ก์‹ ์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”์ง€ ๊ฒ€์ฆ webhook secret ์ •์ฑ… ์œ ํ‹ธ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[โœ… Verification]
 - npx tsc --noEmit
 - npm run lint
 - npm run test # 23 files / 107 tests
 - npm run build
 - npm run test:e2e # 7 passed / 19 skipped
 - git diff --check
- [๐Ÿ“Œ README]

- ์ฒซ ํ™”๋ฉด์— Key Engineering Problems ์„น์…˜ ์ถ”๊ฐ€
- ๋Œ€ํ‘œ ๋ฌธ์ œ ํ•ด๊ฒฐ ์‚ฌ๋ก€๋ฅผ ์ฆ์ƒ/ํ•ด๊ฒฐ ๋ฐฉํ–ฅ/๋ฌธ์„œ ๋งํฌ ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
- Demo ์•ž๋’ค ํ๋ฆ„์„ ์„œ๋น„์Šค ์†Œ๊ฐœ, ํ•ต์‹ฌ ๋ฌธ์ œ, ๋ฐ๋ชจ, Feature Flow ์ˆœ์„œ๋กœ ์žฌ๋ฐฐ์น˜
- Recommended Reading์„ ํ•ต์‹ฌ ์„ค๊ณ„์™€ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฌธ์„œ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌ

- [๐Ÿ›ก๏ธ Access Control]

- ๊ถŒํ•œ / ์ ‘๊ทผ ์ œ์–ด ๋งคํŠธ๋ฆญ์Šค ๋ฌธ์„œ ์ถ”๊ฐ€
- ํŽ˜์ด์ง€, Route Handler, Server Action, service ๊ณ„์ธต์˜ ๋ณดํ˜ธ ๊ธฐ์ค€ ์ •๋ฆฌ
- ๋„๋ฉ”์ธ๋ณ„ ์ฃผ์š” ํ–‰์œ„์™€ ๋Œ€ํ‘œ ๊ถŒํ•œ ๊ฐ€๋“œ ์ •๋ฆฌ
- Webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์›์น™ ๋ฌธ์„œํ™”

- [๐Ÿ“š Docs Index]

- docs ์ถ”์ฒœ ์ฝ๋Š” ์ˆœ์„œ๋ฅผ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”์™€ ํ•ต์‹ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ ์‚ฌ๋ก€ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌ
- ๊ถŒํ•œ ๋งคํŠธ๋ฆญ์Šค์™€ PWA Push ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ ์‚ฌ๋ก€๋กœ ์—ฐ๊ฒฐ

- [โœ… Verification]

- git diff --check
- [๐Ÿ“Œ README]

- ์ฒซ ํ™”๋ฉด์— Key Engineering Problems ์„น์…˜ ์ถ”๊ฐ€
- ๋Œ€ํ‘œ ๋ฌธ์ œ ํ•ด๊ฒฐ ์‚ฌ๋ก€๋ฅผ ์ฆ์ƒ/ํ•ด๊ฒฐ ๋ฐฉํ–ฅ/๋ฌธ์„œ ๋งํฌ ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
- Demo ์•ž๋’ค ํ๋ฆ„์„ ์„œ๋น„์Šค ์†Œ๊ฐœ, ํ•ต์‹ฌ ๋ฌธ์ œ, ๋ฐ๋ชจ, Feature Flow ์ˆœ์„œ๋กœ ์žฌ๋ฐฐ์น˜
- Recommended Reading์„ ํ•ต์‹ฌ ์„ค๊ณ„์™€ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฌธ์„œ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌ

- [๐Ÿ›ก๏ธ Access Control]

- ๊ถŒํ•œ / ์ ‘๊ทผ ์ œ์–ด ๋งคํŠธ๋ฆญ์Šค ๋ฌธ์„œ ์ถ”๊ฐ€
- ํŽ˜์ด์ง€, Route Handler, Server Action, service ๊ณ„์ธต์˜ ๋ณดํ˜ธ ๊ธฐ์ค€ ์ •๋ฆฌ
- ๋„๋ฉ”์ธ๋ณ„ ์ฃผ์š” ํ–‰์œ„์™€ ๋Œ€ํ‘œ ๊ถŒํ•œ ๊ฐ€๋“œ ์ •๋ฆฌ
- Webhook secret ๋ˆ„๋ฝ ์‹œ production fail-closed ์›์น™ ๋ฌธ์„œํ™”

- [๐Ÿ“š Docs Index]

- docs ์ถ”์ฒœ ์ฝ๋Š” ์ˆœ์„œ๋ฅผ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”์™€ ํ•ต์‹ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ ์‚ฌ๋ก€ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌ
- ๊ถŒํ•œ ๋งคํŠธ๋ฆญ์Šค์™€ PWA Push ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ ์‚ฌ๋ก€๋กœ ์—ฐ๊ฒฐ

- [โœ… Verification]

- git diff --check
[๐Ÿ’ฌ Chat]

์ฑ„ํŒ… ์•ฝ์† ์ˆ˜๋ฝ ํ›„ ํ™•์ • ์ƒํƒœ์™€ ์ƒํ’ˆ ์˜ˆ์•ฝ ์ƒํƒœ ์œ ์ง€ ๊ฒ€์ฆ
์ˆ˜๋ฝ ํ›„ ์ƒˆ๋กœ๊ณ ์นจ๊ณผ ์ƒํ’ˆ ๋ชฉ๋ก/์ƒ์„ธ ์ง„์ž…์—์„œ๋„ ์˜ˆ์•ฝ ์ƒํƒœ ํ™•์ธ

[๐Ÿ›๏ธ Product]

์ƒํ’ˆ ๋ชฉ๋ก์—์„œ ๋ชจ๋‹ฌ ์ƒ์„ธ ์ง„์ž… ํ›„ ์ˆ˜์ • ์ €์žฅ ๊ฒ€์ฆ
์ˆ˜์ • ํ›„ ์›๋ž˜ ๋ชฉ๋ก ๊ฒ€์ƒ‰ ๋ฌธ๋งฅ ๋ณต๊ท€์™€ ๋ณ€๊ฒฝ ๋‚ด์šฉ ๋ฐ˜์˜ ํ™•์ธ

[๐ŸŽฅ Stream]

ํŒ”๋กœ์šฐ ํ›„ ํŒ”๋กœ์›Œ ์ „์šฉ VOD ์ž ๊ธˆ ํ•ด์ œ ๊ฒ€์ฆ
ํŒ”๋กœ์ž‰ ๋ชฉ๋ก ๋…ธ์ถœ๊ณผ VOD ์ƒ์„ธ ์ ‘๊ทผ๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์ƒํƒœ ์ˆ˜๋ ด ํ™•์ธ

[๐Ÿงช E2E Seed]

์•ฝ์† ์ˆ˜๋ฝ ์ƒํ’ˆ ์ƒํƒœ ๋ณต์› seed ๋ณด๊ฐ•
๋ชจ๋‹ฌ ์ˆ˜์ • ์ƒํ’ˆ๊ณผ ํŒ”๋กœ์›Œ ์ „์šฉ VOD seed ์ถ”๊ฐ€
E2E ๊ณ„์ • ๊ฐ„ follow ๊ด€๊ณ„ cleanup ์ถ”๊ฐ€
์ƒํƒœ ๋ณ€๊ฒฝํ˜• E2E ๋ถ„๋ฆฌ ์‹คํ–‰ ์‹œ seed ์žฌ์‹คํ–‰ ๊ธฐ์ค€ ๋ฌธ์„œํ™”

[โœ… Verification]

git diff --check
npx tsc --noEmit
npm run lint
npm run test # 23 files / 107 tests
E2E_SEEDED=1 npm run test:e2e -- tests/e2e/chat-appointment.spec.ts tests/e2e/product-modal-edit.spec.ts tests/e2e/stream-follow-access.spec.ts --project=chromium # 3 passed
E2E_SEEDED=1 npm run test:e2e -- --project=chromium # 28 passed
npm run cleanup:e2e
[๐Ÿ’ฌ Chat]
- ์ฑ„ํŒ… ์•ฝ์† ์ˆ˜๋ฝ ํ›„ ํ™•์ • ์ƒํƒœ์™€ ์ƒํ’ˆ ์˜ˆ์•ฝ ์ƒํƒœ ์œ ์ง€ ๊ฒ€์ฆ
- ์ˆ˜๋ฝ ํ›„ ์ƒˆ๋กœ๊ณ ์นจ๊ณผ ์ƒํ’ˆ ๋ชฉ๋ก/์ƒ์„ธ ์ง„์ž…์—์„œ๋„ ์˜ˆ์•ฝ ์ƒํƒœ ํ™•์ธ

[๐Ÿ›๏ธ Product]
- ์ƒํ’ˆ ๋ชฉ๋ก์—์„œ ๋ชจ๋‹ฌ ์ƒ์„ธ ์ง„์ž… ํ›„ ์ˆ˜์ • ์ €์žฅ ๊ฒ€์ฆ
- ์ˆ˜์ • ํ›„ ์›๋ž˜ ๋ชฉ๋ก ๊ฒ€์ƒ‰ ๋ฌธ๋งฅ ๋ณต๊ท€์™€ ๋ณ€๊ฒฝ ๋‚ด์šฉ ๋ฐ˜์˜ ํ™•์ธ

[๐ŸŽฅ Stream]
- ํŒ”๋กœ์šฐ ํ›„ ํŒ”๋กœ์›Œ ์ „์šฉ VOD ์ž ๊ธˆ ํ•ด์ œ ๊ฒ€์ฆ
- ํŒ”๋กœ์ž‰ ๋ชฉ๋ก ๋…ธ์ถœ๊ณผ VOD ์ƒ์„ธ ์ ‘๊ทผ๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์ƒํƒœ ์ˆ˜๋ ด ํ™•์ธ

[๐Ÿงช E2E Seed]
- ์•ฝ์† ์ˆ˜๋ฝ ์ƒํ’ˆ ์ƒํƒœ ๋ณต์› seed ๋ณด๊ฐ•
- ๋ชจ๋‹ฌ ์ˆ˜์ • ์ƒํ’ˆ๊ณผ ํŒ”๋กœ์›Œ ์ „์šฉ VOD seed ์ถ”๊ฐ€
- E2E ๊ณ„์ • ๊ฐ„ follow ๊ด€๊ณ„ cleanup ์ถ”๊ฐ€
- ์ƒํƒœ ๋ณ€๊ฒฝํ˜• E2E ๋ถ„๋ฆฌ ์‹คํ–‰ ์‹œ seed ์žฌ์‹คํ–‰ ๊ธฐ์ค€ ๋ฌธ์„œํ™”

[โœ… Verification]
- git diff --check
- npx tsc --noEmit
- npm run lint
- npm run test # 23 files / 107 tests
- E2E_SEEDED=1 npm run test:e2e -- tests/e2e/chat-appointment.spec.ts tests/e2e/product-modal-edit.spec.ts tests/e2e/stream-follow-access.spec.ts --project=chromium # 3 passed
- E2E_SEEDED=1 npm run test:e2e -- --project=chromium # 28 passed
- npm run cleanup:e2e"
[๐Ÿ” Auth / Rate Limit]

- SMS ์ธ์ฆ๋ฒˆํ˜ธ TTL ์ถ”๊ฐ€
- SMS ์žฌ์ „์†ก ์ฟจ๋‹ค์šด๊ณผ IP ๊ธฐ์ค€ ๋ฐœ์†ก ์š”์ฒญ ์ œํ•œ ์ถ”๊ฐ€
- ํšŒ์›๊ฐ€์ž… ์ œ์ถœ IP ๊ธฐ์ค€ ๋‹จ๊ธฐ ์ œํ•œ ์ถ”๊ฐ€
- rate limit ์‹๋ณ„์ž๋Š” RATE_LIMIT_SALT ๊ธฐ๋ฐ˜ HMAC hash๋กœ ์ €์žฅ
- kind/keyHash ๋‹จ์œ„ advisory lock์œผ๋กœ rate limit ๊ธฐ๋ก ๊ฒฝํ•ฉ ์ง๋ ฌํ™”

[๐Ÿ“ฑ SMS]

- ๋กœ๊ทธ์ธ SMS ํ† ํฐ ๊ต์ฒด๋ฅผ ์กฐ๊ฑด๋ถ€ updateMany ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
- provider ์‹คํŒจ ์‹œ ๊ธฐ์กด ํ† ํฐ ๋ณต๊ตฌ ๋˜๋Š” ์‹ ๊ทœ ํ† ํฐ ์‚ญ์ œ
- ๋งŒ๋ฃŒ๋œ SMS ์ธ์ฆ๋ฒˆํ˜ธ ๊ฒ€์ฆ ๊ฑฐ์ ˆ ๋ฐ ์ง€์—ฐ ์ •๋ฆฌ
- ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ ์ธ์ฆ์—๋„ ๋™์ผํ•œ TTL/์ฟจ๋‹ค์šด/IP ์ œํ•œ/์‹คํŒจ ๋ณต๊ตฌ ์ •์ฑ… ์ ์šฉ

[โ˜๏ธ Upload]

- Cloudflare Images direct upload URL ๋ฐœ๊ธ‰ ์ „ ์„ธ์…˜ ํ™•์ธ
- ์‚ฌ์šฉ์ž ์ƒํƒœ ๊ฒ€์ฆ ํ›„ Cloudflare API ํ˜ธ์ถœ
- ๋น„๋กœ๊ทธ์ธ/์ •์ง€ ์‚ฌ์šฉ์ž upload URL ๋ฐœ๊ธ‰ ๊ฑฐ์ ˆ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[๐Ÿ—„๏ธ Database]

- SMSToken expires_at ์ถ”๊ฐ€ ๋ฐ ๊ธฐ์กด ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ backfill
- AuthRateLimitEvent ๋ชจ๋ธ ์ถ”๊ฐ€
- rate limit ์กฐํšŒ/cleanup์šฉ ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ถ”๊ฐ€

[๐Ÿ“š Docs]

- .env.example ์ถ”๊ฐ€
- README ํ•„์ˆ˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ‘œ ์ •๋ฆฌ
- Rate Limit ์šด์˜ ์ •์ฑ… ๋ฌธ์„œ ์ถ”๊ฐ€
- ๊ถŒํ•œ ๋งคํŠธ๋ฆญ์Šค์™€ docs ์ธ๋ฑ์Šค์— rate limit ๋ฌธ์„œ ์—ฐ๊ฒฐ

[โ˜”๏ธ Test]

- SMS TTL/์ฟจ๋‹ค์šด/IP ์ œํ•œ/๋™์‹œ ์š”์ฒญ/rollback ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ SMS ๋ณดํ˜ธ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- rate limit hash/advisory lock ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- Cloudflare Images upload URL ์ธ์ฆ ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[โœ… Verification]

- npx prisma migrate dev
- npx prisma generate
- git diff --check
- npm run lint
- npm run test # 27 files / 125 tests
- npx tsc --noEmit
- npm run build
- npx prisma validate
[๐Ÿ” Auth / Rate Limit]

- SMS ์ธ์ฆ๋ฒˆํ˜ธ TTL ์ถ”๊ฐ€
- SMS ์žฌ์ „์†ก ์ฟจ๋‹ค์šด๊ณผ IP ๊ธฐ์ค€ ๋ฐœ์†ก ์š”์ฒญ ์ œํ•œ ์ถ”๊ฐ€
- ํšŒ์›๊ฐ€์ž… ์ œ์ถœ IP ๊ธฐ์ค€ ๋‹จ๊ธฐ ์ œํ•œ ์ถ”๊ฐ€
- rate limit ์‹๋ณ„์ž๋Š” RATE_LIMIT_SALT ๊ธฐ๋ฐ˜ HMAC hash๋กœ ์ €์žฅ
- kind/keyHash ๋‹จ์œ„ advisory lock์œผ๋กœ rate limit ๊ธฐ๋ก ๊ฒฝํ•ฉ ์ง๋ ฌํ™”

[๐Ÿ“ฑ SMS]

- ๋กœ๊ทธ์ธ SMS ํ† ํฐ ๊ต์ฒด๋ฅผ ์กฐ๊ฑด๋ถ€ updateMany ๊ธฐ์ค€์œผ๋กœ ์ •๋ฆฌ
- provider ์‹คํŒจ ์‹œ ๊ธฐ์กด ํ† ํฐ ๋ณต๊ตฌ ๋˜๋Š” ์‹ ๊ทœ ํ† ํฐ ์‚ญ์ œ
- ๋งŒ๋ฃŒ๋œ SMS ์ธ์ฆ๋ฒˆํ˜ธ ๊ฒ€์ฆ ๊ฑฐ์ ˆ ๋ฐ ์ง€์—ฐ ์ •๋ฆฌ
- ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ ์ธ์ฆ์—๋„ ๋™์ผํ•œ TTL/์ฟจ๋‹ค์šด/IP ์ œํ•œ/์‹คํŒจ ๋ณต๊ตฌ ์ •์ฑ… ์ ์šฉ

[โ˜๏ธ Upload]

- Cloudflare Images direct upload URL ๋ฐœ๊ธ‰ ์ „ ์„ธ์…˜ ํ™•์ธ
- ์‚ฌ์šฉ์ž ์ƒํƒœ ๊ฒ€์ฆ ํ›„ Cloudflare API ํ˜ธ์ถœ
- ๋น„๋กœ๊ทธ์ธ/์ •์ง€ ์‚ฌ์šฉ์ž upload URL ๋ฐœ๊ธ‰ ๊ฑฐ์ ˆ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[๐Ÿ—„๏ธ Database]

- SMSToken expires_at ์ถ”๊ฐ€ ๋ฐ ๊ธฐ์กด ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ backfill
- AuthRateLimitEvent ๋ชจ๋ธ ์ถ”๊ฐ€
- rate limit ์กฐํšŒ/cleanup์šฉ ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ถ”๊ฐ€

[๐Ÿ“š Docs]

- .env.example ์ถ”๊ฐ€
- README ํ•„์ˆ˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ‘œ ์ •๋ฆฌ
- Rate Limit ์šด์˜ ์ •์ฑ… ๋ฌธ์„œ ์ถ”๊ฐ€
- ๊ถŒํ•œ ๋งคํŠธ๋ฆญ์Šค์™€ docs ์ธ๋ฑ์Šค์— rate limit ๋ฌธ์„œ ์—ฐ๊ฒฐ

[โ˜”๏ธ Test]

- SMS TTL/์ฟจ๋‹ค์šด/IP ์ œํ•œ/๋™์‹œ ์š”์ฒญ/rollback ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ SMS ๋ณดํ˜ธ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- rate limit hash/advisory lock ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- Cloudflare Images upload URL ์ธ์ฆ ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€

[โœ… Verification]

- npx prisma migrate dev
- npx prisma generate
- git diff --check
- npm run lint
- npm run test # 27 files / 125 tests
- npx tsc --noEmit
- npm run build
- npx prisma validate
[๐Ÿ“ฑ SMS]
 - SMS ๋กœ๊ทธ์ธ ํ† ํฐ์ด ์ธ์ฆ ์ „ User.phone์„ ์ ์œ ํ•˜์ง€ ์•Š๋„๋ก ์ •๋ฆฌ
 - SMS ๋กœ๊ทธ์ธ ํ† ํฐ๊ณผ ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ ์ธ์ฆ ํ† ํฐ์˜ userId ๋ชฉ์  ๋ถ„๋ฆฌ
 - ํ”„๋กœํ•„ ์ธ์ฆ ํ† ํฐ์ด SMS ๋กœ๊ทธ์ธ ๊ฒ€์ฆ์—์„œ ์†Œ๋น„๋˜์ง€ ์•Š๋„๋ก ์ฐจ๋‹จ
 - ๋กœ๊ทธ์ธ SMS ๋ฐœ์†ก ์‹คํŒจ ์‹œ ์ด์ „ ํ† ํฐ์˜ userId๊นŒ์ง€ ์กฐ๊ฑด๋ถ€ ๋ณต๊ตฌ

[๐Ÿ—„๏ธ Database]
 - SMSToken.userId nullable ์ „ํ™˜ migration ์ถ”๊ฐ€
 - ๋กœ๊ทธ์ธ SMS ํ† ํฐ์€ userId ์—†์ด ์ €์žฅํ•˜๊ณ , ๊ฒ€์ฆ ์„ฑ๊ณต ์‹œ ์ „ํ™”๋ฒˆํ˜ธ ๊ธฐ์ค€ User๋ฅผ ์กฐํšŒ ๋˜๋Š” ์ƒ์„ฑ

[๐Ÿ” Rate Limit]
 - ์˜ค๋ž˜๋œ rate limit ๊ธฐ๋ก ์‚ญ์ œ๋ฅผ advisory lock transaction ๋ฐ–์œผ๋กœ ๋ถ„๋ฆฌ
 - PostgreSQL transaction ๋‚ด๋ถ€ ์‚ญ์ œ ์‹คํŒจ๊ฐ€ ์ดํ›„ ์กฐํšŒ/๊ธฐ๋ก์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ์ •๋ฆฌ
 - rate limit ํŒ๋‹จ transaction์€ lock, ์ตœ๊ทผ ์š”์ฒญ ์กฐํšŒ, ํ˜„์žฌ ์š”์ฒญ ๊ธฐ๋ก๋งŒ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋‹จ์ˆœํ™”

[โ˜”๏ธ Test]
 - SMS ์ธ์ฆ ์ „ User ์ƒ์„ฑ ๋ฐฉ์ง€ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
 - ํ”„๋กœํ•„ ํ† ํฐ userId ์ž”์กด ๋ฐ ๋กœ๊ทธ์ธ ๋ชฉ์  ํ˜ผ์šฉ ๋ฐฉ์ง€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
 - SMS ๋ฐœ์†ก ์‹คํŒจ rollback ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๋ณด๊ฐ•

[โœ… Verification]
 - npx prisma generate
 - npm run test -- features/auth/service/sms.test.ts features/auth/service/rateLimit.test.ts features/user/service/phone.test.ts
 - npx tsc --noEmit
 - npm run lint
 - npm run test
 - npx prisma validate
 - git diff --check
[๐Ÿ“ฑ SMS]
 - SMS ๋กœ๊ทธ์ธ ํ† ํฐ์ด ์ธ์ฆ ์ „ User.phone์„ ์ ์œ ํ•˜์ง€ ์•Š๋„๋ก ์ •๋ฆฌ
 - SMS ๋กœ๊ทธ์ธ ํ† ํฐ๊ณผ ํ”„๋กœํ•„ ์ „ํ™”๋ฒˆํ˜ธ ์ธ์ฆ ํ† ํฐ์˜ userId ๋ชฉ์  ๋ถ„๋ฆฌ
 - ํ”„๋กœํ•„ ์ธ์ฆ ํ† ํฐ์ด SMS ๋กœ๊ทธ์ธ ๊ฒ€์ฆ์—์„œ ์†Œ๋น„๋˜์ง€ ์•Š๋„๋ก ์ฐจ๋‹จ
 - ๋กœ๊ทธ์ธ SMS ๋ฐœ์†ก ์‹คํŒจ ์‹œ ์ด์ „ ํ† ํฐ์˜ userId๊นŒ์ง€ ์กฐ๊ฑด๋ถ€ ๋ณต๊ตฌ

[๐Ÿ—„๏ธ Database]
 - SMSToken.userId nullable ์ „ํ™˜ migration ์ถ”๊ฐ€
 - ๋กœ๊ทธ์ธ SMS ํ† ํฐ์€ userId ์—†์ด ์ €์žฅํ•˜๊ณ , ๊ฒ€์ฆ ์„ฑ๊ณต ์‹œ ์ „ํ™”๋ฒˆํ˜ธ ๊ธฐ์ค€ User๋ฅผ ์กฐํšŒ ๋˜๋Š” ์ƒ์„ฑ

[๐Ÿ” Rate Limit]
 - ์˜ค๋ž˜๋œ rate limit ๊ธฐ๋ก ์‚ญ์ œ๋ฅผ advisory lock transaction ๋ฐ–์œผ๋กœ ๋ถ„๋ฆฌ
 - PostgreSQL transaction ๋‚ด๋ถ€ ์‚ญ์ œ ์‹คํŒจ๊ฐ€ ์ดํ›„ ์กฐํšŒ/๊ธฐ๋ก์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ์ •๋ฆฌ
 - rate limit ํŒ๋‹จ transaction์€ lock, ์ตœ๊ทผ ์š”์ฒญ ์กฐํšŒ, ํ˜„์žฌ ์š”์ฒญ ๊ธฐ๋ก๋งŒ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋‹จ์ˆœํ™”

[โ˜”๏ธ Test]
 - SMS ์ธ์ฆ ์ „ User ์ƒ์„ฑ ๋ฐฉ์ง€ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
 - ํ”„๋กœํ•„ ํ† ํฐ userId ์ž”์กด ๋ฐ ๋กœ๊ทธ์ธ ๋ชฉ์  ํ˜ผ์šฉ ๋ฐฉ์ง€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
 - SMS ๋ฐœ์†ก ์‹คํŒจ rollback ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๋ณด๊ฐ•

[โœ… Verification]
 - npx prisma generate
 - npm run test -- features/auth/service/sms.test.ts features/auth/service/rateLimit.test.ts features/user/service/phone.test.ts
 - npx tsc --noEmit
 - npm run lint
 - npm run test
 - npx prisma validate
 - git diff --check
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
board-port Ready Ready Preview Jun 29, 2026 11:19am

@DoHeonLim
DoHeonLim merged commit 7115014 into master Jun 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant