[TIL] Optimization

ㅜㅜ·2022년 12월 5일
1

Today I learn

목록 보기
66/77
post-thumbnail

최적화란 ?

최적화는 주어진 조건으로 최대 효율을 낼 수 있도록 하는 것을 말한다.

컴퓨터 공학에서의 최적화는 가능한 적은 리소스를 소모하면서 가능한 한 빠르게 원하는 결과를 얻을 수 있도록 하는 것을 의미하며,

웹 개발에서의 최적화는 주어진 조건 아래에서 최대한 빠르게 화면을 표시하도록 만드는 것을 말한다.
오늘 공부한 내용은 웹 개발 중에서도 프론트엔드 단에서 할 수 있는 최적화 방법이다.

최적화 필요성 및 효과

  • 이탈률 감소
  • 전환율 증가
  • 수익 증대
  • 사용자 경험 (UX) 향상


최적화 기법

🟢 HTML, CSS 최적화 기법

HTML 최적화

  • DOM 트리 가볍게 만들기
  • 인라인 스타일 사용하지 않기

CSS 최적화

  • 사용하지 않는 CSS 제거
  • 간결한 셀렉터 사용

🟢 리소스 로딩 최적화

  • CSS 파일 불러오기
    : CSSOM 트리 가능한 빠르게 구성할 수 있도록 HTML 문서 최상단에 배치하는 것 권장. (head 요소 안에서)

  • Js 파일 불러오기
    : Js는 DOM 트리와 CSSOM 트리를 동적으로 변경할 수 있다.

    HTML 코드 파싱 중 script 요소 만나면 해당 스크립트가 실행되며 script 요소 이전까지 생성된 DOM까지만 접근 가능.
    script 요소를 HTML 코드 중간에 넣는다면 해당 요소 이후 생성될 DOM을 수정하는 코드가 있는 경우 화면이 의도한 대로 표시되지 않음.게다가 스크립트 실행 완료 전까지 DOM 트리 생성이 중단되므로 랜더링 완료 시간이 늦춰진다.

    이런 이유로 JS 파일을 DOM 트리 생성 완료 시점인 HTML 문서 최하단에 배치하는 것이 좋음. (body 요소가 닫히기 직전)

🟢 브라우저 이미지 최적화

미디어 파일이 차지하므로 이미지의 용량을 줄이거나 요청의 수를 줄이는 것을 우선적으로 고려할 시, 사용자 경험을 빠르게 개선할 수 있다.

  • 이미지 스프라이트 사용
    : 여러 개의 이미지를 모아 하나의 스프라이트 이미지로 만들고 CSS의 background-position 속성을 이용해 일정 부분만 클래스 등으로 구분해 사용하는 방법.
    하나의 이미지를 배경 이미지로 사용하되, 표시하고 싶은 부분에 맞춰 width, height, background-position속성을 주어 아이콘을 만든다.
    한번의 이미지 요청으로 대부분의 개별 이미지를 사용할 수 있기 때문에 네트워크 로딩 시간을 줄일 수 있다.
    많은 이미지 파일을 개별로 관리할 필요없이 특정 스프라이트 이미지 파일만을 관리하면 되므로 관리가 용이하다는 장점도 있음.

  • 아이콘 폰트 이용하기
    : 아이콘 사용이 많을 때에는, 모든 아이콘을 이미지로 사용하는 것이 아니라 아이콘 폰트를 사용하여 용량을 줄일 수 있다.

대표적인 아이콘 글꼴 서비스 Font Awesome 사용법

  • CDN으로 사용
    : 가입 후 발급해주는 키트를 HTML 파일 <head> 요소에 넣어주고, 원하는 아이콘을 사용할 환경(HTML, React, Vue)에 맞는 코드로 복사해서 붙여넣기 해 사용 가능.
  • Font Awesome 모듈 설치
    리액트 환경에서 사용할 경우 다음과 같은 패키지들 설치
// 핵심 패키지 설치
npm i --save @fortawesome/fontawesome-svg-core
// 아이콘 패키지 설치 (해당 코드는 무료 아이콘들)
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons
// Font Awesome React 구성 요소 설치
npm i --save @fortawesome/react-fontawesome@latest

