[TIL] 211206

dev·2021년 12ė›” 6ėž
0

TIL

ëŠĐ록 ëģīęļ°
110/204
post-thumbnail

📝 ė˜Ī늘 한 ęēƒ

  1. user profile ëģĩėŠĩ

  2. webpack - babel-loader / SCSS loader / MiniCssExtractPlugin


📚 ë°°ėšī ęēƒ

1. user profile ëģĩėŠĩ

ėē˜ėŒëķ€í„° ë‹Īė‹œ ęĩŽí˜„í•īëģļ 후 í—·ę°ˆë ļ거나 ë‹Īė‹œ ëģīęģ  ė‹ķė€ ëķ€ëķ„ ė •ëĶŽ

1) DB ė—…ë°ėīíŠļ / session ė—…ë°ėīíŠļ

model.findByIdAndUpdate()는 ė—…ë°ėīíŠļ ė „ė˜ 데ėī터ëĨž return 한ë‹Ī.
ė—…ë°ėīíŠļ í›„ė˜ 데ėī터ëĨž 받ęļ° ėœ„í•īė„œëŠ” { new: true } ė˜ĩė…˜ė„ ėĪ˜ė•ž 한ë‹Ī.

2) multer ė‚ŽėšĐ

íŒŒėžė„ ė—…ëĄœë“œ 하ęļ° ėœ„í•īė„œëŠ” express가 íŒŒėž ęē―로ëĨž ė•Œ 눘 ėžˆë„ëĄ ėķ”ę°€í•˜ęģ , express.static("ëļŒëžėš°ė €ė— ë…ļėķœė‹œí‚Ž íī더 ėīëĶ„")ė„ ė‚ŽėšĐí•īė•ž 한ë‹Ī.

app.use("/uploads", express.static("uploads"));

3) video owner profile

videoëĨž ė—…ëĄœë“œí•œ userė˜ profileė€ 누ęĩŽë‚˜ ëģž ėˆ˜ ėžˆë„ëĄ req.session.user._id가 ė•„ë‹ˆëž req.params.idëĨž ė‚ŽėšĐí•īė•ž 한ë‹Ī.

4) loggedInUser is undefined

// watch.pug

// ėƒëžĩ
  if String(video.owner._id) === loggedInUser._id
    a(href=`${video.id}/edit`) Edit Video →
    a(href=`${video.id}/delete`) Delete Video →

ėœ„ ė―”ë“œė˜ ęē°ęģž, 로ę·ļėļ í•˜ė§€ ė•Šė€ user가 homeė—ė„œ video.titleė„ íīëĶ­í•ī watch 페ėī맀뗐 ë“Īė–ī가ë Īęģ  í•˜ëĐī, loggedInUser가 undefinedëžė„œ ė—ëŸŽę°€ ëœĻ는 ëŽļė œę°€ ë°œėƒí•œë‹Ī.

// middlewares.js
export const localsMiddleware = (req, res, next) => {
  res.locals.siteName = "Wetube";
  res.locals.loggedIn = true;
  res.locals.loggedInUser = req.session.user || {}; // ėī ëķ€ëķ„뗐 || {} ëĨž ėķ”ę°€í–ˆë‹Ī ❗
};

localsMiddlewareė—ė„œ user가 로ę·ļėļí•˜ė§€ ė•Šė•„ë„ loggedInUser가 true ę°’ė„ ę°€ė§ˆ 눘 ėžˆë„ëĄ ėˆ˜ė •í•īė•ž 한ë‹Ī.

5) req.session.loggedInęģž res.locals.loggedInUser ëđ„ęĩ

// middlewares.js
export const localsMiddleware = (req, res, next) => {
  res.locals.siteName = "Wetube";
  res.locals.loggedIn = Boolean(req.session.loggedIn);
  res.locals.loggedInUser = req.session.user || {};
  next();
};

export const protectorMiddleware = (req, res, next) => {
  if (res.locals.loggedInUser) { // req.session.loggedInėœžëĄœ ëģ€ęē― â—
    next();
  } else {
    return res.redirect("/login");
  }
};

