Vue Cheat Sheet 2 (심화과정)

0. 시작

npm create vue@latest

1. 소개 및 프로젝트 개요

  • 개발자 측면 기능:

    • 타입 지원이 되는 자동 라우팅
    • 컴포넌트와 Composition API의 자동 임포트
    • Vue Devtools를 통한 디버깅
    • 린팅과 자동 포매팅
  • 사용자 측면 기능:

    • CRUD 작업(생성, 읽기, 업데이트, 삭제)
    • 견고한 인증 시스템
    • FormKit을 사용한 고급 폼
    • Supabase와의 실시간 데이터 동기화
    • 동적 UI 상호작용
    • 알림 및 이메일 기능
    • 차트를 통한 데이터 시각화
    • GA4, GTM, Sentry와의 통합

2. 필수 조건 및 초기 환경

  • 터미널 설정:

    • Mac 사용자: 기본 터미널 사용 가능. 대안으로 iTerm을 추천하며, 추가 기능인 Amazon Q로 AI 자동 완성 및 채팅 기능 활용 가능.
    • Windows 사용자: Microsoft Store에서 무료 Windows Terminal 애플리케이션 다운로드 추천.
  • Node.js 및 NPM 설치:

    • Node.js LTS 버전 18.3 이상 설치 권장 (Vue 3에 추천되는 버전).
    • 터미널에서 node -v 입력하여 설치된 버전 확인.
    • NVM(Node Version Manager) 소개:
      • 여러 Node.js 버전을 쉽게 관리할 수 있는 도구.
      • Mac 사용자: Homebrew를 통해 NVM 설치 가능.
      • Windows 사용자: NVM 저장소에서 설치 방법 확인.
      • 주요 명령어
        • nvm ls-remote: 설치 가능한 Node.js 버전 목록 확인.
        • nvm install [버전번호]: 특정 버전 설치.
        • nvm ls: 설치된 Node.js 버전 목록 확인.
        • nvm use [버전번호]: 특정 버전 사용 설정.
        • nvm alias default [버전번호]: 기본 사용할 버전 설정.
        • nvm uninstall [버전번호]: 특정 버전 삭제.
      • NVM 설정은 필수가 아니며, 여러 버전을 관리해야 하는 경우에만 권장
  • 웹 브라우저:

    • Chrome 추천하지만, Vue DevTools 확장 프로그램을 지원하는 브라우저 사용 가능.

3. Vite를 사용하여 번개처럼 빠른 Vue.js 3 앱 스캐폴딩

  • Vue.js 프로젝트 생성 방법:
    • 간단한 코드 실험을 위해 JSFiddle이나 StackBlitz와 같은 온라인 플레이그라운드를 사용할 수 있지만, 실제 프로젝트를 구축하려면 더 완전한 설정이 필요합니다.
    • Create Vue 도구를 사용하여 Vue 프로젝트를 생성합니다. 이는 Vite를 기반으로 하는 도구로, Vite는 Vue.js의 창시자 Evan You가 만든 빠르고 신뢰성 있는 개발 서버입니다.
    • Vite의 주요 특징:
      • 매우 빠른 핫 모듈 교체(HMR)로 코드 변경 시 브라우저에 즉시 반영됩니다.
      • 부드러운 TypeScript 지원과 현대적인 JavaScript의 강력한 기능을 제공합니다.
    • 터미널을 사용하여 프로젝트를 생성합니다:
      • Mac 사용자는 기본 터미널이나 iTerm을 사용할 수 있습니다.
      • Windows 사용자는 Windows Terminal을 사용할 수 있습니다.
    • 프로젝트 생성 단계:
      • 새로운 디렉토리를 생성하고(mkdir masterclass), 해당 디렉토리로 이동합니다(cd masterclass).
      • Vue 공식 문서에서 설치 명령어를 복사하여 터미널에 입력합니다.
      • 프로젝트 설정 시 다음과 같은 옵션을 선택합니다:
        • 프로젝트 이름: VueJS Masterclass 2024
        • TypeScript: 예
        • JSX: 아니요
        • Vue Router: 예
        • Pinia: 예
        • Vitest: 아니요
        • E2E 테스트: 아니요
        • ESLint와 Prettier: 예
        • Vue Devtools 확장: 아니요 (실험적 기능이므로)
    • 설정 완료 후, 프로젝트 디렉토리로 이동하여 npm install을 실행하여 의존성을 설치합니다.
    • 이제 Vue.js 애플리케이션을 구축할 준비가 되었습니다.

4. 모든 개발자를 위한 Git 기본 사항

  • Git 소개 및 설치 확인: Git은 버전 관리 시스템으로 파일의 변경 사항을 추적하여 협업을 용이하게 합니다. 터미널에서 git --version 명령어로 Git이 설치되어 있는지 확인합니다.

  • Git 설정: 사용자 이름과 이메일을 설정하기 위해 다음 명령어를 사용합니다.

  • 로컬 저장소 초기화:

    • 프로젝트의 메인 디렉토리로 이동하여 git init -b main 명령어로 새로운 Git 저장소를 초기화하고 main 브랜치를 생성합니다.
    • git add . 명령어로 현재 디렉토리의 모든 파일을 스테이징합니다.
    • git commit -m "Initial commit" 명령어로 커밋을 생성합니다.
  • GitHub와 연결:

    • GitHub 계정을 생성하고 이메일 인증을 완료합니다.
    • SSH 키를 생성하여 로컬 컴퓨터와 GitHub를 안전하게 연결합니다.
      • SSH 키가 있는지 확인: ls -al ~/.ssh
      • SSH 키 생성: GitHub 문서의 명령어를 복사하여 실행
      • SSH 공개 키를 GitHub 설정에 추가
  • 원격 저장소 생성 및 연결:

    • GitHub에서 새로운 저장소를 생성합니다.
    • 터미널에서 원격 저장소를 추가: git remote add origin [SSH_URL]
    • 로컬 main 브랜치를 원격 저장소에 푸시: git push -u origin main
  • 향후 변경 사항 커밋 및 푸시:

    • 변경 사항을 스테이징: git add -A
    • 커밋 생성: git commit -m "Commit message"
    • 원격 저장소에 푸시: git push origin main

