폴리필을 써야 하는 이유

바벨의 플러그인인 es5로 변환할수 있는 것만 변경해준다. es5에 이미 존재 했었던 개념은 플러그인으로 변경이 가능하지만 ECMAScript2015+에서 새롭게 등장한 개념은 플러그인을 통해서 변경이 되지 않는다.
예를 들어 화살표 함수의 경우에는 일반 함수로 변경이 가능하지만, Promise같은 새로운 개념은 변경이 안된다.
그래서 이렇게 새롭게 등장한 개념에 대한 변경이 가능하도록 폴리필이라고 부르는 코드조각을 추가해서 해결하게 된다.
폴리필로 사용되는 것중 하나에 core-js가 존재한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// babel.config.js

module.exports = {
presets: [
['@babel/preset-env', {
targets: {
chrome: 79,
ie: 11
},
useBuiltIns: 'usage', // 폴리필을 쓰겠다.
corejs: { // 폴리필로 corejs를 쓰겠다.
version: 2
}
}]
]
}
  • useBuiltIns: 어떠한 방식으로 폴리필을 사용하지에 대한 옵션으로, value에 ‘usage’를 주면 코드상에서 사용이 되는 폴리필만 import 하도록 해준다.
  • corejs: 폴리필시에 사용되는 파일로써 version을 옵션으로 사용하고자 하는 버전을 선택가능하다.

웹팩에서 바벨 사용하기

실무환경에서는 바벨을 따로 사용하지 않고 웹팩을 통해서 사용하게 된다. 보통은 바벨은 로더 형태로 제공된다(babel-loader).

1
npm install -D babel-loader
1
2
3
4
5
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/ // node_modlues에도 js가 있어서 제외 시켜준다
}

Comment and share

바벨이 나온 배경

브라우저마다 사용하는 언어가 달라서 프론트엔드 코드는 일관적이지 못할때가 많다. 프론트엔드 개발에서 크로스브라우징 이슈는 코드의 일관성을 헤친다. 크로스브라우징의 혼란을 해결할수 있는 것이 바벨이다. ECMAScript2015+로 작성한 코드를 모든 브라우저에서 동작하도록 호환성을 지켜준다.


바벨의 기본동작

바벨은 ECMAScript2015이상의 코드를 적당한 하위버전으로 바꾸는 것이 주된 역할이다. 이렇게 바뀐 코드는 오래된 버전의 브라우저에서도 작동할수 있도록 해준다.

파싱(Parsing)

코드를 읽고 추상 구문 트리로 변환하는 단계

변환(Transforming)

추상 구문 트리를 변경하는 것이 변환으로써, 실제로 코드를 변경하는 작업

출력(Printing)

변경된 결과물을 출력


바벨 설치하기

@babel/cli는 터미널도구로 사용하기 위해서 설치해야 한다.

1
npm install @babel/core @babel/cli

아래의 명령어로 바벨을 실행시키면 콘솔로 출력 해준다.

1
npx babel 파일경로

바벨 플러그인

기본적으로 바벨은 파싱과 출력만 담당을 하고 변환작업은 플러그인에게 맡긴다. 그래서 플러그인이 설치되어 있지 않으면 기본적으로 입력된 것과 동일한 출력만 하게 된다.


커스텀 플러그인

모듈 함수를 실행하면 visitor 객체를 가진 객체를 반환해야 한다. 그리고 visitor 객체는 Identifier 메소드를 가지고 path 파라미터를 넘겨준다.
visitor에는 여러개의 메소드를 작성할수 있는데 이미 정해져 있는 메소드명을 사용해야 원하는 결과를 얻을수 있다.

1
2
3
// app.js

let data = () => 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// babel-plugin.js

module.exports = function myBabelPlugin() {
return {
visitor: {
Identifier(path) {
console.log(path.node.name) // data
},
VariableDeclaration(path) {
console.log(path.node.kind) // let
}
}
}
}
1
npx babel app.js --plugins './babel-plugin.js'

바벨의 플러그인을 직접 만들어서 사용하는 일은 드물다. 이미 만들어진 플러그인을 사용하는게 좋다.


플러그인 사용하기

@babel/plugin-transform-block-scoping

let, const를 var로 변경해준다.

@babel/plugin-transform-arrow-functions

화살표 함수를 일반 함수로 변경해준다.

@babel/plugin-transform-strict-mode

자바스크립트에 strict-mode를 추가해준다.


설정파일 만들기

cli로 매번 바벨을 실행 시켜주는 것은 매우 불편한 일이다. 설정파일로 작성 해 놓으면 편하게 실행시킬수 있다. 바벨에서 설정 파일을 사용하기 위해서는 babel.config.js 파일을 만들면 된다.

1
2
3
4
5
6
7
8
9
// bable.config.js

module.exports = {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode"
]
}
1
npx babel 파일 경로

위 처럼 실행해도 플러그인이 적용되는 것을 확인 할수 있다.


프리셋

코딩시에 필요한 플러그인을 일일이 설정하는 일은 지나친 일이다. 코드 한줄 작성하더라도 세개의 플러그인을 셋팅해주어야 한다. 목적에 맞는 여러가지 플러그인을 모아놓은 것을 프리셋이라 한다. 한마디로 프리셋은 플러그인 모음이라고 할수 있다.


커스텀 프리셋

1
2
3
4
5
6
7
8
9
10
11
// preset.js

