[DAY-40] Vue (7)
๐ ํท๊ฐ๋ ธ๋ & ๋ชฐ๋๋ ๋ถ๋ถ๋ค๋ง ์ ๋ฆฌํ๋ ๋๋ง์ TIL
๐ฏ ๋ชจ๋ ๊ฐ์ ๋ด์ฉ์ ์ ์ง ์์์!
์ค๋์ ์๊ฐ์?
๋๋์ด ๋ทฐ, webpack์ ๊ธฐ์ด๊ฐ ๋๋ฌ์ต๋๋ค.
๋ทฐ๋ก ๋
ธ์
ํด๋ก๋์ ํ๋ฉด ์ ๋ง ํธ๋ฆฌํ ๊ฒ ๊ฐ์์.
VanillaJS์ ์ต์ํ์๋๋ฐ.. ๋ ํธ๋ฆฌํ ๊ฑธ ์์๋ฒ๋ ธ์ด..
[1] Vue Router
Vue Router
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ํ์ด์ง ๋ถ๋ฆฌ ๋ฐ ์ด๋์ ํ ์ ์์ต๋๋ค.
Vue Router
๋ ํด๋น ๋ช
๋ น์ด๋ก ์ค์นํ ์ ์์ต๋๋ค.
npm i vue-router@next // next๋ฒ์ ์ผ๋ก ์ค์นํฉ๋๋ค.
[1-1] Router mode
[1] Hash ๋ชจ๋
// router hash ๋ชจ๋
export default createRouter({
history: createWebHashHistory(),
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About,
},
{
path: "/documents",
component: Docs,
children: [
{
path: ":id",
component: DocsId,
},
],
},
{
path: "/:notFound(.*)",
component: NotFound,
},
],
});
// main.js
app.use(router); // use๋ฅผ ์ฌ์ฉํด์ ๋ฑ๋กํฉ๋๋ค.
<template>
<!-- ๋งํฌ ์ด๋์ ์ํ ํ๊ทธ -->
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<!-- ๋ผ์ฐํ
์ ๋ฐ๋ฅธ ์ปดํฌ๋ํธ ๋ ๋๋ง ํ๊ทธ -->
<RouterView />
<!-- RouterLink์ ๊ฐ์ ์ญํ ์
๋๋ค. -->
<button @click="$router.push('/')">Home</button>
<button @click="$router.push('/about')">About</button>
</template>
- ์ฅ์
๊ธฐ๋ณธ์ ์ธ ๋๋ฉ์ธ ์ฃผ์๋ก๋ง ์์ฒญ์ด ๋ค์ด๊ฐ๊ณ , ๊ทธ ์ธ์ ๋๋จธ์ง ํ์ด์ง ๋ถ๋ฅ๋ ์์ฒญ์ ์๋ฒ๋ก ์ ๋ฌํ์ง ์์ต๋๋ค.
๋ฐ๋ผ์ ์๋ฒ์ ์์ฒญ์ ์ถ๊ฐ์ ์ผ๋ก ์ ์กํ ํ์๊ฐ ์์ต๋๋ค.
- ๋จ์
๋๋ฉ์ธ ์ฃผ์์ ๋ค์ชฝ์ ๋ถํ์ํ๊ฒ #
๊ฐ ๋ค์ด๊ฐ๋๋ค.
ํ๋์ ๋๋ฉ์ธ์ ์ฌ๋ฌ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๊ฒ์ ์์ง ์ต์ ํ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
[2] HTML5 ๋ชจ๋(History ๋ชจ๋)
// router HTML5 ๋ชจ๋
export default createRouter({
history: createWebHistory(),
routes: [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About,
},
{
path: "/documents",
component: Docs,
children: [
{
path: ":id",
component: DocsId,
},
],
},
{
path: "/:notFound(.*)",
component: NotFound,
},
],
});
ํด๋น ๋ชจ๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋ช๊ฐ์ง ์ค์ ์ด ํ์ํฉ๋๋ค.
// webpack.config.js
// ...
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/", // publicPath๋ก script์ main.js๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ๋ถ๋ฌ์ฌ ์ ์๊ฒ ํฉ๋๋ค.
clean: true,
},
// ...
devServer: { // ์ ํฌ๋ ๋ก์ปฌ ์๋ฒ๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ devServer์ ์ธํ
ํฉ๋๋ค.
historyApiFallback: true, // ํน์ ํ ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ์ ๋, index.html๋ก ๋ฆฌ๋ค์ด๋ ํธ ์์ผ์ค๋๋ค.
},
- ์ฅ์
์ฃผ์๊ฐ ๊น๋ํฉ๋๋ค.
๊ฒ์ ์์ง ์ต์ ํ๊ฐ ํธ๋ฆฌํฉ๋๋ค.
- ๋จ์
์ฌ๋ฌ ์ฃผ์๋ง๋ค ๋งค๋ฒ ์๋ฒ์ ๋ํ ์์ฒญ์ด ๋ค์ด๊ฐ๋๋ค.
๐ฅบ ๊ฐ์ธ์ ์ผ๋ก
history mode
๊ฐ ๋ ์ฌ์ฉ์ฑ์ด ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ํ๋จํ์ต๋๋ค.
[1-2] ๋ค๋น๊ฒ์ด์ ๊ฐ๋
๋ค๋น๊ฒ์ด์ ์๋ ๊ฐ๊ฐ ์ฌ์ฉํ ์ ์๋ ๊ฐ๋๊ฐ ์์ต๋๋ค.
๋ผ์ฐํฐ๋ก ์ธํด ๊ฒฝ๋ก๊ฐ ๋ณ๊ฒฝ๋๊ธฐ ์ด์ , ๊ทธ๋ฆฌ๊ณ ์ดํ์ ๋ค์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๊ฐ๋๋ ๋ณดํต ๋ณ๋์ ํ์ผ์ ๋ง๋ค์ด ์์ฑํฉ๋๋ค.
// guard.js
import router from "./index";
import store from "~/store";
router.beforeEach((to) => {
// beforeEach ๊ฐ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ ๊ทผ ๊ถํ์ด ์๋ ํ์ด์ง์ ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ์ฒดํฌํด์ค ์ ์์ต๋๋ค.
console.log(to);
if (to.meta.requiresAuth && !store.state.user.isLoggedIn) {
return {
path: "/login",
query: { redirect: to.fullPath },
};
}
});
[1-3] ์คํฌ๋กค
SPA
์์๋ ์คํฌ๋กค ์์น๊ฐ ๊ณ ์ ๋ฉ๋๋ค.
home ํ์ด์ง์์ route ์ฒ๋ฆฌ๋ก about ํ์ด์ง๋ก ์ด๋ํ๋๋ผ๋, ํด๋น ์คํฌ๋กค์ ์์น๋ ๊ณ ์ ๋ ์ฑ ํ์ด์ง ์ด๋์ด ์ผ์ด๋ฉ๋๋ค.
Vue router
์์๋ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๊ฒ ์คํฌ๋กค ๋์์ ์ ์ดํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
// routes/index.js
export default createRouter({
history: createWebHistory(),
scrollBehavior() {
return { top: 0 }; // ์คํฌ๋กค ์์น๋ฅผ ํน์ ์์น๋ก ๊ณ ์ ์์ผ์ค ์ ์์ต๋๋ค.
},
ํน์ ํ ์์์ ์์น๋ก ์คํฌ๋กค ์ด๋, ๋ธ๋ผ์ฐ์ ๋ฅผ ์ค์ฌ์ผ๋ก ๊ณ ์ ๋ ์์น๋ก ์คํฌ๋กค ์ด๋, ๋น๋๊ธฐ๋ก ์์น ๋ณ๊ฒฝ ๋ฑ์ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
[2] Babel
Babel
์ ์ต์ ์ JavaScript ๋ฌธ๋ฒ์ ๊ตฌํ์ ๋ธ๋ผ์ฐ์ ์์๋ ๋์ํ ์ ์๊ฒ ์ด์ ์ JavaScript ๋ฌธ๋ฒ์ผ๋ก ๋ณํ์์ผ์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
๋ฐ๋ฒจ์ ๋ค์์ ๋ช ๋ น์ด๋ก ์ค์นํ ์ ์์ต๋๋ค.
npm i -D @babel/core @babel/cli // babel core์ babel cli๋ฅผ ํจ๊ป ์ค์นํฉ๋๋ค.
"scripts": {
"babel": "babel main.js"
}
์ง์ ์ ์ ํจ๊ป ์ง์ ํด์ค์ผ ํฉ๋๋ค.
babel core
๋ ์ง์
์ ์ ์๋ ํ์ผ๋ค์ ๋ณํํด์ฃผ๋ ์ญํ ์ ํ์ง๋ง, ํด๋น ๋ณํ๋๋ ์ต์
๋ค์ ์ง์ ์ง์ ํด์ค์ผ ํฉ๋๋ค.
ํด๋น ์ต์
๋ค์ babel.config.json
(๋๋ js)ํ์ผ ๋ด๋ถ์ ์์ฑํ ์ ์์ต๋๋ค.
ํ์ง๋ง ๋ณ์ํ, ํจ์ํ ๋ฑ์ ๋ค์ํ JavaScript ๋ฌธ๋ฒ์ ๋ฐ๋ผ ํ๋ฌ๊ทธ์ธ์ ํ๋์ฉ ์ค์นํด์ฃผ๋ ๊ฒ์ ๋งค์ฐ ๋ฒ๊ฑฐ๋ก์ด ์์ ์ ๋๋ค.
๋ฐ๋ผ์ ์ด๋ฅผ ๋ฌถ์ด๋์ preset-env
ํจํค์ง๋ฅผ ์ค์นํ ์ ์์ต๋๋ค.
npm i -D @babel/preset-env
{
"presets": ["@babel/preset-env"]
}
ํญ์ ์ค์น ํ์๋
babel.config.json
์ ๋ฑ๋กํด์ผ ํฉ๋๋ค!
[2-1] Transform runtime
Babel
์ JS์ ์ต์ ์ฝ๋๋ฅผ ๊ตฌํ ์ฝ๋๋ก ๋ณํํด์ค๋๋ค.
์ด๋ ๊ฒ ๋ณํ๋ ๊ฒฐ๊ณผ๋ ์ ์ญ์ผ๋ก ๋ฑ๋ก์ด ๋๊ธฐ ๋๋ฌธ์ ์ ์ญ์ด ์ค์ผ๋ ์ ์๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
์ด๋ฅผ ๋ณด์ํ๊ธฐ ์ํด transform-runtime
ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํด๋น ํ๋ฌ๊ทธ์ธ์ ์ ์ญ์ด ์ค์ผ๋์ง ์๊ณ ๋ ๋ณํ๋ ์ฝ๋๊ฐ ์คํ๋ ์ ์๊ฒ ํฉ๋๋ค.
๋ํ ์ค์ ๋ก ์์ฑํ ์ฝ๋๋ง ๋ณํ์์ผ์ฃผ๊ธฐ ๋๋ฌธ์, ์ฝ๋์ ์ฉ๋ ๋ํ ์ ์ฝํ ์ ์์ต๋๋ค.
npm i -D @babel/plugin-transform-runtime @babel/runtime-corejs3
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
corejs
์ ๋ฒ์ ์ด 3์ด์ด์ผincludes
์ ๊ฐ์ ๋ฉ์๋๋ ์๋ฒฝํ๊ฒ ๋ณํ์ํฌ ์ ์์ต๋๋ค.
[2-2] browserslist
ํ์ฌ ์น ํ์ด์ง๊ฐ ๋์ํ ๋ธ๋ผ์ฐ์ ์ ๋ฒ์๋ฅผ ์ง์ ํด์ค ์ ์์ต๋๋ค.
์ด๋ package.json
ํ์ผ์ ๋ฑ๋กํ๋ฉฐ, ๋ฌธ์์ด์ ๋ด์ ๋ฐฐ์ด ํํ๋ก ์์ฑํฉ๋๋ค.
"browserslist" : [
"> 1%",
"last 2 versions",
"not dead"
]
์ด๋ฌํ ๋ธ๋ผ์ฐ์ ๋ค์ ๋ฒ์๋ฅผ ์ง์ ํ์ง ์์ผ๋ฉด, ๋ฐ๋ฒจ๋ก ์ฝ๋๋ฅผ ๋ณํํด์ผ ํ๋ ๋ฒ์๊ฐ ๋์ด ๋นํจ์จ์ ์ ๋๋ค.
๋ฐ๋ผ์ ์ด๋ฌํ ๋ธ๋ผ์ฐ์ ์ ๋ฒ์๋ฅผ ๋ช ์ํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค.
[2-3] webpack๊ณผ babel
webpack
๊ณผ babel
์ ํจ๊ป ์ฌ์ฉํ๊ธฐ ์ํด์๋ webpack
์ฝ์ด ํจํค์ง ์ธ์ babel-loader
๋ฅผ ์ค์นํด์ค์ผ ํฉ๋๋ค.
// webpack.config.js
module: {
rules: [
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.s?css$/,
use: ["vue-style-loader", "css-loader", "sass-loader"],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: "babel-loader",
// js ํ์ผ์ ๋ง๋๋ฉด babel-loader๋ฅผ ๋์ ์ํต๋๋ค.
// ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์นํฉ์ ์ต์ข
๋ฒ๋ค๋ก ์ถ๋ ฅํ ์ ์์ต๋๋ค.
// node_modules๋ ์ ์ธํฉ๋๋ค.
},
],
},
[3] PortCSS, Autoprefixer
PostCSS
๋ scss
์ ๊ฐ์ ์ ์ฒ๋ฆฌ ๋๊ตฌ๊ฐ ์๋ ํ์ฒ๋ฆฌ ๋๊ตฌ์
๋๋ค.
๋ฐ๋ฒจ๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ๋ณํํ๊ณ ํ์ฒ๋ฆฌํ๋ ์ญํ ์ ์ํํฉ๋๋ค.
PostCSS
์ค ๊ฐ์ฅ ๋ํ์ ์ธ ํ๋ฌ๊ทธ์ธ์ด Autoprefixer
์
๋๋ค.
์ด ๋์ ์ด์ฉํด์ display: flex, grid
์ ๊ฐ์ด CSS ์ต์ ๊ธฐ์ ๋ค์ ์ด์ ์ ๋ฌธ๋ฒ๋ค๋ก ๋ณํํ ์ ์์ต๋๋ค.
postCSS
๋ ๋ค์ ๋ช
๋ น์ด๋ก ์ค์นํ ์ ์์ต๋๋ค.
npm i -D postcss autoprefixer postcss-loader
Postcss
์Autoprefixer
, webpack์postcss-loader
๋ ํจ๊ป ์ค์นํฉ๋๋ค.
// webpack.config.js
{
test: /\.s?css$/,
use: [
"vue-style-loader",
"css-loader",
"postcss-loader",
"sass-loader",
],
},
// ๊ธฐ์กด์ ์คํ์ผ ๋ก๋, css ๋ก๋ ๋ฑ๊ณผ ํจ๊ป ์ฌ์ฉํฉ๋๋ค.
PostCSS
๋ postcss.config.js
์ ์ฌ๋ฌ ์ต์
๋ค์ ์ง์ ํด์ค์ผ ํฉ๋๋ค.
// postcss.config.js
module.exports = {
plugins: [require("autoprefixer")],
};
๋ฐ๋ฒจ๊ณผ ๋์ผํ๊ฒ
browserslist
๋ฅผ ์ง์ ํด์ค์ผ ํฉ๋๋ค.
[4] Webpack Template
์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ชจ๋ vuex, babel, postCSS ๋ฑ์ webpack์ ์ ์ฉํด๋ณด๋๋ก ํฉ๋๋ค.
// webpack.config.js
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const HtmlPlugin = require("html-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
resolve: {
extensions: [".vue", ".js"],
alias: {
"~": path.resolve(__dirname, "src"),
},
},
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/",
clean: true,
},
module: {
rules: [
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.js$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.s?css$/,
use: [
"vue-style-loader",
"css-loader",
"postcss-loader",
"sass-loader",
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new HtmlPlugin({
template: "./src/index.html",
}),
new CopyPlugin({
patterns: [{ from: "static" }],
}),
],
devServer: {
historyApiFallback: true,
},
};
์ด๋ฌํ ํ ํ๋ฆฟ์ ์ ์ฅ์์ ์ฌ๋ ค๋๊ณ , ํ์ํ ๋ ๋ง๋ค ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ฉด ํธ๋ฆฌํฉ๋๋ค. ๐
๋๊ธ๋จ๊ธฐ๊ธฐ