설치 후 사용하고 싶은 아이콘 정보 확인한 뒤 불러와 사용하면 되고, 아이콘 이름은 카멜 케이스로 작성해야 함.
불러온 아이콘은 클래스명을 직접 붙이거나 Font Awesome이 정해준 방법을 사용해 스타일링 해줄 수 있음.

  • WebP 또는 AVIF 이미지 포맷 사용하기
    : 용량 감소에 효과적인데, 비교적 최근에 등장한 이미지 포맷이기 때문에 JPEG 포맷처럼 모든 브라우저에서 호환되지 않는다는 단점이 있음. (WebP는 구버전, 사파리 브라우저에서 지원하지 않고, AVFI는 크롬이나 오페라 등 소수 브라우저에서만 지원)
    호환 문제는 HTML의 <picture> 태그 이용해 각 브라우저 호환에 맞도록 분기 대체할 수 있음.
// 접속한 브라우저에서 source 태그 내 srcset에 정의한
//WebP 포맷을 지원하지 않는다면 해당 source 태그는 무시됨.

<picture>
  <source srcset="logo.webp" type="image/webp">
  <img src="logo.png" alt="logo">
</picture>

🟢 CDN 사용하기

CDN은 네트워크 지연(유저와 호스팅 서버간 물리적 거리의 한계로 인해 발생)을 해결하고자 세계 곳곳에 분산된 서버에 콘텐츠를 저장한다.

CDN은 유저가 가까운 곳에 위치한 테이터 센터(서버)의 데이터를 가져오는데, 데이터가 전달되기 위해 거쳐야하는 서버 개수가 크게 줄기 때문에 로딩 속도가 빨라진다.

CloudFront, Cloudflare와 같은 CDN 서비스들이 있음.

캐시 사용하기

캐시는 다운로드 받은 데이터나 값을 미리 복사해놓는 임시 장소를 뜻하며 데이터에 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우 사용.

캐시 효과

  • 캐시가 유효한 시간 동안 네트워크 리소스 아낄 수 있음
  • 파일 다시 받아올 필요 없어 브라우저 로딩 빨라짐
  • 로딩 빨라진 만큼 빠른 사용자 경험 제공 가능

캐시 검증 헤더와 조건부 요청

캐시 유효 시간은 지났지만 캐시에 저장된 파일과 완전히 동일한 파일을 받아와야 할 때 사용할 수 있는 HTTP 헤더들

  • 캐시 검증 헤더 : 캐시에 저장된 데이터와 서버의 데이터가 동일한지 확인하기 위한 정보를 담은 응답 헤더.
    - Last-Modified : 데이터가 마지막으로 수정된 시점을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-Modified-Since 와 묶어서 사용합니다.
    - Etag : 데이터의 버전을 의미하는 응답 헤더로, 조건부 요청 헤더인 If-None-Match 와 묶어서 사용합니다.
  • 조건부 요청 헤더 : 캐시 테이터와 서버 데이터가 동일하다면 재사용하게 해달라는 의미의 요청 헤더
    - If-Modified-Since : 캐시된 리소스의 Last-Modified 값 이후에 서버 리소스가 수정되었는지 확인하고, 수정되지 않았다면 캐시된 리소스를 사용합니다.
    - If-None-Match : 캐시된 리소스의 ETag 값과 현재 서버 리소스의 ETag 값이 같은지 확인하고, 같으면 캐시된 리소스를 사용합니다.

실제 사용

//Last-Modified와 If-Modified-Since 확인 후 
//서버와 캐시의 데이터가 동일한 데이터임이 검증되면 데이터가 수정되지 않았음을 의미하는
//304 Not Modified 라는 응답을 보내주고 캐시 데이터 유효 시간이 갱신되면서 해당 데이터 재사용할 수 있게 됨 
    
//Etag와 If-None-Match 확인 후 
//서버 데이터의 Etag와 캐시에 저장된 데이터의 Etag가 동일하면
//데이터가 수정되지 않았음을 의미하는 304 Not Modified 응답 보내주고
//캐시 데이터 유효 시간 갱신되면서 해당 데이터 재사용 가능해짐

두 쌍의 헤더 중 한 쌍만 사용할 수도 있지만 보통은 두 종류를 동시에 사용한다.
둘 중 하나만 사용했다 매칭되는 응답 헤더가 없는 경우에는 재사용할 수 있는 경우에도 리소스 다시 받아와야 하는 경우가 생길 수 있기 때문.




🌲 트리 쉐이킹

나무를 흔들어 잔가지를 털어내듯 불필요한 코드 제거하는 것 의미