export const publicOnlyMiddleware = (req, res, next) => {
  if (!res.locals.loggedInUser) { // req.session.loggedInėœžëĄœ ëģ€ęē― â—
    next();
  } else {
    return res.redirect("/");
  }
};

ė›đ ė‚ŽėīíŠļė˜ ė–īë–Ī 페ėī맀뗐 ë“Īė–ī갈 때도 localsMiddleware가 ė‹Ī행되ęļ° ë•ŒëŽļ뗐 req.session.userė˜ 氒뗐 따띾 loggedInUserė˜ 값ėī ë‹Žëžė§ˆ ėˆ˜ë„ ėžˆė§€ë§Œ

ė•žė„œ localsMiddlewareė—ė„œ loggedInUserė˜ ę°’ė„ ëŽīėĄ°ęąī true가 되도록 || {}ė„ ėķ”ę°€í–ˆęļ° ë•ŒëŽļ뗐
ėœ„ė™€ 같ėī protectorMiddleware뙀 publicOnlyMiddlewareė—ė„œ res.locals.loggedInUserëĨž ė‚ŽėšĐ하ëĐī ę·ļ 값ėī ëŽīėĄ°ęąī true가 되ė–ī ė—ëŸŽę°€ ë°œėƒí•œë‹Ī.

ë”°ëžė„œ, res.locals.loggedInUser ëŒ€ė‹ ė— req.session.loggedInė„ ė‚ŽėšĐí•īė•ž 한ë‹Ī.


2. Webpack

1) Webpackėī란?

Webpackėī란 ėžë°”ėŠĪ큎ëĶ―íŠļ나 CSS, ėīëŊļė§€ ë“ąė˜ ëĶŽė†ŒėŠĪë“Īė„ ëģ€í™˜í•˜ęģ  ëŽķė–īėĢžëŠ” ëŠĻ듈 ëēˆë“Ī럮ëĨž 말한ë‹Ī.

ë‹Ī만, react.js, vue.js 등 대ëķ€ëķ„ė˜ 큰 í”„ë ˆėž„ė›ŒíŽė—ëŠ” webpackėī ėīëŊļ ë‚īėžĨ되ė–ī ėžˆęļ° ë•ŒëŽļ뗐 ė‹Īė œëĄœ webpackė„ 링렑 ėž‘ė„ąí•īëģž ėžė€ ęą°ė˜ ė—†ë‹Ī.

ę·ļ럮나, ėī는 ė—…ęģ„ í‘œėĪ€ėœžëĄœėĻ ėĩœė†Œí•œ ė–īë–ŧęēŒ ėž‘ë™í•˜ëŠ”ė§€ëŠ” ė•Œė•„ė•ž 하ęļ° ë•ŒëŽļ뗐 ėīí•īëĨž ėœ„í•ī webpack configuration íŒŒėžė„ ėž‘ė„ąí•īëģīë Īęģ  í•œë‹Ī.

2) webpack ė„Īėđ˜ 및 ė‚ŽėšĐëē•

(1) ė„Īėđ˜

npmė„ ėīėšĐí•ī webpackęģž webpack cliëĨž devDependencies뗐 ė„Īėđ˜í•œë‹Ī.
webpack cliëĨž ėīėšĐí•ī ė―˜ė†”ė—ė„œ webpackė„ ëķˆëŸŽë‚ž 눘 ėžˆë‹Ī.

$ npm i webpack webpack-cli --save-dev

(2) entry / output

í”„ëĄœė íŠļ íīë”ė— webpack.config.js íŒŒėžė„ 만든ë‹Ī.
ėī íŒŒėžė—ëŠ” ėĩœė‹  ė―”ë“œëĨž ė‚ŽėšĐí•īė„œëŠ” ė•ˆëœë‹Ī.

export default, import (x)
module.exports = {}, const ~ require() (o)

