VueJS/Vite

[VueJS] Vite에 NuxtJS의 layouts, pages 규칙 적용해보기

SongMinu 2023. 2. 6. 17:59
728x90

Nuxt를 사용하면서 제공되는 기능들 중에 마음에 들었던 기능은 layouts와 pages 디렉터리의 기능이었다.

pages안에 생성된 디렉터리와 파일명에 맞춰 자동으로 라우트 경로를 만들어 줘서 편리했고,

각 페이지 파일마다 보여줄 레이아웃을 지정해 줘서 기본 화면들에선 메뉴가 보이다 특정화면에선 메뉴가 없는 다른 레이아웃을 보여줄 수 있어 편리했다.

pages의 자동 라우트 경로 생성 기능은 사람마다 호불호는 좀 있는 것 같았다.

nuxt 프로젝트 구조 중 일부

위 예시로 pages/vuetify/vuetify1.vue가 http://localhost:5000/vuetify/vuetify1로 자동으로 페이지 경로가 생성된다.

그리고 pages/login.vue의 경우는 layouts의 login.vue 파일로 레이아웃을 잡아놔서 로그인 페이지에 접속하면 해당 레이아웃 화면을 보여준다.

그래서 페이지의 역할에 따라 맞는 레이아웃을 사용해 화면을 구현할 수 있어서 Nuxt에서 제공해주는 이 규칙은 개인적으로 나에겐 상당한 편리함을 줬었다.

(별도로 레이아웃을 지정해주지 않으면 default.vue가 레이아웃으로 사용된다.)

 

Vite를 이용해 개인 프로젝트를 만들다보니 만들고 있는 한 화면에서 다른 레이아웃을 보여줘야 하는 상황이 생겼었는데

이런저런 고민도 해보고 생각난 몇 가지 방법들을 적용해보려고 했으나 잘 안됐다.

https://stackblitz.com/edit/vue3-vue-router-meta-layout?file=src%2Flayouts%2FAppLayout.vue 

 

Vue 3 + Vue Router - meta layout - StackBlitz

Vue 3 + Vue Router meta layout - https://stackoverflow.com/q/69048657/6277151

stackblitz.com

찾다 보니 이런 방법이 있길래 적용해 봤는데 자꾸 경고 문구가 출력되는 게 보기 싫었고,

Nuxt의 기능이 있으면 참 좋겠다는 생각을 하고 있다가 찾아보니 같은 기능을 구현할 수 있게 해 주는 플러그인 2개를 찾았다.

이걸 적용하는데 대략 2일의 시간을 사용한 김에 블로그에도 작성해 보기로 했다.


프로젝트 생성부터 적용까지의 내용으로 작성할 생각이다. (다시 복습한다는 생각으로)

1. Vite 프로젝트 생성

npm create vite@3

vue와 typescript 선택

vite버전은 3버전대로 설치한다.

현재 최신버전이 4인데 해당 버전에서는 사용할 2개의 npm이 아직 적용이 안된다.다음 단계에서 설치할 npm이 아직 안된다.

2. npm 설치

npm install vue-router@4
npm install -D vite-plugin-vue-layouts
npm install -D vite-plugin-pages

3. vite.config.ts 수정

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()]
})

프로젝트 생성 시 기본적으로 이렇게 되어 있고 아래와 같이 수정한다.

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pages from 'vite-plugin-pages';
import layouts from 'vite-plugin-vue-layouts';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    pages({
      // dirs: 'src/views'
    }),
    layouts({
      layoutsDirs: 'src/layouts',
      defaultLayout: 'default'
    })
  ]
})

pages는 별도의 옵션을 주지 않는다면 기본 값으로 src/pages가 지정된다.

pages가 아닌 다른 디렉터리를 지정하고 싶으면 위에 dirs 주석을 풀고 다른 경로를 작성하면 된다.

 

layouts 안의 layoutsDirs 속성에는 레이아웃 기능을 사용할 디렉터리 경로를 입력해 주고, 

defaultLayout에는 기본 레이아웃 파일명을 입력해주면 된다.

입력 시 .vue와 같은 확장자를 제거하고 파일명만 입력하면 된다.

난 기본 레이아웃 화면으로 default.vue를 만들고 사용할 예정이라 default라고 지정했다.

4. tsconfig.json 수정

"types": ["vite-plugin-pages/client", "vite-plugin-vue-layouts/client"]

compilerOptions 안에 위 옵션을 추가한다.

 

또는 tsconfig.json에 추가하지 않고 vite-env.d.ts에 추가하는 방법도 있다.

/// <reference types="vite/client" />
/// <reference types="vite-plugin-pages/client"/>
/// <reference types="vite-plugin-vue-layouts/client"/>

