Optimization

leekoby·2023년 3월 30일
0

Optimization

목록 보기
1/2
post-thumbnail

🔧변경내용🔨

제목날짜내용
발행일23.03.30

📌들어가기에 앞서


해당 포스트는 Optimization를 학습한 내용을 정리한 것입니다.





🌈 최적화(Optimization)의 개념

최적화란 무엇일까?

한국정보통신기술협회의 정보통신 용어사전에서는 다음과 같이 정의한다.

최적화, 最適化, optimization

주어진 상황에서 원하는 가장 알맞은 결과를 얻을 수 있도록 처리하는 과정.
최적화는 허용된 자원의 한계 내에서 주어진 요구사항을 만족시키면서 최선의 결과를 얻는 과정이다. 수익과 관련되는 분야에서는 이익을 최대로 내는 과정을 말하기도 한다. 다양한 분야와 때에 따라 다르게 정의할 수 있고 물류(logistics), 설계(design) 문제 등에 응용된다.

분야에 따라서 의미가 조금씩 달라지긴 하지만, 최적화는 보통 주어진 조건으로 최대 효율을 낼 수 있도록 하는 것을 의미한다.

물류업을 예로 들어보자.

물류업에서는 상품의 특성, 출발지와 도착지의 위치, 운송 수단의 속도, 운송 비용, 도착해야 하는 기한 등 고려해야 하는 여러 조건이 존재한다.

이 조건들 아래에서 가능한 적은 비용으로 정해진 시간 내에 운송이 완료될 수 있는 최적의 답안을 찾아가는 것을 물류 최적화라고 할 수 있다.

같은 물건이라도 도착 기한이 길다면 느려도 비용이 저렴한 운송 수단을 선택하는 것이, 도착 기한이 짧다면 비싸더라도 기한 내에 도착할 수 있는 운송 수단을 선택하는 것이 주어진 상황에서 가장 효율적인, 최적의 답안일 것이다.

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

알고리즘 문제를 푸는 것을 생각하면 이해하기 쉽다.

원하는 결과가 나온다면, 메모리를 조금이라도 덜 소모하거나 연산 횟수가 한 번이라도 더 적은 코드가 더 효율적이고 최적화된 코드다.

더 적은 비용, 더 적은 시간을 소모하기 때문이다.

물론 컴퓨터 성능을 업그레이드하면 같은 코드를 사용하더라도 더 빠르게 결과를 얻을 수 있다.

하지만 알고리즘 문제의 결과를 빠르게 확인하기 위해 부품을 업그레이드하는 것은 비용도 많이 들고, 업그레이드한 상태에서도 최적화되지 않은 코드보다 최적화된 코드가 더 빠를 테니, 기본적으로 더 효율적인 코드를 작성하기 위해서 노력하는 것이 좋다.

\

그렇다면 웹 개발에서의 최적화는 무엇을 의미할까?

주어진 조건 아래에서 최대한 빠르게 화면을 표시하도록 만드는 것이다.

이번에 학습한 것은 바로 웹 개발 중에서도 프론트엔드 단에서 할 수 있는 최적화 방법이다.

본격적으로 최적화 방법에 대해서 알아보기 전에, 웹 개발에서 최적화가 왜 필요한지, 그 필요성에 대해서 먼저 알아보자.




💻 최적화의 필요성 및 효과


1. 이탈률 감소

  • 웹 개발에서의 최적화는 화면을 최대한 빠른 속도로 표시하게 하는 것

  • 구글 조사 결과 페이지 로드가 3초 이상 걸리면 53%의 사용자가 사이트를 이탈

  • 또한 페이지 로드 시간이 길어지면 사이트 방문자가 이탈률이 폭발적으로 증가

  • 반대로, 웹 사이트의 성능 최적화를 통해 페이지 로딩 속도를 줄이면, 사용자의 이탈률을 효과적으로 줄일 수 있다는 의미