webpackė„ ė‚ŽėšĐ하ęļ° ėœ„í•īė„œëŠ” entry뙀 outputė„ ë°˜ë“œė‹œ 맀렕í•īėĪ˜ė•ž 한ë‹Ī.
entry란 webpackė„ ęą°ėģ ëģ€í˜•ė‹œí‚Īęģ ėž 하는 íŒŒėžė˜ ęē―로ëĨž 말한ë‹Ī.
outputėī란 webpackė„ ęą°ėģ ëģ€í˜•된 ęē°ęģžëŽžė„ ė €ėžĨ할 íŒŒėžëŠ…ęģž ę·ļ íŒŒėžėī ė €ėžĨ될 ęē―로ëĨž 말한ë‹Ī.

ėī때 ę·ļ íŒŒėžėī ė €ėžĨ될 ęē―로는 ė ˆëŒ€ ęē―로로 ėž‘ė„ąí•īė•ž 한ë‹Ī.
ėīëĨž ėœ„í•ī path.resolve(__dirname, "")ëĨž ėīėšĐ한ë‹Ī.
path.resolve()는 ėž…ë Ĩ한 ëŠĻ든 파íŠļë“Īė„ ëŠĻė•„ ęē―로로 만ë“Īė–īėĪ€ë‹Ī.
dirnameėī란 rootëķ€í„° íī더ęđŒė§€ė˜ ęē―로 ė „ėēīëĨž ė˜ëŊļ한ë‹Ī.

ė‹ĪėŠĩė„ ėœ„í•ī ė•„ëž˜ė™€ 같ėī ė„Īė •í•œ 후, src/client/js íī더 ė•ˆė— main.js íŒŒėžė„ 만ë“Īė–ī ėĩœė‹  ė―”ë“œëĨž ėž‘ė„ąí–ˆë‹Ī.

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./src/client/js/main.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "assets", "js"),
  },
};

package.json íŒŒėžė—ė„œ assets scriptëĨž 만든 후 ė―˜ė†”ė—ė„œ ė‹Ī행한ë‹Ī.

// package.json
"scripts": {
  "assets": "webpack --config webpack.config.js"
},
$ npm run assets

ėžë™ėœžëĄœ assets/js íīë”ė™€ í•Ļęŧ˜ ę·ļ ė•ˆė— ëģ€í˜•된 ė―”ë“œę°€ ë‹īęļī main.js íŒŒėžėī 만ë“Īė–īė§„ ęēƒė„ 확ėļ할 눘 ėžˆë‹Ī.
( + assets íī더ëĨž .gitignore íŒŒėžė— ėķ”ę°€í•˜ė—Ž github뗐 ė—…ëĄœë“œí•˜ė§€ ė•Šë„ëĄ 한ë‹Ī.)

ėī ė―”ë“œëŠ” ė œëŒ€ëĄœ ėž‘ë™í•˜ęģ  ėžˆė§€ë§Œ, ėžëķ€ ė―”ë“œëŠ” ëļŒëžėš°ė €ę°€ ėīí•īí•˜ė§€ ëŠŧ할 ėˆ˜ë„ ėžˆęļ° ë•ŒëŽļ뗐 í˜ļí™˜ė„ąė„ 확ëģīí•īė•ž 한ë‹Ī.

ë”°ëžė„œ, ė•žė„œ ë°ąė—”ë“œ ė―”ë“œ ėē˜ëĶŽëĨž ėœ„í•ī package.json íŒŒėžė—ė„œ babelė„ ė‚ŽėšĐ했ë“Ŋėī, 프론íŠļė—”ë“œ ė―”ë“œ ėē˜ëĶŽëĨž ėœ„í•ī webpack.config.js íŒŒėžė—ė„œ babelė„ ė‚ŽėšĐí•īė•ž 한ë‹Ī.

(3) rules (ëŠĻ든 js íŒŒėžė— babel-loader 렁ėšĐ)

íŠđė • ėĒ…ëĨ˜ė˜ íŒŒėžė— íŠđė • ëģ€í˜•ė„ 렁ėšĐ하ęļ° ėœ„í•ī rulesëĨž ė‚ŽėšĐí•īė•ž 한ë‹Ī.
webpackė€ loaderëĨž í†ĩí•ī íŒŒėžė„ ė „í™˜ė‹œí‚Ļë‹Ī.
ėī ęē―ėš°ė—ëŠ” JavaScript ė―”ë“œ(test)ëĨž babel-loader(use, loader)ëĨž ėīėšĐí•ī ëģ€í™˜í•īė•ž 한ë‹Ī.