Java Script에 트리쉐이킹이 필요한 이유

  • 파일의 크기 : 규모 있고 화려한 인터랙션 사용할 수 있으므로 파일 크기가 크다
  • Js 파일의 실행 시간 : 코드 실행까지 거쳐야 하는 과정이 많아서 다른 리소스에 비해 실행까지 상대적으로 많은 시간을 소모한다.

🙄 다운로드가 필요한 경우 요청 보내 파일 다운 받아온 다음 압축 해제 ⇒ Js 코드 파싱해 DOM트리 생성 ⇒ 파싱 끝나면 컴파일해 컴퓨터가 이해할 수 있는 언어로 바꾸기 ⇒ 코드 실행

Java Script 트리쉐이킹

웹팩 4버전 이상 사용시 ES6 모듈(import, export 사용하는 모듈) 대상으로 기본적인 트리쉐이킹 제공함.

Create React App 통해 만든 React 어플리케이션도 웹팩 사용하므로 트리쉐이킹 가능.

  • 필요한 모듈만 import 하기 (라이브러리 전체가 아닌)
  • Babelrc 파일 설정하기 : 바벨은 구형 브라우저에서 호환 가능하도록 ES5 문법으로 변환하는 라이브러리인데 ES5문법은 import 지원하지 않아 commonJs 문법의 require로 변경시키는데 이것이 트리쉐이킹에 걸림돌이 됨. require는 export되는 모든 모듈을 불러오기 때문임. Babelrc 파일에 아래와 같은 코드 작성해주면 ES5로 변환하는 것 막을 수 있음.
    {
      “presets”: [ 
        [
          “@babel/preset-env”,
          {
    	    "modules": false
          }
        ]
     ]
    }
    //modules 값이 true이면 항상 ES5 문법으로 변환 
  • sideEffects 설정하기 : 웹팩은 사이드 이펙트를 일으킬 수 있는 코드의 경우, 사용하지 않는 코드라도 트리쉐이킹 대상에서 제외시킨다. package.json 파일에서 sideEffects를 설정해 사이드 이펙트가 생기지 않을 것으로 코드를 제외시켜도 됨을 웹팩으로 알려줄 수 있다. 아래와 같이 작성하면 어플리케이션 전체에서 사이드 이펙트가 발생하지 않을 것이라고 알려주는것.
    {
      "name": "tree-shaking",
      "version": "1.0.0",
      "sideEffects": false
    }
    특정 파일에서 발생하지 않을 것임을 알려주려면 아래와 같이 작성
    {
      "name": "tree-shaking",
      "version": "1.0.0",
      "sideEffects": ["./src/components/NoSideEffect.js"]
    }
  • ES6 문법 사용하는 모듈 사용하기 : 보통은 위 과정만 잘 작성시 트리쉐이킹이 잘 작동하나 트리쉐이킹이 잘 작동되지 않는 라이브러리가 있다면 어떤 문법 사용하고 있는지 확인해봐야 함. 모듈에 따라 일부만 ES5로 작성된 경우 해당 모듈 대체할 수 있으면서 ES6를 지원하는 다른 모듈을 사용하는 것이 트리쉐이킹에 유리함. (ES6 문법을 사용하는 모듈을 사용하면 해당 모듈에서도 필요한 부분만 import해서 사용하지 않는 코드는 빌드할 때 제외되기 때문)



💡 Lighthouse

구글에서 개발한 오픈소스로서 웹 페이지의 품질을 개선할 수 있는 자동화 툴.

Lighthouse는 다양한 지표를 이용하여 웹페이지의 성능 검사를 해줄 뿐만 아니라 그에 대한 개선책도 제공해줌.

성능, 접근성, PWA, SEO 등을 검사하며 이를 이용해 사용자는 어떤 웹페이지든 품질 검사를 할 수 있다.

Lighthouse는 Chrome DevTools부터 CLI, 노드 모듈 등 다양한 경로를 통해 사용할 수 있다. 검사할 페이지의 url을 Lighthouse에 전달하면 Lighthouse는 해당 페이지에 대한 여러 검사를 실행함.

그 후, 검사 결과에 따른 리포트를 생성하고 개발자는 해당 리포트를 통해 점수가 낮은 지표에 대해 개선할 수 있음. 또한 각각의 지표가 왜 중요한지, 어떻게 개선할 수 있는 지에 대한 레퍼런스도 리포트에서 참고할 수 있다.