2. 전환율 증가

  • 이탈률이 줄어들면, 전환율이 높하질 확률도 커짐

  • 전환율이란,

    • 웹 사이트를 방문한 사용자 중 회원가입, 상품 구매, 게시글 조회, 다운로드 등의 행위를 한 방문자의 비율을 의미

3. 수익 증대

  • 빠른 웹 사이트 로딩 속도는 수익 증대까지 이어질 수 있다

  • 이탈률 감소, 전환율 증가는 트래픽 증대 및 회원 수 증가로 이어지고, 이는 곧 수익 증대를 의미


4. 사용자 경험(UX) 향상

  • 최적화는 효과적인 UX 개선 수단



💻 HTML, CSS 코드 최적화하기

  • 화면을 렌더링할 때는 HTML 파일과 CSS 파일이 필요

  • HTML 파일은 DOM 트리를, CSS 파일은 CSSOM 트리를 만들고 두 트리를 결합하여 렌더링할 때 사용

  • 두 트리 중에서 하나라도 변경되면 리렌더링을 유발

  • 따라서 HTML, CSS 코드를 최적화함으로써 렌더링 성능을 향상


1. HTML 최적화 방법


(1) DOM 트리 가볍게 만들기

  • DOM 트리가 깊을수록, 자식 요소가 많을수록 DOM 트리의 복잡도 증가

  • 불필요하게 깊이를 증가시키는 요소가 있다면 삭제

  • ex) < div > 태그가 쓸때없이 있음

// 수정 전
<div>
	<ol>
		<li> 첫 번째 </li>
		<li> 두 번째 </li>
		<li> 세 번째 </li>
	</ol>
</div>

// 수정 후 : 불필요한 div 요소 제거
<ol>
	<li> 첫 번째 </li>
	<li> 두 번째 </li>
	<li> 세 번째 </li>
</ol>

(2) 인라인 스타일 사용하지 않기

  • 인라인 스타일은 개별 요소에 스타일 속성을 작성해주는 것

  • 클래스로 묶어서 한 번에 작성해도 될 스타일 속성을 중복으로 작성하게 되는 경우가 생김.

  • 불필요한 코드 중복은 가독성을 떨어뜨릴 뿐 아니라 파일 크기를 증가시킴

  • ex) < div style= ~~ > 가 쓸때없이 많음

//수정 전
<div style="margin: 10px;"> 마진 10px </div>
<div style="margin: 10px;"> 이것도 마진 10px </div>

//수정 후 : class와 CSS로 대체
<div class="margin10"> 마진 10px </div>
<div class="margin10"> 이것도 마진 10px </div>

.margin10 {
	margin: 10px;
}

2. CSS 최적화 방법


(1) 사용하지 않는 CSS 제거하기

  • CSS 파일의 모든 코드의 분석이 끝난 후에 CSSOM 트리가 생성

  • 그만큼 불필요한 CSS 코드가 있다면 CSSOM 트리의 완성이 늦어짐

  • 사용하지 않는 CSS 코드가 있다면 제거하는 것이 좋음.

  • 요소를 삭제할 일이 생기면, CSS 코드만 남지는 않는지 확인하고 함께 삭제하면 이런 상황을 방지 가능


(2) 간결한 셀렉터 사용하기

셀렉터가 복잡할수록 스타일 계산과 레이아웃에 시간을 더 많이 소모

// 복잡한 CSS 셀렉터 예시
.cart_page .cart_item #firstItem { ... }

// 필요한 경우에는 어쩔 수 없지만, 가능한 한 간결하게 작성해줍니다.
.cart_item { ... }



💻 리소스 로딩 최적화하기

  • HTML 파일에서 JavaScript 파일을 불러올 땐 <script> 요소

  • CSS 파일을 불러올 땐 <link> 요소를 사용

  • 이때 파일을 불러오는 위치에 따라서 렌더링 완료 시점이 달라질 수 있다.


1. CSS 파일 불러오기

  • CSSOM 트리는 CSS 코드를 모두 해석해야 구성할 수 있다.

  • 따라서 CSSOM 트리를 가능한 빠르게 구성할 수 있도록 HTML 문서 최상단에 배치하는 것이 좋다.