babel-loaderė„ ėīėšĐ하ęļ° ėœ„í•īė„œëŠ” ë‹ĪėŒ ëŠĻ듈ë“Īė„ ė„Īėđ˜í•īė•ž 한ë‹Ī.
나ëĻļė§€ëŠ” ëŠĻ두 ė„Īėđ˜ë˜ė–ī ėžˆėœžëŊ€ëĄœ babel-loader만 따로 ė„Īėđ˜í•īėĢžė—ˆë‹Ī.

npm install -D babel-loader @babel/core @babel/preset-env webpack

babel-loader ė‚ŽėšĐëē•ė€ ė•„ëž˜ė™€ 같ë‹Ī.
babel-loaderëĨž ė°ļęģ í•ī webpack.config.js íŒŒėžė„ ėˆ˜ė •í–ˆë‹Ī.

// webpack.config.js
const path = require("path");

module.exports = {
  // ėƒëžĩ
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env", { targets: "defaults" }]],
          },
        },
      },
    ],
  },
};

ėī렜 webpackė€ babel-loader뗐 멇 氀맀 ė˜ĩė…˜ė„ ė „í•īėĢžëĐīė„œ ėīëĨž ėīėšĐí•ī ëŠĻ든 js íŒŒėžė„ ëģ€í˜•í•˜ë„ëĄ 한ë‹Ī.

ë‹Īė‹œ assets/jsė—ė„œ main.js íŒŒėžė„ 확ėļí•īëģīëĐī, babel-loader뗐 ė˜í•ī JavaScript ė―”ë“œę°€ 한ëēˆ ë” ëģ€í™˜ëœ ęēƒė„ 확ėļ할 눘 ėžˆë‹Ī.

(4) mode ė˜ĩė…˜

npm run assetsė„ ė‹Īí–‰í•˜ė—Ž assets íī더ëĨž 만ë“Īė—ˆė„ 때ëķ€í„° ė―˜ė†” ė°―ė— mode optionėī ė„Īė •ë˜ė§€ ė•Šė•˜ë‹Ī는 ęē―ęģ  ëŽļęĩŽę°€ 떠 ėžˆë‹Ī.

ėīëĨž í•īęē°í•˜ęļ° ėœ„í•ī webpack뗐ęēŒ ėī ė―”ë“œę°€ ė§€ęļˆ ę°œë°œ ėĪ‘ėļė§€ ė•„ë‹Œė§€ëĨž ė•Œë ĪėĪ˜ė•ž 한ë‹Ī.
modeëĨž ė„Īė •í•˜ė§€ ė•ŠėœžëĐī webpackė€ ęļ°ëģļė ėœžëĄœ production mode로 ė„Īė •ë˜ė–ī ëŠĻ든 ė―”ë“œë“Īė„ ė••ėķ•í•ĻėœžëĄœėĻ ė—ëŸŽëĨž ė°ūęļ° íž˜ë“Īęļ° ë•ŒëŽļėīë‹Ī.

ë”°ëžė„œ, í˜„ėžŽëŠ” development mode로 ė„Īė •í•œ 후, 나ėΑ뗐 ė„œëē„뗐 ë°ąė—”ë“œëĨž 링렑 ė˜ŽëĶī 때 ėīëĨž 바ęŋ”ëģž ęēƒėīë‹Ī.

const path = require("path");

module.exports = {
  entry: "./src/client/js/main.js",
  mode: "development", // ėķ”ę°€ ❗
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "assets", "js"),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env", { targets: "defaults" }]],
          },
        },
      },
    ],
  },
};

(5) ëļŒëžėš°ė €ė— assets íī더 ęģĩ氜

땄링 express는 assets띾는 íīë”ė˜ ėĄīėžŽëĨž ëŠĻëĨīęģ , ëļŒëžėš°ė €ë„ í•īë‹đ íīë”ė— ė ‘ę·ží•  눘 ė—†ë‹Ī.
server.js íŒŒėžė—ė„œ express.static()ė„ ėīėšĐí•ī ëļŒëžėš°ė €ę°€ assets íīë”ė— ė ‘ę·ží•  눘 ėžˆë„ëĄ 한ë‹Ī.