module.exports = function babelPreset() {
return {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode"
]
}
}
1
2
3
4
5
6
7
// babel.config.js

module.exports = {
presets: [
'./preset.js'
]
}

자주사용되는 프리셋

preset-env

ECMAScript2015+를 변환할때 사용한다. 바벨7 이전 버전에는 연도별로 각 프리셋을 제공했지만(babel-reset-es2015, babel-reset-es2016, babel-reset-es2017, babel-reset-latest) 지금은 preset-env로 통합되었다.

1
npm install -D @babel/preset-env
preset-typescript

타입스크립트를 변환하기 위한 프리셋이다.

@babel/preset- 으로 시작하는 모듈은 바벨의 프리셋이라고 보면 될것이다.


타겟브라우저

지원하는 브라우저의 버전을 명시할수 있다. 예를 들어 A라는 회사의 홈페이지는 크롬 최신버전만 지원한다. 이럴 경우 IE를 위한 변환은 불필요하다. 타켓브라우저를 사용해서 최적의 코드로 출력할수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
// babel.config.js

module.exports = {
presets: [
['@babel/preset-env', {
targets: {
chrome: 79,
ie: 11
}
}]
]
}

프리셋에 옵션을 줄 경우에는 배열로 넘겨준다. 배열의 첫번째 값으로는 프리셋 모듈을, 두번째 값으로는 옵션을 넘겨준다.

1
2
3
4
5
6
7
module.exports = {
presets: [
['preset명', {
// 옵션
}]
]
}

타겟브라우저는 chrome, opera, edge, firefox, safari, ie, ios, android, node, electron 들을 지원한다.


Comment and share

플러그인

로더가 파일단위로 처리하는 반면 플러그인은 번들된 결과물을 처리할때 사용한다. 번들된 자바스크립트를 난독화 하거나 특정 텍스트를 추출할하는 용도로도 사용이 가능하다.


커스텀 플러그인 만들기

로더는 함수 기반이었지만 플러그인은 클래스 기반으로 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// plugin.js

class MyPlugin {
apply(compiler) {
// 플러그인이 완료 된 후 실행될 콜백함수를 지정한다.
compiler.hooks.done.tap('My Plugin', stats => {
console.log('MyPlugin: done');
});

// 실제로 플러그인이 처리할 코드가 들어가는 부분이다.
compiler.plugin('emit', (compliation, callback) => {
// compliation.assets['main.js'].source();를 통해서 번들링 된 파일을 가져올수 있다.
const source = compliation.assets['main.js'].source();

// source 메소드를 재정의 하고 있다.
compliation.assets['main.js'].source = () => {
const banner = [
'/**',
' * 이것은 BannerPlugin이 처리한 결과입니다.',
' * Build Date: 2019-10-10',
' */'
].join('\n');
return banner + '\n\n' + source;
}

callback();
});
}
}

module.exports = MyPlugin;
1
2
3
4
5
6
7
8
9
10
// webpack.config.js

const MyPlugin = require('./plugin.js');

module.exports = {
// ...
plugins: [
new MyPlugin()
]
}

웹팩에 plugins 배열에 등록해준다. 직접 클래스를 생성후에 넣어준다.

플러그인 또한 로더처럼 커스텀 플러그인을 만들 일을 거의 없다.


자주사용되는 플러그인

BannerPlugin

결과물에 빌드 정보다 커밋 버전 등을 추가할때 주로 사용한다. 말그대로 배너를 추가할때 사용한다.
웹팩이 기본적으로 제공해주기 때문에 따로 설치할 필요가 없다.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

const webpack = require('webpack');

plugins: [
new webpack.BannerPlugin({
banner: `
Build Date: ${new Date().toLocaleString()}
Commit Version: ${childProcess.execSync('git config user.name')}
`
})
]

DefinePlugin

웹팩에서 제공하는 DefinePlugin은 모든 자바스크립트 코드에서 접근이 가능한 전역 변수를 선언하기 위해서 사용되는 플러그인이다. 사용방법은 webpack.DefinePlugin의 생성자에 전역 변수로 사용할 값을 객체 형태로 넘겨준다.

1
2
3
4
5
6
7
8
9
10
11
// webpack.config.js

const webpack = require('webpack');

plugins: [
new webpack.DefinePlugin({
TWO: '1+1',
TWO2: JSON.stringify('1+1'),
'api.domian': JSON.stringify('http://naver.com')
})
]

주의 할 점은 값이 문자열이 아니라 계산식이라는 것이다. 첫번째 값이 ‘1+1’은 문자열이 아니라 계산되어 2라는 값이 TWO에 저장된다. ‘1+1’이라는 문자열을 사용하고 싶으면 JSON.stringify를 사용하여야 한다.
키가 api.domian 이런식으로 등록하면 실제 사용할때는 객체 형식으로 접근이 가능하다.

1
2
3
4
5
// main.js

console.log(TWO); // 2
console.log(TWO2); // 1+1
console.log(api.domain); // http://naver.com

노드의 process 객체는 기본적으로 전역적으로 사용할수 있도록 등록이 되어 있다.

1
2
3
// main.js

console.log(process.env);

HtmlWebpackPlugin

서드파티로 제공하는 HtmlWebpackPlugin는 HTML 파일을 후처할때 사용한다. 이 플러그인을 사용하면 HTML도 빌드의 과정에 포함 시킬수 있다.

1
npm install -D html-webpack-plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// webpack.config.js

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

plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
templateParameters: {
'param': 'value'
},
minify: {
collapseWhitespace: true,
removeComments: true
}
})
]
  • template : index.html의 경로를 적어준다. 빌드 후에 output 경로에 index.html 생성이 된다. 번들링된 자바스크립트 경로도 자동으로 index.html에 추가되기때문에 수동으로 작성할 필요가 없어진다.
  • templateParameters: index.html에 파라미터를 전달할수 있다. <%= 파라미터명 %> 으로 값을 가져올수 있다.
    1
    2
    3
    <html>
    <%= param %>
    <html>
  • minify: 말그대로 코드를 경량화 하기 위한 옵션이다.
    • collapseWhitespace: value가 true로 설정 하면 모든 빈칸을 제거해준다.
    • removeComments: value가 true이면 모든 주석을 제거해준다.

CleanWebpackPlugin

서드파티로 제공하는 CleanWebpackPlugin는 빌드 이전 결과물을 모두 제거해 주는 플러그인다(output 폴더를 모두 삭제 후에 빌드가 된다). 해당 플러그인은 export시에 객체로 감싸기 때문에 { CleanWebpackPlugin }으로 사용해야 한다.

1
npm install -D clean-webpack-plugin
1
2
3
4
5
6
7
// webpack.config.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

plugins: [
new CleanWebpackPlugin()
]

MiniCssExtractPlugin

스타일 시트의 양이 많을때 자바스크립트 하나로 결과물을 모두 번들링 하게 되면 부담이 커진다. 번들 결과에서 스타일시트 코드만 뽑아서 별도의 css 파일로 관리 할수 있다. 최종 결과물이 css 파일하나, js 파일 하나가 된다(물론 그 이상도 만들수 있다).
개발 모드일때는 하나의 js로 하는게 작업에 좋다고 한다. 배포시에만 설정하도록 해보자.

1
npm install mini-css-extract-plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// webpack.config.js
const MiniCssExtracPlugin = require('mini-css-extract-plugin');
module: {
rules: [
{
test: /\.css$/,
use: [
process.env.NODE_ENV === 'development'
? MiniCssExtracPlugin.loader
: 'style-loader',
'css-loader'
]
}
]
},
plugins: [
...(
process.env.NODE_ENV === 'development'
? [new MiniCssExtracPlugin({ filename: '[name].css' })]
: []
)
]

위에 코드에서도 확인 할수 있듯이 MiniCssExtracPlugin를 사용하려면 loader 또한 MiniCssExtracPlugin.loader를 사용하여야 한다.

Comment and share

로더의 역할

로더(Loader)는 웹팩이 웹 애플리케이션을 해석할 때 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, Images, 폰트 등)들을 변환할 수 있도록 도와주는 속성이다. 로더를 사용하면 모든 파일을 모듈로 변환해 주기 때문에 자바스크립트에서 import 구문을 통해서 자바스크립트 영역에서 사용이 가능하다.

1. 모든 파일을 자바스크립트의 모듈처럼 만들어준다.
2. 타입스크립트 같은 다른 언어를 자바스킙트 문법으로 변환해 준다.
3. 이미지를 data URL 형식의 문자열로도 변환 가능하다.
4. CSS 파일을 자바스크립트에서 직접 로딩할수 있게 해준다.
결론 네가지의 역할 뿐 아니라 로더는 각 파일들을 처리 할때 사용할수있다.


커스텀 로더 만들기

로더는 함수 형식으로 만든다. 함수의 파라미터로 content를 받아온다. 이 content에는 파일의 content가 저장되어 있다. 그래서 로더는 파일의 내용을 변경하거나 처리할때에 사용할수 있다.

1
2
3
4
// loader.js
module.exports = function myWebpackLoader(content) {
return content.replace('console.log', 'alert');
}
1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
loader: './loader.js'
}
]
}
}

로더는 webpack.config.js의 module.rules 배열에 들어간다. 로더는 파일 유형별로 처리할 로더들을 등록 해준다.
하나의 로더에 처리할 파일들을 등록 하는게 아닌, 파일 하나(또는 그 이상)에 처리할 로더를 등록 해주는 것이기 때문에, 파일 유형별로 여러개의 로더가 등록 가능하다.

  • test : 로더를 적용할 파일 유형 (일반적으로 정규 표현식 사용)
  • loader : 로더를 하나만 등록 할때 이렇게 사용 하면 될거 같다.
  • use : 파일에 적용할 로더를 등록 (배열)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    module.exports = {
    // ...
    module: {
    rules: [
    {
    test: /\.js$/,
    use: [
    'style-loader',
    'css-loader',
    '로더 모듈 이름',
    '로더 경로' // './loader.js' 또는 path.resolve('./loader.js'),
    // 객체로도 등록 가능하다.
    {
    loader : '로더',
    options: {

    }
    }
    ]
    }
    ]
    }
    }
    use에 등록 된 로더는 뒤에서 부터 먼저 실행된다.
    style-loader는 css-loader가 등록 된 후에 사용가능하다. 그렇기 때문에 css-loader보다 앞쪽에 등록해야 한다.

실제로 커스텀로더를 사용할 일은 거의 없다. 로더가 어떤식으로 파일을 처리 하기 위해서 만들어본것이다.


자주사용되는 로더

css-loader

자바스크립트에서 CSS를 모듈로 변환해주는 역할을 한다.

