์์ฝ
css ๋ด๋ถ์ font asset์ ๋ฐ์์ค๋ path๋ฅผ ์ ๋๊ฒฝ๋ก๊ฐ ์๋ ์๋๊ฒฝ๋ก๋ก ๋ฐ๊พธ๋ฉด ๋๋ค.
๋ฌธ์
css์ url๋ก ์ ์ํด ๋ font asset์ด ๊ฐ๋ฐ๋ชจ๋(dev)์์๋ ์ ์ ์ฉ๋๋ค๊ฐ ์ค์๋ฒ(production)์์ ๋ฐ์๋์ง ์๋ ์ด์
์ค์ ๊ตฌํ
/* style.css */
@font-face {
font-family: spoqaSansNeo;
font-weight: 700;
src:url('/font/SpoqaHanSansNeo-Bold.ttf');
src:
url('/font/SpoqaHanSansNeo-Bold.otf') format("opentype"),
url('/font/SpoqaHanSansNeo-Bold.ttf') format("truetype");
}
/* next.config.js */
module.exports = withTM({
assetPrefix: isProd ? 'https://assets.com' : '',
/* ์๋ต */
});
/* ํ์ผ ๊ฒฝ๋ก */
public
ใด font
ใด SpoqaHanSansNeo-Bold.otf
ใด SpoqaHanSansNeo-Bold.ttf
src
ใด styles
ใด style.css
์์ ์ฝ๋๊ฐ ์ด์๊ฐ ๋ฐ์ํ ์ฝ๋์ ์๋ต ๋ฒ์ ์ด๋ค.
style.css์์ url๋ก ๋ฐ์์ค๋ ๊ฒฝ๋ก๋ ์ ๋๊ฒฝ๋ก๋ก ๋์ด์๋๋ฐ, ์ด๋ ๊ฒ url์ ์ค์ ํ ๊ฒฝ์ฐ dev ๋ชจ๋์์๋ ์ ๋์ํ์ง๋ง buildํด์ ์ฝ๋๋ฅผ ์ฌ๋ฆฌ๊ณ ๋๋ฉด production ๋ชจ๋์์ ์ ๋๋ก ๋์ํ์ง ์๋๋ค.
๋ฌธ์ ๋ถ์
- css ๋ด๋ถ์์ url์ ์ด์ฉํด์ asset์ ๋ฐ์์ฌ ๋, path๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ํ๋๊ฐ, ์๋ ๊ฒฝ๋ก๋ก ํ๋๊ฐ์ ๋ฐ๋ผ ๋์์ด ๋ค๋ฅด๋ค.
- ๋ง์ฝ path๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ๋ฐ์์จ๋ค๋ฉด ํด๋น ๊ฒฝ๋ก์ asset์ ๋ฐ๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์์ด css ๋ด๋ถ์๋ path๊ฐ ๊ทธ๋๋ก ์ฌ๋ผ๊ฐ๊ฒ ๋๋ค. ์์ ์์ ์์์ ๊ฐ์ด
/font/SpoqaHanSansNeo-Bold.otf
path ๊ทธ๋๋ก ์ฌ๋ผ๊ฐ๊ฒ ๋๋ ๊ฒ์ด๋ค. - ๋ง์ฝ path๋ฅผ ์๋ ๊ฒฝ๋ก๋ก ๋ฐ์์จ๋ค๋ฉด next์ webpack build ์์ ํด๋น ๊ฒฝ๋ก์ asset์ ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ๋ก ๋ด๋ณด๋ด๊ณ , css ์ฝ๋์์์ url์ ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ์ path๋ก ์์ ํ๋ค. ์์ ์์ ์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด css ํ์ผ์ด ์ฌ๊ตฌ์ฑ๋ ๊ฒ์ด๋ค.
-
/* style.css */ @font-face { font-family: spoqaSansNeo; font-weight: 700; src: url('../../public/font/SpoqaHanSansNeo-Bold.otf') format("opentype"), url('../../public/font/SpoqaHanSansNeo-Bold.ttf') format("truetype"); }
- ๋น๋ ํ์๋ webpack์ ์ํด font asset๋ค์ด .next/static/media ๊ฒฝ๋ก์ ์์ฑ๋๋ฉฐ, ๋ฒ๋ค๋ css ํ์ผ(.next/static/css์ cssํ์ผ)์ ์ดํด๋ณด๋ฉด ๊ทธ์ ๋ง์ถฐ path๊ฐ
url(/_next/static/media/SpoqaHanSansNeo-Bold.ํด์๊ฐ.ttf)
๋ก ๋ฐ๋์ด์์์ ์ ์ ์๋ค.
- ๋ง์ฝ path๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ๋ฐ์์จ๋ค๋ฉด ํด๋น ๊ฒฝ๋ก์ asset์ ๋ฐ๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์์ด css ๋ด๋ถ์๋ path๊ฐ ๊ทธ๋๋ก ์ฌ๋ผ๊ฐ๊ฒ ๋๋ค. ์์ ์์ ์์์ ๊ฐ์ด
- prod์์๋ assetPrefix๊ฐ ๋ถ๋๋ค.
- assetPrefix๋ asset๋ค์ path์ ์ ๋์ฌ๋ก ์ถ๊ฐ๋๋๋ฐ, prod์ผ ๋๋ง ์คํํ ์ ์๋๋ก next.config.js์ ์ ์ํด๋์์ผ๋ฏ๋ก prod(์ค์๋ฒ)์์๋
url(https://assets.com/_next/static/media/SpoqaHanSansNeo-Bold.ํด์๊ฐ.ttf)
๋ก css ํ์ผ์ด ๊ตฌ์ฑ๋๋ค. - ํด๋น ํ๋ก์ ํธ์์๋ github action ํ์ผ์ ๊ตฌ์ฑํ์ฌ ๋ก์ปฌ์์ .next/static ๊ฒฝ๋ก์ ์๋ asset๋ค์ ๋ชจ๋
https://assets.com
์ด๋ผ๋ CDN์ _next/static ๊ฒฝ๋ก๋ก ์ฌ๋ฆด ์ ์๊ฒ ํ๊ณ ์๋ค. - ๋ฐ๋ผ์ prod์์ ๋น๋ํ๊ธฐ ์ .next/static ์ font asset๋ค์ด ์์ด์ผ ์์ ๊ฒฝ๋ก๋ก ์ ๊ทผํ ์ ์๋ ๊ฒ์ด๋ค.
- assetPrefix๋ asset๋ค์ path์ ์ ๋์ฌ๋ก ์ถ๊ฐ๋๋๋ฐ, prod์ผ ๋๋ง ์คํํ ์ ์๋๋ก next.config.js์ ์ ์ํด๋์์ผ๋ฏ๋ก prod(์ค์๋ฒ)์์๋
ํด๊ฒฐ
๊ฒฐ๊ตญ dev์ prod ๋ชจ๋์์ font๊ฐ ์ ์์ ์ผ๋ก ํ์๋๊ฒ ํ๊ธฐ ์ํด์๋ ์ฐ์ .next/static ํด๋์ font asset๋ค์ด build๋ ์ ์๋๋ก ํ๊ณ , ๋์์ css ๋ด๋ถ์ url์๋ ํด๋น asset์ ๊ฒฝ๋ก๊ฐ ์ ํํ ๋ค์ด๊ฐ ์ ์๋๋ก ํด์ผ ํ๋ค.
์ด๋ฅผ ์ํด์๋ url ๋ด๋ถ์ path๋ฅผ ์๋๊ฒฝ๋ก๋ก ๋ฐ๊พธ์ด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
๊ทธ๋ผ font asset๋ค์ build์ webpack์ ์ํด .next/static/media ๊ฒฝ๋ก์ ์์ฑ๋๊ณ , css ๋ด๋ถ์ path๋ ๊ทธ์ ๋ง์ถฐ _next/static/media๋ก ์ธํ ๋ ๊ฒ์ด๋ค.
๋ํด์ prod ํ๊ฒฝ์ด๋ผ๋ฉด github action ํ์ผ์ ์ํด CDN์ _next/static/media๋ก ์ฎ๊ฒจ์ง ๊ฒ์ด๋ฉฐ, css ๋ด๋ถ์ path๋ assetPrefix๊น์ง ๋ถ์ ๊ฒฝ๋ก๋ก ๋ณ๊ฒฝ๋๋ ๊ณผ์ ์ ๊ฑฐ์น ๊ฒ์ด๋ค.
Bonus
์์ธ์ ๋ถ์ํ๋ฉด์ next ๋ด๋ถ์ webpack ์ฝ๋๋ฅผ ์ข ํ์ด๋ณด์๋ค.
css ํ์ผ์ webpack/config/blocks/css/index.ts์์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ์ดํด๋ณด์.
if (ctx.isClient) {
// Automatically transform references to files (i.e. url()) into URLs
// e.g. url(./logo.svg)
fns.push(
loader({
oneOf: [
markRemovable({
// This should only be applied to CSS files
issuer: regexLikeCss,
// Exclude extensions that webpack handles by default
exclude: [
/\.(js|mjs|jsx|ts|tsx)$/,
/\.html$/,
/\.json$/,
/\.webpack\[[^\]]+\]$/,
],
// `asset/resource` always emits a URL reference, where `asset`
// might inline the asset as a data URI
type: 'asset/resource',
}),
],
})
)
}
css ํ์ผ๋ง์ ๋ชจ์์ ์ฒ๋ฆฌํ๋ฉฐ, type์ ๋ณด๋ฉด asset/resource๋ก ๋์ด์๋ค. ์ด๋ file-loader์์์ ์ฒ๋ฆฌ์ ๋์ผํ๊ณ (์ฐธ๊ณ : https://webpack.kr/guides/asset-modules/) ํ์ผ์ ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ๋ก ๋ด๋ณด๋๊ณผ ๋์์ ํด๋น ๊ฒฝ๋ก๋ฅผ css ๋ฒ๋ค์ ์ฝ์ ํด์ฃผ๋ ์ต์ ์ด๋ค.
→ ๋ฐ๋ผ์ ์ด ๋ถ๋ถ์์ font asset์ด ์ฒ๋ฆฌ๋์ด ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ๋ก ๋ณด๋ด์ง๊ณ ํด๋น ๊ฒฝ๋ก๊ฐ css ๋ฒ๋ค์ ์ฝ์ ๋ ๊ฒ์ด๋ค.
Comment