ėī때 ęē―ëĄœė˜ ėīëĶ„ė€ ė–īë–Ī ėīëĶ„ėœžëĄœë“  ėž‘ė„ąí•  눘 ėžˆė§€ë§Œ, íī더 ėīëĶ„ė€ ėœ„ė—ė„œ 만든 íī더 ėīëĶ„ ę·ļ대로 ėž‘ė„ąí•īė•ž 한ë‹Ī.
똈ëĨž ë“Īė–ī, ė•„ëž˜ė™€ 같ėī ėž‘ė„ąí–ˆë‹ĪëĐī assets íīë”ė˜ ë‚īėšĐė„ /static ėĢžė†ŒëĨž í†ĩí•ī ęģĩ개하도록 하는 ęēƒėīë‹Ī.

// server.js
app.use("/static", express.static("assets"));

base.pug íŒŒėžėī main.js íŒŒėžė„ ëķˆëŸŽė˜Ž 눘 ėžˆë„ëĄ assets/js/main.jsëĨž base.pug뙀 ė—°ęē°í•œë‹Ī.
ëļŒëžėš°ė €ëŠ” /static ~ ęē―ëĄœė—ė„œ assets íī더 ë‚īėšĐė„ ëģž ėˆ˜ ėžˆë‹Ī.

//- base.pug

// ėƒëžĩ
include partials/footer
script(src="/static/js/main.js")

ėī렜 ė›đ ė‚ŽėīíŠļė˜ ė–īë–Ī 페ėī맀뗐 ë“Īė–ī가든 main.js íŒŒėžė˜ ë‚īėšĐėī ė‹Ī행된ë‹Ī.
â€ŧ /static/js/main.js는 node.js ė―”ë“œę°€ ė•„ë‹ˆëž 'ëļŒëžėš°ė €ė—ė„œ ėž‘ë™í•˜ëŠ” js ė―”ë“œ'ėīë‹Ī.

ðŸ’Ą ė •ëĶŽí•˜ëĐī

📌 src/client/js/main.js뗐 ėĩœė‹  js ė―”ë“œëĨž ėž‘ė„ąí•˜ëĐī
📌 webpack.config.js뗐 따띾 ę·ļęēƒė˜ ëģ€í˜•된 ęē°ęģžëŽžėī assets/js/main.js뗐 ë‹īęļ°ęēŒ ëœë‹Ī.
📌 pug íŒŒėžė€ assets íī더로ëķ€í„° ę·ļ ęē°ęģžëŽž(js ė―”ë“œ)ė„ ëķˆëŸŽė˜Ļë‹Ī.

3) SCSS loader

(1) scss íŒŒėž 만ë“Īęļ°

client íī더 ė•ˆė— scss íī더ëĨž 만든 후 ę·ļ ė•ˆė— styles.scss íŒŒėžęģž _variables.scss íŒŒėžė„ 만든ë‹Ī.

// _variables.scss
$red: red;
// style.scss
@import "./_variables";

body {
  background-color: $red;
}

client íī더 ė•ˆė˜ js íī더 ė•ˆė˜ main.js íŒŒėžė—ė„œ, client íī더 ė•ˆė˜ scss íī더 ė•ˆė˜ styles.scss íŒŒėžė„ import 한ë‹Ī.

// main.js
import "../scss/styles.scss";

console.log("hi");

(2) rules

loaderëĨž ė§€ė •í•  때는 ėœ„ė—ė„œ ėž‘ė„ąí•œ ęēƒėē˜ëŸž 할 ėˆ˜ë„ ėžˆė§€ë§Œ, ė—ŽëŸŽ ę°œė˜ loaderëĨž ëŠĻė•„ė„œ ė§€ė •í•  ėˆ˜ë„ ėžˆë‹Ī.

