ํ์ผ ์ด๋ฆ์ webpack [hash:8]
๋๋ [contenthash:8]
๊ฐ ํฌํจ ๋ razzle์์ ๋น๋ ํ ํ์ผ์ ์ ๊ณต ํ ๋๋ง๋ค ๊ฐ์งํ๊ธฐ ์ํด razzle ์๋ฒ์ ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์กฐ์ฌํ๊ณ ์์ต๋๋ค. ๋๋ ์ฒ์ ์ฌ๊ธฐ์์ ๋ด๊ฐ ๊ฒช๊ณ ์๋ ๋ช ๊ฐ์ง ๋ฌธ์ ์ ๋ํด ๋
ผ์ํ๋ค https://github.com/jaredpalmer/razzle/pull/1368#issuecomment -664015050
๋๋ razzle์ด "๋ถ๋ณ"(์๋ต์์ Cache-Control
ํค๋๋ฅผ ์ค์ ํ๊ธฐ์ํ ๋ชฉ์ ์ผ๋ก)์ผ๋ก ๊ฐ์ฃผ ๋ ์์๋ ์์ ํ ํ์ผ / ์์ฐ ๋ชฉ๋ก์ ์์ฑํ๊ณ ๋
ธ์ถํ๊ธฐ๋ฅผ ์ํฉ๋๋ค. chunks.json ๋ฐ / ๋๋ assets.json ํ์ผ
์ฐธ๊ณ : ์๋ช ์ด ๊ธธ๊ณ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์บ์ ์ ์ด ์๋ต์ ์ค์ ํ ๋ ํ์ผ์ด ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ ๋ ์ ์๋์ง ์ฌ๋ถ (ํ์ผ ์ด๋ฆ์ ํด์๋ฅผ ๊ฐ์งํ๋ ์ ๊ท์)์ ๋ํด ์ด๋ค ์ข ๋ฅ์ "๊ทผ์ฌ์น"๋ ์ํํ์ง ์์ผ๋ ค ๊ณ ํฉ๋๋ค. ํ์ผ์ด ์ค๋ซ๋์ ๋ถ๋ณ์ผ๋ก ์บ์๋๊ณ ์๋ฒ ์ธก ์บ์ ๋ฌดํจํ๋ก ๊ณ ์น ์ ์์ต๋๋ค. ์ด๋ ํด๊ฒฐํ๊ธฐ ๋งค์ฐ ๊ณ ํต์ค๋ฌ์ด ๋ฌธ์ ์ผ ์ ์์ต๋๋ค.
ํ์ฌ ๋ ธ์ถ ๋ json ํ์ผ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ด๋ ค์ด ์ด์ ์ ๋ํ ์์ฝ :
chunks.json
๋ฐ assets.json
๋ชจ๋ ์ฌ์ฉํด์ผํฉ๋๋ค. chunks.json์ ์์ค ๋งต ํ์ผ์ ํฌํจํ๊ณ assets.json์๋ chunks.json์ด์๋ png / fonts ๋ฑ๊ณผ ๊ฐ์ ํ์ผ์ด ์์ต๋๋ค.(assets.json).client
(์ : "client": { "js": "/static/js/bundle.6fc534aa.js" }
)์์๋ ์ฒญํฌ์ ๊ฒฝ์ฐ assets.json์ ๋น ๋ฌธ์์ด (์ : "": { "js": "/static/js/0.cb47cee9.chunk.js" }
) ์๋์ ๋ค๋ฅธ ๋ชจ๋ ์์ฐ์ ๊ทธ๋ฃนํํฉ๋๋ค."client": { "css": ["filename.css"] }
), assets.json์ ํ๋์ ํ์ผ ํ์ผ ๋ง์๋ ๊ฒฝ์ฐ ๋์ ๋จ์ผ ๋ฌธ์์ด (์ : "client": { "css": "filename.css" }
)."json": "/../chunks.json"
ํฌํจ๋์ด ์์ง๋ง ์ฌ๊ธฐ์ ์์ด์ผํ๋ค๊ณ ์๊ฐํ๋ ํญ๋ชฉ์ด ์๋์ง๋ง (๋ฒ๊ทธ์ธ์ง ์ฌ๋ถ๋ ํ์คํ์ง ์์) ๋ชฉ๋ก์ ๋ง๋ค ๋ ์๋์ผ๋ก ์ ๊ฑฐํด์ผํฉ๋๋ค. ์๋ช
์ด ๊ธด cache-Control ์๋ต ํค๋๋ฅผ ์ ๊ณต ํ ์์๋ ํ์ผ.chunks: ["1", "2", "3"]
๋ฐฐ์ด์ ์ถ๊ฐํ๋ ค๋ ๊ณํ์ ๋ค์๊ณผ ๊ฐ์ ํ์ผ ๋ฐฐ์ด์ด ํฌํจ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ (chunks.json).client.chunks
๋ฅผ ํํฐ๋งํ๊ธฐ ์ํด ์ถ๊ฐ ์์
์ํด์ผํ๊ธฐ ๋๋ฌธ์ ๋ค์ ์ง์ฆ์ด๋ฉ๋๋ค. (chunks.json).client.css
๋ฐ (chunks.json).client.js
๋ฑclient
์ฒญํฌ์์๋ ํ์ผ์ chunks.json
ํ์ผ์๋ ๋ํ๋์ง ์์์ต๋๋ค. ์ฒญํฌ ๋ฒํธ๋ฅผ ํค๋ก ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝํ๊ฑฐ๋ ์ ์ํ์ต๋๋ค. ์ต์ํ ํ์ผ์ ํ์๋๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด๊ฒ์ ๋จ์ ์ ์ด์ ๊ธฐ๋ณธ ๋ช
๋ช
๋ ์ฒญํฌ ( "client": {/* blah */ }
)๊ฐ ์๋ ์ฒญํฌ๋ฅผ ์ฒ๋ฆฌ ํ ๋ ์คํค๋ง์์ chunks.json
๋ฐ assets.json
๋ ๋ค์ํ๋ค๋ ๊ฒ์
๋๋ค.ํ์ฌ assets.json ๋ฐ chunks.json์ ์ฌ์ฉํ๋ ๊ฒ์ ์ง๊ธ๊น์ง ๋๋ต์ ์ผ๋ก ์ํํด์ผํ๋ ์์ ์ ๋๋ค.
๋๋ํ์ง ์์๋ค :
"chunks": ["1", "2", "3"]
๋ฐ "json": "/../chunks.json"
์ ๊ฐ์ด json์์ ํ์ผ / ํ๋๋ฅผ ํํฐ๋ง ํ ์ ์์ต๋๋ค.function razzleCacheableFiles() {
// TODO: Add loading the assets.json file to support (png/txt files etc)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const chunks = require(process.env.RAZZLE_CHUNKS_MANIFEST!);
const filesByType = Object.entries(chunks).reduce(
(chunkAcc: any, [, chunk]) => {
const types = Object.entries(chunk as any).reduce(
(typeAcc, [fileType, files]) => {
return {
[fileType]: chunkAcc[fileType]
? [...chunkAcc[fileType], ...(files as string[])]
: files,
};
},
{},
);
return types;
},
{},
);
const files = Object.entries(filesByType).reduce(
(acc: any[], [, files]) => [...acc, ...(files as string[])],
[],
);
return files;
}
const cacheableFiles = razzleCacheableFiles();
// Serve static files located under `process.env.RAZZLE_PUBLIC_DIR`
const assetCaching = {
immutable: {
maxAge: CacheFor.OneMonth,
sMaxAge: CacheFor.OneYear,
},
default: {
maxAge: CacheFor.OneDay,
sMaxAge: CacheFor.OneWeek,
}
};
app.use(
serve(process.env.RAZZLE_PUBLIC_DIR!, {
setHeaders(res, path) {
const filename = path.replace(process.env.RAZZLE_PUBLIC_DIR!, "");
const hasHashInFilename = cacheableFiles.includes(filename);
if (hasHashInFilename) {
const { immutable } = assetCaching;
res.setHeader(
"Cache-Control",
`max-age=${immutable.maxAge},s-maxage=${immutable.sMaxAge},immutable`,
);
return;
}
res.setHeader(
"Cache-Control",
`max-age=${assetCaching.default.maxAge},s-maxage=${asetCaching.default.sMaxAge}`,
);
},
}),
);
ํ์ฌ ํ๋ / ์ฌ์ฉ์ ๋ํ ๋ช ํํ๊ณ ๊ฐ๊ฒฐํ ์ค๋ช .
์ด ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ์์ ์ ์์ง๋ง ๊ฐ์ฅ ์ค์ํ ๊ฒ์ razzle ๋น๋์์ ์์ฑ ๋ ๋ชจ๋ ์บ์ ๊ฐ๋ฅ / ๋ถ๋ณ ์์ฐ์ ๋ฐฐ์ด์๋ก๋ํ๋ ๋ฐฉ๋ฒ์ ๋๋ค. ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ต๋๋ค.
// File: caching.json
// contains all files/assets with a hash in them regardless of what type of file they are.
{
"immutable": [
"/static/js/0.cb47cee9.chunk.js",
"/static/js/0.cb47cee9.chunk.js.map",
"/static/js/0.cb47cee9.chunk.js.LICENSE.txt",
"/static/media/ferris-error.407b714e.png"
],
// I'm not even sure if this is required because I don't think razzle generates any files that don't have hashes in them?
// possibly files copied in from the `public/` directory during build. but I'm not even sure if it'd that'd useful
"standard": []
}
// RAZZLE_CACHING_MANIFEST is probably a silly name but
const cacheableFiles = require(process.env.RAZZLE_CACHING_MANIFEST!);
// Serve static files located under `process.env.RAZZLE_PUBLIC_DIR`
const assetCaching = {
immutable: {
maxAge: CacheFor.OneMonth,
sMaxAge: CacheFor.OneYear,
},
default: {
maxAge: CacheFor.OneDay,
sMaxAge: CacheFor.OneWeek,
}
};
app.use(
serve(process.env.RAZZLE_PUBLIC_DIR!, {
setHeaders(res, path) {
const filename = path.replace(process.env.RAZZLE_PUBLIC_DIR!, "");
const hasHashInFilename = cacheableFiles.immutable.includes(filename);
if (hasHashInFilename) {
const { immutable } = assetCaching;
res.setHeader(
"Cache-Control",
`max-age=${immutable.maxAge},s-maxage=${immutable.sMaxAge},immutable`,
);
return;
}
res.setHeader(
"Cache-Control",
`max-age=${assetCaching.default.maxAge},s-maxage=${asetCaching.default.sMaxAge}`,
);
},
}),
);
์ข์ ์๋ฃจ์
์ด ๋ฌด์์ธ์ง ์์ ํ ์กฐ์ฌํ์ง๋ ์์์ง๋ง ๋ฐํ์์ assets.json
๋ฐ chunks.json
์ฌ์ฉํ์ฌ์ด "์บ์ ๊ฐ๋ฅํ ์์ฐ"๋ชฉ๋ก์ ์์ฑํ๋ ค๊ณ ์๋ํ ํ์๋ ์ด ์์
์ ์ํํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์ด๋ค ์ข
๋ฅ์ ์นํฉ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ์ฌ ๋น๋์์ด ๋ ํ์ผ์ ๋ถ์ผ์น๋ฅผ ์ฐํํ๋ ๊ฒ์
๋๋ค.
๋ด ๋ชฉ์ ์ ์ํด ์ฒ์์๋ ๋ด๊ฐ ํด์ ๋ ๊ฒ์ฒ๋ผ ๋ฐํ์๋ณด๋ค๋ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ์ฌ์ด ์์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์กฐ์ฌํ๊ธฐ ์์ํ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํ ๊ฐ์น๊ฐ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ํด์ ๋ ํ์ผ์ ๋ํด ์๋ช ์ด ๊ธด ์บ์ ์ ์ด๋ฅผ ์ค์ ํ ์ ์๋ค๋ ๊ฒ์ ์ฃผ๋ก ํด์ ๋ ํ์ผ์ด ์์๋๋ ์ด์ ์ด๋ฏ๋ก ๋ชจ๋ ํ์ผ ๋ชฉ๋ก์ ๋ ธ์ถํ๋ ๊ฒ์ด ์ ์ ํด ๋ณด์ ๋๋ค.
razzle์ ์ํด ์์ฑ ๋ฐ ํด์ ๋ ํ์ผ์ ๋ํด ์ ์ ํ ์๋ช ๋ฐ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์บ์ ์ ์ด ์๋ต ํค๋๋ฅผ ์ค์ ํ๋ ค๋ ๋ชจ๋ ์ฌ์ฉ์.
chunks.json
๋ฐ assets.json
ํจ๊ป ๋ฌถ์ด ๋ฐํ์์ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅ / ์บ์ ๊ฐ๋ฅํ ๋ชจ๋ ํ์ผ ๋ชฉ๋ก์ ์์ฑํฉ๋๋ค (์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ๊ณ ์ทจ์ฝํ ๊ฒ์ผ๋ก ๋ณด์).require(process.env.RAZZLE_CACHING_MANIFEST!)
. ()์ด ๋ณ๊ฒฝ์ ์ํด ๊ธฐ๊บผ์ด ๋๊ณ / ๊ธฐ์ฌํ ์ํฅ์ด ์์ง๋ง ์ฌ๋ฐ๋ฅธ ๋ฐฉํฅ์ผ๋ก ์ฝ๊ฐ์ ์์ ์ด ํ์ํ ์ ์์ต๋๋ค (๋ฌผ๋ก ์ด๊ฒ์ด ์๋ฝ / ํ์๋๋ ๋ณ๊ฒฝ์ธ์ง ์ฌ๋ถ).
๋ํ ์๊ฐ์, ์ด๋ฐ ์์ผ๋ก ๋ญ๊ฐ์๋ ๊ฒ์ ์ฝ๊ฒ / ์ผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณด์ฅ ์ฃผ์ ์์ ์ฑ ๋ช ๊ฐ์ง ๊ฒ์ฌ๋ฅผ ํ ์ ์๋๋ก ํ ์ [contenthash:8]
๋์ [hash:8]
(๋น๋ ํด์) IF / ๊ทธ๋ค์ด ํ ์ / : HTTPS /github.com/jaredpalmer/razzle/issues/1331
์ด๊ฒ์ ๊ฐ์น์๋ ์์ด๋์ด์ฒ๋ผ ๋ณด์ ๋๋ค.
๋ํ ์ต์ ํ์์ chunkGroup ๊ตฌ์ฑ์ด๋ผ๋ ๋ ๋ค๋ฅธ ๋ฌธ์ ์ ์ฐ๊ฒฐ๋์ด ์์ต๋๋ค. ๊ทธ๊ฒ์ด ์ค์ ๋์๋ค๋ฉด ๋น ๋ฌธ์์ด์ "shared", "framework"๋ฑ์ด ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค.
next.js๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ chunkGroups ๊ตฌ์ฑ์ ์ฌ์ฉํฉ๋๋ค.
์ด๋ฅผ ๋ณ๊ฒฝํ๋ฉด ์ด์ ๋ฒ์ ๊ณผ๋ ํธํ๋์ง ์์ง๋ง ์ํ๋์ด์ผํฉ๋๋ค. ์ฃผ์ ๋ฆด๋ฆฌ์ค๊ฐ ํ์ํ ๋ช ๊ฐ์ง ํฐ ๋ณ๊ฒฝ ์ฌํญ์ด ์์ต๋๋ค.
ํ์ง๋ง์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ช ๊ฐ์ง ์ฝ๋๋ฅผ ์์ ๋กญ๊ฒ ์๊ฐํด๋ณด์ธ์ ๐
next.js๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ chunkGroups ๊ตฌ์ฑ์ ์ฌ์ฉํฉ๋๋ค.
์ค ๋ฉ์ง๋ค, ๋๋ ๋ค๋ฅธ ๋๊ตฌ / ํ๋ ์ ์ํฌ๊ฐ ์ด๊ฒ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ / ์ด๋ป๊ฒ ์ ๊ทผํ์ง ๋ชปํฉ๋๋ค. ๋งํฌ / ์์ ๊ฐ ์์ต๋๊น?
๋ํ ์ต์ ํ์์ chunkGroup ๊ตฌ์ฑ์ด๋ผ๋ ๋ค๋ฅธ ๋ฌธ์ ์ ๊ด๋ จ์ด ์์ต๋๋ค.
๊ณต๊ฐ ๋ ๋ฌธ์ ? ๋ ๋ง์ ์ปจํ ์คํธ๋ฅผ ์ป์ ์ ์๋๋ก ์ด๋ ๊ฒ์ ์๋ ค ์ฃผ์๊ฒ ์ต๋๊น?
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ ์ฌ์ ์ธ ํ ๊ฐ์ง ๋ฐฉ๋ฒ์ ๊ธฐ์กด chunks.json
๋ฐ assets.json
์ ๋ชจ์ / ์คํค๋ง๋ฅผ๋ณด๋ค ๊ฐ๋ ฅํ๊ฒ ์ ์ํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค. ์ ์คํ๊ฒ ๊ณ ๋ คํ ํ์๊ฐ์์ ์ ์์ง๋ง (๋ฉ์ด์ ๋ฒ์ ๋ฒํ๊ฐ ์์) ๋ค๋ฅธ ํ๋ ์ ์ํฌ ๋ฑ์ด ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง์ ๋ํ ์๊ฐ ์๋ค๋ฉด ๋น์ทํ ๋ฐฉํฅ์ ๋ฐ๋ฅด๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์ผ ์ ์์ต๋๋ค.
๊ณผ
https://github.com/vercel/next.js/blob/canary/packages/next/build/webpack-config.ts#L378
๊ทธ๋ค์ด ๋งค๋ํ์คํธ๋ฅผ ์ด๋ป๊ฒ ์ํํ๋์ง ํ์คํ์ง ์์ต๋๋ค.
https://github.com/jaredpalmer/razzle/issues/1377์ ์ดํด๋ณด๊ณ ์๋ก์ด ์์ ๋ฅผ ์ถ๊ฐํ์ต๋๋ค. :)
@fivethreeo ํน๋ณํ์ด ๋ฌธ์ ์ ๋ ์ด์ ์๊ฐ์ ํ ์ ํ์ง ๋ชปํ์ต๋๋ค. ๐ , v4 ํ๋ฆฌ ๋ฆด๋ฆฌ์ฆ๋ฅผ ์ํ ํด๋ณด๋ ๋ฐ ์๊ฐ์ ํ ์ ํ ๊ฒ์ ๋๋ค. ์ค๋น๊ฐ๋์๋ค๊ณ ์๊ฐํ๋ค๋ฉด ์์ผ๋ก ๋ฉฐ์น ๋์ ์ฌ์ฉํด ๋ณด๊ธธ ๋ฐ๋๋๋ค.
๋ง์ ๊ด์ฌ์ด ์๋์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง ์ง๊ธ ์ฌ๊ธฐ ์์ ์์ ์ค์ธ ๊ฒ์ ๊ณต๊ฐ
์ ๋ v4๋ฅผ ๋งค์ฐ ์ข์ํฉ๋๋ค. ๊ทธ ์ด์ ๋ ์ฌ๊ธฐ ์์ ์ค์ ํด์ผํ๋ "ํ๋ฌ๊ทธ์ธ"์ฌ์ ์๋ฅผ ๋ง์ด ์ ๊ฑฐ ํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ํนํ ํ์ดํ ์คํฌ๋ฆฝํธ์ ๋ํ ๊ฒ์ ๋๋ค.
์บ์ ๊ฐ๋ฅํ ์์ฐ์ ์ฌ๊ธฐ์ ์์ต๋๋ค .
๋ชจ๋ ํ์ผ์ ์ด์ contenthash๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ด๊ฐ ๋งํ๊ณ ์ถ์ ๋ณต์ฌ ๋ ํ์ผ์ ๋ฒ ๋ค๋ฌ๊ฐ์์ ๋ ๋์ ์ต๊ด์ ๋๋ค.
"๋ฒ ๋ค๋ฌ๊ฐ์์ ๋ ๋ณต์ฌ ๋ ํ์ผ์ ๋์ ์ต๊ด"์ด๋ผ๋ ๋ป์ ์ดํดํ์ง ๋ชปํฉ๋๋ค.
ํ์ฌ ๋์์ razzle ํ๋ก์ ํธ์ ์ต์์ public/
ํด๋์ ํ์ผ์ ๋ฃ๋ ๊ฒ์
๋๋ค.
build/
public/
robots.txt
manifest.json
package.json
razzle build
๋ ์ ์ ์์ฐ์ ๋์ฒํฉ๋๋ค.
build/
public/
robots.txt
manifest.json
static/
...
public/
robots.txt
manifest.json
package.json
๋น๋ ์ค์ ๋ณต์ฌ ๋ ๋ชจ๋ ์์ฐ์ ๋ชฉ๋ก์ ์ ์งํ์ฌ ์บ์ ์ ์ด๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ๋ณ๋๋ก ํ๊ฒํ ํ ์ ์๋๋กํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
๋๋ ๊ทธ๊ฒ์ ๋ํ ๋
ผ์์ด (๋ด๊ฐ ์๊ฐํ ์์๋) razzle์ด ๋น๋ํ๋ ๋์ ๋ณต์ฌ ํ ํ์ผ๊ณผ razzle build
์ธ๋ถ์ ์๋์ผ๋ก ๋ฃ์์ ์์๋ ํ์ผ์ ๊ตฌ๋ณ ํ ํ์๊ฐ ์๋ค๋ ๊ฒ์
๋๋ค
public์ robots.txt์ favicon.ico ๋ง ํฌํจํด์ผํ๋ฉฐ ํด์๋ก ๋ฒ์ ์ด ์ง์ ๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
๋ค๋ฅธ ๋ชจ๋ ๊ฒ์ ์นํฉ์ ์ํด ๋ฒ๋ค๋ก ์ ๊ณต๋์ด์ผํฉ๋๋ค. ๋ ํฐ ํ๋น์ฝ์ ๋ฒ๋ค๋ก ์ ๊ณต๋์ด์ผํฉ๋๋ค.
ํ์ง๋ง ๊ธฐ๋ณธ create-react-app
์ "ํ๋ฌ๊ทธ ์ค ํ๋ ์ด"ํธํ์ฑ์ ์ ์งํ๋ ค๋ ๊ฒฝ์ฐ์๋ ์ฑ ๋งค๋ํ์คํธ์ ์ผ๋ถ ์์ด์ฝ ๋ ์ฌ๊ธฐ์ ํ์๋๋ค๋ ์ ์ ๊ณ ๋ คํ ๊ฐ์น๊ฐ ์์ต๋๋ค.
๋๋ manifest.json
/ manifest.webmanifest
์ ๋น๋ ํด์๋ฅผ ํฌํจํ์ง ์์์ผํ๋ ์ด์ ๊ฐ ์๋ค๋ ๊ฒ์ ๋ง์ฐํ๊ฒ ๊ธฐ์ตํฉ๋๋ค. ์ด๋ ๋ฒ ๋ค๋ฌ์์ ์ฒ๋ฆฌ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ์์ฃผ ๋ฐ์ํ๋ ์ด์ ์ค ํ๋์
๋๋ค. ๋ด๊ฐ ํ๋ ธ๊ฑฐ๋ ์๋ชป ๊ธฐ์ตํ ์ ์์ง๋ง PWA ๋ฐ ์คํ๋ผ์ธ ๋ชจ๋์ ๊ด๋ จ์ด์์ ์ ์์ต๋๋ค.
razzle ์์ ํ๋ก์ ํธ์์ PWA (๋ฐ / ๋๋ ์๋น์ค ์์ปค) ์ง์์ ๊ตฌํํฉ๋๊น?
๋ ๊ด๋ จ์ฑ์ด ์์ง๋ง ๊ณผ๊ฑฐ์ create-react-app์ ์ฌ์ฉํ ๋ public/
ํด๋์ ๋ฃ์ ๋ค๋ฅธ ๊ฒ์ ์น ์ฌ์ดํธ์ ๊ด๋ จ๋ ๋ค์ด๋ก๋ ๊ฐ๋ฅํ ํ์ผ์ด์ง๋ง ์๊ตฌ URL์ด ํ์ํ ๊ณณ์
๋๋ค. ์ด๋ฉ์ผ ๋ฑ์ ๋ณด๋ผ ๋ ๋งํฌ ํ ์์๋ pdf ๋ฌธ์๊ฐ์๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค.
if / why / when ์น ๋งค๋ํ์คํธ๊ฐ ๋ฒ ๋ค๋ฌ์ ๋ถ๋ฆฌ๋์ด์ผํ๋ ๊ฒฝ์ฐ์ ์๋ฅผ ์ฐพ์ผ๋ ค๊ณ ํฉ๋๋ค.
ํด๋น ๊ฒ์๋ฌผ์๋ https://github.com/w3c/manifest/issues/446#issuecomment -351368893์ผ๋ก ์ฐ๊ฒฐ๋๋ ๋๊ธ์ด ์์ต๋๋ค.
์, ๋ค์ด๋ก๋ ๊ฐ๋ฅํ ํ์ผ์ด ๊ฑฐ๊ธฐ์ ์์ด์ผํฉ๋๋ค. ํ ,ํ์ง๋ง ์ด๋ฌํ ํ์ผ์ assets.json์ ์ด๋ป๊ฒ ์ถ๊ฐํฉ๋๊น? ์ด๋ค ์์ด๋์ด? ๐ webpack์ด ๊ทธ๊ฒ๋ค์ ์ฐพ์์์๋ ๊ทธ๋๋ก ๋ฌถ์ด์ผํฉ๋๊น? assets.json ์์ ์ ์๋ง์ธ ๊ฒ ๊ฐ์ต๋๋ค.
๋๋ PWA ์์ ๊ฐ ์๋ค๊ณ ์๊ฐํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ผ๊ด๋ ์ด๋ฆ์ด ํ์ํ ๊ฒฝ์ฐ. ๊ทธ๊ฒ์ webpack์ ์ํด ์ฒ๋ฆฌ๋์ด์ผํฉ๋๋ค.
์ถ๋ ฅ์ ์กฐ์ ํ ์ ์๋๋ก ์์ฐ ํ๋ฌ๊ทธ์ธ์ ๋งค๋ํ์คํธ ํ๋ฌ๊ทธ์ธ์ผ๋ก ๋์ฒดํ๊ฒ ์ต๋๋ค.
๋ชจ๋ ํ์ผ๊ณผ ์๋ก์ด ์์ฐ ๋งค๋ํ์คํธ ์ถ๊ฐ https://github.com/jaredpalmer/razzle/commit/1c6e9169e9d8eee256d0f118f8a88da8de85989f ๊ฐ์ ์ ๋ํ ์ด๋ค ์ ์?
์ง๊ธ ์นด๋๋ฆฌ์๊ฐ ์ถ์ ๋์๋์ :)
๋งค๋ํ์คํธ ํ๋ฌ๊ทธ์ธ์ด ์ค์ ๋ก ์ ์ง๋์ง ์๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. ์ต์ ์ ์ฐ๋ฆฌ ์ค์ค๋กํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๋๋ ํ์ฌ ์๋ฌด๋ ์์ง ๋ชปํ์ง๋ง (์๋ง๋) ์ ๋๋ ๊ทธ๋ ๊ฒ ํ ์์๋ ์นํฉ ์ฌ๋๋ค์ ๋๋ค.
์ง๊ธ ์นด๋๋ฆฌ์ ์ง์ ์ ์ถ๊ฐ๋์์ต๋๋ค. ์ง๊ธ์ ์ผ์ข ์ ํดํน์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ํจ๊ณผ๊ฐ ์์ผ๋ฉฐ ๊ฐ์ ๋ ์์๋ ์์์ ๋๋ค.
์ฝ๊ฐ์ ๊ณ ๋ ค ํ์ ๋๋ ์ด๊ฒ์ ์ฝ์ด์ ์ถ๊ฐํ์ง ์์ ๊ฒ์ ๋๋ค.
๊ทธ๋ฌ๋ ์ฌ๊ธฐ์ ๋ด๊ฐ ์๊ฐํด ๋ธ ์ฝ๋๊ฐ ์์ต๋๋ค.
new ManifestPlugin({
fileName: path.join(paths.appBuild, 'assets.json'),
writeToFileEmit: true,
generate: (seed, files) => {
const entrypoints = new Set();
const noChunkFiles = new Set();
const longTermCacheFiles = new Set();
files.forEach(file => {
if (file.isChunk) {
const groups = (
(file.chunk || {})._groups || []
).forEach(group => entrypoints.add(group));
} else {
noChunkFiles.add(file);
}
if (!webpackOptions.fileLoaderExclude.some(re=>re.test(file.path))) {
let fileHasHash = /\[(build|content)?hash/.test(
typeof webpackOptions.fileLoaderOutputName == 'function' ?
webpackOptions.fileLoaderOutputName(file) : webpackOptions.fileLoaderOutputName);
if (fileHasHash) longTermCacheFiles.add(file);
} else if (webpackOptions.urlLoaderTest.some(re=>re.test(file.path))) {
let urlHasHash = /\[(build|content)?hash/.test(
typeof webpackOptions.urlLoaderOutputName == 'function' ?
webpackOptions.urlLoaderOutputName(file) : webpackOptions.urlLoaderOutputName);
if (urlHasHash) longTermCacheFiles.add(file);
} else if (webpackOptions.cssTest.some(re=>re.test(file.path))) {
let cssHasHash = /\[(build|content)?hash/.test(
typeof webpackOptions.cssOutputFilename == 'function' ?
webpackOptions.cssOutputFilename(file) : webpackOptions.cssOutputFilename);
if (cssHasHash) longTermCacheFiles.add(file);
} else if (webpackOptions.jsTest.some(re=>re.test(file.path))) {
let jsHasHash = /\[(build|content)?hash/.test(
typeof webpackOptions.jsOutputFilename == 'function' ?
webpackOptions.jsOutputFilename(file) : webpackOptions.jsOutputFilename);
if (jsHasHash) longTermCacheFiles.add(file);
}
});
const entries = [...entrypoints];
const entryArrayManifest = entries.reduce((acc, entry) => {
const name =
(entry.options || {}).name ||
(entry.runtimeChunk || {}).name ||
entry.id;
const allFiles = []
.concat(
...(entry.chunks || []).map(chunk =>
chunk.files.map(path => config.output.publicPath + path)
)
)
.filter(Boolean);
const filesByType = allFiles.reduce((types, file) => {
const fileType = file.slice(file.lastIndexOf('.') + 1);
types[fileType] = types[fileType] || [];
types[fileType].push(file);
return types;
}, {});
const chunkIds = [].concat(
...(entry.chunks || []).map(chunk => chunk.ids)
);
return name
? {
...acc,
[name]: { ...filesByType, chunks: chunkIds },
}
: acc;
}, seed);
entryArrayManifest['noentry'] = [...noChunkFiles]
.map(file => file.path)
.reduce((types, file) => {
const fileType = file.slice(file.lastIndexOf('.') + 1);
types[fileType] = types[fileType] || [];
types[fileType].push(file);
return types;
}, {});
entryArrayManifest['cacheable'] = [...longTermCacheFiles]
.map(file => file.path);
return entryArrayManifest;
},
})
ํ์ง๋ง ์์ฐ์ ๋ํด ๋ง์ด ๋ฐฐ์ ์ต๋๋ค.)
ํ๋์ ์ด๊ฒ์ผ๋ก ๋ง์ ์๊ฐ์ ๋ณด๋ด์ง ๋ชปํด ๋ฏธ์ํ์ง๋ง ๊น๋ํด ๋ณด์ ๋๋ค. ๋ด ๋ฌผ๊ฑด์ ์ต์ ์์ ๋ razzle ๋ฒ์ ์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ๊ณ ์ฌ์ฉ์ ์ ์ ํ๋ฌ๊ทธ์ธ์ผ๋ก ์ ์์ ์๋ํด๋ณด์ญ์์ค.
๊ฝค ์ข์ ๋ณด์ด์ง๋ง ์ด๊ฒ์ ๋ํด ์ฝ๊ฐ ํผ๋ ์ค๋ฝ์ต๋๋ค.
let fileHasHash = /\[(build|content)?hash/.test(
typeof webpackOptions.fileLoaderOutputName == 'function'
? webpackOptions.fileLoaderOutputName(file)
: webpackOptions.fileLoaderOutputName);
if (fileHasHash) longTermCacheFiles.add(file);
webpackOptions.fileLoaderOutputName
์๋ฏธ๋ ๋ฌด์์
๋๊น? ๋๋ฅผ ์ํด ๊ทธ๊ฒ์ ํญ์ ์ ์๋์ง ์์ ๊ฒ์ฒ๋ผ ๋ณด์
๋๋ค.
razzle ์นด๋๋ฆฌ์์์๋ง
๊น๋ํ, ๋๋ ์นด๋๋ฆฌ์ ๋ธ๋์น์์ ์์ ํ๋ ๋ด ํ๋ก์ ํธ์ ๋ธ๋์น๋ฅผ ๊ฐ์ ธ ์์ ์ฝ๊ฐ์ ์ง์ ์ ์ด๋ฃจ์์ต๋๋ค. ์ ๋๋ก ์๋ํ์ง ์์ต๋๋ค. ํ์ฌ ์ ๋ฌธ์ ๋ ์ฃผ๋ก ํ์ ํจํค์ง๋ฅผ ์ธ์ํ๋๋ก ๋ฐ๋ฒจ ๋ก๋๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ๊ณผ ๊ด๋ จ๋ ๊ฒ ๊ฐ์ต๋๋ค. ๋น๋ ํ ์ ์์ง๋ง ์คํํ๋ ค๊ณ ํ ๋ "๋ชจ๋์ ์ฐพ์ ์ ์์"๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ด๊ฒ์ ์๋ง๋ ๋๋ฌด ํฅ๋ฏธ ๋กญ๊ฑฐ๋ ์ ์ฉํ์ง๋ ์์ง๋ง :
https://github.com/bootleg-rust/sites/pull/2/files
๋ฉ๋ชจ๋ฆฌ์์ ์๋ https://github.com/jaredpalmer/razzle/issues/664 ์์ ๊ตฌ์ฑ์ ๋น ๋ ธ์ต๋๋ค.
/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime sync:2
var e = new Error("Cannot find module '" + req + "'");
^
Error: Cannot find module 'undefined'
at require (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime sync:2:10)
at razzleCacheableFiles (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime/server.tsx:106:18)
at createKoaApp (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/lib-ssr-runtime/server.tsx:61:26)
at Module.call (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/src/server.tsx:42:13)
at a (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:19:22)
at Object.call (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/server.js:1:31123)
at __webpack_require__ (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:19:22)
at /Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/webpack:/webpack/bootstrap:83:10
at Object.<anonymous> (/Users/jstableford/Desktop/@bootleg-rust/sites/packages/web-rust-lang/build/server.js:1:935)
https://github.com/jaredpalmer/razzle/issues/1459
NODE_PATH = .. / ๋๋ ๋ฌด์ธ๊ฐ๋ฅผ ์ค์ ํ์ญ์์ค
์ฌ๊ธฐ์ ๋ช ๊ฐ์ง๋ฅผ ๋ณ๊ฒฝํด์ผ ํ ์๋ ์์ต๋๋ค.
https://github.com/jaredpalmer/razzle/blob/canary/packages/razzle/config/modules.js
์ข์, ๊ทธ๋์ ์กฐ๊ธ ํํค์ณ ์ ๋ฐฉ๊ธ ๋ฌธ์ ๊ฐ ์ค์ ๋ก process.env.RAZZLE_CHUNKS_MANIFEST
๋ ์ด์ ์ ์๋์ง ์๋ ์์ธ์ด๋ผ๋ ๊ฒ์ ๊นจ๋ฌ์์ต๋๋ค.
๋ด๊ฐ ์ฌ์ฉํ๋ ์ ์ผํ ๊ฒ์ ์บ์ ๊ฐ๋ฅํ ์์ฐ์ ๊ฐ์งํ๋ ๊ฒ์ด ์์ผ๋ฏ๋ก ์ง๊ธ ์ฐ๊ฒฐ ํ ์ ManifestPlugin
๊ตฌ์ฑ์ ์ ๊ณตํ์ฌ ๋์ฒด ํ ์ ์์ด์ผํฉ๋๋ค ๐.
ํ์ธ!
๋ด ํ๋ก์ ํธ์์ ๋น๋ถ๊ฐ ๋ด ์ฌ์ฉ ์ฌ๋ก์ ์ถฉ๋ถํ ์ ์๋ํ๋ ์ฌ์ฉ์ ์ ์ ํ๋ฌ๊ทธ์ธ์ ๋ง๋ค์์ต๋๋ค. ๋น์ ์ด ๋ง๋ ์ฝ๋๋ ๊ทธ๊ฒ์ ์์์ ์ผ๋ก ์ผ๋ ๋ฐ ๋งค์ฐ ๋์์ด๋์์ต๋๋ค.
๋๋ ๊ทธ๊ฒ์ ๊ณต์ ํ ๋นํธ๋ฅผ ๋ณ๊ฒฝํ์ง๋ง ์ฐธ๊ณ ๋ก ๋๋ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌ๋๋ ์๊ฐ ๊ทธ๊ฒ์ผ๋ก ๋ฌธ์ ๊ฐ ์๋ค๊ณ ์๊ฐ file-loader
์ด ์ฌ์ฉ์ํ๊ธฐ ๋๋ฌธ์ Array.prototype.every()
๋์ Array.prototype.some()
: !webpackOptions.fileLoaderExclude.every(re=>re.test(file.path))
์ฌ๊ธฐ์์ ๊ณต์ ํ๋ ๊ฒ์ด ์ ์ฉํ ๊ฒฝ์ฐ :
function modifyWebpackConfig({
env: { target, dev },
webpackConfig,
webpackObject,
options: { pluginOptions, razzleOptions, webpackOptions },
paths,
}) {
// TODO: allow passing in extra file categorizers with `pluginOptions`
const fileCategorizers = [
{
test: webpackOptions.urlLoaderTest,
outputName: webpackOptions.urlLoaderOutputName,
},
{
test: webpackOptions.cssTest,
outputName: webpackOptions.cssOutputFilename,
},
{
test: webpackOptions.jsTest,
outputName: webpackOptions.jsOutputFilename,
},
{
exclude: webpackOptions.fileLoaderExclude,
outputName: webpackOptions.fileLoaderOutputName,
},
];
const fileName = path.join(paths.appBuild, "cacheable-assets.json");
const assetPlugin = new WebpackManifestPlugin({
fileName,
writeToFileEmit: true,
generate: (seed, files) => {
const notHashedFiles = new Set();
const hashedFiles = new Set();
const setFileAs = (file, { containsHash }) => {
if (containsHash) {
hashedFiles.add(file);
} else {
notHashedFiles.add(file);
}
};
files.forEach((file) => {
if (file.name.startsWith("..")) {
// Files that start with ".." will live outside of the public/
// folder and therefore can't/shouldn't be accessed.
return;
}
const fileCategorized = fileCategorizers.some(
({ test, exclude, outputName }) => {
const passesTest =
test != null ? fileMatchesAnyRegexp(file, test) : true;
const passesExclude =
exclude != null ? !fileMatchesAnyRegexp(file, exclude) : true;
const fileMatches =
passesTest &&
passesExclude &&
fileMatchesTemplate(file.path, outputName);
if (fileMatches) {
const containsHash = webpackLoaderOutputContainsHash(
outputName,
file,
);
setFileAs(file, { containsHash });
}
return fileMatches;
},
);
if (!fileCategorized) {
// TODO: allow "strict" vs "lazy" mode here where we can only use
// regex on the filename to guess if a file contains a hash in it.
setFileAs(file, { containsHash: false });
}
});
const mutable = [...notHashedFiles].map((file) => file.path);
const immutable = [...hashedFiles].map((file) => file.path);
return {
mutable,
immutable,
};
},
});
if (target === "web") {
webpackConfig.plugins.push(assetPlugin);
}
if (target === "node") {
// NOTE: adding multiple DefinePlugin's causes issues
// so we have to find and edit the existing one.
const definePlugin = webpackConfig.plugins.find(
(p) => p.constructor.name === "DefinePlugin",
);
definePlugin.definitions[
"process.env.RAZZLE_PLUGIN_CACHEABLE_ASSETS"
] = JSON.stringify(fileName);
}
return webpackConfig;
}
const cacheableAssetsPlugin = {
modifyWebpackConfig,
};
๋๋ https://github.com/bootleg-rust/sites/pull/2/files#diff -59ee436c0396a1f925f067b7e7cbcdee354003236a279e0a87cf8831c7f587e3
์ ๋ง๋ค, ๊ณ ๋ง์. ๋๋ ์ฌ์ ํ ์๋ก์ด ํ๋ฌ๊ทธ์ธ ํํฌ์ ์ต์ํด์ง๊ณ ์์ต๋๋ค.
๋ด๊ฐ ํด๊ฒฐํ์ง ๋ชปํ ์ ์ผํ ์ฃผ์ ๋ฌธ์ ๋ razzle start
์ฌ์ฉํ์ฌ ๊ฐ๋ฐ ๋ชจ๋์์ ์คํํ ๋ ์ด๋ค ์ด์ ๋ก scss
ํ๋ฌ๊ทธ์ธ / ๋ก๋๊ฐ ์๋ํ์ง ์๋๋ค๋ ๊ฒ์
๋๋ค razzle start
ํ์ง๋ง razzle build
์ ์ฒด๋ฅผ ์ํํ๋ฉด ๋ชจ๋ ๊ด์ฐฎ์ ๋ณด์
๋๋ค.
๊ทธ๊ฒ์ด ๋ฌด์์ธ์ง ์ด๋ค ์์ด๋์ด? ์๋๋ฉด ์ด๋๊ฐ์ ๋ค๋ฅธ github ๋ฌธ์ ์ ์ด๊ฒ์ ๋ฃ์ ๊ฐ์น๊ฐ ์์ต๋๊น?
๋ํ ์ฌ์ฉ์ ์ง์ ๊ฒฝ๋ก์๋ modifyPaths๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ์ฑ ํ ์ ์์ต๋๋ค.
์ด๋ป๊ฒ ์๋ํ์ง ์์ต๋๊น?
์๋ก์ด ๋ฌธ์ ์ผ ์ ์์ต๋๋ค .. :)
์ ๊ฒฝ ์ฐ์ง ๋ง์ญ์์ค. sass ๋ก๋๊ฐ ์๋ํ์ง ์๋ ๊ฒ์ razzle๊ณผ ๊ด๋ จ๋ ํน์ ์ฌํญ์ด ์๋๋๋ค. ๋ฒ์ ๋ถ์ผ์น์ ๊ด๋ จ์ด ์๊ฑฐ๋ react-scripts
๋ฒ์ ๋ฐ / ๋๋ deps๋ฅผ ๋์ด ์ฌ๋ฆฌ๋ ํ์ ํจํค์ง์์๋ ์คํ ๋ฆฌ ๋ถ๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค.
์์ฐ ์ฒ๋ฆฌ๋ฅผ์ํ ํํฌ ์ถ๊ฐ, ์ข ๋ฃ ๐
์ธ๋ถ ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐ ํ ๊ฒ์ ํ์ธํ์ต๋๋ค. ์ฌ์ ํ ํด๋ผ์ด์ธํธ / ์๋ฒ / ์๋ฒ๋ฆฌ์ค์ ๋ํด ์์ ํด์ผํฉ๋๋ค. ์นด๋๋ฆฌ์์ ๋ํ ์์ด๋์ด๊ฐ ์์ต๋๊น? ์ฝ๊ฐ ๋ถ์ด ์์ต๋๋ค.
์ง๊ธ ์ฌ์ฉํ๋ ํํฌ์ ๋๋ค.
์ธ๋ถ ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐ ํ ๊ฒ์ ํ์ธํ์ต๋๋ค. ์ฌ์ ํ ํด๋ผ์ด์ธํธ / ์๋ฒ / ์๋ฒ๋ฆฌ์ค์ ๋ํด ์์ ํด์ผํฉ๋๋ค. ์นด๋๋ฆฌ์์ ๋ํ ์์ด๋์ด๊ฐ ์์ต๋๊น? ์ฝ๊ฐ ๋ถ์ด ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ node_modules
๋ฅผ build/server.js
์ ๋ฌถ๋ ๊ฒ์ด ๊ธฐ๋ณธ์ ์ผ๋ก ๋งค์ฐ ํธ๋ฆฌํ๋ค๋ ๊ฒ์ ์์์ต๋๋ค (์ฃผ๋ก ์๋ฒ์์). ๋ด ํ๋ก๋์
๋์ปค ์ด๋ฏธ์ง์์ node_modules
ํด๋๋ฅผ ์์ ํ ์ ์ธ ํ ์ ์๋ค๋ ๊ฒ์ ์ ๋ง ๋ฉ์ ธ ๋ณด์
๋๋ค.
๋ค์ดํฐ๋ธ / ํ๋ซํผ ๋ณ ์ข ์์ฑ๊ณผ ํจ๊ป ์๋ํ๋ ๋ฐฉ์์ ์ฌ์ฉ / ํ ์คํธ ํ ํ์๊ฐ ์๋ค๊ณ ๋งํ์ง๋ง (imagemagick๊ณผ ๊ฐ์ ๊ฒ์ด ๋ฌธ์ ๊ฐ ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค)
๋ด๊ฐ ๋ง๋ "externals"ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ ์ผ๋ฐ์ ์ธ ์ฌ๊ณ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
const externalsPluginOptions = {
// By default the NodeJS process uses the externals function razzle has and relies on `node_modules` to still be available
// after performing a razzle build. Setting this to `true` would mean that all dependencies attempt to get bundled into
// the build/ output unless explicitly specified as an external
resetNodeExternals: false,
// This probably wouldn't actually be required because the browser runtime
// doesn't have externals by default (i'm assuming)
resetWebExternals: true,
webExternals: [
// add externals for the web runtime here
],
nodeExternals: [
// add externals for the node runtime here
],
};
"์ ์ ํ"๊ตฌ์ฑ API๋ฅผ ์ค์ ํ๊ธฐ ์ ์ ์์งํ ๋งํ๋ฉด (ํนํ razzle ์ฝ์ด์์์ ๊ฒฝ์ฐ) ์นํฉ ๋ฌธ์์์ externals
์ ๋ํด ์ข ๋ ๊น์ด ์ฝ์ด์ผ ํ ๊ฒ์
๋๋ค. ์ธ๋ถ์ ๋ํ ๋ค๋ฅธ ์ฌ์ฉ ์ฌ๋ก ๐
.
ํ์ฌ๋ ์ธ๋ถ๋ฅผ ๋น์ ๋๋ ๋ฐ๋ง ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก ๋ฐํ์์ node_modules์ ์์กดํ์ง ์๋ ์ฝ๊ฒ ์ด์ ๊ฐ๋ฅํ ์ฑ์ ๋ชจ๋ ๊ฒ์ ๋ฒ๋ค๋ก ์ ๊ณตํฉ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ง๊ธ ์นด๋๋ฆฌ์๊ฐ ์ถ์ ๋์๋์ :)