5. 프로젝트 디렉토리 탐색 및 보일러플레이트 정리

  • 프로젝트 정리:
    • node_modules 디렉토리: npm install로 설치된 프로젝트 의존성.
    • public 디렉토리: 빌드 과정 없이 그대로 제공되는 정적 자산(예: favicon).
    • src 디렉토리: 주요 코드가 위치하는 곳.
      • assets 디렉토리: 이미지, 폰트 등의 정적 파일 저장. 기본 파일 삭제.
      • components 디렉토리: 재사용 가능한 컴포넌트 저장. 기존 컴포넌트 삭제.
      • router 디렉토리: 내비게이션 설정을 위한 Vue Router 구성. 기본 라우트와 import 문 삭제.
      • stores 디렉토리: Pinia를 사용한 상태 관리. 기본 스토어 삭제.
      • views 디렉토리: 사용자에게 표시되는 주요 뷰 또는 페이지. 기본 뷰 삭제.
<script setup lang="ts">
</script>

<template>
  <RouterView />
</template>

<style scoped>
</style>
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

6. vue-router 설정 및 RouterLink 로 탐색

프로젝트 관리 앱을 계획할 때, 이를 여러 페이지로 구성하는 것이 필수적입니다. 모든 프로젝트를 볼 수 있는 페이지와 특정 프로젝트를 볼 수 있는 페이지가 필요합니다. 이러한 페이지 간에 원활하게 탐색하기 위해 Vue Router를 설정합니다. 필요한 경로를 만들고 구성한 다음 RouterLink구성 요소를 사용하여 원활한 탐색을 가능하게 합니다. 이렇게 하면 페이지 간에 효율적으로 탐색할 수 있는 훌륭한 단일 페이지 애플리케이션 경험이 보장됩니다.

  • 프로젝트 관리 앱을 계획하면서 여러 페이지로 구성해야 합니다. 예를 들어:
    • 모든 프로젝트를 보는 페이지
    • 특정 프로젝트를 보는 페이지
    • 프로젝트 내의 작업을 보는 페이지 등

각 페이지는 Vue 컴포넌트로 표현되며, 사용자들이 페이지 간에 원활하게 이동할 수 있도록 Vue.js의 공식 라우팅 엔진인 Vue Router를 사용합니다. Vue Router를 사용하면 특정 경로에 컴포넌트를 매핑하여 전체 페이지를 새로 고침하지 않고도 필요한 컴포넌트만 교체하여 렌더링할 수 있습니다.

  • Vue Router 설정:

    • src/router/index.ts에서 createRouter와 createWebHistory를 가져와 새로운 라우터 인스턴스를 생성합니다.
    • history 옵션으로 createWebHistory()를 사용하여 HTML5 히스토리 모드를 활성화합니다.
    • routes 배열에 애플리케이션의 경로와 해당 컴포넌트를 정의합니다.
  • 라우트 및 컴포넌트 생성:

    • HomeView.vue와 ProjectsView.vue 컴포넌트를 생성하고, 각각의 경로에 매핑합니다.
    • 각 컴포넌트에서 다른 페이지로 이동할 수 있는 링크를 제공합니다.
  • 라우터 링크 사용:

    • 초기에는 <a> 태그를 사용하여 내비게이션을 구현했지만, 전체 페이지가 새로 고침되는 문제가 있었습니다.
    • 이를 해결하기 위해 <router-link> 컴포넌트로 변경하고 to 속성을 사용하여 페이지 전환 시 전체 페이지 새로 고침 없이 컴포넌트를 교체하도록 수정하였습니다.
    • 이렇게 하면 싱글 페이지 애플리케이션(SPA) 경험을 제공하지만, 현재 상태에서는 성능 최적화가 이루어지지 않아 오히려 성능에 부정적인 영향을 줄 수 있습니다. 이는 다음에 자세히 다룹니다.
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import HomeView from '@/views/HomeView.vue';
import ProjectsView from '@/views/ProjectsView.vue';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
  },
  {
    path: '/projects',
    name: 'projects',
    component: ProjectsView,
  },
];

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

export default router;
<script setup lang="ts">
</script>

<template>
  <RouterView />
</template>

<style scoped>
</style>
<template>
  <div>
    <h1>홈 페이지</h1>
    <router-link to="/projects">프로젝트로 이동</router-link>
  </div>
</template>

<script setup lang="ts">
// 스크립트 내용 없음
</script>

<style scoped>
/* 스타일 내용 없음 */
</style>
<template>
  <div>
    <h1>프로젝트 페이지</h1>
    <router-link to="/">홈으로 이동</router-link>
  </div>
</template>

<script setup lang="ts">
// 스크립트 내용 없음
</script>

<style scoped>
/* 스타일 내용 없음 */
</style>

7. Vue Router에서 Vite의 동적 가져오기를 사용한 Lazy Load Routes

잘 구성된 Vue Router 설정에도 숨겨진 성능 문제가 있을 수 있습니다. Vue Router를 사용하는 동안 흔히 범하는 함정 중 하나는 모든 페이지 뷰 구성 요소를 브라우저에 미리 제공하는 것입니다. 이렇게 하면 불필요한 JavaScript 파일로 브라우저가 부풀어 오르고 초기 로드 시간에 영향을 미칠 수 있습니다.

