데브코스

[Day 38] Vue Router, Babel, PostCSS

라다디 2022. 12. 11. 18:54

Vue Router 기초

Vue Router를 이용해서 SPA를 만드는 것은 매우 쉽다.

 

다음 명령어를 통해 vue router를 설치한다.

npm i vue-router@next

 

// main.js
import { createApp } from 'vue'
import App from '~/App'
import router from '~/routes'

const app = createApp(App)
app.use(router)
app.mount('#app')

 

라우터를 구성해서 플러그인으로 연결하면 $route$router 객체를 접근해서 사용할 수 있다.

$route 객체는 현재 그 라우트 객체가 사용되는 컴포넌트의 페이지에 대한 정보를 가지고 있다. 

$router 객체는 페이지에 대한 조작을 할 수 있는 여러가지 메소드를 가지고 있다.

 

<!-- routes/Home.vue -->
<template>
  <h1>Home.vue</h1>
</template>
<!-- routes/About.vue -->
<template>
  <h1>About.vue</h1>
</template>

routes 폴더 안에 만들어진 컴포넌트들은 하나의 페이지라고 보면 된다.

그리고 그것을 실제로 사용해서 제어하는 것이 router이다.

 

// routes/index.js
import {createRouter, createWebHashHistory } from 'vue-router'
import Home from './Home'
import About from './About'

export default createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: '/',
      component: Home
    },
    {
      path: '/about',
      component: About
    },
  ]
})

routes 옵션과 함께 router 인스턴스를 만든다. 

경로와 연결할 컴포넌트를 작성한다. 

 

<!-- App.vue -->
<template>
  <RouterLink to="/">
    Home
  </RouterLink>
  <RouterLink to="/about">
    About
  </RouterLink>
  <RouterView />
</template>

RouterLink네비게이션을 위해 사용되는 컴포넌트이다.

RouterView에는 현재 라우트에 맞는 컴포넌트가 렌더링된다. 

 

404 페이지를 만들 때도 라우터로 관리하면 손쉽게 내용이 구현 가능하다. 

...
import NotFound from './NotFound'

export default createRouter({
  history: createWebHashHistory(), // 해쉬 모드
  routes: [
    ...
    {
      path: '/:notFount(.*)', // 정규표현식 사용
      component: NotFound
    },
  ]
})
<!-- routes/NotFound -->
<template>
  <h1>404 Not Found</h1>
</template>

 

$router.push를 활용해서 RouterLink 컴포넌트와 동일한 기능을 구현할 수도 있다.

<template>
  <RouterLink to="/">
    Home
  </RouterLink>
  <button @click="$router.push('/')">
    Home
  </button>
  <RouterView />
</template>

 

만일 route의 이름을 지정한다면 push의 인수를 다음과 같이 작성할 수도 있다.

{
  path: '/',
  name: 'home', // 경로 이름 지정
  component: Home
},
<button @click="$router.push({ name: 'home'})">
  Home
</button>

 

동적으로 변경되는 파라미터콜론 :으로 표시한다.

라우트가 일치하면 동적 파라미터의 값은 모든 컴포넌트에서 this.$route.params로 표시된다.

{
  path: '/documents/:id', // id 파라미터
  name: 'docsId',
  component: DocsId
 },
<template>
  <h1>DocsId.vue</h1>
  <h2>{{ $route.params.id }}</h2>
</template>

동적 파라미터는 보통 화면에 출력하는 용도보다는 특정 id 값을 주소 상태로 받아서 그것을 서버로 전송해서 어떤 특정한 데이터들을 가져올 때 활용한다.

 

[참고] 쿼리 스트링은 주소 부분에서 key-value 형태로 데이터를 담을 때 사용하는 방식이다.

?name=leon&age=85

 

경로에 이름을 사용하게 되면 파라미터나 쿼리 스트링같이 다양한 옵션을 함께 사용할 수 있다.

<template>  
  <RouterLink
    :to="{ 
      name: 'docsId', 
      params: { id: '7777' },
      query: {name: 'Leon', age: 85, email: 'leon@abc.com'}
    }">
    Documents ID
  </RouterLink>
</template>

 

Vue Router 모드

Vue router에서 제공하는 모드는 크게 두 가지이다.

 

Hash 모드 도메인/#/~

  • 기본적인 도메인 주소로만 요청이 들어감
  • 그 외의 나머지 페이지에 대한 요청을 서버로 전달하지 않음
  • 단, 하나의 도메인을 사용해서 요청을 처리하기 때문에 SEO에 좋지 않음
  • 도메인 주소 뒷쪽에 불필요하게 #이 들어감

 

