From lpad
현재 디렉토리의 lpad 프로젝트가 배포 준비가 되었는지 사전 점검. 트랙(Amplify/ECS) 자동 감지, 파일 검증, 실제 빌드/린트 실행, 구체적 수정 가이드 제공. "lpad preflight", "배포 전 점검", "lpad-preflight" 키워드로 트리거.
How this skill is triggered — by the user, by Claude, or both
Slash command
/lpad:lpad-preflightThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- SKILL_VERSION: 0.5.0 — 리포트 헤더 생성 시 이 값을 사용할 것.
lpad 인프라에 배포할 프로젝트를 사전 점검한다. lpad = launchpad(발사대)에서 착안한 "이륙 전 점검(preflight check)".
목표: 개발자가 /lpad-preflight 한 번 실행하면 자기 프로젝트의 배포 준비 상태를 종합 진단받는다.
✅ PASS 또는 ❌ FAIL 둘 중 하나만 존재한다. "경고", "보류", "주의", "스킵" 같은 중간 상태 없음.❌ 실패로 종료하고 배포 금지.tsc 체크) 리포트에 넣지 않는다. 리포트에 나온 항목은 전부 실행됐고, 전부 PASS여야 한다.반드시 이 순서대로 진행한다. 각 단계 결과를 누적해서 마지막에 종합 리포트를 만든다.
이 단계는 preflight 검사 자체가 시작되기 전에 반드시 먼저 실행한다. 구버전이면 검사 전체를 건너뛰고 에러만 출력하고 종료한다.
현재 스킬 버전(SKILL_VERSION)을 GitHub의 최신 plugin.json과 비교한다.
CURRENT_VERSION="0.5.0" # ← SKILL.md 상단 SKILL_VERSION과 동일하게 유지
# 최신 버전 조회 (3초 timeout — 네트워크 이슈로 오래 기다리지 않음)
LATEST_JSON=$(curl -sf --max-time 3 \
https://raw.githubusercontent.com/syntnote/lpad-devtools/main/lpad/.claude-plugin/plugin.json \
2>/dev/null)
if [[ -n "$LATEST_JSON" ]]; then
LATEST_VERSION=$(echo "$LATEST_JSON" | grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' \
| sed 's/.*"\([^"]*\)"$/\1/')
# semver 비교: sort -V 로 더 큰 쪽이 LATEST면 현재가 구버전
if [[ -n "$LATEST_VERSION" && "$LATEST_VERSION" != "$CURRENT_VERSION" ]]; then
NEWER=$(printf '%s\n%s\n' "$CURRENT_VERSION" "$LATEST_VERSION" | sort -V | tail -1)
if [[ "$NEWER" == "$LATEST_VERSION" ]]; then
# ❌ 구버전 감지 — 여기서 즉시 종료, 검사 절대 진행 금지
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "❌ lpad-preflight 업데이트 필요"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "현재 버전: v$CURRENT_VERSION"
echo "최신 버전: v$LATEST_VERSION"
echo ""
echo "Claude Code에서 아래 명령 실행 후 다시 시도하세요:"
echo " /plugin marketplace update syntnote"
echo ""
echo "또는 Claude Code를 완전히 재시작하세요."
exit 1
fi
fi
fi
# 네트워크 실패(LATEST_JSON 비어있음) 또는 최신 버전이면 정상 진행
중요:
⚠️ 버전 체크 실패 1줄 표시.CURRENT_VERSION 값은 이 SKILL.md 상단의 SKILL_VERSION 주석과 반드시 일치.버전 체크 통과 후, 리포트 헤더에 쓸 정보를 수집한다.
date '+%Y-%m-%d %H:%M:%S %Z' # 실행 시각
pwd # 작업 디렉토리
버전은 위 CURRENT_VERSION (= SKILL_VERSION 주석 값)을 사용한다 (현재: 0.5.0).
현재 작업 디렉토리를 기준으로 프로젝트 구조를 파악한다.
ls -la
탐색 규칙:
Dockerfile 있으면 → ECS 트랙package.json과 (vite.config.* 또는 next.config.* 또는 webpack.config.*)이 있으면 → Amplify 트랙api/, frontend/, landing/ 등)에 각각 위 조건 매치되면 → 모노레포 (각 서브디렉토리를 독립 프로젝트로 취급)모노레포인 경우 각 서브 프로젝트마다 2~4단계를 반복 실행.
파일을 Read/Grep으로 읽어 확인. 각 항목을 하나씩 명시적으로 검증한다. 적용 가능한 항목은 전부 PASS해야 한다.
C1. .gitignore 민감 파일 제외 (적용: 항상)
.gitignore Read.env, .env.*, *.pem, *.key 모두 포함되어야 함 → 하나라도 없으면 FAILC2. 민감 파일 커밋 이력 없음 (적용: 항상)
git ls-files | grep -E '\.env$|\.env\.|firestore-key\.json|\.pem$|\.key$'C3. 배포 워크플로우 존재 (적용: 항상)
.github/workflows/deploy-*.yml 1개 이상 존재해야 함C4. main 직접 commit 없음 (적용: git 히스토리에 main 브랜치 있을 때)
git log main --oneline -20에서 비-merge 직접 commit 확인C5. 작업 트리 클린 (적용: git 저장소일 때)
git status --porcelain 결과가 비어있어야 함.gitignore에 추가C6. origin 기준 최신 상태 (적용: git 저장소일 때)
UPSTREAM=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)
if [[ -z "$UPSTREAM" ]]; then
echo "FAIL: upstream 미설정 — 'git push -u origin <branch>' 필요"
else
git fetch --quiet
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse @{u})
BASE=$(git merge-base HEAD @{u})
if [[ "$LOCAL" == "$REMOTE" ]]; then
echo "PASS"
elif [[ "$LOCAL" == "$BASE" ]]; then
echo "FAIL: behind — 'git pull' 필요 (로컬이 원격보다 뒤처짐)"
elif [[ "$REMOTE" == "$BASE" ]]; then
echo "FAIL: ahead — 'git push' 필요 (로컬 commit이 원격에 없음, 배포는 원격 기준 실행)"
else
echo "FAIL: diverged — 'git pull --rebase' 후 재시도 (로컬과 원격이 갈라짐)"
fi
fi
V1. .env.example 존재 + 코드와 일치 (적용: 코드에서 env 참조가 있을 때 또는 .env.example이 있을 때)
.env.example 파일이 루트(또는 해당 서브 프로젝트 루트)에 존재해야 함. 없으면 FAIL..env.example의 키 목록에 있어야 함. 누락 시 FAIL.process\.env\.[A-Z_][A-Z0-9_]*, import\.meta\.env\.[A-Z_][A-Z0-9_]*os\.getenv\(["']([^"']+)["'], os\.environ\[["']([^"']+)["']\], os\.environ\.get\(["']([^"']+)["'].env.example에 해당 변수를 추가하세요" 안내..env.example로 파악함.V2. 하드코딩 URL 금지 (적용: 항상)
src/, app/, 루트의 .py·.ts·.tsx·.js·.jsx 파일. 제외: *.test.*, *.spec.*, __tests__/, tests/, .env.example, 주석.localhost:\d+127\.0\.0\.1:\d+http://[^/"']*\.(com|net|io|app|dev|co|kr) — http 프로토콜 외부 도메인VITE_API_URL, API_BASE_URL)" 안내.http://localhost:8000/api가 그대로 박혀 있는 빈도 극악.V3. Vite 프로젝트의 process.env 오용 (적용: vite.config.* 있는 프로젝트)
src/ 내부 파일에서 process\.env\. grep.vite.config.* 자체와 설정 파일은 제외 (Node 환경에서 정당).import.meta.env.VITE_*로 접근. process.env.*는 번들에 undefined로 남음."V4. 프론트 번들 비밀 노출 금지 (적용: Amplify 트랙)
.env.example의 키 이름에서 블랙리스트 패턴 검사:
VITE_.*SECRET.*VITE_.*PRIVATE.*VITE_.*PASSWORD.*VITE_.*JWT.*VITE_.*_SK_·VITE_.*_SERVICE_KEY (Supabase service role 등)VITE_.*PUBLIC.*, VITE_.*ANON.*(Supabase anon key 등 공개 키)V7. .env.local / .env.development 의존 금지 (적용: 항상)
\.env\.local, \.env\.developmentdotenv\.config\(\s*\{\s*path[^}]*\.(local|development).env.example에 옮기고 배포 환경에서 주입되도록 인프라팀에 요청하세요."E1. Dockerfile 존재
E2. EXPOSE 포트와 앱 실행 포트 일치
EXPOSE <port> 값과 CMD/ENTRYPOINT의 port, 앱 코드(main.py, index.js 등)의 listen port가 전부 동일해야 함E3. 헬스체크 엔드포인트 구현
/api/health"/api/health", '/api/health', @app.get.*health, app.get.*healthE4. .dockerignore 존재 + .env 제외
.env, .env.* 누락 시 FAIL (시크릿 이미지 포함 위험)node_modules, __pycache__, .git도 포함되어야 함. 빠지면 FAIL.E5. 비-root 사용자 설정
USER <name> 지시문 필수V5. CORS 미들웨어 설정 (적용: ECS 트랙의 웹 API)
from fastapi.middleware.cors import CORSMiddleware + app\.add_middleware\(\s*CORSMiddleware 존재require\(['"]cors['"]\) 또는 import\s+cors\s+from\s+['"]cors['"] + app\.use\(\s*cors\(allow_origins\s*=\s*\[["']\*["']\] 또는 origin:\s*['"]\*['"] → FAIL ("와일드카드는 자격증명 포함 요청에서 차단되고 운영 보안상 위험")* 아니면 PASS (구체 값은 환경변수 주입일 수 있어 더 깊이 검증 안 함)V6. 헬스체크 단순성 (적용: ECS 트랙)
/api/health 핸들러 본문(함수 정의 블록)에서 외부 의존성 호출 grep:
session\.(query|execute), db\., prisma\., \.find\(, \.findOne\(, \.findMany\(, select\(, SELECT\s+requests\.(get|post), httpx\., urllib\.request, fetch\(, axios\.redis\., cache\.get, cache\.setreturn {\"ok\": True} 같은 단순 응답만. 외부 의존성 체크하면 DB 장애 시 컨테이너 재시작 루프로 장애 증폭."A1. package.json의 build 스크립트
scripts.build 없으면 FAILA2. 빌드 설정 파일 존재
vite.config.*, next.config.*, webpack.config.* 중 하나 존재A3. lockfile 존재
package-lock.json, pnpm-lock.yaml, yarn.lock 중 하나 필수B1. 테스트 스크립트·파일 존재 (적용: 항상)
package.json의 scripts.test가 존재하고 placeholder("echo \"Error: no test specified\" && exit 1" 또는 이와 유사한 패턴) 아니어야 함*.test.{js,jsx,ts,tsx}, *.spec.{js,jsx,ts,tsx}, __tests__/ 내부 파일pyproject.toml([tool.pytest] 섹션), pytest.ini, setup.cfg([tool:pytest] 섹션) 중 하나tests/ 디렉토리 내 test_*.py 또는 *_test.pyB2. Dockerfile 멀티스테이지 빌드 (적용: ECS 트랙)
^FROM\s+.*\s+AS\s+\w+(대소문자 무관) 패턴 2회 이상 등장해야 함FROM ... AS builder → FROM ... AS runtime 구조로 분리."B3. CI 워크플로우에 빌드·테스트 스텝 포함 (적용: 항상)
.github/workflows/deploy-*.yml 내용을 Read해서 빌드 명령 grep:
docker\s+build, docker\s+buildx, docker/build-push-actionnpm\s+ci와 npm\s+run\s+build(또는 pnpm/yarn 등가), build-push-action 등npm\s+test, pytest, yarn\s+test, pnpm\s+test 중 하나.github/workflows/ 수정은 인프라팀 권한. 본 검사는 검증만 한다.실행 전 안내: "빌드와 테스트를 실제로 실행합니다 (2~5분 소요)" 사용자에게 알리고 진행.
각 명령은 Bash로 직접 실행. timeout 설정 필수. 사전 조건 위반은 FAIL (예: Docker 필요한 프로젝트인데 Docker 미설치 → "Docker가 필요합니다" FAIL).
R-E1. Docker 빌드
docker build -t lpad-preflight-test:latest . 2>&1 | tail -50
docker 명령 없으면 FAILR-E2. 컨테이너 헬스체크
CID=$(docker run -d -p 0:<container_port> lpad-preflight-test:latest)
HOST_PORT=$(docker port $CID <container_port> | head -1 | sed 's/.*://')
HTTP_CODE=000
for i in 1 2 3 4 5; do
sleep 2
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" "http://localhost:${HOST_PORT}/api/health" 2>/dev/null || echo 000)
[[ "$HTTP_CODE" == "200" ]] && break
done
if [[ "$HTTP_CODE" != "200" ]]; then
docker logs $CID 2>&1 | tail -30
fi
docker rm -f $CID >/dev/null 2>&1 || true
docker rmi lpad-preflight-test:latest >/dev/null 2>&1 || true
rm, rmi)는 FAIL 상황에서도 반드시 실행R-A1 + R-A2. 의존성 설치 + 빌드
if [[ -f pnpm-lock.yaml ]]; then
PM="pnpm"; INSTALL="pnpm install --frozen-lockfile"; BUILD="pnpm run build"
elif [[ -f yarn.lock ]]; then
PM="yarn"; INSTALL="yarn install --frozen-lockfile"; BUILD="yarn run build"
elif [[ -f package-lock.json ]]; then
PM="npm"; INSTALL="npm ci"; BUILD="npm run build"
fi
$INSTALL # timeout 300000
$BUILD 2>&1 | tail -40 # timeout 600000
ls dist/ 2>/dev/null || ls build/ 2>/dev/null || ls .next/ 2>/dev/null | head
Node 명령은 앞서 감지한 $PM을 재사용.
R-C1. 린트 (적용: 설정 있을 때)
package.json의 scripts.lint 있으면 $PM run lint → 실패 시 FAILruff.toml, .ruff.toml, pyproject.toml에 ruff 설정 있으면 ruff check . → 실패 시 FAIL.flake8, setup.cfg에 flake8 설정 있으면 flake8 . → 실패 시 FAILR-C2. 타입 체크 (적용: 설정 있을 때)
package.json의 scripts.typecheck → $PM run typecheck → 실패 시 FAILtsconfig.json 있으면 npx tsc --noEmit → 실패 시 FAILmypy.ini 또는 pyproject.toml에 mypy 설정 있으면 mypy . → 실패 시 FAILpyrightconfig.json 있으면 pyright → 실패 시 FAILR-C3. 테스트 (적용: 테스트 코드 있을 때)
package.json의 scripts.test가 placeholder("echo ... exit 1") 아닌 경우 → $PM test → 실패 시 FAILtests/ 디렉토리 + pytest 설정이 있으면 pytest → 실패 시 FAIL적용 가능 판단 원칙:
리포트 최상단에 반드시 버전과 실행 시각을 포함한다.
🚀 lpad-preflight v<SKILL_VERSION>
실행 시각: <YYYY-MM-DD HH:MM:SS TZ>
프로젝트: <프로젝트명 또는 디렉토리명>
트랙: <ECS | Amplify | 모노레포>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[정적 검사]
✅ <항목명>
❌ <항목명>
💡 <구체적 수정 방법>
...
[빌드 검사]
✅ <항목명> (<소요시간>)
❌ <항목명> (<소요시간>)
💡 <로그 요약 or 수정 방법>
...
[코드 품질]
✅ <도구명>: <결과>
❌ <도구명>: <결과 요약>
💡 <수정 방법>
...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
결과: <✅ 통과 | ❌ 실패> (FAIL N건 / 전체 M건)
<다음 단계 안내>
상태 판정 (엄격 이진):
✅ 통과 — "배포 가능"❌ 실패 — "FAIL 항목을 모두 해결한 뒤 재실행하세요. 현 상태로 배포 금지."아이콘 규칙 (단 두 개):
⚠️, ⏭️, 기타 상태 아이콘은 절대 사용 금지.
path:line 형식docker run한 컨테이너는 반드시 docker rm -f, docker build로 만든 lpad-preflight-test:latest 이미지도 docker rmi로 정리lpad-preflight v<SKILL_VERSION>과 실행 시각을 반드시 기록docker 명령이 없거나 데몬이 안 떠있으면 R-E1/R-E2는 FAIL로 처리 ("Docker Desktop 실행 필요" 안내 포함).cd로 디렉토리 이동.| 증상 | 실제 원인 |
|---|---|
| Amplify 빌드 성공했는데 API 호출 실패 | VITE_API_URL 미설정 → /api로 상대경로 fallback |
| ECS 배포는 성공했는데 서비스 안 됨 | 헬스체크 경로 불일치 또는 200 미반환 |
| CORS 에러 | 백엔드 ALLOWED_ORIGINS에 프론트 도메인 누락 |
| 빌드는 되는데 런타임 오류 | .env.local 등 로컬 전용 파일에 의존 |
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub syntnote/lpad-devtools --plugin lpad