똈ëĨž ë“Īė–ī, scss íŒŒėžė„ ėē˜ëĶŽí•˜ęļ° ėœ„í•īė„œëŠ” ë‹ĪėŒęģž ę°™ė€ ė„ļ ę°œė˜ loader가 í•„ėš”í•˜ë‹Ī.

  • sass-loader: scss íŒŒėžė„ ėžë°˜ css íŒŒėžëĄœ ëģ€í™˜
$ npm install sass-loader sass webpack --save-dev
  • css-loader: scss íŒŒėžė˜ @import뙀 url()ė„ import/require()로 풀ė–īė„œ í•īė„
$ npm install --save-dev css-loader
  • style-loader: ëģ€í™˜ëœ cssëĨž ëļŒëžėš°ė €ė— 렁ėšĐ (cssëĨž DOM뗐 ėĢžėž…)
$ npm install --save-dev style-loader

ėœ„ 3ę°œė˜ loaderëĨž ė•„ëž˜ė™€ 같ėī 한 ëēˆė— ëŠĻė•„ė„œ 렁ė–īėĪ„ 눘 ėžˆë‹Ī.
ėī때 ėĢžė˜í•  ė ė€ ė‚ŽėšĐ ėˆœė„œė™€ 반대로 렁ė–īė•ž 한ë‹Ī는 ęēƒėīë‹Ī.
ė͉, 가ėžĨ ėē˜ėŒ ė‚ŽėšĐ될 loaderëĨž 가ėžĨ 나ėΑ뗐 렁ė–īė•ž 한ë‹Ī.

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./src/client/js/main.js",
  mode: "development",
  output: path.resolve(__dirname, "assets", "js"),
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env", { targets: "defaults" }]],
          },
        },
      },
      {
        test: /\.scss$/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  },
};

ėī렜 ė―˜ė†”ė— npm run assetsëĨž ėž…ë Ĩ하ëĐī

webpackė€ entry íŒŒėž(main.js)ė„ 가ė ļė˜Ļë‹Ī.
webpackė€ ę·ļ main.js íŒŒėžėī JavaScript íŒŒėžėž„ė„ ėļė‹í•œ 후, ėīëĨž babelė„ ėīėšĐí•ī ëģ€í™˜í•˜ëŠ”ë°
main.js íŒŒėžė—ė„œ import í•īėĪ€ íŒŒėžėī scss íŒŒėžėž„ė„ ėļė‹í•œ 후, scss íŒŒėžė„ scss, css, style loaderëĨž ėīėšĐí•ī css íŒŒėžëĄœ ëģ€í™˜í•˜ęģ  ėīëĨž ė›đ ė‚ŽėīíŠļė˜ head ė•ˆė— ėž…ë Ĩí•ī ëļŒëžėš°ė €ė— 렁ėšĐė‹œí‚Ļë‹Ī.

(3) MiniCssExtractPlugin

CSSëĨž ėķ”ėķœí•īė„œ ë‹ĪëĨļ íŒŒėžëĄœ ëķ„ëĶŽí•˜ęļ° ėœ„í•ī MiniCssExtractPluginė„ ė„Īėđ˜í•œë‹Ī.

$ npm install --save-dev mini-css-extract-plugin

ėī는 style-loaderëĨž ëŒ€ė‹ í•˜ė—Ž ë‹ĪėŒęģž ę°™ėī ė‚ŽėšĐ할 눘 ėžˆë‹Ī.
ė―”ë“œ ė–‘ė‹ėīęļ° ë•ŒëŽļ뗐 ė™ļėšļ í•„ėš”ëŠ” ė—†ë‹Ī. ( MiniCssExtractPlugin ė°ļęģ  )

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // ėķ”ę°€ ❗

module.exports = {
  entry: "./src/client/js/main.js",
  mode: "development",
  plugins: [new MiniCssExtractPlugin()], // ėķ”ę°€ ❗
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "assets", "js"),
  },
  module: {
    rules: [
      // ėƒëžĩ (babel-loader)
      {
        test: /\.scss$/, // ëŠĻ든 scss íŒŒėž
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"], // ėˆ˜ė • ❗
      },
    ],
  },
};

ë‹Īė‹œ npm run assetsëĨž ė‹Ī행하ëĐī assets íī더 ė•ˆė˜ js íī더 ė•ˆė— main.js íŒŒėžęģž ë”ëķˆė–ī main.css íŒŒėžėī ėƒęļīë‹Ī.