이번엔 Vue Router의 지연 로딩 지원과 Vite의 동적 가져오기를 활용하여 이 문제를 해결합니다. 이 접근 방식은 필요할 때만 구성 요소가 로드되도록 하여 앱의 속도와 반응성을 유지합니다.

현재 애플리케이션에서는 모든 페이지의 컴포넌트가 초기 로드 시 한꺼번에 번들되어 브라우저로 전송되고 있습니다. 이는 사용자가 특정 페이지만 방문하더라도 불필요한 컴포넌트까지 모두 다운로드하게 되어 초기 로딩 시간이 길어지고 성능이 저하되는 문제를 야기합니다. 이를 해결하기 위해 동적 임포트(Dynamic Imports)를 사용하여 필요한 컴포넌트만 로드하도록 Vue Router 설정을 수정합니다. 이렇게 하면 초기 번들 크기가 감소하여 애플리케이션의 성능과 사용자 경험이 향상됩니다.

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/HomeView.vue'),
  },
  {
    path: '/projects',
    name: 'projects',
    component: () => import('@/views/ProjectsView.vue'),
  },
];

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

export default router;
  • component 속성에 동적 임포트를 사용한 콜백 함수를 전달합니다.
  • import() 함수는 해당 컴포넌트를 필요로 할 때 비동기로 로드합니다.
  • 기존의 정적 임포트 문을 제거하여 초기 번들 크기를 줄입니다.

8. Vue Router에서 와일드카드를 사용하여 동적 경로 만들기

Vue Router에서 와일드카드를 사용하여 동적 경로로 라우팅 설정을 개선할 때입니다. 이 기능을 사용하면 각 프로젝트에 대한 전용 페이지를 만들 수 있습니다. 또한 Vue Router의 useRoute() 컴포저블을 탐색하여 동적 경로 정보에 액세스하고 관련 프로젝트 세부 정보를 표시합니다. 이 세션을 마치면 프로처럼 동적 경로를 관리하여 사용자 친화적인 앱을 보장할 수 있습니다.


  • 동적 라우팅 설정: 프로젝트 관리 앱에서 각 프로젝트마다 개별 페이지를 제공하기 위해 동적 라우트를 설정합니다.
  • 컴포넌트 생성: src/views 디렉토리에 SingleProjectView.vue 컴포넌트를 생성하고, 프로젝트 이름을 표시하는 기본 내용을 작성합니다.
  • Vue Router 설정:
    • path에 '/projects/:id'를 설정하여 id를 동적 매개변수로 지정합니다.
    • component는 동적 임포트를 사용하여 해당 컴포넌트를 비동기로 로드합니다.
  • 라우트 매개변수 접근:
    • 컴포넌트에서 useRoute 컴포저블을 사용하여 현재 경로의 매개변수에 접근합니다.
    • route.params.id를 통해 URL에서 전달된 id 값을 가져옵니다.
    • 템플릿에서 {{ route.params.id }}를 사용하여 해당 id를 표시합니다.
  • 테스트 및 검증:
    • 브라우저에서 /projects/1, /projects/2 등 다양한 경로로 접속하여 동적 페이지가 올바르게 작동하는지 확인합니다.
  • 결과:
    • 사용자 요청에 따라 다른 데이터를 표시하는 동적 페이지를 구현하였습니다.
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/HomeView.vue'),
  },
  {
    path: '/projects',
    name: 'projects',
    component: () => import('@/views/ProjectsView.vue'),
  },
  {
    path: '/projects/:id',
    name: 'single-project',
    component: () => import('@/views/SingleProjectView.vue'),
  },
];

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

export default router;
<template>
  <div>
    <h1>프로젝트 상세 페이지</h1>
    <p>프로젝트 이름: {{ projectName }}</p>
    <p>프로젝트 ID: {{ route.params.id }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';

const route = useRoute();
const projectName = ref('프로젝트 이름');

// 추가 로직이 필요한 경우 여기에 작성합니다.
</script>

<style scoped>
/* 스타일 정의 */
</style>

9. Vue Router에서 정의되지 않은 모든 경로를 포착하고 404 Not Found 페이지 생성

정의되지 않은 경로를 처리하는 것은 사용자가 존재하지 않는 페이지에 도착했을 때 적절한 피드백을 제공하는 데 중요합니다. Vue Router에서 404 오류를 효과적으로 관리하기 위해 catch-all 경로를 설정합니다. NotFound 페이지를 생성하면 사용자가 빈 페이지를 마주치는 대신 항상 명확하고 유익한 피드백을 받을 수 있습니다. 또한 더 세부적인 제어를 위해 다른 경로 아래에 중첩된 모든 정의되지 않은 경로를 catch하는 방법도 알아봅니다.


현재 애플리케이션에서는 정의되지 않은 경로로 이동할 경우 빈 페이지가 나타나고, Vue Router는 콘솔에서 오류를 표시합니다. 이를 개선하기 위해 모든 정의되지 않은 경로를 포착하여 404 Not Found 페이지를 제공하는 방법을 구현합니다.

  • 문제점: 정의되지 않은 경로로 접근하면 빈 페이지가 나타나고, 사용자 경험이 저하됩니다.
  • 해결 방법
    • Vue Router에서 catch-all 라우트를 설정하여 모든 undefined 경로를 포착합니다.
    • path에 /:catchAll(.*)와 같은 정규식을 사용하여 모든 경로를 매칭합니다.
    • 해당 라우트에 표시할 컴포넌트를 지정합니다.
    • 추가 팁:
      • 특정 경로 아래의 undefined 경로만 포착하려면 path를 /projects/:catchAll(.*)와 같이 설정합니다.
      • h 함수(Hyperscript)를 사용하여 간단한 컴포넌트를 즉석에서 생성할 수 있습니다.
    • 결론: 이렇게 하면 사용자가 잘못된 경로로 접근했을 때 적절한 404 페이지를 제공하여 사용자 경험을 향상시킬 수 있습니다.
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/HomeView.vue'),
  },
  {
    path: '/projects',
    name: 'projects',
    component: () => import('@/views/ProjectsView.vue'),
  },
  {
    path: '/projects/:id',
    name: 'single-project',
    component: () => import('@/views/SingleProjectView.vue'),
  },
  // 모든 정의되지 않은 경로를 포착하는 catch-all 라우트
  {
    path: '/:catchAll(.*)',
    name: 'not-found',
    component: {
      render() {
        return h(
          'p',
          { style: { color: 'red' } },
          '404 Not Found'
        );
      },
    },
  },
];

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

