|
1 | 1 | # 13. 웹페이지의 성능을 측정하는 다양한 방법
|
2 | 2 | ## 13.1 애플리케이션에서 확인하기
|
3 | 3 | ### 13.1.1 create-react-app
|
| 4 | +``` |
| 5 | +// reportWebVitals.ts |
| 6 | +import { ReportHandler } from 'web-vitals'; |
| 7 | +
|
| 8 | +const reportWebVitals = (onPerfEntry?: ReportHandler) => { |
| 9 | + if (onPerfEntry && onPerfEntry instanceof Function) { |
| 10 | + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { |
| 11 | + getCLS(onPerfEntry); |
| 12 | + getFID(onPerfEntry); |
| 13 | + getFCP(onPerfEntry); |
| 14 | + getLCP(onPerfEntry); |
| 15 | + getTTFB(onPerfEntry); |
| 16 | + }) |
| 17 | + } |
| 18 | +} |
| 19 | +
|
| 20 | +export default reportWebVitals |
| 21 | +``` |
| 22 | +#### reportWebVitals |
| 23 | +web-vitals 라이브러리를 이용한 웹에서 성능을 측정하기 위한 함수 |
| 24 | + |
| 25 | +누적 레이아웃 이동CLS, 최초 입력 지연FID, 최초 콘텐츠풀 페인트FCP, 최대 콘텐츠 페인팅LCP, 첫 바이트까지의 시간TTFB를 측정하는 용도 |
| 26 | + |
| 27 | +#### PerformanceObserver |
| 28 | +웹페이지에서 다양한 성능을 측정할 수 있도록 도와주는 API |
4 | 29 | ### 13.1.2 create-next-app
|
| 30 | +NextWebVitalsMetric을 통해 성능 측정 |
| 31 | +- Next.js-hydration: 페이지가 서버 사이드에서 렌더링되어 하이드레이션하는 데 걸리는 시간 |
| 32 | +- Next.js-route-change-to-render: 페이지가 경로를 변경한 후 페이지를 렌더링을 시작하는 데 걸리는 시간 |
| 33 | +- Next.js-render: 경로 변경이 완료된 후 페이지를 렌더링하는 데 걸린 시간 |
5 | 34 | ## 13.2 구글 라이트하우스
|
| 35 | +핵심 웹 지표 및 접근성, PWA, SEO 등 웹 페이지를 둘러싼 다양한 요소들을 측정하고 점검할 수 있는 구글 제공 웹 페이지 성능 측정 도구 |
| 36 | +- 크롬 및 파이어폭스 |
| 37 | +- 크롬 개발자 도구 |
| 38 | +- lighthouse npm 라이브러리(CI/CD 환경에서 사용) |
6 | 39 | ### 13.2.1 구글 라이트하우스 - 탐색 모드
|
| 40 | +페이지를 접속했을 때부터 페이지 로딩이 완료될 때까지의 성능을 측정하는 모드 |
| 41 | +#### 성능 |
| 42 | +- Time to Interactive: 페이지에서 사용자가 완전히 상호작용할 수 있을 때까지 걸리는 시간을 측정(FCP로 측정되는 페이지 내 콘텐츠가 표시되는 시점, 보여지는 페이지 요소의 대부분에 이벤트 핸들러가 부착되는 시점, 페이지가 유저의 상호작용에 50ms내로 응답하는 시점) |
| 43 | +- Speed Index: 페이지가 로드되는 동안 콘텐츠가 얼마나 빨리 시각적으로 표시되는지를 계산 |
| 44 | +- Total Blocking Time: 메인 스레드에서 특정 시간 이상 실행되는 작업, 즉 긴 작업이 수행될 때마다 메인 스레드가 차단된 것으로 간주하며, 이렇게 실행되는 긴 작업을 모아서 Total Blocking Time이라고 함 |
| 45 | +#### 접근성 |
| 46 | +신체적으로 불편한 사람들이 일반적인 사용자와 동등하게 웹페이지를 이용할 수 있도록 보장하는 웹 접근성 |
| 47 | + |
| 48 | +스크린 리더를 통한 웹페이지의 내용을 직접 듣기, 그림이나 사진의 경우 적절한 대체 문자, 오디오나 비디오의 경우 자막, 마우스를 활용할 수 없는 상황에서 키보드로 대체가능한지 등 다양한 사용자를 배려하기 위해 HTML과 CSS 등에 적절한 대안을 삽입하는 것 |
| 49 | +#### 권장사항 |
| 50 | +웹사이트를 개발할 때 고려해야 할 요소들을 얼마나 잘 지키고 있는지 확인 가능 |
| 51 | +- CSP가 XSS 공격에 효과적인지 확인: 개발자가 아닌 제 3자가 삽입한 스크립트를 통해 공격하는 XSS에 대비하여 웹 사이트에서 호출할 수 있는 컨텐츠를 제한 |
| 52 | +- 감지된 JavaScript 라이브러리: 페이지에서 감지되는 자바스크립트 라이브러리. jQuery, Next.js, React, Lodash, create-reate-app 등. |
| 53 | +- HTTPS 사용 |
| 54 | +- 페이지 로드 시 위치정보 권한 요청 방지하기 |
| 55 | +- 페이지 로드 시 알림 권한 요청 방지하기 |
| 56 | +- 알려진 보안 취약점이 있는 프론트엔드 자바스크립트를 사용하지 하지 않음 |
| 57 | +- 사용자가 비밀번호 입력란에 붙여놓을 수 있도록 허용 |
| 58 | +- 이미지를 올바른 가로세로 비율로 표시 |
| 59 | +- 이미지가 적절한 해상도로 제공됨 |
| 60 | +- 페이지에 HTML Doctype 있음: 표준을 준수해 웹페이지가 작성됐다고 의미하는 Doctype 선언 필요 |
| 61 | +- 문자 집합을 제대로 정의함: charset 지정하여 브라우저가 각 바이트가 나타내는 문자를 인식하게 함 |
| 62 | +- 지원 중단 API 사용하지 않기 |
| 63 | +- 콘솔에 로그된 브라우저 오류 없음 |
| 64 | +- Chrome Devtools의 Issues 패널에 문제없음 |
| 65 | +- 페이지에 유효한 소스맵이 있음 |
| 66 | +- `font-display: optional`을 사용하는 폰트가 미리 로드됨 |
| 67 | +#### 검색 엔진 최적화 |
| 68 | +웹페이지가 구글과 같은 검색엔진이 쉽게 웹피이지 정보를 가져가서 공개할 수 있도록 최적화돼 있는지 확인 |
| 69 | + |
| 70 | +문서를 크롤링하기 쉽게 만들었는지, robots.txt가 유효한지, 이미지와 링크에 설명 문자가 존재하는지, `<meta>`나 `<title>` 등으로 페이지 정보를 확인할 수 있는지 등 |
7 | 71 | ### 13.2.2 구글 라이트하우스 - 기간 모드
|
| 72 | +실제 웹페이지를 탐색하는 동안 지표를 측정 가능 |
| 73 | +#### 흔적 |
| 74 | +View Trace |
| 75 | +시감의 흐름에 따라 어떻게 웹페이지가 로딩됐는지 웹 성능을 추적한 기간을 성능 탭에서 보여줌 |
| 76 | +#### 트리맵 |
| 77 | +페이지를 불러올 때 함께 로딩한 모든 리소스를 함께 모아서 볼 수 있는 곳 |
| 78 | + |
| 79 | +웹페이지의 전체 자바스크립트 리소스 중 어떠한 파일이 전체 데이터 로딩 중 어느 정도를 차지했는지를 비율로 확인가능하며, 실제 불러온 데이터의 크기를 확인 가능 |
| 80 | + |
| 81 | +로딩한 리소스에서 사용하지 않은 바이트의 크기를 확인 |
8 | 82 | ### 13.2.3 구글 라이트하우스 - 스냅샷
|
| 83 | +현재 상태에서 검색엔진의 최적화, 접근성, 성능 등을 분석 가능 |
9 | 84 | ## 13.3 WebPageTest
|
| 85 | +웹사이트 성능을 분석하는 도구(단, 거리가 먼 서버를 기준으로 측정하여 성능 지표가 낮을 수 있음) |
| 86 | +- Site Performance: 웹사이트의 성능을 분석하기 위한 도구 |
| 87 | +- Core Web Vitals: 웹사이트의 핵심 웹 지표를 확인하기 위한 도구 |
| 88 | +- Lighthouse: 구글 라이트하우스 도구 |
| 89 | +- Visual Comparison: 2개 이상의 사이트를 동시에 실행해 시간의 흐름에 따른 로딩 과정을 비교하는 도구 |
| 90 | +- Traceroute: 네트워크 경로를 확인하는 도구 |
10 | 91 | ### 13.3.1 Performance Summary
|
11 | 92 | ### 13.3.2 Opportunities & Experiments
|
12 | 93 | ### 13.3.3 Filmstrip
|
|
21 | 102 | ### 13.3.12 Lighthouse Report
|
22 | 103 | ### 13.3.13 기타
|
23 | 104 | ## 13.4 크롬 개발자 도구
|
| 105 | +크롬 확장 프로그램으로 인한 성능 이슈 파악 방해가 있을 수 있으므로 시크릿 창으로 웹사이트 열기 권장 |
24 | 106 | ### 13.4.1 성능 통계
|
| 107 | +#### Insight |
| 108 | +성능을 측정하는 기간 동안 발생한 이벤트 중 눈여겨봐야 할 내용을 시간의 흐름에 따라 모아서 보여줌 |
| 109 | +#### 메인 메뉴 |
| 110 | +성능을 측정하는 기간 동안 무슨 일이 일어나는지 확인할 수 있는 다양한 기능을 제공 |
25 | 111 | ### 13.4.2 성능
|
| 112 | +#### 메뉴 |
| 113 | +#### 요약 |
| 114 | +측정 기간의 CPU, 네트워크 요청, 스크린숏, 메모리 점유율 등을 요약해서 확인, 시점 선택 및 정보 확인 가능 |
| 115 | +#### 네트워크 |
| 116 | +#### Web Vitals |
| 117 | +#### 소요 시간과 기능 |
| 118 | +시간의 흐름에 따라 메인 스레드의 작업은 어떻게 이뤄졌는지, 또 자바스크립트 힙 영역은 어떻게 변화하는지 등을 확인 |
26 | 119 | ## 13.5 정리
|
27 | 120 |
|
28 | 121 | # 14. 웹사이트 보안을 위한 리액트와 웹페이지 보안 이슈
|
29 | 122 | ## 14.1 리액트에서 발생하는 크로스 사이트 스크립팅(XSS)
|
| 123 | +Cross-Site Scripting, XSS는 웹 애플리케이션에서 가장 많이 보이는 취약점 중 하나 |
| 124 | + |
| 125 | +웹사이트 개발자가 아닌 제 3자가 웹사이트에 악성 스크립트를 삽입해 실행할 수 있는 취약점 |
30 | 126 | ### 14.1.1 dangerouslySetInnerHTML prop
|
| 127 | +특정 브라우저 DOM의 innerHTML을 특정한 내용으로 교체할 수 있는 방법 |
| 128 | + |
| 129 | +일반적으로 게시판과 같이 사용자나 관리자가 입력한 내용을 브라우저에 표시하는 용도로 사용 |
| 130 | + |
| 131 | +리액트는 기본적으로 XSS를 방어하기 위해 이스케이프 과정을 거치므로 활용에 따라 props로 해당 메소드를 받으면 이스케이프 작업이 수행되지 않음 |
| 132 | +``` |
| 133 | +function App() => { |
| 134 | + return <div dangerouslySetInnerHTML={{__html: 'First · Second' }} /> |
| 135 | +} |
| 136 | +
|
| 137 | +// <div>First · Second</div> |
| 138 | +``` |
31 | 139 | ### 14.1.2 useRef를 활용한 직접 삽입
|
32 | 140 | ### 14.1.3 리액트에서 XSS 문제를 피하는 방법
|
| 141 | +제 3자가 삽입할 수 있는 HTML을 안전한 HTML 코드로 한 번 치환하기(새니타이즈sanitize, 이스케이프escape) |
| 142 | +- DOMpurity |
| 143 | +- sanitize-html: 허용 목록 작성 |
| 144 | +- js-xss |
| 145 | +보여줄 때뿐만 아니라 콘텐츠를 저장할 때도 이스케이프 과정을 거치면 이후 보여줄 때 바로 보여줄 수 있어 효율적 |
| 146 | +치환 과정은 되도록 서버에서 진행 |
33 | 147 | ## 14.2 getServerSideProps와 서버 컴포넌트를 주의하자
|
| 148 | +getServerSideProps가 반환하는 props나 서버 컴포넌트가 클라이언트 컴포넌트에 반환하는 props는 노출되기 때문에 필요한 값으로만 철저하게 제한되어야함 |
34 | 149 | ## 14.3 <a> 태그의 값에 적절한 제한을 둬야 한다
|
| 150 | +`href"javascript:;"`는 자바스크립트 코드가 존재한다면 실행하는 `<a>`태그의 기본 기능을 막고 별도 이벤트 핸들러만 동시키기 위한 용도 |
| 151 | +``` |
| 152 | +function isSafeHref(href: string) { |
| 153 | + let isState = false |
| 154 | + try { |
| 155 | + const url = new URL(href) |
| 156 | + if (['http:', 'https:'].includes(url.protocol) { |
| 157 | + isState = true |
| 158 | + } |
| 159 | + } catch { |
| 160 | + isState = false |
| 161 | + } |
| 162 | +
|
| 163 | + return isState; |
| 164 | +} |
| 165 | +
|
| 166 | +function App () { |
| 167 | + const unsafeHref = "javascript:alert('hello');" |
| 168 | + const safeHref = 'https://www.naver.com' |
| 169 | + return ( |
| 170 | + <> |
| 171 | + <a href={isSafeHref(unsafeHref) ? unsafeHref: '#'}>위험한 href</a> // # |
| 172 | + <a href={isSafeHref(safeHref) ? safeHref: '#'}>안전한 href</a> // safeHref |
| 173 | + </> |
| 174 | + ) |
| 175 | +} |
| 176 | +``` |
35 | 177 | ## 14.4 HTTP 보안 헤더 설정하기
|
| 178 | +브라우저가 렌더링하는 내용과 관련도니 보안 취약점을 미연에 방지하기 위해 브라우저와 함께 작동하는 헤더 |
36 | 179 | ### 14.4.1 Strict-Transport-Security
|
| 180 | +모든 사이트가 HTTPS를 통해 접근해야하며 HTTP 접근 시도를 HTTPS로 변경함 |
37 | 181 | ### 14.4.2 X-XSS-Protection
|
| 182 | +비표준 기술로, 현재 사파리와 구형 브라우저에서만 제공 |
| 183 | + |
| 184 | +XSS 취약점이 발견되면 페이지 로딩을 중단하는 헤더 |
38 | 185 | ### 14.4.3 X-Frame-Options
|
| 186 | +페이지를 frame, iframe, embed, object 내부에서 렌더링을 허용할지를 나타냄 |
39 | 187 | ### 14.4.4 Permissions-Policy
|
| 188 | +웹사이트에서 사용할 수 있는 기능과 사용할 수 없는 기능을 명시적으로 선언하는 헤더 |
40 | 189 | ### 14.4.5 X-Content-Type-Options
|
| 190 | +Content-type 헤더에서 제공하는 MIME(Multipurpose Internet Mail Extensions) 유형이 브라우저에 의해 임의로 변경되지 않게 하는 헤더 |
41 | 191 | ### 14.4.6 Referrer-Policy
|
| 192 | +현재 요청을 보낸 페이지의 주소를 나타내며 사용자가 어디서 와서 방문 중인지 인식할 수 있는 Referer 헤더에서 사용할 수 있는 데이터를 나타냄 |
42 | 193 | ### 14.4.7 Content-Security-Policy
|
| 194 | +XSS 공격이나 데이터 삽입 공격과 같은 다양한 보안 위협을 막기 위해 설계된 콘텐츠 보안 정책Content-Security-Policy, CSP |
| 195 | +#### *-src |
| 196 | +src로 가져올 수 있는 소스를 제한 |
| 197 | +#### form-action |
| 198 | +폼 양식으로 제출할 수 있는 URL을 제한 |
43 | 199 | ### 14.4.8 보안 헤더 설정하기
|
| 200 | +#### Next.js |
| 201 | +Next.js에서는 애플리케이션 보안을 위해 HTTP 경로별로 보안 헤더 적용 가능 |
| 202 | +#### NGINX |
| 203 | +경로별로 add_header 지시자를 사용해 원하는 응답 헤더를 추가 |
44 | 204 | ### 14.4.9 보안 헤더 확인하기
|
| 205 | +[보안 헤더의 현황을 알려주는 사이트](https://securityheaders.com/) |
45 | 206 | ## 14.5 취약점이 있는 패키지의 사용을 피하자
|
46 | 207 | ## 14.6 OWASP Top 10
|
| 208 | +Open Worldwide (Web) Application Security Project 오픈소스 웹 애플리케이션 보안 프로젝트 |
| 209 | +웹에서 발생할 수 있는 정보 노출, 악성 스크립트, 보안 취약점 등을 연구하며, 주로 10대 웹 애플리케이션 취약점을 공개 |
| 210 | +- Broken Access Control: 사용자가 자신의 권한 밖의 행동을 할 수 있는 취약점 |
| 211 | +- Cryptographic Failures:: 암호화에 실패하거나 오류가 있는 경우, 암호화되지 않은 데이터 등 |
| 212 | +- Injection: XSS와 같이 사용자가 제공하는 데이터를 조작한 공격 |
| 213 | +- Insecure Design: 기획 설계 단계에서 발생한 보안 취약점 |
| 214 | +- Security Misconfiguration: 애플리케이션 설정 시에 잘못된 설정으로 발생하는 취약점 |
| 215 | +- Vulnerable and Outdated Components: 취약점이 있거나 지원이 종료된 소프트웨어를 사용하는 경우에 발생하는 취약점 |
| 216 | +- Software and Data Integrity Failures: 애플리케이션이 신뢰할 수 없는 소스의 소프트웨어나 데이터를 사용하는 경우에 발생하는 취약점 |
| 217 | +- Security Logging and Monitoring Failures: 주요 기능에 대한 적절한 로깅이 없거나 정보가 부족해 사전에 공격을 감지하지 못하는 취약점 |
| 218 | +- Server-Side Request Forgery: 서버에서 이뤄지는 요청을 변조해 원래 가야 할 서버가 아닌 공격자가 의도한 서버로 가게 하거나 위조된 요청을 보내는 취약점 |
47 | 219 | ## 14.7 정리
|
48 | 220 |
|
49 | 221 | # 15. 마치며
|
|
56 | 228 | ## 15.2 언젠가 사라질 수도 있는 리액트
|
57 | 229 | ### 15.2.1 리액트는 그래서 정말 완벽한 라이브러리인가?
|
58 | 230 | ### 15.2.2 오픈소스 생태계의 명과 암
|
| 231 | +#### 페이스북 라이선스 이슈 |
| 232 | +#### 오픈소스는 무료로 계속 제공될 수 있는가? colors.js, faker.js, 그리고 바벨 |
59 | 233 | ### 15.2.3 제이쿼리, AngularJS, 리액트, 그리고 다음은 무엇인가?
|
60 | 234 | ### 15.2.4 웹 개발자로서 가져야 할 유연한 자세
|
0 commit comments