1
npm install -D css-loader
1
2
3
4
5
6
7
8
9
10
11
// webpack.config.js

// ...
rules: [
{
test: '/\.css$/',
use: [
'css-loader'
]
}
]

이제 자바스크립트에서 css를 모듈로 가져올수 있다.

1
2
3
4
5
/* test.css */

body {
color: #111;
}
1
2
3
4
5
// test.js

import style from './test.css';

console.log(style); // 배열로 넘어온다.


style-loader

모듈로 등록된 css를 스타일로 적용시켜 주는 역할을 한다. 모듈로 등록된 css만 적용가능하기 때문에 css-loader가 존재하여야 한다.

1
npm install -D style-loader
1
2
3
4
5
6
7
8
// webpack.config.js

// 웹팩에 style-loader 등록시 순서에 유의!

use: [
'style-loader',
'css-loader'
]
1
2
3
4
// test.js

import './test.css'
import style from './test.css'; // 이렇게 해도 등록 해주지만 보통은 위에 처럼 작성한다.

브라우저에서 확인 해보면 head의 style 태그로 스타일이 적용된 것을 확인할수 있다.


file-loader

이름에서도 알수 있듯이 파일을 모듈화 해주는 로더이다. 보통은 이미지 파일이나 폰트 파일을 모듈로 변환할때 사용이된다.

1. 파일을 모듈화 해준다(주로 이미지에 사용)
2. 빌드 후에는 파일을 output 경로에 만들어준다.
3. publicPath 옵션을 통해서 실제 경로와 호출 경로를 일치 시켜주어야 한다.

1
npm install -D file-loader
1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js

rules: [
{
test: '/\.png$/',
loader: 'file-loader',
options: {
publicPath: './dist',
name: '[name].[ext]?[hash]'
}
}
]

file-loader는 options 속성을 사용가능하다.

  • publicPath : 파일을 사용할때 경로 앞에 붙는 문자열이다. 아래와 같은 경로에 있을때 publicPath를 설정하지 않으면 파일이 저장된 경로와 호출 경로가 달라 이미지를 불러오지못한다.
    1
    2
    3
    4
    index.html
    dist
    ├── components
    └── image.png
  • name : 번들링 된 이미지 파일을 저장할 때 사용될 파일이름이다. [name], [ext], [hash] 를 사용 가능하다.
    • [name] : 실제 이미지 파일 이름을 그대로 사용한다.
    • [ext] : 실제 확장자를 사용한다.
    • [hash] : 파일이름에 해쉬 값을 사용한다. 브라우저는 이름이 동일한 파일에 대해서 캐시를 저장하고 있어, 파일의 내용이 달라져도 캐시가 존재하면 새로운 파일이 로드되지 않는다. hash처럼 계속 값이 달라지면 캐시를 무력화하여 최신 파일을 로드 할수 있다.

자바스크립트에서 모듈화된 이미지를 불러 올때 아래와 같이 사용한다.

1
2
3
4
5
import image from './image.png';

document.querySelector('body').innerHTML = `
<img src='${image}'>
`

url-loader

사용하는 이미지 개수가 많다면 네트워크 리소스를 사용하는 부담이 생겨 사이트 성능에 영향을 준다. 한 페이지에 작은 이미지를 여러개 사용한다면 DATA URI Scheme을 이용하는 것이 좋은 방안이 될수 있다(네트워크 통신을 안하게 해준다). 이러한 처리를 해주는 것이 url-loader이다.

1
npm install -D url-loader
1
2
3
4
5
6
7
8
9
10
11
rules: [
{
test: '/\.png$/',
loader: 'file-loader',
options: {
publicPath: './dist',
filename: '[name].[ext]?[hash]',
limit: 20 * 1024
}
}
]

사용방법은 file-loader와 유사하다. url-loader는 limit라는 옵션을 사용가능한데 해당 파일의 크기 이하는 url-loader를 사용하고 그 이상은 file-loader를 사용하도록 해준다. 그래서 웹팩에 url-loader만 등록 하더라도 file-loader가 설치되어 있어야 한다(물론 limit 이하만 사용하는 경우에는 에러는 발생하지 않는다. limit 초과하는 파일이 존재할때 에러발생)

  • limit : url-loader를 사용할 파일 크기. limit을 너무 높게 잡아도 좋지 않다. (단위는 byte)

Comment and share

유의적 버전이란?

유의적 버전이란 버전을 통해서 API의 변경을 표현하는 명세이다. 버전은 X.Y.Z(주.부.수)로 표현한다.


유의적 버전을 사용하는 이유

시스템 규모가 커질수록 더 많은 API를 사용하게 된다. 이에 따라 소프트웨어의 의존성이 점점 더 높아지고 있다. 이러한 상황에서 API의 버전이 올라가는 경우 의존성이 깨지는지 확인을 해 주어야 하는데 직접 하나하나 확인을 하기에는 힘들다. 그래서 버전만 보고도 의존성이 깨지는지 아닌지를 판단 할수 있도록 하기 위해서 사용하게 되었다.


유의적 버전

버전은 3부분으로 나뉘어 진다.

  1. 주버전(MAJOR) : 기존 버전과 호환되지 않게 변경되는 경우
  2. 부버전(MINOR) : 기존 버전과 호환되면서 기능이 추가된 경우
  3. 수버전(PATCH) : 기존 버전과 호환되면서 버그를 수정한 경우

