์น์ฑ๋ฅ ๊ฐ์ ์ ์ํ ์ด๊ฒ์ ๊ฒ/์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ ์์ฑํด๋ณด๋ ค๊ณ ํ๋ค. ๊ฐ์ธ์ ์ผ๋ก ๊ณต๋ถํ๋ ๋ด์ฉ์ด๋ผ ๋์๊ฐ ์์ ์ ์์ผ๋ ์ฃผ์!
Bundle analyzer
bundle๋ JS ํ์ผ์ด ์ด๋ค ์ฝ๋๋ค๋ก ์ด๋์ ๋์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ฐ์ํ ํ์ฌ ๋ณด์ฌ์ฃผ๋๊ฒ์ด ๋ฐ๋ก bundle-analyzer์ด๋ค. NEXTJS๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ @next/bundle-analyzer๋ฅผ, CRA(create-react-app)์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ cra-bundle-analyzer๋ฅผ ์ธํ
ํ๋ฉด ๋๋ค. bundle analyzer๋ฅผ ์คํํ๋ฉด ์๋ ์ฌ์ง์ฒ๋ผ JS ์ฝ๋๊ฐ ์ด๋ป๊ฒ ๊ตฌ์ฑ๋์ด ์๋์ง ๋ณด์ฌ์ค๋ค. ๊ทธ ํฌ๊ธฐ๊ฐ ํด์๋ก ํฐ ์ฉ๋์ ์ฐจ์งํ๋ค.
Image size optimization with CDN
image CDN์ ์ฌ์ฉํ๋ค. CDN(Content Delivery Network)์ ์ปจํ
์ธ ๋ฅผ ๋น ๋ฅด๊ณ , ์ ๋ ดํ๊ณ , ์์ ํ๊ฒ ์ ์กํ๊ธฐ ์ํด ์ฐ๊ฒฐ๋ ์๋ฒ ๋คํธ์ํฌ์ด๋ค. ์ง๋ฆฌ์ ์ผ๋ก ๋ถ์ฐ์์ผ ์น ์ปจํ
์ธ ๋ฅผ ์ฌ์ฉ์์ ๊ฐ๊น์ด ๊ณณ์์ ์ ์กํจ์ผ๋ก์จ ์ ์ก ์๋๋ฅผ ๋์ธ๋ค.
CDN์ image ๋ฟ๋ง ์๋๋ผ web page, image, video ๋ฑ์ ์ปจํ
์ธ ๋ฅผ ์ฌ์ฉ์์ ๋ฌผ๋ฆฌ์ ์์น์ ๊ฐ๊น์ด server์ cachingํ๋ค. ๋ํ์ ์ธ image CDN service๋ cloudinary, imagekit.io ๋ฑ์ด ์๋ค.
Code splitting & Lazy loading
bundler๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋์ bundle๋ก ์ฝ๋๋ฅผ ๋ณํํด์ฃผ์ด ์ฑ์ ๋ก๋ํ ๋ ํธ๋ฆฌํ์ง๋ง, ๋ณ๋์ ํฐ third-party library๋ฅผ ํฌํจํ๋ ๊ฒฝ์ฐ ์ด ๋ํ bundle์ ํฌํจ๋๊ธฐ ๋๋ฌธ์ ๋ก๋์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ด๋ code splitting๊ณผ lazy loading์ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ์ ํฅ์ ์ํฌ ์ ์๋ค.
lazy component๋ Suspense ํ์์์ rendering๋์ด์ผ ํ๋ฉฐ, suspense๋ lazy component๊ฐ ๋ก๋๋๊ธธ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ก๋ฉํ๋ฉด์ ๋ณด์ฌ์ค ์ ์๋ค.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}Don't use the INDEX as KEY
key๋ React๊ฐ ์ด๋ค ํญ๋ชฉ์ ๋ณ๊ฒฝ, ์ถ๊ฐ ๋๋ ์ญ์ ํ ์ง ์๋ณํ๋ ๊ฒ์ ๋๋๋ค. ์์ ์ ์ธ ๊ณ ์ ์ฑ์ ๋ถ์ฌํ๊ธฐ ์ํด ๋ด๋ถ์ element์ ๋ช
์ํ๊ฒํ๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ๋ data set์ uniqueํ id๋ฅผ key๋ก ์ฌ์ฉํ๋ค.
todos.map((todo, index) => (
<Todo {...todo} key={index} /> // don't
));
}๋ง์ฝ ํญ๋ชฉ์ ์์๊ฐ ๋ฐ๋ ์ ์๋ ๊ฒฝ์ฐ, key์ index๋ฅผ ์ฌ์ฉํ๋๊ฒ์ ๊ถ์ฅํ์ง ์๋๋ค. ์ฑ๋ฅ์ด ์ ํ๋๊ฑฐ๋ component์ state์ ๊ด๋ จ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ํฌ์คํ
์์ ์์ธํ ์ค๋ช
๋์ด ์๋ค. component๋ key๋ฅผ ๋ณด๊ณ ๊ฐฑ์ ๋๊ณ ์ฌ์ฌ์ฉ๋๋ค. index๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํญ๋ชฉ์ ์์๊ฐ ๋ฐ๋์์ ๊ฒฝ์ฐ key๋ํ ๋ฐ๋์์ ๊ฑฐ๊ณ ์ด๋ state์ ์ํฅ์ ์ค ์ ์๋ค. ํ์ง๋ง ๋ฐฐ์ด์ ํญ๋ชฉ๋ค์ด staticํ์ฌ ๋ณ๊ฒฝ๋์ง ์๋ ๋จ์ ๋ฐ๋ณต์ ๊ฒฝ์ฐ์๋ index๋ฅผ key๋ก ์ฌ์ฉํด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ํ๋ฅ ์ด ๋ฎ๋ค.
Animation optimization
will-change ์์ฑ์ ์์์ ์์๋๋ ๋ณํ์ ์ข
๋ฅ์ ๊ดํ ํํธ๋ฅผ ๋ธ๋ผ์ฐ์ ์ ์ ๊ณตํ๋ค. ๊ทธ๋์ ์์๊ฐ ๋ณํ๋๊ธฐ ์ ์ ๋ฏธ๋ฆฌ ๋ธ๋ผ์ฐ์ ๋ ์ ์ ํ๊ฒ ์ต์ ํํ ์ ์๋ค.
animation์ ๊ตฌํํ ๋ javascript code๋ฅผ ์ด์ฉํ ์๋ ์์ง๋ง css๋ฅผ ์ด์ฉํด์๋ ํ๋ฅญํ animation์ ๊ตฌํํ ์ ์๋ค. ํ์ง๋ง animation ์ฒ๋ผ ์์ง์์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ ๊ทธ์ ๋ฐ๋ฅธ ์ฐ์ฐ๋์๋ ๊ฝค๋ ๋ง์ ๋ถํ๊ฐ ํ์ํ๊ณ ์ ์ ํ ํจ์จ์ ์ธ ์ฝ๋๋ก ์์ฑํ๋๊ฒ์ด ๋ฐ๋์ ํ์ํ๋ค.
์ผ๋ฐ์ ์ผ๋ก animation์ ๊ตฌํํ๊ฒ ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ ํ๋ฆ์ผ๋ก ๋์ํ๊ฒ ๋๋ค.
- Recalculate (
element์style์ด ์ ์ฉ๋ ๊ฒฝ์ฐ ๋ฐ์) - Reflow (์ ์ฉ๋
style์ ์ํด ์์น๋ ํฌ๊ธฐ๊ฐ ๋ณ๊ฒฝ๋์ด ๊ณ์ฐ์ด ํ์ํ ๊ฒฝ์ฐ ๋ฐ์ : CPU๋ฅผ ์ด์ฉํ์ฌ ์ํ) - Repaint (
element๊ฐ ํ๋ฉด์ ๋ค์ ๊ทธ๋ ค์ ธ์ผ ํ ๊ฒฝ์ฐ ๋ฐ์)
ํ์ง๋ง GPU๋ฅผ ์ฌ์ฉํ๋ option์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ ํ๋ฆ์ผ๋ก ๋์ํ๋ค.
- Recalculate
- Composite layer (Render layer๋ฅผ ๊ณ์ฐํ๊ณ ํฉ์ฑ : GPU๋ฅผ ์ด์ฉํ์ฌ ์ํ)
GPU๋ฅผ ํตํด animation์ ์ํํ๋ค๋ ๊ฒ์ ๊ธฐํ๊ตฌ์กฐ๋ฅผ ๊ณ์ฐํ๊ณ ๊ทธ๋ ค์ฃผ๋ reflow/repaint ๋์์ CPU์์ ๊ฑฐ์น ํ์๊ฐ ์๋ค๋ ๋ป์ด๋ค. ๋ํ์ ์ผ๋ก GPU๋ฅผ ์ฌ์ฉํ๋ css option์ transform๊ณผ opacity๊ฐ ์๋ค. ํจ์จ์ ์ธ animation์ ๊ตฌํํ๊ธฐ ์ํด ์ด top/left, display๋ฅผ ์ฌ์ฉํ๊ธฐ๋ณด๋ค ์ด ๋๊ฐ์ง๋ฅผ ์ ๊ทน์ ์ผ๋ก ํ์ฉํ๋๋ก ํ์.
- https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318
- https://developer.mozilla.org/en-US/docs/Web/Performance/CSS_JavaScript_animation_performance#running_the_performance_test
- https://www.smashingmagazine.com/2016/12/gpu-animation-doing-it-right/