HTML5 모드(= History 모드) 도메인/~

  • 주소가 깔끔함
  • 검색 엔진 최적화에 도움
  • 여러 주소마다 매번 서버로 요청이 전송

 

// routes/index.js
import {createRouter, createWebHistory } from 'vue-router'
import About from './About'

export default createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/about',
      component: About
    }, 
  ]
})

createWebHistory라는 함수를 가져와서 history 옵션에서 실행하면  HTML5 모드로 프로젝트가 동작한다.

 

HTML5 모드는 해쉬 기호를 통해 눈속임이 불가능하기 때문에 실제로 서버에 해당하는 주소에 대한 요청이 들어오게 된다.

따라서 페이지 이동 후 새로고침을 하면 다음과 같은 문제가 발생한다. 

 

 

따라서 이 문제를 해결하기 위한 기본적인 웹팩의 세팅이 필요하다.

// webpack.config.js
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
    clean: true,
  },

output 옵션에 publicPath 옵션을 추가해서 build 시의 main.js의 경로를 절대경로로 지정한다.

 

만일 해당 옵션 없이 빌드하고 dist 폴더의 index.html을 확인해보면 src="main.js"으로 작성이 되어 있는데 이는 ./가 생략되어 있는 것과 동일하다. 

따라서 publicPath 옵션을 통해 main.js를 절대 경로로 불러올 수 있게 해야 한다.

옵션 작성 후 dist 폴더의 index.html을 다시 확인하면 src="/main.js"으로 작성이 된 것을 볼 수 있다. 

 

// webpack.config.js
  devServer: {
    historyApiFallback: true
  }

devServer 옵션에서 historyApiFallback 옵션을 true로 설정한다. 

이는 모든 요청을 index.html 파일로 리다이렉트 시킨다는 의미이다. 

 

 

/about 페이지로 접근이 되면 로컬 서버로 about에 해당하는 페이지를 보여 달라는 요청이 들어오게 되는데, 그것을 devServer가 index.html으로 리다이렉트 시켜주는 것이다.

옵션의 이름처럼 historyApi를 Fallback, 기본 경로로 대체해서 제공하겠다는 의미이다.

 

네비게이션 가드, 메타 필드

네비게이션 가드는 네비게이션을 리디렉션하거나 취소하여 네비게이션을 보호하는 데 사용한다.

메타 필드는 경로에 대한 임의의 정보를 전달하고 싶을 때 사용한다.

 

about 페이지 접근시 requiresAuth라는 정보를 전달한다.

{
  path: '/about',
  component: About,
  meta: {
    requiresAuth: true // 접근 권한이 필요한 페이지
  }
}

 

경로 이동 전 beforeEach를 통해 먼저 검증을 거친다.

// guard.js
import router from "./index";
import store from "~/store";

router.beforeEach(to => {
  // 접근 권한이 있는 페이지의 로그인 여부 체크
  if (to.meta.requiresAuth && !store.state.user.isLoggedIn) {
    return {
      path: '/login',
      query: { redirect: to.fullPath }
    }
  }
})

 queryString에 현재의 주소를 보내줌으로써 login 이후 redirect 값이 있다면 해당 주소로 리다이렉트 시킨다.

 

스크롤

SPA에서는 스크롤이 어느정도 되어 있는 상태에서 다른 페이지로 이동시 그 스크롤이 유지가 된 상태로 이동하게 된다.

 

Vue router에서는 이 문제를 해결할 수 있도록 스크롤 동작을 제어할 수 있는 기능을 제공한다.

// routes/index.js
export default createRouter({
  history: createWebHistory(),
  scrollBehavior() {
    return { top: 0 }
  },
})

scrollBehavior의 return값으로 페이지 이동 시 스크롤 위치를 지정할 수 있다.

이외에도 특정한 요소의 위치로 스크롤 이동, 브라우저를 중심으로 고정된 위치로 스크롤 이동, 비동기로 위치 변경 등 다양한 기능을 제공한다. 

 

Babel 구성

Babel최신의 자바스크립트 문법을 구형의 브라우저에서 동작할 수 있는 자바스크립트 문법으로 변환시켜주는 역할을 수행하는 트랜스파일러이다.

 

다음 명령어로 바벨을 설치한다. 

npm i -D @babel/core @babel/cli

@babel/core는 babel이 동작할 수 있는 코어 패키지이다.