// CSS 파일은 HTML 파일 상단의 head 요소 안에서 불러오는 것이 좋다.
<head>
	<link href="style.css" rel="stylesheet" />
</head>

2. JavaScript 파일 불러오기

JavaScript 파일은 DOM 트리 생성이 완료되는 시점인 HTML 문서 최하단에 배치하는 것이 좋다.

  • JavaScript는 DOM 트리와 CSSOM 트리를 동적으로 변경할 수 있다.

    • HTML 코드 파싱 중에 <script> 요소를 만나는 순간 해당 스크립트가 실행

    • <script> 요소 이전까지 생성된 DOM까지만 접근이 가능하다

    • <script> 요소를 HTML 코드 중간에 넣는다면, 해당 요소 이후에 생성될 DOM을 수정하는 코드가 있는 경우에는 화면이 의도한 대로 표시되지 않는

  • 스크립트 실행이 완료되기 전까지 DOM 트리 생성이 중단된다.

    • JavaScript 파일을 다운로드해서 사용하는 경우에는 다운로드 및 스크립트 실행이 완료될 때까지 DOM 트리 생성이 중단된다.

    • DOM 트리 생성이 중단된 시간만큼 렌더링 완료 시간은 늦춰진다.

<body>
	<div>...</div>
	...

	// JavsScript 파일은 body 요소가 닫히기 직전에 작성하는 것이 가장 좋다. 
	<script src="script.js" type="text/javascript"></script>
</body>




💻 브라우저 이미지 최적화하기

  • 페이지의 대부분의 용량은 HTML/CSS/JS와 같은 코드 데이터가 아닌 이미지 파일과 같은 미디어 파일이 차지

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


1. 이미지 스프라이트

클라이언트에서 서버 요청이 증가할수록 로딩 시간은 점점 늘어난다.

따라서 웹 페이지를 로드하는 데 필요한 서버 요청 수를 줄이기 위해 이미지 스프라이트 기법을 사용할 수 있다.

이미지 스프라이트 기법은 여러 개의 이미지를 모아 하나의 스프라이트 이미지로 만들고 CSS의 width, height,background-position 속성을 사용해 이미지의 일정 부분만 클래스 등으로 구분하여 사용하는 방법


2. 아이콘 폰트 사용하기

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

대표적인 아이콘 글꼴 서비스로는 Font Awesome이 있다.

Font Awesome의 사용 방법에는 두 가지가 있다.


(1) CDN으로 사용하기

Font Awesome에 가입하면 키트를 발급해주는데, 이 키트를 HTML 파일에서 <head> 요소에 넣어주기만 하면 CDN으로 Font Awesome을 사용할 준비가 완료

Font Awesome 사이트에서 사용하고 싶은 아이콘을 찾아서 사용할 환경(HTML, React, Vue)에 맞는 코드를 복사하고 붙여넣기만 하면 사용할 수 있다.


(2) Font Awesome 모듈 설치하기

Font Awesome을 다른 라이브러리처럼 설치해서 사용하는 방법도 있다.

React 환경에서 사용할 경우에는 다음과 같은 패키지들을 설치

  • 핵심 패키지 설치

    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 사이트에서 사용하고 싶은 아이콘의 정보를 확인한 후에, 알맞게 불러와서 사용

  • 이때 아이콘 이름은 camelCase로 작성

불러온 아이콘은 클래스명을 직접 붙이거나 Font Awesome이 정해준 방법을 사용하여 스타일링


3. WebP 또는 AVIF 이미지 포맷 사용하기

새롭게 등장한 이미지 포맷인 WebP 또는 AVIF를 사용하여 용량을 감소시킬 수 있다.

모든 브라우저에서 호환되지 않는다는 단점이 있다.
( Can I use WebP?, Can I use AVIF?)

HTML의 <picture> 태그를 이용하면 각 브라우저의 호환에 맞도록 분기를 대체할 수 있다.

