|
1 | 1 | # 바닐라 JS 프로젝트 성능 개선 |
2 | | -- url: https://front-5th-chapter4-2-basic.netlify.app/ |
| 2 | +- 배포 url: https://front-5th-chapter4-2-basic.netlify.app/ |
3 | 3 |
|
4 | 4 | ## 성능 개선 보고서 |
5 | 5 | ### 초기 성능 |
6 | | -- PageSpeed Insights 보고서 링크: |
7 | | - - 모바일: https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/mjziitvm5k?hl=ko&form_factor=mobile |
8 | | - - 데스크톱: https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/mjziitvm5k?hl=ko&form_factor=desktop |
9 | | -- 보고서 전체 스크린샷 |
10 | | - - 모바일 |
| 6 | +- **PageSpeed Insights 보고서 링크:** |
| 7 | + - **모바일:** https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/mjziitvm5k?hl=ko&form_factor=mobile |
| 8 | + - **데스크톱:** https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/mjziitvm5k?hl=ko&form_factor=desktop |
| 9 | +- **보고서 전체 스크린샷** |
| 10 | + - **모바일** |
11 | 11 |  |
12 | | - - 데스크톱 |
| 12 | + - **데스크톱** |
13 | 13 |  |
| 14 | + |
| 15 | +### 개선 후 성능 |
| 16 | +- **PageSpeed Insights 보고서 링크:** |
| 17 | + - **모바일:** https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/aucafrqeym?hl=ko&form_factor=mobile |
| 18 | + - **데스크톱:** https://pagespeed.web.dev/analysis/https-front-5th-chapter4-2-basic-netlify-app/aucafrqeym?hl=ko&form_factor=desktop |
| 19 | +- **보고서 전체 스크린샷** |
| 20 | + - **모바일** |
| 21 | +  |
| 22 | + - **데스크톱** |
| 23 | +  |
| 24 | +- **개선 결과** |
| 25 | + **[모바일]** |
| 26 | + |
| 27 | + | 항목 | 개선 전 | 개선 후 | 개선 결과(%) | |
| 28 | + | --- | --- | --- | --- | |
| 29 | + | 성능 | 49 | 92 | 88% | |
| 30 | + | 접근성 | 81 | 95 | 17% | |
| 31 | + | 권장사항 | 93 | 93 | - | |
| 32 | + | 검색엔진 최적화 | 82 | 91 | 11% | |
| 33 | + |
| 34 | + **[데스크톱]** |
| 35 | + |
| 36 | + | 항목 | 개선 전 | 개선 후 | 개선 결과(%) | |
| 37 | + | --- | --- | --- | --- | |
| 38 | + | 성능 | 62 | 97 | 56% | |
| 39 | + | 접근성 | 81 | 95 | 17% | |
| 40 | + | 권장사항 | 96 | 96 | - | |
| 41 | + | 검색엔진 최적화 | 82 | 91 | 11% | |
| 42 | + |
| 43 | +--- |
| 44 | +### 개선 세부 내용 |
| 45 | +#### 이미지 jpg/png → webp |
| 46 | +- **cwebp** (Google의 공식 WebP 인코더) |
| 47 | + - 가장 안정적이고 널리 사용됨 |
| 48 | + - 다양한 품질 설정 옵션 |
| 49 | + - 명령줄 인터페이스로 쉬운 사용 |
| 50 | +- **사용법** |
| 51 | + - `brew install webp` |
| 52 | + - `cd images && for file in *.jpg; do cwebp -q 80 -m 4 "$file" -o "${file%.jpg}.webp"; done` |
| 53 | +- **결과** |
| 54 | + |
| 55 | + |
| 56 | + | 이미지 | 변환 전 크기 | 변환 후 크기 | 감소 비율 | |
| 57 | + | --- | --- | --- | --- | |
| 58 | + | Hero_Desktop.jpg | 1.0MB | 188KB | 81% | |
| 59 | + | Hero_Mobile.jpg | 405KB | 63KB | 84% | |
| 60 | + | Hero_Tablet.jpg | 769KB | 123KB | 84% | |
| 61 | + | vr1.jpg | 52KB | 7.4KB | 86% | |
| 62 | + | vr2.jpg | 89KB | 14KB | 84% | |
| 63 | + | vr3.jpg | 75KB | 9.5KB | 87% | |
| 64 | + |
| 65 | +#### lazy loading 적용 |
| 66 | +- **예시** |
| 67 | + |
| 68 | + ```jsx |
| 69 | + <picture> |
| 70 | + <source srcset="images/vr2.webp" type="image/webp"> |
| 71 | + <img src="images/vr2.jpg" alt="product: Noise Cancelling Headphone" loading="lazy"> |
| 72 | + </picture> |
| 73 | + ``` |
| 74 | + |
| 75 | +- **결과** |
| 76 | + 1. 화면에 보이지 않는 이미지들은 지연 로딩 |
| 77 | + 2. 초기 페이지 로딩 시간이 개선 |
| 78 | + 3. 대역폭 사용량이 줄어듬 |
| 79 | + |
| 80 | +#### LCP 성능 개선 |
| 81 | +1. **LCP(Largest Contentful Paint)란?** |
| 82 | + - 페이지 로드 중에 뷰포트(화면)에 표시되는 가장 큰 콘텐츠 요소가 그려지는 시간 |
| 83 | + - 사용자가 페이지의 주요 콘텐츠를 볼 수 있는 시점을 의미 |
| 84 | + - 현재 사이트에서는 모바일 Hero 이미지가 LCP 요소로 확인됨 |
| 85 | + |
| 86 | +2. **현재 문제 상황** |
| 87 | + |
| 88 | + - LCP 시간: 5,020ms (목표: 2.5초 이내) |
| 89 | + - 주요 지연 요소: |
| 90 | + - 로드 지연: 2,290ms (46%) |
| 91 | + - 로드 시간: 1,500ms (30%) |
| 92 | + - 렌더링 지연: 480ms (10%) |
| 93 | + - TTFB: 750ms (15%) |
| 94 | + |
| 95 | +3. **개선 작업** |
| 96 | + |
| 97 | + ```jsx |
| 98 | + <!-- 모바일 이미지 (LCP 요소) --> |
| 99 | + <picture> |
| 100 | + <source srcset="images/Hero_Mobile.webp" type="image/webp"> |
| 101 | + <img class="mobile" src="images/Hero_Mobile.jpg" fetchpriority="high"> |
| 102 | + </picture> |
| 103 | +
|
| 104 | + <!-- 데스크톱/태블릿 이미지 --> |
| 105 | + <picture> |
| 106 | + <source srcset="images/Hero_Desktop.webp" type="image/webp"> |
| 107 | + <img class="desktop" src="images/Hero_Desktop.jpg" loading="lazy" fetchpriority="low"> |
| 108 | + </picture> |
| 109 | + <picture> |
| 110 | + <source srcset="images/Hero_Tablet.webp" type="image/webp"> |
| 111 | + <img class="tablet" src="images/Hero_Tablet.jpg" loading="lazy" fetchpriority="low"> |
| 112 | + </picture> |
| 113 | + ``` |
| 114 | + |
| 115 | +4. **최적화 이유와 방법** |
| 116 | + |
| 117 | + 1. **모바일 이미지 최적화** |
| 118 | + - 이유: LCP 요소이므로 가장 먼저 로드되어야 함 |
| 119 | + - 방법: |
| 120 | + - fetchpriority="high" 추가로 우선 로딩 |
| 121 | + - loading="lazy" 제거로 지연 로딩 방지 |
| 122 | + 2. **데스크톱/태블릿 이미지 최적화** |
| 123 | + - 이유: 화면에 보이지 않을 수 있어 우선순위가 낮아도 됨 |
| 124 | + - 방법: |
| 125 | + - fetchpriority="low" 추가로 우선순위 낮춤 |
| 126 | + - loading="lazy" 유지로 필요할 때 로딩 |
| 127 | + |
| 128 | +#### 이미지 크기 적절하게 설정하기 |
| 129 | +- **방법** |
| 130 | + |
| 131 | + ```jsx |
| 132 | + cwebp -q 80 -m 4 -resize 1440 670 Hero_Desktop.jpg -o Hero_Desktop.webp && cwebp -q 80 -m 4 -resize 960 770 Hero_Tablet.jpg -o Hero_Tablet.webp && cwebp -q 80 -m 4 -resize 576 576 Hero_Mobile.jpg -o Hero_Mobile.webp && cwebp -q 80 -m 4 -resize 500 500 vr1.jpg -o vr1.webp && cwebp -q 80 -m 4 -resize 500 500 vr2.jpg -o vr2.webp && cwebp -q 80 -m 4 -resize 500 500 vr3.jpg -o vr3.webp |
| 133 | + ``` |
| 134 | + |
| 135 | +- **결과** |
| 136 | + |
| 137 | + |
| 138 | + | 이미지 | 변환 전 크기 | 변환 전 크기 | 감소 비율 | |
| 139 | + | --- | --- | --- | --- | |
| 140 | + | Hero_Desktop.jpg | 188KB | 113.2KB | 38% | |
| 141 | + | Hero_Mobile.jpg | 63KB | 37.9KB | 40% | |
| 142 | + | Hero_Tablet.jpg | 123KB | 74.3KB | 38% | |
| 143 | + | vr1.jpg | 7.4KB | 4.7KB | 36% | |
| 144 | + | vr2.jpg | 14KB | 9.0KB | 37% | |
| 145 | + | vr3.jpg | 9.5KB | 5.7KB | 40% | |
| 146 | + |
| 147 | +#### 이미지 접근성 개선을 위한 alt 속성 추가 |
| 148 | +#### 폰트 로딩 최적화 |
| 149 | +#### 쿠키 동의 배너 스크립트 에러 해결 |
| 150 | +- **문제 상황** |
| 151 | + - 에러 메시지: `Cannot read properties of null (reading 'insertAdjacentElement')` |
| 152 | + - 쿠키 동의 배너가 정상적으로 표시되지 않는 문제 발생 |
| 153 | +- **원인 분석** |
| 154 | + - 쿠키 동의 스크립트가 DOM이 완전히 로드되기 전에 실행되어 발생 |
| 155 | + - 스크립트가 head 태그 내에서 실행되어 DOM 요소에 접근할 수 없었음 |
| 156 | +- **해결 방법** |
| 157 | + 1. 쿠키 동의 스크립트를 DOMContentLoaded 이벤트 이후에 실행되도록 수정 |
| 158 | + 2. 스크립트 위치를 body 태그 끝부분으로 이동 |
| 159 | +- **결과** |
| 160 | + - 쿠키 동의 배너가 정상적으로 표시됨 |
| 161 | + - 스크립트 에러가 해결됨 |
0 commit comments