웹 개발 패턴 & 실수 방지 가이드¶
비개발자가 Claude와 사이트/앱 만들 때 반복되는 패턴 정리.
코드 구조 관리 규칙¶
언제 정리해야 하나¶
- 같은 코드 3곳 이상 복붙 → 컴포넌트/함수로 분리
- 파일 1개가 300줄 초과 → 데이터와 UI 분리 검토
- 기능 추가 시 6개 파일 동시 수정 → 공통 부분 추출 필요
- 데이터가 계속 쌓이는 구조 → 데이터 추가 전에 구조 정리 (짐 넣기 전 선반 정리)
정리 타이밍¶
- 기능이 안정된 직후 (버그 수정 끝나고, 데이터 대량 추가 전)
- "나중에 해야지" = 안 하게 됨. 데이터 쌓인 후 리팩토링은 3배 귀찮음
CSS Grid 필수 체크¶
pre/code + Grid 조합¶
- Grid 아이템의 min-width 기본값 = auto (콘텐츠 최소 너비) -<pre> 태그는 줄바꿈 안 하니까 긴 줄이 컬럼을 밀어냄
- overflow-x: auto 있어도 min-width:auto면 소용없음
iframe 주의사항¶
srcdoc 안에서 CSS 충돌¶
<style>+<body>가 한 문서 안에 있으면 태그 셀렉터 주의blockquote {},table {}같은 태그 셀렉터 → 미리보기 요소에도 적용됨- 해결: class 기반 셀렉터 사용 or 미리보기는 inline style
DOM 이동 시 참조 깨짐¶
parent.after(element)로 iframe 포함 요소 이동하면 iframe 리로드- JS에서 iframe 참조 캐싱하지 말고, 필요할 때 동적으로 querySelector
Vercel 배포 체크¶
- 커밋 이메일 ≠ Vercel 계정 이메일 → 배포 조용히 무시 (에러 없음!)
- 확인:
git config user.email→ Vercel 계정과 동일한지 - 배포 안 되면 이메일 불일치 먼저 의심
배포 전 필수 루틴¶
매번 하기¶
- 빌드 테스트:
npx astro build(또는npm run build) - 에러 없는지 확인 - 커밋은 작게: 기능 1개 = 커밋 1개. 문제 생기면 그 커밋만 되돌릴 수 있음
- push 후 배포 확인: Vercel 대시보드에서 배포 시작됐는지 체크
확인할 때¶
- Ctrl+Shift+R (강력 새로고침) — 브라우저 캐시 때문에 옛 버전 보이는 경우 많음
- "수정했는데 똑같아요" → 80%는 브라우저 캐시 문제
- 시크릿 모드(Ctrl+Shift+N)로 열어보면 캐시 없이 확인 가능
되돌리기 (문제 생겼을 때)¶
git log --oneline -10— 최근 커밋 목록 확인git revert [커밋해시]— 특정 커밋만 취소 (안전, 기록 남음)- 절대 하지 말 것:
git reset --hard(작업 내용 날아감) - 커밋을 작게 해둬야 → 되돌릴 때 딱 그 부분만 취소 가능
스크린샷으로 디버깅¶
- 말로 설명하면 오해 생김 → 스크린샷이 최고의 버그 리포트
- "안 돼요" → 뭐가 안 되는지 캡처해서 보여주기
- 작동하는 것 vs 안 되는 것 나란히 비교 → 차이점이 원인 (이번에 Grid 버그 이렇게 찾음)
디버깅 마인드셋¶
- 같은 코드인데 특정 요소만 깨질 때 → 콘텐츠 차이 의심 (길이, 특수문자, 태그)
- "코드가 똑같은데 왜 다르지?" → 코드가 아니라 데이터가 다른 것
- 여러 번 수정해도 안 되면 → 원인 가설 자체를 바꿔야 함
- 같은 방향 3번 시도해도 안 되면 → 완전히 다른 각도에서 보기
GitHub Actions (자동화 워크플로우)¶
CI/CD의 일종. "특정 조건이 되면 서버에서 자동으로 스크립트를 실행"하는 것. 비유: 매일 아침 7시에 알바생이 가게 문 열고 청소하는 것.
핵심 개념¶
- 워크플로우 파일:
.github/workflows/이름.yml(YAML 형식) - 트리거:
schedule(정기 실행),push(코드 올릴 때),workflow_dispatch(수동 실행) - Secrets: 비밀번호/API 키를 안전하게 저장. 리포 Settings > Secrets and variables > Actions
- 코드에서
${{ secrets.이름 }}으로 접근 - 한번 저장하면 다시 볼 수 없음 (수정만 가능)
기본 구조 (예: 매일 뉴스 수집)¶
name: 뉴스 수집
on:
schedule:
- cron: '0 22 * * *' # UTC 22시 = KST 07시
workflow_dispatch: # 수동 실행 버튼
jobs:
fetch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # 코드 가져오기
- uses: actions/setup-python@v5 # 파이썬 설치
with:
python-version: '3.11'
- run: pip install requests # 라이브러리 설치
- run: python scripts/fetch-news.py # 스크립트 실행
env:
API_KEY: ${{ secrets.API_KEY }} # Secrets 주입
- run: | # 결과를 git에 저장
git config user.name "github-actions"
git config user.email "actions@github.com"
git add game/news.json
git diff --cached --quiet || git commit -m "auto: update news"
git push
실전 주의사항¶
- cron 시간은 UTC: KST = UTC + 9시간. KST 07시 = UTC 22시 (전날)
- push 충돌: Actions가 자동 커밋 + 사용자도 push하면
rejected (fetch first)에러 - 해결:
git pull --rebase후 다시 push - Re-run vs Run workflow: Re-run은 옛날 코드로 재실행. Run workflow가 최신 코드 실행
- 빈 커밋 방지:
git diff --cached --quiet || git commit(변경 없으면 커밋 안 함) - Actions 결과 확인: 리포 > Actions 탭에서 실행 로그 확인 가능
외부 API 활용 패턴¶
네이버 검색 API¶
- 용도: 뉴스 검색, 블로그 검색 등
- 인증: Client ID + Client Secret (네이버 개발자센터에서 발급)
- 호출: HTTP GET + 헤더에 인증 정보
- 주의: 일일 호출 한도 있음 (무료: 25,000건/일)
- HTML 태그 제거: 응답에
<b>,"등 포함 → 정규식으로 제거 필요
Google News RSS¶
- 용도: API 키 없이 뉴스 수집 (백업용)
- URL:
https://news.google.com/rss/search?q=키워드&hl=ko&gl=KR&ceid=KR:ko - 형식: XML → 파이썬 xml.etree.ElementTree로 파싱
- 주의: URL이
news.google.com/rss/articles/...형태의 리다이렉트 → 실제 기사 URL이 아님 - 리다이렉트 URL은 "원문 보기"에 쓰면 안 됨 (빈 문자열로 처리)
뉴스 데이터 품질 관리¶
- 제목 필수 키워드: "보이스피싱", "스미싱", "피싱" 등 → 없으면 제외
- 제외 키워드: "전세사기", "검거", "교육", "대통령", "국회" 등 → 수법 기사가 아닌 것 걸러내기
- 날짜 필터: 7일 이내만 (오래된 기사 제외)
- URL 검증: 도메인만 있는 URL (
https://example.com/), 리다이렉트 URL → 차단 - 시나리오 매칭: 키워드 → 시나리오 직접 매핑이 카테고리 기반보다 정확함
- 직접: "빗썸" →
sms_finance_01(정확) - 카테고리: "금융" →
sms_finance_01(애매)
JSON 데이터 설계¶
캐시 버스팅¶
- 정적 파일(JSON, CSS, JS)은 브라우저가 캐시함 - 자주 바뀌는 데이터 →?t=타임스탬프 또는 ?v=버전번호 붙이기
- CSS/JS 버전 관리: style.css?v=16 → 수정할 때마다 숫자 올리기
롤링 데이터 + 아카이브 패턴¶
- 현재 데이터:
news.json(최근 7일, 5~10개) - 아카이브:
news-archive/2026-03.json(월별 누적) - 스크립트가 매일 실행 → 현재 파일 갱신 + 아카이브에 추가
- 아카이브는 나중에 통계/분석에 활용 가능
fallback 데이터¶
- API 실패 시를 대비해 fallback 데이터를 JSON에 포함
news배열(실시간) +fallback배열(수동 큐레이션) 구조- 코드에서:
var items = data.news.length >= 3 ? data.news : data.fallback
호스팅 & 배포 비용 비교¶
호스팅 트래픽 비교¶
| 호스팅 | 트래픽 무료 | 초과 시 |
|---|---|---|
| Cloudflare Pages | 무제한 | 없음 |
| Vercel | 월 100GB | 추가 과금 |
| Netlify | 월 100GB | GB당 $55 |
| AWS Amplify | 월 15GB | GB당 $0.15 |
이미지 저장소 비교¶
| 서비스 | 저장 무료 | 전송비 (1GB당) |
|---|---|---|
| Cloudflare R2 | 10GB | 0원 |
| AWS S3 | 5GB | $0.09 |
| Google Cloud | 5GB | $0.12 |
Cloudflare R2 무료 범위 (매월)¶
- 저장: 10GB / 업로드: 100만 건 / 읽기: 1,000만 건 / 전송비: 무제한 무료
- 활성화 시 카드 등록 필요 (무료 범위 초과 대비, 실 결제 안 됨)
Cloudflare 비즈니스 모델¶
- "무료로 끌어들여서 기업 고객한테 돈 받기" (카카오톡과 같은 구조)
- 개인/소규모: 무료. 대기업: Pro/Business/Enterprise (월 $20 ~ 수천만원)
- 전 세계 인터넷 트래픽 약 20%가 Cloudflare 네트워크 경유
Cloudflare 선택 기준¶
- 콘텐츠 사이트(블로그, 포트폴리오, 홈페이지) → Cloudflare Pages (트래픽 무제한)
- 이미지 많은 사이트 → R2 추가 (전송비 0원)
- 서버 기능 필요한 앱 → Vercel (서버리스 함수 편함)
R2 세팅 순서¶
- Cloudflare 계정 > Storage & databases > R2 > 활성화 (카드 등록)
- wrangler CLI:
npm install -D wrangler - 로그인:
npx wrangler login - 버킷 생성:
npx wrangler r2 bucket create 버킷이름 - 퍼블릭 접근: 대시보드 > R2 > 버킷 > Settings > Public access
도메인 이전 절차 (기존 사이트 -> Cloudflare Pages)¶
- 새 사이트 완성 -> Cloudflare Pages에 임시 URL로 배포
- 기존 사이트 URL 목록 정리 -> 301 리다이렉트 매핑
- Cloudflare에서 도메인 추가 -> 네임서버 2개 받기
- 기존 도메인 관리사이트에서 네임서버 변경
- DNS 전파 완료 후 sitemap 재제출
프로젝트 시작할 때 체크리스트¶
첫 세팅¶
- [ ]
git config user.email확인 (배포 서비스 계정과 일치하는지) - [ ] 배포 연결 후 테스트 push → 실제 배포되는지 확인
- [ ] CLAUDE.md에 프로젝트 구조, 배포 설정, 현재 상태 기록
구조 설계¶
- [ ] 반복될 UI 패턴 → 컴포넌트로 분리 (나중에 하면 귀찮음)
- [ ] 데이터와 UI 분리 (데이터만 추가하면 페이지가 늘어나는 구조)
- [ ] 공유 타입/인터페이스 정의 (여러 파일에서 같은 데이터 구조 쓸 때)
스타일¶
- [ ] CSS Grid 쓸 때 → 자식에
min-width: 0습관적으로 넣기 - [ ] 모바일 반응형 → 개발 초반에 확인 (나중에 하면 전면 수정)
- [ ] 폰트/색상 → CSS 변수(custom property)로 관리