2 웹팩
source: categories/study/vue-principle/vue-principle_2.md
2.1 웹팩 사용하기
- script 가 많아졌을 때 웹팩 배우는 어려움보다 script 파일 관리하는 어려움이 더 커졌을 때, 그때 웹팩의 필요성이 대두가 된다.
- 웹팩을 사용하기 위해선 node 설치
- node 설치하면 npm 설치된다.
- npm 다른 사람이 만든 코드를 가져다 쓸 수 있게 해놓은 것
- 요즘은 타인이 만들어놓은 스크립트 코드를 가지고와 퍼즐 맞추듯이 코딩하는 시대
- 그거를 도와주는 것이 npm
- vue도 남이 만들어놓은 스크립트 코드임
npm init
- package.json 생성
- 내가 쓰는 남의 소스 코드들을 package.json 에 정리
- 각각의 소스 코드마다 버전이 존재
- 나중에 실무를 하다보면 남이 만들어놓은 코드가 몇천개까지 될 수 있다.
- 이렇게 많은 스크립트 코드들 버전관리하기 매우 어려움
- 그래서 그 버전을 정확하게 기억하기 위해서 package.json 을 만든다.
npm i vue
- 위 명령어를 통해 vue 설치
- 이전까지 script 로 직접 파일을 가져왔지만, 이제 더 이상 script 로 불러오지 않을 것이다.
- 지금부턴 이렇게 npm 으로 패키지 관리를 할 것이다.
npm i webpack webpack-cli -D
- webpack, webpack-cli 설치
-D
(개발할 때만 webpack, webpack-cli를 사용하겠다 라는 뜻) - 위에서 설치한 vue 는
dependencies
에 기록- vue 는 배포할 때(사용자에게)도 필요한 라이브러리
- webpack, webpack-cli 는
devDependencies
에 기록- webpack, webpack-cli 는 개발할 때만 필요한 라이브러리이다.
# webpack.config.js 파일 생성
- 참고로
npm i
를 하면node_modules
폴더와package-lock.json
파일이 생긴다.node_modules
폴더엔 우리가 설치한vue
,webpack
,webpack-cli
뿐만 아니라 해당 라이브러리들이 의존하고 있는 라이브러리들도 모두 설치된다.- 그래서 실제
node
를 사용하는 프로젝트를 하다보면node_modules
에 설치되는 남이 만든 코드들이 수백개, 수천개가 된다. - 이런 것들을 효율적으로 관리하기 위해서
package.json
이 존재
webpack.config.js 에 webpack 관련 설정 작성
// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
entry: {
// app <- 하나로 합쳐질 파일의 이름 app.js
app: './main.js',
},
module: { // webpack의 핵심
rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
{},
],
},
plugins: [],
output: {
filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
path: './dist', // output 경로
},
}
- 기본적인 웹팩 구조는 위와 같다.
- 위와 같은 형태에서 부수적인 설정들이 많아 복잡해지는 것이다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="/dist/app.js"></script>
</body>
</html>
- 위에처럼 html 파일엔 웹팩 결과물을 연결해주기만 하면 끝이다.
2.2 프로젝트 구조와 웹팩 빌드
main.js
// main.js
import Vue from 'vue'; // ES6 module 문법
new Vue().$mount('#root'); // 이전에 el 속성에 vue가 통제할 태그를 적었었다. 그거와 같은 역할을 한다.
NumberBaseball.vue (확장자는 vue이지만 javascript 파일이라고 생각하면됨)
- 아래 내용을 보시면
html
이라고 생각드실 수도 있음 - 특수한 문법을 사용하는 자바스크립트 파일이라고 생각하시면됨
- 쉽게 작성할 수 있도록 문법적으로 도움을 준 것이지 실제론 자바스크립트 파일이라고 생각하면됨
<template>
<div>
<h1>{{ result }}</h1>
<form @submit="onSubmitForm">
<input type="text" maxlength="4" v-model="value" ref="answer">
<button type="submit">입력</button>
</form>
<div>시도: {{ }}</div>
</div>
</template>
<script>
export default {
name: 'NumberBaseball',
data() {
return {
value: '',
result: '',
}
},
methods: {
onSubmitForm(e) {
e.preventDefault();
}
}
}
</script>
<style>
</style>
NumberBaseball.vue
와main.js
를 합쳐서dist/app.js
파일로 내보내야지dist/app.js
파일을 불러오는html
파일이 완성된다.
{
"name": "number-baseball",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "2.6.10"
},
"devDependencies": {
"webpack": "4.35.2",
"webpack-cli": "3.3.5"
}
}
npm run build
# 지금은 build 에러가 날 것이다.
# 이 에러들을 하나하나 해결해나가면 웹팩이 정상적으로 작동할 것이다.
# 에러를 해결해보자.
- Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema - configuration.output.path: The provided value "./dist" is not an absolute path!
- The output directory as absolute path (required).
위 에러를 해결하기 위해 절대 경로를 설정해준다.
절대 경로를 설정할 때 C:W//~~
이런식으로 설정하면 너무 복잡해진다.
이를 쉽게 설정할 수 있도록 node
에서 제공하는 path
라이브러리를 사용하면 된다.
const path = require('path');
// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
entry: {
// app <- 하나로 합쳐질 파일의 이름 app.js
app: path.join(__dirname, 'main.js'),
},
module: { // webpack의 핵심
rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
{},
],
},
plugins: [],
output: {
filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
// 아래와 같이 path 라이브러리를 사용해서 경로를 명시해줘야 절대 경로로 설정된다.
path: path.join(__dirname, 'dist'), // output 경로
},
}
2.3 웹팩 로더 사용하기
main.js
import Vue from 'vue';
import NumberBaseball from './NumberBaseball.vue';
new Vue().$mount('#root');
- 위와 같이
main.js
에NumberBaseball.vue
파일을import
해야 두 파일이 합쳐져서app.js
파일로 나온다. main.js
파일이 가장 중요한 파일이다.
- Module parse failed: Unexpected token
- You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file See https://webpack~~
- webpack 입장
- entry 에 설정되어있는 파일로 부터 시작
- main.js 맨 위에 Vue 를 불러옴 <- 이거부터 처리
- 그 다음에 NumberBaseball.vue 불러오라그러네? 이거 불러오자.
- 불러와서 읽을라고했더니 못읽음. 너 자바스크립트 아니잖아!?
- 웹팩은 자바스크립트만 합쳐준다고 했잖아? 그런데 NumberBaseball.vue 는 자바스크립트 파일이 아니다.
- 그래서 이를 해결해주는게 바로
rules
webpack.config.js
const path = require('path');
// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
entry: {
// app <- 하나로 합쳐질 파일의 이름 app.js
app: path.join(__dirname, 'main.js'),
},
module: { // webpack의 핵심
rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
plugins: [],
output: {
filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
// 아래와 같이 path 라이브러리를 사용해서 경로를 명시해줘야 절대 경로로 설정된다.
path: path.join(__dirname, 'dist'), // output 경로
},
}
npm i vue-loader -D
npm run build
- 이렇게 하면
.vue
파일은 웹팩이 처리하는 것이 아니라vue-loader
가 처리한다. - 웹팩은 기본적으로 자바스크립트 파일만 처리
- [vue-loader] vue-template-compiler must be installed as a peer dependency, or a compatible compiler implementation must be passed via options.
- vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');
// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
mode: 'development', // 개발중일 땐 development, 배포할 땐 production
devtool: 'eval', // 개발할 땐 eval, 배포할 땐 hidden-source-map
// eval로 하면 웹팩 빌드 속도가 빨라진다.
// 이 모드별로 app.js에 나오는 내용이 달라진다.
resolve: { // 확장자 처리 가능
extensions: ['.js', '.vue'], // 해당 확장자들은 import 할 때 생략 가능
},
entry: {
// app <- 하나로 합쳐질 파일의 이름 app.js
app: path.join(__dirname, 'main.js'),
},
module: { // webpack의 핵심
rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
{
test: /\.vue$/,
loader: 'vue-loader', // use: 'vue-loader'라고 작성해도된다.
},
],
},
plugins: [ // module 처리 이후 output 으로 내보내기 전에 한번 더 가공해주는 역할
new VueLoaderPlugin(),
],
output: {
filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
// 아래와 같이 path 라이브러리를 사용해서 경로를 명시해줘야 절대 경로로 설정된다.
path: path.join(__dirname, 'dist'), // output 경로
},
}
node
환경에서는require
문법을 사용 (웹팩에선require
)vue
환경에서는es6 module
문법 사용
npm i vue-template-compiler -D
vue
,vue-template-compiler
는 서로 버전이 일치해야된다.
main.js 위에서 실수한점
import Vue from 'vue';
import NumberBaseball from './NumberBaseball.vue';
// 아래와 같이 NumberBaseball을 넣어줘야한다.
new Vue(NumberBaseball).$mount('#root');
NumberBaseball.html
/dist/app.js
로 입력하니깐 절대경로로 잡히는 듯,app.js
파일을 불러오지 못함./dist/app.js
상대경로로 수정해주자.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
</html>
2.4 v-for 반복문 사용하기
<template>
<div>
<h1>{{ result }}</h1>
<form @submit.prevent="onSubmitForm">
<input type="text" maxlength="4" minlength="4" v-model="value" ref="answer">
<button type="submit">입력</button>
</form>
<div>시도: {{ tries.length }}</div>
<ul>
<li v-for="t in tries">
<div>{{ t.try }}</div>
<div>{{ t.result }}</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'NumberBaseball',
data() {
return {
tries: [],
value: '',
result: '',
}
},
methods: {
onSubmitForm() {
this.tries.push({
try: this.value,
result: '홈런',
});
this.value = '';
this.$refs.answer.focus();
}
}
}
</script>
<style>
</style>
- 코드 수정하고나서 매번 다시 빌드해야되는 것도 귀찮
- 나중에 빌드도 자동으로 다시 되도록 하는 방법 알려줌
- 처음부터 안한 이유는 이 불편함 간접체험 할 수 있도록하기 위해..
2.5 숫자야구 완성하기
2.5.1 어떤게 데이터이고 어떤게 그냥 변수일까요?
- 화면에 보여지는 것들은 데이터
- 계산식에서 쓰이는 화면과는 관련없는 애들은 그냥 변수로 선언하면됨
- 더 정확하게하려면 인풋에 중복 숫자까지 입력 방지
<template>
<div>
<h1>{{ result }}</h1>
<form @submit.prevent="onSubmitForm">
<input type="text" maxlength="4" minlength="4" v-model="value" ref="answer">
<button type="submit">입력</button>
</form>
<div>시도: {{ tries.length }}</div>
<ul>
<li v-for="t in tries">
<div>{{ t.try }}</div>
<div>{{ t.result }}</div>
</li>
</ul>
</div>
</template>
<script>
// getNumbers 함수 - methods 로 구현해도 됨
// 그런데 getNumbers 는 화면이랑 크게 관련이 없음, 그래서 굳이 methods에 같이 엮지 않는다.
// data, methods에는 현재 컴포넌트, 화면과 밀접한 연관이 있는 애들만 묶어주시면 된다.
const getNumbers = () => {
const candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const array = [];
for (let i = 0; i < 4; i++) {
const chosen = candidates.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
array.push(chosen);
}
return array;
}
export default {
name: 'NumberBaseball',
data() {
return {
answer: getNumbers(), // ex) [1, 5, 3, 4]
tries: [], // 시도 내용 및 시도 횟수를 알아내기 위한 배열
value: '', // 입력
result: '', // 결과
}
},
methods: {
onSubmitForm() {
// this.value는 문자열
// this.answer은 배열
if (this.value === this.answer.join('')) {
this.tries.push({
try: this.value,
result: '홈런'
})
this.result = '홈런';
alert('게임을 다시 실행합니다.');
this.value = '';
this.answer = getNumbers();
this.tries = [];
this.$refs.answer.focus();
} else { // 정답 틀렸을 때
if (this.tries.length >= 9) { // 10번째일 때
this.result = `10번 넘게 돌려서 실패! 답은! ${this.answer.join(',')}였습니다!`;
alert('게임을 다시 시작합니다.');
this.value = '';
this.answer = getNumbers();
this.tries = [];
this.$refs.answer.focus();
}
let strike = 0;
let ball = 0;
const answerArray = this.value.split('').map(v => parseInt(v));
for (let i = 0; i < 4; i++) {
if (answerArray[i] === this.answer[i]) { // 숫자 자릿수 모두 정답
strike++;
} else if (this.answer.includes(answerArray[i])) { // 숫자만 정답
ball++;
}
}
this.tries.push({
try: this.value,
result: `${strike} 스트라이크, ${ball} 볼입니다.`
})
this.value = '';
this.$refs.answer.focus();
}
}
}
}
</script>
<style>
</style>
2.5.2 import, export VS. require, module.exports
- 노드의 모듈 시스템
require
,module.exports
- 자바스크립트 모듈 시스템
import
,export