๐Ÿ  ๋ณ€ํ™˜

๋Œ€๋ถ€๋ถ„์˜ ๋ฒˆ๋“ค๋Ÿฌ๋“ค์ด ์• ์…‹ ๋ณ€ํ™˜(transform)์„ ์œ„ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜์™€ ์„ค์ •(configuration)์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. Parcel์€ ๋งŽ์€ ์ˆ˜์˜ ์ผ๋ฐ˜์ ์ธ ๋ณ€ํ™˜๊ณผ ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ๋ฅผ ๋‚ด์žฅํ•˜์—ฌ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Babel์„ ์‚ฌ์šฉํ•˜๋Š” JavaScript, PostCSS์„ ์‚ฌ์šฉํ•˜๋Š” CSS, PostHTML์„ ์‚ฌ์šฉํ•˜๋Š” HTML์„ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Parcel์€ ๋ชจ๋“ˆ ์•ˆ์—์„œ ์„ค์ • ํŒŒ์ผ(์˜ˆ: .babelrc, .postcssrc)์„ ๋ฐœ๊ฒฌํ–ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ์ด ๋ณ€ํ™˜์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์จ๋“œํŒŒํ‹ฐ(third-party) node_modules์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์„ค์ • ํŒŒ์ผ์ด ํŒจํ‚ค์ง€์˜ ์ผ๋ถ€๋กœ ๋งŒ๋“ค์–ด์ง€๋ฉด ๊ทธ ๋ณ€ํ™˜์€ ํ•ด๋‹น ๋ชจ๋“ˆ์—์„œ๋งŒ ์ž๋™์œผ๋กœ ์ผœ์ง‘๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฒˆ๋“ค๋ง์„ ๋น ๋ฅด๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์˜ค์ง ๋ณ€ํ™˜์— ํ•„์š”ํ•œ ๋ชจ๋“ˆ๋งŒ์ด ๊ฐ€๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋˜ํ•œ ์–ด๋–ค ํŒŒ์ผ์„ ํฌํ•จํ•˜๊ณ  ์ œ์™ธํ•˜๊ธฐ ์œ„ํ•ด ์ˆ˜๋™์œผ๋กœ ๋ณ€ํ™˜์„ ๊ตฌ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์Œ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์จ๋“œํŒŒํ‹ฐ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ๋นŒ๋“œ ๋˜์—ˆ๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

Babel

Babel์€ ์ปค๋‹ค๋ž€ ํ”Œ๋Ÿฌ๊ทธ์ธ ํ™˜๊ฒฝ์„ ๊ฐ–์ถ˜ ์ธ๊ธฐ์žˆ๋Š” JavaScript ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ์ž…๋‹ˆ๋‹ค. Parcel์„ Babel๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋…๋ฆฝํ˜•์œผ๋กœ ์‚ฌ์šฉ ํ•  ๋•Œ๋‚˜ ๋‹ค๋ฅธ ๋ฒˆ๋“ค๋Ÿฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋˜‘๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ํ”„๋ฆฌ์…‹์„ ์•ฑ์— ์„ค์น˜ ํ•˜์„ธ์š”:

yarn add @babel/preset-env

๊ทธ๋ฆฌ๊ณ ๋‚˜์„œ, .babelrc๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค:

{
  "presets": ["@babel/preset-env"]
}

PostCSS

PostCSS๋Š” plugin์„ ์‚ฌ์šฉํ•˜์—ฌ CSS๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. autoprefixer, cssnext, CSS Modules์™€ ๊ฐ™์ด ๋ง์ž…๋‹ˆ๋‹ค. .postcssrc (JSON), .postcssrc.js, ๋˜๋Š” postcss.config.js ์ค‘ ํ•˜๋‚˜์˜ ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์—ฌ Parcel๋กœ PostCSS๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์•ฑ์— ์„ค์น˜ ํ•˜์„ธ์š”:

yarn add postcss-modules autoprefixer

๊ทธ๋ฆฌ๊ณ  .postcssrc๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค:

{
  "modules": true,
  "plugins": {
    "autoprefixer": {
      "grid": true
    }
  }
}

ํ”Œ๋Ÿฌ๊ทธ์ธ์€ plugins ๊ฐ์ฒด์— ํ‚ค๋กœ ์ง€์ •๋˜๊ณ  ์˜ต์…˜์€ ๊ฐ์ฒด์˜ ๊ฐ’์œผ๋กœ ์ •์˜ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์˜ต์…˜์ด ์—†๋‹ค๋ฉด, ๋Œ€์‹  true๋กœ ์„ค์ •ํ•˜์„ธ์š”.

Autoprefixer, cssnext ๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ๋„๊ตฌ์˜ ๋Œ€์ƒ ๋ธŒ๋ผ์šฐ์ €๋Š” .browserslistrc๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

> 1%
last 2 versions

