이를 박박 갈며 정리하는 웹팩 설정

리린·2021년 8월 2일
1

웹팩 진짜..

  • 뭐가 이렇게 어려워
  • 공식사이트 보고 하는 게 가장 최고다. 공식 document의 중요성을 깨닫게 한 토픽

문제의 설정

  • cra를 eject해서 웹팩 코드를 꺼낸 뒤 서버사이드렌더링을 위해 웹팩 설정을 해야 한다는 것까지는 이해
  • 그런데 이 설정 파일이 도대체 뭘 의미하는지 모름
  • 아무 설명도 없음
  • 한글로 검색해도 뭐 없음
  • 그래서 직접 정리함

경로 코드

  • config/path.js
"use strict";

const path = require("path");
const fs = require("fs");
const getPublicUrlOrPath = require("react-dev-utils/getPublicUrlOrPath");

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);

// We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served.
// webpack needs to know it to put the right <script> hrefs into HTML even in
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
const publicUrlOrPath = getPublicUrlOrPath(
  process.env.NODE_ENV === "development",
  require(resolveApp("package.json")).homepage,
  process.env.PUBLIC_URL
);

const buildPath = process.env.BUILD_PATH || "build";

const moduleFileExtensions = [
  "web.mjs",
  "mjs",
  "web.js",
  "js",
  "web.ts",
  "ts",
  "web.tsx",
  "tsx",
  "json",
  "web.jsx",
  "jsx",
];

// Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => {
  const extension = moduleFileExtensions.find((extension) =>
    fs.existsSync(resolveFn(`${filePath}.${extension}`))
  );

  if (extension) {
    return resolveFn(`${filePath}.${extension}`);
  }

  return resolveFn(`${filePath}.js`);
};

// config after eject: we're in ./config/
module.exports = {
  dotenv: resolveApp(".env"),
  appPath: resolveApp("."),
  appBuild: resolveApp(buildPath),
  appPublic: resolveApp("public"),
  appHtml: resolveApp("public/index.html"),
  appIndexJs: resolveModule(resolveApp, "src/index"),
  appPackageJson: resolveApp("package.json"),
  appSrc: resolveApp("src"),
  appTsConfig: resolveApp("tsconfig.json"),
  appJsConfig: resolveApp("jsconfig.json"),
  yarnLockFile: resolveApp("yarn.lock"),
  testsSetup: resolveModule(resolveApp, "src/setupTests"),
  proxySetup: resolveApp("src/setupProxy.js"),
  appNodeModules: resolveApp("node_modules"),
  swSrc: resolveModule(resolveApp, "src/service-worker"),
  ssrIndexJs: resolveApp("src/index.server.js"),
  ssrBuild: resolveApp("dist"),
  publicUrlOrPath,
};

module.exports.moduleFileExtensions = moduleFileExtensions;
  • 실제로 추가한 코드 : module.exports 맨 아래의 이 두줄
 ssrIndexJs: resolveApp("src/index.server.js"),
 // 서버사이드 렌더링 엔트리 
 ssrBuild: resolveApp("dist"),
 //웹팩 처리 후 저장 경로 

문제의 그 웹팩 코드

  • config/webpack.config.server.js
const paths = require("./paths");
const getCSSModuleLocalIndent = require("react-dev-utils/getCSSModuleLocalIdent");
const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
const getClientEnvironment = require("./env");
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));