유의적 버전 사용 방법

  1. 유의적 버전을 쓰는 소프트웨어는 반드시 공개 API를 선언한다.
  2. 버전 번호는 반드시 X.Y.Z의 형태로 하고, X, Y, Z는 각각 자연수이고, 절대로 0이 앞에 붙어서는 안 된다. 그리고 반드시 증가하는 수여야 한다.
  3. 한번 배포가 되면 그 버전의 내용은 변경이 되어서는 안된다. 변경사항 있으면 새로운 버전으로 배포되어야 한다.
  4. 주부전이 0인 경우는 초기 개발을 위해서 사용한다. 아무때나 바뀔수 있다. 이 버전은 안전판으로 보지 않는게 좋다.
  5. 1.0.0 버전은 공개 API를 제공한다.
  6. 수(PATCH)버전은 반드시 그 이전 버전 API와 호환되는 버그 수정의 경우에만 버전을 올린다. 버그 수정이란 잘못된 내부기능을 수정하는 것을 말한다.
  7. 공개 API에 기존과 호환되는 기능을 추가하는 경우에는 반드시 부(MINOR)버전을 올린다.
  8. 공개 API에 기존과 호환되지 않는 기능을 추가하는 경우에는 주(MAJOR)버전을 올린다.
  9. 상위 버전에서는 하위 버전의 변화를 포함할수 있다.
  10. 상위 버전이 올라갈 경우 하위 버전은 0으로 초기화한다.
  11. 수 버전 뒤에 -나 .을 붙여서 pre-release 버전을 표기할수 있다.

자세한 사용방법은 https://semver.org/lang/ko/ 여기를 참고 하도록 하자.


FAQ

초기 개발 단계는 버전 관리는 어떻게 하는가

가장 간단한 방법은 최초개발을 0.1.0으로 하고, 이후 배포마다 부버전을 올린다.

언제 1.0.0을 배포해야 하는가

소프트웨어가 실서버에 사용하기 시작했다면 1.0.0이라고 할수 있다. 사용자들이 믿고 쓸수 있는 안정한 API 라면 1.0.0이라 할만하다.

아주 사소한 부분이 하위호환이 되지 않는 경우 매번 주버전을 올릴경우, 주버전이 매우 높아진다

의존하는 코드가 많은 소프트웨어에, 호환되지 않는 변화를 가볍게 도입해서는 안된다. 주버전을 업그레이드 하는데 필요한 비용이 어마어마하게 증가하게 된다. 바뀌는 부분에 대한 여파를 충분히 평가해야 한다.

실수로 부버전에 호환되지 않은 변경사항이 추가된 경우는 어떻게 하는가

이 경우 즉시 호환성이 깨진 부분을 수정하여 새로운 부버전을 배포한다. 이 경우라도 기존에 배포한 버전을 수정해서는 안된다.

Comment and share

npm 이란

npm이란 Node Package Manager의 약자이다. 이름에서 유추 할수 있는지 Node로 만들어진 모듈을 패키지화하여 관리하는 해주는 프로그램이다.


npm 명령어

package.json 생성

npm init 명령어를 실행하면 package.json 파일을 생성해준다. package.json 파일은 프로젝트 의존모듈 관리 및 프로젝트 정보를 관리한다. npm init 명령어를 입력하면 package.json에 들어갈 메타정보를 입력할수 있는 화면을 제공한다.

1
$ npm init

옵션으로 -y으로 설정 할 경우 메타 정보의 입력 없이 모두 default 값으로 package.json을 생성해 준다.

1
$ npm init -y

아래는 생성된 package.json의 내용이다.