export default router;
  • 설명:

    • path: '/:catchAll(.*)'는 모든 경로를 매칭하는 정규식을 사용합니다.
    • component에 직접 렌더 함수를 사용하여 간단한 메시지를 표시합니다.
    • 필요에 따라 별도의 404 컴포넌트를 생성하여 사용할 수도 있습니다.
  • 특정 경로 아래의 undefined 경로 포착 예시:

{
  path: '/projects/:catchAll(.*)',
  name: 'projects-not-found',
  component: {
    render() {
      return h(
        'p',
        { style: { color: 'blue' } },
        '404 Project Not Found'
      );
    },
  },
},

10. TypeScript 지원을 사용하여 Vue Router에서 자동 경로 설정 (unplugin-vue-router)

파일 기반 라우팅과 TypeScript를 사용하여 Vue Router에서 자동 경로를 설정하면 라우팅 설정이 간소화되어 코드베이스가 더 깔끔하고 관리하기 쉬워집니다. Nuxt.js와 유사하게 파일 구조에 따라 경로 생성을 자동화하기 위해 unplugin-vue-router를 사용합니다. 이렇게 하면 수동 경로 정의가 필요 없습니다.

unplugin-vue-router를 설치하고 구성하고, 디렉토리 구조를 조정하고, TypeScript 오류를 해결하는 것으로 시작합니다. 마지막에는 앱에서 동적으로 경로를 생성하여 간소화되고 효율적인 라우팅 시스템을 제공합니다.


이번 시간에는 Vue Router에서 파일 기반 라우팅(file-based routing)과 TypeScript를 사용한 자동 라우팅(auto routing)을 탐구합니다. 이를 통해 라우팅 설정을 단순화하고 코드베이스를 더욱 깔끔하고 관리하기 쉽게 만들 수 있습니다.

  • 파일 기반 라우팅이란?

    • 파일 기반 라우팅은 파일 구조를 통해 라우트를 직접 정의하는 방법입니다.
    • 프로젝트 규모가 커질수록 라우트 관리를 쉽게 해줍니다.
    • Nuxt.js와 같은 프레임워크에서는 이 방식을 사용하여 개발자가 라우트를 수동으로 정의하지 않고 파일 생성만으로 새로운 라우트를 추가할 수 있습니다.
  • Vue에서 파일 기반 라우팅 구현하기

    • unplugin-vue-router 플러그인을 사용하여 Vue에서 파일 기반 라우팅을 구현합니다.
    • 이 플러그인은 파일 구조를 기반으로 라우트를 자동 생성해줍니다.
  1. 플러그인 설치

    npm install -D unplugin-vue-router
  2. Vite 설정 파일 수정 (vite.config.ts)

    • 플러그인을 가져와서 Vite 플러그인 배열에 추가합니다.

      import { fileURLToPath, URL } from 'node:url'
      
      import { defineConfig } from 'vite'
      import vue from '@vitejs/plugin-vue'
      import vueDevTools from 'vite-plugin-vue-devtools'
      import VueRouter from 'unplugin-vue-router/vite'
      
      // https://vite.dev/config/
      export default defineConfig({
        plugins: [
          VueRouter(), // Vue 플러그인보다 앞에 위치해야 합니다.
          vue(),
          vueDevTools(),
        ],
        resolve: {
          alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url))
          },
        },
      })
  3. 경로 별칭(alias) 설명

    • @ 기호는 src 디렉토리를 가리키도록 Vite에서 설정되어 있습니다.

    • 임포트 시 경로를 간결하게 작성할 수 있습니다.

      // 예시:
      import MyComponent from '@/components/MyComponent.vue';
  4. Vue Router 설정 파일 수정 (src/router/index.ts)

    • vue-router 대신 vue-router/auto에서 필요한 함수를 가져옵니다.

      import { createRouter, createWebHistory } from 'vue-router/auto'
      import { routes } from 'vue-router/auto-routes'
      
      const router = createRouter({
        history: createWebHistory(import.meta.env.BASE_URL),
        routes
      })
      
      export default router;
    • 기존에 수동으로 정의한 라우트는 삭제합니다.

    • 플러그인이 자동으로 라우트를 생성하므로 직접 정의할 필요가 없습니다.

  5. 디렉토리 이름 변경

    • 플러그인의 규칙에 따라 views 디렉토리를 pages로 이름을 변경합니다.
  6. TypeScript 타입 오류 해결

    • tsconfig.json 또는 tsconfig.app.json 파일에서 다음을 추가합니다.

      {
        "compilerOptions": {
          // 기존 설정...
          "moduleResolution": "bundler"
        },
        "include": [
          "src/**/*",
          "typed-router.d.ts"
        ]
      }
    • moduleResolution을 bundler로 설정하여 모듈 해석 방식을 지정합니다.

    • typed-router.d.ts 파일을 include 배열에 추가하여 타입 정보를 포함합니다.

  7. 환경 타입 파일 수정 (src/env.d.ts)

    • 파일 상단에 다음을 추가하여 글로벌 타입을 설정합니다.

      /// <reference types="vite/client" />
      /// <reference types="unplugin-vue-router/client" />
  8. 개발 서버 실행 및 타입 파일 생성

    • 터미널에서 개발 서버를 실행합니다.

      npm run dev
    • 이때 typed-router.d.ts 파일이 자동으로 생성되며, pages 디렉토리의 라우트 타입 정보를 선언합니다.

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import VueRouter from 'unplugin-vue-router/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    VueRouter(), // Vue 플러그인보다 앞에 위치해야 합니다.
    vue(),
    vueDevTools(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
})
import {createRouter, createWebHistory} from 'vue-router'
import {routes} from "vue-router/auto-routes";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
})