module.exports = {
  mode: "production",
  entry: paths.ssrIndexJs,
  target: "node",
  output: {
    path: paths.ssrBuild,
    filename: "server.js",
    chunkFilename: "js/[name].chunk.js",
    publicPath: paths.publicUrlOrPath,
  },
  module: {
    rules: [
      {
        oneOf: [
          {
            test: /\.(js|mjs|jsx|ts|tsx)$/,
            include: paths.appSrc,
            loader: require.resolve("babel-loader"),
            options: {
              customize: require.resolve(
                "babel-preset-react-app/webpack-overrides"
              ),
              presets: [
                [
                  require.resolve("babel-preset-react-app"),
                  {
                    runtime: "automatic",
                  },
                ],
              ],
              plugins: [
                [
                  require.resolve("babel-plugin-named-asset-import"),
                  {
                    loaderMap: {
                      svg: {
                        ReactComponent:
                          "@svgr/webpack?-svgo,+titleProp,+ref![path]",
                      },
                    },
                  },
                ],
              ],
              cacheDirectory: true,
              cacheCompression: false,
              compact: false,
            },
          },
          {
            test: cssRegex,
            exclude: cssModuleRegex,
            loader: require.resolve("css-loader"),
            options: {
              importLoaders: 1,
              modules: {
                exportOnlyLocals: true,
              },
            },
          },
          {
            test: cssModuleRegex,
            loader: require.resolve("css-loader"),
            options: {
              importLoaders: 1,
              modules: {
                exportOnlyLocals: true,
                getLocalIdent: getCSSModuleLocalIndent,
              },
            },
          },
          {
            test: sassRegex,
            exclude: sassModuleRegex,
            use: [
              {
                loader: require.resolve("css-loader"),
                options: {
                  importLoaders: 3,
                  modules: {
                    exportOnlyLocals: true,
                    getLocalIdent: getCSSModuleLocalIndent,
                  },
                },
              },
              require.resolve("sass-loader"),
            ],
          },
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve("url-loader"),
            options: {
              emitFile: false,
              limit: 10000,
              name: "static/media/[name].[hash:8].[ext]",
            },
          },
          {
            loader: require.resolve("file-loader"),
            exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
            options: {
              emitFile: false,
              name: "static/media/[name].[hash:8].[ext]",
            },
          },
        ],
      },
    ],
  },
  resolve: {
    modules: ["node_modules"],
  },
  externals: [
    nodeExternals({
      allowlist: [/@babel/],
    }),
  ],
};
  • loader: 번들링 로더 설정
  • include: 파일확장명 포함
  • exclude: 파일확장명 불포함
  • options: loader의 설정 (loader에 따라 달라질 수 있으므로 공식 홈페이지 참고)
  • resolve: 귀결
  • modules: 어떤 폴더가 모듈인지 설정
  • externals: 바깥에서 불러올 정보(import) 설정 . 이 경우 번들링 안됨

과정

  1. eject하기
    콘솔:
    git add .
    git commit -m'Commit before eject'
    yarn eject

  2. config/webpack.config.server.js 파일 작성하기

  3. node_modules에서 불러오는 것 제외하고 번들링하기
    콘솔 : yarn add webpack-node-externals
    설정 적용: webpack.config.server.js의 상단에
    'const nodeExternals = require('webpack-node-externals'); ' , modules 설정 적기

  4. 빌드 스크립트 작성하기

  • scripts/build.server.js
process.env.BABEL_ENV = "production";
process.env.NODE_ENV = "production";

process.on("unhandledRejection", (err) => {
  throw err;
});

require("../config/env");
const fs = require("fs-extra");
const webpack = require("webpack");
const config = require("../config/webpack.config.server");
const paths = require("../config/paths");

function build() {
  console.log("Creating server build..");
  fs.emptyDirSync(paths.ssrBuild);
  let compiler = webpack(config);
  return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      if (err) {
        console.log(err);
        return;
      }
      console.log(stats.toString());
    });
  });
}

build();
  • fs.emptyDirSync: 비어있다는 사실을 귀결시켜줌(있으면 삭제 함 )
  • compiler.run( (err, statas) => ...) : entry포인트를 stats로 받아옴
  1. package.json의 scripts 수정
"scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js",
    "start:server": "node dist/server.js",
    "build:server": "node scripts/build.server.js"
  },
  1. 빌드하기
    콘솔: build:server
profile
개발자지망생

0개의 댓글