#!/bin/bash # 이 파일이 하는 일: 배포 "후에" 실서버가 진짜로 잘 도는지 밖에서 두드려 확인한다(스모크 테스트). # # 학습 포인트 — CI 테스트와 무엇이 다른가? # CI(mvn test/npm test)는 "코드"가 맞는지 배포 전에 본다. 하지만 우리가 실제로 겪은 사고 # (보안헤더 X-Frame-Options:DENY로 학습 문서가 통째로 안 뜬 것)는 코드가 아니라 Caddy # "설정" 문제라 CI 테스트로는 안 잡혔다. 스모크 테스트는 배포된 "살아있는 사이트"를 실제 # 사용자처럼 두드려, 설정·인프라·헤더까지 포함해 "정말 열리는가"를 확인한다. # → 코드 그물(CI) + 설정·운영 그물(스모크). 둘이 겹쳐야 회귀가 빠져나갈 틈이 좁아진다. # # 사용법: bash deploy/smoke-test.sh (기본: 운영 URL 검사) # BASE=https://다른주소 bash deploy/smoke-test.sh # 하나라도 실패하면 exit 1 (배포 스크립트/CI에서 실패로 인지 가능). set -uo pipefail BASE="${BASE:-https://edu.awesomedevapp.com}" PASS=0 FAIL=0 # check "설명" 실제값 기대값 → 일치하면 통과, 아니면 실패로 기록 check() { local desc="$1" actual="$2" expected="$3" if [ "$actual" = "$expected" ]; then echo " ✅ $desc" PASS=$((PASS + 1)) else echo " ❌ $desc (기대: $expected, 실제: $actual)" FAIL=$((FAIL + 1)) fi } # contains "설명" 본문 기대문자열 → 포함하면 통과 contains() { local desc="$1" body="$2" needle="$3" if printf '%s' "$body" | grep -q "$needle"; then echo " ✅ $desc" PASS=$((PASS + 1)) else echo " ❌ $desc ('$needle' 없음)" FAIL=$((FAIL + 1)) fi } echo "🔎 배포 스모크 테스트 — $BASE" # ── 1) 홈(SPA) 200 ── code=$(curl -s -o /dev/null -m 15 -w '%{http_code}' "$BASE/") check "홈페이지 200 OK" "$code" "200" # ── 2) HTTP → HTTPS 리다이렉트 (평문 표면 없음) ── rc=$(curl -s -o /dev/null -m 15 -w '%{http_code}' "http://edu.awesomedevapp.com/") case "$rc" in 301|302|308) echo " ✅ HTTP→HTTPS 리다이렉트($rc)"; PASS=$((PASS+1));; *) echo " ❌ HTTP→HTTPS 리다이렉트 (기대 30x, 실제 $rc)"; FAIL=$((FAIL+1));; esac # ── 3) 보안 헤더 ── hdr=$(curl -sI -m 15 "$BASE/") contains "HSTS 헤더" "$hdr" "Strict-Transport-Security" contains "X-Frame-Options 헤더" "$hdr" "X-Frame-Options" contains "X-Content-Type-Options" "$hdr" "X-Content-Type-Options" # ── 4) 학습 문서 iframe (실제로 났던 그 회귀!) ── # DENY면 문서 iframe이 막힌다 → 반드시 SAMEORIGIN이어야, 200이어야, 한국어 본문이 있어야. dochdr=$(curl -sI -m 15 "$BASE/docs/intern-guide.html") doccode=$(curl -s -o /dev/null -m 15 -w '%{http_code}' "$BASE/docs/intern-guide.html") docbody=$(curl -s -m 15 "$BASE/docs/intern-guide.html") check "학습 문서 200 OK" "$doccode" "200" xfo=$(printf '%s' "$dochdr" | grep -i "x-frame-options" | tr -d '\r' | awk '{print $2}') check "학습 문서 X-Frame-Options=SAMEORIGIN (DENY면 iframe 깨짐)" "$xfo" "SAMEORIGIN" contains "학습 문서 캐시 재검증(no-cache)" "$dochdr" "no-cache" contains "학습 문서 한국어 본문 로딩" "$docbody" "AWESOMEDEV" # ── 5) 백엔드 살아있음 (Caddy→백엔드→DB 경로) ── # 없는 계정 로그인 → 401. 이게 나오면 백엔드가 뜨고 DB 조회까지 도는 것. loginc=$(curl -s -o /dev/null -m 15 -w '%{http_code}' -X POST "$BASE/api/auth/login" \ -H "Content-Type: application/json" -d '{"username":"__smoke__","password":"__nope__"}') check "백엔드 API 응답(없는 계정 로그인 401)" "$loginc" "401" # ── 6) SPA 라우트 폴백 (새로고침해도 라우터 동작) ── spac=$(curl -s -o /dev/null -m 15 -w '%{http_code}' "$BASE/learn") check "SPA 라우트 폴백(/learn 200)" "$spac" "200" echo "" echo "── 결과: 통과 $PASS · 실패 $FAIL ──" if [ "$FAIL" -gt 0 ]; then echo "🔴 스모크 테스트 실패 — 배포된 사이트에 문제가 있어요. 위 ❌ 항목을 확인하세요." exit 1 fi echo "🟢 전부 통과 — 배포가 건강합니다."