<picture>: img 요소의 다중 이미지 리소스(multiple image resources)를 위한 컨테이너를 정의할 때 사용

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

다음과 같이 HTML 태그를 작성할 시, 만약 접속한 브라우저에서 <source>태그 내의 srcset에 정의한 WebP 포맷을 지원하지 않는다면 해당 <source> 태그는 무시

이와 같은 속성을 이용하여 각 브라우저에 따라 이미지 포맷을 최적화할 수 있다.




💻 CDN 사용하기

CDN은 콘텐츠를 좀 더 빠르고 효율적으로 제공하기 위해 설계되었다.

네트워크 지연(latency)은 유저와 호스팅 서버간의 물리적 거리의 한계가 존재하기 때문에 발생할 수 밖에 없다.

유저와 서버의 거리가 멀다면 지연(latency) 또한 늘어난다.

CDN은 이를 해결하고자 세계 곳곳에 분포한 분산된 서버에 콘텐츠를 저장한다.

간단히 말해, CDN은 유저가 가까운 곳에 위치한 데이터 센터(서버)의 데이터를 가져온다.

그러므로 데이터가 전달되기 위해 거쳐야하는 서버의 갯수가 크게 줄기 때문에 로딩 속도가 빨라진다.

추후에 CloudFront, Cloudflare와 같은 CDN 서비스들에 대해서 알아보자.




💻 캐시 사용하기

캐시(Cache)는 다운로드 받은 데이터나 값을 미리 복사해 놓는 임시 장소를 뜻함

데이터에 접근하는 시간이 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고
싶은 경우에 사용

서버에서 logo.jpg라는 이미지를 받아오는 요청을 보낸다고 해보자

첫 번째 요청에서는 이미지를 통째로 받아온다. 해당 이미지를 받아온 적이 없기 때문.

문제는 두 번째 요청부터 완전히 똑같은 파일을 또 다시 받아오는 일이 발생

똑같은 데이터를 굳이 다시 받을 필요가 있을요?

전에 받아두었던 파일을 재사용할 수 있다면, 첫 번째 요청을 보냈을 때처럼 통째로 받아올 필요없이 HTTP헤더의 용량인 0.1M만 받을 수 있을 것 같다.

한 두번은 그렇다고 쳐도, 100번, 1000번의 요청을 보내는 동안 똑같은 파일을 받아온다고 생각해보자.

똑같은 파일을 받느라 100M, 1000M의 네트워크 리소스를 낭비하게 될 것이다.

이럴 때 캐시를 활용하면 이러한 리소스 낭비를 막을 수 있다.

  1. 이번에는 서버에서 응답을 보내줄 때 이미지 파일과 함께 헤더에 Cache-Control 을 작성해서 보내낸다.
    (값은 60으로, 해당 이미지 파일이 60초동안 유효하다는 것을 의미)

  1. 두 번째 요청부터는 캐시를 우선 조회
  • 캐시에 데이터가 존재하면서 아직 60초가 지나지 않아 유효하다면 캐시에서 해당하는 데이터를 가져와서 사용

  1. 만약 유효 시간이 60초가 지났다면? 서버에서 다시 이미지를 받아옴

이렇게 브라우저 캐시를 활용하면 다음과 같은 효과를 볼 수 있다.

  • 캐시가 유효한 시간 동안 네트워크 리소스를 아낄 수 있음

  • 파일을 다시 받아올 필요가 없기 때문에 브라우저 로딩이 빨라짐

  • 로딩이 빨라진 만큼 빠른 사용자 경험 제공 가능




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

캐시를 활용하면 캐시가 유효한 시간 동안은 캐시에 저장해놓은 데이터를 재활용할 수 있다. 하지만 다음과 같은 경우는 어떨까?

캐시 유효 시간은 지났지만, 서버에서 다시 받아와야하는 파일이 캐시에 저장되어 있는 파일과 완전히 동일한 경우,

이때도 똑같은 파일을 다시 받아와야하는 경우가 발생한다.

