소개
저는 산림청·교육청·지자체를 대상으로 한 도시숲 안전관리 플랫폼 'TreeSafe'(thetreesafe.com)를 준비하고 있습니다. 나무의 전도·낙지·낙과·고사 같은 중대 안전사고를 IoT·AI·GIS·디지털트윈으로 사전 예방하는 B2G 서비스입니다.
문제는 기술명세서가 하드웨어까지 포함한 많은 규모의 엔터프라이즈라, 한 번에 만들 수 없다는 점이었습니다. 그래서 "웹 관제 SaaS를 먼저 빠르게 만들고, IoT·AI는 인터페이스만 열어둔다"는 전략으로 Claude Code와 함께 MVP를 만들어 왔고, 이번에는 그 위에 실제 영업·제안에 바로 쓸 수 있는 기능들을 얹는 작업을 진행했습니다.
이번 세션에서 실제로 라이브 배포까지 끝낸 것:
📄 PDF 종합 리포트 (한글 임베딩, 정부 제출용)
🖥️ 공개 랜딩 페이지 (관 제 콘솔과 분리)
🎨 공식 로고 + ㈜더코어 코퍼레이트 푸터
📱 QR 스캔 현장점검 (모바일 카메라 → 수목 상세)
🗂️ 녹지 유형별 필터·구분 UI (학교숲→도시공원→가로수→생활권녹지→도시숲)
🌱 5개 유형 데모 데이터 시드
[이미지 첨부: 완성된 랜딩 페이지 전체 화면 — 히어로 "도시숲을 지키는 디지털트윈 AI 안전관리 플랫폼"]
진행 방법
사용 도구
Claude Code (Opus 4.8) — 메인 개발/배포 에이전트
Next.js 16 + TypeScript + Tailwind / Supabase(PostgreSQL+PostGIS) / Vercel(서울 icn1 리전)
Claude in Chrome (브라우저 MCP) — 라이브 사이트 로그인·검증
Supabase MCP — DB 마이그레이션·쿼리
Firecrawl MCP — 회사 홈페이지(푸터/로고) 정보 스크래핑
핵심 활용법 ① — "메모리 파일"로 세션을 이어붙이기
가장 큰 꿀팁입니다. Claude Code에 프로젝트 상태를 통째로 적어둔 메모리 파일을 두고, 매 세션 첫 메시지에서 "메모리 확인 후 이어서 작업"이라고만 하면 됩니다. 제가 실제로 쓴 시작 프롬프트 전문:
TreeSafe(thetreesafe.com) 도시숲 안전관리 플랫폼 이어서 작업.메모리(treesafe-mvp-decisions)에 전체 상태 있음. 먼저 확인해줘.
현황(라이브): https://www.thetreesafe.com (로그인 [email protected] / ...)
- 코드: C:\dev\treesafe (git, push 시 Vercel 자동배포 / 서울 icn1 리전)
- Supabase project: treesafe (서울)
- 완료: 인증·RLS / VWorld 지도 / 수목 CRUD·점검 / 기상청 연동 / 알림 / 리포트·CSV / 커스텀도메인
다음 할 일(순차):
1) PDF 리포트 2) QR 스캔 현장점검 3) 카카오 로드뷰 ...
먼저 dev 서버 띄우고, 1번 PDF 리포트부터 진행해줘.
이후로는 "랜딩 페이지 만들어 줘", "로고 이걸로 바꿔줘", "유형별 필터 만들어줘" 처럼 짧게만 지시해도, Claude가 메모리와 코드베이스를 읽고 알아서 이어갔습니다.
핵심 활용법 ② — 한글 PDF는 폰트를 직접 번들
@react-pdf/renderer로 서버에서 PDF를 생성했는데, 한글이 깨지지 않게 Pretendard(OFL) 폰트를 레포에 직접 번들하고 등록한 게 핵심이었습니다.
// src/lib/pdf/font.tsimport path from 'node:path'
import { Font } from '@react-pdf/renderer'
export function registerFonts(): void {
const dir = path.join(process.cwd(), 'src/fonts')
Font.register({
family: 'Pretendard',
fonts: [
{ src: path.join(dir, 'Pretendard-Regular.otf'), fontWeight: 'normal' },
{ src: path.join(dir, 'Pretendard-Bold.otf'), fontWeight: 'bold' },
],
})
Font.registerHyphenationCallback((word) => [word]) // 한글 단어 중간 줄바꿈 방지
}
Vercel 서버리스에서 폰트 파일이 누락되지 않도록 next.config.ts에 한 줄을 추가한 게 포인트입니다.
const nextConfig: NextConfig = {serverExternalPackages: ['@react-pdf/renderer'],
outputFileTracingIncludes: { '/api/reports/pdf': ['./src/fonts/**'] },
}
[이미지 첨부: 생성된 PDF "수목 안전관리 종합 리포트" — 요약카드·위험등급분포·중점관리수목 표]
핵심 활용법 ③ — 카메라 없이 QR을 "검증"하는 법
QR 스캔 기능을 만들 때, 데스크톱엔 카메라가 없어 실제 스캔 테스트가 어려웠습니다. 그래서 QR 인코딩 → (스캐너가 쓰는 바로 그 디코더 jsQR로) 디코딩 라운드트립으로 검증했습니다.
import QRCode from 'qrcode'import jsQR from 'jsqr'
const url = 'https://www.thetreesafe.com/trees/3'
const qr = QRCode.create(url, { errorCorrectionLevel: 'M' })
// QR 매트릭스를 4px/모듈로 래스터화해서 jsQR에 넣고 복원 확인
const decoded = jsQR(rgba, dim, dim)
console.log('match:', decoded?.data === url ? 'PASS' : 'FAIL')
// → match: PASS (인코딩→디코딩 파이프라인 검증 완료)
[이미지 첨부: /scan 카메라 스 캔 화면 (타겟 프레임 + 수동입력 폴백) / QR 라벨 인쇄 페이지]
핵심 활용법 ④ — 확산 전략을 DB+UI로 구현
"학교숲 → 도시공원 → 가로수 → 생활권녹지 → 도시숲" 순차 확산을 위해, PostGIS RPC에 유형 필터를 넣고(지도·목록·리포트 공통), Supabase MCP로 라이브 DB에 바로 마이그레이션했습니다.
-- trees_geojson: forest_type 노출 + 유형 필터(p_forest_type[]) 추가-- SECURITY INVOKER → RLS(기관 단위 권한) 그대로 유지
create or replace function public.trees_geojson(
p_forest bigint default null, p_forest_type text[] default null
) returns jsonb language sql stable as $$
select jsonb_build_object('type','FeatureCollection','features', coalesce(jsonb_agg(
jsonb_build_object('type','Feature',
'geometry', extensions.st_asgeojson(t.geom)::jsonb,
'properties', jsonb_build_object('tree_id', t.tree_id, 'risk_level', t.risk_level,
'forest_type', f.forest_type::text)))), '[]'::jsonb))
from public.tree_info t join public.forest_info f on f.forest_id = t.forest_id
where (p_forest_type is null or f.forest_type::text = any(p_forest_type))
and t.geom is not null;
$$;
[이미지 첨부: 리포트 "녹지 유형별 분포" — 학교숲50%·도시공원14%·근린11%·가로수14%·생활권11% / 지도 레이어 패널의 "녹지 유형" 필터 체크박스]
핵심 활용법 ⑤ — 라이브에서 직접 검증
Claude가 코드만 짜고 끝내는 게 아니라, Chrome MCP로 실제 라이브 사이트에 로그인해 결과를 눈으로 확인하게 했습니다. 예를 들어 PDF가 Vercel에서 폰트 임베딩까지 제대로 되는지를, 로그인 상태에서 직접 fetch해 확인:
status 200 · application/pdf · 67KB · 매직바이트 %PDF→ 로컬 렌더(67KB)와 동일 크기 = Vercel 폰트 임베딩 정상
결과와 배운 점
결과 (전부 라이브 배포 완료)
PDF 리포트
/api/reports/pdf— 한글 임베딩 제출용랜딩 페이지
/(공개) +