1
2
3
4
5
6
7
8
9
10
11
{
"name": "sample", // 프로젝트 명
"version": "1.0.0", // 프로젝트 버전
"description": "", // 프로젝트 설명
"main": "index.js", // node.js에서 사용 되는 것
"scripts": { // 프로젝트를 자동화 할수 있는 쉘 스크립트 입력
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "", // 프로젝트 작성자
"license": "ISC" // 라이센스 정보
}

패키지 설치

npm install(npm i) 명령어는 package.json 파일의 dependency에 있는 모듈을 모두 설치해 준다.

1
2
$ npm install <package>
$ npm i <package>

npm install의 옵션으로 –save 사용한 경우 package.json의 dependency에 추가시켜 준다. 하지만 npm 버전 5 이상부터는 –save 옵션을 사용하지 않아도 자동으로 dependency에 추가해 준다.


–save-dev 옵션을 사용하는 경우에는 devDependency에 추가해준다. devDependency란 개발시에만 사용하는 개발용 의존 패키지를 작성할때 사용한다. 축양형은 -D를 사용한다.


그리고 패키지명 뒤에 @유의적버전을 붙여주면 해당 버전으로 설치할수 있다.

1
2
3
$ npm install --save <package>
$ npm install --save-dev <package>
$ npm install --save-dev package@유의적버전

패키지 삭제

1
2
3
4
# 로컬/개발 패키지 제거
$ npm uninstall <package-name>
# 전역 패키지 제거
$ npm uninstall -g <package-name>

패키지 업데이트

1
$ npm update <package-name>

스크립트 실행하기

package.json의 scripts에서 작성했었던 스크립트를 실행할수 있다. npm에서는 수많은 명령어를 제공해주는데 이것 이외에 스크립트를 통해서 추가한 명령을 실행할때 사용한다.

1
$ npm run <script-name>

패키지 설치 방법

1. CDN 방식(컨텐츠 전송 방식)

콘텐츠 전송 네트워크(이하 CDN)는 콘텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장하여 제공하는 시스템을 말한다. 인터넷 서비스 제공자에 직접 연결되어 데이터를 전송하므로, 콘텐츠 병목을 피할 수 있는 장점이 있다.
하지만 이 방식의 단점으로는 서버장애시 사용이 불가능 하다는 단점이 있다. 그리고 네트워크의 속도에 영향을 받기가 쉽다.


2. 직접 다운 받아 사용하기

해당 방법은 패키지를 제공하는 곳에서 직접 다운 받아서 사용하는 방법 이다. 이렇게 사용하면 서버의 장애와 무관하게 사용이 가능하다.
하지만 패키지가 업데이트 될 경우 직접 수동으로 다시 다운 받아서 관리를 해야하는 번걸움이 존재한다.


3. npm 이용하기

npm install 패키지명으로 손쉽게 설치가 가능하다. 직접 다운 받는 것보다 훨씐 편하게 버전 관리가 가능하다.


유의적 버전 범위

npm은 API의 버전을 관리 할때 유의적 범위를 사용한다. 아래의 버전 범위 표기법을 이용하여 업데이트 될 버전의 범위를 지정할수 있다.

표기법 설명
version 명시된 version과 일치
>version 명시된 version보다 높은 버전
>=version 명시된 version보다 높거나 같은 버전
<version 명시된 version보다 낮은 버전
<=version 명시된 version보다 낮거나 같은 버전
~version 패치버전을 변경하는 버전
^version 마이너버전을 변경하는 버전

여기에서 주목해야 할 것은 ~(틸드)와 ^(캐럿)이다.


~(틸드)

~(틸드)는 마이너 버전이 명시되어 있으면 패치버전을 업데이트 하고, 마이너 버전이 없으면 마이너 버전을 업데이트 한다.

  • ~1.1.0 : 1.1.0 <= version < 1.2.0
  • ~1.1.1 : 1.1.1 <= version < 1.2.0
  • ~1 : 1.0.0 <= version < 2.0.0

^(캐럿)

^(캐럿)은 마이너와 패치 버전 내에서 업데이트를 한다.

  • ~1.1.0 : 1.1.0 <= version < 2.0.0
  • ~1 : 1.0.0 <= version < 2.0.0

https://semver.npmjs.com 해당 사이트를 통해서 유의적 버전 범위에 따라서 어떤 버전을 사용하는지 대해서 알아볼수 있다.

위의 사이트를 통해 확인 결과 정식미만버전(0.y.z)의 경우에는 ~, ^ 모두 마이너버전까지 명시한경우 패치 버전만 업데이트를 한다고 나온다.

Comment and share

웹팩이란?

최근 자바스크립트에서는 모듈을 이용하여 주로 작성이 되고 있다. 번들러는 모듈의 의존 관계를 분석하여 브라우저가 인식할 수 있는 자바스크립트 코드로 변환 후 하나 또는 여러개의 파일로 만들어 주는 도구이다. 번들러를 통해서 소스코드를 모듈별로 작성 할수 있고 모듈간의 의존성을 쉽게 관리할수 있다. 자바스크립트에서 많이 사용되고 있는 모듈 번들러 중에 하나가 웹팩이다.
웹팩을 사용하면 자바스크립트 뿐 아니라 HTML, CSS, Image 파일의 리소스까지 의존성 관리를 할수 있게 해준다.


웹팩 설치

옵션을 -D(–save-dev) 옵션을 줄 경우에는 devDependencies에 들어가는데 이는 개발용 패키지이다.
webpack-cli를 설치하면 터미널에서 webpack 커맨드를 사용할수 있다.

1
npm install webpack webpack-cli -D

설치가 완료되면 아래와 같이 실행할수 있다.

1
2
node_modules/.bin/webpack --mode development
npx webpack --mode development

웹팩 옵션

웹팩을 사용할 때 여러가지 옵션을 줄수 있다.
아래는 - 하나로 보이지만 실제로는 -를 2개 써주어야 한다.


  1. –mode

    • development : 개발용으로 번들링된다.
    • production : 배포용으로 번들링된다.
    • none
  2. –entry
    모듈의 의존성 그래프를 만드는 시작점을 지정 해주는 것이다. 모든 모듈의 시작 파일을 지정해준다고 생각하면 쉽다.

  3. –output
    번들링된 파일의 위치와 이름을 지정할때 사용한다.

  4. –config
    config 파일의 위치를 지정할때 사용한다. 기본값으로는 webpack.config.js 또는 webpackfile.js이다.

  5. –progress
    웹팩이 빌드되는 것을 커맨드라인으로 볼수 있게 해준다.

1
npx webpack --mode development --entry ./src/app.js --output dist/main.js

설정 파일 webpack.config.js

매번 위처럼 cli 명령어를 통해서 작업하기는 힘들다. config 파일을 통해서 작성 후에 webpack만 실행하면 편하게 작업할수 있다.


설정 파일 만들기

프로젝트 폴더의 루트에서 webpack.config.js 파일을 생성한다(package.json 파일과 동일한 위치).

1
module.exports = {};

옵션 설정하기

옵션은 cli에서 사용했었던 옵션명을 사용한다.

mode

번들링할 모드를 설정한다.

development
  • 개발용모드이다.
  • 소스맵을 제공한다.
production
  • 배포용모드이다.
  • 코드를 압축, 난독화 한다.
  • mode를 설정 하지 않을 경우 production이 default이다.
1
2
3
module.exports = {
mode: '[development|production|none]',
}

entry

entry 옵션을 통해서 모듈의 시작점을 지정할수 있다.

1
2
3
module.exports = {
entry: './src/app.js'
}

entry를 여러개 사용 할 경우 객체 형태로 넘겨 주면 된다. 이때 key 값은 사용자가 커스텀하게 설정 가능하다.

1
2
3
4
5
6
module.exports = {
entry: {
main1: './src/main1.js',
main2: './src/main2.js'
}
}

output

output은 번들링 된 결과를 저장하기 위한 위치를 설정한다. 기본값은 './dist/main.js' 이다. 절대경로를 사용하는 것을 권장한다(path.resolve 사용).

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
}
}

