Webpack HtmlWebpackPlugin chunksSortMode로 실행순서 조절하기

Maliethy·2022년 3월 4일
0

webpack

목록 보기
1/2

1. issue

현재 프로젝트는 반복해서 사용되는 header, footer.html를 분리하고 나서 jquery로 각 페이지에 추가하도록 구성되어 있다. 분리된 html 중 header_nav.html에 mouseover시 drop down menu가 나오거나 버튼 클릭 이벤트 등 모든 페이지에서 사용해야될 로직은 common.js에 담겨 있다.
sub2, sub3, sub6.html은 각각 하위메뉴를 가지고 있어 해당 페이지를 클릭할 때마다 해당 하위메뉴가 활성화되고 나머지 하위메뉴들은 감추어져야한다.
예를 들어 sub2.html로 들어가면 sub2_menu에 active 클래스 이름 추가되고 sub6_menu에서는 active 클래스 이름 제거되어야 한다.

  • load_HTML.js - jquery로 header_nav.html, - footer_float_menu.html 등을 원하는 html 위치에 추가하는 로직 실행
  • common.js - header nav 관련 event 로직 실행
  • sub2.js - sub2_menu에 active 클래스 이름 추가, sub3_menu, sub6_menu에서는 active 클래스 이름 제거하기

이때 /src/js/sub2.js에서 세 js 파일 순서를 조절하려고 하니 로드할 때마다 달라져 sub2_menu가 보이지 않는 문제가 발생했다.
예를 들어 common 함수 실행->sub2함수 실행->load_HTML함수 실행과 같은 순서로 해당 로직들이 실행되면서 해당 페이지 하위 메뉴가 보이지 않는 문제가 발생했다. 왜냐하면 이와 같이 함수를 실행하면 sub2_menu가 로드되지도 않았는데 sub2 함수가 sub2_menu에 active 클래스 이름 추가하기 때문이다.

sub2_menu가 사라진 상태

sub2_menu가 보여지는 상태

/src/js/sub2.js

import("./load_HTML.js")
  .then(({ default: load_HTML }) => {
    load_HTML();
  })
  .catch((err) => {
    console.error("load_HTML error", err);
  });

import("./common.js")
  .then(({ default: common }) => {
    common();

    $(function () {
      sub2();
    });
  })
  .catch((err) => {
    console.error("common error", err);
  });

function sub2() {
  if ($(".sub2_menu").hasClass("active") === false) {
    $(".sub2_menu").addClass("active");
    $(".sub3_menu").removeClass("active");
    $(".sub6_menu").removeClass("active");
    for (let i = 2; i < 6; i++) {
      if (window.location.href.split("/")[3] === `sub2_${i}.html`) {
        $(".sub_header > ul > li").removeClass("on");
        $(`.sub_header > ul > li:nth-child(${i})`).addClass("on");
      }
    }
  }
}

/src/header_nav.html

  <header id="header" class="clearfix">
    <div class="inner">
      <h1>
        <a href="./index.html"> <p class="logo"></p> </a>
      </h1>
      <nav id="nav" class="clearfix only_pc">
        <ul class="gnb clearfix">
          <li><a href="./sub1.html">회사소개</a></li>
          <li class="sub2_active">
            <a href="./sub2.html">서비스</a>
            <ul class="sub_gnb">
              <li><a href="sub2.html">풀필먼트 서비스</a></li>
              <li><a href="sub2_2.html">SAVE3</a></li>
              <li><a href="sub2_3.html">해외수입 유통물류</a></li>
              <li><a href="sub2_4.html">라이브쇼핑/이벤트 물류</a></li>
              <li><a href="sub2_5.html">쿠팡쉽먼트</a></li>
            </ul>
          </li>

                 ...

    <div class="sub_header sub2_menu">
      <ul>
        <li class="on"><a href="sub2.html">풀필먼트 서비스</a></li>
        <li><a href="sub2_2.html">SAVE3</a></li>
        <li><a href="sub2_3.html">해외수입 유통물류</a></li>
        <li><a href="sub2_4.html">라이브쇼핑/이벤트 물류</a></li>
        <li><a href="sub2_5.html">쿠팡쉽먼트</a></li>
      </ul>
    </div>
    <div class="sub_header sub3_menu">
      <ul>
        <li class="on"><a href="sub3.html">요금안내</a></li>
        <li><a href="sub3_2.html">실시간 견적 서비스</a></li>
        <li><a href="sub3_3.html">스마트스토어 셀러 견적 서비스</a></li>
      </ul>
    </div>
    <div class="sub_header sub6_menu">
      <ul>
        <li class="on"><a href="sub6.html">자주묻는 질문</a></li>
        <li><a href="sub6_2.html">공지사항</a></li>
        <li>
          <a onclick="window.open('https://modument.com/welcome/')">1:1문의</a>
        </li>
      </ul>
    </div>
  </header>

/src/js/load_HTML.js

function load_HTML() {
  $(function () {
    $("#header_nav").load("header_nav.html", function () {
      console.log("hello");
    });
    $("#footer_float_menu").load("footer_float_menu.html");
    $(".top_banner").load("top_banner.html");
  });
}

export default load_HTML;

/src/js/common.js