실행 방법

  • Lighthouse 크롬 개발자 도구에서 실행하기
  • Node CLI에서 실행하기
//Node CLI에서 실행하려면 설치 필요 

//설치
npm install -g lighthouse

//검사 실행 
lighthouse <url>

//모든 옵션 볼 수 있음 
lighthouse --help

Lighthouse 분석 결과 항목

  • Performance : 웹 성능 측정 항목으로 화면에 콘텐츠가 표시되는지 걸리는 시간, 표시된 뒤 사용자와 상호작용하기까지 걸리는 시간, 화면에 불안정한 요소는 없는지 등 확인
  • Accessibility : 웹 접근성 측정 항목으로 대체 텍스트 잘 작성했는지, 배경색과 콘텐츠 생상의 대비가 충분한지, 적절한 WAI-ARIA 속성을 사용했는지 등 확인함.
  • Best Practices : 웹 페이지가 웹 표준 모범 사례를 잘 따르고 있는지 확인함. HTTPS 프로토콜을 사용하는지, 사용자가 확인할 확률은 높지 않더라도 콘솔 창에 오류가 표시되지는 않는지 등을 확인.
  • SEO : 웹 페이지가 검색 엔진 최적화가 잘 되어있는지 확인함. 애플리케이션의 robots.txt가 유효한지, <meta> 요소는 잘 작성되어 있는지, 텍스트 크기가 읽기에 무리가 없는지 등을 확인함.
  • PWA(Progressive Web App) : 해당 웹 사이트가 모바일 애플리케이션으로 잘 작동하는지 확인하는 항목으로 앱 아이콘을 제공하는지, 스플래시 화면이 있는지, 화면 크기에 맞게 콘텐츠를 적절하게 배치했는지 등을 점수가 아닌 체크리스트로 확인함.

주요 Metric들이 무엇을 측정하는가?

  • First Contentful Paint(FCP) : Performance 지표를 추적하는 매트릭. 사용자가 페이지에 접속했을 때 브라우저가 DOM 컨텐츠의 첫 번째 부분을 렌더링하는 데 걸리는 시간을 측정. (= 사용자가 감지하는 페이지의 로딩 속도를 측정) 우수한 UX를 제공하기 위해서는 FCP가 1.8초 이하여야 함.
  • Largest Contentful Paing(LCP) : 뷰포트를 차지하는 가장 큰 콘텐츠(이미지 또는 텍스트 블록)의 렌더 시간을 측정한다. 콘텐츠가 유저에게 보이는 시간까지를 가늠할 수 있음.
  • Speed Index : 성능으로 지표를 추적하는 매트릭으로 페이지를 로드하는 동안 얼마나 빨리 컨텐츠가 시작적으로 표시되는지를 측정함. Lighthouse는 먼저 브라우저의 페이지 로딩과정을 각 프레임마다 캡쳐하고 프레임 간 화면에 보이는 요소들을 계산한다. 그 후 Speedline Node.js module을 이용하여 Speed Index 점수를 그래프의 형태로 나타냄.
  • Time to interactive : TTI는 페이지가 로드되는 시점부터 사용자와의 상호작용이 가능한 시점까지의 시간을 측정함. 페이지가 완전히 상호 작용 가능하기까지의 기준은
    • 페이지에 FCP로 측정된 컨텐츠가 표시된

    • 이벤트 핸들러가 가장 잘 보이는 페이지의 엘리먼트에 등록됨

    • 페이지가 0.05초안에 사용자의 상호작용에 응답함.

      TTI 점수는 아카이브된 HTTP 데이터를 기반으로 백분위 단위로 점수를 측정하며 아래의 표를 기준으로 점수를 해석할 수 있다.

  • Total Blocking Time : 대부분의 사용자는 0.05초가 넘는 작업에는 응답이 올때까지 계속 키보드를 두드리거나 마우스를 클릭하기 때문에 페이지가 느리다고 인식한다. 이를 개선하기 위한 지표가 TBT인데, 페이지가 유저와 상호작용하기까지의 막혀있는 시간을 측정함. Lighthouse에서는 FCP와 TTI 사이에 긴 시간이 걸리는 작업들을 모두 기록하여 TBT를 측정함.
  • Cumulative Laycout Shift : 갑자기 페이지 일부분이 바뀌거나, 아무런 경고 없이 텍스트가 움직이며 읽던 부분을 놓치게 되거나, 더 심한 경우 링크나 버튼을 탭하기 직전 갑작스레 링크가 움직이는 바람에 의도하지 않은 것을 클릭할 수도 있음. CLS는 사용자에게 컨텐츠가 화면에서 얼마나 많이 움직이는지(불안정한 지)를 수치화한 지표임.