ę·ļ런데 js뙀 css가 ëķ„ëĶŽë˜ęļ°ëŠ” í–ˆė§€ë§Œ, assets íī더 ė•ˆė˜ js íīë”ė— í•Ļęŧ˜ ë“Īė–ī가 ėžˆë‹Ī.

css íŒŒėžė€ css íīë”ė—, js íŒŒėžė€ js íīë”ė— ë„Ģęļ° ėœ„í•ī
webpack.config.js íŒŒėžė˜ output ëķ€ëķ„ė—ė„œ filenameė„ ėˆ˜ė •í•˜ęģ 
MiniCssExtractPlugin ëķ€ëķ„뗐 filename ė˜ĩė…˜ė„ ë„Ģė–īėĪ€ë‹Ī.

module.exports = {
  entry: "./src/client/js/main.js",
  mode: "development",
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/styles.css", // ėķ”ę°€ ❗
    }),
  ],
  output: { 
    filename: "js/main.js", // ėˆ˜ė • ❗
    path: path.resolve(__dirname, "assets"), // "js" ė‚­ė œ ❗
  },
  // ėƒëžĩ
};

(4) pug íŒŒėžęģž css íŒŒėž ė—°ęē°

base.pug íŒŒėžėī style.css íŒŒėžė„ ëķˆëŸŽė˜Ž 눘 ėžˆë„ëĄ assets/css/style.cssëĨž base.pug뙀 ė—°ęē°í•œë‹Ī.

//- base.pug

doctype html
html(lange="ko")
  head
    title #{pageTitle} | #{siteName}
    link(rel="stylesheet" href="https://unpkg.com/mvp.css")
    link(rel="stylesheet" href="/static/css/styles.css")

// ėƒëžĩ
  include partials/footer
  script(src="/static/js/main.js")

ðŸ’Ą ë‹Īė‹œ 한ëēˆ ë” ė •ëĶŽí•˜ëĐī

📌 webpack뗐 ė˜í•īė„œ client íī더 ë‚īė˜ íŒŒėžë“Īėī 로ë”Đ된ë‹Ī.
📌 webpackė„ ęą°ėđœ í›„ė˜ ęē°ęģžëŽžë“Īė€ assets íī더 ė•ˆė— 만ë“Īė–īė§„ë‹Ī.
📌 ę·ļ ęē°ęģžëŽžë“Īė€ base.pug íŒŒėžė—ė„œ 로ë”Đ되ė–ī ëļŒëžėš°ė €ė— ëģīėīęēŒ ëœë‹Ī.

4) 개발 환ęē― ę°œė„ 

(1) ė €ėžĨ할 때마ë‹Ī ėžë™ ė—…ë°ėīíŠļ

ė§€ęļˆėœžëĄœėŽ scss나 프론íŠļė—”ë“œ ėŠ― js íŒŒėžė—ė„œ ė―”ë“œëĨž ëģ€ęē―할 때마ë‹Ī ė―˜ė†”ė— npm run devëĨž ėž…ë Ĩí•īė•žë§Œ ė—…ë°ėīíŠļ가 된ë‹Ī.
또한 멅ë đė–īëĨž ė‹Ī행하ęļ° ė „ė— í˜đė‹œ ëŠĻëĨž ė—ëŸŽëĨž ë°Đė§€í•˜ęļ° ėœ„í•ī í•­ėƒ assets íī더ëĨž ė§€ė›ŒėĪ˜ė•žë§Œ 한ë‹Ī.

ė €ėžĨ할 때마ë‹Ī ėžë™ėœžëĄœ ė—…ë°ėīíŠļ가 되도록 watch functionė„ ė‚ŽėšĐ하도록 한ë‹Ī.
output íī더가 만ë“Īė–īė§€ęļ° ė „ė— ęļ°ėĄīė˜ output íī더가 ė‚­ė œë˜ë„ëĄ clean ė˜ĩė…˜ė„ ė‚ŽėšĐ하도록 한ë‹Ī.

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: "./src/client/js/main.js",
  mode: "development",
  watch: true,
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/style.css",
    }),
  ],
  output: {
    filename: "js/main.js",
    path: path.resolve(__dirname, "assets"),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/, // ëŠĻ든 js íŒŒėž
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env", { targets: "defaults" }]],
          },
        },
      },
      {
        test: /\.scss$/, // ëŠĻ든 scss íŒŒėž
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
    ],
  },
};