저장할 파일이름 [name]는 동적으로 이름을 설정가능하다. entry에서 설정한 key를 filename의 이름으로 사용가능하다.
아래처럼 entry의 key를 설정 하지 않으면 default로 main이다.

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}

entry의 key가 app이기 때문에 번들링 된 파일은 ./dist/app.js로 만들어진다.

1
2
3
4
5
6
7
8
9
10
11
const path = require('path');

module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}

package.json에 스크립트 등록하기

package.json의 scripts 부분에 아래와 같이 추가한다. 보통 build로 설정하는 듯 하다. 빌드를 할때는 npm run build 명령어를 사용하면 된다.

1
2
3
4
5
6
{
// ...
scripts: {
'build': webpack
}
}

프로젝트에 기본적인 웹팩 설정하기 순서

1. npm init 명령어 실행

package.json 파일을 생성한다.

2. 웹팩 설치
1
npm i -D webpack webpack-cli
3. 환경설정 파일 생성하기

프로젝트의 루트 디렉토리에 webpack.config.js을 생성한다.

4. 웹팩 스크립트를 추가

package.json에 웹팩을 실행할 스크립트를 추가한다.

5. 웹팩 스크립트 실행

npm run build를 통해서 웹팩을 실행한다.

Comment and share

before, after 사용하기

Jest에서는 before와 after를 활용해서 테스트 전후에 실행할 코드를 작성 할 수 있습니다.


beforeEach와 afterEach

beforeEach는 테스트가 실행 되기전 afterEach는 테스트가 실행 된 후 실행이 됩니다. 단어 Each에서도 유추할수 있듯이 각각의 테스트가 실행되기 전후에 모두 실행이된다. 예를 들어 3개의 테스트가 있으면 beforeEach 또한 3번 호출된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
beforeEach(() => {
console.log('테스트 전 실행 됨');
});

afterEach(() => {
console.log('테스트 후 실행 돔');
})

for (; i < 6; i++) {
test(`테스트${i}`, ((i) => (() => {
console.log(`테스트${i}`);
}))(i));
}

비동기 사용하기

before, after에서도 비동기를 사용할수 있다. Jest 비동기를 통해서 Jest의 비동기 사용법을 알수 있다. 여기에서도 promise를 반환해주면 비동기를 사용할수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
function asyncFn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('비동기 사용');
resolve();
}, 500);
});
}

beforeEach(() => {
return asyncFn();
});

async, await를 통해서도 비동기를 사용할수 있다.

1
2
3
beforeEach(async() => {
await asyncFn();
})

오직 한번만 실행 하기

before와 after를 테스트 파일에서 오직 한번 만 사용하기 위해서는 beforeAllafterAll를 사용하면 된다.

1
2
3
4
5
6
7
beforeAll(() => {
console.log('테스트 전 오직 한번만 실행된다.');
});

afterAll(() => {
console.log('테스트 후 한번만 호출된다.');
});

describe 내부에서의 before, after 사용하기

describe 내부에서 before와 after를 사용하는 경우에는 describe 내부의 테스트가 호출 될 경우에만 호출된다. 하지만 describe 외부의 before와 after는 describe 내부의 test를 포함한 테스트 파일 전체의 test가 호출 될때 호출이 된다.


test.only 사용하기

테스트가 실패할때 가장 먼저 확인 해봐야 할것은 테스트 케이스가 오직 실패하는 케이스만 실행 했을때에도 실패하는지 이다.
이를 위해서 test.only를 사용하면 다른 테스트는 스킵하고 해당 테스트만 실행된다. 이때 스킵되는 테스트는 test.only가 존재하는 테스트 파일에 한해서이다.

1
2
3
test.only('', () => {

});

Comment and share

비동기에서 사용할 함수

1
2
3
4
5
function fetchData(check) {
return new Promise((resolve, reject) => {
typeof check === 'function' ? check() : check ? resolve('peanut butter') : reject('error');
});
}

콜백을 이용한 비동기 테스트 해보기

일반적인 상황에서 비동기를 사용하는 보편적인 방법 중 하나는 콜백을 사용하는 것이다. 하지만 Jest에서 테스트 할때는 의도대로 작동 하지 않는다.
Jest 테스트는 콜백을 호출할때까지 기다려주지 않는다. 테스트 함수 내에서 마지막 코드까지 실행을 하게되면 테스트를 완료하게 된다. 그래서 항상 테스트가 통과하게 된다. 테스트 실패하도록 값을 변경하여도 통과하는 것을 확인 할수있다.

1
2
3
4
5
6
7
test('비동기를 콜백 활용하기 - 실패', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}