💡 Lighthouse 활용해보기

네이버로 검사를 해봤는데 위는 모바일, 그러니까 m.naver.com일 때의 검사 결과이고, 아래는 데스크탑일 때 naver.com일 때의 검사 결과이다.

다른 항목들은 비슷한데, 모바일 환경에서 성능이 안 좋게 나왔다.

아래는 모바일 환경 네이버 웹 리포트의 Opportunities 부분이다.

1. Serve images in next-gen formats : 이미지들을 다음 세대 포맷으로 다룰 것
: 브라우저 이미지 최적화 파트에서 배웠듯이 WebP, AVIF는 PNG나 JPEG보다 더 나은 압축을 제공하는 이미지 포맷이고, 더 빠른 다운로드들과 적은 데이터를 소비한다고 한다.
오늘 소그룹에서 한 팀원이 이야기해주시기론 네이버 같은 큰 회사들은 작년까지는 WebP, AVIF 같은 포맷을 잘 사용하지 않았는데 인터넷 익스플로러에서 해당 포맷들을 지원하지 않았기 때문이라고 한다.
모쪼록 이제는 편히 잠든 인터넷 익스플로러이므로...이제는 해당 포맷들을 좀 더 자유롭게 사용해볼 수 있지 않을까...

2. use video formats for animated content : 애니메이션이 있는 콘텐츠들을 비디오 포맷으로 사용할 것
: 네이버에서 모바일 환경에서 움직이는 이미지들에 GIF 포맷을 사용하고 있는데, 해당 포맷은 애니메이션이 있는 포맷들을 제공하는 데 비효율적이라고 한다. 리포트에서 추천하기로는 MPEG4/WebM 비디오 포맷을 사용할 것을 권장하고 있었다. PNG나 WebP와 같은 정적인 이미지들을 GIF 대신 사용하는 것도 네트워크의 byte들을 아끼는 방법으로 추천했다.

3. Reduce unused Js, Reduce unused CSS : 사용되지 않는 JS나 CSS를 줄일 것
: 해당 부분을 펼쳐서 보면 어떤 부분들에서 얼마나 감소를 시킬 수 있는지 'Potential Savings'에 대해서도 알려주고 있는데 URL을 눌러보면... 엄청난 양의 코드가 나와서 사실 눈으로 직접 확인하기는 어려워보였다. 모쪼록 사용되지 않는 JS, CSS를 포착해서 고칠 수 있다면 성능 최적화에 도움이 될 것이다.

4. Efficiently encode images : 효율적으로 이미지들을 인코드할 것
: 이 부분도 1번과 비슷하게 이미지 용량을 최적화하라는 의미인데, 이런 이미지 용량 최적화를 위한 방법으로 Lighthouse에서 추천해주고 있는 방법은 이미지 CDN을 사용하는 방법, 수동 이미지 압축 등이 있다.
이미지 CDN은 서버로부터 이미지를 불러오면 이미지 사이즈를 조절해주는 회사에 의뢰해서 원하는 형태로 이미지를 바꾸는 방법을 말한다.

5. Eliminate render-blocking resources : 렌더링을 지연시키는 리소스들을 제거할 것
: Lighthouse는 웹 페이지의 first paint를 지연시키는 리소스를 알려주고, 이 리스트를 확인해서 중요하지 않은 리소스들을 연기하고, 사용되지 않는 경우 제거하는 방식으로 성능을 높이도록 권장하고 있다.
Lighthouse는 두 종류의 렌더링 차단 리소스를 찾는데, scripts와 stylesheet이다.

아래와 같은 <script>를 지정한다.

  • 문서의 head 안에 있는 것
  • defer 속성이 없는 것
  • async 속성이 없는 것

아래와 같은 <link rel="stylesheet">를 지정합니다.

  • disabled 속성이 없는 것. (disabled 속성이 지정되어 있다면 브라우저는 그 파일을 다운로드하지 않습니다.)
  • 유저의 기기에 맞는 media 속성을 가지지 않은 것.

5번에 대한 자세한 부분을 정리해둔 블로그가 있어 연결해둔다.

profile
다시 일어나는 중

0개의 댓글