Dev Note
Claude Code를 활용한 개발 과정 기록 — 사진작가 DY가 직접 만드는 사진 프레임 도구
10개 언어 다국어 지원 (i18n)
i18n.js IIFE 구현 — URL 경로 → localStorage(pfm-lang) → navigator.language → 기본값(ko) 순으로 언어 감지- 지원 언어 10개: 한국어, 영어, 스페인어, 포르투갈어, 프랑스어, 독일어, 일본어, 중국어, 아랍어(RTL), 인도네시아어
data-i18n 어트리뷰트 패턴: applyI18n()가 DOM 탐색 후 텍스트 치환 — 자식 요소가 있는 경우 <span data-i18n> 래퍼 방식으로 처리index.html, about.html, tools.html, featured.html 전 페이지 번역 키 적용- 아랍어 RTL:
document.documentElement.dir = 'rtl' 자동 처리
URL 라우팅 & 배포 설정
- Cloudflare Worker:
/:lang/ 경로 감지 후 해당 언어 쿠키 없이 정적 파일 서빙 — new Request(url.toString())로 전달 (body 재사용 시 빈 응답 버그 수정) vercel.json: /(ko|en|...)/$ → /index.html, /(ko|en|...)/(.+) → /$2 라우팅 추가 — handle: filesystem 이전에 배치하여 /ja/tools.html 등 정상 서빙site.js 공통 내비게이션에 언어 전환 <select> 컴포넌트 추가 (한 곳 수정으로 전 정적 페이지 적용)- 블로그 SSR 템플릿(
worker.js)에도 언어 전환기 및 i18n.js 로드 추가
에셋 경로 & 빌드
index.html의 에셋 경로를 상대 경로 → 절대 경로로 수정 (style.css → /style.css 등) — 언어 접두어 URL(/en/)에서 404 방지- 캐시버스팅 스크립트도 동일하게 절대 경로 통일
- 빌드 결과:
i18n.js 260.7KB → 208.5KB (-20%), 총 번역 키 약 250개 (10개 언어 × 25 공통 + 페이지별 번역)
Featured 랜딩 페이지 전면 재설계
- 기존 React 19 + Vite SPA(
featured/ 서브디렉터리)를 루트 레벨 정적 HTML(featured.html)로 전환 - Botanical × 324.ing 디자인 시스템 적용 — CSS 변수(
--sage, --terra, --clay), 종이 그레인 텍스처 오버레이 - 신규 섹션 구성: Hero(EXIF 수치 목업) → Stats 바 → 5개 툴 카드 → Frame Colors → Preview Modes → 10개 EXIF 스타일 갤러리 → Process 4단계 → FAQ 아코디언 → CTA
- AI 업스케일러 카드 추가 (Beta 배지, Real-ESRGAN 4× 설명) — 퍼플 그라디언트는 디자인 리뷰 후 제거
- IntersectionObserver 기반 스크롤 리빌 애니메이션 (
fade-up, scale, stagger 효과) - 반응형 브레이크포인트 3단계 (1024px, 900px, 600px) + 모바일 최적화
빌드 & 라우팅 개선
build.js: featured/ 서브디렉터리 방식 제거, htmlFiles 배열에 featured.html 추가vercel.json: /featured → /featured.html 라우팅 단순화, canonical·OG URL 수정- 빌드 결과: CSS -31%, JS -45%
QA 감사
- 15개 테스트케이스 전체 통과 — 8개 페이지 렌더링, 모바일 햄버거 메뉴, FAQ 아코디언, CTA 링크 정확성
- 발견 이슈:
cl.324.ing/books iframe HTTP 이미지 mixed content 경고, tools.html SEO 중복 콘텐츠 잠재 문제
빌드 시스템 전환 (build.js)
package.json 빌드 스크립트를 긴 cp 체인에서 node build.js로 전환- CSS 미니파이: PostCSS + cssnano (
style.css -32%, nav.css -25%) - JS 미니파이: terser (
app.js -45%, site.js -44%), js/ 하위 모듈 재귀 미니파이 - HTML, admin, images(<1MB), 서브디렉토리(newexif, upscale, featured) 자동 복사
execFileSync 사용으로 shell injection 방지 (보안 강화)
사이트 속도 최적화
- Google Fonts 비렌더블로킹 로딩:
media="print" onload="this.media='all'" + <link rel="preload"> - 모든 HTML 파일
<script src="/site.js" defer> 적용 (파서 블로킹 제거) - grain 텍스처 오버레이 GPU 컴포지팅:
will-change: transform; transform: translateZ(0) - 정적 에셋 30일 캐시:
Cache-Control: public, max-age=2592000, stale-while-revalidate=86400
Vercel 설정 이슈 해결
cleanUrls: true 추가 시 404 발생 — legacy routes와 modern cleanUrls 혼용 불가 확인- modern
rewrites 전환 시도 → /blog/ Workers 프록시 깨짐 (filesystem이 먼저 처리) - 최종 해결: legacy
routes 유지, cleanUrls 제거, 캐시 헤더 룰을 { "handle": "filesystem" } 직전에 배치
tools.html 업데이트
- AI 사진 업스케일러 카드 추가 (5번째, Beta 배지)
- EXIF 프레임 디자이너에 "New" 배지 추가
- JSON-LD 구조화 데이터 포지션 업데이트: Frame(1), Split(2), EXIF(3), Convert(4), Upscaler(5)
빌드 스크립트 버그 수정
/featured/ 빈 페이지 — build.js의 cpDirs 배열에서 'featured' 누락 → 추가- 빌드 후
ls www/로 출력물 검증하는 습관 필요 (반복 이슈 방지)
EXIF 프레임 모드 신규 구현
exif.html — EXIF 프레임 전용 페이지 신규 추가js/modes/exif-mode.js — AppBase 상속 ExifApp 클래스, 18개 EXIF 스타일 렌더링js/overlays/exif-overlays.js — filmstrip, minimal, magazine, signature, letterbox, polaroid, leica, fujistyle, fujirecipe, glass, leicalux, instax, filmstock, shoton, editorial, hud, minimalbar, cardgrid 오버레이 구현- 스타일별 전용 옵션: 필름 브랜드, 폰트, Glass 테마/위치, HUD 액센트, CardGrid 레이아웃
EXIF 프레임 캔버스 비율 처리
- 각 스타일이 자체 확장 치수만 사용 — 사진 원본 비율을 강제로 맞추는 여백 제거
- filmstrip/letterbox/hud/fujistyle → 상하에만 바 추가, magazine → 좌측 바, signature/leica 등 → 하단 바
- instax/editorial → 사방 여백+바 구조 유지, 스타일 외 영역에 불필요한 패딩 없음
프레임 기능 제거 (EXIF 모드 전용)
ExifApp.getFrameWidth() 오버라이드 → 항상 0 반환 (프레임 두께 없음)- 프레임 비율, 프레임 색상 UI 섹션
display:none으로 숨김 - AppBase가 요구하는 stub 엘리먼트 전체
display:none 처리 (다운로드 버튼 위 불필요한 인풋 제거)
다운로드 버그 수정
downloadExifFrame()에서 존재하지 않는 app.drawExifOverlay() 호출 → app._drawExifOverlay()로 수정- 다운로드 시에도 renderMode()와 동일한 오버레이 치수 로직 적용, 올바른 원본 해상도 출력
Featured 랜딩 페이지 제작
- React 19 + Vite + TypeScript + Tailwind CSS v4 + Framer Motion 기반 랜딩 페이지 신규 제작
- Hero, Modes, Steps, Colors, EXIF, Preview, FAQ, CTA 총 8개 섹션 구성
- 스크롤 트리거 애니메이션 (fade-up, scale, stagger) 적용
- 히어로 이미지에 실제 촬영 사진(야자수) 적용, EXIF 메타데이터(35mm · f/4.0 · 1/2500s · ISO 400) 실제 값 표시
- 반응형 레이아웃: 데스크톱/모바일 최적화 (Steps 섹션 가로↔세로 전환 등)
SEO 최적화
- Open Graph, Twitter Card 메타태그 추가
- JSON-LD 구조화 데이터 (WebApplication 스키마)
- Canonical URL, robots, keywords, author, theme-color 설정
- 기존 photoframemaker 파비콘 활용 (SVG 대신 ICO/PNG)
네비게이션 통합
- 전체 사이트 네비게이션 순서 통일: Featured → Tools → Blog → Dev Note → About
- Featured 랜딩 Navbar,
site.js, worker.js SSR 3곳 동기화
듀얼 배포 구성
- Vite
base: '/featured/' 설정으로 서브디렉토리 배포 대응 - 빌드 결과물을
www/featured/(Cloudflare Workers)와 featured/(Vercel) 양쪽에 복사 f.324.ing/featured/ 경로로 Vercel + Cloudflare Workers 동시 서빙
PDCA 품질 관리
- Plan → Design → Do → Check(Gap Analysis) → Act 전 과정 수행
- 초기 Gap Analysis 89% → 애니메이션 3건 보완 후 95%+ 달성
블로그 시스템 구축
- Cloudflare Workers (Hono) + D1 SQLite + R2 기반 블로그 시스템 구축
- SSR 블로그 목록/상세 페이지 (
/blog/, /blog/:slug/) - Toast UI Editor 기반 WYSIWYG 어드민 에디터 (
/admin/editor.html) - 글 임시저장/발행/수정/삭제 기능, 카테고리·태그·커버 이미지 지원
- RSS Feed (
/feed.xml), 동적 사이트맵 (/sitemap.xml) - 어드민 대시보드, 글 목록, 사이트 설정 페이지 구현
호스팅 아키텍처
- Vercel: 메인 사이트 정적 파일 서빙 (
f.324.ing) - Cloudflare Workers: 블로그/API/어드민 (
photoframemaker.dynoworld.workers.dev) - Vercel
vercel.json rewrites로 /blog/*, /api/*, /admin/*를 Workers로 프록시 - 정적 파일(파비콘, OG 이미지)은 Vercel 프로젝트 루트에 위치해야 서빙됨
파비콘 / OG 이미지 적용
images/ 폴더의 favicon.ico, favicon_*.png, og-image.png를 프로젝트 루트에 복사 후 Vercel 배포- 어드민 사이트 설정에서 파비콘·OG 이미지를 R2에 업로드하고 적용하는 기능 추가
- 블로그 SSR 페이지에 파비콘 link 태그 자동 삽입
블로그 SEO 개선
og:type 'blog' → 유효한 값 'website'/'article'로 수정og:image 상대 URL → 절대 URL 자동 변환 (소셜 크롤러 호환)- 이미지 없을 때
og:image 태그 자체 생략 (빈 content 방지) - Twitter 카드 완성:
twitter:title, twitter:description, twitter:image 추가 og:locale: ko_KR, og:site_name 추가- JSON-LD BlogPosting: 이미지 절대 URL 변환,
dateModified null 폴백 처리 - 사이트맵
lastmod 형식 W3C Datetime 표준으로 수정
어드민 에디터 개선
- 글 수정 시 ID로 직접 조회하는
GET /api/admin/posts/:id 라우트 추가 - 기존 목록 API 검색 방식(10개 제한으로 누락 가능) → ID 직접 조회로 수정
- HTML 소스 직접 편집 모드 추가 (WYSIWYG ↔ HTML 토글)
블로그 기타 버그 수정
- 블로그 nav
/about/ → /about.html (Vercel 정적 파일 경로 맞춤) - 블로그 헤더/타이틀이 어드민 설정값 반영하도록 수정 (하드코딩 제거)
- 어드민 비밀번호: Cloudflare Workers 시크릿
ADMIN_PASSWORD_HASH로 관리
iOS Safari 다운로드 개선 (Web Share API)
- iOS Safari에서 저장 시
navigator.share() 사용하여 공유 시트 표시 - 기존
<a download> 방식은 파일앱으로만 저장되던 문제 해결 - 공유 시트에서 "사진에 저장" 선택 시 사진앱에 직접 저장 가능
- 유저 취소(AbortError) 시 조용히 종료, 미지원 환경에서는 기존 방식 폴백
여러 장 일괄 공유 (Batch Share)
tryBatchShare() 메서드 추가: 여러 파일을 한 번의 공유 시트로 처리- 기존에는 여러 장/분할 다운로드 시 공유 시트가 반복 표시되던 문제 해결
navigator.share({ files: [...] })로 전체 파일을 한 번에 공유- 데스크톱은 기존대로 ZIP, Android 네이티브는 갤러리 직접 저장 유지
SEO/GEO 전략 적용
- 메타 태그 최적화 (title, description, keywords, canonical URL)
- Open Graph / Twitter Card 메타태그 추가
- 구조화된 SEO 콘텐츠 섹션 추가 (소개, 기능, 사용법, FAQ)
- 한국어/영어 이중 콘텐츠 제공
애널리틱스/서치콘솔 연동
- Google Tag Manager (GTM-5F5CWSJX) 추가
- Google Analytics 4 (G-ZE7EBPTLFG) 추가
- 네이버 서치어드바이저 사이트 인증 태그 추가
이미지 드래그 위치 조정 수정
- 이미지 표시 모드를 contain → cover로 변경 (프레임 내 꽉 채움)
- 드래그로 이미지 위치 미세조정 시 offset clamping 적용
썸네일 스트립 개선
- 썸네일 크기 25% 확대 (52→65px, 모바일 48→60px)
- 썸네일 스트립 가운데 정렬 수정
인스타그램 다크모드 지원
prefers-color-scheme: dark로 피드/프로필 목업 다크모드 자동 전환
About 페이지 분리
- 메인 페이지 하단 SEO 콘텐츠를
about.html로 분리 - Vercel 배포 설정 (
vercel.json) 추가
모바일 썸네일 UX 전면 개편
- 썸네일 목록을 하단 '사진' 탭 패널 안으로 이동, 5열 그리드 레이아웃
- 피드/프로필 모드에서 썸네일이 목업을 가리는 문제 수정
인스타그램 비율 추가
- 4:5 (인스타 게시물), 9:16 (스토리/릴스) 비율 버튼 추가
- 비율 버튼 레이아웃을 그리드로 변경 (데스크톱 4열, 모바일 3열)
인스타그램 미리보기 모드
- 피드 모드: 인스타 게시물 형태 목업 (프로필 헤더 + 이미지 + 아이콘 + 캡션)
- 프로필 모드: 인스타 계정 페이지 목업 (프로필 헤더 + 통계 + 3×3 그리드)
- 기본/피드/프로필 3가지 프리뷰 모드 토글
다중 이미지 처리 구현
- 최대 10장까지 이미지 업로드 지원
- 이미지 배열 데이터 모델 (
images[], currentIndex) - 썸네일 스트립 UI, 캔버스 화살표/스와이프 네비게이션
- 다중 이미지 ZIP 다운로드 (JSZip)
Capacitor 프로젝트 셋업
- Capacitor 8.1.0 기반 Android 네이티브 앱 구조 추가
capacitor.config.ts 설정 (appId: com.dy.photoframemaker)
프로젝트 초기 구현
- HTML/CSS/JS 기반 사진 프레임 메이커 웹앱 구현
- 캔버스 비율 선택 (1:1, 3:4, 4:3, 3:2, 2:3)
- 프레임 두께 조절 (슬라이더 0~25%), 색상 프리셋 8종 + 커스텀 컬러 피커
- PNG 무손실 다운로드
EXIF 메타데이터
- JPEG EXIF 파싱 구현 (카메라, 렌즈, 초점거리, 조리개, 셔터속도, ISO, 촬영일)
- 자체 파서 구현 (외부 라이브러리 없음)
Cloudflare Workers 배포 설정
wrangler.jsonc 설정 추가, Cloudflare 배포 구성- Dev Note, Prompt Log 페이지 생성,
CLAUDE.md 프로젝트 컨텍스트 문서 추가