declare module '*.vue' {
  import type { DefineComponent } from 'vue';
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

위에 2, 3번 라인 처럼 reference types 2개를 똑같이 추가해주면 된다.

내가 작업중이던 개인 프로젝트에서는 types를 추가했더니 typeRoots와 같이 못쓰는 것 같다.

빌드 시 에러가 출력되던 모듈 2개가 있었고, typeRoots에 처리해놨었는데 다시 똑같은 에러가 발생하는 현상이 발생했었다.

그래서 tsconfig.json에 작업하지 않고 vite-env.d.ts에 처리했다.

이부분의 대한 내용은 https://github.com/hannoeru/vite-plugin-pages 여기서 README.md를 읽던 중 다른 방법이 있길래 적용해보니 해결됐다.

이런 경우가 아니라면 tsconfig.json만 작업해줘도 된다.

5. vue-router 적용

src 아래 routes 디렉터리를 생성, 그리고 그 안에 index.ts를 생성하고 코드를 작성한다.

//src/routes/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { setupLayouts } from 'virtual:generated-layouts';
import generatedRoutes from 'virtual:generated-pages';

const routes = setupLayouts(generatedRoutes);

export const router = createRouter({
  history: createWebHistory(),
  routes,
  strict: true,
});

그리고 main.ts를 열어서 적용!

//main.ts
import { createApp } from 'vue';
import { router } from './routes';
import './style.css';
import App from './App.vue';

createApp(App).use(router).mount('#app');

이제 Nuxt처럼 pages 생성되는 디렉터리와 파일에 맞춰 라우터 경로가 자동으로 생성된다. 

6. layouts 디렉터리 작업

src 아래 layouts 디렉터리를 생성 후 기본 레이아웃 파일과 추가 레이아웃 파일을 생성한다.

기본 레이아웃으로 사용될 default.vue 파일을 생성했고, 테스트를 위해 second.vue 파일을 추가했다.

그리고 App.vue 파일을 열어서 <router-view />를 추가해 준다.

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <!-- <HelloWorld msg="Vite + Vue" /> -->
  <router-view />
</template>

난 이렇게 작성했다.

App.vue에서는 레이아웃과 상관없이 모든 레이아웃에서 보여줘야 할 내용은 여기다 작성하면 된다.

 

내가 작업 중인 프로젝트에선 이렇게 했다.

적용한 개인프로젝트 App.vue 소스 중 일부

헤더는 모든 레이아웃에서 유지시키기 위해 저렇게 작성했다.

 

layouts 디렉터리에 생성한 default.vue와 second.vue에 코드를 작성한다. (확인만 하기위해 간단하게 작성함)

<script setup lang="ts"></script>
<template>
  <h1>default layout</h1>
  <router-view />
</template>
<style scoped></style>
<script setup lang="ts"></script>
<template>
  <h1>second layout</h1>
  <router-view />
</template>
<style scoped></style>

vouter-view를 넣어주고 해당 레이아웃에 보여줘야 할 내용들을 작성하면 된다.

7. pages 디렉터리 작업

아까 vite.config.ts에서 pages 플러그인에 별도의 옵션을 주지 않았기 때문에 기본적으로 src/pages 디렉터리가 라우터 경로가 된다.

src아래 pages 디렉터리를 생성 후 확인을 위해 아래와 같이 디렉터리와 파일을 만들었다.

index.vue는 이제 루트 경로의 화면이 될 거고, 그 외에는 디렉터리와 파일명에 맞춰 경로가 생성된다.

http://localhost:5173 -> pages/index.vue 화면 출력

http://localhost:5173/board -> pages/board/index.vue 화면 출력

http://localhost:5173/board/detail -> pages/board/detail.vue 화면 출력

만약 웹 구동 중에 적용이 안된다면 재구동해 주면 된다.

각 파일에 코드는 이렇게만 작성했다.

<script setup lang="ts"></script>
<template>
 <p>index page</p>
</template>
<style scoped></style>

<script setup lang="ts"></script>
<template>
 <p>board page</p>
</template>
<style scoped></style>

<script setup lang="ts"></script>
<template>
 <p>board detail page</p>
</template>
<style scoped></style>

순서대로 pages/index.vue, pages/board/index.vue, pages/board/detail.vue이다.

8.pages에 layout 적용

위에 만든 파일 중에서 board안에 있는 detail.vue만 second.vue 레이아웃을 지정한다면 이렇게 하면 된다.

<script setup lang="ts"></script>
<template>
 <p>board detail page</p>
</template>
<style scoped></style>
<route>
  {
    meta: {
      layout: "second" ## 큰 따옴표 빠지면 적용안됨
    }
  }
</route>

route 태그와 meta 속성 그리고 layout 속성에 확장자를 뺀 레이아웃 파일명을 입력해 주면 된다. 

그리고 yaml 방식의 입력도 제공된다.

<script setup lang="ts"></script>
<template>
 <p>board detail page</p>
</template>
<style scoped></style>
<route lang="yaml">
meta:
  layout: second
</route>

이 외에도 다른 속성들도 많이 제공되는 것 같은데 써보진 않았다.

9. 각 화면

http://localhost:5173, pages/index.vue
http://localhost:5173/board, pages/baord/index.vue
http://localhost:5173/board/detail, pages/board/detail.vue

잘 적용된 걸 확인할 수 있다.

10. Dynamic routes

Nuxt에서도 몇 번 사용했었던 다이나믹 라우트의 기능도 vite-plugin-pages에서 제공된다.

이 기능도 구현하고자 하는 것에 따라 유용하게 사용할 수 있는 기능인데

vite-plugin-pages 깃허브 readme를 읽어보다 기능이 있길래 사용해 봤다.

먼저 Nuxt에서는 파일명 앞에 _ 를 붙여서 사용하면 다이나믹 라우트 기능을 사용할 수 있다.

자세한 설명은 공식 홈페이지 주소를 참고...

https://nuxtjs.org/docs/features/file-system-routing/#dynamic-routes

 

File System Routing

Nuxt automatically generates the vue-router configuration based on your file tree of Vue files inside the pages directory. When you create a .vue file in your pages directory you will have basic routing working with no extra configuration needed.

nuxtjs.org

vite-plugin-pages에서는 대괄호를 이용해 사용할 수 있다.

위 사진 처럼 저 위치에 [id].vue 파일을 생성 후 코드를 아래와 같이 작성한다.

<script setup lang="ts">
const props = defineProps({
  id: String
})
</script>
<template>
 <p>dynamic routes</p>
 <span>{{ id }}</span>
</template>
<style scoped></style>
<route lang="yaml">
  {
    meta: {
      layout: "second"
    }
  }
</route>

그리고 http://localhost:5173/board/1q2w3e4r 입력하면

결과는 이렇다.

라우터 값을 만들 때 /board/:id 이렇게 만든것과 유사하다고 생각하면 된다.

 

대괄호를 통해 받을 값을 정해서 해당 파일에서는 props로 받으면 된다.

[id].vue에서 대괄호 안의 id 값이 props의 id 속성으로 넘어온다.

2개 이상의 값으로 받고 싶다면 디렉터리로 뎁스를 구성해서 만들면 사용할 수 있다.

 

내 개인 프로젝트에 사용한 예시로 보여주면 이렇다.

search 디렉터리 아래 대괄호를 사용해 만든 [platform] 디렉터리를 생성했고, 그 안에 [nickname].vue 라는 파일을 생성했다.

이렇게 하면 [nickname].vue 파일 안에서는 platform과 nickname 두 개의 props를 받을 수 있다.

map 디렉터리 안에도 이 기능을 적용하기 전에는 3개의 파일로 구성되어 있었으나 다이나믹 라우트를 적용시켜 1개로 처리하게 만들었다.

teams 디렉터리 안도 2개의 파일로 구성되어 있었지만 1개로 처리했다.

map과 teams안에 각 파일들이 중복되는 소스가 좀 많았었는데 1개의 파일로 합치니 작업하는데 수월해졌었다.

 

상황에 맞게 여러 개의 디렉터리와 파일을 만들어서 다양하게 응용할 수 있을 거라 생각된다.


https://www.npmjs.com/package/vite-plugin-vue-layouts

 

vite-plugin-vue-layouts

Router based layout plugin for Vite and Vue. Latest version: 0.7.0, last published: 6 months ago. Start using vite-plugin-vue-layouts in your project by running `npm i vite-plugin-vue-layouts`. There are 7 other projects in the npm registry using vite-plug

www.npmjs.com

https://github.com/hannoeru/vite-plugin-pages

 

GitHub - hannoeru/vite-plugin-pages: File system based route generator for ⚡️Vite

File system based route generator for ⚡️Vite. Contribute to hannoeru/vite-plugin-pages development by creating an account on GitHub.

github.com

이 글에서 사용된 예제 소스

https://github.com/smw0807/vue3/tree/main/vite-plugin-vue-layouts-ts

 

 

이 플러그인들을 알게되고 괜찮은 것 같아 적용하면서 한글화된 정보가 없는 것 같아 이렇게 블로그에 작성하게 되었습니다.

영어가 부족해서.... 잘못된 정보가 있다면 지적 부탁드립니다.

반응형