ėī렜 í•­ėƒ 두 ę°œė˜ ė―˜ė†”ė„ í•Ļęŧ˜ ęĩŽë™í•īė•ž 한ë‹Ī.

$ npm run dev
$ npm run assets

(2) webpack뗐 ė˜í•ī ė„œëē„ę°€ ėžŽė‹œėž‘í•˜ė§€ ė•Šë„ëĄ

ę·ļ런데 ėī렇ęēŒ í•˜ëĐī 프론íŠļė—”ë“œ ėžë°”ėŠĪ큎ëĶ―íŠļ ė―”ë“œëĨž ëģ€ęē―한 후 ė €ėžĨė„ 할 때마ë‹Ī nodemon뗐 ė˜í•ī ë°ąė—”ë“œę°€ ėžŽė‹œėž‘ëœë‹Ī.

ëŽļ렜ëĨž í•īęē°í•˜ęļ° ėœ„í•ī ėžëķ€ íŒŒėž 및 íī더는 ëģ€ęē―되더띾도 nodemonėī ėīëĨž ëŽīė‹œí•˜ęģ  ė„œëē„ëĨž ėžŽė‹œėž‘í•˜ė§€ ė•Šë„ëĄ í•īė•ž 한ë‹Ī.

ėīëĨž ėœ„í•ī í”„ëĄœė íŠļ íīë”ė— nodemon.json íŒŒėžė„ ėƒė„ąí•œ 후 ė•„ëž˜ė™€ 같ėī ėž‘ė„ąí•œë‹Ī. (https://github.com/remy/nodemon ė°ļęģ )

// nodemon.json
{
  "ignore": ["webpack.package.js", "src/client/*", "assets/*"],
  "exec": "babel-node src/init.js"
}

ę·ļ 후, package.json íŒŒėžė—ė„œ dev script (nodemon -L --exec ...)ëĨž ėˆ˜ė •í•œë‹Ī.
( + assets script도 ėˆ˜ė •í•œë‹Ī. webpack.config.json íŒŒėžė€ webpackėī ė‹Ī행될 때 ęļ°ëģļė ėœžëĄœ ė°ū는 ė„Īė • íŒŒėžėīęļ° ë•ŒëŽļ뗐 ęĩģėī ë’Ī뗐 렁ė–īėĢžė§€ ė•Šė•„ë„ 된ë‹Ī. )

// ėˆ˜ė • ė „ package.json
"scripts": {
  "dev": "nodemon -L --exec babel-node src/init.js",
  "assets": "webpack --config webpack.config.json"
}
// ėˆ˜ė • 후 package.json
"scripts": {
  "dev:server": "nodemon -L",
  "dev:assets": "webpack"
}

script ėīëĶ„ë„ 링ęī€ė ėœžëĄœ 바ęŋ”ėĢžė—ˆë‹Ī.
í•­ėƒ 각각 ë‹ĪëĨļ ė―˜ė†”ė—ė„œ 두 ę°œė˜ 멅ë đė–īëĨž ëŠĻ두 ė‹Ī행í•īė•ž 한ë‹Ī.
하나는 ė„œëē„ëĨž ė‹Īí–‰ė‹œí‚Īęģ  í…œí”ŒëĶŋęģž urlė„ 확ėļ하ęļ° ėœ„í•ī í•„ėš”í•˜ęģ , ë‹ĪëĨļ 하나는 client íŒŒėžė„ 확ėļ하ęļ° ėœ„í•ī í•„ėš”í•˜ë‹Ī.


âœĻ ë‚īėž 할 ęēƒ

  1. ę°•ė˜ ęģ„ė† ë“Ģęļ°
profile
dev log

0ę°œė˜ 댓ęļ€