export default router
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": [
    // ...
    "typed-router.d.ts"
  ],
  // ...
  "compilerOptions": {
    // ...
    "moduleResolution": "bundler",
    // ...
  }
}
/// <reference types="vite/client" />
/// <reference types="unplugin-vue-router/client" />

11. 파일 기반 라우팅을 위한 Vue.js 코드베이스 리팩토링

이번엔 Vue.js 앱에서 파일 기반 라우팅의 개념을 적용하기 위해 코드베이스를 리팩토링합니다. 디렉토리와 파일 이름을 경로에 손쉽게 매핑하고, 동적 및 중첩 경로를 만들고, 랜딩 페이지를 설정하는 방법을 알아봅니다. 또한 훌륭한 개발 경험을 위해 TypeScript를 Vue Router와 통합하는 이점도 다룹니다.


이번엔 파일 기반 라우팅(file-based routing)을 활용하여 Vue.js 애플리케이션의 라우팅을 더욱 간단하고 효율적으로 관리하는 방법을 배웁니다. 주요 내용은 다음과 같습니다:

  • 파일 및 디렉토리 구조가 라우트 경로를 결정합니다.

    • 예를 들어, pages 디렉토리 아래에 projects.vue 파일이 있으면, 이는 /projects 경로와 매핑됩니다.
    • 디렉토리 내에 또 다른 파일이 있으면 중첩된 라우트를 생성합니다.
      • 예: pages/projects/about.vue는 /projects/about 경로와 매핑됩니다.
    • 중첩 라우트 생성: 디렉토리를 사용하여 라우트를 계층적으로 구성할 수 있습니다.
  • 특정 경로의 랜딩 페이지를 만들기 위해 index.vue 파일을 사용합니다.

    • pages/index.vue는 사이트의 홈 페이지(/)로 렌더링됩니다.
    • pages/projects/index.vue는 /projects 경로의 랜딩 페이지로 렌더링됩니다.
  • 동적 라우트(Dynamic Route) 생성:

    • 파일명을 대괄호로 감싸서 동적 매개변수를 만듭니다.
      • 예: pages/projects/[id].vue는 /projects/:id 경로와 매핑되며, :id는 동적 매개변수가 됩니다.
    • 이를 통해 개별 프로젝트 페이지를 동적으로 생성할 수 있습니다.
  • 캐치올(Catch-All) 라우트 생성:

    • 파일명 앞에 세 개의 점 ...을 추가하고 대괄호로 감싸서 모든 경로를 포착합니다.
      • 예: pages/[...catchAll].vue는 정의되지 않은 모든 경로를 포착하여 404 페이지로 사용할 수 있습니다.
      • 파일의 위치에 따라 포착되는 경로가 달라집니다.
        • pages/projects/[...catchAll].vue는 /projects 경로 아래의 정의되지 않은 모든 경로를 포착합니다.
  • 코드베이스 변경 사항 적용:

    1. 홈 페이지 설정:
      • home.vue를 index.vue로 이름 변경하여 루트 경로(/)의 랜딩 페이지로 사용합니다.
    2. 프로젝트 페이지 설정:
      • projects 디렉토리를 생성합니다.
      • 기존의 projects.vue를 projects/index.vue로 이동하여 /projects 경로의 랜딩 페이지로 사용합니다.
    3. 개별 프로젝트 페이지 설정:
      • singleProjectView.vue를 projects/[id].vue로 이동하여 동적 라우트를 만듭니다.
    4. 캐치올 라우트 설정:
      • pages/[...catchAll].vue 파일을 생성하여 전체 애플리케이션의 정의되지 않은 모든 경로를 포착합니다.
  • 타입스크립트와의 통합 및 자동 완성 기능 활용:

    • 라우트 정의가 파일 기반으로 이루어지므로, 타입스크립트는 라우트의 타입 정보를 자동으로 생성합니다.
    • 컴포넌트에서 router-link의 to 속성에 객체를 전달할 때, 타입스크립트의 자동 완성 기능으로 라우트 이름과 매개변수를 쉽게 확인할 수 있습니다.
      • 예: <router-link :to="{ name: 'projects-[id]', params: { id: 1 } }">프로젝트 1로 이동</router-link>
    • 이를 통해 개발 생산성을 높이고, 오타나 실수를 줄일 수 있습니다.
  • ESLint 오류 해결:

    • 파일 기반 라우팅에서는 단일 단어로 파일명을 사용하므로, ESLint에서 "컴포넌트 이름은 항상 여러 단어여야 합니다"라는 경고가 발생할 수 있습니다.
    • .eslintrc 파일에서 해당 규칙을 비활성화하여 오류를 해결합니다.
      • rules 섹션에 "vue/multi-word-component-names": 0을 추가합니다.
  • 결론:

    • 파일 기반 라우팅과 타입스크립트 지원을 통해 라우팅 설정을 간소화하고 유지 보수성을 향상시켰습니다.
    • 모든 라우트는 자동으로 지연 로드(lazy loading)되며, 파일 구조에 따라 자동으로 생성됩니다.
src/
├─ pages/
│   ├─ index.vue
│   ├─ projects/
│   │   ├─ index.vue
│   │   ├─ [id].vue
│   │   └─ [...catchAll].vue
│   └─ [...catchAll].vue
<script setup lang="ts">

