App.vue
Vuetify를 사용하여 App Bar, Footer, Drawer가 포함된 기본 Layout을 구성하였습니다.
이 UI는 모바일과 데스크톱 환경 모두에서 잘 작동하며,
Drawer는 사이드 메뉴로, App Bar는 상단 헤더로, Footer는 하단에 고정된 레이아웃으로 구성됩니다.
Drawer 메뉴 항목
myBlog에서 Drawer 메뉴 항목은 Vue Router와 연결되어, 사용자가 메뉴를 클릭할 때 애플리케이션의 경로를 변경하고 새로운 페이지를 로드합니다..
Drawer 메뉴와 Router의 연결 구조
1. Vue Router 설정
- Vue Router는 애플리케이션의 경로(path)와 해당 경로에 렌더링할 컴포넌트를 매핑합니다.
- 각 메뉴 항목은 특정 경로(path)와 연결됩니다.
2. Drawer 메뉴 항목 구성
- Drawer의 각 항목은 Vue Router의 router-link 또는 push 메서드를 통해 라우터와 연결됩니다.
- 사용자가 메뉴 항목을 클릭하면 해당 경로로 이동하고, Vue Router가 적절한 컴포넌트를 렌더링합니다.
Vue Router
Vue Router는 Vue.js에서 공식적으로 지원하는 클라이언트 사이드 라우팅 라이브러리입니다.
Vue 애플리케이션에서 여러 페이지 또는 뷰 컴포넌트를 연결하고,
사용자가 탐색할 수 있도록 라우터를 정의할 수 있게 해줍니다.
Vue Router를 사용하면 SPA(Single Page Application)에서도 URL별로 뷰를 변경하거나,
동적 라우팅, 네비게이션 가드, 중첩된 라우터를 쉽게 구현할 수 있습니다.
Vue Router의 주요 기능
- 라우트 정의:
URL 경로에 따라 특정 컴포넌트를 렌더링. - 동적 라우팅:
URL 파라미터를 기반으로 동적으로 페이지를 생성. - 중첩된 라우트:
부모-자식 관계의 라우터를 정의 가능. - 네비게이션 가드:
특정 라우터로 이동하기 전에 인증 또는 조건 검사를 수행. - 히스토리 모드 지원:
브라우저의 히스토리 API를 사용하여 깔끔한 URL 구현 가능. - 라우트 트랜지션:
페이지 전환 애니메이션 지원. - Lazy Loading:
필요한 컴포넌트를 비동기로 로드하여 초기 로딩 속도 최적화.
기본 사용 방법
1. 라우터 구성 파일 생성
router/index.js 파일을 생성하고, 아래와 같이 라우트를 정의합니다:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
];
const router = createRouter({
history: createWebHistory(), // 브라우저 히스토리 모드
routes,
});
export default router;
2. 라우터 등록
main.js 파일에서 라우터를 등록합니다:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 라우터 가져오기
const app = createApp(App);
app.use(router); // 라우터 사용
app.mount('#app');
3. 라우터 링크
라우트를 탐색하려면 <router-link> 컴포넌트를 사용합니다:
<template>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view></router-view>
</template>
<router-link>: SPA에서 다른 경로로 이동할 수 있는 링크 생성.
<router-view>: 현재 활성화된 라우터에 따라 컴포넌트를 렌더링.
myBlog Router
// src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '@/views/HomeView.vue';
import RegisterView from '@/views/RegisterView.vue';
import LoginView from '@/views/LoginView.vue';
import ReadersView from '@/views/ReadersView.vue';
import ProfileView from '@/views/ProfileView.vue';
import CategoryView from '@/views/CategoryView.vue';
import WriteView from '@/views/WriteView.vue';
import PostView from '@/views/PostView.vue';
import EditView from '@/views/EditView.vue';
import BlogView from '@/views/BlogView.vue';
import SearchView from '@/views/SearchView.vue';
import SubscriptionView from '@/views/SubscriptionView.vue';
import NotificationView from '@/views/NotificationView.vue';
import AboutView from '@/views/AboutView.vue';
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: HomeView,
},
{
path: '/login',
name: 'Login',
component: LoginView,
},
{
path: '/register',
name: 'Register',
component: RegisterView,
},
{
path: '/profile',
name: 'Profile',
component: ProfileView,
},
{
path: '/category',
name: 'Category',
component: CategoryView,
},
{
path: '/write',
name: 'Write',
component: WriteView,
},
{
path: '/post/:id',
name: 'Post',
component: PostView
},
{
path: '/post/edit',
name: 'Edit',
component: EditView,
props: true,
},
{
path: '/blog/:userId',
name: 'Blog',
component: BlogView
},
{
path: '/subscription',
name: 'Subscription',
component: SubscriptionView
},
{
path: '/readers',
name: 'Readers',
component: ReadersView
},
{
path: '/search',
name: 'Search',
component: SearchView
},
{
path: '/notofocation',
name: 'Notofocation',
component: NotificationView
},
{
path: '/about',
name: 'About',
component: AboutView,
},
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
myBlog Router 등록
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')
myBlog 라우터 링크 - App.vue
<!-- src/App.vue -->
<template>
<v-app>
<!-- App Bar -->
<v-app-bar color="primary" dark app>
<v-app-bar-nav-icon @click="toggleDrawer" />
<v-toolbar-title>마이 블로그</v-toolbar-title>
</v-app-bar>
<!-- Navigation Drawer -->
<v-navigation-drawer v-model="drawer" app :clipped="$vuetify.breakpoint.lgAndUp">
<v-list>
<v-list-item-group>
<v-list-item v-for="(item, index) in GetMenuItems"
:key="index"
@click="$router.push(item.path).catch(() => { drawer = !drawer });">
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-navigation-drawer>
<!-- Main Content -->
<v-main>
<v-container>
<router-view /> <!-- 라우터가 여기에 컴포넌트를 렌더링 -->
</v-container>
</v-main>
<!-- Footer -->
<v-footer app color="secondary" dark>
<v-btn icon v-if="$route.name !== 'Home'" @click="$router.go(-1)">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<v-spacer></v-spacer>
<router-link to="/" style="cursor: pointer">
<v-icon>mdi-home</v-icon>
</router-link>
</v-footer>
</v-app>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import router from '@/router';
export default {
data() {
return {
drawer: false,
menuItems: [
],
};
},
computed: {
GetMenuItems() {
return [
{ title: '홈', icon: 'mdi-home', path: '/' },
{ title: '로그인', icon: 'mdi-login', path: '/login' },
{ title: '계정만들기', icon: 'mdi-account-box-edit-outline', path: '/register' },
{ title: '새글쓰기', icon: 'mdi-pencil', path: '/write' },
{ title: '블로그', icon: 'mdi-post', path: '/blog/myblog' },
{ title: '구독', icon: 'mdi-account-heart', path: '/subscription' },
{ title: '독자', icon: 'mdi-account-details', path: '/readers' },
{ title: '검색', icon: 'mdi-file-search-outline', path: '/search' },
{ title: '계정설정', icon: 'mdi-account-box-edit-outline', path: '/profile'},
{ title: '카테고리', icon: 'mdi-cog', path: '/category' },
{ title: '알림설정', icon: 'mdi-bell', path: '/fcm' },
{ title: 'About', icon: 'mdi-information', path: '/about' }
]
}
},
methods: {
toggleDrawer() {
this.drawer = !this.drawer;
}
},
};
</script>