이럴 땐 유효 시간이 지났다고해도 굳이 똑같은 파일을 다시 받아올 필요 없이 서버의 파일과 캐시의 파일이 동일한지 확인해서 재사용하면 더 효율적이지 않을까?

다행히도 이런 상황에서 사용할 수 있는 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

  • 첫 번째 요청을 보내고 응답을 받으면서 캐시 유효 시간이 60초인 이미지 파일을 같이 받아온다.

  • 이 때, 서버의 파일이 마지막으로 수정된 시간을 의미하는 Last-Modified 헤더에 담긴 내용도 캐시에 함께 저장

  • 캐시 유효 시간인 60초를 초과한 후에 두 번째 요청을 보낸다고 해봅자.

  • If-Modified-Since 를 작성하고 캐시에 함께 저장해놓았던 Last-Modified 값을 담아 요청을 보낸다.

  • 이 값을 이용해 서버 데이터의 최종 수정일과 캐시에 저장된 데이터의 수정일을 비교

  • 두 데이터가 동일한 데이터라면 최종 수정일이 같다.

  • 서버와 캐시의 데이터가 동일한 데이터임이 검증되었다면 서버는 “데이터가 수정되지 않았음”을 의미하는 304 Not Modified 라는 응답

  • 캐시 데이터의 유효 시간이 갱신되면서 해당 데이터를 재사용한다.


Etag 와 If-None-Match

  • 첫 번째 요청을 보내고 응답을 받으면서 캐시 유효 시간이 60초인 이미지 파일을 같이 받아온다.

  • 이 때, 서버의 파일 버전을 의미하는 Etag 헤더에 담긴 내용도 캐시에 함께 저장

캐시 유효 시간인 60초를 초과한 후에 두 번째 요청을 보낸다고 해보자.

  • 비록 유효 시간이 지났어도 해당 데이터를 재사용해도 되는지 확인하기 위해서 요청 헤더 If-None-Match 를 작성

  • 캐시에 함께 저장해놓았던 Etag 값을 담아 요청

  • 이 값을 이용해 서버 데이터의 Etag캐시에 저장된 데이터의 Etag 를 비교

  • 두 데이터가 동일한 데이터라면 두 Etag 값이 같다.

서버와 캐시의 데이터가 동일한 데이터임이 검증되었다면

  • 서버는 “데이터가 수정되지 않았음”을 의미하는 304 Not Modified 를 응답

  • 캐시 데이터의 유효 시간이 갱신되면서 해당 데이터를 재사용할 수 있게 된다.

두 쌍중의 헤더 중 한 쌍만 사용할 수도 있지만, 보통 두 종류를 동시에 사용한다.

둘 중 하나만 사용했다가 매칭되는 응답 헤더가 없는 경우에는 재사용할 수 있는 경우에도 리소스를 다시 받아와야 하는 경우가 생길 수 있기 때문




💻 트리쉐이킹(Tree Shaking)이란?

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

웹 개발을 할 때, 애플리케이션의 규모가 커지면서 코드의 양이 방대해지고, 다양한 라이브러리를 가져다 사용하게 되면 불필요한 코드를 그대로 가져가는 경우,

이런 불필요한 코드들을 찾아내어 제거하면 웹 사이트 성능 최적화에 큰 도움이 된다.

특히 JavaScript는 다음과 같은 이유로 가능하면 트리쉐이킹을 해주는 것이 좋다.


1. JavaScript 파일의 크기

요즘은 규모 있고 화려한 인터랙션을 자랑하는 웹 애플리케이션들이 많다.

웹 사이트에서 인터랙션이 많아졌다는 것은 그만큼 JavaScript의 비중이 높아졌다는 뜻

실제로 http archive를 보면, 2011년도에 비해 웹 애플리케이션의 JavaScript 파일 크기의 중윗값이 데스크톱에서는 477.3% 증가했고, 모바일에서는 무려 827.1%나 증가했다(2023년 3월 기준).

이 자료에서의 파일 크기는 네트워크를 오고 갈 때의 크기로, 압축되어 있는 상태에서의 크기