</script>

<template>
  <h1>메인페이지</h1>
  <RouterLink to="/projects">프로젝트로 이동</RouterLink>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>프로젝트 메인 페이지</h1>
  <RouterLink :to="{name: '/projects/[id]', params: {id: 1}}">프로젝트 1로 이동</RouterLink>
</template>

<style scoped>

</style>
<script setup lang="ts">
  import { useRoute } from 'vue-router'

  const route = useRoute('/projects/[id]')
</script>

<template>
  <h1>프로젝트 상세 페이지</h1>
  <p>프로젝트 ID: {{ route.params.id }}</p>
  <RouterLink to="/">홈으로 이동</RouterLink>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>404 에러 페이지</h1>
  <RouterLink to="/projects">프로젝트로 이동</RouterLink>
</template>

<style scoped>

</style>
<script setup lang="ts">

</script>

<template>
  <h1>404 프로젝트 에러 페이지</h1>
  <RouterLink to="/">홈으로 이동</RouterLink>
</template>

<style scoped>

</style>
import pluginVue from 'eslint-plugin-vue'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default [
  {
    name: 'app/files-to-lint',
    files: ['**/*.{ts,mts,tsx,vue}']
  },

  {
    name: 'app/files-to-ignore',
    ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**']
  },
  ...pluginVue.configs['flat/essential'],
  // rules 위치 // ...pluginVue.configs['flat/essential'] 뒤에 추가
  {
    rules: {
      'vue/multi-word-component-names': 0
    }
  },
  ...vueTsEslintConfig(),
  skipFormatting
]

12. Supabase: Vue.js 프런트엔드를 위한 완벽한 백엔드

프런트엔드를 만드는 것은 이야기의 절반에 불과합니다! 앱의 데이터를 저장하고, 사용자 인증을 처리하고, 모든 것을 원활하게 실행하려면 강력한 백엔드가 필요합니다. 바로 여기서 Supabase가 등장합니다. 이 오픈소스 Firebase 대안은 즉각적인 API, 사용자 인증, 안전한 스토리지, 심지어 실시간 기능까지 갖춘 강력한 PostgreSQL 데이터베이스를 제공합니다. 이 모든 것이 견고한 백엔드를 위한 요소입니다.

Supabase는 사용하기 쉬운 것으로 유명하므로 복잡한 설정을 건너뛰고 정말 중요한 것, 즉 멋진 Vue.js 애플리케이션을 만드는 데 집중할 수 있습니다. 이 수업에서는 Supabase를 함께 살펴보고 앞으로의 작업에 대비할 수 있도록 준비합니다.


이번엔 Vue.js 애플리케이션을 위한 백엔드 솔루션으로 Supabase를 소개합니다. 대규모 애플리케이션을 구축할 때는 프론트엔드뿐만 아니라 데이터 저장, 인증, API 통신 등을 위한 백엔드가 필요합니다.

Supabase는 오픈 소스 Firebase 대안으로, 강력하고 안정적인 PostgreSQL 데이터베이스를 기반으로 합니다. 다음과 같은 주요 기능을 제공합니다:

  • 데이터베이스: 즉시 사용 가능한 API와 함께 제공되는 PostgreSQL 데이터베이스로, 데이터와 쉽게 상호 작용할 수 있습니다.

  • 인증: 회원 가입, 로그인, 사용자 세션 관리를 간편하게 처리할 수 있습니다.

  • 스토리지: 이미지, 문서 등 다양한 파일 유형을 저장할 수 있는 확장 가능하고 안전한 스토리지 솔루션을 제공합니다.

  • 엣지 함수(Edge Functions): 사용자에게 가까운 위치에서 커스텀 백엔드 로직을 실행할 수 있는 서버리스 함수로, 낮은 지연 시간과 높은 성능을 보장합니다.

  • 실시간 기능: 데이터베이스의 변경 사항에 따라 애플리케이션이 실시간으로 업데이트될 수 있도록 실시간 구독 기능을 제공합니다.

Supabase를 선택한 이유로 백엔드 설정에 소요되는 시간을 줄이고, 인증 로직이나 백엔드 구축에 대한 별도의 학습 없이도 쉽게 백엔드를 구성할 수 있다는 점을 강조합니다.

또한, 스키마(Schema), 시드(Seed), 마이그레이션(Migration)과 같은 용어에 익숙하지 않아도 걱정할 필요 없으며, 모든 과정을 단계별로 안내할 것입니다. 결과적으로 Vue.js 애플리케이션을 위한 견고한 백엔드를 구축하게 될 것입니다.

[사용자 요청]
      ↓
[Vue.js 프론트엔드 애플리케이션]
      ↓
[Supabase SDK를 통한 API 호출]
      ↓
[Supabase 백엔드]
  ├─ PostgreSQL 데이터베이스 (데이터 저장 및 조회)
  ├─ 인증 서비스 (회원 가입, 로그인, 세션 관리)
  ├─ 스토리지 서비스 (파일 업로드 및 관리)
  ├─ 엣지 함수 (커스텀 백엔드 로직 실행)
  └─ 실시간 기능 (데이터 변경 사항 실시간 반영)
      ↓
[데이터 또는 응답 반환]
      ↓