CSS Modules์€ ์ตœ์ƒ์œ„ modules ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฝ๊ฐ„ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ํ™œ์„ฑํ™” ๋ฉ๋‹ˆ๋‹ค. CSS Modules์€ JavaScript ๋ฒˆ๋“ค์— ํฌํ•จ๋  ๊ฐ์ฒด๋ฅผ ๋‚ด๋ณด๋‚ด๋ฏ€๋กœ Parcel๋กœ ํ•˜์—ฌ๊ธˆ ํŠน๋ณ„ํ•œ ์ง€์›์„ ํ•„์š”๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. postcss-modules๊ฐ€ ํ”„๋กœ์ ํŠธ ์•ˆ์— ์„ค์น˜๋˜์–ด์•ผ ํ•จ์„ ์ฃผ์˜ํ•˜์„ธ์š”.

CSS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ธฐ

CSS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์•ˆ์˜ .postcssrc ์—์„œ CSS Module์„ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

cssnano ์ฝ”๋“œ ์ตœ์†Œํ™” ์„ค์ •ํ•˜๊ธฐ

Parcel์€ ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ๋ฅผ ํ•  ๋•Œ cssnano๋ฅผ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด cssnano.config.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.

module.exports = {
  calc: false,
  discardComments: {
    removeAll: true,
  }
};

PostHTML

PostHTML ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ HTML์„ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. .posthtmlrc (JSON), posthtmlrc.js, posthtml.config.js ์ค‘ ํ•˜๋‚˜์˜ ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์—ฌ Parcel์— PostHTML์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์•ฑ์— ์„ค์น˜ ํ•˜์„ธ์š”.

yarn add posthtml-img-autosize

๊ทธ ํ›„, .posthtmlrc์„ ์ž‘์„ฑํ•˜์„ธ์š”.

{
  "plugins": {
    "posthtml-img-autosize": {
      "root": "./images"
    }
  }
}

ํ”Œ๋Ÿฌ๊ทธ์ธ์€ plugins ๊ฐ์ฒด์— ํ‚ค๋กœ ์ง€์ •๋˜๊ณ  ์˜ต์…˜์€ ๊ฐ์ฒด์˜ ๊ฐ’์œผ๋กœ ์ •์˜ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์˜ต์…˜์ด ์—†๋‹ค๋ฉด, ๋Œ€์‹  true๋กœ ์„ค์ •ํ•˜์„ธ์š”.

TypeScript

TypeScript๋Š” ํƒ€์ž…์ด ์ถ”๊ฐ€๋œ JavaScript์˜ Superset ์–ธ์–ด๋กœ, ์ปดํŒŒ์ผํ•˜๋ฉด ์ผ๋ฐ˜ JavaScript๋กœ ๋ณ€ํ™˜๋˜๋ฉฐ ์ตœ์‹  ES2015+ ๊ธฐ๋Šฅ๋“ค๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. TypeScript์˜ ๋ณ€ํ™˜ ์ž‘์—…์€ ์ถ”๊ฐ€์ ์ธ ์„ค์ • ์—†์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

<!-- index.html -->
<!doctype html>
<html>
<body>
  <script src="./index.ts"></script>
</body>
</html>
// index.ts
import message from "./message";
console.log(message);
// message.ts
export default "Hello, world";

ReasonML/BuckleScript

ReasonML์€ BuckleScript๋ฅผ ์ด์šฉํ•ด OCaml์„ JavaScript๋กœ ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ์„ ์„ค์น˜ํ•˜๊ณ  bsconfig.json์„ ๋งŒ๋“ค๋ฉด ReasonML์„ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$ yarn add bs-platform --dev
// bsconfig.json
// from https://github.com/BuckleScript/bucklescript/blob/master/jscomp/bsb/templates/basic-reason/bsconfig.json

{
  "name": "whatever",
  "sources": {
    "dir": "src",
    "subdirs": true
  },
  "package-specs": {
    "module": "commonjs",
    "in-source": true
  },
  "suffix": ".bs.js",
  "bs-dependencies": [
  ],
  "warnings": {
    "error": "+101"
  },
  "namespace": true,
  "refmt": 3
}
<!-- index.html -->
<html>
<body>
  <script src="./src/index.re"></script>
</body>
</html>
/* src/index.re */
print_endline("Hello World");

ReasonReact

ReasonReact๋Š” ReasonML์˜ React ๋ฐ”์ธ๋”ฉ์ž…๋‹ˆ๋‹ค. ์—ญ์‹œ Parcel๊ณผ ํ•จ๊ป˜ ์ด์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

$ yarn add react react-dom reason-react
// bsconfig.json

{
  "name": "whatever",
+ "reason": {
+   "react-jsx": 2
+ },
  "sources": {
    "dir": "src",
    "subdirs": true
  },
  "package-specs": {
    "module": "commonjs",
    "in-source": true
  },
  "suffix": ".bs.js",
  "bs-dependencies": [
+   "reason-react"
  ],
  "warnings": {
    "error": "+101"
  },
  "namespace": true,
  "refmt": 3
}
<!-- index.html -->
<html>
<body>
+  <div id="app"></div>
  <script src="./src/index.re"></script>
</body>
</html>
/* src/Greeting.re */

let component = ReasonReact.statelessComponent("Greeting");

let make = (~name, _children) => {
  ...component,
  render: _self => <div> (ReasonReact.string("Hello! " ++ name)) </div>,
};
/* src/index.re */

ReactDOMRe.renderToElementWithId(<Greeting name="Parcel" />, "app");