fetchData(callback);
});

파라미터 done을 활용한 비동기 테스트

done을 파라미터로 받은 경우에는 Jest에서는 done 함수가 호출 될때까지 테스트를 완료하지 않고 기다린다.

1
2
3
4
5
6
7
8
9
10
11
12
test('done을 활용하기', (done) => {
function callback(data) {
try {
expect(data).toBe('peanut butter');
done();
} catch (e) {
done(error);
}
}

fetchData(callback);
});

Promise 활용 하기

비동기로 promise를 사용하는 경우에는 훨씐 간편하게 작성 할수 있다. promise를 리턴해주면 테스트가 정상적으로 작동하는 것을 확인할수 있다. 이때 promise를 리턴하지 않으면 테스트를 수행하기 전에 끝난다.

1
2
3
4
5
test('프로미스 활용하기', () => {
return fetchData(true).then((data) => {
expect(data).toBe('peanut butter');
});
})

Promise Catch 테스트 하기

promise의 reject 또한 테스트 할수 있다. 이경우에는 then 대신에 catch를 활용한다. catch 내부에서 테스트 할 코드를 작성한다. 이때 expect.assertions(에러 발생한 횟수)를 테스트 위에 적어주지 않으면 테스트는 정상적으로 실행되지 못하고 완료되어 버린다.

1
2
3
4
test('프로미스 catch 테스트하기', () => {
expect.assertions(1);
return fetchData(false).catch((e) => expect(e).toMatch('error'));
})

Matchers인 resolves를 활용하기

promise then 대신에 Jest에서 제공하는 Matchers 중에 하나인 resolves를 활용할수도 있다. expect에 promise를 넘겨준다. 이때 여전히 return을 사용하여야 한다.

1
2
3
test('resolves 사용하기', () => {
return expect(fetchData(true)).resolves.toBe('peanut butter');
});

Matchers인 rejects를 활용하기

promise catch 대신에 Jest에서 제공하는 Matchers 중에 하나인 rejects를 활용 할수도 있다. 사용 방법은 resolves와 유사하다. 이때 promise catch와 async와는 달리 expect.assertions를 사용하지 않아도 된다.

1
2
3
test('rejects 활용하기', () => {
return expect(fetchData(false)).rejects.toBe('error');
});

async와 await를 활용하기

es6에서 제공하는 비동기 방법인 async와 await를 활용해서도 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
test('async와 await를 활용하기', async() => {
let data = await fetchData(true);
expect(data).toBe('peanut butter');
});

test('async와 await를 활용하기', async() => {
expect.assertions(1);

try {
await fetchData(false);
} catch (e) {
expect(e).toMatch('error');
}
});

Comment and share

전개 연산자

전개 연산자는 ES6에서 추가된 문법이다. 사용 방법은 ...값으로 사용이다. 이 문법을 사용하면, 객체 혹은 배열을 펼칠수있다. 설명만으로는 제대로 이해가 가지 않기 때문에 아래의 예제를 기억하도록 하자.


배열에서 전개연산자 사용

1
2
3
4
5
6
const arr = [1, 2, 3];
console.log(...arr); // 1, 2, 3

const arr2 = [4, 5, 6];

arr.push(...arr2;)

위의 예제에서 arr3를 만들려면 기존에는 아래와 같은 코드를 사용해야 했다.

1
2
3
4
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];

Array.prototype.push.apply(arr, arr2);

함수의 매개변수로도 전달이 가능하다

1
2
3
4
5
6
7
const arr = [1, 2, 3];

function fn(i, j , k) {
console.log(i, j, k);
}

fn(...arr);

객체에서 사용하기

전개연산자를 통해서 아와 같이 새로운 객체를 생성할수 있다.이를 사용하면JSON.parse(JSON.stringify()))를 더이상 사용할 필요가 없어보인다.

1
2
3
4
const obj = { name : 'Jone', age : 24 };
const obj2 = {...obj};

console.log(obj2);

아래와 같인 특정 key의 값만 가져올수가 있다. 이때 key가 정확히 일치 할때만 가져올수 있다.

1
2
3
4
const obj = { name : 'Jone', age : 24 };
const { name } = obj;

console.log(name);

아래와 같이 작성 하면 Object.assign을 대체 할수 있다. 중복된 key를 가지고 있으면 뒤에 오는 객체의 value로 덮어 씌워진다.

1
2
3
4
5
const obj = { name : 'Jone', age : 24};
const obj2 = { name : 'Jane' };
const obj3 = { school : 'high school', born : 'SEOUL' };

const obj4 = {...obj, ...obj2, ...obj3, born : 'ULSAN' };

나머지 연산자

전개 연산자와 나머지 연산자는 다른 연산자 이지만 모양이 동일하다. 나머지 연산자는 앞에 오는 변수에 값을 할당 한 뒤 남은 모든 값을 하나의 배열로 변수에 저장해준다.
나머지 연산자가 중간에 올수 없으며, 나머지 연산자는 항상 하나만 사용이 가능하다.

1
2
3
4
const [head, ...tail] = [1, 2, 3];

console.log(head); // 1
console.log(tail); // [2, 3]

함수의 파라미터로도 사용이 가능하다.

1
2
3
4
5
6
7
function fn(a, b, ...c) {
console.log(a)
console.log(b)
console.log(c)
}

fn(1, 2, 3, 4, 5, 6);

Comment and share

Moon Star

author.bio


author.job