[Vue.js 애플리케이션에서 데이터 표시 및 업데이트]
  1. Supabase 클라이언트 설정 (supabaseClient.ts):

    import { createClient } from '@supabase/supabase-js';
    
    const supabaseUrl = 'https://your-supabase-url.supabase.co';
    const supabaseAnonKey = 'your-anon-key';
    
    export const supabase = createClient(supabaseUrl, supabaseAnonKey);
  2. 사용자가 로그인하는 컴포넌트 (Login.vue):

    <template>
      <div>
        <h1>로그인</h1>
        <form @submit.prevent="login">
          <input v-model="email" type="email" placeholder="이메일" required />
          <input v-model="password" type="password" placeholder="비밀번호" required />
          <button type="submit">로그인</button>
        </form>
        <p v-if="error">{{ error }}</p>
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue';
    import { supabase } from '@/supabaseClient';
    
    const email = ref('');
    const password = ref('');
    const error = ref<string | null>(null);
    
    const login = async () => {
      const { user, session, error: loginError } = await supabase.auth.signIn({
        email: email.value,
        password: password.value,
      });
    
      if (loginError) {
        error.value = loginError.message;
      } else {
        error.value = null;
        // 로그인 성공 처리 (예: 페이지 이동)
      }
    };
    </script>
    
    <style scoped>
    /* 스타일 정의 */
    </style>
  3. 데이터베이스에서 데이터 가져오기 (Projects.vue):

    <template>
      <div>
        <h1>프로젝트 목록</h1>
        <ul>
          <li v-for="project in projects" :key="project.id">
            {{ project.name }}
          </li>
        </ul>
      </div>
    </template>
    
    <script setup lang="ts">
      import { ref, onMounted } from 'vue';
      import { supabase } from '@/supabaseClient';
    
      interface Project {
        id: number;
        name: string;
      }
    
      const projects = ref<Project[]>([]);
    
      onMounted(async () => {
        const { data, error } = await supabase
                .from<Project>('projects')
                .select('*');
    
        if (error) {
          console.error('프로젝트를 불러오는 중 오류 발생:', error.message);
        } else {
          projects.value = data || [];
        }
      });
    </script>
    
    <style scoped>
      /* 스타일 정의 */
    </style>

13. Supabase를 통합하고 Vue.js와 연결

이번엔 오픈소스 백엔드 솔루션인 Supabase를 설정하는 방법을 보여드리겠습니다. Supabase 계정, 조직 및 프로젝트를 만드는 방법을 안내해드리겠습니다. 이 프로젝트는 백엔드 명령 센터 역할을 하며 앱의 데이터를 저장하고 사용자 인증을 처리합니다.

Vue.js 앱을 Supabase에 원활하게 연결하기 위해 Supabase JavaScript 클라이언트 라이브러리를 활용하겠습니다. 이 라이브러리는 브릿지 역할을 하여 프런트엔드 Vue.js 앱과 Supabase 백엔드 간의 원활한 통신을 가능하게 합니다.

또한, 보안 문제와 이를 해결하는 방법에 대해서도 나중에 다루겠습니다. 지금은 Supabase를 연결하고 작동 준비를 합시다!


이번엔 Vue.js 애플리케이션을 위한 백엔드 솔루션으로 Supabase를 설정하고 통합하는 과정을 다룹니다. Supabase는 PostgreSQL 기반의 오픈 소스 백엔드 서비스로, 데이터베이스 관리, 인증, 스토리지, 실시간 기능 등을 제공하여 Vue.js 애플리케이션과의 원활한 통합을 지원합니다. 주요 내용은 다음과 같습니다:

  • Supabase 계정 생성 및 설정
    • Supabase 웹사이트에서 계정을 생성하고 로그인합니다.
    • 새로운 조직(Organization)을 생성합니다. 예시에서는 "masterclass"라는 이름으로 조직을 생성하고, 유형을 "교육(Educational)"으로 설정합니다.
    • 조직 내에서 프로젝트(Project)를 생성합니다. 예시에서는 "Project Pulse"라는 이름의 프로젝트를 생성합니다.
    • 프로젝트 생성 시 PostgreSQL 데이터베이스의 강력한 비밀번호를 설정하고, 원하는 지역(Region)을 선택합니다.

  • Supabase 클라이언트 설정

    • Supabase 대시보드에서 프로젝트의 URL과 API 키(API Key)를 복사합니다.
    • Vue.js 프로젝트의 src/lib 디렉토리에 supabaseClient.ts 파일을 생성하고, 복사한 URL과 익명 키를 사용하여 Supabase 클라이언트를 초기화합니다.
  • Supabase 클라이언트 설정 (supabaseClient.ts):

    import { createClient } from '@supabase/supabase-js';
    
    const supabaseUrl = 'https://your-supabase-url.supabase.co';
    const supabaseAnonKey = 'your-anon-key';
    
    export const supabase = createClient(supabaseUrl, supabaseAnonKey);
  • Vue.js 애플리케이션과 Supabase 통합

    • Supabase 클라이언트를 Vue 컴포넌트에서 사용하여 데이터베이스와 상호작용합니다.
    • 예시로 로그인 기능과 데이터베이스에서 프로젝트 데이터를 가져오는 방법을 소개합니다.
    • Supabase와의 통합 과정에서 발생할 수 있는 타입스크립트 오류를 해결하기 위해 올바르게 파일을 내보내고(import/export) 설정합니다.
  • 보안 고려사항

    • Supabase URL과 익명 키는 클라이언트 측에서 노출되므로, 역할 기반 보안(Role-Level Security)을 구현하여 데이터베이스 접근을 제어해야 합니다.
    • 이 부분은 이후 강의에서 자세히 다룰 예정입니다.
[사용자]
    ↓
[Vue.js 프론트엔드 애플리케이션]
    ↓
[Supabase SDK를 통한 API 호출]
    ↓
[Supabase 백엔드]
  ├─ PostgreSQL 데이터베이스 (데이터 저장 및 조회)
  ├─ 인증 서비스 (회원 가입, 로그인, 세션 관리)
  ├─ 스토리지 서비스 (파일 업로드 및 관리)
  ├─ 엣지 함수 (커스텀 백엔드 로직 실행)
  └─ 실시간 기능 (데이터 변경 사항 실시간 반영)
    ↓
[데이터 또는 응답 반환]
    ↓
[Vue.js 애플리케이션에서 데이터 표시 및 업데이트]