실제로 파일을 사용할 땐 압축을 해제한 후에 사용해야 하는 것을 생각하면, 파일의 크기는 훨씬 클 것

JavaScript 파일의 크기만 커진 것이 아니다.

JavaScript 파일을 요청하는 HTTP 요청 수 또한 데스크탑에서 144.4%, 모바일에서 425.0% 증가했다.

크기가 훨씬 커진 JavaScript 파일이 늘어난 요청 횟수만큼 더 오가는 것이니, 네트워크 리소스 소모가 그만큼 커졌다는 것을 알 수 있다.

JavaScript 파일 크기의 증가, 요청 횟수의 증가는 그만큼 파일이 오고 가는 동안 화면 표시가 늦어진다는 것을 뜻하고, 네트워크 속도가 느린 환경에서는 더 큰 병목현상을 유발한다.

따라서 트리쉐이킹을 통해 파일 크기를 가능한 줄이는 것이 최적화에 도움이 된다.


2. JavaScript 파일의 실행 시간

JavaScript 파일이 실행되기 위해서는 여러 과정을 거친다.

  • 다운로드부터 필요한 경우에는 우선 요청을 보내어 파일을 다운받아 온 다음 압축을 해제해야 한다.

  • 그다음에는 JavaScript 코드를 파싱하여 DOM 트리를 생성

  • 파싱이 끝나면 컴파일하여 컴퓨터가 이해할 수 있는 언어 변환

이 컴파일 과정까지 거쳐야지 비로소 코드를 실행할 수 있다.

이처럼 코드 실행까지 거쳐야 하는 과정이 많기 때문에 JavaScript는 다른 리소스에 비해서 실행까지 상대적으로 많은 시간을 소모하게 된다.

실제로 JavaScript 파일의 크기가 커진 만큼, 파일의 실행 시간 또한 증가한 것을 알 수 있다.

데스크톱에서는 측정한 기간이 얼마 되지 않아 확인하기 어렵지만, 모바일에서는 277.8%만큼 실행 시간이 길어져 3.4초의 시간이 소요되는 것을 확인할 수 있다.

JavaScript 파일의 실행은 CPU에 크게 영향을 받는데, 그렇다 보니 사양이 천차만별인 모바일 환경에서 그 영향이 더욱 두드러진다.

실제로 휴대폰의 사양에 따라 소모 시간이 크게 차이 나는 것을 아래 도표를 통해 확인할 수 있다.

앞서 공부한 최적화의 개념에서, 페이지 로드 시간이 3초를 넘어가면 53%의 사용자가 이탈한다고 했다.

JavaScript 파일을 요청하고 다운받아 오는 시간을 제외하고서 파일을 실행하는 데만 3.4초가 걸린다면 파일을 실행하는 동안에만 이미 50% 이상의 사용자가 이탈할 것이라고 예상할 수 있다.

기기 환경에 따라서 3.4초의 몇 배의 시간을 파일 실행에만 사용할 수도 있는 만큼 이탈률은 그만큼 커질 수 있다.

이러한 상황을 최대한 줄이기 위해서라도 트리쉐이킹을 통한 최적화가 필요하다.




💻 JavaScript 트리쉐이킹

  • 웹팩 4버전 이상을 사용하는 경우에는 ES6 모듈(import, export를 사용하는 모듈)을 대상으로는 기본적인 트리쉐이킹을 제공

  • Create React App을 통해 만든 React 애플리케이션도 웹팩을 사용하고 있기 때문에 트리쉐이킹이 가능

    웹팩을 사용하는 환경에서 효과적으로 트리쉐이킹을 수행하는 방법에 대해서 알아보자.


1. 필요한 모듈만 import 하기

import 구문을 사용해서 라이브러리를 불러와서 사용할 때, 라이브러리 전체를 불러오는 것이 아니라 필요한 모듈만 불러오면 번들링 과정에서 사용하는 부분의 코드만 포함시키기 때문에 트리쉐이킹이 가능해진다.

예시를 살펴보자.