function common() {
  $(function () {
    $(".gnb > li > a").on("mouseover", function () {
      $(".sub_gnb").removeClass("active");
      $(this).siblings(".sub_gnb").addClass("active");
    });

    $("#header").on("mouseleave", function () {
      $(".sub_gnb").removeClass("active");
    });

    $(".float_top").on("click", function () {
      $("html, body").animate(
        {
          scrollTop: 0,
        },
        400
      );
      return false;
    });

    $(".togglebar").on("click", function () {
      $("#m_nav").addClass("on");

      $("#m_nav .close").on("click", function () {
        $("#m_nav").removeClass("on");
      });
    });

    $(window).on("scroll", function () {
      if ($(window).scrollTop() >= 120) {
        $("#header").addClass("sticky");
      } else {
        $("#header").removeClass("sticky");
      }
    });
    console.log("load common");
  });
 }

2. solution

원하는 js load 순서는 다음과 같다.
1. load_HTML.js
2. common.js
3. sub2.js

HtmlWebpackPlugin git의 example에서 chunks순서를 조절할 수 있는 옵션에 대한 예시가 나온다. 다음 코드와 같이 chunksSortMode를 manual로 설정한 후 chunks를 내가 원하는 순서대로 맞추면 된다.

new HtmlWebpackPlugin({
inject: true,
filename: 'second-file.html',
template: 'template.html',
chunksSortMode: 'manual',
chunks: ['a', 'b', 'd']
}),

webpack.config.js

module.exports = (env, options) => {
  const prod = options.mode === "production";
  const htmlPageNames = [
    "index",
    "sub1",
    "sub2",
    "sub2_2", //3
    "sub2_3",
    "sub2_4", //5
    "sub2_5", //6
    "sub3", //7
    "sub3_2",
    "sub3_3",
    "sub4", //10
    "sub5", //11
    "sub6",
    "sub6_2",
    "sub6_2_detail",
    "sub6_3",
    "service_use_term",
    "privacy_info_use_term", //17
    "header_nav",
    "footer_float_menu",
    "top_banner",
  ];

  let entry = {
    load_HTML: path.resolve(__dirname, "src/js/load_HTML.js"),
    load_common: path.resolve(__dirname, "src/js/load_common.js"),
    common: path.resolve(__dirname, "src/js/common.js"),
    review_slider: path.resolve(__dirname, "src/js/review_slider.js"),
  };
  
  const multipleHtmlPlugins = htmlPageNames.map((name, idx) => {
    const splited = name.split("_")[0];
    let chunks = idx < 18 ? ["load_HTML", "load_common"] : [];

    (splited === "index" ||
      splited === "sub2" ||
      splited === "sub5" ||
      splited === "sub6") &&
      ((entry[splited] = path.resolve(__dirname, `src/js/${splited}.js`)),
      (chunks = ["load_HTML", splited]));//load_HTML을 앞에 두기

    splited === "sub3" &&
      ((entry[splited] = path.resolve(__dirname, `src/js/${splited}.ts`)),
      (chunks = ["load_HTML", splited]));

    ((idx > 4 && idx < 8) || idx === 3 || idx === 10 || idx === 11) &&
      chunks.push("review_slider");

    console.log(name, chunks);//아래 이미지로 결과를 확인할 수 있습니다
    return new HtmlWebpackPlugin({
      template: path.resolve(`src/${name}.html`),
      filename: `${name}.html`,
      chunks,
      inject: "body",
      chunksSortMode: "manual",
    });
  });

이렇게 설정해 각 페이지별로 로드되는 chunks 순서를 다음과 같이 정렬시킨다.

그리고 /src/js/sub2.js에서는 common.js만 로드하고 load_HTML은 따로sub2.js에서 import로 불러와 실행하지 않고 html에서 바로 실행되도록 다음과 같이 수정한다.

/src/js/load_HTML.js

$("#header_nav").load("header_nav.html", function () {
  console.log("load html");
});
$("#footer_float_menu").load("footer_float_menu.html");
$(".top_banner").load("top_banner.html");

/src/js/sub2.js

import("./common.js")
  .then(({ default: common }) => {
    common();
    $(function () {
      sub2();
    });
  })
  .catch((err) => {
    console.error("common error", err);
  });

function sub2() {
 console.log("sub2");
  if ($(".sub2_menu").hasClass("active") === false) {
    $(".sub2_menu").addClass("active");
    $(".sub3_menu").removeClass("active");
    $(".sub6_menu").removeClass("active");
    for (let i = 2; i < 6; i++) {
      if (window.location.href.split("/")[3] === `sub2_${i}.html`) {
        $(".sub_header > ul > li").removeClass("on");
        $(`.sub_header > ul > li:nth-child(${i})`).addClass("on");
      }
    }
  }
}

빌드 결과는 다음과 같다.
/dist/sub2.html

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1"
    />
    <title>MODUMENT</title>
    <link rel="icon" href="assets/favicon.png" />
    <link href="css/vendors.4e59dc5734b4c633e946.css" rel="stylesheet" />
  </head>
  <body>
    <div id="header_nav"></div>
    <div class="sub2_banner">
      ...
    </div>
    <div class="sub sub2">
      ...
    </div>
    <div id="footer_float_menu"></div>
    <script defer="defer" src="js/runtime.9cd00242584b50ecc473.js"></script>
    <script defer="defer" src="js/vendors.4e59dc5734b4c633e946.js"></script>
    <script defer="defer" src="js/load_HTML.02b569f8b9344162b230.js"></script>
    <script defer="defer" src="js/sub2.f18402af9e7adb7b11b8.js"></script>
  </body>
</html>

참고:
https://github.com/jantimon/html-webpack-plugin/blob/main/examples/sort-manually/webpack.config.js

profile
바꿀 수 있는 것에 주목하자

0개의 댓글