14. Vite 환경 변수를 사용하여 비밀 앱 데이터 보호

우리 애플리케이션의 보안은 항상 최우선이어야 합니다. 따라서 민감한 데이터를 보호하고 숨기는 방법을 이해하는 데 시간을 투자해야 합니다.

이번엔 Vue.js와 같은 클라이언트 측 단일 페이지 애플리케이션에서도 환경 변수를 사용하여 민감한 데이터를 숨기는 이점에 대해 논의합니다. 이 접근 방식이 코드를 더 유연하고 안전하게 만들어 다양한 환경에 대한 키를 동적으로 관리하고 민감한 정보를 보호할 수 있는 방법을 알아봅니다.

이는 보안을 향한 한 걸음일 뿐이지만, 앱의 보안을 강화하기 위해 해야 할 일은 아직 많습니다.


이번엔 Vue.js 애플리케이션의 보안을 강화하기 위해 환경 변수(Environment Variables)를 사용하는 방법을 다룹니다. 환경 변수는 민감한 정보(예: Supabase URL, 익명 키)를 코드에 직접 노출하지 않고 관리할 수 있게 해주어 보안과 유연성을 동시에 제공합니다. 주요 내용은 다음과 같습니다:

  • 환경 변수의 중요성 및 역할:

    • 환경 변수는 코드 내에서 직접 값을 노출하지 않고도 민감한 데이터를 참조할 수 있는 자리 표시자 역할을 합니다.
    • 서버 사이드에서는 환경 변수가 완전히 보호되지만, 클라이언트 사이드(싱글 페이지 애플리케이션)에서는 환경 변수가 노출되므로 추가적인 보안 조치가 필요합니다.
    • 환경 변수의 주된 이점은 유연성입니다. 개발 환경과 배포 환경에 따라 다른 값을 동적으로 설정할 수 있습니다.
    • 코드 보안 측면에서는 환경 변수를 사용함으로써 코드베이스를 공유할 때 민감한 정보를 노출하지 않을 수 있습니다.
  • Vue.js에서 환경 변수 사용 방법:

    • 환경 변수 참조: import.meta.env를 통해 환경 변수를 참조합니다.
    • 환경 변수 설정:
      • .env 파일을 사용하여 환경 변수를 정의합니다.
      • 환경 변수 이름 앞에 VITE_를 붙이면 클라이언트 사이드에서 접근할 수 있습니다.
      • .env.example 파일을 생성하여 팀원들이 필요한 환경 변수를 쉽게 설정할 수 있도록 템플릿을 제공합니다.
    • 환경 변수 파일 관리:
      • .env 파일은 민감한 정보를 포함하므로 .gitignore에 추가하여 버전 관리 시스템(Git)에 포함되지 않도록 합니다.
      • .env.example 파일은 Git에 포함시켜 팀원들이 참고할 수 있도록 합니다.
  • vite env key 정의 방법

    1. node 실행시 VITE_ 프리픽스 붙은 키에 값 정의하고 실행하기
    VITE_SUPER_SECRET_KEY=MasterclassRules! npm run dev
    console.log(import.meta.env.VITE_SUPER_SECRET_KEY)

15. Supabase UI를 사용하여 테이블과 행 만들기

이번엔 PostgreSQL용 Supabase UI를 사용하여 PostgreSQL 데이터베이스를 설정하고 채우는 것을 시작합니다. 프로젝트에 대한 테이블을 만들고 열을 추가합니다.

  1. supabase dashboard -> database -> tables

  2. table editor

16. Supabase SQL Editor를 사용하여 테이블과 행 만들기

이번엔 PostgreSQL 구문을 사용하여 유형을 정의하고 쿼리를 작성하는 방법을 알아봅니다.

  1. sql editor

  2. table editor

  3. sql editor

17. Vue.js에서 Supabase CLI를 사용하고 원격 프로젝트에 연결

이번엔 Supabase CLI를 사용하여 로컬 Supabase 놀이터를 초기화한 다음 라이브 Supabase 프로젝트에 연결합니다. 이렇게 하면 Vue.js 프로젝트를 벗어나지 않고도 원격 데이터베이스를 제어할 수 있습니다.

npm install supabase --save-dev
{
  "scripts": {
    "supabase:init": "supabase init"
  }
}
npm run supabase:init

> vue-2024@0.0.0 supabase:init
> supabase init

Generate VS Code settings for Deno? [y/N] n
Generate IntelliJ Settings for Deno? [y/N] n
Finished supabase init.

{
  "scripts": {
    "supabase:login": "supabase login"
  }
}
npm run supabase:login

> vue-2024@0.0.0 supabase:login
> supabase login

Hello from Supabase! Press Enter to open browser and login automatically.

Here is your login link in case browser did not open https://supabase.com/dashboard/cli/login?session_id=d79f3da9-ea85-4eb7-a744-4f4527d4b149&token_name=cli_aaa@AL01598765.local_1732314487&public_key=04c8a5657869f42e30c515dfeebc28118f9a3a3c28c8eb2bd5370b082965e1e178165b7d26d496038f834734c26cfdf504f1d169267abccb501152413bbbdbecb1

Enter your verification code: xxxxxxx
Token cli_aaa@AL01598765.local_1732314487 created successfully.

You are now logged in. Happy coding!
  • link to supabase project

  • package.json에 프로젝트 연결 명령어 추가

    • ******************** 이 부분은 project id (or project reference)
{
  "scripts": {
    "supabase:link": "supabase link --project-ref ********************"
  }
}

npm run supabase:link

> vue-2024@0.0.0 supabase:link
> supabase link --project-ref yfuucibtdvvzzxlswqva

WARN: no seed files matched pattern: supabase/seed.sql
Enter your database password (or leave blank to skip): 
Finished supabase link.