React를 사용하는 애플리케이션에서 React를 통째로 불러온 다음 그 안에 무엇이 있는지 console.log를 사용해 확인해보자.

확인 결과, 아래와 같이 React의 모든 코드가 불려 온 것을 확인할 수 있다.

이렇게 모든 코드를 불러오면, 이 중에서 실제로 사용하는 코드는 얼마 되지 않더라도 번들링할때 이 모든 코드를 같이 빌드하게 된다.

불필요한 코드가 포함되는 것이다.

이를 방지하기 위해서 import 해올 때 아래와 같이 실제로 사용할 코드만 불러와 주면 된다.

import { useState, useEffect } from 'react'

그러면 불러오지 않은 코드는 빌드할 때 제외되므로 코드의 크기를 줄일 수 있다.


2. Babelrc 파일 설정하기

Babel은 자바스크립트 문법이 구형 브라우저에서도 호환이 가능하도록 ES5 문법으로 변환하는 라이브러리

이 때 ES5문법은 import를 지원하지 않기 때문에 commonJS 문법의 require로 변경시키는데, 이 과정은 트리쉐이킹에 큰 걸림돌이 된다.

require는 export 되는 모든 모듈을 불러오기 때문이다.

1번에서 작성한 것처럼 필요한 모듈만 불러오기 위한 코드를 작성해도 소용이 없어진다.

이를 방지하기 위해서 Barbelrc 파일에 다음과 같은 코드를 작성해주면 ES5로 변환하는 것을 막을 수 있다.

{
  “presets”: [ 
    [
      “@babel/preset-env”,
      {
	    "modules": false
      }
    ]
 ]
}

반대로, modules 값을 true로 설정하면 항상 ES5 문법으로 변환하므로 주의


3. sideEffects 설정하기

웹팩은 사이드 이펙트를 일으킬 수 있는 코드의 경우, 사용하지 않는 코드라도 트리쉐이킹 대상에서 제외시킨다.

const crews = ['kimcoding', 'parkhacker']

const addCrew = function (name) {
	crews.push(name)
}

위 코드에서 addCrew 함수는 함수 외부에 있는 배열인 crews를 변경시키는 함수

해당 함수는 비순수함수이기 때문에 트리쉐이킹을 통해 제외시킬 경우엔 문제가 생길 수도 있다고 판단되기 때문에 웹팩은 이 코드를 제외시키지 않는다.

이럴 때 package.json 파일에서 sideEffects를 설정해서 사이드 이펙트가 발생하지 않으니 코드를 제외시켜도 된다고 웹팩에게 알려줄 수 있다.

다음과 같이 작성하면 애플리케이션 전체에서 사이드 이펙트가 발생하지 않을 것이라고 알려줄 수 있다.

{
  "name": "tree-shaking",
  "version": "1.0.0",
  "sideEffects": false
}

혹은 아래와 같이 작성하여 특정 파일에서는 발생하지 않을 것임을 알려줄 수 있다.

{
  "name": "tree-shaking",
  "version": "1.0.0",
  "sideEffects": ["./src/components/NoSideEffect.js"]
}

4. ES6 문법을 사용하는 모듈 사용하기

모듈에 따라서 ES5로 작성된 모듈이 있을 수도 있다.

앞의 순서대로 작성해도 트리쉐이킹이 작동하지 않는 라이브러리가 있다면,

해당 라이브러리가 어떤 문법을 사용하고 있는지 확인해보자.

대체할 수 없는 경우가 아니라면, ES6를 지원하는 다른 모듈을 사용하는 것이 트리쉐이킹에 유리하다.

ES6 문법을 사용하는 모듈을 사용하면 해당 모듈에서도 필요한 부분만 import 해서 사용하지 않는 코드는 빌드할 때 제외되기 때문이다.




📚 레퍼런스

코드스테이츠 수업자료

HTML에서 script태그는 어디에 위치해야 할까?

script의 async와 defer 속성

http archive

The cost of JavaScript in 2019

Tree Shaking

0개의 댓글