초보자용 웹팩 서버 설정

자바스크립트에 재미가 들려서 한발한발 나아가고 있습니다. 순서가 조금 뒤죽박죽인데, jQuery 나 jQueryUI 는 오래전부터 쓰고 있고, 회사에서 작업중인 소스에는 이미 Vue 가 들어있어서 남이 만들어놓은 소스의 수정 정도는 하고 있는 정도입니다.

공부시작

하지만, 조금 어려운 문법같은게 나오면 (this 라던가, this 라던가.) 자바스크립트 문법을 비난하며 회피하곤 했습니다. 자바스크립트라는 것은 UI 다듬을 때나 ajax 를 써야할 때만 억지로 쓰는 언어라고 단정하곤 했습니다.

아래는 자바스크립트의 또 다른 약점인 더하기 빼기입니다.

얼마전부터 온라인 스터디에 참여하고 있는데, 다행히, 이번에는 진도가 조금씩 나가고 있습니다. 선생님이 내준 숙제는 간단했지만, 소스를 다듬다보니 점점 빠져듭니다. 파이썬만 쓴지 몇년째라 화살표 함수가 다시 낯설어졌었는데, 지금은 이런 코드도 만들 수 있게되었습니다.

function validate(msg, fn) {
  if (fn()) throw new Error(msg);
}

function validateTodoItems(items) {
  validate('items must not be null', () => !items);
  validate('items must be array', () => !Array.isArray(items))
}

웹훅 필요

암튼, 신기해하면서 숙제를 하다가, 자바스크립트에서도 importexport가 된다길래 써보았는데 오류가 발생했습니다. 그 동안은 숙제를 하면 브라우저에서 file:///로 열어서 확인했는데 저건 못받아 주시겠답니다.

아래는 문제의 html, css, js 파일들. 초 단순 문법 확인용 파일들입니다.

<!DOCTYPE html>
<html lang='ko'>
<head>
  <link href='style.css' rel='stylesheet'></head>
</head>
<body>
  <div id='testblock'>Test text</div>
  <script type='text/javascript' src='src/index.js'></script>
</body>
</html>
// src/index.js
import App from './App.js'; // 이거다 import 문법을 써보고 싶었다.

new App();</code></pre>

// src/App.js
function App() {
    console.log("Hello from App"); // 나중에 이 자리를 수정해보자.
}

export default App;
/* style.css */
#testblock {
    color: red;
}

오류 내용은 Uncaught SyntaxError: Cannot use import statement outside a module 입니다. 모듈로 로딩하지 않아서 모듈기능은 쓸 수 없다네요.

저걸 해결하겠다고 type="text/javascript"부분을 type="module"으로 수정하면 CORS 관련한 더 이상한 메시지가 뜹니다. 검색해보니 이 문법을 제대로 쓰려면 로컬서버든 뭐든 암튼 http따위로 가져와야 할 것 같습니다.

서버 띄우는 거야 간단하지만, 기왕 하는 것 자바스크립트 세계에 들어왔으니 node기반의 webpack을 써보겠습니다. 그러려면 package.json 이라는 파일을 만들어줘야합니다.

{
  "name": "js_study",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0"
  },
  "scripts": {
    "start:dev": "webpack-dev-server --watch src"
  }
}

이제 npm install;npm run start:dev 를 실행하면 웹서버가 뜹니다. 만세!

HMR 써보자

접속도 잘되고, 기쁜마음으로 숙제를 더 하는데… 언젠가 보았던 “자바스크립트 소스를 수정하자마자 웹브라우저가 수정된 소스를 읽어들여서 화면을 갱신하는 장면”이 떠올랐습니다. 웹팩 설정을 하다보니, 그게 vue 나 react 에서만 가능한 기능은 아니었겠다는 느낌… 어쨌든, 나도 그걸 써보고 싶었습니다.

이런 저런 키워드로 검색해보니 그 기능은 HMR (hot module replacement)이라고 합니다. 그전에는 브라우저쪽에서 제어하는 LiveReload 라는 것도 있었던것 같은데, 웹팩쪽에서는 저렇게 부르는 모양이네요.

시간을 한참 들이긴 했지만, 웹팩에 적절한 플러그인만 추가/설정하면 됩니다. 간단해보이지만, NPM 생태계에서 여러가지 모듈을 조합해서 잘 동작하는 설정을 찾아내는 것은 쉽지 않았습니다.