@babel/cli는 터미널에서 명령을 동작시킬 수 있도록 도와주는 패키지이다.

 

// package.json
  "scripts": {
    "babel": "babel main.js --out-dir dist"
  }

다음 명령어로 바벨을 실행시킨다.

npm run babel

 

@babel/core 패키지는 진입점으로 설정한 main.js를 어느 경로에 내보낼 것인가에 대한 출력 결과만 제공한다.

따라서 실제로 동작해야 하는 내용들은 직접 등록해야 한다.

 

루트 경로에 babel.config.json 파일을 생성하고 플러그인을 등록하여 어떤식으로 변환을 할지 지정할 수 있다.

{
  "plugins": [
    "@babel/plugin-transform-block-scoping"
  ]
}

 

npm i -D @babel/plugin-transform-block-scoping

바벨이 동작할 때 해당 플러그인이 개입해서 코드를 변환 시켜줄 것이다.

이제 코드 변환 결과를 보면 변수 키워드가  var로 모두 바뀐 것을 볼 수 있다.

 

하지만 es6 문법은 변환을 못하고 있다.

{
  "plugins": [
    "@babel/plugin-transform-block-scoping",
    "@babel/plugin-transform-arrow-functions"
  ]
}

npm i -D @babel/plugin-transform-arrow-functions

이제 화살표 함수가 일반 함수로 변환된 것을 볼 수 있다.

 

하지만 이런식으로 매번 필요한 플러그인을 직접 설치해서 관리하는 것은 번거롭다.

그럴 때 사용할 수 있는 플러그인들이 자동으로 설치 되는 패키지가 존재한다. 

 

package.json에서 설치 된 플러그인을 모두 지우고 npm i를 실행한다.

그 후 다음 명령어를 통해 preset을 설치한다.

npm i -D @babel/preset-env

// babel.config.json
{
  "presets": ["@babel/preset-env"]
}

 

babel의 preset-env는 최신의 문법을 구형의 브라우저에서도 동작할 수 있도록 변환 시켜주는데, 변환된 결과가 전역으로 등록이 되기 때문에 전역이 오염될 수 있다는 단점이 있다.

 

그 점을 보완하기 위해 @babel/plugin-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와 같은 메소드도 완벽하게 변환시킬 수 있다.

 

package.json의 browerslist옵션을 추가하여 지원할 브라우저의 범위를 지정해 줄 수 있다.

// package.json
  "browerslist": [
    "> 1%", // 글로벌 브라우저 점유율의 1% 이상에 해당하는 모든 브라우저
    "last 2 versions", // 마지막 2개 버전까지 지원
    "not dead" // 죽지 않은 브라우저 지원
  ],

 

webpack과 babel을 함께 사용하기 위해서는 webpack 코어 패키지 외에 babel-loader를 설치해야 한다.

npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader

// webpack.config.js
const HtmlPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './main.js',
  output: {
    publicPath: '/',
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.js$/, // js 파일을 만나면 babel-loader를 동작
        exclude: /node_modules/, // node_modules는 제외
        use: 'babel-loader' // 결과를 웹팩의 최종 번들로 출력
      }
    ]
  },
  plugins: [
    new HtmlPlugin({
      template: './index.html'
    })
  ]
}
// package.json
  "scripts": {
    "babel": "babel main.js --out-dir dist",
    "dev": "webpack-dev-server --mode development",
    "build": "webpack --mode production"
  },

 

PostCSS, Autoprefixer

PostCSS scss와 같은 전처리 도구가 아닌 후처리 도구다.

바벨과 같이 코드를 변환하고 후처리하는 역할을 수행한다.

 

PostCSS의 여러 플러그인 중 하나가 Autoprefixer이다.

이 둘을 이용해서 display: flex, grid와 같이 최신 CSS 기술을 이전의 문법들로 변환할 수 있다.

 

postCSS는 다음 명령어로 설치할 수 있다.

npm i -D postcss autoprefixer postcss-loader

// webpack.config.js
rules: [
  {
    test: /\.s?css$/,
    use: [
      "style-loader",
      "css-loader",
      "postcss-loader",
      "sass-loader",
    ],
  },
]

 

postcss.config.js 파일을 생성하고 플러그인을 연결한다.

module.exports = {
  plugins:[
    require('autoprefixer')
  ]
};

 

바벨과 동일하게 browserslist를 지정한다.

// package.json
  "browerslist": [
    "> 1%", 
    "last 2 versions",
  ],

 


출처: 프로그래머스 프론트엔드 데브코스 

[Day 38] Vue (6)