SAL Grid 방법론으로 분해·분석·점수화한 성능개선 보고서
기능은 정상 동작하나, CSS 중복·모듈화 부재·테스트 부재·렌더 블로킹이 주요 문제입니다.
Critical Path 병목: CSS 중복 → 캐싱 실패 → 페이지 이동마다 재파싱
즉시 조치 시 성능 20~30점 향상 가능 (목표: 80점 이상)
분해 기준: 페이지=SAL ID 단위 / Area = 기능영역(FE/SC/DB) / Level = 의존성 깊이(공통모듈→페이지→기능) / Variant = 동일 Area 내 병렬 페이지
| Area | S2 분해 (Level 1 = 기반) | S2 분해 (Level 2 = 공통) | S2 분해 (Level 3 = 페이지) | S2 분해 (Level 4 = 기능) |
|---|---|---|---|---|
| FE | S2FE1 CSS 변수/:root 전 페이지 중복 |
S2FE2 NAV 컴포넌트 4개 페이지 복사 |
S2FE3a index.html S2FE3b pool.html S2FE3c community.html |
S2FE4a Hero섹션 S2FE4b How-It-Works S2FE4c Why-Join |
| AU | S2AU1 config.js AXON_CONFIG 초기화 |
S2AU2 Supabase Auth login/signup/profile |
S2AU3a login.html S2AU3b signup.html S2AU3c profile.html |
S2AU4a forgot-pw.html S2AU4b reset-pw.html |
| DB | S2DB1 ax_experts 전문가 테이블 |
S2DB2 ax_project_requests 프로젝트 요청 |
— |
— |
| SC | S2SC1 Anon Key 하드코딩 config.js L28-29 |
S2SC2 Admin 권한체크 클라이언트 사이드 |
S2SC3 escapeHtml() XSS 방어 ✅ |
— |
| SAL ID | 컴포넌트 | 현재 점수 | 주요 문제 | 우선순위 |
|---|---|---|---|---|
| S2FE1 | CSS 변수/:root 선언 | 동일한 `:root{}` CSS 변수 선언이 최소 5개 HTML 파일에 복사. 수정 시 5곳 동기화 필요. 브라우저 캐싱 불가. | 🔴 즉시 | |
| S2FE2 | NAV 컴포넌트 | NAV HTML + CSS + JS가 index/pool/community/profile 4개 파일에 완전 복사. Nav 수정 시 4곳 동시 수정 필요. 동기화 오류 위험. | 🔴 즉시 | |
| S2AU1 | config.js — AXON_CONFIG | Supabase Anon Key + URL이 fallback으로 하드코딩 (L28-29). Anon Key는 공개 설계이지만 프로젝트 URL 노출 = 대상 특정 가능. `/api/config` 매 페이지 호출, 캐싱 없음. | 🟠 단기 | |
| S2SC2 | injectAdminLink() — 관리자 권한 | user.app_metadata?.role !== 'admin' 체크가 클라이언트 사이드. 브라우저 콘솔에서 조작 가능. Supabase DB RLS 정책이 별도로 있어야 실제 보호됨. RLS 존재 여부 미확인. |
🟠 단기 | |
| S2FE3a~c | 메인 페이지 3개 (index/pool/community) | Google Fonts CDN (`Noto Sans KR` + `Outfit`)을 렌더 블로킹 `<link>`로 로드. Noto Sans KR은 한국어 폰트로 용량이 매우 큼. 각 페이지마다 동일 요청. `font-display: swap` 미사용. | 🟠 단기 | |
| S2FE3b | pool.html — 전문가 풀 페이지 | OG 이미지 경로 `/images/og-default.png`가 404 에러. 카카오톡·슬랙 공유 시 미리보기 없음. SEO 점수 하락 요인. | 🟠 단기 | |
| S2AU2 | Supabase Auth (login/signup/profile) | Auth 로직이 각 페이지에 인라인 JS로 중복. `getUser()` 호출이 페이지마다 독립. Auth 상태 캐싱 없음 → 매 페이지 로드마다 Supabase 왕복 API 호출. | 🟡 중기 | |
| S2DB1 | ax_experts 테이블 | 전문가 수 카운트 쿼리 select('*', {count:'exact', head:true}) — 캐싱 없이 매 랜딩 페이지 로드마다 실행. 전문가 수가 자주 바뀌지 않는다면 캐싱 가능. |
🟡 중기 | |
| S2SC3 | escapeHtml() / isSafeUrl() / isValidEmail() | XSS 방어 함수가 구현되어 있고 사용됨 ✅. URL 검증, 이메일 검증 존재 ✅. 양호함. | 🟢 양호 | |
| S2DB2 | ax_project_requests 테이블 | RLS 정책 마이그레이션 파일 존재 (`allow_insert_returning.sql`) ✅. 구조 양호. SQL injection은 Supabase 클라이언트 라이브러리가 기본 방어. | 🟢 양호 |
Critical Path 병목: CSS 중복(S2FE1) → 캐싱 실패 → 매 페이지 재파싱 → 렌더 지연
이 경로를 먼저 해결하면 가장 큰 성능 향상 효과를 얻습니다.
:root { --ink, --amber, --teal, ... } CSS 변수 블록이 index.html, pool.html, community.html, login.html, profile.html 최소 5개 파일에 복사되어 있습니다. NAV CSS (~80줄), Footer CSS (~30줄)도 마찬가지입니다./css/common.css로 분리하여 `<link rel="stylesheet">`로 외부 로드. 브라우저 캐싱 적용 → 첫 페이지 이후 CSS 재다운로드 0KB.fetch()로 nav.html을 로드하거나, Web Components/HTML Template을 사용. 또는 빌드 도구(Vite) 도입으로 컴포넌트화.fetch('/components/nav.html').then(r=>r.text()).then(html=>{navEl.innerHTML=html})
js/config.js 28~29번 줄에 실제 Supabase URL과 Anon Key가 하드코딩되어 있습니다. Anon Key는 공개 설계이지만, GitHub에 커밋되면 공개 저장소 시 완전 노출됩니다.https://www.ax-on.net/images/og-default.png를 참조하고 있으나 해당 파일이 존재하지 않아 404 에러가 발생합니다. 카카오톡, 슬랙, 링크드인 등에서 링크 공유 시 미리보기 이미지 없음.
injectAdminLink() 함수는 user.app_metadata?.role !== 'admin'로 관리자 여부를 체크합니다. 이는 UI 표시 목적이지만, 실제 관리자 페이지(/pages/admin/admin.html)에 직접 URL로 접근 시 클라이언트 체크는 우회될 수 있습니다.await sb.auth.getUser()를 독립적으로 호출합니다. 사용자가 index → pool → community 3페이지를 방문하면 Supabase Auth API가 3번 호출됩니다. Supabase 클라이언트 라이브러리가 내부적으로 일부 캐싱하지만, 페이지 간 상태 공유는 없습니다.
sb.from('ax_experts').select('*', {count:'exact', head:true})를 실행합니다. 전문가 수는 분 단위로 바뀌지 않는 데이터인데, 방문자마다 DB 쿼리가 발생합니다.
/css/common.css)CSS 변수, reset, NAV, Footer, 공통 버튼/카드 CSS를 외부 파일로 분리. 예상 작업: 2~3시간. 효과: 전송 데이터 -40KB+, 캐싱 적용, 유지보수성 대폭 향상.
fetch()로 nav.html 외부 로드 또는 JS 함수로 NAV DOM 생성. 예상 작업: 3~4시간. 효과: NAV 수정 시 1곳만 수정, 버그 위험 제거.
1200×630 PNG 이미지 생성. 예상 작업: 30분. 효과: 소셜 공유 미리보기 즉시 복구.
렌더 블로킹 제거. 예상 작업: 30분. 효과: LCP -300~500ms, FCP 개선.
Cache-Control 헤더 추가, sessionStorage 캐싱. 예상 작업: 1시간. 효과: API 호출 -80%, 보안 강화.
Supabase RLS 정책 감사 및 Vercel middleware 추가. 예상 작업: 2~3시간. 효과: 권한 우회 공격 방어.
페이지 간 Auth 상태 공유. 예상 작업: 1~2시간. 효과: Supabase Auth API 호출 -70%.
localStorage 5분 캐싱. 예상 작업: 30분. 효과: DB 쿼리 -95%.
Noto Sans KR wght@400;700만 로드 (500,900 제거). 예상 작업: 10분. 효과: 폰트 전송량 -30%.
S7FE1~2 (CSS 분리 + NAV 컴포넌트화)만 완료해도 전체 점수가 54점 → 약 72점으로 향상됩니다.
S7FE1~S7SC2 (1~6순위) 전부 완료 시 목표 80점+ 달성 가능.
| 항목 | 현재 점수 | 개선 후 예상 | 개선 작업 |
|---|---|---|---|
| FE 코드구조 | 32점 | 82점 | S7FE1 (CSS 분리) + S7FE2 (NAV 컴포넌트화) |
| PF 성능 | 58점 | 80점 | S7PF1 (Fonts 비동기) + S7DB1 (캐싱) |
| SC 보안 | 68점 | 85점 | S7SC1 (캐싱+정리) + S7SC2 (RLS 확인) |
| QA 품질 | 25점 | 55점 | 컴포넌트화 후 유닛 테스트 가능해짐 |
| AR 아키텍처 | 55점 | 78점 | 공통 파일 분리, 컴포넌트 구조 도입 |
| 종합 | 54점 | 76점 | 1~6순위 작업 완료 시 (예상 총 작업: 10~15시간) |