어쨌든 package.json 의 devDependencies 항목에 이녀석들을 넣어주고,

"css-loader": "^3.2.0",
"style-loader": "^1.0.0",
"html-webpack-plugin": "^3.2.0",

npm install을 실행한다. 그리고, 웹팩 설정파일 (webpack.config.js)을 다음처럼 만든다.

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js' // 기본값이라 생략가능하다.
  },
  module: {
    rules: [
      {
        test: /\.(scss|css)$/,
        use: ['style-loader', 'css-loader',]
      }
    ]
  },
  devServer: { hot: true },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: 'index.html',
      showErrors: true
    }),
  ]
};

이러면 index.js 나 style.css 은 웹팩이 알아서 넣어주시기 때문에 index.html 에서 style.css 나 index.js 참조하는 코드는 삭제해버려도 됩니다.

대신 style.css 는 index.js 에서 넣어주어야 합니다. 다른 설정방법도 있겠지만, 제일 간단해보입니다.

// index.js
require ('../style.css')

import App from './App.js'; // 이거다 import 문법을 써보고 싶었다.
new App();

자.. npm install , npm run start:dev 를 실행하고 크롬으로 localhost:8080 에 접속한 다음 소스를 수정해보면 브라우저에 곧바로 반영되는 것을 볼 수 있다.

설정을 찾는 과정은 (지금보면 별거아닌데) 꽤 복잡했습니다.

복잡했다

예를 들어 대부분의 예제 문서는 CSS를 처리하려면 ExtractTextPlugin 을 사용하라고 하고 있었다. 하지만 아무리 설정을 해봐도 잘 되지 않았고 결국 npmjs나 github 리포에 가서야 그건 올드하니 대신 MiniCssExtractPlugin 을 쓰라는 얘기를 읽을 수 있었다. 하지만, 그것도 HtmlWebpackPlugin 만 잘 동작하면 ‘style-loader’ 로 충분했다.

이런 설정을 위해 webpack.config.js 가 필요하다는 것도 한참만에 알게되었다. 그냥 package.json 만 있어도 npm run start 같은 게 동작하지만, 상세한 설정을 위해서는 저 파일이 필요한 거였다.

그리고, 도중에 parcel이란 녀석을 만나서 저거로 간단하게 가버릴까 유혹을 받기도 했지만, 어쩐지 아직은 webpack 설정을 한번쯤 해보는게 앞으로 공부에 도움될 것 같았다. 이것 저것 귀찮으면 parcel 도 괜찮을 것 듯.

바벨 추가

이제 뭘 추가하는 건 쉬웠다. npm install babel-loader @babel/core @babel/preset-env --save-dev 하고, 웹팩 설정에 js 파일의 로딩 규칙에 바벨을 추가한다.

{
  test: /\.js$/,
  exclude: /node_modules/,
  use : {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env']
    }
  }
}

이제 npx webpack 을 실행하면 dist 폴더에 바벨이 번역한 js 파일이 만들어진다.

스타일 검사

소스스타일 검사기는 eslint 를 설정했다. npm install eslint eslint-config-airbnb eslint-plugin-import --save-dev를 실행하고, package.json 에 eslint 관련 항목을 추가한다.

"eslintConfig": {
  "extends": "airbnb-base",
  "env": {
    "browser": true
  }
},

이제 npx eslint src 하면 소스 스타일 검사를 해준다.

테스트 도구 jest

테스트 도구는 jest 를 설정했다. npm install jest --save-dev하고 npx jest를 실행하면 된다. 다음을 src/App.test.js 에 저장하고 실행해보니 잘 돌아간다.

test('1 is 1', () => {
  expect(1).toBe(1);
});

추가 : jest 만으로는 import 관련 문법을 처리하지 못한다. babel 을 통과하도록 해야한다. 결국 npm install babel-jest regenerator-runtime --save-dev로 몇가지 추가해주고, bable.config.js 설정도 추가해야한다. package.json에 관련 설정을 넣어주면 된다. (파일하나 줄였다!)

여기까지.. 기본적인 환경은 완료인 듯 !

동작하는 마지막 소스는 https://github.com/jinto/js_study에 올려두었다.