초기 설정
- 웹팩을 쓰기 위해서는 먼저 node를 깔아야한다.
- node를 설치하면 npm이 같이 따라온다.
- npm은 다른 사람이 작성해둔 javascript 코드를 끌어다 쓸 수 있다.
- 결국 Vue도 남이 만들어둔 소스라고 볼 수 있다.
- node를 제대로 설치하면 다음과 같이 버전 확인이 가능하다.
- node -v
- npm -v
- npm init
- 터미널로 들어와서 npm init 입력하고 나면 다음과 같이 버전 및 여러 가지가 나오는데 package name만 다음과 같이 입력하고 나머지는 모두 넘긴다.
- 입력하고나면 다음과 같이 json 파일이 하나 생성된다.
- 내가 쓸 수 있는 다른 사람의 소스 코드가 정리된 파일이다.
- 정리하는 이유: 각 소스코드마다 버전이 있다. 내가 어떤 버전을 쓰고 있는지 버전관리하고 몇 버전인지 파악하기 위한 package.json을 만든다.
- npm install vue
- (npm install vue)
- Vue를 설치한다.
- 더이상 <script>를 사용하지 않을 예정이다.
- 앞으로 npm으로 소스 코드를 관리하자.
- npm i webpack webpack -cli -D
- 웹팩 설치
- webpack 과 webpack cli 두 패키지를 설치하는데 옵션으로 "-D"를 선택했다. (D는 개발할 때만 쓰겠다는 설정이다.)
- package.json 파일
- main: 숫자 야구 프로젝트에서 가장 중요한 파일명을 넣어준다.
- 앞에서 "-D" 옵션을 줬기 때문에 devDependencies{}에 웹팩 내용이 추가됐다.
- vue는 개발, 배포 시 둘다 사용되지만 웹팩, 웹팩 cli는 개발을 위한 보조 도구이다.
<hide/>
{
"name": "number-baseball",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^3.2.47"
},
"devDependencies": {
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1"
}
}
웹팩 사용하기
웹팩 관련 핵심 옵션
- entry: 웹팩을 이용하면 여러 스크립를 관리하는데 가장 대표가 되는 파일을 entry에 넣어준다.
- module: 웹 팩의 핵심이다. rules에서 합칠 때 어떻게 합칠 것인지, 처리할 건지 정해준다.
- plugins: 후처리하는 부분
- module이 먼저 처리하다가 최종 처리
- output 나오기 직전에 plugins이 적용되서 한번 더 가공된다.
- output
- 가장 먼저 entry 부분부터 처리하다가 희한한? 부분을 만나면 module의 rules가 처리한다. s
- exports에 넣는 옵션
- mode: 개발 중인 경우는 "development" , 배포시에는 mode: "production"을 넣어준다.
- devtools: 개발 중인 경우는 'eval' , 배포시에는 devtools: 'hidden-source-map'
- (eval로 설정하면 빌드 속도가 빠르다. )
- resolve: {extensions: ['.js', '.vue']} .. 확장자 를 처리한다.
- app.js 파일을 보면
- import NumberBaseball from './NumberBaseball.vue' 가 있는데 뒷부분의 확장자인".vue"를 생략할 수 있다. 없어도 자동으로 .vue 파일이라고 인식한다.
Ex)
- webpack.config.js 파일을 만든다.
- module.exports= {} : 노드에 모듈을 만들어준다.
- 웹팩이 웹팩 처리할 때(웹 패킹) 방금 만든 모듈로 만든 객체가 사용된다.
- entry 에 넣으려는 main.js 파일을 만든다.
- entry 안에 있는 app 은 나중에 하나로 합쳐줄 파일의 이름이다.
- main.js 외 여러 개의 script가 app.js로 합쳐질 예정이다.
- dist 라는 폴더가 생기고 그 안에 app.js라는 최종 결과물이 생길 것이다.
- 그러고나서 html 파일에 <script src = "/dist/app.js"></script> ... 이런 식으로 가져올 수 있다. 뷰 가져오는 소스 경로 같은 부분도 필요없다.
- plugins:
- output:
- webpack.config.js
<hide/>
module.exports = {
entry: {
app: './main.js'
},
module: {
},
plugins: [],
output: {
filename: 'app.js',
path: './dist'
},
};
- 복잡한 스크립트 코드가 모두 app.js 로 합쳐지기 때문에 html 파일이 다음과 같이 간단해진다.
<hide/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>숫자 야구</title>
</head>
<body>
<div id="root"></div>
<script src="/dist/app.js"></script>
</body>
</html>
프로젝트 구조와 웹팩 빌드
- main.js
- import 문: package.json에서 설치한 뷰를 가져오고 있다. => new Vue() 를 사용 가능하다.
- mount는 "el" 과 같은 역할을 한다. (el에 Vue가 통제하도록 div 태그의 이름을 넣어줬었다. )
- Vue 인스턴스로 만든다.
- 기존에 만들었던 Vue 컴포넌트가 NumberBaseball.vue가 된다.
import Vue from 'vue';
new Vue().$mount('#root');
- NumberBaseball.vue
- 확장자는 뷰지만 javascript 라고 생각하면 된다.
- 특수한 문법을 쓰는 javascript
- template: 기존에 만든 Vue 인스턴스의 template 영역에 해당한다.
- <script> 안에 export default:
- main.js 와 NumberBaseball.vue 를 모두 합쳐서 dist/app.js로 만들어줘야 NumberBaseball.html 파일이 완성된다.
- NumberBaseball.html
<hide/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>숫자 야구</title>
</head>
<body>
<div id="root"></div>
<script src="/dist/app.js"></script>
</body>
</html>
- 기존의 package.json 파일의 부분을 다음과 같이 수정한다.
- scripts의 test => build
<hide/>
{
"name": "number-baseball",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^3.2.47"
},
"devDependencies": {
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1"
}
}
- npm run "스크립트명" 을 터미널에 입력하면 웹팩이 실행된다.
- 웹팩을 돌리는 명령어가 바로 webpack이다.
- npm run build 입력
- 다음과 같이 에러가 난다.
- dist 폴더의 절대 경로가 잘못되었기 때문이다.
- 하지만 절대 경로를 직접 적기 보다는 노드에서 절대경로 입력 도와주는 기능을 이용하는 게 훨씬 편리하다.
- 다음과 같이
- path: 노드가 만든 스트립트를 의미한다.
- __dirname: 현재 폴더 경로에 접근 가능하다. (현재 폴더: ch03)
- join(): path안에 있는 메서드로 어느 경로로 들어갈건지 경로를 넣어준다.
- path: path.join(__dirname, 'dist'): 경로를 합쳐준다. 괄호 안에 폴더, 파일명을 차례로 넣는다.
- 아래와 같이 두 곳에 적용할 수 있다.
<hide/>
const path = require('path');
module.exports = {
entry: {
app: path.join(__dirname, 'main.js')
},
module: {
},
plugins: [],
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
},
};
웹팩 로더 사용하기
- npm run build 입력
- NumberBaseball.vue와 main.js를 연결시키려한다.
- 웹팩은 자바스크립트만 합쳐준다.
- 그런데 NumberBaseball.vue는 자바스크립트가 아니라서 문제가 생긴다.
- 그래서 webpack.config.js에 rules가 필요하다.
- 정규 표현식: 파일명이 ".vue"로 끝나는($) 파일에 대해 vue-loader를 사용하겠다.
- npm i vue-loader 를 입력해서 다운받는다.
- 오류
- package.json 파일에 다음과 같이 vue-loader 가 생기는 게 정상이다.
- 앞으로 vue 파일은 웹팩이 아닌 vue-loader가 처리한다.
{
"name": "number-baseball",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"vue": "^3.2.47"
},
"devDependencies": {
"vue-loader": "^15.7.0",
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1"
}
}
- 지금 이상태로 npm run build 해보면 에러가 난다.
- config 파일에 다음 내용을 추가한다.
- VueLoaderPlugin()
- VueLoaderPlugin()
- main.js
- Node 환경에서는 require, Vue 환경에서는 import
- 웹팩은 노드 환경이므로 require를 쓴다.
<hide/>
import Vue from 'vue';
import NumberBaseball from './NumberBaseball.vue'
new Vue().$mount('#root');
- npm i vue-template-compiler -D
- cf) Vue와 vue-template-compiler 의 버전이 항상 일치해야한다.
- 특정 버전으로 깔기 위해서는 다음과 같이 @ 뒤에 버전을 입력한다.
- ex)npm i vue@2.7.0
- @를 쓰지 않으면 항상 최신 버전으로 깔린다.
<hide/>
npm ERR! Missing script: "build"
npm ERR!
npm ERR! To see a list of scripts, run:
npm ERR! npm run
<hide/>
S C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03> npm run build
Debugger attached.
> number-baseball@1.0.0 build
> webpack
Debugger attached.
[webpack-cli] Failed to load 'C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\webpack.config.js' config
[webpack-cli] Error: Cannot find module 'webpack/lib/RuleSet'
Require stack:
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\vue-loader\lib\plugin.js
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\webpack.config.js
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\webpack-cli\lib\webpack-cli.js
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\webpack-cli\lib\bootstrap.js
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\webpack-cli\bin\cli.js
- C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\webpack\bin\webpack.js
at Module._resolveFilename (node:internal/modules/cjs/loader:1075:15)
at Module._load (node:internal/modules/cjs/loader:920:27)
at Module.require (node:internal/modules/cjs/loader:1141:19)
at require (node:internal/modules/cjs/helpers:110:18)
at Object.<anonymous> (C:\Users\lan42\OneDrive\바탕 화면\vue\web_game\ch03\node_modules\vue-loader\lib\plugin.js:2:17)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Module._load (node:internal/modules/cjs/loader:958:12)
at Module.require (node:internal/modules/cjs/loader:1141:19) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\node_modules\\vue-loader\\lib\\plugin.js',
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\webpack.config.js',
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\node_modules\\webpack-cli\\lib\\webpack-cli.js',
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\node_modules\\webpack-cli\\lib\\bootstrap.js',
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\node_modules\\webpack-cli\\bin\\cli.js',
'C:\\Users\\lan42\\OneDrive\\바탕 화면\\vue\\web_game\\ch03\\node_modules\\webpack\\bin\\webpack.js'
]
}
Waiting for the debugger to disconnect...
Waiting for the debugger to disconnect.
- 오류: build 관련 스크립트가 없다
- 원인: 빌드 스크립트를 넣었는데도 이런 오류가 난다.
- 해결
- 1) vue loader 관련 모듈을 다시 설치한다.
- 2) webpack.config 파일에서 const.. 부분을 수정한다.
<hide/>
npm install vue-loader vue-template-compiler --save-dev
- webpack
- 주석 처리한 부분을 기존 코드인데 뒷 부분의 lib으로 시작되는 부분을 지우니까 해결됐다.
<hide/>
// const {VueLoaderPlugin} = require('vue-loader/lib/plugin');
const {VueLoaderPlugin} = require('vue-loader');
const path = require('path');
module.exports = {
entry: {
app: path.join(__dirname, 'main.js')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new VueLoaderPlugin(),
],
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
},
};
- 빌드 성공 화면
- 성공하고 나면 dist 폴더에 app.js라는 파일이 생긴다.
- webpack.config 파일에 extensions에 ['.js', '.vue'] 를 설정한다.
<hide/>
// const {VueLoaderPlugin} = require('vue-loader/lib/plugin');
const {VueLoaderPlugin} = require('vue-loader');
const path = require('path');
module.exports = {
mode: "development",
devtool: 'eval',
resolve: {
extensions: ['.js', '.vue']
},
entry: {
app: path.join(__dirname, 'main.js')
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new VueLoaderPlugin(),
],
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
},
};
- main 파일을 다음과 같이 바꿀 수 있다.
- 확장자 ".vue"를 없애거 Vue () 안에 매개변수를 넣었다.
<hide/>
import Vue from 'vue';
import NumberBaseball from './NumberBaseball'
new Vue(NumberBaseball).$mount('#root');
- 새롭게 빌드한다. npm run build
- 빌드해주고 나면 app.js 파일에 "eval" 단어가 여러 개 등장한다.
- Vue 컴포넌트를 전환하고 설정을 통해서 웹팩이 main.js, NumberBaseball.vue 가 NumberBaseball.vue를 dist 폴더 안의 "app.js"로 합쳐준 것이다.
v-for 로 반복문 사용하기
Ex) 숫자 야구
- 숫자 야구는 랜덤으로 주어진 네 자리 숫자에 대해서 숫자만 맞추면 볼, 숫자와 자리까지 맞추면 스트라이크로 카운트해주는 게임이다.
- 랜덤으로 숫자 만들기
- default에 배열 tries를 추가한다. 입력값이 들어올 때마다 tries 배열 안에 push 하려고 한다.
- 어떤 기능을 추가할 때마다 npm run build 해준다.
- vue 파일
- v-for문 안에 보면 "t" 라는 인스턴스를 만들었다.
- try에 target 값을 추가해준다.
- 입력한 다음에는 focus()하고 value도 ''로 초기화해준다.
- "v-"가 붙으면 뷰가 통제하는 속성이다.
- 입력할 때 마다 "tries" 배열에 데이터가 푸시되고 값을 화면에 보여준다.
<hide>
<template>
<div>
<h1>{{ result }}</h1>
<form v-on:submit="onSubmitForm">
<input ref="answer" maxlength="4" v-model="value" />
<button>입력</button>
</form>
<div>시도: {{ tries.length }}</div>
<ul>
<li v-for="t in tries">{{ t }}</li>
</ul>
</div>
</template>
<script>
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.random());
}
};
export default {
data() {
return {
tries: [],
value: "",
result: "",
};
},
methods: {
onSubmitForm(e) {
e.preventDefault();
this.tries.push(this.value);
this.value = "";
this.$refs.answer.focus();
},
},
};
</script>
<style></style>
Note) 실행 결과
Ex) 배열 안에 인스턴스 넣기
- 아까는 this.value 라는 값을 넣었지만 이번에는 인스턴스를 넣으려고 한다.
- methods 안에 배열에 입력값을 넣어주고 result에는 "홈런"을 넣는다.
<hide/>
<template>
<div>
<h1>{{ result }}</h1>
<form v-on:submit="onSubmitForm">
<input ref="answer" maxlength="4" v-model="value" />
<button>입력</button>
</form>
<div>시도: {{ tries.length }}</div>
<ul>
<li v-for="t in tries"><div>
{{ t.tries }}
{{ t.result }}
</div></li>
</ul>
</div>
</template>
<script>
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.random());
}
};
export default {
data() {
return {
tries: [],
value: "",
result: "",
};
},
methods: {
onSubmitForm(e) {
e.preventDefault();
this.tries.push({
tries: this.value,
result: "홈런",
});
this.value = "";
this.$refs.answer.focus();
},
},
};
</script>
<style></style>
Note) 실행 결과
cf) 코드 줄이기
- <form> 안에 있는 "v-on:" 은 "@"로 바꿀 수 있다.
- submitForm에서 preventDefault를 자주 쓰는데 이 부분을 지우고 다으과 같이 form 태그 안에 표현 가능하다.
- ===> <form @submit.prevent="onSubmitForm">
Ex)
- npm run build 를 매번 입력하는 게 번거롭다. build가 자동으로 되도록 하려면?
숫자 야구 완성하기
- 네 개의 랜덤으로 숫자 만들기
- 아래 코드를 메서드로 만들 수도 있다.
- 그러면 다른 곳에서 this.getNumbers()로 접근 가능하다.
- data{} 또는 methods{} 같은 영역에는 화면과 밀접한 관련이 있는 것들만 묶어준다.
<hide/>
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;
};
- submitForm
- 게임이 끝나고 나면 다음과 같이 초기화해준다.
- 틀린 경우는 스트라이크와 볼 개수를 카운트해야하므로 복잡하다.
- 변수더라도 화면에 보이면 데이터, 화면에 보이지 않으면 그냥 변수로 선언한다.
- const answerArr = this.value.split('').map(v => parseInt(v));
- this.value가 문자 배열이더라도 숫자 배열로 바꿔준다.
- 주의) result는 물결 위에 있는 따옴표( ` , 백틱)로 감싸야한다. 그냥 홑따옴표로 감싸면 오류가 난다.
<hide/>
<template>
<div>
<h1>{{ result }}</h1>
<form @submit.prevent="onSubmitForm">
<input ref="answer" minlength="4" maxlength="4" v-model="value" />
<button>입력</button>
</form>
<div>시도: {{ tries.length }}</div>
<ul>
<!-- <li v-for="t in tries" :key="t.try">
<div>
{{ t.try }}
{{ t.result }}
</div>
</li> -->
<li v-for="t in tries" :key="t.try">
<div>
{{ t.try }}: {{ t.result }}
</div>
</li>
</ul>
</div>
</template>
<script>
const getNumbers = () => {
const candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const array = [];
for (let i = 0; i < 4; i += 1) {
const chosen = candidates.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
array.push(chosen);
}
return array;
};
export default {
data() {
return {
answer: getNumbers(), // 정답은 배열 형태 ex. [1, 5, 3, 4]
tries: [],
value: '',
result: '',
}
},
methods: {
onSubmitForm() {
// 맞춘 경우
if(this.value === this.answer.join('')){ // 문자열과 배열은 join()해줘야 비교 가능
this.tries.push({
try: this.value,
result: '홈런',
});
this.result = '홈런';
alert('게임을 다시 실행합니다.');
this.value = ''; // 초기화
this.tries = [];
this.$refs.answer.focus();
}else{ // 틀린 경우
let strike = 0;
let ball = 0;
const answerArr = this.value.split('').map(v => parseInt(v));
for(let i = 0; i < 4; i += 1){
if(answerArr[i] === this.answer[i] ){ // 숫자와 자릿수 모두 맞는 경우: 스트라이크
++strike;
}else if(this.answer.includes(answerArr[i])){ // 볼
++ball;
}
}
this.tries.push({
try: this.value,
result : `${strike} 스트라이크, ${ball} 볼입니다.`
});
this.value = '';
this.$refs.answer.focus();
}
}
}
};
</script>
<style></style>
Note) 실행 결과
Ex) 10번 너게 틀린 경우, 초기화하기
- 틀린 경우의 앞 부분에 다음과 같이 추가한다.
<hide/>
if(this.tries.length >= 9){
this.result = `10번 넘게 틀려서 실패! 답은 ${this.answer.join(',')} 였습니다.`;
alert("게임을 다시 시작합니다. ");
this.value = ''; // 초기화
this.answer = getNumbers();
this.tries = [];
this.$refs.answer.focus();
}
Note) 실행 결과
- NumberBasebll 파일의 export default {} 를 이용하면?
- main.js 파일에서 import로 가져올 수 있다.
- export default{} 와 import from은 세트라고 생각하면 된다.
- node에서는 import나 default를 쓰지 않고 require, module.export를 쓴다.
- (뷰) import => require
- (뷰) export default => module.export
- 오류
- try는 예약어라서 아래와 같이 쓸 수 없다.
- 원인: build 스크립트가 없다는 뜻이다.
- 해결: try말고 다른 변수를 사용한다.
- 오류: npm run build가 안되는 경우
- 원인: package.json 파일이 실행중인 상태가 아니라서 오류 발생
- 해결: package 파일을 한 번 실행해준 다음에 새롭게 빌드한다.
<hide/>
<li v-for="t in tries" :key="t.try">
<div>
{{ t.try }}: {{ t.result }}
</div>
</li>
Note
- content
출처 - https://www.inflearn.com/course/web-game-vue/dashboard
'FrontEnd > Vue를 이용한 웹 게임 만들기' 카테고리의 다른 글
Chapter 04. 반응 속도 체크 게임 (4) | 2023.04.13 |
---|---|
Chapter 02. 컴포넌트와 웹팩을 쓰는 이유 (0) | 2023.03.30 |
Chapter 01. Vue 환경 설정, 끝말잇기 게임 (0) | 2023.03.29 |