LogoSkills

pr

Pre-PR test gate โ€” automatically runs unit + widget + integration tests scoped to changed packages right before PR creation, blocking the PR on failure. Use when creating a PR or running /pr:prefli...

PR Preflight โ€” ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ๋ฒ”์œ„ 3-๋ ˆ์ด์–ด ํ…Œ์ŠคํŠธ ๊ฒŒ์ดํŠธ#

ํ•œ๋งˆ๋””๋กœ (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

์ฝ”๋“œ๋ฅผ ํŒ€์— ํ•ฉ์น˜๊ธฐ ์ „, ์ด๋ฒˆ์— ๋ฐ”๋€ ๋ถ€๋ถ„๋งŒ ๊ณจ๋ผ์„œ ์ž๋™์œผ๋กœ ๊ฒ€์‚ฌํ•ด ์ฃผ๋Š” "์ถœ๊ณ  ์ „ ํ’ˆ์งˆ ๊ฒ€์‚ฌ๋Œ€"์ž…๋‹ˆ๋‹ค. ๋น„ํ–‰๊ธฐ๊ฐ€ ์ด๋ฅ™ ์ „์— ์ ๊ฒ€ ๋ชฉ๋ก(pre-flight check)์„ ํ™•์ธํ•˜๋“ฏ, ๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ํ•ฉ์น˜๋Š” ์ž‘์—… ์ž์ฒด๋ฅผ ๋ง‰์•„ ์ค๋‹ˆ๋‹ค. ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด์•ผ๋งŒ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ์–ด, ๋ง๊ฐ€์ง„ ์ฝ”๋“œ๊ฐ€ ์Šฌ์ฉ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์„ ์‚ฌ์ „์— ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

๋ฌด์—‡์„ยท์–ธ์ œ (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

  • ๋ฌด์—‡์„ ํ•ด ์ฃผ๋‚˜์š”
    • ์ด๋ฒˆ์— ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋˜‘๋˜‘ํ•˜๊ฒŒ ๊ณจ๋ผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”๋€Œ์ง€ ์•Š์€ ๊ณณ๊นŒ์ง€ ์ „๋ถ€ ๋Œ๋ฆฌ์ง€ ์•Š์•„ ์‹œ๊ฐ„์„ ์•„๋‚๋‹ˆ๋‹ค.
    • ์„ธ ๋‹จ๊ณ„ ๊ฒ€์‚ฌ(์ž‘์€ ๋‹จ์œ„ โ†’ ํ™”๋ฉด โ†’ ์‹ค์ œ ๋™์ž‘)๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋Œ๋ฆฌ๊ณ , ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ์ฆ‰์‹œ ๋ฉˆ์ถ”๊ณ  ๋ณด๊ณ ์„œ๋ฅผ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.
    • ๊ฒ€์‚ฌ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์œ ๋ฅผ ๋‚จ๊ฒจ์•ผ ํ•˜๋ฉฐ, ๊ธฐ๋ก์ด ๋‚จ์•„ ๋‚˜์ค‘์— ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์–ธ์ œ ์ž‘๋™ํ•˜๋‚˜์š”
    • ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํŒ€์— ํ•ฉ์น˜์ž๊ณ  ์š”์ฒญ(PR)ํ•˜๊ธฐ ์ง์ „์— ์ž๋™์œผ๋กœ ์ผœ์ง‘๋‹ˆ๋‹ค.
    • "PR ์˜ฌ๋ฆฌ๊ธฐ ์ „์— ํ…Œ์ŠคํŠธ", "pre-flight check", "๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ํ…Œ์ŠคํŠธ" ๊ฐ™์€ ์š”์ฒญ์„ ํ•  ๋•Œ.
    • ์ž๋™ ๊ฐœ๋ฐœ ํ๋ฆ„(/dev:run)์˜ ๋งˆ์ง€๋ง‰ ์ ๊ฒ€ ๋‹จ๊ณ„์—์„œ๋„ ์ž๋™์œผ๋กœ ๋ถˆ๋ ค ์˜ต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์šฉ์–ด (๋น„๊ฐœ๋ฐœ์ž์šฉ)#

์šฉ์–ด์‰ฌ์šด ์„ค๋ช…
PR (Pull Request)๋‚ด๊ฐ€ ๋งŒ๋“  ๋ณ€๊ฒฝ์„ ํŒ€์˜ ๊ณต์‹ ์ฝ”๋“œ์— ํ•ฉ์ณ ๋‹ฌ๋ผ๊ณ  ๋ณด๋‚ด๋Š” ์š”์ฒญ์„œ
Preflight์ด๋ฅ™ ์ „ ์ ๊ฒ€์ฒ˜๋Ÿผ, PR์„ ๋ณด๋‚ด๊ธฐ ์ง์ „์— ๋ฏธ๋ฆฌ ๋Œ๋ฆฌ๋Š” ๊ฒ€์‚ฌ
Unit ํ…Œ์ŠคํŠธ๊ฐ€์žฅ ์ž‘์€ ๋ถ€ํ’ˆ ํ•˜๋‚˜ํ•˜๋‚˜๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ๋ณด๋Š” ๊ฒ€์‚ฌ
Widget ํ…Œ์ŠคํŠธํ™”๋ฉด์— ๋ณด์ด๋Š” ์š”์†Œ(๋ฒ„ํŠผยท๋ชฉ๋ก ๋“ฑ)๊ฐ€ ์ž˜ ๊ทธ๋ ค์ง€๋Š”์ง€ ๋ณด๋Š” ๊ฒ€์‚ฌ
Integration ํ…Œ์ŠคํŠธ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์„ ์ด์–ด ๋ถ™์—ฌ ์‹ค์ œ ์‚ฌ์šฉ์ฒ˜๋Ÿผ ๋๊นŒ์ง€ ๋Œ์•„๊ฐ€๋Š”์ง€ ๋ณด๋Š” ๊ฒ€์‚ฌ
ํŒจํ‚ค์ง€(package)๊ธฐ๋Šฅ๋ณ„๋กœ ๋ฌถ์ธ ์ฝ”๋“œ ๊พธ๋Ÿฌ๋ฏธ. ๋ณ€๊ฒฝ๋œ ๊พธ๋Ÿฌ๋ฏธ๋งŒ ๊ฒ€์‚ฌ ๋Œ€์ƒ์ด ๋จ
Base ๋ธŒ๋žœ์น˜๋‚ด ๋ณ€๊ฒฝ์„ ํ•ฉ์น  ๊ธฐ์ค€์ด ๋˜๋Š” ์›๋ณธ ์ฝ”๋“œ ์ค„๊ธฐ
Regression๋ฉ€์ฉกํ•˜๋˜ ๊ธฐ๋Šฅ์ด ์ƒˆ ๋ณ€๊ฒฝ ๋•Œ๋ฌธ์— ๋‹ค์‹œ ๋ง๊ฐ€์ง€๋Š” ํ˜„์ƒ
CI์ฝ”๋“œ๊ฐ€ ํ•ฉ์ณ์งˆ ๋•Œ ์„œ๋ฒ„๊ฐ€ ์ž๋™์œผ๋กœ ์ „์ฒด ๊ฒ€์‚ฌ๋ฅผ ๋Œ๋ ค ์ฃผ๋Š” ์‹œ์Šคํ…œ
SkipํŠน์ • ๊ฒ€์‚ฌ๋ฅผ ์˜๋„์ ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๋Š” ๊ฒƒ (์‚ฌ์œ  ๊ธฐ๋ก ํ•„์ˆ˜)
hotfix์šด์˜ ์ค‘์ธ ์„œ๋น„์Šค์˜ ๊ธด๊ธ‰ ์žฅ์• ๋ฅผ ๊ธ‰ํžˆ ๊ณ ์น˜๋Š” ์ž‘์—…
๊ฒŒ์ดํŠธ(gate)ํ†ต๊ณผํ•ด์•ผ๋งŒ ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ด€๋ฌธ

๋ชจ๋“  PR ์€ ๋ณธ ์Šคํ‚ฌ PASS ์—†์ด ์ƒ์„ฑ ๊ธˆ์ง€ ์ž…๋‹ˆ๋‹ค. /pr:preflight ์ปค๋งจ๋“œ ๋˜๋Š” /dev:run Step 8 ์ž๋™ ํ˜ธ์ถœ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

์™œ ํ•„์š”ํ•œ๊ฐ€#

  1. ์ „์ฒด ๋ ˆํฌ ํ…Œ์ŠคํŠธ๋Š” ๋А๋ฆฌ๋‹ค โ€” ๋ณ€๊ฒฝ ์—†๋Š” ํŒจํ‚ค์ง€๊นŒ์ง€ ๋Œ๋ฆฌ๋ฉด ์ˆ˜ ๋ถ„ ๋‚ญ๋น„
  2. Regression ์˜ ์ฆ‰์‹œ ๋ฐœ๊ฒฌ โ€” PR ์˜ฌ๋ฆฐ ๋’ค CI ์—์„œ ๋ฐœ๊ฒฌํ•˜๋ฉด feedback loop ์ด ๊ธธ์–ด์ง
  3. 3 ๋ ˆ์ด์–ด ์ผ๊ด€ ๋ณด์žฅ โ€” unit ๋งŒ ๋Œ๋ฆฌ๊ณ  widget ์„ ๊ฑด๋„ˆ๋›ฐ๋Š” ๊ฐœ๋ฐœ์ž ์Šต๊ด€ ์ฐจ๋‹จ
  4. ์„ ์–ธ์  ์ •์ฑ… โ€” ์Šคํ‚ต ์กฐ๊ฑด์ด ๋ช…์‹œ์ (--skip-tests, --skip-integration)์ด์–ด์•ผ ๊ฐ์‚ฌ ๊ฐ€๋Šฅ

ํŠธ๋ฆฌ๊ฑฐ#

  • /pr:preflight ์ปค๋งจ๋“œ
  • /dev:run Step 8 ์ž๋™ ํ˜ธ์ถœ
  • /e2e:kick-off ์™„๋ฃŒ ํ›„ ๋‹ค์Œ ๋‹จ๊ณ„ ์ œ์•ˆ
  • ์‚ฌ์šฉ์ž ์š”์ฒญ: "PR ์˜ฌ๋ฆฌ๊ธฐ ์ „์— ํ…Œ์ŠคํŠธ", "pre-flight check", "๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ํ…Œ์ŠคํŠธ"

์ „์ œ#

  • ํ”„๋กœ์ ํŠธ๊ฐ€ melos ๋ชจ๋…ธ๋ ˆํฌ (kobic/good-teacher ํ‘œ์ค€)
  • ๊ฐ ํŒจํ‚ค์ง€์— pubspec.yaml + (ํ•ด๋‹น๋˜๋ฉด) test/ ๋””๋ ‰ํ† ๋ฆฌ
  • flutter_test, bloc_test, mocktail ๋“ฑ ์˜์กด์„ฑ ์„ค์ •
  • BDD dual ์‚ฌ์šฉ ์‹œ bdd_test_gen ์ƒ์„ฑ๋ฌผ์ด ์ตœ์‹  ์ƒํƒœ (dart run build_runner build)

๋™์ž‘#

1. Base ๋ธŒ๋žœ์น˜ ๊ฒฐ์ •#

์šฐ์„ ์ˆœ์œ„:

  1. --base ์˜ต์…˜ ๋ช…์‹œ ๊ฐ’
  2. ํ˜„์žฌ ๋ธŒ๋žœ์น˜์˜ upstream (git rev-parse --abbrev-ref '@{u}')
  3. development (์กฐ์ง ๊ธฐ๋ณธ base)
  4. main (fallback)

2. ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ ์ž๋™ ๊ฐ์ง€#

# 1) ๋ณ€๊ฒฝ ํŒŒ์ผ ๋ชฉ๋ก
git diff --name-only  " $BASE " ..HEAD

# 2) ๊ฐ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉฐ pubspec.yaml ์„ ๋งŒ๋‚˜๋Š” ์ฒซ ๋””๋ ‰ํ† ๋ฆฌ = ํŒจํ‚ค์ง€ ๋ฃจํŠธ
# 3) pubspec.yaml ์˜ name ํ•„๋“œ โ†’ melos scope ์ด๋ฆ„

์กฐ์ง ๋ชจ๋…ธ๋ ˆํฌ ๊ฒฝ๋กœ ๋งค์นญ ํžŒํŠธ:

๊ฒฝ๋กœ ํŒจํ„ดํ•ด์„
app/{name}/์•ฑ ํŒจํ‚ค์ง€ (integration_test ์‹คํ–‰ ํ›„๋ณด)
feature/{type}/{name}/feature ํŒจํ‚ค์ง€ (unit + widget + bdd_test_gen)
package/{name}/๊ณต์šฉ ํŒจํ‚ค์ง€ (unit ๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๅคš)
backend/{name}/์„œ๋ฒ„ํŒŸ ํŒจํ‚ค์ง€ (serverpod-test-agent ์œ„์ž„)
shared/{name}/๊ณต์œ  ์„ค์ • โ€” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์—์„œ ์ œ์™ธ (๋˜๋Š” --include-shared)

๋ณ€๊ฒฝ ์—†์Œ โ†’ ์กฐ๊ธฐ ์ข…๋ฃŒ (PR ์— ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ์—†๋‹ค๋Š” ๋œป, ๊ฒŒ์ดํŠธ skip).

3. ๋ ˆ์ด์–ด๋ณ„ ์‹คํ–‰ ๋งคํŠธ๋ฆญ์Šค#

๋ ˆ์ด์–ด๋Œ€์ƒ ํŒŒ์ผ์‹คํ–‰ ๋ช…๋ น๋””๋ฐ”์ด์Šค๊ธฐ๋ณธ ์‹คํ–‰ ์—ฌ๋ถ€
Unit test/src/domain/, test/src/data/, test/src/presentation/bloc/ melos exec --scope="$SCOPES" --dir-exists=test -- flutter test --exclude-tags=integration โŒ ๋ถˆํ•„์š” โœ… ํ•„์ˆ˜
Widget test/src/presentation/widget/, test/src/bdd/*.widget_test.dart flutter test(์œ„์™€ ํ†ตํ•ฉ) + BDD widget ์ž๋™ ํฌํ•จ โŒ ๋ถˆํ•„์š” โœ… ํ•„์ˆ˜
Integration (BDD patrol) test/src/bdd/*.patrol_test.dart patrol test --target=feature/.../*.patrol_test.dart --flavor=staging โœ… ์‹ค ๋””๋ฐ”์ด์Šค/์—๋ฎฌ โš ๏ธ ์กฐ๊ฑด๋ถ€
Integration (app E2E) app/*/integration_test/features/*.feature patrol test --target=integration_test/features/<@smoke ์‹œ๋‚˜๋ฆฌ์˜ค> โœ… ์‹ค ๋””๋ฐ”์ด์Šค/์—๋ฎฌ โš ๏ธ ์กฐ๊ฑด๋ถ€

Integration ์กฐ๊ฑด๋ถ€ ์‹คํ–‰ ๊ทœ์น™:

  • --with-integration ํ”Œ๋ž˜๊ทธ โ†’ ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  integration ์‹คํ–‰
  • --with-integration=smoke (๊ธฐ๋ณธ) โ†’ @smoke ํƒœ๊ทธ ์‹œ๋‚˜๋ฆฌ์˜ค๋งŒ ์‹คํ–‰
  • ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์— integration_test ๋˜๋Š” *.patrol_test.dart ๊ฐ€ ์—†์Œ โ†’ integration skip
  • ๋””๋ฐ”์ด์Šค ์—†์Œ (flutter devices ๊ฒฐ๊ณผ 0) โ†’ integration skip + ๊ฒฝ๊ณ 
  • ์ „๋Ÿ‰ integration ์€ CI ์ฑ…์ž„ (PR ๋จธ์ง€ ์ „ CI ๊ฐ€ ์‹คํ–‰)

4. ์‹คํ–‰ ์ˆœ์„œ#

1. Unit (๋น ๋ฅธ ์‹คํŒจ ํƒ์ง€)
   โ”œโ”€ FAIL โ†’ ์ฆ‰์‹œ ์ค‘๋‹จ, ๋ ˆ์ด์–ด ๊ฒฐ๊ณผ ๋ฆฌํฌํŠธ, PR ์ฐจ๋‹จ
   โ””โ”€ PASS โ†’ ๋‹ค์Œ

2. Widget (BDD widget_test ํฌํ•จ)
   โ”œโ”€ FAIL โ†’ ์ค‘๋‹จ, ๋ฆฌํฌํŠธ, PR ์ฐจ๋‹จ
   โ””โ”€ PASS โ†’ ๋‹ค์Œ

3. Integration (์กฐ๊ฑด๋ถ€)
   โ”œโ”€ SKIP (์กฐ๊ฑด ๋ฏธ์ถฉ์กฑ) โ†’ ๋‹ค์Œ
   โ”œโ”€ FAIL โ†’ ์ค‘๋‹จ, ๋ฆฌํฌํŠธ, PR ์ฐจ๋‹จ
   โ””โ”€ PASS โ†’ ๋‹ค์Œ

4. ์ง‘๊ณ„ + ๋ฆฌํฌํŠธ + PR ์ง„ํ–‰ ํ—ˆ์šฉ

5. ์‹คํ–‰ ์„ธ๋ถ€ ๋ช…๋ น ์˜ˆ์‹œ#

# ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ scope ๋ฌธ์ž์—ด ๋นŒ๋“œ
SCOPES= " feature_sign_in,feature_store,kobic " 

 # Unit + Widget (๋™์ผ ๋ช…๋ น์œผ๋กœ ์ปค๋ฒ„)
melos exec --scope= " $SCOPES "   --dir-exists=test -- flutter test \
  --coverage --exclude-tags=integration

# BDD dual ์ด๋ฉด build_runner ๊ฐ€ ์ตœ์‹ ์ธ์ง€ ํ™•์ธ
melos exec --scope= " $SCOPES "   --depends-on= " bdd_test_gen "   \
  -- dart run build_runner build --delete-conflicting-outputs

# Integration @smoke (์„ ํƒ)
cd app/kobic
patrol test \
  --target integration_test/features/smoke.feature \
  --flavor staging

6. ์ง‘๊ณ„ ๋ฆฌํฌํŠธ ํฌ๋งท#

๐Ÿงช PR Preflight Report (base: development โ†’ feature/1234-sign-in)

๐Ÿ“ฆ ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€ (3)
  - feature_common_auth
  - feature_application_store
  - kobic (app)

๐Ÿ”ฌ Unit
  โœ… 42 passed / 0 failed    feature_common_auth
  โœ… 18 passed / 0 failed    feature_application_store
  โœ…  5 passed / 0 failed    kobic

๐ŸŽจ Widget
  โœ… 12 passed / 0 failed    feature_common_auth (BDD widget_test ํฌํ•จ)
  โœ…  8 passed / 0 failed    feature_application_store

๐Ÿ“ฑ Integration (@smoke)
  โœ… 3 passed / 0 failed     kobic / integration_test/features/smoke.feature

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โœ… ALL LAYERS PASSED โ€” PR ์ƒ์„ฑ ํ—ˆ์šฉ

๋‹ค์Œ ๋‹จ๊ณ„: gh pr create --base development

์‹คํŒจ ์‹œ:

โŒ WIDGET LAYER FAILED

๐Ÿ“ฆ feature_common_auth
  โŒ test/src/bdd/sign_in.widget_test.dart
     -  " Successful login with valid email "   FAILED
       expected: K.storeScreen visible
       actual:   timeout after 5s

๐Ÿšซ PR ์ƒ์„ฑ ์ฐจ๋‹จ. ์กฐ์น˜:
  1. ์‹คํŒจ ์›์ธ ํ™•์ธ: flutter test feature/common/auth/test/src/bdd/sign_in.widget_test.dart --name  " Successful login " 
   2. TestDriver step ๊ฒ€ํ† : feature/common/auth/test/src/bdd/step/*.dart
  3. ์ˆ˜์ • ํ›„ /pr:preflight ์žฌ์‹คํ–‰

7. Skip ์ •์ฑ… (์‚ฌ์šฉ์ž ๋ช…์‹œ ํ•„์ˆ˜)#

์˜ต์…˜ํ—ˆ์šฉ ์กฐ๊ฑด๊ฐ์‚ฌ ๊ธฐ๋ก
--skip-unit์—†์Œ โ€” ํ•ญ์ƒ ๊ฑฐ๋ถ€โ€”
--skip-widget์—†์Œ โ€” ํ•ญ์ƒ ๊ฑฐ๋ถ€โ€”
--skip-integration๊ธฐ๋ณธ๊ฐ’ (๋กœ์ปฌ ์„ฑ๋Šฅ)์ •์ƒ
--skip-all hotfix ์ „์šฉ + ๋ช…์‹œ ์‚ฌ์œ  ํ•„์ˆ˜ .claude/preflight-skip.log ์— ๊ธฐ๋ก

--skip-all ํ—ˆ์šฉ ์กฐ๊ฑด:

  • Urgent hotfix (ํ”„๋กœ๋•์…˜ ์žฅ์•  ๋Œ€์‘)
  • ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€์— hotfix: ๋˜๋Š” fix!: ์ ‘๋‘์‚ฌ
  • ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กฌํ”„ํŠธ์— ์ด์œ  ๋ช…์‹œ

๊ฐ์‚ฌ ๋กœ๊ทธ ํ˜•์‹:

[2026-04-23T12:34:56Z] user=dongwoo branch=hotfix/payment-crash
reason= " ํ”„๋กœ๋•์…˜ ๊ฒฐ์ œ ํฌ๋ž˜์‹œ ์‘๊ธ‰ ํŒจ์น˜, CI ์—์„œ ์ „๋Ÿ‰ ๊ฒ€์ฆ "

8. lefthook ํ†ตํ•ฉ (์„ ํƒ)#

ํ”„๋กœ์ ํŠธ lefthook.yml ์˜ pre-push ์— ์–‡์€ wrapper ์ถ”๊ฐ€ ๊ถŒ์žฅ:

pre-push:
  parallel: false
  commands:
    preflight-unit:
      tags: test
      run: |
        # ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์˜ unit/widget ๋งŒ ๋น ๋ฅด๊ฒŒ
        # --skip-integration ๋Š” default
        claude-code-cli run /pr:preflight --from-hook || exit 1

์ฃผ์˜: integration ์€ hook ์— ๋‘์ง€ ๋ง ๊ฒƒ (push ์†๋„ ์ €ํ•˜). /pr:preflight ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ช…์‹œ ์‹คํ–‰.

9. CI ์—ฐ๊ณ„#

CI ๋Š” preflight ์™€ ๋ณ„๋„๋กœ ์ „๋Ÿ‰ ํ…Œ์ŠคํŠธ ๋ฅผ ์‹คํ–‰:

  • PR ๋งคํŠธ๋ฆญ์Šค: ๋ณ€๊ฒฝ ํŒจํ‚ค์ง€์˜ unit + widget + ์ „์ฒด integration
  • Nightly: ์ „์ฒด ๋ ˆํฌ unit + widget + integration
  • .claude/preflight-skip.log ๊ฐ์ง€ ์‹œ CI ๊ฐ€ ๊ฐ•ํ•œ ๊ฒŒ์ดํŠธ๋กœ ๋™์ž‘

์ถœ๋ ฅ#

  • ์‹คํ–‰ ๋ฆฌํฌํŠธ (stdout)
  • .claude/preflight-report-{timestamp}.json (์„ ํƒ, --save-report)
  • .claude/preflight-skip.log (skip ์‹œ๋งŒ)
  • ์‹คํŒจ ์‹œ exit code โ‰  0 (์ž๋™ํ™” ์—ฐ๊ณ„์šฉ)

๊ธˆ์ง€ ์‚ฌํ•ญ#

  • โŒ --skip-unit / --skip-widget ์„ ์ •์ฑ… ๋ณ€๊ฒฝ ์—†์ด ํ—ˆ์šฉ
  • โŒ base ๋ธŒ๋žœ์น˜ ๊ฐ์ง€ ์‹คํŒจ ์‹œ ๋กœ์ปฌ ์ „์ฒด ๋ธŒ๋žœ์น˜๋กœ fallback (์ž˜๋ชป๋œ diff)
  • โŒ ๋ณ€๊ฒฝ ์—†๋Š” ํŒจํ‚ค์ง€๋ฅผ scope ์— ํฌํ•จ (์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰)
  • โŒ ๋””๋ฐ”์ด์Šค ์—†์Œ ์ƒํƒœ์—์„œ integration ์„ ๊ฐ•์ œ ์‹คํ–‰ํ•˜์—ฌ false positive ์ƒ์„ฑ
  • โŒ ์ž๋™ ์ƒ์„ฑ ํŒŒ์ผ(.g.dart, .freezed.dart, .widget_test.dart, .patrol_test.dart) ์— ์˜ํ•œ ๋ณ€๊ฒฝ์„ "๋ณ€๊ฒฝ ํŒจํ‚ค์ง€" ๋กœ ์˜ค์ธ โ€” ์ด๋“ค์€ source ๋ณ€๊ฒฝ์„ ๋”ฐ๋ผ๊ฐ„ ๊ฒฐ๊ณผ์ด๋ฏ€๋กœ ์›์ฒœ ์†Œ์Šค ํŒจํ‚ค์ง€ ๊ธฐ์ค€์œผ๋กœ ๋งค์นญ

์ž๋™ ์ƒ์„ฑ ํŒŒ์ผ ํ•„ํ„ฐ#

๋ณ€๊ฒฝ ํŒŒ์ผ ๋ชฉ๋ก์—์„œ ๋‹ค์Œ์€ ํŒจํ‚ค์ง€ scope ํŒ์ • ์‹œ ๋ถ€์ˆ˜์ ์œผ๋กœ๋งŒ ๋ฐ˜์˜:

  • **/*.g.dart, **/*.freezed.dart, **/*.gr.dart, **/*.config.dart, **/*.gen.dart, **/*.mocks.dart
  • **/*.widget_test.dart, **/*.patrol_test.dart (bdd_test_gen ์‚ฐ์ถœ๋ฌผ)

์ด๋“ค์ด ์œ ์ผํ•œ ๋ณ€๊ฒฝ์ด๋ฉด scope ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Œ (์ฝ”๋“œ ์ƒ์„ฑ ์žฌ์‹คํ–‰์ผ ๊ฐ€๋Šฅ์„ฑ).

๋‹ค๋ฅธ ์Šคํ‚ฌ/์—์ด์ „ํŠธ์™€์˜ ๊ด€๊ณ„#

ํ•ญ๋ชฉ๊ด€๊ณ„
cc-uiux:unit-testingUnit ๋ ˆ์ด์–ด ์œ„์ž„ (ํ…Œ์ŠคํŠธ ๋‚ด์šฉ ํ‘œ์ค€)
cc-uiux:widget-testingWidget ๋ ˆ์ด์–ด ์œ„์ž„
cc-uiux:bdd-testingBDD dual ์ƒ์„ฑ๋ฌผ ํฌํ•จ ์‹คํ–‰
cc-uiux:integration-testingPatrol integration ์‹คํ–‰ ๋ช…๋ น
cc-flutter-dev:test (์—์ด์ „ํŠธ)์‹คํŒจ ์‹œ ์ˆ˜์ • ์ž‘์—… ์œ„์ž„
cc-e2e-authoring:feature-test-bundlekick-off ํ›„ preflight ์—ฐ๊ณ„
cc-e2e-authoring:scenario-locklock ๋Œ€์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” preflight ์— ๋ฐ˜๋“œ์‹œ ํฌํ•จ
cc-dev-cycle:dev:runStep 8 ์—์„œ ๋ณธ ์Šคํ‚ฌ ์ž๋™ ํ˜ธ์ถœ

์‹คํŒจ ์ผ€์ด์Šค#

์›์ธ๋Œ€์‘
melos ์—†์Œflutter test + ๋””๋ ‰ํ† ๋ฆฌ ์Šค์บ” ํด๋ฐฑ
bdd_test_gen ์ƒ์„ฑ๋ฌผ ๋ˆ„๋ฝ dart run build_runner build ์ž๋™ ์‹คํ–‰ ํ›„ ์žฌ์‹œ๋„
Integration ๋””๋ฐ”์ด์Šค ์—†์Œskip + ๊ฒฝ๊ณ , CI ์ฑ…์ž„์œผ๋กœ ์ด๊ด€
๋ณ€๊ฒฝ ํŒŒ์ผ ์ค‘ .gitignore ๋œ ๊ฒฝ๋กœskip (๋…ธ์ด์ฆˆ ์ œ๊ฑฐ)
์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์š”์ฒญ (--full)ํ—ˆ์šฉํ•˜๋˜ ๋ฆฌํฌํŠธ์— "Full scope" ๋ช…์‹œ

์ฐธ๊ณ #