2019-05-05
base64 ๋ก ์ด๋ฏธ์ง๋ฅผ ์ธ์ฝ๋ฉํ์ฌ html ์ ์ธ๋ผ์ธ์ผ๋ก ๋ฃ๋ ๋ฐฉ๋ฒ์ผ๋ก, ํฌ๊ธฐ๊ฐ ์์ ์ด๋ฏธ์ง๋ฅผ ๋ ๋ํ ๋ ์ ํฉํ๋ค. ์ด๋ฏธ์ง ํ์ผ์ ๊ฐ์ ธ์ค๋ http ์์ฒญ์ ๋ฐ๋ก ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ก๋ฉ ์๋๋ฅผ ์ค์ผ ์ ์๋ค.
์ธ์ฝ๋ฉ๋ ๊ฐ์ data URI scheme (data:[<media type>][;base64],<data>
) ํ์์ผ๋ก src
property ์ ๋ฃ์ผ๋ฉด ๋๋ค.
<img src="data:image/png;base64,iVBORw0KGgoAAAANSU..."/>
์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ํด ๊ฒฝ์ฐ์๋ ์ธ์ฝ๋ฉํ์ฌ html ์ ๋ค์ด๊ฐ๋ ์ฝ๋๋ ์ปค์ง๊ณ , request ๋ฅผ ํ์ง ์๊ธฐ ๋๋ฌธ์ caching ๋ ๋์ง ์๋๋ค๋ ๋จ์ ์ด ์๋ค.
Webpack ์ ์ฌ์ฉํ๋ค๋ฉด url-loader
ํ๋ฌ๊ทธ์ธ์ ํตํด ์ด๋ฏธ์ง ๋ชจ๋์ ์๋์ผ๋ก ์ธ์ฝ๋ฉ/์ธ๋ผ์ธ ์ฝ์
ํ ์ ์๋ค. limit ์ต์
๋ณด๋ค ์์ ์ด๋ฏธ์ง ๋ชจ๋์ ์ธ๋ผ์ธ์ผ๋ก ์ฝ์
ํ๊ณ , ํฐ ๋ชจ๋์ fallback ์ ์ง์ ํ ์ ์๋ค. fallback ์ ์ง์ ํ์ง ์์ผ๋ฉด ์ด๋ฏธ์ง๋ฅผ ํ์ผ๋ก ์ทจ๊ธํ๋ file-loader
๊ฐ ๊ธฐ๋ณธ๊ฐ์ด ๋๋ค.
{
test: /\.(png|jpe?g)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
fallback: 'file-loader' // default
}
}
]
}
์ด๋ฏธ์ง๊ฐ ๋ก๋๋๋๋์ ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ๋ ํ์์ ํผํ๋ ค๋ฉด preload
๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๋ค๋ฅธ ์์๋ค๋ณด๋ค ์ฐ์ ์์๋ฅผ ๋์ฌ์ ์ฐจ๋จ ์์ด(non-render-blocking) ์ด๋ฏธ์ง๋ฅผ ๋จผ์ ๋ก๋ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
<link>
์ฌ์ฉํ๊ธฐlink
๋ ํ์ฌ ๋ฌธ์์ ์ธ๋ถ ๋ฆฌ์์ค ๊ฐ์ ๊ด๊ณ๋ฅผ ๋ช
์ํ๋ค. as
์์ฑ๊ฐ์ผ๋ก๋ ์ด๋ฏธ์ง ๋ฟ๋ง ์๋๋ผ video, css, font ๋ฑ๋ ์ฌ์ฉํ ์ ์๋ค.
<head>
<link rel="preload" as="image" href="logo.jpg"/>
</head>
...
<body>
<img src="logo.jpg"/>
</body>
Image()
constructor ์ฌ์ฉํ๊ธฐnew Image()
constructor ๋ HTMLImageElement
๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. ์ด๋ฅผ window
๊ฐ ๋ก๋๋ ๋ ์คํํ ์ ์๋ค.
window.onload = function() {
const img = new Image();
img.src = "assets/image.png";
};
IntersectionObserver
API์ด๋ฏธ์ง๊ฐ viewport ์ ๋ค์ด์ฌ ๋๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ๋ก๋ํ๋ ๊ฒ์ผ๋ก, medium ์์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋จผ์ ํด์๋๊ฐ ๋ฎ์์ ๋น ๋ฅด๊ฒ ๋ก๋ฉํ ์ ์๋ placeholder ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ , viewport ์์ ์ด๋ฏธ์ง๊ฐ ๋ค์ด์ฌ ๋ ์ค์ ์ด๋ฏธ์ง๋ฅผ ๋ก๋ฉํ๋ค. ๊ธฐ์กด์ ์ด๋ฅผ ๊ตฌํํ๋ ค๋ฉด scroll ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋
ํด์ getBoundingClientRect()
ํจ์๋ก ์ง์ element ์ ํฌ๊ธฐ๋ฅผ ๋น๊ตํ๋ ๋ฑ ๊ท์ฐฎ์ ์์
์ด ๋ง์๋ค.
ํ์ง๋ง IntersectionObserver
API ๋ฅผ ์ฌ์ฉํ๋ฉด ์ํ๋ element ๊ฐ ํ์ฌ ๋ช %๋ ๋ณด์ด๋์ง ์์์ ๊ฐ์งํ๊ณ ์ํ๋ ์ฝ๋ฐฑํจ์(์ด๋ฏธ์ง ๋ก๋ฉ)๋ฅผ ์คํํ ์ ์๋ค. ์ด๋ฏธ์ง lazy loading ๋ฟ๋ง ์๋๋ผ ๋ฌดํ ์คํฌ๋กค, ๊ด๊ณ ๋ฐฐ๋ ๋ทฐ ์ธก์ ๋ฑ์์๋ ํ์ฉ๋๋ค.
<img class="lazy" src="placeholder.png" data-src="image.png" data-srcset="image@2x.png 2x, image@3x.png 3x" />
๋จผ์ , lazy loading ํ ์ด๋ฏธ์ง์ class ๋ฑ์ ์ง์ ํ๊ณ src, srcset ์ data attribute ๋ก ์ ๋ฌํ๋ค. ๊ทธ๋ฆฌ๊ณ ์๋์ ๊ฐ์ด
IntersectionObserver
๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
document.addEventListener("DOMContentLoaded", function() {
const lazyImages = Array.from(document.querySelectorAll("img.lazy"));
let lazyImageObserver = new IntersectionObserver(
(entries, observer) => {
/** entries ๋ ๋ณํ๊ฐ ๊ฐ์ง๋๋ ๋ค์ํ ์์ฑ๋ค์ด๋ค. (e.g. isIntersecting, boundingClientRect, intersectionRect) */
entries.forEach(entry => {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
/** load๋ฅผ ๋ง์น๋ฉด observe๋ฅผ ๋๋ธ๋ค. */
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
},
{ threshold: 0.8 } /** 80%๊ฐ ๋ณด์ด๋ฉด callback์ ์คํํ๋ค. */
);
lazyImages.forEach(lazyImage => {
/** lazy ๋ก๋ฉ์ด ํ์ํ ๋ชจ๋ ์ด๋ฏธ์ง์ ๋ํ์ฌ observe ํจ์๋ฅผ ์คํํ๋ค. */
lazyImageObserver.observe(lazyImage);
});
});
Chrome 51 ๋ฒ์ , Safari 12.1 ๋ฒ์ ์ด์๋ถํฐ ์ง์ํ๋ฉฐ, ์ง์๋์ง ์๋ ๋ธ๋ผ์ฐ์ ์์๋ polyfill์ ์ฌ์ฉํ ์ ์๋ค.
gzip
& CDN์ด๋ฐ์๋ ์์์ gzip
์ผ๋ก ์์ถํ๊ฑฐ๋ CDN ์ ์ฌ์ฉํ์ฌ ์ต์ ํํ ์ ์๋ค. CDN(Content Delivery Network)๋ ๋ง์น ์ฟ *์ ๋ก์ผ์ง๊ตฌ์ฒ๋ผ ์ ์ธ๊ณ ๊ณณ๊ณณ์ ์ฃ์ง ๋ก์ผ์ด์
์์ ์ฌ์ฉ์์๊ฒ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ์ ๋ฌํ ์ ์๋ ๋ฃจํธ๋ก ์์์ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ฐธ๊ณ